Connecting to a car using Seeed CAN shield

Hello!

I’ve been pulling my head off trying to make my Seeed CAN shield V2.0 talk to my car via OBDII port and I really don’t know what to do :slightly_frowning_face:

I see people easily get engine RPM and speed data but even the test sketch won’t work for me. I need this data for my custom instrument cluster project.

Since I only have one shield, I cant be certain its even working, I connected an oscilloscope to it and it does seem like its sending something, but I’m not sure if that is right: if I send even one single message, it will keep spamming stuff (like shown on image below - the oscilloscope image) like crazy. There are no pauses in between. Doesnt matter if I put delay in between sends, if I make one send only, I can even upload blink after OBDII sketch in the meantime and it will keep spamming until I disconnect USB and reconnect (probably it has its own processor that has to receive other instructions). Is it normal that it keeps spamming? Maybe until it receives an answer it spams same message? Could anyone please please shed some light on this please? Before this I tried waking up an BMW E90 instrument cluster by following some instructions on someone’s blog, also unsuccessfully :confused: I tested car with OBD2 scanner to be sure can is sending data and it worked ok. I cant figure out what Im doing wrong :-\

Pics:




I made my own OBD2 to DB9 cable and used 3 different sources to be sure it was right, also tested it multiple times with multimeter

Test code:

First example I tried and which I then modified: Seeed-Studio/CAN_BUS_Shield/tree/master/examples/OBDII_PIDs
Whatever I send, 0x0c, 0x0d, 0x0C, 0x0D, 0c, 0C, 0d, 0D, nothing returned a response, it just printed that it sent the command…

My modified code that shouldnt need human interaction:

#include <SPI.h>
#include "mcp_can.h"

/*SAMD core*/
#ifdef ARDUINO_SAMD_VARIANT_COMPLIANCE
#define SERIAL SerialUSB
#else
#define SERIAL Serial
#endif

// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 9;

MCP_CAN CAN(SPI_CS_PIN); // Set CS pin

#define MODE1_PID_ENGINE_RPM 0x0C
#define MODE1_PID_VEHICLE_SPEED 0x0D
#define MODE1_PID_COOLANT_TEMP 0x05

#define REQ_MSG 0x7DF
#define RESP_MSG 0x7E8

#define MODE1_CURRENT_DATA 0x01

#define STANDARD_FRAME 0
#define EXTENDED_FRAME 1

#define BYTE_UNUSED 0x55

void sendPid(unsigned char __pid)
{
  const uint8_t DATA_LEN = 0x02;

  uint8_t data_buf[8] = {
      DATA_LEN,
      MODE1_CURRENT_DATA,
      __pid,
      BYTE_UNUSED,
      BYTE_UNUSED,
      BYTE_UNUSED,
      BYTE_UNUSED,
      BYTE_UNUSED};

  char serial_msg_buf[5];
  snprintf(serial_msg_buf, 5, "%02x", __pid);
  SERIAL.print("SEND PID: 0x");
  SERIAL.println(serial_msg_buf);

  const uint8_t MSG_LEN = 8;

  CAN.sendMsgBuf(REQ_MSG, STANDARD_FRAME, MSG_LEN, data_buf);
}

void setup()
{
  SERIAL.begin(115200);
  while (CAN_OK != CAN.begin(CAN_500KBPS))
  { // init can bus : baudrate = 500k
    SERIAL.println("CAN BUS Shield init fail");
    SERIAL.println(" Init CAN BUS Shield again");
    delay(100);
  }

  /*
        set mask, set both the mask to 0x3ff
    */
  CAN.init_Mask(0, 0, 0x7FC);
  CAN.init_Mask(1, 0, 0x7FC);

  /*
        set filter, we can receive id from 0x04 ~ 0x09
    */
  CAN.init_Filt(0, 0, RESP_MSG);
  CAN.init_Filt(1, 0, RESP_MSG);

  CAN.init_Filt(2, 0, RESP_MSG);
  CAN.init_Filt(3, 0, RESP_MSG);
  CAN.init_Filt(4, 0, RESP_MSG);
  CAN.init_Filt(5, 0, RESP_MSG);

  SERIAL.println("CAN BUS Shield init ok!");
}

void loop()
{
  sendPid(MODE1_PID_ENGINE_RPM);
  delay(5);
  taskCanRecv();
  delay(1000);
}

