Protocol
Audio·
The ring's microphone: 4-tap voice memos on flash, the 0x34 pull, live streaming modes, and the Opus packet layout.
In 10 seconds
Tap 4× → the ring records a memo to its flash; pull it with 0x3D count → 0x40 → 0x34 stream → echo
0x36 to delete. Or stream the mic live: 0x30 mode 1 → 0x32/0x33 packets. Same payload everywhere:
u32 counter · u16 0 · 12 × 40 B Opus (16 kHz mono, 20 ms). ring-cli audio-pull · ring-cli audio-record
The ring is mic + storage; every byte of intelligence is on the other side of the link. The flash is bigger than the
MCU's 128 KB ROM — one 6-minute memo came off as ≈ 730 KB of Opus (16 kbps CBR, TOC 0xB8 on every frame).
Audio state (cmd 0x2F)·
→ 2F 00 00 → ← 2F state ext
| state | meaning |
|---|---|
0x00 |
idle |
0x10 |
recording (a memo is being dictated — do not pull now) |
0x40 |
recording to flash after an online-record request that could not stream |
ring-cli audio-state reads state and file count.
Pulling a memo [LIVE]·
sequenceDiagram
participant H as host
participant R as ring
H->>R: 2F audio state
H->>R: 3D count?
R-->>H: 3D count u16
H->>R: 40 stop offline transfer
H->>R: 34 get offline audio
loop N packets (total=N, index 1…N)
R-->>H: 34 486 B Opus
end
R-->>H: 36 remaining u16
H->>R: 36 echo → file deleted
→ 2F 00 00 state must not be recording
→ 3D ← 3D count u16 files waiting
→ 40 stopOfflineRecordTransfer (LoraFit sends it 1 s before the pull)
→ 34 getOfflineAudioData
← 34 × N packets: header total=N, index=1…N; payload 486 B
← 36 remaining u16 the ring says "upload complete, this many files remain"
→ 36 remaining echo it → the ring deletes the file; 3D now says count−1
A 6-minute memo was 1522 packets (≈ 30 s over the air). Not echoing 0x36 keeps the file (--no-clear).
Packet payload — all three audio commands·
u32 LE ring clock at recording START, then +1 per packet (a counter, not a timestamp)
u16 0000
12 × 40-byte Opus frame (12 × 20 ms = 240 ms of audio per packet)
Packets are never glued — each keeps its header. Duration = packets × 0.24 s. ring/audio.py muxes the frames
into Ogg Opus in pure Python; ffmpeg makes the .wav.
Live modes (cmd 0x30)·
→ 30 mode 00 → ← 30 action final_state
| mode | LoraFit screen | frames |
|---|---|---|
| 0 | stop | — |
| 1 | online recording | 0x32 / 0x33 stream while worn |
| 2 | AI dialog | same stream; phone does the model |
On the charger
Mode 1 is acknowledged but nothing streams — the ring records to flash instead (state 0x40; 6 minutes for an
8-second request). The mic is live in the cradle; streaming is what is gated. Test live audio worn.
Where memos go next — inbox, transcript, agent: tiny → Voice memos.