Reading data from CAN bus volkswagen

Paul:

Thanks for finding that golden nugget of info !! It will be after the Labor Day holiday before I can get back home to test this . . .

I will give that a try & let you know what I find out !!

Thanks again,

Mark J Culross
KD5RXT

Paul:

I added the ability to send that 0x7e0 02 10 03 00 00 00 00 00 of length 08 diagnostic session packet, but that alone did not activate the CAN bus in my 2020 Honda Civic. Next, I'll try the K-line init sequence that you detailed in P47 to see if that brings things to life.

Thanks again,

Mark J Culross
KD5RXT

P.S. I successfully captured the following data on my Teensy CAN Bus Monitor while using my ELM327-based OBDII adapter (another of my tools, different from the Actron CP9145) to activate the CAN bus. From this capture, I have to assume that this ELM327 device does something else to activate the CAN bus (maybe the K-line init sequence ??), since we see that the first packet sent across the CAN bus seems to be a "show current data" packet.

Code:
=============================================
       Teensy CAN bus monitor utility
     version 1.0 dated 09/08/2022 @1100
designed & written by Mark J Culross (KD5RXT)
=============================================



Attempting to read/verify saved settings (8 values) from EEPROM...verified settings (8 values) in EEPROM are valid...


EEPROM USED: 8
EEPROM MAX ALLOWED: 4284


CAN baudrate set to: 500000 (not scanning)

CONTROL MENU (send from the serial monitor, terminated with LF or CR/LF):
   B: LED intensity NORMAL or BRIGHT (toggle between the two selections)
   F: FIXED CAN baudrate (not scanning)
   H: HUNT for a matching CAN baudrate (scanning)
   N: NEXT (higher) CAN baudrate (not scanning)
   P: PREVIOUS (lower) CAN baudrate (not scanning)
   W: send CAN WAKEUP commands


CAN baudrate set to: 500000 (not scanning)

CONTROL MENU (send from the serial monitor, terminated with LF or CR/LF):
   B: LED intensity NORMAL or BRIGHT (toggle between the two selections)
   F: FIXED CAN baudrate (not scanning)
   H: HUNT for a matching CAN baudrate (scanning)
   N: NEXT (higher) CAN baudrate (not scanning)
   P: PREVIOUS (lower) CAN baudrate (not scanning)
   W: send CAN WAKEUP commands

RX: MBX: 04  LEN: 8  EXT: 1  TS: 59360  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x00 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 59900  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x00 0xB6 0x3C 0xA8 0x13 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 53388  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x00 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 53843  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x00 0xB6 0x3C 0xA8 0x13 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 32038  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x00 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 32491  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x00 0xB6 0x3C 0xA8 0x13 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 50412  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x00 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 50866  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x00 0xB6 0x3C 0xA8 0x13 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 11873  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x00 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 12516  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x00 0xB6 0x3C 0xA8 0x13 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 63125  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x20 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 63577  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x20 0x90 0x07 0xE0 0x11 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 35708  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x40 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 36982  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x40 0xF2 0xC0 0x8C 0x01 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 12043  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x60 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 12497  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x60 0x67 0x10 0x00 0x01 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 50790  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x80 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 51459  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x80 0x00 0x04 0x00 0x0F 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 12216  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0xA0 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 12674  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0xA0 0x04 0x00 0x00 0x00 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 22928  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 23373  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS:  1140  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS:  1586  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 41761  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 42207  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 22476  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 23750  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS:  1310  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS:  1755  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 45065  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 45510  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 56985  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 57489  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 58364  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x2F 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 58809  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x2F 0x55 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 20951  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 21407  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 54071  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 54694  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 17910  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 18356  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 51659  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 52330  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 19249  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0B 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 21195  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0B 0x61 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 44826  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x10 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 60130  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0C 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 60862  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x0C 0x00 0x00 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 23486  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 23931  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 45218  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0F 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 62443  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x33 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 63566  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x33 0x61 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 23778  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x66 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 24233  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x07 0x41 0x66 0x01 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 51416  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0B 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 51869  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0B 0x61 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS:  7620  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x10 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 18729  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0C 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 19179  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x0C 0x00 0x00 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 39355  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 39800  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 59833  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0F 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 38539  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x33 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 38989  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x33 0x61 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 57775  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x24 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 58498  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x24 0xFF 0xFF 0x44 0x3C 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 14695  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x34 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 30135  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x66 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 30591  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x07 0x41 0x66 0x01 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS:  5313  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0B 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS:  7161  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0B 0x61 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 28295  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x10 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 46812  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0C 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 47262  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x0C 0x00 0x00 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS:  3777  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS:  4414  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 44987  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0F 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 62876  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x24 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 63326  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x24 0xFF 0xFF 0x44 0x33 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 19075  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x34 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 40103  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x66 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 40559  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x07 0x41 0x66 0x01 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 55103  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0B 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 55565  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0B 0x61 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 11436  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0C 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 11993  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x0C 0x00 0x00 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 33935  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 34572  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 47066  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x24 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 47515  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x24 0xFF 0xFF 0x44 0x3C 0x55 
RX: MBX: 04  LEN: 8  EXT: 1  TS:  2640  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x34 0x00 0x00 0x00 0x00 0x00
 
Last edited:
Hi Mark, thanks for testing.
I added the ability to send that 0x7e0 02 10 03 00 00 00 00 00 of length 08 diagnostic session packet, but that alone did not activate the CAN bus in my 2020 Honda Civic
So you did not receive the reply 0x07E8 06 50 03 00 32 01 F4 00, length 8?

Another observation, your Teensy CAN bus monitor utility output shows this:
Code:
RX: MBX: 04  LEN: 8  EXT: 1  TS: 59360  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x00 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 59900  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x00 0xB6 0x3C 0xA8 0x13 0x55
...

Those are extended 29-bits ID CAN messages, not the standard 11-bit ID CAN I expected. Could that be an issue? Did you perhaps send the diagnostic session packet above as an extended 29-bits ID CAN message?

Paul
 
Hi Mark, thanks for testing.

So you did not receive the reply 0x07E8 06 50 03 00 32 01 F4 00, length 8?

Those are extended 29-bits ID CAN messages, not the standard 11-bit ID CAN I expected. Could that be an issue? Did you perhaps send the diagnostic session packet above as an extended 29-bits ID CAN message?

Paul

Paul:

The data that I included was everything which was captured when the ELM327 activated the CAN bus. My particular ELM327 unit makes a bluetooth connection with my phone. Prior to my phone & the ELM327 establishing the bluetooth connection, the CAN bus showed absolutely no activity at all. When I commanded my Teensy CAN bus monitor to send the diagnostic session command (while in this idle CAN bus state, before the BT connection was established between phone & ELM327), neither the expected reply, nor anything else, was detected/received on the bus. I tried both the "11-bit standard frame" format & the "29-bit extended frame" format. All send/receive activity on the CAN bus was monitored/observed using the Seeed Studios CAN bus analyzer that you recommended to Sandra. I also tried having the Seeed CAN bus analyzer to send the same diagnostic session command (both formats), with the same silent results. Only the ELM327 has successfully activated the CAN bus, but not by sending a CAN command frame (as indicated by the data frames captured, unless it is initiating these commands at a different/lower baudrate).

Thanks again for your suggestions . . .

Mark J Culross
KD5RXT
 
Hi Mark,

I searched a bit more on the CAN bus wake-up message and ran into the ISO 15765-4 spec.
Paragraph 6.3.2.2 11 bit CAN identifiers and paragraph 6.3.2.3 29 bit CAN identifiers have interesting info.

It seems that CAN ID 0x7DF and CAN ID 0x18DB33F1 are the wakeup messages for respectively 11-bit CAN messages and 29-bit CAN messages.
Especially CAN ID 0x18DB33F1 is exactly what you captured while monitoring your ELM327-based OBDII adapter!
Code:
RX: MBX: 04  LEN: 8  EXT: 1  TS: 59360  ID: [COLOR="#FF0000"]0x18DB33F1[/COLOR]  OVERRUN: 0  MSG: 0x02 0x01 0x00 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 59900  ID: [COLOR="#008000"]0x18DAF110[/COLOR]  OVERRUN: 0  MSG: 0x06 0x41 0x00 0xB6 0x3C 0xA8 0x13 0x55
And the response from CAN ID 0x18DAF110 seems also valid.

Then I googled for "0x7DF 0x18DB33F1". It returned a lot of hits.
Now reading and trying to understand this article. It looks like CAN ID 07E0 from message #48 may not be correct and should be 0x7DF?

To be continued...

Paul
 
Hi Mark,

I searched a bit more on the CAN bus wake-up message and ran into the ISO 15765-4 spec.
Paragraph 6.3.2.2 11 bit CAN identifiers and paragraph 6.3.2.3 29 bit CAN identifiers have interesting info.

It seems that CAN ID 0x7DF and CAN ID 0x18DB33F1 are the wakeup messages for respectively 11-bit CAN messages and 29-bit CAN messages.
Especially CAN ID 0x18DB33F1 is exactly what you captured while monitoring your ELM327-based OBDII adapter!

To be continued...

Paul

Paul:

I have updated my TeensyCANmonitor utility to support generating the 5 baud init sequence from the command menu.

I don't have a permanent workbench, so digging around for my widely scattered (envelopes, boxes, ziploc bags, etc.) collection of small electronic components, I was able to find my assorted selection of transistors, specifically a 2N2222 for this task, but I was unable to find my collection of assorted resistors. I am anticipating delivery today of another DigiKey order with some additional resistors. I bought a ready supply (qty 100) of several values, but for this project, I will be using a 1kohm + a 470ohm as the pull-up (making sure the wattage does not exceed the 1/8W rating) between the collector & 12VDC, as well as a 1kohm for the base current limiter resistor.

While waiting, I have been doing some more experimenting with my ELM327 device. I added the ability to provide 12VDC power without having to connect to an actual vehicle. By monitoring the K-line pin when the ELM327 is activated over the BT connection with my phone, I see the ELM327 device sending a 5-byte sequence (I forgot to write down the exact values, but it does repeat) at 10,400 baud (the standard K-line bit rate), followed by the 5 baud 0x33 init sequence. It appears that the ELM327 device is trying as many avenues as it can to enable the CAN bus !!

Thanks again for your suggestions !! I'll change the 0x7e0 to 0x7df, & I'll add the ID as 0x18DB33F1 for the extended message.

Mark J Culross
KD5RXT
 
@PaulS:

Progress continues to be made: I have now successfully sent both the 11-bit wakeup command (0x7DF) & the extended 29-bit wakeup command (0x18DB33F1).

The expected extended 29-bit response (0x18DAF110) was received as follows:

Code:
=============================================
       Teensy CAN bus monitor utility
     version 1.0 dated 09/14/2022 @1415
designed & written by Mark J Culross (KD5RXT)
=============================================

Attempting to read/verify saved settings (8 values) from EEPROM...verified settings (8 values) in EEPROM are valid...

EEPROM USED: 8
EEPROM MAX ALLOWED: 4284

CAN baudrate set to: 500000 (not scanning)

CONTROL MENU (send from the serial monitor, terminated with LF or CR/LF):
   B: LED intensity NORMAL or BRIGHT (toggle between the two selections)
   F: FIXED CAN baudrate (not scanning)
   H: HUNT for a matching CAN baudrate (scanning)
   K: execute K-line 5-baud init sequence
   N: NEXT (higher) CAN baudrate (not scanning)
   P: PREVIOUS (lower) CAN baudrate (not scanning)
   W: send CAN WAKEUP commands

[ sending wakeup commands was initiated via 'W' at the serial console command line ]

TX: MBX: 00  LEN: 8  EXT: 0  TS:     0  ID: 0x000007DF  OVERRUN: 0  MSG: 0x02 0x10 0x03 0x00 0x00 0x00 0x00 0x00 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x00 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 04  LEN: 8  EXT: 1  TS: 36928  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x00 0xB6 0x3C 0xA8 0x13 0x55 

[ executing the 5 baud 0x33 init sequence on the K-line was initiated via 'K' at the serial command line ]

sending 0x33 at 5-baud init sequence over K-line...complete

It does not appear that anything was received in response to the standard 11-bit wakeup (0x7DF) command. Likewise, executing the 5 baud 0x33 command on the K-line also did not appear to elicit any response.

Thanks again for your help & I'll continue playing !!

Mark J Culross
KD5RXT

P.S. I should note that all of this most current testing was done without the assistance of the ELM327 device & its CAN bus initialization sequence(s). I had only my TeensyCANmonitor device hooked up to the OBDII port on my 2020 Honda Civic, so it's doing everything on its own !! MJC
 
Last edited:
Here's the latest version of my TeensyCANmonitor utility sketch:

Code:
//
//  Teensy 4.x CAN bus monitor utility - version 1.0 dated 20220914-1415
//
//    - 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)
//
//    - 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/14/2022 @1415";
const String AUTHOR    = "designed & written by Mark J Culross (KD5RXT)";

#include <FlexCAN_T4.h>
#include <EEPROM.h>

FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can1;

//#define DEBUG_HEARTBEAT                 // uncomment to use the onboard LED as a heartbeat indicator
//#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
// PIN D22/A8   = CAN1 TX
// PIN D23/A9   = CAN1 RX
// 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

// 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

unsigned long heartbeat_timer = millis();
bool heartbeat_toggle = 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();


#define CAN_TX_TYPE_FRAME true
#define CAN_RX_TYPE_FRAME false


#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_PID_MODE_1_0x01                           0x01
#define CAN_ENGINE_COOLANT_TEMP_0X05                           0x05


#define MAX_T4X_EEPROM_SIZE_ALLOWED 4284

// header: "Teensy CAN Bus Monitor"
const char EEPROM_HEADER[] = "TCBM";

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_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

// function headers
void can_sniff(const CAN_message_t &msg, bool TxRx);
void execute_kline_5_baud_init(void);
void loop();
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_wakeup_commands(void);
void setup(void);
void show_byte_value(unsigned char byte_value);
void show_CONTROL_MENU(void);
void show_index(int index);



void can_sniff(const CAN_message_t &msg, bool TxRx)
{
   if (TxRx)
   {
      Serial.print("TX: ");
   } else {
      Serial.print("RX: ");
   }
   Serial.print("MBX: ");
   if (msg.mb < 10)
   {
      Serial.print("0");
   }
   Serial.print(msg.mb);

   Serial.print("  LEN: ");
   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();

#ifndef DEBUG_HEARTBEAT
   LED_timer = millis();
   analogWrite(LED_PIN, led_intensity_on);
#endif

   CAN_baudrate_match_found = true;
   CAN_baudrate_match_timeout = millis();
}  // can_sniff()


//
// 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)
{
   Serial.println("");
   Serial.print("sending 0x33 at 5-baud init sequence over K-line...");

   digitalWrite(KLINE_PIN, KLINE_HIGH);
   delay(3000);

   digitalWrite(KLINE_PIN, KLINE_LOW);
   delay(200);

   digitalWrite(KLINE_PIN, KLINE_HIGH);
   delay(400);

   digitalWrite(KLINE_PIN, KLINE_LOW);
   delay(400);

   digitalWrite(KLINE_PIN, KLINE_HIGH);
   delay(400);

   digitalWrite(KLINE_PIN, KLINE_LOW);
   delay(400);

   digitalWrite(KLINE_PIN, KLINE_HIGH);
   delay(200);

   Serial.println("complete");
}  // execute_kline 5_baud_init()


// repeated loop forever
void loop()
{
   CAN_message_t msg;
   char inKey = 0x00;

   if ( Can1.read(msg) ) {
      can_sniff(msg, CAN_RX_TYPE_FRAME);
   }

#ifndef DEBUG_HEARTBEAT
   if ((LED_timer) && ((millis() - LED_timer) > LED_DURATION_MSEC))
   {
      analogWrite(LED_PIN, LED_INTENSITY_OFF);
      LED_timer = 0;
   }
#else
   if (millis() - heartbeat_timer > 250)
   {
      heartbeat_toggle = !heartbeat_toggle;
      analogWrite(LED_PIN, heartbeat_toggle);
      heartbeat_timer = millis();
   }
#endif

   // 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();

#ifndef DEBUG_HEARTBEAT
            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);
