Implement rotary thermostat firmware from PRD

ESP-IDF v5.3.2 firmware for the CrowPanel 2.1" rotary display. Builds clean;
nothing has been run on hardware yet.

Hardware verified against Elecrow's own source for this board and
cross-checked against their ESPHome config, per PRD 8.1. Three corrections to
the PRD's pinout, all documented in docs/HARDWARE.md:

  - touch is CST826 at 0x15, not CST816 (chip ID reads 0x11, not 0xB4)
  - the ST7701S needs a 3-wire SPI bus on GPIO 16/2/1 that the PRD omitted;
    without it the init sequence cannot be sent and the panel stays black
  - the backlight is GPIO6

Two further departures from the PRD, both forced by facts on the ground:

  - Transport is MQTT, not HTTP. zigbee2mqtt has no REST endpoint for live
    sensor state. Subscribing to the broker also gets retained topics, so
    every room's last reading arrives on connect rather than after a poll.
  - The furnace is 2-wire R/W with no C, not 3-wire. The board needs its own
    5V supply. The thermostat's 5 CPH setting is enforced as a minimum
    12-minute period between heat-call starts, which a minimum off-time alone
    does not achieve.

All of PRD section 5 is implemented in the relay layer itself: fail-open,
watchdog-gated output, max on-time, min off-time, cycle-rate limit, startup
lockout, and the stale-data fallback ladder down to open-loop freeze
protection. The one gap that cannot be closed in software — the PCF8574 not
resetting when the ESP32 does — is documented rather than papered over.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mattandClaude Opus 5 committed 2026-08-03 19:47:00 -07:00
1 parent 393592e327
commit d2e4c974dd
35 files changed
+4685

No files matched your search

+33
View File
@@ -0,0 +1,33 @@
/*
* lvgl_port — tick, service task, and locking.
*
* LVGL is not thread-safe. Everything that touches an lv_* object must hold
* the lock, including code called from timers and event handlers that runs on
* other tasks. The lock is recursive so a UI callback can re-enter without
* deadlocking itself.
*
* Rendering uses the panel's two framebuffers directly in LVGL's FULL refresh
* mode: LVGL draws a whole frame into the back buffer, flush hands it to
* esp_lcd_panel_draw_bitmap(), and the RGB driver swaps buffers rather than
* copying. Nothing ever writes into the buffer being scanned out, which is
* what PRD 8.2's warning about persistent framebuffer corruption is about.
*/
#pragma once
#include <stdbool.h>
#include "esp_err.h"
#include "lvgl.h"
/* Requires display_init(). Creates the display, input devices and task. */
esp_err_t lvgl_port_init(void);
/*
* Take the LVGL lock. timeout_ms < 0 waits forever.
* Every lv_* call from outside a UI callback must be wrapped in this.
*/
bool lvgl_port_lock(int timeout_ms);
void lvgl_port_unlock(void);
/* The display LVGL is driving. */
lv_display_t *lvgl_port_display(void);