Library dir: lib/AutoWire/ -> lib/KLine/ Site title, docs, CLAUDE.md, platformio.ini all updated. All 4 firmware environments build clean.
140 lines
3.8 KiB
C++
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;
|
|
}
|