SRNE ASP48120SH3 Safe Settings Write: design, registers, and commissioning (#53)
Purpose and scope
This page documents the SRNE ASP48120SH3 safe settings write feature (story #53): the ability to remotely change a bounded set of inverter parameters over Modbus, with every write guarded, verified by read back, and never retried. It records the design, the exact registers, the commissioning procedure, and, importantly, the current verification status so the work stays traceable and defendable.
Read this alongside the validated register map (page 126) and the mbpoll command reference (page 125).
What can and cannot be written
The feature can change eight settings only. Each is disabled by default (sentinel -1), bounded, applied only when the configured target differs from the live reading, and verified by a fresh read back.
| # | Setting | Register (FC16) | Bounds | Units / raw multiplier |
|---|---|---|---|---|
| 0 | Discharge cutoff SoC | 0xE00F | 0 to 100 | percent, x1 |
| 1 | Stop charge current | 0xE01C | 0 to 100 A | amps, x10 (deci-amp raw) |
| 2 | Stop charge SoC | 0xE01D | 0 to 100 | percent, x1 |
| 3 | Low SoC alarm | 0xE01E | 0 to 100 | percent, x1 |
| 4 | Switch to line (grid) SoC | 0xE01F | 0 to 100 | percent, x1 |
| 5 | Switch to battery SoC | 0xE020 | 0 to 100 | percent, x1 |
| 6 | AC charge current limit | 0xE205 | 0 to 100 A | amps, x10 |
| 7 | Max total charge current limit | 0xE20A | 0 to 100 A | amps, x10 |
The current ceiling is MAX_BATTERY_CURRENT_A = 100 A, the continuous rating. The datasheet peak is 120 A for 3 seconds, so the continuous value is the correct, conservative cap.
It explicitly cannot: change charge or discharge time windows, command a specific charge or discharge power, change operating or source priority modes, start or stop the inverter, or reset or clear data.
Safety design
Every write passes through the same defended path (component SrneBatteryInverterImpl, helper SafeWriteHandler in the io.openems.edge.ess.srne bundle):
- Default off. Nothing writes unless the operator sets controlEnabled true in the gateway config. Default is false.
- Machine state gate. A write is only queued when the inverter reports a verified stable state. Register 0x0210 value 2 (on-grid, mains bypass) is the only verified state; every other state, including off-grid (3), transitioning, fault, and undefined, blocks all writes.
- Bounds. Each setting is range checked against the table above. An out of range target is rejected before any Modbus write and marked failed (terminal).
- One shot per activation. The write state machine is IDLE, then QUEUED, then AWAITING_READBACK, then VERIFIED or FAILED. A setting is queued at most once per component activation; VERIFIED and FAILED are terminal, so there is no re-write and no storm even though reconciliation now runs every cycle.
- Read back verification. After the FC16 write executes, a fresh read of the same register must equal the target to reach VERIFIED. A mismatch goes to FAILED.
- Bounded wait, no retry. The read back is allowed up to 30 Edge cycles; on timeout the state is FAILED. A failed or mismatched write is never retried automatically.
- Thread safe. The handler methods are synchronized so the cycle thread (reconcile) and the Modbus worker thread (execute and read back) transition state atomically.
The aggregate SAFE_WRITE_STATE channel surfaces the highest severity handler state so an operator can watch commissioning progress.
The standalone topology fix (openems PR #23)
During #53 commissioning, gateway config v18 enabled control and targeted Low SoC alarm 15 to 16, but no FC16 write occurred and the read back stayed at 15. Root cause: gw-aaron runs a standalone Srne.Ess topology, so the generic ManagedSymmetricBatteryInverter run() path was never invoked, and reconciliation was hung off run(). The gateway was restored to the healthy v17 read only profile; the inverter setting did not change.
The fix moves the reconcile trigger to the Edge TOPIC_CYCLE_AFTER_PROCESS_IMAGE event, which fires every cycle in all topologies. It drives both the read back timeout advance and the queue, so the failure path works too. All guards above are unchanged; only the trigger moved.
Commissioning procedure (staged)
The commissioning is deliberately low risk and staged, with a pre staged rollback:
- v17: read only baseline, control disabled.
- v18: enable control, target exactly one setting (Low SoC alarm 15 to 16), leave the other seven at -1 (disabled). Verify a fresh read back of 16 and SAFE_WRITE_STATE VERIFIED, with stable telemetry and no other writes.
- v19: restore Low SoC alarm 16 to 15, keep control enabled, all other targets at -1.
- Rollback profile (microgrid PR #33): a break glass config that targets only Low SoC alarm 15, used if v18 changes the inverter but verification fails.
Gate: the v19 enable step (microgrid PR #34) must not be deployed until v18 has been deployed and verified with SAFE_WRITE_STATE VERIFIED on the real inverter.
Verification status (as of 2026-08-08)
Be precise here, because the live unit has not yet confirmed a write.
- Code reviewed at source level, including an adversarial second pass and mutation testing of the safety gates.
- Unit and integration tested in Java on JDK 21. Coverage includes: the write queues via the new event path in the standalone topology; control disabled never queues; a non verified machine state never queues (both mutation proven, that is, removing the guard makes the test fail with a real queued write); the one shot property, no re-queue across extra cycles; and, at unit level, the read back mismatch and timeout paths with no retry.
- Not yet verified on the live inverter. No setting has been successfully written and read back on the physical unit. The only live result so far is the v18 non write above, which exposed the topology bug. Live verification is pending: merge openems #23, build a new openems-edge image, then repeat the v18 15 to 16 commissioning gate on the real inverter.
- There is no Python live test harness for this. Writes are exercised through OpenEMS plus the gateway commissioning config and verified by Modbus read back, not by a standalone script.
Traceability
- Story: nfe-modbus-energy-logger issue #53.
- Code: openems PR #21 (guarded write infrastructure, merged), openems PR #23 (standalone topology fix plus gate tests, in review).
- Deployment: microgrid-gateway PR #32 (v18 commission, merged), #33 (rollback pre stage), #34 (v19 enable). Gated as above.
- Registers: SRNE ASP48120SH3 Modbus Register Map, validated (page 126). mbpoll command reference (page 125).