WiFiThermostat.ino 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. // PRE-COMPILE CONFIGURATION
  2. //#define FORCE_SPIFFS_FORMAT
  3. //#define DEBUG_VERBOSE
  4. #define DEBUG_SERIAL true
  5. #define DEBUG_MQTT true
  6. #define SPIFFS_DBG
  7. #define SPIFFS_USE_MAGIC
  8. #define FIRMWARE_NAME "WiFiThermostat"
  9. #define FIRMWARE_VERSION "0.4.0"
  10. #define FIRMWARE_URL "https://git.flokra.at/flo/WiFiThermostat"
  11. #define FIRMWARE_COPYRIGHT "FloKra"
  12. #define FIRMWARE_COPYRIGHT_URL "https://www.flokra.at/"
  13. // default values, can later be overridden via configuration
  14. #define MAX_MEASUREMENT_AGE 30000 // in ms, for measured inside Temp/Hum
  15. #define MAX_MEASUREMENT_AGE_OUT 300000 // in ms, for received outside Temp/Hum
  16. //confDev
  17. #define DEVICE_NAME "WiFiThermo-1"
  18. #define HOST_NAME ""
  19. #define WIFI_APMODE_PASSWORD "LassMiRein"
  20. //confWeb
  21. #define HTTP_SET_TOKEN "grzbrz"
  22. #define DEFAULT_HTTP_USER ""
  23. #define DEFAULT_HTTP_PASS ""
  24. //confMqtt
  25. #define MQTT_ENABLE true
  26. #define MQTT_SERVER "mqtt.lan"
  27. #define MQTT_PORT 1883
  28. #define MQTT_USER ""
  29. #define MQTT_PASS ""
  30. #define MQTT_TOPIC_IN "Test/Thermostat"
  31. #define MQTT_TOPIC_OUT "Test/Thermostat/stat"
  32. #define MQTT_OUT_RETAIN false
  33. #define MQTT_WILLTOPIC "Test/Thermostat/availability"
  34. #define MQTT_WILLQOS 2
  35. #define MQTT_WILLRETAIN false
  36. #define MQTT_WILLMSG "offline"
  37. #define MQTT_CONNMSG "online"
  38. #define MQTT_ENABLE_HEARTBEAT true
  39. #define MQTT_HEARTBEAT_MAXAGE 180000 // max interval for MQTT heartbeat message. only applicable if MQTT IN-topic is defined. after this timeout MQTT reconnect is forced
  40. #define MQTT_HEARTBEAT_MAXAGE_REBOOT 1800000 // max interval for MQTT heartbeat message. only applicable if MQTT IN-topic is defined. after this timeout the ESP will reboot
  41. //confBas
  42. #define DEFAULT_SETTEMP_MIN 16.0 // minimal temperature that can be set
  43. #define DEFAULT_SETTEMP_MAX 26.0 // maximal temperature that can be set
  44. #define AUTOSAVE_SETTEMP true
  45. #define AUTOSAVE_SETMODE true
  46. #define DEFAULT_MEASURE_INTERVAL 15 // interval for temp/hum measurement
  47. #define DEFAULT_DISPLAY_INTERVAL 5 // interval for display updates (if out-temp is active, display will toggle in this interval)
  48. #define DEFAULT_DISPLAY_TIMEOUT 30 // display timeout after keypress (illumination)
  49. #define DEFAULT_PIR_ENABLES_DISPLAY false
  50. #define DEFAULT_TOGGLING_I_O_TEMPHUM false
  51. //confAdv
  52. #define DEFAULT_HYSTERESIS 0.2 // hysteresis, normally 0.1 - 0.5
  53. #define DEFAULT_HEATING_MIN_OFFTIME 120 // minimal time the heating keeps turned off in s
  54. #define TEMPSENSOR_CORRECTION_VALUE 0.0 // correction value for temperature sensor reading
  55. #define HUMSENSOR_CORRECTION_VALUE 0 // correction value for humidity sensor reading
  56. #define SETTEMP_DECREASE_VALUE 0.0 // decreases the set temp to overcome further temperature rise when the heating is already switched off
  57. #define OFF_MESSAGE "HEATING OFF"
  58. #define INSIDE_TEMP_LABEL "I"
  59. #define OUTSIDE_TEMP_LABEL "O"
  60. #define MODE_NAME_0 "off"
  61. #define MODE_NAME_1 "heat"
  62. #define PRESET_NAME_0 "none"
  63. #define PRESET_NAME_1 "reduction1"
  64. #define PRESET_NAME_2 "reduction2"
  65. //confAdd
  66. #define OUTTEMP_TOPIC_IN ""
  67. #define OUTHUM_TOPIC_IN ""
  68. #define MQTT_TOPIC_PIR "" // extra publish topic for PIR sensor
  69. #define MQTT_TOPIC_PIR_ON "ON"
  70. #define MQTT_TOPIC_PIR_OFF "OFF"
  71. // other (not configurable via WebIF)
  72. #define DEFAULT_SETTEMP_HEATOFF 5.0 // set temperature in OFF mode (freezing guard > 0)
  73. // default initial values
  74. #define DEFAULT_SETTEMP 21.5
  75. #define DEFAULT_HEATINGMODE 1
  76. #define DEFAULT_PRESET 1
  77. #define DEFAULT_SETTEMP_LOW 20.0 // set temperature in night/low mode
  78. #define DEFAULT_SETTEMP_LOW2 18.0 // set temperature in night/low mode
  79. // default values that can only be configured at compile time / hardware configuration
  80. #define BUTTON_DEBOUNCE_TIME 120
  81. #define BUTTON_HOLD_TIME 750
  82. #define SETTEMP_LOW_MIN 14.0 // minimal configurable temperature for reduction mode
  83. #define SETTEMP_LOW_MAX 21.5 // maximal configurable temperature for reduction mode
  84. // pin assignments and I2C addresses
  85. #define PIN_DHTSENSOR 13
  86. #define PIN_RELAIS 15 //16
  87. #define PIN_BUTTON_PLUS 2
  88. #define PIN_BUTTON_MINUS 0
  89. #define PIN_BUTTON_MODE 14
  90. #define PIN_PIRSENSOR 12
  91. #define DHTTYPE DHT22 // DHT sensor type
  92. #define LCDADDR 0x27 // I2C address LCD
  93. #define LCDCOLS 16
  94. #define LCDLINES 2
  95. // default logic levels
  96. #define RELAISONSTATE HIGH
  97. #define BUTTONONSTATE LOW
  98. // END PRE-COMPILE CONFIGURATION
  99. //---------------------------------------------------------------------------------------------------------------------------------------------
  100. #include <Button.h>
  101. #include <ButtonEventCallback.h>
  102. #include <PushButton.h>
  103. #include <Bounce2.h>
  104. PushButton buttonPlus = PushButton(PIN_BUTTON_PLUS, ENABLE_INTERNAL_PULLUP);
  105. PushButton buttonMinus = PushButton(PIN_BUTTON_MINUS, ENABLE_INTERNAL_PULLUP);
  106. PushButton buttonMode = PushButton(PIN_BUTTON_MODE, ENABLE_INTERNAL_PULLUP);
  107. PushButton pirSensor = PushButton(PIN_PIRSENSOR, PRESSED_WHEN_HIGH);
  108. #include <PersWiFiManager.h>
  109. #include <ArduinoJson.h>
  110. #include <ESP8266WiFi.h>
  111. #include <WiFiClient.h>
  112. #include <ESP8266WebServer.h>
  113. #include <ESP8266mDNS.h>
  114. #include <ESP8266HTTPUpdateServer.h>
  115. #include "PubSubClient.h"
  116. #include <DNSServer.h>
  117. #include <FS.h>
  118. #include <Wire.h>
  119. #include "LiquidCrystal_I2C.h"
  120. #include <DHT.h>
  121. #include <string.h>
  122. boolean serialdebug = DEBUG_SERIAL;
  123. boolean mqttdebug = DEBUG_MQTT;
  124. boolean WifiInApMode = false;
  125. unsigned long WifiApModeStartedAt;
  126. // config variables - do not change here!
  127. // confDev
  128. char deviceName[31]; // device name - for web interface and AP-Mode SSID
  129. char hostName[31]; // announced hostname on WiFi connection
  130. char wifiAPModePassword[31];
  131. // confWeb
  132. char http_token[31];
  133. char http_user[31];
  134. char http_pass[31];
  135. boolean http_user_auth = false;
  136. char http_user1[31];
  137. char http_pass1[31];
  138. char http_user2[31];
  139. char http_pass2[31];
  140. // confMqtt
  141. boolean mqtt_enable = MQTT_ENABLE;
  142. char mqtt_server[41];
  143. int mqtt_port = MQTT_PORT;
  144. char mqtt_user[31];
  145. char mqtt_pass[31];
  146. char mqtt_topic_in[51]; // MQTT in topic for commands
  147. char mqtt_topic_out[51]; // MQTT out base topic, will be extended by various value names
  148. char mqtt_willTopic[51]; // MQTT Last Will topic
  149. int mqtt_willQos = MQTT_WILLQOS; // MQTT Last Will topic QOS
  150. boolean mqtt_willRetain = MQTT_WILLRETAIN; // MQTT Last Will retain
  151. char mqtt_willMsg[31]; // MQTT Last Will payload
  152. char mqtt_connMsg[31];
  153. boolean mqtt_outRetain = MQTT_OUT_RETAIN; // send MQTT out with retain flag
  154. boolean mqtt_enable_heartbeat = MQTT_ENABLE_HEARTBEAT;
  155. unsigned long mqtt_heartbeat_maxage_reconnect = MQTT_HEARTBEAT_MAXAGE;
  156. unsigned long mqtt_heartbeat_maxage_reboot = MQTT_HEARTBEAT_MAXAGE_REBOOT;
  157. // confBas
  158. float setTempMin = DEFAULT_SETTEMP_MIN; // minimal temperature that can be set
  159. float setTempMax = DEFAULT_SETTEMP_MAX; // maximal temperature that can be set
  160. boolean autoSaveSetTemp = AUTOSAVE_SETTEMP;
  161. boolean autoSaveHeatingMode = AUTOSAVE_SETMODE;
  162. int measureInterval = DEFAULT_MEASURE_INTERVAL; // interval for temp/hum measurement
  163. int displayInterval = DEFAULT_DISPLAY_INTERVAL; // interval for display updates (if out-temp is active, display will toggle in this interval)
  164. int displayInterval_saved = DEFAULT_DISPLAY_INTERVAL;
  165. int displayTimeout = DEFAULT_DISPLAY_TIMEOUT; // display timeout after keypress (illumination)
  166. boolean PIR_enablesDisplay = DEFAULT_PIR_ENABLES_DISPLAY; // PIR sensor enables display illumination
  167. boolean togglingTempHumAIDisplay = DEFAULT_TOGGLING_I_O_TEMPHUM;
  168. // confAdv
  169. float hysteresis = DEFAULT_HYSTERESIS; // hysteresis, normally 0.1 - 0.5
  170. int heatingMinOffTime = DEFAULT_HEATING_MIN_OFFTIME; // minimal time the heating keeps turned off in s
  171. float tempCorrVal = TEMPSENSOR_CORRECTION_VALUE; // correction value for temperature sensor reading
  172. int humCorrVal = HUMSENSOR_CORRECTION_VALUE; // correction value for humidity sensor reading
  173. float setTempDecreaseVal = SETTEMP_DECREASE_VALUE; // decreases the set temp to overcome further temperature rise when the heating is already switched off
  174. char offMessage[15];
  175. char itemplab[2];
  176. char otemplab[2];
  177. char modename0[15];
  178. char modename1[15];
  179. char psetname0[15];
  180. char psetname1[15];
  181. char psetname2[15];
  182. // confAdd
  183. char mqtt_topic_pir[51];
  184. char mqtt_payload_pir_on[10];
  185. char mqtt_payload_pir_off[10];
  186. char outTemp_topic_in[51];
  187. char outHum_topic_in[51];
  188. boolean mqtt_tempDisabled_credentialError = false;
  189. char mqtt_topic_in_cmd[61];
  190. char mqtt_topic_in_setTemp[61];
  191. char mqtt_topic_in_setMode[61];
  192. char mqtt_topic_in_setPreset[61];
  193. // set values
  194. float setTemp = DEFAULT_SETTEMP;
  195. float setTempLow = DEFAULT_SETTEMP_LOW; // set temperature in night/low mode
  196. float setTempLow2 = DEFAULT_SETTEMP_LOW2; // set temperature in night/low mode
  197. float currSetTemp = DEFAULT_SETTEMP;
  198. byte heatingMode = DEFAULT_HEATINGMODE; // 0 = off, 1 = heat
  199. byte preset = DEFAULT_PRESET; // 0 = normal/day, 1 = night/reduction 1, 2 = reduction 2 (long term leave)
  200. // saved set values (same as above, held in memory additionally to prevent saving unchanged values)
  201. float setTempSaved;
  202. float setTempLowSaved;
  203. float setTempLow2Saved;
  204. byte heatingModeSaved;
  205. byte presetSaved;
  206. // global pre set conf variables - not changeable via configuration
  207. float setTempLowMin = SETTEMP_LOW_MIN;
  208. float setTempLowMax = SETTEMP_LOW_MAX;
  209. int debounceTime = BUTTON_DEBOUNCE_TIME;
  210. int buttonHoldTime = BUTTON_HOLD_TIME;
  211. int maxMeasurementAge = MAX_MEASUREMENT_AGE;
  212. int maxMeasurementAgeOut = MAX_MEASUREMENT_AGE_OUT;
  213. // global variables for program flow
  214. float currTemp; // last reading from DHT sensor with smoothing
  215. float currTemp_raw; // last reading from DHT sensor (raw)
  216. int currHum; // last reading from DHT sensor with smoothing
  217. int currHum_raw; // last reading from DHT sensor (raw)
  218. boolean turnHeatingOn = false; // true if heating is active (relais switched on)
  219. unsigned long heatingLastOnMillis; // last time heating was switched on
  220. unsigned long heatingLastOffMillis; // last time heating was switched off
  221. float outTemp; // outside temp (via MQTT if enabled and in-topic configured)
  222. int outHum; // outside temp (via MQTT if enabled and in-topic configured)
  223. long outTempHumLastUpdate; // last reading from out temp/hum source
  224. char outTemp_newValue[6];
  225. boolean outTemp_parseNewValue;
  226. char outHum_newValue[4];
  227. boolean outHum_parseNewValue;
  228. char currentModeName[15];
  229. char currentPresetName[15];
  230. 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
  231. unsigned long lastMeasure = 0; // millis of last temp/hum measurement
  232. unsigned long lastDisplayUpdate = 0; // millis of last display update
  233. unsigned long lastDisplayToggle = 0; // millis of last display toggle
  234. unsigned long lastTempUpdate = 0; // last update time of DHT reading
  235. char msg[50]; // buffer MQTT in payload
  236. char topic[50]; // buffer MQTT in topic
  237. 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
  238. boolean PIRSensorOn = false;
  239. unsigned long heatingOnTime, heatingOffTime;
  240. unsigned long lastUpdate_setTemp = 0; // set to millis() every time setTemp value is changed
  241. unsigned long lastUpdate_setTempLow = 0; // set to millis() every time setTemp value is changed
  242. unsigned long lastUpdate_setTempLow2 = 0; // set to millis() every time setTemp value is changed
  243. unsigned long lastUpdate_heatingMode = 0; // set to millis() every time heatingMode value is changed
  244. unsigned long lastUpdate_preset = 0; // set to millis() every time preset value is changed
  245. char cmdPayload[101]; // buffer for commands
  246. boolean cmdInQueue = false; // command is queued and will be processed next loop() run
  247. boolean saveConfigToFlash = false; // conf is saved in next loop() run
  248. boolean saveConfig2ToFlash = false; // conf2 is saved in next loop() run
  249. unsigned int saveValuesTimeout = 5000;
  250. unsigned long lastValueChange; // is set to millis() whenever setTemp value and/or heatingMode value is changed. used for autoSave function with hardcoded 5s timeout
  251. boolean setTempAlreadySaved = true; // only save if not yet done
  252. boolean setTempLowAlreadySaved = true; // only save if not yet done
  253. boolean setTempLow2AlreadySaved = true; // only save if not yet done
  254. boolean heatingModeAlreadySaved = true; // only save if not yet done
  255. boolean presetAlreadySaved = true; // only save if not yet done
  256. unsigned long lastRun = 0;
  257. int count100ms = 0;
  258. int countSeconds = 0;
  259. int countMeasureInterval = 0;
  260. int countDisplayInterval = 0;
  261. int displayOverlayMsgTimeout = 2;
  262. unsigned long mqttLastReconnectAttempt = 0;
  263. int mqttReconnectAttempts = 0;
  264. int mqttReconnects = 0;
  265. boolean mqttConnected = false;
  266. unsigned long mqttLastHeartbeat;
  267. boolean mqttInTopicSubscribed = false;
  268. boolean pendingRestart = false;
  269. boolean doRestart = false;
  270. unsigned long pendingRestart_lastMillis = 0;
  271. boolean displayShowFullscreenMsg = false;
  272. //unsigned long displayShowFullscreenMsg_lastMillis = 0;
  273. boolean pendingPresetToggle = false;
  274. boolean updateDisplayImmediately = false;
  275. int pendingPreset;
  276. char pendingPresetName[15];
  277. //unsigned long pendingPreset_millis = 0;
  278. //int pendingPreset_timeout = 2000;
  279. boolean displayShowLine2OverlayMsg = false;
  280. // build Uptime String (unnecessary, i know ;) )
  281. unsigned int sysUptime_days = 0;
  282. unsigned int sysUptime_hours = 0;
  283. unsigned int sysUptime_mins = 0;
  284. char uptimeStr[15];
  285. DHT dht(PIN_DHTSENSOR, DHTTYPE);
  286. LiquidCrystal_I2C lcd(LCDADDR, LCDCOLS, LCDLINES);
  287. WiFiClient espClient;
  288. void mqttCallback(char* topic, byte* payload, unsigned int length);
  289. PubSubClient mqttclient(espClient);
  290. ESP8266WebServer httpServer(80);
  291. DNSServer dnsServer;
  292. PersWiFiManager persWM(httpServer, dnsServer);
  293. ESP8266HTTPUpdateServer httpUpdater;
  294. void setup() {
  295. Serial.begin(115200);
  296. delay(500);
  297. Serial.println();
  298. Serial.print(FIRMWARE_NAME);
  299. Serial.print(" v");
  300. Serial.print(FIRMWARE_VERSION);
  301. Serial.println(" starting...");
  302. pinMode(PIN_RELAIS, OUTPUT);
  303. digitalWrite(PIN_RELAIS, !RELAISONSTATE);
  304. pinMode(PIN_PIRSENSOR, INPUT);
  305. buttonPlus.configureButton(configurePushButton);
  306. //buttonPlus.onPress(onButtonPressed); // When the button is first pressed, call the function onButtonPressed (further down the page)
  307. 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
  308. buttonPlus.onRelease(50, 500, onButtonReleased); // When the button is held >50ms and released after <500ms, call onButtonReleased
  309. buttonMinus.configureButton(configurePushButton);
  310. //buttonMinus.onPress(onButtonPressed); // When the button is first pressed, call the function onButtonPressed (further down the page)
  311. 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
  312. buttonMinus.onRelease(50, 500, onButtonReleased); // When the button is held >50ms and released after <500ms, call onButtonReleased
  313. buttonMode.configureButton(configurePushButton);
  314. //buttonMode.onPress(onButtonPressed); // When the button is first pressed, call the function onButtonPressed (further down the page)
  315. //buttonMode.onHold(1000, onButtonHeldNoRepeat); // Once the button has been held for 1 second (1000ms) call onButtonHeld
  316. buttonMode.onHoldRepeat(1000, 1000, onButtonHeld); // Once the button has been held for 1 second (1000ms) call onButtonHeld
  317. buttonMode.onRelease(50, 500, onButtonReleased); // When the button is held >50ms and released after <500ms, call onButtonReleased
  318. pirSensor.configureButton(configurePushButton);
  319. //pirSensor.onPress(onButtonPressed); // When the button is first pressed, call the function onButtonPressed (further down the page)
  320. pirSensor.onHold(500, onButtonHeldNoRepeat); // Once the button has been held for 1 second (1000ms) call onButtonHeld
  321. pirSensor.onRelease(500, onButtonReleased); // When the button is held >50ms and released after <500ms, call onButtonReleased
  322. //set conf default values (bool, int and float variables are set at declaration)
  323. // confDev
  324. strlcpy(deviceName, DEVICE_NAME, sizeof(deviceName));
  325. strlcpy(hostName, HOST_NAME, sizeof(hostName));
  326. strlcpy(wifiAPModePassword, WIFI_APMODE_PASSWORD, sizeof(wifiAPModePassword));
  327. //confWeb
  328. strlcpy(http_token, HTTP_SET_TOKEN, sizeof(http_token));
  329. strlcpy(http_user, DEFAULT_HTTP_USER, sizeof(http_user));
  330. strlcpy(http_pass, DEFAULT_HTTP_PASS, sizeof(http_pass));
  331. //confMqtt
  332. strlcpy(mqtt_server, MQTT_SERVER, sizeof(mqtt_server));
  333. strlcpy(mqtt_user, MQTT_USER, sizeof(mqtt_user));
  334. strlcpy(mqtt_pass, MQTT_PASS, sizeof(mqtt_pass));
  335. strlcpy(mqtt_topic_in, MQTT_TOPIC_IN, sizeof(mqtt_topic_in));
  336. strlcpy(mqtt_topic_out, MQTT_TOPIC_OUT, sizeof(mqtt_topic_out));
  337. strlcpy(mqtt_willTopic, MQTT_WILLTOPIC, sizeof(mqtt_willTopic));
  338. strlcpy(mqtt_willMsg, MQTT_WILLMSG, sizeof(mqtt_willMsg));
  339. strlcpy(mqtt_connMsg, MQTT_CONNMSG, sizeof(mqtt_connMsg));
  340. //confBas
  341. // all values are int, float, boolean - set at var declaration
  342. //confAdv
  343. strlcpy(offMessage, OFF_MESSAGE, sizeof(offMessage));
  344. strlcpy(itemplab, INSIDE_TEMP_LABEL, sizeof(itemplab));
  345. strlcpy(otemplab, OUTSIDE_TEMP_LABEL, sizeof(otemplab));
  346. strlcpy(modename0, MODE_NAME_0, sizeof(modename0));
  347. strlcpy(modename1, MODE_NAME_1, sizeof(modename1));
  348. strlcpy(psetname0, PRESET_NAME_0, sizeof(psetname0));
  349. strlcpy(psetname1, PRESET_NAME_1, sizeof(psetname1));
  350. strlcpy(psetname2, PRESET_NAME_2, sizeof(psetname2));
  351. //confAdd
  352. strlcpy(mqtt_topic_pir, MQTT_TOPIC_PIR, sizeof(mqtt_topic_pir));
  353. strlcpy(mqtt_payload_pir_on, MQTT_TOPIC_PIR_ON, sizeof(mqtt_payload_pir_on));
  354. strlcpy(mqtt_payload_pir_off, MQTT_TOPIC_PIR_OFF, sizeof(mqtt_payload_pir_off));
  355. strlcpy(outTemp_topic_in, OUTTEMP_TOPIC_IN, sizeof(outTemp_topic_in));
  356. strlcpy(outHum_topic_in, OUTHUM_TOPIC_IN, sizeof(outHum_topic_in));
  357. if(serialdebug) Serial.println("default config values loaded..");
  358. initDisplay();
  359. Serial.println(F("Mounting SPIFFS..."));
  360. if (!SPIFFS.begin()) {
  361. Serial.println(F("Failed to mount SPIFFS"));
  362. return;
  363. }
  364. #ifdef FORCE_SPIFFS_FORMAT
  365. SPIFFS.format();
  366. Serial.print(F("Format SPIFFS complete."));
  367. #endif
  368. if (!SPIFFS.exists("/formatted")) {
  369. Serial.println(F("formatting SPIFFS, please wait 30 secs"));
  370. SPIFFS.format();
  371. Serial.println(F("SPIFFS formatted"));
  372. File f = SPIFFS.open("/formatted", "w");
  373. if (!f) {
  374. Serial.println(F("creating file '/formatted' failed"));
  375. } else {
  376. f.println(F("Format Complete"));
  377. }
  378. f.close();
  379. } else {
  380. if(serialdebug) Serial.println(F("SPIFFS is formatted. Moving along..."));
  381. }
  382. Serial.println(F("files in SPIFFS:"));
  383. Dir dir = SPIFFS.openDir("/");
  384. while (dir.next()) {
  385. Serial.print(dir.fileName());
  386. File f = dir.openFile("r");
  387. Serial.print(" ");
  388. Serial.println(f.size());
  389. f.close();
  390. }
  391. Serial.println(F("----"));
  392. loadConfigs();
  393. loadSavedValues();
  394. //if configuration returned empty strings - use the defaults where a values is necessary
  395. if (strlen(deviceName) < 4) strlcpy(deviceName, DEVICE_NAME, 31);
  396. if (modename0[0] == '\0') strlcpy(modename0, MODE_NAME_0, 15);
  397. if (modename1[0] == '\0') strlcpy(modename1, MODE_NAME_1, 15);
  398. if (psetname0[0] == '\0') strlcpy(psetname0, PRESET_NAME_0, 15);
  399. if (psetname1[0] == '\0') strlcpy(psetname1, PRESET_NAME_1, 15);
  400. if (psetname2[0] == '\0') strlcpy(psetname2, PRESET_NAME_2, 15);
  401. if (itemplab[0] == '\0') strlcpy(itemplab, INSIDE_TEMP_LABEL, 2);
  402. if (otemplab[0] == '\0') strlcpy(otemplab, OUTSIDE_TEMP_LABEL, 2);
  403. updateCurrentHeatingModeName();
  404. updateCurrentPresetName();
  405. // initialize DHT11/22 temp/hum sensor
  406. dht.begin();
  407. if (strlen(hostName) >= 4) { // if no hostname is set WiFi manager will create a unique one automatically based on MAC address
  408. WiFi.hostname(hostName);
  409. }
  410. //optional code handlers to run everytime wifi is connected...
  411. persWM.onConnect([]() {
  412. Serial.print("WiFi connected to '");
  413. Serial.print(WiFi.SSID());
  414. Serial.print("' with IP: ");
  415. Serial.println(WiFi.localIP());
  416. WifiInApMode = false;
  417. displayShowWifiConnected();
  418. });
  419. //...or AP mode is started
  420. persWM.onAp([]() {
  421. Serial.print("WiFi in AP mode with SSID '");
  422. Serial.print(persWM.getApSsid());
  423. if (strlen(wifiAPModePassword) >= 4) {
  424. Serial.print("' and password '");
  425. Serial.print(wifiAPModePassword);
  426. }
  427. Serial.println("'");
  428. WifiInApMode = true;
  429. WifiApModeStartedAt = millis();
  430. displayShowWifiConnectionError();
  431. });
  432. // sets network name and password for AP mode
  433. char wifiAPModeSSID[45];
  434. sprintf(wifiAPModeSSID, "%s-%s", FIRMWARE_NAME, deviceName);
  435. if (strlen(wifiAPModePassword) < 4) persWM.setApCredentials(wifiAPModeSSID);
  436. else persWM.setApCredentials(wifiAPModeSSID, wifiAPModePassword);
  437. //make connecting/disconnecting non-blocking
  438. persWM.setConnectNonBlock(true);
  439. //in non-blocking mode, program will continue past this point without waiting
  440. persWM.begin();
  441. delay(500);
  442. httpServerInit();
  443. if (mqtt_enable) {
  444. mqttPrepareSubscribeTopics();
  445. mqttClientInit();
  446. }
  447. Serial.println("setup complete.");
  448. } //void setup
  449. void loop() {
  450. checkMillis();
  451. persWM.handleWiFi(); //in non-blocking mode, handleWiFi must be called in the main loop
  452. yield();
  453. mqttHandleConnection();
  454. yield();
  455. outTempHum_updateOnNewValue();
  456. yield();
  457. dnsServer.processNextRequest();
  458. httpServer.handleClient();
  459. buttonPlus.update();
  460. buttonMinus.update();
  461. buttonMode.update();
  462. pirSensor.update();
  463. yield();
  464. evalCmd();
  465. yield();
  466. if (Serial.available()) {
  467. serialEvent();
  468. yield();
  469. }
  470. if (updateDisplayImmediately) {
  471. updateDisplayImmediately = false;
  472. updateDisplay();
  473. }
  474. } //void loop