ESP ZeroCode AI · Community

BLE Xmas Light

Xmas LED light with buzzer that can be controlled using a phone app over bluetooth. It should support 5 modes - Red, Green, White, Rainbow and Random. The modes can be cycled through using ESP32-C3-DevKitM-1 BOOT button. BLE GATT attribute can also be written to a numerical value to set the mode. Each mode has a Xmas related tune associated that is played on the buzzer.

esp32c3 ble_hid
Download the firmware Fork it in the studio

What it does

Manual

Device Type
A standalone Christmas light ornament: a 37-LED hexagonal RGB panel plus a small buzzer on an ESP32-C3 devkit, running off a USB phone charger on a mantel or desk. It solves the 'I want a festive light with music but no cloud, no account and no Wi-Fi setup' problem — you plug it in, it lights up in the mode you left it in, and you change the mode either by tapping the button on the board or from a phone app over Bluetooth.

Hardware
chip: esp32c3 — board: esp32-c3-devkitm-1 — gpios: pin: GPIO5 — role: M5 Unit HEX data (SK6812, 37 LEDs), 3V3 logic from the ESP32-C3 · pin: GPIO4 — role: Grove buzzer drive, LEDC PWM square wave · pin: GPIO9 — role: on-board BOOT button (already on the board) — short press = next mode, 5 s hold = factory reset · pin: GPIO8 — role: on-board WS2812 status LED (already on the board) — unused, left dark · pin: 5V — role: M5 Unit HEX VCC — the panel is a 5 V part · pin: GND — role: common ground for panel and buzzer — peripherals: name: M5 Unit HEX (M5Stack, 37x SK6812) — quantity: 1 — interface: single-wire addressable RGB, data GPIO5, 5 V + GND — notes: Grove/HY2.0-4P cable. Chain input port only; the output port is left free for a future second panel. 5 V-powered part driven by 3.3 V data — works on a short cable but is below the SK6812 datasheet VIH of ~0.7xVDD, so a first-pixel glitch is possible; a 3.3 V-to-5 V level shifter on the data line would make it bulletproof if it ever misbehaves. · name: Grove buzzer — quantity: 1 — interface: single digital/PWM input on GPIO4 — notes: Piezo buzzer with a driver transistor in the Grove module. Melodies are note tables (note frequency + duration) played as PWM tones; no audio codec, no amplifier, monophonic. — power: supply: USB phone charger into the DevKit's USB port (5 V) — worstCaseDraw: 37 SK6812 LEDs at full white are ~2.2 A; a phone charger supplies 1-2 A. Panel brightness is therefore globally capped (a firmware ceiling, not full scale) so that the white and rainbow modes stay inside the charger's budget. — notes: No battery, no charging circuit, no sleep modes. The board's own regulator and the panel share the USB 5 V rail.

Inputs
kind: button — label: BOOT button (on-board) — hardware: GPIO9, pulled up on the devkit; active low — behavior: Short press (50 ms debounce, under 5 s) advances to the next mode, wrapping Random back to Red. Holding 5 s or longer is a factory reset. Works whether or not a phone is connected. · kind: ble_write — label: Mode value from the phone app — hardware: Custom GATT service, one writable characteristic (one byte, values 0-4) — behavior: A write selects that mode immediately and is saved as the new remembered mode. Values outside 0-4 are ignored and the current mode is kept. Any connected phone can write; no pairing or authentication.

Outputs
kind: led_panel — label: M5 Unit HEX — 37 addressable RGB LEDs — hardware: SK6812 chain on GPIO5, 5 V — behavior: Mode 0 Red = all LEDs solid red. Mode 1 Green = all solid green. Mode 2 White = all solid white. Mode 3 Rainbow = hue spread across the panel, animation scrolling around the hexagon continuously. Mode 4 Random = each LED a random colour, reshuffled continuously while the mode is active. Panel brightness is globally capped to stay within the USB supply budget. · kind: buzzer — label: Buzzer melody — hardware: Grove buzzer on GPIO4, PWM tone generation — behavior: One carol per mode, played exactly once when the mode becomes active, then silence: Red = Jingle Bells, Green = Silent Night, White = Deck the Halls, Rainbow = We Wish You a Merry Christmas, Random = O Christmas Tree. A mode change during a tune aborts the old tune and starts the new one. Silence at all other times — this is not a continuous music player.

Connectivity
stack: ble — transport: BLE GATT peripheral — purpose: Advertises a custom GATT service so the phone app can select a mode. Deliberately a plain BLE peripheral, not HID: the phone needs to write a mode number, which media-key HID reports cannot carry.