#endif

            CAN_baud_change_timer = millis();
         }
      }
   }

   while (Serial.available() > 0)
   {
      char cmdKey = inKey;
      inKey = Serial.read();

      bool report_current = false;
      switch (inKey)
      {
         case LF:
         {
            report_current = true;
         }
         break;

         // set the brightness of the onbaord LED
         case 'b':
         case 'B':
         {
            if (led_intensity_on == LED_INTENSITY_ON_NORMAL)
            {
               led_intensity_on = LED_INTENSITY_ON_BRIGHT;
            } else {
               led_intensity_on = LED_INTENSITY_ON_NORMAL;
            }
         }
         break;

         // set fixed CAN baudrate
         case 'f':
         case 'F':
         {
            CAN_baudrate_fixed = true;

            // save settings to EEPROM
            save_settings();
         }
         break;

         // hunt for a matching baudrate & stop there
         case 'h':
         case 'H':
         {
            CAN_baudrate_fixed = false;

            // save settings to EEPROM
            save_settings();
         }
         break;


         // execute K-line 5-baud init sequence
         case 'k':
         case 'K':
         {
            execute_kline_5_baud_init();
         }
         break;
         // next (higher) baudrate
         case 'n':
         case 'N':
         {
            CAN_baudrate_fixed = true;

            next_CAN_baudrate();

            // save settings to EEPROM
            save_settings();
         }
         break;

         // previous (lower) baudrate
         case 'p':
         case 'P':
         {
            CAN_baudrate_fixed = true;

            previous_CAN_baudrate();

            // save settings to EEPROM
            save_settings();
         }
         break;

         // send CAN wakeup commands once
         case 'w':
         case 'W':
         {
            send_CAN_wakeup_commands();
         }
         break;
      }

      if ((cmdKey == 'w') || (cmdKey == 'W'))
      {
         report_current = false;
      }

      if (report_current)
      {
         report_current = false;

         report_current_baudrate();
      }
   }

}  // 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 ");
      showIndex((int)(eeprom_index));
      Serial.print(": ");
      showByteValue(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 ");
         showIndex((int)(eeprom_index));
         Serial.print(": ");
         showByteValue(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_CHECKSUM:
            {
               eeprom_value = (char)(xor_result);

#ifdef DEBUG_EEPROM_READ
               Serial.print(") Calculated CHECKSUM             = ");
               showByteValue(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     = ");
               showByteValue((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 ");
         showIndex((int)(EEPROM_INDEX_CHECKSUM));
         Serial.print  (") xor_value          = ");
         Serial.println(xor_value);
         Serial.print("(CALC) xor_result             = ");
         Serial.println(xor_result);
         Serial.print("(READ ");
         showIndex((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 ");
      showIndex((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
            showByteValue(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
            showByteValue(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
            showByteValue(eeprom_value);

            Serial.print(")    CAN baudrate index                  = ");
            showByteValue(eeprom_value);
            Serial.println("");
#endif
         }
         break;


         case EEPROM_INDEX_CHECKSUM:
         {
            eeprom_value = (char)(xor_result);

#ifdef DEBUG_EEPROM_WRITE
            showByteValue(eeprom_value);
            Serial.print(") Calculated CHECKSUM                    = ");
            showByteValue(xor_result);
            Serial.println("");
#endif
         }
         break;

         case EEPROM_INDEX_INV_CHECKSUM:
         {
            eeprom_value = (char)(~xor_result);

#ifdef DEBUG_EEPROM_WRITE
            showByteValue(eeprom_value);
            Serial.print(") Calculated INVERSE CHECKSUM            = ");
            showByteValue((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()


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

   Can1.write(can_MsgTx);
   can_sniff(can_MsgTx, CAN_TX_TYPE_FRAME);


   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_EXTENDED_DIAGNOSTIC_SESSION_CONTROL_0X01;
   can_MsgTx.buf[2] = CAN_EXTENDED_DIAGNOSTIC_SESSION_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;

   Can1.write(can_MsgTx);
   can_sniff(can_MsgTx, CAN_TX_TYPE_FRAME);
}  // send_CAN_wakeup_commands()


// one-time setup
void setup(void)
{
   Serial.begin(115200);
   while (!Serial && (millis() <= 1000));

   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]);

   report_current_baudrate();

   CAN_baud_change_timer = millis();

   pinMode(LED_PIN, OUTPUT);
   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));
}  // showByteValue()


// show CAN CONTROL MENU choices
void show_CONTROL_MENU(void)
{
   Serial.println("");
   Serial.println("CONTROL MENU (send from the serial monitor, terminated with LF or CR/LF):");
   Serial.println("   B: LED intensity NORMAL or BRIGHT (toggle between the two selections)");
   Serial.println("   F: FIXED CAN baudrate (not scanning)");
   Serial.println("   H: HUNT for a matching CAN baudrate (scanning)");
   Serial.println("   K: execute K-line 5-baud init sequence");
   Serial.println("   N: NEXT (higher) CAN baudrate (not scanning)");
   Serial.println("   P: PREVIOUS (lower) CAN baudrate (not scanning)");
   Serial.println("   W: send CAN WAKEUP commands");
   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));
}  // showIndex()


// EOF PLACEHOLDER
 
Hi Mark, thanks for testing again.
I feel a bit like standing on the side line and shouting things while you're doing all the hard work :) But I just don't have the time now to do testing on my car in parallel - hope you don't mind.
Just to be sure: your BT-based ELM327 tool is able to read diagnostic information from your car, isn't it? If so, then it looks like the it's getting all the info using the K-line since you don't see any useful data on the CAN bus. Is there a way for you to verify this last assumption?
Anyway, I will keep pondering about this and respond when I have new ideas.

Paul
 
Paul:

I am certainly enjoying this !! My major objectives as I entered this adventure were as follows:

1) learn about the CAN bus, using my 2020 Honda Civic as the target (every day & every new attempt furthers the depth of my knowledge, which started from absolutely nothing !!)
2) be able to monitor some basic aspects of my 2020 Honda Civic (e.g. coolant temp, engine air intake temp, engine RPM, etc.)
3) possibly be able to control some simple aspect of my 2020 Honda Civic (e.g. ECO mode, etc.)

Work towards objective #1 is an on-going work in progress, but I am certainly happy with how it has gone to date. It's a slow but steady learning process.

Work towards objective #2 is progressing. I'm still looking for the magic command that can tell the ECM to stream continuous updates (if this is even possible), but in the meantime, I can send selected CAN requests & elicit the expected responses. That's positive progress !!

Work towards objective #3 has not yet begun. I need to fully understand the mechanisms from objective #2 before I attempt this.

Here's what I have determined/confirmed so far:

Code:
- my Actron CP9145 utilizes only the K-line interface when communicating with my 2020 Honda CIVIC (absolutely no data detected/seen on the CAN bus, even while continuously reading current status)

- everything on the CAN BUS can be detected/received equally by my Seeed USB CAN analyzer & by my TeensyCANanalyzer

- any CAN command sent from my Seeed USB CAN analyzer is correctly received on my TeensyCANanalyzer, so CAN receive is 100% operational on my TeensyCANanalyzer

- any CAN command sent from my TeensyCANanalyzer is correctly received on my Seeed USB CAN analyzer, so CAN transmit is 100% operational on my TeensyCANanalyzer

- executing the 5-baud 0x33 init command on the K-line by my TeensyCANanalyzer does not seem to elicit any response (I have verified that the bitrate is correct, & the waveform matches that executed by my ELM327)

- sending the standard 11-bit 0x7DF command from my TeensyCANanalyzer does not elicit any response (I may not be sending the correct data payload)

- sending the extended 29-bit 0x18DB33F1 wakeup command from my TeensyCANanalyzer elicits the expected 0x18DAF110 wakeup response (the latest success !!)

- my ELM327 is able to activate the CAN bus (don't know if that is by K-line command, by CAN command, by 5-baud 0x33 init command, or by some combination of some/all of these)

- after activating the CAN bus using my ELM327, if the ELM327 is unplugged, the CAN bus data flow ceases, so this seems to imply that the ELM327 is using a request/response to get its CAN data

- sending any CAN PID01 commands from my TeensyCANanalyzer or equally from my Seeed USB CAN analyzer without first sending the extended 29-bit 0x18DB33F1 wakeup command does not seem to elicit any response

- sending the standard 11-bit version of PID01 commands (e.g. 0x05 engine coolant temp, 0x0C engine RPM, 0x0F engine air intake temp) from my TeensyCANanalyzer does not seem to elicit any response (I may not be sending the correct data payloads)

- sending the extended 29-bit version of PID01 0x0C engine RPM from my TeensyCANanalyzer or equally from my Seeed USB CAN analyzer elicits a response that matches the dash instrument (the most latest success !!)

- sending the extended 29-bit version of PID01 0x05 engine coolant temp from my TeensyCANanalyzer or equally from my Seeed USB CAN analyzer does not seem to elicit any response

- sending the extended 29-bit version of PID01 0x0F engine air intake temp from my TeensyCANanalyzer or equally from my Seeed USB CAN analyzer does not seem to elicit any response

Thanks again for your help !! I will continue to experiment & will post any updates . . .

Mark J Culross
KD5RXT
 
Successfully querying PID 0x01 & getting responses !!

@PaulS:

Here's the latest testing results:

Code:
=============================================
       Teensy CAN bus monitor utility
     version 1.0 dated 09/16/2022 @1530
designed & written by Mark J Culross (KD5RXT)
=============================================



Attempting to read/verify saved settings (8 values) from EEPROM...verified settings (8 values) in EEPROM are valid...


EEPROM USED: 8
EEPROM MAX ALLOWED: 4284


CAN baudrate set to: 500000 (not scanning)

CONTROL MENU (send from the serial monitor, terminated with LF or CR/LF):
   B: LED intensity NORMAL or BRIGHT (toggle between the two selections)
   C: send CAN CUSTOM commands
   F: FIXED CAN baudrate (not scanning)
   H: HUNT for a matching CAN baudrate (scanning)
   K: execute K-line 5-baud init sequence
   N: NEXT (higher) CAN baudrate (not scanning)
   P: PREVIOUS (lower) CAN baudrate (not scanning)
   W: send CAN WAKEUP commands

[ initiate sending CAN custom command sequence using 'C' from the serial console command line ]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x00 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS: 55286  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x00 0xB6 0x3C 0xA8 0x13 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x20 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS: 39671  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x20 0x90 0x07 0xE0 0x11 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x40 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS: 24056  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x40 0xF2 0xC0 0x8C 0x01 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x60 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS:  8585  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x60 0x67 0x10 0x00 0x01 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x80 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS: 58526  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x80 0x00 0x04 0x00 0x0F 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0xA0 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS: 42997  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0xA0 0x04 0x00 0x00 0x00 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0xC0 0x00 0x00 0x00 0x00 0x00 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0xE0 0x00 0x00 0x00 0x00 0x00 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x01 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS: 61914  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x01 0x00 0x07 0xE5 0x00 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x03 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS: 46382  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x03 0x02 0x00 0x55 0x55 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x04 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS: 30847  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x04 0x44 0x55 0x55 0x55 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x06 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS: 15304  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x06 0x83 0x55 0x55 0x55 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x07 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS: 65304  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x07 0x81 0x55 0x55 0x55 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0B 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS: 49761  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0B 0x29 0x55 0x55 0x55 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0C 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS: 34231  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x0C 0x0B 0xA4 0x55 0x55 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS: 18690  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0E 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS:  3156  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0E 0x81 0x55 0x55 0x55 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x11 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS: 53250  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x11 0x25 0x55 0x55 0x55 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x13 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS: 37619  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x13 0x03 0x55 0x55 0x55 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x15 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS: 22439  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x15 0x75 0x80 0x55 0x55 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x1C 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS:  6549  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x1C 0x01 0x55 0x55 0x55 0x55 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x1E 0x00 0x00 0x00 0x00 0x00 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x1F 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS: 41015  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x1F 0x00 0x32 0x55 0x55 0x55 

CAN baudrate set to: 500000 (not scanning)

CONTROL MENU (send from the serial monitor, terminated with LF or CR/LF):
   B: LED intensity NORMAL or BRIGHT (toggle between the two selections)
   C: send CAN CUSTOM commands
   F: FIXED CAN baudrate (not scanning)
   H: HUNT for a matching CAN baudrate (scanning)
   K: execute K-line 5-baud init sequence
   N: NEXT (higher) CAN baudrate (not scanning)
   P: PREVIOUS (lower) CAN baudrate (not scanning)
   W: send CAN WAKEUP commands


[ initiate sending CAN wake-up using 'W' from the serial console command line ]

TX: MBX: 00  LEN: 8  EXT: 0  TS:     0  ID: 0x000007DF  OVERRUN: 0  MSG: 0x02 0x10 0x03 0x00 0x00 0x00 0x00 0x00 
TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x10 0x03 0x00 0x00 0x00 0x00 0x00 
RX: MBX: 99  LEN: 8  EXT: 1  TS:  8248  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x02 0x50 0x03 0x55 0x55 0x55 0x55 0x55 

CAN baudrate set to: 500000 (not scanning)

CONTROL MENU (send from the serial monitor, terminated with LF or CR/LF):
   B: LED intensity NORMAL or BRIGHT (toggle between the two selections)
   C: send CAN CUSTOM commands
   F: FIXED CAN baudrate (not scanning)
   H: HUNT for a matching CAN baudrate (scanning)
   K: execute K-line 5-baud init sequence
   N: NEXT (higher) CAN baudrate (not scanning)
   P: PREVIOUS (lower) CAN baudrate (not scanning)
   W: send CAN WAKEUP commands


[ initiate sending 5-baud K-line init sequence using 'K' from the serial console command line ]

sending 0x33 at 5-baud init sequence over K-line...starting...
...pausing K-line interface for 3 seconds...
...pulling K-line interface LOW for 200 milliseconds...
...pulling K-line interface HIGH for 400 milliseconds...
...pulling K-line interface LOW for 400 milliseconds...
...pulling K-line interface HIGH for 400 milliseconds...
...pulling K-line interface LOW for 400 milliseconds...
...pulling K-line interface HIGH for 200 milliseconds...
sending 0x33 at 5-baud init sequence over K-line...complete...

CAN baudrate set to: 500000 (not scanning)

CONTROL MENU (send from the serial monitor, terminated with LF or CR/LF):
   B: LED intensity NORMAL or BRIGHT (toggle between the two selections)
   C: send CAN CUSTOM commands
   F: FIXED CAN baudrate (not scanning)
   H: HUNT for a matching CAN baudrate (scanning)
   K: execute K-line 5-baud init sequence
   N: NEXT (higher) CAN baudrate (not scanning)
   P: PREVIOUS (lower) CAN baudrate (not scanning)
   W: send CAN WAKEUP commands

As you can see, I am now able to query which PIDs are supported (using PID 0x01 Service 0x00, as well as PID 0x01 Service 0x20, PID 0x01 Service 0x40, PID 0x01 Service 0x80, PID 0x01 Service 0xA0, PID 0x01 Service 0xC0, & PID 0x01 Service 0xE0). I can also, using the report back for PIDs supported from 0x01 thru 0x20, query the supported services (PID 0x01 Service 0x01, PID 0x01 Service 0x03, PID 0x01 Service 0x04, PID 0x01 Service 0x06, PID 0x01 Service 0x07, PID 0x01 Service 0x0B, PID 0x01 Service 0x0C, PID 0x01 Service 0x0D, PID 0x01 Service 0x0E, PID 0x01 Service 0x11, PID 0x01 Service 0x13, PID 0x01 Service 0x15, PID 0x01 Service 0x1C, PID 0x01 Service 0x1E, PID 0x01 Service 0x1F), & most of these queries elicit an expected response.

Observation: I still don't seem to be able to elicit any responses initiating the standard 11-bit queries addressed to 0x7df. However, initiating extended 29-bit queries addressed to 0x18DB33F1 seem to work as expected.

As just one successful example, the engine RPM query (PID 0x01 Service 0x0C) returns 0x04 0x41 0x0C 0x0B 0xA4 0x55 0x55 0x55, the data portion of which translates to 745, which corresponds very closely to the RPM indicator on the dash, which was hovering around 750 at idle.

As an unexplained curiosity, the engine coolant query (PID 0x01 Service 0x05) does not return any data. I"m guessing that I may need to query a specific ECM (addressed to 0x18DBxxF1, where xx might be 01-08). The responses that I am getting seems to be reported as coming from ECM 10 (0x18DAF110) . . . no explanation there, else I am misunderstanding the CAN spec.

I believe that I have made enough progress that I can now start playing with the possibility of periodically querying for the desired PIDs & outputting the responses in some kind of graphical form on a TFT, just for the fun of it.

Once again & as always, thanks for your help !! I will continue to experiment & will post any updates . . .

Mark J Culross
KD5RXT

P.S. Next I will actually add a "decoding" of the responses in the serial monitor so I don't have to continue doing HEX math in my head to calculate/compare the engine RPM !! MJC
 
As an unexplained curiosity, the engine coolant query (PID 0x01 Service 0x05) does not return any data.

DOH !! Now that I'm decoding the bit specifics in some of the messages, I notice the PID 0x01 Service 0x05 is not supported in my 2020 Honda Civic. That would definitely explain why I'm not getting any response !!

The learning process continues !!

Mark J Culross
KD5RXT
 
Hi Mark, great to see your progress!

The responses that I am getting seems to be reported as coming from ECM 10 (0x18DAF110)
Yeah, that's what I noticed earlier also, but I guess that's how Honda assigned the ECU for getting the diagnostic data out.

Next I will actually add a "decoding" of the responses in the serial monitor so I don't have to continue doing HEX math in my head to calculate/compare the engine RPM !!
Yep, that is what I would have done also!

Success!
Paul
 
TeensyCANmonitor is working GREAT !!

@Paul:

I have completed the decoding of all CAN responses (using the wiki information that you pointed me to earler) in the latest version of my TeensyCANmonitor utility using a T4.0 + SN65HVD230 breakout board & I am very happy with the results that I have been able to confirm. I've tested this on both my 2020 Honda Civic & my wife's 2020 Honda CR-V. The Civic replies as expected to every CAN request. The CR-V seems to be sending a 2nd reply to some requests (which, interestingly, has different contents from the first reply), & thus appears to miss some of the subsequent requests (I'll have to look further into that, but my wife is not very keen on my using her car for extended experimentation . . . even though I've repeatedly told her that my hardware & firmware are only passively monitoring the elicited reports & not doing any active changing, she's still not convinced that I won't somehow harm her car !!).

Here's the latest version (20220919-2150) of my TeensyCANmonitor utility sketch for the Teensy 4.x:

Code:
//
//  Teensy 4.x CAN bus monitor utility - version 1.0 dated 20220919-2150
//
//    - 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)
//
//    - 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/19/2022 @2150";
const String AUTHOR    = "designed & written by Mark J Culross (KD5RXT)";

#include <FlexCAN_T4.h>
#include <EEPROM.h>

FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can1;

//#define DEBUG_HEARTBEAT                 // uncomment to use the onboard LED as a heartbeat indicator
//#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
#define DEBUG_CAN_RX                    // uncomment to show the specifics CAN RX messages


//
// 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

unsigned long heartbeat_timer = millis();
bool heartbeat_toggle = 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_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];

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_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

// function headers
void can_sniff_RX(const CAN_message_t &msg);
void can_sniff_TX(const CAN_message_t &msg);
void can_TX(const CAN_message_t &msg);
void decode_extended_service_01_PID_content(const CAN_message_t &msg);
void decode_service_01_PID_content(const CAN_message_t &msg);
void decode_service_01_PID_title(int i);
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(void);
void send_CAN_custom_commands(void);
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);



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: ");
  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("");

#ifndef DEBUG_HEARTBEAT
  LED_timer = millis();
  analogWrite(LED_PIN, led_intensity_on);
#endif

  CAN_baudrate_match_found = true;
  CAN_baudrate_match_timeout = millis();

  if (msg.buf[0] <= 0x07)
  {
    switch (msg.buf[1])
    {
      case 0x41:
        {
          Serial.println("      0x41: service 0x01 request response");

          Serial.print("      0x");
          Serial.print((msg.buf[1] & 0xF0) >> 4, HEX);
          Serial.print(msg.buf[1] & 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_PID_title(msg.buf[2]);
          Serial.println("");

#ifdef DEBUG_CAN_RX
          decode_service_01_PID_content(msg);
#endif
        }
        break;

      default:
        {
          Serial.print("      0x");
          Serial.print((msg.buf[1] & 0xF0) >> 4, HEX);
          Serial.print(msg.buf[1] & 0x0F, HEX);
          Serial.println(": unknown service request response");
        }
        break;
    }
  } else {
    switch (msg.buf[0])
    {
      case 0x10:
        {
          switch (msg.buf[2])
          {
            case 0x41:
              {
                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_PID_title(msg.buf[3]);
                Serial.println("");

#ifdef DEBUG_CAN_RX
                decode_extended_service_01_PID_content(msg);
#endif
              }
              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;
    }
  }
}  // can_sniff_RX()


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: ");
  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("");

  if (msg.buf[1] == 0x01)
  {
    Serial.println("      0x01: service 0x01 request");

    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_PID_title(msg.buf[2]);
    Serial.println("");
    Serial.println("");
  }

#ifndef DEBUG_HEARTBEAT
  LED_timer = millis();
  analogWrite(LED_PIN, led_intensity_on);
#endif

  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()


// decode the extended service 0x01 PID content into data reported
void decode_extended_service_01_PID_content(const CAN_message_t &msg)
{
  int service = msg.buf[3];
  int A = msg.buf[4];
  int B = msg.buf[5];
//  int C = msg.buf[6];
//  int D = msg.buf[7];
  bool valid_command = true;

  switch (service)
  {
    case 0x68:
      {
        Serial.print("         PID # 0x68 - ");
        decode_service_01_PID_title(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 0x70:
    case 0x9F:
    case 0xA3:
      {
        Serial.print("         PID # 0x");
        Serial.print((service & 0xF0) >> 4, HEX);
        Serial.print(service & 0x0F, HEX);
        Serial.print(" - ");
        decode_service_01_PID_title(service);
        Serial.println(" (detailed definition of content is unavailable)");
      }
      break;

    default:
      {
        valid_command = false;
      }
      break;
  }

  if (valid_command)
  {
    Serial.println("");
  }
}  // decode_extended_service_01_PID_content()


// decode the service 0x01 PID content into data reported
void decode_service_01_PID_content(const CAN_message_t &msg)
{
  int j = 1;
  int service = 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];
  bool valid_command = true;

  switch (service)
  {
    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 (service)
  {
    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_PID_title(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_PID_title(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_PID_title(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_PID_title(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_PID_title(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);

        // B3
        if (B & 0x08)
        {
          Serial.println("            Compression ignition monitors supported (e.g. Diesel engines)");
        } else {
          Serial.println("            Spark ignition monitors supported (e.g. Otto ot 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 0x03:
      {
        Serial.print("         PID # 0x03 - ");
        decode_service_01_PID_title(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_PID_title(0x04);
        Serial.println("");

        Serial.print("            Engine load (%) = ");
        Serial.println((float)(A) / 2.56);
      }
      break;

    case 0x05:
      {
        Serial.print("         PID # 0x05 - ");
        decode_service_01_PID_title(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_PID_title(0x06);
        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_PID_title(0x07);
        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_PID_title(0x08);
        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_PID_title(0x09);
        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_PID_title(0x0A);
        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_PID_title(0x0B);
        Serial.println("");

        Serial.print("            Intake manifold absolute pressure (kPa) = ");
        Serial.println((float)(A));
      }
      break;

    case 0x0C:
      {
        Serial.print("         PID # 0x0C - ");
        decode_service_01_PID_title(0x0C);
        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_PID_title(0x0D);
        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_PID_title(0x0E);
        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_PID_title(0x0F);
        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_PID_title(0x10);
        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_PID_title(0x11);
        Serial.println("");

        Serial.print("            Throttle position (%) = ");
        Serial.println((float)(A) * 100.0 / 255.0);
      }
      break;

    case 0x12:
      {
        Serial.print("         PID # 0x12 - ");
        decode_service_01_PID_title(0x12);
        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_PID_title(0x13);
        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_PID_title(0x14);
        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_PID_title(0x15);
        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_PID_title(0x16);
        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_PID_title(0x17);
        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_PID_title(0x18);
        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_PID_title(0x19);
        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_PID_title(0x1A);
        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_PID_title(0x1B);
        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_PID_title(0x1C);
        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_PID_title(0x1D);
        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_PID_title(0x1E);
        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_PID_title(0x1F);
        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_PID_title(0x21);
        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_PID_title(0x22);
        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_PID_title(0x23);
        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_PID_title(0x24);
        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_PID_title(0x25);
        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_PID_title(0x26);
        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_PID_title(0x27);
        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_PID_title(0x28);
        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_PID_title(0x29);
        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_PID_title(0x2A);
        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_PID_title(0x2B);
        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_PID_title(0x2C);
        Serial.println("");

        Serial.print("            Commanded EGR (%) = ");
        Serial.println((float)(A) * 100.0 / 255.0);
      }
      break;

    case 0x2D:
      {
        Serial.print("         PID # 0x2D - ");
        decode_service_01_PID_title(0x2D);
        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_PID_title(0x2E);
        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_PID_title(0x2F);
        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_PID_title(0x30);
        Serial.println("");

        Serial.print("            Warm-ups since codes cleared = ");
        Serial.println((float)(A));
      }
      break;

    case 0x31:
      {
        Serial.print("         PID # 0x31 - ");
        decode_service_01_PID_title(0x31);
        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_PID_title(0x32);
        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_PID_title(0x33);
        Serial.println("");

        Serial.print("            Absolute Barometric Pressure (kPa) = ");
        Serial.println((float)(A));
      }
      break;

    case 0x34:
      {
        Serial.print("         PID # 0x34 - ");
        decode_service_01_PID_title(0x34);
        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_PID_title(0x35);
        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_PID_title(0x36);
        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_PID_title(0x37);
        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_PID_title(0x38);
        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_PID_title(0x39);
        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_PID_title(0x3A);
        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_PID_title(0x3B);
        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_PID_title(0x3C);
        Serial.println("");

        Serial.print("            Catalyst Temperature: Bank 1, Sensor 1 = ");
        Serial.println(((((float)(A) * 256.0) + (float)(B)) / 10.0) - 40.0);
      }
      break;

    case 0x3D:
      {
        Serial.print("         PID # 0x3D - ");
        decode_service_01_PID_title(0x3D);
        Serial.println("");

        Serial.print("            Catalyst Temperature: Bank 1, Sensor 2 = ");
        Serial.println(((((float)(A) * 256.0) + (float)(B)) / 10.0) - 40.0);
      }
      break;

    case 0x3E:
      {
        Serial.print("         PID # 0x3E - ");
        decode_service_01_PID_title(0x3E);
        Serial.println("");

        Serial.print("            Catalyst Temperature: Bank 2, Sensor 1 = ");
        Serial.println(((((float)(A) * 256.0) + (float)(B)) / 10.0) - 40.0);
      }
      break;

    case 0x3F:
      {
        Serial.print("         PID # 0x3F - ");
        decode_service_01_PID_title(0x3F);
        Serial.println("");

        Serial.print("            Catalyst Temperature: Bank 2, Sensor 2 = ");
        Serial.println(((((float)(A) * 256.0) + (float)(B)) / 10.0) - 40.0);
      }
      break;

    case 0x41:
      {
        Serial.print("         PID # 0x41 - ");
        decode_service_01_PID_title(0x41);
        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 & 0x01)
        {
          // 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_PID_title(0x42);
        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_PID_title(0x43);
        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_PID_title(0x44);
        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_PID_title(0x45);
        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_PID_title(0x46);
        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_PID_title(0x47);
        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_PID_title(0x48);
        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_PID_title(0x49);
        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_PID_title(0x4A);
        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_PID_title(0x4B);
        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_PID_title(0x4C);
        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_PID_title(0x4D);
        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_PID_title(0x4E);
        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_PID_title(0x4F);
        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_PID_title(0x51);
        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_PID_title(0x52);
        Serial.println("");

        Serial.print("            Ethanol Fuel % = ");
        Serial.println((float)(A) * 100.0 / 255.0);
      }
      break;

    case 0x53:
      {
        Serial.print("         PID # 0x53 - ");
        decode_service_01_PID_title(0x53);
        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_PID_title(0x54);
        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_PID_title(0x55);
        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_PID_title(0x56);
        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_PID_title(0x57);
        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_PID_title(0x58);
        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_PID_title(0x59);
        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_PID_title(0x5A);
        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_PID_title(0x5B);
        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_PID_title(0x5C);
        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_PID_title(0x5D);
        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_PID_title(0x5E);
        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_PID_title(0x5F);
        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_PID_title(0x61);
        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_PID_title(0x62);
        Serial.println("");

        Serial.print("            Actual engine - percent torque (%) = ");
        Serial.println((float)(A) - 125.0);
      }
      break;

    case 0x63:
      {
        Serial.print("         PID # 0x63 - ");
        decode_service_01_PID_title(0x63);
        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_PID_title(0x64);
        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_PID_title(0x65);
        Serial.println("");

        Serial.println("            Auxiliary input / output supported (bit encoded, but definitions are unavailable)");
      }
      break;

    case 0x66:
      {
        Serial.print("         PID # 0x66 - ");
        decode_service_01_PID_title(0x66);
        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_PID_title(0x67);
        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_PID_title(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((service & 0xF0) >> 4, HEX);
        Serial.print(service & 0x0F, HEX);
        Serial.print(" - ");
        decode_service_01_PID_title(service);
        Serial.println(" (detailed definition of content is unavailable)");
      }
      break;

    case 0x78:
      {
        Serial.print("         PID # 0x78 - ");
        decode_service_01_PID_title(0x78);
        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_PID_title(0x79);
        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((service & 0xF0) >> 4, HEX);
        Serial.print(service & 0x0F, HEX);
        Serial.print(" - ");
        decode_service_01_PID_title(service);
        Serial.println(" (detailed definition of content is unavailable)");
      }
      break;

    case 0x7C:
      {
        Serial.print("         PID # 0x7C - ");
        decode_service_01_PID_title(0x7C);
        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((service & 0xF0) >> 4, HEX);
        Serial.print(service & 0x0F, HEX);
        Serial.print(" - ");
        decode_service_01_PID_title(service);
        Serial.println(" (detailed definition of content is unavailable)");
      }
      break;

    case 0x8E:
      {
        Serial.print("         PID # 0x8E - ");
        decode_service_01_PID_title(0x8E);
        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((service & 0xF0) >> 4, HEX);
        Serial.print(service & 0x0F, HEX);
        Serial.print(" - ");
        decode_service_01_PID_title(service);
        Serial.println(" (detailed definition of content is unavailable)");
      }
      break;

    case 0xA2:
      {
        Serial.print("         PID # 0xA2 - ");
        decode_service_01_PID_title(0xA2);
        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((service & 0xF0) >> 4, HEX);
        Serial.print(service & 0x0F, HEX);
        Serial.print(" - ");
        decode_service_01_PID_title(service);
        Serial.println(" (detailed definition of content is unavailable)");
      }
      break;

    case 0xA4:
      {
        Serial.print("         PID # 0xA4 - ");
        decode_service_01_PID_title(0xA4);
        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_PID_title(0xA5);
        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)(A) * 256.0 * 256.0) + ((float)(A) * 256.0) + (float)(D));
        Serial.print("         PID # 0xA6 - ");
        decode_service_01_PID_title(0xA6);
        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((service & 0xF0) >> 4, HEX);
        Serial.print(service & 0x0F, HEX);
        Serial.print(" - ");
        decode_service_01_PID_title(service);
        Serial.println(" (detailed definition of content is unavailable)");
      }
      break;

    case 0xA9:
      {
        Serial.print("         PID # 0xA9 - ");
        decode_service_01_PID_title(0xA9);
        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((service & 0xF0) >> 4, HEX);
        Serial.print(service & 0x0F, HEX);
        Serial.print(" - ");
        decode_service_01_PID_title(service);
        Serial.println(" (detailed definition of content is unavailable)");
      }
      break;

    case 0xC4:
      {
        Serial.print("         PID # 0xC4 - ");
        decode_service_01_PID_title(0xC4);
        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("");
  }
}  // decode_service_01_PID_content()


// decode the service 0x01 PID into a title
void decode_service_01_PID_title(int i)
{
  switch (i)
  {
    case 0x00:
      {
        Serial.print("PIDs supported [0x01-0x20]");
      }
      break;

    case 0x01:
      {
        Serial.print("Monitor status since DTCs cleared");
      }
      break;

    case 0x02:
      {
        Serial.print("Freeze DTC");
      }
      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((i & 0xF0) >> 4, HEX);
        Serial.print(i & 0x0F, HEX);
      }
      break;
  }
}  // decode_service_01_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 inKey = 0x00;

  Can1.events();

#ifndef DEBUG_HEARTBEAT
  if ((LED_timer) && ((millis() - LED_timer) > LED_DURATION_MSEC))
  {
    analogWrite(LED_PIN, LED_INTENSITY_OFF);
    LED_timer = 0;
  }
#else
  if (millis() - heartbeat_timer > 250)
  {
    heartbeat_toggle = !heartbeat_toggle;
    analogWrite(LED_PIN, heartbeat_toggle);
    heartbeat_timer = millis();
  }
#endif

  // 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();

#ifndef DEBUG_HEARTBEAT
        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);
#endif

        CAN_baud_change_timer = millis();
      }
    }
  }

  while (Serial.available() > 0)
  {
    char cmdKey = inKey;
    inKey = Serial.read();

    bool report_current = false;
    switch (inKey)
    {
      case LF:
        {
          report_current = true;
        }
        break;

      // send all supported service 0x01 PID CAN commands
      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();
        }
        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;
          }
#ifndef DEBUG_HEARTBEAT
          LED_timer = millis();
          analogWrite(LED_PIN, led_intensity_on);
#endif

        }
        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("");

          send_CAN_custom_commands();
        }
        break;

      // set fixed CAN baudrate
      case 'f':
      case 'F':
        {
          Serial.println("");
          Serial.println("[ F: initiate set fixed CAN baudrate from the serial monitor command line ]");
          Serial.println("");

          CAN_baudrate_fixed = true;

          // save settings to EEPROM
          save_settings();
        }
        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 = false;

          // 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;
      // 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 the supported service 0x01 PIDs
      case 'q':
      case 'Q':
        {
          Serial.println("");
          Serial.println("[ Q: initiate send CAN query for the supported service 0x01 PIDs from the serial monitor command line ]");
          Serial.println("");

          send_CAN_query_supported_PIDs_for_service_01();
        }
        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 ((cmdKey == 'w') || (cmdKey == 'W'))
    {
      report_current = false;
    }

    if (report_current)
    {
      report_current = false;

      report_current_baudrate();
    }
  }

}  // 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 ");
    showIndex((int)(eeprom_index));
    Serial.print(": ");
    showByteValue(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 ");
      showIndex((int)(eeprom_index));
      Serial.print(": ");
      showByteValue(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_CHECKSUM:
          {
            eeprom_value = (char)(xor_result);

#ifdef DEBUG_EEPROM_READ
            Serial.print(") Calculated CHECKSUM             = ");
            showByteValue(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     = ");
            showByteValue((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 ");
      showIndex((int)(EEPROM_INDEX_CHECKSUM));
      Serial.print  (") xor_value          = ");
      Serial.println(xor_value);
      Serial.print("(CALC) xor_result             = ");
      Serial.println(xor_result);
      Serial.print("(READ ");
      showIndex((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 ");
    showIndex((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
          showByteValue(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
          showByteValue(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
          showByteValue(eeprom_value);

          Serial.print(")    CAN baudrate index                  = ");
          showByteValue(eeprom_value);
          Serial.println("");
#endif
        }
        break;


      case EEPROM_INDEX_CHECKSUM:
        {
          eeprom_value = (char)(xor_result);

#ifdef DEBUG_EEPROM_WRITE
          showByteValue(eeprom_value);
          Serial.print(") Calculated CHECKSUM                    = ");
          showByteValue(xor_result);
          Serial.println("");
#endif
        }
        break;

      case EEPROM_INDEX_INV_CHECKSUM:
        {
          eeprom_value = (char)(~xor_result);

#ifdef DEBUG_EEPROM_WRITE
          showByteValue(eeprom_value);
          Serial.print(") Calculated INVERSE CHECKSUM            = ");
          showByteValue((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 PID CAN requests
void send_CAN_all_supported_PIDs_for_service_01(void)
{
  CAN_message_t can_MsgTx;

  for (int i = 0; i < 256; i++)
  {
    if ((supported_PIDs_for_service_01[i]) && (i % 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] = CAN_STANDARD_SERVICE_MODE_1_0x01;
      can_MsgTx.buf[2] = i;
      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_all_supported_PIDs_for_service_01()


// send CAN custom commands
void send_CAN_custom_commands(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] = 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);


  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] = 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_STANDARD_SERVICE_MODE_1_0x01;
  can_MsgTx.buf[2] = 0x04;
  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] = 0x06;
  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] = 0x07;
  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] = 0x0B;
  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] = 0x0C;
  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] = 0x0D;
  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] = 0x0E;
  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] = 0x11;
  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] = 0x13;
  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] = 0x15;
  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] = 0x1C;
  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] = 0x1E;
  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] = 0x1F;
  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] = 0x2F;
  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] = 0x68;
  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 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);

  report_current_baudrate();

  for (int i = 0; i < 256; i++)
  {
    supported_PIDs_for_service_01[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));
}  // showByteValue()


// 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("   A: send CAN request for all supported service 0x01 PIDs");
  Serial.println("   B: LED intensity NORMAL or BRIGHT (toggle between the two selections)");
  Serial.println("   C: send CAN CUSTOM commands");
  Serial.println("   F: FIXED CAN baudrate (not scanning)");
  Serial.println("   H: HUNT for a matching CAN baudrate (scanning)");
  Serial.println("   K: execute K-line 5-baud init sequence");
  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 the supported service 0x01 PIDs");
  Serial.println("   W: send CAN WAKEUP commands");
  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));
}  // showIndex()


// EOF PLACEHOLDER


And, here's an example capture from my 2020 Honda Civic (in this example, I requested the list of all PIDs supported for service 0x01, then used that list to send requests for all PIDs supported for service 0x01):

Code:
=============================================
       Teensy CAN bus monitor utility
     version 1.0 dated 09/19/2022 @2150
designed & written by Mark J Culross (KD5RXT)
=============================================



Attempting to read/verify saved settings (8 values) from EEPROM...verified settings (8 values) in EEPROM are valid...


EEPROM USED: 8
EEPROM MAX ALLOWED: 4284


CAN baudrate set to: 500000 (not scanning)

CONTROL MENU (send from the serial monitor command line, terminated with LF or CR/LF):
   A: send CAN request for all supported service 0x01 PIDs
   B: LED intensity NORMAL or BRIGHT (toggle between the two selections)
   C: send CAN CUSTOM commands
   F: FIXED CAN baudrate (not scanning)
   H: HUNT for a matching CAN baudrate (scanning)
   K: execute K-line 5-baud init sequence
   N: NEXT (higher) CAN baudrate (not scanning)
   P: PREVIOUS (lower) CAN baudrate (not scanning)
   Q: send CAN QUERY for the supported service 0x01 PIDs
   W: send CAN WAKEUP commands


[ Q: initiate send CAN query for the supported service 0x01 PIDs from the serial monitor command line ]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x00 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x00: Service 0x00 - PIDs supported [0x01-0x20]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 38388  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x00 0xB6 0x3C 0xA8 0x13 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x00 - PIDs supported [0x01-0x20]
         PID # 0x01 is supported - Monitor status since DTCs cleared
         PID # 0x03 is supported - Fuel System Status
         PID # 0x04 is supported - Calculated engine load [0 to 100%]
         PID # 0x06 is supported - Short term fuel trim - Bank 1 [-100 to 99.2%]
         PID # 0x07 is supported - Long term fuel trim - Bank 1 [-100 to 99.2%]
         PID # 0x0B is supported - Intake manifold absolute pressure [0 to 255 kPa]
         PID # 0x0C is supported - Engine speed [0 to 16383.75 rpm]
         PID # 0x0D is supported - Vehicle speed [0 to 255 km/h]
         PID # 0x0E is supported - Timing advance [-64 to 63.5 degrees before TDC
         PID # 0x11 is supported - Throttle position [0 to 100%]
         PID # 0x13 is supported - Oxygen sensors present (in 2 banks)
         PID # 0x15 is supported - Oxygen Sensor 2 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]
         PID # 0x1C is supported - OBD standards this vehicle conforms to [1 to 250]
         PID # 0x1F is supported - Run time since engine start [0 to 65535 s]
         PID # 0x20 is supported - PIDs supported [0x21-0x40]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x20 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x20: Service 0x20 - PIDs supported [0x21-0x40]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 22627  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x20 0x90 0x07 0xE0 0x11 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x20 - PIDs supported [0x21-0x40]
         PID # 0x21 is supported - Distance traveled with malfunction indicator lamp (MIL) on [0 to 65535 km]
         PID # 0x24 is supported - Oxygen Sensor 1 - Air-Fuel Equivalence Ratio  [0 to <2] & Voltage [0 to <8 V]
         PID # 0x2E is supported - Commanded evaporative purge [0 to 100%]
         PID # 0x2F is supported - Fuel Tank Level input [0 to 100%]
         PID # 0x30 is supported - Warm-ups since codes cleared [0 to 255]
         PID # 0x31 is supported - Distance traveled since codes cleared [0 to 65535 km]
         PID # 0x32 is supported - Evap. System Vapor Pressure [-8192 to 8191.75 Pa]
         PID # 0x33 is supported - Absolute Barometric Pressure [0 to 255 kPa]
         PID # 0x3C is supported - Catalyst Temperature: Bank 1, Sensor 1 [-40 to 6513.5 degrees C]
         PID # 0x40 is supported - PIDs supported [0x41-0x60]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x40 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x40: Service 0x40 - PIDs supported [0x41-0x60]

RX: MBX: 99  LEN: 8  EXT: 1  TS:  7090  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x40 0xF2 0xC0 0x8C 0x01 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x40 - PIDs supported [0x41-0x60]
         PID # 0x41 is supported - Monitor status this drive cycle
         PID # 0x42 is supported - Control module voltage [0 to 65535 V]
         PID # 0x43 is supported - Absolute load value [0 to 27500%]
         PID # 0x44 is supported - Commanded Air-Fuel Equivalence Ratio [0 to <2]
         PID # 0x47 is supported - Absolute throttle position B [0 to 100%]
         PID # 0x49 is supported - Absolute throttle position D [0 to 100%]
         PID # 0x4A is supported - Absolute throttle position E [0 to 100%]
         PID # 0x51 is supported - Fuel Type
         PID # 0x55 is supported - Short term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]
         PID # 0x56 is supported - Long term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]
         PID # 0x60 is supported - PIDs supported [0x61-0x80]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x60 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x60: Service 0x60 - PIDs supported [0x61-0x80]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 57092  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x60 0x67 0x10 0x00 0x01 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x60 - PIDs supported [0x61-0x80]
         PID # 0x62 is supported - Actual engine - percent torque [-125 to 130%]
         PID # 0x63 is supported - Engine reference torque [0 to 65535 N-m]
         PID # 0x66 is supported - Mass air flow sensor [0 to 2047.96875 g/s]
         PID # 0x67 is supported - Engine coolant temperature [-40 to 215 degrees C]
         PID # 0x68 is supported - Intake air temperature sensor [-40 to 215 degrees C]
         PID # 0x6C is supported - Commanded throttle actuator control and relative throttle position
         PID # 0x80 is supported - PIDs supported [0x81-0xA0]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x80 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x80: Service 0x80 - PIDs supported [0x81-0xA0]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 41560  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x80 0x00 0x04 0x00 0x0F 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x80 - PIDs supported [0x81-0xA0]
         PID # 0x8E is supported - Engine Friction - Percent Torque [-125 to 130%]
         PID # 0x9D is supported - Engine Fuel Rate [g/s]
         PID # 0x9E is supported - Engine Exhaust Flow Rate [kg/h]
         PID # 0x9F is supported - Fuel System Percentage Use
         PID # 0xA0 is supported - PIDs supported [0xA1-0xC0]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0xA0 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0xA0: Service 0xA0 - PIDs supported [0xA1-0xC0]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 26026  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0xA0 0x04 0x00 0x00 0x00 0x55 
      0x41: service 0x01 request response
      0x41: Service 0xA0 - PIDs supported [0xA1-0xC0]
         PID # 0xA6 is supported - Odometer [0 to 429496729.5 km]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0xC0 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0xC0: Service 0xC0 - PIDs supported [0xC1-0xE0]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0xE0 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0xE0: Service 0xE0 - UNDOCUMENTED: 0xE0


CAN baudrate set to: 500000 (not scanning)

CONTROL MENU (send from the serial monitor command line, terminated with LF or CR/LF):
   A: send CAN request for all supported service 0x01 PIDs
   B: LED intensity NORMAL or BRIGHT (toggle between the two selections)
   C: send CAN CUSTOM commands
   F: FIXED CAN baudrate (not scanning)
   H: HUNT for a matching CAN baudrate (scanning)
   K: execute K-line 5-baud init sequence
   N: NEXT (higher) CAN baudrate (not scanning)
   P: PREVIOUS (lower) CAN baudrate (not scanning)
   Q: send CAN QUERY for the supported service 0x01 PIDs
   W: send CAN WAKEUP commands


[ A: initiate send CAN all supported service 0x01 PIDs from the serial monitor command line ]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x01 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x01: Service 0x01 - Monitor status since DTCs cleared

RX: MBX: 99  LEN: 8  EXT: 1  TS:  4213  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x01 0x00 0x07 0xE5 0x00 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x01 - Monitor status since DTCs cleared
         PID # 0x01 - Monitor status since DTCs cleared
            MIL is OFF
            DTC count = 0x00
            Spark ignition monitors supported (e.g. Otto ot Wankel engines)
               Components Test  = available and complete
               Fuel System Test = available and complete
               Misfire Test     = available and complete
                  EGR and/or VVT System = available and complete
                  Oxygen Sensor Heater  = available and complete
                  Oxygen Sensor         = available and complete
                  A/C Refrigerant       = unavailable
                  Secondary Air System  = unavailable
                  Evaporative System    = available and complete
                  Heated Catalyst       = unavailable
                  Catalyst              = available and complete

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x03 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x03: Service 0x03 - Fuel System Status

RX: MBX: 99  LEN: 8  EXT: 1  TS: 53945  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x03 0x02 0x00 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x03 - Fuel System Status
         PID # 0x03 - Fuel System Status
            Fuel System #1 - closed loop, using oxygen sensor feedback to determine fuel mix
            Fuel System #2 - the motor is off

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x04 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x04: Service 0x04 - Calculated engine load [0 to 100%]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 38505  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x04 0x36 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x04 - Calculated engine load [0 to 100%]
         PID # 0x04 - Calculated engine load [0 to 100%]
            Engine load (%) = 21.09

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x06 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x06: Service 0x06 - Short term fuel trim - Bank 1 [-100 to 99.2%]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 22870  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x06 0x84 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x06 - Short term fuel trim - Bank 1 [-100 to 99.2%]
         PID # 0x06 - Short term fuel trim - Bank 1 [-100 to 99.2%]
            Short term fuel trim - Bank 1 (%) = 3.12

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x07 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x07: Service 0x07 - Long term fuel trim - Bank 1 [-100 to 99.2%]

RX: MBX: 99  LEN: 8  EXT: 1  TS:  7433  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x07 0x7C 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x07 - Long term fuel trim - Bank 1 [-100 to 99.2%]
         PID # 0x07 - Long term fuel trim - Bank 1 [-100 to 99.2%]
            Long term fuel trim - Bank 1 (%) = -3.12

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0B 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x0B: Service 0x0B - Intake manifold absolute pressure [0 to 255 kPa]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 57345  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0B 0x21 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x0B - Intake manifold absolute pressure [0 to 255 kPa]
         PID # 0x0B - Intake manifold absolute pressure [0 to 255 kPa]
            Intake manifold absolute pressure (kPa) = 33.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0C 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x0C: Service 0x0C - Engine speed [0 to 16383.75 rpm]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 41900  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x0C 0x0B 0xB4 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x0C - Engine speed [0 to 16383.75 rpm]
         PID # 0x0C - Engine speed [0 to 16383.75 rpm]
            Engine speed (rpm) = 749.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x0D: Service 0x0D - Vehicle speed [0 to 255 km/h]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 26280  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x0D - Vehicle speed [0 to 255 km/h]
         PID # 0x0D - Vehicle speed [0 to 255 km/h]
            Vehicle speed (km/h) = 0.00 (0.00 mph)

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0E 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x0E: Service 0x0E - Timing advance [-64 to 63.5 degrees before TDC

RX: MBX: 99  LEN: 8  EXT: 1  TS: 10945  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0E 0x88 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x0E - Timing advance [-64 to 63.5 degrees before TDC
         PID # 0x0E - Timing advance [-64 to 63.5 degrees before TDC
            Timing advance (degrees before TDC) = 4.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x11 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x11: Service 0x11 - Throttle position [0 to 100%]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 60826  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x11 0x24 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x11 - Throttle position [0 to 100%]
         PID # 0x11 - Throttle position [0 to 100%]
            Throttle position (%) = 14.12

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x13 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x13: Service 0x13 - Oxygen sensors present (in 2 banks)

RX: MBX: 99  LEN: 8  EXT: 1  TS: 45185  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x13 0x03 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x13 - Oxygen sensors present (in 2 banks)
         PID # 0x13 - Oxygen sensors present (in 2 banks)
            Oxygen sensors - Bank 1, Sensor 1 is present
            Oxygen sensors - Bank 1, Sensor 2 is present
            Oxygen sensors - Bank 1, Sensor 3 is not present
            Oxygen sensors - Bank 1, Sensor 4 is not present
            Oxygen sensors - Bank 2, Sensor 1 is not present
            Oxygen sensors - Bank 2, Sensor 2 is not present
            Oxygen sensors - Bank 2, Sensor 3 is not present
            Oxygen sensors - Bank 2, Sensor 4 is not present

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x15 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x15: Service 0x15 - Oxygen Sensor 2 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 29736  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x15 0x89 0x7C 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x15 - Oxygen Sensor 2 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]
         PID # 0x15 - Oxygen Sensor 2 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]
            Oxygen Sensor 2 - Voltage (V) = 0.69
            Oxygen Sensor 2 - Short term fuel trim (%) = -3.12

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x1C 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x1C: Service 0x1C - OBD standards this vehicle conforms to [1 to 250]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 14261  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x1C 0x01 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x1C - OBD standards this vehicle conforms to [1 to 250]
         PID # 0x1C - OBD standards this vehicle conforms to [1 to 250]
            OBD standards this vehicle conforms to = OBD-II as defined by CARB

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x1F 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x1F: Service 0x1F - Run time since engine start [0 to 65535 s]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 64376  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x1F 0x00 0x2B 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x1F - Run time since engine start [0 to 65535 s]
         PID # 0x1F - Run time since engine start [0 to 65535 s]
            Run time since engine start (s) = 43.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x21 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x21: Service 0x21 - Distance traveled with malfunction indicator lamp (MIL) on [0 to 65535 km]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 48933  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x21 0x00 0x00 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x21 - Distance traveled with malfunction indicator lamp (MIL) on [0 to 65535 km]
         PID # 0x21 - Distance traveled with malfunction indicator lamp (MIL) on [0 to 65535 km]
            Distance traveled with malfunction indicator lamp (MIL) on (km) = 0.00 (0.00 miles)

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x24 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x24: Service 0x24 - Oxygen Sensor 1 - Air-Fuel Equivalence Ratio  [0 to <2] & Voltage [0 to <8 V]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 33845  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x24 0x84 0xAF 0x4A 0xCE 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x24 - Oxygen Sensor 1 - Air-Fuel Equivalence Ratio  [0 to <2] & Voltage [0 to <8 V]
         PID # 0x24 - Oxygen Sensor 1 - Air-Fuel Equivalence Ratio  [0 to <2] & Voltage [0 to <8 V]
            Oxygen Sensor 1 - Air-Fuel Equivalence Ratio = 1.04
                            - Voltage (V) = 2.34

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x2E 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x2E: Service 0x2E - Commanded evaporative purge [0 to 100%]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 18042  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x2E 0x11 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x2E - Commanded evaporative purge [0 to 100%]
         PID # 0x2E - Commanded evaporative purge [0 to 100%]
            Commanded evaporative purge (%) = 6.67

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x2F 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x2F: Service 0x2F - Fuel Tank Level input [0 to 100%]

RX: MBX: 99  LEN: 8  EXT: 1  TS:  2568  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x2F 0x34 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x2F - Fuel Tank Level input [0 to 100%]
         PID # 0x2F - Fuel Tank Level input [0 to 100%]
            Fuel Tank Level Input (%) = 20.39

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x30 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x30: Service 0x30 - Warm-ups since codes cleared [0 to 255]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 52007  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x30 0xFF 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x30 - Warm-ups since codes cleared [0 to 255]
         PID # 0x30 - Warm-ups since codes cleared [0 to 255]
            Warm-ups since codes cleared = 255.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x31 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x31: Service 0x31 - Distance traveled since codes cleared [0 to 65535 km]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 37051  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x31 0xFF 0xE9 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x31 - Distance traveled since codes cleared [0 to 65535 km]
         PID # 0x31 - Distance traveled since codes cleared [0 to 65535 km]
            Distance traveled since codes cleared = 65513.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x32 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x32: Service 0x32 - Evap. System Vapor Pressure [-8192 to 8191.75 Pa]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 20900  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x32 0xFE 0x8C 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x32 - Evap. System Vapor Pressure [-8192 to 8191.75 Pa]
         PID # 0x32 - Evap. System Vapor Pressure [-8192 to 8191.75 Pa]
            Evap. System Vapor Pressure (Pa) = -93.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x33 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x33: Service 0x33 - Absolute Barometric Pressure [0 to 255 kPa]

RX: MBX: 99  LEN: 8  EXT: 1  TS:  5622  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x33 0x62 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x33 - Absolute Barometric Pressure [0 to 255 kPa]
         PID # 0x33 - Absolute Barometric Pressure [0 to 255 kPa]
            Absolute Barometric Pressure (kPa) = 98.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x3C 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x3C: Service 0x3C - Catalyst Temperature: Bank 1, Sensor 1 [-40 to 6513.5 degrees C]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 55363  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x3C 0x07 0xD0 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x3C - Catalyst Temperature: Bank 1, Sensor 1 [-40 to 6513.5 degrees C]
         PID # 0x3C - Catalyst Temperature: Bank 1, Sensor 1 [-40 to 6513.5 degrees C]
            Catalyst Temperature: Bank 1, Sensor 1 = 160.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x41 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x41: Service 0x41 - Monitor status this drive cycle

RX: MBX: 99  LEN: 8  EXT: 1  TS: 39832  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x41 0x00 0x27 0xE1 0xA5 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x41 - Monitor status this drive cycle
         PID # 0x41 - Monitor status this drive cycle
               Components Test  = available and complete
               Fuel System Test = available but incomplete
               Misfire Test     = available and complete
                  EGR and/or VVT System = available but incomplete
                  PM filter monitoring  = available and complete
                  Exhaust Gas Sensor    = available but incomplete
                  - Reserved -          = unavailable
                  Boost Pressure        = unavailable
                  - Reserved -          = unavailable
                  NOx/SCR Monitor       = unavailable
                  NMHC Catalyst         = available but incomplete

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x42 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x42: Service 0x42 - Control module voltage [0 to 65535 V]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 24288  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x42 0x31 0xEE 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x42 - Control module voltage [0 to 65535 V]
         PID # 0x42 - Control module voltage [0 to 65535 V]
            Control Module voltage (V) = 12.78

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x43 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x43: Service 0x43 - Absolute load value [0 to 27500%]

RX: MBX: 99  LEN: 8  EXT: 1  TS:  8756  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x43 0x00 0x33 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x43 - Absolute load value [0 to 27500%]
         PID # 0x43 - Absolute load value [0 to 27500%]
            Absolute load value (%) = 20.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x44 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x44: Service 0x44 - Commanded Air-Fuel Equivalence Ratio [0 to <2]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 58757  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x44 0x7D 0x8C 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x44 - Commanded Air-Fuel Equivalence Ratio [0 to <2]
         PID # 0x44 - Commanded Air-Fuel Equivalence Ratio [0 to <2]
            Commanded Air-Fuel Equivalence Ratio = 0.98

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x47 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x47: Service 0x47 - Absolute throttle position B [0 to 100%]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 43218  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x47 0x23 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x47 - Absolute throttle position B [0 to 100%]
         PID # 0x47 - Absolute throttle position B [0 to 100%]
            Absolute throttle position B (%) = 13.73

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x49 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x49: Service 0x49 - Absolute throttle position D [0 to 100%]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 27683  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x49 0x31 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x49 - Absolute throttle position D [0 to 100%]
         PID # 0x49 - Absolute throttle position D [0 to 100%]
            Absolute throttle position D (%) = 19.22

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x4A 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x4A: Service 0x4A - Absolute throttle position E [0 to 100%]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 12145  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x4A 0x18 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x4A - Absolute throttle position E [0 to 100%]
         PID # 0x4A - Absolute throttle position E [0 to 100%]
            Absolute throttle position E (%) = 9.41

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x51 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x51: Service 0x51 - Fuel Type

RX: MBX: 99  LEN: 8  EXT: 1  TS: 62145  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x51 0x01 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x51 - Fuel Type
         PID # 0x51 - Fuel Type
            Fuel Type = Gasoline

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x55 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x55: Service 0x55 - Short term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 46609  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x55 0x82 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x55 - Short term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]
         PID # 0x55 - Short term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]
            Short term secondary oxygen sensor trim, bank 1 (%) = 1.56
            Short term secondary oxygen sensor trim, bank 3 (%) = -33.59

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x56 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x56: Service 0x56 - Long term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 31076  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x56 0x80 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x56 - Long term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]
         PID # 0x56 - Long term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]
            Long term secondary oxygen sensor trim, bank 1 (%) = 0.00
            Long term secondary oxygen sensor trim, bank 3 (%) = -33.59

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x62 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x62: Service 0x62 - Actual engine - percent torque [-125 to 130%]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 15536  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x62 0x91 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x62 - Actual engine - percent torque [-125 to 130%]
         PID # 0x62 - Actual engine - percent torque [-125 to 130%]
            Actual engine - percent torque (%) = 20.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x63 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x63: Service 0x63 - Engine reference torque [0 to 65535 N-m]

