Browse Source

* split "mode" to "mode" and "preset", changes in nearly all source files
* add In+Out Temperature display (without humidity) without toggling on LCD line 1
* add publish topic "presetName" and "presetHA", where presetHA returns "none" for default preset and the name for the rest
* add configuration for preset names, off-message, option for non toggling temp display (conf2 page)
* minor fixed in http api and Web-IF
* fixes/improvements in MQTT handling (subscribe to less topics than before using wildcard)
* restart function: when holding down MODE button for >4s, display shows "Restart?". Restart is executed when:
* MODE button is keeped holding
* MODE button is pressed/held again as long as the "Restarting?" message is displayed
* return to default operation mode when + or - button is pressed
* changed position of preset mode indicator - now in LCD line 2 after the set temp
* added "flame" symbol in LCD line 2, shown when the heating is active
* added connection symbol (also LCD line 2), depending on received MQTT HEARTBEAT messages
* added fullscreen messages for WiFi connect and MQTT connect
* shows IP on startup and successful WiFi connection
* shows AP mode notification and SSID on unsuccessful WiFi connection
* shows to which MQTT broker we are connected if connecting was successful
* added halfscreen (line 2) notifications - used for displaying the full name of the preset when using the MODE button
* fixed http authentication

FloKra 4 years ago
parent
commit
8260c5c877

+ 22 - 1
CHANGELOG.md

@@ -1,4 +1,25 @@
 # WiFiThermostat - Changelog
+## 0.3.0
+* split "mode" to "mode" and "preset", changes in nearly all source files
+* add In+Out Temperature display (without humidity) without toggling on LCD line 1
+* add publish topic "presetName" and "presetHA", where presetHA returns "none" for default preset and the name for the rest
+* add configuration for preset names, off-message, option for non toggling temp display (conf2 page)
+* minor fixed in http api and Web-IF
+* fixes/improvements in MQTT handling (subscribe to less topics than before using wildcard)
+* restart function: when holding down MODE button for >4s, display shows "Restart?". Restart is executed when:
+    * MODE button is keeped holding
+    * MODE button is pressed/held again as long as the "Restarting?" message is displayed
+* return to default operation mode when + or - button is pressed
+* changed position of preset mode indicator - now in LCD line 2 after the set temp
+* added "flame" symbol in LCD line 2, shown when the heating is active
+* added connection symbol (also LCD line 2), depending on received MQTT HEARTBEAT messages
+* added fullscreen messages for WiFi connect and MQTT connect
+    * shows IP on startup and successful WiFi connection
+	* shows AP mode notification and SSID on unsuccessful WiFi connection
+	* shows to which MQTT broker we are connected if connecting was successful
+* added halfscreen (line 2) notifications - used for displaying the full name of the preset when using the MODE button
+* fixed http authentication
+
 ## 0.2.2
 * Home Assistant direct support (setTemp and setMode topics, real name used in setMode, mode names are configurable)
 * added MQTT connection message on LWT topic
@@ -21,7 +42,7 @@
 * added http control api with token
 * added mqttReconnect http call
 * bugfixes
-* some documentation
+* some documentation
 
 Important: for now compiled using Arduino 1.8.5 with ESP8266-Arduino core 2.4.0-rc2, as WiFi (re)connection does not work properly in release 2.4.0 (spent many hours on that)!!
 

+ 53 - 0
Home Assistant integration NEW.txt

@@ -0,0 +1,53 @@
+
+momentane Temperatur (Messung):
+    Home Assistant config:
+        current_temperature_topic: Test/Thermostat/stat/temp
+
+Zieltemperatur:
+    Home Assistant config:
+        temperature_state_topic: Test/Thermostat/stat/currSetTemp
+        temperature_command_topic: Test/Thermostat/setTemp
+        temp_step: 0.5
+        initial: 21.0
+        min_temp: 16.0
+        max_temp: 26.0
+
+Betriebsart:
+    Hier hat sich die Implementierung von Home Assistant geändert, so dass diese Änderung notwendig wurde. 
+    Frühere Versionen von WiFiThermostat hatten nur einen Mode mit Zahlenwerten (0 = off, 1 = heat, 2 = Absenkung,... ). 
+    Das funktioniert nun nicht mehr, da der Parameter "mode" der climate.mqtt platform dies nicht mehr zulässt. Es 
+    ist nur noch ["auto", "off", "cool", "heat", "dry", "fan_only"] als operation mode möglich. 
+    Deshalb wird diese Änderung nun nachgezogen, die Betriebsart kann nun nur noch zwischen "off" und "heat" umschalten, 
+    ist somit nur noch ein Hauptschalter, und die weiteren Betriebsarten (Normalbetrieb, Nachtabsenkung...) werden als 
+    zweiter Parameter implementiert. Siehe nächster Absatz. 
+  
+    Home Assistant config:
+        mode_command_topic: "Test/Thermostat/setMode"
+        mode_state_topic: "Test/Thermostat/stat/mode"
+        modes: ["off", "heat"]
+   
+
+Preset: 
+    Damit wird nun die Voreinstellung gewählt - also (derzeit) Normalbetrieb, Absenkung 1, Absenkung 2
+    
+    Home Assistant config:
+        hold_command_topic: "Test/Thermostat/setPreset"
+        hold_state_topic: "Test/Thermostat/stat/preset"
+        #hold_modes: ["Normal", "Absenkung 1", "Absenkung 2"]
+        hold_modes: ["Absenkung", "Absenkung 2"]
+    
+    
+    
+Das Gerät muss auf jeden command (via command_topic) mit dem zugehörigen state_topic antworten, damit der Wert in 
+Home Assistant auch wirklich übernommen wird - ansonsten springt dieser nach einigen Sekunden wieder auf den vorherigen
+Wert. Ausnahme: wenn kein state_topic konfiguriert wurde, dann läuft HASS für diesen Parameter im optimistic mode, man hat 
+aber auch keine Rückmeldung. 
+
+
+
+away_mode erweitert den Preset selector um die Position "Away". Die sendet dann auf eigenem Topic ein "on" wenn man es aktiviert, 
+und auf preset_command_topic ein "off". danach ist die auswahl eines anderen modes anscheinend über die GUI nicht möglich. 
+Damit der Schalter auf "Away" stehen bleibt muss man an preset_state_topic = "away" senden. 
+Irgendwie ist die Funktion aber undurchsichtig. Man kommt aus diesem Away-Mode nicht mehr raus. Jede weitere UI-Aktion sendet 
+dann preset_command_topic = "off" ....
+-> kein Away-Mode nutzen, nur "hold_mode"

+ 149 - 102
src/WiFiThermostat/Buttonhandling.ino

@@ -1,103 +1,150 @@
-
-// Use this function to configure the internal Bounce object to suit you. See the documentation at: https://github.com/thomasfredericks/Bounce2/wiki
-// This function can be left out if the defaults are acceptable - just don't call configureButton
-void configurePushButton(Bounce& bouncedButton) {
-  // Set the debounce interval to 15ms - default is 10ms
-  bouncedButton.interval(15);
-}
-
-// btn is a reference to the button that fired the event. That means you can use the same event handler for many buttons
-//void onButtonPressed(Button& btn) {
-//
-//}
-
-// duration reports back how long it has been since the button was originally pressed.
-// repeatCount tells us how many times this function has been called by this button.
-void onButtonHeld(Button& btn, uint16_t duration, uint16_t repeatCount) {
-  if (buttonPlus.isPressed()) {
-    plusButtonAction();
-  }
-  if (buttonMinus.isPressed()) {
-    minusButtonAction();
-  }
-}
-
-void onButtonHeldNoRepeat(Button& btn, uint16_t duration) {
-  if (buttonMode.isPressed()) {
-    modeButtonHoldAction();
-  }
-  if (pirSensor.isPressed()) {
-    pirSensorOnAction();
-  }
-}
-
-// duration reports back the total time that the button was held down
-void onButtonReleased(Button& btn, uint16_t duration) {
-  if (btn.is(buttonPlus)) {
-    plusButtonAction();
-  }
-  else if (btn.is(buttonMinus)) {
-    minusButtonAction();
-  }
-  else if (btn.is(buttonMode)) {
-    modeButtonAction();
-  }
-  else if (btn.is(pirSensor)) {
-    pirSensorOffAction();
-  }
-}
-
-void plusButtonAction() {
-  Serial.println("Btn +");
-  if (!displayActive) {
-    enableDisplay();
-  }
-  else {
-    setTempStepUp();
-    extendDisplayTimeout();
-  }
-}
-
-void minusButtonAction() {
-  Serial.println("Btn -");
-  if (!displayActive) {
-    enableDisplay();
-  }
-  else {
-    setTempStepDown();
-    extendDisplayTimeout();
-  }
-}
-
-void modeButtonAction() {
-  Serial.println("Btn mode");
-  if (!displayActive) {
-    enableDisplay();
-  }
-  else {
-    toggleHeatingMode();
-    extendDisplayTimeout();
-  }
-}
-
-void modeButtonHoldAction() {
-  Serial.println("Btn mode held - toggle on/off");
-  extendDisplayTimeout();
-  toggleThermostatOnOff();
-}
-
-void pirSensorOnAction() {
-  PIRSensorOn = true;
-  Serial.println("PIR sensor ON");
-  publishCurrentPIRValue();
-  sendToDomoticz_PIR();
-  if(PIR_enablesDisplay) enableDisplay();
-}
-
-void pirSensorOffAction() {
-  PIRSensorOn = false;
-  Serial.println("PIR sensor OFF");
-  publishCurrentPIRValue();
-  sendToDomoticz_PIR();
-}
 
+// Use this function to configure the internal Bounce object to suit you. See the documentation at: https://github.com/thomasfredericks/Bounce2/wiki
+// This function can be left out if the defaults are acceptable - just don't call configureButton
+void configurePushButton(Bounce& bouncedButton) {
+  // Set the debounce interval to 15ms - default is 10ms
+  bouncedButton.interval(15);
+}
+
+// btn is a reference to the button that fired the event. That means you can use the same event handler for many buttons
+//void onButtonPressed(Button& btn) {
+//
+//}
+
+// duration reports back how long it has been since the button was originally pressed.
+// repeatCount tells us how many times this function has been called by this button.
+//void onButtonPressed(Button& btn) {
+//  if (buttonMode.isPressed()) {
+//
+//  }
+//}
+
+void onButtonHeld(Button& btn, uint16_t duration, uint16_t repeatCount) {
+  if (buttonPlus.isPressed()) {
+    plusButtonAction();
+  }
+  if (buttonMinus.isPressed()) {
+    minusButtonAction();
+  }
+  if (buttonMode.isPressed()) {
+    if (repeatCount <= 2) modeButtonHoldAction();
+    else if (repeatCount == 4) modeButtonRebootPendingAction();
+    else if (repeatCount > 4) modeButtonRebootAction();
+  }
+}
+
+void onButtonHeldNoRepeat(Button& btn, uint16_t duration) {
+  //if (buttonMode.isPressed()) {
+  //  modeButtonHoldAction();
+  //}
+  if (pirSensor.isPressed()) {
+    pirSensorOnAction();
+  }
+}
+
+// duration reports back the total time that the button was held down
+void onButtonReleased(Button& btn, uint16_t duration) {
+  if (btn.is(buttonPlus) && duration < 200) {
+    plusButtonAction();
+  }
+  else if (btn.is(buttonMinus) && duration < 200) {
+    minusButtonAction();
+  }
+  else if (btn.is(buttonMode) && duration < 200) {
+    modeButtonAction();
+  }
+  else if (btn.is(pirSensor)) {
+    pirSensorOffAction();
+  }
+}
+
+void plusButtonAction() {
+  Serial.println("Btn +");
+  if (!displayActive) {
+    enableDisplay();
+  }
+  else {
+    extendDisplayTimeout();
+    if (heatingMode == 1 && preset != 0) setPresetTo(0);
+    else setTempStepUp();
+  }
+}
+
+void minusButtonAction() {
+  Serial.println("Btn -");
+  if (heatingMode == 1 && preset != 0) setPresetTo(0);
+  if (!displayActive) {
+    enableDisplay();
+  }
+  else {
+    extendDisplayTimeout();
+    if (heatingMode == 1 && preset != 0) setPresetTo(0);
+    else setTempStepDown();
+  }
+}
+
+void modeButtonAction() {
+  Serial.println("Btn mode");
+  if (!displayActive) {
+    enableDisplay();
+    //displayShowLine2OverlayMessage(currentPresetName);
+  }
+  else {
+    extendDisplayTimeout();
+    if (pendingRestart) {
+      doRestart = true;
+      updateDisplay();
+    }
+    else {
+      if (heatingMode > 0) {
+        if (pendingPresetToggle) {
+          togglePreset();
+        }
+        else {
+          pendingPresetToggle = true;
+          displayShowLine2OverlayMessage(currentPresetName);
+        }
+      }
+    }
+  }
+}
+
+void modeButtonHoldAction() {
+  Serial.println("Btn mode held - toggle on/off");
+  extendDisplayTimeout();
+  if (pendingRestart) {
+    doRestart = true;
+    updateDisplay();
+  }
+  else {
+    toggleOnOff();
+  }
+}
+
+void modeButtonRebootPendingAction() {
+  extendDisplayTimeout();
+  pendingRestart = true;
+  pendingRestart_lastMillis = millis();
+  updateDisplay();
+}
+
+void modeButtonRebootAction() {
+  extendDisplayTimeout();
+  doRestart = true;
+  updateDisplay();
+}
+
+void pirSensorOnAction() {
+  PIRSensorOn = true;
+  Serial.println("PIR sensor ON");
+  publishCurrentPIRValue();
+  sendToDomoticz_PIR();
+  if (PIR_enablesDisplay) enableDisplay();
+}
+
+void pirSensorOffAction() {
+  PIRSensorOn = false;
+  Serial.println("PIR sensor OFF");
+  publishCurrentPIRValue();
+  sendToDomoticz_PIR();
+}

+ 341 - 201
src/WiFiThermostat/Display.ino

@@ -1,202 +1,342 @@
-// LCD
-byte customCharSun[8] = {0b00100, 0b10001, 0b01110, 0b11111, 0b11111, 0b01110, 0b10001, 0b00100};
-byte customCharMoon[8] = {0b11000, 0b01110, 0b00110, 0b00111, 0b00111, 0b00110, 0b01110, 0b11000};
-byte customCharArrowRight[8] = {0b00000, 0b00100, 0b00110, 0b11111, 0b11111, 0b00110, 0b00100, 0b00000};
-byte customCharDegC[8] = {0b01000, 0b10100, 0b01000, 0b00011, 0b00100, 0b00100, 0b00011, 0b00000};
-//bool displayActive = false;
-unsigned long displayLastOnMillis = 0;
-
-void enableDisplay() {
-  lcd.backlight();
-  displayActive = true;
-  displayLastOnMillis = millis();
-}
-
-void disableDisplay() {
-  lcd.noBacklight();
-  displayActive = false;
-}
-
-void extendDisplayTimeout() {
-  displayLastOnMillis = millis();
-}
-
-void handleDisplayTimeout() {
-  if (displayActive && (millis() - displayLastOnMillis) > (displayTimeout * 1000)) {
-    disableDisplay();
-  }
-}
-
-void initDisplay() {
-  lcd.init();
-  lcd.createChar(0, customCharMoon);
-  lcd.createChar(1, customCharSun);
-  lcd.createChar(2, customCharArrowRight);
-  lcd.createChar(3, customCharDegC);  
-
-  lcd.setCursor(0, 0);
-
-  lcd.print("WiFiThermostat  ");
-  lcd.setCursor(0, 1);
-  lcd.print(" (c) 2018 FloKra");
-
-  //  for ( int i = 0; i < 3; i++) {
-  //    lcd.backlight();
-  //    delay(200);
-  //    lcd.noBacklight();
-  //    delay(200);
-  //  }
-  lcd.backlight();
-  //delay(1000);
-  ///lcd.clear();
-  displayActive = true;
-  displayLastOnMillis = millis();
-}
-
-void updateDisplay() {
-
-  long now = millis();
-  if ( ((now - outTempHumLastUpdate) < 300000) && outTempHumLastUpdate != 0 && whichTempToDisplay == 1 && ((now - lastDisplayToggle) > (displayInterval * 1000)) ) {
-    // outside temp has been updated < 5 min ago, last displayed temp was "I" and display interval is overdue
-    whichTempToDisplay = 2; // 1= I, 2= A
-    lastDisplayToggle = now;
-  }
-  else if ( whichTempToDisplay != 1 && ((now - lastDisplayToggle) > (displayInterval * 1000)) ) {
-    whichTempToDisplay = 1; // 1= I, 2= A
-    lastDisplayToggle = now;
-  }
-
-  lastDisplayUpdate = now;
-
-  boolean showTemp = false;
-  char temp_chararr[6];
-  char hum_chararr[4];
-  char tempLabel[2];
-
-  if (whichTempToDisplay == 2) {
-    if ( outTempHumLastUpdate != 0 && (now - outTempHumLastUpdate) < 120000 ) {
-      showTemp = true;
-      dtostrf(outTemp, 5, 1, temp_chararr );
-      sprintf(hum_chararr, "%2i", outHum);
-      strcpy(tempLabel, "A");
-    }
-  }
-  else {
-    if ( lastTempUpdate != 0 && (now - lastTempUpdate) < 120000 ) {
-      showTemp = true;
-      dtostrf(currTemp, 5, 1, temp_chararr );
-      sprintf(hum_chararr, "%2i", currHum);
-      strcpy(tempLabel, "I");
-    }
-  }
-
-  // 12345667890123456
-  // =20.0° 35% >22.5°
-  // A -10.2° 95%    N
-  // A 22.2° 95%     N
-  // A 5.2° 95%      N
-  
-  
-
-  // LCD line 1
-  // print temperature incl = and ° symbol + humidity to lcd, line 1, first 11 chars
-  lcd.setCursor(0, 0);
-  
-  
-
-  if (showTemp) {
-    //lcd.write(0x3D); // = Zeichen
-    lcd.print(tempLabel);
-    lcd.print(" ");
-    lcd.print(temp_chararr);
-    //lcd.write(0xDF); // degree symbol
-    lcd.write((uint8_t)3); // °C symbol
-    lcd.print(" ");
-
-    if (currHum == 0) lcd.print("--");
-    else if (currHum < 10) {
-      lcd.print(" ");
-      lcd.print(hum_chararr);
-    }
-    else {
-      lcd.print(hum_chararr);
-    }
-    lcd.print("%");
-    lcd.print("  ");
-  }
-  else {
-    lcd.print("             ");
-  }
-
-  // display current mode on LCD
-  lcd.setCursor(13, 0);
-  if (heatingMode > 0) {
-    if ( heatingMode == 1 ) { // day/normal mode
-      lcd.print(" ");
-      lcd.write((uint8_t)1); // sun symbol if mode is day/normal
-      lcd.print(" ");
-    }
-    else if ( heatingMode == 2 ) { // night/reduction mode
-      lcd.print(" ");
-      lcd.write((uint8_t)0); // moon symbol if mode is night/reduction
-      lcd.print("1");
-    }
-    else if ( heatingMode == 3 ) { // night/reduction mode
-      lcd.print(" ");
-      lcd.write((uint8_t)0); // moon symbol if mode is night/reduction
-      lcd.print("2");
-    }
-  }
-  else lcd.print("   "); // mode is heating off
-
-
-
-  float curr_setTemp;
-  if (heatingMode == 1) curr_setTemp = setTemp;
-  else if (heatingMode == 2) curr_setTemp = setTempLow;
-  else if (heatingMode == 3) curr_setTemp = setTempLow2;
-  char currSetTemp_chararr[6];
-  dtostrf(curr_setTemp, 5, 1, currSetTemp_chararr );
-
-
-  // LCD line 2
-  // display target temperature to line 2, 8 chars length incl space at the end
-  lcd.setCursor(0, 1);
-
-  // when the heating mode is OFF, do not display target temp - instead show "Heating off" info in line 2
-  if ( heatingMode == 0 ) {
-    //         1234567890123456
-    lcd.print("  Heizung aus   ");
-  }
-  else {
-    lcd.write((uint8_t)2); // Pfeil rechts
-    lcd.print(" ");
-    lcd.print(currSetTemp_chararr);
-    //lcd.write(0xDF); // degree symbol
-    lcd.write((uint8_t)3); // °C symbol
-    lcd.print(" ");
-
-    // display status info to line 2 from char 9 -> 8 chars length
-    lcd.setCursor(8, 1);
-    lcd.print(" ");
-    if (turnHeatingOn) {
-      //         1234567
-      lcd.print(" heizen");
-    }
-    else if ( heatingMode == 1 ) { // day/normal mode
-      //         1234567
-      lcd.print("       ");
-    }
-    else if ( heatingMode == 2 ) { // night/reduction mode
-      //         1234567
-      lcd.print(" Abs. 1");
-    }
-    else if ( heatingMode == 3 ) { // night/reduction mode
-      //         1234567
-      lcd.print(" Abs. 2");
-    }
-    else lcd.print("        ");
-  }
-
-}
+// LCD
+byte customCharSun[] = {B00100, B10001, B01110, B11111, B11111, B01110, B10001, B00100};
+byte customCharMoon[] = {B11000, B01110, B00110, B00111, B00111, B00110, B01110, B11000};
+byte customCharArrowRight[] = {B01000, B01100, B01110, B01111, B01110, B01100, B01000, B00000};
+byte customCharDegC[] = {B01000, B10100, B01000, B00011, B00100, B00100, B00011, B00000};
+byte customCharConn[] = {B00000, B00000, B11100, B00010, B11001, B00101, B10101, B00000};
+byte customCharDisconn[] = {B10001, B01010, B00100, B01010, B10001, B00000, B10000, B00000};
+byte customCharReduction2[] = {B00000, B11111, B01110, B00100, B00000, B11111, B01110, B00100}; //(2 arrows down)
+byte customCharFlame[] = {B00100, B00100, B01010, B01010, B10101, B10101, B10101, B01110};
+//boolean displayActive = false;
+unsigned long displayLastOnMillis = 0;
 
+void enableDisplay() {
+  lcd.backlight();
+  displayActive = true;
+  displayLastOnMillis = millis();
+}
+
+void disableDisplay() {
+  lcd.noBacklight();
+  displayActive = false;
+}
+
+void extendDisplayTimeout() {
+  displayLastOnMillis = millis();
+}
+
+void handleDisplayTimeout() {
+  if (displayActive && (millis() - displayLastOnMillis) > (displayTimeout * 1000)) {
+    disableDisplay();
+  }
+}
+
+void initDisplay() {
+  lcd.init();
+  lcd.createChar(0, customCharMoon);
+  lcd.createChar(1, customCharSun);
+  lcd.createChar(2, customCharArrowRight);
+  lcd.createChar(3, customCharDegC);
+  lcd.createChar(4, customCharConn);
+  lcd.createChar(5, customCharDisconn);
+  lcd.createChar(6, customCharReduction2);
+  lcd.createChar(7, customCharFlame);
+
+  lcd.setCursor(0, 0);
+
+  lcd.print("WiFiThermostat  ");
+  lcd.setCursor(0, 1);
+  lcd.print("(c) by FloKra.at");
+
+  //  for ( int i = 0; i < 3; i++) {
+  //    lcd.backlight();
+  //    delay(200);
+  //    lcd.noBacklight();
+  //    delay(200);
+  //  }
+  lcd.backlight();
+  //delay(1000);
+  ///lcd.clear();
+  displayActive = true;
+  displayLastOnMillis = millis();
+}
+
+void displayShowWifiConnected() {
+  lcd.setCursor(0, 0);
+
+  lcd.print("WiFi connected  ");
+  lcd.setCursor(0, 1);
+  lcd.print("IP:             ");
+  lcd.setCursor(4, 1);
+  lcd.print(WiFi.localIP());
+
+  lcd.backlight();
+  displayActive = true;
+  displayLastOnMillis = millis();
+  delay(2000);
+}
+
+void displayShowWifiConnectionError() {
+  lcd.setCursor(0, 0);
+  lcd.print("WiFi connecting ");
+  lcd.setCursor(0, 1);
+  lcd.print("FAILED          ");
+  lcd.backlight();
+  displayActive = true;
+  displayLastOnMillis = millis();
+  delay(2000);
+
+  lcd.setCursor(0, 0);
+  lcd.print("AP mode active  ");
+  lcd.setCursor(0, 1);
+  lcd.print("as              ");
+  lcd.setCursor(3, 1);
+  lcd.print(persWM.getApSsid());
+  lcd.backlight();
+  displayActive = true;
+  displayLastOnMillis = millis();
+  delay(2000);
+}
+
+void displayShowMQTTConnected() {
+  lcd.setCursor(0, 0);
+
+  lcd.print("MQTT connected  ");
+  lcd.setCursor(0, 1);
+  lcd.print("                ");
+  lcd.setCursor(0, 1);
+  lcd.print(mqtt_server);
+
+  lcd.backlight();
+  displayActive = true;
+  displayLastOnMillis = millis();
+
+  displayShowFullscreenMsg = true;
+  countDisplayInterval = 0;
+  //displayInterval_saved = displayInterval; //already done in loadconf2
+  displayInterval = displayOverlayMsgTimeout;
+  //delay(2000);
+}
+
+void displayShowMQTTConnectionError() {
+  lcd.setCursor(0, 0);
+  lcd.print("MQTT connection ");
+  lcd.setCursor(0, 1);
+  lcd.print("FAILED          ");
+
+  lcd.backlight();
+  displayActive = true;
+  displayLastOnMillis = millis();
+  displayShowFullscreenMsg = true;
+  countDisplayInterval = 0;
+  //displayInterval_saved = displayInterval; //already done in loadconf2
+  displayInterval = displayOverlayMsgTimeout;
+  //delay(2000);
+}
+
+void displayShowLine2OverlayMessage(char* msg) {
+  lcd.setCursor(0, 1);
+  lcd.print("                ");
+  lcd.setCursor(0, 1);
+  lcd.write((uint8_t)2); // arrow right symbol
+  lcd.print(" ");
+  lcd.print(msg);
+
+  for (int i = strlen(msg); i < 14; i++) {
+    lcd.print(" ");
+  }
+
+  displayShowLine2OverlayMsg = true;
+  countDisplayInterval = 0;
+  //displayInterval_saved = displayInterval; //already done in loadconf2
+  displayInterval = displayOverlayMsgTimeout;
+}
+
+void displayAddConnectionIcon() {
+  lcd.setCursor(14, 1);
+  lcd.print(" ");
+  if (mqttConnected) lcd.write((uint8_t)4); // connected symbol
+  else lcd.write((uint8_t)5); // disconnected symbol
+}
+
+void updateDisplay() {
+  if (displayShowFullscreenMsg) {
+    displayShowFullscreenMsg = false;
+    displayInterval = displayInterval_saved;
+    updateDisplayImmediately = true;
+  }
+  else {
+    boolean showTemp = false;
+    char ch_temp[6];
+    char ch_hum[4];
+    char tempLabel[2];
+    long now = millis();
+
+    if (togglingTempHumAIDisplay) {
+
+      if ( ((now - outTempHumLastUpdate) < 300000) && outTempHumLastUpdate != 0 && whichTempToDisplay == 1 && ((now - lastDisplayToggle) > (displayInterval * 1000)) ) {
+        // outside temp has been updated < 5 min ago, last displayed temp was "I" and display interval is overdue
+        whichTempToDisplay = 2; // 1= I, 2= A
+        lastDisplayToggle = now;
+      }
+      else if ( whichTempToDisplay != 1 && ((now - lastDisplayToggle) > (displayInterval * 1000)) ) {
+        whichTempToDisplay = 1; // 1= I, 2= A
+        lastDisplayToggle = now;
+      }
+
+      lastDisplayUpdate = now;
+
+      if (whichTempToDisplay == 2) { //2 = outside temp/hum received via MQTT
+        if ( outTempHumLastUpdate != 0 && (now - outTempHumLastUpdate) < 120000 ) {
+          showTemp = true;
+          dtostrf(outTemp, 5, 1, ch_temp);
+          sprintf(ch_hum, "%2i", outHum);
+          strcpy(tempLabel, "A");
+        }
+      }
+      else {
+        if ( lastTempUpdate != 0 && (now - lastTempUpdate) < 120000 ) {
+          showTemp = true;
+          dtostrf(currTemp, 5, 1, ch_temp);
+          sprintf(ch_hum, "%2i", currHum);
+          strcpy(tempLabel, "I");
+        }
+      }
+
+      // 12345667890123456
+      // =20.0° 35% >22.5°
+      // A -10.2° 95%    N
+      // A 22.2° 95%     N
+      // A 5.2° 95%      N
+
+
+
+      // LCD line 1
+      // print temperature incl = and ° symbol + humidity to lcd, line 1, first 11 chars
+      lcd.setCursor(0, 0);
+
+      if (showTemp) {
+        //lcd.write(0x3D); // = Zeichen
+        lcd.print(tempLabel);
+        lcd.print(" ");
+        lcd.print(ch_temp);
+        //lcd.write(0xDF); // degree symbol
+        lcd.write((uint8_t)3); // °C symbol
+        lcd.print(" ");
+
+        if (currHum == 0) lcd.print("--");
+        else if (currHum < 10) {
+          lcd.print(" ");
+          lcd.print(ch_hum);
+        }
+        else {
+          lcd.print(ch_hum);
+        }
+        lcd.print("%");
+        lcd.print("        ");
+      }
+      else {
+        lcd.print("               ");
+      }
+    }
+
+    else { // non toggling temperature I + A display without humidity in line 1
+      lcd.setCursor(0, 0);
+      lcd.print("I ");
+
+      if ( lastTempUpdate != 0 && (now - lastTempUpdate) < 120000 ) {
+        dtostrf(currTemp, 2, 1, ch_temp);
+        lcd.print(ch_temp);
+      }
+      else {
+        lcd.print("--.-");
+      }
+      lcd.write((uint8_t)3); // °C symbol
+      lcd.print(" ");
+
+      if ( outTempHumLastUpdate != 0 && (now - outTempHumLastUpdate) < 120000 ) {
+        lcd.print(" A");
+        if (outTemp <= -10.0) {
+          dtostrf(outTemp, 4, 1, ch_temp);
+        }
+        else {
+          lcd.print(" ");
+          dtostrf(outTemp, 3, 1, ch_temp);
+        }
+        lcd.print(ch_temp);
+        lcd.write((uint8_t)3); // °C symbol
+      }
+      else {
+        lcd.print("        ");
+      }
+    }
+
+    updateCurrSetTemp();
+
+    char ch_currSetTemp[6];
+    dtostrf(currSetTemp, 2, 1, ch_currSetTemp);
+
+    // LCD line 2
+    // display target temperature to line 2, 8 chars length incl space at the end
+    lcd.setCursor(0, 1);
+
+    if ( doRestart ) {
+      lcd.print("Restarting... ");
+    }
+    else if ( pendingRestart ) {
+      lcd.print("Restart?      ");
+    }
+    else if ( heatingMode == 0 ) { // when the heating mode is OFF, do not display target temp - instead show "Heating off" info in line 2
+      //         1234567890123456
+      lcd.print("               ");
+      lcd.setCursor(0, 1);
+      lcd.print(offMessage);
+      //for(int i=strlen(offMessage); i < 14; i++) {
+      //lcd.print(" ");
+      //}
+      displayAddConnectionIcon();
+    }
+    else if ( displayShowLine2OverlayMsg ) {
+      displayShowLine2OverlayMsg = false;
+      displayInterval = displayInterval_saved;
+      if (pendingPresetToggle) {
+        preset = pendingPreset;
+        updateCurrentPresetName();
+        pendingPresetToggle = false;
+      }
+      updateDisplayImmediately = true;
+    }
+    else {
+      lcd.write((uint8_t)2); // arrow right symbol
+      lcd.print(" ");
+      lcd.print(ch_currSetTemp);
+      //lcd.write(0xDF); // degree symbol
+      lcd.write((uint8_t)3); // °C symbol
+      lcd.print(" ");
+
+      lcd.setCursor(8, 1);
+      lcd.print("        ");
+
+      // display status info to line 2 from char 9 -> 8 chars length
+      lcd.setCursor(9, 1);
+      if ( preset == 0 ) { // day/normal mode
+        lcd.write((uint8_t)1); // sun symbol if mode is day/normal
+      }
+      else if ( preset == 1 ) { // night/reduction mode
+        lcd.write((uint8_t)0); // moon symbol if mode is night/reduction
+      }
+      else if ( preset == 2 ) { // night/reduction mode
+        lcd.write((uint8_t)6); // reduction 2 (2 arrows down) symbol if mode is reduction 2
+      }
+
+      lcd.setCursor(12, 1);
+      if (turnHeatingOn) {
+        lcd.write((uint8_t)7); // flame symbol if heating is active
+      }
+      else {
+        lcd.print(" ");
+      }
+      displayAddConnectionIcon();
+    }
+  }
+}

+ 652 - 653
src/WiFiThermostat/PubSubClient.cpp

@@ -1,654 +1,653 @@
-/*
-  PubSubClient.cpp - A simple client for MQTT.
-  Nick O'Leary
-  http://knolleary.net
-*/
-
-#include "PubSubClient.h"
-#include "Arduino.h"
-
-PubSubClient::PubSubClient() {
-    this->_state = MQTT_DISCONNECTED;
-    this->_client = NULL;
-    this->stream = NULL;
-    setCallback(NULL);
-}
-
-PubSubClient::PubSubClient(Client& client) {
-    this->_state = MQTT_DISCONNECTED;
-    setClient(client);
-    this->stream = NULL;
-}
-
-PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) {
-    this->_state = MQTT_DISCONNECTED;
-    setServer(addr, port);
-    setClient(client);
-    this->stream = NULL;
-}
-PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) {
-    this->_state = MQTT_DISCONNECTED;
-    setServer(addr,port);
-    setClient(client);
-    setStream(stream);
-}
-PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
-    this->_state = MQTT_DISCONNECTED;
-    setServer(addr, port);
-    setCallback(callback);
-    setClient(client);
-    this->stream = NULL;
-}
-PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
-    this->_state = MQTT_DISCONNECTED;
-    setServer(addr,port);
-    setCallback(callback);
-    setClient(client);
-    setStream(stream);
-}
-
-PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) {
-    this->_state = MQTT_DISCONNECTED;
-    setServer(ip, port);
-    setClient(client);
-    this->stream = NULL;
-}
-PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) {
-    this->_state = MQTT_DISCONNECTED;
-    setServer(ip,port);
-    setClient(client);
-    setStream(stream);
-}
-PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
-    this->_state = MQTT_DISCONNECTED;
-    setServer(ip, port);
-    setCallback(callback);
-    setClient(client);
-    this->stream = NULL;
-}
-PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
-    this->_state = MQTT_DISCONNECTED;
-    setServer(ip,port);
-    setCallback(callback);
-    setClient(client);
-    setStream(stream);
-}
-
-PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) {
-    this->_state = MQTT_DISCONNECTED;
-    setServer(domain,port);
-    setClient(client);
-    this->stream = NULL;
-}
-PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) {
-    this->_state = MQTT_DISCONNECTED;
-    setServer(domain,port);
-    setClient(client);
-    setStream(stream);
-}
-PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
-    this->_state = MQTT_DISCONNECTED;
-    setServer(domain,port);
-    setCallback(callback);
-    setClient(client);
-    this->stream = NULL;
-}
-PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
-    this->_state = MQTT_DISCONNECTED;
-    setServer(domain,port);
-    setCallback(callback);
-    setClient(client);
-    setStream(stream);
-}
-
-boolean PubSubClient::connect(const char *id) {
-    return connect(id,NULL,NULL,0,0,0,0,1);
-}
-
-boolean PubSubClient::connect(const char *id, const char *user, const char *pass) {
-    return connect(id,user,pass,0,0,0,0,1);
-}
-
-boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
-    return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1);
-}
-
-boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
-    return connect(id,user,pass,willTopic,willQos,willRetain,willMessage,1);
-}
-
-boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) {
-    if (!connected()) {
-        int result = 0;
-
-        if (domain != NULL) {
-            result = _client->connect(this->domain, this->port);
-        } else {
-            result = _client->connect(this->ip, this->port);
-        }
-        if (result == 1) {
-            nextMsgId = 1;
-            // Leave room in the buffer for header and variable length field
-            uint16_t length = MQTT_MAX_HEADER_SIZE;
-            unsigned int j;
-
-#if MQTT_VERSION == MQTT_VERSION_3_1
-            uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION};
-#define MQTT_HEADER_VERSION_LENGTH 9
-#elif MQTT_VERSION == MQTT_VERSION_3_1_1
-            uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION};
-#define MQTT_HEADER_VERSION_LENGTH 7
-#endif
-            for (j = 0;j<MQTT_HEADER_VERSION_LENGTH;j++) {
-                buffer[length++] = d[j];
-            }
-
-            uint8_t v;
-            if (willTopic) {
-                v = 0x04|(willQos<<3)|(willRetain<<5);
-            } else {
-                v = 0x00;
-            }
-            if (cleanSession) {
-                v = v|0x02;
-            }
-
-            if(user != NULL) {
-                v = v|0x80;
-
-                if(pass != NULL) {
-                    v = v|(0x80>>1);
-                }
-            }
-
-            buffer[length++] = v;
-
-            buffer[length++] = ((MQTT_KEEPALIVE) >> 8);
-            buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF);
-
-            CHECK_STRING_LENGTH(length,id)
-            length = writeString(id,buffer,length);
-            if (willTopic) {
-                CHECK_STRING_LENGTH(length,willTopic)
-                length = writeString(willTopic,buffer,length);
-                CHECK_STRING_LENGTH(length,willMessage)
-                length = writeString(willMessage,buffer,length);
-            }
-
-            if(user != NULL) {
-                CHECK_STRING_LENGTH(length,user)
-                length = writeString(user,buffer,length);
-                if(pass != NULL) {
-                    CHECK_STRING_LENGTH(length,pass)
-                    length = writeString(pass,buffer,length);
-                }
-            }
-
-            write(MQTTCONNECT,buffer,length-MQTT_MAX_HEADER_SIZE);
-
-            lastInActivity = lastOutActivity = millis();
-
-            while (!_client->available()) {
-                unsigned long t = millis();
-                if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) {
-                    _state = MQTT_CONNECTION_TIMEOUT;
-                    _client->stop();
-                    return false;
-                }
-            }
-            uint8_t llen;
-            uint16_t len = readPacket(&llen);
-
-            if (len == 4) {
-                if (buffer[3] == 0) {
-                    lastInActivity = millis();
-                    pingOutstanding = false;
-                    _state = MQTT_CONNECTED;
-                    return true;
-                } else {
-                    _state = buffer[3];
-                }
-            }
-            _client->stop();
-        } else {
-            _state = MQTT_CONNECT_FAILED;
-        }
-        return false;
-    }
-    return true;
-}
-
-// reads a byte into result
-boolean PubSubClient::readByte(uint8_t * result) {
-   uint32_t previousMillis = millis();
-   while(!_client->available()) {
-     yield();
-     uint32_t currentMillis = millis();
-     if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){
-       return false;
-     }
-   }
-   *result = _client->read();
-   return true;
-}
-
-// reads a byte into result[*index] and increments index
-boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){
-  uint16_t current_index = *index;
-  uint8_t * write_address = &(result[current_index]);
-  if(readByte(write_address)){
-    *index = current_index + 1;
-    return true;
-  }
-  return false;
-}
-
-uint16_t PubSubClient::readPacket(uint8_t* lengthLength) {
-    uint16_t len = 0;
-    if(!readByte(buffer, &len)) return 0;
-    bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH;
-    uint32_t multiplier = 1;
-    uint16_t length = 0;
-    uint8_t digit = 0;
-    uint16_t skip = 0;
-    uint8_t start = 0;
-
-    do {
-        if (len == 5) {
-            // Invalid remaining length encoding - kill the connection
-            _state = MQTT_DISCONNECTED;
-            _client->stop();
-            return 0;
-        }
-        if(!readByte(&digit)) return 0;
-        buffer[len++] = digit;
-        length += (digit & 127) * multiplier;
-        multiplier *= 128;
-    } while ((digit & 128) != 0);
-    *lengthLength = len-1;
-
-    if (isPublish) {
-        // Read in topic length to calculate bytes to skip over for Stream writing
-        if(!readByte(buffer, &len)) return 0;
-        if(!readByte(buffer, &len)) return 0;
-        skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2];
-        start = 2;
-        if (buffer[0]&MQTTQOS1) {
-            // skip message id
-            skip += 2;
-        }
-    }
-
-    for (uint16_t i = start;i<length;i++) {
-        if(!readByte(&digit)) return 0;
-        if (this->stream) {
-            if (isPublish && len-*lengthLength-2>skip) {
-                this->stream->write(digit);
-            }
-        }
-        if (len < MQTT_MAX_PACKET_SIZE) {
-            buffer[len] = digit;
-        }
-        len++;
-    }
-
-    if (!this->stream && len > MQTT_MAX_PACKET_SIZE) {
-        len = 0; // This will cause the packet to be ignored.
-    }
-
-    return len;
-}
-
-boolean PubSubClient::loop() {
-    if (connected()) {
-        unsigned long t = millis();
-        if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) {
-            if (pingOutstanding) {
-                this->_state = MQTT_CONNECTION_TIMEOUT;
-                _client->stop();
-                return false;
-            } else {
-                buffer[0] = MQTTPINGREQ;
-                buffer[1] = 0;
-                _client->write(buffer,2);
-                lastOutActivity = t;
-                lastInActivity = t;
-                pingOutstanding = true;
-            }
-        }
-        if (_client->available()) {
-            uint8_t llen;
-            uint16_t len = readPacket(&llen);
-            uint16_t msgId = 0;
-            uint8_t *payload;
-            if (len > 0) {
-                lastInActivity = t;
-                uint8_t type = buffer[0]&0xF0;
-                if (type == MQTTPUBLISH) {
-                    if (callback) {
-                        uint16_t tl = (buffer[llen+1]<<8)+buffer[llen+2]; /* topic length in bytes */
-                        memmove(buffer+llen+2,buffer+llen+3,tl); /* move topic inside buffer 1 byte to front */
-                        buffer[llen+2+tl] = 0; /* end the topic as a 'C' string with \x00 */
-                        char *topic = (char*) buffer+llen+2;
-                        // msgId only present for QOS>0
-                        if ((buffer[0]&0x06) == MQTTQOS1) {
-                            msgId = (buffer[llen+3+tl]<<8)+buffer[llen+3+tl+1];
-                            payload = buffer+llen+3+tl+2;
-                            callback(topic,payload,len-llen-3-tl-2);
-
-                            buffer[0] = MQTTPUBACK;
-                            buffer[1] = 2;
-                            buffer[2] = (msgId >> 8);
-                            buffer[3] = (msgId & 0xFF);
-                            _client->write(buffer,4);
-                            lastOutActivity = t;
-
-                        } else {
-                            payload = buffer+llen+3+tl;
-                            callback(topic,payload,len-llen-3-tl);
-                        }
-                    }
-                } else if (type == MQTTPINGREQ) {
-                    buffer[0] = MQTTPINGRESP;
-                    buffer[1] = 0;
-                    _client->write(buffer,2);
-                } else if (type == MQTTPINGRESP) {
-                    pingOutstanding = false;
-                }
-            } else if (!connected()) {
-                // readPacket has closed the connection
-                return false;
-            }
-        }
-        return true;
-    }
-    return false;
-}
-
-boolean PubSubClient::publish(const char* topic, const char* payload) {
-    return publish(topic,(const uint8_t*)payload,strlen(payload),false);
-}
-
-boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) {
-    return publish(topic,(const uint8_t*)payload,strlen(payload),retained);
-}
-
-boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) {
-    return publish(topic, payload, plength, false);
-}
-
-boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
-    if (connected()) {
-        if (MQTT_MAX_PACKET_SIZE < MQTT_MAX_HEADER_SIZE + 2+strlen(topic) + plength) {
-            // Too long
-            return false;
-        }
-        // Leave room in the buffer for header and variable length field
-        uint16_t length = MQTT_MAX_HEADER_SIZE;
-        length = writeString(topic,buffer,length);
-        uint16_t i;
-        for (i=0;i<plength;i++) {
-            buffer[length++] = payload[i];
-        }
-        uint8_t header = MQTTPUBLISH;
-        if (retained) {
-            header |= 1;
-        }
-        return write(header,buffer,length-MQTT_MAX_HEADER_SIZE);
-    }
-    return false;
-}
-
-boolean PubSubClient::publish_P(const char* topic, const char* payload, boolean retained) {
-    return publish_P(topic, (const uint8_t*)payload, strlen(payload), retained);
-}
-
-boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
-    uint8_t llen = 0;
-    uint8_t digit;
-    unsigned int rc = 0;
-    uint16_t tlen;
-    unsigned int pos = 0;
-    unsigned int i;
-    uint8_t header;
-    unsigned int len;
-
-    if (!connected()) {
-        return false;
-    }
-
-    tlen = strlen(topic);
-
-    header = MQTTPUBLISH;
-    if (retained) {
-        header |= 1;
-    }
-    buffer[pos++] = header;
-    len = plength + 2 + tlen;
-    do {
-        digit = len % 128;
-        len = len / 128;
-        if (len > 0) {
-            digit |= 0x80;
-        }
-        buffer[pos++] = digit;
-        llen++;
-    } while(len>0);
-
-    pos = writeString(topic,buffer,pos);
-
-    rc += _client->write(buffer,pos);
-
-    for (i=0;i<plength;i++) {
-        rc += _client->write((char)pgm_read_byte_near(payload + i));
-    }
-
-    lastOutActivity = millis();
-
-    return rc == tlen + 4 + plength;
-}
-
-boolean PubSubClient::beginPublish(const char* topic, unsigned int plength, boolean retained) {
-    if (connected()) {
-        // Send the header and variable length field
-        uint16_t length = MQTT_MAX_HEADER_SIZE;
-        length = writeString(topic,buffer,length);
-        uint16_t i;
-        uint8_t header = MQTTPUBLISH;
-        if (retained) {
-            header |= 1;
-        }
-        size_t hlen = buildHeader(header, buffer, plength+length-MQTT_MAX_HEADER_SIZE);
-        uint16_t rc = _client->write(buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen));
-        lastOutActivity = millis();
-        return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen)));
-    }
-    return false;
-}
-
-int PubSubClient::endPublish() {
- return 1;
-}
-
-size_t PubSubClient::write(uint8_t data) {
-    lastOutActivity = millis();
-    return _client->write(data);
-}
-
-size_t PubSubClient::write(const uint8_t *buffer, size_t size) {
-    lastOutActivity = millis();
-    return _client->write(buffer,size);
-}
-
-size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint16_t length) {
-    uint8_t lenBuf[4];
-    uint8_t llen = 0;
-    uint8_t digit;
-    uint8_t pos = 0;
-    uint16_t len = length;
-    do {
-        digit = len % 128;
-        len = len / 128;
-        if (len > 0) {
-            digit |= 0x80;
-        }
-        lenBuf[pos++] = digit;
-        llen++;
-    } while(len>0);
-
-    buf[4-llen] = header;
-    for (int i=0;i<llen;i++) {
-        buf[MQTT_MAX_HEADER_SIZE-llen+i] = lenBuf[i];
-    }
-    return llen+1; // Full header size is variable length bit plus the 1-byte fixed header
-}
-
-boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
-    uint16_t rc;
-    uint8_t hlen = buildHeader(header, buf, length);
-
-#ifdef MQTT_MAX_TRANSFER_SIZE
-    uint8_t* writeBuf = buf+(MQTT_MAX_HEADER_SIZE-hlen);
-    uint16_t bytesRemaining = length+hlen;  //Match the length type
-    uint8_t bytesToWrite;
-    boolean result = true;
-    while((bytesRemaining > 0) && result) {
-        bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
-        rc = _client->write(writeBuf,bytesToWrite);
-        result = (rc == bytesToWrite);
-        bytesRemaining -= rc;
-        writeBuf += rc;
-    }
-    return result;
-#else
-    rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen);
-    lastOutActivity = millis();
-    return (rc == hlen+length);
-#endif
-}
-
-boolean PubSubClient::subscribe(const char* topic) {
-    return subscribe(topic, 0);
-}
-
-boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
-    if (qos > 1) {
-        return false;
-    }
-    if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
-        // Too long
-        return false;
-    }
-    if (connected()) {
-        // Leave room in the buffer for header and variable length field
-        uint16_t length = MQTT_MAX_HEADER_SIZE;
-        nextMsgId++;
-        if (nextMsgId == 0) {
-            nextMsgId = 1;
-        }
-        buffer[length++] = (nextMsgId >> 8);
-        buffer[length++] = (nextMsgId & 0xFF);
-        length = writeString((char*)topic, buffer,length);
-        buffer[length++] = qos;
-        return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
-    }
-    return false;
-}
-
-boolean PubSubClient::unsubscribe(const char* topic) {
-    if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
-        // Too long
-        return false;
-    }
-    if (connected()) {
-        uint16_t length = MQTT_MAX_HEADER_SIZE;
-        nextMsgId++;
-        if (nextMsgId == 0) {
-            nextMsgId = 1;
-        }
-        buffer[length++] = (nextMsgId >> 8);
-        buffer[length++] = (nextMsgId & 0xFF);
-        length = writeString(topic, buffer,length);
-        return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
-    }
-    return false;
-}
-
-void PubSubClient::disconnect() {
-    buffer[0] = MQTTDISCONNECT;
-    buffer[1] = 0;
-    _client->write(buffer,2);
-    _state = MQTT_DISCONNECTED;
-    _client->flush();
-    _client->stop();
-    lastInActivity = lastOutActivity = millis();
-}
-
-uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos) {
-    const char* idp = string;
-    uint16_t i = 0;
-    pos += 2;
-    while (*idp) {
-        buf[pos++] = *idp++;
-        i++;
-    }
-    buf[pos-i-2] = (i >> 8);
-    buf[pos-i-1] = (i & 0xFF);
-    return pos;
-}
-
-
-boolean PubSubClient::connected() {
-    boolean rc;
-    if (_client == NULL ) {
-        rc = false;
-    } else {
-        rc = (int)_client->connected();
-        if (!rc) {
-            if (this->_state == MQTT_CONNECTED) {
-                this->_state = MQTT_CONNECTION_LOST;
-                _client->flush();
-                _client->stop();
-            }
-        }
-    }
-    return rc;
-}
-
-PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) {
-    IPAddress addr(ip[0],ip[1],ip[2],ip[3]);
-    return setServer(addr,port);
-}
-
-PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) {
-    this->ip = ip;
-    this->port = port;
-    this->domain = NULL;
-    return *this;
-}
-
-PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {
-    this->domain = domain;
-    this->port = port;
-    return *this;
-}
-
-PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) {
-    this->callback = callback;
-    return *this;
-}
-
-PubSubClient& PubSubClient::setClient(Client& client){
-    this->_client = &client;
-    return *this;
-}
-
-PubSubClient& PubSubClient::setStream(Stream& stream){
-    this->stream = &stream;
-    return *this;
-}
-
-int PubSubClient::state() {
-    return this->_state;
-}
+/*
+  PubSubClient.cpp - A simple client for MQTT.
+  Nick O'Leary
+  http://knolleary.net
+*/
 
+#include "PubSubClient.h"
+#include "Arduino.h"
+
+PubSubClient::PubSubClient() {
+    this->_state = MQTT_DISCONNECTED;
+    this->_client = NULL;
+    this->stream = NULL;
+    setCallback(NULL);
+}
+
+PubSubClient::PubSubClient(Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setClient(client);
+    this->stream = NULL;
+}
+
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(addr, port);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(addr,port);
+    setClient(client);
+    setStream(stream);
+}
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(addr, port);
+    setCallback(callback);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(addr,port);
+    setCallback(callback);
+    setClient(client);
+    setStream(stream);
+}
+
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(ip, port);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(ip,port);
+    setClient(client);
+    setStream(stream);
+}
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(ip, port);
+    setCallback(callback);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(ip,port);
+    setCallback(callback);
+    setClient(client);
+    setStream(stream);
+}
+
+PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(domain,port);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(domain,port);
+    setClient(client);
+    setStream(stream);
+}
+PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(domain,port);
+    setCallback(callback);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(domain,port);
+    setCallback(callback);
+    setClient(client);
+    setStream(stream);
+}
+
+boolean PubSubClient::connect(const char *id) {
+    return connect(id,NULL,NULL,0,0,0,0,1);
+}
+
+boolean PubSubClient::connect(const char *id, const char *user, const char *pass) {
+    return connect(id,user,pass,0,0,0,0,1);
+}
+
+boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
+    return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1);
+}
+
+boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
+    return connect(id,user,pass,willTopic,willQos,willRetain,willMessage,1);
+}
+
+boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) {
+    if (!connected()) {
+        int result = 0;
+
+        if (domain != NULL) {
+            result = _client->connect(this->domain, this->port);
+        } else {
+            result = _client->connect(this->ip, this->port);
+        }
+        if (result == 1) {
+            nextMsgId = 1;
+            // Leave room in the buffer for header and variable length field
+            uint16_t length = MQTT_MAX_HEADER_SIZE;
+            unsigned int j;
+
+#if MQTT_VERSION == MQTT_VERSION_3_1
+            uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION};
+#define MQTT_HEADER_VERSION_LENGTH 9
+#elif MQTT_VERSION == MQTT_VERSION_3_1_1
+            uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION};
+#define MQTT_HEADER_VERSION_LENGTH 7
+#endif
+            for (j = 0;j<MQTT_HEADER_VERSION_LENGTH;j++) {
+                buffer[length++] = d[j];
+            }
+
+            uint8_t v;
+            if (willTopic) {
+                v = 0x04|(willQos<<3)|(willRetain<<5);
+            } else {
+                v = 0x00;
+            }
+            if (cleanSession) {
+                v = v|0x02;
+            }
+
+            if(user != NULL) {
+                v = v|0x80;
+
+                if(pass != NULL) {
+                    v = v|(0x80>>1);
+                }
+            }
+
+            buffer[length++] = v;
+
+            buffer[length++] = ((MQTT_KEEPALIVE) >> 8);
+            buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF);
+
+            CHECK_STRING_LENGTH(length,id)
+            length = writeString(id,buffer,length);
+            if (willTopic) {
+                CHECK_STRING_LENGTH(length,willTopic)
+                length = writeString(willTopic,buffer,length);
+                CHECK_STRING_LENGTH(length,willMessage)
+                length = writeString(willMessage,buffer,length);
+            }
+
+            if(user != NULL) {
+                CHECK_STRING_LENGTH(length,user)
+                length = writeString(user,buffer,length);
+                if(pass != NULL) {
+                    CHECK_STRING_LENGTH(length,pass)
+                    length = writeString(pass,buffer,length);
+                }
+            }
+
+            write(MQTTCONNECT,buffer,length-MQTT_MAX_HEADER_SIZE);
+
+            lastInActivity = lastOutActivity = millis();
+
+            while (!_client->available()) {
+                unsigned long t = millis();
+                if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) {
+                    _state = MQTT_CONNECTION_TIMEOUT;
+                    _client->stop();
+                    return false;
+                }
+            }
+            uint8_t llen;
+            uint16_t len = readPacket(&llen);
+
+            if (len == 4) {
+                if (buffer[3] == 0) {
+                    lastInActivity = millis();
+                    pingOutstanding = false;
+                    _state = MQTT_CONNECTED;
+                    return true;
+                } else {
+                    _state = buffer[3];
+                }
+            }
+            _client->stop();
+        } else {
+            _state = MQTT_CONNECT_FAILED;
+        }
+        return false;
+    }
+    return true;
+}
+
+// reads a byte into result
+boolean PubSubClient::readByte(uint8_t * result) {
+   uint32_t previousMillis = millis();
+   while(!_client->available()) {
+     yield();
+     uint32_t currentMillis = millis();
+     if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){
+       return false;
+     }
+   }
+   *result = _client->read();
+   return true;
+}
+
+// reads a byte into result[*index] and increments index
+boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){
+  uint16_t current_index = *index;
+  uint8_t * write_address = &(result[current_index]);
+  if(readByte(write_address)){
+    *index = current_index + 1;
+    return true;
+  }
+  return false;
+}
+
+uint16_t PubSubClient::readPacket(uint8_t* lengthLength) {
+    uint16_t len = 0;
+    if(!readByte(buffer, &len)) return 0;
+    boolean isPublish = (buffer[0]&0xF0) == MQTTPUBLISH;
+    uint32_t multiplier = 1;
+    uint16_t length = 0;
+    uint8_t digit = 0;
+    uint16_t skip = 0;
+    uint8_t start = 0;
+
+    do {
+        if (len == 5) {
+            // Invalid remaining length encoding - kill the connection
+            _state = MQTT_DISCONNECTED;
+            _client->stop();
+            return 0;
+        }
+        if(!readByte(&digit)) return 0;
+        buffer[len++] = digit;
+        length += (digit & 127) * multiplier;
+        multiplier *= 128;
+    } while ((digit & 128) != 0);
+    *lengthLength = len-1;
+
+    if (isPublish) {
+        // Read in topic length to calculate bytes to skip over for Stream writing
+        if(!readByte(buffer, &len)) return 0;
+        if(!readByte(buffer, &len)) return 0;
+        skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2];
+        start = 2;
+        if (buffer[0]&MQTTQOS1) {
+            // skip message id
+            skip += 2;
+        }
+    }
+
+    for (uint16_t i = start;i<length;i++) {
+        if(!readByte(&digit)) return 0;
+        if (this->stream) {
+            if (isPublish && len-*lengthLength-2>skip) {
+                this->stream->write(digit);
+            }
+        }
+        if (len < MQTT_MAX_PACKET_SIZE) {
+            buffer[len] = digit;
+        }
+        len++;
+    }
+
+    if (!this->stream && len > MQTT_MAX_PACKET_SIZE) {
+        len = 0; // This will cause the packet to be ignored.
+    }
+
+    return len;
+}
+
+boolean PubSubClient::loop() {
+    if (connected()) {
+        unsigned long t = millis();
+        if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) {
+            if (pingOutstanding) {
+                this->_state = MQTT_CONNECTION_TIMEOUT;
+                _client->stop();
+                return false;
+            } else {
+                buffer[0] = MQTTPINGREQ;
+                buffer[1] = 0;
+                _client->write(buffer,2);
+                lastOutActivity = t;
+                lastInActivity = t;
+                pingOutstanding = true;
+            }
+        }
+        if (_client->available()) {
+            uint8_t llen;
+            uint16_t len = readPacket(&llen);
+            uint16_t msgId = 0;
+            uint8_t *payload;
+            if (len > 0) {
+                lastInActivity = t;
+                uint8_t type = buffer[0]&0xF0;
+                if (type == MQTTPUBLISH) {
+                    if (callback) {
+                        uint16_t tl = (buffer[llen+1]<<8)+buffer[llen+2]; /* topic length in bytes */
+                        memmove(buffer+llen+2,buffer+llen+3,tl); /* move topic inside buffer 1 byte to front */
+                        buffer[llen+2+tl] = 0; /* end the topic as a 'C' string with \x00 */
+                        char *topic = (char*) buffer+llen+2;
+                        // msgId only present for QOS>0
+                        if ((buffer[0]&0x06) == MQTTQOS1) {
+                            msgId = (buffer[llen+3+tl]<<8)+buffer[llen+3+tl+1];
+                            payload = buffer+llen+3+tl+2;
+                            callback(topic,payload,len-llen-3-tl-2);
+
+                            buffer[0] = MQTTPUBACK;
+                            buffer[1] = 2;
+                            buffer[2] = (msgId >> 8);
+                            buffer[3] = (msgId & 0xFF);
+                            _client->write(buffer,4);
+                            lastOutActivity = t;
+
+                        } else {
+                            payload = buffer+llen+3+tl;
+                            callback(topic,payload,len-llen-3-tl);
+                        }
+                    }
+                } else if (type == MQTTPINGREQ) {
+                    buffer[0] = MQTTPINGRESP;
+                    buffer[1] = 0;
+                    _client->write(buffer,2);
+                } else if (type == MQTTPINGRESP) {
+                    pingOutstanding = false;
+                }
+            } else if (!connected()) {
+                // readPacket has closed the connection
+                return false;
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+boolean PubSubClient::publish(const char* topic, const char* payload) {
+    return publish(topic,(const uint8_t*)payload,strlen(payload),false);
+}
+
+boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) {
+    return publish(topic,(const uint8_t*)payload,strlen(payload),retained);
+}
+
+boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) {
+    return publish(topic, payload, plength, false);
+}
+
+boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
+    if (connected()) {
+        if (MQTT_MAX_PACKET_SIZE < MQTT_MAX_HEADER_SIZE + 2+strlen(topic) + plength) {
+            // Too long
+            return false;
+        }
+        // Leave room in the buffer for header and variable length field
+        uint16_t length = MQTT_MAX_HEADER_SIZE;
+        length = writeString(topic,buffer,length);
+        uint16_t i;
+        for (i=0;i<plength;i++) {
+            buffer[length++] = payload[i];
+        }
+        uint8_t header = MQTTPUBLISH;
+        if (retained) {
+            header |= 1;
+        }
+        return write(header,buffer,length-MQTT_MAX_HEADER_SIZE);
+    }
+    return false;
+}
+
+boolean PubSubClient::publish_P(const char* topic, const char* payload, boolean retained) {
+    return publish_P(topic, (const uint8_t*)payload, strlen(payload), retained);
+}
+
+boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
+    uint8_t llen = 0;
+    uint8_t digit;
+    unsigned int rc = 0;
+    uint16_t tlen;
+    unsigned int pos = 0;
+    unsigned int i;
+    uint8_t header;
+    unsigned int len;
+
+    if (!connected()) {
+        return false;
+    }
+
+    tlen = strlen(topic);
+
+    header = MQTTPUBLISH;
+    if (retained) {
+        header |= 1;
+    }
+    buffer[pos++] = header;
+    len = plength + 2 + tlen;
+    do {
+        digit = len % 128;
+        len = len / 128;
+        if (len > 0) {
+            digit |= 0x80;
+        }
+        buffer[pos++] = digit;
+        llen++;
+    } while(len>0);
+
+    pos = writeString(topic,buffer,pos);
+
+    rc += _client->write(buffer,pos);
+
+    for (i=0;i<plength;i++) {
+        rc += _client->write((char)pgm_read_byte_near(payload + i));
+    }
+
+    lastOutActivity = millis();
+
+    return rc == tlen + 4 + plength;
+}
+
+boolean PubSubClient::beginPublish(const char* topic, unsigned int plength, boolean retained) {
+    if (connected()) {
+        // Send the header and variable length field
+        uint16_t length = MQTT_MAX_HEADER_SIZE;
+        length = writeString(topic,buffer,length);
+        uint16_t i;
+        uint8_t header = MQTTPUBLISH;
+        if (retained) {
+            header |= 1;
+        }
+        size_t hlen = buildHeader(header, buffer, plength+length-MQTT_MAX_HEADER_SIZE);
+        uint16_t rc = _client->write(buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen));
+        lastOutActivity = millis();
+        return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen)));
+    }
+    return false;
+}
+
+int PubSubClient::endPublish() {
+ return 1;
+}
+
+size_t PubSubClient::write(uint8_t data) {
+    lastOutActivity = millis();
+    return _client->write(data);
+}
+
+size_t PubSubClient::write(const uint8_t *buffer, size_t size) {
+    lastOutActivity = millis();
+    return _client->write(buffer,size);
+}
+
+size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint16_t length) {
+    uint8_t lenBuf[4];
+    uint8_t llen = 0;
+    uint8_t digit;
+    uint8_t pos = 0;
+    uint16_t len = length;
+    do {
+        digit = len % 128;
+        len = len / 128;
+        if (len > 0) {
+            digit |= 0x80;
+        }
+        lenBuf[pos++] = digit;
+        llen++;
+    } while(len>0);
+
+    buf[4-llen] = header;
+    for (int i=0;i<llen;i++) {
+        buf[MQTT_MAX_HEADER_SIZE-llen+i] = lenBuf[i];
+    }
+    return llen+1; // Full header size is variable length bit plus the 1-byte fixed header
+}
+
+boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
+    uint16_t rc;
+    uint8_t hlen = buildHeader(header, buf, length);
+
+#ifdef MQTT_MAX_TRANSFER_SIZE
+    uint8_t* writeBuf = buf+(MQTT_MAX_HEADER_SIZE-hlen);
+    uint16_t bytesRemaining = length+hlen;  //Match the length type
+    uint8_t bytesToWrite;
+    boolean result = true;
+    while((bytesRemaining > 0) && result) {
+        bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
+        rc = _client->write(writeBuf,bytesToWrite);
+        result = (rc == bytesToWrite);
+        bytesRemaining -= rc;
+        writeBuf += rc;
+    }
+    return result;
+#else
+    rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen);
+    lastOutActivity = millis();
+    return (rc == hlen+length);
+#endif
+}
+
+boolean PubSubClient::subscribe(const char* topic) {
+    return subscribe(topic, 0);
+}
+
+boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
+    if (qos > 1) {
+        return false;
+    }
+    if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
+        // Too long
+        return false;
+    }
+    if (connected()) {
+        // Leave room in the buffer for header and variable length field
+        uint16_t length = MQTT_MAX_HEADER_SIZE;
+        nextMsgId++;
+        if (nextMsgId == 0) {
+            nextMsgId = 1;
+        }
+        buffer[length++] = (nextMsgId >> 8);
+        buffer[length++] = (nextMsgId & 0xFF);
+        length = writeString((char*)topic, buffer,length);
+        buffer[length++] = qos;
+        return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
+    }
+    return false;
+}
+
+boolean PubSubClient::unsubscribe(const char* topic) {
+    if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
+        // Too long
+        return false;
+    }
+    if (connected()) {
+        uint16_t length = MQTT_MAX_HEADER_SIZE;
+        nextMsgId++;
+        if (nextMsgId == 0) {
+            nextMsgId = 1;
+        }
+        buffer[length++] = (nextMsgId >> 8);
+        buffer[length++] = (nextMsgId & 0xFF);
+        length = writeString(topic, buffer,length);
+        return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
+    }
+    return false;
+}
+
+void PubSubClient::disconnect() {
+    buffer[0] = MQTTDISCONNECT;
+    buffer[1] = 0;
+    _client->write(buffer,2);
+    _state = MQTT_DISCONNECTED;
+    _client->flush();
+    _client->stop();
+    lastInActivity = lastOutActivity = millis();
+}
+
+uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos) {
+    const char* idp = string;
+    uint16_t i = 0;
+    pos += 2;
+    while (*idp) {
+        buf[pos++] = *idp++;
+        i++;
+    }
+    buf[pos-i-2] = (i >> 8);
+    buf[pos-i-1] = (i & 0xFF);
+    return pos;
+}
+
+
+boolean PubSubClient::connected() {
+    boolean rc;
+    if (_client == NULL ) {
+        rc = false;
+    } else {
+        rc = (int)_client->connected();
+        if (!rc) {
+            if (this->_state == MQTT_CONNECTED) {
+                this->_state = MQTT_CONNECTION_LOST;
+                _client->flush();
+                _client->stop();
+            }
+        }
+    }
+    return rc;
+}
+
+PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) {
+    IPAddress addr(ip[0],ip[1],ip[2],ip[3]);
+    return setServer(addr,port);
+}
+
+PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) {
+    this->ip = ip;
+    this->port = port;
+    this->domain = NULL;
+    return *this;
+}
+
+PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {
+    this->domain = domain;
+    this->port = port;
+    return *this;
+}
+
+PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) {
+    this->callback = callback;
+    return *this;
+}
+
+PubSubClient& PubSubClient::setClient(Client& client){
+    this->_client = &client;
+    return *this;
+}
+
+PubSubClient& PubSubClient::setStream(Stream& stream){
+    this->stream = &stream;
+    return *this;
+}
+
+int PubSubClient::state() {
+    return this->_state;
+}

+ 172 - 173
src/WiFiThermostat/PubSubClient.h

@@ -1,174 +1,173 @@
-/*
- PubSubClient.h - A simple client for MQTT.
-  Nick O'Leary
-  http://knolleary.net
-*/
-
-#ifndef PubSubClient_h
-#define PubSubClient_h
-
-#include <Arduino.h>
-#include "IPAddress.h"
-#include "Client.h"
-#include "Stream.h"
-
-#define MQTT_VERSION_3_1      3
-#define MQTT_VERSION_3_1_1    4
-
-// MQTT_VERSION : Pick the version
-//#define MQTT_VERSION MQTT_VERSION_3_1
-#ifndef MQTT_VERSION
-#define MQTT_VERSION MQTT_VERSION_3_1_1
-#endif
-
-// MQTT_MAX_PACKET_SIZE : Maximum packet size
-#ifndef MQTT_MAX_PACKET_SIZE
-#define MQTT_MAX_PACKET_SIZE 512
-#endif
-
-// MQTT_KEEPALIVE : keepAlive interval in Seconds
-#ifndef MQTT_KEEPALIVE
-#define MQTT_KEEPALIVE 10
-#endif
-
-// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds
-#ifndef MQTT_SOCKET_TIMEOUT
-#define MQTT_SOCKET_TIMEOUT 20
-#endif
-
-// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
-//  in each write call. Needed for the Arduino Wifi Shield. Leave undefined to
-//  pass the entire MQTT packet in each write call.
-//#define MQTT_MAX_TRANSFER_SIZE 80
-
-// Possible values for client.state()
-#define MQTT_CONNECTION_TIMEOUT     -4
-#define MQTT_CONNECTION_LOST        -3
-#define MQTT_CONNECT_FAILED         -2
-#define MQTT_DISCONNECTED           -1
-#define MQTT_CONNECTED               0
-#define MQTT_CONNECT_BAD_PROTOCOL    1
-#define MQTT_CONNECT_BAD_CLIENT_ID   2
-#define MQTT_CONNECT_UNAVAILABLE     3
-#define MQTT_CONNECT_BAD_CREDENTIALS 4
-#define MQTT_CONNECT_UNAUTHORIZED    5
-
-#define MQTTCONNECT     1 << 4  // Client request to connect to Server
-#define MQTTCONNACK     2 << 4  // Connect Acknowledgment
-#define MQTTPUBLISH     3 << 4  // Publish message
-#define MQTTPUBACK      4 << 4  // Publish Acknowledgment
-#define MQTTPUBREC      5 << 4  // Publish Received (assured delivery part 1)
-#define MQTTPUBREL      6 << 4  // Publish Release (assured delivery part 2)
-#define MQTTPUBCOMP     7 << 4  // Publish Complete (assured delivery part 3)
-#define MQTTSUBSCRIBE   8 << 4  // Client Subscribe request
-#define MQTTSUBACK      9 << 4  // Subscribe Acknowledgment
-#define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request
-#define MQTTUNSUBACK    11 << 4 // Unsubscribe Acknowledgment
-#define MQTTPINGREQ     12 << 4 // PING Request
-#define MQTTPINGRESP    13 << 4 // PING Response
-#define MQTTDISCONNECT  14 << 4 // Client is Disconnecting
-#define MQTTReserved    15 << 4 // Reserved
-
-#define MQTTQOS0        (0 << 1)
-#define MQTTQOS1        (1 << 1)
-#define MQTTQOS2        (2 << 1)
-
-// Maximum size of fixed header and variable length size header
-#define MQTT_MAX_HEADER_SIZE 5
-
-#if defined(ESP8266) || defined(ESP32)
-#include <functional>
-#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
-#else
-#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
-#endif
-
-#define CHECK_STRING_LENGTH(l,s) if (l+2+strlen(s) > MQTT_MAX_PACKET_SIZE) {_client->stop();return false;}
-
-class PubSubClient : public Print {
-private:
-   Client* _client;
-   uint8_t buffer[MQTT_MAX_PACKET_SIZE];
-   uint16_t nextMsgId;
-   unsigned long lastOutActivity;
-   unsigned long lastInActivity;
-   bool pingOutstanding;
-   MQTT_CALLBACK_SIGNATURE;
-   uint16_t readPacket(uint8_t*);
-   boolean readByte(uint8_t * result);
-   boolean readByte(uint8_t * result, uint16_t * index);
-   boolean write(uint8_t header, uint8_t* buf, uint16_t length);
-   uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos);
-   // Build up the header ready to send
-   // Returns the size of the header
-   // Note: the header is built at the end of the first MQTT_MAX_HEADER_SIZE bytes, so will start
-   //       (MQTT_MAX_HEADER_SIZE - <returned size>) bytes into the buffer
-   size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length);
-   IPAddress ip;
-   const char* domain;
-   uint16_t port;
-   Stream* stream;
-   int _state;
-public:
-   PubSubClient();
-   PubSubClient(Client& client);
-   PubSubClient(IPAddress, uint16_t, Client& client);
-   PubSubClient(IPAddress, uint16_t, Client& client, Stream&);
-   PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
-   PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
-   PubSubClient(uint8_t *, uint16_t, Client& client);
-   PubSubClient(uint8_t *, uint16_t, Client& client, Stream&);
-   PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
-   PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
-   PubSubClient(const char*, uint16_t, Client& client);
-   PubSubClient(const char*, uint16_t, Client& client, Stream&);
-   PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
-   PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
-
-   PubSubClient& setServer(IPAddress ip, uint16_t port);
-   PubSubClient& setServer(uint8_t * ip, uint16_t port);
-   PubSubClient& setServer(const char * domain, uint16_t port);
-   PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE);
-   PubSubClient& setClient(Client& client);
-   PubSubClient& setStream(Stream& stream);
-
-   boolean connect(const char* id);
-   boolean connect(const char* id, const char* user, const char* pass);
-   boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
-   boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
-   boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession);
-   void disconnect();
-   boolean publish(const char* topic, const char* payload);
-   boolean publish(const char* topic, const char* payload, boolean retained);
-   boolean publish(const char* topic, const uint8_t * payload, unsigned int plength);
-   boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
-   boolean publish_P(const char* topic, const char* payload, boolean retained);
-   boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
-   // Start to publish a message.
-   // This API:
-   //   beginPublish(...)
-   //   one or more calls to write(...)
-   //   endPublish()
-   // Allows for arbitrarily large payloads to be sent without them having to be copied into
-   // a new buffer and held in memory at one time
-   // Returns 1 if the message was started successfully, 0 if there was an error
-   boolean beginPublish(const char* topic, unsigned int plength, boolean retained);
-   // Finish off this publish message (started with beginPublish)
-   // Returns 1 if the packet was sent successfully, 0 if there was an error
-   int endPublish();
-   // Write a single byte of payload (only to be used with beginPublish/endPublish)
-   virtual size_t write(uint8_t);
-   // Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish)
-   // Returns the number of bytes written
-   virtual size_t write(const uint8_t *buffer, size_t size);
-   boolean subscribe(const char* topic);
-   boolean subscribe(const char* topic, uint8_t qos);
-   boolean unsubscribe(const char* topic);
-   boolean loop();
-   boolean connected();
-   int state();
-};
-
-
-#endif
+/*
+ PubSubClient.h - A simple client for MQTT.
+  Nick O'Leary
+  http://knolleary.net
+*/
 
+#ifndef PubSubClient_h
+#define PubSubClient_h
+
+#include <Arduino.h>
+#include "IPAddress.h"
+#include "Client.h"
+#include "Stream.h"
+
+#define MQTT_VERSION_3_1      3
+#define MQTT_VERSION_3_1_1    4
+
+// MQTT_VERSION : Pick the version
+//#define MQTT_VERSION MQTT_VERSION_3_1
+#ifndef MQTT_VERSION
+#define MQTT_VERSION MQTT_VERSION_3_1_1
+#endif
+
+// MQTT_MAX_PACKET_SIZE : Maximum packet size
+#ifndef MQTT_MAX_PACKET_SIZE
+#define MQTT_MAX_PACKET_SIZE 512
+#endif
+
+// MQTT_KEEPALIVE : keepAlive interval in Seconds
+#ifndef MQTT_KEEPALIVE
+#define MQTT_KEEPALIVE 10
+#endif
+
+// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds
+#ifndef MQTT_SOCKET_TIMEOUT
+#define MQTT_SOCKET_TIMEOUT 20
+#endif
+
+// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
+//  in each write call. Needed for the Arduino Wifi Shield. Leave undefined to
+//  pass the entire MQTT packet in each write call.
+//#define MQTT_MAX_TRANSFER_SIZE 80
+
+// Possible values for client.state()
+#define MQTT_CONNECTION_TIMEOUT     -4
+#define MQTT_CONNECTION_LOST        -3
+#define MQTT_CONNECT_FAILED         -2
+#define MQTT_DISCONNECTED           -1
+#define MQTT_CONNECTED               0
+#define MQTT_CONNECT_BAD_PROTOCOL    1
+#define MQTT_CONNECT_BAD_CLIENT_ID   2
+#define MQTT_CONNECT_UNAVAILABLE     3
+#define MQTT_CONNECT_BAD_CREDENTIALS 4
+#define MQTT_CONNECT_UNAUTHORIZED    5
+
+#define MQTTCONNECT     1 << 4  // Client request to connect to Server
+#define MQTTCONNACK     2 << 4  // Connect Acknowledgment
+#define MQTTPUBLISH     3 << 4  // Publish message
+#define MQTTPUBACK      4 << 4  // Publish Acknowledgment
+#define MQTTPUBREC      5 << 4  // Publish Received (assured delivery part 1)
+#define MQTTPUBREL      6 << 4  // Publish Release (assured delivery part 2)
+#define MQTTPUBCOMP     7 << 4  // Publish Complete (assured delivery part 3)
+#define MQTTSUBSCRIBE   8 << 4  // Client Subscribe request
+#define MQTTSUBACK      9 << 4  // Subscribe Acknowledgment
+#define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request
+#define MQTTUNSUBACK    11 << 4 // Unsubscribe Acknowledgment
+#define MQTTPINGREQ     12 << 4 // PING Request
+#define MQTTPINGRESP    13 << 4 // PING Response
+#define MQTTDISCONNECT  14 << 4 // Client is Disconnecting
+#define MQTTReserved    15 << 4 // Reserved
+
+#define MQTTQOS0        (0 << 1)
+#define MQTTQOS1        (1 << 1)
+#define MQTTQOS2        (2 << 1)
+
+// Maximum size of fixed header and variable length size header
+#define MQTT_MAX_HEADER_SIZE 5
+
+#if defined(ESP8266) || defined(ESP32)
+#include <functional>
+#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
+#else
+#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
+#endif
+
+#define CHECK_STRING_LENGTH(l,s) if (l+2+strlen(s) > MQTT_MAX_PACKET_SIZE) {_client->stop();return false;}
+
+class PubSubClient : public Print {
+private:
+   Client* _client;
+   uint8_t buffer[MQTT_MAX_PACKET_SIZE];
+   uint16_t nextMsgId;
+   unsigned long lastOutActivity;
+   unsigned long lastInActivity;
+   boolean pingOutstanding;
+   MQTT_CALLBACK_SIGNATURE;
+   uint16_t readPacket(uint8_t*);
+   boolean readByte(uint8_t * result);
+   boolean readByte(uint8_t * result, uint16_t * index);
+   boolean write(uint8_t header, uint8_t* buf, uint16_t length);
+   uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos);
+   // Build up the header ready to send
+   // Returns the size of the header
+   // Note: the header is built at the end of the first MQTT_MAX_HEADER_SIZE bytes, so will start
+   //       (MQTT_MAX_HEADER_SIZE - <returned size>) bytes into the buffer
+   size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length);
+   IPAddress ip;
+   const char* domain;
+   uint16_t port;
+   Stream* stream;
+   int _state;
+public:
+   PubSubClient();
+   PubSubClient(Client& client);
+   PubSubClient(IPAddress, uint16_t, Client& client);
+   PubSubClient(IPAddress, uint16_t, Client& client, Stream&);
+   PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
+   PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
+   PubSubClient(uint8_t *, uint16_t, Client& client);
+   PubSubClient(uint8_t *, uint16_t, Client& client, Stream&);
+   PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
+   PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
+   PubSubClient(const char*, uint16_t, Client& client);
+   PubSubClient(const char*, uint16_t, Client& client, Stream&);
+   PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
+   PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
+
+   PubSubClient& setServer(IPAddress ip, uint16_t port);
+   PubSubClient& setServer(uint8_t * ip, uint16_t port);
+   PubSubClient& setServer(const char * domain, uint16_t port);
+   PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE);
+   PubSubClient& setClient(Client& client);
+   PubSubClient& setStream(Stream& stream);
+
+   boolean connect(const char* id);
+   boolean connect(const char* id, const char* user, const char* pass);
+   boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
+   boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
+   boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession);
+   void disconnect();
+   boolean publish(const char* topic, const char* payload);
+   boolean publish(const char* topic, const char* payload, boolean retained);
+   boolean publish(const char* topic, const uint8_t * payload, unsigned int plength);
+   boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
+   boolean publish_P(const char* topic, const char* payload, boolean retained);
+   boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
+   // Start to publish a message.
+   // This API:
+   //   beginPublish(...)
+   //   one or more calls to write(...)
+   //   endPublish()
+   // Allows for arbitrarily large payloads to be sent without them having to be copied into
+   // a new buffer and held in memory at one time
+   // Returns 1 if the message was started successfully, 0 if there was an error
+   boolean beginPublish(const char* topic, unsigned int plength, boolean retained);
+   // Finish off this publish message (started with beginPublish)
+   // Returns 1 if the packet was sent successfully, 0 if there was an error
+   int endPublish();
+   // Write a single byte of payload (only to be used with beginPublish/endPublish)
+   virtual size_t write(uint8_t);
+   // Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish)
+   // Returns the number of bytes written
+   virtual size_t write(const uint8_t *buffer, size_t size);
+   boolean subscribe(const char* topic);
+   boolean subscribe(const char* topic, uint8_t qos);
+   boolean unsubscribe(const char* topic);
+   boolean loop();
+   boolean connected();
+   int state();
+};
+
+
+#endif

+ 524 - 471
src/WiFiThermostat/WiFiThermostat.ino

@@ -1,472 +1,525 @@
-
-// pre compiletime config
-
-//#define DEBUG_VERBOSE
-
-#define SPIFFS_DBG
-#define SPIFFS_USE_MAGIC
-
-#define FIRMWARE_NAME "WiFiThermostat"
-#define VERSION "0.2.2"
-
-
-
-// default values, can later be overridden via configuration (conf)
-#define DEVICE_NAME "WiFi-Thermostat-1"
-#define DEFAULT_HTTP_USER ""
-#define DEFAULT_HTTP_PASS ""
-#define HTTP_SET_TOKEN "grzbrz"
-#define MQTT_SERVER "10.1.1.11"
-#define MQTT_PORT 1883
-#define MQTT_USER ""
-#define MQTT_PASS ""
-#define MQTT_TOPIC_IN "Test/Thermostat"
-#define MQTT_TOPIC_OUT "Test/Thermostat/status"
-#define MQTT_OUT_RETAIN false
-#define MQTT_WILLTOPIC "Test/Thermostat/availability"
-#define MQTT_WILLQOS 2
-#define MQTT_WILLRETAIN false
-#define MQTT_WILLMSG "offline"
-#define MQTT_CONNMSG "online"
-#define DOMOTICZ_OUT_TOPIC "domoticz/out"
-
-// default values, can later be overridden via configuration (conf2)
-#define DOMOTICZ_IDX_THERMOSTAT 0
-#define DOMOTICZ_IDX_THERMOSTATMODE 0
-#define DOMOTICZ_IDX_TEMPHUMSENSOR 0
-#define DOMOTICZ_IDX_HEATING 0
-#define DOMOTICZ_IDX_PIR 0
-#define OUTTEMP_TOPIC_IN ""
-#define OUTHUM_TOPIC_IN ""
-#define MQTT_TOPIC_PIR ""                // extra publish topic for PIR sensor
-#define AUTOSAVE_SETTEMP true
-#define AUTOSAVE_SETMODE true
-#define DEFAULT_HEATING_MIN_OFFTIME 120  // minimal time the heating keeps turned off in s
-#define DEFAULT_SETTEMP_MIN 16.0         // minimal temperature that can be set
-#define DEFAULT_SETTEMP_MAX 25.0         // maximal temperature that can be set
-#define DEFAULT_SETTEMP_LOW 18.0         // set temperature in night/low mode
-#define DEFAULT_SETTEMP_LOW2 20.0         // set temperature in night/low mode
-#define DEFAULT_HYSTERESIS 0.1           // hysteresis, normally 0.1 - 0.5
-#define SETTEMP_DECREASE_VALUE 0.0       // decreases the set temp to overcome further temperature rise when the heating is already switched off
-#define TEMPSENSOR_CORRECTION_VALUE 0.0  // correction value for temperature sensor reading
-#define HUMSENSOR_CORRECTION_VALUE 0     // correction value for humidity sensor reading
-#define DEFAULT_MEASURE_INTERVAL 15      // interval for temp/hum measurement
-#define DEFAULT_DISPLAY_INTERVAL 5       // interval for display updates (if out-temp is active, display will toggle in this interval)
-#define DEFAULT_DISPLAY_TIMEOUT 30       // display timeout after keypress (illumination)
-#define DEFAULT_PIR_ENABLES_DISPLAY false
-
-#define MODE_NAME_0 "Heizung aus"
-#define MODE_NAME_1 "Heizen"
-#define MODE_NAME_2 "Absenkung 1"
-#define MODE_NAME_3 "Absenkung 2"
-
-// default initial values
-#define DEFAULT_SETTEMP 21.5
-#define DEFAULT_HEATINGMODE 1
-
-
-// default values that can only be configured at compile time / hardware configuration
-#define CLEARCONF_TOKEN "DOIT!" // Token used to reset configuration via http call on http://<IP>/delconf?token=<TOKEN> (use when password is forgotten)
-#define BUTTON_DEBOUNCE_TIME 120
-#define BUTTON_HOLD_TIME 750
-#define DOMOTICZ_IN_TOPIC "domoticz/in" // if Domoticz IDXes are configured, updates will be sent to this topic
-#define SETTEMP_LOW_MIN 14.0 // minimal configurable temperature for reduction mode
-#define SETTEMP_LOW_MAX 20.0 // maximal configurable temperature for reduction mode
-#define DOMOTICZ_DISMISSUPDATE_TIMEOUT 2500 // after a value was changed by data from domoticz/out, domoticz/out parsing for this device will be turned off for this time to prevent infinite loops
-#define DOMOTICZ_FORCEUPDATE_INTERVAL 15 // interval in min to force update of domoticz devices
-#define MQTT_HEARTBEAT_MAXAGE 180000 // interval for MQTT heartbeat message. only applicable if MQTT IN-topic is defined. after this timeout MQTT reconnect is forced
-
-// pin assignments and I2C addresses
-#define PIN_DHTSENSOR 13
-#define PIN_RELAIS 15 //16
-#define PIN_BUTTON_PLUS 2
-#define PIN_BUTTON_MINUS 0
-#define PIN_BUTTON_MODE 14
-#define PIN_PIRSENSOR 12
-#define DHTTYPE DHT22 // DHT sensor type
-#define LCDADDR 0x27  // I2C address LCD
-#define LCDCOLS 16
-#define LCDLINES 2
-
-// default logic levels
-#define RELAISONSTATE HIGH
-#define BUTTONONSTATE LOW
-
-#include <Button.h>
-#include <ButtonEventCallback.h>
-#include <PushButton.h>
-#include <Bounce2.h>
-
-PushButton buttonPlus = PushButton(PIN_BUTTON_PLUS, ENABLE_INTERNAL_PULLUP);
-PushButton buttonMinus = PushButton(PIN_BUTTON_MINUS, ENABLE_INTERNAL_PULLUP);
-PushButton buttonMode = PushButton(PIN_BUTTON_MODE, ENABLE_INTERNAL_PULLUP);
-PushButton pirSensor = PushButton(PIN_PIRSENSOR, PRESSED_WHEN_HIGH);
-
-#include <PersWiFiManager.h>
-#include <ArduinoJson.h>
-#include <ESP8266WiFi.h>
-#include <WiFiClient.h>
-#include <ESP8266WebServer.h>
-#include <ESP8266mDNS.h>
-#include <ESP8266HTTPUpdateServer.h>
-#include "PubSubClient.h"
-#include <DNSServer.h>
-#include <FS.h>
-#include <Wire.h>
-#include <LiquidCrystal_I2C.h>
-#include <DHT.h>
-
-
-#ifndef MESSZ
-#define MESSZ                405          // Max number of characters in JSON message string (4 x DS18x20 sensors)
-#endif
-// Max message size calculated by PubSubClient is (MQTT_MAX_PACKET_SIZE < 5 + 2 + strlen(topic) + plength)
-#if (MQTT_MAX_PACKET_SIZE -TOPSZ -7) < MESSZ  // If the max message size is too small, throw an error at compile time
-// See pubsubclient.c line 359
-#error "MQTT_MAX_PACKET_SIZE is too small in libraries/PubSubClient/src/PubSubClient.h, increase it to at least 512"
-#endif
-
-
-bool serialdebug = true;
-bool mqttdebug = true;
-
-// config variables - do not change here!
-//conf
-char deviceName[31];  // device name - just for web interface
-char http_user[31];
-char http_pass[31];
-char http_token[31];
-char mqtt_server[41];
-int mqtt_port = MQTT_PORT;
-char mqtt_user[31];
-char mqtt_pass[31];
-char mqtt_topic_in[51];   // MQTT in topic for commands
-char mqtt_topic_out[51];  // MQTT out base topic, will be extended by various value names
-boolean mqtt_outRetain = MQTT_OUT_RETAIN; // send MQTT out with retain flag
-char mqtt_willTopic[51];  // MQTT Last Will topic
-int mqtt_willQos = MQTT_WILLQOS;     // MQTT Last Will topic QOS
-boolean mqtt_willRetain = MQTT_WILLRETAIN;  // MQTT Last Will retain
-char mqtt_willMsg[31];    // MQTT Last Will payload
-char mqtt_connMsg[31];
-char domoticz_out_topic[55];  // domoticz out topic to subscribe to (only applicable if domoticzIdx_Thermostat and/or domoticzIdx_ThermostatMode is set to >0)
-
-char mqtt_topic_in_cmd[61];
-char mqtt_topic_in_setTemp[61];
-char mqtt_topic_in_setMode[61];
-
-
-//conf2
-int domoticzIdx_Thermostat = DOMOTICZ_IDX_THERMOSTAT;
-int domoticzIdx_ThermostatMode = DOMOTICZ_IDX_THERMOSTATMODE;
-int domoticzIdx_TempHumSensor = DOMOTICZ_IDX_TEMPHUMSENSOR;
-int domoticzIdx_Heating = DOMOTICZ_IDX_HEATING;
-int domoticzIdx_PIR = DOMOTICZ_IDX_PIR;
-char outTemp_topic_in[51];
-char outHum_topic_in[51];
-char mqtt_topic_pir[51];
-boolean autoSaveSetTemp = AUTOSAVE_SETTEMP;
-boolean autoSaveHeatingMode = AUTOSAVE_SETMODE;
-int heatingMinOffTime = DEFAULT_HEATING_MIN_OFFTIME; // minimal time the heating keeps turned off in s
-float setTempMin = DEFAULT_SETTEMP_MIN;           // minimal temperature that can be set
-float setTempMax = DEFAULT_SETTEMP_MAX;           // maximal temperature that can be set
-float setTempLow = DEFAULT_SETTEMP_LOW;           // set temperature in night/low mode
-float setTempLow2 = DEFAULT_SETTEMP_LOW2;           // set temperature in night/low mode
-float hysteresis = DEFAULT_HYSTERESIS;            // hysteresis, normally 0.1 - 0.5
-float setTempDecreaseVal = SETTEMP_DECREASE_VALUE;    // decreases the set temp to overcome further temperature rise when the heating is already switched off
-float tempCorrVal = TEMPSENSOR_CORRECTION_VALUE;  // correction value for temperature sensor reading
-int humCorrVal = HUMSENSOR_CORRECTION_VALUE;      // correction value for humidity sensor reading
-int measureInterval = DEFAULT_MEASURE_INTERVAL;   // interval for temp/hum measurement
-int displayInterval = DEFAULT_DISPLAY_INTERVAL;   // interval for display updates (if out-temp is active, display will toggle in this interval)
-int displayTimeout = DEFAULT_DISPLAY_TIMEOUT;     // display timeout after keypress (illumination)
-boolean PIR_enablesDisplay = DEFAULT_PIR_ENABLES_DISPLAY; // PIR sensor enables display illumination
-char modename0[15];
-char modename1[15];
-char modename2[15];
-char modename3[15];
-
-//set values
-float setTemp = DEFAULT_SETTEMP;
-byte heatingMode = DEFAULT_HEATINGMODE; // 0 = off, 1 = normal/day, 2 = night/reduction
-
-float setTempSaved;
-byte heatingModeSaved; // 0 = off, 1 = normal/day, 2 = night/reduction
-
-// not changeable via configuration
-float setTempLowMin = SETTEMP_LOW_MIN;
-float setTempLowMax = SETTEMP_LOW_MAX;
-boolean debug = true;
-int debounceTime = BUTTON_DEBOUNCE_TIME;
-int buttonHoldTime = BUTTON_HOLD_TIME;
-
-// global variables
-float currTemp; // last reading from DHT sensor
-float currTemp_raw; // last reading from DHT sensor
-int currHum; // last reading from DHT sensor
-int currHum_raw; // last reading from DHT sensor
-bool turnHeatingOn = false; // true if heating is active (relais switched on)
-unsigned long heatingLastOnMillis; // last time heating was switched on
-unsigned long heatingLastOffMillis; // last time heating was switched off
-float outTemp; // outside temp (via MQTT if enabled and in-topic configured)
-int outHum; // outside temp (via MQTT if enabled and in-topic configured)
-long outTempHumLastUpdate; // last reading from out temp/hum source
-char outTemp_newValue[6];
-bool outTemp_parseNewValue;
-char outHum_newValue[4];
-bool outHum_parseNewValue;
-char currentModeName[15];
-
-byte whichTempToDisplay; // 1=temp inside (from DHT sensor), 2= temp outside (via MQTT) - if out temp/hum available this value and the displayed value pair toggles with every displayInterval
-unsigned long lastMeasure = 0; // millis of last temp/hum measurement
-unsigned long lastDisplayUpdate = 0; // millis of last display update
-unsigned long lastDisplayToggle = 0; // millis of last display toggle
-unsigned long lastTempUpdate = 0; // last update time of DHT reading
-char msg[50];   // buffer MQTT in payload
-char topic[50]; // buffer MQTT in topic
-bool displayActive = false; // gets true when button is pressed. display light gets switched on until timeout. button actions are only performed while display is active
-bool PIRSensorOn = false;
-unsigned long heatingOnTime, heatingOffTime;
-
-boolean useDomoticz = false; // will be set to true in setup() if idx-values other than 0 are configured
-boolean domoticzOutParseData = false; // indicates that domoticz/out json data is buffered, will then be parsed in next loop() run
-boolean domoticzOutParserBusy = false; // indicates that domoticz/out json data is currently processed - no futher data will be accepted until finished
-char domoticzOutPayload[450]; // buffer for domoticz/out data
-int dismissUpdateFromDomoticzTimeout = DOMOTICZ_DISMISSUPDATE_TIMEOUT; // after a value was changed by data from domoticz/out, domoticz/out parsing for this device will be turned off for this time to prevent infinite loops
-unsigned long lastUpdate_setTemp = 0; // set to millis() every time setTemp value is changed. next update from domoticz/out will be rejected for dismissUpdateFromDomoticzTimeout in this case
-unsigned long lastUpdate_heatingMode = 0; // set to millis() every time heatingMode value is changed. next update from domoticz/out will be rejected for dismissUpdateFromDomoticzTimeout in this case
-bool lastUpdateFromDomoticz_setTemp = false;
-bool lastUpdateFromDomoticz_heatingMode = false;
-int domoticzUpdateInterval = DOMOTICZ_FORCEUPDATE_INTERVAL; // interval in min to force update of domoticz devices
-
-char cmdPayload[101]; // buffer for commands
-boolean cmdInQueue = false; // command is queued and will be processed next loop() run
-bool saveConfigToFlash = false; // conf is saved in next loop() run
-bool saveConfig2ToFlash = false; // conf2 is saved in next loop() run
-
-unsigned int saveValuesTimeout = 5000;
-unsigned long lastValueChange; // is set to millis() whenever setTemp value and/or heatingMode value is changed. used for autoSave function with hardcoded 5s timeout
-bool setTempAlreadySaved = true; // only save if not yet done
-bool heatingModeAlreadySaved = true; // only save if not yet done
-
-byte mqttMode = 0;
-unsigned long mqttLastReconnectAttempt = 0;
-int mqttReconnectAttempts = 0;
-int mqttReconnects = 0;
-unsigned long mqttLastHeartbeat;
-bool mqttInTopicSubscribed = false;
-
-
-
-DHT dht(PIN_DHTSENSOR, DHTTYPE);
-LiquidCrystal_I2C lcd(LCDADDR, LCDCOLS, LCDLINES); // set the LCD address to 0x27 for a 16 chars and 2 line display
-
-WiFiClient espClient;
-
-void mqttCallback(char* topic, byte* payload, unsigned int length);
-PubSubClient mqttclient(espClient);
-
-ESP8266WebServer httpServer(80);
-DNSServer dnsServer;
-PersWiFiManager persWM(httpServer, dnsServer);
-
-ESP8266HTTPUpdateServer httpUpdater;
-
-void setup() {
-  Serial.begin(115200);
-  delay(500);
-  Serial.println();
-  Serial.print(FIRMWARE_NAME);
-  Serial.print(" v");
-  Serial.print(VERSION);
-  Serial.println("starting...");
-
-  pinMode(PIN_RELAIS, OUTPUT);
-  digitalWrite(PIN_RELAIS, !RELAISONSTATE);
-  pinMode(PIN_PIRSENSOR, INPUT);
-
-  buttonPlus.configureButton(configurePushButton);
-  //buttonPlus.onPress(onButtonPressed); // When the button is first pressed, call the function onButtonPressed (further down the page)
-  buttonPlus.onHoldRepeat(1000, 350, onButtonHeld); // Once the button has been held for 1 second (1000ms) call onButtonHeld. Call it again every 350ms until it is let go
-  buttonPlus.onRelease(50, 500, onButtonReleased); // When the button is held >50ms and released after <500ms, call onButtonReleased
-
-  buttonMinus.configureButton(configurePushButton);
-  //buttonMinus.onPress(onButtonPressed); // When the button is first pressed, call the function onButtonPressed (further down the page)
-  buttonMinus.onHoldRepeat(1000, 350, onButtonHeld); // Once the button has been held for 1 second (1000ms) call onButtonHeld. Call it again every 350ms until it is let go
-  buttonMinus.onRelease(50, 500, onButtonReleased); // When the button is held >50ms and released after <500ms, call onButtonReleased
-
-  buttonMode.configureButton(configurePushButton);
-  //buttonMode.onPress(onButtonPressed); // When the button is first pressed, call the function onButtonPressed (further down the page)
-  buttonMode.onHold(1000, onButtonHeldNoRepeat); // Once the button has been held for 1 second (1000ms) call onButtonHeld
-  buttonMode.onRelease(50, 500, onButtonReleased); // When the button is held >50ms and released after <500ms, call onButtonReleased
-
-  pirSensor.configureButton(configurePushButton);
-  //pirSensor.onPress(onButtonPressed); // When the button is first pressed, call the function onButtonPressed (further down the page)
-  pirSensor.onHold(500, onButtonHeldNoRepeat); // Once the button has been held for 1 second (1000ms) call onButtonHeld
-  pirSensor.onRelease(500, onButtonReleased); // When the button is held >50ms and released after <500ms, call onButtonReleased
-
-  //set conf default values (bool, int and float variables are set at declaration)
-  strlcpy(deviceName, DEVICE_NAME, 31);
-  strlcpy(http_user, DEFAULT_HTTP_USER, 31);
-  strlcpy(http_pass, DEFAULT_HTTP_PASS, 31);
-  strlcpy(http_token, HTTP_SET_TOKEN, 31);  
-  strlcpy(mqtt_server, MQTT_SERVER, 41);
-  strlcpy(mqtt_user, MQTT_USER, 31);
-  strlcpy(mqtt_pass, MQTT_PASS, 31);
-  strlcpy(mqtt_topic_in, MQTT_TOPIC_IN, 51);
-  strlcpy(mqtt_topic_out, MQTT_TOPIC_OUT, 51);
-  strlcpy(mqtt_willTopic, MQTT_WILLTOPIC, 51);
-  strlcpy(mqtt_willMsg, MQTT_WILLMSG, 31);
-  strlcpy(mqtt_connMsg, MQTT_CONNMSG, 31);
-  strlcpy(domoticz_out_topic, DOMOTICZ_OUT_TOPIC, 51); // changeable subscription topic, as domoticz supports different flat/hierarchical out-topics
-
-  //set conf2 default values (bool, int and float variables are set at declaration)
-  strlcpy(outTemp_topic_in, OUTTEMP_TOPIC_IN, 51);
-  strlcpy(outHum_topic_in, OUTHUM_TOPIC_IN, 51);
-  strlcpy(mqtt_topic_pir, MQTT_TOPIC_PIR, 51);
-
-  strlcpy(modename0, MODE_NAME_0, 14);
-  strlcpy(modename1, MODE_NAME_1, 14);
-  strlcpy(modename2, MODE_NAME_2, 14);
-  strlcpy(modename3, MODE_NAME_3, 14);
-
-  Serial.println("default config values loaded..");
-
-  Serial.println("Mounting FS...");
-  if (!SPIFFS.begin()) {
-    Serial.println("Failed to mount file system");
-    return;
-  }
-
-  //uncomment for initial SPIFFS format
-  //SPIFFS.format();
-  //Serial.print("Format SPIFFS complete.");
-
-  if (!SPIFFS.exists("/formatComplete.txt")) {
-    Serial.println("Please wait 30 secs for SPIFFS to be formatted");
-    SPIFFS.format();
-    Serial.println("Spiffs formatted");
-
-    File f = SPIFFS.open("/formatComplete.txt", "w");
-    if (!f) {
-      Serial.println("file open failed");
-    } else {
-      f.println("Format Complete");
-    }
-    f.close();
-  } else {
-    Serial.println("SPIFFS is formatted. Moving along...");
-  }
-
-
-  //  // load config from SPIFFS if files exist
-  if (!loadConfig()) {
-    Serial.println("Failed to load conf.json");
-  } else {
-    Serial.println("conf.json loaded");
-  }
-
-  if (!loadConfig2()) {
-    Serial.println("Failed to load conf2.json");
-  } else {
-    Serial.println("conf2.json loaded");
-  }
-
-  if (!loadSetTemp()) {
-    Serial.println("Failed to load file 'setTemp'");
-  } else {
-    Serial.println("file 'setTemp' loaded");
-  }
-
-  if (!loadHeatingMode()) {
-    Serial.println("Failed to load file 'heatingMode'");
-  } else {
-    Serial.println("file 'heatingMode' loaded");
-  }
-
-  setTempSaved = setTemp;
-  heatingModeSaved = heatingMode;
-
-  updateCurrentHeatingModeName();
-
-  // initialize DHT11/22 temp/hum sensor
-  dht.begin();
-
-  
-  
-  mqttPrepareSubscribeTopics();
-
-  checkUseDomoticz();
-  delay(500);
-
-
-  //optional code handlers to run everytime wifi is connected...
-  persWM.onConnect([]() {
-    Serial.println("wifi connected");
-    Serial.println(WiFi.SSID());
-    Serial.println(WiFi.localIP());
-  });
-  //...or AP mode is started
-  persWM.onAp([]() {
-    Serial.println("AP MODE");
-    Serial.println(persWM.getApSsid());
-  });
-
-  //sets network name for AP mode
-  persWM.setApCredentials(DEVICE_NAME);
-  //persWM.setApCredentials(DEVICE_NAME, "password"); optional password
-
-  //make connecting/disconnecting non-blocking
-  persWM.setConnectNonBlock(true);
-
-  //in non-blocking mode, program will continue past this point without waiting
-  persWM.begin();
-  delay(500);
-
-  httpServerInit();
-
-  mqttPrepareConnection();
-  mqttClientInit();
-
-  initDisplay();
-
-  Serial.println("setup complete.");
-  //delay(1000);
-} //void setup
-
-void loop() {
-  checkMillis();
-
-  persWM.handleWiFi(); //in non-blocking mode, handleWiFi must be called in the main loop
-  yield();
-
-  mqttHandleConnection();
-  yield();
-
-  outTempHum_updateOnNewValue();
-
-  yield();
-
-  dnsServer.processNextRequest();
-  httpServer.handleClient();
-
-  buttonPlus.update();
-  buttonMinus.update();
-  buttonMode.update();
-  pirSensor.update();
-
-  yield();
-
-  evalCmd();
-  yield();
-
-  if ( domoticzOutParseData ) {
-    parseDomoticzOut();
-    yield();
-  }
-
-  if (Serial.available()) {
-    serialEvent();
-    yield();
-  }
-
-} //void loop
 
+// pre compiletime config
+
+//#define DEBUG_VERBOSE
+
+#define SPIFFS_DBG
+#define SPIFFS_USE_MAGIC
+
+#define FIRMWARE_NAME "WiFiThermostat"
+#define VERSION "0.3.0"
+
+
+
+// default values, can later be overridden via configuration (conf)
+#define DEVICE_NAME "WiFi-Thermostat-1"
+#define DEFAULT_HTTP_USER ""
+#define DEFAULT_HTTP_PASS ""
+#define HTTP_SET_TOKEN "grzbrz"
+#define MQTT_SERVER "10.1.1.11"
+#define MQTT_PORT 1883
+#define MQTT_USER ""
+#define MQTT_PASS ""
+#define MQTT_TOPIC_IN "Test/Thermostat"
+#define MQTT_TOPIC_OUT "Test/Thermostat/stat"
+#define MQTT_OUT_RETAIN false
+#define MQTT_WILLTOPIC "Test/Thermostat/availability"
+#define MQTT_WILLQOS 2
+#define MQTT_WILLRETAIN false
+#define MQTT_WILLMSG "offline"
+#define MQTT_CONNMSG "online"
+#define DOMOTICZ_OUT_TOPIC "domoticz/out"
+
+// default values, can later be overridden via configuration (conf2)
+#define DOMOTICZ_IDX_THERMOSTAT 0
+#define DOMOTICZ_IDX_THERMOSTATMODE 0
+#define DOMOTICZ_IDX_TEMPHUMSENSOR 0
+#define DOMOTICZ_IDX_HEATING 0
+#define DOMOTICZ_IDX_PIR 0
+#define OUTTEMP_TOPIC_IN ""
+#define OUTHUM_TOPIC_IN ""
+#define MQTT_TOPIC_PIR ""                // extra publish topic for PIR sensor
+#define AUTOSAVE_SETTEMP true
+#define AUTOSAVE_SETMODE true
+#define DEFAULT_HEATING_MIN_OFFTIME 120  // minimal time the heating keeps turned off in s
+#define DEFAULT_SETTEMP_MIN 16.0         // minimal temperature that can be set
+#define DEFAULT_SETTEMP_MAX 25.0         // maximal temperature that can be set
+#define DEFAULT_SETTEMP_LOW 18.0         // set temperature in night/low mode
+#define DEFAULT_SETTEMP_LOW2 20.0        // set temperature in night/low mode
+#define DEFAULT_SETTEMP_HEATOFF 3.0      // set temperature in OFF mode (freezing guard > 0)
+#define DEFAULT_HYSTERESIS 0.1           // hysteresis, normally 0.1 - 0.5
+#define SETTEMP_DECREASE_VALUE 0.0       // decreases the set temp to overcome further temperature rise when the heating is already switched off
+#define TEMPSENSOR_CORRECTION_VALUE 0.0  // correction value for temperature sensor reading
+#define HUMSENSOR_CORRECTION_VALUE 0     // correction value for humidity sensor reading
+#define DEFAULT_MEASURE_INTERVAL 15      // interval for temp/hum measurement
+#define DEFAULT_DISPLAY_INTERVAL 5       // interval for display updates (if out-temp is active, display will toggle in this interval)
+#define DEFAULT_DISPLAY_TIMEOUT 30       // display timeout after keypress (illumination)
+#define DEFAULT_PIR_ENABLES_DISPLAY false
+
+#define MODE_NAME_0 "off"
+#define MODE_NAME_1 "heat"
+
+#define PRESET_NAME_0 "none"
+#define PRESET_NAME_1 "reduction1"
+#define PRESET_NAME_2 "reduction2"
+
+// default initial values
+#define DEFAULT_SETTEMP 21.5
+#define DEFAULT_HEATINGMODE 1
+#define DEFAULT_PRESET 1
+
+// default values that can only be configured at compile time / hardware configuration
+#define CLEARCONF_TOKEN "DOIT!" // Token used to reset configuration via http call on http://<IP>/delconf?token=<TOKEN> (use when password is forgotten)
+#define BUTTON_DEBOUNCE_TIME 120
+#define BUTTON_HOLD_TIME 750
+#define DOMOTICZ_IN_TOPIC "domoticz/in" // if Domoticz IDXes are configured, updates will be sent to this topic
+#define SETTEMP_LOW_MIN 14.0 // minimal configurable temperature for reduction mode
+#define SETTEMP_LOW_MAX 20.0 // maximal configurable temperature for reduction mode
+#define DOMOTICZ_DISMISSUPDATE_TIMEOUT 2500 // after a value was changed by data from domoticz/out, domoticz/out parsing for this device will be turned off for this time to prevent infinite loops
+#define DOMOTICZ_FORCEUPDATE_INTERVAL 15 // interval in min to force update of domoticz devices
+#define MQTT_HEARTBEAT_MAXAGE 180000 // interval for MQTT heartbeat message. only applicable if MQTT IN-topic is defined. after this timeout MQTT reconnect is forced
+
+// pin assignments and I2C addresses
+#define PIN_DHTSENSOR 13
+#define PIN_RELAIS 15 //16
+#define PIN_BUTTON_PLUS 2
+#define PIN_BUTTON_MINUS 0
+#define PIN_BUTTON_MODE 14
+#define PIN_PIRSENSOR 12
+#define DHTTYPE DHT22 // DHT sensor type
+#define LCDADDR 0x27  // I2C address LCD
+#define LCDCOLS 16
+#define LCDLINES 2
+
+// default logic levels
+#define RELAISONSTATE HIGH
+#define BUTTONONSTATE LOW
+
+#include <Button.h>
+#include <ButtonEventCallback.h>
+#include <PushButton.h>
+#include <Bounce2.h>
+
+PushButton buttonPlus = PushButton(PIN_BUTTON_PLUS, ENABLE_INTERNAL_PULLUP);
+PushButton buttonMinus = PushButton(PIN_BUTTON_MINUS, ENABLE_INTERNAL_PULLUP);
+PushButton buttonMode = PushButton(PIN_BUTTON_MODE, ENABLE_INTERNAL_PULLUP);
+PushButton pirSensor = PushButton(PIN_PIRSENSOR, PRESSED_WHEN_HIGH);
+
+#include <PersWiFiManager.h>
+#include <ArduinoJson.h>
+#include <ESP8266WiFi.h>
+#include <WiFiClient.h>
+#include <ESP8266WebServer.h>
+#include <ESP8266mDNS.h>
+#include <ESP8266HTTPUpdateServer.h>
+#include "PubSubClient.h"
+#include <DNSServer.h>
+#include <FS.h>
+#include <Wire.h>
+#include <LiquidCrystal_I2C.h>
+#include <DHT.h>
+#include <string.h>
+
+
+#ifndef MESSZ
+#define MESSZ                405          // Max number of characters in JSON message string (4 x DS18x20 sensors)
+#endif
+// Max message size calculated by PubSubClient is (MQTT_MAX_PACKET_SIZE < 5 + 2 + strlen(topic) + plength)
+#if (MQTT_MAX_PACKET_SIZE -TOPSZ -7) < MESSZ  // If the max message size is too small, throw an error at compile time
+// See pubsubclient.c line 359
+#error "MQTT_MAX_PACKET_SIZE is too small in libraries/PubSubClient/src/PubSubClient.h, increase it to at least 512"
+#endif
+
+
+boolean serialdebug = true;
+boolean mqttdebug = true;
+
+// config variables - do not change here!
+//conf
+char deviceName[31];  // device name - just for web interface
+char http_user[31];
+char http_pass[31];
+char http_token[31];
+char mqtt_server[41];
+int mqtt_port = MQTT_PORT;
+char mqtt_user[31];
+char mqtt_pass[31];
+char mqtt_topic_in[51];   // MQTT in topic for commands
+char mqtt_topic_out[51];  // MQTT out base topic, will be extended by various value names
+boolean mqtt_outRetain = MQTT_OUT_RETAIN; // send MQTT out with retain flag
+char mqtt_willTopic[51];  // MQTT Last Will topic
+int mqtt_willQos = MQTT_WILLQOS;     // MQTT Last Will topic QOS
+boolean mqtt_willRetain = MQTT_WILLRETAIN;  // MQTT Last Will retain
+char mqtt_willMsg[31];    // MQTT Last Will payload
+char mqtt_connMsg[31];
+char domoticz_out_topic[55];  // domoticz out topic to subscribe to (only applicable if domoticzIdx_Thermostat and/or domoticzIdx_ThermostatMode is set to >0)
+
+char mqtt_topic_in_cmd[61];
+char mqtt_topic_in_setTemp[61];
+char mqtt_topic_in_setMode[61];
+char mqtt_topic_in_setPreset[61];
+
+
+//conf2
+int domoticzIdx_Thermostat = DOMOTICZ_IDX_THERMOSTAT;
+int domoticzIdx_ThermostatMode = DOMOTICZ_IDX_THERMOSTATMODE;
+int domoticzIdx_TempHumSensor = DOMOTICZ_IDX_TEMPHUMSENSOR;
+int domoticzIdx_Heating = DOMOTICZ_IDX_HEATING;
+int domoticzIdx_PIR = DOMOTICZ_IDX_PIR;
+char outTemp_topic_in[51];
+char outHum_topic_in[51];
+char mqtt_topic_pir[51];
+boolean autoSaveSetTemp = AUTOSAVE_SETTEMP;
+boolean autoSaveHeatingMode = AUTOSAVE_SETMODE;
+int heatingMinOffTime = DEFAULT_HEATING_MIN_OFFTIME; // minimal time the heating keeps turned off in s
+float setTempMin = DEFAULT_SETTEMP_MIN;           // minimal temperature that can be set
+float setTempMax = DEFAULT_SETTEMP_MAX;           // maximal temperature that can be set
+float setTempLow = DEFAULT_SETTEMP_LOW;           // set temperature in night/low mode
+float setTempLow2 = DEFAULT_SETTEMP_LOW2;           // set temperature in night/low mode
+float hysteresis = DEFAULT_HYSTERESIS;            // hysteresis, normally 0.1 - 0.5
+float setTempDecreaseVal = SETTEMP_DECREASE_VALUE;    // decreases the set temp to overcome further temperature rise when the heating is already switched off
+float tempCorrVal = TEMPSENSOR_CORRECTION_VALUE;  // correction value for temperature sensor reading
+int humCorrVal = HUMSENSOR_CORRECTION_VALUE;      // correction value for humidity sensor reading
+int measureInterval = DEFAULT_MEASURE_INTERVAL;   // interval for temp/hum measurement
+int displayInterval = DEFAULT_DISPLAY_INTERVAL;   // interval for display updates (if out-temp is active, display will toggle in this interval)
+int displayInterval_saved = DEFAULT_DISPLAY_INTERVAL;
+int displayTimeout = DEFAULT_DISPLAY_TIMEOUT;     // display timeout after keypress (illumination)
+boolean PIR_enablesDisplay = DEFAULT_PIR_ENABLES_DISPLAY; // PIR sensor enables display illumination
+char modename0[15];
+char modename1[15];
+char psetname0[15];
+char psetname1[15];
+char psetname2[15];
+char offMessage[15];
+
+//set values
+float setTemp = DEFAULT_SETTEMP;
+float currSetTemp = DEFAULT_SETTEMP;
+byte heatingMode = DEFAULT_HEATINGMODE; // 0 = off, 1 = heat
+byte preset = DEFAULT_PRESET; // 0 = normal/day, 1 = night/reduction 1, 2 = reduction 2 (long term leave)
+
+float setTempSaved;
+byte heatingModeSaved; // 0 = off, 1 = heat/on
+byte presetSaved; // 0 = normal/day, 1 = night/reduction 1, 2 = reduction 2
+
+// not changeable via configuration
+float setTempLowMin = SETTEMP_LOW_MIN;
+float setTempLowMax = SETTEMP_LOW_MAX;
+boolean debug = true;
+int debounceTime = BUTTON_DEBOUNCE_TIME;
+int buttonHoldTime = BUTTON_HOLD_TIME;
+
+// global variables
+float currTemp; // last reading from DHT sensor
+float currTemp_raw; // last reading from DHT sensor
+int currHum; // last reading from DHT sensor
+int currHum_raw; // last reading from DHT sensor
+boolean turnHeatingOn = false; // true if heating is active (relais switched on)
+unsigned long heatingLastOnMillis; // last time heating was switched on
+unsigned long heatingLastOffMillis; // last time heating was switched off
+float outTemp; // outside temp (via MQTT if enabled and in-topic configured)
+int outHum; // outside temp (via MQTT if enabled and in-topic configured)
+long outTempHumLastUpdate; // last reading from out temp/hum source
+char outTemp_newValue[6];
+boolean outTemp_parseNewValue;
+char outHum_newValue[4];
+boolean outHum_parseNewValue;
+char currentModeName[15];
+char currentPresetName[15];
+
+byte whichTempToDisplay; // 1=temp inside (from DHT sensor), 2= temp outside (via MQTT) - if out temp/hum available this value and the displayed value pair toggles with every displayInterval
+unsigned long lastMeasure = 0; // millis of last temp/hum measurement
+unsigned long lastDisplayUpdate = 0; // millis of last display update
+unsigned long lastDisplayToggle = 0; // millis of last display toggle
+unsigned long lastTempUpdate = 0; // last update time of DHT reading
+char msg[50];   // buffer MQTT in payload
+char topic[50]; // buffer MQTT in topic
+boolean displayActive = false; // gets true when button is pressed. display light gets switched on until timeout. button actions are only performed while display is active
+boolean PIRSensorOn = false;
+unsigned long heatingOnTime, heatingOffTime;
+
+boolean useDomoticz = false; // will be set to true in setup() if idx-values other than 0 are configured
+boolean domoticzOutParseData = false; // indicates that domoticz/out json data is buffered, will then be parsed in next loop() run
+boolean domoticzOutParserBusy = false; // indicates that domoticz/out json data is currently processed - no futher data will be accepted until finished
+char domoticzOutPayload[450]; // buffer for domoticz/out data
+int dismissUpdateFromDomoticzTimeout = DOMOTICZ_DISMISSUPDATE_TIMEOUT; // after a value was changed by data from domoticz/out, domoticz/out parsing for this device will be turned off for this time to prevent infinite loops
+unsigned long lastUpdate_setTemp = 0; // set to millis() every time setTemp value is changed. next update from domoticz/out will be rejected for dismissUpdateFromDomoticzTimeout in this case
+unsigned long lastUpdate_heatingMode = 0; // set to millis() every time heatingMode value is changed. next update from domoticz/out will be rejected for dismissUpdateFromDomoticzTimeout in this case
+unsigned long lastUpdate_preset = 0; // set to millis() every time preset value is changed. next update from domoticz/out will be rejected for dismissUpdateFromDomoticzTimeout in this case
+boolean lastUpdateFromDomoticz_setTemp = false;
+boolean lastUpdateFromDomoticz_heatingMode = false;
+int domoticzUpdateInterval = DOMOTICZ_FORCEUPDATE_INTERVAL; // interval in min to force update of domoticz devices
+
+char cmdPayload[101]; // buffer for commands
+boolean cmdInQueue = false; // command is queued and will be processed next loop() run
+boolean saveConfigToFlash = false; // conf is saved in next loop() run
+boolean saveConfig2ToFlash = false; // conf2 is saved in next loop() run
+
+unsigned int saveValuesTimeout = 5000;
+unsigned long lastValueChange; // is set to millis() whenever setTemp value and/or heatingMode value is changed. used for autoSave function with hardcoded 5s timeout
+boolean setTempAlreadySaved = true; // only save if not yet done
+boolean heatingModeAlreadySaved = true; // only save if not yet done
+boolean presetAlreadySaved = true; // only save if not yet done
+
+unsigned long lastRun = 0;
+int count100ms = 0;
+int countSeconds = 0;
+int countMeasureInterval = 0;
+int countDisplayInterval = 0;
+int displayOverlayMsgTimeout = 2;
+boolean togglingTempHumAIDisplay = false;
+
+byte mqttMode = 0;
+unsigned long mqttLastReconnectAttempt = 0;
+int mqttReconnectAttempts = 0;
+int mqttReconnects = 0;
+boolean mqttConnected = false;
+unsigned long mqttLastHeartbeat;
+boolean mqttInTopicSubscribed = false;
+
+boolean pendingRestart = false;
+boolean doRestart = false;
+unsigned long pendingRestart_lastMillis = 0;
+
+boolean displayShowFullscreenMsg = false;
+//unsigned long displayShowFullscreenMsg_lastMillis = 0;
+
+boolean pendingPresetToggle = false;
+boolean updateDisplayImmediately = false;
+int pendingPreset;
+char pendingPresetName[15];
+//unsigned long pendingPreset_millis = 0;
+//int pendingPreset_timeout = 2000;
+
+boolean displayShowLine2OverlayMsg = false;
+
+DHT dht(PIN_DHTSENSOR, DHTTYPE);
+LiquidCrystal_I2C lcd(LCDADDR, LCDCOLS, LCDLINES); // set the LCD address to 0x27 for a 16 chars and 2 line display
+
+WiFiClient espClient;
+
+void mqttCallback(char* topic, byte* payload, unsigned int length);
+PubSubClient mqttclient(espClient);
+
+ESP8266WebServer httpServer(80);
+DNSServer dnsServer;
+PersWiFiManager persWM(httpServer, dnsServer);
+
+ESP8266HTTPUpdateServer httpUpdater;
+
+void setup() {
+  Serial.begin(115200);
+  delay(500);
+  Serial.println();
+  Serial.print(FIRMWARE_NAME);
+  Serial.print(" v");
+  Serial.print(VERSION);
+  Serial.println("starting...");
+
+  pinMode(PIN_RELAIS, OUTPUT);
+  digitalWrite(PIN_RELAIS, !RELAISONSTATE);
+  pinMode(PIN_PIRSENSOR, INPUT);
+
+  buttonPlus.configureButton(configurePushButton);
+  //buttonPlus.onPress(onButtonPressed); // When the button is first pressed, call the function onButtonPressed (further down the page)
+  buttonPlus.onHoldRepeat(1000, 350, onButtonHeld); // Once the button has been held for 1 second (1000ms) call onButtonHeld. Call it again every 350ms until it is let go
+  buttonPlus.onRelease(50, 500, onButtonReleased); // When the button is held >50ms and released after <500ms, call onButtonReleased
+
+  buttonMinus.configureButton(configurePushButton);
+  //buttonMinus.onPress(onButtonPressed); // When the button is first pressed, call the function onButtonPressed (further down the page)
+  buttonMinus.onHoldRepeat(1000, 350, onButtonHeld); // Once the button has been held for 1 second (1000ms) call onButtonHeld. Call it again every 350ms until it is let go
+  buttonMinus.onRelease(50, 500, onButtonReleased); // When the button is held >50ms and released after <500ms, call onButtonReleased
+
+  buttonMode.configureButton(configurePushButton);
+  //buttonMode.onPress(onButtonPressed); // When the button is first pressed, call the function onButtonPressed (further down the page)
+  //buttonMode.onHold(1000, onButtonHeldNoRepeat); // Once the button has been held for 1 second (1000ms) call onButtonHeld
+  buttonMode.onHoldRepeat(1000, 1000, onButtonHeld); // Once the button has been held for 1 second (1000ms) call onButtonHeld
+  buttonMode.onRelease(50, 500, onButtonReleased); // When the button is held >50ms and released after <500ms, call onButtonReleased
+
+  pirSensor.configureButton(configurePushButton);
+  //pirSensor.onPress(onButtonPressed); // When the button is first pressed, call the function onButtonPressed (further down the page)
+  pirSensor.onHold(500, onButtonHeldNoRepeat); // Once the button has been held for 1 second (1000ms) call onButtonHeld
+  pirSensor.onRelease(500, onButtonReleased); // When the button is held >50ms and released after <500ms, call onButtonReleased
+
+  //set conf default values (bool, int and float variables are set at declaration)
+  strlcpy(deviceName, DEVICE_NAME, 31);
+  strlcpy(http_user, DEFAULT_HTTP_USER, 31);
+  strlcpy(http_pass, DEFAULT_HTTP_PASS, 31);
+  strlcpy(http_token, HTTP_SET_TOKEN, 31);
+  strlcpy(mqtt_server, MQTT_SERVER, 41);
+  strlcpy(mqtt_user, MQTT_USER, 31);
+  strlcpy(mqtt_pass, MQTT_PASS, 31);
+  strlcpy(mqtt_topic_in, MQTT_TOPIC_IN, 51);
+  strlcpy(mqtt_topic_out, MQTT_TOPIC_OUT, 51);
+  strlcpy(mqtt_willTopic, MQTT_WILLTOPIC, 51);
+  strlcpy(mqtt_willMsg, MQTT_WILLMSG, 31);
+  strlcpy(mqtt_connMsg, MQTT_CONNMSG, 31);
+  strlcpy(domoticz_out_topic, DOMOTICZ_OUT_TOPIC, 51); // changeable subscription topic, as domoticz supports different flat/hierarchical out-topics
+
+  //set conf2 default values (bool, int and float variables are set at declaration)
+  strlcpy(outTemp_topic_in, OUTTEMP_TOPIC_IN, 51);
+  strlcpy(outHum_topic_in, OUTHUM_TOPIC_IN, 51);
+  strlcpy(mqtt_topic_pir, MQTT_TOPIC_PIR, 51);
+
+  strlcpy(modename0, MODE_NAME_0, 14);
+  strlcpy(modename1, MODE_NAME_1, 14);
+  strlcpy(psetname0, PRESET_NAME_0, 14);
+  strlcpy(psetname1, PRESET_NAME_1, 14);
+  strlcpy(psetname2, PRESET_NAME_2, 14);
+
+  Serial.println("default config values loaded..");
+
+  Serial.println("Mounting FS...");
+  if (!SPIFFS.begin()) {
+    Serial.println("Failed to mount file system");
+    return;
+  }
+
+  //uncomment for initial SPIFFS format
+  //SPIFFS.format();
+  //Serial.print("Format SPIFFS complete.");
+
+  if (!SPIFFS.exists("/formatComplete.txt")) {
+    Serial.println("Please wait 30 secs for SPIFFS to be formatted");
+    SPIFFS.format();
+    Serial.println("Spiffs formatted");
+
+    File f = SPIFFS.open("/formatComplete.txt", "w");
+    if (!f) {
+      Serial.println("file open failed");
+    } else {
+      f.println("Format Complete");
+    }
+    f.close();
+  } else {
+    Serial.println("SPIFFS is formatted. Moving along...");
+  }
+
+
+  //  // load config from SPIFFS if files exist
+  if (!loadConfig()) {
+    Serial.println("Failed to load conf.json");
+  } else {
+    Serial.println("conf.json loaded");
+  }
+
+  if (!loadConfig2()) {
+    Serial.println("Failed to load conf2.json");
+  } else {
+    Serial.println("conf2.json loaded");
+  }
+
+  if (!loadSetTemp()) {
+    Serial.println("Failed to load file 'setTemp'");
+  } else {
+    Serial.println("file 'setTemp' loaded");
+  }
+
+  if (!loadHeatingMode()) {
+    Serial.println("Failed to load file 'heatingMode'");
+  } else {
+    Serial.println("file 'heatingMode' loaded");
+  }
+
+  if (!loadPreset()) {
+    Serial.println("Failed to load file 'preset'");
+  } else {
+    Serial.println("file 'preset' loaded");
+  }
+
+  setTempSaved = setTemp;
+  heatingModeSaved = heatingMode;
+  presetSaved = preset;
+
+  updateCurrentHeatingModeName();
+  updateCurrentPresetName();
+
+  // initialize DHT11/22 temp/hum sensor
+  dht.begin();
+
+
+
+  mqttPrepareSubscribeTopics();
+
+  checkUseDomoticz();
+  delay(500);
+
+
+  //optional code handlers to run everytime wifi is connected...
+  persWM.onConnect([]() {
+    Serial.println("wifi connected");
+    Serial.println(WiFi.SSID());
+    Serial.println(WiFi.localIP());
+    displayShowWifiConnected();
+  });
+  //...or AP mode is started
+  persWM.onAp([]() {
+    Serial.println("AP MODE");
+    Serial.println(persWM.getApSsid());
+    displayShowWifiConnectionError();
+  });
+
+  //sets network name for AP mode
+  persWM.setApCredentials(DEVICE_NAME);
+  //persWM.setApCredentials(DEVICE_NAME, "password"); optional password
+
+  //make connecting/disconnecting non-blocking
+  persWM.setConnectNonBlock(true);
+
+  //in non-blocking mode, program will continue past this point without waiting
+  persWM.begin();
+  delay(500);
+
+  httpServerInit();
+
+  mqttPrepareConnection();
+  mqttClientInit();
+
+  initDisplay();
+
+  Serial.println("setup complete.");
+  //delay(1000);
+} //void setup
+
+void loop() {
+  checkMillis();
+
+  persWM.handleWiFi(); //in non-blocking mode, handleWiFi must be called in the main loop
+  yield();
+
+  mqttHandleConnection();
+  yield();
+
+  outTempHum_updateOnNewValue();
+
+  yield();
+
+  dnsServer.processNextRequest();
+  httpServer.handleClient();
+
+  buttonPlus.update();
+  buttonMinus.update();
+  buttonMode.update();
+  pirSensor.update();
+
+  yield();
+
+  evalCmd();
+  yield();
+
+  if ( domoticzOutParseData ) {
+    parseDomoticzOut();
+    yield();
+  }
+
+  if (Serial.available()) {
+    serialEvent();
+    yield();
+  }
+
+  if (updateDisplayImmediately) {
+    updateDisplayImmediately = false;
+    updateDisplay();
+  }
+
+} //void loop

BIN
src/WiFiThermostat/WiFiThermostat.ino.d1_mini.bin


+ 11 - 0
src/WiFiThermostat/changes_v0.3.txt

@@ -0,0 +1,11 @@
+- split "mode" to "mode" and "preset", changes in nearly all source files
+- add In+Out Temperature display (without humidity) without toggling on LCD line 1
+- add publish topic "presetName" and "presetHA", where presetHA returns "none" for default preset and the name for the rest
+- add configuration for preset names, off-message, option for non toggling temp display (conf2 page)
+- minor fixed in http api and Web-IF
+- fixes/improvements in MQTT handling (subscribe to less topics than before using wildcard)
+- restart: when holding down MODE button for >4s, display shows "Restart?". 
+  Restart is executed if: 
+  - MODE button is keeped holding
+  - MODE button is pressed/held again as long as the "Restarting?" message is displayed
+- return to default operation mode when + or - button is pressed

+ 181 - 181
src/WiFiThermostat/commands.ino

@@ -1,182 +1,182 @@
-
-#define SER_INPUT_SIZE 70
-char serBuffer[SER_INPUT_SIZE + 1];    // Serial Input-Buffer
-int serBufferCount;    // Anzahl der eingelesenen Zeichen
-
-void serialEvent() {
-  char ch = Serial.read();
-  serBuffer[serBufferCount] = ch;
-  serBufferCount++;
-  if (ch == 13 || ch == 10) { // ASCII code 13 = "CR",  10 = "LF"
-    serBuffer[serBufferCount - 1] = '\0'; // string nullterminieren
-#ifdef DEBUG_VERBOSE
-    Serial.print("serial cmd: '");
-    Serial.print(serBuffer);
-    Serial.println("'");
-#endif
-    strlcpy(cmdPayload, serBuffer, 101);
-    cmdInQueue = true;
-    evalCmd();
-    serBufferCount = 0;
-  }
-}
-
-
-
-void evalCmd() {
-  if (cmdInQueue) {
-
-    //Serial.print("cmdPayload: ");
-    //Serial.println(cmdPayload);
-
-    if (strncmp(cmdPayload, "HEARTBEAT", 9) == 0) {
-      Serial.println("resetting HEARTBEAT");
-      mqttLastHeartbeat = millis();
-    }
-
-    else if (strncmp(cmdPayload, "loadconf", 8) == 0) {
-      loadConfig();
-      loadConfig2();
-      mqttPrepareConnection();
-    }
-
-    else if (strncmp(cmdPayload, "set ", 4) == 0) {
-      char buf[81];
-      char setconfCmd[16];
-      char setconfPayload[62];
-
-      int len = strlen(cmdPayload) - 4;
-      for (int i = 0; i < len; i++) {
-        if (i < 81) buf[i] = cmdPayload[i + 4];
-      }
-      if (len <= 81) buf[len] = '\0';
-      else buf[81] = '\0';
-
-#ifdef DEBUG_VERBOSE
-      Serial.print("Buf: ");
-      Serial.println(buf);
-#endif
-
-      bool cmdNoPayload = false;
-      int setconfCmdLen = 0;
-      for (int i = 0; i < len; i++) {
-        //if (buf[i] == 32 || buf[i] == '\0') break; // if SPACE command name is finished, if \0 command parameter is missing
-        if (buf[i] == 32 || buf[i] == 61 || buf[i] == 58) break; // if SPACE(32) or =(61) or :(58) command name is finished
-        else if (buf[i] == 0 || buf[i] == 10 || buf[i] == 13) { // if \0, LF (10) or CR (13) command parameter is missing
-          cmdNoPayload = true;
-          break;
-        }
-        setconfCmd[i] = buf[i];
-        setconfCmdLen++;
-      }
-      setconfCmd[setconfCmdLen] = '\0';
-      yield();
-
-      if (setconfCmdLen == len) cmdNoPayload = true;
-
-#ifdef DEBUG_VERBOSE
-      Serial.print("setconfCmd: '");
-      Serial.print(setconfCmd);
-      Serial.print("'");
-#endif
-
-      if ( cmdNoPayload ) {
-#ifdef DEBUG_VERBOSE
-        Serial.println(", no payload, displaying current value");
-#endif
-        getConfig(setconfCmd);
-      }
-      else {
-#ifdef DEBUG_VERBOSE
-        Serial.println();
-#endif
-        int setconfPayloadLen = 0;
-        for (int i = 0; i < len; i++) {
-          char c = buf[i + setconfCmdLen + 1];
-          if (c == 0 || c == 10 || c == 13) break; // if \0, LF (10) or CR (13) command parameter is finished
-          setconfPayload[i] = c;
-          setconfPayloadLen++;
-        }
-        setconfPayload[setconfPayloadLen] = '\0';
-
-#ifdef DEBUG_VERBOSE
-        Serial.print("setconfPayload: '");
-        Serial.print(setconfPayload);
-        Serial.println("'");
-#endif
-        setConfig(setconfCmd, setconfPayload);
-      }
-    }
-    else if (strncmp(cmdPayload, "get ", 4) == 0) {
-      char buf[81];
-      char setconfCmd[16];
-      char setconfPayload[62];
-
-      int len = strlen(cmdPayload) - 4;
-      for (int i = 0; i < len; i++) {
-        if (i < 81) buf[i] = cmdPayload[i + 4];
-      }
-      if (len <= 81) buf[len] = '\0';
-      else buf[81] = '\0';
-
-#ifdef DEBUG_VERBOSE
-      Serial.print("Buf: ");
-      Serial.println(buf);
-#endif
-
-      bool cmdNoPayload = false;
-      int setconfCmdLen = 0;
-      for (int i = 0; i < len; i++) {
-        //if (buf[i] == 32 || buf[i] == '\0') break; // if SPACE command name is finished, if \0 command parameter is missing
-        if (buf[i] == 32 || buf[i] == 61 || buf[i] == 58) break; // if SPACE(32) or =(61) or :(58) command name is finished
-        else if (buf[i] == 0 || buf[i] == 10 || buf[i] == 13) { // if \0, LF (10) or CR (13) command parameter is missing
-          cmdNoPayload = true;
-          break;
-        }
-        setconfCmd[i] = buf[i];
-        setconfCmdLen++;
-      }
-      setconfCmd[setconfCmdLen] = '\0';
-      yield();
-#ifdef DEBUG_VERBOSE
-      Serial.print("setconfCmd: '");
-      Serial.print(setconfCmd);
-      Serial.println("'");
-#endif
-      getConfig(setconfCmd);
-    }
-    else if (strncmp(cmdPayload, "restart", 7) == 0) {
-      Serial.print("restarting...");
-      delay(100);
-      ESP.restart();
-    }
-    else if (strncmp(cmdPayload, "save", 4) == 0) {
-      saveConfig();
-      yield();
-      saveConfig2();
-      yield();
-
-      saveSetTemp();
-      saveHeatingMode();
-      
-      //Serial.println("saved config to SPIFFS");
-      sendStatus("saved config to SPIFFS");
-      Serial.println("reloading config to check...");
-      loadConfig();
-      yield();
-      loadConfig2();
-      yield();
-    }
-    else if (strncmp(cmdPayload, "getconf", 7) == 0) {
-      printConfig();
-      printConfig2();
-    }
-    else if (strncmp(cmdPayload, "delconf", 7) == 0) {
-      deleteConfig();
-    }
-
-
-    cmdInQueue = false;
-  }
-}
 
+#define SER_INPUT_SIZE 70
+char serBuffer[SER_INPUT_SIZE + 1];    // Serial Input-Buffer
+int serBufferCount;    // Anzahl der eingelesenen Zeichen
+
+void serialEvent() {
+  char ch = Serial.read();
+  serBuffer[serBufferCount] = ch;
+  serBufferCount++;
+  if (ch == 13 || ch == 10) { // ASCII code 13 = "CR",  10 = "LF"
+    serBuffer[serBufferCount - 1] = '\0'; // string nullterminieren
+#ifdef DEBUG_VERBOSE
+    Serial.print("serial cmd: '");
+    Serial.print(serBuffer);
+    Serial.println("'");
+#endif
+    strlcpy(cmdPayload, serBuffer, 101);
+    cmdInQueue = true;
+    evalCmd();
+    serBufferCount = 0;
+  }
+}
+
+
+
+void evalCmd() {
+  if (cmdInQueue) {
+
+    //Serial.print("cmdPayload: ");
+    //Serial.println(cmdPayload);
+
+    if (strncmp(cmdPayload, "HEARTBEAT", 9) == 0) {
+      Serial.println("resetting HEARTBEAT");
+      mqttLastHeartbeat = millis();
+      mqttConnected = true;
+    }
+
+    else if (strncmp(cmdPayload, "loadconf", 8) == 0) {
+      loadConfig();
+      loadConfig2();
+      mqttPrepareConnection();
+    }
+
+    else if (strncmp(cmdPayload, "set ", 4) == 0) {
+      char buf[81];
+      char setconfCmd[16];
+      char setconfPayload[62];
+
+      int len = strlen(cmdPayload) - 4;
+      for (int i = 0; i < len; i++) {
+        if (i < 81) buf[i] = cmdPayload[i + 4];
+      }
+      if (len <= 81) buf[len] = '\0';
+      else buf[81] = '\0';
+
+#ifdef DEBUG_VERBOSE
+      Serial.print("Buf: ");
+      Serial.println(buf);
+#endif
+
+      boolean cmdNoPayload = false;
+      int setconfCmdLen = 0;
+      for (int i = 0; i < len; i++) {
+        //if (buf[i] == 32 || buf[i] == '\0') break; // if SPACE command name is finished, if \0 command parameter is missing
+        if (buf[i] == 32 || buf[i] == 61 || buf[i] == 58) break; // if SPACE(32) or =(61) or :(58) command name is finished
+        else if (buf[i] == 0 || buf[i] == 10 || buf[i] == 13) { // if \0, LF (10) or CR (13) command parameter is missing
+          cmdNoPayload = true;
+          break;
+        }
+        setconfCmd[i] = buf[i];
+        setconfCmdLen++;
+      }
+      setconfCmd[setconfCmdLen] = '\0';
+      yield();
+
+      if (setconfCmdLen == len) cmdNoPayload = true;
+
+#ifdef DEBUG_VERBOSE
+      Serial.print("setconfCmd: '");
+      Serial.print(setconfCmd);
+      Serial.print("'");
+#endif
+
+      if ( cmdNoPayload ) {
+#ifdef DEBUG_VERBOSE
+        Serial.println(", no payload, displaying current value");
+#endif
+        getConfig(setconfCmd);
+      }
+      else {
+#ifdef DEBUG_VERBOSE
+        Serial.println();
+#endif
+        int setconfPayloadLen = 0;
+        for (int i = 0; i < len; i++) {
+          char c = buf[i + setconfCmdLen + 1];
+          if (c == 0 || c == 10 || c == 13) break; // if \0, LF (10) or CR (13) command parameter is finished
+          setconfPayload[i] = c;
+          setconfPayloadLen++;
+        }
+        setconfPayload[setconfPayloadLen] = '\0';
+
+#ifdef DEBUG_VERBOSE
+        Serial.print("setconfPayload: '");
+        Serial.print(setconfPayload);
+        Serial.println("'");
+#endif
+        setConfig(setconfCmd, setconfPayload);
+      }
+    }
+    else if (strncmp(cmdPayload, "get ", 4) == 0) {
+      char buf[81];
+      char setconfCmd[16];
+      char setconfPayload[62];
+
+      int len = strlen(cmdPayload) - 4;
+      for (int i = 0; i < len; i++) {
+        if (i < 81) buf[i] = cmdPayload[i + 4];
+      }
+      if (len <= 81) buf[len] = '\0';
+      else buf[81] = '\0';
+
+#ifdef DEBUG_VERBOSE
+      Serial.print("Buf: ");
+      Serial.println(buf);
+#endif
+
+      boolean cmdNoPayload = false;
+      int setconfCmdLen = 0;
+      for (int i = 0; i < len; i++) {
+        //if (buf[i] == 32 || buf[i] == '\0') break; // if SPACE command name is finished, if \0 command parameter is missing
+        if (buf[i] == 32 || buf[i] == 61 || buf[i] == 58) break; // if SPACE(32) or =(61) or :(58) command name is finished
+        else if (buf[i] == 0 || buf[i] == 10 || buf[i] == 13) { // if \0, LF (10) or CR (13) command parameter is missing
+          cmdNoPayload = true;
+          break;
+        }
+        setconfCmd[i] = buf[i];
+        setconfCmdLen++;
+      }
+      setconfCmd[setconfCmdLen] = '\0';
+      yield();
+#ifdef DEBUG_VERBOSE
+      Serial.print("setconfCmd: '");
+      Serial.print(setconfCmd);
+      Serial.println("'");
+#endif
+      getConfig(setconfCmd);
+    }
+    else if (strncmp(cmdPayload, "restart", 7) == 0) {
+      Serial.print("restarting...");
+      delay(100);
+      ESP.restart();
+    }
+    else if (strncmp(cmdPayload, "save", 4) == 0) {
+      saveConfig();
+      yield();
+      saveConfig2();
+      yield();
+
+      saveSetTemp();
+      saveHeatingMode();
+      
+      //Serial.println("saved config to SPIFFS");
+      sendStatus("saved config to SPIFFS");
+      Serial.println("reloading config to check...");
+      loadConfig();
+      yield();
+      loadConfig2();
+      yield();
+    }
+    else if (strncmp(cmdPayload, "getconf", 7) == 0) {
+      printConfig();
+      printConfig2();
+    }
+    else if (strncmp(cmdPayload, "delconf", 7) == 0) {
+      deleteConfig();
+    }
+
+
+    cmdInQueue = false;
+  }
+}

+ 976 - 901
src/WiFiThermostat/config.ino

@@ -1,902 +1,977 @@
-
-bool setConfig(char* param, char* value) {
-  // sets the corresponding config variable for 'param' to new value
-  // does not trigger saving to flash
-  // does not distinguish between config and config2 as this is only split on flash and web-interface
-
-  if (debug) {
-    Serial.print("setConfig - '");
-    Serial.print(param);
-    Serial.print("' to '");
-    Serial.print(value);
-    Serial.println("'");
-  }
-
-  // values
-  if ( strcmp(param, "temp") == 0 ) {
-    float valueFloat = round(atof(value) * 2.0) / 2.0;
-#ifdef DEBUG_VERBOSE
-    Serial.print(valueFloat);
-#endif
-    setTempTo(valueFloat);
-  }
-  else if ( strcmp(param, "tempLow") == 0 ) {
-    float valueFloat = round(atof(value) * 2.0) / 2.0;
-#ifdef DEBUG_VERBOSE
-    Serial.print(valueFloat);
-#endif
-    setTempLowTo(valueFloat);
-  }
-  else if ( strcmp(param, "tempLow2") == 0 ) {
-    float valueFloat = round(atof(value) * 2.0) / 2.0;
-#ifdef DEBUG_VERBOSE
-    Serial.print(valueFloat);
-#endif
-    setTempLow2To(valueFloat);
-  }
-  else if ( strcmp(param, "mode") == 0 ) {
-    int val = atoi(value);
-    if (val >= 0 && val <= 3) {
-      setHeatingmodeTo(val);
-    }
-  }
-
-  //confdata
-  else if ( strcmp(param, "devName") == 0 ) {
-    strlcpy(deviceName, value, 31);
-  }
-  else if ( strcmp(param, "httpUser") == 0 ) {
-    strlcpy(http_user, value, 31);
-  }
-  else if ( strcmp(param, "httpPass") == 0 ) {
-    strlcpy(http_pass, value, 31);
-  }
-  else if ( strcmp(param, "httpToken") == 0 ) {
-    strlcpy(http_token, value, 31);
-  }
-  else if ( strcmp(param, "mqttHost") == 0 ) {
-    strlcpy(mqtt_server, value, 41);
-  }
-  else if ( strcmp(param, "mqttPort") == 0 ) {
-    mqtt_port = atoi(value);
-  }
-  else if ( strcmp(param, "mqttUser") == 0 ) {
-    strlcpy(mqtt_user, value, 31);
-  }
-  else if ( strcmp(param, "mqttPass") == 0 ) {
-    strlcpy(mqtt_pass, value, 31);
-  }
-  else if ( strcmp(param, "inTop") == 0 ) {
-    strlcpy(mqtt_topic_in, value, 51);
-  }
-  else if ( strcmp(param, "outTop") == 0 ) {
-    strlcpy(mqtt_topic_out, value, 51);
-  }
-  else if ( strcmp(param, "outRet") == 0 ) {
-    if (atoi(value) == 1) mqtt_outRetain = true;
-    else mqtt_outRetain = false;
-  }
-  else if ( strcmp(param, "willTop") == 0 ) {
-    strlcpy(mqtt_willTopic, value, 51);
-  }
-  else if ( strcmp(param, "willQos") == 0 ) {
-    int tmpval = atoi(value);
-    if (tmpval >= 0 && tmpval <= 2) mqtt_willQos = tmpval;
-  }
-  else if ( strcmp(param, "willRet") == 0 ) {
-    if (atoi(value) == 1) mqtt_willRetain = true;
-    else mqtt_willRetain = false;
-  }
-  else if ( strcmp(param, "willMsg") == 0 ) {
-    strlcpy(mqtt_willMsg, value, 31);
-  }
-  else if ( strcmp(param, "connMsg") == 0 ) {
-    strlcpy(mqtt_connMsg, value, 31);
-  }
-  else if ( strcmp(param, "domOutTop") == 0 ) {
-    strlcpy(domoticz_out_topic, value, 51);
-  }
-
-  //confdata2
-  else if ( strcmp(param, "domIdxTherm") == 0 ) {
-    domoticzIdx_Thermostat = atoi(value);
-  }
-  else if ( strcmp(param, "domIdxMode") == 0 ) {
-    domoticzIdx_ThermostatMode = atoi(value);
-  }
-  else if ( strcmp(param, "domIdxTempHum") == 0 ) {
-    domoticzIdx_TempHumSensor = atoi(value);
-  }
-  else if ( strcmp(param, "domIdxHeating") == 0 ) {
-    domoticzIdx_Heating = atoi(value);
-  }
-  else if ( strcmp(param, "domIdxPIR") == 0 ) {
-    domoticzIdx_PIR = atoi(value);
-  }
-  else if ( strcmp(param, "outTempTop") == 0 ) {
-    strlcpy(outTemp_topic_in, value, 51);
-  }
-  else if ( strcmp(param, "outHumTop") == 0 ) {
-    strlcpy(outHum_topic_in, value, 51);
-  }
-  else if ( strcmp(param, "PIRTop") == 0 ) {
-    strlcpy(mqtt_topic_pir, value, 51);
-  }
-  else if ( strcmp(param, "autoSaveTemp") == 0 ) {
-    if (atoi(value) == 1) autoSaveSetTemp = true;
-    else autoSaveSetTemp = false;
-  }
-  else if ( strcmp(param, "autoSaveMode") == 0 ) {
-    if (atoi(value) == 1) autoSaveHeatingMode = true;
-    else autoSaveHeatingMode = false;
-  }
-  else if ( strcmp(param, "minOffTime") == 0 ) {
-    heatingMinOffTime = atoi(value);
-  }
-  else if ( strcmp(param, "tempMin") == 0 ) {
-    float valueFloat = round(atof(value) * 2.0) / 2.0;
-#ifdef DEBUG_VERBOSE
-    Serial.print(valueFloat);
-#endif
-    if (valueFloat >= 10 && valueFloat <= 16) {
-      setTempMin = valueFloat;
-    }
-  }
-  else if ( strcmp(param, "tempMax") == 0 ) {
-    float valueFloat = round(atof(value) * 2.0) / 2.0;
-#ifdef DEBUG_VERBOSE
-    Serial.print(valueFloat);
-#endif
-    if (valueFloat >= 18 && valueFloat <= 30) {
-      setTempMax = valueFloat;
-    }
-  }
-  else if ( strcmp(param, "tempDec") == 0 ) {
-    float valueFloat = atof(value);
-#ifdef DEBUG_VERBOSE
-    Serial.print(valueFloat);
-#endif
-    if (valueFloat >= 0.0 && valueFloat <= 1.5) {
-      setTempDecreaseVal = valueFloat;
-    }
-  }
-  else if ( strcmp(param, "hyst") == 0 ) {
-    float valueFloat = atof(value);
-#ifdef DEBUG_VERBOSE
-    Serial.print(valueFloat);
-#endif
-    if (valueFloat >= 0.1 && valueFloat <= 4.0) {
-      hysteresis = valueFloat;
-    }
-  }
-  else if ( strcmp(param, "tempCorr") == 0 ) {
-    float valueFloat = atof(value);
-    if (valueFloat >= -5.0 && valueFloat <= 5.0) {
-      tempCorrVal = valueFloat;
-    }
-  }
-  else if ( strcmp(param, "humCorr") == 0 ) {
-    int valueInt = atoi(value);
-    if (valueInt >= -40 && valueInt <= 40) {
-      humCorrVal = valueInt;
-    }
-  }
-  else if ( strcmp(param, "measInt") == 0 ) {
-    int valueInt = atoi(value);
-    if (valueInt >= 5 && valueInt <= 120) {
-      measureInterval = valueInt;
-    }
-  }
-  else if ( strcmp(param, "dispInt") == 0 ) {
-    int valueInt = atoi(value);
-    if (valueInt >= 2 && valueInt <= 120) {
-      displayInterval = valueInt;
-    }
-  }
-  else if ( strcmp(param, "dispTout") == 0 ) {
-    int valueInt = atoi(value);
-    if (valueInt >= 2 && valueInt <= 1200) {
-      displayTimeout = valueInt;
-    }
-  }
-  else if ( strcmp(param, "modename0") == 0 ) {
-    strlcpy(modename0, value, 14);
-  }
-  else if ( strcmp(param, "modename1") == 0 ) {
-    strlcpy(modename1, value, 14);
-  }
-  else if ( strcmp(param, "modename2") == 0 ) {
-    strlcpy(modename2, value, 14);
-  }
-  else if ( strcmp(param, "modename3") == 0 ) {
-    strlcpy(modename3, value, 14);
-  }
-  else if ( strcmp(param, "PIRenDisp") == 0 ) {
-    int valueInt = atoi(value);
-    if (valueInt == 1) PIR_enablesDisplay = true;
-    else PIR_enablesDisplay = false;
-  }
-}
-
-void getConfig(char* param) {
-  // gets and prints the corresponding config variable for 'param'
-
-  if (debug) {
-    Serial.print("getConfig - '");
-    Serial.print(param);
-    Serial.println("'");
-  }
-
-  char buf[101];
-
-  // values
-  if ( strcmp(param, "temp") == 0 ) {
-    char buf2[11];
-    dtostrf(setTemp, 2, 1, buf2);
-    sprintf(buf, "setTemp: '%s'", buf2);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "tempLow") == 0 ) {
-    char buf2[11];
-    dtostrf(setTempLow, 2, 1, buf2);
-    sprintf(buf, "setTempLow: '%s'", buf2);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "tempLow2") == 0 ) {
-    char buf2[11];
-    dtostrf(setTempLow2, 2, 1, buf2);
-    sprintf(buf, "setTempLow2: '%s'", buf2);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "mode") == 0 ) {
-    sprintf(buf, "heatingMode: '%d'", heatingMode);
-    sendStatus(buf);
-  }
-
-  //confdata
-  else if ( strcmp(param, "devName") == 0 ) {
-    sprintf(buf, "devName: '%s'", deviceName);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "httpUser") == 0 ) {
-    sprintf(buf, "httpUser: '%s'", http_user);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "httpPass") == 0 ) {
-    sprintf(buf, "httpPass: '%s'", http_pass);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "httpToken") == 0 ) {
-    sprintf(buf, "httpToken: '%s'", http_token);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "mqttHost") == 0 ) {
-    sprintf(buf, "mqttHost: '%s'", mqtt_server);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "mqttPort") == 0 ) {
-    sprintf(buf, "mqttPort: '%s'", mqtt_port);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "mqttUser") == 0 ) {
-    sprintf(buf, "mqttUser: '%s'", mqtt_user);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "mqttPass") == 0 ) {
-    sprintf(buf, "mqttPass: '%s'", mqtt_pass);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "inTop") == 0 ) {
-    sprintf(buf, "inTop: '%s'", mqtt_topic_in);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "outTop") == 0 ) {
-    sprintf(buf, "outTop: '%s'", mqtt_topic_out);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "outRet") == 0 ) {
-    char buf2[11];
-    if (mqtt_outRetain) strcpy(buf2, "1");
-    else strcpy(buf2, "0");
-    sprintf(buf, "outRet: '%s'", buf2);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "willTop") == 0 ) {
-    sprintf(buf, "willTop: '%s'", mqtt_willTopic);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "willQos") == 0 ) {
-    sprintf(buf, "willQos: '%d'", mqtt_willQos);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "willRet") == 0 ) {
-    char buf2[11];
-    if (mqtt_willRetain) strcpy(buf2, "1");
-    else strcpy(buf2, "0");
-    sprintf(buf, "willRet: '%s'", buf2);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "willMsg") == 0 ) {
-    sprintf(buf, "willMsg: '%s'", mqtt_willMsg);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "connMsg") == 0 ) {
-    sprintf(buf, "connMsg: '%s'", mqtt_connMsg);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "domOutTop") == 0 ) {
-    sprintf(buf, "domOutTop: '%s'", domoticz_out_topic);
-    sendStatus(buf);
-  }
-
-  //confdata2
-  else if ( strcmp(param, "domIdxTherm") == 0 ) {
-    sprintf(buf, "domIdxTherm: '%d'", domoticzIdx_Thermostat);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "domIdxMode") == 0 ) {
-    sprintf(buf, "domIdxMode: '%d'", domoticzIdx_ThermostatMode);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "domIdxTempHum") == 0 ) {
-    sprintf(buf, "domIdxTempHum: '%d'", domoticzIdx_TempHumSensor);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "domIdxHeating") == 0 ) {
-    sprintf(buf, "domIdxHeating: '%d'", domoticzIdx_Heating);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "domIdxPIR") == 0 ) {
-    sprintf(buf, "domIdxPIR: '%d'", domoticzIdx_PIR);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "outTempTop") == 0 ) {
-    sprintf(buf, "outTempTop: '%s'", outTemp_topic_in);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "outHumTop") == 0 ) {
-    sprintf(buf, "outHumTop: '%s'", outHum_topic_in);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "PIRTop") == 0 ) {
-    sprintf(buf, "PIRTop: '%s'", mqtt_topic_pir);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "autoSaveTemp") == 0 ) {
-    char buf2[11];
-    if (autoSaveSetTemp) strcpy(buf2, "1");
-    else strcpy(buf2, "0");
-    sprintf(buf, "autoSaveTemp: '%s'", buf2);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "autoSaveMode") == 0 ) {
-    char buf2[11];
-    if (autoSaveHeatingMode) strcpy(buf2, "1");
-    else strcpy(buf2, "0");
-    sprintf(buf, "autoSaveMode: '%s'", buf2);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "minOffTime") == 0 ) {
-    sprintf(buf, "minOffTime: '%d'", heatingMinOffTime);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "tempMin") == 0 ) {
-    char buf2[11];
-    dtostrf(setTempMin, 2, 1, buf2);
-    sprintf(buf, "tempMin: '%s'", buf2);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "tempMax") == 0 ) {
-    char buf2[11];
-    dtostrf(setTempMax, 2, 1, buf2);
-    sprintf(buf, "tempMax: '%s'", buf2);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "tempDec") == 0 ) {
-    char buf2[11];
-    dtostrf(setTempDecreaseVal, 2, 1, buf2);
-    sprintf(buf, "tempDec: '%s'", buf2);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "hyst") == 0 ) {
-    char buf2[11];
-    dtostrf(hysteresis, 2, 1, buf2);
-    sprintf(buf, "hyst: '%s'", buf2);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "tempCorr") == 0 ) {
-    char buf2[11];
-    dtostrf(tempCorrVal, 2, 1, buf2);
-    sprintf(buf, "tempCorr: '%s'", buf2);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "humCorr") == 0 ) {
-    sprintf(buf, "humCorr: '%d'", humCorrVal);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "measInt") == 0 ) {
-    sprintf(buf, "measInt: '%d'", measureInterval);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "dispInt") == 0 ) {
-    sprintf(buf, "dispInt: '%d'", displayInterval);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "dispTout") == 0 ) {
-    sprintf(buf, "dispTout: '%d'", displayTimeout);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "modename0") == 0 ) {
-    sprintf(buf, "modename0: '%s'", modename0);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "modename1") == 0 ) {
-    sprintf(buf, "modename1: '%s'", modename1);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "modename2") == 0 ) {
-    sprintf(buf, "modename2: '%s'", modename2);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "modename3") == 0 ) {
-    sprintf(buf, "modename3: '%s'", modename3);
-    sendStatus(buf);
-  }
-  else if ( strcmp(param, "PIRenDisp") == 0 ) {
-    char buf2[11];
-    if (PIR_enablesDisplay) strcpy(buf2, "1");
-    else strcpy(buf2, "0");
-    sprintf(buf, "pirEnDisp: '%d'", PIR_enablesDisplay);
-    sendStatus(buf);
-  }
-}
-
-void printConfig() {
-  // prints current config vars to serial
-  Serial.println("\nconfdata:");
-  Serial.print("devName: ");
-  Serial.println(deviceName);
-  Serial.print("httpUser: ");
-  Serial.println(http_user);
-  Serial.print("httpPass: ");
-  Serial.println(http_pass);
-  Serial.print("httpToken: ");
-  Serial.println(http_token);
-  Serial.print("mqttHost: ");
-  Serial.println(mqtt_server);
-  Serial.print("mqttPort: ");
-  Serial.println(mqtt_port);
-  Serial.print("mqttUser: ");
-  Serial.println(mqtt_user);
-  Serial.print("mqttPass: ");
-  Serial.println(mqtt_pass);
-  Serial.print("inTop: ");
-  Serial.println(mqtt_topic_in);
-  Serial.print("outTop: ");
-  Serial.println(mqtt_topic_out);
-  Serial.print("outRet: ");
-  Serial.println(mqtt_outRetain);
-  Serial.print("willTop: ");
-  Serial.println(mqtt_willTopic);
-  Serial.print("willQos: ");
-  Serial.println(mqtt_willQos);
-  Serial.print("willRet: ");
-  Serial.println(mqtt_willRetain);
-  Serial.print("willMsg: ");
-  Serial.println(mqtt_willMsg);
-  Serial.print("connMsg: ");
-  Serial.println(mqtt_connMsg);
-  Serial.print("domOutTop: ");
-  Serial.println(domoticz_out_topic);
-  Serial.println();
-}
-void printConfig2() {
-  Serial.println("\nconfdata2:");
-  Serial.print("domIdxTherm: ");
-  Serial.println(domoticzIdx_Thermostat);
-  Serial.print("domIdxMode: ");
-  Serial.println(domoticzIdx_ThermostatMode);
-  Serial.print("domIdxTempHum: ");
-  Serial.println(domoticzIdx_TempHumSensor);
-  Serial.print("domIdxHeating: ");
-  Serial.println(domoticzIdx_Heating);
-  Serial.print("domIdxPIR: ");
-  Serial.println(domoticzIdx_PIR);
-  Serial.print("outTempTop: ");
-  Serial.println(outTemp_topic_in);
-  Serial.print("outHumTop: ");
-  Serial.println(outHum_topic_in);
-  Serial.print("PIRTop: ");
-  Serial.println(mqtt_topic_pir);
-  Serial.print("autoSaveTemp: ");
-  Serial.println(autoSaveSetTemp);
-  Serial.print("autoSaveMode: ");
-  Serial.println(autoSaveHeatingMode);
-  Serial.print("minOffTime: ");
-  Serial.println(heatingMinOffTime);
-  Serial.print("tempMin: ");
-  Serial.println(setTempMin);
-  Serial.print("tempMax: ");
-  Serial.println(setTempMax);
-  Serial.print("tempLow: ");
-  Serial.print(setTempLow);
-  Serial.print("tempLow2: ");
-  Serial.print(setTempLow2);
-  Serial.print("tempDec: ");
-  Serial.println(setTempDecreaseVal);
-  Serial.print("hyst: ");
-  Serial.println(hysteresis);
-  Serial.print("tempCorr: ");
-  Serial.println(tempCorrVal);
-  Serial.print("humCorr: ");
-  Serial.println(humCorrVal);
-  Serial.print("measInt: ");
-  Serial.println(measureInterval);
-  Serial.print("dispInt: ");
-  Serial.println(displayInterval);
-  Serial.print("dispTout: ");
-  Serial.println(displayTimeout);
-  Serial.print("modename0: ");
-  Serial.println(modename0);
-  Serial.print("modename1: ");
-  Serial.println(modename1);
-  Serial.print("modename2: ");
-  Serial.println(modename2);
-  Serial.print("modename3: ");
-  Serial.println(modename3);
-  Serial.print("PIRenDisp: ");
-  Serial.println(PIR_enablesDisplay);
-  Serial.println();
-}
-
-
-bool loadConfig() { // loadConfig 1
-  if (SPIFFS.exists("/conf.json")) {
-    File configFile = SPIFFS.open("/conf.json", "r");
-    if (!configFile) {
-      Serial.println("ERR: Failed to open file /conf.json");
-      return false;
-    }
-    else {
-      Serial.println("file /conf.json opened");
-      size_t size = configFile.size();
-
-      Serial.print("file size: ");
-      Serial.println(size);
-
-      //      Serial.println("file content:");
-      //
-      //      while (configFile.available()) {
-      //        //Lets read line by line from the file
-      //        String line = configFile.readStringUntil('\n');
-      //        Serial.println(line);
-      //      }
-      //      Serial.println();
-
-      if (size > 1000) {
-        Serial.println("Config file size is too large");
-        return false;
-      }
-
-      Serial.println("allocate buffer");
-      // Allocate a buffer to store contents of the file.
-      std::unique_ptr<char[]> buf(new char[size]);
-
-      Serial.println("read file bytes to buffer");
-      // We don't use String here because ArduinoJson library requires the input
-      // buffer to be mutable. If you don't use ArduinoJson, you may as well
-      // use configFile.readString instead.
-      configFile.readBytes(buf.get(), size);
-
-      StaticJsonBuffer<1050> jsonBuffer;
-      JsonObject& json = jsonBuffer.parseObject(buf.get());
-
-      if (!json.success()) {
-        Serial.println("Failed to parse config file");
-        return false;
-      }
-
-      strlcpy(deviceName, json["devName"] | "", 31);
-      strlcpy(http_user, json["httpUser"] | "", 31);
-      strlcpy(http_pass, json["httpPass"] | "", 31);
-      strlcpy(http_token, json["httpToken"] | "", 31);
-      strlcpy(mqtt_server, json["mqttHost"] | "", 41);
-      mqtt_port = atoi(json["mqttPort"] | "");
-      strlcpy(mqtt_user, json["mqttUser"] | "", 31);
-      strlcpy(mqtt_pass, json["mqttPass"] | "", 31);
-      strlcpy(mqtt_topic_in, json["inTop"] | "", 51);
-      strlcpy(mqtt_topic_out, json["outTop"] | "", 51);
-
-      if (atoi(json["outRet"] | "") == 1) mqtt_outRetain = true;
-      else mqtt_outRetain = false;
-
-      strlcpy(mqtt_willTopic, json["willTop"] | "", 51);
-      mqtt_willQos = atoi(json["willQos"] | "0");
-
-      if (atoi(json["willRet"] | "") == 1) mqtt_willRetain = true;
-      else mqtt_willRetain = false;
-
-      strlcpy(mqtt_willMsg, json["willMsg"] | "", 31);
-      strlcpy(mqtt_connMsg, json["connMsg"] | "", 31);
-      strlcpy(domoticz_out_topic, json["domOutTop"] | "", 51);
-
-      Serial.println("Loaded config values:");
-      printConfig();
-      return true;
-    }
-    configFile.close();
-  }
-  else {
-    Serial.println("file /config.json file does not exist");
-    return false;
-  }
-
-
-} // loadConfig 1
-
-bool loadConfig2() {
-  if (SPIFFS.exists("/conf2.json")) {
-    File configFile = SPIFFS.open("/conf2.json", "r");
-    if (!configFile) {
-      Serial.println("ERR: Failed to open file /conf2.json");
-      return false;
-    }
-    else {
-      Serial.println("file /conf2.json opened");
-      size_t size = configFile.size();
-      Serial.print("file size: ");
-      Serial.println(size);
-      if (size > 1000) {
-        Serial.println("Config file size is too large");
-        return false;
-      }
-
-      // Allocate a buffer to store contents of the file.
-      std::unique_ptr<char[]> buf(new char[size]);
-
-      // We don't use String here because ArduinoJson library requires the input
-      // buffer to be mutable. If you don't use ArduinoJson, you may as well
-      // use configFile.readString instead.
-      configFile.readBytes(buf.get(), size);
-
-      StaticJsonBuffer<1050> jsonBuffer;
-      JsonObject& json = jsonBuffer.parseObject(buf.get());
-
-      if (!json.success()) {
-        Serial.println("Failed to parse config file");
-        return false;
-      }
-
-      domoticzIdx_Thermostat = atoi(json["domIdxTherm"] | "");
-      domoticzIdx_ThermostatMode = atoi(json["domIdxMode"] | "");
-      domoticzIdx_TempHumSensor = atoi(json["domIdxTempHum"] | "");
-      domoticzIdx_Heating = atoi(json["domIdxHeating"] | "");
-
-      domoticzIdx_PIR = atoi(json["domIdxPIR"] | "");
-      strlcpy(outTemp_topic_in, json["outTempTop"] | "", 51);
-      strlcpy(outHum_topic_in, json["outHumTop"] | "", 51);
-      strlcpy(mqtt_topic_pir, json["PIRTop"] | "", 51);
-
-      if (atoi(json["autoSaveTemp"] | "") == 1) autoSaveSetTemp = true;
-      else autoSaveSetTemp = false;
-
-      if (atoi(json["autoSaveMode"] | "") == 1) autoSaveHeatingMode = true;
-      else autoSaveHeatingMode = false;
-
-      heatingMinOffTime = atoi(json["minOffTime"] | "");
-      setTempMin = atof(json["tempMin"] | "");
-      setTempMax = atof(json["tempMax"] | "");
-      setTempLow = atof(json["tempLow"] | "");
-      setTempLow2 = atof(json["tempLow2"] | "");
-      setTempDecreaseVal = atof(json["tempDec"] | "");
-      hysteresis = atof(json["hyst"] | "");
-      tempCorrVal = atof(json["tempCorr"] | "");
-      humCorrVal = atoi(json["humCorr"] | "");
-      measureInterval = atoi(json["measInt"] | "");
-      displayInterval = atoi(json["dispInt"] | "");
-      displayTimeout = atoi(json["dispTout"] | "");
-      strlcpy(modename0, json["modename0"] | "", 14);
-      strlcpy(modename1, json["modename1"] | "", 14);
-      strlcpy(modename2, json["modename2"] | "", 14);
-      strlcpy(modename3, json["modename3"] | "", 14);
-
-      if (atoi(json["PIRenDisp"] | "") == 1) PIR_enablesDisplay = true;
-      else PIR_enablesDisplay = false;
-
-      Serial.println("Loaded config values:");
-      printConfig2();
-      return true;
-    }
-  }
-  else {
-    Serial.println("file /conf2.json file does not exist");
-    return false;
-  }
-} //loadConfig2
-
-
-bool loadSetTemp() { // loadSetTemp
-  File configFile = SPIFFS.open("/setTemp", "r");
-  if (!configFile) {
-    Serial.println("ERR: Failed to open file /setTemp");
-    return false;
-  }
-  String s = configFile.readStringUntil('\n');
-  configFile.close();
-  float tmpSetTemp = s.toFloat();
-  if ( tmpSetTemp >= setTempMin && tmpSetTemp <= setTempMax ) {
-    setTemp = tmpSetTemp;
-    return true;
-  }
-  else return false;
-} // loadSetTemp
-
-
-bool loadHeatingMode() { // loadHeatingMode
-  File configFile = SPIFFS.open("/heatingMode", "r");
-  if (!configFile) {
-    Serial.println("ERR: Failed to open file /heatingMode");
-    return false;
-  }
-  String s = configFile.readStringUntil('\n');
-  configFile.close();
-  int tmpHeatingMode = s.toInt();
-  if ( tmpHeatingMode >= 0 && tmpHeatingMode <= 2 ) {
-    heatingMode = tmpHeatingMode;
-    return true;
-  }
-  else return false;
-} // loadHeatingMode
-
-
-bool saveConfig() { // safeConfig
-  StaticJsonBuffer<1050> jsonBuffer;
-  JsonObject& json = jsonBuffer.createObject();
-
-  json["devName"] = deviceName;
-  json["httpUser"] = http_user;
-  json["httpPass"] = http_pass;
-  json["httpToken"] = http_token;
-  json["mqttHost"] = mqtt_server;
-  json["mqttPort"] = mqtt_port;
-  json["mqttUser"] = mqtt_user;
-  json["mqttPass"] = mqtt_pass;
-  json["inTop"] = mqtt_topic_in;
-  json["outTop"] = mqtt_topic_out;
-
-  if (mqtt_outRetain) json["outRet"] = 1;
-  else json["outRet"] = 0;
-
-  json["willTop"] = mqtt_willTopic;
-  json["willQos"] = mqtt_willQos;
-
-  if (mqtt_willRetain) json["willRet"] = 1;
-  else json["willRet"] = 0;
-
-  json["willMsg"] = mqtt_willMsg;
-  json["connMsg"] = mqtt_connMsg;
-  json["domOutTop"] = domoticz_out_topic;
-
-  yield();
-
-  File configFile = SPIFFS.open("/conf.json", "w");
-  if (!configFile) {
-    Serial.println("Failed to open conf file for writing");
-    return false;
-  }
-
-  json.printTo(configFile);
-  configFile.close();
-  return true;
-} // safeConfig
-
-
-bool saveConfig2() { // safeConfig2
-  StaticJsonBuffer<1050> jsonBuffer;
-  JsonObject& json = jsonBuffer.createObject();
-
-  json["domIdxTherm"] = domoticzIdx_Thermostat;
-  json["domIdxMode"] = domoticzIdx_ThermostatMode;
-  json["domIdxTempHum"] = domoticzIdx_TempHumSensor;
-  json["domIdxHeating"] = domoticzIdx_Heating;
-  json["domIdxPIR"] = domoticzIdx_PIR;
-  json["outTempTop"] = outTemp_topic_in;
-  json["outHumTop"] = outHum_topic_in;
-  json["PIRTop"] = mqtt_topic_pir;
-
-  if (autoSaveSetTemp) json["autoSaveTemp"] = 1;
-  else json["autoSaveTemp"] = 0;
-
-  if (autoSaveHeatingMode) json["autoSaveMode"] = 1;
-  else json["autoSaveMode"] = 0;
-
-  json["minOffTime"] = heatingMinOffTime;
-  json["tempMin"] = setTempMin;
-  json["tempMax"] = setTempMax;
-  json["tempLow"] = setTempLow;
-  json["tempLow2"] = setTempLow2;
-  json["tempDec"] = setTempDecreaseVal;
-  json["hyst"] = hysteresis;
-  json["tempCorr"] = tempCorrVal;
-  json["humCorr"] = humCorrVal;
-  json["measInt"] = measureInterval;
-  json["dispInt"] = displayInterval;
-  json["dispTout"] = displayTimeout;
-
-  json["modename0"] = modename0;
-  json["modename1"] = modename1;
-  json["modename2"] = modename2;
-  json["modename3"] = modename3;
-
-  if (PIR_enablesDisplay) json["PIRenDisp"] = 1;
-  else json["PIRenDisp"] = 0;
-
-  yield();
-
-  File configFile = SPIFFS.open("/conf2.json", "w");
-  if (!configFile) {
-    Serial.println("Failed to open conf2 file for writing");
-    return false;
-  }
-
-  json.printTo(configFile);
-  configFile.close();
-  return true;
-} // safeConfig2
-
-
-bool saveSetTemp() { // saveSetTemp
-  File configFile = SPIFFS.open("/setTemp", "w");
-  if (!configFile) {
-    Serial.println("Failed to open setTemp file for writing");
-    return false;
-  }
-  configFile.println(setTemp);
-  configFile.close();
-  setTempSaved = setTemp;
-  return true;
-} // saveSetTemp
-
-bool saveHeatingMode() { // saveHeatingMode
-  File configFile = SPIFFS.open("/heatingMode", "w");
-  if (!configFile) {
-    Serial.println("Failed to open heatingMode file for writing");
-    return false;
-  }
-  configFile.println(heatingMode);
-  configFile.close();
-  heatingModeSaved = heatingMode;
-  return true;
-} // saveHeatingMode
-
-
-void checkSaveConfigTriggered() {
-  if (saveConfigToFlash) {
-    saveConfigToFlash = false;
-    saveConfig();
-  }
-  if (saveConfig2ToFlash) {
-    saveConfig2ToFlash = false;
-    saveConfig2();
-  }
-  //  if (saveSetTempToFlash) {
-  //    saveSetTempToFlash = false;
-  //    saveSetTemp();
-  //  }
-  //  if (saveHeatingModeToFlash) {
-  //    saveHeatingModeToFlash = false;
-  //    saveHeatingMode();
-  //  }
-
-}
-
-void deleteConfig() {
-  Serial.println("deleting configuration");
-  if (SPIFFS.remove("/formatComplete.txt")) {
-    Serial.println("config will be deleted after reboot");
-    delay(100);
-    ESP.restart();
-  }
-}
-
 
+boolean setConfig(char* param, char* value) {
+  // sets the corresponding config variable for 'param' to new value
+  // does not trigger saving to flash
+  // does not distinguish between config and config2 as this is only split on flash and web-interface
+
+  if (debug) {
+    Serial.print("setConfig - '");
+    Serial.print(param);
+    Serial.print("' to '");
+    Serial.print(value);
+    Serial.println("'");
+  }
+
+  // values
+  if ( strcmp(param, "temp") == 0 ) {
+    float valueFloat = round(atof(value) * 2.0) / 2.0;
+#ifdef DEBUG_VERBOSE
+    Serial.print(valueFloat);
+#endif
+    setTempTo(valueFloat);
+  }
+  else if ( strcmp(param, "tempLow") == 0 ) {
+    float valueFloat = round(atof(value) * 2.0) / 2.0;
+#ifdef DEBUG_VERBOSE
+    Serial.print(valueFloat);
+#endif
+    setTempLowTo(valueFloat);
+  }
+  else if ( strcmp(param, "tempLow2") == 0 ) {
+    float valueFloat = round(atof(value) * 2.0) / 2.0;
+#ifdef DEBUG_VERBOSE
+    Serial.print(valueFloat);
+#endif
+    setTempLow2To(valueFloat);
+  }
+  else if ( strcmp(param, "mode") == 0 ) {
+    int val = atoi(value);
+    if (val >= 0 && val <= 3) {
+      setHeatingmodeTo(val);
+    }
+  }
+
+  //confdata
+  else if ( strcmp(param, "devName") == 0 ) {
+    strlcpy(deviceName, value, 31);
+  }
+  else if ( strcmp(param, "httpUser") == 0 ) {
+    strlcpy(http_user, value, 31);
+  }
+  else if ( strcmp(param, "httpPass") == 0 ) {
+    strlcpy(http_pass, value, 31);
+  }
+  else if ( strcmp(param, "httpToken") == 0 ) {
+    strlcpy(http_token, value, 31);
+  }
+  else if ( strcmp(param, "mqttHost") == 0 ) {
+    strlcpy(mqtt_server, value, 41);
+  }
+  else if ( strcmp(param, "mqttPort") == 0 ) {
+    mqtt_port = atoi(value);
+  }
+  else if ( strcmp(param, "mqttUser") == 0 ) {
+    strlcpy(mqtt_user, value, 31);
+  }
+  else if ( strcmp(param, "mqttPass") == 0 ) {
+    strlcpy(mqtt_pass, value, 31);
+  }
+  else if ( strcmp(param, "inTop") == 0 ) {
+    strlcpy(mqtt_topic_in, value, 51);
+  }
+  else if ( strcmp(param, "outTop") == 0 ) {
+    strlcpy(mqtt_topic_out, value, 51);
+  }
+  else if ( strcmp(param, "outRet") == 0 ) {
+    if (atoi(value) == 1) mqtt_outRetain = true;
+    else mqtt_outRetain = false;
+  }
+  else if ( strcmp(param, "willTop") == 0 ) {
+    strlcpy(mqtt_willTopic, value, 51);
+  }
+  else if ( strcmp(param, "willQos") == 0 ) {
+    int tmpval = atoi(value);
+    if (tmpval >= 0 && tmpval <= 2) mqtt_willQos = tmpval;
+  }
+  else if ( strcmp(param, "willRet") == 0 ) {
+    if (atoi(value) == 1) mqtt_willRetain = true;
+    else mqtt_willRetain = false;
+  }
+  else if ( strcmp(param, "willMsg") == 0 ) {
+    strlcpy(mqtt_willMsg, value, 31);
+  }
+  else if ( strcmp(param, "connMsg") == 0 ) {
+    strlcpy(mqtt_connMsg, value, 31);
+  }
+  else if ( strcmp(param, "domOutTop") == 0 ) {
+    strlcpy(domoticz_out_topic, value, 51);
+  }
+
+  //confdata2
+  else if ( strcmp(param, "domIdxTherm") == 0 ) {
+    domoticzIdx_Thermostat = atoi(value);
+  }
+  else if ( strcmp(param, "domIdxMode") == 0 ) {
+    domoticzIdx_ThermostatMode = atoi(value);
+  }
+  else if ( strcmp(param, "domIdxTempHum") == 0 ) {
+    domoticzIdx_TempHumSensor = atoi(value);
+  }
+  else if ( strcmp(param, "domIdxHeating") == 0 ) {
+    domoticzIdx_Heating = atoi(value);
+  }
+  else if ( strcmp(param, "domIdxPIR") == 0 ) {
+    domoticzIdx_PIR = atoi(value);
+  }
+  else if ( strcmp(param, "outTempTop") == 0 ) {
+    strlcpy(outTemp_topic_in, value, 51);
+  }
+  else if ( strcmp(param, "outHumTop") == 0 ) {
+    strlcpy(outHum_topic_in, value, 51);
+  }
+  else if ( strcmp(param, "PIRTop") == 0 ) {
+    strlcpy(mqtt_topic_pir, value, 51);
+  }
+  else if ( strcmp(param, "autoSaveTemp") == 0 ) {
+    if (atoi(value) == 1) autoSaveSetTemp = true;
+    else autoSaveSetTemp = false;
+  }
+  else if ( strcmp(param, "autoSaveMode") == 0 ) {
+    if (atoi(value) == 1) autoSaveHeatingMode = true;
+    else autoSaveHeatingMode = false;
+  }
+  else if ( strcmp(param, "minOffTime") == 0 ) {
+    heatingMinOffTime = atoi(value);
+  }
+  else if ( strcmp(param, "tempMin") == 0 ) {
+    float valueFloat = round(atof(value) * 2.0) / 2.0;
+#ifdef DEBUG_VERBOSE
+    Serial.print(valueFloat);
+#endif
+    if (valueFloat >= 10 && valueFloat <= 16) {
+      setTempMin = valueFloat;
+    }
+  }
+  else if ( strcmp(param, "tempMax") == 0 ) {
+    float valueFloat = round(atof(value) * 2.0) / 2.0;
+#ifdef DEBUG_VERBOSE
+    Serial.print(valueFloat);
+#endif
+    if (valueFloat >= 18 && valueFloat <= 30) {
+      setTempMax = valueFloat;
+    }
+  }
+  else if ( strcmp(param, "tempDec") == 0 ) {
+    float valueFloat = atof(value);
+#ifdef DEBUG_VERBOSE
+    Serial.print(valueFloat);
+#endif
+    if (valueFloat >= 0.0 && valueFloat <= 1.5) {
+      setTempDecreaseVal = valueFloat;
+    }
+  }
+  else if ( strcmp(param, "hyst") == 0 ) {
+    float valueFloat = atof(value);
+#ifdef DEBUG_VERBOSE
+    Serial.print(valueFloat);
+#endif
+    if (valueFloat >= 0.1 && valueFloat <= 4.0) {
+      hysteresis = valueFloat;
+    }
+  }
+  else if ( strcmp(param, "tempCorr") == 0 ) {
+    float valueFloat = atof(value);
+    if (valueFloat >= -5.0 && valueFloat <= 5.0) {
+      tempCorrVal = valueFloat;
+    }
+  }
+  else if ( strcmp(param, "humCorr") == 0 ) {
+    int valueInt = atoi(value);
+    if (valueInt >= -40 && valueInt <= 40) {
+      humCorrVal = valueInt;
+    }
+  }
+  else if ( strcmp(param, "measInt") == 0 ) {
+    int valueInt = atoi(value);
+    if (valueInt >= 5 && valueInt <= 120) {
+      measureInterval = valueInt;
+    }
+  }
+  else if ( strcmp(param, "dispInt") == 0 ) {
+    int valueInt = atoi(value);
+    if (valueInt >= 2 && valueInt <= 120) {
+      displayInterval = valueInt;
+    }
+  }
+  else if ( strcmp(param, "dispTout") == 0 ) {
+    int valueInt = atoi(value);
+    if (valueInt >= 2 && valueInt <= 1200) {
+      displayTimeout = valueInt;
+    }
+  }
+  else if ( strcmp(param, "offMsg") == 0 ) {
+    strlcpy(offMessage, value, 14);
+  }
+  else if ( strcmp(param, "modename0") == 0 ) {
+    strlcpy(modename0, value, 14);
+  }
+  else if ( strcmp(param, "modename1") == 0 ) {
+    strlcpy(modename1, value, 14);
+  }
+  else if ( strcmp(param, "psetname0") == 0 ) {
+    strlcpy(psetname0, value, 14);
+  }
+  else if ( strcmp(param, "psetname1") == 0 ) {
+    strlcpy(psetname1, value, 14);
+  }
+  else if ( strcmp(param, "psetname2") == 0 ) {
+    strlcpy(psetname2, value, 14);
+  }
+  else if ( strcmp(param, "PIRenDisp") == 0 ) {
+    int valueInt = atoi(value);
+    if (valueInt == 1) PIR_enablesDisplay = true;
+    else PIR_enablesDisplay = false;
+  }
+  else if ( strcmp(param, "togTHdisp") == 0 ) {
+    int valueInt = atoi(value);
+    if (valueInt == 1) togglingTempHumAIDisplay = true;
+    else togglingTempHumAIDisplay = false;
+  }
+}
+
+void getConfig(char* param) {
+  // gets and prints the corresponding config variable for 'param'
+
+  if (debug) {
+    Serial.print("getConfig - '");
+    Serial.print(param);
+    Serial.println("'");
+  }
+
+  char buf[101];
+
+  // values
+  if ( strcmp(param, "temp") == 0 ) {
+    char buf2[11];
+    dtostrf(setTemp, 2, 1, buf2);
+    sprintf(buf, "setTemp: '%s'", buf2);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "tempLow") == 0 ) {
+    char buf2[11];
+    dtostrf(setTempLow, 2, 1, buf2);
+    sprintf(buf, "setTempLow: '%s'", buf2);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "tempLow2") == 0 ) {
+    char buf2[11];
+    dtostrf(setTempLow2, 2, 1, buf2);
+    sprintf(buf, "setTempLow2: '%s'", buf2);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "mode") == 0 ) {
+    sprintf(buf, "heatingMode: '%d'", heatingMode);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "preset") == 0 ) {
+    sprintf(buf, "preset: '%d'", preset);
+    sendStatus(buf);
+  }
+
+  //confdata
+  else if ( strcmp(param, "devName") == 0 ) {
+    sprintf(buf, "devName: '%s'", deviceName);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "httpUser") == 0 ) {
+    sprintf(buf, "httpUser: '%s'", http_user);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "httpPass") == 0 ) {
+    sprintf(buf, "httpPass: '%s'", http_pass);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "httpToken") == 0 ) {
+    sprintf(buf, "httpToken: '%s'", http_token);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "mqttHost") == 0 ) {
+    sprintf(buf, "mqttHost: '%s'", mqtt_server);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "mqttPort") == 0 ) {
+    sprintf(buf, "mqttPort: '%s'", mqtt_port);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "mqttUser") == 0 ) {
+    sprintf(buf, "mqttUser: '%s'", mqtt_user);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "mqttPass") == 0 ) {
+    sprintf(buf, "mqttPass: '%s'", mqtt_pass);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "inTop") == 0 ) {
+    sprintf(buf, "inTop: '%s'", mqtt_topic_in);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "outTop") == 0 ) {
+    sprintf(buf, "outTop: '%s'", mqtt_topic_out);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "outRet") == 0 ) {
+    char buf2[11];
+    if (mqtt_outRetain) strcpy(buf2, "1");
+    else strcpy(buf2, "0");
+    sprintf(buf, "outRet: '%s'", buf2);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "willTop") == 0 ) {
+    sprintf(buf, "willTop: '%s'", mqtt_willTopic);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "willQos") == 0 ) {
+    sprintf(buf, "willQos: '%d'", mqtt_willQos);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "willRet") == 0 ) {
+    char buf2[11];
+    if (mqtt_willRetain) strcpy(buf2, "1");
+    else strcpy(buf2, "0");
+    sprintf(buf, "willRet: '%s'", buf2);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "willMsg") == 0 ) {
+    sprintf(buf, "willMsg: '%s'", mqtt_willMsg);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "connMsg") == 0 ) {
+    sprintf(buf, "connMsg: '%s'", mqtt_connMsg);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "domOutTop") == 0 ) {
+    sprintf(buf, "domOutTop: '%s'", domoticz_out_topic);
+    sendStatus(buf);
+  }
+
+  //confdata2
+  else if ( strcmp(param, "domIdxTherm") == 0 ) {
+    sprintf(buf, "domIdxTherm: '%d'", domoticzIdx_Thermostat);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "domIdxMode") == 0 ) {
+    sprintf(buf, "domIdxMode: '%d'", domoticzIdx_ThermostatMode);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "domIdxTempHum") == 0 ) {
+    sprintf(buf, "domIdxTempHum: '%d'", domoticzIdx_TempHumSensor);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "domIdxHeating") == 0 ) {
+    sprintf(buf, "domIdxHeating: '%d'", domoticzIdx_Heating);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "domIdxPIR") == 0 ) {
+    sprintf(buf, "domIdxPIR: '%d'", domoticzIdx_PIR);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "outTempTop") == 0 ) {
+    sprintf(buf, "outTempTop: '%s'", outTemp_topic_in);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "outHumTop") == 0 ) {
+    sprintf(buf, "outHumTop: '%s'", outHum_topic_in);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "PIRTop") == 0 ) {
+    sprintf(buf, "PIRTop: '%s'", mqtt_topic_pir);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "autoSaveTemp") == 0 ) {
+    char buf2[11];
+    if (autoSaveSetTemp) strcpy(buf2, "1");
+    else strcpy(buf2, "0");
+    sprintf(buf, "autoSaveTemp: '%s'", buf2);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "autoSaveMode") == 0 ) {
+    char buf2[11];
+    if (autoSaveHeatingMode) strcpy(buf2, "1");
+    else strcpy(buf2, "0");
+    sprintf(buf, "autoSaveMode: '%s'", buf2);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "minOffTime") == 0 ) {
+    sprintf(buf, "minOffTime: '%d'", heatingMinOffTime);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "tempMin") == 0 ) {
+    char buf2[11];
+    dtostrf(setTempMin, 2, 1, buf2);
+    sprintf(buf, "tempMin: '%s'", buf2);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "tempMax") == 0 ) {
+    char buf2[11];
+    dtostrf(setTempMax, 2, 1, buf2);
+    sprintf(buf, "tempMax: '%s'", buf2);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "tempDec") == 0 ) {
+    char buf2[11];
+    dtostrf(setTempDecreaseVal, 2, 1, buf2);
+    sprintf(buf, "tempDec: '%s'", buf2);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "hyst") == 0 ) {
+    char buf2[11];
+    dtostrf(hysteresis, 2, 1, buf2);
+    sprintf(buf, "hyst: '%s'", buf2);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "tempCorr") == 0 ) {
+    char buf2[11];
+    dtostrf(tempCorrVal, 2, 1, buf2);
+    sprintf(buf, "tempCorr: '%s'", buf2);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "humCorr") == 0 ) {
+    sprintf(buf, "humCorr: '%d'", humCorrVal);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "measInt") == 0 ) {
+    sprintf(buf, "measInt: '%d'", measureInterval);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "dispInt") == 0 ) {
+    sprintf(buf, "dispInt: '%d'", displayInterval);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "dispTout") == 0 ) {
+    sprintf(buf, "dispTout: '%d'", displayTimeout);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "offMsg") == 0 ) {
+    sprintf(buf, "offMsg: '%s'", offMessage);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "modename0") == 0 ) {
+    sprintf(buf, "modename0: '%s'", modename0);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "modename1") == 0 ) {
+    sprintf(buf, "modename1: '%s'", modename1);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "psetname0") == 0 ) {
+    sprintf(buf, "psetname0: '%s'", psetname0);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "psetname1") == 0 ) {
+    sprintf(buf, "psetname1: '%s'", psetname1);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "psetname2") == 0 ) {
+    sprintf(buf, "psetname2: '%s'", psetname2);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "PIRenDisp") == 0 ) {
+    char buf2[11];
+    if (PIR_enablesDisplay) strcpy(buf2, "1");
+    else strcpy(buf2, "0");
+    sprintf(buf, "pirEnDisp: '%d'", PIR_enablesDisplay);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "togTHdisp") == 0 ) {
+    char buf2[11];
+    if (togglingTempHumAIDisplay) strcpy(buf2, "1");
+    else strcpy(buf2, "0");
+    sprintf(buf, "togTHdisp: '%d'", togglingTempHumAIDisplay);
+    sendStatus(buf);
+  }
+
+  
+}
+
+void printConfig() {
+  // prints current config vars to serial
+  Serial.println("\nconfdata:");
+  Serial.print("devName: ");
+  Serial.println(deviceName);
+  Serial.print("httpUser: ");
+  Serial.println(http_user);
+  Serial.print("httpPass: ");
+  Serial.println(http_pass);
+  Serial.print("httpToken: ");
+  Serial.println(http_token);
+  Serial.print("mqttHost: ");
+  Serial.println(mqtt_server);
+  Serial.print("mqttPort: ");
+  Serial.println(mqtt_port);
+  Serial.print("mqttUser: ");
+  Serial.println(mqtt_user);
+  Serial.print("mqttPass: ");
+  Serial.println(mqtt_pass);
+  Serial.print("inTop: ");
+  Serial.println(mqtt_topic_in);
+  Serial.print("outTop: ");
+  Serial.println(mqtt_topic_out);
+  Serial.print("outRet: ");
+  Serial.println(mqtt_outRetain);
+  Serial.print("willTop: ");
+  Serial.println(mqtt_willTopic);
+  Serial.print("willQos: ");
+  Serial.println(mqtt_willQos);
+  Serial.print("willRet: ");
+  Serial.println(mqtt_willRetain);
+  Serial.print("willMsg: ");
+  Serial.println(mqtt_willMsg);
+  Serial.print("connMsg: ");
+  Serial.println(mqtt_connMsg);
+  Serial.print("domOutTop: ");
+  Serial.println(domoticz_out_topic);
+  Serial.println();
+}
+void printConfig2() {
+  Serial.println("\nconfdata2:");
+  Serial.print("domIdxTherm: ");
+  Serial.println(domoticzIdx_Thermostat);
+  Serial.print("domIdxMode: ");
+  Serial.println(domoticzIdx_ThermostatMode);
+  Serial.print("domIdxTempHum: ");
+  Serial.println(domoticzIdx_TempHumSensor);
+  Serial.print("domIdxHeating: ");
+  Serial.println(domoticzIdx_Heating);
+  Serial.print("domIdxPIR: ");
+  Serial.println(domoticzIdx_PIR);
+  Serial.print("outTempTop: ");
+  Serial.println(outTemp_topic_in);
+  Serial.print("outHumTop: ");
+  Serial.println(outHum_topic_in);
+  Serial.print("PIRTop: ");
+  Serial.println(mqtt_topic_pir);
+  Serial.print("autoSaveTemp: ");
+  Serial.println(autoSaveSetTemp);
+  Serial.print("autoSaveMode: ");
+  Serial.println(autoSaveHeatingMode);
+  Serial.print("minOffTime: ");
+  Serial.println(heatingMinOffTime);
+  Serial.print("tempMin: ");
+  Serial.println(setTempMin);
+  Serial.print("tempMax: ");
+  Serial.println(setTempMax);
+  Serial.print("tempLow: ");
+  Serial.print(setTempLow);
+  Serial.print("tempLow2: ");
+  Serial.print(setTempLow2);
+  Serial.print("tempDec: ");
+  Serial.println(setTempDecreaseVal);
+  Serial.print("hyst: ");
+  Serial.println(hysteresis);
+  Serial.print("tempCorr: ");
+  Serial.println(tempCorrVal);
+  Serial.print("humCorr: ");
+  Serial.println(humCorrVal);
+  Serial.print("measInt: ");
+  Serial.println(measureInterval);
+  Serial.print("dispInt: ");
+  Serial.println(displayInterval);
+  Serial.print("dispTout: ");
+  Serial.println(displayTimeout);
+  Serial.print("modename0: ");
+  Serial.println(modename0);
+  Serial.print("modename1: ");
+  Serial.println(modename1);
+  Serial.print("psetname0: ");
+  Serial.println(psetname0);
+  Serial.print("psetname1: ");
+  Serial.println(psetname1);
+  Serial.print("psetname2: ");
+  Serial.println(psetname2);
+  Serial.print("offMsg: ");
+  Serial.println(offMessage);
+  Serial.print("PIRenDisp: ");
+  Serial.println(PIR_enablesDisplay);
+  Serial.print("togTHdisp: ");
+  Serial.println(togglingTempHumAIDisplay);
+  Serial.println();
+}
+
+
+boolean loadConfig() { // loadConfig 1
+  if (SPIFFS.exists("/conf.json")) {
+    File configFile = SPIFFS.open("/conf.json", "r");
+    if (!configFile) {
+      Serial.println("ERR: Failed to open file /conf.json");
+      return false;
+    }
+    else {
+      Serial.println("file /conf.json opened");
+      size_t size = configFile.size();
+
+      Serial.print("file size: ");
+      Serial.println(size);
+
+      //      Serial.println("file content:");
+      //
+      //      while (configFile.available()) {
+      //        //Lets read line by line from the file
+      //        String line = configFile.readStringUntil('\n');
+      //        Serial.println(line);
+      //      }
+      //      Serial.println();
+
+      if (size > 1000) {
+        Serial.println("Config file size is too large");
+        return false;
+      }
+
+      Serial.println("allocate buffer");
+      // Allocate a buffer to store contents of the file.
+      std::unique_ptr<char[]> buf(new char[size]);
+
+      Serial.println("read file bytes to buffer");
+      // We don't use String here because ArduinoJson library requires the input
+      // buffer to be mutable. If you don't use ArduinoJson, you may as well
+      // use configFile.readString instead.
+      configFile.readBytes(buf.get(), size);
+
+      StaticJsonBuffer<1050> jsonBuffer;
+      JsonObject& json = jsonBuffer.parseObject(buf.get());
+
+      if (!json.success()) {
+        Serial.println("Failed to parse config file");
+        return false;
+      }
+
+      strlcpy(deviceName, json["devName"] | "", 31);
+      strlcpy(http_user, json["httpUser"] | "", 31);
+      strlcpy(http_pass, json["httpPass"] | "", 31);
+      strlcpy(http_token, json["httpToken"] | "", 31);
+      strlcpy(mqtt_server, json["mqttHost"] | "", 41);
+      mqtt_port = atoi(json["mqttPort"] | "");
+      strlcpy(mqtt_user, json["mqttUser"] | "", 31);
+      strlcpy(mqtt_pass, json["mqttPass"] | "", 31);
+      strlcpy(mqtt_topic_in, json["inTop"] | "", 51);
+      strlcpy(mqtt_topic_out, json["outTop"] | "", 51);
+
+      if (atoi(json["outRet"] | "") == 1) mqtt_outRetain = true;
+      else mqtt_outRetain = false;
+
+      strlcpy(mqtt_willTopic, json["willTop"] | "", 51);
+      mqtt_willQos = atoi(json["willQos"] | "0");
+
+      if (atoi(json["willRet"] | "") == 1) mqtt_willRetain = true;
+      else mqtt_willRetain = false;
+
+      strlcpy(mqtt_willMsg, json["willMsg"] | "", 31);
+      strlcpy(mqtt_connMsg, json["connMsg"] | "", 31);
+      strlcpy(domoticz_out_topic, json["domOutTop"] | "", 51);
+
+      Serial.println("Loaded config values:");
+      printConfig();
+      return true;
+    }
+    configFile.close();
+  }
+  else {
+    Serial.println("file /config.json file does not exist");
+    return false;
+  }
+
+
+} // loadConfig 1
+
+boolean loadConfig2() {
+  if (SPIFFS.exists("/conf2.json")) {
+    File configFile = SPIFFS.open("/conf2.json", "r");
+    if (!configFile) {
+      Serial.println("ERR: Failed to open file /conf2.json");
+      return false;
+    }
+    else {
+      Serial.println("file /conf2.json opened");
+      size_t size = configFile.size();
+      Serial.print("file size: ");
+      Serial.println(size);
+      if (size > 1000) {
+        Serial.println("Config file size is too large");
+        return false;
+      }
+
+      // Allocate a buffer to store contents of the file.
+      std::unique_ptr<char[]> buf(new char[size]);
+
+      // We don't use String here because ArduinoJson library requires the input
+      // buffer to be mutable. If you don't use ArduinoJson, you may as well
+      // use configFile.readString instead.
+      configFile.readBytes(buf.get(), size);
+
+      StaticJsonBuffer<1050> jsonBuffer;
+      JsonObject& json = jsonBuffer.parseObject(buf.get());
+
+      if (!json.success()) {
+        Serial.println("Failed to parse config file");
+        return false;
+      }
+
+      domoticzIdx_Thermostat = atoi(json["domIdxTherm"] | "");
+      domoticzIdx_ThermostatMode = atoi(json["domIdxMode"] | "");
+      domoticzIdx_TempHumSensor = atoi(json["domIdxTempHum"] | "");
+      domoticzIdx_Heating = atoi(json["domIdxHeating"] | "");
+
+      domoticzIdx_PIR = atoi(json["domIdxPIR"] | "");
+      strlcpy(outTemp_topic_in, json["outTempTop"] | "", 51);
+      strlcpy(outHum_topic_in, json["outHumTop"] | "", 51);
+      strlcpy(mqtt_topic_pir, json["PIRTop"] | "", 51);
+
+      if (atoi(json["autoSaveTemp"] | "") == 1) autoSaveSetTemp = true;
+      else autoSaveSetTemp = false;
+
+      if (atoi(json["autoSaveMode"] | "") == 1) autoSaveHeatingMode = true;
+      else autoSaveHeatingMode = false;
+
+      heatingMinOffTime = atoi(json["minOffTime"] | "");
+      setTempMin = atof(json["tempMin"] | "");
+      setTempMax = atof(json["tempMax"] | "");
+      setTempLow = atof(json["tempLow"] | "");
+      setTempLow2 = atof(json["tempLow2"] | "");
+      setTempDecreaseVal = atof(json["tempDec"] | "");
+      hysteresis = atof(json["hyst"] | "");
+      tempCorrVal = atof(json["tempCorr"] | "");
+      humCorrVal = atoi(json["humCorr"] | "");
+      measureInterval = atoi(json["measInt"] | "");
+      displayInterval = atoi(json["dispInt"] | "");
+      displayInterval_saved = displayInterval;
+      displayTimeout = atoi(json["dispTout"] | "");
+      strlcpy(modename0, json["modename0"] | "", 14);
+      strlcpy(modename1, json["modename1"] | "", 14);
+      strlcpy(psetname0, json["psetname0"] | "", 14);
+      strlcpy(psetname1, json["psetname1"] | "", 14);
+      strlcpy(psetname2, json["psetname2"] | "", 14);
+      strlcpy(offMessage, json["offMsg"] | "", 14);
+
+      if (atoi(json["PIRenDisp"] | "") == 1) PIR_enablesDisplay = true;
+      else PIR_enablesDisplay = false;
+
+      if (atoi(json["togTHdisp"] | "") == 1) togglingTempHumAIDisplay = true;
+      else togglingTempHumAIDisplay = false;
+
+      Serial.println("Loaded config values:");
+      printConfig2();
+      return true;
+    }
+  }
+  else {
+    Serial.println("file /conf2.json file does not exist");
+    return false;
+  }
+} //loadConfig2
+
+
+boolean loadSetTemp() { // loadSetTemp
+  File configFile = SPIFFS.open("/setTemp", "r");
+  if (!configFile) {
+    Serial.println("ERR: Failed to open file /setTemp");
+    return false;
+  }
+  String s = configFile.readStringUntil('\n');
+  configFile.close();
+  float tmpSetTemp = s.toFloat();
+  if ( tmpSetTemp >= setTempMin && tmpSetTemp <= setTempMax ) {
+    setTemp = tmpSetTemp;
+    return true;
+  }
+  else return false;
+} // loadSetTemp
+
+
+boolean loadHeatingMode() { // loadHeatingMode
+  File configFile = SPIFFS.open("/heatingMode", "r");
+  if (!configFile) {
+    Serial.println("ERR: Failed to open file /heatingMode");
+    return false;
+  }
+  String s = configFile.readStringUntil('\n');
+  configFile.close();
+  int tmpHeatingMode = s.toInt();
+  if ( tmpHeatingMode >= 0 && tmpHeatingMode <= 1 ) {
+    heatingMode = tmpHeatingMode;
+    return true;
+  }
+  else return false;
+} // loadHeatingMode
+
+boolean loadPreset() { // loadPreset
+  File configFile = SPIFFS.open("/preset", "r");
+  if (!configFile) {
+    Serial.println("ERR: Failed to open file /preset");
+    return false;
+  }
+  String s = configFile.readStringUntil('\n');
+  configFile.close();
+  int tmpPreset = s.toInt();
+  if ( tmpPreset >= 0 && tmpPreset <= 2 ) {
+    preset = tmpPreset;
+    return true;
+  }
+  else return false;
+} // loadPreset
+
+boolean saveConfig() { // safeConfig
+  StaticJsonBuffer<1050> jsonBuffer;
+  JsonObject& json = jsonBuffer.createObject();
+
+  json["devName"] = deviceName;
+  json["httpUser"] = http_user;
+  json["httpPass"] = http_pass;
+  json["httpToken"] = http_token;
+  json["mqttHost"] = mqtt_server;
+  json["mqttPort"] = mqtt_port;
+  json["mqttUser"] = mqtt_user;
+  json["mqttPass"] = mqtt_pass;
+  json["inTop"] = mqtt_topic_in;
+  json["outTop"] = mqtt_topic_out;
+
+  if (mqtt_outRetain) json["outRet"] = 1;
+  else json["outRet"] = 0;
+
+  json["willTop"] = mqtt_willTopic;
+  json["willQos"] = mqtt_willQos;
+
+  if (mqtt_willRetain) json["willRet"] = 1;
+  else json["willRet"] = 0;
+
+  json["willMsg"] = mqtt_willMsg;
+  json["connMsg"] = mqtt_connMsg;
+  json["domOutTop"] = domoticz_out_topic;
+
+  yield();
+
+  File configFile = SPIFFS.open("/conf.json", "w");
+  if (!configFile) {
+    Serial.println("Failed to open conf file for writing");
+    return false;
+  }
+
+  json.printTo(configFile);
+  configFile.close();
+  return true;
+} // safeConfig
+
+
+boolean saveConfig2() { // safeConfig2
+  StaticJsonBuffer<1050> jsonBuffer;
+  JsonObject& json = jsonBuffer.createObject();
+
+  json["domIdxTherm"] = domoticzIdx_Thermostat;
+  json["domIdxMode"] = domoticzIdx_ThermostatMode;
+  json["domIdxTempHum"] = domoticzIdx_TempHumSensor;
+  json["domIdxHeating"] = domoticzIdx_Heating;
+  json["domIdxPIR"] = domoticzIdx_PIR;
+  json["outTempTop"] = outTemp_topic_in;
+  json["outHumTop"] = outHum_topic_in;
+  json["PIRTop"] = mqtt_topic_pir;
+
+  if (autoSaveSetTemp) json["autoSaveTemp"] = 1;
+  else json["autoSaveTemp"] = 0;
+
+  if (autoSaveHeatingMode) json["autoSaveMode"] = 1;
+  else json["autoSaveMode"] = 0;
+
+  json["minOffTime"] = heatingMinOffTime;
+  json["tempMin"] = setTempMin;
+  json["tempMax"] = setTempMax;
+  json["tempLow"] = setTempLow;
+  json["tempLow2"] = setTempLow2;
+  json["tempDec"] = setTempDecreaseVal;
+  json["hyst"] = hysteresis;
+  json["tempCorr"] = tempCorrVal;
+  json["humCorr"] = humCorrVal;
+  json["measInt"] = measureInterval;
+  json["dispInt"] = displayInterval;
+  json["dispTout"] = displayTimeout;
+
+  json["modename0"] = modename0;
+  json["modename1"] = modename1;
+  json["psetname0"] = psetname0;
+  json["psetname1"] = psetname1;
+  json["psetname2"] = psetname2;
+  json["offMsg"] = offMessage;
+
+  if (PIR_enablesDisplay) json["PIRenDisp"] = 1;
+  else json["PIRenDisp"] = 0;
+
+  if (togglingTempHumAIDisplay) json["togTHdisp"] = 1;
+  else json["togTHdisp"] = 0;
+
+  yield();
+
+  File configFile = SPIFFS.open("/conf2.json", "w");
+  if (!configFile) {
+    Serial.println("Failed to open conf2 file for writing");
+    return false;
+  }
+
+  json.printTo(configFile);
+  configFile.close();
+  return true;
+} // safeConfig2
+
+
+boolean saveSetTemp() { // saveSetTemp
+  File configFile = SPIFFS.open("/setTemp", "w");
+  if (!configFile) {
+    Serial.println("Failed to open setTemp file for writing");
+    return false;
+  }
+  configFile.println(setTemp);
+  configFile.close();
+  setTempSaved = setTemp;
+  return true;
+} // saveSetTemp
+
+boolean saveHeatingMode() { // saveHeatingMode
+  File configFile = SPIFFS.open("/heatingMode", "w");
+  if (!configFile) {
+    Serial.println("Failed to open heatingMode file for writing");
+    return false;
+  }
+  configFile.println(heatingMode);
+  configFile.close();
+  heatingModeSaved = heatingMode;
+  return true;
+} // saveHeatingMode
+
+
+boolean savePreset() { // savePreset
+  File configFile = SPIFFS.open("/preset", "w");
+  if (!configFile) {
+    Serial.println("Failed to open preset file for writing");
+    return false;
+  }
+  configFile.println(preset);
+  configFile.close();
+  presetSaved = preset;
+  return true;
+} // savePreset
+
+
+void checkSaveConfigTriggered() {
+  if (saveConfigToFlash) {
+    saveConfigToFlash = false;
+    saveConfig();
+  }
+  if (saveConfig2ToFlash) {
+    saveConfig2ToFlash = false;
+    saveConfig2();
+  }
+  //  if (saveSetTempToFlash) {
+  //    saveSetTempToFlash = false;
+  //    saveSetTemp();
+  //  }
+  //  if (saveHeatingModeToFlash) {
+  //    saveHeatingModeToFlash = false;
+  //    saveHeatingMode();
+  //  }
+
+}
+
+void deleteConfig() {
+  Serial.println("deleting configuration");
+  if (SPIFFS.remove("/formatComplete.txt")) {
+    Serial.println("config will be deleted after reboot");
+    delay(100);
+    ESP.restart();
+  }
+}

+ 823 - 682
src/WiFiThermostat/httpServer.ino

@@ -1,683 +1,824 @@
-//extern ESP8266WebServer httpServer;
-
-static const char httpRoot[] PROGMEM =
-  R"(<html><body>
-  <h1><span id='devname'></span></h1>
-  <h3>WiFi Thermostat</h3>
-  
-  <form id='minusBtnFrm'>
-  <input type='hidden' name='minusBtn' value='1'>
-  </form>
-  <form id='plusBtnFrm'>
-  <input type='hidden' name='plusBtn' value='1'>
-  </form>
-  <form id='modeBtnFrm'>
-  <input type='hidden' name='modeBtn' value='1'>
-  </form>
-  <form id='onoffBtnFrm'>
-  <input type='hidden' name='onoffBtn' value='1'>
-  </form>  
-  <span style="font-size:xx-large" id='setTemp'></span> &#8451;<br>
-  <span id='mode'></span><br>
- <input type='button' onclick='return sendMinusBtn()' value='-'/>
-  <input type='button' onclick='return sendPlusBtn()' value='+'/>
-  <input type='button' onclick='return sendModeBtn()' value='MODE'/>
-  <br>  
-  <br>  
-  <input id='btn_onoff' type='button' onclick='return sendOnOffBtn()' value=''/><br>
-  <br>
-  
-  Current: <span id='temp'></span> &#8451;&nbsp;&nbsp;&nbsp;<span id='hum'></span> %<br>
-  Heating <span id='heating'></span><br>
-  <br>
-  WiFi connected to <i><span id='ssid'></span></i>.<br>
-  <h6>Last update 
-  <span id='ut'></span> seconds ago. 
-  <span id='status'></span>
-  </h6>
-  <br>
-  <a href='/wifi.htm'>WiFi settings</a><br>
-  <a href='/conf'>Base configuration</a><br>
-  <a href='/conf2'>Extended configuration</a><br>
-  <a href='/update'>Firmware update</a><br>
-  <a href='/restart'>Restart</a>
-  <script>
-  function g(i) { return document.getElementById(i) };
-  var xhttp, updateTime, reqTime, reqFin;
-  var textA = 'OFF';
-  var textE = 'ON';
-  
-  function sendMinusBtn() {
-  var form = document.getElementById('minusBtnFrm');
-  return transmit(form);
-  }
-  function sendPlusBtn() {
-  var form = document.getElementById('plusBtnFrm');
-  return transmit(form);
-  }
-  function sendModeBtn() {
-  var form = document.getElementById('modeBtnFrm');
-  return transmit(form);
-  }
-  function sendOnOffBtn() {
-  var form = document.getElementById('onoffBtnFrm');
-  return transmit(form);
-  }
-  
-  function transmit(f) {
-    if (!xhttp) { 
-      g('status').innerHTML = 'loading...';
-   reqTime = 0;
-      reqFin = false;
-      xhttp = new XMLHttpRequest();
-    xhttp.timeout = 2000;
-      xhttp.open('POST', 'api');
-      xhttp.send(f ? (new FormData(f)) : '');
-      xhttp.onreadystatechange = function () {
-        if (xhttp.readyState === XMLHttpRequest.DONE && xhttp.status === 200) {
-          var data = JSON.parse(xhttp.responseText);
-      if(data.devname != undefined) g('devname').innerHTML = data.devname;
-          g('temp').innerHTML = data.temp.toFixed(1);
-          g('hum').innerHTML = data.hum;
-          g('setTemp').innerHTML = data.setTemp.toFixed(1);
-          g('ssid').innerHTML = data.ssid;
-          g('mode').innerHTML = data.modeName + ' (' + data.mode + ')';
-          
-          if(data.mode == '0') {
-        g('btn_onoff').value = textA;
-          }
-          else {
-        g('btn_onoff').value = textE;
-          }
-          
-          if(data.heating == '1') g('heating').innerHTML = 'active';
-          else if(data.heating == '0') g('heating').innerHTML = 'not active';
-
-          xhttp = null;
-          g('status').innerHTML = '';
-          updateTime = 0;
-          reqFin = true;
-        }
-    else {
-    if(!reqFin && reqTime > 10) {
-            xhttp = null;
-            reqFin = true;
-          }
-    }
-      }
-    }
-    return false;
-  }
-  transmit();
-  setInterval(function () { g('ut').innerHTML = ++updateTime; ++reqTime; }, 1000);
-  setInterval(transmit, 5000);
-</script>
-  </body></html>)";
-
-
-static const char httpConfPage[] PROGMEM =
-  R"(<html><head><body>
-<h3>Base configuration</h3>
-<a href='/'>Home</a><br><br>
-<input type='button' value='reload' onclick='return transmit()'/><br><br>
-<form id='form1' onsubmit='return transmit(this)'>
-Device Name: <input type='text' name='devName' id='devName'/><br><br>
-HTTP User *: <input type='text' name='httpUser' id='httpUser'/><br>
-HTTP Password *: <input type='text' name='httpPass' id='httpPass'/><br><br>
-HTTP set token: <input type='text' name='httpToken' id='httpToken'/><br>
-MQTT Server *: <input type='text' name='mqttHost' id='mqttHost'/><br>
-MQTT Port *: <input type='number' name='mqttPort' id='mqttPort'/><br>
-MQTT User *: <input type='text' name='mqttUser' id='mqttUser'/><br>
-MQTT Password *: <input type='text' name='mqttPass' id='mqttPass'/><br><br>
-In Topic *: <input type='text' name='inTop' id='inTop'/><br>
-Out Topic: <input type='text' name='outTop' id='outTop'/><br>
-Out Retain *: <input type='checkbox' name='outRet' id='outRet'/><br><br>
-LastWill Topic *: <input type='text' name='willTop' id='willTop'/><br>
-LastWill Qos *: <select name='willQos' id='willQos'><option>0</option><option>1</option><option>2</option></select><br>
-LastWill Retain *: <input type='checkbox' name='willRet' id='willRet'/><br>
-LastWill Message *: <input type='text' name='willMsg' id='willMsg'/><br><br>
-Connect Message *: <input type='text' name='connMsg' id='connMsg'/><br><br>
-Domoticz Out Topic *: <input type='text' name='domOutTop' id='domOutTop'/><br>
-<br>
-<input type='submit' value='Save'/>
-</form>
-<form id='restartForm' onsubmit='return res()'>
-<input type='hidden' name='restart' value='1'>
-<input type='submit' value='Restart'/>
-</form>
-
-<script>
- function g(i) { return document.getElementById(i) };
-  var xhttp, reqTime, reqFin, rxhttp;
-  
-  function res() {
-    rxhttp = new XMLHttpRequest();
- rxhttp.timeout = 1000;
-  rxhttp.open('POST', 'restart');
-  rxhttp.send('');
-  rxhttp = null;
-  return false;
-  }
-  function setCheckbox(ele, dat) {
-    if(dat == 1) {
-      ele.checked = true;
-      ele.style.visibility = 'visible';
-    }
-    else {
-      ele.checked = false;
-      ele.style.visibility = 'visible';
-    }
-  }
-  function updateCheckboxValue(ele) {
-    if (ele.checked) ele.value ='1';
-    else {
-      ele.value = '0';
-      ele.checked = true;
-      ele.style.visibility = 'hidden';
-    }
-  }
-  
-  function transmit(f) {
-    if (!xhttp) {   
-    reqTime = 0;
-    reqFin = false;
-    updateCheckboxValue(g('outRet'));
-    updateCheckboxValue(g('willRet'));    
-    xhttp = new XMLHttpRequest();
-    xhttp.timeout = 2000;
-    xhttp.open('POST', 'confdata');
-    xhttp.send(f ? (new FormData(f)) : '');
-    xhttp.onreadystatechange = function () {
-      if (xhttp.readyState === XMLHttpRequest.DONE && xhttp.status === 200) {
-        var data = JSON.parse(xhttp.responseText);
-        g('httpUser').value = data.httpUser;
-        g('httpPass').value = data.httpPass;
-        g('httpToken').value = data.httpToken;
-        g('devName').value = data.devName
-        g('mqttHost').value = data.mqttHost;
-        g('mqttPort').value = data.mqttPort;
-        g('mqttUser').value = data.mqttUser;
-        g('mqttPass').value = data.mqttPass;
-        g('inTop').value = data.inTop;
-        g('outTop').value = data.outTop;
-        g('willTop').value = data.willTop;
-        g('willQos').value = data.willQos;
-        setCheckbox(g('outRet'), data.outRet);
-        setCheckbox(g('willRet'), data.willRet);
-        g('willMsg').value = data.willMsg;
-        g('connMsg').value = data.connMsg;
-        g('domOutTop').value = data.domOutTop;
-        xhttp = null;
-        reqFin = true;
-       }
-       else {
-         if(!reqFin && reqTime > 10) {
-           xhttp = null;
-           reqFin = true;
-         }
-       }
-     }
-   }
-    return false;
-  }
-  transmit();
-  setInterval(function () { ++reqTime; }, 1000);
-</script>
-</body></html>)";
-
-
-
-static const char httpConf2Page[] PROGMEM =
-  R"(<html><head><body>
-<h3>Extended configuration</h3>
-<a href='/'>Home</a><br><br>
-<input type='button' value='reload' onclick='return transmit()'/><br>
-<form id='form1' onsubmit='return transmit(this)'>
-
-<h4>Domoticz</h4>
-Idx setTemp: <input type='number' name='domIdxTherm' id='domIdxTherm'/><br>
-Idx mode: <input type='number' name='domIdxMode' id='domIdxMode'/><br>
-Idx TempHum Sensor: <input type='number' name='domIdxTempHum' id='domIdxTempHum'/><br>
-Idx Heating: <input type='number' name='domIdxHeating' id='domIdxHeating'/><br>
-Idx PIR: <input type='number' name='domIdxPIR' id='domIdxPIR'/><br>
-
-<h4>Outside Temp/Hum via MQTT</h4>
-Temp In-Topic: <input type='text' name='outTempTop' id='outTempTop'/><br>
-Hum In-Topic: <input type='text' name='outHumTop' id='outHumTop'/><br>
-
-<h4>Additional MQTT publish topics</h4>
-PIR sensor: <input type='text' name='PIRTop' id='PIRTop'/><br>
-
-<h4>Auto-Save</h4>
-setTemp: <input type='checkbox' name='autoSaveTemp' id='autoSaveTemp'/><br>
-Mode: <input type='checkbox' name='autoSaveMode' id='autoSaveMode'/><br>
-
-<h4>Thermostat / Heating</h4>
-Min. Off-Time: <input type='number' name='minOffTime' id='minOffTime'/><br>
-Min. Temp: <input type='text' name='tempMin' id='tempMin'/><br>
-Max. Temp: <input type='text' name='tempMax' id='tempMax'/><br>
-Reduction Mode Temp: <input type='text' name='tempLow' id='tempLow'/><br>
-Reduction Mode 2 Temp: <input type='text' name='tempLow2' id='tempLow2'/><br>
-set temp decrease value: <input type='text' name='tempDec' id='tempDec'/><br>
-Hysteresis: <input type='text' name='hyst' id='hyst'/><br>
-Temp Correction.: <input type='text' name='tempCorr' id='tempCorr'/><br>
-Hum Correction: <input type='number' name='humCorr' id='humCorr'/><br>
-
-<h4>Intervals / Timeouts</h4>
-Measure Interval: <input type='number' name='measInt' id='measInt'/><br>
-Display Interval: <input type='number' name='dispInt' id='dispInt'/><br>
-Display Timeout: <input type='number' name='dispTout' id='dispTout'/><br>
-
-<h4>Names</h4>
-Mode Off: <input type='text' name='modename0' id='modename0'/><br>
-Mode Normal: <input type='text' name='modename1' id='modename1'/><br>
-Mode Reduction 1: <input type='text' name='modename2' id='modename2'/><br>
-Mode Reduction 2: <input type='text' name='modename3' id='modename3'/><br>
-
-<h4>Misc</h4>
-PIR enables Display: <input type='checkbox' name='PIRenDisp' id='PIRenDisp'/>
-
-<br><br>
-<input type='submit' value='Save'/>
-</form>
-<form id='rebootForm' onsubmit='return res()'>
-<input type='submit' value='Restart'/>
-</form>
-
-<script>
- function g(i) { return document.getElementById(i) };
-  var xhttp, reqTime, reqFin, rxhttp;
-  
-  function res() {
-    rxhttp = new XMLHttpRequest();
- rxhttp.timeout = 1000;
-  rxhttp.open('POST', 'restart');
-  rxhttp.send('');
-  rxhttp = null;
-  return false;
-  }
-  function setCheckbox(ele, dat) {
-    if(dat == 1) {
-      ele.checked = true;
-      ele.style.visibility = 'visible';
-    }
-    else {
-      ele.checked = false;
-      ele.style.visibility = 'visible';
-    }
-  }
-  function updateCheckboxValue(ele) {
-    if (ele.checked) ele.value ='1';
-    else {
-      ele.value = '0';
-      ele.checked = true;
-      ele.style.visibility = 'hidden';
-    }
-  }
-
-  function transmit(f) {
-    if (!xhttp) {
-      updateCheckboxValue(g('autoSaveTemp'));
-      updateCheckboxValue(g('autoSaveMode'));
-   updateCheckboxValue(g('PIRenDisp'));
-      xhttp = new XMLHttpRequest();
-      xhttp.timeout = 2000;
-      xhttp.open('POST', 'confdata2');
-      xhttp.send(f ? (new FormData(f)) : '');
-      xhttp.onreadystatechange = function () {
-        if (xhttp.readyState === XMLHttpRequest.DONE && xhttp.status === 200) {
-          var data = JSON.parse(xhttp.responseText);
-          setCheckbox(g('autoSaveTemp'), data.autoSaveTemp);
-          setCheckbox(g('autoSaveMode'), data.autoSaveMode);
-          setCheckbox(g('PIRenDisp'), data.PIRenDisp);
-          g('domIdxTherm').value = data.domIdxTherm;
-          g('domIdxMode').value = data.domIdxMode;
-          g('domIdxTempHum').value = data.domIdxTempHum;
-          g('domIdxHeating').value = data.domIdxHeating;
-          g('domIdxPIR').value = data.domIdxPIR;
-          g('outTempTop').value = data.outTempTop;
-          g('outHumTop').value = data.outHumTop;
-          g('PIRTop').value = data.PIRTop;
-          g('minOffTime').value = data.minOffTime;
-          g('tempMin').value = data.tempMin;
-          g('tempMax').value = data.tempMax;
-          g('tempLow').value = data.tempLow;
-          g('tempLow2').value = data.tempLow2;
-          g('tempDec').value = data.tempDec;
-          g('hyst').value = data.hyst;
-          g('tempCorr').value = data.tempCorr;
-          g('humCorr').value = data.humCorr;
-          g('measInt').value = data.measInt;
-          g('dispInt').value = data.dispInt;
-          g('dispTout').value = data.dispTout;
-          g('modename0').value = data.modename0;
-          g('modename1').value = data.modename1;
-          g('modename2').value = data.modename2;
-          g('modename3').value = data.modename3;
-          
-          xhttp = null;
-          reqFin = false;
-        }
-      else {
-          if(!reqFin && reqTime > 10) {
-            xhttp = null;
-            reqFin = true;
-          }
-       }
-    }
-    }
-    return false;
-  }
-  transmit();
-  setInterval(function () { ++reqTime; }, 1000);
-</script>
-</body></html>)";
-
-void httpServerHandleRoot() {
-  httpServer.send_P(200, "text/html", httpRoot);
-}
-
-void httpServerHandleConfPage() {
-  httpServer.send_P(200, "text/html", httpConfPage);
-}
-
-void httpServerHandleConf2Page() {
-  httpServer.send_P(200, "text/html", httpConf2Page);
-}
-
-//void httpServerHandleNotFound() {
-//  String message = "File Not Found\n\n";
-//  message += "URI: ";
-//  message += httpServer.uri();
-//  message += "\nMethod: ";
-//  message += (httpServer.method() == HTTP_GET) ? "GET" : "POST";
-//  message += "\nArguments: ";
-//  message += httpServer.args();
-//  message += "\n";
-//  for (uint8_t i = 0; i < httpServer.args(); i++) {
-//    message += " " + httpServer.argName(i) + ": " + httpServer.arg(i) + "\n";
-//  }
-//  httpServer.send(404, "text / plain", message);
-//}
-
-void httpServerHandleNotFound() {
-  //  if (strlen(http_user) > 0 && strlen(http_pass) > 0) {
-  //    if (!httpServer.authenticate(http_user, http_pass))
-  //      return httpServer.requestAuthentication();
-  httpServer.send(404, "text/plain", "");
-  //}
-}
-
-void httpServerInit() {
-  httpServer.on("/delconf", []() {
-    Serial.println("httpServer.on /delconf");
-    if (httpServer.hasArg("token")) {
-      char buf[20];
-      httpServer.arg("token").toCharArray(buf, 20);
-      if (strcmp(buf, CLEARCONF_TOKEN) == 0) {
-        //        httpServer.send(200, "text / plain", "Token OK - deleting config");
-        deleteConfig();
-      }
-    } //if
-    //    else {
-    //      httpServer.send(200, "text / plain", "not allowed");
-    //    }
-  });
-
-  httpServer.on("/api", []() {
-    DEBUG_PRINT("httpServer.on /api");
-    if (httpServer.hasArg("plusBtn")) {
-      setTempStepUp();
-      DEBUG_PRINT(P("web plusBtn"));
-    } //if
-    if (httpServer.hasArg("minusBtn")) {
-      setTempStepDown();
-      DEBUG_PRINT(P("web minusBtn"));
-    } //if
-    if (httpServer.hasArg("modeBtn")) {
-      toggleHeatingMode();
-      DEBUG_PRINT(P("web modeBtn"));
-    } //if
-    if (httpServer.hasArg("onoffBtn")) {
-      toggleThermostatOnOff();
-      DEBUG_PRINT(P("web onoffBtn"));
-    } //if
-
-    //build json object of program data
-    StaticJsonBuffer<200> jsonBuffer;
-    JsonObject &json = jsonBuffer.createObject();
-    json["devname"] = deviceName;
-    json["ssid"] = WiFi.SSID();
-    json["setTemp"] = setTemp;
-    json["temp"] = currTemp;
-    json["hum"] = int(currHum);
-    json["mode"] = heatingMode;
-    json["modeName"] = currentModeName;
-    json["heating"] = turnHeatingOn;
-
-    char jsonchar[200];
-    json.printTo(jsonchar); //print to char array, takes more memory but sends in one piece
-    httpServer.send(200, "application/json", jsonchar);
-
-  }); //httpServer.on /api
-
-  httpServer.on("/restart", []() {
-    Serial.println("web triggered restart");
-    ESP.restart();
-  });
-
-  httpServer.on("/mqttReconnect", []() {
-    Serial.println("web triggered mqttReconnect");
-    mqttReconnect();
-    httpServer.sendHeader("Location", String("/"), true);
-    httpServer.send (302, "text/plain", "OK");
-  });
-
-  httpServer.on("/setPoint", []() {
-    if (httpServer.hasArg("token")) {
-      char buf[20];
-      httpServer.arg("token").toCharArray(buf, 20);
-      if (strcmp(buf, http_token) == 0) {
-        Serial.println("web triggered setPoint");
-        if (httpServer.hasArg("value")) {
-          char bufVal[20];
-          httpServer.arg("value").toCharArray(bufVal, 20);
-          float valueFloat = round(atof(bufVal) * 2.0) / 2.0;
-          setTempTo(valueFloat);
-          httpServer.send (200, "text/plain", "OK");
-        }
-      }
-    } //if
-    else {
-      httpServer.send (403, "text/plain", "FORBIDDEN");
-    }
-  });
-
-  httpServer.on("/setMode", []() {
-    if (httpServer.hasArg("token")) {
-      char buf[20];
-      httpServer.arg("token").toCharArray(buf, 20);
-      if (strcmp(buf, http_token) == 0) {
-        Serial.println("web triggered setMode");
-        if (httpServer.hasArg("value")) {
-          char bufVal[20];
-          httpServer.arg("value").toCharArray(bufVal, 20);
-          int valueInt = atoi(bufVal);
-          if(valueInt >= 0 && valueInt <= 3) setHeatingmodeTo(valueInt);
-          httpServer.send (200, "text/plain", "OK");
-        }
-      }
-    } //if
-    else {
-      httpServer.send (403, "text/plain", "FORBIDDEN");
-    }
-  });
-
-  httpServer.on("/confdata", []() {
-    boolean sendData = false;
-    if (strlen(http_user) > 0 && strlen(http_pass) > 0) {
-      if (!httpServer.authenticate(http_user, http_pass)) return httpServer.requestAuthentication();
-      sendData = true;
-    }
-    else sendData = true;
-    if (sendData) {
-      Serial.println("httpServer.on /confdata");
-
-      for (int i = 0; i < httpServer.args(); i++) {
-        char bufName[20];
-        char bufValue[101];
-        httpServer.argName(i).toCharArray(bufName, 20);
-        httpServer.arg(i).toCharArray(bufValue, 101);
-
-        if (strlen(bufName) > 0) {
-          Serial.print("web update ");
-          Serial.print(bufName);
-          Serial.print(" = ");
-          Serial.println(bufValue);
-          setConfig(bufName, bufValue);
-        }
-        saveConfigToFlash = true; // will be saved in next loop()
-        Serial.println("web triggered saveConfigToFlash");
-      }
-      yield();
-
-      //build json object of program data
-      StaticJsonBuffer<1000> jsonBuffer;
-      JsonObject &json = jsonBuffer.createObject();
-      json["devName"] = deviceName;
-      json["httpUser"] =  http_user;
-      json["httpPass"] =  http_pass;
-      json["httpToken"] =  http_token;
-      json["mqttHost"] = mqtt_server;
-      json["mqttPort"] = mqtt_port;
-      json["mqttUser"] =  mqtt_user;
-      json["mqttPass"] =  mqtt_pass;
-      json["inTop"] = mqtt_topic_in;
-      json["outTop"] = mqtt_topic_out;
-      json["outRet"] = mqtt_outRetain;
-      json["willTop"] = mqtt_willTopic;
-      json["willQos"] = mqtt_willQos;
-      json["willRet"] = mqtt_willRetain;
-      json["willMsg"] = mqtt_willMsg;
-      json["connMsg"] = mqtt_connMsg;
-      json["domOutTop"] = domoticz_out_topic;
-
-      yield();
-
-      char jsonchar[1000];
-      json.printTo(jsonchar); //print to char array, takes more memory but sends in one piece
-      httpServer.send(200, "application/json", jsonchar);
-    }
-  }); //httpServer.on /confdata
-
-  httpServer.on("/confdata2", []() {
-    boolean sendData = false;
-    if (strlen(http_user) > 0 && strlen(http_pass) > 0) {
-      if (!httpServer.authenticate(http_user, http_pass)) return httpServer.requestAuthentication();
-      sendData = true;
-    }
-    else sendData = true;
-    if (sendData) {
-      Serial.println("httpServer.on /confdata2");
-
-      for (int i = 0; i < httpServer.args(); i++) {
-        char bufName[20];
-        char bufValue[101];
-        httpServer.argName(i).toCharArray(bufName, 20);
-        httpServer.arg(i).toCharArray(bufValue, 101);
-
-        if (strlen(bufName) > 0) {
-          Serial.print("web update ");
-          Serial.print(bufName);
-          Serial.print(" = ");
-          Serial.println(bufValue);
-          setConfig(bufName, bufValue);
-        }
-        saveConfig2ToFlash = true;
-        Serial.println("web triggered saveConfig2ToFlash");
-      }
-      yield();
-
-      //build json object of program data
-      StaticJsonBuffer<1000> jsonBuffer;
-      JsonObject &json = jsonBuffer.createObject();
-
-      json["domIdxTherm"] = domoticzIdx_Thermostat;
-      json["domIdxMode"] = domoticzIdx_ThermostatMode;
-      json["domIdxTempHum"] = domoticzIdx_TempHumSensor;
-      json["domIdxHeating"] = domoticzIdx_Heating;
-      json["domIdxPIR"] = domoticzIdx_PIR;
-      json["outTempTop"] = outTemp_topic_in;
-      json["outHumTop"] = outHum_topic_in;
-      json["PIRTop"] = mqtt_topic_pir;
-      json["autoSaveTemp"] = autoSaveSetTemp;
-      json["autoSaveMode"] = autoSaveHeatingMode;
-      json["minOffTime"] = heatingMinOffTime;
-      json["tempMin"] = setTempMin;
-      json["tempMax"] = setTempMax;
-      json["tempLow"] = setTempLow;
-      json["tempLow2"] = setTempLow2;
-      json["tempDec"] = setTempDecreaseVal;
-      json["hyst"] = hysteresis;
-      json["tempCorr"] = tempCorrVal;
-      json["humCorr"] = humCorrVal;
-      json["measInt"] = measureInterval;
-      json["dispInt"] = displayInterval;
-      json["dispTout"] = displayTimeout;
-      json["modename0"] = modename0;
-      json["modename1"] = modename1;
-      json["modename2"] = modename2;
-      json["modename3"] = modename3;
-      json["PIRenDisp"] = PIR_enablesDisplay;
-
-      yield();
-
-      char jsonchar[1000];
-      json.printTo(jsonchar); //print to char array, takes more memory but sends in one piece
-      httpServer.send(200, "application/json", jsonchar);
-    }
-  }); //httpServer.on /confdata2
-
-
-
-
-  //get heap status, analog input value and all GPIO statuses in one json call
-  httpServer.on("/info", HTTP_GET, []() {
-    boolean sendData = false;
-    if (strlen(http_user) > 0 && strlen(http_pass) > 0) {
-      if (!httpServer.authenticate(http_user, http_pass)) return httpServer.requestAuthentication();
-      sendData = true;
-    }
-    else sendData = true;
-    if (sendData) {
-      String json = " {";
-      json += "\"wifissid\":\"" + WiFi.SSID() + "\"";
-      json += "\"heap\":" + String(ESP.getFreeHeap());
-      json += "}";
-      httpServer.send(200, "text/json", json);
-      json = String();
-    }
-  }); //httpServer.on /info
-
-  httpServer.on("/", []() {
-    httpServerHandleRoot();
-  });
-
-  httpServer.on("/conf", []() {
-    httpServerHandleConfPage();
-  });
-
-  httpServer.on("/conf2", []() {
-    httpServerHandleConf2Page();
-  });
-
-  httpServer.onNotFound([]() {
-    httpServerHandleNotFound();
-  }); //httpServer.onNotFound
-
-  // HTTP Updater at /update
-  httpUpdater.setup(&httpServer);
-
-  httpServer.begin();
-}
+//extern ESP8266WebServer httpServer;
 
+static const char httpRoot[] PROGMEM =
+  R"(<html><body>
+  <h1><span id='devname'></span></h1>
+  <h3>WiFi Thermostat</h3>
+  
+  <form id='minusBtnFrm'>
+  <input type='hidden' name='minusBtn' value='1'>
+  </form>
+  <form id='plusBtnFrm'>
+  <input type='hidden' name='plusBtn' value='1'>
+  </form>
+  <form id='pset0BtnFrm'>
+  <input type='hidden' name='pset0Btn' value='1'>
+  </form>
+  <form id='pset1BtnFrm'>
+  <input type='hidden' name='pset1Btn' value='1'>
+  </form>
+  <form id='pset2BtnFrm'>
+  <input type='hidden' name='pset2Btn' value='1'>
+  </form>
+  <form id='onBtnFrm'>
+  <input type='hidden' name='onBtn' value='1'>
+  </form>
+  <form id='offBtnFrm'>
+  <input type='hidden' name='offBtn' value='1'>
+  </form>
+  <span style="font-size:xx-large" id='setTemp'></span> &#8451;<br>
+  <input type='button' onclick='return sendMinusBtn()' value='-'/>
+  <input type='button' onclick='return sendPlusBtn()' value='+'/><br>
+  Curr.setTemp: <span id='currSetTemp'></span><br><br>
+  Preset: <span id='pset'></span><br>
+  <input id='btnPs0' type='button' onclick='return sendPset0Btn()' value=''/>
+  <input id='btnPs1' type='button' onclick='return sendPset1Btn()' value=''/>
+  <input id='btnPs2' type='button' onclick='return sendPset2Btn()' value=''/>
+  <br>  
+  <br>
+  Mode: <span id='mode'></span><br>
+  <input id='btn_on' type='button' onclick='return sendOnBtn()' value='HEAT'/>
+  <input id='btn_off' type='button' onclick='return sendOffBtn()' value='OFF'/><br>
+  <br>
+  
+  Current Temp/Hum: <span id='temp'></span> &#8451;&nbsp;&nbsp;&nbsp;<span id='hum'></span> %<br><br>
+  heating currently <span id='heating'></span><br>
+  <br>
+  WiFi connected to <i><span id='ssid'></span></i>.<br>
+  <h6>Last update 
+  <span id='ut'></span> seconds ago. 
+  <span id='status'></span>
+  </h6>
+  <br>
+  <a href='/wifi.htm'>WiFi settings</a><br>
+  <a href='/conf'>Base configuration</a><br>
+  <a href='/conf2'>Extended configuration</a><br>
+  <a href='/update'>Firmware update</a><br>
+  <a href='/restart'>Restart</a>
+  <script>
+  function g(i) { return document.getElementById(i) };
+  var xhttp, updateTime, reqTime, reqFin;
+  
+  function sendMinusBtn() {
+  var form = document.getElementById('minusBtnFrm');
+  return transmit(form);
+  }
+  function sendPlusBtn() {
+  var form = document.getElementById('plusBtnFrm');
+  return transmit(form);
+  }
+  function sendPset0Btn() {
+  var form = document.getElementById('pset0BtnFrm');
+  return transmit(form);
+  }
+  function sendPset1Btn() {
+  var form = document.getElementById('pset1BtnFrm');
+  return transmit(form);
+  }
+  function sendPset2Btn() {
+  var form = document.getElementById('pset2BtnFrm');
+  return transmit(form);
+  }
+  function sendOnBtn() {
+  var form = document.getElementById('onBtnFrm');
+  return transmit(form);
+  }
+  function sendOffBtn() {
+  var form = document.getElementById('offBtnFrm');
+  return transmit(form);
+  }
+  
+  function transmit(f) {
+    if (!xhttp) { 
+      g('status').innerHTML = 'loading...';
+   reqTime = 0;
+      reqFin = false;
+      xhttp = new XMLHttpRequest();
+    xhttp.timeout = 2000;
+      xhttp.open('POST', 'api');
+      xhttp.send(f ? (new FormData(f)) : '');
+      xhttp.onreadystatechange = function () {
+        if (xhttp.readyState === XMLHttpRequest.DONE && xhttp.status === 200) {
+          var data = JSON.parse(xhttp.responseText);
+      if(data.devname != undefined) g('devname').innerHTML = data.devname;
+          g('temp').innerHTML = data.temp.toFixed(1);
+          g('hum').innerHTML = data.hum;
+          g('setTemp').innerHTML = data.setTemp.toFixed(1);
+          g('ssid').innerHTML = data.ssid;
+          g('mode').innerHTML = data.modeName;
+          g('pset').innerHTML = data.psetName;
+          g('btnPs0').value = data.psetName0;
+          g('btnPs1').value = data.psetName1;
+          g('btnPs2').value = data.psetName2;
+          g('currSetTemp').innerHTML = data.currSetTemp.toFixed(1);
+          
+          if(data.heating == '1') g('heating').innerHTML = 'ACTIVE';
+          else if(data.heating == '0') g('heating').innerHTML = 'NOT ACTIVE';
+
+          xhttp = null;
+          g('status').innerHTML = '';
+          updateTime = 0;
+          reqFin = true;
+        }
+    else {
+    if(!reqFin && reqTime > 10) {
+            xhttp = null;
+            reqFin = true;
+          }
+    }
+      }
+    }
+    return false;
+  }
+  transmit();
+  setInterval(function () { g('ut').innerHTML = ++updateTime; ++reqTime; }, 1000);
+  setInterval(transmit, 5000);
+</script>
+  </body></html>)";
+
+
+static const char httpConfPage[] PROGMEM =
+  R"(<html><head><body>
+<h3>Base configuration</h3>
+<a href='/'>Home</a><br><br>
+<input type='button' value='reload' onclick='return transmit()'/><br><br>
+<form id='form1' onsubmit='return transmit(this)'>
+Device Name: <input type='text' name='devName' id='devName'/><br><br>
+HTTP User *: <input type='text' name='httpUser' id='httpUser'/><br>
+HTTP Password *: <input type='text' name='httpPass' id='httpPass'/><br><br>
+HTTP set token: <input type='text' name='httpToken' id='httpToken'/><br>
+MQTT Server *: <input type='text' name='mqttHost' id='mqttHost'/><br>
+MQTT Port *: <input type='number' name='mqttPort' id='mqttPort'/><br>
+MQTT User *: <input type='text' name='mqttUser' id='mqttUser'/><br>
+MQTT Password *: <input type='text' name='mqttPass' id='mqttPass'/><br><br>
+In Topic *: <input type='text' name='inTop' id='inTop'/><br>
+Out Topic: <input type='text' name='outTop' id='outTop'/><br>
+Out Retain *: <input type='checkbox' name='outRet' id='outRet'/><br><br>
+LastWill Topic *: <input type='text' name='willTop' id='willTop'/><br>
+LastWill Qos *: <select name='willQos' id='willQos'><option>0</option><option>1</option><option>2</option></select><br>
+LastWill Retain *: <input type='checkbox' name='willRet' id='willRet'/><br>
+LastWill Message *: <input type='text' name='willMsg' id='willMsg'/><br><br>
+Connect Message *: <input type='text' name='connMsg' id='connMsg'/><br><br>
+Domoticz Out Topic *: <input type='text' name='domOutTop' id='domOutTop'/><br>
+<br>
+<input type='submit' value='Save'/>
+</form>
+<form id='restartForm' onsubmit='return res()'>
+<input type='hidden' name='restart' value='1'>
+<input type='submit' value='Restart'/>
+</form>
+
+<script>
+ function g(i) { return document.getElementById(i) };
+  var xhttp, reqTime, reqFin, rxhttp;
+  
+  function res() {
+    rxhttp = new XMLHttpRequest();
+ rxhttp.timeout = 1000;
+  rxhttp.open('POST', 'restart');
+  rxhttp.send('');
+  rxhttp = null;
+  return false;
+  }
+  function setCheckbox(ele, dat) {
+    if(dat == 1) {
+      ele.checked = true;
+      ele.style.visibility = 'visible';
+    }
+    else {
+      ele.checked = false;
+      ele.style.visibility = 'visible';
+    }
+  }
+  function updateCheckboxValue(ele) {
+    if (ele.checked) ele.value ='1';
+    else {
+      ele.value = '0';
+      ele.checked = true;
+      ele.style.visibility = 'hidden';
+    }
+  }
+  
+  function transmit(f) {
+    if (!xhttp) {   
+    reqTime = 0;
+    reqFin = false;
+    updateCheckboxValue(g('outRet'));
+    updateCheckboxValue(g('willRet'));    
+    xhttp = new XMLHttpRequest();
+    xhttp.timeout = 2000;
+    xhttp.open('POST', 'confdata');
+    xhttp.send(f ? (new FormData(f)) : '');
+    xhttp.onreadystatechange = function () {
+      if (xhttp.readyState === XMLHttpRequest.DONE && xhttp.status === 200) {
+        var data = JSON.parse(xhttp.responseText);
+        g('httpUser').value = data.httpUser;
+        g('httpPass').value = data.httpPass;
+        g('httpToken').value = data.httpToken;
+        g('devName').value = data.devName
+        g('mqttHost').value = data.mqttHost;
+        g('mqttPort').value = data.mqttPort;
+        g('mqttUser').value = data.mqttUser;
+        g('mqttPass').value = data.mqttPass;
+        g('inTop').value = data.inTop;
+        g('outTop').value = data.outTop;
+        g('willTop').value = data.willTop;
+        g('willQos').value = data.willQos;
+        setCheckbox(g('outRet'), data.outRet);
+        setCheckbox(g('willRet'), data.willRet);
+        g('willMsg').value = data.willMsg;
+        g('connMsg').value = data.connMsg;
+        g('domOutTop').value = data.domOutTop;
+        xhttp = null;
+        reqFin = true;
+       }
+       else {
+         if(!reqFin && reqTime > 10) {
+           xhttp = null;
+           reqFin = true;
+         }
+       }
+     }
+   }
+    return false;
+  }
+  transmit();
+  setInterval(function () { ++reqTime; }, 1000);
+</script>
+</body></html>)";
+
+
+
+static const char httpConf2Page[] PROGMEM =
+  R"(<html><head><body>
+<h3>Extended configuration</h3>
+<a href='/'>Home</a><br><br>
+<input type='button' value='reload' onclick='return transmit()'/><br>
+<form id='form1' onsubmit='return transmit(this)'>
+
+<h4>Domoticz</h4>
+Idx setTemp: <input type='number' name='domIdxTherm' id='domIdxTherm'/><br>
+Idx mode: <input type='number' name='domIdxMode' id='domIdxMode'/><br>
+Idx TempHum Sensor: <input type='number' name='domIdxTempHum' id='domIdxTempHum'/><br>
+Idx Heating: <input type='number' name='domIdxHeating' id='domIdxHeating'/><br>
+Idx PIR: <input type='number' name='domIdxPIR' id='domIdxPIR'/><br>
+
+<h4>Outside Temp/Hum via MQTT</h4>
+Temp In-Topic: <input type='text' name='outTempTop' id='outTempTop'/><br>
+Hum In-Topic: <input type='text' name='outHumTop' id='outHumTop'/><br>
+
+<h4>Additional MQTT publish topics</h4>
+PIR sensor: <input type='text' name='PIRTop' id='PIRTop'/><br>
+
+<h4>Auto-Save</h4>
+setTemp: <input type='checkbox' name='autoSaveTemp' id='autoSaveTemp'/><br>
+mode/preset: <input type='checkbox' name='autoSaveMode' id='autoSaveMode'/><br>
+
+<h4>Thermostat / Heating</h4>
+Min. Off-Time: <input type='number' name='minOffTime' id='minOffTime'/><br>
+Min. Temp: <input type='text' name='tempMin' id='tempMin'/><br>
+Max. Temp: <input type='text' name='tempMax' id='tempMax'/><br>
+Reduction Mode Temp: <input type='text' name='tempLow' id='tempLow'/><br>
+Reduction Mode 2 Temp: <input type='text' name='tempLow2' id='tempLow2'/><br>
+set temp decrease value: <input type='text' name='tempDec' id='tempDec'/><br>
+Hysteresis: <input type='text' name='hyst' id='hyst'/><br>
+Temp Correction.: <input type='text' name='tempCorr' id='tempCorr'/><br>
+Hum Correction: <input type='number' name='humCorr' id='humCorr'/><br>
+
+<h4>Intervals / Timeouts</h4>
+Measure Interval: <input type='number' name='measInt' id='measInt'/><br>
+Display Interval: <input type='number' name='dispInt' id='dispInt'/><br>
+Display Timeout: <input type='number' name='dispTout' id='dispTout'/><br>
+
+<h4>Names</h4>
+used in MQTT status/commands and HTTP interface<br>
+max. 13 chars!<br>
+Off-Message: <input type='text' name='offMsg' id='offMsg'/><br>
+<h5>Mode</h5>
+Off: <input type='text' name='modename0' id='modename0'/><br>
+On: <input type='text' name='modename1' id='modename1'/><br>
+<br>
+<h5>Presets:</h5>
+Normal*: <input type='text' name='psetname0' id='psetname0'/><br>
+Reduction 1: <input type='text' name='psetname1' id='psetname1'/><br>
+Reduction 2: <input type='text' name='psetname2' id='psetname2'/><br>
+* will always send/receive "none" as hardcoded in Home Assistant.
+
+<h4>Misc</h4>
+PIR enables Display: <input type='checkbox' name='PIRenDisp' id='PIRenDisp'/><br>
+toggling I/A-Temp and Hum display*: <input type='checkbox' name='togTHdisp' id='togTHdisp'/><br>
+* otherwise fixed I/A-Temp<br>
+<br><br>
+<input type='submit' value='Save'/>
+</form>
+<form id='rebootForm' onsubmit='return res()'>
+<input type='submit' value='Restart'/>
+</form>
+
+<script>
+ function g(i) { return document.getElementById(i) };
+  var xhttp, reqTime, reqFin, rxhttp;
+  
+  function res() {
+    rxhttp = new XMLHttpRequest();
+ rxhttp.timeout = 1000;
+  rxhttp.open('POST', 'restart');
+  rxhttp.send('');
+  rxhttp = null;
+  return false;
+  }
+  function setCheckbox(ele, dat) {
+    if(dat == 1) {
+      ele.checked = true;
+      ele.style.visibility = 'visible';
+    }
+    else {
+      ele.checked = false;
+      ele.style.visibility = 'visible';
+    }
+  }
+  function updateCheckboxValue(ele) {
+    if (ele.checked) ele.value ='1';
+    else {
+      ele.value = '0';
+      ele.checked = true;
+      ele.style.visibility = 'hidden';
+    }
+  }
+
+  function transmit(f) {
+    if (!xhttp) {
+      updateCheckboxValue(g('autoSaveTemp'));
+      updateCheckboxValue(g('autoSaveMode'));
+   updateCheckboxValue(g('PIRenDisp'));
+      xhttp = new XMLHttpRequest();
+      xhttp.timeout = 2000;
+      xhttp.open('POST', 'confdata2');
+      xhttp.send(f ? (new FormData(f)) : '');
+      xhttp.onreadystatechange = function () {
+        if (xhttp.readyState === XMLHttpRequest.DONE && xhttp.status === 200) {
+          var data = JSON.parse(xhttp.responseText);
+          setCheckbox(g('autoSaveTemp'), data.autoSaveTemp);
+          setCheckbox(g('autoSaveMode'), data.autoSaveMode);
+          setCheckbox(g('PIRenDisp'), data.PIRenDisp);
+          setCheckbox(g('togTHdisp'), data.togTHdisp);
+          g('domIdxTherm').value = data.domIdxTherm;
+          g('domIdxMode').value = data.domIdxMode;
+          g('domIdxTempHum').value = data.domIdxTempHum;
+          g('domIdxHeating').value = data.domIdxHeating;
+          g('domIdxPIR').value = data.domIdxPIR;
+          g('outTempTop').value = data.outTempTop;
+          g('outHumTop').value = data.outHumTop;
+          g('PIRTop').value = data.PIRTop;
+          g('minOffTime').value = data.minOffTime;
+          g('tempMin').value = data.tempMin;
+          g('tempMax').value = data.tempMax;
+          g('tempLow').value = data.tempLow;
+          g('tempLow2').value = data.tempLow2;
+          g('tempDec').value = data.tempDec;
+          g('hyst').value = data.hyst;
+          g('tempCorr').value = data.tempCorr;
+          g('humCorr').value = data.humCorr;
+          g('measInt').value = data.measInt;
+          g('dispInt').value = data.dispInt;
+          g('dispTout').value = data.dispTout;
+          g('offMsg').value = data.offMsg;
+          g('modename0').value = data.modename0;
+          g('modename1').value = data.modename1;
+          g('psetname0').value = data.psetname0;
+          g('psetname1').value = data.psetname1;          
+          g('psetname2').value = data.psetname2;
+          
+          xhttp = null;
+          reqFin = false;
+        }
+      else {
+          if(!reqFin && reqTime > 10) {
+            xhttp = null;
+            reqFin = true;
+          }
+       }
+    }
+    }
+    return false;
+  }
+  transmit();
+  setInterval(function () { ++reqTime; }, 1000);
+</script>
+</body></html>)";
+
+void httpServerHandleRoot() {
+  httpServer.send_P(200, "text/html", httpRoot);
+}
+
+void httpServerHandleConfPage() {
+  httpServer.send_P(200, "text/html", httpConfPage);
+}
+
+void httpServerHandleConf2Page() {
+  httpServer.send_P(200, "text/html", httpConf2Page);
+}
+
+//void httpServerHandleNotFound() {
+//  String message = "File Not Found\n\n";
+//  message += "URI: ";
+//  message += httpServer.uri();
+//  message += "\nMethod: ";
+//  message += (httpServer.method() == HTTP_GET) ? "GET" : "POST";
+//  message += "\nArguments: ";
+//  message += httpServer.args();
+//  message += "\n";
+//  for (uint8_t i = 0; i < httpServer.args(); i++) {
+//    message += " " + httpServer.argName(i) + ": " + httpServer.arg(i) + "\n";
+//  }
+//  httpServer.send(404, "text / plain", message);
+//}
+
+void httpServerHandleNotFound() {
+  //  if (strlen(http_user) > 0 && strlen(http_pass) > 0) {
+  //    if (!httpServer.authenticate(http_user, http_pass))
+  //      return httpServer.requestAuthentication();
+  httpServer.send(404, "text/plain", "");
+  //}
+}
+
+void httpServerInit() {
+  httpServer.on("/delconf", []() {
+    Serial.println("httpServer.on /delconf");
+    if (httpServer.hasArg("token")) {
+      char buf[20];
+      httpServer.arg("token").toCharArray(buf, 20);
+      if (strcmp(buf, CLEARCONF_TOKEN) == 0) {
+        //        httpServer.send(200, "text / plain", "Token OK - deleting config");
+        deleteConfig();
+      }
+    } //if
+    //    else {
+    //      httpServer.send(200, "text / plain", "not allowed");
+    //    }
+  });
+
+  httpServer.on("/api", []() {
+    boolean isAuthenticated = false;
+    if (strlen(http_user) > 0 && strlen(http_pass) > 0) {
+      if (!httpServer.authenticate(http_user, http_pass)) return httpServer.requestAuthentication();
+      isAuthenticated = true;
+    }
+    else isAuthenticated = true;
+    if (isAuthenticated) {
+      DEBUG_PRINT("httpServer.on /api");
+      if (httpServer.hasArg("plusBtn")) {
+        setTempStepUp();
+        DEBUG_PRINT(P("web plusBtn"));
+      } //if
+      if (httpServer.hasArg("minusBtn")) {
+        setTempStepDown();
+        DEBUG_PRINT(P("web minusBtn"));
+      } //if
+      if (httpServer.hasArg("pset0Btn")) {
+        setPresetTo(0);
+        DEBUG_PRINT(P("web pset0Btn"));
+      } //if
+      if (httpServer.hasArg("pset1Btn")) {
+        setPresetTo(1);
+        DEBUG_PRINT(P("web pset1Btn"));
+      } //if
+      if (httpServer.hasArg("pset2Btn")) {
+        setPresetTo(2);
+        DEBUG_PRINT(P("web pset2Btn"));
+      } //if
+      if (httpServer.hasArg("onBtn")) {
+        setHeatingmodeTo(1);
+        DEBUG_PRINT(P("web onBtn"));
+      } //if
+      if (httpServer.hasArg("offBtn")) {
+        setHeatingmodeTo(0);
+        DEBUG_PRINT(P("web offBtn"));
+      } //if
+
+      //char ch_currTemp[6];
+      //char ch_currSetTemp[6];
+      //dtostrf(currTemp, 1, 1, ch_currTemp );
+      //dtostrf(setTemp, 1, 1, ch_currSetTemp );
+
+      //build json object of program data
+      StaticJsonBuffer<400> jsonBuffer;
+      JsonObject &json = jsonBuffer.createObject();
+      json["devname"] = deviceName;
+      json["ssid"] = WiFi.SSID();
+      json["setTemp"] = setTemp;
+      json["currSetTemp"] = currSetTemp;
+      json["temp"] = currTemp;
+      json["hum"] = int(currHum);
+      json["heating"] = turnHeatingOn;
+      json["mode"] = heatingMode;
+      json["modeName"] = currentModeName;
+      json["pset"] = preset;
+      json["psetName"] = currentPresetName;
+      json["psetName0"] = psetname0;
+      json["psetName1"] = psetname1;
+      json["psetName2"] = psetname2;
+
+      char jsonchar[400];
+      json.printTo(jsonchar); //print to char array, takes more memory but sends in one piece
+      httpServer.send(200, "application/json", jsonchar);
+    }
+    else {
+      httpServer.send (401, "text/plain", "UNAUTHORIZED");
+    }
+  }); //httpServer.on /api
+
+  httpServer.on("/restart", []() {
+    if (httpServer.hasArg("token")) {
+      Serial.println("web triggered restart");
+      ESP.restart();
+    }
+    else {
+      httpServer.send (403, "text/plain", "FORBIDDEN");
+    }
+  });
+
+  httpServer.on("/mqttReconnect", []() {
+    if (httpServer.hasArg("token")) {
+      Serial.println("web triggered mqttReconnect");
+      mqttReconnect();
+      httpServer.sendHeader("Location", String("/"), true);
+      httpServer.send (302, "text/plain", "OK");
+    }
+    else {
+      httpServer.send (403, "text/plain", "FORBIDDEN");
+    }
+  });
+
+  httpServer.on("/setPoint", []() {
+    if (httpServer.hasArg("token")) {
+      char buf[20];
+      httpServer.arg("token").toCharArray(buf, 20);
+      if (strcmp(buf, http_token) == 0) {
+        Serial.println("web triggered setPoint");
+        if (httpServer.hasArg("value")) {
+          char bufVal[20];
+          httpServer.arg("value").toCharArray(bufVal, 20);
+          float valueFloat = round(atof(bufVal) * 2.0) / 2.0;
+          setTempTo(valueFloat);
+          httpServer.send (200, "text/plain", "OK");
+        }
+      }
+    } //if
+    else {
+      httpServer.send (403, "text/plain", "FORBIDDEN");
+    }
+  });
+
+  httpServer.on("/setMode", []() {
+    if (httpServer.hasArg("token")) {
+      char buf[20];
+      httpServer.arg("token").toCharArray(buf, 20);
+      if (strcmp(buf, http_token) == 0) {
+        Serial.println("web triggered setMode");
+        if (httpServer.hasArg("value")) {
+          char bufVal[20];
+          httpServer.arg("value").toCharArray(bufVal, 20);
+          int valueInt = atoi(bufVal);
+          if (valueInt >= 0 && valueInt <= 1) setHeatingmodeTo(valueInt);
+          httpServer.send (200, "text/plain", "OK");
+        }
+      }
+    } //if
+    else {
+      httpServer.send (403, "text/plain", "FORBIDDEN");
+    }
+  });
+
+  httpServer.on("/setPreset", []() {
+    if (httpServer.hasArg("token")) {
+      char buf[20];
+      httpServer.arg("token").toCharArray(buf, 20);
+      if (strcmp(buf, http_token) == 0) {
+        Serial.println("web triggered setPreset");
+        if (httpServer.hasArg("value")) {
+          char bufVal[20];
+          httpServer.arg("value").toCharArray(bufVal, 20);
+          int valueInt = atoi(bufVal);
+          if (valueInt >= 0 && valueInt <= 2) setPresetTo(valueInt);
+          httpServer.send (200, "text/plain", "OK");
+        }
+      }
+    } //if
+    else {
+      httpServer.send (403, "text/plain", "FORBIDDEN");
+    }
+  });
+
+  httpServer.on("/confdata", []() {
+    boolean isAuthenticated = false;
+    if (strlen(http_user) > 0 && strlen(http_pass) > 0) {
+      if (!httpServer.authenticate(http_user, http_pass)) return httpServer.requestAuthentication();
+      isAuthenticated = true;
+    }
+    else isAuthenticated = true;
+    if (isAuthenticated) {
+      Serial.println("httpServer.on /confdata");
+
+      for (int i = 0; i < httpServer.args(); i++) {
+        char bufName[20];
+        char bufValue[101];
+        httpServer.argName(i).toCharArray(bufName, 20);
+        httpServer.arg(i).toCharArray(bufValue, 101);
+
+        if (strlen(bufName) > 0) {
+          Serial.print("web update ");
+          Serial.print(bufName);
+          Serial.print(" = ");
+          Serial.println(bufValue);
+          setConfig(bufName, bufValue);
+        }
+        saveConfigToFlash = true; // will be saved in next loop()
+        Serial.println("web triggered saveConfigToFlash");
+      }
+      yield();
+
+      //build json object of program data
+      StaticJsonBuffer<1000> jsonBuffer;
+      JsonObject &json = jsonBuffer.createObject();
+      json["devName"] = deviceName;
+      json["httpUser"] =  http_user;
+      json["httpPass"] =  http_pass;
+      json["httpToken"] =  http_token;
+      json["mqttHost"] = mqtt_server;
+      json["mqttPort"] = mqtt_port;
+      json["mqttUser"] =  mqtt_user;
+      json["mqttPass"] =  mqtt_pass;
+      json["inTop"] = mqtt_topic_in;
+      json["outTop"] = mqtt_topic_out;
+      json["outRet"] = mqtt_outRetain;
+      json["willTop"] = mqtt_willTopic;
+      json["willQos"] = mqtt_willQos;
+      json["willRet"] = mqtt_willRetain;
+      json["willMsg"] = mqtt_willMsg;
+      json["connMsg"] = mqtt_connMsg;
+      json["domOutTop"] = domoticz_out_topic;
+
+      yield();
+
+      char jsonchar[1000];
+      json.printTo(jsonchar); //print to char array, takes more memory but sends in one piece
+      httpServer.send(200, "application/json", jsonchar);
+    }
+    else {
+      httpServer.send (401, "text/plain", "UNAUTHORIZED");
+    }
+  }); //httpServer.on /confdata
+
+  httpServer.on("/confdata2", []() {
+    boolean isAuthenticated = false;
+    if (strlen(http_user) > 0 && strlen(http_pass) > 0) {
+      if (!httpServer.authenticate(http_user, http_pass)) return httpServer.requestAuthentication();
+      isAuthenticated = true;
+    }
+    else isAuthenticated = true;
+    if (isAuthenticated) {
+      Serial.println("httpServer.on /confdata2");
+
+      for (int i = 0; i < httpServer.args(); i++) {
+        char bufName[20];
+        char bufValue[101];
+        httpServer.argName(i).toCharArray(bufName, 20);
+        httpServer.arg(i).toCharArray(bufValue, 101);
+
+        if (strlen(bufName) > 0) {
+          Serial.print("web update ");
+          Serial.print(bufName);
+          Serial.print(" = ");
+          Serial.println(bufValue);
+          setConfig(bufName, bufValue);
+        }
+        saveConfig2ToFlash = true;
+        Serial.println("web triggered saveConfig2ToFlash");
+      }
+      yield();
+
+      //build json object of program data
+      StaticJsonBuffer<1000> jsonBuffer;
+      JsonObject &json = jsonBuffer.createObject();
+
+      json["domIdxTherm"] = domoticzIdx_Thermostat;
+      json["domIdxMode"] = domoticzIdx_ThermostatMode;
+      json["domIdxTempHum"] = domoticzIdx_TempHumSensor;
+      json["domIdxHeating"] = domoticzIdx_Heating;
+      json["domIdxPIR"] = domoticzIdx_PIR;
+      json["outTempTop"] = outTemp_topic_in;
+      json["outHumTop"] = outHum_topic_in;
+      json["PIRTop"] = mqtt_topic_pir;
+      json["autoSaveTemp"] = autoSaveSetTemp;
+      json["autoSaveMode"] = autoSaveHeatingMode;
+      json["minOffTime"] = heatingMinOffTime;
+      json["tempMin"] = setTempMin;
+      json["tempMax"] = setTempMax;
+      json["tempLow"] = setTempLow;
+      json["tempLow2"] = setTempLow2;
+      json["tempDec"] = setTempDecreaseVal;
+      json["hyst"] = hysteresis;
+      json["tempCorr"] = tempCorrVal;
+      json["humCorr"] = humCorrVal;
+      json["measInt"] = measureInterval;
+      json["dispInt"] = displayInterval;
+      json["dispTout"] = displayTimeout;
+      json["offMsg"] = offMessage;
+      json["modename0"] = modename0;
+      json["modename1"] = modename1;
+      json["psetname0"] = psetname0;
+      json["psetname1"] = psetname1;
+      json["psetname2"] = psetname2;
+      json["PIRenDisp"] = PIR_enablesDisplay;
+      json["togTHdisp"] = togglingTempHumAIDisplay;
+
+      yield();
+
+      char jsonchar[1000];
+      json.printTo(jsonchar); //print to char array, takes more memory but sends in one piece
+      httpServer.send(200, "application/json", jsonchar);
+    }
+    else {
+      httpServer.send (401, "text/plain", "UNAUTHORIZED");
+    }
+  }); //httpServer.on /confdata2
+
+
+
+
+  //get heap status, analog input value and all GPIO statuses in one json call
+  httpServer.on("/info", HTTP_GET, []() {
+    boolean isAuthenticated = false;
+    if (strlen(http_user) > 0 && strlen(http_pass) > 0) {
+      if (!httpServer.authenticate(http_user, http_pass)) return httpServer.requestAuthentication();
+      isAuthenticated = true;
+    }
+    else isAuthenticated = true;
+    if (isAuthenticated) {
+      String json = " {";
+      json += "\"wifissid\":\"" + WiFi.SSID() + "\"";
+      json += "\"heap\":" + String(ESP.getFreeHeap());
+      json += "}";
+      httpServer.send(200, "text/json", json);
+      json = String();
+    }
+    else {
+      httpServer.send (401, "text/plain", "UNAUTHORIZED");
+    }
+  }); //httpServer.on /info
+
+  httpServer.on("/", []() {
+    boolean isAuthenticated = false;
+    if (strlen(http_user) > 0 && strlen(http_pass) > 0) {
+      if (!httpServer.authenticate(http_user, http_pass)) return httpServer.requestAuthentication();
+      isAuthenticated = true;
+    }
+    else isAuthenticated = true;
+    if (isAuthenticated) {
+      httpServerHandleRoot();
+    }
+    else {
+      httpServer.send (401, "text/plain", "UNAUTHORIZED");
+    }
+  });
+
+  httpServer.on("/conf", []() {
+    boolean isAuthenticated = false;
+    if (strlen(http_user) > 0 && strlen(http_pass) > 0) {
+      if (!httpServer.authenticate(http_user, http_pass)) return httpServer.requestAuthentication();
+      isAuthenticated = true;
+    }
+    else isAuthenticated = true;
+    if (isAuthenticated) {
+      httpServerHandleConfPage();
+    }
+    else {
+      httpServer.send (401, "text/plain", "UNAUTHORIZED");
+    }
+  });
+
+  httpServer.on("/conf2", []() {
+    boolean isAuthenticated = false;
+    if (strlen(http_user) > 0 && strlen(http_pass) > 0) {
+      if (!httpServer.authenticate(http_user, http_pass)) return httpServer.requestAuthentication();
+      isAuthenticated = true;
+    }
+    else isAuthenticated = true;
+    if (isAuthenticated) {
+      httpServerHandleConf2Page();
+    }
+    else {
+      httpServer.send (401, "text/plain", "UNAUTHORIZED");
+    }
+  });
+
+  httpServer.onNotFound([]() {
+    httpServerHandleNotFound();
+  }); //httpServer.onNotFound
+
+  // HTTP Updater at /update
+  httpUpdater.setup(&httpServer);
+
+  httpServer.begin();
+}

+ 63 - 62
src/WiFiThermostat/misc.ino

@@ -1,63 +1,64 @@
-unsigned long lastRun = 0;
-int count100ms = 0;
-int countSeconds = 0;
-int countMeasureInterval = 0;
-int countDisplayInterval = 0;
-
-void checkMillis() {
-  if ( (millis() - lastRun) > 100 ) {
-    lastRun = millis();
-    every100ms();
-  }
-}
-
-void every100ms() {
-  if (count100ms < 10) count100ms++;
-  else {
-    count100ms = 0;
-    everySecond();
-  }
-
-  checkSaveConfigTriggered();
-
-}
-
-void everySecond() {
-  if (countSeconds < 60) countSeconds++;
-  else {
-    countSeconds = 0;
-    everyMinute();
-  }
-
-  checkUseDomoticz();
-  handleDisplayTimeout();
-  checkValuesChanged();
-
-  if (countMeasureInterval < measureInterval) countMeasureInterval++;
-  else {
-    countMeasureInterval = 0;
-    measureTempHum();
-    thermostat();
-  }
-
-  if (countDisplayInterval < displayInterval) countDisplayInterval++;
-  else {
-    countDisplayInterval = 0;
-    updateDisplay();
-  }
-
-
-}
-
-void everyMinute() {
-  mqttPublishHeartbeat();
-  publishStatus();
-  publishCurrentSensorValues();
-  publishCurrentThermostatValues();
-  updateDomoticzDevices();
-
-//  Serial.print("WiFi Status: ");
-//  Serial.println(WiFi.status());
-}
-
 
+void checkMillis() {
+  if ( (millis() - lastRun) > 100 ) {
+    lastRun = millis();
+    every100ms();
+  }
+}
+
+void every100ms() {
+  if (count100ms < 10) count100ms++;
+  else {
+    count100ms = 0;
+    everySecond();
+  }
+
+  checkSaveConfigTriggered();
+
+}
+
+void everySecond() {
+  if (countSeconds < 60) countSeconds++;
+  else {
+    countSeconds = 0;
+    everyMinute();
+  }
+
+  checkUseDomoticz();
+  handleDisplayTimeout();
+  checkValuesChanged();
+
+  if (countMeasureInterval < measureInterval) countMeasureInterval++;
+  else {
+    countMeasureInterval = 0;
+    measureTempHum();
+    thermostat();
+  }
+
+  if (countDisplayInterval < displayInterval) countDisplayInterval++;
+  else {
+    countDisplayInterval = 0;
+    updateDisplay();
+  }
+
+  if (doRestart) ESP.restart();
+
+  if ( (millis() - pendingRestart_lastMillis) > 3000 ) pendingRestart = false;
+
+  //  if ( preset != pendingPreset) {
+  //    if ( (millis() - pendingPreset_millis) > pendingPreset_timeout ) {
+  //      preset = pendingPreset;
+  //    }
+  //  }
+}
+
+void everyMinute() {
+  mqttPublishHeartbeat();
+  publishStatus();
+  publishCurrentSensorValues();
+  publishCurrentThermostatValues();
+  updateDomoticzDevices();
+
+  //  Serial.print("WiFi Status: ");
+  //  Serial.println(WiFi.status());
+}

+ 606 - 519
src/WiFiThermostat/mqtt.ino

@@ -1,520 +1,607 @@
-
-// MQTT callback
-void mqttCallback(char* topic, byte* payload, unsigned int length) {
-  //  Serial.print("MQTT payload arrived [");
-  //  Serial.print(topic);
-  //  Serial.print("] ");
-  //  for (int i = 0; i < length; i++) {
-  //    Serial.print((char)payload[i]);
-  //  }
-  //  Serial.println();
-
-  char tmp_topic_pub[51];
-  char tmp_payload_pub[51];
-
-  if (strcmp(topic, mqtt_topic_in_cmd) == 0) { //if topic = mqtt_topic_in_cmd
-    int len;
-    if (length < 101) len = length; // if input is bigger than dest buffer, cut
-    else len = 100;
-
-    for (int i = 0; i < len; i++) {
-      cmdPayload[i] = (char)payload[i];
-    }
-    //cmdPayload[len + 1] = '\0';
-    cmdPayload[len] = '\0';
-    //    Serial.print("cmdPayload:");
-    //    Serial.println(cmdPayload);
-
-    if (serialdebug) {
-      Serial.print("received MQTT ");
-      Serial.print(mqtt_topic_in_cmd);
-      Serial.print(" - \"");
-      Serial.print(cmdPayload);
-      Serial.println("\"");
-    }
-    if (mqttdebug) {
-      sprintf(tmp_topic_pub, "%s/%s", mqtt_topic_out, "mqtt_received");
-      sprintf(tmp_payload_pub, "%s: %s", mqtt_topic_in_cmd, cmdPayload);
-      mqttclient.publish(tmp_topic_pub, tmp_payload_pub);
-    }
-
-    cmdInQueue = true; // payload is processed in "commands"
-  }//if topic = mqtt_topic_in_cmd
-
-  if (strcmp(topic, mqtt_topic_in_setTemp) == 0) { //if topic = mqtt_topic_in_setTemp
-    int len;
-    if (length < 101) len = length; // if input is bigger than dest buffer, cut
-    else len = 100;
-
-    for (int i = 0; i < len; i++) {
-      cmdPayload[i] = (char)payload[i];
-    }
-    //cmdPayload[len + 1] = '\0';
-    cmdPayload[len] = '\0';
-    //    Serial.print("cmdPayload:");
-    //    Serial.println(cmdPayload);
-
-    char tmpPayload[11];
-    strlcpy(tmpPayload, cmdPayload, 10);
-
-    float valueFloat;
-    valueFloat = round(atof(tmpPayload) * 2.0) / 2.0;
-    setTempTo(valueFloat);
-
-    if (serialdebug) {
-      Serial.print("received MQTT ");
-      Serial.print(mqtt_topic_in_setTemp);
-      Serial.print(" - \"");
-      Serial.print(cmdPayload);
-      Serial.println("\"");
-    }
-    if (mqttdebug) {
-      sprintf(tmp_topic_pub, "%s/%s", mqtt_topic_out, "mqtt_received");
-      sprintf(tmp_payload_pub, "%s: %s", mqtt_topic_in_setTemp, cmdPayload);
-      mqttclient.publish(tmp_topic_pub, tmp_payload_pub);
-    }
-
-    cmdInQueue = false; // payload is processed in "commands"
-  }//if topic = mqtt_topic_in_setTemp
-
-  if (strcmp(topic, mqtt_topic_in_setMode) == 0) { //if topic = mqtt_topic_in_setMode
-    int len;
-    if (length < 101) len = length; // if input is bigger than dest buffer, cut
-    else len = 100;
-
-    for (int i = 0; i < len; i++) {
-      cmdPayload[i] = (char)payload[i];
-    }
-    //cmdPayload[len + 1] = '\0';
-    cmdPayload[len] = '\0';
-    //    Serial.print("cmdPayload:");
-    //    Serial.println(cmdPayload);
-
-    char tmpPayload[21];
-    strlcpy(tmpPayload, cmdPayload, 20);
-    strlwr(tmpPayload);
-
-    byte tmpHeatMode;
-
-    char tmpModename0[15];
-    char tmpModename1[15];
-    char tmpModename2[15];
-    char tmpModename3[15];
-    strlcpy(tmpModename0, modename0, 14);
-    strlcpy(tmpModename1, modename1, 14);
-    strlcpy(tmpModename2, modename2, 14);
-    strlcpy(tmpModename3, modename3, 14);
-    strlwr(tmpModename0);
-    strlwr(tmpModename1);
-    strlwr(tmpModename2);
-    strlwr(tmpModename3);
-
-    if (strcmp(tmpPayload, tmpModename0) == 0) tmpHeatMode = 0;
-    else if (strcmp(tmpPayload, tmpModename1) == 0) tmpHeatMode = 1;
-    else if (strcmp(tmpPayload, tmpModename2) == 0) tmpHeatMode = 2;
-    else if (strcmp(tmpPayload, tmpModename3) == 0) tmpHeatMode = 3;
-    else if (strcmp(tmpPayload, "off") == 0) tmpHeatMode = 0;
-    else if (strcmp(tmpPayload, "aus") == 0) tmpHeatMode = 0;
-    else if (strcmp(tmpPayload, "on") == 0) tmpHeatMode = 1;
-    else if (strcmp(tmpPayload, "ein") == 0) tmpHeatMode = 1;
-    else if (atoi(tmpPayload) == 0) tmpHeatMode = 0;
-    else if (atoi(tmpPayload) == 1) tmpHeatMode = 1;
-    else if (atoi(tmpPayload) == 2) tmpHeatMode = 2;
-    else if (atoi(tmpPayload) == 3) tmpHeatMode = 3;
-
-    if (serialdebug) {
-      Serial.print("set heatmode to: ");
-      Serial.println(tmpHeatMode);
-    }
-
-    setHeatingmodeTo(tmpHeatMode);
-
-    if (serialdebug) {
-      Serial.print("received MQTT ");
-      Serial.print(mqtt_topic_in_setMode);
-      Serial.print(" - \"");
-      Serial.print(cmdPayload);
-      Serial.println("\"");
-    }
-    if (mqttdebug) {
-      sprintf(tmp_topic_pub, "%s/%s", mqtt_topic_out, "mqtt_received");
-      sprintf(tmp_payload_pub, "%s: %s", mqtt_topic_in_setMode, cmdPayload);
-      mqttclient.publish(tmp_topic_pub, tmp_payload_pub);
-    }
-
-    cmdInQueue = false; // payload is processed in "commands"
-  }//if topic = mqtt_topic_in_setMode
-
-  if (strcmp(topic, outTemp_topic_in) == 0) { //if topic = outTemp_topic_in
-    int len;
-    if (length < 6) len = length; // if input is bigger than dest buffer, cut
-    else len = 5;
-    for (int i = 0; i < len; i++) {
-      outTemp_newValue[i] = (char)payload[i];
-    }
-    //outTemp_newValue[len + 1] = '\0';
-    outTemp_newValue[len] = '\0';
-    outTemp_parseNewValue = true;
-  }//if topic = outTemp_topic_in
-
-  if (strcmp(topic, outHum_topic_in) == 0) { //if topic = outHum_topic_in
-    int len;
-    if (length < 4) len = length; // if input is bigger than dest buffer, cut
-    else len = 3;
-    for (int i = 0; i < len; i++) {
-      outHum_newValue[i] = (char)payload[i];
-    }
-    //outHum_newValue[len + 1] = '\0';
-    outHum_newValue[len] = '\0';
-    outHum_parseNewValue = true;
-  }//if topic = outHum_topic_in
-
-  if (strcmp(topic, domoticz_out_topic) == 0) { //if topic = domoticz_out_topic
-    Serial.print("received MQTT ");
-    Serial.println(domoticz_out_topic);
-    if ( !domoticzOutParserBusy ) {
-      int len;
-      if (length < 450) len = length; // if input is bigger than dest buffer, cut
-      else len = 449;
-      for (int i = 0; i < len; i++) {
-        domoticzOutPayload[i] = (char)payload[i];
-      }
-      //domoticzOutPayload[len + 1] = '\0';
-      domoticzOutPayload[len] = '\0';
-      domoticzOutParseData = true; // parse domoticz data in the next loop()
-    }
-  }//if topic = domoticz_out_topic
-
-}//mqttCallback
-
-
-void mqttPrepareConnection() {
-  Serial.print("MQTT connection with ");
-  if (strlen(mqtt_user) > 0 && strlen(mqtt_willTopic) == 0) {
-    // user and password, no Last Will
-    Serial.println("user and password, no Last Will");
-    mqttMode = 2;
-  }
-  else if (strlen(mqtt_user) > 0 && strlen(mqtt_willTopic) > 0) {
-    // user, password and Last Will
-    Serial.println("user, password and Last Will");
-    mqttMode = 3;
-  }
-  else if (strlen(mqtt_user) == 0 && strlen(mqtt_willTopic) > 0) {
-    // Last Will but no user and password
-    Serial.println("Last Will but no user and password");
-    mqttMode = 4;
-  }
-  else {
-    // no user, password and no Last Will
-    Serial.println("no user, password and no Last Will");
-    mqttMode = 1;
-  }
-}
-
-void mqttPrepareSubscribeTopics() {
-  char tmp_topic_out[50];
-  sprintf(mqtt_topic_in_cmd, "%s/%s", mqtt_topic_in, "cmd");
-  sprintf(mqtt_topic_in_setTemp, "%s/%s", mqtt_topic_in, "setTemp");
-  sprintf(mqtt_topic_in_setMode, "%s/%s", mqtt_topic_in, "setMode");
-}
-
-bool mqttReconnect() {
-  mqttclient.disconnect();
-  delay(10);
-  // Create MQTT client ID from device name
-  String mqttClientId = "ESP8266Client-";
-  //mqttClientId += String(random(0xffff), HEX);   //or random
-  mqttClientId += String(deviceName);
-
-  if (serialdebug) Serial.print("connecting to MQTT broker with ");
-
-  bool connRes;
-
-  switch (mqttMode) {
-    case 1:
-      if (serialdebug) Serial.println("no user, password and no Last Will");
-      connRes = mqttclient.connect(mqttClientId.c_str());
-      break;
-    case 2:
-      if (serialdebug) Serial.println("user and password, no Last Will");
-      connRes = mqttclient.connect(mqttClientId.c_str(), mqtt_user, mqtt_pass);
-      break;
-    case 3:
-      if (serialdebug) Serial.println("user, password and Last Will");
-      connRes = mqttclient.connect(mqttClientId.c_str(), mqtt_user, mqtt_pass, mqtt_willTopic, mqtt_willQos, mqtt_willRetain, mqtt_willMsg);
-      break;
-    case 4:
-      if (serialdebug) Serial.println("Last Will, no user and password");
-      connRes = mqttclient.connect(mqttClientId.c_str(), mqtt_willTopic, mqtt_willQos, mqtt_willRetain, mqtt_willMsg);
-      break;
-  }
-
-  if (serialdebug) {
-    Serial.print("... attempt: ");
-    Serial.print(mqttReconnectAttempts);
-    Serial.println();
-  }
-
-  if (connRes) {
-    if (serialdebug) {
-      Serial.print("MQTT connected. Reconnects: ");
-      Serial.println(mqttReconnects);
-    }
-    mqttReconnects++;
-
-    // Once connected, publish an announcement...
-    //    char outMsg[30];
-    //    sprintf(outMsg, "connected, %d reconnects", mqttReconnects);
-    //    mqttclient.publish(mqtt_topic_out, outMsg, mqtt_outRetain);
-
-    mqttclient.publish(mqtt_willTopic, mqtt_connMsg, mqtt_willRetain);
-    publishStatus();
-    //mqttclient.publish(mqtt_topic_out, "connected");
-    // ... and resubscribe
-    if (serialdebug) Serial.println("Subscribed to:");
-    if (strlen(mqtt_topic_in_cmd) > 0) {
-      if (mqttclient.subscribe(mqtt_topic_in_cmd)) {
-        if (serialdebug) Serial.println(mqtt_topic_in_cmd);
-        mqttInTopicSubscribed = true;
-      }
-    }
-    if (strlen(mqtt_topic_in_setTemp) > 0) {
-      if (mqttclient.subscribe(mqtt_topic_in_setTemp)) {
-        if (serialdebug) Serial.println(mqtt_topic_in_setTemp);
-      }
-    }
-    if (strlen(mqtt_topic_in_setMode) > 0) {
-      if (mqttclient.subscribe(mqtt_topic_in_setMode)) {
-        if (serialdebug) Serial.println(mqtt_topic_in_setMode);
-      }
-    }
-    if (strlen(outTemp_topic_in) > 0) {
-      if (mqttclient.subscribe(outTemp_topic_in)) {
-        if (serialdebug) Serial.println(outTemp_topic_in);
-      }
-    }
-    if (strlen(outHum_topic_in) > 0) {
-      if (mqttclient.subscribe(outHum_topic_in)) {
-        if (serialdebug) Serial.println(outHum_topic_in);
-      }
-    }
-    if (useDomoticz && strlen(domoticz_out_topic) > 0) {
-      if (mqttclient.subscribe(domoticz_out_topic)) {
-        if (serialdebug) Serial.println(domoticz_out_topic);
-      }
-    }
-    return mqttclient.connected();
-  }
-  else {
-    if (serialdebug) {
-      Serial.print("MQTT connect FAILED, rc=");
-      Serial.println(mqttclient.state());
-    }
-  }
-} //mqttReconnect
-
-
-
-void mqttClientInit() {
-  mqttclient.setServer(mqtt_server, mqtt_port);
-  mqttclient.setCallback(mqttCallback);
-  mqttLastReconnectAttempt = 0;
-  mqttReconnectAttempts = 0;
-}
-
-int lastWifiStatus;
-
-void mqttHandleConnection() {
-  int currWifiStatus = WiFi.status();
-  if ( currWifiStatus != lastWifiStatus ) {
-    lastWifiStatus = currWifiStatus;
-    Serial.print("WiFi status changed to: ");
-    Serial.println(currWifiStatus);
-  }
-
-  if ( currWifiStatus == WL_CONNECTED ) {
-    // MQTT reconnect if not connected (nonblocking)
-    bool doReconnect = false;
-    if (!mqttclient.connected()) doReconnect = true;
-
-    if (mqttInTopicSubscribed) { //  if mqtt_topic_in is subscribed
-      if ( (millis() - mqttLastHeartbeat) > MQTT_HEARTBEAT_MAXAGE) { // also reconnect if no HEARTBEAT was received for some time
-        doReconnect = true;
-        mqttLastHeartbeat = millis();
-        Serial.println("MQTT HEARTBEAT overdue. Force-Reconnecting MQTT...");
-      }
-    }
-
-    if (doReconnect) {
-      unsigned int mqttReconnectAttemptDelay;
-      if (mqttReconnectAttempts < 3) mqttReconnectAttemptDelay = 15000; // if this is the 1-3rd attempt, try again in 15s
-      else if (mqttReconnectAttempts < 10) mqttReconnectAttemptDelay = 60000; // if more than 3 attempts failed, try again every min
-      else if (mqttReconnectAttempts < 20) mqttReconnectAttemptDelay = 300000; // if more than 10 attempts failed, try again every 5 min
-      else if (mqttReconnectAttempts >= 6) {
-        // if more than 6 attempts (= > 30 min) failed, restart the ESP
-        delay(100);
-        ESP.restart();
-      }
-
-      if ((millis() - mqttLastReconnectAttempt) > mqttReconnectAttemptDelay) {
-        mqttLastReconnectAttempt = millis();
-        mqttReconnectAttempts++;
-        if (mqttReconnect()) { // Attempt to reconnect
-          // attempt successful - reset mqttReconnectAttempts
-          mqttReconnectAttempts = 0;
-        }
-      }
-      mqttclient.loop();
-    }
-    else {
-      mqttclient.loop();
-    }
-  }
-}
-
-void publishStatus() {
-  char outMsg[60];
-  long upTime = millis() / 1000;
-  sprintf(outMsg, "connected, reconnects: %d, uptime: %d, free heap: %d", mqttReconnects - 1, upTime, ESP.getFreeHeap());
-  mqttclient.publish(mqtt_topic_out, outMsg, mqtt_outRetain);
-
-  mqttclient.publish(mqtt_willTopic, mqtt_connMsg, mqtt_willRetain);
-  yield();
-}
-
-void sendStatus(char* payload) {
-  char buf[101];
-  strlcpy(buf, payload, 101);
-  Serial.println(buf);
-  mqttclient.publish(mqtt_topic_out, buf, mqtt_outRetain);
-  yield();
-}
-
-void publishCurrentThermostatValues() {
-  char tmp_topic_out[50];
-
-  float curr_setTemp;
-  if (heatingMode == 1) curr_setTemp = setTemp;
-  else if (heatingMode == 2) curr_setTemp = setTempLow;
-  else if (heatingMode == 3) curr_setTemp = setTempLow2;
-
-  char setTemp_chararr[6];
-  char currSetTemp_chararr[6];
-
-  dtostrf(setTemp, 1, 1, setTemp_chararr );
-  dtostrf(curr_setTemp, 1, 1, currSetTemp_chararr );
-
-  Serial.print("heatingMode: '");
-  Serial.print(heatingMode);
-  Serial.println("'");
-  Serial.print("set temp: '");
-  Serial.print(setTemp_chararr);
-  Serial.println("'");
-  Serial.print("current set temp: '");
-  Serial.print(currSetTemp_chararr);
-  Serial.println("'");
-
-  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "setTemp");
-  mqttclient.publish(tmp_topic_out, setTemp_chararr);
-  yield();
-
-  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "currSetTemp");
-  mqttclient.publish(tmp_topic_out, currSetTemp_chararr);
-  yield();
-
-  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "heatingMode");
-  char heatingMode_chararr[3];
-  sprintf(heatingMode_chararr, "%d", heatingMode);
-  mqttclient.publish(tmp_topic_out, heatingMode_chararr);
-
-  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "heatingModeName");
-  mqttclient.publish(tmp_topic_out, currentModeName);
-
-  yield();
-
-  char turnHeatingOn_char[5];
-  if (turnHeatingOn) strcpy(turnHeatingOn_char, "on");
-  else strcpy(turnHeatingOn_char, "off");
-  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "heating");
-  mqttclient.publish(tmp_topic_out, turnHeatingOn_char);
-  yield();
-
-  char buf[101];
-  sprintf(buf, "%d", heatingOnTime);
-  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "heatingOnTime");
-  mqttclient.publish(tmp_topic_out, buf);
-  yield();
-
-  sprintf(buf, "%d", heatingOffTime);
-  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "heatingOffTime");
-  mqttclient.publish(tmp_topic_out, buf);
-  yield();
-}
-
-void publishCurrentSensorValues() {
-  if ( lastTempUpdate != 0 && (millis() - lastTempUpdate) < 120000 ) {
-    char tmp_topic_out[50];
-
-    char temp_chararr[6];
-    char hum_chararr[4];
-    dtostrf(currTemp, 1, 1, temp_chararr );
-    sprintf(hum_chararr, "%2i", currHum);
-
-    Serial.print("temp: '");
-    Serial.print(temp_chararr);
-    Serial.println("'");
-    sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "temp");
-    mqttclient.publish(tmp_topic_out, temp_chararr);
-    yield();
-
-    Serial.print("hum: '");
-    Serial.print(currHum);
-    Serial.println("'");
-    sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "hum");
-    mqttclient.publish(tmp_topic_out, hum_chararr);
-    yield();
-
-    dtostrf(currTemp_raw, 1, 1, temp_chararr );
-    sprintf(hum_chararr, "%2i", currHum_raw);
-
-    Serial.print("temp_raw: '");
-    Serial.print(temp_chararr);
-    Serial.println("'");
-    sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "temp_raw");
-    mqttclient.publish(tmp_topic_out, temp_chararr);
-    yield();
-
-    Serial.print("hum_raw: '");
-    Serial.print(currHum_raw);
-    Serial.println("'");
-    sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "hum_raw");
-    mqttclient.publish(tmp_topic_out, hum_chararr);
-    yield();
-  }
-}
-
-void publishCurrentPIRValue() {
-  char tmp_topic_out[50];
-  char PIRStatus[4];
-
-  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "PIR");
-
-  if (PIRSensorOn) {
-    strcpy(PIRStatus, "on");
-  }
-  else {
-    strcpy(PIRStatus, "off");
-  }
-  mqttclient.publish(tmp_topic_out, PIRStatus);
-
-  if (strlen(mqtt_topic_pir) > 0) {
-    mqttclient.publish(mqtt_topic_pir, PIRStatus);
-  }
-}
-
-void mqttPublishHeartbeat() {
-  mqttclient.publish(mqtt_topic_in_cmd, "HEARTBEAT"); // publishes to IN-topic. MQTT will force-reconnect if it does not get a HEARTBEAT for 2 min
-}
-
 
+#include <string.h>
+#include <ctype.h>
+char *strlwr(char *str) {
+  unsigned char *p = (unsigned char *)str;
+  while (*p) {
+    *p = tolower((unsigned char) * p);
+    p++;
+  }
+  return str;
+}
+
+
+// MQTT callback
+void mqttCallback(char* topic, byte* payload, unsigned int length) {
+  //  Serial.print("MQTT payload arrived [");
+  //  Serial.print(topic);
+  //  Serial.print("] ");
+  //  for (int i = 0; i < length; i++) {
+  //    Serial.print((char)payload[i]);
+  //  }
+  //  Serial.println();
+
+  char tmp_topic_pub[51];
+  char tmp_payload_pub[51];
+
+  if (strcmp(topic, mqtt_topic_in_cmd) == 0) { //if topic = mqtt_topic_in_cmd
+    int len;
+    if (length < 101) len = length; // if input is bigger than dest buffer, cut
+    else len = 100;
+
+    for (int i = 0; i < len; i++) {
+      cmdPayload[i] = (char)payload[i];
+    }
+    //cmdPayload[len + 1] = '\0';
+    cmdPayload[len] = '\0';
+    //    Serial.print("cmdPayload:");
+    //    Serial.println(cmdPayload);
+
+    if (serialdebug) {
+      Serial.print("received MQTT ");
+      Serial.print(mqtt_topic_in_cmd);
+      Serial.print(" - \"");
+      Serial.print(cmdPayload);
+      Serial.println("\"");
+    }
+    if (mqttdebug) {
+      sprintf(tmp_topic_pub, "%s/%s", mqtt_topic_out, "mqtt_received");
+      sprintf(tmp_payload_pub, "%s: %s", mqtt_topic_in_cmd, cmdPayload);
+      mqttclient.publish(tmp_topic_pub, tmp_payload_pub);
+    }
+
+    cmdInQueue = true; // payload is processed in "commands"
+  }//if topic = mqtt_topic_in_cmd
+
+  if (strcmp(topic, mqtt_topic_in_setTemp) == 0) { //if topic = mqtt_topic_in_setTemp
+    int len;
+    if (length < 101) len = length; // if input is bigger than dest buffer, cut
+    else len = 100;
+
+    for (int i = 0; i < len; i++) {
+      cmdPayload[i] = (char)payload[i];
+    }
+    //cmdPayload[len + 1] = '\0';
+    cmdPayload[len] = '\0';
+    //    Serial.print("cmdPayload:");
+    //    Serial.println(cmdPayload);
+
+    char tmpPayload[11];
+    strlcpy(tmpPayload, cmdPayload, 10);
+
+    float valueFloat;
+    valueFloat = round(atof(tmpPayload) * 2.0) / 2.0;
+    setTempTo(valueFloat);
+
+    if (serialdebug) {
+      Serial.print("received MQTT ");
+      Serial.print(mqtt_topic_in_setTemp);
+      Serial.print(" - \"");
+      Serial.print(cmdPayload);
+      Serial.println("\"");
+    }
+    if (mqttdebug) {
+      sprintf(tmp_topic_pub, "%s/%s", mqtt_topic_out, "mqtt_received");
+      sprintf(tmp_payload_pub, "%s: %s", mqtt_topic_in_setTemp, cmdPayload);
+      mqttclient.publish(tmp_topic_pub, tmp_payload_pub);
+    }
+
+    cmdInQueue = false; // payload is processed in "commands"
+  }//if topic = mqtt_topic_in_setTemp
+
+
+
+  if (strcmp(topic, mqtt_topic_in_setMode) == 0) { //if topic = mqtt_topic_in_setMode
+    int len;
+    if (length < 101) len = length; // if input is bigger than dest buffer, cut
+    else len = 100;
+
+    for (int i = 0; i < len; i++) {
+      cmdPayload[i] = (char)payload[i];
+    }
+    //cmdPayload[len + 1] = '\0';
+    cmdPayload[len] = '\0';
+    //    Serial.print("cmdPayload:");
+    //    Serial.println(cmdPayload);
+
+    char tmpPayload[21];
+    strlcpy(tmpPayload, cmdPayload, 20);
+    strlwr(tmpPayload);
+
+    byte tmpHeatMode;
+
+    char tmpModename0[15];
+    char tmpModename1[15];
+    strlcpy(tmpModename0, modename0, 14);
+    strlcpy(tmpModename1, modename1, 14);
+    strlwr(tmpModename0);
+    strlwr(tmpModename1);
+
+    if (strcmp(tmpPayload, tmpModename0) == 0) tmpHeatMode = 0;
+    else if (strcmp(tmpPayload, tmpModename1) == 0) tmpHeatMode = 1;
+    else if (strcmp(tmpPayload, "off") == 0) tmpHeatMode = 0;
+    else if (strcmp(tmpPayload, "aus") == 0) tmpHeatMode = 0;
+    else if (strcmp(tmpPayload, "on") == 0) tmpHeatMode = 1;
+    else if (strcmp(tmpPayload, "ein") == 0) tmpHeatMode = 1;
+    else if (atoi(tmpPayload) == 0) tmpHeatMode = 0;
+    else if (atoi(tmpPayload) == 1) tmpHeatMode = 1;
+
+    if (serialdebug) {
+      Serial.print("set heatmode to: ");
+      Serial.println(tmpHeatMode);
+    }
+
+    setHeatingmodeTo(tmpHeatMode);
+
+    if (serialdebug) {
+      Serial.print("received MQTT ");
+      Serial.print(mqtt_topic_in_setMode);
+      Serial.print(" - \"");
+      Serial.print(cmdPayload);
+      Serial.println("\"");
+    }
+    if (mqttdebug) {
+      sprintf(tmp_topic_pub, "%s/%s", mqtt_topic_out, "mqtt_received");
+      sprintf(tmp_payload_pub, "%s: %s", mqtt_topic_in_setMode, cmdPayload);
+      mqttclient.publish(tmp_topic_pub, tmp_payload_pub);
+    }
+
+    cmdInQueue = false; // payload is processed in "commands"
+  }//if topic = mqtt_topic_in_setMode
+
+
+  if (strcmp(topic, mqtt_topic_in_setPreset) == 0) { //if topic = mqtt_topic_in_setPreset
+    int len;
+    if (length < 101) len = length; // if input is bigger than dest buffer, cut
+    else len = 100;
+
+    for (int i = 0; i < len; i++) {
+      cmdPayload[i] = (char)payload[i];
+    }
+    //cmdPayload[len + 1] = '\0';
+    cmdPayload[len] = '\0';
+    //    Serial.print("cmdPayload:");
+    //    Serial.println(cmdPayload);
+
+    char tmpPayload[21];
+    strlcpy(tmpPayload, cmdPayload, 20);
+    strlwr(tmpPayload);
+
+    byte tmpPreset;
+
+    char tmpPresetName0[15];
+    char tmpPresetName1[15];
+    char tmpPresetName2[15];
+    strlcpy(tmpPresetName0, psetname0, 14);
+    strlcpy(tmpPresetName1, psetname1, 14);
+    strlcpy(tmpPresetName2, psetname2, 14);
+    strlwr(tmpPresetName0);
+    strlwr(tmpPresetName1);
+    strlwr(tmpPresetName2);
+
+    if (strcmp(tmpPayload, tmpPresetName0) == 0) tmpPreset = 0;
+    else if (strcmp(tmpPayload, tmpPresetName1) == 0) tmpPreset = 1;
+    else if (strcmp(tmpPayload, tmpPresetName2) == 0) tmpPreset = 2;
+    else if (strcmp(tmpPayload, "none") == 0) tmpPreset = 0;
+    else if (strcmp(tmpPayload, "red1") == 0) tmpPreset = 1;
+    else if (strcmp(tmpPayload, "red2") == 0) tmpPreset = 2;
+    else if (atoi(tmpPayload) == 0) tmpPreset = 0;
+    else if (atoi(tmpPayload) == 1) tmpPreset = 1;
+    else if (atoi(tmpPayload) == 2) tmpPreset = 2;
+
+    if (serialdebug) {
+      Serial.print("set preset to: ");
+      Serial.println(tmpPreset);
+    }
+
+    setPresetTo(tmpPreset);
+
+    if (serialdebug) {
+      Serial.print("received MQTT ");
+      Serial.print(mqtt_topic_in_setPreset);
+      Serial.print(" - \"");
+      Serial.print(cmdPayload);
+      Serial.println("\"");
+    }
+    if (mqttdebug) {
+      sprintf(tmp_topic_pub, "%s/%s", mqtt_topic_out, "mqtt_received");
+      sprintf(tmp_payload_pub, "%s: %s", mqtt_topic_in_setPreset, cmdPayload);
+      mqttclient.publish(tmp_topic_pub, tmp_payload_pub);
+    }
+
+    cmdInQueue = false; // payload is processed in "commands"
+  }//if topic = mqtt_topic_in_setPreset
+
+
+  if (strcmp(topic, outTemp_topic_in) == 0) { //if topic = outTemp_topic_in
+    int len;
+    if (length < 6) len = length; // if input is bigger than dest buffer, cut
+    else len = 5;
+    for (int i = 0; i < len; i++) {
+      outTemp_newValue[i] = (char)payload[i];
+    }
+    //outTemp_newValue[len + 1] = '\0';
+    outTemp_newValue[len] = '\0';
+    outTemp_parseNewValue = true;
+  }//if topic = outTemp_topic_in
+
+  if (strcmp(topic, outHum_topic_in) == 0) { //if topic = outHum_topic_in
+    int len;
+    if (length < 4) len = length; // if input is bigger than dest buffer, cut
+    else len = 3;
+    for (int i = 0; i < len; i++) {
+      outHum_newValue[i] = (char)payload[i];
+    }
+    //outHum_newValue[len + 1] = '\0';
+    outHum_newValue[len] = '\0';
+    outHum_parseNewValue = true;
+  }//if topic = outHum_topic_in
+
+  if (strcmp(topic, domoticz_out_topic) == 0) { //if topic = domoticz_out_topic
+    Serial.print("received MQTT ");
+    Serial.println(domoticz_out_topic);
+    if ( !domoticzOutParserBusy ) {
+      int len;
+      if (length < 450) len = length; // if input is bigger than dest buffer, cut
+      else len = 449;
+      for (int i = 0; i < len; i++) {
+        domoticzOutPayload[i] = (char)payload[i];
+      }
+      //domoticzOutPayload[len + 1] = '\0';
+      domoticzOutPayload[len] = '\0';
+      domoticzOutParseData = true; // parse domoticz data in the next loop()
+    }
+  }//if topic = domoticz_out_topic
+
+}//mqttCallback
+
+
+void mqttPrepareConnection() {
+  Serial.print("MQTT connection with ");
+  if (strlen(mqtt_user) > 0 && strlen(mqtt_willTopic) == 0) {
+    // user and password, no Last Will
+    Serial.println("user and password, no Last Will");
+    mqttMode = 2;
+  }
+  else if (strlen(mqtt_user) > 0 && strlen(mqtt_willTopic) > 0) {
+    // user, password and Last Will
+    Serial.println("user, password and Last Will");
+    mqttMode = 3;
+  }
+  else if (strlen(mqtt_user) == 0 && strlen(mqtt_willTopic) > 0) {
+    // Last Will but no user and password
+    Serial.println("Last Will but no user and password");
+    mqttMode = 4;
+  }
+  else {
+    // no user, password and no Last Will
+    Serial.println("no user, password and no Last Will");
+    mqttMode = 1;
+  }
+}
+
+void mqttPrepareSubscribeTopics() {
+  //char tmp_topic_out[50];
+  sprintf(mqtt_topic_in_cmd, "%s/%s", mqtt_topic_in, "cmd");
+  sprintf(mqtt_topic_in_setTemp, "%s/%s", mqtt_topic_in_cmd, "setTemp");
+  sprintf(mqtt_topic_in_setMode, "%s/%s", mqtt_topic_in_cmd, "setMode");
+  sprintf(mqtt_topic_in_setPreset, "%s/%s", mqtt_topic_in_cmd, "setPreset");
+}
+
+boolean mqttReconnect() {
+  mqttclient.disconnect();
+  delay(10);
+  // Create MQTT client ID from device name
+  String mqttClientId = "ESP8266Client-";
+  //mqttClientId += String(random(0xffff), HEX);   //or random
+  mqttClientId += String(deviceName);
+
+  if (serialdebug) Serial.print("connecting to MQTT broker with ");
+
+  boolean connRes;
+
+  switch (mqttMode) {
+    case 1:
+      if (serialdebug) Serial.println("no user, password and no Last Will");
+      connRes = mqttclient.connect(mqttClientId.c_str());
+      break;
+    case 2:
+      if (serialdebug) Serial.println("user and password, no Last Will");
+      connRes = mqttclient.connect(mqttClientId.c_str(), mqtt_user, mqtt_pass);
+      break;
+    case 3:
+      if (serialdebug) Serial.println("user, password and Last Will");
+      connRes = mqttclient.connect(mqttClientId.c_str(), mqtt_user, mqtt_pass, mqtt_willTopic, mqtt_willQos, mqtt_willRetain, mqtt_willMsg);
+      break;
+    case 4:
+      if (serialdebug) Serial.println("Last Will, no user and password");
+      connRes = mqttclient.connect(mqttClientId.c_str(), mqtt_willTopic, mqtt_willQos, mqtt_willRetain, mqtt_willMsg);
+      break;
+  }
+
+  if (serialdebug) {
+    Serial.print("... attempt: ");
+    Serial.print(mqttReconnectAttempts);
+    Serial.println();
+  }
+
+  if (connRes) {
+    if (serialdebug) {
+      Serial.print("MQTT connected. Reconnects: ");
+      Serial.println(mqttReconnects);
+    }
+    displayShowMQTTConnected();
+    mqttConnected = true;
+    mqttReconnects++;
+
+    // Once connected, publish an announcement...
+    //    char outMsg[30];
+    //    sprintf(outMsg, "connected, %d reconnects", mqttReconnects);
+    //    mqttclient.publish(mqtt_topic_out, outMsg, mqtt_outRetain);
+
+    mqttclient.publish(mqtt_willTopic, mqtt_connMsg, mqtt_willRetain);
+    publishStatus();
+    //mqttclient.publish(mqtt_topic_out, "connected");
+    // ... and resubscribe
+    if (serialdebug) Serial.println("Subscribed to:");
+    if (strlen(mqtt_topic_in_cmd) > 0) {
+      char mqtt_topic_in_subscribe[52];
+      sprintf(mqtt_topic_in_subscribe, "%s/%s", mqtt_topic_in_cmd, "#");
+      if (mqttclient.subscribe(mqtt_topic_in_subscribe)) {
+        if (serialdebug) Serial.println(mqtt_topic_in_subscribe);
+        mqttInTopicSubscribed = true;
+      }
+    }
+    //    if (strlen(mqtt_topic_in_setTemp) > 0) {
+    //      if (mqttclient.subscribe(mqtt_topic_in_setTemp)) {
+    //        if (serialdebug) Serial.println(mqtt_topic_in_setTemp);
+    //      }
+    //    }
+    //    if (strlen(mqtt_topic_in_setMode) > 0) {
+    //      if (mqttclient.subscribe(mqtt_topic_in_setMode)) {
+    //        if (serialdebug) Serial.println(mqtt_topic_in_setMode);
+    //      }
+    //    }
+    //    if (strlen(mqtt_topic_in_setPreset) > 0) {
+    //      if (mqttclient.subscribe(mqtt_topic_in_setPreset)) {
+    //        if (serialdebug) Serial.println(mqtt_topic_in_setPreset);
+    //      }
+    //    }
+    if (strlen(outTemp_topic_in) > 0) {
+      if (mqttclient.subscribe(outTemp_topic_in)) {
+        if (serialdebug) Serial.println(outTemp_topic_in);
+      }
+    }
+    if (strlen(outHum_topic_in) > 0) {
+      if (mqttclient.subscribe(outHum_topic_in)) {
+        if (serialdebug) Serial.println(outHum_topic_in);
+      }
+    }
+    if (useDomoticz && strlen(domoticz_out_topic) > 0) {
+      if (mqttclient.subscribe(domoticz_out_topic)) {
+        if (serialdebug) Serial.println(domoticz_out_topic);
+      }
+    }
+    return mqttclient.connected();
+  }
+  else {
+    if (serialdebug) {
+      Serial.print("MQTT connect FAILED, rc=");
+      Serial.println(mqttclient.state());
+    }
+    displayShowMQTTConnectionError();
+  }
+} //mqttReconnect
+
+
+
+void mqttClientInit() {
+  mqttclient.setServer(mqtt_server, mqtt_port);
+  mqttclient.setCallback(mqttCallback);
+  mqttLastReconnectAttempt = 0;
+  mqttReconnectAttempts = 0;
+}
+
+int lastWifiStatus;
+
+void mqttHandleConnection() {
+  int currWifiStatus = WiFi.status();
+  if ( currWifiStatus != lastWifiStatus ) {
+    lastWifiStatus = currWifiStatus;
+    Serial.print("WiFi status changed to: ");
+    Serial.println(currWifiStatus);
+  }
+
+  if ( currWifiStatus == WL_CONNECTED ) {
+    // MQTT reconnect if not connected (nonblocking)
+    boolean doReconnect = false;
+    if (!mqttclient.connected()) doReconnect = true;
+
+    if (mqttInTopicSubscribed) { //  if mqtt_topic_in is subscribed
+      if ( (millis() - mqttLastHeartbeat) > MQTT_HEARTBEAT_MAXAGE) { // also reconnect if no HEARTBEAT was received for some time
+        doReconnect = true;
+        mqttConnected = false;
+        mqttLastHeartbeat = millis();
+        Serial.println("MQTT HEARTBEAT overdue. Force-Reconnecting MQTT...");
+      }
+    }
+
+    if (doReconnect) {
+      unsigned int mqttReconnectAttemptDelay;
+      if (mqttReconnectAttempts < 3) mqttReconnectAttemptDelay = 15000; // if this is the 1-3rd attempt, try again in 15s
+      else if (mqttReconnectAttempts < 10) mqttReconnectAttemptDelay = 60000; // if more than 3 attempts failed, try again every min
+      else if (mqttReconnectAttempts < 20) mqttReconnectAttemptDelay = 300000; // if more than 10 attempts failed, try again every 5 min
+      else if (mqttReconnectAttempts >= 6) {
+        // if more than 6 attempts (= > 30 min) failed, restart the ESP
+        delay(100);
+        ESP.restart();
+      }
+
+      if ((millis() - mqttLastReconnectAttempt) > mqttReconnectAttemptDelay) {
+        mqttLastReconnectAttempt = millis();
+        mqttReconnectAttempts++;
+        if (mqttReconnect()) { // Attempt to reconnect
+          // attempt successful - reset mqttReconnectAttempts
+          mqttReconnectAttempts = 0;
+        }
+      }
+      mqttclient.loop();
+    }
+    else {
+      mqttclient.loop();
+    }
+  }
+}
+
+void publishStatus() {
+  char outMsg[60];
+  long upTime = millis() / 1000;
+  sprintf(outMsg, "connected, reconnects: %d, uptime: %d, free heap: %d", mqttReconnects - 1, upTime, ESP.getFreeHeap());
+  mqttclient.publish(mqtt_topic_out, outMsg, mqtt_outRetain);
+
+  mqttclient.publish(mqtt_willTopic, mqtt_connMsg, mqtt_willRetain);
+  yield();
+}
+
+void sendStatus(char* payload) {
+  char buf[101];
+  strlcpy(buf, payload, 101);
+  Serial.println(buf);
+  mqttclient.publish(mqtt_topic_out, buf, mqtt_outRetain);
+  yield();
+}
+
+void publishCurrentThermostatValues() {
+  char tmp_topic_out[50];
+
+  updateCurrentHeatingModeName();
+  updateCurrentPresetName();
+
+  char ch_setTemp[6];
+  char ch_currSetTemp[6];
+  dtostrf(setTemp, 1, 1, ch_setTemp );
+  dtostrf(currSetTemp, 1, 1, ch_currSetTemp );
+
+  Serial.print("heatingMode: '");
+  Serial.print(heatingMode);
+  Serial.println("'");
+  Serial.print("set temp: '");
+  Serial.print(ch_setTemp);
+  Serial.println("'");
+  Serial.print("current set temp: '");
+  Serial.print(ch_currSetTemp);
+  Serial.println("'");
+
+  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "setTemp");
+  mqttclient.publish(tmp_topic_out, ch_setTemp);
+  yield();
+
+  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "currSetTemp");
+  mqttclient.publish(tmp_topic_out, ch_currSetTemp);
+  yield();
+
+  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "mode");
+  char ch_heatingMode[3];
+  sprintf(ch_heatingMode, "%d", heatingMode);
+  mqttclient.publish(tmp_topic_out, ch_heatingMode);
+
+  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "modeName");
+  mqttclient.publish(tmp_topic_out, currentModeName);
+
+  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "preset");
+  char ch_preset[3];
+  sprintf(ch_preset, "%d", preset);
+  mqttclient.publish(tmp_topic_out, ch_preset);
+
+  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "presetName");
+  mqttclient.publish(tmp_topic_out, currentPresetName);
+
+  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "presetHA");
+  if(preset == 0) mqttclient.publish(tmp_topic_out, "none");
+  else mqttclient.publish(tmp_topic_out, currentPresetName);
+
+  yield();
+
+  char ch_turnHeatingOn[5];
+  if (turnHeatingOn) strcpy(ch_turnHeatingOn, "on");
+  else strcpy(ch_turnHeatingOn, "off");
+  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "heating");
+  mqttclient.publish(tmp_topic_out, ch_turnHeatingOn);
+  yield();
+
+  char buf[101];
+  sprintf(buf, "%d", heatingOnTime);
+  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "heatingOnTime");
+  mqttclient.publish(tmp_topic_out, buf);
+  yield();
+
+  sprintf(buf, "%d", heatingOffTime);
+  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "heatingOffTime");
+  mqttclient.publish(tmp_topic_out, buf);
+  yield();
+}
+
+void publishCurrentSensorValues() {
+  if ( lastTempUpdate != 0 && (millis() - lastTempUpdate) < 120000 ) {
+    char tmp_topic_out[50];
+
+    char temp_chararr[6];
+    char hum_chararr[4];
+    dtostrf(currTemp, 1, 1, temp_chararr );
+    sprintf(hum_chararr, "%2i", currHum);
+
+    Serial.print("temp: '");
+    Serial.print(temp_chararr);
+    Serial.println("'");
+    sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "temp");
+    mqttclient.publish(tmp_topic_out, temp_chararr);
+    yield();
+
+    Serial.print("hum: '");
+    Serial.print(currHum);
+    Serial.println("'");
+    sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "hum");
+    mqttclient.publish(tmp_topic_out, hum_chararr);
+    yield();
+
+    dtostrf(currTemp_raw, 1, 1, temp_chararr );
+    sprintf(hum_chararr, "%2i", currHum_raw);
+
+    Serial.print("temp_raw: '");
+    Serial.print(temp_chararr);
+    Serial.println("'");
+    sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "temp_raw");
+    mqttclient.publish(tmp_topic_out, temp_chararr);
+    yield();
+
+    Serial.print("hum_raw: '");
+    Serial.print(currHum_raw);
+    Serial.println("'");
+    sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "hum_raw");
+    mqttclient.publish(tmp_topic_out, hum_chararr);
+    yield();
+  }
+}
+
+void publishCurrentPIRValue() {
+  char tmp_topic_out[50];
+  char PIRStatus[4];
+
+  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "PIR");
+
+  if (PIRSensorOn) {
+    strcpy(PIRStatus, "on");
+  }
+  else {
+    strcpy(PIRStatus, "off");
+  }
+  mqttclient.publish(tmp_topic_out, PIRStatus);
+
+  if (strlen(mqtt_topic_pir) > 0) {
+    mqttclient.publish(mqtt_topic_pir, PIRStatus);
+  }
+}
+
+void mqttPublishHeartbeat() {
+  mqttclient.publish(mqtt_topic_in_cmd, "HEARTBEAT"); // publishes to IN-topic. MQTT will force-reconnect if it does not get a HEARTBEAT for 2 min
+}

+ 25 - 25
src/WiFiThermostat/outTempHum.ino

@@ -1,26 +1,26 @@
-// variables - must be declared globally!
-//char outTemp_newValue[6];
-//bool outTemp_parseNewValue;
-//char outHum_newValue[4];
-//bool outHum_parseNewValue;
-
-void outTempHum_updateOnNewValue() {
-  if(outTemp_parseNewValue) outTemp_update();
-  if(outHum_parseNewValue) outHum_update();
-}
-
-void outTemp_update() {
-  outTemp = atof(outTemp_newValue);
-  outTempHumLastUpdate = millis();
-  Serial.print("outTemp=");
-  Serial.println(outTemp);
-  outTemp_parseNewValue = false;
-}
-
-void outHum_update() {
-  outHum = atoi(outHum_newValue);
-  outTempHumLastUpdate = millis();
-  Serial.print("outHum=");
-  Serial.println(outHum);
-  outHum_parseNewValue = false;
+// variables - must be declared globally!
+//char outTemp_newValue[6];
+//boolean outTemp_parseNewValue;
+//char outHum_newValue[4];
+//boolean outHum_parseNewValue;
+
+void outTempHum_updateOnNewValue() {
+  if(outTemp_parseNewValue) outTemp_update();
+  if(outHum_parseNewValue) outHum_update();
+}
+
+void outTemp_update() {
+  outTemp = atof(outTemp_newValue);
+  outTempHumLastUpdate = millis();
+  Serial.print("outTemp=");
+  Serial.println(outTemp);
+  outTemp_parseNewValue = false;
+}
+
+void outHum_update() {
+  outHum = atoi(outHum_newValue);
+  outTempHumLastUpdate = millis();
+  Serial.print("outHum=");
+  Serial.println(outHum);
+  outHum_parseNewValue = false;
 }

+ 387 - 329
src/WiFiThermostat/thermostat.ino

@@ -1,330 +1,388 @@
-void measureTempHum() {
-  float tmpHum = round(dht.readHumidity()) + humCorrVal;
-  float tmpTemp = dht.readTemperature() + tempCorrVal;  // Read temperature as Celsius (the default)
-
-  int tmpHumInt = tmpHum;
-
-  // Check if any reads failed
-  if (isnan(tmpHum) || isnan(tmpTemp)) {
-    //Serial.println("Failed to read from DHT sensor!");
-    sendStatus("Error: Failed to read from DHT sensor!");
-  }
-  else {
-    if (tmpTemp < 50.0 && tmpTemp > -20.0) {
-      // measurement is in range
-      currTemp_raw = tmpTemp;
-      currHum_raw = tmpHumInt;
-      
-      if ( lastTempUpdate > 0 && tmpTemp <= ( currTemp + 2.0 ) && tmpTemp >= ( currTemp - 2.0 ) ) {
-        // temp has already been measured - only accept new measurement if it does not differ much from the last value
-        //Temp = (Temp * (FilterFaktor -1) + AktuellerMesswert) / FilterFaktor;
-        //temperature = tmpTemp;
-        currTemp = (currTemp * 9 + tmpTemp) / 10; // filter
-        currHum = (currHum * 9 + tmpHumInt) / 10; // filter
-        lastTempUpdate = millis();
-      }
-      else if ( lastTempUpdate == 0 || (millis() - lastTempUpdate) > 300000 ) {
-        // this is the first measurement or the last one is older than 5m - then accept this measurement
-        currTemp = tmpTemp + tempCorrVal;
-        currHum = tmpHumInt + humCorrVal;
-        lastTempUpdate = millis();
-
-      }
-      // skip in all other cases
-
-      //#ifdef DEBUG_VERBOSE
-      //      Serial.print("lastTempUpdate: ");
-      //      long lastTempUpdateDelta = millis() - lastTempUpdate;
-      //      Serial.print(lastTempUpdateDelta / 1000);
-      //      Serial.println("s ago");
-      //#endif
-
-      
-    }
-  }
-}
-
-void thermostat() {
-  float curr_setTemp;
-  // set target temp for heating mode
-  if (heatingMode == 1) { // heating on - default/day mode
-    curr_setTemp = setTemp;
-  }
-  else if (heatingMode == 2) { // heating of - night/reduction mode
-    curr_setTemp = setTempLow;
-  }
-  else if (heatingMode == 3) { // heating of - night/reduction mode
-    curr_setTemp = setTempLow2;
-  }
-
-  char tmp_topic_out[50];
-  if (heatingMode > 0 && turnHeatingOn) {
-    heatingOnTime = (millis() - heatingLastOnMillis) / 1000;
-
-    char buf[101];
-    sprintf(buf, "heating on since %d s", heatingOnTime);
-    sendStatus(buf);
-  }
-  else if (heatingMode > 0 && !turnHeatingOn) {
-    heatingOffTime = (millis() - heatingLastOffMillis) / 1000;
-
-    char buf[101];
-    sprintf(buf, "heating off since %d s", heatingOffTime);
-    sendStatus(buf);
-  }
-
-
-  //char tmp_topic_out[50];
-  //sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "heating");
-
-  if ( lastTempUpdate != 0 && (millis() - lastTempUpdate) <= 120000 ) {
-    // thermostat - only active if measured temperature is < 2 min old
-
-#ifdef DEBUG_VERBOSE
-    Serial.print("thermostat, lastTempUpdate=");
-    Serial.print(lastTempUpdate);
-    Serial.print(", lastTempUpdate_delta=");
-    long lastTempUpdateDelta = millis() - lastTempUpdate;
-    Serial.println(lastTempUpdateDelta);
-#endif
-
-    // thermostat with hysteresis
-    if ( turnHeatingOn && currTemp >= (curr_setTemp - setTempDecreaseVal) ) {
-      turnHeatingOn = false;
-      heatingLastOffMillis = millis();
-      digitalWrite(PIN_RELAIS, !RELAISONSTATE);
-      updateDisplay();
-
-      char buf[101];
-      sprintf(buf, "switch heating OFF, on since %d s", heatingOnTime);
-      sendStatus(buf);
-
-      //Serial.println("heating off");
-      //mqttclient.publish(tmp_topic_out, "off");
-      publishCurrentThermostatValues();
-      sendToDomoticz_Heating();
-    }
-    else if ( !turnHeatingOn && heatingMode > 0 && ( currTemp < (curr_setTemp - setTempDecreaseVal - hysteresis) ) && ( heatingOffTime > heatingMinOffTime ) ) {
-      turnHeatingOn = true;
-      heatingLastOnMillis = millis();
-      digitalWrite(PIN_RELAIS, RELAISONSTATE);
-      updateDisplay();
-
-      char buf[101];
-      sprintf(buf, "switch heating ON, off since %d s", heatingOffTime);
-      sendStatus(buf);
-
-      //Serial.println("heating on");
-      //mqttclient.publish(tmp_topic_out, "on");
-      publishCurrentThermostatValues();
-      sendToDomoticz_Heating();
-    }
-  }
-  else {
-    if (turnHeatingOn) {
-      digitalWrite(PIN_RELAIS, !RELAISONSTATE);
-      turnHeatingOn = false;
-      heatingLastOffMillis = millis();
-    }
-
-    if ( lastTempUpdate != 0 ) sendStatus("switch heating OFF, temp reading not yet available");
-    else if ( (millis() - lastTempUpdate) > 120000 ) sendStatus("switch heating OFF, last temp reading too old");
-
-    //mqttclient.publish(tmp_topic_out, "off");
-    publishCurrentThermostatValues();
-    sendToDomoticz_Heating();
-  }
-
-}
-
-void toggleHeatingMode() {
-  if (heatingMode > 0) {
-    Serial.print("switch mode to ");
-    if (heatingMode == 1) {
-      heatingMode = 2;
-      lastValueChange = millis();
-      heatingModeAlreadySaved = false;
-    }
-    else if (heatingMode == 2) {
-      heatingMode = 3;
-      lastValueChange = millis();
-      heatingModeAlreadySaved = false;
-    }
-    else if (heatingMode == 3) {
-      heatingMode = 1;
-      lastValueChange = millis();
-      heatingModeAlreadySaved = false;
-    }
-    Serial.print(heatingMode);
-    Serial.print(" - \"");
-    Serial.print(currentModeName);
-    Serial.println("\"");
-    updateDisplay();
-    updateCurrentHeatingModeName();
-  }
-}
-
-void updateCurrentHeatingModeName() {
-  if(heatingMode == 0) strlcpy(currentModeName, modename0, 14);
-  else if(heatingMode == 1) strlcpy(currentModeName, modename1, 14);
-  else if(heatingMode == 2) strlcpy(currentModeName, modename2, 14);
-  else if(heatingMode == 3) strlcpy(currentModeName, modename3, 14);
-}
-
-void toggleThermostatOnOff() {
-  if (heatingMode > 0) {
-    heatingMode = 0;
-    lastValueChange = millis();
-    heatingModeAlreadySaved = false;
-  }
-  else {
-    heatingMode = 1;
-    lastValueChange = millis();
-    heatingModeAlreadySaved = false;
-  }
-  updateDisplay();
-  updateCurrentHeatingModeName();
-}
-
-void setTempStepUp() {
-  if (heatingMode == 1) {
-    Serial.println("setTemp +0.5");
-    if ( setTemp <= (setTempMax - 0.5)) {
-      setTemp += 0.5;
-      lastValueChange = millis();
-      setTempAlreadySaved = false;
-    }
-    updateDisplay();
-  }
-}
-
-void setTempStepDown() {
-  if (heatingMode == 1) {
-    Serial.println("setTemp -0.5");
-    if ( setTemp >= (setTempMin + 0.5)) {
-      setTemp -= 0.5;
-      lastValueChange = millis();
-      setTempAlreadySaved = false;
-    }
-    updateDisplay();
-  }
-}
-
-void setTempTo(float setTo) {
-  bool changes = false;
-  if (setTo >= setTempMin && setTo <= setTempMax) {
-    setTemp = setTo;
-    changes = true;
-  }
-  else if (setTo > setTempMax) {
-    setTemp = setTempMax;
-    changes = true;
-  }
-  else if (setTo < setTempMin) {
-    setTemp = setTempMin;
-    changes = true;
-  }
-  if (changes) {
-    lastValueChange = millis();
-    setTempAlreadySaved = false;
-    updateDisplay();
-    publishCurrentThermostatValues();
-  }
-}
-
-void setTempLowTo(float setTo) {
-  bool changes = false;
-  if (setTo >= setTempLowMin && setTo <= setTempLowMax) {
-    setTempLow = setTo;
-    changes = true;
-  }
-  else if (setTo > setTempLowMax) {
-    setTempLow = setTempLowMax;
-    changes = true;
-  }
-  else if (setTo < setTempLowMin) {
-    setTempLow = setTempLowMin;
-    changes = true;
-  }
-  if (changes) {
-    updateDisplay();
-    publishCurrentThermostatValues();
-  }
-}
-
-void setTempLow2To(float setTo) {
-  bool changes = false;
-  if (setTo >= setTempLowMin && setTo <= setTempLowMax) {
-    setTempLow2 = setTo;
-    changes = true;
-  }
-  else if (setTo > setTempLowMax) {
-    setTempLow2 = setTempLowMax;
-    changes = true;
-  }
-  else if (setTo < setTempLowMin) {
-    setTempLow2 = setTempLowMin;
-    changes = true;
-  }
-  if (changes) {
-    updateDisplay();
-    publishCurrentThermostatValues();
-  }
-}
-
-void setHeatingmodeTo(byte setTo) {
-  bool changes = false;
-  switch (setTo) {
-    case 0:
-      heatingMode = 0;
-      changes = true;
-      break;
-    case 1:
-      heatingMode = 1;
-      changes = true;
-      break;
-    case 2:
-      heatingMode = 2;
-      changes = true;
-      break;
-    case 3:
-      heatingMode = 3;
-      changes = true;
-      break;
-  }
-  if (changes) {
-    lastValueChange = millis();
-    heatingModeAlreadySaved = false;
-    updateCurrentHeatingModeName();
-    updateDisplay();
-    publishCurrentThermostatValues();
-  }
-}
-
-void checkValuesChanged() { // called every second by everySecond() / misc.ino
-  if ( !setTempAlreadySaved || !heatingModeAlreadySaved ) {
-    if ( (millis() - lastValueChange) > saveValuesTimeout ) { // value was changed 5s ago. now save if auto-save enabled
-      publishCurrentThermostatValues();
-      if (!setTempAlreadySaved) {
-        lastUpdate_setTemp = millis();
-        sendToDomoticz_thermostat();
-        if (autoSaveSetTemp && setTemp != setTempSaved) {
-          saveSetTemp();
-          sendStatus("setTemp autosave done");
-        }
-        setTempAlreadySaved = true;
-      }
-      if (!heatingModeAlreadySaved) {
-        lastUpdate_heatingMode = millis();
-        sendToDomoticz_heatingMode();
-        if (autoSaveHeatingMode && heatingMode != heatingModeSaved) {
-          saveHeatingMode();
-          sendStatus("heatingMode autosave done");
-        }
-        heatingModeAlreadySaved = true;
-      }
-    }
-  }
-}
-
+void measureTempHum() {
+  float tmpHum = round(dht.readHumidity()) + humCorrVal;
+  float tmpTemp = dht.readTemperature() + tempCorrVal;  // Read temperature as Celsius (the default)
 
+  int tmpHumInt = tmpHum;
+
+  // Check if any reads failed
+  if (isnan(tmpHum) || isnan(tmpTemp)) {
+    //Serial.println("Failed to read from DHT sensor!");
+    sendStatus("Error: Failed to read from DHT sensor!");
+  }
+  else {
+    if (tmpTemp < 50.0 && tmpTemp > -20.0) {
+      // measurement is in range
+      currTemp_raw = tmpTemp;
+      currHum_raw = tmpHumInt;
+
+      if ( lastTempUpdate > 0 && tmpTemp <= ( currTemp + 2.0 ) && tmpTemp >= ( currTemp - 2.0 ) ) {
+        // temp has already been measured - only accept new measurement if it does not differ much from the last value
+        //Temp = (Temp * (FilterFaktor -1) + AktuellerMesswert) / FilterFaktor;
+        //temperature = tmpTemp;
+        currTemp = (currTemp * 9 + tmpTemp) / 10; // filter
+        currHum = (currHum * 9 + tmpHumInt) / 10; // filter
+        lastTempUpdate = millis();
+      }
+      else if ( lastTempUpdate == 0 || (millis() - lastTempUpdate) > 300000 ) {
+        // this is the first measurement or the last one is older than 5m - then accept this measurement
+        currTemp = tmpTemp + tempCorrVal;
+        currHum = tmpHumInt + humCorrVal;
+        lastTempUpdate = millis();
+
+      }
+      // skip in all other cases
+
+      //#ifdef DEBUG_VERBOSE
+      //      Serial.print("lastTempUpdate: ");
+      //      long lastTempUpdateDelta = millis() - lastTempUpdate;
+      //      Serial.print(lastTempUpdateDelta / 1000);
+      //      Serial.println("s ago");
+      //#endif
+
+
+    }
+  }
+}
+
+void updateCurrSetTemp() {
+  // set target temp for heating mode
+  if (heatingMode > 0) { // heating on
+    if (preset == 0) { // normal/day preset
+      currSetTemp = setTemp;
+    }
+    else if (preset == 1) { // night/reduction preset
+      currSetTemp = setTempLow;
+    }
+    else if (preset == 2) { // night/reduction 2 preset
+      currSetTemp = setTempLow2;
+    }
+  }
+  else { // if heatingMode == 0
+    currSetTemp = DEFAULT_SETTEMP_HEATOFF;
+  }
+}
+
+void thermostat() {
+  updateCurrSetTemp();
+  char tmp_topic_out[50];
+  if (heatingMode > 0 && turnHeatingOn) {
+    heatingOnTime = (millis() - heatingLastOnMillis) / 1000;
+
+    char buf[101];
+    sprintf(buf, "heating on since %d s", heatingOnTime);
+    sendStatus(buf);
+  }
+  else if (heatingMode > 0 && !turnHeatingOn) {
+    heatingOffTime = (millis() - heatingLastOffMillis) / 1000;
+
+    char buf[101];
+    sprintf(buf, "heating off since %d s", heatingOffTime);
+    sendStatus(buf);
+  }
+
+
+  //char tmp_topic_out[50];
+  //sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "heating");
+
+  if ( lastTempUpdate != 0 && (millis() - lastTempUpdate) <= 120000 ) {
+    // thermostat - only active if measured temperature is < 2 min old
+
+#ifdef DEBUG_VERBOSE
+    Serial.print("thermostat, lastTempUpdate=");
+    Serial.print(lastTempUpdate);
+    Serial.print(", lastTempUpdate_delta=");
+    long lastTempUpdateDelta = millis() - lastTempUpdate;
+    Serial.println(lastTempUpdateDelta);
+#endif
+
+    // thermostat with hysteresis
+    if ( turnHeatingOn && currTemp >= (currSetTemp - setTempDecreaseVal) ) {
+      turnHeatingOn = false;
+      heatingLastOffMillis = millis();
+      digitalWrite(PIN_RELAIS, !RELAISONSTATE);
+      updateDisplay();
+
+      char buf[101];
+      sprintf(buf, "switch heating OFF, on since %d s", heatingOnTime);
+      sendStatus(buf);
+
+      //Serial.println("heating off");
+      //mqttclient.publish(tmp_topic_out, "off");
+      publishCurrentThermostatValues();
+      sendToDomoticz_Heating();
+    }
+    else if ( !turnHeatingOn && heatingMode > 0 && ( currTemp < (currSetTemp - setTempDecreaseVal - hysteresis) ) && ( heatingOffTime > heatingMinOffTime ) ) {
+      turnHeatingOn = true;
+      heatingLastOnMillis = millis();
+      digitalWrite(PIN_RELAIS, RELAISONSTATE);
+      updateDisplay();
+
+      char buf[101];
+      sprintf(buf, "switch heating ON, off since %d s", heatingOffTime);
+      sendStatus(buf);
+
+      //Serial.println("heating on");
+      //mqttclient.publish(tmp_topic_out, "on");
+      publishCurrentThermostatValues();
+      sendToDomoticz_Heating();
+    }
+  }
+  else {
+    if (turnHeatingOn) {
+      digitalWrite(PIN_RELAIS, !RELAISONSTATE);
+      turnHeatingOn = false;
+      heatingLastOffMillis = millis();
+    }
+
+    if ( lastTempUpdate != 0 ) sendStatus("switch heating OFF, temp reading not yet available");
+    else if ( (millis() - lastTempUpdate) > 120000 ) sendStatus("switch heating OFF, last temp reading too old");
+
+    //mqttclient.publish(tmp_topic_out, "off");
+    publishCurrentThermostatValues();
+    sendToDomoticz_Heating();
+  }
+
+}
+
+void toggleOnOff() {
+  if (heatingMode > 0) {
+    heatingMode = 0;
+    lastValueChange = millis();
+    heatingModeAlreadySaved = false;
+  }
+  else {
+    heatingMode = 1;
+    lastValueChange = millis();
+    heatingModeAlreadySaved = false;
+  }
+  updateCurrentHeatingModeName();
+  updateDisplay();
+}
+
+void togglePreset() {
+  Serial.print("switch preset to ");
+  if (pendingPreset < 0 || pendingPreset > 2) pendingPreset = preset;
+
+  //    if (pendingPresetToggle && preset == 0 && pendingPreset == 0) pendingPreset = 1;
+  //    else if (pendingPresetToggle && preset == 1 && pendingPreset == 1) pendingPreset = 0;
+  //    else if (pendingPreset == 1 && pendingPresetToggle) pendingPreset = 2;
+  //    else if (pendingPreset == 2 && !pendingPresetToggle) pendingPreset = 0;
+  //    else if (pendingPreset == 2 && pendingPresetToggle) pendingPreset = 1;
+
+  if (pendingPreset == 0) pendingPreset = 1;
+  else if (pendingPreset == 1) pendingPreset = 2;
+  else if (pendingPreset == 2) pendingPreset = 0;
+
+  //  if (preset == 0 && pendingPreset == 0) pendingPreset = 1;
+  //  else if (preset == 0 && pendingPreset == 1) pendingPreset = 2;
+  //  else if (preset == 1 && pendingPreset == 0) pendingPreset = 2;
+  //  else if (preset == 1 && pendingPreset == 1) pendingPreset = 0;
+  //  else if (preset == 1 && pendingPreset == 2) pendingPreset = 1;
+  //  else if (preset == 1 && pendingPreset == 0) pendingPreset = 1;
+  //  else if (preset == 2 && pendingPreset == 0) pendingPreset = 1;
+  //  else if (preset == 2 && pendingPreset == 1) pendingPreset = 0;
+  //  else if (preset == 2 && pendingPreset == 2) pendingPreset = 0;
+
+
+  lastValueChange = millis();
+  presetAlreadySaved = false;
+
+  updatePendingPresetName();
+  //updateDisplay();
+  //pendingPresetToggle = true;
+  displayShowLine2OverlayMessage(pendingPresetName);
+  Serial.print(pendingPreset);
+  Serial.print(" - \"");
+  Serial.print(currentPresetName);
+  Serial.println("\"");
+}
+
+void updateCurrentHeatingModeName() {
+  if (heatingMode == 0) strlcpy(currentModeName, modename0, 14);
+  else if (heatingMode == 1) strlcpy(currentModeName, modename1, 14);
+}
+
+void updateCurrentPresetName() {
+  if (preset == 0) strlcpy(currentPresetName, psetname0, 14);
+  else if (preset == 1) strlcpy(currentPresetName, psetname1, 14);
+  else if (preset == 2) strlcpy(currentPresetName, psetname2, 14);
+}
+
+void updatePendingPresetName() {
+  if (pendingPreset == 0) strlcpy(pendingPresetName, psetname0, 14);
+  else if (pendingPreset == 1) strlcpy(pendingPresetName, psetname1, 14);
+  else if (pendingPreset == 2) strlcpy(pendingPresetName, psetname2, 14);
+}
+
+void setTempStepUp() {
+  if (heatingMode == 1) {
+    Serial.println("setTemp +0.5");
+    if ( setTemp <= (setTempMax - 0.5)) {
+      setTemp += 0.5;
+      lastValueChange = millis();
+      setTempAlreadySaved = false;
+    }
+    updateDisplay();
+  }
+}
+
+void setTempStepDown() {
+  if (heatingMode == 1) {
+    Serial.println("setTemp -0.5");
+    if ( setTemp >= (setTempMin + 0.5)) {
+      setTemp -= 0.5;
+      lastValueChange = millis();
+      setTempAlreadySaved = false;
+    }
+    updateDisplay();
+  }
+}
+
+void setTempTo(float setTo) {
+  boolean changes = false;
+  if (setTo >= setTempMin && setTo <= setTempMax) {
+    setTemp = setTo;
+    changes = true;
+  }
+  else if (setTo > setTempMax) {
+    setTemp = setTempMax;
+    changes = true;
+  }
+  else if (setTo < setTempMin) {
+    setTemp = setTempMin;
+    changes = true;
+  }
+  if (changes) {
+    lastValueChange = millis();
+    setTempAlreadySaved = false;
+    updateDisplay();
+    publishCurrentThermostatValues();
+  }
+}
+
+void setTempLowTo(float setTo) {
+  boolean changes = false;
+  if (setTo >= setTempLowMin && setTo <= setTempLowMax) {
+    setTempLow = setTo;
+    changes = true;
+  }
+  else if (setTo > setTempLowMax) {
+    setTempLow = setTempLowMax;
+    changes = true;
+  }
+  else if (setTo < setTempLowMin) {
+    setTempLow = setTempLowMin;
+    changes = true;
+  }
+  if (changes) {
+    updateDisplay();
+    publishCurrentThermostatValues();
+  }
+}
+
+void setTempLow2To(float setTo) {
+  boolean changes = false;
+  if (setTo >= setTempLowMin && setTo <= setTempLowMax) {
+    setTempLow2 = setTo;
+    changes = true;
+  }
+  else if (setTo > setTempLowMax) {
+    setTempLow2 = setTempLowMax;
+    changes = true;
+  }
+  else if (setTo < setTempLowMin) {
+    setTempLow2 = setTempLowMin;
+    changes = true;
+  }
+  if (changes) {
+    updateDisplay();
+    publishCurrentThermostatValues();
+  }
+}
+
+void setHeatingmodeTo(byte setTo) {
+  boolean changes = false;
+  switch (setTo) {
+    case 0:
+      heatingMode = 0;
+      changes = true;
+      break;
+    case 1:
+      heatingMode = 1;
+      changes = true;
+      break;
+  }
+  if (changes) {
+    updateCurrSetTemp();
+    lastValueChange = millis();
+    heatingModeAlreadySaved = false;
+    updateCurrentHeatingModeName();
+    updateDisplay();
+    publishCurrentThermostatValues();
+  }
+}
+
+void setPresetTo(byte setTo) {
+  boolean changes = false;
+  switch (setTo) {
+    case 0:
+      preset = 0;
+      pendingPreset = 0;
+      changes = true;
+      break;
+    case 1:
+      preset = 1;
+      pendingPreset = 1;
+      changes = true;
+      break;
+    case 2:
+      preset = 2;
+      pendingPreset = 2;
+      changes = true;
+      break;
+  }
+  if (changes) {
+    updateCurrSetTemp();
+    lastValueChange = millis();
+    presetAlreadySaved = false;
+    updateCurrentPresetName();
+    updateDisplay();
+    publishCurrentThermostatValues();
+  }
+}
+
+void checkValuesChanged() { // called every second by everySecond() / misc.ino
+  if ( !setTempAlreadySaved || !heatingModeAlreadySaved || !presetAlreadySaved) {
+    if ( (millis() - lastValueChange) > saveValuesTimeout ) { // value was changed 5s ago. now save if auto-save enabled
+      if (!setTempAlreadySaved) {
+        lastUpdate_setTemp = millis();
+        sendToDomoticz_thermostat();
+        if (autoSaveSetTemp && setTemp != setTempSaved) {
+          saveSetTemp();
+          sendStatus("setTemp autosave done");
+        }
+        setTempAlreadySaved = true;
+      }
+      if (!heatingModeAlreadySaved) {
+        lastUpdate_heatingMode = millis();
+        sendToDomoticz_heatingMode();
+        if (autoSaveHeatingMode && heatingMode != heatingModeSaved) {
+          saveHeatingMode();
+          sendStatus("heatingMode autosave done");
+        }
+        heatingModeAlreadySaved = true;
+      }
+      if (!presetAlreadySaved) {
+        lastUpdate_preset = millis();
+        //sendToDomoticz_heatingMode();
+        preset = pendingPreset;
+        if (autoSaveHeatingMode && preset != presetSaved) {
+          savePreset();
+          sendStatus("preset autosave done");
+        }
+        presetAlreadySaved = true;
+      }
+      publishCurrentThermostatValues();
+    }
+  }
+}