Commissioning
method: bluetooth_connect — payload: Advertised device name (e.g. 'Xmas Light') plus a custom service UUID with a single writable mode characteristic — instructions: No setup, no network, no pairing code. Plug the DevKit into a USB charger — the panel lights up in the last mode used. To control it, open the app, scan, tap the light in the list, then tap one of the five mode buttons. Nothing has to be configured first; the device is always advertising and always ready to be connected.

Business Logic
powerOnBehavior: On every power-up: read the remembered mode from flash (mode 0 Red if nothing is stored, or after a factory reset), render it on the panel immediately, then play that mode's carol once. The panel then holds the mode indefinitely — no timeout, no dimming, no idle animation other than Rainbow and Random doing their own thing. — automationRules: None. There is no schedule, no timer, no auto-advance and no sunrise/sunset behaviour — the light changes only in response to a button press or an app command. — dayToDayOperation: The light lives plugged in, showing whatever mode it was left in. Tapping BOOT steps through the modes one at a time, each step announcing itself with that mode's carol; the panel changes instantly and the tune follows. For a specific mode, open the app and tap it directly — jumping straight from Red to Rainbow skips the intermediate tunes, so only the destination mode's carol plays. Random shuffles continuously and Rainbow animates continuously while selected, both surviving a power cut: on the next boot they come back rather than dropping to a solid colour. The mode in use is remembered, so unplugging the light and moving it to another room brings it back exactly as it was.

Factory Reset
trigger: Hold the BOOT button for 5 seconds or more. — effect: The remembered mode is erased and the light returns to mode 0 (Red), rendering red and playing Jingle Bells once. Because this product has no Wi-Fi credentials, no bonds and no cloud account, wiping the saved mode is the whole reset — it is the fix for a light that is stuck in the wrong mode or whose saved state has become confusing.

Failure Modes
scenario: Phone out of range, Bluetooth off, or a connection dropped — behavior: Completely invisible to the user: the panel keeps its mode and animation and the tune state is unaffected. The device simply advertises again and waits. · scenario: No phone app available or app closed — behavior: The light is fully usable with the BOOT button alone — all five modes and all five tunes are reachable without the app. · scenario: Power blip or unplug while in Random mode — behavior: The last random frame is kept in memory that survives a reset and is restored within a couple of seconds of boot, so the panel does not freeze in a stale colour or fall back to a solid mode. · scenario: Power blip while in Red / Green / White / Rainbow — behavior: The mode comes back from flash and is re-rendered on boot; the user sees only a brief dark gap followed by the same mode and its carol. · scenario: Supply sags under the white panel's current draw — behavior: The global brightness cap keeps the worst case inside a phone charger's budget. If the supply still browns out, the BLE radio may drop and the board may reset — it reboots straight back into the same mode, and the fix is a stronger charger or a lower brightness cap. · scenario: A mode change arrives while a carol is still playing — behavior: The playing tune is aborted and the new mode's carol starts from its beginning — no overlap, no queueing. · scenario: Out-of-range or malformed value written by the app — behavior: Ignored; the current mode and tune continue unchanged. The characteristic keeps its last valid value so the app can read back the true mode. · scenario: Very rapid button tapping or a bouncing press — behavior: Presses are debounced (~50 ms) so one physical tap is one mode step; a press held past 5 s is unambiguously a factory reset, never a burst of mode steps.

Power Management
batteryPowered: false

Custom Behavior
randomFramePersistence: Random has no deterministic content to re-derive, and the ESP32-C3-DevKitM-1 has no panel reset line (its only on-board LED is a single WS2812 on GPIO8), so after a reset the 37 LEDs would latch whatever they last faded in and keep glimmering. A copy of the current frame is therefore kept in memory that survives a reset, refreshed every couple of seconds while Random is active, and restored on boot. Red, Green, White and Rainbow need nothing extra — they re-render themselves from the mode. — bleStateIsInvisible: Advertising, connecting and disconnecting are deliberately not surfaced anywhere: no status LED, no panel blink and no connection chime. The product behaves identically with or without a phone, which keeps it usable as a plain button-driven ornament. — bootButtonDualRole: The same button is both the mode stepper (short press) and the factory reset (5 s hold), with the 5 s threshold making the two unambiguous. — nonGoals: No Wi-Fi, no Matter, no cloud, no voice assistant, no OTA, no scheduling, no brightness control from the app, no colour picker — the five modes are the whole interface.

Published 2026-09-21
Built with ESP ZeroCode AI — describe a device, get production firmware.
Terms of Service · Privacy Policy