1 '''
2 Support from the Freakduino platform from Abika/Freaklabs.
3
4 This is not a maintained platfrom and functionality may be broken or lacking.
5 '''
6
7 import usb
8 import serial
9 import time
10 import struct
11 from datetime import datetime, date
12 from datetime import time as dttime
13 from kbutils import KBCapabilities, makeFCS
14
15 MODE_NONE = 0x01
16 MODE_SNIFF = 0x02
17
20 '''
21 Instantiates the KillerBee class for our sketch running on ChibiArduino on Freakduino hardware.
22 @param serialpath: /dev/ttyUSB* type serial port identifier
23 @return: None
24 @rtype: None
25 '''
26 self._channel = None
27 self.handle = None
28 self.dev = serialpath
29 self.date = None
30 self.lon, self.lat, self.alt = (None, None, None)
31 self.handle = serial.Serial(port=self.dev, baudrate=57600, \
32 timeout=1, bytesize=8, parity='N', stopbits=1, xonxoff=0)
33 self.capabilities = KBCapabilities()
34 self.__set_capabilities()
35
37 '''
38 Closes the serial port. After closing, must reinitialize class again before use.
39 @return: None
40 @rtype: None
41 '''
42 self.handle.close()
43 self.handle = None
44
45
46
48 return self.capabilities.check(capab)
50 return self.capabilities.getlist()
51
63
65 '''
66 Returns device information in a list identifying the device.
67 @rtype: List
68 @return: List of 3 strings identifying device.
69 '''
70 return [self.dev, "Dartmouth Freakduino", ""]
71
73 self.handle.flush()
74 time.sleep(1.5)
75
76 self.handle.write("S")
77 self.handle.write(cmdstr)
78 if arg != None: self.handle.write(arg)
79 self.handle.write('\r')
80
81
82 self.handle.flush()
83
84
86 '''
87 Sends a command over the self.conn serial connection.
88 Ex: If provided cmdstr = "C!N" it will send "SC!N", telling the device to turn on sniffing ("N"),
89 and it expects to receive a confirmation back "&C!N" to confirm success.
90 '''
91 print "Flushing out of buffer:", self.handle.inWaiting()
92 self.handle.flushInput()
93 if len(cmdstr) > 3:
94 raise Exception("Command string is less than minimum length (S%s)." % cmdstr)
95 self.__send_cmd(cmdstr, arg)
96
97
98
99
100 print "Line:", self.handle.readline(eol='&')
101 counter = 0
102 char = self.handle.read()
103 while (char != '&'):
104 print self.handle.inWaiting(), "Waiting...", char
105 time.sleep(0.01)
106 if (counter > 8):
107 self.__send_cmd(cmdstr, arg)
108 counter = 0
109 print "Resend Response Line:", self.handle.readline(eol='&')
110 else: counter += 1
111 char = self.handle.read()
112 response = ''
113 for i in range(3):
114 response += self.handle.read()
115
116 if response == cmdstr[:3]:
117 print "Got a response:", response, "matches", cmdstr
118 return True
119 else:
120 print "Invalid response:", response, cmdstr[:3]
121 return False
122
123
126
127
129 '''
130 Turns the sniffer on such that pnext() will start returning observed
131 data. Will set the command mode to Air Capture if it is not already
132 set.
133 @type channel: Integer
134 @param channel: Sets the channel, optional
135 @rtype: None
136 '''
137 self.capabilities.require(KBCapabilities.SNIFF)
138
139 if channel != None:
140 self.set_channel(channel)
141
142
143 self.__send_cmd("C!N")
144 self.mode = MODE_SNIFF
145 self.__stream_open = True
146
147
149 '''
150 Turns the sniffer off, freeing the hardware for other functions. It is
151 not necessary to call this function before closing the interface with
152 close().
153 @rtype: None
154 '''
155 self.__send_cmd("C!F")
156 self.mode = MODE_NONE
157 self.__stream_open = False
158
159
161 '''
162 Sets the radio interface to the specifid channel (limited to 2.4 GHz channels 11-26)
163 @type channel: Integer
164 @param channel: Sets the channel, optional
165 @rtype: None
166 '''
167 self.capabilities.require(KBCapabilities.SETCHAN)
168
169 if channel >= 10 or channel <= 26:
170 self._channel = channel
171
172 self.__send_cmd("C!C %d" % channel)
173 else:
174 raise Exception('Invalid channel')
175
176
177 - def inject(self, packet, channel=None, count=1, delay=0):
178 '''
179 Injects the specified packet contents.
180 @type packet: String
181 @param packet: Packet contents to transmit, without FCS.
182 @type channel: Integer
183 @param channel: Sets the channel, optional
184 @type count: Integer
185 @param count: Transmits a specified number of frames, def=1
186 @type delay: Float
187 @param delay: Delay between each frame, def=1
188 @rtype: None
189 '''
190 self.capabilities.require(KBCapabilities.INJECT)
191
192 if len(packet) < 1:
193 raise Exception('Empty packet')
194 if len(packet) > 125:
195 raise Exception('Packet too long')
196
197 if channel != None:
198 self.set_channel(channel)
199
200
201 packet = ''.join([packet, "\x00\x00"])
202
203 for pnum in range(0, count):
204 raise Exception('Not yet implemented')
205
206
207 time.sleep(delay)
208
209
210
211 - def pnext(self, timeout=100):
212 '''
213 Returns packet data as a string, else None.
214 @type timeout: Integer
215 @param timeout: Timeout to wait for packet reception in usec
216 @rtype: List
217 @return: Returns None is timeout expires and no packet received. When a packet is received,
218 a list is returned, in the form [ String: packet contents | Bool: Valid CRC | Int: Unscaled RSSI ]
219 '''
220 if self.__stream_open == False:
221 self.sniffer_on()
222 return self.pnext_rec(timeout)
223
224
226 pdata = ''
227 ldata = ''
228
229 self.handle.timeout=timeout
230 startChar = self.handle.read()
231 if startChar == None: return None
232
233
234 if startChar == "R":
235 if self.handle.read() == "!":
236 x = self.handle.read()
237 while (x != ";"):
238 pdata += x
239 x = self.handle.read()
240 if startChar == "L":
241 if self.handle.read() == "!":
242 x = self.handle.read()
243 while (x != ";"):
244 ldata += x
245 x = self.handle.read()
246 if startChar == "[":
247 if self.handle.read(40) == "{[ DONE READING BACK ALL LOGGED DATA ]}]":
248 raise StopIteration("All Data Read")
249
250
251 if ldata != None and ldata != '':
252 self.processLocationUpdate(ldata)
253
254 if pdata == None or pdata == '':
255 return None
256
257
258 data = pdata.split("!", 3)
259 try:
260 rssi = ord(data[0])
261 frame = data[3]
262 if frame[-2:] == makeFCS(frame[:-2]): validcrc = True
263 else: validcrc = False
264 except:
265 print "Error parsing stream received from device:", pdata, data
266 return None
267
268
269 result = {0:frame, 1:validcrc, 2:rssi, 'bytes':frame, 'validcrc':validcrc, 'rssi':rssi}
270 result['dbm'] = None
271 result['datetime'] = self.getCaptureDateTime(data)
272 result['location'] = (self.lon, self.lat, self.alt)
273 return result
274
276 try:
277 timestr = "%08d" % (struct.unpack('L', data[1])[0])
278 time = dttime(int(timestr[:2]), int(timestr[2:4]), int(timestr[4:6]), int(timestr[6:]))
279 except:
280 print "Issue with time format:", timestr, data
281 time = None
282 if self.date == None: self.date = date.today()
283 if time == None or time == dttime.min: time = (datetime.now()).time()
284
285 return datetime.combine(self.date, time)
286
288 '''
289 Take a location string passed from the device and update the driver's internal state of last received location.
290 Format of ldata: longlatialtidate
291 '''
292 self.lon = struct.unpack('l', ldata[0:4])[0]
293 self.lat = struct.unpack('l', ldata[4:8])[0]
294 self.alt = struct.unpack('l', ldata[8:12])[0]
295 date = str(struct.unpack('L', ldata[12:16])[0])
296 self.date = datetime.date(date[-2:], date[-4:-2], date[:-4])
297
298 print self.lon, self.lat, self.alt, self.date
299
300 - def ping(self, da, panid, sa, channel=None):
301 '''
302 Not yet implemented.
303 @return: None
304 @rtype: None
305 '''
306 raise Exception('Not yet implemented')
307
325
327 '''
328 Not yet implemented.
329 @return: None
330 @rtype: None
331 '''
332
333 raise Exception('Not yet implemented')
334