Skip to content

Commit

Permalink
Adds WiFi capability to ELMulator
Browse files Browse the repository at this point in the history
  • Loading branch information
jimwhitelaw committed Apr 23, 2024
1 parent ab3c60d commit 08ac2c5
Show file tree
Hide file tree
Showing 12 changed files with 471 additions and 27 deletions.
6 changes: 6 additions & 0 deletions examples/ESP32_Wifi/ESP32_WiFi_Custom_ELMulator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#pragma once
#include <Arduino.h>
#include <ELMulator.h>
#include <definitions.h>

void handlePIDRequest(const String& pidRequest);
172 changes: 172 additions & 0 deletions examples/ESP32_Wifi/ESP32_WiFi_Custom_ELMulator.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
#include "ESP32_WiFi_Custom_ELMulator.h"

const String deviceName = "OBDII";

// 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;
}
}
}
15 changes: 8 additions & 7 deletions src/ATCommands.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
#include "ATCommands.h"
#include "definitions.h"


#if USE_WIFI
ATCommands::ATCommands(OBDWiFiComm *connection) {
this->connection = connection;
}
#else
ATCommands::ATCommands(OBDSerialComm *connection) {
this->connection = connection;
}
#endif

ATCommands::~ATCommands() {
operator delete(this->connection);
Expand Down Expand Up @@ -70,17 +76,12 @@ void ATCommands::processCommand(const String& command) {
// set all to defaults
void ATCommands::ATD() {
connection->setToDefaults();
connection->writeTo("BUS INIT: ...");
connection->writeEndOK();

}


// reset all
void ATCommands::ATZ() {
connection->setEcho(true);
connection->setStatus(connection->IDLE);
connection->writeTo(ID);
connection->setToDefaults();
connection->writeEndOK();
}

Expand Down
16 changes: 13 additions & 3 deletions src/ATCommands.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,33 @@
#define ELMulator_ATCommands_h

#include <Arduino.h>
#include "OBDSerialComm.h"
#include "definitions.h"
#if USE_WIFI
#include "OBDWiFiComm.h"
#else
#include "OBDSerialComm.h"
#endif

class ATCommands
{

public:
#if USE_WIFI
ATCommands(OBDWiFiComm *connection);
#else
ATCommands(OBDSerialComm *connection);

#endif
~ATCommands();

bool process(const String &string);

private:
// Variables
#if USE_WIFI
OBDWiFiComm *connection;
#else
OBDSerialComm *connection;

#endif
void ATD();

void ATZ();
Expand Down
14 changes: 9 additions & 5 deletions src/ELMulator.cpp
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
#include "ELMulator.h"

ELMulator::ELMulator(uint32_t baudRate, uint8_t rxPin, uint8_t txPin)
#if USE_WIFI
ELMulator::ELMulator()
{
_connection = new OBDSerialComm(baudRate, rxPin, txPin);
_connection = new OBDWiFiComm();
_atProcessor = new ATCommands(_connection);
_pidProcessor = new PidProcessor(_connection);
_lastCommand = "";
elmRequest.reserve(MAX_REQUEST_SIZE);
elmRequest = "";
}

ELMulator::ELMulator()
#else
ELMulator::ELMulator(uint32_t baudRate, uint8_t rxPin, uint8_t txPin)
{
_connection = new OBDSerialComm();
_connection = new OBDSerialComm(baudRate, rxPin, txPin);
_atProcessor = new ATCommands(_connection);
_pidProcessor = new PidProcessor(_connection);
_lastCommand = "";
elmRequest.reserve(MAX_REQUEST_SIZE);
elmRequest = "";
}
#endif



ELMulator::~ELMulator() {}

Expand Down
12 changes: 11 additions & 1 deletion src/ELMulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
#define ELMulator_ELMulator_h

#include <Arduino.h>
#include "definitions.h"

#if USE_WIFI
#include "OBDWiFiComm.h"
#else
#include "OBDSerialComm.h"
#endif

#include "ATCommands.h"
#include "PidProcessor.h"
#include "definitions.h"
Expand Down Expand Up @@ -108,8 +115,11 @@ class ELMulator
String elmRequest;

private:
#if USE_WIFI
OBDWiFiComm *_connection;
#else
OBDSerialComm *_connection;

#endif
ATCommands *_atProcessor;

PidProcessor *_pidProcessor;
Expand Down
12 changes: 4 additions & 8 deletions src/OBDSerialComm.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@
#include <Arduino.h>
#include "definitions.h"

#ifndef BLUETOOTH_BUILTIN
#include <SoftwareSerial.h>
#else
#include <BluetoothSerial.h>
#endif


class OBDSerialComm
{
Expand All @@ -19,9 +16,8 @@ class OBDSerialComm
READY = 1
};

#ifndef BLUETOOTH // Bluetooth is not built in, we are using a BT module via GPIO
OBDSerialComm(uint32_t baudRate, uint8_t rxPin, uint8_t txPin);
#endif
// // Bluetooth is not built in, we are using a BT module via GPIO
// OBDSerialComm(uint32_t baudRate, uint8_t rxPin, uint8_t txPin);

OBDSerialComm();

Expand Down Expand Up @@ -85,7 +81,7 @@ class OBDSerialComm
void addSpacesToResponse(const char *response, char string[]);

#ifndef BLUETOOTH_BUILTIN
SoftwareSerial *serial; // lib to communicate with bluetooth
HardwareSerial *serial; // lib to communicate with bluetooth
#else
BluetoothSerial *serial;
#endif
Expand Down
Loading

0 comments on commit 08ac2c5

Please sign in to comment.