Mercurial > ~darius > hgwebdir.cgi > ZigBee
annotate zb.py @ 15:a472d6eab97e
Play back the last 5 lines to newly connected clients rather than 1.
author | darius@Inchoate |
---|---|
date | Sat, 17 Jan 2009 21:43:59 +1030 |
parents | 729f2393f296 |
children | c6ee9eae9e49 |
rev | line source |
---|---|
11 | 1 # |
2 # Code to talk to MaxStream ZigBee modules in API (no escaped | |
3 # characters) | |
4 # | |
5 # Copyright (c) 2009 | |
6 # Daniel O'Connor <darius@dons.net.au>. All rights reserved. | |
7 # | |
8 # Redistribution and use in source and binary forms, with or without | |
9 # modification, are permitted provided that the following conditions | |
10 # are met: | |
11 # 1. Redistributions of source code must retain the above copyright | |
12 # notice, this list of conditions and the following disclaimer. | |
13 # 2. Redistributions in binary form must reproduce the above copyright | |
14 # notice, this list of conditions and the following disclaimer in the | |
15 # documentation and/or other materials provided with the distribution. | |
16 # | |
17 # THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
18 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 # ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE | |
21 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 # SUCH DAMAGE. | |
28 # | |
29 """MaxStream ZigBee module API interface | |
30 | |
31 This code expects the module to be in API mode 1 (no escape | |
32 characters), ie you have done something like.. | |
33 +++ | |
34 ATAP=1 | |
35 ATWR | |
36 ATCN | |
37 | |
38 See here for details | |
39 http://www.digi.com/products/wireless/point-multipoint/xbee-series1-moduledocs.jsp | |
40 """ | |
41 | |
42 import inspect | |
1 | 43 |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
44 def easyord(i): |
11 | 45 """Helper function to return the ordinal of a string, or just the |
46 passed in value""" | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
47 if (type(i) != type(str())): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
48 return i |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
49 else: |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
50 return ord(i) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
51 |
1 | 52 class PktBase(object): |
11 | 53 """Base class for all packet types""" |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
54 PKT_MAXLEN = 2 ** 16 |
11 | 55 PKT_TYPE = None |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
56 |
10
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
57 def __init__(self): |
11 | 58 #print "Constructing " + self.__class__.__name__ |
59 pass | |
60 | |
61 def Encapsulate(self): | |
62 """Encapsulate the packet""" | |
63 return Packets.Encapsulate([self.PKT_TYPE] + self.data) | |
1 | 64 |
11 | 65 def Pack(self): |
66 """Return string version of encapsulated packet""" | |
67 return reduce(lambda x, y: str(x) + str(y), self.Encapsulate()) | |
1 | 68 |
4 | 69 def resize(self, dlen): |
70 """Ensure the data list can hold at least len elements (0 fill)""" | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
71 if (len(self._data) < dlen): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
72 self._data = (self._data + [0] * dlen)[0:dlen] |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
73 |
11 | 74 @staticmethod |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
75 def _checklist(list, min = 0, max = 255, maxlen = None): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
76 if (maxlen != None and len(list) > maxlen): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
77 raise ValueError("must have %d elements" % (maxlen)) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
78 |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
79 for i in xrange(len(list)): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
80 if (easyord(list[i]) < min or easyord(list[i]) > max): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
81 raise ValueError("element %d (= %d) out of range must be between %d and %d inclusive" % |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
82 (i, ord(list[i]), min, max)) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
83 |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
84 class TXPkts(PktBase): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
85 """Base class for all packets that go to the module""" |
10
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
86 |
13
729f2393f296
Auto increment frame ID when creating a TX packet.
darius@Inchoate
parents:
11
diff
changeset
|
87 frameidcounter = 0 |
729f2393f296
Auto increment frame ID when creating a TX packet.
darius@Inchoate
parents:
11
diff
changeset
|
88 |
11 | 89 def __init__(self): |
13
729f2393f296
Auto increment frame ID when creating a TX packet.
darius@Inchoate
parents:
11
diff
changeset
|
90 self._frameid = self.getNextFrameId() |
729f2393f296
Auto increment frame ID when creating a TX packet.
darius@Inchoate
parents:
11
diff
changeset
|
91 |
729f2393f296
Auto increment frame ID when creating a TX packet.
darius@Inchoate
parents:
11
diff
changeset
|
92 @classmethod |
729f2393f296
Auto increment frame ID when creating a TX packet.
darius@Inchoate
parents:
11
diff
changeset
|
93 def getNextFrameId(clss): |
729f2393f296
Auto increment frame ID when creating a TX packet.
darius@Inchoate
parents:
11
diff
changeset
|
94 """Generate the next frame ID, skip 0 as it will cause the module to not send a response back""" |
729f2393f296
Auto increment frame ID when creating a TX packet.
darius@Inchoate
parents:
11
diff
changeset
|
95 clss.frameidcounter = (clss.frameidcounter + 1) % 255 |
729f2393f296
Auto increment frame ID when creating a TX packet.
darius@Inchoate
parents:
11
diff
changeset
|
96 |
729f2393f296
Auto increment frame ID when creating a TX packet.
darius@Inchoate
parents:
11
diff
changeset
|
97 if clss.frameidcounter == 0: |
729f2393f296
Auto increment frame ID when creating a TX packet.
darius@Inchoate
parents:
11
diff
changeset
|
98 clss.frameidcounter = 1 |
729f2393f296
Auto increment frame ID when creating a TX packet.
darius@Inchoate
parents:
11
diff
changeset
|
99 |
729f2393f296
Auto increment frame ID when creating a TX packet.
darius@Inchoate
parents:
11
diff
changeset
|
100 return clss.frameidcounter |
729f2393f296
Auto increment frame ID when creating a TX packet.
darius@Inchoate
parents:
11
diff
changeset
|
101 |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
102 def setframeid(self, value): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
103 if (value < 0 or value > 255): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
104 raise ValueError("FrameID must be 0-255") |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
105 self._frameid = value |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
106 frameid = property(lambda s: s._frameid, setframeid) |
4 | 107 |
11 | 108 class AT_Cmd(TXPkts): |
109 """AT command packet""" | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
110 |
1 | 111 PKT_TYPE = 0x08 |
112 PKT_DESC = "AT Command" | |
113 | |
10
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
114 def __init__(self, cmd = None, cmdarg = None): |
11 | 115 self.frameid = 1 # XXX: why do I need to dupe this? |
116 self.cmdarg = [] | |
117 | |
10
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
118 super(AT_Cmd, self).__init__() |
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
119 if (cmd != None): |
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
120 self.cmd = cmd |
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
121 if (cmdarg != None): |
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
122 self.cmdarg = cmdarg |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
123 |
4 | 124 def setcmd(self, value): |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
125 if (len(value) != 2): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
126 raise ValueError("must have 2 elements") |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
127 self._checklist(value, ord('0'), ord('z')) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
128 self._cmd = value |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
129 cmd = property(lambda s: s._cmd, setcmd) |
4 | 130 |
131 def setcmdarg(self, value): | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
132 self._checklist(value, maxlen = self.PKT_MAXLEN - 3) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
133 self._cmdarg = value |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
134 cmdarg = property(lambda s: s._cmdarg, setcmdarg) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
135 |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
136 def getdata(self): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
137 return([self.frameid] + map(ord, self.cmd) + map(easyord, self.cmdarg)) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
138 data = property(getdata) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
139 |
4 | 140 class AT_Cmd_Queue(AT_Cmd): |
11 | 141 """Queued AT command packet""" |
142 | |
1 | 143 PKT_TYPE = 0x09 |
144 PKT_DESC = "AT Command (queued)" | |
145 | |
146 class AT_Response(PktBase): | |
11 | 147 """Response from an AT command packet""" |
148 | |
1 | 149 PKT_TYPE = 0x88 |
150 PKT_DESC = "AT Command response" | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
151 frameid = property(lambda s: s._data[0], None) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
152 cmd = property(lambda s: chr(s._data[1]) + chr(s._data[2]), None) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
153 statusOK = property(lambda s: s._data[3] == 0, None) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
154 payload = property(lambda s: s._data[4:], None) |
1 | 155 |
11 | 156 def __init__(self, data = []): |
157 super(AT_Response, self).__init__() | |
158 self._data = data | |
159 | |
1 | 160 class Modem_Status(PktBase): |
161 PKT_TYPE = 0x8a | |
162 PKT_DESC = "Modem Status" | |
163 | |
164 class RX_16_Bit(PktBase): | |
11 | 165 """RX packet from a remote module (16 bit)""" |
1 | 166 PKT_TYPE = 0x81 |
167 PKT_DESC = "RX Packet: 16 bit address" | |
4 | 168 ADDR_SIZE = 2 |
11 | 169 ADRBCASTMSK = 0x01 |
170 PANBCASTMSK = 0x02 | |
4 | 171 |
10
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
172 def __init__(self, data = []): |
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
173 super(RX_16_Bit, self).__init__() |
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
174 self._data = data |
11 | 175 |
8
9f0808b13454
Use non-blocking serial access. Add __str__ method for RX packets.
darius@inchoate.localdomain
parents:
7
diff
changeset
|
176 def __str__(self): |
11 | 177 return "RX_%d_Bit 0x%0*x (%ddBm) -> %s" % (self.ADDR_SIZE * 8, self.ADDR_SIZE * 2, |
178 self.sender, self.rssi, self.payloadstr) | |
4 | 179 def getsender(self): |
180 value = 0 | |
11 | 181 # Done this way so we can reuse the code for the 64 bit version |
4 | 182 for i, j in zip(reversed(range(self.ADDR_SIZE)), range(0, self.ADDR_SIZE * 8, 8)): |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
183 value |= self._data[i] << j |
4 | 184 return value |
11 | 185 #: Source module address |
5 | 186 sender = property(getsender, None) |
187 | |
11 | 188 def isAdrBcast(self): |
189 """Is this an address broadcast packet?""" | |
190 return self.flags & self.ADRBCASTMSK | |
191 | |
192 def isPanBcast(self): | |
193 """Is this an PAN broadcast packet?""" | |
194 return self.flags & self.PANBCASTMSK | |
195 | |
196 #: RX signal strength (dBm) | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
197 rssi = property(lambda s: -1 * s._data[s.ADDR_SIZE], None) |
1 | 198 |
11 | 199 #: Return flag byte |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
200 flags = property(lambda s: s._data[s.ADDR_SIZE + 1], None) |
4 | 201 |
11 | 202 #: Payload (list of ords) |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
203 payload = property(lambda s: s._data[s.ADDR_SIZE + 2:], None) |
11 | 204 |
205 #: String version of payload | |
206 def payloadstr(self): | |
207 return reduce(lambda a, b: a + chr(b), self.payload, "") | |
208 payloadstr = property(payloadstr, None) | |
209 | |
210 | |
4 | 211 class RX_64_Bit(RX_16_Bit): |
212 PKT_TYPE = 0x80 | |
213 PKT_DESC = "RX Packet: 64 bit address" | |
214 ADDR_SIZE = 8 | |
215 | |
216 class RXIO_16_Bit(RX_16_Bit): | |
11 | 217 """RX I/O packet from remote module (16 bit). |
218 | |
219 This is sent when a remote module is configured to send data based on its IO or DAC pins | |
220 """ | |
1 | 221 PKT_TYPE = 0x83 |
222 PKT_DESC = "RXIO Packet: 16 bit address" | |
223 | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
224 nsamples = property(lambda s: s._data[s.ADDR_SIZE + 2]) |
5 | 225 |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
226 mask = property(lambda s: s._data[s.ADDR_SIZE + 3] << 8 | s._data[s.ADDR_SIZE + 4]) |
4 | 227 |
1 | 228 def __str__(self): |
4 | 229 rtn = "0x%0*x (%ddBm) -> %d samples, mask 0x%04x" % (self.ADDR_SIZE * 2, self.sender, |
230 self.rssi, self.nsamples, self.mask) | |
1 | 231 # Any DIO lines enabled? |
4 | 232 if (self.mask | 0x01ff): |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
233 rtn = rtn + ", DIO - 0x%03x" % (self._data[self.ADDR_SIZE + 5] << 8 | |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
234 self._data[self.ADDR_SIZE + 6]) |
4 | 235 offs = self.ADDR_SIZE + 7 |
1 | 236 else: |
4 | 237 offs = self.ADDR_SIZE + 5 |
1 | 238 |
239 # Any ADC lines enabled? | |
4 | 240 if (self.mask | 0x7e00): |
1 | 241 for i in range(6): |
4 | 242 if (self.mask & 1 << (i + 9)): |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
243 rtn = rtn + ", ADC%d - 0x%02x" % (i, self._data[offs] << 8 | |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
244 self._data[offs + 1]) |
1 | 245 offs = offs + 2 |
246 return rtn | |
247 | |
5 | 248 class RXIO_64_Bit(RXIO_16_Bit): |
4 | 249 PKT_TYPE = 0x82 |
250 PKT_DESC = "RXIO Packet: 64 bit address" | |
251 ADDR_SIZE = 8 | |
252 | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
253 class TX_16_Bit(TXPkts): |
11 | 254 """Transmit to a 16 bit destination""" |
6 | 255 PKT_TYPE = 0x01 |
4 | 256 PKT_DESC = "TX Packet: 16 bit address" |
6 | 257 ADDR_SIZE = 2 |
11 | 258 #: Flag to disable ACK |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
259 FLG_DISABLE_ACK = 0x01 |
11 | 260 #: Send to broadcast PAN ID |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
261 FLG_BCAST_PANID = 0x04 |
11 | 262 #: Maximum size payload we can send |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
263 PKT_MAX_PAYLOAD = 100 |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
264 |
6 | 265 def __init__(self, *args): |
11 | 266 """Takes 0 to 2 arguments. First is the recipient, the second is the payload (string)""" |
267 self._flags = 0 | |
268 self.payload = [] | |
269 | |
270 if len(args) == 0: | |
271 pass | |
272 if len(args) == 1: | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
273 super(TX_16_Bit, self).__init__() |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
274 self.recipient = args[0] |
11 | 275 elif len(args) == 2: |
276 super(TX_16_Bit, self).__init__() | |
6 | 277 self.recipient = args[0] |
278 self.payload = args[1] | |
279 else: | |
11 | 280 raise TypeError("incorrect number of arguments"); |
4 | 281 |
11 | 282 def __str__(self): |
13
729f2393f296
Auto increment frame ID when creating a TX packet.
darius@Inchoate
parents:
11
diff
changeset
|
283 return "TX_%d_Bit ID: %d 0x%0*x <- %s" % (self.ADDR_SIZE * 8, self._frameid, self.ADDR_SIZE * 2, |
729f2393f296
Auto increment frame ID when creating a TX packet.
darius@Inchoate
parents:
11
diff
changeset
|
284 self.recipient, self.payload) |
11 | 285 |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
286 def setrecipient(self, value): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
287 if (value < 0 or value > 2 ** (self.ADDR_SIZE * 8)): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
288 raise ValueError("value out of range must be between 0 and %d" % (2 ** self.ADDR_SIZE)) |
6 | 289 |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
290 self._recipient = value |
11 | 291 |
292 """Destination address of the packet""" | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
293 recipient = property(lambda s: s._recipient, setrecipient) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
294 |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
295 def setflags(self, value): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
296 if (value < 0 or value > 255): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
297 raise ValueError("Value must be between 0 and 255 inclusive") |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
298 |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
299 self._flags = value |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
300 flags = property(lambda s: s._flags, setflags) |
5 | 301 |
6 | 302 def setpayload(self, value): |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
303 self._checklist(value, maxlen = self.PKT_MAX_PAYLOAD) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
304 self._payload = value |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
305 payload = property(lambda s: s._payload, setpayload) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
306 |
11 | 307 def payloadstr(self): |
308 return reduce(lambda a, b: a + chr(b), self.payload, "") | |
309 payloadstr = property(payloadstr, None) | |
310 | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
311 def getdata(self): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
312 data = [self.frameid] |
11 | 313 for i, j in zip(reversed(range(self.ADDR_SIZE)), reversed(range(0, self.ADDR_SIZE * 8, 8))): |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
314 data.append((self.recipient & (0xff << j)) >> j) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
315 data.append(self.flags) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
316 data.extend(map(easyord, self.payload)) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
317 return(data) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
318 data = property(getdata) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
319 |
6 | 320 class TX_64_Bit(TX_16_Bit): |
1 | 321 PKT_TYPE = 0x00 |
322 PKT_DESC = "TX Packet: 64 bit address" | |
6 | 323 ADDR_SIZE = 8 |
1 | 324 |
325 class TX_Status(PktBase): | |
326 PKT_TYPE = 0x89 | |
327 PKT_DESC = "TX Status" | |
6 | 328 statusTxt = ['OK', 'No Ack', 'CCA failure', 'Purged'] |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
329 frameid = property(lambda s: s._data[0], None) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
330 status = property(lambda s: s._data[1], None) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
331 statusMsg = property(lambda s: s.statusTxt[s._data[1]], None) |
11 | 332 |
333 def __init__(self, data = []): | |
334 super(TX_Status, self).__init__() | |
335 self._data = data | |
336 | |
1 | 337 class Packets(object): |
11 | 338 """Packet parsing class (misnamed)""" |
4 | 339 PKT_CLASSES = None |
11 | 340 |
341 @classmethod | |
4 | 342 def Build(self, data): |
11 | 343 """Build a packet from data""" |
4 | 344 if (self.PKT_CLASSES == None): |
345 m = inspect.getmodule(self) | |
346 # Generate list of objects from their names | |
347 mobjs = map(lambda n: m.__dict__[n], m.__dict__) | |
348 # Find all the classes | |
349 pktclasses = filter(inspect.isclass, mobjs) | |
350 # Find all subclasses of PktBase (but not PktBase itself) | |
351 pktclasses = filter(lambda s: issubclass(s, m.PktBase) and s != m.PktBase, pktclasses) | |
352 self.PKT_CLASSES = pktclasses | |
353 | |
354 for p in self.PKT_CLASSES: | |
1 | 355 if (p.PKT_TYPE == data[0]): |
11 | 356 #print "Matched " + str(p.PKT_TYPE) |
1 | 357 return(p(data[1:])) |
4 | 358 |
359 raise ValueError("Unknown packet type 0x%02x" % (data[0])) | |
1 | 360 |
11 | 361 @staticmethod |
1 | 362 def Encapsulate(data): |
11 | 363 """Encapsulate a packet so it can be sent to the module. Calculates checksum etc..""" |
1 | 364 pktsum = reduce(lambda x, y: x + y, data) & 0xff |
365 pkt = [0x7e] + [len(data) >> 8] + [len(data) & 0xff] + data + [0xff - pktsum] | |
366 return(map(chr, pkt)) | |
367 | |
10
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
368 def __init__(self, s = None): |
11 | 369 """Init class, if s is passed in it is used for reading & writing data""" |
370 #print str(inspect.getmodule(self)) | |
1 | 371 self.buffer = [] |
372 self.state = 'init' | |
373 self.packets = [] | |
374 | |
375 self.bufmsb = 0 | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
376 self._dataleft = 0 |
1 | 377 |
378 self.fr_err = 0 # Framing error | |
379 self.ck_err = 0 # Checksum error | |
380 self.rx_cnt = 0 # Packet count | |
381 | |
382 self.pktq = [] | |
11 | 383 self.s = s # Output handle for convenience methods |
6 | 384 |
385 def writedata(self, data): | |
11 | 386 """Convenience method to write data""" |
6 | 387 self.s.write("".join(map(str, data))) |
5 | 388 |
6 | 389 def getdata(self): |
11 | 390 """Read data until nothing is available (assumes non-blocking) and process it""" |
1 | 391 l = [] |
11 | 392 while 1: |
6 | 393 a = self.s.read() |
11 | 394 if a == '': |
1 | 395 break |
5 | 396 l.append(ord(a)) |
1 | 397 |
398 return self.process(l) | |
399 | |
11 | 400 def processstr(self, data): |
401 """Process a string of data""" | |
402 return self.process(map(ord, data)) | |
403 | |
1 | 404 def process(self, data): |
11 | 405 """Process (ordinal) data through the state machine. |
406 | |
407 Returns the number of packets in the queue when finished. Updates | |
408 various internal counters too. | |
409 """ | |
1 | 410 pktcount = 0 |
411 for d in data: | |
412 if (self.state == 'init'): | |
5 | 413 if (d != 0x7e): |
11 | 414 print "Framing error, got 0x%02x, expected 0x7e" % d |
4 | 415 self.fr_err += 1 |
1 | 416 continue |
417 | |
418 self.state = 'sizemsb' | |
419 elif (self.state == 'sizemsb'): | |
5 | 420 self.bufmsb = d |
1 | 421 self.state = 'sizelsb' |
422 elif (self.state == 'sizelsb'): | |
7
579dedf5a1f1
Rejoin line break, left over from removal of struct.
darius@inchoate.localdomain
parents:
6
diff
changeset
|
423 self.dataleft = self.bufmsb << 8 | d |
1 | 424 self.state = 'data' |
425 elif (self.state == 'data'): | |
5 | 426 self.buffer.append(d) |
1 | 427 self.dataleft = self.dataleft - 1 |
428 if (self.dataleft == 0): | |
429 self.state = 'cksum' | |
430 elif (self.state == 'cksum'): | |
431 pktsum = reduce(lambda x, y: x + y, self.buffer) & 0xff | |
5 | 432 rxcksum = d |
1 | 433 self.state = 'init' |
434 if (pktsum + rxcksum != 0xff): | |
435 self.buffer = [] | |
4 | 436 self.ck_err += 1 |
1 | 437 print "Checksum error, got 0x%02x, expected 0x%02x" % \ |
438 (rxcksum, 0xff - pktsum) | |
439 else: | |
11 | 440 #print "Got a packet - " + str(self.buffer) |
1 | 441 p = Packets.Build(self.buffer) |
442 self.pktq.append(p) | |
443 self.buffer = [] | |
4 | 444 pktcount += 1 |
445 self.rx_cnt += 1 | |
1 | 446 else: |
447 print "Invalid state %s! Resetting" % (self.state) | |
448 self.state = 'init' | |
449 | |
450 return pktcount | |
451 |