CAN, MCP2515, Arduino, & SavvyCAN data exchange format ?

Hello one and all...
Late last year I decided to investigate Tapping into my car CAN bus, so that I can read Engine RPM, Vehicle Speed, and (maybe) Auto-Trans gear selection data.
This is all with the aim of building my own Cruise Control.
I'm an electronics tech, so no issues on the 'Build' side of things.
And YES - I will have Heaps of Safe-guards.
And NO - I'm NOT using the OBD Port.

So I bought some Generic 5v MCP2515 CAN boards (from AliExpress).
(I won't worry about any Full Description on those, as they aren't the trouble).
Found the F-CAN bus in my [2006 Honda Accord] at a node/junction point, and have confirmed everything with my Scope etc.

Recently I played around with a few CAN Examples for Receiving the data, and got something meaningful.

Note: I am currently starting with an Arduino Nano... Yes, I know that it is too slow for Vast amounts of CAN data, but when I start Masking & Filtering the data, I'm sure it will do fine. Otherwise, I can always throw in a Seeeduino or ESP32 instead.

Anyway -
I'm on a Mac, running OS 12.7.4 Monterey - This limits what Sniffer programs I can use, but SavvyCan seems to look good enough for the initial job.

I see there are several MCP2515 libraries to choose from. The 2 that I have installed in the IDE are the AUTOWP 'mcp2515.h', and the alternate 'mcp_can.h'
The AUTOWP library files certainly look like they can allow much more control of the MCP2515 settings etc. It is with their 'Receive Example' code that I have had the easiest success.

--- Hopefully that gives you enough Basic Background ---

So - On to my pressing issue: How to get that CAN data into SavvyCAN (?)

Obviously, the 'Receive Example' does a great job of displaying data in the Serial Monitor.
But what is the Data requirement of SavvyCAN... Complete Unedited Raw Data, or something else ?

I'm not a programmer's Backside, so when it gets heavy into the code / classes / lib files etc, I get in Way over my head.
This afternoon I connected back up to the car & tried to see if I gould get anything into SavvyCan.
The answer was... Slightly...

Here's my code:

#include <SPI.h>
#include <mcp2515.h>

struct can_frame canMsg;
MCP2515 mcp2515(10);    // Sets the CS pin


void setup() {
  Serial.begin(115200);
  
  mcp2515.reset();
  mcp2515.setBitrate(CAN_500KBPS, MCP_8MHZ);    // Added, to make sure 8MHZ Xtal is selected.
  mcp2515.setNormalMode();
//  mcp2515.setListenOnlyMode();                  // Pure Listen mode - sends no Error or ACK messages
  
  Serial.println("------- CAN Read ----------");
  Serial.println("ID    DLC         DATA");
}

void loop() 
{
  if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK) 
   {
    if (canMsg.can_id < 0x100) {Serial.print ("0");}
    Serial.print(canMsg.can_id, HEX); // print ID
    Serial.print("   "); 

    if (canMsg.can_dlc < 0x10) {Serial.print ("0");}
    Serial.print(canMsg.can_dlc, HEX); // print DLC
    Serial.print("   ");
 
    for (int i = 0; i<canMsg.can_dlc; i++)  
   {  // print the data
    if (canMsg.data[i] < 0x10) {Serial.print ("0");}
      Serial.print(canMsg.data[i],HEX);
      Serial.print(" ");
   }

    Serial.println();      
  }
}

For display on the screen, I added a few extra items (leading Zeros), purely to clean up the display in the Serial Monitor... This would obviously not be included in data for SavvyCAN.
Here's a Snippet of displayed Data:

