ON AIR · build your own

A desk sign
that never sleeps.

It rotates live stocks, weather, and the time across a bright dot-matrix display. Tap once and it flips to a status sign that stays up until you clear it. It runs on an ESP32-S3 microcontroller and an LED matrix, set up over Bluetooth from an iOS app.

The LED Ticker on a desk in its white case, its red dot-matrix display reading CALL, wired to an ESP32-S3 board over a braided USB-C cable.

/ what it does

Glanceable info. Instant status.

An ambient rotation you set once and forget. An active sign that takes over the second you need it.

01

Sign mode

One-tap status text (BUSY, FOCUS, ON AIR) overrides the ambient rotation, with an optional auto-clear timer.

02

Countdown timer

A live MM:SS countdown from 1–99 min, ending in a random animation (fireworks, sonar, sparkle), then back to ambient.

03

Live data

Stock quotes from Finnhub and multi-location weather from Open-Meteo, geocoded on-device. No phone tether required.

04

12-hour clock

Steady H:MM when shown alone, scrolls H:MM AM/PM when mixed in. NTP-synced, timezone-aware.

05

iOS companion

A native app: multi-device switcher, preset chip grid, and a Display tab with per-category toggles plus a master on/off.

06

PIN-gated BLE

Every write is gated on a 6-digit PIN. iOS uses the native pairing dialog; the PIN rotates on factory reset.

/ under the hood

How it works.

Single-file firmware, deliberately. Here's what makes it tick.

Dual-core, never blocks

Core 0 runs a background task that pulls quotes and weather. Core 1 drives the display in a cooperative loop() with no delay() anywhere. They hand off through volatile flags, so a slow network never stutters what's on screen.

One protocol, many clients

Wi-Fi, API keys, tickers, locations, mode, the active sign. All of it is configured over one documented BLE service. The iOS app and the Python CLI are just two clients of that same contract. Writes get queued out of the radio callback and applied by the main loop.

A prioritized render stack

Every frame draws by priority. A countdown or status sign wins first, then the idle animation, then the steady clock, then the scrolling rotation. One small state machine decides what lands on the matrix.

Degrades gracefully

No Wi-Fi yet, or no API key? It drops into a setup mode that scrolls its own name and PIN. Categories that are still loading just say Loading…. Signs and timers run off millis(), so they keep working with no network at all.

Flash vs RAM, on purpose

Your settings live in flash and survive a power cycle. The fetched quotes, weather, and active sign stay in RAM. Reboot, and the transient stuff clears while your ambient rotation picks right back up.

One button, two jobs

At power-on, the BOOT button is the bootloader strap. At runtime it's just a button. Hold it ten seconds and the matrix counts down, then wipes every setting and forgets every BLE bond.

Go deeper: Firmware guide Protocol iOS app

/ make your own

Build one.

Two off-the-shelf modules, a custom PCB, and a 3D-printed bracket. Everything you need is in the repo.

PartWhat
MCU board Freenove ESP32-S3-WROOM (FNK0099) · onboard NeoPixel, native USB-C Buy ↗
Display DIYables 4-in-1 MAX7219 8×8 LED matrix Buy ↗
PCB Custom board carrying both modules · EasyEDA sources, order via JLCPCB Files ↗
Case 3D-printed case (STLs included) STLs ↗
Cable USB-C for power + flashing any

// then flash it

git clone https://github.com/ssayala/esp32-led-simple
cd esp32-led-simple/firmware
pio run -t upload   // build & flash over USB-C

First boot scrolls the BLE name and a 6-digit PIN. Pair from the iOS app or the Python CLI, set your Wi-Fi, and you're live. Full walkthrough in the Getting started guide.

No toolchain? Flash it from your browser with Chrome, over USB.

3D render of the custom PCB that carries the Freenove module and the MAX7219 matrix header on a single board.
The custom PCB, designed in EasyEDA and fabbed at JLCPCB.

/ not a tinkerer

Want one pre-assembled?

Building isn't for everyone. If you'd rather have a finished unit that arrives assembled, flashed, and ready to plug in, leave your email. It's just a waitlist to gauge interest, with no commitment and no charge. I'll only reach out if pre-built devices become available.

// no spam · US only for now