Browse Source

Merge branch 'deep_sleep'. Fixes #3

main
Hendrik Langer 5 years ago
parent
commit
07b80f388d
  1. 4
      README.md
  2. 12
      platformio.ini
  3. 470
      src/main.cpp
  4. 22
      src/network/XD0MQTT.cpp
  5. 5
      src/network/XD0MQTT.h
  6. 10
      src/network/XD0OTA.cpp

4
README.md

@ -64,8 +64,8 @@ Serial (HardwareSerial2)
SDS011 pin | ESP32 pin | Notes
-----------|-----------|----------
TXD | IO16 |
RXD | IO17 |
TXD | IO15 | (don't use IO16 together with PSRAM)
RXD | IO2 | (don't use IO17 together with PSRAM)
GND | GND |
25µm | nc |
5V | VUSB | 5V

12
platformio.ini

@ -18,11 +18,11 @@ board = lolin_d32_pro
board_build.f_cpu = 160000000L
build_flags =
-DLOG_DEFAULT_LEVEL=ESP_LOG_VERBOSE
-DLOG_LOCAL_LEVEL=ESP_LOG_VERBOSE
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
; -DBOARD_HAS_PSRAM
; -mfix-esp32-psram-cache-issue
; -DLOG_DEFAULT_LEVEL=ESP_LOG_VERBOSE
; -DLOG_LOCAL_LEVEL=ESP_LOG_VERBOSE
; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
-DBOARD_HAS_PSRAM
-mfix-esp32-psram-cache-issue
-DWIFI_SSID="\"${sysenv.WIFI_SSID}\""
-DWIFI_PASSWD="\"${sysenv.WIFI_PASSWD}\""
-DWIFI_SSID2="\"${sysenv.WIFI_SSID2}\""
@ -47,7 +47,7 @@ lib_deps =
; SDS011 sensor Library
; Nova Fitness Sds dust sensors library
https://github.com/lewapek/sds-dust-sensors-arduino-library.git
; ArduinoJSON
ArduinoJSON
monitor_speed = 115200
upload_speed = 115200

470
src/main.cpp

@ -32,6 +32,8 @@
#include "network/XD0OTA.h"
#include "network/XD0MQTT.h"
#include <ArduinoJson.h>
#include "SensorHistory.h"
#include "icons.h"
@ -42,6 +44,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<GxEPD2_213_B72, GxEPD2_213_B72::HEIGHT> display(GxEPD2_213_B72(/*CS=SS*/ TFT_CS, /*DC=*/ TFT_DC, /*RST=*/ TFT_RST, /*BUSY=*/ -1)); // GDEH0213B72
static constexpr uint8_t y_offset = 6;
@ -53,8 +59,22 @@ SdsDustSensor sds(Serial2);
Adafruit_VEML6075 uv = Adafruit_VEML6075();
BH1750 lightMeter;
XD0OTA ota("esp32-weatherstation");
constexpr unsigned int JSON_BUF_LEN = 512;
constexpr unsigned int JSON_CAPACITY = JSON_OBJECT_SIZE(16) + 0*JSON_ARRAY_SIZE(2) + 120;
XD0MQTT mqtt;
XD0OTA ota("esp32-weatherstation");
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
@ -72,22 +92,143 @@ struct __attribute__((packed)) sensor_readings_t {
float temperature_min = NAN; // °C
int battery = 0;
int8_t rssi = 0;
uint32_t lastUpdate = 0;
time_t lastUpdate = 0;
} sensor_readings;
sensor_readings_t sensors_a4cf1211c3e4, sensors_246f28d1fa5c, sensors_246f28d1a080, sensors_246f28d1eff4;
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();
if (sensors_active.bme680) {
bme680.setGasHeater(0, 0);
}
if (sensors_active.bme280) {
bme280.setSampling(Adafruit_BME280::MODE_SLEEP,
Adafruit_BME280::SAMPLING_X1, // temperature
Adafruit_BME280::SAMPLING_X1, // pressure
Adafruit_BME280::SAMPLING_X1, // humidity
Adafruit_BME280::FILTER_OFF );
}
if (sensors_active.light) {
static constexpr byte BH1750_I2CADDR = 0x23;
Wire.beginTransmission(BH1750_I2CADDR);
Wire.write(BH1750_POWER_DOWN);
byte ack = Wire.endTransmission();
}
if (sensors_active.uv) {
uv.shutdown(true);
}
}
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);
ESP_LOGI(TAG, "going to to sleep for %d seconds", sleep_time);
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()));
ESP_LOGD(TAG, "previous dhcp: %lu s ago", getTimestamp() - network.last_dhcp);
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++) {
ESP_LOGD("WiFi", ".");
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();
}
}
bool obtain_time() {
ESP_LOGI(TAG, "syncing time");
configTzTime("CET-1CEST,M3.5.0/2,M10.5.0/3", "de.pool.ntp.org");
struct tm timeinfo;
return getLocalTime(&timeinfo, 5000);
}
void helloWorld()
{
@ -101,7 +242,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;
if (display.epd2.hasFastPartialUpdate) {
display.setPartialWindow(0, 0, display.width(), display.height());
} else {
display.setFullWindow();
}
display.firstPage();
do
{
@ -139,20 +284,28 @@ 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();
ESP_LOGI(TAG, "Temperature : %8.2f °C", sensor_readings.temperature);
ESP_LOGI(TAG, "Pressure (Raw): %8.2f Pa", sensor_readings.pressure_raw);
ESP_LOGI(TAG, "Humidity : %8.2f %", sensor_readings.humidity);
}
if (bme680_active) {
if (sensors_active.bme680) {
bme680.endReading(); // ToDo
if (bme680.performReading()) {
sensor_readings.temperature = bme680.temperature;
sensor_readings.humidity = bme680.humidity;
sensor_readings.pressure_raw = bme680.pressure;
sensor_readings.voc = bme680.gas_resistance;
ESP_LOGI(TAG, "Temperature : %8.2f °C", sensor_readings.temperature);
ESP_LOGI(TAG, "Pressure (Raw): %8.2f Pa", sensor_readings.pressure_raw);
ESP_LOGI(TAG, "Humidity : %8.2f %", sensor_readings.humidity);
ESP_LOGI(TAG, "VOC : %5lu kOhm", sensor_readings.voc);
} else {
Serial.println("Failed to perform reading :(");
ESP_LOGE(TAG, "Failed to perform BME680 reading :(");
}
}
@ -174,50 +327,62 @@ 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();
ESP_LOGI(TAG, "UVI : %8.2f", sensor_readings.uvi);
ESP_LOGI(TAG, "UVA : %8.2f", sensor_readings.uva);
ESP_LOGI(TAG, "UVB : %8.2f", sensor_readings.uvb);
}
if (light_active) {
if (sensors_active.light) {
sensor_readings.lux = lightMeter.readLightLevel();
// auto-adjust sensitivity
if (sensor_readings.lux < 0) {
Serial.println("Error reading light level");
ESP_LOGE(TAG, "Error reading light level");
} else if (sensor_readings.lux > 40000.0) {
if (lightMeter.setMTreg(32)) {
Serial.println(F("Setting MTReg to low value for high light environment"));
ESP_LOGD(TAG, "Setting MTReg to low value for high light environment");
}
} else if (sensor_readings.lux <= 10.0) {
if (lightMeter.setMTreg(138)) {
Serial.println(F("Setting MTReg to high value for low light environment"));
ESP_LOGD(TAG, "Setting MTReg to high value for low light environment");
}
} else { // if (sensor_readings.lux > 10.0)
if (lightMeter.setMTreg(69)) {
Serial.println(F("Setting MTReg to default value for normal light environment"));
ESP_LOGD(TAG, "Setting MTReg to default value for normal light environment");
}
}
ESP_LOGI(TAG, "Lux : %8.2f lx", sensor_readings.lux);
}
if (sds_active) {
if (sensors_active.sds) {
PmResult pm = sds.readPm();
if (pm.isOk()) {
sensor_readings.pm10 = pm.pm10;
sensor_readings.pm25 = pm.pm25;
ESP_LOGI(TAG, "PM10 : %8.2f µg/m³", sensor_readings.pm10);
ESP_LOGI(TAG, "PM2.5 : %8.2f µg/m³", sensor_readings.pm25);
}
}
sensor_readings.battery = analogRead(_VBAT);
sensor_readings.rssi = WiFi.RSSI();
sensor_readings.lastUpdate = millis();
ESP_LOGI(TAG, "RSSI : %5d dBm", sensor_readings.rssi);
ESP_LOGI(TAG, "Battery : %5d ", sensor_readings.battery);
ESP_LOGI(TAG, "Heap : %5lu", ESP.getFreeHeap());
sensor_readings.lastUpdate = getTimestamp();
}
void receiveMqtt(const char* topic, const char* data) {
void receiveMqtt(const char* topic, const char* data, int data_len) {
sensor_readings_t* sensor = NULL;
ESP_LOGI(TAG, "received MQTT message on subscribed topic %s", topic);
if (strstr(topic, "thomas/sensor/a4cf1211c3e4") == topic) {
sensor = &sensors_a4cf1211c3e4;
} else if (strstr(topic, "thomas/sensor/246f28d1fa5c") == topic) {
@ -231,8 +396,32 @@ void receiveMqtt(const char* topic, const char* data) {
char* topic_last = strrchr(topic, '/');
if (topic_last && sensor) {
if (strcmp("/temperature", topic_last) == 0) {
if (strcmp("/json", topic_last) == 0) {
StaticJsonDocument<JSON_CAPACITY+120> jsonDoc;
DeserializationError err = deserializeJson(jsonDoc, data, data_len);
if (err) {
ESP_LOGW(TAG, "Error parsing JSON, code: %s", err.c_str());
} else {
// got json
if (jsonDoc.containsKey("temperature")) sensor->temperature = jsonDoc["temperature"].as<float>();
if (jsonDoc.containsKey("humidity")) sensor->humidity = jsonDoc["humidity"].as<float>();
if (jsonDoc.containsKey("pressure")) sensor->pressure = jsonDoc["pressure"].as<float>();
if (jsonDoc.containsKey("voc")) sensor->voc = jsonDoc["voc"].as<uint32_t>();
if (jsonDoc.containsKey("lux")) sensor->lux = jsonDoc["lux"].as<float>();
if (jsonDoc.containsKey("uvi")) sensor->uvi = jsonDoc["uvi"].as<float>();
if (jsonDoc.containsKey("uva")) sensor->uva = jsonDoc["uva"].as<float>();
if (jsonDoc.containsKey("uvb")) sensor->uvb = jsonDoc["uvb"].as<float>();
if (jsonDoc.containsKey("pm10")) sensor->pm10 = jsonDoc["pm10"].as<float>();
if (jsonDoc.containsKey("pm2.5")) sensor->pm25 = jsonDoc["pm2.5"].as<float>();
if (jsonDoc.containsKey("voltage")) sensor->battery = jsonDoc["voltage"].as<int>();
if (jsonDoc.containsKey("rssi")) sensor->rssi = jsonDoc["rssi"].as<int8_t>();
if (jsonDoc.containsKey("timestamp")) sensor->lastUpdate = jsonDoc["timestamp"].as<time_t>();
ESP_LOGI(TAG, "got new values from %s, timestamp: %lu", topic, sensor->lastUpdate);
ESP_LOGI(TAG, "%lu seconds ago", topic, getTimestamp() - sensor->lastUpdate);
}
} else if (strcmp("/temperature", topic_last) == 0) {
sensor->temperature = atof(data);
sensor->lastUpdate = getTimestamp();
} else if (strcmp("/humidity", topic_last) == 0) {
sensor->humidity = atof(data);
} else if (strcmp("/pressure", topic_last) == 0) {
@ -252,7 +441,6 @@ void receiveMqtt(const char* topic, const char* data) {
} else if (strcmp("/voc", topic_last) == 0) {
sensor->voc = atof(data);
}
sensor->lastUpdate = millis();
}
}
@ -267,6 +455,7 @@ void displayValues() {
char timeStr[40];
getTime(timeStr, sizeof(timeStr), "%d. %b %Y %H:%M:%S");
ESP_LOGD(TAG, "displayValues()");
if (display.epd2.hasFastPartialUpdate) {
display.setPartialWindow(0, 0, display.width(), display.height());
@ -354,7 +543,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 +567,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 +584,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;
@ -428,16 +617,16 @@ void displayValues() {
// other nodes
display.setFont(NULL);
display.setCursor(0, y_offset+70);
if (!ota.getMAC().equals("246f28d1fa5c") && millis() - sensors_246f28d1fa5c.lastUpdate < 15*60*1000) {
if (!ota.getMAC().equals("246f28d1fa5c") && getTimestamp() - sensors_246f28d1fa5c.lastUpdate < 15*60) {
display.printf("246f28d1fa5c: %4.1f %4.1f %6.1f\n", sensors_246f28d1fa5c.temperature, sensors_246f28d1fa5c.humidity, sensors_246f28d1fa5c.pressure);
}
if (!ota.getMAC().equals("a4cf1211c3e4") && millis() - sensors_a4cf1211c3e4.lastUpdate < 15*60*1000) {
if (!ota.getMAC().equals("a4cf1211c3e4") && getTimestamp() - sensors_a4cf1211c3e4.lastUpdate < 15*60) {
display.printf("a4cf1211c3e4: %4.1f %4.1f %6.1f\n", sensors_a4cf1211c3e4.temperature, sensors_a4cf1211c3e4.humidity, sensors_a4cf1211c3e4.pressure);
}
if (!ota.getMAC().equals("246f28d1a080") && millis() - sensors_246f28d1a080.lastUpdate < 15*60*1000) {
if (!ota.getMAC().equals("246f28d1a080") && getTimestamp() - sensors_246f28d1a080.lastUpdate < 15*60) {
display.printf("246f28d1a080: %4.1f %4.1f %6.1f\n", sensors_246f28d1a080.temperature, sensors_246f28d1a080.humidity, sensors_246f28d1a080.pressure);
}
if (!ota.getMAC().equals("246f28d1eff4") && millis() - sensors_246f28d1eff4.lastUpdate < 15*60*1000) {
if (!ota.getMAC().equals("246f28d1eff4") && getTimestamp() - sensors_246f28d1eff4.lastUpdate < 15*60) {
display.printf("246f28d1eff4: %4.1f %4.1f %6.1f\n", sensors_246f28d1eff4.temperature, sensors_246f28d1eff4.humidity, sensors_246f28d1eff4.pressure);
}
@ -448,58 +637,45 @@ void displayValues() {
}
void printValues() {
if (bme280_active || bme680_active) {
#define SEALEVELPRESSURE_HPA (1013.25)
Serial.print("Temperature = ");
Serial.print(sensor_readings.temperature);
Serial.println(" *C");
Serial.print("Pressure = ");
Serial.print(sensor_readings.pressure);
Serial.println(" hPa");
Serial.print("Pressure [RAW] = ");
Serial.print(sensor_readings.pressure_raw / 100.0F);
Serial.println(" hPa");
Serial.print("Humidity = ");
Serial.print(sensor_readings.humidity);
Serial.println(" %");
}
if (bme680_active) {
Serial.print("VOC = ");
Serial.print(sensor_readings.voc / 1000.0F);
Serial.println(" kOhm");
}
Serial.println();
if (uv_active) {
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) {
Serial.print("PM2.5 = ");
Serial.print(sensor_readings.pm25);
Serial.print(", PM10 = ");
Serial.println(sensor_readings.pm10);
}
Serial.print("RSSI: "); Serial.println(sensor_readings.rssi);
Serial.print("Battery: "); Serial.println(sensor_readings.battery);
Serial.print("Free HEAP: "); Serial.println(ESP.getFreeHeap());
}
void sendValues() {
for (int tries=0; mqtt.isConnected() == false && tries < 10; tries++) {
ESP_LOGD(TAG, "waiting for mqtt connection");
delay(300);
}
/* send values MQTT JSON */
char buf[JSON_BUF_LEN];
StaticJsonDocument<JSON_CAPACITY> jsonDoc;
if (sensors_active.bme280 || sensors_active.bme680) {
jsonDoc["temperature"] = sensor_readings.temperature;
jsonDoc["humidity"] = sensor_readings.humidity;
jsonDoc["pressure"] = sensor_readings.pressure;
}
if (sensors_active.bme680) {
jsonDoc["voc"] = sensor_readings.voc;
}
if (sensors_active.light) {
jsonDoc["lux"] = sensor_readings.lux;
}
if (sensors_active.uv) {
jsonDoc["uvi"] = sensor_readings.uvi;
jsonDoc["uva"] = sensor_readings.uva;
jsonDoc["uvb"] = sensor_readings.uvb;
}
if (sensors_active.sds) {
jsonDoc["pm10"] = sensor_readings.pm10;
jsonDoc["pm2.5"] = sensor_readings.pm25;
}
jsonDoc["voltage"] = sensor_readings.battery;
jsonDoc["rssi"] = sensor_readings.rssi;
jsonDoc["timestamp"] = sensor_readings.lastUpdate;
serializeJson(jsonDoc, buf, JSON_BUF_LEN);
String topic_json = String("thomas/sensor/") + ota.getMAC() + String("/json");
mqtt.publish(topic_json.c_str(), buf, strlen(buf), 1, 1);
delay(10);
/* 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");
@ -510,20 +686,13 @@ void sendValues() {
mqtt.publishf(topic_pressure.c_str(), "%.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));
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");
@ -533,13 +702,13 @@ void sendValues() {
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);
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);
@ -569,7 +738,12 @@ void sendValues() {
void setup()
{
Serial.begin(115200);
delay(10);
Serial2.begin(9600, SERIAL_8N1, /*rx*/15, /*tx*/2); // IMPORTANT: don't run with default pins 16, 17 as they are connected to PSRAM on boards that ship with it
esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
++bootCount;
ESP_LOGI(TAG, "Boot number: %d", bootCount);
Serial.println("millis(): " + String(millis()));
ESP_LOGD(TAG, "setup hardware and sensors");
@ -583,20 +757,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 +779,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
@ -619,72 +797,85 @@ void setup()
2.95, 1.74, // UVB_C and UVB_D coefficients
0.001461, 0.002591); // UVA and UVB responses
} else {
Serial.println("Failed to communicate with VEML6075 sensor, check wiring?");
ESP_LOGW(TAG, "Failed to communicate with VEML6075 sensor, check wiring?");
}
Serial.println("millis(): " + String(millis()));
if (lightMeter.begin()) {
light_active = true;
sensors_active.light = true;
lightMeter.setMTreg((byte) BH1750_DEFAULT_MTREG);
} else {
Serial.println("Failed to communicate with BH1750 sensor, check wiring?");
ESP_LOGW(TAG, "Failed to communicate with BH1750 sensor, check wiring?");
}
sds.begin();
Serial.println("millis(): " + String(millis()));
//sds.begin(); // don't call begin, only messes with Serial
Serial.println("millis(): " + String(millis()));
if (wakeup_reason == ESP_SLEEP_WAKEUP_UNDEFINED || bootCount == 1) {
FirmwareVersionResult sds_fw = sds.queryFirmwareVersion();
if (sds_fw.isOk()) {
sds_active = true;
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?");
ESP_LOGW(TAG, "Failed to communicate with SDS011 sensor, check wiring?");
}
}
//display.clearScreen();
//display.refresh();
Serial.println("millis(): " + String(millis()));
// 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);
Serial.println("millis(): " + String(millis()));
ESP_LOGD(TAG, "displaying welcome screen");
if (wakeup_reason == ESP_SLEEP_WAKEUP_UNDEFINED || bootCount == 1) {
// wakeup not caused by deep sleep
display.clearScreen();
display.refresh();
lastDisplayRefresh = getTimestamp();
helloWorld();
display.powerOff();
} else {
// wakeup by deep sleep
// displayValues();
}
ESP_LOGD(TAG, "connecting to WiFi");
Serial.println("millis(): " + String(millis()));
WiFi.setHostname("esp32-weatherstation");
wifiConnect();
wifiMulti.addAP(WIFI_SSID, WIFI_PASSWD);
wifiMulti.addAP(WIFI_SSID2, WIFI_PASSWD2);
wifiMulti.addAP(WIFI_SSID3, WIFI_PASSWD3);
for (int tries=0; wifiMulti.run() != WL_CONNECTED && tries < 10; tries++) {
Serial.print(".");
delay(500);
}
if(wifiMulti.run() == WL_CONNECTED) {
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
WiFi.waitForConnectResult();
displayIcoPartial(ico_wifi16, display.width()-20, y_offset+0, ico_wifi16_width, ico_wifi16_height);
}
if (wakeup_reason == ESP_SLEEP_WAKEUP_UNDEFINED || bootCount == 1) {
// wakeup not caused by deep sleep
obtain_time();
ESP_LOGD(TAG, "trying to fetch over-the-air update");
if (WiFi.status() == WL_CONNECTED) {
ota.update();
}
}
WiFi.setSleep(true);
ESP_LOGD(TAG, "connecting to MQTT");
mqtt.begin();
if (!ota.getMAC().equals("a4cf1211c3e4")) mqtt.subscribe("thomas/sensor/a4cf1211c3e4/#", receiveMqtt);
if (!ota.getMAC().equals("246f28d1fa5c")) mqtt.subscribe("thomas/sensor/246f28d1fa5c/#", receiveMqtt);
if (!ota.getMAC().equals("246f28d1a080")) mqtt.subscribe("thomas/sensor/246f28d1a080/#", receiveMqtt);
if (!ota.getMAC().equals("246f28d1eff4")) mqtt.subscribe("thomas/sensor/246f28d1eff4/#", receiveMqtt);
if (!ota.getMAC().equals("a4cf1211c3e4")) mqtt.subscribe("thomas/sensor/a4cf1211c3e4/json", receiveMqtt);
if (!ota.getMAC().equals("246f28d1fa5c")) mqtt.subscribe("thomas/sensor/246f28d1fa5c/json", receiveMqtt);
if (!ota.getMAC().equals("246f28d1a080")) mqtt.subscribe("thomas/sensor/246f28d1a080/json", receiveMqtt);
if (!ota.getMAC().equals("246f28d1eff4")) mqtt.subscribe("thomas/sensor/246f28d1eff4/json", 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 +896,26 @@ 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();
if (getTimestamp() - lastDisplayRefresh >= 60*60) {
lastDisplayRefresh = getTimestamp();
display.clearScreen();
display.refresh();
}
getSensorMeasurements();
displayValues();
printValues();
sendValues();
}
delay(1);
displayValues();
if(wifiMulti.run() != WL_CONNECTED) {
Serial.println("WiFi not connected!");
delay(1000);
}
int runtime = millis()/1000;
if (runtime < 0 || runtime >= TIME_TO_SLEEP) runtime = 0;
gotoSleep(TIME_TO_SLEEP - runtime);
delay(2000);
}

22
src/network/XD0MQTT.cpp

@ -60,13 +60,15 @@ esp_err_t XD0MQTT::mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
ESP_LOGI(TAG, "TOPIC=%.*s\r\n", event->topic_len, event->topic);
ESP_LOGI(TAG, "DATA=%.*s\r\n", event->data_len, event->data);
for (const auto &subscription : subscriptions_) {
char topic[128];
strncpy(topic, event->topic, event->topic_len); topic[event->topic_len] = '\0';
// literal match
if (strcmp(event->topic, subscription.topic) == 0) {
subscription.cb(event->topic, event->data);
if (event->topic_len == strlen(subscription.topic) && strncmp(event->topic, subscription.topic, event->topic_len) == 0) {
subscription.cb(topic, event->data, event->data_len);
// '#' wildcard
} else if (subscription.topic[strlen(subscription.topic)-1] == '#'){
if (strncmp(event->topic, subscription.topic, strlen(subscription.topic)-1) == 0) {
subscription.cb(event->topic, event->data);
if (event->topic_len >= strlen(subscription.topic) && strncmp(event->topic, subscription.topic, strlen(subscription.topic)-1) == 0) {
subscription.cb(topic, event->data, event->data_len);
}
}
// ToDo: '+' wildcard
@ -106,10 +108,10 @@ bool XD0MQTT::begin(void) {
mqtt_cfg.password = MQTT_PASSWORD;
mqtt_cfg.user_context = (void*)this;
ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
Serial.printf("BROKER URI: %s\n", MQTT_BROKER_URI);
Serial.printf("MQTT USERNAME: %s\n", MQTT_USERNAME);
Serial.printf("MQTT PASSWORD: %s\n", MQTT_PASSWORD);
ESP_LOGD(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
ESP_LOGI(TAG, "BROKER URI: %s\n", MQTT_BROKER_URI);
ESP_LOGI(TAG, "MQTT USERNAME: %s\n", MQTT_USERNAME);
ESP_LOGI(TAG, "MQTT PASSWORD: %s\n", MQTT_PASSWORD);
client = esp_mqtt_client_init(&mqtt_cfg);
//esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
esp_mqtt_client_start(client);
@ -128,6 +130,10 @@ void XD0MQTT::end(void) {
esp_mqtt_client_destroy(client);
}
bool XD0MQTT::isConnected(void) {
return connected;
}
bool XD0MQTT::publish(const char* topic, const char* data, int len, int qos, int retain) {
int msg_id = esp_mqtt_client_publish(client, topic, data, len, qos, retain);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);

5
src/network/XD0MQTT.h

@ -17,7 +17,7 @@
#include "mqtt_client.h"
typedef std::function<void(char*, char*)> cb_t;
typedef std::function<void(char*, char*, int)> cb_t;
struct subscription_t {
const char* topic;
@ -37,9 +37,10 @@ class XD0MQTT {
bool subscribe(const char* topic, const cb_t &cb, int qos=1);
bool unsubscribe(const char* topic);
esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event);
bool connected = false;
bool isConnected(void);
std::vector<subscription_t> subscriptions_;
private:
bool connected = false;
// openssl s_client -showcerts -connect home.xd0.de:8883 </dev/null 2>/dev/null|openssl x509 -outform PEM >mqtt_xd0.de.pem
const char* rootCACertificate = \
"-----BEGIN CERTIFICATE-----\n" \

10
src/network/XD0OTA.cpp

@ -16,23 +16,21 @@ XD0OTA::XD0OTA(String deviceName) : deviceName{deviceName} {}
void XD0OTA::setClock() {
configTime(0, 0, "de.pool.ntp.org", "time.nist.gov"); // UTC
Serial.print(F("Waiting for NTP time sync: "));
ESP_LOGI(TAG, "Waiting for NTP time sync: ");
time_t now = time(nullptr);
int tries = 0;
while (now < 8 * 3600 * 2) {
yield();
delay(500);
Serial.print(F("."));
ESP_LOGI(TAG, ".");
now = time(nullptr);
tries++;
if (tries>15) return;
}
Serial.println(F(""));
struct tm timeinfo;
gmtime_r(&now, &timeinfo);
Serial.print(F("Current time: "));
Serial.print(asctime(&timeinfo));
ESP_LOGI(TAG, "Current time: %s", asctime(&timeinfo));
// setenv("TZ", "CET-1CEST,M3.5.0/2,M10.5.0/3", 1);
// tzset();
@ -57,7 +55,7 @@ String XD0OTA::getUpdateURL(String file, String extension) {
}
void XD0OTA::update(void) {
setClock();
//setClock();
// try device specific image first
String fwURL = getUpdateURL(getMAC(), ".bin");

Loading…
Cancel
Save