A working demonstration on how to read GMLAN packets using an mbed and a compatible CAN transceiver such as (and tested with) the CAN-Bus demo board. The SparkFun CAN Shield should also work perfectly too, as should just about every MCP2551-based solution (but this code should be portable to other transceivers). Please note to get this to work, you must tie CAN_L to ground and connect CAN_H to the single wire CAN.

Dependencies:   mbed

GMLAN Sniffer

Introduction

This project makes use of CAN on an LPC1768 to communicate with General Motors GMLAN network via a compatible transceiver. GMLAN is a single wire CAN Bus that operates at 33.333 kbit with 29-bit header packets and is commonly used for transmission of non-critical information between such nodes on automobiles as the following;

  • Dash cluster
  • Entertainment system (head unit)
  • Satellite Navigation (OnStar for example)
  • HVAC controls
  • Immobiliser / alarm / other security systems
  • Door locks and door status
  • Power Windows
  • Interior / exterior lighting
  • Reversing sensors

The list is extensive, for a better idea of what can be done, view the ARBIDs tab on the GMLAN Bible Google Docs spreadsheet (tabs are at the bottom of the page).

Hardware setup

This is just one example of how you can interface with GMLAN using an SKPang CAN-Bus Breakout Board (based around an MCP2551 CAN Transceiver), using these instructions I assembled the following on my trusty prototyping board;

/media/uploads/foxdie/_scaled_mbed-canbus-layout.jpg

(Click to open high resolution version)

