Skip to content

## Two DC-only B2500 (HMJ-2): only one registers as consumer, and both BMS SoC counters collapse to 0% in auto mode #559

Description

@SidvsKaliman

What's the issue?

Two DC-only B2500 (HMJ-2): only one registers as consumer, and both BMS SoC counters collapse to 0% in auto mode

Setup

  • AstraMeter Home Assistant add-on v2.2.4
  • 2× Marstek B2500-D (HMJ-2), identical firmware
    • LEFT 18cedf9aaaaa @ 192.168.178.71
    • RIGHT 6824994aaaaa @ 192.168.178.72
  • Per battery: 2 PV panels (450 W each), one feeding each of the battery's two PV inputs — so both PV inputs are in use on both batteries.
  • Per battery: one dedicated Marstek HMI-1 micro-inverter (MST-MI0600W), driven from both battery outputs (both outputs in use, so the commanded setpoint applies in full — not halved).
    • LEFT battery → inverter 98a14a3aaaaa
    • RIGHT battery → inverter 98a14a3bbbbb
  • Strictly DC-coupled: panels → battery → inverter. No panel is wired directly to an inverter.
  • Two separate, independent PV strings — the batteries share no panels and no inverter. AC-side, they sit on different phases (L1 and L3).
  • Grid meter: EasyMeter Q3AA1174 via Tasmota IR head, pushed to HA every 5 s. Meter is netting across all three phases.
  • power_input_alias = sensor.strom_leistung_l1,sensor.strom_leistung_l2,sensor.strom_leistung_l3
  • device_types = ct002, min_dc_output = 20, fair_distribution = true, throttle_interval = 1.0
  • Both batteries set to Self-Adaptation in the Marstek app, both bound to the emulated CT002. The inverters run in standard mode.

Issue 1 — Only one of two batteries is registered as a consumer

The two batteries poll AstraMeter in different roles, consistently, over thousands of requests:

LEFT  (192.168.178.71): phase='0'  → "inspection mode", never becomes a consumer
RIGHT (192.168.178.72): phase='A'  → the only registered consumer

Counted over one log window: 917/917 requests from LEFT with phase='0', 967/967 from RIGHT with phase='A'. Never a single switch.

Result: sensor.astrameter_consumer_count = 1. Fair distribution has nothing to distribute — there is only one consumer in the fleet. HA exposes consumer entities only for the RIGHT battery.

Both batteries are the same model with the same firmware, both configured identically in the Marstek app. I have not found any app setting that controls this.


Issue 2 — The single consumer gets trapped at min_dc_output (active control)

With active_control = true, the registered battery ended up in a self-sustaining trap:

  • reported_power = 0 — even though the battery was physically outputting 109 W (confirmed via hm2mqtt: micro_inverter_power = 109)
  • Because it reports 0, AstraMeter scores it saturation = 100%
  • Being "saturated", it gets capped at min_dc_outputtarget_l1 = 20 W
  • A DC-coupled B2500 cannot run its inverter below ~80 W, so 20 W means the output collapses to 0
  • Output 0 → still reports 0 → saturation stays at 100% → capped again

Log evidence: over 967 consecutive responses, the RIGHT battery received only ever this:

fields=['HME-4', '02b2500aaaaa', 'HMJ-2', '6824994aaaaa',
        '20', '0', '0', '20', '1', '0', '0', '0', '-50', ...]

while LEFT (inspection mode) received the real phase values:

fields=['HME-4', '02b2500aaaaa', 'HMJ-2', '18cedf9aaaaa',
        '1349', '71', '-618', '802', '1', '1', '1', '0', '-50', ...]

Switching active_control = false (relay mode) breaks the trap immediately: saturation drops to 0%, target_l1 rises to 299 W, and both batteries start receiving identical real phase values.

Possible root cause: for DC-coupled batteries, reported_power appears unreliable (always 0 here). Using it as the saturation input creates a deadlock that min_dc_output cannot escape, because the floor it enforces (20 W) is below the inverter's minimum operating power (~80 W).


