Mercurial > ~darius > hgwebdir.cgi > ZigBee
annotate zb.py @ 8:9f0808b13454
Use non-blocking serial access. Add __str__ method for RX packets.
author | darius@inchoate.localdomain |
---|---|
date | Sun, 04 Nov 2007 21:10:21 +1030 |
parents | 579dedf5a1f1 |
children | d147529ad2db |
rev | line source |
---|---|
4 | 1 import serial, inspect |
1 | 2 |
3 class PktBase(object): | |
4 | 4 def __init__(self, data = []): |
1 | 5 self.data = data |
6 print "Constructing " + self.__class__.__name__ | |
7 | |
8 def __repr__(self): | |
9 return str(self.__class__) + "(" + str([self.PKT_TYPE] + self.data) + ")" | |
10 | |
4 | 11 def Encapsulate(self): |
12 return Packets.Encapsulate([self.PKT_TYPE] + self.data) | |
1 | 13 |
4 | 14 def resize(self, dlen): |
15 """Ensure the data list can hold at least len elements (0 fill)""" | |
16 if (len(self.data) < dlen): | |
17 self.data = (self.data + [0] * dlen)[0:dlen] | |
18 | |
1 | 19 class AT_Cmd(PktBase): |
20 PKT_TYPE = 0x08 | |
21 PKT_DESC = "AT Command" | |
22 | |
4 | 23 def __init__(self, *args): |
24 if (len(args) == 1): | |
6 | 25 if (type(args[0]) == type(str())): |
26 super(AT_Cmd, self).__init__([]) | |
27 self.cmd = args[0] | |
28 else: | |
29 super(AT_Cmd, self).__init__(args[0]) | |
4 | 30 elif (len(args) == 2): |
31 super(AT_Cmd, self).__init__([]) | |
32 self.cmd = args[0] | |
6 | 33 self.cmdarg = args[1] |
4 | 34 else: |
6 | 35 raise TypeError("__init__ takes 1 list of ordinals, 1 string, or 2 strings") |
36 | |
37 self.data[0] = 1 # XXX: could be smarter about Frame ID | |
4 | 38 |
39 def setcmd(self, value): | |
6 | 40 self.resize(3) |
41 self.data[1] = ord(value[0]) | |
42 self.data[2] = ord(value[1]) | |
43 cmd = property(lambda s: chr(s.data[1]) + chr(s.data[2]), setcmd) | |
4 | 44 |
45 def setcmdarg(self, value): | |
6 | 46 self.resize(3 + len(value)) |
47 self.data = self.data[0:3] + map(ord, value) | |
4 | 48 def delcmdarg(self): |
6 | 49 self.data = self.data[0:3] |
50 cmdarg = property(lambda s: map(chr, s.data[3:]), setcmdarg, delcmdarg) | |
4 | 51 |
52 class AT_Cmd_Queue(AT_Cmd): | |
1 | 53 PKT_TYPE = 0x09 |
54 PKT_DESC = "AT Command (queued)" | |
55 | |
56 class AT_Response(PktBase): | |
57 PKT_TYPE = 0x88 | |
58 PKT_DESC = "AT Command response" | |
6 | 59 frameid = property(lambda s: s.data[0], None) |
60 cmd = property(lambda s: chr(s.data[1]) + chr(s.data[2]), None) | |
61 statusOK = property(lambda s: s.data[3] == 0, None) | |
62 payload = property(lambda s: s.data[4:], None) | |
1 | 63 |
64 class Modem_Status(PktBase): | |
65 PKT_TYPE = 0x8a | |
66 PKT_DESC = "Modem Status" | |
67 | |
68 class RX_16_Bit(PktBase): | |
69 PKT_TYPE = 0x81 | |
70 PKT_DESC = "RX Packet: 16 bit address" | |
4 | 71 ADDR_SIZE = 2 |
72 | |
8
9f0808b13454
Use non-blocking serial access. Add __str__ method for RX packets.
darius@inchoate.localdomain
parents:
7
diff
changeset
|
73 def __str__(self): |
9f0808b13454
Use non-blocking serial access. Add __str__ method for RX packets.
darius@inchoate.localdomain
parents:
7
diff
changeset
|
74 return "0x%0*x (%ddBm) -> %s" % (self.ADDR_SIZE * 2, self.sender, |
9f0808b13454
Use non-blocking serial access. Add __str__ method for RX packets.
darius@inchoate.localdomain
parents:
7
diff
changeset
|
75 self.rssi, str(self.payload)) |
9f0808b13454
Use non-blocking serial access. Add __str__ method for RX packets.
darius@inchoate.localdomain
parents:
7
diff
changeset
|
76 |
4 | 77 def getsender(self): |
78 value = 0 | |
79 for i, j in zip(reversed(range(self.ADDR_SIZE)), range(0, self.ADDR_SIZE * 8, 8)): | |
80 value |= self.data[i] << j | |
81 return value | |
5 | 82 sender = property(getsender, None) |
83 | |
84 rssi = property(lambda s: -1 * s.data[s.ADDR_SIZE], None) | |
1 | 85 |
5 | 86 flags = property(lambda s: s.data[s.ADDR_SIZE + 1], None) |
4 | 87 |
5 | 88 payload = property(lambda s: s.data[s.ADDR_SIZE + 2:], None) |
89 | |
4 | 90 class RX_64_Bit(RX_16_Bit): |
91 PKT_TYPE = 0x80 | |
92 PKT_DESC = "RX Packet: 64 bit address" | |
93 ADDR_SIZE = 8 | |
94 | |
95 class RXIO_16_Bit(RX_16_Bit): | |
1 | 96 PKT_TYPE = 0x83 |
97 PKT_DESC = "RXIO Packet: 16 bit address" | |
98 | |
4 | 99 def setnsamples(self, value): |
100 self.resize(self.ADDR_SIZE + 3) | |
101 self.data[self.ADDR_SIZE + 2] = value | |
5 | 102 |
103 def getnsamples(self): | |
104 return self.data[self.ADDR_SIZE + 2] | |
105 nsamples = property(getnsamples, setnsamples) | |
4 | 106 |
107 def setmask(self, value): | |
108 self.resize(self.ADDR_SIZE + 5) | |
109 self.data[self.ADDR_SIZE + 3] = (value & 0xff00) >> 8 | |
110 self.data[self.ADDR_SIZE + 4] = value & 0xff | |
111 mask = property(lambda s: s.data[s.ADDR_SIZE + 3] << 8 | s.data[s.ADDR_SIZE + 4], setmask) | |
112 | |
1 | 113 def __str__(self): |
4 | 114 rtn = "0x%0*x (%ddBm) -> %d samples, mask 0x%04x" % (self.ADDR_SIZE * 2, self.sender, |
115 self.rssi, self.nsamples, self.mask) | |
1 | 116 # Any DIO lines enabled? |
4 | 117 if (self.mask | 0x01ff): |
118 rtn = rtn + ", DIO - 0x%03x" % (self.data[self.ADDR_SIZE + 5] << 8 | | |
119 self.data[self.ADDR_SIZE + 6]) | |
120 offs = self.ADDR_SIZE + 7 | |
1 | 121 else: |
4 | 122 offs = self.ADDR_SIZE + 5 |
1 | 123 |
124 # Any ADC lines enabled? | |
4 | 125 if (self.mask | 0x7e00): |
1 | 126 for i in range(6): |
4 | 127 if (self.mask & 1 << (i + 9)): |
1 | 128 rtn = rtn + ", ADC%d - 0x%02x" % (i, self.data[offs] << 8 | |
129 self.data[offs + 1]) | |
130 offs = offs + 2 | |
131 | |
132 return rtn | |
133 | |
5 | 134 class RXIO_64_Bit(RXIO_16_Bit): |
4 | 135 PKT_TYPE = 0x82 |
136 PKT_DESC = "RXIO Packet: 64 bit address" | |
137 ADDR_SIZE = 8 | |
138 | |
5 | 139 class TX_16_Bit(RX_64_Bit): |
6 | 140 PKT_TYPE = 0x01 |
4 | 141 PKT_DESC = "TX Packet: 16 bit address" |
6 | 142 ADDR_SIZE = 2 |
143 | |
144 def __init__(self, *args): | |
145 if (len(args) == 1): | |
146 if (type(args[0]) == type(int())): | |
147 super(TX_16_Bit, self).__init__([]) | |
148 self.recipient = args[0] | |
149 else: | |
150 super(TX_16_Bit, self).__init__(args[0]) | |
151 elif (len(args) == 2): | |
152 super(TX_16_Bit, self).__init__([]) | |
153 self.recipient = args[0] | |
154 self.payload = args[1] | |
155 else: | |
156 raise TypeError("__init__ takes 1 list of ordinals or 2 strings") | |
4 | 157 |
6 | 158 self.resize(self.ADDR_SIZE + 2) |
159 self.data[0] = 1 # XXX: could be smarter about Frame ID | |
160 self.data[3] = 0 # XXX: should be able to set flags | |
161 | |
162 def getrecipient(self): | |
163 value = 0 | |
5 | 164 for i, j in zip(reversed(range(self.ADDR_SIZE)), range(0, self.ADDR_SIZE * 8, 8)): |
6 | 165 value |= self.data[i + 1] << j |
166 return value | |
167 | |
168 def setrecipient(self, value): | |
169 self.resize(self.ADDR_SIZE + 1) | |
170 self.data[0] = 1 # XXX: could be smarter about Frame ID | |
171 for i, j in zip(reversed(range(self.ADDR_SIZE)), range(0, self.ADDR_SIZE * 8, 8)): | |
172 self.data[i + 1] = (value & (0xff << j)) >> j | |
173 recipient = property(getrecipient, setrecipient) | |
5 | 174 |
6 | 175 def setpayload(self, value): |
176 self.resize(self.ADDR_SIZE + 2) | |
177 self.data[self.ADDR_SIZE + 2:] = value | |
178 payload = property(lambda s: s.data[self.ADDR_SIZE + 2:], setpayload) | |
179 | |
180 class TX_64_Bit(TX_16_Bit): | |
1 | 181 PKT_TYPE = 0x00 |
182 PKT_DESC = "TX Packet: 64 bit address" | |
6 | 183 ADDR_SIZE = 8 |
1 | 184 |
185 class TX_Status(PktBase): | |
186 PKT_TYPE = 0x89 | |
187 PKT_DESC = "TX Status" | |
6 | 188 statusTxt = ['OK', 'No Ack', 'CCA failure', 'Purged'] |
189 frameid = property(lambda s: s.data[0], None) | |
190 status = property(lambda s: s.data[1], None) | |
191 statusMsg = property(lambda s: s.statusTxt[s.data[1]], None) | |
192 | |
1 | 193 class Packets(object): |
4 | 194 PKT_CLASSES = None |
1 | 195 |
4 | 196 def Build(self, data): |
197 if (self.PKT_CLASSES == None): | |
198 m = inspect.getmodule(self) | |
199 # Generate list of objects from their names | |
200 mobjs = map(lambda n: m.__dict__[n], m.__dict__) | |
201 # Find all the classes | |
202 pktclasses = filter(inspect.isclass, mobjs) | |
203 # Find all subclasses of PktBase (but not PktBase itself) | |
204 pktclasses = filter(lambda s: issubclass(s, m.PktBase) and s != m.PktBase, pktclasses) | |
205 self.PKT_CLASSES = pktclasses | |
206 | |
207 for p in self.PKT_CLASSES: | |
1 | 208 if (p.PKT_TYPE == data[0]): |
209 return(p(data[1:])) | |
4 | 210 |
211 raise ValueError("Unknown packet type 0x%02x" % (data[0])) | |
212 Build = classmethod(Build) | |
1 | 213 |
214 def Encapsulate(data): | |
215 pktsum = reduce(lambda x, y: x + y, data) & 0xff | |
216 pkt = [0x7e] + [len(data) >> 8] + [len(data) & 0xff] + data + [0xff - pktsum] | |
217 return(map(chr, pkt)) | |
218 | |
219 Encapsulate = staticmethod(Encapsulate) | |
220 | |
6 | 221 def __init__(self, s): |
4 | 222 print str(inspect.getmodule(self)) |
1 | 223 self.buffer = [] |
224 self.state = 'init' | |
225 self.packets = [] | |
226 | |
227 self.bufmsb = 0 | |
228 self.dataleft = 0 | |
229 | |
230 self.fr_err = 0 # Framing error | |
231 self.ck_err = 0 # Checksum error | |
232 self.rx_cnt = 0 # Packet count | |
233 | |
234 self.pktq = [] | |
6 | 235 self.s = s |
236 | |
237 def writedata(self, data): | |
238 self.s.write("".join(map(str, data))) | |
5 | 239 |
6 | 240 def getdata(self): |
1 | 241 l = [] |
242 while (1): | |
6 | 243 a = self.s.read() |
1 | 244 if (a == ''): |
245 break | |
5 | 246 l.append(ord(a)) |
1 | 247 |
248 return self.process(l) | |
249 | |
250 def process(self, data): | |
251 pktcount = 0 | |
252 for d in data: | |
253 if (self.state == 'init'): | |
5 | 254 if (d != 0x7e): |
255 print "Framing error, got 0x%02x, expected 0x7e" % (d) | |
4 | 256 self.fr_err += 1 |
1 | 257 continue |
258 | |
259 self.state = 'sizemsb' | |
260 elif (self.state == 'sizemsb'): | |
5 | 261 self.bufmsb = d |
1 | 262 self.state = 'sizelsb' |
263 elif (self.state == 'sizelsb'): | |
7
579dedf5a1f1
Rejoin line break, left over from removal of struct.
darius@inchoate.localdomain
parents:
6
diff
changeset
|
264 self.dataleft = self.bufmsb << 8 | d |
1 | 265 self.state = 'data' |
266 elif (self.state == 'data'): | |
5 | 267 self.buffer.append(d) |
1 | 268 self.dataleft = self.dataleft - 1 |
269 if (self.dataleft == 0): | |
270 self.state = 'cksum' | |
271 elif (self.state == 'cksum'): | |
272 pktsum = reduce(lambda x, y: x + y, self.buffer) & 0xff | |
5 | 273 rxcksum = d |
1 | 274 self.state = 'init' |
275 if (pktsum + rxcksum != 0xff): | |
276 self.buffer = [] | |
4 | 277 self.ck_err += 1 |
1 | 278 print "Checksum error, got 0x%02x, expected 0x%02x" % \ |
279 (rxcksum, 0xff - pktsum) | |
280 else: | |
6 | 281 print "Got a packet - " + str(self.buffer) |
1 | 282 p = Packets.Build(self.buffer) |
283 self.pktq.append(p) | |
284 self.buffer = [] | |
4 | 285 pktcount += 1 |
286 self.rx_cnt += 1 | |
1 | 287 else: |
288 print "Invalid state %s! Resetting" % (self.state) | |
289 self.state = 'init' | |
290 | |
291 return pktcount | |
292 | |
293 #for c in dir(): | |
294 # if (issubclass(c, PktBase)): | |
295 # print .. | |
0
bdcdd8380d94
Some test routines & framework for talking to MaxStream ZigBee modules.
darius@inchoate.localdomain
parents:
diff
changeset
|
296 |
bdcdd8380d94
Some test routines & framework for talking to MaxStream ZigBee modules.
darius@inchoate.localdomain
parents:
diff
changeset
|
297 s = serial.Serial(port='/dev/cuad0', baudrate=9600, bytesize=8, parity='N', \ |
bdcdd8380d94
Some test routines & framework for talking to MaxStream ZigBee modules.
darius@inchoate.localdomain
parents:
diff
changeset
|
298 stopbits=1, rtscts=0) |
8
9f0808b13454
Use non-blocking serial access. Add __str__ method for RX packets.
darius@inchoate.localdomain
parents:
7
diff
changeset
|
299 # Non-blocking |
9f0808b13454
Use non-blocking serial access. Add __str__ method for RX packets.
darius@inchoate.localdomain
parents:
7
diff
changeset
|
300 s.timeout = 0 |
0
bdcdd8380d94
Some test routines & framework for talking to MaxStream ZigBee modules.
darius@inchoate.localdomain
parents:
diff
changeset
|
301 #s.write('+++') |
bdcdd8380d94
Some test routines & framework for talking to MaxStream ZigBee modules.
darius@inchoate.localdomain
parents:
diff
changeset
|
302 #s.readline(eol='\r') |
bdcdd8380d94
Some test routines & framework for talking to MaxStream ZigBee modules.
darius@inchoate.localdomain
parents:
diff
changeset
|
303 |
4 | 304 |
305 # 0x0001 (-36dBm) -> 1 samples, mask 0x000f, DIO - 0x00f | |
5 | 306 goodtest = [126, 0, 10, 131, 0, 1, 36, 0, 1, 0, 15, 0, 15, 56] |
4 | 307 |
308 # Checksum error | |
5 | 309 badtest = [126, 0, 10, 131, 0, 1, 36, 0, 1, 0, 15, 0, 14, 56] |
4 | 310 |
311 #0x0005 (-36dBm) -> 1 samples, mask 0x020e, DIO - 0x00e, ADC0 - 0x3ff | |
5 | 312 adctest = [126, 0, 12, 131, 0, 5, 36, 0, 1, 2, 14, 0, 14, 3, 255, 50] |
4 | 313 |
314 # Exception | |
5 | 315 badpkttypetest = [126, 0, 3, 10, 86, 76, 83] |
4 | 316 |
6 | 317 up = Packets(s) |
1 | 318 up.process(goodtest) |
3
7bf4d4265339
Pop off the packets we test with.
darius@inchoate.localdomain
parents:
1
diff
changeset
|
319 up.process(badtest) |
7bf4d4265339
Pop off the packets we test with.
darius@inchoate.localdomain
parents:
1
diff
changeset
|
320 up.process(adctest) |
7bf4d4265339
Pop off the packets we test with.
darius@inchoate.localdomain
parents:
1
diff
changeset
|
321 print up.pktq.pop(0) |
7bf4d4265339
Pop off the packets we test with.
darius@inchoate.localdomain
parents:
1
diff
changeset
|
322 print up.pktq.pop(0) |
0
bdcdd8380d94
Some test routines & framework for talking to MaxStream ZigBee modules.
darius@inchoate.localdomain
parents:
diff
changeset
|
323 |