diff --git a/README.md b/README.md index 9494826..380545a 100644 --- a/README.md +++ b/README.md @@ -1,114 +1,9 @@ -# Weather Predictor +# Arduino -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. +A collection of Arduino / ESP microcontroller projects. -## Features +## Projects -- **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) - -1. Install the **ESP8266** board package; select board **"LOLIN(WEMOS) D1 R2 & mini"**. -2. Set **Tools → Flash Size** to a layout with a filesystem, e.g. **4MB (FS:2MB)**. -3. 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`, `time` ship with the core. -4. Open `Arduino/WeatherPredictor/WeatherPredictor.ino` and upload. - -## First run - -1. 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. -2. After connecting, the device syncs time over NTP and writes it to the DS3231. -3. The screen and Serial (115200) print the station's IP — open `http:///`. -4. 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 - -``` -Arduino/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 the `invertDisplay` / `setRotation` in `displayBegin()`). -- **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](https://github.com/G6EJD/ESP32_Weather_Forecaster_TN061). +- **[WeatherPredictor](WeatherPredictor/)** — autonomous barometric weather + station on a Wemos D1 mini (ESP8266): local Zambretti forecast, TFT display + and a self-hosted web interface. See its [README](WeatherPredictor/README.md). diff --git a/WeatherPredictor/README.md b/WeatherPredictor/README.md new file mode 100644 index 0000000..5a8ecfe --- /dev/null +++ b/WeatherPredictor/README.md @@ -0,0 +1,114 @@ +# 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) + +1. Install the **ESP8266** board package; select board **"LOLIN(WEMOS) D1 R2 & mini"**. +2. Set **Tools → Flash Size** to a layout with a filesystem, e.g. **4MB (FS:2MB)**. +3. 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`, `time` ship with the core. +4. Open `WeatherPredictor/WeatherPredictor.ino` and upload. + +## First run + +1. 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. +2. After connecting, the device syncs time over NTP and writes it to the DS3231. +3. The screen and Serial (115200) print the station's IP — open `http:///`. +4. 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 the `invertDisplay` / `setRotation` in `displayBegin()`). +- **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](https://github.com/G6EJD/ESP32_Weather_Forecaster_TN061). diff --git a/Arduino/WeatherPredictor/WeatherPredictor.ino b/WeatherPredictor/WeatherPredictor.ino similarity index 100% rename from Arduino/WeatherPredictor/WeatherPredictor.ino rename to WeatherPredictor/WeatherPredictor.ino diff --git a/Arduino/WeatherPredictor/app_settings.cpp b/WeatherPredictor/app_settings.cpp similarity index 100% rename from Arduino/WeatherPredictor/app_settings.cpp rename to WeatherPredictor/app_settings.cpp diff --git a/Arduino/WeatherPredictor/app_settings.h b/WeatherPredictor/app_settings.h similarity index 100% rename from Arduino/WeatherPredictor/app_settings.h rename to WeatherPredictor/app_settings.h diff --git a/Arduino/WeatherPredictor/config.h b/WeatherPredictor/config.h similarity index 100% rename from Arduino/WeatherPredictor/config.h rename to WeatherPredictor/config.h diff --git a/Arduino/WeatherPredictor/display_ui.cpp b/WeatherPredictor/display_ui.cpp similarity index 100% rename from Arduino/WeatherPredictor/display_ui.cpp rename to WeatherPredictor/display_ui.cpp diff --git a/Arduino/WeatherPredictor/display_ui.h b/WeatherPredictor/display_ui.h similarity index 100% rename from Arduino/WeatherPredictor/display_ui.h rename to WeatherPredictor/display_ui.h diff --git a/Arduino/WeatherPredictor/docs/superpowers/plans/2026-07-17-weather-predictor.md b/WeatherPredictor/docs/superpowers/plans/2026-07-17-weather-predictor.md similarity index 100% rename from Arduino/WeatherPredictor/docs/superpowers/plans/2026-07-17-weather-predictor.md rename to WeatherPredictor/docs/superpowers/plans/2026-07-17-weather-predictor.md diff --git a/Arduino/WeatherPredictor/docs/superpowers/specs/2026-07-17-weather-predictor-design.md b/WeatherPredictor/docs/superpowers/specs/2026-07-17-weather-predictor-design.md similarity index 100% rename from Arduino/WeatherPredictor/docs/superpowers/specs/2026-07-17-weather-predictor-design.md rename to WeatherPredictor/docs/superpowers/specs/2026-07-17-weather-predictor-design.md diff --git a/Arduino/WeatherPredictor/flog.cpp b/WeatherPredictor/flog.cpp similarity index 100% rename from Arduino/WeatherPredictor/flog.cpp rename to WeatherPredictor/flog.cpp diff --git a/Arduino/WeatherPredictor/flog.h b/WeatherPredictor/flog.h similarity index 100% rename from Arduino/WeatherPredictor/flog.h rename to WeatherPredictor/flog.h diff --git a/Arduino/WeatherPredictor/forecast.cpp b/WeatherPredictor/forecast.cpp similarity index 100% rename from Arduino/WeatherPredictor/forecast.cpp rename to WeatherPredictor/forecast.cpp diff --git a/Arduino/WeatherPredictor/forecast.h b/WeatherPredictor/forecast.h similarity index 100% rename from Arduino/WeatherPredictor/forecast.h rename to WeatherPredictor/forecast.h diff --git a/Arduino/WeatherPredictor/history.cpp b/WeatherPredictor/history.cpp similarity index 100% rename from Arduino/WeatherPredictor/history.cpp rename to WeatherPredictor/history.cpp diff --git a/Arduino/WeatherPredictor/history.h b/WeatherPredictor/history.h similarity index 100% rename from Arduino/WeatherPredictor/history.h rename to WeatherPredictor/history.h diff --git a/Arduino/WeatherPredictor/net.cpp b/WeatherPredictor/net.cpp similarity index 100% rename from Arduino/WeatherPredictor/net.cpp rename to WeatherPredictor/net.cpp diff --git a/Arduino/WeatherPredictor/net.h b/WeatherPredictor/net.h similarity index 100% rename from Arduino/WeatherPredictor/net.h rename to WeatherPredictor/net.h diff --git a/Arduino/WeatherPredictor/rtc_time.cpp b/WeatherPredictor/rtc_time.cpp similarity index 100% rename from Arduino/WeatherPredictor/rtc_time.cpp rename to WeatherPredictor/rtc_time.cpp diff --git a/Arduino/WeatherPredictor/rtc_time.h b/WeatherPredictor/rtc_time.h similarity index 100% rename from Arduino/WeatherPredictor/rtc_time.h rename to WeatherPredictor/rtc_time.h diff --git a/Arduino/WeatherPredictor/sensors.cpp b/WeatherPredictor/sensors.cpp similarity index 100% rename from Arduino/WeatherPredictor/sensors.cpp rename to WeatherPredictor/sensors.cpp diff --git a/Arduino/WeatherPredictor/sensors.h b/WeatherPredictor/sensors.h similarity index 100% rename from Arduino/WeatherPredictor/sensors.h rename to WeatherPredictor/sensors.h diff --git a/Arduino/WeatherPredictor/state.h b/WeatherPredictor/state.h similarity index 100% rename from Arduino/WeatherPredictor/state.h rename to WeatherPredictor/state.h diff --git a/Arduino/WeatherPredictor/web_page.h b/WeatherPredictor/web_page.h similarity index 100% rename from Arduino/WeatherPredictor/web_page.h rename to WeatherPredictor/web_page.h diff --git a/Arduino/WeatherPredictor/web_server.cpp b/WeatherPredictor/web_server.cpp similarity index 100% rename from Arduino/WeatherPredictor/web_server.cpp rename to WeatherPredictor/web_server.cpp diff --git a/Arduino/WeatherPredictor/web_server.h b/WeatherPredictor/web_server.h similarity index 100% rename from Arduino/WeatherPredictor/web_server.h rename to WeatherPredictor/web_server.h