Rebuild firmware scaffold for ESP32-C6/ESP-IDF, replacing NCS/Zephyr

The product pivoted away from a Matter-first nRF54L15 device to an
ESP32-C6/ESP-IDF/C++17 platform where behaviour is defined by a
Blockly-generated Lua program instead of custom firmware (see the
updated kvida meta-repo vision). Removes the west/NCS/Zephyr scaffold
and replaces it with:

- A pinned ESP-IDF v6.0.2 Docker build environment (Espressif's
  official image already bundles the toolchain, unlike NCS/west).
- A layered component structure (drivers -> sensors -> profiles ->
  lua_runtime -> transport) matching the new architecture principles,
  with per-component READMEs describing responsibilities.
- An A/B OTA + LittleFS partition table and a minimal main.cpp.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Ronny Eia
2026-07-08 19:54:25 +02:00
parent 13406a24a3
commit 524714148b
33 changed files with 133 additions and 132 deletions

15
.gitignore vendored
View File

@@ -1,18 +1,13 @@
# Zephyr / nRF Connect SDK
# ESP-IDF
/build/
/.west/
/bootloader/
/modules/
/nrf/
/nrfxlib/
/zephyr/
/tools/
/managed_components/
/dependencies.lock
sdkconfig
sdkconfig.old
*.o
*.elf
*.hex
*.bin
*.map
*.uf2
# Python
__pycache__/

7
CMakeLists.txt Normal file
View File

@@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(kvida_os)

View File

@@ -1,36 +1,37 @@
# kvida-os
Fastvare for Kvida — en universell, batteridrevet Matter-node (Nordic nRF54L15) fra Xylon.
Fastvare for Kvida — en konfigurerbar automasjonsplattform (ESP32-C6) fra Xylon.
Visjon og arkitektur er beskrevet i meta-repoet [`kvida`](../kvida/AGENTS.md). Dette repoet inneholder kun fastvaren, bygget på Nordic nRF Connect SDK (Zephyr).
Visjon og arkitektur er beskrevet i meta-repoet [`kvida`](../kvida/AGENTS.md). Dette repoet inneholder kun fastvaren, bygget på ESP-IDF (C++17).
## Forutsetninger
- Docker
- WSL2
- `usbipd-win` (for å videresende J-Link/SWD-probe fra Windows til WSL2 ved flashing)
- `usbipd-win` (for å videresende ESP32-C6 sin USB-seriellport fra Windows til WSL2 ved flashing)
## Board
## Arkitektur
Bygget er foreløpig satt opp mot Nordic sitt offisielle utviklingskort (`nrf54l15dk/nrf54l15/cpuapp`) som plassholder. Kvida sin egen hardware trenger et custom board-oppsett under `boards/` når skjema/pinout er klart — ikke gjort ennå.
Lagdelt: `drivers` -> `sensors` -> `profiles` -> `lua_runtime` -> `transport`, se komponent-READMEene under `components/` og `AGENTS.md` for detaljer. Bruker konfigurerer enheten visuelt (Blockly -> Lua), ikke ved å skrive/kompilere fastvare.
## Quickstart
Containeren kjører med arbeidsmappe `/workspace` (west-workspacets topdir), hvor dette repoet er mountet inn som undermappen `kvida-os/`. NCS/Zephyr-modulene havner som søsken av repoet (`/workspace/nrf`, `/workspace/zephyr`, ...) i et eget navngitt Docker-volum.
```sh
# Bygg toolchain-imaget
# Bygg toolchain-imaget (Espressif sitt offisielle ESP-IDF v6.0.2-image)
docker compose build
# Hent NCS/Zephyr-moduler (kjøres én gang, lagres i det navngitte volumet)
docker compose run --rm build west init -l kvida-os
docker compose run --rm build west update
# Velg target (kjøres én gang; genererer sdkconfig)
docker compose run --rm build idf.py set-target esp32c6
# Bygg fastvaren
docker compose run --rm build west build -b nrf54l15dk/nrf54l15/cpuapp kvida-os/app
docker compose run --rm build idf.py build
# Flash (krever J-Link-probe videresendt via usbipd-win til WSL2)
docker compose run --rm build west flash
# Flash + monitor (krever seriellport videresendt via usbipd-win til WSL2)
docker compose run --rm build idf.py -p <PORT> flash monitor
```
`west.yml` er pinnet til NCS `v3.4.0`.
Prosjektet er pinnet til ESP-IDF `v6.0.2` (se `docker/Dockerfile`).
## Status
`main/` bygger og logger oppstart. `components/*` inneholder foreløpig bare README-er som beskriver ansvar per lag — ingen reell logikk ennå. `partitions.csv` har et A/B OTA + LittleFS-oppsett med plassholder-størrelser (4MB flash) som bør justeres når ekte flash-størrelse/image-størrelse er kjent.

