jpenner64
Published © GPL3+

Sony Spresense Vehicle Hacks

Want to track your child's driving habits? Want to alert them as to their speed or excessive speed? Sony's Spresense can support you!

AdvancedShowcase (no instructions)24 hours2,989
Sony Spresense Vehicle Hacks

Things used in this project

Hardware components

Spresense boards (main & extension)
Sony Spresense boards (main & extension)
×1
SparkFun OBD-II UART
Soldering of a header on the board is required. Product should include M/F headers.
×1
SparkFun OBD-II to DB9 Cable
×1
16x2 Character LCD with I2C backpack
Many other vendors provide this LCD panel in different forms, however it is important to ensure it has an I2C backplate to align with this projects code.
×1
SparkFun M/F DuPont style jumpers
×1
Micro SD Card
A common formatted Micro SD card
×1
Portable speaker with 3.5mm output jack
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Google Earth - KML overlays
Vehicle with an OBD-II conenctor
Designed for a vehicle with an OBD-II connector. These vehicles should be 1996 models or later sold in the United States. There are other standards which exist in other contras, for which wiring variation may be required.

Story

Read more

Custom parts and enclosures

Audible Numbers for speed call out

Contains mp3 files representing numbers 20 to 125 in increments of 5. The numbers are used for playback as the vehicle's speed reaches each point.

Schematics

Wiring a SPRESENSE

Wiring to LCD Display and SparkFun OBD-II UART on SPRENSE

Wiring an UNO

Wiring to LCD Display and SparkFun OBD-II UART on UNO

Code

ELM327/OBD-II-UART vehicle interfacing

