WiFiThermostat.ino 28 KB

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