├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── question.md ├── .gitignore ├── LICENSE.md ├── README.md ├── examples ├── Arduino_hardware_serial_test │ └── Arduino_hardware_serial_test.ino ├── Arduino_software_serial_test │ └── Arduino_software_serial_test.ino ├── ESP32_Bluetooth_Serial │ └── ESP32_Bluetooth_Serial.ino ├── ESP32_Bluetooth_multiple_pids │ └── ESP32_Bluetooth_multiple_pids.ino ├── ESP32_CheckPIDs_1_20 │ └── ESP32_CheckPIDs_1_20.ino ├── ESP32_Check_DTC │ └── ESP32_Check_DTC.ino ├── ESP32_Check_MIL │ └── ESP32_Check_MIL.ino ├── ESP32_Check_PID_Support │ └── ESP32_Check_PID_Support.ino ├── ESP32_Check_Voltage │ └── ESP32_Check_Voltage.ino ├── ESP32_CustomHeader │ └── ESP32_CustomHeader.ino ├── ESP32_CustomMultilinePID │ └── ESP32_CustomMultilinePID.ino ├── ESP32_Reset_DTC │ └── ESP32_Reset_DTC.ino ├── ESP32_Test_Functions │ └── ESP32_test_functions.ino ├── ESP32_WiFi │ └── ESP32_WiFi.ino ├── ESP32_dual_core │ └── ESP32_dual_core.ino ├── ESP32_test │ └── ESP32_test.ino ├── ESP8266_WiFi │ └── ESP8266_WiFi.ino ├── ESP8266_test │ └── ESP8266_test.ino └── multiple_pids │ └── multiple_pids.ino ├── library.properties ├── reference └── ELM327DS.pdf └── src ├── ELMduino.cpp └── ELMduino.h /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: PowerBroker2 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior. 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Equipment** 20 | Provide the type of microcontroller, OBD scanner, and bluetooth radio (if applicable) used. 21 | 22 | **Code** 23 | Provide all applicable code in tags. 24 | 25 | **Wiring** 26 | If applicable, add a straightforward schematic or short description of the wiring. 27 | 28 | **Screenshots** 29 | If applicable, add screenshots to help explain your problem. 30 | 31 | **Additional Context** 32 | Add any other context about the problem here. 33 | 34 | **Troubleshooting Connection Issues** 35 | - Try 38400 baud instead of 115200 36 | - If using an HC-05 for communication, [ensure it's configured properly](https://forum.arduino.cc/index.php?topic=600014.msg4159554#msg4159554) 37 | - Ensure the car is on and running during troubleshooting 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: PowerBroker2 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Please describe your hardware setup, code (in tags), and what question(s) you 4 | may have 5 | title: '' 6 | labels: question 7 | assignees: PowerBroker2 8 | 9 | --- 10 | 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .pio 3 | .vscode/.browse.c_cpp.db* 4 | .vscode/c_cpp_properties.json 5 | .vscode/launch.json 6 | .vscode/ipch 7 | 8 | # Prerequisites 9 | *.d 10 | 11 | # Compiled Object files 12 | *.slo 13 | *.lo 14 | *.o 15 | *.obj 16 | 17 | # Precompiled Headers 18 | *.gch 19 | *.pch 20 | 21 | # Compiled Dynamic libraries 22 | *.so 23 | *.dylib 24 | *.dll 25 | 26 | # Fortran module files 27 | *.mod 28 | *.smod 29 | 30 | # Compiled Static libraries 31 | *.lai 32 | *.la 33 | *.a 34 | *.lib 35 | 36 | # Executables 37 | *.exe 38 | *.out 39 | *.app 40 | include/README 41 | lib/README 42 | test/README 43 | .vscode/extensions.json 44 | platformio.ini 45 | .gitignore 46 | .vscode/settings.json 47 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 PB2 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ELMduino 2 | [![GitHub version](https://badge.fury.io/gh/PowerBroker2%2FELMduino.svg)](https://badge.fury.io/gh/PowerBroker2%2FELMduino) [![arduino-library-badge](https://www.ardu-badge.com/badge/ELMDuino.svg?)](https://www.ardu-badge.com/ELMDuino)

3 | This is a simple yet powerful library to effortlessly interface your Arduino with an ELM327 OBD-II scanner. With this library, you can query any and all OBD-II supported PIDs to collect a wide variety of car data (i.e. speed, rpm, engine temp, etc). Also, you can use ELMduino to view and clear your car's trouble codes - no need to go to AutoZone anymore! 4 | 5 | # Example Project: 6 | 7 | [![youtube](https://user-images.githubusercontent.com/20977405/102416634-2fa0ab80-3fc8-11eb-8baf-d24a6a27bd3f.PNG)](https://www.youtube.com/watch?v=JxBvukUipc4&feature=youtu.be) 8 | 9 | # Install: 10 | Install ELMduino using the Arduino IDE's Libraries Manager (search "ELMduino.h") 11 | 12 | # Available Features: 13 | See the contents of ELMduino.h for lists of supported OBD PID processing functions, OBD protocols, standard PIDs, and AT commands. The associated functions are documented in "doc strings" in ELMduino.cpp. 14 | 15 | # Note: 16 | If you're having difficulty in connecting/keeping connection to your ELM327, try using 38400 baud instead of 115200. If you still have trouble, try all other possible bauds. Lastly, if using BluetoothSerial on an ESP32, try using the ELM327's MAC address instead of the device name "OBDII" and [remove paired devices using this sketch](https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/examples/bt_remove_paired_devices/bt_remove_paired_devices.ino). 17 | 18 | # Concept of Execution 19 | The library is non-blocking. This means when you query a PID e.g. `myELM327.rpm()`, the code does not wait around for the response, which would block your other code in the main loop from executing. With ELMDuino, your main loop can continue do other tasks. To make this work, you need to repeatedly call the PID query function and check the non-blocking receive state (`myELM327.nb_rx_state`) until it is equal to `ELM_SUCCESS`. If the status is not `ELM_SUCCESS`, the library could still be waiting for a response to be received. This is indicated by `myELM327.nb_rx_state` being equal to `ELM_GETTING_MSG`. If the status is not equal to either of these values (ELM_SUCCESS or ELM_GETTING_MSG), it indicates an error has occurred. You can call `myELM327.printError()` to check what the problem was. See the simple example below which queries the engine speed in RPM. 20 | 21 | Just to be clear, do not try to query more than one PID at a time. You must wait for the current PID query to complete before starting the next one. 22 | 23 | # Example Code: 24 | ```C++ 25 | #include "ELMduino.h" 26 | 27 | 28 | 29 | 30 | #define ELM_PORT Serial1 31 | 32 | 33 | 34 | 35 | const bool DEBUG = true; 36 | const int TIMEOUT = 2000; 37 | const bool HALT_ON_FAIL = false; 38 | 39 | 40 | 41 | 42 | ELM327 myELM327; 43 | 44 | 45 | 46 | 47 | typedef enum { ENG_RPM, 48 | SPEED } obd_pid_states; 49 | obd_pid_states obd_state = ENG_RPM; 50 | 51 | float rpm = 0; 52 | float mph = 0; 53 | 54 | 55 | 56 | 57 | void setup() 58 | { 59 | Serial.begin(115200); 60 | ELM_PORT.begin(115200); 61 | 62 | Serial.println("Attempting to connect to ELM327..."); 63 | 64 | if (!myELM327.begin(ELM_PORT, DEBUG, TIMEOUT)) 65 | { 66 | Serial.println("Couldn't connect to OBD scanner"); 67 | 68 | if (HALT_ON_FAIL) 69 | while (1); 70 | } 71 | 72 | Serial.println("Connected to ELM327"); 73 | } 74 | 75 | 76 | 77 | 78 | void loop() 79 | { 80 | switch (obd_state) 81 | { 82 | case ENG_RPM: 83 | { 84 | rpm = myELM327.rpm(); 85 | 86 | if (myELM327.nb_rx_state == ELM_SUCCESS) 87 | { 88 | Serial.print("rpm: "); 89 | Serial.println(rpm); 90 | obd_state = SPEED; 91 | } 92 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 93 | { 94 | myELM327.printError(); 95 | obd_state = SPEED; 96 | } 97 | 98 | break; 99 | } 100 | 101 | case SPEED: 102 | { 103 | mph = myELM327.mph(); 104 | 105 | if (myELM327.nb_rx_state == ELM_SUCCESS) 106 | { 107 | Serial.print("mph: "); 108 | Serial.println(mph); 109 | obd_state = ENG_RPM; 110 | } 111 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 112 | { 113 | myELM327.printError(); 114 | obd_state = ENG_RPM; 115 | } 116 | 117 | break; 118 | } 119 | } 120 | } 121 | ``` 122 | -------------------------------------------------------------------------------- /examples/Arduino_hardware_serial_test/Arduino_hardware_serial_test.ino: -------------------------------------------------------------------------------- 1 | #include "ELMduino.h" 2 | 3 | 4 | #define ELM_PORT Serial1 5 | 6 | 7 | ELM327 myELM327; 8 | 9 | 10 | uint32_t rpm = 0; 11 | 12 | 13 | void setup() 14 | { 15 | #if LED_BUILTIN 16 | pinMode(LED_BUILTIN, OUTPUT); 17 | digitalWrite(LED_BUILTIN, LOW); 18 | #endif 19 | 20 | Serial.begin(115200); 21 | ELM_PORT.begin(115200); 22 | 23 | Serial.println("Attempting to connect to ELM327..."); 24 | 25 | if (!myELM327.begin(ELM_PORT, true, 2000)) 26 | { 27 | Serial.println("Couldn't connect to OBD scanner"); 28 | while (1); 29 | } 30 | 31 | Serial.println("Connected to ELM327"); 32 | } 33 | 34 | 35 | void loop() 36 | { 37 | float tempRPM = myELM327.rpm(); 38 | 39 | if (myELM327.nb_rx_state == ELM_SUCCESS) 40 | { 41 | rpm = (uint32_t)tempRPM; 42 | Serial.print("RPM: "); Serial.println(rpm); 43 | } 44 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 45 | myELM327.printError(); 46 | } 47 | -------------------------------------------------------------------------------- /examples/Arduino_software_serial_test/Arduino_software_serial_test.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ELMduino.h" 3 | 4 | 5 | SoftwareSerial mySerial(2, 3); // RX, TX 6 | #define ELM_PORT mySerial 7 | 8 | 9 | ELM327 myELM327; 10 | 11 | 12 | uint32_t rpm = 0; 13 | 14 | 15 | void setup() 16 | { 17 | #if LED_BUILTIN 18 | pinMode(LED_BUILTIN, OUTPUT); 19 | digitalWrite(LED_BUILTIN, LOW); 20 | #endif 21 | 22 | Serial.begin(115200); 23 | ELM_PORT.begin(115200); 24 | 25 | Serial.println("Attempting to connect to ELM327..."); 26 | 27 | if (!myELM327.begin(ELM_PORT, true, 2000)) 28 | { 29 | Serial.println("Couldn't connect to OBD scanner"); 30 | while (1); 31 | } 32 | 33 | Serial.println("Connected to ELM327"); 34 | } 35 | 36 | 37 | void loop() 38 | { 39 | float tempRPM = myELM327.rpm(); 40 | 41 | if (myELM327.nb_rx_state == ELM_SUCCESS) 42 | { 43 | rpm = (uint32_t)tempRPM; 44 | Serial.print("RPM: "); Serial.println(rpm); 45 | } 46 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 47 | myELM327.printError(); 48 | } 49 | -------------------------------------------------------------------------------- /examples/ESP32_Bluetooth_Serial/ESP32_Bluetooth_Serial.ino: -------------------------------------------------------------------------------- 1 | #include "BluetoothSerial.h" 2 | #include "ELMduino.h" 3 | 4 | 5 | BluetoothSerial SerialBT; 6 | #define ELM_PORT SerialBT 7 | #define DEBUG_PORT Serial 8 | 9 | 10 | ELM327 myELM327; 11 | 12 | 13 | uint32_t rpm = 0; 14 | 15 | 16 | void setup() 17 | { 18 | #if LED_BUILTIN 19 | pinMode(LED_BUILTIN, OUTPUT); 20 | digitalWrite(LED_BUILTIN, LOW); 21 | #endif 22 | 23 | DEBUG_PORT.begin(115200); 24 | //SerialBT.setPin("1234"); 25 | ELM_PORT.begin("ArduHUD", true); 26 | 27 | if (!ELM_PORT.connect("OBDII")) 28 | { 29 | DEBUG_PORT.println("Couldn't connect to OBD scanner - Phase 1"); 30 | while(1); 31 | } 32 | 33 | if (!myELM327.begin(ELM_PORT, true, 2000)) 34 | { 35 | Serial.println("Couldn't connect to OBD scanner - Phase 2"); 36 | while (1); 37 | } 38 | 39 | Serial.println("Connected to ELM327"); 40 | } 41 | 42 | 43 | void loop() 44 | { 45 | float tempRPM = myELM327.rpm(); 46 | 47 | if (myELM327.nb_rx_state == ELM_SUCCESS) 48 | { 49 | rpm = (uint32_t)tempRPM; 50 | Serial.print("RPM: "); Serial.println(rpm); 51 | } 52 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 53 | myELM327.printError(); 54 | } 55 | -------------------------------------------------------------------------------- /examples/ESP32_Bluetooth_multiple_pids/ESP32_Bluetooth_multiple_pids.ino: -------------------------------------------------------------------------------- 1 | #include "BluetoothSerial.h" 2 | #include "ELMduino.h" 3 | 4 | BluetoothSerial SerialBT; 5 | #define ELM_PORT SerialBT 6 | #define DEBUG_PORT Serial 7 | 8 | ELM327 myELM327; 9 | 10 | typedef enum { ENG_RPM, 11 | SPEED } obd_pid_states; 12 | obd_pid_states obd_state = ENG_RPM; 13 | 14 | float rpm = 0; 15 | float mph = 0; 16 | 17 | void setup() 18 | { 19 | DEBUG_PORT.begin(115200); 20 | // SerialBT.setPin("1234"); 21 | ELM_PORT.begin("ArduHUD", true); 22 | 23 | if (!ELM_PORT.connect("OBDII")) 24 | { 25 | DEBUG_PORT.println("Couldn't connect to OBD scanner - Phase 1"); 26 | while (1) 27 | ; 28 | } 29 | 30 | if (!myELM327.begin(ELM_PORT, true, 2000)) 31 | { 32 | DEBUG_PORT.println("Couldn't connect to OBD scanner - Phase 2"); 33 | while (1) 34 | ; 35 | } 36 | 37 | DEBUG_PORT.println("Connected to ELM327"); 38 | } 39 | 40 | void loop() 41 | { 42 | switch (obd_state) 43 | { 44 | case ENG_RPM: 45 | { 46 | rpm = myELM327.rpm(); 47 | 48 | if (myELM327.nb_rx_state == ELM_SUCCESS) 49 | { 50 | DEBUG_PORT.print("rpm: "); 51 | DEBUG_PORT.println(rpm); 52 | obd_state = SPEED; 53 | } 54 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 55 | { 56 | myELM327.printError(); 57 | obd_state = SPEED; 58 | } 59 | 60 | break; 61 | } 62 | 63 | case SPEED: 64 | { 65 | mph = myELM327.mph(); 66 | 67 | if (myELM327.nb_rx_state == ELM_SUCCESS) 68 | { 69 | DEBUG_PORT.print("mph: "); 70 | DEBUG_PORT.println(mph); 71 | obd_state = ENG_RPM; 72 | } 73 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 74 | { 75 | myELM327.printError(); 76 | obd_state = ENG_RPM; 77 | } 78 | 79 | break; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /examples/ESP32_CheckPIDs_1_20/ESP32_CheckPIDs_1_20.ino: -------------------------------------------------------------------------------- 1 | #include "BluetoothSerial.h" 2 | #include "ELMduino.h" 3 | 4 | BluetoothSerial SerialBT; 5 | #define ELM_PORT SerialBT 6 | #define DEBUG_PORT Serial 7 | 8 | ELM327 myELM327; 9 | 10 | uint32_t rpm = 0; 11 | 12 | void setup() 13 | { 14 | #if LED_BUILTIN 15 | pinMode(LED_BUILTIN, OUTPUT); 16 | digitalWrite(LED_BUILTIN, LOW); 17 | #endif 18 | 19 | DEBUG_PORT.begin(115200); 20 | // SerialBT.setPin("1234"); 21 | ELM_PORT.begin("ArduHUD", true); 22 | 23 | if (!ELM_PORT.connect("OBDII")) 24 | { 25 | DEBUG_PORT.println("Couldn't connect to OBD scanner - Phase 1"); 26 | while (1) 27 | ; 28 | } 29 | 30 | if (!myELM327.begin(ELM_PORT, true, 2000)) 31 | { 32 | Serial.println("Couldn't connect to OBD scanner - Phase 2"); 33 | while (1) 34 | ; 35 | } 36 | 37 | Serial.println("Connected to ELM327"); 38 | } 39 | 40 | void loop() 41 | { 42 | uint32_t pids = myELM327.supportedPIDs_1_20(); 43 | 44 | if (myELM327.nb_rx_state == ELM_SUCCESS) 45 | { 46 | Serial.print("Supported PIDS: "); Serial.println(pids); 47 | delay(10000); 48 | } 49 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 50 | { 51 | myELM327.printError(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/ESP32_Check_DTC/ESP32_Check_DTC.ino: -------------------------------------------------------------------------------- 1 | #include "ELMduino.h" 2 | #include 3 | 4 | #define ELM_PORT SerialBT 5 | #define DEBUG_PORT Serial 6 | 7 | typedef enum 8 | { MILSTATUS, 9 | DTCCODES 10 | } dtc_states; 11 | 12 | BluetoothSerial SerialBT; 13 | ELM327 myELM327; 14 | dtc_states dtc_state = MILSTATUS; 15 | uint8_t numCodes = 0; 16 | uint8_t milStatus =0; 17 | 18 | void setup() 19 | { 20 | DEBUG_PORT.begin(115200); 21 | ELM_PORT.begin("ArduHUD", true); 22 | ELM_PORT.setPin("1234"); 23 | 24 | DEBUG_PORT.println("Starting connection..."); 25 | if (!ELM_PORT.connect("ELMULATOR")) 26 | { 27 | DEBUG_PORT.println("Couldn't connect to OBD scanner - Phase 1"); 28 | while (1) 29 | ; 30 | } 31 | 32 | if (!myELM327.begin(ELM_PORT)) 33 | { 34 | DEBUG_PORT.println("ELM327 Couldn't connect to ECU - Phase 2"); 35 | while (1) 36 | ; 37 | } 38 | 39 | DEBUG_PORT.println("Connected to ELM327"); 40 | 41 | delay(1000); 42 | 43 | // Demonstration of calling currentDTCCodes in blocking (default) mode 44 | // This is the simplest use case. Just scan for codes without first 45 | // checking if MIL is on or how many codes are present. 46 | 47 | DEBUG_PORT.println("Performing DTC check in blocking mode..."); 48 | 49 | myELM327.currentDTCCodes(); 50 | if (myELM327.nb_rx_state == ELM_SUCCESS) 51 | { 52 | DEBUG_PORT.println("Current DTCs found: "); 53 | 54 | for (int i = 0; i < myELM327.DTC_Response.codesFound; i++) 55 | { 56 | DEBUG_PORT.println(myELM327.DTC_Response.codes[i]); 57 | } 58 | delay(10000); // Pause for 10 sec after successful fetch of DTC codes. 59 | } 60 | 61 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 62 | { 63 | myELM327.printError(); 64 | } 65 | } 66 | 67 | void loop() 68 | { 69 | // This is the typical use case: First check if any codes are present, and then make a request to get them. 70 | // monitorStatus() is a non-blocking call and must be called repeatedly until a response is found. 71 | switch (dtc_state) 72 | { 73 | case MILSTATUS: 74 | myELM327.monitorStatus(); // Gets the number of current DTC codes present 75 | 76 | if (myELM327.nb_rx_state == ELM_SUCCESS) 77 | { 78 | // We are only interested in the third byte of the response that 79 | // encodes the MIL status and number of codes present 80 | milStatus = (myELM327.responseByte_2 & 0x80); 81 | numCodes = (myELM327.responseByte_2 - 0x80); 82 | 83 | DEBUG_PORT.print("MIL (Check Engine Light) is: "); 84 | if (milStatus) 85 | { 86 | DEBUG_PORT.println("ON."); 87 | } 88 | else 89 | { 90 | DEBUG_PORT.println("OFF."); 91 | } 92 | 93 | DEBUG_PORT.print("Number of codes present: "); 94 | DEBUG_PORT.println(numCodes); 95 | dtc_state = DTCCODES; 96 | break; 97 | } 98 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 99 | { 100 | myELM327.printError(); 101 | dtc_state = DTCCODES; 102 | } 103 | break; 104 | 105 | case DTCCODES: // If current DTC codes were found in previous request, then retrieve the codes 106 | if (numCodes > 0) 107 | { 108 | myELM327.currentDTCCodes(false); // Call in NB mode 109 | 110 | if (myELM327.nb_rx_state == ELM_SUCCESS) 111 | { 112 | DEBUG_PORT.println("Current DTCs found: "); 113 | 114 | for (int i = 0; i < myELM327.DTC_Response.codesFound; i++) 115 | { 116 | DEBUG_PORT.println(myELM327.DTC_Response.codes[i]); 117 | } 118 | dtc_state = MILSTATUS; 119 | delay(10000); // Pause for 10 sec after successful fetch of DTC codes. 120 | } 121 | 122 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 123 | { 124 | myELM327.printError(); 125 | dtc_state = MILSTATUS; 126 | } 127 | } 128 | break; 129 | 130 | default: 131 | break; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /examples/ESP32_Check_MIL/ESP32_Check_MIL.ino: -------------------------------------------------------------------------------- 1 | #include "ELMduino.h" 2 | #include 3 | 4 | #define ELM_PORT SerialBT 5 | #define DEBUG_PORT Serial 6 | 7 | typedef enum 8 | { MILSTATUS, 9 | DTCCODES 10 | } dtc_states; 11 | 12 | BluetoothSerial SerialBT; 13 | ELM327 myELM327; 14 | dtc_states dtc_state = MILSTATUS; 15 | uint8_t numCodes = 0; 16 | uint8_t milStatus =0; 17 | 18 | // This example program demonstrates how to use ELMduino to check the MIL (Check Engine Light) 19 | // status and the number of current DTC codes that are present. 20 | 21 | void setup() 22 | { 23 | DEBUG_PORT.begin(115200); 24 | ELM_PORT.begin("ArduHUD", true); 25 | ELM_PORT.setPin("1234"); 26 | 27 | DEBUG_PORT.println("Starting connection..."); 28 | if (!ELM_PORT.connect("ELMULATOR")) 29 | { 30 | DEBUG_PORT.println("Couldn't connect to ELM327 - Phase 1"); 31 | while (1) 32 | ; 33 | } 34 | 35 | if (!myELM327.begin(ELM_PORT)) 36 | { 37 | DEBUG_PORT.println("ELM327 Couldn't connect to ECU - Phase 2"); 38 | while (1) 39 | ; 40 | } 41 | 42 | DEBUG_PORT.println("Connected to ELM327"); 43 | 44 | } 45 | 46 | void loop() 47 | { 48 | myELM327.monitorStatus(); // Gets the number of current DTC codes present 49 | 50 | if (myELM327.nb_rx_state == ELM_SUCCESS) 51 | { 52 | // We are only interested in the third byte of the response that 53 | // encodes the MIL status and number of codes present 54 | milStatus = (myELM327.responseByte_2 & 0x80); 55 | numCodes = (myELM327.responseByte_2 - 0x80); 56 | 57 | DEBUG_PORT.print("MIL (Check Engine Light) is: "); 58 | if (milStatus) 59 | { 60 | DEBUG_PORT.println("ON."); 61 | } 62 | else 63 | { 64 | DEBUG_PORT.println("OFF."); 65 | } 66 | 67 | DEBUG_PORT.print("Number of codes present: "); 68 | DEBUG_PORT.println(numCodes); 69 | 70 | DEBUG_PORT.println("Checking again in 10 seconds.\n\n"); 71 | delay(10000); // Wait 10 seconds before we query again. 72 | } 73 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 74 | { 75 | myELM327.printError(); 76 | DEBUG_PORT.println("Trying again in 10 seconds.\n\n"); 77 | delay(10000); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /examples/ESP32_Check_PID_Support/ESP32_Check_PID_Support.ino: -------------------------------------------------------------------------------- 1 | #include "BluetoothSerial.h" 2 | #include "ELMduino.h" 3 | 4 | BluetoothSerial SerialBT; 5 | #define ELM_PORT SerialBT 6 | #define DEBUG_PORT Serial 7 | 8 | ELM327 myELM327; 9 | 10 | uint32_t rpm = 0; 11 | 12 | void setup() 13 | { 14 | #if LED_BUILTIN 15 | pinMode(LED_BUILTIN, OUTPUT); 16 | digitalWrite(LED_BUILTIN, LOW); 17 | #endif 18 | 19 | DEBUG_PORT.begin(115200); 20 | // SerialBT.setPin("1234"); 21 | ELM_PORT.begin("ArduHUD", true); 22 | 23 | if (!ELM_PORT.connect("ELMULATOR")) 24 | { 25 | DEBUG_PORT.println("Couldn't connect to OBD scanner - Phase 1"); 26 | while (1) 27 | ; 28 | } 29 | 30 | if (!myELM327.begin(ELM_PORT, true, 2000)) 31 | { 32 | DEBUG_PORT.println("Couldn't connect to OBD scanner - Phase 2"); 33 | while (1) 34 | ; 35 | } 36 | 37 | Serial.println("Connected to ELM327"); 38 | } 39 | 40 | void loop() 41 | { 42 | uint8_t pid = ENGINE_RPM; 43 | bool pidOK = myELM327.isPidSupported(pid); 44 | if (myELM327.nb_rx_state == ELM_SUCCESS) 45 | { 46 | if(pidOK) { 47 | Serial.print("PID "); Serial.print(pid); Serial.println(" is supported"); 48 | } 49 | else 50 | { 51 | Serial.print("PID "); Serial.print(pid); Serial.println(" is not supported"); 52 | } 53 | delay(10000); 54 | } 55 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 56 | { 57 | myELM327.printError(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /examples/ESP32_Check_Voltage/ESP32_Check_Voltage.ino: -------------------------------------------------------------------------------- 1 | #include "BluetoothSerial.h" 2 | #include "ELMduino.h" 3 | 4 | BluetoothSerial SerialBT; 5 | #define ELM_PORT SerialBT 6 | #define DEBUG_PORT Serial 7 | 8 | ELM327 myELM327; 9 | 10 | void setup() 11 | { 12 | #if LED_BUILTIN 13 | pinMode(LED_BUILTIN, OUTPUT); 14 | digitalWrite(LED_BUILTIN, LOW); 15 | #endif 16 | 17 | DEBUG_PORT.begin(115200); 18 | // SerialBT.setPin("1234"); 19 | ELM_PORT.begin("ArduHUD", true); 20 | 21 | if (!ELM_PORT.connect("OBDII")) 22 | { 23 | DEBUG_PORT.println("Couldn't connect to OBD scanner - Phase 1"); 24 | while (1) 25 | ; 26 | } 27 | 28 | if (!myELM327.begin(ELM_PORT, true, 2000)) 29 | { 30 | Serial.println("Couldn't connect to OBD scanner - Phase 2"); 31 | while (1) 32 | ; 33 | } 34 | 35 | Serial.println("Connected to ELM327"); 36 | } 37 | 38 | void loop() 39 | { 40 | float volts = myELM327.batteryVoltage(); 41 | 42 | if (myELM327.nb_rx_state == ELM_SUCCESS) 43 | { 44 | Serial.print("Battery Voltage: "); 45 | Serial.println(volts); 46 | delay(10000); 47 | } 48 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 49 | { 50 | myELM327.printError(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/ESP32_CustomHeader/ESP32_CustomHeader.ino: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This example shows how to perform several opearations for using custom (non-OBD2 standard) 4 | queries. 5 | 6 | Custom Header 7 | We perform a query that requires a custom header to be sent first. The custom header in this 8 | case determines which ECU in the vehicle's system we want to query. 9 | 10 | Custom PID 11 | The query sent to the vehicle is also a custom (non OBD2 standard) PID using the 0x22 12 | (enhanced data) mode. 13 | 14 | Manually Processing Response 15 | In this example, we manually extract the data value from the query response and perform 16 | some post-processing to calculate the correct value. 17 | 18 | Managing Query State 19 | We also demonstrate managing the query state used by the loop() method. This is typically 20 | managed internally by ELMduino for standard PID methods. 21 | 22 | Customize all the things! 23 | The header value, PID, data value bytes and adjustment formula are generally unique for 24 | each different vehicle and PID. You will need to source the correct values for those for 25 | your specific vehicle. This example almost certainly will not work for you "as-is". 26 | 27 | */ 28 | 29 | #include "BluetoothSerial.h" 30 | #include "ELMduino.h" 31 | 32 | BluetoothSerial SerialBT; 33 | #define ELM_PORT SerialBT 34 | #define DEBUG_PORT Serial 35 | 36 | ELM327 myELM327; 37 | 38 | int nb_query_state = SEND_COMMAND; // Set the inital query state ready to send a command 39 | 40 | void setup() 41 | { 42 | DEBUG_PORT.begin(115200); 43 | // SerialBT.setPin("1234"); 44 | ELM_PORT.begin("ArduHUD", true); 45 | 46 | if (!ELM_PORT.connect("OBDII")) 47 | { 48 | DEBUG_PORT.println("Couldn't connect to ELM327 device."); 49 | while (1); 50 | } 51 | 52 | if (!myELM327.begin(ELM_PORT, true, 2000)) 53 | { 54 | Serial.println("ELM327 device couldn't connect to ECU."); 55 | while (1); 56 | } 57 | 58 | Serial.println("Connected to ELM327"); 59 | 60 | // Set a custom header using ATSH command. 61 | myELM327.sendCommand_Blocking("ATSH DA18F1"); 62 | } 63 | 64 | void loop() 65 | { 66 | if (nb_query_state == SEND_COMMAND) // We are ready to send a new command 67 | { 68 | myELM327.sendCommand("2204FE"); // Send the custom PID commnad 69 | nb_query_state = WAITING_RESP; // Set the query state so we are waiting for response 70 | } 71 | else if (nb_query_state == WAITING_RESP) // Our query has been sent, check for a response 72 | { 73 | myELM327.get_response(); // Each time through the loop we will check again 74 | } 75 | 76 | if (myELM327.nb_rx_state == ELM_SUCCESS) // Our response is fully received, let's get our data 77 | { 78 | int A = myELM327.payload[6]; // Parse the temp value A from the response 79 | float temperature = A - 40; // Apply the formula for the temperature 80 | Serial.println(temperature); // Print the adjusted value 81 | nb_query_state = SEND_COMMAND; // Reset the query state for the next command 82 | delay(5000); // Wait 5 seconds until we query again 83 | } 84 | 85 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 86 | { // If state == ELM_GETTING_MSG, response is not yet complete. Restart the loop. 87 | nb_query_state = SEND_COMMAND; // Reset the query state for the next command 88 | myELM327.printError(); 89 | delay(5000); // Wait 5 seconds until we query again 90 | } 91 | } -------------------------------------------------------------------------------- /examples/ESP32_CustomMultilinePID/ESP32_CustomMultilinePID.ino: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This example shows how to query and process OBD2 standard PIDs that 4 | return a multiline response and require custom processing. 5 | 6 | Processing Response with a custom calculation function 7 | In this example, we manually extract the data value from the query response and perform 8 | some post-processing to calculate the correct value. 9 | 10 | Managing Query State 11 | We also demonstrate managing the query state used by the loop() method. This is typically 12 | managed internally by ELMduino for standard PID methods. 13 | 14 | */ 15 | 16 | #include "BluetoothSerial.h" 17 | #include "ELMduino.h" 18 | 19 | BluetoothSerial SerialBT; 20 | #define ELM_PORT SerialBT 21 | #define DEBUG_PORT Serial 22 | 23 | ELM327 myELM327; 24 | 25 | int nb_query_state = SEND_COMMAND; // Set the inital query state ready to send a command 26 | 27 | 28 | // Applies calculation formula for PID 0x22 (Fuel Rail Pressure) 29 | double calcDPF() { 30 | double B = myELM327.payload[6]; 31 | double C = myELM327.payload[5]; 32 | return ((B * 256) + C) / 100; 33 | } 34 | 35 | void setup() 36 | { 37 | DEBUG_PORT.begin(115200); 38 | // SerialBT.setPin("1234"); 39 | ELM_PORT.begin("ArduHUD", true); 40 | 41 | if (!ELM_PORT.connect("OBDII")) 42 | { 43 | DEBUG_PORT.println("Couldn't connect to ELM327 device."); 44 | while (1); 45 | } 46 | 47 | if (!myELM327.begin(ELM_PORT, true, 2000)) 48 | { 49 | Serial.println("ELM327 device couldn't connect to ECU."); 50 | while (1); 51 | } 52 | 53 | Serial.println("Connected to ELM327"); 54 | 55 | } 56 | 57 | void loop() 58 | { 59 | if (nb_query_state == SEND_COMMAND) // We are ready to send a new command 60 | { 61 | myELM327.sendCommand("017A"); // Send the PID commnad 62 | nb_query_state = WAITING_RESP; // Set the query state so we are waiting for response 63 | } 64 | else if (nb_query_state == WAITING_RESP) // Our query has been sent, check for a response 65 | { 66 | myELM327.get_response(); // Each time through the loop we will check again 67 | } 68 | 69 | if (myELM327.nb_rx_state == ELM_SUCCESS) // Our response is fully received, let's get our data 70 | { 71 | double dpf = myELM327.conditionResponse(calcDPF); // Apply the formula for dpf 72 | Serial.println(dpf); // Print the adjusted value 73 | nb_query_state = SEND_COMMAND; // Reset the query state for the next command 74 | delay(5000); // Wait 5 seconds until we query again 75 | } 76 | 77 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 78 | { // If state == ELM_GETTING_MSG, response is not yet complete. Restart the loop. 79 | nb_query_state = SEND_COMMAND; // Reset the query state for the next command 80 | myELM327.printError(); 81 | delay(5000); // Wait 5 seconds until we query again 82 | } 83 | } -------------------------------------------------------------------------------- /examples/ESP32_Reset_DTC/ESP32_Reset_DTC.ino: -------------------------------------------------------------------------------- 1 | #include "BluetoothSerial.h" 2 | #include "ELMduino.h" 3 | 4 | BluetoothSerial SerialBT; 5 | #define ELM_PORT SerialBT 6 | #define DEBUG_PORT Serial 7 | 8 | ELM327 myELM327; 9 | 10 | void setup() 11 | { 12 | DEBUG_PORT.begin(115200); 13 | // SerialBT.setPin("1234"); 14 | ELM_PORT.begin("ESP32", true); 15 | 16 | if (!ELM_PORT.connect("OBDII")) 17 | { 18 | DEBUG_PORT.println("Couldn't connect to OBD scanner - Phase 1"); 19 | while (1) 20 | ; 21 | } 22 | 23 | if (!myELM327.begin(ELM_PORT, true, 2000)) 24 | { 25 | DEBUG_PORT.println("Couldn't connect to OBD scanner - Phase 2"); 26 | while (1) 27 | ; 28 | } 29 | 30 | DEBUG_PORT.println("Connected to ELM327"); 31 | DEBUG_PORT.println("Sending DTC reset command..."); 32 | 33 | if (myELM327.resetDTC()) 34 | { 35 | DEBUG_PORT.println("DTC reset successful."); 36 | } 37 | else 38 | { 39 | DEBUG_PORT.println("DTC reset failed."); 40 | } 41 | } 42 | 43 | void loop(){} 44 | -------------------------------------------------------------------------------- /examples/ESP32_Test_Functions/ESP32_test_functions.ino: -------------------------------------------------------------------------------- 1 | #include "BluetoothSerial.h" 2 | #include "ELMduino.h" 3 | 4 | BluetoothSerial SerialBT; 5 | #define ELM_PORT SerialBT 6 | #define DEBUG_PORT Serial 7 | 8 | ELM327 myELM327; 9 | uint8_t current_pid = 0; 10 | int nb_query_state = SEND_COMMAND; 11 | 12 | // Define a list of PIDs to test 13 | const uint16_t pidsToTest[] = { 14 | SUPPORTED_PIDS_1_20, // 0x00 15 | MONITOR_STATUS_SINCE_DTC_CLEARED, // 0x01 16 | FREEZE_DTC, // 0x02 17 | FUEL_SYSTEM_STATUS, // 0x03 18 | ENGINE_LOAD, // 0x04 19 | ENGINE_COOLANT_TEMP, // 0x05 20 | SHORT_TERM_FUEL_TRIM_BANK_1, // 0x06 21 | LONG_TERM_FUEL_TRIM_BANK_1, // 0x07 22 | SHORT_TERM_FUEL_TRIM_BANK_2, // 0x08 23 | LONG_TERM_FUEL_TRIM_BANK_2, // 0x09 24 | FUEL_PRESSURE, // 0x0A 25 | INTAKE_MANIFOLD_ABS_PRESSURE, // 0x0B 26 | ENGINE_RPM, // 0x0C 27 | VEHICLE_SPEED, // 0x0D 28 | TIMING_ADVANCE, // 0x0E 29 | INTAKE_AIR_TEMP, // 0x0F 30 | MAF_FLOW_RATE, // 0x10 31 | THROTTLE_POSITION, // 0x11 32 | COMMANDED_SECONDARY_AIR_STATUS, // 0x12 33 | OXYGEN_SENSORS_PRESENT_2_BANKS, // 0x13 34 | OXYGEN_SENSOR_1_A, // 0x14 35 | OXYGEN_SENSOR_2_A, // 0x15 36 | OXYGEN_SENSOR_3_A, // 0x16 37 | OXYGEN_SENSOR_4_A, // 0x17 38 | OXYGEN_SENSOR_5_A, // 0x18 39 | OXYGEN_SENSOR_6_A, // 0x19 40 | OXYGEN_SENSOR_7_A, // 0x1A 41 | OXYGEN_SENSOR_8_A, // 0x1B 42 | OBD_STANDARDS, // 0x1C 43 | OXYGEN_SENSORS_PRESENT_4_BANKS, // 0x1D 44 | AUX_INPUT_STATUS, // 0x1E 45 | RUN_TIME_SINCE_ENGINE_START, // 0x1F 46 | SUPPORTED_PIDS_21_40, // 0x20 47 | DISTANCE_TRAVELED_WITH_MIL_ON, // 0x21 48 | FUEL_RAIL_PRESSURE, // 0x22 49 | FUEL_RAIL_GUAGE_PRESSURE, // 0x23 50 | OXYGEN_SENSOR_1_B, // 0x24 51 | OXYGEN_SENSOR_2_B, // 0x25 52 | OXYGEN_SENSOR_3_B, // 0x26 53 | OXYGEN_SENSOR_4_B, // 0x27 54 | OXYGEN_SENSOR_5_B, // 0x28 55 | OXYGEN_SENSOR_6_B, // 0x29 56 | OXYGEN_SENSOR_7_B, // 0x2A 57 | OXYGEN_SENSOR_8_B, // 0x2B 58 | COMMANDED_EGR, // 0x2C 59 | EGR_ERROR, // 0x2D 60 | COMMANDED_EVAPORATIVE_PURGE, // 0x2E 61 | FUEL_TANK_LEVEL_INPUT, // 0x2F 62 | WARM_UPS_SINCE_CODES_CLEARED, // 0x30 - count 63 | DIST_TRAV_SINCE_CODES_CLEARED, // 0x31 - km 64 | EVAP_SYSTEM_VAPOR_PRESSURE, // 0x32 - Pa 65 | ABS_BAROMETRIC_PRESSURE, // 0x33 - kPa 66 | OXYGEN_SENSOR_1_C, // 0x34 - ratio mA 67 | OXYGEN_SENSOR_2_C, // 0x35 - ratio mA 68 | OXYGEN_SENSOR_3_C, // 0x36 - ratio mA 69 | OXYGEN_SENSOR_4_C, // 0x37 - ratio mA 70 | OXYGEN_SENSOR_5_C, // 0x38 - ratio mA 71 | OXYGEN_SENSOR_6_C, // 0x39 - ratio mA 72 | OXYGEN_SENSOR_7_C, // 0x3A - ratio mA 73 | OXYGEN_SENSOR_8_C, // 0x3B - ratio mA 74 | CATALYST_TEMP_BANK_1_SENSOR_1, // 0x3C - °C 75 | CATALYST_TEMP_BANK_2_SENSOR_1, // 0x3D - °C 76 | CATALYST_TEMP_BANK_1_SENSOR_2, // 0x3E - °C 77 | CATALYST_TEMP_BANK_2_SENSOR_2, // 0x3F - °C 78 | SUPPORTED_PIDS_41_60, // 0x40 - bit encoded 79 | MONITOR_STATUS_THIS_DRIVE_CYCLE, // 0x41 - bit encoded 80 | CONTROL_MODULE_VOLTAGE, // 0x42 - V 81 | ABS_LOAD_VALUE, // 0x43 - % 82 | FUEL_AIR_COMMANDED_EQUIV_RATIO, // 0x44 - ratio 83 | RELATIVE_THROTTLE_POSITION, // 0x45 - % 84 | AMBIENT_AIR_TEMP, // 0x46 - °C 85 | ABS_THROTTLE_POSITION_B, // 0x47 - % 86 | ABS_THROTTLE_POSITION_C, // 0x48 - % 87 | ABS_THROTTLE_POSITION_D, // 0x49 - % 88 | ABS_THROTTLE_POSITION_E, // 0x4A - % 89 | ABS_THROTTLE_POSITION_F, // 0x4B - % 90 | COMMANDED_THROTTLE_ACTUATOR, // 0x4C - % 91 | TIME_RUN_WITH_MIL_ON, // 0x4D - min 92 | TIME_SINCE_CODES_CLEARED, // 0x4E - min 93 | MAX_VALUES_EQUIV_V_I_PRESSURE, // 0x4F - ratio V mA kPa 94 | MAX_MAF_RATE, // 0x50 - g/s 95 | FUEL_TYPE, // 0x51 96 | ETHANOL_FUEL_PERCENT, // 0x52 97 | ABS_EVAP_SYS_VAPOR_PRESSURE, // 0x53 98 | EVAP_SYS_VAPOR_PRESSURE, // 0x54 99 | SHORT_TERM_SEC_OXY_SENS_TRIM_1_3, // 0x55 100 | LONG_TERM_SEC_OXY_SENS_TRIM_1_3, // 0x56 101 | SHORT_TERM_SEC_OXY_SENS_TRIM_2_4, // 0x57 102 | LONG_TERM_SEC_OXY_SENS_TRIM_2_4, // 0x58 103 | FUEL_RAIL_ABS_PRESSURE, // 0x59 104 | RELATIVE_ACCELERATOR_PEDAL_POS, // 0x5A 105 | HYBRID_BATTERY_REMAINING_LIFE, // 0x5B 106 | ENGINE_OIL_TEMP, // 0x5C 107 | FUEL_INJECTION_TIMING, // 0x5D 108 | ENGINE_FUEL_RATE, // 0x5E 109 | EMISSION_REQUIREMENTS, // 0x5F 110 | SUPPORTED_PIDS_61_80, // 0x60 111 | DEMANDED_ENGINE_PERCENT_TORQUE, // 0x61 112 | ACTUAL_ENGINE_TORQUE, // 0x62 113 | ENGINE_REFERENCE_TORQUE, // 0x63 114 | ENGINE_PERCENT_TORQUE_DATA // 0x64 115 | }; 116 | const uint8_t responseBytes[0xA9] = 117 | { 118 | 4, 4, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 119 | 4, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 120 | 4, 4, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 4, 4, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 1, 121 | 4, 1, 1, 2, 5, 2, 5, 3, 3, 7, 5, 5, 5, 11, 9, 3, 10, 6, 5, 5, 5, 7, 7, 5, 9, 9, 7, 7, 9, 1, 1, 13, 122 | 4, 41, 41, 9, 1, 10, 5, 5, 13, 41, 41, 7, 17, 1, 1, 7, 3, 5, 2, 3, 12, 9, 9, 6, 4, 17, 4, 2, 9 123 | }; 124 | 125 | void setup() 126 | { 127 | DEBUG_PORT.begin(115200); 128 | // SerialBT.setPin("1234"); 129 | ELM_PORT.begin("ArduHUD", true); 130 | 131 | if (!ELM_PORT.connect("OBDII")) 132 | { 133 | DEBUG_PORT.println("Couldn't connect to OBD scanner - Phase 1"); 134 | while (1) 135 | ; 136 | } 137 | 138 | if (!myELM327.begin(ELM_PORT, true, 2000)) 139 | { 140 | DEBUG_PORT.println("Couldn't connect to OBD scanner - Phase 2"); 141 | while (1) 142 | ; 143 | } 144 | 145 | DEBUG_PORT.println("Connected to ELM327"); 146 | } 147 | 148 | 149 | 150 | void loop() { 151 | if (current_pid > (sizeof(pidsToTest)/sizeof(uint16_t)) - 1) 152 | { 153 | current_pid = 0; 154 | } 155 | uint16_t pid = pidsToTest[current_pid]; 156 | double result = myELM327.processPID(0x01, pid, 1, responseBytes[current_pid], 1, 0); 157 | 158 | if (myELM327.nb_rx_state == ELM_SUCCESS) // Our response is fully received, let's get our data 159 | { 160 | nb_query_state = SEND_COMMAND; 161 | DEBUG_PORT.print("Result: "); 162 | DEBUG_PORT.println(result); 163 | current_pid++; // Reset the query state for the next command 164 | delay(500); // Wait 0.5 seconds until we query again 165 | } 166 | 167 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 168 | { // If state == ELM_GETTING_MSG, response is not yet complete. Restart the loop. 169 | nb_query_state = SEND_COMMAND; // Reset the query state for the next command 170 | myELM327.printError(); 171 | delay(500); // Wait 0.5 seconds until we query again 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /examples/ESP32_WiFi/ESP32_WiFi.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ELMduino.h" 3 | 4 | 5 | const char* ssid = "WiFi_OBDII"; 6 | const char* password = "your-password"; 7 | 8 | 9 | //IP Adress of your ELM327 Dongle 10 | IPAddress server(192, 168, 0, 10); 11 | WiFiClient client; 12 | ELM327 myELM327; 13 | 14 | 15 | uint32_t rpm = 0; 16 | 17 | 18 | void setup() 19 | { 20 | Serial.begin(115200); 21 | 22 | // Connecting to ELM327 WiFi 23 | Serial.print("Connecting to "); 24 | Serial.println(ssid); 25 | 26 | WiFi.mode(WIFI_AP); 27 | WiFi.begin(ssid); 28 | // WiFi.begin(ssid, password); //Use this line if your ELM327 has a password protected WiFi 29 | 30 | while (WiFi.status() != WL_CONNECTED) 31 | { 32 | delay(500); 33 | Serial.print("."); 34 | } 35 | 36 | Serial.println(""); 37 | Serial.println("Connected to Wifi"); 38 | Serial.println("IP address: "); 39 | Serial.println(WiFi.localIP()); 40 | 41 | if (client.connect(server, 35000)) 42 | Serial.println("connected"); 43 | else 44 | { 45 | Serial.println("connection failed"); 46 | while(1); 47 | } 48 | 49 | myELM327.begin(client, true, 2000); 50 | } 51 | 52 | 53 | void loop() 54 | { 55 | float tempRPM = myELM327.rpm(); 56 | 57 | if (myELM327.nb_rx_state == ELM_SUCCESS) 58 | { 59 | rpm = (uint32_t)tempRPM; 60 | Serial.print("RPM: "); Serial.println(rpm); 61 | } 62 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 63 | myELM327.printError(); 64 | } 65 | -------------------------------------------------------------------------------- /examples/ESP32_dual_core/ESP32_dual_core.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | const uint8_t led = 2; 6 | 7 | ELM327 obd; 8 | BluetoothSerial SerialBT; 9 | TaskHandle_t rpm_task; 10 | TaskHandle_t led_task; 11 | 12 | std::atomic rpm{0}; 13 | 14 | void setup(void) 15 | { 16 | Serial.begin(115200); 17 | 18 | connect_to_obd(); 19 | 20 | pinMode(led, OUTPUT); 21 | 22 | // Do blocking OBD call on one core 23 | xTaskCreatePinnedToCore( 24 | get_rpm_task, // Task function. 25 | "Task1", // Name of task. 26 | 10000, // Stack size of task 27 | NULL, // Parameter of the task 28 | 0, // Priority of the task 29 | &rpm_task, // Task handle to keep track of created task 30 | 0); // Pin task to core 0 31 | 32 | // Do timing specific work on another core 33 | xTaskCreatePinnedToCore( 34 | flash_led_task, // Task function. 35 | "Task2", // Name of task. 36 | 10000, // Stack size of task 37 | NULL, // Parameter of the task 38 | 0, // Priority of the task 39 | &led_task, // Task handle to keep track of created task 40 | 1); // Pin task to core 1 41 | 42 | delay(500); 43 | } 44 | 45 | void loop() 46 | { 47 | vTaskDelete(NULL); 48 | } 49 | 50 | void flash_led_task(void* parameters) 51 | { 52 | static unsigned long current_time{0}; 53 | static unsigned long previous_flash_time{0}; 54 | static const int flash_interval{75}; 55 | static bool is_on = false; 56 | 57 | for (;;) 58 | { 59 | current_time = millis(); 60 | 61 | if (rpm > 2500) 62 | { 63 | if (current_time - previous_flash_time >= flash_interval) 64 | { 65 | if (is_on) 66 | { 67 | digitalWrite(led, LOW); 68 | } 69 | else 70 | { 71 | digitalWrite(led, HIGH); 72 | } 73 | 74 | previous_flash_time = current_time; 75 | is_on = !is_on; 76 | } 77 | } 78 | else 79 | { 80 | is_on = false; 81 | digitalWrite(led, LOW); 82 | } 83 | 84 | // Sadly you need this or you will starve the IDLE task 85 | delay(1); 86 | } 87 | } 88 | 89 | void get_rpm_task(void* parameters) 90 | { 91 | for (;;) 92 | { 93 | read_rpm(); 94 | // Sadly you need this or you will starve the IDLE task 95 | delay(1); 96 | } 97 | } 98 | 99 | void connect_to_obd() 100 | { 101 | SerialBT.begin("led_flasher", true); 102 | 103 | bool connected = SerialBT.connect("OBDII"); 104 | 105 | if(connected) 106 | { 107 | Serial.println("Connected Succesfully!"); 108 | } 109 | else 110 | { 111 | while(!SerialBT.connected(10000)) 112 | { 113 | Serial.println("Failed to connect. Make sure remote device is available and in range, then restart app."); 114 | } 115 | } 116 | 117 | while(!obd.begin(SerialBT, true, 2000)) 118 | { 119 | Serial.println("Couldn't connect to OBD scanner"); 120 | } 121 | 122 | Serial.println("Connected to ELM327"); 123 | } 124 | 125 | void read_rpm() 126 | { 127 | static unsigned long current_time{0}; 128 | static unsigned long previous_rpm_time{0}; 129 | static const int rpm_interval{112}; 130 | 131 | current_time = millis(); 132 | 133 | if (current_time - previous_rpm_time >= rpm_interval) 134 | { 135 | rpm = static_cast(obd.rpm()); 136 | Serial.print("RPM: "); Serial.println(rpm); 137 | previous_rpm_time = current_time; 138 | 139 | if ((obd.nb_rx_state != ELM_GETTING_MSG && obd.nb_rx_state != ELM_SUCCESS) 140 | { 141 | obd.printError(); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /examples/ESP32_test/ESP32_test.ino: -------------------------------------------------------------------------------- 1 | /* 2 | To test connection, type the following into the serial monitor: 3 | AT Z 4 | AT E0 5 | AT S0 6 | AT AL 7 | AT TP A0 8 | */ 9 | 10 | #include "BluetoothSerial.h" 11 | 12 | 13 | BluetoothSerial SerialBT; 14 | 15 | 16 | #define DEBUG_PORT Serial 17 | #define ELM_PORT SerialBT 18 | 19 | 20 | void setup() 21 | { 22 | pinMode(LED_BUILTIN, OUTPUT); 23 | digitalWrite(LED_BUILTIN, HIGH); 24 | 25 | DEBUG_PORT.begin(115200); 26 | ELM_PORT.begin("ESP32test", true); 27 | //ELM_PORT.setPin("1234"); 28 | 29 | DEBUG_PORT.println("Attempting to connect to ELM327..."); 30 | 31 | if (!ELM_PORT.connect("OBDII")) 32 | { 33 | DEBUG_PORT.println("Couldn't connect to OBD scanner"); 34 | while(1); 35 | } 36 | 37 | DEBUG_PORT.println("Connected to ELM327"); 38 | DEBUG_PORT.println("Ensure your serial monitor line ending is set to 'Carriage Return'"); 39 | DEBUG_PORT.println("Type and send commands/queries to your ELM327 through the serial monitor"); 40 | DEBUG_PORT.println(); 41 | } 42 | 43 | 44 | void loop() 45 | { 46 | if(DEBUG_PORT.available()) 47 | { 48 | char c = DEBUG_PORT.read(); 49 | 50 | DEBUG_PORT.write(c); 51 | ELM_PORT.write(c); 52 | } 53 | 54 | if(ELM_PORT.available()) 55 | { 56 | char c = ELM_PORT.read(); 57 | 58 | if(c == '>') 59 | DEBUG_PORT.println(); 60 | 61 | DEBUG_PORT.write(c); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /examples/ESP8266_WiFi/ESP8266_WiFi.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ELMduino.h" 3 | 4 | 5 | const char* ssid = "WiFi_OBDII"; 6 | const char* password = "your-password"; 7 | 8 | 9 | //IP Adress of your ELM327 Dongle 10 | IPAddress server(192, 168, 0, 10); 11 | WiFiClient client; 12 | ELM327 myELM327; 13 | 14 | 15 | uint32_t rpm = 0; 16 | 17 | 18 | void setup() 19 | { 20 | Serial.begin(115200); 21 | 22 | // Connecting to ELM327 WiFi 23 | Serial.print("Connecting to "); 24 | Serial.println(ssid); 25 | 26 | WiFi.mode(WIFI_AP); 27 | WiFi.begin(ssid); 28 | // WiFi.begin(ssid, password); //Use this line if your ELM327 has a password protected WiFi 29 | 30 | while (WiFi.status() != WL_CONNECTED) 31 | { 32 | delay(500); 33 | Serial.print("."); 34 | } 35 | 36 | Serial.println(""); 37 | Serial.println("Connected to Wifi"); 38 | Serial.println("IP address: "); 39 | Serial.println(WiFi.localIP()); 40 | 41 | if (client.connect(server, 35000)) 42 | Serial.println("connected"); 43 | else 44 | { 45 | Serial.println("connection failed"); 46 | ESP.reset(); 47 | } 48 | 49 | myELM327.begin(client, true, 2000); 50 | } 51 | 52 | 53 | void loop() 54 | { 55 | float tempRPM = myELM327.rpm(); 56 | 57 | if (myELM327.nb_rx_state == ELM_SUCCESS) 58 | { 59 | rpm = (uint32_t)tempRPM; 60 | Serial.print("RPM: "); Serial.println(rpm); 61 | } 62 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 63 | myELM327.printError(); 64 | } 65 | -------------------------------------------------------------------------------- /examples/ESP8266_test/ESP8266_test.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ELMduino.h" 3 | 4 | 5 | const char* ssid = "WiFi_OBDII"; 6 | const char* password = "your-password"; 7 | 8 | 9 | //IP Adress of your ELM327 Dongle 10 | IPAddress server(192, 168, 0, 10); 11 | WiFiClient client; 12 | ELM327 myELM327; 13 | 14 | 15 | uint32_t rpm = 0; 16 | 17 | 18 | void setup() 19 | { 20 | Serial.begin(115200); 21 | 22 | // Connecting to ELM327 WiFi 23 | Serial.print("Connecting to "); 24 | Serial.println(ssid); 25 | 26 | WiFi.mode(WIFI_STA); 27 | WiFi.begin(ssid); 28 | // WiFi.begin(ssid, password); //Use this line if your ELM327 has a password protected WiFi 29 | 30 | while (WiFi.status() != WL_CONNECTED) 31 | { 32 | delay(500); 33 | Serial.print("."); 34 | } 35 | 36 | Serial.println(""); 37 | Serial.println("Connected to Wifi"); 38 | Serial.println("IP address: "); 39 | Serial.println(WiFi.localIP()); 40 | 41 | if (client.connect(server, 35000)) 42 | Serial.println("connected"); 43 | else 44 | { 45 | Serial.println("connection failed"); 46 | ESP.reset(); 47 | } 48 | 49 | myELM327.begin(client, true, 2000); 50 | 51 | Serial.println("Connected to ELM327"); 52 | Serial.println("Ensure your serial monitor line ending is set to 'Carriage Return'"); 53 | Serial.println("Type and send commands/queries to your ELM327 through the serial monitor"); 54 | Serial.println(); 55 | } 56 | 57 | 58 | void loop() 59 | { 60 | if(Serial.available()) 61 | { 62 | char c = Serial.read(); 63 | 64 | Serial.write(c); 65 | client.write(c); 66 | } 67 | 68 | if(client.available()) 69 | { 70 | char c = client.read(); 71 | 72 | if(c == '>') 73 | Serial.println(); 74 | 75 | Serial.write(c); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /examples/multiple_pids/multiple_pids.ino: -------------------------------------------------------------------------------- 1 | #include "ELMduino.h" 2 | 3 | 4 | 5 | 6 | #define ELM_PORT Serial1 7 | 8 | 9 | 10 | 11 | const bool DEBUG = true; 12 | const int TIMEOUT = 2000; 13 | const bool HALT_ON_FAIL = false; 14 | 15 | 16 | 17 | 18 | ELM327 myELM327; 19 | 20 | 21 | 22 | 23 | typedef enum { ENG_RPM, 24 | SPEED } obd_pid_states; 25 | obd_pid_states obd_state = ENG_RPM; 26 | 27 | float rpm = 0; 28 | float mph = 0; 29 | 30 | 31 | 32 | 33 | void setup() 34 | { 35 | Serial.begin(115200); 36 | ELM_PORT.begin(115200); 37 | 38 | Serial.println("Attempting to connect to ELM327..."); 39 | 40 | if (!myELM327.begin(ELM_PORT, DEBUG, TIMEOUT)) 41 | { 42 | Serial.println("Couldn't connect to OBD scanner"); 43 | 44 | if (HALT_ON_FAIL) 45 | while (1); 46 | } 47 | 48 | Serial.println("Connected to ELM327"); 49 | } 50 | 51 | 52 | 53 | 54 | void loop() 55 | { 56 | switch (obd_state) 57 | { 58 | case ENG_RPM: 59 | { 60 | rpm = myELM327.rpm(); 61 | 62 | if (myELM327.nb_rx_state == ELM_SUCCESS) 63 | { 64 | Serial.print("rpm: "); 65 | Serial.println(rpm); 66 | obd_state = SPEED; 67 | } 68 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 69 | { 70 | myELM327.printError(); 71 | obd_state = SPEED; 72 | } 73 | 74 | break; 75 | } 76 | 77 | case SPEED: 78 | { 79 | mph = myELM327.mph(); 80 | 81 | if (myELM327.nb_rx_state == ELM_SUCCESS) 82 | { 83 | Serial.print("mph: "); 84 | Serial.println(mph); 85 | obd_state = ENG_RPM; 86 | } 87 | else if (myELM327.nb_rx_state != ELM_GETTING_MSG) 88 | { 89 | myELM327.printError(); 90 | obd_state = ENG_RPM; 91 | } 92 | 93 | break; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ELMDuino 2 | version=3.4.1 3 | author=PowerBroker2 4 | maintainer=PowerBroker2 5 | sentence=Arduino library to easily interface with the common OBDII scanner: ELM327 6 | paragraph=Arduino library to easily interface with the common OBDII scanner: ELM327 7 | category=Communication 8 | url=https://github.com/PowerBroker2/ELMduino 9 | architectures=* 10 | -------------------------------------------------------------------------------- /reference/ELM327DS.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerBroker2/ELMduino/e9d6b55fd63f4afd7789d0981861e9990cdfcd8e/reference/ELM327DS.pdf -------------------------------------------------------------------------------- /src/ELMduino.cpp: -------------------------------------------------------------------------------- 1 | #include "ELMduino.h" 2 | 3 | /* 4 | bool ELM327::begin(Stream &stream, const bool& debug, const uint16_t& timeout, const char& protocol, const uint16_t& payloadLen, const byte& dataTimeout) 5 | 6 | Description: 7 | ------------ 8 | * Constructor for the ELM327 Class; initializes ELM327 9 | 10 | Inputs: 11 | ------- 12 | * Stream &stream - Reference to Serial port connected to ELM327 13 | * bool debug - Specify whether or not to print debug statements to "Serial" 14 | * uint16_t timeout - Time in ms to wait for a query response 15 | * char protocol - Protocol ID to specify the ELM327 to communicate with the ECU over 16 | * uint16_t payloadLen - Maximum number of bytes expected to be returned by the ELM327 after a query 17 | * byte dataTimeout - Number of ms to wait after receiving data before the ELM327 will 18 | return the data - see https://web.archive.org/web/20230729213500/https://www.elmelectronics.com/help/obd/tips/#UnderstandingOBD 19 | Return: 20 | ------- 21 | * bool - Whether or not the ELM327 was properly initialized 22 | */ 23 | bool ELM327::begin( Stream& stream, 24 | const bool& debug, 25 | const uint16_t& timeout, 26 | const char& protocol, 27 | const uint16_t& payloadLen, 28 | const byte& dataTimeout) 29 | { 30 | elm_port = &stream; 31 | PAYLOAD_LEN = payloadLen; 32 | debugMode = debug; 33 | timeout_ms = timeout; 34 | payload = (char *)malloc(PAYLOAD_LEN + 1); // allow for terminating '\0' 35 | 36 | // test if serial port is connected 37 | if (!elm_port) 38 | return false; 39 | 40 | // try to connect 41 | if (!initializeELM(protocol, dataTimeout)) 42 | return false; 43 | 44 | return true; 45 | } 46 | 47 | ELM327::~ELM327() { 48 | if (payload) free(payload); 49 | } 50 | 51 | /* 52 | bool ELM327::initializeELM(const char& protocol, const byte& dataTimeout) 53 | 54 | Description: 55 | ------------ 56 | * Initializes ELM327 57 | 58 | Inputs: 59 | ------- 60 | * char protocol - Protocol ID to specify the ELM327 to communicate with the ECU over 61 | * byte dataTimeout - Number of ms to wait after receiving data before the ELM327 will 62 | return the data - see https://www.elmelectronics.com/help/obd/tips/#UnderstandingOBD 63 | 64 | Return: 65 | ------- 66 | * bool - Whether or not the ELM327 was propperly initialized 67 | 68 | Notes: 69 | ------ 70 | * Protocol - Description 71 | * 0 - Automatic 72 | * 1 - SAE J1850 PWM (41.6 kbaud) 73 | * 2 - SAE J1850 PWM (10.4 kbaud) 74 | * 3 - ISO 9141-2 (5 baud init) 75 | * 4 - ISO 14230-4 KWP (5 baud init) 76 | * 5 - ISO 14230-4 KWP (fast init) 77 | * 6 - ISO 15765-4 CAN (11 bit ID, 500 kbaud) 78 | * 7 - ISO 15765-4 CAN (29 bit ID, 500 kbaud) 79 | * 8 - ISO 15765-4 CAN (11 bit ID, 250 kbaud) 80 | * 9 - ISO 15765-4 CAN (29 bit ID, 250 kbaud) 81 | * A - SAE J1939 CAN (29 bit ID, 250* kbaud) 82 | * B - User1 CAN (11* bit ID, 125* kbaud) 83 | * C - User2 CAN (11* bit ID, 50* kbaud) 84 | 85 | * --> *user adjustable 86 | */ 87 | bool ELM327::initializeELM(const char& protocol, 88 | const byte& dataTimeout) 89 | { 90 | char command[10] = {'\0'}; 91 | connected = false; 92 | 93 | sendCommand_Blocking(SET_ALL_TO_DEFAULTS); 94 | delay(100); 95 | 96 | sendCommand_Blocking(RESET_ALL); 97 | delay(100); 98 | 99 | sendCommand_Blocking(ECHO_OFF); 100 | delay(100); 101 | 102 | sendCommand_Blocking(PRINTING_SPACES_OFF); 103 | delay(100); 104 | 105 | sendCommand_Blocking(ALLOW_LONG_MESSAGES); 106 | delay(100); 107 | 108 | // // Set data timeout 109 | snprintf(command, sizeof(command), SET_TIMEOUT_TO_H_X_4MS, dataTimeout / 4); 110 | sendCommand_Blocking(command); 111 | delay(100); 112 | 113 | // Automatic searching for protocol requires setting the protocol to AUTO and then 114 | // sending an OBD command to initiate the protocol search. The OBD command "0100" 115 | // requests a list of supported PIDs 0x00 - 0x20 and is guaranteed to work 116 | if (protocol == '0') 117 | { 118 | // Tell the ELM327 to do an auto protocol search. If a valid protocol is found, it will be saved to memory. 119 | // Some ELM clones may not have memory enabled and thus will perform the search every time. 120 | snprintf(command, sizeof(command), SET_PROTOCOL_TO_AUTO_H_SAVE, protocol); 121 | if (sendCommand_Blocking(command) == ELM_SUCCESS) 122 | { 123 | if (strstr(payload, RESPONSE_OK) != NULL) 124 | { 125 | // Protocol search can take a comparatively long time. Temporarily set 126 | // the timeout value to 30 seconds, then restore the previous value. 127 | uint16_t prevTimeout = timeout_ms; 128 | timeout_ms = 30000; 129 | 130 | int8_t state = sendCommand_Blocking("0100"); 131 | 132 | if (state == ELM_SUCCESS) 133 | { 134 | timeout_ms = prevTimeout; 135 | connected = true; 136 | return connected; 137 | } 138 | else if (state == ELM_BUFFER_OVERFLOW) 139 | { 140 | while (elm_port->available()) 141 | elm_port->read(); 142 | } 143 | 144 | timeout_ms = prevTimeout; 145 | } 146 | } 147 | } 148 | else 149 | { 150 | // Set protocol 151 | snprintf(command, sizeof(command), TRY_PROT_H_AUTO_SEARCH, protocol); 152 | 153 | if (sendCommand_Blocking(command) == ELM_SUCCESS) 154 | { 155 | if (strstr(payload, RESPONSE_OK) != NULL) 156 | { 157 | connected = true; 158 | return connected; 159 | } 160 | } 161 | } 162 | 163 | if (debugMode) 164 | { 165 | Serial.print(F("Setting protocol via ")); 166 | Serial.print(TRY_PROT_H_AUTO_SEARCH); 167 | Serial.print(F(" did not work - trying via ")); 168 | Serial.println(SET_PROTOCOL_TO_H_SAVE); 169 | } 170 | 171 | // Set protocol and save 172 | snprintf(command, sizeof(command), SET_PROTOCOL_TO_H_SAVE, protocol); 173 | 174 | if (sendCommand_Blocking(command) == ELM_SUCCESS) 175 | { 176 | if (strstr(payload, RESPONSE_OK) != NULL) 177 | { 178 | connected = true; 179 | return connected; 180 | } 181 | } 182 | 183 | if (debugMode) 184 | { 185 | Serial.print(F("Setting protocol via ")); 186 | Serial.print(SET_PROTOCOL_TO_H_SAVE); 187 | Serial.println(F(" did not work")); 188 | } 189 | 190 | return connected; 191 | } 192 | 193 | /* 194 | void ELM327::formatQueryArray(uint8_t service, uint16_t pid, uint8_t num_responses) 195 | 196 | Description: 197 | ------------ 198 | * Creates a query stack to be sent to ELM327 199 | 200 | Inputs: 201 | ------- 202 | * uint16_t service - Service number of the queried PID 203 | * uint32_t pid - PID number of the queried PID 204 | * uint8_t num_responses - see function header for "queryPID()" 205 | 206 | Return: 207 | ------- 208 | * void 209 | */ 210 | void ELM327::formatQueryArray(const uint8_t& service, 211 | const uint16_t& pid, 212 | const uint8_t& num_responses) 213 | { 214 | if (debugMode) 215 | { 216 | Serial.print(F("Service: ")); 217 | Serial.println(service); 218 | Serial.print(F("PID: ")); 219 | Serial.println(pid); 220 | } 221 | 222 | isMode0x22Query = (service == 0x22 && pid <= 0xFF); // mode 0x22 responses always zero-pad the pid to 4 chars, even for a 2-char pid 223 | 224 | query[0] = ((service >> 4) & 0xF) + '0'; 225 | query[1] = (service & 0xF) + '0'; 226 | 227 | // determine PID length (standard queries have 16-bit PIDs, 228 | // but some custom queries have PIDs with 32-bit values) 229 | if (pid & 0xFF00) 230 | { 231 | if (debugMode) 232 | Serial.println(F("Long query detected")); 233 | 234 | longQuery = true; 235 | 236 | query[2] = ((pid >> 12) & 0xF) + '0'; 237 | query[3] = ((pid >> 8) & 0xF) + '0'; 238 | query[4] = ((pid >> 4) & 0xF) + '0'; 239 | query[5] = (pid & 0xF) + '0'; 240 | 241 | if (specifyNumResponses) 242 | { 243 | if (num_responses > 0xF) 244 | { 245 | query[6] = ((num_responses >> 4) & 0xF) + '0'; 246 | query[7] = (num_responses & 0xF) + '0'; 247 | query[8] = '\0'; 248 | 249 | upper(query, 8); 250 | } 251 | else 252 | { 253 | query[6] = (num_responses & 0xF) + '0'; 254 | query[7] = '\0'; 255 | query[8] = '\0'; 256 | 257 | upper(query, 7); 258 | } 259 | } 260 | else 261 | { 262 | query[6] = '\0'; 263 | query[7] = '\0'; 264 | query[8] = '\0'; 265 | 266 | upper(query, 6); 267 | } 268 | } 269 | else 270 | { 271 | if (debugMode) 272 | Serial.println(F("Normal length query detected")); 273 | 274 | longQuery = false; 275 | 276 | query[2] = ((pid >> 4) & 0xF) + '0'; 277 | query[3] = (pid & 0xF) + '0'; 278 | 279 | if (specifyNumResponses) 280 | { 281 | if (num_responses > 0xF) 282 | { 283 | query[4] = ((num_responses >> 4) & 0xF) + '0'; 284 | query[5] = (num_responses & 0xF) + '0'; 285 | query[6] = '\0'; 286 | query[7] = '\0'; 287 | query[8] = '\0'; 288 | 289 | upper(query, 6); 290 | } 291 | else 292 | { 293 | query[4] = (num_responses & 0xF) + '0'; 294 | query[5] = '\0'; 295 | query[6] = '\0'; 296 | query[7] = '\0'; 297 | query[8] = '\0'; 298 | 299 | upper(query, 5); 300 | } 301 | } 302 | else 303 | { 304 | query[4] = '\0'; 305 | query[5] = '\0'; 306 | query[6] = '\0'; 307 | query[7] = '\0'; 308 | query[8] = '\0'; 309 | 310 | upper(query, 4); 311 | } 312 | } 313 | 314 | if (debugMode) 315 | { 316 | Serial.print(F("Query string: ")); 317 | Serial.println(query); 318 | } 319 | } 320 | 321 | /* 322 | void ELM327::upper(char string[], uint8_t buflen) 323 | 324 | Description: 325 | ------------ 326 | * Converts all elements of char array string[] to 327 | uppercase ascii 328 | 329 | Inputs: 330 | ------- 331 | * uint8_t string[] - Char array 332 | * uint8_t buflen - Length of char array 333 | 334 | Return: 335 | ------- 336 | * void 337 | */ 338 | void ELM327::upper(char string[], 339 | uint8_t buflen) 340 | { 341 | for (uint8_t i = 0; i < buflen; i++) 342 | { 343 | if (string[i] > 'Z') 344 | string[i] -= 32; 345 | else if ((string[i] > '9') && (string[i] < 'A')) 346 | string[i] += 7; 347 | } 348 | } 349 | 350 | /* 351 | bool ELM327::timeout() 352 | 353 | Description: 354 | ------------ 355 | * Determines if a time-out has occurred 356 | 357 | Inputs: 358 | ------- 359 | * void 360 | 361 | Return: 362 | ------- 363 | * bool - whether or not a time-out has occurred 364 | */ 365 | bool ELM327::timeout() 366 | { 367 | currentTime = millis(); 368 | if ((currentTime - previousTime) >= timeout_ms) 369 | return true; 370 | return false; 371 | } 372 | 373 | /* 374 | uint8_t ELM327::ctoi(uint8_t value) 375 | 376 | Description: 377 | ------------ 378 | * converts a decimal or hex char to an int 379 | 380 | Inputs: 381 | ------- 382 | * uint8_t value - char to be converted 383 | 384 | Return: 385 | ------- 386 | * uint8_t - int value of parameter "value" 387 | */ 388 | uint8_t ELM327::ctoi(uint8_t value) 389 | { 390 | if (value >= 'A') 391 | return value - 'A' + 10; 392 | else 393 | return value - '0'; 394 | } 395 | 396 | /* 397 | int8_t ELM327::nextIndex(char const *str, 398 | char const *target, 399 | uint8_t numOccur) 400 | 401 | Description: 402 | ------------ 403 | * Finds and returns the first char index of 404 | numOccur'th instance of target in str 405 | 406 | Inputs: 407 | ------- 408 | * char const *str - string to search target within 409 | * char const *target - String to search for in str 410 | * uint8_t numOccur - Which instance of target in str 411 | 412 | Return: 413 | ------- 414 | * int8_t - First char index of numOccur'th 415 | instance of target in str. -1 if there is no 416 | numOccur'th instance of target in str 417 | */ 418 | int8_t ELM327::nextIndex(char const *str, 419 | char const *target, 420 | uint8_t numOccur) 421 | { 422 | char const *p = str; 423 | char const *r = str; 424 | uint8_t count; 425 | 426 | for (count = 0;; ++count) 427 | { 428 | p = strstr(p, target); 429 | 430 | if (count == (numOccur - 1)) 431 | break; 432 | 433 | if (!p) 434 | break; 435 | 436 | p++; 437 | } 438 | 439 | if (!p) 440 | return -1; 441 | 442 | return p - r; 443 | } 444 | 445 | /* 446 | void ELM327::removeChar(char *from, 447 | char const *remove) 448 | 449 | Description: 450 | ------------ 451 | * Removes all instances of each char in string "remove" from the string "from" 452 | 453 | Inputs: 454 | ------- 455 | * char *from - String to remove target(s) from 456 | * char const *remove - Chars to find/remove 457 | 458 | Return: 459 | ------- 460 | * void 461 | */ 462 | void ELM327::removeChar(char *from, const char *remove) 463 | { 464 | size_t i = 0, j = 0; 465 | while (from[i]) { 466 | if (!strchr(remove, from[i])) 467 | from[j++] = from[i]; 468 | i++; 469 | } 470 | from[j] = '\0'; 471 | } 472 | /* 473 | double ELM327::conditionResponse(const uint8_t &numExpectedBytes, const float &scaleFactor, const float &bias) 474 | 475 | Description: 476 | ------------ 477 | * Converts the ELM327's response into its correct, numerical value. Returns 0 if numExpectedBytes > numPayChars 478 | 479 | Inputs: 480 | ------- 481 | * uint64_t response - ELM327's response 482 | * uint8_t numExpectedBytes - Number of valid bytes from the response to process 483 | * double scaleFactor - Amount to scale the response by 484 | * float bias - Amount to bias the response by 485 | 486 | Return: 487 | ------- 488 | * double - Converted numerical value 489 | */ 490 | double ELM327::conditionResponse(const uint8_t& numExpectedBytes, 491 | const double& scaleFactor, 492 | const double& bias) 493 | { 494 | uint8_t numExpectedPayChars = numExpectedBytes * 2; 495 | uint8_t payCharDiff = numPayChars - numExpectedPayChars; 496 | 497 | if (numExpectedBytes > 8) 498 | { 499 | if (debugMode) 500 | Serial.println(F("WARNING: Number of expected response bytes is greater than 8 - returning 0")); 501 | 502 | return 0; 503 | } 504 | 505 | if (numPayChars < numExpectedPayChars) 506 | { 507 | if (debugMode) 508 | Serial.println(F("WARNING: Number of payload chars is less than the number of expected response chars returned by ELM327 - returning 0")); 509 | 510 | return 0; 511 | } 512 | else if (numPayChars & 0x1) 513 | { 514 | if (debugMode) 515 | Serial.println(F("WARNING: Number of payload chars returned by ELM327 is an odd value - returning 0")); 516 | 517 | return 0; 518 | } 519 | else if (numExpectedPayChars == numPayChars) 520 | { 521 | if (scaleFactor == 1 && bias == 0) // No scale/bias needed 522 | return response; 523 | else 524 | return (response * scaleFactor) + bias; 525 | } 526 | 527 | // If there were more payload bytes returned than we expected, test the first and last bytes in the 528 | // returned payload and see which gives us a higher value. Sometimes ELM327's return leading zeros 529 | // and others return trailing zeros. The following approach gives us the best chance at determining 530 | // where the real data is. Note that if the payload returns BOTH leading and trailing zeros, this 531 | // will not give accurate results! 532 | 533 | if (debugMode) 534 | Serial.println(F("Looking for lagging zeros")); 535 | 536 | uint16_t numExpectedBits = numExpectedBytes * 8; 537 | uint64_t laggingZerosMask = 0; 538 | 539 | for (uint16_t i = 0; i < numExpectedBits; i++) 540 | laggingZerosMask |= (1 << i); 541 | 542 | if (!(laggingZerosMask & response)) // Detect all lagging zeros in `response` 543 | { 544 | if (debugMode) 545 | Serial.println(F("Lagging zeros found")); 546 | 547 | if (scaleFactor == 1 && bias == 0) // No scale/bias needed 548 | return (response >> (4 * payCharDiff)); 549 | else 550 | return ((response >> (4 * payCharDiff)) * scaleFactor) + bias; 551 | } 552 | else 553 | { 554 | if (debugMode) 555 | Serial.println(F("Lagging zeros not found - assuming leading zeros")); 556 | 557 | if (scaleFactor == 1 && bias == 0) // No scale/bias needed 558 | return response; 559 | else 560 | return (response * scaleFactor) + bias; 561 | } 562 | } 563 | 564 | /* 565 | double ELM327::conditionResponse(double (*func)()) 566 | 567 | Description: 568 | ------------ 569 | * Provides a means to pass in a user-defined function to process the response. Used for PIDs that 570 | don't use the common scaleFactor + Bias formula to calculate the value from the response data. Also 571 | useful for processing OEM custom PIDs which are too numerous and varied to encode in the lib. 572 | 573 | Inputs: 574 | ------- 575 | * (*func)() - pointer to function to do calculate response value 576 | 577 | Return: 578 | ------- 579 | * double - Converted numerical value 580 | */ 581 | 582 | double ELM327::conditionResponse(double (*func)()) { 583 | return func(); 584 | } 585 | 586 | /* 587 | void ELM327::flushInputBuff() 588 | 589 | Description: 590 | ------------ 591 | * Flushes input serial buffer 592 | 593 | Inputs: 594 | ------- 595 | * void 596 | 597 | Return: 598 | ------- 599 | * void 600 | */ 601 | void ELM327::flushInputBuff() 602 | { 603 | if (debugMode) 604 | Serial.println(F("Clearing input serial buffer")); 605 | 606 | while (elm_port->available()) 607 | elm_port->read(); 608 | } 609 | 610 | /* 611 | void ELM327::queryPID(const uint8_t& service, const uint16_t& pid, const uint8_t& num_responses) 612 | 613 | Description: 614 | ------------ 615 | * Create a PID query command string and send the command 616 | 617 | Inputs: 618 | ------- 619 | * uint8_t service - The diagnostic service ID. 01 is "Show current data" 620 | * uint16_t pid - The Parameter ID (PID) from the service 621 | * uint8_t num_responses - Number of lines of data to receive - see ELM datasheet "Talking to the vehicle". 622 | This can speed up retrieval of information if you know how many responses will be sent. 623 | Basically the OBD scanner will not wait for more responses if it does not need to go through 624 | final timeout. Also prevents OBD scanners from sending mulitple of the same response. 625 | 626 | Return: 627 | ------- 628 | * void 629 | */ 630 | void ELM327::queryPID(const uint8_t& service, 631 | const uint16_t& pid, 632 | const uint8_t& num_responses) 633 | { 634 | formatQueryArray(service, pid, num_responses); 635 | sendCommand(query); 636 | } 637 | 638 | /* 639 | void ELM327::queryPID(char queryStr[]) 640 | 641 | Description: 642 | ------------ 643 | * Queries ELM327 for a specific type of vehicle telemetry data 644 | 645 | Inputs: 646 | ------- 647 | * char queryStr[] - Query string (service and PID) 648 | 649 | Return: 650 | ------- 651 | * void 652 | */ 653 | void ELM327::queryPID(char queryStr[]) 654 | { 655 | if (strlen(queryStr) <= 4) 656 | longQuery = false; 657 | else 658 | longQuery = true; 659 | 660 | sendCommand(queryStr); 661 | } 662 | 663 | /* 664 | double ELM327::processPID(const uint8_t& service, const uint16_t& pid, const uint8_t& num_responses, const uint8_t& numExpectedBytes, const float& scaleFactor, const float& bias) 665 | 666 | Description: 667 | ------------ 668 | * Queries ELM327 for a specific type of vehicle telemetry data 669 | 670 | Inputs: 671 | ------- 672 | * uint8_t service - The diagnostic service ID. 01 is "Show current data" 673 | * uint16_t pid - The Parameter ID (PID) from the service 674 | * uint8_t num_responses - Number of lines of data to receive - see ELM datasheet "Talking to the vehicle". 675 | This can speed up retrieval of information if you know how many responses will be sent. 676 | Basically the OBD scanner will not wait for more responses if it does not need to go through 677 | final timeout. Also prevents OBD scanners from sending mulitple of the same response. 678 | * uint8_t numExpectedBytes - Number of valid bytes from the response to process 679 | * float scaleFactor - Amount to scale the response by 680 | * float bias - Amount to bias the response by 681 | 682 | Return: 683 | ------- 684 | * double - The PID value if successfully received, else 0.0 685 | */ 686 | double ELM327::processPID(const uint8_t& service, 687 | const uint16_t& pid, 688 | const uint8_t& num_responses, 689 | const uint8_t& numExpectedBytes, 690 | const double& scaleFactor, 691 | const float& bias) 692 | { 693 | if (nb_query_state == SEND_COMMAND) 694 | { 695 | queryPID(service, pid, num_responses); 696 | nb_query_state = WAITING_RESP; 697 | } 698 | else if (nb_query_state == WAITING_RESP) 699 | { 700 | get_response(); 701 | if (nb_rx_state == ELM_SUCCESS) 702 | { 703 | nb_query_state = SEND_COMMAND; // Reset the query state machine for next command 704 | findResponse(); 705 | 706 | /* This data manipulation seems duplicative of the responseByte_0, responseByte_1, etc vars and it is. 707 | The duplcation is deliberate to provide a clear way for the calculator functions to access the relevant 708 | data bytes from the response in the format they are commonly expressed in and without breaking backward 709 | compatability with existing code that may be using the responseByte_n vars. 710 | 711 | In addition, we need to place the response values into static vars that can be accessed by the (static) 712 | calculator functions. A future (breaking!) change could be made to eliminate this duplication. 713 | */ 714 | uint8_t responseBits = numExpectedBytes * 8; 715 | uint8_t extractedBytes[8] = {0}; // Store extracted bytes 716 | 717 | // Extract bytes only if shift is non-negative 718 | for (int i = 0; i < numExpectedBytes; i++) 719 | { 720 | int shiftAmount = responseBits - (8 * (i + 1)); // Compute shift amount 721 | if (shiftAmount >= 0) { // Ensure valid shift 722 | extractedBytes[i] = (response >> shiftAmount) & 0xFF; // Extract byte 723 | } 724 | } 725 | 726 | // Assign extracted values to response_A, response_B, ..., response_H safely 727 | response_A = extractedBytes[0]; 728 | response_B = extractedBytes[1]; 729 | response_C = extractedBytes[2]; 730 | response_D = extractedBytes[3]; 731 | response_E = extractedBytes[4]; 732 | response_F = extractedBytes[5]; 733 | response_G = extractedBytes[6]; 734 | response_H = extractedBytes[7]; 735 | 736 | double (*calculator)() = selectCalculator(pid); 737 | 738 | if (nullptr == calculator) { 739 | //Use the default scaleFactor + Bias calculation 740 | return conditionResponse(numExpectedBytes, scaleFactor, bias); 741 | } 742 | else { 743 | return conditionResponse(calculator); 744 | } 745 | } 746 | else if (nb_rx_state != ELM_GETTING_MSG) 747 | nb_query_state = SEND_COMMAND; // Error or timeout, so reset the query state machine for next command 748 | } 749 | return 0.0; 750 | } 751 | /* 752 | double ELM327::selectCalculator(uint16_t pid))() 753 | 754 | Description: 755 | ------------ 756 | * Selects the appropriate calculation function for a given PID. 757 | 758 | Inputs: 759 | ------- 760 | * uint16_t pid - The Parameter ID (PID) from the service 761 | 762 | 763 | Return: 764 | ------- 765 | * double (*func()) - Pointer to a function to be used to calculate the value for this PID. 766 | Returns nullptr if the PID is calculated using the default scaleFactor + Bias formula as 767 | implemented in conditionResponse(). (Maintained for backward compatibility) 768 | */ 769 | double (*ELM327::selectCalculator(uint16_t pid))() 770 | { 771 | switch (pid) 772 | { 773 | case ENGINE_LOAD: 774 | case ENGINE_COOLANT_TEMP: 775 | case SHORT_TERM_FUEL_TRIM_BANK_1: 776 | case LONG_TERM_FUEL_TRIM_BANK_1: 777 | case SHORT_TERM_FUEL_TRIM_BANK_2: 778 | case LONG_TERM_FUEL_TRIM_BANK_2: 779 | case FUEL_PRESSURE: 780 | case INTAKE_MANIFOLD_ABS_PRESSURE: 781 | case VEHICLE_SPEED: 782 | case TIMING_ADVANCE: 783 | case INTAKE_AIR_TEMP: 784 | case THROTTLE_POSITION: 785 | case COMMANDED_EGR: 786 | case EGR_ERROR: 787 | case COMMANDED_EVAPORATIVE_PURGE: 788 | case FUEL_TANK_LEVEL_INPUT: 789 | case WARM_UPS_SINCE_CODES_CLEARED: 790 | case ABS_BAROMETRIC_PRESSURE: 791 | case RELATIVE_THROTTLE_POSITION: 792 | case AMBIENT_AIR_TEMP: 793 | case ABS_THROTTLE_POSITION_B: 794 | case ABS_THROTTLE_POSITION_C: 795 | case ABS_THROTTLE_POSITION_D: 796 | case ABS_THROTTLE_POSITION_E: 797 | case ABS_THROTTLE_POSITION_F: 798 | case COMMANDED_THROTTLE_ACTUATOR: 799 | case ETHANOL_FUEL_PERCENT: 800 | case RELATIVE_ACCELERATOR_PEDAL_POS: 801 | case HYBRID_BATTERY_REMAINING_LIFE: 802 | case ENGINE_OIL_TEMP: 803 | case DEMANDED_ENGINE_PERCENT_TORQUE: 804 | case ACTUAL_ENGINE_TORQUE: 805 | return nullptr; 806 | 807 | case ENGINE_RPM: 808 | return calculator_0C; 809 | 810 | case MAF_FLOW_RATE: 811 | return calculator_10; 812 | 813 | case OXYGEN_SENSOR_1_A: 814 | case OXYGEN_SENSOR_2_A: 815 | case OXYGEN_SENSOR_3_A: 816 | case OXYGEN_SENSOR_4_A: 817 | case OXYGEN_SENSOR_5_A: 818 | case OXYGEN_SENSOR_6_A: 819 | case OXYGEN_SENSOR_7_A: 820 | case OXYGEN_SENSOR_8_A: 821 | case OXYGEN_SENSOR_1_B: 822 | case OXYGEN_SENSOR_2_B: 823 | case OXYGEN_SENSOR_3_B: 824 | case OXYGEN_SENSOR_4_B: 825 | case OXYGEN_SENSOR_6_B: 826 | case OXYGEN_SENSOR_7_B: 827 | case OXYGEN_SENSOR_8_B: 828 | case OXYGEN_SENSOR_1_C: 829 | case OXYGEN_SENSOR_2_C: 830 | case OXYGEN_SENSOR_3_C: 831 | case OXYGEN_SENSOR_4_C: 832 | case OXYGEN_SENSOR_5_C: 833 | case OXYGEN_SENSOR_6_C: 834 | case OXYGEN_SENSOR_7_C: 835 | case OXYGEN_SENSOR_8_C: 836 | return calculator_14; 837 | 838 | case RUN_TIME_SINCE_ENGINE_START: 839 | case DISTANCE_TRAVELED_WITH_MIL_ON: 840 | case DIST_TRAV_SINCE_CODES_CLEARED: 841 | case TIME_RUN_WITH_MIL_ON: 842 | case TIME_SINCE_CODES_CLEARED: 843 | case ENGINE_REFERENCE_TORQUE: 844 | return calculator_1F; 845 | 846 | case FUEL_RAIL_PRESSURE: 847 | return calculator_22; 848 | 849 | case FUEL_RAIL_GUAGE_PRESSURE: 850 | case FUEL_RAIL_ABS_PRESSURE: 851 | return calculator_23; 852 | 853 | case EVAP_SYSTEM_VAPOR_PRESSURE: 854 | return calculator_32; 855 | 856 | case CATALYST_TEMP_BANK_1_SENSOR_1: 857 | case CATALYST_TEMP_BANK_2_SENSOR_1: 858 | case CATALYST_TEMP_BANK_1_SENSOR_2: 859 | case CATALYST_TEMP_BANK_2_SENSOR_2: 860 | return calculator_3C; 861 | 862 | case CONTROL_MODULE_VOLTAGE: 863 | return calculator_42; 864 | 865 | case ABS_LOAD_VALUE: 866 | return calculator_43; 867 | 868 | case FUEL_AIR_COMMANDED_EQUIV_RATIO: 869 | return calculator_44; 870 | 871 | case MAX_VALUES_EQUIV_V_I_PRESSURE: 872 | return calculator_4F; 873 | 874 | case MAX_MAF_RATE: 875 | return calculator_50; 876 | 877 | case ABS_EVAP_SYS_VAPOR_PRESSURE: 878 | return calculator_53; 879 | 880 | case SHORT_TERM_SEC_OXY_SENS_TRIM_1_3: 881 | case LONG_TERM_SEC_OXY_SENS_TRIM_1_3: 882 | case SHORT_TERM_SEC_OXY_SENS_TRIM_2_4: 883 | case LONG_TERM_SEC_OXY_SENS_TRIM_2_4: 884 | return calculator_55; 885 | 886 | case FUEL_INJECTION_TIMING: 887 | return calculator_5D; 888 | 889 | case ENGINE_FUEL_RATE: 890 | return calculator_5E; 891 | 892 | default: 893 | return nullptr; 894 | 895 | } 896 | } 897 | 898 | /* 899 | uint32_t ELM327::supportedPIDs_1_20() 900 | 901 | Description: 902 | ------------ 903 | * Determine which of PIDs 0x1 through 0x20 are supported (bit encoded) 904 | 905 | Inputs: 906 | ------- 907 | * void 908 | 909 | Return: 910 | ------- 911 | * uint32_t - Bit encoded booleans of supported PIDs 0x1-0x20 912 | */ 913 | uint32_t ELM327::supportedPIDs_1_20() 914 | { 915 | return (uint32_t)processPID(SERVICE_01, SUPPORTED_PIDS_1_20, 1, 4); 916 | } 917 | 918 | /* 919 | uint32_t ELM327::monitorStatus() 920 | 921 | Description: 922 | ------------ 923 | * Monitor status since DTCs cleared (Includes malfunction indicator 924 | lamp (MIL) status and number of DTCs). See https://en.wikipedia.org/wiki/OBD-II_PIDs#Service_01_PID_01 925 | for more info 926 | 927 | Inputs: 928 | ------- 929 | * void 930 | 931 | Return: 932 | ------- 933 | * uint32_t - Bit encoded status (https://en.wikipedia.org/wiki/OBD-II_PIDs#Service_01_PID_01) 934 | */ 935 | uint32_t ELM327::monitorStatus() 936 | { 937 | return (uint32_t)processPID(SERVICE_01, MONITOR_STATUS_SINCE_DTC_CLEARED, 1, 4); 938 | } 939 | 940 | /* 941 | uint16_t ELM327::freezeDTC() 942 | 943 | Description: 944 | ------------ 945 | * Freeze DTC - see https://www.samarins.com/diagnose/freeze-frame.html for more info 946 | 947 | Inputs: 948 | ------- 949 | * void 950 | 951 | Return: 952 | ------- 953 | * uint16_t - Various vehicle information (https://www.samarins.com/diagnose/freeze-frame.html) 954 | */ 955 | uint16_t ELM327::freezeDTC() 956 | { 957 | return (uint16_t)processPID(SERVICE_01, FREEZE_DTC, 1, 2); 958 | } 959 | 960 | /* 961 | uint16_t ELM327::fuelSystemStatus() 962 | 963 | Description: 964 | ------------ 965 | * Freeze DTC - see https://en.wikipedia.org/wiki/OBD-II_PIDs#Service_01_PID_03 for more info 966 | 967 | Inputs: 968 | ------- 969 | * void 970 | 971 | Return: 972 | ------- 973 | * uint16_t - Bit encoded status (https://en.wikipedia.org/wiki/OBD-II_PIDs#Service_01_PID_03) 974 | */ 975 | uint16_t ELM327::fuelSystemStatus() 976 | { 977 | return (uint16_t)processPID(SERVICE_01, FUEL_SYSTEM_STATUS, 1, 2); 978 | } 979 | 980 | /* 981 | float ELM327::engineLoad() 982 | 983 | Description: 984 | ------------ 985 | * Find the current engine load in % 986 | 987 | Inputs: 988 | ------- 989 | * void 990 | 991 | Return: 992 | ------- 993 | * float - Engine load % 994 | */ 995 | float ELM327::engineLoad() 996 | { 997 | return processPID(SERVICE_01, ENGINE_LOAD, 1, 1, 100.0 / 255.0); 998 | } 999 | 1000 | /* 1001 | float ELM327::engineCoolantTemp() 1002 | 1003 | Description: 1004 | ------------ 1005 | * Find the current engine coolant temp in C 1006 | 1007 | Inputs: 1008 | ------- 1009 | * void 1010 | 1011 | Return: 1012 | ------- 1013 | * float - Engine load % 1014 | */ 1015 | float ELM327::engineCoolantTemp() 1016 | { 1017 | return processPID(SERVICE_01, ENGINE_COOLANT_TEMP, 1, 1, 1, -40.0); 1018 | } 1019 | 1020 | /* 1021 | float ELM327::shortTermFuelTrimBank_1() 1022 | 1023 | Description: 1024 | ------------ 1025 | * Find fuel trim % 1026 | 1027 | Inputs: 1028 | ------- 1029 | * void 1030 | 1031 | Return: 1032 | ------- 1033 | * float - Fuel trim % 1034 | */ 1035 | float ELM327::shortTermFuelTrimBank_1() 1036 | { 1037 | return processPID(SERVICE_01, SHORT_TERM_FUEL_TRIM_BANK_1, 1, 1, 100.0 / 128.0, -100.0); 1038 | } 1039 | 1040 | /* 1041 | float ELM327::longTermFuelTrimBank_1() 1042 | 1043 | Description: 1044 | ------------ 1045 | * Find fuel trim % 1046 | 1047 | Inputs: 1048 | ------- 1049 | * void 1050 | 1051 | Return: 1052 | ------- 1053 | * float - Fuel trim % 1054 | */ 1055 | float ELM327::longTermFuelTrimBank_1() 1056 | { 1057 | return processPID(SERVICE_01, LONG_TERM_FUEL_TRIM_BANK_1, 1, 1, 100.0 / 128.0, -100.0); 1058 | } 1059 | 1060 | /* 1061 | float ELM327::shortTermFuelTrimBank_2() 1062 | 1063 | Description: 1064 | ------------ 1065 | * Find fuel trim % 1066 | 1067 | Inputs: 1068 | ------- 1069 | * void 1070 | 1071 | Return: 1072 | ------- 1073 | * float - Fuel trim % 1074 | */ 1075 | float ELM327::shortTermFuelTrimBank_2() 1076 | { 1077 | return processPID(SERVICE_01, SHORT_TERM_FUEL_TRIM_BANK_2, 1, 1, 100.0 / 128.0, -100.0); 1078 | } 1079 | 1080 | /* 1081 | float ELM327::longTermFuelTrimBank_2() 1082 | 1083 | Description: 1084 | ------------ 1085 | * Find fuel trim % 1086 | 1087 | Inputs: 1088 | ------- 1089 | * void 1090 | 1091 | Return: 1092 | ------- 1093 | * float - Fuel trim % 1094 | */ 1095 | float ELM327::longTermFuelTrimBank_2() 1096 | { 1097 | return processPID(SERVICE_01, LONG_TERM_FUEL_TRIM_BANK_2, 1, 1, 100.0 / 128.0, -100.0); 1098 | } 1099 | 1100 | /* 1101 | float ELM327::fuelPressure() 1102 | 1103 | Description: 1104 | ------------ 1105 | * Find fuel pressure in kPa 1106 | 1107 | Inputs: 1108 | ------- 1109 | * void 1110 | 1111 | Return: 1112 | ------- 1113 | * float - Fuel pressure in kPa 1114 | */ 1115 | float ELM327::fuelPressure() 1116 | { 1117 | return processPID(SERVICE_01, FUEL_PRESSURE, 1, 1, 3.0); 1118 | } 1119 | 1120 | /* 1121 | uint8_t ELM327::manifoldPressure() 1122 | 1123 | Description: 1124 | ------------ 1125 | * Find intake manifold absolute pressure in kPa 1126 | 1127 | Inputs: 1128 | ------- 1129 | * void 1130 | 1131 | Return: 1132 | ------- 1133 | * uint8_t - Intake manifold absolute pressure in kPa 1134 | */ 1135 | uint8_t ELM327::manifoldPressure() 1136 | { 1137 | return (uint8_t)processPID(SERVICE_01, INTAKE_MANIFOLD_ABS_PRESSURE, 1, 1); 1138 | } 1139 | 1140 | /* 1141 | float ELM327::rpm() 1142 | 1143 | Description: 1144 | ------------ 1145 | * Queries and parses received message for/returns vehicle RMP data 1146 | 1147 | Inputs: 1148 | ------- 1149 | * void 1150 | 1151 | Return: 1152 | ------- 1153 | * float - Vehicle RPM 1154 | */ 1155 | float ELM327::rpm() 1156 | { 1157 | return processPID(SERVICE_01, ENGINE_RPM, 1, 2, 1.0 / 4.0); 1158 | } 1159 | 1160 | /* 1161 | int32_t ELM327::kph() 1162 | 1163 | Description: 1164 | ------------ 1165 | * Queries and parses received message for/returns vehicle speed data (kph) 1166 | 1167 | Inputs: 1168 | ------- 1169 | * void 1170 | 1171 | Return: 1172 | ------- 1173 | * int32_t - Vehicle speed in kph 1174 | */ 1175 | int32_t ELM327::kph() 1176 | { 1177 | return (int32_t)processPID(SERVICE_01, VEHICLE_SPEED, 1, 1); 1178 | } 1179 | 1180 | /* 1181 | float ELM327::mph() 1182 | 1183 | Description: 1184 | ------------ 1185 | * Queries and parses received message for/returns vehicle speed data (mph) 1186 | 1187 | Inputs: 1188 | ------- 1189 | * void 1190 | 1191 | Return: 1192 | ------- 1193 | * float - Vehicle speed in mph 1194 | */ 1195 | float ELM327::mph() 1196 | { 1197 | return kph() * KPH_MPH_CONVERT; 1198 | } 1199 | 1200 | /* 1201 | float ELM327::timingAdvance() 1202 | 1203 | Description: 1204 | ------------ 1205 | * Find timing advance in degrees before Top Dead Center (TDC) 1206 | 1207 | Inputs: 1208 | ------- 1209 | * void 1210 | 1211 | Return: 1212 | ------- 1213 | * float - Timing advance in degrees before Top Dead Center (TDC) 1214 | */ 1215 | float ELM327::timingAdvance() 1216 | { 1217 | return processPID(SERVICE_01, TIMING_ADVANCE, 1, 1, 1.0 / 2.0, -64.0); 1218 | } 1219 | 1220 | /* 1221 | float ELM327::intakeAirTemp() 1222 | 1223 | Description: 1224 | ------------ 1225 | * Find intake air temperature in C 1226 | 1227 | Inputs: 1228 | ------- 1229 | * void 1230 | 1231 | Return: 1232 | ------- 1233 | * float - Intake air temperature in C 1234 | */ 1235 | float ELM327::intakeAirTemp() 1236 | { 1237 | return processPID(SERVICE_01, INTAKE_AIR_TEMP, 1, 1, 1, -40.0); 1238 | } 1239 | 1240 | /* 1241 | float ELM327::mafRate() 1242 | 1243 | Description: 1244 | ------------ 1245 | * Find mass air flow sensor (MAF) air flow rate rate in g/s 1246 | 1247 | Inputs: 1248 | ------- 1249 | * void 1250 | 1251 | Return: 1252 | ------- 1253 | * float - Mass air flow sensor (MAF) air flow rate rate in g/s 1254 | */ 1255 | float ELM327::mafRate() 1256 | { 1257 | return processPID(SERVICE_01, MAF_FLOW_RATE, 1, 2, 1.0 / 100.0); 1258 | } 1259 | 1260 | /* 1261 | float ELM327::throttle() 1262 | 1263 | Description: 1264 | ------------ 1265 | * Find throttle position in % 1266 | 1267 | Inputs: 1268 | ------- 1269 | * void 1270 | 1271 | Return: 1272 | ------- 1273 | * float - Throttle position in % 1274 | */ 1275 | float ELM327::throttle() 1276 | { 1277 | return processPID(SERVICE_01, THROTTLE_POSITION, 1, 1, 100.0 / 255.0); 1278 | } 1279 | 1280 | /* 1281 | uint8_t ELM327::commandedSecAirStatus() 1282 | 1283 | Description: 1284 | ------------ 1285 | * Find commanded secondary air status 1286 | 1287 | Inputs: 1288 | ------- 1289 | * void 1290 | 1291 | Return: 1292 | ------- 1293 | * uint8_t - Bit encoded status (https://en.wikipedia.org/wiki/OBD-II_PIDs#Service_01_PID_12) 1294 | */ 1295 | uint8_t ELM327::commandedSecAirStatus() 1296 | { 1297 | return (uint8_t)processPID(SERVICE_01, COMMANDED_SECONDARY_AIR_STATUS, 1, 1); 1298 | } 1299 | 1300 | /* 1301 | uint8_t ELM327::oxygenSensorsPresent_2banks() 1302 | 1303 | Description: 1304 | ------------ 1305 | * Find which oxygen sensors are present ([A0..A3] == Bank 1, Sensors 1-4. [A4..A7] == Bank 2...) 1306 | 1307 | Inputs: 1308 | ------- 1309 | * void 1310 | 1311 | Return: 1312 | ------- 1313 | * uint8_t - Bit encoded 1314 | */ 1315 | uint8_t ELM327::oxygenSensorsPresent_2banks() 1316 | { 1317 | return (uint8_t)processPID(SERVICE_01, OXYGEN_SENSORS_PRESENT_2_BANKS, 1, 1); 1318 | } 1319 | 1320 | /* 1321 | uint8_t ELM327::obdStandards() 1322 | 1323 | Description: 1324 | ------------ 1325 | * Find the OBD standards this vehicle conforms to (https://en.wikipedia.org/wiki/OBD-II_PIDs#Service_01_PID_1C) 1326 | 1327 | Inputs: 1328 | ------- 1329 | * void 1330 | 1331 | Return: 1332 | ------- 1333 | * uint8_t - Bit encoded (https://en.wikipedia.org/wiki/OBD-II_PIDs#Service_01_PID_1C) 1334 | */ 1335 | uint8_t ELM327::obdStandards() 1336 | { 1337 | return (uint8_t)processPID(SERVICE_01, OBD_STANDARDS, 1, 1); 1338 | } 1339 | 1340 | /* 1341 | uint8_t ELM327::oxygenSensorsPresent_4banks() 1342 | 1343 | Description: 1344 | ------------ 1345 | * Find which oxygen sensors are present (Similar to PID 13, but [A0..A7] == [B1S1, B1S2, B2S1, B2S2, B3S1, B3S2, B4S1, B4S2]) 1346 | 1347 | Inputs: 1348 | ------- 1349 | * void 1350 | 1351 | Return: 1352 | ------- 1353 | * uint8_t - Bit encoded 1354 | */ 1355 | uint8_t ELM327::oxygenSensorsPresent_4banks() 1356 | { 1357 | return (uint8_t)processPID(SERVICE_01, OXYGEN_SENSORS_PRESENT_4_BANKS, 1, 1); 1358 | } 1359 | 1360 | /* 1361 | bool ELM327::auxInputStatus() 1362 | 1363 | Description: 1364 | ------------ 1365 | * Find Power Take Off (PTO) status 1366 | 1367 | Inputs: 1368 | ------- 1369 | * void 1370 | 1371 | Return: 1372 | ------- 1373 | * bool - Power Take Off (PTO) status 1374 | */ 1375 | bool ELM327::auxInputStatus() 1376 | { 1377 | return (bool)processPID(SERVICE_01, AUX_INPUT_STATUS, 1, 1); 1378 | } 1379 | 1380 | /* 1381 | uint16_t ELM327::runTime() 1382 | 1383 | Description: 1384 | ------------ 1385 | * Find run time since engine start in s 1386 | 1387 | Inputs: 1388 | ------- 1389 | * void 1390 | 1391 | Return: 1392 | ------- 1393 | * uint16_t - Run time since engine start in s 1394 | */ 1395 | uint16_t ELM327::runTime() 1396 | { 1397 | return (uint16_t)processPID(SERVICE_01, RUN_TIME_SINCE_ENGINE_START, 1, 2); 1398 | } 1399 | 1400 | /* 1401 | uint32_t ELM327::supportedPIDs_21_40() 1402 | 1403 | Description: 1404 | ------------ 1405 | * Determine which of PIDs 0x1 through 0x20 are supported (bit encoded) 1406 | 1407 | Inputs: 1408 | ------- 1409 | * void 1410 | 1411 | Return: 1412 | ------- 1413 | * uint32_t - Bit encoded booleans of supported PIDs 0x21-0x20 1414 | */ 1415 | uint32_t ELM327::supportedPIDs_21_40() 1416 | { 1417 | return (uint32_t)processPID(SERVICE_01, SUPPORTED_PIDS_21_40, 1, 4); 1418 | } 1419 | 1420 | /* 1421 | uint16_t ELM327::distTravelWithMIL() 1422 | 1423 | Description: 1424 | ------------ 1425 | * Find distance traveled with malfunction indicator lamp (MIL) on in km 1426 | 1427 | Inputs: 1428 | ------- 1429 | * void 1430 | 1431 | Return: 1432 | ------- 1433 | * uint16_t - Distance traveled with malfunction indicator lamp (MIL) on in km 1434 | */ 1435 | uint16_t ELM327::distTravelWithMIL() 1436 | { 1437 | return (uint16_t)processPID(SERVICE_01, DISTANCE_TRAVELED_WITH_MIL_ON, 1, 2); 1438 | } 1439 | 1440 | /* 1441 | float ELM327::fuelRailPressure() 1442 | 1443 | Description: 1444 | ------------ 1445 | * Find fuel Rail Pressure (relative to manifold vacuum) in kPa 1446 | 1447 | Inputs: 1448 | ------- 1449 | * void 1450 | 1451 | Return: 1452 | ------- 1453 | * float - Fuel Rail Pressure (relative to manifold vacuum) in kPa 1454 | */ 1455 | float ELM327::fuelRailPressure() 1456 | { 1457 | return processPID(SERVICE_01, FUEL_RAIL_PRESSURE, 1, 2, 0.079); 1458 | } 1459 | 1460 | /* 1461 | float ELM327::fuelRailGuagePressure() 1462 | 1463 | Description: 1464 | ------------ 1465 | * Find fuel Rail Gauge Pressure (diesel, or gasoline direct injection) in kPa 1466 | 1467 | Inputs: 1468 | ------- 1469 | * void 1470 | 1471 | Return: 1472 | ------- 1473 | * float - Fuel Rail Gauge Pressure (diesel, or gasoline direct injection) in kPa 1474 | */ 1475 | float ELM327::fuelRailGuagePressure() 1476 | { 1477 | return processPID(SERVICE_01, FUEL_RAIL_GUAGE_PRESSURE, 1, 2, 10.0); 1478 | } 1479 | 1480 | /* 1481 | float ELM327::commandedEGR() 1482 | 1483 | Description: 1484 | ------------ 1485 | * Find commanded Exhaust Gas Recirculation (EGR) in % 1486 | 1487 | Inputs: 1488 | ------- 1489 | * void 1490 | 1491 | Return: 1492 | ------- 1493 | * float - Commanded Exhaust Gas Recirculation (EGR) in % 1494 | */ 1495 | float ELM327::commandedEGR() 1496 | { 1497 | return processPID(SERVICE_01, COMMANDED_EGR, 1, 1, 100.0 / 255.0); 1498 | } 1499 | 1500 | /* 1501 | float ELM327::egrError() 1502 | 1503 | Description: 1504 | ------------ 1505 | * Find Exhaust Gas Recirculation (EGR) error in % 1506 | 1507 | Inputs: 1508 | ------- 1509 | * void 1510 | 1511 | Return: 1512 | ------- 1513 | * float - Exhaust Gas Recirculation (EGR) error in % 1514 | */ 1515 | float ELM327::egrError() 1516 | { 1517 | return processPID(SERVICE_01, EGR_ERROR, 1, 1, 100.0 / 128.0, -100); 1518 | } 1519 | 1520 | /* 1521 | float ELM327::commandedEvapPurge() 1522 | 1523 | Description: 1524 | ------------ 1525 | * Find commanded evaporative purge in % 1526 | 1527 | Inputs: 1528 | ------- 1529 | * void 1530 | 1531 | Return: 1532 | ------- 1533 | * float - Commanded evaporative purge in % 1534 | */ 1535 | float ELM327::commandedEvapPurge() 1536 | { 1537 | return processPID(SERVICE_01, COMMANDED_EVAPORATIVE_PURGE, 1, 1, 100.0 / 255.0); 1538 | } 1539 | 1540 | /* 1541 | float ELM327::fuelLevel() 1542 | 1543 | Description: 1544 | ------------ 1545 | * Find fuel tank level input in % 1546 | 1547 | Inputs: 1548 | ------- 1549 | * void 1550 | 1551 | Return: 1552 | ------- 1553 | * float - Fuel tank level input in % 1554 | */ 1555 | float ELM327::fuelLevel() 1556 | { 1557 | return processPID(SERVICE_01, FUEL_TANK_LEVEL_INPUT, 1, 1, 100.0 / 255.0); 1558 | } 1559 | 1560 | /* 1561 | uint8_t ELM327::warmUpsSinceCodesCleared() 1562 | 1563 | Description: 1564 | ------------ 1565 | * Find num warm-ups since codes cleared 1566 | 1567 | Inputs: 1568 | ------- 1569 | * void 1570 | 1571 | Return: 1572 | ------- 1573 | * uint8_t - Num warm-ups since codes cleared 1574 | */ 1575 | uint8_t ELM327::warmUpsSinceCodesCleared() 1576 | { 1577 | return (uint8_t)processPID(SERVICE_01, WARM_UPS_SINCE_CODES_CLEARED, 1, 1); 1578 | } 1579 | 1580 | /* 1581 | uint16_t ELM327::distSinceCodesCleared() 1582 | 1583 | Description: 1584 | ------------ 1585 | * Find distance traveled since codes cleared in km 1586 | 1587 | Inputs: 1588 | ------- 1589 | * void 1590 | 1591 | Return: 1592 | ------- 1593 | * uint16_t - Distance traveled since codes cleared in km 1594 | */ 1595 | uint16_t ELM327::distSinceCodesCleared() 1596 | { 1597 | return (uint16_t)processPID(SERVICE_01, DIST_TRAV_SINCE_CODES_CLEARED, 1, 2); 1598 | } 1599 | 1600 | /* 1601 | float ELM327::evapSysVapPressure() 1602 | 1603 | Description: 1604 | ------------ 1605 | * Find evap. system vapor pressure in Pa 1606 | 1607 | Inputs: 1608 | ------- 1609 | * void 1610 | 1611 | Return: 1612 | ------- 1613 | * float - Evap. system vapor pressure in Pa 1614 | */ 1615 | float ELM327::evapSysVapPressure() 1616 | { 1617 | return processPID(SERVICE_01, EVAP_SYSTEM_VAPOR_PRESSURE, 1, 2, 1.0 / 4.0); 1618 | } 1619 | 1620 | /* 1621 | uint8_t ELM327::absBaroPressure() 1622 | 1623 | Description: 1624 | ------------ 1625 | * Find absolute barometric pressure in kPa 1626 | 1627 | Inputs: 1628 | ------- 1629 | * void 1630 | 1631 | Return: 1632 | ------- 1633 | * uint8_t - Absolute barometric pressure in kPa 1634 | */ 1635 | uint8_t ELM327::absBaroPressure() 1636 | { 1637 | return (uint8_t)processPID(SERVICE_01, ABS_BAROMETRIC_PRESSURE, 1, 1); 1638 | } 1639 | 1640 | /* 1641 | float ELM327::catTempB1S1() 1642 | 1643 | Description: 1644 | ------------ 1645 | * Find catalyst temperature in C 1646 | 1647 | Inputs: 1648 | ------- 1649 | * void 1650 | 1651 | Return: 1652 | ------- 1653 | * float - Catalyst temperature in C 1654 | */ 1655 | float ELM327::catTempB1S1() 1656 | { 1657 | return processPID(SERVICE_01, CATALYST_TEMP_BANK_1_SENSOR_1, 1, 2, 1.0 / 10.0, -40.0); 1658 | } 1659 | 1660 | /* 1661 | float ELM327::catTempB2S1() 1662 | 1663 | Description: 1664 | ------------ 1665 | * Find catalyst temperature in C 1666 | 1667 | Inputs: 1668 | ------- 1669 | * void 1670 | 1671 | Return: 1672 | ------- 1673 | * float - Catalyst temperature in C 1674 | */ 1675 | float ELM327::catTempB2S1() 1676 | { 1677 | return processPID(SERVICE_01, CATALYST_TEMP_BANK_2_SENSOR_1, 1, 2, 1.0 / 10.0, -40.0); 1678 | } 1679 | 1680 | /* 1681 | float ELM327::catTempB1S2() 1682 | 1683 | Description: 1684 | ------------ 1685 | * Find catalyst temperature in C 1686 | 1687 | Inputs: 1688 | ------- 1689 | * void 1690 | 1691 | Return: 1692 | ------- 1693 | * float - Catalyst temperature in C 1694 | */ 1695 | float ELM327::catTempB1S2() 1696 | { 1697 | return processPID(SERVICE_01, CATALYST_TEMP_BANK_1_SENSOR_2, 1, 2, 1.0 / 10.0, -40.0); 1698 | } 1699 | 1700 | /* 1701 | float ELM327::catTempB2S2() 1702 | 1703 | Description: 1704 | ------------ 1705 | * Find catalyst temperature in C 1706 | 1707 | Inputs: 1708 | ------- 1709 | * void 1710 | 1711 | Return: 1712 | ------- 1713 | * float - Catalyst temperature in C 1714 | */ 1715 | float ELM327::catTempB2S2() 1716 | { 1717 | return processPID(SERVICE_01, CATALYST_TEMP_BANK_2_SENSOR_2, 1, 2, 1.0 / 10.0, -40.0); 1718 | } 1719 | 1720 | /* 1721 | uint32_t ELM327::supportedPIDs_41_60() 1722 | 1723 | Description: 1724 | ------------ 1725 | * Determine which of PIDs 0x41 through 0x60 are supported (bit encoded) 1726 | 1727 | Inputs: 1728 | ------- 1729 | * void 1730 | 1731 | Return: 1732 | ------- 1733 | * uint32_t - Bit encoded booleans of supported PIDs 0x41-0x60 1734 | */ 1735 | uint32_t ELM327::supportedPIDs_41_60() 1736 | { 1737 | return (uint32_t)processPID(SERVICE_01, SUPPORTED_PIDS_41_60, 1, 4); 1738 | } 1739 | 1740 | /* 1741 | uint32_t ELM327::monitorDriveCycleStatus() 1742 | 1743 | Description: 1744 | ------------ 1745 | * Find status this drive cycle (https://en.wikipedia.org/wiki/OBD-II_PIDs#Service_01_PID_41) 1746 | 1747 | Inputs: 1748 | ------- 1749 | * void 1750 | 1751 | Return: 1752 | ------- 1753 | * uint32_t - Bit encoded status (https://en.wikipedia.org/wiki/OBD-II_PIDs#Service_01_PID_41) 1754 | */ 1755 | uint32_t ELM327::monitorDriveCycleStatus() 1756 | { 1757 | return (uint32_t)processPID(SERVICE_01, MONITOR_STATUS_THIS_DRIVE_CYCLE, 1, 4); 1758 | } 1759 | 1760 | /* 1761 | float ELM327::ctrlModVoltage() 1762 | 1763 | Description: 1764 | ------------ 1765 | * Find control module voltage in V 1766 | 1767 | Inputs: 1768 | ------- 1769 | * void 1770 | 1771 | Return: 1772 | ------- 1773 | * float - Control module voltage in V 1774 | */ 1775 | float ELM327::ctrlModVoltage() 1776 | { 1777 | return processPID(SERVICE_01, CONTROL_MODULE_VOLTAGE, 1, 2, 1.0 / 1000.0); 1778 | } 1779 | 1780 | /* 1781 | float ELM327::absLoad() 1782 | 1783 | Description: 1784 | ------------ 1785 | * Find absolute load value in % 1786 | 1787 | Inputs: 1788 | ------- 1789 | * void 1790 | 1791 | Return: 1792 | ------- 1793 | * float - Absolute load value in % 1794 | */ 1795 | float ELM327::absLoad() 1796 | { 1797 | return processPID(SERVICE_01, ABS_LOAD_VALUE, 1, 2, 100.0 / 255.0); 1798 | } 1799 | 1800 | /* 1801 | float ELM327::commandedAirFuelRatio() 1802 | 1803 | Description: 1804 | ------------ 1805 | * Find commanded air-fuel equivalence ratio 1806 | 1807 | Inputs: 1808 | ------- 1809 | * void 1810 | 1811 | Return: 1812 | ------- 1813 | * float - Commanded air-fuel equivalence ratio 1814 | */ 1815 | float ELM327::commandedAirFuelRatio() 1816 | { 1817 | return processPID(SERVICE_01, FUEL_AIR_COMMANDED_EQUIV_RATIO, 1, 2, 2.0 / 65536.0); 1818 | } 1819 | 1820 | /* 1821 | float ELM327::relativeThrottle() 1822 | 1823 | Description: 1824 | ------------ 1825 | * Find relative throttle position in % 1826 | 1827 | Inputs: 1828 | ------- 1829 | * void 1830 | 1831 | Return: 1832 | ------- 1833 | * float - Relative throttle position in % 1834 | */ 1835 | float ELM327::relativeThrottle() 1836 | { 1837 | return processPID(SERVICE_01, RELATIVE_THROTTLE_POSITION, 1, 1, 100.0 / 255.0); 1838 | } 1839 | 1840 | /* 1841 | float ELM327::ambientAirTemp() 1842 | 1843 | Description: 1844 | ------------ 1845 | * Find ambient air temperature in C 1846 | 1847 | Inputs: 1848 | ------- 1849 | * void 1850 | 1851 | Return: 1852 | ------- 1853 | * float - Ambient air temperature in C 1854 | */ 1855 | float ELM327::ambientAirTemp() 1856 | { 1857 | return processPID(SERVICE_01, AMBIENT_AIR_TEMP, 1, 1, 1, -40); 1858 | } 1859 | 1860 | /* 1861 | float ELM327::absThrottlePosB() 1862 | 1863 | Description: 1864 | ------------ 1865 | * Find absolute throttle position B in % 1866 | 1867 | Inputs: 1868 | ------- 1869 | * void 1870 | 1871 | Return: 1872 | ------- 1873 | * float - Absolute throttle position B in % 1874 | */ 1875 | float ELM327::absThrottlePosB() 1876 | { 1877 | return processPID(SERVICE_01, ABS_THROTTLE_POSITION_B, 1, 1, 100.0 / 255.0); 1878 | } 1879 | 1880 | /* 1881 | float ELM327::absThrottlePosC() 1882 | 1883 | Description: 1884 | ------------ 1885 | * Find absolute throttle position C in % 1886 | 1887 | Inputs: 1888 | ------- 1889 | * void 1890 | 1891 | Return: 1892 | ------- 1893 | * float - Absolute throttle position C in % 1894 | */ 1895 | float ELM327::absThrottlePosC() 1896 | { 1897 | return processPID(SERVICE_01, ABS_THROTTLE_POSITION_C, 1, 1, 100.0 / 255.0); 1898 | } 1899 | 1900 | /* 1901 | float ELM327::absThrottlePosD() 1902 | 1903 | Description: 1904 | ------------ 1905 | * Find absolute throttle position D in % 1906 | 1907 | Inputs: 1908 | ------- 1909 | * void 1910 | 1911 | Return: 1912 | ------- 1913 | * float - Absolute throttle position D in % 1914 | */ 1915 | float ELM327::absThrottlePosD() 1916 | { 1917 | return processPID(SERVICE_01, ABS_THROTTLE_POSITION_D, 1, 1, 100.0 / 255.0); 1918 | } 1919 | 1920 | /* 1921 | float ELM327::absThrottlePosE() 1922 | 1923 | Description: 1924 | ------------ 1925 | * Find absolute throttle position E in % 1926 | 1927 | Inputs: 1928 | ------- 1929 | * void 1930 | 1931 | Return: 1932 | ------- 1933 | * float - Absolute throttle position E in % 1934 | */ 1935 | float ELM327::absThrottlePosE() 1936 | { 1937 | return processPID(SERVICE_01, ABS_THROTTLE_POSITION_E, 1, 1, 100.0 / 255.0); 1938 | } 1939 | 1940 | /* 1941 | float ELM327::absThrottlePosF() 1942 | 1943 | Description: 1944 | ------------ 1945 | * Find absolute throttle position F in % 1946 | 1947 | Inputs: 1948 | ------- 1949 | * void 1950 | 1951 | Return: 1952 | ------- 1953 | * float - Absolute throttle position F in % 1954 | */ 1955 | float ELM327::absThrottlePosF() 1956 | { 1957 | return processPID(SERVICE_01, ABS_THROTTLE_POSITION_F, 1, 1, 100.0 / 255.0); 1958 | } 1959 | 1960 | /* 1961 | float ELM327::commandedThrottleActuator() 1962 | 1963 | Description: 1964 | ------------ 1965 | * Find commanded throttle actuator in % 1966 | 1967 | Inputs: 1968 | ------- 1969 | * void 1970 | 1971 | Return: 1972 | ------- 1973 | * float - Commanded throttle actuator in % 1974 | */ 1975 | float ELM327::commandedThrottleActuator() 1976 | { 1977 | return processPID(SERVICE_01, COMMANDED_THROTTLE_ACTUATOR, 1, 1, 100.0 / 255.0); 1978 | } 1979 | 1980 | /* 1981 | uint16_t ELM327::timeRunWithMIL() 1982 | 1983 | Description: 1984 | ------------ 1985 | * Find time run with MIL on in min 1986 | 1987 | Inputs: 1988 | ------- 1989 | * void 1990 | 1991 | Return: 1992 | ------- 1993 | * uint16_t - Time run with MIL on in min 1994 | */ 1995 | uint16_t ELM327::timeRunWithMIL() 1996 | { 1997 | return (uint16_t)processPID(SERVICE_01, TIME_RUN_WITH_MIL_ON, 1, 2); 1998 | } 1999 | 2000 | /* 2001 | uint16_t ELM327::timeSinceCodesCleared() 2002 | 2003 | Description: 2004 | ------------ 2005 | * Find time since trouble codes cleared in min 2006 | 2007 | Inputs: 2008 | ------- 2009 | * void 2010 | 2011 | Return: 2012 | ------- 2013 | * uint16_t - Time since trouble codes cleared in min 2014 | */ 2015 | uint16_t ELM327::timeSinceCodesCleared() 2016 | { 2017 | return (uint16_t)processPID(SERVICE_01, TIME_SINCE_CODES_CLEARED, 1, 2); 2018 | } 2019 | 2020 | /* 2021 | float ELM327::maxMafRate() 2022 | 2023 | Description: 2024 | ------------ 2025 | * Find maximum value for air flow rate from mass air flow sensor in g/s 2026 | 2027 | Inputs: 2028 | ------- 2029 | * void 2030 | 2031 | Return: 2032 | ------- 2033 | * float - Maximum value for air flow rate from mass air flow sensor in g/s 2034 | */ 2035 | float ELM327::maxMafRate() 2036 | { 2037 | return processPID(SERVICE_01, MAX_MAF_RATE, 1, 1, 10.0); 2038 | } 2039 | 2040 | /* 2041 | uint8_t ELM327::fuelType() 2042 | 2043 | Description: 2044 | ------------ 2045 | * Find fuel type (https://en.wikipedia.org/wiki/OBD-II_PIDs#Fuel_Type_Coding) 2046 | 2047 | Inputs: 2048 | ------- 2049 | * void 2050 | 2051 | Return: 2052 | ------- 2053 | * uint8_t - Bit encoded (https://en.wikipedia.org/wiki/OBD-II_PIDs#Fuel_Type_Coding) 2054 | */ 2055 | uint8_t ELM327::fuelType() 2056 | { 2057 | return (uint8_t)processPID(SERVICE_01, FUEL_TYPE, 1, 1); 2058 | } 2059 | 2060 | /* 2061 | float ELM327::ethanolPercent() 2062 | 2063 | Description: 2064 | ------------ 2065 | * Find ethanol fuel in % 2066 | 2067 | Inputs: 2068 | ------- 2069 | * void 2070 | 2071 | Return: 2072 | ------- 2073 | * float - Ethanol fuel in % 2074 | */ 2075 | float ELM327::ethanolPercent() 2076 | { 2077 | return processPID(SERVICE_01, ETHANOL_FUEL_PERCENT, 1, 1, 100.0 / 255.0); 2078 | } 2079 | 2080 | /* 2081 | float ELM327::absEvapSysVapPressure() 2082 | 2083 | Description: 2084 | ------------ 2085 | * Find absolute evap. system vapor pressure in kPa 2086 | 2087 | Inputs: 2088 | ------- 2089 | * void 2090 | 2091 | Return: 2092 | ------- 2093 | * float - Absolute evap. system vapor pressure in kPa 2094 | */ 2095 | float ELM327::absEvapSysVapPressure() 2096 | { 2097 | return processPID(SERVICE_01, ABS_EVAP_SYS_VAPOR_PRESSURE, 1, 2, 1.0 / 200.0); 2098 | } 2099 | 2100 | /* 2101 | float ELM327::evapSysVapPressure2() 2102 | 2103 | Description: 2104 | ------------ 2105 | * Find evap. system vapor pressure in Pa 2106 | 2107 | Inputs: 2108 | ------- 2109 | * void 2110 | 2111 | Return: 2112 | ------- 2113 | * float - Evap. system vapor pressure in Pa 2114 | */ 2115 | float ELM327::evapSysVapPressure2() 2116 | { 2117 | return processPID(SERVICE_01, EVAP_SYS_VAPOR_PRESSURE, 1, 2, 1, -32767); 2118 | } 2119 | 2120 | /* 2121 | float ELM327::absFuelRailPressure() 2122 | 2123 | Description: 2124 | ------------ 2125 | * Find absolute fuel rail pressure in kPa 2126 | 2127 | Inputs: 2128 | ------- 2129 | * void 2130 | 2131 | Return: 2132 | ------- 2133 | * float - absolute fuel rail pressure in kPa 2134 | */ 2135 | float ELM327::absFuelRailPressure() 2136 | { 2137 | return processPID(SERVICE_01, FUEL_RAIL_ABS_PRESSURE, 1, 2, 10.0); 2138 | } 2139 | 2140 | /* 2141 | float ELM327::relativePedalPos() 2142 | 2143 | Description: 2144 | ------------ 2145 | * Find relative accelerator pedal position in % 2146 | 2147 | Inputs: 2148 | ------- 2149 | * void 2150 | 2151 | Return: 2152 | ------- 2153 | * float - Relative accelerator pedal position in % 2154 | */ 2155 | float ELM327::relativePedalPos() 2156 | { 2157 | return processPID(SERVICE_01, RELATIVE_ACCELERATOR_PEDAL_POS, 1, 1, 100.0 / 255.0); 2158 | } 2159 | 2160 | /* 2161 | float ELM327::hybridBatLife() 2162 | 2163 | Description: 2164 | ------------ 2165 | * Find hybrid battery pack remaining life in % 2166 | 2167 | Inputs: 2168 | ------- 2169 | * void 2170 | 2171 | Return: 2172 | ------- 2173 | * float - Hybrid battery pack remaining life in % 2174 | */ 2175 | float ELM327::hybridBatLife() 2176 | { 2177 | return processPID(SERVICE_01, HYBRID_BATTERY_REMAINING_LIFE, 1, 1, 100.0 / 255.0); 2178 | } 2179 | 2180 | /* 2181 | float ELM327::oilTemp() 2182 | 2183 | Description: 2184 | ------------ 2185 | * Find engine oil temperature in C 2186 | 2187 | Inputs: 2188 | ------- 2189 | * void 2190 | 2191 | Return: 2192 | ------- 2193 | * float - Engine oil temperature in C 2194 | */ 2195 | float ELM327::oilTemp() 2196 | { 2197 | return processPID(SERVICE_01, ENGINE_OIL_TEMP, 1, 1, 1, -40.0); 2198 | } 2199 | 2200 | /* 2201 | float ELM327::fuelInjectTiming() 2202 | 2203 | Description: 2204 | ------------ 2205 | * Find fuel injection timing in degrees 2206 | 2207 | Inputs: 2208 | ------- 2209 | * void 2210 | 2211 | Return: 2212 | ------- 2213 | * float - Fuel injection timing in degrees 2214 | */ 2215 | float ELM327::fuelInjectTiming() 2216 | { 2217 | return processPID(SERVICE_01, FUEL_INJECTION_TIMING, 1, 2, 1.0 / 128.0, -210.0); 2218 | } 2219 | 2220 | /* 2221 | float ELM327::fuelRate() 2222 | 2223 | Description: 2224 | ------------ 2225 | * Find engine fuel rate in L/h 2226 | 2227 | Inputs: 2228 | ------- 2229 | * void 2230 | 2231 | Return: 2232 | ------- 2233 | * float - Engine fuel rate in L/h 2234 | */ 2235 | float ELM327::fuelRate() 2236 | { 2237 | return processPID(SERVICE_01, ENGINE_FUEL_RATE, 1, 2, 1.0 / 20.0); 2238 | } 2239 | 2240 | /* 2241 | uint8_t ELM327::emissionRqmts() 2242 | 2243 | Description: 2244 | ------------ 2245 | * Find emission requirements to which vehicle is designed 2246 | 2247 | Inputs: 2248 | ------- 2249 | * void 2250 | 2251 | Return: 2252 | ------- 2253 | * uint8_t - Bit encoded (?) 2254 | */ 2255 | uint8_t ELM327::emissionRqmts() 2256 | { 2257 | return (uint8_t)processPID(SERVICE_01, EMISSION_REQUIREMENTS, 1, 1); 2258 | } 2259 | 2260 | /* 2261 | uint32_t ELM327::supportedPIDs_61_80() 2262 | 2263 | Description: 2264 | ------------ 2265 | * Determine which of PIDs 0x61 through 0x80 are supported (bit encoded) 2266 | 2267 | Inputs: 2268 | ------- 2269 | * void 2270 | 2271 | Return: 2272 | ------- 2273 | * uint32_t - Bit encoded booleans of supported PIDs 0x61-0x80 2274 | */ 2275 | uint32_t ELM327::supportedPIDs_61_80() 2276 | { 2277 | return (uint32_t)processPID(SERVICE_01, SUPPORTED_PIDS_61_80, 1, 4); 2278 | } 2279 | 2280 | /* 2281 | float ELM327::demandedTorque() 2282 | 2283 | Description: 2284 | ------------ 2285 | * Find driver's demanded engine torque in % 2286 | 2287 | Inputs: 2288 | ------- 2289 | * void 2290 | 2291 | Return: 2292 | ------- 2293 | * float - Driver's demanded engine torque in % 2294 | */ 2295 | float ELM327::demandedTorque() 2296 | { 2297 | return processPID(SERVICE_01, DEMANDED_ENGINE_PERCENT_TORQUE, 1, 1, 1, -125.0); 2298 | } 2299 | 2300 | /* 2301 | float ELM327::torque() 2302 | 2303 | Description: 2304 | ------------ 2305 | * Find actual engine torque in % 2306 | 2307 | Inputs: 2308 | ------- 2309 | * void 2310 | 2311 | Return: 2312 | ------- 2313 | * float - Actual engine torque in % 2314 | */ 2315 | float ELM327::torque() 2316 | { 2317 | return processPID(SERVICE_01, ACTUAL_ENGINE_TORQUE, 1, 1, 1, -125.0); 2318 | } 2319 | 2320 | /* 2321 | uint16_t ELM327::referenceTorque() 2322 | 2323 | Description: 2324 | ------------ 2325 | * Find engine reference torque in Nm 2326 | 2327 | Inputs: 2328 | ------- 2329 | * void 2330 | 2331 | Return: 2332 | ------- 2333 | * uint16_t - Engine reference torque in Nm 2334 | */ 2335 | uint16_t ELM327::referenceTorque() 2336 | { 2337 | return processPID(SERVICE_01, ENGINE_REFERENCE_TORQUE, 1, 2); 2338 | } 2339 | 2340 | /* 2341 | uint16_t ELM327::auxSupported() 2342 | 2343 | Description: 2344 | ------------ 2345 | * Find auxiliary input/output supported 2346 | 2347 | Inputs: 2348 | ------- 2349 | * void 2350 | 2351 | Return: 2352 | ------- 2353 | * uint16_t - Bit encoded (?) 2354 | */ 2355 | uint16_t ELM327::auxSupported() 2356 | { 2357 | return (uint16_t)processPID(SERVICE_01, AUX_INPUT_OUTPUT_SUPPORTED, 1, 2); 2358 | } 2359 | 2360 | /* 2361 | void ELM327::sendCommand(const char *cmd) 2362 | 2363 | Description: 2364 | ------------ 2365 | * Sends a command/query for Non-Blocking PID queries 2366 | 2367 | Inputs: 2368 | ------- 2369 | * const char *cmd - Command/query to send to ELM327 2370 | 2371 | Return: 2372 | ------- 2373 | * void 2374 | */ 2375 | void ELM327::sendCommand(const char *cmd) 2376 | { 2377 | // clear payload buffer 2378 | memset(payload, '\0', PAYLOAD_LEN + 1); 2379 | 2380 | // reset input serial buffer and number of received bytes 2381 | recBytes = 0; 2382 | flushInputBuff(); 2383 | connected = false; 2384 | 2385 | // Reset the receive state ready to start receiving a response message 2386 | nb_rx_state = ELM_GETTING_MSG; 2387 | 2388 | if (debugMode) 2389 | { 2390 | Serial.print(F("Sending the following command/query: ")); 2391 | Serial.println(cmd); 2392 | } 2393 | 2394 | elm_port->print(cmd); 2395 | elm_port->print('\r'); 2396 | 2397 | // prime the timeout timer 2398 | previousTime = millis(); 2399 | currentTime = previousTime; 2400 | } 2401 | 2402 | /* 2403 | obd_rx_states ELM327::sendCommand_Blocking(const char* cmd) 2404 | 2405 | Description: 2406 | ------------ 2407 | * Sends a command/query and waits for a respoonse (blocking function) 2408 | Sometimes it's desirable to use a blocking command, e.g when sending an AT command. 2409 | This function removes the need for the caller to set up a loop waiting for the command to finish. 2410 | Caller is free to parse the payload string if they need to use the response. 2411 | 2412 | Inputs: 2413 | ------- 2414 | * const char *cmd - Command/query to send to ELM327 2415 | 2416 | Return: 2417 | ------- 2418 | * int8_t - the ELM_XXX status of getting the OBD response 2419 | */ 2420 | int8_t ELM327::sendCommand_Blocking(const char *cmd) 2421 | { 2422 | sendCommand(cmd); 2423 | uint32_t startTime = millis(); 2424 | while (get_response() == ELM_GETTING_MSG) { 2425 | if (millis() - startTime > timeout_ms) break; 2426 | } 2427 | return nb_rx_state; 2428 | } 2429 | 2430 | /* 2431 | obd_rx_states ELM327::get_response(void) 2432 | 2433 | Description: 2434 | ------------ 2435 | * Non Blocking (NB) receive OBD scanner response. Must be called repeatedly until 2436 | the status progresses past ELM_GETTING_MSG. 2437 | 2438 | Inputs: 2439 | ------- 2440 | * void 2441 | 2442 | Return: 2443 | ------- 2444 | * int8_t - the ELM_XXX status of getting the OBD response 2445 | */ 2446 | int8_t ELM327::get_response(void) 2447 | { 2448 | // buffer the response of the ELM327 until either the 2449 | // end marker is read or a timeout has occurred 2450 | // last valid idx is PAYLOAD_LEN but want to keep one free for terminating '\0' 2451 | // so limit counter to < PAYLOAD_LEN 2452 | if (!elm_port->available()) 2453 | { 2454 | nb_rx_state = ELM_GETTING_MSG; 2455 | if (timeout()) 2456 | nb_rx_state = ELM_TIMEOUT; 2457 | } 2458 | else 2459 | { 2460 | char recChar = elm_port->read(); 2461 | 2462 | if (debugMode) 2463 | { 2464 | Serial.print(F("\tReceived char: ")); 2465 | // display each received character, make non-printables printable 2466 | if (recChar == '\f') 2467 | Serial.println(F("\\f")); 2468 | else if (recChar == '\n') 2469 | Serial.println(F("\\n")); 2470 | else if (recChar == '\r') 2471 | Serial.println(F("\\r")); 2472 | else if (recChar == '\t') 2473 | Serial.println(F("\\t")); 2474 | else if (recChar == '\v') 2475 | Serial.println(F("\\v")); 2476 | // convert spaces to underscore, easier to see in debug output 2477 | else if (recChar == ' ') 2478 | Serial.println(F("_")); 2479 | // display regular printable 2480 | else 2481 | Serial.println(recChar); 2482 | } 2483 | 2484 | // this is the end of the OBD response 2485 | if (recChar == '>') 2486 | { 2487 | if (debugMode) 2488 | Serial.println(F("Delimiter found.")); 2489 | 2490 | nb_rx_state = ELM_MSG_RXD; 2491 | } 2492 | else if (!isalnum(recChar) && (recChar != ':') && (recChar != '.') && (recChar != '\r')) 2493 | // Keep only alphanumeric, decimal, colon, CR. These are needed for response parsing 2494 | // decimal places needed to extract floating point numbers, e.g. battery voltage 2495 | nb_rx_state = ELM_GETTING_MSG; // Discard this character 2496 | else 2497 | { 2498 | if (recBytes < PAYLOAD_LEN) 2499 | { 2500 | payload[recBytes] = recChar; 2501 | recBytes++; 2502 | nb_rx_state = ELM_GETTING_MSG; 2503 | } 2504 | else 2505 | nb_rx_state = ELM_BUFFER_OVERFLOW; 2506 | } 2507 | } 2508 | 2509 | // Message is still being received (or is timing out), so exit early without doing all the other checks 2510 | if (nb_rx_state == ELM_GETTING_MSG) 2511 | return nb_rx_state; 2512 | 2513 | // End of response delimiter was found 2514 | if (debugMode && nb_rx_state == ELM_MSG_RXD) 2515 | { 2516 | Serial.print(F("All chars received: ")); 2517 | Serial.println(payload); 2518 | } 2519 | 2520 | if (nb_rx_state == ELM_TIMEOUT) 2521 | { 2522 | if (debugMode) 2523 | { 2524 | Serial.print(F("Timeout detected with overflow of ")); 2525 | Serial.print((currentTime - previousTime) - timeout_ms); 2526 | Serial.println(F("ms")); 2527 | } 2528 | return nb_rx_state; 2529 | } 2530 | 2531 | if (nb_rx_state == ELM_BUFFER_OVERFLOW) 2532 | { 2533 | if (debugMode) 2534 | { 2535 | Serial.print(F("OBD receive buffer overflow (> ")); 2536 | Serial.print(PAYLOAD_LEN); 2537 | Serial.println(F(" bytes)")); 2538 | } 2539 | return nb_rx_state; 2540 | } 2541 | 2542 | // Now we have successfully received OBD response, check if the payload indicates any OBD errors 2543 | if (nextIndex(payload, RESPONSE_UNABLE_TO_CONNECT) >= 0) 2544 | { 2545 | if (debugMode) 2546 | Serial.println(F("ELM responded with error \"UNABLE TO CONNECT\"")); 2547 | 2548 | nb_rx_state = ELM_UNABLE_TO_CONNECT; 2549 | return nb_rx_state; 2550 | } 2551 | 2552 | connected = true; 2553 | 2554 | if (nextIndex(payload, RESPONSE_NO_DATA) >= 0) 2555 | { 2556 | if (debugMode) 2557 | Serial.println(F("ELM responded with error \"NO DATA\"")); 2558 | 2559 | nb_rx_state = ELM_NO_DATA; 2560 | return nb_rx_state; 2561 | } 2562 | 2563 | if (nextIndex(payload, RESPONSE_STOPPED) >= 0) 2564 | { 2565 | if (debugMode) 2566 | Serial.println(F("ELM responded with error \"STOPPED\"")); 2567 | 2568 | nb_rx_state = ELM_STOPPED; 2569 | return nb_rx_state; 2570 | } 2571 | 2572 | if (nextIndex(payload, RESPONSE_ERROR) >= 0) 2573 | { 2574 | if (debugMode) 2575 | Serial.println(F("ELM responded with \"ERROR\"")); 2576 | 2577 | nb_rx_state = ELM_GENERAL_ERROR; 2578 | return nb_rx_state; 2579 | } 2580 | 2581 | nb_rx_state = ELM_SUCCESS; 2582 | // Need to process multiline repsonses, remove '\r' from non multiline resp 2583 | if (NULL != strchr(payload, ':')) { 2584 | parseMultiLineResponse(); 2585 | } 2586 | else { 2587 | removeChar(payload, " \r"); 2588 | } 2589 | recBytes = strlen(payload); 2590 | return nb_rx_state; 2591 | } 2592 | 2593 | /* 2594 | void ELM327::parseMultilineResponse() 2595 | 2596 | Description: 2597 | ------------ 2598 | * Parses a buffered multiline response into a single line with the specified data 2599 | * Modifies the value of payload for further processing and removes the '\r' chars 2600 | 2601 | Inputs: 2602 | ------- 2603 | * void 2604 | 2605 | Return: 2606 | ------- 2607 | * void 2608 | */ 2609 | void ELM327::parseMultiLineResponse() { 2610 | uint8_t totalBytes = 0; 2611 | uint8_t bytesReceived = 0; 2612 | char newResponse[PAYLOAD_LEN]; 2613 | memset(newResponse, 0, PAYLOAD_LEN * sizeof(char)); // Initialize newResponse to empty string 2614 | char line[256] = ""; 2615 | char* start = payload; 2616 | char* end = strchr(start, '\r'); 2617 | 2618 | do 2619 | { //Step 1: Get a line from the response 2620 | memset(line, '\0', 256); 2621 | if (end != NULL) { 2622 | strncpy(line, start, end - start); 2623 | line[end - start] = '\0'; 2624 | } else { 2625 | strncpy(line, start, strlen(start)); 2626 | line[strlen(start)] = '\0'; 2627 | 2628 | // Exit when there's no more data 2629 | if (strlen(line) == 0) break; 2630 | } 2631 | 2632 | if (debugMode) { 2633 | Serial.print(F("Found line in response: ")); 2634 | Serial.println(line); 2635 | } 2636 | // Step 2: Check if this is the first line of the response 2637 | if (0 == totalBytes) 2638 | // Some devices return the response header in the first line instead of the data length, ignore this line 2639 | // Line containing totalBytes indicator is 3 hex chars only, longer first line will be a header. 2640 | { 2641 | if (strlen(line) > 3) { 2642 | if (debugMode) 2643 | { 2644 | Serial.print(F("Found header in response line: ")); 2645 | Serial.println(line); 2646 | } 2647 | } 2648 | else { 2649 | if (strlen(line) > 0) { 2650 | totalBytes = strtol(line, NULL, 16) * 2; 2651 | if (debugMode) { 2652 | Serial.print(F("totalBytes = ")); 2653 | Serial.println(totalBytes); 2654 | } 2655 | } 2656 | } 2657 | } 2658 | // Step 3: Process data response lines 2659 | else { 2660 | if (strchr(line, ':')) { 2661 | char* dataStart = strchr(line, ':') + 1; 2662 | uint8_t dataLength = strlen(dataStart); 2663 | uint8_t bytesToCopy = (bytesReceived + dataLength > totalBytes) ? (totalBytes - bytesReceived) : dataLength; 2664 | if (bytesReceived + bytesToCopy > PAYLOAD_LEN - 1) { 2665 | bytesToCopy = (PAYLOAD_LEN - 1) - bytesReceived; 2666 | } 2667 | strncat(newResponse, dataStart, bytesToCopy); 2668 | bytesReceived += bytesToCopy; 2669 | 2670 | if (debugMode) { 2671 | Serial.print(F("Response data: ")); 2672 | Serial.println(dataStart); 2673 | } 2674 | } 2675 | } 2676 | if (*(end + 1) == '\0') { 2677 | start = NULL; 2678 | } else { 2679 | start = end + 1; 2680 | } 2681 | end = (start != NULL) ? strchr(start, '\r') : NULL; 2682 | 2683 | } while ((bytesReceived < totalBytes || 0 == totalBytes) && start != NULL); 2684 | 2685 | // Replace payload with parsed response, null-terminate after totalBytes 2686 | int nullTermPos = (totalBytes < PAYLOAD_LEN - 1) ? totalBytes : PAYLOAD_LEN - 1; 2687 | strncpy(payload, newResponse, nullTermPos); 2688 | payload[nullTermPos] = '\0'; // Ensure null termination 2689 | if (debugMode) 2690 | { 2691 | Serial.print(F("Parsed multiline response: ")); 2692 | Serial.println(payload); 2693 | } 2694 | } 2695 | 2696 | 2697 | /* 2698 | uint64_t ELM327::findResponse(const uint8_t& service, const uint8_t& pid) 2699 | 2700 | Description: 2701 | ------------ 2702 | * Parses the buffered ELM327's response and returns the queried data 2703 | 2704 | Inputs: 2705 | ------- 2706 | * const uint8_t& service - The diagnostic service ID. 01 is "Show current data" 2707 | * const uint8_t& pid - The Parameter ID (PID) from the service 2708 | 2709 | Return: 2710 | ------- 2711 | * void 2712 | */ 2713 | uint64_t ELM327::findResponse() 2714 | { 2715 | uint8_t firstDatum = 0; 2716 | char header[7] = {'\0'}; 2717 | 2718 | if (longQuery) 2719 | { 2720 | header[0] = query[0] + 4; 2721 | header[1] = query[1]; 2722 | header[2] = query[2]; 2723 | header[3] = query[3]; 2724 | header[4] = query[4]; 2725 | header[5] = query[5]; 2726 | } 2727 | else 2728 | { 2729 | header[0] = query[0] + 4; 2730 | header[1] = query[1]; 2731 | 2732 | if (isMode0x22Query) // mode 0x22 responses always zero-pad the pid to 4 chars, even for a 2-char pid 2733 | { 2734 | header[2] = '0'; 2735 | header[3] = '0'; 2736 | header[4] = query[2]; 2737 | header[5] = query[3]; 2738 | } 2739 | else 2740 | { 2741 | header[2] = query[2]; 2742 | header[3] = query[3]; 2743 | } 2744 | } 2745 | 2746 | if (debugMode) 2747 | { 2748 | Serial.print(F("Expected response header: ")); 2749 | Serial.println(header); 2750 | } 2751 | 2752 | int8_t firstHeadIndex = nextIndex(payload, header, 1); 2753 | int8_t secondHeadIndex = nextIndex(payload, header, 2); 2754 | 2755 | if (firstHeadIndex >= 0) 2756 | { 2757 | if (longQuery | isMode0x22Query) 2758 | firstDatum = firstHeadIndex + 6; 2759 | else 2760 | firstDatum = firstHeadIndex + 4; 2761 | 2762 | // Some ELM327s (such as my own) respond with two 2763 | // "responses" per query. "numPayChars" represents the 2764 | // correct number of bytes returned by the ELM327 2765 | // regardless of how many "responses" were returned 2766 | if (secondHeadIndex >= 0) 2767 | { 2768 | if (debugMode) 2769 | Serial.println(F("Double response detected")); 2770 | 2771 | numPayChars = secondHeadIndex - firstDatum; 2772 | } 2773 | else 2774 | { 2775 | if (debugMode) 2776 | Serial.println(F("Single response detected")); 2777 | 2778 | numPayChars = strlen(payload) - firstDatum; 2779 | } 2780 | 2781 | response = 0; 2782 | for (uint8_t i = 0; i < numPayChars; i++) 2783 | { 2784 | uint8_t payloadIndex = firstDatum + i; 2785 | uint8_t bitsOffset = 4 * (numPayChars - i - 1); 2786 | 2787 | if (debugMode) 2788 | { 2789 | Serial.print(F("\tProcessing hex nibble: ")); 2790 | Serial.println(payload[payloadIndex]); 2791 | } 2792 | response = response | ((uint64_t)ctoi(payload[payloadIndex]) << bitsOffset); 2793 | } 2794 | 2795 | // It is useful to have the response bytes 2796 | // broken-out because some PID algorithms (standard 2797 | // and custom) require special operations for each 2798 | // byte returned 2799 | 2800 | responseByte_0 = response & 0xFF; 2801 | responseByte_1 = (response >> 8) & 0xFF; 2802 | responseByte_2 = (response >> 16) & 0xFF; 2803 | responseByte_3 = (response >> 24) & 0xFF; 2804 | responseByte_4 = (response >> 32) & 0xFF; 2805 | responseByte_5 = (response >> 40) & 0xFF; 2806 | responseByte_6 = (response >> 48) & 0xFF; 2807 | responseByte_7 = (response >> 56) & 0xFF; 2808 | 2809 | if (debugMode) 2810 | { 2811 | Serial.println(F("64-bit response: ")); 2812 | Serial.print(F("\tresponseByte_0: ")); 2813 | Serial.println(responseByte_0); 2814 | Serial.print(F("\tresponseByte_1: ")); 2815 | Serial.println(responseByte_1); 2816 | Serial.print(F("\tresponseByte_2: ")); 2817 | Serial.println(responseByte_2); 2818 | Serial.print(F("\tresponseByte_3: ")); 2819 | Serial.println(responseByte_3); 2820 | Serial.print(F("\tresponseByte_4: ")); 2821 | Serial.println(responseByte_4); 2822 | Serial.print(F("\tresponseByte_5: ")); 2823 | Serial.println(responseByte_5); 2824 | Serial.print(F("\tresponseByte_6: ")); 2825 | Serial.println(responseByte_6); 2826 | Serial.print(F("\tresponseByte_7: ")); 2827 | Serial.println(responseByte_7); 2828 | } 2829 | 2830 | return response; 2831 | } 2832 | 2833 | if (debugMode) 2834 | Serial.println(F("Response not detected")); 2835 | 2836 | return 0; 2837 | } 2838 | 2839 | /* 2840 | void ELM327::printError() 2841 | 2842 | Description: 2843 | ------------ 2844 | * Prints appropriate error description if an error has occurred 2845 | 2846 | Inputs: 2847 | ------- 2848 | * void 2849 | 2850 | Return: 2851 | ------- 2852 | * void 2853 | */ 2854 | void ELM327::printError() 2855 | { 2856 | Serial.print(F("Received: ")); 2857 | Serial.println(payload); 2858 | 2859 | if (nb_rx_state == ELM_SUCCESS) 2860 | Serial.println(F("ELM_SUCCESS")); 2861 | else if (nb_rx_state == ELM_NO_RESPONSE) 2862 | Serial.println(F("ERROR: ELM_NO_RESPONSE")); 2863 | else if (nb_rx_state == ELM_BUFFER_OVERFLOW) 2864 | Serial.println(F("ERROR: ELM_BUFFER_OVERFLOW")); 2865 | else if (nb_rx_state == ELM_UNABLE_TO_CONNECT) 2866 | Serial.println(F("ERROR: ELM_UNABLE_TO_CONNECT")); 2867 | else if (nb_rx_state == ELM_NO_DATA) 2868 | Serial.println(F("ERROR: ELM_NO_DATA")); 2869 | else if (nb_rx_state == ELM_STOPPED) 2870 | Serial.println(F("ERROR: ELM_STOPPED")); 2871 | else if (nb_rx_state == ELM_TIMEOUT) 2872 | Serial.println(F("ERROR: ELM_TIMEOUT")); 2873 | else if (nb_rx_state == ELM_BUFFER_OVERFLOW) 2874 | Serial.println(F("ERROR: BUFFER OVERFLOW")); 2875 | else if (nb_rx_state == ELM_GENERAL_ERROR) 2876 | Serial.println(F("ERROR: ELM_GENERAL_ERROR")); 2877 | else 2878 | Serial.println(F("No error detected")); 2879 | 2880 | delay(100); 2881 | } 2882 | 2883 | /* 2884 | float ELM327::batteryVoltage() 2885 | 2886 | Description: 2887 | ------------ 2888 | * Get the current vehicle battery voltage in Volts DC 2889 | 2890 | Inputs: 2891 | ------- 2892 | * void 2893 | 2894 | Return: 2895 | ------- 2896 | * float - vehicle battery voltage in VDC 2897 | */ 2898 | float ELM327::batteryVoltage() 2899 | { 2900 | if (nb_query_state == SEND_COMMAND) 2901 | { 2902 | sendCommand(READ_VOLTAGE); 2903 | nb_query_state = WAITING_RESP; 2904 | } 2905 | else if (nb_query_state == WAITING_RESP) 2906 | { 2907 | get_response(); 2908 | if (nb_rx_state == ELM_SUCCESS) 2909 | { 2910 | nb_query_state = SEND_COMMAND; // Reset the query state machine for next command 2911 | payload[strlen(payload) - 1] = '\0'; // Remove the last char ("V") from the payload value 2912 | 2913 | if (strncmp(payload, "ATRV", 4) == 0) 2914 | return (float)strtod(payload + 4, NULL); 2915 | else 2916 | return (float)strtod(payload, NULL); 2917 | } 2918 | else if (nb_rx_state != ELM_GETTING_MSG) 2919 | nb_query_state = SEND_COMMAND; // Error or timeout, so reset the query state machine for next command 2920 | } 2921 | return 0.0; 2922 | } 2923 | 2924 | /* 2925 | int8_t ELM327::get_vin_blocking(char *vin) 2926 | 2927 | Description: 2928 | ------------ 2929 | * Read Vehicle Identification Number (VIN). This is a blocking function. 2930 | 2931 | Inputs: 2932 | ------- 2933 | * char vin[] - pointer to c-string in which to store VIN 2934 | Note: (allocate memory for 18 character c-string in calling function) 2935 | 2936 | Return: 2937 | ------- 2938 | * int8_t - the ELM_XXX status of getting the VIN 2939 | */ 2940 | int8_t ELM327::get_vin_blocking(char vin[]) 2941 | { 2942 | char temp[3] = {0}; 2943 | char *idx; 2944 | uint8_t vin_counter = 0; 2945 | uint8_t ascii_val; 2946 | 2947 | if (debugMode) 2948 | Serial.println(F("Getting VIN...")); 2949 | 2950 | sendCommand("0902"); // VIN is command 0902 2951 | while (get_response() == ELM_GETTING_MSG) 2952 | ; 2953 | 2954 | // strcpy(payload, "0140:4902013144341:475030305235352:42313233343536"); 2955 | if (nb_rx_state == ELM_SUCCESS) 2956 | { 2957 | memset(vin, 0, 18); 2958 | // **** Decoding **** 2959 | if (strstr(payload, "490201")) 2960 | { 2961 | // OBD scanner provides this multiline response: 2962 | // 014 ==> 0x14 = 20 bytes following 2963 | // 0: 49 02 01 31 44 34 ==> 49 02 = Header. 01 = 1 VIN number in message. 31, 44, 34 = First 3 VIN digits 2964 | // 1: 47 50 30 30 52 35 35 ==> 47->35 next 7 VIN digits 2965 | // 2: 42 31 32 33 34 35 36 ==> 42->36 next 7 VIN digits 2966 | // 2967 | // The resulitng payload buffer is: 2968 | // "0140:4902013144341:475030305235352:42313233343536" ==> VIN="1D4GP00R55B123456" (17-digits) 2969 | idx = strstr(payload, "490201") + 6; // Pointer to first ASCII code digit of first VIN digit 2970 | // Loop over each pair of ASCII code digits. 17 VIN digits + 2 skipped line numbers = 19 loops 2971 | for (int i = 0; i < (19 * 2); i += 2) 2972 | { 2973 | temp[0] = *(idx + i); // Get first digit of ASCII code 2974 | temp[1] = *(idx + i + 1); // Get second digit of ASCII code 2975 | // No need to add string termination, temp[3] always == 0 2976 | 2977 | if (strstr(temp, ":")) 2978 | continue; // Skip the second "1:" and third "2:" line numbers 2979 | 2980 | ascii_val = strtol(temp, 0, 16); // Convert ASCII code to integer 2981 | snprintf(vin + vin_counter++, sizeof(uint8_t), "%c", ascii_val); // Convert ASCII code integer back to character 2982 | // Serial.printf("Chars %s, ascii_val=%d[dec] 0x%02hhx[hex] ==> VIN=%s\n", temp, ascii_val, ascii_val, vin); 2983 | } 2984 | } 2985 | if (debugMode) 2986 | { 2987 | Serial.print(F("VIN: ")); 2988 | Serial.println(vin); 2989 | } 2990 | } 2991 | else 2992 | { 2993 | if (debugMode) 2994 | { 2995 | Serial.println(F("No VIN response")); 2996 | printError(); 2997 | } 2998 | } 2999 | return nb_rx_state; 3000 | } 3001 | 3002 | /* 3003 | bool ELM327::resetDTC() 3004 | 3005 | Description: 3006 | ------------ 3007 | * Resets the stored DTCs in the ECU. This is a blocking function. 3008 | Note: The SAE spec requires that scan tools verify that a reset 3009 | is intended ("Are you sure?") before sending the mode 04 3010 | reset command to the vehicle. See p.32 of ELM327 datasheet. 3011 | 3012 | Inputs: 3013 | ------- 3014 | * void 3015 | 3016 | Return: 3017 | ------- 3018 | * bool - Indicates the success (or not) of the reset command. 3019 | */ 3020 | bool ELM327::resetDTC() 3021 | { 3022 | if (sendCommand_Blocking("04") == ELM_SUCCESS) 3023 | { 3024 | if (strstr(payload, "44") != NULL) 3025 | { 3026 | if (debugMode) 3027 | Serial.println(F("ELMduino: DTC successfully reset.")); 3028 | 3029 | return true; 3030 | } 3031 | } 3032 | else 3033 | { 3034 | if (debugMode) 3035 | Serial.println(F("ELMduino: Resetting DTC codes failed.")); 3036 | } 3037 | 3038 | return false; 3039 | } 3040 | 3041 | /* 3042 | void ELM327::currentDTCCodes(const bool& isBlocking) 3043 | 3044 | Description: 3045 | ------------ 3046 | * Get the list of current DTC codes. This method is blocking by default, but can be run 3047 | in non-blocking mode if desired with optional boolean argument. Typical use involves 3048 | calling the monitorStatus() function first to get the number of DTC current codes stored, 3049 | then calling this function to retrieve those codes. This would not typically 3050 | be done in NB mode in a loop, but optional NB mode is supported. 3051 | 3052 | * To check the results of this query, inspect the DTC_Response struct: DTC_Response.codesFound 3053 | will contain the number of codes present and DTC_Response.codes is an array 3054 | of 5 char codes that were retrieved. 3055 | 3056 | Inputs: 3057 | ------- 3058 | * bool isBlocking - optional arg to set (non)blocking mode - defaults to true / blocking mode 3059 | 3060 | Return: 3061 | ------- 3062 | * void 3063 | */ 3064 | void ELM327::currentDTCCodes(const bool& isBlocking) 3065 | { 3066 | char *idx; 3067 | char codeType = '\0'; 3068 | char codeNumber[5] = {0}; 3069 | char temp[6] = {0}; 3070 | 3071 | if (isBlocking) // In blocking mode, we loop here until get_response() is past ELM_GETTING_MSG state 3072 | { 3073 | sendCommand("03"); // Check DTC is always Service 03 with no PID 3074 | while (get_response() == ELM_GETTING_MSG) 3075 | ; 3076 | } 3077 | else 3078 | { 3079 | if (nb_query_state == SEND_COMMAND) 3080 | { 3081 | sendCommand("03"); 3082 | nb_query_state = WAITING_RESP; 3083 | } 3084 | 3085 | else if (nb_query_state == WAITING_RESP) 3086 | get_response(); 3087 | } 3088 | 3089 | if (nb_rx_state == ELM_SUCCESS) 3090 | { 3091 | nb_query_state = SEND_COMMAND; // Reset the query state machine for next command 3092 | memset(DTC_Response.codes, 0, DTC_CODE_LEN * DTC_MAX_CODES); 3093 | 3094 | if (strstr(payload, "43") != NULL) // Successful response to Mode 03 request 3095 | { 3096 | // OBD scanner will provide a response that contains one or more lines indicating the codes present. 3097 | // Each response line will start with "43" indicating it is a response to a Mode 03 request. 3098 | // See p. 31 of ELM327 datasheet for details and lookup table of code types. 3099 | 3100 | uint8_t codesFound = strlen(payload) / 8; // Each code found returns 8 chars starting with "43" 3101 | idx = strstr(payload, "43") + 4; // Pointer to first DTC code digit (third char in the response) 3102 | 3103 | if (codesFound > DTC_MAX_CODES) // I don't think the ELM is capable of returning 3104 | { // more than 0xF (16) codes, but just in case... 3105 | codesFound = DTC_MAX_CODES; 3106 | Serial.print(F("DTC response truncated at ")); 3107 | Serial.print(DTC_MAX_CODES); 3108 | Serial.println(F(" codes.")); 3109 | } 3110 | 3111 | DTC_Response.codesFound = codesFound; 3112 | 3113 | for (int i = 0; i < codesFound; i++) 3114 | { 3115 | memset(temp, 0, sizeof(temp)); 3116 | memset(codeNumber, 0, sizeof(codeNumber)); 3117 | 3118 | codeType = *idx; // Get first digit of second byte 3119 | codeNumber[0] = *(idx + 1); // Get second digit of second byte 3120 | codeNumber[1] = *(idx + 2); // Get first digit of third byte 3121 | codeNumber[2] = *(idx + 3); // Get second digit of third byte 3122 | 3123 | switch (codeType) // Set the correct type prefix for the code 3124 | { 3125 | case '0': 3126 | strcat(temp, "P0"); 3127 | break; 3128 | 3129 | case '1': 3130 | strcat(temp, "P1"); 3131 | break; 3132 | 3133 | case '2': 3134 | strcat(temp, "P2"); 3135 | break; 3136 | case '3': 3137 | strcat(temp, "P3"); 3138 | break; 3139 | 3140 | case '4': 3141 | strcat(temp, "C0"); 3142 | break; 3143 | 3144 | case '5': 3145 | strcat(temp, "C1"); 3146 | break; 3147 | 3148 | case '6': 3149 | strcat(temp, "C2"); 3150 | break; 3151 | 3152 | case '7': 3153 | strcat(temp, "C3"); 3154 | break; 3155 | 3156 | case '8': 3157 | strcat(temp, "B0"); 3158 | break; 3159 | 3160 | case '9': 3161 | strcat(temp, "B1"); 3162 | break; 3163 | 3164 | case 'A': 3165 | strcat(temp, "B2"); 3166 | break; 3167 | 3168 | case 'B': 3169 | strcat(temp, "B3"); 3170 | break; 3171 | 3172 | case 'C': 3173 | strcat(temp, "U0"); 3174 | break; 3175 | 3176 | case 'D': 3177 | strcat(temp, "U1"); 3178 | break; 3179 | 3180 | case 'E': 3181 | strcat(temp, "U2"); 3182 | break; 3183 | 3184 | case 'F': 3185 | strcat(temp, "U3"); 3186 | break; 3187 | 3188 | default: 3189 | break; 3190 | } 3191 | 3192 | strcat(temp, codeNumber); // Append the code number to the prefix 3193 | strcpy(DTC_Response.codes[i], temp); // Add the fully parsed code to the list (array) 3194 | idx = idx + 8; // reset idx to start of next code 3195 | 3196 | if (debugMode) 3197 | { 3198 | Serial.print(F("ELMduino: Found code: ")); 3199 | Serial.println(temp); 3200 | } 3201 | } 3202 | } 3203 | else 3204 | { 3205 | if (debugMode) 3206 | { 3207 | Serial.println(F("ELMduino: DTC response received with no valid data.")); 3208 | } 3209 | } 3210 | return; 3211 | } 3212 | else if (nb_rx_state != ELM_GETTING_MSG) 3213 | { 3214 | nb_query_state = SEND_COMMAND; // Error or timeout, so reset the query state machine for next command 3215 | 3216 | if (debugMode) 3217 | { 3218 | Serial.println(F("ELMduino: Getting current DTC codes failed.")); 3219 | printError(); 3220 | } 3221 | } 3222 | } 3223 | 3224 | /* 3225 | bool ELM327::isPidSupported(uint8_t pid) 3226 | 3227 | Description: 3228 | ------------ 3229 | * Checks if a particular PID is supported by the connected ECU. 3230 | 3231 | * This is a convenience method that selects the correct supportedPIDS_xx_xx() query and parses 3232 | the bit-encoded result, returning a simple Boolean value indicating PID support from the ECU. 3233 | 3234 | Inputs: 3235 | ------- 3236 | * uint8_t pid - the PID to check for support. 3237 | 3238 | Return: 3239 | ------- 3240 | * bool - Whether or not the queried PID is supported by the ECU. 3241 | */ 3242 | bool ELM327::isPidSupported(uint8_t pid) 3243 | { 3244 | uint8_t pidInterval = (pid / PID_INTERVAL_OFFSET) * PID_INTERVAL_OFFSET; 3245 | 3246 | switch (pidInterval) 3247 | { 3248 | case SUPPORTED_PIDS_1_20: 3249 | supportedPIDs_1_20(); 3250 | break; 3251 | 3252 | case SUPPORTED_PIDS_21_40: 3253 | supportedPIDs_21_40(); 3254 | pid = (pid - SUPPORTED_PIDS_21_40); 3255 | break; 3256 | 3257 | case SUPPORTED_PIDS_41_60: 3258 | supportedPIDs_41_60(); 3259 | pid = (pid - SUPPORTED_PIDS_41_60); 3260 | break; 3261 | 3262 | case SUPPORTED_PIDS_61_80: 3263 | supportedPIDs_61_80(); 3264 | pid = (pid - SUPPORTED_PIDS_61_80); 3265 | break; 3266 | 3267 | default: 3268 | break; 3269 | } 3270 | 3271 | if (nb_rx_state == ELM_SUCCESS) 3272 | { 3273 | return ((response >> (32 - pid)) & 0x1); 3274 | } 3275 | return false; 3276 | } 3277 | 3278 | double ELM327::calculator_0C() { 3279 | return (double)((response_A << 8) | response_B)/4; 3280 | } 3281 | 3282 | double ELM327::calculator_10() { 3283 | return (double)((response_A << 8) | response_B)/100; 3284 | } 3285 | 3286 | double ELM327::calculator_14(){ 3287 | return (double)(response_A/200) ; 3288 | } 3289 | 3290 | double ELM327::calculator_1F() { 3291 | return (double)((response_A << 8) | response_B); 3292 | } 3293 | 3294 | double ELM327::calculator_22() { 3295 | return (double) ((response_A << 8) | response_B) * 0.079; 3296 | } 3297 | 3298 | double ELM327::calculator_23() { 3299 | return (double) ((response_A << 8) | response_B) * 10; 3300 | } 3301 | 3302 | double ELM327::calculator_32() 3303 | { 3304 | return (double) ((int16_t)((response_A << 8) | response_B)) / 4.0; 3305 | } 3306 | 3307 | double ELM327::calculator_3C() { 3308 | return (double) (((response_A << 8) | response_B) / 10) - 40; 3309 | } 3310 | 3311 | double ELM327::calculator_42() { 3312 | return (double) ((response_A << 8) | response_B) / 1000; 3313 | } 3314 | 3315 | double ELM327::calculator_43() { 3316 | return (double) ((response_A << 8) | response_B) * (100.0 / 255.0); 3317 | } 3318 | 3319 | double ELM327::calculator_44() { 3320 | return ((double) ((response_A << 8) | response_B) * 2.0) / 65536.0; 3321 | } 3322 | 3323 | double ELM327::calculator_4F() { 3324 | return (double) (response_A); 3325 | } 3326 | 3327 | double ELM327::calculator_50() { 3328 | return (double) (response_A * 10.0); 3329 | } 3330 | 3331 | double ELM327::calculator_53() { 3332 | return (double) ((response_A << 8) | response_B) / 200; 3333 | } 3334 | 3335 | double ELM327::calculator_54() { 3336 | return (double) ((int16_t)((response_A << 8) | response_B)); 3337 | } 3338 | 3339 | double ELM327::calculator_55() { 3340 | return ((double) response_A * (100.0 / 128.0)) - 100.0; 3341 | } 3342 | 3343 | //calc 23 3344 | double ELM327::calculator_59() { 3345 | return (double) ((response_A << 8) | response_B) * 10; 3346 | } 3347 | 3348 | double ELM327::calculator_5D() { 3349 | return (double) (((response_A << 8) | response_B) / 128) - 210; 3350 | } 3351 | 3352 | double ELM327::calculator_5E() { 3353 | return (double) ((response_A << 8) | response_B) / 20; 3354 | } 3355 | 3356 | double ELM327::calculator_61() { 3357 | return (double) response_A - 125; 3358 | } 3359 | -------------------------------------------------------------------------------- /src/ELMduino.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Arduino.h" 3 | 4 | //-------------------------------------------------------------------------------------// 5 | // Protocol IDs 6 | //-------------------------------------------------------------------------------------// 7 | const char AUTOMATIC = '0'; 8 | const char SAE_J1850_PWM_41_KBAUD = '1'; 9 | const char SAE_J1850_PWM_10_KBAUD = '2'; 10 | const char ISO_9141_5_BAUD_INIT = '3'; 11 | const char ISO_14230_5_BAUD_INIT = '4'; 12 | const char ISO_14230_FAST_INIT = '5'; 13 | const char ISO_15765_11_BIT_500_KBAUD = '6'; 14 | const char ISO_15765_29_BIT_500_KBAUD = '7'; 15 | const char ISO_15765_11_BIT_250_KBAUD = '8'; 16 | const char ISO_15765_29_BIT_250_KBAUD = '9'; 17 | const char SAE_J1939_29_BIT_250_KBAUD = 'A'; 18 | const char USER_1_CAN = 'B'; 19 | const char USER_2_CAN = 'C'; 20 | 21 | //-------------------------------------------------------------------------------------// 22 | // PIDs (https://en.wikipedia.org/wiki/OBD-II_PIDs) 23 | //-------------------------------------------------------------------------------------// 24 | constexpr uint8_t SERVICE_01 = 1; 25 | constexpr uint8_t SERVICE_02 = 2; 26 | constexpr uint8_t SERVICE_03 = 3; 27 | constexpr uint8_t PID_INTERVAL_OFFSET = 0x20; 28 | 29 | 30 | constexpr uint8_t SUPPORTED_PIDS_1_20 = 0; // 0x00 - bit encoded 31 | constexpr uint8_t MONITOR_STATUS_SINCE_DTC_CLEARED = 1; // 0x01 - bit encoded 32 | constexpr uint8_t FREEZE_DTC = 2; // 0x02 - 33 | constexpr uint8_t FUEL_SYSTEM_STATUS = 3; // 0x03 - bit encoded 34 | constexpr uint8_t ENGINE_LOAD = 4; // 0x04 - % 35 | constexpr uint8_t ENGINE_COOLANT_TEMP = 5; // 0x05 - °C 36 | constexpr uint8_t SHORT_TERM_FUEL_TRIM_BANK_1 = 6; // 0x06 - % 37 | constexpr uint8_t LONG_TERM_FUEL_TRIM_BANK_1 = 7; // 0x07 - % 38 | constexpr uint8_t SHORT_TERM_FUEL_TRIM_BANK_2 = 8; // 0x08 - % 39 | constexpr uint8_t LONG_TERM_FUEL_TRIM_BANK_2 = 9; // 0x09 - % 40 | constexpr uint8_t FUEL_PRESSURE = 10; // 0x0A - kPa 41 | constexpr uint8_t INTAKE_MANIFOLD_ABS_PRESSURE = 11; // 0x0B - kPa 42 | constexpr uint8_t ENGINE_RPM = 12; // 0x0C - rpm 43 | constexpr uint8_t VEHICLE_SPEED = 13; // 0x0D - km/h 44 | constexpr uint8_t TIMING_ADVANCE = 14; // 0x0E - ° before TDC 45 | constexpr uint8_t INTAKE_AIR_TEMP = 15; // 0x0F - °C 46 | constexpr uint8_t MAF_FLOW_RATE = 16; // 0x10 - g/s 47 | constexpr uint8_t THROTTLE_POSITION = 17; // 0x11 - % 48 | constexpr uint8_t COMMANDED_SECONDARY_AIR_STATUS = 18; // 0x12 - bit encoded 49 | constexpr uint8_t OXYGEN_SENSORS_PRESENT_2_BANKS = 19; // 0x13 - bit encoded 50 | constexpr uint8_t OXYGEN_SENSOR_1_A = 20; // 0x14 - V % 51 | constexpr uint8_t OXYGEN_SENSOR_2_A = 21; // 0x15 - V % 52 | constexpr uint8_t OXYGEN_SENSOR_3_A = 22; // 0x16 - V % 53 | constexpr uint8_t OXYGEN_SENSOR_4_A = 23; // 0x17 - V % 54 | constexpr uint8_t OXYGEN_SENSOR_5_A = 24; // 0x18 - V % 55 | constexpr uint8_t OXYGEN_SENSOR_6_A = 25; // 0x19 - V % 56 | constexpr uint8_t OXYGEN_SENSOR_7_A = 26; // 0x1A - V % 57 | constexpr uint8_t OXYGEN_SENSOR_8_A = 27; // 0x1B - V % 58 | constexpr uint8_t OBD_STANDARDS = 28; // 0x1C - bit encoded 59 | constexpr uint8_t OXYGEN_SENSORS_PRESENT_4_BANKS = 29; // 0x1D - bit encoded 60 | constexpr uint8_t AUX_INPUT_STATUS = 30; // 0x1E - bit encoded 61 | constexpr uint8_t RUN_TIME_SINCE_ENGINE_START = 31; // 0x1F - sec 62 | 63 | constexpr uint8_t SUPPORTED_PIDS_21_40 = 32; // 0x20 - bit encoded 64 | constexpr uint8_t DISTANCE_TRAVELED_WITH_MIL_ON = 33; // 0x21 - km 65 | constexpr uint8_t FUEL_RAIL_PRESSURE = 34; // 0x22 - kPa 66 | constexpr uint8_t FUEL_RAIL_GUAGE_PRESSURE = 35; // 0x23 - kPa 67 | constexpr uint8_t OXYGEN_SENSOR_1_B = 36; // 0x24 - ratio V 68 | constexpr uint8_t OXYGEN_SENSOR_2_B = 37; // 0x25 - ratio V 69 | constexpr uint8_t OXYGEN_SENSOR_3_B = 38; // 0x26 - ratio V 70 | constexpr uint8_t OXYGEN_SENSOR_4_B = 39; // 0x27 - ratio V 71 | constexpr uint8_t OXYGEN_SENSOR_5_B = 40; // 0x28 - ratio V 72 | constexpr uint8_t OXYGEN_SENSOR_6_B = 41; // 0x29 - ratio V 73 | constexpr uint8_t OXYGEN_SENSOR_7_B = 42; // 0x2A - ratio V 74 | constexpr uint8_t OXYGEN_SENSOR_8_B = 43; // 0x2B - ratio V 75 | constexpr uint8_t COMMANDED_EGR = 44; // 0x2C - % 76 | constexpr uint8_t EGR_ERROR = 45; // 0x2D - % 77 | constexpr uint8_t COMMANDED_EVAPORATIVE_PURGE = 46; // 0x2E - % 78 | constexpr uint8_t FUEL_TANK_LEVEL_INPUT = 47; // 0x2F - % 79 | constexpr uint8_t WARM_UPS_SINCE_CODES_CLEARED = 48; // 0x30 - count 80 | constexpr uint8_t DIST_TRAV_SINCE_CODES_CLEARED = 49; // 0x31 - km 81 | constexpr uint8_t EVAP_SYSTEM_VAPOR_PRESSURE = 50; // 0x32 - Pa 82 | constexpr uint8_t ABS_BAROMETRIC_PRESSURE = 51; // 0x33 - kPa 83 | constexpr uint8_t OXYGEN_SENSOR_1_C = 52; // 0x34 - ratio mA 84 | constexpr uint8_t OXYGEN_SENSOR_2_C = 53; // 0x35 - ratio mA 85 | constexpr uint8_t OXYGEN_SENSOR_3_C = 54; // 0x36 - ratio mA 86 | constexpr uint8_t OXYGEN_SENSOR_4_C = 55; // 0x37 - ratio mA 87 | constexpr uint8_t OXYGEN_SENSOR_5_C = 56; // 0x38 - ratio mA 88 | constexpr uint8_t OXYGEN_SENSOR_6_C = 57; // 0x39 - ratio mA 89 | constexpr uint8_t OXYGEN_SENSOR_7_C = 58; // 0x3A - ratio mA 90 | constexpr uint8_t OXYGEN_SENSOR_8_C = 59; // 0x3B - ratio mA 91 | constexpr uint8_t CATALYST_TEMP_BANK_1_SENSOR_1 = 60; // 0x3C - °C 92 | constexpr uint8_t CATALYST_TEMP_BANK_2_SENSOR_1 = 61; // 0x3D - °C 93 | constexpr uint8_t CATALYST_TEMP_BANK_1_SENSOR_2 = 62; // 0x3E - °C 94 | constexpr uint8_t CATALYST_TEMP_BANK_2_SENSOR_2 = 63; // 0x3F - °C 95 | 96 | constexpr uint8_t SUPPORTED_PIDS_41_60 = 64; // 0x40 - bit encoded 97 | constexpr uint8_t MONITOR_STATUS_THIS_DRIVE_CYCLE = 65; // 0x41 - bit encoded 98 | constexpr uint8_t CONTROL_MODULE_VOLTAGE = 66; // 0x42 - V 99 | constexpr uint8_t ABS_LOAD_VALUE = 67; // 0x43 - % 100 | constexpr uint8_t FUEL_AIR_COMMANDED_EQUIV_RATIO = 68; // 0x44 - ratio 101 | constexpr uint8_t RELATIVE_THROTTLE_POSITION = 69; // 0x45 - % 102 | constexpr uint8_t AMBIENT_AIR_TEMP = 70; // 0x46 - °C 103 | constexpr uint8_t ABS_THROTTLE_POSITION_B = 71; // 0x47 - % 104 | constexpr uint8_t ABS_THROTTLE_POSITION_C = 72; // 0x48 - % 105 | constexpr uint8_t ABS_THROTTLE_POSITION_D = 73; // 0x49 - % 106 | constexpr uint8_t ABS_THROTTLE_POSITION_E = 74; // 0x4A - % 107 | constexpr uint8_t ABS_THROTTLE_POSITION_F = 75; // 0x4B - % 108 | constexpr uint8_t COMMANDED_THROTTLE_ACTUATOR = 76; // 0x4C - % 109 | constexpr uint8_t TIME_RUN_WITH_MIL_ON = 77; // 0x4D - min 110 | constexpr uint8_t TIME_SINCE_CODES_CLEARED = 78; // 0x4E - min 111 | constexpr uint8_t MAX_VALUES_EQUIV_V_I_PRESSURE = 79; // 0x4F - ratio V mA kPa 112 | constexpr uint8_t MAX_MAF_RATE = 80; // 0x50 - g/s 113 | constexpr uint8_t FUEL_TYPE = 81; // 0x51 - ref table 114 | constexpr uint8_t ETHANOL_FUEL_PERCENT = 82; // 0x52 - % 115 | constexpr uint8_t ABS_EVAP_SYS_VAPOR_PRESSURE = 83; // 0x53 - kPa 116 | constexpr uint8_t EVAP_SYS_VAPOR_PRESSURE = 84; // 0x54 - Pa 117 | constexpr uint8_t SHORT_TERM_SEC_OXY_SENS_TRIM_1_3 = 85; // 0x55 - % 118 | constexpr uint8_t LONG_TERM_SEC_OXY_SENS_TRIM_1_3 = 86; // 0x56 - % 119 | constexpr uint8_t SHORT_TERM_SEC_OXY_SENS_TRIM_2_4 = 87; // 0x57 - % 120 | constexpr uint8_t LONG_TERM_SEC_OXY_SENS_TRIM_2_4 = 88; // 0x58 - % 121 | constexpr uint8_t FUEL_RAIL_ABS_PRESSURE = 89; // 0x59 - kPa 122 | constexpr uint8_t RELATIVE_ACCELERATOR_PEDAL_POS = 90; // 0x5A - % 123 | constexpr uint8_t HYBRID_BATTERY_REMAINING_LIFE = 91; // 0x5B - % 124 | constexpr uint8_t ENGINE_OIL_TEMP = 92; // 0x5C - °C 125 | constexpr uint8_t FUEL_INJECTION_TIMING = 93; // 0x5D - ° 126 | constexpr uint8_t ENGINE_FUEL_RATE = 94; // 0x5E - L/h 127 | constexpr uint8_t EMISSION_REQUIREMENTS = 95; // 0x5F - bit encoded 128 | 129 | constexpr uint8_t SUPPORTED_PIDS_61_80 = 96; // 0x60 - bit encoded 130 | constexpr uint8_t DEMANDED_ENGINE_PERCENT_TORQUE = 97; // 0x61 - % 131 | constexpr uint8_t ACTUAL_ENGINE_TORQUE = 98; // 0x62 - % 132 | constexpr uint8_t ENGINE_REFERENCE_TORQUE = 99; // 0x63 - Nm 133 | constexpr uint8_t ENGINE_PERCENT_TORQUE_DATA = 100; // 0x64 - % 134 | constexpr uint8_t AUX_INPUT_OUTPUT_SUPPORTED = 101; // 0x65 - bit encoded 135 | 136 | //-------------------------------------------------------------------------------------// 137 | // AT commands (https://www.sparkfun.com/datasheets/Widgets/ELM327_AT_Commands.pdf) 138 | //-------------------------------------------------------------------------------------// 139 | const char * const DISP_DEVICE_DESCRIPT = "AT @1"; // General 140 | const char * const DISP_DEVICE_ID = "AT @2"; // General 141 | const char * const STORE_DEVICE_ID = "AT @3 %s"; // General 142 | const char * const REPEAT_LAST_COMMAND = "AT \r"; // General 143 | const char * const ALLOW_LONG_MESSAGES = "AT AL"; // General 144 | const char * const AUTOMATIC_RECEIVE = "AT AR"; // OBD 145 | const char * const ADAPTIVE_TIMING_OFF = "AT AT0"; // OBD 146 | const char * const ADAPTIVE_TIMING_AUTO_1 = "AT AT1"; // OBD 147 | const char * const ADAPTIVE_TIMING_AUTO_2 = "AT AT2"; // OBD 148 | const char * const DUMP_BUFFER = "AT BD"; // OBD 149 | const char * const BYPASS_INIT_SEQUENCE = "AT BI"; // OBD 150 | const char * const TRY_BAUD_DIVISOR = "AT BRD %02d"; // General 151 | const char * const SET_HANDSHAKE_TIMEOUT = "AT BRT %02d"; // General 152 | const char * const CAN_AUTO_FORMAT_OFF = "AT CAF0"; // CAN 153 | const char * const CAN_AUTO_FORMAT_ON = "AT CAF1"; // CAN 154 | const char * const CAN_EXTENDED_ADDRESS_OFF = "AT CEA"; // CAN 155 | const char * const USE_CAN_EXTENDED_ADDRESS = "AT CEA %02d"; // CAN 156 | const char * const SET_ID_FILTER = "AT CF %s"; // CAN 157 | const char * const CAN_FLOW_CONTROL_OFF = "AT CFC0"; // CAN 158 | const char * const CAN_FLOW_CONTROL_ON = "AT CFC1"; // CAN 159 | const char * const SET_ID_MASK = "AT CM %s"; // CAN 160 | const char * const SET_CAN_PRIORITY = "AT CP %02d"; // CAN 161 | const char * const SHOW_CAN_STATUS = "AT CS"; // CAN 162 | const char * const CAN_SILENT_MODE_OFF = "AT CSM0"; // CAN 163 | const char * const CAN_SILENT_MODE_ON = "AT CSM1"; // CAN 164 | const char * const CALIBRATE_VOLTAGE_CUSTOM = "AT CV %04d"; // Volts 165 | const char * const RESTORE_CV_TO_FACTORY = "AT CV 0000"; // Volts 166 | const char * const SET_ALL_TO_DEFAULTS = "AT D"; // General 167 | const char * const DISP_DLC_OFF = "AT D0"; // CAN 168 | const char * const DISP_DLC_ON = "AT D1"; // CAN 169 | const char * const MONITOR_FOR_DM1_MESSAGES = "AT DM1"; // J1939 170 | const char * const DISP_CURRENT_PROTOCOL = "AT DP"; // OBD 171 | const char * const DISP_CURRENT_PROTOCOL_NUM = "AT DPN"; // OBD 172 | const char * const ECHO_OFF = "AT E0"; // General 173 | const char * const ECHO_ON = "AT E1"; // General 174 | const char * const FLOW_CONTROL_SET_DATA_TO = "AT FC SD %s"; // CAN 175 | const char * const FLOW_CONTROL_SET_HEAD_TO = "AT FC SH %s"; // CAN 176 | const char * const FLOW_CONTROL_SET_MODE_TO = "AT FC SM %c"; // CAN 177 | const char * const FORGE_EVENTS = "AT FE"; // General 178 | const char * const PERFORM_FAST_INIT = "AT FI"; // ISO 179 | const char * const HEADERS_OFF = "AT H0"; // OBD 180 | const char * const HEADERS_ON = "AT H1"; // OBD 181 | const char * const DISP_ID = "AT I"; // General 182 | const char * const SET_ISO_BAUD_10400 = "AT IB 10"; // ISO 183 | const char * const SET_ISO_BAUD_4800 = "AT IB 48"; // ISO 184 | const char * const SET_ISO_BAUD_9600 = "AT IB 96"; // ISO 185 | const char * const IFR_VAL_FROM_HEADER = "AT IFR H"; // J1850 186 | const char * const IFR_VAL_FROM_SOURCE = "AT IFR S"; // J1850 187 | const char * const IFRS_OFF = "AT IFR0"; // J1850 188 | const char * const IFRS_AUTO = "AT IFR1"; // J1850 189 | const char * const IFRS_ON = "AT IFR2"; // J1850 190 | const char * const IREAD_IGNMON_INPUT_LEVEL = "AT IGN"; // Other 191 | const char * const SET_ISO_SLOW_INIT_ADDRESS = "AT IIA %02d"; // ISO 192 | const char * const USE_J1939_ELM_DATA_FORMAT = "AT JE"; // J1850 193 | const char * const J1939_HEAD_FORMAT_OFF = "AT JHF0"; // J1850 194 | const char * const J1939_HEAD_FORMAT_ON = "AT JHF1"; // J1850 195 | const char * const USE_J1939_SAE_DATA_FORMAT = "AT JS"; // J1850 196 | const char * const SET_J1939_TIMER_X_TO_1X = "AT JTM1"; // J1850 197 | const char * const SET_J1939_TIMER_X_TO_5X = "AT JTM5"; // J1850 198 | const char * const DISP_KEY_WORDS = "AT KW"; // ISO 199 | const char * const KEY_WORD_CHECKING_OFF = "AT KW0"; // ISO 200 | const char * const KEY_WORD_CHECKING_ON = "AT KW1"; // ISO 201 | const char * const LINEFEEDS_OFF = "AT L0"; // General 202 | const char * const LINEFEEDS_ON = "AT L1"; // General 203 | const char * const LOW_POWER_MODE = "AT LP"; // General 204 | const char * const MEMORY_OFF = "AT M0"; // General 205 | const char * const MEMORY_ON = "AT M1"; // General 206 | const char * const MONITOR_ALL = "AT MA"; // OBD 207 | const char * const MONITOR_FOR_PGN = "AT MP %s"; // J1939 208 | const char * const MONITOR_FOR_RECEIVER = "AT MR %02d"; // OBD 209 | const char * const MONITOR_FOR_TRANSMITTER = "AT MT %02d"; // OBD 210 | const char * const NORMAL_LENGTH_MESSAGES = "AT NL"; // OBD 211 | const char * const SET_PROTO_OPTIONS_AND_BAUD = "AT PB %s"; // OBD 212 | const char * const PROTOCOL_CLOSE = "AT PC"; // OBD 213 | const char * const ALL_PROG_PARAMS_OFF = "AT PP FF OFF";// PPs 214 | const char * const ALL_PROG_PARAMS_ON = "AT PP FF ON"; // PPs 215 | const char * const SET_PROG_PARAM_OFF = "AT PP %s OFF";// PPs 216 | const char * const SET_PROG_PARAM_ON = "AT PP %s ON"; // PPs 217 | const char * const SET_PROG_PARAM_VAL = "AT PP %s SV %s"; // PPs 218 | const char * const DISP_PP_SUMMARY = "AT PPS"; // PPs 219 | const char * const RESPONSES_OFF = "AT R0"; // OBD 220 | const char * const RESPONSES_ON = "AT R1"; // OBD 221 | const char * const SET_RECEIVE_ADDRESS_TO = "AT RA %02d"; // OBD 222 | const char * const READ_STORED_DATA = "AT RD"; // General 223 | const char * const SEND_RTR_MESSAGE = "AT RTR"; // CAN 224 | const char * const READ_VOLTAGE = "AT RV"; // Volts 225 | const char * const PRINTING_SPACES_OFF = "AT S0"; // OBD 226 | const char * const PRINTING_SPACES_ON = "AT S1"; // OBD 227 | const char * const STORE_DATA_BYTE = "AT SD "; // General 228 | const char * const SET_HEADER = "AT SH %s"; // OBD 229 | const char * const PERFORM_SLOW_INIT = "AT SI"; // ISO 230 | const char * const SET_PROTOCOL_TO_AUTO_H_SAVE = "AT SP A%c"; // OBD 231 | const char * const SET_PROTOCOL_TO_H_SAVE = "AT SP %c"; // OBD 232 | const char * const SET_PROTOCOL_TO_AUTO_SAVE = "AT SP 00"; // OBD 233 | const char * const SET_REC_ADDRESS = "AT SR %02d"; // OBD 234 | const char * const SET_STANDARD_SEARCH_ORDER = "AT SS"; // OBD 235 | const char * const SET_TIMEOUT_TO_H_X_4MS = "AT ST %02d"; // OBD 236 | const char * const SET_WAKEUP_TO_H_X_20MS = "AT SW %02d"; // ISO 237 | const char * const SET_TESTER_ADDRESS_TO = "AT TA %02d"; // OBD 238 | const char * const TRY_PROT_H_AUTO_SEARCH = "AT TP A%c"; // OBD 239 | const char * const TRY_PROT_H = "AT TP %c"; // OBD 240 | const char * const VARIABLE_DLC_OFF = "AT V0"; // CAN 241 | const char * const VARIABLE_DLC_ON = "AT V1"; // CAN 242 | const char * const SET_WAKEUP_MESSAGE = "AT WM"; // ISO 243 | const char * const WARM_START = "AT WS"; // General 244 | const char * const RESET_ALL = "AT Z"; // General 245 | 246 | //-------------------------------------------------------------------------------------// 247 | // Class constants 248 | //-------------------------------------------------------------------------------------// 249 | constexpr float KPH_MPH_CONVERT = 0.6213711922; 250 | constexpr int8_t QUERY_LEN = 9; 251 | constexpr int8_t ELM_SUCCESS = 0; 252 | constexpr int8_t ELM_NO_RESPONSE = 1; 253 | constexpr int8_t ELM_BUFFER_OVERFLOW = 2; 254 | constexpr int8_t ELM_GARBAGE = 3; 255 | constexpr int8_t ELM_UNABLE_TO_CONNECT = 4; 256 | constexpr int8_t ELM_NO_DATA = 5; 257 | constexpr int8_t ELM_STOPPED = 6; 258 | constexpr int8_t ELM_TIMEOUT = 7; 259 | constexpr int8_t ELM_GETTING_MSG = 8; 260 | constexpr int8_t ELM_MSG_RXD = 9; 261 | constexpr int8_t ELM_GENERAL_ERROR = -1; 262 | constexpr uint8_t DTC_CODE_LEN = 6; 263 | constexpr uint8_t DTC_MAX_CODES = 16; 264 | 265 | const char * const RESPONSE_OK = "OK"; 266 | const char * const RESPONSE_UNABLE_TO_CONNECT = "UNABLETOCONNECT"; 267 | const char * const RESPONSE_NO_DATA = "NODATA"; 268 | const char * const RESPONSE_STOPPED = "STOPPED"; 269 | const char * const RESPONSE_ERROR = "ERROR"; 270 | 271 | // Non-blocking (NB) command states 272 | typedef enum { SEND_COMMAND, 273 | WAITING_RESP, 274 | RESPONSE_RECEIVED, 275 | DECODED_OK, 276 | ERROR } obd_cmd_states; 277 | 278 | // Pointers to existing response bytes, to be used for new calculators without breaking 279 | // backward compatability with code that may use the above response bytes. 280 | static byte response_A; 281 | static byte response_B; 282 | static byte response_C; 283 | static byte response_D; 284 | static byte response_E; 285 | static byte response_F; 286 | static byte response_G; 287 | static byte response_H; 288 | 289 | 290 | class ELM327 291 | { 292 | public: 293 | Stream* elm_port; 294 | 295 | bool connected = false; 296 | bool specifyNumResponses = true; 297 | bool debugMode; 298 | char* payload; 299 | uint16_t PAYLOAD_LEN; 300 | int8_t nb_rx_state = ELM_GETTING_MSG; 301 | uint64_t response = 0; 302 | uint16_t recBytes; 303 | uint8_t numPayChars; 304 | uint16_t timeout_ms; 305 | byte responseByte_0; 306 | byte responseByte_1; 307 | byte responseByte_2; 308 | byte responseByte_3; 309 | byte responseByte_4; 310 | byte responseByte_5; 311 | byte responseByte_6; 312 | byte responseByte_7; 313 | 314 | 315 | struct dtcResponse { 316 | uint8_t codesFound = 0; 317 | char codes[DTC_MAX_CODES][DTC_CODE_LEN]; 318 | } DTC_Response; 319 | 320 | bool begin(Stream& stream, const bool& debug = false, const uint16_t& timeout = 1000, const char& protocol = '0', const uint16_t& payloadLen = 128, const byte& dataTimeout = 0); 321 | ~ELM327(); 322 | bool initializeELM(const char& protocol = '0', const byte& dataTimeout = 0); 323 | void flushInputBuff(); 324 | uint64_t findResponse(); 325 | void queryPID(const uint8_t& service, const uint16_t& pid, const uint8_t& num_responses = 1); 326 | void queryPID(char queryStr[]); 327 | double processPID(const uint8_t& service, const uint16_t& pid, const uint8_t& num_responses, const uint8_t& numExpectedBytes, const double& scaleFactor = 1, const float& bias = 0); 328 | void sendCommand(const char *cmd); 329 | int8_t sendCommand_Blocking(const char *cmd); 330 | int8_t get_response(); 331 | bool timeout(); 332 | double conditionResponse(const uint8_t& numExpectedBytes, const double& scaleFactor = 1, const double& bias = 0); 333 | double conditionResponse(double (*func)()); 334 | double (*selectCalculator(uint16_t pid))(); 335 | float batteryVoltage(void); 336 | int8_t get_vin_blocking(char vin[]); 337 | bool resetDTC(); 338 | void currentDTCCodes(const bool& isBlocking = true); 339 | bool isPidSupported(uint8_t pid); 340 | void parseMultiLineResponse(); 341 | 342 | uint32_t supportedPIDs_1_20(); 343 | 344 | uint32_t monitorStatus(); 345 | uint16_t freezeDTC(); 346 | uint16_t fuelSystemStatus(); 347 | float engineLoad(); 348 | float engineCoolantTemp(); 349 | float shortTermFuelTrimBank_1(); 350 | float longTermFuelTrimBank_1(); 351 | float shortTermFuelTrimBank_2(); 352 | float longTermFuelTrimBank_2(); 353 | float fuelPressure(); 354 | uint8_t manifoldPressure(); 355 | float rpm(); 356 | int32_t kph(); 357 | float mph(); 358 | float timingAdvance(); 359 | float intakeAirTemp(); 360 | float mafRate(); 361 | float throttle(); 362 | uint8_t commandedSecAirStatus(); 363 | uint8_t oxygenSensorsPresent_2banks(); 364 | uint8_t obdStandards(); 365 | uint8_t oxygenSensorsPresent_4banks(); 366 | bool auxInputStatus(); 367 | uint16_t runTime(); 368 | 369 | 370 | uint32_t supportedPIDs_21_40(); 371 | 372 | uint16_t distTravelWithMIL(); 373 | float fuelRailPressure(); 374 | float fuelRailGuagePressure(); 375 | float commandedEGR(); 376 | float egrError(); 377 | float commandedEvapPurge(); 378 | float fuelLevel(); 379 | uint8_t warmUpsSinceCodesCleared(); 380 | uint16_t distSinceCodesCleared(); 381 | float evapSysVapPressure(); 382 | uint8_t absBaroPressure(); 383 | float catTempB1S1(); 384 | float catTempB2S1(); 385 | float catTempB1S2(); 386 | float catTempB2S2(); 387 | 388 | uint32_t supportedPIDs_41_60(); 389 | 390 | uint32_t monitorDriveCycleStatus(); 391 | float ctrlModVoltage(); 392 | float absLoad(); 393 | float commandedAirFuelRatio(); 394 | float relativeThrottle(); 395 | float ambientAirTemp(); 396 | float absThrottlePosB(); 397 | float absThrottlePosC(); 398 | float absThrottlePosD(); 399 | float absThrottlePosE(); 400 | float absThrottlePosF(); 401 | float commandedThrottleActuator(); 402 | uint16_t timeRunWithMIL(); 403 | uint16_t timeSinceCodesCleared(); 404 | float maxMafRate(); 405 | uint8_t fuelType(); 406 | float ethanolPercent(); 407 | float absEvapSysVapPressure(); 408 | float evapSysVapPressure2(); 409 | float absFuelRailPressure(); 410 | float relativePedalPos(); 411 | float hybridBatLife(); 412 | float oilTemp(); 413 | float fuelInjectTiming(); 414 | float fuelRate(); 415 | uint8_t emissionRqmts(); 416 | 417 | 418 | uint32_t supportedPIDs_61_80(); 419 | 420 | float demandedTorque(); 421 | float torque(); 422 | uint16_t referenceTorque(); 423 | uint16_t auxSupported(); 424 | void printError(); 425 | 426 | 427 | 428 | 429 | private: 430 | char query[QUERY_LEN] = { '\0' }; 431 | bool longQuery = false; 432 | bool isMode0x22Query = false; 433 | uint32_t currentTime; 434 | uint32_t previousTime; 435 | double* calculator; 436 | 437 | static double calculator_0C(); 438 | static double calculator_10(); 439 | static double calculator_14(); 440 | static double calculator_1F(); 441 | static double calculator_22(); 442 | static double calculator_23(); 443 | static double calculator_24(); 444 | static double calculator_32(); 445 | static double calculator_3C(); 446 | static double calculator_42(); 447 | static double calculator_43(); 448 | static double calculator_44(); 449 | static double calculator_4F(); 450 | static double calculator_50(); 451 | static double calculator_53(); 452 | static double calculator_54(); 453 | static double calculator_55(); 454 | static double calculator_59(); 455 | static double calculator_5D(); 456 | static double calculator_5E(); 457 | static double calculator_61(); 458 | 459 | obd_cmd_states nb_query_state = SEND_COMMAND; // Non-blocking query state 460 | 461 | void upper(char string[], 462 | uint8_t buflen); 463 | void formatQueryArray(const uint8_t& service, 464 | const uint16_t& pid, 465 | const uint8_t& num_responses); 466 | 467 | uint8_t ctoi(uint8_t value); 468 | int8_t nextIndex(char const *str, 469 | char const *target, 470 | uint8_t numOccur = 1); 471 | void removeChar(char *from, const char *remove); 472 | }; 473 | --------------------------------------------------------------------------------