Skip to content

Latest commit

 

History

History
94 lines (71 loc) · 5.14 KB

File metadata and controls

94 lines (71 loc) · 5.14 KB

AGENTS.md — OpenCode Session Guide

Compact reference for agents working in this repo. For full architecture and hardware context see CLAUDE.md.

Project

WellD — ESP32-C6 battery-powered well-level monitor. ESP-IDF v6.0.1 only. One app_main() = one wakeup → read sensors → Zigbee report → deep sleep.

Developer Commands

# First-time setup
idf.py set-target esp32c6
cp sdkconfig.defaults.local.example sdkconfig.defaults.local

# Build / flash / monitor
idf.py build
idf.py -p /dev/ttyUSB0 flash monitor
idf.py menuconfig          # WellD Configuration submenu
idf.py fullclean           # nukes build/ and managed_components/

# Host tests (no ESP-IDF, runs in CI)
cmake -S test/host -B test/host/build
cmake --build test/host/build
ctest --test-dir test/host/build --output-on-failure

# On-device tests (needs hardware; CI only builds them)
idf.py -C test/sensor build flash monitor
idf.py -C test/welld_core build flash monitor

# Zigbee2MQTT converter tests
cd zigbee2mqtt && npm test

Architecture Constraints (Load-Bearing)

  • Sensor reads must precede zigbee_send() — the ADC is sensitive to 802.15.4 RF interference.
  • GPIO5 (VLOOP) HIGH ≥ 10 ms before 4–20 mA read, LOW immediately after. Max ON time 100 ms. (12 V rail settles through the Q3 load-disconnect P-FET into C20/C22.)
  • All power-control GPIOs (4, 5, 15) must be held LOW through deep sleep via esp_sleep_gpio_isolate().
  • zigbee_send() blocks until radio idle (success/timeout/OTA). Caller must wait for STOPPED_BIT before esp_deep_sleep() so the radio is fully released.
  • 5 consecutive Zigbee failures → next boot erases NVS and forces fresh join (namespace "welld", key "zb_fails").
  • OTA_FW_VERSION is derived from PROJECT_VER in root CMakeLists.txt at build time. To release: bump PROJECT_VER only; never edit OTA_FW_VERSION directly.
  • OTA manufacturer/image type are hardcoded (0x1234 / 0x0001 in components/zigbee/zigbee.c). Do not wildcard to 0xFFFF.

Testing Gotchas

  • Host tests reuse on-device test sources. test/host/CMakeLists.txt compiles them with -DHOST_BUILD, which swaps app_main() for main() via #ifdef HOST_BUILD at the bottom of each test file.
  • Pure helpers only in sensor_pure.c and welld_core.c. These files must stay free of NVS/ADC/log/freertos calls so they compile on any host.
  • test/host/stub_sdkconfig/sdkconfig.h mirrors Kconfig defaults. Keep it in sync if components/sensor/Kconfig defaults change.
  • On-device test apps must use EXTRA_COMPONENT_DIRS = "../../components/<component>" (specific component path, not the whole components/ dir) — otherwise ESP-IDF discovers sibling components like zigbee and tries to build them outside their managed-dependency context.
  • Guard hardware-only test cases with #ifndef HOST_BUILD.

Dependency & Config Rules

  • Exact == pins in components/sensor/idf_component.yml and components/zigbee/idf_component.yml. Bump one at a time and run a hardware build; expect API renames.
  • Config layering:
    • sdkconfig.defaults — committed, holds invariant settings (flash size, Zigbee role).
    • sdkconfig.defaults.local — gitignored, user overrides.
    • sdkconfig — committed and regenerated by menuconfig. Updates land via PRs.
  • New tunables go in main/Kconfig.projbuild with a range clause.

Monorepo Boundaries

Directory Ownership
main/ Wake orchestration, NVS fail counter, RTC history
components/sensor/ ADC, DS18B20, battery divider, GPIO power control
components/zigbee/ esp-zigbee-lib wrapper, OTA client, BDB task
components/welld_core/ Pure helpers: rate-of-change, adaptive sleep, ZCL encoding
zigbee2mqtt/ External converter (welld.js) and its Node tests
test/host/ Host CMake test runner (Unity v2.6.0)
test/sensor/, test/welld_core/ Standalone ESP-IDF test apps for on-device hardware

Style Conventions

  • One static const char *TAG per .c file, named for the component ("main", "sensor", "zigbee").
  • Component REQUIRES lists are minimal and explicit; prefer adding to REQUIRES over transitive includes.
  • Guard ESP32-C6-specific code with CONFIG_IDF_TARGET_ESP32C6.

Sub-Agent Routing

  • Parallel (no shared files): firmware + test + z2m-converter + case
  • Sequential: pcbfirmware (pin map) → case (dimensions)
  • Handoff contract: PCB GPIO changes must update components/sensor/sensor.c, main/Kconfig.projbuild defaults, and CLAUDE.md before any case work begins.

CI

.github/workflows/build.yml runs six jobs:

  1. ESP-IDF build (esp32c6, v6.0.1) — builds firmware and OTA image (no artifact uploads — no third-party actions policy)
  2. C static analysis (cppcheck) — fails on any warning across main/ and components/
  3. Version bump check (PR-only) — fails if firmware sources changed without bumping PROJECT_VER
  4. Host unit tests (plain ubuntu-latest, ctest) — no ESP-IDF or hardware required
  5. On-device test build (compile-only for test/sensor and test/welld_core)
  6. Zigbee2MQTT converter tests (Node 20, npm test)