diff --git a/NodeManager.cpp b/NodeManager.cpp
index 0002d32a95c872332605c7787e138bd38c5845e8..5e5e6117a6088ee12d2a93a11e90af30126c9746 100644
--- a/NodeManager.cpp
+++ b/NodeManager.cpp
@@ -195,6 +195,53 @@ int Timer::getUnit() {
   return _unit;
 }
 
+
+/******************************************
+    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
 */
@@ -208,6 +255,7 @@ Sensor::Sensor(NodeManager* node_manager, int child_id, int pin) {
   _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);
 }
@@ -416,11 +464,50 @@ void Sensor::loop(const MyMessage & message) {
 // 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 8: setForceUpdateCycles(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 15: setReportIntervalCycles(request.getValueInt()); break;
+    case 16: setReportIntervalMinutes(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
@@ -468,7 +555,7 @@ bool Sensor::_isWorthSending(bool comparison) {
   return false;
 }
 
-
+#if MODULE_ANALOG_INPUT == 1
 /*
    SensorAnalogInput
 */
@@ -528,6 +615,20 @@ 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));
+}
+
 // read the analog input
 int SensorAnalogInput::_getAnalogRead() {
   #ifndef MY_GATEWAY_ESP8266
@@ -639,6 +740,19 @@ 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));
+}
 
 /*
    SensorML8511
@@ -688,6 +802,10 @@ 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) {
+}
+
 // 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;
@@ -743,6 +861,17 @@ 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));
+}
+
 /*
    SensorRainGauge
 */
@@ -825,6 +954,17 @@ void SensorRainGauge::onReceive(const MyMessage & message) {
   }
 }
 
+// what to do when receiving a remote message
+void SensorRainGauge::onProcess(Request & request) {
+  int function = request.getFunction();
+  switch(function) {
+    case 101: setReportInterval(request.getValueInt()); break;
+    case 102: setSingleTip(request.getValueFloat()); break;
+    default: return;
+  }
+  _send(_msg_service.set(function));
+}
+
 /*
    SensorRain
 */
@@ -855,151 +995,9 @@ SensorSoilMoisture::SensorSoilMoisture(NodeManager* node_manager, int child_id,
   setRangeMin(100);
 }
 
+#endif
 
-/*
- * SensorMQ
- */
-SensorMQ::SensorMQ(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id,pin) {
-  setPresentation(S_AIR_QUALITY);
-  setType(V_LEVEL);
-}
-
-//setter/getter
-void SensorMQ::setRlValue(float value) {
-  _rl_value = value;
-}
-void SensorMQ::setRoValue(float value) {
-  _ro = value;
-}
-void SensorMQ::setCleanAirFactor(float value) {
-  _ro_clean_air_factor = value;
-}
-void SensorMQ::setCalibrationSampleTimes(int value) {
-  _calibration_sample_times = value;
-}
-void SensorMQ::setCalibrationSampleInterval(int value){
-  _calibration_sample_interval = value;
-}
-void SensorMQ::setReadSampleTimes(int value) {
-  _read_sample_times = value;
-}
-void SensorMQ::setReadSampleInterval(int value) {
-  _read_sample_interval = value;
-}
-void SensorMQ::setLPGCurve(float *value) {
-  _LPGCurve[0] = value[0];
-  _LPGCurve[2] = value[1];
-  _LPGCurve[2] = value[2];
-}
-void SensorMQ::setCOCurve(float *value) {
-  _COCurve[0] = value[0];
-  _COCurve[2] = value[1];
-  _COCurve[2] = value[2];
-}
-void SensorMQ::setSmokeCurve(float *value) {
-  _SmokeCurve[0] = value[0];
-  _SmokeCurve[2] = value[1];
-  _SmokeCurve[2] = value[2];
-}
-
-// what to do during before
-void SensorMQ::onBefore() {
-  // prepare the pin for input
-  pinMode(_pin, INPUT);
-}
-
-// what to do during setup
-void SensorMQ::onSetup() {
-  _ro = _MQCalibration();
-}
-
-// what to do during loop
-void SensorMQ::onLoop() {
-  if (_pin == -1) return;
-  // calculate rs/ro
-  float mq = _MQRead()/_ro;
-  // calculate the ppm
-  float lpg = _MQGetGasPercentage(mq,_gas_lpg);
-  float co = _MQGetGasPercentage(mq,_gas_co);
-  float smoke = _MQGetGasPercentage(mq,_gas_smoke);
-  // assign to the value the requested gas
-  uint16_t value;
-  if (_target_gas == _gas_lpg) value = lpg;
-  if (_target_gas == _gas_co) value = co;
-  if (_target_gas == _gas_smoke) value = smoke;
-  #if DEBUG == 1
-    Serial.print(F("MQ I="));
-    Serial.print(_child_id);
-    Serial.print(F(" V="));
-    Serial.print(value);
-    Serial.print(F(" LPG="));
-    Serial.print(lpg);
-    Serial.print(F(" CO="));
-    Serial.print(co);
-    Serial.print(F(" SMOKE="));
-    Serial.println(smoke);
-  #endif
-  // store the value
-  _value_int = (int16_t)ceil(value);
-}
-
-// what to do as the main task when receiving a message
-void SensorMQ::onReceive(const MyMessage & message) {
-  if (message.getCommand() == C_REQ) onLoop();
-}
-
-// returns the calculated sensor resistance
-float SensorMQ::_MQResistanceCalculation(int raw_adc) {
-  return ( ((float)_rl_value*(1023-raw_adc)/raw_adc));
-}
-
-//  This function assumes that the sensor is in clean air
-float SensorMQ::_MQCalibration() {
-  int i;
-  float val=0;
-  //take multiple samples
-  for (i=0; i< _calibration_sample_times; i++) {  
-    val += _MQResistanceCalculation(analogRead(_pin));
-    wait(_calibration_sample_interval);
-  }
-  //calculate the average value
-  val = val/_calibration_sample_times;                   
-  //divided by RO_CLEAN_AIR_FACTOR yields the Ro
-  val = val/_ro_clean_air_factor;
-  //according to the chart in the datasheet
-  return val;
-}
-
-// This function use MQResistanceCalculation to caculate the sensor resistenc (Rs).
-float SensorMQ::_MQRead() {
-  int i;
-  float rs=0;
-  for (i=0; i<_read_sample_times; i++) {
-    rs += _MQResistanceCalculation(analogRead(_pin));
-    wait(_read_sample_interval);
-  }
-  rs = rs/_read_sample_times;
-  return rs;
-}
-
-// This function passes different curves to the MQGetPercentage function which calculates the ppm (parts per million) of the target gas.
-int SensorMQ::_MQGetGasPercentage(float rs_ro_ratio, int gas_id) {
-  if ( gas_id == _gas_lpg ) {
-    return _MQGetPercentage(rs_ro_ratio,_LPGCurve);
-  } else if ( gas_id == _gas_co) {
-    return _MQGetPercentage(rs_ro_ratio,_COCurve);
-  } else if ( gas_id == _gas_smoke) {
-    return _MQGetPercentage(rs_ro_ratio,_SmokeCurve);
-  }
-  return 0;
-}
-
-// returns ppm of the target gas
-int SensorMQ::_MQGetPercentage(float rs_ro_ratio, float *pcurve) {
-  return (pow(10,( ((log10(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0])));
-}
-
-
+#if MODULE_DIGITAL_INPUT == 1
 /*
    SensorDigitalInput
 */
@@ -1039,12 +1037,16 @@ 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) {
+}
+#endif
 
+#if MODULE_DIGITAL_OUTPUT == 1
 /*
    SensorDigitalOutput
 */
 
-// contructor
 SensorDigitalOutput::SensorDigitalOutput(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id, pin) {
   _safeguard_timer = new Timer(node_manager);
 }
@@ -1114,6 +1116,21 @@ void SensorDigitalOutput::onReceive(const MyMessage & message) {
   }
 }
 
