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