You are on page 1of 9

Xin chào các bạn sinh viên.

Mình làm series về điện tử ô tô dành cho các bạn sinh viên thích tìm
tòi khám phá. Hy vọng thông tin cung cấp sẽ giúp ích cho các bạn trong quá trình học tập.
Bài đầu tiên xin chia sẻ với các bạn cách kết nối Arduino với canbus của ô tô thông qua cổng
OBD2 để đọc các thông số tốc độ, vòng tua, nhiệt độ, … có rất nhiều thông số có thể đọc qua
can bus nhưng hiện tại trong ví dụ này mình chỉ đọc vòng tua và nhiệt độ nước làm mát thôi nhé.
Các thông số khác đọc tương tự.
Lý do mình chọn arduino vì đây là module rất thông dụng, có rất nhiều thư viện hỗ trợ. Người sử
dụng hầu như chỉ cần ghép nối các thư viện, chỉnh sửa chút ít thông tin là có thể hoàn thành đồ
án một cách nhanh chóng.
Arduino là module dựa vào vi điều khiển AVR nhung câu lệnh đã được chuyển thế sang ngôn
ngữ C dễ nhớ hơn.
Nhưng riêng Arduino due là dùng chip của ARM nhé.
Các bước chuẩn bị như sau.
1. Mua mạch Arduino UNO, module can bus loại kết nối trực tiếp với Arduino (mình dùng
Sunflower can shield V3).

Canbus này sử dụng controller MCP2515 và bộ nhận TJA1050.


Các thư viện đều viết cho tần số thạch anh 16 trong khi mạch CAN bus đang dùng thạch
anh 8. Vì vậy sau khi mua về các bạn phải thay bằng thạch anh 16 thì mới chạy được.

Các bạn có thể dùng module có hình dạng sau, cách giao tiếp vẫn tương tự.

Created By PEARL 05-Feb-2020


Các bạn lưu ý hai module này giao tiếp với Arduino qua chuẩn SPI nhé.
Hai module này đều mua được trên web tại thị trường Việt Nam. Module sunflower CAN
shield tiện hơn vì không phải nối dây giữa Arduino.

Luu ý khi mua Arduino thì phải mua cả cáp nạp nhé. Arduino nạp luôn trên mạch nên
không cần mạch nạp. Arduino quá thông dụng rồi nên mình không giới thiệu nhiều nữa.

2. Sau khi các bạn có các module này rồi, bước tiếp theo sẽ tải IDE của Arduino. Đây là
giao diện lập trình cho Arduino bằng ngôn ngữ C rất trực quan.

Link tải phần mềm: https://www.arduino.cc/en/main/software


Hoặc lên google search: “IDE Arduino” sẽ có.
Phần mềm này khá nhẹ (vài trăm MB), có nhiều phiên bản và các bạn dùng verson nào
cũng được.
3. Hướng dẫn sử dụng Arduino. Các bạn tải Arduino cook book theo link sau (bản tiếng
anh).
Link: https://juniorfall.files.wordpress.com/2011/11/arduino-cookbook.pdf
Các bạn học hết được hướng dẫn sử dụng này là trở thành MASTER rồi.
4. Các thư viện cần có.
Thư viện tải từ Github theo đường link: https://github.com/Mebus/CAN_BUS_Shield-1
5. Lý thuyết về OBD2. Các bạn them khảo link sau nhé. Thông tin trong link này sẽ giúp
các bạn hiểu được cách tính toán vòng tua, nhiệt độ… sau khi đọc được thông tin. Vì
thông tin đọc được là giá trị nguyên sơ, cần phải đưa thông tin vào tính toán mới ra được
giá trị.

Link: https://en.wikipedia.org/wiki/OBD-II_PIDs
Ví dụ: ta sẽ đọc được giá trị A và B thông quá Can bus. Vòng tua RPM sẽ được tính qua
công thức (256A+B)/4.

Created By PEARL 05-Feb-2020