Issue 3 — steer_to_zero fires on tiny surpluses (related to #359)

Even on v2.2.4, this appears repeatedly:

CT002: 1 W surplus but no AC-chargeable battery reporting — nothing here can absorb it. Reporting device_types: ['HMJ-2']
CT002: 3 W surplus but no AC-chargeable battery reporting — ...
CT002: 7 W surplus but no AC-chargeable battery reporting — ...
CT002: 9 W surplus but no AC-chargeable battery reporting — ...

Fires at 1 W / 3 W / 7 W / 9 W. Setting deadband = 30 suppresses these, but it looks like the #359 fix does not fully cover the near-zero-crossing case.


Issue 4 — Cross-talk fields stay zero (batteries never learn about each other)

In every response — active control and relay mode — the cross-talk fields (*_chrg_power / *_dchrg_power) are all zeros:

'-877', '363', '137', '-377', '1', '0', '0', '0', '-50', '141', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'

So neither battery knows what the other is doing. Both regulate independently against the same grid value.


Observation — BMS SoC collapse correlates with auto mode

This is the part I cannot explain, and I want to flag it carefully — I am not claiming AstraMeter causes it, only that the correlation is hard to ignore:

  • 2 days in Manual mode (fixed 150 W per battery): completely stable. Both batteries tracked each other closely, SoC and pack voltage nearly identical.
  • Switched to Self-Adaptation + AstraMeter. Within one hour, both batteries collapsed:
    • RIGHT at 21:22 → SoC 0%, output 0 W, discharging: off, charging: off
    • LEFT at 21:49 → SoC 0%, output 0 W, same state
    • Real pack voltages at that moment: 46.52 V and 46.62 V — i.e. both packs are essentially full

This is a BMS coulomb-counter drift, not a hardware fault. Marstek's own FAQ documents the fix (charge to 100% without discharging, then discharge to 0% without charging).

My working hypothesis: with cross-talk fields at zero (Issue 4), both batteries hunt independently around the zero-crossing, constantly reversing between charge and discharge. That repeated direction reversal is what drifts the coulomb counter. In manual mode there is no hunting, and no drift.

Historical HA data supports the drift pattern — SoC readings jumping like 17 → 61 → 17 and 0 → 60 → 0 within a minute, while pack voltage stayed steady.


Additional context that may matter

Parallel hm2mqtt connection. Both batteries are simultaneously connected to hm2mqtt (v1.8.1, MQTT proxy enabled) alongside AstraMeter's UDP CT002 session. This is how I could see that reported_power = 0 was wrong: hm2mqtt reported micro_inverter_power = 109 W for the same battery at the same moment. If a parallel MQTT session can affect what the battery reports over UDP, that would be worth knowing.

Prior custom controller. Before AstraMeter, I ran a custom HA automation for months that wrote time_period_1_output_value directly via hm2mqtt (60 s cycle, 20 W write deadband). It is possible one battery is left in a different internal state than the other as a result — which might explain why they now take different CT002 roles. I have no way to inspect or reset that state.

Timeline of the BMS collapse (relay mode did not prevent the second one):

  • ~20:45 — both batteries physically outputting (LEFT 172 W, RIGHT 109 W), grid at −19 W
  • 21:22 — RIGHT collapses: SoC 0%, output 0 W (still in active_control = true, capped at 20 W)
  • 21:26 — I switch to active_control = false (relay mode). Saturation trap clears, target_l1 jumps 20 W → 299 W, both batteries now receive identical real phase values
  • 21:49 — LEFT collapses too: SoC 0%, output 0 W — 23 minutes after the relay switch
  • 21:52 — both at SoC 0%, pack voltages 46.52 V / 46.62 V (full), grid +273 W

So relay mode fixed the balancer trap but did not prevent the second battery from going down. Whatever drives the BMS drift is not (only) the saturation logic.


Questions

  1. Is phase='0' (inspection mode) expected for a B2500 in Self-Adaptation, or does it indicate the battery declined to participate? Is there anything on the AstraMeter side that influences which role a battery takes? Could the fact that the two batteries sit on different AC phases (L1 / L3) be related?
  2. Should reported_power be trusted as the saturation input for DC-coupled batteries at all? Here it is always 0 despite real output.
  3. Should min_dc_output be floored at the inverter's minimum operating power (~80 W for B2500) rather than allowing 20 W, which for DC batteries effectively means "off"?
  4. Are the cross-talk fields expected to be populated with two DC batteries, and does relay mode change that?

What I can provide: full DEBUG logs for any window, HA history for SoC / pack voltage / inverter power / grid power at 1 min resolution going back ~10 days, and I can reproduce on demand — though I would rather not, given it takes a full charge/discharge calibration cycle to recover the packs afterwards.

Version

2.2.4

Installation method

Home Assistant add-on

If "Other", which?

No response

Configuration

[GENERAL]
DEVICE_TYPE = ct002
THROTTLE_INTERVAL = 1.0
WAIT_FOR_NEXT_MESSAGE = false
LOG_LEVEL = debug
DEADBAND = 30
HAMPEL_WINDOW = 7
HAMPEL_N_SIGMA = 3.0
HAMPEL_MIN_THRESHOLD = 50

[HOMEASSISTANT]
POWER_INPUT_ALIAS = sensor.strom_leistung_l1,sensor.strom_leistung_l2,sensor.strom_leistung_l3

[CT002]
ACTIVE_CONTROL = false
MIN_DC_OUTPUT = 20.0
MIN_EFFICIENT_POWER = 0
EFFICIENCY_ROTATION_INTERVAL = 900
GRID_PREDICT_TRUST = 0.5
FAIR_DISTRIBUTION = true
BALANCE_GAIN = 0.3
BALANCE_DEADBAND = 25
CONCENTRATE_DEADBAND = 60.0
IMPORT_TRIM_W = 15.0
MARSTEK_AUTO_REGISTER_CT_DEVICE = false
CLOUD_REPORTING = false

Logs

2026-07-12 19:51:48 DEBUG:astrameter:CT002 parsed fields from ('192.168.178.71', 10000): meter_dev_type=HMJ-2 meter_mac=18cedf984748 ct_type=HME-4 ct_mac=02b2500516f7 phase='0' power=0 consumer_id=18cedf984748 in inspection mode
2026-07-12 19:51:48 DEBUG:astrameter:CT002 parsed fields from ('192.168.178.72', 10000): meter_dev_type=HMJ-2 meter_mac=68249943636a ct_type=HME-4 ct_mac=02b2500516f7 phase='A' power=0 consumer_id=68249943636a
2026-07-12 19:51:48 DEBUG:astrameter:CT002 response to ('192.168.178.71', 10000): fields=['HME-4', '02b2500516f7', 'HMJ-2', '18cedf984748', '1349', '71', '-618', '802', '1', '1', '1', '0', '-50', '4', '0', '0', '0', '0', '0', '0', '20', '0', '0', '0']
2026-07-12 19:51:48 DEBUG:astrameter:CT002 response to ('192.168.178.72', 10000): fields=['HME-4', '02b2500516f7', 'HMJ-2', '68249943636a', '20', '0', '0', '20', '1', '0', '0', '0', '-50', '5', '0', '0', '0', '0', '0', '0', '20', '0', '0', '0']

2026-07-12 20:11:06 INFO:astrameter:CT002 consumer 68249943636a phase detected: A
2026-07-12 20:13:42 INFO:astrameter:CT002: 1 W surplus but no AC-chargeable battery reporting — nothing here can absorb it. Reporting device_types: ['HMJ-2']
2026-07-12 20:09:21 INFO:astrameter:CT002: 3 W surplus but no AC-chargeable battery reporting — nothing here can absorb it. Reporting device_types: ['HMJ-2']
2026-07-12 20:11:50 INFO:astrameter:CT002: 7 W surplus but no AC-chargeable battery reporting — nothing here can absorb it. Reporting device_types: ['HMJ-2']
2026-07-12 20:21:50 INFO:astrameter:CT002: 9 W surplus but no AC-chargeable battery reporting — nothing here can absorb it. Reporting device_types: ['HMJ-2']

--- After switching ACTIVE_CONTROL = false at 21:26:50 ---
2026-07-12 21:28:xx DEBUG:astrameter:CT002 response to ('192.168.178.71', 10000): fields=['HME-4', '02b2500516f7', 'HMJ-2', '18cedf984748', '-877', '363', '137', '-377', '1', '0', '0', '0', '-50', '141', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0']
2026-07-12 21:28:xx DEBUG:astrameter:CT002 response to ('192.168.178.72', 10000): fields=['HME-4', '02b2500516f7', 'HMJ-2', '68249943636a', '-877', '363', '137', '-377', '1', '0', '0', '0', '-50', '145', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0']

Note: LEFT sent phase='0' in 917/917 requests, RIGHT sent phase='A' in 967/967 requests. RIGHT received '20','0','0','20' in 967/967 responses under active control.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions