- 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>
173 lines
6.3 KiB
C++
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;
|
|
}
|
|
}
|
|
}
|