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