Arduino
The following project reads from an OBD-II UART vehicle information; and while it calls out speeds at 5 mph increments will also continue to track and record the vehicles speed and location. Recorded information is kept on the onboard SD card in a KML format suitable for viewing on Google Earth.
// ------------------------------------------------------------------------------
// ELM327/OBD-II-UART interface 
// James Penner
// March 10, 2015, Updated January 1, 2018, Spritzer update February 22, 2018
//
// Spritzer version History
// - Pull request #64 02/24/2018 - Version 2.0 Spritzer with SD, GNSS, and without Audio
// - Pull request #74 03/11/2018 - Version 2.1 Spritzer with SD, GNSS, and with Audio
//
// This will pull the speed and RMP from the vehicle's computer through the SparkFun OBD-II-UART board and
// data will be displayed on a 2x16 LCD display.
//
// SparkFun OBD-II UART
// Programing elements based on an original Quickstart sketch and was written by Ryan Owens and Toni Klopfenstein
// for SparkFun Electronics 7/5/2011
//
// LCD HD44780 2x16
// - The LiquidCrystal library works with LCD displays that are compatible with the HD44780 and 12C backplate
// - I2C devices are connected to the Arduino, there address may vary. if required by the library you choose,
//   you may need to identify address allocated to the LCD panel. Usually, it is 0x27, but can vary. Check 
//   using an I2C scanning tool
// - If wiring with no I2C backplate, use the Arduino Liquid Crystal library though the IDE. However, considerable
//   more wiring is required
// - Using some libraries with the HD44780 board may result in issue with displaying a single (first) character
//   of a string. This issue is called out here (https://forum.arduino.cc/index.php?topic=365435.0) and may
//   need to modify the return value in the display fuction
// 
//Serial/SoftwareSerial
//- For the Arduino UNO, pins 0 & 1 are the same connections used by the IDE when flashing/programming. If used
//  for serial I/O to devices other than the IDE computer connection, these lines must be disconnected prior to
//  flashing/uploading. Alternatively, use the softwareSerial.h library and use two digital pins not in use
//  for Rx and Tx. The Sony Arduino Spritzer has 2 seperate UARTs available and UART 2 is associated with pins
//  1 & 2, while UART 1 is associated with the USB port
//
// Equipmnet
// - Sony Arduino Spritzer 5vdc out, Ground pins, 4 digital pins (D0/D1(Serial),D14/D15(I2C))
// - SparkFun OBD-II UART using ELM327 v1.3a
// - HP HD44780 LCD panel with I2C backplate (typical 2x16)
// - M-M/M-F jumper lines
//
// Arduino Jumper OBD-II  Jumper  Spritzer
// ---------------------------------------
//                NC
// 10-->RX Yellow TX-0    Yellow  -->RX D0
// 11<--TX Green  RX-1    Green   <--TX D1
//                NC
//                NC
// GND     Black  GND     Black   GND
// 
// LCD1602 2x16 panel Jumper  Arduino  Jumper Spritzer
// ----------------------------------------------------
// GND                Black   GND      Black  GND
// VCC(+5vdc)         Red     5vdc     Red    5vdc
// SDA                White   A4       White  14
// SCL                Blue    A5       Blue   15
//
//ELM327 AT Commands
//  OBDII.print("ATZ\r");             Used        // --> Reset OBD
//  OBDII.print("ATE0\r");            Used        // --> Echo off
//  OBDII.print("ATI\r");                         // --> Sparkfun OBD-II response "ELM327 v1.3a"
//OBD-II PID Codes
//  OBDII.print("010C\r");            Used        // --> Vehicle RPM - 1/4RPM
//  OBDII.print("010D\r");            Used        // --> Vehicle speed - km/hr
//  OBDII.print("015C\r");-No Data                // --> Oil Temperature in Celcius
//  OBDII.print("012F\r");                        // --> Fuel gauge 0-100% (255=100%)
//  OBDII.print("0142\r");-No Data                // --> Battery Voltage in vdc
//  OBDII.print("0105\r");                        // --> Water Temperature in Celcius
//  OBDII.print("010F\r");                        // --> Intake Air Temperature
//  OBDII.print("0146\r");-No Data                // --> Ambient Temperature
//  OBDII.print("0111\r");                        // --> Throttle 0-100% (255=100%)
//  OBDII.print("0110\r");-No Data                // --> Air Flow
//  OBDII.print("0133\r");-No Data                // --> Barometric Preasure
// 
// ------------------------------------------------------------------------------
// Revision plans for the Arduino UNO Spritzer from Sony
// February 8-28, 2018
// Features added
// - GPS (GNSS) locating
// - SD card use for data logging and audio
// - Google Earth data loging (KML format)
// - Audible speed callouts at 35,40,45,50,55,60,65,70,75,80,85,90,95 KPM
//
// Open items as of 2/22/2018
// 1.  SD libraries, and method used in code does not support multiple file management properly.
//     As a result, if VER_AUDIO is defined, audible speed is heard by playing mp3 files from the
//     SD card, however the KML file will fail to be recognized as open. If the VER_AUDIO is not
//     defined, KML file logging works fine, however not mp3 audio. (Accepted for now)
// 2.  Sometimes an error from the media player occurs. This error seems not to be an issue with
//     functionality as the mp3 file plays sucessfully. (To be passed on Sony/Github)
//     - Attention: module[5] attention id[1]/code[5] (media_player/media_player_obj.cpp L794)
//     - attention!! ecode 1 subcode 5
// 
// Arduino UNO/Spritzer differences:
//   Serial communication: Spritzer is designed very much like the Arduino Mega which has 2 UARTs.
//     Although engineered differntly and unlike the UNO, the spritzer uses UART2 on D0 and D1, will
//     directing flashing and debug monitoring to UART across the USB port. As long as there is one
//     serial project designed serial communication, UART2 is a good choice and there is no need to
//     integrate SoftwareSerial.h library.
//   I2C: The UNO uses A4/A5 for SCL/SDA, while the Spitzer uses D14/D15 for SDA/SCL on the main 
//     or extension board
//
// Operation:
//   The OBD, once connected, will continue to cycle through producing readings from
//   the vehicle. Once the GNSS is signaling/location is received, the logging begins. To close
//   the logging the OBD connector is to be removed, and remain inactive for 20-30 seconds before the
//   file is closed.
//
// Compiler Program Control
// - VER_SPRITZER - Not used. Intended for making the code portable between differnet boards
// - VER_MONITORING - If defined will send some debuging messages over IDE monitor
// - VER_AUDIO - If defined incorporates the Audio component. Doing so will result in no KML loggging
// - VER_OBD - If not defined will run, simulating OBD input (Check SD>file>KML) 
//
// To incorporate:
//   Read files and play
//   If no files exist Do not play
//   Use Spritzer codec/audio 
//   Do not call out the same speed as last call
//   Record as mp3 files on SD card. Read and play
//
// Key GNSS data elements:
//   pNavData->latitude                          // Latitude
//   pNavData->longitude                         // Longitude
//   pNavData->time.year                         // Year
//   pNavData->time.month                        // Month
//   pNavData->time.day                          // Day
//   pNavData->time.hour                         // Hour
//   pNavData->time.minute                       // Minutes
//   fsec                                        // Seconds in floating point notation
//   pNavData->getSatelliteElevation(1)          // Elevation of Satellite 1
// ------------------------------------------------------------------------------
#define VER_SPRITZER                            // Compile for Spritzer or UNO
//#define VER_MONITORING                          // If defined send some debuging messages over IDE monitor
//#define VER_AUDIO                               // If defined incorporates the Audio component
#define VER_OBD                                 // If not defined will run simulating OBD input (Check SD>file>KML) 

