How can I pass CAN message via bluetooth HC-05 (like ELM327)?

Hello there,
As the title says, I'm very confused about the way I can link together a CAN module (MCP2515) with a Bluetooth module (HC-05), i have read a lot of forum posts but couldn't figure it out by myself.

My setup:

  • Arduino Uno
  • Can Module (MCP2515)
  • Bluetooth Module (HC-05)

Here's my goal :

  • To be able to monitor live pids (engine rpm, coolant temp etc) on an android device using Torque app or similar.

I'm using this library ( GitHub - coryjfowler/MCP_CAN_lib: MCP_CAN Library ) for mcp2515

All hardware connections are working. (tried CAN_Loopback and also sent a raw random command from android device via bluetooth terminal to arduino and it worked well)

I have read about ELM327, OBD, how messages are formated, that i need to send the canmessage as an ASCII via AT command to Blutooth but couldn't find a working way to achieve my goal.

I think there's something with AT commands that i'm not getting it.

Can you guys, please point me up to what do i need to focus on ?
a snippet of code with working can-to-bt would be greatly appreciated.

All the best and thank you for your time reading it.

unless you want to change the default HC-05 setting, there is no really need to use the 'AT' command.

have a look at this example where command are sent over bluetooth to turn an LED ON/OFF. the status of the LED is also fed back to the bluetooth terminal.

You can extrapolate from that as in

send a command to arduino over bluetooth -> arduino sends corresponding CAN message -> arduino receives CAN reply -> arduino sends CAN reply to bluetooth terminal

I don't know if this applies to the bluetooth module you are using but it is sometime recommended to use a voltage divider on the bluetooth RX pin like this.

hope that helps...

What you are trying to do is far from clear. Even your title is confusing. Reading the How to Use This Forum notes and posting the code you have would help quite a lot.

At a guess you are able to read the engine OK, and do so by sending the data to the serial monitor. IF that is the case, you may simply connect HC-05 to pins 0,1 and read the same data on an Android Bluetooth terminal, of which there are many. No change in code, no AT commands, no messing about. I say IF because I am only aware of getting CANbus information out of a car via Bluetooth, which would make Arduino redundant.

The Bluetooth Graphics Terminal would probably be a good choice.

You might find the following background notes useful.

Thank you so much for your replies. I tried the code suggested and i can control the LED using Bluetooth terminal from the phone, so the connection it's ok.

I have read the attached files but i still couldn't make it work.

Here's my wiring:

Bluetooth logic level converter 5V -> 3.3 V
MCP2515 Wiring

HC-05

  • TX Bluetooth -> RX Arduino (Digital 0)
  • RX Bluetooth -> TX Arduino (Digital 1)

Also this is the code :

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

const String ATE = "ATE"; // Echo off/on
const String ATI = "ATI"; // Version id
const String ATZ = "ATZ"; // Reset
const String ATS = "ATS"; // Set protocol X
const String ATH = "ATH"; // Headers off / on
const String ATL = "ATL"; // Linefeeds off/on
const String ATM = "ATM"; // Memory off/on
const String GETDEFINITIONS = "GETDEFINITIONS"; // Get sensor definitions
const String GETCONFIGURATION = "GETCONFIGURATION"; // Get config of app (hide car sensors, devices sensors, etc)
const String GETSENSORS = "G"; // Get sensor values, one shot.
const String SETSENSOR = "S"; // Set a sensor value
const String PROMPT = ">";
const String CANBUS = "6"; // canbus 500k 11 bit protocol id for elm.
const String ATDPN = "ATDPN";
const String ATDESC = "AT@1";
const String ATAT = "ATAT";
const String LF = "\n";
const String VERSION = "Torque Protocol Interface v0.0.1"; // Don't change this - it's used by Torque so it knows what interface it is connected to
const String VERSION_DESC = "Torque For Android Protocol Interface";
const String OK = "OK";
const String ANALOG = "a";
const String DIGITAL = "d";
const String IS_INPUT = "i";
const String IS_OUTPUT = "o";

String fromTorque = "";

const String CONFIGURATION = ""; 

SoftwareSerial mySerial(0,1); 

unsigned long prevTX = 0;                                        // Variable to store last execution time
const unsigned int invlTX = 1000;                                // One second interval constant
byte data[] = {0xAA, 0x55, 0x01, 0x10, 0xFF, 0x12, 0x34, 0x56};  // Generic CAN data to send

