Add support for the Shearwater Perdix 3 BLE protocol#117
Conversation
The Perdix 3 does not expose the classic Shearwater serial service. It uses a new vendor service and a modified transport framing while keeping the application protocol (SLIP, RDBI/WDBI, block download) unchanged: - The transfer header carries a 24-bit big-endian payload length (FF 01 <len24> request, 01 FF <len24> response) instead of the classic 8-bit length, and BLE writes are a raw SLIP stream without the per-packet [nframes, index] prefix. - The download init uses record type 0x24 with a 16-bit size field, and the init response is 75 20 <blocksize:16be>. The device uses 514-byte manifest blocks and 2306-byte compressed dive blocks, so the packet buffers are raised from 254 to 4096 bytes. A framing field on shearwater_common_device_t selects the classic or Perdix 3 behavior. It is derived from the descriptor model, which is now passed into shearwater_petrel_device_open. A new descriptor row maps the "Perdix 3" BLE name to model 14 and hardware type 0x39C2 to the new model. Verified end-to-end against a Perdix 3 (firmware V38): identity reads, manifest and compressed dive downloads, and dive import all succeed over BLE on Linux. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Vojtěch Šplíchal <splichal@gmail.com>
There was a problem hiding this comment.
Pull request overview
Adds support for the Shearwater Perdix 3 over BLE by extending the existing Shearwater/Petrel implementation with a framing switch, enabling both classic Shearwater BLE framing and the Perdix 3’s raw-SLIP + 24-bit length transport.
Changes:
- Extends the Petrel device open path to accept a descriptor model and select classic vs Perdix 3 framing.
- Updates Shearwater common transport code for Perdix 3 framing (no per-write BLE header, 24-bit transfer length, updated download init) and increases packet buffer sizing.
- Adds a new descriptor entry and model mapping for “Perdix 3” and maps hardware type
0x39C2to the new model.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/shearwater_petrel.h | Extends the device open API to accept a model for framing selection. |
| src/shearwater_petrel.c | Selects Perdix 3 framing at open time based on descriptor model. |
| src/shearwater_common.h | Adds model/framing constants and a framing field to the shared device struct. |
| src/shearwater_common.c | Implements Perdix 3 transfer/download framing behavior and increases packet buffer sizes. |
| src/device.c | Passes descriptor model into the Shearwater Petrel open call. |
| src/descriptor.c | Adds “Perdix 3” descriptor + name filter entry for discovery. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "NERD", | ||
| "NERD 2", | ||
| "Perdix", | ||
| "Perdix 2", | ||
| "Perdix 3", |
| // The Perdix 3 framing uses a 24-bit length field and negotiates larger | ||
| // download blocks than the classic 254 byte packets: 514 bytes (including | ||
| // the 2 byte block header) for uncompressed manifest reads, and 2306 bytes | ||
| // for compressed dive downloads (init response 75 20 0902 observed live). | ||
| #define SZ_PACKET 4096 |
|
Making this change directly in the Subsurface 'fork' of libdivecomputer is not the best way to get this done - this wil create friction when merging changes from uptream libdivecomputer in the future. Also note that this is an open source project, and maintainers will volunteer their free time to work on it whenever they have it available, and have the motivation to do so. Pinging them within hours of submitting something will not help with motivation... |
|
Ooh, didn't know it's a fork :) |
|
Yeah, it's an unofficial fork of libdivecomputer - we regularly pull in updates from upstream, but we also have some improvements (like support for sending string messages to the host applications, or file / MTP based devices) and bugfixes that were never accepted into upstream. |
The Shearwater Perdix 3 keeps the classic Shearwater application protocol
(SLIP, RDBI/WDBI, block download, same register IDs and dive parser) but
changes two things at the BLE transport layer. This adds a framing switch
to the shared Shearwater/Petrel code so both the classic devices and the
Perdix 3 are handled by one implementation.
What changes on the Perdix 3
service; it advertises a new vendor service. Detection is by the BLE
name
Perdix 3(matched beforePerdix, since matching is by prefix).with a 2-byte
[nframes, index]header. The Perdix 3 sends a raw SLIPstream with no per-packet header; notifications are reassembled until
the SLIP END byte
0xC0.big-endian payload length (
FF 01 <len24>request,01 FF <len24>response) instead of the classic 8-bit length.
0x24with a 16-bit size field and the init response is
75 20 <blocksize:16be>.Observed block sizes are 514 bytes (manifest) and 2306 bytes (compressed
dive), so the packet buffers are raised from 254 to 4096 bytes.
Implementation
A
framingfield onshearwater_common_device_t(
SHEARWATER_FRAMING_CLASSIC/SHEARWATER_FRAMING_PERDIX3) selects thebehavior. It is derived from the descriptor model, which is now passed into
shearwater_petrel_device_open. A new descriptor row maps thePerdix 3BLE name to a new model and maps hardware type
0x39C2to it. The classicpath is unchanged.
Testing
Verified end-to-end against a Perdix 3 (firmware V38) over BLE on Linux:
identity reads (serial/firmware/hardware), manifest read, compressed dive
download, and full dive import all succeed. The same build cross-compiled
for Android arm64 was validated end-to-end on a physical phone.
Notes for reviewers
Portions of the new protocol logic were AI-assisted; the non-trivial new
blocks in
shearwater_common.care marked with anAI-generated (Claude)comment. The protocol was reverse-engineered from an HCI snoop of the
official Shearwater app and byte-matched against the capture.
Generated with Claude Code (https://claude.com/claude-code)