void taskCanRecv()
{
  unsigned char len = 0;
  unsigned char buf[8];

  if (CAN_MSGAVAIL == CAN.checkReceive())
  {                            // check if get data
    CAN.readMsgBuf(&len, buf); // read data,  len: data length, buf: data buf

    SERIAL.println("\r\n------------------------------------------------------------------");
    SERIAL.print("Get Data From id: 0x");
    SERIAL.println(CAN.getCanId(), HEX);
    for (int i = 0; i < len; i++)
    { // print the data
      SERIAL.print("0x");
      SERIAL.print(buf[i], HEX);
      SERIAL.print("\t");
    }
    SERIAL.println();
  }
}

I also tried another code from another guy (used my SSD1306 OLED screen for this), but no luck :confused:

#include <Canbus.h>
#include <defaults.h>
#include <global.h>
#include <mcp2515.h>
#include <mcp2515_defs.h>

#include <Wire.h>
#include "U8glib.h"

// U8g setup
U8GLIB_SSD1306_128X64_2X u8g(U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST);

const uint8_t N_CHARS_MAX = 15;

int rpm = 0;
char textBuf[N_CHARS_MAX];

void obd2Request()
{
  tCAN message;

  message.id = 0x7DF;
  message.header.rtr = 0;
  message.header.length = 8;
  message.data[0] = 0x02;
  message.data[1] = 0x01;
  message.data[2] = 0x0C;
  message.data[3] = 0x55;
  message.data[4] = 0x55;
  message.data[5] = 0x55;
  message.data[6] = 0x55;
  message.data[7] = 0x55;

  mcp2515_bit_modify(CANCTRL, (1 << REQOP2) | (1 << REQOP1) | (1 << REQOP0), 0);
  mcp2515_send_message(&message);
}

void obd2Reply()
{
  tCAN message;

  if (mcp2515_check_message())
  {
    if (mcp2515_get_message(&message))
    {
      if (message.id == 0x7E8)
      {
        Serial.print("ID: ");
        Serial.print(message.id, HEX);
        Serial.print(" ");
        Serial.print("Data: ");
        Serial.print(message.header.length, DEC);
        Serial.print(" ");

        for (uint16_t i = 0; i < message.header.length; i++)
        {
          Serial.print(message.data[i], HEX);
          Serial.print(" ");
        }

        rpm = ((message.data[3] * 256) + message.data[4]) / 4;

        Serial.print(rpm);
        Serial.println("");
      }
    }
  }
}

void draw(void)
{
  // graphic commands to redraw the complete screen should be placed here
  u8g.setFont(u8g_font_unifont);
  u8g.drawStr(0, 20, textBuf);
}

void refreshScreen()
{
  // picture loop
  u8g.firstPage();
  do
  {
    draw();
  } while (u8g.nextPage());
}

void setup()
{
  Serial.begin(115200);
  Serial.println("CAN Read - Testing receival of CAN bus message");

  snprintf(textBuf, N_CHARS_MAX, "INIT STARTING");
  refreshScreen();

  delay(3000);

  if (Canbus.init(CANSPEED_500))
  {
    Serial.println("CAN Init ok");
    snprintf(textBuf, N_CHARS_MAX, "INIT SUCCESS");
    refreshScreen();
  }
  else
  {
    Serial.println("Can't init CAN");
    snprintf(textBuf, N_CHARS_MAX, "COULDNT INIT");
    refreshScreen();
  }

  delay(3000);
}

void loop()
{
  obd2Request();
  delay(5);
  obd2Reply();

  snprintf(textBuf, N_CHARS_MAX, "%d", rpm);
  refreshScreen();

  // rebuild the picture after some delay
  delay(100);
}

Please help :frowning:

Googled on "Arduino + QBDII" and got this:
https://www.bing.com/search?q=Arduino+%2B+OBDII&form=ANNTH1&refig=39b8dfb18d61434eb902c200046752d3&sp=-1&pq=arduino+%2B+obdii&sc=0-15&qs=n&sk=&cvid=39b8dfb18d61434eb902c200046752d3

Railroader:
Googled on "Arduino + QBDII" and got this:
Arduino OBDII - Bing

I was weighting whether to get this (Freematics) adapter, Sparkfun shield or Seeed CAN shield. The Seeed shield was the most readily available (from Amazon) since I wanted to start quickly (and the Freematics is based in Australia if I read correctly). If I cant get it to work with Seeed shield I will have no choice but to order Freematics one, but that will already be over 100€ just for the adapters :frowning:

Have You done the research about the QBDII bus? Clock rate, what ever...? I've worked a lot using CAN-bus control of heavy machinary but don't remember the critical basics, setups, done by my collegue.

Railroader:
Have You done the research about the QBDII bus? Clock rate, what ever...? I've worked a lot using CAN-bus control of heavy machinary but don't remember the critical basics, setups, done by my collegue.

