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