You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Merge branch 'main' into fix/history-10min-cadence
Brings in #42 (execute_from_psram), #44 (LilyGO T-CAN485), the
reset-reason diagnostics and the config-builder work.
Every source file auto-merged. Two things needed hand resolution:
CHANGELOG.md — both sides appended to Fixed; kept both. While here,
corrected two statements main now contradicts and I had left stale:
the LilyGO entry claimed the Config Builder was still closed to
PSRAM-less boards, and the Removed entry claimed a test blocked them
outright. Both were true when written and are not now — the builder
offers the T-CAN485, and the test enforces "PSRAM and the web server
imply each other" instead of a blanket ban. Also added the two entries
my own commits never wrote: the reset reason, and the `ref: next` fix.
docs/images/*.png — generated artifacts, so neither side was right to
keep. Regenerated from the merged app.html.
Verified on the merged tree: 46/46 site tests, and both board tiers
compile — test-local (PSRAM, with history) and the T-CAN485 example
(no PSRAM, no history), which is the interaction this merge creates,
since this branch adds a history_interval option to a schema the
no-history board also uses.
Copy file name to clipboardExpand all lines: CHANGELOG.md
+8-1Lines changed: 8 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
## [Unreleased]
9
9
10
10
### Added
11
+
- **LilyGO T-CAN485 support, for Home-Assistant-only installs.** A board with no PSRAM can now run a sensors-only build — `tigo_monitor` without `tigo_server` — and feed every value to Home Assistant over the native API. PSRAM was never a requirement of the monitor itself: the device and node tables run about 600 bytes per optimizer, so twenty of them cost ~12 KB of the ~180-200 KB an ESP32 has free once WiFi is up. It is the web server that needs PSRAM, and it stays out of these configs. `boards/esp32-lilygo-t-can485.yaml` sets up the board's built-in isolated RS485 transceiver (GPIO21/22, plus the three enable lines LilyGO's own example drives high at boot), and `boards/example-t-can485.yaml` is ready to flash. The Config Builder offers it too, restricted to exactly this shape: selecting it hides the CCA, cloud and display options, because every one of them is a web-server feature, and clears them if you had them set on another board. What you give up: the dashboard, the REST API, on-flash history, CCA/cloud import, and BLE.
11
12
-**Docs screenshots generate themselves.** The guides now show the real device UI — dashboard, history, topology, node table, diagnostics — rendered during the docs build by driving the actual `app.html` against synthetic API responses. No firmware, no hardware, and nothing committed: the images are rebuilt from the current UI every time, so they cannot drift from it. All data is invented, with IPs and MACs from the IETF documentation ranges and a reserved fake-serial prefix, enforced by a test so a real serial, SSID or address can't reach a published page.
12
13
13
14
### Changed
@@ -17,10 +18,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
17
18
-**New brand mark.** The green-to-blue tile is replaced by an optimizer emitting a telemetry frame, in silicon indigo and amber. It's drawn at three sizes rather than scaled — the device favicon, the device UI sidebar, and the docs — so it stays legible in a browser tab. The docs site had no logo or favicon configured before and now carries the same mark the device serves.
18
19
19
20
### Removed
20
-
-**Boards without PSRAM are no longer offered.** The Config Builder listed the M5Stack AtomS3/AtomS3 Lite and a generic ESP32 DevKit, both of which lack PSRAM — so it could hand you a ready-to-flash YAML for a board the project doesn't support. Both are gone from the builder, along with their `boards/*.yaml` reference configs, and a test now blocks a PSRAM-less board from being added back.
21
+
-**Boards without PSRAM are no longer offered.** The Config Builder listed the M5Stack AtomS3/AtomS3 Lite and a generic ESP32 DevKit, both of which lack PSRAM — so it could hand you a ready-to-flash YAML for a board the project doesn't support. Both are gone from the builder, along with their `boards/*.yaml` reference configs. A test still guards this, but it now enforces the real rule rather than a blanket ban: PSRAM and the web server go together, so a board may be offered without PSRAM only if it is also offered without `tigo_server` — which is what makes the T-CAN485 above possible.
21
22
22
23
### Fixed
24
+
-**The device now tells you why it last restarted.** An unexplained reboot used to leave nothing behind: the reset cause the chip records only reaches the serial console at boot, which nothing is listening to, so an unattended restart was indistinguishable from any other. Diagnostics now shows it under Uptime — "since power-on", "after a crash", "after brownout" — and it is in `/api/status` for the whole time the device stays up, so you can still ask hours later. Abnormal causes are highlighted; a routine reboot or an update is not. This is the difference between "it restarted at some point" and knowing whether to look at your firmware or your power supply: on the reference rig it identified a storm-time restart as a power loss rather than a fault, in a case that had been unexplained for days.
25
+
-**Configs from the Config Builder pulled the wrong branch.** Every generated config fetched the components from `next`, a branch development moved off some time ago, so anyone following the docs got older code than the docs describe — including everything added since. It now pins `main`. Rebuild an existing config to pick this up; nothing on your device changes until you do.
23
26
-**Updating the firmware could crash the device.** The history writer is meant to stand down while an update is being written, since both are writing the same flash chip — but it only checked for an update at the moment it started a snapshot, and a snapshot takes ten to twenty seconds to write. An update beginning during one was ignored until it was too late, and the two collided. Starting an update now waits for any snapshot in progress to finish before transferring, so the collision can't happen. If you update at the wrong moment you'll see a short pause and a "waited N ms" message before the transfer begins; that's the wait working. An update is refused outright if a snapshot somehow doesn't finish within 45 seconds, rather than proceeding into a known crash.
27
+
-**A config without a `text_sensor:` section failed to compile.**`tigo_monitor.h` includes `sensor.h`, `text_sensor.h` and `binary_sensor.h` unconditionally, but ESPHome only copies a component's sources into the build when that component appears in the config — so any YAML that didn't happen to declare all three died on `fatal error: esphome/components/text_sensor/text_sensor.h: No such file or directory`. The documented fix was an empty `text_sensor:` stanza, which you had to already know about to write. `AUTO_LOAD` now pulls the three in whenever `tigo_monitor` is used, so hand-written configs work without the incantation. Existing configs that declare the stanzas are unaffected.
28
+
-**The "Generate YAML Config" button printed a platform name that doesn't exist.** Every generated sensor block said `platform: tigo_server`; the sensor platform is `platform: tigo_monitor`, so pasting the output verbatim failed validation. Harmless on boards with the web UI, where the node table is a page you can read — but on a board without one, that button *is* the panel-discovery workflow, and its output has to paste in clean.
29
+
-**PSRAM allocation no longer probes a heap that isn't there.**`psram_malloc()` tried `heap_caps_malloc(MALLOC_CAP_SPIRAM)` on every allocation and fell back to the internal heap when it failed. On a board with no PSRAM that attempt can never succeed, so it ran — and logged a "PSRAM unavailable" warning for anything over 1 KB — on every single allocation, all day. It is now compiled out when the IDF's `CONFIG_SPIRAM` is unset. No change on boards that have PSRAM.
30
+
- **The flash-write crash appears to be fixed, not just made rarer.** The device now executes from PSRAM (`execute_from_psram`), and firmware configures this itself — you don't have to know it exists. Every history commit takes an ESP-IDF lock that disables the instruction cache across both cores; with code running from flash, that stall raced the WiFi/BLE radio ISRs and faulted. ESP-IDF skips the cache-disable entirely when instructions and read-only data live in PSRAM instead, which removes the race rather than shrinking its window. Previous builds survived 13.5, 42.0 and 14.0 hours before faulting; the current one has run **142 hours and counting** with no movement in any memory watermark, and with the per-panel databases already at full size — so it has been running at the worst-case flash cost the whole time, not easing into it. Costs about 1.7 MB of the 8 MB PSRAM, which moves out of the heap to hold the relocated code. Applies to ESP32-S3 boards with PSRAM configured; the reference config and the Config Builder set the flag explicitly as well.
24
31
25
32
-**History pages no longer fail when a snapshot is being written.** Requests gave up after 15 seconds, which is less than a snapshot takes, so any chart loaded at the wrong moment returned an error. They now wait up to 30 seconds and get their data.
Copy file name to clipboardExpand all lines: CLAUDE.md
+3-1Lines changed: 3 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -62,6 +62,8 @@ PSRAM is required for 15+ devices. Custom STL-compatible allocators (`PSRAMAlloc
62
62
63
63
Always use PSRAM containers for large data. Internal RAM is <200KB; PSRAM is 8MB. Use `#ifdef USE_ESP_IDF` guards for PSRAM types.
64
64
65
+
**Boards without PSRAM** (e.g. `boards/esp32-lilygo-t-can485.yaml`) are supported for *sensors-only* builds — `tigo_monitor` without `tigo_server`. The `psram_*` aliases still compile there; `psram_malloc()` keys off the IDF's `CONFIG_SPIRAM` and resolves straight to the internal heap when it is unset, so no call site changes. Keep it that way: gate on `CONFIG_SPIRAM` inside the allocator rather than sprinkling new `#ifdef`s at usage sites, and never assume `USE_ESP_IDF` implies PSRAM exists. The web server genuinely does require PSRAM and must stay out of those configs.
66
+
65
67
### Web Server
66
68
67
69
5 HTML pages (`/`, `/nodes`, `/status`, `/yaml`, `/cca`) + JSON API endpoints (`/api/devices`, `/api/overview`, `/api/strings`, `/api/status`, `/api/health`, `/api/inverters`, `/api/energy-history`). Auth: `api_token` for API, HTTP Basic for HTML pages. All responses built in PSRAM via `PSRAMString`.
@@ -77,7 +79,7 @@ Fuzzy barcode matching (`match_barcode()`): compares last 6 chars of UART-discov
77
79
-**JSON field naming**: `snake_case` in JSON, `kebab-case` in HTML IDs, `camelCase` in JavaScript
78
80
-**When renaming methods** in `tigo_monitor.h`, update all call sites: header → member variable → web server → Python config → JavaScript
79
81
-**When changing any `/api/*` JSON shape** (rename a field, restructure a response), update `site/screenshots/fixtures.mjs` in the same commit. The docs screenshots are rendered from the real `app.html` against those fixtures at build time — a stale fixture doesn't fail the build, it publishes screenshots of a UI rendering `undefined`. Check with `cd site && npm run screenshots` — which also refreshes the committed README images in `docs/images/`, so commit those too
80
-
-**Sensor/text_sensor/binary_sensor sections**must be declared in YAML (even if empty) or compilation fails with missing header errors
82
+
-**Sensor/text_sensor/binary_sensor**are pulled in by `AUTO_LOAD`in `tigo_monitor/__init__.py`, because `tigo_monitor.h` includes all three unconditionally. YAML no longer needs to declare empty stanzas for them. If you add an unconditional `#include` of another ESPHome component to that header, add it to `AUTO_LOAD` too — otherwise configs that don't happen to use it fail with "No such file or directory". Prefer a `USE_*` guard (as `time`/`button` already do) when the dependency is genuinely optional
81
83
-`CONFIG_UART_ISR_IN_IRAM: "y"` in sdkconfig is required for reduced frame loss
-**⚠ Flash over USB the first time.** The config sets `sram1_as_iram` (+40KB of
57
+
IRAM, which is what pays for `CONFIG_UART_ISR_IN_IRAM`), and that needs an
58
+
ESP-IDF v5.1+ bootloader. A USB flash updates the bootloader automatically; an
59
+
OTA does not, so an OTA-first device will fail to boot.
60
+
-**⚠ Assumes chip revision 3.1.**`minimum_chip_revision: '3.1'` drops the IDF's
61
+
workarounds for older silicon. The bootloader hard-checks it — an older chip
62
+
halts at boot. Your boot log prints `chip revision: v3.1`; lower the value in
63
+
the board file if yours reports less.
64
+
65
+
Because there is no web UI on this board, panel discovery happens through the
66
+
**"Generate YAML Config"** button, which prints a paste-ready `sensor:` block to
67
+
the logs. `example-t-can485.yaml` documents that workflow.
68
+
29
69
## Usage
30
70
31
71
To use a board configuration, include it in your main YAML file:
@@ -47,18 +87,20 @@ Or copy the relevant sections directly into your main configuration file.
47
87
48
88
## GPIO Pin Compatibility
49
89
50
-
All configurations assume the standard Tigo UART connection:
90
+
Most configurations assume the standard Tigo UART connection:
51
91
-**TX Pin**: GPIO1
52
92
-**RX Pin**: GPIO3
53
93
-**Baud Rate**: 38400
54
94
55
-
Adjust these in your main configuration if your hardware differs.
95
+
Adjust these in your main configuration if your hardware differs. Boards with a
96
+
built-in transceiver already set their own pins — the AtomS3R uses GPIO1/GPIO2,
97
+
and the T-CAN485 uses GPIO22/GPIO21 plus three transceiver enable lines.
56
98
57
99
## Performance Notes
58
100
59
101
-**ESP32-P4** offers the best performance with massive PSRAM and higher CPU frequency
60
102
-**ESP32-S3 with PSRAM** is the sweet spot for most installations
61
-
- Boards without PSRAM are not supported — the web server and device tables have nowhere to live and the firmware goes unstable
103
+
-**Boards without PSRAM** work for sensors-only builds that talk to Home Assistant over the native API. Do not add `tigo_server:`to one — it compiles, then fragments the internal heap to OOM under dashboard polling
0 commit comments