RX: MBX: 99  LEN: 8  EXT: 1  TS:     4  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x63 0x00 0x93 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x63 - Engine reference torque [0 to 65535 N-m]
         PID # 0x63 - Engine reference torque [0 to 65535 N-m]
            Engine reference torque (N-m) = 147.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x66 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x66: Service 0x66 - Mass air flow sensor [0 to 2047.96875 g/s]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 50008  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x07 0x41 0x66 0x01 0x00 0x59 0x00 0x00 
      0x41: service 0x01 request response
      0x41: Service 0x66 - Mass air flow sensor [0 to 2047.96875 g/s]
         PID # 0x66 - Mass air flow sensor [0 to 2047.96875 g/s]
            Mass air flow sensor A (g/s) = 2.78
            Mass air flow sensor B is not supported

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x67 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x67: Service 0x67 - Engine coolant temperature [-40 to 215 degrees C]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 34465  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x05 0x41 0x67 0x03 0x71 0x4F 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x67 - Engine coolant temperature [-40 to 215 degrees C]
         PID # 0x67 - Engine coolant temperature [-40 to 215 degrees C]
            Engine coolant temperature - Sensor 1 (degrees C) = 73.00 (163.40 degrees F)
            Engine coolant temperature - Sensor 2 (degrees C) = 73.00 (163.40 degrees F)

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x68 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x68: Service 0x68 - Intake air temperature sensor [-40 to 215 degrees C]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 18937  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x10 0x09 0x41 0x68 0x01 0x58 0x00 0x00 
      0x41: Service 0x68 - Intake air temperature sensor [-40 to 215 degrees C]
         PID # 0x68 - Intake air temperature sensor [-40 to 215 degrees C]
            Intake air temperature sensor #1 (degrees C) = 48.00 (118.40 degrees F)
            Intake air temperature sensor #2 is not supported

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x6C 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x6C: Service 0x6C - Commanded throttle actuator control and relative throttle position

