Masahiro Mizuno
Published © GPL3+

OBD2 Simulator

With this simulator, you can develop an OBD2 device and related service on your desk without going to the garage.

IntermediateFull instructions provided2,989
OBD2 Simulator

Story

Read more

Custom parts and enclosures

OBD2 Simulator Gerber data

Schematics

OBD2 Simulator Schematic

Code

OBD2 Simulator

Arduino
// ============================================
//  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;   

    

Credits

Masahiro Mizuno

Masahiro Mizuno

10 projects • 22 followers
Chief Prototypist. 20+ years R&D experience in wide range of technology.

Comments