├── SampleLogs └── log.db ├── Pictures ├── OBDII Cable.jpg ├── CanBusHacker01.png ├── OBDII Connection.jpg └── Arduino UNO + CAN Bus Shield.jpg ├── CANBridge ├── Arduino Library │ ├── defaults.h │ ├── global.h │ ├── Canbus.h │ ├── mcp2515.h │ ├── Canbus.cpp │ ├── mcp2515_defs.h │ └── mcp2515.c └── CANBridge.ino ├── README.md └── CanBusHacker.py /SampleLogs/log.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohjeongwook/CanBusHacker/HEAD/SampleLogs/log.db -------------------------------------------------------------------------------- /Pictures/OBDII Cable.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohjeongwook/CanBusHacker/HEAD/Pictures/OBDII Cable.jpg -------------------------------------------------------------------------------- /Pictures/CanBusHacker01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohjeongwook/CanBusHacker/HEAD/Pictures/CanBusHacker01.png -------------------------------------------------------------------------------- /Pictures/OBDII Connection.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohjeongwook/CanBusHacker/HEAD/Pictures/OBDII Connection.jpg -------------------------------------------------------------------------------- /Pictures/Arduino UNO + CAN Bus Shield.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohjeongwook/CanBusHacker/HEAD/Pictures/Arduino UNO + CAN Bus Shield.jpg -------------------------------------------------------------------------------- /CANBridge/Arduino Library/defaults.h: -------------------------------------------------------------------------------- 1 | #ifndef DEFAULTS_H 2 | #define DEFAULTS_H 3 | 4 | #define P_MOSI B,3 5 | #define P_MISO B,4 6 | #define P_SCK B,5 7 | 8 | //#define MCP2515_CS D,3 // Rev A 9 | #define MCP2515_CS B,2 // Rev B 10 | #define MCP2515_INT D,2 11 | #define LED2_HIGH B,0 12 | #define LED2_LOW B,0 13 | 14 | #endif // DEFAULTS_H 15 | -------------------------------------------------------------------------------- /CANBridge/Arduino Library/global.h: -------------------------------------------------------------------------------- 1 | #ifndef GLOBAL_H 2 | #define GLOBAL_H 3 | 4 | // ---------------------------------------------------------------------------- 5 | 6 | #define true 1 7 | #define false 0 8 | 9 | #define True 1 10 | #define False 0 11 | 12 | //typedef _Bool bool; 13 | //typedef boolean Bool; 14 | 15 | // ---------------------------------------------------------------------------- 16 | 17 | #define RESET(x) _XRS(x) 18 | #define SET(x) _XS(x) 19 | #define TOGGLE(x) _XT(x) 20 | #define SET_OUTPUT(x) _XSO(x) 21 | #define SET_INPUT(x) _XSI(x) 22 | #define IS_SET(x) _XR(x) 23 | 24 | #define PORT(x) _port2(x) 25 | #define DDR(x) _ddr2(x) 26 | #define PIN(x) _pin2(x) 27 | 28 | #define _XRS(x,y) PORT(x) &= ~(1< 11 | 12 | #define CANSPEED_125 7 // CAN speed at 125 kbps 13 | #define CANSPEED_250 3 // CAN speed at 250 kbps 14 | #define CANSPEED_500 1 // CAN speed at 500 kbps 15 | 16 | 17 | #define ENGINE_COOLANT_TEMP 0x05 18 | #define ENGINE_RPM 0x0C 19 | #define VEHICLE_SPEED 0x0D 20 | #define MAF_SENSOR 0x10 21 | #define O2_VOLTAGE 0x14 22 | #define THROTTLE 0x11 23 | 24 | #define PID_REQUEST 0x7DF 25 | #define PID_REPLY 0x7E8 26 | 27 | class CanbusClass 28 | { 29 | public: 30 | 31 | CanbusClass(); 32 | char init(unsigned char); 33 | char message_tx(unsigned int ID, unsigned char *buffer); 34 | unsigned int message_rx(unsigned char *buffer); 35 | bool raw_message_rx(uint16_t &id, int8_t &rtr, uint8_t &length, unsigned char *raw_buffer, unsigned char *buffer); 36 | char ecu_req(unsigned char pid, char *buffer); 37 | private: 38 | 39 | }; 40 | extern CanbusClass Canbus; 41 | //extern tCAN message; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /CANBridge/CANBridge.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | unsigned char raw_buffer[13]; 4 | unsigned char buffer[8]; // Buffer to store the incoming data 5 | unsigned char serial_input_buffer[12]; 6 | int serial_input_buffer_i=0; 7 | 8 | void setup() { 9 | Serial.begin(115200); 10 | delay(1000); 11 | 12 | if(Canbus.init(CANSPEED_500)) /* Initialise MCP2515 CAN controller at the specified speed */ 13 | Serial.println("CAN Init ok"); 14 | else 15 | Serial.println("Can't init CAN"); 16 | 17 | delay(1000); 18 | } 19 | 20 | void loop() { 21 | uint16_t id; 22 | int8_t rtr; 23 | uint8_t length; 24 | 25 | if(Canbus.raw_message_rx(id, rtr, length, raw_buffer, buffer)) // Check to see if we have a message on the Bus 26 | { 27 | char str_buffer[100]; 28 | 29 | sprintf(str_buffer,"CAN Message: [%2x] %d %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x", id, length, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7] ); 30 | Serial.println(str_buffer); 31 | } 32 | 33 | if (Serial.available() > 0) { 34 | int byte = Serial.read(); 35 | 36 | Serial.print("Received: "); 37 | Serial.println(byte, DEC); 38 | 39 | serial_input_buffer[serial_input_buffer_i++]=byte; 40 | 41 | if(serial_input_buffer_i==12) 42 | { 43 | serial_input_buffer_i=0; 44 | unsigned char message[8]; 45 | Canbus.message_tx(*((int *)serial_input_buffer),serial_input_buffer+4); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CanBusHacker 2 | This is a project to make a real time CAN packet monitoring system using Arduino and CAN BUS shield hardware. This makes a very affordable and reliable CAN packet monitor and injector. 3 | 4 | ## Prerequisites 5 | ### Hardware 6 | * Arduino UNO 7 | * CAN BUS Shield: Currently this project supports Can Bus Shield v1.1 product (http://www.jayconsystems.com/can-bus-shield.html). You can also purchase this from eBay. 8 | * Probably other products will work without modification or minor tweak. We will add support for other products as we have access to them 9 | 10 | * OBD-II to DB9 Cable: something similar to this https://www.sparkfun.com/products/10087 11 | * USB to PC Cable to program and transfer serial data to and from Arduino 12 | 13 | ### Software 14 | * Python 2.7.x: http://python.org 15 | * Pyside: https://pypi.python.org/pypi/PySide (pip install pyside) 16 | * PySerial: https://pypi.python.org/pypi/pyserial (pip install pyserial) 17 | 18 | ## How to Install 19 | 20 | ### Arudino Programming 21 | * First assemble your Arduino and CanBusShield, refer to your CanBusShield's manufacturer's manual for more details. 22 | * Download Arduino Library from CanBusShield's vendor site. 23 | * For example, for the JayCon product, I could download it from http://www.jayconsystems.com/fileuploader/download/download/?d=0&file=custom%2Fupload%2FFile-1363136372.zip. But, you need to modify the code to gather raw packets. 24 | * The "CANBridge\Arduino Library" folder contains the library for JayCon product. It has some modifications in the code to support raw level packet collection. 25 | * If you want to support your board, you should go through similar code change. To get some idea how you modify your library code, please look at these changes: https://github.com/ohjeongwook/CanBusHacker/commit/36e3019e95acba9e92f10366f415be9ebad8a710 26 | * Next, import the library using the method described here: http://arduino.cc/en/guide/libraries 27 | * Open CanBridge.ino file, compile and upload to your device 28 | 29 | ### CanBusHacker.py 30 | This program is for Windows that communicates with Arduino board through serial port. Probably you can use this code on Linux or OSX with minor changes. 31 | * Install dependencies and run CanBusHacker.py. 32 | * Now connect your Arduino device to your laptop. 33 | * Connect your OBD-II cable to your car 34 | * Select Arduino -> Start Capture menu. You need to select serial port that is connected to your Aruino and need to specify output database file. 35 | * If you didn't start your engine, now is the good time to start it, you will see various packets coming up 36 | * The database file format is SQLite and you can open it up later using File -> Open Log menu. 37 | -------------------------------------------------------------------------------- /CANBridge/Arduino Library/mcp2515.h: -------------------------------------------------------------------------------- 1 | #ifndef MCP2515_H 2 | #define MCP2515_H 3 | 4 | // ---------------------------------------------------------------------------- 5 | /* Copyright (c) 2007 Fabian Greif 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright 13 | * notice, this list of conditions and the following disclaimer. 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | // ---------------------------------------------------------------------------- 31 | 32 | #include 33 | 34 | #include "mcp2515_defs.h" 35 | #include "global.h" 36 | #ifdef __cplusplus 37 | 38 | extern "C" 39 | { 40 | 41 | 42 | #endif 43 | // ---------------------------------------------------------------------------- 44 | typedef struct 45 | { 46 | uint16_t id; 47 | struct { 48 | int8_t rtr : 1; 49 | uint8_t length : 4; 50 | } header; 51 | uint8_t data[8]; 52 | uint8_t raw_data[13]; 53 | } tCAN; 54 | 55 | // ---------------------------------------------------------------------------- 56 | uint8_t spi_putc( uint8_t data ); 57 | 58 | // ---------------------------------------------------------------------------- 59 | void mcp2515_write_register( uint8_t adress, uint8_t data ); 60 | 61 | // ---------------------------------------------------------------------------- 62 | uint8_t mcp2515_read_register(uint8_t adress); 63 | 64 | // ---------------------------------------------------------------------------- 65 | void mcp2515_bit_modify(uint8_t adress, uint8_t mask, uint8_t data); 66 | 67 | // ---------------------------------------------------------------------------- 68 | uint8_t mcp2515_read_status(uint8_t type); 69 | 70 | // ---------------------------------------------------------------------------- 71 | 72 | uint8_t mcp2515_init(uint8_t speed); 73 | 74 | // ---------------------------------------------------------------------------- 75 | // check if there are any new messages waiting 76 | uint8_t mcp2515_check_message(void); 77 | 78 | // ---------------------------------------------------------------------------- 79 | // check if there is a free buffer to send messages 80 | uint8_t mcp2515_check_free_buffer(void); 81 | 82 | // ---------------------------------------------------------------------------- 83 | uint8_t mcp2515_get_message(tCAN *message); 84 | 85 | // ---------------------------------------------------------------------------- 86 | uint8_t mcp2515_send_message(tCAN *message); 87 | 88 | 89 | #ifdef __cplusplus 90 | } 91 | #endif 92 | 93 | #endif // MCP2515_H 94 | -------------------------------------------------------------------------------- /CANBridge/Arduino Library/Canbus.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * Copyright (c) 2008-2009 All rights reserved. 5 | */ 6 | 7 | #if ARDUINO>=100 8 | #include // Arduino 1.0 9 | #else 10 | #include // Arduino 0022 11 | #endif 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "pins_arduino.h" 20 | #include 21 | #include "global.h" 22 | #include "mcp2515.h" 23 | #include "defaults.h" 24 | #include "Canbus.h" 25 | 26 | 27 | 28 | 29 | /* C++ wrapper */ 30 | CanbusClass::CanbusClass() { 31 | 32 | 33 | } 34 | 35 | unsigned int CanbusClass::message_rx(unsigned char *buffer) { 36 | tCAN message; 37 | 38 | if (mcp2515_check_message()) { 39 | 40 | 41 | // Read the message from the buffer the MCP2515 42 | if (mcp2515_get_message(&message)) { 43 | buffer[0] = message.data[0]; 44 | buffer[1] = message.data[1]; 45 | buffer[2] = message.data[2]; 46 | buffer[3] = message.data[3]; 47 | buffer[4] = message.data[4]; 48 | buffer[5] = message.data[5]; 49 | buffer[6] = message.data[6]; 50 | buffer[7] = message.data[7]; 51 | return message.id; 52 | //Serial.println("Got Something"); 53 | } 54 | else { 55 | //Serial.println("Can not read the message"); 56 | } 57 | } 58 | return 0; 59 | } 60 | 61 | bool CanbusClass::raw_message_rx(uint16_t &id, int8_t &rtr, uint8_t &length, unsigned char *raw_buffer, unsigned char *buffer) { 62 | tCAN message; 63 | 64 | if (mcp2515_check_message()) { 65 | // Read the message from the buffer the MCP2515 66 | if (mcp2515_get_message(&message)) { 67 | id = message.id; 68 | rtr = message.header.rtr; 69 | length = message.header.length; 70 | 71 | buffer[0] = message.data[0]; 72 | buffer[1] = message.data[1]; 73 | buffer[2] = message.data[2]; 74 | buffer[3] = message.data[3]; 75 | buffer[4] = message.data[4]; 76 | buffer[5] = message.data[5]; 77 | buffer[6] = message.data[6]; 78 | buffer[7] = message.data[7]; 79 | 80 | raw_buffer[0] = message.raw_data[0]; 81 | raw_buffer[1] = message.raw_data[1]; 82 | raw_buffer[2] = message.raw_data[2]; 83 | raw_buffer[3] = message.raw_data[3]; 84 | raw_buffer[4] = message.raw_data[4]; 85 | raw_buffer[5] = message.raw_data[5]; 86 | raw_buffer[6] = message.raw_data[6]; 87 | raw_buffer[7] = message.raw_data[7]; 88 | raw_buffer[8] = message.raw_data[8]; 89 | raw_buffer[9] = message.raw_data[9]; 90 | raw_buffer[10] = message.raw_data[10]; 91 | raw_buffer[11] = message.raw_data[11]; 92 | raw_buffer[12] = message.raw_data[12]; 93 | return true; 94 | } 95 | } 96 | return false; 97 | } 98 | 99 | char CanbusClass::message_tx(unsigned int ID, unsigned char *buffer) { 100 | tCAN message; 101 | 102 | message.id = ID; 103 | message.header.rtr = 0; 104 | message.header.length = 8; 105 | message.data[0] = buffer[0]; 106 | message.data[1] = buffer[1]; 107 | message.data[2] = buffer[2]; 108 | message.data[3] = buffer[3]; 109 | message.data[4] = buffer[4]; 110 | message.data[5] = buffer[5]; 111 | message.data[6] = buffer[6]; 112 | message.data[7] = buffer[7]; 113 | 114 | mcp2515_bit_modify(CANCTRL, (1< 30 | #include 31 | 32 | #if ARDUINO>=100 33 | #include // Arduino 1.0 34 | #else 35 | #include // Arduino 0022 36 | #endif 37 | #include 38 | #include 39 | 40 | #include "global.h" 41 | #include "mcp2515.h" 42 | #include "mcp2515_defs.h" 43 | 44 | 45 | #include "defaults.h" 46 | 47 | // ------------------------------------------------------------------------- 48 | // Schreibt/liest ein Byte ueber den Hardware SPI Bus 49 | 50 | uint8_t spi_putc( uint8_t data ) 51 | { 52 | // put byte in send-buffer 53 | SPDR = data; 54 | 55 | // wait until byte was send 56 | while( !( SPSR & (1< is the chip accessible? 175 | if (mcp2515_read_register(CNF1) != speed) { 176 | SET(LED2_HIGH); 177 | 178 | return false; 179 | } 180 | 181 | // deaktivate the RXnBF Pins (High Impedance State) 182 | mcp2515_write_register(BFPCTRL, 0x0F); 183 | 184 | // set TXnRTS as inputs 185 | mcp2515_write_register(TXRTSCTRL, 0); 186 | 187 | // turn off filters => receive any message 188 | mcp2515_write_register(RXB0CTRL, (1<raw_data[0] = spi_putc(0xff); 244 | message->raw_data[1] = spi_putc(0xff); 245 | message->raw_data[2] = spi_putc(0xff); 246 | message->raw_data[3] = spi_putc(0xff); 247 | message->raw_data[4] = spi_putc(0xff); 248 | 249 | message->id = (uint16_t)message->raw_data[0] << 3; 250 | message->id |= message->raw_data[1] >> 5; 251 | 252 | // read DLC 253 | uint8_t length = message->raw_data[4] & 0x0f; 254 | 255 | message->header.length = length; 256 | message->header.rtr = (bit_is_set(status, 3)) ? 1 : 0; 257 | 258 | // read data 259 | for (t=0;tdata[t] = spi_putc(0xff); 261 | message->raw_data[t + 5] = message->data[t]; 262 | } 263 | SET(MCP2515_CS); 264 | 265 | // clear interrupt flag 266 | if (bit_is_set(status, 6)) { 267 | mcp2515_bit_modify(CANINTF, (1< could not send message 301 | return 0; 302 | } 303 | 304 | RESET(MCP2515_CS); 305 | spi_putc(SPI_WRITE_TX | address); 306 | 307 | spi_putc(message->id >> 3); 308 | spi_putc(message->id << 5); 309 | 310 | spi_putc(0); 311 | spi_putc(0); 312 | 313 | uint8_t length = message->header.length & 0x0f; 314 | 315 | if (message->header.rtr) { 316 | // a rtr-frame has a length, but contains no data 317 | spi_putc((1<data[t]); 326 | } 327 | } 328 | SET(MCP2515_CS); 329 | 330 | _delay_us(1); 331 | 332 | // send message 333 | RESET(MCP2515_CS); 334 | address = (address == 0) ? 1 : address; 335 | spi_putc(SPI_RTS | address); 336 | SET(MCP2515_CS); 337 | 338 | return address; 339 | } 340 | -------------------------------------------------------------------------------- /CanBusHacker.py: -------------------------------------------------------------------------------- 1 | from PySide.QtCore import * 2 | from PySide.QtGui import * 3 | from PySide.QtSql import * 4 | import time 5 | import os 6 | import re 7 | import sys 8 | import time 9 | import random 10 | import sqlite3 11 | import pprint 12 | 13 | import serial 14 | if os.name == 'nt': #sys.platform == 'win32': 15 | from serial.tools.list_ports_windows import * 16 | elif os.name == 'posix': 17 | from serial.tools.list_ports_posix import * 18 | 19 | class CanLogReader(QThread): 20 | canMessageSignal=Signal(list) 21 | EmulationMode=False 22 | def __init__(self,log_db,emulation_mode=False): 23 | QThread.__init__(self) 24 | self.EmulationMode=emulation_mode 25 | 26 | def run(self): 27 | Conn = sqlite3.connect(log_db) 28 | Cursor = Conn.cursor() 29 | 30 | last_packet_time=0 31 | for (packet_time,packet_id,payload) in Cursor.execute('SELECT Time,PacketID,Payload FROM Packets ORDER BY ID ASC'): 32 | self.canMessageSignal.emit((packet_time,packet_id,payload)) 33 | 34 | if self.EmulationMode: 35 | time_interval=packet_time-last_packet_time 36 | if last_packet_time!=0: 37 | time.sleep(time_interval) 38 | last_packet_time=packet_time 39 | 40 | class CanPacketReader(QThread): 41 | canMessageSignal=Signal(list) 42 | Debug=1 43 | def __init__(self,com,log_db=''): 44 | QThread.__init__(self) 45 | self.COM=com 46 | self.LogDB=log_db 47 | 48 | def run(self): 49 | try: 50 | Serial=serial.Serial(self.COM, baudrate=115200) 51 | except: 52 | import traceback 53 | traceback.print_exc() 54 | Serial=None 55 | 56 | if self.LogDB: 57 | if Serial is not None: 58 | Conn = sqlite3.connect(self.LogDB) 59 | Cursor = Conn.cursor() 60 | 61 | try: 62 | Cursor.execute('''CREATE TABLE Packets(ID INTEGER PRIMARY KEY, Time DATETIME, PacketID INTEGER, Payload VARCHAR(15))''') 63 | except: 64 | pass 65 | 66 | if self.Debug>0: 67 | fd=None 68 | try: 69 | fd=open("raw_packets.log","wb") 70 | except: 71 | import traceback 72 | traceback.print_exc() 73 | 74 | CANMessagePattern=re.compile('CAN Message: \[(.*)\] ([^ ]+) ([^\r\n]+)') 75 | while Serial is not None: 76 | message=Serial.readline() 77 | 78 | if self.Debug>0: 79 | if fd is not None: 80 | try: 81 | fd.write(messsage) 82 | except: 83 | pass 84 | 85 | m=CANMessagePattern.match(message) 86 | if m!=None: 87 | current_time=time.time() 88 | 89 | try: 90 | id=m.group(1) 91 | length=int(m.group(2)) 92 | bytes=m.group(3)[0:length*3] 93 | 94 | if self.LogDB: 95 | Cursor.execute('''INSERT INTO Packets(Time, PacketID, Payload) VALUES(?,?,?)''', (current_time, id, bytes)) 96 | Conn.commit() 97 | 98 | self.canMessageSignal.emit((current_time,id,bytes)) 99 | 100 | except: 101 | import traceback 102 | if self.Debug>0: 103 | if fd is not None: 104 | fd.write("Exception:" + traceback.format_exc()) 105 | else: 106 | traceback.print_exc() 107 | 108 | class PacketTable(QAbstractTableModel): 109 | def __init__(self,parent, *args): 110 | QAbstractTableModel.__init__(self,parent,*args) 111 | self.PacketList=[] 112 | self.BufferedPacketList=[] 113 | self.LastIndex=None 114 | self.LastDataChangedEmitTime=time.time() 115 | 116 | def rowCount(self,parent): 117 | return len(self.PacketList) 118 | 119 | def columnCount(self,parent): 120 | return 3 121 | 122 | def data(self,index,role): 123 | if not index.isValid(): 124 | return None 125 | elif role!=Qt.DisplayRole: 126 | return None 127 | 128 | self.LastIndex=index 129 | 130 | if index.column()==0: 131 | return time.ctime(self.PacketList[index.row()][index.column()]) 132 | else: 133 | return str(self.PacketList[index.row()][index.column()]) 134 | 135 | def headerData(self,col,orientation,role): 136 | if orientation==Qt.Horizontal and role==Qt.DisplayRole: 137 | return ["Time","Id","Payload"][col] 138 | return None 139 | 140 | def addPacket(self,packet): 141 | self.BufferedPacketList.append(packet) 142 | 143 | cur_time=time.time() 144 | 145 | if cur_time-self.LastDataChangedEmitTime>0.1: 146 | start_row=len(self.PacketList) 147 | end_row=start_row+len(self.BufferedPacketList)-1 148 | 149 | self.beginInsertRows(QModelIndex(), start_row, end_row) 150 | self.PacketList.extend(self.BufferedPacketList) 151 | self.endInsertRows() 152 | 153 | self.dataChanged.emit(start_row,end_row) 154 | self.LastDataChangedEmitTime=cur_time 155 | self.BufferedPacketList=[] 156 | 157 | def addPackets(self,packets): 158 | self.beginRemoveRows(QModelIndex(), 0, len(self.PacketList)-1) 159 | self.PacketList=[] 160 | self.endRemoveRows() 161 | 162 | start_row=0 163 | end_row=start_row+len(packets)-1 164 | self.beginInsertRows(QModelIndex(), start_row, end_row) 165 | self.PacketList=packets 166 | self.endInsertRows() 167 | 168 | self.dataChanged.emit(start_row,end_row) 169 | 170 | class TreeItem(object): 171 | def __init__(self,data,parent=None,assoc_data=None): 172 | self.parentItem=parent 173 | self.itemData=data 174 | self.childItems=[] 175 | self.assocData=assoc_data 176 | 177 | def appendChild(self,item): 178 | self.childItems.append(item) 179 | 180 | def child(self,row): 181 | return self.childItems[row] 182 | 183 | def childCount(self): 184 | return len(self.childItems) 185 | 186 | def children(self): 187 | return self.childItems 188 | 189 | def columnCount(self): 190 | return len(self.itemData) 191 | 192 | def data(self,column): 193 | try: 194 | return self.itemData[column] 195 | except: 196 | import traceback 197 | traceback.print_exc() 198 | 199 | def parent(self): 200 | return self.parentItem 201 | 202 | def row(self): 203 | if self.parentItem: 204 | return parentItem.childItems.index(self) 205 | 206 | return 0 207 | 208 | def setAssocData(self,data): 209 | self.assocData=data 210 | 211 | def getAssocData(self): 212 | return self.assocData 213 | 214 | class TreeModel(QAbstractItemModel): 215 | def __init__(self,root_item,parent=None): 216 | super(TreeModel,self).__init__(parent) 217 | self.rootItem=TreeItem(root_item) 218 | self.ID2Item={} 219 | self.BufferedMap={} 220 | self.LastDataChangedEmitTime=time.time() 221 | 222 | def columnCount(self,parent): 223 | if parent.isValid(): 224 | return parent.internalPointer().columnCount() 225 | else: 226 | return self.rootItem.columnCount() 227 | 228 | def rowCount(self,parent): 229 | if parent.column()>0: 230 | return 0 231 | 232 | if not parent.isValid(): 233 | parentItem=self.rootItem 234 | else: 235 | parentItem=parent.internalPointer() 236 | 237 | return parentItem.childCount() 238 | 239 | def data(self,index,role): 240 | if not index.isValid(): 241 | return None 242 | 243 | if role==Qt.DisplayRole: 244 | item=index.internalPointer() 245 | return item.data(index.column()) 246 | 247 | return None 248 | 249 | def flags(self,index): 250 | if not index.isValid(): 251 | return Qt.NoItemFlags 252 | 253 | return Qt.ItemIsEnabled|Qt.ItemIsSelectable 254 | 255 | def index(self,row,column,parent): 256 | if not self.hasIndex(row,column,parent): 257 | return QModelIndex() 258 | 259 | if not parent.isValid(): 260 | parentItem=self.rootItem 261 | else: 262 | parentItem=parent.internalPointer() 263 | 264 | childItem=parentItem.child(row) 265 | if childItem: 266 | return self.createIndex(row,column,childItem) 267 | else: 268 | return QModelIndex() 269 | 270 | def parent(self,index): 271 | if not index.isValid(): 272 | return QModelindex() 273 | 274 | childItem=index.internalPointer() 275 | parentItem=childItem.parent() 276 | 277 | if parentItem is not None: 278 | if parentItem==self.rootItem: 279 | return QModelIndex() 280 | 281 | return self.createIndex(parentItem.row(),0,parentItem) 282 | return QModelIndex() 283 | 284 | def headerData(self,section,orientation,role): 285 | if orientation==Qt.Horizontal and role==Qt.DisplayRole: 286 | return self.rootItem.data(section) 287 | 288 | return None 289 | 290 | def addIDData(self,id,count): 291 | self.BufferedMap[id]=count 292 | 293 | cur_time=time.time() 294 | if cur_time-self.LastDataChangedEmitTime>0.1: 295 | start_row=0 296 | end_row=0 297 | for (id,count) in self.BufferedMap.items(): 298 | insert_row=0 299 | if self.ID2Item.has_key(id): 300 | tree_item=self.ID2Item[id] 301 | tree_item.itemData[1]="%d" % count 302 | else: 303 | self.beginInsertRows(QModelIndex(), start_row, end_row) 304 | tree_item=TreeItem([str(id),"%d" % count],assoc_data=id) 305 | self.ID2Item[id]=tree_item 306 | self.rootItem.appendChild(tree_item) 307 | self.endInsertRows() 308 | start_row=end_row 309 | end_row+=1 310 | self.dataChanged.emit(start_row,end_row) 311 | self.LastDataChangedEmitTime=cur_time 312 | self.BufferedMap={} 313 | 314 | def getAssocData(self,index): 315 | if not index.isValid(): 316 | return None 317 | item=index.internalPointer() 318 | return item.getAssocData() 319 | 320 | class StartCaptureDlg(QDialog): 321 | def __init__(self,parent=None,default_log_db=''): 322 | super(StartCaptureDlg,self).__init__(parent) 323 | self.setWindowTitle("Start Capture") 324 | 325 | self.Index2Port={} 326 | self.comboBox=QComboBox(self) 327 | i=0 328 | for port, desc, hwid in comports(): 329 | name= '%s - %s' % (port,desc) 330 | self.comboBox.addItem(name) 331 | if os.name == 'nt': 332 | self.Index2Port[i]='\\\\.\\'+port 333 | else: 334 | self.Index2Port[i]=port 335 | i+=1 336 | 337 | log_db_button=QPushButton('Output Log:',self) 338 | log_db_button.clicked.connect(self.askLogDB) 339 | self.log_db_line=QLineEdit("") 340 | self.log_db_line.setAlignment(Qt.AlignLeft) 341 | self.log_db_line.setMinimumWidth(250) 342 | self.log_db_line.setText(default_log_db) 343 | 344 | buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) 345 | buttonBox.accepted.connect(self.accept) 346 | buttonBox.rejected.connect(self.reject) 347 | 348 | main_layout=QGridLayout() 349 | main_layout.addWidget(self.comboBox,0,0,1,2) 350 | main_layout.addWidget(log_db_button,1,0) 351 | main_layout.addWidget(self.log_db_line,1,1) 352 | main_layout.addWidget(buttonBox,2,0,1,2,Qt.AlignCenter) 353 | 354 | self.setLayout(main_layout) 355 | 356 | def askLogDB(self): 357 | log_db=QFileDialog.getSaveFileName(self,'Save File', 358 | "", 359 | 'DB (*.db *.*)')[0] 360 | self.log_db_line.setText(log_db) 361 | 362 | def getLogDB(self): 363 | return self.log_db_line.text() 364 | 365 | def getCOMPort(self): 366 | return self.Index2Port[self.comboBox.currentIndex()] 367 | 368 | 369 | class MainWindow(QMainWindow): 370 | DebugPacketLoad=0 371 | def __init__(self,com='',log_db='',emulation_mode=False): 372 | super(MainWindow,self).__init__() 373 | self.setWindowTitle("CanBusHacker") 374 | 375 | self.COM=com 376 | self.LogDB=log_db 377 | self.EmulationMode=emulation_mode 378 | 379 | vertical_splitter=QSplitter() 380 | vertical_splitter.setOrientation(Qt.Vertical) 381 | horizontal_splitter=QSplitter() 382 | self.idTreeView=QTreeView() 383 | self.idTreeModel=TreeModel(("ID","Count")) 384 | self.idTreeView.setModel(self.idTreeModel) 385 | self.idTreeView.connect(self.idTreeView.selectionModel(),SIGNAL("selectionChanged(QItemSelection, QItemSelection)"), self.idTreeSelected) 386 | horizontal_splitter.addWidget(self.idTreeView) 387 | 388 | self.PacketTableView=QTableView() 389 | vheader=QHeaderView(Qt.Orientation.Vertical) 390 | #vheader.setResizeMode(QHeaderView.ResizeToContents) 391 | self.PacketTableView.setVerticalHeader(vheader) 392 | self.PacketTableView.horizontalHeader().setResizeMode(QHeaderView.Stretch) 393 | self.PacketTableView.setSortingEnabled(True) 394 | self.PacketTableView.setSelectionBehavior(QAbstractItemView.SelectRows) 395 | self.PacketTableModel=PacketTable(self) 396 | self.PacketTableView.setModel(self.PacketTableModel) 397 | 398 | self.CurrentPacketTableView=QTableView() 399 | vheader=QHeaderView(Qt.Orientation.Vertical) 400 | self.CurrentPacketTableView.setVerticalHeader(vheader) 401 | self.CurrentPacketTableView.horizontalHeader().setResizeMode(QHeaderView.Stretch) 402 | self.CurrentPacketTableView.setSortingEnabled(True) 403 | self.CurrentPacketTableView.setSelectionBehavior(QAbstractItemView.SelectRows) 404 | self.CurrentPacketTableModel=PacketTable(self) 405 | self.CurrentPacketTableView.setModel(self.CurrentPacketTableModel) 406 | 407 | self.rightTabWidget=QTabWidget() 408 | self.rightTabWidget.addTab(self.PacketTableView,"All Packets") 409 | self.rightTabWidget.addTab(self.CurrentPacketTableView,"Current Packets") 410 | horizontal_splitter.addWidget(self.rightTabWidget) 411 | horizontal_splitter.setStretchFactor(0,0) 412 | horizontal_splitter.setStretchFactor(1,1) 413 | 414 | vertical_splitter.addWidget(horizontal_splitter) 415 | self.logWidget=QTextEdit() 416 | vertical_splitter.addWidget(self.logWidget) 417 | vertical_splitter.setStretchFactor(0,1) 418 | vertical_splitter.setStretchFactor(1,0) 419 | 420 | if self.DebugPacketLoad>0: 421 | Conn = sqlite3.connect(log_db) 422 | Cursor = Conn.cursor() 423 | 424 | for (time,packet_id,payload) in Cursor.execute('SELECT Time,PacketID,Payload FROM Packets ORDER BY ID ASC'): 425 | self.PacketTableModel.addPacket((time,packet_id,payload)) 426 | self.PacketTableView.setModel(self.PacketTableModel) 427 | 428 | main_widget=QWidget() 429 | vlayout=QVBoxLayout() 430 | vlayout.addWidget(vertical_splitter) 431 | main_widget.setLayout(vlayout) 432 | self.setCentralWidget(main_widget) 433 | 434 | self.createMenus() 435 | 436 | self.show() 437 | 438 | self.IDCountMap={} 439 | self.ID2Packets={} 440 | self.TimeMap={} 441 | 442 | def openLog(self): 443 | self.LogDB=QFileDialog.getOpenFileName(self, 444 | "Open Log DB", 445 | "", 446 | "DB Files (*.db)|All Files (*.*)")[0] 447 | if self.LogDB: 448 | self.can_log_reader=CanLogReader(self.LogDB,self.EmulationMode) 449 | self.can_log_reader.canMessageSignal.connect(self.getCanMessage) 450 | self.can_log_reader.start() 451 | 452 | def startCapture(self): 453 | start_capture_dlg=StartCaptureDlg(default_log_db="log.db") 454 | if start_capture_dlg.exec_(): 455 | self.COM=start_capture_dlg.getCOMPort() 456 | log_db=start_capture_dlg.getLogDB() 457 | if self.COM: 458 | self.can_packet_reader=CanPacketReader(self.COM,log_db) 459 | self.can_packet_reader.canMessageSignal.connect(self.getCanMessage) 460 | self.can_packet_reader.start() 461 | 462 | def stopCapture(self): 463 | pass 464 | 465 | def createMenus(self): 466 | self.fileMenu=self.menuBar().addMenu("&File") 467 | self.openAct=QAction("&Open Log...", 468 | self, 469 | triggered=self.openLog) 470 | self.fileMenu.addAction(self.openAct) 471 | 472 | self.arduinoMenu=self.menuBar().addMenu("&Arduino") 473 | self.startCaptureAct=QAction("&Start Capture", 474 | self, 475 | triggered=self.startCapture) 476 | self.arduinoMenu.addAction(self.startCaptureAct) 477 | self.stopCaptureAct=QAction("&Start Capture", 478 | self, 479 | triggered=self.stopCapture) 480 | self.arduinoMenu.addAction(self.stopCaptureAct) 481 | 482 | def idTreeSelected(self,newSelection,oldSelection): 483 | for index in newSelection.indexes(): 484 | id=self.idTreeModel.getAssocData(index) 485 | #pprint.pprint(self.ID2Packets[id]) 486 | self.rightTabWidget.setCurrentIndex(1) 487 | self.CurrentPacketTableModel.addPackets(self.ID2Packets[id]) 488 | 489 | def getCanMessage(self,(current_time,id,bytes)): 490 | self.PacketTableModel.addPacket((current_time,id,bytes)) 491 | self.PacketTableView.scrollToBottom() 492 | 493 | if not self.IDCountMap.has_key(id): 494 | self.IDCountMap[id]=1 495 | else: 496 | self.IDCountMap[id]+=1 497 | 498 | if not self.ID2Packets.has_key(id): 499 | self.ID2Packets[id]=[] 500 | 501 | self.ID2Packets[id].append([current_time,id,bytes]) 502 | 503 | if self.TimeMap.has_key(id): 504 | elapsed_time=current_time - self.TimeMap[id] 505 | else: 506 | elapsed_time=0 507 | self.TimeMap[id]=current_time 508 | 509 | self.idTreeModel.addIDData(id,self.IDCountMap[id]) 510 | 511 | if __name__=='__main__': 512 | import sys 513 | 514 | com='' #TODO: 515 | log_db=r'SampleLogs\log.db' 516 | emulation_mode=False 517 | 518 | app=QApplication(sys.argv) 519 | app.processEvents() 520 | window=MainWindow(com,log_db,emulation_mode) 521 | window.show() 522 | sys.exit(app.exec_()) --------------------------------------------------------------------------------