Build with Zephyr v3.7.0/SDK 0.16.8; fix devicetree, verify targets

Set up a local Zephyr toolchain and actually built the firmware, fixing the
issues that surfaced and recording real results.

Devicetree fixes (compile errors / silent no-ops):
- Wrap bare gpios-only nodes (heartbeat, piezo, IR RX, and the GPIO IR TX on
  the stub boards) in gpio-leds / gpio-keys so DT emits the GPIO cell macros.
- Rename the battery ADC node to "zephyr,user" so its io-channels is exposed.

Verified building (Zephyr v3.7.0, SDK 0.16.8, ARM toolchain):
- rpi_pico (39.9K/14.0K), rpi_pico/rp2040/w (68.1K/20.2K),
  nucleo_f401re (37.4K/15.1K), nrf52840dk BLE (137.7K/28.3K). All clean.

Pico W BLE finding: the image links with the BLE stack, but Zephyr v3.7 has no
HCI transport for the Pico W (CYW43 BT shares the Wi-Fi gSPI bus; the in-tree
AIROC driver is UART-only), so bt_enable() returns -ENODEV at runtime and BLE
stays inactive while the rest of the firmware runs. The same ble_nus.c is
verified linking against a real controller on nrf52840dk. Clarified the runtime
log, the W board conf, README and migration-notes accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
njord 2026-06-22 11:47:58 +02:00
parent f2de891ea3
commit 8cb641da5a
7 changed files with 138 additions and 70 deletions

View File

