6e9b76eff1850ab496627639e99abd1b6ca252e6
[goodfet] / client / GoodFETMCPCANCommunication.py
1 #!/usr/bin/env python
2 # GoodFET SPI Flash Client
3 #
4 # (C) 2012 Travis Goodspeed <travis at radiantmachines.com>
5 #
6 #
7 # Ted's working copy
8 #   1) getting hot reads on frequency
9 #   2) allow sniffing in "normal" mode to get ack bits
10 #       --check if that's whats causing error flags in board-to-board transmission
11 #
12 #
13
14 import sys;
15 import binascii;
16 import array;
17 import csv, time, argparse;
18 import datetime
19 import os
20 from random import randrange
21 from GoodFETMCPCAN import GoodFETMCPCAN;
22 from intelhex import IntelHex;
23 import Queue
24
25 class GoodFETMCPCANCommunication:
26     
27     def __init__(self, dataLocation):
28        self.client=GoodFETMCPCAN();
29         """ Communication with the bus"""
30        self.client.serInit()
31        self.client.MCPsetup();
32        #self.DATA_LOCATION = "../../contrib/ThayerData/"
33        self.DATA_LOCATION = dataLocation; 
34        """ Stores file data location. This is the root folder where basic sniffs will be stored"""
35        self.INJECT_DATA_LOCATION  = self.DATA_LOCATION+"InjectedData/" 
36        """ stores the sub folder path where injected data will be stored"""
37        
38
39     
40     def printInfo(self):
41         """ 
42         This method will print information about the board to the terminal. 
43         It is good for diagnostics
44         """
45         
46         self.client.MCPreqstatConfiguration();
47         
48         print "MCP2515 Info:\n\n";
49         
50         print "Mode: %s" % self.client.MCPcanstatstr();
51         print "Read Status: %02x" % self.client.MCPreadstatus();
52         print "Rx Status:   %02x" % self.client.MCPrxstatus();
53         print "Error Flags:  %02x" % self.client.peek8(0x2D);
54         print "Tx Errors:  %3d" % self.client.peek8(0x1c);
55         print "Rx Errors:  %3d\n" % self.client.peek8(0x1d);
56         
57         print "Timing Info:";
58         print "CNF1: %02x" %self.client.peek8(0x2a);
59         print "CNF2: %02x" %self.client.peek8(0x29);
60         print "CNF3: %02x\n" %self.client.peek8(0x28);
61         print "RXB0 CTRL: %02x" %self.client.peek8(0x60);
62         print "RXB1 CTRL: %02x" %self.client.peek8(0x70);
63         
64         print "RX Info:";
65         print "RXB0: %02x" %self.client.peek8(0x60);
66         print "RXB1: %02x" %self.client.peek8(0x70);
67         print "RXB0 masks: %02x, %02x, %02x, %02x" %(self.client.peek8(0x20), self.client.peek8(0x21), self.client.peek8(0x22), self.client.peek8(0x23));
68         print "RXB1 masks: %02x, %02x, %02x, %02x" %(self.client.peek8(0x24), self.client.peek8(0x25), self.client.peek8(0x26), self.client.peek8(0x27));
69
70         
71         print "RX Buffers:"
72         packet0=self.client.readrxbuffer(0);
73         packet1=self.client.readrxbuffer(1);
74         for foo in [packet0, packet1]:
75            print self.client.packet2str(foo);
76            
77     def reset(self):
78         """ 
79         Reset the chip
80         """
81         self.client.MCPsetup();
82     
83     
84     ##########################
85     #   SNIFF
86     ##########################
87          
88     def sniff(self,freq,duration,description, verbose=True, comment=None, filename=None, standardid=None, debug=False, faster=False, parsed=True, data = None,writeToFile=True, db0 = None, db1 = None):
89         """
90         
91         """
92         #reset eveything on the chip
93         self.client.serInit() 
94         self.reset()
95         
96         # filtering for specific packets
97         if(db0 != None and db1 != None and standardid != None):
98             self.filterForPacket(standardid[0], db0, db1, verbose)
99             if(comment == None):
100                 comment = ""
101             comment += ("f%d[%d][%d]" %(standardid[0], db0, db1))
102         # filtering for standard ID
103         elif(standardid != None):
104             self.addFilter(standardid, verbose)
105             if(comment == None):
106                 comment = ""
107                 for ID in standardid:
108                     comment += ("f%d" %(ID))
109
110                 
111         self.client.MCPsetrate(freq);
112         
113         # This will handle the files so that we do not loose them. each day we will create a new csv file
114         if( filename==None and writeToFile == True):
115             #get folder information (based on today's date)
116             now = datetime.datetime.now()
117             datestr = now.strftime("%Y%m%d")
118             path = self.DATA_LOCATION+datestr+".csv"
119             filename = path
120             
121         if( writeToFile == True):
122             outfile = open(filename,'a');
123             dataWriter = csv.writer(outfile,delimiter=',');
124             dataWriter.writerow(['# Time     Error        Bytes 1-13']);
125             dataWriter.writerow(['#' + description])
126             
127         self.client.MCPreqstatNormal();
128         print "Listening...";
129         packetcount = 0;
130         starttime = time.time();
131         
132         while((time.time()-starttime < duration)):
133             
134             if(faster):
135                 packet=self.client.fastrxpacket();
136             else:
137                 packet=self.client.rxpacket();
138                 
139             #add the data to list if the pointer was included
140             if(data != None and packet != None):
141                 #data.append(self.client.packet2parsedstr(packet))
142                 packetParsed = self.client.packet2parsed(packet)
143                 packetParsed["time"] =time.time()
144                 data.put(packetParsed)
145             if(debug == True):
146                 #check packet status
147                 MCPstatusReg = self.client.MCPrxstatus();
148                 messagestat=MCPstatusReg&0xC0;
149                 messagetype=MCPstatusReg&0x18;
150                 if(messagestat == 0xC0):
151                     print "Message in both buffers; message type is %02x (0x00 is standard data, 0x08 is standard remote)." %messagetype
152                 elif(messagestat == 0x80):
153                     print "Message in RXB1; message type is %02x (0x00 is standard data, 0x08 is standard remote)." %messagetype
154                 elif(messagestat == 0x40):
155                     print "Message in RXB0; message type is %02x (0x00 is standard data, 0x08 is standard remote)." %messagetype
156                 elif(messagestat == 0x00):
157                     print "No messages in buffers."
158             #check to see if there was a packet
159             if( packet != None):
160                 packetcount+=1;
161             if (packet!=None and writeToFile == True):
162                 
163                 row = [];
164                 row.append("%f"%time.time());
165                 
166                 if( verbose==True):
167                     #if we want to print a parsed message
168                     if( parsed == True):
169                         packetParsed = self.client.packet2parsed(packet)
170                         sId = packetParsed.get('sID')
171                         msg = "sID: %04d" %sId
172                         if( packetParsed.get('eID')):
173                             msg += " eID: %d" %packetParsed.get('eID')
174                         msg += " rtr: %d"%packetParsed['rtr']
175                         length = packetParsed['length']
176                         msg += " length: %d"%length
177                         msg += " data:"
178                         for i in range(0,length):
179                             dbidx = 'db%d'%i
180                             msg +=" %03d"% packetParsed[dbidx]
181                         #msg = self.client.packet2parsedstr(packet)
182                         print msg
183                     # if we want to print just the message as it is read off the chip
184                     else:
185                         print self.client.packet2str(packet)
186                 
187                 if(debug == True):
188                     
189                     #check overflow
190                     MCPeflgReg=self.client.peek8(0x2D);
191                     print"EFLG register equals: %x" %MCPeflgReg;
192                     if((MCPeflgReg & 0xC0)==0xC0):
193                         print "WARNING: BOTH overflow flags set. Missed a packet. Clearing and proceeding."
194                     elif(MCPeflgReg & 0x80):
195                         print "WARNING: RXB1 overflow flag set. A packet has been missed. Clearing and proceeding."
196                     elif(MCPeflgReg & 0x40):
197                         print "WARNING: RXB0 overflow flag set. A packet has been missed. Clearing and proceeding."
198                     self.client.MCPbitmodify(0x2D,0xC0,0x00);
199                     print"EFLG register set to: %x" % self.client.peek(0x2D);
200                 
201                     #check for errors
202                     if (self.client.peek8(0x2C) & 0x80):
203                         self.client.MCPbitmodify(0x2C,0x80,0x00);
204                         print "ERROR: Malformed packet recieved: " + self.client.packet2str(packet);
205                         row.append(1);
206                     else:
207                         row.append(0);
208                 else:
209                     row.append(0);  #since we don't check for errors if we're not in debug mode...
210                             
211                 row.append(comment)
212                 #how long the sniff was for
213                 row.append(duration)
214                 #boolean that tells us if there was filtering. 0 == no filters, 1 == filters
215                 if(standardid != None):
216                     row.append(1)
217                 else:
218                     row.append(0)
219                 #write packet to file
220                 for byte in packet:
221                     row.append("%02x"%ord(byte));
222                 dataWriter.writerow(row);
223         if(writeToFile == True):
224             outfile.close()
225         print "Listened for %d seconds, captured %d packets." %(duration,packetcount);
226         return packetcount
227         
228         
229 #    def filterStdSweep(self, freq, low, high, time = 5):
230 #        msgIDs = []
231 #        self.client.serInit()
232 #        self.client.MCPsetup()
233 #        for i in range(low, high+1, 6):
234 #            print "sniffing id: %d, %d, %d, %d, %d, %d" % (i,i+1,i+2,i+3,i+4,i+5)
235 #            comment= "sweepFilter: "
236 #            #comment = "sweepFilter_%d_%d_%d_%d_%d_%d" % (i,i+1,i+2,i+3,i+4,i+5)
237 #            description = "Running a sweep filer for all the possible standard IDs. This run filters for: %d, %d, %d, %d, %d, %d" % (i,i+1,i+2,i+3,i+4,i+5)
238 #            count = self.sniff(freq=freq, duration = time, description = description,comment = comment, standardid = [i, i+1, i+2, i+3, i+4, i+5])
239 #            if( count != 0):
240 #                for j in range(i,i+5):
241 #                    comment = "sweepFilter: "
242 #                    #comment = "sweepFilter: %d" % (j)
243 #                    description = "Running a sweep filer for all the possible standard IDs. This run filters for: %d " % j
244 #                    count = self.sniff(freq=freq, duration = time, description = description,comment = comment, standardid = [j, j, j, j])
245 #                    if( count != 0):
246 #                        msgIDs.append(j)
247 #        return msgIDs
248     
249 #    def sweepRandom(self, freq, number = 5, time = 200):
250 #        msgIDs = []
251 #        ids = []
252 #        self.client.serInit()
253 #        self.client.MCPsetup()
254 #        for i in range(0,number+1,6):
255 #            idsTemp = []
256 #            comment = "sweepFilter: "
257 #            for j in range(0,6,1):
258 #                id = randrange(2047)
259 #                #comment += "_%d" % id
260 #                idsTemp.append(id)
261 #                ids.append(id)
262 #            print comment
263 #            description = "Running a sweep filer for all the possible standard IDs. This runs the following : " + comment
264 #            count = self.sniff(freq=freq, duration=time, description=description, comment = comment, standardid = idsTemp)
265 #            if( count != 0):
266 #                for element in idsTemp:
267 #                    #comment = "sweepFilter: %d" % (element)
268 #                    comment="sweepFilter: "
269 #                    description = "Running a sweep filer for all the possible standard IDs. This run filters for: %d " % element
270 #                    count = self.sniff(freq=freq, duration = time, description = description,comment = comment, standardid = [element, element, element])
271 #                    if( count != 0):
272 #                        msgIDs.append(j)
273 #        return msgIDs, ids
274     
275     def sniffTest(self, freq):
276         """
277         This method will preform a test to see if we can sniff corretly formed packets from the CAN bus.
278         
279         @type freq: number
280         @param freq: frequency of the CAN bus
281         """
282         rate = freq;
283         
284         print "Calling MCPsetrate for %i." %rate;
285         self.client.MCPsetrate(rate);
286         self.client.MCPreqstatNormal();
287         
288         print "Mode: %s" % self.client.MCPcanstatstr();
289         print "CNF1: %02x" %self.client.peek8(0x2a);
290         print "CNF2: %02x" %self.client.peek8(0x29);
291         print "CNF3: %02x\n" %self.client.peek8(0x28);
292         
293         while(1):
294             packet=self.client.rxpacket();
295             
296             if packet!=None:                
297                 if (self.client.peek8(0x2C) & 0x80):
298                     self.client.MCPbitmodify(0x2C,0x80,0x00);
299                     print "malformed packet recieved: "+ self.client.packet2str(packet);
300                 else:
301                     print "properly formatted packet recieved" + self.client.packet2str(packet);
302    
303     
304     def freqtest(self,freq):
305         """
306         This method will test the frequency provided to see if it is the correct frequency for this CAN bus.
307         
308         @type freq: Number
309         @param freq: The frequency to listen to the CAN bus.
310         
311         """
312         self.client.MCPsetup();
313
314         self.client.MCPsetrate(freq);
315         self.client.MCPreqstatListenOnly();
316     
317         print "CAN Freq Test: %3d kHz" %freq;
318     
319         x = 0;
320         errors = 0;
321     
322         starttime = time.time();
323         while((time.time()-starttime < args.time)):
324             packet=self.client.rxpacket();
325             if packet!=None:
326                 x+=1;
327                 
328                 if (self.client.peek8(0x2C) & 0x80):
329                     print "malformed packet recieved"
330                     errors+=1;
331                     self.client.MCPbitmodify(0x2C,0x80,0x00);
332                 else:         
333                     print self.client.packet2str(packet);
334     
335         print "Results for %3.1d kHz: recieved %3d packets, registered %3d RX errors." %(freq, x, errors);
336     
337
338     def isniff(self,freq):
339         """ An intelligent sniffer, decodes message format """
340         """ More features to be added soon """
341         
342         self.client.MCPsetrate(freq);
343         self.client.MCPreqstatListenOnly();
344         while 1:
345             packet=self.client.rxpacket();
346             if packet!=None:
347                 plist=[];
348                 for byte in packet:
349                     plist.append(byte);
350                 arbid=plist[0:2];
351                 eid=plist[2:4];
352                 dlc=plist[4:5];
353                 data=plist[5:13];         
354                 print "\nArbID: " + self.client.packet2str(arbid);
355                 print "EID: " + self.client.packet2str(eid);
356                 print "DLC: " + self.client.packet2str(dlc);
357                 print "Data: " + self.client.packet2str(data);
358
359     def test(self):
360         """ This will perform a test on the GOODTHOPTER10. Diagnostic messages will be printed
361         out to the terminal
362         """
363         comm.reset();
364         print "Just reset..."
365         print "EFLG register:  %02x" % self.client.peek8(0x2d);
366         print "Tx Errors:  %3d" % self.client.peek8(0x1c);
367         print "Rx Errors:  %3d" % self.client.peek8(0x1d);
368         print "CANINTF: %02x"  %self.client.peek8(0x2C);
369         self.client.MCPreqstatConfiguration();
370         self.client.poke8(0x60,0x66);
371         self.client.MCPsetrate(500);
372         self.client.MCPreqstatNormal();
373         print "In normal mode now"
374         print "EFLG register:  %02x" % self.client.peek8(0x2d);
375         print "Tx Errors:  %3d" % self.client.peek8(0x1c);
376         print "Rx Errors:  %3d" % self.client.peek8(0x1d);
377         print "CANINTF: %02x"  %self.client.peek8(0x2C);
378         print "Waiting on packets.";
379         checkcount = 0;
380         packet=None;
381         while(1):
382             packet=self.client.rxpacket();
383             if packet!=None:
384                 print "Message recieved: %s" % self.client.packet2str(packet);
385             else:
386                 checkcount=checkcount+1;
387                 if (checkcount%30==0):
388                     print "EFLG register:  %02x" % self.client.peek8(0x2d);
389                     print "Tx Errors:  %3d" % self.client.peek8(0x1c);
390                     print "Rx Errors:  %3d" % self.client.peek8(0x1d);
391                     print "CANINTF: %02x"  %self.client.peek8(0x2C);
392
393     
394     
395     
396     def addFilter(self,standardid, verbose= True):
397         """ This method will configure filters on the board. Filters are positive filters meaning that they will only 
398         store messages that match the ids provided in the list of standardid. Since there are 2 buffers and due to the configuration
399         of how the filtering works (see MCP2515 documentation), at least 3 filters must be set to guarentee you do not get any
400         unwanted messages. However even with only 1 filter set you should get all messages from that ID but the other buffer will store 
401         any additional messages.
402         @type standardid: list of integers
403         @param standardid: List of standard ids that need to be set. There can be at most 6 filters set.
404         @type verbose: Boolean
405         @param verbose: If true it will print out messages and diagnostics to terminal.
406         
407         @rtype: None
408         @return: This method does not return anything
409         @todo: rename setFilters
410         """
411        
412         ### ON-CHIP FILTERING
413         if(standardid != None):
414             self.client.MCPreqstatConfiguration();  
415             self.client.poke8(0x60,0x26); # set RXB0 CTRL register to ONLY accept STANDARD messages with filter match (RXM1=0, RMX0=1, BUKT=1)
416             self.client.poke8(0x20,0xFF); #set buffer 0 mask 1 (SID 10:3) to FF
417             self.client.poke8(0x21,0xE0); #set buffer 0 mask 2 bits 7:5 (SID 2:0) to 1s
418             if(len(standardid)>2):
419                self.client.poke8(0x70,0x20); # set RXB1 CTRL register to ONLY accept STANDARD messages with filter match (RXM1=0, RMX0=1)
420                self.client.poke8(0x24,0xFF); #set buffer 1 mask 1 (SID 10:3) to FF
421                self.client.poke8(0x25,0xE0); #set buffer 1 mask 2 bits 7:5 (SID 2:0) to 1s 
422             
423             for filter,ID in enumerate(standardid):
424         
425                if (filter==0):
426                 RXFSIDH = 0x00;
427                 RXFSIDL = 0x01;
428                elif (filter==1):
429                 RXFSIDH = 0x04;
430                 RXFSIDL = 0x05;
431                elif (filter==2):
432                 RXFSIDH = 0x08;
433                 RXFSIDL = 0x09;
434                elif (filter==3):
435                 RXFSIDH = 0x10;
436                 RXFSIDL = 0x11;
437                elif (filter==4):
438                 RXFSIDH = 0x14;
439                 RXFSIDL = 0x15;
440                else:
441                 RXFSIDH = 0x18;
442                 RXFSIDL = 0x19;
443         
444                #### split SID into different regs
445                SIDlow = (ID & 0x07) << 5;  # get SID bits 2:0, rotate them to bits 7:5
446                SIDhigh = (ID >> 3) & 0xFF; # get SID bits 10:3, rotate them to bits 7:0
447                
448                #write SID to regs 
449                self.client.poke8(RXFSIDH,SIDhigh);
450                self.client.poke8(RXFSIDL, SIDlow);
451                        
452                if (verbose == True):
453                    print "Filtering for SID %d (0x%02xh) with filter #%d"%(ID, ID, filter);
454                
455         self.client.MCPreqstatNormal();
456     
457     def filterForPacket(self, standardid, DB0, DB1, verbose= True):
458         """ 
459         This method will configure filters on the board to listen for a specific packet originating 
460         from standardid with data bytes 0 and 1. It will configure all six filters, so you will not receive any other packets.
461         
462         @type standardid: integer
463         @param standardid: standardID to listen for
464         @type DB0: integer
465         @param standardid: DB0 contents to filter for
466         @type DB1: integer
467         @param standardid: DB1 contents to filter for
468         @type verbose: Boolean
469         @param verbose: If true it will print out messages and diagnostics to terminal.
470         
471         @rtype: None
472         @return: This method does not return anything
473         """
474         
475         ### ON-CHIP FILTERING
476        
477         self.client.MCPreqstatConfiguration();  
478         
479         # SID filtering: set CTRL registers to only accept standard messages
480         self.client.poke8(0x60,0x06); # set RXB0 CTRL register to ONLY accept STANDARD messages with filter match (RXM1=0, RXM=1, BUKT=1)
481         self.client.poke8(0x70,0x00); # set RXB1 CTRL register to ONLY accept STANDARD messages with filter match (RXM1=0, RXM0=1)
482
483         # Mask buffer 0 to match SID, DB0, DB1
484         self.client.poke8(0x20,0xFF); #set buffer 0 mask 1 (SID 10:3) to FF
485         self.client.poke8(0x21,0xE0); #set buffer 0 mask 2 bits 7:5 (SID 2:0) to 1s
486         self.client.poke8(0x22,0xFF); #set buffer 0 mask 3 (DB0) to FF 
487         self.client.poke8(0x23,0xFF); #set buffer 0 mask 4 (DB0) to FF
488
489         # Mask buffer 1 to match SID, DB0, DB1
490         self.client.poke8(0x24,0xFF); #set buffer 1 mask 1 (SID 10:3) to FF
491         self.client.poke8(0x25,0xE0); #set buffer 1 mask 2 bits 7:5 (SID 2:0) to 1s
492         self.client.poke8(0x26,0xFF); #set buffer 1 mask 3 (DB0) to FF
493         self.client.poke8(0x27,0xFF); #set buffer 1 mask 4 (DB1) to FF
494         
495         # Split SID into high and low bytes
496         SIDlow = (standardid & 0x07) << 5;  # get SID bits 2:0, rotate them to bits 7:5
497         SIDhigh = (standardid >> 3) & 0xFF; # get SID bits 10:3, rotate them to bits 7:0
498
499             
500         for filter in range(0,5):
501             if (filter==0):
502                 RXFSIDH = 0x00;
503                 RXFSIDL = 0x01;
504                 RXFDB0 = 0x02;
505                 RXFDB1 = 0x03;
506             elif (filter==1):
507                 RXFSIDH = 0x04;
508                 RXFSIDL = 0x05;
509                 RXFDB0 = 0x06;
510                 RXFDB1 = 0x07;
511             elif (filter==2):
512                 RXFSIDH = 0x08;
513                 RXFSIDL = 0x09;
514                 RXFDB0 = 0x0A;
515                 RXFDB1 = 0x0B;
516             elif (filter==3):
517                 RXFSIDH = 0x10;
518                 RXFSIDL = 0x11;
519                 RXFDB0 = 0x12;
520                 RXFDB1 = 0x13;
521             elif (filter==4):
522                 RXFSIDH = 0x14;
523                 RXFSIDL = 0x15;
524                 RXFDB0 = 0x16;
525                 RXFDB1 = 0x17;
526             else:
527                 RXFSIDH = 0x18;
528                 RXFSIDL = 0x19;
529                 RXFDB0 = 0x1A;
530                 RXFDB1 = 0x1B;
531             
532
533             self.client.poke8(RXFSIDH, SIDhigh);
534             self.client.poke8(RXFSIDL, SIDlow);
535             self.client.poke8(RXFDB0, DB0);
536             self.client.poke8(RXFDB1, DB1);
537                 
538             if (verbose == True):
539                 print "Filtering for SID %d DB0 %d DB1 %d with filter #%d"%(standardid, DB0, DB1, filter);
540         
541         self.client.MCPreqstatNormal();    
542    
543         
544     def spitSetup(self,freq):
545         """ 
546         This method sets up the chip for transmitting messages, but does not transmit anything itself.
547         """
548         self.reset();
549         self.client.MCPsetrate(freq);
550         self.client.MCPreqstatNormal();
551         
552     
553     def spitSingle(self,freq, standardid, repeat,writes, period = None, debug = False, packet = None):
554         """ 
555         This method will spit a single message onto the bus. If there is no packet information provided then the 
556         message will be sent as a remote transmission request (RTR). The packet length is assumed to be 8 bytes The message can be repeated given number of times with
557         a gap of period (milliseconds) between each message. This will continue for the the number of times specified in the writes input.
558         This method will setup the bus and call the spit method, L{spit}. This method includes a bus reset and initialization.
559         
560         @type freq: number
561         @param freq: The frequency of the bus
562         
563         @type standardid: list of integer
564         @param standardid: This is a single length list with one integer elment that corresponds to the standard id you wish to write to
565         
566         @type repeat: Boolean
567         @param repeat: If true the message will be repeatedly injected. if not the message will only be injected 1 time
568         
569         @type writes: Integer
570         @param writes: Number of writes of the packet
571         
572         @type period: Integer
573         @param period: Time delay between injections of the packet in Milliseconds
574         
575         @type debug: Boolean
576         @param debug: When true debug status messages will be printed to the terminal
577         
578         @type packet: List
579         @param packet: Contains the data bytes for the packet which is assumed to be of length 8. Each byte is stored as
580                        an integer and can range from 0 to 255 (8 bits). If packet == None then an RTR will be sent on the given
581                        standard id.
582         
583         """
584         self.spitSetup(freq);
585         spit(self,freq, standardid, repeat,writes,  period, debug , packet)
586
587     def spit(self,freq, standardid, repeat,writes, period = None, debug = False, packet = None):
588         """ 
589         This method will spit a single message onto the bus. If there is no packet information provided then the 
590         message will be sent as a remote transmission request (RTR). The packet length is assumed to be 8 bytes The message can be repeated a given number of times with
591         a gap of period (milliseconds) between each message. This will continue for the the number of times specified in the writes input.
592         This method does not include bus setup, it must be done before the method call.
593         
594         
595         @type freq: number
596         @param freq: The frequency of the bus
597         
598         @type standardid: list of integer
599         @param standardid: This is a single length list with one integer elment that corresponds to the standard id you wish to write to
600         
601         @type repeat: Boolean
602         @param repeat: If true the message will be repeatedly injected. if not the message will only be injected 1 time
603         
604         @type writes: Integer
605         @param writes: Number of writes of the packet
606         
607         @type period: Integer
608         @param period: Time delay between injections of the packet in Milliseconds
609         
610         @type debug: Boolean
611         @param debug: When true debug status messages will be printed to the terminal
612         
613         @type packet: List
614         @param packet: Contains the data bytes for the packet which is assumed to be of length 8. Each byte is stored as
615                        an integer and can range from 0 to 255 (8 bits). If packet == None then an RTR will be sent on the given
616                        standard id.
617         
618         """
619
620         #### split SID into different regs
621         SIDlow = (standardid[0] & 0x07) << 5;  # get SID bits 2:0, rotate them to bits 7:5
622         SIDhigh = (standardid[0] >> 3) & 0xFF; # get SID bits 10:3, rotate them to bits 7:0
623         
624         if(packet == None):
625             
626             # if no packet, RTR for inputted arbID
627             # so packet to transmit is SID + padding out EID registers + RTR request (set bit 6, clear lower nibble of DLC register)
628             packet = [SIDhigh, SIDlow, 0x00,0x00,0x40] 
629         
630         
631         else:
632
633             # if we do have a packet, packet is SID + padding out EID registers + DLC of 8 + packet
634             #
635             """@todo: allow for variable-length packets"""
636             #    TODO: allow for variable-length packets
637             #
638             packet = [SIDhigh, SIDlow, 0x00,0x00, # pad out EID regs
639                   0x08, # bit 6 must be set to 0 for data frame (1 for RTR) 
640                   # lower nibble is DLC                   
641                  packet[0],packet[1],packet[2],packet[3],packet[4],packet[5],packet[6],packet[7]]
642             
643         
644         if(debug):
645             if self.client.MCPcanstat()>>5!=0:
646                 print "Warning: currently in %s mode. NOT in normal mode! May not transmit.\n" %self.client.MCPcanstatstr();
647             print "\nInitial state:"
648             print "Tx Errors:  %3d" % self.client.peek8(0x1c);
649             print "Rx Errors:  %3d" % self.client.peek8(0x1d);
650             print "Error Flags:  %02x\n" % self.client.peek8(0x2d);
651             print "TXB0CTRL: %02x" %self.client.peek8(0x30);
652             print "CANINTF: %02x\n"  %self.client.peek8(0x2C);
653             print "\n\nATTEMPTING TRANSMISSION!!!"
654                 
655         print "Transmitting packet: "
656         #print self.client.packet2str(packet)
657                 
658         self.client.txpacket(packet);
659             
660         if repeat:
661             """@todo: the repeat variable is no longer needed and can be removed """
662             print "\nNow looping on transmit. "
663             if period != None:
664                 for i in range(0,writes):
665                     self.client.MCPrts(TXB0=True);
666                     #tic = time.time()
667                     time.sleep(period/1000) # pause for period ms before sending again
668                     #print time.time()-tic
669                 #starttime = time.time();
670                 #while((time.time()-starttime < duration)):
671                 #    self.client.MCPrts(TXB0=True);
672                 #    print "MSG printed"
673             else:
674                 for i in range(0,writes): 
675                     self.client.MCPrts(TXB0=True);
676         print "messages injected"
677         
678         # MORE DEBUGGING        
679         if(debug): 
680             checkcount = 0;
681             TXB0CTRL = self.client.peek8(0x30);
682         
683             print "Tx Errors:  %3d" % self.client.peek8(0x1c);
684             print "Rx Errors:  %3d" % self.client.peek8(0x1d);
685             print "EFLG register:  %02x" % self.client.peek8(0x2d);
686             print "TXB0CTRL: %02x" %TXB0CTRL;
687             print "CANINTF: %02x\n"  %self.client.peek8(0x2C);
688         
689             while(TXB0CTRL | 0x00 != 0x00):
690                 checkcount+=1;
691                 TXB0CTRL = self.client.peek8(0x30);
692                 if (checkcount %30 ==0):
693                     print "Tx Errors:  %3d" % self.client.peek8(0x1c);
694                     print "Rx Errors:  %3d" % self.client.peek8(0x1d);
695                     print "EFLG register:  %02x" % self.client.peek8(0x2d);
696                     print "TXB0CTRL: %02x" %TXB0CTRL;
697                     print "CANINTF: %02x\n"  %self.client.peek8(0x2C);
698
699
700     def setRate(self,freq):
701         """ 
702         This method will reset the frequency that the MCP2515 expects the CAN bus to be on.
703         
704         @type freq: Number
705         @param freq: Frequency of the CAN bus
706         """
707         self.client.MCPsetrate(freq);
708
709         
710
711     # This will write the data provided in the packets which is expected to be a list of lists
712     # of the following form:
713     # for a given row = packets[i]
714     # row[0] time delay relative to the last packet. if 0 or empty there will be no delay
715     # row[1] = Standard ID (integer)
716     # row[2] = Data Length (0-8) (if it is zero we assume an Remote Transmit Request)
717     # row[3] = Data Byte 0
718     # row[4] = Data Byte 1
719     #    .... up to Data Byte 8 ( THIS ASSUMES A PACKET OF LENGTH 8!!!
720     def writeData(self,packets,freq):
721         self.client.serInit()
722         self.spitSetup(freq)
723         for row in packets:
724             if( row[0] != 0 and row[0] != ""):
725                 time.sleep(row[0])
726             sID = row[1]
727             #### split SID into different regs
728             SIDlow = (sID & 0x07) << 5;  # get SID bits 2:0, rotate them to bits 7:5
729             SIDhigh = (sID >> 3) & 0xFF; # get SID bits 10:3, rotate them to bits 7:0
730             packet = [SIDhigh,SIDlow,0x00,0x00,0x08]
731             #dlc = row[2]
732             dlc = 8
733             for i in range(3,dlc+3):
734                 packet.append(row[i])
735             print packet
736             self.client.txpacket(packet)
737                 
738         
739         
740         
741
742 if __name__ == "__main__":  
743
744     parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,description='''\
745     
746         Run commands on the MCP2515. Valid commands are:
747         
748             info 
749             test
750             peek 0x(start) [0x(stop)]
751             reset
752             
753             sniff 
754             freqtest
755             snifftest
756             spit
757         ''')
758         
759     
760     parser.add_argument('verb', choices=['info', 'test','peek', 'reset', 'sniff', 'freqtest','snifftest', 'spit', 'packet']);
761     parser.add_argument('-f', '--freq', type=int, default=500, help='The desired frequency (kHz)', choices=[100, 125, 250, 500, 1000]);
762     parser.add_argument('-t','--time', type=int, default=15, help='The duration to run the command (s)');
763     parser.add_argument('-o', '--output', default=None,help='Output file');
764     parser.add_argument("-d", "--description", help='Description of experiment (included in the output file)', default="");
765     parser.add_argument('-v',"--verbose",action='store_false',help='-v will stop packet output to terminal', default=True);
766     parser.add_argument('-c','--comment', help='Comment attached to ech packet uploaded',default=None);
767     parser.add_argument('-b', '--debug', action='store_true', help='-b will turn on debug mode, printing packet status', default=False);
768     parser.add_argument('-a', '--standardid', type=int, action='append', help='Standard ID to accept with filter 0 [1, 2, 3, 4, 5]', default=None);
769     parser.add_argument('-x', '--faster', action='store_true', help='-x will use "fast packet recieve," which may duplicate packets and/or cause other weird behavior.', default=False);
770     parser.add_argument('-r', '--repeat', action='store_true', help='-r with "spit" will continuously send the inputted packet. This will put the GoodTHOPTHER into an infinite loop.', default=False);
771     parser.add_argument('-db0', '--databyte0', type=int, default = None, help='-db0 to filter for a specfic data byte');
772     parser.add_argument('-db1', '--databyte1', type=int, default = None, help='-db0 to filter for a specfic data byte');
773
774     
775     
776     args = parser.parse_args();
777     freq = args.freq
778     duration = args.time
779     filename = args.output
780     description = args.description
781     verbose = args.verbose
782     comments = args.comment
783     debug = args.debug
784     standardid = args.standardid
785     faster=args.faster
786     repeat = args.repeat
787     db0 = args.databyte0
788     db1 = args.databyte1
789
790     comm = GoodFETMCPCANCommunication("./");
791     
792     if(args.verb=="packet"):
793         comm.filterForPacket(standardid=standardid[0], DB0=db0, DB1=db1, verbose= True)
794     
795     ##########################
796     #   INFO
797     ##########################
798     #
799     # Prints MCP state info
800     #
801     if(args.verb=="info"):
802         comm.printInfo()
803         
804            
805     ##########################
806     #   RESET
807     ##########################
808     #
809     #
810             
811     if(args.verb=="reset"):
812         comm.reset()
813         
814     ##########################
815     #   SNIFF
816     ##########################
817     #
818     #   runs in ListenOnly mode
819     #   utility function to pull info off the car's CAN bus
820     #
821     
822     if(args.verb=="sniff"):
823         comm.sniff(freq=freq,duration=duration,description=description,verbose=verbose,comment=comments,filename=filename, standardid=standardid, debug=debug, faster=faster, db0=db0, db1=db1)    
824                     
825     ##########################
826     #   SNIFF TEST
827     ##########################
828     #
829     #   runs in NORMAL mode
830     #   intended for NETWORKED MCP chips to verify proper operation
831     #
832        
833     if(args.verb=="snifftest"):
834         comm.sniffTest(freq=freq)
835         
836         
837     ##########################
838     #   FREQ TEST
839     ##########################
840     #
841     #   runs in LISTEN ONLY mode
842     #   tests bus for desired frequency --> sniffs bus for specified length of time and reports
843     #   if packets were properly formatted
844     #
845     #
846     
847     if(args.verb=="freqtest"):
848         comm.freqtest(freq=freq)
849
850
851
852     ##########################
853     #   iSniff
854     ##########################
855     #
856     #    """ An intelligent sniffer, decodes message format """
857     #    """ More features to be added soon """
858     if(args.verb=="isniff"):
859         comm.isniff(freq=freq)
860                 
861                 
862     ##########################
863     #   MCP TEST
864     ##########################
865     #
866     #   Runs in LOOPBACK mode
867     #   self-check diagnostic
868     #   wasn't working before due to improperly formatted packet
869     #
870     #   ...add automatic packet check rather than making user verify successful packet
871     if(args.verb=="test"):
872         comm.test()
873         
874     if(args.verb=="peek"):
875         start=0x0000;
876         if(len(sys.argv)>2):
877             start=int(sys.argv[2],16);
878         stop=start;
879         if(len(sys.argv)>3):
880             stop=int(sys.argv[3],16);
881         print "Peeking from %04x to %04x." % (start,stop);
882         while start<=stop:
883             print "%04x: %02x" % (start,client.peek8(start));
884             start=start+1;
885             
886     ##########################
887     #   SPIT
888     ##########################
889     #
890     #   Basic packet transmission
891     #   runs in NORMAL MODE!
892     # 
893     #   checking TX error flags--> currently throwing error flags on every
894     #   transmission (travis thinks this is because we're sniffing in listen-only
895     #   and thus not generating an ack bit on the recieving board)
896     if(args.verb=="spit"):
897         comm.spitSingle(freq=freq, standardid=standardid,duration=duration, repeat=repeat, debug=debug)
898
899
900     
901     
902     
903     
904         
905         
906     
907     
908     
909