arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
HDLCStream.h
1#include "AudioToolsConfig.h"
2#include "AudioTools/CoreAudio/Buffers.h"
3#include <stdbool.h>
4#include <stdint.h>
5
8#define FRAME_BOUNDARY_OCTET 0x7E
9
11#define CONTROL_ESCAPE_OCTET 0x7D
12
15#define INVERT_OCTET 0x20
16
20#define CRC16_CCITT_INIT_VAL 0xFFFF
21
23#define low(x) ((x)&0xFF)
24#define high(x) (((x) >> 8) & 0xFF)
25#define lo8(x) ((x)&0xff)
26#define hi8(x) ((x) >> 8)
27
28namespace audio_tools {
29
36class HDLCStream : public Stream {
37public:
39 HDLCStream(Print &out, uint16_t max_frame_length) {
40 setOutput(out);
41 this->max_frame_length = max_frame_length;
42 begin();
43 }
44
46 HDLCStream(Stream &io, uint16_t max_frame_length) {
47 setStream(io);
48 this->max_frame_length = max_frame_length;
49 begin();
50 }
51
52 bool begin() {
53 this->frame_position = 0;
54 this->frame_checksum = CRC16_CCITT_INIT_VAL;
55 this->escape_character = false;
56 if (frame_buffer.size() == 0) {
57 frame_buffer.resize(max_frame_length + 1);
58 }
59 return p_out != nullptr || p_in != nullptr;
60 }
61
62 void end() { this->frame_buffer.resize(0); }
63
64 int availableForWrite() override {
65 return p_out == nullptr ? 0 : DEFAULT_BUFFER_SIZE;
66 }
67
69 size_t write(const uint8_t *data, size_t len) override {
70 LOGD("HDLCStream::write: %zu", len);
71
72 for (int j = 0; j < len; j++) {
73 bool ok = frame_buffer.write(data[j]);
74 assert(ok);
75 if (frame_buffer.available() == max_frame_length) {
76 sendFrame(frame_buffer.data(), max_frame_length);
77 frame_buffer.reset();
78 }
79 }
80 return len;
81 }
82
83 int available() override {
84 return p_in == nullptr ? 0 : max_frame_length;
85 }
86
88 size_t readBytes(uint8_t *data, size_t len) override {
89 if (p_in == nullptr) {
90 LOGI("No data source");
91 return 0;
92 }
93
94 int result = 0;
95 // process bytes from input
96 while (result == 0) {
97 int ch = p_in->read();
98 // ch is -1 when no data
99 if (ch >= 0){
100 result = charReceiver(ch);
101 if (result > 0) {
102 result = frame_buffer.readArray(data, result);
103 break;
104 }
105 } else {
106 break;
107 }
108 }
109 LOGD("HDLCStream::readBytes: %zu -> %d", len, result);
110 return result;
111 ;
112 }
113
114 void setStream(Stream &io) {
115 p_out = &io;
116 p_in = &io;
117 }
118
119 void setStream(Print &out) { p_out = &out; }
120
121 void setOutput(Print &out) { p_out = &out; }
122
123 size_t write(uint8_t ch) override { return write(&ch, 1); };
124
125 int read() override {
126 uint8_t c[1];
127 if (readBytes(c, 1) == 0) {
128 return -1;
129 }
130 return c[0];
131 }
132
134 int peek() override { return -1; }
135
136private:
137 Print *p_out = nullptr;
138 Stream *p_in = nullptr;
139 bool escape_character = false;
140 SingleBuffer<uint8_t> frame_buffer{0};
141 uint8_t frame_position = 0;
142 // 16bit CRC sum for _crc_ccitt_update
143 uint16_t frame_checksum;
144 uint16_t max_frame_length;
145
147 int charReceiver(uint8_t data) {
148 int result = 0;
149 uint8_t *frame_buffer_data = frame_buffer.address();
150 LOGD("charReceiver: %c", data);
151
152 if (data == FRAME_BOUNDARY_OCTET) {
153 if (escape_character == true) {
154 escape_character = false;
155 } else if ((frame_position >= 2) &&
156 (frame_checksum ==
157 ((frame_buffer_data[frame_position - 1] << 8) |
158 (frame_buffer_data[frame_position - 2] & 0xff)))) {
159 // trigger processing of result data
160 frame_buffer.setAvailable(frame_position - 2);
161 result = frame_buffer.available();
162 LOGD("==> Result: %d", result);
163 }
164 frame_position = 0;
165 frame_checksum = CRC16_CCITT_INIT_VAL;
166 return result;
167 }
168
169 if (escape_character) {
170 escape_character = false;
171 data ^= INVERT_OCTET;
172 } else if (data == CONTROL_ESCAPE_OCTET) {
173 escape_character = true;
174 return result;
175 }
176
177 frame_buffer_data[frame_position] = data;
178
179 if (frame_position - 2 >= 0) {
180 frame_checksum = _crc_ccitt_update(frame_checksum,
181 frame_buffer_data[frame_position - 2]);
182 }
183
184 frame_position++;
185
186 if (frame_position == max_frame_length) {
187 frame_position = 0;
188 frame_checksum = CRC16_CCITT_INIT_VAL;
189 }
190 return result;
191 }
192
194 void sendFrame(const uint8_t *framebuffer, size_t frame_length) {
195 LOGD("HDLCStream::sendFrame: %zu", frame_length);
196 uint8_t data;
197 uint16_t fcs = CRC16_CCITT_INIT_VAL;
198
199 p_out->write((uint8_t)FRAME_BOUNDARY_OCTET);
200
201 while (frame_length) {
202 data = *framebuffer++;
203 fcs = HDLCStream::_crc_ccitt_update(fcs, data);
204 if ((data == CONTROL_ESCAPE_OCTET) || (data == FRAME_BOUNDARY_OCTET)) {
205 p_out->write((uint8_t)CONTROL_ESCAPE_OCTET);
206 data ^= INVERT_OCTET;
207 }
208 p_out->write((uint8_t)data);
209 frame_length--;
210 }
211 data = low(fcs);
212 if ((data == CONTROL_ESCAPE_OCTET) || (data == FRAME_BOUNDARY_OCTET)) {
213 p_out->write((uint8_t)CONTROL_ESCAPE_OCTET);
214 data ^= (uint8_t)INVERT_OCTET;
215 }
216 p_out->write((uint8_t)data);
217 data = high(fcs);
218 if ((data == CONTROL_ESCAPE_OCTET) || (data == FRAME_BOUNDARY_OCTET)) {
219 p_out->write(CONTROL_ESCAPE_OCTET);
220 data ^= INVERT_OCTET;
221 }
222 p_out->write(data);
223 p_out->write(FRAME_BOUNDARY_OCTET);
224 p_out->flush();
225 }
226
227 static uint16_t crc16_update(uint16_t crc, uint8_t a) {
228 int i;
229 crc ^= a;
230 for (i = 0; i < 8; ++i) {
231 if (crc & 1)
232 crc = (crc >> 1) ^ 0xA001;
233 else
234 crc = (crc >> 1);
235 }
236 return crc;
237 }
238
239 static uint16_t crc_xmodem_update(uint16_t crc, uint8_t data) {
240 int i;
241
242 crc = crc ^ ((uint16_t)data << 8);
243 for (i = 0; i < 8; i++) {
244 if (crc & 0x8000)
245 crc = (crc << 1) ^ 0x1021;
246 else
247 crc <<= 1;
248 }
249
250 return crc;
251 }
252
253 static uint16_t _crc_ccitt_update(uint16_t crc, uint8_t data) {
254 data ^= lo8(crc);
255 data ^= data << 4;
256 return ((((uint16_t)data << 8) | hi8(crc)) ^ (uint8_t)(data >> 4) ^
257 ((uint16_t)data << 3));
258 }
259
260 static uint8_t _crc_ibutton_update(uint8_t crc, uint8_t data) {
261 uint8_t i;
262 crc = crc ^ data;
263 for (i = 0; i < 8; i++) {
264 if (crc & 0x01)
265 crc = (crc >> 1) ^ 0x8C;
266 else
267 crc >>= 1;
268 }
269 return crc;
270 }
271};
272
273} // namespace audio_tools
virtual int readArray(T data[], int len)
reads multiple values
Definition Buffers.h:37
High-Level Data Link Control (HDLC) is a bit-oriented code-transparent synchronous data link layer pr...
Definition HDLCStream.h:36
HDLCStream(Print &out, uint16_t max_frame_length)
Defines the output for the hdlc encoding.
Definition HDLCStream.h:39
HDLCStream(Stream &io, uint16_t max_frame_length)
Defines the input for the hdlc decoding.
Definition HDLCStream.h:46
size_t readBytes(uint8_t *data, size_t len) override
Provides the decoded data.
Definition HDLCStream.h:88
int peek() override
not supported
Definition HDLCStream.h:134
size_t write(const uint8_t *data, size_t len) override
Sends the encoded data to the defined output.
Definition HDLCStream.h:69
Definition NoArduino.h:62
A simple Buffer implementation which just uses a (dynamically sized) array.
Definition Buffers.h:175
size_t setAvailable(size_t available_size)
Definition Buffers.h:273
bool write(T sample) override
write add an entry to the buffer
Definition Buffers.h:200
int available() override
provides the number of entries that are available to read
Definition Buffers.h:227
T * address() override
Provides address to beginning of the buffer.
Definition Buffers.h:258
T * data()
Provides address of actual data.
Definition Buffers.h:261
void reset() override
clears the buffer
Definition Buffers.h:263
Definition NoArduino.h:142
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10