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.