This is an old revision of the document!
This project aims to design and build a truly autonomous weather station by leveraging solar energy harvesting. The weather station will monitor the temperature, the humidity, the atmospheric pressure and the state of charge of the Li-Po battery. The project will use MQTT communication protocol with ESP32 to publish messages and subscripe to topics, making the data accessible for various applications.
The following parts were used:
The following electric schematic represents the project’s hardware
The code flowchart can be seen bellow
#include <WiFi.h> #include <AsyncMqttClient.h> #define WIFI_SSID "" #define WIFI_PASSWORD "" #define MQTT_HOST "test.mosquitto.org" #define MQTT_PORT 1883
This part includes necessary libraries for Wi-Fi and MQTT functionality. It defines the Wi-Fi credentials and the MQTT broker details.
#define batPin 4 #define LOW 3300 #define HIGH 4095 #define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */ #define TIME_TO_SLEEP 7 /* Time ESP32 will go to sleep (in seconds) */ // Temperature MQTT Topics #define MQTT_PUB_TEMP "esp32/weather_station/temperature" #define MQTT_PUB_HUM "esp32/weather_station/humidity" #define MQTT_PUB_PRES "esp32/weather_station/pressure" #define MQTT_PUB_BAT "esp32/weather_station/batteryPercentage" // BME280 I2C Adafruit_BME280 bme; // Variables to hold sensor readings float temp; float hum; float pres; //int publishACK = 0; int batValue = 0; //RTC_DATA_ATTR int bootCount = 0; AsyncMqttClient mqttClient; TimerHandle_t mqttReconnectTimer; TimerHandle_t wifiReconnectTimer;
Here, constants and global variables are defined, including the pin for battery measurement, sensor reading variables (temp, hum, pres), and MQTT client and timer objects.
void connectToWifi() { Serial.println("Connecting to Wi-Fi..."); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); } void connectToMqtt() { Serial.println("Connecting to MQTT..."); mqttClient.connect(); } void WiFiEvent(WiFiEvent_t event) { Serial.printf("[WiFi-event] event: %d\n", event); switch(event) { case SYSTEM_EVENT_STA_GOT_IP: Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); connectToMqtt(); break; case SYSTEM_EVENT_STA_DISCONNECTED: Serial.println("WiFi lost connection"); xTimerStop(mqttReconnectTimer, 0); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi xTimerStart(wifiReconnectTimer, 0); break; } } void onMqttConnect(bool sessionPresent) { Serial.println("Connected to MQTT."); Serial.print("Session present: "); Serial.println(sessionPresent); } void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) { Serial.println("Disconnected from MQTT."); if (WiFi.isConnected()) { xTimerStart(mqttReconnectTimer, 0); } } void onMqttPublish(uint16_t packetId) { Serial.print("Publish acknowledged."); Serial.print(" packetId: "); Serial.println(packetId); //publishACK = publishACK + 1; }
These functions manage Wi-Fi and MQTT connectivity. They handle events like connecting to Wi-Fi, MQTT broker, and publishing messages.
void EnterSleep() { /* First we configure the wake up source We set our ESP32 to wake up every 7 seconds */ esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) + " Seconds"); Serial.println("Going to sleep now"); Serial.flush(); // force specific powerdown modes for RTC peripherals and RTC memories esp_sleep_pd_config(ESP_PD_DOMAIN_MAX, ESP_PD_OPTION_OFF); esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF); esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_OFF); esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF); // Enter Deep Sleep -- Hibernation esp_deep_sleep_start(); }
EnterSleep configures the ESP32 to enter deep sleep mode for power saving, waking up periodically to perform measurements and send data.
void setup() { Serial.begin(115200); Serial.println(); // Initialize BME280 sensor if (!bme.begin(0x76)) { Serial.println("Could not find a valid BME280 sensor, check wiring!"); while (1); } mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToMqtt)); wifiReconnectTimer = xTimerCreate("wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToWifi)); WiFi.onEvent(WiFiEvent); mqttClient.onConnect(onMqttConnect); mqttClient.onDisconnect(onMqttDisconnect); mqttClient.onPublish(onMqttPublish); mqttClient.setServer(MQTT_HOST, MQTT_PORT); connectToWifi(); } }
In setup, serial communication is started, the BME280 sensor is initialized, Wi-Fi and MQTT timers and event handlers are set up, and the ESP32 connects to Wi-Fi.
void loop() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { // Save the last time a new reading was published previousMillis = currentMillis; // New BME280 sensor readings temp = bme.readTemperature(); //temp = 1.8*bme.readTemperature() + 32; hum = bme.readHumidity(); pres = bme.readPressure()/100.0F; batValue = (analogRead(batPin) - LOW)* 100 / (HIGH - LOW); // Publish an MQTT message on topic esp32/BME2800/temperature uint16_t packetIdPub1 = mqttClient.publish(MQTT_PUB_TEMP, 1, true, String(temp).c_str()); Serial.printf("Publishing on topic %s at QoS 1, packetId: %i", MQTT_PUB_TEMP, packetIdPub1); Serial.printf("Message: %.2f \n", temp); // Publish an MQTT message on topic esp32/weather_station/humidity uint16_t packetIdPub2 = mqttClient.publish(MQTT_PUB_HUM, 1, true, String(hum).c_str()); Serial.printf("Publishing on topic %s at QoS 1, packetId %i: ", MQTT_PUB_HUM, packetIdPub2); Serial.printf("Message: %.2f \n", hum); // Publish an MQTT message on topic esp32/weather_station/pressure uint16_t packetIdPub3 = mqttClient.publish(MQTT_PUB_PRES, 1, true, String(pres).c_str()); Serial.printf("Publishing on topic %s at QoS 1, packetId %i: ", MQTT_PUB_PRES, packetIdPub3); Serial.printf("Message: %.2f \n", pres); uint16_t packetIdPub4 = mqttClient.publish(MQTT_PUB_BAT, 1, true, String(batValue).c_str()); Serial.printf("Publishing on topic %s at QoS 1, packetId: %i", MQTT_PUB_BAT, packetIdPub4); Serial.printf("Message: %d \n", batValue); delay(3000); EnterSleep(); } }
The loop function periodically reads data from the BME280 sensor and publishes it to the MQTT broker. After publishing, it puts the ESP32 into deep sleep mode for 7 seconds.
As you can see, the ADC reading for the battery are not that precise. We can think of at least 2 culprits in this case. First would be the ESP32 ADC's poor performance and the second one, the method chosen; because we wake the ESP wakes from deep sleep, the energy draw increases very drastically and the battery voltage might vary.
In conclusion, the proposed solution demonstrates how an ESP32, combined with a BME280 sensor, can be used to create a power-efficient, wireless weather station capable of continuously monitoring environmental conditions and transmitting the data in real-time via Wi-Fi and MQTT, showcasing the potential of IoT in remote sensing and data communication. Further improvements will be to find a way to get precise and constant readings of the battery state of charge, although the literature says it isn't quite correct to calculate it based on voltage; the most correct way to solve this problem from an strictly engineering standpoint is to use an IC such as a fuel gauge or a coulomb counter.
https://ocw.cs.pub.ro/courses/iothings
https://randomnerdtutorials.com/esp32-bme280-arduino-ide-pressure-temperature-humidity/;
https://randomnerdtutorials.com/esp32-useful-wi-fi-functions-arduino/;
https://randomnerdtutorials.com/esp32-mqtt-publish-subscribe-arduino-ide/;
https://randomnerdtutorials.com/esp32-deep-sleep-arduino-ide-wake-up-sources/;
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/sleep_modes.html;
https://docs.espressif.com/projects/esp-idf/en/v4.4/esp32/api-reference/peripherals/adc.html.