RX: MBX: 99  LEN: 8  EXT: 1  TS:  3398  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x07 0x41 0x6C 0x03 0x09 0x08 0x00 0x00 
      0x41: service 0x01 request response
      0x41: Service 0x6C - Commanded throttle actuator control and relative throttle position
         PID # 0x6C - Commanded throttle actuator control and relative throttle position (detailed definition of content is unavailable)

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x8E 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x8E: Service 0x8E - Engine Friction - Percent Torque [-125 to 130%]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 53394  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x8E 0x8D 0x55 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x8E - Engine Friction - Percent Torque [-125 to 130%]
         PID # 0x8E - Engine Friction - Percent Torque [-125 to 130%]
            Engine Friction - Percent Torque (%) = 16.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x9D 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x9D: Service 0x9D - Engine Fuel Rate [g/s]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 37861  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x9D 0x00 0x0A 0x00 0x0A 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x9D - Engine Fuel Rate [g/s]
         PID # 0x9D - Engine Fuel Rate [g/s] (detailed definition of content is unavailable)

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x9E 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x9E: Service 0x9E - Engine Exhaust Flow Rate [kg/h]

RX: MBX: 99  LEN: 8  EXT: 1  TS: 22325  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x9E 0x00 0x3A 0x55 0x55 0x55 
      0x41: service 0x01 request response
      0x41: Service 0x9E - Engine Exhaust Flow Rate [kg/h]
         PID # 0x9E - Engine Exhaust Flow Rate [kg/h] (detailed definition of content is unavailable)

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x9F 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0x9F: Service 0x9F - Fuel System Percentage Use

RX: MBX: 99  LEN: 8  EXT: 1  TS:  7269  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x10 0x0B 0x41 0x9F 0x01 0xFF 0x00 0x00 
      0x41: Service 0x9F - Fuel System Percentage Use
         PID # 0x9F - Fuel System Percentage Use (detailed definition of content is unavailable)

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0xA6 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request
      0xA6: Service 0xA6 - Odometer [0 to 429496729.5 km]


CAN baudrate set to: 500000 (not scanning)

CONTROL MENU (send from the serial monitor command line, terminated with LF or CR/LF):
   A: send CAN request for all supported service 0x01 PIDs
   B: LED intensity NORMAL or BRIGHT (toggle between the two selections)
   C: send CAN CUSTOM commands
   F: FIXED CAN baudrate (not scanning)
   H: HUNT for a matching CAN baudrate (scanning)
   K: execute K-line 5-baud init sequence
   N: NEXT (higher) CAN baudrate (not scanning)
   P: PREVIOUS (lower) CAN baudrate (not scanning)
   Q: send CAN QUERY for the supported service 0x01 PIDs
   W: send CAN WAKEUP commands

RX: MBX: 99  LEN: 8  EXT: 1  TS: 56794  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0xA6 0x00 0x0D 0xAF 0xAC 0x55 
      0x41: service 0x01 request response
      0x41: Service 0xA6 - Odometer [0 to 429496729.5 km]
         PID # 0xA6 - Odometer [0 to 429496729.5 km]
            Odometer (km) = 172.00 (106.88 miles)


One additional thing of note that I have observed since last time is that some replies contain 0x10 in the first data byte. <This> OBD-II wiki page specifies that the first data byte will be <= 0x07 & is used to indicate the number of additional data bytes in the reply. So, replies which start with 0x10 have all of the included data bytes shitfed one byte later than expected (following the 0x10). Sandra mentioned something to this effect earlier:

the thing is that when the car is off I get the pid on buf 2 but when engine is on I get the pid on buf 3, the numbers seem to be good ones.

I want to once again thank you for your invaluable help !! I would still be floundering around without the keen guidance that you have provided. Now I just have to test my TeensyCANmonitor utility on as many other cars as I can to make sure that the code for handling the additional PIDs not supported by my two cars does not have any latent bugs in it. Also, many of the higher value PIDs do not inlcude formula details, so I will also look at the returned data & see if I can discern the flags & values contained in the data to expand the decoding of those to include the desired details.

Mark J Culross
KD5RXT
 
Hi Mark, you've made huge progress, great!
Thanks for sharing your code - I will definitely give it a try when I got some more spare time!

Paul
 
In case anyone is either following along with this thread, or stumbles across it sometime in the future, I'm posting another update to my TeensyCANmonitor utility sketch. The version that I last posted failed to include some of the updates that I had previously made when changing function titles from camelCase to underscore_separated...whoops !!

So, here's the latest version (20220920-1940) of my TeensyCANmonitor utility sketch for the Teensy 4.x:

Code:
//
//  Teensy 4.x CAN bus monitor utility - version 1.0 dated 20220920-1940
//
//    - 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)
//
//    - 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/20/2022 @1940";
const String AUTHOR    = "designed & written by Mark J Culross (KD5RXT)";

#include <FlexCAN_T4.h>
#include <EEPROM.h>

FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can1;

//#define DEBUG_HEARTBEAT                 // uncomment to use the onboard LED as a heartbeat indicator
//#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

unsigned long heartbeat_timer = millis();
bool heartbeat_toggle = false;

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_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];

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

// function headers
void can_sniff_RX(const CAN_message_t &msg);
void can_sniff_TX(const CAN_message_t &msg);
void can_TX(const CAN_message_t &msg);
void decode_extended_service_01_PID_content(const CAN_message_t &msg);
void decode_service_01_PID_content(const CAN_message_t &msg);
void decode_service_01_PID_title(int i);
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(void);
void send_CAN_custom_commands(void);
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);



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: ");
   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("");

#ifndef DEBUG_HEARTBEAT
   LED_timer = millis();
   analogWrite(LED_PIN, led_intensity_on);
