protocol: decode ring_start (0x41) and user_information (0x5c) - #12
Open
nsandoval wants to merge 1 commit into
Open
protocol: decode ring_start (0x41) and user_information (0x5c)#12nsandoval wants to merge 1 commit into
nsandoval wants to merge 1 commit into
Conversation
These were the only two event types still reaching the store with a name but no body decoder, so a full sync left them raw. ring_start is a boot record: a u32 reason word, one unidentified byte, then the firmware, bootloader and API version triplets — byte for byte the values DeviceInfo reports for the same ring. Recovered by correlating a captured body against that command's response on a Ring 4 (ORE_06) right after a factory reset. user_information is four bytes of anthropometric profile. Its field meanings are inferred rather than recovered from libringeventparser.so, so the decoder emits "_status": "inferred". The basis is that ecore's own metabolic functions take exactly these inputs — vo2max_jackson(age, female, weight_kg) and bmr_schofield(age, sex, weight_kg), both ported in oura-analysis::ported::metabolic — and the sleep-score limits are initialised "from age byte". On the captured sample the sex byte is 2: neither male (0) nor female (1), but the value bmr_schofield documents as "anything else", the unset path that averages both sexes. That ring had just been factory reset, so the values read as firmware defaults (40 y, 75 kg, 176 cm) rather than a configured profile. height_cm is flagged as the least certain field, since no ported algorithm consumes it — steps_to_meters uses a fixed 0.762 m stride. Both layouts are covered by tests using the captured bytes, and the crate README notes their provenance, which is not the usual Ghidra port. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
nsandoval
force-pushed
the
decode-ring-start-and-user-information
branch
from
August 22, 2026 12:46
689091b to
d41c4e7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
These were the only two event types still reaching the store with a name but no body decoder, so a full sync left them raw. With both landed, a sync of my Ring 4 decodes 19,117 / 19,117 events.
Tested on a Ring 4 Horizon (
ORE_06, firmware 2.12.3) paired with a client-generated key viaoura pair.Checked against the open branches first: this does not overlap #7, which leaves
0x41and0x5cuntouched.ring_start(0x41)A boot record. Layout:
Recovered by correlating a captured body against the
DeviceInforesponse of the same ring: bytes 5..14 are byte for byte the firmware, bootloader and API triplets that command reports. The leadingu32and the byte at offset 4 are not identified — one sample only — so they are surfaced verbatim (reason,unknown_byte_4) rather than guessed at.user_information(0x5c)Four bytes of anthropometric profile:
[age_years][weight_kg][sex][height_cm].Field meanings here are inferred, not recovered from
libringeventparser.so, so the decoder emits"_status": "inferred". Flagging that explicitly since it differs from how every other decoder in the crate was obtained. The basis:vo2max_jackson(age, female, weight_kg)andbmr_schofield(age, sex, weight_kg), both already ported inoura-analysis::ported::metabolic.init_limits_v2 @ 0x1f5b20, perdocs/algorithms/README.md).2— neither male (0) nor female (1), but the valuebmr_schofielddocuments as "anything else", the unset path that averages both sexes. That ring had just been factory reset, so40 y / 75 kg / 176 cmread as firmware defaults rather than a configured profile.height_cmis the least certain field and is called out as such: no ported algorithm consumes it, sincesteps_to_metersuses a fixed 0.762 m stride.Why this matters beyond completeness
If the reading is right, a ring that was re-keyed rather than onboarded through the app runs its metabolic path on defaults. For my body composition that is a 21% low BMR and a 25% systematic underestimate of active calories, since those derive from MET x weight. Anything downstream of
oura-analysis::ported::metabolicinherits that error silently.Possibly worth a follow-up
tools/oura_protocol.pydescribes the0x20setters as the "unused gender/height/weight field". If the reading above is right they are not unused, just unset. I left that wording alone because I cannot yet prove a write persists: the ring ACKsset_user_gender_emptywith21020200(type=2, result=0), but there is no read command for the profile, and the onlyuser_informationemission I have landed at exactly 00:00, which looks scheduled rather than change-driven. Happy to follow up once a round trip confirms it. This may also be a partial answer to #8.Testing
cargo test -p oura-protocol→ 37 passed.cargo clippy -p oura-protocol→ clean.cargo fmt --check→ the new code adds no diffs of its own.oura redecode, which backfilled both events from bodies stored before the decoders existed.