b80f7beec58b84f76434a551ce640228e7237c82
[goodfet] / 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             self.client.poke8(RXFSIDH, SIDhigh);
533             self.client.poke8(RXFSIDL, SIDlow);
534             self.client.poke8(RXFDB0, DB0);
535             self.client.poke8(RXFDB1, DB1);
536                 
537             if (verbose == True):
538                 print "Filtering for SID %d DB0 %d DB1 %d with filter #%d"%(standardid, DB0, DB1, filter);
539         
540         self.client.MCPreqstatNormal();   
541     
542     def multiPacketTest(self):
543         
544         self.reset();
545         self.client.MCPsetrate(500);
546         self.client.MCPreqstatNormal();
547         
548         packet0 = [0x00, 0x00, 0x00,0x00, # pad out EID regs
549                    0x08, # bit 6 must be set to 0 for data frame (1 for RTR) 
550                    # lower nibble is DLC                   
551                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
552         
553         packet1 = [0x00, 0x20, 0x00,0x00, # pad out EID regs
554                    0x08, # bit 6 must be set to 0 for data frame (1 for RTR) 
555                    # lower nibble is DLC                   
556                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
557         packet2 = [0x00, 0x40, 0x00,0x00, # pad out EID regs
558                    0x08, # bit 6 must be set to 0 for data frame (1 for RTR) 
559                    # lower nibble is DLC                   
560                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
561             
562         comm.multiPacketSpit(packet0=packet0, packet1=packet1, packet2=packet2)
563         
564         comm.multiPacketSpit(packet0rts=True, packet1rts=True, packet2rts=True)
565         comm.multiPacketSpit(packet2rts=True)
566         comm.multiPacketSpit(packet1rts=True)
567         comm.multiPacketSpit(packet0rts=True)
568         
569
570
571     def multiPacketSpit(self, packet0 = None, packet1 = None, packet2 = None, packet0rts = False, packet1rts = False, packet2rts = False):
572         """ 
573             This method writes packets to the chip's TX buffers and/or sends the contents of the buffers onto the bus.
574             
575             @type packet0: list of integer
576             @param packet0: A list of 13 integers of the format [SIDhigh SIDlow 0 0 DLC DB0-7] to be loaded into TXBF0
577             
578             @type packet1: list of integer
579             @param packet1: A list of 13 integers of the format [SIDhigh SIDlow 0 0 DLC DB0-7] to be loaded into TXBF1
580             
581             @type packet2: list of integer
582             @param packet2: A list of 13 integers of the format [SIDhigh SIDlow 0 0 DLC DB0-7] to be loaded into TXBF2
583             
584             @type packet0rts: Boolean
585             @param packet0rts: If true the message in TX buffer 0 will be sent
586             
587             @type packet2rts: Boolean
588             @param packet0rts: If true the message in TX buffer 1 will be sent
589             
590             @type packet2rts: Boolean
591             @param packet0rts: If true the message in TX buffer 2 will be sent
592             
593         """
594         
595         if(packet0 != None):
596             self.client.writetxbuffer(packet0,0)
597         #   print("trying to write TX buffer 0");
598         #  for db in packet0:
599         #      print" %d" %db
600         if (packet1 != None):
601             self.client.writetxbuffer(packet1,1)
602             # print("trying to write TX buffer 1");
603                 # for db in packet0:
604                     #     print" %d" %db
605         if (packet2 != None):
606             self.client.writetxbuffer(packet2,2)
607             # print("trying to write TX buffer 2");
608                 #  for db in packet0:
609                     #   print" %d" %db
610             
611         #  if(packet0rts):
612             #    print("trying to send TX buffer 0")
613         #if(packet1rts):
614             #    print("trying to send TX buffer 1")
615         #if(packet2rts):
616             #    print("trying to send TX buffer 2")
617
618         self.client.MCPrts(TXB0=packet0rts, TXB1=packet1rts, TXB2=packet2rts)
619  
620         
621     def spitSetup(self,freq):
622         """ 
623         This method sets up the chip for transmitting messages, but does not transmit anything itself.
624         """
625         self.reset();
626         self.client.MCPsetrate(freq);
627         self.client.MCPreqstatNormal();
628         
629     
630     def spitSingle(self,freq, standardid, repeat,writes, period = None, debug = False, packet = None):
631         """ 
632         This method will spit a single message onto the bus. If there is no packet information provided then the 
633         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
634         a gap of period (milliseconds) between each message. This will continue for the the number of times specified in the writes input.
635         This method will setup the bus and call the spit method, L{spit}. This method includes a bus reset and initialization.
636         
637         @type freq: number
638         @param freq: The frequency of the bus
639         
640         @type standardid: list of integer
641         @param standardid: This is a single length list with one integer elment that corresponds to the standard id you wish to write to
642         
643         @type repeat: Boolean
644         @param repeat: If true the message will be repeatedly injected. if not the message will only be injected 1 time
645         
646         @type writes: Integer
647         @param writes: Number of writes of the packet
648         
649         @type period: Integer
650         @param period: Time delay between injections of the packet in Milliseconds
651         
652         @type debug: Boolean
653         @param debug: When true debug status messages will be printed to the terminal
654         
655         @type packet: List
656         @param packet: Contains the data bytes for the packet which is assumed to be of length 8. Each byte is stored as
657                        an integer and can range from 0 to 255 (8 bits). If packet == None then an RTR will be sent on the given
658                        standard id.
659         
660         """
661         self.spitSetup(freq);
662         spit(self,freq, standardid, repeat,writes,  period, debug , packet)
663
664     def spit(self,freq, standardid, repeat,writes, period = None, debug = False, packet = None):
665         """ 
666         This method will spit a single message onto the bus. If there is no packet information provided then the 
667         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
668         a gap of period (milliseconds) between each message. This will continue for the the number of times specified in the writes input.
669         This method does not include bus setup, it must be done before the method call.
670         
671         
672         @type freq: number
673         @param freq: The frequency of the bus
674         
675         @type standardid: list of integer
676         @param standardid: This is a single length list with one integer elment that corresponds to the standard id you wish to write to
677         
678         @type repeat: Boolean
679         @param repeat: If true the message will be repeatedly injected. if not the message will only be injected 1 time
680         
681         @type writes: Integer
682         @param writes: Number of writes of the packet
683         
684         @type period: Integer
685         @param period: Time delay between injections of the packet in Milliseconds
686         
687         @type debug: Boolean
688         @param debug: When true debug status messages will be printed to the terminal
689         
690         @type packet: List
691         @param packet: Contains the data bytes for the packet which is assumed to be of length 8. Each byte is stored as
692                        an integer and can range from 0 to 255 (8 bits). If packet == None then an RTR will be sent on the given
693                        standard id.
694         
695         """
696
697         #### split SID into different regs
698         SIDlow = (standardid[0] & 0x07) << 5;  # get SID bits 2:0, rotate them to bits 7:5
699         SIDhigh = (standardid[0] >> 3) & 0xFF; # get SID bits 10:3, rotate them to bits 7:0
700         
701         if(packet == None):
702             
703             # if no packet, RTR for inputted arbID
704             # so packet to transmit is SID + padding out EID registers + RTR request (set bit 6, clear lower nibble of DLC register)
705             packet = [SIDhigh, SIDlow, 0x00,0x00,0x40] 
706         
707         
708         else:
709
710             # if we do have a packet, packet is SID + padding out EID registers + DLC of 8 + packet
711             #
712             """@todo: allow for variable-length packets"""
713             #    TODO: allow for variable-length packets
714             #
715             packet = [SIDhigh, SIDlow, 0x00,0x00, # pad out EID regs
716                   0x08, # bit 6 must be set to 0 for data frame (1 for RTR) 
717                   # lower nibble is DLC                   
718                  packet[0],packet[1],packet[2],packet[3],packet[4],packet[5],packet[6],packet[7]]
719             
720         
721         if(debug):
722             if self.client.MCPcanstat()>>5!=0:
723                 print "Warning: currently in %s mode. NOT in normal mode! May not transmit.\n" %self.client.MCPcanstatstr();
724             print "\nInitial state:"
725             print "Tx Errors:  %3d" % self.client.peek8(0x1c);
726             print "Rx Errors:  %3d" % self.client.peek8(0x1d);
727             print "Error Flags:  %02x\n" % self.client.peek8(0x2d);
728             print "TXB0CTRL: %02x" %self.client.peek8(0x30);
729             print "CANINTF: %02x\n"  %self.client.peek8(0x2C);
730             print "\n\nATTEMPTING TRANSMISSION!!!"
731                 
732         print "Transmitting packet: "
733         #print self.client.packet2str(packet)
734                 
735         self.client.txpacket(packet);
736             
737         if repeat:
738             """@todo: the repeat variable is no longer needed and can be removed """
739             print "\nNow looping on transmit. "
740             if period != None:
741                 for i in range(0,writes):
742                     self.client.MCPrts(TXB0=True);
743                     #tic = time.time()
744                     time.sleep(period/1000) # pause for period ms before sending again
745                     #print time.time()-tic
746                 #starttime = time.time();
747                 #while((time.time()-starttime < duration)):
748                 #    self.client.MCPrts(TXB0=True);
749                 #    print "MSG printed"
750             else:
751                 for i in range(0,writes): 
752                     self.client.MCPrts(TXB0=True);
753         print "messages injected"
754         
755         # MORE DEBUGGING        
756         if(debug): 
757             checkcount = 0;
758             TXB0CTRL = self.client.peek8(0x30);
759         
760             print "Tx Errors:  %3d" % self.client.peek8(0x1c);
761             print "Rx Errors:  %3d" % self.client.peek8(0x1d);
762             print "EFLG register:  %02x" % self.client.peek8(0x2d);
763             print "TXB0CTRL: %02x" %TXB0CTRL;
764             print "CANINTF: %02x\n"  %self.client.peek8(0x2C);
765         
766             while(TXB0CTRL | 0x00 != 0x00):
767                 checkcount+=1;
768                 TXB0CTRL = self.client.peek8(0x30);
769                 if (checkcount %30 ==0):
770                     print "Tx Errors:  %3d" % self.client.peek8(0x1c);
771                     print "Rx Errors:  %3d" % self.client.peek8(0x1d);
772                     print "EFLG register:  %02x" % self.client.peek8(0x2d);
773                     print "TXB0CTRL: %02x" %TXB0CTRL;
774                     print "CANINTF: %02x\n"  %self.client.peek8(0x2C);
775
776
777     def setRate(self,freq):
778         """ 
779         This method will reset the frequency that the MCP2515 expects the CAN bus to be on.
780         
781         @type freq: Number
782         @param freq: Frequency of the CAN bus
783         """
784         self.client.MCPsetrate(freq);
785
786         
787
788     # This will write the data provided in the packets which is expected to be a list of lists
789     # of the following form:
790     # for a given row = packets[i]
791     # row[0] time delay relative to the last packet. if 0 or empty there will be no delay
792     # row[1] = Standard ID (integer)
793     # row[2] = Data Length (0-8) (if it is zero we assume an Remote Transmit Request)
794     # row[3] = Data Byte 0
795     # row[4] = Data Byte 1
796     #    .... up to Data Byte 8 ( THIS ASSUMES A PACKET OF LENGTH 8!!!
797     def writeData(self,packets,freq):
798         self.client.serInit()
799         self.spitSetup(freq)
800         for row in packets:
801             if( row[0] != 0 and row[0] != ""):
802                 time.sleep(row[0])
803             sID = row[1]
804             #### split SID into different regs
805             SIDlow = (sID & 0x07) << 5;  # get SID bits 2:0, rotate them to bits 7:5
806             SIDhigh = (sID >> 3) & 0xFF; # get SID bits 10:3, rotate them to bits 7:0
807             packet = [SIDhigh,SIDlow,0x00,0x00,0x08]
808             #dlc = row[2]
809             dlc = 8
810             for i in range(3,dlc+3):
811                 packet.append(row[i])
812             print packet
813             self.client.txpacket(packet)
814                 
815         
816         
817         
818
819 if __name__ == "__main__":  
820
821     parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,description='''\
822     
823         Run commands on the MCP2515. Valid commands are:
824         
825             info 
826             test
827             peek 0x(start) [0x(stop)]
828             reset
829             
830             sniff 
831             freqtest
832             snifftest
833             spit
834         ''')
835         
836     
837     parser.add_argument('verb', choices=['info', 'test','peek', 'reset', 'sniff', 'freqtest','snifftest', 'spit', 'packet', 'multipacket']);
838     parser.add_argument('-f', '--freq', type=int, default=500, help='The desired frequency (kHz)', choices=[100, 125, 250, 500, 1000]);
839     parser.add_argument('-t','--time', type=int, default=15, help='The duration to run the command (s)');
840     parser.add_argument('-o', '--output', default=None,help='Output file');
841     parser.add_argument("-d", "--description", help='Description of experiment (included in the output file)', default="");
842     parser.add_argument('-v',"--verbose",action='store_false',help='-v will stop packet output to terminal', default=True);
843     parser.add_argument('-c','--comment', help='Comment attached to ech packet uploaded',default=None);
844     parser.add_argument('-b', '--debug', action='store_true', help='-b will turn on debug mode, printing packet status', default=False);
845     parser.add_argument('-a', '--standardid', type=int, action='append', help='Standard ID to accept with filter 0 [1, 2, 3, 4, 5]', default=None);
846     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);
847     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);
848     parser.add_argument('-db0', '--databyte0', type=int, default = None, help='-db0 to filter for a specfic data byte');
849     parser.add_argument('-db1', '--databyte1', type=int, default = None, help='-db0 to filter for a specfic data byte');
850
851     
852     
853     args = parser.parse_args();
854     freq = args.freq
855     duration = args.time
856     filename = args.output
857     description = args.description
858     verbose = args.verbose
859     comments = args.comment
860     debug = args.debug
861     standardid = args.standardid
862     faster=args.faster
863     repeat = args.repeat
864     db0 = args.databyte0
865     db1 = args.databyte1
866
867     comm = GoodFETMCPCANCommunication("./");
868     
869     if(args.verb=="packet"):
870         comm.filterForPacket(standardid=standardid[0], DB0=db0, DB1=db1, verbose= True)
871     if(args.verb=="multipacket"):
872         comm.multiPacketTest();
873     
874     ##########################
875     #   INFO
876     ##########################
877     #
878     # Prints MCP state info
879     #
880     if(args.verb=="info"):
881         comm.printInfo()
882         
883            
884     ##########################
885     #   RESET
886     ##########################
887     #
888     #
889             
890     if(args.verb=="reset"):
891         comm.reset()
892         
893     ##########################
894     #   SNIFF
895     ##########################
896     #
897     #   runs in ListenOnly mode
898     #   utility function to pull info off the car's CAN bus
899     #
900     
901     if(args.verb=="sniff"):
902         comm.sniff(freq=freq,duration=duration,description=description,verbose=verbose,comment=comments,filename=filename, standardid=standardid, debug=debug, faster=faster, db0=db0, db1=db1)    
903                     
904     ##########################
905     #   SNIFF TEST
906     ##########################
907     #
908     #   runs in NORMAL mode
909     #   intended for NETWORKED MCP chips to verify proper operation
910     #
911        
912     if(args.verb=="snifftest"):
913         comm.sniffTest(freq=freq)
914         
915         
916     ##########################
917     #   FREQ TEST
918     ##########################
919     #
920     #   runs in LISTEN ONLY mode
921     #   tests bus for desired frequency --> sniffs bus for specified length of time and reports
922     #   if packets were properly formatted
923     #
924     #
925     
926     if(args.verb=="freqtest"):
927         comm.freqtest(freq=freq)
928
929
930
931     ##########################
932     #   iSniff
933     ##########################
934     #
935     #    """ An intelligent sniffer, decodes message format """
936     #    """ More features to be added soon """
937     if(args.verb=="isniff"):
938         comm.isniff(freq=freq)
939                 
940                 
941     ##########################
942     #   MCP TEST
943     ##########################
944     #
945     #   Runs in LOOPBACK mode
946     #   self-check diagnostic
947     #   wasn't working before due to improperly formatted packet
948     #
949     #   ...add automatic packet check rather than making user verify successful packet
950     if(args.verb=="test"):
951         comm.test()
952         
953     if(args.verb=="peek"):
954         start=0x0000;
955         if(len(sys.argv)>2):
956             start=int(sys.argv[2],16);
957         stop=start;
958         if(len(sys.argv)>3):
959             stop=int(sys.argv[3],16);
960         print "Peeking from %04x to %04x." % (start,stop);
961         while start<=stop:
962             print "%04x: %02x" % (start,client.peek8(start));
963             start=start+1;
964             
965     ##########################
966     #   SPIT
967     ##########################
968     #
969     #   Basic packet transmission
970     #   runs in NORMAL MODE!
971     # 
972     #   checking TX error flags--> currently throwing error flags on every
973     #   transmission (travis thinks this is because we're sniffing in listen-only
974     #   and thus not generating an ack bit on the recieving board)
975     if(args.verb=="spit"):
976         comm.spitSingle(freq=freq, standardid=standardid,duration=duration, repeat=repeat, debug=debug)
977
978
979     
980     
981     
982     
983         
984         
985     
986     
987     
988