summaryrefslogtreecommitdiff
path: root/libraries/OBD
diff options
context:
space:
mode:
authorStanley Huang <stanleyhuangyc@gmail.com>2013-03-25 00:57:28 +0800
committerStanley Huang <stanleyhuangyc@gmail.com>2013-03-25 00:57:28 +0800
commit8e57c683d3d16f1dee320f10c411602ea38ba651 (patch)
tree75df3040d13c3f1850d93e29eb93a3836cae133c /libraries/OBD
download2021-arduino-obd-8e57c683d3d16f1dee320f10c411602ea38ba651.tar.gz
2021-arduino-obd-8e57c683d3d16f1dee320f10c411602ea38ba651.tar.bz2
2021-arduino-obd-8e57c683d3d16f1dee320f10c411602ea38ba651.zip
initial commit
Diffstat (limited to 'libraries/OBD')
-rw-r--r--libraries/OBD/OBD.cpp269
-rw-r--r--libraries/OBD/OBD.h84
2 files changed, 353 insertions, 0 deletions
diff --git a/libraries/OBD/OBD.cpp b/libraries/OBD/OBD.cpp
new file mode 100644
index 0000000..b3da34e
--- /dev/null
+++ b/libraries/OBD/OBD.cpp
@@ -0,0 +1,269 @@
+/*************************************************************************
+* OBD-II (ELM327) data accessing library for Arduino
+* Distributed under GPL v2.0
+* Copyright (c) 2012 Stanley Huang <stanleyhuangyc@gmail.com>
+* All rights reserved.
+*************************************************************************/
+
+#include <Arduino.h>
+#include <avr/pgmspace.h>
+#include "OBD.h"
+
+#define INIT_CMD_COUNT 7
+#define MAX_CMD_LEN 6
+
+const char PROGMEM s_initcmd[INIT_CMD_COUNT][MAX_CMD_LEN] = {"ATZ\r","ATE0\r","ATL1\r","ATI\r","0100\r","0120\r","0140\r"};
+const char PROGMEM s_searching[] = "SEARCHING";
+const char PROGMEM s_cmd_fmt[] = "%02X%02X 1\r";
+const char PROGMEM s_cmd_sleep[MAX_CMD_LEN] = "atlp\r";
+const char PROGMEM s_response_begin[] = "41 ";
+
+unsigned int hex2uint16(const char *p)
+{
+ char c = *p;
+ unsigned int i = 0;
+ for (char n = 0; c && n < 4; c = *(++p)) {
+ if (c >= 'A' && c <= 'F') {
+ c -= 7;
+ } else if (c>='a' && c<='f') {
+ c -= 39;
+ } else if (c == ' ') {
+ continue;
+ } else if (c < '0' || c > '9') {
+ break;
+ }
+ i = (i << 4) | (c & 0xF);
+ n++;
+ }
+ return i;
+}
+
+unsigned char hex2uint8(const char *p)
+{
+ unsigned char c1 = *p;
+ unsigned char c2 = *(p + 1);
+ if (c1 >= 'A' && c1 <= 'F')
+ c1 -= 7;
+ else if (c1 >='a' && c1 <= 'f')
+ c1 -= 39;
+ else if (c1 < '0' || c1 > '9')
+ return 0;
+
+ if (c2 >= 'A' && c2 <= 'F')
+ c2 -= 7;
+ else if (c2 >= 'a' && c2 <= 'f')
+ c2 -= 39;
+ else if (c2 < '0' || c2 > '9')
+ return 0;
+
+ return c1 << 4 | (c2 & 0xf);
+}
+
+void COBD::Query(unsigned char pid)
+{
+ char cmd[8];
+ sprintf_P(cmd, s_cmd_fmt, dataMode, pid);
+ WriteData(cmd);
+}
+
+bool COBD::ReadSensor(byte pid, int& result, bool passive)
+{
+ if (passive) {
+ bool hasData;
+ unsigned long tick = millis();
+ while (!(hasData = DataAvailable()) && millis() - tick < OBD_TIMEOUT_SHORT);
+ if (!hasData) {
+ errors++;
+ return false;
+ }
+ } else {
+ Query(pid);
+ }
+ return GetResponse(pid, result);
+}
+
+bool COBD::DataAvailable()
+{
+ return OBDUART.available();
+}
+
+char COBD::ReadData()
+{
+ return OBDUART.read();
+}
+
+void COBD::WriteData(const char* s)
+{
+ OBDUART.write(s);
+}
+
+void COBD::WriteData(const char c)
+{
+ OBDUART.write(c);
+}
+
+char* COBD::GetResponse(byte pid, char* buffer)
+{
+ unsigned long startTime = millis();
+ byte i = 0;
+
+ for (;;) {
+ if (DataAvailable()) {
+ char c = ReadData();
+ buffer[i] = c;
+ if (++i == OBD_RECV_BUF_SIZE - 1) {
+ // buffer overflow
+ break;
+ }
+ if (c == '>' && i > 6) {
+ // prompt char reached
+ break;
+ }
+ } else {
+ buffer[i] = 0;
+ unsigned int timeout;
+ if (dataMode != 1 || strstr_P(buffer, s_searching)) {
+ timeout = OBD_TIMEOUT_LONG;
+ } else {
+ timeout = OBD_TIMEOUT_SHORT;
+ }
+ if (millis() - startTime > timeout) {
+ // timeout
+ errors++;
+ break;
+ }
+ }
+ }
+ buffer[i] = 0;
+
+ char *p = buffer;
+ while ((p = strstr_P(p, s_response_begin))) {
+ p += 3;
+ if (pid == 0 || hex2uint8(p) == pid) {
+ errors = 0;
+ p += 2;
+ if (*p == ' ')
+ return p + 1;
+ }
+ }
+ return 0;
+}
+
+bool COBD::GetParsedData(byte pid, char* data, int& result)
+{
+ switch (pid) {
+ case PID_RPM:
+ result = GetLargeValue(data) >> 2;
+ break;
+ case PID_FUEL_PRESSURE:
+ result = GetSmallValue(data) * 3;
+ break;
+ case PID_COOLANT_TEMP:
+ case PID_INTAKE_TEMP:
+ case PID_AMBIENT_TEMP:
+ result = GetTemperatureValue(data);
+ break;
+ case PID_ABS_ENGINE_LOAD:
+ result = GetLargeValue(data) * 100 / 255;
+ break;
+ case PID_MAF_FLOW:
+ result = GetLargeValue(data) / 100;
+ break;
+ case PID_THROTTLE:
+ case PID_ENGINE_LOAD:
+ case PID_FUEL_LEVEL:
+ result = GetPercentageValue(data);
+ break;
+ case PID_SPEED:
+ case PID_BAROMETRIC:
+ case PID_INTAKE_PRESSURE:
+ result = GetSmallValue(data);
+ break;
+ case PID_TIMING_ADVANCE:
+ result = (GetSmallValue(data) - 128) >> 1;
+ break;
+ case PID_DISTANCE:
+ case PID_RUNTIME:
+ result = GetLargeValue(data);
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool COBD::GetResponse(byte pid, int& result)
+{
+ char buffer[OBD_RECV_BUF_SIZE];
+ char* data = GetResponse(pid, buffer);
+ if (!data) {
+ // try recover next time
+ WriteData('\r');
+ return false;
+ }
+ return GetParsedData(pid, data, result);
+}
+
+bool COBD::GetResponsePassive(byte& pid, int& result)
+{
+ char buffer[OBD_RECV_BUF_SIZE];
+ char* data = GetResponse(0, buffer);
+ if (!data) {
+ // try recover next time
+ return false;
+ }
+ pid = hex2uint8(data - 3);
+ return GetParsedData(pid, data, result);
+}
+
+void COBD::Sleep(int seconds)
+{
+ char cmd[MAX_CMD_LEN];
+ strcpy_P(cmd, s_cmd_sleep);
+ WriteData(cmd);
+ if (seconds) {
+ delay((unsigned long)seconds << 10);
+ WriteData('\r');
+ }
+}
+
+bool COBD::Init(bool passive)
+{
+ unsigned long currentMillis;
+ unsigned char n;
+ char prompted;
+ char buffer[OBD_RECV_BUF_SIZE];
+
+ for (unsigned char i = 0; i < INIT_CMD_COUNT; i++) {
+ if (!passive) {
+ char cmd[MAX_CMD_LEN];
+ strcpy_P(cmd, s_initcmd[i]);
+ WriteData(cmd);
+ }
+ n = 0;
+ prompted = 0;
+ currentMillis = millis();
+ for (;;) {
+ if (DataAvailable()) {
+ char c = ReadData();
+ if (c == '>') {
+ buffer[n] = 0;
+ prompted++;
+ } else if (n < OBD_RECV_BUF_SIZE - 1) {
+ buffer[n++] = c;
+ }
+ } else if (prompted) {
+ break;
+ } else {
+ unsigned long elapsed = millis() - currentMillis;
+ if (elapsed > OBD_TIMEOUT_INIT) {
+ // init timeout
+ //WriteData("\r");
+ return false;
+ }
+ }
+ }
+ }
+ errors = 0;
+ return true;
+}
diff --git a/libraries/OBD/OBD.h b/libraries/OBD/OBD.h
new file mode 100644
index 0000000..1247c49
--- /dev/null
+++ b/libraries/OBD/OBD.h
@@ -0,0 +1,84 @@
+/*************************************************************************
+* OBD-II (ELM327) data accessing library for Arduino
+* Distributed under GPL v2.0
+* Copyright (c) 2012 Stanley Huang <stanleyhuangyc@gmail.com>
+* All rights reserved.
+*************************************************************************/
+
+#define OBD_TIMEOUT_SHORT 2000 /* ms */
+#define OBD_TIMEOUT_LONG 7000 /* ms */
+#define OBD_TIMEOUT_INIT 3000 /* ms */
+#define OBD_SERIAL_BAUDRATE 38400
+#define OBD_RECV_BUF_SIZE 48
+
+#ifndef OBDUART
+#ifdef __AVR_ATmega32U4__ /* for Leonardo */
+#define OBDUART Serial1
+#else
+#define OBDUART Serial
+#endif
+#endif
+
+// mode 0 pids
+#define PID_RPM 0x0C
+#define PID_SPEED 0x0D
+#define PID_THROTTLE 0x11
+#define PID_ENGINE_LOAD 0x04
+#define PID_COOLANT_TEMP 0x05
+#define PID_INTAKE_TEMP 0x0F
+#define PID_MAF_FLOW 0x10
+#define PID_ABS_ENGINE_LOAD 0x43
+#define PID_AMBIENT_TEMP 0x46
+#define PID_FUEL_PRESSURE 0x0A
+#define PID_INTAKE_PRESSURE 0x0B
+#define PID_BAROMETRIC 0x33
+#define PID_TIMING_ADVANCE 0x0E
+#define PID_FUEL_LEVEL 0x2F
+#define PID_RUNTIME 0x1F
+#define PID_DISTANCE 0x31
+
+unsigned int hex2uint16(const char *p);
+unsigned char hex2uint8(const char *p);
+
+class COBD
+{
+public:
+ COBD()
+ {
+ dataMode = 1;
+ errors = 0;
+ }
+ bool Init(bool passive = false);
+ bool ReadSensor(byte pid, int& result, bool passive = false);
+ void Sleep(int seconds);
+ // Query and GetResponse for advanced usage only
+ void Query(byte pid);
+ virtual char* GetResponse(byte pid, char* buffer);
+ virtual bool GetResponse(byte pid, int& result);
+ virtual bool GetResponsePassive(byte& pid, int& result);
+ virtual bool DataAvailable();
+ byte dataMode;
+ byte errors;
+ //char recvBuf[OBD_RECV_BUF_SIZE];
+protected:
+ static bool GetParsedData(byte pid, char* data, int& result);
+ static int GetPercentageValue(char* data)
+ {
+ return (int)hex2uint8(data) * 100 / 255;
+ }
+ static int GetLargeValue(char* data)
+ {
+ return hex2uint16(data);
+ }
+ static int GetSmallValue(char* data)
+ {
+ return hex2uint8(data);
+ }
+ static int GetTemperatureValue(char* data)
+ {
+ return (int)hex2uint8(data) - 40;
+ }
+ virtual char ReadData();
+ virtual void WriteData(const char* s);
+ virtual void WriteData(const char c);
+};