#include <LiquidCrystal_I2C.h>                  // Include the LCD I2C library
LiquidCrystal_I2C lcd(0x27,16,2);               // Set alias and I2C address of 16x2 character display
#include <GNSS.h>                               // Include the Sony Spritzer GPS library
SpGnss gnss;                                    // Set the alias to the library functions
#include <SPI.h>                                // Supports SD card interface
#include <SD.h>                                 // Include the Sony Spritzer SD card library
#define SDCARD_SS_PIN A3                        // Define SD card Slave Select on A3
SDClass mySD(SPI4);                             // Use Serial Process Interface (SPI) 4
#ifdef VER_AUDIO
  #include <Audio.h>                            // Include the Sony Spritzer Audio library
  #include <fcntl.h>
  AudioClass myAudio;                          // Instanciate the Audio class
#endif

#define PINRX       0                           // D0 - Spritzer RX --> OBDII(TX) - Yellow
#define PINTX       1                           // D1 - Spritzer TX <-- OBDII(RX) - Greeen

char rxData[32];                                // Character buffer for OBD data recieved. Assumes short bursts
char rxIndex=0;                                 // Array index
char buf[10];                                   // for fprint ourput
int vehicleSpeed=0;                             // Vehicle Speed
int lastSpeed=0;                                // Last Vehicle Speed - Used on Audio
int vehicleRPM=0;                               // Vehicle RPMs
unsigned long markTime;                         // Time mark for the delay timer
unsigned long markTime2;                        // Time mark for the OBD timeout
bool timeoutOBD = false;                        // Timeout on OBD response
int LastPrintMin = 0;                           // Marker for GNSS 1 minute check
SpNavData *pNavData;                            // Pointer for NavData
String sdString;                                // Character Array to capture data and write to SD card
int caLen;                                      // Character Array Length
char caString[36];                              // Character Array for formating String
String fileName;                                // Name of the file to use
File myGNSSFile;                                // File handle for GNSS KML file
#ifdef VER_AUDIO
  File myMP3File;                               // File handle for MP3 Audio file
#endif
bool sdCardOK = true;                           // Have to initialize SD card, will let error set to false
bool switchOn = true;                           // State of operation for SD file
bool fileClosed = true;                         // State of last processed file
bool fileStart = false;                         // Inication that file open an header written

#ifndef VER_OBD
  int tmpCnt =0;                                // Used to debug standing alone (OBD not connected)
#endif