+// what to do when receiving a remote message
+void SensorDigitalOutput::onProcess(Request & request) {
+  int function = request.getFunction();
+  switch(function) {
+    case 101: setInitialValue(request.getValueInt()); break;
+    case 102: setPulseWidth(request.getValueInt()); break;
+    case 103: setOnValue(request.getValueInt()); break;
+    case 104: setLegacyMode(request.getValueInt()); break;
+    case 105: setSafeguard(request.getValueInt()); break;
+    case 106: setInputIsElapsed(request.getValueInt()); break;
+    default: return;
+  }
+  _send(_msg_service.set(function));
+}
+
 // write the value to the output
 void SensorDigitalOutput::set(int value) {
   if (_input_is_elapsed) {
@@ -1169,14 +1186,7 @@ SensorRelay::SensorRelay(NodeManager* node_manager, int child_id, int pin): Sens
   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;
-    _last_value_int = -1;
-}
-*/
+
 /*
    SensorLatchingRelay
 */
@@ -1187,6 +1197,7 @@ SensorLatchingRelay::SensorLatchingRelay(NodeManager* node_manager, int child_id
   setPulseWidth(50);
 }
 
+#endif
 /*
    SensorDHT
 */
@@ -1258,6 +1269,10 @@ 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) {
+}
 #endif
 
 /*
@@ -1329,6 +1344,10 @@ 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) {
+}
 #endif
 
 /*
@@ -1340,6 +1359,7 @@ SensorHTU21D::SensorHTU21D(NodeManager* node_manager, int child_id, int pin): Se
 }
 #endif 
 
+#if MODULE_SWITCH == 1
 /*
  * SensorSwitch
  */
@@ -1407,6 +1427,19 @@ void SensorSwitch::onReceive(const MyMessage & message) {
   if (message.getCommand() == C_REQ) onLoop();
 }
 
+// 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));
+}
+
 /*
  * SensorDoor
  */
@@ -1424,6 +1457,7 @@ SensorMotion::SensorMotion(NodeManager* node_manager, int child_id, int pin): Se
   // set initial value to LOW
   setInitial(LOW);
 }
+#endif
 
 /*
    SensorDs18b20
@@ -1478,6 +1512,17 @@ 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));
+}
+
 // function to print a device address
 DeviceAddress* SensorDs18b20::getDeviceAddress() {
   return &_device_address;
@@ -1536,6 +1581,10 @@ 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) {
+}
 #endif
 
 /*
@@ -1580,6 +1629,10 @@ 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) {
+}
 #endif
 
 
@@ -1640,7 +1693,17 @@ 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));
+}
 
+// calculate and send the forecast back
 void SensorBosch::_forecast(float pressure) {
   if (isnan(pressure)) return;
   // Calculate the average of the last n minutes.
@@ -1860,35 +1923,98 @@ void SensorBMP085::onLoop() {
 }
 #endif
 
-
 /*
-   SensorSonoff
+   SensorHCSR04
 */
-#if MODULE_SONOFF == 1
+#if MODULE_HCSR04 == 1
 // contructor
