$search
00001 """ 00002 zigbee.py 00003 00004 By Greg Rapp, 2010 00005 Inspired by code written by Paul Malmsten, 2010 00006 Inspired by code written by Amit Synderman and Marco Sangalli 00007 gdrapp@gmail.com 00008 00009 This module implements an XBee ZB (ZigBee) API library. 00010 """ 00011 import struct 00012 from xbee.base import XBeeBase 00013 00014 class ZigBee(XBeeBase): 00015 """ 00016 Provides an implementation of the XBee API for XBee ZB (ZigBee) modules 00017 with recent firmware. 00018 00019 Commands may be sent to a device by instantiating this class with 00020 a serial port object (see PySerial) and then calling the send 00021 method with the proper information specified by the API. Data may 00022 be read from a device synchronously by calling wait_read_frame. 00023 For asynchronous reads, see the defintion of XBeeBase. 00024 """ 00025 # Packets which can be sent to an XBee 00026 00027 # Format: 00028 # {name of command: 00029 # [{name:field name, len:field length, default: default value sent} 00030 # ... 00031 # ] 00032 # ... 00033 # } 00034 api_commands = {"at": 00035 [{'name':'id', 'len':1, 'default':'\x08'}, 00036 {'name':'frame_id', 'len':1, 'default':'\x01'}, 00037 {'name':'command', 'len':2, 'default':None}, 00038 {'name':'parameter', 'len':None, 'default':None}], 00039 "queued_at": 00040 [{'name':'id', 'len':1, 'default':'\x09'}, 00041 {'name':'frame_id', 'len':1, 'default':'\x01'}, 00042 {'name':'command', 'len':2, 'default':None}, 00043 {'name':'parameter', 'len':None, 'default':None}], 00044 "remote_at": 00045 [{'name':'id', 'len':1, 'default':'\x17'}, 00046 {'name':'frame_id', 'len':1, 'default':'\x00'}, 00047 # dest_addr_long is 8 bytes (64 bits), so use an unsigned long long 00048 {'name':'dest_addr_long', 'len':8, 'default':struct.pack('>Q', 0)}, 00049 {'name':'dest_addr', 'len':2, 'default':'\xFF\xFE'}, 00050 {'name':'options', 'len':1, 'default':'\x02'}, 00051 {'name':'command', 'len':2, 'default':None}, 00052 {'name':'parameter', 'len':None, 'default':None}], 00053 "tx": 00054 [{'name':'id', 'len':1, 'default':'\x10'}, 00055 {'name':'frame_id', 'len':1, 'default':'\x01'}, 00056 {'name':'dest_addr_long', 'len':8, 'default':None}, 00057 {'name':'dest_addr', 'len':2, 'default':None}, 00058 {'name':'broadcast_radius','len':1, 'default':'\x00'}, 00059 {'name':'options', 'len':1, 'default':'\x00'}, 00060 {'name':'data', 'len':None, 'default':None}], 00061 "tx_explicit": 00062 [{'name':'id', 'len':1, 'default':'\x11'}, 00063 {'name':'frame_id', 'len':1, 'default':'\x00'}, 00064 {'name':'dest_addr_long', 'len':8, 'default':None}, 00065 {'name':'dest_addr', 'len':2, 'default':None}, 00066 {'name':'src_endpoint', 'len':1, 'default':None}, 00067 {'name':'dest_endpoint', 'len':1, 'default':None}, 00068 {'name':'cluster', 'len':1, 'default':None}, 00069 {'name':'profile', 'len':1, 'default':None}, 00070 {'name':'broadcast_radius','len':1, 'default':'\x00'}, 00071 {'name':'options', 'len':1, 'default':'\x00'}, 00072 {'name':'data', 'len':None, 'default':None}] 00073 } 00074 00075 # Packets which can be received from an XBee 00076 00077 # Format: 00078 # {id byte received from XBee: 00079 # {name: name of response 00080 # structure: 00081 # [ {'name': name of field, 'len':length of field} 00082 # ... 00083 # ] 00084 # parse_as_io_samples:name of field to parse as io 00085 # } 00086 # ... 00087 # } 00088 # 00089 api_responses = {"\x90": 00090 {'name':'rx', 00091 'structure': 00092 [{'name':'source_addr_long','len':8}, 00093 {'name':'source_addr', 'len':2}, 00094 {'name':'options', 'len':1}, 00095 {'name':'rf_data', 'len':None}]}, 00096 "\x91": 00097 {'name':'rx_explicit', 00098 'structure': 00099 [{'name':'source_addr_long','len':8}, 00100 {'name':'source_addr', 'len':2}, 00101 {'name':'source_endpoint', 'len':1}, 00102 {'name':'dest_endpoint', 'len':1}, 00103 {'name':'cluster', 'len':2}, 00104 {'name':'profile', 'len':2}, 00105 {'name':'options', 'len':1}, 00106 {'name':'rf_data', 'len':None}]}, 00107 "\x92": # Checked by GDR-parse_samples_header function appears to need update to support 00108 {'name':'rx_io_data_long_addr', 00109 'structure': 00110 [{'name':'source_addr_long','len':8}, 00111 {'name':'source_addr', 'len':2}, 00112 {'name':'options', 'len':1}, 00113 {'name':'samples', 'len':None}], 00114 'parse_as_io_samples':'samples'}, 00115 "\x8b": 00116 {'name':'tx_status', 00117 'structure': 00118 [{'name':'frame_id', 'len':1}, 00119 {'name':'dest_addr', 'len':2}, 00120 {'name':'retries', 'len':1}, 00121 {'name':'deliver_status', 'len':1}, 00122 {'name':'discover_status', 'len':1}]}, 00123 "\x8a": 00124 {'name':'status', 00125 'structure': 00126 [{'name':'status', 'len':1}]}, 00127 "\x88": 00128 {'name':'at_response', 00129 'structure': 00130 [{'name':'frame_id', 'len':1}, 00131 {'name':'command', 'len':2}, 00132 {'name':'status', 'len':1}, 00133 {'name':'parameter', 'len':None}]}, 00134 "\x97": #Checked GDR (not sure about parameter, could be 4 bytes) 00135 {'name':'remote_at_response', 00136 'structure': 00137 [{'name':'frame_id', 'len':1}, 00138 {'name':'source_addr_long','len':8}, 00139 {'name':'source_addr', 'len':2}, 00140 {'name':'command', 'len':2}, 00141 {'name':'status', 'len':1}, 00142 {'name':'parameter', 'len':None}]}, 00143 "\x95": 00144 {'name':'node_id_indicator', 00145 'structure': 00146 [{'name':'sender_addr_long','len':8}, 00147 {'name':'sender_addr', 'len':2}, 00148 {'name':'options', 'len':1}, 00149 {'name':'source_addr', 'len':2}, 00150 {'name':'source_addr_long','len':8}, 00151 {'name':'node_id', 'len':'null_terminated'}, 00152 {'name':'parent_source_addr','len':2}, 00153 {'name':'device_type', 'len':1}, 00154 {'name':'source_event', 'len':1}, 00155 {'name':'digi_profile_id', 'len':2}, 00156 {'name':'manufacturer_id', 'len':2}]} 00157 } 00158 00159 def __init__(self, *args, **kwargs): 00160 # Call the super class constructor to save the serial port 00161 super(ZigBee, self).__init__(*args, **kwargs) 00162 00163 def _parse_samples_header(self, io_bytes): 00164 """ 00165 _parse_samples_header: binary data in XBee ZB IO data format -> 00166 (int, [int ...], [int ...], int, int) 00167 00168 _parse_samples_header will read the first three bytes of the 00169 binary data given and will return the number of samples which 00170 follow, a list of enabled digital inputs, a list of enabled 00171 analog inputs, the dio_mask, and the size of the header in bytes 00172 00173 _parse_samples_header is overloaded here to support the additional 00174 IO lines offered by the XBee ZB 00175 """ 00176 header_size = 4 00177 00178 # number of samples (always 1?) is the first byte 00179 sample_count = ord(io_bytes[0]) 00180 00181 # bytes 1 and 2 are the DIO mask; bits 9 and 8 aren't used 00182 dio_mask = (ord(io_bytes[1]) << 8 | ord(io_bytes[2])) & 0x0E7F 00183 00184 # byte 3 is the AIO mask 00185 aio_mask = ord(io_bytes[3]) 00186 00187 # sorted lists of enabled channels; value is position of bit in mask 00188 dio_chans = [] 00189 aio_chans = [] 00190 00191 for i in range(0,13): 00192 if dio_mask & (1 << i): 00193 dio_chans.append(i) 00194 00195 dio_chans.sort() 00196 00197 for i in range(0,8): 00198 if aio_mask & (1 << i): 00199 aio_chans.append(i) 00200 00201 aio_chans.sort() 00202 00203 return (sample_count, dio_chans, aio_chans, dio_mask, header_size)