void setup(){
//  timeoutOBD = false;                   // Not needed? Used when debuging memory issue
  
  Serial.begin(9600);
  while(!Serial){
    ; // wait for COM port to open/connect; USB Monitoring
  }
  waitTime(1000);
  Serial.println("IDE Monitoring: OK");  

#ifdef VER_OBD
  Serial2.begin(9600);
  while(!Serial2){
    ; // wait for OBD-II UART port to open/connect; vehicle Monitoring
  }
#endif
  waitTime(1000);
  Serial.println("OBD-II UART: Is listenting");  
  
  gnss.setDebugMode(PrintInfo);                              // Put GPS readings into a debug mode
  int r;
  r = gnss.begin();                                          // Initialize GNSS handling
  if (r != 0){                                               // Error encountered
    Serial.print("GNSS: Unable to initialize! (");Serial.print(r);Serial.println(")");
  }else{
    r = gnss.start(COLD_START);                              // Start GNSS listening
    if (r != 0){                                             // Error encountered
      Serial.print("GNSS: Unable to start! (");Serial.print(r);Serial.println(")");
    }else{
      Serial.println("GNSS: Is online/listening");           // GSNN is online
    }
  }

  if (mySD.begin(SDCARD_SS_PIN)){
   Serial.println("SD Card: Initialized!");
   sdCardOK = true;
  }else{ 
   Serial.println("SD Card: Initialization failed!");
   sdCardOK = false;
  }

#ifdef VER_AUDIO
  myAudio.begin();
  myAudio.setPlayerMode(AS_OUT_SP);
  myAudio.initPlayer(AS_INITPLAYER_MP3,
                      AS_INITPLAYER_INPUT_FS_44100,
                      AS_INITPLAYER_CHNL_STEREO);
  Serial.println("Audio setup complete");
#endif
  
  lcd.init();                                               // Initialize LCD Display
  waitTime(500);
  lcd.backlight();                                          // Initialize backlight display
  waitTime(500);
  lcd.clear();                                              // Clear the display
  waitTime(500);
  lcd.setCursor(0, 0);                                      // Set the cursor to column 0, line 0
  lcd.print("ELM327 Reader   ");                            // Print template line 1
  lcd.setCursor(0, 1);                                      // Set the cursor to column 0, line 1
  lcd.print("Starting up.... ");                            // Print template line 2
  waitTime(1500);
  lcd.clear();                                              // Clear the display
  lcd.setCursor(0, 0);                                      // Set the cursor to column 0, line 0
  lcd.print("Speed:");                                      // Print a message to the LCD.
  lcd.setCursor(0, 1);                                      // Set the cursor to column 0, line 1
  lcd.print("RPM:");                                        // Print a message to the LCD.

#ifdef VER_AUDIO
  lcd.setCursor(14, 1);                                     // Set the cursor to column 14, line 1
  lcd.print("A");                                           // Print a message to the LCD.
#endif
#ifdef VER_OBD
  InitOBD();                                                // Initilize the connection
  Led_On(LED0);
  lcd.setCursor(15, 1);                                     // Set the cursor to column 15, line 1
  lcd.print("O");                                           // Print a message to the LCD.
#endif
}

boolean getNavData(SpNavData *pNavData){
  boolean ret;
  if (pNavData->posDataExist == 0)  {
    Serial.println("GNSS: Position is not yet fixed");
    ret = false;
  }else{
    float fsec = (float)pNavData->time.sec + ((float)pNavData->time.usec / 1000000);
    ret = true;
  }
  return ret;
}