// Serial Output String Buffer
char msgString[128];

// CAN0 INT and CS
#define CAN0_INT 2                              // Set INT to pin 2
MCP_CAN CAN0(10);                               // Set CS to pin 10

void setup() {

  mySerial.begin(9600);  
  
  if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_8MHZ) == CAN_OK)
    mySerial.println("MCP2515 Initialized Successfully!");
  else
    mySerial.println("Error Initializing MCP2515...");

  CAN0.setMode(MCP_NORMAL);

  pinMode(CAN0_INT, INPUT);                      
  
  mySerial.println("MCP2515 Library Loopback Example...");
  
 }

void loop() {

  if (mySerial.available()) {
     char c = mySerial.read();   
     if ((c == '\n' || c == '\r') && fromTorque.length() > 0) {
        fromTorque.toUpperCase();
        processCommand(fromTorque);
        fromTorque = "";
     } else if (c != ' ' && c != '\n' && c !='\r') {
        // Ignore spaces.
        fromTorque += c; 
     }
  }

  if(!digitalRead(CAN0_INT))                          // If CAN0_INT pin is low, read receive buffer
  {
    CAN0.readMsgBuf(&rxId, &len, rxBuf);              // Read data: len = data length, buf = data byte(s)
    
    if((rxId & 0x80000000) == 0x80000000)             // Determine if ID is standard (11 bits) or extended (29 bits)
      sprintf(msgString, "Extended ID: 0x%.8lX  DLC: %1d  Data:", (rxId & 0x1FFFFFFF), len);
    else
      sprintf(msgString, "Standard ID: 0x%.3lX       DLC: %1d  Data:", rxId, len);
  
    mySerial.print(msgString);
  
    if((rxId & 0x40000000) == 0x40000000){            // Determine if message is a remote request frame.
      sprintf(msgString, " REMOTE REQUEST FRAME");
      Serial.print(msgString);
    } else {
      for(byte i = 0; i<len; i++){
        sprintf(msgString, " 0x%.2X", rxBuf[i]);
        mySerial.print(msgString);
      }
    }
        
    mySerial.println();
  }
  if(millis() - prevTX >= invlTX){                    // Send this at a one second interval. 
    prevTX = millis();
    byte sndStat = CAN0.sendMsgBuf(0x100, 8, data);
  }
}

void processCommand(String command) {

   Serial.println(command);

   if (command.equals(ATZ)) {
       mySerial.print(VERSION);
       mySerial.print(LF); 
       mySerial.print(OK);
   } else if (command.startsWith(ATE)) {
       mySerial.print(OK); 
   } else if(command.startsWith(ATI)) {
       mySerial.print(VERSION);
       mySerial.print(LF);
       mySerial.print(OK);
   } else if (command.startsWith(ATDESC)) {
       mySerial.print(VERSION_DESC); 
       mySerial.print(LF);
       mySerial.print(OK);
   } else if (command.startsWith(ATL)) {
       mySerial.print(OK);
   } else if (command.startsWith(ATAT)) {
       mySerial.print(OK);
   } else if (command.startsWith(ATH)) {
       mySerial.print(OK);
   } else if (command.startsWith(ATM)) {
       mySerial.print(OK);
   } else if (command.startsWith(ATS)) {
       // Set protocol
       mySerial.print(OK);
   } else if (command.startsWith(ATDPN)) {
       mySerial.print(CANBUS);
   }   else if (command.startsWith(GETCONFIGURATION)) {
       getConfiguration();
   } 

   mySerial.print(LF); 
   mySerial.print(PROMPT);

 
}

void getConfiguration() {
  mySerial.print(CONFIGURATION);
}

There are some AT cmds for the Torque app in order to negociate with the arduino (took part of the code from here Arduino code - Torque Wiki)

At this moment, I have the Torque app connected to arduino via bluetooth but not getting at data. No connection to the car, i tried to use a test send code to simulate a package but haven't received anything on the bluetooth terminal.
To summarize all, i need the can messages to be broadcasted via bluetooth and torque app to recognize them. As far as i know, torque app sends a "request" like 01 05 (mode and pid) and expects to receive something like this (41 05 45), i can also be wrong.

Sorry but i truly don't know how to better explain it.

mcp2515-can-bus-breakout-board-connecting-to-arduino-uno.png

