WiFiThermostat.ino 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. #define VERSION "0.1.1"
  2. #include <ESP8266WiFi.h> //ESP8266 Core WiFi Library (you most likely already have this in your sketch)
  3. #include <WiFiClient.h>
  4. #include <ESP8266WebServer.h>
  5. #include <ESP8266mDNS.h>
  6. #include <ESP8266HTTPUpdateServer.h>
  7. #include <PubSubClient.h>
  8. ESP8266WebServer httpServer(80);
  9. ESP8266HTTPUpdateServer httpUpdater;
  10. #include <Wire.h>
  11. #include "LiquidCrystal_I2C.h"
  12. //#include <DNSServer.h> //Local DNS Server used for redirecting all requests to the configuration portal
  13. //#include <ESP8266WebServer.h> //Local WebServer used to serve the configuration portal
  14. //#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager WiFi Configuration Magic
  15. #include <AS_BH1750.h> // BH1750 lux sensor
  16. #include "DHT.h"
  17. #define DHTTYPE DHT22
  18. #define LCDADDR 0x27
  19. #define LUXSENSORADDR 0x23
  20. #define PIN_DHTSENSOR 13
  21. #define PIN_RELAIS 16
  22. #define PIN_BUTTON_PLUS 0
  23. #define PIN_BUTTON_MINUS 2
  24. #define PIN_BUTTON_MODE 14
  25. #define PIN_PIRSENSOR 12
  26. /*const int PIN_DHTSENSOR = 13;
  27. const int PIN_RELAIS = 16;
  28. const int PIN_BUTTON_PLUS = 0;
  29. const int PIN_BUTTON_MINUS = 2;
  30. const int PIN_BUTTON_MODE = 14;
  31. const int PIN_PIRSENSOR = 12;*/
  32. const bool RelaisOnState = LOW;
  33. const bool ButtonOnState = LOW;
  34. float setTemp;
  35. float setTempDay = 21.5;
  36. float setTempNight = 18.0;
  37. float setTempMin = 14.0;
  38. float setTempMax = 29.0;
  39. float setTempMinLow = 10.0;
  40. float setTempMaxLow = 19.0;
  41. float hysteresis = 0.5;
  42. long heatingLastOnMillis;
  43. long heatingLastOffMillis;
  44. long minOffTime = 10; // s
  45. //uint16_t lux;
  46. //int lux;
  47. //float lux;
  48. int measureinterval = 60000; // ms
  49. bool heatingEnabled = true;
  50. byte heatingMode = 1;
  51. ///// 0 = off
  52. // 1 = default/day
  53. // 2 = night/reduction
  54. bool turnHeatingOn = false;
  55. float humidity, temperature;
  56. //int humidity, temperature;
  57. const char* host = "espthermostat";
  58. const char* ssid = "KS61T5SH"; // WiFi SSID
  59. const char* password = "Gurken651salat"; // WiFi PWD
  60. const char* mqtt_server = "10.1.1.11";
  61. const int mqtt_port = 1883;
  62. //const char* mqtt_topic_prefix = "Test/Thermostat/";
  63. const char* mqtt_topic_temp = "Test/Thermostat/Temp";
  64. const char* mqtt_topic_settemp = "Test/Thermostat/SetTemp";
  65. //const char* mqtt_topic_setmode = "Test/Thermostat/SetMode";
  66. const char* mqtt_topic_hum = "Test/Thermostat/Hum";
  67. const char* mqtt_topic_lux = "Test/Thermostat/Lux";
  68. const char* mqtt_topic_heating = "Test/Thermostat/Heating";
  69. const char* mqtt_topic_heating_lastofftime = "Test/Thermostat/HeatingLastOffTime";
  70. const char* mqtt_topic_heating_lastontime = "Test/Thermostat/HeatingLastOnTime";
  71. // subscribe topic
  72. const char* mqtt_topic_in = "Test/Thermostat/in";
  73. DHT dht(PIN_DHTSENSOR, DHTTYPE);
  74. // global variables from other modules
  75. extern bool displayActive;
  76. //AS_BH1750 lightMeter; // Lichtstärkemessung mit BH1750
  77. LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display
  78. WiFiClient espClient;
  79. PubSubClient client(espClient);
  80. long lastMsg = 0;
  81. char msg[50];
  82. char topic[50];
  83. int displayTimeout = 10000;
  84. //bool lightMeter_present;
  85. void setup_wifi() {
  86. delay(10);
  87. // We start by connecting to a WiFi network
  88. Serial.println();
  89. Serial.print("Connecting to ");
  90. Serial.println(ssid);
  91. /////
  92. WiFi.mode(WIFI_STA);
  93. /////
  94. WiFi.begin(ssid, password);
  95. while (WiFi.status() != WL_CONNECTED) {
  96. delay(500);
  97. Serial.print(".");
  98. }
  99. randomSeed(micros());
  100. Serial.println("");
  101. Serial.println("WiFi connected");
  102. Serial.println("IP address: ");
  103. Serial.println(WiFi.localIP());
  104. }
  105. void callback(char* topic, byte* payload, unsigned int length) {
  106. Serial.print("Message arrived [");
  107. Serial.print(topic);
  108. Serial.print("] ");
  109. for (int i = 0; i < length; i++) {
  110. Serial.print((char)payload[i]);
  111. }
  112. Serial.println();
  113. if ((char)payload[0] == 's' && \
  114. (char)payload[1] == 'e' && \
  115. (char)payload[2] == 't' && \
  116. (char)payload[3] == ' ' ) {
  117. if ((char)payload[4] == 't' && \
  118. (char)payload[5] == 'e' && \
  119. (char)payload[6] == 'm' && \
  120. (char)payload[7] == 'p' && \
  121. (char)payload[8] == ' ' ) {
  122. char inValue[length - 9 + 1];
  123. for (int i = 0; i < length; i++) {
  124. inValue[i] = (char)payload[i + 9];
  125. }
  126. inValue[sizeof(inValue) - 1] = '\0';
  127. Serial.print(inValue);
  128. Serial.println();
  129. float invalueFloat = round(atof(inValue) * 2.0) / 2.0;
  130. Serial.print(invalueFloat);
  131. if (invalueFloat >= setTempMin && invalueFloat <= setTempMax) {
  132. setTempDay = invalueFloat;
  133. lastMsg = 0; // reset var lastMsg to speed up lcd update
  134. }
  135. else if (invalueFloat > setTempMax) {
  136. setTempDay = setTempMax;
  137. lastMsg = 0; // reset var lastMsg to speed up lcd update
  138. }
  139. else if (invalueFloat < setTempMin) {
  140. setTempDay = setTempMin;
  141. lastMsg = 0; // reset var lastMsg to speed up lcd update
  142. }
  143. }
  144. else if ((char)payload[4] == 't' && \
  145. (char)payload[5] == 'l' && \
  146. (char)payload[6] == 'o' && \
  147. (char)payload[7] == 'w' && \
  148. (char)payload[8] == ' ' ) {
  149. char inValue[length - 9 + 1];
  150. for (int i = 0; i < length; i++) {
  151. inValue[i] = (char)payload[i + 9];
  152. }
  153. inValue[sizeof(inValue) - 1] = '\0';
  154. Serial.print(inValue);
  155. Serial.println();
  156. float invalueFloat = round(atof(inValue) * 2.0) / 2.0;
  157. Serial.print(invalueFloat);
  158. if (invalueFloat >= setTempMinLow && invalueFloat <= setTempMaxLow) {
  159. setTempNight = invalueFloat;
  160. lastMsg = 0; // reset var lastMsg to speed up lcd update
  161. }
  162. else if (invalueFloat > setTempMaxLow) {
  163. setTempNight = setTempMaxLow;
  164. lastMsg = 0; // reset var lastMsg to speed up lcd update
  165. }
  166. else if (invalueFloat < setTempMinLow) {
  167. setTempNight = setTempMinLow;
  168. lastMsg = 0; // reset var lastMsg to speed up lcd update
  169. }
  170. }
  171. else if ((char)payload[4] == 'm' && \
  172. (char)payload[5] == 'o' && \
  173. (char)payload[6] == 'd' && \
  174. (char)payload[7] == 'e' && \
  175. (char)payload[8] == ' ' ) {
  176. if ((char)payload[9] == '1') {
  177. // switch to mode default/day
  178. heatingMode = 1;
  179. lastMsg = 0; // reset var lastMsg to speed up lcd update
  180. }
  181. else if ((char)payload[9] == '2') {
  182. // switch to mode night/reduction
  183. heatingMode = 2;
  184. lastMsg = 0; // reset var lastMsg to speed up lcd update
  185. }
  186. }
  187. else if ((char)payload[4] == 'e' && \
  188. (char)payload[5] == 'n' && \
  189. (char)payload[6] == 'a' && \
  190. (char)payload[7] == 'b' && \
  191. (char)payload[8] == ' ' ) {
  192. if ((char)payload[9] == '0') {
  193. // switch heating off
  194. heatingEnabled = false;
  195. lastMsg = 0; // reset var lastMsg to speed up lcd update
  196. }
  197. else if ((char)payload[9] == '1') {
  198. // switch heating on
  199. heatingEnabled = true;
  200. lastMsg = 0; // reset var lastMsg to speed up lcd update
  201. }
  202. }
  203. }
  204. }
  205. void reconnect() {
  206. // Loop until we're reconnected
  207. while (!client.connected()) {
  208. Serial.print("Attempting MQTT connection...");
  209. // Create a random client ID
  210. String clientId = "ESP8266Client-";
  211. clientId += String(random(0xffff), HEX);
  212. // Attempt to connect
  213. if (client.connect(clientId.c_str())) {
  214. Serial.println("connected");
  215. // Once connected, publish an announcement...
  216. //client.publish("TestoutTopic", "hello world");
  217. // ... and resubscribe
  218. client.subscribe(mqtt_topic_in);
  219. } else {
  220. Serial.print("failed, rc=");
  221. Serial.print(client.state());
  222. Serial.println(" try again in 5 seconds");
  223. // Wait 5 seconds before retrying
  224. delay(5000);
  225. }
  226. }
  227. }
  228. void setup() {
  229. //WiFiManager wifiManager;
  230. //wifiManager.autoConnect("Thermostat", "configesp");
  231. //wifiManager.setConfigPortalTimeout(180);
  232. Serial.begin(115200);
  233. delay(300);
  234. Serial.print("WiFi Thermostat v");
  235. Serial.print(VERSION);
  236. Serial.println(" by Flo Kra");
  237. Serial.println("starting...");
  238. pinMode(PIN_RELAIS, OUTPUT);
  239. digitalWrite(PIN_RELAIS, HIGH);
  240. pinMode(PIN_BUTTON_PLUS, INPUT_PULLUP);
  241. pinMode(PIN_BUTTON_MINUS, INPUT_PULLUP);
  242. pinMode(PIN_BUTTON_MODE, INPUT_PULLUP);
  243. // DHT11/22 Temp/Hum-Sensor initialisieren
  244. dht.begin();
  245. // // lightMeter BH1750
  246. // //if (! lightMeter.begin(BH1750_CONTINUOUS_HIGH_RES_MODE_2)) {
  247. // if (! lightMeter.begin(RESOLUTION_AUTO_HIGH)) {
  248. // lightMeter_present = false;
  249. // delay(50);
  250. // Serial.println(F("ERROR: no BH1750 light meter found"));
  251. // }
  252. // else {
  253. // lightMeter_present = true;
  254. // delay(50);
  255. // Serial.println(F("BH1750 light meter found"));
  256. // }
  257. setup_wifi();
  258. MDNS.begin(host);
  259. httpUpdater.setup(&httpServer);
  260. httpServer.on("/", handleRoot);
  261. httpServer.onNotFound(handleNotFound);
  262. httpServer.begin();
  263. MDNS.addService("http", "tcp", 80);
  264. Serial.printf("HTTPUpdateServer ready! Open http://%s.local/update in your browser\n", host);
  265. client.setServer(mqtt_server, mqtt_port);
  266. client.setCallback(callback);
  267. initDisplay();
  268. }
  269. void loop() {
  270. httpServer.handleClient();
  271. if (!client.connected()) {
  272. reconnect();
  273. }
  274. client.loop();
  275. checkButtonStates();
  276. handleDisplayTimeout();
  277. if ( (millis() - lastMsg) > measureinterval) {
  278. lastMsg = millis();
  279. humidity = dht.readHumidity();
  280. temperature = dht.readTemperature(); // Read temperature as Celsius (the default)
  281. unsigned long heatingOnTime, heatingOffTime;
  282. if (heatingEnabled && turnHeatingOn) {
  283. heatingOnTime = (millis() - heatingLastOffMillis) / 1000;
  284. Serial.print("heatingOnTime: ");
  285. Serial.print(heatingOnTime);
  286. Serial.println();
  287. }
  288. else if (heatingEnabled && !turnHeatingOn) {
  289. heatingOffTime = (millis() - heatingLastOnMillis) / 1000;
  290. Serial.print("heatingOffTime: ");
  291. Serial.print(heatingOffTime);
  292. Serial.println();
  293. }
  294. if ( temperature >= setTemp ) {
  295. turnHeatingOn = false;
  296. heatingLastOnMillis = millis();
  297. digitalWrite(PIN_RELAIS, !RelaisOnState);
  298. Serial.println("heating off");
  299. Serial.print("last onTime: ");
  300. Serial.print(heatingOnTime);
  301. Serial.println();
  302. client.publish(mqtt_topic_heating, "off");
  303. //client.publish(mqtt_topic_heating_lastontime, heatingOnTime);
  304. }
  305. else if ( heatingEnabled && ( temperature < (setTemp - hysteresis) ) && ( heatingOffTime > minOffTime ) ) {
  306. turnHeatingOn = true;
  307. digitalWrite(PIN_RELAIS, RelaisOnState);
  308. Serial.println("heating on");
  309. Serial.print("last offTime: ");
  310. Serial.print(heatingOffTime);
  311. Serial.println();
  312. client.publish(mqtt_topic_heating, "on");
  313. //client.publish(mqtt_topic_heating_lastofftime, heatingOffTime);
  314. }
  315. // set target temp for heating mode
  316. if (heatingMode == 1) { // heating on - default/day mode
  317. setTemp = setTempDay;
  318. }
  319. else if (heatingMode == 2) { // heating of - night/reduction mode
  320. setTemp = setTempNight;
  321. }
  322. // if (lightMeter_present) {
  323. // delay(50);
  324. // lux = lightMeter.readLightLevel();
  325. //
  326. // String lux_str = String(lux, 1); //converting humidity (the float variable above) to a string with 0 decimals
  327. // char lux_chararr[lux_str.length() + 1];
  328. // lux_str.toCharArray(lux_chararr, lux_str.length() + 1); //packaging up the data to publish to mqtt whoa...
  329. //
  330. // Serial.print("Lux: ");
  331. // Serial.println(lux_str);
  332. // client.publish(mqtt_topic_lux, lux_chararr);
  333. // }
  334. String hum_str = String(humidity, 0); //converting humidity (the float variable above) to a string with 0 decimals
  335. char hum_chararr[hum_str.length() + 1];
  336. hum_str.toCharArray(hum_chararr, hum_str.length() + 1); //packaging up the data to publish to mqtt whoa...
  337. String temp_str = String(temperature, 1); //converting Temperature (the float variable above) to a string with 1 decimal
  338. char temp_chararr[temp_str.length() + 1];
  339. temp_str.toCharArray(temp_chararr, temp_str.length() + 1); //packaging up the data to publish to mqtt whoa...
  340. char setTemp_chararr[5]; //result string 4 positions + \0 at the end
  341. // convert float to fprintf type string format 2 positions with 1 decimal place
  342. dtostrf(setTemp, 2, 1, setTemp_chararr );
  343. Serial.println("Publish messages temp/hum ");
  344. client.publish(mqtt_topic_temp, temp_chararr);
  345. client.publish(mqtt_topic_hum, hum_chararr);
  346. client.publish(mqtt_topic_settemp, setTemp_chararr);
  347. Serial.print("set temp: ");
  348. Serial.println(setTemp_chararr);
  349. Serial.print("current temp: ");
  350. Serial.println(temp_str);
  351. // print is-temperature incl = and ° symbol + humidity to lcd, line 1, first 11 chars
  352. lcd.setCursor(0, 0);
  353. lcd.write(0x3D); // = Zeichen
  354. lcd.print(" ");
  355. //if (temperature < -9) lcd.print(" ");
  356. //else if (temperature < 0) lcd.print("");
  357. //else if (temperature < 10) lcd.print(" ");
  358. //else lcd.print(" ");
  359. //lcd.print(tempstr2);
  360. lcd.print(temp_str); // inside temperature should hopefully always be 2.1 chars, so no special formatting necessary
  361. lcd.write(0xDF); // degree symbol
  362. lcd.print(" ");
  363. //lcd.print(humstr2);
  364. lcd.print(hum_str); // always 2 chars
  365. lcd.print("%");
  366. // display current mode on LCD
  367. lcd.setCursor(13, 0); // to char 15, line 1
  368. if (heatingEnabled) {
  369. if ( heatingMode == 1 ) { // day/normal mode
  370. lcd.print(" ");
  371. lcd.write((uint8_t)1); // sun symbol if mode is day/normal
  372. lcd.print(" ");
  373. }
  374. else if ( heatingMode == 2 ) { // night/reduction mode
  375. lcd.print(" ");
  376. lcd.write((uint8_t)0); // moon symbol if mode is night/reduction
  377. lcd.print(" ");
  378. }
  379. }
  380. else lcd.print(" "); // mode is heating off
  381. // display target temperature to line 2, 8 chars length incl space at the end
  382. lcd.setCursor(0, 1);
  383. // when the heating mode is OFF, do not display target temp - instead show "Heating off" info in line 2
  384. if ( !heatingEnabled ) {
  385. // 1234567890123456
  386. lcd.print(" Heizung aus ");
  387. }
  388. else {
  389. lcd.write((uint8_t)2); // Pfeil rechts
  390. lcd.print(" ");
  391. lcd.print(setTemp_chararr);
  392. lcd.write(0xDF); // degree symbol
  393. lcd.print(" ");
  394. // display status info to line 2 from char 9 -> 8 chars length
  395. lcd.setCursor(8, 1);
  396. if (turnHeatingOn) {
  397. // 12345678
  398. lcd.print("heizen..");
  399. }
  400. else if ( heatingMode == 1 ) { // day/normal mode
  401. // 12345678
  402. lcd.print(" ");
  403. }
  404. else if ( heatingMode == 2 ) { // night/reduction mode
  405. // 12345678
  406. lcd.print("N-Absenk");
  407. }
  408. else lcd.print(" ");
  409. }
  410. }
  411. // Check if any reads failed and exit early (to try again).
  412. //if (isnan(hum) || isnan(temp)) {
  413. // Serial.println("Failed to read from DHT sensor!");
  414. // return;
  415. //}
  416. }