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.
Above we can see a prolonged use time with a lot of data entry points.*
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 the ESP wakes from deep sleep, the energy draw increases very drastically and the battery voltage might vary.*
*For data visualization and plotting, MQTT Explore was used.
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 dedicated 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.