Skip to content

Commit ab8be18

Browse files
authored
feat(boards): add the Waveshare ESP32-S3-RS485-CAN (#54)
Requested in #53; already running in the field per #22. ESP32-S3 with 8MB PSRAM and 16MB flash, an isolated RS485 front end, DIN-rail mounting and a 7-36V input — so it can share the CCA's supply instead of needing a USB brick. The full profile fits with room to spare: web UI, the whole 8MB history partition, and BLE without a repartition (the 16MB table's 3MB OTA slots already exceed the 2.25MB the BLE build needs on the 8MB boards). The board's one trap is the transceiver enable. GPIO21 drives DE and /RE of the SP3485EN as a single net, and it floats at reset, so a config that sets only tx_pin/rx_pin comes up with the *receiver* disabled and reads zero bytes off the bus forever — no frames, no checksum errors, just `Buffer: 0 bytes`. That is exactly what #22 looked like. The board file holds the pin low, which also disables the driver: the same hardware read-only guarantee the wiring guide gets from strapping a discrete MAX485, and better than the T-CAN485 can offer. That polarity is the opposite of the T-CAN485's three enables, and the config builder emitted `restore_mode: ALWAYS_ON` unconditionally — which would have generated a config that boots deaf. gpioSwitches entries now carry an optional restore_mode, defaulting to ALWAYS_ON so the T-CAN485 is untouched. Both directions are covered by tests. Verified: `esphome config` and `esphome compile` clean on the example (40.4% of a 3MB slot); 52 site tests pass; docs build with valid links. Not bench-tested here — no maintainer unit — so the pin map rests on Waveshare's schematic plus the working install in #22.
1 parent 24c3445 commit ab8be18

8 files changed

Lines changed: 406 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111
- **LilyGO T-Connect Pro Lite is a supported board.** ESP32-S3 with 8MB PSRAM, 8MB flash and wired Ethernet (W5500) — the full profile, web UI and on-flash history included, on a wired network. `boards/esp32s3-lilygo-t-connect-pro-lite.yaml` plus a ready-to-flash `boards/example-t-connect-pro-lite.yaml`. Contributed by @davidcoulson from a working install ([discussion #30](https://github.com/RAR/esphome-tigomonitor/discussions/30)); the pin map is theirs, and the config compiles clean here.
12+
- **Waveshare ESP32-S3-RS485-CAN is a supported board.** ESP32-S3 with 8MB PSRAM and 16MB flash, an isolated RS485 front end, DIN-rail mounting and a 7-36V input, so it can run off the same supply as the CCA. The full profile fits with room to spare — web UI, the full 8MB history partition, and BLE without a repartition. `boards/esp32s3-waveshare-rs485-can.yaml` plus a ready-to-flash `boards/example-waveshare-rs485-can.yaml`. Verified working by @Brooklyn18m in [#22](https://github.com/RAR/esphome-tigomonitor/issues/22); requested again in [#53](https://github.com/RAR/esphome-tigomonitor/issues/53).
13+
- The board's transceiver enable (GPIO21, DE and /RE on one net) floats at reset, so a config that sets only `tx_pin`/`rx_pin` boots with the **receiver disabled** and reads zero bytes off the bus forever — the exact symptom in #22. The board file holds it low, which also disables the driver: the same hardware read-only guarantee the wiring guide gets from strapping a discrete MAX485.
1214
- **The config builder can generate wired configs.** A board that declares an on-board Ethernet PHY now emits an `ethernet:` block and no `wifi:`/`captive_portal:` at all, and the Wi-Fi fields disappear from the form. Bluetooth is compiled out on this board to buy back flash, so CCA-over-BLE is unavailable there; HTTP CCA import is unaffected.
1315

1416
### Fixed

boards/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,33 @@ Assistant over the native API; it just cannot serve the dashboard.
4141
- **Other on-board hardware** (pins documented in the board file, nothing
4242
instantiated): RS232, CAN, four WS2812 LEDs, two buttons, a relay
4343

44+
#### `esp32s3-waveshare-rs485-can.yaml` - Waveshare ESP32-S3-RS485-CAN (8MB PSRAM, DIN rail)
45+
- **Board**: Waveshare ESP32-S3-RS485-CAN, 16MB flash, 8MB octal PSRAM
46+
- **CPU**: 240MHz
47+
- **Power**: 7-36V DC wide input (or USB-C), DIN-rail mountable — it can run off
48+
the same supply as the CCA rather than a separate USB brick
49+
- **RS485**: on-board *isolated* transceiver (SP3485EN) on GPIO17 (TX) /
50+
GPIO18 (RX), on its own hardware UART, so the USB serial console stays available
51+
- **⚠ The transceiver enable on GPIO21 is not optional.** DE and /RE are tied to
52+
one net that floats at reset, so a config setting only `tx_pin`/`rx_pin` boots
53+
with the **receiver disabled** and reads zero bytes off the bus forever — no
54+
frames, no checksum errors, `Buffer: 0 bytes` ([issue #22](https://github.com/RAR/esphome-tigomonitor/issues/22)).
55+
The board file holds it LOW with a `switch:` entry. That also disables the
56+
driver, which is the same hardware read-only guarantee the wiring guide gets
57+
from strapping a discrete MAX485 — the ESP32 physically cannot transmit onto
58+
the Tigo bus
59+
- **Recommended for**: DIN-rail / cabinet installs that want the full web UI.
60+
PSRAM and 16MB of flash mean `tigo_server`, the full 8MB history partition and
61+
BLE all fit — BLE needs no repartition here, unlike the 8MB boards
62+
- **Ready-to-flash example**: `example-waveshare-rs485-can.yaml`
63+
- **Provenance**: verified working by @Brooklyn18m in
64+
[issue #22](https://github.com/RAR/esphome-tigomonitor/issues/22); pin map from
65+
Waveshare's schematic plus that report. No maintainer unit exists to bench-test
66+
it against, though the config compiles clean here (40.4% of a 3MB slot)
67+
- **Other on-board hardware** (not instantiated): CAN (TWAI) on GPIO15/GPIO16
68+
- **Note**: if you have a variant with 8MB flash rather than 16MB, upload will
69+
refuse the image — switch to `flash_size: 8MB` and `partitions/tigo-8mb.csv`
70+
4471
### ESP32-P4 Boards
4572

4673
#### `esp32p4-evboard.yaml` - ESP32-P4 Evaluation Board (32MB PSRAM)
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Waveshare ESP32-S3-RS485-CAN Configuration
2+
# ESP32-S3, 16MB flash, 8MB octal PSRAM, isolated RS485 + CAN, 7-36V DC input
3+
# Recommended for: DIN-rail installs that want the full web UI
4+
#
5+
# Verified working by @Brooklyn18m in issue #22 (frames flowing, panels
6+
# populating). The maintainer has no unit of this board, so the pin map below
7+
# comes from Waveshare's schematic and that report rather than from bench
8+
# testing here.
9+
#
10+
# WHY THIS BOARD
11+
# --------------
12+
# It is the full-fat profile — PSRAM for `tigo_server`, 16MB of flash for the
13+
# on-device history database — in an industrial package: DIN-rail mountable,
14+
# opto-isolated RS485, and a 7-36V wide input so it can share the Tigo CCA's own
15+
# supply instead of needing a separate USB brick.
16+
#
17+
# THE ENABLE PIN IS THE WHOLE TRICK
18+
# ---------------------------------
19+
# TX/RX alone are not enough here. The SP3485EN's DE and /RE are tied together
20+
# to one net driven by GPIO21, and it floats at reset — so a config that sets
21+
# only tx_pin/rx_pin comes up with the *receiver disabled* and reads zero bytes
22+
# from the bus forever. That is exactly what issue #22 looked like: no frames,
23+
# no checksum errors, `Buffer: 0 bytes`. The `switch:` block below is the fix.
24+
#
25+
# Driving it LOW is also a genuine read-only guarantee, and a stronger one than
26+
# most boards give you. With DE and /RE tied together, low means "receiver on,
27+
# driver off" in hardware — the same strapping the project's wiring guide
28+
# recommends for a discrete MAX485. The ESP32 physically cannot talk onto the
29+
# Tigo bus while that pin is held down, so it can never contend with the CCA.
30+
#
31+
# If you ever see zero bytes after this, the only thing worth trying is
32+
# inverting it (`restore_mode: ALWAYS_ON`) in case your revision buffers the
33+
# net through an inverting isolator.
34+
#
35+
# OTHER ON-BOARD HARDWARE (not configured here, pins for reference)
36+
# -----------------------------------------------------------------
37+
# CAN (TWAI) TX GPIO15, RX GPIO16
38+
#
39+
# Nothing else is instantiated — those pins stay free.
40+
41+
esphome:
42+
name: tigo-server
43+
friendly_name: "Tigo Server"
44+
# allow_partition_access in the OTA config requires ESPHome 2026.5.0+
45+
min_version: "2026.5.0"
46+
47+
esp32:
48+
variant: esp32s3
49+
board: esp32-s3-devkitc-1
50+
# Waveshare ships this board with 16MB. If yours is a variant with 8MB, the
51+
# flash will refuse the image at upload time (a loud failure, not a silent
52+
# one) — drop to `flash_size: 8MB` and `partitions/tigo-8mb.csv`.
53+
flash_size: 16MB
54+
partitions: partitions/tigo-16mb.csv
55+
framework:
56+
type: esp-idf
57+
version: recommended
58+
advanced:
59+
# XIP-from-PSRAM — the fix for the tsdb flash-write crash
60+
# (docs/tsdb-flash-crash-issue.md). Every history commit takes ESP-IDF's
61+
# cross-core cache-disable; with code running from flash that stall raced
62+
# WiFi ISRs and faulted. This flag sets SPIRAM_FETCH_INSTRUCTIONS +
63+
# SPIRAM_RODATA, which compiles the cache-disable out entirely. Costs
64+
# ~1.7 MiB of PSRAM.
65+
execute_from_psram: true
66+
components:
67+
# Time-series database for persistent history. Pinned to a fork by
68+
# immutable SHA — see the long explanation in esp32s3-atoms3r.yaml and
69+
# docs/esp-tsdb-fork.md. A branch can move under a build; a commit cannot.
70+
- name: zakery292/esp_tsdb
71+
source: https://github.com/RAR/esp_tsdb.git
72+
ref: ebfc360f00263ab90116ee3e556a9153ab4041a2
73+
# LittleFS — backing filesystem for the tsdb partition
74+
- joltwallet/littlefs^1.16
75+
sdkconfig_options:
76+
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240: "y"
77+
# Run the UART ISR from IRAM. The single most important setting for frame
78+
# loss — without it the ISR stalls whenever flash is busy and Tigo frames
79+
# get dropped mid-burst.
80+
CONFIG_UART_ISR_IN_IRAM: "y"
81+
CONFIG_UART_RX_BUFFER_SIZE: "2048"
82+
# TX stays small: tigo_monitor only ever listens to the bus.
83+
CONFIG_UART_TX_BUFFER_SIZE: "512"
84+
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE: "32"
85+
CONFIG_LOG_DEFAULT_LEVEL_INFO: "y"
86+
# PSRAM: 8MB octal at 80MHz
87+
CONFIG_SPIRAM_MODE_OCT: "y"
88+
CONFIG_SPIRAM_SPEED_80M: "y"
89+
# WiFi + lwIP buffers into PSRAM to spare internal RAM — prevents
90+
# new-connection resets (OTA / API) once internal RAM gets tight.
91+
CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP: "y"
92+
# TLS buffers to PSRAM. The cloud path re-parses the pinned CA per
93+
# request, which otherwise craters the internal heap floor mid-handshake.
94+
CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC: "y"
95+
# VFS directory operations (stat/unlink/rename/opendir), off by default in
96+
# IDF. esp_tsdb reads database sizes via stat(), so without this the
97+
# History page's size column reads 0 and esp_tsdb's unlink() recovery
98+
# paths silently no-op.
99+
CONFIG_VFS_SUPPORT_DIR: "y"
100+
# More lwIP sockets for the web server + API running together (avoids
101+
# "Failed to create socket" under load, #20).
102+
CONFIG_LWIP_MAX_SOCKETS: "16"
103+
CONFIG_LWIP_MAX_ACTIVE_TCP: "16"
104+
CONFIG_LWIP_MAX_LISTENING_TCP: "16"
105+
106+
# Enable PSRAM
107+
psram:
108+
mode: octal
109+
speed: 80MHz
110+
111+
# --- RS485 transceiver enable ------------------------------------------------
112+
# SP3485EN, DE and /RE tied to one net on GPIO21. LOW = receive (driver off).
113+
# `internal: true` keeps it out of Home Assistant — it is board wiring, not a
114+
# control anyone should be able to flip from a dashboard. Read the long note at
115+
# the top of this file before touching it: turning this on enables the driver.
116+
switch:
117+
- platform: gpio
118+
id: rs485_enable
119+
name: "RS485 Transceiver Enable"
120+
internal: true
121+
pin: GPIO21 # DE + /RE, low => receiver on and driver off
122+
restore_mode: ALWAYS_OFF
123+
124+
# --- Tigo bus ----------------------------------------------------------------
125+
# The board's isolated RS485 transceiver, on its own hardware UART. That leaves
126+
# the USB console free, so keep `logger:` on its defaults — do NOT set
127+
# `baud_rate: 0`.
128+
uart:
129+
id: tigo_uart
130+
tx_pin: GPIO17
131+
rx_pin: GPIO18
132+
baud_rate: 38400
133+
data_bits: 8
134+
parity: NONE
135+
stop_bits: 1
136+
rx_buffer_size: 2048
137+
138+
# --- Tigo Monitor ------------------------------------------------------------
139+
tigo_monitor:
140+
id: tigo_hub
141+
uart_id: tigo_uart
142+
# Device and node tables live in PSRAM here, so this is cheap to raise. Set it
143+
# to your optimizer count.
144+
number_of_devices: 30
145+
update_interval: 30s
146+
147+
# --- Web server --------------------------------------------------------------
148+
tigo_server:
149+
id: tigo_web
150+
tigo_monitor_id: tigo_hub
151+
port: 80
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Example Tigo Monitor Configuration using the Waveshare ESP32-S3-RS485-CAN
2+
#
3+
# This is the "industrial, full features" profile: web UI, REST API, on-flash
4+
# history, and Home Assistant over the native API — on a DIN-rail board that can
5+
# run off the same 7-36V supply as the rest of the cabinet.
6+
#
7+
# The hardware base (esp32s3-waveshare-rs485-can.yaml) provides:
8+
# - the ESP32-S3 / 16MB flash / 8MB PSRAM setup and the tsdb history storage
9+
# - the GPIO21 transceiver-enable switch, without which the board reads
10+
# nothing off the bus (see the note at the top of that file)
11+
# - the UART on GPIO17/18, the tigo_monitor hub, and tigo_server
12+
# so this file only layers on credentials, sensors, and app config.
13+
#
14+
# Panel addresses come from the web UI once it is up (Nodes view), or from the
15+
# "Generate YAML Config" button below if you would rather work from the logs.
16+
17+
packages:
18+
board: !include esp32s3-waveshare-rs485-can.yaml
19+
20+
external_components:
21+
- source:
22+
type: git
23+
url: https://github.com/RAR/esphome-tigomonitor
24+
ref: main
25+
components: [tigo_monitor, tigo_server]
26+
27+
logger:
28+
level: INFO # Change to DEBUG if you need detailed logs for troubleshooting
29+
30+
wifi:
31+
ssid: !secret wifi_ssid
32+
password: !secret wifi_password
33+
34+
captive_portal:
35+
36+
api:
37+
38+
ota:
39+
- platform: esphome
40+
password: "YOUR_OTA_PASSWORD"
41+
# Permits a later partition-table change over the air (ESPHome 2026.5.0+).
42+
# It is only the permission: applying one still takes an explicit
43+
# `esphome upload --partition-table`. A plain `esphome run` never sends a
44+
# partition table, and succeeds without changing the layout.
45+
allow_partition_access: true
46+
47+
# Timestamps for the history database. Any time source works; SNTP needs no
48+
# Home Assistant connection.
49+
time:
50+
- platform: sntp
51+
id: sntp_time
52+
timezone: "America/New_York" # Adjust for your timezone
53+
54+
button:
55+
- platform: tigo_monitor
56+
name: "Generate YAML Config"
57+
id: generate_yaml_button
58+
tigo_monitor_id: tigo_hub
59+
button_type: yaml_generator
60+
- platform: tigo_monitor
61+
name: "Print Device Mappings"
62+
id: device_mappings_button
63+
tigo_monitor_id: tigo_hub
64+
button_type: device_mappings
65+
- platform: tigo_monitor
66+
name: "Reset Node Table"
67+
id: reset_node_table_button
68+
tigo_monitor_id: tigo_hub
69+
button_type: reset_node_table
70+
71+
sensor:
72+
# System-wide sensors — these need no addresses and work from first boot.
73+
- platform: tigo_monitor
74+
tigo_monitor_id: tigo_hub
75+
name: "Total System Power"
76+
id: total_system_power
77+
78+
- platform: tigo_monitor
79+
tigo_monitor_id: tigo_hub
80+
name: "Total System Energy"
81+
id: total_system_energy
82+
83+
- platform: tigo_monitor
84+
tigo_monitor_id: tigo_hub
85+
name: "Active Device Count"
86+
87+
- platform: tigo_monitor
88+
tigo_monitor_id: tigo_hub
89+
name: "Missed Frame Count"
90+
91+
- platform: tigo_monitor
92+
tigo_monitor_id: tigo_hub
93+
name: "Free Internal RAM"
94+
95+
- platform: tigo_monitor
96+
tigo_monitor_id: tigo_hub
97+
name: "Free PSRAM"
98+
99+
- platform: uptime
100+
name: "Tigo Server Uptime"
101+
102+
- platform: wifi_signal
103+
name: "Tigo Server WiFi Signal"
104+
update_interval: 60s
105+
106+
# Per-panel sensors go here. An entry needs `address:` AND at least one
107+
# measurement sub-key — address and name alone create no entities:
108+
#
109+
# - platform: tigo_monitor
110+
# tigo_monitor_id: tigo_hub
111+
# address: "1234"
112+
# name: "Panel A1"
113+
# power: {}
114+
# voltage_in: {}
115+
# current_in: {}
116+
# temperature: {}
117+
118+
text_sensor:
119+
- platform: wifi_info
120+
ip_address:
121+
name: "Tigo Server IP Address"
122+
entity_category: diagnostic
123+
124+
- platform: version
125+
name: "Tigo Server ESPHome Version"
126+
127+
binary_sensor:
128+
- platform: status
129+
name: "Tigo Server Status"
130+
131+
- platform: tigo_monitor
132+
tigo_monitor_id: tigo_hub
133+
night_mode:
134+
name: "Solar Night Mode"
135+
136+
switch:
137+
- platform: restart
138+
name: "Tigo Server Restart"

0 commit comments

Comments
 (0)