Skip to content

Commit 6e300dd

Browse files
committed
Publish EIC event-code and protection sensors for seplos_bms_v3_ble
decode_eic_data_ only published problem_code/problem_text. Publish the remaining EIC entities (already defined in the schema): the system state, voltage, temperature and current event codes plus the voltage, temperature, current protection and system-fault binary sensors. The cell-temperature (TB03) and ambient/power-temperature (TB04) tables are merged into a single temperature event code (TB03 high byte, TB04 low byte) so no detail is lost; the heating status bit (TB04 bit6) stays in the raw code but is masked out of the protection flag. The per-event debug log is dropped now that the values are published. Covered by EIC tests for every code and binary sensor, including a new EIC_DATA_AMBIENT_TEMP frame for the combined temperature path.
1 parent a746495 commit 6e300dd

3 files changed

Lines changed: 132 additions & 25 deletions

File tree

components/seplos_bms_v3_ble/seplos_bms_v3_ble.cpp

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -444,35 +444,35 @@ void SeplosBmsV3Ble::decode_eic_data_(const std::vector<uint8_t> &data) {
444444

445445
// EIC carries ten single-byte event codes (registers 0x2200–0x2248, see protocol
446446
// status tables). System state (TB09), FET event (TB07) and equalization state
447-
// (TB08) describe normal operation, not faults, so they must not raise a problem.
448-
// Each remaining alarm/protection/fault byte contributes one bit to the problem code.
449-
struct EventCode {
450-
uint8_t index;
451-
uint8_t fault_mask;
452-
uint32_t flag;
453-
const char *name;
454-
};
455-
static const EventCode EVENT_CODES[] = {
456-
{1, 0xFF, 1 << 0, "voltage"}, // TB02
457-
{2, 0xFF, 1 << 1, "cell temperature"}, // TB03
458-
{3, 0x3F, 1 << 2, "ambient temperature"}, // TB04 (bit6 "low temperature heating" is status)
459-
{4, 0xFF, 1 << 3, "current"}, // TB05
460-
{5, 0xFF, 1 << 4, "current latch"}, // TB16
461-
{6, 0xFF, 1 << 5, "capacity"}, // TB06
462-
{9, 0xFF, 1 << 6, "hard fault"}, // TB15
463-
};
464-
447+
// (TB08) describe normal operation, not faults, so they do not raise a problem.
448+
// Each remaining alarm/protection/fault byte contributes one bit to the problem
449+
// code; TB04 bit6 ("low temperature heating") is an operating state and is masked.
465450
uint32_t problem_code = 0;
466-
for (auto &event : EVENT_CODES) {
467-
uint8_t value = data[event.index] & event.fault_mask;
468-
if (value == 0)
469-
continue;
470-
problem_code |= event.flag;
471-
ESP_LOGD(TAG, " %s event code: 0x%02X", event.name, value);
472-
}
451+
problem_code |= uint32_t(data[1] != 0) << 0; // TB02 voltage
452+
problem_code |= uint32_t(data[2] != 0) << 1; // TB03 cell temperature
453+
problem_code |= uint32_t((data[3] & 0x3F) != 0) << 2; // TB04 ambient temperature
454+
problem_code |= uint32_t(data[4] != 0) << 3; // TB05 current
455+
problem_code |= uint32_t(data[5] != 0) << 4; // TB16 current latch
456+
problem_code |= uint32_t(data[6] != 0) << 5; // TB06 capacity
457+
problem_code |= uint32_t(data[9] != 0) << 6; // TB15 hard fault
473458

474459
this->publish_state_(this->problem_code_sensor_, (float) problem_code);
475460
this->publish_state_(this->problem_text_sensor_, problem_code != 0 ? "Problem detected" : "No problems");
461+
462+
// Raw event-code registers (diagnostic). The temperature code merges the cell
463+
// (TB03, data[2]) and ambient/power (TB04, data[3]) event bytes into one 16-bit
464+
// value — TB03 high byte, TB04 low byte — so neither table is lost.
465+
this->publish_state_(this->system_state_code_sensor_, (float) data[0]); // TB09
466+
this->publish_state_(this->voltage_event_code_sensor_, (float) data[1]); // TB02
467+
this->publish_state_(this->temperature_event_code_sensor_, (float) ((data[2] << 8) | data[3])); // TB03 + TB04
468+
this->publish_state_(this->current_event_code_sensor_, (float) data[4]); // TB05
469+
470+
// Protection / fault flags. "Low temperature heating" (TB04 bit6) is an operating
471+
// state, not a fault, so it is masked out of the temperature protection flag.
472+
this->publish_state_(this->voltage_protection_binary_sensor_, data[1] != 0);
473+
this->publish_state_(this->temperature_protection_binary_sensor_, (data[2] != 0) || ((data[3] & 0x3F) != 0));
474+
this->publish_state_(this->current_protection_binary_sensor_, (data[4] != 0) || (data[5] != 0));
475+
this->publish_state_(this->system_fault_binary_sensor_, data[9] != 0);
476476
}
477477