Committer:
foxdie
Date:
Mon Dec 10 22:24:49 2012 +0000
Revision:
8:cdd2bb40e72f
Parent:
7:8bc6a5d2ba1e
Switched back to monitor / sniffing mode, inadvertent change from a project running in parallel I copy/pasted from.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
foxdie 6:81bd5717b4ed 1 #include "mbed.h"
foxdie 6:81bd5717b4ed 2 #include <vector>
foxdie 6:81bd5717b4ed 3 #include <algorithm>
foxdie 6:81bd5717b4ed 4
foxdie 6:81bd5717b4ed 5 DigitalOut rx_led(LED1);
foxdie 6:81bd5717b4ed 6 DigitalOut filter_led(LED4);
foxdie 6:81bd5717b4ed 7 Serial pc(USBTX, USBRX);
foxdie 6:81bd5717b4ed 8 CAN gmlan(p30, p29);
foxdie 6:81bd5717b4ed 9 Timer uptime;
foxdie 6:81bd5717b4ed 10 bool filterPackets = false;
foxdie 6:81bd5717b4ed 11 vector<int> filteredPackets;
foxdie 6:81bd5717b4ed 12 extern "C" void mbed_reset();
foxdie 6:81bd5717b4ed 13
foxdie 6:81bd5717b4ed 14 namespace mbed {
foxdie 6:81bd5717b4ed 15 class CANHeader {
foxdie 6:81bd5717b4ed 16 // Example header packet for Steering Wheel Switches:
foxdie 6:81bd5717b4ed 17 // Hexadecimal: 0x10 0x0D 0x00 0x60
foxdie 6:81bd5717b4ed 18 // Binary: 00010000 00001101 00000000 01100000
foxdie 6:81bd5717b4ed 19 // Priority: ---
foxdie 6:81bd5717b4ed 20 // Arbitration: -- -------- ---
foxdie 6:81bd5717b4ed 21 // Sending ECU: ----- --------
foxdie 6:81bd5717b4ed 22
foxdie 6:81bd5717b4ed 23 private:
foxdie 6:81bd5717b4ed 24 int priorityID, arbitrationID, senderID;
foxdie 6:81bd5717b4ed 25
foxdie 6:81bd5717b4ed 26 public:
foxdie 6:81bd5717b4ed 27 int priority(void) { return priorityID; }
foxdie 6:81bd5717b4ed 28 void priority(int _priority) { priorityID = _priority; }
foxdie 6:81bd5717b4ed 29 int arbitration(void) { return arbitrationID; }
foxdie 6:81bd5717b4ed 30 void arbitration(int _arbitration) { arbitrationID = _arbitration; }
foxdie 6:81bd5717b4ed 31 int sender(void) { return senderID; }
foxdie 6:81bd5717b4ed 32 void sender(int _sender) { senderID = _sender; }
foxdie 6:81bd5717b4ed 33
foxdie 6:81bd5717b4ed 34 void decode(int _header) {
foxdie 6:81bd5717b4ed 35 priorityID = (_header >> 26) & 0x7;
foxdie 6:81bd5717b4ed 36 arbitrationID = (_header >> 13) & 0x1FFF;
foxdie 6:81bd5717b4ed 37 senderID = (_header >> 0) & 0x1FFF;
foxdie 6:81bd5717b4ed 38 }
foxdie 6:81bd5717b4ed 39 int encode(void) {
foxdie 6:81bd5717b4ed 40 long int buffer = 0;
foxdie 6:81bd5717b4ed 41 buffer = (buffer << 3) | 0x0; // 3 bit padding
foxdie 6:81bd5717b4ed 42 buffer = (buffer << 3) | priorityID;
foxdie 6:81bd5717b4ed 43 buffer = (buffer << 13) | arbitrationID;
foxdie 6:81bd5717b4ed 44 buffer = (buffer << 13) | senderID;
foxdie 6:81bd5717b4ed 45 return buffer;
foxdie 6:81bd5717b4ed 46 }
foxdie 6:81bd5717b4ed 47 };
foxdie 6:81bd5717b4ed 48 }
foxdie 6:81bd5717b4ed 49
foxdie 6:81bd5717b4ed 50 void processMessage()
foxdie 6:81bd5717b4ed 51 {
foxdie 6:81bd5717b4ed 52 // Turn on rx_led to indicate we are receiving data
foxdie 6:81bd5717b4ed 53 rx_led = 1;
foxdie 6:81bd5717b4ed 54
foxdie 6:81bd5717b4ed 55 // Create a CANMessage object and read the buffer into it
foxdie 6:81bd5717b4ed 56 CANMessage rxmsg;
foxdie 6:81bd5717b4ed 57 gmlan.read(rxmsg);
foxdie 6:81bd5717b4ed 58
foxdie 6:81bd5717b4ed 59 // Check to see if our header is in the filtered packet list or we are filtering, if so we skip over
foxdie 6:81bd5717b4ed 60 bool packetFound = false;
foxdie 6:81bd5717b4ed 61 if (find(filteredPackets.begin(), filteredPackets.end(), rxmsg.id) != filteredPackets.end())
foxdie 6:81bd5717b4ed 62 packetFound = true;
foxdie 6:81bd5717b4ed 63
foxdie 6:81bd5717b4ed 64 // If we are filtering packets and this one isn't found, add it to the list
foxdie 6:81bd5717b4ed 65 if (filterPackets == true && packetFound == false)
foxdie 6:81bd5717b4ed 66 {
foxdie 6:81bd5717b4ed 67 filteredPackets.push_back(rxmsg.id);
foxdie 6:81bd5717b4ed 68 pc.printf("Added 0x%08X to filter at position %u\r\n", rxmsg.id, filteredPackets.size());
foxdie 6:81bd5717b4ed 69 }
foxdie 6:81bd5717b4ed 70 else if (filterPackets == false && packetFound == false) {
foxdie 6:81bd5717b4ed 71 CANHeader rxHeader;
foxdie 6:81bd5717b4ed 72 rxHeader.decode(rxmsg.id);
foxdie 6:81bd5717b4ed 73
foxdie 6:81bd5717b4ed 74 // Print these results to the serial terminal using printf to format
foxdie 6:81bd5717b4ed 75 // Ref: http://www.cplusplus.com/reference/cstdio/printf/
foxdie 6:81bd5717b4ed 76 pc.printf("[%10.2f] ", uptime.read()); // Seconds (to 2 decimal places) since start
foxdie 6:81bd5717b4ed 77 pc.printf("[0x%1X] ", rxHeader.priority()); // Packet priority (0-7, 0 being highest)
foxdie 6:81bd5717b4ed 78 pc.printf("[0x%4X] ", rxHeader.arbitration()); // Arbitration ID (0x0 to 0x1FFF)
foxdie 6:81bd5717b4ed 79 pc.printf("[0x%4X] ", rxHeader.sender()); // ECU ID (0x0 to 0x1FFF)
foxdie 6:81bd5717b4ed 80 pc.printf("[0x%08X] ", rxmsg.id); // Full header for legacy reasons (4 bytes)
foxdie 6:81bd5717b4ed 81 pc.printf("[%d] ", rxmsg.len); // Length of message (0-8 bytes typically)
foxdie 6:81bd5717b4ed 82
foxdie 6:81bd5717b4ed 83 // Process actual message here, only run this if we have a message
foxdie 6:81bd5717b4ed 84 if (rxmsg.len > 0)
foxdie 6:81bd5717b4ed 85 {
foxdie 6:81bd5717b4ed 86 pc.printf("[%02X", rxmsg.data[0]); // Print first byte
foxdie 6:81bd5717b4ed 87 for (unsigned int i = 1; i < rxmsg.len; i++)
foxdie 6:81bd5717b4ed 88 pc.printf(" %02X", rxmsg.data[i]); // Print additional byte(s) with a preceeding space
foxdie 6:81bd5717b4ed 89 pc.printf("]"); // Print closing bracket
foxdie 6:81bd5717b4ed 90 }
foxdie 6:81bd5717b4ed 91 pc.printf("\r\n"); // Print carriage return and newline
foxdie 6:81bd5717b4ed 92 }
foxdie 6:81bd5717b4ed 93
foxdie 6:81bd5717b4ed 94 // Turn off our rx_led as we have finished reading data
foxdie 6:81bd5717b4ed 95 rx_led = 0;
foxdie 6:81bd5717b4ed 96 }
foxdie 6:81bd5717b4ed 97
foxdie 6:81bd5717b4ed 98 void clearAndHome()
foxdie 6:81bd5717b4ed 99 {
foxdie 6:81bd5717b4ed 100 pc.printf("%c", 27); // ESC
foxdie 6:81bd5717b4ed 101 pc.printf("[2J"); // clear screen
foxdie 6:81bd5717b4ed 102 pc.printf("%c", 27); // ESC
foxdie 6:81bd5717b4ed 103 pc.printf("[H"); // cursor to home
foxdie 6:81bd5717b4ed 104 }
foxdie 6:81bd5717b4ed 105
foxdie 6:81bd5717b4ed 106 int main() {
foxdie 6:81bd5717b4ed 107 // Set serial baud rate to 115kbit
foxdie 6:81bd5717b4ed 108 pc.baud(115200);
foxdie 6:81bd5717b4ed 109
foxdie 6:81bd5717b4ed 110 // Set CANBUS to 33.3kbit and put into monitor mode (does not ACK packets, aka stealth mode)
foxdie 6:81bd5717b4ed 111 int baudrate = 33333;
foxdie 6:81bd5717b4ed 112 gmlan.frequency(baudrate);
foxdie 8:cdd2bb40e72f 113 gmlan.monitor(true);
foxdie 6:81bd5717b4ed 114
foxdie 6:81bd5717b4ed 115 // Clear serial terminal
foxdie 6:81bd5717b4ed 116 clearAndHome();
foxdie 7:8bc6a5d2ba1e 117 pc.printf("Keys:\r\nF = start/stop filter capture\r\nD = display filtered headers\r\nC = clear filter\r\nR = restart mbed\r\n");
foxdie 6:81bd5717b4ed 118 pc.printf("Starting packet capture at %i bps\r\n\r\n", baudrate);
foxdie 6:81bd5717b4ed 119
foxdie 6:81bd5717b4ed 120 // Start capturing packets
foxdie 6:81bd5717b4ed 121 uptime.start();
foxdie 6:81bd5717b4ed 122 gmlan.attach(&processMessage);
foxdie 6:81bd5717b4ed 123
foxdie 6:81bd5717b4ed 124 while(1) {
foxdie 6:81bd5717b4ed 125 // Poll serial for keypresses for certain tasks
foxdie 6:81bd5717b4ed 126 if (pc.readable())
foxdie 6:81bd5717b4ed 127 {
foxdie 6:81bd5717b4ed 128 switch (pc.getc())
foxdie 6:81bd5717b4ed 129 {
foxdie 6:81bd5717b4ed 130 case 'f':
foxdie 6:81bd5717b4ed 131 case 'F':
foxdie 6:81bd5717b4ed 132 {
foxdie 6:81bd5717b4ed 133 // Toggle filter
foxdie 6:81bd5717b4ed 134 filterPackets = !filterPackets;
foxdie 6:81bd5717b4ed 135 filter_led = (int)filterPackets;
foxdie 6:81bd5717b4ed 136 break;
foxdie 6:81bd5717b4ed 137 }
foxdie 6:81bd5717b4ed 138 case 'c':
foxdie 6:81bd5717b4ed 139 case 'C':
foxdie 6:81bd5717b4ed 140 {
foxdie 6:81bd5717b4ed 141 // Clear filter
foxdie 6:81bd5717b4ed 142 filteredPackets.clear();
foxdie 6:81bd5717b4ed 143 pc.printf("Packet filter cleared\r\n");
foxdie 6:81bd5717b4ed 144 break;
foxdie 6:81bd5717b4ed 145 }
foxdie 6:81bd5717b4ed 146 case 'd':
foxdie 6:81bd5717b4ed 147 case 'D':
foxdie 6:81bd5717b4ed 148 {
foxdie 6:81bd5717b4ed 149 // Show filter
foxdie 6:81bd5717b4ed 150 clearAndHome();
foxdie 6:81bd5717b4ed 151 pc.printf("[%u] entries in filter:\r\n", filteredPackets.size());
foxdie 6:81bd5717b4ed 152 for (int i=0; i < filteredPackets.size(); i++)
foxdie 6:81bd5717b4ed 153 pc.printf("%u: 0x%08X ", i+1, filteredPackets[i]);
foxdie 6:81bd5717b4ed 154 pc.printf("\r\n");
foxdie 6:81bd5717b4ed 155 break;
foxdie 6:81bd5717b4ed 156 }
foxdie 6:81bd5717b4ed 157 case 'r':
foxdie 6:81bd5717b4ed 158 case 'R':
foxdie 6:81bd5717b4ed 159 {
foxdie 6:81bd5717b4ed 160 // Restart mbed
foxdie 6:81bd5717b4ed 161 pc.printf("Restarting mbed...\r\n");
foxdie 6:81bd5717b4ed 162 mbed_reset();
foxdie 6:81bd5717b4ed 163 break;
foxdie 6:81bd5717b4ed 164 }
foxdie 6:81bd5717b4ed 165 default:
foxdie 6:81bd5717b4ed 166 pc.printf("Unknown keypress!\r\n");
foxdie 6:81bd5717b4ed 167 break;
foxdie 6:81bd5717b4ed 168 }
foxdie 6:81bd5717b4ed 169 }
foxdie 6:81bd5717b4ed 170 // Sleep for 20ms repeatedly, as all messages are handled by an interrupt, this prevents
foxdie 6:81bd5717b4ed 171 // keeping the mbed at full load
foxdie 6:81bd5717b4ed 172 wait(0.02);
foxdie 6:81bd5717b4ed 173 }
foxdie 5:f9107433b66a 174 }