AUTOWP_CAN Bus data received 5/4/24 6:23pm
Column Justified better
------- CAN Read ----------
ID    DLC         DATA
0D4   08   04 E0 80 00 AA 00 00 4E 
1F4   08   02 16 00 01 B9 0D 00 40 
327   08   00 00 12 51 03 A5 02 40 
2E7   08   00 67 19 FF 1F 00 1C 53 
0D4   08   04 E0 80 00 AA 00 00 8A 
0D4   08   04 E0 80 00 AA 00 00 C6 
0D4   08   04 E0 80 00 AA 00 00 02 
0D4   08   04 E0 80 00 AA 00 00 4E 
1F4   08   02 16 00 01 B9 0D 00 80 
0D4   08   04 E0 80 00 AA 00 00 8A 
0D4   08   04 E0 80 00 AA 00 00 C6 
0C8   08   00 00 00 00 00 00 00 4C 
12C   08   57 50 00 00 00 00 10 4A 
188   08   07 38 00 01 00 00 0E 4B 
0D4   08   04 E0 80 00 AA 00 00 02 
0C8   08   00 00 00 00 00 00 00 88 
12C   08   57 50 00 00 00 00 10 86 
188   08   07 38 80 01 00 00 0E 8F 
0D4   08   04 E0 80 00 AA 00 00 4E 
1F4   08   02 16 00 01 B9 0D 00 C0 
0C8   08   00 00 00 00 00 00 00 C4 
188   08   07 38 80 01 00 00 0E CB 
0D4   08   04 E0 80 00 AA 00 00 8A 
0C8   08   00 00 00 00 00 00 00 00 
188   08   07 38 80 01 00 00 0E 07 
327   08   00 00 12 51 03 A5 02 80 
0C8   08   00 00 00 00 00 00 00 4C 
188   08   07 38 80 01 00 00 0E 43 
0D4   08   04 E0 80 00 AA 00 00 02 
0C8   08   00 00 00 00 00 00 00 88 

I opened SavvyCAN, and tried to select an Input device.
As I'm not using any form of GVRET, I tried various input forms until it accepted something.


That then showed me this:

But that didn't translate to anything:

And didn't go through to the Sniffer part:

I've previously searched the net for this kind of info, but nothing jumped out at me.
Many articles simply refer to using a Bought CAN interface, then dealt with how to use the data with Sniffers.
So.... Anyone know what I should be doing next ?

Also (if this is not being too cheeky), something more towards some form of Finished Code would be way more productive for me, than lots of reply posts with 'pieces of advice'... which I probably won't be able to piece together (Geez - I wish I knew how to programme properly !!!).
Alas - I'm getting older in the tooth, and barely manage to keep up with all the other work I'm doing, let alone start all these extra little 'Projects'.
Oh how I long for death...
I'll call it Time off, for Good Behaviour :roll_eyes:

Many thanks in advance, to all who can put me on the right path...

I would guess that all the information you need is contained within the Savvycan source code which is on Github.

The documentation says use to use GVRET, therefore why do you want to create your own firmware from scratch that does the same thing as GVRET?

Perhaps you are confused, analysis tools such as Savvycan and the firmware you need to alter the behavior of your car are two different things.

Hmmm... ok.
I've just pulled up GVRET on GitHub, and see it requires specific hardware - namely the DueCAN board.
I think that's why I've always thought that the GVRET software was something more specifically designed for the Due, for the circumstance where you were needing an 'All Singing All Dancing' board to handle every facet of CAN that you would ever desire.
And also - as I'm no programmer to that level, I'd never understand the source code in the first place.

Perhaps you are right - I am thinking of the project in the wrong terms.
My belief was, that CAN Data is standardised in its Frame Layouts, CRCs etc (hence the MCP2515 handles all of that), and the Arduino was simply Reading the messages & passing them over to the SavvyCAN program to sort/display, rather than having to manually try and find/read/interpret every ID individually.

Hi,
when you will change to an ESP32, this is the way to go:

I bought a MKS CANable Pro 2.0 recently, which states to be Savvycan compatible. And just had CAN High & Low connectors, rather than an OBD2 connector. But it´s still on it´s way.
Maybe this is also an option for you.

[EDIT]
To stay on the Nano, you can also take a look here:

Hi TriB.
Thanks for your Positive Up-beat reply.

