In high-throughput embedded systems (e.g., automotive CAN/UART nodes, telemetry links, industrial Modbus), blocking communication routines introduce catastrophic latency. If a microcontroller stalls inside a busy-wait loop waiting for an entire packet to land, it misses critical tasks like sensor sampling, motor control loops, or actuation sequences.
This repository implements a production-ready, ultra-efficient, non-blocking serial packet parser written in C99. The architecture enforces a strict decoupling of data ingestion from data processing:
- Data Ingestion (Background): A simulated Hardware Interrupt Service Routine (ISR) intercepts incoming serial bytes and pushes them onto a thread-safe, power-of-two sized FIFO Circular Buffer (Ring Buffer) with zero processing overhead.
- Data Parsing (Foreground): The main application loop calls an asynchronous Finite State Machine (FSM) Parser that dequeues bytes one by one when CPU cycles are available, processes the frame protocol boundaries, and validates payload integrity using a CRC-16-CCITT mathematical checksum.
The core data structure is a fixed-size ring buffer utilizing an array and tracking indices for head and tail.
- The
volatileSafeguard: The tracking indices are declared with thevolatilequalifier to prevent compiler registers from caching values, forcing memory re-evaluation because theheadchanges context asynchronously within an ISR. - Lock-Free Concurrency: Because the ISR only writes to the
headand increments it, and the main thread only reads from thetailand increments it, the structure remains inherently thread-safe without the use of heavy mutexes or disabling global interrupts, keeping critical sections lightning fast.
The protocol frame format is explicitly defined to guarantee data framing and boundary alignment:
+--------------+------------------+-----------------------+--------------------+--------------+
| SOF (1 Byte) | Length (1 Byte) | Payload (N Bytes) | CRC-16 (2 Bytes) | EOF (1 Byte) |
| 0x02 | Length of Data | Actual Serial Data | CCITT Checksum | 0x03 |
+--------------+------------------+-----------------------+--------------------+--------------+
The parser iterates through a non-blocking execution cycle tracking these states:
STATE_WAIT_SOF: Discards all line noise or stray bytes until a valid Start of Frame (0x02) is identified.STATE_GET_LENGTH: Stores the dynamic payload size and checks it against maximum hardware buffer bounds to prevent buffer overflow vulnerabilities.STATE_GET_PAYLOAD: Streams incoming bytes into an internal packet buffer without blocking the runtime engine.STATE_GET_CRC_HIGH/STATE_GET_CRC_LOW: Reconstructs the 16-bit incoming network byte-ordered checksum.STATE_WAIT_EOF: Validates the End of Frame byte boundary (0x03). If validated, it runs the mathematical CRC calculation over the accumulated payload. If the checksum matches, the data packet is dispatched to the system application layer.
#ifndef RING_BUFFER_H
#define RING_BUFFER_H
#include <stdint.h>
#include <stdbool.h>
#define RING_BUFFER_SIZE 128 // Must be power of 2 for fast optimization
typedef struct {
uint8_t buffer[RING_BUFFER_SIZE];
volatile uint16_t head; // Handled by incoming Hardware ISR
volatile uint16_t tail; // Handled by main foreground parsing loop
} ring_buffer_t;
void ring_buffer_init(ring_buffer_t *rb);
bool ring_buffer_is_full(const ring_buffer_t *rb);
bool ring_buffer_is_empty(const ring_buffer_t *rb);
bool ring_buffer_queue(ring_buffer_t *rb, uint8_t byte);
bool ring_buffer_dequeue(ring_buffer_t *rb, uint8_t *byte);
#endif// Standard CRC-16-CCITT implementation using the 0x1021 generator polynomial
uint16_t crc16_ccitt(const uint8_t *data, uint16_t length) {
uint16_t crc = 0xFFFF; // Preset initial value
for (uint16_t i = 0; i < length; i++) {
crc ^= (uint16_t)data[i] << 8;
for (uint8_t bit = 0; bit < 8; bit++) {
if (crc & 0x8000) {
crc = (crc << 1) ^ 0x1021;
} else {
crc <<= 1;
}
}
}
return crc;
}The system is built alongside a behavioral validation test bench in src/main.c. It injects simulated raw byte arrays mimicking normal operations and transmission interference:
- Test Case 1 (Nominal Operation): Transmits a clean packet payload
[0x41, 0x42, 0x43]packed with matching boundaries and correct CRC calculations. - Test Case 2 (Line Noise / Data Corruption): Simulates electronic interference over a physical line by altering data contents directly inside the ring buffer memory space before processing.
When evaluated in a cloud Linux environment via GCC, the runtime execution provides these metrics:
Embedded Serial Parser Simulation Initialized.
--- Simulating Incoming Serial Transmission ---
[PARSER SUCCESS] Valid Packet Received! Length: 3
--- Simulating Incoming Serial Transmission ---
[PARSER ERROR] CRC Mismatch! Expected: 0x4B3A, Got: 0x5A42
The logs prove that the state machine intercepts data corruption errors, drops the invalid frame packet, resets its internal registers instantly, and prepares to read the next transmission string without blocking application performance.
circular-buffer-serial-parser/
│
├── src/ # Implementation source files (.c)
│ ├── main.c # Behavioral simulation test bench
│ ├── ring_buffer.c # Circular queue read/write logic
│ └── parser.c # Finite State Machine & CRC-16 verification
│
├── include/ # Modular structure interface headers (.h)
│ ├── ring_buffer.h # Ring buffer configuration macros and type structures
│ └── parser.h # Parser operational states and protocol tokens
│
├── .gitignore # Tells git which compiled binaries/junk files to ignore
├── LICENSE # Open-source MIT software license
└── README.md # Production-grade system documentation