00001
00002 """
00003 This module provides a 313MHz/434MHz radio interface compatible
00004 with the Virtual Wire library used on Arduinos.
00005
00006 It has been tested between a Pi, TI Launchpad, and Arduino Pro Mini.
00007 """
00008
00009
00010
00011 import time
00012
00013 import pigpio
00014
00015 MAX_MESSAGE_BYTES=77
00016
00017 MIN_BPS=50
00018 MAX_BPS=10000
00019
00020 _HEADER=[0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x38, 0x2c]
00021
00022 _CTL=3
00023
00024 _SYMBOL=[
00025 0x0d, 0x0e, 0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c,
00026 0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x32, 0x34]
00027
00028
00029 def _sym2nibble(symbol):
00030 for nibble in range(16):
00031 if symbol == _SYMBOL[nibble]:
00032 return nibble
00033 return 0
00034
00035 def _crc_ccitt_update(crc, data):
00036
00037 data = data ^ (crc & 0xFF);
00038
00039 data = (data ^ (data << 4)) & 0xFF;
00040
00041 return (
00042 (((data << 8) & 0xFFFF) | (crc >> 8)) ^
00043 ((data >> 4) & 0x00FF) ^ ((data << 3) & 0xFFFF)
00044 )
00045
00046 class tx():
00047
00048 def __init__(self, pi, txgpio, bps=2000):
00049 """
00050 Instantiate a transmitter with the Pi, the transmit gpio,
00051 and the bits per second (bps). The bps defaults to 2000.
00052 The bps is constrained to be within MIN_BPS to MAX_BPS.
00053 """
00054 self.pi = pi
00055
00056 self.txbit = (1<<txgpio)
00057
00058 if bps < MIN_BPS:
00059 bps = MIN_BPS
00060 elif bps > MAX_BPS:
00061 bps = MAX_BPS
00062
00063 self.mics = int(1000000 / bps)
00064
00065 self.wave_id = None
00066
00067 pi.wave_add_new()
00068
00069 pi.set_mode(txgpio, pigpio.OUTPUT)
00070
00071
00072 def _nibble(self, nibble):
00073
00074 for i in range(6):
00075 if nibble & (1<<i):
00076 self.wf.append(pigpio.pulse(self.txbit, 0, self.mics))
00077 else:
00078 self.wf.append(pigpio.pulse(0, self.txbit, self.mics))
00079
00080 def _byte(self, crc, byte):
00081 self._nibble(_SYMBOL[byte>>4])
00082 self._nibble(_SYMBOL[byte&0x0F])
00083 return _crc_ccitt_update(crc, byte)
00084
00085 def put(self, data):
00086 """
00087 Transmit a message. If the message is more than
00088 MAX_MESSAGE_BYTES in size it is discarded. If a message
00089 is currently being transmitted it is aborted and replaced
00090 with the new message. True is returned if message
00091 transmission has successfully started. False indicates
00092 an error.
00093 """
00094 if len(data) > MAX_MESSAGE_BYTES:
00095 return False
00096
00097 self.wf = []
00098
00099 self.cancel()
00100
00101 for i in _HEADER:
00102 self._nibble(i)
00103
00104 crc = self._byte(0xFFFF, len(data)+_CTL)
00105
00106 for i in data:
00107
00108 if type(i) == type(""):
00109 v = ord(i)
00110 else:
00111 v = i
00112
00113 crc = self._byte(crc, v)
00114
00115 crc = ~crc
00116
00117 self._byte(0, crc&0xFF)
00118 self._byte(0, crc>>8)
00119
00120 self.pi.wave_add_generic(self.wf)
00121
00122 self.wave_id = self.pi.wave_create()
00123
00124 if self.wave_id >= 0:
00125 self.pi.wave_send_once(self.wave_id)
00126 return True
00127 else:
00128 return False
00129
00130
00131 def ready(self):
00132 """
00133 Returns True if a new message may be transmitted.
00134 """
00135 return not self.pi.wave_tx_busy()
00136
00137 def cancel(self):
00138 """
00139 Cancels the wireless transmitter, aborting any message
00140 in progress.
00141 """
00142 if self.wave_id is not None:
00143 self.pi.wave_tx_stop()
00144 self.pi.wave_delete(self.wave_id)
00145 self.pi.wave_add_new()
00146
00147 self.wave_id = None
00148
00149 class rx():
00150
00151 def __init__(self, pi, rxgpio, bps=2000):
00152 """
00153 Instantiate a receiver with the Pi, the receive gpio, and
00154 the bits per second (bps). The bps defaults to 2000.
00155 The bps is constrained to be within MIN_BPS to MAX_BPS.
00156 """
00157 self.pi = pi
00158 self.rxgpio = rxgpio
00159
00160 self.messages = []
00161 self.bad_CRC = 0
00162
00163 if bps < MIN_BPS:
00164 bps = MIN_BPS
00165 elif bps > MAX_BPS:
00166 bps = MAX_BPS
00167
00168 slack = 0.20
00169 self.mics = int(1000000 / bps)
00170 slack_mics = int(slack * self.mics)
00171 self.min_mics = self.mics - slack_mics
00172 self.max_mics = (self.mics + slack_mics) * 4
00173
00174 self.timeout = 8 * self.mics / 1000
00175 if self.timeout < 8:
00176 self.timeout = 8
00177
00178 self.last_tick = None
00179 self.good = 0
00180 self.bits = 0
00181 self.token = 0
00182 self.in_message = False
00183 self.message = [0]*(MAX_MESSAGE_BYTES+_CTL)
00184 self.message_len = 0
00185 self.byte = 0
00186
00187 pi.set_mode(rxgpio, pigpio.INPUT)
00188
00189 self.cb = pi.callback(rxgpio, pigpio.EITHER_EDGE, self._cb)
00190
00191 def _calc_crc(self):
00192
00193 crc = 0xFFFF
00194 for i in range(self.message_length):
00195 crc = _crc_ccitt_update(crc, self.message[i])
00196 return crc
00197
00198 def _insert(self, bits, level):
00199
00200 for i in range(bits):
00201
00202 self.token >>= 1
00203
00204 if level == 0:
00205 self.token |= 0x800
00206
00207 if self.in_message:
00208
00209 self.bits += 1
00210
00211 if self.bits >= 12:
00212
00213 byte = (
00214 _sym2nibble(self.token & 0x3f) << 4 |
00215 _sym2nibble(self.token >> 6))
00216
00217 if self.byte == 0:
00218 self.message_length = byte
00219
00220 if byte > (MAX_MESSAGE_BYTES+_CTL):
00221 self.in_message = False
00222 return
00223
00224 self.message[self.byte] = byte
00225
00226 self.byte += 1
00227 self.bits = 0
00228
00229 if self.byte >= self.message_length:
00230 self.in_message = False
00231 self.pi.set_watchdog(self.rxgpio, 0)
00232
00233 crc = self._calc_crc()
00234
00235 if crc == 0xF0B8:
00236 self.messages.append(
00237 self.message[1:self.message_length-2])
00238 else:
00239 self.bad_CRC += 1
00240
00241 else:
00242 if self.token == 0xB38:
00243 self.in_message = True
00244 self.pi.set_watchdog(self.rxgpio, self.timeout)
00245 self.bits = 0
00246 self.byte = 0
00247
00248 def _cb(self, gpio, level, tick):
00249
00250 if self.last_tick is not None:
00251
00252 if level == pigpio.TIMEOUT:
00253
00254 self.pi.set_watchdog(self.rxgpio, 0)
00255
00256 if self.in_message:
00257 self._insert(4, not self.last_level)
00258
00259 self.good = 0
00260 self.in_message = False
00261
00262 else:
00263
00264 edge = pigpio.tickDiff(self.last_tick, tick)
00265
00266 if edge < self.min_mics:
00267
00268 self.good = 0
00269 self.in_message = False
00270
00271 elif edge > self.max_mics:
00272
00273 if self.in_message:
00274 self._insert(4, level)
00275
00276 self.good = 0
00277 self.in_message = False
00278
00279 else:
00280
00281 self.good += 1
00282
00283 if self.good > 8:
00284
00285 bitlen = (100 * edge) / self.mics
00286
00287 if bitlen < 140:
00288 bits = 1
00289 elif bitlen < 240:
00290 bits = 2
00291 elif bitlen < 340:
00292 bits = 3
00293 else:
00294 bits = 4
00295
00296 self._insert(bits, level)
00297
00298 self.last_tick = tick
00299 self.last_level = level
00300
00301 def get(self):
00302 """
00303 Returns the next unread message, or None if none is avaiable.
00304 """
00305 if len(self.messages):
00306 return self.messages.pop(0)
00307 else:
00308 return None
00309
00310 def ready(self):
00311 """
00312 Returns True if there is a message available to be read.
00313 """
00314 return len(self.messages)
00315
00316 def cancel(self):
00317 """
00318 Cancels the wireless receiver.
00319 """
00320 if self.cb is not None:
00321 self.cb.cancel()
00322 self.pi.set_watchdog(self.rxgpio, 0)
00323 self.cb = None
00324
00325 if __name__ == "__main__":
00326
00327 import time
00328
00329 import pigpio
00330
00331 import vw
00332
00333 RX=11
00334 TX=25
00335
00336 BPS=2000
00337
00338 pi = pigpio.pi()
00339
00340 rx = vw.rx(pi, RX, BPS)
00341 tx = vw.tx(pi, TX, BPS)
00342
00343 msg = 0
00344
00345 start = time.time()
00346
00347 while (time.time()-start) < 300:
00348
00349 msg += 1
00350
00351 while not tx.ready():
00352 time.sleep(0.1)
00353
00354 time.sleep(0.2)
00355
00356 tx.put([48, 49, 65, ((msg>>6)&0x3F)+32, (msg&0x3F)+32])
00357
00358 while not tx.ready():
00359 time.sleep(0.1)
00360
00361 time.sleep(0.2)
00362
00363 tx.put("Hello World #{}!".format(msg))
00364
00365 while rx.ready():
00366 print("".join(chr (c) for c in rx.get()))
00367
00368 rx.cancel()
00369 tx.cancel()
00370
00371 pi.stop()
00372