void loop(){

#ifndef VER_OBD
  tmpCnt++;
  Serial.print("Count: ");
  Serial.println(tmpCnt);
#endif

// --- Get switch state: -----------------------------------------------------------------------------
// --- To be replaced if external switch is addded for "switchOn"
// --- Read external switch state and set "switchOn" accordinly
// --- Get switch state: -----------------------------------------------------------------------------

// --- Get Vehicle Speed -----------------------------------------------------------------------------
#ifdef VER_OBD
  vehicleSpeed = getSpeed();                         // Get the Speed from the car via OBD interface
  lcd.setCursor(8, 0);                               // Set the cursor to column 8, line 0
  sprintf(buf,"%03d",vehicleSpeed);                  // Format reading
  lcd.print(buf);                                    // Display reading
  lcd.print(" km/h");
  waitTime(100);
#else
  vehicleSpeed = 36;                                 // Dummy up a vehicle Speed for non OBD run
#endif

#ifdef VER_AUDIO
// --- Audible vehicle Speed -------------------------------------------------------------------------
  if ((vehicleSpeed>=25)&&(vehicleSpeed<=125)){                 // Acceptible range 
    if ((vehicleSpeed%5)==0){                                   // On a divisible by 5?
      if (vehicleSpeed!=lastSpeed){                             // We dont want to keep repeating ourselves!
        Serial.print("Audio: vehicleSpeed is ");Serial.println(vehicleSpeed);
        openPlay(vehicleSpeed);                                 // Play the Audio of the Vehicle speed
        lastSpeed=vehicleSpeed;                                 // Reset the lastSpeed as not to repeat
      }
    }
  }
#endif
  
// --- Get Vehicle RPMs ------------------------------------------------------------------------------
#ifdef VER_OBD
  vehicleRPM = getRPM();                                        // Get the RPMs from the car via OBD interface
  lcd.setCursor(8, 1);                                          // Set the cursor to column 8, line 1
  sprintf(buf,"%04d",vehicleRPM);                               // Format reading
  lcd.print(buf);                                               // Display reading
  waitTime(100);
#else
  vehicleRPM = 1000;                                            // Dummy up a vehicle Speed for non OBD run
#endif

// --- Get Vehicle Location --------------------------------------------------------------------------
  if (gnss.waitUpdate(-1)){
    SpNavData NavData;
    gnss.getNavData(&NavData);

    if (getNavData(&NavData)){                                  // Get updated position data if available
      Led_On(LED1);                                             // Indicate satillites acquired
      pNavData = &NavData;
      Serial.print("GNSS: Found Satellite! ");Serial.print(pNavData->longitude, 6);Serial.print(", ");Serial.print(pNavData->latitude, 6);Serial.print(", ");Serial.println(vehicleSpeed);
      caLen = sprintf(caString, "%3.6f,%3.6f,%d\0", pNavData->longitude, pNavData->latitude, (int)(vehicleSpeed+100));
      sdString = caString;

      if(timeoutOBD==true){                                     // Did we have a timeout? (Changed as "if(timeoutOBD){" did not work 
        switchOn = false;                                       // Lost connection with OBD, drive to close the file
        Led_Off(LED0);                                          // Lost connection with OBD, kill status LED
        timeoutOBD = false;                                     // Reset timeout
      }

// --- File handling only while GNSS data is available -----------------------------------------------------
      if (sdCardOK){                                            // SD Card OK..
        if (switchOn){                                          // Switch to record to file still ON...
          Serial.println("Switch is On");
          if (fileClosed){                                      // File not open, then Open...
            caLen = sprintf(caString, "vl%02d%02d%02d.kml\0", pNavData->time.day, pNavData->time.hour, pNavData->time.minute);
            Serial.print("File=");Serial.print(caString);Serial.print("<");Serial.print(caLen);Serial.println(">");
            fileName = caString;
            myGNSSFile = mySD.open(fileName,FILE_WRITE);        // Format = "vl",dd=day,hh=hr,mm=min,".kml"
            if (myGNSSFile){                                    // Opened OK
              Serial.println("SD Card: File opened!");
              Led_On(LED2);                                     // Logging begun
              fileClosed=false;                                 // File is Open. No longer closed
              writeKMLheader();                                 // Write KML header
              fileStart = true;                                 // Indicate we have started file with header  
            }else{
              Serial.println("SD Card: File Open error!");
              switchOn = false;
            }
          }
          if (!fileClosed){
            if (myGNSSFile){
              Serial.println("MyGNSSfile: File is open");
              if (fileStart){                                   // Header already KML written
                fileStart = false;                              // Switch to KML details
                Serial.println("MyGNSSfile: Header will be written, switching to write coordinates");
              }else{                                            // With this we will miss 1st itteration, but OK
                writeKMLcoordinates(sdString);                  // Write KML details
                Serial.println("MyGNSSfile: Coordinates written");
              }
            }else{
              switchOn = false;                                 // Lost the file handle
              Serial.println("SD card: Lost the file handle or File error");
            }
          }
        }else{                                                  // Indication to close file
          Serial.println("Switch is Off");
          writeKMLfooter();                                     // Write KML footet
          myGNSSFile.flush();                                   // Push residule data from bus to file
          myGNSSFile.close();                                   // Close file 
          Led_Off(LED2);                                        // Logging ended
          fileClosed=true;                                      // file is closed
          waitTime(60000);                                      // Delay to give a subsequent opportunity to record
          switchOn = true;                                      // Reset the switch to start again
#ifdef VER_OBD
  InitOBD();                                            // Re-Initilize the connection
#endif
        }
      }else{
        Serial.println("SD Card: Failed to initialize in setup!");
      }             
    }else{
      Serial.println("GNSS: No satellite discovered");
      Led_Off(LED1);                                          // Indicate satillites NOT acquired
    }
  }else{
    Serial.println("GNSS: Data not updated");                 // Catch if data not updated
  }
#ifndef VER_OBD
  if (tmpCnt > 900){                                          // Terminated after 900 itterations (~12min)
    timeoutOBD = true;
    tmpCnt = 0;
  }
#endif
}

