Skip to content

Protocol

Audio·

The ring's microphone: 4-tap voice memos on flash, the 0x34 pull, live streaming modes, and the Opus packet layout.

  • hackers and protocol readers
  • 2 min read
  • 291 words

In 10 seconds

Tap 4× → the ring records a memo to its flash; pull it with 0x3D count → 0x400x34 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.