Skip to main content
The Totem’s visible output is a ring of addressable RGB LEDs (the halo) plus the Touch Crystal. Driven by apa106.py / neopixel.py / f_lib/neopixel_v2.py, with behavior in leds.py and animations.py.

Driver

APA106 is driven via the ESP32 RMT peripheral as a precise bit-stream, the standard technique for these single-wire addressable LEDs.

Colors

The Colors class (project_data.py) defines 13 named colors (confirmed): red, orange, yellow, yl_green, green, bl_green, teal, white, aqua, blue, indigo, magenta, hot_pink. That is also the order of the internal 13-tuple the class uses as its color→id table, so it doubles as the module’s COLOR_LIST-style index order. (There is no pink or cyan qstr; hot_pink and teal fill those roles. There is a red and a green qstr — the earlier claim that these were absent was wrong.) The name→RGB pairings are confirmed: the class binds each name to its tuple with an explicit STORE_ATTR in the disassembly, so these are no longer inferred: Note: the disassembly settles the earlier teal/aqua ambiguity — teal is (0, 255, 255) and aqua is (0, 128, 255) (the previous table had these two swapped). There is no named OFF color; the “off” / clear state is the literal (0, 0, 0) used by LightStrip.off / fill, not a palette entry. When Totems auto-bond, peers are assigned colors by shuffle_bond_colors, invoked right after the log Setting new auto-bond colors (confirmed — the log immediately precedes the call). It shuffles a 9-color subset given as color indices [12, 4, 9, 2, 10, 1, 6, 7, 11] — i.e. hot_pink, green, blue, yellow, indigo, orange, teal, white, magenta (confirmed; the earlier “8-color: …, orange, aqua, indigo, teal” list was wrong on both count and order).

Animation set

Animations are event-driven — subsystems raise events and the LED task plays the matching effect: All rows above are confirmed symbol names; the exact frame data and timings live in undisassembled frozen bytecode. The underlying effect primitives (confirmed qstrs) are breathe_effect / breathe_gen / breathe_v2, twinkle / twinkle_crystal, and fade_step. Vibe Mode’s v5.0.3 tuning (newvibe-1.10) lowers its brightness base from 0.1 to 0.07 — about 30% dimmer (inferred) — and raises the auto-gain up rate from 13 to 25. The compass heading itself is rendered as a lit point on the ring that rotates to point toward the target (_spin_deg, Dial rotation cancelled).

Status colors

Identifying the firmware version by the power-up Touch Crystal color (e.g. “5.0 → Sparkly Pink”) comes from the official update guide, not from firmware evidence. No version→color table was found in the binary, and no sparkly or pink string exists (only hot_pink is a defined color), so this mapping cannot be verified here.
What the firmware does show is that the crystal color is a configurable default that is also mapped per bonded peer, not derived from __version__: In v5.0.3, when compass.start_peer_management runs while the device is vertical it now sleeps 300 ms and then restores the default crystal colour (crystal_default()). OTA/update state→color mappings are described in the WiFi OTA guide. The disassembly of ota_callback.py resolves the two color constants (previously “values undetermined”), so two of the four guide claims are now confirmed in firmware and two remain guide-only: dim_leds(rgb, brt) returns the RGB tuple with each channel scaled by brt. A third constant, LOG_RGB = dim_leds(colors.white, GLOBAL_BRT), is defined alongside these.

Power interaction

LEDs are one of the largest current draws, so the LED task is likely gated by power state. A general lightsleep mechanism exists (dev_total_lightsleep_ms, log Awoke from lightsleep …), but no LED-task-specific sleep-gating symbol was found, so the LED-task suspension is an inference rather than a confirmed binding. Brightness is independently controllable: GLOBAL_BRT, dim_leds, toggle_brightness (with toggle_brightness_timeout), update_brightness, and the log Revert to full brightness. toggle_brightness is wired to a single tap of the physical power button (sw_power.cb_single_tap, confirmed; see physical buttons). Note that the binary’s low-battery mode is evidenced for touch sensitivity, not LED brightness — the low-battery symbols (_low_batt_sensitivity, _LOW_BATT_FLOOR, _LOW_BATT_MULTIPLIER) and logs ([touch] Low battery mode ON | base_sens: {:.3f}) all tie to touch, with nothing linking low-battery state to LED brightness in the evidence.