View File

@@ -1,11 +0,0 @@
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(kvida_os)
target_sources(app PRIVATE src/main.c)
# Module directories under src/ (hal, gpio_manager, power_manager,
# matter_manager, ota, config_manager, sensor_profiles, rule_engine) are
# currently placeholders with no source files. Add add_subdirectory()
# calls for each once it gains a CMakeLists.txt and real sources.

View File

@@ -1 +0,0 @@
rsource "Kconfig.zephyr"

View File

@@ -1,6 +0,0 @@
# Minimal base configuration so the application boots and logs.
# Matter, Thread, power management, and other feature-specific Kconfig
# options are added as the corresponding modules gain real implementations.
CONFIG_LOG=y
CONFIG_GPIO=y

View File

@@ -1,3 +0,0 @@
# config_manager
Stores and applies the user's device configuration (selected sensor profile, GPIO assignment, options) without requiring a firmware change.

View File

@@ -1,3 +0,0 @@
# gpio_manager
Maps configured sensor profiles to physical GPIO/ADC/I2C pins at runtime, based on the active configuration rather than build-time wiring.

View File

@@ -1,3 +0,0 @@
# hal
Hardware abstraction layer: wraps nRF54L15 peripherals (GPIO, ADC, I2C) behind a board-independent interface used by the other modules.

View File

@@ -1,10 +0,0 @@
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(kvida_os, LOG_LEVEL_INF);
int main(void)
{
LOG_INF("kvida-os starting");
return 0;
}

View File

@@ -1,3 +0,0 @@
# matter_manager
Exposes configured sensor profiles as Matter endpoints/clusters over Thread, and handles Matter commissioning.

View File

@@ -1,3 +0,0 @@
# ota
Firmware update handling (DFU over Matter/Thread, image validation, rollback).

View File

@@ -1,3 +0,0 @@
# power_manager
Battery-first power management: sleep/wake scheduling, power state transitions, and battery level reporting.

View File

@@ -1,3 +0,0 @@
# rule_engine
Executes user-defined behavior rules (level 3: Lua scripts, level 2: Blockly-generated intermediate representation) without requiring a firmware rebuild.

View File

@@ -1,3 +0,0 @@
# sensor_profiles
Reusable sensor profile implementations (reed switch, hall sensor, push button, pulse counter, analog input, DS18B20, BME280, leak sensor, PIR, generic I2C device) selectable via configuration.

View File

@@ -0,0 +1,2 @@
idf_component_register()

View File

@@ -0,0 +1,5 @@
# drivers
Hardware abstraction: wraps ESP32-C6 peripherals (GPIO, ADC, I2C) behind a board-independent interface used by `sensors`. No dependency on other Kvida components.
TODO: mounting the `storage` LittleFS partition (see `partitions.csv`) belongs here once a LittleFS component (e.g. `joltwallet/esp_littlefs` from the IDF Component Registry) is picked and pinned via `idf_component.yml`.

View File

@@ -0,0 +1,3 @@
idf_component_register(
REQUIRES profiles transport
)

View File

