### Send Raw AT/OBD Commands and Get Response Source: https://context7.com/powerbroker2/elmduino/llms.txt Use `sendCommand()` for non-blocking command transmission and poll `get_response()` until the response is ready. `sendCommand_Blocking()` is suitable for setup commands. ```cpp #include "BluetoothSerial.h" #include "ELMduino.h" BluetoothSerial SerialBT; ELM327 myELM327; int nb_query_state = SEND_COMMAND; // Custom DPF soot mass calculation (OEM PID 0x017A) double calcDPF() { // payload bytes 5 and 6 hold the raw 16-bit value double B = myELM327.payload[6]; double C = myELM327.payload[5]; return ((B * 256) + C) / 100.0; // Result in grams } void setup() { Serial.begin(115200); SerialBT.begin("ArduHUD", true); if (!SerialBT.connect("OBDII")) { while (1); } if (!myELM327.begin(SerialBT, true, 2000)) { while (1); } // Example: send a blocking AT command to read the protocol in use int8_t result = myELM327.sendCommand_Blocking("AT DP"); if (result == ELM_SUCCESS) Serial.println(myELM327.payload); // e.g. "ISO 15765-4 (CAN 11/500)" } void loop() { if (nb_query_state == SEND_COMMAND) { myELM327.sendCommand("017A"); // Send custom multiline OBD PID nb_query_state = WAITING_RESP; } else if (nb_query_state == WAITING_RESP) { myELM327.get_response(); // Poll for response each iteration } if (myELM327.nb_rx_state == ELM_SUCCESS) { double dpfSoot = myELM327.conditionResponse(calcDPF); Serial.print("DPF soot: "); Serial.print(dpfSoot); Serial.println(" g"); nb_query_state = SEND_COMMAND; delay(5000); } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); nb_query_state = SEND_COMMAND; delay(5000); } } // Expected output: // DPF soot: 12.34 g ``` -------------------------------- ### Read Engine Sensor Data with ELMduino Source: https://context7.com/powerbroker2/elmduino/llms.txt This example demonstrates how to sequentially read various engine sensor PIDs using ELMduino. Ensure BluetoothSerial and ELMduino are included and initialized. The code cycles through different sensor states, checking for successful data reception or errors. ```cpp #include "BluetoothSerial.h" #include "ELMduino.h" BluetoothSerial SerialBT; ELM327 myELM327; typedef enum { COOLANT_TEMP, ENGINE_LOAD_STATE, THROTTLE_POS, INTAKE_TEMP, FUEL_LVL, BATTERY_V } sensor_states; sensor_states sensor_state = COOLANT_TEMP; void setup() { Serial.begin(115200); SerialBT.begin("ArduHUD", true); if (!SerialBT.connect("OBDII")) { while (1); } if (!myELM327.begin(SerialBT, false, 2000)) { while (1); } } void loop() { float val = 0; switch (sensor_state) { case COOLANT_TEMP: val = myELM327.engineCoolantTemp(); // returns °C; formula: byte - 40 if (myELM327.nb_rx_state == ELM_SUCCESS) { Serial.print("Coolant: "); Serial.print(val); Serial.println(" C"); sensor_state = ENGINE_LOAD_STATE; } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); sensor_state = ENGINE_LOAD_STATE; } break; case ENGINE_LOAD_STATE: val = myELM327.engineLoad(); // 0–100 % if (myELM327.nb_rx_state == ELM_SUCCESS) { Serial.print("Load: "); Serial.print(val); Serial.println(" %"); sensor_state = THROTTLE_POS; } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); sensor_state = THROTTLE_POS; } break; case THROTTLE_POS: val = myELM327.throttle(); // 0–100 % if (myELM327.nb_rx_state == ELM_SUCCESS) { Serial.print("Throttle: "); Serial.print(val); Serial.println(" %"); sensor_state = INTAKE_TEMP; } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); sensor_state = INTAKE_TEMP; } break; case INTAKE_TEMP: val = myELM327.intakeAirTemp(); // °C if (myELM327.nb_rx_state == ELM_SUCCESS) { Serial.print("Intake air: "); Serial.print(val); Serial.println(" C"); sensor_state = FUEL_LVL; } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); sensor_state = FUEL_LVL; } break; case FUEL_LVL: val = myELM327.fuelLevel(); // 0–100 % if (myELM327.nb_rx_state == ELM_SUCCESS) { Serial.print("Fuel: "); Serial.print(val); Serial.println(" %"); sensor_state = BATTERY_V; } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); sensor_state = BATTERY_V; } break; case BATTERY_V: val = myELM327.batteryVoltage(); // reads "AT RV" — vehicle battery V if (myELM327.nb_rx_state == ELM_SUCCESS) { Serial.print("Battery: "); Serial.print(val); Serial.println(" V"); sensor_state = COOLANT_TEMP; delay(500); } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); sensor_state = COOLANT_TEMP; } break; } } ``` -------------------------------- ### Query Engine RPM and Speed with ELMduino Source: https://github.com/powerbroker2/elmduino/blob/master/README.md This example demonstrates how to query engine RPM and vehicle speed using the ELMduino library. It utilizes a state machine to sequentially request PIDs and handles non-blocking responses. Ensure the correct serial port and baud rate are configured for your ELM327 device. ```C++ #include "ELMduino.h" #define ELM_PORT Serial1 const bool DEBUG = true; const int TIMEOUT = 2000; const bool HALT_ON_FAIL = false; ELM327 myELM327; typedef enum { ENG_RPM, SPEED } obd_pid_states; obd_pid_states obd_state = ENG_RPM; float rpm = 0; float mph = 0; void setup() { Serial.begin(115200); ELM_PORT.begin(115200); Serial.println("Attempting to connect to ELM327..."); if (!myELM327.begin(ELM_PORT, DEBUG, TIMEOUT)) { Serial.println("Couldn't connect to OBD scanner"); if (HALT_ON_FAIL) while (1); } Serial.println("Connected to ELM327"); } void loop() { switch (obd_state) { case ENG_RPM: { rpm = myELM327.rpm(); if (myELM327.nb_rx_state == ELM_SUCCESS) { Serial.print("rpm: "); Serial.println(rpm); obd_state = SPEED; } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); obd_state = SPEED; } break; } case SPEED: { mph = myELM327.mph(); if (myELM327.nb_rx_state == ELM_SUCCESS) { Serial.print("mph: "); Serial.println(mph); obd_state = ENG_RPM; } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); obd_state = ENG_RPM; } break; } } } ``` -------------------------------- ### Check ECU PID Support with `isPidSupported()` Source: https://context7.com/powerbroker2/elmduino/llms.txt Use `isPidSupported()` to check if an ECU supports a given PID. The method is non-blocking and should be called repeatedly until `nb_rx_state` indicates completion or an error. This example iterates through a predefined list of PIDs and prints their support status. ```cpp #include "BluetoothSerial.h" #include "ELMduino.h" BluetoothSerial SerialBT; ELM327 myELM327; // PIDs to probe uint8_t pidsToCheck[] = { ENGINE_RPM, VEHICLE_SPEED, ENGINE_OIL_TEMP, HYBRID_BATTERY_REMAINING_LIFE, ENGINE_FUEL_RATE }; uint8_t pidIdx = 0; void setup() { Serial.begin(115200); SerialBT.begin("ArduHUD", true); if (!SerialBT.connect("ELMULATOR")) { while (1); } if (!myELM327.begin(SerialBT, false, 2000)) { while (1); } } void loop() { if (pidIdx >= sizeof(pidsToCheck)) { delay(30000); pidIdx = 0; return; } uint8_t pid = pidsToCheck[pidIdx]; bool supported = myELM327.isPidSupported(pid); if (myELM327.nb_rx_state == ELM_SUCCESS) { Serial.print("PID 0x"); Serial.print(pid, HEX); Serial.println(supported ? " : SUPPORTED" : " : NOT SUPPORTED"); pidIdx++; delay(500); } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); pidIdx++; } } // Expected output: // PID 0xC : SUPPORTED // PID 0xD : SUPPORTED // PID 0x5C : NOT SUPPORTED // PID 0x5B : NOT SUPPORTED // PID 0x5E : SUPPORTED ``` -------------------------------- ### ELM327::sendCommand() / ELM327::sendCommand_Blocking() and ELM327::get_response() Source: https://context7.com/powerbroker2/elmduino/llms.txt These methods allow sending raw AT commands or OBD hex strings to the ELM327 device. `sendCommand` returns immediately, requiring polling with `get_response` for the response. `sendCommand_Blocking` is a synchronous wrapper suitable for setup commands. ```APIDOC ## `ELM327::sendCommand()` / `ELM327::sendCommand_Blocking()` and `ELM327::get_response()` — Raw AT and OBD commands `sendCommand(cmd)` sends any raw command string (AT command or raw OBD hex string) and returns immediately; the caller must poll `get_response()` each loop iteration until `nb_rx_state` leaves `ELM_GETTING_MSG`. `sendCommand_Blocking(cmd)` wraps this into a synchronous call, suitable for AT commands during setup. ```cpp #include "BluetoothSerial.h" #include "ELMduino.h" BluetoothSerial SerialBT; ELM327 myELM327; int nb_query_state = SEND_COMMAND; // Custom DPF soot mass calculation (OEM PID 0x017A) double calcDPF() { // payload bytes 5 and 6 hold the raw 16-bit value double B = myELM327.payload[6]; double C = myELM327.payload[5]; return ((B * 256) + C) / 100.0; // Result in grams } void setup() { Serial.begin(115200); SerialBT.begin("ArduHUD", true); if (!SerialBT.connect("OBDII")) { while (1); } if (!myELM327.begin(SerialBT, true, 2000)) { while (1); } // Example: send a blocking AT command to read the protocol in use int8_t result = myELM327.sendCommand_Blocking("AT DP"); if (result == ELM_SUCCESS) Serial.println(myELM327.payload); // e.g. "ISO 15765-4 (CAN 11/500)" } void loop() { if (nb_query_state == SEND_COMMAND) { myELM327.sendCommand("017A"); // Send custom multiline OBD PID nb_query_state = WAITING_RESP; } else if (nb_query_state == WAITING_RESP) { myELM327.get_response(); // Poll for response each iteration } if (myELM327.nb_rx_state == ELM_SUCCESS) { double dpfSoot = myELM327.conditionResponse(calcDPF); Serial.print("DPF soot: "); Serial.print(dpfSoot); Serial.println(" g"); nb_query_state = SEND_COMMAND; delay(5000); } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); nb_query_state = SEND_COMMAND; delay(5000); } } // Expected output: // DPF soot: 12.34 g ``` ``` -------------------------------- ### Raw PID Queries with `queryPID()` and `processPID()` Source: https://context7.com/powerbroker2/elmduino/llms.txt Use `queryPID()` to send raw OBD queries and `processPID()` to combine the non-blocking send/receive cycle with a linear transformation for engineering-unit values. This is useful for PIDs not covered by built-in methods, including OEM-specific PIDs via Service 0x22. The example demonstrates querying standard coolant temperature and shows how to apply a scale factor and bias. ```cpp #include "BluetoothSerial.h" #include "ELMduino.h" BluetoothSerial SerialBT; ELM327 myELM327; // Custom PID: manufacturer-specific coolant temp via service 0x22, PID 0x1A10 // Expected: 2 response bytes, formula: (A*256+B)/10 - 40 degC // Expressed as scaleFactor=0.1, bias=-40 on the combined 16-bit value double customCoolant = 0; void setup() { Serial.begin(115200); SerialBT.begin("ArduHUD", true); if (!SerialBT.connect("OBDII")) { while (1); } if (!myELM327.begin(SerialBT, false, 2000)) { while (1); } } void loop() { // Standard PID via processPID directly (same as engineCoolantTemp() internally) float stdCoolant = myELM327.processPID(SERVICE_01, ENGINE_COOLANT_TEMP, 1, 1, 1.0, -40.0); if (myELM327.nb_rx_state == ELM_SUCCESS) { Serial.print("Standard coolant temp: "); Serial.print(stdCoolant); Serial.println(" C"); delay(1000); } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); } } ``` -------------------------------- ### ELM327::begin() Source: https://context7.com/powerbroker2/elmduino/llms.txt Initializes the ELM327 adapter on any Arduino Stream. It sets defaults, resets the adapter, disables echo/spaces, optionally sets the OBD protocol, and verifies connectivity. Returns true on success. ```APIDOC ## ELM327::begin() — Initialize the ELM327 connection ### Description Initializes the ELM327 adapter on any Arduino `Stream` (hardware serial, SoftwareSerial, BluetoothSerial, WiFiClient, etc.). Sets defaults, resets the adapter, disables echo/spaces, optionally sets the OBD protocol, and verifies connectivity. Returns `true` on success. ### Method ```cpp bool begin(Stream &stream, bool debug = false, uint32_t timeout_ms = 2000, OBDProtocol protocol = AUTOMATIC, uint16_t payloadLen = 512, uint16_t dataTimeout = 100) ``` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```cpp #include "BluetoothSerial.h" #include "ELMduino.h" BluetoothSerial SerialBT; ELM327 myELM327; void setup() { Serial.begin(115200); SerialBT.begin("ArduHUD", true); // ESP32 BT master mode if (!SerialBT.connect("OBDII")) { // Connect to ELM327 BT dongle by name Serial.println("BT connection failed"); while (1); } // begin(stream, debug, timeout_ms, protocol, payloadLen, dataTimeout) // protocol '0' = automatic detection; ISO_15765_11_BIT_500_KBAUD = '6' if (!myELM327.begin(SerialBT, true, 2000, AUTOMATIC)) { Serial.println("ELM327 init failed"); while (1); } Serial.println("Connected to ELM327"); // myELM327.connected == true } ``` ### Response #### Success Response (true) Returns `true` if the ELM327 adapter was successfully initialized. #### Response Example `true` ``` -------------------------------- ### Query Engine RPM and Vehicle Speed (Non-Blocking) Source: https://context7.com/powerbroker2/elmduino/llms.txt Demonstrates non-blocking queries for engine RPM and vehicle speed. Each PID query must be called repeatedly until `nb_rx_state` indicates success or an error. Only one PID can be queried at a time; manage multiple PIDs using a state machine. ```cpp #include "BluetoothSerial.h" #include "ELMduino.h" BluetoothSerial SerialBT; ELM327 myELM327; typedef enum { ENG_RPM, SPEED } obd_pid_states; obd_pid_states obd_state = ENG_RPM; float rpm = 0; float mph = 0; void setup() { Serial.begin(115200); SerialBT.begin("ArduHUD", true); if (!SerialBT.connect("OBDII")) { while (1); } if (!myELM327.begin(SerialBT, true, 2000)) { while (1); } } void loop() { switch (obd_state) { case ENG_RPM: rpm = myELM327.rpm(); if (myELM327.nb_rx_state == ELM_SUCCESS) { Serial.print("RPM: "); Serial.println(rpm); obd_state = SPEED; } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); // prints human-readable error to Serial obd_state = SPEED; } break; case SPEED: mph = myELM327.mph(); // internally calls kph() * 0.6213711922 if (myELM327.nb_rx_state == ELM_SUCCESS) { Serial.print("MPH: "); Serial.println(mph); obd_state = ENG_RPM; } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); obd_state = ENG_RPM; } break; } } // Expected Serial output: // RPM: 850.00 // MPH: 0.00 // RPM: 1250.50 // MPH: 35.40 ``` -------------------------------- ### Initialize ELM327 Connection with BluetoothSerial Source: https://context7.com/powerbroker2/elmduino/llms.txt Initializes the ELM327 adapter using a BluetoothSerial stream. Ensure the Bluetooth dongle is paired and connected before calling `begin()`. The `debug` parameter enables verbose output to the Serial monitor. ```cpp #include "BluetoothSerial.h" #include "ELMduino.h" BluetoothSerial SerialBT; ELM327 myELM327; void setup() { Serial.begin(115200); SerialBT.begin("ArduHUD", true); // ESP32 BT master mode if (!SerialBT.connect("OBDII")) { // Connect to ELM327 BT dongle by name Serial.println("BT connection failed"); while (1); } // begin(stream, debug, timeout_ms, protocol, payloadLen, dataTimeout) // protocol '0' = automatic detection; ISO_15765_11_BIT_500_KBAUD = '6' if (!myELM327.begin(SerialBT, true, 2000, AUTOMATIC)) { Serial.println("ELM327 init failed"); while (1); } Serial.println("Connected to ELM327"); // myELM327.connected == true } ``` -------------------------------- ### ELM327::queryPID() and ELM327::processPID() Source: https://context7.com/powerbroker2/elmduino/llms.txt These methods allow for raw PID queries. `queryPID` formats and sends a raw OBD query, while `processPID` handles the non-blocking send/receive cycle and applies a linear transformation to the raw value to produce an engineering-unit value. Useful for PIDs not covered by built-in methods or for OEM-specific PIDs. ```APIDOC ## `ELM327::queryPID()` and `ELM327::processPID()` — Raw PID queries `queryPID(service, pid, num_responses)` formats and sends a raw OBD query string. `processPID(service, pid, num_responses, numExpectedBytes, scaleFactor, bias)` combines the non-blocking send/receive cycle and applies a linear transformation `(rawValue * scaleFactor) + bias` to produce the final engineering-unit value. Use these to query any PID not covered by a built-in method, including OEM-specific PIDs via Service 0x22. ``` -------------------------------- ### ELM327::rpm(), ELM327::kph(), ELM327::mph() Source: https://context7.com/powerbroker2/elmduino/llms.txt Non-blocking queries for engine RPM, vehicle speed in km/h, and vehicle speed in mph. These functions must be called repeatedly until `nb_rx_state` indicates success or an error. Only one PID query can be active at a time. ```APIDOC ## ELM327::rpm() / ELM327::kph() / ELM327::mph() — Engine speed and vehicle speed ### Description Non-blocking queries for engine RPM (float, rpm), vehicle speed in km/h (int32_t), and vehicle speed in mph (float). Each must be called repeatedly in the `loop()` until `nb_rx_state == ELM_SUCCESS`. Only one PID may be in-flight at a time; use a state machine to cycle through multiple PIDs. ### Method ```cpp float rpm(); int32_t kph(); float mph(); // internally calls kph() * 0.6213711922 ``` ### Parameters None ### Request Example ```cpp #include "BluetoothSerial.h" #include "ELMduino.h" BluetoothSerial SerialBT; ELM327 myELM327; typedef enum { ENG_RPM, SPEED } obd_pid_states; obd_pid_states obd_state = ENG_RPM; float rpm = 0; float mph = 0; void setup() { Serial.begin(115200); SerialBT.begin("ArduHUD", true); if (!SerialBT.connect("OBDII")) { while (1); } if (!myELM327.begin(SerialBT, true, 2000)) { while (1); } } void loop() { switch (obd_state) { case ENG_RPM: rpm = myELM327.rpm(); if (myELM327.nb_rx_state == ELM_SUCCESS) { Serial.print("RPM: "); Serial.println(rpm); obd_state = SPEED; } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); // prints human-readable error to Serial obd_state = SPEED; } break; case SPEED: mph = myELM327.mph(); // internally calls kph() * 0.6213711922 if (myELM327.nb_rx_state == ELM_SUCCESS) { Serial.print("MPH: "); Serial.println(mph); obd_state = ENG_RPM; } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); obd_state = ENG_RPM; } break; } } // Expected Serial output: // RPM: 850.00 // MPH: 0.00 // RPM: 1250.50 // MPH: 35.40 ``` ### Response #### Success Response (ELM_SUCCESS) - **rpm** (float) - Engine Revolutions Per Minute. - **kph** (int32_t) - Vehicle speed in kilometers per hour. - **mph** (float) - Vehicle speed in miles per hour. #### Response Example ``` 850.00 ``` ``` 0 ``` ``` 0.00 ``` ``` -------------------------------- ### Apply Custom Calculations to OBD Response Data Source: https://context7.com/powerbroker2/elmduino/llms.txt Use `conditionResponse()` with a function pointer to apply complex calculations to response bytes, such as for PIDs with multi-byte or signed arithmetic formulas. The `response_A` through `response_H` bytes are available for calculations. ```cpp #include "ELMduino.h" // (Connection setup omitted for brevity — see begin() example above) ELM327 myELM327; // Custom calculator: OBD PID 0x24 (O2 Sensor 1 — equivalence ratio & voltage) // Formula: ratio = (2 / 65536) * ((A<<8)|B), voltage = (8 / 65536) * ((C<<8)|D) double calcO2Sensor1Ratio() { return (2.0 / 65536.0) * ((response_A << 8) | response_B); } void loop() { // Query with 4 expected response bytes; provide calculator for ratio part only double ratio = myELM327.processPID(SERVICE_01, OXYGEN_SENSOR_1_B, 1, 4); if (myELM327.nb_rx_state == ELM_SUCCESS) { // Use conditionResponse with custom function on the already-populated response bytes double o2ratio = myELM327.conditionResponse(calcO2Sensor1Ratio); Serial.print("O2 Sensor 1 Equiv Ratio: "); Serial.println(o2ratio, 4); delay(1000); } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); } } ``` -------------------------------- ### Fuel Rate Source: https://context7.com/powerbroker2/elmduino/llms.txt Retrieves the current rate of fuel consumption in liters per hour. ```APIDOC ## fuelRate() ### Description Retrieves the current fuel consumption rate. ### Method `float fuelRate()` ### Return Value - `float`: Fuel consumption rate in liters per hour (L/h). ### Usage Example ```cpp float rate = myELM327.fuelRate(); if (myELM327.nb_rx_state == ELM_SUCCESS) { // Process fuel rate value } ``` ``` -------------------------------- ### ELM327::conditionResponse() Source: https://context7.com/powerbroker2/elmduino/llms.txt This method applies a custom calculation to the raw response data received from the ELM327 device. It can use a simple linear transformation or a user-provided function pointer for complex calculations. ```APIDOC ## `ELM327::conditionResponse()` — Apply custom calculation to raw response Overloaded method that either applies a linear `(rawValue * scaleFactor) + bias` transformation to the raw `response` integer, or accepts a function pointer `double (*func)()` for arbitrary calculations. The function-pointer variant is the correct approach for PIDs whose formula cannot be expressed as a simple scale+bias (e.g., those requiring signed arithmetic or multi-byte manipulations). The globally accessible `response_A` through `response_H` static bytes hold the extracted response bytes for use inside the calculator function. ```cpp #include "ELMduino.h" // (Connection setup omitted for brevity — see begin() example above) ELM327 myELM327; // Custom calculator: OBD PID 0x24 (O2 Sensor 1 — equivalence ratio & voltage) // Formula: ratio = (2 / 65536) * ((A<<8)|B), voltage = (8 / 65536) * ((C<<8)|D) double calcO2Sensor1Ratio() { return (2.0 / 65536.0) * ((response_A << 8) | response_B); } void loop() { // Query with 4 expected response bytes; provide calculator for ratio part only double ratio = myELM327.processPID(SERVICE_01, OXYGEN_SENSOR_1_B, 1, 4); if (myELM327.nb_rx_state == ELM_SUCCESS) { // Use conditionResponse with custom function on the already-populated response bytes double o2ratio = myELM327.conditionResponse(calcO2Sensor1Ratio); Serial.print("O2 Sensor 1 Equiv Ratio: "); Serial.println(o2ratio, 4); delay(1000); } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); } } ``` ``` -------------------------------- ### Oil Temperature Source: https://context7.com/powerbroker2/elmduino/llms.txt Retrieves the temperature of the engine oil in degrees Celsius. ```APIDOC ## oilTemp() ### Description Retrieves the engine oil temperature. ### Method `float oilTemp()` ### Return Value - `float`: Engine oil temperature in degrees Celsius (°C). ### Usage Example ```cpp float oilTemp = myELM327.oilTemp(); if (myELM327.nb_rx_state == ELM_SUCCESS) { // Process oil temperature value } ``` ``` -------------------------------- ### Battery Voltage Source: https://context7.com/powerbroker2/elmduino/llms.txt Retrieves the vehicle's battery voltage. This is equivalent to reading the 'AT RV' command. ```APIDOC ## batteryVoltage() ### Description Retrieves the vehicle's battery voltage. ### Method `float batteryVoltage()` ### Return Value - `float`: Battery voltage in Volts (V DC). ### Usage Example ```cpp float voltage = myELM327.batteryVoltage(); if (myELM327.nb_rx_state == ELM_SUCCESS) { // Process battery voltage value } ``` ``` -------------------------------- ### Fuel Level Source: https://context7.com/powerbroker2/elmduino/llms.txt Retrieves the current fuel level in the tank as a percentage. ```APIDOC ## fuelLevel() ### Description Retrieves the current fuel level in the tank. ### Method `float fuelLevel()` ### Return Value - `float`: Fuel level as a percentage (0–100%). ### Usage Example ```cpp float fuel = myELM327.fuelLevel(); if (myELM327.nb_rx_state == ELM_SUCCESS) { // Process fuel level value } ``` ``` -------------------------------- ### Intake Air Temperature Source: https://context7.com/powerbroker2/elmduino/llms.txt Retrieves the temperature of the air entering the engine's intake manifold in degrees Celsius. ```APIDOC ## intakeAirTemp() ### Description Retrieves the intake air temperature. ### Method `float intakeAirTemp()` ### Return Value - `float`: Intake air temperature in degrees Celsius (°C). ### Usage Example ```cpp float intakeTemp = myELM327.intakeAirTemp(); if (myELM327.nb_rx_state == ELM_SUCCESS) { // Process intake air temperature value } ``` ``` -------------------------------- ### Read DTCs and Monitor MIL Status with ELM327 Source: https://context7.com/powerbroker2/elmduino/llms.txt Use `currentDTCCodes()` for blocking DTC retrieval and `monitorStatus()` for non-blocking MIL status and DTC count. The `monitorStatus()` function encodes MIL status in bit 7 and DTC count in bits 0-6 of `responseByte_2`. ```cpp #include "BluetoothSerial.h" #include "ELMduino.h" BluetoothSerial SerialBT; ELM327 myELM327; typedef enum { MILSTATUS, DTCCODES } dtc_states; dtcc_states dtc_state = MILSTATUS; uint8_t numCodes = 0; void setup() { Serial.begin(115200); SerialBT.begin("ArduHUD", true); if (!SerialBT.connect("OBDII")) { while (1); } if (!myELM327.begin(SerialBT)) { while (1); } // --- Blocking mode: simplest use case --- myELM327.currentDTCCodes(); // blocks until response received if (myELM327.nb_rx_state == ELM_SUCCESS) { Serial.print("DTCs found (blocking): "); Serial.println(myELM327.DTC_Response.codesFound); for (int i = 0; i < myELM327.DTC_Response.codesFound; i++) Serial.println(myELM327.DTC_Response.codes[i]); // e.g. "P0420" } } void loop() { // --- Non-blocking mode: check MIL first, then fetch codes --- switch (dtc_state) { case MILSTATUS: myELM327.monitorStatus(); if (myELM327.nb_rx_state == ELM_SUCCESS) { bool milOn = (myELM327.responseByte_2 & 0x80); numCodes = (myELM327.responseByte_2 & 0x7F); Serial.print("MIL: "); Serial.println(milOn ? "ON" : "OFF"); Serial.print("Codes: "); Serial.println(numCodes); dtc_state = DTCCODES; } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); dtc_state = DTCCODES; } break; case DTCCODES: if (numCodes > 0) { myELM327.currentDTCCodes(false); // non-blocking if (myELM327.nb_rx_state == ELM_SUCCESS) { for (int i = 0; i < myELM327.DTC_Response.codesFound; i++) Serial.println(myELM327.DTC_Response.codes[i]); dtc_state = MILSTATUS; delay(10000); } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); dtc_state = MILSTATUS; } } else { dtc_state = MILSTATUS; delay(5000); } break; } } ``` -------------------------------- ### ELM327::isPidSupported() Source: https://context7.com/powerbroker2/elmduino/llms.txt Checks if a specific PID (Parameter ID) is supported by the ECU. This method is non-blocking and should be called repeatedly until the `nb_rx_state` indicates a complete message. ```APIDOC ## `ELM327::isPidSupported()` — Check ECU PID support Convenience method that queries the appropriate `supportedPIDs_xx_xx()` range for the given PID number and returns a boolean. Internally non-blocking; call repeatedly until `nb_rx_state != ELM_GETTING_MSG`. ``` -------------------------------- ### Mass Air Flow Rate Source: https://context7.com/powerbroker2/elmduino/llms.txt Retrieves the rate at which air is flowing into the engine, measured in grams per second. ```APIDOC ## mafRate() ### Description Retrieves the mass air flow rate into the engine. ### Method `float mafRate()` ### Return Value - `float`: Mass air flow rate in grams per second (g/s). ### Usage Example ```cpp float maf = myELM327.mafRate(); if (myELM327.nb_rx_state == ELM_SUCCESS) { // Process MAF rate value } ``` ``` -------------------------------- ### Engine Load Source: https://context7.com/powerbroker2/elmduino/llms.txt Retrieves the current engine load percentage. Engine load indicates the percentage of maximum available torque the engine is currently producing. ```APIDOC ## engineLoad() ### Description Retrieves the current engine load. ### Method `float engineLoad()` ### Return Value - `float`: Engine load as a percentage (0–100%). ### Usage Example ```cpp float load = myELM327.engineLoad(); if (myELM327.nb_rx_state == ELM_SUCCESS) { // Process load value } ``` ``` -------------------------------- ### Timing Advance Source: https://context7.com/powerbroker2/elmduino/llms.txt Retrieves the engine's ignition timing advance value, measured in degrees before Top Dead Center (TDC). ```APIDOC ## timingAdvance() ### Description Retrieves the engine's ignition timing advance. ### Method `float timingAdvance()` ### Return Value - `float`: Timing advance in degrees before TDC (° before TDC). ### Usage Example ```cpp float timing = myELM327.timingAdvance(); if (myELM327.nb_rx_state == ELM_SUCCESS) { // Process timing advance value } ``` ``` -------------------------------- ### ELM327::currentDTCCodes() and ELM327::monitorStatus() Source: https://context7.com/powerbroker2/elmduino/llms.txt These functions allow reading Diagnostic Trouble Codes (DTCs) and monitoring engine status. `monitorStatus()` provides a non-blocking way to check the MIL status and DTC count, while `currentDTCCodes()` retrieves the actual DTC strings. ```APIDOC ## `ELM327::currentDTCCodes()` and `ELM327::monitorStatus()` — Read Diagnostic Trouble Codes `monitorStatus()` (non-blocking) queries OBD Service 01 PID 01 and returns a 32-bit encoded value; `responseByte_2` encodes the MIL (check engine light) status in bit 7 and the count of stored DTCs in bits 0–6. `currentDTCCodes(bool isBlocking = true)` retrieves the actual DTC strings (e.g., `"P0300"`) into `DTC_Response.codes[]`, with `DTC_Response.codesFound` reporting the count. ### Usage **Blocking Mode:** ```cpp myELM327.currentDTCCodes(); // blocks until response received if (myELM327.nb_rx_state == ELM_SUCCESS) { Serial.print("DTCs found (blocking): "); Serial.println(myELM327.DTC_Response.codesFound); for (int i = 0; i < myELM327.DTC_Response.codesFound; i++) Serial.println(myELM327.DTC_Response.codes[i]); // e.g. "P0420" } ``` **Non-blocking Mode:** ```cpp // Check MIL status and DTC count myELM327.monitorStatus(); if (myELM327.nb_rx_state == ELM_SUCCESS) { bool milOn = (myELM327.responseByte_2 & 0x80); numCodes = (myELM327.responseByte_2 & 0x7F); Serial.print("MIL: "); Serial.println(milOn ? "ON" : "OFF"); Serial.print("Codes: "); Serial.println(numCodes); } // Retrieve DTC strings if codes are present if (numCodes > 0) { myELM327.currentDTCCodes(false); // non-blocking if (myELM327.nb_rx_state == ELM_SUCCESS) { for (int i = 0; i < myELM327.DTC_Response.codesFound; i++) Serial.println(myELM327.DTC_Response.codes[i]); } } ``` ### Parameters - `currentDTCCodes(bool isBlocking = true)`: - `isBlocking` (bool): If true, the function blocks until a response is received. Defaults to true. ### Response - `ELM327.nb_rx_state`: Status of the non-blocking receive operation. `ELM_SUCCESS` indicates a successful response. - `ELM327.DTC_Response.codesFound` (uint8_t): The number of DTCs found. - `ELM327.DTC_Response.codes[]` (char array): An array containing the DTC strings (e.g., "P0300"). - `ELM327.responseByte_2` (uint8_t): For `monitorStatus()`, this byte contains: - Bit 7: MIL (check engine light) status (1 = ON, 0 = OFF). - Bits 0-6: Count of stored DTCs. ``` -------------------------------- ### Read Vehicle Identification Number (VIN) with ELM327::get_vin_blocking() Source: https://context7.com/powerbroker2/elmduino/llms.txt This blocking function queries OBD Service 09 PID 02 to retrieve the VIN. Ensure an 18-byte buffer is allocated for the 17-character VIN plus null terminator. Returns an `ELM_XXX` status code. ```cpp #include "BluetoothSerial.h" #include "ELMduino.h" BluetoothSerial SerialBT; ELM327 myELM327; void setup() { Serial.begin(115200); SerialBT.begin("ArduHUD", true); if (!SerialBT.connect("OBDII")) { while (1); } if (!myELM327.begin(SerialBT, false, 2000)) { while (1); } char vin[18] = {0}; // 17 chars + null terminator int8_t status = myELM327.get_vin_blocking(vin); if (status == ELM_SUCCESS) { Serial.print("VIN: "); Serial.println(vin); // e.g. "1D4GP00R55B123456" } else { Serial.print("VIN query failed, status: "); Serial.println(status); myELM327.printError(); } } void loop() {} ``` -------------------------------- ### Engine Coolant Temperature Source: https://context7.com/powerbroker2/elmduino/llms.txt Retrieves the engine coolant temperature in degrees Celsius. The raw value is converted using the formula: `byte - 40`. ```APIDOC ## engineCoolantTemp() ### Description Retrieves the engine coolant temperature. ### Method `float engineCoolantTemp()` ### Return Value - `float`: Engine coolant temperature in degrees Celsius (°C). ### Usage Example ```cpp float temp = myELM327.engineCoolantTemp(); if (myELM327.nb_rx_state == ELM_SUCCESS) { // Process temperature value } ``` ``` -------------------------------- ### Throttle Position Source: https://context7.com/powerbroker2/elmduino/llms.txt Retrieves the current throttle position percentage. This indicates how far the throttle plate is open. ```APIDOC ## throttle() ### Description Retrieves the current throttle position. ### Method `float throttle()` ### Return Value - `float`: Throttle position as a percentage (0–100%). ### Usage Example ```cpp float throttlePos = myELM327.throttle(); if (myELM327.nb_rx_state == ELM_SUCCESS) { // Process throttle position value } ``` ``` -------------------------------- ### ELM327::printError() - Human-readable error reporting Source: https://context7.com/powerbroker2/elmduino/llms.txt Prints the raw payload and named error constant for the current nb_rx_state. Useful during development, call this in any else if (nb_rx_state != ELM_GETTING_MSG) branch. ```cpp // Error states and their meanings: // ELM_SUCCESS (0) — Response received and parsed successfully // ELM_NO_RESPONSE (1) — No bytes received at all // ELM_BUFFER_OVERFLOW (2) — Response exceeded PAYLOAD_LEN bytes // ELM_GARBAGE (3) — Unrecognized/corrupt response // ELM_UNABLE_TO_CONNECT (4) — ELM327 reported "UNABLE TO CONNECT" // ELM_NO_DATA (5) — ELM327 reported "NO DATA" (PID not supported live) // ELM_STOPPED (6) — ELM327 reported "STOPPED" // ELM_TIMEOUT (7) — Response not received within timeout_ms // ELM_GETTING_MSG (8) — Still waiting (not an error; continue polling) // ELM_GENERAL_ERROR (-1) — ELM327 reported "ERROR" void loop() { float temp = myELM327.engineCoolantTemp(); if (myELM327.nb_rx_state == ELM_SUCCESS) { Serial.print("Temp: "); Serial.println(temp); } else if (myELM327.nb_rx_state == ELM_TIMEOUT) { Serial.println("Timed out — check baud rate or try 38400"); myELM327.printError(); } else if (myELM327.nb_rx_state == ELM_NO_DATA) { Serial.println("PID not supported by this vehicle"); } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { myELM327.printError(); // Prints e.g.: // Received: UNABLETOCONNECT // ERROR: ELM_UNABLE_TO_CONNECT } } ``` -------------------------------- ### Manifold Absolute Pressure Source: https://context7.com/powerbroker2/elmduino/llms.txt Retrieves the pressure inside the engine's intake manifold, measured in kilopascals. ```APIDOC ## manifoldPressure() ### Description Retrieves the intake manifold absolute pressure. ### Method `float manifoldPressure()` ### Return Value - `float`: Manifold pressure in kilopascals (kPa). ### Usage Example ```cpp float pressure = myELM327.manifoldPressure(); if (myELM327.nb_rx_state == ELM_SUCCESS) { // Process manifold pressure value } ``` ```