Compare commits
4 Commits
4e963b97b0
...
c9232ee477
| Author | SHA1 | Date | |
|---|---|---|---|
| c9232ee477 | |||
| bc47200e38 | |||
| 3d4eb21a88 | |||
| 9476ac9682 |
36
sketches/doorbell-touch/.clangd
Normal file
36
sketches/doorbell-touch/.clangd
Normal file
@@ -0,0 +1,36 @@
|
||||
CompileFlags:
|
||||
Add:
|
||||
- "-std=c++17"
|
||||
- "-DARDUINO=200"
|
||||
- "-DESP32"
|
||||
- "-DCORE_DEBUG_LEVEL=0"
|
||||
- "-DBOARD_HAS_PSRAM"
|
||||
- "-DLGFX_USE_V1"
|
||||
- "-DDEBUG_MODE"
|
||||
- "-I/home/david/.arduino15/packages/esp32/hardware/esp32/3.3.6/cores/esp32"
|
||||
- "-I/home/david/.arduino15/packages/esp32/hardware/esp32/3.3.6/tools"
|
||||
- "-I/home/david/.arduino15/packages/esp32/hardware/esp32/3.3.6/libraries"
|
||||
- "-I/home/david/.arduino15/packages/esp32/tools/esp32-libs/3.3.6/include"
|
||||
- "-I/home/david/.arduino15/packages/esp32/tools/esp32-libs/3.3.6/include/freertos/FreeRTOS-Kernel/include"
|
||||
- "-I/home/david/.arduino15/packages/esp32/tools/esp32-libs/3.3.6/include/freertos/config/include/freertos"
|
||||
- "-I/home/david/.arduino15/packages/esp32/tools/esp32-libs/3.3.6/include/freertos/config/include"
|
||||
- "-I/home/david/.arduino15/packages/arduino/hardware/arduino/1.8.6/cores/arduino"
|
||||
- "-I/home/david/.arduino15/packages/arduino/hardware/arduino/1.8.6/libraries/WiFi/src"
|
||||
- "-I/home/david/Arduino/sketchbook/libraries/ArduinoJson/src"
|
||||
- "-I/home/david/Arduino/sketchbook/libraries/NTPClient"
|
||||
- "-I/home/david/Arduino/sketches/doorbell-touch/libraries/KlubhausCore/src"
|
||||
- "-I/home/david/Arduino/sketches/doorbell-touch/boards/esp32-s3-lcd-43"
|
||||
- "-I/home/david/Arduino/sketches/doorbell-touch/vendor/esp32-s3-lcd-43/LovyanGFX/src/lgfx"
|
||||
- "-I/home/david/Arduino/sketches/doorbell-touch/vendor/esp32-s3-lcd-43/LovyanGFX/src/lgfx/v0"
|
||||
- "-I/home/david/Arduino/sketches/doorbell-touch/vendor/esp32-s3-lcd-43/LovyanGFX/src/lgfx/v0/platforms/esp32"
|
||||
- "-I/home/david/Arduino/sketches/doorbell-touch/boards/esp32-32e"
|
||||
- "-I/home/david/Arduino/sketches/doorbell-touch/boards/esp32-32e-4"
|
||||
- "-I/home/david/Arduino/sketches/doorbell-touch/vendor/esp32-32e/TFT_eSPI"
|
||||
- "-I/home/david/Arduino/sketches/doorbell-touch/vendor/esp32-32e-4/TFT_eSPI"
|
||||
- "-I/home/david/Arduino/sketchbook/libraries/XPT2046_Touchscreen"
|
||||
|
||||
Diagnostics:
|
||||
ClangTidy:
|
||||
Remove: [readability-*, modernize-*, performance-*, bugprone-*]
|
||||
Add: [clang-diagnostic-*, modernize-use-trailing-return-type]
|
||||
UnusedIncludes: Strict
|
||||
1
sketches/doorbell-touch/.gitignore
vendored
1
sketches/doorbell-touch/.gitignore
vendored
@@ -6,6 +6,7 @@
|
||||
|
||||
# Vendored libraries (re-created by install-libs)
|
||||
vendor/esp32-32e/TFT_eSPI/
|
||||
vendor/esp32-32e-4/TFT_eSPI/
|
||||
vendor/esp32-s3-lcd-43/GFX_Library_for_Arduino/
|
||||
|
||||
# IDE
|
||||
|
||||
158
sketches/doorbell-touch/AGENTS.md
Normal file
158
sketches/doorbell-touch/AGENTS.md
Normal file
@@ -0,0 +1,158 @@
|
||||
# AGENTS.md — Klubhaus Doorbell
|
||||
|
||||
Multi-target Arduino/ESP32 doorbell alert system using ntfy.sh.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Three board targets share business logic via a common library:
|
||||
|
||||
| Board | Display | Library | Build Target |
|
||||
|-------|---------|---------|--------------|
|
||||
| ESP32-32E | SPI TFT 320x240 (ILI9341) | TFT_eSPI | `compile-32e` |
|
||||
| ESP32-32E-4" | SPI TFT 320x480 (ST7796) | TFT_eSPI | `compile-32e-4` |
|
||||
| ESP32-S3-Touch-LCD-4.3 | 800x480 RGB parallel | LovyanGFX | `compile-s3-43` |
|
||||
|
||||
## Essential Commands
|
||||
|
||||
All commands run via **mise**:
|
||||
|
||||
```bash
|
||||
# Install all dependencies (shared libs + vendored display libs)
|
||||
mise run install-libs
|
||||
|
||||
# Build ESP32-32E
|
||||
mise run compile-32e
|
||||
mise run upload-32e
|
||||
mise run monitor-32e
|
||||
|
||||
# Build ESP32-S3-LCD-4.3
|
||||
mise run compile-s3-43
|
||||
mise run upload-s3-43
|
||||
mise run monitor-s3-43
|
||||
|
||||
# Format code
|
||||
mise run format
|
||||
|
||||
# Clean build artifacts
|
||||
mise run clean
|
||||
```
|
||||
|
||||
**Upload port override**: `PORT=/dev/ttyXXX mise run upload-32e`
|
||||
|
||||
**Prerequisites**: arduino-cli with `esp32:esp32` platform installed, mise.
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
libraries/KlubhausCore/src/ Shared Arduino library
|
||||
├── KlubhausCore.h Umbrella include (board sketches use this)
|
||||
├── Config.h Constants, timing, WiFiCred struct
|
||||
├── ScreenState.h State enums/structs
|
||||
├── IDisplayDriver.h Pure virtual display interface
|
||||
├── DisplayManager.h Thin wrapper delegating to IDisplayDriver
|
||||
├── NetManager.* WiFi, HTTP, NTP
|
||||
└── DoorbellLogic.* State machine, ntfy polling
|
||||
|
||||
boards/
|
||||
├── esp32-32e/
|
||||
│ ├── esp32-32e.ino Main sketch
|
||||
│ ├── board_config.h Board-specific config
|
||||
│ ├── secrets.h WiFi creds (gitignored, copy from .example)
|
||||
│ ├── tft_user_setup.h TFT_eSPI config
|
||||
│ └── DisplayDriverTFT.* Concrete IDisplayDriver for TFT
|
||||
└── esp32-s3-lcd-43/
|
||||
├── esp32-s3-lcd-43.ino Main sketch
|
||||
├── board_config.h Board-specific config
|
||||
├── secrets.h WiFi creds (gitignored, copy from .example)
|
||||
├── LovyanPins.h Pin definitions
|
||||
└── DisplayDriverGFX.* Concrete IDisplayDriver for LovyanGFX
|
||||
|
||||
vendor/ Vendored display libs (recreated by install-libs)
|
||||
├── esp32-32e/TFT_eSPI/
|
||||
└── esp32-s3-lcd-43/LovyanGFX/
|
||||
```
|
||||
|
||||
## Code Patterns
|
||||
|
||||
### Header Guards
|
||||
Use `#pragma once` (not `#ifndef` guards).
|
||||
|
||||
### Formatting
|
||||
- 4-space indentation, no tabs
|
||||
- WebKit-based style (see `.clang-format`)
|
||||
- Column limit: 100
|
||||
- Opening brace stays on same line (`BreakBeforeBraces: Attach`)
|
||||
|
||||
### Class Design
|
||||
- Pure virtual `IDisplayDriver` interface in shared library
|
||||
- Each board implements a concrete driver (e.g., `DisplayDriverTFT`, `DisplayDriverGFX`)
|
||||
- `DisplayManager` delegates to `IDisplayDriver` — no display-lib coupling in shared code
|
||||
|
||||
### Arduino Patterns
|
||||
- `setup()` runs once at boot — call `begin()` on managers
|
||||
- `loop()` runs continuously — call `update()` on managers
|
||||
- Use `millis()` for timing (not `delay()`)
|
||||
- Serial console at 115200 baud for debug commands
|
||||
|
||||
## Testing/Debugging
|
||||
|
||||
**Serial commands** (type into serial monitor at 115200 baud):
|
||||
|
||||
| Command | Action |
|
||||
|---------|--------|
|
||||
| `alert` | Trigger a test alert |
|
||||
| `silence` | Silence current alert |
|
||||
| `dashboard` | Show dashboard screen |
|
||||
| `off` | Turn off display |
|
||||
| `status` | Print state + memory info |
|
||||
| `reboot` | Restart device |
|
||||
|
||||
## Gotchas
|
||||
|
||||
1. **secrets.h is gitignored**: Copy from `.example` before building:
|
||||
```bash
|
||||
cp boards/esp32-32e/secrets.h.example boards/esp32-32e/secrets.h
|
||||
cp boards/esp32-s3-lcd-43/secrets.h.example boards/esp32-s3-lcd-43/secrets.h
|
||||
```
|
||||
|
||||
2. **Display libs are vendored**: Each board uses a different display library. The build system uses `--libraries` to link only the board's vendored lib — never link both TFT_eSPI and LovyanGFX in the same build.
|
||||
|
||||
3. **No unit tests**: This is an embedded Arduino sketch — no test suite exists. Verify changes by building and deploying to hardware.
|
||||
|
||||
4. **LSP errors are expected**: The LSP cannot find `Arduino.h` because it requires arduino-cli to resolve. Ignore clangd/IntelliSense errors about missing Arduino types; builds work correctly via arduino-cli.
|
||||
|
||||
5. **Build artifacts in board dirs**: Build output goes to `boards/[board]/build/` — cleaned by `mise run clean`.
|
||||
|
||||
6. **WiFi credentials are per-board**: Each board directory has its own `secrets.h` because boards may be on different networks.
|
||||
|
||||
## Hardware Research Log
|
||||
|
||||
### Hosyond ESP32-32E 4" (320x480) - Planned
|
||||
|
||||
**Source**: https://www.lcdwiki.com/4.0inch_ESP32-32E_Display
|
||||
|
||||
| Spec | Value |
|
||||
|------|-------|
|
||||
| Display Controller | ST7796S |
|
||||
| Resolution | 320x480 |
|
||||
| Touch | XPT2046 (resistive) |
|
||||
| Library | TFT_eSPI V2.5.43 (same as 32E) |
|
||||
|
||||
**GPIO Pinout**:
|
||||
|
||||
| Function | GPIO |
|
||||
|----------|------|
|
||||
| LCD CS | 15 |
|
||||
| LCD DC | 2 |
|
||||
| LCD MOSI | 13 |
|
||||
| LCD SCLK | 14 |
|
||||
| LCD RST | EN |
|
||||
| LCD BL | 27 |
|
||||
| Touch CS | 33 |
|
||||
| Touch IRQ | 36 |
|
||||
|
||||
**Quirks**:
|
||||
- SPI pins shared between LCD and touch
|
||||
- Touch IRQ on IO36 (input-only) triggers LOW on touch
|
||||
- Backlight on IO27 (HIGH = on)
|
||||
- Common anode RGB LEDs on IO16, IO17, IO22 (LOW = on)
|
||||
191
sketches/doorbell-touch/boards/esp32-32e-4/DisplayDriverTFT.cpp
Normal file
191
sketches/doorbell-touch/boards/esp32-32e-4/DisplayDriverTFT.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
#include "DisplayDriverTFT.h"
|
||||
|
||||
void DisplayDriverTFT::begin() {
|
||||
// Backlight
|
||||
pinMode(PIN_LCD_BL, OUTPUT);
|
||||
digitalWrite(PIN_LCD_BL, LOW);
|
||||
|
||||
_tft.init();
|
||||
_tft.setRotation(DISPLAY_ROTATION);
|
||||
_tft.fillScreen(TFT_BLACK);
|
||||
|
||||
Serial.printf("[GFX] Display OK: %dx%d\n", DISPLAY_WIDTH, DISPLAY_HEIGHT);
|
||||
Serial.flush();
|
||||
|
||||
// Debug: check if touch controller is responding
|
||||
uint16_t z = _tft.getTouchRawZ();
|
||||
Serial.printf("[TOUCH] Raw Z=%d (non-zero = controller detected)\n", z);
|
||||
Serial.flush();
|
||||
|
||||
drawBoot();
|
||||
|
||||
digitalWrite(PIN_LCD_BL, HIGH);
|
||||
Serial.println("[GFX] Backlight ON");
|
||||
Serial.flush();
|
||||
}
|
||||
|
||||
void DisplayDriverTFT::setBacklight(bool on) { digitalWrite(PIN_LCD_BL, on ? HIGH : LOW); }
|
||||
|
||||
// ── Rendering ───────────────────────────────────────────────
|
||||
|
||||
void DisplayDriverTFT::render(const ScreenState& st) {
|
||||
if(st.screen != _lastScreen) {
|
||||
_needsRedraw = true;
|
||||
_lastScreen = st.screen;
|
||||
}
|
||||
|
||||
switch(st.screen) {
|
||||
case ScreenID::BOOT:
|
||||
if(_needsRedraw) {
|
||||
drawBoot();
|
||||
_needsRedraw = false;
|
||||
}
|
||||
break;
|
||||
case ScreenID::ALERT:
|
||||
drawAlert(st);
|
||||
break;
|
||||
|
||||
case ScreenID::DASHBOARD:
|
||||
if(_needsRedraw) {
|
||||
drawDashboard(st);
|
||||
_needsRedraw = false;
|
||||
}
|
||||
break;
|
||||
case ScreenID::OFF:
|
||||
if(_needsRedraw) {
|
||||
_tft.fillScreen(TFT_BLACK);
|
||||
_needsRedraw = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayDriverTFT::drawBoot() {
|
||||
_tft.fillScreen(TFT_BLACK);
|
||||
_tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
_tft.setTextSize(2);
|
||||
_tft.setCursor(10, 10);
|
||||
_tft.printf("KLUBHAUS v%s", FW_VERSION);
|
||||
_tft.setTextSize(1);
|
||||
_tft.setCursor(10, 40);
|
||||
_tft.print(BOARD_NAME);
|
||||
_tft.setCursor(10, 60);
|
||||
_tft.print("Booting...");
|
||||
}
|
||||
|
||||
void DisplayDriverTFT::drawAlert(const ScreenState& st) {
|
||||
uint32_t elapsed = millis() - st.alertStartMs;
|
||||
uint8_t pulse = 180 + (uint8_t)(75.0f * sinf(elapsed / 300.0f));
|
||||
uint16_t bg = _tft.color565(pulse, 0, 0);
|
||||
|
||||
_tft.fillScreen(bg);
|
||||
_tft.setTextColor(TFT_WHITE, bg);
|
||||
|
||||
_tft.setTextSize(3);
|
||||
_tft.setCursor(10, 20);
|
||||
_tft.print(st.alertTitle.length() > 0 ? st.alertTitle : "ALERT");
|
||||
|
||||
_tft.setTextSize(2);
|
||||
_tft.setCursor(10, 80);
|
||||
_tft.print(st.alertBody);
|
||||
|
||||
_tft.setTextSize(1);
|
||||
_tft.setCursor(10, DISPLAY_HEIGHT - 20);
|
||||
_tft.print("Hold to silence...");
|
||||
}
|
||||
|
||||
void DisplayDriverTFT::drawDashboard(const ScreenState& st) {
|
||||
_tft.fillScreen(TFT_BLACK);
|
||||
_tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
|
||||
_tft.setTextSize(1);
|
||||
_tft.setCursor(5, 5);
|
||||
_tft.printf("KLUBHAUS — %s", deviceStateStr(st.deviceState));
|
||||
|
||||
int y = 30;
|
||||
_tft.setCursor(5, y);
|
||||
y += 18;
|
||||
_tft.printf("WiFi: %s %ddBm", st.wifiSsid.c_str(), st.wifiRssi);
|
||||
|
||||
_tft.setCursor(5, y);
|
||||
y += 18;
|
||||
_tft.printf("IP: %s", st.ipAddr.c_str());
|
||||
|
||||
_tft.setCursor(5, y);
|
||||
y += 18;
|
||||
_tft.printf("Up: %lus Heap: %d", st.uptimeMs / 1000, ESP.getFreeHeap());
|
||||
|
||||
_tft.setCursor(5, y);
|
||||
y += 18;
|
||||
_tft.printf("Last poll: %lus ago", st.lastPollMs > 0 ? (millis() - st.lastPollMs) / 1000 : 0);
|
||||
}
|
||||
|
||||
// ── Touch ───────────────────────────────────────────────────
|
||||
|
||||
TouchEvent DisplayDriverTFT::readTouch() {
|
||||
TouchEvent evt;
|
||||
uint16_t tx, ty;
|
||||
uint8_t touched = _tft.getTouch(&tx, &ty, 100);
|
||||
if(touched) {
|
||||
evt.pressed = true;
|
||||
evt.x = tx;
|
||||
evt.y = ty;
|
||||
Serial.printf("[TOUCH] x=%d, y=%d\n", tx, ty);
|
||||
}
|
||||
return evt;
|
||||
}
|
||||
|
||||
uint16_t DisplayDriverTFT::getRawTouchZ() {
|
||||
return _tft.getTouchRawZ();
|
||||
}
|
||||
|
||||
int DisplayDriverTFT::dashboardTouch(int x, int y) {
|
||||
// 2x2 grid, accounting for 30px header
|
||||
if(y < 30)
|
||||
return -1;
|
||||
|
||||
int col = (x * 2) / DISPLAY_WIDTH; // 0 or 1
|
||||
int row = ((y - 30) * 2) / (DISPLAY_HEIGHT - 30); // 0 or 1
|
||||
|
||||
if(col < 0 || col > 1 || row < 0 || row > 1)
|
||||
return -1;
|
||||
|
||||
return row * 2 + col; // 0, 1, 2, or 3
|
||||
}
|
||||
|
||||
HoldState DisplayDriverTFT::updateHold(unsigned long holdMs) {
|
||||
HoldState h;
|
||||
TouchEvent t = readTouch();
|
||||
|
||||
if(t.pressed) {
|
||||
if(!_holdActive) {
|
||||
_holdActive = true;
|
||||
_holdStartMs = millis();
|
||||
h.started = true;
|
||||
}
|
||||
uint32_t held = millis() - _holdStartMs;
|
||||
h.active = true;
|
||||
h.progress = constrain((float)held / (float)holdMs, 0.0f, 1.0f);
|
||||
h.completed = (held >= holdMs);
|
||||
|
||||
// Simple progress bar at bottom of screen
|
||||
int barW = (int)(DISPLAY_WIDTH * h.progress);
|
||||
_tft.fillRect(0, DISPLAY_HEIGHT - 8, barW, 8, TFT_WHITE);
|
||||
_tft.fillRect(barW, DISPLAY_HEIGHT - 8, DISPLAY_WIDTH - barW, 8, TFT_DARKGREY);
|
||||
} else {
|
||||
if(_holdActive) {
|
||||
// Clear the progress bar when released
|
||||
_tft.fillRect(0, DISPLAY_HEIGHT - 8, DISPLAY_WIDTH, 8, TFT_DARKGREY);
|
||||
}
|
||||
_holdActive = false;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
void DisplayDriverTFT::updateHint(int x, int y, bool active) {
|
||||
float period = active ? 500.0f : 2000.0f;
|
||||
float t = fmodf(millis(), period) / period;
|
||||
uint8_t v = static_cast<uint8_t>(30.0f + 30.0f * sinf(t * 2.0f * PI));
|
||||
uint16_t col = _tft.color565(v, v, v);
|
||||
_tft.drawRect(x - 40, y - 20, 80, 40, col);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "board_config.h"
|
||||
|
||||
#include <KlubhausCore.h>
|
||||
#include <TFT_eSPI.h>
|
||||
|
||||
class DisplayDriverTFT : public IDisplayDriver {
|
||||
public:
|
||||
void begin() override;
|
||||
void setBacklight(bool on) override;
|
||||
void render(const ScreenState& state) override;
|
||||
TouchEvent readTouch() override;
|
||||
uint16_t getRawTouchZ();
|
||||
int dashboardTouch(int x, int y) override;
|
||||
HoldState updateHold(unsigned long holdMs) override;
|
||||
void updateHint(int x, int y, bool active) override;
|
||||
int width() override { return DISPLAY_WIDTH; }
|
||||
int height() override { return DISPLAY_HEIGHT; }
|
||||
|
||||
private:
|
||||
void drawBoot();
|
||||
void drawAlert(const ScreenState& st);
|
||||
void drawDashboard(const ScreenState& st);
|
||||
|
||||
TFT_eSPI _tft;
|
||||
|
||||
bool _holdActive = false;
|
||||
uint32_t _holdStartMs = 0;
|
||||
ScreenID _lastScreen = ScreenID::BOOT;
|
||||
bool _needsRedraw = true;
|
||||
};
|
||||
18
sketches/doorbell-touch/boards/esp32-32e-4/board_config.h
Normal file
18
sketches/doorbell-touch/boards/esp32-32e-4/board_config.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#define BOARD_NAME "WS_32E_4"
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// Hosyond ESP32-32E 4" (320x480) with ST7796 + XPT2046
|
||||
// Pin mapping from lcdwiki.com/4.0inch_ESP32-32E_Display
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
#define DISPLAY_WIDTH 320
|
||||
#define DISPLAY_HEIGHT 480
|
||||
#define DISPLAY_ROTATION 1 // landscape
|
||||
|
||||
// Backlight GPIO (HIGH = on)
|
||||
#define PIN_LCD_BL 27
|
||||
|
||||
// Touch — XPT2046 configured in tft_user_setup.h
|
||||
// Touch CS: GPIO33, Touch IRQ: GPIO36
|
||||
88
sketches/doorbell-touch/boards/esp32-32e-4/esp32-32e-4.ino
Normal file
88
sketches/doorbell-touch/boards/esp32-32e-4/esp32-32e-4.ino
Normal file
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// Klubhaus Doorbell — ESP32-32E-4" target
|
||||
//
|
||||
|
||||
#include "DisplayDriverTFT.h"
|
||||
#include "board_config.h"
|
||||
#include "secrets.h"
|
||||
|
||||
#include <KlubhausCore.h>
|
||||
|
||||
DisplayDriverTFT tftDriver;
|
||||
DisplayManager display(&tftDriver);
|
||||
DoorbellLogic logic(&display);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(500);
|
||||
|
||||
logic.begin(FW_VERSION, BOARD_NAME, wifiNetworks, wifiNetworkCount);
|
||||
logic.finishBoot();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// ── Touch debug: poll continuously ──
|
||||
TouchEvent evt = display.readTouch();
|
||||
if(evt.pressed) {
|
||||
Serial.printf("[TOUCH] pressed: x=%d, y=%d\n", evt.x, evt.y);
|
||||
} else {
|
||||
// Debug: print raw Z even when not touched, to see controller state
|
||||
static uint32_t lastDebug = 0;
|
||||
if(millis() - lastDebug > 2000) {
|
||||
lastDebug = millis();
|
||||
uint16_t z = tftDriver.getRawTouchZ();
|
||||
Serial.printf("[TOUCH] Raw Z=%d\n", z);
|
||||
}
|
||||
}
|
||||
|
||||
// ── State machine tick ──
|
||||
logic.update();
|
||||
|
||||
// ── Render current screen ──
|
||||
display.render(logic.getScreenState());
|
||||
|
||||
// ── Touch handling ──
|
||||
const ScreenState& st = logic.getScreenState();
|
||||
static int holdStartX = -1;
|
||||
static int holdStartY = -1;
|
||||
|
||||
if(st.deviceState == DeviceState::ALERTING) {
|
||||
HoldState h = display.updateHold(HOLD_TO_SILENCE_MS);
|
||||
if(h.completed) {
|
||||
logic.silenceAlert();
|
||||
holdStartX = -1;
|
||||
holdStartY = -1;
|
||||
}
|
||||
if(h.started) {
|
||||
TouchEvent t = display.readTouch();
|
||||
holdStartX = t.x;
|
||||
holdStartY = t.y;
|
||||
}
|
||||
if(holdStartX >= 0) {
|
||||
display.updateHint(holdStartX, holdStartY, h.active);
|
||||
}
|
||||
} else {
|
||||
holdStartX = -1;
|
||||
holdStartY = -1;
|
||||
}
|
||||
|
||||
if(st.screen == ScreenID::DASHBOARD) {
|
||||
TouchEvent evt = display.readTouch();
|
||||
if(evt.pressed) {
|
||||
int tile = display.dashboardTouch(evt.x, evt.y);
|
||||
if(tile >= 0)
|
||||
Serial.printf("[DASH] Tile %d tapped\n", tile);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Serial console ──
|
||||
if(Serial.available()) {
|
||||
String cmd = Serial.readStringUntil('\n');
|
||||
cmd.trim();
|
||||
if(cmd.length() > 0)
|
||||
logic.onSerialCommand(cmd);
|
||||
}
|
||||
|
||||
// Yield to WiFi/BT stack (prevents Task Watchdog timeout)
|
||||
delay(10);
|
||||
}
|
||||
11
sketches/doorbell-touch/boards/esp32-32e-4/secrets.h.example
Normal file
11
sketches/doorbell-touch/boards/esp32-32e-4/secrets.h.example
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include <KlubhausCore.h>
|
||||
|
||||
// Copy this file to secrets.h and fill in your credentials.
|
||||
// secrets.h is gitignored.
|
||||
|
||||
static const WiFiCred wifiNetworks[] = {
|
||||
{ "Your_SSID_1", "password1" },
|
||||
{ "Your_SSID_2", "password2" },
|
||||
};
|
||||
static const int wifiNetworkCount = sizeof(wifiNetworks) / sizeof(wifiNetworks[0]);
|
||||
45
sketches/doorbell-touch/boards/esp32-32e-4/tft_user_setup.h
Normal file
45
sketches/doorbell-touch/boards/esp32-32e-4/tft_user_setup.h
Normal file
@@ -0,0 +1,45 @@
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// TFT_eSPI User_Setup for ESP32-32E-4" (Hosyond 320x480)
|
||||
// This file is copied over vendor/esp32-32e-4/TFT_eSPI/User_Setup.h
|
||||
// by the install-libs-32e-4 task.
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
#define USER_SETUP_ID 201
|
||||
|
||||
// ── Driver ──
|
||||
#define ST7796_DRIVER
|
||||
|
||||
// ── Resolution ──
|
||||
#define TFT_WIDTH 320
|
||||
#define TFT_HEIGHT 480
|
||||
|
||||
// ── SPI Pins (from lcdwiki.com/4.0inch_ESP32-32E_Display) ──
|
||||
#define TFT_MISO 12
|
||||
#define TFT_MOSI 13
|
||||
#define TFT_SCLK 14
|
||||
#define TFT_CS 15
|
||||
#define TFT_DC 2
|
||||
#define TFT_RST -1 // tied to EN, handled by board
|
||||
|
||||
// ── Backlight ──
|
||||
#define TFT_BL 27
|
||||
#define TFT_BACKLIGHT_ON HIGH
|
||||
|
||||
// ── Touch (XPT2046 resistive) ──
|
||||
#define TOUCH_CS 33
|
||||
#define TOUCH_IRQ 36
|
||||
|
||||
// ── SPI speed ──
|
||||
#define SPI_FREQUENCY 40000000
|
||||
#define SPI_READ_FREQUENCY 20000000
|
||||
#define SPI_TOUCH_FREQUENCY 2500000
|
||||
|
||||
// ── Misc ──
|
||||
#define LOAD_GLCD
|
||||
#define LOAD_FONT2
|
||||
#define LOAD_FONT4
|
||||
#define LOAD_FONT6
|
||||
#define LOAD_FONT7
|
||||
#define LOAD_FONT8
|
||||
#define LOAD_GFXFF
|
||||
#define SMOOTH_FONT
|
||||
@@ -26,6 +26,22 @@ cp boards/esp32-32e/tft_user_setup.h vendor/esp32-32e/TFT_eSPI/User_Setup.h
|
||||
echo "[OK] TFT_eSPI 2.5.43 vendored + configured"
|
||||
"""
|
||||
|
||||
[tasks.install-libs-32e-4]
|
||||
description = "Vendor TFT_eSPI into vendor/esp32-32e-4 (ST7796 320x480)"
|
||||
run = """
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
if [ ! -d "vendor/esp32-32e-4/TFT_eSPI" ]; then
|
||||
echo "Cloning TFT_eSPI..."
|
||||
git clone --depth 1 --branch V2.5.43 \
|
||||
https://github.com/Bodmer/TFT_eSPI.git \
|
||||
vendor/esp32-32e-4/TFT_eSPI
|
||||
fi
|
||||
echo "Copying board-specific User_Setup.h..."
|
||||
cp boards/esp32-32e-4/tft_user_setup.h vendor/esp32-32e-4/TFT_eSPI/User_Setup.h
|
||||
echo "[OK] TFT_eSPI 2.5.43 vendored + configured for 4 inch"
|
||||
"""
|
||||
|
||||
[tasks.install-libs-s3-43]
|
||||
description = "Vendor LovyanGFX into vendor/esp32-s3-lcd-43"
|
||||
run = """
|
||||
@@ -68,7 +84,7 @@ echo "[OK] LovyanGFX vendored"
|
||||
|
||||
[tasks.install-libs]
|
||||
description = "Install all libraries (shared + vendored)"
|
||||
depends = ["install-libs-shared", "install-libs-32e", "install-libs-s3-43"]
|
||||
depends = ["install-libs-shared", "install-libs-32e", "install-libs-32e-4", "install-libs-s3-43"]
|
||||
|
||||
# ── ESP32-32E ────────────────────────────────────────────
|
||||
|
||||
@@ -77,7 +93,7 @@ description = "Compile ESP32-32E sketch"
|
||||
depends = ["install-libs"]
|
||||
run = """
|
||||
arduino-cli compile \
|
||||
--fqbn "esp32:esp32@2.0.11:esp32:FlashSize=4M,PartitionScheme=default" \
|
||||
--fqbn "esp32:esp32:esp32:FlashSize=4M,PartitionScheme=default" \
|
||||
--libraries ./libraries \
|
||||
--libraries ./vendor/esp32-32e/TFT_eSPI \
|
||||
--build-property "compiler.cpp.extra_flags=-DDEBUG_MODE -DBOARD_HAS_PSRAM" \
|
||||
@@ -100,6 +116,36 @@ run = """
|
||||
arduino-cli monitor --port "${PORT:-/dev/ttyUSB0}" --config baudrate=115200
|
||||
"""
|
||||
|
||||
# ── ESP32-32E-4 inch ─────────────────────────────────────────
|
||||
|
||||
[tasks.compile-32e-4]
|
||||
description = "Compile ESP32-32E-4 inch sketch (ST7796 320x480)"
|
||||
depends = ["install-libs-32e-4"]
|
||||
run = """
|
||||
arduino-cli compile \
|
||||
--fqbn "esp32:esp32:esp32:FlashSize=4M,PartitionScheme=default" \
|
||||
--libraries ./libraries \
|
||||
--libraries ./vendor/esp32-32e-4/TFT_eSPI \
|
||||
--build-property "compiler.cpp.extra_flags=-DDEBUG_MODE -DBOARD_HAS_PSRAM" \
|
||||
--warnings default \
|
||||
./boards/esp32-32e-4
|
||||
"""
|
||||
|
||||
[tasks.upload-32e-4]
|
||||
description = "Upload to ESP32-32E-4\""
|
||||
run = """
|
||||
arduino-cli upload \
|
||||
--fqbn "esp32:esp32:esp32:FlashSize=4M,PartitionScheme=default" \
|
||||
--port "${PORT:-/dev/ttyUSB0}" \
|
||||
./boards/esp32-32e-4
|
||||
"""
|
||||
|
||||
[tasks.monitor-32e-4]
|
||||
description = "Serial monitor for ESP32-32E 4 inch"
|
||||
run = """
|
||||
arduino-cli monitor -p "${PORT:-/dev/ttyUSB0}" --config 115200
|
||||
"""
|
||||
|
||||
# ── ESP32-S3-LCD-4.3 ────────────────────────────────────
|
||||
|
||||
[tasks.compile-s3-43]
|
||||
@@ -137,6 +183,7 @@ description = "Remove build artifacts"
|
||||
run = """
|
||||
rm -rf vendor/
|
||||
rm -rf boards/esp32-32e/build
|
||||
rm -rf boards/esp32-32e-4/build
|
||||
rm -rf boards/esp32-s3-lcd-43/build
|
||||
echo "[OK] Build artifacts cleaned"
|
||||
"""
|
||||
@@ -147,6 +194,9 @@ clang-format -i --style=file \
|
||||
boards/esp32-32e/*.cpp \
|
||||
boards/esp32-32e/*.h \
|
||||
boards/esp32-32e/*.ino \
|
||||
boards/esp32-32e-4/*.cpp \
|
||||
boards/esp32-32e-4/*.h \
|
||||
boards/esp32-32e-4/*.ino \
|
||||
boards/esp32-s3-lcd-43/*.cpp \
|
||||
boards/esp32-s3-lcd-43/*.h \
|
||||
boards/esp32-s3-lcd-43/*.ino \
|
||||
|
||||
Reference in New Issue
Block a user