ESP32 / ESP8266 & BME280 / SHT2x sensor with InfluxDB support
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

main.cpp 33KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242
  1. /*
  2. * main.cpp
  3. *
  4. * ESP8266 / ESP32 Environmental Sensor
  5. *
  6. * ----------------------------------------------------------------------------
  7. * "THE BEER-WARE LICENSE" (Revision 42):
  8. * <xythobuz@xythobuz.de> wrote this file. As long as you retain this notice
  9. * you can do whatever you want with this stuff. If we meet some day, and you
  10. * think this stuff is worth it, you can buy me a beer in return. Thomas Buck
  11. * ----------------------------------------------------------------------------
  12. */
  13. #include <Arduino.h>
  14. #include <Adafruit_BME280.h>
  15. #include <SHT2x.h>
  16. #ifdef ENABLE_CCS811
  17. #include <Adafruit_CCS811.h>
  18. #endif // ENABLE_CCS811
  19. #if defined(ARDUINO_ARCH_ESP8266)
  20. #include <ESP8266WiFi.h>
  21. #include <ESP8266WebServer.h>
  22. #include <ESP8266mDNS.h>
  23. #define ESP_PLATFORM_NAME "ESP8266"
  24. #elif defined(ARDUINO_ARCH_ESP32)
  25. #include <WiFi.h>
  26. #include <WebServer.h>
  27. #include <ESPmDNS.h>
  28. #define ESP_PLATFORM_NAME "ESP32"
  29. #endif
  30. #include "config.h"
  31. #include "moisture.h"
  32. #include "relais.h"
  33. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  34. #include <Wire.h>
  35. #include "SimpleUpdater.h"
  36. #define BUILTIN_LED_PIN 1
  37. UPDATE_WEB_SERVER server(80);
  38. SimpleUpdater updater;
  39. #ifdef ENABLE_MQTT
  40. #include <PubSubClient.h>
  41. WiFiClient mqttClient;
  42. PubSubClient mqtt(mqttClient);
  43. unsigned long last_mqtt_reconnect_time = 0;
  44. #endif // ENABLE_MQTT
  45. #elif defined(ARDUINO_ARCH_AVR)
  46. #define ESP_PLATFORM_NAME "Uno WiFi"
  47. #define BUILTIN_LED_PIN 13
  48. #endif
  49. #ifdef ENABLE_INFLUXDB_LOGGING
  50. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  51. #include <InfluxDb.h>
  52. #else
  53. #include "SimpleInflux.h"
  54. #endif
  55. Influxdb influx(INFLUXDB_HOST, INFLUXDB_PORT);
  56. #define INFLUX_MAX_ERRORS_RESET 10
  57. int error_count = 0;
  58. #endif // ENABLE_INFLUXDB_LOGGING
  59. #define SHT_I2C_ADDRESS HTDU21D_ADDRESS
  60. #define BME_I2C_ADDRESS_1 0x76
  61. #define BME_I2C_ADDRESS_2 0x77
  62. #define CCS811_ADDRESS_1 0x5A
  63. #define CCS811_ADDRESS_2 0x5B
  64. #if defined(ARDUINO_ARCH_ESP8266)
  65. #define I2C_SDA_PIN 2
  66. #define I2C_SCL_PIN 0
  67. TwoWire Wire2;
  68. SHT2x sht(SHT_I2C_ADDRESS, &Wire2);
  69. #elif defined(ARDUINO_ARCH_ESP32)
  70. SHT2x sht(SHT_I2C_ADDRESS, &Wire);
  71. #elif defined(ARDUINO_ARCH_AVR)
  72. #include <UnoWiFiDevEdSerial1.h>
  73. #include <WiFiLink.h>
  74. WiFiServer server(80);
  75. SHT2x sht(SHT_I2C_ADDRESS, &Wire);
  76. #endif
  77. Adafruit_BME280 bme1, bme2;
  78. bool found_bme1 = false;
  79. bool found_bme2 = false;
  80. bool found_sht = false;
  81. #ifdef ENABLE_CCS811
  82. Adafruit_CCS811 ccs1, ccs2;
  83. bool found_ccs1 = false;
  84. bool found_ccs2 = false;
  85. bool ccs1_data_valid = false;
  86. bool ccs2_data_valid = false;
  87. int ccs1_error_code = 0;
  88. int ccs2_error_code = 0;
  89. #endif // ENABLE_CCS811
  90. unsigned long last_server_handle_time = 0;
  91. unsigned long last_db_write_time = 0;
  92. unsigned long last_led_blink_time = 0;
  93. void writeDatabase();
  94. static float bme1_temp(void) {
  95. while (1) {
  96. float a = bme1.readTemperature();
  97. float b = bme1.readTemperature();
  98. if ((a > b) && ((a - b) < 2.0)) {
  99. return (a + b) / 2.0;
  100. }
  101. if ((a < b) && ((b - a) < 2.0)) {
  102. return (a + b) / 2.0;
  103. }
  104. }
  105. return 0.0;
  106. }
  107. static float bme2_temp(void) {
  108. while (1) {
  109. float a = bme2.readTemperature();
  110. float b = bme2.readTemperature();
  111. if ((a > b) && ((a - b) < 2.0)) {
  112. return (a + b) / 2.0;
  113. }
  114. if ((a < b) && ((b - a) < 2.0)) {
  115. return (a + b) / 2.0;
  116. }
  117. }
  118. return 0.0;
  119. }
  120. static float bme1_humid(void) {
  121. while (1) {
  122. float a = bme1.readHumidity();
  123. float b = bme1.readHumidity();
  124. if ((a > b) && ((a - b) < 2.0)) {
  125. return (a + b) / 2.0;
  126. }
  127. if ((a < b) && ((b - a) < 2.0)) {
  128. return (a + b) / 2.0;
  129. }
  130. }
  131. return 0.0;
  132. }
  133. static float bme2_humid(void) {
  134. while (1) {
  135. float a = bme2.readHumidity();
  136. float b = bme2.readHumidity();
  137. if ((a > b) && ((a - b) < 2.0)) {
  138. return (a + b) / 2.0;
  139. }
  140. if ((a < b) && ((b - a) < 2.0)) {
  141. return (a + b) / 2.0;
  142. }
  143. }
  144. return 0.0;
  145. }
  146. static float bme1_pressure(void) {
  147. while (1) {
  148. float a = bme1.readPressure();
  149. float b = bme1.readPressure();
  150. if ((a > b) && ((a - b) < 2.0)) {
  151. return (a + b) / 2.0;
  152. }
  153. if ((a < b) && ((b - a) < 2.0)) {
  154. return (a + b) / 2.0;
  155. }
  156. }
  157. return 0.0;
  158. }
  159. static float bme2_pressure(void) {
  160. while (1) {
  161. float a = bme2.readPressure();
  162. float b = bme2.readPressure();
  163. if ((a > b) && ((a - b) < 2.0)) {
  164. return (a + b) / 2.0;
  165. }
  166. if ((a < b) && ((b - a) < 2.0)) {
  167. return (a + b) / 2.0;
  168. }
  169. }
  170. return 0.0;
  171. }
  172. static float sht_temp(void) {
  173. while (1) {
  174. float a = sht.GetTemperature();
  175. float b = sht.GetTemperature();
  176. if ((a > b) && ((a - b) < 2.0)) {
  177. return (a + b) / 2.0;
  178. }
  179. if ((a < b) && ((b - a) < 2.0)) {
  180. return (a + b) / 2.0;
  181. }
  182. }
  183. return 0.0;
  184. }
  185. static float sht_humid(void) {
  186. while (1) {
  187. float a = sht.GetHumidity();
  188. float b = sht.GetHumidity();
  189. if ((a > b) && ((a - b) < 2.0)) {
  190. return (a + b) / 2.0;
  191. }
  192. if ((a < b) && ((b - a) < 2.0)) {
  193. return (a + b) / 2.0;
  194. }
  195. }
  196. return 0.0;
  197. }
  198. #ifdef ENABLE_CCS811
  199. static float ccs1_eco2(void) {
  200. return ccs1.geteCO2();
  201. }
  202. static float ccs1_tvoc(void) {
  203. return ccs1.getTVOC();
  204. }
  205. static float ccs2_eco2(void) {
  206. return ccs2.geteCO2();
  207. }
  208. static float ccs2_tvoc(void) {
  209. return ccs2.getTVOC();
  210. }
  211. #endif // ENABLE_CCS811
  212. #if defined(ARDUINO_ARCH_AVR)
  213. #define ARDUINO_SEND_PARTIAL_PAGE() do { \
  214. size_t len = message.length(), off = 0; \
  215. while (off < len) { \
  216. if ((len - off) >= 50) { \
  217. client.write(message.c_str() + off, 50); \
  218. off += 50; \
  219. } else { \
  220. client.write(message.c_str() + off, len - off); \
  221. off = len; \
  222. } \
  223. } \
  224. message = ""; \
  225. } while (false);
  226. #else
  227. #define ARDUINO_SEND_PARTIAL_PAGE() while (false) { }
  228. #endif
  229. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  230. void handlePage(int mode = -1, int id = 0) {
  231. #else
  232. void handlePage(WiFiClient &client, int mode = -1, int id = 0) {
  233. #endif
  234. String message;
  235. message += F("<html><head>");
  236. message += F("<title>" ESP_PLATFORM_NAME " Environment Sensor</title>");
  237. message += F("</head><body>");
  238. message += F("<h1>" ESP_PLATFORM_NAME " Environment Sensor</h1>");
  239. message += F("\n<p>\n");
  240. message += F("Version: ");
  241. message += ESP_ENV_VERSION;
  242. message += F("\n<br>\n");
  243. message += F("Location: ");
  244. message += SENSOR_LOCATION;
  245. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  246. message += F("\n<br>\n");
  247. message += F("MAC: ");
  248. message += WiFi.macAddress();
  249. #endif
  250. message += F("\n</p>\n");
  251. ARDUINO_SEND_PARTIAL_PAGE();
  252. #if defined(ARDUINO_ARCH_ESP8266)
  253. message += F("<p>");
  254. message += F("Reset reason: ");
  255. message += ESP.getResetReason();
  256. message += F("<br>");
  257. message += F("Free heap: ");
  258. message += String(ESP.getFreeHeap());
  259. message += F(" (");
  260. message += String(ESP.getHeapFragmentation());
  261. message += F("% fragmentation)");
  262. message += F("<br>");
  263. message += F("Free sketch space: ");
  264. message += String(ESP.getFreeSketchSpace());
  265. message += F("<br>");
  266. message += F("Flash chip real size: ");
  267. message += String(ESP.getFlashChipRealSize());
  268. if (ESP.getFlashChipSize() != ESP.getFlashChipRealSize()) {
  269. message += F("<br>");
  270. message += F("WARNING: sdk chip size (");
  271. message += (ESP.getFlashChipSize());
  272. message += F(") does not match!");
  273. }
  274. message += F("</p>");
  275. #elif defined(ARDUINO_ARCH_ESP32)
  276. message += F("<p>");
  277. message += F("Free heap: ");
  278. message += String(ESP.getFreeHeap() / 1024.0);
  279. message += F("k<br>");
  280. message += F("Free sketch space: ");
  281. message += String(ESP.getFreeSketchSpace() / 1024.0);
  282. message += F("k<br>");
  283. message += F("Flash chip size: ");
  284. message += String(ESP.getFlashChipSize() / 1024.0);
  285. message += F("k</p>");
  286. #endif
  287. message += F("\n<p>\n");
  288. if (found_bme1) {
  289. message += F("BME280 Low:");
  290. message += F("\n<br>\n");
  291. message += F("Temperature: ");
  292. message += String(bme1_temp());
  293. message += F("\n<br>\n");
  294. message += F("Humidity: ");
  295. message += String(bme1_humid());
  296. message += F("\n<br>\n");
  297. message += F("Pressure: ");
  298. message += String(bme1_pressure());
  299. } else {
  300. message += F("BME280 (low) not connected!");
  301. }
  302. message += F("\n</p>\n");
  303. message += F("\n<p>\n");
  304. if (found_bme2) {
  305. message += F("BME280 High:");
  306. message += F("\n<br>\n");
  307. message += F("Temperature: ");
  308. message += String(bme2_temp());
  309. message += F("\n<br>\n");
  310. message += F("Humidity: ");
  311. message += String(bme2_humid());
  312. message += F("\n<br>\n");
  313. message += F("Pressure: ");
  314. message += String(bme2_pressure());
  315. } else {
  316. message += F("BME280 (high) not connected!");
  317. }
  318. message += F("\n</p>\n");
  319. ARDUINO_SEND_PARTIAL_PAGE();
  320. message += F("\n<p>\n");
  321. if (found_sht) {
  322. message += F("SHT21:");
  323. message += F("\n<br>\n");
  324. message += F("Temperature: ");
  325. message += String(sht_temp());
  326. message += F("\n<br>\n");
  327. message += F("Humidity: ");
  328. message += String(sht_humid());
  329. } else {
  330. message += F("SHT21 not connected!");
  331. }
  332. message += F("\n</p>\n");
  333. #ifdef ENABLE_CCS811
  334. message += F("\n<p>\n");
  335. if (found_ccs1) {
  336. message += F("CCS811 Low:");
  337. message += F("\n<br>\n");
  338. message += F("eCO2: ");
  339. message += String(ccs1_eco2());
  340. message += F("ppm");
  341. message += F("\n<br>\n");
  342. message += F("TVOC: ");
  343. message += String(ccs1_tvoc());
  344. message += F("ppb");
  345. if (!ccs1_data_valid) {
  346. message += F("\n<br>\n");
  347. message += F("Data invalid (");
  348. message += String(ccs1_error_code);
  349. message += F(")!");
  350. }
  351. } else {
  352. message += F("CCS811 (Low) not connected!");
  353. }
  354. message += F("\n</p>\n");
  355. message += F("\n<p>\n");
  356. if (found_ccs2) {
  357. message += F("CCS811 High:");
  358. message += F("\n<br>\n");
  359. message += F("eCO2: ");
  360. message += String(ccs2_eco2());
  361. message += F("ppm");
  362. message += F("\n<br>\n");
  363. message += F("TVOC: ");
  364. message += String(ccs2_tvoc());
  365. message += F("ppb");
  366. if (!ccs2_data_valid) {
  367. message += F("\n<br>\n");
  368. message += F("Data invalid (");
  369. message += String(ccs2_error_code);
  370. message += F(")!");
  371. }
  372. } else {
  373. message += F("CCS811 (High) not connected!");
  374. }
  375. message += F("\n</p>\n");
  376. #endif // ENABLE_CCS811
  377. ARDUINO_SEND_PARTIAL_PAGE();
  378. #ifdef FEATURE_MOISTURE
  379. for (int i = 0; i < moisture_count(); i++) {
  380. int moisture = moisture_read(i);
  381. if (moisture < moisture_max()) {
  382. message += F("\n<p>\n");
  383. message += F("Sensor ");
  384. message += String(i + 1);
  385. message += F(":\n<br>\n");
  386. message += F("Moisture: ");
  387. message += String(moisture);
  388. message += F(" / ");
  389. message += String(moisture_max());
  390. message += F("\n</p>\n");
  391. }
  392. }
  393. if (moisture_count() <= 0) {
  394. message += F("\n<p>\n");
  395. message += F("No moisture sensors configured!");
  396. message += F("\n</p>\n");
  397. }
  398. ARDUINO_SEND_PARTIAL_PAGE();
  399. #endif // FEATURE_MOISTURE
  400. #ifdef FEATURE_RELAIS
  401. message += F("\n<p>\n");
  402. for (int i = 0; i < relais_count(); i++) {
  403. message += String(F("<a href=\"/on?id=")) + String(i) + String(F("\">Relais ")) + String(i) + String(F(" On (")) + relais_name(i) + String(F(")</a><br>\n"));
  404. message += String(F("<a href=\"/off?id=")) + String(i) + String(F("\">Relais ")) + String(i) + String(F(" Off (")) + relais_name(i) + String(F(")</a><br>\n"));
  405. }
  406. message += String(F("<a href=\"/on?id=")) + String(relais_count()) + String(F("\">All Relais On</a><br>\n"));
  407. message += String(F("<a href=\"/off?id=")) + String(relais_count()) + String(F("\">All Relais Off</a><br>\n"));
  408. message += F("</p>\n");
  409. if (mode >= 0) {
  410. message += F("<p>");
  411. message += F("Turned Relais ");
  412. message += (id < relais_count()) ? String(id) : String(F("1-4"));
  413. message += (mode ? String(F(" On")) : String(F(" Off")));
  414. message += F("</p>\n");
  415. }
  416. message += F("\n<p>\n");
  417. for (int i = 0; i < relais_count(); i++) {
  418. message += String(F("Relais ")) + String(i) + String(F(" (")) + relais_name(i) + String(F(") = ")) + (relais_get(i) ? String(F("On")) : String(F("Off"))) + String(F("<br>\n"));
  419. }
  420. message += F("</p>\n");
  421. ARDUINO_SEND_PARTIAL_PAGE();
  422. #endif // FEATURE_RELAIS
  423. #if ! defined(ARDUINO_ARCH_AVR)
  424. message += F("<p>");
  425. message += F("Try <a href=\"/update\">/update</a> for OTA firmware updates!");
  426. message += F("</p>");
  427. #endif
  428. message += F("<p>");
  429. #ifdef ENABLE_INFLUXDB_LOGGING
  430. message += F("InfluxDB: ");
  431. message += INFLUXDB_DATABASE;
  432. message += F(" @ ");
  433. message += INFLUXDB_HOST;
  434. message += F(":");
  435. message += String(INFLUXDB_PORT);
  436. #else
  437. message += F("InfluxDB logging not enabled!");
  438. #endif
  439. message += F("</p>");
  440. message += F("</body></html>");
  441. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  442. server.send(200, "text/html", message);
  443. #else
  444. ARDUINO_SEND_PARTIAL_PAGE();
  445. #endif
  446. }
  447. #ifdef FEATURE_RELAIS
  448. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  449. void handleOn() {
  450. #else
  451. void handleOn(WiFiClient &client) {
  452. #endif
  453. String id_string = server.arg("id");
  454. int id = id_string.toInt();
  455. if ((id >= 0) && (id < relais_count())) {
  456. relais_set(id, 1);
  457. } else {
  458. for (int i = 0; i < relais_count(); i++) {
  459. relais_set(i, 1);
  460. }
  461. }
  462. #ifdef ENABLE_INFLUXDB_LOGGING
  463. writeDatabase();
  464. #endif // ENABLE_INFLUXDB_LOGGING
  465. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  466. handlePage(1, id);
  467. #else
  468. handlePage(client, 1, id);
  469. #endif
  470. }
  471. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  472. void handleOff() {
  473. #else
  474. void handleOff(WiFiClient &client) {
  475. #endif
  476. String id_string = server.arg("id");
  477. int id = id_string.toInt();
  478. if ((id >= 0) && (id < relais_count())) {
  479. relais_set(id, 0);
  480. } else {
  481. for (int i = 0; i < relais_count(); i++) {
  482. relais_set(i, 0);
  483. }
  484. }
  485. #ifdef ENABLE_INFLUXDB_LOGGING
  486. writeDatabase();
  487. #endif // ENABLE_INFLUXDB_LOGGING
  488. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  489. handlePage(0, id);
  490. #else
  491. handlePage(client, 0, id);
  492. #endif
  493. }
  494. #endif // FEATURE_RELAIS
  495. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  496. void handleRoot() {
  497. handlePage();
  498. #else
  499. void handleRoot(WiFiClient &client) {
  500. handlePage(client);
  501. #endif
  502. }
  503. #ifdef ENABLE_MQTT
  504. void writeMQTT() {
  505. if (!mqtt.connected()) {
  506. return;
  507. }
  508. if (found_bme1) {
  509. mqtt.publish(SENSOR_LOCATION "/temperature", String(bme1_temp()).c_str());
  510. mqtt.publish(SENSOR_LOCATION "/humidity", String(bme1_humid()).c_str());
  511. mqtt.publish(SENSOR_LOCATION "/pressure", String(bme1_pressure()).c_str());
  512. } else if (found_bme2) {
  513. mqtt.publish(SENSOR_LOCATION "/temperature", String(bme2_temp()).c_str());
  514. mqtt.publish(SENSOR_LOCATION "/humidity", String(bme2_humid()).c_str());
  515. mqtt.publish(SENSOR_LOCATION "/pressure", String(bme2_pressure()).c_str());
  516. } else if (found_sht) {
  517. mqtt.publish(SENSOR_LOCATION "/temperature", String(sht_temp()).c_str());
  518. mqtt.publish(SENSOR_LOCATION "/humidity", String(sht_humid()).c_str());
  519. }
  520. #ifdef ENABLE_CCS811
  521. if (found_ccs1) {
  522. mqtt.publish(SENSOR_LOCATION "/eco2", String(ccs1_eco2()).c_str());
  523. mqtt.publish(SENSOR_LOCATION "/tvoc", String(ccs1_tvoc()).c_str());
  524. } else if (found_ccs2) {
  525. mqtt.publish(SENSOR_LOCATION "/eco2", String(ccs2_eco2()).c_str());
  526. mqtt.publish(SENSOR_LOCATION "/tvoc", String(ccs2_tvoc()).c_str());
  527. }
  528. #endif // ENABLE_CCS811
  529. }
  530. void mqttCallback(char* topic, byte* payload, unsigned int length) {
  531. #ifdef FEATURE_RELAIS
  532. int state = 0;
  533. int id = -1;
  534. String ts(topic), ps((char *)payload);
  535. String our_topic(SENSOR_LOCATION);
  536. our_topic += "/";
  537. if (!ts.startsWith(our_topic)) {
  538. Serial.print(F("Unknown MQTT room "));
  539. Serial.println(ts);
  540. return;
  541. }
  542. String ids = ts.substring(our_topic.length());
  543. for (int i = 0; i < relais_count(); i++) {
  544. if (ids == relais_name(i)) {
  545. id = i;
  546. break;
  547. }
  548. }
  549. if (id < 0) {
  550. Serial.print(F("Unknown MQTT topic "));
  551. Serial.println(ts);
  552. return;
  553. }
  554. if (ps.indexOf("on") != -1) {
  555. state = 1;
  556. } else if (ps.indexOf("off") != -1) {
  557. state = 0;
  558. } else {
  559. return;
  560. }
  561. if ((id >= 0) && (id < relais_count())) {
  562. relais_set(id, state);
  563. #ifdef ENABLE_INFLUXDB_LOGGING
  564. writeDatabase();
  565. #endif // ENABLE_INFLUXDB_LOGGING
  566. }
  567. #endif // FEATURE_RELAIS
  568. }
  569. void mqttReconnect() {
  570. // Create a random client ID
  571. String clientId = F("ESP-" SENSOR_LOCATION "-");
  572. clientId += String(random(0xffff), HEX);
  573. // Attempt to connect
  574. #if defined(MQTT_USER) && defined(MQTT_PASS)
  575. if (mqtt.connect(clientId.c_str(), MQTT_USER, MQTT_PASS)) {
  576. #else
  577. if (mqtt.connect(clientId.c_str())) {
  578. #endif
  579. // Once connected, publish an announcement...
  580. mqtt.publish(SENSOR_LOCATION, "sensor online");
  581. // ... and resubscribe
  582. #ifdef FEATURE_RELAIS
  583. mqtt.subscribe(SENSOR_LOCATION);
  584. for (int i = 0; i < relais_count(); i++) {
  585. String topic(SENSOR_LOCATION);
  586. topic += String("/") + relais_name(i);
  587. mqtt.subscribe(topic.c_str());
  588. }
  589. #endif // FEATURE_RELAIS
  590. }
  591. }
  592. #endif // ENABLE_MQTT
  593. void setup() {
  594. pinMode(BUILTIN_LED_PIN, OUTPUT);
  595. Serial.begin(115200);
  596. // Blink LED for init
  597. for (int i = 0; i < 2; i++) {
  598. digitalWrite(BUILTIN_LED_PIN, LOW); // LED on
  599. delay(LED_INIT_BLINK_INTERVAL);
  600. digitalWrite(BUILTIN_LED_PIN, HIGH); // LED off
  601. delay(LED_INIT_BLINK_INTERVAL);
  602. }
  603. Serial.print(F("Relais"));
  604. relais_init();
  605. Serial.print(F("Moisture"));
  606. moisture_init();
  607. // Init I2C and try to connect to sensors
  608. #if defined(ARDUINO_ARCH_ESP8266)
  609. Serial.print(F("Wire2"));
  610. Wire2.begin(I2C_SDA_PIN, I2C_SCL_PIN);
  611. Serial.print(F("BME"));
  612. found_bme1 = (!bme1.begin(BME_I2C_ADDRESS_1, &Wire2)) ? false : true;
  613. found_bme2 = (!bme2.begin(BME_I2C_ADDRESS_2, &Wire2)) ? false : true;
  614. #ifdef ENABLE_CCS811
  615. Serial.print(F("CCS"));
  616. found_ccs1 = ccs1.begin(CCS811_ADDRESS_1, &Wire2);
  617. found_ccs2 = ccs2.begin(CCS811_ADDRESS_2, &Wire2);
  618. #endif // ENABLE_CCS811
  619. #elif defined(ARDUINO_ARCH_ESP32)
  620. Serial.print(F("Wire"));
  621. Wire.begin();
  622. Serial.print(F("BME"));
  623. found_bme1 = (!bme1.begin(BME_I2C_ADDRESS_1, &Wire)) ? false : true;
  624. found_bme2 = (!bme2.begin(BME_I2C_ADDRESS_2, &Wire)) ? false : true;
  625. #ifdef ENABLE_CCS811
  626. Serial.print(F("CCS"));
  627. found_ccs1 = ccs1.begin(CCS811_ADDRESS_1, &Wire);
  628. found_ccs2 = ccs2.begin(CCS811_ADDRESS_2, &Wire);
  629. #endif // ENABLE_CCS811
  630. #elif defined(ARDUINO_ARCH_AVR)
  631. Serial.print(F("BME"));
  632. found_bme1 = (!bme1.begin(BME_I2C_ADDRESS_1, &Wire)) ? false : true;
  633. found_bme2 = (!bme2.begin(BME_I2C_ADDRESS_2, &Wire)) ? false : true;
  634. #ifdef ENABLE_CCS811
  635. Serial.print(F("CCS"));
  636. found_ccs1 = ccs1.begin(CCS811_ADDRESS_1, &Wire);
  637. found_ccs2 = ccs2.begin(CCS811_ADDRESS_2, &Wire);
  638. #endif // ENABLE_CCS811
  639. #endif
  640. Serial.print(F("SHT"));
  641. found_sht = sht.GetAlive();
  642. // Build hostname string
  643. String hostname = SENSOR_HOSTNAME_PREFIX;
  644. hostname += SENSOR_LOCATION;
  645. #if defined(ARDUINO_ARCH_ESP8266)
  646. // Connect to WiFi AP
  647. Serial.print(F("Connecting WiFi"));
  648. WiFi.hostname(hostname);
  649. WiFi.mode(WIFI_STA);
  650. WiFi.begin(WIFI_SSID, WIFI_PASS);
  651. while (WiFi.status() != WL_CONNECTED) {
  652. delay(LED_CONNECT_BLINK_INTERVAL);
  653. digitalWrite(BUILTIN_LED_PIN, !digitalRead(BUILTIN_LED_PIN));
  654. Serial.print(F("."));
  655. }
  656. Serial.println(F("\nWiFi connected!"));
  657. #elif defined(ARDUINO_ARCH_ESP32)
  658. // Set hostname workaround
  659. WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
  660. WiFi.setHostname(hostname.c_str());
  661. // Workaround for WiFi connecting only every 2nd reset
  662. // https://github.com/espressif/arduino-esp32/issues/2501#issuecomment-513602522
  663. WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info) {
  664. if (info.disconnected.reason == 202) {
  665. esp_sleep_enable_timer_wakeup(10);
  666. esp_deep_sleep_start();
  667. delay(100);
  668. }
  669. }, WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
  670. // Connect to WiFi AP
  671. Serial.print(F("Connecting WiFi"));
  672. WiFi.mode(WIFI_STA);
  673. WiFi.begin(WIFI_SSID, WIFI_PASS);
  674. while (WiFi.status() != WL_CONNECTED) {
  675. delay(LED_CONNECT_BLINK_INTERVAL);
  676. digitalWrite(BUILTIN_LED_PIN, !digitalRead(BUILTIN_LED_PIN));
  677. Serial.print(F("."));
  678. }
  679. Serial.println(F("\nWiFi connected!"));
  680. // Set hostname workaround
  681. WiFi.setHostname(hostname.c_str());
  682. #elif defined(ARDUINO_ARCH_AVR)
  683. Serial1.begin(115200);
  684. WiFi.init(&Serial1);
  685. Serial.print(F("Connecting WiFi"));
  686. WiFi.begin(WIFI_SSID, WIFI_PASS);
  687. while (WiFi.status() != WL_CONNECTED) {
  688. delay(LED_CONNECT_BLINK_INTERVAL);
  689. digitalWrite(BUILTIN_LED_PIN, !digitalRead(BUILTIN_LED_PIN));
  690. Serial.print(F("."));
  691. }
  692. Serial.println(F("\nWiFi connected!"));
  693. #endif
  694. Serial.println(F("Seeding"));
  695. randomSeed(micros());
  696. #ifdef ENABLE_MQTT
  697. Serial.println(F("MQTT"));
  698. mqtt.setServer(MQTT_HOST, MQTT_PORT);
  699. mqtt.setCallback(mqttCallback);
  700. #endif // ENABLE_MQTT
  701. #ifdef ENABLE_INFLUXDB_LOGGING
  702. Serial.println(F("Influx"));
  703. influx.setDb(INFLUXDB_DATABASE);
  704. #endif // ENABLE_INFLUXDB_LOGGING
  705. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  706. // Setup HTTP Server
  707. Serial.println(F("HTTP"));
  708. MDNS.begin(hostname.c_str());
  709. updater.setup(&server);
  710. server.on("/", handleRoot);
  711. #ifdef FEATURE_RELAIS
  712. server.on("/on", handleOn);
  713. server.on("/off", handleOff);
  714. #endif // FEATURE_RELAIS
  715. MDNS.addService("http", "tcp", 80);
  716. #endif
  717. server.begin();
  718. }
  719. #if defined(ARDUINO_ARCH_AVR)
  720. void http_server() {
  721. // listen for incoming clients
  722. WiFiClient client = server.available();
  723. if (client) {
  724. Serial.println(F("new http client"));
  725. // an http request ends with a blank line
  726. boolean currentLineIsBlank = true;
  727. while (client.connected()) {
  728. if (client.available()) {
  729. char c = client.read();
  730. Serial.write(c);
  731. // if you've gotten to the end of the line (received a newline
  732. // character) and the line is blank, the http request has ended,
  733. // so you can send a reply
  734. if ((c == '\n') && currentLineIsBlank) {
  735. // send a standard http response header
  736. client.println(F("HTTP/1.1 200 OK"));
  737. client.println(F("Content-Type: text/html"));
  738. client.println(F("Connection: close"));
  739. client.println();
  740. // TODO parse path and handle different pages
  741. handleRoot(client);
  742. break;
  743. }
  744. if (c == '\n') {
  745. // you're starting a new line
  746. currentLineIsBlank = true;
  747. } else if (c != '\r') {
  748. // you've gotten a character on the current line
  749. currentLineIsBlank = false;
  750. }
  751. }
  752. }
  753. // give the web browser time to receive the data
  754. delay(10);
  755. // close the connection
  756. client.stop();
  757. Serial.println(F("http client disconnected"));
  758. }
  759. }
  760. #endif
  761. void handleServers() {
  762. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  763. server.handleClient();
  764. #else
  765. http_server();
  766. #endif
  767. #if defined(ARDUINO_ARCH_ESP8266)
  768. MDNS.update();
  769. #endif
  770. }
  771. #ifdef ENABLE_INFLUXDB_LOGGING
  772. static boolean writeMeasurement(InfluxData &measurement) {
  773. boolean success = influx.write(measurement);
  774. if (!success) {
  775. error_count++;
  776. for (int i = 0; i < 10; i++) {
  777. digitalWrite(BUILTIN_LED_PIN, LOW); // LED on
  778. delay(LED_ERROR_BLINK_INTERVAL);
  779. digitalWrite(BUILTIN_LED_PIN, HIGH); // LED off
  780. delay(LED_ERROR_BLINK_INTERVAL);
  781. }
  782. }
  783. return success;
  784. }
  785. void writeDatabase() {
  786. #if defined(ARDUINO_ARCH_AVR)
  787. Serial.println(F("Writing to InfluxDB"));
  788. InfluxData measurement("");
  789. #endif
  790. if (found_bme1) {
  791. #if defined(ARDUINO_ARCH_AVR)
  792. measurement.clear();
  793. measurement.setName("environment");
  794. #else
  795. InfluxData measurement("environment");
  796. #endif
  797. measurement.addTag("location", SENSOR_LOCATION);
  798. measurement.addTag("placement", "1");
  799. measurement.addTag("sensor", "bme280");
  800. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  801. measurement.addTag("device", WiFi.macAddress());
  802. #endif
  803. measurement.addValue("temperature", bme1_temp());
  804. measurement.addValue("pressure", bme1_pressure());
  805. measurement.addValue("humidity", bme1_humid());
  806. Serial.println(F("Writing bme1"));
  807. writeMeasurement(measurement);
  808. Serial.println(F("Done!"));
  809. }
  810. if (found_bme2) {
  811. #if defined(ARDUINO_ARCH_AVR)
  812. measurement.clear();
  813. measurement.setName("environment");
  814. #else
  815. InfluxData measurement("environment");
  816. #endif
  817. measurement.addTag("location", SENSOR_LOCATION);
  818. measurement.addTag("placement", "2");
  819. measurement.addTag("sensor", "bme280");
  820. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  821. measurement.addTag("device", WiFi.macAddress());
  822. #endif
  823. measurement.addValue("temperature", bme2_temp());
  824. measurement.addValue("pressure", bme2_pressure());
  825. measurement.addValue("humidity", bme2_humid());
  826. Serial.println(F("Writing bme2"));
  827. writeMeasurement(measurement);
  828. Serial.println(F("Done!"));
  829. }
  830. if (found_sht) {
  831. #if defined(ARDUINO_ARCH_AVR)
  832. measurement.clear();
  833. measurement.setName("environment");
  834. #else
  835. InfluxData measurement("environment");
  836. #endif
  837. measurement.addTag("location", SENSOR_LOCATION);
  838. measurement.addTag("sensor", "sht21");
  839. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  840. measurement.addTag("device", WiFi.macAddress());
  841. #endif
  842. measurement.addValue("temperature", sht_temp());
  843. measurement.addValue("humidity", sht_humid());
  844. Serial.println(F("Writing sht"));
  845. writeMeasurement(measurement);
  846. Serial.println(F("Done!"));
  847. }
  848. #ifdef ENABLE_CCS811
  849. if (found_ccs1) {
  850. #if defined(ARDUINO_ARCH_AVR)
  851. measurement.clear();
  852. measurement.setName("environment");
  853. #else
  854. InfluxData measurement("environment");
  855. #endif
  856. measurement.addTag("location", SENSOR_LOCATION);
  857. measurement.addTag("placement", "1");
  858. measurement.addTag("sensor", "ccs811");
  859. String err(ccs1_error_code);
  860. measurement.addTag("error", err);
  861. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  862. measurement.addTag("device", WiFi.macAddress());
  863. #endif
  864. measurement.addValue("eco2", ccs1_eco2());
  865. measurement.addValue("tvoc", ccs1_tvoc());
  866. Serial.println(F("Writing ccs1"));
  867. writeMeasurement(measurement);
  868. Serial.println(F("Done!"));
  869. }
  870. if (found_ccs2) {
  871. #if defined(ARDUINO_ARCH_AVR)
  872. measurement.clear();
  873. measurement.setName("environment");
  874. #else
  875. InfluxData measurement("environment");
  876. #endif
  877. measurement.addTag("location", SENSOR_LOCATION);
  878. measurement.addTag("placement", "2");
  879. measurement.addTag("sensor", "ccs811");
  880. String err(ccs2_error_code);
  881. measurement.addTag("error", err);
  882. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  883. measurement.addTag("device", WiFi.macAddress());
  884. #endif
  885. measurement.addValue("eco2", ccs2_eco2());
  886. measurement.addValue("tvoc", ccs2_tvoc());
  887. Serial.println(F("Writing ccs2"));
  888. writeMeasurement(measurement);
  889. Serial.println(F("Done!"));
  890. }
  891. #endif // ENABLE_CCS811
  892. #ifdef FEATURE_MOISTURE
  893. for (int i = 0; i < moisture_count(); i++) {
  894. int moisture = moisture_read(i);
  895. if (moisture < moisture_max()) {
  896. #if defined(ARDUINO_ARCH_AVR)
  897. measurement.clear();
  898. measurement.setName("moisture");
  899. #else
  900. InfluxData measurement("moisture");
  901. #endif
  902. measurement.addTag("location", SENSOR_LOCATION);
  903. String sensor(i + 1, DEC);
  904. measurement.addTag("sensor", sensor);
  905. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  906. measurement.addTag("device", WiFi.macAddress());
  907. #endif
  908. measurement.addValue("value", moisture);
  909. measurement.addValue("maximum", moisture_max());
  910. Serial.print(F("Writing moisture "));
  911. Serial.println(i);
  912. writeMeasurement(measurement);
  913. Serial.println(F("Done!"));
  914. }
  915. }
  916. #endif // FEATURE_MOISTURE
  917. #ifdef FEATURE_RELAIS
  918. for (int i = 0; i < relais_count(); i++) {
  919. InfluxData measurement("relais");
  920. measurement.addTag("location", SENSOR_LOCATION);
  921. measurement.addTag("id", String(i));
  922. measurement.addTag("name", relais_name(i));
  923. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  924. measurement.addTag("device", WiFi.macAddress());
  925. #endif
  926. measurement.addValue("state", relais_get(i));
  927. writeMeasurement(measurement);
  928. }
  929. #endif // FEATURE_RELAIS
  930. Serial.println(F("All Done!"));
  931. }
  932. #endif // ENABLE_INFLUXDB_LOGGING
  933. #ifdef ENABLE_CCS811
  934. void ccs_update() {
  935. if (found_ccs1) {
  936. if (ccs1.available()) {
  937. ccs1_error_code = ccs1.readData();
  938. ccs1_data_valid = (ccs1_error_code == 0);
  939. if (found_bme1) {
  940. ccs1.setEnvironmentalData(bme1_humid(), bme1_temp());
  941. } else if (found_bme2) {
  942. ccs1.setEnvironmentalData(bme2_humid(), bme2_temp());
  943. } else if (found_sht) {
  944. ccs1.setEnvironmentalData(sht_humid(), sht_temp());
  945. }
  946. }
  947. }
  948. if (found_ccs2) {
  949. if (ccs2.available()) {
  950. ccs2_error_code = ccs2.readData();
  951. ccs2_data_valid = (ccs2_error_code == 0);
  952. if (found_bme1) {
  953. ccs2.setEnvironmentalData(bme1_humid(), bme1_temp());
  954. } else if (found_bme2) {
  955. ccs2.setEnvironmentalData(bme2_humid(), bme2_temp());
  956. } else if (found_sht) {
  957. ccs2.setEnvironmentalData(sht_humid(), sht_temp());
  958. }
  959. }
  960. }
  961. }
  962. #endif // ENABLE_CCS811
  963. void loop() {
  964. unsigned long time = millis();
  965. #ifdef ENABLE_CCS811
  966. if (found_ccs1 || found_ccs2) {
  967. ccs_update();
  968. }
  969. #endif // ENABLE_CCS811
  970. if ((time - last_server_handle_time) >= SERVER_HANDLE_INTERVAL) {
  971. last_server_handle_time = time;
  972. handleServers();
  973. }
  974. if ((time - last_db_write_time) >= DB_WRITE_INTERVAL) {
  975. last_db_write_time = time;
  976. #ifdef ENABLE_INFLUXDB_LOGGING
  977. writeDatabase();
  978. #endif // ENABLE_INFLUXDB_LOGGING
  979. #ifdef ENABLE_MQTT
  980. writeMQTT();
  981. #endif // ENABLE_MQTT
  982. }
  983. #ifdef ENABLE_INFLUXDB_LOGGING
  984. #ifdef INFLUX_MAX_ERRORS_RESET
  985. if (error_count >= INFLUX_MAX_ERRORS_RESET) {
  986. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  987. ESP.restart();
  988. #endif
  989. }
  990. #endif // INFLUX_MAX_ERRORS_RESET
  991. #endif // ENABLE_INFLUXDB_LOGGING
  992. #ifdef ENABLE_MQTT
  993. if (!mqtt.connected() && ((millis() - last_mqtt_reconnect_time) >= MQTT_RECONNECT_INTERVAL)) {
  994. last_mqtt_reconnect_time = millis();
  995. mqttReconnect();
  996. }
  997. mqtt.loop();
  998. #endif // ENABLE_MQTT
  999. // blink heartbeat LED
  1000. if ((time - last_led_blink_time) >= LED_BLINK_INTERVAL) {
  1001. last_led_blink_time = time;
  1002. digitalWrite(BUILTIN_LED_PIN, !digitalRead(BUILTIN_LED_PIN));
  1003. }
  1004. #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  1005. // reset ESP every 3d to be safe
  1006. if (time >= (3UL * 24UL * 60UL * 60UL * 1000UL)) {
  1007. ESP.restart();
  1008. }
  1009. #endif
  1010. }