Segment display and clock face
Hardware interface
The Mini has a 30-element monochrome display driven through /dev/is31fl32xx. Logically it is four seven-element digit groups plus two colon indicators: indices 0–6, 7–13, 14–20, and 21–27 form the four digit positions; indices 28–29 are the colon. Animations also use these segments for eyes, icons, and loading effects.
Decompiled: both voice-agent (0x14c6d4, constructor body) and check-factory-reset (sub_10C00) open /dev/is31fl32xx and issue ioctl 0x80046b02 to query the element count. The expected count is exactly 30. Frame updates use LEDS_IS31FL32XX_CONTROL_IOCTL_SET (check-factory-reset:0x17264; normal agent 0x2b9910).
The separate RGB ring uses /dev/topleds; do not confuse the two interfaces.
JSON-to-hardware index map
voice-agent:0x2b9910 does not write JSON indices directly. It applies the 30-byte permutation at file offset 0xa1a218 before the ioctl. The complete logical-index → driver-index map is:
| JSON indices | Driver indices in the same order |
|---|---|
| 0–9 | 1, 28, 21, 17, 22, 29, 24, 26, 25, 19 |
| 10–19 | 18, 23, 27, 20, 16, 15, 0, 2, 7, 5 |
| 20–29 | 6, 4, 3, 10, 12, 11, 14, 13, 8, 9 |
Use this table when capturing physical glyphs or writing a new animation. The JSON schema remains the canonical userspace representation.
JSON format and timing
Display animations are arrays under /usr/share/voice-agent/led/:
[
{
"duration": 0.0166,
"brightness": [0, 3, 22, 54, 85]
}
]
The example is abbreviated; real Mini frames have 30 brightness entries, 0–255. duration is seconds. seconds is an alternative timestamp form: the next frame duration is computed from the timestamp difference. A caller supplying an FPS argument overrides per-frame durations. The shared parser accepts up to 72 monochrome values and up to 24 RGB colors, logging too many values above those limits.
Decompiled: led_animation_parser.cpp stores each parsed frame in a 96-byte internal structure. The reset renderer waits with pthread_cond_clockwait.
State/resource map
The following associations are observed from filenames and direct string references in voice-agent.
| State | Display resources |
|---|---|
| Ready/idle | ready_digits.json, eyesReady_digits.json |
| Intro/blink | eyesIntro_digits.json, eyesBlink1_digits.json, eyesBlink2_digits.json, eyesHappy_digits.json |
| Setup | settingsEnter_digits.json, settings_intro and related RGB sequences |
| OTA download | downloadIconLoop_digits.json |
| Bluetooth | btWaiting_digits.json, btConnected_digits.json, btDisconnected_digits.json |
| Track navigation | nextTrack_digits.json, prevTrack_digits.json |
| Factory reset | resetIconLoop_digits.json, factoryResetEnter_digits.json |
| Hidden reset joke | factoryResetJoke_digits.json |
| Demonstration | Demo_Digits.json, Demo_Digits_frame_1.json through frame_8 |
Additional monochrome eye/loader/demo assets include Eyes_*.json, Loader-4.json, Loader-5.json, One_eye_open.json, Tennis.json, and Thinking_warms.json.
Reset-specific rendering
Decompiled from check-factory-reset:sub_10C00:
- During the five-second combo window,
resetIconLoop_digits.jsonis repeated to fill the requested duration. - If the combo completes,
factoryResetEnter_digits.jsonplays once as the final display sequence. - Both display and RGB updates stop by writing an all-zero frame afterward.
The normal agent has a separate hidden-joke path. voice-agent:0x1442e0 (doFun) handles more than 19 activations by selecting factoryResetJoke_digits.json, factoryResetJoke_diodes.json, and 30_reset_v3.flac; this is an Easter egg, not the actual wipe trigger.
Ambient-light brightness
voice-agent:0x27bf20 reads the IIO illuminance channel and maps it to 0–100%:
- below
5.0: 0%; - above
3000.0: 100%; - otherwise
int(19.937704 * ln(0.05 * lux + 0.75)).
Updates are suppressed when the percentage changes by five or fewer points unless the difference crosses the configured dead band. Clock settings separately expose clock_brightness, clock_night_brightness, and day_clock_brightness; adjustCapsulaMiniClockBrightness clamps manual adjustment to 0–100.
The live read-only sample was 0.239 in the sensor's raw units. No unit conversion to lux is claimed; the formula above uses the binary's lux variable name for its input.