/* * ESP32 Environmental Sensor Node with SI7021s (Temp + Hum) * * copyright: Jannik Beyerstedt | https://jannikbeyerstedt.de | code@jannikbeyerstedt.de * license: http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 License */ #include "Arduino.h" #include "si7021.hpp" #include #include #define BOARD_HUZZAH 1 /* Adafruit Feather HUZZAH */ #define BOARD_LOLIN 2 /* LoLin NodeMCU */ #define BOARD_ESP07S 3 /* standalone ESP07S */ #define BOARD_ESP32 4 /* standalone ESP32 */ /* DEBUG SETTINGS */ #define TEST /* use test config and database */ #define PRINT_INFO /* enable Serial output */ #define PRINT_DEBUG /* enable additional debug output */ /* CONFIGURATION OPTIONS */ #ifndef NODENAME #define NODENAME "env-esp32-01" /* IMPORTANT: CHANGE FOR EACH DEVICE */ #endif #ifndef BOARD #define BOARD 4 /* IMPORTANT: select one of the BOARD_* types */ #endif #define DB_HOSTNAME "ursaminor.fra80" #define DB_PASSWD "c2Vuc29yczpTZW5zb3JzLXcuaW5mbHV4QGhvbWU" // BasicAuth String #if BOARD == BOARD_ESP32 #define BATT_FULL 2812 // TODO: get actual value #define BATT_CUTOFF 2280 // TODO: get actual value #else #error "unsupported board chosen" #endif /* SETTINGS */ float tempOffset = +0.0; float humiOffset = +0.0; const char* host = DB_HOSTNAME; const int httpPort = 8086; const char* wlan_ssid = "WLAN-Bey-IoT"; const char* wlan_passwd = "IoT-Fra80"; #ifndef TEST String loggerName = NODENAME; const uint16_t sendInterval_sec = 1800; /* 1800 sec (30 min) */ const uint8_t sampleAvgNum = 6; /* 6 (every 5 minutes) */ const uint16_t sampleInterval_sec = sendInterval_sec / sampleAvgNum; String serviceUri = "/write?db=sensors"; #else String loggerName = "esp-test"; const uint16_t sendInterval_sec = 180; /* 180 sec (3 min) */ const uint8_t sampleAvgNum = 6; /* 3 (every 1 minute) */ const uint16_t sampleInterval_sec = sendInterval_sec / sampleAvgNum; String serviceUri = "/write?db=test"; #endif /* GLOBAL VARIABLES */ const unsigned int wifiConnectTimeout = 30; /* num of 500ms intervals */ Si7021 si7021 = Si7021(); float temp = 0.0; float humi = 0.0; uint16_t battLevel = 0; float battPerc = 0.0; RTC_DATA_ATTR uint8_t sampleCnt = 0; RTC_DATA_ATTR float tempAccu = 0.0; // for average value RTC_DATA_ATTR float humiAccu = 0.0; // for average value #ifdef PRINT_INFO #define INFO_PRINT(x) Serial.print(x) #define INFO_PRINTLN(x) Serial.println(x) #else #define INFO_PRINT(x) #define INFO_PRINTLN(x) #endif #if defined(PRINT_INFO) && defined(PRINT_DEBUG) #define DEBUG_PRINT(x) Serial.print(x) #define DEBUG_PRINTLN(x) Serial.println(x) #else #define DEBUG_PRINT(x) #define DEBUG_PRINTLN(x) #endif /* GLOBAL FUNCTIONS */ bool wlanConnect(void) { unsigned int cycles = 0; INFO_PRINT("[INFO ] Connecting to WiFi"); WiFi.mode(WIFI_STA); WiFi.begin(wlan_ssid, wlan_passwd); while (WiFi.status() != WL_CONNECTED) { delay(500); INFO_PRINT("."); cycles++; if (cycles > wifiConnectTimeout) { INFO_PRINTLN(" FAILED"); return false; } } INFO_PRINT(" ok, IP: "); INFO_PRINTLN(WiFi.localIP()); return true; } void doDeepSleep(uint16_t durationSec) { esp_sleep_enable_timer_wakeup(durationSec * 1000000); // esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF); // TODO more fine grained settings #ifdef PRINT_DEBUG Serial.printf("[DEBUG] Going to deep sleep for %d seconds\n", durationSec); #endif #ifdef PRINT_INFO delay(100); // time to send the messages via Serial #endif esp_deep_sleep_start(); } void printWakeupReason() { esp_sleep_wakeup_cause_t wakeup_reason; wakeup_reason = esp_sleep_get_wakeup_cause(); switch (wakeup_reason) { case 1: DEBUG_PRINTLN("[DEBUG] Wakeup caused by external signal using RTC_IO"); break; case 2: DEBUG_PRINTLN("[DEBUG] Wakeup caused by external signal using RTC_CNTL"); break; case 3: DEBUG_PRINTLN("[DEBUG] Wakeup caused by timer"); break; case 4: DEBUG_PRINTLN("[DEBUG] Wakeup caused by touchpad"); break; case 5: DEBUG_PRINTLN("[DEBUG] Wakeup caused by ULP program"); break; default: DEBUG_PRINTLN("[DEBUG] Wakeup was not caused by deep sleep"); break; } } extern "C" void app_main() { initArduino(); pinMode(4, OUTPUT); digitalWrite(4, HIGH); //do your own thing #ifdef PRINT_INFO Serial.begin(115200); delay(100); Serial.println(""); Serial.print("[INFO ] Node "); Serial.print(loggerName); Serial.print(", BoardType: "); Serial.println(BOARD); #endif #ifdef PRINT_DEBUG printWakeupReason(); #endif // switch WiFi off for the moment DEBUG_PRINTLN("[DEBUG] Turning wifi off for now"); WiFi.mode(WIFI_OFF); // setup the si7021 sensor if (false == si7021.begin()) { INFO_PRINTLN("[ERROR] SI7021 not set up properly"); } // setup the adc for VCC measurement // TODO! adc1_config_width(ADC_WIDTH_12Bit); adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_11db); // 11dB ~ max 3.14V = 4069 /* GET VALUE SAMPLES */ // measure environmental data from si7021 if (false == si7021.measure()) { INFO_PRINT("[ERROR] SI7021 measurement failed"); } humi = si7021.humidity + humiOffset; temp = si7021.temperature + tempOffset; // measure battery level battLevel = adc1_get_raw(ADC1_CHANNEL_0); // ADC0 = GPIO36 = VP = pin 4 (after en) battPerc = (0 == battLevel) ? 0 : 100.0 * (battLevel - BATT_CUTOFF) / (BATT_FULL - BATT_CUTOFF); #ifdef PRINT_INFO Serial.printf("[INFO ] > Humidity: %5.2f Temperature: %5.2f", humi, temp); Serial.printf(" Battery Bat: %5.1f /100 (raw: %4d)\n", battPerc, battLevel); #endif /* ---------- MAIN LOGIC START ---------- */ // accumulate and average $sampleAvgNum samples before sending data if (sampleCnt < sampleAvgNum) { sampleCnt++; tempAccu += temp; humiAccu += humi; } // calculate average and send data, if $sampleAvgNum are reached if (sampleCnt >= sampleAvgNum) { INFO_PRINTLN("[INFO ] Enough samples collected -> send average to database"); temp = tempAccu / (float)sampleCnt; humi = humiAccu / (float)sampleCnt; sampleCnt = 0; tempAccu = 0; humiAccu = 0; #ifdef PRINT_INFO Serial.printf("[INFO ] < Humidity: %5.2f Temperature: %5.2f\n", humi, temp); #endif // turn wifi on again DEBUG_PRINTLN("[DEBUG] Turning wifi on again and connecting"); if (wlanConnect()) { // send data to influxdb String influxDataLine = "weather,sensor=" + loggerName + " temp=" + String(temp, 2); influxDataLine += ",hum=" + String(humi, 2); influxDataLine += ",batRaw=" + String(battLevel); influxDataLine += ",bat=" + String(battPerc); INFO_PRINT("[INFO ] Connecting to "); INFO_PRINT(host); WiFiClient client; if (client.connect(host, httpPort)) { INFO_PRINTLN(" > success"); String httpRequest = "POST " + serviceUri + " HTTP/1.1\r\n" + "Host: " + host + ":" + httpPort + "\r\n" + "Content-Length: " + influxDataLine.length() + "\r\n" + "Authorization: Basic " + DB_PASSWD + "=\r\n" + "Connection: close\r\n" + "\r\n" + influxDataLine + "\r\n"; client.print(httpRequest); delay(100); while (client.available()) { String line = client.readStringUntil('\n'); if (line.indexOf("HTTP") != -1) { if (line.indexOf("204") == -1) { // expect a HTTP 204 status code INFO_PRINTLN("[ERROR]: invalid http status code"); INFO_PRINTLN(line); } break; } else { ; } } } else { INFO_PRINTLN(" > FAILED"); } client.stop(); WiFi.disconnect(true); // continue with deep sleep at end of main function } else { // ELSE: !wifiConnect /* WIFI CONNECT FAILED, WAIT MAX 5 MINUTES IN DEEP SLEEP */ INFO_PRINTLN("[INFO ] WiFi connect failed -> wait a bit and try again"); if (sendInterval_sec > (5 * 60)) { doDeepSleep(5 * 60); } else { doDeepSleep(sendInterval_sec); } } // ENDIF: wifiConnect } else { // ELSE: sampleCnt < sampleAvgNum #ifdef PRINT_INFO Serial.printf("[INFO ] %d of %d samples collected -> sleep\n", sampleCnt, sampleAvgNum); // continue with deep sleep at end of main function #endif } // ENDIF: sampleCnt /* ---------- MAIN LOGIC END ---------- */ /* SAVE ENERGY BY WAITING IN DEEP SLEEP */ doDeepSleep(sampleInterval_sec); }