5.2 KiB
Weather Predictor
An autonomous barometric weather station built on a Wemos D1 mini (ESP8266). It reads atmospheric pressure and temperature, keeps accurate battery-backed time, and predicts near-term local weather entirely on-device using the Zambretti algorithm — no internet forecast service required. Readings and the forecast are shown on a small TFT and on a self-hosted web interface styled as a barometer instrument.
Features
- Local Zambretti forecast from the 3-hour sea-level pressure trend (rising / steady / falling), adjusted for season.
- On-device TFT screen (160×80): time, date, pressure with trend arrow, temperature, a scaled weather icon, and the station's Wi-Fi/IP.
- Web interface served from the device (no external CDNs, works offline):
- live barometer dial with a needle and a "set hand" ghost at the pressure 3 h ago;
- barograph of pressure & temperature with labelled axes;
- forecast change log;
- settings with city presets and a UTC-offset time-zone picker.
- Persistence in LittleFS: settings, pressure history, and the forecast log all survive reboots.
- Wi-Fi provisioning via WiFiManager (captive portal — no credentials in code) and NTP time sync to the DS3231.
Hardware
| Component | Part | Bus / notes |
|---|---|---|
| MCU | Wemos D1 mini (ESP8266, 4 MB flash) | — |
| Pressure & temperature | BMP180 (GY-68) | I²C, addr 0x77 |
| Real-time clock | DS3231 (HW-022, battery-backed) | I²C, addr 0x68 |
| Display | 0.96" 80×160 TFT, ST7735S | 4-wire SPI, run landscape 160×80 |
Wiring
The display uses hardware SPI; the two I²C modules share one bus.
| Signal | Wemos pin | GPIO |
|---|---|---|
| TFT CS | D8 | 15 |
| TFT DC | D3 | 0 |
| TFT RST | D4 | 2 |
| TFT SDA (MOSI) | D7 | 13 |
| TFT SCL (SCK) | D5 | 14 |
| TFT BLK (backlight) | D2 | 4 |
| I²C SDA (BMP180 + DS3231) | D6 | 12 |
| I²C SCL (BMP180 + DS3231) | D1 | 5 |
All modules run on 3.3 V / GND. I²C is started with Wire.begin(D6 /*SDA*/, D1 /*SCL*/).
Build & flash (Arduino IDE)
- Install the ESP8266 board package; select board "LOLIN(WEMOS) D1 R2 & mini".
- Set Tools → Flash Size to a layout with a filesystem, e.g. 4MB (FS:2MB).
- Install libraries via Library Manager:
Adafruit GFX, Adafruit ST7735 and ST7789, Adafruit BMP085, RTClib,
ArduinoJson (v7.x), WiFiManager (by tzapu).
ESP8266WiFi,ESP8266WebServer,LittleFS,Wire,SPI,timeship with the core. - Open
WeatherPredictor/WeatherPredictor.inoand upload.
First run
- On first boot the device opens a Wi-Fi access point
WeatherPredictor-Setup. Join it from a phone and pick your network in the captive portal. - After connecting, the device syncs time over NTP and writes it to the DS3231.
- The screen and Serial (115200) print the station's IP — open
http://<ip>/. - The forecast shows "Collecting data…" until ~3 h of pressure history exists; that is expected — the Zambretti method needs a trend before its first call.
Set your altitude in the web settings (or pick a city preset): sea-level pressure — and therefore the forecast — depends on it (~0.12 hPa per metre).
REST API
| Endpoint | Method | Returns |
|---|---|---|
/ |
GET | Web UI |
/api/current |
GET | time, pressure (abs + sea level), temperature, trend, forecast |
/api/history |
GET | recent pressure/temperature samples |
/api/forecasts |
GET | forecast change log (newest first) |
/api/settings |
GET / POST | altitude, time-zone offset, coordinates |
Project structure
WeatherPredictor/
WeatherPredictor.ino scheduler; wires the modules together
config.h pins, addresses, intervals, defaults
state.h shared current-readings struct
sensors.* BMP180 read + sea-level conversion
rtc_time.* DS3231 read/set + NTP sync
display_ui.* ST7735 rendering + weather icons
forecast.* Zambretti forecast + trend classification
history.* pressure ring buffer + LittleFS persistence
flog.* forecast change log
app_settings.* settings struct + LittleFS JSON
net.* WiFiManager connection
web_server.* HTTP server + REST API
web_page.h web UI (HTML/CSS/JS) in PROGMEM
docs/superpowers/ design spec and implementation plan
Notes & gotchas
- BGR panel: this 0.96" ST7735S has red/blue swapped, so colours are built
R/B-exchanged in
display_ui.cpp. If your panel looks wrong, adjust there (and theinvertDisplay/setRotationindisplayBegin()). - Restrictive networks: on some LANs the router does not resolve external hostnames, so NTP uses hard-coded Cloudflare/Google anycast IPs instead of names. Such networks may also block SSH.
- Flash wear: history is flushed every 15 min and the forecast log only on change, so LittleFS wear leveling keeps flash life at years/decades.
Credits
Zambretti forecast constants and lookup tables adapted from G6EJD's ESP Zambretti forecaster.