Yes... it sounds like I may have to think about upgrading to the ESP32 stuff.
As always, sometimes it can by a Smidge annoying, when you have several 38pin ESP32 boards (that I've never actually got around to using), then when you Do have a Specific need, there's a newer version that has everything on-board :sob:

As you mention, the 'collin80/ESP32RET' seems to have everything I'm trying to find (...I think).
The second link you added (autowp...) might be something I've partially overlooked.
It is their 'MCP2515' CAN Lib and "Receive Example" Code that I'm using (for the above posted bits), but perhaps I've overlooked whether I can tailor some of the rest of the code for my use.
One minor problem, is that the CanHacker program is Windows only (and I'm on a Mac).

But with the CanHacker and ESP32RET links / code, it somewhat confirms what I was thinking... that as long as you have the right Software, there's no other form of actual Hardware needed in between - the MCP2515 is doing the physical Interface layer job with the CAN bus, and the micro controller is simply receiving the messages.
That's why I had thought it must just be a case of finding the Specific Raw Data Format that you need to get the Arduino to Spit Out to (a program like) SavvyCAN.

The info listed in your 2nd (CanHacker) link, states

Implement communication with CAN bus via MCP2515 by CanHacker (lawicel) protocol.

Interesting, as the lawicel protocol was one of the listed Protocols in the SavvyCAN Connection window.
So - as SavvyCAN accepts that data format, this might be the clue I have overlooked - lawicel is the Specific Format.

So there you go - you've been Super helpful.
Now all I need to do, is actually find some spare time (?!?) :tired_face:

Thanks again...

1 Like

I should have also added - that for my final project, I Literally only want to Receive a couple of CAN messages (Speed, RPM etc), to use for my Cruise control (my Throttle Body is Cable actuated, and I already have the hardware & drivers to do this).
It's another reason I thought I could just use a spare (basic / slow) Arduino Nano board that I have lying around - and sometimes, a 5v interface cab be simpler to implement.
I only thought I'd trying to go down the road of using SavvyCAN, to try and Find the right CAN Message ID's in the first place (didn't appreciate how difficult this might be).

Cheers !

I also overlooked that :smiley:
So I already got everything together here and buying the CANable was not needed at all.

Funny, because I was searching for a hardware supporting SavvyCan directly which does not cost hundreds. An a Nano with a MCP2515 is good enough!
I´ll give it a go tomorrow.

Awesome.
Please let me know how you get on... :smiley_cat:

I found this on Github:

which gives a direct download link to the Doc:

The (first mentioned) GitHub page gives some basic explanation of things in the Read Me file, and there's also some Source Code given for "Lawicel.cpp" & "NVMInterface.cpp".
... I'll try to find some 'Play Time' sometime soon, to look through it all properly.

I was quite sure to have another MCP2515 laying around, somewhere...
But I needed to order another one and still wait for it.
Hopefully I receive it before the weekend to give it a go :muscle:t2:

Funny, that we are both thinking along similar, yet different Tangents.
In my original post, I mentioned that I have 3 or 4 ESP32's that I've never actually got around to using.
After our previous posting above, and finding the "CanHacker" stuff (albeit for Windows), with the fact that it uses the LAWICEL protocol - which SavvyCAN can take, I looked more at the ESP32 code.
As the ESP32's are 3v3, I decided to purchase some TJA1051 SMD chips from AliExpress. That way, I could replace the (5v only) TJA1050 chips (on the MCP2515 boards I have), with the TJA1051's - which are Dual voltage... There's a 5v connection for the active CAN side (Can H & L), but the rest can be run from 3v3.
I thought - Perfect... I can take advantage of the CanHacker code for ESP32's, but have the LAWICEL output going to SavvyCAN. Excellent.
...then about 10minutes after putting the order through (for the SMD TJA1051's), I noticed that the ESP32's have the CAN 2.0 controller Built In.
Meh ! No need for the MCP2515.
On the ESP32's, the CAN is called TWAI, which is short for "Two Wire Automotive Interface".
So I turned around & purchased a couple of TJA1051 (only) PCB's from AliExpress.
Screen Shot 2024-04-10 at 8.49.51 PM
Screen Shot 2024-04-10 at 8.50.10 PM
(Don't ask me why they say 'NC' on one side, then say 'VIO' on the other).
That way, I can take advantage of the 'CanHacker' code for the ESP32, but with the output going to SavvyCAN on my Mac.
If it all works out, I'l be able to try & find what CAN ID's I'm looking for (Fingers Crossed), then use those ID's in a Nano (or similar) for may actual Cruise Control device.

Now (like you), I'll have to wait until the boards arrive (usually somewhere between 15 & 30 days).

I have found it a little frustrating, that None of this info Readily pops up for people.
I feel that maybe the companies who make all the CAN Interface Hardware, are being very quiet on the matter, so as not to lose sales of their products - I can understand that, but for us Poor Uneducated peoples... :joy:
Either that, or those experienced people just assume everyone has the same knowledge as them, therefore make the connections automatically (as they do).
So for some of us who are Basic programmers (to say the least), these Forums can be a life line.
And I certainly do pay Homage to all the Great Helpers out there in the Arduino world, who spend some of their valuable time to help the rest of us Plebeians...
One person I've particularly noticed is johnwasser, but I'm sure there's plenty of others out there as well ! (Ref: CANBUS ID Filter).
Cheers !!!

1 Like

Received my MCP2515 today and immediately tested it.
It worked with any kind of code, but not in combination with SavvyCan and CanHacker.
The connection is successful but no message appears.
Capturing the interrupt state, shows no result.
So I expect it did not received the setup for the baudrate, etc.

It´s not related to the crystal, I already set it to 8Mhz. This was my first idea.
Tomorrow I will try to capture the debug output with the CanHacker software and compare it with SavvyCan. As far as I read, the lawicel protocol was only implemented poorly and not quite optimized (from what I read from the release notes).
So maybe I need to setup and start the connection on Arduino side and Savvy only reads properly, then.

You are right, the ESP32 already has CAN on board. But I like the idea to have an external device to read, filter and preprocess the messages rather than doing all of that on the same processor. I know it has two, but needs RTOS for that.

All good fun right !? :thinking:
Yes, it can be a little disappointing when you think "Great - Here we go !", then.... nothing.
It sounds like exactly what I had...
The [Nano] with the MCP2515 board registered the Car CAN messages fine** in the Serial Monitor, but nothing translated over to SavvyCan (even though Something showed up in the SavvyCan connection window).
[** I cannot say whether there were Dropped Frames or not]
I doubt if I'll see my TJA1051 boards turn up in less than another week (at least), but will update you on any findings I have.
Cheers !

It seems that the Windows implementation is the problem.
SavvyCan is focussed on Linux.
I´m on that within a GitHub discussion and already received an updated version. But it didn´t worked out for me, yet.
I´m quite confident to get that working soon.

Hello again,

the latest Version worked for me on Windows.
I slightly changed the code to 8MHZ and added a LED.
Now it works great with the Nano.

#include <can.h>
#include <mcp2515.h>

#include <CanHacker.h>
#include <CanHackerLineReader.h>
#include <lib.h>

#include <SPI.h>
#include <SoftwareSerial.h>

const int SPI_CS_PIN = 10;
const int INT_PIN = 2;

const int SS_RX_PIN = 5;
const int SS_TX_PIN = 6;

const int LED_PIN = 4;
long int lastMsg = 0;

CanHackerLineReader *lineReader = NULL;
CanHacker *canHacker = NULL;

SoftwareSerial softwareSerial(SS_RX_PIN, SS_TX_PIN);

void setup() {
    Serial.begin(115200);
    while (!Serial);
    SPI.begin();
    softwareSerial.begin(115200);

    Stream *interfaceStream = &Serial;
    Stream *debugStream = &softwareSerial;
        
    canHacker = new CanHacker(interfaceStream, debugStream, SPI_CS_PIN);
    canHacker->setClock(MCP_8MHZ);    
    
    //canHacker->enableLoopback(); // uncomment this for loopback
    lineReader = new CanHackerLineReader(canHacker);
    
    pinMode(INT_PIN, INPUT);
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, LOW);
}

bool lastLedState = LOW;
void loop() {
    CanHacker::ERROR error;
  
    if (digitalRead(INT_PIN) == LOW) {
        lastMsg = millis();
        lastLedState = HIGH;
        digitalWrite(LED_PIN, lastLedState);
        error = canHacker->processInterrupt();
        handleError(error);
    }

    error = lineReader->process();
    handleError(error);
    if(lastLedState && millis() - lastMsg > 20){
      lastLedState = LOW;
      digitalWrite(LED_PIN, lastLedState);
    }
}

void handleError(const CanHacker::ERROR error) {

    switch (error) {
        case CanHacker::ERROR_OK:
        case CanHacker::ERROR_UNKNOWN_COMMAND:
        case CanHacker::ERROR_NOT_CONNECTED:
        case CanHacker::ERROR_MCP2515_ERRIF:
        case CanHacker::ERROR_INVALID_COMMAND:
            return;

        default:
            break;
    }
  
    softwareSerial.print("Failure (code ");
    softwareSerial.print((int)error);
    softwareSerial.println(")");

    digitalWrite(SPI_CS_PIN, HIGH);
    pinMode(LED_BUILTIN, OUTPUT);
  
    while (1) {
        int c = (int)error;
        for (int i=0; i<c; i++) {
            digitalWrite(LED_BUILTIN, HIGH);
            delay(500);
            digitalWrite(LED_BUILTIN, LOW);
            delay(500);
        }
        
        delay(2000);
    } ;
}

SavvyCan still has some little problems on Windows with the serial port. But creating the connection from scratch on every restart works.
Now I need to get back to the vehicles to research.

Great work.
Right at this very moment I'm too tied up to be able to test it for myself, but will eagerly get to it as soon as I can.
Just having a quick Sqizz at your code, I was surprised to see a "pinMode(LED_BUILTIN, OUTPUT);" in the 'handleError() function.
I would have thought you'd just specify this Once, in void Setup().
Anyway - I'll try it out as soon as I get a chance...
Cheers !

I forgot to confirm -
When you say you changed the code to 8MHz, I presume you're referring to the setting for the MCP2515 Xtal freq ?

... And where do you set the CAN Baud speed (eg 500Kbps in my case) ?
Is there a setting in one of the Libs, or is it Auto-baud selection ?
(I've forgotten the proper name/term for the Auto-baud rate detection).

No, I think you missread the code :slight_smile:
The pinMode(LED_PIN, OUTPUT); is in the setup() function.
And setting it to HIGH is when the interrupt pin, which states if there is any data, is triggered.
The library then checks if there is any error and handles it.

Oh, and now I see! There seems to be a standard code with the builtin LED. This is not mine :sweat_smile:

Exactly, this line changes the 16MHZ to 8, which the very most cheap MCP2515 have.
canHacker->setClock(MCP_8MHZ);

The CAN Baud comes from SavvyCan or CANHacker software. There are a few control commands received by serial. That´s what the error = lineReader->process(); does.

Currently I create a case for it. With a LED and a switch for the 120Ohm end-resistor.

I managed to find a spare 5 minutes, so thought I'd upload your code to my Nano.
That way, it would be ready for me to test with if I get the chance.

But - then I noticed something I'm not sure of (why).
I'm using the Hugely common MCP2515 board (containing the TJA1050).
This is the usual stated connection method:


It uses the SPI between it and the Nano (or other board).
Therefore... It doesn't use Serial (Tx/Rx) for the interface.
In the code, the def's for the SoftwareSerial pins state these as Pin 6 (Tx) & 5 (Rx).
Here-in lies my puzzlement - What's connected to Pins 5 & 6 ?
It was my belief that the Nano pins D0 & D1 are the default Serial pins, that go out to the USB connector via the interface chip.

I have to admit I haven't had time to look through any of the Lib files, to see if the answer is in there somewhere...