A high-contrast, low-power weather dashboard built on the LilyGo T5-4.7 S3 E-Paper display. This project fetches real-time weather data and forecasts via MQTT, displays them using a custom 16-level grayscale UI, and enters deep sleep for extended battery life.
- Hardware: LilyGo T5-4.7 inch E-Paper Display (ESP32-S3, 960x540px)
- Deep Sleep Architecture:
- Active time: ~5 seconds per update
- Sleep current: ~20Β΅A
- Battery Life: Pretty long
- Smart Forecast Window:
- Morning (< 6:00): Shows today's 6:00, 14:00 & tomorrow's forecasts
- Day (6:00-14:00): Shows today's 14:00 & full tomorrow
- Evening (> 14:00): Shows full tomorrow & day after tomorrow
- Custom Icons: 16-level grayscale weather icons optimized for E-Ink
- Weather Data: Powered by BrightSky API (DWD Open Data)
This is a two-part system:
βββββββββββββββββββ ββββββββββββββββ βββββββββββββββββββ
β BrightSky API ββββββ>β Python Scriptββββββ>β MQTT Broker β
β (DWD Data) β JSON β(Home Server) β JSON β (Mosquitto) β
βββββββββββββββββββ ββββββββββββββββ ββββββββββ¬βββββββββ
β
β Subscribe
β
ββββββββββββββββββ
β LilyGo T5-4.7 β
β ESP32-S3 β
β E-Paper β
ββββββββββββββββββ
- Python 3.x
- Libraries:
paho-mqtt,requests - A 24/7 device (Raspberry Pi, NAS, home server)
Your Python script must publish a retained message to topic home/weather/display:
{
"update_ts": "2026-02-06T08:00:00",
"current": {
"ts": "2026-02-06T08:00:00+01:00",
"temp": 4.5,
"humi": 80,
"wind": 15.0,
"wind60": 14.2,
"rain": 0.0,
"rain_probability": 0,
"cloudco": 20,
"dew_point": 2.0,
"icon": "partly-cloudy-day"
},
"forecast": [
{
"ts": "2026-02-06T08:00:00+01:00",
"temp": 4.5,
"humi": 80,
"wind": 15.0,
"wind60": 14.2,
"rain": 0.0,
"rain_probability": 0,
"cloudco": 20,
"dew_point": 2.0,
"icon": "partly-cloudy-day"
}
]
}Key Points:
- Use ISO 8601 timestamps
- Include at least 6 forecast items (mix of 6:00 and 14:00 preferred)
- Set retain flag to
trueon MQTT publish
This project uses the BrightSky API (free, open-source, DWD weather data for Germany):
import requests
# Example: Fetch weather for Munich
url = "https://api.brightsky.dev/weather"
params = {
"lat": 48.1351,
"lon": 11.5820,
"date": "2026-02-06"
}
response = requests.get(url, params=params)
data = response.json()Documentation: https://brightsky.dev/docs/
- Use cron to run your script every 60 minutes:
*/60 * * * * /usr/bin/python3 /path/to/main.py- LilyGo T5-4.7 S3 (E-Paper V2.3+)
- LiPo Battery (3.7V, PH 2.0 connector)
- USB-C Cable
git clone https://github.com/christianhermann/weatherDisplay.git
cd weatherDisplayInstall the PlatformIO extension for VS Code.
Create source/secrets.h:
#ifndef SECRETS_H
#define SECRETS_H
const char* WIFI_SSID = "YourWiFiName";
const char* WIFI_PASS = "YourPassword";
const char* MQTT_SERVER = "192.168.1.X";
const int MQTT_PORT = 1883;
const char* MQTT_USER = "mqtt_user";
const char* MQTT_PASS = "mqtt_password";
const char* MQTT_TOPIC = "home/weather/display";
#endif- Connect via USB
- If upload fails: Hold BOOT β Press RST β Release BOOT
- Click PlatformIO: Upload
- lewisxhe/SensorLib @ ^0.1.9
- lennarthennigs/Button2 @ 2.3.2
- Wire
- SPI
- https://github.com/Xinyuan-LilyGO/LilyGo-EPD47#esp32s3
- bblanchon/ArduinoJson
- knolleary/PubSubClient
Icons are stored as 4-bit grayscale bitmaps in weather_icons.h.
clear-day,clear-nightpartly-cloudy-day,partly-cloudy-nightcloudy,fog,windrain,sleet,snow,hail,thunderstorm
python3 /python/Icons/addPadding.py # Adds 15px padding
python3 /python/Icons/convertToGrayscale.py # converts to grayscale
python3 /python/Icons/createHeaders.py # Converts images to C header| Update Interval | Consumption/Day | Battery Life (2000mAh) |
|---|---|---|
| 30 minutes | ~8.4 mAh | ~7.5 months |
| 60 minutes | ~4.5 mAh | ~14.5 months |
Based on 5-second wake time, 20Β΅A sleep current
weatherDisplay/
β
βββ assets/ # Documentation & icon assets
β βββ preview.jpg # Screenshot of the display
β βββ icons/ # Weather icon source files
β βββ Grayscale/ # Processed 16-level grayscale versions
β
β
βββ Python/ # Backend scripts
β βββ Icons/ # Icon processing tools
β β βββ addPadding.py # Adds 15px padding to icons
β β βββ convertToGrayscale.py # Converts transparent PNGs to E-Ink format
β β βββ createHeaders.py # Generates C header files from images
β β
β βββ WeatherData/ # Weather fetching & MQTT publishing
β βββ main.py # Main entry point for weather fetch script
β βββ exportWeatherData.py # Fetches data from BrightSky API
β βββ importWeatherData.py # Publishes JSON to MQTT broker
β βββ .env # MQTT credentials
βββ LilyGo-EPD47/ # ESP32 firmware
βββ src/
βββ main.cpp # Entry point, setup, loop & deep sleep
βββ network_management.cpp # WiFi, MQTT subscribe & JSON parsing
βββ network_management.h # Weather data structs & prototypes
βββ ui_layout.cpp # E-Ink drawing functions
βββ weather_icons.h # Icon bitmaps
βββ icon_display.h # Icon lookup helpers
βββ firasans.h # FiraSans font
βββ opensans8b.h # OpenSans 8pt Bold font
βββ opensans10b.h # OpenSans 10pt Bold font
βββ digits_48pt.h # Large digit font for temperature
βββ secrets.h # WiFi/MQTT credentials
| Component | Purpose |
|---|---|
| Python/WeatherData/ | Backend scripts that fetch weather from BrightSky API and publish to MQTT |
| Python/Icons/ | Tools to process icon PNGs into E-Ink compatible C headers |
| LilyGo-EPD47/src/ | ESP32 firmware that subscribes to MQTT, renders UI, and sleeps |
| assets/icons/ | Source PNG files and processed grayscale versions |
- Icon Processing:
addPadding.pyβconvertToGrayscale.pyβcreateHeaders.pyβweather_icons.h - Data Pipeline:
exportWeatherData.pyβimportWeatherData.pyβ MQTT Broker β ESP32 - Display Update: ESP32 wakes β fetches MQTT β
ui_layout.cpprenders β deep sleep
MIT License - Free to use, modify, and distribute.