#endif

   CAN_baudrate_match_found = true;
   CAN_baudrate_match_timeout = millis();

   if (msg.buf[0] <= 0x07)
   {
      switch (msg.buf[1])
      {
         case 0x41:
         {
            Serial.println("      0x41: service 0x01 request response");

            Serial.print("      0x");
            Serial.print((msg.buf[1] & 0xF0) >> 4, HEX);
            Serial.print(msg.buf[1] & 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_PID_title(msg.buf[2]);
            Serial.println("");

            if (debug_CAN_rx)
            {
               decode_service_01_PID_content(msg);
            }
         }
         break;

         default:
         {
            Serial.print("      0x");
            Serial.print((msg.buf[1] & 0xF0) >> 4, HEX);
            Serial.print(msg.buf[1] & 0x0F, HEX);
            Serial.println(": unknown service request response");
         }
         break;
      }
   } else {
      switch (msg.buf[0])
      {
         case 0x10:
         {
            switch (msg.buf[2])
            {
               case 0x41:
               {
                  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_PID_title(msg.buf[3]);
                  Serial.println("");

                  if (debug_CAN_rx)
                  {
                     decode_extended_service_01_PID_content(msg);
                  }
               }
               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;
      }
   }
}  // can_sniff_RX()


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: ");
   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("");

   if (msg.buf[1] == 0x01)
   {
      Serial.println("      0x01: service 0x01 request");

      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_PID_title(msg.buf[2]);
      Serial.println("");
      Serial.println("");
   }

#ifndef DEBUG_HEARTBEAT
   LED_timer = millis();
   analogWrite(LED_PIN, led_intensity_on);
#endif

   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()


// decode the extended service 0x01 PID content into data reported
void decode_extended_service_01_PID_content(const CAN_message_t &msg)
{
   int service = msg.buf[3];
   int A = msg.buf[4];
   int B = msg.buf[5];
   //  int C = msg.buf[6];
   //  int D = msg.buf[7];
   bool valid_command = true;

   switch (service)
   {
      case 0x68:
      {
         Serial.print("         PID # 0x68 - ");
         decode_service_01_PID_title(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 0x70:
      case 0x9F:
      case 0xA3:
      {
         Serial.print("         PID # 0x");
         Serial.print((service & 0xF0) >> 4, HEX);
         Serial.print(service & 0x0F, HEX);
         Serial.print(" - ");
         decode_service_01_PID_title(service);
         Serial.println(" (detailed definition of content is unavailable)");
      }
      break;

      default:
      {
         valid_command = false;
      }
      break;
   }

   if (valid_command)
   {
      Serial.println("");
   }
}  // decode_extended_service_01_PID_content()


// decode the service 0x01 PID content into data reported
void decode_service_01_PID_content(const CAN_message_t &msg)
{
   int j = 1;
   int service = 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];
   bool valid_command = true;

   switch (service)
   {
      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 (service)
   {
      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_PID_title(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_PID_title(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_PID_title(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_PID_title(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_PID_title(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);

         // B3
         if (B & 0x08)
         {
            Serial.println("            Compression ignition monitors supported (e.g. Diesel engines)");
         } else {
            Serial.println("            Spark ignition monitors supported (e.g. Otto ot 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 0x03:
      {
         Serial.print("         PID # 0x03 - ");
         decode_service_01_PID_title(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_PID_title(0x04);
         Serial.println("");

         Serial.print("            Engine load (%) = ");
         Serial.println((float)(A) / 2.56);
      }
      break;

      case 0x05:
      {
         Serial.print("         PID # 0x05 - ");
         decode_service_01_PID_title(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_PID_title(0x06);
         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_PID_title(0x07);
         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_PID_title(0x08);
         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_PID_title(0x09);
         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_PID_title(0x0A);
         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_PID_title(0x0B);
         Serial.println("");

         Serial.print("            Intake manifold absolute pressure (kPa) = ");
         Serial.println((float)(A));
      }
      break;

      case 0x0C:
      {
         Serial.print("         PID # 0x0C - ");
         decode_service_01_PID_title(0x0C);
         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_PID_title(0x0D);
         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_PID_title(0x0E);
         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_PID_title(0x0F);
         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_PID_title(0x10);
         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_PID_title(0x11);
         Serial.println("");

         Serial.print("            Throttle position (%) = ");
         Serial.println((float)(A) * 100.0 / 255.0);
      }
      break;

      case 0x12:
      {
         Serial.print("         PID # 0x12 - ");
         decode_service_01_PID_title(0x12);
         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_PID_title(0x13);
         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_PID_title(0x14);
         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_PID_title(0x15);
         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_PID_title(0x16);
         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_PID_title(0x17);
         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_PID_title(0x18);
         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_PID_title(0x19);
         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_PID_title(0x1A);
         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_PID_title(0x1B);
         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_PID_title(0x1C);
         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_PID_title(0x1D);
         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_PID_title(0x1E);
         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_PID_title(0x1F);
         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_PID_title(0x21);
         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_PID_title(0x22);
         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_PID_title(0x23);
         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_PID_title(0x24);
         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_PID_title(0x25);
         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_PID_title(0x26);
         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_PID_title(0x27);
         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_PID_title(0x28);
         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_PID_title(0x29);
         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_PID_title(0x2A);
         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_PID_title(0x2B);
         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_PID_title(0x2C);
         Serial.println("");

         Serial.print("            Commanded EGR (%) = ");
         Serial.println((float)(A) * 100.0 / 255.0);
      }
      break;

      case 0x2D:
      {
         Serial.print("         PID # 0x2D - ");
         decode_service_01_PID_title(0x2D);
         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_PID_title(0x2E);
         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_PID_title(0x2F);
         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_PID_title(0x30);
         Serial.println("");

         Serial.print("            Warm-ups since codes cleared = ");
         Serial.println((float)(A));
      }
      break;

      case 0x31:
      {
         Serial.print("         PID # 0x31 - ");
         decode_service_01_PID_title(0x31);
         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_PID_title(0x32);
         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_PID_title(0x33);
         Serial.println("");

         Serial.print("            Absolute Barometric Pressure (kPa) = ");
         Serial.println((float)(A));
      }
      break;

      case 0x34:
      {
         Serial.print("         PID # 0x34 - ");
         decode_service_01_PID_title(0x34);
         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_PID_title(0x35);
         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_PID_title(0x36);
         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_PID_title(0x37);
         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_PID_title(0x38);
         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_PID_title(0x39);
         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_PID_title(0x3A);
         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_PID_title(0x3B);
         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_PID_title(0x3C);
         Serial.println("");

         Serial.print("            Catalyst Temperature: Bank 1, Sensor 1 (degrees C) = ");
         Serial.println(((((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_PID_title(0x3D);
         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_PID_title(0x3E);
         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_PID_title(0x3F);
         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_PID_title(0x41);
         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 & 0x01)
         {
            // 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_PID_title(0x42);
         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_PID_title(0x43);
         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_PID_title(0x44);
         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_PID_title(0x45);
         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_PID_title(0x46);
         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_PID_title(0x47);
         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_PID_title(0x48);
         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_PID_title(0x49);
         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_PID_title(0x4A);
         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_PID_title(0x4B);
         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_PID_title(0x4C);
         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_PID_title(0x4D);
         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_PID_title(0x4E);
         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_PID_title(0x4F);
         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_PID_title(0x51);
         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_PID_title(0x52);
         Serial.println("");

         Serial.print("            Ethanol Fuel % = ");
         Serial.println((float)(A) * 100.0 / 255.0);
      }
      break;

      case 0x53:
      {
         Serial.print("         PID # 0x53 - ");
         decode_service_01_PID_title(0x53);
         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_PID_title(0x54);
         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_PID_title(0x55);
         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_PID_title(0x56);
         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_PID_title(0x57);
         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_PID_title(0x58);
         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_PID_title(0x59);
         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_PID_title(0x5A);
         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_PID_title(0x5B);
         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_PID_title(0x5C);
         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_PID_title(0x5D);
         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_PID_title(0x5E);
         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_PID_title(0x5F);
         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_PID_title(0x61);
         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_PID_title(0x62);
         Serial.println("");

         Serial.print("            Actual engine - percent torque (%) = ");
         Serial.println((float)(A) - 125.0);
      }
      break;

      case 0x63:
      {
         Serial.print("         PID # 0x63 - ");
         decode_service_01_PID_title(0x63);
         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_PID_title(0x64);
         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_PID_title(0x65);
         Serial.println("");

         Serial.println("            Auxiliary input / output supported (bit encoded, but definitions are unavailable)");
      }
      break;

      case 0x66:
      {
         Serial.print("         PID # 0x66 - ");
         decode_service_01_PID_title(0x66);
         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_PID_title(0x67);
         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_PID_title(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((service & 0xF0) >> 4, HEX);
         Serial.print(service & 0x0F, HEX);
         Serial.print(" - ");
         decode_service_01_PID_title(service);
         Serial.println(" (detailed definition of content is unavailable)");
      }
      break;

      case 0x78:
      {
         Serial.print("         PID # 0x78 - ");
         decode_service_01_PID_title(0x78);
         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_PID_title(0x79);
         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((service & 0xF0) >> 4, HEX);
         Serial.print(service & 0x0F, HEX);
         Serial.print(" - ");
         decode_service_01_PID_title(service);
         Serial.println(" (detailed definition of content is unavailable)");
      }
      break;

      case 0x7C:
      {
         Serial.print("         PID # 0x7C - ");
         decode_service_01_PID_title(0x7C);
         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((service & 0xF0) >> 4, HEX);
         Serial.print(service & 0x0F, HEX);
         Serial.print(" - ");
         decode_service_01_PID_title(service);
         Serial.println(" (detailed definition of content is unavailable)");
      }
      break;

      case 0x8E:
      {
         Serial.print("         PID # 0x8E - ");
         decode_service_01_PID_title(0x8E);
         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((service & 0xF0) >> 4, HEX);
         Serial.print(service & 0x0F, HEX);
         Serial.print(" - ");
         decode_service_01_PID_title(service);
         Serial.println(" (detailed definition of content is unavailable)");
      }
      break;

      case 0xA2:
      {
         Serial.print("         PID # 0xA2 - ");
         decode_service_01_PID_title(0xA2);
         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((service & 0xF0) >> 4, HEX);
         Serial.print(service & 0x0F, HEX);
         Serial.print(" - ");
         decode_service_01_PID_title(service);
         Serial.println(" (detailed definition of content is unavailable)");
      }
      break;

      case 0xA4:
      {
         Serial.print("         PID # 0xA4 - ");
         decode_service_01_PID_title(0xA4);
         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_PID_title(0xA5);
         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)(A) * 256.0 * 256.0) + ((float)(A) * 256.0) + (float)(D));
         Serial.print("         PID # 0xA6 - ");
         decode_service_01_PID_title(0xA6);
         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((service & 0xF0) >> 4, HEX);
         Serial.print(service & 0x0F, HEX);
         Serial.print(" - ");
         decode_service_01_PID_title(service);
         Serial.println(" (detailed definition of content is unavailable)");
      }
      break;

      case 0xA9:
      {
         Serial.print("         PID # 0xA9 - ");
         decode_service_01_PID_title(0xA9);
         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((service & 0xF0) >> 4, HEX);
         Serial.print(service & 0x0F, HEX);
         Serial.print(" - ");
         decode_service_01_PID_title(service);
         Serial.println(" (detailed definition of content is unavailable)");
      }
      break;

      case 0xC4:
      {
         Serial.print("         PID # 0xC4 - ");
         decode_service_01_PID_title(0xC4);
         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("");
   }
}  // decode_service_01_PID_content()


// decode the service 0x01 PID into a title
void decode_service_01_PID_title(int i)
{
   switch (i)
   {
      case 0x00:
      {
         Serial.print("PIDs supported [0x01-0x20]");
      }
      break;

      case 0x01:
      {
         Serial.print("Monitor status since DTCs cleared");
      }
      break;

      case 0x02:
      {
         Serial.print("Freeze DTC");
      }
      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((i & 0xF0) >> 4, HEX);
         Serial.print(i & 0x0F, HEX);
      }
      break;
   }
}  // decode_service_01_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 inKey = 0x00;

   Can1.events();

#ifndef DEBUG_HEARTBEAT
   if ((LED_timer) && ((millis() - LED_timer) > LED_DURATION_MSEC))
   {
      analogWrite(LED_PIN, LED_INTENSITY_OFF);
      LED_timer = 0;
   }
#else
   if (millis() - heartbeat_timer > 250)
   {
      heartbeat_toggle = !heartbeat_toggle;
      analogWrite(LED_PIN, heartbeat_toggle);
      heartbeat_timer = millis();
   }
#endif

   // 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();

#ifndef DEBUG_HEARTBEAT
            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);
#endif

            CAN_baud_change_timer = millis();
         }
      }
   }

   while (Serial.available() > 0)
   {
      char cmdKey = inKey;
      inKey = Serial.read();

      bool report_current = false;
      switch (inKey)
      {
         case LF:
         {
            report_current = true;
         }
         break;

         // send all supported service 0x01 PID CAN commands
         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();
         }
         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;
            }
#ifndef DEBUG_HEARTBEAT
            LED_timer = millis();
            analogWrite(LED_PIN, led_intensity_on);
#endif

            // 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("");

            send_CAN_custom_commands();
         }
         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;

         // set fixed CAN baudrate
         case 'f':
         case 'F':
         {
            Serial.println("");
            Serial.println("[ F: initiate set fixed CAN baudrate from the serial monitor command line ]");
            Serial.println("");

            if (!(CAN_baudrate_fixed))
            {
               CAN_baudrate_fixed = true;

               // save settings to EEPROM
               save_settings();
            }
         }
         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("");

            if (CAN_baudrate_fixed)
            {
               CAN_baudrate_fixed = false;

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

         // 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 the supported service 0x01 PIDs
         case 'q':
         case 'Q':
         {
            Serial.println("");
            Serial.println("[ Q: initiate send CAN query for the supported service 0x01 PIDs from the serial monitor command line ]");
            Serial.println("");

            send_CAN_query_supported_PIDs_for_service_01();
         }
         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 ((cmdKey == 'w') || (cmdKey == 'W'))
      {
         report_current = false;
      }

      if (report_current)
      {
         report_current = false;

         report_current_baudrate();
      }
   }

}  // 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 PID CAN requests
void send_CAN_all_supported_PIDs_for_service_01(void)
{
   CAN_message_t can_MsgTx;

   for (int i = 0; i < 256; i++)
   {
      if ((supported_PIDs_for_service_01[i]) && (i % 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] = CAN_STANDARD_SERVICE_MODE_1_0x01;
         can_MsgTx.buf[2] = i;
         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_all_supported_PIDs_for_service_01()


// send CAN custom commands
void send_CAN_custom_commands(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] = 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);


   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] = 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_STANDARD_SERVICE_MODE_1_0x01;
   can_MsgTx.buf[2] = 0x04;
   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] = 0x06;
   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] = 0x07;
   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] = 0x0B;
   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] = 0x0C;
   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] = 0x0D;
   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] = 0x0E;
   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] = 0x11;
   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] = 0x13;
   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] = 0x15;
   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] = 0x1C;
   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] = 0x1E;
   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] = 0x1F;
   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] = 0x2F;
   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] = 0x68;
   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 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);

   report_current_baudrate();

   for (int i = 0; i < 256; i++)
   {
      supported_PIDs_for_service_01[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 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)");
   }

   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("   C[*] : send CAN CUSTOM commands");

   Serial.print("   F[");
   if (CAN_baudrate_fixed)
   {
      Serial.println("+] : FIXED CAN baudrate (not scanning)");
   } else {
      Serial.println("-] : FIXED CAN baudrate (not scanning)");
   }

   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("   N[*] : NEXT (higher) CAN baudrate (not scanning)");

   Serial.println("   P[*] : PREVIOUS (lower) CAN baudrate (not scanning)");

   Serial.println("   Q[*] : send CAN QUERY for the supported service 0x01 PIDs");

   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

Mark J Culross
KD5RXT
 
Last edited:
Hi Paul and Mark,

I'm a bit out off the topic right now, as I have no time to test anythting, I'm travelling a lot these last months due to work and I don't have free time for it. I keep reading your progress and I will try to test as much as possible whenever I can.
Thanks for sharing it! :)
 
Latest additions:

- reassigned menu selection "F": ability to request service 0x02 freeze frame data (queries the DTC count, then if != 0, reports the DTC for each freeze frame, followed by the freeze frame data for each of those DTCs)
- modified menu selection "H": now a toggle
- expanded menu selection "C": now able to send service 0x02 custom commands (with or without frame number specified) in addition to service 0x01 custom commands


Here's yesterday's updated version (20220922-2350) of my TeensyCANmonitor utility sketch for the Teensy 4.x:

Code:
//
//  Teensy 4.x CAN bus monitor utility - version 1.0 dated 20220922-2350
//
//    - 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)
//
//    - 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/22/2022 @2350";
const String AUTHOR    = "designed & written by Mark J Culross (KD5RXT)";

#include <FlexCAN_T4.h>
#include <EEPROM.h>

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_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];

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_TX(const CAN_message_t &msg);
void can_TX(const CAN_message_t &msg);
void decode_extended_service_01_PID_content(const CAN_message_t &msg);
void decode_service_01_02_PID_content(const CAN_message_t &msg);
void decode_service_01_02_PID_title(int response, int service);
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_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);



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: ");
   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);
            }
         }
         break;

         default:
         {
            Serial.print("      0x");
            Serial.print((msg.buf[1] & 0xF0) >> 4, HEX);
            Serial.print(msg.buf[1] & 0x0F, HEX);
            Serial.println(": unknown service request response");
         }
         break;
      }
   } else {
      switch (msg.buf[0])
      {
         case 0x10:
         {
            switch (msg.buf[2])
            {
               case 0x41:
               {
                  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("");

                  if (debug_CAN_rx)
                  {
                     decode_extended_service_01_PID_content(msg);
                  }
               }
               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;
      }
   }
}  // can_sniff_RX()


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: ");
   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("");

   if ((msg.buf[1] == 0x01) || (msg.buf[1] == 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("");
   }

   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()


// decode the extended service 0x01 PID content into data reported
void decode_extended_service_01_PID_content(const CAN_message_t &msg)
{
   int response = msg.buf[2];
   int service = msg.buf[3];
   int A = msg.buf[4];
   int B = msg.buf[5];
   //  int C = msg.buf[6];
   //  int D = msg.buf[7];
   bool valid_command = true;

   switch (service)
   {
      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 0x70:
      case 0x9F:
      case 0xA3:
      {
         Serial.print("         PID # 0x");
         Serial.print((service & 0xF0) >> 4, HEX);
         Serial.print(service & 0x0F, HEX);
         Serial.print(" - ");
         decode_service_01_02_PID_title(response, service);
         Serial.println("");
         Serial.println("            (detailed definition of content is unavailable)");
      }
      break;

      default:
      {
         valid_command = false;
      }
      break;
   }

   if (valid_command)
   {
      Serial.println("");
   }
}  // decode_extended_service_01_PID_content()


// decode the service 0x01 PID content into data reported
void decode_service_01_02_PID_content(const CAN_message_t &msg)
{
   int j = 1;
   int response = msg.buf[1];
   int service = 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];
   bool valid_command = true;

   switch (service)
   {
      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 (service)
   {
      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 ((msg.buf[1] == 0x42) && (msg.buf[2] == 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.56);
      }
      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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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, service);
         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((service & 0xF0) >> 4, HEX);
         Serial.print(service & 0x0F, HEX);
         Serial.print(" - ");
         decode_service_01_02_PID_title(response, service);
         Serial.println("");
         Serial.println("            (detailed definition of content is unavailable)");
      }
      break;

      case 0x78:
      {
         Serial.print("         PID # 0x78 - ");
         decode_service_01_02_PID_title(response, service);
         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, service);
         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((service & 0xF0) >> 4, HEX);
         Serial.print(service & 0x0F, HEX);
         Serial.print(" - ");
         decode_service_01_02_PID_title(response, service);
         Serial.println("");
         Serial.println("            (detailed definition of content is unavailable)");
      }
      break;

      case 0x7C:
      {
         Serial.print("         PID # 0x7C - ");
         decode_service_01_02_PID_title(response, service);
         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((service & 0xF0) >> 4, HEX);
         Serial.print(service & 0x0F, HEX);
         Serial.print(" - ");
         decode_service_01_02_PID_title(response, service);
         Serial.println("");
         Serial.println("            (detailed definition of content is unavailable)");
      }
      break;

      case 0x8E:
      {
         Serial.print("         PID # 0x8E - ");
         decode_service_01_02_PID_title(response, service);
         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((service & 0xF0) >> 4, HEX);
         Serial.print(service & 0x0F, HEX);
         Serial.print(" - ");
         decode_service_01_02_PID_title(response, service);
         Serial.println("");
         Serial.println("            (detailed definition of content is unavailable)");
      }
      break;

      case 0xA2:
      {
         Serial.print("         PID # 0xA2 - ");
         decode_service_01_02_PID_title(response, service);
         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((service & 0xF0) >> 4, HEX);
         Serial.print(service & 0x0F, HEX);
         Serial.print(" - ");
         decode_service_01_02_PID_title(response, service);
         Serial.println("");
         Serial.println("            (detailed definition of content is unavailable)");
      }
      break;

      case 0xA4:
      {
         Serial.print("         PID # 0xA4 - ");
         decode_service_01_02_PID_title(response, service);
         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, service);
         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, service);
         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((service & 0xF0) >> 4, HEX);
         Serial.print(service & 0x0F, HEX);
         Serial.print(" - ");
         decode_service_01_02_PID_title(response, service);
         Serial.println("");
         Serial.println("            (detailed definition of content is unavailable)");
      }
      break;

      case 0xA9:
      {
         Serial.print("         PID # 0xA9 - ");
         decode_service_01_02_PID_title(response, service);
         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((service & 0xF0) >> 4, HEX);
         Serial.print(service & 0x0F, HEX);
         Serial.print(" - ");
         decode_service_01_02_PID_title(response, service);
         Serial.println("");
         Serial.println("            (detailed definition of content is unavailable)");
      }
      break;

      case 0xC4:
      {
         Serial.print("         PID # 0xC4 - ");
         decode_service_01_02_PID_title(response, service);
         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("");
   }
}  // decode_service_01_02_PID_content()


