diff --git a/src/main.cpp b/src/main.cpp index 2b2c113..e1d3c7a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,6 +42,10 @@ uint8_t temprature_sens_read(); static const char* TAG = "MAIN"; + +#define TIME_TO_SLEEP 60 // seconds +constexpr unsigned int dhcp_interval = 60*60; + WiFiMulti wifiMulti; GxEPD2_BW display(GxEPD2_213_B72(/*CS=SS*/ TFT_CS, /*DC=*/ TFT_DC, /*RST=*/ TFT_RST, /*BUSY=*/ -1)); // GDEH0213B72 static constexpr uint8_t y_offset = 6; @@ -56,6 +60,18 @@ BH1750 lightMeter; XD0OTA ota("esp32-weatherstation"); XD0MQTT mqtt; +struct __attribute__((packed)) network_t { + uint32_t ip; + uint32_t dns; + uint32_t gateway; + uint32_t subnet; + char ssid[64]; + char password[64]; + int32_t channel; + time_t last_dhcp; +}; +RTC_DATA_ATTR network_t network; + struct __attribute__((packed)) sensor_readings_t { float temperature = NAN; // °C float humidity = NAN; // %H @@ -79,15 +95,111 @@ sensor_readings_t sensors_a4cf1211c3e4, sensors_246f28d1fa5c, sensors_246f28d1a0 SensorHistory history_pressure(30); -uint32_t lastDisplayUpdate = 0; -uint32_t lastDisplayRefresh = 0; -bool bme280_active = false; -bool bme680_active = false; -bool uv_active = false; -bool light_active = false; -bool sds_active = false; +RTC_DATA_ATTR time_t lastDisplayRefresh = 0; +struct __attribute__((packed)) sensors_active_t { + bool bme280 = false; + bool bme680 = false; + bool uv = false; + bool light = false; + bool sds = false; +}; +RTC_DATA_ATTR sensors_active_t sensors_active; float station_height = 0; +RTC_DATA_ATTR int bootCount = 0; + + +time_t getTimestamp() { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec; +} + + +void poweroffDevices() { + display.powerOff(); + + // ToDo: Sensors +} + + +void gotoSleep(unsigned int sleep_time = TIME_TO_SLEEP) { + mqtt.end(); + WiFi.disconnect(); + WiFi.mode(WIFI_OFF); + + poweroffDevices(); + + //rtc_gpio_isolate(GPIO_NUM_12); + + esp_sleep_enable_timer_wakeup(sleep_time * 1000000LL); + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF); + Serial.println("going to to sleep for " + String(sleep_time) + + " Seconds"); + Serial.flush(); + esp_deep_sleep_start(); + delay(1); +} + +void wifiConnect() { + WiFi.persistent(false); + WiFi.setHostname("esp32-weatherstation"); + + wifiMulti.addAP(WIFI_SSID, WIFI_PASSWD); + wifiMulti.addAP(WIFI_SSID2, WIFI_PASSWD2); + wifiMulti.addAP(WIFI_SSID3, WIFI_PASSWD3); + + IPAddress ip = IPAddress(network.ip); + IPAddress dns = IPAddress(network.dns); + IPAddress subnet = IPAddress(network.subnet); + IPAddress gateway = IPAddress(network.gateway); + + Serial.println("millis(): " + String(millis())); + + Serial.println("previous dhcp: " + String(getTimestamp() - network.last_dhcp) + "s ago"); + + if ( ip != INADDR_NONE && dns != INADDR_NONE && gateway != INADDR_NONE && subnet != INADDR_NONE + && ((ip[0] == 192 && ip[1] == 168) || (ip[0] == 172 && ip[1] == 16)) + && strlen(network.ssid) > 0 && strlen(network.password) > 0 + && (getTimestamp() - network.last_dhcp < dhcp_interval) + ) { + + ESP_LOGD("WiFi", "STATIC IP"); + WiFi.config(ip, gateway, subnet, dns); + WiFi.begin(network.ssid, network.password, network.channel); + for (int tries=0; WiFi.status() != WL_CONNECTED && tries < 10; tries++) { + ESP_LOGD("WiFi", "."); + delay(500); + } + } else { + ESP_LOGD("WiFi", "DHCP"); + for (int tries=0; wifiMulti.run() != WL_CONNECTED && tries < 20; tries++) { + Serial.print("."); + delay(500); + } + + network.ip = (uint32_t)WiFi.localIP(); + network.dns = (uint32_t)WiFi.dnsIP(); + network.gateway = (uint32_t)WiFi.gatewayIP(); + network.subnet = (uint32_t)WiFi.subnetMask(); + strncpy(network.ssid, WiFi.SSID().c_str(), 64); + strncpy(network.password, WiFi.psk().c_str(), 64); + network.channel = WiFi.channel(); + network.last_dhcp = getTimestamp(); + } + + Serial.println("millis(): " + String(millis())); + + if(WiFi.status() == WL_CONNECTED) { + ESP_LOGD("WiFi", "connected"); + //ESP_LOGD("WiFi", (WiFi.localIP().toString().c_str())); + + } else { + ESP_LOGE("WiFi", "could not connect to WiFi"); + ESP_LOGE(TAG, "restarting"); + ESP.restart(); + } +} void helloWorld() { @@ -101,7 +213,11 @@ void helloWorld() // center bounding box by transposition of origin: uint16_t x = ((display.width() - tbw) / 2) - tbx; uint16_t y = ((display.height() - tbh) / 2) - tby; - display.setFullWindow(); + if (display.epd2.hasFastPartialUpdate) { + display.setPartialWindow(0, 0, display.width(), display.height()); + } else { + display.setFullWindow(); + } display.firstPage(); do { @@ -139,13 +255,14 @@ void getTime(char* ptr, size_t maxsize, const char* format) { void getSensorMeasurements() { - if (bme280_active) { + if (sensors_active.bme280) { bme280.takeForcedMeasurement(); sensor_readings.temperature = bme280.readTemperature(); sensor_readings.humidity = bme280.readHumidity(); sensor_readings.pressure_raw = bme280.readPressure(); } - if (bme680_active) { + if (sensors_active.bme680) { + bme680.endReading(); // ToDo if (bme680.performReading()) { sensor_readings.temperature = bme680.temperature; sensor_readings.humidity = bme680.humidity; @@ -174,13 +291,13 @@ void getSensorMeasurements() { history_pressure.addValue(sensor_readings.pressure); - if (uv_active) { + if (sensors_active.uv) { sensor_readings.uvi = uv.readUVI(); sensor_readings.uva = uv.readUVA(); sensor_readings.uvb = uv.readUVB(); } - if (light_active) { + if (sensors_active.light) { sensor_readings.lux = lightMeter.readLightLevel(); // auto-adjust sensitivity if (sensor_readings.lux < 0) { @@ -200,7 +317,7 @@ void getSensorMeasurements() { } } - if (sds_active) { + if (sensors_active.sds) { PmResult pm = sds.readPm(); if (pm.isOk()) { sensor_readings.pm10 = pm.pm10; @@ -354,7 +471,7 @@ void displayValues() { display.printf("%.1f k\xe9", sensor_readings.voc / 1000.0F); // PM float pm10, pm25; - if (sds_active) { + if (sensors_active.sds) { pm10 = sensor_readings.pm10; pm25 = sensor_readings.pm25; } else if (!isnan(sensors_a4cf1211c3e4.pm10) || !isnan(sensors_a4cf1211c3e4.pm25)) { @@ -378,7 +495,7 @@ void displayValues() { display.printf("%.1f", pm25); // Lux float lux; - if (light_active) { + if (sensors_active.light) { lux = sensor_readings.lux; } else if (!isnan(sensors_a4cf1211c3e4.lux)) { lux = sensors_a4cf1211c3e4.lux; @@ -395,7 +512,7 @@ void displayValues() { display.printf("%.1f lx", lux); // UV float uvi, uva, uvb; - if (uv_active) { + if (sensors_active.uv) { uvi = sensor_readings.uvi; uva = sensor_readings.uva; uvb = sensor_readings.uvb; @@ -449,7 +566,7 @@ void displayValues() { void printValues() { - if (bme280_active || bme680_active) { + if (sensors_active.bme280 || sensors_active.bme680) { #define SEALEVELPRESSURE_HPA (1013.25) Serial.print("Temperature = "); Serial.print(sensor_readings.temperature); @@ -468,7 +585,7 @@ void printValues() { Serial.println(" %"); } - if (bme680_active) { + if (sensors_active.bme680) { Serial.print("VOC = "); Serial.print(sensor_readings.voc / 1000.0F); Serial.println(" kOhm"); @@ -476,14 +593,14 @@ void printValues() { Serial.println(); - if (uv_active) { + if (sensors_active.uv) { Serial.print("UV Index reading: "); Serial.println(sensor_readings.uvi); Serial.print("Raw UVA reading: "); Serial.println(sensor_readings.uva); Serial.print("Raw UVB reading: "); Serial.println(sensor_readings.uvb); Serial.println(); } - if (sds_active) { + if (sensors_active.sds) { Serial.print("PM2.5 = "); Serial.print(sensor_readings.pm25); Serial.print(", PM10 = "); @@ -499,63 +616,56 @@ void printValues() { void sendValues() { /* send values MQTT */ - if (bme280_active || bme680_active) { + if (sensors_active.bme280 || sensors_active.bme680) { String topic_temperature = String("thomas/sensor/") + ota.getMAC() + String("/temperature"); String topic_humidity = String("thomas/sensor/") + ota.getMAC() + String("/humidity"); String topic_pressure = String("thomas/sensor/") + ota.getMAC() + String("/pressure"); - mqtt.publishf(topic_temperature.c_str(), "%.2f", sensor_readings.temperature); + mqtt.publishf2(topic_temperature.c_str(), 1, 1, "%.2f", sensor_readings.temperature); delay(10); - mqtt.publishf(topic_humidity.c_str(), "%.2f", sensor_readings.humidity); + mqtt.publishf2(topic_humidity.c_str(), 1, 1, "%.2f", sensor_readings.humidity); delay(10); - mqtt.publishf(topic_pressure.c_str(), "%.2f", sensor_readings.pressure); + mqtt.publishf2(topic_pressure.c_str(), 1, 1, "%.2f", sensor_readings.pressure); delay(10); } - if (bme680_active) { + if (sensors_active.bme680) { String topic_voc = String("thomas/sensor/") + ota.getMAC() + String("/voc"); - mqtt.publishf(topic_voc.c_str(), "%.2f", sensor_readings.voc / 1000.0F); - delay(10); - } - if (!bme280_active && !bme680_active) { - String topic_temperature = String("thomas/sensor/") + ota.getMAC() + String("/temperature"); - float esp32_temperature = (temprature_sens_read() - 32) / 1.8; - char temperature[8]; sprintf(temperature, "%.2f", esp32_temperature-29.40); - mqtt.publish(topic_temperature.c_str(), temperature, strlen(temperature)); + mqtt.publishf2(topic_voc.c_str(), 1, 1, "%.2f", sensor_readings.voc / 1000.0F); delay(10); } - if (uv_active) { + if (sensors_active.uv) { String topic_uvi = String("thomas/sensor/") + ota.getMAC() + String("/uvi"); String topic_uva = String("thomas/sensor/") + ota.getMAC() + String("/uva"); String topic_uvb = String("thomas/sensor/") + ota.getMAC() + String("/uvb"); - mqtt.publishf(topic_uvi.c_str(), "%.2f", sensor_readings.uvi); - mqtt.publishf(topic_uva.c_str(), "%.2f", sensor_readings.uva); - mqtt.publishf(topic_uvb.c_str(), "%.2f", sensor_readings.uvb); + mqtt.publishf2(topic_uvi.c_str(), 1, 1, "%.2f", sensor_readings.uvi); + mqtt.publishf2(topic_uva.c_str(), 1, 1, "%.2f", sensor_readings.uva); + mqtt.publishf2(topic_uvb.c_str(), 1, 1, "%.2f", sensor_readings.uvb); delay(10); } - if (light_active) { + if (sensors_active.light) { String topic_lux = String("thomas/sensor/") + ota.getMAC() + String("/lux"); - mqtt.publishf(topic_lux.c_str(), "%.2f", sensor_readings.lux); + mqtt.publishf2(topic_lux.c_str(), 1, 1, "%.2f", sensor_readings.lux); delay(10); } - if (sds_active) { + if (sensors_active.sds) { String topic_pm10 = String("thomas/sensor/") + ota.getMAC() + String("/pm10"); String topic_pm25 = String("thomas/sensor/") + ota.getMAC() + String("/pm25"); - mqtt.publishf(topic_pm10.c_str(), "%.2f", sensor_readings.pm10); - mqtt.publishf(topic_pm25.c_str(), "%.2f", sensor_readings.pm25); + mqtt.publishf2(topic_pm10.c_str(), 1, 1, "%.2f", sensor_readings.pm10); + mqtt.publishf2(topic_pm25.c_str(), 1, 1, "%.2f", sensor_readings.pm25); delay(10); } { String topic_battery = String("thomas/sensor/") + ota.getMAC() + String("/battery"); - mqtt.publishf(topic_battery.c_str(), "%.2f", (sensor_readings.battery/4096.0)*2*3.42); + mqtt.publishf2(topic_battery.c_str(), 1, 1, "%.2f", (sensor_readings.battery/4096.0)*2*3.42); delay(10); } { String topic_rssi = String("thomas/sensor/") + ota.getMAC() + String("/rssi"); - mqtt.publishf(topic_rssi.c_str(), "%d", sensor_readings.rssi); + mqtt.publishf2(topic_rssi.c_str(), 1, 1, "%d", sensor_readings.rssi); delay(10); } @@ -569,7 +679,11 @@ void sendValues() { void setup() { Serial.begin(115200); - delay(10); + + esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause(); + ++bootCount; + Serial.println("Boot number: " + String(bootCount)); + Serial.println("millis(): " + String(millis())); ESP_LOGD(TAG, "setup hardware and sensors"); @@ -583,20 +697,21 @@ void setup() adcAttachPin(_VBAT); adcStart(_VBAT); - // initialize e-paper display - SPI.begin(18, 19, 23, TFT_CS); // MISO is not connected to TFT_MISO! - display.init(); + Serial.println("millis(): " + String(millis())); #define BME_SDA 21 #define BME_SCL 22 Wire.begin(BME_SDA, BME_SCL); if (bme280.begin()) { - bme280_active = true; + sensors_active.bme280 = true; } else { ESP_LOGE(TAG, "Could not find a valid BME280 sensor, check wiring!"); } + + Serial.println("millis(): " + String(millis())); + if (bme680.begin()) { - bme680_active = true; + sensors_active.bme680 = true; // Set up oversampling and filter initialization bme680.setTemperatureOversampling(BME680_OS_8X); @@ -604,12 +719,15 @@ void setup() bme680.setPressureOversampling(BME680_OS_4X); bme680.setIIRFilterSize(BME680_FILTER_SIZE_3); bme680.setGasHeater(320, 150); // 320*C for 150 ms + bme680.beginReading(); } else { ESP_LOGE(TAG, "Could not find a valid BME680 sensor, check wiring!"); } + Serial.println("millis(): " + String(millis())); + if (uv.begin()) { - uv_active = true; + sensors_active.uv = true; uv.setIntegrationTime(VEML6075_100MS); // Set the integration constant uv.setHighDynamic(true); // Set the high dynamic mode @@ -622,55 +740,66 @@ void setup() Serial.println("Failed to communicate with VEML6075 sensor, check wiring?"); } + Serial.println("millis(): " + String(millis())); + if (lightMeter.begin()) { - light_active = true; + sensors_active.light = true; } else { Serial.println("Failed to communicate with BH1750 sensor, check wiring?"); } + Serial.println("millis(): " + String(millis())); + sds.begin(); - FirmwareVersionResult sds_fw = sds.queryFirmwareVersion(); - if (sds_fw.isOk()) { - sds_active = true; + Serial.println("millis(): " + String(millis())); - sds.setActiveReportingMode(); // ensures sensor is in 'active' reporting mode - sds.setCustomWorkingPeriod(5); // sensor sends data every 3 minutes - } else { - Serial.println("Failed to communicate with SDS011 sensor, check wiring?"); + if (wakeup_reason == ESP_SLEEP_WAKEUP_UNDEFINED || bootCount == 1) { + FirmwareVersionResult sds_fw = sds.queryFirmwareVersion(); + if (sds_fw.isOk()) { + sensors_active.sds = true; + + sds.setActiveReportingMode(); // ensures sensor is in 'active' reporting mode + sds.setCustomWorkingPeriod(5); // sensor sends data every 3 minutes + } else { + Serial.println("Failed to communicate with SDS011 sensor, check wiring?"); + } } - //display.clearScreen(); - //display.refresh(); + Serial.println("millis(): " + String(millis())); - ESP_LOGD(TAG, "displaying welcome screen"); - helloWorld(); - display.powerOff(); + // initialize e-paper display + SPI.begin(18, 19, 23, TFT_CS); // MISO is not connected to TFT_MISO! + display.init(0, false, false); + display.setRotation(1); - ESP_LOGD(TAG, "connecting to WiFi"); + Serial.println("millis(): " + String(millis())); - WiFi.setHostname("esp32-weatherstation"); + if (wakeup_reason == ESP_SLEEP_WAKEUP_UNDEFINED || bootCount == 1) { + // wakeup not caused by deep sleep + display.clearScreen(); + display.refresh(); + helloWorld(); + display.powerOff(); + } else { + // wakeup by deep sleep +// displayValues(); + } - wifiMulti.addAP(WIFI_SSID, WIFI_PASSWD); - wifiMulti.addAP(WIFI_SSID2, WIFI_PASSWD2); - wifiMulti.addAP(WIFI_SSID3, WIFI_PASSWD3); + ESP_LOGD(TAG, "connecting to WiFi"); + Serial.println("millis(): " + String(millis())); - for (int tries=0; wifiMulti.run() != WL_CONNECTED && tries < 10; tries++) { - Serial.print("."); - delay(500); - } + wifiConnect(); - if(wifiMulti.run() == WL_CONNECTED) { - Serial.println(""); - Serial.println("WiFi connected"); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - displayIcoPartial(ico_wifi16, display.width()-20, y_offset+0, ico_wifi16_width, ico_wifi16_height); - } + WiFi.waitForConnectResult(); + //displayIcoPartial(ico_wifi16, display.width()-20, y_offset+0, ico_wifi16_width, ico_wifi16_height); - ESP_LOGD(TAG, "trying to fetch over-the-air update"); - if (WiFi.status() == WL_CONNECTED) { - ota.update(); + if (wakeup_reason == ESP_SLEEP_WAKEUP_UNDEFINED || bootCount == 1) { + // wakeup not caused by deep sleep + ESP_LOGD(TAG, "trying to fetch over-the-air update"); + if (WiFi.status() == WL_CONNECTED) { + ota.update(); + } } WiFi.setSleep(true); @@ -684,7 +813,6 @@ void setup() if (!ota.getMAC().equals("246f28d1eff4")) mqtt.subscribe("thomas/sensor/246f28d1eff4/#", receiveMqtt); /* temp: publish version */ - delay(5000); String topic_version = String("thomas/sensor/") + ota.getMAC() + String("/version"); const char* fw_version_str = String(FW_VERSION).c_str(); mqtt.publish(topic_version.c_str(), fw_version_str, strlen(fw_version_str)); @@ -705,27 +833,27 @@ void setup() */ void loop() { - /* Do an e-paper display refresh every 1 minutes */ - if (millis() - lastDisplayUpdate >= 1*60*1000) { - lastDisplayUpdate = millis(); +/* if(wifiMulti.run() != WL_CONNECTED) { + Serial.println("WiFi not connected!"); + delay(1000); + }*/ /* Do a full refresh every hour */ - if (millis() - lastDisplayRefresh >= 60*60*1000) { - lastDisplayRefresh = millis(); - display.clearScreen(); - display.refresh(); - } - - getSensorMeasurements(); - displayValues(); - printValues(); - sendValues(); + if (getTimestamp() - lastDisplayRefresh >= 60*60) { + Serial.printf("last display refresh :%ds ago", getTimestamp() - lastDisplayRefresh); + lastDisplayRefresh = getTimestamp(); + display.clearScreen(); + display.refresh(); } - if(wifiMulti.run() != WL_CONNECTED) { - Serial.println("WiFi not connected!"); - delay(1000); - } + getSensorMeasurements(); + displayValues(); + printValues(); + sendValues(); + + int runtime = millis()/1000; + if (runtime < 0 || runtime >= TIME_TO_SLEEP) runtime = 0; + gotoSleep(TIME_TO_SLEEP - runtime); delay(2000); }