Vậy A/B là các giá trị nào? Các bạn tham khảo link sau.
https://www.csselectronics.com/screen/page/simple-intro-obd2-explained/language/en

Bởi vì giá trị đọc về được là giá trị 8 byte (64 bit). A sẽ là giá trị byte thứ 3, B là giá trị
tại byte thứ 4.
Nhân tiện chúng ta tham khảo cấu trúc bức điện của 1 tin nhắn, giải thích như sau.
Bức điện request: 7DF 02 01 0D 55 55 55 55 55
Bức điện trả lời: 7E8 03 41 0D 32 xx xx xx xx
 Identifier:
- 7DF: định nghĩa đây là bức điện yêu cầu thông tin
- 7E8  7EF: định nghĩa đây là bức điện hồi đáp.
 Byte (length)
- 02 (thông tin bao gồm 02 dữ liệu: mode và địa chỉ thông tin 01 và 0D):
cho bức điện request
- 03 (bởi vì lúc này thông tin bao gồm 03 dữ liệu: mod, địa chỉ và dữ
liệu phản hồi): cho bức điện phản hồi.
 Mode
Request: là 01
Response: 41 (chuyển 04)
 PID: chứa địa chỉ các biến cần lấy thông tin.
Ví dụ: RPM thì địa chỉ lấy thông tin là 0C, speed địa chỉ lấy thông tin là 0D.
 Ah, Bh,Ch,Dh: dữ liệu đọc về lưu dưới dạng HEX

6. Sơ đồ kết nối.
- Nếu các bạn dùng Sunflower CAN shield thì cách kết nối rất đơn giản, chỉ ghép hai
module vào mới nhau như hình bên dưới. Kết nối qua cổng com có sẵn trên CAN bus
shield như hình chụp. Nhược điểm của kết nối này là mạch Arduino UNO dùng
nguồn 12V từ xe nên IC nguồn rất nóng. Các bạn có thể dùng nguồn ngoài <12 để
thay thế.

Created By PEARL 05-Feb-2020


Với cách này phải làm thêm cáp chuyển đổi DB9-OBD2. Cáp này có trên web nhưng
phải mua từ nước ngoài. Giải pháp khác là mua riêng cáp OBD2 loại thường, rồi mua
thêm cổng DB9 rời để hàn vào đầu còn lại.

Nếu không có cổng DB9 có thể kết nối vào domino như bên dưới. cấp nguồn cho
Arduino là CAN shield có nguồn, không cần phải cấp hai mạch riêng.

- Nếu các bạn dùng module rời nhứ hình dưới thì cách kết nối như sau.

CAN Module Arduini Uno


INT 2
SCK 13
SI 11

Created By PEARL 05-Feb-2020


SO 12
CS 10 Chân chọn chip có thể thay đổi theo chương trình
GND Chân cấp nguồn 0V
VCC 5V

Để kiểm tra lại sơ đồ của Arduino uno, các bạn có thể vào google tra từ “Arduino uno
schematic”
7. Giải thích một số câu lệnh quan trọng.
- #define PID_COOLANT_TEMP 0x05 //địa chỉ để lấy dữ liệu nhiệt đồ
- #define CAN_ID_PID 0x7DF //identifier 0x7DF để chỉ ra đây là bứ điện
yêu cầu thông tin.
- unsigned char tmp[8] = {0x02, 0x01, __pid, 0, 0, 0, 0, 0};
--pid: chưa địa chỉ dữ liệu cần yêu cầu.
Các thông tin khác tham khảo mục trên.
- CAN.sendMsgBuf(CAN_ID_PID, 0, 8, tmp);
CAN_ID_PID: địa chỉ xuất phát của dữ liệu
0: standard frame, 1-extended frame
8: chiều dài dữ liệu là 8 byte
Tmp: nội dung của tin nhắn.
- CAN.readMsgBuf(&len, buf);
&len: chiều dài dữ liệu
Buf: bộ đệm chứa dữ liệu
- while (CAN_OK != CAN.begin(CAN_500KBPS))
Khởi tạo giao thức can bus với tốc độ 500KBPS.
8. Chương trình chính.
#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 = 10;

