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 20:02:19 2012 +0000
Revision:
5:f9107433b66a
Parent:
4:e40a05b32518
Child:
6:81bd5717b4ed
Added rudimentary support for filtering GMLAN packets and migrated header code into its own class

Who changed what in which revision?

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