//------------------------------------------------------------------------
// Write to SD KML Header
//------------------------------------------------------------------------
void writeKMLheader(void){
  Serial.println("KML Header");
  sdString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; myGNSSFile.println(sdString);
  sdString = "<kml xmlns=\"http://www.opengis.net/kml/2.2\">"; myGNSSFile.println(sdString);
  sdString = "<Document>"; myGNSSFile.println(sdString);
  sdString = "<name>Paths</name>"; myGNSSFile.println(sdString);
  sdString = "<description>Vehicle pathing from OBD-II UART and Sony Spritzer. James Penner February 14, 2018</description>"; myGNSSFile.println(sdString);
  sdString = "<Style id=\"yellowLineGreenPoly\">"; myGNSSFile.println(sdString);
  sdString = "<LineStyle>"; myGNSSFile.println(sdString);
  sdString = "<color>7f00ffff</color>"; myGNSSFile.println(sdString);
  sdString = "<width>4</width>"; myGNSSFile.println(sdString);
  sdString = "</LineStyle>"; myGNSSFile.println(sdString);
  sdString = "<PolyStyle>"; myGNSSFile.println(sdString);
  sdString = "<color>7f00ff00</color>"; myGNSSFile.println(sdString);
  sdString = "</PolyStyle>"; myGNSSFile.println(sdString);
  sdString = "</Style>"; myGNSSFile.println(sdString);
  sdString = "<Placemark>"; myGNSSFile.println(sdString);
  sdString = "<name>Absolute Extruded</name>"; myGNSSFile.println(sdString);
  sdString = "<description>Transparent green wall with yellow outlines</description>"; myGNSSFile.println(sdString);
  sdString = "<styleUrl>#yellowLineGreenPoly</styleUrl>"; myGNSSFile.println(sdString);
  sdString = "<LineString>"; myGNSSFile.println(sdString);
  sdString = "<extrude>1</extrude>"; myGNSSFile.println(sdString);
  sdString = "<tessellate>1</tessellate>"; myGNSSFile.println(sdString);
  sdString = "<altitudeMode>absolute</altitudeMode>"; myGNSSFile.println(sdString);
  sdString = "<coordinates>"; myGNSSFile.println(sdString);
}

//------------------------------------------------------------------------
// Write to SD KML coordinates & Speed. Example: -112.2549277039738, 36.08117083492122, 75
//------------------------------------------------------------------------
void writeKMLcoordinates(String& sdString){
  Serial.print("KML coordinates: ");
  Serial.println(sdString);
  myGNSSFile.println(sdString);
}

//------------------------------------------------------------------------
// Write to SD KML footer
//------------------------------------------------------------------------
void writeKMLfooter(void){
  Serial.println("KML footer");
  sdString = "</coordinates>"; myGNSSFile.println(sdString);
  sdString = "</LineString>"; myGNSSFile.println(sdString);
  sdString = "</Placemark>"; myGNSSFile.println(sdString);
  sdString = "</Document>"; myGNSSFile.println(sdString);
  sdString = "</kml>"; myGNSSFile.println(sdString);
}

//------------------------------------------------------------------------
// Get vehicle speed km/h
//------------------------------------------------------------------------
int getSpeed(void){
  Serial2.print("010D\r");                            // Get from OBD-II the --> Vehicle speed
  waitTime(50);                                       // Wait for reposne to complete before reading
  readOBD();                                          // Read response from board
#ifdef VER_MONITORING
  Serial.print("KMH:[010D], Receive:[");Serial.print(rxData);
  Serial.print("] Hex2DEC:[");Serial.print(strtol(&rxData[6],NULL,16));Serial.println("]");
#endif
  return strtol(&rxData[6],NULL,16);                  // "41 0D XX" --> Single byte value XX
}

//------------------------------------------------------------------------
// Get vehicle engine RPM
//------------------------------------------------------------------------
int getRPM(void){
  Serial2.print("010C\r");                           // Get from OBD-II the --> Vehicle RPM
  waitTime(50);                                      // Wait for reposne to complete before reading
  readOBD();                                         // Read response from board
#ifdef VER_MONITORING
  Serial.print("RPM:[010C], Receive:[");Serial.print(rxData);Serial.println("]");
#endif
  // Calculation to RPM is based on the following:
  // - "41 0C XX YY" --> Two byte value XX YY
  // - ((high order XX byte *256)+low order YY byte) / 4
  // - RPM value is in 1/4 RPM, divide by 4 to get whole RPM
  return ((strtol(&rxData[6], NULL, 16) * 256) + strtol(&rxData[9], NULL, 16)) / 4;
}

