MySensors Library & Examples  2.3.2-62-ge298769
ThroughSerial.h
1 
2 /* ThroughSerial data link layer
3  used as a Strategy by the PJON framework (included in version v11.2)
4  Compliant with TSDL (Tardy Serial Data Link) specification v2.0
5 
6  Contributors:
7  - sticilface async reception development
8  - Fred Larsen, Development, testing and debugging
9  - Zbigniew Zasieczny, collision avoidance multi-drop RS485 (latency)
10  and SoftwareSerial compatibility
11  - Franketto (Arduino forum user) RS485 TX enable pin compatibility
12  - Endre Karlson separate RS485 enable pins handling, flush timing hack
13  ___________________________________________________________________________
14 
15  ThroughSerial, based on ThroughSerial, developed by sticilface
16  copyright 2018 by Giovanni Blu Mitolo All rights reserved
17 
18  Licensed under the Apache License, Version 2.0 (the "License");
19  you may not use this file except in compliance with the License.
20  You may obtain a copy of the License at
21 
22  http://www.apache.org/licenses/LICENSE-2.0
23 
24  Unless required by applicable law or agreed to in writing, software
25  distributed under the License is distributed on an "AS IS" BASIS,
26  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
27  See the License for the specific language governing permissions and
28  limitations under the License. */
29 
30 #pragma once
31 
32 // START symbol 10010101 - 0x95 - •
33 #define TS_START 149
34 // END symbol 11101010 - 0xea - ê
35 #define TS_END 234
36 // ESCAPE symbol 10111011 - 0xBB - »
37 #define TS_ESC 187
38 
39 // Used to signal communication failure
40 #define TS_FAIL 65535
41 // Used for unused pin handling
42 #define TS_NOT_ASSIGNED 255
43 
44 #include "Timing.h"
45 
46 enum TS_state_t : uint8_t {
47  TS_WAITING,
48  TS_RECEIVING,
49  TS_WAITING_ESCAPE,
50  TS_WAITING_END,
51  TS_DONE
52 };
53 
54 // Recommended receive time for this strategy, in microseconds
55 #ifndef TS_RECEIVE_TIME
56 #define TS_RECEIVE_TIME 0
57 #endif
58 
60 {
61 public:
62  uint8_t buffer[PJON_PACKET_MAX_LENGTH] = {0};
63  uint16_t position = 0;
64  TS_state_t state = TS_WAITING;
65  PJON_SERIAL_TYPE serial;
66 
67  /* Returns suggested delay related to the attempts passed as parameter: */
68 
69  uint32_t back_off(uint8_t attempts)
70  {
71  uint32_t result = attempts;
72  for(uint8_t d = 0; d < TS_BACK_OFF_DEGREE; d++) {
73  result *= (uint32_t)(attempts);
74  }
75  return result + PJON_RANDOM(TS_COLLISION_DELAY);
76  };
77 
78 
79  /* Begin method, to be called on initialization:
80  (returns always true) */
81 
82  bool begin(uint8_t did = 0)
83  {
84  PJON_DELAY(PJON_RANDOM(TS_INITIAL_DELAY) + did);
85  _last_reception_time = PJON_MICROS();
86  return true;
87  };
88 
89 
90  /* Check if the channel is free for transmission: */
91 
92  bool can_start()
93  {
94  PJON_DELAY_MICROSECONDS(PJON_RANDOM(TS_COLLISION_DELAY));
95  if(
96  (state != TS_WAITING) ||
97  PJON_SERIAL_AVAILABLE(serial) ||
98  ((uint32_t)(PJON_MICROS() - _last_reception_time) < TS_TIME_IN)
99  ) {
100  return false;
101  }
102  return true;
103  };
104 
105 
106  /* Function called when a frame reception fails */
107 
108  uint16_t fail(TS_state_t s)
109  {
110  state = s;
111  return (uint16_t)TS_FAIL;
112  }
113 
114 
115  /* Returns the maximum number of attempts for each transmission: */
116 
117  static uint8_t get_max_attempts()
118  {
119  return TS_MAX_ATTEMPTS;
120  };
121 
122 
123  /* Returns the recommended receive time for this strategy: */
124 
125  static uint16_t get_receive_time()
126  {
127  return TS_RECEIVE_TIME;
128  };
129 
130 
131  /* Handle a collision: */
132 
133  void handle_collision()
134  {
135  PJON_DELAY_MICROSECONDS(PJON_RANDOM(TS_COLLISION_DELAY));
136  };
137 
138 
139  /* Receive Byte */
140 
141  int16_t receive_byte()
142  {
143  int16_t value = PJON_SERIAL_READ(serial);
144  if(value == -1) {
145  return -1;
146  }
147  _last_reception_time = PJON_MICROS();
148  return value;
149  };
150 
151 
152  /* It returns the state of the previous transmission: */
153 
154  uint16_t receive_response()
155  {
156  if(_fail) {
157  return TS_FAIL;
158  }
159  uint32_t time = PJON_MICROS();
160  uint8_t i = 0;
161  while((uint32_t)(PJON_MICROS() - time) < TS_RESPONSE_TIME_OUT) {
162  if(PJON_SERIAL_AVAILABLE(serial)) {
163  int16_t read = PJON_SERIAL_READ(serial);
164  _last_reception_time = PJON_MICROS();
165  if(read >= 0) {
166  if(_response[i++] != read) {
167  return TS_FAIL;
168  }
169  if(i == TS_RESPONSE_LENGTH) {
170  return PJON_ACK;
171  }
172  }
173  }
174 #if defined(_WIN32)
175  PJON_DELAY_MICROSECONDS(TS_RESPONSE_TIME_OUT / 10);
176 #endif
177  }
178  return TS_FAIL;
179  };
180 
181 
182  /* Receive a string: */
183 
184  uint16_t receive_frame(uint8_t *data, uint16_t max_length)
185  {
186  if( // Reception attempts are spaced by an interval
187  _last_call_time &&
188  (uint32_t)(PJON_MICROS() - _last_call_time) < _read_interval
189  ) {
190  return TS_FAIL;
191  }
192 
193  _last_call_time = PJON_MICROS();
194 
195  if( // If reception timeout is reached discard data
196  (
197  (state == TS_RECEIVING) ||
198  (state == TS_WAITING_END) ||
199  (state == TS_WAITING_ESCAPE)
200  ) &&
201  ((uint32_t)(PJON_MICROS() - _last_reception_time) > TS_BYTE_TIME_OUT)
202  ) {
203  return fail(TS_WAITING);
204  }
205 
206  switch(state) {
207  case TS_WAITING: {
208  while(PJON_SERIAL_AVAILABLE(serial)) {
209  int16_t value = receive_byte();
210  if(value == -1) {
211  return TS_FAIL;
212  }
213  if(value == TS_START) {
214  position = 0;
215  return fail(TS_RECEIVING);
216  }
217  };
218  break;
219  }
220  case TS_RECEIVING: {
221  while(PJON_SERIAL_AVAILABLE(serial)) {
222  int16_t value = receive_byte();
223  if((value == TS_START) || (value == -1)) {
224  return fail(TS_WAITING);
225  }
226  if(value == TS_ESC) {
227  if(!PJON_SERIAL_AVAILABLE(serial)) {
228  return fail(TS_WAITING_ESCAPE);
229  } else {
230  value = receive_byte();
231  if(value == -1) {
232  return fail(TS_WAITING);
233  }
234  value = value ^ TS_ESC;
235  if(
236  (value != TS_START) &&
237  (value != TS_ESC) &&
238  (value != TS_END)
239  ) {
240  return fail(TS_WAITING);
241  }
242  buffer[position++] = (uint8_t)value;
243  continue;
244  }
245  }
246  if(max_length == 1) {
247  return fail(TS_WAITING_END);
248  }
249  if(position + 1 >= PJON_PACKET_MAX_LENGTH) {
250  return fail(TS_WAITING);
251  }
252  if(value == TS_END) {
253  return fail(TS_DONE);
254  }
255  buffer[position++] = (uint8_t)value;
256  }
257  return TS_FAIL;
258  }
259 
260  case TS_WAITING_ESCAPE: {
261  if(PJON_SERIAL_AVAILABLE(serial)) {
262  int16_t value = receive_byte();
263  if(value == -1) {
264  return fail(TS_WAITING);
265  }
266  value = value ^ TS_ESC;
267  if(
268  (value != TS_START) &&
269  (value != TS_ESC) &&
270  (value != TS_END)
271  ) {
272  return fail(TS_WAITING);
273  }
274  buffer[position++] = (uint8_t)value;
275  return fail(TS_RECEIVING);
276  }
277  break;
278  }
279 
280  case TS_WAITING_END: {
281  if(PJON_SERIAL_AVAILABLE(serial)) {
282  int16_t value = receive_byte();
283  if(value == -1) {
284  return fail(TS_WAITING);
285  }
286  if(value == TS_END) {
287  return fail(TS_DONE);
288  } else {
289  return fail(TS_WAITING);
290  }
291  }
292  break;
293  }
294 
295  case TS_DONE: {
296  memcpy(&data[0], &buffer[0], position);
297  prepare_response(buffer, position);
298  state = TS_WAITING;
299  return position;
300  }
301 
302  };
303  return TS_FAIL;
304  };
305 
306 
307  /* Send a byte and wait for its transmission end */
308 
309  void send_byte(uint8_t b)
310  {
311  uint32_t time = PJON_MICROS();
312  int16_t result = 0;
313  while(
314  ((result = PJON_SERIAL_WRITE(serial, b)) != 1) &&
315  ((uint32_t)(PJON_MICROS() - time) < TS_BYTE_TIME_OUT)
316  );
317  if(result != 1) {
318  _fail = true;
319  }
320  };
321 
322 
323  /* The last 5 bytes of the frame are used as a unique identifier within
324  the response. PJON has CRC8 or CRC32 at the end of the packet, encoding
325  a CRC (that is a good hashing algorithm) and using 40 bits looks enough
326  to provide a relatively safe response that should be nearly flawless
327  (yield few false positives per millennia). */
328 
329  void prepare_response(const uint8_t *buffer, uint16_t position)
330  {
331  uint8_t raw = 0;
332  for(int8_t i = 0; i < TS_RESPONSE_LENGTH; i++) {
333  raw = buffer[(position - ((TS_RESPONSE_LENGTH - 1) - i)) - 1];
334  _response[i] = (
335  (raw == TS_START) || (raw == TS_ESC) || (raw == TS_END)
336  ) ? (raw - 1) : raw; // Avoid encoding symbols
337  }
338  };
339 
340 
341  /* Send byte response to the packet's transmitter */
342 
343  void send_response(uint8_t response)
344  {
345  if(response == PJON_ACK) {
346  start_tx();
347  wait_RS485_pin_change();
348  for(uint8_t i = 0; i < TS_RESPONSE_LENGTH; i++) {
349  send_byte(_response[i]);
350  }
351  PJON_SERIAL_FLUSH(serial);
352  wait_RS485_pin_change();
353  end_tx();
354  }
355  };
356 
357 
358  /* Send a string: */
359 
360  void send_frame(uint8_t *data, uint16_t length)
361  {
362  _fail = false;
363  start_tx();
364  uint16_t overhead = 2;
365  // Add frame flag
366  send_byte(TS_START);
367  for(uint16_t b = 0; b < length; b++) {
368  if(_fail) {
369  return;
370  }
371  // Byte-stuffing
372  if(
373  (data[b] == TS_START) ||
374  (data[b] == TS_ESC) ||
375  (data[b] == TS_END)
376  ) {
377  send_byte(TS_ESC);
378  send_byte(data[b] ^ TS_ESC);
379  overhead++;
380  } else {
381  send_byte(data[b]);
382  }
383  }
384  send_byte(TS_END);
385  /* On RPI flush fails to wait until all bytes are transmitted
386  here RPI forced to wait blocking using delayMicroseconds */
387 #if defined(RPI) || defined(LINUX)
388  if(_bd)
389  PJON_DELAY_MICROSECONDS(
390  ((1000000 / (_bd / 8)) + _flush_offset) * (overhead + length)
391  );
392 #endif
393  PJON_SERIAL_FLUSH(serial);
394  end_tx();
395  // Prepare expected response for the receive_response call
396  prepare_response(data, length);
397  };
398 
399 
400  /* Pass the Serial port where you want to operate with */
401 
402  void set_serial(PJON_SERIAL_TYPE serial_port)
403  {
404  serial = serial_port;
405  };
406 
407 
408  /* RS485 enable pins handling: */
409 
410  void start_tx()
411  {
412  if(_enable_RS485_txe_pin != TS_NOT_ASSIGNED) {
413  PJON_IO_WRITE(_enable_RS485_txe_pin, HIGH);
414  if(_enable_RS485_rxe_pin != TS_NOT_ASSIGNED) {
415  PJON_IO_WRITE(_enable_RS485_rxe_pin, HIGH);
416  }
417  wait_RS485_pin_change();
418  }
419  };
420 
421  void end_tx()
422  {
423  if(_enable_RS485_txe_pin != TS_NOT_ASSIGNED) {
424  wait_RS485_pin_change();
425  PJON_IO_WRITE(_enable_RS485_txe_pin, LOW);
426  if(_enable_RS485_rxe_pin != TS_NOT_ASSIGNED) {
427  PJON_IO_WRITE(_enable_RS485_rxe_pin, LOW);
428  }
429  }
430  };
431 
432 #if defined(RPI) || defined(LINUX)
433  /* Pass baudrate to ThroughSerial
434  (needed only for RPI flush hack): */
435 
436  void set_baud_rate(uint32_t baud)
437  {
438  _bd = baud;
439  };
440 
441 
442  /* Set flush timing offset in microseconds between expected and real
443  serial byte transmission: */
444 
445  void set_flush_offset(uint16_t offset)
446  {
447  _flush_offset = offset;
448  };
449 #endif
450 
451  /* Sets the interval between each read attempt from serial
452  (TS_READ_INTERVAL or 100 microseconds by default) to allow the buffer
453  to fill and to reduce the computation time consumed while polling for
454  incoming data. */
455 
456  uint32_t get_read_interval()
457  {
458  return _read_interval;
459  };
460 
461  void set_read_interval(uint32_t t)
462  {
463  _read_interval = t;
464  };
465 
466  /* RS485 enable pins setters: */
467 
468  void set_enable_RS485_pin(uint8_t pin)
469  {
470  set_RS485_txe_pin(pin);
471  };
472 
473  void set_RS485_rxe_pin(uint8_t pin)
474  {
475  _enable_RS485_rxe_pin = pin;
476  PJON_IO_MODE(_enable_RS485_rxe_pin, OUTPUT);
477  }
478 
479  void set_RS485_txe_pin(uint8_t pin)
480  {
481  _enable_RS485_txe_pin = pin;
482  PJON_IO_MODE(_enable_RS485_txe_pin, OUTPUT);
483  }
484 
485  void wait_RS485_pin_change()
486  {
487  if(_enable_RS485_txe_pin != TS_NOT_ASSIGNED) {
488  PJON_DELAY(_RS485_delay);
489  }
490  };
491 
492 private:
493 #if defined(RPI) || defined(LINUX)
494  uint16_t _flush_offset = TS_FLUSH_OFFSET;
495  uint32_t _bd;
496 #endif
497  bool _fail = false;
498  uint8_t _response[TS_RESPONSE_LENGTH];
499  uint32_t _last_reception_time = 0;
500  uint32_t _last_call_time = 0;
501  uint8_t _enable_RS485_rxe_pin = TS_NOT_ASSIGNED;
502  uint8_t _enable_RS485_txe_pin = TS_NOT_ASSIGNED;
503  uint32_t _RS485_delay = TS_RS485_DELAY;
504  uint32_t _read_interval = TS_READ_INTERVAL;
505 };
data
char data[MAX_PAYLOAD_SIZE+1]
Buffer for raw payload data.
Definition: MyMessage.h:654
HIGH
#define HIGH
Definition: bcm2835.h:572
LOW
#define LOW
Definition: bcm2835.h:574
ThroughSerial
Definition: ThroughSerial.h:59