├── OBD.cpp ├── OBD.h ├── README.md └── examples └── OBD_II └── OBD_II.ino /OBD.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * Arduino Library for OBD-II UART/I2C Adapter 3 | * Distributed under GPL v2.0 4 | * Visit http://freematics.com for more information 5 | * (C)2012-2014 Stanley Huang 6 | *************************************************************************/ 7 | 8 | #include "OBD.h" 9 | 10 | //#define DEBUG Serial 11 | 12 | uint16_t hex2uint16(const char *p) 13 | { 14 | char c = *p; 15 | uint16_t i = 0; 16 | for (char n = 0; c && n < 4; c = *(++p)) { 17 | if (c >= 'A' && c <= 'F') { 18 | c -= 7; 19 | } else if (c >= 'a' && c <= 'f') { 20 | c -= 39; 21 | } else if (c == ' ') { 22 | continue; 23 | } else if (c < '0' || c > '9') { 24 | break; 25 | } 26 | i = (i << 4) | (c & 0xF); 27 | n++; 28 | } 29 | return i; 30 | } 31 | 32 | byte hex2uint8(const char *p) 33 | { 34 | byte c1 = *p; 35 | byte c2 = *(p + 1); 36 | if (c1 >= 'A' && c1 <= 'F') 37 | c1 -= 7; 38 | else if (c1 >= 'a' && c1 <= 'f') 39 | c1 -= 39; 40 | else if (c1 < '0' || c1 > '9') 41 | return 0; 42 | 43 | if (c2 >= 'A' && c2 <= 'F') 44 | c2 -= 7; 45 | else if (c2 >= 'a' && c2 <= 'f') 46 | c2 -= 39; 47 | else if (c2 < '0' || c2 > '9') 48 | return 0; 49 | 50 | return c1 << 4 | (c2 & 0xf); 51 | } 52 | 53 | /************************************************************************* 54 | * OBD-II UART Adapter 55 | *************************************************************************/ 56 | 57 | void COBD::sendQuery(byte pid) 58 | { 59 | char cmd[8]; 60 | sprintf(cmd, "%02X%02X\r", dataMode, pid); 61 | #ifdef DEBUG 62 | debugOutput(cmd); 63 | #endif 64 | write(cmd); 65 | } 66 | 67 | bool COBD::read(byte pid, int& result) 68 | { 69 | // send a query command 70 | sendQuery(pid); 71 | // receive and parse the response 72 | return getResult(pid, result); 73 | } 74 | 75 | void COBD::clearDTC() 76 | { 77 | write("04\r"); 78 | receive(0, 1000); 79 | } 80 | 81 | bool COBD::available() 82 | { 83 | return OBDUART.available(); 84 | } 85 | 86 | char COBD::read() 87 | { 88 | char c = OBDUART.read(); 89 | #ifdef DEBUG 90 | DEBUG.write(c); 91 | #endif 92 | return c; 93 | } 94 | 95 | void COBD::write(const char* s) 96 | { 97 | OBDUART.write(s); 98 | } 99 | 100 | void COBD::write(char c) 101 | { 102 | OBDUART.write(c); 103 | } 104 | 105 | int COBD::normalizeData(byte pid, char* data) 106 | { 107 | int result; 108 | switch (pid) { 109 | case PID_RPM: 110 | case PID_EVAP_SYS_VAPOR_PRESSURE: 111 | result = getLargeValue(data) >> 2; 112 | break; 113 | case PID_FUEL_PRESSURE: 114 | result = getSmallValue(data) * 3; 115 | break; 116 | case PID_COOLANT_TEMP: 117 | case PID_INTAKE_TEMP: 118 | case PID_AMBIENT_TEMP: 119 | case PID_ENGINE_OIL_TEMP: 120 | result = getTemperatureValue(data); 121 | break; 122 | case PID_THROTTLE: 123 | case PID_COMMANDED_EGR: 124 | case PID_COMMANDED_EVAPORATIVE_PURGE: 125 | case PID_FUEL_LEVEL: 126 | case PID_RELATIVE_THROTTLE_POS: 127 | case PID_ABSOLUTE_THROTTLE_POS_B: 128 | case PID_ABSOLUTE_THROTTLE_POS_C: 129 | case PID_ACC_PEDAL_POS_D: 130 | case PID_ACC_PEDAL_POS_E: 131 | case PID_ACC_PEDAL_POS_F: 132 | case PID_COMMANDED_THROTTLE_ACTUATOR: 133 | case PID_ENGINE_LOAD: 134 | case PID_ABSOLUTE_ENGINE_LOAD: 135 | case PID_ETHANOL_FUEL: 136 | case PID_HYBRID_BATTERY_PERCENTAGE: 137 | result = getPercentageValue(data); 138 | break; 139 | case PID_MAF_FLOW: 140 | result = getLargeValue(data) / 100; 141 | break; 142 | case PID_TIMING_ADVANCE: 143 | result = (int)(getSmallValue(data) / 2) - 64; 144 | break; 145 | case PID_DISTANCE: // km 146 | case PID_DISTANCE_WITH_MIL: // km 147 | case PID_TIME_WITH_MIL: // minute 148 | case PID_TIME_SINCE_CODES_CLEARED: // minute 149 | case PID_RUNTIME: // second 150 | case PID_FUEL_RAIL_PRESSURE: // kPa 151 | case PID_ENGINE_REF_TORQUE: // Nm 152 | result = getLargeValue(data); 153 | break; 154 | case PID_CONTROL_MODULE_VOLTAGE: // V 155 | result = getLargeValue(data) / 1000; 156 | break; 157 | case PID_ENGINE_FUEL_RATE: // L/h 158 | result = getLargeValue(data) / 20; 159 | break; 160 | case PID_ENGINE_TORQUE_DEMANDED: // % 161 | case PID_ENGINE_TORQUE_PERCENTAGE: // % 162 | result = (int)getSmallValue(data) - 125; 163 | break; 164 | case PID_SHORT_TERM_FUEL_TRIM_1: 165 | case PID_LONG_TERM_FUEL_TRIM_1: 166 | case PID_SHORT_TERM_FUEL_TRIM_2: 167 | case PID_LONG_TERM_FUEL_TRIM_2: 168 | case PID_EGR_ERROR: 169 | result = ((int)getSmallValue(data) - 128) * 100 / 128; 170 | break; 171 | case PID_FUEL_INJECTION_TIMING: 172 | result = ((int32_t)getLargeValue(data) - 26880) / 128; 173 | break; 174 | case PID_CATALYST_TEMP_B1S1: 175 | case PID_CATALYST_TEMP_B2S1: 176 | case PID_CATALYST_TEMP_B1S2: 177 | case PID_CATALYST_TEMP_B2S2: 178 | result = getLargeValue(data) / 10 - 40; 179 | break; 180 | default: 181 | result = getSmallValue(data); 182 | } 183 | return result; 184 | } 185 | 186 | char* COBD::getResponse(byte& pid, char* buffer) 187 | { 188 | while (receive(buffer, OBD_TIMEOUT_SHORT) > 0) { 189 | char *p = buffer; 190 | while ((p = strstr(p, "41 "))) { 191 | p += 3; 192 | byte curpid = hex2uint8(p); 193 | if (pid == 0) pid = curpid; 194 | if (curpid == pid) { 195 | errors = 0; 196 | p += 2; 197 | if (*p == ' ') 198 | return p + 1; 199 | } 200 | } 201 | } 202 | return 0; 203 | } 204 | 205 | bool COBD::getResult(byte& pid, int& result) 206 | { 207 | char buffer[OBD_RECV_BUF_SIZE]; 208 | char* data = getResponse(pid, buffer); 209 | if (!data) { 210 | recover(); 211 | errors++; 212 | return false; 213 | } 214 | result = normalizeData(pid, data); 215 | return true; 216 | } 217 | 218 | bool COBD::setProtocol(OBD_PROTOCOLS h) 219 | { 220 | char buf[OBD_RECV_BUF_SIZE]; 221 | if (h == PROTO_AUTO) { 222 | write("ATSP00\r"); 223 | } else { 224 | sprintf(buf, "ATSP%d\r", h); 225 | write(buf); 226 | } 227 | if (receive(buf, 3000) > 0 && strstr(buf, "OK")) 228 | return true; 229 | else 230 | return false; 231 | } 232 | 233 | void COBD::sleep() 234 | { 235 | write("ATLP\r"); 236 | receive(); 237 | } 238 | 239 | void COBD::wakeup() 240 | { 241 | write('\r'); 242 | receive(); 243 | } 244 | 245 | int COBD::getVoltage() 246 | { 247 | char buf[OBD_RECV_BUF_SIZE]; 248 | write("ATRV\r"); 249 | byte n = receive(buf, 100); 250 | if (n > 0) { 251 | for (byte i = 0; i < n; i++) { 252 | if (buf[i] >= '0' && buf[i] <= '9') { 253 | int v1 = atoi(buf); 254 | int v2 = 0; 255 | char *p = strchr(buf, '.'); 256 | if (p++) { 257 | if (*p >= '0' && *p <= '9') { 258 | v2 = *p - '0'; 259 | } 260 | } 261 | return v1 * 10 + v2; 262 | } 263 | } 264 | } 265 | return -1; 266 | } 267 | 268 | bool COBD::isValidPID(byte pid) 269 | { 270 | if (pid >= 0x7f) 271 | return true; 272 | pid--; 273 | byte i = pid >> 3; 274 | byte b = 0x80 >> (pid & 0x7); 275 | return pidmap[i] & b; 276 | } 277 | 278 | void COBD::begin() 279 | { 280 | OBDUART.begin(OBD_SERIAL_BAUDRATE); 281 | #ifdef DEBUG 282 | DEBUG.begin(115200); 283 | #endif 284 | } 285 | 286 | byte COBD::receive(char* buffer, int timeout) 287 | { 288 | unsigned char n = 0; 289 | for (unsigned long startTime = millis();;) { 290 | if (available()) { 291 | char c = read(); 292 | if (n > 2 && c == '>') { 293 | // prompt char received 294 | break; 295 | } else if (!buffer) { 296 | n++; 297 | } else if (n < OBD_RECV_BUF_SIZE - 1) { 298 | if (c == '.' && n > 2 && buffer[n - 1] == '.' && buffer[n - 2] == '.') { 299 | n = 0; 300 | timeout = OBD_TIMEOUT_LONG; 301 | } else { 302 | buffer[n++] = c; 303 | } 304 | } 305 | } else { 306 | if (millis() - startTime > timeout) { 307 | // timeout 308 | return 0; 309 | } 310 | dataIdleLoop(); 311 | } 312 | } 313 | if (buffer) buffer[n] = 0; 314 | return n; 315 | } 316 | 317 | // async: 318 | void COBD::asyncRequest(byte pid) 319 | { 320 | if (aInProgress) 321 | { 322 | while(this->available()) // need to complete previous request 323 | this->read(); 324 | } 325 | aInProgress = true; 326 | aPid = pid; 327 | aTimestamp = millis(); 328 | aBufferN = 0; 329 | sendQuery(pid); 330 | } 331 | 332 | bool COBD::asyncInProgress() 333 | { 334 | return aInProgress; 335 | } 336 | 337 | bool COBD::asyncGet(int& result) 338 | { 339 | if (!aInProgress) 340 | return false; 341 | 342 | while(this->available()) 343 | { 344 | char c = this->read(); 345 | 346 | if ((aBufferN > 2) && (c == '>')) // prompt char received 347 | { // all data received, filter buffer: 348 | aBuffer[aBufferN] = 0; 349 | char *p = aBuffer; 350 | while ((p = strstr(p, "41 "))) // common prefix 351 | { 352 | p += 3; 353 | byte pid = hex2uint8(p); // 2 hex digits of PID 354 | if (pid == aPid) 355 | { 356 | p += 2; 357 | if (*p == ' ') 358 | { 359 | p++; 360 | break; 361 | } 362 | } 363 | } 364 | result = normalizeData(aPid, p); 365 | aInProgress = false; 366 | return true; 367 | } 368 | else if (aBufferN < (OBD_RECV_BUF_SIZE - 1)) 369 | { 370 | if ((c == '.') && (aBufferN > 2) && (aBuffer[aBufferN - 1] == '.') && (aBuffer[aBufferN - 2] == '.')) 371 | aBufferN = 0; 372 | else 373 | { 374 | aBuffer[aBufferN] = c; 375 | aBufferN++; 376 | } 377 | } 378 | } 379 | return false; 380 | } 381 | 382 | int COBD::asyncDuration() 383 | { 384 | if (!aInProgress) 385 | return 0; 386 | return millis() - aTimestamp; 387 | } 388 | 389 | 390 | void COBD::recover() 391 | { 392 | write('\r'); 393 | delay(100); 394 | while (available()) 395 | read(); 396 | } 397 | 398 | bool COBD::init(OBD_PROTOCOLS protocol) 399 | { 400 | const char *initcmd[] = {"ATZ\r", "ATE0\r", "ATL1\r"}; 401 | char buffer[OBD_RECV_BUF_SIZE]; 402 | 403 | m_state = OBD_CONNECTING; 404 | recover(); 405 | 406 | for (unsigned char i = 0; i < sizeof(initcmd) / sizeof(initcmd[0]); i++) { 407 | #ifdef DEBUG 408 | debugOutput(initcmd[i]); 409 | #endif 410 | write(initcmd[i]); 411 | if (receive(buffer) == 0) { 412 | m_state = OBD_DISCONNECTED; 413 | return false; 414 | } 415 | delay(50); 416 | } 417 | while (available()) 418 | read(); 419 | 420 | if (protocol != PROTO_AUTO) { 421 | setProtocol(protocol); 422 | } 423 | int value; 424 | if (!read(PID_RPM, value)) { 425 | m_state = OBD_DISCONNECTED; 426 | return false; 427 | } 428 | 429 | // load pid map 430 | memset(pidmap, 0, sizeof(pidmap)); 431 | for (byte i = 0; i < 4; i++) { 432 | byte pid = i * 0x20; 433 | sendQuery(pid); 434 | char* data = getResponse(pid, buffer); 435 | if (!data) 436 | break; 437 | data--; 438 | for (byte n = 0; n < 4; n++) { 439 | if (data[n * 3] != ' ') 440 | break; 441 | pidmap[i * 4 + n] = hex2uint8(data + n * 3 + 1); 442 | } 443 | delay(100); 444 | } 445 | while (available()) 446 | read(); 447 | 448 | m_state = OBD_CONNECTED; 449 | errors = 0; 450 | return true; 451 | } 452 | 453 | void COBD::end() 454 | { 455 | m_state = OBD_DISCONNECTED; 456 | OBDUART.end(); 457 | } 458 | 459 | void COBD::setBaudRate(long baudrate) 460 | { 461 | OBDUART.print("ATBR1 "); 462 | OBDUART.print(baudrate); 463 | OBDUART.print('\r'); 464 | OBDUART.end(); 465 | delay(100); 466 | OBDUART.begin(baudrate); 467 | } 468 | 469 | #ifdef DEBUG 470 | void COBD::debugOutput(const char *s) 471 | { 472 | DEBUG.print('['); 473 | DEBUG.print(millis()); 474 | DEBUG.print(']'); 475 | DEBUG.print(s); 476 | } 477 | #endif 478 | 479 | /************************************************************************* 480 | * OBD-II I2C Adapter 481 | *************************************************************************/ 482 | /* 483 | #if 0 484 | #include 485 | 486 | void COBDI2C::begin() 487 | { 488 | Wire.begin(); 489 | memset(obdPid, 0, sizeof(obdPid)); 490 | memset(obdInfo, 0, sizeof(obdInfo)); 491 | #ifdef DEBUG 492 | DEBUG.begin(115200); 493 | #endif 494 | } 495 | 496 | void COBDI2C::end() 497 | { 498 | m_state = OBD_DISCONNECTED; 499 | } 500 | 501 | bool COBDI2C::init(OBD_PROTOCOLS protocol) 502 | { 503 | bool success = false; 504 | m_state = OBD_CONNECTING; 505 | sendCommand(CMD_QUERY_STATUS); 506 | 507 | char recvbuf[MAX_PAYLOAD_SIZE]; 508 | for (byte n = 0; n < 3; n++) { 509 | memset(recvbuf, 0, sizeof(recvbuf)); 510 | receive(recvbuf); 511 | if (!memcmp(recvbuf, "OBD ", 4)) 512 | break; 513 | } 514 | if (recvbuf[4] == 'Y') { 515 | memcpy(pidmap, recvbuf + 16, sizeof(pidmap)); 516 | if (protocol != PROTO_AUTO) { 517 | setProtocol(protocol); 518 | } 519 | int value; 520 | success = read(PID_RPM, value); 521 | } 522 | if (success) { 523 | return true; 524 | m_state = OBD_CONNECTED; 525 | } else { 526 | m_state = OBD_DISCONNECTED; 527 | return false; 528 | } 529 | } 530 | 531 | bool COBDI2C::read(byte pid, int& result) 532 | { 533 | sendQuery(pid); 534 | dataIdleLoop(); 535 | return getResult(pid, result); 536 | } 537 | 538 | void COBDI2C::write(const char* s) 539 | { 540 | COMMAND_BLOCK cmdblock = {millis(), CMD_SEND_AT_COMMAND}; 541 | Wire.beginTransmission(I2C_ADDR); 542 | Wire.write((byte*)&cmdblock, sizeof(cmdblock)); 543 | Wire.write(s); 544 | Wire.endTransmission(); 545 | } 546 | 547 | bool COBDI2C::sendCommand(byte cmd, uint8_t data, byte* payload, byte payloadBytes) 548 | { 549 | COMMAND_BLOCK cmdblock = {millis(), cmd, data}; 550 | Wire.beginTransmission(I2C_ADDR); 551 | bool success = Wire.write((byte*)&cmdblock, sizeof(COMMAND_BLOCK)) == sizeof(COMMAND_BLOCK); 552 | if (payload) Wire.write(payload, payloadBytes); 553 | Wire.endTransmission(); 554 | return success; 555 | } 556 | 557 | byte COBDI2C::receive(char* buffer, int timeout) 558 | { 559 | uint32_t start = millis(); 560 | byte offset = 0; 561 | do { 562 | Wire.requestFrom((byte)I2C_ADDR, (byte)MAX_PAYLOAD_SIZE, (byte)1); 563 | 564 | bool hasEnd = false; 565 | for (byte i = 0; i < MAX_PAYLOAD_SIZE && Wire.available(); i++) { 566 | char c = Wire.read(); 567 | buffer[offset + i] = c; 568 | if (c == 0) 569 | hasEnd = true; 570 | } 571 | 572 | if (buffer[0] == 0) { 573 | // data not ready 574 | dataIdleLoop(); 575 | continue; 576 | } 577 | 578 | offset += MAX_PAYLOAD_SIZE; 579 | if (!hasEnd) { 580 | continue; 581 | } 582 | 583 | return offset; 584 | } while(millis() - start < OBD_TIMEOUT_LONG); 585 | return 0; 586 | } 587 | 588 | bool COBDI2C::gpsQuery(GPS_DATA* gpsdata) 589 | { 590 | if (!sendCommand(CMD_GPS_QUERY, 0)) return false; 591 | Wire.requestFrom((byte)I2C_ADDR, (byte)MAX_PAYLOAD_SIZE, (byte)1); 592 | Wire.readBytes((char*)gpsdata, MAX_PAYLOAD_SIZE); 593 | return true; 594 | } 595 | 596 | void COBDI2C::gpsSetup(uint32_t baudrate, const char* cmds) 597 | { 598 | sendCommand(CMD_GPS_SETUP, baudrate / 1200, (byte*)cmds, cmds ? strlen(cmds) : 0); 599 | } 600 | 601 | void COBDI2C::setPID(byte pid) 602 | { 603 | byte n = 0; 604 | for (; n < MAX_PIDS && obdPid[n]; n++) { 605 | if (obdPid[n] == pid) 606 | return; 607 | } 608 | if (n == MAX_PIDS) { 609 | memmove(obdPid, obdPid + 1, sizeof(obdPid[0]) * (MAX_PIDS - 1)); 610 | n = MAX_PIDS - 1; 611 | } 612 | obdPid[n] = pid; 613 | } 614 | 615 | void COBDI2C::applyPIDs() 616 | { 617 | sendCommand(CMD_APPLY_OBD_PIDS, 0, (byte*)obdPid, sizeof(obdPid)); 618 | delay(200); 619 | } 620 | 621 | void COBDI2C::loadData() 622 | { 623 | sendCommand(CMD_LOAD_OBD_DATA); 624 | dataIdleLoop(); 625 | Wire.requestFrom((byte)I2C_ADDR, (byte)MAX_PAYLOAD_SIZE, (byte)0); 626 | Wire.readBytes((char*)obdInfo, MAX_PAYLOAD_SIZE); 627 | } 628 | #endif 629 | */ 630 | -------------------------------------------------------------------------------- /OBD.h: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * Arduino Library for OBD-II UART/I2C Adapter 3 | * Distributed under GPL v2.0 4 | * Visit http://freematics.com for more information 5 | * (C)2012-2014 Stanley Huang 6 | *************************************************************************/ 7 | 8 | #include 9 | 10 | #define OBD_MODEL_UART 0 11 | #define OBD_MODEL_I2C 1 12 | 13 | #define OBD_TIMEOUT_SHORT 2000 /* ms */ 14 | #define OBD_TIMEOUT_LONG 7000 /* ms */ 15 | #define OBD_SERIAL_BAUDRATE 38400 16 | #define OBD_RECV_BUF_SIZE 128 17 | 18 | #ifndef OBDUART 19 | #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168P__) 20 | #define OBDUART Serial 21 | #else 22 | #define OBDUART Serial1 23 | #endif 24 | #endif 25 | 26 | // Mode 1 PIDs 27 | #define PID_ENGINE_LOAD 0x04 28 | #define PID_COOLANT_TEMP 0x05 29 | #define PID_SHORT_TERM_FUEL_TRIM_1 0x06 30 | #define PID_LONG_TERM_FUEL_TRIM_1 0x07 31 | #define PID_SHORT_TERM_FUEL_TRIM_2 0x08 32 | #define PID_LONG_TERM_FUEL_TRIM_2 0x09 33 | #define PID_FUEL_PRESSURE 0x0A 34 | #define PID_INTAKE_MAP 0x0B 35 | #define PID_RPM 0x0C 36 | #define PID_SPEED 0x0D 37 | #define PID_TIMING_ADVANCE 0x0E 38 | #define PID_INTAKE_TEMP 0x0F 39 | #define PID_MAF_FLOW 0x10 40 | #define PID_THROTTLE 0x11 41 | #define PID_AUX_INPUT 0x1E 42 | #define PID_RUNTIME 0x1F 43 | #define PID_DISTANCE_WITH_MIL 0x21 44 | #define PID_COMMANDED_EGR 0x2C 45 | #define PID_EGR_ERROR 0x2D 46 | #define PID_COMMANDED_EVAPORATIVE_PURGE 0x2E 47 | #define PID_FUEL_LEVEL 0x2F 48 | #define PID_WARMS_UPS 0x30 49 | #define PID_DISTANCE 0x31 50 | #define PID_EVAP_SYS_VAPOR_PRESSURE 0x32 51 | #define PID_BAROMETRIC 0x33 52 | #define PID_CATALYST_TEMP_B1S1 0x3C 53 | #define PID_CATALYST_TEMP_B2S1 0x3D 54 | #define PID_CATALYST_TEMP_B1S2 0x3E 55 | #define PID_CATALYST_TEMP_B2S2 0x3F 56 | #define PID_CONTROL_MODULE_VOLTAGE 0x42 57 | #define PID_ABSOLUTE_ENGINE_LOAD 0x43 58 | #define PID_RELATIVE_THROTTLE_POS 0x45 59 | #define PID_AMBIENT_TEMP 0x46 60 | #define PID_ABSOLUTE_THROTTLE_POS_B 0x47 61 | #define PID_ABSOLUTE_THROTTLE_POS_C 0x48 62 | #define PID_ACC_PEDAL_POS_D 0x49 63 | #define PID_ACC_PEDAL_POS_E 0x4A 64 | #define PID_ACC_PEDAL_POS_F 0x4B 65 | #define PID_COMMANDED_THROTTLE_ACTUATOR 0x4C 66 | #define PID_TIME_WITH_MIL 0x4D 67 | #define PID_TIME_SINCE_CODES_CLEARED 0x4E 68 | #define PID_ETHANOL_FUEL 0x52 69 | #define PID_FUEL_RAIL_PRESSURE 0x59 70 | #define PID_HYBRID_BATTERY_PERCENTAGE 0x5B 71 | #define PID_ENGINE_OIL_TEMP 0x5C 72 | #define PID_FUEL_INJECTION_TIMING 0x5D 73 | #define PID_ENGINE_FUEL_RATE 0x5E 74 | #define PID_ENGINE_TORQUE_DEMANDED 0x61 75 | #define PID_ENGINE_TORQUE_PERCENTAGE 0x62 76 | #define PID_ENGINE_REF_TORQUE 0x63 77 | 78 | typedef enum { 79 | PROTO_AUTO = 0, 80 | PROTO_ISO_9141_2 = 3, 81 | PROTO_KWP2000_5KBPS = 4, 82 | PROTO_KWP2000_FAST = 5, 83 | PROTO_CAN_11B_500K = 6, 84 | PROTO_CAN_29B_500K = 7, 85 | PROTO_CAN_29B_250K = 8, 86 | PROTO_CAN_11B_250K = 9, 87 | } OBD_PROTOCOLS; 88 | 89 | // states 90 | typedef enum { 91 | OBD_DISCONNECTED = 0, 92 | OBD_CONNECTING = 1, 93 | OBD_CONNECTED = 2 94 | } OBD_STATES; 95 | 96 | uint16_t hex2uint16(const char *p); 97 | uint8_t hex2uint8(const char *p); 98 | 99 | class COBD 100 | { 101 | public: 102 | COBD() 103 | :dataMode(1) 104 | ,errors(0) 105 | ,m_state(OBD_DISCONNECTED) 106 | ,aInProgress(false) 107 | ,aPid(0) 108 | ,aTimestamp(0) 109 | ,aBufferN(0) 110 | {} 111 | /* 112 | Serial baudrate is only adjustable for Arduino OBD-II Adapters V2 113 | Check out http://freematics.com/pages/products/arduino-obd-adapter 114 | */ 115 | virtual void begin(); 116 | // initialize OBD-II connection 117 | virtual bool init(OBD_PROTOCOLS protocol = PROTO_AUTO); 118 | // un-initialize OBD-II connection 119 | virtual void end(); 120 | // set serial baud rate 121 | virtual void setBaudRate(long baudrate); 122 | // get connection state 123 | virtual OBD_STATES getState() { return m_state; } 124 | // read specified OBD-II PID value 125 | virtual bool read(byte pid, int& result); 126 | // set device into 127 | virtual void sleep(); 128 | // wake up device from previous sleep 129 | virtual void wakeup(); 130 | // set working protocol (default auto) 131 | virtual bool setProtocol(OBD_PROTOCOLS h = PROTO_AUTO); 132 | // clear diagnostic trouble code 133 | virtual void clearDTC(); 134 | // get battery voltage (in 0.1V, e.g. 125 for 12.5V, works without ECU) 135 | virtual int getVoltage(); 136 | // send query for specified PID 137 | virtual void sendQuery(byte pid); 138 | // retrive and parse the response of specifie PID 139 | virtual bool getResult(byte& pid, int& result); 140 | // determine if the PID is supported 141 | virtual bool isValidPID(byte pid); 142 | 143 | // async requesting data: 144 | virtual void asyncRequest(byte pid); 145 | virtual bool asyncInProgress(); 146 | virtual bool asyncGet(int& result); 147 | virtual int asyncDuration(); 148 | 149 | // set current PID mode 150 | byte dataMode; 151 | // occurrence of errors 152 | byte errors; 153 | // bit map of supported PIDs 154 | byte pidmap[4 * 4]; 155 | protected: 156 | virtual char* getResponse(byte& pid, char* buffer); 157 | virtual void dataIdleLoop() {} 158 | void recover(); 159 | void debugOutput(const char* s); 160 | int normalizeData(byte pid, char* data); 161 | OBD_STATES m_state; 162 | 163 | bool aInProgress; 164 | byte aPid; 165 | int aTimestamp; 166 | int aBufferN; 167 | char aBuffer[OBD_RECV_BUF_SIZE]; 168 | private: 169 | virtual byte receive(char* buffer = 0, int timeout = OBD_TIMEOUT_SHORT); 170 | virtual bool available(); 171 | virtual char read(); 172 | virtual void write(const char* s); 173 | virtual void write(char c); 174 | virtual uint8_t getPercentageValue(char* data) 175 | { 176 | return (uint16_t)hex2uint8(data) * 100 / 255; 177 | } 178 | virtual uint16_t getLargeValue(char* data) 179 | { 180 | return hex2uint16(data); 181 | } 182 | virtual uint8_t getSmallValue(char* data) 183 | { 184 | return hex2uint8(data); 185 | } 186 | virtual int16_t getTemperatureValue(char* data) 187 | { 188 | return (int)hex2uint8(data) - 40; 189 | } 190 | }; 191 | /* 192 | #define I2C_ADDR 0x62 193 | 194 | #define MAX_PAYLOAD_SIZE 32 195 | #define MAX_PIDS 8 196 | 197 | #define CMD_QUERY_STATUS 0x10 198 | #define CMD_SEND_AT_COMMAND 0x11 199 | #define CMD_APPLY_OBD_PIDS 0x12 200 | #define CMD_LOAD_OBD_DATA 0x13 201 | #define CMD_GPS_SETUP 0x20 202 | #define CMD_GPS_QUERY 0x22 203 | 204 | typedef struct { 205 | uint16_t age; 206 | uint16_t value; 207 | } PID_INFO; 208 | 209 | typedef struct { 210 | uint16_t time; 211 | uint8_t message; 212 | uint8_t data; 213 | } COMMAND_BLOCK; 214 | 215 | typedef struct { 216 | uint32_t time; 217 | uint32_t date; 218 | float lat; 219 | float lon; 220 | float speed; 221 | float alt; 222 | uint8_t sat; 223 | uint8_t state; 224 | uint16_t age; 225 | uint8_t reserved[4]; 226 | } GPS_DATA; 227 | 228 | class COBDI2C : public COBD { 229 | public: 230 | void begin(); 231 | void end(); 232 | bool init(OBD_PROTOCOLS protocol = PROTO_AUTO); 233 | bool read(byte pid, int& result); 234 | void write(const char* s); 235 | // Asynchronized access API 236 | void setPID(byte pid); 237 | void applyPIDs(); 238 | void loadData(); 239 | uint16_t getData(byte pid, int& result); 240 | // GPS API 241 | bool gpsQuery(GPS_DATA* gpsdata); 242 | void gpsSetup(uint32_t baudrate, const char* cmds = 0); 243 | protected: 244 | bool sendCommand(byte cmd, uint8_t data = 0, byte* payload = 0, byte payloadBytes = 0); 245 | byte receive(char* buffer, int timeout = OBD_TIMEOUT_SHORT); 246 | byte m_addr; 247 | PID_INFO obdInfo[MAX_PIDS]; 248 | byte obdPid[MAX_PIDS]; 249 | }; 250 | */ 251 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arduino-OBD2-Async 2 | Based on [stanleyhuangyc/ArduinoOBD](https://github.com/stanleyhuangyc/ArduinoOBD/tree/master/libraries/OBD), with asynchronous data requesting. 3 | 4 | There are new functions: 5 | ``` 6 | void asyncRequest(byte pid); // send request for specific PID 7 | bool asyncInProgress(); // check that we already are waiting for response 8 | bool asyncGet(int& result); // try to get response into 'result' 9 | ``` 10 | 11 | Typical usage: 12 | ``` 13 | if (!obd.asyncInProgress()) 14 | obd.asyncRequest(PID_RPM); 15 | int response = 0; 16 | if (obd.asyncGet(response)) 17 | Serial.println(response); 18 | ``` 19 | -------------------------------------------------------------------------------- /examples/OBD_II/OBD_II.ino: -------------------------------------------------------------------------------- 1 | #include "OBD.h" 2 | #include "U8glib.h" 3 | 4 | //U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0); // I2C / TWI 5 | U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_NO_ACK|U8G_I2C_OPT_FAST); // Fast I2C / TWI 6 | 7 | /* 8 | Engine 9 | PID_RPM – Engine RPM (rpm) 10 | PID_ENGINE_LOAD – Calculated engine load (%) 11 | PID_COOLANT_TEMP – Engine coolant temperature (°C) 12 | PID_ENGINE_LOAD – Calculated Engine load (%) 13 | PID_ABSOLUTE_ENGINE_LOAD – Absolute Engine load (%) 14 | PID_TIMING_ADVANCE – Ignition timing advance (°) 15 | PID_ENGINE_OIL_TEMP – Engine oil temperature (°C) 16 | PID_ENGINE_TORQUE_PERCENTAGE – Engine torque percentage (%) 17 | PID_ENGINE_REF_TORQUE – Engine reference torque (Nm) 18 | 19 | Intake/Exhaust 20 | PID_INTAKE_TEMP – Intake temperature (°C) 21 | PID_INTAKE_PRESSURE – Intake manifold absolute pressure (kPa) 22 | PID_MAF_FLOW – MAF flow pressure (grams/s) 23 | PID_BAROMETRIC – Barometric pressure (kPa) 24 | 25 | Speed/Time 26 | PID_SPEED – Vehicle speed (km/h) 27 | PID_RUNTIME – Engine running time (second) 28 | PID_DISTANCE – Vehicle running distance (km) 29 | 30 | Driver 31 | PID_THROTTLE – Throttle position (%) 32 | PID_AMBIENT_TEMP – Ambient temperature (°C) 33 | 34 | Electric Systems 35 | PID_CONTROL_MODULE_VOLTAGE – vehicle control module voltage (V) 36 | PID_HYBRID_BATTERY_PERCENTAGE – Hybrid battery pack remaining life (%) 37 | */ 38 | 39 | const int TEST_LED_PIN = 5; 40 | const int MODE_BTN_PIN = 4; 41 | COBD obd; 42 | int mode = 0; 43 | bool modeBtnWasPushed = false; 44 | int framesCount = 0; 45 | int requestsCount = 0; 46 | int lastTime = 0; 47 | int frameTime = 0; 48 | 49 | struct Data 50 | { 51 | const int pid; 52 | int value; 53 | const char *title; 54 | const char *unit; 55 | }; 56 | 57 | Data data[] = 58 | { 59 | {PID_RPM, -1, "RPM: ", "r/m"}, 60 | {PID_ENGINE_LOAD, -1, "Rel. load: ", "%"}, 61 | //{PID_ABSOLUTE_ENGINE_LOAD, -1, "Abs. load: ", "%"}, 62 | //{PID_ENGINE_TORQUE_PERCENTAGE, -1, "Torque: ", "%"}, 63 | {PID_THROTTLE, -1, "Throttle open: ", "%"}, 64 | {PID_AMBIENT_TEMP, -1, "Ambient t: ", "*C"}, 65 | {PID_INTAKE_TEMP, -1, "Intake t: ", "*C"}, 66 | //{PID_ENGINE_OIL_TEMP, -1, "Oil t: ", "*C"}, 67 | {PID_COOLANT_TEMP, -1, "Coolant t: ", "*C"}, 68 | //{PID_CONTROL_MODULE_VOLTAGE, -1, "Voltage: ", "V"}, 69 | {PID_SPEED, -1, "Speed: ", "km/h"}, 70 | {PID_FUEL_LEVEL, -1, "Fuel in tank: ", "%"}, 71 | //{PID_DISTANCE, -1, "Distance: ", "km"}, 72 | //{PID_RUNTIME, -1, "Runtime: ", "s"} 73 | 74 | {PID_DISTANCE_WITH_MIL, -1, "Distance *: ", ""}, 75 | {PID_TIME_WITH_MIL, -1, "Time *: ", ""}, 76 | {PID_ENGINE_FUEL_RATE, -1, "Fuel rate: ", ""}, 77 | }; 78 | 79 | void setup() 80 | { 81 | //Serial.begin(9600); 82 | //Serial.println("Start setup"); 83 | 84 | pinMode(TEST_LED_PIN, OUTPUT); 85 | pinMode(MODE_BTN_PIN, INPUT); 86 | 87 | u8g.firstPage(); 88 | do { 89 | u8g.setFont(u8g_font_6x12); 90 | u8g.setPrintPos(0, 32); 91 | u8g.print("Initializing OBD..."); 92 | } while(u8g.nextPage()); 93 | 94 | digitalWrite(TEST_LED_PIN, HIGH); 95 | obd.begin(); // start communication with OBD-II UART adapter 96 | while(!obd.init()) // initiate OBD-II connection until success 97 | ; 98 | digitalWrite(TEST_LED_PIN, LOW); 99 | 100 | //Serial.println("Setup complete"); 101 | } 102 | 103 | void printStat(int y, const char *text, int value, const char *suffix) 104 | { 105 | u8g.setPrintPos(0, y); 106 | String str = text; 107 | str += value; 108 | if (suffix != 0) 109 | str += suffix; 110 | u8g.print(str); 111 | } 112 | 113 | void draw() 114 | { 115 | u8g.setFont(u8g_font_6x12); 116 | printStat(20, data[mode].title, data[mode].value, data[mode].unit); 117 | 118 | printStat(60, "Frame duration ", frameTime, "ms"); 119 | 120 | u8g.drawPixel(abs(128 - (framesCount % 256)), 62); 121 | u8g.drawPixel(abs(128 - (requestsCount % 256)), 63); 122 | digitalWrite(TEST_LED_PIN, (requestsCount % 2 > 0) ? HIGH : LOW); 123 | } 124 | 125 | void loop() 126 | { 127 | //Serial.println("Loop..."); 128 | 129 | bool modeBtnPushed = (digitalRead(MODE_BTN_PIN) == HIGH); 130 | if (modeBtnPushed != modeBtnWasPushed) 131 | { 132 | if (modeBtnPushed) 133 | { 134 | mode = (mode + 1) % (sizeof(data) / sizeof(data[0])); 135 | //Serial.print("Change mode to "); 136 | //Serial.println(mode); 137 | } 138 | modeBtnWasPushed = modeBtnPushed; 139 | } 140 | 141 | //obd.read(data[mode].pid, data[mode].value); 142 | if (!obd.asyncInProgress()) 143 | obd.asyncRequest(data[mode].pid); 144 | int responce = 0; 145 | if (obd.asyncGet(responce)) 146 | { 147 | data[mode].value = responce; 148 | requestsCount++; 149 | digitalWrite(TEST_LED_PIN, HIGH); 150 | } 151 | else 152 | digitalWrite(TEST_LED_PIN, LOW); 153 | 154 | 155 | u8g.firstPage(); 156 | do { 157 | draw(); 158 | } while(u8g.nextPage()); 159 | 160 | framesCount++; 161 | int freshTime = millis(); 162 | frameTime = freshTime - lastTime; 163 | lastTime = freshTime; 164 | } 165 | --------------------------------------------------------------------------------