arduino-audio-tools
HDLCStream.h
1 #include "AudioConfig.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 
28 namespace audio_tools {
29 
36 class HDLCStream : public Stream {
37 public:
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 
136 private:
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:41
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:58
T * address() override
Provides address to beginning of the buffer.
Definition: Buffers.h:249
T * data()
Provides address of actual data.
Definition: Buffers.h:252
size_t setAvailable(size_t available_size)
Definition: Buffers.h:264
bool write(T sample) override
write add an entry to the buffer
Definition: Buffers.h:194
int available() override
provides the number of entries that are available to read
Definition: Buffers.h:219
void reset() override
clears the buffer
Definition: Buffers.h:254
Definition: NoArduino.h:125
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:821