This project measures temperature, humidity and air pressure and periodically publishes them to an MQTT broker (for example Mosquitto in Home Assistant). Between readings the ESP32 goes into deep sleep to save battery.
Parts
- ESP32 DevKit board (30- or 38-pin both work)
- BME280 sensor module (I2C). Note: the BMP280 chip has no humidity sensor, so check carefully when buying.
- Jumper wires, breadboard; an 18650 Li-ion cell + charger board for battery use
Wiring
| BME280 | ESP32 |
|---|---|
| VIN / VCC | 3V3 |
| GND | GND |
| SDA | GPIO21 |
| SCL | GPIO22 |
The I2C address is usually 0x76 (or 0x77 depending on the module). If the sensor isn't detected, run an I2C scanner sketch.
Libraries (Arduino IDE)
Adafruit BME280 Library(with Adafruit Unified Sensor)PubSubClientby Nick O'Leary
Source code
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
const char* WIFI_SSID = "your_wifi";
const char* WIFI_PASS = "your_password";
const char* MQTT_HOST = "192.168.1.10";
const uint16_t MQTT_PORT = 1883;
const uint64_t SLEEP_US = 5ULL * 60ULL * 1000000ULL; // 5 minutes
Adafruit_BME280 bme;
WiFiClient net;
PubSubClient mqtt(net);
void goSleep() {
WiFi.disconnect(true);
esp_sleep_enable_timer_wakeup(SLEEP_US);
esp_deep_sleep_start();
}
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
if (!bme.begin(0x76)) {
Serial.println("BME280 not found!");
goSleep();
}
// "Forced" mode: measure once, then return to sleep, using less power
bme.setSampling(Adafruit_BME280::MODE_FORCED);
bme.takeForcedMeasurement();
float t = bme.readTemperature();
float h = bme.readHumidity();
float p = bme.readPressure() / 100.0F;
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
uint32_t t0 = millis();
while (WiFi.status() != WL_CONNECTED && millis() - t0 < 15000) delay(100);
if (WiFi.status() != WL_CONNECTED) goSleep();
mqtt.setServer(MQTT_HOST, MQTT_PORT);
if (mqtt.connect("esp32diy-tram1")) { // add user/pass if your broker requires it
char buf[32];
snprintf(buf, sizeof buf, "%.2f", t); mqtt.publish("esp32diy/tram1/nhiet_do", buf, true);
snprintf(buf, sizeof buf, "%.2f", h); mqtt.publish("esp32diy/tram1/do_am", buf, true);
snprintf(buf, sizeof buf, "%.1f", p); mqtt.publish("esp32diy/tram1/ap_suat", buf, true);
mqtt.loop();
delay(100); // give the packets time to leave
}
goSleep();
}
void loop() {} // never reached
Home Assistant configuration
Add this to configuration.yaml (the MQTT integration must already be set up):
mqtt:
sensor:
- name: "Outdoor temperature"
state_topic: "esp32diy/tram1/nhiet_do"
unit_of_measurement: "°C"
device_class: temperature
- name: "Outdoor humidity"
state_topic: "esp32diy/tram1/do_am"
unit_of_measurement: "%"
device_class: humidity
- name: "Pressure"
state_topic: "esp32diy/tram1/ap_suat"
unit_of_measurement: "hPa"
device_class: atmospheric_pressure
Tip: publish with
retain = true so Home Assistant always has the latest value after a restart instead of waiting 5 minutes.Next steps
- Read the battery voltage through the ADC (a 2-resistor divider) and send it along. See Deep sleep and GPIO pins to avoid.
- Use a static IP, or store the WiFi channel/BSSID in RTC memory, to connect faster and stay awake less.
- Switch to ESPHome if you'd rather drop the C++ entirely.