It looks like Torque expects to talk to an ELM32x device. So Torque will be sending out some AT like commands. The Bluetooth module also uses some AT commands for configuration, so you will need to ensure a conflict does not occur here.

What you are asking from my perspective is to basically turn an Ardunio into a CAN-only ELM327 (or ELM329) clone. My quick google search for such a project didn't come with any immediate results, but I would not be surprised if one does exist in some capacity.

Edit:
Looks like here is a start to get the Arduino and Torque communicating... It would need work to use CAN functionality to talk OBDII to a vehicle. GitHub - TheBigBadWolfClub/GTTurboEcu: Arduino ODBII ELM327 Emulator - Allows Arduino to act as an OBD2 ELM327 device and responde to PID requests

You are using software serial on hardware serial (pins 0,1), which is fatal. Either relocate Bluetooth to another pair of pins, or use Serial to communicate with Bluetooth and delete all reference to software serial. If you use the latter, just make sure Bluetooth is disconnected while you upload your programme.

You have a serial command which won't work because you have failed to initialise serial, but is almost certainly redundant anyway. If you want to test things with the serial monitor, you can do that by having bluetooth on hardware serial as described in the notes

God only knows what that red thing is about. You only need to consider Arduino Tx to Bluetooth Rx, and a 1k/2k voltage divider will suffice. This is shown in my notes.

No comment on the phone app other than that it is probably just a fancy terminal and, as far as Bluetooth is concerned, will work just like any other terminal. You should have no problem there.

Sorry for my inactivity.

Thank you u/coryjfowler
That's exactly what I want to achieve, an elm327 clone functionality.

With this library (GTTurboEcu) i wasn't able to even compile the code (it's probably outdated or arduino changed the way things are working nowadays).

Thank you u/Nick_Pyner

That "red thing" it's something like this : Converter Logic Level . To summarize all, it's a middleware to not use the solution with 2 resistors, it does the same).

First of all, I'm a newbie trying to make something not that simple "from my point of view". I took your advice and moved the bluetooth module to some unused pins (didn't know that those are hardware serial pins and was wondering why I need to disconnect bluetooth while uploading new sketch to arduino)
For the moment, i have an idea reading again the pdf attached (thanks again) but I need to focus on another problem before the bluetooth part.

My current goal is to discover the pid dlc and data to change the audio source to AUX (here's a video with exactly what i talk about : How to change to AUX )

I was able to test things on the car (Opel Astra H MK5 2006) and here's where I am at this moment:

  • connected mcp2515 to OBD (pin 6, 14) HS-CAN 500kbps
  • using coryjfowler's library i was able to receive messages (can traffic data) but i couldn't even been able to filter things up due to my lack of knowledge (i don't know how to filter messages to show only the new ones when i push a button. I read that i somehow need a Can Sniffer for that.
  • i tried using this library : Arduino CanBus Monitor to see can traffic on a "can sniffer" windows app like CAN-Hacker-Tool or Can-Cool). I was able to connect using those windows softwares and "somehow filter the messages" but frankly i've noticed that there were only a few unique canID's (around 25).

My assumption is that either I "miss" some of them due to my 8Mhz Crystal Oscillator installed on MCP2515 (Shall I swap it with a new 16Mhz or 20Mhz ?) or I'm behind a "firewall" or gateway and the traffic is somehow "protected".

I found out that this car has 3 Can Buses :

HS-CAN = 500kbps
MS-CAN = 92.5/95.6 kbps
SW-CAN = 33.3 kbps)

Is there any CAN Sniffer I can properly use to detect what i need (I think i need to scan MSCAN obd pins 3 can H and 11 can L) or is it anything i'm doing wrong ?

Thank you again for your help and any info's highly appreciated,
Have a great day

h4x7o0r:
That "red thing" it's something like this : Converter Logic Level . To summarize all, it's a middleware to not use the solution with 2 resistors, it does the same).

As I said - God only knows . Maybe everything is fine, but note that Bluetooth Tx does not need to be converted, it just goes direct to Arduino Rx. Anything else is just adding to the confusion.

Thank you and noted , i will try the 2 resistors on Bluetooth RX <-> Arduino TX when I connect back the bluetooth part. For the moment, I'm trying to filter messages using coryjfowler's MCP LIB library, but I don't know how to properly do it to work as a can sniffer. Is there any code available for this ?