-SensorSonoff::SensorSonoff(NodeManager* node_manager, int child_id): Sensor(node_manager, child_id,1) {
-  setPresentation(S_BINARY);
-  setType(V_STATUS);
+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 SensorSonoff::setButtonPin(int value) {
-    _button_pin = value;
+void SensorHCSR04::setTriggerPin(int value) {
+  _trigger_pin = value;
 }
-void SensorSonoff::setRelayPin(int value) {
-    _relay_pin = value;
+void SensorHCSR04::setEchoPin(int value) {
+  _echo_pin = value;
 }
-void SensorSonoff::setLedPin(int value) {
-    _led_pin = value;
+void SensorHCSR04::setMaxDistance(int value) {
+  _max_distance = value;
 }
 
-// what to do during before
-void SensorSonoff::onBefore() {
+// what to do during setup
+void SensorHCSR04::onSetup() {
 }
 
-// what to do during setup
-void SensorSonoff::onSetup() {
-  // Setup the button
+// 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));
+}
+#endif
+
+/*
+   SensorSonoff
+*/
+#if MODULE_SONOFF == 1
+// contructor
+SensorSonoff::SensorSonoff(NodeManager* node_manager, int child_id): Sensor(node_manager, child_id,1) {
+  setPresentation(S_BINARY);
+  setType(V_STATUS);
+}
+
+// setter/getter
+void SensorSonoff::setButtonPin(int value) {
+    _button_pin = value;
+}
+void SensorSonoff::setRelayPin(int value) {
+    _relay_pin = value;
+}
+void SensorSonoff::setLedPin(int value) {
+    _led_pin = value;
+}
+
+// what to do during before
+void SensorSonoff::onBefore() {
+}
+
+// what to do during setup
+void SensorSonoff::onSetup() {
+  // Setup the button
   pinMode(_button_pin, INPUT_PULLUP);
   // After setting up the button, setup debouncer
   _debouncer.attach(_button_pin);
@@ -1931,6 +2057,18 @@ 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));
+}
+
 // toggle the state
 void SensorSonoff::_toggle() {
   // toggle the state
@@ -1962,96 +2100,211 @@ void SensorSonoff::_blink() {
 
 
 /*
-   SensorHCSR04
+   SensorMCP9808
 */
-#if MODULE_HCSR04 == 1
+#if MODULE_MCP9808 == 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;
+SensorMCP9808::SensorMCP9808(NodeManager* node_manager, int child_id, Adafruit_MCP9808* mcp): Sensor(node_manager, child_id,A2) {
+  _mcp = mcp;
+  setPresentation(S_TEMP);
+  setType(V_TEMP);
+  setValueType(TYPE_FLOAT);
 }
 
 // what to do during before
-void 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;
+void SensorMCP9808::onBefore() {
 }
 
 // what to do during setup
-void SensorHCSR04::onSetup() {
+void SensorMCP9808::onSetup() {
 }
 
 // what to do during loop
-void SensorHCSR04::onLoop() {
-  int distance = _node_manager->getIsMetric() ? _sonar->ping_cm() : _sonar->ping_in();
+void SensorMCP9808::onLoop() {
+  float temperature = _mcp->readTempC();
+  // convert it
+  temperature = _node_manager->celsiusToFahrenheit(temperature);
   #if DEBUG == 1
-    Serial.print(F("HC I="));
+    Serial.print(F("MCP I="));
     Serial.print(_child_id);
-    Serial.print(F(" D="));
-    Serial.println(distance);
+    Serial.print(F(" T="));
+    Serial.println(temperature);
   #endif
-  _value_int = distance;
+  // store the value
+  if (! isnan(temperature)) _value_float = temperature;
 }
 
 // what to do as the main task when receiving a message
-void SensorHCSR04::onReceive(const MyMessage & message) {
+void SensorMCP9808::onReceive(const MyMessage & message) {
   if (message.getCommand() == C_REQ) onLoop();
 }
+
+// what to do when receiving a remote message
+void SensorMCP9808::onProcess(Request & request) {
+}
 #endif
 
+
 /*
-   SensorMCP9808
-*/
-#if MODULE_MCP9808 == 1
-// contructor
-SensorMCP9808::SensorMCP9808(NodeManager* node_manager, int child_id, Adafruit_MCP9808* mcp): Sensor(node_manager, child_id,A2) {
-  _mcp = mcp;
-  setPresentation(S_TEMP);
-  setType(V_TEMP);
-  setValueType(TYPE_FLOAT);
+ * SensorMQ
+ */
+#if MODULE_MQ == 1
+SensorMQ::SensorMQ(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id,pin) {
+  setPresentation(S_AIR_QUALITY);
+  setType(V_LEVEL);
+}
+
+//setter/getter
+void SensorMQ::setRlValue(float value) {
+  _rl_value = value;
+}
+void SensorMQ::setRoValue(float value) {
+  _ro = value;
+}
+void SensorMQ::setCleanAirFactor(float value) {
+  _ro_clean_air_factor = value;
+}
+void SensorMQ::setCalibrationSampleTimes(int value) {
+  _calibration_sample_times = value;
+}
+void SensorMQ::setCalibrationSampleInterval(int value){
+  _calibration_sample_interval = value;
+}
+void SensorMQ::setReadSampleTimes(int value) {
+  _read_sample_times = value;
+}
+void SensorMQ::setReadSampleInterval(int value) {
+  _read_sample_interval = value;
+}
+void SensorMQ::setLPGCurve(float *value) {
+  _LPGCurve[0] = value[0];
+  _LPGCurve[2] = value[1];
+  _LPGCurve[2] = value[2];
+}
+void SensorMQ::setCOCurve(float *value) {
+  _COCurve[0] = value[0];
+  _COCurve[2] = value[1];
+  _COCurve[2] = value[2];
+}
+void SensorMQ::setSmokeCurve(float *value) {
+  _SmokeCurve[0] = value[0];
+  _SmokeCurve[2] = value[1];
+  _SmokeCurve[2] = value[2];
 }
 
 // what to do during before
-void SensorMCP9808::onBefore() {
+void SensorMQ::onBefore() {
+  // prepare the pin for input
+  pinMode(_pin, INPUT);
 }
 
 // what to do during setup
-void SensorMCP9808::onSetup() {
+void SensorMQ::onSetup() {
+  _ro = _MQCalibration();
 }
 
 // what to do during loop
-void SensorMCP9808::onLoop() {
-  float temperature = _mcp->readTempC();
-  // convert it
-  temperature = _node_manager->celsiusToFahrenheit(temperature);
+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("MCP I="));
+    Serial.print(F("MQ I="));
     Serial.print(_child_id);
-    Serial.print(F(" T="));
-    Serial.println(temperature);
+    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
-  if (! isnan(temperature)) _value_float = temperature;
+  _value_int = (int16_t)ceil(value);
 }
 
 // what to do as the main task when receiving a message
-void SensorMCP9808::onReceive(const MyMessage & 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));
+}
+
+// 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
 
 /*******************************************
@@ -2116,9 +2369,9 @@ int NodeManager::getSleepUnit() {
   return _sleep_unit;
 }
 void NodeManager::setSleep(int value1, int value2, int value3) {
-  _sleep_mode = value1;
-  _sleep_time = value2;
-  _sleep_unit = value3;
+  setMode(value1);
+  setSleepTime(value2);
+  setSleepUnit(value3);
 }
 void NodeManager::setSleepInterruptPin(int value) {
   _sleep_interrupt_pin = value;
@@ -2186,7 +2439,6 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) {
     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_MQ) return registerSensor(new SensorMQ(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_GAUGE) return registerSensor(new SensorRainGauge(this,child_id, pin));
@@ -2339,6 +2591,11 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) {
       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
   else {
     #if DEBUG == 1
       Serial.print(F("INVALID "));
@@ -2442,26 +2699,9 @@ void NodeManager::before() {
     Serial.print(F("INT2 M="));
     Serial.println(_interrupt_2_mode);
   #endif
-  #if REMOTE_CONFIGURATION == 1 && PERSIST == 1
-    // restore sleep configuration from eeprom
-    if (loadState(EEPROM_SLEEP_SAVED) == 1) {
-      // sleep settings found in the eeprom, restore them
-      _sleep_mode = loadState(EEPROM_SLEEP_MODE);
-      _sleep_time = loadState(EEPROM_SLEEP_TIME_MINOR);
-      int major = loadState(EEPROM_SLEEP_TIME_MAJOR);
-      if (major == 1) _sleep_time =  _sleep_time + 250;
-      else if (major == 2) _sleep_time =  _sleep_time + 250 * 2;
-      else if (major == 3) _sleep_time =  _sleep_time + 250 * 3;
-      _sleep_unit = loadState(EEPROM_SLEEP_UNIT);
-      #if DEBUG == 1
-        Serial.print(F("LOADSLP M="));
-        Serial.print(_sleep_mode);
-        Serial.print(F(" T="));
-        Serial.print(_sleep_time);
-        Serial.print(F(" U="));
-        Serial.println(_sleep_unit);
-      #endif
-    }
+  #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
@@ -2492,7 +2732,7 @@ void NodeManager::presentation() {
     // present the battery service
     _present(BATTERY_CHILD_ID, S_MULTIMETER);
     // report battery level
-    _process("BATTERY");
+    batteryReport();
   #endif
   // present each sensor
   for (int i = 0; i < MAX_SENSORS; i++) {
@@ -2545,7 +2785,7 @@ void NodeManager::loop() {
     // if it is time to report the battery level
     if (_battery_report_timer.isOver()) {
       // time to report the battery level again
-      _process("BATTERY");
+      batteryReport();
       // restart the timer
       _battery_report_timer.restart();
     }
@@ -2585,9 +2825,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 (_sensors[message.sensor] != 0) {
@@ -2630,6 +2875,140 @@ 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 13: setBatteryReportCycles(request.getValueInt()); 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:
+      setSleepMode(request.getValueInt());
+      #if PERSIST == 1
+        _saveConfig(SAVE_SLEEP_MODE);
+      #endif
+      break;
+    case 4:
+      setSleepTime(request.getValueInt());
+      #if PERSIST == 1
+        _saveConfig(SAVE_SLEEP_TIME);
+      #endif
+      break;
+    case 5:
+      setSleepUnit(request.getValueInt());
+      #if PERSIST == 1
+        _saveConfig(SAVE_SLEEP_UNIT);
+      #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;
+    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
+  // 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
+  _sleep_mode = IDLE;
+}
+
+// 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);
+}
+
 // send a message to the network
 void NodeManager::_send(MyMessage & message) {
   // send the message, multiple times if requested
@@ -2656,158 +3035,6 @@ 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
@@ -2908,4 +3135,46 @@ int NodeManager::_getInterruptInitialValue(int mode) {
   return -1;
 }
 
+// load the configuration stored in the eeprom
+void NodeManager::_loadConfig() {
+  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
+  }
+}
 
+// save the configuration in the eeprom
+void NodeManager::_saveConfig(int what) {
+  if (what == SAVE_SLEEP_MODE) {
+    saveState(EEPROM_SLEEP_SAVED, 1);
+    saveState(EEPROM_SLEEP_MODE, _sleep_mode);
+  }
+  else if (what == SAVE_SLEEP_TIME) {
+    // 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);
+  }
+  else if (what == SAVE_SLEEP_UNIT) {
+    saveState(EEPROM_SLEEP_UNIT, _sleep_unit);
+  }
+}
diff --git a/NodeManager.h b/NodeManager.h
index 224e3e5fb1a8dbf16554ac04373535b40eecf488..8498dd94bc8b4c00eb14230499c34868abc8f485 100644
--- a/NodeManager.h
+++ b/NodeManager.h
@@ -35,13 +35,20 @@
 #define INTERRUPT_PIN_1 3
 #define INTERRUPT_PIN_2 2
 
+// define configuration settings that can be saved and loaded from the EEPROM
+#define SAVE_SLEEP_MODE 0
+#define SAVE_SLEEP_TIME 1
+#define SAVE_SLEEP_UNIT 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_USER_START 100
+
+// define requests
 
 /************************************
  * Include user defined configuration settings
@@ -100,7 +107,7 @@
    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_ACS712
 #ifndef MODULE_ANALOG_INPUT
   #define MODULE_ANALOG_INPUT 0
 #endif
@@ -156,6 +163,10 @@
 #ifndef MODULE_MCP9808
   #define MODULE_MCP9808 0
 #endif
+// Enable this module to use one of the following sensors: SENSOR_MQ
+#ifndef MODULE_MQ
+  #define MODULE_MQ 0
+#endif
 
 /***********************************
    Supported Sensors
@@ -168,8 +179,6 @@ enum supported_sensors {
     SENSOR_LDR,
     // Thermistor sensor, return the temperature based on the attached thermistor
     SENSOR_THERMISTOR,
-    // MQ2 air quality sensor
-    SENSOR_MQ,
     // ML8511 UV sensor
     SENSOR_ML8511,
     // Current sensor
@@ -243,6 +252,10 @@ enum supported_sensors {
     // MCP9808 sensor, precision temperature sensor
     SENSOR_MCP9808,
   #endif
+  #if MODULE_MQ == 1
+    // MQ2 air quality sensor
+    SENSOR_MQ,
+  #endif
 };
 /***********************************
   Libraries
@@ -301,7 +314,7 @@ enum supported_sensors {
   #include "Adafruit_MCP9808.h"
 #endif
 
-/**************************************
+/*******************************************************************
    Classes
 */
 class NodeManager;
@@ -370,46 +383,68 @@ class Timer {
     bool _is_running = false;
     bool _is_configured = false;
 };
+
+/*
+   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(NodeManager* node_manager, int child_id, int pin);
-    // 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)
+    // [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 than the previous one (default: false)
+    // [7] if true will report the measure only if different than the previous one (default: false)
     void setTrackLastValue(bool value);
-    // if track last value is enabled, force to send an update after the configured number of cycles (default: -1)
+    // [8] if track last value is enabled, force to send an update after the configured number of cycles (default: -1)
     void setForceUpdate(int value);
     void setForceUpdateCycles(int value);
-    // if track last value is enabled, force to send an update after the configured number of minutes (default: -1)
+    // [9] if track last value is enabled, force to send an update after the configured number of minutes (default: -1)
     void setForceUpdateMinutes(int value);
-    // the value type of this sensor (default: TYPE_INTEGER)
+    // [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);
+    // [11] 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)
@@ -418,21 +453,23 @@ class Sensor {
     #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();
-    // After how many cycles the sensor will report back its measure (default: 1 cycle)
+    // [15] After how many cycles the sensor will report back its measure (default: 1 cycle)
     void setReportIntervalCycles(int value);
-    // After how many minutes the sensor will report back its measure (default: 1 cycle)
+    // [16] After how many minutes the sensor will report back its measure (default: 1 cycle)
     void setReportIntervalMinutes(int value);
+    // process a remote request
+    void process(Request & request);
     // define what to do at each stage of the sketch
     virtual void before();
     virtual void presentation();
@@ -444,8 +481,10 @@ class Sensor {
     virtual void onSetup() = 0;
     virtual void onLoop() = 0;
     virtual void onReceive(const MyMessage & message) = 0;
+    virtual void onProcess(Request & request) = 0;
   protected:
     MyMessage _msg;
+    MyMessage _msg_service;
     NodeManager* _node_manager;
     int _sleep_between_send = 0;
     int _pin = -1;
@@ -478,27 +517,29 @@ class Sensor {
     bool _isWorthSending(bool comparison);
 };
 
+#if MODULE_ANALOG_INPUT == 1
 /*
    SensorAnalogInput: read the analog input of a configured pin
 */
 class SensorAnalogInput: public Sensor {
   public:
     SensorAnalogInput(NodeManager* node_manager, int child_id, int pin);
-    // 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);
     // 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);
   protected:
     int _reference = -1;
     bool _reverse = false;
@@ -523,21 +564,22 @@ class SensorLDR: public SensorAnalogInput {
 class SensorThermistor: public Sensor {
   public:
     SensorThermistor(NodeManager* node_manager, int child_id, int pin);
-    // 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);
     // 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);
   protected:
     long _nominal_resistor = 10000;
     int _nominal_temperature = 25;
@@ -546,61 +588,6 @@ class SensorThermistor: public Sensor {
     float _offset = 0;
 };
 
-/*
-    SensorMQ
- */
-class SensorMQ: public Sensor {
-  public:
-    SensorMQ(NodeManager* node_manager, 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
 */
@@ -613,6 +600,7 @@ class SensorML8511: public Sensor {
     void onSetup();
     void onLoop();
     void onReceive(const MyMessage & message);
+    void onProcess(Request & request);
   protected:
     float _mapfloat(float x, float in_min, float in_max, float out_min, float out_max);
 };
@@ -624,15 +612,16 @@ class SensorML8511: public Sensor {
 class SensorACS712: public Sensor {
   public:
     SensorACS712(NodeManager* node_manager, 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);
+    // [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);
   protected:
     int _ACS_offset = 2500;
     int _mv_per_amp = 185;
@@ -645,15 +634,16 @@ class SensorACS712: public Sensor {
 class SensorRainGauge: public Sensor {
   public:
     SensorRainGauge(NodeManager* node_manager, int child_id, int pin);
-    // set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60)
+    // [101] 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)
+    // [102] 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);
+    void onProcess(Request & request);
   public:
     static void _onTipped();
     static long _last_tip;
@@ -679,7 +669,10 @@ class SensorSoilMoisture: public SensorAnalogInput {
   public:
     SensorSoilMoisture(NodeManager* node_manager, int child_id, int pin);
 };
+#endif
 
+
+#if MODULE_DIGITAL_INPUT == 1
 /*
    SensorDigitalInput: read the digital input of the configured pin
 */
@@ -691,25 +684,28 @@ class SensorDigitalInput: public Sensor {
     void onSetup();
     void onLoop();
     void onReceive(const MyMessage & message);
+    void onProcess(Request & request);
 };
+#endif
 
+#if MODULE_DIGITAL_OUTPUT == 1
 /*
    SensorDigitalOutput: control a digital output of the configured pin
 */
 class SensorDigitalOutput: public Sensor {
   public:
     SensorDigitalOutput(NodeManager* node_manager, int child_id, int pin);
-    // set how to initialize the output (default: LOW)
+    // [101] 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)
+    // [102] 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)
+    // [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);
-    // automatically turn the output off after the given number of minutes
+    // [105] automatically turn the output off after the given number of minutes
     void setSafeguard(int value);
-    // if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false)
+    // [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);
     // manually switch the output to the provided value
     void set(int value);
@@ -720,6 +716,7 @@ class SensorDigitalOutput: public Sensor {
     void onSetup();
     void onLoop();
     void onReceive(const MyMessage & message);
+    void onProcess(Request & request);
   protected:
     int _initial_value = LOW;
     int _on_value = HIGH;
@@ -737,8 +734,6 @@ class SensorDigitalOutput: public Sensor {
 class SensorRelay: public SensorDigitalOutput {
   public:
     SensorRelay(NodeManager* node_manager, int child_id, int pin);
-    // define what to do at each stage of the sketch
-    //void onLoop();
 };
 
 /*
@@ -748,6 +743,7 @@ class SensorLatchingRelay: public SensorRelay {
   public:
     SensorLatchingRelay(NodeManager* node_manager, int child_id, int pin);
 };
+#endif
 
 /*
    SensorDHT
@@ -761,6 +757,7 @@ class SensorDHT: public Sensor {
     void onSetup();
     void onLoop();
     void onReceive(const MyMessage & message);
+    void onProcess(Request & request);
     // constants
     const static int TEMPERATURE = 0;
     const static int HUMIDITY = 1;
@@ -784,6 +781,7 @@ class SensorSHT21: public Sensor {
     void onSetup();
     void onLoop();
     void onReceive(const MyMessage & message);
+    void onProcess(Request & request);
     // constants
     const static int TEMPERATURE = 0;
     const static int HUMIDITY = 1;
@@ -805,17 +803,18 @@ class SensorHTU21D: public SensorSHT21 {
 /*
  * SensorSwitch
  */
+#if MODULE_SWITCH == 1
 class SensorSwitch: public Sensor {
   public:
     SensorSwitch(NodeManager* node_manager, int child_id, int pin);
-    // set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE)
+    // [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
@@ -823,6 +822,7 @@ class SensorSwitch: public Sensor {
     void onSetup();
     void onLoop();
     void onReceive(const MyMessage & message);
+    void onProcess(Request & request);
   protected:
     int _debounce = 0;
     int _trigger_time = 0;
@@ -845,7 +845,7 @@ class SensorMotion: public SensorSwitch {
   public:
     SensorMotion(NodeManager* node_manager, int child_id, int pin);
 };
-
+#endif
 /*
    SensorDs18b20
 */
@@ -853,19 +853,20 @@ class SensorMotion: public SensorSwitch {
 class SensorDs18b20: public Sensor {
   public:
     SensorDs18b20(NodeManager* node_manager, int child_id, int pin, DallasTemperature* sensors, int index);
-    // 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();
     // 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);
   protected:
     float _offset = 0;
     int _index;
@@ -887,6 +888,7 @@ class SensorBH1750: public Sensor {
     void onSetup();
     void onLoop();
     void onReceive(const MyMessage & message);
+    void onProcess(Request & request);
   protected:
     BH1750* _lightSensor;
 };
@@ -904,6 +906,7 @@ class SensorMLX90614: public Sensor {
     void onSetup();
     void onLoop();
     void onReceive(const MyMessage & message);
+    void onProcess(Request & request);
     // constants
     const static int TEMPERATURE_AMBIENT = 0;
     const static int TEMPERATURE_OBJECT = 1;
@@ -922,13 +925,14 @@ class SensorMLX90614: public Sensor {
 class SensorBosch: public Sensor {
   public:
     SensorBosch(NodeManager* node_manager, int child_id, int sensor_type);
-    // 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);
     // 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);
     // constants
     const static int TEMPERATURE = 0;
     const static int HUMIDITY = 1;
@@ -983,17 +987,18 @@ class SensorBMP085: public SensorBosch {
 class SensorHCSR04: public Sensor {
   public:
     SensorHCSR04(NodeManager* node_manager, int child_id, int pin);
-    // 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);
     // 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);
   protected:
     int _trigger_pin;
     int _echo_pin;
@@ -1009,17 +1014,18 @@ class SensorHCSR04: public Sensor {
 class SensorSonoff: public Sensor {
   public:
     SensorSonoff(NodeManager* node_manager, int child_id);
-    // set the button's pin (default: 0)
+    // [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);
   protected:
     Bounce _debouncer = Bounce();
     int _button_pin = 0;
@@ -1048,11 +1054,69 @@ class SensorMCP9808: public Sensor {
     void onSetup();
     void onLoop();
     void onReceive(const MyMessage & message);
+    void onProcess(Request & request);
   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);
+  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;
+};
+#endif
 
 /***************************************
    NodeManager: manages all the aspects of the node
@@ -1060,51 +1124,51 @@ 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);
     #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: -)
+      // [13] after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -)
       void setBatteryReportCycles(int value);
-      // after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60)
+      // [14] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60)
       void setBatteryReportMinutes(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)
+      // [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)
+    // [3] define the way the node should behave. It can be (0) IDLE (stay awake withtout executing each sensors' loop), (1) SLEEP (go to sleep for the configured interval), (2) WAIT (wait for the configured interval), (3) ALWAYS_ON (stay awake and execute each sensors' loop)
     void setSleepMode(int value);
     void setMode(int value);
     int getMode();
-    // define for how long the board will sleep (default: 0)
+    // [4] define for how long the board will sleep (default: 0)
     void setSleepTime(int value);
     int getSleepTime();
-    // define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES)
+    // [5] define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES)
     void setSleepUnit(int value);
     int getSleepUnit();
     // 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)
+    // [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)
+    // [20] optionally sleep interval in milliseconds before sending each message to the radio network (default: 0)
     void setSleepBetweenSend(int value);
     // 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);
@@ -1114,26 +1178,42 @@ 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);
     // request and return the current timestamp from the controller
     long getTimestamp();
     // Request the controller's configuration on startup (default: true)
     void setGetControllerConfig(bool value);
-    // Manually set isMetric setting
+    // [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);
     // hook into the main sketch functions
     void before();
     void presentation();
@@ -1173,13 +1253,14 @@ class NodeManager {
     long _timestamp = -1;
     Sensor* _sensors[MAX_SENSORS] = {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;
+    void _loadConfig();
+    void _saveConfig(int what);
 };
 
 #endif
diff --git a/NodeManager.ino b/NodeManager.ino
index 4e6def9f1e4b10b0e9d756ad43e231581f256c64..c1d9f953e92403cbdb370461e7523d956a3f647f 100644
--- a/NodeManager.ino
+++ b/NodeManager.ino
@@ -33,10 +33,14 @@ void before() {
   /*
    * Register below your sensors
   */
+  int n = nodeManager.registerSensor(SENSOR_THERMISTOR,A1);
+  Sensor* sensor = nodeManager.get(n);
+  Serial.println(sizeof(Sensor));
+  Serial.println(sizeof(SensorThermistor));
+  Serial.println(sizeof(NodeManager));
+  Serial.println(sizeof(Timer));
   
-  
-  
-  
+
   /*
    * Register above your sensors
   */
diff --git a/README.md b/README.md
index f9c299d3723e626d46029a16a00cd579be91b553..e5cc8a877aedc1e6587cc0a1125f8fa94ea869a1 100644
--- a/README.md
+++ b/README.md
@@ -143,7 +143,7 @@ Those NodeManager's directives in the `config.h` file control which module/libra
 // 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
 
-// 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, SENSOR_RAIN, SENSOR_SOIL_MOISTURE
+// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN_GAUGE, 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
@@ -171,6 +171,8 @@ 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
 ~~~
 
 ### Installing the dependencies
@@ -195,78 +197,96 @@ MODULE_MCP9808 | https://github.com/adafruit/Adafruit_MCP9808_Library
 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:
 
 ~~~c
-    // send the same service message multiple times (default: 1)
+    // [10] send the same service message multiple times (default: 1)
     void setRetries(int value);
     #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: -)
+      // [13] after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -)
       void setBatteryReportCycles(int value);
-      // after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60)
+      // [14] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60)
       void setBatteryReportMinutes(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)
+      // [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);
     #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)
+    // [3] define the way the node should behave. It can be (0) IDLE (stay awake withtout executing each sensors' loop), (1) SLEEP (go to sleep for the configured interval), (2) WAIT (wait for the configured interval), (3) 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)
+    int getMode();
+    // [4] 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)
+    int getSleepTime();
+    // [5] define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES)
     void setSleepUnit(int value);
+    int getSleepUnit();
     // 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)
+    // [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)
+    // [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);
     // 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);
     // request and return the current timestamp from the controller
     long getTimestamp();
     // Request the controller's configuration on startup (default: true)
     void setGetControllerConfig(bool value);
-    // Manually set isMetric setting
+    // [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();
+    // [2] Send a battery level report to the controller
+    void batteryReport();
+    // [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(const char * message);
+    // return the value stored at the requested index from the EEPROM
+    int loadFromMemory(int index);
+    // save the given index of the EEPROM the provided value
+    void saveToMemory(int index, int value);
 ~~~
 
 For example
@@ -331,6 +351,8 @@ 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);
 ~~~
 
 You can then instantiate your newly created class and register with NodeManager:
@@ -352,40 +374,40 @@ To do so, use `nodeManager.getSensor(child_id)` which will return a pointer to t
 
 The following methods are available for all the sensors:
 ~~~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)
+    // [7] if true will report the measure only if different than the previous one (default: false)
     void setTrackLastValue(bool value);
-    // if track last value is enabled, force to send an update after the configured number of cycles (default: -1)
+    // [8] if track last value is enabled, force to send an update after the configured number of cycles (default: -1)
     void setForceUpdate(int value);
     void setForceUpdateCycles(int value);
-    // if track last value is enabled, force to send an update after the configured number of minutes (default: -1)
+    // [9] if track last value is enabled, force to send an update after the configured number of minutes (default: -1)
     void setForceUpdateMinutes(int value);
-    // the value type of this sensor (default: TYPE_INTEGER)
+    // [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);
+    int getValueType();
+    // [11] 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)
@@ -393,22 +415,24 @@ The following methods are available for all the sensors:
     int getInterruptPin();
     #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();
-    // After how many cycles the sensor will report back its measure (default: 1 cycle)
+    // [15] After how many cycles the sensor will report back its measure (default: 1 cycle)
     void setReportIntervalCycles(int value);
-    // After how many minutes the sensor will report back its measure (default: 1 cycle)
+    // [16] After how many minutes the sensor will report back its measure (default: 1 cycle)
     void setReportIntervalMinutes(int value);
+    // process a remote request
+    void process(Request & request);
 ~~~
 
 #### Sensor's specific configuration
@@ -417,49 +441,49 @@ Each sensor class can expose additional methods.
 
 * 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
 ~~~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
 ~~~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);
@@ -469,19 +493,35 @@ Each sensor class can expose additional methods.
     void setSmokeCurve(float *value);
 ~~~
 
+* SensorACS712
+~~~c
+    // [101] set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185);
+    void setmVPerAmp(int value);
+    // [102] set ACS offset (default: 2500);
+    void setOffset(int value);
+~~~
+
+* SensorRainGauge
+~~~c
+    // [101] set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60)
+    void setReportInterval(int value);
+    // [102] set how many mm of rain to count for each tip (default: 0.11)
+    void setSingleTip(float value);
+~~~
+
 * SensorDigitalOutput / SensorRelay / SensorLatchingRelay
 ~~~c
-    // set how to initialize the output (default: LOW)
+    // [101] 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)
+    // [102] 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)
+    // [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);
-    // automatically turn the output off after the given number of minutes
+    // [105] automatically turn the output off after the given number of minutes
     void setSafeguard(int value);
-    // if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false)
+    // [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);
     // manually switch the output to the provided value
     void set(int value);
@@ -491,74 +531,54 @@ Each sensor class can expose additional methods.
 
 *  SensorSwitch / SensorDoor / SensorMotion
 ~~~c
-    // set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE)
+    // [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)
+    int getMode();
+    // [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();
 ~~~
 
 *  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
-~~~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);
-~~~
-
-* SensorBMP085
+*  SensorBME280 / SensorBMP085
 ~~~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
 ~~~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
-~~~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);
-~~~
-
-* SensorRainGauge
+*  SensorSonoff
 ~~~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);
+    // [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);
 ~~~
 
 ### Upload your sketch
@@ -570,51 +590,65 @@ When `DEBUG` is enabled, detailed information is available through the serial po
 
 ### Communicate with NodeManager and its sensors
 
-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:
+You can interact with each registered sensor by asking to execute their main tasks by sending to the child id a `REQ` command (or a `SET` for output sensors like relays). For example to request the temperature to node_id 254 and child_id 1:
 
 `254;1;2;0;0;`
 
 To activate a relay connected to the same node, child_id 100:
 
-`254;100;2;0;2;1`
+`254;100;1;0;2;1`
 
 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.
-
-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:
-
-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
-
-For example, to request the battery level to node id 254:
-
-`254;200;2;0;48;BATTERY`
+Once the node will be sleeping, it will report automatically each measure at the end of every sleep cycle, unless configured otherwise.
 
-To set the sleeping cycle to 1 hour:
+NodeManager exposes also 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 if successful. 
+Almost all the functions made available through the API can be called remotely. To do so, the payload must be in the format `<function_id>[,<value_to_set>]` where function_id is the number between square brackets you can find in the description just above each function 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 the 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;INTVL001H`
+The change the sleep time from e.g. 10 minutes as set in the sketch to 5 minutes:
+~~~c
+    // [4] define for how long the board will sleep (default: 0)
+    void setSleepTime(int value);
+~~~
+`<node_id>;<configuration_child_id>;<req>;0;<V_CUSTOM>;<function_id>,<value>`
+`100;200;2;0;48;4,5`
 
 To ask the node to start sleeping (and waking up based on the previously configured interval):
+~~~c
+    // [3] define the way the node should behave. It can be (0) IDLE (stay awake withtout executing each sensors' loop), (1) SLEEP (go to sleep for the configured interval), (2) WAIT (wait for the configured interval), (3) ALWAYS_ON (stay awake and execute each sensors' loop)
+    void setSleepMode(int value);
+~~~
+`100;200;2;0;48;3,1`
 
-`254;200;2;0;48;MODE1`
-
-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 just it 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 those provided to setSleepMode(), setSleepTime() and setSleepUnit() which are saved to the EEPROM (provided `PERSIST` is enabled).
 
 ## Understanding NodeManager: how it works
 
diff --git a/config.h b/config.h
index eded817ecdd4e1a5fae01362df58035e1247f369..497e4eee39a663659f8c4d8169e8da72e22991b1 100644
--- a/config.h
+++ b/config.h
@@ -15,11 +15,11 @@
 // General settings
 #define MY_BAUD_RATE 9600
 //#define MY_DEBUG
-//#define MY_NODE_ID 100
+#define MY_NODE_ID 100
 
 // NRF24 radio settings
 #define MY_RADIO_NRF24
-//#define MY_RF24_ENABLE_ENCRYPTION
+#define MY_RF24_ENABLE_ENCRYPTION
 //#define MY_RF24_CHANNEL 76
 //#define MY_RF24_PA_LEVEL RF24_PA_HIGH
 //#define MY_DEBUG_VERBOSE_RF24
@@ -102,12 +102,12 @@
 // 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_GAUGE, 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
+#define MODULE_DIGITAL_INPUT 0
 // Enable this module to use one of the following sensors: SENSOR_DIGITAL_OUTPUT, SENSOR_RELAY, SENSOR_LATCHING_RELAY
-#define MODULE_DIGITAL_OUTPUT 1
+#define MODULE_DIGITAL_OUTPUT 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
@@ -130,5 +130,7 @@
 #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
 #endif