Yeah, I have educated myself about how CAN bus (and OBD2) funcion before taking on this project (requests, responses, frames, PIDs etc). But as its with anything, Im slowly gaining practical knowledge as I try to do things, but Im a bit at a loss at the moment because I cant figure out what Im doing wrong

As for speed, library offers you multiple bitrates, but I have seen 500kbps used in a few different places now

Communication is likely a key factor. I assume You gave checked the electrical factors like voltage levels, eventual terminating resistors?
From Your first post I think something is basicly wrong, maybe wrong way the library functions are used. You make one transmission but strange signalling appears on the CAN buss.

Railroader:
Communication is likely a key factor. I assume You gave checked the electrical factors like voltage levels, eventual terminating resistors?
From Your first post I think something is basicly wrong, maybe wrong way the library functions are used. You make one transmission but strange signalling appears on the CAN buss.

I agree. I checked again and figured out that I connected probes from oscilloscope wrong for scanning CAN bus. I connected CAN_HI to probe and CAN_LO to ground. Now I connected probe1 to CAN_HI and CAN_LO to probe2, leaving grounds to float. This doesnt impact my problem, only analysis of it. Sorry for this. Oh and I now used Stop feature on oscilloscope to photograph it, thats why previously the photos looked bad, I left it running when taking the photo)

When there is nothing going on, both CAN_HI and CAN_LO are at 2.3V

When signals are being transmitted, CAN_HI alternates between 2.2V (min on oscilloscope says around 2V) and 3.1V (max on oscilloscope says around 3.4V) and CAN_LO between 1.4V (min on osc says around 1.2V) and 2.2V (max on osc says around 2.4V)

If I do a subtraction on those two signals (thats what I understand its supposed to do - since these are supposed to make a 2.5V differential to be resiliant to noise) I get a signal that is alternating between 1.7V and 0V

According to this the voltages for CAN_HI should be 2.5V for idle and 3.75V for can CAN_HI when active and 1.25V for CAN_LO when active.

The shield instructions did not mention anything about terminating resistors, I assume they have that built in. They did mention about some modifications (trace cutting) if using multiple of those shields, but Im not using multiple of them

I would agree about potential wrong library use but I also used their example to test it, I hope they dont have their example wrong?

Your measurements looks very odd to me. Read specifications for the QBDII and act upon them.

Your code is making one transmission but more signals are comming. Do they come from the controller or are they comming from the OBDII port? Use serial.print and Serial Monitor to monitor the controller transmissions.

Hi,
You should not have to Tx anything to the CanBus, there should be data flowing all the time.
In fact if you want to get RPM and Speed, they could possibly be on the CanBus ready to read and need no Tx command.

What scope traces do you get without the SEED connected at the OBDII socket of the vehicle?

Can you post diagrams of your adapter leads please?

Tom.... :slight_smile:

Railroader:
Your measurements looks very odd to me. Read specifications for the QBDII and act upon them.

Your code is making one transmission but more signals are comming. Do they come from the controller or are they comming from the OBDII port? Use serial.print and Serial Monitor to monitor the controller transmissions.

The signals are coming from the shield (DB9 port which then goes to OBDII port). Serial print was already included in sketches, it said that it successfully connected and that it sent the request but then nothing happened.

TomGeorge:
Hi,
You should not have to Tx anything to the CanBus, there should be data flowing all the time.
In fact if you want to get RPM and Speed, they could possibly be on the CanBus ready to read and need no Tx command.

What scope traces do you get without the SEED connected at the OBDII socket of the vehicle?

Can you post diagrams of your adapter leads please?

Tom.... :slight_smile:

Hello!

True, but isnt OBDII port a bit different than connecting to CAN bus of the car directly? I agree that if I did connect to CAN bus of the car, I should see the chatter between modules, but I read on different pages (ie this one) that you have to request data from OBDII:

How to log OBD2 data?
OBD2 data logging works as follows:
You connect an OBD2 logger to the OBD2 connector
Using the tool, you send 'request frames' via CAN
The relevant ECUs send 'response frames' via CAN
In other words, a CAN logger that is able to transmit custom CAN frames can also be used as an OBD2 logger.

I can try to only scan for frames and send no requests (will do tomorrow), no harm in trying

Unfortunately I cannot test the scope in vehicle because I only have a scope that connects to mains and no way to get mains power to my vehicle at this time

The DB9 to OBDII adapter was made according to this table and this table

Using the Freematics OBDII reader, I request from the reader a PID, the Freematics OBDII does the thing and returns the results of my PID request. The Freematics sends the data to the micro-controller via a serial port.