← All writing

Weather drawings on the EleksWFD clock

How current weather and daylight select nine animated scenes for a 7×7 LED grid, and how the firmware draws them a few pixels at a time.

The EleksWFD clock has a 7×7 grid of white LEDs. I used those 49 lights to draw weather scenes: a sun with turning rays, clouds moving across the panel, falling rain and snow, and a crescent moon with twinkling stars.

The drawings change with the weather at the configured location. Clear weather during the day selects the sun. The same conditions at night select the moon. Rain, fog, snow, and thunderstorms each have their own animation.

Nine animated weather scenes on simulated 7×7 LED grids: sunny, partly cloudy, cloudy, fog, rain, snow, storm, clear night, and partly cloudy night.

This software preview uses the project's saved animation test frames, slowed to five frames per second. It shows all nine scenes together; the clock displays one scene at a time. The storm scene includes flashes of the full grid.

Drawing with 49 lights

Each LED is either on or off. A frame is stored as seven bytes, one per row, with seven bits in each byte describing the lights across that row. There is enough room for a small recognizable shape and a little movement.

The art starts with reusable shapes. A cloud occupies three rows. The crescent moon uses the full seven-row height. The sun has a small center with separate rays. Drawing helpers place those shapes on the grid and light individual dots for rain, snow, and stars.

Those pieces can be combined. Partly cloudy weather draws a small sun toward the upper left and moves a cloud across the lower rows. Its night version keeps the moving cloud and uses a crescent moon and fewer stars.

The fixed grid also shapes the movement. The cloudy scene scrolls its artwork around an imaginary strip 14 columns wide, with only seven columns visible. Clouds leave one edge and return through the other as the strip advances.

Turning weather data into a scene

Both the firmware and the Rust companion app request current conditions from Open-Meteo. Alongside temperature, they request weather_code and is_day. The condition code describes the weather; the daylight flag determines whether a clear sky gets a sun or a moon.

The scene selector groups those inputs into a small set of drawings:

  • Clear or mainly clear conditions select Sunny during daylight and Clear night after dark.
  • Partly cloudy conditions select a sun and cloud during daylight, or a moon and cloud at night.
  • Overcast conditions select the moving cloud layers.
  • Fog selects horizontal bands moving in opposite directions.
  • Drizzle, rain, and rain showers select a cloud with falling drops.
  • Snow and snow showers select drifting flakes.
  • Thunderstorms select a cloud, lightning, rain, and flashes of the whole grid.

For example, weather code 73 selects snow. The animation engine then draws the snow scene for each successive frame. It needs the selected scene and a frame counter to produce that motion.

Night does not put a moon into every drawing. An overcast night still shows clouds; a rainy night still shows rain. The sun and moon variants belong to the clear and partly cloudy scenes.

Weather updates and animation frames

Weather data and animation run at different speeds. In standalone operation, the firmware attempts a weather fetch every 15 minutes after time synchronization. Between those requests, it continues animating the selected scene at the configured frame rate.

The reaction is based on the condition category and daylight flag. Animation speed comes from the grid's frame-rate setting. A change to snow selects the snow drawing; individual snowflakes continue moving locally between weather updates.

Each scene uses the frame counter differently. Rain drops have staggered starting positions and advance through the four rows beneath a cloud. Snow moves every other frame and shifts sideways as it falls. Fog slides alternate bands in opposing directions. The sun changes which rays are lit, while stars blink on a repeating schedule around the moon.

The storm runs through a repeating sequence of full-grid flashes, a lightning bolt, and rain. These are generated drawings: the renderer calculates each frame from the scene and its position in the animation.

Sharing the drawings between the app and clock

The ESP32 firmware implements the animation engine in C++. The Windows companion app implements the same weather scenes in Rust. When the app drives the display, it generates frames on the PC and sends them over USB or WiFi. When the app stops, the clock can run the weather animation itself.

Tests keep those implementations aligned. A shared fixture stores 48 frames for each of the nine weather scenes. Rust tests compare the app's output to those frames. The Python test suite compiles the firmware's C++ engine on the PC and compares its output to the same fixture.

There are also checks for behavior that matters visually: every scene moves, every row fits within seven lights, and the crescent remains visible in the night scenes. Separate tests cover the mapping from weather codes and daylight to scene names.

The animated preview above comes from that shared fixture, so it shows the same frame data used to check the two renderers.

Choosing a scene on the dashboard

Select the Weather animation for the grid, then set Grid scene to Follow the weather. The dashboard also lets you select any scene directly, so you can see snow or a storm without waiting for those conditions outside.

For standalone operation, enable the grid under “when the app is off” and choose Weather there. The PC app has its own live grid controls; Save to clock makes a supported live animation the clock's standalone selection too.

The firmware gives an explicit scene choice priority over automatic weather selection. Otherwise, it uses its detailed weather scene for up to two hours after a successful fetch. After that, it falls back to the simpler sunny, cloudy, or rain condition and local time, using a daytime window of 06:00–20:00 for clear skies.

The EleksWFD project page covers the firmware and companion app. The animation engine contains the shapes and movement rules, and the weather animation tests show the scene mappings and shared frame checks.