//
// Teensy 4.x CAN bus monitor utility - version 1.0 dated 20220926-2030
//
// - designed & written by Mark J Culross (KD5RXT)
//
// - credit & thanks to PaulS (https://forum.pjrc.com/members/39362-PaulS) for his very helpful posts on the Teensy forum
//
// - includes the following:
// - ability to change baudrate on-the-fly (manually or automatically)
// - ability to send CAN wakeup commands (manually or with each baudrate change)
// - ability to execute the 5-baud 0x33 K-line init sequence
// - ability to determine & query the supported PIDs associated with service 0x01 (show current data)
// - ability to determine & query the supported PIDs associated with service 0x02 (show freeze frame data)
// - ability to determine & query any stored DTCs by requesting service 0x03 (show stored DTCs)
// - ability to determine & query the supported PIDs associated with service 0x09 (Vehicle information)
// - ability to detect & process either standard CAN frames (typical 8 msg payload bytes) or extended isotp CAN frames (up to 512 msg payload bytes]
//
// - configuration is controlled via the USB serial interface
//
// - all data & status is reported via the USB serial interface
//
//
// Arduino IDE Configuration (last built with Arduino 1.8.19 + Teensyduino 1.58b2):
// Tools/Board: "Teensy 4.0"
// Tools/USB Type: "Serial"
// Tools/CPU Speed: "600MHz"
// Tools/Optimize: "Smallest Code"
// Tools/Keyboard Layout: "US English"
// Tools/Port: "COMx Serial (Teensy 4.0)"
//
const String TITLE = "Teensy CAN bus monitor utility";
const String VERSION = "version 1.0 dated 09/26/2022 @2030";
const String AUTHOR = "designed & written by Mark J Culross (KD5RXT)";
#include <FlexCAN_T4.h>
#include <EEPROM.h>
#include <isotp.h>
isotp<RX_BANKS_16, 512> isotp1; /* 16 slots for multi-ID support, at 512bytes buffer each payload rebuild */
FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can1;
//#define DEBUG_EEPROM_WRITE // uncomment to show specifics of EEPROM writes
//#define DEBUG_EEPROM_READ // uncomment to show specifics of EEPROM reads
//#define DISABLE_EEPROM_READ_SETTINGS // uncomment to not read settings from EEPROM (to simply use program defaults for all settings)
//#define DISABLE_EEPROM_WRITE_SETTINGS // uncomment to not write settings to EEPROM
//
// The following pins are used in this (Audio) portion of the Teensy 4.x PolySynth project:
//
// PIN D0 = (not used)
// PIN D1 = (not used)
// PIN D2 = (not used)
// PIN D3 = (not used)
// PIN D4 = (not used)
// PIN D5 = (not used)
// PIN D6 = (not used)
// PIN D7 = (not used)
// PIN D8 = (not used)
// PIN D9 = (not used)
// PIN D10 = (not used)
// PIN D11 = (not used)
// PIN D12 = (not used)
// PIN D13 = (onboard LED)
// PIN D14/A0 = (not used)
// PIN D15/A1 = (not used)
// PIN D16/A2 = (not used)
// PIN D17/A3 = (not used)
// PIN D18/A4 = (not used)
// PIN D19/A5 = (not used)
// PIN D20/A6 = (not used)
// PIN D21/A7 = K-line for 5 baud init (to base of 2N2222A NPN transistor)
// PIN D22/A8 = CAN1 TX (to CTX on SN65HVD230 breakout board)
// PIN D23/A9 = CAN1 RX (to CRX on SN65HVD230 breakout board)
// PIN D24/A10 = (not used)
// PIN D25/A11 = (not used)
// PIN D26/A12 = (not used)
// PIN D27/A13 = (not used)
// PIN D28 = (not used)
// PIN D29 = (not used)
// PIN D30 = (not used)
// PIN D31 = (not used)
// PIN D32 = (not used)
// PIN D33 = (not used)
// PIN D34 = (not used)
// PIN D35 = (not used)
// PIN D36 = (not used)
// PIN D37 = (not used)
// PIN D38/A14 = (not used)
// PIN D39/A15 = (not used)
// PIN D40/A16 = (not used)
// PIN D41/A17 = (not used)
// linefeed
#define LF 0x0A
// onboard LED on pin 13
#define LED_PIN 13
// conversion constants
#define MILES_PER_KM 0.62137119224
// pin 21 for K-line 5-baud init toggling
#define KLINE_PIN 21
// since the K-line pin is driving thru the base of an NPN transistor, the drive level is inverted
#define KLINE_LOW HIGH
#define KLINE_HIGH LOW
unsigned long LED_timer;
#define LED_DURATION_MSEC 5
// how long to stay on a given baudrate, looking for packets
unsigned long CAN_baud_change_timer = millis();
#define CAN_BAUD_CHANGE_INTERVAL_MSEC 500
bool debug_CAN_rx = false;
#define LED_INTENSITY_OFF 0
#define LED_INTENSITY_ON_NORMAL 31
#define LED_INTENSITY_ON_BRIGHT 255
int led_intensity_on = LED_INTENSITY_ON_NORMAL;
// array of CAN baudrates
const uint32_t CAN_baudrate_list[] = { 5000, 10000, 20000, 25000, 33333, 50000, 100000, 125000, 200000, 250000, 400000, 500000, 800000, 1000000 };
int CAN_baudrate_size = sizeof(CAN_baudrate_list) / sizeof(uint32_t);
int CAN_baudrate_index;
volatile bool CAN_baudrate_fixed = false;
volatile bool CAN_baudrate_match_found = false;
// how long to wait for more packets (timing out) before scanning thru the baudrate list
#define CAN_BAUDRATE_MATCH_TIMEOUT_MSEC 5000
volatile unsigned long CAN_baudrate_match_timeout = millis();
unsigned long last_CAN_TX_millis = millis();
#define INTER_CAN_MSG_TX_IN_MILLIS 100
#define CAN_DIAGNOSTIC_BROADCAST_REQUEST_0X7DF 0x7DF
#define CAN_DIAGNOSTIC_SESSION_REQUEST_0X7E0 0x7E0
#define CAN_DIAGNOSTIC_SESSION_REPLY_0X7E8 0x7E8
#define CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1 0X18DB33F1
#define CAN_EXTENDED_DIAGNOSTIC_SESSION_CONTROL_0X01 0x01
#define CAN_EXTENDED_DIAGNOSTIC_SESSION_0X00 0x00
#define CAN_DIAGNOSTIC_SESSION_CONTROL_0X10 0x10
#define CAN_EXTENDED_DIAGNOSTIC_SESSION_0X03 0x03
#define CAN_STANDARD_SERVICE_MODE_1_0x01 0x01
#define CAN_STANDARD_SERVICE_MODE_2_0x02 0x02
#define CAN_STANDARD_SERVICE_MODE_3_0x03 0x03
#define CAN_STANDARD_SERVICE_MODE_9_0x09 0x09
#define CAN_SERVICE_1_PIDS_SUPPORTED_01_20_0X00 0x00
#define CAN_SERVICE_1_PIDS_SUPPORTED_21_40_0X20 0x20
#define CAN_SERVICE_1_PIDS_SUPPORTED_41_60_0X40 0x40
#define CAN_SERVICE_1_PIDS_SUPPORTED_61_80_0X60 0x60
#define CAN_SERVICE_1_PIDS_SUPPORTED_81_A0_0X80 0x80
#define CAN_SERVICE_1_PIDS_SUPPORTED_A1_C0_0XA0 0xA0
#define CAN_SERVICE_1_PIDS_SUPPORTED_C1_E0_0XC0 0xC0
#define CAN_SERVICE_1_PIDS_SUPPORTED_E1_FF_0XE0 0xE0
#define MAX_T4X_EEPROM_SIZE_ALLOWED 4284
// header: "Teensy CAN Bus Monitor"
const char EEPROM_HEADER[] = "TCBM";
bool supported_PIDs_for_service_01[256];
bool supported_PIDs_for_service_09[33];
bool service_09_supported_PID_response_rxd = false;
int DTC_count = 0xFF; // where 0xFF means that Service 0x01 PID 0x01 has not yet been sent/received
typedef enum
{
EEPROM_INDEX_HEADER_00 = 0, EEPROM_INDEX_HEADER_01, EEPROM_INDEX_HEADER_02, EEPROM_INDEX_HEADER_03,
EEPROM_INDEX_CAN_BAUDRATE_FIXED,
EEPROM_INDEX_CAN_BAUDRATE_INDEX,
EEPROM_INDEX_LED_BRIGHTNESS,
EEPROM_INDEX_DEBUG_CAN_RX,
EEPROM_INDEX_CHECKSUM, EEPROM_INDEX_INV_CHECKSUM,
EEPROM_INDEX_VALUE_COUNT
} EEPROM_INDEX;
#define EEPROM_INDEX_HEADER_FIRST EEPROM_INDEX_HEADER_00
#define EEPROM_INDEX_HEADER_LAST EEPROM_INDEX_HEADER_03
typedef enum
{
KEY_STATE_CMD_KEY = 0, KEY_STATE_C_CMD_HEX0, KEY_STATE_C_CMD_HEX1, KEY_STATE_C_CMD_HEX2, KEY_STATE_C_CMD_HEX3, KEY_STATE_C_CMD_HEX4, KEY_STATE_C_CMD_HEX5,
} KEY_STATE;
int key_state = KEY_STATE_CMD_KEY;
// function headers
void can_sniff_RX(const CAN_message_t &msg);
void can_sniff_RX_isotp(const ISOTP_data &config, const uint8_t *buf);
void can_sniff_TX(const CAN_message_t &msg);
void can_TX(const CAN_message_t &msg);
void common_decode_service_01_02_PID_content(int response, int pid_num, int A, int B, int C, int D, int E);
void common_decode_service_09_PID_content(int response, int pid_num, int A, int B, int C, int D, int E);
void decode_isotp_service_01_PID_content(int msg_char_count, const uint8_t *buf);
void decode_isotp_service_09_PID_content(int msg_char_count, const uint8_t *buf);
void decode_service_01_02_PID_content(const CAN_message_t &msg);
void decode_service_01_02_PID_title(int response, int service);
void decode_service_09_PID_content(const CAN_message_t &msg);
void decode_service_09_PID_title(int pid_num);
void execute_kline_5_baud_init(void);
void loop(void);
void next_CAN_baudrate(void);
void previous_CAN_baudrate(void);
bool read_settings(void);
void report_current_baudrate(void);
void save_settings(void);
void send_CAN_all_supported_PIDs_for_service_01_02(int service_num);
void send_CAN_custom_command(int service_num, int pid_num, int frame_num);
void send_CAN_isotp_flow_control(void);
void send_CAN_query_service_03();
void send_CAN_query_service_09(int pid_num);
void send_CAN_query_supported_PIDs_for_service_01(void);
void send_CAN_wakeup_commands(void);
void setup(void);
void show_byte_value(unsigned char byte_value);
void show_CONTROL_MENU(void);
void show_index(int index);
// callback to spit out contents of CAN receive frame
void can_sniff_RX(const CAN_message_t &msg)
{
Serial.print("RX: ");
Serial.print("MBX: ");
if (msg.mb < 10)
{
Serial.print("0");
}
Serial.print(msg.mb);
Serial.print(" LEN: ");
if (msg.len < 100)
{
Serial.print(" ");
}
if (msg.len < 10)
{
Serial.print(" ");
}
Serial.print(msg.len);
Serial.print(" EXT: ");
Serial.print(msg.flags.extended);
Serial.print(" TS: ");
if (msg.timestamp < 10000)
{
Serial.print(" ");
}
if (msg.timestamp < 1000)
{
Serial.print(" ");
}
if (msg.timestamp < 100)
{
Serial.print(" ");
}
if (msg.timestamp < 10)
{
Serial.print(" ");
}
Serial.print(msg.timestamp);
Serial.print(" ID: 0x");
uint32_t i = 0xF0000000;
for (int j = 7; j >= 0; j--)
{
Serial.print(((msg.id & i) >> (4 * j)), HEX);
i = i >> 4;
}
Serial.print(" OVERRUN: ");
Serial.print(msg.flags.overrun);
Serial.print(" MSG: ");
for ( uint8_t i = 0; i < msg.len; i++ ) {
Serial.print("0x");
Serial.print((msg.buf[i] & 0xF0) >> 4, HEX);
Serial.print(msg.buf[i] & 0x0F, HEX);
Serial.print(" ");
}
Serial.println("");
LED_timer = millis();
analogWrite(LED_PIN, led_intensity_on);
CAN_baudrate_match_found = true;
CAN_baudrate_match_timeout = millis();
if (msg.buf[0] <= 0x07)
{
switch (msg.buf[1])
{
case 0x41:
case 0x42:
{
if (msg.buf[1] == 0x41)
{
Serial.println(" 0x41: service 0x01 (Show current data) request response");
}
if (msg.buf[1] == 0x42)
{
Serial.println(" 0x41: service 0x01 (Show freeze frame data) request response");
}
Serial.print(" 0x");
Serial.print((msg.buf[1] & 0xF0) >> 4, HEX);
Serial.print(msg.buf[1] & 0x0F, HEX);
Serial.print(": PID 0x");
Serial.print((msg.buf[2] & 0xF0) >> 4, HEX);
Serial.print(msg.buf[2] & 0x0F, HEX);
Serial.print(" - ");
decode_service_01_02_PID_title(msg.buf[1], msg.buf[2]);
Serial.println("");
if (debug_CAN_rx)
{
decode_service_01_02_PID_content(msg);
} else {
Serial.println("");
}
}
break;
case 0x43:
{
Serial.println(" 0x43: service 0x03 (list DTCs) request response");
Serial.println("");
}
break;
case 0x49:
{
Serial.println(" 0x49: service 0x09 (vehicle info) request response");
Serial.print(" 0x");
Serial.print((msg.buf[1] & 0xF0) >> 4, HEX);
Serial.print(msg.buf[1] & 0x0F, HEX);
Serial.print(": PID 0x");
Serial.print((msg.buf[2] & 0xF0) >> 4, HEX);
Serial.print(msg.buf[2] & 0x0F, HEX);
Serial.print(" - ");
decode_service_09_PID_title(msg.buf[2]);
Serial.println("");
if (debug_CAN_rx)
{
decode_service_09_PID_content(msg);
} else {
Serial.println("");
}
}
break;
default:
{
Serial.print(" 0x");
Serial.print((msg.buf[1] & 0xF0) >> 4, HEX);
Serial.print(msg.buf[1] & 0x0F, HEX);
Serial.println(": unexpected standard CAN service request response");
}
break;
}
} else {
switch (msg.buf[0])
{
case 0x10:
{
send_CAN_isotp_flow_control();
switch (msg.buf[2])
{
case 0x41:
case 0x42:
{
Serial.print(" 0x");
Serial.print((msg.buf[2] & 0xF0) >> 4, HEX);
Serial.print(msg.buf[2] & 0x0F, HEX);
Serial.print(": Service 0x");
Serial.print((msg.buf[3] & 0xF0) >> 4, HEX);
Serial.print(msg.buf[3] & 0x0F, HEX);
Serial.print(" - ");
decode_service_01_02_PID_title(msg.buf[2], msg.buf[3]);
Serial.println("");
}
break;
case 0x49:
{
Serial.print(" 0x");
Serial.print((msg.buf[2] & 0xF0) >> 4, HEX);
Serial.print(msg.buf[2] & 0x0F, HEX);
Serial.print(": Service 0x");
Serial.print((msg.buf[3] & 0xF0) >> 4, HEX);
Serial.print(msg.buf[3] & 0x0F, HEX);
Serial.print(" - ");
decode_service_09_PID_title(msg.buf[3]);
Serial.println("");
}
break;
default:
{
// Serial.print(" 0x");
// Serial.print((msg.buf[2] & 0xF0) >> 4, HEX);
// Serial.print(msg.buf[2] & 0x0F, HEX);
// Serial.println(": unknown service request response");
}
break;
}
}
break;
default:
{
// Serial.print(" 0x");
// Serial.print((msg.buf[0] & 0xF0) >> 4, HEX);
// Serial.print(msg.buf[0] & 0x0F, HEX);
// Serial.println(": unknown extended service request response");
}
break;
}
Serial.println("");
}
} // can_sniff_RX()
// callback to spit out contents of CAN ISOTP receive frame
void can_sniff_RX_isotp(const ISOTP_data &config, const uint8_t *buf)
{
Serial.print("RX: (isotp)");
Serial.print(" LEN: ");
if (config.len < 100)
{
Serial.print(" ");
}
if (config.len < 10)
{
Serial.print(" ");
}
Serial.print(config.len);
Serial.print(" EXT: ");
Serial.print(config.flags.extended);
Serial.print(" ID: 0x");
uint32_t i = 0xF0000000;
for (int j = 7; j >= 0; j--)
{
Serial.print(((config.id & i) >> (4 * j)), HEX);
i = i >> 4;
}
Serial.print(" MSG: ");
for (int i = 0; i < config.len; i++) {
Serial.print("0x");
Serial.print((buf[i] & 0xF0) >> 4, HEX);
Serial.print(buf[i] & 0x0F, HEX);
if ((i != 0) && ((i % 8) == 0))
{
Serial.println("");
Serial.print(" ");
} else {
Serial.print(" ");
}
}
Serial.println("");
LED_timer = millis();
analogWrite(LED_PIN, led_intensity_on);
CAN_baudrate_match_found = true;
CAN_baudrate_match_timeout = millis();
switch (buf[0])
{
case 0x41:
case 0x42:
{
if (buf[0] == 0x41)
{
Serial.println(" 0x41: service 0x01 (Show current data) request response - isotp frame");
}
if (buf[0] == 0x42)
{
Serial.println(" 0x41: service 0x01 (Show freeze frame data) request response - isotp frame");
}
Serial.print(" 0x");
Serial.print((buf[0] & 0xF0) >> 4, HEX);
Serial.print(buf[0] & 0x0F, HEX);
Serial.print(": PID 0x");
Serial.print((buf[1] & 0xF0) >> 4, HEX);
Serial.print(buf[1] & 0x0F, HEX);
Serial.print(" - ");
decode_service_01_02_PID_title(buf[0], buf[1]);
Serial.println("");
if (debug_CAN_rx)
{
decode_isotp_service_01_PID_content(config.len, buf);
} else {
Serial.println("");
}
}
break;
case 0x49:
{
Serial.println(" 0x49: service 0x09 (vehicle info) request response - isotp frame");
Serial.print(" 0x");
Serial.print((buf[0] & 0xF0) >> 4, HEX);
Serial.print(buf[0] & 0x0F, HEX);
Serial.print(": PID 0x");
Serial.print((buf[1] & 0xF0) >> 4, HEX);
Serial.print(buf[1] & 0x0F, HEX);
Serial.print(" - ");
decode_service_09_PID_title(buf[1]);
Serial.println("");
if (debug_CAN_rx)
{
decode_isotp_service_09_PID_content(config.len, buf);
} else {
Serial.println("");
}
}
break;
default:
{
Serial.print(" 0x");
Serial.print((buf[0] & 0xF0) >> 4, HEX);
Serial.print(buf[0] & 0x0F, HEX);
Serial.println(": unexpected isotp CAN service request response");
}
break;
}
}
// spit out contents of CAN transmit frame
void can_sniff_TX(const CAN_message_t &msg)
{
Serial.print("TX: ");
Serial.print("MBX: ");
if (msg.mb < 10)
{
Serial.print("0");
}
Serial.print(msg.mb);
Serial.print(" LEN: ");
if (msg.len < 100)
{
Serial.print(" ");
}
if (msg.len < 10)
{
Serial.print(" ");
}
Serial.print(msg.len);
Serial.print(" EXT: ");
Serial.print(msg.flags.extended);
Serial.print(" TS: ");
if (msg.timestamp < 10000)
{
Serial.print(" ");
}
if (msg.timestamp < 1000)
{
Serial.print(" ");
}
if (msg.timestamp < 100)
{
Serial.print(" ");
}
if (msg.timestamp < 10)
{
Serial.print(" ");
}
Serial.print(msg.timestamp);
Serial.print(" ID: 0x");
uint32_t i = 0xF0000000;
for (int j = 7; j >= 0; j--)
{
Serial.print(((msg.id & i) >> (4 * j)), HEX);
i = i >> 4;
}
Serial.print(" OVERRUN: ");
Serial.print(msg.flags.overrun);
Serial.print(" MSG: ");
for ( uint8_t i = 0; i < msg.len; i++ ) {
Serial.print("0x");
Serial.print((msg.buf[i] & 0xF0) >> 4, HEX);
Serial.print(msg.buf[i] & 0x0F, HEX);
Serial.print(" ");
}
Serial.println("");
switch (msg.buf[1])
{
case 0x01:
case 0x02:
{
if (msg.buf[1] == 0x01)
{
Serial.println(" 0x01: service 0x01 request (Show current data)");
} else {
Serial.println(" 0x02: service 0x02 request (Show freeze frame data)");
}
Serial.print(" 0x");
Serial.print((msg.buf[2] & 0xF0) >> 4, HEX);
Serial.print(msg.buf[2] & 0x0F, HEX);
Serial.print(": Service 0x");
Serial.print((msg.buf[2] & 0xF0) >> 4, HEX);
Serial.print(msg.buf[2] & 0x0F, HEX);
Serial.print(" - ");
decode_service_01_02_PID_title(msg.buf[1], msg.buf[2]);
Serial.println("");
if (msg.buf[1] == 0x02)
{
Serial.print(" 0x");
Serial.print((msg.buf[3] & 0xF0) >> 4, HEX);
Serial.print(msg.buf[3] & 0x0F, HEX);
Serial.print(": DTC frame # 0x");
Serial.print((msg.buf[3] & 0xF0) >> 4, HEX);
Serial.print(msg.buf[3] & 0x0F, HEX);
}
Serial.println("");
Serial.println("");
}
break;
case 0x03:
{
Serial.println(" 0x03: service 0x03 request (Show DTCs)");
}
break;
case 0x09:
{
Serial.println(" 0x03: service 0x03 request (Request vehicle information)");
Serial.print(" 0x");
Serial.print((msg.buf[2] & 0xF0) >> 4, HEX);
Serial.print(msg.buf[2] & 0x0F, HEX);
Serial.print(": Service 0x");
Serial.print((msg.buf[2] & 0xF0) >> 4, HEX);
Serial.print(msg.buf[2] & 0x0F, HEX);
Serial.print(" - ");
decode_service_09_PID_title(msg.buf[2]);
Serial.println("");
Serial.println("");
}
}
LED_timer = millis();
analogWrite(LED_PIN, led_intensity_on);
CAN_baudrate_match_found = true;
CAN_baudrate_match_timeout = millis();
} // can_sniff_TX()
// TX a CAN message
void can_TX(const CAN_message_t &msg)
{
while (millis() < (last_CAN_TX_millis + INTER_CAN_MSG_TX_IN_MILLIS))
{
Can1.events();
}
Can1.write(msg);
can_sniff_TX(msg);
last_CAN_TX_millis = millis();
} // can_TX()
// common decode of the service 0x01 PID content into data reported
void common_decode_service_01_02_PID_content(int response, int pid_num, int A, int B, int C, int D, int E)
{
int j = 1;
bool valid_command = true;
switch (pid_num)
{
case 0x00:
{
j = 1;
}
break;
case 0x20:
{
j = 33;
}
break;
case 0x40:
{
j = 65;
}
break;
case 0x60:
{
j = 97;
}
break;
case 0x80:
{
j = 129;
}
break;
case 0xA0:
{
j = 161;
}
break;
case 0xC0:
{
j = 193;
}
break;
case 0xE0:
{
j = 225;
}
break;
}
switch (pid_num)
{
case 0x00:
case 0x20:
case 0x40:
case 0x60:
case 0x80:
case 0xA0:
case 0xC0:
case 0xE0:
{
for (int i = 0x80; i > 0; i = i / 2)
{
if (A & i)
{
Serial.print(" PID # 0x");
Serial.print((j & 0xF0) >> 4, HEX);
Serial.print(j & 0x0F, HEX);
Serial.print(" is supported - ");
decode_service_01_02_PID_title(response, j);
Serial.println("");
supported_PIDs_for_service_01[j] = true;
} else {
supported_PIDs_for_service_01[j] = false;
}
j++;
}
for (int i = 0x80; i > 0; i = i / 2)
{
if (B & i)
{
Serial.print(" PID # 0x");
Serial.print((j & 0xF0) >> 4, HEX);
Serial.print(j & 0x0F, HEX);
Serial.print(" is supported - ");
decode_service_01_02_PID_title(response, j);
Serial.println("");
supported_PIDs_for_service_01[j] = true;
} else {
supported_PIDs_for_service_01[j] = false;
}
j++;
}
for (int i = 0x80; i > 0; i = i / 2)
{
if (C & i)
{
Serial.print(" PID # 0x");
Serial.print((j & 0xF0) >> 4, HEX);
Serial.print(j & 0x0F, HEX);
Serial.print(" is supported - ");
decode_service_01_02_PID_title(response, j);
Serial.println("");
supported_PIDs_for_service_01[j] = true;
} else {
supported_PIDs_for_service_01[j] = false;
}
j++;
}
for (int i = 0x80; i > 0; i = i / 2)
{
if (D & i)
{
Serial.print(" PID # 0x");
Serial.print((j & 0xF0) >> 4, HEX);
Serial.print(j & 0x0F, HEX);
Serial.print(" is supported - ");
decode_service_01_02_PID_title(response, j);
Serial.println("");
supported_PIDs_for_service_01[j] = true;
} else {
supported_PIDs_for_service_01[j] = false;
}
j++;
}
}
break;
case 0x01:
{
Serial.print(" PID # 0x01 - ");
decode_service_01_02_PID_title(response, 0x01);
Serial.println("");
// 0xA7
if (A & 0x80)
{
Serial.println(" MIL is ON");
} else {
Serial.println(" MIL is OFF");
}
// 0xA6-0xA0
Serial.print(" DTC count = 0x");
Serial.print((A & 0x70) >> 4, HEX);
Serial.println(A & 0x0F, HEX);
DTC_count = (A & 0x70) >> 4;
// B3
if (B & 0x08)
{
Serial.println(" Compression ignition monitors supported (e.g. Diesel engines)");
} else {
Serial.println(" Spark ignition monitors supported (e.g. Otto or Wankel engines)");
}
// B2
if (B & 0x04)
{
Serial.print(" Components Test = available");
// B6
if (B & 0x40)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Components Test = unavailable");
}
// B1
if (B & 0x02)
{
Serial.print(" Fuel System Test = available");
// B5
if (B & 0x20)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Fuel System Test = unavailable");
}
// B0
if (B & 0x01)
{
Serial.print(" Misfire Test = available");
// B4
if (B & 0x10)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Misfire Test = unavailable");
}
// B3
if (B & 0x08)
{
// compression
// C7
if (C & 0x80)
{
Serial.print(" EGR and/or VVT System = available");
// D7
if (D & 0x80)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" EGR and/or VVT System = unavailable");
}
// C6
if (C & 0x40)
{
Serial.print(" PM filter monitoring = available");
// D6
if (D & 0x40)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" PM filter monitoring = unavailable");
}
// C5
if (C & 0x20)
{
Serial.print(" Exhaust Gas Sensor = available");
// D5
if (D & 0x20)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Exhaust Gas Sensor = unavailable");
}
// C4
if (C & 0x10)
{
Serial.print(" - Reserved - = available");
// D4
if (D & 0x10)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" - Reserved - = unavailable");
}
// C3
if (C & 0x08)
{
Serial.print(" Boost Pressure = available");
// D3
if (D & 0x08)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Boost Pressure = unavailable");
}
// C2
if (C & 0x04)
{
Serial.print(" - Reserved - = available");
// D2
if (D & 0x04)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" - Reserved - = unavailable");
}
// C1
if (C & 0x02)
{
Serial.print(" NOx/SCR Monitor = available");
// D1
if (D & 0x02)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" NOx/SCR Monitor = unavailable");
}
// C0
if (C & 0x01)
{
Serial.print(" NMHC Catalyst = available");
// D0
if (D & 0x01)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" NMHC Catalyst = unavailable");
}
} else {
// spark
// C7
if (C & 0x80)
{
Serial.print(" EGR and/or VVT System = available");
// D7
if (D & 0x80)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" EGR and/or VVT System = unavailable");
}
// C6
if (C & 0x40)
{
Serial.print(" Oxygen Sensor Heater = available");
// D6
if (D & 0x40)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Oxygen Sensor Heater = unavailable");
}
// C5
if (C & 0x20)
{
Serial.print(" Oxygen Sensor = available");
// D5
if (D & 0x20)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Oxygen Sensor = unavailable");
}
// C4
if (C & 0x10)
{
Serial.print(" A/C Refrigerant = available");
// D4
if (D & 0x10)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" A/C Refrigerant = unavailable");
}
// C3
if (C & 0x08)
{
Serial.print(" Secondary Air System = available");
// D3
if (D & 0x08)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Secondary Air System = unavailable");
}
// C2
if (C & 0x04)
{
Serial.print(" Evaporative System = available");
// D2
if (D & 0x04)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Evaporative System = unavailable");
}
// C1
if (C & 0x02)
{
Serial.print(" Heated Catalyst = available");
// D1
if (D & 0x02)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Heated Catalyst = unavailable");
}
// C0
if (C & 0x01)
{
Serial.print(" Catalyst = available");
// D0
if (D & 0x01)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Catalyst = unavailable");
}
}
}
break;
case 0x02:
{
char dtc[6];
dtc[5] = 0x00;
if ((response == 0x42) && (pid_num == 0x02))
{
switch ((A & 0xC0) >> 6)
{
case 0x00:
{
dtc[0] = 'P';
}
break;
case 0x01:
{
dtc[0] = 'C';
}
break;
case 0x02:
{
dtc[0] = 'B';
}
break;
case 0x03:
{
dtc[0] = 'U';
}
break;
}
switch ((A & 0x30) >> 4)
{
case 0x00:
{
dtc[1] = '0';
}
break;
case 0x01:
{
dtc[1] = '1';
}
break;
case 0x02:
{
dtc[1] = '2';
}
break;
case 0x03:
{
dtc[0] = '3';
}
break;
}
switch (A & 0x0F)
{
case 0x00:
{
dtc[2] = '0';
}
break;
case 0x01:
{
dtc[2] = '1';
}
break;
case 0x02:
{
dtc[2] = '2';
}
break;
case 0x03:
{
dtc[2] = '3';
}
break;
case 0x04:
{
dtc[2] = '4';
}
break;
case 0x05:
{
dtc[2] = '5';
}
break;
case 0x06:
{
dtc[2] = '6';
}
break;
case 0x07:
{
dtc[2] = '7';
}
break;
case 0x08:
{
dtc[2] = '8';
}
break;
case 0x09:
{
dtc[2] = '9';
}
break;
case 0x0A:
{
dtc[2] = 'A';
}
break;
case 0x0B:
{
dtc[2] = 'B';
}
break;
case 0x0C:
{
dtc[2] = 'C';
}
break;
case 0x0D:
{
dtc[2] = 'D';
}
break;
case 0x0E:
{
dtc[2] = 'E';
}
break;
case 0x0F:
{
dtc[2] = 'F';
}
break;
}
switch ((B & 0xF0) >> 4)
{
case 0x00:
{
dtc[3] = '0';
}
break;
case 0x01:
{
dtc[3] = '1';
}
break;
case 0x02:
{
dtc[3] = '2';
}
break;
case 0x03:
{
dtc[3] = '3';
}
break;
case 0x04:
{
dtc[3] = '4';
}
break;
case 0x05:
{
dtc[3] = '5';
}
break;
case 0x06:
{
dtc[3] = '6';
}
break;
case 0x07:
{
dtc[3] = '7';
}
break;
case 0x08:
{
dtc[3] = '8';
}
break;
case 0x09:
{
dtc[3] = '9';
}
break;
case 0x0A:
{
dtc[3] = 'A';
}
break;
case 0x0B:
{
dtc[3] = 'B';
}
break;
case 0x0C:
{
dtc[3] = 'C';
}
break;
case 0x0D:
{
dtc[3] = 'D';
}
break;
case 0x0E:
{
dtc[3] = 'E';
}
break;
case 0x0F:
{
dtc[3] = 'F';
}
break;
}
switch (B & 0x0F)
{
case 0x00:
{
dtc[4] = '0';
}
break;
case 0x01:
{
dtc[4] = '1';
}
break;
case 0x02:
{
dtc[4] = '2';
}
break;
case 0x03:
{
dtc[4] = '3';
}
break;
case 0x04:
{
dtc[4] = '4';
}
break;
case 0x05:
{
dtc[4] = '5';
}
break;
case 0x06:
{
dtc[4] = '6';
}
break;
case 0x07:
{
dtc[4] = '7';
}
break;
case 0x08:
{
dtc[4] = '8';
}
break;
case 0x09:
{
dtc[4] = '9';
}
break;
case 0x0A:
{
dtc[4] = 'A';
}
break;
case 0x0B:
{
dtc[4] = 'B';
}
break;
case 0x0C:
{
dtc[4] = 'C';
}
break;
case 0x0D:
{
dtc[4] = 'D';
}
break;
case 0x0E:
{
dtc[4] = 'E';
}
break;
case 0x0F:
{
dtc[4] = 'F';
}
break;
}
Serial.print(" PID # 0x02 - ");
decode_service_01_02_PID_title(response, 0x02);
Serial.println("");
Serial.print(" DTC: ");
Serial.println(dtc);
} else {
Serial.print(" PID # 0x02 - ");
decode_service_01_02_PID_title(response, 0x02);
Serial.println("");
}
}
break;
case 0x03:
{
Serial.print(" PID # 0x03 - ");
decode_service_01_02_PID_title(response, 0x03);
Serial.println("");
switch (A)
{
case 0x00:
{
Serial.println(" Fuel System #1 - the motor is off");
}
break;
case 0x01:
{
Serial.println(" Fuel System #1 - open loop due to insufficient engine temperature");
}
break;
case 0x02:
{
Serial.println(" Fuel System #1 - closed loop, using oxygen sensor feedback to determine fuel mix");
}
break;
case 0x04:
{
Serial.println(" Fuel System #1 - open loop due to engine load OR fuel cut due to deceleration");
}
break;
case 0x08:
{
Serial.println(" Fuel System #1 - open loop due to system failure");
}
break;
case 0x10:
{
Serial.println(" Fuel System #1 - closed loop, using at least one oxygen sensor but there is a fault in the feedback system");
}
break;
default:
{
Serial.print(" Fuel System #1 - invalid response: 0x");
Serial.print((A & 0xF0) >> 4, HEX);
Serial.println(A & 0x0F, HEX);
}
break;
}
switch (B)
{
case 0x00:
{
Serial.println(" Fuel System #2 - the motor is off");
}
break;
case 0x01:
{
Serial.println(" Fuel System #2 - open loop due to insufficient engine temperature");
}
break;
case 0x02:
{
Serial.println(" Fuel System #2 - closed loop, using oxygen sensor feedback to determine fuel mix");
}
break;
case 0x04:
{
Serial.println(" Fuel System #2 - open loop due to engine load OR fuel cut due to deceleration");
}
break;
case 0x08:
{
Serial.println(" Fuel System #2 - open loop due to system failure");
}
break;
case 0x10:
{
Serial.println(" Fuel System #2 - closed loop, using at least one oxygen sensor but there is a fault in the feedback system");
}
break;
default:
{
Serial.print(" Fuel System #2 - invalid response: 0x");
Serial.print((B & 0xF0) >> 4, HEX);
Serial.println(B & 0x0F, HEX);
}
break;
}
}
break;
case 0x04:
{
Serial.print(" PID # 0x04 - ");
decode_service_01_02_PID_title(response, 0x04);
Serial.println("");
Serial.print(" Engine load (%) = ");
Serial.println((float)(A) / 2.55);
}
break;
case 0x05:
{
Serial.print(" PID # 0x05 - ");
decode_service_01_02_PID_title(response, 0x05);
Serial.println("");
Serial.print(" Engine coolant temperature (degrees C) = ");
Serial.print((float)(A) - 40.0);
Serial.print(" (");
Serial.print((((float)(A) - 40.0) * 9.0 / 5.0) + 32.0);
Serial.println(" degrees F)");
}
break;
case 0x06:
{
Serial.print(" PID # 0x06 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Short term fuel trim - Bank 1 (%) = ");
Serial.println(((float)(A) / 1.28) - 100.0);
}
break;
case 0x07:
{
Serial.print(" PID # 0x07 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Long term fuel trim - Bank 1 (%) = ");
Serial.println(((float)(A) / 1.28) - 100.0);
}
break;
case 0x08:
{
Serial.print(" PID # 0x08 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Short term fuel trim - Bank 2 (%) = ");
Serial.println(((float)(A) / 1.28) - 100.0);
}
break;
case 0x09:
{
Serial.print(" PID # 0x09 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Long term fuel trim - Bank 2 (%) = ");
Serial.println(((float)(A) / 1.28) - 100.0);
}
break;
case 0x0A:
{
Serial.print(" PID # 0x0A - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Fuel pressure (gauge pressure) (kPa) = ");
Serial.println((float)(A) * 3.0);
}
break;
case 0x0B:
{
Serial.print(" PID # 0x0B - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Intake manifold absolute pressure (kPa) = ");
Serial.println((float)(A));
}
break;
case 0x0C:
{
Serial.print(" PID # 0x0C - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Engine speed (rpm) = ");
Serial.println((((float)(A) * 256.0) + (float)(B)) / 4.0);
}
break;
case 0x0D:
{
Serial.print(" PID # 0x0D - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Vehicle speed (km/h) = ");
Serial.print((float)(A));
Serial.print(" (");
Serial.print((float)(A) * MILES_PER_KM);
Serial.println(" mph)");
}
break;
case 0x0E:
{
Serial.print(" PID # 0x0E - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Timing advance (degrees before TDC) = ");
Serial.println(((float)(A) / 2.0) - 64.0);
}
break;
case 0x0F:
{
Serial.print(" PID # 0x0F - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Intake air temperature (degrees C) = ");
Serial.print((float)(A) - 40.0);
Serial.print(" (");
Serial.print((((float)(A) - 40.0) * 9.0 / 5.0) + 32.0);
Serial.println(" degrees F)");
}
break;
case 0x10:
{
Serial.print(" PID # 0x10 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Mass air flow sensor (MAF) air flow rate (g/s) = ");
Serial.println((((float)(A) * 256.0) + (float)(B)) / 100.0);
}
break;
case 0x11:
{
Serial.print(" PID # 0x11 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Throttle position (%) = ");
Serial.println((float)(A) * 100.0 / 255.0);
}
break;
case 0x12:
{
Serial.print(" PID # 0x12 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
switch (A)
{
case 0x01:
{
Serial.println(" Commanded secondary air status - Upstream");
}
break;
case 0x02:
{
Serial.println(" Commanded secondary air status - Downstream of catalytic convertor");
}
break;
case 0x04:
{
Serial.println(" Commanded secondary air status - From the outside atmosphere or off");
}
break;
case 0x08:
{
Serial.println(" Commanded secondary air status - Pump commanded on for diagnostics");
}
break;
default:
{
Serial.print(" Commanded secondary air status - invalid response: 0x");
Serial.print((A & 0xF0) >> 4, HEX);
Serial.println(A & 0x0F, HEX);
}
break;
}
}
break;
case 0x13:
{
Serial.print(" PID # 0x13 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
if (A & 0x01)
{
Serial.println(" Oxygen sensors - Bank 1, Sensor 1 is present");
} else {
Serial.println(" Oxygen sensors - Bank 1, Sensor 1 is not present");
}
if (A & 0x02)
{
Serial.println(" Oxygen sensors - Bank 1, Sensor 2 is present");
} else {
Serial.println(" Oxygen sensors - Bank 1, Sensor 2 is not present");
}
if (A & 0x04)
{
Serial.println(" Oxygen sensors - Bank 1, Sensor 3 is present");
} else {
Serial.println(" Oxygen sensors - Bank 1, Sensor 3 is not present");
}
if (A & 0x08)
{
Serial.println(" Oxygen sensors - Bank 1, Sensor 4 is present");
} else {
Serial.println(" Oxygen sensors - Bank 1, Sensor 4 is not present");
}
if (A & 0x10)
{
Serial.println(" Oxygen sensors - Bank 2, Sensor 1 is present");
} else {
Serial.println(" Oxygen sensors - Bank 2, Sensor 1 is not present");
}
if (A & 0x20)
{
Serial.println(" Oxygen sensors - Bank 2, Sensor 2 is present");
} else {
Serial.println(" Oxygen sensors - Bank 2, Sensor 2 is not present");
}
if (A & 0x40)
{
Serial.println(" Oxygen sensors - Bank 2, Sensor 3 is present");
} else {
Serial.println(" Oxygen sensors - Bank 2, Sensor 3 is not present");
}
if (A & 0x80)
{
Serial.println(" Oxygen sensors - Bank 2, Sensor 4 is present");
} else {
Serial.println(" Oxygen sensors - Bank 2, Sensor 4 is not present");
}
}
break;
case 0x14:
{
Serial.print(" PID # 0x14 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 1 - Voltage (V) = ");
Serial.println((float)(A) / 200.0);
if (B != 0xFF)
{
Serial.print(" Oxygen Sensor 1 - Short term fuel trim (%) = ");
Serial.println(((float)(B) * 100.0 / 128.0) - 100.0);
} else {
Serial.println(" Oxygen Sensor 1 - is not used in trim calculation");
}
}
break;
case 0x15:
{
Serial.print(" PID # 0x15 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 2 - Voltage (V) = ");
Serial.println((float)(A) / 200.0);
if (B != 0xFF)
{
Serial.print(" Oxygen Sensor 2 - Short term fuel trim (%) = ");
Serial.println(((float)(B) * 100.0 / 128.0) - 100.0);
} else {
Serial.println(" Oxygen Sensor 2 - is not used in trim calculation");
}
}
break;
case 0x16:
{
Serial.print(" PID # 0x16 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 3 - Voltage (V) = ");
Serial.println((float)(A) / 200.0);
if (B != 0xFF)
{
Serial.print(" Oxygen Sensor 3 - Short term fuel trim (%) = ");
Serial.println(((float)(B) * 100.0 / 128.0) - 100.0);
} else {
Serial.println(" Oxygen Sensor 3 - is not used in trim calculation");
}
}
break;
case 0x17:
{
Serial.print(" PID # 0x17 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 4 - Voltage (V) = ");
Serial.println((float)(A) / 200.0);
if (B != 0xFF)
{
Serial.print(" Oxygen Sensor 4 - Short term fuel trim (%) = ");
Serial.println(((float)(B) * 100.0 / 128.0) - 100.0);
} else {
Serial.println(" Oxygen Sensor 4 - is not used in trim calculation");
}
}
break;
case 0x18:
{
Serial.print(" PID # 0x18 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 5 - Voltage (V) = ");
Serial.println((float)(A) / 200.0);
if (B != 0xFF)
{
Serial.print(" Oxygen Sensor 5 - Short term fuel trim (%) = ");
Serial.println(((float)(B) * 100.0 / 128.0) - 100.0);
} else {
Serial.println(" Oxygen Sensor 5 - is not used in trim calculation");
}
}
break;
case 0x19:
{
Serial.print(" PID # 0x19 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 6 - Voltage (V) = ");
Serial.println((float)(A) / 200.0);
if (B != 0xFF)
{
Serial.print(" Oxygen Sensor 6 - Short term fuel trim (%) = ");
Serial.println(((float)(B) * 100.0 / 128.0) - 100.0);
} else {
Serial.println(" Oxygen Sensor 6 - is not used in trim calculation");
}
}
break;
case 0x1A:
{
Serial.print(" PID # 0x1A - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 7 - Voltage (V) = ");
Serial.println((float)(A) / 200.0);
if (B != 0xFF)
{
Serial.print(" Oxygen Sensor 7 - Short term fuel trim (%) = ");
Serial.println(((float)(B) * 100.0 / 128.0) - 100.0);
} else {
Serial.println(" Oxygen Sensor 7 - is not used in trim calculation");
}
}
break;
case 0x1B:
{
Serial.print(" PID # 0x1B - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 8 - Voltage (V) = ");
Serial.println((float)(A) / 200.0);
if (B != 0xFF)
{
Serial.print(" Oxygen Sensor 8 - Short term fuel trim (%) = ");
Serial.println(((float)(B) * 100.0 / 128.0) - 100.0);
} else {
Serial.println(" Oxygen Sensor 8 - is not used in trim calculation");
}
}
break;
case 0x1C:
{
Serial.print(" PID # 0x1C - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" OBD standards this vehicle conforms to = ");
switch (A)
{
case 1:
{
Serial.println("OBD-II as defined by CARB");
}
break;
case 2:
{
Serial.println("OBD as defined by the EPA");
}
break;
case 3:
{
Serial.println("OBD and OBD-II");
}
break;
case 4:
{
Serial.println("OBD-I");
}
break;
case 5:
{
Serial.println("Not OBD complaint");
}
break;
case 6:
{
Serial.println("EOBD (Europe)");
}
break;
case 7:
{
Serial.println("EOBD and OBD-II");
}
break;
case 8:
{
Serial.println("EOBD and OBD");
}
break;
case 9:
{
Serial.println("EOBD, OBD and OBD-II");
}
break;
case 10:
{
Serial.println("JOBD (Japan)");
}
break;
case 11:
{
Serial.println("JOBD and OBD-II");
}
break;
case 12:
{
Serial.println("JOBD and EOBD");
}
break;
case 13:
{
Serial.println("JOBD, EOBD, and OBD-II");
}
break;
case 17:
{
Serial.println("Engine Manufacturer Diagnostics (EMD)");
}
break;
case 18:
{
Serial.println("Engine Manufacturer Diagnostics Enhanced (EMD+)");
}
break;
case 19:
{
Serial.println("Heavy Duty On-Board Diagnostics (Child/Partial) (HD OBD-C)");
}
break;
case 20:
{
Serial.println("Heavy Duty On-Board Diagnostics (HD OBD)");
}
break;
case 21:
{
Serial.println("World Wide Harmonized OBD (WWH OBD)");
}
break;
case 23:
{
Serial.println("Heavy Duty Euro OBD Stage I without NOx control (HD EOBD-I)");
}
break;
case 24:
{
Serial.println("Heavy Duty Euro OBD Stage I with NOx control (HD EOBD-I N)");
}
break;
case 25:
{
Serial.println("Heavy Duty Euro OBD Stage II without NOx control (HD EOBD-II)");
}
break;
case 26:
{
Serial.println("Heavy Duty Euro OBD Stage II with NOx control (HD EOBD-II N)");
}
break;
case 28:
{
Serial.println("Brazil OBD Pahse 1 (OBDBr-1)");
}
break;
case 29:
{
Serial.println("Brazil OBD Pahse 2 (OBDBr-2)");
}
break;
case 30:
{
Serial.println("Korean OBD (KOBD)");
}
break;
case 31:
{
Serial.println("India OBD I (IOBD I)");
}
break;
case 32:
{
Serial.println("India OBD II (IOBD II)");
}
break;
case 33:
{
Serial.println("Heavy Duty Euro OBD Stage VI (HD EOBD-IV)");
}
break;
default:
{
Serial.println("Reserved");
}
break;
}
}
break;
case 0x1D:
{
Serial.print(" PID # 0x1D - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
if (A & 0x01)
{
Serial.println(" Oxygen sensors - Bank 1, Sensor 1 is present");
} else {
Serial.println(" Oxygen sensors - Bank 1, Sensor 1 is not present");
}
if (A & 0x02)
{
Serial.println(" Oxygen sensors - Bank 1, Sensor 2 is present");
} else {
Serial.println(" Oxygen sensors - Bank 1, Sensor 2 is not present");
}
if (A & 0x04)
{
Serial.println(" Oxygen sensors - Bank 2, Sensor 1 is present");
} else {
Serial.println(" Oxygen sensors - Bank 2, Sensor 1 is not present");
}
if (A & 0x08)
{
Serial.println(" Oxygen sensors - Bank 2, Sensor 2 is present");
} else {
Serial.println(" Oxygen sensors - Bank 2, Sensor 2 is not present");
}
if (A & 0x10)
{
Serial.println(" Oxygen sensors - Bank 3, Sensor 1 is present");
} else {
Serial.println(" Oxygen sensors - Bank 3, Sensor 1 is not present");
}
if (A & 0x20)
{
Serial.println(" Oxygen sensors - Bank 3, Sensor 2 is present");
} else {
Serial.println(" Oxygen sensors - Bank 3, Sensor 2 is not present");
}
if (A & 0x40)
{
Serial.println(" Oxygen sensors - Bank 4, Sensor 1 is present");
} else {
Serial.println(" Oxygen sensors - Bank 4, Sensor 1 is not present");
}
if (A & 0x80)
{
Serial.println(" Oxygen sensors - Bank 4, Sensor 2 is present");
} else {
Serial.println(" Oxygen sensors - Bank 4, Sensor 2 is not present");
}
}
break;
case 0x1E:
{
Serial.print(" PID # 0x1E - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
if (A & 0x01)
{
Serial.println(" Auxiliary input status - Power Take Off (PTO) is active");
} else {
Serial.println(" Auxiliary input status - Power Take Off (PTO) is inactive");
}
}
break;
case 0x1F:
{
Serial.print(" PID # 0x1F - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Run time since engine start (s) = ");
Serial.println(((float)(A) * 256.0) + (float)(B));
}
break;
case 0x21:
{
Serial.print(" PID # 0x21 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Distance traveled with malfunction indicator lamp (MIL) on (km) = ");
Serial.print(((float)(A) * 256.0) + (float)(B));
Serial.print(" (");
Serial.print((float)(A) * MILES_PER_KM);
Serial.println(" miles)");
}
break;
case 0x22:
{
Serial.print(" PID # 0x22 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Fuel Rail Pressure (relative to manifold vacuum) (kPa) = ");
Serial.println(0.079 * ((float)(A) * 256.0 + (float)(B)));
}
break;
case 0x23:
{
Serial.print(" PID # 0x23 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Fuel Rail Gauge Pressure (diesel, or gasoline direct injection) (kPa) = ");
Serial.println(10.0 * ((float)(A) * 256.0 + (float)(B)));
}
break;
case 0x24:
{
Serial.print(" PID # 0x24 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 1 - Air-Fuel Equivalence Ratio = ");
Serial.println((2.0 / 65536.0) * (((float)(A) * 256.0) + (float)(B)));
Serial.print(" - Voltage (V) = ");
Serial.println((8.0 / 65536.0) * (((float)(C) * 256.0) + (float)(D)));
}
break;
case 0x25:
{
Serial.print(" PID # 0x25 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 2 - Air-Fuel Equivalence Ratio = ");
Serial.println((2.0 / 65536.0) * (((float)(A) * 256.0) + (float)(B)));
Serial.print(" - Voltage (V) = ");
Serial.println((8.0 / 65536.0) * (((float)(C) * 256.0) + (float)(D)));
}
break;
case 0x26:
{
Serial.print(" PID # 0x26 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 3 - Air-Fuel Equivalence Ratio = ");
Serial.println((2.0 / 65536.0) * (((float)(A) * 256.0) + (float)(B)));
Serial.print(" - Voltage (V) = ");
Serial.println((8.0 / 65536.0) * (((float)(C) * 256.0) + (float)(D)));
}
break;
case 0x27:
{
Serial.print(" PID # 0x27 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 4 - Air-Fuel Equivalence Ratio = ");
Serial.println((2.0 / 65536.0) * (((float)(A) * 256.0) + (float)(B)));
Serial.print(" - Voltage (V) = ");
Serial.println((8.0 / 65536.0) * (((float)(C) * 256.0) + (float)(D)));
}
break;
case 0x28:
{
Serial.print(" PID # 0x28 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 5 - Air-Fuel Equivalence Ratio = ");
Serial.println((2.0 / 65536.0) * (((float)(A) * 256.0) + (float)(B)));
Serial.print(" - Voltage (V) = ");
Serial.println((8.0 / 65536.0) * (((float)(C) * 256.0) + (float)(D)));
}
break;
case 0x29:
{
Serial.print(" PID # 0x29 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 6 - Air-Fuel Equivalence Ratio = ");
Serial.println((2.0 / 65536.0) * (((float)(A) * 256.0) + (float)(B)));
Serial.print(" - Voltage (V) = ");
Serial.println((8.0 / 65536.0) * (((float)(C) * 256.0) + (float)(D)));
}
break;
case 0x2A:
{
Serial.print(" PID # 0x2A - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 7 - Air-Fuel Equivalence Ratio = ");
Serial.println((2.0 / 65536.0) * (((float)(A) * 256.0) + (float)(B)));
Serial.print(" - Voltage (V) = ");
Serial.println((8.0 / 65536.0) * (((float)(C) * 256.0) + (float)(D)));
}
break;
case 0x2B:
{
Serial.print(" PID # 0x2B - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 8 - Air-Fuel Equivalence Ratio = ");
Serial.println((2.0 / 65536.0) * (((float)(A) * 256.0) + (float)(B)));
Serial.print(" - Voltage (V) = ");
Serial.println((8.0 / 65536.0) * (((float)(C) * 256.0) + (float)(D)));
}
break;
case 0x2C:
{
Serial.print(" PID # 0x2C - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Commanded EGR (%) = ");
Serial.println((float)(A) * 100.0 / 255.0);
}
break;
case 0x2D:
{
Serial.print(" PID # 0x2D - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" EGR Error (%) = ");
Serial.println(((float)(A) * 100.0 / 255.0) - 100.0);
}
break;
case 0x2E:
{
Serial.print(" PID # 0x2E - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Commanded evaporative purge (%) = ");
Serial.println((float)(A) * 100.0 / 255.0);
}
break;
case 0x2F:
{
Serial.print(" PID # 0x2F - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Fuel Tank Level Input (%) = ");
Serial.println((float)(A) * 100.0 / 255.0);
}
break;
case 0x30:
{
Serial.print(" PID # 0x30 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Warm-ups since codes cleared = ");
Serial.println((float)(A));
}
break;
case 0x31:
{
Serial.print(" PID # 0x31 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Distance traveled since codes cleared = ");
Serial.println(((float)(A) * 256.0) + (float)(B));
}
break;
case 0x32:
{
float esvp;
int esvp2c;
Serial.print(" PID # 0x32 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Evap. System Vapor Pressure (Pa) = ");
if (A & 0x80)
{
esvp2c = ((A * 256) + B) - 65536;
} else {
esvp2c = ((A * 256) + B);
}
esvp = (float)(esvp2c) / 4.0;
Serial.println(esvp);
}
break;
case 0x33:
{
Serial.print(" PID # 0x33 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Absolute Barometric Pressure (kPa) = ");
Serial.println((float)(A));
}
break;
case 0x34:
{
Serial.print(" PID # 0x34 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 1 - Air-Fuel Equivalence Ratio = ");
Serial.println((2.0 / 65536.0) * (((float)(A) * 256.0) + (float)(B)));
Serial.print(" - Current (mA) = ");
Serial.println(((((float)(C) * 256.0) + (float)(D)) / 256.0) - 128.0);
}
break;
case 0x35:
{
Serial.print(" PID # 0x35 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 2 - Air-Fuel Equivalence Ratio = ");
Serial.println((2.0 / 65536.0) * (((float)(A) * 256.0) + (float)(B)));
Serial.print(" - Current (mA) = ");
Serial.println(((((float)(C) * 256.0) + (float)(D)) / 256.0) - 128.0);
}
break;
case 0x36:
{
Serial.print(" PID # 0x36 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 3 - Air-Fuel Equivalence Ratio = ");
Serial.println((2.0 / 65536.0) * (((float)(A) * 256.0) + (float)(B)));
Serial.print(" - Current (mA) = ");
Serial.println(((((float)(C) * 256.0) + (float)(D)) / 256.0) - 128.0);
}
break;
case 0x37:
{
Serial.print(" PID # 0x37 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 4 - Air-Fuel Equivalence Ratio = ");
Serial.println((2.0 / 65536.0) * (((float)(A) * 256.0) + (float)(B)));
Serial.print(" - Current (mA) = ");
Serial.println(((((float)(C) * 256.0) + (float)(D)) / 256.0) - 128.0);
}
break;
case 0x38:
{
Serial.print(" PID # 0x38 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 5 - Air-Fuel Equivalence Ratio = ");
Serial.println((2.0 / 65536.0) * (((float)(A) * 256.0) + (float)(B)));
Serial.print(" - Current (mA) = ");
Serial.println(((((float)(C) * 256.0) + (float)(D)) / 256.0) - 128.0);
}
break;
case 0x39:
{
Serial.print(" PID # 0x39 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 6 - Air-Fuel Equivalence Ratio = ");
Serial.println((2.0 / 65536.0) * (((float)(A) * 256.0) + (float)(B)));
Serial.print(" - Current (mA) = ");
Serial.println(((((float)(C) * 256.0) + (float)(D)) / 256.0) - 128.0);
}
break;
case 0x3A:
{
Serial.print(" PID # 0x3A - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 7 - Air-Fuel Equivalence Ratio = ");
Serial.println((2.0 / 65536.0) * (((float)(A) * 256.0) + (float)(B)));
Serial.print(" - Current (mA) = ");
Serial.println(((((float)(C) * 256.0) + (float)(D)) / 256.0) - 128.0);
}
break;
case 0x3B:
{
Serial.print(" PID # 0x3B - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Oxygen Sensor 8 - Air-Fuel Equivalence Ratio = ");
Serial.println((2.0 / 65536.0) * (((float)(A) * 256.0) + (float)(B)));
Serial.print(" - Current (mA) = ");
Serial.println(((((float)(C) * 256.0) + (float)(D)) / 256.0) - 128.0);
}
break;
case 0x3C:
{
Serial.print(" PID # 0x3C - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Catalyst Temperature: Bank 1, Sensor 1 (degrees C) = ");
Serial.print(((((float)(A) * 256.0) + (float)(B)) / 10.0) - 40.0);
Serial.print(" (");
Serial.print(((((((float)(A) * 256.0) + (float)(B)) / 10.0) - 40.0) * 9.0 / 5.0) + 32.0);
Serial.println(" degrees F)");
}
break;
case 0x3D:
{
Serial.print(" PID # 0x3D - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Catalyst Temperature: Bank 1, Sensor 2 (degrees C) = ");
Serial.println(((((float)(A) * 256.0) + (float)(B)) / 10.0) - 40.0);
}
break;
case 0x3E:
{
Serial.print(" PID # 0x3E - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Catalyst Temperature: Bank 2, Sensor 1 = (degrees C) ");
Serial.println(((((float)(A) * 256.0) + (float)(B)) / 10.0) - 40.0);
}
break;
case 0x3F:
{
Serial.print(" PID # 0x3F - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Catalyst Temperature: Bank 2, Sensor 2 = (degrees C) ");
Serial.println(((((float)(A) * 256.0) + (float)(B)) / 10.0) - 40.0);
}
break;
case 0x41:
{
Serial.print(" PID # 0x41 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
// B2
if (B & 0x04)
{
Serial.print(" Components Test = available");
// B6
if (B & 0x40)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Components Test = unavailable");
}
// B1
if (B & 0x02)
{
Serial.print(" Fuel System Test = available");
// B5
if (B & 0x20)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Fuel System Test = unavailable");
}
// B0
if (B & 0x01)
{
Serial.print(" Misfire Test = available");
// B4
if (B & 0x10)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Misfire Test = unavailable");
}
// B3
if (B & 0x08)
{
// compression
// C7
if (C & 0x80)
{
Serial.print(" EGR and/or VVT System = available");
// D7
if (D & 0x80)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" EGR and/or VVT System = unavailable");
}
// C6
if (C & 0x40)
{
Serial.print(" PM filter monitoring = available");
// D6
if (D & 0x40)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" PM filter monitoring = unavailable");
}
// C5
if (C & 0x20)
{
Serial.print(" Exhaust Gas Sensor = available");
// D5
if (D & 0x20)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Exhaust Gas Sensor = unavailable");
}
// C4
if (C & 0x10)
{
Serial.print(" - Reserved - = available");
// D4
if (D & 0x10)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" - Reserved - = unavailable");
}
// C3
if (C & 0x08)
{
Serial.print(" Boost Pressure = available");
// D3
if (D & 0x08)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Boost Pressure = unavailable");
}
// C2
if (C & 0x04)
{
Serial.print(" - Reserved - = available");
// D2
if (D & 0x04)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" - Reserved - = unavailable");
}
// C1
if (C & 0x02)
{
Serial.print(" NOx/SCR Monitor = available");
// D1
if (D & 0x02)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" NOx/SCR Monitor = unavailable");
}
// C0
if (C & 0x01)
{
Serial.print(" NMHC Catalyst = available");
// D0
if (D & 0x01)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" NMHC Catalyst = unavailable");
}
} else {
// spark
// C7
if (C & 0x80)
{
Serial.print(" EGR and/or VVT System = available");
// D7
if (D & 0x80)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" EGR and/or VVT System = unavailable");
}
// C6
if (C & 0x40)
{
Serial.print(" Oxygen Sensor Heater = available");
// D6
if (D & 0x40)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Oxygen Sensor Heater = unavailable");
}
// C5
if (C & 0x20)
{
Serial.print(" Oxygen Sensor = available");
// D5
if (D & 0x20)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Oxygen Sensor = unavailable");
}
// C4
if (C & 0x10)
{
Serial.print(" A/C Refrigerant = available");
// D4
if (D & 0x10)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" A/C Refrigerant = unavailable");
}
// C3
if (C & 0x08)
{
Serial.print(" Secondary Air System = available");
// D3
if (D & 0x08)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Secondary Air System = unavailable");
}
// C2
if (C & 0x04)
{
Serial.print(" Evaporative System = available");
// D2
if (D & 0x04)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Evaporative System = unavailable");
}
// C1
if (C & 0x02)
{
Serial.print(" Heated Catalyst = available");
// D1
if (D & 0x02)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Heated Catalyst = unavailable");
}
// C0
if (C & 0x01)
{
Serial.print(" Catalyst = available");
// D0
if (D & 0x01)
{
Serial.println(" but incomplete");
} else {
Serial.println(" and complete");
}
} else {
Serial.println(" Catalyst = unavailable");
}
}
}
break;
case 0x42:
{
Serial.print(" PID # 0x42 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Control Module voltage (V) = ");
Serial.println((((float)(A) * 256.0) + (float)(B)) / 1000.0);
}
break;
case 0x43:
{
Serial.print(" PID # 0x43 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Absolute load value (%) = ");
Serial.println((((float)(A) * 256.0) + (float)(B)) * 100.0 / 255.0);
}
break;
case 0x44:
{
Serial.print(" PID # 0x44 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Commanded Air-Fuel Equivalence Ratio = ");
Serial.println((2.0 / 65536.0) * (((float)(A) * 256.0) + (float)(B)));
}
break;
case 0x45:
{
Serial.print(" PID # 0x45 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Relative throttle position (%) = ");
Serial.println((float)(A) * 100.0 / 255.0);
}
break;
case 0x46:
{
Serial.print(" PID # 0x46 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Ambient air temperature (degrees C) = ");
Serial.print((float)(A) - 40.0);
Serial.print(" (");
Serial.print((((float)(A) - 40.0) * 9.0 / 5.0) + 32.0);
Serial.println(" degrees F)");
}
break;
case 0x47:
{
Serial.print(" PID # 0x47 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Absolute throttle position B (%) = ");
Serial.println((float)(A) * 100.0 / 255.0);
}
break;
case 0x48:
{
Serial.print(" PID # 0x48 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Absolute throttle position C (%) = ");
Serial.println((float)(A) * 100.0 / 255.0);
}
break;
case 0x49:
{
Serial.print(" PID # 0x49 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Absolute throttle position D (%) = ");
Serial.println((float)(A) * 100.0 / 255.0);
}
break;
case 0x4A:
{
Serial.print(" PID # 0x4A - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Absolute throttle position E (%) = ");
Serial.println((float)(A) * 100.0 / 255.0);
}
break;
case 0x4B:
{
Serial.print(" PID # 0x4B - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Absolute throttle position F (%) = ");
Serial.println((float)(A) * 100.0 / 255.0);
}
break;
case 0x4C:
{
Serial.print(" PID # 0x4C - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Commanded throttle actuator (%) = ");
Serial.println((float)(A) * 100.0 / 255.0);
}
break;
case 0x4D:
{
Serial.print(" PID # 0x4D - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Time run with MIL on (min) = ");
Serial.println(((float)(A) * 256.0) + (float)(B));
}
break;
case 0x4E:
{
Serial.print(" PID # 0x4E - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Time since trouble codes cleared (min) = ");
Serial.println(((float)(A) * 256.0) + (float)(B));
}
break;
case 0x4F:
{
Serial.print(" PID # 0x4F - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Maximum Fuel-Air Equivalence Ratio = ");
Serial.println((float)(A));
Serial.print(" Maximum oxygen sensor voltage (V) = ");
Serial.println((float)(B));
Serial.print(" Maximum oxygen sensor current (mA) = ");
Serial.println((float)(C));
Serial.print(" Maximum intake manifold absolute pressure (kPa) = ");
Serial.println((float)(D) * 10.0);
}
break;
case 0x51:
{
Serial.print(" PID # 0x51 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Fuel Type = ");
switch (A)
{
case 0:
{
Serial.println("Not available");
}
break;
case 1:
{
Serial.println("Gasoline");
}
break;
case 2:
{
Serial.println("Methanol");
}
break;
case 3:
{
Serial.println("Ethanol");
}
break;
case 4:
{
Serial.println("Diesel");
}
break;
case 5:
{
Serial.println("LPG");
}
break;
case 6:
{
Serial.println("CNG");
}
break;
case 7:
{
Serial.println("Propane");
}
break;
case 8:
{
Serial.println("Electric");
}
break;
case 9:
{
Serial.println("Bifuel running Gasoline");
}
break;
case 10:
{
Serial.println("Bifuel running Methanol");
}
break;
case 11:
{
Serial.println("Bifuel running Ethanol");
}
break;
case 12:
{
Serial.println("Bifuel running LPG");
}
break;
case 13:
{
Serial.println("Bifuel running CNG");
}
break;
case 14:
{
Serial.println("Bifuel running Propane");
}
break;
case 15:
{
Serial.println("Bifuel running Electricity");
}
break;
case 16:
{
Serial.println("Bifuel running electric and combustion engine");
}
break;
case 17:
{
Serial.println("Hybrid gasoline");
}
break;
case 18:
{
Serial.println("Hybrid Ethanol");
}
break;
case 19:
{
Serial.println("Hybrid Diesel");
}
break;
case 20:
{
Serial.println("Hybrid Electric");
}
break;
case 21:
{
Serial.println("Hybrid running electric and combustion engine");
}
break;
case 22:
{
Serial.println("Hybrid Regenerative");
}
break;
case 23:
{
Serial.println("Bifuel running Diesel");
}
break;
default:
{
Serial.println("reserved");
}
break;
}
}
break;
case 0x52:
{
Serial.print(" PID # 0x52 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Ethanol Fuel % = ");
Serial.println((float)(A) * 100.0 / 255.0);
}
break;
case 0x53:
{
Serial.print(" PID # 0x53 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Absolute Evap system Vapor Pressure (kPa) = ");
Serial.println((((float)(A) * 256.0) + (float)(B)) / 200.0);
}
break;
case 0x54:
{
float esvp;
int esvp2c;
Serial.print(" PID # 0x54 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Absolute Evap. system Vapor Pressure (Pa) = ");
if (A & 0x80)
{
esvp2c = ((A * 256) + B) - 65536;
} else {
esvp2c = ((A * 256) + B);
}
esvp = (float)(esvp2c);
Serial.println(esvp);
}
break;
case 0x55:
{
Serial.print(" PID # 0x55 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Short term secondary oxygen sensor trim, bank 1 (%) = ");
Serial.println(((float)(A) * 100.0 / 128.0) - 100.0);
Serial.print(" Short term secondary oxygen sensor trim, bank 3 (%) = ");
Serial.println(((float)(B) * 100.0 / 128.0) - 100.0);
}
break;
case 0x56:
{
Serial.print(" PID # 0x56 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Long term secondary oxygen sensor trim, bank 1 (%) = ");
Serial.println(((float)(A) * 100.0 / 128.0) - 100.0);
Serial.print(" Long term secondary oxygen sensor trim, bank 3 (%) = ");
Serial.println(((float)(B) * 100.0 / 128.0) - 100.0);
}
break;
case 0x57:
{
Serial.print(" PID # 0x57 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Short term secondary oxygen sensor trim, bank 2 (%) = ");
Serial.println(((float)(A) * 100.0 / 128.0) - 100.0);
Serial.print(" Short term secondary oxygen sensor trim, bank 4 (%) = ");
Serial.println(((float)(B) * 100.0 / 128.0) - 100.0);
}
break;
case 0x58:
{
Serial.print(" PID # 0x58 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Long term secondary oxygen sensor trim, bank 2 (%) = ");
Serial.println(((float)(A) * 100.0 / 128.0) - 100.0);
Serial.print(" Long term secondary oxygen sensor trim, bank 4 (%) = ");
Serial.println(((float)(B) * 100.0 / 128.0) - 100.0);
}
break;
case 0x59:
{
Serial.print(" PID # 0x59 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Fuel rail absolute pressure (kPa) = ");
Serial.println((((float)(A) * 256.0) + (float)(B)) * 10.0);
}
break;
case 0x5A:
{
Serial.print(" PID # 0x5A - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Relative accelerator pedal position (%) = ");
Serial.println((float)(A) * 100.0 / 255.0);
}
break;
case 0x5B:
{
Serial.print(" PID # 0x5B - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Hybrid battery pack remaining life (%) = ");
Serial.println((float)(A) * 100.0 / 255.0);
}
break;
case 0x5C:
{
Serial.print(" PID # 0x5C - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Engine oil temperature (degrees C) = ");
Serial.print((float)(A) - 40.0);
Serial.print(" (");
Serial.print((((float)(A) - 40.0) * 9.0 / 5.0) + 32.0);
Serial.println(" degrees F)");
}
break;
case 0x5D:
{
Serial.print(" PID # 0x5D - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Fuel injection timing (degrees) = ");
Serial.println(((((float)(A) * 256.0) + (float)(B)) / 128.0) - 210.0);
}
break;
case 0x5E:
{
Serial.print(" PID # 0x5E - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Engine fuel rate (L/h) = ");
Serial.println((((float)(A) * 256.0) + (float)(B)) / 20.0);
}
break;
case 0x5F:
{
Serial.print(" PID # 0x5F - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.println(" Emission requirements to which vehicle is designed (bit encoded, but definitions are unavailable)");
}
break;
case 0x61:
{
Serial.print(" PID # 0x61 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Driver's demand engine - percent torque (%) = ");
Serial.println((float)(A) - 125.0);
}
break;
case 0x62:
{
Serial.print(" PID # 0x62 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Actual engine - percent torque (%) = ");
Serial.println((float)(A) - 125.0);
}
break;
case 0x63:
{
Serial.print(" PID # 0x63 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Engine reference torque (N-m) = ");
Serial.println(((float)(A) * 256.0) + (float)(B));
}
break;
case 0x64:
{
Serial.print(" PID # 0x64 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Engine percent torque data - 125 idle (%) = ");
Serial.println((float)(A));
Serial.print(" Engine percent torque data - 125 Engine point 1 (%) = ");
Serial.println((float)(B));
Serial.print(" Engine percent torque data - 125 Engine point 2 (%) = ");
Serial.println((float)(C));
Serial.print(" Engine percent torque data - 125 Engine point 3 (%) = ");
Serial.println((float)(D));
Serial.print(" Engine percent torque data - 125 Engine point 4 (%) = ");
Serial.println((float)(E));
}
break;
case 0x65:
{
Serial.print(" PID # 0x65 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.println(" Auxiliary input / output supported (bit encoded, but definitions are unavailable)");
}
break;
case 0x66:
{
Serial.print(" PID # 0x66 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
if (A & 0x01)
{
Serial.print(" Mass air flow sensor A (g/s) = ");
Serial.println((((float)(B) * 256.0) + (float)(C)) / 32.0);
} else {
Serial.println(" Mass air flow sensor A is not supported");
}
if (A & 0x02)
{
Serial.print(" Mass air flow sensor B (g/s) = ");
Serial.println((((float)(D) * 256.0) + (float)(E)) / 32.0);
} else {
Serial.println(" Mass air flow sensor B is not supported");
}
}
break;
case 0x67:
{
Serial.print(" PID # 0x67 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
if (A & 0x01)
{
Serial.print(" Engine coolant temperature - Sensor 1 (degrees C) = ");
Serial.print((float)(B) - 40.0);
Serial.print(" (");
Serial.print((((float)(B) - 40.0) * 9.0 / 5.0) + 32.0);
Serial.println(" degrees F)");
} else {
Serial.println(" Engine coolant temperature - Sensor 1 is not supported");
}
if (A & 0x02)
{
Serial.print(" Engine coolant temperature - Sensor 2 (degrees C) = ");
Serial.print((float)(B) - 40.0);
Serial.print(" (");
Serial.print((((float)(B) - 40.0) * 9.0 / 5.0) + 32.0);
Serial.println(" degrees F)");
} else {
Serial.println(" Engine coolant temperature - Sensor 2 is not supported");
}
}
break;
case 0x68:
{
Serial.print(" PID # 0x68 - ");
decode_service_01_02_PID_title(response, 0x68);
Serial.println("");
if (A & 0x01)
{
Serial.print(" Intake air temperature sensor #1 (degrees C) = ");
Serial.print((float)(B) - 40.0);
Serial.print(" (");
Serial.print((((float)(B) - 40.0) * 9.0 / 5.0) + 32.0);
Serial.println(" degrees F)");
} else {
Serial.println(" Intake air temperature sensor #1 is not supported");
}
if (A & 0x02)
{
Serial.print(" Intake air temperature sensor #2 (degrees C) = ");
Serial.print((float)(B) - 40.0);
Serial.print(" (");
Serial.print((((float)(B) - 40.0) * 9.0 / 5.0) + 32.0);
Serial.println(" degrees F)");
} else {
Serial.println(" Intake air temperature sensor #2 is not supported");
}
}
break;
case 0x69:
case 0x6A:
case 0x6B:
case 0x6C:
case 0x6D:
case 0x6E:
case 0x6F:
case 0x70:
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x76:
case 0x77:
{
Serial.print(" PID # 0x");
Serial.print((pid_num & 0xF0) >> 4, HEX);
Serial.print(pid_num & 0x0F, HEX);
Serial.print(" - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.println(" (detailed definition of content is unavailable)");
}
break;
case 0x78:
{
Serial.print(" PID # 0x78 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
if (A & 0x01)
{
Serial.print(" Exhaust Gas temperature (EGT) Bank 1, sensor 1 = ");
Serial.print(((((float)(B) * 256.0) + (float)(C)) / 10.0) - 40.0);
Serial.print(" (");
Serial.print(((((((float)(B) * 256.0) + (float)(C)) / 10.0) - 40.0) * 9.0 / 5.0) + 32.0);
Serial.println(" degrees F)");
} else {
Serial.println(" Exhaust Gas temperature (EGT) Bank 1, sensor 1 is unsupported");
}
if (A & 0x02)
{
Serial.print(" Exhaust Gas temperature (EGT) Bank 1, sensor 2 = ");
Serial.print(((((float)(C) * 256.0) + (float)(D)) / 10.0) - 40.0);
Serial.print(" (");
Serial.print(((((((float)(C) * 256.0) + (float)(D)) / 10.0) - 40.0) * 9.0 / 5.0) + 32.0);
Serial.println(" degrees F)");
} else {
Serial.println(" Exhaust Gas temperature (EGT) Bank 1, sensor 2 is unsupported");
}
if (!(A & 0x04))
{
Serial.println(" Exhaust Gas temperature (EGT) Bank 1, sensor 3 is unsupported");
}
if (!(A & 0x08))
{
Serial.println(" Exhaust Gas temperature (EGT) Bank 1, sensor 4 is unsupported");
}
}
break;
case 0x79:
{
Serial.print(" PID # 0x79 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
if (A & 0x01)
{
Serial.print(" Exhaust Gas temperature (EGT) Bank 2, sensor 1 = ");
Serial.print(((((float)(B) * 256.0) + (float)(C)) / 10.0) - 40.0);
Serial.print(" (");
Serial.print(((((((float)(B) * 256.0) + (float)(C)) / 10.0) - 40.0) * 9.0 / 5.0) + 32.0);
Serial.println(" degrees F)");
} else {
Serial.println(" Exhaust Gas temperature (EGT) Bank 1, sensor 1 is unsupported");
}
if (A & 0x02)
{
Serial.print(" Exhaust Gas temperature (EGT) Bank 2, sensor 2 = ");
Serial.print(((((float)(C) * 256.0) + (float)(D)) / 10.0) - 40.0);
Serial.print(" (");
Serial.print(((((((float)(C) * 256.0) + (float)(D)) / 10.0) - 40.0) * 9.0 / 5.0) + 32.0);
Serial.println(" degrees F)");
} else {
Serial.println(" Exhaust Gas temperature (EGT) Bank 1, sensor 2 is unsupported");
}
if (!(A & 0x04))
{
Serial.println(" Exhaust Gas temperature (EGT) Bank 2, sensor 3 is unsupported");
}
if (!(A & 0x08))
{
Serial.println(" Exhaust Gas temperature (EGT) Bank 2, sensor 4 is unsupported");
}
}
break;
case 0x7A:
case 0x7B:
{
Serial.print(" PID # 0x");
Serial.print((pid_num & 0xF0) >> 4, HEX);
Serial.print(pid_num & 0x0F, HEX);
Serial.print(" - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.println(" (detailed definition of content is unavailable)");
}
break;
case 0x7C:
{
Serial.print(" PID # 0x7C - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Diesel Particulate filter (DPF) temperature (degrees C) = ");
Serial.print(((((float)(A) * 256.0) + (float)(B)) / 10.0) - 40.0);
Serial.print(" (");
Serial.print(((((((float)(A) * 256.0) + (float)(B)) / 10.0) - 40.0) * 9.0 / 5.0) + 32.0);
Serial.println(" degrees F)");
}
break;
case 0x7D:
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x86:
case 0x87:
case 0x88:
case 0x89:
case 0x8A:
case 0x8B:
case 0x8C:
case 0x8D:
{
Serial.print(" PID # 0x");
Serial.print((pid_num & 0xF0) >> 4, HEX);
Serial.print(pid_num & 0x0F, HEX);
Serial.print(" - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.println(" (detailed definition of content is unavailable)");
}
break;
case 0x8E:
{
Serial.print(" PID # 0x8E - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Engine Friction - Percent Torque (%) = ");
Serial.println((float)(A) - 125.0);
}
break;
case 0x8F:
case 0x90:
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x96:
case 0x97:
case 0x98:
case 0x99:
case 0x9A:
case 0x9B:
case 0x9C:
case 0x9D:
case 0x9E:
case 0x9F:
case 0xA1:
{
Serial.print(" PID # 0x");
Serial.print((pid_num & 0xF0) >> 4, HEX);
Serial.print(pid_num & 0x0F, HEX);
Serial.print(" - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.println(" (detailed definition of content is unavailable)");
}
break;
case 0xA2:
{
Serial.print(" PID # 0xA2 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Cylinder Fuel Rate (mg/stroke) = ");
Serial.println((((float)(A) * 256.0) + (float)(B)) / 32.0);
}
break;
case 0xA3:
{
Serial.print(" PID # 0x");
Serial.print((pid_num & 0xF0) >> 4, HEX);
Serial.print(pid_num & 0x0F, HEX);
Serial.print(" - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.println(" (detailed definition of content is unavailable)");
}
break;
case 0xA4:
{
Serial.print(" PID # 0xA4 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
if (A & 0x02)
{
Serial.print(" Transmission Actual Gear = ");
Serial.println((((float)(C) * 256.0) + (float)(D)) / 1000.0);
} else {
Serial.println(" Transmission Actual Gear is unsupported");
}
}
break;
case 0xA5:
{
Serial.print(" PID # 0xA5 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
if (A & 0x01)
{
Serial.print(" Commanded Diesel Exhaust Fluid Dosing = ");
Serial.println((float)(B) / 2.0);
} else {
Serial.println(" Commanded Diesel Exhaust Fluid Dosing is unsupported");
}
}
break;
case 0xA6:
{
double odometer = (((float)(A) * 256.0 * 256.0 * 256.0) + ((float)(B) * 256.0 * 256.0) + ((float)(C) * 256.0) + (float)(D)) / 10.0;
Serial.print(" PID # 0xA6 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.print(" Odometer (km) = ");
Serial.print(odometer);
Serial.print(" (");
Serial.print(odometer * MILES_PER_KM);
Serial.println(" miles)");
}
break;
case 0xA7:
case 0xA8:
{
Serial.print(" PID # 0x");
Serial.print((pid_num & 0xF0) >> 4, HEX);
Serial.print(pid_num & 0x0F, HEX);
Serial.print(" - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.println(" (detailed definition of content is unavailable)");
}
break;
case 0xA9:
{
Serial.print(" PID # 0xA9 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
if (A & 0x01)
{
if (B & 0x01)
{
Serial.println(" ABS Disable Switch is active");
} else {
Serial.println(" ABS Disable Switch is not active");
}
} else {
Serial.println(" ABS Disable Switch State is unsupported");
}
}
break;
case 0xC3:
{
Serial.print(" PID # 0x");
Serial.print((pid_num & 0xF0) >> 4, HEX);
Serial.print(pid_num & 0x0F, HEX);
Serial.print(" - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
Serial.println(" (detailed definition of content is unavailable)");
}
break;
case 0xC4:
{
Serial.print(" PID # 0xC4 - ");
decode_service_01_02_PID_title(response, pid_num);
Serial.println("");
if (B & 0x20)
{
Serial.println(" Engine Idle Request is active");
} else {
Serial.println(" Engine Idle Request is not active");
}
if (B & 0x40)
{
Serial.println(" Engine Stop Request is active");
} else {
Serial.println(" Engine Stop Request is not active");
}
}
break;
default:
{
valid_command = false;
}
break;
}
if (valid_command)
{
Serial.println("");
}
} // common_decode_service_01_02_PID_content()
// common decode of the service 0x09 PID content
void common_decode_service_09_PID_content(int response, int pid_num, int A, int B, int C, int D, int E)
{
int j = 1;
bool valid_command = true;
switch(pid_num)
{
case 0x00:
{
service_09_supported_PID_response_rxd = true;
for (int i = 0x80; i > 0; i = i / 2)
{
if (A & i)
{
Serial.print(" PID # 0x");
Serial.print((j & 0xF0) >> 4, HEX);
Serial.print(j & 0x0F, HEX);
Serial.print(" is supported - ");
decode_service_09_PID_title(j);
Serial.println("");
supported_PIDs_for_service_09[j] = true;
} else {
supported_PIDs_for_service_09[j] = false;
}
j++;
}
for (int i = 0x80; i > 0; i = i / 2)
{
if (B & i)
{
Serial.print(" PID # 0x");
Serial.print((j & 0xF0) >> 4, HEX);
Serial.print(j & 0x0F, HEX);
Serial.print(" is supported - ");
decode_service_09_PID_title(j);
Serial.println("");
supported_PIDs_for_service_09[j] = true;
} else {
supported_PIDs_for_service_09[j] = false;
}
j++;
}
for (int i = 0x80; i > 0; i = i / 2)
{
if (C & i)
{
Serial.print(" PID # 0x");
Serial.print((j & 0xF0) >> 4, HEX);
Serial.print(j & 0x0F, HEX);
Serial.print(" is supported - ");
decode_service_09_PID_title(j);
Serial.println("");
supported_PIDs_for_service_09[j] = true;
} else {
supported_PIDs_for_service_09[j] = false;
}
j++;
}
for (int i = 0x80; i > 0; i = i / 2)
{
if (D & i)
{
Serial.print(" PID # 0x");
Serial.print((j & 0xF0) >> 4, HEX);
Serial.print(j & 0x0F, HEX);
Serial.print(" is supported - ");
decode_service_09_PID_title(j);
Serial.println("");
supported_PIDs_for_service_09[j] = true;
} else {
supported_PIDs_for_service_09[j] = false;
}
j++;
}
}
break;
default:
{
valid_command = false;
}
break;
}
if (valid_command)
{
Serial.println("");
}
} // common_decode_service_09_PID_content()
// decode the extended service 0x01 PID content into data reported
void decode_isotp_service_01_PID_content(int msg_char_count, const uint8_t *buf)
{
int response = buf[0];
int pid_num = buf[1];
int A = buf[2];
int B = buf[3];
int C = buf[4];
int D = buf[5];
int E = buf[6];
common_decode_service_01_02_PID_content(response, pid_num, A, B, C, D, E);
} // decode_isotp_service_01_PID_content()
// decode the service 0x09 PID isotp content into data reported
void decode_isotp_service_09_PID_content(int msg_char_count, const uint8_t *buf)
{
int response = buf[0];
int pid_num = buf[1];
int A = buf[2];
int B = buf[3];
int C = buf[4];
int D = buf[5];
int E = buf[6];
bool valid_command = true;
switch(pid_num)
{
case 0x00:
{
common_decode_service_09_PID_content(response, pid_num, A, B, C, D, E);
}
break;
case 0x02: // VIN query
{
Serial.print(" VIN: ");
for (int vin_char_num = 3; vin_char_num < msg_char_count; vin_char_num++)
{
if (buf[vin_char_num])
{
Serial.print((char)(buf[vin_char_num]));
}
}
Serial.println("");
}
break;
case 0x0A: // ECU name query
{
Serial.print(" ECU name: ");
for (int ecu_name_char_num = 3; ecu_name_char_num < msg_char_count; ecu_name_char_num++)
{
if (buf[ecu_name_char_num])
{
Serial.print((char)(buf[ecu_name_char_num]));
}
}
Serial.println("");
}
break;
default:
{
valid_command = false;
}
break;
}
if (valid_command)
{
Serial.println("");
}
} // decode_isotp_service_09_PID_content()
// decode the service 0x01 PID content into data reported
void decode_service_01_02_PID_content(const CAN_message_t &msg)
{
int response = msg.buf[1];
int pid_num = msg.buf[2];
int A = msg.buf[3];
int B = msg.buf[4];
int C = msg.buf[5];
int D = msg.buf[6];
int E = msg.buf[7];
common_decode_service_01_02_PID_content(response, pid_num, A, B, C, D, E);
} // decode_service_01_02_PID_content()
// decode the service 0x01/0x02 PID into a title
void decode_service_01_02_PID_title(int response, int pid_num)
{
switch (pid_num)
{
case 0x00:
{
Serial.print("PIDs supported [0x01-0x20]");
}
break;
case 0x01:
{
Serial.print("Monitor status since DTCs cleared");
}
break;
case 0x02:
{
if (response == 0x41)
{
Serial.print("Freeze DTC");
} else {
Serial.print("DTC that caused freeze frame data to be stored");
}
}
break;
case 0x03:
{
Serial.print("Fuel System Status");
}
break;
case 0x04:
{
Serial.print("Calculated engine load [0 to 100%]");
}
break;
case 0x05:
{
Serial.print("Engine coolant temperature [-40 to 215 degrees C]");
}
break;
case 0x06:
{
Serial.print("Short term fuel trim - Bank 1 [-100 to 99.2%]");
}
break;
case 0x07:
{
Serial.print("Long term fuel trim - Bank 1 [-100 to 99.2%]");
}
break;
case 0x08:
{
Serial.print("Short term fuel trim - Bank 2 [-100 to 99.2%]");
}
break;
case 0x09:
{
Serial.print("Long term fuel trim - Bank 2 [-100 to 99.2%]");
}
break;
case 0x0A:
{
Serial.print("Fuel pressure (gauge pressure) [0 to 765 kPa]");
}
break;
case 0x0B:
{
Serial.print("Intake manifold absolute pressure [0 to 255 kPa]");
}
break;
case 0x0C:
{
Serial.print("Engine speed [0 to 16383.75 rpm]");
}
break;
case 0x0D:
{
Serial.print("Vehicle speed [0 to 255 km/h]");
}
break;
case 0x0E:
{
Serial.print("Timing advance [-64 to 63.5 degrees before TDC");
}
break;
case 0x0F:
{
Serial.print("Intake air temperature [-40 to 215 degrees C");
}
break;
case 0x10:
{
Serial.print("Mass air flow sensor (MAF) air flow rate [0 to 655.35 g/s]");
}
break;
case 0x11:
{
Serial.print("Throttle position [0 to 100%]");
}
break;
case 0x12:
{
Serial.print("Commanded secondary air status");
}
break;
case 0x13:
{
Serial.print("Oxygen sensors present (in 2 banks)");
}
break;
case 0x14:
{
Serial.print("Oxygen Sensor 1 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]");
}
break;
case 0x15:
{
Serial.print("Oxygen Sensor 2 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]");
}
break;
case 0x16:
{
Serial.print("Oxygen Sensor 3 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]");
}
break;
case 0x17:
{
Serial.print("Oxygen Sensor 4 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]");
}
break;
case 0x18:
{
Serial.print("Oxygen Sensor 5 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]");
}
break;
case 0x19:
{
Serial.print("Oxygen Sensor 6 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]");
}
break;
case 0x1A:
{
Serial.print("Oxygen Sensor 7 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]");
}
break;
case 0x1B:
{
Serial.print("Oxygen Sensor 8 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]");
}
break;
case 0x1C:
{
Serial.print("OBD standards this vehicle conforms to [1 to 250]");
}
break;
case 0x1D:
{
Serial.print("Oxygen sensors present (in 4 banks)");
}
break;
case 0x1E:
{
Serial.print("Auxiliary input status");
}
break;
case 0x1F:
{
Serial.print("Run time since engine start [0 to 65535 s]");
}
break;
case 0x20:
{
Serial.print("PIDs supported [0x21-0x40]");
}
break;
case 0x21:
{
Serial.print("Distance traveled with malfunction indicator lamp (MIL) on [0 to 65535 km]");
}
break;
case 0x22:
{
Serial.print("Fuel Rail Pressure (relative to manifold vacuum) [0 to 5177.265 kPa]");
}
break;
case 0x23:
{
Serial.print("Fuel Rail Gauge Pressure (diesel, or gasoline direct injection) [0 to 655350 kPa]");
}
break;
case 0x24:
{
Serial.print("Oxygen Sensor 1 - Air-Fuel Equivalence Ratio [0 to <2] & Voltage [0 to <8 V]");
}
break;
case 0x25:
{
Serial.print("Oxygen Sensor 2 - Air-Fuel Equivalence Ratio [0 to <2] & Voltage [0 to <8 V]");
}
break;
case 0x26:
{
Serial.print("Oxygen Sensor 3 - Air-Fuel Equivalence Ratio [0 to <2] & Voltage [0 to <8 V]");
}
break;
case 0x27:
{
Serial.print("Oxygen Sensor 4 - Air-Fuel Equivalence Ratio [0 to <2] & Voltage [0 to <8 V]");
}
break;
case 0x28:
{
Serial.print("Oxygen Sensor 5 - Air-Fuel Equivalence Ratio [0 to <2] & Voltage [0 to <8 V]");
}
break;
case 0x29:
{
Serial.print("Oxygen Sensor 6 - Air-Fuel Equivalence Ratio [0 to <2] & Voltage [0 to <8 V]");
}
break;
case 0x2A:
{
Serial.print("Oxygen Sensor 7 - Air-Fuel Equivalence Ratio [0 to <2] & Voltage [0 to <8 V]");
}
break;
case 0x2B:
{
Serial.print("Oxygen Sensor 8 - Air-Fuel Equivalence Ratio [0 to <2] & Voltage [0 to <8 V]");
}
break;
case 0x2C:
{
Serial.print("Commanded EGR [0 to 100%]");
}
break;
case 0x2D:
{
Serial.print("EGR Error [-100 to 99.2%]");
}
break;
case 0x2E:
{
Serial.print("Commanded evaporative purge [0 to 100%]");
}
break;
case 0x2F:
{
Serial.print("Fuel Tank Level input [0 to 100%]");
}
break;
case 0x30:
{
Serial.print("Warm-ups since codes cleared [0 to 255]");
}
break;
case 0x31:
{
Serial.print("Distance traveled since codes cleared [0 to 65535 km]");
}
break;
case 0x32:
{
Serial.print("Evap. System Vapor Pressure [-8192 to 8191.75 Pa]");
}
break;
case 0x33:
{
Serial.print("Absolute Barometric Pressure [0 to 255 kPa]");
}
break;
case 0x34:
{
Serial.print("Oxygen Sensor 1 - Air-Fuel Equivalence Ratio [0 to <2] & Current [-128 to <128 mA]");
}
break;
case 0x35:
{
Serial.print("Oxygen Sensor 2 - Air-Fuel Equivalence Ratio [0 to <2] & Current [-128 to <128 mA]");
}
break;
case 0x36:
{
Serial.print("Oxygen Sensor 3 - Air-Fuel Equivalence Ratio [0 to <2] & Current [-128 to <128 mA]");
}
break;
case 0x37:
{
Serial.print("Oxygen Sensor 4 - Air-Fuel Equivalence Ratio [0 to <2] & Current [-128 to <128 mA]");
}
break;
case 0x38:
{
Serial.print("Oxygen Sensor 5 - Air-Fuel Equivalence Ratio [0 to <2] & Current [-128 to <128 mA]");
}
break;
case 0x39:
{
Serial.print("Oxygen Sensor 6 - Air-Fuel Equivalence Ratio [0 to <2] & Current [-128 to <128 mA]");
}
break;
case 0x3A:
{
Serial.print("Oxygen Sensor 7 - Air-Fuel Equivalence Ratio [0 to <2] & Current [-128 to <128 mA]");
}
break;
case 0x3B:
{
Serial.print("Oxygen Sensor 8 - Air-Fuel Equivalence Ratio [0 to <2] & Current [-128 to <128 mA]");
}
break;
case 0x3C:
{
Serial.print("Catalyst Temperature: Bank 1, Sensor 1 [-40 to 6513.5 degrees C]");
}
break;
case 0x3D:
{
Serial.print("Catalyst Temperature: Bank 2, Sensor 1 [-40 to 6513.5 degrees C]");
}
break;
case 0x3E:
{
Serial.print("Catalyst Temperature: Bank 1, Sensor 2 [-40 to 6513.5 degrees C]");
}
break;
case 0x3F:
{
Serial.print("Catalyst Temperature: Bank 2, Sensor 2 [-40 to 6513.5 degrees C]");
}
break;
case 0x40:
{
Serial.print("PIDs supported [0x41-0x60]");
}
break;
case 0x41:
{
Serial.print("Monitor status this drive cycle");
}
break;
case 0x42:
{
Serial.print("Control module voltage [0 to 65535 V]");
}
break;
case 0x43:
{
Serial.print("Absolute load value [0 to 27500%]");
}
break;
case 0x44:
{
Serial.print("Commanded Air-Fuel Equivalence Ratio [0 to <2]");
}
break;
case 0x45:
{
Serial.print("Relative throttle position [0 to 100%]");
}
break;
case 0x46:
{
Serial.print("Ambient air temperature [-40 to 215 degrees C]");
}
break;
case 0x47:
{
Serial.print("Absolute throttle position B [0 to 100%]");
}
break;
case 0x48:
{
Serial.print("Absolute throttle position C [0 to 100%]");
}
break;
case 0x49:
{
Serial.print("Absolute throttle position D [0 to 100%]");
}
break;
case 0x4A:
{
Serial.print("Absolute throttle position E [0 to 100%]");
}
break;
case 0x4B:
{
Serial.print("Absolute throttle position F");
}
break;
case 0x4C:
{
Serial.print("Commanded throttle actuator [0 to 100%]");
}
break;
case 0x4D:
{
Serial.print("Time run with MIL on [0 to 65535 min]");
}
break;
case 0x4E:
{
Serial.print("Time since trouble codes cleared [0 to 65535 min]");
}
break;
case 0x4F:
{
Serial.print("Maximum value for Fuel-Air eqivalence ratio [0 to 255], O2 sensor voltage [0 to 255 V], O2 sensor current [0 to 255 mA], & intake MAP [0 to 2550 kPa]");
}
break;
case 0x50:
{
Serial.print("Maximum value for air flow rate from mass air flow sensor [0 to 2550 g/s]");
}
break;
case 0x51:
{
Serial.print("Fuel Type");
}
break;
case 0x52:
{
Serial.print("Ethanol fuel % [0 to 100%]");
}
break;
case 0x53:
{
Serial.print("Absolute Evap system Vapor Pressure [0 to 327.675 kPa]");
}
break;
case 0x54:
{
Serial.print("Evap system vapor pressure [-32768 to 32767 Pa]");
}
break;
case 0x55:
{
Serial.print("Short term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]");
}
break;
case 0x56:
{
Serial.print("Long term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]");
}
break;
case 0x57:
{
Serial.print("Short term secondary oxygen sensor trim - bank 2 & bank 4 [-100 to 99.2%]");
}
break;
case 0x58:
{
Serial.print("Long term secondary oxygen sensor trim - bank 2 & bank 4 [-100 to 99.2%]");
}
break;
case 0x59:
{
Serial.print("Fuel rail absolute pressure [0 to 655350 kPa]");
}
break;
case 0x5A:
{
Serial.print("Relative accelerator pedal position [0 to 100%]");
}
break;
case 0x5B:
{
Serial.print("Hybrid battery pack remaining life [0 to 100%]");
}
break;
case 0x5C:
{
Serial.print("Engine oil temperature [-40 to 210 degrees C]");
}
break;
case 0x5D:
{
Serial.print("Fuel injection timimg -210.00 to 301.992 degrees]");
}
break;
case 0x5E:
{
Serial.print("Engine fuel rate [0 to 3212.75 L/h]");
}
break;
case 0x5F:
{
Serial.print("Emission requirements to which vehicle is designed");
}
break;
case 0x60:
{
Serial.print("PIDs supported [0x61-0x80]");
}
break;
case 0x61:
{
Serial.print("Driver's demand engine - percent torque [-125 to 130%]");
}
break;
case 0x62:
{
Serial.print("Actual engine - percent torque [-125 to 130%]");
}
break;
case 0x63:
{
Serial.print("Engine reference torque [0 to 65535 N-m]");
}
break;
case 0x64:
{
Serial.print("Engine percent torque data [-125 to 130%]");
}
break;
case 0x65:
{
Serial.print("Auxiliary input / output supported");
}
break;
case 0x66:
{
Serial.print("Mass air flow sensor [0 to 2047.96875 g/s]");
}
break;
case 0x67:
{
Serial.print("Engine coolant temperature [-40 to 215 degrees C]");
}
break;
case 0x68:
{
Serial.print("Intake air temperature sensor [-40 to 215 degrees C]");
}
break;
case 0x69:
{
Serial.print("Actual EGR, Commanded EGR, and EGR Error");
}
break;
case 0x6A:
{
Serial.print("Commanded Diesel intake air flow control and relative intake air flow position");
}
break;
case 0x6B:
{
Serial.print("Exhaust gas recirculation temperature");
}
break;
case 0x6C:
{
Serial.print("Commanded throttle actuator control and relative throttle position");
}
break;
case 0x6D:
{
Serial.print("Fuel pressure control system");
}
break;
case 0x6E:
{
Serial.print("Injection pressure control system");
}
break;
case 0x6F:
{
Serial.print("Turbocharger compressor inlet pressure");
}
break;
case 0x70:
{
Serial.print("Boost pressure control");
}
break;
case 0x71:
{
Serial.print("Variable Geometry turbo (VGT) control");
}
break;
case 0x72:
{
Serial.print("Wastegate control");
}
break;
case 0x73:
{
Serial.print("Exhaust pressure");
}
break;
case 0x74:
{
Serial.print("Turbocharger RPM");
}
break;
case 0x75:
case 0x76:
{
Serial.print("Turbocharger temperature");
}
break;
case 0x77:
{
Serial.print("Charge air cooler temperature (CACT)");
}
break;
case 0x78:
{
Serial.print("Exhaust Gas temperature (EGT) Bank 1 [-40 to 6513.5 degrees C]");
}
break;
case 0x79:
{
Serial.print("Exhaust Gas temperature (EGT) Bank 2 [-40 to 6513.5 degrees C]");
}
break;
case 0x7A:
{
Serial.print("Diesel particulate filter (DPF) differential pressure");
}
break;
case 0x7B:
{
Serial.print("Diesel particulate filter (DPF)");
}
break;
case 0x7C:
{
Serial.print("Diesel particulate filter (DPF) temperature [degrees C]");
}
break;
case 0x7D:
{
Serial.print("NOx NTE (Not-To-Exceed) control area status");
}
break;
case 0x7E:
{
Serial.print("PM NTE (Not-To-Exceed) control area status");
}
break;
case 0x7F:
{
Serial.print("Engine run time [s]");
}
break;
case 0x80:
{
Serial.print("PIDs supported [0x81-0xA0]");
}
break;
case 0x81:
case 0x82:
{
Serial.print("Engine run time for Auxiliary Emissions Control Device (AECD)");
}
break;
case 0x83:
{
Serial.print("NOx sensor");
}
break;
case 0x84:
{
Serial.print("Manifold surface temperature");
}
break;
case 0x85:
{
Serial.print("NOx reagent system");
}
break;
case 0x86:
{
Serial.print("Particulate matter (PM) sensor");
}
break;
case 0x87:
{
Serial.print("Intake manifold absolute pressure");
}
break;
case 0x88:
{
Serial.print("SCR Induce System");
}
break;
case 0x89:
{
Serial.print("Run Time for AECD #11-#15");
}
break;
case 0x8A:
{
Serial.print("Run Time for AECD #16-#20");
}
break;
case 0x8B:
{
Serial.print("Diesel Aftertreatment");
}
break;
case 0x8C:
{
Serial.print("O2 Sensor (Wide Range)");
}
break;
case 0x8D:
{
Serial.print("Throttle Position G [0 to 100%]");
}
break;
case 0x8E:
{
Serial.print("Engine Friction - Percent Torque [-125 to 130%]");
}
break;
case 0x8F:
{
Serial.print("PM Sensor Bank 1 & 2");
}
break;
case 0x90:
case 0x91:
{
Serial.print("WWH-OBD Vehicle OBD System Information [h]");
}
break;
case 0x92:
{
Serial.print("Fuel System Control");
}
break;
case 0x93:
{
Serial.print("WWH-OBD Vehicle OBD Counters support [h]");
}
break;
case 0x94:
{
Serial.print("NOx Warning And Inducement System");
}
break;
case 0x98:
case 0x99:
{
Serial.print("Exhaust Gas Temperature Sensor");
}
break;
case 0x9A:
{
Serial.print("Hybrid/EV Vehicle System Data, Battery, Voltage");
}
break;
case 0x9B:
{
Serial.print("Diesel Exhaust Fluid Sensor Data");
}
break;
case 0x9C:
{
Serial.print(")2 Sensor Data");
}
break;
case 0x9D:
{
Serial.print("Engine Fuel Rate [g/s]");
}
break;
case 0x9E:
{
Serial.print("Engine Exhaust Flow Rate [kg/h]");
}
break;
case 0x9F:
{
Serial.print("Fuel System Percentage Use");
}
break;
case 0xA0:
{
Serial.print("PIDs supported [0xA1-0xC0]");
}
break;
case 0xA1:
{
Serial.print("NOx Sensor Corrected Data [ppm]");
}
break;
case 0xA2:
{
Serial.print("Cylinder Fuel Rate [0 to 2047.96875 mg/stroke]");
}
break;
case 0xA3:
{
Serial.print("Evap System Vapor Pressure [Pa]");
}
break;
case 0xA4:
{
Serial.print("Transmission Actual Gear [0 to 65.535]");
}
break;
case 0xA5:
{
Serial.print("Commanded Diesel Exhaust Fluid Dosing [0 to 127.5 %]");
}
break;
case 0xA6:
{
Serial.print("Odometer [0 to 429496729.5 km]");
}
break;
case 0xA7:
{
Serial.print("NOx Sensor Concentration Sensors 3 & 4");
}
break;
case 0xA8:
{
Serial.print("NOx Sensor Corrected Concentration Sensors 3 & 4");
}
break;
case 0xA9:
{
Serial.print("ABS Disable Switch State");
}
break;
case 0xC0:
{
Serial.print("PIDs supported [0xC1-0xE0]");
}
break;
case 0xC3:
case 0xC4:
{
Serial.print("Unknown ???");
}
break;
default:
{
Serial.print("UNDOCUMENTED: 0x");
Serial.print((pid_num & 0xF0) >> 4, HEX);
Serial.print(pid_num & 0x0F, HEX);
}
break;
}
} // decode_service_01_02_PID_title()
// decode the service 0x09 PID content
void decode_service_09_PID_content(const CAN_message_t &msg)
{
int response = msg.buf[1];
int pid_num = msg.buf[2];
int A = msg.buf[3];
int B = msg.buf[4];
int C = msg.buf[5];
int D = msg.buf[6];
int E = msg.buf[7];
switch(pid_num)
{
case 0x00:
{
common_decode_service_09_PID_content(response, pid_num, A, B, C, D, E);
}
break;
}
} // decode_service_09_PID_content()
// decode the service 0x09 PID into a title
void decode_service_09_PID_title(int pid_num)
{
switch (pid_num)
{
case 0x00:
{
Serial.print("PIDs supported [0x01-0x20]");
}
break;
case 0x01:
{
Serial.print("VIN Message Count in POD 0x02 (usually = 5)");
}
break;
case 0x02:
{
Serial.print("Vehicle Identification Number (VIN)");
}
break;
case 0x03:
{
Serial.print("Calibration ID message count for PID 0x04");
}
break;
case 0x04:
{
Serial.print("Calibration ID");
}
break;
case 0x05:
{
Serial.print("Calibration verification numbers (CVN) message count for PID 0x06");
}
break;
case 0x06:
{
Serial.print("Calibration Verification Numbers (CVN)");
}
break;
case 0x07:
{
Serial.print("In-use performance tracking message count for PID 0x08 and PID 0x0B");
}
break;
case 0x08:
{
Serial.print("In-use performance tracking for spark ignition vehicles");
}
break;
case 0x09:
{
Serial.print("ECU name message count for PID 0x0A");
}
break;
case 0x0A:
{
Serial.print("ECU name");
}
break;
case 0x0B:
{
Serial.print("In-use performance tracking for compression ignition vehicles");
}
break;
}
} // decode_service_09_PID_title()
//
// Activation of CAN bus using magic 5 baud on k-line
//
// k-line HIGH, delay for 3 secs (no traffic on k-line for 3 seconds before starting)
// k-line LOW, delay for 200 msecs (start bit)
// k-line HIGH, delay for 400 msecs (two bits)
// k-line LOW, delay for 400 msecs (two bits)
// k-line HIGH, delay for 400 msecs (two bits)
// k-line LOW, delay for 400 msecs (two bits)
// k-line HIGH, delay for 200 msecs (stop bit)
// attempt to wake the CAN bus by sending 5-baud init sequence (0x33) on the K-line
void execute_kline_5_baud_init(void)
{
unsigned long last_millis;
Serial.println("");
Serial.println("sending 0x33 at 5-baud init sequence over K-line...starting...");
Serial.println("...pausing K-line interface for 3 seconds...");
digitalWrite(KLINE_PIN, KLINE_HIGH);
last_millis = millis();
while (millis() < (last_millis + 3000))
{
Can1.events();
}
Serial.println("...pulling K-line interface LOW for 200 milliseconds...");
digitalWrite(KLINE_PIN, KLINE_LOW);
last_millis = millis();
while (millis() < (last_millis + 200))
{
Can1.events();
}
Serial.println("...pulling K-line interface HIGH for 400 milliseconds...");
digitalWrite(KLINE_PIN, KLINE_HIGH);
last_millis = millis();
while (millis() < (last_millis + 400))
{
Can1.events();
}
Serial.println("...pulling K-line interface LOW for 400 milliseconds...");
digitalWrite(KLINE_PIN, KLINE_LOW);
last_millis = millis();
while (millis() < (last_millis + 400))
{
Can1.events();
}
Serial.println("...pulling K-line interface HIGH for 400 milliseconds...");
digitalWrite(KLINE_PIN, KLINE_HIGH);
last_millis = millis();
while (millis() < (last_millis + 400))
{
Can1.events();
}
Serial.println("...pulling K-line interface LOW for 400 milliseconds...");
digitalWrite(KLINE_PIN, KLINE_LOW);
last_millis = millis();
while (millis() < (last_millis + 400))
{
Can1.events();
}
Serial.println("...pulling K-line interface HIGH for 200 milliseconds...");
digitalWrite(KLINE_PIN, KLINE_HIGH);
last_millis = millis();
while (millis() < (last_millis + 200))
{
Can1.events();
}
Serial.println("sending 0x33 at 5-baud init sequence over K-line...complete...");
} // execute_kline 5_baud_init()
// repeated loop forever
void loop()
{
CAN_message_t msg;
char in_key = 0x00;
Can1.events();
if ((LED_timer) && ((millis() - LED_timer) > LED_DURATION_MSEC))
{
analogWrite(LED_PIN, LED_INTENSITY_OFF);
LED_timer = 0;
}
// if we're not parked on a baudrate, then see if it's time to hunt
if (!(CAN_baudrate_fixed))
{
// if we're currently successfully receiving CAN packets at this baudrate
if (CAN_baudrate_match_found)
{
if ((millis() - CAN_baudrate_match_timeout) > CAN_BAUDRATE_MATCH_TIMEOUT_MSEC)
{
// go back to scanning the baudrate list
CAN_baudrate_match_found = false;
}
} else {
if ((millis() - CAN_baud_change_timer) > CAN_BAUD_CHANGE_INTERVAL_MSEC)
{
next_CAN_baudrate();
report_current_baudrate();
analogWrite(LED_PIN, led_intensity_on);
delay(10 * LED_DURATION_MSEC);
analogWrite(LED_PIN, LED_INTENSITY_OFF);
delay(5 * LED_DURATION_MSEC);
analogWrite(LED_PIN, led_intensity_on);
delay(10 * LED_DURATION_MSEC);
analogWrite(LED_PIN, LED_INTENSITY_OFF);
CAN_baud_change_timer = millis();
}
}
}
while (Serial.available() > 0)
{
unsigned char cmd_key = in_key;
int service_num;
int pid_num;
int frame_num;
in_key = Serial.read();
bool report_current = false;
switch (key_state)
{
case KEY_STATE_CMD_KEY:
{
switch (in_key)
{
case LF:
{
report_current = true;
}
break;
// send all supported service 0x01 PID CAN commands (current data)
case 'a':
case 'A':
{
Serial.println("");
Serial.println("[ A: initiate send CAN all supported service 0x01 PIDs from the serial monitor command line ]");
Serial.println("");
send_CAN_all_supported_PIDs_for_service_01_02(0x01);
}
break;
// set the brightness of the onbaord LED
case 'b':
case 'B':
{
Serial.println("");
Serial.println("[ B: initiate set the LED brightness from the serial monitor command line ]");
Serial.println("");
if (led_intensity_on == LED_INTENSITY_ON_NORMAL)
{
led_intensity_on = LED_INTENSITY_ON_BRIGHT;
} else {
led_intensity_on = LED_INTENSITY_ON_NORMAL;
}
LED_timer = millis();
analogWrite(LED_PIN, led_intensity_on);
// save settings to EEPROM
save_settings();
}
break;
// send a collection of custom CAN commands
case 'c':
case 'C':
{
Serial.println("");
Serial.println("[ C: initiate send CAN custom commands from the serial monitor command line ]");
Serial.println("");
key_state = KEY_STATE_C_CMD_HEX0;
}
break;
// toggle CAN RX debug
case 'd':
case 'D':
{
Serial.println("");
Serial.println("[ D: initiate toggle the CAN RX debug details from the serial monitor command line ]");
Serial.println("");
debug_CAN_rx = !debug_CAN_rx;
// save settings to EEPROM
save_settings();
}
break;
// send CAN query for service 0x09 PID 0x0A (ECU name)
case 'e':
case 'E':
{
Serial.println("");
Serial.println("[ E: initiate send CAN service 0x09 PID 0x0A (ECU name) from the serial monitor command line ]");
Serial.println("");
send_CAN_query_service_09(0x0A);
}
break;
// send all supported service 0x02 PID CAN commands (freeze frame data)
case 'f':
case 'F':
{
Serial.println("");
Serial.println("[ F: initiate send CAN all supported service 0x02 PIDs from the serial monitor command line ]");
Serial.println("");
send_CAN_all_supported_PIDs_for_service_01_02(0x02);
}
break;
// hunt for a matching baudrate & stop there
case 'h':
case 'H':
{
Serial.println("");
Serial.println("[ H: initiate hunt for matching CAN baudrate from the serial monitor command line ]");
Serial.println("");
CAN_baudrate_fixed = !CAN_baudrate_fixed;
// save settings to EEPROM
save_settings();
}
break;
// execute K-line 5-baud init sequence
case 'k':
case 'K':
{
Serial.println("");
Serial.println("[ K: initiate execute the 5-baud 0x33 K-line init sequence from the serial monitor command line ]");
Serial.println("");
execute_kline_5_baud_init();
}
break;
// send CAN query for service 0x03 (all DTCs)
case 'l':
case 'L':
{
Serial.println("");
Serial.println("[ L: initiate send CAN service 0x03 (list all DTCs) from the serial monitor command line ]");
Serial.println("");
send_CAN_query_service_03();
}
break;
// next (higher) baudrate
case 'n':
case 'N':
{
Serial.println("");
Serial.println("[ N: initiate set next (higher) CAN baudrate from the serial monitor command line ]");
Serial.println("");
CAN_baudrate_fixed = true;
next_CAN_baudrate();
// save settings to EEPROM
save_settings();
}
break;
// previous (lower) baudrate
case 'p':
case 'P':
{
Serial.println("");
Serial.println("[ P: initiate set previous (lower) CAN baudrate from the serial monitor command line ]");
Serial.println("");
CAN_baudrate_fixed = true;
previous_CAN_baudrate();
// save settings to EEPROM
save_settings();
}
break;
// send CAN query for all supported service 0x01 PIDs
case 'q':
case 'Q':
{
Serial.println("");
Serial.println("[ Q: initiate send CAN query for all supported service 0x01 PIDs from the serial monitor command line ]");
Serial.println("");
send_CAN_query_supported_PIDs_for_service_01();
}
break;
// send CAN query for service 0x09 PID 0x02 (VIN)
case 'v':
case 'V':
{
Serial.println("");
Serial.println("[ V: initiate send CAN service 0x09 PID 0x02 (VIN) from the serial monitor command line ]");
Serial.println("");
send_CAN_query_service_09(0x02);
}
break;
// send CAN wakeup commands once
case 'w':
case 'W':
{
Serial.println("");
Serial.println("[ W: initiate send CAN wakeup commands from the serial monitor command line ]");
Serial.println("");
send_CAN_wakeup_commands();
}
break;
}
if ((cmd_key == 'w') || (cmd_key == 'W'))
{
report_current = false;
}
if (report_current)
{
report_current = false;
report_current_baudrate();
}
}
break;
case KEY_STATE_C_CMD_HEX0:
{
if ((in_key >= '0') && (in_key <= '9'))
{
service_num = 16 * (in_key - '0');
key_state = KEY_STATE_C_CMD_HEX1;
} else {
if ((in_key >= 'a') && (in_key <= 'f'))
{
service_num = 16 * ((in_key - 'a') + 10);
key_state = KEY_STATE_C_CMD_HEX1;
} else {
if ((in_key >= 'A') && (in_key <= 'F'))
{
service_num = 16 * ((in_key - 'A') + 10);
key_state = KEY_STATE_C_CMD_HEX1;
} else {
Serial.println("");
Serial.println("invalid custom CAN format (expected 'C' followed by four hex characters)...custom CAN command cancelled");
Serial.println("");
if (in_key == LF)
{
report_current_baudrate();
}
key_state = KEY_STATE_CMD_KEY;
}
}
}
}
break;
case KEY_STATE_C_CMD_HEX1:
{
if ((in_key >= '0') && (in_key <= '9'))
{
service_num += (in_key - '0');
key_state = KEY_STATE_C_CMD_HEX2;
} else {
if ((in_key >= 'a') && (in_key <= 'f'))
{
service_num += ((in_key - 'a') + 10);
key_state = KEY_STATE_C_CMD_HEX2;
} else {
if ((in_key >= 'A') && (in_key <= 'F'))
{
service_num += ((in_key - 'A') + 10);
key_state = KEY_STATE_C_CMD_HEX2;
} else {
Serial.println("");
Serial.println("invalid custom CAN format (expected 'C' followed by four hex characters)...custom CAN command cancelled");
Serial.println("");
if (in_key == LF)
{
report_current_baudrate();
}
key_state = KEY_STATE_CMD_KEY;
}
}
}
}
break;
case KEY_STATE_C_CMD_HEX2:
{
if ((in_key >= '0') && (in_key <= '9'))
{
pid_num = 16 * (in_key - '0');
key_state = KEY_STATE_C_CMD_HEX3;
} else {
if ((in_key >= 'a') && (in_key <= 'f'))
{
pid_num = 16 * ((in_key - 'a') + 10);
key_state = KEY_STATE_C_CMD_HEX3;
} else {
if ((in_key >= 'A') && (in_key <= 'F'))
{
pid_num = 16 * ((in_key - 'A') + 10);
key_state = KEY_STATE_C_CMD_HEX3;
} else {
Serial.println("");
Serial.println("invalid custom CAN format (expected 'C' followed by four hex characters)...custom CAN command cancelled");
Serial.println("");
if (in_key == LF)
{
report_current_baudrate();
}
key_state = KEY_STATE_CMD_KEY;
}
}
}
}
break;
case KEY_STATE_C_CMD_HEX3:
{
if ((in_key >= '0') && (in_key <= '9'))
{
pid_num += (in_key - '0');
key_state = KEY_STATE_CMD_KEY;
if ((service_num != 0x02) && (pid_num != 0x02))
{
key_state = KEY_STATE_CMD_KEY;
send_CAN_custom_command(service_num, pid_num, 0x00);
} else {
key_state = KEY_STATE_C_CMD_HEX4;
}
} else {
if ((in_key >= 'a') && (in_key <= 'f'))
{
pid_num += ((in_key - 'a') + 10);
if ((service_num != 0x02) && (pid_num != 0x02))
{
key_state = KEY_STATE_CMD_KEY;
send_CAN_custom_command(service_num, pid_num, 0x00);
} else {
key_state = KEY_STATE_C_CMD_HEX4;
}
} else {
if ((in_key >= 'A') && (in_key <= 'F'))
{
pid_num += ((in_key - 'A') + 10);
if ((service_num != 0x02) && (pid_num != 0x02))
{
key_state = KEY_STATE_CMD_KEY;
send_CAN_custom_command(service_num, pid_num, 0x00);
} else {
key_state = KEY_STATE_C_CMD_HEX4;
}
} else {
Serial.println("");
Serial.println("invalid custom CAN format (expected 'C' followed by four hex characters)...custom CAN command cancelled");
Serial.println("");
if (in_key == LF)
{
report_current_baudrate();
}
key_state = KEY_STATE_CMD_KEY;
}
}
}
}
break;
case KEY_STATE_C_CMD_HEX4:
{
if (in_key == LF)
{
send_CAN_custom_command(service_num, pid_num, 0x00);
report_current_baudrate();
key_state = KEY_STATE_CMD_KEY;
} else {
if ((in_key >= '0') && (in_key <= '9'))
{
frame_num = 16 * (in_key - '0');
key_state = KEY_STATE_C_CMD_HEX5;
} else {
if ((in_key >= 'a') && (in_key <= 'f'))
{
frame_num = 16 * ((in_key - 'a') + 10);
key_state = KEY_STATE_C_CMD_HEX5;
} else {
if ((in_key >= 'A') && (in_key <= 'F'))
{
frame_num = 16 * ((in_key - 'a') + 10);
key_state = KEY_STATE_C_CMD_HEX5;
} else {
Serial.println("");
Serial.println("invalid custom CAN format (expected 'C' followed by four hex characters)...custom CAN command cancelled");
Serial.println("");
key_state = KEY_STATE_CMD_KEY;
}
}
}
}
}
break;
case KEY_STATE_C_CMD_HEX5:
{
if ((in_key >= '0') && (in_key <= '9'))
{
frame_num += (in_key - '0');
key_state = KEY_STATE_CMD_KEY;
send_CAN_custom_command(service_num, pid_num, frame_num);
} else {
if ((in_key >= 'a') && (in_key <= 'f'))
{
frame_num += ((in_key - 'a') + 10);
key_state = KEY_STATE_CMD_KEY;
send_CAN_custom_command(service_num, pid_num, frame_num);
} else {
if ((in_key >= 'A') && (in_key <= 'F'))
{
frame_num += ((in_key - 'a') + 10);
key_state = KEY_STATE_CMD_KEY;
send_CAN_custom_command(service_num, pid_num, frame_num);
} else {
Serial.println("");
Serial.println("invalid custom CAN format (expected 'C' followed by four hex characters)...custom CAN command cancelled");
Serial.println("");
key_state = KEY_STATE_CMD_KEY;
}
}
}
}
break;
}
}
} // loop()
void next_CAN_baudrate(void)
{
if (++CAN_baudrate_index >= CAN_baudrate_size)
{
CAN_baudrate_index = 0;
}
Can1.reset();
Can1.begin();
Can1.setBaudRate(CAN_baudrate_list[CAN_baudrate_index]);
send_CAN_wakeup_commands();
} // next_CAN_baudrate
void previous_CAN_baudrate(void)
{
if (--CAN_baudrate_index < 0)
{
CAN_baudrate_index = CAN_baudrate_size - 1;
}
Can1.reset();
Can1.begin();
Can1.setBaudRate(CAN_baudrate_list[CAN_baudrate_index]);
send_CAN_wakeup_commands();
} // previous_CAN_baudrate
// read the current settings from EEPROM
bool read_settings(void)
{
byte eeprom_value, xor_value, inv_xor_value;
byte xor_result = 0x4d; // start with a non-zero value
bool header_is_good = true;
bool eeprom_settings_good = false;
Serial.println("");
Serial.print("Attempting to read/verify saved settings (");
Serial.print(EEPROM_INDEX_VALUE_COUNT);
Serial.print(" values) from EEPROM...");
for (byte eeprom_index = EEPROM_INDEX_HEADER_FIRST; eeprom_index < EEPROM_INDEX_CHECKSUM; eeprom_index++)
{
EEPROM.get((int)(eeprom_index), eeprom_value);
xor_result = xor_result ^ eeprom_value;
#ifdef DEBUG_EEPROM_READ
if (eeprom_index == EEPROM_INDEX_HEADER_FIRST)
{
Serial.println("");
}
Serial.print("(READ ");
show_index((int)(eeprom_index));
Serial.print(": ");
show_byte_value(eeprom_value);
Serial.println(")");
#endif
if (eeprom_index <= EEPROM_INDEX_HEADER_LAST)
{
if (eeprom_value != EEPROM_HEADER[eeprom_index])
{
header_is_good = false;
}
}
}
// read the checksum & inverse checksum
EEPROM.get(EEPROM_INDEX_CHECKSUM, xor_value);
EEPROM.get(EEPROM_INDEX_INV_CHECKSUM, inv_xor_value);
// if the checksums match & the header values match, then we seem to have valid settings in EEPROM, so read all of the settings
if ((xor_value == xor_result) &&
(inv_xor_value == (byte)~xor_result) &&
(header_is_good))
{
Serial.print("verified settings (");
Serial.print(EEPROM_INDEX_VALUE_COUNT);
Serial.println(" values) in EEPROM are valid...");
Serial.println("");
#ifndef DISABLE_EEPROM_READ_SETTINGS
for (byte eeprom_index = EEPROM_INDEX_HEADER_FIRST; eeprom_index <= EEPROM_INDEX_INV_CHECKSUM; eeprom_index++)
{
EEPROM.get((int)(eeprom_index), eeprom_value);
#ifdef DEBUG_EEPROM_READ
Serial.print("(READ ");
show_index((int)(eeprom_index));
Serial.print(": ");
show_byte_value(eeprom_value);
#endif
switch (eeprom_index)
{
case EEPROM_INDEX_HEADER_00:
case EEPROM_INDEX_HEADER_01:
case EEPROM_INDEX_HEADER_02:
case EEPROM_INDEX_HEADER_03:
{
#ifdef DEBUG_EEPROM_READ
Serial.print(") Header[");
Serial.print(eeprom_index / 10);
Serial.print(eeprom_index % 10);
Serial.print("] = ");
Serial.println((char)eeprom_value);
#endif
}
break;
case EEPROM_INDEX_CAN_BAUDRATE_FIXED:
{
if (eeprom_value & 0x01)
{
CAN_baudrate_fixed = true;
} else {
CAN_baudrate_fixed = false;
}
#ifdef DEBUG_EEPROM_READ
Serial.print(") CAN baudrate is fixed = ");
if (eeprom_value & 0x01)
{
Serial.println("ON");
} else {
Serial.println("OFF");
}
#endif
}
break;
case EEPROM_INDEX_CAN_BAUDRATE_INDEX:
{
CAN_baudrate_index = eeprom_value;
#ifdef DEBUG_EEPROM_READ
Serial.print(") CAN baudrate index = ");
Serial.println(eeprom_value);
#endif
}
break;
case EEPROM_INDEX_DEBUG_CAN_RX:
{
debug_CAN_rx = eeprom_value;
#ifdef DEBUG_EEPROM_READ
Serial.print(") debug CAN RX setting = ");
if (eeprom_value & 0x01)
{
Serial.println("ON");
} else {
Serial.println("OFF");
}
#endif
}
break;
case EEPROM_INDEX_LED_BRIGHTNESS:
{
led_intensity_on = eeprom_value;
#ifdef DEBUG_EEPROM_READ
Serial.print(") LED intensity on value = ");
Serial.println(eeprom_value);
#endif
}
break;
case EEPROM_INDEX_CHECKSUM:
{
eeprom_value = (char)(xor_result);
#ifdef DEBUG_EEPROM_READ
Serial.print(") Calculated CHECKSUM = ");
show_byte_value(xor_result);
Serial.println("");
#endif
}
break;
case EEPROM_INDEX_INV_CHECKSUM:
{
eeprom_value = (char)(~xor_result);
#ifdef DEBUG_EEPROM_READ
Serial.print(") Calculated INVERSE CHECKSUM = ");
show_byte_value((byte)~xor_result);
Serial.println("");
#endif
}
break;
}
}
#endif
eeprom_settings_good = true;
} else {
Serial.println("EEPROM values failed checksum verification...");
Serial.println("");
Serial.println("Discarding invalid EEPROM settings & storing/using defaults...");
Serial.println("");
#ifdef DEBUG_EEPROM_READ
if (!header_is_good)
{
Serial.println("HEADER mismatch between EEPROM values & expected values...");
} else {
Serial.println("CHECKSUM mismatch between EEPROM values & expected values...");
Serial.print("(READ ");
show_index((int)(EEPROM_INDEX_CHECKSUM));
Serial.print (") xor_value = ");
Serial.println(xor_value);
Serial.print("(CALC) xor_result = ");
Serial.println(xor_result);
Serial.print("(READ ");
show_index((int)(EEPROM_INDEX_INV_CHECKSUM));
Serial.print (") inv_xor_value = ");
Serial.println(inv_xor_value);
Serial.print("(CALC) inv_xor_result = ");
Serial.println((byte)~xor_result);
}
Serial.println("");
#endif
eeprom_settings_good = false;
}
return (eeprom_settings_good);
} // read_settings()
void report_current_baudrate(void)
{
Serial.println("");
Serial.print("CAN baudrate set to: ");
Serial.print(CAN_baudrate_list[CAN_baudrate_index]);
if (CAN_baudrate_fixed)
{
Serial.println(" (not scanning)");
} else {
Serial.println(" (scanning)");
}
show_CONTROL_MENU();
} // report_current_baudrate()
// save the current settings to EEPROM
void save_settings(void)
{
byte xor_result = 0x4D; // start with a non-zero value
char eeprom_value = 0x00;
Serial.println("");
Serial.print("Saving settings (");
Serial.print(EEPROM_INDEX_VALUE_COUNT);
Serial.println(" values) to EEPROM...");
Serial.println("");
for (byte eeprom_index = (byte)(EEPROM_INDEX_HEADER_FIRST); eeprom_index <= (byte)(EEPROM_INDEX_INV_CHECKSUM); eeprom_index++)
{
#ifdef DEBUG_EEPROM_WRITE
Serial.print("(WRITE ");
show_index((int)(eeprom_index));
Serial.print(": ");
#endif
switch (eeprom_index)
{
case EEPROM_INDEX_HEADER_00:
case EEPROM_INDEX_HEADER_01:
case EEPROM_INDEX_HEADER_02:
case EEPROM_INDEX_HEADER_03:
{
eeprom_value = EEPROM_HEADER[eeprom_index];
#ifdef DEBUG_EEPROM_WRITE
show_byte_value(eeprom_value);
Serial.print(") Header[");
Serial.print(eeprom_index / 10);
Serial.print(eeprom_index % 10);
Serial.print("] = ");
Serial.println(eeprom_value);
#endif
}
break;
case EEPROM_INDEX_CAN_BAUDRATE_FIXED:
{
if (CAN_baudrate_fixed)
{
eeprom_value = 0x01;
} else {
eeprom_value = 0x00;
}
#ifdef DEBUG_EEPROM_WRITE
show_byte_value(eeprom_value);
Serial.print(") CAN baudrate is fixed = ");
if (eeprom_value & 0x01)
{
Serial.println("ON");
} else {
Serial.println("OFF");
}
#endif
}
break;
case EEPROM_INDEX_CAN_BAUDRATE_INDEX:
{
eeprom_value = CAN_baudrate_index;
#ifdef DEBUG_EEPROM_WRITE
show_byte_value(eeprom_value);
Serial.print(") CAN baudrate index = ");
show_byte_value(eeprom_value);
Serial.println("");
#endif
}
break;
case EEPROM_INDEX_DEBUG_CAN_RX:
{
eeprom_value = debug_CAN_rx;
#ifdef DEBUG_EEPROM_WRITE
show_byte_value(eeprom_value);
Serial.print(") debug CAN RX setting = ");
if (eeprom_value & 0x01)
{
Serial.println("ON");
} else {
Serial.println("OFF");
}
#endif
}
break;
case EEPROM_INDEX_LED_BRIGHTNESS:
{
eeprom_value = led_intensity_on;
#ifdef DEBUG_EEPROM_WRITE
show_byte_value(eeprom_value);
Serial.print(") LED brightness value = ");
show_byte_value(eeprom_value);
Serial.println("");
#endif
}
break;
case EEPROM_INDEX_CHECKSUM:
{
eeprom_value = (char)(xor_result);
#ifdef DEBUG_EEPROM_WRITE
show_byte_value(eeprom_value);
Serial.print(") Calculated CHECKSUM = ");
show_byte_value(xor_result);
Serial.println("");
#endif
}
break;
case EEPROM_INDEX_INV_CHECKSUM:
{
eeprom_value = (char)(~xor_result);
#ifdef DEBUG_EEPROM_WRITE
show_byte_value(eeprom_value);
Serial.print(") Calculated INVERSE CHECKSUM = ");
show_byte_value((byte)~xor_result);
Serial.println("");
#endif
}
break;
}
#ifndef DISABLE_EEPROM_WRITE_SETTINGS
EEPROM.update((int)(eeprom_index), (byte)(eeprom_value));
#endif
if (eeprom_index < EEPROM_INDEX_CHECKSUM)
{
xor_result = (byte)(xor_result ^ eeprom_value);
}
}
} // save_settings()
// send all supported service 0x01 (current data) or service 0x02 (freeze frame) PID CAN requests
void send_CAN_all_supported_PIDs_for_service_01_02(int service_num)
{
CAN_message_t can_MsgTx;
unsigned long start_wait_time = millis(); // timer for waiting up to 3 seconds for the response to Service 0x01 PID 0x01 request to return
if (service_num == 0x01)
{
for (int pid_num = 0; pid_num < 256; pid_num++)
{
if ((supported_PIDs_for_service_01[pid_num]) && (pid_num % 0x20))
{
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
can_MsgTx.buf[0] = 0x02; // single frame, 2 data bytes
can_MsgTx.buf[1] = service_num;
can_MsgTx.buf[2] = pid_num;
can_MsgTx.buf[3] = 0x00;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
}
}
} else {
DTC_count = 0xFF;
// first, query how many DTCs (& therefore, how many freeze frames) we have
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
can_MsgTx.buf[0] = 0x02; // single frame, 2 data bytes
can_MsgTx.buf[1] = CAN_STANDARD_SERVICE_MODE_1_0x01;
can_MsgTx.buf[2] = 0x01;
can_MsgTx.buf[3] = 0x00;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
while ((millis() < (start_wait_time + 3000)) && (DTC_count == 0xFF))
{
Can1.events();
}
if ((DTC_count != 0xFF) && (DTC_count > 0))
{
for (int frame_num = 0; frame_num < DTC_count; frame_num++)
{
// query the DTC that caused the freeze frame to be captured
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
can_MsgTx.buf[0] = 0x03; // single frame, 3 data bytes
can_MsgTx.buf[1] = CAN_STANDARD_SERVICE_MODE_2_0x02;
can_MsgTx.buf[2] = 0x02;
can_MsgTx.buf[3] = frame_num;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
for (int pid_num = 0; pid_num < 256; pid_num++)
{
if ((supported_PIDs_for_service_01[pid_num]) && (pid_num % 0x20))
{
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
can_MsgTx.buf[0] = 0x03; // single frame, 3 data bytes
can_MsgTx.buf[1] = CAN_STANDARD_SERVICE_MODE_2_0x02;
can_MsgTx.buf[2] = pid_num;
can_MsgTx.buf[3] = frame_num;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
}
}
}
} else {
Serial.println("");
Serial.println("[ No freeze frame data from any DTC errors saved / available ]");
Serial.println("");
}
}
} // send_CAN_all_supported_PIDs_for_service_01_02()
// send CAN custom command
void send_CAN_custom_command(int service_num, int pid_num, int frame_num)
{
CAN_message_t can_MsgTx;
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
if (service_num == 0x02)
{
can_MsgTx.buf[0] = 0x03; // single frame, 3 data bytes (service, pid, frame)
} else {
can_MsgTx.buf[0] = 0x02; // single frame, 2 data bytes (service, pid)
}
can_MsgTx.buf[1] = service_num;
can_MsgTx.buf[2] = pid_num;
if (service_num == 0x02)
{
can_MsgTx.buf[3] = frame_num;
} else {
can_MsgTx.buf[3] = 0x00;
}
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
} // send_CAN_custom_commands()
// send CAN isotp flow control frame
void send_CAN_isotp_flow_control(void)
{
ISOTP_data isotp_msg;
isotp_msg.len = 8;
isotp_msg.flags.extended = 1;
isotp_msg.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
isotp1.sendFlowControl(isotp_msg);
} // send_CAN_isotp_flow_control()
// send CAN query for service 0x03 (list DTCs)
void send_CAN_query_service_03()
{
CAN_message_t can_MsgTx;
// first, query all current DTCs service 0x03
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
can_MsgTx.buf[0] = 0x01; // single frame, 2 data bytes
can_MsgTx.buf[1] = CAN_STANDARD_SERVICE_MODE_3_0x03;
can_MsgTx.buf[2] = 0x00;
can_MsgTx.buf[3] = 0x00;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
} // void send_CAN_query_service_03()
// send CAN query for service 0x09 (vehicle info)
void send_CAN_query_service_09(int pid_num)
{
CAN_message_t can_MsgTx;
unsigned long start_wait_time = millis(); // timer for waiting up to 3 seconds for the response to Service 0x09 PID 0x01 request to return
service_09_supported_PID_response_rxd = false;
// first, query which service 0x09 PIDs are supported
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
can_MsgTx.buf[0] = 0x02; // single frame, 2 data bytes
can_MsgTx.buf[1] = CAN_STANDARD_SERVICE_MODE_9_0x09;
can_MsgTx.buf[2] = 0x00;
can_MsgTx.buf[3] = 0x00;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
while ((millis() < (start_wait_time + 3000)) && (service_09_supported_PID_response_rxd == false))
{
Can1.events();
}
switch(pid_num)
{
case 0x02: // query VIN
{
if (supported_PIDs_for_service_09[0x02])
{
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
can_MsgTx.buf[0] = 0x02; // single frame, 2 data bytes
can_MsgTx.buf[1] = CAN_STANDARD_SERVICE_MODE_9_0x09;
can_MsgTx.buf[2] = 0x02;
can_MsgTx.buf[3] = 0x00;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
} else {
Serial.println("");
Serial.println("[ Service 0x09 PID 0x02 reported as not supported - VIN can't be queried ]");
Serial.println("");
}
}
break;
case 0x0A:
{
if (supported_PIDs_for_service_09[0x0A])
{
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
can_MsgTx.buf[0] = 0x02; // single frame, 2 data bytes
can_MsgTx.buf[1] = CAN_STANDARD_SERVICE_MODE_9_0x09;
can_MsgTx.buf[2] = 0x0A;
can_MsgTx.buf[3] = 0x00;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
} else {
Serial.println("");
Serial.println("[ Service 0x09 PID 0x0A reported as not supported - ECU name can't be queried ]");
Serial.println("");
}
}
break;
}
} // send_CAN_query_service_09()
// send CAN query for all supported service 0x01 PIDs
void send_CAN_query_supported_PIDs_for_service_01(void)
{
CAN_message_t can_MsgTx;
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
can_MsgTx.buf[0] = 0x02; // single frame, 2 data bytes
can_MsgTx.buf[1] = CAN_STANDARD_SERVICE_MODE_1_0x01;
can_MsgTx.buf[2] = CAN_SERVICE_1_PIDS_SUPPORTED_01_20_0X00;
can_MsgTx.buf[3] = 0x00;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
can_MsgTx.buf[0] = 0x02; // single frame, 2 data bytes
can_MsgTx.buf[1] = CAN_STANDARD_SERVICE_MODE_1_0x01;
can_MsgTx.buf[2] = CAN_SERVICE_1_PIDS_SUPPORTED_21_40_0X20;
can_MsgTx.buf[3] = 0x00;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
can_MsgTx.buf[0] = 0x02; // single frame, 2 data bytes
can_MsgTx.buf[1] = CAN_STANDARD_SERVICE_MODE_1_0x01;
can_MsgTx.buf[2] = CAN_SERVICE_1_PIDS_SUPPORTED_41_60_0X40;
can_MsgTx.buf[3] = 0x00;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
can_MsgTx.buf[0] = 0x02; // single frame, 2 data bytes
can_MsgTx.buf[1] = CAN_STANDARD_SERVICE_MODE_1_0x01;
can_MsgTx.buf[2] = CAN_SERVICE_1_PIDS_SUPPORTED_61_80_0X60;
can_MsgTx.buf[3] = 0x00;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
can_MsgTx.buf[0] = 0x02; // single frame, 2 data bytes
can_MsgTx.buf[1] = CAN_STANDARD_SERVICE_MODE_1_0x01;
can_MsgTx.buf[2] = CAN_SERVICE_1_PIDS_SUPPORTED_81_A0_0X80;
can_MsgTx.buf[3] = 0x00;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
can_MsgTx.buf[0] = 0x02; // single frame, 2 data bytes
can_MsgTx.buf[1] = CAN_STANDARD_SERVICE_MODE_1_0x01;
can_MsgTx.buf[2] = CAN_SERVICE_1_PIDS_SUPPORTED_A1_C0_0XA0;
can_MsgTx.buf[3] = 0x00;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
can_MsgTx.buf[0] = 0x02; // single frame, 2 data bytes
can_MsgTx.buf[1] = CAN_STANDARD_SERVICE_MODE_1_0x01;
can_MsgTx.buf[2] = CAN_SERVICE_1_PIDS_SUPPORTED_C1_E0_0XC0;
can_MsgTx.buf[3] = 0x00;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
can_MsgTx.buf[0] = 0x02; // single frame, 2 data bytes
can_MsgTx.buf[1] = CAN_STANDARD_SERVICE_MODE_1_0x01;
can_MsgTx.buf[2] = CAN_SERVICE_1_PIDS_SUPPORTED_E1_FF_0XE0;
can_MsgTx.buf[3] = 0x00;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
} // send_CAN_query_supported_PIDs_for_service_01()
// HINT on waking up CAN bus from PaulS on Teensy forum (https://forum.pjrc.com/threads/63063-Reading-data-from-CAN-bus-volkswagen?p=311882&viewfull=1#post311882)
//
// When reading Adventures in Automotive Networks and Control Units (https://illmatics.com/car_hacking.pdf), on page 14 I found this:
//
// DiagnosticSessionControl
//
// This establishes a diagnostic session with the ECU and is usually necessary before any other commands can be sent.
// IDH: 07, IDL: E0, Len: 08, Data: 02 10 03 00 00 00 00 00
//
// After sending that CAN frame, the reply for a successful establishment should be:
// IDH: 07, IDL: E8, Len: 08, Data: 06 50 03 00 32 01 F4 00
// send CAN commands to try & wake-up the CAN bus
void send_CAN_wakeup_commands(void)
{
CAN_message_t can_MsgTx;
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 0;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_DIAGNOSTIC_BROADCAST_REQUEST_0X7DF;
can_MsgTx.buf[0] = 0x02; // single frame, 2 data bytes
can_MsgTx.buf[1] = CAN_DIAGNOSTIC_SESSION_CONTROL_0X10;
can_MsgTx.buf[2] = CAN_EXTENDED_DIAGNOSTIC_SESSION_0X03;
can_MsgTx.buf[3] = 0x00;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
can_MsgTx.len = 8;
can_MsgTx.flags.extended = 1;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = CAN_EXTENDED_DIAGNOSTIC_SESSION_REQUEST_0X18DB33F1;
can_MsgTx.buf[0] = 0x02; // single frame, 2 data bytes
can_MsgTx.buf[1] = CAN_DIAGNOSTIC_SESSION_CONTROL_0X10;
can_MsgTx.buf[2] = CAN_EXTENDED_DIAGNOSTIC_SESSION_0X03;
can_MsgTx.buf[3] = 0x00;
can_MsgTx.buf[4] = 0x00;
can_MsgTx.buf[5] = 0x00;
can_MsgTx.buf[6] = 0x00;
can_MsgTx.buf[7] = 0x00;
can_TX(can_MsgTx);
} // send_CAN_wakeup_commands()
// one-time setup
void setup(void)
{
Serial.begin(115200);
while (!Serial && (millis() <= 3000));
Serial.println("=============================================");
Serial.print(" ");
Serial.println(TITLE);
Serial.print(" ");
Serial.println(VERSION);
Serial.println(AUTHOR);
Serial.println("=============================================");
Serial.println("");
Serial.println("");
if (CrashReport) {
Serial.print(CrashReport);
}
// try to read the settings from EEPROM for this index
if (!read_settings())
{
// if the setting in EEPROM for this index are invalid, then set to default values & save
CAN_baudrate_index = CAN_baudrate_size - 1;
CAN_baudrate_fixed = false;
save_settings();
}
Serial.println("");
Serial.print("EEPROM USED: ");
Serial.println(EEPROM_INDEX_VALUE_COUNT);
Serial.print("EEPROM MAX ALLOWED: ");
Serial.println(MAX_T4X_EEPROM_SIZE_ALLOWED);
Serial.println("");
Can1.begin();
Can1.setBaudRate(CAN_baudrate_list[CAN_baudrate_index]);
Can1.setMaxMB(16);
Can1.enableFIFO();
Can1.enableFIFOInterrupt();
Can1.onReceive(can_sniff_RX);
isotp1.begin();
isotp1.setWriteBus(&Can1); /* we write to this bus */
isotp1.onReceive(can_sniff_RX_isotp); /* set callback */
report_current_baudrate();
for (int i = 0; i < 256; i++)
{
supported_PIDs_for_service_01[i] = false;
}
for (int i = 0; i < 33; i++)
{
supported_PIDs_for_service_09[i] = false;
}
CAN_baud_change_timer = millis();
pinMode(LED_PIN, OUTPUT);
analogWrite(LED_PIN, LED_INTENSITY_OFF);
delay(100);
analogWrite(LED_PIN, led_intensity_on);
delay(100);
analogWrite(LED_PIN, LED_INTENSITY_OFF);
delay(100);
analogWrite(LED_PIN, led_intensity_on);
delay(100);
analogWrite(LED_PIN, LED_INTENSITY_OFF);
delay(100);
analogWrite(LED_PIN, led_intensity_on);
delay(100);
analogWrite(LED_PIN, LED_INTENSITY_OFF);
delay(100);
analogWrite(LED_PIN, led_intensity_on);
delay(100);
analogWrite(LED_PIN, LED_INTENSITY_OFF);
delay(100);
analogWrite(LED_PIN, led_intensity_on);
delay(100);
analogWrite(LED_PIN, LED_INTENSITY_OFF);
pinMode(KLINE_PIN, OUTPUT);
digitalWrite(KLINE_PIN, KLINE_HIGH);
LED_timer = millis();
} // setup()
// show index of EEPROM reads/writes
void show_byte_value(unsigned char byte_value)
{
if (byte_value < 100)
{
Serial.print("0");
} else {
Serial.print((char)(((byte_value / 100) % 10) + 0x30));
}
if (byte_value < 10)
{
Serial.print("0");
} else {
Serial.print((char)(((byte_value / 10) % 10) + 0x30));
}
Serial.print((char)((byte_value % 10) + 0x30));
} // show_byte_value()
// show CAN CONTROL MENU choices
void show_CONTROL_MENU(void)
{
Serial.println("");
Serial.println("CONTROL MENU (send from the serial monitor command line, terminated with LF or CR/LF):");
Serial.println("");
Serial.println(" A[*] : send CAN request for all supported service 0x01 (current data) PIDs");
if (led_intensity_on == LED_INTENSITY_ON_BRIGHT)
{
Serial.println(" B[+] : LED intensity NORMAL or BRIGHT (toggle between the two selections)");
} else {
Serial.println(" B[-] : LED intensity NORMAL or BRIGHT (toggle between the two selections)");
}
Serial.println(" C[*] : send CAN CUSTOM command (specify 2-char hex SERVICE, 2-char hex PID, & optional (only for Service #2) 2-char hex FRAME)");
if (debug_CAN_rx)
{
Serial.println(" D[+] : DEBUG CAN receive details (toggle between enable & disable)");
} else {
Serial.println(" D[-] : DEBUG CAN receive details (toggle between enable & disable)");
}
Serial.println(" E[*] : send CAN query for ECU name (Service 0x09 PID 0x0A)");
Serial.println(" F[*] : send CAN request for all supported service 0x02 (freeze frame data) PIDs");
if (!(CAN_baudrate_fixed))
{
Serial.println(" H[+] : HUNT for a matching CAN baudrate (scanning)");
} else {
Serial.println(" H[-] : HUNT for a matching CAN baudrate (scanning)");
}
Serial.println(" K[*] : execute K-line 5-baud init sequence");
Serial.println(" L[*} : send CAN query for LIST DTCs (Service 0x03)");
Serial.println(" N[*] : NEXT (higher) CAN baudrate (not scanning)");
Serial.println(" P[*] : PREVIOUS (lower) CAN baudrate (not scanning)");
Serial.println(" Q[*] : send CAN QUERY for all supported service 0x01 PIDs");
Serial.println(" V[*} : send CAN query for VIN (Service 0x09 PID 0x02)");
Serial.println(" W[*] : send CAN WAKEUP commands");
Serial.println("");
Serial.println(" NOTE: [*] indicates an immediate command");
Serial.println(" [+] indicates a toggle that is currently enabled");
Serial.println(" [-] indicates a toggle that is currently disabled");
Serial.println("");
} // show_CONTROL_MENU()
// show index of EEPROM reads/writes
void show_index(int index)
{
if (index < 1000)
{
Serial.print("0");
} else {
Serial.print((char)((index / 1000) + 0x30));
}
if (index < 100)
{
Serial.print("0");
} else {
Serial.print((char)(((index / 100) % 10) + 0x30));
}
if (index < 10)
{
Serial.print("0");
} else {
Serial.print((char)(((index / 10) % 10) + 0x30));
}
Serial.print((char)((index % 10) + 0x30));
} // show_index()
// EOF PLACEHOLDER