CodexPadFrameDecoder Arduino Lib 1.0.3
Loading...
Searching...
No Matches
robust_frame.h
Go to the documentation of this file.
1#pragma once
2
3#ifndef _ROBUST_FRAME_H_
4#define _ROBUST_FRAME_H_
5
6#include <stddef.h>
7#include <stdint.h>
8
9/**
10 * @file robust_frame.h
11 */
12
13namespace robust_frame {
14/**
15 * @~Chinese
16 * @class Parser
17 * @brief robust_frame 协议帧解析器。
18 * @details 负责帧同步、转义还原和 CRC 校验,通过回调输出完整的有效载荷。
19 * 不依赖任何特定通信方式,仅需通过 Feed() 逐字节喂入数据。
20 * 最大载荷长度由构造函数参数指定,内部缓冲区按需动态分配。
21 */
22/**
23 * @~English
24 * @class Parser
25 * @brief robust_frame protocol frame parser (receiver only).
26 * @details Handles frame synchronization, escape reversal, and CRC verification,
27 * outputting complete payloads via callback. Does not depend on any specific
28 * communication method; only requires feeding bytes one at a time via Feed().
29 * The maximum payload size is specified via the constructor parameter, and
30 * the internal buffer is dynamically allocated accordingly.
31 */
32class Parser {
33 public:
34 /**
35 * @~Chinese
36 * @brief 帧解析回调函数类型。
37 * @param data 有效载荷数据指针(不含帧头帧尾和 CRC)。
38 * @param user_data 用户自定义上下文指针,由构造函数传入,在回调中原样返回。
39 */
40 /**
41 * @~English
42 * @brief Frame parse callback function type.
43 * @param data Pointer to the payload data (excluding frame header, footer, and CRC).
44 * @param user_data User-defined context pointer, passed in via the constructor and returned as-is in the callback.
45 */
46 using FrameCallback = void (*)(const uint8_t *data, void *user_data);
47
48 /**
49 * @~Chinese
50 * @brief 构造函数,根据最大载荷长度动态分配内部缓冲区。
51 * @param[in] max_payload_size 最大有效载荷长度(字节数),不含 CRC。
52 * 内部实际分配的缓冲区大小为 max_payload_size + 1,
53 * 额外 1 字节用于存储 CRC 校验值。
54 * @param[in] callback 帧解析成功时的回调函数(若为 nullptr 则不回调)。
55 * @param[in] user_data 回调时传入的用户自定义上下文指针(通常传 this 指针)。
56 */
57 /**
58 * @~English
59 * @brief Constructor. Dynamically allocates the internal buffer based on the maximum payload size.
60 * @param[in] max_payload_size Maximum payload size in bytes (excluding CRC).
61 * The actual allocated buffer size is max_payload_size + 1,
62 * where the extra byte is used to store the CRC checksum.
63 * @param[in] callback Callback function invoked when a frame is successfully parsed (no callback if nullptr).
64 * @param[in] user_data User-defined context pointer passed through to the callback (typically a `this` pointer).
65 */
66 Parser(const size_t max_payload_size, FrameCallback callback, void *user_data);
67
68 /**
69 * @~Chinese
70 * @brief 析构函数,释放内部动态分配的缓冲区。
71 */
72 /**
73 * @~English
74 * @brief Destructor. Frees the internally allocated buffer.
75 */
76 ~Parser();
77
78 /**
79 * @~Chinese
80 * @brief 处理单个字节的核心状态机。
81 * @param[in] byte 输入的字节。
82 *
83 * @details 状态转换图:
84 * - kSyncHeader:等待帧头 0xAA,收到后进入 kCollectPayload。
85 * - kCollectPayload:
86 * - 收到 0x55(帧尾)→ 校验 CRC,若通过则回调有效载荷,然后 Reset。
87 * - 收到 0xDB(转义字符)→ 进入 kUnescape。
88 * - 收到 0xAA(意外帧头)或缓冲区满 → Reset 并重新从该字节开始同步。
89 * - 其他 → 存入内部缓冲区。
90 * - kUnescape:将字节与 0x20 异或还原,检查结果是否为合法特殊字符(0xAA/0x55/0xDB),
91 * 若不是则丢弃当前帧并重新同步;否则存储还原后的字节并回到 kCollectPayload。
92 *
93 * @note 缓冲区满或遇到意外的帧头会触发 Reset 并从当前字节重新尝试同步。
94 */
95 /**
96 * @~English
97 * @brief Core state machine for processing a single byte.
98 * @param[in] byte The input byte.
99 *
100 * @details State transition diagram:
101 * - kSyncHeader: Wait for frame header 0xAA, enter kCollectPayload upon receiving it.
102 * - kCollectPayload:
103 * - Receive 0x55 (frame footer) → Verify CRC, callback payload if valid, then Reset.
104 * - Receive 0xDB (escape character) → Enter kUnescape.
105 * - Receive 0xAA (unexpected frame header) or buffer full → Reset and resync starting from this byte.
106 * - Otherwise → Store into internal buffer.
107 * - kUnescape: XOR the byte with 0x20 to restore, check if the result is a valid special character
108 * (0xAA/0x55/0xDB). If not, discard the current frame and resync; otherwise store the restored byte
109 * and return to kCollectPayload.
110 *
111 * @note Buffer full or encountering an unexpected frame header will trigger Reset and attempt resync from the
112 * current byte.
113 */
114 void Feed(const uint8_t byte);
115
116 private:
117 Parser(const Parser &) = delete;
118 Parser &operator=(const Parser &) = delete;
119
120 enum class State {
121 kSyncHeader,
122 kCollectPayload,
123 kUnescape,
124 };
125
126 void Reset();
127
128 const size_t max_payload_size_ = 0;
129 const FrameCallback callback_;
130 const void *const user_data_ = nullptr;
131 uint8_t *const buffer_ = nullptr;
132 uint8_t buffer_len_ = 0;
133 State state_ = State::kSyncHeader;
134};
135} // namespace robust_frame
136
137#endif // _ROBUST_FRAME_H_
void(*)(const uint8_t *data, void *user_data) FrameCallback
Frame parse callback function type.
~Parser()
Destructor. Frees the internally allocated buffer.
Parser(const size_t max_payload_size, FrameCallback callback, void *user_data)
Constructor. Dynamically allocates the internal buffer based on the maximum payload size.
void Feed(const uint8_t byte)
Core state machine for processing a single byte.