Skip to content

Share the packet format with the Pi instead of duplicating it - #6

Merged
PonderForge merged 4 commits into
mainfrom
feat/use-shared-packet-library
Sep 26, 2026
Merged

PonderForge merged 4 commits into
mainfrom
feat/use-shared-packet-library

Conversation

@taciturnaxolotl

@taciturnaxolotl taciturnaxolotl commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Why

This merges the definitions for the binary packed protocol so the rpi and the arduino don't go out of sync

Both ends now use a library from SensorHub

Depends on HEEV/SensorHub#2 to merge first. CI installs from SensorHub main

/* <sensorhub/sensorhub.h> — works everywhere including on arduino */
void    sh_parser_init  (sh_parser_t *parser);
bool    sh_parser_feed  (sh_parser_t *parser, uint8_t byte, sh_packet_t *out);   uint8_t sh_checksum     (const uint8_t *data, size_t length);
void    sh_encode_frame (const sh_packet_t *packet, uint8_t *buffer);

/* <sensorhub/serial.h> — POSIX only */
int  sh_serial_open        (const char *device);
void sh_serial_close       (int fd);
bool sh_serial_read_packet (int fd, sh_parser_t *parser, sh_packet_t *out);

etc

engine temp needs a defined address as currently its just reading zeros

The firmware defined the packet struct, the checksum, and the frame
encoder itself, and the Raspberry Pi defined all three again on its side.
Editing one without the other would not break a build, it would just make
the car log nonsense.

Both ends now use the same definition from the SensorHub library, so the
drift is no longer expressible. Costs 22 bytes of flash, 0.07% of a Nano,
and no RAM. Frames are byte-identical to before, checked against the old
encoder over 200,000 random packets.

Adds CI that compiles for the actual board, reports flash and RAM use,
and fails if the sketch grows past 80% of flash, since running out of room
on a Nano is a real failure mode when someone adds a sensor.

Needs HEEV/SensorHub to land first; CI here installs it from main.
The install script reads its first positional argument as a version, so
passing a bindir made it try to download arduino-cli_-b_Linux_64bit.
Follows the wire format change in SensorHub: the frame now carries a version
and a length, and the payload has room to grow.

The fan and pump pins have been configured as outputs since this was written
and their state appeared nowhere in telemetry, so nobody could see or log
what the car was doing to itself. They are now reported alongside the inputs.

Also adds the sequence number the receiver needs to notice a packet never
arrived, and names the output pins so the pinMode calls and the packet cannot
drift apart.

Costs 292 bytes of flash, taking the sketch from 18% to 19% of a Nano.
Without pipefail, tee's exit status hid a broken build and the error
turned up two steps later as an empty grep. Also checks that the
SensorHub checkout actually contains a usable library.
@taciturnaxolotl

Copy link
Copy Markdown
Contributor Author

Updated: the wire format now carries a version and a length byte from the start, rather than being retrofitted later.

The version byte exists for one specific failure. If the payload changes size, framing desyncs and you notice. But if it stays the same size and a field changes meaning, the checksum still passes and the receiver decodes confidently wrong data. That is exactly what happened on the Python side in April, when columns 5 and 6 silently went from Speed/button to voltage/timer_reset_button and nobody caught it for months.

The length byte lets an old receiver skip a packet from a newer sender and stay framed instead of desynchronising.

Payload also widened while it was cheap to do so, since breaking the format twice is much worse than once:

before now
temperatures 2 named floats 4 slots
analog 1 4 slots
digital 5 bytes, inputs only 8 inputs + 8 outputs, as bits
sequence none uint16_t

Outputs are reported now. The firmware drives a radiator fan and a water pump whose state appeared nowhere in telemetry, so there was no way to see or log what the car was doing to itself.

The sequence number makes dropped packets visible. A checksum cannot tell you about a packet that never arrived, so a link losing half its traffic looked identical to a healthy one.

Costs: frame 26 to 41 bytes, which takes the link from 4.5% to 7% utilisation, and the sketch from 18% to 19% of a Nano.

@PonderForge

Copy link
Copy Markdown
Member

Version is interesting, yet kinda a waste to send each packet, same thing with packet length. If the packet structure version is the newest, then length and version wouldn't change at all. If anything, you may wanna add a interrupt on the nano when it receives a byte that causes it to hand over a version, then have the Sensor Hub throw an error if the version doesn't match. Add that and I'll approve this one, but I gotta finish looking over the SensorHub pull request.

Thanks for the effort btw!

@PonderForge

Copy link
Copy Markdown
Member

Oh also, if you would mind checking to see if you can put the engine and radiator temp on a timer interrupt as well so that the packet sending wouldn't be bogged down by the temp polling. I'm thinking polling every 100ms, since the datasheet for these sensors says it only can really update every 90ish ms

@PonderForge
PonderForge merged commit 56bf54f into main Sep 26, 2026
1 check failed
@PonderForge
PonderForge deleted the feat/use-shared-packet-library branch September 26, 2026 02:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants