├── .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 | [](https://badge.fury.io/gh/PowerBroker2%2FELMduino) [](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 | [](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 |
--------------------------------------------------------------------------------