//------------------------------------------------------------------------
// Get vehicle Oil Temperature in Celcius
//------------------------------------------------------------------------
int getOil(void){
  Serial2.print("015C\r");                            // Get from OBD-II the --> Oil Temperature in Celcius
  waitTime(50);                                       // Wait for reposne to complete before reading
  readOBD();                                          // Read response from board
  return int(((strtol(&rxData[6], NULL, 16)*9.0)/5.0 + 32) - 40);      // "41 5C XX" --> Single byte value XX - 40
}

//------------------------------------------------------------------------
// Get vehicle Fuel gauge 0-100%
//------------------------------------------------------------------------
int getFuel(void){
  Serial2.print("012F\r");                            // Get from OBD-II the --> Fuel gauge 0-100%
  waitTime(50);                                       // Wait for reposne to complete before reading
  readOBD();                                          // Read response from board
  return strtol(&rxData[6], NULL, 16);                // "41 2F XX" --> Single byte value XX
}

//------------------------------------------------------------------------
// Get vehicle Battery Voltage in vdc
//------------------------------------------------------------------------
int getBatt(void){
  Serial2.print("0142\r");                            // Get from OBD-II the --> Battery Voltage in vdc
  waitTime(50);                                       // Wait for reposne to complete before reading
  readOBD();                                          // Read response from board
  // Calculation to vdc is based on the following:
  // - "41 42 XX YY" --> Two byte value XX YY
  // - ((high order XX byte *256)+low order YY byte) / 1000
  // - Divide by 1000 to get decimal
  return ((strtol(&rxData[6], NULL, 16) * 256) + strtol(&rxData[9], NULL, 16)) / 1000;
}

//------------------------------------------------------------------------
// Get car Water Temperature in Celcius
//------------------------------------------------------------------------
int getCoolant(void){
  Serial2.print("0105\r");                            // Get from OBD-II the --> Water Temperature in Celcius
  waitTime(50);                                       // Wait for reposne to complete before reading
  readOBD();                                          // Read response from board
  return int(((strtol(&rxData[6], NULL, 16)*9.0)/5.0 + 32) - 40);      // "41 05 XX" --> Single byte value XX - 40
}

//------------------------------------------------------------------------
// Get car odometer reading in Kilometers
//------------------------------------------------------------------------
int getOdometer(void){
  Serial2.print("0131\r");                           // Get from OBD-II the --> Odometer reading Kilometers
  waitTime(50);                                      // Wait for reposne to complete before reading
  readOBD();                                         // Read response from board
  // Calculation to Kilometers is based on the following:
  // - "41 0C XX YY" --> Two byte value XX YY
  // - ((high order XX byte *256)+low order YY byte) / 4
  return ((strtol(&rxData[6], NULL, 16) * 256) + strtol(&rxData[9], NULL, 16));
}

//------------------------------------------------------------------------
// Get car Intake Air Temperature in Celcius
//------------------------------------------------------------------------
int getIntakeAir(void){
  Serial2.print("010F\r");                            // Get from OBD-II the --> Intake Air Temperature in Celcius
  waitTime(50);                                       // Wait for reposne to complete before reading
  readOBD();                                          // Read response from board
  return int(((strtol(&rxData[6], NULL, 16)*9.0)/5.0 + 32) - 40);      // "41 0F XX" --> Single byte value XX - 40
}

//------------------------------------------------------------------------
// Get car Intake Air Flow Rate
//------------------------------------------------------------------------
int getAirFlowRate(void){
  Serial2.print("0110\r");                            // Get from OBD-II the --> Air Flow Rate
  waitTime(50);                                       // Wait for reposne to complete before reading
  readOBD();                                          // Read response from board
  // Calculation to vdc is based on the following:
  // - "41 10 XX YY" --> Two byte value XX YY
  // - ((high order XX byte *256)+low order YY byte) / 100
  // - Divide by 100 to get decimal
  return ((strtol(&rxData[6], NULL, 16) * 256) + strtol(&rxData[9], NULL, 16)) / 100;
}
   
