Ramblings of an aging IT geek
← Ramblings of an aging IT geek
hardware

an esp32 weather station, warts and all

Building a deep-sleeping ESP32 weather station that reads temperature, humidity and pressure, and the battery and wifi quirks that make "mostly works" the honest verdict.

A soldering iron and microcontroller on a workbench

I built a weather station. It sits on a north-facing wall, reads temperature, humidity and pressure every few minutes, and posts the numbers to a little server in the house. It mostly works, and I want to be honest about the "mostly", because the gap between a thing that works on the bench and a thing that works on a wall for months is where all the actual engineering lives.

The brief was modest. Battery powered, no mains anywhere near it, readings often enough to be useful, and ideally I never have to touch it again. An ESP32 is the obvious brain: it has wifi, it is cheap, and crucially it can deep-sleep down to microamps between readings, which is the only way battery life gets measured in months rather than days.

the easy part

The sensing is genuinely easy now, which still slightly amazes me. A BME280 gives you temperature, humidity and barometric pressure over I2C from a single tiny board. Three wires plus power, an address, and a library that hands you back floats in real units. The whole sensor side took an evening, most of which was deciding where to physically mount the thing so the sun did not cook it and report 40°C in the shade.

Adafruit_BME280 bme;

void setup() {
  Wire.begin();
  bme.begin(0x76);
  float tempC = bme.readTemperature();
  float humidity = bme.readHumidity();
  float pressure = bme.readPressure() / 100.0F;  // hPa
  postReading(tempC, humidity, pressure);
  esp_deep_sleep(300 * 1000000ULL);  // 5 minutes
}

That is essentially the whole program. Wake, read, post, sleep. There is no loop() worth speaking of, because deep sleep on the ESP32 restarts from setup() every time it wakes. You learn to think of the device as stateless: it boots, does one job, and dies again, and anything it needs to remember between wakes either goes in RTC memory or off the device entirely.

A close-up of the circuit board and sensor

where "mostly" comes in

The first problem is wifi, and specifically how long it takes to associate. Sensing takes milliseconds. Joining the access point, getting a DHCP lease, and opening a connection can take several seconds, and during those seconds the ESP32 is drawing a couple of hundred milliamps with the radio lit up. That join time, not the reading, is what drains the battery. If the access point is slow to respond, or the signal is marginal on a north wall through a cavity, the device sits there burning current waiting to associate.

Two things helped. First, store the IP configuration statically in RTC memory and skip DHCP after the first boot, which shaves a meaningful chunk off the wake time. Second, give the whole network attempt a hard timeout. If it cannot post a reading within, say, ten seconds, it gives up and goes back to sleep rather than draining the battery flat waiting for a router that is not listening. A missed reading every so often is fine. A dead battery because one bad wifi join held the radio on for a minute is not.

Another close-up of the wiring and components

The second problem is the battery itself, in two ways. Reporting its own voltage is awkward because the ESP32's ADC is, to put it kindly, not laboratory grade; it is nonlinear near the rails and needs calibrating per board if you want a number you can trust. I settled for a rough percentage rather than pretending to millivolt accuracy. And the lithium cell's voltage sags under the load spike when the radio fires, so a naive reading taken mid-transmit reads low and panics you into thinking it is flat. I take the voltage reading before bringing wifi up, while the board is quiet, and it is far steadier for it.

the honest verdict

It has been on the wall for a few weeks. The numbers are good, the trends line up with the proper forecast, and the dashboard at home is genuinely useful for deciding whether to bother with a coat. The deep-sleep current is where I wanted it, and the battery is dropping slowly enough that I think months is realistic.

A word on the enclosure, because it nearly undid the whole thing. The first box I used was a plain sealed project case, which is excellent for keeping rain off the electronics and catastrophic for measuring humidity, because the air inside never exchanges with the air outside. The readings lagged the real weather by hours and the humidity sat stubbornly high. The fix is a radiation shield, the stack of stacked white plates you see on proper weather stations, which lets air move freely past the sensor while keeping direct sun and rain off it. A printed shield and the readings came alive immediately, tracking the real outside air within a minute or two rather than lagging it by half a day. I had spent the best part of an evening suspecting the sensor, swapping I2C wires and rereading the datasheet, when the problem was simply that I had sealed a humidity sensor inside an airtight box and asked it to measure the weather.

But every so often there is a gap in the data. A wake where the wifi did not come up in time, or a reading that did not post, and the device shrugged and went back to sleep as designed. I could chase those gaps. I could add a local buffer in RTC memory and replay missed readings on the next successful connection, which is the obviously correct thing to do and the next job on the list. For now the gaps are rare enough that I have left them, and "mostly works" is, I have decided, a perfectly respectable place for a homemade weather station to be.

The thing I keep coming back to is how the hard parts were never the parts I expected. The sensor was trivial. The maths was trivial. The entire difficulty was power and radios, the unglamorous reality that a battery-powered wireless thing lives or dies on how briefly it can be awake. Get that right and the rest is an evening's fun. Get it wrong and you have a beautiful little brick with a flat battery on a cold wall.