478478
void SeplosBmsV3Ble::decode_via_data_(const std::vector<uint8_t> &data) {

tests/components/seplos_bms_v3_ble/frames.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,13 @@ static const std::vector<uint8_t> EIC_DATA_HEATING = {
162162
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
163163
};
164164

165+
// EIC with an ambient/power temperature fault (TB04, byte 3, Bit0) — unlike the
166+
// heating status bit this is a real fault, so it must fold into the temperature
167+
// event code and raise temperature protection.
168+
static const std::vector<uint8_t> EIC_DATA_AMBIENT_TEMP = {
169+
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
170+
};
171+
165172
// SPA captures retained for upcoming sensor tests (no consumer yet).
166173
//
167174
// SPA payload, first request (106 bytes) – System Parameters, registers 0x1300–0x1334

tests/components/seplos_bms_v3_ble/seplos_bms_v3_ble_test.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,105 @@ TEST(SeplosBmsV3BleEicTest, HeatingIsNotAProblem) {
285285
EXPECT_EQ(problem_text.state, "No problems");
286286
}
287287

288+
// ── EIC: event-code sensors and protection binary sensors ─────────────────────
289+
290+
// Raw event-code registers: TB09 state (byte 0), TB02 voltage (byte 1),
291+
// TB05 current (byte 4); the cell-temperature alarm (TB03, byte 2) lands in the
292+
// high byte of the combined temperature code.
293+
TEST(SeplosBmsV3BleEicTest, EventCodeSensors) {
294+
TestableSeplosBmsV3Ble bms;
295+
sensor::Sensor system_state, voltage_code, temperature_code, current_code;
296+
bms.set_system_state_code_sensor(&system_state);
297+
bms.set_voltage_event_code_sensor(&voltage_code);
298+
bms.set_temperature_event_code_sensor(&temperature_code);
299+
bms.set_current_event_code_sensor(&current_code);
300+
301+
bms.decode_eic(EIC_DATA_WITH_PROBLEM); // data[2] = 0x01
302+
303+
EXPECT_FLOAT_EQ(system_state.state, 0.0f);
304+
EXPECT_FLOAT_EQ(voltage_code.state, 0.0f);
305+
EXPECT_FLOAT_EQ(temperature_code.state, 256.0f); // (0x01 << 8) | 0x00
306+
EXPECT_FLOAT_EQ(current_code.state, 0.0f);
307+
}
308+
309+
// TB03 (cell temperature, high byte) and TB04 (ambient/power temperature, low byte)
310+
// share one temperature event-code sensor; an ambient fault sets the low byte and
311+
// raises temperature protection.
312+
TEST(SeplosBmsV3BleEicTest, TemperatureEventCodeCombinesCellAndAmbient) {
313+
TestableSeplosBmsV3Ble bms;
314+
sensor::Sensor temperature_code;
315+
binary_sensor::BinarySensor temperature_protection;
316+
bms.set_temperature_event_code_sensor(&temperature_code);
317+
bms.set_temperature_protection_binary_sensor(&temperature_protection);
318+
319+
bms.decode_eic(EIC_DATA_AMBIENT_TEMP); // data[3] = 0x01
320+
321+
EXPECT_FLOAT_EQ(temperature_code.state, 1.0f); // (0x00 << 8) | 0x01
322+
EXPECT_TRUE(temperature_protection.state);
323+
}
324+
325+
// The heating status bit (TB04 bit6) is kept in the raw event code but masked out
326+
// of the protection flag.
327+
TEST(SeplosBmsV3BleEicTest, HeatingShowsInCodeButNotProtection) {
328+
TestableSeplosBmsV3Ble bms;
329+
sensor::Sensor temperature_code;
330+
binary_sensor::BinarySensor temperature_protection;
331+
bms.set_temperature_event_code_sensor(&temperature_code);
332+
bms.set_temperature_protection_binary_sensor(&temperature_protection);
333+
334+
bms.decode_eic(EIC_DATA_HEATING); // data[3] = 0x40
335+
336+
EXPECT_FLOAT_EQ(temperature_code.state, 64.0f); // (0x00 << 8) | 0x40, raw byte kept
337+
EXPECT_FALSE(temperature_protection.state); // 0x40 & 0x3F == 0
338+
}
339+
340+
TEST(SeplosBmsV3BleEicTest, ProtectionBinarySensors) {
341+
TestableSeplosBmsV3Ble bms;
342+
binary_sensor::BinarySensor voltage_protection, temperature_protection, current_protection, system_fault;
343+
bms.set_voltage_protection_binary_sensor(&voltage_protection);
344+
bms.set_temperature_protection_binary_sensor(&temperature_protection);
345+
bms.set_current_protection_binary_sensor(&current_protection);
346+
bms.set_system_fault_binary_sensor(&system_fault);
347+
348+
bms.decode_eic(EIC_DATA_WITH_PROBLEM); // data[2] = 0x01 (cell temperature)
349+
350+
EXPECT_FALSE(voltage_protection.state);
351+
EXPECT_TRUE(temperature_protection.state);
352+
EXPECT_FALSE(current_protection.state);
353+
EXPECT_FALSE(system_fault.state);
354+
}
355+
356+
// Hard fault (TB15, byte 9) drives the dedicated system-fault flag.
357+
TEST(SeplosBmsV3BleEicTest, SystemFaultBinarySensor) {
358+
TestableSeplosBmsV3Ble bms;
359+
binary_sensor::BinarySensor system_fault;
360+
bms.set_system_fault_binary_sensor(&system_fault);
361+
362+
bms.decode_eic(EIC_DATA_HARD_FAULT); // data[9] = 0x02
363+
364+
EXPECT_TRUE(system_fault.state);
365+
}
366+
367+
// Operating-status state code (TB09) is exposed but is not a protection event.
368+
TEST(SeplosBmsV3BleEicTest, SystemStateCodeIsNotProtection) {
369+
TestableSeplosBmsV3Ble bms;
370+
sensor::Sensor system_state;
371+
binary_sensor::BinarySensor voltage_protection, temperature_protection, current_protection, system_fault;
372+
bms.set_system_state_code_sensor(&system_state);
373+
bms.set_voltage_protection_binary_sensor(&voltage_protection);
374+
bms.set_temperature_protection_binary_sensor(&temperature_protection);
375+
bms.set_current_protection_binary_sensor(&current_protection);
376+
bms.set_system_fault_binary_sensor(&system_fault);
377+
378+
bms.decode_eic(EIC_DATA_STATUS_ONLY); // data[0] = 0x10
379+
380+
EXPECT_FLOAT_EQ(system_state.state, 16.0f);
381+
EXPECT_FALSE(voltage_protection.state);
382+
EXPECT_FALSE(temperature_protection.state);
383+
EXPECT_FALSE(current_protection.state);
384+
EXPECT_FALSE(system_fault.state);
385+
}
386+
288387
// ── PCT: inverter / protocol sensors ──────────────────────────────────────────
289388

290389
TEST(SeplosBmsV3BlePctTest, Sensors) {
@@ -402,6 +501,7 @@ TEST(SeplosBmsV3BleSafetyTest, NullSensorsDoNotCrash) {
402501
EXPECT_NO_FATAL_FAILURE(bms.decode_eib(EIB_DATA));
403502
EXPECT_NO_FATAL_FAILURE(bms.decode_eic(EIC_DATA_NO_PROBLEM));
404503
EXPECT_NO_FATAL_FAILURE(bms.decode_eic(EIC_DATA_WITH_PROBLEM));
504+
EXPECT_NO_FATAL_FAILURE(bms.decode_eic(EIC_DATA_AMBIENT_TEMP));
405505
}
406506

407507
} // namespace esphome::seplos_bms_v3_ble::testing

0 commit comments

Comments
 (0)