A lightweight, portable UART communication protocol stack implementing TCP-like reliable transmission with guaranteed delivery, ordering, and deduplication. Also supports UDP-like unreliable transmission.
- TCP-like Reliable Transmission: Guarantees message delivery, ordering, and deduplication
- UDP-like Unreliable Transmission: Fast, best-effort delivery without acknowledgment
- Platform Independent: No system-specific dependencies, fully portable
- Minimal Footprint: Suitable for embedded systems with limited resources
- Thread-Safe: Optional semaphore support for concurrent access
- CRC16 Checksum: Data integrity verification
- Automatic Retransmission: Configurable retry mechanism for reliable mode
|--6byte-|-1byte-|-1byte-|-2byte-|-2byte-|-2byte-|-2byte-|-N byte--|
|"uArTcP"| seq | ctrl | cmd | crc16 | len |cs(len)|payload |
- sync: Magic bytes "uArTcP" for frame synchronization
- seq: Sequence number for ordering and deduplication
- ctrl: Control flags (ACK, ACKED, NACK)
- cmd: Command identifier [1-10000]
- crc16: CRC16 checksum of entire frame
- len: Payload length
- cs(len): CRC16 checksum of payload length
- payload: Message data
#include "uni_communication.h"
CommProtocolHooks hooks = {
.malloc_fn = malloc,
.free_fn = free,
.realloc_fn = realloc,
.msleep_fn = my_sleep_ms,
// Optional: for better performance
.sem_alloc_fn = my_sem_alloc,
.sem_destroy_fn = my_sem_destroy,
.sem_init_fn = my_sem_init,
.sem_post_fn = my_sem_post,
.sem_wait_fn = my_sem_wait,
.sem_timedwait_fn = my_sem_timedwait
};
CommProtocolRegisterHooks(&hooks);int uart_write(char *buf, unsigned int len) {
// Your UART write implementation
return write(uart_fd, buf, len);
}
void on_packet_received(CommPacket *packet) {
printf("Received cmd=%d, len=%d\n", packet->cmd, packet->payload_len);
// Process packet->payload
}
CommProtocolInit(uart_write, on_packet_received);// In your UART receive thread/interrupt
void uart_rx_handler(unsigned char *data, int len) {
CommProtocolReceiveUartData(data, len);
}#define CMD_HEARTBEAT 100
typedef struct {
uint32_t timestamp;
uint16_t counter;
} heartbeat_t;
heartbeat_t hb = {.timestamp = get_time(), .counter = 1};
// Reliable transmission (TCP-like)
int ret = CommProtocolPacketAssembleAndSend(CMD_HEARTBEAT, (char*)&hb, sizeof(hb), 1);
// Unreliable transmission (UDP-like)
ret = CommProtocolPacketAssembleAndSend(CMD_HEARTBEAT, (char*)&hb, sizeof(hb), 0);Register platform-specific functions for memory allocation, synchronization, and sleep.
Parameters:
hooks: Pointer to hooks structure
Required hooks:
malloc_fn,free_fn,realloc_fn,msleep_fn
Optional hooks (recommended):
- Semaphore functions for thread safety
Initialize the protocol stack.
Parameters:
write_handler: UART write function callbackrecv_handler: Packet receive callback
Returns: 0 on success, -1 on failure
Send a packet through the protocol stack.
Parameters:
cmd: Command ID [1-10000]payload: Message data (NULL if no payload)payload_len: Payload length (0 if no payload)mode: 1 for reliable (TCP-like), 0 for unreliable (UDP-like)
Returns:
0: SuccessE_UNI_COMM_ALLOC_FAILED: Memory allocation failedE_UNI_COMM_PAYLOAD_TOO_LONG: Payload exceeds max size (8KB)E_UNI_COMM_PAYLOAD_ACK_TIMEOUT: ACK timeout in reliable mode
Feed received UART data into the protocol stack for parsing.
Parameters:
buf: Received data bufferlen: Data length
Cleanup and release all resources. Should rarely be called in embedded systems.
Edit uni_communication.c to adjust:
#define PROTOCOL_BUF_SUPPORT_MAX_SIZE (8192) // Max packet size
#define WAIT_ACK_TIMEOUT_MSEC (200) // ACK timeout
#define TRY_RESEND_TIMES (5) // Retry countSee example/ directory:
linux_posix_demo.c- Linux/POSIX systemsrt_thread_demo.c- RT-Thread RTOS8051_demo.c- 8051 microcontroller
- With semaphore hooks: Fully thread-safe for concurrent send/receive
- Without semaphore hooks: User must ensure external synchronization
At 921600 bps with 512-byte payload:
- Bandwidth utilization: ~80% (90 KB/s)
- Window size: 1 (stop-and-wait ARQ)
int ret = CommProtocolPacketAssembleAndSend(cmd, data, len, 1);
switch (ret) {
case 0:
// Success
break;
case E_UNI_COMM_ALLOC_FAILED:
// Out of memory
break;
case E_UNI_COMM_PAYLOAD_TOO_LONG:
// Payload > 8KB
break;
case E_UNI_COMM_PAYLOAD_ACK_TIMEOUT:
// No ACK received after retries
break;
}CommProtocolPacketAssembleAndSend inside the receive callback - use async mechanism (separate thread/task) to avoid deadlock.
# Include in your project
gcc -I./protocol -c protocol/uni_communication.c -o uni_communication.o
# Link with your application
gcc your_app.c uni_communication.o -o your_appGPL v2 - See LICENSE file for details.
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Submit a pull request
- Junlon - junlon2006@163.com
- Refactored protocol stack
- Added payload length CRC16 verification
- Improved memory management
- Enhanced portability