Solar powered weather station with ESP32

Project Description

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.

Hardware Description

The following parts were used:

  • ESP32 S3 - The ESP32 S3 is a microcontroller unit (MCU) developed by Espressif Systems. It's known for its low power consumption and integrated Wi-Fi and Bluetooth capabilities. It was mainly used for data acquisition and to publish the data from the sensor online.
  • BME 680 - The BME680 is a small, integrated environmental sensor developed by Bosch Sensortec. It can measure barometric pressure, humidity, and ambient temperature;
  • Pololu 3.3V Step-Up/Step-Down Voltage Regulator S7V8F3 - This is a compact, efficient voltage regulator capable of outputting a fixed 3.3V from input voltages between 2.7V and 11.8V. It's ideal for powering 3.3V electronic devices from various battery types or power sources;
  • Solar panel 6V, 160mA, 0.96W - A small solar panel capable of converting sunlight into electrical energy. With a power output of 0.96 watts and a current output of 160mA at 6 volts, it's suitable for small-scale energy harvesting applications, such as powering low-power electronic devices or charging small batteries. It was used because the board has an integrated LDO that can't work with voltages under 3.6 V; besides that, once the drop-out voltage is getting smaller, the LDO will become more and more inefficient, consuming more energy. This buck-boost has a very low quiescent current since it's a switching power supply; it also provides short-circuit protection.
  • MPPT SOLAR BATTERY CHARGING MODULE, 3.7V, CN3791 - This module is designed for charging single-cell Lithium batteries using solar power. It incorporates Maximum Power Point Tracking (MPPT) technology, which optimizes the power output from the solar panel to efficiently charge the battery. It's specifically designed for 3.7V batteries and is commonly used in portable solar charging systems;
  • Li-Po battery 1050 mAh - used for energy storage;
  • 10k and 36k resistors;
  • Breadboard and jumper wires - used for fast prototyping.

The following electric schematic represents the project’s hardware

The prototyping board can be seen bellow

Software Description

The code flowchart can be seen bellow

  • Wi-Fi and MQTT Configuration
 
#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.

  • Constants and Global Variables
 
#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.

  • Wi-Fi and MQTT Event Handling Functions
 
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.

  • Deep Sleep Function
 
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.

  • Setup Function
 
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.

  • Main loop
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.

Results

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.

Conclusion

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.

Bibliography

iothings/proiecte/2023/solar_powered_weather_station_with_esp32.txt · Last modified: 2024/01/15 14:13 by tudorel.tibrea
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0