fix endianness in device descriptor
[goodfet] / client / GoodFETatmel128.py
1 # GoodFETclient to interface zigduino/atmel128 radio
2 # forked by bx from code by neighbor Travis Goodspeed
3 from GoodFETAVR import GoodFETAVR
4 import sys, binascii, os, array, time, glob, struct
5
6 fmt = ("B", "<H", None, "<L")
7
8 class GoodFETatmel128rfa1(GoodFETAVR):
9     ATMELRADIOAPP = 0x53
10     autocrc = 0
11     verbose = False
12     connected = 0
13     enable_AACK = False
14     def serInit(self, port=None, timeout=2, attemptlimit=None):
15         if port==None:
16             port=os.environ.get("GOODFET");
17         self.pyserInit(port, timeout, attemptlimit)
18
19     def pyserInit(self, port, timeout, attemptlimit):
20         """Open the serial port"""
21         if self.connected == 0:
22             if (not (attemptlimit == None)) and (attemptlimit <= 1):
23                 # it always takes at least 2 tries
24                 attemptlimit == 2
25
26             # Make timeout None to wait forever, 0 for non-blocking mode.
27             import serial;
28
29             if os.name=='nt' and sys.version.find('64 bit')!=-1:
30                 print "WARNING: PySerial requires a 32-bit Python build in Windows.";
31
32             if port is None and os.environ.get("GOODFET")!=None:
33                 glob_list = glob.glob(os.environ.get("GOODFET"));
34                 if len(glob_list) > 0:
35                     port = glob_list[0];
36                 else:
37                     port = os.environ.get("GOODFET");
38             if port is None:
39                 glob_list = glob.glob("/dev/tty.usbserial*");
40                 if len(glob_list) > 0:
41                     port = glob_list[0];
42             if port is None:
43                 glob_list = glob.glob("/dev/ttyUSB*");
44                 if len(glob_list) > 0:
45                     port = glob_list[0];
46             if port is None:
47                 glob_list = glob.glob("/dev/ttyU0");
48                 if len(glob_list) > 0:
49                     port = glob_list[0];
50             if port is None and os.name=='nt':
51                 from scanwin32 import winScan;
52                 scan=winScan();
53                 for order,comport,desc,hwid in sorted(scan.comports()):
54                     try:
55                         if hwid.index('FTDI')==0:
56                             port=comport;
57                             #print "Using FTDI port %s" % port
58                     except:
59                         #Do nothing.
60                         a=1;
61
62             baud=115200;
63
64             self.serialport = serial.Serial(
65                 port,
66                 baud,
67                 parity = serial.PARITY_NONE,
68                 timeout=timeout
69                 )
70
71             self.verb=0;
72             self.data=""
73             attempts=0;
74             self.connected=0;
75
76             while self.connected==0:
77                 self.serialport.setDTR(False)
78                 while self.verb!=0x7F or self.data!="http://goodfet.sf.net/":
79                     if attemptlimit is not None and attempts >= attemptlimit:
80                         return
81
82                     attempts=attempts+1;
83                     self.readcmd(); #Read the first command.
84                     if self.verbose:
85                         print "Got %02x,%02x:'%s'" % (self.app,self.verb,self.data);
86
87                 #Here we have a connection, but maybe not a good one.
88                 #print "We have a connection."
89                 for foo in range(1,30):
90                     time.sleep(1)
91                     if not self.monitorecho():
92                         self.connected = 0
93                         if self.verbose:
94                             print "Comm error on try %i." % (foo)
95                     else:
96                         self.connected = 1
97                         break
98             if self.verbose:
99                 print "Connected after %02i attempts." % attempts;
100             self.serialport.setTimeout(12);
101
102     def serClose(self):
103         self.connected = 0
104         self.serialport.close()
105
106
107     def writecmd(self, app, verb, count=0, data=[]):
108         """Write a command and some data to the GoodFET."""
109         self.serialport.write(chr(app));
110         self.serialport.write(chr(verb));
111         if self.verbose:
112             print "Tx: ( 0x%02x, 0x%02x, %d )" % ( app, verb, count )
113         if count > 0:
114             if(isinstance(data,list)):
115                 old = data
116                 data = []
117                 for i in range(0,count):
118                     data += chr(old[i]);
119             outstr=''.join(data);
120
121         #little endian 16-bit length
122             count = len(outstr)
123         self.serialport.write(chr(count&0xFF));
124         self.serialport.write(chr(count>>8));
125         if count > 0:
126             if self.verbose:
127                 print "sending: %s" %outstr.encode("hex")
128             self.serialport.write(outstr);
129
130
131         if not self.besilent:
132             out = self.readcmd()
133             if out and self.verbose:
134                 print "read: " + out.encode("hex")
135             return out
136         else:
137             return None
138
139     def readcmd(self):
140
141         """Read a reply from the GoodFET."""
142         app = self.serialport.read(1)
143
144         if len(app) < 1:
145             if self.verbose:
146                 print "Rx: None"
147
148             self.app = 0
149             self.verb = 0
150             self.count = 0
151             self.data = ""
152             return
153
154         self.app=ord(app);
155
156         v = self.serialport.read(1);
157         if v:
158             self.verb = ord(v)
159         else:
160             self.verb = 0
161             
162         c1 = self.serialport.read(1)
163         c2 = self.serialport.read(1)
164         if (c1 and c2):
165             self.count= ord(c1) + (ord(c2)<<8)
166         else:
167             self.count = 0
168         if self.verbose:
169             print "Rx: ( 0x%02x, 0x%02x, %i )" % ( self.app, self.verb, self.count )
170
171         #Debugging string; print, but wait.
172         if self.app==0xFF:
173             if self.verb==0xFF:
174                 print "# DEBUG %s" % self.serialport.read(self.count)
175             elif self.verb==0xFE:
176                 print "# DEBUG 0x%x" % struct.unpack(fmt[self.count-1], self.serialport.read(self.count))[0]
177             elif self.verb==0xFD:
178                         #Do nothing, just wait so there's no timeout.
179                 print "# NOP.";
180             return ""
181         else:
182             self.data=self.serialport.read(self.count);
183             return self.data;
184
185     def RF_setchannel(self, chan):
186         if (chan < 11) or (chan > 26):
187             print "Channel out of range"
188         else:
189             self.poke(0x8, chan)
190
191     def peek(self,reg,bytes=1):
192         """Read a Register. """
193         #Automatically calibrate the len.
194         if bytes != 1:
195             print "Warning, currently cannot poke more than 1 byte"
196             bytes = 1
197         data = [reg, 0, bytes%255, bytes>>8] #+ ([0]*bytes)
198         self.data = None
199         self.writecmd(self.ATMELRADIOAPP,0x02,len(data),data);
200         toret=0;
201         #print self.data.encode("hex")
202         if self.data:
203             #for i in range(0,bytes):
204             #    toret=toret|(ord(self.data[i+1])<<(8*i));
205             #return toret;
206             # right now only works with a byte of data
207             return ord(self.data)
208         else:
209             return -1
210
211     def poke(self,reg,val,bytes=1): # todo, support >1 byte
212         """Write an Register."""
213         data = [reg, 0] #+ ([0]*bytes)
214         data=[reg, 0]
215         if bytes != 1:
216             print "Warning, currently cannot poke more than 1 byte"
217             bytes = 1
218         for i in range(0,bytes):
219             data=data+[(val>>(8*i))&0xFF];
220
221         self.writecmd(self.ATMELRADIOAPP,0x03,len(data),data);
222         newval = self.peek(reg,bytes)
223         if newval!=val:
224             print "Warning, failed to set r%02x=%02x, got %02x." %(
225                 reg,
226                 val,
227                 newval);
228
229         return;
230
231     def setup(self):
232         self.RF_setup()
233
234     def RF_setup(self):
235         self.writecmd(self.ATMELRADIOAPP, 0x10, 0, None)
236
237     def RF_rxpacket(self):
238         """Get a packet from the radio.  Returns None if none is waiting."""
239         #doto: check if packet has arrived, flush if not new
240         self.writecmd(self.ATMELRADIOAPP, 0x80, 0, None)
241         data=self.data;
242         self.packetlen = len(data)
243         if (self.packetlen > 0):
244             return data;
245         else:
246             return None
247
248     def RF_txpacket(self, payload):
249         if type(payload) == list: #convert to string
250             import array
251             payload = array.array('B', payload).tostring()
252         self.writecmd(self.ATMELRADIOAPP, 0x81, len(payload), payload)
253
254
255     def RF_getrssi(self):
256         """Returns the received signal strength"""
257         base = -90
258         val = self.peek(0x7) & 0x7f # read rssi bits
259         if val == 0:
260             return base - 1
261         elif val < 0x53:
262             return val + base
263         else:
264             return 0x53 + base
265
266     def RF_enable_AACK(self, enable = True):
267         if (enable and (not self.enable_AACK)):
268             self.enable_AACK = True
269             self.writecmd(self.ATMELRADIOAPP, 0x84)
270         elif ((not enable) and self.enable_AACK):
271             self.enable_AACK = False
272             self.writecmd(self.ATMELRADIOAPP, 0x85)
273
274
275     def RF_autocrc(self, autocrc=1):
276         self.autocrc = autocrc
277         if autocrc:
278             self.writecmd(self.ATMELRADIOAPP, 0x86)
279         else:
280             self.writecmd(self.ATMELRADIOAPP, 0x87)
281