mysensors/libraries/NodeManager/NodeManager.cpp

2628 lines
75 KiB
C++

/*
* NodeManager
*/
#include "NodeManager.h"
/***************************************
Global functions
*/
// return vcc in V
float getVcc() {
#ifndef MY_GATEWAY_ESP8266
// Measure Vcc against 1.1V Vref
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = (_BV(MUX5) | _BV(MUX0));
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = (_BV(MUX3) | _BV(MUX2));
#else
ADMUX = (_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
#endif
// Vref settle
wait(70);
// Do conversion
ADCSRA |= _BV(ADSC);
while (bit_is_set(ADCSRA, ADSC)) {};
// return Vcc in mV
return (float)((1125300UL) / ADC) / 1000;
#else
return (float)0;
#endif
}
/***************************************
PowerManager
*/
// set the vcc and ground pin the sensor is connected to
void PowerManager::setPowerPins(int ground_pin, int vcc_pin, int wait_time) {
#if DEBUG == 1
Serial.print(F("PWR G="));
Serial.print(ground_pin);
Serial.print(F(" V="));
Serial.println(vcc_pin);
#endif
// configure the vcc pin as output and initialize to high (power on)
_vcc_pin = vcc_pin;
pinMode(_vcc_pin, OUTPUT);
digitalWrite(_vcc_pin, HIGH);
// configure the ground pin as output and initialize to low
_ground_pin = ground_pin;
pinMode(_ground_pin, OUTPUT);
digitalWrite(_ground_pin, LOW);
_wait = wait_time;
}
// return true if power pins have been configured
bool PowerManager::isConfigured() {
if (_vcc_pin != -1 && _ground_pin != -1) return true;
return false;
}
// turn on the sensor by activating its power pins
void PowerManager::powerOn() {
if (! isConfigured()) return;
#if DEBUG == 1
Serial.print(F("ON P="));
Serial.println(_vcc_pin);
#endif
// power on the sensor by turning high the vcc pin
digitalWrite(_vcc_pin, HIGH);
// wait a bit for the device to settle down
if (_wait > 0) wait(_wait);
}
// turn off the sensor
void PowerManager::powerOff() {
if (! isConfigured()) return;
#if DEBUG == 1
Serial.print(F("OFF P="));
Serial.println(_vcc_pin);
#endif
// power off the sensor by turning low the vcc pin
digitalWrite(_vcc_pin, LOW);
}
/******************************************
Sensors
*/
/*
Sensor class
*/
// constructor
Sensor::Sensor(int child_id, int pin) {
_child_id = child_id;
_pin = pin;
_msg = MyMessage(_child_id, _type);
}
// setter/getter
void Sensor::setPin(int value) {
_pin = value;
}
int Sensor::getPin() {
return _pin;
}
void Sensor::setChildId(int value) {
_child_id = value;
}
int Sensor::getChildId() {
return _child_id;
}
void Sensor::setPresentation(int value) {
_presentation = value;
}
int Sensor::getPresentation() {
return _presentation;
}
void Sensor::setType(int value) {
_type = value;
_msg.setType(_type);
}
int Sensor::getType() {
return _type;
}
void Sensor::setDescription(char* value) {
_description = value;
}
void Sensor::setAck(bool value) {
_ack = value;
}
void Sensor::setRetries(int value) {
_retries = value;
}
void Sensor::setSamples(int value) {
_samples = value;
}
void Sensor::setSamplesInterval(int value) {
_samples_interval = value;
}
void Sensor::setTackLastValue(bool value) {
_track_last_value = value;
}
void Sensor::setForceUpdate(int value) {
_force_update = value;
}
void Sensor::setValueType(int value) {
_value_type = value;
}
int Sensor::getValueType() {
return _value_type;
}
void Sensor::setFloatPrecision(int value) {
_float_precision = value;
}
#if POWER_MANAGER == 1
void Sensor::setPowerPins(int ground_pin, int vcc_pin, int wait_time) {
_powerManager.setPowerPins(ground_pin, vcc_pin, wait_time);
}
void Sensor::setAutoPowerPins(bool value) {
_auto_power_pins = value;
}
void Sensor::powerOn() {
_powerManager.powerOn();
}
void Sensor::powerOff() {
_powerManager.powerOff();
}
#endif
void Sensor::setSleepBetweenSend(int value) {
_sleep_between_send = value;
}
void Sensor::setInterruptPin(int value) {
_interrupt_pin = value;
}
int Sensor::getInterruptPin() {
return _interrupt_pin;
}
int Sensor::getValueInt() {
return _last_value_int;
}
float Sensor::getValueFloat() {
return _last_value_float;
}
char* Sensor::getValueString() {
return _last_value_string;
}
// present the sensor to the gateway and controller
void Sensor::presentation() {
#if DEBUG == 1
Serial.print(F("PRES I="));
Serial.print(_child_id);
Serial.print(F(" T="));
Serial.println(_presentation);
#endif
present(_child_id, _presentation,_description,_ack);
}
// call the sensor-specific implementation of before
void Sensor::before() {
if (_pin == -1) return;
onBefore();
}
// call the sensor-specific implementation of setup
void Sensor::setup() {
if (_pin == -1) return;
onSetup();
}
// call the sensor-specific implementation of loop
void Sensor::loop(const MyMessage & message) {
if (_pin == -1) return;
#if POWER_MANAGER == 1
// turn the sensor on
if (_auto_power_pins) powerOn();
#endif
// for numeric sensor requiring multiple samples, keep track of the total
float total = 0;
// keep track of the number of cycles since the last update
if (_force_update > 0) _cycles++;
// collect multiple samples if needed
for (int i = 0; i < _samples; i++) {
// call the sensor-specific implementation of the main task which will store the result in the _value variable
if (message.sender == 0 && message.sensor == 0 && message.getCommand() == 0 && message.type == 0) {
// empty message, we'be been called from loop()
onLoop();
}
else {
// we've been called from receive(), pass the message along
onReceive(message);
}
// for integers and floats, keep track of the total
if (_value_type == TYPE_INTEGER) total += (float)_value_int;
else if (_value_type == TYPE_FLOAT) total += _value_float;
// wait between samples
if (_samples_interval > 0) wait(_samples_interval);
}
// process the result and send a response back.
if (_value_type == TYPE_INTEGER && total > -1) {
// if the value is an integer, calculate the average value of the samples
int avg = (int) (total / _samples);
// if track last value is disabled or if enabled and the current value is different then the old value, send it back
if (! _track_last_value || (_track_last_value && avg != _last_value_int) || (_track_last_value && _force_update > 0 && _cycles > _force_update)) {
_cycles = 0;
_last_value_int = avg;
_send(_msg.set(avg));
}
}
// process a float value
else if (_value_type == TYPE_FLOAT && total > -1) {
// calculate the average value of the samples
float avg = total / _samples;
// if track last value is disabled or if enabled and the current value is different then the old value, send it back
if (! _track_last_value || (_track_last_value && avg != _last_value_float) || (_track_last_value && _cycles >= _force_update)) {
_cycles = 0;
_last_value_float = avg;
_send(_msg.set(avg, _float_precision));
}
}
// process a string value
else if (_value_type == TYPE_STRING) {
// if track last value is disabled or if enabled and the current value is different then the old value, send it back
if (! _track_last_value || (_track_last_value && strcmp(_value_string, _last_value_string) != 0) || (_track_last_value && _cycles >= _force_update)) {
_cycles = 0;
_last_value_string = _value_string;
_send(_msg.set(_value_string));
}
}
// turn the sensor off
#if POWER_MANAGER == 1
if (_auto_power_pins) powerOff();
#endif
}
// receive a message from the radio network
void Sensor::receive(const MyMessage &message) {
// return if not for this sensor
if (message.sensor != _child_id || message.type != _type) return;
// a request would make the sensor executing its main task passing along the message
loop(message);
}
// send a message to the network
void Sensor::_send(MyMessage & message) {
// send the message, multiple times if requested
for (int i = 0; i < _retries; i++) {
// if configured, sleep beetween each send
if (_sleep_between_send > 0) sleep(_sleep_between_send);
#if DEBUG == 1
Serial.print(F("SEND D="));
Serial.print(message.destination);
Serial.print(F(" I="));
Serial.print(message.sensor);
Serial.print(F(" C="));
Serial.print(message.getCommand());
Serial.print(F(" T="));
Serial.print(message.type);
Serial.print(F(" S="));
Serial.print(message.getString());
Serial.print(F(" I="));
Serial.print(message.getInt());
Serial.print(F(" F="));
Serial.println(message.getFloat());
#endif
send(message,_ack);
}
}
/*
SensorAnalogInput
*/
// contructor
SensorAnalogInput::SensorAnalogInput(int child_id, int pin): Sensor(child_id, pin) {
}
// setter/getter
void SensorAnalogInput::setReference(int value) {
_reference = value;
}
void SensorAnalogInput::setReverse(bool value) {
_reverse = value;
}
void SensorAnalogInput::setOutputPercentage(bool value) {
_output_percentage = value;
}
void SensorAnalogInput::setRangeMin(int value) {
_range_min = value;
}
void SensorAnalogInput::setRangeMax(int value) {
_range_max = value;
}
// what to do during before
void SensorAnalogInput::onBefore() {
// prepare the pin for input
pinMode(_pin, INPUT);
}
// what to do during setup
void SensorAnalogInput::onSetup() {
}
// what to do during loop
void SensorAnalogInput::onLoop() {
// read the input
int adc = _getAnalogRead();
// calculate the percentage
int percentage = 0;
if (_output_percentage) percentage = _getPercentage(adc);
#if DEBUG == 1
Serial.print(F("A-IN I="));
Serial.print(_child_id);
Serial.print(F(" V="));
Serial.print(adc);
Serial.print(F(" %="));
Serial.println(percentage);
#endif
// store the result
_value_int = _output_percentage ? percentage : adc;
}
// what to do during loop
void SensorAnalogInput::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) onLoop();
}
// read the analog input
int SensorAnalogInput::_getAnalogRead() {
#ifndef MY_GATEWAY_ESP8266
// set the reference
if (_reference != -1) {
analogReference(_reference);
wait(100);
}
#endif
// read and return the value
int value = analogRead(_pin);
if (_reverse) value = _range_max - value;
return value;
}
// return a percentage from an analog value
int SensorAnalogInput::_getPercentage(int adc) {
float value = (float)adc;
// restore the original value
if (_reverse) value = 1024 - value;
// scale the percentage based on the range provided
float percentage = ((value - _range_min) / (_range_max - _range_min)) * 100;
if (_reverse) percentage = 100 - percentage;
if (percentage > 100) percentage = 100;
if (percentage < 0) percentage = 0;
return (int)percentage;
}
/*
SensorLDR
*/
// contructor
SensorLDR::SensorLDR(int child_id, int pin): SensorAnalogInput(child_id, pin) {
// set presentation and type and reverse (0: no light, 100: max light)
setPresentation(S_LIGHT_LEVEL);
setType(V_LIGHT_LEVEL);
setReverse(true);
}
/*
SensorThermistor
*/
// contructor
SensorThermistor::SensorThermistor(int child_id, int pin): Sensor(child_id, pin) {
// set presentation, type and value type
setPresentation(S_TEMP);
setType(V_TEMP);
setValueType(TYPE_FLOAT);
}
// setter/getter
void SensorThermistor::setNominalResistor(long value) {
_nominal_resistor = value;
}
void SensorThermistor::setNominalTemperature(int value) {
_nominal_temperature = value;
}
void SensorThermistor::setBCoefficient(int value) {
_b_coefficient = value;
}
void SensorThermistor::setSeriesResistor(long value) {
_series_resistor = value;
}
void SensorThermistor::setOffset(float value) {
_offset = value;
}
// what to do during before
void SensorThermistor::onBefore() {
// set the pin as input
pinMode(_pin, INPUT);
}
// what to do during setup
void SensorThermistor::onSetup() {
}
// what to do during loop
void SensorThermistor::onLoop() {
// read the voltage across the thermistor
float adc = analogRead(_pin);
// calculate the temperature
float reading = (1023 / adc) - 1;
reading = _series_resistor / reading;
float temperature;
temperature = reading / _nominal_resistor; // (R/Ro)
temperature = log(temperature); // ln(R/Ro)
temperature /= _b_coefficient; // 1/B * ln(R/Ro)
temperature += 1.0 / (_nominal_temperature + 273.15); // + (1/To)
temperature = 1.0 / temperature; // Invert
temperature -= 273.15; // convert to C
if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32;
#if DEBUG == 1
Serial.print(F("THER I="));
Serial.print(_child_id);
Serial.print(F(" V="));
Serial.print(adc);
Serial.print(F(" T="));
Serial.print(temperature);
Serial.print(F(" M="));
Serial.println(getControllerConfig().isMetric);
#endif
// store the value
_value_float = temperature;
}
// what to do as the main task when receiving a message
void SensorThermistor::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) onLoop();
}
/*
SensorML8511
*/
// contructor
SensorML8511::SensorML8511(int child_id, int pin): Sensor(child_id, pin) {
// set presentation, type and value type
setPresentation(S_UV);
setType(V_UV);
setValueType(TYPE_FLOAT);
}
// what to do during before
void SensorML8511::onBefore() {
// set the pin as input
pinMode(_pin, INPUT);
}
// what to do during setup
void SensorML8511::onSetup() {
}
// what to do during loop
void SensorML8511::onLoop() {
// read the voltage
int uvLevel = analogRead(_pin);
int refLevel = getVcc()*1024/3.3;
//Use the 3.3V power pin as a reference to get a very accurate output value from sensor
float outputVoltage = 3.3 / refLevel * uvLevel;
//Convert the voltage to a UV intensity level
float uvIntensity = _mapfloat(outputVoltage, 0.99, 2.8, 0.0, 15.0);
#if DEBUG == 1
Serial.print(F("UV I="));
Serial.print(_child_id);
Serial.print(F(" V="));
Serial.print(outputVoltage);
Serial.print(F(" I="));
Serial.println(uvIntensity);
#endif
// store the value
_value_float = uvIntensity;
}
// what to do as the main task when receiving a message
void SensorML8511::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) onLoop();
}
// The Arduino Map function but for floats
float SensorML8511::_mapfloat(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
/*
SensorACS712
*/
// contructor
SensorACS712::SensorACS712(int child_id, int pin): Sensor(child_id, pin) {
// set presentation, type and value type
setPresentation(S_MULTIMETER);
setType(V_CURRENT);
setValueType(TYPE_FLOAT);
}
// setter/getter
void SensorACS712::setmVPerAmp(int value) {
_mv_per_amp = value;
}
void SensorACS712::setOffset(int value) {
_ACS_offset = value;
}
// what to do during before
void SensorACS712::onBefore() {
// set the pin as input
pinMode(_pin, INPUT);
}
// what to do during setup
void SensorACS712::onSetup() {
}
// what to do during loop
void SensorACS712::onLoop() {
int value = analogRead(_pin);
// convert the analog read in mV
double voltage = (value / 1024.0) * 5000;
// convert voltage in amps
_value_float = ((voltage - _ACS_offset) / _mv_per_amp);
#if DEBUG == 1
Serial.print(F("ACS I="));
Serial.print(_child_id);
Serial.print(F(" A="));
Serial.println(_value_float);
#endif
}
// what to do as the main task when receiving a message
void SensorACS712::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) onLoop();
}
/*
SensorRainGauge
*/
// contructor
SensorRainGauge::SensorRainGauge(int child_id, int pin): Sensor(child_id, pin) {
// set presentation, type and value type
setPresentation(S_RAIN);
setType(V_RAIN);
setValueType(TYPE_FLOAT);
}
// initialize static variables
long SensorRainGauge::_last_tip = 0;
long SensorRainGauge::_count = 0;
// setter/getter
void SensorRainGauge::setReportInterval(int value) {
_report_interval = value;
}
void SensorRainGauge::setSingleTip(float value) {
_single_tip = value;
}
// what to do during before
void SensorRainGauge::onBefore() {
// set the pin as input and enabled pull up
pinMode(_pin, INPUT_PULLUP);
// attach to the pin's interrupt and execute the routine on falling
attachInterrupt(digitalPinToInterrupt(_pin), _onTipped, FALLING);
}
// what to do during setup
void SensorRainGauge::onSetup() {
}
// what to do when when receiving an interrupt
void SensorRainGauge::_onTipped() {
long now = millis();
// on tipping, two consecutive interrupts are received, ignore the second one
if ( (now - _last_tip > 100) || (now < _last_tip) ){
// increase the counter
_count++;
#if DEBUG == 1
Serial.println(F("RAIN+"));
#endif
}
_last_tip = now;
}
// what to do during loop
void SensorRainGauge::onLoop() {
// avoid reporting the same value multiple times
_value_float = -1;
long now = millis();
// time elapsed since the last report
long elapsed = now - _last_report;
// minimum time interval between reports
long min_interval = ((long)_report_interval*1000)*60;
// time to report or millis() reset
if ( (elapsed > min_interval) || (now < _last_report)) {
// report the total amount of rain for the last period
_value_float = _count*_single_tip;
#if DEBUG == 1
Serial.print(F("RAIN I="));
Serial.print(_child_id);
Serial.print(F(" T="));
Serial.println(_value_float);
#endif
// reset the counters
_count = 0;
_last_report = now;
}
}
// what to do as the main task when receiving a message
void SensorRainGauge::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) {
// report the total amount of rain for the last period
_value_float = _count*_single_tip;
}
}
/*
* SensorMQ
*/
SensorMQ::SensorMQ(int child_id, int pin): Sensor(child_id,pin) {
setPresentation(S_AIR_QUALITY);
setType(V_LEVEL);
}
//setter/getter
void SensorMQ::setRlValue(float value) {
_rl_value = value;
}
void SensorMQ::setRoValue(float value) {
_ro = value;
}
void SensorMQ::setCleanAirFactor(float value) {
_ro_clean_air_factor = value;
}
void SensorMQ::setCalibrationSampleTimes(int value) {
_calibration_sample_times = value;
}
void SensorMQ::setCalibrationSampleInterval(int value){
_calibration_sample_interval = value;
}
void SensorMQ::setReadSampleTimes(int value) {
_read_sample_times = value;
}
void SensorMQ::setReadSampleInterval(int value) {
_read_sample_interval = value;
}
void SensorMQ::setLPGCurve(float *value) {
_LPGCurve[0] = value[0];
_LPGCurve[2] = value[1];
_LPGCurve[2] = value[2];
}
void SensorMQ::setCOCurve(float *value) {
_COCurve[0] = value[0];
_COCurve[2] = value[1];
_COCurve[2] = value[2];
}
void SensorMQ::setSmokeCurve(float *value) {
_SmokeCurve[0] = value[0];
_SmokeCurve[2] = value[1];
_SmokeCurve[2] = value[2];
}
// what to do during before
void SensorMQ::onBefore() {
// prepare the pin for input
pinMode(_pin, INPUT);
}
// what to do during setup
void SensorMQ::onSetup() {
_ro = _MQCalibration();
}
// what to do during loop
void SensorMQ::onLoop() {
if (_pin == -1) return;
// calculate rs/ro
float mq = _MQRead()/_ro;
// calculate the ppm
float lpg = _MQGetGasPercentage(mq,_gas_lpg);
float co = _MQGetGasPercentage(mq,_gas_co);
float smoke = _MQGetGasPercentage(mq,_gas_smoke);
// assign to the value the requested gas
uint16_t value;
if (_target_gas == _gas_lpg) value = lpg;
if (_target_gas == _gas_co) value = co;
if (_target_gas == _gas_smoke) value = smoke;
#if DEBUG == 1
Serial.print(F("MQ I="));
Serial.print(_child_id);
Serial.print(F(" V="));
Serial.print(value);
Serial.print(F(" LPG="));
Serial.print(lpg);
Serial.print(F(" CO="));
Serial.print(co);
Serial.print(F(" SMOKE="));
Serial.println(smoke);
#endif
// store the value
_value_int = (int16_t)ceil(value);
}
// what to do as the main task when receiving a message
void SensorMQ::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) onLoop();
}
// returns the calculated sensor resistance
float SensorMQ::_MQResistanceCalculation(int raw_adc) {
return ( ((float)_rl_value*(1023-raw_adc)/raw_adc));
}
// This function assumes that the sensor is in clean air
float SensorMQ::_MQCalibration() {
int i;
float val=0;
//take multiple samples
for (i=0; i< _calibration_sample_times; i++) {
val += _MQResistanceCalculation(analogRead(_pin));
wait(_calibration_sample_interval);
}
//calculate the average value
val = val/_calibration_sample_times;
//divided by RO_CLEAN_AIR_FACTOR yields the Ro
val = val/_ro_clean_air_factor;
//according to the chart in the datasheet
return val;
}
// This function use MQResistanceCalculation to caculate the sensor resistenc (Rs).
float SensorMQ::_MQRead() {
int i;
float rs=0;
for (i=0; i<_read_sample_times; i++) {
rs += _MQResistanceCalculation(analogRead(_pin));
wait(_read_sample_interval);
}
rs = rs/_read_sample_times;
return rs;
}
// This function passes different curves to the MQGetPercentage function which calculates the ppm (parts per million) of the target gas.
int SensorMQ::_MQGetGasPercentage(float rs_ro_ratio, int gas_id) {
if ( gas_id == _gas_lpg ) {
return _MQGetPercentage(rs_ro_ratio,_LPGCurve);
} else if ( gas_id == _gas_co) {
return _MQGetPercentage(rs_ro_ratio,_COCurve);
} else if ( gas_id == _gas_smoke) {
return _MQGetPercentage(rs_ro_ratio,_SmokeCurve);
}
return 0;
}
// returns ppm of the target gas
int SensorMQ::_MQGetPercentage(float rs_ro_ratio, float *pcurve) {
return (pow(10,( ((log10(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0])));
}
/*
SensorDigitalInput
*/
// contructor
SensorDigitalInput::SensorDigitalInput(int child_id, int pin): Sensor(child_id, pin) {
}
// what to do during before
void SensorDigitalInput::onBefore() {
// set the pin for input
pinMode(_pin, INPUT);
}
// what to do during setup
void SensorDigitalInput::onSetup() {
}
// what to do during loop
void SensorDigitalInput::onLoop() {
// read the value
int value = digitalRead(_pin);
#if DEBUG == 1
Serial.print(F("D-IN I="));
Serial.print(_child_id);
Serial.print(F(" P="));
Serial.print(_pin);
Serial.print(F(" V="));
Serial.println(value);
#endif
// store the value
_value_int = value;
}
// what to do as the main task when receiving a message
void SensorDigitalInput::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) onLoop();
}
/*
SensorDigitalOutput
*/
// contructor
SensorDigitalOutput::SensorDigitalOutput(int child_id, int pin): Sensor(child_id, pin) {
}
// what to do during before
void SensorDigitalOutput::onBefore() {
// set the pin as output and initialize it accordingly
pinMode(_pin, OUTPUT);
_state = _initial_value == LOW ? LOW : HIGH;
digitalWrite(_pin, _state);
// the initial value is now the current value
_value_int = _initial_value;
}
// what to do during setup
void SensorDigitalOutput::onSetup() {
}
// setter/getter
void SensorDigitalOutput::setInitialValue(int value) {
_initial_value = value;
}
void SensorDigitalOutput::setPulseWidth(int value) {
_pulse_width = value;
}
void SensorDigitalOutput::setOnValue(int value) {
_on_value = value;
}
void SensorDigitalOutput::setLegacyMode(bool value) {
_legacy_mode = value;
}
// main task
void SensorDigitalOutput::onLoop() {
// do nothing on loop
}
// what to do as the main task when receiving a message
void SensorDigitalOutput::onReceive(const MyMessage & message) {
// by default handle a SET message but when legacy mode is set when a REQ message is expected instead
if ( (message.getCommand() == C_SET && ! _legacy_mode) || (message.getCommand() == C_REQ && _legacy_mode)) {
// retrieve from the message the value to set
int value = message.getInt();
if (value != 0 && value != 1) return;
#if DEBUG == 1
Serial.print(F("DOUT I="));
Serial.print(_child_id);
Serial.print(F(" P="));
Serial.print(_pin);
Serial.print(F(" V="));
Serial.print(value);
Serial.print(F(" P="));
Serial.println(_pulse_width);
#endif
// reverse the value if needed
int value_to_write = value;
if (_on_value == LOW) {
if (value == HIGH) value_to_write = LOW;
if (value == LOW) value_to_write = HIGH;
}
// set the value
digitalWrite(_pin, value_to_write);
if (_pulse_width > 0) {
// if this is a pulse output, restore the value to the original value after the pulse
wait(_pulse_width);
digitalWrite(_pin, value_to_write == 0 ? HIGH: LOW);
}
// store the current value so it will be sent to the controller
_state = value;
_value_int = value;
}
if (message.getCommand() == C_REQ && ! _legacy_mode) {
// return the current status
_value_int = _state;
}
}
/*
SensorRelay
*/
// contructor
SensorRelay::SensorRelay(int child_id, int pin): SensorDigitalOutput(child_id, pin) {
// set presentation and type
setPresentation(S_BINARY);
setType(V_STATUS);
}
// define what to do during loop
void SensorRelay::onLoop() {
// set the value to -1 so to avoid reporting to the gateway during loop
_value_int = -1;
}
/*
SensorLatchingRelay
*/
// contructor
SensorLatchingRelay::SensorLatchingRelay(int child_id, int pin): SensorRelay(child_id, pin) {
// like a sensor with a default pulse set
setPulseWidth(50);
}
/*
SensorDHT
*/
#if MODULE_DHT == 1
// contructor
SensorDHT::SensorDHT(int child_id, int pin, DHT* dht, int sensor_type, int dht_type): Sensor(child_id, pin) {
// store the dht object
_dht = dht;
_sensor_type = sensor_type;
_dht_type = dht_type;
if (_sensor_type == SensorDHT::TEMPERATURE) {
// temperature sensor
setPresentation(S_TEMP);
setType(V_TEMP);
setValueType(TYPE_FLOAT);
}
else if (_sensor_type == SensorDHT::HUMIDITY) {
// humidity sensor
setPresentation(S_HUM);
setType(V_HUM);
setValueType(TYPE_FLOAT);
}
}
// what to do during before
void SensorDHT::onBefore() {
// initialize the dht library
_dht->begin();
}
// what to do during setup
void SensorDHT::onSetup() {
}
// what to do during loop
void SensorDHT::onLoop() {
// temperature sensor
if (_sensor_type == SensorDHT::TEMPERATURE) {
// read the temperature
float temperature = _dht->readTemperature();
// convert it
if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32;
#if DEBUG == 1
Serial.print(F("DHT I="));
Serial.print(_child_id);
Serial.print(F(" T="));
Serial.println(temperature);
#endif
// store the value
if (! isnan(temperature)) _value_float = temperature;
}
// humidity sensor
else if (_sensor_type == SensorDHT::HUMIDITY) {
// read humidity
float humidity = _dht->readHumidity();
if (isnan(humidity)) return;
#if DEBUG == 1
Serial.print(F("DHT I="));
Serial.print(_child_id);
Serial.print(F(" H="));
Serial.println(humidity);
#endif
// store the value
if (! isnan(humidity)) _value_float = humidity;
}
}
// what to do as the main task when receiving a message
void SensorDHT::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) onLoop();
}
#endif
/*
SensorSHT21
*/
#if MODULE_SHT21 == 1
// contructor
SensorSHT21::SensorSHT21(int child_id, int sensor_type): Sensor(child_id,A2) {
// store the sensor type (0: temperature, 1: humidity)
_sensor_type = sensor_type;
if (_sensor_type == SensorSHT21::TEMPERATURE) {
// temperature sensor
setPresentation(S_TEMP);
setType(V_TEMP);
setValueType(TYPE_FLOAT);
}
else if (_sensor_type == SensorSHT21::HUMIDITY) {
// humidity sensor
setPresentation(S_HUM);
setType(V_HUM);
setValueType(TYPE_FLOAT);
}
}
// what to do during before
void SensorSHT21::onBefore() {
// initialize the library
Wire.begin();
}
// what to do during setup
void SensorSHT21::onSetup() {
}
// what to do during loop
void SensorSHT21::onLoop() {
// temperature sensor
if (_sensor_type == SensorSHT21::TEMPERATURE) {
// read the temperature
float temperature = SHT2x.GetTemperature();
// convert it
if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32;
#if DEBUG == 1
Serial.print(F("SHT I="));
Serial.print(_child_id);
Serial.print(F(" T="));
Serial.println(temperature);
#endif
// store the value
if (! isnan(temperature)) _value_float = temperature;
}
// Humidity Sensor
else if (_sensor_type == SensorSHT21::HUMIDITY) {
// read humidity
float humidity = SHT2x.GetHumidity();
if (isnan(humidity)) return;
#if DEBUG == 1
Serial.print(F("SHT I="));
Serial.print(_child_id);
Serial.print(F(" H="));
Serial.println(humidity);
#endif
// store the value
if (! isnan(humidity)) _value_float = humidity;
}
}
// what to do as the main task when receiving a message
void SensorSHT21::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) onLoop();
}
#endif
/*
* SensorHTU21D
*/
#if MODULE_SHT21 == 1
// constructor
SensorHTU21D::SensorHTU21D(int child_id, int pin): SensorSHT21(child_id, pin) {
}
#endif
/*
* SensorSwitch
*/
SensorSwitch::SensorSwitch(int child_id, int pin): Sensor(child_id,pin) {
setType(V_TRIPPED);
}
// setter/getter
void SensorSwitch::setMode(int value) {
_mode = value;
}
int SensorSwitch::getMode() {
return _mode;
}
void SensorSwitch::setDebounce(int value) {
_debounce = value;
}
void SensorSwitch::setTriggerTime(int value) {
_trigger_time = value;
}
void SensorSwitch::setInitial(int value) {
_initial = value;
}
int SensorSwitch::getInitial() {
return _initial;
}
// what to do during before
void SensorSwitch::onBefore() {
// initialize the value
if (_mode == RISING) _value_int = LOW;
else if (_mode == FALLING) _value_int = HIGH;
}
// what to do during setup
void SensorSwitch::onSetup() {
}
// what to do during loop
void SensorSwitch::onLoop() {
// wait to ensure the the input is not floating
if (_debounce > 0) wait(_debounce);
// read the value of the pin
int value = digitalRead(_pin);
// process the value
if ( (_mode == RISING && value == HIGH ) || (_mode == FALLING && value == LOW) || (_mode == CHANGE) ) {
#if DEBUG == 1
Serial.print(F("SWITCH I="));
Serial.print(_child_id);
Serial.print(F(" P="));
Serial.print(_pin);
Serial.print(F(" V="));
Serial.println(value);
#endif
_value_int = value;
// allow the signal to be restored to its normal value
if (_trigger_time > 0) wait(_trigger_time);
} else {
// invalid
_value_int = -1;
}
}
// what to do as the main task when receiving a message
void SensorSwitch::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) onLoop();
}
/*
* SensorDoor
*/
SensorDoor::SensorDoor(int child_id, int pin): SensorSwitch(child_id,pin) {
setPresentation(S_DOOR);
}
/*
* SensorMotion
*/
SensorMotion::SensorMotion(int child_id, int pin): SensorSwitch(child_id,pin) {
setPresentation(S_MOTION);
// capture only when it triggers
setMode(RISING);
// set initial value to LOW
setInitial(LOW);
}
/*
SensorDs18b20
*/
#if MODULE_DS18B20 == 1
// contructor
SensorDs18b20::SensorDs18b20(int child_id, int pin, DallasTemperature* sensors, int index): Sensor(child_id, pin) {
setPresentation(S_TEMP);
setType(V_TEMP);
setValueType(TYPE_FLOAT);
_index = index;
_sensors = sensors;
// retrieve and store the address from the index
_sensors->getAddress(_device_address, index);
}
// what to do during before
void SensorDs18b20::onBefore() {
}
// what to do during setup
void SensorDs18b20::onSetup() {
}
// what to do during loop
void SensorDs18b20::onLoop() {
// do not wait for conversion, will sleep manually during it
if (_sleep_during_conversion) _sensors->setWaitForConversion(false);
// request the temperature
_sensors->requestTemperatures();
if (_sleep_during_conversion) {
// calculate conversion time and sleep
int16_t conversion_time = _sensors->millisToWaitForConversion(_sensors->getResolution());
sleep(conversion_time);
}
// read the temperature
float temperature = _sensors->getTempCByIndex(_index);
// convert it
if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32;
#if DEBUG == 1
Serial.print(F("DS18B20 I="));
Serial.print(_child_id);
Serial.print(F(" T="));
Serial.println(temperature);
#endif
// store the value
_value_float = temperature;
}
// what to do as the main task when receiving a message
void SensorDs18b20::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) onLoop();
}
// function to print a device address
DeviceAddress* SensorDs18b20::getDeviceAddress() {
return &_device_address;
}
// returns the sensor's resolution in bits
int SensorDs18b20::getResolution() {
return _sensors->getResolution(_device_address);
}
// set the sensor's resolution in bits
void SensorDs18b20::setResolution(int value) {
_sensors->setResolution(_device_address, value);
}
// sleep while DS18B20 calculates temperature
void SensorDs18b20::setSleepDuringConversion(bool value) {
_sleep_during_conversion = value;
}
#endif
/*
SensorBH1750
*/
#if MODULE_BH1750 == 1
// contructor
SensorBH1750::SensorBH1750(int child_id): Sensor(child_id,A4) {
setPresentation(S_LIGHT_LEVEL);
setType(V_LEVEL);
_lightSensor = new BH1750();
}
// what to do during before
void SensorBH1750::onBefore() {
_lightSensor->begin();
}
// what to do during setup
void SensorBH1750::onSetup() {
}
// what to do during loop
void SensorBH1750::onLoop() {
// request the light level
_value_int = _lightSensor->readLightLevel();
#if DEBUG == 1
Serial.print(F("BH1 I="));
Serial.print(_child_id);
Serial.print(F(" L="));
Serial.println(_value_int);
#endif
}
// what to do as the main task when receiving a message
void SensorBH1750::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) onLoop();
}
#endif
/*
SensorMLX90614
*/
#if MODULE_MLX90614 == 1
// contructor
SensorMLX90614::SensorMLX90614(int child_id, Adafruit_MLX90614* mlx, int sensor_type): Sensor(child_id,A4) {
_sensor_type = sensor_type;
_mlx = mlx;
// set presentation and type
setPresentation(S_TEMP);
setType(V_TEMP);
setValueType(TYPE_FLOAT);
}
// what to do during before
void SensorMLX90614::onBefore() {
// initialize the library
_mlx->begin();
}
// what to do during setup
void SensorMLX90614::onSetup() {
}
// what to do during loop
void SensorMLX90614::onLoop() {
float temperature = _sensor_type == SensorMLX90614::TEMPERATURE_OBJECT ? _mlx->readAmbientTempC() : _mlx->readObjectTempC();
// convert it
if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32;
#if DEBUG == 1
Serial.print(F("MLX I="));
Serial.print(_child_id);
Serial.print(F(" T="));
Serial.println(temperature);
#endif
if (! isnan(temperature)) _value_float = temperature;
}
// what to do as the main task when receiving a message
void SensorMLX90614::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) onLoop();
}
#endif
/*
SensorBosch
*/
#if MODULE_BME280 == 1 || MODULE_BMP085 == 1
// contructor
SensorBosch::SensorBosch(int child_id, int sensor_type): Sensor(child_id,A4) {
_sensor_type = sensor_type;
if (_sensor_type == SensorBosch::TEMPERATURE) {
// temperature sensor
setPresentation(S_TEMP);
setType(V_TEMP);
setValueType(TYPE_FLOAT);
}
else if (_sensor_type == SensorBosch::HUMIDITY) {
// humidity sensor
setPresentation(S_HUM);
setType(V_HUM);
setValueType(TYPE_FLOAT);
}
else if (_sensor_type == SensorBosch::PRESSURE) {
// pressure sensor
setPresentation(S_BARO);
setType(V_PRESSURE);
setValueType(TYPE_FLOAT);
}
else if (_sensor_type == SensorBosch::FORECAST) {
// pressure sensor
setPresentation(S_BARO);
setType(V_FORECAST);
setValueType(TYPE_STRING);
}
}
// setter/getter
void SensorBosch::setForecastSamplesCount(int value) {
_forecast_samples_count = value;
}
// what to do during before
void SensorBosch::onBefore() {
// initialize the forecast samples array
_forecast_samples = new float[_forecast_samples_count];
}
// what to do during setup
void SensorBosch::onSetup() {
}
// what to do during loop
void SensorBosch::onLoop() {
}
// what to do as the main task when receiving a message
void SensorBosch::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) onLoop();
}
void SensorBosch::_forecast(float pressure) {
if (isnan(pressure)) return;
// Calculate the average of the last n minutes.
int index = _minute_count % _forecast_samples_count;
_forecast_samples[index] = pressure;
_minute_count++;
if (_minute_count > 185) _minute_count = 6;
if (_minute_count == 5) _pressure_avg = _getLastPressureSamplesAverage();
else if (_minute_count == 35) {
float last_pressure_avg = _getLastPressureSamplesAverage();
float change = (last_pressure_avg - _pressure_avg) * 0.1;
// first time initial 3 hour
if (_first_round) _dP_dt = change * 2; // note this is for t = 0.5hour
else _dP_dt = change / 1.5; // divide by 1.5 as this is the difference in time from 0 value.
}
else if (_minute_count == 65) {
float last_pressure_avg = _getLastPressureSamplesAverage();
float change = (last_pressure_avg - _pressure_avg) * 0.1;
//first time initial 3 hour
if (_first_round) _dP_dt = change; //note this is for t = 1 hour
else _dP_dt = change / 2; //divide by 2 as this is the difference in time from 0 value
}
else if (_minute_count == 95) {
float last_pressure_avg = _getLastPressureSamplesAverage();
float change = (last_pressure_avg - _pressure_avg) * 0.1;
// first time initial 3 hour
if (_first_round)_dP_dt = change / 1.5; // note this is for t = 1.5 hour
else _dP_dt = change / 2.5; // divide by 2.5 as this is the difference in time from 0 value
}
else if (_minute_count == 125) {
float last_pressure_avg = _getLastPressureSamplesAverage();
// store for later use.
_pressure_avg2 = last_pressure_avg;
float change = (last_pressure_avg - _pressure_avg) * 0.1;
if (_first_round) _dP_dt = change / 2; // note this is for t = 2 hour
else _dP_dt = change / 3; // divide by 3 as this is the difference in time from 0 value
}
else if (_minute_count == 155) {
float last_pressure_avg = _getLastPressureSamplesAverage();
float change = (last_pressure_avg - _pressure_avg) * 0.1;
if (_first_round) _dP_dt = change / 2.5; // note this is for t = 2.5 hour
else _dP_dt = change / 3.5; // divide by 3.5 as this is the difference in time from 0 value
}
else if (_minute_count == 185) {
float last_pressure_avg = _getLastPressureSamplesAverage();
float change = (last_pressure_avg - _pressure_avg) * 0.1;
if (_first_round) _dP_dt = change / 3; // note this is for t = 3 hour
else _dP_dt = change / 4; // divide by 4 as this is the difference in time from 0 value
}
// Equating the pressure at 0 to the pressure at 2 hour after 3 hours have past.
_pressure_avg = _pressure_avg2;
// flag to let you know that this is on the past 3 hour mark. Initialized to 0 outside main loop.
_first_round = false;
// calculate the forecast (STABLE = 0, SUNNY = 1, CLOUDY = 2, UNSTABLE = 3, THUNDERSTORM = 4, UNKNOWN = 5)
int forecast = 5;
//if time is less than 35 min on the first 3 hour interval.
if (_minute_count < 35 && _first_round) forecast = 5;
else if (_dP_dt < (-0.25)) forecast = 5;
else if (_dP_dt > 0.25) forecast = 4;
else if ((_dP_dt > (-0.25)) && (_dP_dt < (-0.05))) forecast = 2;
else if ((_dP_dt > 0.05) && (_dP_dt < 0.25)) forecast = 1;
else if ((_dP_dt >(-0.05)) && (_dP_dt < 0.05)) forecast = 0;
else forecast = 5;
_value_string = _weather[forecast];
#if DEBUG == 1
Serial.print(F("BMP I="));
Serial.print(_child_id);
Serial.print(F(" M="));
Serial.print(_minute_count);
Serial.print(F(" dP="));
Serial.print(_dP_dt);
Serial.print(F(" F="));
Serial.println(_value_string);
#endif
}
// returns the average of the latest pressure samples
float SensorBosch::_getLastPressureSamplesAverage() {
float avg = 0;
for (int i = 0; i < _forecast_samples_count; i++) avg += _forecast_samples[i];
avg /= _forecast_samples_count;
return avg;
}
// search for a given chip on i2c bus
uint8_t SensorBosch::GetI2CAddress(uint8_t chip_id) {
uint8_t addresses[] = {0x77, 0x76};
uint8_t register_address = 0xD0;
for (int i = 0; i <= sizeof(addresses); i++) {
uint8_t i2c_address = addresses[i];
uint8_t value;
Wire.beginTransmission((uint8_t)i2c_address);
Wire.write((uint8_t)register_address);
Wire.endTransmission();
Wire.requestFrom((uint8_t)i2c_address, (byte)1);
value = Wire.read();
if (value == chip_id) {
#if DEBUG == 1
Serial.print(F("I2C="));
Serial.println(i2c_address);
#endif
return i2c_address;
}
}
return addresses[0];
}
#endif
/*
* SensorBME280
*/
#if MODULE_BME280 == 1
SensorBME280::SensorBME280(int child_id, Adafruit_BME280* bme, int sensor_type): SensorBosch(child_id,sensor_type) {
_bme = bme;
}
void SensorBME280::onLoop() {
// temperature sensor
if (_sensor_type == SensorBME280::TEMPERATURE) {
// read the temperature
float temperature = _bme->readTemperature();
// convert it
if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32;
#if DEBUG == 1
Serial.print(F("BME I="));
Serial.print(_child_id);
Serial.print(F(" T="));
Serial.println(temperature);
#endif
if (isnan(temperature)) return;
// store the value
_value_float = temperature;
}
// Humidity Sensor
else if (_sensor_type == SensorBME280::HUMIDITY) {
// read humidity
float humidity = _bme->readHumidity();
#if DEBUG == 1
Serial.print(F("BME I="));
Serial.print(_child_id);
Serial.print(F(" H="));
Serial.println(humidity);
#endif
if (isnan(humidity)) return;
// store the value
_value_float = humidity;
}
// Pressure Sensor
else if (_sensor_type == SensorBME280::PRESSURE) {
// read pressure
float pressure = _bme->readPressure() / 100.0F;
if (isnan(pressure)) return;
#if DEBUG == 1
Serial.print(F("BME I="));
Serial.print(_child_id);
Serial.print(F(" P="));
Serial.println(pressure);
#endif
if (isnan(pressure)) return;
// store the value
_value_float = pressure;
}
// Forecast Sensor
else if (_sensor_type == SensorBME280::FORECAST) {
float pressure = _bme->readPressure() / 100.0F;
_forecast(pressure);
}
}
#endif
/*
SensorBMP085
*/
#if MODULE_BMP085 == 1
// contructor
SensorBMP085::SensorBMP085(int child_id, Adafruit_BMP085* bmp, int sensor_type): SensorBosch(child_id,sensor_type) {
_bmp = bmp;
}
// what to do during loop
void SensorBMP085::onLoop() {
// temperature sensor
if (_sensor_type == SensorBMP085::TEMPERATURE) {
// read the temperature
float temperature = _bmp->readTemperature();
// convert it
if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32;
#if DEBUG == 1
Serial.print(F("BMP I="));
Serial.print(_child_id);
Serial.print(F(" T="));
Serial.println(temperature);
#endif
if (isnan(temperature)) return;
// store the value
_value_float = temperature;
}
// Pressure Sensor
else if (_sensor_type == SensorBMP085::PRESSURE) {
// read pressure
float pressure = _bmp->readPressure() / 100.0F;
#if DEBUG == 1
Serial.print(F("BMP I="));
Serial.print(_child_id);
Serial.print(F(" P="));
Serial.println(pressure);
#endif
if (isnan(pressure)) return;
// store the value
_value_float = pressure;
}
// Forecast Sensor
else if (_sensor_type == SensorBMP085::FORECAST) {
float pressure = _bmp->readPressure() / 100.0F;
_forecast(pressure);
}
}
#endif
/*
SensorSonoff
*/
#if MODULE_SONOFF == 1
// contructor
SensorSonoff::SensorSonoff(int child_id): Sensor(child_id,1) {
setPresentation(S_BINARY);
setType(V_STATUS);
}
// setter/getter
void SensorSonoff::setButtonPin(int value) {
_button_pin = value;
}
void SensorSonoff::setRelayPin(int value) {
_relay_pin = value;
}
void SensorSonoff::setLedPin(int value) {
_led_pin = value;
}
// what to do during before
void SensorSonoff::onBefore() {
}
// what to do during setup
void SensorSonoff::onSetup() {
// Setup the button
pinMode(_button_pin, INPUT_PULLUP);
// After setting up the button, setup debouncer
_debouncer.attach(_button_pin);
_debouncer.interval(5);
// Make sure relays and LED are off when starting up
digitalWrite(_relay_pin, _relay_off);
digitalWrite(_led_pin, _led_off);
// Then set relay pins in output mode
pinMode(_relay_pin, OUTPUT);
pinMode(_led_pin, OUTPUT);
_blink();
}
// what to do during loop
void SensorSonoff::onLoop() {
// set the value to -1 so to avoid reporting to the gateway during loop
_value_int = -1;
_debouncer.update();
// Get the update value from the button
int value = _debouncer.read();
if (value != _old_value && value == 0) {
// button pressed, toggle the state
_toggle();
}
_old_value = value;
}
// what to do as the main task when receiving a message
void SensorSonoff::onReceive(const MyMessage & message) {
if (message.getCommand() == C_SET) {
// retrieve from the message the value to set
int value = message.getInt();
if (value != 0 && value != 1 || value == _state) return;
// toggle the state
_toggle();
}
if (message.getCommand() == C_REQ) {
// return the current state
_value_int = _state;
}
}
// toggle the state
void SensorSonoff::_toggle() {
// toggle the state
_state = _state ? false : true;
// Change relay state
digitalWrite(_relay_pin, _state? _relay_on: _relay_off);
// Change LED state
digitalWrite(_led_pin, _state? _led_on: _led_off);
#if DEBUG == 1
Serial.print(F("SONOFF I="));
Serial.print(_child_id);
Serial.print(F(" V="));
Serial.println(_state);
#endif
_value_int = _state;
}
// blink the led
void SensorSonoff::_blink() {
digitalWrite(_led_pin, digitalRead(_led_pin) ? _led_on : _led_off);
wait(200);
digitalWrite(_led_pin, digitalRead(_led_pin) ? _led_on : _led_off);
wait(200);
digitalWrite(_led_pin, digitalRead(_led_pin) ? _led_on : _led_off);
wait(200);
digitalWrite(_led_pin, digitalRead(_led_pin) ? _led_on : _led_off);
}
#endif
/*
SensorHCSR04
*/
#if MODULE_HCSR04 == 1
// contructor
SensorHCSR04::SensorHCSR04(int child_id, int pin): Sensor(child_id, pin) {
// set presentation and type
setPresentation(S_DISTANCE);
setType(V_DISTANCE);
_trigger_pin = pin;
_echo_pin = pin;
}
// what to do during before
void SensorHCSR04::onBefore() {
// initialize the library
_sonar = new NewPing(_trigger_pin,_echo_pin,_max_distance);
}
// setter/getter
void SensorHCSR04::setTriggerPin(int value) {
_trigger_pin = value;
}
void SensorHCSR04::setEchoPin(int value) {
_echo_pin = value;
}
void SensorHCSR04::setMaxDistance(int value) {
_max_distance = value;
}
// what to do during setup
void SensorHCSR04::onSetup() {
}
// what to do during loop
void SensorHCSR04::onLoop() {
int distance = getControllerConfig().isMetric ? _sonar->ping_cm() : _sonar->ping_in();
#if DEBUG == 1
Serial.print(F("HC I="));
Serial.print(_child_id);
Serial.print(F(" D="));
Serial.println(distance);
#endif
_value_int = distance;
}
// what to do as the main task when receiving a message
void SensorHCSR04::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) onLoop();
}
#endif
/*
SensorMCP9808
*/
#if MODULE_MCP9808 == 1
// contructor
SensorMCP9808::SensorMCP9808(int child_id, Adafruit_MCP9808* mcp): Sensor(child_id,A2) {
_mcp = mcp;
setPresentation(S_TEMP);
setType(V_TEMP);
setValueType(TYPE_FLOAT);
}
// what to do during before
void SensorMCP9808::onBefore() {
}
// what to do during setup
void SensorMCP9808::onSetup() {
}
// what to do during loop
void SensorMCP9808::onLoop() {
float temperature = _mcp->readTempC();
// convert it
if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32;
#if DEBUG == 1
Serial.print(F("MCP I="));
Serial.print(_child_id);
Serial.print(F(" T="));
Serial.println(temperature);
#endif
// store the value
if (! isnan(temperature)) _value_float = temperature;
}
// what to do as the main task when receiving a message
void SensorMCP9808::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) onLoop();
}
#endif
/*******************************************
NodeManager
*/
// initialize the node manager
NodeManager::NodeManager() {
// setup the service message container
_msg = MyMessage(CONFIGURATION_CHILD_ID, V_CUSTOM);
}
// setter/getter
void NodeManager::setRetries(int value) {
_retries = value;
}
#if BATTERY_MANAGER == 1
void NodeManager::setBatteryMin(float value) {
_battery_min = value;
}
void NodeManager::setBatteryMax(float value) {
_battery_max = value;
}
void NodeManager::setBatteryReportCycles(int value) {
_battery_report_cycles = value;
}
void NodeManager::setBatteryInternalVcc(bool value) {
_battery_internal_vcc = value;
}
void NodeManager::setBatteryPin(int value) {
_battery_pin = value;
}
void NodeManager::setBatteryVoltsPerBit(float value) {
_battery_volts_per_bit = value;
}
void NodeManager::setBatteryReportWithInterrupt(bool value) {
_battery_report_with_interrupt = value;
}
#endif
void NodeManager::setSleepMode(int value) {
_sleep_mode = value;
}
void NodeManager::setMode(int value) {
setSleepMode(value);
}
void NodeManager::setSleepTime(int value) {
_sleep_time = value;
}
void NodeManager::setSleepUnit(int value) {
_sleep_unit = value;
}
void NodeManager::setSleep(int value1, int value2, int value3) {
_sleep_mode = value1;
_sleep_time = value2;
_sleep_unit = value3;
}
void NodeManager::setSleepInterruptPin(int value) {
_sleep_interrupt_pin = value;
}
void NodeManager::setInterrupt(int pin, int mode, int pull) {
if (pin == INTERRUPT_PIN_1) {
_interrupt_1_mode = mode;
_interrupt_1_pull = pull;
}
if (pin == INTERRUPT_PIN_2) {
_interrupt_2_mode = mode;
_interrupt_2_pull = pull;
}
}
#if POWER_MANAGER == 1
void NodeManager::setPowerPins(int ground_pin, int vcc_pin, int wait_time) {
_powerManager.setPowerPins(ground_pin, vcc_pin, wait_time);
}
void NodeManager::setAutoPowerPins(bool value) {
_auto_power_pins = value;
}
void NodeManager::powerOn() {
_powerManager.powerOn();
}
void NodeManager::powerOff() {
_powerManager.powerOff();
}
#endif
void NodeManager::setSleepBetweenSend(int value) {
_sleep_between_send = value;
}
void NodeManager::setAck(bool value) {
_ack = value;
}
// register a sensor to this manager
int NodeManager::registerSensor(int sensor_type, int pin, int child_id) {
// get a child_id if not provided by the user
if (child_id < 0) child_id = _getAvailableChildId();
// based on the given sensor type instantiate the appropriate class
if (sensor_type == 0) return -1;
#if MODULE_ANALOG_INPUT == 1
else if (sensor_type == SENSOR_ANALOG_INPUT) return registerSensor(new SensorAnalogInput(child_id, pin));
else if (sensor_type == SENSOR_LDR) return registerSensor(new SensorLDR(child_id, pin));
else if (sensor_type == SENSOR_THERMISTOR) return registerSensor(new SensorThermistor(child_id, pin));
else if (sensor_type == SENSOR_MQ) return registerSensor(new SensorMQ(child_id, pin));
else if (sensor_type == SENSOR_ML8511) return registerSensor(new SensorML8511(child_id, pin));
else if (sensor_type == SENSOR_ACS712) return registerSensor(new SensorACS712(child_id, pin));
else if (sensor_type == SENSOR_RAIN_GAUGE) return registerSensor(new SensorRainGauge(child_id, pin));
#endif
#if MODULE_DIGITAL_INPUT == 1
else if (sensor_type == SENSOR_DIGITAL_INPUT) return registerSensor(new SensorDigitalInput(child_id, pin));
#endif
#if MODULE_DIGITAL_OUTPUT == 1
else if (sensor_type == SENSOR_DIGITAL_OUTPUT) return registerSensor(new SensorDigitalOutput(child_id, pin));
else if (sensor_type == SENSOR_RELAY) return registerSensor(new SensorRelay(child_id, pin));
else if (sensor_type == SENSOR_LATCHING_RELAY) return registerSensor(new SensorLatchingRelay(child_id, pin));
#endif
#if MODULE_DHT == 1
else if (sensor_type == SENSOR_DHT11 || sensor_type == SENSOR_DHT22) {
int dht_type = sensor_type == SENSOR_DHT11 ? DHT11 : DHT22;
DHT* dht = new DHT(pin,dht_type);
// register temperature sensor
registerSensor(new SensorDHT(child_id,pin,dht,SensorDHT::TEMPERATURE,dht_type));
// register humidity sensor
child_id = _getAvailableChildId();
return registerSensor(new SensorDHT(child_id,pin,dht,SensorDHT::HUMIDITY,dht_type));
}
#endif
#if MODULE_SHT21 == 1
else if (sensor_type == SENSOR_SHT21) {
// register temperature sensor
registerSensor(new SensorSHT21(child_id,SensorSHT21::TEMPERATURE));
// register humidity sensor
child_id = _getAvailableChildId();
return registerSensor(new SensorSHT21(child_id,SensorSHT21::HUMIDITY));
}
else if (sensor_type == SENSOR_HTU21D) {
// register temperature sensor
registerSensor(new SensorHTU21D(child_id,SensorHTU21D::TEMPERATURE));
// register humidity sensor
child_id = _getAvailableChildId();
return registerSensor(new SensorHTU21D(child_id,SensorHTU21D::HUMIDITY));
}
#endif
#if MODULE_SWITCH == 1
else if (sensor_type == SENSOR_SWITCH || sensor_type == SENSOR_DOOR || sensor_type == SENSOR_MOTION) {
// ensure an interrupt pin is provided
if (pin != INTERRUPT_PIN_1 && pin != INTERRUPT_PIN_2) return -1;
// register the sensor
int index = 0;
if (sensor_type == SENSOR_SWITCH) index = registerSensor(new SensorSwitch(child_id, pin));
else if (sensor_type == SENSOR_DOOR) index = registerSensor(new SensorDoor(child_id, pin));
else if (sensor_type == SENSOR_MOTION) index = registerSensor(new SensorMotion(child_id, pin));
// set an interrupt on the pin and set the initial value
SensorSwitch* sensor = (SensorSwitch*)getSensor(index);
sensor->setInterruptPin(pin);
setInterrupt(pin,sensor->getMode(),sensor->getInitial());
return index;
}
#endif
#if MODULE_DS18B20 == 1
else if (sensor_type == SENSOR_DS18B20) {
// initialize the library
OneWire* oneWire = new OneWire(pin);
DallasTemperature* sensors = new DallasTemperature(oneWire);
// initialize the sensors
sensors->begin();
int index = 0;
// register a new child for each sensor on the bus
for(int i = 0; i < sensors->getDeviceCount(); i++) {
if (i > 0) child_id = _getAvailableChildId();
index = registerSensor(new SensorDs18b20(child_id,pin,sensors,i));
}
return index;
}
#endif
#if MODULE_BH1750 == 1
else if (sensor_type == SENSOR_BH1750) {
return registerSensor(new SensorBH1750(child_id));
}
#endif
#if MODULE_MLX90614 == 1
else if (sensor_type == SENSOR_MLX90614) {
Adafruit_MLX90614* mlx = new Adafruit_MLX90614();
// register ambient temperature sensor
registerSensor(new SensorMLX90614(child_id,mlx,SensorMLX90614::TEMPERATURE_AMBIENT));
// register object temperature sensor
child_id = _getAvailableChildId();
return registerSensor(new SensorMLX90614(child_id,mlx,SensorMLX90614::TEMPERATURE_OBJECT));
}
#endif
#if MODULE_BME280 == 1
else if (sensor_type == SENSOR_BME280) {
Adafruit_BME280* bme = new Adafruit_BME280();
if (! bme->begin(SensorBosch::GetI2CAddress(0x60))) {
#if DEBUG == 1
Serial.println(F("NO BME"));
#endif
return -1;
}
// register temperature sensor
registerSensor(new SensorBME280(child_id,bme,SensorBME280::TEMPERATURE));
child_id = _getAvailableChildId();
// register humidity sensor
registerSensor(new SensorBME280(child_id,bme,SensorBME280::HUMIDITY));
// register pressure sensor
child_id = _getAvailableChildId();
registerSensor(new SensorBME280(child_id,bme,SensorBME280::PRESSURE));
// register forecast sensor
child_id = _getAvailableChildId();
return registerSensor(new SensorBME280(child_id,bme,SensorBME280::FORECAST));
}
#endif
#if MODULE_SONOFF == 1
else if (sensor_type == SENSOR_SONOFF) {
return registerSensor(new SensorSonoff(child_id));
}
#endif
#if MODULE_BMP085 == 1
else if (sensor_type == SENSOR_BMP085) {
Adafruit_BMP085* bmp = new Adafruit_BMP085();
if (! bmp->begin(SensorBosch::GetI2CAddress(0x55))) {
#if DEBUG == 1
Serial.println(F("NO BMP"));
#endif
return -1;
}
// register temperature sensor
registerSensor(new SensorBMP085(child_id,bmp,SensorBMP085::TEMPERATURE));
// register pressure sensor
child_id = _getAvailableChildId();
registerSensor(new SensorBMP085(child_id,bmp,SensorBMP085::PRESSURE));
// register forecast sensor
child_id = _getAvailableChildId();
return registerSensor(new SensorBMP085(child_id,bmp,SensorBMP085::FORECAST));
}
#endif
#if MODULE_HCSR04 == 1
else if (sensor_type == SENSOR_HCSR04) {
return registerSensor(new SensorHCSR04(child_id, pin));
}
#endif
#if MODULE_MCP9808 == 1
else if (sensor_type == SENSOR_MCP9808) {
Adafruit_MCP9808 * mcp = new Adafruit_MCP9808();
if (! mcp->begin()) {
#if DEBUG == 1
Serial.println(F("NO MCP"));
#endif
return -1;
}
// register temperature sensor
registerSensor(new SensorMCP9808(child_id,mcp));
}
#endif
else {
#if DEBUG == 1
Serial.print(F("INVALID "));
Serial.println(sensor_type);
#endif
return -1;
};
}
// attach a built-in or custom sensor to this manager
int NodeManager::registerSensor(Sensor* sensor) {
#if DEBUG == 1
Serial.print(F("REG I="));
Serial.print(sensor->getChildId());
Serial.print(F(" P="));
Serial.print(sensor->getPin());
Serial.print(F(" P="));
Serial.print(sensor->getPresentation());
Serial.print(F(" T="));
Serial.println(sensor->getType());
#endif
#if POWER_MANAGER == 1
// set auto power pin
sensor->setAutoPowerPins(_auto_power_pins);
#endif
// add the sensor to the array of registered sensors
_sensors[sensor->getChildId()] = sensor;
// return the child_id
return sensor->getChildId();
}
// un-register a sensor to this manager
void NodeManager::unRegisterSensor(int sensor_index) {
// unlink the pointer to this sensor
_sensors[sensor_index] == 0;
}
// return a sensor given its index
Sensor* NodeManager::get(int child_id) {
// return a pointer to the sensor from the given child_id
return _sensors[child_id];
}
Sensor* NodeManager::getSensor(int child_id) {
return get(child_id);
}
// assign a different child id to a sensor'
bool NodeManager::renameSensor(int old_child_id, int new_child_id) {
// ensure the old id exists and the new is available
if (_sensors[old_child_id] == 0 || _sensors[new_child_id] != 0) return false;
// assign the sensor to new id
_sensors[new_child_id] = _sensors[old_child_id];
// set the new child id
_sensors[new_child_id]->setChildId(new_child_id);
// free up the old id
_sensors[old_child_id] = 0;
return true;
}
// setup NodeManager
void NodeManager::before() {
#if DEBUG == 1
Serial.print(F("NodeManager v"));
Serial.println(VERSION);
#endif
// setup the sleep interrupt pin
if (_sleep_interrupt_pin > -1) {
// set the interrupt when the pin is connected to ground
setInterrupt(_sleep_interrupt_pin,FALLING,HIGH);
}
// setup the interrupt pins
if (_interrupt_1_mode != MODE_NOT_DEFINED) {
pinMode(INTERRUPT_PIN_1,INPUT);
if (_interrupt_1_pull > -1) digitalWrite(INTERRUPT_PIN_1,_interrupt_1_pull);
}
if (_interrupt_2_mode != MODE_NOT_DEFINED) {
pinMode(INTERRUPT_PIN_2, INPUT);
if (_interrupt_2_pull > -1) digitalWrite(INTERRUPT_PIN_2,_interrupt_2_pull);
}
#if DEBUG == 1
Serial.print(F("INT1 M="));
Serial.println(_interrupt_1_mode);
Serial.print(F("INT2 M="));
Serial.println(_interrupt_2_mode);
#endif
#if REMOTE_CONFIGURATION == 1 && PERSIST == 1
// restore sleep configuration from eeprom
if (loadState(EEPROM_SLEEP_SAVED) == 1) {
// sleep settings found in the eeprom, restore them
_sleep_mode = loadState(EEPROM_SLEEP_MODE);
_sleep_time = loadState(EEPROM_SLEEP_TIME_MINOR);
int major = loadState(EEPROM_SLEEP_TIME_MAJOR);
if (major == 1) _sleep_time = _sleep_time + 250;
else if (major == 2) _sleep_time = _sleep_time + 250 * 2;
else if (major == 3) _sleep_time = _sleep_time + 250 * 3;
_sleep_unit = loadState(EEPROM_SLEEP_UNIT);
#if DEBUG == 1
Serial.print(F("LOADSLP M="));
Serial.print(_sleep_mode);
Serial.print(F(" T="));
Serial.print(_sleep_time);
Serial.print(F(" U="));
Serial.println(_sleep_unit);
#endif
}
#endif
#if BATTERY_MANAGER == 1 && !defined(MY_GATEWAY_ESP8266)
// set analogReference to internal if measuring the battery through a pin
if (! _battery_internal_vcc && _battery_pin > -1) analogReference(INTERNAL);
#endif
// setup individual sensors
for (int i = 0; i < 255; i++) {
if (_sensors[i] == 0) continue;
// call each sensor's setup()
_sensors[i]->before();
}
}
// present NodeManager and its sensors
void NodeManager::presentation() {
#if DEBUG == 1
Serial.println(F("RADIO OK"));
#endif
// Send the sketch version information to the gateway and Controller
if (_sleep_between_send > 0) sleep(_sleep_between_send);
sendSketchInfo(SKETCH_NAME,SKETCH_VERSION);
// present the service as a custom sensor to the controller
_present(CONFIGURATION_CHILD_ID, S_CUSTOM);
#if BATTERY_MANAGER == 1 && BATTERY_SENSOR == 1
// present the battery service
_present(BATTERY_CHILD_ID, S_MULTIMETER);
// report battery level
_process("BATTERY");
#endif
// present each sensor
for (int i = 0; i < 255; i++) {
if (_sensors[i] == 0) continue;
// call each sensor's presentation()
if (_sleep_between_send > 0) sleep(_sleep_between_send);
_sensors[i]->presentation();
}
#if DEBUG == 1
Serial.println(F("READY"));
Serial.println("");
#endif
}
// setup NodeManager
void NodeManager::setup() {
#if DEBUG == 1
Serial.print(F("MY I="));
Serial.print(getNodeId());
Serial.print(F(" M="));
Serial.println(getControllerConfig().isMetric);
#endif
#if SERVICE_MESSAGES == 1
_send(_msg.set("STARTED"));
#endif
// run setup for all the registered sensors
for (int i = 0; i < 255; i++) {
if (_sensors[i] == 0) continue;
// call each sensor's setup()
_sensors[i]->setup();
}
}
// run the main function for all the register sensors
void NodeManager::loop() {
MyMessage empty;
// if in idle mode, do nothing
if (_sleep_mode == IDLE) return;
// if sleep time is not set, do nothing
if ((_sleep_mode == SLEEP || _sleep_mode == WAIT) && _sleep_time == 0) return;
#if POWER_MANAGER == 1
// turn on the pin powering all the sensors
if (_auto_power_pins) powerOn();
#endif
// run loop for all the registered sensors
for (int i = 0; i < 255; i++) {
// skip not configured sensors
if (_sensors[i] == 0) continue;
// if waking up from an interrupt skip all the sensor without that interrupt configured
if (_last_interrupt_pin != -1 && _sensors[i]->getInterruptPin() != _last_interrupt_pin) continue;
// call each sensor's loop()
_sensors[i]->loop(empty);
}
#if POWER_MANAGER == 1
// turn off the pin powering all the sensors
if (_auto_power_pins) powerOff();
#endif
// continue/start sleeping as requested
if (_sleep_mode == SLEEP || _sleep_mode == WAIT) _sleep();
}
// dispacth inbound messages
void NodeManager::receive(const MyMessage &message) {
#if DEBUG == 1
Serial.print(F("RECV S="));
Serial.print(message.sender);
Serial.print(F(" I="));
Serial.print(message.sensor);
Serial.print(F(" C="));
Serial.print(message.getCommand());
Serial.print(F(" T="));
Serial.print(message.type);
Serial.print(F(" P="));
Serial.println(message.getString());
#endif
// process incoming service messages
if (message.sensor == CONFIGURATION_CHILD_ID && message.getCommand() == C_REQ && message.type == V_CUSTOM) {
_process(message.getString());
}
// dispatch the message to the registered sensor
else if (_sensors[message.sensor] != 0) {
#if POWER_MANAGER == 1
// turn on the pin powering all the sensors
if (_auto_power_pins) powerOn();
#endif
// call the sensor's receive()
_sensors[message.sensor]->receive(message);
#if POWER_MANAGER == 1
// turn off the pin powering all the sensors
if (_auto_power_pins) powerOff();
#endif
}
}
// request and return the current timestamp from the controller
long NodeManager::getTimestamp() {
int retries = 3;
_timestamp = -1;
while (_timestamp == -1 && retries > 0) {
#if DEBUG == 1
Serial.println(F("TIME"));
#endif
// request the time to the controller
requestTime();
// keep asking every 1 second
wait(1000);
retries--;
}
return _timestamp;
}
// receive the time from the controller and save it
void NodeManager::receiveTime(unsigned long ts) {
_timestamp = ts;
#if DEBUG == 1
Serial.print(F("TIME T="));
Serial.print(_timestamp);
#endif
}
// send a message to the network
void NodeManager::_send(MyMessage & message) {
// send the message, multiple times if requested
for (int i = 0; i < _retries; i++) {
// if configured, sleep beetween each send
if (_sleep_between_send > 0) sleep(_sleep_between_send);
#if DEBUG == 1
Serial.print(F("SEND D="));
Serial.print(message.destination);
Serial.print(F(" I="));
Serial.print(message.sensor);
Serial.print(F(" C="));
Serial.print(message.getCommand());
Serial.print(F(" T="));
Serial.print(message.type);
Serial.print(F(" S="));
Serial.print(message.getString());
Serial.print(F(" I="));
Serial.print(message.getInt());
Serial.print(F(" F="));
Serial.println(message.getFloat());
#endif
send(message,_ack);
}
}
// process a service message
void NodeManager::_process(const char * message) {
// HELLO: hello request
if (strcmp(message, "HELLO") == 0) {
_send(_msg.set(message));
}
#if BATTERY_MANAGER == 1
// BATTERY: return the battery level
else if (strcmp(message, "BATTERY") == 0) {
// measure the board vcc
float volt = 0;
if (_battery_internal_vcc || _battery_pin == -1) volt = getVcc();
else volt = analogRead(_battery_pin) * _battery_volts_per_bit;
// calculate the percentage
int percentage = ((volt - _battery_min) / (_battery_max - _battery_min)) * 100;
if (percentage > 100) percentage = 100;
if (percentage < 0) percentage = 0;
#if DEBUG == 1
Serial.print(F("BATT V="));
Serial.print(volt);
Serial.print(F(" P="));
Serial.println(percentage);
#endif
#if BATTERY_SENSOR == 1
// report battery voltage
MyMessage battery_msg(BATTERY_CHILD_ID, V_VOLTAGE);
_send(battery_msg.set(volt, 2));
#endif
// report battery level percentage
sendBatteryLevel(percentage,_ack);
}
#endif
#ifndef MY_GATEWAY_ESP8266
// REBOOT: reboot the board
else if (strcmp(message, "REBOOT") == 0) {
#if DEBUG == 1
Serial.println(F("REBOOT"));
#endif
// set the reboot pin connected to RST to low so to reboot the board
_send(_msg.set(message));
// Software reboot with watchdog timer. Enter Watchdog Configuration mode:
WDTCSR |= (1<<WDCE) | (1<<WDE);
// Reset enable
WDTCSR= (1<<WDE);
// Infinite loop until watchdog reset after 16 ms
while(true){}
}
#endif
// CLEAR: clear the user's eeprom
else if (strcmp(message, "CLEAR") == 0) {
#if DEBUG == 1
Serial.println(F("CLEAR"));
#endif
for (int i = 0; i <= EEPROM_LAST_ID; i++) saveState(i, 0xFF);
_send(_msg.set(message));
}
// VERSION: send back the extension's version
else if (strcmp(message, "VERSION") == 0) {
_send(_msg.set(VERSION));
}
#if REMOTE_CONFIGURATION == 1
// IDxxx: change the node id to the provided one. E.g. ID025: change the node id to 25. Requires a reboot/restart
else if (strlen(message) == 5 && strncmp("ID", message, strlen("ID")) == 0) {
// extract the node id
char s[4];
s[0] = message[2];
s[1] = message[3];
s[2] = message[4];
s[3] = '\0';
int node_id = atoi(s);
#if DEBUG == 1
Serial.print(F("MY I="));
Serial.println(node_id);
#endif
#ifndef MY_GATEWAY_ESP8266
// Save static ID to eeprom
//hwWriteConfig(EEPROM_NODE_ID_ADDRESS, (uint8_t)node_id);
#endif
// reboot the board
_process("REBOOT");
}
// MODEx: change the way the node behaves. 0: stay awake withtout executing each sensors' loop(), 1: go to sleep for the configured interval, 2: wait for the configured interval, 3: stay awake and execute each sensors' loop() (e.g. MODE1)
else if (strlen(message) == 5 && strncmp("MODE", message, strlen("MODE")) == 0) {
// extract mode
char s[2];
s[0] = message[4];
s[1] = '\0';
_sleep_mode = atoi(s);
#if DEBUG == 1
Serial.print(F("SLEEP M="));
Serial.println(_sleep_mode);
#endif
#if PERSIST == 1
// save it to the eeprom
saveState(EEPROM_SLEEP_SAVED, 1);
saveState(EEPROM_SLEEP_MODE, _sleep_mode);
#endif
_send(_msg.set(message));
}
// INTVLnnnX: set and save the wait/sleep interval to nnn where X is S=Seconds, M=mins, H=Hours, D=Days. E.g. INTVL010M would be 10 minutes
else if (strlen(message) == 9 && strncmp("INTVL", message, strlen("INTVL")) == 0) {
// parse and set the sleep interval
int offset = 5;
// extract the unit (S=secs, M=mins, H=hours, D=Days)
char unit[2];
sprintf(unit, "%c", message[3 + offset]);
unit[1] = '\0';
if (strcmp(unit, "S") == 0) _sleep_unit = SECONDS;
else if (strcmp(unit, "M") == 0) _sleep_unit = MINUTES;
else if (strcmp(unit, "H") == 0) _sleep_unit = HOURS;
else if (strcmp(unit, "D") == 0) _sleep_unit = DAYS;
else return;
// extract the requested time
char s[4];
s[0] = message[0 + offset];
s[1] = message[1 + offset];
s[2] = message[2 + offset];
s[3] = '\0';
_sleep_time = atoi(s);
#if DEBUG == 1
Serial.print(F("SLEEP T="));
Serial.print(_sleep_time);
Serial.print(F(" U="));
Serial.println(_sleep_unit);
#endif
#if PERSIST == 1
// save it to eeprom
saveState(EEPROM_SLEEP_UNIT, _sleep_unit);
// encode sleep time
int major = 0;
if (_sleep_time > 750) major = 3;
else if (_sleep_time > 500) major = 2;
else if (_sleep_time > 250) major = 1;
int minor = _sleep_time - 250 * major;
saveState(EEPROM_SLEEP_SAVED, 1);
saveState(EEPROM_SLEEP_TIME_MINOR, minor);
saveState(EEPROM_SLEEP_TIME_MAJOR, major);
#endif
// interval set, reply back with the same message to acknowledge.
_send(_msg.set(message));
}
#endif
// WAKEUP: when received after a sleeping cycle or during wait, abort the cycle and stay awake
else if (strcmp(message, "WAKEUP") == 0) {
#if DEBUG == 1
Serial.println(F("WAKEUP"));
#endif
_send(_msg.set(message));
_sleep_mode = IDLE;
}
}
// wrapper of smart sleep
void NodeManager::_sleep() {
// reset the last interrupt pin
_last_interrupt_pin = -1;
// calculate the seconds to sleep
long sleep_sec = _sleep_time;
if (_sleep_unit == MINUTES) sleep_sec = sleep_sec * 60;
else if (_sleep_unit == HOURS) sleep_sec = sleep_sec * 3600;
else if (_sleep_unit == DAYS) sleep_sec = sleep_sec * 43200;
long sleep_ms = sleep_sec * 1000;
#if DEBUG == 1
Serial.print(F("SLEEP "));
Serial.print(sleep_sec);
Serial.println(F("s"));
#endif
#if SERVICE_MESSAGES == 1
// notify the controller I'm going to sleep
_send(_msg.set("SLEEPING"));
#endif
#if DEBUG == 1
// print a new line to separate the different cycles
Serial.println("");
#endif
// go to sleep
int interrupt = -1;
if (_sleep_mode == WAIT) {
// wait for the given interval
wait(sleep_ms);
// send heartbeat to the controller
sendHeartbeat(_ack);
}
else if (_sleep_mode == SLEEP) {
// setup interrupt pins
int interrupt_1_pin = _interrupt_1_mode == MODE_NOT_DEFINED ? INTERRUPT_NOT_DEFINED : digitalPinToInterrupt(INTERRUPT_PIN_1);
int interrupt_2_pin = _interrupt_2_mode == MODE_NOT_DEFINED ? INTERRUPT_NOT_DEFINED : digitalPinToInterrupt(INTERRUPT_PIN_2);
// enter smart sleep for the requested sleep interval and with the configured interrupts
interrupt = sleep(interrupt_1_pin,_interrupt_1_mode,interrupt_2_pin,_interrupt_2_mode,sleep_ms, true);
if (interrupt > -1) {
// woke up by an interrupt
int pin_number = -1;
int interrupt_mode = -1;
// map the interrupt to the pin
if (digitalPinToInterrupt(INTERRUPT_PIN_1) == interrupt) {
pin_number = INTERRUPT_PIN_1;
interrupt_mode = _interrupt_1_mode;
}
if (digitalPinToInterrupt(INTERRUPT_PIN_2) == interrupt) {
pin_number = INTERRUPT_PIN_2;
interrupt_mode = _interrupt_2_mode;
}
_last_interrupt_pin = pin_number;
#if DEBUG == 1
Serial.print(F("WAKE P="));
Serial.print(pin_number);
Serial.print(F(", M="));
Serial.println(interrupt_mode);
#endif
// when waking up from an interrupt on the wakup pin, stop sleeping
if (_sleep_interrupt_pin == pin_number) _sleep_mode = IDLE;
}
}
// coming out of sleep
#if DEBUG == 1
Serial.println(F("AWAKE"));
#endif
#if SERVICE_MESSAGES == 1
// notify the controller I am awake
_send(_msg.set("AWAKE"));
#endif
#if BATTERY_MANAGER == 1
// keep track of the number of sleeping cycles (ignoring if woke up by an interrupt)
if (interrupt == -1 || _battery_report_with_interrupt) _cycles++;
// battery has to be reported after the configured number of sleep cycles
if (_battery_report_cycles == _cycles) {
// time to report the battery level again
_process("BATTERY");
_cycles = 0;
}
#endif
}
// present the service
void NodeManager::_present(int child_id, int type) {
#if DEBUG == 1
Serial.print(F("PRES I="));
Serial.print(child_id);
Serial.print(F(", T="));
Serial.println(type);
#endif
if (_sleep_between_send > 0) sleep(_sleep_between_send);
present(child_id,type,"",_ack);
}
// return the next available child_id
int NodeManager::_getAvailableChildId() {
for (int i = 1; i < 255; i++) {
if (i == CONFIGURATION_CHILD_ID) continue;
if (i == BATTERY_CHILD_ID) continue;
// empty place, return it
if (_sensors[i] == 0) return i;
}
}
// guess the initial value of a digital output based on the configured interrupt mode
int NodeManager::_getInterruptInitialValue(int mode) {
if (mode == RISING) return LOW;
if (mode == FALLING) return HIGH;
return -1;
}