diff --git a/NodeManager.cpp b/NodeManager.cpp deleted file mode 100644 index 1302c1ae..00000000 --- a/NodeManager.cpp +++ /dev/null @@ -1,4135 +0,0 @@ -/* - * NodeManager - */ - -#include "NodeManager.h" - -/*************************************** - PowerManager -*/ - -// set the vcc and ground pin the sensor is connected to -void PowerManager::setPowerPins(int ground_pin, int vcc_pin, int wait_time) { - _ground_pin = ground_pin; - _vcc_pin = vcc_pin; - #if DEBUG == 1 - Serial.print(F("PWR G=")); - Serial.print(_ground_pin); - Serial.print(F(" V=")); - Serial.println(_vcc_pin); - #endif - if (_ground_pin > 0) { - // configure the ground pin as output and initialize to low - pinMode(_ground_pin, OUTPUT); - digitalWrite(_ground_pin, LOW); - } - if (_vcc_pin > 0) { - // configure the vcc pin as output and initialize to high (power on) - pinMode(_vcc_pin, OUTPUT); - digitalWrite(_vcc_pin, HIGH); - } - // save wait time - _wait = wait_time; -} - - -// turn on the sensor by activating its power pins -void PowerManager::powerOn() { - if (_vcc_pin == -1) 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 (_vcc_pin == -1) 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); -} - -/****************************************** - Timer -*/ - -Timer::Timer(NodeManager* node_manager) { - _node_manager = node_manager; -} - -// start the timer -void Timer::start(int target, int unit) { - set(target,unit); - start(); -} -void Timer::start() { - if (_is_configured) _is_running = true; -} - -// stop the timer -void Timer::stop() { - _is_running = false; -} - -// reset the timer -void Timer::reset() { - // reset the timer - _elapsed = 0; - _last_millis = 0; -} - -// restart the timer -void Timer::restart() { - if (! isRunning()) return; - stop(); - reset(); - // if using millis(), keep track of the current timestamp for calculating the difference - if (! _node_manager->isSleepingNode()) _last_millis = millis(); - start(); -} - -// setup the timer -void Timer::set(int target, int unit) { - reset(); - // save the settings - _target = target; - if (unit == MINUTES) _target = _target * 60; - else if (unit == HOURS) _target = _target * 60 *60; - else if (unit == DAYS) _target = _target * 60 * 60 *24; - _is_running = false; - _is_configured = true; -} - -// unset the timer -void Timer::unset() { - stop(); - _is_configured = true; -} - -// update the timer at every cycle -void Timer::update() { - if (! isRunning()) return; - if (_node_manager->isSleepingNode()) { - // millis() is not reliable while sleeping so calculate how long a sleep cycle would last in seconds and update the elapsed time - _elapsed += _node_manager->getSleepSeconds(); - } else { - // use millis() to calculate the elapsed time in seconds - _elapsed = (long)((millis() - _last_millis)/1000); - } - _first_run = false; -} - -// return true if the time is over -bool Timer::isOver() { - if (! isRunning()) return false; - // time has elapsed - if (_elapsed >= _target) return true; - // millis has started over - if (_elapsed < 0 ) return true; - return false; -} - -// return true if the timer is running -bool Timer::isRunning() { - if (! isConfigured()) return false; - return _is_running; -} - -// return true if the time is configured -bool Timer::isConfigured() { - return _is_configured; -} - -// return true if this is the first time the timer runs -bool Timer::isFirstRun() { - return _first_run; -} - -// return elapsed seconds so far -float Timer::getElapsed() { - return _elapsed; -} - - -/****************************************** - Request -*/ - -Request::Request(const char* string) { - char* ptr; - // copy to working area - strcpy((char*)&_value, string); - // tokenize the string and split function from value - strtok_r(_value, ",", &ptr); - // get function code - _function = atoi(_value); - // move user data to working area - strcpy(_value,ptr); - #if DEBUG == 1 - Serial.print(F("REQ F=")); - Serial.print(getFunction()); - Serial.print(F(" I=")); - Serial.print(getValueInt()); - Serial.print(F(" F=")); - Serial.print(getValueFloat()); - Serial.print(F(" S=")); - Serial.println(getValueString()); - #endif -} - -// return the parsed function -int Request::getFunction() { - return _function; -} - -// return the value as an int -int Request::getValueInt() { - return atoi(_value); - -} - -// return the value as a float -float Request::getValueFloat() { - return atof(_value); -} - -// return the value as a string -char* Request::getValueString() { - return _value; -} - - -/****************************************** - Sensors -*/ - -/* - Sensor class -*/ -// constructor -Sensor::Sensor(NodeManager* node_manager, int child_id, int pin) { - _node_manager = node_manager; - _child_id = child_id; - _pin = pin; - _msg = _node_manager->getMessage(); - _report_timer = new Timer(_node_manager); - _force_update_timer = new Timer(_node_manager); -} - -// 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; -} -int Sensor::getType() { - return _type; -} -void Sensor::setDescription(char* value) { - _description = value; -} -void Sensor::setSamples(int value) { - _samples = value; -} -void Sensor::setSamplesInterval(int value) { - _samples_interval = value; -} -void Sensor::setTrackLastValue(bool value) { - _track_last_value = value; -} -void Sensor::setForceUpdateMinutes(int value) { - _force_update_timer->start(value,MINUTES); -} -void Sensor::setForceUpdateHours(int value) { - _force_update_timer->start(value,HOURS); -} -void Sensor::setValueType(int value) { - _value_type = value; -} -int Sensor::getValueType() { - return _value_type; -} -void Sensor::setFloatPrecision(int value) { - _float_precision = value; -} -void Sensor::setDoublePrecision(int value) { - _double_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 -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; -} - -// After how many seconds the sensor will report back its measure -void Sensor::setReportIntervalSeconds(int value) { - _report_timer->start(value,SECONDS); -} - -// After how many minutes the sensor will report back its measure -void Sensor::setReportIntervalMinutes(int value) { - _report_timer->start(value,MINUTES); -} - -// After how many minutes the sensor will report back its measure -void Sensor::setReportIntervalHours(int value) { - _report_timer->start(value,HOURS); -} - -// After how many minutes the sensor will report back its measure -void Sensor::setReportIntervalDays(int value) { - _report_timer->start(value,DAYS); -} - - -// return true if the report interval has been already configured -bool Sensor::isReportIntervalConfigured() { - return _report_timer->isConfigured(); -} - -// listen for interrupts on the given pin so interrupt() will be called when occurring -void Sensor::setInterrupt(int pin, int mode, int initial) { - _interrupt_pin = pin; - _node_manager->setInterrupt(pin,mode,initial); -} - -// 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,_node_manager->getAck()); -} - -// 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; - // update the timers if within a loop cycle - if (! _isReceive(message)) { - if (_report_timer->isRunning()) { - // store the elapsed time before updating it - bool first_run = _report_timer->isFirstRun(); - // update the timer - _report_timer->update(); - // if it is not the time yet to report a new measure, just return (unless the first time) - if (! _report_timer->isOver() && ! first_run) return; - } - if (_force_update_timer->isRunning()) _force_update_timer->update(); - } - #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 - double total = 0; - // 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 (_isReceive(message)) { - // we've been called from receive(), pass the message along - onReceive(message); - } - else { - // we'be been called from loop() - onLoop(); - } - // for integers, floats and doubles, keep track of the total - if (_value_type == TYPE_INTEGER) total += (float)_value_int; - else if (_value_type == TYPE_FLOAT) total += _value_float; - else if (_value_type == TYPE_DOUBLE) total += _value_double; - // wait between samples - if (_samples_interval > 0) _node_manager->sleepOrWait(_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 (_isReceive(message) || _isWorthSending(avg != _last_value_int)) { - _last_value_int = avg; - _sendSensorMessage(_msg->set(avg)); - _value_int = -1; - } - } - // process a float value - else if (_value_type == TYPE_FLOAT && total > -1) { - // calculate the average value of the samples - float avg = total / _samples; - // report the value back - if (_isReceive(message) || _isWorthSending(avg != _last_value_float)) { - _last_value_float = avg; - _sendSensorMessage(_msg->set(avg, _float_precision)); - _value_float = -1; - } - } - // process a double value - else if (_value_type == TYPE_DOUBLE && total > -1) { - // calculate the average value of the samples - double avg = total / _samples; - // report the value back - if (_isReceive(message) || _isWorthSending(avg != _last_value_double)) { - _last_value_double = avg; - _sendSensorMessage(_msg->set(avg, _double_precision)); - _value_double = -1; - } - } - // 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 (_isReceive(message) || _isWorthSending(strcmp(_value_string, _last_value_string) != 0)) { - _last_value_string = _value_string; - _sendSensorMessage(_msg->set(_value_string)); - _value_string = ""; - } - } - // turn the sensor off - #if POWER_MANAGER == 1 - if (_auto_power_pins) powerOff(); - #endif - // restart the report timer if over - if (! _isReceive(message) && _report_timer->isRunning() && _report_timer->isOver()) _report_timer->restart(); -} - -// receive and handle an interrupt -void Sensor::interrupt() { - // call the implementation of onInterrupt() - onInterrupt(); -} - -// receive a message from the radio network -void Sensor::receive(const MyMessage &message) { - // return if not for this sensor - if (message.sensor != _child_id) return; - // check if it is a request for the API - if (message.getCommand() == C_REQ && message.type == V_CUSTOM) { - #if REMOTE_CONFIGURATION == 1 - // parse the request - Request request = Request(message.getString()); - // if it is for a sensor-generic function, call process(), otherwise the sensor-specific onProcess(); - if (request.getFunction() < 100) process(request); - else onProcess(request); - #endif - } - // return if the type is not correct - if (message.type != _type) return; - // a request would make the sensor executing its main task passing along the message - loop(message); -} - -// process a remote configuration request message -void Sensor::process(Request & request) { - int function = request.getFunction(); - switch(function) { - case 1: setPin(request.getValueInt()); break; - case 2: setChildId(request.getValueInt()); break; - case 3: setType(request.getValueInt()); break; - case 4: setDescription(request.getValueString()); break; - case 5: setSamples(request.getValueInt()); break; - case 6: setSamplesInterval(request.getValueInt()); break; - case 7: setTrackLastValue(request.getValueInt()); break; - case 9: setForceUpdateMinutes(request.getValueInt()); break; - case 10: setValueType(request.getValueInt()); break; - case 11: setFloatPrecision(request.getValueInt()); break; - #if POWER_MANAGER == 1 - case 12: setAutoPowerPins(request.getValueInt()); break; - case 13: powerOn(); break; - case 14: powerOff(); break; - #endif - case 16: setReportIntervalMinutes(request.getValueInt()); break; - case 17: setReportIntervalSeconds(request.getValueInt()); break; - case 19: setReportIntervalHours(request.getValueInt()); break; - case 20: setReportIntervalDays(request.getValueInt()); break; - case 18: setForceUpdateHours(request.getValueInt()); break; - case 21: setDoublePrecision(request.getValueInt()); break; - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - -// send a message to the network -void Sensor::_sendSensorMessage(MyMessage & message) { - // setup the message - message.setSensor(_child_id); - message.setType(_type); - _node_manager->sendMessage(); - } - -// send a message to the network -void Sensor::_sendServiceMessage(MyMessage & message) { - // setup the message - message.setSensor(_child_id); - message.setType(V_CUSTOM); - _node_manager->sendMessage(); -} - -// return true if the message is coming from the radio network -bool Sensor::_isReceive(const MyMessage & message) { - if (message.sender == 0 && message.sensor == 0 && message.getCommand() == 0 && message.type == 0) return false; - return true; -} - -// determine if a value is worth sending back to the controller -bool Sensor::_isWorthSending(bool comparison) { - // track last value is disabled - if (! _track_last_value) return true; - // track value is enabled and the current value is different then the old value - if (_track_last_value && comparison) return true; - // track value is enabled and the timer is over - if (_track_last_value && _force_update_timer->isRunning() && _force_update_timer->isOver()) { - // restart the timer - _force_update_timer->restart(); - return true; - } - return false; -} - -#if MODULE_ANALOG_INPUT == 1 -/* - SensorAnalogInput -*/ - -// contructor -SensorAnalogInput::SensorAnalogInput(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, 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(); -} - -// what to do when receiving a remote message -void SensorAnalogInput::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setReference(request.getValueInt()); break; - case 102: setReverse(request.getValueInt()); break; - case 103: setOutputPercentage(request.getValueInt()); break; - case 104: setRangeMin(request.getValueInt()); break; - case 105: setRangeMax(request.getValueInt()); break; - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - -// what to do when receiving an interrupt -void SensorAnalogInput::onInterrupt() { -} - -// 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(NodeManager* node_manager, int child_id, int pin): SensorAnalogInput(node_manager, 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(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, 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 - temperature = _node_manager->celsiusToFahrenheit(temperature); - #if DEBUG == 1 - Serial.print(F("THER I=")); - Serial.print(_child_id); - Serial.print(F(" V=")); - Serial.print(adc); - 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 SensorThermistor::onReceive(const MyMessage & message) { - if (message.getCommand() == C_REQ) onLoop(); -} - -// what to do when receiving a remote message -void SensorThermistor::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setNominalResistor((long)request.getValueInt()); break; - case 102: setNominalTemperature(request.getValueInt()); break; - case 103: setBCoefficient(request.getValueInt()); break; - case 104: setSeriesResistor((long)request.getValueString()); break; - case 105: setOffset(request.getValueFloat()); break; - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - -// what to do when receiving an interrupt -void SensorThermistor::onInterrupt() { -} - -/* - SensorML8511 -*/ - -// contructor -SensorML8511::SensorML8511(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, 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 = _node_manager->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(); -} - -// what to do when receiving a remote message -void SensorML8511::onProcess(Request & request) { -} - -// what to do when receiving an interrupt -void SensorML8511::onInterrupt() { -} - -// 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(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, 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(); -} - -// what to do when receiving a remote message -void SensorACS712::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 100: setmVPerAmp(request.getValueInt()); break; - case 102: setOffset(request.getValueInt()); break; - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - -// what to do when receiving an interrupt -void SensorACS712::onInterrupt() { -} - -/* - SensorRain -*/ - -// contructor -SensorRain::SensorRain(NodeManager* node_manager, int child_id, int pin): SensorAnalogInput(node_manager,child_id, pin) { - // set presentation and type and reverse - setPresentation(S_RAIN); - setType(V_RAINRATE); - setReference(DEFAULT); - setOutputPercentage(true); - setReverse(true); - setRangeMin(100); -} - -/* - SensorSoilMoisture -*/ - -// contructor -SensorSoilMoisture::SensorSoilMoisture(NodeManager* node_manager, int child_id, int pin): SensorAnalogInput(node_manager, child_id, pin) { - // set presentation and type and reverse - setPresentation(S_MOISTURE); - setType(V_LEVEL); - setReference(DEFAULT); - setOutputPercentage(true); - setReverse(true); - setRangeMin(100); -} - -#endif - -#if MODULE_DIGITAL_INPUT == 1 -/* - SensorDigitalInput -*/ - -// contructor -SensorDigitalInput::SensorDigitalInput(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,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(); -} - -// what to do when receiving a remote message -void SensorDigitalInput::onProcess(Request & request) { -} - -// what to do when receiving an interrupt -void SensorDigitalInput::onInterrupt() { -} -#endif - - -#if MODULE_DIGITAL_OUTPUT == 1 -/* - SensorDigitalOutput -*/ - -SensorDigitalOutput::SensorDigitalOutput(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id, pin) { - _safeguard_timer = new Timer(node_manager); -} - -// what to do during before -void SensorDigitalOutput::onBefore() { - _setupPin(_pin); - -} - -// what to do during setup -void SensorDigitalOutput::onSetup() { -} - -// setter/getter -void SensorDigitalOutput::setOnValue(int value) { - _on_value = value; -} -void SensorDigitalOutput::setLegacyMode(bool value) { - _legacy_mode = value; -} -void SensorDigitalOutput::setSafeguard(int value) { - _safeguard_timer->set(value,MINUTES); -} -int SensorDigitalOutput::getStatus() { - return _status; -} -void SensorDigitalOutput::setInputIsElapsed(bool value) { - _input_is_elapsed = value; -} -void SensorDigitalOutput::setWaitAfterSet(int value) { - _wait_after_set = value; -} - -// main task -void SensorDigitalOutput::onLoop() { - // set the value to -1 so to avoid reporting to the gateway during loop - _value_int = -1; - _last_value_int = -1; - // if a safeguard is set, check if it is time for it - if (_safeguard_timer->isRunning()) { - // update the timer - _safeguard_timer->update(); - // if the time is over, turn the output off - if (_safeguard_timer->isOver()) setStatus(OFF); - } -} - -// 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)) { - // switch the output - setStatus(message.getInt()); - } - if (message.getCommand() == C_REQ && ! _legacy_mode) { - // return the current status - _value_int = _status; - } -} - -// what to do when receiving a remote message -void SensorDigitalOutput::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 103: setOnValue(request.getValueInt()); break; - case 104: setLegacyMode(request.getValueInt()); break; - case 105: setSafeguard(request.getValueInt()); break; - case 106: setInputIsElapsed(request.getValueInt()); break; - case 107: setWaitAfterSet(request.getValueInt()); break; - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - -// what to do when receiving an interrupt -void SensorDigitalOutput::onInterrupt() { -} - -// write the value to the output -void SensorDigitalOutput::setStatus(int value) { - // pre-process the input value - if (_input_is_elapsed) { - // the input provided is an elapsed time - if (value == OFF) { - // turning it off, no need for a safeguard anymore, stop the timer - _safeguard_timer->stop(); - } - else if (value == ON) { - // configure and start the timer - _safeguard_timer->start(value,MINUTES); - // if the input is an elapsed time, unless the value is OFF, the output will be always ON - value = ON; - } - } else { - // if turning the output on and a safeguard timer is configured, start it - if (value == ON && _safeguard_timer->isConfigured() && ! _safeguard_timer->isRunning()) _safeguard_timer->start(); - } - _setStatus(value); - // wait if needed for relay drawing a lot of current - if (_wait_after_set > 0) _node_manager->sleepOrWait(_wait_after_set); - // store the new status so it will be sent to the controller - _status = value; - _value_int = value; -} - -// setup the provided pin for output -void SensorDigitalOutput::_setupPin(int pin) { - // set the pin as output and initialize it accordingly - pinMode(pin, OUTPUT); - // setup the pin in a off status - _status = ! _on_value; - digitalWrite(pin, _status); - // the initial value is now the current value - _value_int = _status; -} - -// switch to the requested status -void SensorDigitalOutput::_setStatus(int value) { - int value_to_write = _getValueToWrite(value); - // set the value to the pin - digitalWrite(_pin, value_to_write); - #if DEBUG == 1 - Serial.print(F("DOUT I=")); - Serial.print(_child_id); - Serial.print(F(" P=")); - Serial.print(_pin); - Serial.print(F(" V=")); - Serial.println(value_to_write); - #endif -} - -// reverse the value if needed based on the _on_value -int SensorDigitalOutput::_getValueToWrite(int value) { - int value_to_write = value; - if (_on_value == LOW) { - // if the "on" value is LOW, reverse the value - if (value == ON) value_to_write = LOW; - if (value == OFF) value_to_write = HIGH; - } - return value_to_write; -} - - -/* - SensorRelay -*/ - -// contructor -SensorRelay::SensorRelay(NodeManager* node_manager, int child_id, int pin): SensorDigitalOutput(node_manager, child_id, pin) { - // set presentation and type - setPresentation(S_BINARY); - setType(V_STATUS); -} - -/* - SensorLatchingRelay -*/ - -// contructor -SensorLatchingRelay::SensorLatchingRelay(NodeManager* node_manager, int child_id, int pin): SensorRelay(node_manager, child_id, pin) { - // set the "off" pin to the provided pin - setPinOff(pin); - // set the "on" pin to the provided pin + 1 - setPinOn(pin + 1); -} - -// setter/getter -void SensorLatchingRelay::setPulseWidth(int value) { - _pulse_width = value; -} -void SensorLatchingRelay::setPinOn(int value) { - _pin_on = value; -} -void SensorLatchingRelay::setPinOff(int value) { - _pin_off = value; -} - -// what to do during before -void SensorLatchingRelay::onBefore() { - _setupPin(_pin_on); - _setupPin(_pin_off); -} - -// what to do when receiving a remote message -void SensorLatchingRelay::onProcess(Request & request) { - int function = request.getFunction(); - if (function < 200) { - // if this is for SensorDigitalOutput call its onProcess() - SensorDigitalOutput::onProcess(request); - return; - } - switch(function) { - case 201: setPulseWidth(request.getValueInt()); break; - case 202: setPinOff(request.getValueInt()); break; - case 203: setPinOn(request.getValueInt()); break; - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - -// switch to the requested status -void SensorLatchingRelay::_setStatus(int value) { - // select the right pin to send the pulse to - int pin = value == OFF ? _pin_off : _pin_on; - // set the value - digitalWrite(pin, _on_value); - // wait for the given time before restoring the value to the original value after the pulse - _node_manager->sleepOrWait(_pulse_width); - digitalWrite(pin, ! _on_value); - #if DEBUG == 1 - Serial.print(F("LAT I=")); - Serial.print(_child_id); - Serial.print(F(" P=")); - Serial.print(pin); - Serial.print(F(" S=")); - Serial.print(value); - Serial.print(F(" V=")); - Serial.print(_on_value); - Serial.print(F(" P=")); - Serial.println(_pulse_width); - #endif -} - -#endif -/* - SensorDHT -*/ -#if MODULE_DHT == 1 -// contructor -SensorDHT::SensorDHT(NodeManager* node_manager, int child_id, int pin, DHT* dht, int sensor_type, int dht_type): Sensor(node_manager, 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() { -} - -// what to do during setup -void SensorDHT::onSetup() { - // initialize the dht library - _dht->setup(_pin,_dht_type); -} - -// what to do during loop -void SensorDHT::onLoop() { - _node_manager->sleepOrWait(_dht->getMinimumSamplingPeriod()); - _dht->readSensor(true); - // temperature sensor - if (_sensor_type == SensorDHT::TEMPERATURE) { - // read the temperature - float temperature = _dht->getTemperature(); - if (! _node_manager->getIsMetric()) temperature = _dht->toFahrenheit(temperature); - #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->getHumidity(); - #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(); -} - -// what to do when receiving a remote message -void SensorDHT::onProcess(Request & request) { -} - -// what to do when receiving an interrupt -void SensorDHT::onInterrupt() { -} -#endif - -/* - SensorSHT21 -*/ -#if MODULE_SHT21 == 1 -// contructor -SensorSHT21::SensorSHT21(NodeManager* node_manager, int child_id, int sensor_type): Sensor(node_manager,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 - temperature = _node_manager->celsiusToFahrenheit(temperature); - #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(); -} - -// what to do when receiving a remote message -void SensorSHT21::onProcess(Request & request) { -} - -// what to do when receiving an interrupt -void SensorSHT21::onInterrupt() { -} -#endif - -/* - * SensorHTU21D - */ - #if MODULE_SHT21 == 1 -// constructor -SensorHTU21D::SensorHTU21D(NodeManager* node_manager, int child_id, int pin): SensorSHT21(node_manager, child_id, pin) { -} -#endif - -#if MODULE_SWITCH == 1 -/* - * SensorSwitch - */ -SensorSwitch::SensorSwitch(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id,pin) { - setType(V_TRIPPED); -} - -// setter/getter -void SensorSwitch::setMode(int value) { - _mode = value; -} -void SensorSwitch::setDebounce(int value) { - _debounce = value; -} -void SensorSwitch::setTriggerTime(int value) { - _trigger_time = value; -} -void SensorSwitch::setInitial(int value) { - _initial = value; -} - -// what to do during before -void SensorSwitch::onBefore() { - // set the interrupt pin so it will be called only when waking up from that interrupt - setInterrupt(_pin,_mode,_initial); -} - -// what to do during setup -void SensorSwitch::onSetup() { - // report immediately - _report_timer->unset(); -} - -// what to do during loop -void SensorSwitch::onLoop() { -} - -// what to do as the main task when receiving a message -void SensorSwitch::onReceive(const MyMessage & message) { - if (message.getCommand() == C_REQ) { - _value_int = digitalRead(_pin); - } -} - -// what to do when receiving a remote message -void SensorSwitch::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setMode(request.getValueInt()); break; - case 102: setDebounce(request.getValueInt()); break; - case 103: setTriggerTime(request.getValueInt()); break; - case 104: setInitial(request.getValueInt()); break; - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - -// what to do when receiving an interrupt -void SensorSwitch::onInterrupt() { - // wait to ensure the the input is not floating - if (_debounce > 0) _node_manager->sleepOrWait(_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) _node_manager->sleepOrWait(_trigger_time); - } else { - // invalid - _value_int = -1; - } -} - -/* - * SensorDoor - */ -SensorDoor::SensorDoor(NodeManager* node_manager, int child_id, int pin): SensorSwitch(node_manager,child_id,pin) { - setPresentation(S_DOOR); -} - -/* - * SensorMotion - */ -SensorMotion::SensorMotion(NodeManager* node_manager, int child_id, int pin): SensorSwitch(node_manager, child_id,pin) { - setPresentation(S_MOTION); - // set initial value to LOW - setInitial(LOW); -} -#endif - -/* - SensorDs18b20 -*/ -#if MODULE_DS18B20 == 1 -// contructor -SensorDs18b20::SensorDs18b20(NodeManager* node_manager, int child_id, int pin, DallasTemperature* sensors, int index): Sensor(node_manager,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 - temperature = _node_manager->celsiusToFahrenheit(temperature); - #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(); -} - -// what to do when receiving a remote message -void SensorDs18b20::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setResolution(request.getValueInt()); break; - case 102: setSleepDuringConversion(request.getValueInt()); break; - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - -// what to do when receiving an interrupt -void SensorDs18b20::onInterrupt() { -} - -// 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(NodeManager* node_manager, int child_id): Sensor(node_manager,child_id,A4) { - setPresentation(S_LIGHT_LEVEL); - setType(V_LEVEL); - _lightSensor = new BH1750(); -} - -void SensorBH1750::setMode(uint8_t mode) { - _lightSensor->configure(mode); -} - -// 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(); -} - -// what to do when receiving a remote message -void SensorBH1750::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setMode(request.getValueInt()); break; - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - - -// what to do when receiving an interrupt -void SensorBH1750::onInterrupt() { -} -#endif - -/* - SensorMLX90614 -*/ -#if MODULE_MLX90614 == 1 -// contructor -SensorMLX90614::SensorMLX90614(NodeManager* node_manager, int child_id, Adafruit_MLX90614* mlx, int sensor_type): Sensor(node_manager,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 - temperature = _node_manager->celsiusToFahrenheit(temperature); - #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(); -} - -// what to do when receiving a remote message -void SensorMLX90614::onProcess(Request & request) { -} - -// what to do when receiving an interrupt -void SensorMLX90614::onInterrupt() { -} -#endif - - -/* - SensorBosch -*/ -#if MODULE_BME280 == 1 || MODULE_BMP085 == 1 || MODULE_BMP280 == 1 -// contructor -SensorBosch::SensorBosch(NodeManager* node_manager, int child_id, int sensor_type): Sensor(node_manager, 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(); -} - -// what to do when receiving a remote message -void SensorBosch::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setForecastSamplesCount(request.getValueInt()); break; - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - -// what to do when receiving an interrupt -void SensorBosch::onInterrupt() { -} - -// calculate and send the forecast back -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(NodeManager* node_manager, int child_id, Adafruit_BME280* bme, int sensor_type): SensorBosch(node_manager, 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 - temperature = _node_manager->celsiusToFahrenheit(temperature); - #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(NodeManager* node_manager, int child_id, Adafruit_BMP085* bmp, int sensor_type): SensorBosch(node_manager, 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 - temperature = _node_manager->celsiusToFahrenheit(temperature); - #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 - -/* - * SensorBMP280 - */ -#if MODULE_BMP280 == 1 -SensorBMP280::SensorBMP280(NodeManager* node_manager, int child_id, Adafruit_BMP280* bmp, int sensor_type): SensorBosch(node_manager, child_id,sensor_type) { - _bmp = bmp; -} - -void SensorBMP280::onLoop() { - // temperature sensor - if (_sensor_type == SensorBMP280::TEMPERATURE) { - // read the temperature - float temperature = _bmp->readTemperature(); - // convert it - temperature = _node_manager->celsiusToFahrenheit(temperature); - #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 == SensorBMP280::PRESSURE) { - // read pressure - float pressure = _bmp->readPressure() / 100.0F; - if (isnan(pressure)) return; - #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 == SensorBMP280::FORECAST) { - float pressure = _bmp->readPressure() / 100.0F; - _forecast(pressure); - } -} -#endif - -/* - SensorHCSR04 -*/ -#if MODULE_HCSR04 == 1 -// contructor -SensorHCSR04::SensorHCSR04(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, 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 = _node_manager->getIsMetric() ? _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(); -} - -// what to do when receiving a remote message -void SensorHCSR04::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setTriggerPin(request.getValueInt()); break; - case 102: setEchoPin(request.getValueInt()); break; - case 103: setMaxDistance(request.getValueInt()); break; - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - -// what to do when receiving an interrupt -void SensorHCSR04::onInterrupt() { -} -#endif - -/* - SensorSonoff -*/ -#if MODULE_SONOFF == 1 -// contructor -SensorSonoff::SensorSonoff(NodeManager* node_manager, int child_id): Sensor(node_manager, 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() { - _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; - } -} - -// what to do when receiving a remote message -void SensorSonoff::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setButtonPin(request.getValueInt()); break; - case 102: setRelayPin(request.getValueInt()); break; - case 103: setLedPin(request.getValueInt()); break; - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - -// what to do when receiving an interrupt -void SensorSonoff::onInterrupt() { -} - -// 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 - - -/* - SensorMCP9808 -*/ -#if MODULE_MCP9808 == 1 -// contructor -SensorMCP9808::SensorMCP9808(NodeManager* node_manager, int child_id, Adafruit_MCP9808* mcp): Sensor(node_manager, 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 - temperature = _node_manager->celsiusToFahrenheit(temperature); - #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(); -} - -// what to do when receiving a remote message -void SensorMCP9808::onProcess(Request & request) { -} - -// what to do when receiving an interrupt -void SensorMCP9808::onInterrupt() { -} -#endif - - -/* - * SensorMQ - */ -#if MODULE_MQ == 1 - -static float SensorMQ::_default_LPGCurve[3] = {2.3,0.21,-0.47}; -static float SensorMQ::_default_COCurve[3] = {2.3,0.72,-0.34}; -static float SensorMQ::_default_SmokeCurve[3] = {2.3,0.53,-0.44}; - -SensorMQ::SensorMQ(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id,pin) { - setPresentation(S_AIR_QUALITY); - setType(V_LEVEL); - _LPGCurve = SensorMQ::_default_LPGCurve; - _COCurve = SensorMQ::_default_COCurve; - _SmokeCurve = SensorMQ::_default_SmokeCurve; -} - -//setter/getter -void SensorMQ::setTargetGas(int value) { - _target_gas = value; -} -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 = value; -} -void SensorMQ::setCOCurve(float *value) { - _COCurve = value; -} -void SensorMQ::setSmokeCurve(float *value) { - _SmokeCurve = value; -} - -// 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(); -} - -// what to do when receiving a remote message -void SensorMQ::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 1: setTargetGas(request.getValueInt()); break; - case 2: setRlValue(request.getValueFloat()); break; - case 3: setRoValue(request.getValueFloat()); break; - case 4: setCleanAirFactor(request.getValueFloat()); break; - case 5: setCalibrationSampleTimes(request.getValueInt()); break; - case 6: setCalibrationSampleInterval(request.getValueInt()); break; - case 7: setReadSampleTimes(request.getValueInt()); break; - case 8: setReadSampleInterval(request.getValueInt()); break; - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - -// what to do when receiving an interrupt -void SensorMQ::onInterrupt() { -} - -// 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]))); -} -#endif - - - -/* - SensorMHZ19 -*/ -#if MODULE_MHZ19 == 1 -// contructor -SensorMHZ19::SensorMHZ19(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { - // set presentation, type and value type - setPresentation(S_AIR_QUALITY); - setType(V_LEVEL); - setRxTx(pin, pin+1); -} - -void SensorMHZ19::setRxTx(int rxpin, int txpin) { - _rx_pin = rxpin; - _tx_pin = txpin; -} - - -// what to do during before -void SensorMHZ19::onBefore() { -} - -// what to do during setup -void SensorMHZ19::onSetup() { - _ser = new SoftwareSerial(_rx_pin, _tx_pin); - _ser->begin(9600); - delay(2000); - while (_ser->read()!=-1) {}; // clear CO2 buffer. -} - -// what to do during loop -void SensorMHZ19::onLoop() { - // Read the ppm value - int co2ppm = readCO2(); // This is there the function gets called that talks to the Co2 sensor. - #if DEBUG == 1 - Serial.print(F("CO2 I=")); - Serial.print(_child_id); - Serial.print(F(" ppm=")); - Serial.println(co2ppm); - #endif - // store the value - _value_int = co2ppm; -} - -// Read out the CO2 data -int SensorMHZ19::readCO2() { - while (_ser->read() != -1) {}; //clear serial buffer - - unsigned char response[9]; // for answer - byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; - - // Command to ask for data. - _ser->write(cmd, 9); //request PPM CO2 - - // Then for 1 second listen for 9 bytes of data. - _ser->readBytes(response, 9); - - #if DEBUG == 1 - for (int i=0; i<9; i++) { - Serial.print(response[i], HEX); - Serial.print(F("-")); - } - Serial.println(F("END")); - #endif - - if (response[0] != 0xFF) { - Serial.println(F("Wrong starting byte from co2 sensor! (should be FF)")); - return -1; - } - - if (response[1] != 0x86) { - Serial.println(F("Wrong command from co2 sensor! (should be 86)")); - return -1; - } - - int responseHigh = (int) response[2]; - int responseLow = (int) response[3]; - int ppm = (256 * responseHigh) + responseLow; - - return ppm; -} - - -// what to do as the main task when receiving a message -void SensorMHZ19::onReceive(const MyMessage & message) { - if (message.getCommand() == C_REQ) onLoop(); -} - -// what to do when receiving a remote message -void SensorMHZ19::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - -// what to do when receiving an interrupt -void SensorMHZ19::onInterrupt() { -} - -#endif - -/* - SensorAM2320 -*/ -#if MODULE_AM2320 == 1 -// constructor -SensorAM2320::SensorAM2320(NodeManager* node_manager, int child_id, AM2320* th, int sensor_type): Sensor(node_manager, child_id,A2) { - _th = th; - _sensor_type = sensor_type; - if (_sensor_type == SensorAM2320::TEMPERATURE) { - // temperature sensor - setPresentation(S_TEMP); - setType(V_TEMP); - setValueType(TYPE_FLOAT); - } - else if (_sensor_type == SensorAM2320::HUMIDITY) { - // humidity sensor - setPresentation(S_HUM); - setType(V_HUM); - setValueType(TYPE_FLOAT); - } -} - -// what do to during before -void SensorAM2320::onBefore() { - -} - -// what do to during setup -void SensorAM2320::onSetup() { -} - -// what do to during loop -void SensorAM2320::onLoop() { - switch(_th->Read()) { - case 0: - // temperature sensor - if (_sensor_type == SensorAM2320::TEMPERATURE) { - // read the temperature - float temperature = _th->t; - #if DEBUG == 1 - Serial.print(F("AM2320 I=")); - Serial.print(_child_id); - Serial.print(F(" T=")); - Serial.println(temperature); - #endif - // store the value - _value_float = temperature; - } - // humidity sensor - else if (_sensor_type == SensorAM2320::HUMIDITY) { - // read humidity - float humidity = _th->h; - if (isnan(humidity)) return; - #if DEBUG == 1 - Serial.print(F("AM2320 I=")); - Serial.print(_child_id); - Serial.print(F(" H=")); - Serial.println(humidity); - #endif - // store the value - _value_float = humidity; - } - break; - case 1: Serial.println(F("AM2320 offline")); break; - case 2: Serial.println(F("AM2320 CRC failed")); break; - } -} - -// what do to as the main task when receiving a message -void SensorAM2320::onReceive(const MyMessage & message) { - onLoop(); -} - -// what to do when receiving a remote message -void SensorAM2320::onProcess(Request & request) { -} - -// what to do when receiving an interrupt -void SensorAM2320::onInterrupt() { -} -#endif - -/* - SensorTSL2561 -*/ -#if MODULE_TSL2561 == 1 -// contructor -SensorTSL2561::SensorTSL2561(NodeManager* node_manager, int child_id): Sensor(node_manager, child_id,A2) { - setPresentation(S_LIGHT_LEVEL); - setType(V_LEVEL); -} - -// setter/getter -void SensorTSL2561::setGain(int value) { - _tsl_gain = value; -} -void SensorTSL2561::setTiming(int value) { - _tsl_timing = value; -} -void SensorTSL2561::setSpectrum(int value) { - _tsl_spectrum = value; -} -void SensorTSL2561::setAddress(int value) { - _tsl_address = value; -} - -// what do to during before -void SensorTSL2561::onBefore() { - switch (_tsl_address) { - case SensorTSL2561::ADDR_FLOAT: - _tsl = new TSL2561(TSL2561_ADDR_FLOAT); - break; - case SensorTSL2561::ADDR_LOW: - _tsl = new TSL2561(TSL2561_ADDR_LOW); - break; - case SensorTSL2561::ADDR_HIGH: - _tsl = new TSL2561(TSL2561_ADDR_HIGH); - break; - } -} - -// what do to during setup -void SensorTSL2561::onSetup() { - if (_tsl->begin()) { - switch (_tsl_gain) { - case SensorTSL2561::GAIN_0X: - _tsl->setGain(TSL2561_GAIN_0X); - break; - case SensorTSL2561::GAIN_16X: - _tsl->setGain(TSL2561_GAIN_16X); - break; - } - switch (_tsl_timing) { - case SensorTSL2561::INTEGRATIONTIME_13MS: - _tsl->setTiming(TSL2561_INTEGRATIONTIME_13MS); - break; - case SensorTSL2561::INTEGRATIONTIME_101MS: - _tsl->setTiming(TSL2561_INTEGRATIONTIME_101MS); - break; - case SensorTSL2561::INTEGRATIONTIME_402MS: - _tsl->setTiming(TSL2561_INTEGRATIONTIME_402MS); - break; - } - } - else { - Serial.println(F("TSL2561 offline")); - } -} - -// what do to during loop -void SensorTSL2561::onLoop() { - // request the light level - switch (_tsl_spectrum) { - case SensorTSL2561::VISIBLE: - _value_int = _tsl->getLuminosity(TSL2561_VISIBLE); - break; - case SensorTSL2561::FULLSPECTRUM: - _value_int = _tsl->getLuminosity(TSL2561_FULLSPECTRUM); - break; - case SensorTSL2561::INFRARED: - _value_int = _tsl->getLuminosity(TSL2561_INFRARED); - break; - case SensorTSL2561::FULL: - // request the full light level - uint32_t lum = _tsl->getFullLuminosity(); - uint16_t ir, full; - ir = lum >> 16; - full = lum & 0xFFFF; - _value_int = _tsl->calculateLux(full, ir); - #if DEBUG == 1 - Serial.print(F("TSL I=")); - Serial.print(_child_id); - Serial.print(F(" LUX=")); - Serial.print(_value_int); - Serial.print(F(" IR=")); - Serial.print(ir); - Serial.print(F(" FULL=")); - Serial.print(full); - Serial.print(F(" VIS=")); - Serial.println(full-ir); - #endif - break; - } - #if DEBUG == 1 - if (_tsl_spectrum < 3) { - Serial.print(F("TSL I=")); - Serial.print(_child_id); - Serial.print(F(" L=")); - Serial.println(_value_int); - } - #endif -} - -// what do to as the main task when receiving a message -void SensorTSL2561::onReceive(const MyMessage & message) { - onLoop(); -} - -// what to do when receiving a remote message -void SensorTSL2561::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setGain(request.getValueInt()); break; - case 102: setTiming(request.getValueInt()); break; - case 103: setSpectrum(request.getValueInt()); break; - case 104: setAddress(request.getValueInt()); break; - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - -// what to do when receiving an interrupt -void SensorTSL2561::onInterrupt() { -} -#endif - -/* - SensorPT100 -*/ -#if MODULE_PT100 == 1 -// contructor -SensorPT100::SensorPT100(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { - // set presentation, type and value type - setPresentation(S_TEMP); - setType(V_TEMP); - setValueType(TYPE_FLOAT); -} - -// setter/getter -void SensorPT100::setVoltageRef(float value) { - _voltageRef = value; -} - -// what to do during before -void SensorPT100::onBefore() { - _PT100 = new DFRobotHighTemperature(_voltageRef); - // set the pin as input - pinMode(_pin, INPUT); -} - -// what to do during setup -void SensorPT100::onSetup() { -} - -// what to do during loop -void SensorPT100::onLoop() { - // read the PT100 sensor - int temperature = _PT100->readTemperature(_pin); - #if DEBUG == 1 - Serial.print(F("PT100 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 SensorPT100::onReceive(const MyMessage & message) { - if (message.getCommand() == C_REQ) onLoop(); -} - -// what to do when receiving a remote message -void SensorPT100::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setVoltageRef(request.getValueFloat()); break; - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - -// what to do when receiving an interrupt -void SensorPT100::onInterrupt() { -} -#endif - -/* - SensorDimmer -*/ - -#if MODULE_DIMMER == 1 -// contructor -SensorDimmer::SensorDimmer(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { - // set presentation, type and value type - setPresentation(S_DIMMER); - setType(V_PERCENTAGE); -} - -// setter/getter -void SensorDimmer::setEasing(int value) { - _easing = value; -} -void SensorDimmer::setDuration(int value) { - _duration = value*1000; -} -void SensorDimmer::setStepDuration(int value) { - _duration = value; -} - -// what to do during before -void SensorDimmer::onBefore() { - pinMode(_pin, OUTPUT); -} - -// what to do during setup -void SensorDimmer::onSetup() { -} - -// what to do during loop -void SensorDimmer::onLoop() { -} - -// what to do as the main task when receiving a message -void SensorDimmer::onReceive(const MyMessage & message) { - if (message.getCommand() == C_SET) { - int percentage = message.getInt(); - // normalize the provided percentage - if (percentage < 0) percentage = 0; - if (percentage > 100) percentage = 100; - fadeTo(percentage); - _value_int = percentage; - } - if (message.getCommand() == C_REQ) { - // return the current status - _value_int = _percentage; - } -} - -// what to do when receiving a remote message -void SensorDimmer::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setEasing(request.getValueInt()); break; - case 102: setDuration(request.getValueInt()); break; - case 103: setStepDuration(request.getValueInt()); break; - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - -// what to do when receiving an interrupt -void SensorDimmer::onInterrupt() { -} - -// fade to the provided value -void SensorDimmer::fadeTo(int target_percentage) { - #if DEBUG == 1 - Serial.print(F("DIM I=")); - Serial.print(_child_id); - Serial.print(F(" V=")); - Serial.println(target_percentage); - #endif - // count how many steps we need to do - int steps = _duration / _step_duration; - // for each step - for (int current_step = 1; current_step <= steps; current_step++) { - // calculate the delta between the target value and the current - int delta = target_percentage - _percentage; - // calculate the smooth transition and adjust it in the 0-255 range - int value_to_write = (int)(_getEasing(current_step,_percentage,delta,steps) / 100. * 255); - // write to the PWM output - analogWrite(_pin,value_to_write); - // wait at the end of this step - wait(_step_duration); - } - _percentage = target_percentage; -} - -// for smooth transitions. t: current time, b: beginning value, c: change in value, d: duration -float SensorDimmer::_getEasing(float t, float b, float c, float d) { - if (_easing == EASE_INSINE) return -c * cos(t/d * (M_PI/2)) + c + b; - else if (_easing == EASE_OUTSINE) return c * sin(t/d * (M_PI/2)) + b; - else if (_easing == EASE_INOUTSINE) return -c/2 * (cos(M_PI*t/d) - 1) + b; - else return c*t/d + b; -} -#endif - -/* - SensorPulseMeter -*/ -#if MODULE_PULSE_METER == 1 -// contructor -SensorPulseMeter::SensorPulseMeter(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id, pin) { - // set presentation, type and value type - setValueType(TYPE_FLOAT); -} - -// setter/getter -void SensorPulseMeter::setPulseFactor(float value) { - _pulse_factor = value; -} -void SensorPulseMeter::setInitialValue(int value) { - _initial_value = value; -} -void SensorPulseMeter::setInterruptMode(int value) { - _interrupt_mode = value; -} - -// what to do during before -void SensorPulseMeter::onBefore() { - // configure the interrupt pin so onInterrupt() will be called on tip - setInterrupt(_pin,_interrupt_mode,_initial_value); -} - -// what to do during setup -void SensorPulseMeter::onSetup() { -} - -// what to do during loop -void SensorPulseMeter::onLoop() { - // do not report anything if called by an interrupt - if (_node_manager->getLastInterruptPin() == _interrupt_pin) return; - // time to report the rain so far - _reportTotal(); - #if DEBUG == 1 - Serial.print(F("PLS I=")); - Serial.print(_child_id); - Serial.print(F(" T=")); - Serial.println(_value_float); - #endif - // reset the counter - _count = 0; -} - -// what to do as the main task when receiving a message -void SensorPulseMeter::onReceive(const MyMessage & message) { - if (message.getCommand() == C_REQ) { - // report the total the last period - _reportTotal(); - } -} - -// what to do when receiving a remote message -void SensorPulseMeter::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 102: setPulseFactor(request.getValueFloat()); break; - default: return; - } - _sendServiceMessage(_msg->set(function)); -} - -// what to do when receiving an interrupt -void SensorPulseMeter::onInterrupt() { - // increase the counter - _count++; - #if DEBUG == 1 - Serial.println(F("PLS+")); - #endif -} - -// return the total based on the pulses counted -void SensorPulseMeter::_reportTotal() { - if (_value_type == TYPE_DOUBLE) _value_double = _count / _pulse_factor; - else _value_float = _count / _pulse_factor; -} - -/* - SensorRainGauge -*/ -// contructor -SensorRainGauge::SensorRainGauge(NodeManager* node_manager, int child_id, int pin): SensorPulseMeter(node_manager,child_id, pin) { - setPresentation(S_RAIN); - setType(V_RAIN); - setPulseFactor(9.09); -} - -/* - SensorPowerMeter -*/ -// contructor -SensorPowerMeter::SensorPowerMeter(NodeManager* node_manager, int child_id, int pin): SensorPulseMeter(node_manager,child_id, pin) { - setPresentation(S_POWER); - setType(V_KWH); - setValueType(TYPE_DOUBLE); - setPulseFactor(1000); -} - -/* - SensorWaterMeter -*/ -// contructor -SensorWaterMeter::SensorWaterMeter(NodeManager* node_manager, int child_id, int pin): SensorPulseMeter(node_manager,child_id, pin) { - setPresentation(S_WATER); - setType(V_VOLUME); - setValueType(TYPE_DOUBLE); - setPulseFactor(1000); -} -#endif - -/******************************************* - NodeManager -*/ - -// initialize the node manager -NodeManager::NodeManager() { - // setup the message container - _msg = MyMessage(); -} - -int NodeManager::_last_interrupt_pin = -1; -long NodeManager::_last_interrupt_1 = millis(); -long NodeManager::_last_interrupt_2 = millis(); -long NodeManager::_interrupt_min_delta = 100; - -// setter/getter -void NodeManager::setRetries(int value) { - _retries = value; -} -int NodeManager::getRetries() { - return _retries; -} -MyMessage* NodeManager::getMessage() { - return &_msg; -} -#if BATTERY_MANAGER == 1 - void NodeManager::setBatteryMin(float value) { - _battery_min = value; - } - void NodeManager::setBatteryMax(float value) { - _battery_max = value; - } - void NodeManager::setBatteryReportSeconds(int value) { - _battery_report_timer.set(value,SECONDS); - } - void NodeManager::setBatteryReportMinutes(int value) { - _battery_report_timer.set(value,MINUTES); - } - void NodeManager::setBatteryReportHours(int value) { - _battery_report_timer.set(value,HOURS); - } - void NodeManager::setBatteryReportDays(int value) { - _battery_report_timer.set(value,DAYS); - } - 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::setSleepSeconds(int value) { - // set the status to AWAKE if the time provided is 0, SLEEP otherwise - if (value == 0) _status = AWAKE; - else _status = SLEEP; - // store the time - _sleep_time = value; -} -void NodeManager::setSleepMinutes(int value) { - setSleepSeconds(value*60); -} -void NodeManager::setSleepHours(int value) { - setSleepMinutes(value*60); -} -void NodeManager::setSleepDays(int value) { - setSleepHours(value*24); -} -long NodeManager::getSleepSeconds() { - return _sleep_time; -} -void NodeManager::setSleepInterruptPin(int value) { - _sleep_interrupt_pin = value; -} -void NodeManager::setInterrupt(int pin, int mode, int initial) { - if (pin == INTERRUPT_PIN_1) { - _interrupt_1_mode = mode; - _interrupt_1_initial = initial; - } - if (pin == INTERRUPT_PIN_2) { - _interrupt_2_mode = mode; - _interrupt_2_initial = initial; - } -} -void NodeManager::setInterruptMinDelta(long value) { - _interrupt_min_delta = value; -} -#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; -} -int NodeManager::getSleepBetweenSend() { - return _sleep_between_send; -} -void NodeManager::setAck(bool value) { - _ack = value; -} -bool NodeManager::getAck() { - return _ack; -} -void NodeManager::setGetControllerConfig(bool value) { - _get_controller_config = value; -} -void NodeManager::setIsMetric(bool value) { - _is_metric = value; -} -bool NodeManager::getIsMetric() { - return _is_metric; -} - -// Convert a temperature from celsius to fahrenheit depending on how isMetric is set -float NodeManager::celsiusToFahrenheit(float temperature) { - if (_is_metric) return temperature; - // convert the temperature from C to F - return temperature * 1.8 + 32; -} - -// return true if sleep or wait is configured and hence this is a sleeping node -bool NodeManager::isSleepingNode() { - if (_status == SLEEP) return true; - return false; -} - -// 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(this,child_id, pin)); - else if (sensor_type == SENSOR_LDR) return registerSensor(new SensorLDR(this,child_id, pin)); - else if (sensor_type == SENSOR_THERMISTOR) return registerSensor(new SensorThermistor(this,child_id, pin)); - else if (sensor_type == SENSOR_ML8511) return registerSensor(new SensorML8511(this,child_id, pin)); - else if (sensor_type == SENSOR_ACS712) return registerSensor(new SensorACS712(this,child_id, pin)); - else if (sensor_type == SENSOR_RAIN) return registerSensor(new SensorRain(this,child_id, pin)); - else if (sensor_type == SENSOR_SOIL_MOISTURE) return registerSensor(new SensorSoilMoisture(this,child_id, pin)); - #endif - #if MODULE_DIGITAL_INPUT == 1 - else if (sensor_type == SENSOR_DIGITAL_INPUT) return registerSensor(new SensorDigitalInput(this,child_id, pin)); - #endif - #if MODULE_DIGITAL_OUTPUT == 1 - else if (sensor_type == SENSOR_DIGITAL_OUTPUT) return registerSensor(new SensorDigitalOutput(this,child_id, pin)); - else if (sensor_type == SENSOR_RELAY) return registerSensor(new SensorRelay(this,child_id, pin)); - else if (sensor_type == SENSOR_LATCHING_RELAY) return registerSensor(new SensorLatchingRelay(this,child_id, pin)); - #endif - #if MODULE_DHT == 1 - else if (sensor_type == SENSOR_DHT11 || sensor_type == SENSOR_DHT22) { - int dht_type; - if (sensor_type == SENSOR_DHT11) dht_type = DHT::DHT11; - else if (sensor_type == SENSOR_DHT22) dht_type = DHT::DHT22; - DHT* dht = new DHT(); - // register temperature sensor - registerSensor(new SensorDHT(this,child_id,pin,dht,SensorDHT::TEMPERATURE,dht_type)); - // register humidity sensor - child_id = _getAvailableChildId(); - return registerSensor(new SensorDHT(this,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(this,child_id,SensorSHT21::TEMPERATURE)); - // register humidity sensor - child_id = _getAvailableChildId(); - return registerSensor(new SensorSHT21(this,child_id,SensorSHT21::HUMIDITY)); - } - else if (sensor_type == SENSOR_HTU21D) { - // register temperature sensor - registerSensor(new SensorHTU21D(this,child_id,SensorHTU21D::TEMPERATURE)); - // register humidity sensor - child_id = _getAvailableChildId(); - return registerSensor(new SensorHTU21D(this,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 - if (sensor_type == SENSOR_SWITCH) return registerSensor(new SensorSwitch(this,child_id, pin)); - else if (sensor_type == SENSOR_DOOR) return registerSensor(new SensorDoor(this,child_id, pin)); - else if (sensor_type == SENSOR_MOTION) return registerSensor(new SensorMotion(this,child_id, pin)); - } - #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(this,child_id,pin,sensors,i)); - } - return index; - } - #endif - #if MODULE_BH1750 == 1 - else if (sensor_type == SENSOR_BH1750) return registerSensor(new SensorBH1750(this,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(this,child_id,mlx,SensorMLX90614::TEMPERATURE_AMBIENT)); - // register object temperature sensor - child_id = _getAvailableChildId(); - return registerSensor(new SensorMLX90614(this,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(this,child_id,bme,SensorBME280::TEMPERATURE)); - child_id = _getAvailableChildId(); - // register humidity sensor - registerSensor(new SensorBME280(this,child_id,bme,SensorBME280::HUMIDITY)); - // register pressure sensor - child_id = _getAvailableChildId(); - registerSensor(new SensorBME280(this,child_id,bme,SensorBME280::PRESSURE)); - // register forecast sensor - child_id = _getAvailableChildId(); - return registerSensor(new SensorBME280(this,child_id,bme,SensorBME280::FORECAST)); - } - #endif - #if MODULE_BMP280 == 1 - else if (sensor_type == SENSOR_BMP280) { - Adafruit_BMP280* bmp = new Adafruit_BMP280(); - if (! bmp->begin(SensorBosch::GetI2CAddress(0x58))) { - #if DEBUG == 1 - Serial.println(F("NO BMP")); - #endif - return -1; - } - // register temperature sensor - registerSensor(new SensorBMP280(this,child_id,bmp,SensorBMP280::TEMPERATURE)); - child_id = _getAvailableChildId(); - // register pressure sensor - child_id = _getAvailableChildId(); - registerSensor(new SensorBMP280(this,child_id,bmp,SensorBMP280::PRESSURE)); - // register forecast sensor - child_id = _getAvailableChildId(); - return registerSensor(new SensorBMP280(this,child_id,bmp,SensorBMP280::FORECAST)); - } - #endif - #if MODULE_SONOFF == 1 - else if (sensor_type == SENSOR_SONOFF) return registerSensor(new SensorSonoff(this,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(this,child_id,bmp,SensorBMP085::TEMPERATURE)); - // register pressure sensor - child_id = _getAvailableChildId(); - registerSensor(new SensorBMP085(this,child_id,bmp,SensorBMP085::PRESSURE)); - // register forecast sensor - child_id = _getAvailableChildId(); - return registerSensor(new SensorBMP085(this,child_id,bmp,SensorBMP085::FORECAST)); - } - #endif - #if MODULE_HCSR04 == 1 - else if (sensor_type == SENSOR_HCSR04) return registerSensor(new SensorHCSR04(this,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(this,child_id,mcp)); - } - #endif - #if MODULE_MQ == 1 - else if (sensor_type == SENSOR_MQ) return registerSensor(new SensorMQ(this,child_id, pin)); - #endif - #if MODULE_MHZ19 == 1 - else if (sensor_type == SENSOR_MHZ19) return registerSensor(new SensorMHZ19(this, child_id, pin)); - #endif - #if MODULE_AM2320 == 1 - else if (sensor_type == SENSOR_AM2320) { - AM2320* th = new AM2320(); - // register temperature sensor - registerSensor(new SensorAM2320(this,child_id,th,SensorAM2320::TEMPERATURE)); - // register humidity sensor - child_id = _getAvailableChildId(); - return registerSensor(new SensorAM2320(this,child_id,th,SensorAM2320::HUMIDITY)); - } - #endif - #if MODULE_TSL2561 == 1 - else if (sensor_type == SENSOR_TSL2561) return registerSensor(new SensorTSL2561(this,child_id)); - #endif - #if MODULE_PT100 == 1 - else if (sensor_type == SENSOR_PT100) return registerSensor(new SensorPT100(this,child_id,pin)); - #endif - #if MODULE_DIMMER == 1 - else if (sensor_type == SENSOR_DIMMER) return registerSensor(new SensorDimmer(this,child_id,pin)); - #endif - #if MODULE_PULSE_METER == 1 - else if (sensor_type == SENSOR_RAIN_GAUGE) return registerSensor(new SensorRainGauge(this,child_id,pin)); - else if (sensor_type == SENSOR_POWER_METER) return registerSensor(new SensorPowerMeter(this,child_id,pin)); - else if (sensor_type == SENSOR_WATER_METER) return registerSensor(new SensorWaterMeter(this,child_id,pin)); - #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 (sensor->getChildId() > MAX_SENSORS) return; - #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) { - if (sensor_index > MAX_SENSORS) return; - // unlink the pointer to this sensor - _sensors[sensor_index] == 0; -} - -// return a sensor given its index -Sensor* NodeManager::get(int child_id) { - if (child_id > MAX_SENSORS) return 0; - // 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) { - if (old_child_id > MAX_SENSORS || new_child_id > MAX_SENSORS) return; - // 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() { - // print out the version - #if DEBUG == 1 - Serial.print(F("NodeManager v")); - Serial.println(VERSION); - #endif - // setup the reboot pin if needed - if (_reboot_pin > -1) { - #if DEBUG == 1 - Serial.print("REB P="); - Serial.println(_reboot_pin); - #endif - pinMode(_reboot_pin, OUTPUT); - digitalWrite(_reboot_pin, HIGH); - } - // print out MySensors' library capabilities - #if DEBUG == 1 - Serial.print(F("LIB V=")); - Serial.print(MYSENSORS_LIBRARY_VERSION); - Serial.print(F(" R=")); - Serial.print(MY_CAP_RADIO); - #ifdef MY_CAP_ENCR - Serial.print(F(" E=")); - Serial.print(MY_CAP_ENCR); - #endif - Serial.print(F(" T=")); - Serial.print(MY_CAP_TYPE); - Serial.print(F(" A=")); - Serial.print(MY_CAP_ARCH); - Serial.print(F(" S=")); - Serial.print(MY_CAP_SIGN); - Serial.print(F(" B=")); - Serial.println(MY_CAP_RXBUF); - #endif - #if PERSIST == 1 - // restore the configuration saved in the eeprom - _loadConfig(); - #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); - // if not already configured, report battery level every 60 minutes - if (! _battery_report_timer.isConfigured()) _battery_report_timer.set(60,MINUTES); - _battery_report_timer.start(); - #endif - #if SIGNAL_SENSOR == 1 && defined(MY_SIGNAL_REPORT_ENABLED) - // if not already configured, report signal level every 60 minutes - if (! _signal_report_timer.isConfigured()) _signal_report_timer.set(60,MINUTES); - _signal_report_timer.start(); - #endif - // setup individual sensors - for (int i = 1; i <= MAX_SENSORS; i++) { - if (_sensors[i] == 0) continue; - // configure reporting interval - if (! _sensors[i]->isReportIntervalConfigured()) _sensors[i]->setReportIntervalSeconds(_report_interval_seconds); - // call each sensor's before() - _sensors[i]->before(); - } - // setup the interrupt pins - setupInterrupts(); -} - -// 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 - batteryReport(); - #endif - #if SIGNAL_SENSOR == 1 && defined(MY_SIGNAL_REPORT_ENABLED) - // present the signal service - _present(SIGNAL_CHILD_ID, S_SOUND); - // report battery level - signalReport(); - #endif - // present each sensor - for (int i = 1; i <= MAX_SENSORS; 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() { - // retrieve and store isMetric from the controller - if (_get_controller_config) _is_metric = getControllerConfig().isMetric; - #if DEBUG == 1 - Serial.print(F("MY I=")); - Serial.print(getNodeId()); - Serial.print(F(" M=")); - Serial.println(_is_metric); - #endif - #if SERVICE_MESSAGES == 1 - _sendUsingConfigChild(_msg.set("STARTED")); - #endif - // run setup for all the registered sensors - for (int i = 1; i <= MAX_SENSORS; 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() { - #if BATTERY_MANAGER == 1 - // update the timer for battery report when not waking up from an interrupt - if (_battery_report_timer.isRunning() && _last_interrupt_pin == -1) _battery_report_timer.update(); - // if it is time to report the battery level - if (_battery_report_timer.isOver()) { - // time to report the battery level again - batteryReport(); - // restart the timer - _battery_report_timer.restart(); - } - #endif - #if SIGNAL_SENSOR == 1 && defined(MY_SIGNAL_REPORT_ENABLED) - // update the timer for signal report when not waking up from an interrupt - if (_signal_report_timer.isRunning() && _last_interrupt_pin == -1) _signal_report_timer.update(); - // if it is time to report the signal level - if (_signal_report_timer.isOver()) { - // time to report the signal level again - signalReport(); - // restart the timer - _signal_report_timer.restart(); - } - #endif - #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 = 1; i <= MAX_SENSORS; i++) { - // skip unconfigured sensors - if (_sensors[i] == 0) continue; - if (_last_interrupt_pin != -1 && _sensors[i]->getInterruptPin() == _last_interrupt_pin) { - // if there was an interrupt for this sensor, call the sensor's interrupt() and then loop() - _msg.clear(); - _sensors[i]->interrupt(); - _sensors[i]->loop(_msg); - // reset the last interrupt pin - _last_interrupt_pin = -1; - } - else if (_last_interrupt_pin == -1) { - // if just at the end of a cycle, call the sensor's loop() - _msg.clear(); - _sensors[i]->loop(_msg); - } - } - #if POWER_MANAGER == 1 - // turn off the pin powering all the sensors - if (_auto_power_pins) powerOff(); - #endif - // continue/start sleeping as requested - if (isSleepingNode()) _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 configuration message - if (message.sensor == CONFIGURATION_CHILD_ID && message.getCommand() == C_REQ && message.type == V_CUSTOM) { - #if REMOTE_CONFIGURATION == 1 - // parse the request - Request request = Request(message.getString()); - // process the request - process(request); - #endif - } - // dispatch the message to the registered sensor - else if (message.sensor <= MAX_SENSORS && _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 - sleepOrWait(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 -} - -// process a request message -void NodeManager::process(Request & request) { - int function = request.getFunction(); - switch(function) { - case 1: hello(); break; - #if BATTERY_MANAGER == 1 - case 2: batteryReport(); return; - case 11: setBatteryMin(request.getValueFloat()); break; - case 12: setBatteryMax(request.getValueFloat()); break; - case 14: setBatteryReportMinutes(request.getValueInt()); break; - case 15: setBatteryInternalVcc(request.getValueInt()); break; - case 16: setBatteryPin(request.getValueInt()); break; - case 17: setBatteryVoltsPerBit(request.getValueFloat()); break; - case 18: setBatteryReportWithInterrupt(request.getValueInt()); break; - #endif - case 3: - setSleepSeconds(request.getValueInt()); - #if PERSIST == 1 - _saveConfig(); - #endif - break; - case 4: - setSleepMinutes(request.getValueInt()); - #if PERSIST == 1 - _saveConfig(); - #endif - break; - case 5: - setSleepHours(request.getValueInt()); - #if PERSIST == 1 - _saveConfig(); - #endif - break; - case 29: - setSleepDays(request.getValueInt()); - #if PERSIST == 1 - _saveConfig(); - #endif - break; - #ifndef MY_GATEWAY_ESP8266 - case 6: reboot(); return; - #endif - case 7: clearEeprom(); break; - case 8: version(); return; - case 9: wakeup(); break; - case 10: setRetries(request.getValueInt()); break; - case 19: setSleepInterruptPin(request.getValueInt()); break; - case 20: setSleepBetweenSend(request.getValueInt()); break; - case 21: setAck(request.getValueInt()); break; - case 22: setIsMetric(request.getValueInt()); break; - #if POWER_MANAGER == 1 - case 23: setAutoPowerPins(request.getValueInt()); break; - case 24: powerOn(); break; - case 25: powerOff(); break; - #endif - case 26: unRegisterSensor(request.getValueInt()); break; - case 27: saveToMemory(0,request.getValueInt()); break; - case 28: setInterruptMinDelta(request.getValueInt()); break; - case 30: setSleepOrWait(request.getValueInt()); break; - case 31: setRebootPin(request.getValueInt()); break; - case 32: setADCOff(); break; - #if SIGNAL_SENSOR == 1 && defined(MY_SIGNAL_REPORT_ENABLED) - case 33: setSignalReportMinutes(request.getValueInt()); break; - case 43: setSignalReportSeconds(request.getValueInt()); break; - case 44: setSignalReportHours(request.getValueInt()); break; - case 45: setSignalReportDays(request.getValueInt()); break; - case 34: setSignalCommand(request.getValueInt()); break; - case 35: signalReport(); break; - #endif - case 36: setReportIntervalSeconds(request.getValueInt()); break; - case 37: setReportIntervalMinutes(request.getValueInt()); break; - case 38: setReportIntervalHours(request.getValueInt()); break; - case 39: setReportIntervalDays(request.getValueInt()); break; - #if BATTERY_MANAGER == 1 - case 40: setBatteryReportSeconds(request.getValueInt()); break; - case 41: setBatteryReportHours(request.getValueInt()); break; - case 42: setBatteryReportDays(request.getValueInt()); break; - #endif - default: return; - } - _sendUsingConfigChild(_msg.set(function)); -} - - -// Send a hello message back to the controller -void NodeManager::hello() { - // do nothing, the request will be echoed back -} - -#if BATTERY_MANAGER == 1 -// Send a battery level report to the controller -void NodeManager::batteryReport() { - // 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 - _sendUsingBatteryChild(_msg.set(volt, 2)); - #endif - // report battery level percentage - sendBatteryLevel(percentage,_ack); -} -#endif - -// reboot the board -void NodeManager::reboot() { - #if DEBUG == 1 - Serial.println(F("REBOOT")); - #endif - if (_reboot_pin > -1) { - // reboot the board through the reboot pin which is connected to RST by setting it to low - digitalWrite(_reboot_pin, LOW); - } else { - // Software reboot with watchdog timer. Enter Watchdog Configuration mode: - WDTCSR |= (1< -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_initial > -1) digitalWrite(INTERRUPT_PIN_1,_interrupt_1_initial); - // for non sleeping nodes, we need to handle the interrupt by ourselves - if (_status != SLEEP) attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN_1), _onInterrupt_1, _interrupt_1_mode); - } - if (_interrupt_2_mode != MODE_NOT_DEFINED) { - pinMode(INTERRUPT_PIN_2, INPUT); - if (_interrupt_2_initial > -1) digitalWrite(INTERRUPT_PIN_2,_interrupt_2_initial); - // for non sleeping nodes, we need to handle the interrupt by ourselves - if (_status != SLEEP) attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN_2), _onInterrupt_2, _interrupt_2_mode); - } - #if DEBUG == 1 - Serial.print(F("INT P=")); - Serial.print(INTERRUPT_PIN_1); - Serial.print(F(" M=")); - Serial.println(_interrupt_1_mode); - Serial.print(F("INT P=")); - Serial.print(INTERRUPT_PIN_2); - Serial.print(F(" M=")); - Serial.println(_interrupt_2_mode); - #endif -} - -// return the pin from which the last interrupt came -int NodeManager::getLastInterruptPin() { - return _last_interrupt_pin; -} - -// set the default interval in seconds all the sensors will report their measures -void NodeManager::setReportIntervalSeconds(int value) { - _report_interval_seconds = value; -} - -// set the default interval in minutes all the sensors will report their measures -void NodeManager::setReportIntervalMinutes(int value) { - _report_interval_seconds = value*60; -} - -// set the default interval in hours all the sensors will report their measures -void NodeManager::setReportIntervalHours(int value) { - _report_interval_seconds = value*60*60; -} - -// set the default interval in days all the sensors will report their measures -void NodeManager::setReportIntervalDays(int value) { - _report_interval_seconds = value*60*60*24; -} - -// if set and when the board is battery powered, sleep() is always called instead of wait() -void NodeManager::setSleepOrWait(bool value) { - _sleep_or_wait = value; -} - -// set which pin is connected to RST of the board to reboot the board when requested. If not set the software reboot is used instead (default: -1) -void NodeManager::setRebootPin(int value) { - _reboot_pin = value; -} - -// turn the ADC off so to save 0.2 mA -void NodeManager::setADCOff() { - // Disable the ADC by setting the ADEN bit (bit 7) to zero - ADCSRA = ADCSRA & B01111111; - // Disable the analog comparator by setting the ACD bit (bit 7) to one - ACSR = B10000000; -} - -// sleep if the node is a battery powered or wait if it is not for the given number of milliseconds -void NodeManager::sleepOrWait(long value) { - // if the node is sleeping, sleep-or-wait is enabled and we need to sleep for a decent amount of time, call sleep() otherwise wait() - if (isSleepingNode() && _sleep_or_wait && value > 200) sleep(value); - else wait(value); -} - -#if SIGNAL_SENSOR == 1 && defined(MY_SIGNAL_REPORT_ENABLED) - void NodeManager::setSignalReportSeconds(int value) { - _signal_report_timer.set(value,SECONDS); - } - void NodeManager::setSignalReportMinutes(int value) { - _signal_report_timer.set(value,MINUTES); - } - void NodeManager::setSignalReportHours(int value) { - _signal_report_timer.set(value,HOURS); - } - void NodeManager::setSignalReportDays(int value) { - _signal_report_timer.set(value,DAYS); - } - void NodeManager::setSignalCommand(int value) { - _signal_command = value; - } - void NodeManager::signalReport() { - int16_t value = transportGetSignalReport(_signal_command); - #if DEBUG == 1 - Serial.print(F("SIG V=")); - Serial.println(value); - #endif - // report signal level - _sendSignalChild(_msg.set(value)); - } -#endif - -// handle an interrupt -void NodeManager::_onInterrupt_1() { - long now = millis(); - if ( (now - _last_interrupt_1 > _interrupt_min_delta) || (now < _last_interrupt_1) ) { - _last_interrupt_pin = INTERRUPT_PIN_1; - #if DEBUG == 1 - Serial.print(F("INT P=")); - Serial.println(INTERRUPT_PIN_1); - #endif - _last_interrupt_1 = now; - } -} -void NodeManager::_onInterrupt_2() { - long now = millis(); - if ( (now - _last_interrupt_2 > _interrupt_min_delta) || (now < _last_interrupt_2) ) { - _last_interrupt_pin = INTERRUPT_PIN_2; - #if DEBUG == 1 - Serial.print(F("INT P=")); - Serial.println(INTERRUPT_PIN_2); - #endif - _last_interrupt_2 = now; - } -} - -// send a message to the network using CONFIGURATION_CHILD_ID, V_CUSTOM -void NodeManager::_sendUsingConfigChild(MyMessage & message) { - // setup the message - message.setSensor(CONFIGURATION_CHILD_ID); - message.setType(V_CUSTOM); - sendMessage(); -} - -// send a message to the network using BATTERY_CHILD_ID, V_VOLTAGE -void NodeManager::_sendUsingBatteryChild(MyMessage & message) { - // setup the message - message.setSensor(BATTERY_CHILD_ID); - message.setType(V_VOLTAGE); - sendMessage(); -} - -// send a message to the network using SIGNAL_CHILD_ID, V_LEVEL -void NodeManager::_sendUsingSignalChild(MyMessage & message) { - // setup the message - message.setSensor(SIGNAL_CHILD_ID); - message.setType(V_LEVEL); - sendMessage(); -} - - -// send a message to the network -void NodeManager::sendMessage() { - // 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(_msg.destination); - Serial.print(F(" I=")); - Serial.print(_msg.sensor); - Serial.print(F(" C=")); - Serial.print(_msg.getCommand()); - Serial.print(F(" T=")); - Serial.print(_msg.type); - Serial.print(F(" S=")); - Serial.print(_msg.getString()); - Serial.print(F(" I=")); - Serial.print(_msg.getInt()); - Serial.print(F(" F=")); - Serial.println(_msg.getFloat()); - #endif - send(_msg, _ack); - } -} - -// wrapper of smart sleep -void NodeManager::_sleep() { - #if DEBUG == 1 - Serial.print(F("SLEEP ")); - Serial.print(_sleep_time); - Serial.println(F("s")); - #endif - #if SERVICE_MESSAGES == 1 - // notify the controller I'm going to sleep - _sendUsingConfigChild(_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; - // 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_time*1000, 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("INT 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) _status = AWAKE; - } - // coming out of sleep - #if DEBUG == 1 - Serial.println(F("AWAKE")); - #endif - #if SERVICE_MESSAGES == 1 - // notify the controller I am awake - _sendUsingConfigChild(_msg.set("AWAKE")); - #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 <= MAX_SENSORS; i++) { - if (i == CONFIGURATION_CHILD_ID) continue; - if (i == BATTERY_CHILD_ID) continue; - // empty place, return it - if (_sensors[i] == 0) return i; - } - return MAX_SENSORS; -} - -// 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; -} - -// load the configuration stored in the eeprom -void NodeManager::_loadConfig() { - if (loadState(EEPROM_SLEEP_SAVED) == 1) { - // load sleep settings - int bit_1 = loadState(EEPROM_SLEEP_1); - int bit_2 = loadState(EEPROM_SLEEP_2); - int bit_3 = loadState(EEPROM_SLEEP_3); - _sleep_time = bit_3*255*255 + bit_2*255 + bit_1; - #if DEBUG == 1 - Serial.print(F("LOADSLP T=")); - Serial.println(_sleep_time); - #endif - } -} - -// save the configuration in the eeprom -void NodeManager::_saveConfig() { - if (_sleep_time == 0) return; - // encode the sleep time in 3 bits - int bit_1, bit_2, bit_3 = 0; - bit_1 = _sleep_time; - if (bit_1 >= 255) { - bit_2 = (int)bit_1/255; - bit_1 = bit_1 - bit_2*255; - } - if (bit_2 >= 255) { - bit_3 = (int)bit_2/255; - bit_2 = bit_2 - bit_3*255; - } - // save the 3 bits - saveState(EEPROM_SLEEP_SAVED,1); - saveState(EEPROM_SLEEP_1,bit_1); - saveState(EEPROM_SLEEP_2,bit_2); - saveState(EEPROM_SLEEP_3,bit_3); -} diff --git a/NodeManager.ino b/NodeManager.ino index 38bf742c..db2f9d9f 100755 --- a/NodeManager.ino +++ b/NodeManager.ino @@ -1,74 +1,325 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2016 Sensnology AB + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + ******************************* -/* -NodeManager is intended to take care on your behalf of all those common tasks a MySensors node has to accomplish, speeding up the development cycle of your projects. -NodeManager includes the following main components: -- Sleep manager: allows managing automatically the complexity behind battery-powered sensors spending most of their time sleeping -- Power manager: allows powering on your sensors only while the node is awake -- Battery manager: provides common functionalities to read and report the battery level -- Remote configuration: allows configuring remotely the node without the need to have physical access to it -- Built-in personalities: for the most common sensors, provide embedded code so to allow their configuration with a single line + DESCRIPTION + +NodeManager is intended to take care on your behalf of all those common tasks a MySensors node has to accomplish, +speeding up the development cycle of your projects. Documentation available on: https://github.com/mysensors/NodeManager +NodeManager provides built-in implementation of a number of sensors through ad-hoc classes. + +To use a buil-in sensor: +* Install the required library if any +* Enable the corresponding module (uncomment it) in the main sketch +* Declare the sensor (uncomment it) in the main sketch + +Once created, the sensor will automatically present one or more child to the gateway and controller. +A list of buil-in sensors, module to enable, required dependencies and the number of child automatically created is presented below: + +Sensor Name |#Child | Module to enable | Description | Dependencies +--------------------|-------|-----------------------|---------------------------------------------------------------------------------------------------|---------------------------------------------------------- +SensorBattery | 1 | - | Built-in sensor for automatic battery reporting | - +SensorSignal | 1 | - | Built-in sensor for automatic signal level reporting | - +SensorConfiguration | 1 | - | Built-in sensor for OTA remote configuration of any registered sensor | - +SensorAnalogInput | 1 | MODULE_ANALOG_INPUT | Generic analog sensor, return a pin's analog value or its percentage | - +SensorLDR | 1 | MODULE_ANALOG_INPUT | LDR sensor, return the light level of an attached light resistor in percentage | - +SensorRain | 1 | MODULE_ANALOG_INPUT | Rain sensor, return the percentage of rain from an attached analog sensor | - +SensorSoilMoisture | 1 | MODULE_ANALOG_INPUT | Soil moisture sensor, return the percentage of moisture from an attached analog sensor | - +SensorThermistor | 1 | MODULE_THERMISTOR | Thermistor sensor, return the temperature based on the attached thermistor | - +SensorML8511 | 1 | MODULE_ML8511 | ML8511 sensor, return UV intensity | - +SensorACS712 | 1 | MODULE_ACS712 | ACS712 sensor, measure the current going through the attached module | - +SensorDigitalInput | 1 | MODULE_DIGITAL_INPUT | Generic digital sensor, return a pin's digital value | - +SensorDigitalOutput | 1 | MODULE_DIGITAL_OUTPUT | Generic digital output sensor, allows setting the digital output of a pin to the requested value | - +SensorRelay | 1 | MODULE_DIGITAL_OUTPUT | Relay sensor, allows activating the relay | - +SensorLatchingRelay | 1 | MODULE_DIGITAL_OUTPUT | Latching Relay sensor, allows activating the relay with a pulse | - +SensorDHT11 | 2 | MODULE_DHT | DHT11 sensor, return temperature/humidity based on the attached DHT sensor | https://github.com/mysensors/MySensorsArduinoExamples/tree/master/libraries/DHT +SensorDHT22 | 2 | MODULE_DHT | DHT22 sensor, return temperature/humidity based on the attached DHT sensor | https://github.com/mysensors/MySensorsArduinoExamples/tree/master/libraries/DHT +SensorSHT21 | 2 | MODULE_SHT21 | SHT21 sensor, return temperature/humidity based on the attached SHT21 sensor | https://github.com/SodaqMoja/Sodaq_SHT2x +SensorHTU21D | 2 | MODULE_SHT21 | HTU21D sensor, return temperature/humidity based on the attached HTU21D sensor | https://github.com/SodaqMoja/Sodaq_SHT2x +SensorSwitch | 1 | MODULE_SWITCH | Generic switch, wake up the board when a pin changes status | - +SensorDoor | 1 | MODULE_SWITCH | Door sensor, wake up the board and report when an attached magnetic sensor has been opened/closed | - +SensorMotion | 1 | MODULE_SWITCH | Motion sensor, wake up the board and report when an attached PIR has triggered | - +SensorDs18b20 | 1+ | MODULE_DS18B20 | DS18B20 sensor, return the temperature based on the attached sensor | https://github.com/milesburton/Arduino-Temperature-Control-Library +SensorBH1750 | 1 | MODULE_BH1750 | BH1750 sensor, return light level in lux | https://github.com/claws/BH1750 +SensorMLX90614 | 2 | MODULE_MLX90614 | MLX90614 contactless temperature sensor, return ambient and object temperature | https://github.com/adafruit/Adafruit-MLX90614-Library +SensorBME280 | 4 | MODULE_BME280 | BME280 sensor, return temperature/humidity/pressure based on the attached BME280 sensor | https://github.com/adafruit/Adafruit_BME280_Library +SensorBMP085 | 3 | MODULE_BMP085 | BMP085/BMP180 sensor, return temperature and pressure | https://github.com/adafruit/Adafruit-BMP085-Library +SensorBMP280 | 3 | MODULE_BMP280 | BMP280 sensor, return temperature/pressure based on the attached BMP280 sensor | https://github.com/adafruit/Adafruit_BMP280_Library +SensorSonoff | 1 | MODULE_SONOFF | Sonoff wireless smart switch | https://github.com/thomasfredericks/Bounce2 +SensorHCSR04 | 1 | MODULE_HCSR04 | HC-SR04 sensor, return the distance between the sensor and an object | https://github.com/mysensors/MySensorsArduinoExamples/tree/master/libraries/NewPing +SensorMCP9808 | 1 | MODULE_MCP9808 | MCP9808 sensor, measure the temperature through the attached module | https://github.com/adafruit/Adafruit_MCP9808_Library +SensorMQ | 1 | MODULE_MQ | MQ sensor, return ppm of the target gas | - +SensorMHZ19 | 1 | MODULE_MHZ19 | MH-Z19 CO2 sensor via UART (SoftwareSerial, default on pins 6(Rx) and 7(Tx) | - +SensorAM2320 | 2 | MODULE_AM2320 | AM2320 sensors, return temperature/humidity based on the attached AM2320 sensor | https://github.com/thakshak/AM2320 +SensorTSL2561 | 1 | MODULE_TSL2561 | TSL2561 sensor, return light in lux | https://github.com/adafruit/TSL2561-Arduino-Library +SensorPT100 | 1 | MODULE_PT100 | DFRobot Driver high temperature sensor, return the temperature from the attached PT100 sensor | - +SensorDimmer | 1 | MODULE_DIMMER | Generic dimmer sensor used to drive a pwm output | - +SensorRainGauge | 1 | MODULE_PULSE_METER | Rain gauge sensor | - +SensorPowerMeter | 1 | MODULE_PULSE_METER | Power meter pulse sensor | - +SensorWaterMeter | 1 | MODULE_PULSE_METER | Water meter pulse sensor | - +SensorPlantowerPMS | 3 | MODULE_PMS | Plantower PMS particulate matter sensors (reporting PM<=1.0, PM<=2.5 and PM<=10.0 in µg/m³) | https://github.com/fu-hsi/pms + */ +/********************************** + * MySensors node configuration + */ + +// General settings +#define SKETCH_NAME "NodeManager" +#define SKETCH_VERSION "1.0" +#define MY_BAUD_RATE 9600 +//#define MY_DEBUG +//#define MY_NODE_ID 99 +//#define MY_SMART_SLEEP_WAIT_DURATION_MS 500 +#define MY_SPLASH_SCREEN_DISABLED + +// NRF24 radio settings +#define MY_RADIO_NRF24 +//#define MY_RF24_ENABLE_ENCRYPTION +//#define MY_RF24_CHANNEL 125 +//#define MY_RF24_PA_LEVEL RF24_PA_HIGH +//#define MY_NODEMANAGER_DEBUG_VERBOSE_RF24 +//#define MY_RF24_DATARATE RF24_250KBPS + +// RFM69 radio settings +//#define MY_RADIO_RFM69 +//#define MY_RFM69_FREQUENCY RF69_868MHZ +//#define MY_RFM69_FREQUENCY RFM69_868MHZ +//#define MY_IS_RFM69HW +//#define MY_RFM69_NEW_DRIVER +//#define MY_RFM69_ENABLE_ENCRYPTION +//#define MY_RFM69_NETWORKID 100 +//#define MY_NODEMANAGER_DEBUG_VERBOSE_RFM69 +//#define MY_RF69_IRQ_PIN D1 +//#define MY_RF69_IRQ_NUM MY_RF69_IRQ_PIN +//#define MY_RF69_SPI_CS D2 +//#define MY_RFM69_ATC_MODE_DISABLED + +// RS485 serial transport settings +//#define MY_RS485 +//#define MY_RS485_BAUD_RATE 9600 +//#define MY_RS485_DE_PIN 2 +//#define MY_RS485_MAX_MESSAGE_LENGTH 40 +//#define MY_RS485_HWSERIAL Serial1 + +// Message signing settings +//#define MY_SIGNING_SOFT +//#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 +//#define MY_SIGNING_REQUEST_SIGNATURES +//#define MY_SIGNING_ATSHA204 + +// OTA Firmware update settings +//#define MY_OTA_FIRMWARE_FEATURE +//#define OTA_WAIT_PERIOD 300 +//#define FIRMWARE_MAX_REQUESTS 2 +//#define MY_OTA_RETRY 2 + +/********************************** + * MySensors gateway configuration + */ -// load user settings -#include "config.h" -// include supporting libraries -#ifdef MY_GATEWAY_ESP8266 - #include -#endif -// load MySensors library -#include -// load NodeManager library -#include "NodeManager.h" - -// create a NodeManager instance -NodeManager nodeManager; +// Common gateway settings +//#define MY_REPEATER_FEATURE + +// Serial gateway settings +//#define MY_GATEWAY_SERIAL + +// Ethernet gateway settings +//#define MY_GATEWAY_W5100 + +// ESP8266 gateway settings +//#define MY_GATEWAY_ESP8266 +//#define MY_ESP8266_SSID "" +//#define MY_ESP8266_PASSWORD "" + +// Gateway networking settings +//#define MY_IP_ADDRESS 192,168,178,87 +//#define MY_IP_GATEWAY_ADDRESS 192,168,178,1 +//#define MY_IP_SUBNET_ADDRESS 255,255,255,0 +//#define MY_PORT 5003 +//#define MY_GATEWAY_MAX_CLIENTS 2 +//#define MY_USE_UDP + +// Gateway MQTT settings +//#define MY_GATEWAY_MQTT_CLIENT +//#define MY_CONTROLLER_IP_ADDRESS 192, 168, 178, 68 +//#define MY_PORT 1883 +//#define MY_MQTT_USER "username" +//#define MY_MQTT_PASSWORD "password" +//#define MY_MQTT_CLIENT_ID "mysensors-1" +//#define MY_MQTT_PUBLISH_TOPIC_PREFIX "mygateway1-out" +//#define MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "mygateway1-in" + +// Gateway inclusion mode +//#define MY_INCLUSION_MODE_FEATURE +//#define MY_INCLUSION_BUTTON_FEATURE +//#define MY_INCLUSION_MODE_DURATION 60 +//#define MY_DEFAULT_LED_BLINK_PERIOD 300 + +// Gateway Leds settings +//#define MY_DEFAULT_ERR_LED_PIN 4 +//#define MY_DEFAULT_RX_LED_PIN 5 +//#define MY_DEFAULT_TX_LED_PIN 6 + +/*********************************** + * NodeManager modules + */ + +//#define MODULE_ANALOG_INPUT +//#define MODULE_THERMISTOR +//#define MODULE_ML8511 +//#define MODULE_ACS712 +//#define MODULE_DIGITAL_INPUT +//#define MODULE_DIGITAL_OUTPUT +//#define MODULE_DHT +//#define MODULE_SHT21 +//#define MODULE_SWITCH +//#define MODULE_DS18B20 +//#define MODULE_BH1750 +//#define MODULE_MLX90614 +//#define MODULE_BME280 +//#define MODULE_BMP085 +//#define MODULE_BMP280 +//#define MODULE_SONOFF +//#define MODULE_HCSR04 +//#define MODULE_MCP9808 +//#define MODULE_MQ +//#define MODULE_MHZ19 +//#define MODULE_AM2320 +//#define MODULE_TSL2561 +//#define MODULE_PT100 +//#define MODULE_DIMMER +//#define MODULE_PULSE_METER +//#define MODULE_PMS + +/*********************************** + * Load NodeManager Library + */ + +// enable NodeManager's debug on serial port +#define NODEMANAGER_DEBUG +// include NodeManager's library +#include "NodeManagerLibrary.h" +NodeManager node; + +/*********************************** + * Add your sensors below + */ + +//SensorBattery battery(node); +//SensorConfiguration configuration(node); +//SensorSignal signal(node); +//PowerManager power(5,6); + +//SensorAnalogInput analog(node,A0); +//SensorLDR ldr(node,A0); +//SensorRain rain(node,A0); +//SensorSoilMoisture soil(node,A0); +//SensorThermistor thermistor(node,A0); +//SensorML8511 ml8511(node,A0); +//SensorACS712 acs712(node,A0); +//SensorDigitalInput digitalIn(node,6); +//SensorDigitalOutput digitalOut(node,6); +//SensorRelay relay(node,6); +//SensorLatchingRelay latching(node,6); +//SensorDHT11 dht11(node,6); +//SensorDHT22 dht22(node,6); +//SensorSHT21 sht21(node); +//SensorHTU21D htu21(node); +//SensorSwitch sensorSwitch(node,3); +//SensorDoor door(node,3); +//SensorMotion motion(node,3); +//SensorDs18b20 ds18b20(node,6); +//SensorBH1750 bh1750(node); +//SensorMLX90614 mlx90614(node); +//SensorBME280 bme280(node); +//SensorBMP085 bmp085(node); +//SensorBMP280 bmp280(node); +//SensorSonoff sonoff(node); +//SensorHCSR04 hcsr04(node,6); +//SensorMCP9808 mcp9808(node); +//SensorMQ mq(node,A0); +//SensorMHZ19 mhz19(node,6,7); +//SensorAM2320 am2320(node); +//SensorTSL2561 tsl2561(node); +//SensorPT100 pt100(node,6); +//SensorDimmer dimmer(node,A0); +//SensorRainGauge rainGauge(node,3); +//SensorPowerMeter powerMeter(node,3); +//SensorWaterMeter waterMeter(node,3); +//SensorPlantowerPMS pms(node,6,7); + +/*********************************** + * Main Sketch + */ // before void before() { // setup the serial port baud rate - Serial.begin(MY_BAUD_RATE); + Serial.begin(MY_BAUD_RATE); /* - * Register below your sensors + * Configure your sensors below */ + + //node.setReportIntervalMinutes(5); + //node.setSleepMinutes(5); - + //node.setPowerManager(power); + //battery.setReportIntervalMinutes(30); + //sht.children.get(1)->child_id = 5; /* - * Register above your sensors + * Configure your sensors above */ - nodeManager.before(); + node.before(); } // presentation void presentation() { // call NodeManager presentation routine - nodeManager.presentation(); + node.presentation(); } // setup void setup() { // call NodeManager setup routine - nodeManager.setup(); + node.setup(); } // loop void loop() { // call NodeManager loop routine - nodeManager.loop(); + node.loop(); } // receive -void receive(const MyMessage &message) { +void receive(MyMessage &message) { // call NodeManager receive routine - nodeManager.receive(message); + node.receive(message); } // receiveTime void receiveTime(unsigned long ts) { // call NodeManager receiveTime routine - nodeManager.receiveTime(ts); + node.receiveTime(ts); } diff --git a/NodeManager.h b/NodeManagerLibrary.h similarity index 54% rename from NodeManager.h rename to NodeManagerLibrary.h index 7660dee5..d75dea76 100644 --- a/NodeManager.h +++ b/NodeManagerLibrary.h @@ -1,6 +1,7 @@ /* - * NodeManager + * NodeManager Library */ + #ifndef NodeManager_h #define NodeManager_h @@ -12,7 +13,6 @@ /*********************************** Constants */ - // define board status #define AWAKE 0 #define SLEEP 1 @@ -44,52 +44,10 @@ #define EEPROM_SLEEP_3 7 #define EEPROM_USER_START 100 -// define requests - -/************************************ - * Include user defined configuration settings - */ - -#include "config.h" - /*********************************** Default configuration settings */ -// if enabled, enable debug messages on serial port -#ifndef DEBUG - #define DEBUG 1 -#endif - -// if enabled, enable the capability to power on sensors with the arduino's pins to save battery while sleeping -#ifndef POWER_MANAGER - #define POWER_MANAGER 1 -#endif -// if enabled, will load the battery manager library to allow the battery level to be reported automatically or on demand -#ifndef BATTERY_MANAGER - #define BATTERY_MANAGER 1 -#endif // if enabled, allow modifying the configuration remotely by interacting with the configuration child id -#ifndef REMOTE_CONFIGURATION - #define REMOTE_CONFIGURATION 1 -#endif -// if enabled, persist the configuration settings on EEPROM -#ifndef PERSIST - #define PERSIST 0 -#endif - -// if enabled, send a SLEEPING and AWAKE service messages just before entering and just after leaving a sleep cycle -#ifndef SERVICE_MESSAGES - #define SERVICE_MESSAGES 0 -#endif -// if enabled, a battery sensor will be created at BATTERY_CHILD_ID (201 by default) and will report vcc voltage together with the battery level percentage -#ifndef BATTERY_SENSOR - #define BATTERY_SENSOR 1 -#endif -// if enabled, a RSSI sensor will be created at SIGNAL_CHILD_ID (202 by default) and will report the signal quality of the transport layer -#ifndef SIGNAL_SENSOR - #define SIGNAL_SENSOR 1 -#endif - // the child id used to allow remote configuration #ifndef CONFIGURATION_CHILD_ID #define CONFIGURATION_CHILD_ID 200 @@ -102,10 +60,6 @@ #ifndef SIGNAL_CHILD_ID #define SIGNAL_CHILD_ID 202 #endif -// define the maximum number of sensors that can be managed -#ifndef MAX_SENSORS - #define MAX_SENSORS 10 -#endif // define default sketch name and version #ifndef SKETCH_NAME #define SKETCH_NAME "NodeManager" @@ -114,220 +68,6 @@ #define SKETCH_VERSION "1.0" #endif - -/*********************************** - Default module settings -*/ - -// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN, SENSOR_SOIL_MOISTURE -#ifndef MODULE_ANALOG_INPUT - #define MODULE_ANALOG_INPUT 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_DIGITAL_INPUT -#ifndef MODULE_DIGITAL_INPUT - #define MODULE_DIGITAL_INPUT 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_DIGITAL_OUTPUT, SENSOR_RELAY, SENSOR_LATCHING_RELAY -#ifndef MODULE_DIGITAL_OUTPUT - #define MODULE_DIGITAL_OUTPUT 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22 -#ifndef MODULE_DHT - #define MODULE_DHT 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_SHT21, SENSOR_HTU21D -#ifndef MODULE_SHT21 - #define MODULE_SHT21 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_SWITCH, SENSOR_DOOR, SENSOR_MOTION -#ifndef MODULE_SWITCH - #define MODULE_SWITCH 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_DS18B20 -#ifndef MODULE_DS18B20 - #define MODULE_DS18B20 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_BH1750 -#ifndef MODULE_BH1750 - #define MODULE_BH1750 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_MLX90614 -#ifndef MODULE_MLX90614 - #define MODULE_MLX90614 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_BME280 -#ifndef MODULE_BME280 - #define MODULE_BME280 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_SONOFF -#ifndef MODULE_SONOFF - #define MODULE_SONOFF 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_BMP085 -#ifndef MODULE_BMP085 - #define MODULE_BMP085 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_HCSR04 -#ifndef MODULE_HCSR04 - #define MODULE_HCSR04 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_MCP9808 -#ifndef MODULE_MCP9808 - #define MODULE_MCP9808 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_MQ -#ifndef MODULE_MQ - #define MODULE_MQ 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_MHZ19 -#ifndef MODULE_MHZ19 - #define MODULE_MHZ19 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_AM2320 -#ifndef MODULE_AM2320 - #define MODULE_AM2320 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_TSL2561 -#ifndef MODULE_TSL2561 - #define MODULE_TSL2561 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_PT100 -#ifndef MODULE_PT100 - #define SENSOR_PT100 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_BMP280 -#ifndef MODULE_BMP280 - #define MODULE_BMP280 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_DIMMER -#ifndef MODULE_DIMMER - #define MODULE_DIMMER 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_RAIN_GAUGE, SENSOR_POWER_METER, SENSOR_WATER_METER -#ifndef MODULE_PULSE_METER - #define MODULE_PULSE_METER 0 -#endif - -/*********************************** - Supported Sensors -*/ -enum supported_sensors { - #if MODULE_ANALOG_INPUT == 1 - // Generic analog sensor, return a pin's analog value or its percentage - SENSOR_ANALOG_INPUT, - // LDR sensor, return the light level of an attached light resistor in percentage - SENSOR_LDR, - // Thermistor sensor, return the temperature based on the attached thermistor - SENSOR_THERMISTOR, - // ML8511 UV sensor - SENSOR_ML8511, - // Current sensor - SENSOR_ACS712, - // Rain sensor, return the percentage of rain from an attached analog sensor - SENSOR_RAIN, - // Soil moisture sensor, return the percentage of moisture from an attached analog sensor - SENSOR_SOIL_MOISTURE, - #endif - #if MODULE_DIGITAL_INPUT == 1 - // Generic digital sensor, return a pin's digital value - SENSOR_DIGITAL_INPUT, - #endif - #if MODULE_DIGITAL_OUTPUT == 1 - // Generic digital output sensor, allows setting the digital output of a pin to the requested value - SENSOR_DIGITAL_OUTPUT, - // Relay sensor, allows activating the relay - SENSOR_RELAY, - // Latching Relay sensor, allows activating the relay with a pulse - SENSOR_LATCHING_RELAY, - #endif - #if MODULE_DHT == 1 - // DHT11/DHT22 sensors, return temperature/humidity based on the attached DHT sensor - SENSOR_DHT11, - SENSOR_DHT22, - #endif - #if MODULE_SHT21 == 1 - // SHT21 sensor, return temperature/humidity based on the attached SHT21 sensor - SENSOR_SHT21, - SENSOR_HTU21D, - #endif - #if MODULE_SWITCH == 1 - // Generic switch, wake up the board when a pin changes status - SENSOR_SWITCH, - // Door sensor, wake up the board and report when an attached magnetic sensor has been opened/closed - SENSOR_DOOR, - // Motion sensor, wake up the board and report when an attached PIR has triggered - SENSOR_MOTION, - #endif - #if MODULE_DS18B20 == 1 - // DS18B20 sensor, return the temperature based on the attached sensor - SENSOR_DS18B20, - #endif - #if MODULE_BH1750 == 1 - // BH1750 sensor, return light in lux - SENSOR_BH1750, - #endif - #if MODULE_MLX90614 == 1 - // MLX90614 sensor, contactless temperature sensor - SENSOR_MLX90614, - #endif - #if MODULE_BME280 == 1 - // BME280 sensor, return temperature, humidity and pressure - SENSOR_BME280, - #endif - #if MODULE_SONOFF == 1 - // Sonoff wireless smart switch - SENSOR_SONOFF, - #endif - #if MODULE_BMP085 == 1 - // BMP085/BMP180 sensor, return temperature and pressure - SENSOR_BMP085, - #endif - #if MODULE_HCSR04 == 1 - // HC-SR04 sensor, return the distance between the sensor and an object - SENSOR_HCSR04, - #endif - #if MODULE_MCP9808 == 1 - // MCP9808 sensor, precision temperature sensor - SENSOR_MCP9808, - #endif - #if MODULE_MQ == 1 - // MQ2 air quality sensor - SENSOR_MQ, - #endif - #if MODULE_MHZ19 == 1 - // MH-Z19 CO2 sensor - SENSOR_MHZ19, - #endif - #if MODULE_TSL2561 == 1 - // TSL2561 sensor, return light in lux - SENSOR_TSL2561, - #endif - #if MODULE_AM2320 == 1 - // AM2320 sensors, return temperature/humidity based on the attached AM2320 sensor - SENSOR_AM2320, - #endif - #if MODULE_PT100 == 1 - // High temperature sensor associated with DFRobot Driver, return the temperature in C° from the attached PT100 sensor - SENSOR_PT100, - #endif - #if MODULE_BMP280 == 1 - // BMP280 sensor, return temperature and pressure - SENSOR_BMP280, - #endif - #if MODULE_DIMMER == 1 - // Generic dimmer sensor used to drive a pwm output - SENSOR_DIMMER, - #endif - #if MODULE_PULSE_METER == 1 - // rain gauge sensor - SENSOR_RAIN_GAUGE, - // power meter pulse sensor - SENSOR_POWER_METER, - // water meter pulse sensor - SENSOR_WATER_METER, - #endif -}; - /*********************************** Libraries */ @@ -339,80 +79,145 @@ enum supported_sensors { #ifdef MY_GATEWAY_ESP8266 #include #endif - -// include MySensors libraries -#include -#include -#include -#include +// load MySensors library +#include // include third party libraries -#if MODULE_DHT == 1 +#ifdef MODULE_DHT #include #endif -#if MODULE_SHT21 == 1 +#ifdef MODULE_SHT21 #include #include #endif -#if MODULE_DS18B20 == 1 +#ifdef MODULE_DS18B20 #include #include #endif -#if MODULE_BH1750 == 1 +#ifdef MODULE_BH1750 #include #include #endif -#if MODULE_MLX90614 == 1 +#ifdef MODULE_MLX90614 #include #include #endif -#if MODULE_BME280 == 1 +#ifdef MODULE_BME280 #include #include #include #include #endif -#if MODULE_SONOFF == 1 +#ifdef MODULE_SONOFF #include #endif -#if MODULE_BMP085 == 1 +#ifdef MODULE_BMP085 #include #include #endif -#if MODULE_HCSR04 == 1 +#ifdef MODULE_HCSR04 #include #endif -#if MODULE_MCP9808 == 1 +#ifdef MODULE_MCP9808 #include #include "Adafruit_MCP9808.h" #endif -#if MODULE_MHZ19 == 1 +#ifdef MODULE_MHZ19 #include #endif -#if MODULE_AM2320 == 1 +#ifdef MODULE_AM2320 #include #include #endif -#if MODULE_TSL2561 == 1 +#ifdef MODULE_TSL2561 #include #include #endif -#if MODULE_PT100 == 1 +#ifdef MODULE_PT100 #include #endif -#if MODULE_BMP280 == 1 +#ifdef MODULE_BMP280 #include #include #include #endif -#if MODULE_DIMMER == 1 +#ifdef MODULE_DIMMER #include #endif +#ifdef MODULE_PMS + #include + #include +#endif /******************************************************************* Classes */ class NodeManager; +class Sensor; + +/* + * List + */ +template class List { +public: + typedef T* iterator; + List() { + _internalArray = NULL; + _endPosition = 0; + _allocBlocks = 0; + _preAllocBlocks = 0; + } + ~List() { + delete[] _internalArray; + _internalArray = NULL; + _endPosition = 0; + _allocBlocks = 0; + _preAllocBlocks = 0; + } + void push(T item) { + if (_endPosition == _allocBlocks) _AllocOneBlock(false); + _internalArray[_endPosition] = item; + ++_endPosition; + } + void pop() { + if (_endPosition == 0) return; + --_endPosition; + if (_allocBlocks > _preAllocBlocks) _DeAllocOneBlock(false); + } + T get(int position) { + position = position -1; + if (position > _endPosition) position = _endPosition; + return _internalArray[position]; + } + inline iterator begin() { return _internalArray; } + inline iterator end() { return _internalArray + _endPosition; } + inline bool empty() { return (_endPosition == 0); } + inline unsigned int size() { return _endPosition; } +private: + T* _internalArray; + int _endPosition; + int _allocBlocks; + int _preAllocBlocks; + void _AllocOneBlock(bool shiftItems) { + ++_allocBlocks; + T* newArray = new T[_allocBlocks]; + for (int i = 0; i < _endPosition; ++i) newArray[shiftItems ? (i + 1) : i] = _internalArray[i]; + delete[] _internalArray; + _internalArray = newArray; + } + void _DeAllocOneBlock(bool shiftItems) { + --_allocBlocks; + if (_allocBlocks == 0) { + delete[] _internalArray; + _internalArray = NULL; + return; + } + T* newArray = new T[_allocBlocks]; + for (int i = 0; i < _endPosition; ++i) newArray[i] = _internalArray[shiftItems ? (i + 1) : i]; + delete[] _internalArray; + _internalArray = newArray; + } +}; /* PowerManager @@ -420,9 +225,10 @@ class NodeManager; class PowerManager { public: - PowerManager() {}; + PowerManager(int ground_pin, int vcc_pin, int wait_time = 50); // to save battery the sensor can be optionally connected to two pins which will act as vcc and ground and activated on demand void setPowerPins(int ground_pin, int vcc_pin, int wait_time = 50); + // if enabled the pins will be automatically powered on while awake and off during sleeping // turns the power pins on void powerOn(); // turns the power pins on @@ -465,7 +271,7 @@ class Timer { // return the current elapsed time float getElapsed(); private: - NodeManager* _node_manager; + NodeManager* _node; int _target = 0; long _elapsed = 0; long _last_millis = 0; @@ -480,42 +286,108 @@ class Timer { class Request { public: - Request(const char* string); + Request(int child_id, const char* string); + // return the child id the message has been requested to + int getRecipientChildId(); + // return the child id the request is for + int getChildId(); // return the parsed function int getFunction(); // return the value as an int int getValueInt(); // return the value as a float float getValueFloat(); - // return the value as a string - char* getValueString(); private: - NodeManager* _node_manager; - int _function; - // Size of buffer to prevent overrun - char _value[MAX_PAYLOAD+1]; + int _function = -1; + int _child_id = -1; + int _recipient_child_id = -1; + float _value; +}; + +/*************************************** + Child: child class +*/ + +class Child { + public: + Child(); + Child(Sensor* sensor, int child_id, int presentation, int type, char* description); + int child_id; + int presentation = S_CUSTOM; + int type = V_CUSTOM; + char* description = ""; + Timer* force_update_timer; + virtual void sendValue(); + virtual bool isNewValue(); + protected: + int _samples = 0; + Sensor* _sensor; +}; + +class ChildInt: public Child { + public: + ChildInt(Sensor* sensor, int child_id, int presentation, int type, char* description); + void setValueInt(int value); + int getValueInt(); + void sendValue(); + bool isNewValue(); + private: + int _value; + int _last_value; + int _total = 0; +}; + +class ChildFloat: public Child { + public: + ChildFloat(Sensor* sensor, int child_id, int presentation, int type, char* description); + void setValueFloat(float value); + float getValueFloat(); + void sendValue(); + bool isNewValue(); + private: + float _value; + float _last_value; + float _total = 0; +}; + +class ChildDouble: public Child { + public: + ChildDouble(Sensor* sensor, int child_id, int presentation, int type, char* description); + void setValueDouble(double value); + double getValueDouble(); + void sendValue(); + bool isNewValue(); + private: + double _value; + double _last_value; + double _total = 0; +}; + +class ChildString: public Child { + public: + ChildString(Sensor* sensor, int child_id, int presentation, int type, char* description); + void setValueString(char* value); + char* getValueString(); + void sendValue(); + bool isNewValue(); + private: + char* _value = ""; + char* _last_value = ""; }; /*************************************** Sensor: generic sensor class */ + class Sensor { public: - Sensor(NodeManager* node_manager, int child_id, int pin); + Sensor(); + Sensor(NodeManager& node_manager, int pin); + // return the name of the sensor + char* getName(); // [1] where the sensor is attached to (default: not set) void setPin(int value); int getPin(); - // [2] child_id of this sensor (default: not set) - void setChildId(int value); - int getChildId(); - // presentation of this sensor (default: S_CUSTOM) - void setPresentation(int value); - int getPresentation(); - // [3] type of this sensor (default: V_CUSTOM) - void setType(int value); - int getType(); - // [4] description of the sensor (default: '') - void setDescription(char *value); // [5] For some sensors, the measurement can be queried multiple times and an average is returned (default: 1) void setSamples(int value); // [6] If more then one sample has to be taken, set the interval in milliseconds between measurements (default: 0) @@ -524,29 +396,12 @@ class Sensor { void setTrackLastValue(bool value); // [9] if track last value is enabled, force to send an update after the configured number of minutes void setForceUpdateMinutes(int value); - // [19] if track last value is enabled, force to send an update after the configured number of hours - void setForceUpdateHours(int value); - // [10] the value type of this sensor (default: TYPE_INTEGER) - void setValueType(int value); - int getValueType(); - // [11] for float values, set the float precision (default: 2) - void setFloatPrecision(int value); - // [21] for double values, set the double precision (default: 4) - void setDoublePrecision(int value); - #if POWER_MANAGER == 1 - // to save battery the sensor can be optionally connected to two pins which will act as vcc and ground and activated on demand - void setPowerPins(int ground_pin, int vcc_pin, int wait_time = 50); - // [12] if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) - void setAutoPowerPins(bool value); - // [13] manually turn the power on - void powerOn(); - // [14] manually turn the power off - void powerOff(); - #endif - // get the latest recorded value from the sensor - int getValueInt(); - float getValueFloat(); - char* getValueString(); + // to save battery the sensor can be optionally connected to two pins which will act as vcc and ground and activated on demand + void setPowerPins(int ground_pin, int vcc_pin, int wait_time = 50); + // [13] manually turn the power on + void powerOn(); + // [14] manually turn the power off + void powerOff(); // [17] After how many minutes the sensor will report back its measure (default: 10 minutes) void setReportIntervalSeconds(int value); // [16] After how many minutes the sensor will report back its measure (default: 10 minutes) @@ -557,68 +412,109 @@ class Sensor { void setReportIntervalDays(int value); // return true if the report interval has been already configured bool isReportIntervalConfigured(); - // process a remote request - void process(Request & request); // return the pin the interrupt is attached to int getInterruptPin(); // listen for interrupts on the given pin so interrupt() will be called when occurring void setInterrupt(int pin, int mode, int initial); + // set a previously configured PowerManager to the sensor so to powering it up with custom pins + void setPowerManager(PowerManager& powerManager); + // list of configured child + List children; // define what to do at each stage of the sketch - virtual void before(); - virtual void presentation(); - virtual void setup(); - virtual void loop(const MyMessage & message); - virtual void interrupt(); - virtual void receive(const MyMessage & message); + void before(); + void presentation(); + void setup(); + void loop(MyMessage* message); + void interrupt(); + void receive(MyMessage &message); // abstract functions, subclasses need to implement - virtual void onBefore() = 0; - virtual void onSetup() = 0; - virtual void onLoop() = 0; - virtual void onReceive(const MyMessage & message) = 0; - virtual void onProcess(Request & request) = 0; - virtual void onInterrupt() = 0; + virtual void onBefore(); + virtual void onSetup(); + virtual void onLoop(Child* child); + virtual void onReceive(MyMessage* message); + virtual void onInterrupt(); + Child* getChild(int child_id); + // register a child + void registerChild(Child* child); + NodeManager* _node; protected: - MyMessage* _msg; - NodeManager* _node_manager; + char* _name = ""; int _pin = -1; - int _child_id; - int _presentation = S_CUSTOM; - int _type = V_CUSTOM; - char* _description = ""; int _samples = 1; int _samples_interval = 0; bool _track_last_value = false; - int _value_type = TYPE_INTEGER; - int _float_precision = 2; - int _double_precision = 4; - int _value_int = -1; - float _value_float = -1; - double _value_double = -1; - double _last_value_double = -1; - char * _value_string = ""; - int _last_value_int = -1; - float _last_value_float = -1; - char * _last_value_string = ""; int _interrupt_pin = -1; - #if POWER_MANAGER == 1 - PowerManager _powerManager; - bool _auto_power_pins = true; - #endif + PowerManager* _powerManager = nullptr; Timer* _report_timer; - Timer* _force_update_timer; - void _sendSensorMessage(MyMessage & msg); - void _sendServiceMessage(MyMessage & msg); - bool _isReceive(const MyMessage & message); - bool _isWorthSending(bool comparison); }; -#if MODULE_ANALOG_INPUT == 1 +/* + SensorBattery: report battery level +*/ +class SensorBattery: public Sensor { + public: + SensorBattery(NodeManager& nodeManager); + // [102] the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) + void setMinVoltage(float value); + // [103] the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) + void setMaxVoltage(float value); + // [104] if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) + void setBatteryInternalVcc(bool value); + // [105] if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) + void setBatteryPin(int value); + // [106] if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075) + void setBatteryVoltsPerBit(float value); + // define what to do at each stage of the sketch + void onBefore(); + void onSetup(); + void onLoop(Child* child); + void onReceive(MyMessage* message); + protected: + float _battery_min = 2.6; + float _battery_max = 3.3; + bool _battery_internal_vcc = true; + int _battery_pin = -1; + float _battery_volts_per_bit = 0.003363075; +}; + +/* + SensorSignal: report RSSI signal strength from the radio +*/ +class SensorSignal: public Sensor { + public: + SensorSignal(NodeManager& nodeManager); + // [101] define which signal report to send. Possible values are SR_UPLINK_QUALITY, SR_TX_POWER_LEVEL, SR_TX_POWER_PERCENT, SR_TX_RSSI, SR_RX_RSSI, SR_TX_SNR, SR_RX_SNR (default: SR_RX_RSSI) + void setSignalCommand(int value); + // define what to do at each stage of the sketch + void onBefore(); + void onSetup(); + void onLoop(Child* child); + void onReceive(MyMessage* message); + protected: + int _signal_command = SR_RX_RSSI; +}; + +/* + SensorConfiguration: allow remote configuration of the board and any configured sensor +*/ +class SensorConfiguration: public Sensor { + public: + SensorConfiguration(NodeManager& nodeManager); + // define what to do at each stage of the sketch + void onBefore(); + void onSetup(); + void onLoop(Child* child); + void onReceive(MyMessage* message); + protected: +}; + +#ifdef MODULE_ANALOG_INPUT /* SensorAnalogInput: read the analog input of a configured pin */ class SensorAnalogInput: public Sensor { public: - SensorAnalogInput(NodeManager* node_manager, int child_id, int pin); + SensorAnalogInput(NodeManager& node_manager, int pin); // [101] the analog reference to use (default: not set, can be either INTERNAL or DEFAULT) void setReference(int value); // [102] reverse the value or the percentage (e.g. 70% -> 30%) (default: false) @@ -632,10 +528,8 @@ class SensorAnalogInput: public Sensor { // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); + void onLoop(Child* child); + void onReceive(MyMessage* message); protected: int _reference = -1; bool _reverse = false; @@ -651,15 +545,42 @@ class SensorAnalogInput: public Sensor { */ class SensorLDR: public SensorAnalogInput { public: - SensorLDR(NodeManager* node_manager, int child_id, int pin); + SensorLDR(NodeManager& node_manager, int pin); + // define what to do at each stage of the sketch + void onBefore(); + void onSetup(); +}; + +/* + SensorRain +*/ +class SensorRain: public SensorAnalogInput { + public: + SensorRain(NodeManager& node_manager, int pin); + // define what to do at each stage of the sketch + void onBefore(); + void onSetup(); +}; + +/* + SensorSoilMoisture +*/ +class SensorSoilMoisture: public SensorAnalogInput { + public: + SensorSoilMoisture(NodeManager& node_manager, int pin); + // define what to do at each stage of the sketch + void onBefore(); + void onSetup(); }; +#endif +#ifdef MODULE_THERMISTOR /* SensorThermistor: read the temperature from a thermistor */ class SensorThermistor: public Sensor { public: - SensorThermistor(NodeManager* node_manager, int child_id, int pin); + SensorThermistor(NodeManager& node_manager, int pin); // [101] resistance at 25 degrees C (default: 10000) void setNominalResistor(long value); // [102] temperature for nominal resistance (default: 25) @@ -673,10 +594,8 @@ class SensorThermistor: public Sensor { // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); + void onLoop(Child* child); + void onReceive(MyMessage* message); protected: long _nominal_resistor = 10000; int _nominal_temperature = 25; @@ -684,32 +603,34 @@ class SensorThermistor: public Sensor { long _series_resistor = 10000; float _offset = 0; }; +#endif +#ifdef MODULE_ML8511 /* SensorML8511 */ class SensorML8511: public Sensor { public: - SensorML8511(NodeManager* node_manager, int child_id, int pin); + SensorML8511(NodeManager& node_Manager, int pin); // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); + void onLoop(Child* child); + void onReceive(MyMessage* message); protected: float _mapfloat(float x, float in_min, float in_max, float out_min, float out_max); }; +#endif +#ifdef MODULE_ACS712 /* SensorACS712 */ class SensorACS712: public Sensor { public: - SensorACS712(NodeManager* node_manager, int child_id, int pin); + SensorACS712(NodeManager& node_manager, int pin); // [101] set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); void setmVPerAmp(int value); // [102] set ACS offset (default: 2500); @@ -717,57 +638,36 @@ class SensorACS712: public Sensor { // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); + void onLoop(Child* child); + void onReceive(MyMessage* message); protected: int _ACS_offset = 2500; int _mv_per_amp = 185; }; - -/* - SensorRain -*/ -class SensorRain: public SensorAnalogInput { - public: - SensorRain(NodeManager* node_manager, int child_id, int pin); -}; - -/* - SensorSoilMoisture -*/ -class SensorSoilMoisture: public SensorAnalogInput { - public: - SensorSoilMoisture(NodeManager* node_manager, int child_id, int pin); -}; #endif - -#if MODULE_DIGITAL_INPUT == 1 +#ifdef MODULE_DIGITAL_INPUT /* SensorDigitalInput: read the digital input of the configured pin */ class SensorDigitalInput: public Sensor { public: - SensorDigitalInput(NodeManager* node_manager, int child_id, int pin); + SensorDigitalInput(NodeManager& node_manager, int pin); // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); + void onLoop(Child* child); + void onReceive(MyMessage* message); }; #endif -#if MODULE_DIGITAL_OUTPUT == 1 +#ifdef MODULE_DIGITAL_OUTPUT /* SensorDigitalOutput: control a digital output of the configured pin */ class SensorDigitalOutput: public Sensor { public: - SensorDigitalOutput(NodeManager* node_manager, int child_id, int pin); + SensorDigitalOutput(NodeManager& node_manager, int pin); // [103] define which value to set to the output when set to on (default: HIGH) void setOnValue(int value); // [104] when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false) @@ -779,16 +679,14 @@ class SensorDigitalOutput: public Sensor { // [107] optionally wait for the given number of milliseconds after changing the status (default: 0) void setWaitAfterSet(int value); // manually switch the output to the provided value - void setStatus(int value); + void setStatus(Child* child, int value); // get the current state int getStatus(); // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); + void onLoop(Child* child); + void onReceive(MyMessage* message); protected: int _on_value = HIGH; int _status = OFF; @@ -796,18 +694,19 @@ class SensorDigitalOutput: public Sensor { bool _input_is_elapsed = false; int _wait_after_set = 0; Timer* _safeguard_timer; - void _setupPin(int pin); - virtual void _setStatus(int value); + void _setupPin(Child* child, int pin); + virtual void _setStatus(Child* child, int value); int _getValueToWrite(int value); }; - /* SensorRelay */ class SensorRelay: public SensorDigitalOutput { public: - SensorRelay(NodeManager* node_manager, int child_id, int pin); + SensorRelay(NodeManager& node_manager, int pin); + // define what to do at each stage of the sketch + void onBefore(); }; /* @@ -815,7 +714,7 @@ class SensorRelay: public SensorDigitalOutput { */ class SensorLatchingRelay: public SensorRelay { public: - SensorLatchingRelay(NodeManager* node_manager, int child_id, int pin); + SensorLatchingRelay(NodeManager& node_manager, int pin); // [201] set the duration of the pulse to send in ms to activate the relay (default: 50) void setPulseWidth(int value); // [202] set the pin which turns the relay off (default: the pin provided while registering the sensor) @@ -824,60 +723,63 @@ class SensorLatchingRelay: public SensorRelay { void setPinOn(int value); // define what to do at each stage of the sketch void onBefore(); - void onProcess(Request & request); + void onSetup(); protected: int _pin_on; int _pin_off; int _pulse_width = 50; - void _setStatus(int value); + void _setStatus(Child* child, int value); }; #endif /* SensorDHT */ -#if MODULE_DHT == 1 +#ifdef MODULE_DHT class SensorDHT: public Sensor { public: - SensorDHT(NodeManager* node_manager, int child_id, int pin, DHT* dht, int sensor_type, int dht_type); + SensorDHT(NodeManager& node_manager, int pin); // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); - // constants - const static int TEMPERATURE = 0; - const static int HUMIDITY = 1; + void onLoop(Child* child); + void onReceive(MyMessage* message); protected: DHT* _dht; int _dht_type; float _offset = 0; - int _sensor_type = 0; +}; + +/* + SensorDHT11 +*/ +class SensorDHT11: public SensorDHT { + public: + SensorDHT11(NodeManager& node_manager, int pin); +}; + +/* + SensorDHT22 +*/ +class SensorDHT22: public SensorDHT { + public: + SensorDHT22(NodeManager& node_manager, int pin); }; #endif /* SensorSHT21: temperature and humidity sensor */ -#if MODULE_SHT21 == 1 +#ifdef MODULE_SHT21 class SensorSHT21: public Sensor { public: - SensorSHT21(NodeManager* node_manager, int child_id, int sensor_type); + SensorSHT21(NodeManager& node_manager); // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); - // constants - const static int TEMPERATURE = 0; - const static int HUMIDITY = 1; + void onLoop(Child* child); + void onReceive(MyMessage* message); protected: - float _offset = 0; - int _sensor_type = 0; }; /* @@ -886,17 +788,17 @@ class SensorSHT21: public Sensor { class SensorHTU21D: public SensorSHT21 { public: - SensorHTU21D(NodeManager* node_manager, int child_id, int pin); + SensorHTU21D(NodeManager& nodeManager); }; #endif /* * SensorSwitch */ -#if MODULE_SWITCH == 1 +#ifdef MODULE_SWITCH class SensorSwitch: public Sensor { public: - SensorSwitch(NodeManager* node_manager, int child_id, int pin); + SensorSwitch(NodeManager& node_manager, int pin); // [101] set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) void setMode(int value); // [102] milliseconds to wait before reading the input (default: 0) @@ -908,9 +810,8 @@ class SensorSwitch: public Sensor { // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); + void onLoop(Child* child); + void onReceive(MyMessage* message); void onInterrupt(); protected: int _debounce = 0; @@ -924,7 +825,8 @@ class SensorSwitch: public Sensor { */ class SensorDoor: public SensorSwitch { public: - SensorDoor(NodeManager* node_manager, int child_id, int pin); + SensorDoor(NodeManager& node_manager, int pin); + void onBefore(); }; /* @@ -932,56 +834,49 @@ class SensorDoor: public SensorSwitch { */ class SensorMotion: public SensorSwitch { public: - SensorMotion(NodeManager* node_manager, int child_id, int pin); + SensorMotion(NodeManager& node_manager, int pin); + void onBefore(); + void onSetup(); }; #endif /* SensorDs18b20 */ -#if MODULE_DS18B20 == 1 +#ifdef MODULE_DS18B20 class SensorDs18b20: public Sensor { public: - SensorDs18b20(NodeManager* node_manager, int child_id, int pin, DallasTemperature* sensors, int index); + SensorDs18b20(NodeManager& node_manager, int pin); // returns the sensor's resolution in bits int getResolution(); // [101] set the sensor's resolution in bits void setResolution(int value); // [102] sleep while DS18B20 calculates temperature (default: false) void setSleepDuringConversion(bool value); - // return the sensors' device address - DeviceAddress* getDeviceAddress(); // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); + void onLoop(Child* child); + void onReceive(MyMessage* message); protected: - float _offset = 0; - int _index; bool _sleep_during_conversion = false; DallasTemperature* _sensors; - DeviceAddress _device_address; }; #endif /* SensorBH1750 */ -#if MODULE_BH1750 == 1 +#ifdef MODULE_BH1750 class SensorBH1750: public Sensor { public: - SensorBH1750(NodeManager* node_manager, int child_id); + SensorBH1750(NodeManager& node_manager); // [101] set sensor reading mode, e.g. BH1750_ONE_TIME_HIGH_RES_MODE void setMode(uint8_t mode); // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); + void onLoop(Child* child); + void onReceive(MyMessage* message); protected: BH1750* _lightSensor; }; @@ -990,20 +885,15 @@ class SensorBH1750: public Sensor { /* SensorMLX90614 */ -#if MODULE_MLX90614 == 1 +#ifdef MODULE_MLX90614 class SensorMLX90614: public Sensor { public: - SensorMLX90614(NodeManager* node_manager, int child_id, Adafruit_MLX90614* mlx, int sensor_type); + SensorMLX90614(NodeManager& node_manager); // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); - // constants - const static int TEMPERATURE_AMBIENT = 0; - const static int TEMPERATURE_OBJECT = 1; + void onLoop(Child* child); + void onReceive(MyMessage* message); protected: Adafruit_MLX90614* _mlx; int _sensor_type; @@ -1015,27 +905,19 @@ class SensorMLX90614: public Sensor { * SensorBosch */ -#if MODULE_BME280 == 1 || MODULE_BMP085 == 1 || MODULE_BMP280 == 1 +#if defined(MODULE_BME280) || defined(MODULE_BMP085) || defined(MODULE_BMP280) class SensorBosch: public Sensor { public: - SensorBosch(NodeManager* node_manager, int child_id, int sensor_type); + SensorBosch(NodeManager& node_manager); // [101] define how many pressure samples to keep track of for calculating the forecast (default: 5) void setForecastSamplesCount(int value); // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); - // constants - const static int TEMPERATURE = 0; - const static int HUMIDITY = 1; - const static int PRESSURE = 2; - const static int FORECAST = 3; + void onLoop(Child* child); + void onReceive(MyMessage* message); static uint8_t GetI2CAddress(uint8_t chip_id); protected: - int _sensor_type; char* _weather[6] = { "stable", "sunny", "cloudy", "unstable", "thunderstorm", "unknown" }; int _forecast_samples_count = 5; float* _forecast_samples; @@ -1045,84 +927,65 @@ class SensorBosch: public Sensor { float _dP_dt; bool _first_round = true; float _getLastPressureSamplesAverage(); - void _forecast(float pressure); + char* _forecast(float pressure); }; #endif /* SensorBME280 */ -#if MODULE_BME280 == 1 +#ifdef MODULE_BME280 class SensorBME280: public SensorBosch { public: - SensorBME280(NodeManager* node_manager, int child_id, Adafruit_BME280* bme, int sensor_type); - void onLoop(); + SensorBME280(NodeManager& node_manager); + // define what to do at each stage of the sketch + void onBefore(); + void onSetup(); + void onLoop(Child* child); protected: - Adafruit_BME280* _bme; + Adafruit_BME280* _bm; }; #endif /* SensorBMP085 */ -#if MODULE_BMP085 == 1 +#ifdef MODULE_BMP085 class SensorBMP085: public SensorBosch { public: - SensorBMP085(NodeManager* node_manager, int child_id, Adafruit_BMP085* bmp, int sensor_type); - void onLoop(); + SensorBMP085(NodeManager& node_manager); + // define what to do at each stage of the sketch + void onBefore(); + void onSetup(); + void onLoop(Child* child); protected: - Adafruit_BMP085* _bmp; + Adafruit_BMP085* _bm; }; #endif /* SensorBMP280 */ -#if MODULE_BMP280 == 1 +#ifdef MODULE_BMP280 class SensorBMP280: public SensorBosch { public: - SensorBMP280(NodeManager* node_manager, int child_id, Adafruit_BMP280* bmp, int sensor_type); - void onLoop(); - protected: - Adafruit_BMP280* _bmp; -}; -#endif - -/* - SensorHCSR04 -*/ -#if MODULE_HCSR04 == 1 -class SensorHCSR04: public Sensor { - public: - SensorHCSR04(NodeManager* node_manager, int child_id, int pin); - // [101] Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) - void setTriggerPin(int value); - // [102] Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor) - void setEchoPin(int value); - // [103] Maximum distance we want to ping for (in centimeters) (default: 300) - void setMaxDistance(int value); + SensorBMP280(NodeManager& node_manager); // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); + void onLoop(Child* child); protected: - int _trigger_pin; - int _echo_pin; - int _max_distance = 300; - NewPing* _sonar; + Adafruit_BMP280* _bm; }; #endif /* SensorSonoff */ -#if MODULE_SONOFF == 1 +#ifdef MODULE_SONOFF class SensorSonoff: public Sensor { public: - SensorSonoff(NodeManager* node_manager, int child_id); + SensorSonoff(NodeManager& node_manager); // [101] set the button's pin (default: 0) void setButtonPin(int value); // [102] set the relay's pin (default: 12) @@ -1132,10 +995,8 @@ class SensorSonoff: public Sensor { // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); + void onLoop(Child* child); + void onReceive(MyMessage* message); protected: Bounce _debouncer = Bounce(); int _button_pin = 0; @@ -1148,24 +1009,49 @@ class SensorSonoff: public Sensor { int _led_on = 0; int _led_off = 1; void _blink(); - void _toggle(); + void _toggle(Child* child); +}; +#endif + +/* + SensorHCSR04 +*/ +#ifdef MODULE_HCSR04 +class SensorHCSR04: public Sensor { + public: + SensorHCSR04(NodeManager& node_manager, int pin); + // [101] Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) + void setTriggerPin(int value); + // [102] Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor) + void setEchoPin(int value); + // [103] Maximum distance we want to ping for (in centimeters) (default: 300) + void setMaxDistance(int value); + // define what to do at each stage of the sketch + void onBefore(); + void onSetup(); + void onLoop(Child* child); + void onReceive(MyMessage* message); + void onProcess(Request & request); + protected: + int _trigger_pin; + int _echo_pin; + int _max_distance = 300; + NewPing* _sonar; }; #endif /* SensorMCP9808 */ -#if MODULE_MCP9808 == 1 +#ifdef MODULE_MCP9808 class SensorMCP9808: public Sensor { public: - SensorMCP9808(NodeManager* node_manager, int child_id, Adafruit_MCP9808* mcp); + SensorMCP9808(NodeManager& node_manager); // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); + void onLoop(Child* child); + void onReceive(MyMessage* message); protected: Adafruit_MCP9808* _mcp; }; @@ -1174,10 +1060,10 @@ class SensorMCP9808: public Sensor { /* SensorMQ */ - #if MODULE_MQ == 1 + #ifdef MODULE_MQ class SensorMQ: public Sensor { public: - SensorMQ(NodeManager* node_manager, int child_id, int pin); + SensorMQ(NodeManager& node_manager, int pin); // [101] define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); void setTargetGas(int value); // [102] define the load resistance on the board, in kilo ohms (default: 1); @@ -1203,10 +1089,8 @@ class SensorMQ: public Sensor { // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); + void onLoop(Child* child); + void onReceive(MyMessage* message); protected: float _rl_value = 1.0; float _ro_clean_air_factor = 9.83; @@ -1236,21 +1120,17 @@ class SensorMQ: public Sensor { /* SensorMHZ19 */ -#if MODULE_MHZ19 == 1 +#ifdef MODULE_MHZ19 class SensorMHZ19: public Sensor { public: - SensorMHZ19(NodeManager* node_manager, int child_id, int pin); - // set the pins for RX and TX of the SoftwareSerial (default: Rx=6, Tx=7) - void setRxTx(int rxpin, int txpin); - int readCO2(); + SensorMHZ19(NodeManager& node_manager, int rxpin, int txpin); // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); + void onLoop(Child* child); + void onReceive(MyMessage* message); protected: + int _readCO2(); SoftwareSerial* _ser; int _tx_pin = 6; int _rx_pin = 7; @@ -1260,33 +1140,27 @@ class SensorMHZ19: public Sensor { /* SensorAM2320 */ -#if MODULE_AM2320 == 1 +#ifdef MODULE_AM2320 class SensorAM2320: public Sensor { public: - SensorAM2320(NodeManager* node_manager, int child_id, AM2320* th, int sensor_type); + SensorAM2320(NodeManager& node_manager); // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); - // constants - const static int TEMPERATURE = 0; - const static int HUMIDITY = 1; + void onLoop(Child* child); + void onReceive(MyMessage* message); protected: AM2320* _th; - int _sensor_type = 0; }; #endif /* SensorTSL2561 */ -#if MODULE_TSL2561 == 1 +#ifdef MODULE_TSL2561 class SensorTSL2561: public Sensor { public: - SensorTSL2561(NodeManager* node_manager, int child_id); + SensorTSL2561(NodeManager& node_manager); // [101] set the gain, possible values are SensorTSL2561::GAIN_0X (0), SensorTSL2561::GAIN_16X (1) (default 16x) void setGain(int value); // [102] set the timing, possible values are SensorTSL2561::INTEGRATIONTIME_13MS (0), SensorTSL2561::INTEGRATIONTIME_101MS (1), SensorTSL2561::INTEGRATIONTIME_402MS (2) (default: 13ms) @@ -1298,10 +1172,9 @@ class SensorTSL2561: public Sensor { // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); + void onLoop(Child* child); + void onReceive(MyMessage* message); void onProcess(Request & request); - void onInterrupt(); // constants const static int ADDR_FLOAT = 0; const static int ADDR_LOW = 1; @@ -1327,19 +1200,17 @@ class SensorTSL2561: public Sensor { /* SensorPT100 */ -#if MODULE_PT100 == 1 +#ifdef MODULE_PT100 class SensorPT100: public Sensor { public: - SensorPT100(NodeManager* node_manager, int child_id, int pin); + SensorPT100(NodeManager& node_manager, int pin); // [101] set the voltageRef used to compare with analog measures void setVoltageRef(float value); // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); + void onLoop(Child* child); + void onReceive(MyMessage* message); protected: DFRobotHighTemperature* _PT100; float _voltageRef = 3.3; @@ -1349,32 +1220,30 @@ class SensorPT100: public Sensor { /* SensorPT100 */ -#if MODULE_DIMMER == 1 +#ifdef MODULE_DIMMER class SensorDimmer: public Sensor { public: - SensorDimmer(NodeManager* node_manager, int child_id, int pin); + SensorDimmer(NodeManager& node_manager, int pin); // [101] set the effect to use for a smooth transition, can be one of SensorDimmer::EASE_LINEAR, SensorDimmer::EASE_INSINE, SensorDimmer::EASE_OUTSINE, SensorDimmer::EASE_INOUTSINE (default: EASE_LINEAR) void setEasing(int value); // [102] the duration of entire the transition in seconds (default: 1) void setDuration(int value); // [103] the duration of a single step of the transition in milliseconds (default: 100) void setStepDuration(int value); + // define what to do at each stage of the sketch + void onBefore(); + void onSetup(); + void onLoop(Child* child); + void onReceive(MyMessage* message); + protected: // fade the output from the current value to the target provided in the range 0-100 - void fadeTo(int value); - enum easing { + void _fadeTo(Child* child, int value); + enum _easing_list { EASE_LINEAR, EASE_INSINE, EASE_OUTSINE, EASE_INOUTSINE, }; - // define what to do at each stage of the sketch - void onBefore(); - void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - void onProcess(Request & request); - void onInterrupt(); - protected: int _percentage = 0; int _easing = EASE_LINEAR; int _duration = 1000; @@ -1386,10 +1255,10 @@ class SensorDimmer: public Sensor { /* SensorPulseMeter */ -#if MODULE_PULSE_METER == 1 +#ifdef MODULE_PULSE_METER class SensorPulseMeter: public Sensor { public: - SensorPulseMeter(NodeManager* node_manager, int child_id, int pin); + SensorPulseMeter(NodeManager& node_manager, int pin); // [102] set how many pulses for each unit (e.g. 1000 pulses for 1 kwh of power, 9 pulses for 1 mm of rain, etc.) void setPulseFactor(float value); // set initial value - internal pull up (default: HIGH) @@ -1399,8 +1268,8 @@ class SensorPulseMeter: public Sensor { // define what to do at each stage of the sketch void onBefore(); void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); + void onLoop(Child* child); + void onReceive(MyMessage* message); void onProcess(Request & request); void onInterrupt(); protected: @@ -1408,7 +1277,7 @@ class SensorPulseMeter: public Sensor { float _pulse_factor; int _initial_value = HIGH; int _interrupt_mode = FALLING; - void _reportTotal(); + virtual void _reportTotal(Child* child); }; /* @@ -1416,7 +1285,9 @@ class SensorPulseMeter: public Sensor { */ class SensorRainGauge: public SensorPulseMeter { public: - SensorRainGauge(NodeManager* node_manager, int child_id, int pin); + SensorRainGauge(NodeManager& node_manager, int pin); + // define what to do at each stage of the sketch + void onBefore(); }; /* @@ -1424,7 +1295,11 @@ class SensorRainGauge: public SensorPulseMeter { */ class SensorPowerMeter: public SensorPulseMeter { public: - SensorPowerMeter(NodeManager* node_manager, int child_id, int pin); + SensorPowerMeter(NodeManager& node_manager, int pin); + // define what to do at each stage of the sketch + void onBefore(); + protected: + void _reportTotal(Child* child); }; /* @@ -1432,7 +1307,36 @@ class SensorPowerMeter: public SensorPulseMeter { */ class SensorWaterMeter: public SensorPulseMeter { public: - SensorWaterMeter(NodeManager* node_manager, int child_id, int pin); + SensorWaterMeter(NodeManager& node_manager, int pin); + // define what to do at each stage of the sketch + void onBefore(); + protected: + void _reportTotal(Child* child); +}; +#endif + +/* + SensorPlantowerPMS +*/ +#ifdef MODULE_PMS +class SensorPlantowerPMS: public Sensor { + public: + SensorPlantowerPMS(NodeManager& node_manager, int rxpin, int txpin); + // define what to do at each stage of the sketch + void onBefore(); + void onSetup(); + void loop(MyMessage* message); + void onLoop(Child* child); + void onReceive(MyMessage* message); + protected: + int _readSensorValues(); + SoftwareSerial* _ser; + int _tx_pin = 4; + int _rx_pin = 3; + PMS *_pms; + PMS::DATA _data; + bool _valuesRead = false; + bool _valuesReadError = false; }; #endif @@ -1442,33 +1346,9 @@ class SensorWaterMeter: public SensorPulseMeter { class NodeManager { public: NodeManager(); - // [10] send the same service message multiple times (default: 1) + // [10] send the same message multiple times (default: 1) void setRetries(int value); int getRetries(); - #if BATTERY_MANAGER == 1 - // [11] the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) - void setBatteryMin(float value); - // [12] the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) - void setBatteryMax(float value); - // [14] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60 minutes) - void setBatteryReportMinutes(int value); - // [40] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60 minutes) - void setBatteryReportSeconds(int value); - // [41] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60 minutes) - void setBatteryReportHours(int value); - // [42] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60 minutes) - void setBatteryReportDays(int value); - // [15] if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) - void setBatteryInternalVcc(bool value); - // [16] if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) - void setBatteryPin(int value); - // [17] if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075) - void setBatteryVoltsPerBit(float value); - // [18] If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: true) - void setBatteryReportWithInterrupt(bool value); - // [2] Send a battery level report to the controller - void batteryReport(); - #endif // [3] set the duration (in seconds) of a sleep cycle void setSleepSeconds(int value); long getSleepSeconds(); @@ -1487,27 +1367,14 @@ class NodeManager { // [20] optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) void setSleepBetweenSend(int value); int getSleepBetweenSend(); - // register a built-in sensor - int registerSensor(int sensor_type, int pin = -1, int child_id = -1); - // register a custom sensor - int registerSensor(Sensor* sensor); - // [26] un-register a sensor - void unRegisterSensor(int sensor_index); - // return a sensor by its index - Sensor* get(int sensor_index); - Sensor* getSensor(int sensor_index); - // assign a different child id to a sensor - bool renameSensor(int old_child_id, int new_child_id); - #if POWER_MANAGER == 1 - // to save battery the sensor can be optionally connected to two pins which will act as vcc and ground and activated on demand - void setPowerPins(int ground_pin, int vcc_pin, int wait_time = 50); - // [23] if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) - void setAutoPowerPins(bool value); - // [24] manually turn the power on - void powerOn(); - // [25] manually turn the power off - void powerOff(); - #endif + // register a sensor + void registerSensor(Sensor* sensor); + // to save battery the sensor can be optionally connected to two pins which will act as vcc and ground and activated on demand + void setPowerPins(int ground_pin, int vcc_pin, int wait_time = 50); + // [24] manually turn the power on + void powerOn(); + // [25] manually turn the power off + void powerOff(); // [21] set this to true if you want destination node to send ack back to this node (default: false) void setAck(bool value); bool getAck(); @@ -1526,14 +1393,10 @@ class NodeManager { void hello(); // [6] reboot the board void reboot(); - // [8] send NodeManager's the version back to the controller - void version(); // [7] clear the EEPROM void clearEeprom(); // [9] wake up the board void wakeup(); - // process a remote request - void process(Request & request); // return the value stored at the requested index from the EEPROM int loadFromMemory(int index); // [27] save the given index of the EEPROM the provided value @@ -1560,55 +1423,32 @@ class NodeManager { void setRebootPin(int value); // [32] turn the ADC off so to save 0.2 mA void setADCOff(); - #if SIGNAL_SENSOR == 1 && defined(MY_SIGNAL_REPORT_ENABLED) - // [33] How frequenly to send a signal report to the controller (default: 60 minutes) - void setSignalReportMinutes(int value); - // [43] How frequenly to send a signal report to the controller (default: 60 minutes) - void setSignalReportSeconds(int value); - // [44] How frequenly to send a signal report to the controller (default: 60 minutes) - void setSignalReportHours(int value); - // [45] How frequenly to send a signal report to the controller (default: 60 minutes) - void setSignalReportDays(int value); - // [34] define which signal report to send. Possible values are SR_UPLINK_QUALITY, SR_TX_POWER_LEVEL, SR_TX_POWER_PERCENT, SR_TX_RSSI, SR_RX_RSSI, SR_TX_SNR, SR_RX_SNR (default: SR_RX_RSSI) - void setSignalCommand(int value); - // [35] report the signal level to the controller - void signalReport(); - #endif + // [30] if set save the sleep settings in memory, also when changed remotely (default: false) + void setSaveSleepSettings(bool value); // hook into the main sketch functions void before(); void presentation(); void setup(); void loop(); - void receive(const MyMessage & msg); + void receive(MyMessage & msg); void receiveTime(unsigned long ts); // handle interrupts static void _onInterrupt_1(); static void _onInterrupt_2(); - MyMessage* getMessage(); - void sendMessage(); + // send a message by providing the source child, type of the message and value + void sendMessage(int child_id, int type, int value); + void sendMessage(int child_id, int type, float value); + void sendMessage(int child_id, int type, double value); + void sendMessage(int child_id, int type, const char* value); + void setPowerManager(PowerManager& powerManager); + int getAvailableChildId(); + List sensors; + Child* getChild(int child_id); + Sensor* getSensorWithChild(int child_id); private: - #if BATTERY_MANAGER == 1 - float _battery_min = 2.6; - float _battery_max = 3.3; - Timer _battery_report_timer = Timer(this); - bool _battery_report_with_interrupt = true; - bool _battery_internal_vcc = true; - int _battery_pin = -1; - float _battery_volts_per_bit = 0.003363075; - #endif - #if POWER_MANAGER == 1 - // to optionally controller power pins - PowerManager _powerManager; - bool _auto_power_pins = true; - #endif - #if SIGNAL_SENSOR == 1 && defined(MY_SIGNAL_REPORT_ENABLED) - Timer _signal_report_timer = Timer(this); - int _signal_command = SR_RX_RSSI; - #endif - MyMessage _msg; - void _sendUsingConfigChild(MyMessage & msg); - void _sendUsingBatteryChild(MyMessage & msg); - void _sendUsingSignalChild(MyMessage & msg); + PowerManager* _powerManager = nullptr; + MyMessage _message; + void _sendMessage(int child_id, int type); int _status = AWAKE; long _sleep_time = 0; int _sleep_interrupt_pin = -1; @@ -1623,19 +1463,17 @@ class NodeManager { static long _last_interrupt_1; static long _last_interrupt_2; long _timestamp = -1; - Sensor* _sensors[MAX_SENSORS+1] = {0}; bool _ack = false; void _sleep(); void _present(int child_id, int type); - int _getAvailableChildId(); - int _getInterruptInitialValue(int mode); bool _get_controller_config = true; int _is_metric = 1; int _report_interval_seconds = 10*60; bool _sleep_or_wait = true; int _reboot_pin = -1; - void _loadConfig(); - void _saveConfig(); + bool _save_sleep_settings = false; + void _loadSleepSettings(); + void _saveSleepSettings(); }; #endif diff --git a/NodeManagerLibrary.ino b/NodeManagerLibrary.ino new file mode 100644 index 00000000..e642b49c --- /dev/null +++ b/NodeManagerLibrary.ino @@ -0,0 +1,3989 @@ +/* + * NodeManager Library + */ + +/*************************************** + PowerManager +*/ + +PowerManager::PowerManager(int ground_pin, int vcc_pin, int wait_time) { + setPowerPins(ground_pin, vcc_pin, wait_time); +} + +// set the vcc and ground pin the sensor is connected to +void PowerManager::setPowerPins(int ground_pin, int vcc_pin, int wait_time) { + _ground_pin = ground_pin; + _vcc_pin = vcc_pin; + #ifdef NODEMANAGER_DEBUG + Serial.print(F("PWR G=")); + Serial.print(_ground_pin); + Serial.print(F(" V=")); + Serial.println(_vcc_pin); + #endif + if (_ground_pin > 0) { + // configure the ground pin as output and initialize to low + pinMode(_ground_pin, OUTPUT); + digitalWrite(_ground_pin, LOW); + } + if (_vcc_pin > 0) { + // configure the vcc pin as output and initialize to high (power on) + pinMode(_vcc_pin, OUTPUT); + digitalWrite(_vcc_pin, HIGH); + } + // save wait time + _wait = wait_time; +} + +// turn on the sensor by activating its power pins +void PowerManager::powerOn() { + if (_vcc_pin == -1) return; + #ifdef NODEMANAGER_DEBUG + 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 (_vcc_pin == -1) return; + #ifdef NODEMANAGER_DEBUG + Serial.print(F("OFF P=")); + Serial.println(_vcc_pin); + #endif + // power off the sensor by turning low the vcc pin + digitalWrite(_vcc_pin, LOW); +} + +/****************************************** + Timer +*/ + +Timer::Timer(NodeManager* node_manager) { + _node = node_manager; +} + +// start the timer +void Timer::start(int target, int unit) { + set(target,unit); + start(); +} +void Timer::start() { + if (_is_configured) _is_running = true; +} + +// stop the timer +void Timer::stop() { + _is_running = false; +} + +// reset the timer +void Timer::reset() { + // reset the timer + _elapsed = 0; + _last_millis = 0; +} + +// restart the timer +void Timer::restart() { + if (! isRunning()) return; + stop(); + reset(); + // if using millis(), keep track of the current timestamp for calculating the difference + if (! _node->isSleepingNode()) _last_millis = millis(); + start(); +} + +// setup the timer +void Timer::set(int target, int unit) { + reset(); + // save the settings + _target = target; + if (unit == MINUTES) _target = _target * 60; + else if (unit == HOURS) _target = _target * 60 *60; + else if (unit == DAYS) _target = _target * 60 * 60 *24; + _is_running = false; + _is_configured = true; +} + +// unset the timer +void Timer::unset() { + stop(); + _is_configured = true; +} + +// update the timer at every cycle +void Timer::update() { + if (! isRunning()) return; + if (_node->isSleepingNode()) { + // millis() is not reliable while sleeping so calculate how long a sleep cycle would last in seconds and update the elapsed time + _elapsed += _node->getSleepSeconds(); + } else { + // use millis() to calculate the elapsed time in seconds + _elapsed = (long)((millis() - _last_millis)/1000); + } + _first_run = false; +} + +// return true if the time is over +bool Timer::isOver() { + if (! isRunning()) return false; + // time has elapsed + if (_elapsed >= _target) return true; + // millis has started over + if (_elapsed < 0 ) return true; + return false; +} + +// return true if the timer is running +bool Timer::isRunning() { + if (! isConfigured()) return false; + return _is_running; +} + +// return true if the time is configured +bool Timer::isConfigured() { + return _is_configured; +} + +// return true if this is the first time the timer runs +bool Timer::isFirstRun() { + return _first_run; +} + +// return elapsed seconds so far +float Timer::getElapsed() { + return _elapsed; +} + + +/****************************************** + Request +*/ + +// contructor, tokenize a request in the format "child_id,function,value" +Request::Request(int recipient_child_id, const char* string) { + _recipient_child_id = recipient_child_id; + char* ptr; + // tokenize the string and get child id + _child_id = atoi(strtok_r(const_cast(string), ",", &ptr)); + // tokenize the string and get function id + _function = atoi(strtok_r(NULL, ",", &ptr)); + // tokenize the string and get the value + _value = atof(strtok_r(NULL, ",", &ptr)); + #ifdef NODEMANAGER_DEBUG + Serial.print(F("REQ C=")); + Serial.print(_child_id); + Serial.print(F(" F=")); + Serial.print(_function); + Serial.print(F(" V=")); + Serial.println(_value); + #endif +} + +// return the child id +int Request::getRecipientChildId() { + return _recipient_child_id; +} + +// return the child id +int Request::getChildId() { + return _child_id; +} + +// return the parsed function +int Request::getFunction() { + return _function; +} + +// return the value as an int +int Request::getValueInt() { + return (int)_value; + +} + +// return the value as a float +float Request::getValueFloat() { + return _value; +} + +/****************************************** + Sensors +*/ + +/* + Child class + */ + +Child::Child() { + +} + +// constructor +Child::Child(Sensor* __sensor, int _child_id, int _presentation, int _type, char* _description = "") { + child_id = _child_id; + presentation = _presentation; + type = _type; + description = _description; + _sensor = __sensor; + _sensor->registerChild(this); + force_update_timer = new Timer(_sensor->_node); +} +// set a value, implemented by the subclasses +void Child::sendValue() { +} + +// check if it is an updated value, implemented by the subclasses +bool Child::isNewValue() { +} + +/* + ChildInt class +*/ + +// ChildInt class +ChildInt::ChildInt(Sensor* sensor, int child_id, int presentation, int type, char* description = ""): Child(sensor, child_id, presentation, type, description) { +} + +// store a new value and update the total +void ChildInt::setValueInt(int value) { + _total = _total + value; + _samples++; + _value = (int) (_total / _samples); +} + +// return the value +int ChildInt::getValueInt() { + return _value; +} + +// send the value back to the controller +void ChildInt::sendValue() { + if (_samples == 0) return; + _sensor->_node->sendMessage(child_id,type,_value); + _last_value = _value; + _total = 0; + _samples = 0; +} + +// check if it is an updated value +bool ChildInt::isNewValue() { + return _last_value != _value; +} + +/* + ChildFloat class +*/ + +// ChildFloat class +ChildFloat::ChildFloat(Sensor* sensor, int child_id, int presentation, int type, char* description = ""): Child(sensor, child_id, presentation, type, description) { +} + +// store a new value and update the total +void ChildFloat::setValueFloat(float value) { + _total = _total + value; + _samples++; + _value = _total / _samples; +} + +// return the value +float ChildFloat::getValueFloat() { + return _value; +} + +// send the value back to the controller +void ChildFloat::sendValue() { + if (_samples == 0) return; + _sensor->_node->sendMessage(child_id,type,_value); + _last_value = _value; + _total = 0; + _samples = 0; +} + +// check if it is an updated value +bool ChildFloat::isNewValue() { + return _last_value != _value; +} + +/* + ChildDouble class +*/ + +// ChildDouble class +ChildDouble::ChildDouble(Sensor* sensor, int child_id, int presentation, int type, char* description = ""): Child(sensor, child_id, presentation, type, description) { +} + +// store a new value and update the total +void ChildDouble::setValueDouble(double value) { + _total = _total + value; + _samples++; + _value = _total / _samples; +} + +// return the value +double ChildDouble::getValueDouble() { + return _value; +} + +// send the value back to the controller +void ChildDouble::sendValue() { + if (_samples == 0) return; + _sensor->_node->sendMessage(child_id,type,_value); + _last_value = _value; + _total = 0; + _samples = 0; +} + +// check if it is an updated value +bool ChildDouble::isNewValue() { + return _last_value != _value; +} + +/* + ChildString class +*/ + +// ChildString class +ChildString::ChildString(Sensor* sensor, int child_id, int presentation, int type, char* description = ""): Child(sensor, child_id, presentation, type, description) { +} + +// store a new value and update the total +void ChildString::setValueString(char* value) { + _value = value; +} + +// return the value +char* ChildString::getValueString() { + return _value; +} + +// send the value back to the controller +void ChildString::sendValue() { + _sensor->_node->sendMessage(child_id,type,_value); + _last_value = _value; + _value = ""; +} + +// check if it is an updated value +bool ChildString::isNewValue() { + return strcmp(_value, _last_value) != 0; +} + +/* + Sensor class +*/ +// constructor +Sensor::Sensor() { +} +Sensor::Sensor(NodeManager& node_manager, int pin = -1) { + _node = &node_manager; + _pin = pin; + _report_timer = new Timer(_node); + _node->registerSensor(this); +} + +// return the name of the sensor +char* Sensor::getName() { + return _name; +} + +// setter/getter +void Sensor::setPin(int value) { + _pin = value; +} +int Sensor::getPin() { + return _pin; +} +void Sensor::setSamples(int value) { + _samples = value; +} +void Sensor::setSamplesInterval(int value) { + _samples_interval = value; +} +void Sensor::setTrackLastValue(bool value) { + _track_last_value = value; +} +void Sensor::setForceUpdateMinutes(int value) { + for (List::iterator itr = children.begin(); itr != children.end(); ++itr) { + Child* child = *itr; + child->force_update_timer->start(value,MINUTES); + } +} +void Sensor::setPowerPins(int ground_pin, int vcc_pin, int wait_time) { + if (_powerManager == nullptr) return; + _powerManager->setPowerPins(ground_pin, vcc_pin, wait_time); +} +void Sensor::powerOn() { + if (_powerManager == nullptr) return; + _powerManager->powerOn(); +} +void Sensor::powerOff() { + if (_powerManager == nullptr) return; + _powerManager->powerOff(); +} +int Sensor::getInterruptPin() { + return _interrupt_pin; +} + +// After how many seconds the sensor will report back its measure +void Sensor::setReportIntervalSeconds(int value) { + _report_timer->start(value,SECONDS); +} + +// After how many minutes the sensor will report back its measure +void Sensor::setReportIntervalMinutes(int value) { + _report_timer->start(value,MINUTES); +} + +// After how many minutes the sensor will report back its measure +void Sensor::setReportIntervalHours(int value) { + _report_timer->start(value,HOURS); +} + +// After how many minutes the sensor will report back its measure +void Sensor::setReportIntervalDays(int value) { + _report_timer->start(value,DAYS); +} + + +// return true if the report interval has been already configured +bool Sensor::isReportIntervalConfigured() { + return _report_timer->isConfigured(); +} + +// listen for interrupts on the given pin so interrupt() will be called when occurring +void Sensor::setInterrupt(int pin, int mode, int initial) { + _interrupt_pin = pin; + _node->setInterrupt(pin,mode,initial); +} + +// register a child +void Sensor::registerChild(Child* child) { + children.push(child); +} + +// present the sensor to the gateway and controller +void Sensor::presentation() { + for (List::iterator itr = children.begin(); itr != children.end(); ++itr) { + Child* child = *itr; + #ifdef NODEMANAGER_DEBUG + Serial.print(F("PRES I=")); + Serial.print(child->child_id); + Serial.print(F(" T=")); + Serial.println(child->presentation); + #endif + present(child->child_id, child->presentation,child->description,_node->getAck()); + } + +} + +// call the sensor-specific implementation of before +void Sensor::before() { + onBefore(); + for (List::iterator itr = children.begin(); itr != children.end(); ++itr) { + Child* child = *itr; + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" P=")); + Serial.print(child->presentation); + Serial.print(F(" T=")); + Serial.println(child->type); + #endif + } +} + +// call the sensor-specific implementation of setup +void Sensor::setup() { + onSetup(); +} + +// call the sensor-specific implementation of loop +void Sensor::loop(MyMessage* message) { + // update the timers if within a loop cycle + if (message == nullptr) { + if (_report_timer->isRunning()) { + // keep track if it is the first time + bool first_run = _report_timer->isFirstRun(); + // update the timer + _report_timer->update(); + // if it is not the time yet to report a new measure, just return (unless it is the first time) + if (! _report_timer->isOver() && ! first_run) return; + } + } + // turn the sensor on + powerOn(); + // iterates over all the children + for (List::iterator itr = children.begin(); itr != children.end(); ++itr) { + Child* child = *itr; + // update the force update timer if running + if (child->force_update_timer->isRunning()) child->force_update_timer->update(); + // if a specific child is requested, skip all the others + if (message != nullptr && message->sensor != child->child_id) continue; + // collect multiple samples if needed + for (int i = 0; i < _samples; i++) { + // we've been called from receive(), pass the message along + if (message != nullptr) onReceive(message); + // we'be been called from loop() + else onLoop(child); + // wait between samples + if (_samples_interval > 0) _node->sleepOrWait(_samples_interval); + } + // process the result and send a response back if 1) is not a loop 2) not tracking last value 3) tracking last value and there is a new value 4) tracking last value and timer is over + if ( + message != nullptr || + ! _track_last_value || + _track_last_value && child->isNewValue() || + _track_last_value && child->force_update_timer->isRunning() && child->force_update_timer->isOver()) + child->sendValue(); + } + // turn the sensor off + powerOff(); + // if called from loop(), restart the report timer if over + if (message == nullptr && _report_timer->isRunning() && _report_timer->isOver()) _report_timer->restart(); +} + +// receive and handle an interrupt +void Sensor::interrupt() { + // call the implementation of onInterrupt() + onInterrupt(); +} + +// receive a message from the radio network +void Sensor::receive(MyMessage &message) { + // a request would make the sensor executing its main task passing along the message + loop(&message); +} + +// return the requested child +Child* Sensor::getChild(int child_id) { + for (List::iterator itr = children.begin(); itr != children.end(); ++itr) { + Child* child = *itr; + if (child->child_id == child_id) return child; + } + return nullptr; +} + +void Sensor::setPowerManager(PowerManager& powerManager) { + _powerManager = &powerManager; +} + + +// virtual functions +void Sensor::onBefore() {} +void Sensor::onSetup(){} +void Sensor::onLoop(Child* child){} +void Sensor::onReceive(MyMessage* message){} +void Sensor::onInterrupt(){} + +/* + SensorBattery +*/ +#ifndef MY_GATEWAY_ESP8266 +// contructor +SensorBattery::SensorBattery(NodeManager& node_manager): Sensor(node_manager) { + _name = "BATTERY"; + // report battery level every 60 minutes by default + setReportIntervalMinutes(60); +} +void SensorBattery::setMinVoltage(float value) { + _battery_min = value; +} +void SensorBattery::setMaxVoltage(float value) { + _battery_max = value; +} +void SensorBattery::setBatteryInternalVcc(bool value) { + _battery_internal_vcc = value; +} +void SensorBattery::setBatteryPin(int value) { + _battery_pin = value; +} +void SensorBattery::setBatteryVoltsPerBit(float value) { + _battery_volts_per_bit = value; +} + +// what to do during before +void SensorBattery::onBefore() { + new ChildFloat(this,BATTERY_CHILD_ID,S_MULTIMETER,V_VOLTAGE); +} + +// what to do during setup +void SensorBattery::onSetup() { + // when measuring the battery from a pin, analog reference must be internal + if (! _battery_internal_vcc && _battery_pin > -1) + #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + analogReference(INTERNAL1V1); + #else + analogReference(INTERNAL); + #endif +} + +// what to do during loop +void SensorBattery::onLoop(Child* child) { + // measure the board vcc + float volt = 0; + if (_battery_internal_vcc || _battery_pin == -1) volt = _node->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; + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" V=")); + Serial.print(volt); + Serial.print(F(" %=")); + Serial.println(percentage); + #endif + ((ChildFloat*)child)->setValueFloat(volt); + // report battery level percentage + sendBatteryLevel(percentage); +} + +// what to do as the main task when receiving a message +void SensorBattery::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} +#endif + +/* + SensorSignal +*/ +#ifndef MY_GATEWAY_ESP8266 +// contructor +SensorSignal::SensorSignal(NodeManager& node_manager): Sensor(node_manager) { + _name = "SIGNAL"; + // report signal level every 60 minutes by default + setReportIntervalMinutes(60); +} +// setter/getter +void SensorSignal::setSignalCommand(int value) { + _signal_command = value; +} + +// what to do during before +void SensorSignal::onBefore() { + new ChildInt(this,SIGNAL_CHILD_ID,S_SOUND,V_LEVEL); +} + +// what to do during setup +void SensorSignal::onSetup() { + +} + +// what to do during loop +void SensorSignal::onLoop(Child* child) { + int16_t value = transportGetSignalReport((signalReport_t)_signal_command); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" V=")); + Serial.println(value); + #endif + ((ChildInt*)child)->setValueInt(value); +} + +// what to do as the main task when receiving a message +void SensorSignal::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} +#endif + +#ifdef MODULE_ANALOG_INPUT +/* + SensorAnalogInput +*/ + +// contructor +SensorAnalogInput::SensorAnalogInput(NodeManager& node_manager, int pin): Sensor(node_manager, pin) { + _name = "ANALOG_I"; +} + +// 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() { + new ChildInt(this,_node->getAvailableChildId(),S_CUSTOM,V_CUSTOM); +} + +// what to do during setup +void SensorAnalogInput::onSetup() { + // prepare the pin for input + pinMode(_pin, INPUT); +} + +// what to do during loop +void SensorAnalogInput::onLoop(Child* child) { + // read the input + int adc = _getAnalogRead(); + // calculate the percentage + int percentage = 0; + if (_output_percentage) percentage = _getPercentage(adc); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" V=")); + Serial.print(adc); + Serial.print(F(" %=")); + Serial.println(percentage); + #endif + // store the result + ((ChildInt*)child)->setValueInt(_output_percentage ? percentage : adc); +} + +// what to do during loop +void SensorAnalogInput::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} + +// 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 = _range_max - 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(NodeManager& node_manager, int pin): SensorAnalogInput(node_manager, pin) { + _name = "LDR"; +} + +// what to do during before +void SensorLDR::onBefore() { + new ChildInt(this,_node->getAvailableChildId(),S_LIGHT_LEVEL,V_LIGHT_LEVEL); +} + +// what to do during setup +void SensorLDR::onSetup() { + setReverse(true); +} + +/* + SensorRain +*/ + +// contructor +SensorRain::SensorRain(NodeManager& node_manager, int pin): SensorAnalogInput(node_manager, pin) { + _name = "RAIN"; +} + +// what to do during before +void SensorRain::onBefore() { + new ChildInt(this,_node->getAvailableChildId(),S_RAIN,V_RAINRATE); +} + +// what to do during setup +void SensorRain::onSetup() { + setReference(DEFAULT); + setOutputPercentage(true); + setReverse(true); + setRangeMin(100); +} + +/* + SensorSoilMoisture +*/ + +// contructor +SensorSoilMoisture::SensorSoilMoisture(NodeManager& node_manager, int pin): SensorAnalogInput(node_manager, pin) { + _name = "SOIL"; +} + +// what to do during before +void SensorSoilMoisture::onBefore() { + new ChildInt(this,_node->getAvailableChildId(),S_MOISTURE,V_LEVEL); +} + +// what to do during setup +void SensorSoilMoisture::onSetup() { + setReverse(true); + setReference(DEFAULT); + setOutputPercentage(true); + setReverse(true); + setRangeMin(100); +} +#endif + +#ifdef MODULE_THERMISTOR +/* + SensorThermistor +*/ + +// contructor +SensorThermistor::SensorThermistor(NodeManager& node_manager, int pin): Sensor(node_manager, pin) { + _name = "THERMISTOR"; +} + +// 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() { + new ChildFloat(this,_node->getAvailableChildId(),S_TEMP,V_TEMP); +} + +// what to do during setup +void SensorThermistor::onSetup() { + // set the pin as input + pinMode(_pin, INPUT); +} + +// what to do during loop +void SensorThermistor::onLoop(Child* child) { + // 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 + temperature = _node->celsiusToFahrenheit(temperature); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" V=")); + Serial.print(adc); + Serial.print(F(" T=")); + Serial.println(temperature); + #endif + // store the value + if (! isnan(temperature)) ((ChildFloat*)child)->setValueFloat(temperature); +} + +// what to do as the main task when receiving a message +void SensorThermistor::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} +#endif + +#ifdef MODULE_ML8511 +/* + SensorML8511 +*/ + +// contructor +SensorML8511::SensorML8511(NodeManager& node_manager, int pin): Sensor(node_manager, pin) { + _name = "ML8511"; +} + +// what to do during before +void SensorML8511::onBefore() { + new ChildFloat(this,_node->getAvailableChildId(),S_UV,V_UV); +} + +// what to do during setup +void SensorML8511::onSetup() { + // set the pin as input + pinMode(_pin, INPUT); +} + +// what to do during loop +void SensorML8511::onLoop(Child* child) { + // read the voltage + int uvLevel = analogRead(_pin); + int refLevel = _node->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); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" V=")); + Serial.print(outputVoltage); + Serial.print(F(" I=")); + Serial.println(uvIntensity); + #endif + // store the value + ((ChildFloat*)child)->setValueFloat(uvIntensity); +} + +// what to do as the main task when receiving a message +void SensorML8511::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} + +// 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; +} +#endif + +#ifdef MODULE_ACS712 +/* + SensorACS712 +*/ + +// contructor +SensorACS712::SensorACS712(NodeManager& node_manager, int pin): Sensor(node_manager, pin) { + _name = "ACS712"; +} + +// 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() { + new ChildFloat(this,_node->getAvailableChildId(),S_MULTIMETER,V_CURRENT); +} + +// what to do during setup +void SensorACS712::onSetup() { + // set the pin as input + pinMode(_pin, INPUT); +} + +// what to do during loop +void SensorACS712::onLoop(Child* child) { + int value = analogRead(_pin); + // convert the analog read in mV + double voltage = (value / 1024.0) * 5000; + // convert voltage in amps + float value_float = ((voltage - _ACS_offset) / _mv_per_amp); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" A=")); + Serial.println(value_float); + #endif + ((ChildFloat*)child)->setValueFloat(value_float); +} + +// what to do as the main task when receiving a message +void SensorACS712::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} + +#endif + +#ifdef MODULE_DIGITAL_INPUT +/* + SensorDigitalInput +*/ + +// contructor +SensorDigitalInput::SensorDigitalInput(NodeManager& node_manager, int pin): Sensor(node_manager, pin) { + _name = "DIGITAL_I"; +} + +// what to do during before +void SensorDigitalInput::onBefore() { + // register the child + new ChildInt(this,_node->getAvailableChildId(),S_CUSTOM,V_CUSTOM); +} + +// what to do during setup +void SensorDigitalInput::onSetup() { + // set the pin for input + pinMode(_pin, INPUT); +} + +// what to do during loop +void SensorDigitalInput::onLoop(Child* child) { + // read the value + int value = digitalRead(_pin); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" P=")); + Serial.print(_pin); + Serial.print(F(" V=")); + Serial.println(value); + #endif + // store the value + ((ChildInt*)child)->setValueInt(value); +} + +// what to do as the main task when receiving a message +void SensorDigitalInput::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} +#endif + + +#ifdef MODULE_DIGITAL_OUTPUT +/* + SensorDigitalOutput +*/ + +SensorDigitalOutput::SensorDigitalOutput(NodeManager& node_manager, int pin): Sensor(node_manager, pin) { + _name = "DIGITAL_O"; +} + +// what to do during before +void SensorDigitalOutput::onBefore() { + new ChildInt(this,_node->getAvailableChildId(),S_CUSTOM,V_CUSTOM); +} + +// what to do during setup +void SensorDigitalOutput::onSetup() { + _setupPin(children.get(1), _pin); + _safeguard_timer = new Timer(_node); +} + +// setter/getter +void SensorDigitalOutput::setOnValue(int value) { + _on_value = value; +} +void SensorDigitalOutput::setLegacyMode(bool value) { + _legacy_mode = value; +} +void SensorDigitalOutput::setSafeguard(int value) { + _safeguard_timer->set(value,MINUTES); +} +int SensorDigitalOutput::getStatus() { + return _status; +} +void SensorDigitalOutput::setInputIsElapsed(bool value) { + _input_is_elapsed = value; +} +void SensorDigitalOutput::setWaitAfterSet(int value) { + _wait_after_set = value; +} + +// main task +void SensorDigitalOutput::onLoop(Child* child) { + // if a safeguard is set, check if it is time for it + if (_safeguard_timer->isRunning()) { + // update the timer + _safeguard_timer->update(); + // if the time is over, turn the output off + if (_safeguard_timer->isOver()) setStatus(child,OFF); + } +} + +// what to do as the main task when receiving a message +void SensorDigitalOutput::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + // 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)) { + // switch the output + setStatus(child, message->getInt()); + } + if (message->getCommand() == C_REQ && ! _legacy_mode) { + // just return the current status + ((ChildInt*)child)->setValueInt(_status); + } +} + +// write the value to the output +void SensorDigitalOutput::setStatus(Child* child, int value) { + // pre-process the input value + if (_input_is_elapsed) { + // the input provided is an elapsed time + if (value == OFF) { + // turning it off, no need for a safeguard anymore, stop the timer + _safeguard_timer->stop(); + } + else if (value == ON) { + // configure and start the timer + _safeguard_timer->start(value,MINUTES); + // if the input is an elapsed time, unless the value is OFF, the output will be always ON + value = ON; + } + } else { + // if turning the output on and a safeguard timer is configured, start it + if (value == ON && _safeguard_timer->isConfigured() && ! _safeguard_timer->isRunning()) _safeguard_timer->start(); + } + _setStatus(child, value); + // wait if needed for relay drawing a lot of current + if (_wait_after_set > 0) _node->sleepOrWait(_wait_after_set); + // store the new status so it will be sent to the controller + _status = value; + ((ChildInt*)child)->setValueInt(value); +} + +// setup the provided pin for output +void SensorDigitalOutput::_setupPin(Child* child, int pin) { + // set the pin as output and initialize it accordingly + pinMode(pin, OUTPUT); + // setup the pin in a off status + _status = ! _on_value; + digitalWrite(pin, _status); + // the initial value is now the current value + ((ChildInt*)child)->setValueInt(_status); +} + +// switch to the requested status +void SensorDigitalOutput::_setStatus(Child* child, int value) { + int value_to_write = _getValueToWrite(value); + // set the value to the pin + digitalWrite(_pin, value_to_write); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" P=")); + Serial.print(_pin); + Serial.print(F(" V=")); + Serial.println(value_to_write); + #endif +} + +// reverse the value if needed based on the _on_value +int SensorDigitalOutput::_getValueToWrite(int value) { + int value_to_write = value; + if (_on_value == LOW) { + // if the "on" value is LOW, reverse the value + if (value == ON) value_to_write = LOW; + if (value == OFF) value_to_write = HIGH; + } + return value_to_write; +} + +/* + SensorRelay +*/ + +// contructor +SensorRelay::SensorRelay(NodeManager& node_manager, int pin): SensorDigitalOutput(node_manager, pin) { + _name = "RELAY"; +} + +// what to do during before +void SensorRelay::onBefore() { + new ChildInt(this,_node->getAvailableChildId(),S_BINARY,V_STATUS); +} + +/* + SensorLatchingRelay +*/ + +// contructor +SensorLatchingRelay::SensorLatchingRelay(NodeManager& node_manager, int pin): SensorRelay(node_manager, pin) { + _name = "LATCHING"; + // set the "off" pin to the provided pin and the "on" pin to the provided pin + 1 + _pin_on = pin; + _pin_off = pin + 1; +} + +// setter/getter +void SensorLatchingRelay::setPulseWidth(int value) { + _pulse_width = value; +} +void SensorLatchingRelay::setPinOn(int value) { + _pin_on = value; +} +void SensorLatchingRelay::setPinOff(int value) { + _pin_off = value; +} + +// what to do during before +void SensorLatchingRelay::onBefore() { + new ChildInt(this,_node->getAvailableChildId(),S_BINARY,V_STATUS); +} + +// what to do during setup +void SensorLatchingRelay::onSetup() { + _setupPin(children.get(1),_pin_on); + _setupPin(children.get(1),_pin_off); +} + +// switch to the requested status +void SensorLatchingRelay::_setStatus(Child* child, int value) { + // select the right pin to send the pulse to + int pin = value == OFF ? _pin_off : _pin_on; + // set the value + digitalWrite(pin, _on_value); + // wait for the given time before restoring the value to the original value after the pulse + _node->sleepOrWait(_pulse_width); + digitalWrite(pin, ! _on_value); + #ifdef NODEMANAGER_DEBUG + Serial.print(F("LAT I=")); + Serial.print(child->child_id); + Serial.print(F(" P=")); + Serial.print(pin); + Serial.print(F(" S=")); + Serial.print(value); + Serial.print(F(" V=")); + Serial.print(_on_value); + Serial.print(F(" P=")); + Serial.println(_pulse_width); + #endif +} + +#endif + +#ifdef MODULE_DHT +/* + SensorDHT +*/ + +// contructor +SensorDHT::SensorDHT(NodeManager& node_manager, int pin): Sensor(node_manager, pin) { + _name = "DHT"; + _dht_type = DHT::DHT11; +} + +// what to do during before +void SensorDHT::onBefore() { + // register the child + new ChildFloat(this,_node->getAvailableChildId(),S_TEMP,V_TEMP); + new ChildFloat(this,_node->getAvailableChildId(),S_HUM,V_HUM); +} + +// what to do during setup +void SensorDHT::onSetup() { + // store the dht object + _dht = new DHT(); + // initialize the dht library + _dht->setup(_pin,(DHT::DHT_MODEL_t)_dht_type); +} + +// what to do during loop +void SensorDHT::onLoop(Child* child) { + _node->sleepOrWait(_dht->getMinimumSamplingPeriod()); + _dht->readSensor(true); + // temperature sensor + if (child->type == V_TEMP) { + // read the temperature + float temperature = _dht->getTemperature(); + if (! _node->getIsMetric()) temperature = _dht->toFahrenheit(temperature); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" T=")); + Serial.println(temperature); + #endif + // store the value + if (! isnan(temperature)) ((ChildFloat*)child)->setValueFloat(temperature); + } + // humidity sensor + else if (child->type == V_HUM) { + // read humidity + float humidity = _dht->getHumidity(); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" H=")); + Serial.println(humidity); + #endif + // store the value + if (! isnan(humidity)) ((ChildFloat*)child)->setValueFloat(humidity); + } +} + +// what to do as the main task when receiving a message +void SensorDHT::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} + +/* + SensorDHT11 +*/ + +// contructor +SensorDHT11::SensorDHT11(NodeManager& node_manager, int pin): SensorDHT(node_manager, pin) { + _name = "DHT11"; + _dht_type = DHT::DHT11; +} + +/* + SensorDHT11 +*/ + +// contructor +SensorDHT22::SensorDHT22(NodeManager& node_manager, int pin): SensorDHT(node_manager, pin) { + _name = "DHT22"; + _dht_type = DHT::DHT22; +} +#endif + +/* + SensorSHT21 +*/ +#ifdef MODULE_SHT21 +// contructor +SensorSHT21::SensorSHT21(NodeManager& node_manager): Sensor(node_manager) { + _name = "SHT21"; +} + +// what to do during before +void SensorSHT21::onBefore() { + // register the child + new ChildFloat(this,_node->getAvailableChildId(),S_TEMP,V_TEMP); + new ChildFloat(this,_node->getAvailableChildId(),S_HUM,V_HUM); +} + +// what to do during setup +void SensorSHT21::onSetup() { + // initialize the library + Wire.begin(); +} + +// what to do during loop +void SensorSHT21::onLoop(Child* child) { + // temperature sensor + if (child->type == V_TEMP) { + // read the temperature + float temperature = SHT2x.GetTemperature(); + // convert it + temperature = _node->celsiusToFahrenheit(temperature); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" T=")); + Serial.println(temperature); + #endif + // store the value + if (! isnan(temperature)) ((ChildFloat*)child)->setValueFloat(temperature); + } + // Humidity Sensor + else if (child->type == V_HUM) { + // read humidity + float humidity = SHT2x.GetHumidity(); + if (isnan(humidity)) return; + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" H=")); + Serial.println(humidity); + #endif + // store the value + if (! isnan(humidity)) ((ChildFloat*)child)->setValueFloat(humidity); + } +} + +// what to do as the main task when receiving a message +void SensorSHT21::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} + +/* + * SensorHTU21D + */ + // constructor +SensorHTU21D::SensorHTU21D(NodeManager& nodeManager): SensorSHT21(nodeManager) { + _name = "HTU21"; +} +#endif + +#ifdef MODULE_SWITCH +/* + * SensorSwitch + */ +SensorSwitch::SensorSwitch(NodeManager& node_manager, int pin): Sensor(node_manager, pin) { + _name = "SWITCH"; +} + +// setter/getter +void SensorSwitch::setMode(int value) { + _mode = value; +} +void SensorSwitch::setDebounce(int value) { + _debounce = value; +} +void SensorSwitch::setTriggerTime(int value) { + _trigger_time = value; +} +void SensorSwitch::setInitial(int value) { + _initial = value; +} + +// what to do during before +void SensorSwitch::onBefore() { + new ChildInt(this,_node->getAvailableChildId(),S_CUSTOM,V_TRIPPED); +} + +// what to do during setup +void SensorSwitch::onSetup() { + // set the interrupt pin so it will be called only when waking up from that interrupt + setInterrupt(_pin,_mode,_initial); + // report immediately + _report_timer->unset(); +} + +// what to do during loop +void SensorSwitch::onLoop(Child* child) { +} + +// what to do as the main task when receiving a message +void SensorSwitch::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == V_STATUS) { + // return current status + ((ChildInt*)child)->setValueInt(digitalRead(_pin)); + } +} + +// what to do when receiving an interrupt +void SensorSwitch::onInterrupt() { + Child* child = children.get(1); + // wait to ensure the the input is not floating + if (_debounce > 0) _node->sleepOrWait(_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) ) { + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" P=")); + Serial.print(_pin); + Serial.print(F(" V=")); + Serial.println(value); + #endif + ((ChildInt*)child)->setValueInt(value); + // allow the signal to be restored to its normal value + if (_trigger_time > 0) _node->sleepOrWait(_trigger_time); + } else { + // invalid + ((ChildInt*)child)->setValueInt(-255); + } +} + +/* + * SensorDoor + */ +SensorDoor::SensorDoor(NodeManager& node_manager, int pin): SensorSwitch(node_manager, pin) { + _name = "DOOR"; +} + +// what to do during before +void SensorDoor::onBefore() { + new ChildInt(this,_node->getAvailableChildId(),S_DOOR,V_TRIPPED); +} + +/* + * SensorMotion + */ +SensorMotion::SensorMotion(NodeManager& node_manager, int pin): SensorSwitch(node_manager, pin) { + _name = "MOTION"; +} + +// what to do during before +void SensorMotion::onBefore() { + new ChildInt(this,_node->getAvailableChildId(),S_MOTION,V_TRIPPED); +} + +// what to do during setup +void SensorMotion::onSetup() { + SensorSwitch::onSetup(); + // set initial value to LOW + setInitial(LOW); +} +#endif + +/* + SensorDs18b20 +*/ +#ifdef MODULE_DS18B20 +// contructor +SensorDs18b20::SensorDs18b20(NodeManager& node_manager, int pin): Sensor(node_manager, pin) { + _name = "DS18B20"; +} + +// what to do during before +void SensorDs18b20::onBefore() { + // initialize the library + OneWire* oneWire = new OneWire(_pin); + _sensors = new DallasTemperature(oneWire); + // initialize the sensors + _sensors->begin(); + // register a new child for each sensor on the bus + for(int i = 0; i < _sensors->getDeviceCount(); i++) { + new ChildFloat(this,_node->getAvailableChildId(),S_TEMP,V_TEMP); + } +} + +// what to do during setup +void SensorDs18b20::onSetup() { +} + +// what to do during loop +void SensorDs18b20::onLoop(Child* child) { + int index = -1; + // get the index of the requested child + for (int i = 1; i <= children.size(); i++) { + if (children.get(i) == child) index = i-1; + } + // 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 + temperature = _node->celsiusToFahrenheit(temperature); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" T=")); + Serial.println(temperature); + #endif + // store the value + if (! isnan(temperature)) ((ChildFloat*)child)->setValueFloat(temperature); +} + +// what to do as the main task when receiving a message +void SensorDs18b20::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} + +// returns the sensor's resolution in bits +int SensorDs18b20::getResolution() { + return _sensors->getResolution(); +} + +// set the sensor's resolution in bits +void SensorDs18b20::setResolution(int value) { + _sensors->setResolution(value); +} + +// sleep while DS18B20 calculates temperature +void SensorDs18b20::setSleepDuringConversion(bool value) { + _sleep_during_conversion = value; +} + +#endif + +/* + SensorBH1750 +*/ +#ifdef MODULE_BH1750 +// contructor +SensorBH1750::SensorBH1750(NodeManager& node_manager): Sensor(node_manager) { + _name = "BH1750"; +} +// setter/getter +void SensorBH1750::setMode(uint8_t mode) { + _lightSensor->configure(mode); +} + +// what to do during before +void SensorBH1750::onBefore() { + new ChildInt(this,_node->getAvailableChildId(),S_LIGHT_LEVEL,V_LEVEL); +} + +// what to do during setup +void SensorBH1750::onSetup() { + _lightSensor = new BH1750(); + _lightSensor->begin(); +} + +// what to do during loop +void SensorBH1750::onLoop(Child* child) { + // request the light level + int value = _lightSensor->readLightLevel(); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" L=")); + Serial.println(value); + #endif + ((ChildInt*)child)->setValueInt(value); +} + +// what to do as the main task when receiving a message +void SensorBH1750::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} +#endif + +/* + SensorMLX90614 +*/ +#ifdef MODULE_MLX90614 +// contructor +SensorMLX90614::SensorMLX90614(NodeManager& node_manager): Sensor(node_manager) { + _name = "MLX90614"; +} + +// what to do during before +void SensorMLX90614::onBefore() { + new ChildFloat(this,_node->getAvailableChildId(),S_TEMP,V_TEMP); + new ChildFloat(this,_node->getAvailableChildId(),S_TEMP,V_TEMP); +} + +// what to do during setup +void SensorMLX90614::onSetup() { + // initialize the library + _mlx = new Adafruit_MLX90614(); + _mlx->begin(); +} + +// what to do during loop +void SensorMLX90614::onLoop(Child* child) { + float temperature; + // the first child is the ambient temperature, the second the object temperature + if (children.get(1) == child) temperature = _mlx->readAmbientTempC(); + else temperature = _mlx->readObjectTempC(); + // convert it + temperature = _node->celsiusToFahrenheit(temperature); + #ifdef NODEMANAGER_DEBUG + Serial.print(F("MLX I=")); + Serial.print(child->child_id); + Serial.print(F(" T=")); + Serial.println(temperature); + #endif + if (! isnan(temperature)) ((ChildFloat*)child)->setValueFloat(temperature); +} + +// what to do as the main task when receiving a message +void SensorMLX90614::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} +#endif + + +/* + SensorBosch +*/ +#if defined(MODULE_BME280) || defined(MODULE_BMP085) || defined(MODULE_BMP280) +// contructor +SensorBosch::SensorBosch(NodeManager& node_manager): Sensor(node_manager) { + _name = "BOSH"; + // initialize the forecast samples array + _forecast_samples = new float[_forecast_samples_count]; +} + +// setter/getter +void SensorBosch::setForecastSamplesCount(int value) { + _forecast_samples_count = value; +} + +// what to do during before +void SensorBosch::onBefore() { +} + +// what to do during setup +void SensorBosch::onSetup() { +} + +// what to do during loop +void SensorBosch::onLoop(Child* child) { +} + +// what to do as the main task when receiving a message +void SensorBosch::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} + +// calculate and send the forecast back +char* 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; + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" M=")); + Serial.print(_minute_count); + Serial.print(F(" dP=")); + Serial.print(_dP_dt); + Serial.print(F(" F=")); + Serial.println(_weather[forecast]); + #endif + return _weather[forecast]; +} + +// 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) { + #ifdef NODEMANAGER_DEBUG + Serial.print(F("I2C=")); + Serial.println(i2c_address); + #endif + return i2c_address; + } + } + return addresses[0]; +} +#endif + +/* + * SensorBME280 + */ +#ifdef MODULE_BME280 +SensorBME280::SensorBME280(NodeManager& node_manager): SensorBosch(node_manager) { + _name = "BME280"; +} + +// what to do during before +void SensorBME280::onBefore() { + // register the child + new ChildFloat(this,_node->getAvailableChildId(),S_TEMP,V_TEMP); + new ChildFloat(this,_node->getAvailableChildId(),S_HUM,V_HUM); + new ChildFloat(this,_node->getAvailableChildId(),S_BARO,V_PRESSURE); + new ChildString(this,_node->getAvailableChildId(),S_BARO,V_FORECAST); +} + +// what to do during setup +void SensorBME280::onSetup() { + _bm = new Adafruit_BME280(); + if (! _bm->begin(SensorBosch::GetI2CAddress(0x60))) { + #ifdef NODEMANAGER_DEBUG + Serial.println(F("ERR")); + #endif + } +} + +void SensorBME280::onLoop(Child* child) { + // temperature sensor + if (child->type == V_TEMP) { + // read the temperature + float temperature = _bm->readTemperature(); + // convert it + temperature = _node->celsiusToFahrenheit(temperature); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" T=")); + Serial.println(temperature); + #endif + // store the value + if (! isnan(temperature)) ((ChildFloat*)child)->setValueFloat(temperature); + } + // Humidity Sensor + else if (child->type == V_HUM) { + // read humidity + float humidity = _bm->readHumidity(); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" H=")); + Serial.println(humidity); + #endif + // store the value + if (! isnan(humidity)) ((ChildFloat*)child)->setValueFloat(humidity); + } + // Pressure Sensor + else if (child->type == V_PRESSURE) { + // read pressure + float pressure = _bm->readPressure() / 100.0F; + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" P=")); + Serial.println(pressure); + #endif + // store the value + if (! isnan(pressure)) ((ChildFloat*)child)->setValueFloat(pressure); + } + // Forecast Sensor + else if (child->type == V_FORECAST) { + float pressure = _bm->readPressure() / 100.0F; + _forecast(pressure); + } +} +#endif + +/* + SensorBMP085 +*/ +#ifdef MODULE_BMP085 +// contructor +SensorBMP085::SensorBMP085(NodeManager& node_manager): SensorBosch(node_manager) { + _name = "BMP085"; +} + +// what to do during before +void SensorBMP085::onBefore() { + // register the child + new ChildFloat(this,_node->getAvailableChildId(),S_TEMP,V_TEMP); + new ChildFloat(this,_node->getAvailableChildId(),S_BARO,V_PRESSURE); + new ChildString(this,_node->getAvailableChildId(),S_BARO,V_FORECAST); +} + +// what to do during setup +void SensorBMP085::onSetup() { + _bm = new Adafruit_BMP085(); + if (! _bm->begin(SensorBosch::GetI2CAddress(0x55))) { + #ifdef NODEMANAGER_DEBUG + Serial.println(F("ERR")); + #endif + } +} + +// what to do during loop +void SensorBMP085::onLoop(Child* child) { + // temperature sensor + if (child->type == V_TEMP) { + // read the temperature + float temperature = _bm->readTemperature(); + // convert it + temperature = _node->celsiusToFahrenheit(temperature); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" T=")); + Serial.println(temperature); + #endif + // store the value + if (! isnan(temperature)) ((ChildFloat*)child)->setValueFloat(temperature); + } + // Pressure Sensor + else if (child->type == V_PRESSURE) { + // read pressure + float pressure = _bm->readPressure() / 100.0F; + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" P=")); + Serial.println(pressure); + #endif + // store the value + if (! isnan(pressure)) ((ChildFloat*)child)->setValueFloat(pressure); + } + // Forecast Sensor + else if (child->type == V_FORECAST) { + float pressure = _bm->readPressure() / 100.0F; + _forecast(pressure); + } +} +#endif + +/* + * SensorBMP280 + */ +#ifdef MODULE_BMP280 +SensorBMP280::SensorBMP280(NodeManager& node_manager): SensorBosch(node_manager) { + _name = "BMP280"; +} + + // what to do during before +void SensorBMP280::onBefore() { + // register the child + new ChildFloat(this,_node->getAvailableChildId(),S_TEMP,V_TEMP); + new ChildFloat(this,_node->getAvailableChildId(),S_BARO,V_PRESSURE); + new ChildString(this,_node->getAvailableChildId(),S_BARO,V_FORECAST); +} + +// what to do during setup +void SensorBMP280::onSetup() { + _bm = new Adafruit_BMP280(); + if (! _bm->begin(SensorBosch::GetI2CAddress(0x58))) { + #ifdef NODEMANAGER_DEBUG + Serial.println(F("ERR")); + #endif + } +} + +void SensorBMP280::onLoop(Child* child) { + // temperature sensor + if (child->type == V_TEMP) { + // read the temperature + float temperature = _bm->readTemperature(); + // convert it + temperature = _node->celsiusToFahrenheit(temperature); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" T=")); + Serial.println(temperature); + #endif + // store the value + if (! isnan(temperature)) ((ChildFloat*)child)->setValueFloat(temperature); + } + // Pressure Sensor + else if (child->type == V_PRESSURE) { + // read pressure + float pressure = _bm->readPressure() / 100.0F; + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" P=")); + Serial.println(pressure); + #endif + // store the value + if (! isnan(pressure)) ((ChildFloat*)child)->setValueFloat(pressure); + } + // Forecast Sensor + else if (child->type == V_FORECAST) { + float pressure = _bm->readPressure() / 100.0F; + _forecast(pressure); + } +} +#endif + +/* + SensorSonoff +*/ +#ifdef MODULE_SONOFF +// contructor +SensorSonoff::SensorSonoff(NodeManager& node_manager): Sensor(node_manager) { + _name = "SONOFF"; +} + +// 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() { + // register the child + new ChildInt(this,_node->getAvailableChildId(),S_BINARY,V_STATUS); +} + +// 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(Child* child) { + _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(child); + } + _old_value = value; +} + +// what to do as the main task when receiving a message +void SensorSonoff::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + 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(child); + } + if (message->getCommand() == C_REQ) { + // return the current state + ((ChildInt*)child)->setValueInt(_state); + } +} + +// toggle the state +void SensorSonoff::_toggle(Child* child) { + // 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); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" V=")); + Serial.println(_state); + #endif + ((ChildInt*)child)->setValueInt(_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 +*/ +#ifdef MODULE_HCSR04 +// contructor +SensorHCSR04::SensorHCSR04(NodeManager& node_manager, int pin): Sensor(node_manager, pin) { + _name = "HCSR04"; + _trigger_pin = pin; + _echo_pin = pin; +} + +// 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 before +void SensorHCSR04::onBefore() { + // register the child + new ChildInt(this,_node->getAvailableChildId(),S_DISTANCE,V_DISTANCE); +} + +// what to do during setup +void SensorHCSR04::onSetup() { + // initialize the library + _sonar = new NewPing(_trigger_pin,_echo_pin,_max_distance); +} + +// what to do during loop +void SensorHCSR04::onLoop(Child* child) { + int distance = _node->getIsMetric() ? _sonar->ping_cm() : _sonar->ping_in(); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" D=")); + Serial.println(distance); + #endif + ((ChildInt*)child)->setValueInt(distance); +} + +// what to do as the main task when receiving a message +void SensorHCSR04::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} +#endif + +/* + SensorMCP9808 +*/ +#ifdef MODULE_MCP9808 +// contructor +SensorMCP9808::SensorMCP9808(NodeManager& node_manager): Sensor(node_manager) { + _name = "MCP9808"; +} + +// what to do during before +void SensorMCP9808::onBefore() { + new ChildFloat(this,_node->getAvailableChildId(),S_TEMP,V_TEMP); +} + +// what to do during setup +void SensorMCP9808::onSetup() { + _mcp = new Adafruit_MCP9808(); +} + +// what to do during loop +void SensorMCP9808::onLoop(Child* child) { + float temperature = _mcp->readTempC(); + // convert it + temperature = _node->celsiusToFahrenheit(temperature); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" T=")); + Serial.println(temperature); + #endif + // store the value + if (! isnan(temperature)) ((ChildFloat*)child)->setValueFloat(temperature); +} + +// what to do as the main task when receiving a message +void SensorMCP9808::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} +#endif + +/* + * SensorMQ + */ +#ifdef MODULE_MQ + +float SensorMQ::_default_LPGCurve[3] = {2.3,0.21,-0.47}; +float SensorMQ::_default_COCurve[3] = {2.3,0.72,-0.34}; +float SensorMQ::_default_SmokeCurve[3] = {2.3,0.53,-0.44}; + +SensorMQ::SensorMQ(NodeManager& node_manager, int pin): Sensor(node_manager, pin) { + _name = "MQ"; + _LPGCurve = SensorMQ::_default_LPGCurve; + _COCurve = SensorMQ::_default_COCurve; + _SmokeCurve = SensorMQ::_default_SmokeCurve; +} + +//setter/getter +void SensorMQ::setTargetGas(int value) { + _target_gas = value; +} +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 = value; +} +void SensorMQ::setCOCurve(float *value) { + _COCurve = value; +} +void SensorMQ::setSmokeCurve(float *value) { + _SmokeCurve = value; +} + +// what to do during before +void SensorMQ::onBefore() { + // prepare the pin for input + pinMode(_pin, INPUT); + // register the child + new ChildInt(this,_node->getAvailableChildId(),S_AIR_QUALITY,V_LEVEL); +} + +// what to do during setup +void SensorMQ::onSetup() { + _ro = _MQCalibration(); +} + +// what to do during loop +void SensorMQ::onLoop(Child* child) { + // 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; + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->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 + ((ChildInt*)child)->setValueInt((int16_t)ceil(value)); +} + +// what to do as the main task when receiving a message +void SensorMQ::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} + +// 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]))); +} +#endif + + + +/* + SensorMHZ19 +*/ +#ifdef MODULE_MHZ19 +// contructor +SensorMHZ19::SensorMHZ19(NodeManager& node_manager, int rxpin, int txpin): Sensor(node_manager, rxpin) { + _name = "MHZ19"; + _rx_pin = rxpin; + _tx_pin = txpin; +} + +// what to do during before +void SensorMHZ19::onBefore() { + // register the child + new ChildInt(this,_node->getAvailableChildId(),S_AIR_QUALITY,V_LEVEL); +} + +// what to do during setup +void SensorMHZ19::onSetup() { + _ser = new SoftwareSerial(_rx_pin, _tx_pin); + _ser->begin(9600); + delay(2000); + // clear CO2 buffer + while (_ser->read()!=-1) {}; +} + +// what to do during loop +void SensorMHZ19::onLoop(Child* child) { + // Read the ppm value + int co2ppm = _readCO2(); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" ppm=")); + Serial.println(co2ppm); + #endif + // store the value + ((ChildInt*)child)->setValueInt(co2ppm); +} + + +// what to do as the main task when receiving a message +void SensorMHZ19::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} + +// Read out the CO2 data +int SensorMHZ19::_readCO2() { + while (_ser->read() != -1) {}; //clear serial buffer + unsigned char response[9]; // for answer + byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; + // Command to ask for data. + _ser->write(cmd, 9); //request PPM CO2 + // Then for 1 second listen for 9 bytes of data. + _ser->readBytes(response, 9); + #ifdef NODEMANAGER_DEBUG + for (int i=0; i<9; i++) { + Serial.print(response[i], HEX); + Serial.print(F("-")); + } + Serial.println(F("END")); + #endif + if (response[0] != 0xFF) { + Serial.println(F("ERR byte")); + return -1; + } + if (response[1] != 0x86) { + Serial.println(F("ERR command")); + return -1; + } + int responseHigh = (int) response[2]; + int responseLow = (int) response[3]; + int ppm = (256 * responseHigh) + responseLow; + return ppm; +} + +#endif + +/* + SensorAM2320 +*/ +#ifdef MODULE_AM2320 +// constructor +SensorAM2320::SensorAM2320(NodeManager& node_manager): Sensor(node_manager) { + _name = "AM2320"; +} + +// what do to during before +void SensorAM2320::onBefore() { + // register the child + new ChildFloat(this,_node->getAvailableChildId(),S_TEMP,V_TEMP); + new ChildFloat(this,_node->getAvailableChildId(),S_HUM,V_HUM); +} + +// what do to during setup +void SensorAM2320::onSetup() { + _th = new AM2320(); +} + +// what do to during loop +void SensorAM2320::onLoop(Child* child) { + // read data from the sensor + int status = _th->Read(); + if (status != 0) return; + // temperature sensor + if (child->type == V_TEMP) { + float temperature = _th->t; + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" T=")); + Serial.println(temperature); + #endif + // store the value + if (! isnan(temperature)) ((ChildFloat*)child)->setValueFloat(temperature); + } + // Humidity Sensor + else if (child->type == V_HUM) { + // read humidity + float humidity = _th->h; + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" T=")); + Serial.println(humidity); + #endif + // store the value + if (! isnan(humidity)) ((ChildFloat*)child)->setValueFloat(humidity); + } +} + +// what do to as the main task when receiving a message +void SensorAM2320::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} +#endif + +/* + SensorTSL2561 +*/ +#ifdef MODULE_TSL2561 +// contructor +SensorTSL2561::SensorTSL2561(NodeManager& node_manager): Sensor(node_manager) { + _name = "TSL2561"; +} + +// setter/getter +void SensorTSL2561::setGain(int value) { + _tsl_gain = value; +} +void SensorTSL2561::setTiming(int value) { + _tsl_timing = value; +} +void SensorTSL2561::setSpectrum(int value) { + _tsl_spectrum = value; +} +void SensorTSL2561::setAddress(int value) { + _tsl_address = value; +} + +// what do to during before +void SensorTSL2561::onBefore() { + // register the child + new ChildInt(this,_node->getAvailableChildId(),S_LIGHT_LEVEL,V_LEVEL); +} + +// what do to during setup +void SensorTSL2561::onSetup() { + switch (_tsl_address) { + case SensorTSL2561::ADDR_FLOAT: + _tsl = new TSL2561(TSL2561_ADDR_FLOAT); + break; + case SensorTSL2561::ADDR_LOW: + _tsl = new TSL2561(TSL2561_ADDR_LOW); + break; + case SensorTSL2561::ADDR_HIGH: + _tsl = new TSL2561(TSL2561_ADDR_HIGH); + break; + } + if (_tsl->begin()) { + switch (_tsl_gain) { + case SensorTSL2561::GAIN_0X: + _tsl->setGain(TSL2561_GAIN_0X); + break; + case SensorTSL2561::GAIN_16X: + _tsl->setGain(TSL2561_GAIN_16X); + break; + } + switch (_tsl_timing) { + case SensorTSL2561::INTEGRATIONTIME_13MS: + _tsl->setTiming(TSL2561_INTEGRATIONTIME_13MS); + break; + case SensorTSL2561::INTEGRATIONTIME_101MS: + _tsl->setTiming(TSL2561_INTEGRATIONTIME_101MS); + break; + case SensorTSL2561::INTEGRATIONTIME_402MS: + _tsl->setTiming(TSL2561_INTEGRATIONTIME_402MS); + break; + } + } + else { + #ifdef NODEMANAGER_DEBUG + Serial.println(F("ERROR")); + #endif + } +} + +// what do to during loop +void SensorTSL2561::onLoop(Child* child) { + // request the light level + switch (_tsl_spectrum) { + case SensorTSL2561::VISIBLE: + ((ChildInt*)child)->setValueInt(_tsl->getLuminosity(TSL2561_VISIBLE)); + break; + case SensorTSL2561::FULLSPECTRUM: + ((ChildInt*)child)->setValueInt(_tsl->getLuminosity(TSL2561_FULLSPECTRUM)); + break; + case SensorTSL2561::INFRARED: + ((ChildInt*)child)->setValueInt(_tsl->getLuminosity(TSL2561_INFRARED)); + break; + case SensorTSL2561::FULL: + // request the full light level + uint32_t lum = _tsl->getFullLuminosity(); + uint16_t ir, full; + ir = lum >> 16; + full = lum & 0xFFFF; + ((ChildInt*)child)->setValueInt(_tsl->calculateLux(full, ir)); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" LUX=")); + Serial.print(((ChildInt*)child)->getValueInt()); + Serial.print(F(" IR=")); + Serial.print(ir); + Serial.print(F(" FULL=")); + Serial.print(full); + Serial.print(F(" VIS=")); + Serial.println(full-ir); + #endif + break; + } + #ifdef NODEMANAGER_DEBUG + if (_tsl_spectrum < 3) { + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" L=")); + Serial.println(((ChildInt*)child)->getValueInt()); + } + #endif +} + +// what do to as the main task when receiving a message +void SensorTSL2561::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} +#endif + +/* + SensorPT100 +*/ +#ifdef MODULE_PT100 +// contructor +SensorPT100::SensorPT100(NodeManager& node_manager, int pin): Sensor(node_manager, pin) { + _name = "PT100"; +} + +// setter/getter +void SensorPT100::setVoltageRef(float value) { + _voltageRef = value; +} + +// what to do during before +void SensorPT100::onBefore() { + // register the child + new ChildFloat(this,_node->getAvailableChildId(),S_TEMP,V_TEMP); +} + +// what to do during setup +void SensorPT100::onSetup() { + _PT100 = new DFRobotHighTemperature(_voltageRef); + // set the pin as input + pinMode(_pin, INPUT); + +} + +// what to do during loop +void SensorPT100::onLoop(Child* child) { + // read the PT100 sensor + int temperature = _PT100->readTemperature(_pin); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" T=")); + Serial.println(temperature); + #endif + // store the value + if (! isnan(temperature)) ((ChildFloat*)child)->setValueFloat(temperature); +} + +// what to do as the main task when receiving a message +void SensorPT100::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} +#endif + +/* + SensorDimmer +*/ + +#ifdef MODULE_DIMMER +// contructor +SensorDimmer::SensorDimmer(NodeManager& node_manager, int pin): Sensor(node_manager, pin) { + _name = "DIMMER"; +} + +// setter/getter +void SensorDimmer::setEasing(int value) { + _easing = value; +} +void SensorDimmer::setDuration(int value) { + _duration = value*1000; +} +void SensorDimmer::setStepDuration(int value) { + _duration = value; +} + +// what to do during before +void SensorDimmer::onBefore() { + // register the child + new ChildInt(this,_node->getAvailableChildId(),S_DIMMER,V_PERCENTAGE); +} + +// what to do during setup +void SensorDimmer::onSetup() { + pinMode(_pin, OUTPUT); +} + +// what to do during loop +void SensorDimmer::onLoop(Child* child) { +} + +// what to do as the main task when receiving a message +void SensorDimmer::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_SET && message->type == child->type) { + int percentage = message->getInt(); + // normalize the provided percentage + if (percentage < 0) percentage = 0; + if (percentage > 100) percentage = 100; + _fadeTo(child,percentage); + ((ChildInt*)child)->setValueInt(_percentage); + } + if (message->getCommand() == C_REQ) { + // return the current status + ((ChildInt*)child)->setValueInt(_percentage); + } +} + +// fade to the provided value +void SensorDimmer::_fadeTo(Child* child, int target_percentage) { + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" V=")); + Serial.println(target_percentage); + #endif + // count how many steps we need to do + int steps = _duration / _step_duration; + // for each step + for (int current_step = 1; current_step <= steps; current_step++) { + // calculate the delta between the target value and the current + int delta = target_percentage - _percentage; + // calculate the smooth transition and adjust it in the 0-255 range + int value_to_write = (int)(_getEasing(current_step,_percentage,delta,steps) / 100. * 255); + // write to the PWM output + analogWrite(_pin,value_to_write); + // wait at the end of this step + wait(_step_duration); + } + _percentage = target_percentage; +} + +// for smooth transitions. t: current time, b: beginning value, c: change in value, d: duration +float SensorDimmer::_getEasing(float t, float b, float c, float d) { + if (_easing == EASE_INSINE) return -c * cos(t/d * (M_PI/2)) + c + b; + else if (_easing == EASE_OUTSINE) return c * sin(t/d * (M_PI/2)) + b; + else if (_easing == EASE_INOUTSINE) return -c/2 * (cos(M_PI*t/d) - 1) + b; + else return c*t/d + b; +} +#endif + +/* + SensorPulseMeter +*/ +#ifdef MODULE_PULSE_METER +// contructor +SensorPulseMeter::SensorPulseMeter(NodeManager& node_manager, int pin): Sensor(node_manager, pin) { + _name = "PULSE"; +} + +// setter/getter +void SensorPulseMeter::setPulseFactor(float value) { + _pulse_factor = value; +} +void SensorPulseMeter::setInitialValue(int value) { + _initial_value = value; +} +void SensorPulseMeter::setInterruptMode(int value) { + _interrupt_mode = value; +} + +// what to do during before +void SensorPulseMeter::onBefore() { + // register the child + new ChildFloat(this,_node->getAvailableChildId(),S_CUSTOM,V_CUSTOM); +} + +// what to do during setup +void SensorPulseMeter::onSetup() { + // configure the interrupt pin so onInterrupt() will be called on tip + setInterrupt(_pin,_interrupt_mode,_initial_value); +} + +// what to do during loop +void SensorPulseMeter::onLoop(Child* child) { + // do not report anything if called by an interrupt + if (_node->getLastInterruptPin() == _interrupt_pin) return; + // time to report the rain so far + _reportTotal(child); + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" T=")); + Serial.println(((ChildFloat*)child)->getValueFloat()); + #endif + // reset the counter + _count = 0; +} + +// what to do as the main task when receiving a message +void SensorPulseMeter::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) { + // report the total the last period + _reportTotal(child); + } +} + +// what to do when receiving an interrupt +void SensorPulseMeter::onInterrupt() { + // increase the counter + _count++; + #ifdef NODEMANAGER_DEBUG + Serial.print(_name); + Serial.println(F("+")); + #endif +} + +// return the total based on the pulses counted +void SensorPulseMeter::_reportTotal(Child* child) { + ((ChildFloat*)child)->setValueFloat(_count / _pulse_factor); +} + +/* + SensorRainGauge +*/ +// contructor +SensorRainGauge::SensorRainGauge(NodeManager& node_manager, int pin): SensorPulseMeter(node_manager, pin) { + _name = "RAIN_GAUGE"; +} + +// what to do during before +void SensorRainGauge::onBefore() { + // register the child + new ChildFloat(this,_node->getAvailableChildId(),S_RAIN,V_RAIN); + setPulseFactor(9.09); +} + +/* + SensorPowerMeter +*/ +// contructor +SensorPowerMeter::SensorPowerMeter(NodeManager& node_manager, int pin): SensorPulseMeter(node_manager, pin) { + _name = "POWER"; +} + +// what to do during before +void SensorPowerMeter::onBefore() { + // register the child + new ChildDouble(this,_node->getAvailableChildId(),S_POWER,V_KWH); + setPulseFactor(1000); +} + +// return the total based on the pulses counted +void SensorPowerMeter::_reportTotal(Child* child) { + ((ChildDouble*)child)->setValueDouble(_count / _pulse_factor); +} + +/* + SensorWaterMeter +*/ +// contructor +SensorWaterMeter::SensorWaterMeter(NodeManager& node_manager, int pin): SensorPulseMeter(node_manager, pin) { + _name = "WATER"; +} + +// what to do during before +void SensorWaterMeter::onBefore() { + // register the child + new ChildDouble(this,_node->getAvailableChildId(),S_WATER,V_VOLUME); + setPulseFactor(1000); +} + +// return the total based on the pulses counted +void SensorWaterMeter::_reportTotal(Child* child) { + ((ChildDouble*)child)->setValueDouble(_count / _pulse_factor); +} +#endif + +/* + SensorPlantowerPMS +*/ +#ifdef MODULE_PMS +// contructor +SensorPlantowerPMS::SensorPlantowerPMS(NodeManager& node_manager, int rxpin, int txpin): Sensor(node_manager, rxpin) { + _name = "PMS"; + _rx_pin = rxpin; + _tx_pin = txpin; +} + +// what to do during before +void SensorPlantowerPMS::onBefore() { + // register the child + new ChildInt(this, _node->getAvailableChildId(), S_AIR_QUALITY, V_LEVEL, "PM1.0"); + new ChildInt(this, _node->getAvailableChildId(), S_AIR_QUALITY, V_LEVEL, "PM2.5"); + new ChildInt(this, _node->getAvailableChildId(), S_AIR_QUALITY, V_LEVEL, "PM10.0"); +} + +// what to do during setup +void SensorPlantowerPMS::onSetup() { + _ser = new SoftwareSerial(_rx_pin, _tx_pin); + _pms = new PMS(*_ser); + _ser->begin(9600); +} + +// Clean _valuesRead flag at the beginning so the onLoop function knows when to read values (should be done only once for all children) +void SensorPlantowerPMS::loop(MyMessage* message) { + _valuesRead = false; + _valuesReadError = false; + Sensor::loop(message); +} + +// what to do during loop +void SensorPlantowerPMS::onLoop(Child* child) { + // Read the ppm values + if (!_valuesRead || _valuesReadError) { + _valuesReadError = !_pms->read(_data, 1000); + if (_valuesReadError) { + Serial.println(F("ERR PMS read")); + return; + } + _valuesRead = true; + } + int val = 0; + if (child == children.get(1)) { + // PM1.0 values + val = _data.PM_AE_UG_1_0; + } else if (child == children.get(2)) { + // PM 2.5 values + val = _data.PM_AE_UG_2_5; + } else if (child == children.get(3)) { + // PM 10.0 values + val = _data.PM_AE_UG_10_0; + } else { + Serial.println(F("ERR child")); + return; + } + // store the value + ((ChildInt*)child)->setValueInt(val); + #if DEBUG == 1 + Serial.print(_name); + Serial.print(F(" I=")); + Serial.print(child->child_id); + Serial.print(F(" µg/m³=")); + Serial.println(val); + #endif +} + +// what to do as the main task when receiving a message +void SensorPlantowerPMS::onReceive(MyMessage* message) { + Child* child = getChild(message->sensor); + if (child == nullptr) return; + if (message->getCommand() == C_REQ && message->type == child->type) onLoop(child); +} +#endif + +/* + SensorConfiguration +*/ +// contructor +SensorConfiguration::SensorConfiguration(NodeManager& node_manager): Sensor(node_manager) { + _name = "CONFIG"; +} + +// what to do during before +void SensorConfiguration::onBefore() { + new ChildInt(this,CONFIGURATION_CHILD_ID,S_CUSTOM,V_CUSTOM); +} + +// what to do during setup +void SensorConfiguration::onSetup() { + +} + +// what to do during loop +void SensorConfiguration::onLoop(Child* child) { +} + +// what to do as the main task when receiving a message +void SensorConfiguration::onReceive(MyMessage* message) { + // expect a REQ, V_CUSTOM message + if (message->getCommand() != C_REQ && message->type != V_CUSTOM) return; + // parse the request + Request request = Request(message->sensor,message->getString()); + int function = request.getFunction(); + int child_id = request.getChildId(); + // if the message is for the board itself + if (child_id == 0) { + switch(function) { + case 1: _node->hello(); break; + case 3: _node->setSleepSeconds(request.getValueInt()); break; + case 4: _node->setSleepMinutes(request.getValueInt()); break; + case 5: _node->setSleepHours(request.getValueInt()); break; + case 29: _node->setSleepDays(request.getValueInt()); break; + #ifndef MY_GATEWAY_ESP8266 + case 6: _node->reboot(); return; + #endif + case 7: _node->clearEeprom(); break; + case 8: _node->sendMessage(CONFIGURATION_CHILD_ID,V_CUSTOM,VERSION); return; + case 9: _node->wakeup(); break; + case 10: _node->setRetries(request.getValueInt()); break; + case 19: _node->setSleepInterruptPin(request.getValueInt()); break; + case 20: _node->setSleepBetweenSend(request.getValueInt()); break; + case 21: _node->setAck(request.getValueInt()); break; + case 22: _node->setIsMetric(request.getValueInt()); break; + case 24: _node->powerOn(); break; + case 25: _node->powerOff(); break; + case 27: _node->saveToMemory(0,request.getValueInt()); break; + case 28: _node->setInterruptMinDelta(request.getValueInt()); break; + case 30: _node->setSleepOrWait(request.getValueInt()); break; + case 31: _node->setRebootPin(request.getValueInt()); break; + case 32: _node->setADCOff(); break; + case 36: _node->setReportIntervalSeconds(request.getValueInt()); break; + case 37: _node->setReportIntervalMinutes(request.getValueInt()); break; + case 38: _node->setReportIntervalHours(request.getValueInt()); break; + case 39: _node->setReportIntervalDays(request.getValueInt()); break; + default: return; + } + // the request is for a sensor + } else { + // retrieve the sensor the child is belonging to + Sensor* sensor = _node->getSensorWithChild(child_id); + if (sensor == nullptr) return; + // if the message is for a function common to all the sensors + if (request.getFunction() < 100) { + switch(function) { + case 1: sensor->setPin(request.getValueInt()); break; + case 5: sensor->setSamples(request.getValueInt()); break; + case 6: sensor->setSamplesInterval(request.getValueInt()); break; + case 7: sensor->setTrackLastValue(request.getValueInt()); break; + case 9: sensor->setForceUpdateMinutes(request.getValueInt()); break; + case 13: sensor->powerOn(); break; + case 14: sensor->powerOff(); break; + case 16: sensor->setReportIntervalMinutes(request.getValueInt()); break; + case 17: sensor->setReportIntervalSeconds(request.getValueInt()); break; + case 19: sensor->setReportIntervalHours(request.getValueInt()); break; + case 20: sensor->setReportIntervalDays(request.getValueInt()); break; + default: return; + } + } else { + #ifndef MY_GATEWAY_ESP8266 + // the message is for a function specific to a sensor + if (strcmp(sensor->getName(),"BATTERY") == 0) { + SensorBattery* custom_sensor = (SensorBattery*)sensor; + switch(function) { + case 102: custom_sensor->setMinVoltage(request.getValueFloat()); break; + case 103: custom_sensor->setMaxVoltage(request.getValueFloat()); break; + case 104: custom_sensor->setBatteryInternalVcc(request.getValueInt()); break; + case 105: custom_sensor->setBatteryPin(request.getValueInt()); break; + case 106: custom_sensor->setBatteryVoltsPerBit(request.getValueFloat()); break; + default: return; + } + } + if (strcmp(sensor->getName(),"SIGNAL") == 0) { + SensorSignal* custom_sensor = (SensorSignal*)sensor; + switch(function) { + case 101: custom_sensor->setSignalCommand(request.getValueInt()); break; + default: return; + } + } + #endif + #ifdef MODULE_ANALOG_INPUT + if (strcmp(sensor->getName(),"ANALOG_I") == 0 || strcmp(sensor->getName(),"LDR") == 0 || strcmp(sensor->getName(),"RAIN") == 0 || strcmp(sensor->getName(),"SOIL") == 0) { + SensorAnalogInput* custom_sensor = (SensorAnalogInput*)sensor; + switch(function) { + case 101: custom_sensor->setReference(request.getValueInt()); break; + case 102: custom_sensor->setReverse(request.getValueInt()); break; + case 103: custom_sensor->setOutputPercentage(request.getValueInt()); break; + case 104: custom_sensor->setRangeMin(request.getValueInt()); break; + case 105: custom_sensor->setRangeMax(request.getValueInt()); break; + default: return; + } + } + #endif + #ifdef MODULE_THERMISTOR + if (strcmp(sensor->getName(),"THERMISTOR") == 0) { + SensorThermistor* custom_sensor = (SensorThermistor*)sensor; + switch(function) { + case 101: custom_sensor->setNominalResistor((long)request.getValueInt()); break; + case 102: custom_sensor->setNominalTemperature(request.getValueInt()); break; + case 103: custom_sensor->setBCoefficient(request.getValueInt()); break; + case 104: custom_sensor->setSeriesResistor((long)request.getValueInt()); break; + case 105: custom_sensor->setOffset(request.getValueFloat()); break; + default: return; + } + } + #endif + #ifdef MODULE_ACS712 + if (strcmp(sensor->getName(),"ACS712") == 0) { + SensorACS712* custom_sensor = (SensorACS712*)sensor; + switch(function) { + case 100: custom_sensor->setmVPerAmp(request.getValueInt()); break; + case 102: custom_sensor->setOffset(request.getValueInt()); break; + default: return; + } + } + #endif + #ifdef MODULE_DIGITAL_OUTPUT + if (strcmp(sensor->getName(),"DIGITAL_O") == 0 || strcmp(sensor->getName(),"RELAY") == 0 || strcmp(sensor->getName(),"LATCHING") == 0) { + SensorDigitalOutput* custom_sensor = (SensorDigitalOutput*)sensor; + switch(function) { + case 103: custom_sensor->setOnValue(request.getValueInt()); break; + case 104: custom_sensor->setLegacyMode(request.getValueInt()); break; + case 105: custom_sensor->setSafeguard(request.getValueInt()); break; + case 106: custom_sensor->setInputIsElapsed(request.getValueInt()); break; + case 107: custom_sensor->setWaitAfterSet(request.getValueInt()); break; + default: return; + } + if (function > 200 && strcmp(sensor->getName(),"LATCHING") == 0) { + SensorLatchingRelay* custom_sensor_2 = (SensorLatchingRelay*)sensor; + switch(function) { + case 201: custom_sensor_2->setPulseWidth(request.getValueInt()); break; + case 202: custom_sensor_2->setPinOff(request.getValueInt()); break; + case 203: custom_sensor_2->setPinOn(request.getValueInt()); break; + default: return; + } + } + } + #endif + #ifdef MODULE_SWITCH + if (strcmp(sensor->getName(),"SWITCH") == 0 || strcmp(sensor->getName(),"DOOR") == 0 || strcmp(sensor->getName(),"MOTION") == 0) { + SensorSwitch* custom_sensor = (SensorSwitch*)sensor; + switch(function) { + case 101: custom_sensor->setMode(request.getValueInt()); break; + case 102: custom_sensor->setDebounce(request.getValueInt()); break; + case 103: custom_sensor->setTriggerTime(request.getValueInt()); break; + case 104: custom_sensor->setInitial(request.getValueInt()); break; + default: return; + } + } + #endif + #ifdef MODULE_DS18B20 + if (strcmp(sensor->getName(),"DS18B20") == 0) { + SensorDs18b20* custom_sensor = (SensorDs18b20*)sensor; + switch(function) { + case 101: custom_sensor->setResolution(request.getValueInt()); break; + case 102: custom_sensor->setSleepDuringConversion(request.getValueInt()); break; + default: return; + } + } + #endif + #ifdef MODULE_BH1750 + if (strcmp(sensor->getName(),"BH1750") == 0) { + SensorBH1750* custom_sensor = (SensorBH1750*)sensor; + switch(function) { + case 101: custom_sensor->setMode(request.getValueInt()); break; + default: return; + } + } + #endif + #if defined(MODULE_BME280) || defined(MODULE_BMP085) || defined(MODULE_BMP280) + if (strcmp(sensor->getName(),"BMP085") == 0 || strcmp(sensor->getName(),"BME280") == 0 || strcmp(sensor->getName(),"BMP280") == 0) { + SensorBosch* custom_sensor = (SensorBosch*)sensor; + switch(function) { + case 101: custom_sensor->setForecastSamplesCount(request.getValueInt()); break; + default: return; + } + } + #endif + #ifdef MODULE_SONOFF + if (strcmp(sensor->getName(),"SONOFF") == 0) { + SensorSonoff* custom_sensor = (SensorSonoff*)sensor; + switch(function) { + case 101: custom_sensor->setButtonPin(request.getValueInt()); break; + case 102: custom_sensor->setRelayPin(request.getValueInt()); break; + case 103: custom_sensor->setLedPin(request.getValueInt()); break; + default: return; + } + } + #endif + #ifdef MODULE_HCSR04 + if (strcmp(sensor->getName(),"HCSR04") == 0) { + SensorHCSR04* custom_sensor = (SensorHCSR04*)sensor; + switch(function) { + case 101: custom_sensor->setTriggerPin(request.getValueInt()); break; + case 102: custom_sensor->setEchoPin(request.getValueInt()); break; + case 103: custom_sensor->setMaxDistance(request.getValueInt()); break; + default: return; + } + } + #endif + #ifdef MODULE_MQ + if (strcmp(sensor->getName(),"MQ") == 0) { + SensorMQ* custom_sensor = (SensorMQ*)sensor; + switch(function) { + case 101: custom_sensor->setTargetGas(request.getValueInt()); break; + case 102: custom_sensor->setRlValue(request.getValueFloat()); break; + case 103: custom_sensor->setRoValue(request.getValueFloat()); break; + case 104: custom_sensor->setCleanAirFactor(request.getValueFloat()); break; + case 105: custom_sensor->setCalibrationSampleTimes(request.getValueInt()); break; + case 106: custom_sensor->setCalibrationSampleInterval(request.getValueInt()); break; + case 107: custom_sensor->setReadSampleTimes(request.getValueInt()); break; + case 108: custom_sensor->setReadSampleInterval(request.getValueInt()); break; + default: return; + } + } + #endif + #ifdef MODULE_TSL2561 + if (strcmp(sensor->getName(),"TSL2561") == 0) { + SensorTSL2561* custom_sensor = (SensorTSL2561*)sensor; + switch(function) { + case 101: custom_sensor->setGain(request.getValueInt()); break; + case 102: custom_sensor->setTiming(request.getValueInt()); break; + case 103: custom_sensor->setSpectrum(request.getValueInt()); break; + case 104: custom_sensor->setAddress(request.getValueInt()); break; + default: return; + } + } + #endif + #ifdef MODULE_PT100 + if (strcmp(sensor->getName(),"PT100") == 0) { + SensorPT100* custom_sensor = (SensorPT100*)sensor; + switch(function) { + case 101: custom_sensor->setVoltageRef(request.getValueFloat()); break; + default: return; + } + } + #endif + #ifdef MODULE_DIMMER + if (strcmp(sensor->getName(),"DIMMER") == 0) { + SensorDimmer* custom_sensor = (SensorDimmer*)sensor; + switch(function) { + case 101: custom_sensor->setEasing(request.getValueInt()); break; + case 102: custom_sensor->setDuration(request.getValueInt()); break; + case 103: custom_sensor->setStepDuration(request.getValueInt()); break; + default: return; + } + } + #endif + #ifdef MODULE_PULSE_METER + if (strcmp(sensor->getName(),"RAIN_GAUGE") == 0 || strcmp(sensor->getName(),"POWER") == 0 || strcmp(sensor->getName(),"WATER") == 0) { + SensorPulseMeter* custom_sensor = (SensorPulseMeter*)sensor; + switch(function) { + case 102: custom_sensor->setPulseFactor(request.getValueFloat()); break; + default: return; + } + } + #endif + } + } + _node->sendMessage(CONFIGURATION_CHILD_ID,V_CUSTOM,function); +} + +/******************************************* + NodeManager +*/ + +// initialize the node manager +NodeManager::NodeManager() { + // setup the message container + _message = MyMessage(); +} + +int NodeManager::_last_interrupt_pin = -1; +long NodeManager::_last_interrupt_1 = millis(); +long NodeManager::_last_interrupt_2 = millis(); +long NodeManager::_interrupt_min_delta = 100; + +// setter/getter +void NodeManager::setRetries(int value) { + _retries = value; +} +int NodeManager::getRetries() { + return _retries; +} + +void NodeManager::setSleepSeconds(int value) { + // set the status to AWAKE if the time provided is 0, SLEEP otherwise + if (value == 0) _status = AWAKE; + else _status = SLEEP; + // store the time + _sleep_time = value; + // save sleep settings to eeprom + if (_save_sleep_settings) _saveSleepSettings(); +} +void NodeManager::setSleepMinutes(int value) { + setSleepSeconds(value*60); +} +void NodeManager::setSleepHours(int value) { + setSleepMinutes(value*60); +} +void NodeManager::setSleepDays(int value) { + setSleepHours(value*24); +} +long NodeManager::getSleepSeconds() { + return _sleep_time; +} +void NodeManager::setSleepInterruptPin(int value) { + _sleep_interrupt_pin = value; +} +void NodeManager::setInterrupt(int pin, int mode, int initial) { + if (pin == INTERRUPT_PIN_1) { + _interrupt_1_mode = mode; + _interrupt_1_initial = initial; + } + if (pin == INTERRUPT_PIN_2) { + _interrupt_2_mode = mode; + _interrupt_2_initial = initial; + } +} +void NodeManager::setInterruptMinDelta(long value) { + _interrupt_min_delta = value; +} +void NodeManager::setPowerPins(int ground_pin, int vcc_pin, int wait_time) { + if (_powerManager == nullptr) return; + _powerManager->setPowerPins(ground_pin, vcc_pin, wait_time); +} +void NodeManager::powerOn() { + if (_powerManager == nullptr) return; + _powerManager->powerOn(); +} +void NodeManager::powerOff() { + if (_powerManager == nullptr) return; + _powerManager->powerOff(); +} +void NodeManager::setSleepBetweenSend(int value) { + _sleep_between_send = value; +} +int NodeManager::getSleepBetweenSend() { + return _sleep_between_send; +} +void NodeManager::setAck(bool value) { + _ack = value; +} +bool NodeManager::getAck() { + return _ack; +} +void NodeManager::setGetControllerConfig(bool value) { + _get_controller_config = value; +} +void NodeManager::setIsMetric(bool value) { + _is_metric = value; +} +bool NodeManager::getIsMetric() { + return _is_metric; +} +void NodeManager::setSaveSleepSettings(bool value) { + _save_sleep_settings = value; +} + +// Convert a temperature from celsius to fahrenheit depending on how isMetric is set +float NodeManager::celsiusToFahrenheit(float temperature) { + if (_is_metric) return temperature; + // convert the temperature from C to F + return temperature * 1.8 + 32; +} + +// return true if sleep or wait is configured and hence this is a sleeping node +bool NodeManager::isSleepingNode() { + if (_status == SLEEP) return true; + return false; +} + +// register a sensor against NodeManager +void NodeManager::registerSensor(Sensor* sensor) { + sensors.push(sensor); +} + +// setup NodeManager +void NodeManager::before() { + // print out the version + #ifdef NODEMANAGER_DEBUG + Serial.print(F("NodeManager v")); + Serial.println(VERSION); + #endif + // setup the reboot pin if needed + if (_reboot_pin > -1) { + #ifdef NODEMANAGER_DEBUG + Serial.print("REB P="); + Serial.println(_reboot_pin); + #endif + pinMode(_reboot_pin, OUTPUT); + digitalWrite(_reboot_pin, HIGH); + } + // print out MySensors' library capabilities + #ifdef NODEMANAGER_DEBUG + Serial.print(F("LIB V=")); + Serial.print(MYSENSORS_LIBRARY_VERSION); + Serial.print(F(" R=")); + Serial.print(MY_CAP_RADIO); + #ifdef MY_CAP_ENCR + Serial.print(F(" E=")); + Serial.print(MY_CAP_ENCR); + #endif + Serial.print(F(" T=")); + Serial.print(MY_CAP_TYPE); + Serial.print(F(" A=")); + Serial.print(MY_CAP_ARCH); + Serial.print(F(" S=")); + Serial.print(MY_CAP_SIGN); + Serial.print(F(" B=")); + Serial.println(MY_CAP_RXBUF); + #endif + // restore the sleep settings saved in the eeprom + if (_save_sleep_settings) _loadSleepSettings(); + // setup individual sensors + for (List::iterator itr = sensors.begin(); itr != sensors.end(); ++itr) { + Sensor* sensor = *itr; + // configure reporting interval + if (! sensor->isReportIntervalConfigured()) sensor->setReportIntervalSeconds(_report_interval_seconds); + // call each sensor's before() + sensor->before(); + } +} + +// present NodeManager and its sensors +void NodeManager::presentation() { + #ifdef NODEMANAGER_DEBUG + 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 each sensor + for (List::iterator itr = sensors.begin(); itr != sensors.end(); ++itr) { + Sensor* sensor = *itr; + // call each sensor's presentation() + if (_sleep_between_send > 0) sleep(_sleep_between_send); + sensor->presentation(); + } + #ifdef NODEMANAGER_DEBUG + Serial.println(F("READY")); + Serial.println(""); + #endif +} + + +// setup NodeManager +void NodeManager::setup() { + // retrieve and store isMetric from the controller + if (_get_controller_config) _is_metric = getControllerConfig().isMetric; + #ifdef NODEMANAGER_DEBUG + Serial.print(F("MY I=")); + Serial.print(getNodeId()); + Serial.print(F(" M=")); + Serial.println(_is_metric); + #endif + // run setup for all the registered sensors + for (List::iterator itr = sensors.begin(); itr != sensors.end(); ++itr) { + Sensor* sensor = *itr; + // call each sensor's setup() + sensor->setup(); + } + // setup the interrupt pins + setupInterrupts(); +} + +// run the main function for all the register sensors +void NodeManager::loop() { + // turn on the pin powering all the sensors + powerOn(); + // run loop for all the registered sensors + for (List::iterator itr = sensors.begin(); itr != sensors.end(); ++itr) { + Sensor* sensor = *itr; + if (_last_interrupt_pin != -1 && sensor->getInterruptPin() == _last_interrupt_pin) { + // if there was an interrupt for this sensor, call the sensor's interrupt() and then loop() + _message.clear(); + sensor->interrupt(); + sensor->loop(nullptr); + // reset the last interrupt pin + _last_interrupt_pin = -1; + } + else if (_last_interrupt_pin == -1) { + // if just at the end of a cycle, call the sensor's loop() + _message.clear(); + sensor->loop(nullptr); + } + } + // turn off the pin powering all the sensors + powerOff(); + // continue/start sleeping as requested + if (isSleepingNode()) _sleep(); +} + +// dispacth inbound messages +void NodeManager::receive(MyMessage &message) { + #ifdef NODEMANAGER_DEBUG + 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 + // dispatch the message to the registered sensor + Sensor* sensor = getSensorWithChild(message.sensor); + if (sensor != nullptr) { + // turn on the pin powering all the sensors + powerOn(); + // call the sensor's receive() + sensor->receive(message); + // turn off the pin powering all the sensors + powerOff(); + } +} + +// request and return the current timestamp from the controller +long NodeManager::getTimestamp() { + int retries = 3; + _timestamp = -1; + while (_timestamp == -1 && retries > 0) { + #ifdef NODEMANAGER_DEBUG + Serial.println(F("TIME")); + #endif + // request the time to the controller + requestTime(); + // keep asking every 1 second + sleepOrWait(1000); + retries--; + } + return _timestamp; +} + +// receive the time from the controller and save it +void NodeManager::receiveTime(unsigned long ts) { + _timestamp = ts; + #ifdef NODEMANAGER_DEBUG + Serial.print(F("TIME T=")); + Serial.print(_timestamp); + #endif +} + +// Send a hello message back to the controller +void NodeManager::hello() { + // do nothing, the request will be echoed back +} + +// reboot the board +void NodeManager::reboot() { + #ifndef MY_GATEWAY_ESP8266 + #ifdef NODEMANAGER_DEBUG + Serial.println(F("REBOOT")); + #endif + if (_reboot_pin > -1) { + // reboot the board through the reboot pin which is connected to RST by setting it to low + digitalWrite(_reboot_pin, LOW); + } else { + // Software reboot with watchdog timer. Enter Watchdog Configuration mode: + WDTCSR |= (1< -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_initial > -1) digitalWrite(INTERRUPT_PIN_1,_interrupt_1_initial); + // for non sleeping nodes, we need to handle the interrupt by ourselves + if (_status != SLEEP) attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN_1), _onInterrupt_1, _interrupt_1_mode); + } + if (_interrupt_2_mode != MODE_NOT_DEFINED) { + pinMode(INTERRUPT_PIN_2, INPUT); + if (_interrupt_2_initial > -1) digitalWrite(INTERRUPT_PIN_2,_interrupt_2_initial); + // for non sleeping nodes, we need to handle the interrupt by ourselves + if (_status != SLEEP) attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN_2), _onInterrupt_2, _interrupt_2_mode); + } + #ifdef NODEMANAGER_DEBUG + Serial.print(F("INT P=")); + Serial.print(INTERRUPT_PIN_1); + Serial.print(F(" M=")); + Serial.println(_interrupt_1_mode); + Serial.print(F("INT P=")); + Serial.print(INTERRUPT_PIN_2); + Serial.print(F(" M=")); + Serial.println(_interrupt_2_mode); + #endif +} + +// return the pin from which the last interrupt came +int NodeManager::getLastInterruptPin() { + return _last_interrupt_pin; +} + +// set the default interval in seconds all the sensors will report their measures +void NodeManager::setReportIntervalSeconds(int value) { + _report_interval_seconds = value; +} + +// set the default interval in minutes all the sensors will report their measures +void NodeManager::setReportIntervalMinutes(int value) { + _report_interval_seconds = value*60; +} + +// set the default interval in hours all the sensors will report their measures +void NodeManager::setReportIntervalHours(int value) { + _report_interval_seconds = value*60*60; +} + +// set the default interval in days all the sensors will report their measures +void NodeManager::setReportIntervalDays(int value) { + _report_interval_seconds = value*60*60*24; +} + +// if set and when the board is battery powered, sleep() is always called instead of wait() +void NodeManager::setSleepOrWait(bool value) { + _sleep_or_wait = value; +} + +// set which pin is connected to RST of the board to reboot the board when requested. If not set the software reboot is used instead (default: -1) +void NodeManager::setRebootPin(int value) { + _reboot_pin = value; +} + +// turn the ADC off so to save 0.2 mA +void NodeManager::setADCOff() { + #ifndef MY_GATEWAY_ESP8266 + // Disable the ADC by setting the ADEN bit (bit 7) to zero + ADCSRA = ADCSRA & B01111111; + // Disable the analog comparator by setting the ACD bit (bit 7) to one + ACSR = B10000000; + #endif +} + +// sleep if the node is a battery powered or wait if it is not for the given number of milliseconds +void NodeManager::sleepOrWait(long value) { + // if the node is sleeping, sleep-or-wait is enabled and we need to sleep for a decent amount of time, call sleep() otherwise wait() + if (isSleepingNode() && _sleep_or_wait && value > 200) sleep(value); + else wait(value); +} + +// return the next available child_id +int NodeManager::getAvailableChildId() { + for (int i = 1; i < 255; i++) { + if (i == CONFIGURATION_CHILD_ID || i == BATTERY_CHILD_ID || i == SIGNAL_CHILD_ID) continue; + Child* child = getChild(i); + if (child == nullptr) return i; + } + return 254; +} + +// handle an interrupt +void NodeManager::_onInterrupt_1() { + long now = millis(); + if ( (now - _last_interrupt_1 > _interrupt_min_delta) || (now < _last_interrupt_1) ) { + _last_interrupt_pin = INTERRUPT_PIN_1; + #ifdef NODEMANAGER_DEBUG + Serial.print(F("INT P=")); + Serial.println(INTERRUPT_PIN_1); + #endif + _last_interrupt_1 = now; + } +} +void NodeManager::_onInterrupt_2() { + long now = millis(); + if ( (now - _last_interrupt_2 > _interrupt_min_delta) || (now < _last_interrupt_2) ) { + _last_interrupt_pin = INTERRUPT_PIN_2; + #ifdef NODEMANAGER_DEBUG + Serial.print(F("INT P=")); + Serial.println(INTERRUPT_PIN_2); + #endif + _last_interrupt_2 = now; + } +} + +// send a message by providing the source child, type of the message and value +void NodeManager::sendMessage(int child_id, int type, int value) { + _message.clear(); + _message.set(value); + _sendMessage(child_id,type); +} +void NodeManager::sendMessage(int child_id, int type, float value) { + _message.clear(); + _message.set(value,2); + _sendMessage(child_id,type); +} +void NodeManager::sendMessage(int child_id, int type, double value) { + _message.clear(); + _message.set(value,4); + _sendMessage(child_id,type); +} +void NodeManager::sendMessage(int child_id, int type, const char* value) { + _message.clear(); + _message.set(value); + _sendMessage(child_id,type); +} + +// send a message to the network +void NodeManager::_sendMessage(int child_id, int type) { + // prepare the message + _message.setSensor(child_id); + _message.setType(type); + // 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); + #ifdef NODEMANAGER_DEBUG + 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); + } +} + +void NodeManager::setPowerManager(PowerManager& powerManager) { + _powerManager = &powerManager; +} + +// return the requested child +Child* NodeManager::getChild(int child_id) { + Sensor* sensor = getSensorWithChild(child_id); + if (sensor == nullptr) return nullptr; + return sensor->getChild(child_id); +} + +// return the sensor with the requested child +Sensor* NodeManager::getSensorWithChild(int child_id) { + for (List::iterator itr = sensors.begin(); itr != sensors.end(); ++itr) { + Sensor* sensor = *itr; + Child* child = sensor->getChild(child_id); + if (child != nullptr) return sensor; + } + return nullptr; +} + +// wrapper of smart sleep +void NodeManager::_sleep() { + #ifdef NODEMANAGER_DEBUG + Serial.print(F("SLEEP ")); + Serial.print(_sleep_time); + Serial.println(F("s")); + // print a new line to separate the different cycles + Serial.println(""); + #endif + // go to sleep + int interrupt = -1; + // 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_time*1000, 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; + #ifdef NODEMANAGER_DEBUG + Serial.print(F("INT 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) _status = AWAKE; + } + // coming out of sleep + #ifdef NODEMANAGER_DEBUG + Serial.println(F("AWAKE")); + #endif +} + +// present the service +void NodeManager::_present(int child_id, int type) { + #ifdef NODEMANAGER_DEBUG + 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); +} + +// load the configuration stored in the eeprom +void NodeManager::_loadSleepSettings() { + if (loadState(EEPROM_SLEEP_SAVED) == 1) { + // load sleep settings + int bit_1 = loadState(EEPROM_SLEEP_1); + int bit_2 = loadState(EEPROM_SLEEP_2); + int bit_3 = loadState(EEPROM_SLEEP_3); + _sleep_time = bit_3*255*255 + bit_2*255 + bit_1; + #ifdef NODEMANAGER_DEBUG + Serial.print(F("LOADSLP T=")); + Serial.println(_sleep_time); + #endif + } +} + +// save the configuration in the eeprom +void NodeManager::_saveSleepSettings() { + if (_sleep_time == 0) return; + // encode the sleep time in 3 bits + int bit_1, bit_2, bit_3 = 0; + bit_1 = _sleep_time; + if (bit_1 >= 255) { + bit_2 = (int)bit_1/255; + bit_1 = bit_1 - bit_2*255; + } + if (bit_2 >= 255) { + bit_3 = (int)bit_2/255; + bit_2 = bit_2 - bit_3*255; + } + // save the 3 bits + saveState(EEPROM_SLEEP_SAVED,1); + saveState(EEPROM_SLEEP_1,bit_1); + saveState(EEPROM_SLEEP_2,bit_2); + saveState(EEPROM_SLEEP_3,bit_3); +} diff --git a/README.md b/README.md index 0804f7af..4581a809 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ NodeManager is intended to take care on your behalf of all those common tasks a MySensors node has to accomplish, speeding up the development cycle of your projects. -NodeManager includes the following main components: +NodeManager includes the following main features: * Sleep manager: allows managing automatically the complexity behind battery-powered sensors spending most of their time sleeping * Power manager: allows powering on your sensors only while the node is awake @@ -15,263 +15,163 @@ NodeManager includes the following main components: * Allow waking up a sleeping node remotely at the end of a sleeping cycle * Allow powering on each connected sensor only while the node is awake to save battery * Report battery level periodically and automatically or on demand -* Report signal level periodically and automatically or on demand * Calculate battery level without requiring an additional pin and the resistors +* Report signal level periodically and automatically or on demand * Allow rebooting the board remotely * Provide out-of-the-box sensors personalities and automatically execute their main task at each cycle * Allow collecting and averaging multiple samples, tracking the last value and forcing periodic updates for any sensor * Provide buil-in capabilities to handle interrupt-based sensors +### Built-in sensors + +NodeManager provides built-in implementation of a number of sensors through ad-hoc classes. +To use a buil-in sensor: +* Install the required library if any +* Enable the corresponding module (uncomment it) in the main sketch +* Declare the sensor (uncomment it) in the main sketch + +Once created, the sensor will automatically present one or more child to the gateway and controller. +A list of buil-in sensors, module to enable, required dependencies and the number of child automatically created is presented below: + +Sensor Name |#Child | Module to enable | Description | Dependencies +--------------------|-------|-----------------------|---------------------------------------------------------------------------------------------------|---------------------------------------------------------- +SensorBattery | 1 | - | Built-in sensor for automatic battery reporting | - +SensorSignal | 1 | - | Built-in sensor for automatic signal level reporting | - +SensorConfiguration | 1 | - | Built-in sensor for OTA remote configuration of any registered sensor | - +SensorAnalogInput | 1 | MODULE_ANALOG_INPUT | Generic analog sensor, return a pin's analog value or its percentage | - +SensorLDR | 1 | MODULE_ANALOG_INPUT | LDR sensor, return the light level of an attached light resistor in percentage | - +SensorRain | 1 | MODULE_ANALOG_INPUT | Rain sensor, return the percentage of rain from an attached analog sensor | - +SensorSoilMoisture | 1 | MODULE_ANALOG_INPUT | Soil moisture sensor, return the percentage of moisture from an attached analog sensor | - +SensorThermistor | 1 | MODULE_THERMISTOR | Thermistor sensor, return the temperature based on the attached thermistor | - +SensorML8511 | 1 | MODULE_ML8511 | ML8511 sensor, return UV intensity | - +SensorACS712 | 1 | MODULE_ACS712 | ACS712 sensor, measure the current going through the attached module | - +SensorDigitalInput | 1 | MODULE_DIGITAL_INPUT | Generic digital sensor, return a pin's digital value | - +SensorDigitalOutput | 1 | MODULE_DIGITAL_OUTPUT | Generic digital output sensor, allows setting the digital output of a pin to the requested value | - +SensorRelay | 1 | MODULE_DIGITAL_OUTPUT | Relay sensor, allows activating the relay | - +SensorLatchingRelay | 1 | MODULE_DIGITAL_OUTPUT | Latching Relay sensor, allows activating the relay with a pulse | - +SensorDHT11 | 2 | MODULE_DHT | DHT11 sensor, return temperature/humidity based on the attached DHT sensor | https://github.com/mysensors/MySensorsArduinoExamples/tree/master/libraries/DHT +SensorDHT22 | 2 | MODULE_DHT | DHT22 sensor, return temperature/humidity based on the attached DHT sensor | https://github.com/mysensors/MySensorsArduinoExamples/tree/master/libraries/DHT +SensorSHT21 | 2 | MODULE_SHT21 | SHT21 sensor, return temperature/humidity based on the attached SHT21 sensor | https://github.com/SodaqMoja/Sodaq_SHT2x +SensorHTU21D | 2 | MODULE_SHT21 | HTU21D sensor, return temperature/humidity based on the attached HTU21D sensor | https://github.com/SodaqMoja/Sodaq_SHT2x +SensorSwitch | 1 | MODULE_SWITCH | Generic switch, wake up the board when a pin changes status | - +SensorDoor | 1 | MODULE_SWITCH | Door sensor, wake up the board and report when an attached magnetic sensor has been opened/closed | - +SensorMotion | 1 | MODULE_SWITCH | Motion sensor, wake up the board and report when an attached PIR has triggered | - +SensorDs18b20 | 1+ | MODULE_DS18B20 | DS18B20 sensor, return the temperature based on the attached sensor | https://github.com/milesburton/Arduino-Temperature-Control-Library +SensorBH1750 | 1 | MODULE_BH1750 | BH1750 sensor, return light level in lux | https://github.com/claws/BH1750 +SensorMLX90614 | 2 | MODULE_MLX90614 | MLX90614 contactless temperature sensor, return ambient and object temperature | https://github.com/adafruit/Adafruit-MLX90614-Library +SensorBME280 | 4 | MODULE_BME280 | BME280 sensor, return temperature/humidity/pressure based on the attached BME280 sensor | https://github.com/adafruit/Adafruit_BME280_Library +SensorBMP085 | 3 | MODULE_BMP085 | BMP085/BMP180 sensor, return temperature and pressure | https://github.com/adafruit/Adafruit-BMP085-Library +SensorBMP280 | 3 | MODULE_BMP280 | BMP280 sensor, return temperature/pressure based on the attached BMP280 sensor | https://github.com/adafruit/Adafruit_BMP280_Library +SensorSonoff | 1 | MODULE_SONOFF | Sonoff wireless smart switch | https://github.com/thomasfredericks/Bounce2 +SensorHCSR04 | 1 | MODULE_HCSR04 | HC-SR04 sensor, return the distance between the sensor and an object | https://github.com/mysensors/MySensorsArduinoExamples/tree/master/libraries/NewPing +SensorMCP9808 | 1 | MODULE_MCP9808 | MCP9808 sensor, measure the temperature through the attached module | https://github.com/adafruit/Adafruit_MCP9808_Library +SensorMQ | 1 | MODULE_MQ | MQ sensor, return ppm of the target gas | - +SensorMHZ19 | 1 | MODULE_MHZ19 | MH-Z19 CO2 sensor via UART (SoftwareSerial, default on pins 6(Rx) and 7(Tx) | - +SensorAM2320 | 2 | MODULE_AM2320 | AM2320 sensors, return temperature/humidity based on the attached AM2320 sensor | https://github.com/thakshak/AM2320 +SensorTSL2561 | 1 | MODULE_TSL2561 | TSL2561 sensor, return light in lux | https://github.com/adafruit/TSL2561-Arduino-Library +SensorPT100 | 1 | MODULE_PT100 | DFRobot Driver high temperature sensor, return the temperature from the attached PT100 sensor | - +SensorDimmer | 1 | MODULE_DIMMER | Generic dimmer sensor used to drive a pwm output | - +SensorRainGauge | 1 | MODULE_PULSE_METER | Rain gauge sensor | - +SensorPowerMeter | 1 | MODULE_PULSE_METER | Power meter pulse sensor | - +SensorWaterMeter | 1 | MODULE_PULSE_METER | Water meter pulse sensor | - +SensorPlantowerPMS | 3 | MODULE_PMS | Plantower PMS particulate matter sensors (reporting PM<=1.0, PM<=2.5 and PM<=10.0 in µg/m³) | https://github.com/fu-hsi/pms + ## Installation + * Download the package or clone the git repository from https://github.com/mysensors/NodeManager -* Open the provided sketch and save it under a different name -* Open `config.h` and customize both MySensors configuration and NodeManager global settings -* Register your sensors in the sketch file -* Upload the sketch to your arduino board +* Open the NodeManager.ino sketch and save it under a different name +* Configure you sensors and upload the sketch to your arduino board Please note NodeManager cannot be used as an arduino library since requires access to your MySensors configuration directives, hence its files have to be placed into the same directory of your sketch. +### Installing the dependencies + +Some of the sensors rely on third party libraries. Those libraries are not included within NodeManager and have to be installed from the Arduino IDE Library Manager (Sketch -> Include Library -> Manager Libraries) or manually. +You need to install the library ONLY if you are planning to enable to use the sensor. + ### Upgrade -* Download the package -* Replace the NodeManager.cpp and NodeManager.h of your project with those just downloaded -* Review the release notes in case there is any manual change required to the existing sketch or config.h file + +* Download the latest version of NodeManager +* Replace the NodeManagerLibrary.ino and NodeManagerLibrary.h of your project with those just downloaded +* Review the release notes in case there is any manual change required to the main sketch ## Configuration -NodeManager configuration includes compile-time configuration directives (which can be set in config.h), runtime global and per-sensor configuration settings (which can be set in your sketch). -### Setup MySensors -Since NodeManager has to communicate with the MySensors gateway on your behalf, it has to know how to do it. Place on top of the `config.h` file all the MySensors typical directives you are used to set on top of your sketch so both your sketch AND NodeManager will be able to share the same configuration. For example: -~~~c -/********************************** - * Sketch configuration - */ +Configuring a sketch with is using NodeManager requires a few steps. All the configuration directives are located within the main sketch. -#define SKETCH_NAME "NodeManager" -#define SKETCH_VERSION "1.0" +### MySensors configuration -/********************************** - * MySensors node configuration - */ +Since NodeManager has to communicate with the MySensors network on your behalf, it has to know how to do it. On top of the main sketch you will find the typical MySensors directives you are used to which can be customized to configure the board to act as a MySensors node or a MySensors gateway. +Please note you don't necessarily need a NodeManager gateway to interact with a NodeManager node. A NodeManager node is fully compatible with any existing gateway you are currently operating with. -// General settings -#define MY_BAUD_RATE 9600 -//#define MY_DEBUG -//#define MY_NODE_ID 100 -//#define MY_SMART_SLEEP_WAIT_DURATION_MS 500 +### NodeManager configuration -// NRF24 radio settings -#define MY_RADIO_NRF24 -//#define MY_RF24_ENABLE_ENCRYPTION -//#define MY_RF24_CHANNEL 76 -//#define MY_RF24_PA_LEVEL RF24_PA_HIGH -//#define MY_DEBUG_VERBOSE_RF24 -//#define MY_RF24_DATARATE RF24_250KBPS - -// RFM69 radio settings -//#define MY_RADIO_RFM69 -//#define MY_RFM69_FREQUENCY RF69_868MHZ -//#define MY_RFM69_FREQUENCY RFM69_868MHZ -//#define MY_IS_RFM69HW -//#define MY_RFM69_NEW_DRIVER -//#define MY_RFM69_ENABLE_ENCRYPTION -//#define MY_RFM69_NETWORKID 100 -//#define MY_DEBUG_VERBOSE_RFM69 -//#define MY_RF69_IRQ_PIN D1 -//#define MY_RF69_IRQ_NUM MY_RF69_IRQ_PIN -//#define MY_RF69_SPI_CS D2 -//#define MY_RFM69_ATC_MODE_DISABLED - -// RS485 serial transport settings -//#define MY_RS485 -//#define MY_RS485_BAUD_RATE 9600 -//#define MY_RS485_DE_PIN 2 -//#define MY_RS485_MAX_MESSAGE_LENGTH 40 -//#define MY_RS485_HWSERIAL Serial1 - -// Message signing settings -//#define MY_SIGNING_SOFT -//#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 -//#define MY_SIGNING_REQUEST_SIGNATURES -//#define MY_SIGNING_ATSHA204 - -// OTA Firmware update settings -//#define MY_OTA_FIRMWARE_FEATURE -//#define OTA_WAIT_PERIOD 300 -//#define FIRMWARE_MAX_REQUESTS 2 -//#define MY_OTA_RETRY 2 +The next step is to enable NodeManager's modules required for your sensors. When a module is enabled, the required library will be loaded and the corresponding sensor will be made available. To enable it, uncomment the line. Enabled only what you need to ensure enough storage is left for your custom code. -/********************************** - * MySensors gateway configuration - */ -// Common gateway settings -//#define MY_REPEATER_FEATURE - -// Serial gateway settings -//#define MY_GATEWAY_SERIAL - -// Ethernet gateway settings -//#define MY_GATEWAY_W5100 - -// ESP8266 gateway settings -//#define MY_GATEWAY_ESP8266 -//#define MY_ESP8266_SSID "" -//#define MY_ESP8266_PASSWORD "" - -// Gateway networking settings -//#define MY_IP_ADDRESS 192,168,178,87 -//#define MY_IP_GATEWAY_ADDRESS 192,168,178,1 -//#define MY_IP_SUBNET_ADDRESS 255,255,255,0 -//#define MY_PORT 5003 -//#define MY_GATEWAY_MAX_CLIENTS 2 -//#define MY_USE_UDP - -// Gateway MQTT settings -//#define MY_GATEWAY_MQTT_CLIENT -//#define MY_CONTROLLER_IP_ADDRESS 192, 168, 178, 68 -//#define MY_PORT 1883 -//#define MY_MQTT_USER "username" -//#define MY_MQTT_PASSWORD "password" -//#define MY_MQTT_CLIENT_ID "mysensors-1" -//#define MY_MQTT_PUBLISH_TOPIC_PREFIX "mygateway1-out" -//#define MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "mygateway1-in" - -// Gateway inclusion mode -//#define MY_INCLUSION_MODE_FEATURE -//#define MY_INCLUSION_BUTTON_FEATURE -//#define MY_INCLUSION_MODE_DURATION 60 -//#define MY_DEFAULT_LED_BLINK_PERIOD 300 - -// Gateway Leds settings -//#define MY_DEFAULT_ERR_LED_PIN 4 -//#define MY_DEFAULT_RX_LED_PIN 5 -//#define MY_DEFAULT_TX_LED_PIN 6 +### Add your sensors + +Find in the main sketch `Add your sensors below` and add your sensors to NodeManager. To add a sensor, just create an instance of the class, passing it `node` as an argument and an optional pin. + +~~~c +SensorThermistor thermistor(node,A0); +SensorSHT21 sht21(node); ~~~ -### Enable/Disable NodeManager's modules +The sensor will be then registered automatically with NodeManager which will take care of it all along its lifecycle. Please ensure the corresponding module has been previously enabled for a successful compilation of the code. +NodeManager will assign a child id automatically, present each sensor for you to the controller, query each sensor and report the measure back to the gateway/controller. For actuators (e.g. relays) those can be triggered by sending a `REQ` message with the expected type to their assigned child id. -The next step is to enable NodeManager's additional functionalities and the modules required for your sensors. The directives in the `config.h` file control which module/library/functionality will be made available to your sketch. Enable (e.g. set to 1) only what you need to ensure enough storage is left to your custom code. +### Configuring your sensors -~~~c -/*********************************** - * NodeManager configuration - */ +NodeManager and all the sensors can be configured from within `before()` in the main sketch. Find `Configure your sensors below` to customize the behavior of any sensor by calling one of the functions available. -// if enabled, enable debug messages on serial port -#define DEBUG 1 - -// if enabled, enable the capability to power on sensors with the arduino's pins to save battery while sleeping -#define POWER_MANAGER 1 -// if enabled, will load the battery manager library to allow the battery level to be reported automatically or on demand -#define BATTERY_MANAGER 1 -// if enabled, allow modifying the configuration remotely by interacting with the configuration child id -#define REMOTE_CONFIGURATION 1 -// if enabled, persist the remote configuration settings on EEPROM -#define PERSIST 0 -// if enabled, a battery sensor will be created at BATTERY_CHILD_ID and will report vcc voltage together with the battery level percentage -#define BATTERY_SENSOR 1 -// if enabled, a signal sensor will be created at RSSI_CHILD_ID (202 by default) and will report the signal quality of the transport layer -#define SIGNAL_SENSOR 0 -// if enabled, send a SLEEPING and AWAKE service messages just before entering and just after leaving a sleep cycle and STARTED when starting/rebooting -#define SERVICE_MESSAGES 0 - -// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN, SENSOR_SOIL_MOISTURE -#define MODULE_ANALOG_INPUT 1 -// Enable this module to use one of the following sensors: SENSOR_DIGITAL_INPUT -#define MODULE_DIGITAL_INPUT 1 -// Enable this module to use one of the following sensors: SENSOR_DIGITAL_OUTPUT, SENSOR_RELAY, SENSOR_LATCHING_RELAY -#define MODULE_DIGITAL_OUTPUT 1 -// Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22 -#define MODULE_DHT 0 -// Enable this module to use one of the following sensors: SENSOR_SHT21, SENSOR_HTU21D -#define MODULE_SHT21 0 -// Enable this module to use one of the following sensors: SENSOR_SWITCH, SENSOR_DOOR, SENSOR_MOTION -#define MODULE_SWITCH 0 -// Enable this module to use one of the following sensors: SENSOR_DS18B20 -#define MODULE_DS18B20 0 -// Enable this module to use one of the following sensors: SENSOR_BH1750 -#define MODULE_BH1750 0 -// Enable this module to use one of the following sensors: SENSOR_MLX90614 -#define MODULE_MLX90614 0 -// Enable this module to use one of the following sensors: SENSOR_BME280 -#define MODULE_BME280 0 -// Enable this module to use one of the following sensors: SENSOR_SONOFF -#define MODULE_SONOFF 0 -// Enable this module to use one of the following sensors: SENSOR_BMP085 -#define MODULE_BMP085 0 -// Enable this module to use one of the following sensors: SENSOR_HCSR04 -#define MODULE_HCSR04 0 -// Enable this module to use one of the following sensors: SENSOR_MCP9808 -#define MODULE_MCP9808 0 -// Enable this module to use one of the following sensors: SENSOR_MQ -#define MODULE_MQ 0 -// Enable this module to use one of the following sensors: SENSOR_MHZ19 -#define MODULE_MHZ19 0 -// Enable this module to use one of the following sensors: SENSOR_AM2320 -#define MODULE_AM2320 0 -// Enable this module to use one of the following sensors: SENSOR_TSL2561 -#define MODULE_TSL2561 0 -// Enable this module to use one of the following sensors: SENSOR_PT100 -#define MODULE_PT100 0 -// Enable this module to use one of the following sensors: SENSOR_BMP280 -#define MODULE_BMP280 0 -// Enable this module to use one of the following sensors: SENSOR_DIMMER -#define MODULE_DIMMER 0 -// Enable this module to use one of the following sensors: SENSOR_RAIN_GAUGE, SENSOR_POWER_METER, SENSOR_WATER_METER -#define MODULE_PULSE_METER 0 +~~~c +// report measures of every attached sensors every 10 minutes +node.setReportIntervalMinutes(10); +// set the node to sleep in 5 minutes cycles +node.setSleepMinutes(5); +// report battery level every 10 minutes +battery.setReportIntervalMinutes(10); +// set an offset to -1 to a thermistor sensor +thermistor.setOffset(-1); +// Change the id of a the first child of a sht21 sensor +sht21.children.get(1)->child_id = 5; +// power all the nodes through dedicated pins +node.setPowerManager(power); ~~~ -### Installing the dependencies +If not instructed differently, the node will stay awake and all the sensors will report every 10 minutes, battery level and signal level will be automatically reported every 60 minutes (if the corresponding sensors have been added). + +Please note, if you configure a sleep cycle, this may have an impact on the reporting interval since the sensor will be able to report its measures ONLY when awake. For example if you set a report interval of 5 minutes and a sleep cycle of 10 minutes, the sensors will report every 10 minutes. + +## Running the node + +Once finished configuring your node, upload your sketch to your arduino board as you are used to. + +Check your gateway's logs to ensure the node is working as expected. You should see the node presenting itself, presenting all the registered sensors and reporting new measures at the configured reporting interval. +When `DEBUG` is enabled, detailed information will be available through the serial port. Remember to disable debug once the tests have been completed to save additional storage. + +## Communicate with the sensors + +You can interact with each registered sensor by sending to the child id a `REQ` command (or a `SET` for output sensors like relays). For example to request the temperature to node_id 254 and child_id 1: + +`254;1;2;0;0;` + +To activate a relay connected to the same node, child_id 100 we need to send a `SET` command with payload set to 1: + +`254;100;1;0;2;1` -Some of the modules above rely on third party libraries. Those libraries are not included within NodeManager and have to be installed from the Arduino IDE Library Manager (Sketch -> Include Library -> Manager Libraries) or manually. You need to install the library ONLY if the module is enabled: +No need to implement anything on your side since for built-in sensors this is handled automatically. -Module | Required Library - ------------- | ------------- -MODULE_SHT21 | https://github.com/SodaqMoja/Sodaq_SHT2x -MODULE_DHT | https://github.com/mysensors/MySensorsArduinoExamples/tree/master/libraries/DHT -MODULE_DS18B20 | https://github.com/milesburton/Arduino-Temperature-Control-Library -MODULE_BH1750 | https://github.com/claws/BH1750 -MODULE_MLX90614 | https://github.com/adafruit/Adafruit-MLX90614-Library -MODULE_BME280 | https://github.com/adafruit/Adafruit_BME280_Library -MODULE_SONOFF | https://github.com/thomasfredericks/Bounce2 -MODULE_BMP085 | https://github.com/adafruit/Adafruit-BMP085-Library -MODULE_HCSR04 | https://github.com/mysensors/MySensorsArduinoExamples/tree/master/libraries/NewPing -MODULE_MCP9808 | https://github.com/adafruit/Adafruit_MCP9808_Library -MODULE_AM2320 | https://github.com/thakshak/AM2320 -MODULE_TSL2561 | https://github.com/adafruit/TSL2561-Arduino-Library -MODULE_BMP280 | https://github.com/adafruit/Adafruit_BMP280_Library +## API -### Configure NodeManager +You can interact with each class provided by NodeManager through a set of API functions. -The next step is to configure NodeManager with settings which will instruct how the node should behave. To do so, go to the main sketch, inside the `before()` function and add call one or more of the functions below just before registering your sensors. The following methods are exposed for your convenience and can be called on the `nodeManager` object already created for you: +### NodeManager API ~~~c - // [10] send the same service message multiple times (default: 1) + // [10] send the same message multiple times (default: 1) void setRetries(int value); int getRetries(); - #if BATTERY_MANAGER == 1 - // [11] the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) - void setBatteryMin(float value); - // [12] the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) - void setBatteryMax(float value); - // [14] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60 minutes) - void setBatteryReportMinutes(int value); - // [40] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60 minutes) - void setBatteryReportSeconds(int value); - // [41] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60 minutes) - void setBatteryReportHours(int value); - // [42] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60 minutes) - void setBatteryReportDays(int value); - // [15] if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) - void setBatteryInternalVcc(bool value); - // [16] if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) - void setBatteryPin(int value); - // [17] if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075) - void setBatteryVoltsPerBit(float value); - // [18] If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: true) - void setBatteryReportWithInterrupt(bool value); - // [2] Send a battery level report to the controller - void batteryReport(); - #endif // [3] set the duration (in seconds) of a sleep cycle void setSleepSeconds(int value); long getSleepSeconds(); @@ -290,27 +190,14 @@ The next step is to configure NodeManager with settings which will instruct how // [20] optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) void setSleepBetweenSend(int value); int getSleepBetweenSend(); - // register a built-in sensor - int registerSensor(int sensor_type, int pin = -1, int child_id = -1); - // register a custom sensor - int registerSensor(Sensor* sensor); - // [26] un-register a sensor - void unRegisterSensor(int sensor_index); - // return a sensor by its index - Sensor* get(int sensor_index); - Sensor* getSensor(int sensor_index); - // assign a different child id to a sensor - bool renameSensor(int old_child_id, int new_child_id); - #if POWER_MANAGER == 1 - // to save battery the sensor can be optionally connected to two pins which will act as vcc and ground and activated on demand - void setPowerPins(int ground_pin, int vcc_pin, int wait_time = 50); - // [23] if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) - void setAutoPowerPins(bool value); - // [24] manually turn the power on - void powerOn(); - // [25] manually turn the power off - void powerOff(); - #endif + // register a sensor + void registerSensor(Sensor* sensor); + // to save battery the sensor can be optionally connected to two pins which will act as vcc and ground and activated on demand + void setPowerPins(int ground_pin, int vcc_pin, int wait_time = 50); + // [24] manually turn the power on + void powerOn(); + // [25] manually turn the power off + void powerOff(); // [21] set this to true if you want destination node to send ack back to this node (default: false) void setAck(bool value); bool getAck(); @@ -329,14 +216,10 @@ The next step is to configure NodeManager with settings which will instruct how void hello(); // [6] reboot the board void reboot(); - // [8] send NodeManager's the version back to the controller - void version(); // [7] clear the EEPROM void clearEeprom(); // [9] wake up the board void wakeup(); - // process a remote request - void process(Request & request); // return the value stored at the requested index from the EEPROM int loadFromMemory(int index); // [27] save the given index of the EEPROM the provided value @@ -363,153 +246,19 @@ The next step is to configure NodeManager with settings which will instruct how void setRebootPin(int value); // [32] turn the ADC off so to save 0.2 mA void setADCOff(); - #if SIGNAL_SENSOR == 1 && defined(MY_SIGNAL_REPORT_ENABLED) - // [33] How frequenly to send a signal report to the controller (default: 60 minutes) - void setSignalReportMinutes(int value); - // [43] How frequenly to send a signal report to the controller (default: 60 minutes) - void setSignalReportSeconds(int value); - // [44] How frequenly to send a signal report to the controller (default: 60 minutes) - void setSignalReportHours(int value); - // [45] How frequenly to send a signal report to the controller (default: 60 minutes) - void setSignalReportDays(int value); - // [34] define which signal report to send. Possible values are SR_UPLINK_QUALITY, SR_TX_POWER_LEVEL, SR_TX_POWER_PERCENT, SR_TX_RSSI, SR_RX_RSSI, SR_TX_SNR, SR_RX_SNR (default: SR_RX_RSSI) - void setSignalCommand(int value); - // [35] report the signal level to the controller - void signalReport(); - #endif -~~~ - -### Set reporting intervals and sleeping cycles - -If not instructed differently, the node will stay awake and all the sensors will report every 10 minutes, battery level and signal level will be automatically reported every 60 minutes. To change those settings, you can call the following functions on the nodeManager object: - -Function | Description ------------- | ------------- -setSleepSeconds(), setSleepMinutes(), setSleepHours(), setSleepDays() | the time interval the node will spend in a (smart) sleep cycle -setReportIntervalSeconds(), setReportIntervalMinutes(), setReportIntervalHours(), setReportIntervalDays() | the time interval the node will report the measures of all the attached sensors -setBatteryReportSeconds(), setBatteryReportMinutes(), setBatteryReportHours(), setBatteryReportDays() | the time interval the node will report the battery level -setSignalReportSeconds(), setSignalReportMinutes(), setSignalReportHours(), setSignalReportDays() | the time interval the node will report the radio signal level - -For example, to put the node to sleep in cycles of 10 minutes: - -~~~c - nodeManager.setSleepMinutes(10); -~~~ - -If you need every sensor to report at a different time interval, you can call `setBatteryReportSeconds(), setBatteryReportMinutes(), setBatteryReportHours(), setBatteryReportDays()` on the sensor's object. For example to have a DHT sensor reporting every 60 seconds while all the other sensors every 20 minutes: -~~~c -int id = nodeManager.registerSensor(SENSOR_DHT22,6); -SensorDHT* dht = (SensorDHT*)nodeManager.get(id); -dht->setReportIntervalSeconds(60); -nodeManager.setReportIntervalMinutes(20); + // [30] if set save the sleep settings in memory, also when changed remotely (default: false) + void setSaveSleepSettings(bool value); ~~~ -Please note, if you configure a sleep cycle, this may have an impact on the reporting interval since the sensor will be able to report its measures ONLY when awake. For example if you set a report interval of 5 minutes and a sleep cycle of 10 minutes, the sensors will report every 10 minutes. +### Sensor API -### Register your sensors -Once configured the node, it is time to tell NodeManager which sensors are attached to the board and where. In your sketch, inside the `before()` function and just before calling `nodeManager.before()`, you can register your sensors against NodeManager. The following built-in sensor types are available. Remember the corresponding module should be enabled in `config.h` for a successful compilation: - -Sensor type | Description - ------------- | ------------- -SENSOR_ANALOG_INPUT | Generic analog sensor, return a pin's analog value or its percentage -SENSOR_LDR | LDR sensor, return the light level of an attached light resistor in percentage -SENSOR_THERMISTOR | Thermistor sensor, return the temperature based on the attached thermistor -SENSOR_DIGITAL_INPUT | Generic digital sensor, return a pin's digital value -SENSOR_DIGITAL_OUTPUT | Generic digital output sensor, allows setting the digital output of a pin to the requested value -SENSOR_RELAY | Relay sensor, allows activating the relay -SENSOR_LATCHING_RELAY| Latching Relay sensor, allows activating the relay with a pulse -SENSOR_DHT11 | DHT11 sensor, return temperature/humidity based on the attached DHT sensor -SENSOR_DHT22 | DHT22 sensor, return temperature/humidity based on the attached DHT sensor -SENSOR_SHT21 | SHT21 sensor, return temperature/humidity based on the attached SHT21 sensor -SENSOR_SWITCH | Generic switch, wake up the board when a pin changes status -SENSOR_DOOR | Door sensor, wake up the board and report when an attached magnetic sensor has been opened/closed -SENSOR_MOTION | Motion sensor, wake up the board and report when an attached PIR has triggered -SENSOR_DS18B20 | DS18B20 sensor, return the temperature based on the attached sensor -SENSOR_HTU21D | HTU21D sensor, return temperature/humidity based on the attached HTU21D sensor -SENSOR_BH1750 | BH1750 sensor, return light level in lux -SENSOR_MLX90614 | MLX90614 contactless temperature sensor, return ambient and object temperature -SENSOR_BME280 | BME280 sensor, return temperature/humidity/pressure based on the attached BME280 sensor -SENSOR_MQ | MQ sensor, return ppm of the target gas -SENSOR_ML8511 | ML8511 sensor, return UV intensity -SENSOR_SONOFF | Sonoff wireless smart switch -SENSOR_BMP085 | BMP085/BMP180 sensor, return temperature and pressure -SENSOR_HCSR04 | HC-SR04 sensor, return the distance between the sensor and an object -SENSOR_ACS712 | ACS712 sensor, measure the current going through the attached module -SENSOR_MCP9808 | MCP9808 sensor, measure the temperature through the attached module -SENSOR_RAIN_GAUGE | Rain gauge sensor -SENSOR_RAIN | Rain sensor, return the percentage of rain from an attached analog sensor -SENSOR_SOIL_MOISTURE | Soil moisture sensor, return the percentage of moisture from an attached analog sensor -SENSOR_MHZ19 | MH-Z19 CO2 sensor via UART (SoftwareSerial, default on pins 6(Rx) and 7(Tx) -SENSOR_TSL2561 | TSL2561 sensor, return light in lux -SENSOR_AM2320 | AM2320 sensors, return temperature/humidity based on the attached AM2320 sensor -SENSOR_PT100 | High temperature sensor associated with DFRobot Driver, return the temperature in C° from the attached PT100 sensor -SENSOR_BMP280 | BMP280 sensor, return temperature/pressure based on the attached BMP280 sensor -SENSOR_DIMMER | Generic dimmer sensor used to drive a pwm output -SENSOR_POWER_METER | Power meter pulse sensor -SENSOR_WATER_METER | Water meter pulse sensor - -To register a sensor simply call the NodeManager instance with the sensory type and the pin the sensor is conncted to and optionally a child id. For example: -~~~c - nodeManager.registerSensor(SENSOR_THERMISTOR,A2); - nodeManager.registerSensor(SENSOR_DOOR,3,1); -~~~ - -Once registered, your job is done. NodeManager will assign a child id automatically if not instructed differently, present each sensor for you to the controller, query each sensor and report the measure back to the gateway/controller. For actuators (e.g. relays) those can be triggered by sending a `REQ` message with the expected type to their assigned child id. - -When called, registerSensor returns the child_id of the sensor so you will be able to retrieve it later if needed. Please note for sensors creating multiple child IDs (like a DHT sensor which creates a temperature and humidity sensor with different IDs), the last id is returned. - -#### Creating a custom sensor - -If you want to create a custom sensor and register it with NodeManager so it can take care of all the common tasks, you can create an inline class inheriting from `Sensor` or other subclasses and implement the following methods: -~~~c - // define what to do during before() to setup the sensor - void onBefore(); - // define what to do during setup() by executing the sensor's main task - void onSetup(); - // define what to do during loop() by executing the sensor's main task - void onLoop(); - // define what to do during receive() when the sensor receives a message - void onReceive(const MyMessage & message); - // define what to do when receiving a remote configuration message - void onProcess(Request & request); - // define what to do when receiving an interrupt - void onInterrupt(); -~~~ - -You can then instantiate your newly created class and register it with NodeManager: -~~~c - nodeManager.registerSensor(new SensorCustom(&nodeManager,child_id, pin)); -~~~ - -### Configuring the sensors -Each built-in sensor class comes with reasonable default settings. In case you want/need to customize any of those settings, after having registered the sensor, you can retrieve it back and call set functions common to all the sensors or specific for a given class. - -To do so, use `nodeManager.getSensor(child_id)` which will return a pointer to the sensor. Remeber to cast it to the right class before calling their functions. For example: - -~~~c - SensorLatchingRelay* relay = (SensorLatchingRelay*) nodeManager.getSensor(2); - relay->setPulseWidth(50); -~~~ - - -#### Sensor's general configuration - -The following methods are available for all the sensors and can be called on the object reference as per the example above: +The following methods are available for all the sensors: ~~~c + // return the name of the sensor + char* getName(); // [1] where the sensor is attached to (default: not set) void setPin(int value); int getPin(); - // [2] child_id of this sensor (default: not set) - void setChildId(int value); - int getChildId(); - // presentation of this sensor (default: S_CUSTOM) - void setPresentation(int value); - int getPresentation(); - // [3] type of this sensor (default: V_CUSTOM) - void setType(int value); - int getType(); - // [4] description of the sensor (default: '') - void setDescription(char *value); // [5] For some sensors, the measurement can be queried multiple times and an average is returned (default: 1) void setSamples(int value); // [6] If more then one sample has to be taken, set the interval in milliseconds between measurements (default: 0) @@ -518,29 +267,12 @@ The following methods are available for all the sensors and can be called on the void setTrackLastValue(bool value); // [9] if track last value is enabled, force to send an update after the configured number of minutes void setForceUpdateMinutes(int value); - // [19] if track last value is enabled, force to send an update after the configured number of hours - void setForceUpdateHours(int value); - // [10] the value type of this sensor (default: TYPE_INTEGER) - void setValueType(int value); - int getValueType(); - // [11] for float values, set the float precision (default: 2) - void setFloatPrecision(int value); - // [21] for double values, set the double precision (default: 4) - void setDoublePrecision(int value); - #if POWER_MANAGER == 1 - // to save battery the sensor can be optionally connected to two pins which will act as vcc and ground and activated on demand - void setPowerPins(int ground_pin, int vcc_pin, int wait_time = 50); - // [12] if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) - void setAutoPowerPins(bool value); - // [13] manually turn the power on - void powerOn(); - // [14] manually turn the power off - void powerOff(); - #endif - // get the latest recorded value from the sensor - int getValueInt(); - float getValueFloat(); - char* getValueString(); + // to save battery the sensor can be optionally connected to two pins which will act as vcc and ground and activated on demand + void setPowerPins(int ground_pin, int vcc_pin, int wait_time = 50); + // [13] manually turn the power on + void powerOn(); + // [14] manually turn the power off + void powerOff(); // [17] After how many minutes the sensor will report back its measure (default: 10 minutes) void setReportIntervalSeconds(int value); // [16] After how many minutes the sensor will report back its measure (default: 10 minutes) @@ -551,17 +283,39 @@ The following methods are available for all the sensors and can be called on the void setReportIntervalDays(int value); // return true if the report interval has been already configured bool isReportIntervalConfigured(); - // process a remote request - void process(Request & request); // return the pin the interrupt is attached to int getInterruptPin(); // listen for interrupts on the given pin so interrupt() will be called when occurring void setInterrupt(int pin, int mode, int initial); + // set a previously configured PowerManager to the sensor so to powering it up with custom pins + void setPowerManager(const PowerManager& powerManager); + // list of configured child + List children; ~~~ -#### Sensor's specific configuration +### Built-in sensors API + +Each sensor class exposes additional methods. + +* SensorBattery +~~~c + // [102] the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) + void setMinVoltage(float value); + // [103] the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) + void setMaxVoltage(float value); + // [104] if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) + void setBatteryInternalVcc(bool value); + // [105] if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) + void setBatteryPin(int value); + // [106] if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075) + void setBatteryVoltsPerBit(float value); +~~~ -Each sensor class can expose additional methods. +* SensorSignal +~~~c + // [101] define which signal report to send. Possible values are SR_UPLINK_QUALITY, SR_TX_POWER_LEVEL, SR_TX_POWER_PERCENT, SR_TX_RSSI, SR_RX_RSSI, SR_TX_SNR, SR_RX_SNR (default: SR_RX_RSSI) + void setSignalCommand(int value); +~~~ * SensorAnalogInput / SensorLDR / SensorRain / SensorSoilMoisture ~~~c @@ -591,32 +345,6 @@ Each sensor class can expose additional methods. void setOffset(float value); ~~~ -* SensorMQ -~~~c - // [101] define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); - void setTargetGas(int value); - // [102] define the load resistance on the board, in kilo ohms (default: 1); - void setRlValue(float value); - // [103] define the Ro resistance on the board (default: 10000); - void setRoValue(float value); - // [104] Sensor resistance in clean air (default: 9.83); - void setCleanAirFactor(float value); - // [105] define how many samples you are going to take in the calibration phase (default: 50); - void setCalibrationSampleTimes(int value); - // [106] define the time interal(in milisecond) between each samples in the cablibration phase (default: 500); - void setCalibrationSampleInterval(int value); - // [107] define how many samples you are going to take in normal operation (default: 50); - void setReadSampleTimes(int value); - // [108] define the time interal(in milisecond) between each samples in the normal operations (default: 5); - void setReadSampleInterval(int value); - // set the LPGCurve array (default: {2.3,0.21,-0.47}) - void setLPGCurve(float *value); - // set the COCurve array (default: {2.3,0.72,-0.34}) - void setCOCurve(float *value); - // set the SmokeCurve array (default: {2.3,0.53,-0.44}) - void setSmokeCurve(float *value); -~~~ - * SensorACS712 ~~~c // [101] set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); @@ -625,16 +353,6 @@ Each sensor class can expose additional methods. void setOffset(int value); ~~~ -* SensorRainGauge / SensorPowerMeter / SensorWaterMeter -~~~c - // [102] set how many pulses for each unit (e.g. 1000 pulses for 1 kwh of power, 9 pulses for 1 mm of rain, etc.) - void setPulseFactor(float value); - // set initial value - internal pull up (default: HIGH) - void setInitialValue(int value); - // set the interrupt mode to attach to (default: FALLING) - void setInterruptMode(int value); -~~~ - * SensorDigitalOutput / SensorRelay ~~~c // [103] define which value to set to the output when set to on (default: HIGH) @@ -647,10 +365,6 @@ Each sensor class can expose additional methods. void setInputIsElapsed(bool value); // [107] optionally wait for the given number of milliseconds after changing the status (default: 0) void setWaitAfterSet(int value); - // manually switch the output to the provided value - void setStatus(int value); - // get the current state - int getStatus(); ~~~ * SensorLatchingRelay (in addition to those available for SensorDigitalOutput / SensorRelay) @@ -699,16 +413,6 @@ Each sensor class can expose additional methods. void setForecastSamplesCount(int value); ~~~ -* SensorHCSR04 -~~~c - // [101] Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) - void setTriggerPin(int value); - // [102] Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor) - void setEchoPin(int value); - // [103] Maximum distance we want to ping for (in centimeters) (default: 300) - void setMaxDistance(int value); -~~~ - * SensorSonoff ~~~c // [101] set the button's pin (default: 0) @@ -719,10 +423,40 @@ Each sensor class can expose additional methods. void setLedPin(int value); ~~~ -* SensorMHZ19 +* SensorHCSR04 ~~~c - // set the RX and TX pins for the software serial port to talk to the sensor - void setRxTx(int rxpin, int txpin); + // [101] Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) + void setTriggerPin(int value); + // [102] Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor) + void setEchoPin(int value); + // [103] Maximum distance we want to ping for (in centimeters) (default: 300) + void setMaxDistance(int value); +~~~ + +* SensorMQ +~~~c + // [101] define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); + void setTargetGas(int value); + // [102] define the load resistance on the board, in kilo ohms (default: 1); + void setRlValue(float value); + // [103] define the Ro resistance on the board (default: 10000); + void setRoValue(float value); + // [104] Sensor resistance in clean air (default: 9.83); + void setCleanAirFactor(float value); + // [105] define how many samples you are going to take in the calibration phase (default: 50); + void setCalibrationSampleTimes(int value); + // [106] define the time interal(in milisecond) between each samples in the cablibration phase (default: 500); + void setCalibrationSampleInterval(int value); + // [107] define how many samples you are going to take in normal operation (default: 50); + void setReadSampleTimes(int value); + // [108] define the time interal(in milisecond) between each samples in the normal operations (default: 5); + void setReadSampleInterval(int value); + // set the LPGCurve array (default: {2.3,0.21,-0.47}) + void setLPGCurve(float *value); + // set the COCurve array (default: {2.3,0.72,-0.34}) + void setCOCurve(float *value); + // set the SmokeCurve array (default: {2.3,0.53,-0.44}) + void setSmokeCurve(float *value); ~~~ * SensorTSL2561 @@ -737,6 +471,12 @@ Each sensor class can expose additional methods. void setAddress(int value); ~~~ +* SensorPT100 +~~~c + // [101] set the voltageRef used to compare with analog measures + void setVoltageRef(float value); +~~~ + * SensorDimmer ~~~c // [101] set the effect to use for a smooth transition, can be one of SensorDimmer::EASE_LINEAR, SensorDimmer::EASE_INSINE, SensorDimmer::EASE_OUTSINE, SensorDimmer::EASE_INOUTSINE (default: EASE_LINEAR) @@ -749,88 +489,67 @@ Each sensor class can expose additional methods. void fadeTo(int value); ~~~ -### Creating a gateway - -NodeManager can be also used to create a MySensors gateway. Open your config.h file and look for the gateway-specific defines under "MySensors gateway configuration". The most common settings are reported there, just uncomment those you need to use based on the network you are creating. - -Please note you don't necessarily need a NodeManager gateway to interact with a NodeManager node. The NodeManager node is fully compatible with any existing gateway you are currently operating with. - -### Upload your sketch - -Upload your sketch to your arduino board as you are used to. - -Check your gateway's logs to ensure the node is working as expected. You should see the node presenting itself, reporting battery level, presenting all the registered sensors and the configuration child id service. -When `DEBUG` is enabled, detailed information is available through the serial port. Remember to disable debug once the tests have been completed. - -### Communicate with NodeManager and its sensors - -You can interact with each registered sensor by sending to the child id a `REQ` command (or a `SET` for output sensors like relays). For example to request the temperature to node_id 254 and child_id 1: - -`254;1;2;0;0;` - -To activate a relay connected to the same node, child_id 100 we need to send a `SET` command with payload set to 1: - -`254;100;1;0;2;1` - -No need to implement anything on your side since for built-in sensors this is handled automatically. - -NodeManager exposes also a configuration service which is by default on child_id 200 so you can interact with it by sending `V_CUSTOM` type of messages and commands within the payload. For each `REQ` message, the node will respond with a `SET` message if successful. - -Almost all the functions made available through the API can be called remotely. To do so, the payload must be in the format `[,]` where `function_id` is the number between square brackets you can find in the description above and, if the function takes and argument, this can be passed along in `value_to_set`. -For example, to request a battery report, find the function you need to call remotely within the documentation: +* SensorRainGauge / SensorPowerMeter / SensorWaterMeter ~~~c - // [2] Send a battery level report to the controller - void batteryReport(); + // [102] set how many pulses for each unit (e.g. 1000 pulses for 1 kwh of power, 9 pulses for 1 mm of rain, etc.) + void setPulseFactor(float value); + // set initial value - internal pull up (default: HIGH) + void setInitialValue(int value); + // set the interrupt mode to attach to (default: FALLING) + void setInterruptMode(int value); ~~~ -In this case `function_id` will be 2. To request a battery report to the node_id 100, send the following message: -`;;;0;;` -`100;200;2;0;48;2` -The change the sleep time to e.g. 10 minutes: +### Remote API + +If SensorConfiguration is added to NodeManager, the API can be also called remotely. SensorConfiguration exposes child id 200 that can be used to interact with the service by sending `V_CUSTOM` type of messages and commands within the payload. For each `REQ` message, the node will respond with a `SET` message if successful. + +Almost all the functions made available through the API can be called remotely. To do so, the payload must be in the format `,[,]` where `child_id` is the recipient child id you want to communicate with (the board has child id 0), `function_id` is the number between square brackets you can find in the API documentation and, if the function takes and argument, this can be passed along in `value_to_set`. +For example, to change the sleep time to e.g. 10 minutes: ~~~c // [4] set the duration (in minutes) of a sleep cycle void setSleepMinutes(int value); ~~~ -`;;;0;;,` -`100;200;2;0;48;4,10` +`;;;0;;,,` +`100;200;2;0;48;0,4,10` To wake up a node previously configured as sleeping, send the following as the node wakes up next: ~~~c // [9] wake up the board void wakeup(); ~~~ -`100;200;2;0;48;9` +`100;200;2;0;48;0,9` -The same protocol can be used to execute remotely also sensor-specific functions. In this case the message has to be sent to the sensor's child_id, with a `V_CUSTOM` type of message. For example if you want to collect and average 10 samples for child_id 1: +if you want to collect and average 10 samples for the sensor on child_id 1: ~~~c // [5] For some sensors, the measurement can be queried multiple times and an average is returned (default: 1) void setSamples(int value); ~~~ -`100;1;2;0;48;5,10` +`100;200;2;0;48;1,5,10` If you want to decrease the temperature offset of a thermistor sensor to -2: ~~~c // [105] set a temperature offset void setOffset(float value); ~~~ -`100;1;2;0;48;105,-2` +`100;200;2;0;48;1,105,-2` -Please note that anything set remotely will NOT persist a reboot apart from the sleep interval which is saved to the EEPROM (provided `PERSIST` is enabled). +Please note that anything set remotely will NOT persist a reboot apart from the sleep interval which is saved to the EEPROM if setSaveSleepSettings() is set. -## Understanding NodeManager: how it works +## Creating a new sensor + +### NodeManager's internal architecture review A NodeManager object is created for you at the beginning of your sketch and its main functions must be called from within `before()`, `presentation()`, `loop()` and `receive()` to work properly. NodeManager will do the following during each phase: NodeManager::before(): * Setup the interrupt pins to wake up the board based on the configured interrupts -* If persistance is enabled, restore from the EEPROM the latest sleeping settings +* Restore from the EEPROM the latest sleeping settings, if setSaveSleepSettings() was set * Call `before()` of each registered sensor Sensor::before(): -* Call sensor-specific implementation of before by invoking `onBefore()` to initialize the sensor +* Call sensor-specific implementation of before by invoking `onBefore()` to initialize the sensor. NodeManager::setup(): -* Send a custom message with a STARTED payload to the controller * Call `setup()` of each registered sensor Sensor::setup(): @@ -843,460 +562,338 @@ NodeManager::loop(): Sensor::loop(): * If the sensor is powered by an arduino pin, this is set to on -* For each registered sensor, the sensor-specific `onLoop()` is called. If multiple samples are requested, this is run multiple times. `onLoop()` is not intended to send out any message but just sets a new value to a local variable -* In case multiple samples have been collected, the average is calculated -* A message is sent to the gateway with the calculated value. Depending on the configuration, this is not sent if it is the same as the previous value or sent anyway after a given number of cycles. These functionalies are not sensor-specific and common to all the sensors inheriting from the `Sensor` class. +* For each registered sensor, the sensor-specific `onLoop()` is called. If multiple samples are requested, this is run multiple times. `onLoop()` is not intended to send out any message but just sets a new value to the requested child +* A message is sent to the gateway with the value. Depending on the configuration, this is not sent if it is the same as the previous value or sent anyway after a given number of cycles. These functionalies are not sensor-specific and common to all the sensors inheriting from the `Sensor` class. * If the sensor is powered by an arduino pin, this is turned off NodeManager::receive(): * Receive a message from the radio network -* If the destination child id is the configuration node, it will handle the incoming message, otherwise will dispatch the message to the recipient sensor +* Dispatch the message to the recipient sensor Sensor::receive(): * Invoke `Sensor::loop()` which will execute the sensor main taks and eventually call `Sensor::onReceive()` -NodeManager::process(): -* Process an incoming remote configuration request - -Sensor::process(): -* Process a sensor-generic incoming remote configuration request -* Calls `onProcess()` for sensor-specific incoming remote configuration request - Sensor::interrupt(): * Calls the sensor's implementation of `onInterrupt()` to handle the interrupt -## Examples -All the examples below takes place within the before() function in the main sketch, just below the "Register below your sensors" comment. - -Set battery minimum and maxium voltage. This will be used to calculate the level percentage: +### Custom sensors +If you want to create a new sensor, you can create a new class inheriting from Sensor or other subclasses. The constructor is supposed to assign to assign the sensor a name through the `_name` variable. The following methods have to be implemented: ~~~c - nodeManager.setBatteryMin(1.8); - nodeManager.setBatteryMin(3.2); -~~~ - -Instruct the board to sleep for 10 minutes at each cycle: - -~~~c - nodeManager.setSleepMinutes(10); -~~~ - -Configure a wake up pin. When pin 3 is connected to ground, the board will stop sleeping: - -~~~c - nodeManager.setSleepInterruptPin(3); -~~~ - -Use the arduino pins to power on and off the attached sensors. All the sensors' vcc and ground are connected to pin 6 (ground) and 7 (vcc). NodeManager will enable the vcc pin every time just before loop() and wait for 100ms for the power to settle before running loop() of each sensor: - -~~~c - nodeManager.setPowerPins(6,7,100); + // define what to do during before(). Usually creates all the Child(ren) which belong to the sensor + void onBefore(); + // define what to do during setup(). Usually initialize the required libraries + void onSetup(); + // define what to do during loop() by executing the sensor's main task. Usually does a calculation and store the value to send back to the given Child class. + void onLoop(Child* child); + // define what to do during receive() when the sensor receives a message + void onReceive(MyMessage* message); + // define what to do when receiving an interrupt + void onInterrupt(); ~~~ -Register a thermistor sensor attached to pin A2. NodeManager will then send the temperature to the controller at the end of each sleeping cycle: +If the sensor implements a remote API, this has to be made available in SensorConfiguration::onReceive. -~~~c - nodeManager.registerSensor(SENSOR_THERMISTOR,A2); -~~~ +## Examples -Register a SHT21 temperature/humidity sensor; since using I2C for communicating with the sensor, the pins used are implicit (A4 and A5). NodeManager will then send the temperature and the humidity to the controller at the end of each sleeping cycle: +* Analog Light and Temperature Sensor +The following sketch can be used to report the temperature and the light level based on a thermistor and LDR sensors attached to two analog pins of the arduino board (A1 and A2). Both the thermistor and the LDR are connected to ground on one side and to vcc via a resistor on the other so to measure the voltage drop across each of them through the analog pins. -~~~c - nodeManager.registerSensor(SENSOR_SHT21); -~~~ +The sensor will be put to sleep after startup and will report both the measures every 10 minutes. NodeManager will take care of presenting the sensors, managing the sleep cycle, reporting the battery level every hour and report the measures in the appropriate format. -Register a LDR sensor attached to pin A1 and send to the gateway the average of 3 samples: +Even if the sensor is sleeping most of the time, it can be potentially woke up by sending a V_CUSTOM message to NodeManager service child id (200 by default) just after having reported its heartbeat. At this point the node will report awake and the user can interact with it by e.g. sending REQ messages to its child IDs, changing the duration of a sleep cycle, etc. ~~~c - int sensor_ldr = nodeManager.registerSensor(SENSOR_LDR,A1); - ((SensorLDR*)nodeManager.getSensor(sensor_ldr))->setSamples(3); -~~~ - -Register a rain sensor connected to A0. This will be powered with via pins 4 (ground) and 5 (vcc) just before reading its value at each cycle, it will be presented as S_RAIN. sending V_RAINRATE messages, the output will be a percentage (calculated between 200 and 1024) and the value will be reversed (so that no rain will be 0%): +*/ -~~~c - int rain = nodeManager.registerSensor(SENSOR_ANALOG_INPUT,A0); - SensorAnalogInput* rainSensor = ((SensorAnalogInput*)nodeManager.getSensor(rain)); - rainSensor->setPowerPins(4,5,300); - rainSensor->setPresentation(S_RAIN); - rainSensor->setType(V_RAINRATE); - rainSensor->setOutputPercentage(true); - rainSensor->setRangeMin(200); - rainSensor->setRangeMax(1024); - rainSensor->setReverse(true); -~~~ +/********************************** + * MySensors node configuration + */ -Register a latching relay connecting to pin 6 (set) and pin 7 (unset): +// General settings +#define SKETCH_NAME "LightTemperatureSensor" +#define SKETCH_VERSION "1.0" +#define MY_BAUD_RATE 9600 +#define MY_NODE_ID 99 +#define MY_SPLASH_SCREEN_DISABLED -~~~c - nodeManager.registerSensor(SENSOR_LATCHING_RELAY,6); -~~~ +// NRF24 radio settings +#define MY_RADIO_NRF24 -## Example Sketches +/*********************************** + * NodeManager modules + */ -* Analog Light and Temperature Sensor +#define MODULE_ANALOG_INPUT +#define MODULE_THERMISTOR -The following sketch can be used to report the temperature and the light level based on a thermistor and LDR sensors attached to two analog pins of the arduino board (A1 and A2). Both the thermistor and the LDR are connected to ground on one side and to vcc via a resistor on the other so to measure the voltage drop across each of them through the analog pins. +/*********************************** + * Load NodeManager Library + */ -The sensor will be put to sleep after startup and will report both the measures every 10 minutes. NodeManager will take care of presenting the sensors, managing the sleep cycle, reporting the battery level every hour and report the measures in the appropriate format. This sketch requires MODULE_ANALOG_INPUT enabled in the global config.h file. +// enable NodeManager's debug on serial port +#define NODEMANAGER_DEBUG +// include NodeManager's library +#include "NodeManagerLibrary.h" +NodeManager node; -Even if the sensor is sleeping most of the time, it can be potentially woke up by sending a V_CUSTOM message to NodeManager service child id (200 by default) just after having reported its heartbeat. At this point the node will report awake and the user can interact with it by e.g. sending REQ messages to its child IDs, changing the duration of a sleep cycle, etc. +/*********************************** + * Add your sensors below + */ -~~~c -/* -NodeManager is intended to take care on your behalf of all those common tasks a MySensors node has to accomplish, speeding up the development cycle of your projects. +SensorBattery battery(node); +SensorConfiguration configuration(node); -NodeManager includes the following main components: -- Sleep manager: allows managing automatically the complexity behind battery-powered sensors spending most of their time sleeping -- Power manager: allows powering on your sensors only while the node is awake -- Battery manager: provides common functionalities to read and report the battery level -- Remote configuration: allows configuring remotely the node without the need to have physical access to it -- Built-in personalities: for the most common sensors, provide embedded code so to allow their configuration with a single line +SensorLDR ldr(node,A1); +SensorThermistor thermistor(node,A2); -Documentation available on: https://github.com/mysensors/NodeManager +/*********************************** + * Main Sketch */ - -// load user settings -#include "config.h" -// load MySensors library -#include -// load NodeManager library -#include "NodeManager.h" - -// create a NodeManager instance -NodeManager nodeManager; - // before void before() { // setup the serial port baud rate - Serial.begin(MY_BAUD_RATE); + Serial.begin(MY_BAUD_RATE); + /* - * Register below your sensors + * Configure your sensors below */ - nodeManager.setSleepMinutes(10); - nodeManager.setReportIntervalMinutes(10); - nodeManager.registerSensor(SENSOR_THERMISTOR,A1); - nodeManager.registerSensor(SENSOR_LDR,A2); + + node.setReportIntervalMinutes(10); + node.setSleepMinutes(10); + /* - * Register above your sensors + * Configure your sensors above */ - nodeManager.before(); + node.before(); } // presentation void presentation() { // call NodeManager presentation routine - nodeManager.presentation(); - + node.presentation(); } // setup void setup() { // call NodeManager setup routine - nodeManager.setup(); + node.setup(); } // loop void loop() { // call NodeManager loop routine - nodeManager.loop(); - + node.loop(); } // receive -void receive(const MyMessage &message) { +void receive(MyMessage &message) { // call NodeManager receive routine - nodeManager.receive(message); + node.receive(message); } // receiveTime void receiveTime(unsigned long ts) { // call NodeManager receiveTime routine - nodeManager.receiveTime(ts); + node.receiveTime(ts); } ~~~ -* Motion Sensor - -The following sketch can be used to report back to the controller when a motion sensor attached to the board's pin 3 triggers. In this example, the board will be put to sleep just after startup and will report a heartbeat every hour. NodeManager will take care of configuring an interrupt associated to the provided pin so automatically wake up when a motion is detected and report a V_TRIPPED message back. This sketch requires MODULE_SWITCH to be enabled in the global config.h file. +* Motion Sensor +The following sketch can be used to report back to the controller when a motion sensor attached to the board's pin 3 triggers. In this example, the board will be put to sleep just after startup and will report a heartbeat every hour. NodeManager will take care of configuring an interrupt associated to the provided pin so automatically wake up when a motion is detected and report a V_TRIPPED message back. ~~~c -/* -NodeManager is intended to take care on your behalf of all those common tasks a MySensors node has to accomplish, speeding up the development cycle of your projects. - -NodeManager includes the following main components: -- Sleep manager: allows managing automatically the complexity behind battery-powered sensors spending most of their time sleeping -- Power manager: allows powering on your sensors only while the node is awake -- Battery manager: provides common functionalities to read and report the battery level -- Remote configuration: allows configuring remotely the node without the need to have physical access to it -- Built-in personalities: for the most common sensors, provide embedded code so to allow their configuration with a single line +*/ -Documentation available on: https://github.com/mysensors/NodeManager +/********************************** + * MySensors node configuration */ - -// load user settings -#include "config.h" -// include supporting libraries -#ifdef MY_GATEWAY_ESP8266 - #include -#endif -// load MySensors library -#include -// load NodeManager library -#include "NodeManager.h" - -// create a NodeManager instance -NodeManager nodeManager; - -// before -void before() { - // setup the serial port baud rate - Serial.begin(MY_BAUD_RATE); - /* - * Register below your sensors - */ - nodeManager.setSleepHours(1); - nodeManager.registerSensor(SENSOR_MOTION,3); - - /* - * Register above your sensors - */ - nodeManager.before(); -} - -// presentation -void presentation() { - // call NodeManager presentation routine - nodeManager.presentation(); - -} - -// setup -void setup() { - // call NodeManager setup routine - nodeManager.setup(); -} - -// loop -void loop() { - // call NodeManager loop routine - nodeManager.loop(); - -} - -// receive -void receive(const MyMessage &message) { - // call NodeManager receive routine - nodeManager.receive(message); -} +// General settings +#define SKETCH_NAME "MotionSensor" +#define SKETCH_VERSION "1.0" +#define MY_BAUD_RATE 9600 +#define MY_NODE_ID 99 +#define MY_SPLASH_SCREEN_DISABLED -// receiveTime -void receiveTime(unsigned long ts) { - // call NodeManager receiveTime routine - nodeManager.receiveTime(ts); -} -~~~ +// NRF24 radio settings +#define MY_RADIO_NRF24 -* Boiler Sensor +/*********************************** + * NodeManager modules + */ -The following sketch controls a latching relay connected to a boiler. A latching relay (requiring only a pulse to switch) has been chosen to minimize the power consumption required by a traditional relay to stay on. This relay has normally two pins, one for closing and the other for opening the controlled circuit, connected to pin 6 and 7 of the arduino board. Since using a SENSOR_LATCHING_RELAY type of sensor, NodeManager will automatically consider the provided pin as the ON pin and the one just after as the OFF pin and will take care of just sending out a single pulse only when a SET command of type V_STATUS is sent to the child id. The appropriate pin will be then used. +#define MODULE_SWITCH -In this example, the board also runs at 1Mhz so it can go down to 1.8V: by setting setBatteryMin() and setBatteryMax(), the battery percentage will be calculated and reported (by default, automatically every 10 sleeping cycles) based on these custom boundaries. +/*********************************** + * Load NodeManager Library + */ -The board will be put to sleep just after startup and will report back to the controller every 5 minutes. It is the controller's responsability to catch when the board reports its heartbeat (using smart sleep behind the scene) and send a command back if needed. This sketch requires MODULE_DIGITAL_OUTPUT to be enabled in the config.h file. +// enable NodeManager's debug on serial port +#define NODEMANAGER_DEBUG +// include NodeManager's library +#include "NodeManagerLibrary.h" +NodeManager node; -~~~c -/* -NodeManager is intended to take care on your behalf of all those common tasks a MySensors node has to accomplish, speeding up the development cycle of your projects. +/*********************************** + * Add your sensors below + */ -NodeManager includes the following main components: -- Sleep manager: allows managing automatically the complexity behind battery-powered sensors spending most of their time sleeping -- Power manager: allows powering on your sensors only while the node is awake -- Battery manager: provides common functionalities to read and report the battery level -- Remote configuration: allows configuring remotely the node without the need to have physical access to it -- Built-in personalities: for the most common sensors, provide embedded code so to allow their configuration with a single line +SensorMotion motion(node,3); -Documentation available on: https://github.com/mysensors/NodeManager +/*********************************** + * Main Sketch */ - -// load user settings -#include "config.h" -// include supporting libraries -#ifdef MY_GATEWAY_ESP8266 - #include -#endif -// load MySensors library -#include -// load NodeManager library -#include "NodeManager.h" - -// create a NodeManager instance -NodeManager nodeManager; - // before void before() { // setup the serial port baud rate - Serial.begin(MY_BAUD_RATE); + Serial.begin(MY_BAUD_RATE); + /* - * Register below your sensors + * Configure your sensors below */ - nodeManager.setBatteryMin(1.8); - nodeManager.setBatteryMax(3.2); - nodeManager.setSleepMinutes(5); - nodeManager.registerSensor(SENSOR_LATCHING_RELAY,6); + node.setSleepMinutes(60); + /* - * Register above your sensors + * Configure your sensors above */ - nodeManager.before(); + node.before(); } // presentation void presentation() { // call NodeManager presentation routine - nodeManager.presentation(); - + node.presentation(); } // setup void setup() { // call NodeManager setup routine - nodeManager.setup(); + node.setup(); } // loop void loop() { // call NodeManager loop routine - nodeManager.loop(); - + node.loop(); } // receive -void receive(const MyMessage &message) { +void receive(MyMessage &message) { // call NodeManager receive routine - nodeManager.receive(message); + node.receive(message); } // receiveTime void receiveTime(unsigned long ts) { // call NodeManager receiveTime routine - nodeManager.receiveTime(ts); + node.receiveTime(ts); } ~~~ +* Boiler Sensor -* Rain and Soil Moisture Sensor +The following sketch controls a latching relay connected to a boiler. A latching relay (requiring only a pulse to switch) has been chosen to minimize the power consumption required by a traditional relay to stay on. This relay has normally two pins, one for closing and the other for opening the controlled circuit, connected to pin 6 and 7 of the arduino board. Since using a SensorLatchingRelay type of sensor, NodeManager will automatically consider the provided pin as the ON pin and the one just after as the OFF pin and will take care of just sending out a single pulse only when a SET command of type V_STATUS is sent to the child id. The appropriate pin will be then used. -The following sketch can be used to report the rain level and the soil moisture based on two sensors connected to the board's analog pins (A1 and A2). In this case we are customizing the out-of-the-box SENSOR_ANALOG_INPUT sensor type since we just need to measure an analog input but we also want to provide the correct type and presentation for each sensor. +In this example, the board also runs at 1Mhz so it can go down to 1.8V: by setting setBatteryMin() and setBatteryMax(), the battery percentage will be calculated and reported (by default, automatically every hour) based on these custom boundaries. -We register the sensors first with registerSensor() which returns the child id assigned to the sensor. We then retrieve the sensor's reference by calling getSensor() so we can invoke the sensor-specific functions, like setPresentation() and setType(). +The board will be put to sleep just after startup and will report back to the controller every 5 minutes. It is the controller's responsability to catch when the board reports its heartbeat (using smart sleep behind the scene) and send a command back if needed. -In this example, the two sensors are not directly connected to the battery's ground and vcc but, to save additional power, are powered through two arduino's pins. By using e.g. setPowerPins(4,5,300), NodeManger will assume pin 4 is ground and pin 5 is vcc for that specific sensor so it will turn on the power just before reading the analog input (and waiting 300ms for the sensor to initialize) and back off before going to sleep. +~~~c +*/ -For both the sensors we want a percentage output and with setRangeMin() and setRangeMax() we define the boundaries for calculating the percentage (if we read e.g. 200 when the rain sensor is completely into the water, we know for sure it will not go below this value which will represent the new lower boundary). -Finally, since both the sensors reports low when wet and high when dry but we need the opposite, we set setReverse() so to have 0% reported when there is no rain/moisture, 100% on the opposite situation. +/********************************** + * MySensors node configuration + */ -~~~c -/* -NodeManager is intended to take care on your behalf of all those common tasks a MySensors node has to accomplish, speeding up the development cycle of your projects. +// General settings +#define SKETCH_NAME "BoilerSensor" +#define SKETCH_VERSION "1.0" +#define MY_BAUD_RATE 9600 +#define MY_NODE_ID 99 +#define MY_SPLASH_SCREEN_DISABLED -NodeManager includes the following main components: -- Sleep manager: allows managing automatically the complexity behind battery-powered sensors spending most of their time sleeping -- Power manager: allows powering on your sensors only while the node is awake -- Battery manager: provides common functionalities to read and report the battery level -- Remote configuration: allows configuring remotely the node without the need to have physical access to it -- Built-in personalities: for the most common sensors, provide embedded code so to allow their configuration with a single line +// NRF24 radio settings +#define MY_RADIO_NRF24 + +/*********************************** + * NodeManager modules + */ + +#define MODULE_DIGITAL_OUTPUT -Documentation available on: https://github.com/mysensors/NodeManager +/*********************************** + * Load NodeManager Library */ - -// load user settings -#include "config.h" -// include supporting libraries -#ifdef MY_GATEWAY_ESP8266 - #include -#endif -// load MySensors library -#include -// load NodeManager library -#include "NodeManager.h" - -// create a NodeManager instance -NodeManager nodeManager; +// enable NodeManager's debug on serial port +#define NODEMANAGER_DEBUG +// include NodeManager's library +#include "NodeManagerLibrary.h" +NodeManager node; + +/*********************************** + * Add your sensors below + */ + +SensorBattery battery(node); +SensorLatchingRelay latching(node,6); + +/*********************************** + * Main Sketch + */ // before void before() { // setup the serial port baud rate - Serial.begin(MY_BAUD_RATE); + Serial.begin(MY_BAUD_RATE); + /* - * Register below your sensors + * Configure your sensors below */ - analogReference(DEFAULT); - nodeManager.setSleepMinutes(10); - nodeManager.setReportIntervalMinutes(10); - - int rain = nodeManager.registerSensor(SENSOR_ANALOG_INPUT,A1); - int soil = nodeManager.registerSensor(SENSOR_ANALOG_INPUT,A2); - - SensorAnalogInput* rainSensor = ((SensorAnalogInput*)nodeManager.getSensor(rain)); - SensorAnalogInput* soilSensor = ((SensorAnalogInput*)nodeManager.getSensor(soil)); + + node.setSleepMinutes(5); - rainSensor->setPresentation(S_RAIN); - rainSensor->setType(V_RAINRATE); - rainSensor->setPowerPins(4,5,300); - rainSensor->setOutputPercentage(true); - rainSensor->setRangeMin(200); - rainSensor->setRangeMax(1024); - rainSensor->setReverse(true); + battery.setBatteryMin(1.8); + battery.setBatteryMax(3.2); - soilSensor->setPresentation(S_MOISTURE); - soilSensor->setType(V_LEVEL); - soilSensor->setPowerPins(6,7,300); - soilSensor->setOutputPercentage(true); - soilSensor->setRangeMin(300); - soilSensor->setRangeMax(1024); - soilSensor->setReverse(true); - /* - * Register above your sensors + * Configure your sensors above */ - nodeManager.before(); + node.before(); } // presentation void presentation() { // call NodeManager presentation routine - nodeManager.presentation(); - + node.presentation(); } // setup void setup() { // call NodeManager setup routine - nodeManager.setup(); + node.setup(); } // loop void loop() { // call NodeManager loop routine - nodeManager.loop(); - + node.loop(); } // receive -void receive(const MyMessage &message) { +void receive(MyMessage &message) { // call NodeManager receive routine - nodeManager.receive(message); + node.receive(message); } // receiveTime void receiveTime(unsigned long ts) { // call NodeManager receiveTime routine - nodeManager.receiveTime(ts); + node.receiveTime(ts); } ~~~ @@ -1429,3 +1026,5 @@ v1.6: * Added safeguard (automatic off) to SensorDigitalOutput * Any sensor can now access all NodeManager's functions * DHT sensor now using MySensors' DHT library + +v1.7: \ No newline at end of file diff --git a/config.h b/config.h deleted file mode 100755 index d93ab3f9..00000000 --- a/config.h +++ /dev/null @@ -1,176 +0,0 @@ -#ifndef config_h -#define config_h - -/********************************** - * Sketch configuration - */ - -#define SKETCH_NAME "NodeManager" -#define SKETCH_VERSION "1.0" - -/********************************** - * MySensors node configuration - */ - -// General settings -#define MY_BAUD_RATE 9600 -//#define MY_DEBUG -//#define MY_NODE_ID 100 -//#define MY_SMART_SLEEP_WAIT_DURATION_MS 500 - -// NRF24 radio settings -#define MY_RADIO_NRF24 -//#define MY_RF24_ENABLE_ENCRYPTION -//#define MY_RF24_CHANNEL 76 -//#define MY_RF24_PA_LEVEL RF24_PA_HIGH -//#define MY_DEBUG_VERBOSE_RF24 -//#define MY_RF24_DATARATE RF24_250KBPS - -// RFM69 radio settings -//#define MY_RADIO_RFM69 -//#define MY_RFM69_FREQUENCY RF69_868MHZ -//#define MY_RFM69_FREQUENCY RFM69_868MHZ -//#define MY_IS_RFM69HW -//#define MY_RFM69_NEW_DRIVER -//#define MY_RFM69_ENABLE_ENCRYPTION -//#define MY_RFM69_NETWORKID 100 -//#define MY_DEBUG_VERBOSE_RFM69 -//#define MY_RF69_IRQ_PIN D1 -//#define MY_RF69_IRQ_NUM MY_RF69_IRQ_PIN -//#define MY_RF69_SPI_CS D2 -//#define MY_RFM69_ATC_MODE_DISABLED - -// RS485 serial transport settings -//#define MY_RS485 -//#define MY_RS485_BAUD_RATE 9600 -//#define MY_RS485_DE_PIN 2 -//#define MY_RS485_MAX_MESSAGE_LENGTH 40 -//#define MY_RS485_HWSERIAL Serial1 - -// Message signing settings -//#define MY_SIGNING_SOFT -//#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 -//#define MY_SIGNING_REQUEST_SIGNATURES -//#define MY_SIGNING_ATSHA204 - -// OTA Firmware update settings -//#define MY_OTA_FIRMWARE_FEATURE -//#define OTA_WAIT_PERIOD 300 -//#define FIRMWARE_MAX_REQUESTS 2 -//#define MY_OTA_RETRY 2 - -/********************************** - * MySensors gateway configuration - */ -// Common gateway settings -//#define MY_REPEATER_FEATURE - -// Serial gateway settings -//#define MY_GATEWAY_SERIAL - -// Ethernet gateway settings -//#define MY_GATEWAY_W5100 - -// ESP8266 gateway settings -//#define MY_GATEWAY_ESP8266 -//#define MY_ESP8266_SSID "" -//#define MY_ESP8266_PASSWORD "" - -// Gateway networking settings -//#define MY_IP_ADDRESS 192,168,178,87 -//#define MY_IP_GATEWAY_ADDRESS 192,168,178,1 -//#define MY_IP_SUBNET_ADDRESS 255,255,255,0 -//#define MY_PORT 5003 -//#define MY_GATEWAY_MAX_CLIENTS 2 -//#define MY_USE_UDP - -// Gateway MQTT settings -//#define MY_GATEWAY_MQTT_CLIENT -//#define MY_CONTROLLER_IP_ADDRESS 192, 168, 178, 68 -//#define MY_PORT 1883 -//#define MY_MQTT_USER "username" -//#define MY_MQTT_PASSWORD "password" -//#define MY_MQTT_CLIENT_ID "mysensors-1" -//#define MY_MQTT_PUBLISH_TOPIC_PREFIX "mygateway1-out" -//#define MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "mygateway1-in" - -// Gateway inclusion mode -//#define MY_INCLUSION_MODE_FEATURE -//#define MY_INCLUSION_BUTTON_FEATURE -//#define MY_INCLUSION_MODE_DURATION 60 -//#define MY_DEFAULT_LED_BLINK_PERIOD 300 - -// Gateway Leds settings -//#define MY_DEFAULT_ERR_LED_PIN 4 -//#define MY_DEFAULT_RX_LED_PIN 5 -//#define MY_DEFAULT_TX_LED_PIN 6 - -/*********************************** - * NodeManager configuration - */ - -// if enabled, enable debug messages on serial port -#define DEBUG 1 - -// if enabled, enable the capability to power on sensors with the arduino's pins to save battery while sleeping -#define POWER_MANAGER 1 -// if enabled, will load the battery manager library to allow the battery level to be reported automatically or on demand -#define BATTERY_MANAGER 1 -// if enabled, allow modifying the configuration remotely by interacting with the configuration child id -#define REMOTE_CONFIGURATION 1 -// if enabled, persist the remote configuration settings on EEPROM -#define PERSIST 0 -// if enabled, a battery sensor will be created at BATTERY_CHILD_ID (201 by default) and will report vcc voltage together with the battery level percentage -#define BATTERY_SENSOR 1 -// if enabled, a signal sensor will be created at RSSI_CHILD_ID (202 by default) and will report the signal quality of the transport layer -#define SIGNAL_SENSOR 0 -// if enabled, send a SLEEPING and AWAKE service messages just before entering and just after leaving a sleep cycle and STARTED when starting/rebooting -#define SERVICE_MESSAGES 0 - -// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN, SENSOR_SOIL_MOISTURE -#define MODULE_ANALOG_INPUT 1 -// Enable this module to use one of the following sensors: SENSOR_DIGITAL_INPUT -#define MODULE_DIGITAL_INPUT 1 -// Enable this module to use one of the following sensors: SENSOR_DIGITAL_OUTPUT, SENSOR_RELAY, SENSOR_LATCHING_RELAY -#define MODULE_DIGITAL_OUTPUT 1 -// Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22 -#define MODULE_DHT 0 -// Enable this module to use one of the following sensors: SENSOR_SHT21, SENSOR_HTU21D -#define MODULE_SHT21 0 -// Enable this module to use one of the following sensors: SENSOR_SWITCH, SENSOR_DOOR, SENSOR_MOTION -#define MODULE_SWITCH 0 -// Enable this module to use one of the following sensors: SENSOR_DS18B20 -#define MODULE_DS18B20 0 -// Enable this module to use one of the following sensors: SENSOR_BH1750 -#define MODULE_BH1750 0 -// Enable this module to use one of the following sensors: SENSOR_MLX90614 -#define MODULE_MLX90614 0 -// Enable this module to use one of the following sensors: SENSOR_BME280 -#define MODULE_BME280 0 -// Enable this module to use one of the following sensors: SENSOR_SONOFF -#define MODULE_SONOFF 0 -// Enable this module to use one of the following sensors: SENSOR_BMP085 -#define MODULE_BMP085 0 -// Enable this module to use one of the following sensors: SENSOR_HCSR04 -#define MODULE_HCSR04 0 -// Enable this module to use one of the following sensors: SENSOR_MCP9808 -#define MODULE_MCP9808 0 -// Enable this module to use one of the following sensors: SENSOR_MQ -#define MODULE_MQ 0 -// Enable this module to use one of the following sensors: SENSOR_MHZ19 -#define MODULE_MHZ19 0 -// Enable this module to use one of the following sensors: SENSOR_AM2320 -#define MODULE_AM2320 0 -// Enable this module to use one of the following sensors: SENSOR_TSL2561 -#define MODULE_TSL2561 0 -// Enable this module to use one of the following sensors: SENSOR_PT100 -#define MODULE_PT100 0 -// Enable this module to use one of the following sensors: SENSOR_BMP280 -#define MODULE_BMP280 0 -// Enable this module to use one of the following sensors: SENSOR_DIMMER -#define MODULE_DIMMER 0 -// Enable this module to use one of the following sensors: SENSOR_RAIN_GAUGE, SENSOR_POWER_METER, SENSOR_WATER_METER -#define MODULE_PULSE_METER 0 - -#endif -