ESP32 / ESP8266 & BME280 / SHT2x sensor with InfluxDB support
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

main.cpp 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  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 <Wire.h>
  15. #include <Adafruit_BME280.h>
  16. #include <SHT2x.h>
  17. #if defined(ARDUINO_ARCH_ESP8266)
  18. #include <ESP8266WiFi.h>
  19. #include <ESP8266WebServer.h>
  20. #include <ESP8266mDNS.h>
  21. #define ESP_PLATFORM_NAME "ESP8266"
  22. #elif defined(ARDUINO_ARCH_ESP32)
  23. #include <WiFi.h>
  24. #include <WebServer.h>
  25. #include <ESPmDNS.h>
  26. #define ESP_PLATFORM_NAME "ESP32"
  27. #endif
  28. #include "config.h"
  29. #include "moisture.h"
  30. #include "relais.h"
  31. #include "SimpleUpdater.h"
  32. #define BUILTIN_LED_PIN 1
  33. UPDATE_WEB_SERVER server(80);
  34. SimpleUpdater updater;
  35. #ifdef ENABLE_INFLUXDB_LOGGING
  36. #include <InfluxDb.h>
  37. Influxdb influx(INFLUXDB_HOST, INFLUXDB_PORT);
  38. #define INFLUX_MAX_ERRORS_RESET 10
  39. int error_count = 0;
  40. #endif // ENABLE_INFLUXDB_LOGGING
  41. #define SHT_I2C_ADDRESS HTDU21D_ADDRESS
  42. #define BME_I2C_ADDRESS_1 0x76
  43. #define BME_I2C_ADDRESS_2 0x77
  44. #if defined(ARDUINO_ARCH_ESP8266)
  45. #define I2C_SDA_PIN 2
  46. #define I2C_SCL_PIN 0
  47. TwoWire Wire2;
  48. SHT2x sht(SHT_I2C_ADDRESS, &Wire2);
  49. #elif defined(ARDUINO_ARCH_ESP32)
  50. SHT2x sht(SHT_I2C_ADDRESS, &Wire);
  51. #endif
  52. Adafruit_BME280 bme1, bme2;
  53. bool found_bme1 = false;
  54. bool found_bme2 = false;
  55. bool found_sht = false;
  56. unsigned long last_server_handle_time = 0;
  57. unsigned long last_db_write_time = 0;
  58. unsigned long last_led_blink_time = 0;
  59. static void relaisTest() {
  60. for (int i = 0; i < 10; i++) {
  61. relais_enable(i, 400 + (i * 1000));
  62. delay(100);
  63. }
  64. }
  65. void handleRelaisTest() {
  66. String message = F("<html><head>\n");
  67. message += F("<title>" ESP_PLATFORM_NAME " Environment Sensor</title>\n");
  68. message += F("</head><body>\n");
  69. message += F("<p>Relais Test started!</p>\n");
  70. message += F("<p><a href=\"/\">Return to Home</a></p>\n");
  71. message += F("</body></html>\n");
  72. server.send(200, "text/html", message);
  73. relaisTest();
  74. }
  75. void handleRoot() {
  76. String message = F("<html><head>\n");
  77. message += F("<title>" ESP_PLATFORM_NAME " Environment Sensor</title>\n");
  78. message += F("</head><body>\n");
  79. message += F("<h1>" ESP_PLATFORM_NAME " Environment Sensor</h1>\n");
  80. message += F("\n<p>\n");
  81. message += F("Version: ");
  82. message += esp_env_version;
  83. message += F("\n<br>\n");
  84. message += F("Location: ");
  85. message += sensor_location;
  86. message += F("\n<br>\n");
  87. message += F("MAC: ");
  88. message += WiFi.macAddress();
  89. message += F("\n</p>\n");
  90. #if defined(ARDUINO_ARCH_ESP8266)
  91. message += F("\n<p>\n");
  92. message += F("Reset reason: ");
  93. message += ESP.getResetReason();
  94. message += F("\n<br>\n");
  95. message += F("Free heap: ");
  96. message += String(ESP.getFreeHeap());
  97. message += F(" (");
  98. message += String(ESP.getHeapFragmentation());
  99. message += F("% fragmentation)");
  100. message += F("\n<br>\n");
  101. message += F("Free sketch space: ");
  102. message += String(ESP.getFreeSketchSpace());
  103. message += F("\n<br>\n");
  104. message += F("Flash chip real size: ");
  105. message += String(ESP.getFlashChipRealSize());
  106. if (ESP.getFlashChipSize() != ESP.getFlashChipRealSize()) {
  107. message += F("\n<br>\n");
  108. message += F("WARNING: sdk chip size (");
  109. message += (ESP.getFlashChipSize());
  110. message += F(") does not match!");
  111. }
  112. message += F("\n</p>\n");
  113. #elif defined(ARDUINO_ARCH_ESP32)
  114. message += F("\n<p>\n");
  115. message += F("Free heap: ");
  116. message += String(ESP.getFreeHeap() / 1024.0);
  117. message += F("k\n<br>\n");
  118. message += F("Free sketch space: ");
  119. message += String(ESP.getFreeSketchSpace() / 1024.0);
  120. message += F("k\n<br>\n");
  121. message += F("Flash chip size: ");
  122. message += String(ESP.getFlashChipSize() / 1024.0);
  123. message += F("k\n</p>\n");
  124. #endif
  125. message += F("\n<p>\n");
  126. if (found_bme1) {
  127. message += F("BME280 Low:");
  128. message += F("\n<br>\n");
  129. message += F("Temperature: ");
  130. message += String(bme1.readTemperature());
  131. message += F("\n<br>\n");
  132. message += F("Humidity: ");
  133. message += String(bme1.readHumidity());
  134. message += F("\n<br>\n");
  135. message += F("Pressure: ");
  136. message += String(bme1.readPressure());
  137. } else {
  138. message += F("BME280 (low) not connected!");
  139. }
  140. message += F("\n</p>\n");
  141. message += F("\n<p>\n");
  142. if (found_bme2) {
  143. message += F("BME280 High:");
  144. message += F("\n<br>\n");
  145. message += F("Temperature: ");
  146. message += String(bme2.readTemperature());
  147. message += F("\n<br>\n");
  148. message += F("Humidity: ");
  149. message += String(bme2.readHumidity());
  150. message += F("\n<br>\n");
  151. message += F("Pressure: ");
  152. message += String(bme2.readPressure());
  153. } else {
  154. message += F("BME280 (high) not connected!");
  155. }
  156. message += F("\n</p>\n");
  157. message += F("\n<p>\n");
  158. if (found_sht) {
  159. message += F("SHT21:");
  160. message += F("\n<br>\n");
  161. message += F("Temperature: ");
  162. message += String(sht.GetTemperature());
  163. message += F("\n<br>\n");
  164. message += F("Humidity: ");
  165. message += String(sht.GetHumidity());
  166. } else {
  167. message += F("SHT21 not connected!");
  168. }
  169. message += F("\n</p>\n");
  170. for (int i = 0; i < moisture_count(); i++) {
  171. int moisture = moisture_read(i);
  172. if (moisture < moisture_max()) {
  173. message += F("\n<p>\n");
  174. message += F("Sensor ");
  175. message += String(i + 1);
  176. message += F(":\n<br>\n");
  177. message += F("Moisture: ");
  178. message += String(moisture);
  179. message += F(" / ");
  180. message += String(moisture_max());
  181. message += F("\n</p>\n");
  182. }
  183. }
  184. if (moisture_count() <= 0) {
  185. message += F("\n<p>\n");
  186. message += F("No moisture sensors configured!");
  187. message += F("\n</p>\n");
  188. }
  189. message += F("<p>\n");
  190. message += F("Try <a href=\"/update\">/update</a> for OTA firmware updates!\n");
  191. message += F("</p>\n");
  192. message += F("<p>\n");
  193. #ifdef ENABLE_INFLUXDB_LOGGING
  194. message += F("InfluxDB: ");
  195. message += INFLUXDB_DATABASE;
  196. message += F(" @ ");
  197. message += INFLUXDB_HOST;
  198. message += F(":");
  199. message += String(INFLUXDB_PORT);
  200. message += F("\n");
  201. #else
  202. message += F("InfluxDB logging not enabled!\n");
  203. #endif
  204. message += F("</p>\n");
  205. message += F("<p><a href=\"/relaistest\">Relais Test</a></p>\n");
  206. message += F("</body></html>\n");
  207. server.send(200, "text/html", message);
  208. }
  209. void setup() {
  210. pinMode(BUILTIN_LED_PIN, OUTPUT);
  211. relais_init();
  212. // Blink LED for init
  213. for (int i = 0; i < 2; i++) {
  214. digitalWrite(BUILTIN_LED_PIN, LOW); // LED on
  215. delay(LED_INIT_BLINK_INTERVAL);
  216. digitalWrite(BUILTIN_LED_PIN, HIGH); // LED off
  217. delay(LED_INIT_BLINK_INTERVAL);
  218. }
  219. moisture_init();
  220. // Init I2C and try to connect to sensors
  221. #if defined(ARDUINO_ARCH_ESP8266)
  222. Wire2.begin(I2C_SDA_PIN, I2C_SCL_PIN);
  223. found_bme1 = (!bme1.begin(BME_I2C_ADDRESS_1, &Wire2)) ? false : true;
  224. found_bme2 = (!bme2.begin(BME_I2C_ADDRESS_2, &Wire2)) ? false : true;
  225. #elif defined(ARDUINO_ARCH_ESP32)
  226. Wire.begin();
  227. found_bme1 = (!bme1.begin(BME_I2C_ADDRESS_1, &Wire)) ? false : true;
  228. found_bme2 = (!bme2.begin(BME_I2C_ADDRESS_2, &Wire)) ? false : true;
  229. #endif
  230. found_sht = sht.GetAlive();
  231. // Build hostname string
  232. String hostname = SENSOR_HOSTNAME_PREFIX;
  233. hostname += sensor_location;
  234. #if defined(ARDUINO_ARCH_ESP8266)
  235. // Connect to WiFi AP
  236. WiFi.hostname(hostname);
  237. WiFi.mode(WIFI_STA);
  238. WiFi.begin(ssid, password);
  239. while (WiFi.status() != WL_CONNECTED) {
  240. delay(LED_CONNECT_BLINK_INTERVAL);
  241. digitalWrite(BUILTIN_LED_PIN, !digitalRead(BUILTIN_LED_PIN));
  242. }
  243. #elif defined(ARDUINO_ARCH_ESP32)
  244. // Set hostname workaround
  245. WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
  246. WiFi.setHostname(hostname.c_str());
  247. // Workaround for WiFi connecting only every 2nd reset
  248. // https://github.com/espressif/arduino-esp32/issues/2501#issuecomment-513602522
  249. WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info) {
  250. if (info.disconnected.reason == 202) {
  251. esp_sleep_enable_timer_wakeup(10);
  252. esp_deep_sleep_start();
  253. delay(100);
  254. }
  255. }, WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
  256. // Connect to WiFi AP
  257. WiFi.mode(WIFI_STA);
  258. WiFi.begin(ssid, password);
  259. while (WiFi.status() != WL_CONNECTED) {
  260. delay(LED_CONNECT_BLINK_INTERVAL);
  261. digitalWrite(BUILTIN_LED_PIN, !digitalRead(BUILTIN_LED_PIN));
  262. }
  263. // Set hostname workaround
  264. WiFi.setHostname(hostname.c_str());
  265. #endif
  266. #ifdef ENABLE_INFLUXDB_LOGGING
  267. // Setup InfluxDB Client
  268. influx.setDb(INFLUXDB_DATABASE);
  269. #endif // ENABLE_INFLUXDB_LOGGING
  270. // Setup HTTP Server
  271. MDNS.begin(hostname.c_str());
  272. updater.setup(&server);
  273. server.on("/", handleRoot);
  274. server.on("/relaistest", handleRelaisTest);
  275. server.begin();
  276. MDNS.addService("http", "tcp", 80);
  277. }
  278. void handleServers() {
  279. server.handleClient();
  280. #if defined(ARDUINO_ARCH_ESP8266)
  281. MDNS.update();
  282. #endif
  283. }
  284. static boolean writeMeasurement(InfluxData &measurement) {
  285. boolean success = influx.write(measurement);
  286. if (!success) {
  287. error_count++;
  288. for (int i = 0; i < 10; i++) {
  289. digitalWrite(BUILTIN_LED_PIN, LOW); // LED on
  290. delay(LED_ERROR_BLINK_INTERVAL);
  291. digitalWrite(BUILTIN_LED_PIN, HIGH); // LED off
  292. delay(LED_ERROR_BLINK_INTERVAL);
  293. }
  294. }
  295. return success;
  296. }
  297. #ifdef ENABLE_INFLUXDB_LOGGING
  298. void writeDatabase() {
  299. if (found_bme1) {
  300. InfluxData measurement("environment");
  301. measurement.addTag("location", sensor_location);
  302. measurement.addTag("placement", "1");
  303. measurement.addTag("device", WiFi.macAddress());
  304. measurement.addTag("sensor", "bme280");
  305. measurement.addValue("temperature", bme1.readTemperature());
  306. measurement.addValue("pressure", bme1.readPressure());
  307. measurement.addValue("humidity", bme1.readHumidity());
  308. writeMeasurement(measurement);
  309. }
  310. if (found_bme2) {
  311. InfluxData measurement("environment");
  312. measurement.addTag("location", sensor_location);
  313. measurement.addTag("placement", "2");
  314. measurement.addTag("device", WiFi.macAddress());
  315. measurement.addTag("sensor", "bme280");
  316. measurement.addValue("temperature", bme2.readTemperature());
  317. measurement.addValue("pressure", bme2.readPressure());
  318. measurement.addValue("humidity", bme2.readHumidity());
  319. writeMeasurement(measurement);
  320. }
  321. if (found_sht) {
  322. InfluxData measurement("environment");
  323. measurement.addTag("location", sensor_location);
  324. measurement.addTag("device", WiFi.macAddress());
  325. measurement.addTag("sensor", "sht21");
  326. measurement.addValue("temperature", sht.GetTemperature());
  327. measurement.addValue("humidity", sht.GetHumidity());
  328. writeMeasurement(measurement);
  329. }
  330. for (int i = 0; i < moisture_count(); i++) {
  331. int moisture = moisture_read(i);
  332. if (moisture < moisture_max()) {
  333. InfluxData measurement("moisture");
  334. measurement.addTag("location", sensor_location);
  335. measurement.addTag("device", WiFi.macAddress());
  336. measurement.addTag("sensor", String(i + 1));
  337. measurement.addValue("value", moisture);
  338. measurement.addValue("maximum", moisture_max());
  339. writeMeasurement(measurement);
  340. }
  341. }
  342. }
  343. #endif // ENABLE_INFLUXDB_LOGGING
  344. void loop() {
  345. unsigned long time = millis();
  346. relais_run();
  347. if ((time - last_server_handle_time) >= SERVER_HANDLE_INTERVAL) {
  348. last_server_handle_time = time;
  349. handleServers();
  350. }
  351. #ifdef ENABLE_INFLUXDB_LOGGING
  352. if ((time - last_db_write_time) >= DB_WRITE_INTERVAL) {
  353. last_db_write_time = time;
  354. writeDatabase();
  355. }
  356. #ifdef INFLUX_MAX_ERRORS_RESET
  357. if (error_count >= INFLUX_MAX_ERRORS_RESET) {
  358. ESP.restart();
  359. }
  360. #endif // INFLUX_MAX_ERRORS_RESET
  361. #endif // ENABLE_INFLUXDB_LOGGING
  362. if ((time - last_led_blink_time) >= LED_BLINK_INTERVAL) {
  363. last_led_blink_time = time;
  364. digitalWrite(BUILTIN_LED_PIN, !digitalRead(BUILTIN_LED_PIN));
  365. }
  366. }