i-k-bus-board/firmware/lib/KLine/IbusHandler.cpp
Ryan Malloy 2aaa6e32a5 Rename library from AutoWire to K-Line
Library dir: lib/AutoWire/ -> lib/KLine/
Site title, docs, CLAUDE.md, platformio.ini all updated.
All 4 firmware environments build clean.
2026-02-13 08:31:34 -07:00

140 lines
3.8 KiB
C++

// BMW I/K-Bus protocol handler
// Extracted from IbusEsp32 — same FSM logic, reads from KLineTransport.
#include "IbusHandler.h"
IbusHandler::IbusHandler(KLineTransport& transport)
: _transport(transport),
_state(FIND_SOURCE),
_source(0),
_length(0),
_findMsgEnteredMs(0),
_callback(nullptr),
_filterEnabled(false),
_filterCount(0) {
memset(_msgBuf, 0, sizeof(_msgBuf));
memset(_filterAddrs, 0, sizeof(_filterAddrs));
}
void IbusHandler::process() {
switch (_state) {
case FIND_SOURCE:
if (_transport.rxAvailable() >= 1) {
_source = _transport.rxPeek(0);
_state = FIND_LENGTH;
}
break;
case FIND_LENGTH:
if (_transport.rxAvailable() >= 2) {
_length = _transport.rxPeek(1);
if (_length >= IBUS_MIN_LENGTH && _length <= IBUS_MAX_LENGTH) {
_state = FIND_MESSAGE;
_findMsgEnteredMs = millis();
} else {
_transport.rxRemove(1);
_state = FIND_SOURCE;
}
}
break;
case FIND_MESSAGE:
if (_transport.rxAvailable() >= _length + 2) {
uint8_t checksum = 0;
for (int i = 0; i <= _length; i++) {
checksum ^= _transport.rxPeek(i);
}
if (_transport.rxPeek(_length + 1) == checksum) {
_state = GOOD_CHECKSUM;
} else {
_state = BAD_CHECKSUM;
}
} else if (millis() - _findMsgEnteredMs > 100) {
// Timeout: partial message never completed (bus glitch).
// At 9600 baud, max-length message takes ~44ms. 100ms is generous.
#ifdef IBUS_DEBUG
Serial.print("RX TIMEOUT: len=0x");
Serial.print(_length, HEX);
Serial.print(" have=");
Serial.println(_transport.rxAvailable());
#endif
_transport.rxRemove(1);
_state = FIND_SOURCE;
}
break;
case GOOD_CHECKSUM:
if (_filterEnabled && _filterCount > 0) {
bool match = false;
for (uint8_t i = 0; i < _filterCount; i++) {
if (_source == _filterAddrs[i]) { match = true; break; }
}
if (!match) {
_transport.rxRemove(_length + 2);
_state = FIND_SOURCE;
return;
}
}
for (int i = 0; i < _length + 2; i++) {
_msgBuf[i] = _transport.rxRead();
}
_transport.ledOn();
if (_callback) {
_callback(_msgBuf, _length + 2);
}
_transport.ledOff();
_state = FIND_SOURCE;
break;
case BAD_CHECKSUM:
#ifdef IBUS_DEBUG
{
uint8_t n = min((int)(_length + 2), _transport.rxAvailable());
for (uint8_t i = 0; i < n; i++) {
_msgBuf[i] = _transport.rxPeek(i);
}
Serial.print("BAD CHK: ");
for (uint8_t i = 0; i < n; i++) {
if (_msgBuf[i] < 0x10) Serial.print('0');
Serial.print(_msgBuf[i], HEX);
Serial.print(' ');
}
Serial.println();
}
#endif
_transport.rxRemove(1);
_state = FIND_SOURCE;
break;
}
}
void IbusHandler::write(const uint8_t* message, uint8_t size) {
_transport.write(message, size);
}
void IbusHandler::onPacket(PacketCallback callback) {
_callback = callback;
}
void IbusHandler::setFilterEnabled(bool enabled) {
_filterEnabled = enabled;
}
void IbusHandler::setFilterAddress(uint8_t addr) {
_filterAddrs[0] = addr;
_filterCount = 1;
_filterEnabled = true;
}
void IbusHandler::setFilterAddresses(const uint8_t* addrs, uint8_t count) {
uint8_t n = min(count, (uint8_t)16);
memcpy(_filterAddrs, addrs, n);
_filterCount = n;
_filterEnabled = true;
}