diff --git a/docs/migration-notes.md b/docs/migration-notes.md index 989feec..3549a19 100644 --- a/docs/migration-notes.md +++ b/docs/migration-notes.md @@ -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). diff --git a/zephyr_app/README.md b/zephyr_app/README.md index e6b73b3..efa5d15 100644 --- a/zephyr_app/README.md +++ b/zephyr_app/README.md @@ -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) diff --git a/zephyr_app/boards/esp32_devkitc_wroom.overlay b/zephyr_app/boards/esp32_devkitc_wroom.overlay index 0e36f42..ce5b33b 100644 --- a/zephyr_app/boards/esp32_devkitc_wroom.overlay +++ b/zephyr_app/boards/esp32_devkitc_wroom.overlay @@ -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 { diff --git a/zephyr_app/boards/nucleo_f401re.overlay b/zephyr_app/boards/nucleo_f401re.overlay index a5620c5..4c36d0e 100644 --- a/zephyr_app/boards/nucleo_f401re.overlay +++ b/zephyr_app/boards/nucleo_f401re.overlay @@ -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 { diff --git a/zephyr_app/boards/rpi_pico.overlay b/zephyr_app/boards/rpi_pico.overlay index 66b5007..6e69e20 100644 --- a/zephyr_app/boards/rpi_pico.overlay +++ b/zephyr_app/boards/rpi_pico.overlay @@ -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>; }; }; diff --git a/zephyr_app/boards/rpi_pico_rp2040_w.conf b/zephyr_app/boards/rpi_pico_rp2040_w.conf index 881cd2f..f40b790 100644 --- a/zephyr_app/boards/rpi_pico_rp2040_w.conf +++ b/zephyr_app/boards/rpi_pico_rp2040_w.conf @@ -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 diff --git a/zephyr_app/src/ble_nus.c b/zephyr_app/src/ble_nus.c index 50da41f..f9fe9e3 100644 --- a/zephyr_app/src/ble_nus.c +++ b/zephyr_app/src/ble_nus.c @@ -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);