diff --git a/.gitignore b/.gitignore
old mode 100644
new mode 100755
diff --git a/NodeManager.cpp b/NodeManager.cpp
index 6471fd0e5b8b9b1d3c6a02a2be4afed93c6b0311..72ec15954d19710a9433b914916ccded0ce721f3 100644
--- a/NodeManager.cpp
+++ b/NodeManager.cpp
@@ -4,68 +4,38 @@
 
 #include "NodeManager.h"
 
-/***************************************
-   Global functions
-*/
-
-// return vcc in V
-float getVcc() {
-  #ifndef MY_GATEWAY_ESP8266
-    // Measure Vcc against 1.1V Vref
-    #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
-      ADMUX = (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
-    #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
-      ADMUX = (_BV(MUX5) | _BV(MUX0));
-    #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
-      ADMUX = (_BV(MUX3) | _BV(MUX2));
-    #else
-      ADMUX = (_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
-    #endif
-    // Vref settle
-    wait(70);
-    // Do conversion
-    ADCSRA |= _BV(ADSC);
-    while (bit_is_set(ADCSRA, ADSC)) {};
-    // return Vcc in mV
-    return (float)((1125300UL) / ADC) / 1000;
-  #else
-    return (float)0;
-  #endif
-}
-
-
 /***************************************
    PowerManager
 */
 
 // set the vcc and ground pin the sensor is connected to
 void PowerManager::setPowerPins(int ground_pin, int vcc_pin, int wait_time) {
+  _ground_pin = ground_pin;
+  _vcc_pin = vcc_pin;
   #if DEBUG == 1
     Serial.print(F("PWR G="));
-    Serial.print(ground_pin);
+    Serial.print(_ground_pin);
     Serial.print(F(" V="));
-    Serial.println(vcc_pin);
+    Serial.println(_vcc_pin);
   #endif
-  // configure the vcc pin as output and initialize to high (power on)
-  _vcc_pin = vcc_pin;
-  pinMode(_vcc_pin, OUTPUT);
-  digitalWrite(_vcc_pin, HIGH);
-  // configure the ground pin as output and initialize to low
-  _ground_pin = ground_pin;
-  pinMode(_ground_pin, OUTPUT);
-  digitalWrite(_ground_pin, LOW);
+  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;
 }
 
-// return true if power pins have been configured
-bool PowerManager::isConfigured() {
-  if (_vcc_pin != -1 && _ground_pin != -1) return true;
-  return false;
-}
 
 // turn on the sensor by activating its power pins
 void PowerManager::powerOn() {
-  if (! isConfigured()) return;
+  if (_vcc_pin == -1) return;
   #if DEBUG == 1
     Serial.print(F("ON P="));
     Serial.println(_vcc_pin);
@@ -78,7 +48,7 @@ void PowerManager::powerOn() {
 
 // turn off the sensor
 void PowerManager::powerOff() {
-  if (! isConfigured()) return;
+  if (_vcc_pin == -1) return;
   #if DEBUG == 1
     Serial.print(F("OFF P="));
     Serial.println(_vcc_pin);
@@ -87,6 +57,153 @@ void PowerManager::powerOff() {
   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 str[10];
+  char* ptr;
+  strcpy(str,string);
+  // tokenize the string and split function from value
+  strtok_r(str,",",&ptr);
+  _function = atoi(str);
+  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
@@ -96,10 +213,14 @@ void PowerManager::powerOff() {
    Sensor class
 */
 // constructor
-Sensor::Sensor(int child_id, int pin) {
+Sensor::Sensor(NodeManager* node_manager, int child_id, int pin) {
+  _node_manager = node_manager;
   _child_id = child_id;
   _pin = pin;
   _msg = MyMessage(_child_id, _type);
+  _msg_service = MyMessage(_child_id, V_CUSTOM);
+  _report_timer = new Timer(_node_manager);
+  _force_update_timer = new Timer(_node_manager);
 }
 
 // setter/getter
@@ -131,23 +252,20 @@ int Sensor::getType() {
 void Sensor::setDescription(char* value) {
   _description = value;
 }
-void Sensor::setAck(bool value) {
-  _ack = value;
-}
-void Sensor::setRetries(int value) {
-  _retries = value;
-}
 void Sensor::setSamples(int value) {
   _samples = value;
 }
 void Sensor::setSamplesInterval(int value) {
   _samples_interval = value;
 }
-void Sensor::setTackLastValue(bool value) {
+void Sensor::setTrackLastValue(bool value) {
   _track_last_value = value;
 }
-void Sensor::setForceUpdate(int value) {
-  _force_update = 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;
@@ -158,6 +276,9 @@ int Sensor::getValueType() {
 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);
@@ -172,12 +293,6 @@ void Sensor::setFloatPrecision(int value) {
       _powerManager.powerOff();
     }
 #endif
-void Sensor::setSleepBetweenSend(int value) {
-  _sleep_between_send = value;
-}
-void Sensor::setInterruptPin(int value) {
-  _interrupt_pin = value;
-}
 int Sensor::getInterruptPin() {
   return _interrupt_pin;
 }
@@ -191,6 +306,38 @@ 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
@@ -199,7 +346,7 @@ void Sensor::presentation() {
     Serial.print(F(" T="));
     Serial.println(_presentation);
   #endif
-  present(_child_id, _presentation,_description,_ack);
+  present(_child_id, _presentation,_description,_node_manager->getAck());
 }
 
 // call the sensor-specific implementation of before
@@ -217,82 +364,154 @@ void Sensor::setup() {
 // 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
-  float total = 0;
-  // keep track of the number of cycles since the last update
-  if (_force_update > 0) _cycles++;
+  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 (message.sender == 0 && message.sensor == 0 && message.getCommand() == 0 && message.type == 0) {
-      // empty message, we'be been called from loop()
-      onLoop();
-    }
-    else {
+    if (_isReceive(message)) {
       // we've been called from receive(), pass the message along
       onReceive(message);
     }
-    // for integers and floats, keep track of the total
+    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) wait(_samples_interval);
+    if (_samples_interval > 0) _node_manager->sleepOrWait(_samples_interval);
   }
-  // process the result and send a response back. 
+  // process the result and send a response back
   if (_value_type == TYPE_INTEGER && total > -1) {
     // if the value is an integer, calculate the average value of the samples
     int avg = (int) (total / _samples);
     // if track last value is disabled or if enabled and the current value is different then the old value, send it back
-    if (! _track_last_value || (_track_last_value && avg != _last_value_int) || (_track_last_value && _force_update > 0 && _cycles > _force_update)) {
-      _cycles = 0;
+    if (_isReceive(message) || _isWorthSending(avg != _last_value_int))  {
       _last_value_int = avg;
       _send(_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;
-    // if track last value is disabled or if enabled and the current value is different then the old value, send it back
-    if (! _track_last_value || (_track_last_value && avg != _last_value_float) || (_track_last_value && _cycles >= _force_update)) {
-      _cycles = 0;
+    // report the value back
+    if (_isReceive(message) || _isWorthSending(avg != _last_value_float))  {
       _last_value_float = avg;
       _send(_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;
+      _send(_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 (! _track_last_value || (_track_last_value && strcmp(_value_string, _last_value_string) != 0) || (_track_last_value && _cycles >= _force_update)) {
-      _cycles = 0;
+    if (_isReceive(message) || _isWorthSending(strcmp(_value_string, _last_value_string) != 0))  {
       _last_value_string = _value_string;
       _send(_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 || message.type != _type) return;
+  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;
+  }
+  _send(_msg_service.set(function));
+}
+
 // send a message to the network
 void Sensor::_send(MyMessage & message) {
   // send the message, multiple times if requested
-  for (int i = 0; i < _retries; i++) {
+  for (int i = 0; i < _node_manager->getRetries(); i++) {
     // if configured, sleep beetween each send
-    if (_sleep_between_send > 0) sleep(_sleep_between_send);
+    if (_node_manager->getSleepBetweenSend() > 0) sleep(_node_manager->getSleepBetweenSend());
     #if DEBUG == 1
       Serial.print(F("SEND D="));
       Serial.print(message.destination);
@@ -309,16 +528,38 @@ void Sensor::_send(MyMessage & message) {
       Serial.print(F(" F="));
       Serial.println(message.getFloat());
     #endif
-    send(message,_ack);
+    send(message,_node_manager->getAck());
+  }
+}
+
+// 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(int child_id, int pin): Sensor(child_id, pin) {
+SensorAnalogInput::SensorAnalogInput(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) {
 }
 
 // setter/getter
@@ -372,6 +613,24 @@ 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;
+  }
+  _send(_msg_service.set(function));
+}
+
+// what to do when receiving an interrupt
+void SensorAnalogInput::onInterrupt() {
+}
+
 // read the analog input
 int SensorAnalogInput::_getAnalogRead() {
   #ifndef MY_GATEWAY_ESP8266
@@ -405,7 +664,7 @@ int SensorAnalogInput::_getPercentage(int adc) {
 */
 
 // contructor
-SensorLDR::SensorLDR(int child_id, int pin): SensorAnalogInput(child_id, pin) {
+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);
@@ -417,7 +676,7 @@ SensorLDR::SensorLDR(int child_id, int pin): SensorAnalogInput(child_id, pin) {
 */
 
 // contructor
-SensorThermistor::SensorThermistor(int child_id, int pin): Sensor(child_id, pin) {
+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);
@@ -465,16 +724,14 @@ void SensorThermistor::onLoop() {
   temperature += 1.0 / (_nominal_temperature + 273.15); // + (1/To)
   temperature = 1.0 / temperature;                 // Invert
   temperature -= 273.15;                         // convert to C
-  if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32;
+  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.print(temperature);
-    Serial.print(F(" M="));
-    Serial.println(getControllerConfig().isMetric);
+    Serial.println(temperature);
   #endif
   // store the value
   _value_float = temperature;
@@ -485,13 +742,30 @@ 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;
+  }
+  _send(_msg_service.set(function));
+}
+
+// what to do when receiving an interrupt
+void SensorThermistor::onInterrupt() {
+}
 
 /*
    SensorML8511
 */
 
 // contructor
-SensorML8511::SensorML8511(int child_id, int pin): Sensor(child_id, pin) {
+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);
@@ -512,7 +786,7 @@ void SensorML8511::onSetup() {
 void SensorML8511::onLoop() {
   // read the voltage 
   int uvLevel = analogRead(_pin);
-  int refLevel = getVcc()*1024/3.3;
+  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
@@ -534,6 +808,14 @@ 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;
@@ -544,7 +826,7 @@ float SensorML8511::_mapfloat(float x, float in_min, float in_max, float out_min
 */
 
 // contructor
-SensorACS712::SensorACS712(int child_id, int pin): Sensor(child_id, pin) {
+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);
@@ -589,389 +871,339 @@ 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;
+  }
+  _send(_msg_service.set(function));
+}
+
+// what to do when receiving an interrupt
+void SensorACS712::onInterrupt() {
+}
+
 /*
-   SensorRainGauge
+   SensorRain
 */
 
 // contructor
-SensorRainGauge::SensorRainGauge(int child_id, int pin): Sensor(child_id, pin) {
-  // set presentation, type and value type
+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_RAIN);
-  setValueType(TYPE_FLOAT);
-
+  setType(V_RAINRATE);
+  setReference(DEFAULT);
+  setOutputPercentage(true);
+  setReverse(true);
+  setRangeMin(100);
 }
 
-// initialize static variables
-long SensorRainGauge::_last_tip = 0;
-long SensorRainGauge::_count = 0;
+/*
+   SensorSoilMoisture
+*/
 
-// setter/getter
-void SensorRainGauge::setReportInterval(int value) {
-  _report_interval = value;
+// 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);
 }
-void SensorRainGauge::setSingleTip(float value) {
-  _single_tip = value;
+
+#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 SensorRainGauge::onBefore() {
-  // set the pin as input and enabled pull up
-  pinMode(_pin, INPUT_PULLUP);
-  // attach to the pin's interrupt and execute the routine on falling
-  attachInterrupt(digitalPinToInterrupt(_pin), _onTipped, FALLING);
+void SensorDigitalInput::onBefore() {
+  // set the pin for input
+  pinMode(_pin, INPUT);
 }
 
 // what to do during setup
-void SensorRainGauge::onSetup() {
-}
-
-// what to do when when receiving an interrupt
-void SensorRainGauge::_onTipped() {
-  long now = millis();
-  // on tipping, two consecutive interrupts are received, ignore the second one
-  if ( (now - _last_tip > 100) || (now < _last_tip) ){
-    // increase the counter
-    _count++;
-    #if DEBUG == 1
-      Serial.println(F("RAIN+"));
-    #endif
-  }
-  _last_tip = now;
+void SensorDigitalInput::onSetup() {
 }
 
 // what to do during loop
-void SensorRainGauge::onLoop() {
-  // avoid reporting the same value multiple times
-  _value_float = -1;
-  long now = millis();
-  // time elapsed since the last report
-  long elapsed = now - _last_report;
-  // minimum time interval between reports
-  long min_interval = ((long)_report_interval*1000)*60;
-  // time to report or millis() reset
-  if ( (elapsed > min_interval) || (now < _last_report)) {
-    // report the total amount of rain for the last period
-    _value_float = _count*_single_tip;
-    #if DEBUG == 1
-      Serial.print(F("RAIN I="));
-      Serial.print(_child_id);
-      Serial.print(F(" T="));
-      Serial.println(_value_float);
-    #endif
-    // reset the counters
-    _count = 0;
-    _last_report = now;
-  }
+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 SensorRainGauge::onReceive(const MyMessage & message) {
-  if (message.getCommand() == C_REQ) {
-    // report the total amount of rain for the last period
-    _value_float = _count*_single_tip;    
-  }
+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) {
+}
 
-/*
- * SensorMQ
- */
-SensorMQ::SensorMQ(int child_id, int pin): Sensor(child_id,pin) {
-  setPresentation(S_AIR_QUALITY);
-  setType(V_LEVEL);
+// what to do when receiving an interrupt
+void SensorDigitalInput::onInterrupt() {
 }
+#endif
 
-//setter/getter
-void SensorMQ::setRlValue(float value) {
-  _rl_value = value;
+
+#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);
 }
-void SensorMQ::setRoValue(float value) {
-  _ro = value;
+
+// what to do during before
+void SensorDigitalOutput::onBefore() {
+  _setupPin(_pin);
+
 }
-void SensorMQ::setCleanAirFactor(float value) {
-  _ro_clean_air_factor = value;
+
+// what to do during setup
+void SensorDigitalOutput::onSetup() {
 }
-void SensorMQ::setCalibrationSampleTimes(int value) {
-  _calibration_sample_times = value;
+
+// setter/getter
+void SensorDigitalOutput::setOnValue(int value) {
+  _on_value = value;
 }
-void SensorMQ::setCalibrationSampleInterval(int value){
-  _calibration_sample_interval = value;
+void SensorDigitalOutput::setLegacyMode(bool value) {
+  _legacy_mode = value;
 }
-void SensorMQ::setReadSampleTimes(int value) {
-  _read_sample_times = value;
+void SensorDigitalOutput::setSafeguard(int value) {
+  _safeguard_timer->set(value,MINUTES);
 }
-void SensorMQ::setReadSampleInterval(int value) {
-  _read_sample_interval = value;
+int SensorDigitalOutput::getStatus() {
+  return _status;
 }
-void SensorMQ::setLPGCurve(float *value) {
-  _LPGCurve[0] = value[0];
-  _LPGCurve[2] = value[1];
-  _LPGCurve[2] = value[2];
+void SensorDigitalOutput::setInputIsElapsed(bool value) {
+  _input_is_elapsed = value;
 }
-void SensorMQ::setCOCurve(float *value) {
-  _COCurve[0] = value[0];
-  _COCurve[2] = value[1];
-  _COCurve[2] = value[2];
-}
-void SensorMQ::setSmokeCurve(float *value) {
-  _SmokeCurve[0] = value[0];
-  _SmokeCurve[2] = value[1];
-  _SmokeCurve[2] = value[2];
-}
-
-// what to do during before
-void SensorMQ::onBefore() {
-  // prepare the pin for input
-  pinMode(_pin, INPUT);
+void SensorDigitalOutput::setWaitAfterSet(int value) {
+  _wait_after_set = value;
 }
 
-// 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);
+// 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 SensorMQ::onReceive(const MyMessage & message) {
-  if (message.getCommand() == C_REQ) onLoop();
-}
-
-// returns the calculated sensor resistance
-float SensorMQ::_MQResistanceCalculation(int raw_adc) {
-  return ( ((float)_rl_value*(1023-raw_adc)/raw_adc));
-}
-
-//  This function assumes that the sensor is in clean air
-float SensorMQ::_MQCalibration() {
-  int i;
-  float val=0;
-  //take multiple samples
-  for (i=0; i< _calibration_sample_times; i++) {  
-    val += _MQResistanceCalculation(analogRead(_pin));
-    wait(_calibration_sample_interval);
+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());
   }
-  //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);
+  if (message.getCommand() == C_REQ && ! _legacy_mode) {
+    // return the current status
+    _value_int = _status;
   }
-  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);
+// 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;
   }
-  return 0;
-}
-
-// returns ppm of the target gas
-int SensorMQ::_MQGetPercentage(float rs_ro_ratio, float *pcurve) {
-  return (pow(10,( ((log10(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0])));
-}
-
-
-/*
-   SensorDigitalInput
-*/
-
-// contructor
-SensorDigitalInput::SensorDigitalInput(int child_id, int pin): Sensor(child_id, pin) {
-}
-
-// what to do during before
-void SensorDigitalInput::onBefore() {
-  // set the pin for input
-  pinMode(_pin, INPUT);
+  _send(_msg_service.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;
 }
 
-// what to do during setup
-void SensorDigitalInput::onSetup() {
+// 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;
 }
 
-// what to do during loop
-void SensorDigitalInput::onLoop() {
-  // read the value
-  int value = digitalRead(_pin);
+// 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("D-IN I="));
+    Serial.print(F("DOUT I="));
     Serial.print(_child_id);
     Serial.print(F(" P="));
     Serial.print(_pin);
     Serial.print(F(" V="));
-    Serial.println(value);
+    Serial.println(value_to_write);
   #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();
+// 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;
 }
 
 
 /*
-   SensorDigitalOutput
+   SensorRelay
 */
 
 // contructor
-SensorDigitalOutput::SensorDigitalOutput(int child_id, int pin): Sensor(child_id, pin) {
+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);
 }
 
-// what to do during before
-void SensorDigitalOutput::onBefore() {
-  // set the pin as output and initialize it accordingly
-  pinMode(_pin, OUTPUT);
-  _state = _initial_value == LOW ? LOW : HIGH;
-  digitalWrite(_pin, _state);
-  // the initial value is now the current value
-  _value_int = _initial_value;
-}
+/*
+   SensorLatchingRelay
+*/
 
-// what to do during setup
-void SensorDigitalOutput::onSetup() {
+// 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 SensorDigitalOutput::setInitialValue(int value) {
-  _initial_value = value;
-}
-void SensorDigitalOutput::setPulseWidth(int value) {
+void SensorLatchingRelay::setPulseWidth(int value) {
   _pulse_width = value;
 }
-void SensorDigitalOutput::setOnValue(int value) {
-  _on_value = value;
-}
-void SensorDigitalOutput::setLegacyMode(bool value) {
-  _legacy_mode = value;
+void SensorLatchingRelay::setPinOn(int value) {
+  _pin_on = value;
 }
-
-// main task
-void SensorDigitalOutput::onLoop() {
-  // do nothing on loop
+void SensorLatchingRelay::setPinOff(int value) {
+  _pin_off = value;
 }
 
-// what to do as the main task when receiving a message
-void SensorDigitalOutput::onReceive(const MyMessage & message) {
-  // by default handle a SET message but when legacy mode is set when a REQ message is expected instead
-  if ( (message.getCommand() == C_SET && ! _legacy_mode) || (message.getCommand() == C_REQ && _legacy_mode)) {
-    // retrieve from the message the value to set
-    int value = message.getInt();
-    if (value != 0 && value != 1) return;
-    #if DEBUG == 1
-      Serial.print(F("DOUT I="));
-      Serial.print(_child_id);
-      Serial.print(F(" P="));
-      Serial.print(_pin);
-      Serial.print(F(" V="));
-      Serial.print(value);
-      Serial.print(F(" P="));
-      Serial.println(_pulse_width);
-    #endif
-    // reverse the value if needed
-    int value_to_write = value;
-    if (_on_value == LOW) {
-      if (value == HIGH) value_to_write = LOW;
-      if (value == LOW) value_to_write = HIGH;
-    }
-    // set the value
-    digitalWrite(_pin, value_to_write);
-    if (_pulse_width > 0) {
-      // if this is a pulse output, restore the value to the original value after the pulse
-      wait(_pulse_width);
-      digitalWrite(_pin, value_to_write == 0 ? HIGH: LOW);
-    }
-    // store the current value so it will be sent to the controller
-    _state = value;
-    _value_int = value;
+// 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;
   }
-  if (message.getCommand() == C_REQ && ! _legacy_mode) {
-    // return the current status
-    _value_int = _state;
+  switch(function) {
+    case 201: setPulseWidth(request.getValueInt()); break;
+    case 202: setPinOff(request.getValueInt()); break;
+    case 203: setPinOn(request.getValueInt()); break;
+    default: return;
   }
+  _send(_msg_service.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
 }
 
-/*
-   SensorRelay
-*/
-
-// contructor
-SensorRelay::SensorRelay(int child_id, int pin): SensorDigitalOutput(child_id, pin) {
-  // set presentation and type
-  setPresentation(S_BINARY);
-  setType(V_STATUS);
-}
-
-// define what to do during loop
-void SensorRelay::onLoop() {
-    // set the value to -1 so to avoid reporting to the gateway during loop
-    _value_int = -1;
-}
-
-/*
-   SensorLatchingRelay
-*/
-
-// contructor
-SensorLatchingRelay::SensorLatchingRelay(int child_id, int pin): SensorRelay(child_id, pin) {
-  // like a sensor with a default pulse set
-  setPulseWidth(50);
-}
-
+#endif
 /*
    SensorDHT
 */
 #if MODULE_DHT == 1
 // contructor
-SensorDHT::SensorDHT(int child_id, int pin, DHT* dht, int sensor_type, int dht_type): Sensor(child_id, pin) {
+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;
@@ -992,22 +1224,23 @@ SensorDHT::SensorDHT(int child_id, int pin, DHT* dht, int sensor_type, int dht_t
 
 // what to do during before
 void SensorDHT::onBefore() {
-    // initialize the dht library
-    _dht->begin();
 }
 
 // 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->readTemperature();
-    // convert it
-    if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32;
+    float temperature = _dht->getTemperature();
+    if (! _node_manager->getIsMetric()) temperature = _dht->toFahrenheit(temperature);
     #if DEBUG == 1
       Serial.print(F("DHT I="));
       Serial.print(_child_id);
@@ -1020,8 +1253,7 @@ void SensorDHT::onLoop() {
   // humidity sensor
   else if (_sensor_type == SensorDHT::HUMIDITY) {
     // read humidity
-    float humidity = _dht->readHumidity();
-    if (isnan(humidity)) return;
+    float humidity = _dht->getHumidity();
     #if DEBUG == 1
       Serial.print(F("DHT I="));
       Serial.print(_child_id);
@@ -1037,6 +1269,14 @@ void SensorDHT::onLoop() {
 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
 
 /*
@@ -1044,7 +1284,7 @@ void SensorDHT::onReceive(const MyMessage & message) {
 */
 #if MODULE_SHT21 == 1
 // contructor
-SensorSHT21::SensorSHT21(int child_id, int sensor_type): Sensor(child_id,A2) {
+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) {
@@ -1078,7 +1318,7 @@ void SensorSHT21::onLoop() {
     // read the temperature
     float temperature = SHT2x.GetTemperature();
     // convert it
-    if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32;
+    temperature = _node_manager->celsiusToFahrenheit(temperature);
     #if DEBUG == 1
       Serial.print(F("SHT I="));
       Serial.print(_child_id);
@@ -1108,6 +1348,14 @@ void SensorSHT21::onLoop() {
 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
 
 /*
@@ -1115,14 +1363,15 @@ void SensorSHT21::onReceive(const MyMessage & message) {
  */
  #if MODULE_SHT21 == 1
 // constructor
-SensorHTU21D::SensorHTU21D(int child_id, int pin): SensorSHT21(child_id, pin) {
+SensorHTU21D::SensorHTU21D(NodeManager* node_manager, int child_id, int pin): SensorSHT21(node_manager, child_id, pin) {
 }
 #endif 
 
+#if MODULE_SWITCH == 1
 /*
  * SensorSwitch
  */
-SensorSwitch::SensorSwitch(int child_id, int pin): Sensor(child_id,pin) {
+SensorSwitch::SensorSwitch(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id,pin) {
   setType(V_TRIPPED);
 }
 
@@ -1130,9 +1379,6 @@ SensorSwitch::SensorSwitch(int child_id, int pin): Sensor(child_id,pin) {
 void SensorSwitch::setMode(int value) {
   _mode = value;
 }
-int SensorSwitch::getMode() {
-  return _mode;
-}
 void SensorSwitch::setDebounce(int value) {
   _debounce = value;
 }
@@ -1142,25 +1388,47 @@ void SensorSwitch::setTriggerTime(int value) {
 void SensorSwitch::setInitial(int value) {
   _initial = value;
 }
-int SensorSwitch::getInitial() {
-  return _initial;
-}
 
 // what to do during before
 void SensorSwitch::onBefore() {
-  // initialize the value
-  if (_mode == RISING) _value_int = LOW;
-  else if (_mode == FALLING) _value_int = HIGH;
+  // 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;
+  }
+  _send(_msg_service.set(function));
+}
+
+// what to do when receiving an interrupt
+void SensorSwitch::onInterrupt() {
   // wait to ensure the the input is not floating
-  if (_debounce > 0) wait(_debounce);
+  if (_debounce > 0) _node_manager->sleepOrWait(_debounce);
   // read the value of the pin
   int value = digitalRead(_pin);
   // process the value
@@ -1175,41 +1443,36 @@ void SensorSwitch::onLoop() {
     #endif
     _value_int = value;
     // allow the signal to be restored to its normal value
-    if (_trigger_time > 0) wait(_trigger_time);
+    if (_trigger_time > 0) _node_manager->sleepOrWait(_trigger_time);
   } else {
     // invalid
     _value_int = -1;
   }
 }
-// what to do as the main task when receiving a message
-void SensorSwitch::onReceive(const MyMessage & message) {
-  if (message.getCommand() == C_REQ) onLoop();
-}
 
 /*
  * SensorDoor
  */
-SensorDoor::SensorDoor(int child_id, int pin): SensorSwitch(child_id,pin) {
+SensorDoor::SensorDoor(NodeManager* node_manager, int child_id, int pin): SensorSwitch(node_manager,child_id,pin) {
   setPresentation(S_DOOR);
 }
 
 /*
  * SensorMotion
  */
-SensorMotion::SensorMotion(int child_id, int pin): SensorSwitch(child_id,pin) {
+SensorMotion::SensorMotion(NodeManager* node_manager, int child_id, int pin): SensorSwitch(node_manager, child_id,pin) {
   setPresentation(S_MOTION);
-  // capture only when it triggers
-  setMode(RISING);
   // set initial value to LOW
   setInitial(LOW);
 }
+#endif
 
 /*
    SensorDs18b20
 */
 #if MODULE_DS18B20 == 1
 // contructor
-SensorDs18b20::SensorDs18b20(int child_id, int pin, DallasTemperature* sensors, int index): Sensor(child_id, pin) {
+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);
@@ -1241,7 +1504,7 @@ void SensorDs18b20::onLoop() {
   // read the temperature
   float temperature = _sensors->getTempCByIndex(_index);
   // convert it
-  if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32;
+  temperature = _node_manager->celsiusToFahrenheit(temperature);
   #if DEBUG == 1
     Serial.print(F("DS18B20 I="));
     Serial.print(_child_id);
@@ -1257,6 +1520,21 @@ 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;
+  }
+  _send(_msg_service.set(function));
+}
+
+// what to do when receiving an interrupt
+void SensorDs18b20::onInterrupt() {
+}
+
 // function to print a device address
 DeviceAddress* SensorDs18b20::getDeviceAddress() {
   return &_device_address;
@@ -1284,12 +1562,16 @@ void SensorDs18b20::setSleepDuringConversion(bool value) {
 */
 #if MODULE_BH1750 == 1
 // contructor
-SensorBH1750::SensorBH1750(int child_id): Sensor(child_id,A4) {
+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();
@@ -1315,6 +1597,21 @@ void SensorBH1750::onLoop() {
 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;
+  }
+  _send(_msg_service.set(function));
+}
+
+
+// what to do when receiving an interrupt
+void SensorBH1750::onInterrupt() {
+}
 #endif
 
 /*
@@ -1322,7 +1619,7 @@ void SensorBH1750::onReceive(const MyMessage & message) {
 */
 #if MODULE_MLX90614 == 1
 // contructor
-SensorMLX90614::SensorMLX90614(int child_id, Adafruit_MLX90614* mlx, int sensor_type): Sensor(child_id,A4) {
+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
@@ -1345,7 +1642,7 @@ void SensorMLX90614::onSetup() {
 void SensorMLX90614::onLoop() {
   float temperature = _sensor_type == SensorMLX90614::TEMPERATURE_OBJECT ? _mlx->readAmbientTempC() : _mlx->readObjectTempC();
   // convert it
-  if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32;
+  temperature = _node_manager->celsiusToFahrenheit(temperature);
   #if DEBUG == 1
     Serial.print(F("MLX I="));
     Serial.print(_child_id);
@@ -1359,15 +1656,23 @@ void SensorMLX90614::onLoop() {
 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
+#if MODULE_BME280 == 1 || MODULE_BMP085 == 1 || MODULE_BMP280 == 1
 // contructor
-SensorBosch::SensorBosch(int child_id, int sensor_type): Sensor(child_id,A4) {
+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
@@ -1419,8 +1724,22 @@ 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;
+  }
+  _send(_msg_service.set(function));
+}
 
-void SensorBosch::_forecast(float pressure) {
+// 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;
@@ -1532,7 +1851,7 @@ uint8_t SensorBosch::GetI2CAddress(uint8_t chip_id) {
  * SensorBME280
  */
 #if MODULE_BME280 == 1
-SensorBME280::SensorBME280(int child_id, Adafruit_BME280* bme, int sensor_type): SensorBosch(child_id,sensor_type) {
+SensorBME280::SensorBME280(NodeManager* node_manager, int child_id, Adafruit_BME280* bme, int sensor_type): SensorBosch(node_manager, child_id,sensor_type) {
   _bme = bme;
 }
 
@@ -1542,7 +1861,7 @@ void SensorBME280::onLoop() {
     // read the temperature
     float temperature = _bme->readTemperature();
     // convert it
-    if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32;
+    temperature = _node_manager->celsiusToFahrenheit(temperature);
     #if DEBUG == 1
       Serial.print(F("BME I="));
       Serial.print(_child_id);
@@ -1595,7 +1914,7 @@ void SensorBME280::onLoop() {
 */
 #if MODULE_BMP085 == 1
 // contructor
-SensorBMP085::SensorBMP085(int child_id, Adafruit_BMP085* bmp, int sensor_type): SensorBosch(child_id,sensor_type) {
+SensorBMP085::SensorBMP085(NodeManager* node_manager, int child_id, Adafruit_BMP085* bmp, int sensor_type): SensorBosch(node_manager, child_id,sensor_type) {
   _bmp = bmp;
 }
 
@@ -1606,7 +1925,7 @@ void SensorBMP085::onLoop() {
     // read the temperature
     float temperature = _bmp->readTemperature();
     // convert it
-    if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32;
+    temperature = _node_manager->celsiusToFahrenheit(temperature);
     #if DEBUG == 1
       Serial.print(F("BMP I="));
       Serial.print(_child_id);
@@ -1639,13 +1958,128 @@ void SensorBMP085::onLoop() {
 }
 #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;
+  }
+  _send(_msg_service.set(function));
+}
+
+// what to do when receiving an interrupt
+void SensorHCSR04::onInterrupt() {
+}
+#endif
 
 /*
    SensorSonoff
 */
 #if MODULE_SONOFF == 1
 // contructor
-SensorSonoff::SensorSonoff(int child_id): Sensor(child_id,1) {
+SensorSonoff::SensorSonoff(NodeManager* node_manager, int child_id): Sensor(node_manager, child_id,1) {
   setPresentation(S_BINARY);
   setType(V_STATUS);
 }
@@ -1683,8 +2117,6 @@ void SensorSonoff::onSetup() {
 
 // what to do during loop
 void SensorSonoff::onLoop() {
-  // set the value to -1 so to avoid reporting to the gateway during loop
-  _value_int = -1;
   _debouncer.update();
   // Get the update value from the button
   int value = _debouncer.read();
@@ -1710,6 +2142,22 @@ void SensorSonoff::onReceive(const MyMessage & message) {
   }
 }
 
+// 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;
+  }
+  _send(_msg_service.set(function));
+}
+
+// what to do when receiving an interrupt
+void SensorSonoff::onInterrupt() {
+}
+
 // toggle the state
 void SensorSonoff::_toggle() {
   // toggle the state
@@ -1740,64 +2188,12 @@ void SensorSonoff::_blink() {
 #endif
 
 
-/*
-   SensorHCSR04
-*/
-#if MODULE_HCSR04 == 1
-// contructor
-SensorHCSR04::SensorHCSR04(int child_id, int pin): Sensor(child_id, pin) {
-  // set presentation and type
-  setPresentation(S_DISTANCE);
-  setType(V_DISTANCE);
-  _trigger_pin = pin;
-  _echo_pin = pin;
-}
-
-// what to do during before
-void SensorHCSR04::onBefore() {
-  // initialize the library
-  _sonar = new NewPing(_trigger_pin,_echo_pin,_max_distance);
-}
-
-// setter/getter
-void SensorHCSR04::setTriggerPin(int value) {
-  _trigger_pin = value;
-}
-void SensorHCSR04::setEchoPin(int value) {
-  _echo_pin = value;
-}
-void SensorHCSR04::setMaxDistance(int value) {
-  _max_distance = value;
-}
-
-// what to do during setup
-void SensorHCSR04::onSetup() {
-}
-
-// what to do during loop
-void SensorHCSR04::onLoop() {
-  int distance = getControllerConfig().isMetric ? _sonar->ping_cm() : _sonar->ping_in();
-  #if DEBUG == 1
-    Serial.print(F("HC I="));
-    Serial.print(_child_id);
-    Serial.print(F(" D="));
-    Serial.println(distance);
-  #endif
-  _value_int = distance;
-}
-
-// what to do as the main task when receiving a message
-void SensorHCSR04::onReceive(const MyMessage & message) {
-  if (message.getCommand() == C_REQ) onLoop();
-}
-#endif
-
 /*
    SensorMCP9808
 */
 #if MODULE_MCP9808 == 1
 // contructor
-SensorMCP9808::SensorMCP9808(int child_id, Adafruit_MCP9808* mcp): Sensor(child_id,A2) {
+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);
@@ -1816,7 +2212,7 @@ void SensorMCP9808::onSetup() {
 void SensorMCP9808::onLoop() {
   float temperature = _mcp->readTempC();
   // convert it
-  if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32;
+  temperature = _node_manager->celsiusToFahrenheit(temperature);
   #if DEBUG == 1
     Serial.print(F("MCP I="));
     Serial.print(_child_id);
@@ -1831,144 +2227,974 @@ void SensorMCP9808::onLoop() {
 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
 
-/*******************************************
-   NodeManager
-*/
 
-// initialize the node manager
-NodeManager::NodeManager() {
-  // setup the service message container
-  _msg = MyMessage(CONFIGURATION_CHILD_ID, V_CUSTOM);
+/*
+ * 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 NodeManager::setRetries(int value) {
-  _retries = value;
+//setter/getter
+void SensorMQ::setTargetGas(int value) {
+  _target_gas = value;
 }
-#if BATTERY_MANAGER == 1
-  void NodeManager::setBatteryMin(float value) {
-    _battery_min = value;
-  }
-  void NodeManager::setBatteryMax(float value) {
-    _battery_max = value;
-  }
-  void NodeManager::setBatteryReportCycles(int value) {
-    _battery_report_cycles = value;
-  }
-  void NodeManager::setBatteryInternalVcc(bool value) {
-    _battery_internal_vcc = value;
-  }
-  void NodeManager::setBatteryPin(int value) {
-    _battery_pin = value;
-  }
-  void NodeManager::setBatteryVoltsPerBit(float value) {
-    _battery_volts_per_bit = value;
-  }
-  void NodeManager::setBatteryReportWithInterrupt(bool value) {
-    _battery_report_with_interrupt = value;
-  }
-#endif
-void NodeManager::setSleepMode(int value) {
-  _sleep_mode = value;
+void SensorMQ::setRlValue(float value) {
+  _rl_value = value;
 }
-void NodeManager::setMode(int value) {
-  setSleepMode(value);
+void SensorMQ::setRoValue(float value) {
+  _ro = value;
 }
-void NodeManager::setSleepTime(int value) {
-  _sleep_time = value;
+void SensorMQ::setCleanAirFactor(float value) {
+  _ro_clean_air_factor = value;
 }
-void NodeManager::setSleepUnit(int value) {
-  _sleep_unit = value;
+void SensorMQ::setCalibrationSampleTimes(int value) {
+  _calibration_sample_times = value;
 }
-void NodeManager::setSleep(int value1, int value2, int value3) {
-  _sleep_mode = value1;
-  _sleep_time = value2;
-  _sleep_unit = value3;
+void SensorMQ::setCalibrationSampleInterval(int value){
+  _calibration_sample_interval = value;
 }
-void NodeManager::setSleepInterruptPin(int value) {
-  _sleep_interrupt_pin = value;
+void SensorMQ::setReadSampleTimes(int value) {
+  _read_sample_times = value;
 }
-void NodeManager::setInterrupt(int pin, int mode, int pull) {
-  if (pin == INTERRUPT_PIN_1) {
-    _interrupt_1_mode = mode;
-    _interrupt_1_pull = pull;
-  }
-  if (pin == INTERRUPT_PIN_2) {
-    _interrupt_2_mode = mode;
-    _interrupt_2_pull = pull;
-  }
+void SensorMQ::setReadSampleInterval(int value) {
+  _read_sample_interval = 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;
+void SensorMQ::setLPGCurve(float *value) {
+  _LPGCurve = value;
 }
-void NodeManager::setAck(bool value) {
-    _ack = value;
+void SensorMQ::setCOCurve(float *value) {
+  _COCurve = value;
+}
+void SensorMQ::setSmokeCurve(float *value) {
+  _SmokeCurve = value;
 }
 
-// register a sensor to this manager
-int NodeManager::registerSensor(int sensor_type, int pin, int child_id) {
-  // get a child_id if not provided by the user
-  if (child_id < 0) child_id = _getAvailableChildId();
-  // based on the given sensor type instantiate the appropriate class
-  if (sensor_type == 0) return -1;
-  #if MODULE_ANALOG_INPUT == 1
-    else if (sensor_type == SENSOR_ANALOG_INPUT) return registerSensor(new SensorAnalogInput(child_id, pin));
-    else if (sensor_type == SENSOR_LDR) return registerSensor(new SensorLDR(child_id, pin));
-    else if (sensor_type == SENSOR_THERMISTOR) return registerSensor(new SensorThermistor(child_id, pin));
-    else if (sensor_type == SENSOR_MQ) return registerSensor(new SensorMQ(child_id, pin));
-    else if (sensor_type == SENSOR_ML8511) return registerSensor(new SensorML8511(child_id, pin));
-    else if (sensor_type == SENSOR_ACS712) return registerSensor(new SensorACS712(child_id, pin));
-    else if (sensor_type == SENSOR_RAIN_GAUGE) return registerSensor(new SensorRainGauge(child_id, pin));
-  #endif
-  #if MODULE_DIGITAL_INPUT == 1
-    else if (sensor_type == SENSOR_DIGITAL_INPUT) return registerSensor(new SensorDigitalInput(child_id, pin));
-  #endif
-  #if MODULE_DIGITAL_OUTPUT == 1
-    else if (sensor_type == SENSOR_DIGITAL_OUTPUT) return registerSensor(new SensorDigitalOutput(child_id, pin));
-    else if (sensor_type == SENSOR_RELAY) return registerSensor(new SensorRelay(child_id, pin));
-    else if (sensor_type == SENSOR_LATCHING_RELAY) return registerSensor(new SensorLatchingRelay(child_id, pin));
-  #endif
-  #if MODULE_DHT == 1
-    else if (sensor_type == SENSOR_DHT11 || sensor_type == SENSOR_DHT22) {
-      int dht_type = sensor_type == SENSOR_DHT11 ? DHT11 : DHT22;
-      DHT* dht = new DHT(pin,dht_type);
-      // register temperature sensor
-      registerSensor(new SensorDHT(child_id,pin,dht,SensorDHT::TEMPERATURE,dht_type));
-      // register humidity sensor
-      child_id = _getAvailableChildId();
-      return registerSensor(new SensorDHT(child_id,pin,dht,SensorDHT::HUMIDITY,dht_type));
+// 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;
+  }
+  _send(_msg_service.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;
+  }
+  _send(_msg_service.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;
+  }
+  _send(_msg_service.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;
+  }
+  _send(_msg_service.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;
+  }
+  _send(_msg_service.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;
+  }
+  _send(_msg_service.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 service message container
+  _msg = MyMessage(CONFIGURATION_CHILD_ID, V_CUSTOM);
+}
+
+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;
+}
+#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(child_id,SensorSHT21::TEMPERATURE));
+      registerSensor(new SensorSHT21(this,child_id,SensorSHT21::TEMPERATURE));
       // register humidity sensor
       child_id = _getAvailableChildId();
-      return registerSensor(new SensorSHT21(child_id,SensorSHT21::HUMIDITY));
+      return registerSensor(new SensorSHT21(this,child_id,SensorSHT21::HUMIDITY));
     }
     else if (sensor_type == SENSOR_HTU21D) {
       // register temperature sensor
-      registerSensor(new SensorHTU21D(child_id,SensorHTU21D::TEMPERATURE));
+      registerSensor(new SensorHTU21D(this,child_id,SensorHTU21D::TEMPERATURE));
       // register humidity sensor
       child_id = _getAvailableChildId();
-      return registerSensor(new SensorHTU21D(child_id,SensorHTU21D::HUMIDITY));
+      return registerSensor(new SensorHTU21D(this,child_id,SensorHTU21D::HUMIDITY));
     }
   #endif
   #if MODULE_SWITCH == 1
@@ -1976,15 +3202,9 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) {
       // ensure an interrupt pin is provided
       if (pin != INTERRUPT_PIN_1 && pin != INTERRUPT_PIN_2) return -1;
       // register the sensor
-      int index = 0;
-      if (sensor_type == SENSOR_SWITCH) index = registerSensor(new SensorSwitch(child_id, pin));
-      else if (sensor_type == SENSOR_DOOR) index = registerSensor(new SensorDoor(child_id, pin));
-      else if (sensor_type == SENSOR_MOTION) index = registerSensor(new SensorMotion(child_id, pin));
-      // set an interrupt on the pin and set the initial value
-      SensorSwitch* sensor = (SensorSwitch*)getSensor(index);
-      sensor->setInterruptPin(pin);
-      setInterrupt(pin,sensor->getMode(),sensor->getInitial());
-      return index;
+      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
@@ -1998,24 +3218,22 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) {
       // register a new child for each sensor on the bus
       for(int i = 0; i < sensors->getDeviceCount(); i++) {
         if (i > 0) child_id = _getAvailableChildId();
-        index = registerSensor(new SensorDs18b20(child_id,pin,sensors,i));
+        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(child_id));
-    }
+    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(child_id,mlx,SensorMLX90614::TEMPERATURE_AMBIENT));
+      registerSensor(new SensorMLX90614(this,child_id,mlx,SensorMLX90614::TEMPERATURE_AMBIENT));
       // register object temperature sensor
       child_id = _getAvailableChildId();
-      return registerSensor(new SensorMLX90614(child_id,mlx,SensorMLX90614::TEMPERATURE_OBJECT));
+      return registerSensor(new SensorMLX90614(this,child_id,mlx,SensorMLX90614::TEMPERATURE_OBJECT));
     }
   #endif
   #if MODULE_BME280 == 1
@@ -2023,27 +3241,45 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) {
       Adafruit_BME280* bme = new Adafruit_BME280();
       if (! bme->begin(SensorBosch::GetI2CAddress(0x60))) {
         #if DEBUG == 1
-          Serial.println(F("NO BME"));
+          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 SensorBME280(child_id,bme,SensorBME280::TEMPERATURE));
+      registerSensor(new SensorBMP280(this,child_id,bmp,SensorBMP280::TEMPERATURE));
       child_id = _getAvailableChildId();
-      // register humidity sensor
-      registerSensor(new SensorBME280(child_id,bme,SensorBME280::HUMIDITY));
       // register pressure sensor
       child_id = _getAvailableChildId();
-      registerSensor(new SensorBME280(child_id,bme,SensorBME280::PRESSURE));
+      registerSensor(new SensorBMP280(this,child_id,bmp,SensorBMP280::PRESSURE));
       // register forecast sensor
       child_id = _getAvailableChildId();
-      return registerSensor(new SensorBME280(child_id,bme,SensorBME280::FORECAST));
+      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(child_id));
-    }
+    else if (sensor_type == SENSOR_SONOFF) return registerSensor(new SensorSonoff(this,child_id));
   #endif
   #if MODULE_BMP085 == 1
     else if (sensor_type == SENSOR_BMP085) {
@@ -2055,19 +3291,17 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) {
         return -1;
       }
       // register temperature sensor
-      registerSensor(new SensorBMP085(child_id,bmp,SensorBMP085::TEMPERATURE));
+      registerSensor(new SensorBMP085(this,child_id,bmp,SensorBMP085::TEMPERATURE));
       // register pressure sensor
       child_id = _getAvailableChildId();
-      registerSensor(new SensorBMP085(child_id,bmp,SensorBMP085::PRESSURE));
+      registerSensor(new SensorBMP085(this,child_id,bmp,SensorBMP085::PRESSURE));
       // register forecast sensor
       child_id = _getAvailableChildId();
-      return registerSensor(new SensorBMP085(child_id,bmp,SensorBMP085::FORECAST));
+      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(child_id, pin));
-    }
+    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) {
@@ -2079,9 +3313,39 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) {
         return -1;
       }
       // register temperature sensor
-      registerSensor(new SensorMCP9808(child_id,mcp));
+      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 "));
@@ -2147,61 +3411,65 @@ bool NodeManager::renameSensor(int old_child_id, int new_child_id) {
 
 // setup NodeManager
 void NodeManager::before() {
+  // print out the version
   #if DEBUG == 1
     Serial.print(F("NodeManager v"));
     Serial.println(VERSION);
   #endif
-  // setup the sleep interrupt pin
-  if (_sleep_interrupt_pin > -1) {
-    // set the interrupt when the pin is connected to ground
-    setInterrupt(_sleep_interrupt_pin,FALLING,HIGH);
-  }
-  // setup the interrupt pins
-  if (_interrupt_1_mode != MODE_NOT_DEFINED) {
-    pinMode(INTERRUPT_PIN_1,INPUT);
-    if (_interrupt_1_pull > -1) digitalWrite(INTERRUPT_PIN_1,_interrupt_1_pull);
-  }
-  if (_interrupt_2_mode != MODE_NOT_DEFINED) {
-    pinMode(INTERRUPT_PIN_2, INPUT);
-    if (_interrupt_2_pull > -1) digitalWrite(INTERRUPT_PIN_2,_interrupt_2_pull);
+  // 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("INT1 M="));
-    Serial.println(_interrupt_1_mode);
-    Serial.print(F("INT2 M="));
-    Serial.println(_interrupt_2_mode);
+    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 REMOTE_CONFIGURATION == 1 && PERSIST == 1
-    // restore sleep configuration from eeprom
-    if (loadState(EEPROM_SLEEP_SAVED) == 1) {
-      // sleep settings found in the eeprom, restore them
-      _sleep_mode = loadState(EEPROM_SLEEP_MODE);
-      _sleep_time = loadState(EEPROM_SLEEP_TIME_MINOR);
-      int major = loadState(EEPROM_SLEEP_TIME_MAJOR);
-      if (major == 1) _sleep_time =  _sleep_time + 250;
-      else if (major == 2) _sleep_time =  _sleep_time + 250 * 2;
-      else if (major == 3) _sleep_time =  _sleep_time + 250 * 3;
-      _sleep_unit = loadState(EEPROM_SLEEP_UNIT);
-      #if DEBUG == 1
-        Serial.print(F("LOADSLP M="));
-        Serial.print(_sleep_mode);
-        Serial.print(F(" T="));
-        Serial.print(_sleep_time);
-        Serial.print(F(" U="));
-        Serial.println(_sleep_unit);
-      #endif
-    }
+  #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;
-    // call each sensor's setup()
+    // 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
@@ -2218,7 +3486,13 @@ void NodeManager::presentation() {
     // present the battery service
     _present(BATTERY_CHILD_ID, S_MULTIMETER);
     // report battery level
-    _process("BATTERY");
+    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++) {
@@ -2236,11 +3510,13 @@ void NodeManager::presentation() {
 
 // 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(getControllerConfig().isMetric);
+    Serial.println(_is_metric);
   #endif
   #if SERVICE_MESSAGES == 1
     _send(_msg.set("STARTED"));
@@ -2256,29 +3532,54 @@ void NodeManager::setup() {
 // run the main function for all the register sensors
 void NodeManager::loop() {
   MyMessage empty;
-  // if in idle mode, do nothing
-  if (_sleep_mode == IDLE) return;
-  // if sleep time is not set, do nothing
-  if ((_sleep_mode == SLEEP || _sleep_mode == WAIT) &&  _sleep_time == 0) return;
-    #if POWER_MANAGER == 1
-      // turn on the pin powering all the sensors
-      if (_auto_power_pins) powerOn();
-    #endif
-    // run loop for all the registered sensors
-    for (int i = 1; i <= MAX_SENSORS; i++) {
-      // skip not configured sensors
-      if (_sensors[i] == 0) continue;
-      // if waking up from an interrupt skip all the sensor without that interrupt configured
-      if (_last_interrupt_pin != -1 && _sensors[i]->getInterruptPin() != _last_interrupt_pin) continue;
-      // call each sensor's loop()
+  #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()
+      _sensors[i]->interrupt();
       _sensors[i]->loop(empty);
+        // reset the last interrupt pin
+      _last_interrupt_pin = -1;
     }
-    #if POWER_MANAGER == 1
-      // turn off the pin powering all the sensors
-      if (_auto_power_pins) powerOff();
-    #endif
-    // continue/start sleeping as requested
-    if (_sleep_mode == SLEEP || _sleep_mode == WAIT) _sleep();
+    else if (_last_interrupt_pin == -1) {
+      // if just at the end of a cycle, call the sensor's loop() 
+      _sensors[i]->loop(empty);
+    }
+  }
+  #if POWER_MANAGER == 1
+    // turn off the pin powering all the sensors
+    if (_auto_power_pins) powerOff();
+  #endif
+  // continue/start sleeping as requested
+  if (isSleepingNode()) _sleep();
 }
 
 // dispacth inbound messages
@@ -2295,9 +3596,14 @@ void NodeManager::receive(const MyMessage &message) {
     Serial.print(F(" P="));
     Serial.println(message.getString());
   #endif
-  // process incoming service messages
+  // process incoming configuration message
   if (message.sensor == CONFIGURATION_CHILD_ID && message.getCommand() == C_REQ && message.type == V_CUSTOM) {
-    _process(message.getString());
+    #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) {
@@ -2325,7 +3631,7 @@ long NodeManager::getTimestamp() {
     // request the time to the controller
     requestTime();
     // keep asking every 1 second
-    wait(1000);
+    sleepOrWait(1000);
     retries--;
   }  
   return _timestamp;
@@ -2340,6 +3646,330 @@ void NodeManager::receiveTime(unsigned long ts) {
   #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; 
+  }
+  _send(_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
+    MyMessage battery_msg(BATTERY_CHILD_ID, V_VOLTAGE);
+    _send(battery_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<<WDCE) | (1<<WDE);
+    // Reset enable
+    WDTCSR= (1<<WDE);
+    // Infinite loop until watchdog reset after 16 ms
+    while(true){}
+  }
+}
+
+// send NodeManager's the version back to the controller
+void NodeManager::version() {
+  _send(_msg.set(VERSION));
+}
+
+// clear the EEPROM
+void NodeManager::clearEeprom() {
+  #if DEBUG == 1
+    Serial.println(F("CLEAR"));
+  #endif
+  for (uint16_t i=0; i<EEPROM_LOCAL_CONFIG_ADDRESS; i++) saveState(i, 0xFF);
+}
+
+// wake up the board
+void NodeManager::wakeup() {
+  #if DEBUG == 1
+    Serial.println(F("WAKEUP"));
+  #endif
+  _status = AWAKE;
+}
+
+// return the value stored at the requested index from the EEPROM
+int NodeManager::loadFromMemory(int index) {
+  return loadState(index+EEPROM_USER_START);
+}
+
+// save the given index of the EEPROM the provided value
+void NodeManager::saveToMemory(int index, int value) {
+  saveState(index+EEPROM_USER_START, value);
+}
+
+// return vcc in V
+float NodeManager::getVcc() {
+  #ifndef MY_GATEWAY_ESP8266
+    // Measure Vcc against 1.1V Vref
+    #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+      ADMUX = (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
+    #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
+      ADMUX = (_BV(MUX5) | _BV(MUX0));
+    #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
+      ADMUX = (_BV(MUX3) | _BV(MUX2));
+    #else
+      ADMUX = (_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
+    #endif
+    // Vref settle
+    wait(70);
+    // Do conversion
+    ADCSRA |= _BV(ADSC);
+    while (bit_is_set(ADCSRA, ADSC)) {};
+    // return Vcc in mV
+    return (float)((1125300UL) / ADC) / 1000;
+  #else
+    return (float)0;
+  #endif
+}
+
+// setup the interrupt pins
+void NodeManager::setupInterrupts() {
+  // configure wakeup pin if needed
+  if (_sleep_interrupt_pin > -1) {
+    // set the interrupt when the pin is connected to ground
+    setInterrupt(_sleep_interrupt_pin,FALLING,HIGH);
+  }
+  // setup the interrupt pins
+  if (_interrupt_1_mode != MODE_NOT_DEFINED) {
+    pinMode(INTERRUPT_PIN_1,INPUT);
+    if (_interrupt_1_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
+    MyMessage signal_msg(SIGNAL_CHILD_ID, V_LEVEL);
+    _send(signal_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
 void NodeManager::_send(MyMessage & message) {
   // send the message, multiple times if requested
@@ -2366,171 +3996,11 @@ void NodeManager::_send(MyMessage & message) {
   }
 }
 
-// process a service message
-void NodeManager::_process(const char * message) {
-  // HELLO: hello request
-  if (strcmp(message, "HELLO") == 0) {
-    _send(_msg.set(message));
-  }
-  #if BATTERY_MANAGER == 1
-    // BATTERY: return the battery level
-    else if (strcmp(message, "BATTERY") == 0) {
-      // measure the board vcc
-      float volt = 0;
-      if (_battery_internal_vcc || _battery_pin == -1) volt = getVcc();
-      else volt = analogRead(_battery_pin) * _battery_volts_per_bit;
-      // calculate the percentage
-      int percentage = ((volt - _battery_min) / (_battery_max - _battery_min)) * 100;
-      if (percentage > 100) percentage = 100;
-      if (percentage < 0) percentage = 0;
-      #if DEBUG == 1
-        Serial.print(F("BATT V="));
-        Serial.print(volt);
-        Serial.print(F(" P="));
-        Serial.println(percentage);
-      #endif
-      #if BATTERY_SENSOR == 1
-        // report battery voltage
-        MyMessage battery_msg(BATTERY_CHILD_ID, V_VOLTAGE);
-        _send(battery_msg.set(volt, 2));
-      #endif
-      // report battery level percentage
-      sendBatteryLevel(percentage,_ack);
-    }
-  #endif
-  #ifndef MY_GATEWAY_ESP8266
-    // REBOOT: reboot the board
-    else if (strcmp(message, "REBOOT") == 0) {
-      #if DEBUG == 1
-        Serial.println(F("REBOOT"));
-      #endif
-      // set the reboot pin connected to RST to low so to reboot the board
-      _send(_msg.set(message));
-      // Software reboot with watchdog timer. Enter Watchdog Configuration mode:
-      WDTCSR |= (1<<WDCE) | (1<<WDE);
-      // Reset enable
-      WDTCSR= (1<<WDE);
-      // Infinite loop until watchdog reset after 16 ms
-      while(true){}
-    }
-  #endif
-  // CLEAR: clear the user's eeprom
-  else if (strcmp(message, "CLEAR") == 0) {
-    #if DEBUG == 1
-      Serial.println(F("CLEAR"));
-    #endif
-    for (int i = 0; i <= EEPROM_LAST_ID; i++) saveState(i, 0xFF);
-    _send(_msg.set(message));
-  }
-  // VERSION: send back the extension's version
-  else if (strcmp(message, "VERSION") == 0) {
-    _send(_msg.set(VERSION));
-  }
-  #if REMOTE_CONFIGURATION == 1
-    // IDxxx: change the node id to the provided one. E.g. ID025: change the node id to 25. Requires a reboot/restart
-    else if (strlen(message) == 5 && strncmp("ID", message, strlen("ID")) == 0) {
-      // extract the node id
-      char s[4];
-      s[0] = message[2];
-      s[1] = message[3];
-      s[2] = message[4];
-      s[3] = '\0';
-      int node_id = atoi(s);
-      #if DEBUG == 1
-        Serial.print(F("MY I="));
-        Serial.println(node_id);
-      #endif
-      #ifndef MY_GATEWAY_ESP8266
-        // Save static ID to eeprom
-        //hwWriteConfig(EEPROM_NODE_ID_ADDRESS, (uint8_t)node_id);
-      #endif
-      // reboot the board
-      _process("REBOOT");
-    }
-    // MODEx: change the way the node behaves. 0: stay awake withtout executing each sensors' loop(), 1: go to sleep for the configured interval, 2: wait for the configured interval, 3: stay awake and execute each sensors' loop() (e.g. MODE1)
-    else if (strlen(message) == 5 && strncmp("MODE", message, strlen("MODE")) == 0) {
-      // extract mode
-      char s[2];
-      s[0] = message[4];
-      s[1] = '\0';
-      _sleep_mode = atoi(s);
-      #if DEBUG == 1
-        Serial.print(F("SLEEP M="));
-        Serial.println(_sleep_mode);
-      #endif
-      #if PERSIST == 1
-        // save it to the eeprom
-        saveState(EEPROM_SLEEP_SAVED, 1);
-        saveState(EEPROM_SLEEP_MODE, _sleep_mode);
-      #endif
-      _send(_msg.set(message));
-    }
-    // INTVLnnnX: set and save the wait/sleep interval to nnn where X is S=Seconds, M=mins, H=Hours, D=Days. E.g. INTVL010M would be 10 minutes
-    else if (strlen(message) == 9 && strncmp("INTVL", message, strlen("INTVL")) == 0) {
-      // parse and set the sleep interval
-      int offset = 5;
-      // extract the unit (S=secs, M=mins, H=hours, D=Days)
-      char unit[2];
-      sprintf(unit, "%c", message[3 + offset]);
-      unit[1] = '\0';
-      if (strcmp(unit, "S") == 0) _sleep_unit = SECONDS;
-      else if (strcmp(unit, "M") == 0) _sleep_unit = MINUTES;
-      else if (strcmp(unit, "H") == 0) _sleep_unit = HOURS;
-      else if (strcmp(unit, "D") == 0) _sleep_unit = DAYS;
-      else return;
-      // extract the requested time
-      char s[4];
-      s[0] = message[0 + offset];
-      s[1] = message[1 + offset];
-      s[2] = message[2 + offset];
-      s[3] = '\0';
-      _sleep_time = atoi(s);
-      #if DEBUG == 1
-        Serial.print(F("SLEEP T="));
-        Serial.print(_sleep_time);
-        Serial.print(F(" U="));
-        Serial.println(_sleep_unit);
-      #endif
-      #if PERSIST == 1
-        // save it to eeprom
-        saveState(EEPROM_SLEEP_UNIT, _sleep_unit);
-        // encode sleep time
-        int major = 0;
-        if (_sleep_time > 750) major = 3;
-        else if (_sleep_time > 500) major = 2;
-        else if (_sleep_time > 250) major = 1;
-        int minor = _sleep_time - 250 * major;
-        saveState(EEPROM_SLEEP_SAVED, 1);
-        saveState(EEPROM_SLEEP_TIME_MINOR, minor);
-        saveState(EEPROM_SLEEP_TIME_MAJOR, major);
-      #endif
-      // interval set, reply back with the same message to acknowledge.
-      _send(_msg.set(message));
-    }
-  #endif
-  // WAKEUP: when received after a sleeping cycle or during wait, abort the cycle and stay awake
-  else if (strcmp(message, "WAKEUP") == 0) {
-    #if DEBUG == 1
-      Serial.println(F("WAKEUP"));
-    #endif
-    _send(_msg.set(message));
-    _sleep_mode = IDLE;
-  }
-}
-
 // wrapper of smart sleep
 void NodeManager::_sleep() {
-  // reset the last interrupt pin
-  _last_interrupt_pin = -1;
-  // calculate the seconds to sleep
-  long sleep_sec = _sleep_time;
-  if (_sleep_unit == MINUTES) sleep_sec = sleep_sec * 60;
-  else if (_sleep_unit == HOURS) sleep_sec = sleep_sec * 3600;
-  else if (_sleep_unit == DAYS) sleep_sec = sleep_sec * 43200;
-  long sleep_ms = sleep_sec * 1000;
   #if DEBUG == 1
     Serial.print(F("SLEEP "));
-    Serial.print(sleep_sec);
+    Serial.print(_sleep_time);
     Serial.println(F("s"));
   #endif
   #if SERVICE_MESSAGES == 1
@@ -2543,41 +4013,33 @@ void NodeManager::_sleep() {
   #endif
   // go to sleep
   int interrupt = -1;
-  if (_sleep_mode == WAIT) {
-    // wait for the given interval
-    wait(sleep_ms);
-    // send heartbeat to the controller
-    sendHeartbeat(_ack);
-  }
-  else if (_sleep_mode == SLEEP) {
-    // setup interrupt pins
-    int interrupt_1_pin = _interrupt_1_mode == MODE_NOT_DEFINED ? INTERRUPT_NOT_DEFINED  : digitalPinToInterrupt(INTERRUPT_PIN_1);
-    int interrupt_2_pin = _interrupt_2_mode == MODE_NOT_DEFINED ? INTERRUPT_NOT_DEFINED  : digitalPinToInterrupt(INTERRUPT_PIN_2);
-    // enter smart sleep for the requested sleep interval and with the configured interrupts
-    interrupt = sleep(interrupt_1_pin,_interrupt_1_mode,interrupt_2_pin,_interrupt_2_mode,sleep_ms, true);
-    if (interrupt > -1) {
-      // woke up by an interrupt
-      int pin_number = -1;
-      int interrupt_mode = -1;
-      // map the interrupt to the pin
-      if (digitalPinToInterrupt(INTERRUPT_PIN_1) == interrupt) {
-        pin_number = INTERRUPT_PIN_1;
-        interrupt_mode = _interrupt_1_mode;
-      }
-      if (digitalPinToInterrupt(INTERRUPT_PIN_2) == interrupt) {
-        pin_number = INTERRUPT_PIN_2;
-        interrupt_mode = _interrupt_2_mode;
-      }
-      _last_interrupt_pin = pin_number;
-      #if DEBUG == 1
-        Serial.print(F("WAKE P="));
-        Serial.print(pin_number);
-        Serial.print(F(", M="));
-        Serial.println(interrupt_mode);
-      #endif
-      // when waking up from an interrupt on the wakup pin, stop sleeping
-      if (_sleep_interrupt_pin == pin_number) _sleep_mode = IDLE;
+  // 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
@@ -2587,16 +4049,6 @@ void NodeManager::_sleep() {
     // notify the controller I am awake
     _send(_msg.set("AWAKE"));
   #endif
-  #if BATTERY_MANAGER == 1
-    // keep track of the number of sleeping cycles (ignoring if woke up by an interrupt)
-    if (interrupt == -1 || _battery_report_with_interrupt) _cycles++;
-    // battery has to be reported after the configured number of sleep cycles
-    if (_battery_report_cycles == _cycles) {
-      // time to report the battery level again
-      _process("BATTERY");
-      _cycles = 0;
-    }
-  #endif
 }
 
 // present the service
@@ -2629,4 +4081,38 @@ int NodeManager::_getInterruptInitialValue(int mode) {
   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.h b/NodeManager.h
index c56a30fe8a6a05ee6885e74bfa2e22c6573a777e..c3bbee9fe09febd99892b57e677b52fd1b27eff7 100644
--- a/NodeManager.h
+++ b/NodeManager.h
@@ -7,17 +7,15 @@
 #include <Arduino.h>
 
 // define NodeManager version
-#define VERSION "1.5.1"
+#define VERSION "1.6"
 
 /***********************************
    Constants
 */
 
-// define sleep mode
-#define IDLE 0
+// define board status
+#define AWAKE 0
 #define SLEEP 1
-#define WAIT 2
-#define ALWAYS_ON 3
 
 // define time unit
 #define SECONDS 0
@@ -25,22 +23,28 @@
 #define HOURS 2
 #define DAYS 3
 
+// define on/off
+#define OFF 0
+#define ON 1
+
 // define value type
 #define TYPE_INTEGER 0
 #define TYPE_FLOAT 1
 #define TYPE_STRING 2
+#define TYPE_DOUBLE 2
 
 // define interrupt pins
 #define INTERRUPT_PIN_1 3
 #define INTERRUPT_PIN_2 2
 
 // define eeprom addresses
-#define EEPROM_LAST_ID 4
 #define EEPROM_SLEEP_SAVED 0
-#define EEPROM_SLEEP_MODE 1
-#define EEPROM_SLEEP_TIME_MAJOR 2
-#define EEPROM_SLEEP_TIME_MINOR 3
-#define EEPROM_SLEEP_UNIT 4
+#define EEPROM_SLEEP_1 5
+#define EEPROM_SLEEP_2 6
+#define EEPROM_SLEEP_3 7
+#define EEPROM_USER_START 100
+
+// define requests
 
 /************************************
  * Include user defined configuration settings
@@ -77,12 +81,45 @@
 #ifndef SERVICE_MESSAGES
   #define SERVICE_MESSAGES 0
 #endif
-// if enabled, a battery sensor will be created at BATTERY_CHILD_ID and will report vcc voltage together with the battery level percentage
+// 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
+#endif
+// the child id used to report the battery voltage to the controller
+#ifndef BATTERY_CHILD_ID
+  #define BATTERY_CHILD_ID 201
+#endif
+// the child id used to report the rssi level to the controller
+#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"
+#endif
+#ifndef SKETCH_VERSION
+  #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_MQ, SENSOR_ACS712
+// 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
@@ -94,14 +131,14 @@
 #ifndef MODULE_DIGITAL_OUTPUT
   #define MODULE_DIGITAL_OUTPUT 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_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
@@ -138,102 +175,159 @@
 #ifndef MODULE_MCP9808
   #define MODULE_MCP9808 0
 #endif
-
-// the child id used to allow remote configuration
-#ifndef CONFIGURATION_CHILD_ID
-  #define CONFIGURATION_CHILD_ID 200
+// Enable this module to use one of the following sensors: SENSOR_MQ
+#ifndef MODULE_MQ
+  #define MODULE_MQ 0
 #endif
-// the child id used to report the battery voltage to the controller
-#ifndef BATTERY_CHILD_ID
-  #define BATTERY_CHILD_ID 201
+// Enable this module to use one of the following sensors: SENSOR_MHZ19
+#ifndef MODULE_MHZ19
+  #define MODULE_MHZ19 0
 #endif
-// define the maximum number of sensors that can be managed
-#ifndef MAX_SENSORS
-  #define MAX_SENSORS 10
+// Enable this module to use one of the following sensors: SENSOR_AM2320
+#ifndef MODULE_AM2320
+  #define MODULE_AM2320 0
 #endif
-
-/***********************************
-   Sensors types
-*/
-#if MODULE_ANALOG_INPUT == 1
-  // Generic analog sensor, return a pin's analog value or its percentage
-  #define SENSOR_ANALOG_INPUT 1
-  // LDR sensor, return the light level of an attached light resistor in percentage
-  #define SENSOR_LDR 2
-  // Thermistor sensor, return the temperature based on the attached thermistor
-  #define SENSOR_THERMISTOR 3
-  // MQ2 air quality sensor
-  #define SENSOR_MQ 19
-  // ML8511 UV sensor
-  #define SENSOR_ML8511 20
-  // Current sensor
-  #define SENSOR_ACS712 24
-  // rain gauge sensor
-  #define SENSOR_RAIN_GAUGE 26
+// Enable this module to use one of the following sensors: SENSOR_TSL2561
+#ifndef MODULE_TSL2561
+  #define MODULE_TSL2561 0
 #endif
-#if MODULE_DIGITAL_INPUT == 1
-  // Generic digital sensor, return a pin's digital value
-  #define SENSOR_DIGITAL_INPUT 4
+// Enable this module to use one of the following sensors: SENSOR_PT100
+#ifndef MODULE_PT100
+  #define SENSOR_PT100 0
 #endif
-#if MODULE_DIGITAL_OUTPUT == 1
-  // Generic digital output sensor, allows setting the digital output of a pin to the requested value
-  #define SENSOR_DIGITAL_OUTPUT 5
-  // Relay sensor, allows activating the relay
-  #define SENSOR_RELAY 6
-  // Latching Relay sensor, allows activating the relay with a pulse
-  #define SENSOR_LATCHING_RELAY 7
+// Enable this module to use one of the following sensors: SENSOR_BMP280
+#ifndef MODULE_BMP280
+  #define MODULE_BMP280 0
 #endif
-#if MODULE_DHT == 1
-  // DHT11/DHT22 sensors, return temperature/humidity based on the attached DHT sensor
-  #define SENSOR_DHT11 8
-  #define SENSOR_DHT22 9
+// Enable this module to use one of the following sensors: SENSOR_DIMMER
+#ifndef MODULE_DIMMER
+  #define MODULE_DIMMER 0
 #endif
-#if MODULE_SHT21 == 1
-  // SHT21 sensor, return temperature/humidity based on the attached SHT21 sensor
-  #define SENSOR_SHT21 10
-  #define SENSOR_HTU21D 15
-#endif
-#if MODULE_SWITCH == 1
-  // Generic switch, wake up the board when a pin changes status
-  #define SENSOR_SWITCH 11
-  // Door sensor, wake up the board and report when an attached magnetic sensor has been opened/closed
-  #define SENSOR_DOOR 12
-  // Motion sensor, wake up the board and report when an attached PIR has triggered
-  #define SENSOR_MOTION 13
-#endif
-#if MODULE_DS18B20 == 1
-  // DS18B20 sensor, return the temperature based on the attached sensor
-  #define SENSOR_DS18B20 14
-#endif
-#if MODULE_BH1750 == 1
-  // BH1750 sensor, return light in lux
-  #define SENSOR_BH1750 16
-#endif
-#if MODULE_MLX90614 == 1
-  // MLX90614 sensor, contactless temperature sensor
-  #define SENSOR_MLX90614 17
-#endif
-#if MODULE_BME280 == 1
-  // MLX90614 sensor, contactless temperature sensor
-  #define SENSOR_BME280 18
-#endif
-#if MODULE_SONOFF == 1
-  // Sonoff wireless smart switch
-  #define SENSOR_SONOFF 21
-#endif
-#if MODULE_BMP085 == 1
-  // BMP085/BMP180 sensor, return temperature and pressure
-  #define SENSOR_BMP085 22
+// 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
-#if MODULE_HCSR04 == 1
-  // HC-SR04 sensor, return the distance between the sensor and an object
-  #define SENSOR_HCSR04 23
-#endif
-#if MODULE_MCP9808 == 1
-  // MCP9808 sensor, precision temperature sensor
-  #define SENSOR_MCP9808 25
-#endif
-// last Id: 26
+
+/***********************************
+   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
 */
@@ -248,6 +342,9 @@
 
 // include MySensors libraries
 #include <core/MySensorsCore.h>
+#include <core/MyCapabilities.h>
+#include <core/MyTransport.h>
+#include <core/Version.h>
 
 // include third party libraries
 #if MODULE_DHT == 1
@@ -289,10 +386,33 @@
   #include <Wire.h>
   #include "Adafruit_MCP9808.h"
 #endif
+#if MODULE_MHZ19 == 1
+  #include <SoftwareSerial.h>
+#endif
+#if MODULE_AM2320 == 1
+  #include <Wire.h>
+  #include <AM2320.h>
+#endif
+#if MODULE_TSL2561 == 1
+  #include <TSL2561.h>
+  #include <Wire.h>
+#endif
+#if MODULE_PT100 == 1
+  #include <DFRobotHighTemperatureSensor.h>
+#endif
+#if MODULE_BMP280 == 1
+  #include <Wire.h>
+  #include <Adafruit_Sensor.h>
+  #include <Adafruit_BMP280.h>
+#endif
+#if MODULE_DIMMER == 1
+  #include <math.h>
+#endif
 
-/**************************************
+/*******************************************************************
    Classes
 */
+class NodeManager;
 
 /*
    PowerManager
@@ -303,103 +423,178 @@ class PowerManager {
     PowerManager() {};
     // 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);
+    // turns the power pins on
     void powerOn();
+    // turns the power pins on
     void powerOff();
-    float getVcc();
-    bool isConfigured();
   private:
     int _vcc_pin = -1;
     int _ground_pin = -1;
     long _wait = 0;
 };
 
+/*
+   Timer
+*/
+
+class Timer {
+  public:
+    Timer(NodeManager* node_manager);
+    // start the timer which will be over when the configured target passes by
+    void start(int target, int unit);
+    void start();
+    // stop the timer
+    void stop();
+    // reset the timer
+    void reset();
+    // reset the timer and start over
+    void restart();
+    // set the timer configuration but do not start it
+    void set(int target, int unit);
+    void unset();
+    // update the timer. To be called at every cycle
+    void update();
+    // return true if the time is over
+    bool isOver();
+    // return true if the timer is running
+    bool isRunning();
+    // return true if the timer has been configured
+    bool isConfigured();
+    // return true if this is the first time the timer runs
+    bool isFirstRun();
+    // return the current elapsed time
+    float getElapsed();
+   private:
+    NodeManager* _node_manager;
+    int _target = 0;
+    long _elapsed = 0;
+    long _last_millis = 0;
+    bool _is_running = false;
+    bool _is_configured = false;
+    bool _first_run = true;
+};
+
+/*
+   Request
+*/
+
+class Request {
+  public:
+    Request(const char* string);
+    // 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;
+    char* _value;
+};
 
 /***************************************
    Sensor: generic sensor class
 */
 class Sensor {
   public:
-    Sensor(int child_id, int pin);
-    // where the sensor is attached to (default: not set)
+    Sensor(NodeManager* node_manager, int child_id, int pin);
+    // [1] where the sensor is attached to (default: not set)
     void setPin(int value);
     int getPin();
-    // child_id of this sensor (default: not set)
+    // [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();
-    // type of this sensor (default: V_CUSTOM)
+    // [3] type of this sensor (default: V_CUSTOM)
     void setType(int value);
     int getType();
-    // description of the sensor (default: '')
+    // [4] description of the sensor (default: '')
     void setDescription(char *value);
-    // set this to true if you want destination node to send ack back to this node (default: false)
-    void setAck(bool value);
-    // when queried, send the message multiple times (default: 1)
-    void setRetries(int value);
-    // For some sensors, the measurement can be queried multiple times and an average is returned (default: 1)
+    // [5] For some sensors, the measurement can be queried multiple times and an average is returned (default: 1)
     void setSamples(int value);
-    // If more then one sample has to be taken, set the interval in milliseconds between measurements (default: 0)
+    // [6] If more then one sample has to be taken, set the interval in milliseconds between measurements (default: 0)
     void setSamplesInterval(int value);
-    // if true will report the measure only if different then the previous one (default: false)
-    void setTackLastValue(bool value);
-    // if track last value is enabled, force to send an update after the configured number of cycles (default: -1)
-    void setForceUpdate(int value);
-    // the value type of this sensor (default: TYPE_INTEGER)
+    // [7] if true will report the measure only if different than the previous one (default: false)
+    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();
-    // for float values, set the float precision (default: 2)
-    void setFloatPrecision(int value);
-    // optionally sleep interval in milliseconds before sending each message to the radio network (default: 0)
-    void setSleepBetweenSend(int value);
-    // set the interrupt pin the sensor is attached to so its loop() will be executed only upon that interrupt (default: -1)
-    void setInterruptPin(int value);
-    int getInterruptPin();
+    // [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);
-      // if enabled the pins will be automatically powered on while awake and off during sleeping (default: true)
+      // [12] if enabled the pins will be automatically powered on while awake and off during sleeping (default: true)
       void setAutoPowerPins(bool value);
-      // manually turn the power on
+      // [13] manually turn the power on
       void powerOn();
-      // manually turn the power off
+      // [14] manually turn the power off
       void powerOff();
     #endif
     // get the latest recorded value from the sensor
     int getValueInt();
     float getValueFloat();
     char* getValueString();
+    // [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)
+    void setReportIntervalMinutes(int value);
+    // [19] After how many hours the sensor will report back its measure (default: 10 minutes)
+    void setReportIntervalHours(int value);
+    // [20] After how many days the sensor will report back its measure (default: 10 minutes)
+    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);
     // 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);
     // 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;
   protected:
     MyMessage _msg;
-    int _sleep_between_send = 0;
+    MyMessage _msg_service;
+    NodeManager* _node_manager;
     int _pin = -1;
     int _child_id;
     int _presentation = S_CUSTOM;
     int _type = V_CUSTOM;
     char* _description = "";
-    bool _ack = false;
-    int _retries = 1;
     int _samples = 1;
     int _samples_interval = 0;
     bool _track_last_value = false;
-    int _cycles = 0;
-    int _force_update = -1;
     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;
@@ -409,30 +604,37 @@ class Sensor {
       PowerManager _powerManager;
       bool _auto_power_pins = true;
     #endif
+    Timer* _report_timer;
+    Timer* _force_update_timer;
     void _send(MyMessage & msg);
+    bool _isReceive(const MyMessage & message);
+    bool _isWorthSending(bool comparison);
 };
 
+#if MODULE_ANALOG_INPUT == 1
 /*
    SensorAnalogInput: read the analog input of a configured pin
 */
 class SensorAnalogInput: public Sensor {
   public:
-    SensorAnalogInput(int child_id, int pin);
-    // the analog reference to use (default: not set, can be either INTERNAL or DEFAULT)
+    SensorAnalogInput(NodeManager* node_manager, int child_id, int pin);
+    // [101] the analog reference to use (default: not set, can be either INTERNAL or DEFAULT)
     void setReference(int value);
-    // reverse the value or the percentage (e.g. 70% -> 30%) (default: false)
+    // [102] reverse the value or the percentage (e.g. 70% -> 30%) (default: false)
     void setReverse(bool value);
-    // when true returns the value as a percentage (default: true)
+    // [103] when true returns the value as a percentage (default: true)
     void setOutputPercentage(bool value);
-    // minimum value for calculating the percentage (default: 0)
+    // [104] minimum value for calculating the percentage (default: 0)
     void setRangeMin(int value);
-    // maximum value for calculating the percentage (default: 1024)
+    // [105] maximum value for calculating the percentage (default: 1024)
     void setRangeMax(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();
   protected:
     int _reference = -1;
     bool _reverse = false;
@@ -448,7 +650,7 @@ class SensorAnalogInput: public Sensor {
 */
 class SensorLDR: public SensorAnalogInput {
   public:
-    SensorLDR(int child_id, int pin);
+    SensorLDR(NodeManager* node_manager, int child_id, int pin);
 };
 
 /*
@@ -456,22 +658,24 @@ class SensorLDR: public SensorAnalogInput {
 */
 class SensorThermistor: public Sensor {
   public:
-    SensorThermistor(int child_id, int pin);
-    // resistance at 25 degrees C (default: 10000)
+    SensorThermistor(NodeManager* node_manager, int child_id, int pin);
+    // [101] resistance at 25 degrees C (default: 10000)
     void setNominalResistor(long value);
-    // temperature for nominal resistance (default: 25)
+    // [102] temperature for nominal resistance (default: 25)
     void setNominalTemperature(int value);
-    // The beta coefficient of the thermistor (default: 3950)
+    // [103] The beta coefficient of the thermistor (default: 3950)
     void setBCoefficient(int value);
-    // the value of the resistor in series with the thermistor (default: 10000)
+    // [104] the value of the resistor in series with the thermistor (default: 10000)
     void setSeriesResistor(long value);
-    // set a temperature offset
+    // [105] set a temperature offset
     void setOffset(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();
   protected:
     long _nominal_resistor = 10000;
     int _nominal_temperature = 25;
@@ -480,73 +684,20 @@ class SensorThermistor: public Sensor {
     float _offset = 0;
 };
 
-/*
-    SensorMQ
- */
-class SensorMQ: public Sensor {
-  public:
-    SensorMQ(int child_id, int pin);
-    // define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1);
-    void setTargetGas(int value);
-    // define the load resistance on the board, in kilo ohms (default: 1);
-    void setRlValue(float value);
-    // define the Ro resistance on the board (default: 10000);
-    void setRoValue(float value);
-    // Sensor resistance in clean air (default: 9.83);
-    void setCleanAirFactor(float value);
-    // define how many samples you are going to take in the calibration phase (default: 50);
-    void setCalibrationSampleTimes(int value);
-    // define the time interal(in milisecond) between each samples in the cablibration phase (default: 500);
-    void setCalibrationSampleInterval(int value);
-    // define how many samples you are going to take in normal operation (default: 50);
-    void setReadSampleTimes(int value);
-    // 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);
-    // define what to do at each stage of the sketch
-    void onBefore();
-    void onSetup();
-    void onLoop();
-    void onReceive(const MyMessage & message);
-  protected:
-    float _rl_value = 1.0;
-    float _ro_clean_air_factor = 9.83;
-    int _calibration_sample_times = 50;
-    int _calibration_sample_interval = 500;
-    int _read_sample_interval = 50;
-    int _read_sample_times = 5;
-    float _ro = 10000.0;
-    float _LPGCurve[3] = {2.3,0.21,-0.47};
-    float _COCurve[3] = {2.3,0.72,-0.34};
-    float _SmokeCurve[3] = {2.3,0.53,-0.44};
-    float _MQResistanceCalculation(int raw_adc);
-    float _MQCalibration();
-    float _MQRead();
-    int _MQGetGasPercentage(float rs_ro_ratio, int gas_id);
-    int  _MQGetPercentage(float rs_ro_ratio, float *pcurve);
-    int _gas_lpg = 0;
-    int _gas_co = 1;
-    int _gas_smoke = 2;
-    int _target_gas = _gas_co;
-};
-
 /*
     SensorML8511
 */
 
 class SensorML8511: public Sensor {
   public:
-    SensorML8511(int child_id, int pin);
+    SensorML8511(NodeManager* node_manager, int child_id, 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();
   protected:
     float _mapfloat(float x, float in_min, float in_max, float out_min, float out_max);
 };
@@ -557,85 +708,96 @@ class SensorML8511: public Sensor {
 
 class SensorACS712: public Sensor {
   public:
-    SensorACS712(int child_id, int pin);
-    // 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);
+    SensorACS712(NodeManager* node_manager, int child_id, 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);
-    // set ACS offset (default: 2500);
+    // [102] set ACS offset (default: 2500);
     void setOffset(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();
   protected:
     int _ACS_offset = 2500;
     int _mv_per_amp = 185;
 };
 
 /*
-    SensorRainGauge
+   SensorRain
 */
-
-class SensorRainGauge: public Sensor {
+class SensorRain: public SensorAnalogInput {
   public:
-    SensorRainGauge(int child_id, int pin);
-    // set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60);
-    void setReportInterval(int value);
-    // set how many mm of rain to count for each tip (default: 0.11);
-    void setSingleTip(float value);
-    // define what to do at each stage of the sketch
-    void onBefore();
-    void onSetup();
-    void onLoop();
-    void onReceive(const MyMessage & message);
+    SensorRain(NodeManager* node_manager, int child_id, int pin);
+};
+
+/*
+   SensorSoilMoisture
+*/
+class SensorSoilMoisture: public SensorAnalogInput {
   public:
-    static void _onTipped();
-    static long _last_tip;
-    static long _count;
-  protected:
-    int _report_interval = 60;
-    float _single_tip = 0.11;
-    long _last_report = 0;
+    SensorSoilMoisture(NodeManager* node_manager, int child_id, int pin);
 };
+#endif
+
 
+#if MODULE_DIGITAL_INPUT == 1
 /*
    SensorDigitalInput: read the digital input of the configured pin
 */
 class SensorDigitalInput: public Sensor {
   public:
-    SensorDigitalInput(int child_id, int pin);
+    SensorDigitalInput(NodeManager* node_manager, int child_id, 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();
 };
+#endif
 
+#if MODULE_DIGITAL_OUTPUT == 1
 /*
    SensorDigitalOutput: control a digital output of the configured pin
 */
 class SensorDigitalOutput: public Sensor {
   public:
-    SensorDigitalOutput(int child_id, int pin);
-    // set how to initialize the output (default: LOW)
-    void setInitialValue(int value);
-    // if greater than 0, send a pulse of the given duration in ms and then restore the output back to the original value (default: 0)
-    void setPulseWidth(int value);
-    // define which value to set to the output when set to on (default: HIGH)
+    SensorDigitalOutput(NodeManager* node_manager, int child_id, int pin);
+    // [103] define which value to set to the output when set to on (default: HIGH)
     void setOnValue(int value);
-    // when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false)
+    // [104] when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false)
     void setLegacyMode(bool value);
+    // [105] automatically turn the output off after the given number of minutes
+    void setSafeguard(int value);
+    // [106] if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false)
+    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();
     // 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 _initial_value = LOW;
     int _on_value = HIGH;
-    int _state = 0;
-    int _pulse_width = 0;
+    int _status = OFF;
     bool _legacy_mode = false;
+    bool _input_is_elapsed = false;
+    int _wait_after_set = 0;
+    Timer* _safeguard_timer;
+    void _setupPin(int pin);
+    virtual void _setStatus(int value);
+    int _getValueToWrite(int value);
 };
 
 
@@ -644,9 +806,7 @@ class SensorDigitalOutput: public Sensor {
 */
 class SensorRelay: public SensorDigitalOutput {
   public:
-    SensorRelay(int child_id, int pin);
-    // define what to do at each stage of the sketch
-    void onLoop();
+    SensorRelay(NodeManager* node_manager, int child_id, int pin);
 };
 
 /*
@@ -654,8 +814,23 @@ class SensorRelay: public SensorDigitalOutput {
 */
 class SensorLatchingRelay: public SensorRelay {
   public:
-    SensorLatchingRelay(int child_id, int pin);
+    SensorLatchingRelay(NodeManager* node_manager, int child_id, 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)
+    void setPinOff(int value);
+    // [203] set the pin which turns the relay on (default: the pin provided while registering the sensor + 1)
+    void setPinOn(int value);
+    // define what to do at each stage of the sketch
+    void onBefore();
+    void onProcess(Request & request);
+  protected:
+    int _pin_on;
+    int _pin_off;
+    int _pulse_width = 50;
+    void _setStatus(int value);
 };
+#endif
 
 /*
    SensorDHT
@@ -663,18 +838,20 @@ class SensorLatchingRelay: public SensorRelay {
 #if MODULE_DHT == 1
 class SensorDHT: public Sensor {
   public:
-    SensorDHT(int child_id, int pin, DHT* dht, int sensor_type, int dht_type);
+    SensorDHT(NodeManager* node_manager, int child_id, int pin, DHT* dht, int sensor_type, int dht_type);
     // 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;
   protected:
     DHT* _dht;
-    int _dht_type = DHT11;
+    int _dht_type;
     float _offset = 0;
     int _sensor_type = 0;
 };
@@ -686,12 +863,14 @@ class SensorDHT: public Sensor {
 #if MODULE_SHT21 == 1
 class SensorSHT21: public Sensor {
   public:
-    SensorSHT21(int child_id, int sensor_type);
+    SensorSHT21(NodeManager* node_manager, int child_id, int sensor_type);
     // 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;
@@ -706,31 +885,32 @@ class SensorSHT21: public Sensor {
 
 class SensorHTU21D: public SensorSHT21 {
   public:
-    SensorHTU21D(int child_id, int pin);
+    SensorHTU21D(NodeManager* node_manager, int child_id, int pin);
 };
 #endif
 
 /*
  * SensorSwitch
  */
+#if MODULE_SWITCH == 1
 class SensorSwitch: public Sensor {
   public:
-    SensorSwitch(int child_id, int pin);
-    // set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE)
+    SensorSwitch(NodeManager* node_manager, int child_id, int pin);
+    // [101] set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE)
     void setMode(int value);
-    int getMode();
-    // milliseconds to wait before reading the input (default: 0)
+    // [102] milliseconds to wait before reading the input (default: 0)
     void setDebounce(int value);
-    // time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0)
+    // [103] time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0)
     void setTriggerTime(int value);
-    // Set initial value on the interrupt pin (default: HIGH)
+    // [104] Set initial value on the interrupt pin (default: HIGH)
     void setInitial(int value);
-    int getInitial();
     // 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 _debounce = 0;
     int _trigger_time = 0;
@@ -743,7 +923,7 @@ class SensorSwitch: public Sensor {
  */
 class SensorDoor: public SensorSwitch {
   public:
-    SensorDoor(int child_id, int pin);
+    SensorDoor(NodeManager* node_manager, int child_id, int pin);
 };
 
 /*
@@ -751,29 +931,31 @@ class SensorDoor: public SensorSwitch {
  */
 class SensorMotion: public SensorSwitch {
   public:
-    SensorMotion(int child_id, int pin);
+    SensorMotion(NodeManager* node_manager, int child_id, int pin);
 };
-
+#endif
 /*
    SensorDs18b20
 */
 #if MODULE_DS18B20 == 1
 class SensorDs18b20: public Sensor {
   public:
-    SensorDs18b20(int child_id, int pin, DallasTemperature* sensors, int index);
-    // return the sensors' device address
-    DeviceAddress* getDeviceAddress();
+    SensorDs18b20(NodeManager* node_manager, int child_id, int pin, DallasTemperature* sensors, int index);
     // returns the sensor's resolution in bits
     int getResolution();
-    // set the sensor's resolution in bits
+    // [101] set the sensor's resolution in bits
     void setResolution(int value);
-    // sleep while DS18B20 calculates temperature (default: false)
+    // [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();
   protected:
     float _offset = 0;
     int _index;
@@ -789,12 +971,16 @@ class SensorDs18b20: public Sensor {
 #if MODULE_BH1750 == 1
 class SensorBH1750: public Sensor {
   public:
-    SensorBH1750(int child_id);
+    SensorBH1750(NodeManager* node_manager, int child_id);
+    // [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();
   protected:
     BH1750* _lightSensor;
 };
@@ -806,12 +992,14 @@ class SensorBH1750: public Sensor {
 #if MODULE_MLX90614 == 1
 class SensorMLX90614: public Sensor {
   public:
-    SensorMLX90614(int child_id, Adafruit_MLX90614* mlx, int sensor_type);
+    SensorMLX90614(NodeManager* node_manager, int child_id, Adafruit_MLX90614* mlx, int sensor_type);
     // 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;
@@ -826,17 +1014,19 @@ class SensorMLX90614: public Sensor {
  * SensorBosch
 */
 
-#if MODULE_BME280 == 1 || MODULE_BMP085 == 1
+#if MODULE_BME280 == 1 || MODULE_BMP085 == 1 || MODULE_BMP280 == 1
 class SensorBosch: public Sensor {
   public:
-    SensorBosch(int child_id, int sensor_type);
-    // define how many pressure samples to keep track of for calculating the forecast (default: 5)
+    SensorBosch(NodeManager* node_manager, int child_id, int sensor_type);
+    // [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;
@@ -864,7 +1054,7 @@ class SensorBosch: public Sensor {
 #if MODULE_BME280 == 1
 class SensorBME280: public SensorBosch {
   public:
-    SensorBME280(int child_id, Adafruit_BME280* bme, int sensor_type);
+    SensorBME280(NodeManager* node_manager, int child_id, Adafruit_BME280* bme, int sensor_type);
     void onLoop();
   protected:
     Adafruit_BME280* _bme;
@@ -877,31 +1067,46 @@ class SensorBME280: public SensorBosch {
 #if MODULE_BMP085 == 1
 class SensorBMP085: public SensorBosch {
   public:
-    SensorBMP085(int child_id, Adafruit_BMP085* bmp, int sensor_type);
+    SensorBMP085(NodeManager* node_manager, int child_id, Adafruit_BMP085* bmp, int sensor_type);
     void onLoop();
   protected:
     Adafruit_BMP085* _bmp;
 };
 #endif
 
+/*
+   SensorBMP280
+*/
+#if MODULE_BMP280 == 1
+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(int child_id, int pin);
-    // Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor)
+    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);
-    // Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor)
+    // [102] Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor)
     void setEchoPin(int value);
-    // Maximum distance we want to ping for (in centimeters) (default: 300)
+    // [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();
     void onReceive(const MyMessage & message);
+    void onProcess(Request & request);
+    void onInterrupt();
   protected:
     int _trigger_pin;
     int _echo_pin;
@@ -916,18 +1121,20 @@ class SensorHCSR04: public Sensor {
 #if MODULE_SONOFF == 1
 class SensorSonoff: public Sensor {
   public:
-    SensorSonoff(int child_id);
-    // set the button's pin (default: 0)
+    SensorSonoff(NodeManager* node_manager, int child_id);
+    // [101] set the button's pin (default: 0)
     void setButtonPin(int value);
-    // set the relay's pin (default: 12)
+    // [102] set the relay's pin (default: 12)
     void setRelayPin(int value);
-    // set the led's pin (default: 13)
+    // [103] set the led's pin (default: 13)
     void setLedPin(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();
   protected:
     Bounce _debouncer = Bounce();
     int _button_pin = 0;
@@ -950,17 +1157,283 @@ class SensorSonoff: public Sensor {
 #if MODULE_MCP9808 == 1
 class SensorMCP9808: public Sensor {
   public:
-    SensorMCP9808(int child_id, Adafruit_MCP9808* mcp);
+    SensorMCP9808(NodeManager* node_manager, int child_id, Adafruit_MCP9808* mcp);
     // 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:
     Adafruit_MCP9808* _mcp;
 };
 #endif
 
+/*
+    SensorMQ
+ */
+ #if MODULE_MQ == 1
+class SensorMQ: public Sensor {
+  public:
+    SensorMQ(NodeManager* node_manager, int child_id, 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);
+    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);
+    // 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:
+    float _rl_value = 1.0;
+    float _ro_clean_air_factor = 9.83;
+    int _calibration_sample_times = 50;
+    int _calibration_sample_interval = 500;
+    int _read_sample_interval = 50;
+    int _read_sample_times = 5;
+    float _ro = 10000.0;
+    static float _default_LPGCurve[3];
+    static float _default_COCurve[3];
+    static float _default_SmokeCurve[3];
+    float *_LPGCurve;
+    float *_COCurve;
+    float *_SmokeCurve;
+    float _MQResistanceCalculation(int raw_adc);
+    float _MQCalibration();
+    float _MQRead();
+    int _MQGetGasPercentage(float rs_ro_ratio, int gas_id);
+    int  _MQGetPercentage(float rs_ro_ratio, float *pcurve);
+    const static int _gas_lpg = 0;
+    const static int _gas_co = 1;
+    const static int _gas_smoke = 2;
+    int _target_gas = _gas_co;
+};
+#endif
+
+/*
+   SensorMHZ19
+*/
+#if MODULE_MHZ19 == 1
+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();
+    // 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:
+    SoftwareSerial* _ser;
+    int _tx_pin = 6;
+    int _rx_pin = 7;
+};
+#endif
+
+/*
+   SensorAM2320
+*/
+#if MODULE_AM2320 == 1
+class SensorAM2320: public Sensor {
+  public:
+    SensorAM2320(NodeManager* node_manager, int child_id, AM2320* th, int sensor_type);
+    // 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;
+  protected:
+    AM2320* _th;
+    int _sensor_type = 0;
+};
+#endif
+
+/*
+   SensorTSL2561
+*/
+#if MODULE_TSL2561 == 1
+class SensorTSL2561: public Sensor {
+  public:
+    SensorTSL2561(NodeManager* node_manager, int child_id);
+    // [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)
+    void setTiming(int value);
+    // [103] set the spectrum, possible values are SensorTSL2561::VISIBLE (0), SensorTSL2561::FULLSPECTRUM (1), SensorTSL2561::INFRARED (2), SensorTSL2561::FULL (3) (default: visible)
+    void setSpectrum(int value);
+    // [104] set the i2c address values are SensorTSL2561::ADDR_FLOAT, SensorTSL2561::ADDR_LOW, SensorTSL2561::ADDR_HIGH
+    void setAddress(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 ADDR_FLOAT = 0;
+    const static int ADDR_LOW = 1;
+    const static int ADDR_HIGH = 2;
+    const static int GAIN_0X = 0;
+    const static int GAIN_16X = 1;
+    const static int INTEGRATIONTIME_13MS = 0;
+    const static int INTEGRATIONTIME_101MS = 1;
+    const static int INTEGRATIONTIME_402MS = 2;
+    const static int VISIBLE = 0;
+    const static int FULLSPECTRUM = 1;
+    const static int INFRARED = 2;
+    const static int FULL = 3;
+  protected:
+    TSL2561* _tsl;
+    int _tsl_address = 0;
+    int _tsl_gain = 1;
+    int _tsl_timing = 0;
+    int _tsl_spectrum = 0;
+};
+#endif
+
+/*
+    SensorPT100
+*/
+#if MODULE_PT100 == 1
+class SensorPT100: public Sensor {
+  public:
+    SensorPT100(NodeManager* node_manager, int child_id, 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();
+  protected:
+    DFRobotHighTemperature* _PT100;
+    float _voltageRef = 3.3;
+};
+#endif
+
+/*
+    SensorPT100
+*/
+#if MODULE_DIMMER == 1
+class SensorDimmer: public Sensor {
+  public:
+    SensorDimmer(NodeManager* node_manager, int child_id, 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);
+    // fade the output from the current value to the target provided in the range 0-100
+    void fadeTo(int value);
+    enum easing {
+      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;
+    int _step_duration = 100;
+    float _getEasing(float t, float b, float c, float d);
+};
+#endif
+
+/*
+    SensorPulseMeter
+*/
+#if MODULE_PULSE_METER == 1
+class SensorPulseMeter: public Sensor {
+  public:
+    SensorPulseMeter(NodeManager* node_manager, int child_id, 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)
+    void setInitialValue(int value);
+    // set the interrupt mode to attach to (default: FALLING)
+    void setInterruptMode(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();
+  protected:
+    long _count = 20;
+    float _pulse_factor;
+    int _initial_value = HIGH;
+    int _interrupt_mode = FALLING;
+    void _reportTotal();
+};
+
+/*
+    SensorRainGauge
+*/
+class SensorRainGauge: public SensorPulseMeter {
+  public:
+    SensorRainGauge(NodeManager* node_manager, int child_id, int pin);
+};
+
+/*
+    SensorPowerMeter
+*/
+class SensorPowerMeter: public SensorPulseMeter {
+  public:
+    SensorPowerMeter(NodeManager* node_manager, int child_id, int pin);
+};
+
+/*
+    SensorWaterMeter
+*/
+class SensorWaterMeter: public SensorPulseMeter {
+  public:
+    SensorWaterMeter(NodeManager* node_manager, int child_id, int pin);
+};
+#endif
 
 /***************************************
    NodeManager: manages all the aspects of the node
@@ -968,46 +1441,56 @@ class SensorMCP9808: public Sensor {
 class NodeManager {
   public:
     NodeManager();
-    // the pin to connect to the RST pin to reboot the board (default: 4)
-    void setRebootPin(int value);
-    // send the same service message multiple times (default: 1)
+    // [10] send the same service message multiple times (default: 1)
     void setRetries(int value);
+    int getRetries();
     #if BATTERY_MANAGER == 1
-      // the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7)
+      // [11] the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7)
       void setBatteryMin(float value);
-      // the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3)
+      // [12] the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3)
       void setBatteryMax(float value);
-      // after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: 10)
-      void setBatteryReportCycles(int value);
-      // 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)
+      // [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);
-      // 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)
+      // [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);
-      // if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075)
+      // [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);
-      // If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: true)
+      // [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
-    // define the way the node should behave. It can be IDLE (stay awake withtout executing each sensors' loop), SLEEP (go to sleep for the configured interval), WAIT (wait for the configured interval), ALWAYS_ON (stay awake and execute each sensors' loop)
-    void setSleepMode(int value);
-    void setMode(int value);
-    // define for how long the board will sleep (default: 0)
-    void setSleepTime(int value);
-    // define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES)
-    void setSleepUnit(int value);
-    // configure the node's behavior, parameters are mode, time and unit
-    void setSleep(int value1, int value2, int value3);
-    // if enabled, when waking up from the interrupt, the board stops sleeping. Disable it when attaching e.g. a motion sensor (default: true)
+    // [3] set the duration (in seconds) of a sleep cycle
+    void setSleepSeconds(int value);
+    long getSleepSeconds();
+    // [4] set the duration (in minutes) of a sleep cycle
+    void setSleepMinutes(int value);
+    // [5] set the duration (in hours) of a sleep cycle
+    void setSleepHours(int value);
+    // [29] set the duration (in days) of a sleep cycle
+    void setSleepDays(int value);
+    // [19] if enabled, when waking up from the interrupt, the board stops sleeping. Disable it when attaching e.g. a motion sensor (default: true)
     void setSleepInterruptPin(int value);
     // configure the interrupt pin and mode. Mode can be CHANGE, RISING, FALLING (default: MODE_NOT_DEFINED)
-    void setInterrupt(int pin, int mode, int pull = -1);
-    // optionally sleep interval in milliseconds before sending each message to the radio network (default: 0)
+    void setInterrupt(int pin, int mode, int initial = -1);
+    // [28] ignore two consecutive interrupts if happening within this timeframe in milliseconds (default: 100)
+    void setInterruptMinDelta(long value);
+    // [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);
-    // un-register a sensor
+    // [26] un-register a sensor
     void unRegisterSensor(int sensor_index);
     // return a sensor by its index
     Sensor* get(int sensor_index);
@@ -1017,17 +1500,79 @@ class NodeManager {
     #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);
-      // if enabled the pins will be automatically powered on while awake and off during sleeping (default: true)
+      // [23] if enabled the pins will be automatically powered on while awake and off during sleeping (default: true)
       void setAutoPowerPins(bool value);
-      // manually turn the power on
+      // [24] manually turn the power on
       void powerOn();
-      // manually turn the power off
+      // [25] manually turn the power off
       void powerOff();
     #endif
-    // set this to true if you want destination node to send ack back to this node (default: false)
+    // [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();
     // request and return the current timestamp from the controller
     long getTimestamp();
+    // Request the controller's configuration on startup (default: true)
+    void setGetControllerConfig(bool value);
+    // [22] Manually set isMetric setting
+    void setIsMetric(bool value);
+    bool getIsMetric();
+    // Convert a temperature from celsius to fahrenheit depending on how isMetric is set
+    float celsiusToFahrenheit(float temperature);
+    // return true if sleep or wait is configured and hence this is a sleeping node
+    bool isSleepingNode();
+    // [1] Send a hello message back to the controller
+    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
+    void saveToMemory(int index, int value);
+    // return vcc in V
+    float getVcc();
+    // setup the configured interrupt pins
+    void setupInterrupts();
+    // return the pin from which the last interrupt came
+    int getLastInterruptPin();
+    // [36] set the default interval in minutes all the sensors will report their measures. If the same function is called on a specific sensor, this will not change the previously set value. or sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes)
+    void setReportIntervalSeconds(int value);
+    // [37] set the default interval in minutes all the sensors will report their measures. If the same function is called on a specific sensor, this will not change the previously set value. or sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes)
+    void setReportIntervalMinutes(int value);
+    // [38] set the default interval in minutes all the sensors will report their measures. If the same function is called on a specific sensor, this will not change the previously set value. or sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes)
+    void setReportIntervalHours(int value);
+    // [39] set the default interval in minutes all the sensors will report their measures. If the same function is called on a specific sensor, this will not change the previously set value. or sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes)
+    void setReportIntervalDays(int value);
+    // [30] if set and when the board is battery powered, sleep() is always called instead of wait() (default: true)
+    void setSleepOrWait(bool value);
+    // sleep if the node is a battery powered or wait if it is not for the given number of milliseconds 
+    void sleepOrWait(long value);
+    // [31] 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 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
     // hook into the main sketch functions
     void before();
     void presentation();
@@ -1035,44 +1580,57 @@ class NodeManager {
     void loop();
     void receive(const MyMessage & msg);
     void receiveTime(unsigned long ts);
+    // handle interrupts
+    static void _onInterrupt_1();
+    static void _onInterrupt_2();
   private:
     #if BATTERY_MANAGER == 1
       float _battery_min = 2.6;
       float _battery_max = 3.3;
-      int _battery_report_cycles = 10;
+      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;
-      int _cycles = 0;
-      float _getVcc();
     #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 _send(MyMessage & msg);
-    int _sleep_mode = IDLE;
-    int _sleep_time = 0;
-    int _sleep_unit = MINUTES;
+    int _status = AWAKE;
+    long _sleep_time = 0;
     int _sleep_interrupt_pin = -1;
     int _sleep_between_send = 0;
     int _retries = 1;
     int _interrupt_1_mode = MODE_NOT_DEFINED;
     int _interrupt_2_mode = MODE_NOT_DEFINED;
-    int _interrupt_1_pull = -1;
-    int _interrupt_2_pull = -1;
-    int _last_interrupt_pin = -1;
+    int _interrupt_1_initial = -1;
+    int _interrupt_2_initial = -1;
+    static int _last_interrupt_pin;
+    static long _interrupt_min_delta;
+    static long _last_interrupt_1;
+    static long _last_interrupt_2;
     long _timestamp = -1;
     Sensor* _sensors[MAX_SENSORS+1] = {0};
     bool _ack = false;
-    void _process(const char * message);
     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();
 };
 
 #endif
diff --git a/NodeManager.ino b/NodeManager.ino
old mode 100644
new mode 100755
index 148442d40191882701374de85c2062f258e0fafb..38bf742c576cb56f705c964c73c6234e66430adb
--- a/NodeManager.ino
+++ b/NodeManager.ino
@@ -1,15 +1,14 @@
+
 /*
 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
- */
+*/
 
  
 // load user settings
@@ -30,10 +29,12 @@ NodeManager nodeManager;
 void before() {
   // setup the serial port baud rate
   Serial.begin(MY_BAUD_RATE);  
+
   /*
    * Register below your sensors
   */
   
+  
 
   
   /*
@@ -58,7 +59,6 @@ void setup() {
 void loop() {
   // call NodeManager loop routine
   nodeManager.loop();
-
 }
 
 // receive
@@ -72,5 +72,3 @@ void receiveTime(unsigned long ts) {
   // call NodeManager receiveTime routine
   nodeManager.receiveTime(ts);
 }
-
-
diff --git a/README.md b/README.md
old mode 100644
new mode 100755
index f7a40c38d2f934c7471cddfcec37b35220dcce55..0804f7af5c3be6b1b1a532f838a7b318b0ae2017
--- a/README.md
+++ b/README.md
@@ -1,5 +1,3 @@
-# Introduction
-
 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:
@@ -13,41 +11,42 @@ NodeManager includes the following main components:
 ## Features
 
 * Manage all the aspects of a sleeping cycle by leveraging smart sleep
-* Allow configuring the sleep mode and the sleep duration remotely
+* Allow configuring the node and any attached sensors remotely
 * 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
+* 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 battery voltage through a built-in sensor
-* Can report battery level 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 
 
-# Installation
-* Download the package or clone the git repository at https://github.com/mysensors/NodeManager
-* Open the provided sketch template and save it under a different name
+## 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
 
 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.
 
-## Upgrade
+### 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
 
-# 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) and settings that can be customized remotely (via a special child id).
+## 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
+### 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
  */
 
-#define SKETCH_NAME "NodeManagerTemplate"
+#define SKETCH_NAME "NodeManager"
 #define SKETCH_VERSION "1.0"
 
 /**********************************
@@ -58,23 +57,48 @@ Since NodeManager has to communicate with the MySensors gateway on your behalf,
 #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
@@ -90,8 +114,8 @@ Since NodeManager has to communicate with the MySensors gateway on your behalf,
 
 // ESP8266 gateway settings
 //#define MY_GATEWAY_ESP8266
-//#define MY_ESP8266_SSID "MySSID"
-//#define MY_ESP8266_PASSWORD "MyVerySecretPassword"
+//#define MY_ESP8266_SSID ""
+//#define MY_ESP8266_PASSWORD ""
 
 // Gateway networking settings
 //#define MY_IP_ADDRESS 192,168,178,87
@@ -123,38 +147,43 @@ Since NodeManager has to communicate with the MySensors gateway on your behalf,
 //#define MY_DEFAULT_TX_LED_PIN  6
 ~~~
 
-## Enable/Disable NodeManager's modules
+### Enable/Disable NodeManager's modules
 
-Those NodeManager's 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 space is left to your custom code.
+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.
 
 ~~~c
+/***********************************
+ * 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 configuration settings on EEPROM
+// if enabled, persist the remote configuration settings on EEPROM
 #define PERSIST 0
-
-// if enabled, enable debug messages on serial port
-#define DEBUG 1
-
-// 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 1
 // 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_MQ, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN_GAUGE
+// 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_SHT21
-#define MODULE_SHT21 0
 // 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
@@ -173,16 +202,32 @@ Those NodeManager's directives in the `config.h` file control which module/libra
 #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
 ~~~
 
-## Installing the dependencies
+### Installing the dependencies
 
-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). You need to install the library ONLY if the module is enabled:
+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:
 
 Module  | Required Library
  ------------- | -------------
 MODULE_SHT21 | https://github.com/SodaqMoja/Sodaq_SHT2x
-MODULE_DHT | https://github.com/adafruit/DHT-sensor-library
+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
@@ -191,83 +236,178 @@ 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
 
-## Configure NodeManager
+### Configure NodeManager
 
-Node Manager comes with a reasonable default configuration. If you want/need to change its settings, this can be done in your sketch, inside the `before()` function and just before registering your sensors. The following methods are exposed for your convenience:
+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:
 
 ~~~c
-    // send the same service message multiple times (default: 1)
+    // [10] send the same service message multiple times (default: 1)
     void setRetries(int value);
+    int getRetries();
     #if BATTERY_MANAGER == 1
-      // the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7)
+      // [11] the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7)
       void setBatteryMin(float value);
-      // the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3)
+      // [12] the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3)
       void setBatteryMax(float value);
-      // after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: 10)
-      void setBatteryReportCycles(int value);
-      // 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)
+      // [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);
-      // 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)
+      // [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);
-      // if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075)
+      // [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);
-      // If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: true)
+      // [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
-    // define the way the node should behave. It can be IDLE (stay awake withtout executing each sensors' loop), SLEEP (go to sleep for the configured interval), WAIT (wait for the configured interval), ALWAYS_ON (stay awake and execute each sensors' loop)
-    void setSleepMode(int value);
-    void setMode(int value);
-    // define for how long the board will sleep (default: 0)
-    void setSleepTime(int value);
-    // define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES)
-    void setSleepUnit(int value);
-    // configure the node's behavior, parameters are mode, time and unit
-    void setSleep(int value1, int value2, int value3);
-    // if enabled, when waking up from the interrupt, the board stops sleeping. Disable it when attaching e.g. a motion sensor (default: true)
+    // [3] set the duration (in seconds) of a sleep cycle
+    void setSleepSeconds(int value);
+    long getSleepSeconds();
+    // [4] set the duration (in minutes) of a sleep cycle
+    void setSleepMinutes(int value);
+    // [5] set the duration (in hours) of a sleep cycle
+    void setSleepHours(int value);
+    // [29] set the duration (in days) of a sleep cycle
+    void setSleepDays(int value);
+    // [19] if enabled, when waking up from the interrupt, the board stops sleeping. Disable it when attaching e.g. a motion sensor (default: true)
     void setSleepInterruptPin(int value);
-    #endif
     // configure the interrupt pin and mode. Mode can be CHANGE, RISING, FALLING (default: MODE_NOT_DEFINED)
-    void setInterrupt(int pin, int mode, int pull = -1);
-    // optionally sleep interval in milliseconds before sending each message to the radio network (default: 0)
+    void setInterrupt(int pin, int mode, int initial = -1);
+    // [28] ignore two consecutive interrupts if happening within this timeframe in milliseconds (default: 100)
+    void setInterruptMinDelta(long value);
+    // [20] optionally sleep interval in milliseconds before sending each message to the radio network (default: 0)
     void setSleepBetweenSend(int value);
-    // set the interrupt pin the sensor is attached to so its loop() will be executed only upon that interrupt (default: -1)
-    void setInterruptPin(int value);
+    int getSleepBetweenSend();
     // register a built-in sensor
     int registerSensor(int sensor_type, int pin = -1, int child_id = -1);
-    // un-register a sensor
-    void unRegisterSensor(int sensor_index);
     // 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
+    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, long wait = 0);
-      // if enabled the pins will be automatically powered on while awake and off during sleeping (default: true)
+      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);
-      // manually turn the power on
+      // [24] manually turn the power on
       void powerOn();
-      // manually turn the power off
+      // [25] manually turn the power off
       void powerOff();
     #endif
-    // set this to true if you want destination node to send ack back to this node (default: false)
+    // [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();
     // request and return the current timestamp from the controller
     long getTimestamp();
+    // Request the controller's configuration on startup (default: true)
+    void setGetControllerConfig(bool value);
+    // [22] Manually set isMetric setting
+    void setIsMetric(bool value);
+    bool getIsMetric();
+    // Convert a temperature from celsius to fahrenheit depending on how isMetric is set
+    float celsiusToFahrenheit(float temperature);
+    // return true if sleep or wait is configured and hence this is a sleeping node
+    bool isSleepingNode();
+    // [1] Send a hello message back to the controller
+    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
+    void saveToMemory(int index, int value);
+    // return vcc in V
+    float getVcc();
+    // setup the configured interrupt pins
+    void setupInterrupts();
+    // return the pin from which the last interrupt came
+    int getLastInterruptPin();
+    // [36] set the default interval in minutes all the sensors will report their measures. If the same function is called on a specific sensor, this will not change the previously set value. or sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes)
+    void setReportIntervalSeconds(int value);
+    // [37] set the default interval in minutes all the sensors will report their measures. If the same function is called on a specific sensor, this will not change the previously set value. or sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes)
+    void setReportIntervalMinutes(int value);
+    // [38] set the default interval in minutes all the sensors will report their measures. If the same function is called on a specific sensor, this will not change the previously set value. or sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes)
+    void setReportIntervalHours(int value);
+    // [39] set the default interval in minutes all the sensors will report their measures. If the same function is called on a specific sensor, this will not change the previously set value. or sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes)
+    void setReportIntervalDays(int value);
+    // [30] if set and when the board is battery powered, sleep() is always called instead of wait() (default: true)
+    void setSleepOrWait(bool value);
+    // sleep if the node is a battery powered or wait if it is not for the given number of milliseconds 
+    void sleepOrWait(long value);
+    // [31] 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 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
 ~~~
 
-For example
+### 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
-	nodeManager.setBatteryMin(1.8);
+int id = nodeManager.registerSensor(SENSOR_DHT22,6);
+SensorDHT* dht = (SensorDHT*)nodeManager.get(id);
+dht->setReportIntervalSeconds(60);
+nodeManager.setReportIntervalMinutes(20);
 ~~~
 
-## Register your sensors
-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:
+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.
+
+### 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
  ------------- | -------------
@@ -297,20 +437,30 @@ SENSOR_HCSR04 | HC-SR04 sensor, return the distance between the sensor and an ob
 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
-
-To register a sensor simply call the NodeManager instance with the sensory type and the pin the sensor is conncted to. For example:
+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);
+	nodeManager.registerSensor(SENSOR_DOOR,3,1);
 ~~~
 
-Once registered, your job is done. NodeManager will assign a child id automatically, present each sensor for you to the controller, query each sensor and report the value back to the gateway/controller at at the end of each sleep cycle. An optional child id can be provided as a third argument if you want to assign it manually. For actuators (e.g. relays) those can be triggered by sending a `REQ` message to their assigned child id.
+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. If you want to set a child_id manually, this can be passed as third argument to the function.
+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
+#### 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 a class inheriting from `Sensor` and implement the following methods:
+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();
@@ -320,128 +470,144 @@ If you want to create a custom sensor and register it with NodeManager so it can
     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 with NodeManager:
+You can then instantiate your newly created class and register it with NodeManager:
 ~~~c
-	nodeManager.registerSensor(new SensorCustom(child_id, pin));
+    nodeManager.registerSensor(new SensorCustom(&nodeManager,child_id, pin));
 ~~~
 
-## Configuring the sensors
+### 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*)nodeManager.getSensor(2))->setPulseWidth(50);
+	SensorLatchingRelay* relay = (SensorLatchingRelay*) nodeManager.getSensor(2);
+	relay->setPulseWidth(50);
 ~~~
 
 
-### Sensor's general configuration
+#### Sensor's general configuration
 
-The following methods are available for all the sensors:
+The following methods are available for all the sensors and can be called on the object reference as per the example above:
 ~~~c
-    // where the sensor is attached to (default: not set)
+    // [1] where the sensor is attached to (default: not set)
     void setPin(int value);
-	int getPin();
-    // child_id of this sensor (default: not set)
+    int getPin();
+    // [2] child_id of this sensor (default: not set)
     void setChildId(int value);
-	int getChildId();
+    int getChildId();
     // presentation of this sensor (default: S_CUSTOM)
     void setPresentation(int value);
-	int getPresentation();
-    // type of this sensor (default: V_CUSTOM)
+    int getPresentation();
+    // [3] type of this sensor (default: V_CUSTOM)
     void setType(int value);
-	int getType();
-    // description of the sensor (default: '')
+    int getType();
+    // [4] description of the sensor (default: '')
     void setDescription(char *value);
-    // set this to true if you want destination node to send ack back to this node (default: false)
-    void setAck(bool value);
-    // when queried, send the message multiple times (default: 1)
-    void setRetries(int value);
-    // For some sensors, the measurement can be queried multiple times and an average is returned (default: 1)
+    // [5] For some sensors, the measurement can be queried multiple times and an average is returned (default: 1)
     void setSamples(int value);
-    // If more then one sample has to be taken, set the interval in milliseconds between measurements (default: 0)
+    // [6] If more then one sample has to be taken, set the interval in milliseconds between measurements (default: 0)
     void setSamplesInterval(int value);
-    // if true will report the measure only if different then the previous one (default: false)
-    void setTackLastValue(bool value);
-    // if track last value is enabled, force to send an update after the configured number of cycles (default: -1)
-    void setForceUpdate(int value);
-    // the value type of this sensor (default: TYPE_INTEGER)
+    // [7] if true will report the measure only if different than the previous one (default: false)
+    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();
-	// for float values, set the float precision (default: 2)
-    void setFloatPrecision(int value);
-    // optionally sleep interval in milliseconds before sending each message to the radio network (default: 0)
-    void setSleepBetweenSend(int value);
-    // set the interrupt pin the sensor is attached to so its loop() will be executed only upon that interrupt (default: -1)
-    void setInterruptPin(int value);
-    int getInterruptPin();
+    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, long wait = 0);
-      // if enabled the pins will be automatically powered on while awake and off during sleeping (default: true)
+      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);
-      // manually turn the power on
+      // [13] manually turn the power on
       void powerOn();
-      // manually turn the power off
+      // [14] manually turn the power off
       void powerOff();
     #endif
     // get the latest recorded value from the sensor
     int getValueInt();
     float getValueFloat();
     char* getValueString();
+    // [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)
+    void setReportIntervalMinutes(int value);
+    // [19] After how many hours the sensor will report back its measure (default: 10 minutes)
+    void setReportIntervalHours(int value);
+    // [20] After how many days the sensor will report back its measure (default: 10 minutes)
+    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);
 ~~~
 
-### Sensor's specific configuration
+#### Sensor's specific configuration
 
 Each sensor class can expose additional methods.
 
-#### SensorAnalogInput / SensorLDR
+* SensorAnalogInput / SensorLDR / SensorRain / SensorSoilMoisture
 ~~~c
-    // the analog reference to use (default: not set, can be either INTERNAL or DEFAULT)
+    // [101] the analog reference to use (default: not set, can be either INTERNAL or DEFAULT)
     void setReference(int value);
-    // reverse the value or the percentage (e.g. 70% -> 30%) (default: false)
+    // [102] reverse the value or the percentage (e.g. 70% -> 30%) (default: false)
     void setReverse(bool value);
-    // when true returns the value as a percentage (default: true)
+    // [103] when true returns the value as a percentage (default: true)
     void setOutputPercentage(bool value);
-    // minimum value for calculating the percentage (default: 0)
+    // [104] minimum value for calculating the percentage (default: 0)
     void setRangeMin(int value);
-    // maximum value for calculating the percentage (default: 1024)
+    // [105] maximum value for calculating the percentage (default: 1024)
     void setRangeMax(int value);
 ~~~
 
-#### SensorThermistor
+* SensorThermistor
 ~~~c
-    // resistance at 25 degrees C (default: 10000)
+    // [101] resistance at 25 degrees C (default: 10000)
     void setNominalResistor(long value);
-    // temperature for nominal resistance (default: 25)
+    // [102] temperature for nominal resistance (default: 25)
     void setNominalTemperature(int value);
-    // The beta coefficient of the thermistor (default: 3950)
+    // [103] The beta coefficient of the thermistor (default: 3950)
     void setBCoefficient(int value);
-    // the value of the resistor in series with the thermistor (default: 10000)
+    // [104] the value of the resistor in series with the thermistor (default: 10000)
     void setSeriesResistor(long value);
-    // set a temperature offset
+    // [105] set a temperature offset
     void setOffset(float value);
 ~~~
 
-#### SensorMQ
+* SensorMQ
 ~~~c
-    // define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1);
+    // [101] define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1);
     void setTargetGas(int value);
-    // define the load resistance on the board, in kilo ohms (default: 1);
+    // [102] define the load resistance on the board, in kilo ohms (default: 1);
     void setRlValue(float value);
-    // define the Ro resistance on the board (default: 10000);
+    // [103] define the Ro resistance on the board (default: 10000);
     void setRoValue(float value);
-    // Sensor resistance in clean air (default: 9.83);
+    // [104] Sensor resistance in clean air (default: 9.83);
     void setCleanAirFactor(float value);
-    // define how many samples you are going to take in the calibration phase (default: 50);
+    // [105] define how many samples you are going to take in the calibration phase (default: 50);
     void setCalibrationSampleTimes(int value);
-    // define the time interal(in milisecond) between each samples in the cablibration phase (default: 500);
+    // [106] define the time interal(in milisecond) between each samples in the cablibration phase (default: 500);
     void setCalibrationSampleInterval(int value);
-    // define how many samples you are going to take in normal operation (default: 50);
+    // [107] define how many samples you are going to take in normal operation (default: 50);
     void setReadSampleTimes(int value);
-    // define the time interal(in milisecond) between each samples in the normal operations (default: 5);
+    // [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);
@@ -451,188 +617,255 @@ Each sensor class can expose additional methods.
     void setSmokeCurve(float *value);
 ~~~
 
-#### SensorDigitalOutput / SensorRelay / SensorLatchingRelay
+* 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);
+    void setmVPerAmp(int value);
+    // [102] set ACS offset (default: 2500);
+    void setOffset(int value);
+~~~
+
+* SensorRainGauge / SensorPowerMeter / SensorWaterMeter
 ~~~c
-    // set how to initialize the output (default: LOW)
+    // [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);
-    // if greater than 0, send a pulse of the given duration in ms and then restore the output back to the original value (default: 0)
-    void setPulseWidth(int value);
-    // define which value to set to the output when set to on (default: HIGH)
+    // 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)
     void setOnValue(int value);
-    // when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false)
+    // [104] when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false)
     void setLegacyMode(bool value);
+    // [105] automatically turn the output off after the given number of minutes
+    void setSafeguard(int value);
+    // [106] if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false)
+    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();
 ~~~
 
-#### SensorSwitch / SensorDoor / SensorMotion
+* SensorLatchingRelay (in addition to those available for SensorDigitalOutput / SensorRelay)
 ~~~c
-    // set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE)
+    // [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)
+    void setPinOff(int value);
+    // [203] set the pin which turns the relay on (default: the pin provided while registering the sensor + 1)
+    void setPinOn(int value);
+~~~
+
+*  SensorSwitch / SensorDoor / SensorMotion
+~~~c
+    // [101] set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE)
     void setMode(int value);
-    // milliseconds to wait before reading the input (default: 0)
+    // [102] milliseconds to wait before reading the input (default: 0)
     void setDebounce(int value);
-    // time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0)
+    // [103] time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0)
     void setTriggerTime(int value);
-    // Set initial value on the interrupt pin (default: HIGH)
+    // [104] Set initial value on the interrupt pin (default: HIGH)
     void setInitial(int value);
 ~~~
 
-#### SensorDs18b20
+*  SensorDs18b20
 ~~~c
-    // return the sensors' device address
-    DeviceAddress* getDeviceAddress();
     // returns the sensor's resolution in bits
     int getResolution();
-    // set the sensor's resolution in bits
+    // [101] set the sensor's resolution in bits
     void setResolution(int value);
-    // sleep while DS18B20 calculates temperature (default: false)
+    // [102] sleep while DS18B20 calculates temperature (default: false)
     void setSleepDuringConversion(bool value);
+    // return the sensors' device address
+    DeviceAddress* getDeviceAddress();
 ~~~
 
-#### SensorBME280
-~~~c
-    // define how many pressure samples to keep track of for calculating the forecast (default: 5)
-    void setForecastSamplesCount(int value);
-~~~
-
-#### SensorSonoff
+*  SensorBH1750
 ~~~c
-    // set the button's pin (default: 0)
-    void setButtonPin(int value);
-    // set the relay's pin (default: 12)
-    void setRelayPin(int value);
-    // set the led's pin (default: 13)
-    void setLedPin(int value);
+    // [101] set sensor reading mode, e.g. BH1750_ONE_TIME_HIGH_RES_MODE
+    void setMode(uint8_t mode);
 ~~~
 
-#### SensorBMP085
+*  SensorBME280 / SensorBMP085 / SensorBMP280
 ~~~c
-    // define how many pressure samples to keep track of for calculating the forecast (default: 5)
+    // [101] define how many pressure samples to keep track of for calculating the forecast (default: 5)
     void setForecastSamplesCount(int value);
 ~~~
 
-#### SensorHCSR04
+* SensorHCSR04
 ~~~c
-    // Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor)
+    // [101] Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor)
     void setTriggerPin(int value);
-    // Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor)
+    // [102] Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor)
     void setEchoPin(int value);
-    // Maximum distance we want to ping for (in centimeters) (default: 300)
+    // [103] Maximum distance we want to ping for (in centimeters) (default: 300)
     void setMaxDistance(int value);
 ~~~
 
-#### SensorACS712
+*  SensorSonoff
 ~~~c
-    // 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);
-    // set ACS offset (default: 2500);
-    void setOffset(int value);
+    // [101] set the button's pin (default: 0)
+    void setButtonPin(int value);
+    // [102] set the relay's pin (default: 12)
+    void setRelayPin(int value);
+    // [103] set the led's pin (default: 13)
+    void setLedPin(int value);
 ~~~
 
-#### SensorRainGauge
+* SensorMHZ19
 ~~~c
-    // set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60);
-    void setReportInterval(int value);
-    // set how many mm of rain to count for each tip (default: 0.11);
-    void setSingleTip(float value);
+    // set the RX and TX pins for the software serial port to talk to the sensor
+    void setRxTx(int rxpin, int txpin);
 ~~~
 
-## Upload your sketch
-
-Upload your sketch to your arduino board as you are used to.
-
-## Verify if everything works fine
+* SensorTSL2561
+~~~c
+    // [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)
+    void setTiming(int value);
+    // [103] set the spectrum, possible values are SensorTSL2561::VISIBLE (0), SensorTSL2561::FULLSPECTRUM (1), SensorTSL2561::INFRARED (2), SensorTSL2561::FULL (3) (default: visible)
+    void setSpectrum(int value);
+    // [104] set the i2c address values are SensorTSL2561::ADDR_FLOAT, SensorTSL2561::ADDR_LOW, SensorTSL2561::ADDR_HIGH
+    void setAddress(int value);
+~~~
 
-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.
+* 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)
+    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);
+    // fade the output from the current value to the target provided in the range 0-100
+    void fadeTo(int value);
+~~~
 
-## Communicate with each sensor
+### Creating a gateway
 
-You can interact with each registered sensor asking to execute their main tasks by sending to the child id a `REQ` command. For example to request the temperature to node_id 254 and child_id 1:
+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.
 
-`254;1;2;0;0;`
+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.
 
-To activate a relay connected to the same node, child_id 100:
+### Upload your sketch
 
-`254;100;2;0;2;1`
+Upload your sketch to your arduino board as you are used to.
 
-No need to implement anything on your side since for built-in sensor types this is handled automatically. 
-Once the node will be sleeping, it will report automatically each measure at the end of every sleep cycle.
+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 the node
+### Communicate with NodeManager and its sensors
 
-NodeManager exposes a configuration service 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.
-The following custom commands are available:
+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:
 
-NodeManager command  | Description
- ------------- | -------------
-BATTERY | Report the battery level back to the gateway/controller
-HELLO | Hello request
-REBOOT | Reboot the board
-CLEAR | Wipe from the EEPROM NodeManager's settings
-VERSION | Respond with NodeManager's version
-IDxxx |  Change the node id to the provided one. E.g. ID025: change the node id to 25. Requires a reboot to take effect
-INTVLnnnX | Set the wait/sleep interval to nnn where X is S=Seconds, M=mins, H=Hours, D=Days. E.g. INTVL010M would be 10 minutes
-MODEx | change the way the node behaves. 0: stay awake withtout executing each sensors' loop(), 1: go to sleep for the configured interval, 2: wait for the configured interval, 3: stay awake and execute each sensors' loop()
-AWAKE | When received after a sleeping cycle or during wait, abort the cycle and stay awake
+`254;1;2;0;0;`
 
-For example, to request the battery level to node id 254:
+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;200;2;0;48;BATTERY`
+`254;100;1;0;2;1`
 
-To set the sleeping cycle to 1 hour:
+No need to implement anything on your side since for built-in sensors this is handled automatically. 
 
-`254;200;2;0;48;INTVL001H`
+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. 
 
-To ask the node to start sleeping (and waking up based on the previously configured interval):
+Almost all the functions made available through the API can be called remotely. To do so, the payload must be in the format `<function_id>[,<value_to_set>]` 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:
+~~~c
+    // [2] Send a battery level report to the controller
+    void batteryReport();
+~~~
+In this case `function_id` will be 2. To request a battery report to the node_id 100, send the following message:
+`<node_id>;<configuration_child_id>;<req>;0;<V_CUSTOM>;<function_id>`
+`100;200;2;0;48;2`
 
-`254;200;2;0;48;MODE1`
+The change the sleep time to e.g. 10 minutes:
+~~~c
+    // [4] set the duration (in minutes) of a sleep cycle
+    void setSleepMinutes(int value);
+~~~
+`<node_id>;<configuration_child_id>;<req>;0;<V_CUSTOM>;<function_id>,<value>`
+`100;200;2;0;48;4,10`
 
-To wake up a node previously configured with `MODE1`, send the following just after reporting `AWAKE`:
+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`
 
-`254;200;2;0;48;WAKEUP`
+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:
+~~~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`
 
-In addition, NodeManager will report with custom messages every time the board is going to sleep (`SLEEPING`) or it is awake (`AWAKE`).
+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`
 
-If `PERSIST` is enabled, the settings provided with `INTVLnnnX` and `MODEx` are saved to the EEPROM to be persistent even after rebooting the board.
+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).
 
-# How it works
+## Understanding NodeManager: how it works
 
-A NodeManager object must be created and called from within your sketch during `before()`, `presentation()`, `loop()` and `receive()` to work properly. NodeManager will do the following during each phase:
+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 (e.g. stop sleeping when the pin is connected to ground or wake up and notify when a motion sensor has trigger)
+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
 * Call `before()` of each registered sensor
 
-### Sensor::before()
+Sensor::before():
 * Call sensor-specific implementation of before by invoking `onBefore()` to initialize the sensor
 
-## NodeManager::setup()
+NodeManager::setup():
 * Send a custom message with a STARTED payload to the controller
 * Call `setup()` of each registered sensor
 
-### Sensor::setup()
+Sensor::setup():
 * Call sensor-specific implementation of setup by invoking `onSetup()` to initialize the sensor
 
-## NodeManager::loop()
-* If all the sensors are powered by an arduino pin, this is set to HIGH
+NodeManager::loop():
+* If all the sensors are powered by an arduino pin, this is turned on
 * Call `loop()` of each registered sensor
-* If all the sensors are powered by an arduino pin, this is set to LOW
+* If all the sensors are powered by an arduino pin, this is turned off
 
-### Sensor::loop()
-* If the sensor is powered by an arduino pin, this is set to HIGH
-* For each registered sensor, the sensor-specific `onLoop()` is called. If multiple samples are requested, this is run multiple times.
+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.
-* If the sensor is powered by an arduino pin, this is set to LOW
+* If the sensor is powered by an arduino pin, this is turned off
 
-## NodeManager::receive()
+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
 
-### Sensor::receive()
+Sensor::receive(): 
 * Invoke `Sensor::loop()` which will execute the sensor main taks and eventually call `Sensor::onReceive()`
 
-# Examples
+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:
@@ -645,7 +878,7 @@ Set battery minimum and maxium voltage. This will be used to calculate the level
 Instruct the board to sleep for 10 minutes at each cycle:
 
 ~~~c
-    nodeManager.setSleep(SLEEP,10,MINUTES);
+    nodeManager.setSleepMinutes(10);
 ~~~
 
 Configure a wake up pin. When pin 3 is connected to ground, the board will stop sleeping:
@@ -666,7 +899,7 @@ Register a thermistor sensor attached to pin A2. NodeManager will then send the
    nodeManager.registerSensor(SENSOR_THERMISTOR,A2);
 ~~~
 
-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:
+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:
 
 ~~~c
    nodeManager.registerSensor(SENSOR_SHT21);
@@ -697,18 +930,17 @@ Register a latching relay connecting to pin 6 (set) and pin 7 (unset):
 
 ~~~c
   nodeManager.registerSensor(SENSOR_LATCHING_RELAY,6);
-  nodeManager.registerSensor(SENSOR_LATCHING_RELAY,7);
 ~~~
 
-# Example Sketches
+## Example Sketches
 
-## Analog Light and Temperature Sensor
+*  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. 
 
-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 10 cycles and report the measures in the appropriate format. This sketch requires MODULE_ANALOG_INPUT enabled in the global config.h file.
+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.
 
-Even if the sensor is sleeping most of the time, it can be potentially woke up by sending a V_CUSTOM message with a WAKEUP payload 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 with a V_CUSTOM message to the NodeManager service child id, etc.
+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
 /*
@@ -742,7 +974,8 @@ void before() {
   /*
    * Register below your sensors
   */
-  nodeManager.setSleep(SLEEP,10,MINUTES); 
+  nodeManager.setSleepMinutes(10);
+  nodeManager.setReportIntervalMinutes(10);
   nodeManager.registerSensor(SENSOR_THERMISTOR,A1);
   nodeManager.registerSensor(SENSOR_LDR,A2);
   /*
@@ -753,8 +986,6 @@ void before() {
 
 // presentation
 void presentation() {
-  // Send the sketch version information to the gateway and Controller
-	sendSketchInfo(SKETCH_NAME,SKETCH_VERSION);
   // call NodeManager presentation routine
   nodeManager.presentation();
 
@@ -778,9 +1009,15 @@ void receive(const MyMessage &message) {
   // call NodeManager receive routine
   nodeManager.receive(message);
 }
+
+// receiveTime
+void receiveTime(unsigned long ts) {
+  // call NodeManager receiveTime routine
+  nodeManager.receiveTime(ts);
+}
 ~~~
 
-## Motion Sensor
+*  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.
 
@@ -801,6 +1038,10 @@ Documentation available on: https://github.com/mysensors/NodeManager
  
 // load user settings
 #include "config.h"
+// include supporting libraries
+#ifdef MY_GATEWAY_ESP8266
+  #include <ESP8266WiFi.h>
+#endif
 // load MySensors library
 #include <MySensors.h>
 // load NodeManager library
@@ -816,7 +1057,7 @@ void before() {
   /*
    * Register below your sensors
   */
-  nodeManager.setSleep(SLEEP,60,MINUTES); 
+  nodeManager.setSleepHours(1);
   nodeManager.registerSensor(SENSOR_MOTION,3);
 
   /*
@@ -827,8 +1068,6 @@ void before() {
 
 // presentation
 void presentation() {
-  // Send the sketch version information to the gateway and Controller
-	sendSketchInfo(SKETCH_NAME,SKETCH_VERSION);
   // call NodeManager presentation routine
   nodeManager.presentation();
 
@@ -852,11 +1091,17 @@ void receive(const MyMessage &message) {
   // call NodeManager receive routine
   nodeManager.receive(message);
 }
+
+// receiveTime
+void receiveTime(unsigned long ts) {
+  // call NodeManager receiveTime routine
+  nodeManager.receiveTime(ts);
+}
 ~~~
 
-## Boiler Sensor
+*  Boiler 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. This is why we have to register two sensors against NodeManager so to control the two funtions indipendently. Since using a SENSOR_LATCHING_RELAY type of sensor, NodeManager will take care of just sending out a single pulse only when a REQ command of type V_STATUS is sent to one or the other child id.
+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.
 
 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.
 
@@ -879,6 +1124,10 @@ Documentation available on: https://github.com/mysensors/NodeManager
  
 // load user settings
 #include "config.h"
+// include supporting libraries
+#ifdef MY_GATEWAY_ESP8266
+  #include <ESP8266WiFi.h>
+#endif
 // load MySensors library
 #include <MySensors.h>
 // load NodeManager library
@@ -896,9 +1145,8 @@ void before() {
   */
   nodeManager.setBatteryMin(1.8);
   nodeManager.setBatteryMax(3.2);
-  nodeManager.setSleep(SLEEP,5,MINUTES);
+  nodeManager.setSleepMinutes(5);
   nodeManager.registerSensor(SENSOR_LATCHING_RELAY,6);
-  nodeManager.registerSensor(SENSOR_LATCHING_RELAY,7);
 
   /*
    * Register above your sensors
@@ -908,8 +1156,6 @@ void before() {
 
 // presentation
 void presentation() {
-  // Send the sketch version information to the gateway and Controller
-	sendSketchInfo(SKETCH_NAME,SKETCH_VERSION);
   // call NodeManager presentation routine
   nodeManager.presentation();
 
@@ -933,10 +1179,16 @@ void receive(const MyMessage &message) {
   // call NodeManager receive routine
   nodeManager.receive(message);
 }
+
+// receiveTime
+void receiveTime(unsigned long ts) {
+  // call NodeManager receiveTime routine
+  nodeManager.receiveTime(ts);
+}
 ~~~
 
 
-## Rain and Soil Moisture Sensor
+*  Rain and Soil Moisture Sensor
 
 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. 
 
@@ -964,6 +1216,10 @@ Documentation available on: https://github.com/mysensors/NodeManager
  
 // load user settings
 #include "config.h"
+// include supporting libraries
+#ifdef MY_GATEWAY_ESP8266
+  #include <ESP8266WiFi.h>
+#endif
 // load MySensors library
 #include <MySensors.h>
 // load NodeManager library
@@ -980,7 +1236,8 @@ void before() {
    * Register below your sensors
   */
   analogReference(DEFAULT);
-  nodeManager.setSleep(SLEEP,10,MINUTES);
+  nodeManager.setSleepMinutes(10);
+  nodeManager.setReportIntervalMinutes(10);
   
   int rain = nodeManager.registerSensor(SENSOR_ANALOG_INPUT,A1);
   int soil = nodeManager.registerSensor(SENSOR_ANALOG_INPUT,A2);
@@ -1012,8 +1269,6 @@ void before() {
 
 // presentation
 void presentation() {
-  // Send the sketch version information to the gateway and Controller
-	sendSketchInfo(SKETCH_NAME,SKETCH_VERSION);
   // call NodeManager presentation routine
   nodeManager.presentation();
 
@@ -1037,9 +1292,61 @@ void receive(const MyMessage &message) {
   // call NodeManager receive routine
   nodeManager.receive(message);
 }
+
+// receiveTime
+void receiveTime(unsigned long ts) {
+  // call NodeManager receiveTime routine
+  nodeManager.receiveTime(ts);
+}
 ~~~
 
-# Release Notes
+## Contributing
+
+Contributes to NodeManager are of course more than welcome. 
+
+### Reporting an issue or request an enhancement
+
+For reporting an issue, requesting support for a new sensor or any other kind of enhancement, please drop a message either on the project's main page (<https://www.mysensors.org/download/node-manager>), on the MySensors Forum (<https://forum.mysensors.org/category/43/nodemanager>) or open an issue directly on Github (<https://github.com/mysensors/NodeManager/issues>).
+
+
+### Contributing to the code
+
+If you want to contribute to the code, a pull request on Github is the way to go. First of all setup your development environment:
+
+* Create a copy of the project in your Github account by clicking on the "Fork" button on `https://github.com/mysensors/NodeManager` 
+* Check the copy actually exists on `https://github.com/<username>/NodeManager`
+* Clone your repository on your computer: `git clone https://github.com/<username>/NodeManager.git`
+* Configure the main project's repository as an upstream: `git remote add upstream https://github.com/mysensors/NodeManager.git`
+* Create and switch to a local development branch: `git checkout -b development origin/development`
+
+Before applying any change, ensure you have the latest development version available:
+* Switch to your local development branch: `git checkout development`
+* Fetch the latest version from the main project's repository: `git fetch upstream`
+* Merge into your development copy all the changes from the main repository: `git merge development upstream/development`
+* Update the development branch of your repository: `git push origin development`
+
+Create a branch for the fix/feature you want to work on and apply changes to the code:
+* Create and switch to a new branch (give it a significant name, e.g. fix/enum-sensors): `git checkout -b <yourbranch>`
+* Do any required change to the code
+* Include all the files changed for your commit: `git add .`
+* Ensure both the main sketch and the config.h file do not present any change
+* Commit the changes: `git  commit -m"Use enum instead of define for defining each sensor #121"`
+* Push the branch with the changes to your repository: `git push origin <yourbranch>`
+* Visit `https://github.com/<username>/NodeManager/branches` and click the "New pull request" button just aside your newly created branch
+* Fill in the request with a significant title and description and select the "development" branch from the main repository to be compared against your branch. Ensure there is one or more issues the pull request will fix and make it explicit within the description
+* Submit the request and start the discussion
+* Any additional commits to your branch which will be presented within the same pull request
+* When the pull request is merged, delete your working branch: `git branch -D <yourbranch>`
+* Update your local and remote development branch as per the instructions above
+
+If there are changes introduced to the development branch that conflicts with an open pull request, you will have to resolve the conflicts and update the PR:
+* Fetch and merge into development any change from upstream/development as detailed above
+* Switch to your branch: `git checkout <yourbranch>`
+* Rebase the branch you filed the PR from against your updated development branch: `git rebase development`
+* Resolve the conflicts and commit again
+* Force push your updated branch so the PR gets updated: `git push HEAD:<yourbranch> -f`
+
+## Release Notes
 
 v1.0:
 
@@ -1097,4 +1404,28 @@ v1.5:
 * ESP8266WiFi.h has to be included in the main sketch if MY_GATEWAY_ESP8266 is defined
 * Added receiveTime() wrapper in the main sketch
 * Fixed the logic for output sensors
-* Added common gateway settings in config.h
\ No newline at end of file
+* Added common gateway settings in config.h
+
+v1.6:
+* Introduced new remote API to allow calling almost ALL NodeManager's and its sensors' functions remotely
+* Reporting interval configuration is now indipendent from the sleep cycle
+* Reporting interval can be customized per-sensor
+* All intervals (measure/battery reports) are now time-based
+* Added support for BMP280 temperature and pressure sensor
+* Added support for RS485 serial transport 
+* Added support for TSL2561 light sensor
+* Added support for DHT21 temperature/humidity sensor
+* Added support for AM2320 temperature/humidity sensor
+* Added support for PT100 high temperature sensor
+* Added support for MH-Z19 CO2 sensor
+* Added support for analog rain and soil moisture sensors
+* Added support for generic dimmer sensor (PWM output)
+* Added support for power and water meter pulse sensors
+* Radio signal level (RSSI) is now reported automatically like the battery level
+* SensorRainGauge now supports sleep mode
+* SensorSwitch now supports awake mode
+* SensorLatchingRealy now handles automatically both on and off commands
+* SensorMQ now depends on its own module
+* Added safeguard (automatic off) to SensorDigitalOutput
+* Any sensor can now access all NodeManager's functions
+* DHT sensor now using MySensors' DHT library
diff --git a/config.h b/config.h
old mode 100644
new mode 100755
index eded817ecdd4e1a5fae01362df58035e1247f369..d93ab3f9292125085d627453d501ad829ae49478
--- a/config.h
+++ b/config.h
@@ -16,6 +16,7 @@
 #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
@@ -23,18 +24,40 @@
 //#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_DEBUG_VERBOSE_RFM69
 //#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
@@ -97,12 +120,14 @@
 #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
+// 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_MQ, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN_GAUGE
+// 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
@@ -110,7 +135,7 @@
 #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
+// 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
@@ -130,5 +155,22 @@
 #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