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>
45 lines
1.3 KiB
C
45 lines
1.3 KiB
C
/*
|
|
* Rotary encoder: A = GPIO42, B = GPIO4, button on expander P5.
|
|
*
|
|
* Quadrature decoding uses the PCNT peripheral with a glitch filter rather
|
|
* than a polling task. The vendor example polls A in a task and infers
|
|
* direction from B, which drops detents when the UI is busy — not acceptable
|
|
* for a knob that sets a heating setpoint.
|
|
*
|
|
* The button is on the I2C expander, so it has to be polled. 20ms with a
|
|
* 3-sample debounce is well inside human reaction time and costs one short
|
|
* I2C transaction per poll.
|
|
*/
|
|
#pragma once
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
#include "esp_err.h"
|
|
|
|
#define ENCODER_PIN_A 42
|
|
#define ENCODER_PIN_B 4
|
|
|
|
typedef enum {
|
|
ENCODER_BTN_NONE = 0,
|
|
ENCODER_BTN_CLICK,
|
|
ENCODER_BTN_LONG_PRESS,
|
|
} encoder_button_event_t;
|
|
|
|
esp_err_t encoder_init(void);
|
|
|
|
/*
|
|
* Detents accumulated since the last call, positive clockwise. Reading
|
|
* clears the accumulator, so exactly one consumer should call this — the UI.
|
|
*/
|
|
int encoder_take_delta(void);
|
|
|
|
/*
|
|
* Pending button event, cleared by reading. Long press is 800ms; the click is
|
|
* reported on release so the two never both fire.
|
|
*/
|
|
encoder_button_event_t encoder_take_button_event(void);
|
|
|
|
/* Instantaneous button state, for diagnostics. */
|
|
bool encoder_button_is_down(void);
|