played around with different inputs to change the temperature display
[goodfet] / client / GoodFETCC2500.py
1 #!/usr/bin/env python
2 # GoodFET Chipcon RF Radio Client for CC2500
3 #
4 # (C) 2013 Jean-Michel Picod <jean-michel.picod at cassidian.com>
5 #
6
7 import time
8 import math
9
10 from GoodFET import GoodFET
11
12 class GoodFETCC2500(GoodFET):
13     APP       = 0x52
14     READ      = 0x00
15     WRITE     = 0x01
16     PEEK      = 0x02
17     POKE      = 0x03
18     SETUP     = 0x10
19     TX        = 0x81
20     RX        = 0x80
21     REPEAT_RX = 0x91
22
23     _chips = {
24         0x8003: "CC2500",
25         0x800f: "CC2500",
26         0x0204: "CC1150"
27     }
28     _regnames = [
29         "IOCFG2", "IOCFG1", "IOCFG0", "FIFOTHR", "SYNC1", "SYNC0",
30         "PKTLEN", "PKTCTRL1", "PKTCTRL0", "ADDR", "CHANNR",
31         "FSCTRL1", "FSCTRL0", "FREQ2", "FREQ1", "FREQ0",
32         "MDMCFG4", "MDMCFG3", "MDMCFG2", "MDMCFG1", "MDMCFG0", "DEVIATN",
33         "MCSM2", "MCSM1", "MCSM0", "FOCCFG", "BSCFG", "AGCCTRL2",
34         "AGCCTRL1", "AGCCTRL0", "WOREVT1", "WOREVT0", "WORCTRL",
35         "FREND1", "FREND0", "FSCAL3", "FSCAL2", "FSCAL1", "FSCAL0",
36         "RCCTRL1", "RCCTRL0", "FSTEST", "PTEST", "AGCTEST", "TEST2",
37         "TEST1", "TEST0", "?",
38         ## Strobes
39         "SRES", "SFSTXON", "SXOFF", "SCAL", "SRX", "STX", "SIDLE",
40         "?", "SWOR", "SPWD", "SFRX", "SFTX", "SWORRST", "SNOP",
41         "PATABLE", "TXFIFO",
42         "IOCFG2", "IOCFG1", "IOCFG0", "FIFOTHR", "SYNC1", "SYNC0",
43         "PKTLEN", "PKTCTRL1", "PKTCTRL0", "ADDR", "CHANNR",
44         "FSCTRL1", "FSCTRL0", "FREQ2", "FREQ1", "FREQ0",
45         "MDMCFG4", "MDMCFG3", "MDMCFG2", "MDMCFG1", "MDMCFG0", "DEVIATN",
46         "MCSM2", "MCSM1", "MCSM0", "FOCCFG", "BSCFG", "AGCCTRL2",
47         "AGCCTRL1", "AGCCTRL0", "WOREVT1", "WOREVT0", "WORCTRL",
48         "FREND1", "FREND0", "FSCAL3", "FSCAL2", "FSCAL1", "FSCAL0",
49         "RCCTRL1", "RCCTRL0", "FSTEST", "PTEST", "AGCTEST", "TEST2",
50         "TEST1", "TEST0", "?",
51         "?", "?", "?", "?", "?", "?", "?",
52         "?", "?", "?", "?", "?", "?", "?",
53         "PATABLE", "TXFIFO",
54         "IOCFG2", "IOCFG1", "IOCFG0", "FIFOTHR", "SYNC1", "SYNC0",
55         "PKTLEN", "PKTCTRL1", "PKTCTRL0", "ADDR", "CHANNR",
56         "FSCTRL1", "FSCTRL0", "FREQ2", "FREQ1", "FREQ0",
57         "MDMCFG4", "MDMCFG3", "MDMCFG2", "MDMCFG1", "MDMCFG0", "DEVIATN",
58         "MCSM2", "MCSM1", "MCSM0", "FOCCFG", "BSCFG", "AGCCTRL2",
59         "AGCCTRL1", "AGCCTRL0", "WOREVT1", "WOREVT0", "WORCTRL",
60         "FREND1", "FREND0", "FSCAL3", "FSCAL2", "FSCAL1", "FSCAL0",
61         "RCCTRL1", "RCCTRL0", "FSTEST", "PTEST", "AGCTEST", "TEST2",
62         "TEST1", "TEST0", "?",
63         ## Strobes
64         "SRES", "SFSTXON", "SXOFF", "SCAL", "SRX", "STX", "SIDLE",
65         "?", "SWOR", "SPWD", "SFRX", "SFTX", "SWORRST", "SNOP",
66         "PATABLE", "RXFIFO",
67         "IOCFG2", "IOCFG1", "IOCFG0", "FIFOTHR", "SYNC1", "SYNC0",
68         "PKTLEN", "PKTCTRL1", "PKTCTRL0", "ADDR", "CHANNR",
69         "FSCTRL1", "FSCTRL0", "FREQ2", "FREQ1", "FREQ0",
70         "MDMCFG4", "MDMCFG3", "MDMCFG2", "MDMCFG1", "MDMCFG0", "DEVIATN",
71         "MCSM2", "MCSM1", "MCSM0", "FOCCFG", "BSCFG", "AGCCTRL2",
72         "AGCCTRL1", "AGCCTRL0", "WOREVT1", "WOREVT0", "WORCTRL",
73         "FREND1", "FREND0", "FSCAL3", "FSCAL2", "FSCAL1", "FSCAL0",
74         "RCCTRL1", "RCCTRL0", "FSTEST", "PTEST", "AGCTEST", "TEST2",
75         "TEST1", "TEST0", "?",
76         "PARTNUM", "VERSION", "FREQEST", "LQI", "RSSI", "MARCSTATE",
77         "WORTIME1", "WORTIME0", "PKTSTATUS", "VCO_VC_DAC", "TXBYTES",
78         "RXBYTES", "RCCTRL1_STATUS", "RCCTRL0_STATUS",
79         "PATABLE", "RXFIFO"
80     ]
81     def CC_setup(self):
82         """Move the FET into the CCSPI application."""
83         self.writecmd(self.APP, self.SETUP, 0, self.data) #CCSPI/SETUP
84
85     def CC_trans8(self, byte):
86         """Read and write 8 bits by CCSPI."""
87         data = self.CC_trans([byte])
88         return ord(data[0])
89
90     def CC_trans(self, data):
91         """Exchange data by CCSPI."""
92         self.data = data
93         self.writecmd(self.APP, self.READ, len(data), data)
94         return self.data
95     def strobe(self, reg=0x00):
96         """Strobes a strobe register, returning the status."""
97         data = [reg]
98         self.CC_trans(data)
99         return ord(self.data[0])
100     def CC_strobe_RES(self):
101         """Reset the chip"""
102         self.int_state = self.strobe(0x30)
103     def CC_strobe_FSTXON(self):
104         """Enable and calibrate the frequency synthesizer"""
105         self.int_state = self.strobe(0x31)
106     def CC_strobe_XOFF(self):
107         """Turn off crystal oscillator"""
108         self.int_state = self.strobe(0x32)
109     def CC_strobe_CAL(self):
110         """Calibrate frequency synthesizer and turn it off"""
111         self.int_state = self.strobe(0x33)
112     def CC_strobe_RX(self):
113         """Enable RX"""
114         self.int_state = self.strobe(0x34)
115     def CC_strobe_TX(self):
116         """Enable TX"""
117         self.int_state = self.strobe(0x35)
118     def CC_strobe_IDLE(self):
119         """Exit RX/TX"""
120         self.int_state = self.strobe(0x36)
121     def CC_strobe_WOR(self):
122         """Wake on Radio"""
123         self.int_state = self.strobe(0x38)
124     def CC_strobe_PWD(self):
125         """Power Down mode"""
126         self.int_state = self.strobe(0x39)
127     def CC_strobe_FRX(self):
128         """Flush RX FIFO buffer. Only issue in IDLE or RXFIFO_OVERFLOW state"""
129         self.int_state = self.strobe(0x3A)
130     def CC_strobe_FTX(self):
131         """Flush TX FIFO buffer. Only issue in IDLE or TXFIFO_UNDERFLOW state"""
132         self.int_state = self.strobe(0x3B)
133     def CC_strobe_WORRST(self):
134         """Reset realtime clock"""
135         self.int_state = self.strobe(0x3C)
136     def CC_strobe_NOP_READ(self):
137         """No operation. May be used to get access to the chip status byte"""
138         self.int_state = self.writecmd(self.APP, self.READ, 1, [0x3D])
139     def CC_strobe_NOP_WRITE(self):
140         """No operation. May be used to get access to the chip status byte"""
141         self.int_state = self.strobe(0x3D)
142
143     def peek_by_name(self, regname):
144         return self.peek(self._regnames.index(regname), 1)[0]
145     def poke_by_name(self, regname, val):
146         return self.poke(self._regnames.index(regname), [val])
147     def peek(self, reg, bytes=1):
148         """Read one or more CCSPI Register."""
149         #Reg is ORed with 0x80 by the GoodFET.
150         if bytes < 1:
151             bytes = 1
152         data = [0 for i in xrange(bytes+1)]
153         data[0] = reg
154         self.writecmd(self.APP, self.PEEK, len(data), data)
155         self.int_state = ord(self.data[0])
156         return [ ord(i) for i in self.data[1:] ]
157
158     def poke(self, reg, val):
159         """Write one or more CCSPI Register."""
160         if isinstance(val, int):
161             val = [ val ]
162         if not isinstance(val, list):
163             raise TypeError("val must be a list of int or an int")
164         data = [ reg ]
165         for v in val:
166             data.append(v & 0x0ff)
167         self.writecmd(self.APP, self.POKE, len(data), data)
168         res = self.peek(reg, len(val))
169         for i in xrange(len(val)):
170             if val[i] != res[i]:
171                 print "Warning, failed to set %s(%02x)=0x%02x, got 0x%02x." % (
172                     self._regnames[reg + i], reg + i,
173                     val[i],
174                     res[i])
175                 return False
176         return True
177
178     def identify(self):
179         (part, ver) = self.peek(0x30, bytes=2)
180         return self._chips.get((part << 8) + ver, "Unsupported chip (0x%02x%02x)" % (part, ver))
181     def status(self):
182         _state = [ "RX_FIFO_LOW", "RX_FIFO_HIGH", "TX_FIFO_LOW", "TX_FIFO_HIGH", "RX_FIFO_OVERFLOW", "TX_FIFO_UNDERFLOW", "SYNC_WORD_SEEN",
183             "PACKET_CRC_OK", "PREAMBLE_QUALITY_REACHED", "CLEAR_CHANNEL_ASSESSMENT", "PLL_LOCK", "SERIAL_CLOCK", "SERIAL_SYNCHRONOUS_DATA",
184             "SERIAL_DATA_OUTPUT", "CARRIER_SENSE", "CRC_OK", "RESERVED (0x10)", "RESERVED (0x11)", "RESERVED (0x12)", "RESERVED (0x13)",
185             "RESERVED (0x14)", "RESERVED (0x15)", "RX_HARD_DATA[1]", "RX_HARD_DATA[0]", "TEST (0x18)", "TEST (0x19)", "TEST (0x1A)", "PA_PD",
186             "LNA_PD", "RX_SYMBOL_TICK", "RESERVED (0x1E)", "RESERVED (0x1F)", "RESERVED (0x20)", "RESERVED (0x21)", "RESERVED (0x22)",
187             "RESERVED (0x23)", "WOR_EVNT0", "WOR_EVNT1", "TEST (0x26)", "CLK_32K", "TEST (0x28)", "CHIP_READY", "TEST (0x2A)", "XOSC_STABLE",
188             "TEST (0x2C)", "GDO0_Z_EN_N", "HIGH_IMPEDANCE", "HW_0", "CLK_XOSC/1", "CLK_XOSC/1.5", "CLK_XOSC/2", "CLK_XOSC/3", "CLK_XOSC/4",
189             "CLK_XOSC/6", "CLK_XOSC/8", "CLK_XOSC/12", "CLK_XOSC/16", "CLK_XOSC/24", "CLK_XOSC/32", "CLK_XOSC/48", "CLK_XOSC/64",
190             "CLK_XOSC/96", "CLK_XOSC/128", "CLK_XOSC/192"
191         ]
192         # Read IOCFG2 register
193         self.writecmd(self.APP, self.PEEK, 2, [ 0x00, 0x00 ])
194         self.int_state = ord(self.data[0])
195         return _state[ord(self.data[1]) & 0x3F]
196
197     def internal_status(self):
198         """Read the status byte."""
199         _statestr = [ "IDLE", "RX", "TX", "FSTXON", "CALIBRATE", "SETTLING", "RXFIFO_OVERFLOW", "TXFIFO_UNDERFLOW" ]
200         status = self.int_state
201         fifo = status & 7
202         state = (status >> 3) & 7
203         ready = status >> 6
204         return "State: %s - Ready: %s - FIFO available bytes: %d" % (_statestr[state], bool(ready == 0), fifo)
205
206     def regs(self):
207         rv = []
208         vals = self.peek(0xC0, bytes=0x2F)
209         for i in xrange(0x2f):
210             rv.append((self._regnames[0xC0 + i], vals[i]))
211         return rv
212
213     #Radio stuff begins here.
214     _bandwith = [ 812, 650, 541, 464, 406, 325, 270, 232, 203, 162, 135, 116, 102, 81, 68, 58 ]
215     def RF_setbandwith(self, bw):
216         """Sets the bandwith"""
217         try:
218             conf = self._bandwith.index(bw)
219             conf <<= 4
220             conf |= self.peek_by_name("MDMCFG4") & 0x0f
221             self.poke_by_name("MDMCFG4", conf)
222         except:
223             print "ERROR: unsupported bandwith (%d KHz). Should be %s" % (bw, ", ".join(self._bandwith))
224     def RF_getbandwith(self):
225         reg = self.peek_by_name("MDMCFG4")
226         bw_e = (reg >> 6) & 0x3
227         bw_m = (reg >> 4) & 0x3
228         conf = (bw_e << 2) + bw_m
229         return self._bandwith[conf]
230
231     def RF_get_frequency(self):
232         vals = self.peek(0x0D, 3)
233         freq = (vals[0] << 16) + (vals[1] << 8) + vals[2]
234         return freq * 26000000.0 / 2**16
235
236     def RF_set_frequency(self, freq):
237         """freq is in Hz"""
238         freq = int(round(freq*2**16/26000000))
239         self.peek_by_name("FREQ2", freq >> 16)
240         self.peek_by_name("FREQ1", (freq >> 8) & 0x0ff)
241         self.peek_by_name("FREQ0", freq & 0x0ff)
242
243     def RF_get_IF_freq(self):
244         return (self.peek_by_name("FSCTRL1") & 15) * 26000000.0 / 2**10
245
246     def RF_set_channel(self, n):
247         self.poke_by_name("CHANNR", n % 256)
248     def RF_get_channel(self):
249         return self.peek_by_name("CHANNR")
250
251     def RF_get_chan_spacing(self):
252         space = (256 + self.peek_by_name("MDMCFG0")) * 2**((self.peek_by_name("MDMCFG1") & 3) - 2)
253         return space * 26000000.0 / 2**16
254
255     def RF_get_addr(self):
256         return self.peek_by_name("ADDR")
257     def RF_set_addr(self, addr):
258         return self.poke_by_name("ADDR", addr % 256)
259     def RF_enable_whitening(self):
260         self.poke_by_name("PKTCTRL0", self.peek_by_name("PKTCTRL0") | 0x40)
261         self.RF_disable_CC2400()
262     def RF_disable_whitening(self):
263         self.poke_by_name("PKTCTRL0", (self.peek_by_name("PKTCTRL0") & ~0x40) % 256)
264     def RF_enable_CC2400(self):
265         self.poke_by_name("PKTCTRL0", self.peek_by_name("PKTCTRL0") | 0x8)
266         self.RF_disable_whitening()
267         self.RF_disable_CRC_autoflush()
268     def RF_disable_CC2400(self):
269         self.poke_by_name("PKTCTRL0", (self.peek_by_name("PKTCTRL0") & ~0x8) % 256)
270     def RF_enable_CRC(self):
271         self.poke_by_name("PKTCTRL0", self.peek_by_name("PKTCTRL0") | 0x4)
272     def RF_disable_CRC(self):
273         self.poke_by_name("PKTCTRL0", (self.peek_by_name("PKTCTRL0") & ~0x4) % 256)
274     def RF_get_pkt_len(self):
275         """Returns packet length.
276         Positive integer means fixed.
277         Negative integer means a variable packet with at most N bytes.
278         None means Infinite length."""
279         conf = self.peek_by_name("PKTCTRL0") & 0x3
280         if conf == 0:
281             # Fixed
282             return self.peek_by_name("PKTLEN")
283         elif conf == 1:
284             # Variable
285             return -self.peek_by_name("PKTLEN")
286         elif conf == 2:
287             # infinite
288             return None
289         else:
290             # Reserved
291             print "ERROR: Reserved packet length"
292     def RF_set_pkt_len(self, size):
293         """Sets packet length.
294         Positive integer means fixed.
295         Negative integer means a variable packet with at most N bytes.
296         None means Infinite length."""
297         if size is None:
298             self.poke_by_name((self.peek_by_name("PKTCTRL0") & 0x0fc) | 0x2)
299         elif size >= 0:
300             self.poke_by_name((self.peek_by_name("PKTCTRL0") & 0x0fc) | 0x0)
301             self.poke_by_name("PKTLEN", abs(size))
302         else:
303             self.poke_by_name((self.peek_by_name("PKTCTRL0") & 0x0fc) | 0x1)
304             self.poke_by_name("PKTLEN", abs(size))
305
306     def RF_get_syncword(self):
307         return (self.peek_by_name("SYNC1") << 8) + self.peek_by_name("SYNC0")
308     def RF_set_syncword(self, sync):
309         self.poke_by_name("SYNC1", (sync >> 8) & 0x0ff)
310         self.poke_by_name("SYNC0", sync & 0x0ff)
311
312     def RF_get_datarate(self):
313         """return the configured value in bauds"""
314         drate_m = self.peek_by_name("MDMCFG3")
315         drate_e = self.peek_by_name("MDMCFG4") & 0x0f
316         return 26000000 * ((256 + drate_m) * 2**drate_e)/2**28
317     def RF_set_datarate(self, rate):
318         """rate is expressed as a float in bauds"""
319         drate_e = int(math.floor(math.log(rate * 2**20 / 26000000.0, 2)))
320         drate_m = int(round(((rate * 2**28) / (26000000.0 * 2**drate_e)) - 256))
321         if drate_m >= 256:
322             drate_m -= 256
323             drate_e += 1
324         if drate_e < 0 or drate_e > 15:
325             print "ERROR: can't set datarate (too high or too low)"
326         else:
327             self.poke_by_name("MDMCFG3", drate_m % 256)
328             self.poke_by_name("MDMCFG4", (self.peek_by_name("MDMCFG4") & 0xf0) | drate_e)
329
330     def RF_set_modulation(self, mod):
331         """modulation can be 2-FSK, GFSK, OOK or MSK"""
332         _vals = { "2-FSK": 0, "GFSK": 1, "OOK": 3, "MSK": 7}
333         if mod not in _vals:
334             print "ERROR: invalid modulation. Must be: %s" % ", ".join(_vals.keys())
335         else:
336             self.poke_by_name("MDMCFG2", (self.peek_by_name("MDMCFG2") & 0x8f) | _vals[mod])
337     def RF_get_modulation(self):
338         _vals = [ "2-FSK", "GFSK", "? (2)", "OOK", "? (4)", "? (5)", "? (6)", "MSK" ]
339         return _vals[(self.peek_by_name("MDMCFG2") & 0x70) >> 4]
340
341     def RF_enable_manchester(self):
342         self.poke_by_name("MDMCFG2", self.peek_by_name("MDMCFG2") | 0x8)
343     def RF_disable_manchester(self):
344         self.poke_by_name("MDMCFG2", (self.peek_by_name("MDMCFG2") & ~0x8) % 256)
345
346     def RF_set_preamble_len(self, length):
347         _valid = [2, 3, 4, 6, 8, 12, 16, 24]
348         if length not in _valid:
349             print "ERROR: preamble len should be %s" % ", ".join(_valid)
350         else:
351             self.poke_by_name("MDMCFG1", (self.peek_by_name("MDMCFG1") & 0x8f) + (length<<4))
352     def RF_get_preamble_len(self):
353         _vals = [2, 3, 4, 6, 8, 12, 16, 24]
354         return _vals[(self.peek_by_name("MDMCFG1") >> 4) & 0x7]
355
356     def RF_set_rxoff_mode(self, mode):
357         _valid = { "IDLE": 0, "FSTXON": 1, "TX": 2, "RX": 3}
358         if mode not in _valid:
359             print "ERROR: mode should be " % ", ".join(_valid)
360         else:
361             self.poke_by_name("MCSM1", (self.peek_by_name("MCSM1") & 0xf3) + (_valid[mode] << 2))
362     def RF_get_rxoff_mode(self):
363         _vals = ["IDLE", "FSTXON", "TX", "RX"]
364         return _vals[(self.peek_by_name("MCSM1") & 0x0c) >> 2]
365
366     def RF_set_txoff_mode(self, mode):
367         _valid = { "IDLE": 0, "FSTXON": 1, "TX": 2, "RX": 3}
368         if mode not in _valid:
369             print "ERROR: mode should be " % ", ".join(_valid)
370         else:
371             self.poke_by_name("MCSM1", (self.peek_by_name("MCSM1") & 0xfc) + _valid[mode])
372     def RF_get_txoff_mode(self):
373         _vals = ["IDLE", "FSTXON", "TX", "RX"]
374         return _vals[self.peek_by_name("MCSM1") & 0x03]
375
376     def RF_get_RSSI(self):
377         rssi = self.peek_by_name("RSSI")
378         if rssi >= 128:
379             rssi = (rssi - 256) / 2.0
380         else:
381             rssi /= 2.0
382         rssi -= 71.0 ## mean rssi offset. Not accurate
383
384     def RF_carrier(self):
385         print "ERROR: RF Carrier is not implemented"
386
387     def RF_rxpacket(self, bytes=None):
388         """Get a packet from the radio.  Returns None if none is
389         waiting."""
390         data = "\0"
391         self.data = data
392         self.writecmd(self.APP, self.RX, len(data), data)
393         if len(self.data) == 0:
394             return None
395         return self.data
396
397     def RF_txpacket(self,packet):
398         """Send a packet through the radio."""
399         if isinstance(packet, str):
400             packet = [ ord(x) for x in packet ]
401         self.writecmd(self.APP, self.TX, len(packet), packet)
402         return
403
404     def RF_rxpacketrepeat(self):
405         self.writecmd(self.APP, self.REPEAT_RX, 0, None)
406