MCP_CAN CAN(SPI_CS_PIN); // Set CS pin

#define PID_ENGIN_PRM 0x0C


#define PID_VEHICLE_SPEED 0x0D
#define PID_COOLANT_TEMP 0x05

Created By PEARL 05-Feb-2020


#define CAN_ID_PID 0x7DF

unsigned char PID_INPUT;


unsigned char getPid = 0;

void set_mask_filt()
{
/*
* 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, 0x7E8);
CAN.init_Filt(1, 0, 0x7E8);

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

void sendPid(unsigned char __pid)


{
unsigned char tmp[8] = {0x02, 0x01, __pid, 0, 0, 0, 0, 0};
SERIAL.print("SEND PID: 0x");
SERIAL.println(__pid, HEX);
CAN.sendMsgBuf(CAN_ID_PID, 0, 8, tmp);
}

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);
}
SERIAL.println("CAN BUS Shield init ok!");
set_mask_filt();
}

Created By PEARL 05-Feb-2020


void loop()
{
sendPid(PID_ENGIN_PRM);
delay(200);
taskCanRecv1();
delay(500);
sendPid(PID_COOLANT_TEMP);
delay(100);
taskCanRecv2();
delay(200);
taskDbg();

if(getPid) // GET A PID


{
getPid = 0;
sendPid(PID_INPUT);
PID_INPUT = 0;
}
}

void taskCanRecv1()
{
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]);
SERIAL.print("\t");
}
SERIAL.println("\n");
SERIAL.print("toc do...:");
SERIAL.println((buf[3]*256+buf[4])/4);
}
}
void taskCanRecv2()

Created By PEARL 05-Feb-2020


{
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]);
SERIAL.print("\t");
}
SERIAL.println("\n");
SERIAL.print("nhiet do...:");
SERIAL.println(buf[3]-40);
}
}
void taskDbg()
{
while(SERIAL.available())
{
char c = SERIAL.read();

if(c>='0' && c<='9')


{
PID_INPUT *= 0x10;
PID_INPUT += c-'0';

}
else if(c>='A' && c<='F')
{
PID_INPUT *= 0x10;
PID_INPUT += 10+c-'A';
}
else if(c>='a' && c<='f')
{
PID_INPUT *= 0x10;
PID_INPUT += 10+c-'a';
}
else if(c == '\n') // END
{
getPid = 1;

Created By PEARL 05-Feb-2020


}
}
}
// END FILE
9. Hình ảnh khi chạy chương trình

10. Phát triển ứng dụng.


Mình viết bài này chỉ mang tính chất định hướng, các bạn có thể phát triển các ứng dụng
sau.
- Kết hợp với module Bluetooth để nhận thông tin trên điện thoại, khi cần truy cập data
thì không phải dùng đến máy tính, sẽ nhanh gọn hơn.
- Viết chương trình đọc mã lỗi và xóa lỗi. (cái này minh chưa làm, nhưng nguyên lý cơ
bản giống đọc thông tin data của xe).
- Kết hợp với labview để đọc thông tin và vẽ dưới dạng đồ thị thì nhìn sẽ rất pro.
Labview là một phần mềm thu thập thông tin / điều khiển, lập trình bằng giao diện đồ
họa rất mạnh, giao diện rất đẹp. cần thêm thông tin các bạn có thể tham khảo trên
internet.

Luu ý cho các bạn muốn phát triển ứng dụng mạnh, cần tốc độ xử lý cao, bộ nhớ
trong lớn thì các bạn sinh viên nên học ARM. Đây là vi điều khiển 32 bit tốc độ xử lý
cao, bộ nhớ trong lớn, có sẵn nhiều giao thức giao tiếp on chip SPI, I2C, CAN… sẽ
đáp ứng được tât cả các yêu cầu của các bạn.

Created By: PEARL

Created By PEARL 05-Feb-2020

You might also like