Add authenticated audio streaming and BLE throughput measurement - #75
Merged
Conversation
Issue #71 asks for an on-request audio recording, which means moving a clip from the node to the relay as GATT notifications out of RAM. Nothing in this firmware does that, so nothing measures it. The only figure available is the OTA relay's, measured on a deployment at ~1-1.5 kB/s -- and that path is a poor proxy in three ways that all push the same direction: it is central to peripheral, every chunk is a write WITH RESPONSE (two connection intervals minimum), and data_write() flushes RRAM synchronously inside the ATT callback, where each 512-byte write runs in a radio timeslot that pre-empts connection events. It largely measures flash scheduling, not the radio. So measure the radio directly, before the design commits to streaming a clip or to buffering a whole one: * src/throughput.c/.h -- a GATT service (0x8e8b00fx, clear of any future audio service) that notifies a sequence-numbered counter pattern for a requested duration and reports what it managed to send. The client compares its own byte count against the node's, so packet loss is distinguishable from slowness. It also logs the negotiated interval, PHY and data length, which is where "are we really running 2M PHY and 251-byte PDUs?" is actually answered. * throughput-spike.conf -- layered like low-power.conf. Carries the Kconfig the link-parameter callbacks need and CONFIG_BT_BUF_ACL_TX_COUNT, which throughput.c takes its in-flight credit count from, so notifications per connection event can be swept without touching code. The connection interval is swept from the client through an optional CTRL byte. * tools/throughput/ble_throughput.py -- laptop client (bleak). An upper bound, useful for telling "the node is the limit" from "the relay is the limit". * docs/ble-throughput-spike.md -- how to run it, how to read it, how to remove it. ENABLE_THROUGHPUT_SPIKE defaults to 0, so a build that does not apply the fragment is byte-identical with these files present. ota_release_arm_timeout() is the one hook into existing code: ota.c drops any central that has not sent BEGIN within six seconds, which would cut every run short. It is compiled out of normal builds, leaving that guard unconditional in anything that ships. A spike image must never be released -- the service lets anything in radio range make the node transmit continuously. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ExXyz8C48CXmsDwyGZP4yf
bt_conn_le_info.interval (1.25 ms units) is deprecated -- it cannot represent the shorter intervals CONFIG_BT_SHORTER_CONNECTION_INTERVALS allows, so it now sits in a union behind the microsecond field. NCS builds deprecation warnings as errors, so the connect-time log line failed the build on v3.3.1. interval_us is the replacement and carries strictly more information. latency and timeout are not deprecated and are unchanged, and the le_param_updated callback still takes the interval in 1.25 ms units as an argument, so its print stays as it was. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ExXyz8C48CXmsDwyGZP4yf
Fix throughput-spike OTA timeout build by defining work item before use
…-feature Add authenticated BLE audio streaming, shared link ownership, mic stream API, and config/docs
…ing-measurement-cycle Serialize sensor measurements with BLE sessions
"Provision the same random 32-byte key on node and HiveHub" left out the part that actually costs time: the two sides want the same bytes in different notations — a C array here, a 64-character hex string in HiveHub's secrets.h — so anyone following the old wording had to convert by hand and hope. Give them the one command that prints both forms, and a filled-in example of the resulting audio_secret.h so there is no doubt about where the rows go. Also states two things that were only discoverable by hitting them: flashing one side and not the other leaves audio failing authentication while the beacon and its measurements carry on normally (so it presents as a radio fault, not a configuration one), and HiveHub's FORCE_RESEED has nothing to do with this key — that flag re-seeds a claim code into NVS, while the audio key is compiled in and read afresh at every session. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ExXyz8C48CXmsDwyGZP4yf
Three gaps between the docs and the firmware on this branch. **The 0.6.2 fix was undocumented.** `audio-over-ble.md` said only that "periodic sensing pauses for any connection" — which was the intent, not the mechanism, and was untrue before 0.6.2. A measurement cycle that had already passed the `link_is_busy()` check went on to drop the sensor rail underneath a running session, and the boolean rail flag made the session's own enable a no-op, so a full-length recording carried sound for ~50 ms and the ±4 LSB of an unpowered microphone thereafter. Every counter reported it clean, because the bytes that arrived were exactly the bytes that were sent. That failure mode is worth writing down: it looks like nothing, and only listening reveals it. The new section describes the session gate, the reference-counted rail, and the move of session setup out of the GATT callback into the capture thread (which is why STREAMING now means the microphone is genuinely running). Two troubleshooting entries follow from it — busy `12`, and audio that starts and then goes silent. **Key provisioning was missing here.** The section landed on the feature branch after it was merged, so this branch still told the reader to "provision the same key" without the command that prints both notations. Cherry-picked. **The throughput spike still read as an open question.** It is answered — 137 kB/s, which is what decided raw PCM streamed rather than a codec — so the page now leads with the answer and says plainly that the spike is safe to delete, rather than leaving a "delete when answered" note that has quietly been satisfied for a while. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ExXyz8C48CXmsDwyGZP4yf
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.
Summary
This PR adds authenticated, on-request audio streaming over BLE to HiveInside, along with a temporary throughput measurement spike to validate the link capacity for audio delivery. The audio feature enables the node to capture PCM16 samples from the microphone and stream them to HiveHub via GATT notifications, with HMAC-SHA256 authentication and configurable gain control.
Key Changes
Audio Streaming Feature
New
src/audio.c: Complete authenticated audio service with:New
src/audio.h: Public interface for audio initialization and link lifecycle callbacksNew
src/audio_secret.example.h: Template for provisioning the authentication key (gitignored in actual deployment)Connection Management
src/link.candsrc/link.h: Centralized ownership model for the single peripheral BLE connectionMicrophone API Refactoring
src/mic.candsrc/mic.h: Extracted streaming functions for audio use:mic_stream_start(): Initialize and start PDM capturemic_stream_read(): Non-blocking read from capture buffermic_stream_release(): Release buffer back to slabmic_stream_stop(): Stop PDM captureOTA Service Updates
src/ota.candsrc/ota.h: Refactored to use shared link managementota_link_connected()andota_link_disconnected()callbackslink_claim()andlink_release()for connection ownershipTemporary Throughput Measurement Spike
New
src/throughput.candsrc/throughput.h: Diagnostic service to measure BLE notification throughputENABLE_THROUGHPUT_SPIKE=1New
tools/throughput/ble_throughput.py: Python client (bleak) for laptop-based throughput testingNew
throughput-spike.conf: Kconfig fragment to enable measurement buildConfiguration and Documentation
src/hive_config.h: Added audio configuration parameters:ENABLE_AUDIO: Feature flag (defaults to 1, fails closed without key)HIVE_AUDIO_MAX_SECONDS: Session timeout (60 seconds)HIVE_AUDIO_RING_BYTES: Ring buffer size (32 KiB)HIVE_AUDIO_STALL_TIMEOUT_MS: Notification stall detection (5 seconds)HIVE_AUDIO_CONN_INTERVAL_UNITS: Optimized 15 ms interval (12 units)HIVE_LINK_ARM_TIMEOUT_MS: Connection arm windowhttps://claude.ai/code/session_01ExXyz8C48CXmsDwyGZP4yf