Make production the default build; decouple USB companion from CONFIG_LOG

Production (LOG=n, ASSERT=n, RTT=n, reboot-on-fatal) is now the prj.conf
default; debug.conf is the opt-in bundle. Removed prod.conf and the
logging.conf auto-include; relocated RTT/ASSERT out of the always-on
platform confs so they no longer override the prod defaults.

Add CONFIG_ZEPHCORE_COMPANION_USB so the USB CDC companion transport
compiles independently of logging (default-y on USB-capable companions,
opt-in on ESP32-S3 via esp32s3_usb.conf). Gate all USB sites behind one
ZEPHCORE_USB_STACK macro.

Rework BLE/USB interface arbitration to first-come-first-served: neither
transport evicts a live session. Make active_iface mutation thread-safe
(mutex + atomic claim) across the BLE callback thread and USB workqueue.

Share the ESP32-S3 USB OTG / console DTS via common dtsi includes; enable
uart0 (GPIO43/44) on station_g2 and xiao so the console reroute works.
This commit is contained in:
liquidraver
2026-05-30 21:05:42 +02:00
parent 7d7a64ff5a
commit 553b71c44a
25 changed files with 332 additions and 126 deletions
+7 -7
View File
@@ -73,20 +73,20 @@ cd <cloned folder>
west init -l zephcore west init -l zephcore
west update west update
# Companion (with logging) # Companion (production — default, no extra conf needed)
west build -b wio_tracker_l1 zephcore --pristine west build -b wio_tracker_l1 zephcore --pristine
# Companion (production, no logging, no USB) # Companion (debug logging)
west build -b wio_tracker_l1 zephcore --pristine -- \ west build -b wio_tracker_l1 zephcore --pristine -- \
-DEXTRA_CONF_FILE="boards/common/prod.conf" -DEXTRA_CONF_FILE="boards/common/debug.conf"
# Repeater (with logging) # Repeater
west build -b rak4631 zephcore --pristine -- \ west build -b rak4631 zephcore --pristine -- \
-DEXTRA_CONF_FILE="boards/common/repeater.conf" -DEXTRA_CONF_FILE="boards/common/repeater.conf"
# Repeater (production) # Repeater (debug logging)
west build -b rak4631 zephcore --pristine -- \ west build -b rak4631 zephcore --pristine -- \
-DEXTRA_CONF_FILE="boards/common/repeater.conf;boards/common/prod.conf" -DEXTRA_CONF_FILE="boards/common/repeater.conf;boards/common/debug.conf"
# Repeater with packet logging (clean RAW/RX/TX lines only, no debug spam) # Repeater with packet logging (clean RAW/RX/TX lines only, no debug spam)
west build -b rak4631 zephcore --pristine -- \ west build -b rak4631 zephcore --pristine -- \
@@ -175,7 +175,7 @@ The old `txdelay`, `rxdelay`, and `direct.txdelay` commands are still accepted f
- **LoRa RX duty cycle**: CAD-based receive windowing reduces LoRa RX current from ~10-15mA to ~3-5mA. Auto-enabled for SX1262 companion and repeater builds (toggleable at runtime via `set rxduty on/off`). Disabled for LR1110 (mid-preamble lock issue) and SX127x. - **LoRa RX duty cycle**: CAD-based receive windowing reduces LoRa RX current from ~10-15mA to ~3-5mA. Auto-enabled for SX1262 companion and repeater builds (toggleable at runtime via `set rxduty on/off`). Disabled for LR1110 (mid-preamble lock issue) and SX127x.
- **Adaptive Power Control (APC)**: compiled in by default but disabled at runtime. Enable per-node with `set tx apc` -- automatically reduces TX power when echo SNR shows excess margin, ramping back up if echoes drop. See [apc.md](zephcore/apc.md). - **Adaptive Power Control (APC)**: compiled in by default but disabled at runtime. Enable per-node with `set tx apc` -- automatically reduces TX power when echo SNR shows excess margin, ramping back up if echoes drop. See [apc.md](zephcore/apc.md).
- **USB disabled in production**: Saves ~2-5mA and 62KB flash when logging is off - **Production by default**: No logging, no asserts, reboot-on-fatal. Add `debug.conf` to enable logging.
- **GPIO-gated GPS**: Powered on only during fix acquisition - **GPIO-gated GPS**: Powered on only during fix acquisition
## Configuration ## Configuration
+10 -10
View File
@@ -37,19 +37,19 @@ if [[ $1 == "nrf" ]]; then
for board in "${nRF_boards[@]}"; do for board in "${nRF_boards[@]}"; do
board_clean_for_path=$(echo "$board" | sed -e 's/\//-/g') board_clean_for_path=$(echo "$board" | sed -e 's/\//-/g')
# build nRF companions # build nRF companions (production is the default — no extra conf needed)
echo "Now building $board companion" echo "Now building $board companion"
if [[ $board == "wio_tracker_l1" ]]; then if [[ $board == "wio_tracker_l1" ]]; then
west build -b "$board" zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/prod.conf" -DCONFIG_ZEPHCORE_EASTER_EGG_DOOM=y west build -b "$board" zephcore --pristine -- -DCONFIG_ZEPHCORE_EASTER_EGG_DOOM=y
else else
west build -b "$board" zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/prod.conf" west build -b "$board" zephcore --pristine
fi fi
mv build/zephyr/zephyr.uf2 firmware/"$board"-companion-"$COMMIT_HASH".uf2 mv build/zephyr/zephyr.uf2 firmware/"$board"-companion-"$COMMIT_HASH".uf2
mv build/zephyr/zephyr.zip firmware/"$board"-companion-"$COMMIT_HASH".zip mv build/zephyr/zephyr.zip firmware/"$board"-companion-"$COMMIT_HASH".zip
# build nRF repeaters # build nRF repeaters
echo "Now building $board repeater" echo "Now building $board repeater"
west build -b "$board" zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/repeater.conf;boards/common/prod.conf" west build -b "$board" zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/repeater.conf"
mv build/zephyr/zephyr.uf2 firmware/"$board"-repeater-"$COMMIT_HASH".uf2 mv build/zephyr/zephyr.uf2 firmware/"$board"-repeater-"$COMMIT_HASH".uf2
mv build/zephyr/zephyr.zip firmware/"$board"-repeater-"$COMMIT_HASH".zip mv build/zephyr/zephyr.zip firmware/"$board"-repeater-"$COMMIT_HASH".zip
@@ -58,12 +58,12 @@ if [[ $1 == "nrf" ]]; then
# Heltec_t114_without_display_* PIO envs. # Heltec_t114_without_display_* PIO envs.
if [[ $board == "heltec_t114" ]]; then if [[ $board == "heltec_t114" ]]; then
echo "Now building $board companion (noscreen)" echo "Now building $board companion (noscreen)"
west build -b "$board" zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/prod.conf;boards/nrf52840/heltec_t114/no_display.conf" west build -b "$board" zephcore --pristine -- -DEXTRA_CONF_FILE="boards/nrf52840/heltec_t114/no_display.conf"
mv build/zephyr/zephyr.uf2 firmware/"$board"-companion-noscreen-"$COMMIT_HASH".uf2 mv build/zephyr/zephyr.uf2 firmware/"$board"-companion-noscreen-"$COMMIT_HASH".uf2
mv build/zephyr/zephyr.zip firmware/"$board"-companion-noscreen-"$COMMIT_HASH".zip mv build/zephyr/zephyr.zip firmware/"$board"-companion-noscreen-"$COMMIT_HASH".zip
echo "Now building $board repeater (noscreen)" echo "Now building $board repeater (noscreen)"
west build -b "$board" zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/repeater.conf;boards/common/prod.conf;boards/nrf52840/heltec_t114/no_display.conf" west build -b "$board" zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/repeater.conf;boards/nrf52840/heltec_t114/no_display.conf"
mv build/zephyr/zephyr.uf2 firmware/"$board"-repeater-noscreen-"$COMMIT_HASH".uf2 mv build/zephyr/zephyr.uf2 firmware/"$board"-repeater-noscreen-"$COMMIT_HASH".uf2
mv build/zephyr/zephyr.zip firmware/"$board"-repeater-noscreen-"$COMMIT_HASH".zip mv build/zephyr/zephyr.zip firmware/"$board"-repeater-noscreen-"$COMMIT_HASH".zip
fi fi
@@ -82,9 +82,9 @@ if [[ $1 == "esp32" ]]; then
fi fi
if [[ $2 == "companions" ]]; then if [[ $2 == "companions" ]]; then
# build ESP32 companions # build ESP32 companions (production is the default)
echo "Now building $board companion" echo "Now building $board companion"
west build -b "$board" zephcore --pristine --sysbuild -- -DEXTRA_CONF_FILE="boards/common/prod.conf" west build -b "$board" zephcore --pristine --sysbuild
FLASH_SIZE=$( FLASH_SIZE=$(
python3 -c ' python3 -c '
import re import re
@@ -127,7 +127,7 @@ print(str(size // 1048576) + "MB")
if [[ $2 == "repeaters" ]]; then if [[ $2 == "repeaters" ]]; then
# build ESP32 repeaters # build ESP32 repeaters
echo "Now building $board repeater" echo "Now building $board repeater"
west build -b "$board" zephcore --pristine --sysbuild -- -DEXTRA_CONF_FILE="boards/common/repeater.conf;boards/common/prod.conf" west build -b "$board" zephcore --pristine --sysbuild -- -DEXTRA_CONF_FILE="boards/common/repeater.conf"
FLASH_SIZE=$( FLASH_SIZE=$(
python3 -c ' python3 -c '
import re import re
+2 -2
View File
@@ -511,11 +511,11 @@ Wolf3D-style raycaster on OLED: textured walls, 2 enemy types, shooting, HUD. By
### 9.1 Config Layering ### 9.1 Config Layering
``` ```
prj.conf (base: console, logging) prj.conf (base: console; production defaults — LOG=n, ASSERT=n)
→ boards/common/zephcore_common.conf (ALL boards: BLE, crypto, FS, LoRa, sensors) → boards/common/zephcore_common.conf (ALL boards: BLE, crypto, FS, LoRa, sensors)
→ boards/common/<platform>_common.conf (nrf52/esp32/nrf54l/mg24 specifics) → boards/common/<platform>_common.conf (nrf52/esp32/nrf54l/mg24 specifics)
→ boards/<mcu>/<board>/board.conf (board-specific pins, features) → boards/<mcu>/<board>/board.conf (board-specific pins, features)
→ [optional] repeater.conf, prod.conf (user extras, LAST = highest priority) → [optional] repeater.conf, debug.conf (user extras, LAST = highest priority)
``` ```
### 9.2 Key Kconfig Choices ### 9.2 Key Kconfig Choices
+1 -11
View File
@@ -306,17 +306,7 @@ if(ZEPHCORE_BOARD_CONF AND EXISTS ${ZEPHCORE_BOARD_CONF})
zephcore_auto_pair_overlay("${ZEPHCORE_BOARD_CONF}") zephcore_auto_pair_overlay("${ZEPHCORE_BOARD_CONF}")
endif() endif()
# Include logging tuning only for debug builds (when prod.conf is NOT active). # Production is the default. Debug logging is opt-in via debug.conf.
# This avoids Kconfig warnings from log settings that depend on CONFIG_LOG.
set(ZEPHCORE_LOGGING_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/logging.conf")
if(EXTRA_CONF_FILE MATCHES "prod\\.conf")
message(STATUS " Production build — skipping logging.conf")
else()
if(EXISTS ${ZEPHCORE_LOGGING_CONF})
list(APPEND ZEPHCORE_CONF_FILES ${ZEPHCORE_LOGGING_CONF})
zephcore_auto_pair_overlay("${ZEPHCORE_LOGGING_CONF}")
endif()
endif()
# Auto-include WiFi OTA for ESP32 repeater builds. # Auto-include WiFi OTA for ESP32 repeater builds.
# Enables WiFi AP + HTTP server + MCUboot image management. # Enables WiFi AP + HTTP server + MCUboot image management.
+21
View File
@@ -66,6 +66,27 @@ config ZEPHCORE_ROLE_COMPANION
BLE companion device that connects to ZephCore mobile app. BLE companion device that connects to ZephCore mobile app.
Includes BLE stack, contact management, offline queue, channels. Includes BLE stack, contact management, offline queue, channels.
config ZEPHCORE_COMPANION_USB
bool "Enable USB CDC companion transport"
depends on USB_DEVICE_STACK_NEXT
default y if ZEPHCORE_ROLE_COMPANION
help
Compile and init the USB CDC-ACM companion protocol stack
(ZephyrCompanionUSB / ZephyrUSBCDC) independently of CONFIG_LOG.
The full ~60-opcode binary protocol is available over USB even in
production builds (CONFIG_LOG=n). USB and BLE arbitrate the active
interface first-come-first-served: CMD_APP_START claims it for USB only
if no BLE session is already active (and is ignored while BLE is live);
on USB unplug the interface is handed back to BLE or released to idle.
Enabled by default for all companion builds on platforms that already
have CONFIG_USB_DEVICE_STACK_NEXT=y (all nRF52840 boards). On
platforms without USB hardware (MG24, ESP32 classic, C3/C6) the
depends forces this to n automatically — no conf change needed.
For ESP32-S3 boards, USB_DEVICE_STACK_NEXT must be enabled first via
boards/common/esp32s3_usb.conf before this becomes active.
config ZEPHCORE_ROLE_REPEATER config ZEPHCORE_ROLE_REPEATER
bool "Repeater" bool "Repeater"
help help
+72 -12
View File
@@ -122,6 +122,44 @@ static int64_t ble_tx_start_time = 0;
/* Active interface tracking */ /* Active interface tracking */
static enum zephcore_iface active_iface = ZEPHCORE_IFACE_NONE; static enum zephcore_iface active_iface = ZEPHCORE_IFACE_NONE;
/* active_iface is mutated from two threads — the Bluetooth callback thread
* (connect / security / pairing / disconnect) and the USB workqueue
* (CMD_APP_START / DTR drop). This mutex makes the check-then-act claim and
* release sequences atomic so the two transports can never both believe they
* own the interface (or lose a write to it). */
K_MUTEX_DEFINE(ble_iface_lock);
/* Atomically claim the interface for `who` iff it is currently idle.
* Returns true only if this call performed the NONE -> who transition. */
static bool iface_claim_if_idle(enum zephcore_iface who)
{
k_mutex_lock(&ble_iface_lock, K_FOREVER);
bool claimed = (active_iface == ZEPHCORE_IFACE_NONE);
if (claimed) {
active_iface = who;
}
k_mutex_unlock(&ble_iface_lock);
return claimed;
}
/* Atomically release the interface if `who` currently owns it. */
static void iface_release(enum zephcore_iface who)
{
k_mutex_lock(&ble_iface_lock, K_FOREVER);
if (active_iface == who) {
active_iface = ZEPHCORE_IFACE_NONE;
}
k_mutex_unlock(&ble_iface_lock);
}
/* Claim the active interface for BLE, but only if nothing else owns it.
* First-come-first-served: a live USB session must not be evicted by BLE
* connecting/pairing in the background. Returns true if BLE now owns it. */
static bool ble_claim_iface_if_idle(void)
{
return iface_claim_if_idle(ZEPHCORE_IFACE_BLE);
}
/* DLE tracking — set after successful DLE request to avoid double-request */ /* DLE tracking — set after successful DLE request to avoid double-request */
static bool dle_requested; static bool dle_requested;
@@ -298,6 +336,16 @@ static void connected(struct bt_conn *conn, uint8_t err)
return; return;
} }
LOG_INF("connected: %s", addr); LOG_INF("connected: %s", addr);
/* Reject BLE connections while USB is the active transport.
* The user connected via BLE expecting to exchange messages, but
* USB owns the interface they would get nothing and be confused. */
if (active_iface == ZEPHCORE_IFACE_USB) {
LOG_INF("connected: USB active — rejecting BLE connection from %s", addr);
bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
return;
}
current_conn = bt_conn_ref(conn); current_conn = bt_conn_ref(conn);
/* Zephyr stops advertising internally when the conn slot is consumed /* Zephyr stops advertising internally when the conn slot is consumed
@@ -349,9 +397,7 @@ static void disconnected(struct bt_conn *conn, uint8_t reason)
conn_params_pending = false; conn_params_pending = false;
/* Clear interface state if BLE was active */ /* Clear interface state if BLE was active */
if (active_iface == ZEPHCORE_IFACE_BLE) { iface_release(ZEPHCORE_IFACE_BLE);
active_iface = ZEPHCORE_IFACE_NONE;
}
/* Clear queues, retry state, and congestion */ /* Clear queues, retry state, and congestion */
k_msgq_purge(&ble_send_queue); k_msgq_purge(&ble_send_queue);
@@ -397,7 +443,7 @@ static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_
if (level >= BT_SECURITY_L2 && !ble_tx_ready) { if (level >= BT_SECURITY_L2 && !ble_tx_ready) {
LOG_INF("security established, enabling TX"); LOG_INF("security established, enabling TX");
ble_tx_ready = true; ble_tx_ready = true;
active_iface = ZEPHCORE_IFACE_BLE; ble_claim_iface_if_idle();
/* If CCC was already subscribed (bonded reconnect — phone writes /* If CCC was already subscribed (bonded reconnect — phone writes
* CCC before security_changed fires), kick TX now. On fresh * CCC before security_changed fires), kick TX now. On fresh
@@ -532,14 +578,12 @@ static void pairing_complete(struct bt_conn *conn, bool bonded)
ARG_UNUSED(conn); ARG_UNUSED(conn);
LOG_INF("pairing complete: bonded=%d", bonded); LOG_INF("pairing complete: bonded=%d", bonded);
/* Switch to BLE interface (fresh pairing only). if (ble_claim_iface_if_idle()) {
* Conn params and TX enable are handled in security_changed(), LOG_INF("pairing complete, activating BLE interface");
* which fires for both fresh pairing and bonded reconnects. */ } else {
if (active_iface == ZEPHCORE_IFACE_USB) { LOG_INF("pairing complete, %s already active — BLE paired but not promoted",
LOG_INF("switching from USB to BLE"); active_iface == ZEPHCORE_IFACE_USB ? "USB" : "BLE");
/* Main handles USB state clearing via on_connected callback */
} }
active_iface = ZEPHCORE_IFACE_BLE;
} }
static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason) static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason)
@@ -985,12 +1029,28 @@ uint32_t zephcore_ble_get_passkey(void)
enum zephcore_iface zephcore_ble_get_active_iface(void) enum zephcore_iface zephcore_ble_get_active_iface(void)
{ {
return active_iface; k_mutex_lock(&ble_iface_lock, K_FOREVER);
enum zephcore_iface iface = active_iface;
k_mutex_unlock(&ble_iface_lock);
return iface;
} }
void zephcore_ble_set_active_iface(enum zephcore_iface iface) void zephcore_ble_set_active_iface(enum zephcore_iface iface)
{ {
k_mutex_lock(&ble_iface_lock, K_FOREVER);
active_iface = iface; active_iface = iface;
k_mutex_unlock(&ble_iface_lock);
}
bool zephcore_ble_iface_try_claim(enum zephcore_iface who)
{
k_mutex_lock(&ble_iface_lock, K_FOREVER);
bool ok = (active_iface == ZEPHCORE_IFACE_NONE || active_iface == who);
if (ok) {
active_iface = who;
}
k_mutex_unlock(&ble_iface_lock);
return ok;
} }
struct k_msgq *zephcore_ble_get_recv_queue(void) struct k_msgq *zephcore_ble_get_recv_queue(void)
+8 -1
View File
@@ -65,10 +65,17 @@ bool zephcore_ble_is_advertising(void);
void zephcore_ble_set_passkey(uint32_t passkey); void zephcore_ble_set_passkey(uint32_t passkey);
uint32_t zephcore_ble_get_passkey(void); uint32_t zephcore_ble_get_passkey(void);
/** Get/set active interface (BLE/USB coexistence). */ /** Get/set active interface (BLE/USB coexistence). Both are thread-safe —
* active_iface is mutated from the BLE callback thread and the USB workqueue. */
enum zephcore_iface zephcore_ble_get_active_iface(void); enum zephcore_iface zephcore_ble_get_active_iface(void);
void zephcore_ble_set_active_iface(enum zephcore_iface iface); void zephcore_ble_set_active_iface(enum zephcore_iface iface);
/** Atomically claim the active interface for `who` unless the other transport
* already owns it. Succeeds (returns true) if the interface is idle or already
* held by `who`; fails if a different interface is active. Thread-safe use
* this instead of a get-then-set sequence to avoid a check-then-act race. */
bool zephcore_ble_iface_try_claim(enum zephcore_iface who);
/** Get recv/send queues for USB path sharing. */ /** Get recv/send queues for USB path sharing. */
struct k_msgq *zephcore_ble_get_recv_queue(void); struct k_msgq *zephcore_ble_get_recv_queue(void);
struct k_msgq *zephcore_ble_get_send_queue(void); struct k_msgq *zephcore_ble_get_send_queue(void);
+24 -8
View File
@@ -115,13 +115,17 @@ static void usb_rx_work_fn(struct k_work *work)
* is actually CMD_SEND_CHANNEL_TXT_MSG and meant the USB * is actually CMD_SEND_CHANNEL_TXT_MSG and meant the USB
* handshake silently dropped the app's first frame.) */ * handshake silently dropped the app's first frame.) */
if (payload_len >= 1 && payload[0] == 0x01 /* CMD_APP_START */) { if (payload_len >= 1 && payload[0] == 0x01 /* CMD_APP_START */) {
if (zephcore_ble_get_active_iface() == ZEPHCORE_IFACE_BLE && /* Atomically claim the interface for USB unless BLE
zephcore_ble_is_connected()) { * already owns it. try_claim succeeds when idle or
LOG_INF("usb_rx: CMD_APP_START, disconnecting BLE"); * already USB (reconnect) and fails only while a BLE
zephcore_ble_disconnect(); * session is live, so USB can't steal it and the
* compare-and-set can't race a concurrent BLE claim. */
if (zephcore_ble_iface_try_claim(ZEPHCORE_IFACE_USB)) {
zephcore_ble_set_enabled(false);
LOG_INF("usb_rx: CMD_APP_START → IFACE_USB");
} else {
LOG_INF("usb_rx: CMD_APP_START ignored, BLE is active");
} }
zephcore_ble_set_active_iface(ZEPHCORE_IFACE_USB);
LOG_INF("usb_rx: active_iface = IFACE_USB");
} }
/* Only process if USB is active interface */ /* Only process if USB is active interface */
@@ -155,9 +159,21 @@ static void on_dtr_change(bool dtr_active)
return; return;
} }
LOG_INF("usb_dtr: DTR dropped, USB disconnected"); LOG_INF("usb_dtr: DTR dropped, USB disconnected");
/* While USB owns the interface, BLE claims are rejected (see connected()),
* so this thread is the only writer of active_iface here the get/set
* pair below needs no extra locking beyond the thread-safe accessors. */
if (zephcore_ble_get_active_iface() == ZEPHCORE_IFACE_USB) { if (zephcore_ble_get_active_iface() == ZEPHCORE_IFACE_USB) {
zephcore_ble_set_active_iface(ZEPHCORE_IFACE_NONE); if (zephcore_ble_is_connected()) {
LOG_INF("usb_dtr: active_iface = IFACE_NONE"); /* BLE client is physically connected — hand off to it. */
zephcore_ble_set_active_iface(ZEPHCORE_IFACE_BLE);
LOG_INF("usb_dtr: → IFACE_BLE (BLE was connected)");
} else {
/* Nobody connected — go idle and restart advertising
* so the phone can find the companion again. */
zephcore_ble_set_active_iface(ZEPHCORE_IFACE_NONE);
zephcore_ble_set_enabled(true);
LOG_INF("usb_dtr: → IFACE_NONE, BLE advertising restarted");
}
} }
ring_buf_reset(&usb_ring_buf); ring_buf_reset(&usb_ring_buf);
usb_frame_len = 0; usb_frame_len = 0;
+40
View File
@@ -0,0 +1,40 @@
# Debug Config — opt-in logging and diagnostics.
#
# Production is the default. Pass this conf to get the full debug experience:
#
# west build -b rak4631 zephcore --pristine -- \
# -DEXTRA_CONF_FILE="boards/common/debug.conf"
#
# # With repeater role:
# west build -b rak4631 zephcore --pristine -- \
# -DEXTRA_CONF_FILE="boards/common/repeater.conf;boards/common/debug.conf"
# ========== Core debug features ==========
CONFIG_LOG=y
CONFIG_ASSERT=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=4096
CONFIG_THREAD_NAME=y
# Halt-and-inspect on fatal faults instead of the production reboot-on-fatal
# (prj.conf sets CONFIG_RESET_ON_FATAL_ERROR=y). A debugger needs the core
# stopped at the fault with a stack dump, not rebooted out from under it.
CONFIG_RESET_ON_FATAL_ERROR=n
# ========== Log tuning ==========
# ZephCore BLE modules at INF level (connection lifecycle, pairing, TX/RX).
CONFIG_ZEPHCORE_BLE_LOG_LEVEL_INF=y
# Deferred logging — non-blocking so BLE/LoRa timing is not disturbed.
CONFIG_LOG_BUFFER_SIZE=8192
CONFIG_LOG_PROCESS_TRIGGER_THRESHOLD=1
CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=2048
# RTT backend only — no UART backend on nRF52.
# UART backend stalls on uart_poll_out() when the USB host disconnects;
# RTT DROP mode discards messages without blocking.
CONFIG_LOG_BACKEND_RTT=y
CONFIG_LOG_BACKEND_RTT_MODE_DROP=y
# Silence noisy subsystems
CONFIG_CFB_LOG_LEVEL_ERR=y
@@ -0,0 +1,45 @@
/*
* Reroute console + shell off USB Serial/JTAG onto uart0 (physical TX/RX).
*
* ESP32-S3 USB Serial/JTAG shares the D+/D- pins with USB OTG and only one
* controller can own the port at a time. Boards that default their console to
* USB Serial/JTAG include this alongside esp32s3_usb_otg.dtsi so that, when
* esp32s3_usb.conf disables USB Serial/JTAG and hands the port to USB OTG, the
* console still has a home. Boards with a dedicated UART console (e.g. Heltec)
* do not need this.
*
* uart0 is disabled by default in the ESP32-S3 SoC dtsi, so we enable it and
* pin it to the chip's ROM-default UART0 pads (GPIO43 TX / GPIO44 RX) the
* same pins Heltec uses. Without this the console would point at a disabled,
* un-pinned node and produce no output at all.
*/
#include <zephyr/dt-bindings/pinctrl/esp-pinctrl-common.h>
#include <zephyr/dt-bindings/pinctrl/esp32s3-pinctrl.h>
#include <zephyr/dt-bindings/pinctrl/esp32s3-gpio-sigmap.h>
&pinctrl {
uart0_default: uart0_default {
group1 {
pinmux = <UART0_TX_GPIO43>;
output-high;
};
group2 {
pinmux = <UART0_RX_GPIO44>;
bias-pull-up;
};
};
};
&uart0 {
status = "okay";
current-speed = <115200>;
pinctrl-0 = <&uart0_default>;
pinctrl-names = "default";
};
/ {
chosen {
zephyr,console = &uart0;
zephyr,shell-uart = &uart0;
};
};
+24
View File
@@ -0,0 +1,24 @@
# ESP32-S3 USB CDC-ACM companion support
#
# ESP32-S3 has a DWC2 USB OTG peripheral (snps,dwc2 compatible) that supports
# the Zephyr USB NEXT stack. This conf enables it and turns on the companion
# USB transport on S3 boards. Do NOT use on ESP32 (original) or C3/C6 — those
# chips have no DWC2 controller.
#
# Build with:
# -DEXTRA_CONF_FILE="boards/common/esp32s3_usb.conf"
#
# Also add the cdc_acm_uart DTS node to the board overlay (the overlays include
# boards/common/esp32s3_usb_otg.dtsi). Without the DT node,
# DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) is false and
# zephcore_usbd_init() is compiled out at the #if check.
CONFIG_USB_DEVICE_STACK_NEXT=y
CONFIG_ZEPHCORE_COMPANION_USB=y
CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT=n
CONFIG_UART_LINE_CTRL=y
# Disable USB Serial/JTAG so it doesn't fight USB OTG for the D+/D- pins.
# ESP32-S3 has an internal mux — only one controller can own the port at a time.
# With USB companion active, the port IS the companion protocol channel.
CONFIG_ESP_USB_SERIAL_JTAG_ENABLED=n
@@ -0,0 +1,21 @@
/*
* ESP32-S3 USB OTG (DWC2) companion USB transport.
*
* Enables the snps,dwc2 USB OTG controller and attaches a CDC-ACM UART node
* for the binary companion protocol. The DWC2 driver is only compiled when
* CONFIG_USB_DEVICE_STACK_NEXT=y (set by boards/common/esp32s3_usb.conf);
* without that conf this node is inert.
*
* Boards whose default console rides USB Serial/JTAG (shares D+/D- with USB
* OTG) must also reroute the console off the shared pins see
* esp32s3_console_uart0.dtsi.
*/
zephyr_udc0: &usb_otg {
status = "okay";
};
&zephyr_udc0 {
cdc_acm_uart: cdc_acm_uart {
compatible = "zephyr,cdc-acm-uart";
};
};
-23
View File
@@ -1,23 +0,0 @@
# Logging Tuning — included automatically for debug builds only.
# Prod builds (CONFIG_LOG=n via prod.conf) skip this file entirely,
# avoiding Kconfig warnings from log settings that depend on CONFIG_LOG.
# ZephCore BLE modules at INF level (connection lifecycle, pairing, TX/RX)
# Default level is WRN — this ensures our INF messages come through.
# Avoid DBG level — it causes system workqueue stack overflow.
CONFIG_ZEPHCORE_BLE_LOG_LEVEL_INF=y
# Deferred mode (non-blocking) — buffer/thread config
CONFIG_LOG_BUFFER_SIZE=8192
CONFIG_LOG_PROCESS_TRIGGER_THRESHOLD=1
CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=2048
# Backends
# NOTE: UART backend on nRF52 routes through USB CDC ACM (zephyr,console = &cdc_acm_uart).
# There is no LOG_BACKEND_UART_MODE_DROP in Zephyr — if the USB host disconnects, the log
# processing thread stalls on uart_poll_out(). Disable UART backend on nRF52 to avoid this;
# RTT (below) is the reliable debug path and already configured with drop mode.
CONFIG_LOG_BACKEND_RTT=y
# Suppress noisy subsystem logs
CONFIG_CFB_LOG_LEVEL_ERR=y
+4 -1
View File
@@ -16,10 +16,13 @@ CONFIG_BT_TX_PROCESSOR_STACK_SIZE=2048
# Controller assert handler — without this, a controller assert silently # Controller assert handler — without this, a controller assert silently
# freezes the CPU at highest IRQ priority with zero log output. # freezes the CPU at highest IRQ priority with zero log output.
# Harmless when CONFIG_ASSERT=n, so it stays on for prod and debug.
CONFIG_BT_CTLR_ASSERT_HANDLER=y CONFIG_BT_CTLR_ASSERT_HANDLER=y
# ========== Debug ========== # ========== Debug ==========
CONFIG_ASSERT=y # CONFIG_ASSERT is debug-only (boards/common/debug.conf, opt-in). Setting it
# here would override the prj.conf production default (CONFIG_ASSERT=n) on every
# MG24 build, since this file loads after prj.conf.
# ========== Heap ========== # ========== Heap ==========
# Silabs BLE stack requires larger heap (override zephcore_common 2KB default) # Silabs BLE stack requires larger heap (override zephcore_common 2KB default)
+7 -6
View File
@@ -44,11 +44,12 @@ CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE=y
# Silently ignored when QSPI is not present (Kconfig guard: if NORDIC_QSPI_NOR). # Silently ignored when QSPI is not present (Kconfig guard: if NORDIC_QSPI_NOR).
CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
# ========== SEGGER RTT (J-Link debug) ==========
# RTT log backend is in boards/common/logging.conf (only included for debug builds).
CONFIG_USE_SEGGER_RTT=y
CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=4096
# ========== Debug ========== # ========== Debug ==========
CONFIG_ASSERT=y # SEGGER RTT and CONFIG_ASSERT are debug-only and live in boards/common/debug.conf
# (opt-in). Keeping them here would override the production defaults from
# prj.conf (CONFIG_USE_SEGGER_RTT=n, CONFIG_ASSERT=n) on every nRF52 build,
# since this file loads after prj.conf.
#
# BT_CTLR_ASSERT_HANDLER is harmless when CONFIG_ASSERT=n (no RAM/flash cost,
# the handler is simply never invoked) so it stays on for both prod and debug.
CONFIG_BT_CTLR_ASSERT_HANDLER=y CONFIG_BT_CTLR_ASSERT_HANDLER=y
+4 -6
View File
@@ -23,11 +23,9 @@ CONFIG_BT_CTLR_DATA_LENGTH_MAX=251
# Coded PHY — enable in nRF LL controller (host auto-request in zephcore_common.conf) # Coded PHY — enable in nRF LL controller (host auto-request in zephcore_common.conf)
CONFIG_BT_CTLR_PHY_CODED=y CONFIG_BT_CTLR_PHY_CODED=y
# ========== SEGGER RTT (J-Link / CMSIS-DAP debug) ==========
CONFIG_LOG_BACKEND_RTT=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=4096
# ========== Debug ========== # ========== Debug ==========
CONFIG_ASSERT=y # SEGGER RTT and CONFIG_ASSERT are debug-only and live in boards/common/debug.conf
# (opt-in). Keeping them here would override the production defaults from
# prj.conf on every nRF54L build, since this file loads after prj.conf.
# BT_CTLR_ASSERT_HANDLER is harmless when CONFIG_ASSERT=n, so it stays on.
CONFIG_BT_CTLR_ASSERT_HANDLER=y CONFIG_BT_CTLR_ASSERT_HANDLER=y
-22
View File
@@ -1,22 +0,0 @@
# Production Config - Common to ALL boards (Companion & Repeater)
# Build: west build -b <board> zephcore --pristine -- \
# -DEXTRA_CONF_FILE="boards/common/prod.conf"
#
# Disables logging for smaller binary and lower power consumption.
# USB CDC still works for CLI commands on repeaters.
# Disable logging subsystem (saves ~62KB flash, ~2-5mA power)
CONFIG_LOG=n
# Disable asserts (saves flash, removes CMake warning)
CONFIG_ASSERT=n
# Disable SEGGER RTT (saves ~4KB RAM ring buffer + init code)
CONFIG_USE_SEGGER_RTT=n
# Disable thread name strings (saves flash — only useful for debug)
CONFIG_THREAD_NAME=n
# Max contacts: Kconfig default is 350 — fits nRF52840 RAM with ~10% headroom
# and is safe for 100KB ExtraFS. RAM-limited boards (xiao_nrf54l15=450,
# xiao_esp32c3=300) override in their own board.conf. Do not set here.
+1 -5
View File
@@ -29,7 +29,6 @@ CONFIG_PSA_WANT_KEY_TYPE_HMAC=y
# ========== Stack Protection ========== # ========== Stack Protection ==========
CONFIG_HW_STACK_PROTECTION=y CONFIG_HW_STACK_PROTECTION=y
CONFIG_THREAD_NAME=y
# ========== Hardware Basics ========== # ========== Hardware Basics ==========
CONFIG_ADC=y CONFIG_ADC=y
@@ -195,10 +194,7 @@ CONFIG_INPUT_EVENT_DUMP=n
CONFIG_INPUT_MODE_SYNCHRONOUS=y CONFIG_INPUT_MODE_SYNCHRONOUS=y
# ========== Logging ========== # ========== Logging ==========
# RTT log backend: DROP mode (not BLOCK) — when no RTT host is connected the # Log backend tuning lives in boards/common/debug.conf (opt-in).
# buffer is always full; BLOCK mode sleeps 20ms per message with irq_lock held,
# disrupting BLE timing and cascading into buffer exhaustion deadlocks.
CONFIG_LOG_BACKEND_RTT_MODE_DROP=y
# Silence USB CH9 control-transfer spam (dozens of INF lines during enumeration). # Silence USB CH9 control-transfer spam (dozens of INF lines during enumeration).
# WRN keeps real USB errors visible. # WRN keeps real USB errors visible.
@@ -101,3 +101,6 @@
/* LittleFS auto-mount — standard /lfs mount point */ /* LittleFS auto-mount — standard /lfs mount point */
#include "../../common/filesystem.dtsi" #include "../../common/filesystem.dtsi"
/* USB CDC-ACM — companion USB transport (esp32s3_usb.conf). Heltec has a
* dedicated UART console, so no console reroute is needed. */
#include "../../common/esp32s3_usb_otg.dtsi"
@@ -101,3 +101,6 @@
/* LittleFS auto-mount — standard /lfs mount point */ /* LittleFS auto-mount — standard /lfs mount point */
#include "../../common/filesystem.dtsi" #include "../../common/filesystem.dtsi"
/* USB CDC-ACM — companion USB transport (esp32s3_usb.conf). Heltec has a
* dedicated UART console, so no console reroute is needed. */
#include "../../common/esp32s3_usb_otg.dtsi"
@@ -109,3 +109,8 @@
/* LittleFS auto-mount — standard /lfs mount point */ /* LittleFS auto-mount — standard /lfs mount point */
#include "../../common/filesystem.dtsi" #include "../../common/filesystem.dtsi"
/* USB OTG companion transport. station_g2 consoles over USB Serial/JTAG, which
* shares D+/D- with USB OTG, so the console is rerouted to uart0. */
#include "../../common/esp32s3_console_uart0.dtsi"
#include "../../common/esp32s3_usb_otg.dtsi"
@@ -86,3 +86,8 @@
/* LittleFS auto-mount — standard /lfs mount point */ /* LittleFS auto-mount — standard /lfs mount point */
#include "../../common/filesystem.dtsi" #include "../../common/filesystem.dtsi"
/* USB OTG companion transport. Consoles over USB Serial/JTAG (shares D+/D-
* with USB OTG), so the console is rerouted to uart0. */
#include "../../common/esp32s3_console_uart0.dtsi"
#include "../../common/esp32s3_usb_otg.dtsi"
@@ -7,7 +7,7 @@ CONFIG_BT_DIS_MODEL_NUMBER_STR="Seeed XIAO nRF52840"
CONFIG_ZEPHCORE_SD_FWID=0x0123 CONFIG_ZEPHCORE_SD_FWID=0x0123
# Default debug (logging.conf) sends LOG_* to RTT only. Plain XIAO is usually # Default debug (debug.conf) sends LOG_* to RTT only. Plain XIAO is usually
# flashed/debugged over USB without an SWD J-Link — enable UART backend so # flashed/debugged over USB without an SWD J-Link — enable UART backend so
# LOG_* appears on the CDC ACM serial port (same as zephyr,console). # LOG_* appears on the CDC ACM serial port (same as zephyr,console).
# RTT remains enabled for J-Link; use DROP mode (see zephcore_common.conf). # RTT remains enabled for J-Link; use DROP mode (see zephcore_common.conf).
+10 -4
View File
@@ -16,10 +16,16 @@ CONFIG_UART_CONSOLE=y
CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_RING_BUFFER=y CONFIG_RING_BUFFER=y
# Logging subsystem — enabled by default (prod.conf disables with CONFIG_LOG=n). # Production by default — add boards/common/debug.conf to enable logging.
# Log tuning (buffer size, backends, etc.) is in boards/common/logging.conf, CONFIG_LOG=n
# which CMake auto-includes only when prod.conf is NOT active. CONFIG_ASSERT=n
CONFIG_LOG=y CONFIG_USE_SEGGER_RTT=n
CONFIG_THREAD_NAME=n
# Reboot on any fatal error (stack overflow, hard fault, etc.) instead of
# spinning forever. Asserts are disabled so they won't reach here, but all
# other fatal paths (k_panic, CPU exceptions) still get a clean reboot.
CONFIG_RESET_ON_FATAL_ERROR=y
# ========== Power Management ========== # ========== Power Management ==========
# Note: nRF52840 automatically uses WFI (Wait For Interrupt) in idle thread. # Note: nRF52840 automatically uses WFI (Wait For Interrupt) in idle thread.
+14 -7
View File
@@ -34,7 +34,13 @@ LOG_MODULE_REGISTER(zephcore_main, CONFIG_ZEPHCORE_MAIN_LOG_LEVEL);
#include <ZephyrBLE.h> #include <ZephyrBLE.h>
#if IS_ENABLED(CONFIG_LOG) /* The USB CDC companion stack is compiled when either logging needs the CDC
* console (debug builds) or the binary companion transport is explicitly
* enabled (CONFIG_ZEPHCORE_COMPANION_USB, default-y on USB-capable boards). */
#define ZEPHCORE_USB_STACK \
(IS_ENABLED(CONFIG_LOG) || IS_ENABLED(CONFIG_ZEPHCORE_COMPANION_USB))
#if ZEPHCORE_USB_STACK
#include <ZephyrCompanionUSB.h> #include <ZephyrCompanionUSB.h>
#include <ZephyrUSBCDC.h> #include <ZephyrUSBCDC.h>
#endif #endif
@@ -181,7 +187,7 @@ static void ble_on_connected(void)
/* BLE disconnected callback — clean up state and notify UI */ /* BLE disconnected callback — clean up state and notify UI */
static void ble_on_disconnected(void) static void ble_on_disconnected(void)
{ {
#if IS_ENABLED(CONFIG_LOG) #if ZEPHCORE_USB_STACK
zephcore_usb_companion_reset_rx(); zephcore_usb_companion_reset_rx();
#endif #endif
#ifdef ZEPHCORE_LORA #ifdef ZEPHCORE_LORA
@@ -562,8 +568,8 @@ int main(void)
/* USB CDC init up front so the host can enumerate, then block until the /* USB CDC init up front so the host can enumerate, then block until the
* host opens the port (DTR-high) event-driven via the shared usbd * host opens the port (DTR-high) event-driven via the shared usbd
* message callback. Unplugged 1 s timeout, banner is buffered for any * message callback. Unplugged 1 s timeout, banner is buffered for any
* later attach. CONFIG_LOG=n prod builds skip the whole stack. */ * later attach. */
#if IS_ENABLED(CONFIG_LOG) && DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) && \ #if ZEPHCORE_USB_STACK && DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) && \
!IS_ENABLED(CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT) !IS_ENABLED(CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT)
zephcore_usbd_init(); zephcore_usbd_init();
zephcore_usbd_wait_dtr(1000); zephcore_usbd_wait_dtr(1000);
@@ -780,10 +786,11 @@ int main(void)
zephcore_ble_init(&ble_cbs); zephcore_ble_init(&ble_cbs);
/* /*
* USB CDC ACM - only enabled when logging is enabled (debug builds). * USB CDC ACM companion transport.
* Production builds (CONFIG_LOG=n) skip USB entirely, saving ~2-5mA. * Enabled when logging is on (debug builds) OR when explicitly requested
* via CONFIG_ZEPHCORE_COMPANION_USB (e.g. prod builds on USB-capable boards).
*/ */
#if IS_ENABLED(CONFIG_LOG) #if ZEPHCORE_USB_STACK
zephcore_usb_companion_init(&mesh_events, &rx_process_work, MESH_EVENT_BLE_RX, zephcore_usb_companion_init(&mesh_events, &rx_process_work, MESH_EVENT_BLE_RX,
&zephyr_board); &zephyr_board);
#endif #endif