Temperature, Humidity and Atmospheric Pressure Sensor (BME280) - Outdoor [RUA]
Project Overview
The Outdoor Environmental Sensor Project is a low-power autonomous system designed to monitor
temperature, relative humidity, and atmospheric pressure in an outdoor environment.
The system prioritizes energy efficiency, reliability, and long-term autonomy, making it suitable
for continuous operation using standard AA batteries.
An ESP32 microcontroller collects data from a BME280 sensor, transmits the measurements via Wi-Fi
to a Raspberry Pi, and immediately enters deep sleep mode to minimize power consumption.
The Raspberry Pi stores, processes, and exposes the data through a web-based interface. You can see here: https://joelbonifacio.dynip.sapo.pt/electro6.1.html
Fig.1 - Materials: ESP32, BME280, batteries, support for the batteries and cables.
BME280 Technical Specifications
Parameter
Value
Temperature Range
-40 °C to +85 °C
Temperature Accuracy
±1.0 °C
Humidity Range
0 – 100 % RH
Humidity Accuracy
±3 % RH
Response Time
1 second
Hysteresis
±1% RH
Pressure Range
300 – 1100 hPa
Absolute Accuracy
±1 hPa
Relative Accuracy
±0.12 hPa (equivalent to ±1 meter in altitude)
RMS Noise
0.2 Pa
Communication Interface
SPI
Operating Voltage
3.3 V
Communication Interface
I2C (Gravity Connector) or SPI
ESP32 ↔ BME280 Wiring Diagram (SPI)
Here is the schematic of the circuit.
Fig.2 - Schematic of the circuit.
FireBeetle 2 ESP32-C6 Firmware (Low Power Mode + SPI)
// ----- CONFIGURAÇÃO HARDWARE -----
// O fio VCC do sensor BME280 liga AQUI, e não nos 3.3V fixos.
// Isto permite desligar o sensor e o LED vermelho completamente.
#define SENSOR_POWER_PIN 14
#define BME_CS 21
#define SPI_SCK 18
#define SPI_MISO 19
#define SPI_MOSI 23
// Battery voltage: built-in ADC on pin 0 (onboard resistive divider)
#define BATTERY_ADC_PIN 0
Adafruit_BME280 bme(BME_CS);
// ----- CONFIGURAÇÃO WIFI -----
const char* ssid = "...";
const char* password = "...";
const char* serverURL = "https://.../api/rua/sensor";
const char* serverBatchURL = "https://.../api/rua/sensor/batch";
// ----- SLEEP -----
const uint64_t SLEEP_MINUTES = 15;
const uint64_t SLEEP_MICROS = SLEEP_MINUTES * 60ULL * 1000000ULL;
void setup() {
// ------------------------------------------------
// STEP 1: POWER ON AND READ SENSOR (Wi-Fi still off!)
// ------------------------------------------------
pinMode(SENSOR_POWER_PIN, OUTPUT);
digitalWrite(SENSOR_POWER_PIN, HIGH);
delay(10);
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
if (!bme.begin(BME_CS)) {
goToSleep();
}
bme.setSampling(Adafruit_BME280::MODE_FORCED, ...);
bme.takeForcedMeasurement();
float temp = bme.readTemperature();
float hum = bme.readHumidity();
float pres = bme.readPressure() / 100.0F;
float volt = (analogReadMilliVolts(BATTERY_ADC_PIN) * 2) / 1000.0F;
// *** CRITICAL POWER SAVING ***
digitalWrite(SENSOR_POWER_PIN, LOW);
// ------------------------------------------------
// STEP 2: CONNECT WI-FI (DHCP, router-reserved IP)
// ------------------------------------------------
WiFi.mode(WIFI_STA);
WiFi.persistent(false); // avoid rewriting credentials to flash every cycle
WiFi.begin(ssid, password);
// ... timeout 10s ...
if (WiFi.status() == WL_CONNECTED) {
// ------------------------------------------------
// STEP 3: SEND DATA (HTTPS with WiFiClientSecure)
// ------------------------------------------------
// ... build JSON {temperature, humidity, pressure, voltage} ...
int response = http.POST(json);
if (response == 200) {
flushQueue(); // send any offline backlog accumulated in LittleFS
}
}
if (!sentLive) {
// Wi-Fi down or server error -> queue reading for later (see Offline Data Buffering)
time_t now = resolveCurrentEpoch(wifiOk, true); // NTP sync on demand
queueReading(now, temp, hum, pres, volt);
}
// ------------------------------------------------
// STEP 4: DEEP SLEEP
// ------------------------------------------------
goToSleep();
}
void goToSleep() {
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
esp_sleep_enable_timer_wakeup(SLEEP_MICROS);
esp_deep_sleep_start();
}
void loop() {
// Not needed — the ESP32 wakes from deep sleep by restarting setup()
}
Fig.3 - FireBeetle 2 ESP32-C6 Partial Code. Full source omitted for brevity; includes offline buffering, NTP synchronization, and battery voltage monitoring not shown in full here.
Python Services on Raspberry Pi
Flask API for data ingestion (/api/rua/sensor for real-time readings, /api/rua/sensor/batch for offline backlog — see Offline Data Buffering below);
SQLite database logger;
Data aggregation, statistics, pressure tendency analysis, and battery life forecasting.
Apache Reverse Proxy (electro6.conf)
Apache is configured as a reverse proxy to forward /api/rua requests
to the internal Flask. This covers all sub-routes under that prefix,
including the real-time ingestion endpoint (/api/rua/sensor), the batch endpoint
for offline backlog (/api/rua/sensor/batch), and the read-only data endpoints
consumed by the frontend. This isolates backend services and provides clean, stable URLs.
Offline Data Buffering (LittleFS Persistent Queue)
In the initial version of this project, any reading taken while the Raspberry Pi was unreachable — whether due to a server restart, a network outage, or scheduled maintenance — was silently lost. For an outdoor sensor that may need to operate autonomously for extended periods, this is unacceptable: the most interesting weather data often coincides with the conditions most likely to cause connectivity issues.
The firmware now implements a persistent offline queue using the ESP32's onboard flash memory via LittleFS. When a reading cannot be delivered (either because Wi-Fi is unavailable or the server does not respond with HTTP 200), it is appended to a CSV file stored on flash along with its estimated timestamp. The queue can hold up to 672 readings (~1 week) at the 15-minute interval. Once connectivity is restored and a live reading is successfully acknowledged by the server, the entire backlog is flushed in a single HTTP POST to a dedicated batch endpoint (/api/rua/sensor/batch), and the queue file is deleted only after the server confirms receipt. If the batch delivery itself fails, the file remains intact and retransmission is attempted on the next cycle — no data is ever partially sent or silently discarded.
Timestamping offline readings required solving a non-trivial problem: the ESP32 has no battery-backed real-time clock, so it only knows the current time when it can reach an NTP server. In earlier iterations, the firmware estimated timestamps by counting deep-sleep cycles and multiplying by the nominal 15-minute interval — but the ESP32-C6's default 150 kHz RC oscillator has a frequency tolerance of approximately ±5%, which translates to roughly 9 minutes of drift over 3 hours. The final implementation avoids this entirely: whenever a reading needs to be queued and Wi-Fi is available, the firmware performs an on-demand NTP synchronization at that exact moment, producing an accurate timestamp with no accumulated drift. The internal RTC clock (which does persist across deep-sleep resets) is used as a fallback only when Wi-Fi itself is unavailable — an uncommon scenario, since the most frequent failure mode is server downtime with Wi-Fi still operational.
On the backend, the Flask API exposes a /api/rua/sensor/batch endpoint that accepts an array of readings, each with its own device-side timestamp, and inserts them into the same SQLite table used by real-time readings. Because all existing queries (charts, statistics, pressure tendency, battery forecast) use ORDER BY timestamp rather than insertion order, the late-arriving historical data integrates seamlessly — no frontend changes were required.
// Offline queue — partial firmware excerpt (simplified)
#define QUEUE_FILE "/queue.csv"
const int MAX_QUEUE_ENTRIES = 672; // ~1 week at 15 min intervals
// Called when the live POST fails (Wi-Fi down or server error)
void queueReading(time_t epoch, float temp, float hum, float pres, float volt) {
if (epoch == 0) return; // no valid time reference yet
File f = LittleFS.open(QUEUE_FILE, "a");
f.printf("%lu,%.2f,%.2f,%.2f,%.3f\n", (unsigned long)epoch, temp, hum, pres, volt);
f.close();
}
// Called after a successful live POST — sends the entire backlog at once
void flushQueue() {
if (!LittleFS.exists(QUEUE_FILE)) return;
// ... reads all lines, builds a JSON array, POSTs to /api/rua/sensor/batch ...
// ... deletes the file ONLY after HTTP 200 confirmation ...
}
Fig.3b - Offline queue — partial firmware excerpt (simplified). Full source omitted for brevity.
Power Consumption Analysis
Let's be conservative with these calculations (meaning I will assume the reasonable "worst-case" scenario).
Your Hardware Data (Datasheets)
Battery: 3x NiMH 2500mAh batteries.
Theoretical Total Capacity: 2500 mAh.
Note: Since the batteries do not discharge to 0V (the ESP32's onboard regulator needs at least ~3.3V to reliably power the chip), we only utilize a fraction of the nominal charge. We will use 2125 useful mAh as a conservative estimate.
ESP32-C6 Consumption (FireBeetle 2):
Wi-Fi On (TX/RX): Average of 180 mA (peaks of 350mA, but the average is lower).
Awake without Wi-Fi (Measuring sensor): ~30 mA.
Deep Sleep: The datasheet says 13 µA, but let's be realistic and assume 25 µA (0.025 mA) to account for small circuit leakages.
BME280 Sensor Consumption:
Measuring: 1 mA (irrelevant as it is very fast).
Sleeping (via GPIO): 0 mA (cut the power at GPIO14).
15-Minute Cycle (900 Seconds)
Let's calculate the "Energy Cost" of each 15-minute cycle. We will use the unit mAs (milliAmpere-seconds), which is the "currency" of energy spent.
With static IP, the connection is fast. Let's assume 2.5 seconds in total to connect, send and receive the "OK".
Time: 2.5 seconds.
Consumption: 180 mA.
Cost: \(180 \text{ mA} \times 2.5 \text{ s} = \mathbf{450 \text{ mAs}}\).
Phase 3: Deep Sleep
The rest of the time until completing the 15 minutes.
Time: \(900 \text{ s} - 2.7 \text{ s} = 897.3 \text{ seconds}\).
Consumption: 0.025 mA (25 µA).
Cost: \(0.025 \text{ mA} \times 897.3 \text{ s} = \mathbf{22.4 \text{ mAs}}\).
Total per Cycle and Duration
Total Cost of 1 cycle (15 min):
\(6 + 450 + 22.4 = \mathbf{478.4 \text{ mAs}}\)
Average Consumption per Second (Average Current):
To know the equivalent continuous average current:
\(\text{Days of life} = \frac{4009}{24} \approx \mathbf{167 \text{ days}}\)
Theoretical Result: ~5 and a half months.
The "Reality Factor" (Important to read)
In theory, it gives 167 days. In practice, there are two invisible enemies:
NiMH Battery Self-discharge: Rechargeable batteries lose charge on their own, even sitting in a drawer. Normal batteries lose 10-20% of their charge per month. If they are "Ready to Use" batteries (like Eneloop or IKEA Ladda), they lose much less.
Regulator Efficiency: The FireBeetle has components that convert the battery voltage to 3.3V. This process is not 100% efficient.
My Estimate:
Applying a safety margin of 30% for self-discharge and inefficiencies (coeficient of security: 100% - 30% = 70%):
⚠️ Important caveat: this theoretical figure is a calculation from datasheets, not a direct measurement. NiMH batteries have a discharge curve that stays almost flat for most of their life and only drops sharply near the very end (the "knee" of the curve). This means voltage alone, observed over a few weeks or months, cannot confirm or contradict this estimate — the battery can look perfectly healthy for a long time and then decline quickly once it reaches that knee. See the Empirical Battery Life Validation section below, which measures the actual voltage decay rate straight from the live sensor data and cross-checks it against this theoretical model.
Conclusion
Something between 3 to 4 guaranteed months (it could reach 5 months if the batteries are of good quality).
This is the massive difference of:
Using DHCP with a router-side IP reservation (avoids hardcoding gateway/DNS while keeping a fixed IP).
Killing the sensor via GPIO (reduces consumption in sleep).
Using Deep Sleep correctly.
Calling WiFi.persistent(false) to prevent the ESP32 from rewriting Wi-Fi credentials to flash on every cycle (reduces write latency and flash wear).
Empirical Battery Life Validation
The calculation above is a theoretical model built from component datasheets. It is useful for a first estimate, but it can't be verified just by "waiting and watching the voltage" — NiMH batteries have a discharge curve that stays almost flat for most of their life and only drops sharply right at the end:
In practice this means: a battery can show a stable, healthy-looking voltage for months and still be perfectly on track for a ~4 month lifespan — it simply hasn't reached the knee yet. The only reliable way to validate the theoretical model is to measure the actual rate of voltage decay directly from the sensor's own historical data, and project it forward.
Methodology: Linear Regression on Real Voltage Data
Every 15 minutes, the ESP32 reports its battery voltage alongside the weather readings. Using that history, this page runs the following steps live, every time it loads:
Detect the last battery replacement — scan the voltage history for a jump upward of more than 0.08V between two consecutive readings (this only happens when batteries are physically swapped, never during normal discharge). Everything before that point is discarded.
Use a recent window only — the last 45 days of data since that replacement (not the full history), because the discharge rate isn't constant over the battery's life; we want the current rate, not a stale average.
Fit a straight line through those (time, voltage) points using least-squares linear regression:
t: days elapsed since the start of the data window
a: the fitted voltage at t = 0 (start of the window)
b: the slope — volts lost per day. This is the key number: the current, measured discharge rate.
t̄, V̄: the average time and average voltage across all points in the window
Once a and b are known, projecting to the real cutoff voltage (3.3V — the minimum the FireBeetle's onboard regulator needs to power the ESP32 reliably) is simple algebra:
\(t_{\text{cutoff}} = \dfrac{3.3 - a}{b}\)
Subtracting the days already elapsed gives the days remaining from today.
One safeguard: if the measured slope is smaller than 0.05 mV/day (essentially flat), no date is projected — extrapolating a near-flat line gives numbers in the hundreds or thousands of days, which (per the plateau/knee explanation above) simply isn't trustworthy. In that case the page reports "stable" instead of a fake precise date.
Live Result (calculated from the current database)
Current voltage—
Measured decay rate—
Data window since—
Sample size—
A carregar dados em tempo real da base de dados...
Theoretical vs. Empirical — Side by Side
Method
Basis
Result
Reliability
Theoretical (datasheets)
Component current draw × phase durations, from the calculation above
~116–167 days
Good first estimate; assumes phase timings and currents match datasheet conditions exactly
Empirical (measured)
Linear regression on the battery's actual live voltage history
—
Reflects real-world conditions, but only trustworthy once the discharge curve leaves the flat plateau
These two numbers are expected to disagree while the battery is still in its early/mid life (the empirical model will look overly optimistic, for the plateau reasons explained above). They should converge as the battery approaches its real end of life — which is exactly the point of tracking both.
But why using 3 AA Batteries Instead of 4 Batteries AA?
Using 3 AA alkaline batteries provide an initial voltage of approximately 4.5 V,
which is safely handled by the ESP32 onboard regulator.
Using 4 batteries would raise the voltage to 6V, exceeding the regulator's optimal input range and increasing thermal dissipation.
SPI Was Mandatory Instead of I²C
During development, the use of I²C combined with active Wi-Fi on the ESP32 caused
intermittent communication failures and bus lockups.
Migrating the BME280 to SPI provided a dedicated, synchronous communication channel, fully eliminating the instability.
Outdoor Weather-Resistant Enclosure For Testing
I bought a waterproof box and made some minor modifications to adapt it to this project.
It has two large holes in the bottom for ventilation, and right above them is the BME280 sensor.
The goal is to prevent water from entering, especially since it's windy and raining heavily here (which is quite common), but at the same time, to avoid interfering with the sensor readings.
I also put a very fine green mesh to prevent insects from getting in and covering the sensor hole.
Now it will be screwed to the outside wall of the house.
P.S. - It will not be exposed directly to sun/rain.
This is the final result:
Fig.4 - Outdoor weather-resistant enclosure I.
Fig.5 - Outdoor weather-resistant enclosure II.
Fig.6 - Outdoor weather-resistant enclosure III.
Fig.7 - Outdoor weather-resistant enclosure IV.
Fig.8 - Outdoor weather-resistant enclosure on the wall below the balcony.
Temperature, Humidity and Atmospheric Pressure Sensor (BME280) - Outdoor [RUA]
Project Overview
The Outdoor Environmental Sensor Project is a low-power autonomous system designed to monitor temperature, relative humidity, and atmospheric pressure in an outdoor environment. The system prioritizes energy efficiency, reliability, and long-term autonomy, making it suitable for continuous operation using standard AA batteries.
An ESP32 microcontroller collects data from a BME280 sensor, transmits the measurements via Wi-Fi to a Raspberry Pi, and immediately enters deep sleep mode to minimize power consumption. The Raspberry Pi stores, processes, and exposes the data through a web-based interface. You can see here: https://joelbonifacio.dynip.sapo.pt/electro6.1.html
Hardware Components
Fig.1 - Materials: ESP32, BME280, batteries, support for the batteries and cables.
BME280 Technical Specifications
ESP32 ↔ BME280 Wiring Diagram (SPI)
Here is the schematic of the circuit.
Fig.2 - Schematic of the circuit.
FireBeetle 2 ESP32-C6 Firmware (Low Power Mode + SPI)
Fig.3 - FireBeetle 2 ESP32-C6 Partial Code. Full source omitted for brevity; includes offline buffering, NTP synchronization, and battery voltage monitoring not shown in full here.
Python Services on Raspberry Pi
/api/rua/sensorfor real-time readings,/api/rua/sensor/batchfor offline backlog — see Offline Data Buffering below);Apache Reverse Proxy (electro6.conf)
Apache is configured as a reverse proxy to forward
/api/ruarequests to the internal Flask. This covers all sub-routes under that prefix, including the real-time ingestion endpoint (/api/rua/sensor), the batch endpoint for offline backlog (/api/rua/sensor/batch), and the read-only data endpoints consumed by the frontend. This isolates backend services and provides clean, stable URLs.Offline Data Buffering (LittleFS Persistent Queue)
In the initial version of this project, any reading taken while the Raspberry Pi was unreachable — whether due to a server restart, a network outage, or scheduled maintenance — was silently lost. For an outdoor sensor that may need to operate autonomously for extended periods, this is unacceptable: the most interesting weather data often coincides with the conditions most likely to cause connectivity issues.
The firmware now implements a persistent offline queue using the ESP32's onboard flash memory via LittleFS. When a reading cannot be delivered (either because Wi-Fi is unavailable or the server does not respond with HTTP 200), it is appended to a CSV file stored on flash along with its estimated timestamp. The queue can hold up to 672 readings (~1 week) at the 15-minute interval. Once connectivity is restored and a live reading is successfully acknowledged by the server, the entire backlog is flushed in a single HTTP POST to a dedicated batch endpoint (
/api/rua/sensor/batch), and the queue file is deleted only after the server confirms receipt. If the batch delivery itself fails, the file remains intact and retransmission is attempted on the next cycle — no data is ever partially sent or silently discarded.Timestamping offline readings required solving a non-trivial problem: the ESP32 has no battery-backed real-time clock, so it only knows the current time when it can reach an NTP server. In earlier iterations, the firmware estimated timestamps by counting deep-sleep cycles and multiplying by the nominal 15-minute interval — but the ESP32-C6's default 150 kHz RC oscillator has a frequency tolerance of approximately ±5%, which translates to roughly 9 minutes of drift over 3 hours. The final implementation avoids this entirely: whenever a reading needs to be queued and Wi-Fi is available, the firmware performs an on-demand NTP synchronization at that exact moment, producing an accurate timestamp with no accumulated drift. The internal RTC clock (which does persist across deep-sleep resets) is used as a fallback only when Wi-Fi itself is unavailable — an uncommon scenario, since the most frequent failure mode is server downtime with Wi-Fi still operational.
On the backend, the Flask API exposes a
/api/rua/sensor/batchendpoint that accepts an array of readings, each with its own device-side timestamp, and inserts them into the same SQLite table used by real-time readings. Because all existing queries (charts, statistics, pressure tendency, battery forecast) useORDER BY timestamprather than insertion order, the late-arriving historical data integrates seamlessly — no frontend changes were required.Fig.3b - Offline queue — partial firmware excerpt (simplified). Full source omitted for brevity.
Power Consumption Analysis
Let's be conservative with these calculations (meaning I will assume the reasonable "worst-case" scenario).
Your Hardware Data (Datasheets)
ESP32-C6 Consumption (FireBeetle 2):
BME280 Sensor Consumption:
15-Minute Cycle (900 Seconds)
Let's calculate the "Energy Cost" of each 15-minute cycle. We will use the unit mAs (milliAmpere-seconds), which is the "currency" of energy spent.
Time: 0.2 seconds (it's very fast).
Consumption: 30 mA.
Cost: \(30 \text{ mA} \times 0.2 \text{ s} = \mathbf{6 \text{ mAs}}\).
With static IP, the connection is fast. Let's assume 2.5 seconds in total to connect, send and receive the "OK".
Time: 2.5 seconds.
Consumption: 180 mA.
Cost: \(180 \text{ mA} \times 2.5 \text{ s} = \mathbf{450 \text{ mAs}}\).
The rest of the time until completing the 15 minutes.
Time: \(900 \text{ s} - 2.7 \text{ s} = 897.3 \text{ seconds}\).
Consumption: 0.025 mA (25 µA).
Cost: \(0.025 \text{ mA} \times 897.3 \text{ s} = \mathbf{22.4 \text{ mAs}}\).
Total per Cycle and Duration
Total Cost of 1 cycle (15 min):
Average Consumption per Second (Average Current):
To know the equivalent continuous average current:
This means that, in practice, the system behaves as if it were consuming only 0.53 mA constantly.
Final Calculation: How Long Does It Last?
We have 2125 mAh (useful capacity). The system consumes 0.53 mA.
The "Reality Factor" (Important to read)
In theory, it gives 167 days. In practice, there are two invisible enemies:
My Estimate:
Applying a safety margin of 30% for self-discharge and inefficiencies (coeficient of security: 100% - 30% = 70%):
⚠️ Important caveat: this theoretical figure is a calculation from datasheets, not a direct measurement. NiMH batteries have a discharge curve that stays almost flat for most of their life and only drops sharply near the very end (the "knee" of the curve). This means voltage alone, observed over a few weeks or months, cannot confirm or contradict this estimate — the battery can look perfectly healthy for a long time and then decline quickly once it reaches that knee. See the Empirical Battery Life Validation section below, which measures the actual voltage decay rate straight from the live sensor data and cross-checks it against this theoretical model.
Conclusion
Something between 3 to 4 guaranteed months (it could reach 5 months if the batteries are of good quality).
This is the massive difference of:
WiFi.persistent(false)to prevent the ESP32 from rewriting Wi-Fi credentials to flash on every cycle (reduces write latency and flash wear).Empirical Battery Life Validation
The calculation above is a theoretical model built from component datasheets. It is useful for a first estimate, but it can't be verified just by "waiting and watching the voltage" — NiMH batteries have a discharge curve that stays almost flat for most of their life and only drops sharply right at the end:
In practice this means: a battery can show a stable, healthy-looking voltage for months and still be perfectly on track for a ~4 month lifespan — it simply hasn't reached the knee yet. The only reliable way to validate the theoretical model is to measure the actual rate of voltage decay directly from the sensor's own historical data, and project it forward.
Methodology: Linear Regression on Real Voltage Data
Every 15 minutes, the ESP32 reports its battery voltage alongside the weather readings. Using that history, this page runs the following steps live, every time it loads:
Once a and b are known, projecting to the real cutoff voltage (3.3V — the minimum the FireBeetle's onboard regulator needs to power the ESP32 reliably) is simple algebra:
Subtracting the days already elapsed gives the days remaining from today.
One safeguard: if the measured slope is smaller than 0.05 mV/day (essentially flat), no date is projected — extrapolating a near-flat line gives numbers in the hundreds or thousands of days, which (per the plateau/knee explanation above) simply isn't trustworthy. In that case the page reports "stable" instead of a fake precise date.
Live Result (calculated from the current database)
Theoretical vs. Empirical — Side by Side
These two numbers are expected to disagree while the battery is still in its early/mid life (the empirical model will look overly optimistic, for the plateau reasons explained above). They should converge as the battery approaches its real end of life — which is exactly the point of tracking both.
But why using 3 AA Batteries Instead of 4 Batteries AA?
Using 3 AA alkaline batteries provide an initial voltage of approximately 4.5 V, which is safely handled by the ESP32 onboard regulator. Using 4 batteries would raise the voltage to 6V, exceeding the regulator's optimal input range and increasing thermal dissipation.
SPI Was Mandatory Instead of I²C
During development, the use of I²C combined with active Wi-Fi on the ESP32 caused intermittent communication failures and bus lockups. Migrating the BME280 to SPI provided a dedicated, synchronous communication channel, fully eliminating the instability.
Outdoor Weather-Resistant Enclosure For Testing
I bought a waterproof box and made some minor modifications to adapt it to this project. It has two large holes in the bottom for ventilation, and right above them is the BME280 sensor. The goal is to prevent water from entering, especially since it's windy and raining heavily here (which is quite common), but at the same time, to avoid interfering with the sensor readings. I also put a very fine green mesh to prevent insects from getting in and covering the sensor hole. Now it will be screwed to the outside wall of the house.
P.S. - It will not be exposed directly to sun/rain.
This is the final result:
Fig.4 - Outdoor weather-resistant enclosure I.
Fig.5 - Outdoor weather-resistant enclosure II.
Fig.6 - Outdoor weather-resistant enclosure III.
Fig.7 - Outdoor weather-resistant enclosure IV.
Fig.8 - Outdoor weather-resistant enclosure on the wall below the balcony.
Vented Radiation Shield Implementation
You can see more in the Solidworks section.
Leave A Comment