This document describes the binary serial protocol used between a host system (Python on Linux) and a microcontroller (Arduino Mega 2560) acting as a GPIO interface.
The protocol is designed to be:
- Low latency
- Deterministic
- Event-driven (no polling required on host)
- Simple to implement on constrained microcontrollers
Communication occurs over USB CDC serial (e.g. /dev/ttyACM0).
- Medium: USB CDC (virtual serial port)
- Baud rate: 115200
- Data format: 8N1
- Flow control: none
Two types of messages exist:
| Direction | Type | Sync Byte |
|---|---|---|
| MCU → Host | Event | 0xAA |
| Host → MCU | Command | 0x55 |
The sync byte allows resynchronization in case of byte loss or misalignment.
[0xAA][pin][value][timestamp_lo][timestamp_hi]
| Field | Size | Description |
|---|---|---|
| Sync | 1 | 0xAA |
| Pin | 1 | GPIO pin number (0–69) |
| Value | 1 | Logic level (0 or 1) |
| Timestamp | 2 | millis() timestamp (uint16, little endian) |
- Timestamp wraps every ~65 seconds.
- Events are generated on debounced state changes only.
- No duplicate events for stable signals.
[0x55][cmd][pin][value]
| Cmd | Name | Description |
|---|---|---|
| 1 | SET_OUTPUT | Set digital output value |
| 2 | SET_MODE | Configure pin mode |
[0x55][1][pin][value]
value: 0 = LOW, 1 = HIGH- Pin must be configured as output
[0x55][2][pin][value]
value = 0→ INPUT (with pull-up enabled)value = 1→ OUTPUT
- Configured as
INPUT_PULLUP - Active low (button press → 0)
- Debounced in firmware
-
Default debounce time: 10 ms
-
Per-pin state tracking
-
Event generated only if:
- State changes
- Debounce interval has elapsed
-
Uses hardware interrupts for supported pins
-
ISR performs:
- State read
- Debounce check
- Event enqueue
- Ring buffer (FIFO)
- Fixed size (e.g. 32 entries)
- Shared between ISR and main loop
-
ISR enqueues events
-
Main loop dequeues and transmits
-
If queue is full:
- New events are dropped (current implementation)
- Queue overflow should be monitored in future revisions
- Optional: add overflow counter or special event
- Read arbitrary byte stream
- Scan for sync byte (
0xAA) - Parse fixed-length frames
If invalid data is encountered:
- Discard bytes until sync byte is found
- Event latency: typically 1–3 ms
- Timestamp resolution: 1 ms
- Timestamp range: 0–65535 ms (wraps)
Current protocol has minimal error handling:
- No checksums
- No acknowledgements
- No retransmission
- Suitable for short USB links
- Not suitable for noisy or long-distance links
- Limited number of interrupt-capable pins
- Timestamp wraparound
- No guaranteed delivery under overflow
- No command acknowledgment
Possible improvements:
Add 1-byte CRC:
[SYNC][...][CRC]
- Bulk GPIO operations
- PWM control
- Analog reads
Extend event format:
[SYNC][type][pin][value][timestamp...]
Special event:
[0xAA][0xFF][overflow_count][...]
Upgrade to 32-bit timestamp
Key decisions:
- Binary protocol → minimal overhead
- Fixed-size frames → simple parsing
- MCU-side debounce → deterministic behavior
- Event-driven → no host polling
Button press on pin 2:
AA 02 00 34 12
- Pin: 2
- Value: 0 (pressed)
- Timestamp: 0x1234
Set pin 13 HIGH:
55 01 0D 01
This protocol turns the microcontroller into a deterministic GPIO front-end:
- Hardware handles timing and debouncing
- Host handles logic and orchestration
The result is a robust and low-latency interface suitable for measurement and control systems.