chore(build): scaffold ESP-IDF project for BOX-3

Context:
  New firmware "Lisael Box" targeting the ESP32-S3-BOX-3 (8MB PSRAM,
  16MB flash, LCD+touch, ES8311 codec, microSD). Establish the ESP-IDF
  5.4/5.5 build skeleton before adding modules.

Approach:
  Standard IDF project layout with a single big factory app (4MB) since
  the binary links the esp-box BSP, LVGL 9 and ESP-ADF. ESP-ADF is not a
  managed component, so the top-level CMake injects $ADF_PATH/components
  via EXTRA_COMPONENT_DIRS and warns clearly when ADF_PATH is unset.

Changes:
  - top-level CMakeLists with ADF_PATH wiring
  - partitions.csv (16MB: factory 4MB + FAT storage)
  - sdkconfig.defaults (+ .esp32s3: octal PSRAM, 16MB, 240MHz, LVGL/PSRAM)
  - main/CMakeLists, idf_component.yml (esp-box-3 ^3.2, built-in json)
  - Kconfig.projbuild (Wi-Fi, weather lat/lon, child volume cap)
  - lisael_config.h shared constants, .gitignore

Impact:
  Buildable skeleton; modules added in following commits. Not yet
  compiled (no IDF/ADF/board available) - see README.