@@ -0,0 +1,5 @@
# lua_runtime
Executes the sandboxed Lua program (compiled from Blockly) that defines device behaviour. Exposes the Kvida API (e.g. `publish("temperature", 21.3)`) to Lua scripts; Lua never touches ESP-IDF or `transport` directly. `REQUIRES profiles transport`.
TODO: needs an actual Lua interpreter dependency, pinned via `idf_component.yml` once a specific IDF Component Registry package/version is confirmed (not guessed here, same caution as pinning `west.yml`'s NCS revision previously).

View File

@@ -0,0 +1,3 @@
idf_component_register(
REQUIRES sensors
)

View File

@@ -0,0 +1,3 @@
# profiles
Exposes reusable sensor/actuator capabilities (reed switch, hall sensor, push button, pulse counter, analog input, DS18B20, BME280, leak sensor, PIR, generic I2C device) to the Lua runtime, backed by `sensors`. `REQUIRES sensors`.

View File

@@ -0,0 +1,3 @@
idf_component_register(
REQUIRES drivers
)

View File

@@ -0,0 +1,3 @@
# sensors
Turns raw `drivers` readings into typed sensor values (e.g. debounced contact state, calibrated analog readings). Knows values, not hardware. `REQUIRES drivers`.

View File

@@ -0,0 +1 @@
idf_component_register()

View File

@@ -0,0 +1,11 @@
# transport
Wi-Fi connectivity, MQTT client, and Home Assistant MQTT Discovery payloads. Exposes a semantic `publish(topic, value)`-style API — application code (including Lua) never depends on MQTT directly, per the transport abstraction principle in `AGENTS.md`. No dependency on other Kvida components.
Also owns Wi-Fi/MQTT configuration storage for now (not broken out into its own component since `AGENTS.md` doesn't call out a separate config layer — revisit if this grows).
Future transports (Zigbee, Thread, Matter) should implement the same publish API as alternate backends behind this component's interface, without changing callers.
TODO: built on ESP-IDF's own `esp_wifi` and `mqtt_client` (`esp-mqtt`) components once real code lands — both ship with ESP-IDF, no extra registry dependency needed.
TODO: A/B OTA update-checking belongs here, driven by ESP-IDF's native `esp_ota_ops` against the `ota_0`/`ota_1` partitions in `partitions.csv` — no extra dependency needed either.

View File

@@ -3,23 +3,25 @@ services:
build:
context: .
dockerfile: docker/Dockerfile
working_dir: /workspace
working_dir: /project
environment:
- IDF_TARGET=esp32c6
volumes:
# West workspace topdir: holds .west/, and the NCS/Zephyr module
# tree fetched by `west update` (nrf/, zephyr/, modules/, ...).
- ncs-workspace:/workspace
# This repo, bind-mounted as the manifest project inside the
# workspace (matches `self.path: kvida-os` in west.yml).
- .:/workspace/kvida-os
# Project source, bind-mounted.
- .:/project
# Build output and IDF Component Registry cache, kept in named
# volumes so they survive across container runs without polluting
# the bind-mounted source tree.
- build-cache:/project/build
- managed-components-cache:/project/managed_components
tty: true
# USB/J-Link access (for `west flash`) is expected to be handled via
# usbipd-win forwarding the probe into WSL2. If `west flash` needs to
# run inside this container rather than on the WSL2 host, uncomment
# and adjust one of the following once the forwarded device node is
# known:
# USB access (for `idf.py flash`/`monitor`) is expected to be handled
# via usbipd-win forwarding the board's serial port into WSL2. If
# flashing needs to run inside this container rather than on the WSL2
# host, uncomment and adjust once the forwarded device node is known:
# devices:
# - "/dev/bus/usb:/dev/bus/usb"
# privileged: true
# - "/dev/ttyUSB0:/dev/ttyUSB0"
volumes:
ncs-workspace:
build-cache:
managed-components-cache:

View File

@@ -1,21 +1,13 @@
# Build-only toolchain image for kvida-os.
#
# Provides the Zephyr SDK toolchain, west, CMake, Ninja and Python that
# NCS builds need. It intentionally does NOT bake in the NCS/Zephyr
# module tree (nrf/, zephyr/, modules/, ...) fetched by `west update` --
# that is left to a named volume mounted at /workspace at container run
# time, so the image stays small and NCS revisions can be bumped without
# rebuilding it.
# Unlike the previous NCS/Zephyr setup, Espressif's official image already
# bakes in the full ESP-IDF toolchain and the SDK itself for the pinned
# version, so there is no separate multi-GB "fetch SDK into a volume" step
# here -- just pin the image tag.
#
# NOTE: verify this base image tag still exists/is current before relying
# on it -- it could not be checked against the live registry while
# writing this Dockerfile.
FROM ghcr.io/zephyrproject-rtos/zephyr-build:latest
# NOTE: verify this tag still exists before relying on it -- it could not
# be checked against the live registry while writing this Dockerfile. If
# the exact patch tag "v6.0.2" isn't published, fall back to "release-v6.0".
FROM espressif/idf:v6.0.2
RUN pip3 install --no-cache-dir -U west
WORKDIR /workspace
# TODO: install Nordic's nRF Command Line Tools / nrfutil here if
# `west flash` should run from inside this container against a J-Link
# probe forwarded into WSL2 via usbipd-win, instead of from the host.
WORKDIR /project

5
main/CMakeLists.txt Normal file
View File

@@ -0,0 +1,5 @@
idf_component_register(
SRCS "main.cpp"
INCLUDE_DIRS "."
REQUIRES drivers sensors profiles transport lua_runtime
)

10
main/main.cpp Normal file
View File

@@ -0,0 +1,10 @@
#include "esp_log.h"
namespace {
constexpr const char *TAG = "kvida_os";
}
extern "C" void app_main(void)
{
ESP_LOGI(TAG, "kvida-os starting");
}

12
partitions.csv Normal file
View File

@@ -0,0 +1,12 @@
# ESP-IDF partition table: A/B OTA app slots + LittleFS storage.
#
# Sized for a 4MB flash module (TODO: adjust once real flash size and
# firmware image size are known).
#
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
otadata, data, ota, 0xf000, 0x2000,
phy_init, data, phy, 0x11000, 0x1000,
ota_0, app, ota_0, 0x20000, 0x180000,
ota_1, app, ota_1, 0x1A0000, 0x180000,
storage, data, littlefs, 0x320000, 0xE0000,
1 # ESP-IDF partition table: A/B OTA app slots + LittleFS storage.
2 #
3 # Sized for a 4MB flash module (TODO: adjust once real flash size and
4 # firmware image size are known).
5 #
6 # Name, Type, SubType, Offset, Size, Flags
7 nvs, data, nvs, 0x9000, 0x6000,
8 otadata, data, ota, 0xf000, 0x2000,
9 phy_init, data, phy, 0x11000, 0x1000,
10 ota_0, app, ota_0, 0x20000, 0x180000,
11 ota_1, app, ota_1, 0x1A0000, 0x180000,
12 storage, data, littlefs, 0x320000, 0xE0000,

6
sdkconfig.defaults Normal file
View File

@@ -0,0 +1,6 @@
# Shared defaults. Target-specific settings are generated into `sdkconfig`
# by `idf.py set-target esp32c6` (first-time setup, see README) rather than
# hardcoded here.
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"

7
test/README.md Normal file
View File

@@ -0,0 +1,7 @@
# test
Host-based unit tests for business logic that doesn't need real hardware, per the testing philosophy in `AGENTS.md`: "Everything that can be tested on a desktop should be."
Primary candidates once they have real code: `components/sensors`, `components/profiles`, and the non-hardware parts of `components/lua_runtime` (Lua sandboxing/API surface, not GPIO access).
ESP-IDF supports building against its `linux` target (`idf.py --preview set-target linux`) to run component unit tests on the host without a board. No test scaffolding is wired up yet since there's no logic to test.

View File

@@ -1,23 +0,0 @@
# West manifest for kvida-os (freestanding NCS application).
#
# This repo is meant to sit as a named subdirectory ("kvida-os") inside a
# west workspace topdir, e.g. `/workspace/kvida-os`. Running
# `west init -l kvida-os && west update` from the topdir fetches sdk-nrf
# (which pulls in Zephyr, MCUboot, Matter, etc. via its own manifest) as
# siblings of this repo (`/workspace/nrf`, `/workspace/zephyr`, ...).
#
# Pinned to NCS v3.4.0.
manifest:
remotes:
- name: ncs
url-base: https://github.com/nrfconnect
projects:
- name: nrf
remote: ncs
repo-path: sdk-nrf
revision: v3.4.0
import: true
self:
path: kvida-os