![](/media/cache/profiles/98be175f18ff12118f056c7e448ade23.50x50_q85.jpg)
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.
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;
(Click to open high resolution version)
main.cpp
- Committer:
- foxdie
- Date:
- 2012-12-10
- Revision:
- 6:81bd5717b4ed
- Parent:
- 5:f9107433b66a
- Child:
- 7:8bc6a5d2ba1e
File content as of revision 6:81bd5717b4ed:
#include "mbed.h" #include <vector> #include <algorithm> DigitalOut rx_led(LED1); DigitalOut filter_led(LED4); Serial pc(USBTX, USBRX); CAN gmlan(p30, p29); Timer uptime; bool filterPackets = false; vector<int> filteredPackets; extern "C" void mbed_reset(); namespace mbed { class CANHeader { // Example header packet for Steering Wheel Switches: // Hexadecimal: 0x10 0x0D 0x00 0x60 // Binary: 00010000 00001101 00000000 01100000 // Priority: --- // Arbitration: -- -------- --- // Sending ECU: ----- -------- private: int priorityID, arbitrationID, senderID; public: int priority(void) { return priorityID; } void priority(int _priority) { priorityID = _priority; } int arbitration(void) { return arbitrationID; } void arbitration(int _arbitration) { arbitrationID = _arbitration; } int sender(void) { return senderID; } void sender(int _sender) { senderID = _sender; } void decode(int _header) { priorityID = (_header >> 26) & 0x7; arbitrationID = (_header >> 13) & 0x1FFF; senderID = (_header >> 0) & 0x1FFF; } int encode(void) { long int buffer = 0; buffer = (buffer << 3) | 0x0; // 3 bit padding buffer = (buffer << 3) | priorityID; buffer = (buffer << 13) | arbitrationID; buffer = (buffer << 13) | senderID; return buffer; } }; } void processMessage() { // Turn on rx_led to indicate we are receiving data rx_led = 1; // Create a CANMessage object and read the buffer into it CANMessage rxmsg; gmlan.read(rxmsg); // Check to see if our header is in the filtered packet list or we are filtering, if so we skip over bool packetFound = false; if (find(filteredPackets.begin(), filteredPackets.end(), rxmsg.id) != filteredPackets.end()) packetFound = true; // If we are filtering packets and this one isn't found, add it to the list if (filterPackets == true && packetFound == false) { filteredPackets.push_back(rxmsg.id); pc.printf("Added 0x%08X to filter at position %u\r\n", rxmsg.id, filteredPackets.size()); } else if (filterPackets == false && packetFound == false) { CANHeader rxHeader; rxHeader.decode(rxmsg.id); // Print these results to the serial terminal using printf to format // Ref: http://www.cplusplus.com/reference/cstdio/printf/ pc.printf("[%10.2f] ", uptime.read()); // Seconds (to 2 decimal places) since start pc.printf("[0x%1X] ", rxHeader.priority()); // Packet priority (0-7, 0 being highest) pc.printf("[0x%4X] ", rxHeader.arbitration()); // Arbitration ID (0x0 to 0x1FFF) pc.printf("[0x%4X] ", rxHeader.sender()); // ECU ID (0x0 to 0x1FFF) pc.printf("[0x%08X] ", rxmsg.id); // Full header for legacy reasons (4 bytes) pc.printf("[%d] ", rxmsg.len); // Length of message (0-8 bytes typically) // Process actual message here, only run this if we have a message if (rxmsg.len > 0) { pc.printf("[%02X", rxmsg.data[0]); // Print first byte for (unsigned int i = 1; i < rxmsg.len; i++) pc.printf(" %02X", rxmsg.data[i]); // Print additional byte(s) with a preceeding space pc.printf("]"); // Print closing bracket } pc.printf("\r\n"); // Print carriage return and newline } // Turn off our rx_led as we have finished reading data rx_led = 0; } void clearAndHome() { pc.printf("%c", 27); // ESC pc.printf("[2J"); // clear screen pc.printf("%c", 27); // ESC pc.printf("[H"); // cursor to home } int main() { // Set serial baud rate to 115kbit pc.baud(115200); // Set CANBUS to 33.3kbit and put into monitor mode (does not ACK packets, aka stealth mode) int baudrate = 33333; gmlan.frequency(baudrate); gmlan.monitor(false); // Clear serial terminal clearAndHome(); pc.printf("Keys: F = start/stop filter capture, D = display filtered headers, C = clear filter\r\n"); pc.printf("Starting packet capture at %i bps\r\n\r\n", baudrate); // Start capturing packets uptime.start(); gmlan.attach(&processMessage); while(1) { // Poll serial for keypresses for certain tasks if (pc.readable()) { switch (pc.getc()) { case 'f': case 'F': { // Toggle filter filterPackets = !filterPackets; filter_led = (int)filterPackets; break; } case 'c': case 'C': { // Clear filter filteredPackets.clear(); pc.printf("Packet filter cleared\r\n"); break; } case 'd': case 'D': { // Show filter clearAndHome(); pc.printf("[%u] entries in filter:\r\n", filteredPackets.size()); for (int i=0; i < filteredPackets.size(); i++) pc.printf("%u: 0x%08X ", i+1, filteredPackets[i]); pc.printf("\r\n"); break; } case 'r': case 'R': { // Restart mbed pc.printf("Restarting mbed...\r\n"); mbed_reset(); break; } default: pc.printf("Unknown keypress!\r\n"); break; } } // Sleep for 20ms repeatedly, as all messages are handled by an interrupt, this prevents // keeping the mbed at full load wait(0.02); } }