├── README.md
└── shiftlight_with_CAN-BUS_for_MQB.ino
/README.md:
--------------------------------------------------------------------------------
1 | # MQB-sniffer
2 | CAN-BUS sniffing for Volkswagen Golf MK7 (and most likely other MQB platform cars).
3 | Currently this is more a work log for future reference.
4 |
5 | ## Hardware used (not everything is required) ##
6 | * Car: VW Golf MK7, 2016, 2.0TDI (110kW, CRLB) + DSG (DQ250)
7 | * OBDeleven (GEN2) OBD2 adapter + iOS device + OBDeleven account with PRO license
8 | * ELM327 v1.5 (Carista)
9 | * MCP2515 + Arduino Nano + bunch of cables + PC/Mac/Linux
10 | * OBD2 cable splitter (to connect two OBD2 devices at the same time to the car)
11 |
12 | ## Sniffing ##
13 | ### Cables and adapters ###
14 | * OBD2 Splitter connected to the car
15 | * OBDeleven connected to one of the ports
16 | * Carista ELM327 connected to the other port
17 | * iOS device connected with OBDeleven adapter
18 | * Mac/PC/Linux connected with Carsita ELM327 dongle
19 | * serial monitor app running on Mac/PC, Carista ELM327 serial port connected at high baudrate (115200bps)
20 |
21 | ### ELM327 config ###
22 | Commands to send to Carista ELM327 to prepare it for data sniffing:
23 | * `at z` - reset ELM327 chip
24 | * `at d` - defaults
25 | * `at sp 6` - set CAN-BUS to 11bit, 500kbps (Golf MK7 is using ISO 15765-4 CAN, 11 bit ID, 500 kbaud standard)
26 | * `at cfc1` - flowcontrol
27 | * `at d1` - show DLC
28 | * `at al` - enable long messages
29 | * `at h1` - show headers
30 | * `at caf0` - disable formating
31 | * `at l0` - disable linefeeds
32 | * `at s1` - enable whitespaces (easier to view data)
33 |
34 | ### Data gathering ###
35 | * `at ma` - start sniffing
36 | * At the same time use OBDeleven to connect with car, go to list of modules, select module, select live data, select data type, view.
37 | * Carista ELM327 will sniff the communication between OBDeleven and car.
38 |
39 | ## Injecting ##
40 | ### ELM327 config ###
41 | * `at z` - reset ELM327 chip
42 | * `at sp 6` - set CAN-BUS to 11bit, 500kbps
43 | * `at al` - enable long messages
44 | * `at caf0` - disable formating
45 | * `at v1` - enable frames shorter than 8 bytes (no padding)
46 | * `at bi` - bypass the initiation step
47 |
48 | ### Sending frame ###
49 | * `at sh 777` - set frame header to 777
50 | * `11 22 33 44 55` - send 0x11 0x22 0x33 0x44 0x55
51 | This will send this frame:
52 | `Standard ID: 0x777 DLC: 5 Data: 0x11 0x22 0x33 0x44 0x55`
53 |
54 |
55 | ## Request headers and modules ##
56 | * `714` - instrument cluster (module 0x17)
57 | * `7E1` - gearbox (module 0x02)
58 | * More to check: https://github.com/commaai/openpilot/issues/1238#issuecomment-602155568
59 |
60 | ## Example data sniffed from OBDeleven-Car communication ##
61 | ### Starting and maintaining communication with instrument cluster ###
62 | `714 8 02 10 03 55 55 55 55 55` - Accessing instrument cluster
63 | `77E 8 06 50 03 00 32 00 C8 AA` - Cluster reply
64 |
65 | `714 8 03 19 02 AE 55 55 55 55`
66 | `77E 8 03 59 02 19 AA AA AA AA`
67 |
68 | `714 8 02 3E 00 55 55 55 55 55` - I'm here, don't sleep
69 | `77E 8 02 7E 00 AA AA AA AA AA` - not sleeping!
70 |
71 | `714 8 02 3E 00 55 55 55 55 55`
72 | `77E 8 02 7E 00 AA AA AA AA AA`
73 | `...`
74 |
75 | ### RPM from instrument cluster ###
76 | `714 8 03 22 22 D1 55 55 55 55` - request
77 | `77E 8 05 62 22 D1 1F 98 AA AA` - reply (0x1F98 = 8088 DEC; 8808 / 4 = 2022RPM)
78 |
79 | ### Phototransistor instrument cluster (ambient light sensor) ###
80 | `714 8 03 22 22 4D 55 55 55 55` - request
81 | `77E 8 04 62 22 4D FA AA AA AA` - reply (0xFA = 250 DEC; 250 out of 0-255 range; higher=brighter)
82 |
83 | ### Enagaged gear from DSG ###
84 | `7E1 8 03 22 38 16 55 55 55 55` - request
85 | `7E9 8 04 62 38 16 00 AA AA AA` - gear not engaged
86 | `7E9 8 04 62 38 16 0C AA AA AA` - reverse
87 | `7E9 8 04 62 38 16 02 AA AA AA` - 1st
88 |
89 | ### Gearbox mode from DSG ###
90 | `7E1 8 03 22 38 15 55 55 55 55` - request
91 | `7E9 8 04 62 38 15 00 AA AA AA` - P
92 | `7E9 8 04 62 38 15 01 AA AA AA` - R
93 | `7E9 8 04 62 38 15 02 AA AA AA` - N
94 | `7E9 8 04 62 38 15 03 AA AA AA` - D
95 | `7E9 8 04 62 38 15 04 AA AA AA` - S
96 | `7E9 8 04 62 38 15 05 AA AA AA` - M
97 |
98 | ## Warnings ##
99 | * Cheap ELM327 adapters will not work. Buffer will overflow with date before you can read the data from it.
100 | * For some reasons, when trying to use MCP2515 to sniff, experiencing communication problems between OBDeleven and the car. Tried both with and without EOL termination resistor with no luck.
101 |
102 | ## Shopping list ##
103 | * OBDeleven adapter - http://mr-fix.info/redirect/obdeleven
104 | * Carista adapter
105 | * Arduino Nano
106 | * MCP2515
107 | * OBD2 splitter
108 | * WS2818 LED panel
109 |
--------------------------------------------------------------------------------
/shiftlight_with_CAN-BUS_for_MQB.ino:
--------------------------------------------------------------------------------
1 | /* Shiftlight with CAN-BUS for MQB */
2 | /* Board: Arduino Nano
3 | * Processor: ATmega328P (Old Bootloader)
4 | * Port: /dev/cu.usbserial-1410
5 | */
6 |
7 |
8 | #include
9 | #include
10 | #include
11 | #ifdef __AVR__
12 | #include // Required for 16 MHz Adafruit Trinket
13 | #endif
14 |
15 | //pin config
16 | #define PINPIXELS 3 // pin D3 to data-in WS2812 module
17 | #define NUMPIXELS 8 // number of LEDs in WS2812 module
18 | #define PINBUZZER 4 // pin D2 to pizo speaker
19 | #define GNDBUZZER 5 // ground for pizo speaker
20 | #define CAN0_INT 2 // MCP2515 INT to pin 2
21 |
22 | //MCP2515 config
23 | MCP_CAN CAN0(10); // MCP2515 CS to pin 10
24 | long unsigned int rxId;
25 | unsigned char len = 0;
26 | unsigned char rxBuf[8];
27 | char msgString[128];
28 |
29 | //initial config
30 | Adafruit_NeoPixel pixels(NUMPIXELS, PINPIXELS, NEO_GRB + NEO_KHZ800);
31 | unsigned int initialBrightness = 15; // 0-255
32 | unsigned int currentBrightness = initialBrightness;
33 |
34 | unsigned int currentFrequency = 500;
35 |
36 | //user config
37 | unsigned int currentRPM;
38 | unsigned int maxRPM = 8000;
39 | unsigned int perfectRPM = 6500;
40 | unsigned int welcomeMode = 2;
41 | bool mute = true;
42 |
43 | unsigned long nextLoopTime=0;
44 |
45 | //run once
46 | void setup() {
47 | //Serial communication setup
48 | Serial.begin(115200);
49 |
50 | //First data output over serial
51 | printInitialDetails();
52 |
53 | ///MCP2515 setup
54 | mcpSetup();
55 |
56 | //WS2812 setup
57 | pixels.begin();
58 | pixels.clear();
59 | pixels.show();
60 | pixels.setBrightness(currentBrightness);
61 |
62 | //Buzzer setup
63 | pinMode(PINBUZZER, OUTPUT);
64 | pinMode(GNDBUZZER, OUTPUT);
65 | digitalWrite(GNDBUZZER, LOW);
66 |
67 | //Run initial animation
68 | welcomeCeremony(welcomeMode);
69 | }
70 |
71 | //run all the time
72 | void loop() {
73 | mcpRead();
74 | //mcpSearch();
75 |
76 | currentRPM = getRPM(false); //false = simulation
77 |
78 | pixels.clear();
79 | pixels.setBrightness(currentBrightness);
80 |
81 | updateAlerts();
82 |
83 | pixels.show();
84 | }
85 |
86 | void welcomeCeremony(int type) {
87 | if(type==1){
88 | for (int i = 0; i < NUMPIXELS; i++) {
89 | pixels.setPixelColor(i, pixels.Color(255, 0, 0));
90 | pixels.show();
91 | tone(PINBUZZER, (currentFrequency*(i+1)));
92 | delay(25);
93 | noTone(PINBUZZER);
94 | delay(25);
95 | }
96 | }
97 |
98 |
99 | if(type==2){
100 | for (int i = 0; i < NUMPIXELS; i++) {
101 | pixels.clear();
102 | pixels.setPixelColor(i, pixels.Color(0, 0, 255));
103 | pixels.show();
104 | tone(PINBUZZER, (currentFrequency*(i+1)));
105 | delay(25);
106 | }
107 | for (int i=7; i>=0; i--) {
108 | pixels.clear();
109 | pixels.setPixelColor(i, pixels.Color(0, 0, 255));
110 | pixels.show();
111 | tone(PINBUZZER, (currentFrequency*(i+1)));
112 | delay(25);
113 | }
114 | noTone(PINBUZZER);
115 |
116 | }
117 | }
118 |
119 | int getRPM(bool real){
120 | if(real){
121 | //read RPM from CAN-BUS
122 | //TODO
123 | }else{
124 | //simulate RPMs for development and debug
125 | return millis()%maxRPM;
126 | }
127 | }
128 |
129 | void updateAlerts(){
130 | if(currentRPM<(perfectRPM-1000)){
131 | for (int i = 0; i < NUMPIXELS; i++) {
132 | pixels.setPixelColor(i, pixels.Color(0, 0, 0));
133 | }
134 | }
135 |
136 | if(currentRPM>=(perfectRPM-1000) && currentRPM<(perfectRPM-800)){
137 | pixels.setPixelColor(0, pixels.Color(0, 255, 0));
138 | pixels.setPixelColor(7, pixels.Color(0, 255, 0));
139 | }
140 |
141 | if(currentRPM>=(perfectRPM-800) && currentRPM<(perfectRPM-600)){
142 | pixels.setPixelColor(0, pixels.Color(0, 255, 0));
143 | pixels.setPixelColor(1, pixels.Color(0, 255, 0));
144 | pixels.setPixelColor(6, pixels.Color(0, 255, 0));
145 | pixels.setPixelColor(7, pixels.Color(0, 255, 0));
146 | }
147 |
148 | if(currentRPM>=(perfectRPM-600) && currentRPM<(perfectRPM-400)){
149 | pixels.setPixelColor(0, pixels.Color(0, 255, 0));
150 | pixels.setPixelColor(1, pixels.Color(0, 255, 0));
151 | pixels.setPixelColor(2, pixels.Color(0, 255, 0));
152 | pixels.setPixelColor(5, pixels.Color(0, 255, 0));
153 | pixels.setPixelColor(6, pixels.Color(0, 255, 0));
154 | pixels.setPixelColor(7, pixels.Color(0, 255, 0));
155 | }
156 |
157 | if(currentRPM>=(perfectRPM-400) && currentRPM<(perfectRPM-200)){
158 | for (int i = 0; i < NUMPIXELS; i++) {
159 | pixels.setPixelColor(i, pixels.Color(0, 255, 0));
160 | }
161 | }
162 |
163 | if(currentRPM>=(perfectRPM-200) && currentRPM<(perfectRPM+200)){
164 | for (int i = 0; i < NUMPIXELS; i++) {
165 | pixels.setPixelColor(i, pixels.Color(0, 0, 255));
166 | }
167 | }
168 |
169 | if(currentRPM>=(perfectRPM+200) && currentRPM<(perfectRPM+500)){
170 | for (int i = 0; i < NUMPIXELS; i++) {
171 | pixels.setPixelColor(i, pixels.Color(255, 0, 0));
172 | }
173 | }
174 |
175 | if(currentRPM>=(perfectRPM+500)){
176 | for (int i = 0; i < NUMPIXELS; i++) {
177 | pixels.setPixelColor(i, pixels.Color(255, 0, 0));
178 | }
179 | pixels.show();
180 | if(!mute){
181 | tone(PINBUZZER, currentFrequency*8);
182 | }
183 | delay(50);
184 | pixels.clear();
185 | pixels.show();
186 | noTone(PINBUZZER);
187 | delay(50);
188 | }
189 | }
190 |
191 | void mcpSetup(){
192 | if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_8MHZ) == CAN_OK){
193 | Serial.println("MCP2515 Initialized Successfully!");
194 | }else{
195 | Serial.println("Error Initializing MCP2515...");
196 | }
197 | CAN0.setMode(MCP_NORMAL); // Set operation mode to normal so the MCP2515 sends acks to received data.
198 | pinMode(CAN0_INT, INPUT); // Configuring pin for /INT input
199 | printSpacer();
200 | }
201 |
202 | void mcpRead(){
203 | if(!digitalRead(CAN0_INT)) // If CAN0_INT pin is low, read receive buffer
204 | {
205 | CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s)
206 |
207 | if((rxId & 0x80000000) == 0x80000000) // Determine if ID is standard (11 bits) or extended (29 bits)
208 | sprintf(msgString, "Extended ID: 0x%.8lX DLC: %1d Data:", (rxId & 0x1FFFFFFF), len);
209 | else
210 | sprintf(msgString, "Standard ID: 0x%.3lX DLC: %1d Data:", rxId, len);
211 |
212 | Serial.print(msgString);
213 |
214 | if((rxId & 0x40000000) == 0x40000000){ // Determine if message is a remote request frame.
215 | sprintf(msgString, " REMOTE REQUEST FRAME");
216 | Serial.print(msgString);
217 | } else {
218 | for(byte i = 0; i