MySensors Library & Examples  2.3.2-62-ge298769
AnalogSampling.h
1 
2 /* AnalogSampling data link layer 18/02/2017
3  used as a Strategy by the PJON framework
4  It complies with PJDLS (Padded Jittering Data Link byte Stuffed)
5  specification v2.0
6 
7  AnalogSampling is designed to sample digital data using analog readings.
8  It can be effectively used to communicate data wirelessly through any
9  sort of radiation transceiver.
10 
11  The most basic example is to use a couple of visible light LEDs as wireless
12  transceivers connecting to the A0 pin of 2 Arduino boards.
13 
14  Leveraging the interesting characteristics of LEDs:
15  - Emit photons if electrons are travelling through the junction
16  - Emit electrons if photons are hitting the junction, thanks to the
17  photo-electric effect
18 
19  It is possibile to use them as wireless (bidirectional) transceivers!
20 
21  The obtained range is related to:
22  - Analog reference (voltage reading resolution)
23  - LED sensitivity to the signal
24  - Available current for transmitter
25 
26  It is possible to use this strategy with a couple of LEDs and an optic
27  fiber cable to have a safe EM interference free data link.
28 
29  It is possible to use this strategy to also communicate long range
30  wirelessly using a couple of photodiodes and laser emitters. It may be
31  necessary to tweak timing constants in Timing.h.
32  ___________________________________________________________________________
33 
34  Copyright 2010-2020 Giovanni Blu Mitolo [email protected]
35 
36  Licensed under the Apache License, Version 2.0 (the "License");
37  you may not use this file except in compliance with the License.
38  You may obtain a copy of the License at
39 
40  http://www.apache.org/licenses/LICENSE-2.0
41 
42  Unless required by applicable law or agreed to in writing, software
43  distributed under the License is distributed on an "AS IS" BASIS,
44  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
45  See the License for the specific language governing permissions and
46  limitations under the License. */
47 
48 #pragma once
49 
50 /* Default reading state threshold: */
51 
52 #ifndef AS_THRESHOLD
53 #define AS_THRESHOLD 1
54 #endif
55 
56 /* MODE 1 performance (default):
57  Transfer speed: 1024Bb or 128B/s
58 
59  MODE 2 performance:
60  Transfer speed: 1361Bb or 170B/s
61 
62  MODE 3 performance:
63  Transfer speed: 3352Bb or 419B/s
64 
65  MODE 4 performance:
66  Transfer speed: 5069Bb or 633B/s
67 
68  MODE 5 performance:
69  Transfer speed: 12658Bb or 1582B/s
70  ADC prescale 8 (Caution out of specification)
71 
72  Set default transmission mode */
73 #ifndef AS_MODE
74 #define AS_MODE 1
75 #endif
76 
77 // Used to signal communication failure
78 #define AS_FAIL 65535
79 // Used for pin handling
80 #define AS_NOT_ASSIGNED 255
81 // START frame symbol 10010101 - 0x95 - •
82 #define AS_START 149
83 // END frame symbol 11101010 - 0xea - ê
84 #define AS_END 234
85 // ESCAPE symbol 10111011 - 0xBB - »
86 #define AS_ESC 187
87 
88 #include "Timing.h"
89 
90 // Recommended receive time for this strategy, in microseconds
91 #ifndef AS_RECEIVE_TIME
92 #define AS_RECEIVE_TIME 1000
93 #endif
94 
96 {
97 public:
98 
99  /* Returns the suggested delay related to attempts passed as parameter: */
100 
101  uint32_t back_off(uint8_t attempts)
102  {
103  uint32_t result = attempts;
104  for(uint8_t d = 0; d < AS_BACK_OFF_DEGREE; d++) {
105  result *= (uint32_t)(attempts);
106  }
107  return result;
108  };
109 
110 
111  /* Begin method, to be called on initialization:
112  (returns always true) */
113 
114  bool begin(uint8_t did = 0)
115  {
116 #ifdef ARDUINO
117  // Set ADC clock prescale
118 #if AS_PRESCALE == 8
119  cbi(ADCSRA, ADPS2);
120  sbi(ADCSRA, ADPS1);
121  sbi(ADCSRA, ADPS0);
122 #elif AS_PRESCALE == 16
123  sbi(ADCSRA, ADPS2);
124  cbi(ADCSRA, ADPS1);
125  cbi(ADCSRA, ADPS0);
126 #elif AS_PRESCALE == 32
127  sbi(ADCSRA, ADPS2);
128  cbi(ADCSRA, ADPS1);
129  sbi(ADCSRA, ADPS0);
130 #endif
131 #endif
132  PJON_DELAY(PJON_RANDOM(AS_INITIAL_DELAY) + did);
133  compute_analog_read_duration();
134  _last_byte = receive_byte();
135  return true;
136  };
137 
138 
139  /* Check if the channel is free for transmission:
140  If receiving 10 bits no 1s are detected
141  there is no active transmission */
142 
143  bool can_start()
144  {
145  uint32_t time = PJON_MICROS();
146  while(
147  (uint32_t)(PJON_MICROS() - time) <=
148  (AS_BIT_SPACER + (AS_BIT_WIDTH * 9))
149  ) if(receive_byte() != PJON_FAIL) {
150  return false;
151  }
152  PJON_DELAY_MICROSECONDS(PJON_RANDOM(AS_COLLISION_DELAY));
153  if((uint16_t)PJON_ANALOG_READ(_input_pin) > threshold) {
154  return false;
155  }
156  return true;
157  };
158 
159 
160  /* compute PJON_ANALOG_READ duration: */
161 
162  void compute_analog_read_duration()
163  {
164  uint32_t time = PJON_MICROS();
165  PJON_ANALOG_READ(_input_pin);
166  _analog_read_time = (uint32_t)(PJON_MICROS() - time);
167  };
168 
169 
170  /* Returns the maximum number of attempts for each transmission: */
171 
172  static uint8_t get_max_attempts()
173  {
174  return AS_MAX_ATTEMPTS;
175  };
176 
177 
178  /* Returns the recommended receive time for this strategy: */
179 
180  static uint16_t get_receive_time()
181  {
182  return AS_RECEIVE_TIME;
183  };
184 
185 
186  /* Handle a collision: */
187 
188  void handle_collision()
189  {
190  PJON_DELAY_MICROSECONDS(PJON_RANDOM(AS_COLLISION_DELAY));
191  };
192 
193 
194  /* Read a byte from the pin */
195 
196  uint8_t read_byte()
197  {
198  uint16_t bit_value = 0;
199  uint16_t high_bit = 0;
200  uint16_t low_bit = 0;
201  uint8_t byte_value = 0;
202  for(int i = 0; i < 8; i++) {
203  long time = PJON_MICROS();
204  PJON_DELAY_MICROSECONDS((AS_BIT_WIDTH / 2) - AS_READ_DELAY);
205  bit_value = PJON_ANALOG_READ(_input_pin);
206  byte_value += (bit_value > threshold) << i;
207 
208  high_bit =
209  (((bit_value > threshold) ? bit_value : high_bit) + high_bit) / 2;
210 
211  low_bit =
212  (((bit_value < threshold) ? bit_value : low_bit) + low_bit) / 2;
213 
214  PJON_DELAY_MICROSECONDS(
215  AS_BIT_WIDTH - (uint32_t)(PJON_MICROS() - time)
216  );
217  }
218  threshold = (high_bit + low_bit) / 2;
219  _last_update = PJON_MICROS();
220  return byte_value;
221  };
222 
223 
224  /* Check if a byte is coming from the pin:
225  This function is looking for padding bits before a byte.
226  If value is 1 for more than ACCEPTANCE and after
227  that comes a 0 probably a byte is coming:
228  ________
229  | Init |
230  |--------|
231  |_____ |
232  | | | |
233  |1 | |0 |
234  |__|__|__|
235  |
236  ACCEPTANCE */
237 
238 
239  uint16_t receive_byte()
240  {
241  PJON_IO_PULL_DOWN(_input_pin);
242  if(_output_pin != AS_NOT_ASSIGNED && _output_pin != _input_pin) {
243  PJON_IO_PULL_DOWN(_output_pin);
244  }
245  uint32_t time = PJON_MICROS();
246 
247  if(
248  (uint32_t)(PJON_MICROS() - _last_update) >
249  AS_THRESHOLD_DECREASE_INTERVAL
250  ) {
251  threshold *= 0.9;
252  _last_update = PJON_MICROS();
253  }
254 
255  while(
256  ((uint16_t)(PJON_ANALOG_READ(_input_pin)) > threshold) &&
257  ((uint32_t)(PJON_MICROS() - time) <= AS_BIT_SPACER)
258  ); // Do nothing
259 
260  time = PJON_MICROS() - time;
261  if(time < AS_BIT_SPACER * 0.75) {
262  return AS_FAIL;
263  }
264  if(time <= AS_BIT_SPACER * 1.25) {
265  PJON_DELAY_MICROSECONDS(AS_BIT_WIDTH);
266  return read_byte();
267  }
268  return AS_FAIL;
269  };
270 
271 
272  /* Receive byte response */
273 
274  uint16_t receive_response()
275  {
276  PJON_IO_PULL_DOWN(_input_pin);
277  if(_output_pin != AS_NOT_ASSIGNED && _output_pin != _input_pin) {
278  PJON_IO_WRITE(_output_pin, LOW);
279  }
280  uint16_t response = AS_FAIL;
281  uint32_t time = PJON_MICROS();
282  while(
283  (response != PJON_ACK) &&
284  (uint32_t)(PJON_MICROS() - AS_RESPONSE_TIMEOUT) <= time
285  ) {
286  response = receive_byte();
287  }
288  return response;
289  };
290 
291 
292  /* Receive a frame: */
293 
294  uint16_t receive_frame(uint8_t *data, uint16_t max_length)
295  {
296  uint16_t result;
297  // No initial flag, byte-stuffing violation
298  if(max_length == PJON_PACKET_MAX_LENGTH)
299  if(
300  (receive_byte() != AS_START) ||
301  (_last_byte == AS_ESC)
302  ) {
303  return AS_FAIL;
304  }
305  result = receive_byte();
306  if(result == AS_FAIL) {
307  return AS_FAIL;
308  }
309  // Unescaped START byte stuffing violation
310  if(result == AS_START) {
311  return AS_FAIL;
312  }
313  if(result == AS_ESC) {
314  result = receive_byte();
315  // Escaping byte-stuffing violation
316  if((result != AS_START) && (result != AS_ESC) && (result != AS_END)) {
317  return AS_FAIL;
318  }
319  result ^= AS_ESC;
320  }
321  // No end flag, byte-stuffing violation
322  if(max_length == 1 && receive_byte() != AS_END) {
323  return AS_FAIL;
324  }
325  *data = result;
326  return 1;
327  };
328 
329 
330  /* Every byte is prepended with 2 synchronization padding bits. The first
331  is a longer than standard logic 1 followed by a standard logic 0.
332  __________ ___________________________
333  | SyncPad | Byte |
334  |______ |___ ___ _____ |
335  | | | | | | | | | |
336  | | 1 | 0 | 1 | 0 0 | 1 | 0 | 1 1 | 0 |
337  |_|____|___|___|_____|___|___|_____|___|
338  |
339  ACCEPTANCE
340 
341  The reception tecnique is based on finding a logic 1 as long as the
342  first padding bit within a certain threshold, synchronizing to its
343  falling edge and checking if it is followed by a logic 0. If this
344  pattern is recognised, reception starts, if not, interference,
345  synchronization loss or simply absence of communication is
346  detected at byte level. */
347 
348  void send_byte(uint8_t b)
349  {
350  PJON_IO_WRITE(_output_pin, HIGH);
351  PJON_DELAY_MICROSECONDS(AS_BIT_SPACER);
352  PJON_IO_WRITE(_output_pin, LOW);
353  PJON_DELAY_MICROSECONDS(AS_BIT_WIDTH);
354  for(uint8_t mask = 0x01; mask; mask <<= 1) {
355  PJON_IO_WRITE(_output_pin, b & mask);
356  PJON_DELAY_MICROSECONDS(AS_BIT_WIDTH);
357  }
358  };
359 
360 
361  /* Send byte response to package transmitter */
362 
363  void send_response(uint8_t response)
364  {
365  PJON_DELAY_MICROSECONDS(AS_BIT_WIDTH);
366  PJON_IO_PULL_DOWN(_input_pin);
367  PJON_IO_MODE(_output_pin, OUTPUT);
368  send_byte(response);
369  PJON_IO_PULL_DOWN(_output_pin);
370  };
371 
372 
373  /* Send a frame: */
374 
375  void send_frame(uint8_t *data, uint16_t length)
376  {
377  PJON_IO_MODE(_output_pin, OUTPUT);
378  // Add frame flag
379  send_byte(AS_START);
380  for(uint16_t b = 0; b < length; b++)
381  if( // Byte-stuffing
382  (data[b] == AS_START) ||
383  (data[b] == AS_ESC) ||
384  (data[b] == AS_END)
385  ) {
386  send_byte(AS_ESC);
387  send_byte(data[b] ^ AS_ESC);
388  } else {
389  send_byte(data[b]);
390  }
391  send_byte(AS_END);
392  PJON_IO_PULL_DOWN(_output_pin);
393  };
394 
395 
396  /* Set the communicaton pin: */
397 
398  void set_pin(uint8_t pin)
399  {
400  PJON_IO_PULL_DOWN(pin);
401  _input_pin = pin;
402  _output_pin = pin;
403  };
404 
405 
406  /* Set a pair of communication pins: */
407 
408  void set_pins(
409  uint8_t input_pin = AS_NOT_ASSIGNED,
410  uint8_t output_pin = AS_NOT_ASSIGNED
411  )
412  {
413  PJON_IO_PULL_DOWN(input_pin);
414  PJON_IO_PULL_DOWN(output_pin);
415  _input_pin = input_pin;
416  _output_pin = output_pin;
417  };
418 
419 
420  /* Set the threshold analog value between a LOW and a HIGH read: */
421 
422  void set_threshold(uint16_t value)
423  {
424  threshold = value;
425  };
426 
427 
428  uint16_t threshold = AS_THRESHOLD;
429 private:
430  uint8_t _last_byte;
431  uint16_t _analog_read_time;
432  uint8_t _input_pin;
433  uint8_t _output_pin;
434  uint32_t _last_update;
435 };
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
AnalogSampling
Definition: AnalogSampling.h:95