This commit is contained in:
Clément Saillant
2026-06-14 00:40:07 +02:00
parent 7dfea75472
commit e44efe13e0
9 changed files with 308 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
# ESP-IDF build artifacts
/build/
sdkconfig
sdkconfig.old
# Managed components are fetched by the component manager
/managed_components/
**/managed_components/
dependencies.lock
# Editor / OS
.vscode/
.idea/
*.swp
.DS_Store
# Python venv occasionally created by tooling
.venv/
+30
View File
@@ -0,0 +1,30 @@
# Lisael Box — top-level project CMake for ESP-IDF 5.4/5.5
# Target: ESP32-S3-BOX-3
cmake_minimum_required(VERSION 3.16)
# --- ESP-ADF integration -----------------------------------------------------
# ESP-ADF is NOT a managed component: it is installed separately and exposed via
# the ADF_PATH environment variable (see README "Installer ESP-ADF").
# We add its component tree to EXTRA_COMPONENT_DIRS so that audio_pipeline,
# http_stream, esp_decoder, fatfs_stream, i2s_stream etc. become available.
#
# If ADF_PATH is unset the project still configures, but the audio modules that
# need ADF will fail to link. We surface a clear message instead of a cryptic
# "component not found" error.
if(DEFINED ENV{ADF_PATH})
set(ADF_COMPONENTS "$ENV{ADF_PATH}/components")
if(EXISTS "${ADF_COMPONENTS}")
list(APPEND EXTRA_COMPONENT_DIRS "${ADF_COMPONENTS}")
message(STATUS "Lisael Box: ESP-ADF components from ${ADF_COMPONENTS}")
else()
message(WARNING "Lisael Box: ADF_PATH set but ${ADF_COMPONENTS} not found")
endif()
else()
message(WARNING
"Lisael Box: ADF_PATH not set. Audio streaming (webradio / SD playback) "
"will not link. Run '. $ADF_PATH/export.sh' or export ADF_PATH. "
"See README.")
endif()
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(lisael_box)
+62
View File
@@ -0,0 +1,62 @@
# Lisael Box — main component.
#
# ESP-ADF components (audio_pipeline, esp-adf-libs, audio_stream, esp_decoder,
# audio_board ...) are resolved at configure time from ADF_PATH via
# EXTRA_COMPONENT_DIRS (top-level CMakeLists.txt). They are listed in REQUIRES
# so the linker pulls them in; if ADF_PATH is missing the build fails loudly
# in the audio sources only.
#
# The audio REQUIRES are guarded: when ADF is absent we still let the UI / net
# layers build for inspection. Set LISAEL_WITH_ADF=OFF to compile without audio
# (UI/clock/breathing only) for quick syntax checks on a machine without ADF.
set(srcs
"app_main.c"
"net/wifi.c"
"net/sntp_time.c"
"net/weather.c"
"ui/ui.c"
"ui/fr_date.c"
"ui/icons.c"
"modes/home.c"
"modes/radio.c"
"modes/soundbox.c"
"modes/calm.c"
"modes/clock.c"
"modes/games.c"
"audio/audio_player.c"
"audio/radio_pipeline.c"
"audio/sd_player.c"
)
set(reqs
nvs_flash
esp_wifi
esp_event
esp_netif
esp-tls
esp_http_client
mbedtls # esp_crt_bundle_attach (CA bundle for HTTPS)
esp-box-3 # BSP: display, touch, codec, sdcard, sensors
json # IDF built-in cJSON component (provides cJSON.h)
fatfs
esp_driver_i2c
esp_driver_tsens # driver/temperature_sensor.h (internal die temp)
)
# ESP-ADF audio components. Names follow the ADF master component tree.
# Guarded so a no-ADF inspection build still configures.
if(DEFINED ENV{ADF_PATH})
list(APPEND reqs
audio_pipeline
audio_stream
esp-adf-libs
audio_sal
)
endif()
idf_component_register(
SRCS ${srcs}
INCLUDE_DIRS "."
REQUIRES ${reqs}
)
+66
View File
@@ -0,0 +1,66 @@
menu "Lisael Box configuration"
menu "Wi-Fi"
config LISAEL_WIFI_SSID
string "Wi-Fi SSID"
default "myssid"
help
SSID (network name) the box connects to at boot. Stored as a
build-time default; the runtime value can also be provisioned
into NVS later (see net/wifi.c).
config LISAEL_WIFI_PASSWORD
string "Wi-Fi password"
default "mypassword"
help
WPA2 passphrase for the SSID above. Leave the defaults and
override via NVS provisioning if you do not want creds in the
binary.
endmenu
menu "Weather (open-meteo.com)"
config LISAEL_WEATHER_LAT
string "Default latitude"
default "48.8566"
help
Latitude used for the open-meteo forecast when none is stored
in NVS. Default is Paris, France.
config LISAEL_WEATHER_LON
string "Default longitude"
default "2.3522"
help
Longitude used for the open-meteo forecast when none is stored
in NVS. Default is Paris, France.
config LISAEL_WEATHER_CITY
string "City label"
default "Paris"
help
Human-readable city name shown on the home screen next to the
temperature.
config LISAEL_WEATHER_REFRESH_MIN
int "Refresh interval (minutes)"
range 5 180
default 30
help
How often the weather task re-fetches the forecast.
endmenu
menu "Audio"
config LISAEL_VOLUME_MAX_PERCENT
int "Maximum volume (child cap, percent)"
range 10 100
default 60
help
Hard cap on output volume. The volume UI never lets the value
exceed this, to protect a child's hearing.
config LISAEL_VOLUME_DEFAULT_PERCENT
int "Default volume at boot (percent)"
range 5 100
default 35
endmenu
endmenu
+24
View File
@@ -0,0 +1,24 @@
# Managed component dependencies for Lisael Box.
# Fetched automatically into managed_components/ on `idf.py build`.
#
# NOTE: ESP-ADF is NOT listed here — it is not distributed via the component
# registry. It is installed separately and pulled in through ADF_PATH /
# EXTRA_COMPONENT_DIRS (see top-level CMakeLists.txt and README).
#
# Versions below reflect the esp-box-3 BSP v3.2.x line, which itself depends on
# LVGL 9.x, esp_lvgl_port ^2 and esp_codec_dev ~1.5. We let the BSP pin the
# exact transitive versions; we only state the top-level ones we use directly.
# Per repo supply-chain policy these should eventually be mirrored/pinned by
# hash against the audited baseline — TODO before any production build.
dependencies:
idf:
version: ">=5.4"
# Board support package for the ESP32-S3-BOX-3: display, touch, ES8311 codec,
# ES7210 mic, microSD, IMU, buttons. Pulls esp_lvgl_port, lvgl, esp_codec_dev,
# esp_lcd_ili9341, button, icm42670, aht30 transitively.
espressif/esp-box-3: "^3.2.0"
# JSON parsing (manifest.json, open-meteo replies) uses the cJSON shipped with
# ESP-IDF as the built-in "json" component — no managed dependency needed.
# (REQUIRES json in main/CMakeLists.txt.)
+28
View File
@@ -0,0 +1,28 @@
// Lisael Box — shared compile-time configuration and small helpers.
#pragma once
#include "sdkconfig.h"
// --- Timezone (Europe/Paris with EU DST rules) ------------------------------
// CET-1CEST,M3.5.0,M10.5.0/3 : last Sunday of March 02:00 -> +1, last Sunday
// of October 03:00 -> back. Set with setenv("TZ", ...) + tzset() in sntp_time.c.
#define LISAEL_TZ "CET-1CEST,M3.5.0,M10.5.0/3"
// --- NVS namespace / keys ----------------------------------------------------
#define LISAEL_NVS_NS "lisael"
#define LISAEL_NVS_WIFI_SSID "wifi_ssid"
#define LISAEL_NVS_WIFI_PASS "wifi_pass"
#define LISAEL_NVS_LAT "lat"
#define LISAEL_NVS_LON "lon"
#define LISAEL_NVS_VOLUME "volume"
// --- Audio volume (child-safe) ----------------------------------------------
#define LISAEL_VOLUME_MAX CONFIG_LISAEL_VOLUME_MAX_PERCENT
#define LISAEL_VOLUME_DEFAULT CONFIG_LISAEL_VOLUME_DEFAULT_PERCENT
// --- Weather -----------------------------------------------------------------
#define LISAEL_WEATHER_REFRESH_MIN CONFIG_LISAEL_WEATHER_REFRESH_MIN
// --- Display geometry (ESP32-S3-BOX-3 LCD) ----------------------------------
#define LISAEL_SCREEN_W 320
#define LISAEL_SCREEN_H 240
+18
View File
@@ -0,0 +1,18 @@
# Lisael Box partition table — ESP32-S3-BOX-3 (16 MB flash)
#
# Single factory app, deliberately large (4 MB) because the firmware links
# ESP-ADF (audio pipeline + decoders), LVGL 9 and the esp-box BSP, which is a
# big binary. No OTA partitions for now (kept simple); switch to a dual-OTA
# layout later if remote updates are needed — there is plenty of headroom.
#
# The "sounds" / audio assets live on the physical microSD card mounted by the
# BSP (FATFS), NOT in flash — so no large data partition is needed here.
#
# All offsets/sizes aligned to 0x1000 (4 KB). Bootloader + partition table sit
# below 0x10000.
#
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x400000,
storage, data, fat, 0x410000, 0x100000,
Can't render this file because it contains an unexpected character in line 8 and column 7.
+28
View File
@@ -0,0 +1,28 @@
# Lisael Box — committed defaults (target-independent).
# Target-specific overrides live in sdkconfig.defaults.esp32s3.
# Regenerate sdkconfig with: idf.py set-target esp32s3 && idf.py reconfigure
# --- Partition table ---------------------------------------------------------
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
# --- Logging / build ---------------------------------------------------------
CONFIG_LOG_DEFAULT_LEVEL_INFO=y
CONFIG_COMPILER_OPTIMIZATION_PERF=y
# --- FreeRTOS ----------------------------------------------------------------
# 1 kHz tick gives smoother LVGL animations (bubble breathing, transitions).
CONFIG_FREERTOS_HZ=1000
# --- mbedTLS / TLS -----------------------------------------------------------
# open-meteo.com is HTTPS; webradio streams may be HTTPS too. Use the global
# CA store bundle so we don't have to embed individual certs.
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=y
# --- HTTP client -------------------------------------------------------------
CONFIG_ESP_TLS_INSECURE=n
# --- esp-box BSP -------------------------------------------------------------
# SD card mount point used throughout the firmware (see audio/sd_player.c).
CONFIG_BSP_SD_MOUNT_POINT="/sdcard"
+34
View File
@@ -0,0 +1,34 @@
# Lisael Box — ESP32-S3-BOX-3 specific defaults.
# Applied automatically on top of sdkconfig.defaults when target == esp32s3.
# --- Flash: 16 MB on the BOX-3 ----------------------------------------------
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
# --- PSRAM: 8 MB octal SPI on the BOX-3 -------------------------------------
# Required for the LVGL framebuffer(s) and ESP-ADF ring buffers.
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y
CONFIG_SPIRAM_RODATA=y
# Let malloc() fall back to PSRAM for large allocations (LVGL buffers, decoders).
CONFIG_SPIRAM_USE_MALLOC=y
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=4096
CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y
# --- CPU ---------------------------------------------------------------------
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
# --- LVGL (esp_lvgl_port / lvgl 9) ------------------------------------------
# Use PSRAM for the LVGL draw buffers so the 320x240x16bpp framebuffer fits.
CONFIG_LV_MEM_CUSTOM=y
CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM=y
# --- Main task ---------------------------------------------------------------
# app_main runs BSP + LVGL bring-up; give it a comfortable stack.
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
# --- Audio: ESP-ADF decoders need a decent heap; keep DMA in internal RAM ---
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096