// decode the service 0x01 PID into a title
void decode_service_01_02_PID_title(int response, int service)
{
   switch (service)
   {
      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((service & 0xF0) >> 4, HEX);
         Serial.print(service & 0x0F, HEX);
      }
      break;
   }
}  // decode_service_01_02_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 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;

               // 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 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] = 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] = 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] = 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 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);

   report_current_baudrate();

   for (int i = 0; i < 256; i++)
   {
      supported_PIDs_for_service_01[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)");
   }

   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("   C[*] : send CAN CUSTOM command (specify 2-char hex SERVICE, 2-char hex PID, & optional (only for Service #2) 2-char hex FRAME)");

   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("   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("   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


Sandra: looking forward to seeing what you get if/when you might be able to test my sketch. My wife's 2020 Honda CR-V supports a few additional PIDs not supported by my 2020 Honda Civic, so I was able to confirm those with her vehicle. I'm very interested if your vehicle supports any PIDs not already supported by our two Hondas.

Thanks for following along !!

Mark J Culross
KD5RXT
 
[ Had to create two separate posts. When I attempted to include both the sketch code & the example capture, it exceeded the maximum allowable size for a single post !! ]

Yet to be implemented:
- service 0x09 vehicle info requests (initiallly, just PID 0x00: service 0x09 supported PIDS, PID 0x01: message count for PID 0x02, PID 0x02: VIN, & PID 0x0A: ECU name)

Here's an example capture from my 2020 Honda Civic using yesterday's updated version (20220922-2350) of my TeensyCANmonitor utility sketch for the Teensy 4.x:


Code:
=============================================
       Teensy CAN bus monitor utility
     version 1.0 dated 09/22/2022 @2350
designed & written by Mark J Culross (KD5RXT)
=============================================



Attempting to read/verify saved settings (10 values) from EEPROM...verified settings (10 values) in EEPROM are valid...


EEPROM USED: 10
EEPROM MAX ALLOWED: 4284


CAN baudrate set to: 500000 (not scanning)

CONTROL MENU (send from the serial monitor command line, terminated with LF or CR/LF):

   A[*] : send CAN request for all supported service 0x01 (current data) PIDs
   B[+] : LED intensity NORMAL or BRIGHT (toggle between the two selections)
   D[+] : DEBUG CAN receive details (toggle between enable & disable)
   C[*] : send CAN CUSTOM command (specify 2-char hex SERVICE, 2-char hex PID, & optional (only for Service #2) 2-char hex FRAME)
   F[*] : send CAN request for all supported service 0x02 (freeze frame data) PIDs
   H[-] : HUNT for a matching CAN baudrate (scanning)
   K[*] : execute K-line 5-baud init sequence
   N[*] : NEXT (higher) CAN baudrate (not scanning)
   P[*] : PREVIOUS (lower) CAN baudrate (not scanning)
   Q[*] : send CAN QUERY for all supported service 0x01 PIDs
   W[*] : send CAN WAKEUP commands

   NOTE:   [*] indicates an immediate command
           [+] indicates a toggle that is currently enabled
           [-] indicates a toggle that is currently disabled


[ Q: initiate send CAN query for all supported service 0x01 PIDs from the serial monitor command line ]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x00 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x00: Service 0x00 - PIDs supported [0x01-0x20]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 56901  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x00 0xB6 0x3C 0xA8 0x13 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x00 - PIDs supported [0x01-0x20]
         PID # 0x01 is supported - Monitor status since DTCs cleared
         PID # 0x03 is supported - Fuel System Status
         PID # 0x04 is supported - Calculated engine load [0 to 100%]
         PID # 0x06 is supported - Short term fuel trim - Bank 1 [-100 to 99.2%]
         PID # 0x07 is supported - Long term fuel trim - Bank 1 [-100 to 99.2%]
         PID # 0x0B is supported - Intake manifold absolute pressure [0 to 255 kPa]
         PID # 0x0C is supported - Engine speed [0 to 16383.75 rpm]
         PID # 0x0D is supported - Vehicle speed [0 to 255 km/h]
         PID # 0x0E is supported - Timing advance [-64 to 63.5 degrees before TDC
         PID # 0x11 is supported - Throttle position [0 to 100%]
         PID # 0x13 is supported - Oxygen sensors present (in 2 banks)
         PID # 0x15 is supported - Oxygen Sensor 2 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]
         PID # 0x1C is supported - OBD standards this vehicle conforms to [1 to 250]
         PID # 0x1F is supported - Run time since engine start [0 to 65535 s]
         PID # 0x20 is supported - PIDs supported [0x21-0x40]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x20 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x20: Service 0x20 - PIDs supported [0x21-0x40]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 41511  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x20 0x90 0x07 0xE0 0x11 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x20 - PIDs supported [0x21-0x40]
         PID # 0x21 is supported - Distance traveled with malfunction indicator lamp (MIL) on [0 to 65535 km]
         PID # 0x24 is supported - Oxygen Sensor 1 - Air-Fuel Equivalence Ratio  [0 to <2] & Voltage [0 to <8 V]
         PID # 0x2E is supported - Commanded evaporative purge [0 to 100%]
         PID # 0x2F is supported - Fuel Tank Level input [0 to 100%]
         PID # 0x30 is supported - Warm-ups since codes cleared [0 to 255]
         PID # 0x31 is supported - Distance traveled since codes cleared [0 to 65535 km]
         PID # 0x32 is supported - Evap. System Vapor Pressure [-8192 to 8191.75 Pa]
         PID # 0x33 is supported - Absolute Barometric Pressure [0 to 255 kPa]
         PID # 0x3C is supported - Catalyst Temperature: Bank 1, Sensor 1 [-40 to 6513.5 degrees C]
         PID # 0x40 is supported - PIDs supported [0x41-0x60]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x40 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x40: Service 0x40 - PIDs supported [0x41-0x60]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 26372  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x40 0xF2 0xC0 0x8C 0x01 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x40 - PIDs supported [0x41-0x60]
         PID # 0x41 is supported - Monitor status this drive cycle
         PID # 0x42 is supported - Control module voltage [0 to 65535 V]
         PID # 0x43 is supported - Absolute load value [0 to 27500%]
         PID # 0x44 is supported - Commanded Air-Fuel Equivalence Ratio [0 to <2]
         PID # 0x47 is supported - Absolute throttle position B [0 to 100%]
         PID # 0x49 is supported - Absolute throttle position D [0 to 100%]
         PID # 0x4A is supported - Absolute throttle position E [0 to 100%]
         PID # 0x51 is supported - Fuel Type
         PID # 0x55 is supported - Short term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]
         PID # 0x56 is supported - Long term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]
         PID # 0x60 is supported - PIDs supported [0x61-0x80]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x60 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x60: Service 0x60 - PIDs supported [0x61-0x80]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 10444  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x60 0x67 0x10 0x00 0x01 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x60 - PIDs supported [0x61-0x80]
         PID # 0x62 is supported - Actual engine - percent torque [-125 to 130%]
         PID # 0x63 is supported - Engine reference torque [0 to 65535 N-m]
         PID # 0x66 is supported - Mass air flow sensor [0 to 2047.96875 g/s]
         PID # 0x67 is supported - Engine coolant temperature [-40 to 215 degrees C]
         PID # 0x68 is supported - Intake air temperature sensor [-40 to 215 degrees C]
         PID # 0x6C is supported - Commanded throttle actuator control and relative throttle position
         PID # 0x80 is supported - PIDs supported [0x81-0xA0]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x80 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x80: Service 0x80 - PIDs supported [0x81-0xA0]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 60305  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x80 0x00 0x04 0x00 0x0F 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x80 - PIDs supported [0x81-0xA0]
         PID # 0x8E is supported - Engine Friction - Percent Torque [-125 to 130%]
         PID # 0x9D is supported - Engine Fuel Rate [g/s]
         PID # 0x9E is supported - Engine Exhaust Flow Rate [kg/h]
         PID # 0x9F is supported - Fuel System Percentage Use
         PID # 0xA0 is supported - PIDs supported [0xA1-0xC0]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0xA0 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0xA0: Service 0xA0 - PIDs supported [0xA1-0xC0]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 45632  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0xA0 0x04 0x00 0x00 0x00 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0xA0 - PIDs supported [0xA1-0xC0]
         PID # 0xA6 is supported - Odometer [0 to 429496729.5 km]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0xC0 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0xC0: Service 0xC0 - PIDs supported [0xC1-0xE0]


TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0xE0 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0xE0: Service 0xE0 - UNDOCUMENTED: 0xE0



CAN baudrate set to: 500000 (not scanning)

CONTROL MENU (send from the serial monitor command line, terminated with LF or CR/LF):

   A[*] : send CAN request for all supported service 0x01 (current data) PIDs
   B[+] : LED intensity NORMAL or BRIGHT (toggle between the two selections)
   D[+] : DEBUG CAN receive details (toggle between enable & disable)
   C[*] : send CAN CUSTOM command (specify 2-char hex SERVICE, 2-char hex PID, & optional (only for Service #2) 2-char hex FRAME)
   F[*] : send CAN request for all supported service 0x02 (freeze frame data) PIDs
   H[-] : HUNT for a matching CAN baudrate (scanning)
   K[*] : execute K-line 5-baud init sequence
   N[*] : NEXT (higher) CAN baudrate (not scanning)
   P[*] : PREVIOUS (lower) CAN baudrate (not scanning)
   Q[*] : send CAN QUERY for all supported service 0x01 PIDs
   W[*] : send CAN WAKEUP commands

   NOTE:   [*] indicates an immediate command
           [+] indicates a toggle that is currently enabled
           [-] indicates a toggle that is currently disabled


[ A: initiate send CAN all supported service 0x01 PIDs from the serial monitor command line ]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x01 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x01: Service 0x01 - Monitor status since DTCs cleared


RX: MBX: 99  LEN: 8  EXT: 1  TS:  1084  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x01 0x00 0x07 0xE5 0x04 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x01 - Monitor status since DTCs cleared
         PID # 0x01 - Monitor status since DTCs cleared
            MIL is OFF
            DTC count = 0x00
            Spark ignition monitors supported (e.g. Otto ot Wankel engines)
               Components Test  = available and complete
               Fuel System Test = available and complete
               Misfire Test     = available and complete
                  EGR and/or VVT System = available and complete
                  Oxygen Sensor Heater  = available and complete
                  Oxygen Sensor         = available and complete
                  A/C Refrigerant       = unavailable
                  Secondary Air System  = unavailable
                  Evaporative System    = available but incomplete
                  Heated Catalyst       = unavailable
                  Catalyst              = available and complete

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x03 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x03: Service 0x03 - Fuel System Status


RX: MBX: 99  LEN: 8  EXT: 1  TS: 50994  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x03 0x02 0x00 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x03 - Fuel System Status
         PID # 0x03 - Fuel System Status
            Fuel System #1 - closed loop, using oxygen sensor feedback to determine fuel mix
            Fuel System #2 - the motor is off

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x04 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x04: Service 0x04 - Calculated engine load [0 to 100%]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 35452  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x04 0x3F 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x04 - Calculated engine load [0 to 100%]
         PID # 0x04 - Calculated engine load [0 to 100%]
            Engine load (%) = 24.61

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x06 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x06: Service 0x06 - Short term fuel trim - Bank 1 [-100 to 99.2%]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 19918  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x06 0x88 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x06 - Short term fuel trim - Bank 1 [-100 to 99.2%]
         PID # 0x06 - Short term fuel trim - Bank 1 [-100 to 99.2%]
            Short term fuel trim - Bank 1 (%) = 6.25

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x07 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x07: Service 0x07 - Long term fuel trim - Bank 1 [-100 to 99.2%]


RX: MBX: 99  LEN: 8  EXT: 1  TS:  4383  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x07 0x83 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x07 - Long term fuel trim - Bank 1 [-100 to 99.2%]
         PID # 0x07 - Long term fuel trim - Bank 1 [-100 to 99.2%]
            Long term fuel trim - Bank 1 (%) = 2.34

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0B 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x0B: Service 0x0B - Intake manifold absolute pressure [0 to 255 kPa]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 54377  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0B 0x22 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x0B - Intake manifold absolute pressure [0 to 255 kPa]
         PID # 0x0B - Intake manifold absolute pressure [0 to 255 kPa]
            Intake manifold absolute pressure (kPa) = 34.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0C 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x0C: Service 0x0C - Engine speed [0 to 16383.75 rpm]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 38846  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x0C 0x0B 0xAC 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x0C - Engine speed [0 to 16383.75 rpm]
         PID # 0x0C - Engine speed [0 to 16383.75 rpm]
            Engine speed (rpm) = 747.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x0D: Service 0x0D - Vehicle speed [0 to 255 km/h]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 23305  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x0D - Vehicle speed [0 to 255 km/h]
         PID # 0x0D - Vehicle speed [0 to 255 km/h]
            Vehicle speed (km/h) = 0.00 (0.00 mph)

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0E 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x0E: Service 0x0E - Timing advance [-64 to 63.5 degrees before TDC


RX: MBX: 99  LEN: 8  EXT: 1  TS:  7769  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0E 0x8B 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x0E - Timing advance [-64 to 63.5 degrees before TDC
         PID # 0x0E - Timing advance [-64 to 63.5 degrees before TDC
            Timing advance (degrees before TDC) = 5.50

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x11 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x11: Service 0x11 - Throttle position [0 to 100%]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 57771  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x11 0x24 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x11 - Throttle position [0 to 100%]
         PID # 0x11 - Throttle position [0 to 100%]
            Throttle position (%) = 14.12

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x13 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x13: Service 0x13 - Oxygen sensors present (in 2 banks)


RX: MBX: 99  LEN: 8  EXT: 1  TS: 42234  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x13 0x03 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x13 - Oxygen sensors present (in 2 banks)
         PID # 0x13 - Oxygen sensors present (in 2 banks)
            Oxygen sensors - Bank 1, Sensor 1 is present
            Oxygen sensors - Bank 1, Sensor 2 is present
            Oxygen sensors - Bank 1, Sensor 3 is not present
            Oxygen sensors - Bank 1, Sensor 4 is not present
            Oxygen sensors - Bank 2, Sensor 1 is not present
            Oxygen sensors - Bank 2, Sensor 2 is not present
            Oxygen sensors - Bank 2, Sensor 3 is not present
            Oxygen sensors - Bank 2, Sensor 4 is not present

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x15 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x15: Service 0x15 - Oxygen Sensor 2 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 26697  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x15 0x9D 0xFF 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x15 - Oxygen Sensor 2 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]
         PID # 0x15 - Oxygen Sensor 2 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]
            Oxygen Sensor 2 - Voltage (V) = 0.79
            Oxygen Sensor 2 - is not used in trim calculation

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x1C 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x1C: Service 0x1C - OBD standards this vehicle conforms to [1 to 250]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 11261  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x1C 0x01 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x1C - OBD standards this vehicle conforms to [1 to 250]
         PID # 0x1C - OBD standards this vehicle conforms to [1 to 250]
            OBD standards this vehicle conforms to = OBD-II as defined by CARB

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x1F 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x1F: Service 0x1F - Run time since engine start [0 to 65535 s]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 61481  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x1F 0x00 0x0D 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x1F - Run time since engine start [0 to 65535 s]
         PID # 0x1F - Run time since engine start [0 to 65535 s]
            Run time since engine start (s) = 13.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x21 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x21: Service 0x21 - Distance traveled with malfunction indicator lamp (MIL) on [0 to 65535 km]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 45727  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x21 0x00 0x00 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x21 - Distance traveled with malfunction indicator lamp (MIL) on [0 to 65535 km]
         PID # 0x21 - Distance traveled with malfunction indicator lamp (MIL) on [0 to 65535 km]
            Distance traveled with malfunction indicator lamp (MIL) on (km) = 0.00 (0.00 miles)

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x24 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x24: Service 0x24 - Oxygen Sensor 1 - Air-Fuel Equivalence Ratio  [0 to <2] & Voltage [0 to <8 V]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 30189  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x24 0x81 0x78 0x4A 0x38 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x24 - Oxygen Sensor 1 - Air-Fuel Equivalence Ratio  [0 to <2] & Voltage [0 to <8 V]
         PID # 0x24 - Oxygen Sensor 1 - Air-Fuel Equivalence Ratio  [0 to <2] & Voltage [0 to <8 V]
            Oxygen Sensor 1 - Air-Fuel Equivalence Ratio = 1.01
                            - Voltage (V) = 2.32

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x2E 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x2E: Service 0x2E - Commanded evaporative purge [0 to 100%]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 14657  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x2E 0x00 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x2E - Commanded evaporative purge [0 to 100%]
         PID # 0x2E - Commanded evaporative purge [0 to 100%]
            Commanded evaporative purge (%) = 0.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x2F 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x2F: Service 0x2F - Fuel Tank Level input [0 to 100%]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 64916  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x2F 0xBF 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x2F - Fuel Tank Level input [0 to 100%]
         PID # 0x2F - Fuel Tank Level input [0 to 100%]
            Fuel Tank Level Input (%) = 74.90

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x30 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x30: Service 0x30 - Warm-ups since codes cleared [0 to 255]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 49251  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x30 0x05 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x30 - Warm-ups since codes cleared [0 to 255]
         PID # 0x30 - Warm-ups since codes cleared [0 to 255]
            Warm-ups since codes cleared = 5.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x31 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x31: Service 0x31 - Distance traveled since codes cleared [0 to 65535 km]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 34092  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x31 0x01 0x01 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x31 - Distance traveled since codes cleared [0 to 65535 km]
         PID # 0x31 - Distance traveled since codes cleared [0 to 65535 km]
            Distance traveled since codes cleared = 257.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x32 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x32: Service 0x32 - Evap. System Vapor Pressure [-8192 to 8191.75 Pa]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 17977  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x32 0xFE 0x8C 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x32 - Evap. System Vapor Pressure [-8192 to 8191.75 Pa]
         PID # 0x32 - Evap. System Vapor Pressure [-8192 to 8191.75 Pa]
            Evap. System Vapor Pressure (Pa) = -93.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x33 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x33: Service 0x33 - Absolute Barometric Pressure [0 to 255 kPa]


RX: MBX: 99  LEN: 8  EXT: 1  TS:  2805  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x33 0x62 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x33 - Absolute Barometric Pressure [0 to 255 kPa]
         PID # 0x33 - Absolute Barometric Pressure [0 to 255 kPa]
            Absolute Barometric Pressure (kPa) = 98.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x3C 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x3C: Service 0x3C - Catalyst Temperature: Bank 1, Sensor 1 [-40 to 6513.5 degrees C]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 52415  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x3C 0x04 0xF9 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x3C - Catalyst Temperature: Bank 1, Sensor 1 [-40 to 6513.5 degrees C]
         PID # 0x3C - Catalyst Temperature: Bank 1, Sensor 1 [-40 to 6513.5 degrees C]
            Catalyst Temperature: Bank 1, Sensor 1 (degrees C) = 87.30 (189.14 degrees F)

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x41 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x41: Service 0x41 - Monitor status this drive cycle


RX: MBX: 99  LEN: 8  EXT: 1  TS: 36884  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x41 0x00 0x27 0xE1 0xA5 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x41 - Monitor status this drive cycle
         PID # 0x41 - Monitor status this drive cycle
               Components Test  = available and complete
               Fuel System Test = available but incomplete
               Misfire Test     = available and complete
                  EGR and/or VVT System = available but incomplete
                  Oxygen Sensor Heater  = available and complete
                  Oxygen Sensor         = available but incomplete
                  A/C Refrigerant       = unavailable
                  Secondary Air System  = unavailable
                  Evaporative System    = unavailable
                  Heated Catalyst       = unavailable
                  Catalyst              = available but incomplete

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x42 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x42: Service 0x42 - Control module voltage [0 to 65535 V]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 21341  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x42 0x33 0xC9 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x42 - Control module voltage [0 to 65535 V]
         PID # 0x42 - Control module voltage [0 to 65535 V]
            Control Module voltage (V) = 13.26

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x43 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x43: Service 0x43 - Absolute load value [0 to 27500%]


RX: MBX: 99  LEN: 8  EXT: 1  TS:  5808  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x43 0x00 0x3C 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x43 - Absolute load value [0 to 27500%]
         PID # 0x43 - Absolute load value [0 to 27500%]
            Absolute load value (%) = 23.53

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x44 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x44: Service 0x44 - Commanded Air-Fuel Equivalence Ratio [0 to <2]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 55818  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x44 0x80 0x00 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x44 - Commanded Air-Fuel Equivalence Ratio [0 to <2]
         PID # 0x44 - Commanded Air-Fuel Equivalence Ratio [0 to <2]
            Commanded Air-Fuel Equivalence Ratio = 1.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x47 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x47: Service 0x47 - Absolute throttle position B [0 to 100%]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 40271  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x47 0x24 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x47 - Absolute throttle position B [0 to 100%]
         PID # 0x47 - Absolute throttle position B [0 to 100%]
            Absolute throttle position B (%) = 14.12

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x49 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x49: Service 0x49 - Absolute throttle position D [0 to 100%]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 24746  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x49 0x31 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x49 - Absolute throttle position D [0 to 100%]
         PID # 0x49 - Absolute throttle position D [0 to 100%]
            Absolute throttle position D (%) = 19.22

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x4A 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x4A: Service 0x4A - Absolute throttle position E [0 to 100%]


RX: MBX: 99  LEN: 8  EXT: 1  TS:  9250  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x4A 0x18 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x4A - Absolute throttle position E [0 to 100%]
         PID # 0x4A - Absolute throttle position E [0 to 100%]
            Absolute throttle position E (%) = 9.41

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x51 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x51: Service 0x51 - Fuel Type


RX: MBX: 99  LEN: 8  EXT: 1  TS: 59197  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x51 0x01 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x51 - Fuel Type
         PID # 0x51 - Fuel Type
            Fuel Type = Gasoline

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x55 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x55: Service 0x55 - Short term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 43740  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x55 0x80 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x55 - Short term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]
         PID # 0x55 - Short term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]
            Short term secondary oxygen sensor trim, bank 1 (%) = 0.00
            Short term secondary oxygen sensor trim, bank 3 (%) = -33.59

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x56 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x56: Service 0x56 - Long term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 28122  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x56 0x80 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x56 - Long term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]
         PID # 0x56 - Long term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]
            Long term secondary oxygen sensor trim, bank 1 (%) = 0.00
            Long term secondary oxygen sensor trim, bank 3 (%) = -33.59

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x62 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x62: Service 0x62 - Actual engine - percent torque [-125 to 130%]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 12584  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x62 0x95 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x62 - Actual engine - percent torque [-125 to 130%]
         PID # 0x62 - Actual engine - percent torque [-125 to 130%]
            Actual engine - percent torque (%) = 24.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x63 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x63: Service 0x63 - Engine reference torque [0 to 65535 N-m]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 62587  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x63 0x00 0x92 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x63 - Engine reference torque [0 to 65535 N-m]
         PID # 0x63 - Engine reference torque [0 to 65535 N-m]
            Engine reference torque (N-m) = 146.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x66 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x66: Service 0x66 - Mass air flow sensor [0 to 2047.96875 g/s]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 47055  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x07 0x41 0x66 0x01 0x00 0x66 0x00 0x00 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x66 - Mass air flow sensor [0 to 2047.96875 g/s]
         PID # 0x66 - Mass air flow sensor [0 to 2047.96875 g/s]
            Mass air flow sensor A (g/s) = 3.19
            Mass air flow sensor B is not supported

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x67 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x67: Service 0x67 - Engine coolant temperature [-40 to 215 degrees C]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 31514  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x05 0x41 0x67 0x03 0x6C 0x4D 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x67 - Engine coolant temperature [-40 to 215 degrees C]
         PID # 0x67 - Engine coolant temperature [-40 to 215 degrees C]
            Engine coolant temperature - Sensor 1 (degrees C) = 68.00 (154.40 degrees F)
            Engine coolant temperature - Sensor 2 (degrees C) = 68.00 (154.40 degrees F)

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x68 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x68: Service 0x68 - Intake air temperature sensor [-40 to 215 degrees C]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 15985  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x10 0x09 0x41 0x68 0x01 0x56 0x00 0x00 
      0x41: Service 0x68 - Intake air temperature sensor [-40 to 215 degrees C]
         PID # 0x68 - Intake air temperature sensor [-40 to 215 degrees C]
            Intake air temperature sensor #1 (degrees C) = 46.00 (114.80 degrees F)
            Intake air temperature sensor #2 is not supported

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x6C 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x6C: Service 0x6C - Commanded throttle actuator control and relative throttle position


RX: MBX: 99  LEN: 8  EXT: 1  TS:   444  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x07 0x41 0x6C 0x03 0x09 0x08 0x00 0x00 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x6C - Commanded throttle actuator control and relative throttle position
         PID # 0x6C - Commanded throttle actuator control and relative throttle position
            (detailed definition of content is unavailable)

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x8E 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x8E: Service 0x8E - Engine Friction - Percent Torque [-125 to 130%]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 50443  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x8E 0x8E 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x8E - Engine Friction - Percent Torque [-125 to 130%]
         PID # 0x8E - Engine Friction - Percent Torque [-125 to 130%]
            Engine Friction - Percent Torque (%) = 17.00

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x9D 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x9D: Service 0x9D - Engine Fuel Rate [g/s]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 34909  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x9D 0x00 0x0A 0x00 0x0A 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x9D - Engine Fuel Rate [g/s]
         PID # 0x9D - Engine Fuel Rate [g/s]
            (detailed definition of content is unavailable)

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x9E 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x9E: Service 0x9E - Engine Exhaust Flow Rate [kg/h]


RX: MBX: 99  LEN: 8  EXT: 1  TS: 19372  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x9E 0x00 0x3C 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x9E - Engine Exhaust Flow Rate [kg/h]
         PID # 0x9E - Engine Exhaust Flow Rate [kg/h]
            (detailed definition of content is unavailable)

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x9F 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x9F: Service 0x9F - Fuel System Percentage Use


RX: MBX: 99  LEN: 8  EXT: 1  TS:  4255  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x10 0x0B 0x41 0x9F 0x01 0xFF 0x00 0x00 
      0x41: Service 0x9F - Fuel System Percentage Use
         PID # 0x9F - Fuel System Percentage Use
            (detailed definition of content is unavailable)

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0xA6 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0xA6: Service 0xA6 - Odometer [0 to 429496729.5 km]



CAN baudrate set to: 500000 (not scanning)

CONTROL MENU (send from the serial monitor command line, terminated with LF or CR/LF):

   A[*] : send CAN request for all supported service 0x01 (current data) PIDs
   B[+] : LED intensity NORMAL or BRIGHT (toggle between the two selections)
   D[+] : DEBUG CAN receive details (toggle between enable & disable)
   C[*] : send CAN CUSTOM command (specify 2-char hex SERVICE, 2-char hex PID, & optional (only for Service #2) 2-char hex FRAME)
   F[*] : send CAN request for all supported service 0x02 (freeze frame data) PIDs
   H[-] : HUNT for a matching CAN baudrate (scanning)
   K[*] : execute K-line 5-baud init sequence
   N[*] : NEXT (higher) CAN baudrate (not scanning)
   P[*] : PREVIOUS (lower) CAN baudrate (not scanning)
   Q[*] : send CAN QUERY for all supported service 0x01 PIDs
   W[*] : send CAN WAKEUP commands

   NOTE:   [*] indicates an immediate command
           [+] indicates a toggle that is currently enabled
           [-] indicates a toggle that is currently disabled

RX: MBX: 99  LEN: 8  EXT: 1  TS: 53834  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0xA6 0x00 0x0D 0xBA 0x92 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0xA6 - Odometer [0 to 429496729.5 km]
         PID # 0xA6 - Odometer [0 to 429496729.5 km]
            Odometer (km) = 89973.00 (55906.63 miles)


[ F: initiate send CAN all supported service 0x02 PIDs from the serial monitor command line ]

TX: MBX: 00  LEN: 8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x01 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x01: Service 0x01 - Monitor status since DTCs cleared


RX: MBX: 99  LEN: 8  EXT: 1  TS: 45498  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x01 0x00 0x07 0xE5 0x04 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x01 - Monitor status since DTCs cleared
         PID # 0x01 - Monitor status since DTCs cleared
            MIL is OFF
            DTC count = 0x00
            Spark ignition monitors supported (e.g. Otto ot Wankel engines)
               Components Test  = available and complete
               Fuel System Test = available and complete
               Misfire Test     = available and complete
                  EGR and/or VVT System = available and complete
                  Oxygen Sensor Heater  = available and complete
                  Oxygen Sensor         = available and complete
                  A/C Refrigerant       = unavailable
                  Secondary Air System  = unavailable
                  Evaporative System    = available but incomplete
                  Heated Catalyst       = unavailable
                  Catalyst              = available and complete


[ No freeze frame data from any DTC errors saved / available ]


CAN baudrate set to: 500000 (not scanning)

CONTROL MENU (send from the serial monitor command line, terminated with LF or CR/LF):

   A[*] : send CAN request for all supported service 0x01 (current data) PIDs
   B[+] : LED intensity NORMAL or BRIGHT (toggle between the two selections)
   D[+] : DEBUG CAN receive details (toggle between enable & disable)
   C[*] : send CAN CUSTOM command (specify 2-char hex SERVICE, 2-char hex PID, & optional (only for Service #2) 2-char hex FRAME)
   F[*] : send CAN request for all supported service 0x02 (freeze frame data) PIDs
   H[-] : HUNT for a matching CAN baudrate (scanning)
   K[*] : execute K-line 5-baud init sequence
   N[*] : NEXT (higher) CAN baudrate (not scanning)
   P[*] : PREVIOUS (lower) CAN baudrate (not scanning)
   Q[*] : send CAN QUERY for all supported service 0x01 PIDs
   W[*] : send CAN WAKEUP commands

   NOTE:   [*] indicates an immediate command
           [+] indicates a toggle that is currently enabled
           [-] indicates a toggle that is currently disabled


Mark J Culross
KD5RXT
 
Also need to figure out how to induce a DTC so that I can fully test the capability to query the service 0x02 DTC freeze frame data . . .

Mark J Culross
KD5RXT

P.S. will also implement the service 0x03: show DTCs (not sure if this is already covered by the service 0x02 report or not) & the service 0x04: clear DTC & stored values
 
Also need to figure out how to induce a DTC so that I can fully test the capability to query the service 0x02 DTC freeze frame data . . .

Mark J Culross
KD5RXT

P.S. will also implement the service 0x03: show DTCs (not sure if this is already covered by the service 0x02 report or not) & the service 0x04: clear DTC & stored values

Nice work so far!
I'll be following for the DTC stuff.. You can unplug your IAT or O2 sensor and start up the car to get a powertrain DTC, should have no harm doing so on idle for a few seconds.
 
Latest additions:

- added menu selection "E": ability to request & process/parse service 0x09 PID 0x0A (ECU name - queries the list of supported service 0x09 PIDs first)
- added menu selection "V": ability to request & process/parse service 0x09 PID 0x02 (VIN - queries the list of supported service 0x09 PIDs first)
- added isotp (expanded) CAN messages (including the two new service 0x09 responses) & the associated flow-control when a 0x10 (expanded CAN) response is detected
- defined common processing of all PIDs for both standard CAN responses & expanded isotp responses

Here's today's updated version (20220926-2030) of my TeensyCANmonitor utility sketch for the Teensy 4.x:

Code:
//
//  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

I talked to my older son today & he is currently running his 2012 Honda Pilot with the MIL lit (he thinks it's just time for an oil change), so I may have the opportunity (if the MIL is indicating something a little more significant) to read a vehicle with an actual DTC to process/parse tomorrow !!

Mark J Culross
KD5RXT
 
Yet to be implemented:

- service 0x04 Clear DTCs (Clear Diagnostic Trouble codes & stored values)

Here's an example capture from my 2020 Honda Civic using today's updated version (20220926-2030) of my TeensyCANmonitor utility sketch for the Teensy 4.x:

Code:
=============================================
       Teensy CAN bus monitor utility
     version 1.0 dated 09/26/2022 @2030
designed & written by Mark J Culross (KD5RXT)
=============================================



Attempting to read/verify saved settings (10 values) from EEPROM...verified settings (10 values) in EEPROM are valid...


EEPROM USED: 10
EEPROM MAX ALLOWED: 4284


CAN baudrate set to: 500000 (not scanning)

CONTROL MENU (send from the serial monitor command line, terminated with LF or CR/LF):

   A[*] : send CAN request for all supported service 0x01 (current data) PIDs
   B[+] : LED intensity NORMAL or BRIGHT (toggle between the two selections)
   C[*] : send CAN CUSTOM command (specify 2-char hex SERVICE, 2-char hex PID, & optional (only for Service #2) 2-char hex FRAME)
   D[+] : DEBUG CAN receive details (toggle between enable & disable)
   E[*] : send CAN query for ECU name (Service 0x09 PID 0x0A)
   F[*] : send CAN request for all supported service 0x02 (freeze frame data) PIDs
   H[-] : HUNT for a matching CAN baudrate (scanning)
   K[*] : execute K-line 5-baud init sequence
   L[*} : send CAN query for LIST DTCs (Service 0x03)
   N[*] : NEXT (higher) CAN baudrate (not scanning)
   P[*] : PREVIOUS (lower) CAN baudrate (not scanning)
   Q[*] : send CAN QUERY for all supported service 0x01 PIDs
   V[*} : send CAN query for VIN (Service 0x09 PID 0x02)
   W[*] : send CAN WAKEUP commands

   NOTE:   [*] indicates an immediate command
           [+] indicates a toggle that is currently enabled
           [-] indicates a toggle that is currently disabled


[ Q: initiate send CAN query for all supported service 0x01 PIDs from the serial monitor command line ]

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x00 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x00: Service 0x00 - PIDs supported [0x01-0x20]


RX: (isotp)  LEN:   6  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x00 0xB6 0x3C 0xA8 0x13 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x00 - PIDs supported [0x01-0x20]
         PID # 0x01 is supported - Monitor status since DTCs cleared
         PID # 0x03 is supported - Fuel System Status
         PID # 0x04 is supported - Calculated engine load [0 to 100%]
         PID # 0x06 is supported - Short term fuel trim - Bank 1 [-100 to 99.2%]
         PID # 0x07 is supported - Long term fuel trim - Bank 1 [-100 to 99.2%]
         PID # 0x0B is supported - Intake manifold absolute pressure [0 to 255 kPa]
         PID # 0x0C is supported - Engine speed [0 to 16383.75 rpm]
         PID # 0x0D is supported - Vehicle speed [0 to 255 km/h]
         PID # 0x0E is supported - Timing advance [-64 to 63.5 degrees before TDC
         PID # 0x11 is supported - Throttle position [0 to 100%]
         PID # 0x13 is supported - Oxygen sensors present (in 2 banks)
         PID # 0x15 is supported - Oxygen Sensor 2 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]
         PID # 0x1C is supported - OBD standards this vehicle conforms to [1 to 250]
         PID # 0x1F is supported - Run time since engine start [0 to 65535 s]
         PID # 0x20 is supported - PIDs supported [0x21-0x40]

RX: MBX: 99  LEN:   8  EXT: 1  TS: 49189  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x00 0xB6 0x3C 0xA8 0x13 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x00 - PIDs supported [0x01-0x20]
         PID # 0x01 is supported - Monitor status since DTCs cleared
         PID # 0x03 is supported - Fuel System Status
         PID # 0x04 is supported - Calculated engine load [0 to 100%]
         PID # 0x06 is supported - Short term fuel trim - Bank 1 [-100 to 99.2%]
         PID # 0x07 is supported - Long term fuel trim - Bank 1 [-100 to 99.2%]
         PID # 0x0B is supported - Intake manifold absolute pressure [0 to 255 kPa]
         PID # 0x0C is supported - Engine speed [0 to 16383.75 rpm]
         PID # 0x0D is supported - Vehicle speed [0 to 255 km/h]
         PID # 0x0E is supported - Timing advance [-64 to 63.5 degrees before TDC
         PID # 0x11 is supported - Throttle position [0 to 100%]
         PID # 0x13 is supported - Oxygen sensors present (in 2 banks)
         PID # 0x15 is supported - Oxygen Sensor 2 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]
         PID # 0x1C is supported - OBD standards this vehicle conforms to [1 to 250]
         PID # 0x1F is supported - Run time since engine start [0 to 65535 s]
         PID # 0x20 is supported - PIDs supported [0x21-0x40]

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x20 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x20: Service 0x20 - PIDs supported [0x21-0x40]


RX: (isotp)  LEN:   6  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x20 0x90 0x07 0xE0 0x11 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x20 - PIDs supported [0x21-0x40]
         PID # 0x21 is supported - Distance traveled with malfunction indicator lamp (MIL) on [0 to 65535 km]
         PID # 0x24 is supported - Oxygen Sensor 1 - Air-Fuel Equivalence Ratio  [0 to <2] & Voltage [0 to <8 V]
         PID # 0x2E is supported - Commanded evaporative purge [0 to 100%]
         PID # 0x2F is supported - Fuel Tank Level input [0 to 100%]
         PID # 0x30 is supported - Warm-ups since codes cleared [0 to 255]
         PID # 0x31 is supported - Distance traveled since codes cleared [0 to 65535 km]
         PID # 0x32 is supported - Evap. System Vapor Pressure [-8192 to 8191.75 Pa]
         PID # 0x33 is supported - Absolute Barometric Pressure [0 to 255 kPa]
         PID # 0x3C is supported - Catalyst Temperature: Bank 1, Sensor 1 [-40 to 6513.5 degrees C]
         PID # 0x40 is supported - PIDs supported [0x41-0x60]

RX: MBX: 99  LEN:   8  EXT: 1  TS: 33411  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x20 0x90 0x07 0xE0 0x11 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x20 - PIDs supported [0x21-0x40]
         PID # 0x21 is supported - Distance traveled with malfunction indicator lamp (MIL) on [0 to 65535 km]
         PID # 0x24 is supported - Oxygen Sensor 1 - Air-Fuel Equivalence Ratio  [0 to <2] & Voltage [0 to <8 V]
         PID # 0x2E is supported - Commanded evaporative purge [0 to 100%]
         PID # 0x2F is supported - Fuel Tank Level input [0 to 100%]
         PID # 0x30 is supported - Warm-ups since codes cleared [0 to 255]
         PID # 0x31 is supported - Distance traveled since codes cleared [0 to 65535 km]
         PID # 0x32 is supported - Evap. System Vapor Pressure [-8192 to 8191.75 Pa]
         PID # 0x33 is supported - Absolute Barometric Pressure [0 to 255 kPa]
         PID # 0x3C is supported - Catalyst Temperature: Bank 1, Sensor 1 [-40 to 6513.5 degrees C]
         PID # 0x40 is supported - PIDs supported [0x41-0x60]

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x40 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x40: Service 0x40 - PIDs supported [0x41-0x60]


RX: (isotp)  LEN:   6  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x40 0xF2 0xC0 0x8C 0x01 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x40 - PIDs supported [0x41-0x60]
         PID # 0x41 is supported - Monitor status this drive cycle
         PID # 0x42 is supported - Control module voltage [0 to 65535 V]
         PID # 0x43 is supported - Absolute load value [0 to 27500%]
         PID # 0x44 is supported - Commanded Air-Fuel Equivalence Ratio [0 to <2]
         PID # 0x47 is supported - Absolute throttle position B [0 to 100%]
         PID # 0x49 is supported - Absolute throttle position D [0 to 100%]
         PID # 0x4A is supported - Absolute throttle position E [0 to 100%]
         PID # 0x51 is supported - Fuel Type
         PID # 0x55 is supported - Short term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]
         PID # 0x56 is supported - Long term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]
         PID # 0x60 is supported - PIDs supported [0x61-0x80]

RX: MBX: 99  LEN:   8  EXT: 1  TS: 17733  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x40 0xF2 0xC0 0x8C 0x01 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x40 - PIDs supported [0x41-0x60]
         PID # 0x41 is supported - Monitor status this drive cycle
         PID # 0x42 is supported - Control module voltage [0 to 65535 V]
         PID # 0x43 is supported - Absolute load value [0 to 27500%]
         PID # 0x44 is supported - Commanded Air-Fuel Equivalence Ratio [0 to <2]
         PID # 0x47 is supported - Absolute throttle position B [0 to 100%]
         PID # 0x49 is supported - Absolute throttle position D [0 to 100%]
         PID # 0x4A is supported - Absolute throttle position E [0 to 100%]
         PID # 0x51 is supported - Fuel Type
         PID # 0x55 is supported - Short term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]
         PID # 0x56 is supported - Long term secondary oxygen sensor trim - bank 1 & bank 3 [-100 to 99.2%]
         PID # 0x60 is supported - PIDs supported [0x61-0x80]

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x60 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x60: Service 0x60 - PIDs supported [0x61-0x80]


RX: (isotp)  LEN:   6  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x60 0x67 0x10 0x00 0x01 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x60 - PIDs supported [0x61-0x80]
         PID # 0x62 is supported - Actual engine - percent torque [-125 to 130%]
         PID # 0x63 is supported - Engine reference torque [0 to 65535 N-m]
         PID # 0x66 is supported - Mass air flow sensor [0 to 2047.96875 g/s]
         PID # 0x67 is supported - Engine coolant temperature [-40 to 215 degrees C]
         PID # 0x68 is supported - Intake air temperature sensor [-40 to 215 degrees C]
         PID # 0x6C is supported - Commanded throttle actuator control and relative throttle position
         PID # 0x80 is supported - PIDs supported [0x81-0xA0]

RX: MBX: 99  LEN:   8  EXT: 1  TS:  2200  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x60 0x67 0x10 0x00 0x01 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x60 - PIDs supported [0x61-0x80]
         PID # 0x62 is supported - Actual engine - percent torque [-125 to 130%]
         PID # 0x63 is supported - Engine reference torque [0 to 65535 N-m]
         PID # 0x66 is supported - Mass air flow sensor [0 to 2047.96875 g/s]
         PID # 0x67 is supported - Engine coolant temperature [-40 to 215 degrees C]
         PID # 0x68 is supported - Intake air temperature sensor [-40 to 215 degrees C]
         PID # 0x6C is supported - Commanded throttle actuator control and relative throttle position
         PID # 0x80 is supported - PIDs supported [0x81-0xA0]

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x80 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x80: Service 0x80 - PIDs supported [0x81-0xA0]


RX: (isotp)  LEN:   6  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x80 0x00 0x04 0x00 0x0F 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x80 - PIDs supported [0x81-0xA0]
         PID # 0x8E is supported - Engine Friction - Percent Torque [-125 to 130%]
         PID # 0x9D is supported - Engine Fuel Rate [g/s]
         PID # 0x9E is supported - Engine Exhaust Flow Rate [kg/h]
         PID # 0x9F is supported - Fuel System Percentage Use
         PID # 0xA0 is supported - PIDs supported [0xA1-0xC0]

RX: MBX: 99  LEN:   8  EXT: 1  TS: 52205  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x80 0x00 0x04 0x00 0x0F 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x80 - PIDs supported [0x81-0xA0]
         PID # 0x8E is supported - Engine Friction - Percent Torque [-125 to 130%]
         PID # 0x9D is supported - Engine Fuel Rate [g/s]
         PID # 0x9E is supported - Engine Exhaust Flow Rate [kg/h]
         PID # 0x9F is supported - Fuel System Percentage Use
         PID # 0xA0 is supported - PIDs supported [0xA1-0xC0]

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0xA0 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0xA0: Service 0xA0 - PIDs supported [0xA1-0xC0]


RX: (isotp)  LEN:   6  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0xA0 0x04 0x00 0x00 0x00 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0xA0 - PIDs supported [0xA1-0xC0]
         PID # 0xA6 is supported - Odometer [0 to 429496729.5 km]

RX: MBX: 99  LEN:   8  EXT: 1  TS: 36668  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0xA0 0x04 0x00 0x00 0x00 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0xA0 - PIDs supported [0xA1-0xC0]
         PID # 0xA6 is supported - Odometer [0 to 429496729.5 km]

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0xC0 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0xC0: Service 0xC0 - PIDs supported [0xC1-0xE0]


TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0xE0 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0xE0: Service 0xE0 - UNDOCUMENTED: 0xE0



CAN baudrate set to: 500000 (not scanning)

CONTROL MENU (send from the serial monitor command line, terminated with LF or CR/LF):

   A[*] : send CAN request for all supported service 0x01 (current data) PIDs
   B[+] : LED intensity NORMAL or BRIGHT (toggle between the two selections)
   C[*] : send CAN CUSTOM command (specify 2-char hex SERVICE, 2-char hex PID, & optional (only for Service #2) 2-char hex FRAME)
   D[+] : DEBUG CAN receive details (toggle between enable & disable)
   E[*] : send CAN query for ECU name (Service 0x09 PID 0x0A)
   F[*] : send CAN request for all supported service 0x02 (freeze frame data) PIDs
   H[-] : HUNT for a matching CAN baudrate (scanning)
   K[*] : execute K-line 5-baud init sequence
   L[*} : send CAN query for LIST DTCs (Service 0x03)
   N[*] : NEXT (higher) CAN baudrate (not scanning)
   P[*] : PREVIOUS (lower) CAN baudrate (not scanning)
   Q[*] : send CAN QUERY for all supported service 0x01 PIDs
   V[*} : send CAN query for VIN (Service 0x09 PID 0x02)
   W[*] : send CAN WAKEUP commands

   NOTE:   [*] indicates an immediate command
           [+] indicates a toggle that is currently enabled
           [-] indicates a toggle that is currently disabled


[ A: initiate send CAN all supported service 0x01 PIDs from the serial monitor command line ]

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x01 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x01: Service 0x01 - Monitor status since DTCs cleared


RX: (isotp)  LEN:   6  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x01 0x00 0x07 0xE5 0x00 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x01 - Monitor status since DTCs cleared
         PID # 0x01 - Monitor status since DTCs cleared
            MIL is OFF
            DTC count = 0x00
            Spark ignition monitors supported (e.g. Otto or Wankel engines)
               Components Test  = available and complete
               Fuel System Test = available and complete
               Misfire Test     = available and complete
                  EGR and/or VVT System = available and complete
                  Oxygen Sensor Heater  = available and complete
                  Oxygen Sensor         = available and complete
                  A/C Refrigerant       = unavailable
                  Secondary Air System  = unavailable
                  Evaporative System    = available and complete
                  Heated Catalyst       = unavailable
                  Catalyst              = available and complete

RX: MBX: 99  LEN:   8  EXT: 1  TS: 45724  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x01 0x00 0x07 0xE5 0x00 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x01 - Monitor status since DTCs cleared
         PID # 0x01 - Monitor status since DTCs cleared
            MIL is OFF
            DTC count = 0x00
            Spark ignition monitors supported (e.g. Otto or Wankel engines)
               Components Test  = available and complete
               Fuel System Test = available and complete
               Misfire Test     = available and complete
                  EGR and/or VVT System = available and complete
                  Oxygen Sensor Heater  = available and complete
                  Oxygen Sensor         = available and complete
                  A/C Refrigerant       = unavailable
                  Secondary Air System  = unavailable
                  Evaporative System    = available and complete
                  Heated Catalyst       = unavailable
                  Catalyst              = available and complete

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x03 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x03: Service 0x03 - Fuel System Status


RX: (isotp)  LEN:   4  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x03 0x02 0x00 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x03 - Fuel System Status
         PID # 0x03 - Fuel System Status
            Fuel System #1 - closed loop, using oxygen sensor feedback to determine fuel mix
            Fuel System #2 - the motor is off

RX: MBX: 99  LEN:   8  EXT: 1  TS: 29874  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x03 0x02 0x00 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x03 - Fuel System Status
         PID # 0x03 - Fuel System Status
            Fuel System #1 - closed loop, using oxygen sensor feedback to determine fuel mix
            Fuel System #2 - the motor is off

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x04 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x04: Service 0x04 - Calculated engine load [0 to 100%]


RX: (isotp)  LEN:   3  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x04 0x21 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x04 - Calculated engine load [0 to 100%]
         PID # 0x04 - Calculated engine load [0 to 100%]
            Engine load (%) = 12.94

RX: MBX: 99  LEN:   8  EXT: 1  TS: 14334  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x04 0x21 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x04 - Calculated engine load [0 to 100%]
         PID # 0x04 - Calculated engine load [0 to 100%]
            Engine load (%) = 12.94

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x06 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x06: Service 0x06 - Short term fuel trim - Bank 1 [-100 to 99.2%]


RX: (isotp)  LEN:   3  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x06 0x82 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x06 - Short term fuel trim - Bank 1 [-100 to 99.2%]
         PID # 0x06 - Short term fuel trim - Bank 1 [-100 to 99.2%]
            Short term fuel trim - Bank 1 (%) = 1.56

RX: MBX: 99  LEN:   8  EXT: 1  TS: 64336  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x06 0x82 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x06 - Short term fuel trim - Bank 1 [-100 to 99.2%]
         PID # 0x06 - Short term fuel trim - Bank 1 [-100 to 99.2%]
            Short term fuel trim - Bank 1 (%) = 1.56

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x07 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x07: Service 0x07 - Long term fuel trim - Bank 1 [-100 to 99.2%]


RX: (isotp)  LEN:   3  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x07 0x7E 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x07 - Long term fuel trim - Bank 1 [-100 to 99.2%]
         PID # 0x07 - Long term fuel trim - Bank 1 [-100 to 99.2%]
            Long term fuel trim - Bank 1 (%) = -1.56

RX: MBX: 99  LEN:   8  EXT: 1  TS: 48798  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x07 0x7E 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x07 - Long term fuel trim - Bank 1 [-100 to 99.2%]
         PID # 0x07 - Long term fuel trim - Bank 1 [-100 to 99.2%]
            Long term fuel trim - Bank 1 (%) = -1.56

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0B 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x0B: Service 0x0B - Intake manifold absolute pressure [0 to 255 kPa]


RX: (isotp)  LEN:   3  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x0B 0x1A 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x0B - Intake manifold absolute pressure [0 to 255 kPa]
         PID # 0x0B - Intake manifold absolute pressure [0 to 255 kPa]
            Intake manifold absolute pressure (kPa) = 26.00

RX: MBX: 99  LEN:   8  EXT: 1  TS: 33257  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0B 0x1A 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x0B - Intake manifold absolute pressure [0 to 255 kPa]
         PID # 0x0B - Intake manifold absolute pressure [0 to 255 kPa]
            Intake manifold absolute pressure (kPa) = 26.00

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0C 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x0C: Service 0x0C - Engine speed [0 to 16383.75 rpm]


RX: (isotp)  LEN:   4  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x0C 0x0B 0xAC 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x0C - Engine speed [0 to 16383.75 rpm]
         PID # 0x0C - Engine speed [0 to 16383.75 rpm]
            Engine speed (rpm) = 747.00

RX: MBX: 99  LEN:   8  EXT: 1  TS: 17726  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x0C 0x0B 0xAC 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x0C - Engine speed [0 to 16383.75 rpm]
         PID # 0x0C - Engine speed [0 to 16383.75 rpm]
            Engine speed (rpm) = 747.00

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0D 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x0D: Service 0x0D - Vehicle speed [0 to 255 km/h]


RX: (isotp)  LEN:   3  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x0D 0x00 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x0D - Vehicle speed [0 to 255 km/h]
         PID # 0x0D - Vehicle speed [0 to 255 km/h]
            Vehicle speed (km/h) = 0.00 (0.00 mph)

RX: MBX: 99  LEN:   8  EXT: 1  TS:  2185  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0D 0x00 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x0D - Vehicle speed [0 to 255 km/h]
         PID # 0x0D - Vehicle speed [0 to 255 km/h]
            Vehicle speed (km/h) = 0.00 (0.00 mph)

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x0E 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x0E: Service 0x0E - Timing advance [-64 to 63.5 degrees before TDC


RX: (isotp)  LEN:   3  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x0E 0x8D 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x0E - Timing advance [-64 to 63.5 degrees before TDC
         PID # 0x0E - Timing advance [-64 to 63.5 degrees before TDC
            Timing advance (degrees before TDC) = 6.50

RX: MBX: 99  LEN:   8  EXT: 1  TS: 52196  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x0E 0x8D 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x0E - Timing advance [-64 to 63.5 degrees before TDC
         PID # 0x0E - Timing advance [-64 to 63.5 degrees before TDC
            Timing advance (degrees before TDC) = 6.50

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x11 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x11: Service 0x11 - Throttle position [0 to 100%]


RX: (isotp)  LEN:   3  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x11 0x21 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x11 - Throttle position [0 to 100%]
         PID # 0x11 - Throttle position [0 to 100%]
            Throttle position (%) = 12.94

RX: MBX: 99  LEN:   8  EXT: 1  TS: 36650  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x11 0x21 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x11 - Throttle position [0 to 100%]
         PID # 0x11 - Throttle position [0 to 100%]
            Throttle position (%) = 12.94

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x13 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x13: Service 0x13 - Oxygen sensors present (in 2 banks)


RX: (isotp)  LEN:   3  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x13 0x03 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x13 - Oxygen sensors present (in 2 banks)
         PID # 0x13 - Oxygen sensors present (in 2 banks)
            Oxygen sensors - Bank 1, Sensor 1 is present
            Oxygen sensors - Bank 1, Sensor 2 is present
            Oxygen sensors - Bank 1, Sensor 3 is not present
            Oxygen sensors - Bank 1, Sensor 4 is not present
            Oxygen sensors - Bank 2, Sensor 1 is not present
            Oxygen sensors - Bank 2, Sensor 2 is not present
            Oxygen sensors - Bank 2, Sensor 3 is not present
            Oxygen sensors - Bank 2, Sensor 4 is not present

RX: MBX: 99  LEN:   8  EXT: 1  TS: 21135  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x13 0x03 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x13 - Oxygen sensors present (in 2 banks)
         PID # 0x13 - Oxygen sensors present (in 2 banks)
            Oxygen sensors - Bank 1, Sensor 1 is present
            Oxygen sensors - Bank 1, Sensor 2 is present
            Oxygen sensors - Bank 1, Sensor 3 is not present
            Oxygen sensors - Bank 1, Sensor 4 is not present
            Oxygen sensors - Bank 2, Sensor 1 is not present
            Oxygen sensors - Bank 2, Sensor 2 is not present
            Oxygen sensors - Bank 2, Sensor 3 is not present
            Oxygen sensors - Bank 2, Sensor 4 is not present

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x15 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x15: Service 0x15 - Oxygen Sensor 2 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]


RX: (isotp)  LEN:   4  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x15 0x7B 0x7E 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x15 - Oxygen Sensor 2 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]
         PID # 0x15 - Oxygen Sensor 2 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]
            Oxygen Sensor 2 - Voltage (V) = 0.62
            Oxygen Sensor 2 - Short term fuel trim (%) = -1.56

RX: MBX: 99  LEN:   8  EXT: 1  TS:  5579  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x15 0x7B 0x7E 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x15 - Oxygen Sensor 2 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]
         PID # 0x15 - Oxygen Sensor 2 - voltage [0 to 1.275 V] & short term fuel trim [-100 to 99.2%]
            Oxygen Sensor 2 - Voltage (V) = 0.62
            Oxygen Sensor 2 - Short term fuel trim (%) = -1.56

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x1C 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x1C: Service 0x1C - OBD standards this vehicle conforms to [1 to 250]


RX: (isotp)  LEN:   3  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x1C 0x01 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x1C - OBD standards this vehicle conforms to [1 to 250]
         PID # 0x1C - OBD standards this vehicle conforms to [1 to 250]
            OBD standards this vehicle conforms to = OBD-II as defined by CARB

RX: MBX: 99  LEN:   8  EXT: 1  TS: 55588  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x1C 0x01 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x1C - OBD standards this vehicle conforms to [1 to 250]
         PID # 0x1C - OBD standards this vehicle conforms to [1 to 250]
            OBD standards this vehicle conforms to = OBD-II as defined by CARB

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x1F 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x1F: Service 0x1F - Run time since engine start [0 to 65535 s]


RX: (isotp)  LEN:   4  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x1F 0x00 0xA5 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x1F - Run time since engine start [0 to 65535 s]
         PID # 0x1F - Run time since engine start [0 to 65535 s]
            Run time since engine start (s) = 165.00

RX: MBX: 99  LEN:   8  EXT: 1  TS: 40045  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x1F 0x00 0xA5 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x1F - Run time since engine start [0 to 65535 s]
         PID # 0x1F - Run time since engine start [0 to 65535 s]
            Run time since engine start (s) = 165.00

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x21 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x21: Service 0x21 - Distance traveled with malfunction indicator lamp (MIL) on [0 to 65535 km]


RX: (isotp)  LEN:   4  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x21 0x00 0x00 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x21 - Distance traveled with malfunction indicator lamp (MIL) on [0 to 65535 km]
         PID # 0x21 - Distance traveled with malfunction indicator lamp (MIL) on [0 to 65535 km]
            Distance traveled with malfunction indicator lamp (MIL) on (km) = 0.00 (0.00 miles)

RX: MBX: 99  LEN:   8  EXT: 1  TS: 24508  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x21 0x00 0x00 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x21 - Distance traveled with malfunction indicator lamp (MIL) on [0 to 65535 km]
         PID # 0x21 - Distance traveled with malfunction indicator lamp (MIL) on [0 to 65535 km]
            Distance traveled with malfunction indicator lamp (MIL) on (km) = 0.00 (0.00 miles)

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x24 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x24: Service 0x24 - Oxygen Sensor 1 - Air-Fuel Equivalence Ratio  [0 to <2] & Voltage [0 to <8 V]


RX: (isotp)  LEN:   6  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x24 0x80 0x0C 0x4A 0x24 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x24 - Oxygen Sensor 1 - Air-Fuel Equivalence Ratio  [0 to <2] & Voltage [0 to <8 V]
         PID # 0x24 - Oxygen Sensor 1 - Air-Fuel Equivalence Ratio  [0 to <2] & Voltage [0 to <8 V]
            Oxygen Sensor 1 - Air-Fuel Equivalence Ratio = 1.00
                            - Voltage (V) = 2.32

RX: MBX: 99  LEN:   8  EXT: 1  TS:  9006  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x06 0x41 0x24 0x80 0x0C 0x4A 0x24 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x24 - Oxygen Sensor 1 - Air-Fuel Equivalence Ratio  [0 to <2] & Voltage [0 to <8 V]
         PID # 0x24 - Oxygen Sensor 1 - Air-Fuel Equivalence Ratio  [0 to <2] & Voltage [0 to <8 V]
            Oxygen Sensor 1 - Air-Fuel Equivalence Ratio = 1.00
                            - Voltage (V) = 2.32

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x2E 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x2E: Service 0x2E - Commanded evaporative purge [0 to 100%]


RX: (isotp)  LEN:   3  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x2E 0x49 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x2E - Commanded evaporative purge [0 to 100%]
         PID # 0x2E - Commanded evaporative purge [0 to 100%]
            Commanded evaporative purge (%) = 28.63

RX: MBX: 99  LEN:   8  EXT: 1  TS: 59102  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x2E 0x49 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x2E - Commanded evaporative purge [0 to 100%]
         PID # 0x2E - Commanded evaporative purge [0 to 100%]
            Commanded evaporative purge (%) = 28.63

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x2F 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x2F: Service 0x2F - Fuel Tank Level input [0 to 100%]


RX: (isotp)  LEN:   3  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x2F 0x79 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x2F - Fuel Tank Level input [0 to 100%]
         PID # 0x2F - Fuel Tank Level input [0 to 100%]
            Fuel Tank Level Input (%) = 47.45

RX: MBX: 99  LEN:   8  EXT: 1  TS: 43432  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x2F 0x79 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x2F - Fuel Tank Level input [0 to 100%]
         PID # 0x2F - Fuel Tank Level input [0 to 100%]
            Fuel Tank Level Input (%) = 47.45

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x30 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x30: Service 0x30 - Warm-ups since codes cleared [0 to 255]


RX: (isotp)  LEN:   3  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x30 0x0D 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x30 - Warm-ups since codes cleared [0 to 255]
         PID # 0x30 - Warm-ups since codes cleared [0 to 255]
            Warm-ups since codes cleared = 13.00

RX: MBX: 99  LEN:   8  EXT: 1  TS: 27921  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x03 0x41 0x30 0x0D 0x55 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x30 - Warm-ups since codes cleared [0 to 255]
         PID # 0x30 - Warm-ups since codes cleared [0 to 255]
            Warm-ups since codes cleared = 13.00

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x31 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x31: Service 0x31 - Distance traveled since codes cleared [0 to 65535 km]


RX: (isotp)  LEN:   4  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x31 0x01 0x9E 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x31 - Distance traveled since codes cleared [0 to 65535 km]
         PID # 0x31 - Distance traveled since codes cleared [0 to 65535 km]
            Distance traveled since codes cleared = 414.00

RX: MBX: 99  LEN:   8  EXT: 1  TS: 12375  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x31 0x01 0x9E 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x31 - Distance traveled since codes cleared [0 to 65535 km]
         PID # 0x31 - Distance traveled since codes cleared [0 to 65535 km]
            Distance traveled since codes cleared = 414.00

TX: MBX: 00  LEN:   8  EXT: 1  TS:     0  ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x01 0x32 0x00 0x00 0x00 0x00 0x00 
      0x01: service 0x01 request (Show current data)
      0x32: Service 0x32 - Evap. System Vapor Pressure [-8192 to 8191.75 Pa]


RX: (isotp)  LEN:   4  EXT: 1             ID: 0x18DAF110              MSG: 0x41 0x32 0xFB 0x6E 
      0x41: service 0x01 (Show current data) request response - isotp frame
      0x41: PID 0x32 - Evap. System Vapor Pressure [-8192 to 8191.75 Pa]
         PID # 0x32 - Evap. System Vapor Pressure [-8192 to 8191.75 Pa]
            Evap. System Vapor Pressure (Pa) = -292.50

RX: MBX: 99  LEN:   8  EXT: 1  TS: 62513  ID: 0x18DAF110  OVERRUN: 0  MSG: 0x04 0x41 0x32 0xFB 0x6E 0x55 0x55 0x55 
      0x41: service 0x01 (Show current data) request response
      0x41: PID 0x32 - Evap. System Vapor Pressure [-8192 to 8191.75 Pa]
         PID # 0x32 - Evap. System Vapor Pressure [-8192 to 8191.75 Pa]
            Evap. System Vapor Pressure (P