vw.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 """
3 This module provides a 313MHz/434MHz radio interface compatible
4 with the Virtual Wire library used on Arduinos.
5 
6 It has been tested between a Pi, TI Launchpad, and Arduino Pro Mini.
7 """
8 # 2014-08-14
9 # vw.py
10 
11 import time
12 
13 import pigpio
14 
15 MAX_MESSAGE_BYTES=77
16 
17 MIN_BPS=50
18 MAX_BPS=10000
19 
20 _HEADER=[0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x38, 0x2c]
21 
22 _CTL=3
23 
24 _SYMBOL=[
25  0x0d, 0x0e, 0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c,
26  0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x32, 0x34]
27 
28 
29 def _sym2nibble(symbol):
30  for nibble in range(16):
31  if symbol == _SYMBOL[nibble]:
32  return nibble
33  return 0
34 
35 def _crc_ccitt_update(crc, data):
36 
37  data = data ^ (crc & 0xFF);
38 
39  data = (data ^ (data << 4)) & 0xFF;
40 
41  return (
42  (((data << 8) & 0xFFFF) | (crc >> 8)) ^
43  ((data >> 4) & 0x00FF) ^ ((data << 3) & 0xFFFF)
44  )
45 
46 class tx():
47 
48  def __init__(self, pi, txgpio, bps=2000):
49  """
50  Instantiate a transmitter with the Pi, the transmit gpio,
51  and the bits per second (bps). The bps defaults to 2000.
52  The bps is constrained to be within MIN_BPS to MAX_BPS.
53  """
54  self.pi = pi
55 
56  self.txbit = (1<<txgpio)
57 
58  if bps < MIN_BPS:
59  bps = MIN_BPS
60  elif bps > MAX_BPS:
61  bps = MAX_BPS
62 
63  self.mics = int(1000000 / bps)
64 
65  self.wave_id = None
66 
67  pi.wave_add_new()
68 
69  pi.set_mode(txgpio, pigpio.OUTPUT)
70 
71 
72  def _nibble(self, nibble):
73 
74  for i in range(6):
75  if nibble & (1<<i):
76  self.wf.append(pigpio.pulse(self.txbit, 0, self.mics))
77  else:
78  self.wf.append(pigpio.pulse(0, self.txbit, self.mics))
79 
80  def _byte(self, crc, byte):
81  self._nibble(_SYMBOL[byte>>4])
82  self._nibble(_SYMBOL[byte&0x0F])
83  return _crc_ccitt_update(crc, byte)
84 
85  def put(self, data):
86  """
87  Transmit a message. If the message is more than
88  MAX_MESSAGE_BYTES in size it is discarded. If a message
89  is currently being transmitted it is aborted and replaced
90  with the new message. True is returned if message
91  transmission has successfully started. False indicates
92  an error.
93  """
94  if len(data) > MAX_MESSAGE_BYTES:
95  return False
96 
97  self.wf = []
98 
99  self.cancel()
100 
101  for i in _HEADER:
102  self._nibble(i)
103 
104  crc = self._byte(0xFFFF, len(data)+_CTL)
105 
106  for i in data:
107 
108  if type(i) == type(""):
109  v = ord(i)
110  else:
111  v = i
112 
113  crc = self._byte(crc, v)
114 
115  crc = ~crc
116 
117  self._byte(0, crc&0xFF)
118  self._byte(0, crc>>8)
119 
120  self.pi.wave_add_generic(self.wf)
121 
122  self.wave_id = self.pi.wave_create()
123 
124  if self.wave_id >= 0:
125  self.pi.wave_send_once(self.wave_id)
126  return True
127  else:
128  return False
129 
130 
131  def ready(self):
132  """
133  Returns True if a new message may be transmitted.
134  """
135  return not self.pi.wave_tx_busy()
136 
137  def cancel(self):
138  """
139  Cancels the wireless transmitter, aborting any message
140  in progress.
141  """
142  if self.wave_id is not None:
143  self.pi.wave_tx_stop()
144  self.pi.wave_delete(self.wave_id)
145  self.pi.wave_add_new()
146 
147  self.wave_id = None
148 
149 class rx():
150 
151  def __init__(self, pi, rxgpio, bps=2000):
152  """
153  Instantiate a receiver with the Pi, the receive gpio, and
154  the bits per second (bps). The bps defaults to 2000.
155  The bps is constrained to be within MIN_BPS to MAX_BPS.
156  """
157  self.pi = pi
158  self.rxgpio = rxgpio
159 
160  self.messages = []
161  self.bad_CRC = 0
162 
163  if bps < MIN_BPS:
164  bps = MIN_BPS
165  elif bps > MAX_BPS:
166  bps = MAX_BPS
167 
168  slack = 0.20
169  self.mics = int(1000000 / bps)
170  slack_mics = int(slack * self.mics)
171  self.min_mics = self.mics - slack_mics # Shortest legal edge.
172  self.max_mics = (self.mics + slack_mics) * 4 # Longest legal edge.
173 
174  self.timeout = 8 * self.mics / 1000 # 8 bits time in ms.
175  if self.timeout < 8:
176  self.timeout = 8
177 
178  self.last_tick = None
179  self.good = 0
180  self.bits = 0
181  self.token = 0
182  self.in_message = False
183  self.message = [0]*(MAX_MESSAGE_BYTES+_CTL)
184  self.message_len = 0
185  self.byte = 0
186 
187  pi.set_mode(rxgpio, pigpio.INPUT)
188 
189  self.cb = pi.callback(rxgpio, pigpio.EITHER_EDGE, self._cb)
190 
191  def _calc_crc(self):
192 
193  crc = 0xFFFF
194  for i in range(self.message_length):
195  crc = _crc_ccitt_update(crc, self.message[i])
196  return crc
197 
198  def _insert(self, bits, level):
199 
200  for i in range(bits):
201 
202  self.token >>= 1
203 
204  if level == 0:
205  self.token |= 0x800
206 
207  if self.in_message:
208 
209  self.bits += 1
210 
211  if self.bits >= 12: # Complete token.
212 
213  byte = (
214  _sym2nibble(self.token & 0x3f) << 4 |
215  _sym2nibble(self.token >> 6))
216 
217  if self.byte == 0:
218  self.message_length = byte
219 
220  if byte > (MAX_MESSAGE_BYTES+_CTL):
221  self.in_message = False # Abort message.
222  return
223 
224  self.message[self.byte] = byte
225 
226  self.byte += 1
227  self.bits = 0
228 
229  if self.byte >= self.message_length:
230  self.in_message = False
231  self.pi.set_watchdog(self.rxgpio, 0)
232 
233  crc = self._calc_crc()
234 
235  if crc == 0xF0B8: # Valid CRC.
236  self.messages.append(
237  self.message[1:self.message_length-2])
238  else:
239  self.bad_CRC += 1
240 
241  else:
242  if self.token == 0xB38: # Start message token.
243  self.in_message = True
244  self.pi.set_watchdog(self.rxgpio, self.timeout)
245  self.bits = 0
246  self.byte = 0
247 
248  def _cb(self, gpio, level, tick):
249 
250  if self.last_tick is not None:
251 
252  if level == pigpio.TIMEOUT:
253 
254  self.pi.set_watchdog(self.rxgpio, 0) # Switch watchdog off.
255 
256  if self.in_message:
257  self._insert(4, not self.last_level)
258 
259  self.good = 0
260  self.in_message = False
261 
262  else:
263 
264  edge = pigpio.tickDiff(self.last_tick, tick)
265 
266  if edge < self.min_mics:
267 
268  self.good = 0
269  self.in_message = False
270 
271  elif edge > self.max_mics:
272 
273  if self.in_message:
274  self._insert(4, level)
275 
276  self.good = 0
277  self.in_message = False
278 
279  else:
280 
281  self.good += 1
282 
283  if self.good > 8:
284 
285  bitlen = (100 * edge) / self.mics
286 
287  if bitlen < 140:
288  bits = 1
289  elif bitlen < 240:
290  bits = 2
291  elif bitlen < 340:
292  bits = 3
293  else:
294  bits = 4
295 
296  self._insert(bits, level)
297 
298  self.last_tick = tick
299  self.last_level = level
300 
301  def get(self):
302  """
303  Returns the next unread message, or None if none is avaiable.
304  """
305  if len(self.messages):
306  return self.messages.pop(0)
307  else:
308  return None
309 
310  def ready(self):
311  """
312  Returns True if there is a message available to be read.
313  """
314  return len(self.messages)
315 
316  def cancel(self):
317  """
318  Cancels the wireless receiver.
319  """
320  if self.cb is not None:
321  self.cb.cancel()
322  self.pi.set_watchdog(self.rxgpio, 0)
323  self.cb = None
324 
325 if __name__ == "__main__":
326 
327  import time
328 
329  import pigpio
330 
331  import vw
332 
333  RX=11
334  TX=25
335 
336  BPS=2000
337 
338  pi = pigpio.pi() # Connect to local Pi.
339 
340  rx = vw.rx(pi, RX, BPS) # Specify Pi, rx gpio, and baud.
341  tx = vw.tx(pi, TX, BPS) # Specify Pi, tx gpio, and baud.
342 
343  msg = 0
344 
345  start = time.time()
346 
347  while (time.time()-start) < 300:
348 
349  msg += 1
350 
351  while not tx.ready():
352  time.sleep(0.1)
353 
354  time.sleep(0.2)
355 
356  tx.put([48, 49, 65, ((msg>>6)&0x3F)+32, (msg&0x3F)+32])
357 
358  while not tx.ready():
359  time.sleep(0.1)
360 
361  time.sleep(0.2)
362 
363  tx.put("Hello World #{}!".format(msg))
364 
365  while rx.ready():
366  print("".join(chr (c) for c in rx.get()))
367 
368  rx.cancel()
369  tx.cancel()
370 
371  pi.stop()
372 
txbit
Definition: vw.py:56
good
Definition: vw.py:179
message_len
Definition: vw.py:184
wave_id
Definition: vw.py:65
wf
Definition: vw.py:97
def cancel(self)
Definition: vw.py:137
last_tick
Definition: vw.py:178
def ready(self)
Definition: vw.py:131
def get(self)
Definition: vw.py:301
pi
Definition: vw.py:157
mics
Definition: vw.py:169
bits
Definition: vw.py:180
pi
Definition: vw.py:54
def cancel(self)
Definition: vw.py:316
Definition: vw.py:46
bad_CRC
Definition: vw.py:161
messages
Definition: vw.py:160
token
Definition: vw.py:181
def put(self, data)
Definition: vw.py:85
message_length
Definition: vw.py:218
min_mics
Definition: vw.py:171
Definition: vw.py:149
cb
Definition: vw.py:189
rxgpio
Definition: vw.py:158
timeout
Definition: vw.py:174
def tickDiff(t1, t2)
Definition: pigpio.py:911
def _calc_crc(self)
Definition: vw.py:191
in_message
Definition: vw.py:182
def _sym2nibble(symbol)
Definition: vw.py:29
last_level
Definition: vw.py:299
max_mics
Definition: vw.py:172
def _byte(self, crc, byte)
Definition: vw.py:80
def __init__(self, pi, rxgpio, bps=2000)
Definition: vw.py:151
def _insert(self, bits, level)
Definition: vw.py:198
def _nibble(self, nibble)
Definition: vw.py:72
def __init__(self, pi, txgpio, bps=2000)
Definition: vw.py:48
byte
Definition: vw.py:185
message
Definition: vw.py:183
mics
Definition: vw.py:63
def _cb(self, gpio, level, tick)
Definition: vw.py:248
def _crc_ccitt_update(crc, data)
Definition: vw.py:35
def ready(self)
Definition: vw.py:310


cob_hand_bridge
Author(s): Mathias Lüdtke
autogenerated on Tue Oct 20 2020 03:35:57