- Move user sketches to 10-OBD-Display-2024/, one folder per sketch - Keep 2-Program Example/Demo_Arduino vendor-only, single docs/ copy - Remove duplicate screenshots/instructions and ELMulator zip - Archive broken LVGL 9 attempt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
218 lines
4.9 KiB
C++
218 lines
4.9 KiB
C++
#include "ELMulator.h"
|
|
|
|
#if USE_WIFI
|
|
ELMulator::ELMulator()
|
|
{
|
|
_connection = new OBDWiFiComm();
|
|
_atProcessor = new ATCommands(_connection);
|
|
_pidProcessor = new PidProcessor(_connection);
|
|
_lastCommand = "";
|
|
elmRequest.reserve(MAX_REQUEST_SIZE);
|
|
elmRequest = "";
|
|
}
|
|
#else
|
|
ELMulator::ELMulator()
|
|
{
|
|
_connection = new OBDSerialComm();
|
|
_atProcessor = new ATCommands(_connection);
|
|
_pidProcessor = new PidProcessor(_connection);
|
|
_lastCommand = "";
|
|
elmRequest.reserve(MAX_REQUEST_SIZE);
|
|
elmRequest = "";
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
ELMulator::~ELMulator() {}
|
|
|
|
void ELMulator::init(const String &deviceName, bool registerPids)
|
|
{
|
|
_connection->init(deviceName);
|
|
if (registerPids)
|
|
{
|
|
registerAllMode01Pids();
|
|
}
|
|
}
|
|
|
|
void ELMulator::registerAllMode01Pids()
|
|
{
|
|
// Set up PIDs 0x00 to 0x65
|
|
// Will be provided a response with mock data value if no special handling is performed.
|
|
// Requests for PID > 0x65 are not currently supported - will return "NO DATA"
|
|
for (int i = 0; i < 0x66; i++)
|
|
{
|
|
_pidProcessor->registerMode01Pid(i);
|
|
}
|
|
}
|
|
|
|
bool ELMulator::readELMRequest()
|
|
{
|
|
do
|
|
{
|
|
elmRequest.clear(); // clear buffer from previous requests
|
|
_connection->readData(elmRequest);
|
|
elmRequest.toUpperCase();
|
|
// TODO ignore spaces, and all control chars (tab, etc)
|
|
// TODO accept single carriage return as repeat last command at or pid
|
|
// ignore null i.e 00
|
|
} while (processRequest(elmRequest)); // processRequest handles all non PID requests (AT commands, errors etc)
|
|
return true; // We have received a valid PID request we need to respond to
|
|
}
|
|
|
|
void ELMulator::sendELMResponse()
|
|
{
|
|
// Respond to any mode 01 request with a mock sensor value
|
|
if (isMode01(elmRequest))
|
|
{
|
|
uint8_t pidCode = getPidCode(elmRequest);
|
|
writePidResponse(elmRequest, responseBytes[pidCode], getMockSensorValue());
|
|
return;
|
|
}
|
|
// Not a mode 01 PID request. Report it as not supported (ie, "NO DATA");
|
|
else
|
|
{
|
|
writePidNotSupported();
|
|
}
|
|
}
|
|
|
|
void ELMulator::begin()
|
|
{
|
|
while (readELMRequest())
|
|
{
|
|
sendELMResponse();
|
|
}
|
|
}
|
|
|
|
uint8_t ELMulator::getPidCode(const String &request)
|
|
{
|
|
return _pidProcessor->getPidCodeFromRequest(request);
|
|
}
|
|
|
|
bool ELMulator::registerMode01Pid(uint32_t pid)
|
|
{
|
|
return _pidProcessor->registerMode01Pid(pid);
|
|
}
|
|
|
|
bool ELMulator::registerMode01MILResponse(const String &response)
|
|
{
|
|
return _pidProcessor->registerMode01MILResponse(response);
|
|
}
|
|
|
|
bool ELMulator::registerMode03Response(const String &response)
|
|
{
|
|
return _pidProcessor->registerMode03Response(response);
|
|
}
|
|
|
|
void ELMulator::writePidNotSupported()
|
|
{
|
|
_connection->writeEndNoData();
|
|
}
|
|
|
|
void ELMulator::writePidResponse(const String &requestPid, uint8_t numberOfBytes, uint32_t value)
|
|
{
|
|
_pidProcessor->writePidResponse(requestPid, numberOfBytes, value);
|
|
}
|
|
|
|
void ELMulator::writeResponse(const String &response)
|
|
{
|
|
_connection->writeTo(response.c_str());
|
|
_connection->writeEnd();
|
|
}
|
|
|
|
bool ELMulator::processRequest(String &command)
|
|
{
|
|
|
|
// TODO: check for no 0X0D char in command, return ? and >
|
|
// TODO: receive only <cr>, repeat last command
|
|
|
|
// carriage return, means repeat last command
|
|
if (command == "\n")
|
|
{
|
|
command = _lastCommand;
|
|
}
|
|
|
|
// empty command, do not send to user
|
|
if (command.length() == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Check for AT command
|
|
if (_atProcessor->process(command))
|
|
{
|
|
_lastCommand = command;
|
|
return true;
|
|
}
|
|
|
|
// Check for a valid hex string, return error if false
|
|
if (!isValidHex(command.c_str()))
|
|
{
|
|
_connection->writeEndUnknown();
|
|
DEBUG("Invalid HEX command: " + command);
|
|
return true;
|
|
}
|
|
|
|
// Check for a valid PID request
|
|
if (_pidProcessor->process(command))
|
|
{
|
|
_lastCommand = command;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ELMulator::isValidHex(const char *pid)
|
|
{
|
|
return (pid[strspn(pid, "0123456789abcdefABCDEF")] == 0);
|
|
}
|
|
|
|
bool ELMulator::isMode01(const String &command)
|
|
{
|
|
return _pidProcessor->isMode01(command);
|
|
}
|
|
|
|
bool ELMulator::isMode22(const String &command)
|
|
{
|
|
return _pidProcessor->isMode22(command);
|
|
}
|
|
|
|
bool ELMulator::isMode03(const String &command)
|
|
{
|
|
return _pidProcessor->isMode03(command);
|
|
}
|
|
|
|
bool ELMulator::isMode01MIL(const String &command)
|
|
{
|
|
return _pidProcessor->isMode01MIL(command);
|
|
}
|
|
|
|
/**
|
|
* Fake some data
|
|
* start counter = 0
|
|
* every call to getFakeSensorData, will increment the counter to a max of 255 (0xff)
|
|
* When 0xff is reached the cycle is reverted
|
|
* and the counter will decrement by one until 0 is reached
|
|
*/
|
|
uint32_t ELMulator::getMockSensorValue()
|
|
{
|
|
if (isCycleUp)
|
|
{
|
|
cycle++;
|
|
if (cycle == 0xff)
|
|
{
|
|
isCycleUp = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cycle--;
|
|
if (cycle == 0)
|
|
{
|
|
isCycleUp = true;
|
|
}
|
|
}
|
|
|
|
return cycle;
|
|
} |