5 Inspired by code written by Amit Synderman and Marco Sangalli
8 _wait_for_frame modified by Adam Stambler to allow for non
12 XBee superclass module
15 This class defines data and methods common to all XBee modules.
16 This class should be subclassed in order to provide
17 series-specific functionality.
19 import struct, threading, time
27 Abstract base class providing command generation and response
28 parsing methods for XBee modules.
30 Constructor arguments:
31 ser: The file-like serial port to use.
34 shorthand: boolean flag which determines whether shorthand command
35 calls (i.e. xbee.at(...) instead of xbee.send("at",...)
38 callback: function which should be called with frame data
39 whenever a frame arrives from the serial port.
40 When this is not None, a background thread to monitor
41 the port and call the given function is automatically
44 escaped: boolean flag which determines whether the library should
45 operate in escaped mode. In this mode, certain data bytes
46 in the output and input streams will be escaped and unescaped
47 in accordance with the XBee API. This setting must match
48 the appropriate api_mode setting of an XBee device; see your
49 XBee device's documentation for more information.
52 def __init__(self, ser, shorthand=True, callback=None, escaped=False):
70 If this instance has a separate thread running, it will be
71 halted. This method will wait until the thread has cleaned
80 _write: binary data -> None
82 Packages the given binary data in an API frame and writes the
83 result to the serial port
92 This method overrides threading.Thread.run() and is automatically
93 called when an instance is created with threading enabled.
98 except ThreadQuitException:
104 _wait_for_frame: None -> binary data
106 _wait_for_frame will read from the serial port until a valid
107 API frame arrives. It will then return the binary data
108 contained within the frame.
110 If this method is called as a separate thread
111 and self.thread_continue is set to False, the thread will
112 exit by raising a ThreadQuitException.
118 raise ThreadQuitException
120 while ( self.
serial.inWaiting() <1):
127 if byte == APIFrame.START_BYTE:
134 if ( (mode==1)
and (frame.remaining_bytes() <=0) ) :
147 _build_command: string (binary data) ... -> binary data
149 _build_command will construct a command packet according to the
150 specified command's specification in api_commands. It will expect
151 named arguments for all fields other than those with a default
152 value or a length of 'None'.
154 Each field will be written out in the order they are defined
155 in the command definition.
158 cmd_spec = self.api_commands[cmd]
159 except AttributeError:
160 raise NotImplementedError(
"API command specifications could not be found; use a derived class which defines 'api_commands'.")
164 for field
in cmd_spec:
167 data = kwargs[field[
'name']]
171 if field[
'len']
is not None:
173 default_value = field[
'default']
180 "The expected field %s of length %d was not provided"
181 % (field[
'name'], field[
'len']))
187 if field[
'len']
and len(data) != field[
'len']:
189 "The data provided for '%s' was not %d bytes long"\
190 % (field[
'name'], field[
'len']))
202 _split_response: binary data -> {'id':str,
206 _split_response takes a data packet received from an XBee device
207 and converts it into a dictionary. This dictionary provides
208 names for each segment of binary data as specified in the
215 packet = self.api_responses[packet_id]
216 except AttributeError:
217 raise NotImplementedError(
"API response specifications could not be found; use a derived class which defines 'api_responses'.")
220 "Unrecognized response packet with id byte %s"
227 info = {
'id':packet[
'name']}
228 packet_spec = packet[
'structure']
231 for field
in packet_spec:
232 if field[
'len'] ==
'null_terminated':
235 while data[index] !=
'\x00':
236 field_data += data[index]
240 info[field[
'name']] = field_data
241 elif field[
'len']
is not None:
245 if index + field[
'len'] > len(data):
247 "Response packet was shorter than expected")
249 field_data = data[index:index + field[
'len']]
250 info[field[
'name']] = field_data
252 index += field[
'len']
256 field_data = data[index:]
261 info[field[
'name']] = field_data
262 index += len(field_data)
266 if index < len(data):
268 "Response packet was longer than expected; expected: %d, got: %d bytes" % (index,
273 if 'parse_as_io_samples' in packet:
274 field_to_process = packet[
'parse_as_io_samples']
276 info[field_to_process])
282 _parse_samples_header: binary data in XBee IO data format ->
283 (int, [int ...], [int ...], int, int)
285 _parse_samples_header will read the first three bytes of the
286 binary data given and will return the number of samples which
287 follow, a list of enabled digital inputs, a list of enabled
288 analog inputs, the dio_mask, and the size of the header in bytes
293 sample_count = ord(io_bytes[0])
296 dio_mask = (ord(io_bytes[1]) << 8 | ord(io_bytes[2])) & 0x01FF
299 aio_mask = (ord(io_bytes[1]) & 0xFE) >> 1
306 if dio_mask & (1 << i):
312 if aio_mask & (1 << i):
317 return (sample_count, dio_chans, aio_chans, dio_mask, header_size)
321 _parse_samples: binary data in XBee IO data format ->
326 _parse_samples reads binary data from an XBee device in the IO
327 data format specified by the API. It will then return a
328 dictionary indicating the status of each enabled IO port.
331 sample_count, dio_chans, aio_chans, dio_mask, header_size = \
337 sample_bytes = [ord(c)
for c
in io_bytes[header_size:]]
340 for sample_ind
in range(0, sample_count):
345 digital_data_set = (sample_bytes.pop(0) << 8 | sample_bytes.pop(0))
346 digital_values = dio_mask & digital_data_set
349 tmp_samples[
'dio-%d' % i] =
True if (digital_values >> i) & 1
else False
353 analog_sample = (sample_bytes.pop(0) << 8 | sample_bytes.pop(0)) & 0x03FF
354 tmp_samples[
'adc-%d' % i] = analog_sample
356 samples.append(tmp_samples)
362 send: string param=binary data ... -> None
364 When send is called with the proper arguments, an API command
365 will be written to the serial port for this XBee device
366 containing the proper instructions and data.
368 This method must be called with named arguments in accordance
369 with the api_command specification. Arguments matching all
370 field names other than those in reserved_names (like 'id' and
371 'order') should be given, unless they are of variable length
372 (of 'None' in the specification. Those are optional).
380 wait_read_frame: None -> frame info dictionary
382 wait_read_frame calls XBee._wait_for_frame() and waits until a
383 valid frame appears on the serial port. Once it receives a frame,
384 wait_read_frame attempts to parse the data contained within it
385 and returns the resulting dictionary
393 If a method by the name of a valid api command is called,
394 the arguments will be automatically sent to an appropriate
400 if name ==
'api_commands':
401 raise NotImplementedError(
"API command specifications could not be found; use a derived class which defines 'api_commands'.")
404 if self.
shorthand and name
in self.api_commands:
407 return lambda **kwargs: self.
send(name, **kwargs)
409 raise AttributeError(
"XBee has no attribute '%s'" % name)