It was troublesome to have to prepare a vehicle when making OBD2 device and related services. With this OBD2 simulator, you can develop OBD2 equipment on the desk without going to the garage.
12V DC power required. When BLE is connected to the mobile device, various parameters, engine speed, car speed, coolant temperature, etc. can be set with the slider bar.
You can modify the software by Arduino IDE by connecting the PC through the micro USB connection.
The idea of the design is based on OBD2 Robot Controller "CAR MATE." Protocol IC is MCP2515 and I used the Arduino-compatible library: https://github.com/coryjfowler/MCP_CAN_lib. I found OBD2 Connector in amazon.com.
// ============================================
// Base design http://simblee.com
// Language Reference http://arduino.cc/en/Reference/HomePage
// 24kRAM, 128kROM
// ============================================
#include "FastLED.h" // RGB LED
#include <mcp_can.h>
#include <SPI.h>
#include <SimbleeForMobile.h>
#include "para.h" //
// ============================================
// SETUP
// ============================================
void setup() {
Serial.begin(115200); // Serial.begin(115200);
FastLED.addLeds<NEOPIXEL, 11>(leds, 1); // FastLED setup for RGB LEDs
leds[0] = CRGB(0, 4, 0); FastLED.show();
pinMode(8, INPUT_PULLUP); // CAN INT
pinMode(19, OUTPUT); // CAN SS
pinMode(15, OUTPUT); // test LED
SimbleeForMobile.deviceName = "OBD2Sim"; // device name
SimbleeForMobile.advertisementData = "V0.1"; // firmware version
SimbleeForMobile.begin(); //
CANsetup();
}
// ============================================
// LOOP
long itime1, itime2, itime3; // event timer
boolean tgll = true; //
int comseq = 0;
// ============================================
void loop() {
SimbleeForMobile.process();
canrcv();
if(millis() - itime1 > 1000) { itime1 = millis(); digitalWrite(15, tgll); tgll = !tgll; }
}
// ============================================
// BLE state
// ============================================
void SimbleeForMobile_onConnect() { /*SimbleeBLE.dualModeBegin(); */ }
void SimbleeForMobile_onDisconnect() { /*SimbleeBLE.dualModeEnd(); */ }
// ============================================
// CAN IF
// ============================================
void CANsetup() {
if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK) { Serial.println("MCP2515 Initialized Successfully!"); }
else { Serial.println("Error Initializing MCP2515..."); }
CAN0.setMode(MCP_NORMAL);
}
void canrcv() {
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);
Serial.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]); Serial.print(msgString); } }
Serial.println();
sdata[0] = 0; sdata[1] = 0x41; sdata[2] = rxBuf[2];
switch(rxBuf[2]) {
case 0x0C: sdata[0] = 0x04; Serial.print("rpm:"); // rpm(rpp) 0-ffff(0-16383.75)
sdata[3] = rpp*4 >> 8; sdata[4] = rpp*4 & 0xFF; break; // 02 01 0C..04 41 0C xx yy 00 00 00
case 0x0D: sdata[0] = 0x03; Serial.print("speed:"); // speed(spp) 0-ff (0-256)
sdata[3] = spp; sdata[4] = 00; break; // 02 01 0D..03 41 0D zz 00 00 00 00
case 0x05: sdata[0] = 0x03; Serial.print("deg:"); // ctemp(tpp) 0-ff (-40-215)
sdata[3] = tpp + 40; sdata[4] = 00; break; // 02 01 05..03 41 05 zz 00 00 00 00
case 0x10: sdata[0] = 0x04; Serial.print("air flow:"); // aflow(fpp) 0-ffff(00-655.35)
sdata[3] = fpp*100 >> 8; sdata[4] = fpp*100 & 0xFF; break; // 02 01 10..04 41 10 xx yy 00 00 00
case 0x14: sdata[0] = 0x03; Serial.print("oxgen:"); // oxgen(opp) 0-ff(0-1.275)
sdata[3] = opp*2; sdata[4] = 00; break; // 02 01 14..03 41 14 zz 00 00 00 00
}
byte sndStat = CAN0.sendMsgBuf(0x100, 8, sdata);
if(sndStat == CAN_OK) Serial.println("Message Sent Successfully!");
else Serial.println("Error Sending Message...");
}
}
// ============================================
// UI
// ============================================
void ui() {
SimbleeForMobile.beginScreen(BLACK, PORTRAIT); // ---
SimbleeForMobile.drawText(83, 50, "OBD2 SIMULATOR", GRAY, 20);
lineH = SimbleeForMobile.drawRect(60, 75, 200, 2, RED);
SimbleeForMobile.drawText(54, 95+1, "Engine RPM (0C)", GRAY, 16);
text1 = SimbleeForMobile.drawText(190, 95, "000", WHITE, 18); // rpm 0000-ffff(0-16383.75)
SimbleeForMobile.drawText(190+38, 95+2, "[rpm]", GRAY, 15); //
slider1 = SimbleeForMobile.drawSlider(50, 95+20, 220, 0 ,9999); //
SimbleeForMobile.drawText(54, 95+60+1, "Vehicle Speed (0D)", GRAY, 16);
text2 = SimbleeForMobile.drawText(190, 95+60, "000", WHITE, 18); // speed 00-ff(0-256)
SimbleeForMobile.drawText(190+30, 95+60+2, "[km/h]", GRAY, 15); //
slider2 = SimbleeForMobile.drawSlider(50, 95+60+20, 220, 0 ,255); //
lineM = SimbleeForMobile.drawRect(60, 235, 200, 2, YELLOW);
SimbleeForMobile.drawText(54, 255+1, "Coolant Temp (05)", GRAY, 16);
text3 = SimbleeForMobile.drawText(190, 255, "000", WHITE, 18); // coolant temp 00-ff(0-215)
SimbleeForMobile.drawText(190+30, 255+2, "[deg]", GRAY, 15); //
slider3 = SimbleeForMobile.drawSlider(50, 255+20, 220, -40 ,215); //
SimbleeForMobile.drawText(54, 255+60+1, "Mass Air Flow (10)", GRAY, 16);
text4 = SimbleeForMobile.drawText(190, 255+60, "000", WHITE, 18); // air flow 0000-ffff(0-655)
SimbleeForMobile.drawText(190+30, 255+60+2, "[g/s]", GRAY, 15); //
slider4 = SimbleeForMobile.drawSlider(50, 255+60+20, 220, 0 ,655); //
SimbleeForMobile.drawText(54, 255+60*2+1, "Oxgen Sensor (14)", GRAY, 16);
text5 = SimbleeForMobile.drawText(190, 255+60*2, "000", WHITE, 18); // oxgen 00-ff(0-1.275)
SimbleeForMobile.drawText(190+30, 255+60*2+2, "[x10V]", GRAY, 15); //
slider5 = SimbleeForMobile.drawSlider(50, 255+60*2+20, 220, 0 ,127); //
lineL = SimbleeForMobile.drawRect(60, 75+380, 200, 2, GREEN);
SimbleeForMobile.setEvents(slider1, EVENT_DRAG | EVENT_RELEASE);
SimbleeForMobile.setEvents(slider2, EVENT_DRAG | EVENT_RELEASE);
SimbleeForMobile.setEvents(slider3, EVENT_DRAG | EVENT_RELEASE);
SimbleeForMobile.setEvents(slider4, EVENT_DRAG | EVENT_RELEASE);
SimbleeForMobile.setEvents(slider5, EVENT_DRAG | EVENT_RELEASE);
SimbleeForMobile.endScreen(); // ---------------------
SimbleeForMobile.updateValue(slider1, rpp); SimbleeForMobile.updateValue(text1, rpp);
SimbleeForMobile.updateValue(slider2, spp); SimbleeForMobile.updateValue(text2, spp);
SimbleeForMobile.updateValue(slider3, tpp); SimbleeForMobile.updateValue(text3, tpp);
SimbleeForMobile.updateValue(slider4, fpp); SimbleeForMobile.updateValue(text4, fpp);
SimbleeForMobile.updateValue(slider5, opp); SimbleeForMobile.updateValue(text5, opp);
}
// ============================================
// UI event
// ============================================
void ui_event(event_t &event) {
if(event.id == slider1 && event.type == EVENT_DRAG) { rpp = event.value; SimbleeForMobile.updateValue(text1, rpp); }
if(event.id == slider2 && event.type == EVENT_DRAG) { spp = event.value; SimbleeForMobile.updateValue(text2, spp); }
if(event.id == slider3 && event.type == EVENT_DRAG) { tpp = event.value; SimbleeForMobile.updateValue(text3, tpp); }
if(event.id == slider4 && event.type == EVENT_DRAG) { fpp = event.value; SimbleeForMobile.updateValue(text4, fpp); }
if(event.id == slider5 && event.type == EVENT_DRAG) { opp = event.value; SimbleeForMobile.updateValue(text5, opp); }
}
// ============================================
// IO assignment
// ============================================
// SPI setting
// #define PIN_SPI_SS (18u)
// #define PIN_SPI_MOSI (9u)
// #define PIN_SPI_MISO (16u)
// #define PIN_SPI_SCK (17u)
// ============================================
// VALIABLES
// ============================================
CRGB leds[1]; // RGB LED buffer
int spp, rpp; // speed, rpm
int tpp = 25; // temp
int fpp, opp; // air flow, oxigen
int COMsndReq; //
// ============================================
// CAN VALIABLES
// ============================================
#define CAN0_INT 8 // Set INT to pin 8
MCP_CAN CAN0(19); // Set CS to pin 19
byte sdata[] = { 0x02, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Generic CAN TX data
long unsigned int rxId; // RX id
unsigned char len; // RX length
unsigned char rxBuf[8]; // RX buffer
char msgString[128]; // Serial Output String Buffer
// ============================================
// UI
// ============================================
byte text1, text2, text3, text4, text5;
byte lineH, lineL, lineM;
byte slider1, slider2, slider3, slider4, slider5;
// ============================================
// COM
// ============================================
struct payloadStructure { // COM communication packet 15byte max
int16_t temp; int8_t humid; int16_t presu;
int16_t xxx; int16_t yyy; int16_t rpm; int16_t sped;
} __attribute__((packed));
payloadStructure payloadd;
Masahiro Mizuno
20 projects • 32 followers
Chief Prototypist. 20+ years R&D experience in wide range of technology.
Comments