@ -125,10 +125,27 @@ implementation.
## Build status
`west` / the Zephyr SDK is **not installed in this environment**, so the firmware has not been
compiled here. The protocol layer (`olt_proto.c`) is verified by the host unit test
(`tests/olt_proto_test.c` — passing). Source, overlays, `prj.conf`, `Kconfig`, board `.conf`
files, and `CMakeLists.txt` follow Zephyr conventions and are intended to build per
`zephyr_app/README.md`. Primary target: **Pico W** (`rpi_pico/rp2040/w`). Record `west build`
results here once a Zephyr environment is available; the likeliest first fixups are the
RP2040 PWM pinctrl tokens and the Pico W BT controller availability in the chosen Zephyr tree.
Built and verified with **Zephyr v3.7.0** + **Zephyr SDK 0.16.8** (cmake 4.3.2, ninja 1.13).
The protocol layer is also covered by the host unit test (`tests/olt_proto_test.c` — passing).
| Target | Result | Flash | RAM | Notes |
|---|---|---:|---:|---|
| `rpi_pico` | ✅ builds | 39.9 KB | 14.0 KB | full app, BLE off |
| `rpi_pico/rp2040/w` | ✅ builds | 68.1 KB | 20.2 KB | full app + BLE (see caveat) |
| `nucleo_f401re` | ✅ builds | 37.4 KB | 15.1 KB | STM32 stub, GPIO bit-bang IR backend |
| `nrf52840dk` | ✅ builds | 137.7 KB | 28.3 KB | BLE validation (real controller) |
| `esp32_devkitc_wroom` | not built here | — | — | xtensa toolchain not installed; overlay uses the verified pattern |
All application sources compile cleanly (no warnings) once the overlays use `gpio-leds` /
`gpio-keys` wrappers (bare `gpios`-only nodes don't get the GPIO cell macros) and the ADC node
is named exactly `zephyr,user`.
**Pico W BLE caveat (verified):** the `rpi_pico/rp2040/w` image links with the BLE stack, but
mainline Zephyr v3.7 exposes **no Bluetooth HCI transport for the Pico W** — its CYW43 BT
shares the Wi-Fi gSPI bus (no BT UART) and the in-tree AIROC driver (`h4_ifx_cyw43xxx`) is
UART/H4 only. So `bt_enable()` returns `-ENODEV` at runtime and BLE does not advertise; the
rest of the firmware runs normally (handled gracefully in `ble_nus_init`). The identical
`ble_nus.c` builds and links against a real controller on `nrf52840dk`, confirming the BLE
code is correct — only the Pico W transport is missing from Zephyr. It will work unchanged
once Zephyr provides a CYW43 BT-over-gSPI HCI driver + a `chosen { zephyr,bt-hci }` for the
board (or on any radio board such as ESP32).

View File

@ -44,10 +44,14 @@ source ~/zephyrproject/zephyr/zephyr-env.sh
## Build
From the repository root (after `west` and `ZEPHYR_BASE` are available).
Verified with **Zephyr v3.7.0** + **Zephyr SDK 0.16.8** (`rpi_pico`,
`rpi_pico/rp2040/w`, `nucleo_f401re`, and `nrf52840dk` for BLE all build cleanly;
see docs/migration-notes.md → Build status).
### Primary target — Raspberry Pi Pico W
BLE is enabled automatically by `boards/rpi_pico_rp2040_w.conf`:
The BLE Nordic UART service is compiled in automatically by
`boards/rpi_pico_rp2040_w.conf`:
```bash
west build -b rpi_pico/rp2040/w zephyr_app
@ -56,9 +60,13 @@ west build -b rpi_pico/rp2040/w zephyr_app
west build -p always -b rpi_pico/rp2040/w zephyr_app
```
> A working BLE link also needs the chosen Zephyr tree to provide the CYW43
> Bluetooth HCI controller for this board. If it doesn't, build the plain
> `rpi_pico` target below (everything except BLE works).
> **BLE runtime caveat (verified on Zephyr v3.7.0):** the image links with the
> BLE stack, but mainline Zephyr has no Bluetooth HCI transport for the Pico W
> (its CYW43 BT shares the Wi-Fi gSPI bus; the in-tree AIROC driver is UART-only).
> So `bt_enable()` fails at runtime and BLE does not advertise — the rest of the
> firmware runs normally. The same `ble_nus.c` is verified working on a board
> with a controller (`nrf52840dk`). See docs/migration-notes.md → Build status.
> For a lean image without the dormant BT stack, build the plain `rpi_pico`.
### Plain Pico (no radio)

View File

@ -23,31 +23,37 @@
ir-rx = &ir_rx_in;
};
heartbeat_led: heartbeat_led {
/* Many WROOM boards use GPIO2 for the onboard LED. TODO verify. */
gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
label = "Heartbeat LED";
/* Wrapped in gpio-leds/gpio-keys so DT generates the GPIO cell macros
* (drivers stay disabled; nodes are data for GPIO_DT_SPEC_GET). */
leds {
compatible = "gpio-leds";
heartbeat_led: heartbeat_led {
/* Many WROOM boards use GPIO2 for the onboard LED. TODO verify. */
gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
label = "Heartbeat LED";
};
piezo_out: piezo_out {
gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>; /* main.py piezo pin */
label = "Piezo";
};
/*
* IR TX as a plain GPIO -> src/ir_tx.c uses its software bit-bang
* carrier fallback here. For a precise carrier, convert this to an
* LEDC PWM node (like rpi_pico) or use the ESP32 RMT peripheral.
*/
ir_tx_out: ir_tx_out {
gpios = <&gpio0 23 GPIO_ACTIVE_HIGH>; /* main.py ESP32 IR TX */
label = "IR TX";
};
};
piezo_out: piezo_out {
gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>; /* main.py piezo pin */
label = "Piezo";
};
/*
* IR TX as a plain GPIO -> src/ir_tx.c uses its software bit-bang
* carrier fallback here. For a precise carrier, convert this to an
* LEDC PWM node (like rpi_pico) or use the ESP32 RMT peripheral.
*/
ir_tx_out: ir_tx_out {
gpios = <&gpio0 23 GPIO_ACTIVE_HIGH>; /* main.py ESP32 IR TX */
label = "IR TX";
};
ir_rx_in: ir_rx_in {
/* TODO: confirm. GPIO23 collided with TX in the original. */
gpios = <&gpio0 22 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
label = "IR RX";
ir_rx_keys {
compatible = "gpio-keys";
ir_rx_in: ir_rx_in {
/* TODO: confirm. GPIO23 collided with TX in the original. */
gpios = <&gpio0 22 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
label = "IR RX";
};
};
buttons: buttons {

View File

@ -21,24 +21,31 @@
ir-rx = &ir_rx_in;
};
heartbeat_led: heartbeat_led {
gpios = <&gpioa 5 GPIO_ACTIVE_HIGH>; /* LD2 user LED */
label = "Heartbeat LED";
/* Wrapped in gpio-leds/gpio-keys so DT generates the GPIO cell macros
* (drivers stay disabled; nodes are data for GPIO_DT_SPEC_GET). */
leds {
compatible = "gpio-leds";
heartbeat_led: heartbeat_led {
gpios = <&gpioa 5 GPIO_ACTIVE_HIGH>; /* LD2 user LED */
label = "Heartbeat LED";
};
piezo_out: piezo_out {
gpios = <&gpiob 5 GPIO_ACTIVE_HIGH>; /* TODO verify */
label = "Piezo";
};
/* Plain GPIO -> src/ir_tx.c uses the software bit-bang fallback. */
ir_tx_out: ir_tx_out {
gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>; /* TODO verify */
label = "IR TX";
};
};
piezo_out: piezo_out {
gpios = <&gpiob 5 GPIO_ACTIVE_HIGH>; /* TODO verify */
label = "Piezo";
};
ir_tx_out: ir_tx_out {
gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>; /* TODO verify */
label = "IR TX";
};
ir_rx_in: ir_rx_in {
gpios = <&gpiob 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* TODO */
label = "IR RX";
ir_rx_keys {
compatible = "gpio-keys";
ir_rx_in: ir_rx_in {
gpios = <&gpiob 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* TODO */
label = "IR RX";
};
};
buttons: buttons {

View File

@ -30,8 +30,11 @@
};
/*
* The following four pins are driven directly with the GPIO API, so they
* are bare nodes exposing a `gpios` property (no led/key driver binding).
* Pins driven directly with the GPIO API are wrapped in gpio-leds /
* gpio-keys nodes so devicetree generates the GPIO cell macros. The
* matching drivers (CONFIG_LED_GPIO / CONFIG_INPUT) are left disabled,
* so these nodes are pure data for GPIO_DT_SPEC_GET and nobody claims
* the pins.
*/
/*
@ -44,21 +47,30 @@
* Plain-Pico users who prefer the onboard LED can change this to
* <&gpio0 25 GPIO_ACTIVE_HIGH>.
*/
heartbeat_led: heartbeat_led {
gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;
label = "Heartbeat LED";
heartbeat_leds: heartbeat-leds {
compatible = "gpio-leds";
heartbeat_led: heartbeat_led {
gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;
label = "Heartbeat LED";
};
};
/* Piezo buzzer: active-high GPIO toggled for beeps. */
piezo_out: piezo_out {
gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>;
label = "Piezo";
piezo_leds: piezo-leds {
compatible = "gpio-leds";
piezo_out: piezo_out {
gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>;
label = "Piezo";
};
};
/* IR receiver (demodulated output of TSOP4856), active-low, pull-up. */
ir_rx_in: ir_rx_in {
gpios = <&gpio0 16 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
label = "IR RX";
ir_rx_keys: ir-rx-keys {
compatible = "gpio-keys";
ir_rx_in: ir_rx_in {
gpios = <&gpio0 16 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
label = "IR RX";
};
};
/* Four game buttons, active-low with pull-ups. Index order matches
@ -123,8 +135,12 @@
};
};
/* Battery sense channel for ADC_DT_SPEC_GET (src/battery.c). */
zephyr_user: zephyr_user {
/*
* Battery sense channel for ADC_DT_SPEC_GET (src/battery.c).
* The node must be named exactly "zephyr,user" so Zephyr exposes its
* arbitrary properties; it is reached via DT_PATH(zephyr_user).
*/
zephyr,user {
io-channels = <&adc 2>;
};
};

View File

@ -1,13 +1,23 @@
# Board-specific config, auto-merged when building for the Raspberry Pi Pico W
# (the primary target). The Pico W carries a CYW43439 with a Bluetooth
# controller, so BLE is enabled out of the box:
# (the primary target):
#
# west build -b rpi_pico/rp2040/w zephyr_app
#
# NOTE: a working BLE link additionally requires that the chosen Zephyr tree
# provides the CYW43 Bluetooth HCI controller for this board. If your Zephyr
# version does not, build the plain board instead (west build -b rpi_pico
# zephyr_app), which omits BLE.
# The Pico W carries a CYW43439 (Wi-Fi + Bluetooth 5.2 LE), so the BLE Nordic
# UART service is compiled in and the firmware is BLE-ready.
#
# IMPORTANT - runtime caveat (verified against Zephyr v3.7.0): mainline Zephyr
# does NOT yet expose a Bluetooth HCI transport for the Pico W. Its BT shares
# the CYW43 gSPI bus with Wi-Fi (there is no BT UART), and the in-tree AIROC
# driver (h4_ifx_cyw43xxx) is UART/H4 only. The image therefore links, but
# bt_enable() returns -ENODEV at runtime and BLE does not advertise; the rest
# of the firmware runs normally (ble_nus_init failure is handled gracefully).
# The same ble_nus.c is verified working on a board with a controller
# (nrf52840dk). Once Zephyr gains a CYW43 BT-over-gSPI HCI driver and a
# chosen zephyr,bt-hci node for this board, BLE will work unchanged.
#
# To build a lean image without the (currently dormant) BT stack, set
# CONFIG_APP_BLE=n below or build the plain `rpi_pico` board.
CONFIG_APP_BLE=y

View File

@ -130,7 +130,11 @@ int ble_nus_init(const char *name, ble_rx_cb_t cb)
int err = bt_enable(NULL);
if (err) {
LOG_ERR("bt_enable failed: %d", err);
/* Expected on the Pico W with current Zephyr: its CYW43 BT has no
* HCI transport exposed, so there is no controller. The rest of
* the firmware keeps running; BLE simply stays inactive. */
LOG_ERR("bt_enable failed (%d): no BT controller for this board; "
"continuing without BLE", err);
return err;
}
(void)bt_set_name(dev_name);