Skip to content

feat(sungrow): read live values once a second (1.6.0) - #179

Merged
frahlg merged 5 commits into
mainfrom
claude/sungrow-1hz-meter
Oct 14, 2026
Merged

frahlg merged 5 commits into
mainfrom
claude/sungrow-1hz-meter

Conversation

@frahlg

@frahlg frahlg commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

Summary

FTW sends live frames once a second, but the home box reads its Sungrow
site meter every 5 s, so the phone's figures jump every 5 s
(srcfl/ftw-app#33). sungrow 1.6.0 returns 1000 ms from driver_poll and
splits its reads:

  • Every poll, 9 reads: power-flow status (13000), battery V/A/W/SoC
    (13019), EMS readback (13049 holding, for setpoint_w), PV power (5016)
    and MPPT (5010), grid power (5600), per-phase W, V and A (5602, 5740,
    5743).
  • Every 5 s, 10 more: running state (12999), PV lifetime (13002),
    rated power (5000), heatsink temperature (5007), grid frequency (5241),
    fault bits (13049 input), battery and grid energy counters (13040, 13026,
    13036, 13045). Diagnostic metrics keep this rate too.

Between full reads, each blob carries the last slow values. FTW stores each
emit whole, and its energy ledger starts over (counter_resumed) whenever a
counter drops out of a reading. The carried values ride only on a poll whose
own power read succeeded, so they never outlive the device. Each is at most
one full cycle old, the age it reaches between polls today.

Per-phase current is read every second as well, because FTW's fuse guard
reads it from the meter blob.

A read FTW refused while it waited to reconnect ("modbus reconnect
backoff") no longer counts toward treating a register as absent. FTW's
first reconnect wait is 2 s, which spans two polls at one per second.
Counted, one dropped packet could mark a whole block absent until restart.

Other bundled drivers. With srcfl/ftw#1616, sdm630 and pixii_pv also
move to the 1 s they already return. The owner accepted both: an SDM630
easily reads at 1 s, and pixii_pv reads MQTT, so it adds no bus traffic.

Paired with srcfl/ftw#1616. Core ignores the return value until that
merges. On an older Core this driver still polls every 5 s, and then every
poll is a full read, as before. The FTW pin moves in a separate PR after
both merge.

Driver evidence

  • Driver id: sungrow 1.6.0
  • Device make/model: Sungrow SH-series hybrid on the owner's home box
    (device type 0x0E0E), Modbus TCP
  • Firmware tested: as installed on the home box
  • Protocol/register source: unchanged register map
  • Hardware test performed: yes, see below.
  • Baseline, read-only, 1.5.10: the meter reading changed every 5.04 s (5038–5042 ms), so one full poll of 19 reads took about 40 ms. No Sungrow poll failures in the box journal since 8 Oct.
  • Known limits: one site, 30 minutes, LAN port. WiNet-S is slower.

Hardware, 14 Oct 2026, owner's home box. Sungrow SH hybrid (device type
0x0E0E), Modbus TCP on the LAN, FTW v0.142.1-beta.1. The owner approved a
30-minute driver draft (POST /api/drivers/sungrow/draft), which reverted by
itself at 06:45. The draft was this driver plus two lines for the test:
host.set_poll_interval(1000), because that Core ignores the return, and a
timer around each Modbus read that logged a summary each minute.

Result
Polls 1712 in 29 whole minutes: 1369 fast, 343 full
Modbus reads 19,040; none failed (0 refused, 0 link, 0 reconnect waits)
Poll errors 0
Poll start to start median 1019–1020 ms in every minute, p95 at most 1042 ms, max 1247 ms
One read median 2 ms, p99 3 ms, max 210 ms (once)
Fast poll, 9 reads median 16–17 ms, max 28 ms
Full poll, 19 reads median 34–37 ms, max 241 ms
Meter reading in /api/drivers/sungrow, sampled 4 times a second new every 1020 ms (median), p95 1040 ms; 5040 ms before

Journal, 06:10–06:55: no warning during the run. Two modbus reconnected
lines, one at each driver restart. One site dispatch inhibited (site_meter_stale) for 3 s at 06:45:04, after the revert: the bundled 1.5.10
waits 5 s for its first poll after a restart, and the same line appears at
every restart in the 24 h before. The draft start, which polled after 1 s,
had none.

Limits: 30 minutes on one site, early morning, battery full and mostly idle,
so few control writes shared the Modbus session. A WiNet-S dongle answers
more slowly than this LAN port.

Verdict: 1 s is safe for this Sungrow. Modbus traffic takes about 17 ms of
each second, and nothing failed.

Safety

  • The driver starts read-only, or this is a separately reviewed control change. No control path changed.
  • Vendor signs are converted at the driver boundary.
  • Cached telemetry becomes stale instead of being re-emitted as fresh. Slow values ride only with a fresh power read this poll; a failed poll emits nothing.
  • No credentials, serial numbers, private addresses or site data are included.

Control evidence

  • HIL evidence or required: not applicable, read path only.
  • Control changes include default-mode, lease-expiry and HIL evidence. Not applicable.

Checks

  • Commits include Signed-off-by.
  • make test-driver ID=sungrow
  • make check (4908 passed)

New tests in test_sungrow_poll_cadence.py: the 1000 ms return, which
registers each poll reads, full reads on a 5 s host, carried values, no
carried values without this poll's power read, reconnect waits not counted,
refused registers still given up. Five of them fail on 1.5.10.
test_fault_clears_on_a_running_state_of_zero now steps the clock 5 s per
poll, because the running state is read on full polls.

Also run, not committed: this driver under FTW's gopher-lua with the
srcfl/ftw#1616 run loop and a fake Modbus, 12 s. Polls 1 s apart, 9 reads
each, 19 every 5 s, counters kept in the meter blob.

🤖 Generated with Claude Code

driver_poll returns 1000 ms. Power, SoC, per-phase values and the EMS
readback are read on every poll; counters, nameplate, temperature,
frequency and fault bits keep the five-second rate and ride along on
the polls between, only when that poll's own power read succeeded.

A read FTW refused during a reconnect wait no longer counts toward
treating a register as absent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
@frahlg
frahlg marked this pull request as ready for review October 14, 2026 05:02
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-14T05:05:06.977492Z 265d909 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 265d909328

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread drivers/lua/sungrow.lua
Comment on lines +736 to +737
else
battery.charge_wh, battery.discharge_wh = slow.charge_wh, slow.discharge_wh

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Stop re-emitting cached slow values as fresh

Between full reads, this copies the previous battery counters into a new one-second host.emit without preserving their observation time or marking them stale; the analogous PV and meter caches do the same. Whenever live power continues but a slow field has not been reread, consumers therefore receive that old measurement as part of a fresh sample, contrary to the repository requirement not to emit stale cached telemetry as fresh. Keep the original sample age explicitly or avoid replaying these fields.

AGENTS.md reference: AGENTS.md:L71-L75

Useful? React with 👍 / 👎.

frahlg and others added 2 commits October 14, 2026 07:45
driver_poll returned 1000 ms, which FTW ignored until srcfl/ftw#1616.
An SDM630 often shares an RS485 bus with an inverter, and its one
76-register read takes about 0.18 s at 9600 baud. Return 5000 until
hardware shows one second is safe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
driver_poll returned 1000 ms, which FTW ignored until srcfl/ftw#1616.
Hold it at the five seconds it has run at until hardware shows one
second works. It reads MQTT, so the hold is about unproven behaviour,
not bus load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
@frahlg frahlg changed the title feat(sungrow): read live values once a second (1.6.0) feat(sungrow): read live values once a second (1.6.0); hold sdm630 and pixii_pv at 5 s Oct 14, 2026
frahlg and others added 2 commits October 14, 2026 07:53
…(0.3.3)"

pixii_pv reads Core's local MQTT queue, so the bus-load reason for the
hold does not apply. The owner chose to let it run at one second like
ferroamp.

This reverts commit b5af185.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
….0.3)"

The owner says an SDM630 easily reads at one second, so it runs at the
1000 ms its driver asks for.

This reverts commit 48cae6c.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
@frahlg frahlg changed the title feat(sungrow): read live values once a second (1.6.0); hold sdm630 and pixii_pv at 5 s feat(sungrow): read live values once a second (1.6.0) Oct 14, 2026
@frahlg
frahlg merged commit afc9739 into main Oct 14, 2026
5 checks passed
@frahlg
frahlg deleted the claude/sungrow-1hz-meter branch October 14, 2026 06:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant