arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Macros Modules Pages
HDLCStream.h
1#pragma once
2
3#include <stdbool.h>
4#include <stdint.h>
5
6#include "AudioTools/CoreAudio/Buffers.h"
7#include "AudioToolsConfig.h"
8
9namespace audio_tools {
10
26class HDLCStream : public Stream {
27 public:
34 HDLCStream(Stream& stream, size_t maxFrameSize)
35 : _maxFrameSize(maxFrameSize) {
36 p_stream = &stream;
37 p_print = &stream;
38 tx_frame_buffer.resize(maxFrameSize);
39 rx_frame_buffer.resize(maxFrameSize);
40 _rxBuffer.resize(maxFrameSize);
41 }
42
49 HDLCStream(Print& stream, size_t maxFrameSize) : _maxFrameSize(maxFrameSize) {
50 p_print = &stream;
51 tx_frame_buffer.resize(maxFrameSize);
52 rx_frame_buffer.resize(maxFrameSize);
53 _rxBuffer.resize(maxFrameSize);
54 }
55
61 int available() override {
62 if (rx_frame_buffer.available() == 0) _processInput();
63 return rx_frame_buffer.available();
64 }
65
71 int read() override { return -1; }
72
80 size_t readBytes(uint8_t* buffer, size_t length) override {
81 size_t available_bytes = rx_frame_buffer.available();
82 // get more data
83 if (available_bytes == 0) {
85 available_bytes = rx_frame_buffer.available();
86 }
87
88 // check that we consume the full frame
89 if (length < available_bytes) {
90 LOGE("readBytes len too small %u instead of %u", (unsigned)length,
91 (unsigned)available_bytes);
92 return 0;
93 }
94
95 // provide the data
96 memcpy(buffer, rx_frame_buffer.data(), available_bytes);
97 rx_frame_buffer.clear();
98 _frameReady = false;
99 return available_bytes;
100 }
101
107 int peek() override { return -1; }
108
112 void flush() override { p_stream->flush(); }
113
120 size_t write(uint8_t b) override { return 0; }
121
129 size_t write(const uint8_t* data, size_t len) override {
130 return writeFrame(data, len);
131 }
132
133 protected:
134 Stream* p_stream = nullptr;
135 Print* p_print = nullptr;
136 const size_t _maxFrameSize;
137 SingleBuffer<uint8_t> tx_frame_buffer;
138 SingleBuffer<uint8_t> rx_frame_buffer;
139 Vector<uint8_t> _rxBuffer;
140 size_t _frameLen = 0;
141 size_t _rxLen = 0;
142 size_t _rxPos = 0;
143 bool _frameReady = false;
144
145 enum RxState { IDLE, RECEIVING, ESCAPED } _rxState = IDLE;
146
147 static constexpr uint8_t HDLC_FLAG = 0x7E;
148 static constexpr uint8_t HDLC_ESC = 0x7D;
149 static constexpr uint8_t HDLC_ESC_XOR = 0x20;
150
158 uint16_t _crc16(uint8_t data, uint16_t crc) {
159 crc ^= (uint16_t)data << 8;
160 for (int i = 0; i < 8; i++)
161 crc = (crc & 0x8000) ? (crc << 1) ^ 0x1021 : (crc << 1);
162 return crc;
163 }
164
170 void _writeEscaped(uint8_t b) {
171 if (b == HDLC_FLAG || b == HDLC_ESC) {
172 tx_frame_buffer.write(HDLC_ESC);
173 tx_frame_buffer.write(b ^ HDLC_ESC_XOR);
174 } else {
175 tx_frame_buffer.write(b);
176 }
177 }
178
186 size_t writeFrame(const uint8_t* data, size_t len) {
187 if (!data || len == 0) return 0;
188
189 uint16_t crc = 0xFFFF;
190 tx_frame_buffer.write(HDLC_FLAG);
191
192 for (size_t i = 0; i < len; ++i) {
193 crc = _crc16(data[i], crc);
194 _writeEscaped(data[i]);
195 }
196
197 _writeEscaped(crc >> 8);
198 _writeEscaped(crc & 0xFF);
199 tx_frame_buffer.write(HDLC_FLAG);
200 p_print->write(tx_frame_buffer.data(), tx_frame_buffer.available());
201 p_print->flush();
202 tx_frame_buffer.clear();
203 return len;
204 }
205
211 while (!_frameReady && p_stream->available()) {
212 uint8_t b = p_stream->read();
213
214 if (b == HDLC_FLAG) {
215 if (_rxLen >= 3) {
216 uint16_t recvCrc =
217 (_rxBuffer[_rxLen - 2] << 8) | _rxBuffer[_rxLen - 1];
218 uint16_t calcCrc = 0xFFFF;
219 for (size_t i = 0; i < _rxLen - 2; ++i)
220 calcCrc = _crc16(_rxBuffer[i], calcCrc);
221
222 if (calcCrc == recvCrc) {
223 for (int j = 0; j < _rxLen - 2; j++) {
224 rx_frame_buffer.write(_rxBuffer[j]);
225 }
226
227 _frameLen = _rxLen - 2;
228 _rxPos = 0;
229 _frameReady = true;
230 }
231 }
232 _rxState = IDLE;
233 _rxLen = 0;
234 continue;
235 }
236
237 switch (_rxState) {
238 case IDLE:
239 _rxLen = 0;
240 if (b == HDLC_ESC) {
241 _rxState = ESCAPED;
242 } else {
243 _rxState = RECEIVING;
244 if (_rxLen < _maxFrameSize) _rxBuffer[_rxLen++] = b;
245 }
246 break;
247
248 case RECEIVING:
249 if (b == HDLC_ESC) {
250 _rxState = ESCAPED;
251 } else if (_rxLen < _maxFrameSize) {
252 _rxBuffer[_rxLen++] = b;
253 }
254 break;
255
256 case ESCAPED:
257 if (_rxLen < _maxFrameSize) {
258 _rxBuffer[_rxLen++] = b ^ HDLC_ESC_XOR;
259 }
260 _rxState = RECEIVING;
261 break;
262 }
263
264 if (_rxLen >= _maxFrameSize) {
265 _rxState = IDLE; // overflow
266 _rxLen = 0;
267 }
268 }
269 }
270};
271
272} // namespace audio_tools
void clear()
same as reset
Definition Buffers.h:95
High-Level Data Link Control (HDLC) is a bit-oriented code-transparent synchronous data link layer pr...
Definition HDLCStream.h:26
void flush() override
Flush the output buffer of the underlying stream.
Definition HDLCStream.h:112
void _processInput()
Process incoming bytes, detect frames, validate CRC and prepare data for reading.
Definition HDLCStream.h:210
int peek() override
Not supported.
Definition HDLCStream.h:107
size_t readBytes(uint8_t *buffer, size_t length) override
Read a full frame from the stream into a buffer.
Definition HDLCStream.h:80
size_t write(uint8_t b) override
Not supported.
Definition HDLCStream.h:120
size_t writeFrame(const uint8_t *data, size_t len)
Write a complete HDLC frame with proper framing and CRC.
Definition HDLCStream.h:186
int available() override
Get the number of bytes available to read from the frame buffer.
Definition HDLCStream.h:61
size_t write(const uint8_t *data, size_t len) override
Write multiple bytes to the stream.
Definition HDLCStream.h:129
void _writeEscaped(uint8_t b)
Write a byte with proper HDLC byte stuffing if needed.
Definition HDLCStream.h:170
HDLCStream(Print &stream, size_t maxFrameSize)
Construct a new HDLCStream object using a Print for output only.
Definition HDLCStream.h:49
uint16_t _crc16(uint8_t data, uint16_t crc)
Calculate CRC-CCITT (16-bit)
Definition HDLCStream.h:158
int read() override
Not supported.
Definition HDLCStream.h:71
HDLCStream(Stream &stream, size_t maxFrameSize)
Construct a new HDLCStream object using a Stream for input and output.
Definition HDLCStream.h:34
Definition NoArduino.h:62
A simple Buffer implementation which just uses a (dynamically sized) array.
Definition Buffers.h:172
bool write(T sample) override
write add an entry to the buffer
Definition Buffers.h:202
int available() override
provides the number of entries that are available to read
Definition Buffers.h:229
bool resize(int size)
Resizes the buffer if supported: returns false if not supported.
Definition Buffers.h:292
T * data()
Provides address of actual data.
Definition Buffers.h:271
Definition NoArduino.h:142
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10