Split the monolithic IbusEsp32 class into composable layers: - KLineTransport: UART, GPIO ISR, ring buffers, idle detection - IbusHandler: BMW I/K-Bus FSM, source filtering, packet callback - IbusEsp32: thin facade preserving the original API Library renamed from IbusEsp32 to AutoWire. Existing sniffer sketch (main.cpp) requires zero changes. All 3 ESP32 environments build cleanly (esp32dev, esp32-c3, esp32-s3).
122 lines
3.5 KiB
C++
122 lines
3.5 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();
|
|
|
|
// 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;
|
|
};
|