Library dir: lib/AutoWire/ -> lib/KLine/ Site title, docs, CLAUDE.md, platformio.ini all updated. All 4 firmware environments build clean.
93 lines
3.4 KiB
C++
93 lines
3.4 KiB
C++
#pragma once
|
|
// OBD-II K-line protocol handler (ISO 9141 / ISO 14230)
|
|
// Uses KLineTransport for UART, ring buffers, and bus management.
|
|
// Handles 5-baud slow init, fast init, request/response framing,
|
|
// and half-duplex echo clearing.
|
|
|
|
#include <Arduino.h>
|
|
#include "KLineTransport.h"
|
|
#include "driver/uart.h"
|
|
|
|
#ifndef KLINE_RESPONSE_TIMEOUT_MS
|
|
#define KLINE_RESPONSE_TIMEOUT_MS 2000
|
|
#endif
|
|
#ifndef KLINE_ECHO_TIMEOUT_MS
|
|
#define KLINE_ECHO_TIMEOUT_MS 100
|
|
#endif
|
|
|
|
class KLineObd2 {
|
|
public:
|
|
KLineObd2(KLineTransport& transport);
|
|
|
|
// ISO 9141 5-baud slow init — bit-bangs target address at 5 baud,
|
|
// then reads sync (0x55) and keyword bytes from ECU.
|
|
// Returns true if ECU responds with valid sync + keywords.
|
|
bool slowInit(uint8_t targetAddr = 0x33);
|
|
|
|
// ISO 14230 fast init — 25ms LOW + 25ms HIGH TiniPulse,
|
|
// then reads StartComm response.
|
|
bool fastInit();
|
|
|
|
// Send an OBD-II request. Frames the message and discards echo bytes.
|
|
// data[] should contain: [header, source, target, mode, pid, ...]
|
|
// For simple PID requests, use requestPid() instead.
|
|
void sendRequest(const uint8_t* data, uint8_t len);
|
|
|
|
// Read response from ECU. Blocks until response received or timeout.
|
|
// Returns number of bytes read, or 0 on timeout/checksum error.
|
|
uint8_t readResponse(uint8_t* buf, uint8_t maxLen,
|
|
uint16_t timeoutMs = KLINE_RESPONSE_TIMEOUT_MS);
|
|
|
|
// Convenience: send a mode/PID request and read the response.
|
|
// Returns response data length (excluding header/checksum), or 0 on failure.
|
|
uint8_t requestPid(uint8_t mode, uint8_t pid,
|
|
uint8_t* response, uint8_t maxLen);
|
|
|
|
// Send TesterPresent (service 0x3E) to keep the diagnostic session alive.
|
|
void sendTesterPresent();
|
|
|
|
// Reset initialized state (for re-init after session loss)
|
|
void reset() { _initialized = false; }
|
|
|
|
bool isInitialized() const { return _initialized; }
|
|
|
|
// Protocol info from init handshake
|
|
uint8_t keyword1() const { return _kw1; }
|
|
uint8_t keyword2() const { return _kw2; }
|
|
|
|
private:
|
|
// Bit-bang a single byte at 5 baud (200ms per bit) on TX pin
|
|
void sendByte5Baud(uint8_t data);
|
|
|
|
// Detach UART TX from GPIO pin (so we can bit-bang)
|
|
void detachUartTx();
|
|
|
|
// Re-attach UART TX to GPIO pin (after bit-bang init)
|
|
void reattachUartTx();
|
|
|
|
// Discard echo bytes from half-duplex bus, verify they match expected.
|
|
// Returns true if all echo bytes matched, false on bus contention.
|
|
bool clearEcho(const uint8_t* expected, uint8_t count);
|
|
|
|
// Read a single byte with timeout
|
|
int readByteTimeout(uint16_t timeoutMs);
|
|
|
|
// Parse ISO 14230 frame structure. Returns index of first data byte
|
|
// (service ID position), or -1 on parse error.
|
|
// Sets *dataLen to the number of data bytes.
|
|
int8_t parseFrameData(const uint8_t* buf, uint8_t bufLen, uint8_t* dataLen);
|
|
|
|
// Remaining timeout helper — clamps to 0 if already expired
|
|
static uint16_t remainingMs(unsigned long startMs, uint16_t timeoutMs);
|
|
|
|
KLineTransport& _transport;
|
|
bool _initialized;
|
|
uint8_t _kw1;
|
|
uint8_t _kw2;
|
|
uint8_t _testerAddr; // our address (0xF1 = external test equipment)
|
|
uint8_t _ecuAddr; // ECU address from init response
|
|
unsigned long _lastRequestMs; // for P3 keepalive timing
|
|
|
|
static constexpr uint16_t P3_KEEPALIVE_MS = 4000; // send TesterPresent before P3max
|
|
};
|