//------------------------------------------------------------------------
// Get car Ambient Temperature in Celcius
//------------------------------------------------------------------------
int getAmbientTemp(void){
  Serial2.print("0146\r");                              // Get from OBD-II the --> Ambient Temperature in Celcius
  waitTime(50);                                       // Wait for reposne to complete before reading
  readOBD();                                          // Read response from board
  return int(((strtol(&rxData[6], NULL, 16)*9.0)/5.0 + 32) - 40);      // "41 46 XX" --> Single byte value XX - 40
}

//------------------------------------------------------------------------
// Get car Trottle position
//------------------------------------------------------------------------
int getTrottlePos(void){
  Serial2.print("0111\r");                            // Get from OBD-II the --> Trottle position
  waitTime(50);                                       // Wait for reposne to complete before reading
  readOBD();                                          // Read response from board
  return strtol(&rxData[6], NULL, 16);                // "41 11 XX" --> Single byte value XX
}

//------------------------------------------------------------------------
// Get car Barometric Preasure
//------------------------------------------------------------------------
int getBarometric(void){
  Serial2.print("0133\r");                            // Get from OBD-II the --> Barometric Preasure
  waitTime(50);                                       // Wait for reposne to complete before reading
  readOBD();                                          // Read response from board
  return strtol(&rxData[6], NULL, 16);                // "41 33 XX" --> Single byte value XX
}

//------------------------------------------------------------------------
// Initialize interface settings on the OBD board
//------------------------------------------------------------------------
void InitOBD(void){
  waitTime(1000);                                     // Wait for a second before resetting the OBD-II
  Serial2.print("ATZ\r");                             // --> Reset OBD
  waitTime(200);                                      // Wait for reposne to complete before reading
  readOBD();                                          // Read response from board
  Serial2.print("ATE0\r");                            // --> Echo off
  waitTime(200);                                      // Wait for reposne to complete before reading
  readOBD();                                          // Read response from board
}

//------------------------------------------------------------------------
// Read an OBD response from the borard and build the character array
//------------------------------------------------------------------------
void readOBD(void){
  char c;
  rxIndex = 0;                                        // Reset buffer character index
  markTime2 = millis();                               // Mark start time
  do{
    if (Serial2.available() > 0){                     // Characters available to process
      c = Serial2.read();                             // Read a character
      if (rxIndex==31) c = '>';                       // Force termination as we ran out of array space
      if ((c!='>')&&(c!='\r')&&(c!='\n')){            // Accept only usable charcters
        rxData[rxIndex++] = c;                        // Add the character to the buffer
        Serial.print(c);
      }
    }else{
      if ((millis()-markTime2) > 20000){              // Timeout after 20 seconds of no OBD response
       Serial.println("OBD-II read timed out!");
       timeoutOBD = true;
      }
    }
  }while ((c!='>')&&(timeoutOBD==false));             // The ELM327 ends with ">" as a command prompt for next command
  Serial.println(rxIndex);
  rxData[rxIndex++] = '\0';                           // Terminate the buffer with null; complete a string
} 

//------------------------------------------------------------------------
// To play audio mp3 files representing numbers. Used to call out speed in increments of 5km/h
//------------------------------------------------------------------------
#ifdef VER_AUDIO
void openPlay(int acnt){
  caLen = sprintf(caString, "%d.MP3\0", acnt);
  fileName = caString;
  myMP3File = mySD.open(fileName);                    // Default read mode
#ifdef VER_MONITORING
  Serial.print("File [");Serial.print(fileName);Serial.print("] is opened. Len=");Serial.println(caLen);
#endif
  int err = myAudio.writeFrames(myMP3File);
  if(err != 0){
    Serial.print("File read error (");Serial.print(err);Serial.println(")");
  }
  myAudio.setVolume(-300);
  myAudio.startPlayer();
  do{
    int err = myAudio.writeFrames(myMP3File);
#ifdef VER_MONITORING
  Serial.print("Error = ");
  Serial.println(err);
#endif
    if(err==1){
      Serial.println("End of file!");
      sleep(1);
      myAudio.stopPlayer();
      break;
    }
    usleep(40000);
  }while(err!=1);
}
#endif

//------------------------------------------------------------------------
// Suplement to delay() fucntion as delay() does not pause all actions and is asynchronous. 
//------------------------------------------------------------------------
void waitTime(int milsec){
  markTime = millis();
  while (millis()-markTime < milsec){
  }
}

Credits

jpenner64

jpenner64

3 projects • 5 followers

Comments