WiFiThermostat.ino 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  1. //#include <Arduino.h>
  2. //#define WIFI_HTM_PROGMEM
  3. #include <PersWiFiManagerExt.h>
  4. #include <ArduinoJson.h>
  5. #include <string.h>
  6. #include <ESP8266HTTPUpdateServer.h>
  7. #include <ESP8266WebServer.h>
  8. #include <ESP8266WiFi.h>
  9. #include <ESP8266mDNS.h>
  10. #include <WiFiClient.h>
  11. #include <DNSServer.h>
  12. #include <PubSubClient.h>
  13. #include <FS.h>
  14. #include <LiquidCrystal_I2C.h>
  15. #include <Wire.h>
  16. #include <DHT.h>
  17. #include <Bounce2.h>
  18. #include <Button.h>
  19. #include <ButtonEventCallback.h>
  20. #include <PushButton.h>
  21. #include <time.h>
  22. #include <WebSocketsServer.h>
  23. // PRE-COMPILE CONFIGURATION
  24. //#define FORCE_SPIFFS_FORMAT
  25. //#define DEBUG_VERBOSE
  26. //#define DEBUGMODE // WARNING - DEBUGMODE reveals saved passwords on serial connection and shows note in web interface
  27. #define DEBUG_SERIAL true
  28. #define DEBUG_MQTT true
  29. #define SPIFFS_DBG
  30. #define SPIFFS_USE_MAGIC
  31. #define FIRMWARE_NAME "WiFiThermostat"
  32. #define FIRMWARE_VERSION "0.6.0"
  33. #define FIRMWARE_URL "https://git.flokra.at/flo/WiFiThermostat"
  34. #define FIRMWARE_COPYRIGHT "FloKra"
  35. #define FIRMWARE_COPYRIGHT_URL "https://www.flokra.at/"
  36. // used as prefix in AP mode SSID, computed hostname and MQTT cliend ID
  37. #define FIRMWARE_SHORTNAME "WTherm"
  38. // default values, can later be overridden via configuration
  39. #define MAX_MEASUREMENT_AGE 30000 // in ms, for measured inside Temp/Hum
  40. #define MAX_MEASUREMENT_AGE_OUT 300000 // in ms, for received outside Temp/Hum
  41. // NTP
  42. #define TIMEZONE "CET-1CEST,M3.5.0/02,M10.5.0/03"
  43. #define NTP_TIMEOUT 1500
  44. #define NTP_TIMEZONE 1
  45. #define NTP_MINUTES_TZ 0
  46. #define NTP_SYNC_INTERVAL 7200
  47. #define NTP_SERVER "pool.ntp.org"
  48. // conf DevWiFi
  49. //#define DEVICE_NAME "WTherm-1" - created from FIRMWARE_SHORTNAME + last 2
  50. // octets of MAC address
  51. #define HOST_NAME ""
  52. #define WIFI_APMODE_PASSWORD ""
  53. #define DEFAULT_WIFI_APMODE_TIMEOUT 5 //min
  54. #define DEFAULT_WIFI_RETRY_INTERVAL 5 //min
  55. #define DEFAULT_WIFI_CONNCHECK_INTERVAL 20 //s
  56. #define DEFAULT_WIFI_REBOOT_ONNOCONNECT 60 //min
  57. // confWeb
  58. #define HTTP_SET_TOKEN ""
  59. #define DEFAULT_HTTP_USER ""
  60. #define DEFAULT_HTTP_PASS ""
  61. #define DEFAULT_HTTP_USER_AUTH false
  62. #define DEFAULT_ENABLE_WEBCONSOLE false
  63. // confMqtt
  64. #define MQTT_ENABLE true
  65. #define MQTT_SERVER "mqtt.lan"
  66. #define MQTT_PORT 1883
  67. #define MQTT_USER ""
  68. #define MQTT_PASS ""
  69. #define MQTT_TOPIC_IN "Test/Thermostat"
  70. #define MQTT_TOPIC_OUT "Test/Thermostat/stat"
  71. #define MQTT_OUT_RETAIN false
  72. #define MQTT_OUT_RETAIN_SENSORS false
  73. #define MQTT_OUT_PUBLISH_INTERVAL 0 // min, 0=only on change
  74. #define MQTT_OUT_PUBLISH_INTERVAL_SENSORS 5 // min, 0=only on change
  75. #define MQTT_WILLTOPIC "Test/Thermostat/availability"
  76. #define MQTT_WILLQOS 2
  77. #define MQTT_WILLRETAIN false
  78. #define MQTT_WILLMSG "offline"
  79. #define MQTT_CONNMSG "online"
  80. #define MQTT_ENABLE_HEARTBEAT true
  81. // max interval for MQTT heartbeat message. only applicable if MQTT
  82. // IN-topic is defined. after this timeout MQTT reconnect is forced
  83. #define MQTT_HEARTBEAT_MAXAGE 180000
  84. // max interval for MQTT heartbeat message. only applicable if MQTT
  85. // IN-topic is defined. after this timeout the ESP will reboot
  86. #define MQTT_HEARTBEAT_MAXAGE_REBOOT 1800000
  87. // confBas
  88. #define DEFAULT_SETTEMP_MIN 16.0 // minimal temperature that can be set
  89. #define DEFAULT_SETTEMP_MAX 26.0 // maximal temperature that can be set
  90. #define AUTOSAVE_SETTEMP true
  91. #define AUTOSAVE_SETMODE false
  92. #define SAVE_TO_MQTT_RETAINED false
  93. #define DEFAULT_MEASURE_INTERVAL 15 // interval for temp/hum measurement
  94. // interval for display updates (if out-temp is active, display will toggle
  95. // in this interval)
  96. #define DEFAULT_DISPLAY_INTERVAL 5
  97. #define DEFAULT_DISPLAY_TIMEOUT 30 // display timeout after keypress (illumination)
  98. #define DEFAULT_PIR_ENABLES_DISPLAY false
  99. #define DEFAULT_PIR_ENABLES_DISPLAY_PRESET0_ONLY true
  100. #define DEFAULT_TOGGLING_I_O_TEMPHUM false
  101. // confAdv
  102. #define DEFAULT_HYSTERESIS 0.2 // hysteresis, normally 0.1 - 0.5
  103. #define DEFAULT_HEATING_MIN_OFFTIME 120 // minimal time the heating keeps turned off in s
  104. // correction value for temperature sensor reading
  105. #define TEMPSENSOR_CORRECTION_VALUE 0.0
  106. #define HUMSENSOR_CORRECTION_VALUE 0 // correction value for humidity sensor reading
  107. // decreases the set temp to overcome further temperature rise when the
  108. // heating is already switched off
  109. #define SETTEMP_DECREASE_VALUE 0.0
  110. #define OFF_MESSAGE "HEATING OFF"
  111. #define INSIDE_TEMP_LABEL "I"
  112. #define OUTSIDE_TEMP_LABEL "O"
  113. #define MODE_NAME_0 "off"
  114. #define MODE_NAME_1 "heat"
  115. #define PRESET_NAME_0 "none"
  116. #define PRESET_NAME_1 "reduction1"
  117. #define PRESET_NAME_2 "reduction2"
  118. // confAdd
  119. #define OUTTEMP_TOPIC_IN ""
  120. #define OUTHUM_TOPIC_IN ""
  121. #define MQTT_TOPIC_PIR "" // extra publish topic for PIR sensor
  122. #define MQTT_TOPIC_PIR_ON "ON"
  123. #define MQTT_TOPIC_PIR_OFF "OFF"
  124. // other (not configurable via WebIF)
  125. #define DEFAULT_SETTEMP_HEATOFF 5.0 // set temperature in OFF mode (freezing guard > 0)
  126. // default initial values
  127. #define DEFAULT_SETTEMP 21.5
  128. #define DEFAULT_HEATINGMODE 1
  129. #define DEFAULT_PRESET 1
  130. #define DEFAULT_SETTEMP_LOW 20.0 // set temperature in night/low mode
  131. #define DEFAULT_SETTEMP_LOW2 18.0 // set temperature in night/low mode
  132. // default values that can only be configured at compile time / hardware
  133. // configuration
  134. #define BUTTON_DEBOUNCE_TIME 120
  135. #define BUTTON_HOLD_TIME 750
  136. #define SETTEMP_LOW_MIN 14.0 // minimal configurable temperature for reduction mode
  137. #define SETTEMP_LOW_MAX 21.5 // maximal configurable temperature for reduction mode
  138. // pin assignments and I2C addresses
  139. #define PIN_DHTSENSOR 13
  140. #define PIN_RELAIS 15 // 16
  141. #define PIN_BUTTON_PLUS 2
  142. #define PIN_BUTTON_MINUS 0
  143. #define PIN_BUTTON_MODE 14
  144. #define PIN_PIRSENSOR 12
  145. #define DHTTYPE DHT22 // DHT sensor type
  146. #define LCDADDR 0x27 // I2C address LCD
  147. #define LCDCOLS 16
  148. #define LCDLINES 2
  149. // default logic levels
  150. #define RELAISONSTATE HIGH
  151. #define BUTTONONSTATE LOW
  152. // LOG LEVELS
  153. #define LOGLEVEL_OFF 0
  154. #define LOGLEVEL_ERROR 1
  155. #define LOGLEVEL_WARN 2
  156. #define LOGLEVEL_INFO 3
  157. #define LOGLEVEL_DEBUG 4
  158. #define LOGLEVEL_VERBOSE 5
  159. #define DEFAULT_LOGLEVEL_SERIAL LOGLEVEL_DEBUG
  160. #define DEFAULT_LOGLEVEL_WEB LOGLEVEL_DEBUG
  161. #define DEFAULT_LOGLEVEL_MQTT LOGLEVEL_DEBUG
  162. #define DEFAULT_LOGLEVEL_SYSLOG LOGLEVEL_DEBUG
  163. // END PRE-COMPILE CONFIGURATION
  164. PROGMEM const char PGMStr_changedTo[] = "changed to";
  165. PROGMEM const char PGMStr_preset[] = "preset";
  166. PROGMEM const char PGMStr_heatingMode[] = "heatingMode";
  167. PROGMEM const char PGMStr_setTemp[] = "setTemp";
  168. PROGMEM const char PGMStr_currentSetTemp[] = "currSetTemp";
  169. PROGMEM const char PGMStr_setTempLow[] = "setTempLow";
  170. PROGMEM const char PGMStr_setTempLow2[] = "setTempLow2";
  171. PROGMEM const char PGMStr_switchHeating[] = "switch heating";
  172. PROGMEM const char PGMStr_mqttRetainedSave[] = "MQTT retained save";
  173. PROGMEM const char PGMStr_heating[] = "heating";
  174. PROGMEM const char PGMStr_thermostat[] = "TSTAT";
  175. PROGMEM const char PGMStr_WiFi[] = "WiFi";
  176. PROGMEM const char PGMStr_connectedTo[] = "connected to";
  177. PROGMEM const char PGMStr_withIP[] = "with IP";
  178. PROGMEM const char PGMStr_MQTT[] = "MQTT";
  179. PROGMEM const char PGMStr_connectedReconnects[] = "connected, reconnects";
  180. PROGMEM const char PGMStr_connectedFailed[] = "connect FAILED";
  181. const char PGMStr_MQTTStateM4[] PROGMEM = "CONNECTION_TIMEOUT";
  182. const char PGMStr_MQTTStateM3[] PROGMEM = "CONNECTION_LOST";
  183. const char PGMStr_MQTTStateM2[] PROGMEM = "CONNECT_FAILED";
  184. const char PGMStr_MQTTStateM1[] PROGMEM = "DISCONNECTED";
  185. const char PGMStr_MQTTState0[] PROGMEM = "CONNECTED";
  186. const char PGMStr_MQTTState1[] PROGMEM = "CONNECT_BAD_PROTOCOL";
  187. const char PGMStr_MQTTState2[] PROGMEM = "CONNECT_BAD_CLIENT_ID";
  188. const char PGMStr_MQTTState3[] PROGMEM = "CONNECT_UNAVAILABLE";
  189. const char PGMStr_MQTTState4[] PROGMEM = "CONNECT_BAD_CREDENTIALS";
  190. const char PGMStr_MQTTState5[] PROGMEM = "CONNECT_UNAUTHORIZED";
  191. char mqttCurrentStateName[25];
  192. const char *const PGMStr_MQTTStates[] PROGMEM =
  193. {
  194. PGMStr_MQTTStateM4,
  195. PGMStr_MQTTStateM3,
  196. PGMStr_MQTTStateM2,
  197. PGMStr_MQTTStateM1,
  198. PGMStr_MQTTState0,
  199. PGMStr_MQTTState1,
  200. PGMStr_MQTTState2,
  201. PGMStr_MQTTState3,
  202. PGMStr_MQTTState4,
  203. PGMStr_MQTTState5,
  204. };
  205. //---------------------------------------------------------------------------------------------------------------------------------------------
  206. PushButton buttonPlus = PushButton(PIN_BUTTON_PLUS, ENABLE_INTERNAL_PULLUP);
  207. PushButton buttonMinus = PushButton(PIN_BUTTON_MINUS, ENABLE_INTERNAL_PULLUP);
  208. PushButton buttonMode = PushButton(PIN_BUTTON_MODE, ENABLE_INTERNAL_PULLUP);
  209. PushButton pirSensor = PushButton(PIN_PIRSENSOR, PRESSED_WHEN_HIGH);
  210. bool serialdebug = DEBUG_SERIAL;
  211. bool mqttdebug = DEBUG_MQTT;
  212. bool WifiInApMode = false;
  213. unsigned long WifiApModeStartedAt;
  214. // time
  215. struct tm lt; // http://www.cplusplus.com/reference/ctime/tm/
  216. const char *const PROGMEM dayNames[] = {"Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"};
  217. const char *const PROGMEM dayShortNames[] = {"So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"};
  218. const char *const PROGMEM monthNames[] = {"Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"};
  219. const char *const PROGMEM monthShortNames[] = {"Jan", "Feb", "Mrz", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"};
  220. // config variables - do not change here!
  221. // confDevWiFi
  222. struct confDataDevWiFi
  223. {
  224. char deviceName[33]; // device name - for web interface and AP-Mode SSID
  225. char hostName[33]; // announced hostname on WiFi connection
  226. char WiFiSSID1[33];
  227. char WiFiPW1[64];
  228. char WiFiSSID2[33];
  229. char WiFiPW2[64];
  230. char WiFiAPModeSSID[33]; // generated, not set via config
  231. char WiFiAPModePassword[64];
  232. uint16_t WiFiAPModeTimeout;
  233. uint16_t WiFiRetryInterval;
  234. uint16_t WiFiConnCheckInterval;
  235. uint16_t WiFiRebootOnNoConnect;
  236. } confDevWiFi;
  237. // confWeb
  238. struct confDataWeb
  239. {
  240. char http_token[31];
  241. char http_user[31];
  242. char http_pass[31];
  243. bool http_user_auth;
  244. char http_user1[31];
  245. char http_pass1[31];
  246. char http_user2[31];
  247. char http_pass2[31];
  248. bool enableConsole;
  249. } confWeb;
  250. // confMqtt
  251. struct confDataMqtt
  252. {
  253. bool mqtt_enable;
  254. char mqtt_server[41];
  255. uint16_t mqtt_port;
  256. char mqtt_user[31];
  257. char mqtt_pass[31];
  258. char mqtt_topic_in[51]; // MQTT in topic for commands
  259. char mqtt_topic_out[51]; // MQTT out base topic, will be extended by various value names
  260. char mqtt_willTopic[51]; // MQTT Last Will topic
  261. uint8_t mqtt_willQos; // MQTT Last Will topic QOS
  262. bool mqtt_willRetain; // MQTT Last Will retain
  263. char mqtt_willMsg[31]; // MQTT Last Will payload
  264. char mqtt_connMsg[31];
  265. bool mqtt_outRetain; // send MQTT out with retain flag
  266. bool mqtt_outRetain_sensors; // send MQTT out with retain flag
  267. uint8_t mqtt_outPubInterval;
  268. uint8_t mqtt_outPubInterval_sensors;
  269. bool mqtt_enable_heartbeat;
  270. unsigned long mqtt_heartbeat_maxage_reconnect;
  271. unsigned long mqtt_heartbeat_maxage_reboot;
  272. } confMqtt;
  273. // confBas
  274. struct confDataBas
  275. {
  276. float setTempMin; // minimal temperature that can be set
  277. float setTempMax; // maximal temperature that can be set
  278. bool autoSaveSetTemp;
  279. bool autoSaveHeatingMode;
  280. bool saveToMqttRetained;
  281. uint16_t measureInterval; // interval for temp/hum measurement
  282. uint16_t displayInterval; // interval for display updates (if out-temp is
  283. // active, display will toggle in this interval)
  284. uint16_t displayTimeout; // display timeout after keypress (illumination)
  285. bool PIR_enablesDisplay; // PIR sensor enables display illumination
  286. bool PIR_enablesDisplay_preset0only;
  287. bool togglingTempHumAIDisplay;
  288. } confBas;
  289. // confAdv
  290. struct confDataAdv
  291. {
  292. float hysteresis; // hysteresis, normally 0.1 - 0.5
  293. uint16_t heatingMinOffTime; // minimal time the heating keeps turned off in
  294. // s
  295. float tempCorrVal; // correction value for
  296. // temperature sensor reading
  297. int humCorrVal; // correction value for humidity sensor reading
  298. float setTempDecreaseVal; // decreases the set temp to overcome further
  299. // temperature rise when the heating is already
  300. // switched off
  301. char offMessage[15];
  302. char iTempLabel[2];
  303. char oTempLabel[2];
  304. char modeName0[15];
  305. char modeName1[15];
  306. char psetName0[15];
  307. char psetName1[15];
  308. char psetName2[15];
  309. } confAdv;
  310. // confAdd
  311. struct confDataAdd
  312. {
  313. char mqtt_topic_pir[51];
  314. char mqtt_payload_pir_on[10];
  315. char mqtt_payload_pir_off[10];
  316. char outTemp_topic_in[51];
  317. char outHum_topic_in[51];
  318. } confAdd;
  319. // confTime
  320. struct confDataTime
  321. {
  322. bool ntpEnable;
  323. char timeZoneStr[50];
  324. char ntpServer1[31];
  325. char ntpServer2[31];
  326. unsigned long ntpSyncInterval;
  327. } confTime;
  328. // confLog
  329. struct confDataLog
  330. {
  331. uint8_t logLevelSerial;
  332. uint8_t logLevelWeb;
  333. uint8_t logLevelMqtt;
  334. uint8_t logLevelSyslog;
  335. } confLog;
  336. bool mqtt_tempDisabled_credentialError = false;
  337. char mqtt_topic_in_cmd[61];
  338. char mqtt_topic_in_setTemp[61];
  339. char mqtt_topic_in_setMode[61];
  340. char mqtt_topic_in_setPreset[61];
  341. // set values
  342. float setTemp = DEFAULT_SETTEMP;
  343. float setTempLow = DEFAULT_SETTEMP_LOW; // set temperature in night/low mode
  344. float setTempLow2 = DEFAULT_SETTEMP_LOW2; // set temperature in night/low mode
  345. float currSetTemp = DEFAULT_SETTEMP;
  346. uint8_t heatingMode = DEFAULT_HEATINGMODE; // 0 = off, 1 = heat
  347. uint8_t preset = DEFAULT_PRESET; // 0 = normal/day, 1 = night/reduction 1, 2 =
  348. // reduction 2 (long term leave)
  349. // saved set values (same as above, held in memory additionally to prevent
  350. // saving unchanged values)
  351. float setTempSaved;
  352. float setTempLowSaved;
  353. float setTempLow2Saved;
  354. uint8_t heatingModeSaved;
  355. uint8_t presetSaved;
  356. // global pre set conf variables - not changeable via configuration
  357. float setTempLowMin = SETTEMP_LOW_MIN;
  358. float setTempLowMax = SETTEMP_LOW_MAX;
  359. uint16_t debounceTime = BUTTON_DEBOUNCE_TIME;
  360. uint16_t buttonHoldTime = BUTTON_HOLD_TIME;
  361. unsigned long maxMeasurementAge = MAX_MEASUREMENT_AGE;
  362. unsigned long maxMeasurementAgeOut = MAX_MEASUREMENT_AGE_OUT;
  363. // global variables for program flow
  364. float currTemp; // last reading from DHT sensor with smoothing
  365. float currTemp_raw; // last reading from DHT sensor (raw)
  366. uint16_t currHum; // last reading from DHT sensor with smoothing
  367. uint16_t currHum_raw; // last reading from DHT sensor (raw)
  368. bool turnHeatingOn = false; // true if heating is active (relais switched on)
  369. unsigned long heatingLastOnMillis; // last time heating was switched on
  370. unsigned long heatingLastOffMillis; // last time heating was switched off
  371. float outTemp; // outside temp (via MQTT if enabled and in-topic configured)
  372. uint16_t outHum; // outside temp (via MQTT if enabled and in-topic configured)
  373. unsigned long outTempHumLastUpdate; // last reading from out temp/hum source
  374. char outTemp_newValue[6];
  375. bool outTemp_parseNewValue;
  376. char outHum_newValue[4];
  377. bool outHum_parseNewValue;
  378. char currentModeName[15];
  379. char currentPresetName[15];
  380. uint16_t displayInterval;
  381. uint8_t whichTempToDisplay; // 1=temp inside (from DHT sensor), 2= temp outside
  382. // (via MQTT) - if out temp/hum available this value
  383. // and the displayed value pair toggles with every
  384. // displayInterval
  385. unsigned long lastMeasure = 0; // millis of last temp/hum measurement
  386. unsigned long lastDisplayUpdate = 0; // millis of last display update
  387. unsigned long lastDisplayToggle = 0; // millis of last display toggle
  388. unsigned long lastTempUpdate = 0; // last update time of DHT reading
  389. char msg[50]; // buffer MQTT in payload
  390. char topic[50]; // buffer MQTT in topic
  391. bool displayActive = false; // gets true when button is pressed. display light
  392. // gets switched on until timeout. button actions
  393. // are only performed while display is active
  394. bool PIRSensorOn = false;
  395. unsigned long heatingOnTime, heatingOffTime;
  396. bool config_was_changed = false;
  397. unsigned long lastUpdate_setTemp = 0; // set to millis() every time setTemp value is changed
  398. unsigned long lastUpdate_setTempLow = 0; // set to millis() every time setTemp value is changed
  399. unsigned long lastUpdate_setTempLow2 = 0; // set to millis() every time setTemp value is changed
  400. unsigned long lastUpdate_heatingMode = 0; // set to millis() every time heatingMode value is changed
  401. unsigned long lastUpdate_preset = 0; // set to millis() every time preset value is changed
  402. char cmdPayload[101]; // buffer for commands
  403. bool cmdInQueue = false; // command is queued and will be processed next loop() run
  404. bool saveConfigToFlash = false; // conf is saved in next loop() run
  405. bool saveConfig2ToFlash = false; // conf2 is saved in next loop() run
  406. uint16_t saveValuesTimeout = 5000;
  407. unsigned long lastValueChange; // is set to millis() whenever setTemp value
  408. // and/or heatingMode value is changed. used for
  409. // autoSave function with hardcoded 5s timeout
  410. bool setTempAlreadySaved = true; // only save if not yet done
  411. bool setTempLowAlreadySaved = true; // only save if not yet done
  412. bool setTempLow2AlreadySaved = true; // only save if not yet done
  413. bool heatingModeAlreadySaved = true; // only save if not yet done
  414. bool presetAlreadySaved = true; // only save if not yet done
  415. unsigned long lastRun = 0;
  416. uint16_t count100ms = 0;
  417. uint16_t countSeconds = 0;
  418. uint16_t countMeasureInterval = 0;
  419. uint16_t countDisplayInterval = 0;
  420. uint16_t displayOverlayMsgTimeout = 2;
  421. unsigned long mqttLastReconnectAttempt = 0;
  422. uint16_t mqttReconnectAttempts = 0;
  423. uint16_t mqttReconnects = 0;
  424. bool mqttConnected = false;
  425. unsigned long mqttLastHeartbeat;
  426. bool mqttInTopicSubscribed = false;
  427. bool pendingRestart = false;
  428. bool doRestart = false;
  429. unsigned long pendingRestart_lastMillis = 0;
  430. bool displayShowFullscreenMsg = false;
  431. // unsigned long displayShowFullscreenMsg_lastMillis = 0;
  432. bool pendingPresetToggle = false;
  433. bool updateDisplayImmediately = false;
  434. uint8_t pendingPreset;
  435. char pendingPresetName[15];
  436. // unsigned long pendingPreset_millis = 0;
  437. // int pendingPreset_timeout = 2000;
  438. bool displayShowLine2OverlayMsg = false;
  439. // build Uptime String (unnecessary, i know ;) )
  440. uint16_t sysUptime_days = 0;
  441. uint16_t sysUptime_hours = 0;
  442. uint16_t sysUptime_mins = 0;
  443. char uptimeStr[15];
  444. DHT dht(PIN_DHTSENSOR, DHTTYPE);
  445. LiquidCrystal_I2C lcd(LCDADDR, LCDCOLS, LCDLINES);
  446. WiFiClient espClient;
  447. void mqttCallback(char *topic, byte *payload, uint16_t length);
  448. PubSubClient mqttclient(espClient);
  449. ESP8266WebServer httpServer(80);
  450. WebSocketsServer webSocket(81);
  451. DNSServer dnsServer;
  452. PersWiFiManager persWM(httpServer, dnsServer);
  453. ESP8266HTTPUpdateServer httpUpdater;
  454. // WebSocketsServer
  455. unsigned long int wsValidSessionId = 0;
  456. void updateTime();
  457. bool setupTime();
  458. void updateTimeFromNTP();
  459. void sendLog(const char *msg, uint8_t loglevel = 1);
  460. void sendLog(const __FlashStringHelper *msg, uint8_t loglevel = 1);
  461. void setup()
  462. {
  463. Serial.begin(115200);
  464. delay(50);
  465. Serial.println();
  466. Serial.print(FIRMWARE_NAME);
  467. Serial.print(" v");
  468. Serial.print(FIRMWARE_VERSION);
  469. Serial.println(" starting...");
  470. pinMode(PIN_RELAIS, OUTPUT);
  471. digitalWrite(PIN_RELAIS, !RELAISONSTATE);
  472. setupInputsButtons();
  473. // set conf default values
  474. // set initial deviceName
  475. createDeviceName();
  476. loadConf_defaults();
  477. //if (serialdebug)
  478. // Serial.println("default config values loaded..");
  479. sendLog(F("DEV: default config values loaded."), LOGLEVEL_INFO);
  480. initDisplay();
  481. sendLog(F("Mounting SPIFFS..."), LOGLEVEL_INFO);
  482. if (!SPIFFS.begin())
  483. {
  484. sendLog(F("Failed to mount SPIFFS"), LOGLEVEL_ERROR);
  485. return;
  486. }
  487. #ifdef FORCE_SPIFFS_FORMAT
  488. SPIFFS_format();
  489. #endif
  490. SPIFFS_formatIfIsnt();
  491. SPIFFS_listFiles();
  492. loadConf_all();
  493. loadSavedValues();
  494. // if configuration returned empty strings - use the defaults where a values
  495. // is necessary if (strlen(deviceName) < 4) strlcpy(deviceName, DEVICE_NAME,
  496. // 31);
  497. loadConf_restoreDefaultWhenMissing();
  498. updateCurrentHeatingModeName();
  499. updateCurrentPresetName();
  500. // initialize DHT11/22 temp/hum sensor
  501. dht.begin();
  502. if (strlen(confDevWiFi.hostName) >= 4)
  503. { // if no hostname is set WiFi manager will create a unique one automatically
  504. // based on MAC address
  505. WiFi.hostname(confDevWiFi.hostName);
  506. }
  507. // optional code handlers to run everytime wifi is connected...
  508. persWM.onConnect([]() {
  509. //Serial.print(F("WiFi: connected to '"));
  510. //Serial.print(WiFi.SSID());
  511. //Serial.print(F("' with IP: "));
  512. //Serial.println(WiFi.localIP());
  513. char buf[60];
  514. sprintf(buf, "%s: %s '%s' %s: '%s'", PGMStr_WiFi, PGMStr_connectedTo, WiFi.SSID().c_str(), PGMStr_withIP, WiFi.localIP().toString().c_str());
  515. sendLog(buf, LOGLEVEL_INFO);
  516. WifiInApMode = false;
  517. if (confTime.ntpEnable)
  518. setupTime();
  519. displayShowWifiConnected();
  520. });
  521. //...or AP mode is started
  522. persWM.onAp([]() {
  523. //Serial.print(F("WiFi: AP mode, SSID: '"));
  524. //Serial.print(persWM.getApSsid());
  525. //if (strlen(confDevWiFi.WiFiAPModePassword) >= 4)
  526. //{
  527. // Serial.print("', PW: '");
  528. // Serial.print(confDevWiFi.WiFiAPModePassword);
  529. //}
  530. //Serial.println("'");
  531. char buf[60];
  532. sprintf(buf, "%s: AP-MODE started, SSID '%s', PW: '%s'", PGMStr_WiFi, persWM.getApSsid().c_str(), confDevWiFi.WiFiAPModePassword);
  533. sendLog(buf, LOGLEVEL_INFO);
  534. WifiInApMode = true;
  535. WifiApModeStartedAt = millis();
  536. displayShowWifiConnectionError();
  537. });
  538. persWM.onApOff([]() {
  539. //Serial.println(F("WiFi: AP mode stopped"));
  540. sendLog(F("WiFi: AP-MODE stopped"), LOGLEVEL_INFO);
  541. });
  542. // sets network name and password for AP mode
  543. // sprintf(WiFiAPModeSSID, "%s-%s", FIRMWARE_SHORTNAME, deviceName);
  544. if (strlen(confDevWiFi.deviceName) >= 8)
  545. strlcpy(confDevWiFi.WiFiAPModeSSID, confDevWiFi.deviceName, sizeof(confDevWiFi.WiFiAPModeSSID));
  546. else
  547. {
  548. byte mac[6];
  549. WiFi.macAddress(mac);
  550. sprintf(confDevWiFi.WiFiAPModeSSID, "%s-%02X%02X", FIRMWARE_SHORTNAME, (uint8_t)mac[4], (uint8_t)mac[5]);
  551. }
  552. //Serial.print(F("WiFi: AP-SSID name: "));
  553. //Serial.println(confDevWiFi.WiFiAPModeSSID);
  554. if (strlen(confDevWiFi.WiFiAPModePassword) < 5)
  555. persWM.setApCredentials(confDevWiFi.WiFiAPModeSSID);
  556. else
  557. persWM.setApCredentials(confDevWiFi.WiFiAPModeSSID, confDevWiFi.WiFiAPModePassword);
  558. //Serial.print(F("WiFi: SSID 1: ")); Serial.println(confDevWiFi.WiFiSSID1); //Serial.print("', PW: '"); Serial.print(confDevWiFi.WiFiPW1); Serial.println("'");
  559. //Serial.print(F("WiFi: SSID 2: ")); Serial.println(confDevWiFi.WiFiSSID2); //Serial.print("', PW: '"); Serial.print(confDevWiFi.WiFiPW2); Serial.println("'");
  560. persWM.setWifi1Credentials(confDevWiFi.WiFiSSID1, confDevWiFi.WiFiPW1);
  561. persWM.setWifi2Credentials(confDevWiFi.WiFiSSID2, confDevWiFi.WiFiPW2);
  562. persWM.setHttpCredentials(confWeb.http_user, confWeb.http_pass);
  563. persWM.setApTimeout(confDevWiFi.WiFiAPModeTimeout);
  564. persWM.setConnCheckInterval(confDevWiFi.WiFiConnCheckInterval);
  565. persWM.setForceRetryWifi1Interval(confDevWiFi.WiFiRetryInterval);
  566. persWM.setRebootOnNoConnect(confDevWiFi.WiFiRebootOnNoConnect);
  567. // make connecting/disconnecting non-blocking
  568. persWM.setConnectNonBlock(true);
  569. // in non-blocking mode, program will continue past this point without waiting
  570. persWM.begin();
  571. delay(500);
  572. httpServerInit();
  573. if (confWeb.enableConsole)
  574. startWebSocketServer();
  575. if (confMqtt.mqtt_enable)
  576. {
  577. mqttPrepareSubscribeTopics();
  578. mqttClientInit();
  579. }
  580. buildUptimeString();
  581. sendLog(F("DEV: setup complete."), LOGLEVEL_INFO);
  582. } // void setup
  583. void loop()
  584. {
  585. checkMillis();
  586. persWM.handleWiFi(); // in non-blocking mode, handleWiFi must be called in the
  587. // main loop
  588. yield();
  589. mqttHandleConnection();
  590. yield();
  591. outTempHum_updateOnNewValue();
  592. yield();
  593. dnsServer.processNextRequest();
  594. httpServer.handleClient();
  595. buttonPlus.update();
  596. buttonMinus.update();
  597. buttonMode.update();
  598. pirSensor.update();
  599. yield();
  600. evalCmd();
  601. yield();
  602. if (Serial.available())
  603. {
  604. serialEvent();
  605. yield();
  606. }
  607. if (updateDisplayImmediately)
  608. {
  609. updateDisplayImmediately = false;
  610. updateDisplay();
  611. }
  612. if (confWeb.enableConsole)
  613. {
  614. if (WiFi.status() == WL_CONNECTED)
  615. {
  616. webSocket.loop();
  617. }
  618. else
  619. {
  620. webSocket.disconnect();
  621. }
  622. }
  623. } // void loop