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>
This commit is contained in:
1 parent
9cefb9ba75
commit
5c04e3f0df
81 files changed
+207
-4807
No files matched your search
@@ -0,0 +1,6 @@
|
||||
# PlatformIO build output and IDE files
|
||||
.pio/
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
@@ -7,23 +7,38 @@ Because the ESP32-S3 has no Bluetooth Classic, the design used a second (classic
|
||||
ELM327 --(BT Classic SPP)--> classic ESP32 (elm327_wifi_bridge) --(WiFi AP, HTTP /speed)--> JC3636W518 (display_speed_wifi)
|
||||
```
|
||||
|
||||
Split out of `2-Program Example/Demo_Arduino/ST77916_LVGL_DEMO/` on 2026-10-22. History is in git.
|
||||
Split out of `2-Program Example/Demo_Arduino/ST77916_LVGL_DEMO/` on 2026-10-22 and converted to PlatformIO. History is in git.
|
||||
|
||||
## Sketches
|
||||
## Projects (all PlatformIO)
|
||||
| Folder | Runs on | What it does | Was |
|
||||
|---|---|---|---|
|
||||
| `display_speed_wifi/` | JC3636W518 | GIF background from SD (`A:wallpaper.gif`) + joins AP `ESP32-Access-Point`, polls `http://192.168.4.1/speed`, shows it in a label | `ST77916_LVGL_DEMO/8.4/` |
|
||||
| `display_gif_sd/` | JC3636W518 | GIF from SD (`A:wallpaper.gif`) + static text label | `ST77916_LVGL_DEMO/working version/` |
|
||||
| `display_gif_embedded/` | JC3636W518 | GIF compiled into firmware (`loligod.c`, 13.9 MB source). WiFi code commented out. Stops in `setup()` if no SD card | `ST77916_LVGL_DEMO/` (top level) |
|
||||
| `elm327_wifi_bridge/` | **classic ESP32** (not S3) | BT Classic → ELM327 named `OBDII` (PIN 1234), WiFi AP `ESP32-Access-Point` / `123456789`, serves `/speed` (kph). Needs esp32 core 2.0.x, ESPAsyncWebServer, ELMduino | `ST77916_LVGL_DEMO/esp23_elm327_to_wifi/` |
|
||||
| `elmulator/` | classic ESP32 | ELMulator library + examples: fake ELM327 over BT/WiFi for testing without a car | `ST77916_LVGL_DEMO/ELMulator-master/` |
|
||||
| `display_gif_embedded/` | JC3636W518 | GIF compiled into firmware (`src/loligod.c`, 2.2 MB GIF → 3.4 MB firmware, uses `default_16MB.csv` with 6.25 MB app slots). WiFi code commented out. Stops in `setup()` if no SD card | `ST77916_LVGL_DEMO/` (top level) |
|
||||
| `elm327_wifi_bridge/` | **classic ESP32** (not S3) | BT Classic → ELM327 named `OBDII` (PIN 1234), WiFi AP `ESP32-Access-Point` / `123456789`, serves `/speed` (kph) | `ST77916_LVGL_DEMO/esp23_elm327_to_wifi/` |
|
||||
| `elmulator/` | classic ESP32 | Fake ELM327 for testing without a car. Library in `lib/ELMulator`, envs `simple`, `custom`, `wifi` (BT name `OBDII`) | `ST77916_LVGL_DEMO/ELMulator-master/` |
|
||||
| `assets/gifs/` | — | GIF files (`dump/` = unused collection) | `ST77916_LVGL_DEMO/gifs/` |
|
||||
| `_archive/lvgl9_broken/` | — | Unfinished LVGL 9 port, does not compile | `ST77916_LVGL_DEMO/9.1/` |
|
||||
| `_archive/lvgl9_broken/` | — | Unfinished LVGL 9 port, does not compile, not converted | `ST77916_LVGL_DEMO/9.1/` |
|
||||
|
||||
## Notes
|
||||
- Each display sketch carries its own `pincfg.h`, `scr_st77916.h` and `lv_conf.h`. The Arduino LVGL library does **not** read the sketch's `lv_conf.h` — the one used is `Documents\Arduino\libraries\lv_conf.h`. Copy the sketch's version there before building:
|
||||
## Build / upload
|
||||
Open a project folder in VS Code with the PlatformIO extension, or from a terminal:
|
||||
```
|
||||
pio run -d display_speed_wifi # build
|
||||
pio run -d display_speed_wifi -t upload # flash (COM21)
|
||||
pio device monitor -d display_speed_wifi # serial monitor
|
||||
pio run -d elmulator -e wifi -t upload --upload-port COMxx
|
||||
```
|
||||
(`pio` = `%USERPROFILE%\.platformio\penv\Scripts\pio.exe` if not on PATH.)
|
||||
|
||||
## Configuration
|
||||
- **JC3636W518 projects:** pioarduino platform `51.03.05` (= Arduino core 3.0.5), board `esp32-s3-devkitc-1` with 16 MB QIO flash, OPI PSRAM, `huge_app.csv` (3 MB app), USB-CDC on boot, COM21.
|
||||
Libraries come from the vendor folder via `lib_extra_dirs = ../../2-Program Example/Demo_Arduino/library`.
|
||||
Each project uses its **own `include/lv_conf.h`** (`-DLV_CONF_INCLUDE_SIMPLE -Iinclude`):
|
||||
- `display_speed_wifi`: LVGL heap 4 MB in PSRAM (`ps_malloc`)
|
||||
- `display_gif_sd`: LVGL heap 7 MB in PSRAM
|
||||
- `montserrat_100.c` (100 px digits font) is present but not referenced by any sketch yet.
|
||||
- `display_gif_embedded` will not fit the 3 MB "Huge APP" partition with `loligod.c`; needs a custom partition table or the GIF on SD.
|
||||
- SD-based GIFs use LVGL drive letter `A:`, which needs an LVGL filesystem driver configured in `lv_conf.h`.
|
||||
- **Classic ESP32 projects:** official `espressif32@6.10.0` (Arduino core 2.0.17), board `esp32dev`, `huge_app.csv`. Set `upload_port` to the classic ESP32's COM port.
|
||||
Bridge libraries: `powerbroker2/ELMduino@3.4.1`, `esphome/ESPAsyncWebServer-esphome@3.4.0`.
|
||||
|
||||
## Notes
|
||||
- `src/montserrat_100.c` (100 px digits font) is present but not referenced by any sketch yet.
|
||||
- SD-based GIFs use LVGL drive `A:` = `LV_USE_FS_FATFS` (reads through ESP-IDF FatFS on the SD_MMC mount). Not yet verified on hardware.
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,29 @@
|
||||
; JC3636W518 — ESP32-S3R8, 16 MB QIO flash, 8 MB OPI PSRAM, 360x360 ST77916 QSPI + CST816S
|
||||
; Settings mirror the vendor Arduino IDE setup (2-Program Example/Demo_Arduino/docs/Arduino set up.png)
|
||||
|
||||
[env:jc3636w518]
|
||||
; pioarduino = Arduino core 3.0.5 (ESP32_Display_Panel 0.1.4 needs core 3.x)
|
||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||
board = esp32-s3-devkitc-1
|
||||
framework = arduino
|
||||
|
||||
board_build.arduino.memory_type = qio_opi
|
||||
board_build.flash_mode = qio
|
||||
board_upload.flash_size = 16MB
|
||||
; GIF compiled into firmware -> 3.4 MB, too big for huge_app (3 MB); 6.25 MB app slots
|
||||
board_build.partitions = default_16MB.csv
|
||||
|
||||
build_flags =
|
||||
-DBOARD_HAS_PSRAM
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
; LVGL (also when compiled as a library) picks up include/lv_conf.h of this project
|
||||
-DLV_CONF_INCLUDE_SIMPLE
|
||||
-Iinclude
|
||||
|
||||
; vendor-bundled libraries: ESP32_Display_Panel 0.1.4, ESP32_IO_Expander 0.0.2, lvgl 8.3.11
|
||||
lib_extra_dirs = ../../2-Program Example/Demo_Arduino/library
|
||||
|
||||
upload_port = COM21
|
||||
monitor_port = COM21
|
||||
monitor_speed = 115200
|
||||
monitor_filters = esp32_exception_decoder
|
||||
File renamed without changes.
+3
-1
@@ -1,7 +1,7 @@
|
||||
#include <Arduino.h>
|
||||
#include "scr_st77916.h"
|
||||
#include "SD_MMC.h"
|
||||
|
||||
#include "loligod.c"
|
||||
|
||||
// https://randomnerdtutorials.com/esp32-client-server-wi-fi/
|
||||
#include <WiFi.h>
|
||||
@@ -10,6 +10,8 @@
|
||||
const char* ssid = "ESP32-Access-Point";
|
||||
const char* password = "123456789";
|
||||
|
||||
String httpGETRequest(const char* serverName);
|
||||
|
||||
|
||||
void my_print(const char* buf) {
|
||||
Serial.println(buf); // Print logs to the serial monitor
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,28 @@
|
||||
; JC3636W518 — ESP32-S3R8, 16 MB QIO flash, 8 MB OPI PSRAM, 360x360 ST77916 QSPI + CST816S
|
||||
; Settings mirror the vendor Arduino IDE setup (2-Program Example/Demo_Arduino/docs/Arduino set up.png)
|
||||
|
||||
[env:jc3636w518]
|
||||
; pioarduino = Arduino core 3.0.5 (ESP32_Display_Panel 0.1.4 needs core 3.x)
|
||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||
board = esp32-s3-devkitc-1
|
||||
framework = arduino
|
||||
|
||||
board_build.arduino.memory_type = qio_opi
|
||||
board_build.flash_mode = qio
|
||||
board_upload.flash_size = 16MB
|
||||
board_build.partitions = huge_app.csv
|
||||
|
||||
build_flags =
|
||||
-DBOARD_HAS_PSRAM
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
; LVGL (also when compiled as a library) picks up include/lv_conf.h of this project
|
||||
-DLV_CONF_INCLUDE_SIMPLE
|
||||
-Iinclude
|
||||
|
||||
; vendor-bundled libraries: ESP32_Display_Panel 0.1.4, ESP32_IO_Expander 0.0.2, lvgl 8.3.11
|
||||
lib_extra_dirs = ../../2-Program Example/Demo_Arduino/library
|
||||
|
||||
upload_port = COM21
|
||||
monitor_port = COM21
|
||||
monitor_speed = 115200
|
||||
monitor_filters = esp32_exception_decoder
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
#include <Arduino.h>
|
||||
#include "scr_st77916.h"
|
||||
#include "SD_MMC.h"
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,28 @@
|
||||
; JC3636W518 — ESP32-S3R8, 16 MB QIO flash, 8 MB OPI PSRAM, 360x360 ST77916 QSPI + CST816S
|
||||
; Settings mirror the vendor Arduino IDE setup (2-Program Example/Demo_Arduino/docs/Arduino set up.png)
|
||||
|
||||
[env:jc3636w518]
|
||||
; pioarduino = Arduino core 3.0.5 (ESP32_Display_Panel 0.1.4 needs core 3.x)
|
||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||
board = esp32-s3-devkitc-1
|
||||
framework = arduino
|
||||
|
||||
board_build.arduino.memory_type = qio_opi
|
||||
board_build.flash_mode = qio
|
||||
board_upload.flash_size = 16MB
|
||||
board_build.partitions = huge_app.csv
|
||||
|
||||
build_flags =
|
||||
-DBOARD_HAS_PSRAM
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
; LVGL (also when compiled as a library) picks up include/lv_conf.h of this project
|
||||
-DLV_CONF_INCLUDE_SIMPLE
|
||||
-Iinclude
|
||||
|
||||
; vendor-bundled libraries: ESP32_Display_Panel 0.1.4, ESP32_IO_Expander 0.0.2, lvgl 8.3.11
|
||||
lib_extra_dirs = ../../2-Program Example/Demo_Arduino/library
|
||||
|
||||
upload_port = COM21
|
||||
monitor_port = COM21
|
||||
monitor_speed = 115200
|
||||
monitor_filters = esp32_exception_decoder
|
||||
+3
@@ -1,3 +1,4 @@
|
||||
#include <Arduino.h>
|
||||
#include "scr_st77916.h"
|
||||
#include "SD_MMC.h"
|
||||
|
||||
@@ -8,6 +9,8 @@
|
||||
const char* ssid = "ESP32-Access-Point";
|
||||
const char* password = "123456789";
|
||||
|
||||
String httpGETRequest(const char* serverName);
|
||||
|
||||
|
||||
void my_print(const char* buf) {
|
||||
Serial.println(buf); // Print logs to the serial monitor
|
||||
File renamed without changes.
@@ -0,0 +1,18 @@
|
||||
; ELM327 -> WiFi bridge — runs on a CLASSIC ESP32 (needs Bluetooth Classic, which the ESP32-S3 lacks)
|
||||
; Original note: "needs esp32 2.0.* in boards manager" -> official platform 6.10.0 = Arduino core 2.0.17
|
||||
|
||||
[env:esp32dev]
|
||||
platform = espressif32@6.10.0
|
||||
board = esp32dev
|
||||
framework = arduino
|
||||
|
||||
; BluetoothSerial + WiFi + web server do not fit the default 1.2 MB app partition
|
||||
board_build.partitions = huge_app.csv
|
||||
|
||||
lib_deps =
|
||||
powerbroker2/ELMduino@3.4.1
|
||||
esphome/ESPAsyncWebServer-esphome@3.4.0
|
||||
|
||||
; upload_port = COMxx ; set to the classic ESP32's port (COM21 is the JC3636W518)
|
||||
monitor_speed = 115200
|
||||
monitor_filters = esp32_exception_decoder
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
#include <Arduino.h>
|
||||
// Import required libraries
|
||||
#include "WiFi.h"
|
||||
#include <ESPAsyncWebServer.h>
|
||||
@@ -4,5 +4,4 @@
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
.vscode/extensions.json
|
||||
platformio.ini
|
||||
|
||||
-162
@@ -1,162 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
|
||||
bool ATCommands::process(const String& command) {
|
||||
bool processed = false;
|
||||
if (isATCommand(command)) {
|
||||
processed = true;
|
||||
processCommand(command);
|
||||
DEBUG("RX AT: " + command);
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
|
||||
void ATCommands::processCommand(const String& command) {
|
||||
|
||||
// if space is enabled (ex: AT H[charPosition])
|
||||
uint8_t offset = 2;
|
||||
if (command.charAt(2) == 0x20) {
|
||||
offset = 3;
|
||||
}
|
||||
|
||||
// refer to ELM327 specs
|
||||
String specificCommand = command.substring(offset, command.length());
|
||||
if (specificCommand.startsWith("D",offset)) {
|
||||
ATCommands::ATD();
|
||||
} else if (specificCommand.startsWith("Z")) {
|
||||
ATCommands::ATZ();
|
||||
} else if (specificCommand.startsWith("I")) {
|
||||
ATCommands::ATI();
|
||||
} else if (specificCommand.startsWith("E")) {
|
||||
ATCommands::ATEx(specificCommand);
|
||||
} else if (specificCommand.startsWith("L")) {
|
||||
ATCommands::ATLx(specificCommand);
|
||||
} else if (specificCommand.startsWith("M")) {
|
||||
ATCommands::ATMx(specificCommand);
|
||||
} else if (specificCommand.startsWith("S")) {
|
||||
if (specificCommand.startsWith("SP")) {
|
||||
ATCommands::ATSPx(specificCommand);
|
||||
} else {
|
||||
ATCommands::ATSx(specificCommand);
|
||||
}
|
||||
} else if (specificCommand.startsWith("H")) {
|
||||
ATCommands::ATHx(specificCommand);
|
||||
} else if (specificCommand.startsWith("AT")) {
|
||||
ATCommands::ATATx(specificCommand);
|
||||
} else if (specificCommand.startsWith("DPN")) {
|
||||
ATCommands::ATDPN();
|
||||
} else if (specificCommand.startsWith("DESC") || specificCommand.startsWith("@1")) {
|
||||
ATCommands::ATDESC();
|
||||
} else if (specificCommand.startsWith("PC")) {
|
||||
ATCommands::ATPC();
|
||||
} else if (specificCommand.startsWith("RV")){
|
||||
ATCommands::ATRV();
|
||||
} else {
|
||||
|
||||
// lets assume we process any at command
|
||||
connection->writeEndOK();
|
||||
}
|
||||
}
|
||||
|
||||
// set all to defaults
|
||||
void ATCommands::ATD() {
|
||||
connection->setToDefaults();
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// reset all
|
||||
void ATCommands::ATZ() {
|
||||
connection->setToDefaults();
|
||||
connection->writeTo(ID);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// Print the version ID
|
||||
void ATCommands::ATI() {
|
||||
connection->writeTo(ID);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// send description
|
||||
void ATCommands::ATDESC() {
|
||||
connection->writeTo(DESC);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// set echoEnable 0=off 1=on
|
||||
void ATCommands::ATEx(String& cmd) {
|
||||
connection->setEcho(cmd.equals("E0") ? false : true);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// set memory off=0 on=1
|
||||
void ATCommands::ATMx(String& cmd) {
|
||||
connection->setMemory(cmd.equals("M0") ? false : true);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// line feeds off=0 on=1
|
||||
void ATCommands::ATLx(String& cmd) {
|
||||
connection->setLineFeeds(cmd.equals("L0") ? false : true);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// ATSx printing spaces off=0 on=1
|
||||
void ATCommands::ATSx(String& cmd) {
|
||||
connection->setWhiteSpaces(cmd.equals("S0") ? false : true);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// Headers off=0 on=1
|
||||
void ATCommands::ATHx(String& cmd) {
|
||||
connection->setHeaders(cmd.equals("H0") ? false : true);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// ATSPx Define protocol 0=auto
|
||||
void ATCommands::ATSPx(String& cmd) {
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// set protocol
|
||||
void ATCommands::ATDPN() {
|
||||
connection->writeTo(PROTOCOL);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// AT AT2 adaptative time control
|
||||
void ATCommands::ATATx(String& cmd) {
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// Terminates current diagnostic session. Protocol close
|
||||
void ATCommands::ATPC() {
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
void ATCommands::ATRV() {
|
||||
connection->writeTo("AT RV\r13.5V");
|
||||
connection->writeEnd();
|
||||
}
|
||||
|
||||
// return true ir connectionand is AT
|
||||
bool ATCommands::isATCommand(const String& command) {
|
||||
toUpperCase(command.charAt(0));
|
||||
return command.startsWith("AT") ? true : false;
|
||||
}
|
||||
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
#ifndef ELMulator_ATCommands_h
|
||||
#define ELMulator_ATCommands_h
|
||||
|
||||
#include <Arduino.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();
|
||||
|
||||
void ATI();
|
||||
|
||||
void ATEx(String &x);
|
||||
|
||||
void ATMx(String &x);
|
||||
|
||||
void ATLx(String &x);
|
||||
|
||||
void ATSx(String &x);
|
||||
|
||||
void ATHx(String &x);
|
||||
|
||||
void ATSPx(String &x);
|
||||
|
||||
void ATATx(String &cmd);
|
||||
|
||||
void ATPC();
|
||||
|
||||
void ATDPN();
|
||||
|
||||
void ATDESC();
|
||||
|
||||
void ATRV();
|
||||
|
||||
void processCommand(const String &command);
|
||||
|
||||
bool isATCommand(const String &command);
|
||||
};
|
||||
|
||||
#endif
|
||||
-218
@@ -1,218 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
-138
@@ -1,138 +0,0 @@
|
||||
#ifndef ELMulator_ELMulator_h
|
||||
#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"
|
||||
|
||||
class ELMulator
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* When creating a new instance of ELMulator with an attached
|
||||
* BT module, we need to set the Bluetooth serial parameters
|
||||
*
|
||||
* @param baudRate - rate for bluetooth serial port (normally 9600)
|
||||
* @param rxPin - the Arduino RX pin for the Bluetooth module (serial port)
|
||||
* @param txPin - the Arduino TX pin for the Bluetooth module (serial port)
|
||||
* @return
|
||||
*/
|
||||
ELMulator(uint32_t baudRate, uint8_t rxPin, uint8_t txPin);
|
||||
|
||||
ELMulator();
|
||||
|
||||
~ELMulator();
|
||||
|
||||
void init(const String &deviceName, const bool registerPids = false);
|
||||
|
||||
/**
|
||||
* Waits and Reads a request from OBD client software (example: torque)
|
||||
*
|
||||
* It will only return if the OBD client software
|
||||
* request's one of the previous registred PID (see: registerMode01Pid())
|
||||
*
|
||||
* Any other request will be automaticly handle or it will be rejected.
|
||||
*
|
||||
* ELMulator lib will automaticly handle:
|
||||
* - any AT command
|
||||
* - PID's support request like 0100, 0120, 0140, 0160, ...
|
||||
*
|
||||
* @return the request PID (Vehicle speed ex: 010D )
|
||||
*/
|
||||
bool readELMRequest();
|
||||
void sendELMResponse();
|
||||
void begin();
|
||||
|
||||
/**
|
||||
* Registry the PID's (sensors) your arduino will support.
|
||||
* Currently ELMulator lib only supports MODE 01 PID's
|
||||
*
|
||||
* In OBDII MODE 01 PID's, are request
|
||||
* to Show current data for a given sensor identified by a PID
|
||||
*
|
||||
* List of MODE 01 PID's: https://en.wikipedia.org/wiki/OBD-II_PIDs#Mode_01
|
||||
*
|
||||
* Example, for Vehicle speed, where OBDII PID code is 0D:
|
||||
*
|
||||
* registerMode01Pid(0x0D)
|
||||
*
|
||||
* @param pidHexId
|
||||
* @return returns true if the entered ID is in the valide range [0x00, 0xFF]
|
||||
*/
|
||||
bool registerMode01Pid(uint32_t pidHexId);
|
||||
|
||||
bool registerMode01MILResponse(const String &response);
|
||||
|
||||
bool registerMode03Response(const String &response);
|
||||
|
||||
uint8_t getPidCodeOnly(uint16_t hexCommand);
|
||||
|
||||
/**
|
||||
* Used to informe the OBD client that the PID,
|
||||
* it has requested is not supported
|
||||
*
|
||||
* In normal conditions, you will only receive,
|
||||
* request for the PID you have registred
|
||||
* All other request is automaticly handle by GTTuboEcu lib
|
||||
*/
|
||||
void writePidNotSupported();
|
||||
|
||||
/**
|
||||
* Respond to a given PID request from OBDII client (ex: torque)
|
||||
*
|
||||
* @param requestPid - the received pid
|
||||
* @param numberOfBytes - number of bytes this PID response VALUE is supposed to have
|
||||
* @param value - sensor value for this PID,
|
||||
* lib will convert it to HEX
|
||||
* with the appropriated number of HEX chars (numberOfChars)
|
||||
*/
|
||||
void writePidResponse(const String &requestPid, uint8_t numberOfBytes, uint32_t value);
|
||||
|
||||
// Write the response back to the requestor without PID formatting, etc
|
||||
// Just pass the response string on. Useful for testing with a specific response
|
||||
// that has been pre-configured.
|
||||
void writeResponse(const String &response);
|
||||
|
||||
bool isMode01(const String &command);
|
||||
bool isMode03(const String &command);
|
||||
bool isMode01MIL(const String &command);
|
||||
bool isMode22(const String &command);
|
||||
|
||||
uint8_t getPidCode(const String &request);
|
||||
void registerAllMode01Pids();
|
||||
uint32_t getMockSensorValue();
|
||||
|
||||
String elmRequest;
|
||||
|
||||
private:
|
||||
#if USE_WIFI
|
||||
OBDWiFiComm *_connection;
|
||||
#else
|
||||
OBDSerialComm *_connection;
|
||||
#endif
|
||||
ATCommands *_atProcessor;
|
||||
|
||||
PidProcessor *_pidProcessor;
|
||||
|
||||
String _lastCommand;
|
||||
|
||||
bool isCycleUp = true;
|
||||
|
||||
uint32_t cycle = 0;
|
||||
|
||||
bool processRequest(String &command);
|
||||
|
||||
bool isValidHex(const char *pid);
|
||||
};
|
||||
|
||||
#endif
|
||||
-145
@@ -1,145 +0,0 @@
|
||||
#include "OBDSerialComm.h"
|
||||
#include "definitions.h"
|
||||
|
||||
|
||||
OBDSerialComm::OBDSerialComm() {
|
||||
|
||||
}
|
||||
|
||||
OBDSerialComm::~OBDSerialComm() {
|
||||
operator delete(serial);
|
||||
}
|
||||
|
||||
void OBDSerialComm::init(const String& deviceName) {
|
||||
Serial.println("Starting BT . . .");
|
||||
serial = new BluetoothSerial();
|
||||
//delay(2000);
|
||||
serial->begin(deviceName, false);
|
||||
setToDefaults();
|
||||
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeEnd() {
|
||||
|
||||
// 1 - write carriage return
|
||||
writeTo("\r");
|
||||
|
||||
// 2- (optional ) write linefeed
|
||||
if (lineFeedEnable) {
|
||||
writeTo("\n");
|
||||
}
|
||||
|
||||
// 3 - Write prompt
|
||||
writeTo(">");
|
||||
|
||||
serial->flush();
|
||||
};
|
||||
|
||||
|
||||
void OBDSerialComm::writeEndOK() {
|
||||
writeTo("OK");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeEndERROR() {
|
||||
writeTo("ERROR");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeEndNoData() {
|
||||
writeTo("NO DATA");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeEndUnknown() {
|
||||
writeTo("?");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDSerialComm::setToDefaults() {
|
||||
setEcho(true);
|
||||
setStatus(READY);
|
||||
setWhiteSpaces(true);
|
||||
setHeaders(false);
|
||||
setLineFeeds(true);
|
||||
setMemory(false);
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeTo(char const *response) {
|
||||
serial->print(response);
|
||||
}
|
||||
|
||||
|
||||
void OBDSerialComm::writeTo(uint8_t cChar) {
|
||||
serial->print(cChar);
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeEndPidTo(char const *response) {
|
||||
if (whiteSpacesEnabled) {
|
||||
uint8_t len = strlen(response);
|
||||
char spacedResponse[len + len / 2 + 1];
|
||||
addSpacesToResponse(response, spacedResponse);
|
||||
writeTo(spacedResponse);
|
||||
} else {
|
||||
writeTo(response);
|
||||
}
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDSerialComm::readData(String& rxData) {
|
||||
serial->flush(); // temp remove this
|
||||
rxData = serial->readStringUntil(SERIAL_END_CHAR);
|
||||
if (isEchoEnable()) {
|
||||
writeTo(rxData.c_str());
|
||||
}
|
||||
//return rxData;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setBaudRate(uint32_t rate) {
|
||||
this->baudRate = rate;
|
||||
}
|
||||
|
||||
long OBDSerialComm::getBaudRate() {
|
||||
return this->baudRate;
|
||||
}
|
||||
|
||||
bool OBDSerialComm::isEchoEnable() {
|
||||
return this->echoEnable;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setEcho(bool echo) {
|
||||
this->echoEnable = echo;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setStatus(STATUS status) {
|
||||
this->status = status;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setLineFeeds(bool status) {
|
||||
this->lineFeedEnable = status;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setMemory(bool status) {
|
||||
this->memoryEnabled = status;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setWhiteSpaces(bool status) {
|
||||
this->whiteSpacesEnabled = status;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setHeaders(bool status) {
|
||||
this->headersEnabled = status;
|
||||
}
|
||||
|
||||
void OBDSerialComm::addSpacesToResponse(const char *response, char spacedRes[]) {
|
||||
uint8_t len = strlen(response);
|
||||
int j = 0;
|
||||
for (int i = 0; i < len;) {
|
||||
*(spacedRes + j++) = *(response + i++);
|
||||
*(spacedRes + j++) = *(response + i++);
|
||||
if (i < len) {
|
||||
*(spacedRes + j++) = 0x20;
|
||||
}
|
||||
}
|
||||
*(spacedRes + j) = '\0';
|
||||
}
|
||||
-90
@@ -1,90 +0,0 @@
|
||||
#ifndef ELMulator_OBDSerialComm_h
|
||||
#define ELMulator_OBDSerialComm_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "definitions.h"
|
||||
|
||||
#include <BluetoothSerial.h>
|
||||
|
||||
|
||||
class OBDSerialComm
|
||||
{
|
||||
public:
|
||||
enum STATUS
|
||||
{
|
||||
IDLE = 0,
|
||||
READY = 1
|
||||
};
|
||||
|
||||
// // Bluetooth is not built in, we are using a BT module via GPIO
|
||||
// OBDSerialComm(uint32_t baudRate, uint8_t rxPin, uint8_t txPin);
|
||||
|
||||
OBDSerialComm();
|
||||
|
||||
~OBDSerialComm();
|
||||
|
||||
void init(const String &deviceName);
|
||||
|
||||
void writeEndOK();
|
||||
|
||||
void writeEndERROR();
|
||||
|
||||
/**
|
||||
* Response for unsupported PID command or missing sensor, respond "NO DATA".
|
||||
*/
|
||||
void writeEndNoData();
|
||||
|
||||
/**
|
||||
* Response for invalid AT command, or invalid PID or other bad input, respond "?"
|
||||
*/
|
||||
void writeEndUnknown();
|
||||
|
||||
void setToDefaults();
|
||||
|
||||
void readData(String &rxData);
|
||||
|
||||
void writeTo(uint8_t cChar);
|
||||
|
||||
void writeTo(char const *string);
|
||||
|
||||
void setEcho(bool echo);
|
||||
|
||||
void writeEnd();
|
||||
|
||||
bool isEchoEnable();
|
||||
|
||||
void setLineFeeds(bool status);
|
||||
|
||||
void setMemory(bool status);
|
||||
|
||||
void setWhiteSpaces(bool status);
|
||||
|
||||
void setHeaders(bool status);
|
||||
|
||||
void setStatus(STATUS status);
|
||||
|
||||
void writeEndPidTo(char const *string);
|
||||
|
||||
private:
|
||||
uint32_t baudRate; // Serial Baud Rate
|
||||
STATUS status; // Operation status
|
||||
bool echoEnable; // echoEnable command after received
|
||||
bool lineFeedEnable;
|
||||
bool memoryEnabled;
|
||||
bool whiteSpacesEnabled;
|
||||
bool headersEnabled;
|
||||
|
||||
void setBaudRate(uint32_t rate);
|
||||
|
||||
long getBaudRate();
|
||||
|
||||
void addSpacesToResponse(const char *response, char string[]);
|
||||
|
||||
#ifndef BLUETOOTH_BUILTIN
|
||||
HardwareSerial *serial; // lib to communicate with bluetooth
|
||||
#else
|
||||
BluetoothSerial *serial;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
-151
@@ -1,151 +0,0 @@
|
||||
#include "OBDWiFiComm.h"
|
||||
#include "definitions.h"
|
||||
|
||||
WiFiServer server(35000);
|
||||
IPAddress localIP = IPAddress(192, 168, 0, 10);
|
||||
IPAddress gateway = IPAddress(192, 168, 0, 10);
|
||||
IPAddress subnet = IPAddress(255,255,255,0);
|
||||
|
||||
OBDWiFiComm::OBDWiFiComm() {
|
||||
|
||||
}
|
||||
|
||||
OBDWiFiComm::~OBDWiFiComm() {
|
||||
}
|
||||
|
||||
void OBDWiFiComm::init(const String& deviceName) {
|
||||
Serial.println("Starting AP . . .");
|
||||
|
||||
WiFi.softAPConfig(localIP, gateway, subnet);
|
||||
WiFi.softAP(deviceName);
|
||||
|
||||
IPAddress myIP = WiFi.softAPIP();
|
||||
Serial.print("AP IP address: ");
|
||||
Serial.println(myIP);
|
||||
Serial.print("AP SSID: ");
|
||||
Serial.println(deviceName);
|
||||
server.begin();
|
||||
setToDefaults();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeEnd() {
|
||||
|
||||
// 1 - write carriage return
|
||||
writeTo("\r");
|
||||
|
||||
// 2- (optional ) write linefeed
|
||||
if (lineFeedEnable) {
|
||||
writeTo("\n");
|
||||
}
|
||||
|
||||
// 3 - Write prompt
|
||||
writeTo(">");
|
||||
};
|
||||
|
||||
|
||||
void OBDWiFiComm::writeEndOK() {
|
||||
writeTo("OK");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeEndERROR() {
|
||||
writeTo("ERROR");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeEndNoData() {
|
||||
writeTo("NO DATA");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeEndUnknown() {
|
||||
writeTo("?");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setToDefaults() {
|
||||
setEcho(true);
|
||||
setStatus(READY);
|
||||
setWhiteSpaces(true);
|
||||
setHeaders(false);
|
||||
setLineFeeds(true);
|
||||
setMemory(false);
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeTo(char const *response) {
|
||||
client.print(response);
|
||||
}
|
||||
|
||||
|
||||
void OBDWiFiComm::writeTo(uint8_t cChar) {
|
||||
client.print(cChar);
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeEndPidTo(char const *response) {
|
||||
if (this->whiteSpacesEnabled) {
|
||||
uint8_t len = strlen(response);
|
||||
char spacedResponse[len + len / 2 + 1];
|
||||
addSpacesToResponse(response, spacedResponse);
|
||||
writeTo(spacedResponse);
|
||||
} else {
|
||||
writeTo(response);
|
||||
}
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::readData(String& rxData) {
|
||||
if (!client.connected()) {
|
||||
client = server.available();
|
||||
}
|
||||
|
||||
if(client)
|
||||
{
|
||||
rxData
|
||||
= client.readStringUntil(SERIAL_END_CHAR);
|
||||
if (isEchoEnable())
|
||||
{
|
||||
writeTo(rxData.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OBDWiFiComm::isEchoEnable() {
|
||||
return this->echoEnable;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setEcho(bool echo) {
|
||||
this->echoEnable = echo;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setStatus(STATUS status) {
|
||||
this->status = status;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setLineFeeds(bool status) {
|
||||
this->lineFeedEnable = status;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setMemory(bool status) {
|
||||
this->memoryEnabled = status;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setWhiteSpaces(bool status) {
|
||||
this->whiteSpacesEnabled = status;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setHeaders(bool status) {
|
||||
this->headersEnabled = status;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::addSpacesToResponse(const char *response, char spacedRes[]) {
|
||||
uint8_t len = strlen(response);
|
||||
int j = 0;
|
||||
for (int i = 0; i < len;) {
|
||||
*(spacedRes + j++) = *(response + i++);
|
||||
*(spacedRes + j++) = *(response + i++);
|
||||
if (i < len) {
|
||||
*(spacedRes + j++) = 0x20;
|
||||
}
|
||||
}
|
||||
*(spacedRes + j) = '\0';
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
#ifndef ELMulator_OBDWiFiComm_h
|
||||
#define ELMulator_OBDWiFiComm_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiServer.h>
|
||||
#include "definitions.h"
|
||||
|
||||
|
||||
class OBDWiFiComm
|
||||
{
|
||||
public:
|
||||
enum STATUS
|
||||
{
|
||||
IDLE = 0,
|
||||
READY = 1
|
||||
};
|
||||
|
||||
OBDWiFiComm();
|
||||
|
||||
~OBDWiFiComm();
|
||||
|
||||
void init(const String &deviceName);
|
||||
|
||||
void writeEndOK();
|
||||
|
||||
void writeEndERROR();
|
||||
|
||||
/**
|
||||
* Response for unsupported PID command or missing sensor, respond "NO DATA".
|
||||
*/
|
||||
void writeEndNoData();
|
||||
|
||||
/**
|
||||
* Response for invalid AT command, or invalid PID or other bad input, respond "?"
|
||||
*/
|
||||
void writeEndUnknown();
|
||||
|
||||
void setToDefaults();
|
||||
|
||||
void readData(String &rxData);
|
||||
|
||||
void writeTo(uint8_t cChar);
|
||||
|
||||
void writeTo(char const *string);
|
||||
|
||||
void setEcho(bool echo);
|
||||
|
||||
void writeEnd();
|
||||
|
||||
bool isEchoEnable();
|
||||
|
||||
void setLineFeeds(bool status);
|
||||
|
||||
void setMemory(bool status);
|
||||
|
||||
void setWhiteSpaces(bool status);
|
||||
|
||||
void setHeaders(bool status);
|
||||
|
||||
void setStatus(STATUS status);
|
||||
|
||||
void writeEndPidTo(char const *string);
|
||||
|
||||
private:
|
||||
STATUS status; // Operation status
|
||||
bool echoEnable; // echoEnable command after received
|
||||
bool lineFeedEnable;
|
||||
bool memoryEnabled;
|
||||
bool whiteSpacesEnabled;
|
||||
bool headersEnabled;
|
||||
|
||||
void addSpacesToResponse(const char *response, char string[]);
|
||||
|
||||
WiFiClient client;
|
||||
};
|
||||
|
||||
#endif
|
||||
-195
@@ -1,195 +0,0 @@
|
||||
#include "PidProcessor.h"
|
||||
|
||||
#if USE_WIFI
|
||||
PidProcessor::PidProcessor(OBDWiFiComm *connection) {
|
||||
_connection = connection;
|
||||
resetPidMode01Array();
|
||||
};
|
||||
#else
|
||||
PidProcessor::PidProcessor(OBDSerialComm *connection) {
|
||||
_connection = connection;
|
||||
resetPidMode01Array();
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
bool PidProcessor::process(String& command) {
|
||||
bool processed = false;
|
||||
|
||||
if (command.length() > 4)
|
||||
{
|
||||
command = command.substring(0, 4); // remove num_responses value if present; lib only handles single responses
|
||||
}
|
||||
|
||||
if (!isMode01(command) & !isMode22(command))
|
||||
{
|
||||
_connection->writeEndNoData();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t hexCommand = strtoul(command.c_str(), NULL, HEX);
|
||||
uint8_t pid = getPidCodeFromHex(hexCommand);
|
||||
if (isSupportedPidRequest(pid)) { //reqeust to return a list of valid PIDs we can respond to
|
||||
processed = true;
|
||||
uint32_t supportedPids = getSupportedPids(pid);
|
||||
writePidResponse(command, 4, supportedPids);
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
|
||||
void PidProcessor::writePidResponse(const String& requestPid, uint8_t numberOfBytes, uint32_t value) {
|
||||
uint8_t nHexChars = PID_N_BYTES * N_CHARS_IN_BYTE + numberOfBytes * N_CHARS_IN_BYTE ;
|
||||
char responseArray[nHexChars + 1]; // one more for termination char
|
||||
getFormattedResponse(responseArray, nHexChars, requestPid, value);
|
||||
_connection->writeEndPidTo(responseArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a supported pid, so it can answer to pid support request, ex 0100, 0120, ...
|
||||
*/
|
||||
bool PidProcessor::registerMode01Pid(uint32_t pid) {
|
||||
// pid must be off type 01
|
||||
if (pid > 0x00 && pid < 0x0200) {
|
||||
// remove PidMode, only use pid code
|
||||
pid = getPidCodeFromHex(pid);
|
||||
setPidBit(pid);
|
||||
|
||||
char buffer[4];
|
||||
sprintf(buffer, "%02X", pid);
|
||||
DEBUG("Registered PID: " + String(buffer ));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PidProcessor::isMode01(const String& command)
|
||||
{
|
||||
return command.startsWith("01") ? true : false;
|
||||
}
|
||||
|
||||
bool PidProcessor::isMode01MIL(const String& command)
|
||||
{
|
||||
return command.startsWith("0101") ? true : false;
|
||||
}
|
||||
|
||||
bool PidProcessor::isMode03(const String& command)
|
||||
{
|
||||
return command.startsWith("03") ? true : false;
|
||||
}
|
||||
|
||||
bool PidProcessor::isMode22(const String& command)
|
||||
{
|
||||
return command.startsWith("22") ? true : false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* return true if:
|
||||
* - 1000
|
||||
* - 1020
|
||||
* - 1040
|
||||
* - 1060
|
||||
* ....
|
||||
*/
|
||||
bool PidProcessor::isSupportedPidRequest(uint8_t pid) {
|
||||
return pid == getPidIntervalId(pid);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the interval RANGEID for the given pid (ex: 00, 20, 40, ...)
|
||||
*/
|
||||
uint8_t PidProcessor::getPidIntervalId(uint8_t pidcode) {
|
||||
return getPidIntervalIndex(pidcode) * PID_INTERVAL_OFFSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the interval INDEX for the given pid (0,1,2,3,4,...)
|
||||
*/
|
||||
uint8_t PidProcessor::getPidIntervalIndex(uint8_t pidcode) {
|
||||
return pidcode / PID_INTERVAL_OFFSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a mode pid ex 010C ,
|
||||
* returns only the pid ex 0C
|
||||
*/
|
||||
uint8_t PidProcessor::getPidCodeFromHex(uint16_t hexCommand) {
|
||||
|
||||
if (hexCommand >= 0x0100) {
|
||||
hexCommand -= 0x100;
|
||||
}
|
||||
return (uint8_t) hexCommand; // return only the lower 8 bits (trim off num_messages or other extra bytes if present)
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a complete PID request,
|
||||
* returns only the pid ex 0C
|
||||
*/
|
||||
uint8_t PidProcessor::getPidCodeFromRequest(const String& request) {
|
||||
uint16_t hexCommand = strtoul(request.c_str(), NULL, HEX);
|
||||
return getPidCodeFromHex(hexCommand);
|
||||
}
|
||||
|
||||
|
||||
void PidProcessor::setPidBit(uint8_t pid) {
|
||||
uint8_t arrayIndex = getPidIntervalIndex(pid);
|
||||
if(isSupportedPidRequest(pid)) {
|
||||
arrayIndex -= 1;
|
||||
}
|
||||
|
||||
uint8_t bitPosition = getPidBitPosition(pid);
|
||||
bitWrite(pidMode01Supported[arrayIndex], bitPosition, 1);
|
||||
|
||||
// mark available intervals
|
||||
for (int i = 0; i < arrayIndex; i++) {
|
||||
bitWrite(pidMode01Supported[i], 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t PidProcessor::getPidBitPosition(uint8_t pid) {
|
||||
uint8_t intervalId = getPidIntervalId(pid) ;
|
||||
if(isSupportedPidRequest(pid)) {
|
||||
intervalId -= PID_INTERVAL_OFFSET;
|
||||
}
|
||||
|
||||
uint16_t pos = pid - intervalId;
|
||||
pos = PID_INTERVAL_OFFSET - pos;
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
uint32_t PidProcessor:: getSupportedPids(uint8_t pid) {
|
||||
uint8_t index = getPidIntervalIndex(pid);
|
||||
return pidMode01Supported[index];
|
||||
}
|
||||
|
||||
void PidProcessor::getFormattedResponse(char *response, uint8_t totalNumberOfChars, const String& pid, uint32_t value) {
|
||||
uint8_t nValueChars = totalNumberOfChars - PID_N_BYTES * N_CHARS_IN_BYTE;
|
||||
char cValue[3] = {0};
|
||||
itoa(nValueChars, cValue, DEC);
|
||||
String mask = "%s%0";
|
||||
mask.concat(cValue);
|
||||
mask.concat("lX\0");
|
||||
String pidResponse = convertToPidResponse(pid);
|
||||
sprintf(response, mask.c_str(), pidResponse.c_str(), value);
|
||||
|
||||
DEBUG("RX PID: " + pid + " - TX: " + String(response));
|
||||
}
|
||||
|
||||
String PidProcessor::convertToPidResponse(String pid) {
|
||||
String x = "4";
|
||||
x.concat(pid.substring(1, pid.length()));
|
||||
return x;
|
||||
}
|
||||
|
||||
void PidProcessor::resetPidMode01Array() {
|
||||
for(uint8_t i = 0; i < N_MODE01_INTERVALS; i++ ) {
|
||||
pidMode01Supported[i] = 0x0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
#ifndef ELMulator_PIDPROCESSOR_H
|
||||
#define ELMulator_PIDPROCESSOR_H
|
||||
|
||||
#include "definitions.h"
|
||||
#include <Arduino.h>
|
||||
#include <WString.h>
|
||||
#include <Print.h>
|
||||
#include "OBDWiFiComm.h"
|
||||
#include "OBDSerialComm.h"
|
||||
|
||||
class PidProcessor
|
||||
{
|
||||
|
||||
public:
|
||||
PidProcessor(OBDWiFiComm *connection);
|
||||
PidProcessor(OBDSerialComm *connection);
|
||||
bool process(String &string);
|
||||
|
||||
bool registerMode01Pid(uint32_t pid);
|
||||
|
||||
bool registerMode01MILResponse(const String &response);
|
||||
|
||||
bool registerMode03Response(const String &response);
|
||||
|
||||
void writePidResponse(const String &requestPid, uint8_t numberOfBytes, uint32_t value);
|
||||
|
||||
uint8_t getPidCodeFromHex(uint16_t hexCommand);
|
||||
uint8_t getPidCodeFromRequest(const String &command);
|
||||
|
||||
bool isMode01(const String &command);
|
||||
bool isMode01MIL(const String &command);
|
||||
bool isMode03(const String &command);
|
||||
bool isMode22(const String &command);
|
||||
|
||||
private:
|
||||
#if USE_WIFI
|
||||
OBDWiFiComm *_connection;
|
||||
#else
|
||||
OBDSerialComm *_connection;
|
||||
#endif
|
||||
uint32_t pidMode01Supported[N_MODE01_INTERVALS];
|
||||
|
||||
bool isSupportedPidRequest(uint8_t pid);
|
||||
|
||||
uint32_t getSupportedPids(uint8_t pidcode);
|
||||
|
||||
uint8_t getPidIntervalId(uint8_t pidcode);
|
||||
|
||||
uint8_t getPidIntervalIndex(uint8_t pidcode);
|
||||
|
||||
void setPidBit(uint8_t pid);
|
||||
|
||||
uint8_t getPidBitPosition(uint8_t pidcode);
|
||||
|
||||
String convertToPidResponse(String pid);
|
||||
|
||||
void getFormattedResponse(char *response, uint8_t totalNumberOfChars, const String &pid, uint32_t value);
|
||||
|
||||
void resetPidMode01Array();
|
||||
};
|
||||
|
||||
#endif // ELMulator_PIDPROCESSOR_H
|
||||
-294
@@ -1,294 +0,0 @@
|
||||
#ifndef ELMulator_DEFINITIONS_H
|
||||
#define ELMulator_DEFINITIONS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// true == We are running on hardware that has builtin Bluetooth
|
||||
// false == We are using hardware that has BT module attached via GPIO pins
|
||||
#define BLUETOOTH_BUILTIN true
|
||||
|
||||
#define USE_WIFI false
|
||||
|
||||
#define DO_DEBUG true
|
||||
#define DEBUG(x) do {if (DO_DEBUG) { Serial.println(x); } } while (0)
|
||||
|
||||
#define xtoc(x) ((x < 10) ? ('0' + x) : ('A' - 10 + x))
|
||||
#define getNumOfHexChars(nBytes) (nBytes * 2)
|
||||
|
||||
#define PID_N_BYTES 2
|
||||
#define N_CHARS_IN_BYTE 2
|
||||
|
||||
// Device ID
|
||||
#define ID "ELM327 / ELMulator V1.3.0"
|
||||
#define DESC "ELMulator OBD2 Arduino library, based on ELM327 protocol"
|
||||
#define PROTOCOL "6" // canbus 500k 11 bit protocol id for elm.
|
||||
|
||||
// Char representing end of serial string
|
||||
#define SERIAL_END_CHAR 0x0D
|
||||
#define SERIAL_READ_TIMEOUT 20000L
|
||||
|
||||
#define WIFI_END_CHAR 0x0A
|
||||
|
||||
const uint8_t maxPid = 0xFF;
|
||||
const uint8_t N_MODE01_INTERVALS = 7;
|
||||
const uint8_t PID_INTERVAL_OFFSET = 0x20;
|
||||
const uint8_t MAX_REQUEST_SIZE = 40;
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------//
|
||||
// Protocol IDs
|
||||
//-------------------------------------------------------------------------------------//
|
||||
const char AUTOMATIC = '0';
|
||||
const char SAE_J1850_PWM_41_KBAUD = '1';
|
||||
const char SAE_J1850_PWM_10_KBAUD = '2';
|
||||
const char ISO_9141_5_BAUD_INIT = '3';
|
||||
const char ISO_14230_5_BAUD_INIT = '4';
|
||||
const char ISO_14230_FAST_INIT = '5';
|
||||
const char ISO_15765_11_BIT_500_KBAUD = '6';
|
||||
const char ISO_15765_29_BIT_500_KBAUD = '7';
|
||||
const char ISO_15765_11_BIT_250_KBAUD = '8';
|
||||
const char ISO_15765_29_BIT_250_KBAUD = '9';
|
||||
const char SAE_J1939_29_BIT_250_KBAUD = 'A';
|
||||
const char USER_1_CAN = 'B';
|
||||
const char USER_2_CAN = 'C';
|
||||
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------//
|
||||
// PIDs (https://en.wikipedia.org/wiki/OBD-II_PIDs)
|
||||
//-------------------------------------------------------------------------------------//
|
||||
const uint8_t SUPPORTED_PIDS_1_20 = 0; // 0x00 - bit encoded
|
||||
const uint8_t MONITOR_STATUS_SINCE_DTC_CLEARED = 1; // 0x01 - bit encoded
|
||||
const uint8_t FREEZE_DTC = 2; // 0x02 -
|
||||
const uint8_t FUEL_SYSTEM_STATUS = 3; // 0x03 - bit encoded
|
||||
const uint8_t ENGINE_LOAD = 4; // 0x04 - %
|
||||
const uint8_t ENGINE_COOLANT_TEMP = 5; // 0x05 - °C
|
||||
const uint8_t SHORT_TERM_FUEL_TRIM_BANK_1 = 6; // 0x06 - %
|
||||
const uint8_t LONG_TERM_FUEL_TRIM_BANK_1 = 7; // 0x07 - %
|
||||
const uint8_t SHORT_TERM_FUEL_TRIM_BANK_2 = 8; // 0x08 - %
|
||||
const uint8_t LONG_TERM_FUEL_TRIM_BANK_2 = 9; // 0x09 - %
|
||||
const uint8_t FUEL_PRESSURE = 10; // 0x0A - kPa
|
||||
const uint8_t INTAKE_MANIFOLD_ABS_PRESSURE = 11; // 0x0B - kPa
|
||||
const uint8_t ENGINE_RPM = 12; // 0x0C - rpm
|
||||
const uint8_t VEHICLE_SPEED = 13; // 0x0D - km/h
|
||||
const uint8_t TIMING_ADVANCE = 14; // 0x0E - ° before TDC
|
||||
const uint8_t INTAKE_AIR_TEMP = 15; // 0x0F - °C
|
||||
const uint8_t MAF_FLOW_RATE = 16; // 0x10 - g/s
|
||||
const uint8_t THROTTLE_POSITION = 17; // 0x11 - %
|
||||
const uint8_t COMMANDED_SECONDARY_AIR_STATUS = 18; // 0x12 - bit encoded
|
||||
const uint8_t OXYGEN_SENSORS_PRESENT_2_BANKS = 19; // 0x13 - bit encoded
|
||||
const uint8_t OXYGEN_SENSOR_1_A = 20; // 0x14 - V %
|
||||
const uint8_t OXYGEN_SENSOR_2_A = 21; // 0x15 - V %
|
||||
const uint8_t OXYGEN_SENSOR_3_A = 22; // 0x16 - V %
|
||||
const uint8_t OXYGEN_SENSOR_4_A = 23; // 0x17 - V %
|
||||
const uint8_t OXYGEN_SENSOR_5_A = 24; // 0x18 - V %
|
||||
const uint8_t OXYGEN_SENSOR_6_A = 25; // 0x19 - V %
|
||||
const uint8_t OXYGEN_SENSOR_7_A = 26; // 0x1A - V %
|
||||
const uint8_t OXYGEN_SENSOR_8_A = 27; // 0x1B - V %
|
||||
const uint8_t OBD_STANDARDS = 28; // 0x1C - bit encoded
|
||||
const uint8_t OXYGEN_SENSORS_PRESENT_4_BANKS = 29; // 0x1D - bit encoded
|
||||
const uint8_t AUX_INPUT_STATUS = 30; // 0x1E - bit encoded
|
||||
const uint8_t RUN_TIME_SINCE_ENGINE_START = 31; // 0x1F - sec
|
||||
|
||||
const uint8_t SUPPORTED_PIDS_21_40 = 32; // 0x20 - bit encoded
|
||||
const uint8_t DISTANCE_TRAVELED_WITH_MIL_ON = 33; // 0x21 - km
|
||||
const uint8_t FUEL_RAIL_PRESSURE = 34; // 0x22 - kPa
|
||||
const uint8_t FUEL_RAIL_GUAGE_PRESSURE = 35; // 0x23 - kPa
|
||||
const uint8_t OXYGEN_SENSOR_1_B = 36; // 0x24 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_2_B = 37; // 0x25 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_3_B = 38; // 0x26 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_4_B = 39; // 0x27 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_5_B = 40; // 0x28 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_6_B = 41; // 0x29 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_7_B = 42; // 0x2A - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_8_B = 43; // 0x2B - ratio V
|
||||
const uint8_t COMMANDED_EGR = 44; // 0x2C - %
|
||||
const uint8_t EGR_ERROR = 45; // 0x2D - %
|
||||
const uint8_t COMMANDED_EVAPORATIVE_PURGE = 46; // 0x2E - %
|
||||
const uint8_t FUEL_TANK_LEVEL_INPUT = 47; // 0x2F - %
|
||||
const uint8_t WARM_UPS_SINCE_CODES_CLEARED = 48; // 0x30 - count
|
||||
const uint8_t DIST_TRAV_SINCE_CODES_CLEARED = 49; // 0x31 - km
|
||||
const uint8_t EVAP_SYSTEM_VAPOR_PRESSURE = 50; // 0x32 - Pa
|
||||
const uint8_t ABS_BAROMETRIC_PRESSURE = 51; // 0x33 - kPa
|
||||
const uint8_t OXYGEN_SENSOR_1_C = 52; // 0x34 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_2_C = 53; // 0x35 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_3_C = 54; // 0x36 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_4_C = 55; // 0x37 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_5_C = 56; // 0x38 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_6_C = 57; // 0x39 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_7_C = 58; // 0x3A - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_8_C = 59; // 0x3B - ratio mA
|
||||
const uint8_t CATALYST_TEMP_BANK_1_SENSOR_1 = 60; // 0x3C - °C
|
||||
const uint8_t CATALYST_TEMP_BANK_2_SENSOR_1 = 61; // 0x3D - °C
|
||||
const uint8_t CATALYST_TEMP_BANK_1_SENSOR_2 = 62; // 0x3E - °C
|
||||
const uint8_t CATALYST_TEMP_BANK_2_SENSOR_2 = 63; // 0x3F - °C
|
||||
|
||||
const uint8_t SUPPORTED_PIDS_41_60 = 64; // 0x40 - bit encoded
|
||||
const uint8_t MONITOR_STATUS_THIS_DRIVE_CYCLE = 65; // 0x41 - bit encoded
|
||||
const uint8_t CONTROL_MODULE_VOLTAGE = 66; // 0x42 - V
|
||||
const uint8_t ABS_LOAD_VALUE = 67; // 0x43 - %
|
||||
const uint8_t FUEL_AIR_COMMANDED_EQUIV_RATIO = 68; // 0x44 - ratio
|
||||
const uint8_t RELATIVE_THROTTLE_POSITION = 69; // 0x45 - %
|
||||
const uint8_t AMBIENT_AIR_TEMP = 70; // 0x46 - °C
|
||||
const uint8_t ABS_THROTTLE_POSITION_B = 71; // 0x47 - %
|
||||
const uint8_t ABS_THROTTLE_POSITION_C = 72; // 0x48 - %
|
||||
const uint8_t ABS_THROTTLE_POSITION_D = 73; // 0x49 - %
|
||||
const uint8_t ABS_THROTTLE_POSITION_E = 74; // 0x4A - %
|
||||
const uint8_t ABS_THROTTLE_POSITION_F = 75; // 0x4B - %
|
||||
const uint8_t COMMANDED_THROTTLE_ACTUATOR = 76; // 0x4C - %
|
||||
const uint8_t TIME_RUN_WITH_MIL_ON = 77; // 0x4D - min
|
||||
const uint8_t TIME_SINCE_CODES_CLEARED = 78; // 0x4E - min
|
||||
const uint8_t MAX_VALUES_EQUIV_V_I_PRESSURE = 79; // 0x4F - ratio V mA kPa
|
||||
const uint8_t MAX_MAF_RATE = 80; // 0x50 - g/s
|
||||
const uint8_t FUEL_TYPE = 81; // 0x51 - ref table
|
||||
const uint8_t ETHANOL_FUEL_PERCENT = 82; // 0x52 - %
|
||||
const uint8_t ABS_EVAP_SYS_VAPOR_PRESSURE = 83; // 0x53 - kPa
|
||||
const uint8_t EVAP_SYS_VAPOR_PRESSURE = 84; // 0x54 - Pa
|
||||
const uint8_t SHORT_TERM_SEC_OXY_SENS_TRIM_1_3 = 85; // 0x55 - %
|
||||
const uint8_t LONG_TERM_SEC_OXY_SENS_TRIM_1_3 = 86; // 0x56 - %
|
||||
const uint8_t SHORT_TERM_SEC_OXY_SENS_TRIM_2_4 = 87; // 0x57 - %
|
||||
const uint8_t LONG_TERM_SEC_OXY_SENS_TRIM_2_4 = 88; // 0x58 - %
|
||||
const uint8_t FUEL_RAIL_ABS_PRESSURE = 89; // 0x59 - kPa
|
||||
const uint8_t RELATIVE_ACCELERATOR_PEDAL_POS = 90; // 0x5A - %
|
||||
const uint8_t HYBRID_BATTERY_REMAINING_LIFE = 91; // 0x5B - %
|
||||
const uint8_t ENGINE_OIL_TEMP = 92; // 0x5C - °C
|
||||
const uint8_t FUEL_INJECTION_TIMING = 93; // 0x5D - °
|
||||
const uint8_t ENGINE_FUEL_RATE = 94; // 0x5E - L/h
|
||||
const uint8_t EMISSION_REQUIREMENTS = 95; // 0x5F - bit encoded
|
||||
|
||||
const uint8_t SUPPORTED_PIDS_61_80 = 96; // 0x60 - bit encoded
|
||||
const uint8_t DEMANDED_ENGINE_PERCENT_TORQUE = 97; // 0x61 - %
|
||||
const uint8_t ACTUAL_ENGINE_TORQUE = 98; // 0x62 - %
|
||||
const uint8_t ENGINE_REFERENCE_TORQUE = 99; // 0x63 - Nm
|
||||
const uint8_t ENGINE_PERCENT_TORQUE_DATA = 100; // 0x64 - %
|
||||
const uint8_t AUX_INPUT_OUTPUT_SUPPORTED = 101; // 0x65 - bit encoded
|
||||
|
||||
const uint8_t ODOMETER = 166; // 0xA6 - km * 10
|
||||
|
||||
const uint8_t SERVICE_01 = 1;
|
||||
const uint8_t SERVICE_02 = 2;
|
||||
const uint8_t SERVICE_03 = 3;
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------//
|
||||
// AT commands (https://www.sparkfun.com/datasheets/Widgets/ELM327_AT_Commands.pdf)
|
||||
//-------------------------------------------------------------------------------------//
|
||||
const char * const DISP_DEVICE_DESCRIPT = "AT @1"; // General
|
||||
const char * const DISP_DEVICE_ID = "AT @2"; // General
|
||||
const char * const STORE_DEVICE_ID = "AT @3 %s"; // General
|
||||
const char * const REPEAT_LAST_COMMAND = "AT \r"; // General
|
||||
const char * const ALLOW_LONG_MESSAGES = "AT AL"; // General
|
||||
const char * const AUTOMATIC_RECEIVE = "AT AR"; // OBD
|
||||
const char * const ADAPTIVE_TIMING_OFF = "AT AT0"; // OBD
|
||||
const char * const ADAPTIVE_TIMING_AUTO_1 = "AT AT1"; // OBD
|
||||
const char * const ADAPTIVE_TIMING_AUTO_2 = "AT AT2"; // OBD
|
||||
const char * const DUMP_BUFFER = "AT BD"; // OBD
|
||||
const char * const BYPASS_INIT_SEQUENCE = "AT BI"; // OBD
|
||||
const char * const TRY_BAUD_DIVISOR = "AT BRD %02d"; // General
|
||||
const char * const SET_HANDSHAKE_TIMEOUT = "AT BRT %02d"; // General
|
||||
const char * const CAN_AUTO_FORMAT_OFF = "AT CAF0"; // CAN
|
||||
const char * const CAN_AUTO_FORMAT_ON = "AT CAF1"; // CAN
|
||||
const char * const CAN_EXTENDED_ADDRESS_OFF = "AT CEA"; // CAN
|
||||
const char * const USE_CAN_EXTENDED_ADDRESS = "AT CEA %02d"; // CAN
|
||||
const char * const SET_ID_FILTER = "AT CF %s"; // CAN
|
||||
const char * const CAN_FLOW_CONTROL_OFF = "AT CFC0"; // CAN
|
||||
const char * const CAN_FLOW_CONTROL_ON = "AT CFC1"; // CAN
|
||||
const char * const SET_ID_MASK = "AT CM %s"; // CAN
|
||||
const char * const SET_CAN_PRIORITY = "AT CP %02d"; // CAN
|
||||
const char * const SHOW_CAN_STATUS = "AT CS"; // CAN
|
||||
const char * const CAN_SILENT_MODE_OFF = "AT CSM0"; // CAN
|
||||
const char * const CAN_SILENT_MODE_ON = "AT CSM1"; // CAN
|
||||
const char * const CALIBRATE_VOLTAGE_CUSTOM = "AT CV %04d"; // Volts
|
||||
const char * const RESTORE_CV_TO_FACTORY = "AT CV 0000"; // Volts
|
||||
const char * const SET_ALL_TO_DEFAULTS = "AT D"; // General
|
||||
const char * const DISP_DLC_OFF = "AT D0"; // CAN
|
||||
const char * const DISP_DLC_ON = "AT D1"; // CAN
|
||||
const char * const MONITOR_FOR_DM1_MESSAGES = "AT DM1"; // J1939
|
||||
const char * const DISP_CURRENT_PROTOCOL = "AT DP"; // OBD
|
||||
const char * const DISP_CURRENT_PROTOCOL_NUM = "AT DPN"; // OBD
|
||||
const char * const ECHO_OFF = "AT E0"; // General
|
||||
const char * const ECHO_ON = "AT E1"; // General
|
||||
const char * const FLOW_CONTROL_SET_DATA_TO = "AT FC SD %s"; // CAN
|
||||
const char * const FLOW_CONTROL_SET_HEAD_TO = "AT FC SH %s"; // CAN
|
||||
const char * const FLOW_CONTROL_SET_MODE_TO = "AT FC SM %c"; // CAN
|
||||
const char * const FORGE_EVENTS = "AT FE"; // General
|
||||
const char * const PERFORM_FAST_INIT = "AT FI"; // ISO
|
||||
const char * const HEADERS_OFF = "AT H0"; // OBD
|
||||
const char * const HEADERS_ON = "AT H1"; // OBD
|
||||
const char * const DISP_ID = "AT I"; // General
|
||||
const char * const SET_ISO_BAUD_10400 = "AT IB 10"; // ISO
|
||||
const char * const SET_ISO_BAUD_4800 = "AT IB 48"; // ISO
|
||||
const char * const SET_ISO_BAUD_9600 = "AT IB 96"; // ISO
|
||||
const char * const IFR_VAL_FROM_HEADER = "AT IFR H"; // J1850
|
||||
const char * const IFR_VAL_FROM_SOURCE = "AT IFR S"; // J1850
|
||||
const char * const IFRS_OFF = "AT IFR0"; // J1850
|
||||
const char * const IFRS_AUTO = "AT IFR1"; // J1850
|
||||
const char * const IFRS_ON = "AT IFR2"; // J1850
|
||||
const char * const IREAD_IGNMON_INPUT_LEVEL = "AT IGN"; // Other
|
||||
const char * const SET_ISO_SLOW_INIT_ADDRESS = "AT IIA %02d"; // ISO
|
||||
const char * const USE_J1939_ELM_DATA_FORMAT = "AT JE"; // J1850
|
||||
const char * const J1939_HEAD_FORMAT_OFF = "AT JHF0"; // J1850
|
||||
const char * const J1939_HEAD_FORMAT_ON = "AT JHF1"; // J1850
|
||||
const char * const USE_J1939_SAE_DATA_FORMAT = "AT JS"; // J1850
|
||||
const char * const SET_J1939_TIMER_X_TO_1X = "AT JTM1"; // J1850
|
||||
const char * const SET_J1939_TIMER_X_TO_5X = "AT JTM5"; // J1850
|
||||
const char * const DISP_KEY_WORDS = "AT KW"; // ISO
|
||||
const char * const KEY_WORD_CHECKING_OFF = "AT KW0"; // ISO
|
||||
const char * const KEY_WORD_CHECKING_ON = "AT KW1"; // ISO
|
||||
const char * const LINEFEEDS_OFF = "AT L0"; // General
|
||||
const char * const LINEFEEDS_ON = "AT L1"; // General
|
||||
const char * const LOW_POWER_MODE = "AT LP"; // General
|
||||
const char * const MEMORY_OFF = "AT M0"; // General
|
||||
const char * const MEMORY_ON = "AT M1"; // General
|
||||
const char * const MONITOR_ALL = "AT MA"; // OBD
|
||||
const char * const MONITOR_FOR_PGN = "AT MP %s"; // J1939
|
||||
const char * const MONITOR_FOR_RECEIVER = "AT MR %02d"; // OBD
|
||||
const char * const MONITOR_FOR_TRANSMITTER = "AT MT %02d"; // OBD
|
||||
const char * const NORMAL_LENGTH_MESSAGES = "AT NL"; // OBD
|
||||
const char * const SET_PROTO_OPTIONS_AND_BAUD = "AT PB %s"; // OBD
|
||||
const char * const PROTOCOL_CLOSE = "AT PC"; // OBD
|
||||
const char * const ALL_PROG_PARAMS_OFF = "AT PP FF OFF";// PPs
|
||||
const char * const ALL_PROG_PARAMS_ON = "AT PP FF ON"; // PPs
|
||||
const char * const SET_PROG_PARAM_OFF = "AT PP %s OFF";// PPs
|
||||
const char * const SET_PROG_PARAM_ON = "AT PP %s ON"; // PPs
|
||||
const char * const SET_PROG_PARAM_VAL = "AT PP %s SV %s"; // PPs
|
||||
const char * const DISP_PP_SUMMARY = "AT PPS"; // PPs
|
||||
const char * const RESPONSES_OFF = "AT R0"; // OBD
|
||||
const char * const RESPONSES_ON = "AT R1"; // OBD
|
||||
const char * const SET_RECEIVE_ADDRESS_TO = "AT RA %02d"; // OBD
|
||||
const char * const READ_STORED_DATA = "AT RD"; // General
|
||||
const char * const SEND_RTR_MESSAGE = "AT RTR"; // CAN
|
||||
const char * const READ_VOLTAGE = "AT RV"; // Volts
|
||||
const char * const PRINTING_SPACES_OFF = "AT S0"; // OBD
|
||||
const char * const PRINTING_SPACES_ON = "AT S1"; // OBD
|
||||
const char * const STORE_DATA_BYTE = "AT SD "; // General
|
||||
const char * const SET_HEADER = "AT SH %s"; // OBD
|
||||
const char * const PERFORM_SLOW_INIT = "AT SI"; // ISO
|
||||
const char * const SET_PROTOCOL_TO_AUTO_H_SAVE = "AT SP A%c"; // OBD
|
||||
const char * const SET_PROTOCOL_TO_H_SAVE = "AT SP %c"; // OBD
|
||||
const char * const SET_PROTOCOL_TO_AUTO_SAVE = "AT SP 00"; // OBD
|
||||
const char * const SET_REC_ADDRESS = "AT SR %02d"; // OBD
|
||||
const char * const SET_STANDARD_SEARCH_ORDER = "AT SS"; // OBD
|
||||
const char * const SET_TIMEOUT_TO_H_X_4MS = "AT ST %02d"; // OBD
|
||||
const char * const SET_WAKEUP_TO_H_X_20MS = "AT SW %02d"; // ISO
|
||||
const char * const SET_TESTER_ADDRESS_TO = "AT TA %02d"; // OBD
|
||||
const char * const TRY_PROT_H_AUTO_SEARCH = "AT TP A%c"; // OBD
|
||||
const char * const TRY_PROT_H = "AT TP %c"; // OBD
|
||||
const char * const VARIABLE_DLC_OFF = "AT V0"; // CAN
|
||||
const char * const VARIABLE_DLC_ON = "AT V1"; // CAN
|
||||
const char * const SET_WAKEUP_MESSAGE = "AT WM"; // ISO
|
||||
const char * const WARM_START = "AT WS"; // General
|
||||
const char * const RESET_ALL = "AT Z"; // General
|
||||
|
||||
|
||||
const uint8_t responseBytes[0xA9] =
|
||||
{
|
||||
4, 4, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2,
|
||||
4, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2,
|
||||
4, 4, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 4, 4, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 1,
|
||||
4, 1, 1, 2, 5, 2, 5, 3, 3, 7, 5, 5, 5, 11, 9, 3, 10, 6, 5, 5, 5, 7, 7, 5, 9, 9, 7, 7, 9, 1, 1, 13,
|
||||
4, 41, 41, 9, 1, 10, 5, 5, 13, 41, 41, 7, 17, 1, 1, 7, 3, 5, 2, 3, 12, 9, 9, 6, 4, 17, 4, 2, 9
|
||||
};
|
||||
|
||||
#endif //ELMulator_DEFINITIONS_H
|
||||
-162
@@ -1,162 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
|
||||
bool ATCommands::process(const String& command) {
|
||||
bool processed = false;
|
||||
if (isATCommand(command)) {
|
||||
processed = true;
|
||||
processCommand(command);
|
||||
DEBUG("RX AT: " + command);
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
|
||||
void ATCommands::processCommand(const String& command) {
|
||||
|
||||
// if space is enabled (ex: AT H[charPosition])
|
||||
uint8_t offset = 2;
|
||||
if (command.charAt(2) == 0x20) {
|
||||
offset = 3;
|
||||
}
|
||||
|
||||
// refer to ELM327 specs
|
||||
String specificCommand = command.substring(offset, command.length());
|
||||
if (specificCommand.startsWith("D",offset)) {
|
||||
ATCommands::ATD();
|
||||
} else if (specificCommand.startsWith("Z")) {
|
||||
ATCommands::ATZ();
|
||||
} else if (specificCommand.startsWith("I")) {
|
||||
ATCommands::ATI();
|
||||
} else if (specificCommand.startsWith("E")) {
|
||||
ATCommands::ATEx(specificCommand);
|
||||
} else if (specificCommand.startsWith("L")) {
|
||||
ATCommands::ATLx(specificCommand);
|
||||
} else if (specificCommand.startsWith("M")) {
|
||||
ATCommands::ATMx(specificCommand);
|
||||
} else if (specificCommand.startsWith("S")) {
|
||||
if (specificCommand.startsWith("SP")) {
|
||||
ATCommands::ATSPx(specificCommand);
|
||||
} else {
|
||||
ATCommands::ATSx(specificCommand);
|
||||
}
|
||||
} else if (specificCommand.startsWith("H")) {
|
||||
ATCommands::ATHx(specificCommand);
|
||||
} else if (specificCommand.startsWith("AT")) {
|
||||
ATCommands::ATATx(specificCommand);
|
||||
} else if (specificCommand.startsWith("DPN")) {
|
||||
ATCommands::ATDPN();
|
||||
} else if (specificCommand.startsWith("DESC") || specificCommand.startsWith("@1")) {
|
||||
ATCommands::ATDESC();
|
||||
} else if (specificCommand.startsWith("PC")) {
|
||||
ATCommands::ATPC();
|
||||
} else if (specificCommand.startsWith("RV")){
|
||||
ATCommands::ATRV();
|
||||
} else {
|
||||
|
||||
// lets assume we process any at command
|
||||
connection->writeEndOK();
|
||||
}
|
||||
}
|
||||
|
||||
// set all to defaults
|
||||
void ATCommands::ATD() {
|
||||
connection->setToDefaults();
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// reset all
|
||||
void ATCommands::ATZ() {
|
||||
connection->setToDefaults();
|
||||
connection->writeTo(ID);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// Print the version ID
|
||||
void ATCommands::ATI() {
|
||||
connection->writeTo(ID);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// send description
|
||||
void ATCommands::ATDESC() {
|
||||
connection->writeTo(DESC);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// set echoEnable 0=off 1=on
|
||||
void ATCommands::ATEx(String& cmd) {
|
||||
connection->setEcho(cmd.equals("E0") ? false : true);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// set memory off=0 on=1
|
||||
void ATCommands::ATMx(String& cmd) {
|
||||
connection->setMemory(cmd.equals("M0") ? false : true);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// line feeds off=0 on=1
|
||||
void ATCommands::ATLx(String& cmd) {
|
||||
connection->setLineFeeds(cmd.equals("L0") ? false : true);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// ATSx printing spaces off=0 on=1
|
||||
void ATCommands::ATSx(String& cmd) {
|
||||
connection->setWhiteSpaces(cmd.equals("S0") ? false : true);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// Headers off=0 on=1
|
||||
void ATCommands::ATHx(String& cmd) {
|
||||
connection->setHeaders(cmd.equals("H0") ? false : true);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// ATSPx Define protocol 0=auto
|
||||
void ATCommands::ATSPx(String& cmd) {
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// set protocol
|
||||
void ATCommands::ATDPN() {
|
||||
connection->writeTo(PROTOCOL);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// AT AT2 adaptative time control
|
||||
void ATCommands::ATATx(String& cmd) {
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// Terminates current diagnostic session. Protocol close
|
||||
void ATCommands::ATPC() {
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
void ATCommands::ATRV() {
|
||||
connection->writeTo("AT RV\r13.5V");
|
||||
connection->writeEnd();
|
||||
}
|
||||
|
||||
// return true ir connectionand is AT
|
||||
bool ATCommands::isATCommand(const String& command) {
|
||||
toUpperCase(command.charAt(0));
|
||||
return command.startsWith("AT") ? true : false;
|
||||
}
|
||||
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
#ifndef ELMulator_ATCommands_h
|
||||
#define ELMulator_ATCommands_h
|
||||
|
||||
#include <Arduino.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();
|
||||
|
||||
void ATI();
|
||||
|
||||
void ATEx(String &x);
|
||||
|
||||
void ATMx(String &x);
|
||||
|
||||
void ATLx(String &x);
|
||||
|
||||
void ATSx(String &x);
|
||||
|
||||
void ATHx(String &x);
|
||||
|
||||
void ATSPx(String &x);
|
||||
|
||||
void ATATx(String &cmd);
|
||||
|
||||
void ATPC();
|
||||
|
||||
void ATDPN();
|
||||
|
||||
void ATDESC();
|
||||
|
||||
void ATRV();
|
||||
|
||||
void processCommand(const String &command);
|
||||
|
||||
bool isATCommand(const String &command);
|
||||
};
|
||||
|
||||
#endif
|
||||
-218
@@ -1,218 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
-138
@@ -1,138 +0,0 @@
|
||||
#ifndef ELMulator_ELMulator_h
|
||||
#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"
|
||||
|
||||
class ELMulator
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* When creating a new instance of ELMulator with an attached
|
||||
* BT module, we need to set the Bluetooth serial parameters
|
||||
*
|
||||
* @param baudRate - rate for bluetooth serial port (normally 9600)
|
||||
* @param rxPin - the Arduino RX pin for the Bluetooth module (serial port)
|
||||
* @param txPin - the Arduino TX pin for the Bluetooth module (serial port)
|
||||
* @return
|
||||
*/
|
||||
ELMulator(uint32_t baudRate, uint8_t rxPin, uint8_t txPin);
|
||||
|
||||
ELMulator();
|
||||
|
||||
~ELMulator();
|
||||
|
||||
void init(const String &deviceName, const bool registerPids = false);
|
||||
|
||||
/**
|
||||
* Waits and Reads a request from OBD client software (example: torque)
|
||||
*
|
||||
* It will only return if the OBD client software
|
||||
* request's one of the previous registred PID (see: registerMode01Pid())
|
||||
*
|
||||
* Any other request will be automaticly handle or it will be rejected.
|
||||
*
|
||||
* ELMulator lib will automaticly handle:
|
||||
* - any AT command
|
||||
* - PID's support request like 0100, 0120, 0140, 0160, ...
|
||||
*
|
||||
* @return the request PID (Vehicle speed ex: 010D )
|
||||
*/
|
||||
bool readELMRequest();
|
||||
void sendELMResponse();
|
||||
void begin();
|
||||
|
||||
/**
|
||||
* Registry the PID's (sensors) your arduino will support.
|
||||
* Currently ELMulator lib only supports MODE 01 PID's
|
||||
*
|
||||
* In OBDII MODE 01 PID's, are request
|
||||
* to Show current data for a given sensor identified by a PID
|
||||
*
|
||||
* List of MODE 01 PID's: https://en.wikipedia.org/wiki/OBD-II_PIDs#Mode_01
|
||||
*
|
||||
* Example, for Vehicle speed, where OBDII PID code is 0D:
|
||||
*
|
||||
* registerMode01Pid(0x0D)
|
||||
*
|
||||
* @param pidHexId
|
||||
* @return returns true if the entered ID is in the valide range [0x00, 0xFF]
|
||||
*/
|
||||
bool registerMode01Pid(uint32_t pidHexId);
|
||||
|
||||
bool registerMode01MILResponse(const String &response);
|
||||
|
||||
bool registerMode03Response(const String &response);
|
||||
|
||||
uint8_t getPidCodeOnly(uint16_t hexCommand);
|
||||
|
||||
/**
|
||||
* Used to informe the OBD client that the PID,
|
||||
* it has requested is not supported
|
||||
*
|
||||
* In normal conditions, you will only receive,
|
||||
* request for the PID you have registred
|
||||
* All other request is automaticly handle by GTTuboEcu lib
|
||||
*/
|
||||
void writePidNotSupported();
|
||||
|
||||
/**
|
||||
* Respond to a given PID request from OBDII client (ex: torque)
|
||||
*
|
||||
* @param requestPid - the received pid
|
||||
* @param numberOfBytes - number of bytes this PID response VALUE is supposed to have
|
||||
* @param value - sensor value for this PID,
|
||||
* lib will convert it to HEX
|
||||
* with the appropriated number of HEX chars (numberOfChars)
|
||||
*/
|
||||
void writePidResponse(const String &requestPid, uint8_t numberOfBytes, uint32_t value);
|
||||
|
||||
// Write the response back to the requestor without PID formatting, etc
|
||||
// Just pass the response string on. Useful for testing with a specific response
|
||||
// that has been pre-configured.
|
||||
void writeResponse(const String &response);
|
||||
|
||||
bool isMode01(const String &command);
|
||||
bool isMode03(const String &command);
|
||||
bool isMode01MIL(const String &command);
|
||||
bool isMode22(const String &command);
|
||||
|
||||
uint8_t getPidCode(const String &request);
|
||||
void registerAllMode01Pids();
|
||||
uint32_t getMockSensorValue();
|
||||
|
||||
String elmRequest;
|
||||
|
||||
private:
|
||||
#if USE_WIFI
|
||||
OBDWiFiComm *_connection;
|
||||
#else
|
||||
OBDSerialComm *_connection;
|
||||
#endif
|
||||
ATCommands *_atProcessor;
|
||||
|
||||
PidProcessor *_pidProcessor;
|
||||
|
||||
String _lastCommand;
|
||||
|
||||
bool isCycleUp = true;
|
||||
|
||||
uint32_t cycle = 0;
|
||||
|
||||
bool processRequest(String &command);
|
||||
|
||||
bool isValidHex(const char *pid);
|
||||
};
|
||||
|
||||
#endif
|
||||
-145
@@ -1,145 +0,0 @@
|
||||
#include "OBDSerialComm.h"
|
||||
#include "definitions.h"
|
||||
|
||||
|
||||
OBDSerialComm::OBDSerialComm() {
|
||||
|
||||
}
|
||||
|
||||
OBDSerialComm::~OBDSerialComm() {
|
||||
operator delete(serial);
|
||||
}
|
||||
|
||||
void OBDSerialComm::init(const String& deviceName) {
|
||||
Serial.println("Starting BT . . .");
|
||||
serial = new BluetoothSerial();
|
||||
//delay(2000);
|
||||
serial->begin(deviceName, false);
|
||||
setToDefaults();
|
||||
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeEnd() {
|
||||
|
||||
// 1 - write carriage return
|
||||
writeTo("\r");
|
||||
|
||||
// 2- (optional ) write linefeed
|
||||
if (lineFeedEnable) {
|
||||
writeTo("\n");
|
||||
}
|
||||
|
||||
// 3 - Write prompt
|
||||
writeTo(">");
|
||||
|
||||
serial->flush();
|
||||
};
|
||||
|
||||
|
||||
void OBDSerialComm::writeEndOK() {
|
||||
writeTo("OK");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeEndERROR() {
|
||||
writeTo("ERROR");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeEndNoData() {
|
||||
writeTo("NO DATA");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeEndUnknown() {
|
||||
writeTo("?");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDSerialComm::setToDefaults() {
|
||||
setEcho(true);
|
||||
setStatus(READY);
|
||||
setWhiteSpaces(true);
|
||||
setHeaders(false);
|
||||
setLineFeeds(true);
|
||||
setMemory(false);
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeTo(char const *response) {
|
||||
serial->print(response);
|
||||
}
|
||||
|
||||
|
||||
void OBDSerialComm::writeTo(uint8_t cChar) {
|
||||
serial->print(cChar);
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeEndPidTo(char const *response) {
|
||||
if (whiteSpacesEnabled) {
|
||||
uint8_t len = strlen(response);
|
||||
char spacedResponse[len + len / 2 + 1];
|
||||
addSpacesToResponse(response, spacedResponse);
|
||||
writeTo(spacedResponse);
|
||||
} else {
|
||||
writeTo(response);
|
||||
}
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDSerialComm::readData(String& rxData) {
|
||||
serial->flush(); // temp remove this
|
||||
rxData = serial->readStringUntil(SERIAL_END_CHAR);
|
||||
if (isEchoEnable()) {
|
||||
writeTo(rxData.c_str());
|
||||
}
|
||||
//return rxData;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setBaudRate(uint32_t rate) {
|
||||
this->baudRate = rate;
|
||||
}
|
||||
|
||||
long OBDSerialComm::getBaudRate() {
|
||||
return this->baudRate;
|
||||
}
|
||||
|
||||
bool OBDSerialComm::isEchoEnable() {
|
||||
return this->echoEnable;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setEcho(bool echo) {
|
||||
this->echoEnable = echo;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setStatus(STATUS status) {
|
||||
this->status = status;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setLineFeeds(bool status) {
|
||||
this->lineFeedEnable = status;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setMemory(bool status) {
|
||||
this->memoryEnabled = status;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setWhiteSpaces(bool status) {
|
||||
this->whiteSpacesEnabled = status;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setHeaders(bool status) {
|
||||
this->headersEnabled = status;
|
||||
}
|
||||
|
||||
void OBDSerialComm::addSpacesToResponse(const char *response, char spacedRes[]) {
|
||||
uint8_t len = strlen(response);
|
||||
int j = 0;
|
||||
for (int i = 0; i < len;) {
|
||||
*(spacedRes + j++) = *(response + i++);
|
||||
*(spacedRes + j++) = *(response + i++);
|
||||
if (i < len) {
|
||||
*(spacedRes + j++) = 0x20;
|
||||
}
|
||||
}
|
||||
*(spacedRes + j) = '\0';
|
||||
}
|
||||
-90
@@ -1,90 +0,0 @@
|
||||
#ifndef ELMulator_OBDSerialComm_h
|
||||
#define ELMulator_OBDSerialComm_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "definitions.h"
|
||||
|
||||
#include <BluetoothSerial.h>
|
||||
|
||||
|
||||
class OBDSerialComm
|
||||
{
|
||||
public:
|
||||
enum STATUS
|
||||
{
|
||||
IDLE = 0,
|
||||
READY = 1
|
||||
};
|
||||
|
||||
// // Bluetooth is not built in, we are using a BT module via GPIO
|
||||
// OBDSerialComm(uint32_t baudRate, uint8_t rxPin, uint8_t txPin);
|
||||
|
||||
OBDSerialComm();
|
||||
|
||||
~OBDSerialComm();
|
||||
|
||||
void init(const String &deviceName);
|
||||
|
||||
void writeEndOK();
|
||||
|
||||
void writeEndERROR();
|
||||
|
||||
/**
|
||||
* Response for unsupported PID command or missing sensor, respond "NO DATA".
|
||||
*/
|
||||
void writeEndNoData();
|
||||
|
||||
/**
|
||||
* Response for invalid AT command, or invalid PID or other bad input, respond "?"
|
||||
*/
|
||||
void writeEndUnknown();
|
||||
|
||||
void setToDefaults();
|
||||
|
||||
void readData(String &rxData);
|
||||
|
||||
void writeTo(uint8_t cChar);
|
||||
|
||||
void writeTo(char const *string);
|
||||
|
||||
void setEcho(bool echo);
|
||||
|
||||
void writeEnd();
|
||||
|
||||
bool isEchoEnable();
|
||||
|
||||
void setLineFeeds(bool status);
|
||||
|
||||
void setMemory(bool status);
|
||||
|
||||
void setWhiteSpaces(bool status);
|
||||
|
||||
void setHeaders(bool status);
|
||||
|
||||
void setStatus(STATUS status);
|
||||
|
||||
void writeEndPidTo(char const *string);
|
||||
|
||||
private:
|
||||
uint32_t baudRate; // Serial Baud Rate
|
||||
STATUS status; // Operation status
|
||||
bool echoEnable; // echoEnable command after received
|
||||
bool lineFeedEnable;
|
||||
bool memoryEnabled;
|
||||
bool whiteSpacesEnabled;
|
||||
bool headersEnabled;
|
||||
|
||||
void setBaudRate(uint32_t rate);
|
||||
|
||||
long getBaudRate();
|
||||
|
||||
void addSpacesToResponse(const char *response, char string[]);
|
||||
|
||||
#ifndef BLUETOOTH_BUILTIN
|
||||
HardwareSerial *serial; // lib to communicate with bluetooth
|
||||
#else
|
||||
BluetoothSerial *serial;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
-151
@@ -1,151 +0,0 @@
|
||||
#include "OBDWiFiComm.h"
|
||||
#include "definitions.h"
|
||||
|
||||
WiFiServer server(35000);
|
||||
IPAddress localIP = IPAddress(192, 168, 0, 10);
|
||||
IPAddress gateway = IPAddress(192, 168, 0, 10);
|
||||
IPAddress subnet = IPAddress(255,255,255,0);
|
||||
|
||||
OBDWiFiComm::OBDWiFiComm() {
|
||||
|
||||
}
|
||||
|
||||
OBDWiFiComm::~OBDWiFiComm() {
|
||||
}
|
||||
|
||||
void OBDWiFiComm::init(const String& deviceName) {
|
||||
Serial.println("Starting AP . . .");
|
||||
|
||||
WiFi.softAPConfig(localIP, gateway, subnet);
|
||||
WiFi.softAP(deviceName);
|
||||
|
||||
IPAddress myIP = WiFi.softAPIP();
|
||||
Serial.print("AP IP address: ");
|
||||
Serial.println(myIP);
|
||||
Serial.print("AP SSID: ");
|
||||
Serial.println(deviceName);
|
||||
server.begin();
|
||||
setToDefaults();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeEnd() {
|
||||
|
||||
// 1 - write carriage return
|
||||
writeTo("\r");
|
||||
|
||||
// 2- (optional ) write linefeed
|
||||
if (lineFeedEnable) {
|
||||
writeTo("\n");
|
||||
}
|
||||
|
||||
// 3 - Write prompt
|
||||
writeTo(">");
|
||||
};
|
||||
|
||||
|
||||
void OBDWiFiComm::writeEndOK() {
|
||||
writeTo("OK");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeEndERROR() {
|
||||
writeTo("ERROR");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeEndNoData() {
|
||||
writeTo("NO DATA");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeEndUnknown() {
|
||||
writeTo("?");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setToDefaults() {
|
||||
setEcho(true);
|
||||
setStatus(READY);
|
||||
setWhiteSpaces(true);
|
||||
setHeaders(false);
|
||||
setLineFeeds(true);
|
||||
setMemory(false);
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeTo(char const *response) {
|
||||
client.print(response);
|
||||
}
|
||||
|
||||
|
||||
void OBDWiFiComm::writeTo(uint8_t cChar) {
|
||||
client.print(cChar);
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeEndPidTo(char const *response) {
|
||||
if (this->whiteSpacesEnabled) {
|
||||
uint8_t len = strlen(response);
|
||||
char spacedResponse[len + len / 2 + 1];
|
||||
addSpacesToResponse(response, spacedResponse);
|
||||
writeTo(spacedResponse);
|
||||
} else {
|
||||
writeTo(response);
|
||||
}
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::readData(String& rxData) {
|
||||
if (!client.connected()) {
|
||||
client = server.available();
|
||||
}
|
||||
|
||||
if(client)
|
||||
{
|
||||
rxData
|
||||
= client.readStringUntil(SERIAL_END_CHAR);
|
||||
if (isEchoEnable())
|
||||
{
|
||||
writeTo(rxData.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OBDWiFiComm::isEchoEnable() {
|
||||
return this->echoEnable;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setEcho(bool echo) {
|
||||
this->echoEnable = echo;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setStatus(STATUS status) {
|
||||
this->status = status;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setLineFeeds(bool status) {
|
||||
this->lineFeedEnable = status;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setMemory(bool status) {
|
||||
this->memoryEnabled = status;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setWhiteSpaces(bool status) {
|
||||
this->whiteSpacesEnabled = status;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setHeaders(bool status) {
|
||||
this->headersEnabled = status;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::addSpacesToResponse(const char *response, char spacedRes[]) {
|
||||
uint8_t len = strlen(response);
|
||||
int j = 0;
|
||||
for (int i = 0; i < len;) {
|
||||
*(spacedRes + j++) = *(response + i++);
|
||||
*(spacedRes + j++) = *(response + i++);
|
||||
if (i < len) {
|
||||
*(spacedRes + j++) = 0x20;
|
||||
}
|
||||
}
|
||||
*(spacedRes + j) = '\0';
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
#ifndef ELMulator_OBDWiFiComm_h
|
||||
#define ELMulator_OBDWiFiComm_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiServer.h>
|
||||
#include "definitions.h"
|
||||
|
||||
|
||||
class OBDWiFiComm
|
||||
{
|
||||
public:
|
||||
enum STATUS
|
||||
{
|
||||
IDLE = 0,
|
||||
READY = 1
|
||||
};
|
||||
|
||||
OBDWiFiComm();
|
||||
|
||||
~OBDWiFiComm();
|
||||
|
||||
void init(const String &deviceName);
|
||||
|
||||
void writeEndOK();
|
||||
|
||||
void writeEndERROR();
|
||||
|
||||
/**
|
||||
* Response for unsupported PID command or missing sensor, respond "NO DATA".
|
||||
*/
|
||||
void writeEndNoData();
|
||||
|
||||
/**
|
||||
* Response for invalid AT command, or invalid PID or other bad input, respond "?"
|
||||
*/
|
||||
void writeEndUnknown();
|
||||
|
||||
void setToDefaults();
|
||||
|
||||
void readData(String &rxData);
|
||||
|
||||
void writeTo(uint8_t cChar);
|
||||
|
||||
void writeTo(char const *string);
|
||||
|
||||
void setEcho(bool echo);
|
||||
|
||||
void writeEnd();
|
||||
|
||||
bool isEchoEnable();
|
||||
|
||||
void setLineFeeds(bool status);
|
||||
|
||||
void setMemory(bool status);
|
||||
|
||||
void setWhiteSpaces(bool status);
|
||||
|
||||
void setHeaders(bool status);
|
||||
|
||||
void setStatus(STATUS status);
|
||||
|
||||
void writeEndPidTo(char const *string);
|
||||
|
||||
private:
|
||||
STATUS status; // Operation status
|
||||
bool echoEnable; // echoEnable command after received
|
||||
bool lineFeedEnable;
|
||||
bool memoryEnabled;
|
||||
bool whiteSpacesEnabled;
|
||||
bool headersEnabled;
|
||||
|
||||
void addSpacesToResponse(const char *response, char string[]);
|
||||
|
||||
WiFiClient client;
|
||||
};
|
||||
|
||||
#endif
|
||||
-195
@@ -1,195 +0,0 @@
|
||||
#include "PidProcessor.h"
|
||||
|
||||
#if USE_WIFI
|
||||
PidProcessor::PidProcessor(OBDWiFiComm *connection) {
|
||||
_connection = connection;
|
||||
resetPidMode01Array();
|
||||
};
|
||||
#else
|
||||
PidProcessor::PidProcessor(OBDSerialComm *connection) {
|
||||
_connection = connection;
|
||||
resetPidMode01Array();
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
bool PidProcessor::process(String& command) {
|
||||
bool processed = false;
|
||||
|
||||
if (command.length() > 4)
|
||||
{
|
||||
command = command.substring(0, 4); // remove num_responses value if present; lib only handles single responses
|
||||
}
|
||||
|
||||
if (!isMode01(command) & !isMode22(command))
|
||||
{
|
||||
_connection->writeEndNoData();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t hexCommand = strtoul(command.c_str(), NULL, HEX);
|
||||
uint8_t pid = getPidCodeFromHex(hexCommand);
|
||||
if (isSupportedPidRequest(pid)) { //reqeust to return a list of valid PIDs we can respond to
|
||||
processed = true;
|
||||
uint32_t supportedPids = getSupportedPids(pid);
|
||||
writePidResponse(command, 4, supportedPids);
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
|
||||
void PidProcessor::writePidResponse(const String& requestPid, uint8_t numberOfBytes, uint32_t value) {
|
||||
uint8_t nHexChars = PID_N_BYTES * N_CHARS_IN_BYTE + numberOfBytes * N_CHARS_IN_BYTE ;
|
||||
char responseArray[nHexChars + 1]; // one more for termination char
|
||||
getFormattedResponse(responseArray, nHexChars, requestPid, value);
|
||||
_connection->writeEndPidTo(responseArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a supported pid, so it can answer to pid support request, ex 0100, 0120, ...
|
||||
*/
|
||||
bool PidProcessor::registerMode01Pid(uint32_t pid) {
|
||||
// pid must be off type 01
|
||||
if (pid > 0x00 && pid < 0x0200) {
|
||||
// remove PidMode, only use pid code
|
||||
pid = getPidCodeFromHex(pid);
|
||||
setPidBit(pid);
|
||||
|
||||
char buffer[4];
|
||||
sprintf(buffer, "%02X", pid);
|
||||
DEBUG("Registered PID: " + String(buffer ));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PidProcessor::isMode01(const String& command)
|
||||
{
|
||||
return command.startsWith("01") ? true : false;
|
||||
}
|
||||
|
||||
bool PidProcessor::isMode01MIL(const String& command)
|
||||
{
|
||||
return command.startsWith("0101") ? true : false;
|
||||
}
|
||||
|
||||
bool PidProcessor::isMode03(const String& command)
|
||||
{
|
||||
return command.startsWith("03") ? true : false;
|
||||
}
|
||||
|
||||
bool PidProcessor::isMode22(const String& command)
|
||||
{
|
||||
return command.startsWith("22") ? true : false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* return true if:
|
||||
* - 1000
|
||||
* - 1020
|
||||
* - 1040
|
||||
* - 1060
|
||||
* ....
|
||||
*/
|
||||
bool PidProcessor::isSupportedPidRequest(uint8_t pid) {
|
||||
return pid == getPidIntervalId(pid);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the interval RANGEID for the given pid (ex: 00, 20, 40, ...)
|
||||
*/
|
||||
uint8_t PidProcessor::getPidIntervalId(uint8_t pidcode) {
|
||||
return getPidIntervalIndex(pidcode) * PID_INTERVAL_OFFSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the interval INDEX for the given pid (0,1,2,3,4,...)
|
||||
*/
|
||||
uint8_t PidProcessor::getPidIntervalIndex(uint8_t pidcode) {
|
||||
return pidcode / PID_INTERVAL_OFFSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a mode pid ex 010C ,
|
||||
* returns only the pid ex 0C
|
||||
*/
|
||||
uint8_t PidProcessor::getPidCodeFromHex(uint16_t hexCommand) {
|
||||
|
||||
if (hexCommand >= 0x0100) {
|
||||
hexCommand -= 0x100;
|
||||
}
|
||||
return (uint8_t) hexCommand; // return only the lower 8 bits (trim off num_messages or other extra bytes if present)
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a complete PID request,
|
||||
* returns only the pid ex 0C
|
||||
*/
|
||||
uint8_t PidProcessor::getPidCodeFromRequest(const String& request) {
|
||||
uint16_t hexCommand = strtoul(request.c_str(), NULL, HEX);
|
||||
return getPidCodeFromHex(hexCommand);
|
||||
}
|
||||
|
||||
|
||||
void PidProcessor::setPidBit(uint8_t pid) {
|
||||
uint8_t arrayIndex = getPidIntervalIndex(pid);
|
||||
if(isSupportedPidRequest(pid)) {
|
||||
arrayIndex -= 1;
|
||||
}
|
||||
|
||||
uint8_t bitPosition = getPidBitPosition(pid);
|
||||
bitWrite(pidMode01Supported[arrayIndex], bitPosition, 1);
|
||||
|
||||
// mark available intervals
|
||||
for (int i = 0; i < arrayIndex; i++) {
|
||||
bitWrite(pidMode01Supported[i], 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t PidProcessor::getPidBitPosition(uint8_t pid) {
|
||||
uint8_t intervalId = getPidIntervalId(pid) ;
|
||||
if(isSupportedPidRequest(pid)) {
|
||||
intervalId -= PID_INTERVAL_OFFSET;
|
||||
}
|
||||
|
||||
uint16_t pos = pid - intervalId;
|
||||
pos = PID_INTERVAL_OFFSET - pos;
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
uint32_t PidProcessor:: getSupportedPids(uint8_t pid) {
|
||||
uint8_t index = getPidIntervalIndex(pid);
|
||||
return pidMode01Supported[index];
|
||||
}
|
||||
|
||||
void PidProcessor::getFormattedResponse(char *response, uint8_t totalNumberOfChars, const String& pid, uint32_t value) {
|
||||
uint8_t nValueChars = totalNumberOfChars - PID_N_BYTES * N_CHARS_IN_BYTE;
|
||||
char cValue[3] = {0};
|
||||
itoa(nValueChars, cValue, DEC);
|
||||
String mask = "%s%0";
|
||||
mask.concat(cValue);
|
||||
mask.concat("lX\0");
|
||||
String pidResponse = convertToPidResponse(pid);
|
||||
sprintf(response, mask.c_str(), pidResponse.c_str(), value);
|
||||
|
||||
DEBUG("RX PID: " + pid + " - TX: " + String(response));
|
||||
}
|
||||
|
||||
String PidProcessor::convertToPidResponse(String pid) {
|
||||
String x = "4";
|
||||
x.concat(pid.substring(1, pid.length()));
|
||||
return x;
|
||||
}
|
||||
|
||||
void PidProcessor::resetPidMode01Array() {
|
||||
for(uint8_t i = 0; i < N_MODE01_INTERVALS; i++ ) {
|
||||
pidMode01Supported[i] = 0x0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
#ifndef ELMulator_PIDPROCESSOR_H
|
||||
#define ELMulator_PIDPROCESSOR_H
|
||||
|
||||
#include "definitions.h"
|
||||
#include <Arduino.h>
|
||||
#include <WString.h>
|
||||
#include <Print.h>
|
||||
#include "OBDWiFiComm.h"
|
||||
#include "OBDSerialComm.h"
|
||||
|
||||
class PidProcessor
|
||||
{
|
||||
|
||||
public:
|
||||
PidProcessor(OBDWiFiComm *connection);
|
||||
PidProcessor(OBDSerialComm *connection);
|
||||
bool process(String &string);
|
||||
|
||||
bool registerMode01Pid(uint32_t pid);
|
||||
|
||||
bool registerMode01MILResponse(const String &response);
|
||||
|
||||
bool registerMode03Response(const String &response);
|
||||
|
||||
void writePidResponse(const String &requestPid, uint8_t numberOfBytes, uint32_t value);
|
||||
|
||||
uint8_t getPidCodeFromHex(uint16_t hexCommand);
|
||||
uint8_t getPidCodeFromRequest(const String &command);
|
||||
|
||||
bool isMode01(const String &command);
|
||||
bool isMode01MIL(const String &command);
|
||||
bool isMode03(const String &command);
|
||||
bool isMode22(const String &command);
|
||||
|
||||
private:
|
||||
#if USE_WIFI
|
||||
OBDWiFiComm *_connection;
|
||||
#else
|
||||
OBDSerialComm *_connection;
|
||||
#endif
|
||||
uint32_t pidMode01Supported[N_MODE01_INTERVALS];
|
||||
|
||||
bool isSupportedPidRequest(uint8_t pid);
|
||||
|
||||
uint32_t getSupportedPids(uint8_t pidcode);
|
||||
|
||||
uint8_t getPidIntervalId(uint8_t pidcode);
|
||||
|
||||
uint8_t getPidIntervalIndex(uint8_t pidcode);
|
||||
|
||||
void setPidBit(uint8_t pid);
|
||||
|
||||
uint8_t getPidBitPosition(uint8_t pidcode);
|
||||
|
||||
String convertToPidResponse(String pid);
|
||||
|
||||
void getFormattedResponse(char *response, uint8_t totalNumberOfChars, const String &pid, uint32_t value);
|
||||
|
||||
void resetPidMode01Array();
|
||||
};
|
||||
|
||||
#endif // ELMulator_PIDPROCESSOR_H
|
||||
-294
@@ -1,294 +0,0 @@
|
||||
#ifndef ELMulator_DEFINITIONS_H
|
||||
#define ELMulator_DEFINITIONS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// true == We are running on hardware that has builtin Bluetooth
|
||||
// false == We are using hardware that has BT module attached via GPIO pins
|
||||
#define BLUETOOTH_BUILTIN true
|
||||
|
||||
#define USE_WIFI false
|
||||
|
||||
#define DO_DEBUG true
|
||||
#define DEBUG(x) do {if (DO_DEBUG) { Serial.println(x); } } while (0)
|
||||
|
||||
#define xtoc(x) ((x < 10) ? ('0' + x) : ('A' - 10 + x))
|
||||
#define getNumOfHexChars(nBytes) (nBytes * 2)
|
||||
|
||||
#define PID_N_BYTES 2
|
||||
#define N_CHARS_IN_BYTE 2
|
||||
|
||||
// Device ID
|
||||
#define ID "ELM327 / ELMulator V1.3.0"
|
||||
#define DESC "ELMulator OBD2 Arduino library, based on ELM327 protocol"
|
||||
#define PROTOCOL "6" // canbus 500k 11 bit protocol id for elm.
|
||||
|
||||
// Char representing end of serial string
|
||||
#define SERIAL_END_CHAR 0x0D
|
||||
#define SERIAL_READ_TIMEOUT 20000L
|
||||
|
||||
#define WIFI_END_CHAR 0x0A
|
||||
|
||||
const uint8_t maxPid = 0xFF;
|
||||
const uint8_t N_MODE01_INTERVALS = 7;
|
||||
const uint8_t PID_INTERVAL_OFFSET = 0x20;
|
||||
const uint8_t MAX_REQUEST_SIZE = 40;
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------//
|
||||
// Protocol IDs
|
||||
//-------------------------------------------------------------------------------------//
|
||||
const char AUTOMATIC = '0';
|
||||
const char SAE_J1850_PWM_41_KBAUD = '1';
|
||||
const char SAE_J1850_PWM_10_KBAUD = '2';
|
||||
const char ISO_9141_5_BAUD_INIT = '3';
|
||||
const char ISO_14230_5_BAUD_INIT = '4';
|
||||
const char ISO_14230_FAST_INIT = '5';
|
||||
const char ISO_15765_11_BIT_500_KBAUD = '6';
|
||||
const char ISO_15765_29_BIT_500_KBAUD = '7';
|
||||
const char ISO_15765_11_BIT_250_KBAUD = '8';
|
||||
const char ISO_15765_29_BIT_250_KBAUD = '9';
|
||||
const char SAE_J1939_29_BIT_250_KBAUD = 'A';
|
||||
const char USER_1_CAN = 'B';
|
||||
const char USER_2_CAN = 'C';
|
||||
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------//
|
||||
// PIDs (https://en.wikipedia.org/wiki/OBD-II_PIDs)
|
||||
//-------------------------------------------------------------------------------------//
|
||||
const uint8_t SUPPORTED_PIDS_1_20 = 0; // 0x00 - bit encoded
|
||||
const uint8_t MONITOR_STATUS_SINCE_DTC_CLEARED = 1; // 0x01 - bit encoded
|
||||
const uint8_t FREEZE_DTC = 2; // 0x02 -
|
||||
const uint8_t FUEL_SYSTEM_STATUS = 3; // 0x03 - bit encoded
|
||||
const uint8_t ENGINE_LOAD = 4; // 0x04 - %
|
||||
const uint8_t ENGINE_COOLANT_TEMP = 5; // 0x05 - °C
|
||||
const uint8_t SHORT_TERM_FUEL_TRIM_BANK_1 = 6; // 0x06 - %
|
||||
const uint8_t LONG_TERM_FUEL_TRIM_BANK_1 = 7; // 0x07 - %
|
||||
const uint8_t SHORT_TERM_FUEL_TRIM_BANK_2 = 8; // 0x08 - %
|
||||
const uint8_t LONG_TERM_FUEL_TRIM_BANK_2 = 9; // 0x09 - %
|
||||
const uint8_t FUEL_PRESSURE = 10; // 0x0A - kPa
|
||||
const uint8_t INTAKE_MANIFOLD_ABS_PRESSURE = 11; // 0x0B - kPa
|
||||
const uint8_t ENGINE_RPM = 12; // 0x0C - rpm
|
||||
const uint8_t VEHICLE_SPEED = 13; // 0x0D - km/h
|
||||
const uint8_t TIMING_ADVANCE = 14; // 0x0E - ° before TDC
|
||||
const uint8_t INTAKE_AIR_TEMP = 15; // 0x0F - °C
|
||||
const uint8_t MAF_FLOW_RATE = 16; // 0x10 - g/s
|
||||
const uint8_t THROTTLE_POSITION = 17; // 0x11 - %
|
||||
const uint8_t COMMANDED_SECONDARY_AIR_STATUS = 18; // 0x12 - bit encoded
|
||||
const uint8_t OXYGEN_SENSORS_PRESENT_2_BANKS = 19; // 0x13 - bit encoded
|
||||
const uint8_t OXYGEN_SENSOR_1_A = 20; // 0x14 - V %
|
||||
const uint8_t OXYGEN_SENSOR_2_A = 21; // 0x15 - V %
|
||||
const uint8_t OXYGEN_SENSOR_3_A = 22; // 0x16 - V %
|
||||
const uint8_t OXYGEN_SENSOR_4_A = 23; // 0x17 - V %
|
||||
const uint8_t OXYGEN_SENSOR_5_A = 24; // 0x18 - V %
|
||||
const uint8_t OXYGEN_SENSOR_6_A = 25; // 0x19 - V %
|
||||
const uint8_t OXYGEN_SENSOR_7_A = 26; // 0x1A - V %
|
||||
const uint8_t OXYGEN_SENSOR_8_A = 27; // 0x1B - V %
|
||||
const uint8_t OBD_STANDARDS = 28; // 0x1C - bit encoded
|
||||
const uint8_t OXYGEN_SENSORS_PRESENT_4_BANKS = 29; // 0x1D - bit encoded
|
||||
const uint8_t AUX_INPUT_STATUS = 30; // 0x1E - bit encoded
|
||||
const uint8_t RUN_TIME_SINCE_ENGINE_START = 31; // 0x1F - sec
|
||||
|
||||
const uint8_t SUPPORTED_PIDS_21_40 = 32; // 0x20 - bit encoded
|
||||
const uint8_t DISTANCE_TRAVELED_WITH_MIL_ON = 33; // 0x21 - km
|
||||
const uint8_t FUEL_RAIL_PRESSURE = 34; // 0x22 - kPa
|
||||
const uint8_t FUEL_RAIL_GUAGE_PRESSURE = 35; // 0x23 - kPa
|
||||
const uint8_t OXYGEN_SENSOR_1_B = 36; // 0x24 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_2_B = 37; // 0x25 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_3_B = 38; // 0x26 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_4_B = 39; // 0x27 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_5_B = 40; // 0x28 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_6_B = 41; // 0x29 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_7_B = 42; // 0x2A - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_8_B = 43; // 0x2B - ratio V
|
||||
const uint8_t COMMANDED_EGR = 44; // 0x2C - %
|
||||
const uint8_t EGR_ERROR = 45; // 0x2D - %
|
||||
const uint8_t COMMANDED_EVAPORATIVE_PURGE = 46; // 0x2E - %
|
||||
const uint8_t FUEL_TANK_LEVEL_INPUT = 47; // 0x2F - %
|
||||
const uint8_t WARM_UPS_SINCE_CODES_CLEARED = 48; // 0x30 - count
|
||||
const uint8_t DIST_TRAV_SINCE_CODES_CLEARED = 49; // 0x31 - km
|
||||
const uint8_t EVAP_SYSTEM_VAPOR_PRESSURE = 50; // 0x32 - Pa
|
||||
const uint8_t ABS_BAROMETRIC_PRESSURE = 51; // 0x33 - kPa
|
||||
const uint8_t OXYGEN_SENSOR_1_C = 52; // 0x34 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_2_C = 53; // 0x35 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_3_C = 54; // 0x36 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_4_C = 55; // 0x37 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_5_C = 56; // 0x38 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_6_C = 57; // 0x39 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_7_C = 58; // 0x3A - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_8_C = 59; // 0x3B - ratio mA
|
||||
const uint8_t CATALYST_TEMP_BANK_1_SENSOR_1 = 60; // 0x3C - °C
|
||||
const uint8_t CATALYST_TEMP_BANK_2_SENSOR_1 = 61; // 0x3D - °C
|
||||
const uint8_t CATALYST_TEMP_BANK_1_SENSOR_2 = 62; // 0x3E - °C
|
||||
const uint8_t CATALYST_TEMP_BANK_2_SENSOR_2 = 63; // 0x3F - °C
|
||||
|
||||
const uint8_t SUPPORTED_PIDS_41_60 = 64; // 0x40 - bit encoded
|
||||
const uint8_t MONITOR_STATUS_THIS_DRIVE_CYCLE = 65; // 0x41 - bit encoded
|
||||
const uint8_t CONTROL_MODULE_VOLTAGE = 66; // 0x42 - V
|
||||
const uint8_t ABS_LOAD_VALUE = 67; // 0x43 - %
|
||||
const uint8_t FUEL_AIR_COMMANDED_EQUIV_RATIO = 68; // 0x44 - ratio
|
||||
const uint8_t RELATIVE_THROTTLE_POSITION = 69; // 0x45 - %
|
||||
const uint8_t AMBIENT_AIR_TEMP = 70; // 0x46 - °C
|
||||
const uint8_t ABS_THROTTLE_POSITION_B = 71; // 0x47 - %
|
||||
const uint8_t ABS_THROTTLE_POSITION_C = 72; // 0x48 - %
|
||||
const uint8_t ABS_THROTTLE_POSITION_D = 73; // 0x49 - %
|
||||
const uint8_t ABS_THROTTLE_POSITION_E = 74; // 0x4A - %
|
||||
const uint8_t ABS_THROTTLE_POSITION_F = 75; // 0x4B - %
|
||||
const uint8_t COMMANDED_THROTTLE_ACTUATOR = 76; // 0x4C - %
|
||||
const uint8_t TIME_RUN_WITH_MIL_ON = 77; // 0x4D - min
|
||||
const uint8_t TIME_SINCE_CODES_CLEARED = 78; // 0x4E - min
|
||||
const uint8_t MAX_VALUES_EQUIV_V_I_PRESSURE = 79; // 0x4F - ratio V mA kPa
|
||||
const uint8_t MAX_MAF_RATE = 80; // 0x50 - g/s
|
||||
const uint8_t FUEL_TYPE = 81; // 0x51 - ref table
|
||||
const uint8_t ETHANOL_FUEL_PERCENT = 82; // 0x52 - %
|
||||
const uint8_t ABS_EVAP_SYS_VAPOR_PRESSURE = 83; // 0x53 - kPa
|
||||
const uint8_t EVAP_SYS_VAPOR_PRESSURE = 84; // 0x54 - Pa
|
||||
const uint8_t SHORT_TERM_SEC_OXY_SENS_TRIM_1_3 = 85; // 0x55 - %
|
||||
const uint8_t LONG_TERM_SEC_OXY_SENS_TRIM_1_3 = 86; // 0x56 - %
|
||||
const uint8_t SHORT_TERM_SEC_OXY_SENS_TRIM_2_4 = 87; // 0x57 - %
|
||||
const uint8_t LONG_TERM_SEC_OXY_SENS_TRIM_2_4 = 88; // 0x58 - %
|
||||
const uint8_t FUEL_RAIL_ABS_PRESSURE = 89; // 0x59 - kPa
|
||||
const uint8_t RELATIVE_ACCELERATOR_PEDAL_POS = 90; // 0x5A - %
|
||||
const uint8_t HYBRID_BATTERY_REMAINING_LIFE = 91; // 0x5B - %
|
||||
const uint8_t ENGINE_OIL_TEMP = 92; // 0x5C - °C
|
||||
const uint8_t FUEL_INJECTION_TIMING = 93; // 0x5D - °
|
||||
const uint8_t ENGINE_FUEL_RATE = 94; // 0x5E - L/h
|
||||
const uint8_t EMISSION_REQUIREMENTS = 95; // 0x5F - bit encoded
|
||||
|
||||
const uint8_t SUPPORTED_PIDS_61_80 = 96; // 0x60 - bit encoded
|
||||
const uint8_t DEMANDED_ENGINE_PERCENT_TORQUE = 97; // 0x61 - %
|
||||
const uint8_t ACTUAL_ENGINE_TORQUE = 98; // 0x62 - %
|
||||
const uint8_t ENGINE_REFERENCE_TORQUE = 99; // 0x63 - Nm
|
||||
const uint8_t ENGINE_PERCENT_TORQUE_DATA = 100; // 0x64 - %
|
||||
const uint8_t AUX_INPUT_OUTPUT_SUPPORTED = 101; // 0x65 - bit encoded
|
||||
|
||||
const uint8_t ODOMETER = 166; // 0xA6 - km * 10
|
||||
|
||||
const uint8_t SERVICE_01 = 1;
|
||||
const uint8_t SERVICE_02 = 2;
|
||||
const uint8_t SERVICE_03 = 3;
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------//
|
||||
// AT commands (https://www.sparkfun.com/datasheets/Widgets/ELM327_AT_Commands.pdf)
|
||||
//-------------------------------------------------------------------------------------//
|
||||
const char * const DISP_DEVICE_DESCRIPT = "AT @1"; // General
|
||||
const char * const DISP_DEVICE_ID = "AT @2"; // General
|
||||
const char * const STORE_DEVICE_ID = "AT @3 %s"; // General
|
||||
const char * const REPEAT_LAST_COMMAND = "AT \r"; // General
|
||||
const char * const ALLOW_LONG_MESSAGES = "AT AL"; // General
|
||||
const char * const AUTOMATIC_RECEIVE = "AT AR"; // OBD
|
||||
const char * const ADAPTIVE_TIMING_OFF = "AT AT0"; // OBD
|
||||
const char * const ADAPTIVE_TIMING_AUTO_1 = "AT AT1"; // OBD
|
||||
const char * const ADAPTIVE_TIMING_AUTO_2 = "AT AT2"; // OBD
|
||||
const char * const DUMP_BUFFER = "AT BD"; // OBD
|
||||
const char * const BYPASS_INIT_SEQUENCE = "AT BI"; // OBD
|
||||
const char * const TRY_BAUD_DIVISOR = "AT BRD %02d"; // General
|
||||
const char * const SET_HANDSHAKE_TIMEOUT = "AT BRT %02d"; // General
|
||||
const char * const CAN_AUTO_FORMAT_OFF = "AT CAF0"; // CAN
|
||||
const char * const CAN_AUTO_FORMAT_ON = "AT CAF1"; // CAN
|
||||
const char * const CAN_EXTENDED_ADDRESS_OFF = "AT CEA"; // CAN
|
||||
const char * const USE_CAN_EXTENDED_ADDRESS = "AT CEA %02d"; // CAN
|
||||
const char * const SET_ID_FILTER = "AT CF %s"; // CAN
|
||||
const char * const CAN_FLOW_CONTROL_OFF = "AT CFC0"; // CAN
|
||||
const char * const CAN_FLOW_CONTROL_ON = "AT CFC1"; // CAN
|
||||
const char * const SET_ID_MASK = "AT CM %s"; // CAN
|
||||
const char * const SET_CAN_PRIORITY = "AT CP %02d"; // CAN
|
||||
const char * const SHOW_CAN_STATUS = "AT CS"; // CAN
|
||||
const char * const CAN_SILENT_MODE_OFF = "AT CSM0"; // CAN
|
||||
const char * const CAN_SILENT_MODE_ON = "AT CSM1"; // CAN
|
||||
const char * const CALIBRATE_VOLTAGE_CUSTOM = "AT CV %04d"; // Volts
|
||||
const char * const RESTORE_CV_TO_FACTORY = "AT CV 0000"; // Volts
|
||||
const char * const SET_ALL_TO_DEFAULTS = "AT D"; // General
|
||||
const char * const DISP_DLC_OFF = "AT D0"; // CAN
|
||||
const char * const DISP_DLC_ON = "AT D1"; // CAN
|
||||
const char * const MONITOR_FOR_DM1_MESSAGES = "AT DM1"; // J1939
|
||||
const char * const DISP_CURRENT_PROTOCOL = "AT DP"; // OBD
|
||||
const char * const DISP_CURRENT_PROTOCOL_NUM = "AT DPN"; // OBD
|
||||
const char * const ECHO_OFF = "AT E0"; // General
|
||||
const char * const ECHO_ON = "AT E1"; // General
|
||||
const char * const FLOW_CONTROL_SET_DATA_TO = "AT FC SD %s"; // CAN
|
||||
const char * const FLOW_CONTROL_SET_HEAD_TO = "AT FC SH %s"; // CAN
|
||||
const char * const FLOW_CONTROL_SET_MODE_TO = "AT FC SM %c"; // CAN
|
||||
const char * const FORGE_EVENTS = "AT FE"; // General
|
||||
const char * const PERFORM_FAST_INIT = "AT FI"; // ISO
|
||||
const char * const HEADERS_OFF = "AT H0"; // OBD
|
||||
const char * const HEADERS_ON = "AT H1"; // OBD
|
||||
const char * const DISP_ID = "AT I"; // General
|
||||
const char * const SET_ISO_BAUD_10400 = "AT IB 10"; // ISO
|
||||
const char * const SET_ISO_BAUD_4800 = "AT IB 48"; // ISO
|
||||
const char * const SET_ISO_BAUD_9600 = "AT IB 96"; // ISO
|
||||
const char * const IFR_VAL_FROM_HEADER = "AT IFR H"; // J1850
|
||||
const char * const IFR_VAL_FROM_SOURCE = "AT IFR S"; // J1850
|
||||
const char * const IFRS_OFF = "AT IFR0"; // J1850
|
||||
const char * const IFRS_AUTO = "AT IFR1"; // J1850
|
||||
const char * const IFRS_ON = "AT IFR2"; // J1850
|
||||
const char * const IREAD_IGNMON_INPUT_LEVEL = "AT IGN"; // Other
|
||||
const char * const SET_ISO_SLOW_INIT_ADDRESS = "AT IIA %02d"; // ISO
|
||||
const char * const USE_J1939_ELM_DATA_FORMAT = "AT JE"; // J1850
|
||||
const char * const J1939_HEAD_FORMAT_OFF = "AT JHF0"; // J1850
|
||||
const char * const J1939_HEAD_FORMAT_ON = "AT JHF1"; // J1850
|
||||
const char * const USE_J1939_SAE_DATA_FORMAT = "AT JS"; // J1850
|
||||
const char * const SET_J1939_TIMER_X_TO_1X = "AT JTM1"; // J1850
|
||||
const char * const SET_J1939_TIMER_X_TO_5X = "AT JTM5"; // J1850
|
||||
const char * const DISP_KEY_WORDS = "AT KW"; // ISO
|
||||
const char * const KEY_WORD_CHECKING_OFF = "AT KW0"; // ISO
|
||||
const char * const KEY_WORD_CHECKING_ON = "AT KW1"; // ISO
|
||||
const char * const LINEFEEDS_OFF = "AT L0"; // General
|
||||
const char * const LINEFEEDS_ON = "AT L1"; // General
|
||||
const char * const LOW_POWER_MODE = "AT LP"; // General
|
||||
const char * const MEMORY_OFF = "AT M0"; // General
|
||||
const char * const MEMORY_ON = "AT M1"; // General
|
||||
const char * const MONITOR_ALL = "AT MA"; // OBD
|
||||
const char * const MONITOR_FOR_PGN = "AT MP %s"; // J1939
|
||||
const char * const MONITOR_FOR_RECEIVER = "AT MR %02d"; // OBD
|
||||
const char * const MONITOR_FOR_TRANSMITTER = "AT MT %02d"; // OBD
|
||||
const char * const NORMAL_LENGTH_MESSAGES = "AT NL"; // OBD
|
||||
const char * const SET_PROTO_OPTIONS_AND_BAUD = "AT PB %s"; // OBD
|
||||
const char * const PROTOCOL_CLOSE = "AT PC"; // OBD
|
||||
const char * const ALL_PROG_PARAMS_OFF = "AT PP FF OFF";// PPs
|
||||
const char * const ALL_PROG_PARAMS_ON = "AT PP FF ON"; // PPs
|
||||
const char * const SET_PROG_PARAM_OFF = "AT PP %s OFF";// PPs
|
||||
const char * const SET_PROG_PARAM_ON = "AT PP %s ON"; // PPs
|
||||
const char * const SET_PROG_PARAM_VAL = "AT PP %s SV %s"; // PPs
|
||||
const char * const DISP_PP_SUMMARY = "AT PPS"; // PPs
|
||||
const char * const RESPONSES_OFF = "AT R0"; // OBD
|
||||
const char * const RESPONSES_ON = "AT R1"; // OBD
|
||||
const char * const SET_RECEIVE_ADDRESS_TO = "AT RA %02d"; // OBD
|
||||
const char * const READ_STORED_DATA = "AT RD"; // General
|
||||
const char * const SEND_RTR_MESSAGE = "AT RTR"; // CAN
|
||||
const char * const READ_VOLTAGE = "AT RV"; // Volts
|
||||
const char * const PRINTING_SPACES_OFF = "AT S0"; // OBD
|
||||
const char * const PRINTING_SPACES_ON = "AT S1"; // OBD
|
||||
const char * const STORE_DATA_BYTE = "AT SD "; // General
|
||||
const char * const SET_HEADER = "AT SH %s"; // OBD
|
||||
const char * const PERFORM_SLOW_INIT = "AT SI"; // ISO
|
||||
const char * const SET_PROTOCOL_TO_AUTO_H_SAVE = "AT SP A%c"; // OBD
|
||||
const char * const SET_PROTOCOL_TO_H_SAVE = "AT SP %c"; // OBD
|
||||
const char * const SET_PROTOCOL_TO_AUTO_SAVE = "AT SP 00"; // OBD
|
||||
const char * const SET_REC_ADDRESS = "AT SR %02d"; // OBD
|
||||
const char * const SET_STANDARD_SEARCH_ORDER = "AT SS"; // OBD
|
||||
const char * const SET_TIMEOUT_TO_H_X_4MS = "AT ST %02d"; // OBD
|
||||
const char * const SET_WAKEUP_TO_H_X_20MS = "AT SW %02d"; // ISO
|
||||
const char * const SET_TESTER_ADDRESS_TO = "AT TA %02d"; // OBD
|
||||
const char * const TRY_PROT_H_AUTO_SEARCH = "AT TP A%c"; // OBD
|
||||
const char * const TRY_PROT_H = "AT TP %c"; // OBD
|
||||
const char * const VARIABLE_DLC_OFF = "AT V0"; // CAN
|
||||
const char * const VARIABLE_DLC_ON = "AT V1"; // CAN
|
||||
const char * const SET_WAKEUP_MESSAGE = "AT WM"; // ISO
|
||||
const char * const WARM_START = "AT WS"; // General
|
||||
const char * const RESET_ALL = "AT Z"; // General
|
||||
|
||||
|
||||
const uint8_t responseBytes[0xA9] =
|
||||
{
|
||||
4, 4, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2,
|
||||
4, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2,
|
||||
4, 4, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 4, 4, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 1,
|
||||
4, 1, 1, 2, 5, 2, 5, 3, 3, 7, 5, 5, 5, 11, 9, 3, 10, 6, 5, 5, 5, 7, 7, 5, 9, 9, 7, 7, 9, 1, 1, 13,
|
||||
4, 41, 41, 9, 1, 10, 5, 5, 13, 41, 41, 7, 17, 1, 1, 7, 3, 5, 2, 3, 12, 9, 9, 6, 4, 17, 4, 2, 9
|
||||
};
|
||||
|
||||
#endif //ELMulator_DEFINITIONS_H
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,27 @@
|
||||
; ELMulator — fake ELM327 OBD-II adapter for testing without a car.
|
||||
; Runs on a CLASSIC ESP32 (Bluetooth Classic SPP). Library lives in lib/ELMulator.
|
||||
; One env per example: pio run -e simple | custom | wifi
|
||||
|
||||
[platformio]
|
||||
default_envs = simple
|
||||
|
||||
[env]
|
||||
platform = espressif32@6.10.0
|
||||
board = esp32dev
|
||||
framework = arduino
|
||||
board_build.partitions = huge_app.csv
|
||||
; upload_port = COMxx ; the classic ESP32's port (COM21 is the JC3636W518)
|
||||
monitor_speed = 115200
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
; all Mode 01 PIDs answered with mock data, BT name "ELMULATOR"
|
||||
[env:simple]
|
||||
build_src_filter = +<simple/>
|
||||
|
||||
; custom PID handler + DTC response, BT name "ELMULATOR"
|
||||
[env:custom]
|
||||
build_src_filter = +<custom/>
|
||||
|
||||
; custom handler, BT name "OBDII" (matches what elm327_wifi_bridge connects to)
|
||||
[env:wifi]
|
||||
build_src_filter = +<wifi/>
|
||||
@@ -1,162 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
|
||||
bool ATCommands::process(const String& command) {
|
||||
bool processed = false;
|
||||
if (isATCommand(command)) {
|
||||
processed = true;
|
||||
processCommand(command);
|
||||
DEBUG("RX AT: " + command);
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
|
||||
void ATCommands::processCommand(const String& command) {
|
||||
|
||||
// if space is enabled (ex: AT H[charPosition])
|
||||
uint8_t offset = 2;
|
||||
if (command.charAt(2) == 0x20) {
|
||||
offset = 3;
|
||||
}
|
||||
|
||||
// refer to ELM327 specs
|
||||
String specificCommand = command.substring(offset, command.length());
|
||||
if (specificCommand.startsWith("D",offset)) {
|
||||
ATCommands::ATD();
|
||||
} else if (specificCommand.startsWith("Z")) {
|
||||
ATCommands::ATZ();
|
||||
} else if (specificCommand.startsWith("I")) {
|
||||
ATCommands::ATI();
|
||||
} else if (specificCommand.startsWith("E")) {
|
||||
ATCommands::ATEx(specificCommand);
|
||||
} else if (specificCommand.startsWith("L")) {
|
||||
ATCommands::ATLx(specificCommand);
|
||||
} else if (specificCommand.startsWith("M")) {
|
||||
ATCommands::ATMx(specificCommand);
|
||||
} else if (specificCommand.startsWith("S")) {
|
||||
if (specificCommand.startsWith("SP")) {
|
||||
ATCommands::ATSPx(specificCommand);
|
||||
} else {
|
||||
ATCommands::ATSx(specificCommand);
|
||||
}
|
||||
} else if (specificCommand.startsWith("H")) {
|
||||
ATCommands::ATHx(specificCommand);
|
||||
} else if (specificCommand.startsWith("AT")) {
|
||||
ATCommands::ATATx(specificCommand);
|
||||
} else if (specificCommand.startsWith("DPN")) {
|
||||
ATCommands::ATDPN();
|
||||
} else if (specificCommand.startsWith("DESC") || specificCommand.startsWith("@1")) {
|
||||
ATCommands::ATDESC();
|
||||
} else if (specificCommand.startsWith("PC")) {
|
||||
ATCommands::ATPC();
|
||||
} else if (specificCommand.startsWith("RV")){
|
||||
ATCommands::ATRV();
|
||||
} else {
|
||||
|
||||
// lets assume we process any at command
|
||||
connection->writeEndOK();
|
||||
}
|
||||
}
|
||||
|
||||
// set all to defaults
|
||||
void ATCommands::ATD() {
|
||||
connection->setToDefaults();
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// reset all
|
||||
void ATCommands::ATZ() {
|
||||
connection->setToDefaults();
|
||||
connection->writeTo(ID);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// Print the version ID
|
||||
void ATCommands::ATI() {
|
||||
connection->writeTo(ID);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// send description
|
||||
void ATCommands::ATDESC() {
|
||||
connection->writeTo(DESC);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// set echoEnable 0=off 1=on
|
||||
void ATCommands::ATEx(String& cmd) {
|
||||
connection->setEcho(cmd.equals("E0") ? false : true);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// set memory off=0 on=1
|
||||
void ATCommands::ATMx(String& cmd) {
|
||||
connection->setMemory(cmd.equals("M0") ? false : true);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// line feeds off=0 on=1
|
||||
void ATCommands::ATLx(String& cmd) {
|
||||
connection->setLineFeeds(cmd.equals("L0") ? false : true);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// ATSx printing spaces off=0 on=1
|
||||
void ATCommands::ATSx(String& cmd) {
|
||||
connection->setWhiteSpaces(cmd.equals("S0") ? false : true);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// Headers off=0 on=1
|
||||
void ATCommands::ATHx(String& cmd) {
|
||||
connection->setHeaders(cmd.equals("H0") ? false : true);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// ATSPx Define protocol 0=auto
|
||||
void ATCommands::ATSPx(String& cmd) {
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// set protocol
|
||||
void ATCommands::ATDPN() {
|
||||
connection->writeTo(PROTOCOL);
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// AT AT2 adaptative time control
|
||||
void ATCommands::ATATx(String& cmd) {
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
// Terminates current diagnostic session. Protocol close
|
||||
void ATCommands::ATPC() {
|
||||
connection->writeEndOK();
|
||||
}
|
||||
|
||||
void ATCommands::ATRV() {
|
||||
connection->writeTo("AT RV\r13.5V");
|
||||
connection->writeEnd();
|
||||
}
|
||||
|
||||
// return true ir connectionand is AT
|
||||
bool ATCommands::isATCommand(const String& command) {
|
||||
toUpperCase(command.charAt(0));
|
||||
return command.startsWith("AT") ? true : false;
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
#ifndef ELMulator_ATCommands_h
|
||||
#define ELMulator_ATCommands_h
|
||||
|
||||
#include <Arduino.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();
|
||||
|
||||
void ATI();
|
||||
|
||||
void ATEx(String &x);
|
||||
|
||||
void ATMx(String &x);
|
||||
|
||||
void ATLx(String &x);
|
||||
|
||||
void ATSx(String &x);
|
||||
|
||||
void ATHx(String &x);
|
||||
|
||||
void ATSPx(String &x);
|
||||
|
||||
void ATATx(String &cmd);
|
||||
|
||||
void ATPC();
|
||||
|
||||
void ATDPN();
|
||||
|
||||
void ATDESC();
|
||||
|
||||
void ATRV();
|
||||
|
||||
void processCommand(const String &command);
|
||||
|
||||
bool isATCommand(const String &command);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,218 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
#ifndef ELMulator_ELMulator_h
|
||||
#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"
|
||||
|
||||
class ELMulator
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* When creating a new instance of ELMulator with an attached
|
||||
* BT module, we need to set the Bluetooth serial parameters
|
||||
*
|
||||
* @param baudRate - rate for bluetooth serial port (normally 9600)
|
||||
* @param rxPin - the Arduino RX pin for the Bluetooth module (serial port)
|
||||
* @param txPin - the Arduino TX pin for the Bluetooth module (serial port)
|
||||
* @return
|
||||
*/
|
||||
ELMulator(uint32_t baudRate, uint8_t rxPin, uint8_t txPin);
|
||||
|
||||
ELMulator();
|
||||
|
||||
~ELMulator();
|
||||
|
||||
void init(const String &deviceName, const bool registerPids = false);
|
||||
|
||||
/**
|
||||
* Waits and Reads a request from OBD client software (example: torque)
|
||||
*
|
||||
* It will only return if the OBD client software
|
||||
* request's one of the previous registred PID (see: registerMode01Pid())
|
||||
*
|
||||
* Any other request will be automaticly handle or it will be rejected.
|
||||
*
|
||||
* ELMulator lib will automaticly handle:
|
||||
* - any AT command
|
||||
* - PID's support request like 0100, 0120, 0140, 0160, ...
|
||||
*
|
||||
* @return the request PID (Vehicle speed ex: 010D )
|
||||
*/
|
||||
bool readELMRequest();
|
||||
void sendELMResponse();
|
||||
void begin();
|
||||
|
||||
/**
|
||||
* Registry the PID's (sensors) your arduino will support.
|
||||
* Currently ELMulator lib only supports MODE 01 PID's
|
||||
*
|
||||
* In OBDII MODE 01 PID's, are request
|
||||
* to Show current data for a given sensor identified by a PID
|
||||
*
|
||||
* List of MODE 01 PID's: https://en.wikipedia.org/wiki/OBD-II_PIDs#Mode_01
|
||||
*
|
||||
* Example, for Vehicle speed, where OBDII PID code is 0D:
|
||||
*
|
||||
* registerMode01Pid(0x0D)
|
||||
*
|
||||
* @param pidHexId
|
||||
* @return returns true if the entered ID is in the valide range [0x00, 0xFF]
|
||||
*/
|
||||
bool registerMode01Pid(uint32_t pidHexId);
|
||||
|
||||
bool registerMode01MILResponse(const String &response);
|
||||
|
||||
bool registerMode03Response(const String &response);
|
||||
|
||||
uint8_t getPidCodeOnly(uint16_t hexCommand);
|
||||
|
||||
/**
|
||||
* Used to informe the OBD client that the PID,
|
||||
* it has requested is not supported
|
||||
*
|
||||
* In normal conditions, you will only receive,
|
||||
* request for the PID you have registred
|
||||
* All other request is automaticly handle by GTTuboEcu lib
|
||||
*/
|
||||
void writePidNotSupported();
|
||||
|
||||
/**
|
||||
* Respond to a given PID request from OBDII client (ex: torque)
|
||||
*
|
||||
* @param requestPid - the received pid
|
||||
* @param numberOfBytes - number of bytes this PID response VALUE is supposed to have
|
||||
* @param value - sensor value for this PID,
|
||||
* lib will convert it to HEX
|
||||
* with the appropriated number of HEX chars (numberOfChars)
|
||||
*/
|
||||
void writePidResponse(const String &requestPid, uint8_t numberOfBytes, uint32_t value);
|
||||
|
||||
// Write the response back to the requestor without PID formatting, etc
|
||||
// Just pass the response string on. Useful for testing with a specific response
|
||||
// that has been pre-configured.
|
||||
void writeResponse(const String &response);
|
||||
|
||||
bool isMode01(const String &command);
|
||||
bool isMode03(const String &command);
|
||||
bool isMode01MIL(const String &command);
|
||||
bool isMode22(const String &command);
|
||||
|
||||
uint8_t getPidCode(const String &request);
|
||||
void registerAllMode01Pids();
|
||||
uint32_t getMockSensorValue();
|
||||
|
||||
String elmRequest;
|
||||
|
||||
private:
|
||||
#if USE_WIFI
|
||||
OBDWiFiComm *_connection;
|
||||
#else
|
||||
OBDSerialComm *_connection;
|
||||
#endif
|
||||
ATCommands *_atProcessor;
|
||||
|
||||
PidProcessor *_pidProcessor;
|
||||
|
||||
String _lastCommand;
|
||||
|
||||
bool isCycleUp = true;
|
||||
|
||||
uint32_t cycle = 0;
|
||||
|
||||
bool processRequest(String &command);
|
||||
|
||||
bool isValidHex(const char *pid);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,145 +0,0 @@
|
||||
#include "OBDSerialComm.h"
|
||||
#include "definitions.h"
|
||||
|
||||
|
||||
OBDSerialComm::OBDSerialComm() {
|
||||
|
||||
}
|
||||
|
||||
OBDSerialComm::~OBDSerialComm() {
|
||||
operator delete(serial);
|
||||
}
|
||||
|
||||
void OBDSerialComm::init(const String& deviceName) {
|
||||
Serial.println("Starting BT . . .");
|
||||
serial = new BluetoothSerial();
|
||||
//delay(2000);
|
||||
serial->begin(deviceName, false);
|
||||
setToDefaults();
|
||||
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeEnd() {
|
||||
|
||||
// 1 - write carriage return
|
||||
writeTo("\r");
|
||||
|
||||
// 2- (optional ) write linefeed
|
||||
if (lineFeedEnable) {
|
||||
writeTo("\n");
|
||||
}
|
||||
|
||||
// 3 - Write prompt
|
||||
writeTo(">");
|
||||
|
||||
serial->flush();
|
||||
};
|
||||
|
||||
|
||||
void OBDSerialComm::writeEndOK() {
|
||||
writeTo("OK");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeEndERROR() {
|
||||
writeTo("ERROR");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeEndNoData() {
|
||||
writeTo("NO DATA");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeEndUnknown() {
|
||||
writeTo("?");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDSerialComm::setToDefaults() {
|
||||
setEcho(true);
|
||||
setStatus(READY);
|
||||
setWhiteSpaces(true);
|
||||
setHeaders(false);
|
||||
setLineFeeds(true);
|
||||
setMemory(false);
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeTo(char const *response) {
|
||||
serial->print(response);
|
||||
}
|
||||
|
||||
|
||||
void OBDSerialComm::writeTo(uint8_t cChar) {
|
||||
serial->print(cChar);
|
||||
}
|
||||
|
||||
void OBDSerialComm::writeEndPidTo(char const *response) {
|
||||
if (whiteSpacesEnabled) {
|
||||
uint8_t len = strlen(response);
|
||||
char spacedResponse[len + len / 2 + 1];
|
||||
addSpacesToResponse(response, spacedResponse);
|
||||
writeTo(spacedResponse);
|
||||
} else {
|
||||
writeTo(response);
|
||||
}
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDSerialComm::readData(String& rxData) {
|
||||
serial->flush(); // temp remove this
|
||||
rxData = serial->readStringUntil(SERIAL_END_CHAR);
|
||||
if (isEchoEnable()) {
|
||||
writeTo(rxData.c_str());
|
||||
}
|
||||
//return rxData;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setBaudRate(uint32_t rate) {
|
||||
this->baudRate = rate;
|
||||
}
|
||||
|
||||
long OBDSerialComm::getBaudRate() {
|
||||
return this->baudRate;
|
||||
}
|
||||
|
||||
bool OBDSerialComm::isEchoEnable() {
|
||||
return this->echoEnable;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setEcho(bool echo) {
|
||||
this->echoEnable = echo;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setStatus(STATUS status) {
|
||||
this->status = status;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setLineFeeds(bool status) {
|
||||
this->lineFeedEnable = status;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setMemory(bool status) {
|
||||
this->memoryEnabled = status;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setWhiteSpaces(bool status) {
|
||||
this->whiteSpacesEnabled = status;
|
||||
}
|
||||
|
||||
void OBDSerialComm::setHeaders(bool status) {
|
||||
this->headersEnabled = status;
|
||||
}
|
||||
|
||||
void OBDSerialComm::addSpacesToResponse(const char *response, char spacedRes[]) {
|
||||
uint8_t len = strlen(response);
|
||||
int j = 0;
|
||||
for (int i = 0; i < len;) {
|
||||
*(spacedRes + j++) = *(response + i++);
|
||||
*(spacedRes + j++) = *(response + i++);
|
||||
if (i < len) {
|
||||
*(spacedRes + j++) = 0x20;
|
||||
}
|
||||
}
|
||||
*(spacedRes + j) = '\0';
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
#ifndef ELMulator_OBDSerialComm_h
|
||||
#define ELMulator_OBDSerialComm_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "definitions.h"
|
||||
|
||||
#include <BluetoothSerial.h>
|
||||
|
||||
|
||||
class OBDSerialComm
|
||||
{
|
||||
public:
|
||||
enum STATUS
|
||||
{
|
||||
IDLE = 0,
|
||||
READY = 1
|
||||
};
|
||||
|
||||
// // Bluetooth is not built in, we are using a BT module via GPIO
|
||||
// OBDSerialComm(uint32_t baudRate, uint8_t rxPin, uint8_t txPin);
|
||||
|
||||
OBDSerialComm();
|
||||
|
||||
~OBDSerialComm();
|
||||
|
||||
void init(const String &deviceName);
|
||||
|
||||
void writeEndOK();
|
||||
|
||||
void writeEndERROR();
|
||||
|
||||
/**
|
||||
* Response for unsupported PID command or missing sensor, respond "NO DATA".
|
||||
*/
|
||||
void writeEndNoData();
|
||||
|
||||
/**
|
||||
* Response for invalid AT command, or invalid PID or other bad input, respond "?"
|
||||
*/
|
||||
void writeEndUnknown();
|
||||
|
||||
void setToDefaults();
|
||||
|
||||
void readData(String &rxData);
|
||||
|
||||
void writeTo(uint8_t cChar);
|
||||
|
||||
void writeTo(char const *string);
|
||||
|
||||
void setEcho(bool echo);
|
||||
|
||||
void writeEnd();
|
||||
|
||||
bool isEchoEnable();
|
||||
|
||||
void setLineFeeds(bool status);
|
||||
|
||||
void setMemory(bool status);
|
||||
|
||||
void setWhiteSpaces(bool status);
|
||||
|
||||
void setHeaders(bool status);
|
||||
|
||||
void setStatus(STATUS status);
|
||||
|
||||
void writeEndPidTo(char const *string);
|
||||
|
||||
private:
|
||||
uint32_t baudRate; // Serial Baud Rate
|
||||
STATUS status; // Operation status
|
||||
bool echoEnable; // echoEnable command after received
|
||||
bool lineFeedEnable;
|
||||
bool memoryEnabled;
|
||||
bool whiteSpacesEnabled;
|
||||
bool headersEnabled;
|
||||
|
||||
void setBaudRate(uint32_t rate);
|
||||
|
||||
long getBaudRate();
|
||||
|
||||
void addSpacesToResponse(const char *response, char string[]);
|
||||
|
||||
#ifndef BLUETOOTH_BUILTIN
|
||||
HardwareSerial *serial; // lib to communicate with bluetooth
|
||||
#else
|
||||
BluetoothSerial *serial;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,151 +0,0 @@
|
||||
#include "OBDWiFiComm.h"
|
||||
#include "definitions.h"
|
||||
|
||||
WiFiServer server(35000);
|
||||
IPAddress localIP = IPAddress(192, 168, 0, 10);
|
||||
IPAddress gateway = IPAddress(192, 168, 0, 10);
|
||||
IPAddress subnet = IPAddress(255,255,255,0);
|
||||
|
||||
OBDWiFiComm::OBDWiFiComm() {
|
||||
|
||||
}
|
||||
|
||||
OBDWiFiComm::~OBDWiFiComm() {
|
||||
}
|
||||
|
||||
void OBDWiFiComm::init(const String& deviceName) {
|
||||
Serial.println("Starting AP . . .");
|
||||
|
||||
WiFi.softAPConfig(localIP, gateway, subnet);
|
||||
WiFi.softAP(deviceName);
|
||||
|
||||
IPAddress myIP = WiFi.softAPIP();
|
||||
Serial.print("AP IP address: ");
|
||||
Serial.println(myIP);
|
||||
Serial.print("AP SSID: ");
|
||||
Serial.println(deviceName);
|
||||
server.begin();
|
||||
setToDefaults();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeEnd() {
|
||||
|
||||
// 1 - write carriage return
|
||||
writeTo("\r");
|
||||
|
||||
// 2- (optional ) write linefeed
|
||||
if (lineFeedEnable) {
|
||||
writeTo("\n");
|
||||
}
|
||||
|
||||
// 3 - Write prompt
|
||||
writeTo(">");
|
||||
};
|
||||
|
||||
|
||||
void OBDWiFiComm::writeEndOK() {
|
||||
writeTo("OK");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeEndERROR() {
|
||||
writeTo("ERROR");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeEndNoData() {
|
||||
writeTo("NO DATA");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeEndUnknown() {
|
||||
writeTo("?");
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setToDefaults() {
|
||||
setEcho(true);
|
||||
setStatus(READY);
|
||||
setWhiteSpaces(true);
|
||||
setHeaders(false);
|
||||
setLineFeeds(true);
|
||||
setMemory(false);
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeTo(char const *response) {
|
||||
client.print(response);
|
||||
}
|
||||
|
||||
|
||||
void OBDWiFiComm::writeTo(uint8_t cChar) {
|
||||
client.print(cChar);
|
||||
}
|
||||
|
||||
void OBDWiFiComm::writeEndPidTo(char const *response) {
|
||||
if (this->whiteSpacesEnabled) {
|
||||
uint8_t len = strlen(response);
|
||||
char spacedResponse[len + len / 2 + 1];
|
||||
addSpacesToResponse(response, spacedResponse);
|
||||
writeTo(spacedResponse);
|
||||
} else {
|
||||
writeTo(response);
|
||||
}
|
||||
writeEnd();
|
||||
}
|
||||
|
||||
void OBDWiFiComm::readData(String& rxData) {
|
||||
if (!client.connected()) {
|
||||
client = server.available();
|
||||
}
|
||||
|
||||
if(client)
|
||||
{
|
||||
rxData
|
||||
= client.readStringUntil(SERIAL_END_CHAR);
|
||||
if (isEchoEnable())
|
||||
{
|
||||
writeTo(rxData.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OBDWiFiComm::isEchoEnable() {
|
||||
return this->echoEnable;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setEcho(bool echo) {
|
||||
this->echoEnable = echo;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setStatus(STATUS status) {
|
||||
this->status = status;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setLineFeeds(bool status) {
|
||||
this->lineFeedEnable = status;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setMemory(bool status) {
|
||||
this->memoryEnabled = status;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setWhiteSpaces(bool status) {
|
||||
this->whiteSpacesEnabled = status;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::setHeaders(bool status) {
|
||||
this->headersEnabled = status;
|
||||
}
|
||||
|
||||
void OBDWiFiComm::addSpacesToResponse(const char *response, char spacedRes[]) {
|
||||
uint8_t len = strlen(response);
|
||||
int j = 0;
|
||||
for (int i = 0; i < len;) {
|
||||
*(spacedRes + j++) = *(response + i++);
|
||||
*(spacedRes + j++) = *(response + i++);
|
||||
if (i < len) {
|
||||
*(spacedRes + j++) = 0x20;
|
||||
}
|
||||
}
|
||||
*(spacedRes + j) = '\0';
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
#ifndef ELMulator_OBDWiFiComm_h
|
||||
#define ELMulator_OBDWiFiComm_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiServer.h>
|
||||
#include "definitions.h"
|
||||
|
||||
|
||||
class OBDWiFiComm
|
||||
{
|
||||
public:
|
||||
enum STATUS
|
||||
{
|
||||
IDLE = 0,
|
||||
READY = 1
|
||||
};
|
||||
|
||||
OBDWiFiComm();
|
||||
|
||||
~OBDWiFiComm();
|
||||
|
||||
void init(const String &deviceName);
|
||||
|
||||
void writeEndOK();
|
||||
|
||||
void writeEndERROR();
|
||||
|
||||
/**
|
||||
* Response for unsupported PID command or missing sensor, respond "NO DATA".
|
||||
*/
|
||||
void writeEndNoData();
|
||||
|
||||
/**
|
||||
* Response for invalid AT command, or invalid PID or other bad input, respond "?"
|
||||
*/
|
||||
void writeEndUnknown();
|
||||
|
||||
void setToDefaults();
|
||||
|
||||
void readData(String &rxData);
|
||||
|
||||
void writeTo(uint8_t cChar);
|
||||
|
||||
void writeTo(char const *string);
|
||||
|
||||
void setEcho(bool echo);
|
||||
|
||||
void writeEnd();
|
||||
|
||||
bool isEchoEnable();
|
||||
|
||||
void setLineFeeds(bool status);
|
||||
|
||||
void setMemory(bool status);
|
||||
|
||||
void setWhiteSpaces(bool status);
|
||||
|
||||
void setHeaders(bool status);
|
||||
|
||||
void setStatus(STATUS status);
|
||||
|
||||
void writeEndPidTo(char const *string);
|
||||
|
||||
private:
|
||||
STATUS status; // Operation status
|
||||
bool echoEnable; // echoEnable command after received
|
||||
bool lineFeedEnable;
|
||||
bool memoryEnabled;
|
||||
bool whiteSpacesEnabled;
|
||||
bool headersEnabled;
|
||||
|
||||
void addSpacesToResponse(const char *response, char string[]);
|
||||
|
||||
WiFiClient client;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,195 +0,0 @@
|
||||
#include "PidProcessor.h"
|
||||
|
||||
#if USE_WIFI
|
||||
PidProcessor::PidProcessor(OBDWiFiComm *connection) {
|
||||
_connection = connection;
|
||||
resetPidMode01Array();
|
||||
};
|
||||
#else
|
||||
PidProcessor::PidProcessor(OBDSerialComm *connection) {
|
||||
_connection = connection;
|
||||
resetPidMode01Array();
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
bool PidProcessor::process(String& command) {
|
||||
bool processed = false;
|
||||
|
||||
if (command.length() > 4)
|
||||
{
|
||||
command = command.substring(0, 4); // remove num_responses value if present; lib only handles single responses
|
||||
}
|
||||
|
||||
if (!isMode01(command) & !isMode22(command))
|
||||
{
|
||||
_connection->writeEndNoData();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t hexCommand = strtoul(command.c_str(), NULL, HEX);
|
||||
uint8_t pid = getPidCodeFromHex(hexCommand);
|
||||
if (isSupportedPidRequest(pid)) { //reqeust to return a list of valid PIDs we can respond to
|
||||
processed = true;
|
||||
uint32_t supportedPids = getSupportedPids(pid);
|
||||
writePidResponse(command, 4, supportedPids);
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
|
||||
void PidProcessor::writePidResponse(const String& requestPid, uint8_t numberOfBytes, uint32_t value) {
|
||||
uint8_t nHexChars = PID_N_BYTES * N_CHARS_IN_BYTE + numberOfBytes * N_CHARS_IN_BYTE ;
|
||||
char responseArray[nHexChars + 1]; // one more for termination char
|
||||
getFormattedResponse(responseArray, nHexChars, requestPid, value);
|
||||
_connection->writeEndPidTo(responseArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a supported pid, so it can answer to pid support request, ex 0100, 0120, ...
|
||||
*/
|
||||
bool PidProcessor::registerMode01Pid(uint32_t pid) {
|
||||
// pid must be off type 01
|
||||
if (pid > 0x00 && pid < 0x0200) {
|
||||
// remove PidMode, only use pid code
|
||||
pid = getPidCodeFromHex(pid);
|
||||
setPidBit(pid);
|
||||
|
||||
char buffer[4];
|
||||
sprintf(buffer, "%02X", pid);
|
||||
DEBUG("Registered PID: " + String(buffer ));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PidProcessor::isMode01(const String& command)
|
||||
{
|
||||
return command.startsWith("01") ? true : false;
|
||||
}
|
||||
|
||||
bool PidProcessor::isMode01MIL(const String& command)
|
||||
{
|
||||
return command.startsWith("0101") ? true : false;
|
||||
}
|
||||
|
||||
bool PidProcessor::isMode03(const String& command)
|
||||
{
|
||||
return command.startsWith("03") ? true : false;
|
||||
}
|
||||
|
||||
bool PidProcessor::isMode22(const String& command)
|
||||
{
|
||||
return command.startsWith("22") ? true : false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* return true if:
|
||||
* - 1000
|
||||
* - 1020
|
||||
* - 1040
|
||||
* - 1060
|
||||
* ....
|
||||
*/
|
||||
bool PidProcessor::isSupportedPidRequest(uint8_t pid) {
|
||||
return pid == getPidIntervalId(pid);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the interval RANGEID for the given pid (ex: 00, 20, 40, ...)
|
||||
*/
|
||||
uint8_t PidProcessor::getPidIntervalId(uint8_t pidcode) {
|
||||
return getPidIntervalIndex(pidcode) * PID_INTERVAL_OFFSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the interval INDEX for the given pid (0,1,2,3,4,...)
|
||||
*/
|
||||
uint8_t PidProcessor::getPidIntervalIndex(uint8_t pidcode) {
|
||||
return pidcode / PID_INTERVAL_OFFSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a mode pid ex 010C ,
|
||||
* returns only the pid ex 0C
|
||||
*/
|
||||
uint8_t PidProcessor::getPidCodeFromHex(uint16_t hexCommand) {
|
||||
|
||||
if (hexCommand >= 0x0100) {
|
||||
hexCommand -= 0x100;
|
||||
}
|
||||
return (uint8_t) hexCommand; // return only the lower 8 bits (trim off num_messages or other extra bytes if present)
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a complete PID request,
|
||||
* returns only the pid ex 0C
|
||||
*/
|
||||
uint8_t PidProcessor::getPidCodeFromRequest(const String& request) {
|
||||
uint16_t hexCommand = strtoul(request.c_str(), NULL, HEX);
|
||||
return getPidCodeFromHex(hexCommand);
|
||||
}
|
||||
|
||||
|
||||
void PidProcessor::setPidBit(uint8_t pid) {
|
||||
uint8_t arrayIndex = getPidIntervalIndex(pid);
|
||||
if(isSupportedPidRequest(pid)) {
|
||||
arrayIndex -= 1;
|
||||
}
|
||||
|
||||
uint8_t bitPosition = getPidBitPosition(pid);
|
||||
bitWrite(pidMode01Supported[arrayIndex], bitPosition, 1);
|
||||
|
||||
// mark available intervals
|
||||
for (int i = 0; i < arrayIndex; i++) {
|
||||
bitWrite(pidMode01Supported[i], 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t PidProcessor::getPidBitPosition(uint8_t pid) {
|
||||
uint8_t intervalId = getPidIntervalId(pid) ;
|
||||
if(isSupportedPidRequest(pid)) {
|
||||
intervalId -= PID_INTERVAL_OFFSET;
|
||||
}
|
||||
|
||||
uint16_t pos = pid - intervalId;
|
||||
pos = PID_INTERVAL_OFFSET - pos;
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
uint32_t PidProcessor:: getSupportedPids(uint8_t pid) {
|
||||
uint8_t index = getPidIntervalIndex(pid);
|
||||
return pidMode01Supported[index];
|
||||
}
|
||||
|
||||
void PidProcessor::getFormattedResponse(char *response, uint8_t totalNumberOfChars, const String& pid, uint32_t value) {
|
||||
uint8_t nValueChars = totalNumberOfChars - PID_N_BYTES * N_CHARS_IN_BYTE;
|
||||
char cValue[3] = {0};
|
||||
itoa(nValueChars, cValue, DEC);
|
||||
String mask = "%s%0";
|
||||
mask.concat(cValue);
|
||||
mask.concat("lX\0");
|
||||
String pidResponse = convertToPidResponse(pid);
|
||||
sprintf(response, mask.c_str(), pidResponse.c_str(), value);
|
||||
|
||||
DEBUG("RX PID: " + pid + " - TX: " + String(response));
|
||||
}
|
||||
|
||||
String PidProcessor::convertToPidResponse(String pid) {
|
||||
String x = "4";
|
||||
x.concat(pid.substring(1, pid.length()));
|
||||
return x;
|
||||
}
|
||||
|
||||
void PidProcessor::resetPidMode01Array() {
|
||||
for(uint8_t i = 0; i < N_MODE01_INTERVALS; i++ ) {
|
||||
pidMode01Supported[i] = 0x0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
#ifndef ELMulator_PIDPROCESSOR_H
|
||||
#define ELMulator_PIDPROCESSOR_H
|
||||
|
||||
#include "definitions.h"
|
||||
#include <Arduino.h>
|
||||
#include <WString.h>
|
||||
#include <Print.h>
|
||||
#include "OBDWiFiComm.h"
|
||||
#include "OBDSerialComm.h"
|
||||
|
||||
class PidProcessor
|
||||
{
|
||||
|
||||
public:
|
||||
PidProcessor(OBDWiFiComm *connection);
|
||||
PidProcessor(OBDSerialComm *connection);
|
||||
bool process(String &string);
|
||||
|
||||
bool registerMode01Pid(uint32_t pid);
|
||||
|
||||
bool registerMode01MILResponse(const String &response);
|
||||
|
||||
bool registerMode03Response(const String &response);
|
||||
|
||||
void writePidResponse(const String &requestPid, uint8_t numberOfBytes, uint32_t value);
|
||||
|
||||
uint8_t getPidCodeFromHex(uint16_t hexCommand);
|
||||
uint8_t getPidCodeFromRequest(const String &command);
|
||||
|
||||
bool isMode01(const String &command);
|
||||
bool isMode01MIL(const String &command);
|
||||
bool isMode03(const String &command);
|
||||
bool isMode22(const String &command);
|
||||
|
||||
private:
|
||||
#if USE_WIFI
|
||||
OBDWiFiComm *_connection;
|
||||
#else
|
||||
OBDSerialComm *_connection;
|
||||
#endif
|
||||
uint32_t pidMode01Supported[N_MODE01_INTERVALS];
|
||||
|
||||
bool isSupportedPidRequest(uint8_t pid);
|
||||
|
||||
uint32_t getSupportedPids(uint8_t pidcode);
|
||||
|
||||
uint8_t getPidIntervalId(uint8_t pidcode);
|
||||
|
||||
uint8_t getPidIntervalIndex(uint8_t pidcode);
|
||||
|
||||
void setPidBit(uint8_t pid);
|
||||
|
||||
uint8_t getPidBitPosition(uint8_t pidcode);
|
||||
|
||||
String convertToPidResponse(String pid);
|
||||
|
||||
void getFormattedResponse(char *response, uint8_t totalNumberOfChars, const String &pid, uint32_t value);
|
||||
|
||||
void resetPidMode01Array();
|
||||
};
|
||||
|
||||
#endif // ELMulator_PIDPROCESSOR_H
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,294 +0,0 @@
|
||||
#ifndef ELMulator_DEFINITIONS_H
|
||||
#define ELMulator_DEFINITIONS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// true == We are running on hardware that has builtin Bluetooth
|
||||
// false == We are using hardware that has BT module attached via GPIO pins
|
||||
#define BLUETOOTH_BUILTIN true
|
||||
|
||||
#define USE_WIFI false
|
||||
|
||||
#define DO_DEBUG true
|
||||
#define DEBUG(x) do {if (DO_DEBUG) { Serial.println(x); } } while (0)
|
||||
|
||||
#define xtoc(x) ((x < 10) ? ('0' + x) : ('A' - 10 + x))
|
||||
#define getNumOfHexChars(nBytes) (nBytes * 2)
|
||||
|
||||
#define PID_N_BYTES 2
|
||||
#define N_CHARS_IN_BYTE 2
|
||||
|
||||
// Device ID
|
||||
#define ID "ELM327 / ELMulator V1.3.0"
|
||||
#define DESC "ELMulator OBD2 Arduino library, based on ELM327 protocol"
|
||||
#define PROTOCOL "6" // canbus 500k 11 bit protocol id for elm.
|
||||
|
||||
// Char representing end of serial string
|
||||
#define SERIAL_END_CHAR 0x0D
|
||||
#define SERIAL_READ_TIMEOUT 20000L
|
||||
|
||||
#define WIFI_END_CHAR 0x0A
|
||||
|
||||
const uint8_t maxPid = 0xFF;
|
||||
const uint8_t N_MODE01_INTERVALS = 7;
|
||||
const uint8_t PID_INTERVAL_OFFSET = 0x20;
|
||||
const uint8_t MAX_REQUEST_SIZE = 40;
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------//
|
||||
// Protocol IDs
|
||||
//-------------------------------------------------------------------------------------//
|
||||
const char AUTOMATIC = '0';
|
||||
const char SAE_J1850_PWM_41_KBAUD = '1';
|
||||
const char SAE_J1850_PWM_10_KBAUD = '2';
|
||||
const char ISO_9141_5_BAUD_INIT = '3';
|
||||
const char ISO_14230_5_BAUD_INIT = '4';
|
||||
const char ISO_14230_FAST_INIT = '5';
|
||||
const char ISO_15765_11_BIT_500_KBAUD = '6';
|
||||
const char ISO_15765_29_BIT_500_KBAUD = '7';
|
||||
const char ISO_15765_11_BIT_250_KBAUD = '8';
|
||||
const char ISO_15765_29_BIT_250_KBAUD = '9';
|
||||
const char SAE_J1939_29_BIT_250_KBAUD = 'A';
|
||||
const char USER_1_CAN = 'B';
|
||||
const char USER_2_CAN = 'C';
|
||||
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------//
|
||||
// PIDs (https://en.wikipedia.org/wiki/OBD-II_PIDs)
|
||||
//-------------------------------------------------------------------------------------//
|
||||
const uint8_t SUPPORTED_PIDS_1_20 = 0; // 0x00 - bit encoded
|
||||
const uint8_t MONITOR_STATUS_SINCE_DTC_CLEARED = 1; // 0x01 - bit encoded
|
||||
const uint8_t FREEZE_DTC = 2; // 0x02 -
|
||||
const uint8_t FUEL_SYSTEM_STATUS = 3; // 0x03 - bit encoded
|
||||
const uint8_t ENGINE_LOAD = 4; // 0x04 - %
|
||||
const uint8_t ENGINE_COOLANT_TEMP = 5; // 0x05 - °C
|
||||
const uint8_t SHORT_TERM_FUEL_TRIM_BANK_1 = 6; // 0x06 - %
|
||||
const uint8_t LONG_TERM_FUEL_TRIM_BANK_1 = 7; // 0x07 - %
|
||||
const uint8_t SHORT_TERM_FUEL_TRIM_BANK_2 = 8; // 0x08 - %
|
||||
const uint8_t LONG_TERM_FUEL_TRIM_BANK_2 = 9; // 0x09 - %
|
||||
const uint8_t FUEL_PRESSURE = 10; // 0x0A - kPa
|
||||
const uint8_t INTAKE_MANIFOLD_ABS_PRESSURE = 11; // 0x0B - kPa
|
||||
const uint8_t ENGINE_RPM = 12; // 0x0C - rpm
|
||||
const uint8_t VEHICLE_SPEED = 13; // 0x0D - km/h
|
||||
const uint8_t TIMING_ADVANCE = 14; // 0x0E - ° before TDC
|
||||
const uint8_t INTAKE_AIR_TEMP = 15; // 0x0F - °C
|
||||
const uint8_t MAF_FLOW_RATE = 16; // 0x10 - g/s
|
||||
const uint8_t THROTTLE_POSITION = 17; // 0x11 - %
|
||||
const uint8_t COMMANDED_SECONDARY_AIR_STATUS = 18; // 0x12 - bit encoded
|
||||
const uint8_t OXYGEN_SENSORS_PRESENT_2_BANKS = 19; // 0x13 - bit encoded
|
||||
const uint8_t OXYGEN_SENSOR_1_A = 20; // 0x14 - V %
|
||||
const uint8_t OXYGEN_SENSOR_2_A = 21; // 0x15 - V %
|
||||
const uint8_t OXYGEN_SENSOR_3_A = 22; // 0x16 - V %
|
||||
const uint8_t OXYGEN_SENSOR_4_A = 23; // 0x17 - V %
|
||||
const uint8_t OXYGEN_SENSOR_5_A = 24; // 0x18 - V %
|
||||
const uint8_t OXYGEN_SENSOR_6_A = 25; // 0x19 - V %
|
||||
const uint8_t OXYGEN_SENSOR_7_A = 26; // 0x1A - V %
|
||||
const uint8_t OXYGEN_SENSOR_8_A = 27; // 0x1B - V %
|
||||
const uint8_t OBD_STANDARDS = 28; // 0x1C - bit encoded
|
||||
const uint8_t OXYGEN_SENSORS_PRESENT_4_BANKS = 29; // 0x1D - bit encoded
|
||||
const uint8_t AUX_INPUT_STATUS = 30; // 0x1E - bit encoded
|
||||
const uint8_t RUN_TIME_SINCE_ENGINE_START = 31; // 0x1F - sec
|
||||
|
||||
const uint8_t SUPPORTED_PIDS_21_40 = 32; // 0x20 - bit encoded
|
||||
const uint8_t DISTANCE_TRAVELED_WITH_MIL_ON = 33; // 0x21 - km
|
||||
const uint8_t FUEL_RAIL_PRESSURE = 34; // 0x22 - kPa
|
||||
const uint8_t FUEL_RAIL_GUAGE_PRESSURE = 35; // 0x23 - kPa
|
||||
const uint8_t OXYGEN_SENSOR_1_B = 36; // 0x24 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_2_B = 37; // 0x25 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_3_B = 38; // 0x26 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_4_B = 39; // 0x27 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_5_B = 40; // 0x28 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_6_B = 41; // 0x29 - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_7_B = 42; // 0x2A - ratio V
|
||||
const uint8_t OXYGEN_SENSOR_8_B = 43; // 0x2B - ratio V
|
||||
const uint8_t COMMANDED_EGR = 44; // 0x2C - %
|
||||
const uint8_t EGR_ERROR = 45; // 0x2D - %
|
||||
const uint8_t COMMANDED_EVAPORATIVE_PURGE = 46; // 0x2E - %
|
||||
const uint8_t FUEL_TANK_LEVEL_INPUT = 47; // 0x2F - %
|
||||
const uint8_t WARM_UPS_SINCE_CODES_CLEARED = 48; // 0x30 - count
|
||||
const uint8_t DIST_TRAV_SINCE_CODES_CLEARED = 49; // 0x31 - km
|
||||
const uint8_t EVAP_SYSTEM_VAPOR_PRESSURE = 50; // 0x32 - Pa
|
||||
const uint8_t ABS_BAROMETRIC_PRESSURE = 51; // 0x33 - kPa
|
||||
const uint8_t OXYGEN_SENSOR_1_C = 52; // 0x34 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_2_C = 53; // 0x35 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_3_C = 54; // 0x36 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_4_C = 55; // 0x37 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_5_C = 56; // 0x38 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_6_C = 57; // 0x39 - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_7_C = 58; // 0x3A - ratio mA
|
||||
const uint8_t OXYGEN_SENSOR_8_C = 59; // 0x3B - ratio mA
|
||||
const uint8_t CATALYST_TEMP_BANK_1_SENSOR_1 = 60; // 0x3C - °C
|
||||
const uint8_t CATALYST_TEMP_BANK_2_SENSOR_1 = 61; // 0x3D - °C
|
||||
const uint8_t CATALYST_TEMP_BANK_1_SENSOR_2 = 62; // 0x3E - °C
|
||||
const uint8_t CATALYST_TEMP_BANK_2_SENSOR_2 = 63; // 0x3F - °C
|
||||
|
||||
const uint8_t SUPPORTED_PIDS_41_60 = 64; // 0x40 - bit encoded
|
||||
const uint8_t MONITOR_STATUS_THIS_DRIVE_CYCLE = 65; // 0x41 - bit encoded
|
||||
const uint8_t CONTROL_MODULE_VOLTAGE = 66; // 0x42 - V
|
||||
const uint8_t ABS_LOAD_VALUE = 67; // 0x43 - %
|
||||
const uint8_t FUEL_AIR_COMMANDED_EQUIV_RATIO = 68; // 0x44 - ratio
|
||||
const uint8_t RELATIVE_THROTTLE_POSITION = 69; // 0x45 - %
|
||||
const uint8_t AMBIENT_AIR_TEMP = 70; // 0x46 - °C
|
||||
const uint8_t ABS_THROTTLE_POSITION_B = 71; // 0x47 - %
|
||||
const uint8_t ABS_THROTTLE_POSITION_C = 72; // 0x48 - %
|
||||
const uint8_t ABS_THROTTLE_POSITION_D = 73; // 0x49 - %
|
||||
const uint8_t ABS_THROTTLE_POSITION_E = 74; // 0x4A - %
|
||||
const uint8_t ABS_THROTTLE_POSITION_F = 75; // 0x4B - %
|
||||
const uint8_t COMMANDED_THROTTLE_ACTUATOR = 76; // 0x4C - %
|
||||
const uint8_t TIME_RUN_WITH_MIL_ON = 77; // 0x4D - min
|
||||
const uint8_t TIME_SINCE_CODES_CLEARED = 78; // 0x4E - min
|
||||
const uint8_t MAX_VALUES_EQUIV_V_I_PRESSURE = 79; // 0x4F - ratio V mA kPa
|
||||
const uint8_t MAX_MAF_RATE = 80; // 0x50 - g/s
|
||||
const uint8_t FUEL_TYPE = 81; // 0x51 - ref table
|
||||
const uint8_t ETHANOL_FUEL_PERCENT = 82; // 0x52 - %
|
||||
const uint8_t ABS_EVAP_SYS_VAPOR_PRESSURE = 83; // 0x53 - kPa
|
||||
const uint8_t EVAP_SYS_VAPOR_PRESSURE = 84; // 0x54 - Pa
|
||||
const uint8_t SHORT_TERM_SEC_OXY_SENS_TRIM_1_3 = 85; // 0x55 - %
|
||||
const uint8_t LONG_TERM_SEC_OXY_SENS_TRIM_1_3 = 86; // 0x56 - %
|
||||
const uint8_t SHORT_TERM_SEC_OXY_SENS_TRIM_2_4 = 87; // 0x57 - %
|
||||
const uint8_t LONG_TERM_SEC_OXY_SENS_TRIM_2_4 = 88; // 0x58 - %
|
||||
const uint8_t FUEL_RAIL_ABS_PRESSURE = 89; // 0x59 - kPa
|
||||
const uint8_t RELATIVE_ACCELERATOR_PEDAL_POS = 90; // 0x5A - %
|
||||
const uint8_t HYBRID_BATTERY_REMAINING_LIFE = 91; // 0x5B - %
|
||||
const uint8_t ENGINE_OIL_TEMP = 92; // 0x5C - °C
|
||||
const uint8_t FUEL_INJECTION_TIMING = 93; // 0x5D - °
|
||||
const uint8_t ENGINE_FUEL_RATE = 94; // 0x5E - L/h
|
||||
const uint8_t EMISSION_REQUIREMENTS = 95; // 0x5F - bit encoded
|
||||
|
||||
const uint8_t SUPPORTED_PIDS_61_80 = 96; // 0x60 - bit encoded
|
||||
const uint8_t DEMANDED_ENGINE_PERCENT_TORQUE = 97; // 0x61 - %
|
||||
const uint8_t ACTUAL_ENGINE_TORQUE = 98; // 0x62 - %
|
||||
const uint8_t ENGINE_REFERENCE_TORQUE = 99; // 0x63 - Nm
|
||||
const uint8_t ENGINE_PERCENT_TORQUE_DATA = 100; // 0x64 - %
|
||||
const uint8_t AUX_INPUT_OUTPUT_SUPPORTED = 101; // 0x65 - bit encoded
|
||||
|
||||
const uint8_t ODOMETER = 166; // 0xA6 - km * 10
|
||||
|
||||
const uint8_t SERVICE_01 = 1;
|
||||
const uint8_t SERVICE_02 = 2;
|
||||
const uint8_t SERVICE_03 = 3;
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------//
|
||||
// AT commands (https://www.sparkfun.com/datasheets/Widgets/ELM327_AT_Commands.pdf)
|
||||
//-------------------------------------------------------------------------------------//
|
||||
const char * const DISP_DEVICE_DESCRIPT = "AT @1"; // General
|
||||
const char * const DISP_DEVICE_ID = "AT @2"; // General
|
||||
const char * const STORE_DEVICE_ID = "AT @3 %s"; // General
|
||||
const char * const REPEAT_LAST_COMMAND = "AT \r"; // General
|
||||
const char * const ALLOW_LONG_MESSAGES = "AT AL"; // General
|
||||
const char * const AUTOMATIC_RECEIVE = "AT AR"; // OBD
|
||||
const char * const ADAPTIVE_TIMING_OFF = "AT AT0"; // OBD
|
||||
const char * const ADAPTIVE_TIMING_AUTO_1 = "AT AT1"; // OBD
|
||||
const char * const ADAPTIVE_TIMING_AUTO_2 = "AT AT2"; // OBD
|
||||
const char * const DUMP_BUFFER = "AT BD"; // OBD
|
||||
const char * const BYPASS_INIT_SEQUENCE = "AT BI"; // OBD
|
||||
const char * const TRY_BAUD_DIVISOR = "AT BRD %02d"; // General
|
||||
const char * const SET_HANDSHAKE_TIMEOUT = "AT BRT %02d"; // General
|
||||
const char * const CAN_AUTO_FORMAT_OFF = "AT CAF0"; // CAN
|
||||
const char * const CAN_AUTO_FORMAT_ON = "AT CAF1"; // CAN
|
||||
const char * const CAN_EXTENDED_ADDRESS_OFF = "AT CEA"; // CAN
|
||||
const char * const USE_CAN_EXTENDED_ADDRESS = "AT CEA %02d"; // CAN
|
||||
const char * const SET_ID_FILTER = "AT CF %s"; // CAN
|
||||
const char * const CAN_FLOW_CONTROL_OFF = "AT CFC0"; // CAN
|
||||
const char * const CAN_FLOW_CONTROL_ON = "AT CFC1"; // CAN
|
||||
const char * const SET_ID_MASK = "AT CM %s"; // CAN
|
||||
const char * const SET_CAN_PRIORITY = "AT CP %02d"; // CAN
|
||||
const char * const SHOW_CAN_STATUS = "AT CS"; // CAN
|
||||
const char * const CAN_SILENT_MODE_OFF = "AT CSM0"; // CAN
|
||||
const char * const CAN_SILENT_MODE_ON = "AT CSM1"; // CAN
|
||||
const char * const CALIBRATE_VOLTAGE_CUSTOM = "AT CV %04d"; // Volts
|
||||
const char * const RESTORE_CV_TO_FACTORY = "AT CV 0000"; // Volts
|
||||
const char * const SET_ALL_TO_DEFAULTS = "AT D"; // General
|
||||
const char * const DISP_DLC_OFF = "AT D0"; // CAN
|
||||
const char * const DISP_DLC_ON = "AT D1"; // CAN
|
||||
const char * const MONITOR_FOR_DM1_MESSAGES = "AT DM1"; // J1939
|
||||
const char * const DISP_CURRENT_PROTOCOL = "AT DP"; // OBD
|
||||
const char * const DISP_CURRENT_PROTOCOL_NUM = "AT DPN"; // OBD
|
||||
const char * const ECHO_OFF = "AT E0"; // General
|
||||
const char * const ECHO_ON = "AT E1"; // General
|
||||
const char * const FLOW_CONTROL_SET_DATA_TO = "AT FC SD %s"; // CAN
|
||||
const char * const FLOW_CONTROL_SET_HEAD_TO = "AT FC SH %s"; // CAN
|
||||
const char * const FLOW_CONTROL_SET_MODE_TO = "AT FC SM %c"; // CAN
|
||||
const char * const FORGE_EVENTS = "AT FE"; // General
|
||||
const char * const PERFORM_FAST_INIT = "AT FI"; // ISO
|
||||
const char * const HEADERS_OFF = "AT H0"; // OBD
|
||||
const char * const HEADERS_ON = "AT H1"; // OBD
|
||||
const char * const DISP_ID = "AT I"; // General
|
||||
const char * const SET_ISO_BAUD_10400 = "AT IB 10"; // ISO
|
||||
const char * const SET_ISO_BAUD_4800 = "AT IB 48"; // ISO
|
||||
const char * const SET_ISO_BAUD_9600 = "AT IB 96"; // ISO
|
||||
const char * const IFR_VAL_FROM_HEADER = "AT IFR H"; // J1850
|
||||
const char * const IFR_VAL_FROM_SOURCE = "AT IFR S"; // J1850
|
||||
const char * const IFRS_OFF = "AT IFR0"; // J1850
|
||||
const char * const IFRS_AUTO = "AT IFR1"; // J1850
|
||||
const char * const IFRS_ON = "AT IFR2"; // J1850
|
||||
const char * const IREAD_IGNMON_INPUT_LEVEL = "AT IGN"; // Other
|
||||
const char * const SET_ISO_SLOW_INIT_ADDRESS = "AT IIA %02d"; // ISO
|
||||
const char * const USE_J1939_ELM_DATA_FORMAT = "AT JE"; // J1850
|
||||
const char * const J1939_HEAD_FORMAT_OFF = "AT JHF0"; // J1850
|
||||
const char * const J1939_HEAD_FORMAT_ON = "AT JHF1"; // J1850
|
||||
const char * const USE_J1939_SAE_DATA_FORMAT = "AT JS"; // J1850
|
||||
const char * const SET_J1939_TIMER_X_TO_1X = "AT JTM1"; // J1850
|
||||
const char * const SET_J1939_TIMER_X_TO_5X = "AT JTM5"; // J1850
|
||||
const char * const DISP_KEY_WORDS = "AT KW"; // ISO
|
||||
const char * const KEY_WORD_CHECKING_OFF = "AT KW0"; // ISO
|
||||
const char * const KEY_WORD_CHECKING_ON = "AT KW1"; // ISO
|
||||
const char * const LINEFEEDS_OFF = "AT L0"; // General
|
||||
const char * const LINEFEEDS_ON = "AT L1"; // General
|
||||
const char * const LOW_POWER_MODE = "AT LP"; // General
|
||||
const char * const MEMORY_OFF = "AT M0"; // General
|
||||
const char * const MEMORY_ON = "AT M1"; // General
|
||||
const char * const MONITOR_ALL = "AT MA"; // OBD
|
||||
const char * const MONITOR_FOR_PGN = "AT MP %s"; // J1939
|
||||
const char * const MONITOR_FOR_RECEIVER = "AT MR %02d"; // OBD
|
||||
const char * const MONITOR_FOR_TRANSMITTER = "AT MT %02d"; // OBD
|
||||
const char * const NORMAL_LENGTH_MESSAGES = "AT NL"; // OBD
|
||||
const char * const SET_PROTO_OPTIONS_AND_BAUD = "AT PB %s"; // OBD
|
||||
const char * const PROTOCOL_CLOSE = "AT PC"; // OBD
|
||||
const char * const ALL_PROG_PARAMS_OFF = "AT PP FF OFF";// PPs
|
||||
const char * const ALL_PROG_PARAMS_ON = "AT PP FF ON"; // PPs
|
||||
const char * const SET_PROG_PARAM_OFF = "AT PP %s OFF";// PPs
|
||||
const char * const SET_PROG_PARAM_ON = "AT PP %s ON"; // PPs
|
||||
const char * const SET_PROG_PARAM_VAL = "AT PP %s SV %s"; // PPs
|
||||
const char * const DISP_PP_SUMMARY = "AT PPS"; // PPs
|
||||
const char * const RESPONSES_OFF = "AT R0"; // OBD
|
||||
const char * const RESPONSES_ON = "AT R1"; // OBD
|
||||
const char * const SET_RECEIVE_ADDRESS_TO = "AT RA %02d"; // OBD
|
||||
const char * const READ_STORED_DATA = "AT RD"; // General
|
||||
const char * const SEND_RTR_MESSAGE = "AT RTR"; // CAN
|
||||
const char * const READ_VOLTAGE = "AT RV"; // Volts
|
||||
const char * const PRINTING_SPACES_OFF = "AT S0"; // OBD
|
||||
const char * const PRINTING_SPACES_ON = "AT S1"; // OBD
|
||||
const char * const STORE_DATA_BYTE = "AT SD "; // General
|
||||
const char * const SET_HEADER = "AT SH %s"; // OBD
|
||||
const char * const PERFORM_SLOW_INIT = "AT SI"; // ISO
|
||||
const char * const SET_PROTOCOL_TO_AUTO_H_SAVE = "AT SP A%c"; // OBD
|
||||
const char * const SET_PROTOCOL_TO_H_SAVE = "AT SP %c"; // OBD
|
||||
const char * const SET_PROTOCOL_TO_AUTO_SAVE = "AT SP 00"; // OBD
|
||||
const char * const SET_REC_ADDRESS = "AT SR %02d"; // OBD
|
||||
const char * const SET_STANDARD_SEARCH_ORDER = "AT SS"; // OBD
|
||||
const char * const SET_TIMEOUT_TO_H_X_4MS = "AT ST %02d"; // OBD
|
||||
const char * const SET_WAKEUP_TO_H_X_20MS = "AT SW %02d"; // ISO
|
||||
const char * const SET_TESTER_ADDRESS_TO = "AT TA %02d"; // OBD
|
||||
const char * const TRY_PROT_H_AUTO_SEARCH = "AT TP A%c"; // OBD
|
||||
const char * const TRY_PROT_H = "AT TP %c"; // OBD
|
||||
const char * const VARIABLE_DLC_OFF = "AT V0"; // CAN
|
||||
const char * const VARIABLE_DLC_ON = "AT V1"; // CAN
|
||||
const char * const SET_WAKEUP_MESSAGE = "AT WM"; // ISO
|
||||
const char * const WARM_START = "AT WS"; // General
|
||||
const char * const RESET_ALL = "AT Z"; // General
|
||||
|
||||
|
||||
const uint8_t responseBytes[0xA9] =
|
||||
{
|
||||
4, 4, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2,
|
||||
4, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2,
|
||||
4, 4, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 4, 4, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 1,
|
||||
4, 1, 1, 2, 5, 2, 5, 3, 3, 7, 5, 5, 5, 11, 9, 3, 10, 6, 5, 5, 5, 7, 7, 5, 9, 9, 7, 7, 9, 1, 1, 13,
|
||||
4, 41, 41, 9, 1, 10, 5, 5, 13, 41, 41, 7, 17, 1, 1, 7, 3, 5, 2, 3, 12, 9, 9, 6, 4, 17, 4, 2, 9
|
||||
};
|
||||
|
||||
#endif //ELMulator_DEFINITIONS_H
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,25 @@
|
||||
; JC3636W518 — ESP32-S3R8, 16 MB QIO flash, 8 MB OPI PSRAM, 360x360 ST77916 QSPI + CST816S
|
||||
; Settings mirror the vendor Arduino IDE setup (2-Program Example/Demo_Arduino/docs/Arduino set up.png)
|
||||
|
||||
[env:jc3636w518]
|
||||
; pioarduino = Arduino core 3.0.5 (ESP32_Display_Panel 0.1.4 needs core 3.x)
|
||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||
board = esp32-s3-devkitc-1
|
||||
framework = arduino
|
||||
|
||||
board_build.arduino.memory_type = qio_opi
|
||||
board_build.flash_mode = qio
|
||||
board_upload.flash_size = 16MB
|
||||
board_build.partitions = huge_app.csv
|
||||
|
||||
build_flags =
|
||||
-DBOARD_HAS_PSRAM
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
|
||||
; vendor-bundled libraries (not needed by this sketch, kept for consistency)
|
||||
lib_extra_dirs = ../library
|
||||
|
||||
upload_port = COM21
|
||||
monitor_port = COM21
|
||||
monitor_speed = 115200
|
||||
monitor_filters = esp32_exception_decoder
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
#include <Arduino.h>
|
||||
#include "pincfg.h"
|
||||
|
||||
#include "SD_MMC.h"
|
||||
@@ -40,6 +40,17 @@ Free : GPIO38, GPIO39 (on CN2 header)
|
||||
USB : D-=19 D+=20
|
||||
```
|
||||
|
||||
## PlatformIO setup (preferred build system, since 2026-10-23)
|
||||
- All projects are **PlatformIO projects** (`platformio.ini`, `src/main.cpp`, `include/`). They no longer open in the Arduino IDE.
|
||||
- PlatformIO IDE extension is installed in VS Code. CLI: `%USERPROFILE%\.platformio\penv\Scripts\pio.exe` (Core 6.2.0, not on PATH).
|
||||
- **JC3636W518 (ESP32-S3)**: platform `https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip` (pioarduino = Arduino core 3.0.5; the official PIO espressif32 only has core 2.x, which ESP32_Display_Panel 0.1.4 can't use).
|
||||
Board `esp32-s3-devkitc-1` + `board_build.arduino.memory_type = qio_opi`, `flash_mode = qio`, `board_upload.flash_size = 16MB`, `partitions = huge_app.csv`, `-DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=1`, upload/monitor COM21 @115200.
|
||||
Libraries: `lib_extra_dirs` → vendor `2-Program Example/Demo_Arduino/library`.
|
||||
**LVGL config:** `-DLV_CONF_INCLUDE_SIMPLE -Iinclude` — the `-Iinclude` is required, otherwise the LVGL library compiles without the project's `lv_conf.h` (symptom: `undefined reference to lv_gif_create`).
|
||||
- **Classic ESP32** projects (bridge, ELMulator): `espressif32@6.10.0` (core 2.0.17), board `esp32dev`, `huge_app.csv`.
|
||||
- Template for a new JC3636W518 project: copy `10-OBD-Display-2024/display_speed_wifi/platformio.ini` (+ `include/pincfg.h`, `include/scr_st77916.h`, `include/lv_conf.h`).
|
||||
- When converting `.ino` → `main.cpp`: add `#include <Arduino.h>` and prototypes for functions used before their definition; don't `#include` `.c` files that are also in `src/`.
|
||||
|
||||
## Software stack used by the vendor demo
|
||||
- **Arduino IDE** + **arduino-esp32 core ≥ 3.0.1** (core **3.0.5 already installed** on this PC under `%LOCALAPPDATA%\Arduino15`).
|
||||
- Libraries (bundled in `2-Program Example/Demo_Arduino/library/`):
|
||||
|
||||
Reference in new issue
Block a user