Thermostat fed by Home Assistant
A wall thermostat on the Olimex ESP32-EVB with its 2.8" glass — with one twist: the room temperature does not come from a wire, it comes from Home Assistant, read off a Zigbee sensor somewhere in the house. The board's own relay switches the heating; two small HA automations feed it the temperature and a heartbeat; a ladder watchdog decides how much to trust them.
| Value | |
|---|---|
| Board | Olimex ESP32-EVB + MOD-LCD2.8RTP (240×320, portrait) |
| Heating | the board's relay 1 (REL1) |
| Temperature | a Zigbee sensor, through Home Assistant and MQTT |
| Project file | ha_thermostat_evb.yaml |
The project is installed with the editor in tutorials\ — open it
while reading. The general route of connecting a board to HA (broker,
discovery, what a variable becomes) is on
Home Assistant; this page builds a complete
working system on top of it.
The design decision worth copying
The temperature arrives over WiFi, ethernet, a broker and a radio —
any link in that chain can die silently. So the board never trusts it
blindly: remote data gets a watchdog. Home Assistant toggles a
ha_beat variable once a minute; the ladder counts the silence, and
three minutes without a beat means the heating goes OFF, a banner
appears on the glass and an alarm lands in the table. Manual mode
keeps working without HA on purpose — the person standing at the
equipment outranks the network.
The same shape protects drive positioning against stale encoder data — it is a doctrine, not a trick for this page.
What you create, step by step
1. Project and board
New project, board Olimex ESP32-EVB, resolution 240×320 portrait.
Ethernet needs no configuration — the board's catalog starts the
LAN8710 with DHCP by itself.
2. WEBview off — deliberately
On the WEBview page, untick WEBview on. This classic board has no PSRAM, and the HTTP/WS server and the MQTT client do not fit its internal RAM together — the editor's memory tiers would skip MQTT and say so in the boot log. Here the choice is easy: the operator has the glass, and the remote view is Home Assistant. The server's memory pays for the MQTT client.
webview:
enabled: false
3. The variables
| Variable | Type | Who writes it | Why |
|---|---|---|---|
ha_temp |
f32 °C | Home Assistant | the Zigbee reading |
ha_beat |
bool | Home Assistant | the heartbeat, toggled every minute |
ha_ok |
bool | the ladder | the watchdog verdict |
temp_sp |
f32 °C, retentive | the glass (and HA) | setpoint, survives power loss |
auto_mode |
bool, retentive | the glass (and HA) | Auto / Manual |
rel1 |
bool, GPIO 32 | the ladder | the real heating relay |
rel1_cmd |
bool | the glass | manual heat request |
4. MQTT
Broker = the machine running Mosquitto (the HA add-on), one topic per variable, discovery on:
mqtt:
broker: 192.168.0.53
username: ctrl32
password: ctrl32mqtt
client_id: ctrl32-evbtherm
topic: ctrl32/evbtherm
publish_mode: per_tag
discovery: true
interval_ms: 2000
tags:
- { name: ha_temp, write: true }
- { name: ha_beat, write: true }
- { name: temp_sp, write: true }
- { name: auto_mode, write: true }
- rel1
- ha_ok
write is the permission: only these four may be changed from the
broker side. rel1 and ha_ok are read-only sensors in HA — the
outside world watches the relay, it never drives it.
5. The ladder
Five things, one rung each (open the project to see them drawn):
- Heartbeat — a 1 Hz BLINK for the lamp in the corner.
- The watchdog — R_TRIG + F_TRIG on
ha_beatmake one pulse per edge; the pulse feeds a TOF with PT = 180 000 ms. Its Q isha_ok: any beat restarts the three minutes. - Hysteresis comparator — HYST on
ha_temparoundtemp_sp(0.5 °C band). A bare comparison would chatter the relay. - The relay rung —
(auto AND cold AND ha_ok) OR (manual AND command)into therel1coil. Note whereha_oksits: it gates only the automatic branch. - The setpoint spinner — R_TRIG per button press, SEL chain, LIMIT 15–30 °C clamped last.
6. The screens
Three portrait screens: the dial (two nested arcs — blue room
temperature, orange setpoint), Manual, and a two-curve Trend
(ha_temp + temp_sp). The watchdog banner is one label with
visible_if: n_haok — drawn only while HA is silent, exactly like
the states and motion page describes.
7. Download
F5 over USB. The boot log prints the MQTT connection, and within seconds Home Assistant shows a new device ctrl32-evbtherm with six entities.
The Home Assistant half
Two automations — create them in HA (Settings → Automations, or paste
into automations.yaml). First the temperature: every change of the
Zigbee sensor (and every 5 minutes as a refresh) is written into the
board's ha_temp number entity:
alias: ctrl32 EVB thermostat - temperature from Zigbee
trigger:
- platform: state
entity_id: sensor.tuya_teplota # your sensor's entity id
- platform: time_pattern
minutes: "/5"
condition:
- condition: numeric_state
entity_id: sensor.tuya_teplota
above: -40
below: 80
action:
- service: number.set_value
target:
entity_id: number.ctrl32_evbtherm_ha_temp
data:
value: "{{ states('sensor.tuya_teplota') | float }}"
mode: single
The condition matters: a battery Zigbee sensor reports unavailable
while it sleeps, and writing that into a number would fail — the
guard lets only a sane reading through.
Then the heartbeat — the whole automation is one toggle:
alias: ctrl32 EVB thermostat - heartbeat
trigger:
- platform: time_pattern
minutes: "/1"
action:
- service: switch.toggle
target:
entity_id: switch.ctrl32_evbtherm_ha_beat
mode: single
Proving it
- Watch the dial follow the sensor; hold the sensor in your hand and the number climbs.
- Kill the beat (disable the heartbeat automation): after three minutes the banner appears, the alarm fires, the relay drops — and Manual still heats.
- Change the setpoint from HA (the
temp_spnumber): the dial's orange arc moves on the glass. The setpoint is retentive — pull the plug, it comes back.
When it does not work
| Symptom | Look at |
|---|---|
| no device in HA | the boot log's mqtt: lines — broker address, credentials; discovery must be on |
mqtt: skipped (memory tier…) in the log |
WEBview was left ON — step 2 of this page |
| entities exist, temperature never moves | the automation's sensor entity id, and whether the sensor is unavailable |
| heating never starts in Auto | ha_ok — no heartbeat automation, or HA down; the banner says so |