Fix UART pin detach: pinMatrixOutDetach() instead of no-op uart_set_pin(ALL_NO_CHANGE). Fix timeout underflow in readResponse() with remainingMs() helper. Add checksum validation on received frames. Echo verification in clearEcho() detects bus contention. Flush RX buffers after init sequences. Structural ISO 14230 frame parsing replaces byte-scanning. TesterPresent keepalive prevents P3 session timeout. Scanner re-initializes after consecutive failures.
125 lines
3.6 KiB
C++
125 lines
3.6 KiB
C++
#pragma once
|
|
// Hardware transport for single-wire automotive buses (K-line family)
|
|
// Extracted from IbusEsp32 — owns UART, GPIO ISR, ring buffers, idle detection.
|
|
// Protocol-agnostic: BMW I/K-Bus and OBD-II K-line use different configs.
|
|
|
|
#include <Arduino.h>
|
|
#include "esp_timer.h"
|
|
#include "RingBuffer.h"
|
|
|
|
// Map IBUS_DEBUG to KLINE_DEBUG for backward compatibility
|
|
#if defined(IBUS_DEBUG) && !defined(KLINE_DEBUG)
|
|
#define KLINE_DEBUG
|
|
#endif
|
|
|
|
// Accept either KLINE_ or IBUS_ prefixed buffer size defines
|
|
#ifndef KLINE_RX_BUFFER_SIZE
|
|
#ifdef IBUS_RX_BUFFER_SIZE
|
|
#define KLINE_RX_BUFFER_SIZE IBUS_RX_BUFFER_SIZE
|
|
#else
|
|
#define KLINE_RX_BUFFER_SIZE 256
|
|
#endif
|
|
#endif
|
|
#ifndef KLINE_TX_BUFFER_SIZE
|
|
#ifdef IBUS_TX_BUFFER_SIZE
|
|
#define KLINE_TX_BUFFER_SIZE IBUS_TX_BUFFER_SIZE
|
|
#else
|
|
#define KLINE_TX_BUFFER_SIZE 128
|
|
#endif
|
|
#endif
|
|
#ifndef KLINE_MAX_MSG
|
|
#define KLINE_MAX_MSG 40
|
|
#endif
|
|
|
|
enum ChecksumType : uint8_t {
|
|
CHECKSUM_XOR, // BMW I/K-Bus: XOR all bytes
|
|
CHECKSUM_MOD256, // OBD-II K-line: sum mod 256
|
|
};
|
|
|
|
struct KLineConfig {
|
|
uint32_t baud = 9600;
|
|
uint32_t framing = SERIAL_8E1;
|
|
uint16_t idleTimeoutUs = 1500; // 0 = no idle detection (master/slave mode)
|
|
uint16_t idleCheckUs = 250;
|
|
uint16_t packetGapMs = 10;
|
|
bool txInvert = true; // true for optocoupler, false for transistor
|
|
ChecksumType checksumType = CHECKSUM_XOR;
|
|
};
|
|
|
|
class KLineTransport {
|
|
public:
|
|
KLineTransport();
|
|
~KLineTransport();
|
|
|
|
void begin(HardwareSerial& serial, int8_t rxPin, int8_t txPin,
|
|
int8_t ledPin = -1, uint8_t uartNum = 1,
|
|
const KLineConfig& config = KLineConfig());
|
|
|
|
// Call from loop() — handles TX scheduling
|
|
void run();
|
|
|
|
// Queue a message for TX. Checksum is appended per config.checksumType.
|
|
void write(const uint8_t* message, uint8_t size);
|
|
|
|
// Send raw bytes without framing/checksum (for init sequences)
|
|
void sendRaw(const uint8_t* data, uint8_t len);
|
|
|
|
// Drain UART FIFO into RX ring buffer
|
|
void drainUart();
|
|
|
|
// RX ring buffer accessors (protocol handlers read from these)
|
|
int rxAvailable();
|
|
int rxPeek(int n = 0);
|
|
void rxRemove(int n);
|
|
int rxRead();
|
|
|
|
// Flush both UART FIFO and RX ring buffer (discard all pending data)
|
|
void flushRx();
|
|
|
|
// Bus idle state
|
|
bool clearToSend() const { return _clearToSend; }
|
|
|
|
// TX state — protocol handlers need this to suppress loopback echo
|
|
bool isTransmitting() const { return _isTransmitting; }
|
|
|
|
// LED control for protocol handlers
|
|
void ledOn();
|
|
void ledOff();
|
|
|
|
// Access to serial port (for direct byte ops during init sequences)
|
|
HardwareSerial* serial() { return _serial; }
|
|
|
|
// Pin access (for bit-bang init sequences that detach UART)
|
|
int8_t txPin() const { return _txPin; }
|
|
uint8_t uartNum() const { return _uartNum; }
|
|
|
|
// Checksum utilities
|
|
static uint8_t checksumXor(const uint8_t* data, uint8_t length);
|
|
static uint8_t checksumMod256(const uint8_t* data, uint8_t length);
|
|
uint8_t calculateChecksum(const uint8_t* data, uint8_t length) const;
|
|
|
|
private:
|
|
void trySend();
|
|
void sendNextPacket();
|
|
void setupBusIdleDetection(int8_t rxPin);
|
|
|
|
static void IRAM_ATTR onRxPinChange(void* arg);
|
|
static void onIdleTimerCallback(void* arg);
|
|
|
|
HardwareSerial* _serial;
|
|
int8_t _ledPin;
|
|
int8_t _txPin;
|
|
uint8_t _uartNum;
|
|
KLineConfig _config;
|
|
|
|
RingBuffer _rxRing;
|
|
RingBuffer _txRing;
|
|
|
|
// Bus idle detection
|
|
esp_timer_handle_t _idleTimer;
|
|
volatile uint32_t _lastRxTransitionUs;
|
|
volatile bool _clearToSend;
|
|
volatile bool _isTransmitting;
|
|
unsigned long _lastTxMs;
|
|
};
|