Files
JC3636W518EN/10-OBD-Display-2024/elmulator/src/custom/main.cpp
T
patriandClaude Opus 5.5 5c04e3f0df Convert all projects to PlatformIO
- JC3636W518 sketches: pioarduino 51.03.05 (Arduino core 3.0.5), ESP32-S3
  16MB QIO / OPI PSRAM, vendor libs via lib_extra_dirs, per-project lv_conf.h
- display_gif_embedded uses default_16MB.csv (firmware 3.4 MB > huge_app)
- elm327_wifi_bridge: espressif32 6.10.0 on esp32dev, pinned lib_deps
- elmulator: library in lib/, one env per example, duplicate copies removed
- .ino -> src/main.cpp, headers/lv_conf.h -> include/

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 00:20:33 +02:00

173 lines
6.3 KiB
C++

#include "ESP32_Custom_ELMulator.h"
const String deviceName = "ELMULATOR"; // Bluetooth device name to use (no pin)
// Set up hardcoded responses to non-standard PID requests for MIL, DTC, ODO data
const String milResponse = "4101830000"; // MIL response code indicating 3 current DTC
const String dtcResponse = "43010341\r\n43010123\r\n43010420"; // DTC response returning 3 DTC codes (multiline response)
const uint32_t odoResponse = 1234567; // Hardcode an odometer reading of 1234567
const String ethPercentResponse = "620052C6";
const String dpfCloggingResponse = "6218E4C6";
ELMulator ELMulator;
void setup()
{
Serial.begin(115200);
Serial.println("Starting ELMulator...");
ELMulator.init(deviceName);
// Register the specific PIDs we are going to handle.
// ELMulator will respond to SUPPORTED_PIDS request ("0101") with the appropriate value
// indicating which PIDs it can respond to.
ELMulator.registerMode01Pid(ODOMETER);
ELMulator.registerMode01Pid(ENGINE_COOLANT_TEMP);
ELMulator.registerMode01Pid(ENGINE_RPM);
ELMulator.registerMode01Pid(MONITOR_STATUS_SINCE_DTC_CLEARED);
ELMulator.registerMode01Pid(INTAKE_MANIFOLD_ABS_PRESSURE);
ELMulator.registerMode01Pid(ENGINE_LOAD);
ELMulator.registerMode01Pid(VEHICLE_SPEED);
ELMulator.registerMode01Pid(INTAKE_AIR_TEMP);
ELMulator.registerMode01Pid(THROTTLE_POSITION);
}
void loop()
{
/**
* Waits for a new request from an ELM client and does some pre-processing to validate the PID and request.
* Non-PID requests like AT commands and SUPPORTED_PIDS queries will be handled by ELMulator automatically.
* PID requests will be verified as supported (registered); unsupported PIDs responded to with NO DATA response.
* Returns true if the request is for one of the PIDS we have registered above so we can respond to it.
*/
if (ELMulator.readELMRequest())
{
handlePIDRequest(ELMulator.elmRequest);
}
}
/**
* After receiving a supported PID(sensor) request, we prepare the response here.
* Response can come from a hardcoded value, a real hardware sensor, or a mock value.
* Default response is a mock value if no other handling is provided.
*/
void handlePIDRequest(const String &request)
{
// Handle special case requests like MIL and DTC checks
if (ELMulator.isMode03(request)) // Mode 03 request == Returns (hardcoded) list current DTC codes
{
if (dtcResponse.length())
{
ELMulator.writeResponse(dtcResponse);
return;
}
else
{
DEBUG("DTC response is empty.");
ELMulator.writePidNotSupported();
return;
}
}
else if (ELMulator.isMode01MIL(request)) // Mode 0101 MIL request == Returns (hardcoded) number of current DTC codes
{
if (milResponse.length())
{
ELMulator.writeResponse(milResponse);
return;
}
else
{
DEBUG("MIL response is empty.");
ELMulator.writePidNotSupported();
return;
}
}
else if (ELMulator.isMode22(request)) // Mode 0x22 (extended info) service - Mfg-dependent and not defined in OBDII spec
{
Serial.print("Mode 22 PID: "); Serial.println(request.substring(2));
if (request.substring(2).compareTo("52") == 0) // Ethanol percent
{
if (ethPercentResponse.length())
{
ELMulator.writeResponse(ethPercentResponse);
return;
}
else
{
DEBUG("ETH % response is empty.");
ELMulator.writePidNotSupported();
return;
}
}
if (request.substring(2).compareTo("18E4") == 0) // DPF Clogging A-R Giulia
{
if (dpfCloggingResponse.length())
{
ELMulator.writeResponse(dpfCloggingResponse);
return;
}
else
{
DEBUG("DPF Clogging response is empty.");
ELMulator.writePidNotSupported();
return;
}
}
ELMulator.writePidNotSupported();
return;
}
else // Handle any other supported PID request
{
uint8_t pidCode = ELMulator.getPidCode(request); // Extract the specific PID code from the request
// Example response for 0x05 (Engine Coolant Temp) - returning a mock data value
if (pidCode == ENGINE_COOLANT_TEMP)
{
uint32_t sensorValue = ELMulator.getMockSensorValue();
/**
* Response parameters:
* request - specifies the original PID request being responded to.
* numberOfBytes - the number of bytes this PID value returns (per OBDII spec), using lookup table responseBytes.
* sensorValue - the value to return in our response; a mock value in this example.
*/
ELMulator.writePidResponse(request, responseBytes[pidCode], sensorValue);
return;
}
// Example response for PID 0x0C (Engine RPM) - returns a modified mock sensor value
else if (pidCode == ENGINE_RPM)
{
uint16_t rpmValue = ELMulator.getMockSensorValue() * 100; // Here we multiply the mock value provided, for a more realistic RPM number
ELMulator.writePidResponse(request, responseBytes[pidCode], rpmValue);
return;
}
// Example response for PID 0xA6 (Odometer) - returns our hardcoded odometer response string
else if (pidCode == ODOMETER)
{
ELMulator.writePidResponse(request, responseBytes[pidCode], odoResponse);
return;
}
// // Example response for PID 0xOD (Vehicle speed) - returns a value from external GPS
// else if (pidCode == VEHICLE_SPEED)
// {
// uint32_t gpsSpeed = myGPS.speed();
// ELMulator.writePidResponse(request, responseBytes[pidCode], gpsSpeed);
// return;
// }
// Default response for any other supported PID request - returns an unmodified mock sensor value
else
{
ELMulator.writePidResponse(request, responseBytes[pidCode], ELMulator.getMockSensorValue());
return;
}
}
}