diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index b6960e8..e3a58bc 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -871,6 +871,12 @@ hook so a CLI change also stops a lit heartbeat and refreshes the UI's LEDs page `NodePrefs.leds_disabled` (companion offset 93; repeater offset 120, magic-encoded — see §13). Does not cover the display backlight, which has its own UI brightness setting (`display_brightness`). +**LED brightness** (`set leds.brightness 0-100`): a board that aliases `heartbeat-pwm-led` / +`lora-tx-pwm-led` drives that LED through PWM (`zephcore_led_pwm_write()`), and the PWM alias wins over +`led0` / `lora-tx-led`. `CONFIG_ZEPHCORE_LED_PWM` follows the aliases: it selects `CONFIG_PWM` and stops +the server and observer mains from configuring `led0` as a GPIO, because that pin belongs to the PWM +driver. Persisted in `NodePrefs.led_brightness` (companion offset 272; repeater offset 311). + ### 7.6 WiFi / MQTT / TCP Transports - **`adapters/wifi/ZephyrWiFiStation.c`**: WiFi STA client (ESP32) used by the observer, the repeater uplink and the WiFi companion diff --git a/docs/CLI_commands.md b/docs/CLI_commands.md index f094afc..9a8ff20 100644 --- a/docs/CLI_commands.md +++ b/docs/CLI_commands.md @@ -497,7 +497,7 @@ four radio parameters together, since they are one interop-critical set. the PWM path, dimmable with `leds.brightness` like the boards above. On ESP32, a board in either PWM category must not configure that pin as plain GPIO in `main()`, or the GPIO matrix takes the pin back from the LEDC peripheral and the PWM output goes dark; `src/server_main_common.cpp` and - `app/main_observer.cpp` skip it automatically (`ZEPHCORE_HAS_PWM_SHARED_LED`) whenever a PWM LED alias + `app/main_observer.cpp` skip it automatically (`CONFIG_ZEPHCORE_LED_PWM`) whenever a PWM LED alias is present. - **No radio LED (19)**: including `wio_tracker_l1`, `t1000_e`, `meshtracker_x1`, Heltec V3/V4 and both ProMicros. `set leds.radio` is accepted and stored (so the setting survives onto a board that does diff --git a/zephcore/Kconfig b/zephcore/Kconfig index dd5d4d2..ace48d7 100644 --- a/zephcore/Kconfig +++ b/zephcore/Kconfig @@ -726,6 +726,14 @@ config ZEPHCORE_VBAT_MV_MULTIPLIER Formula: mv = (raw * multiplier) / 4096 +config ZEPHCORE_LED_PWM + def_bool $(dt_alias_enabled,heartbeat-pwm-led) || $(dt_alias_enabled,lora-tx-pwm-led) + select PWM + help + Set when the devicetree has a heartbeat-pwm-led or lora-tx-pwm-led + alias: that LED is driven through PWM and dimmed by leds.brightness. + Pulls in CONFIG_PWM automatically. + endmenu menu "Debug & Logging" diff --git a/zephcore/app/main_observer.cpp b/zephcore/app/main_observer.cpp index 7f133ae..337eefe 100644 --- a/zephcore/app/main_observer.cpp +++ b/zephcore/app/main_observer.cpp @@ -44,15 +44,8 @@ LOG_MODULE_REGISTER(zephcore_observer_main, CONFIG_ZEPHCORE_MAIN_LOG_LEVEL); /* ========== LED (optional) ========== */ -/* True when a board aliases its heartbeat or TX-activity LED to a PWM node. - * On ESP32, configuring led0 as plain GPIO after the PWM driver has already - * routed the pin takes the pin back from the LEDC peripheral (the GPIO - * matrix only serves one signal at a time), so on those boards led0 must be - * left to the PWM driver instead of reconfigured here. */ -#define ZEPHCORE_HAS_PWM_SHARED_LED \ - (DT_NODE_EXISTS(DT_ALIAS(heartbeat_pwm_led)) || DT_NODE_EXISTS(DT_ALIAS(lora_tx_pwm_led))) - -#if DT_NODE_HAS_PROP(DT_ALIAS(led0), gpios) && !ZEPHCORE_HAS_PWM_SHARED_LED +/* With a PWM LED, led0 is the same pin and belongs to the PWM driver. */ +#if DT_NODE_HAS_PROP(DT_ALIAS(led0), gpios) && !IS_ENABLED(CONFIG_ZEPHCORE_LED_PWM) #include #define LED0_NODE DT_ALIAS(led0) static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(LED0_NODE, gpios); @@ -328,7 +321,7 @@ int main(void) LOG_INF("=== ZephCore Observer starting ==="); /* Configure LED */ -#if DT_NODE_HAS_PROP(DT_ALIAS(led0), gpios) && !ZEPHCORE_HAS_PWM_SHARED_LED +#if DT_NODE_HAS_PROP(DT_ALIAS(led0), gpios) && !IS_ENABLED(CONFIG_ZEPHCORE_LED_PWM) if (gpio_is_ready_dt(&led0)) { gpio_pin_configure_dt(&led0, GPIO_OUTPUT_INACTIVE); } diff --git a/zephcore/boards/esp32/heltec_wifi_lora32_v43/board.conf b/zephcore/boards/esp32/heltec_wifi_lora32_v43/board.conf index c8804a8..cc65409 100644 --- a/zephcore/boards/esp32/heltec_wifi_lora32_v43/board.conf +++ b/zephcore/boards/esp32/heltec_wifi_lora32_v43/board.conf @@ -28,6 +28,3 @@ CONFIG_ZEPHCORE_DEFAULT_TX_POWER_DBM=12 # board.overlay): the default 500ms double-press confirm window (shutdown/ # DFU/offgrid) is too short for two ~1s holds, see Kconfig help text. CONFIG_ZEPHCORE_UI_CONFIRM_WINDOW_MS=3000 - -# PWM for LED brightness. Kept here, not in the defconfig: the defconfig is shared with mcuboot, which fails to link with it. -CONFIG_PWM=y diff --git a/zephcore/boards/esp32/heltec_wireless_tracker_v2/board.conf b/zephcore/boards/esp32/heltec_wireless_tracker_v2/board.conf index 08634ff..ce2e875 100644 --- a/zephcore/boards/esp32/heltec_wireless_tracker_v2/board.conf +++ b/zephcore/boards/esp32/heltec_wireless_tracker_v2/board.conf @@ -28,6 +28,3 @@ CONFIG_ZEPHCORE_DEFAULT_TX_POWER_DBM=9 # board.overlay): the default 500ms double-press confirm window (shutdown/ # DFU/offgrid) is too short for two ~1s holds, see Kconfig help text. CONFIG_ZEPHCORE_UI_CONFIRM_WINDOW_MS=3000 - -# PWM for LED brightness. Kept here, not in the defconfig: the defconfig is shared with mcuboot, which fails to link with it. -CONFIG_PWM=y diff --git a/zephcore/boards/example_board/README.md b/zephcore/boards/example_board/README.md index c5a572c..61760ca 100644 --- a/zephcore/boards/example_board/README.md +++ b/zephcore/boards/example_board/README.md @@ -142,7 +142,7 @@ Only `lora0` is required. Everything else enables its feature by being present. | `lfs_partition` + `filesystem.dtsi` | `/lfs`: identity, prefs, contacts, channels | | chosen `zephyr,settings-partition` | NVS for BLE bonds (nRF52, ESP32). nRF54L and MG24 keep bonds in a file on `/lfs` | | aliases `led0`, `led1`, `lora-tx-led` | Heartbeat, unread, TX blink | -| aliases `heartbeat-pwm-led`, `lora-tx-pwm-led` | Same heartbeat/TX blink, brightness-adjustable (`leds.brightness`). Point both at one `pwm-leds` node to share a single physical LED. On ESP32, leave `led0`/`led1` out of the board's `main()` GPIO setup when either alias is present: configuring the same pin as plain GPIO after the PWM driver has claimed it takes the pin back from the LEDC peripheral (`ZEPHCORE_HAS_PWM_SHARED_LED` in `src/server_main_common.cpp` and `app/main_observer.cpp` already skips this for you) | +| aliases `heartbeat-pwm-led`, `lora-tx-pwm-led` | Same heartbeat/TX blink, brightness-adjustable (`leds.brightness`). Point both at one `pwm-leds` node to share a single physical LED. On ESP32, leave `led0`/`led1` out of the board's `main()` GPIO setup when either alias is present: configuring the same pin as plain GPIO after the PWM driver has claimed it takes the pin back from the LEDC peripheral (`CONFIG_ZEPHCORE_LED_PWM` follows the aliases: it selects `CONFIG_PWM` and makes `src/server_main_common.cpp` and `app/main_observer.cpp` skip this for you) | | alias `sw0` | User button; wake source from nRF System OFF and ESP32 light sleep | | chosen `zephyr,display` | Display UI | | node labelled `buzzer` + alias `buzzer` | PWM buzzer | diff --git a/zephcore/boards/example_board/board.overlay b/zephcore/boards/example_board/board.overlay index 706e26d..0c88048 100644 --- a/zephcore/boards/example_board/board.overlay +++ b/zephcore/boards/example_board/board.overlay @@ -616,7 +616,7 @@ zephyr_udc0: &usbd { * ESP32 only: also declare a plain led0 (gpio-leds) for this pin if the * board wants one, but do NOT add GPIO setup for it in the board's * main() -- src/server_main_common.cpp and app/main_observer.cpp skip - * that already (ZEPHCORE_HAS_PWM_SHARED_LED) when either PWM alias is + * that already (CONFIG_ZEPHCORE_LED_PWM) when either PWM alias is * present, because on ESP32 configuring the pin as plain GPIO after * the PWM driver has claimed it takes the pin back from the LEDC * peripheral: the GPIO matrix only routes one signal to a pin at a diff --git a/zephcore/boards/nrf52840/heltec_t096/heltec_t096_nrf52840_defconfig b/zephcore/boards/nrf52840/heltec_t096/heltec_t096_nrf52840_defconfig index 22dd914..98c300e 100644 --- a/zephcore/boards/nrf52840/heltec_t096/heltec_t096_nrf52840_defconfig +++ b/zephcore/boards/nrf52840/heltec_t096/heltec_t096_nrf52840_defconfig @@ -10,9 +10,6 @@ CONFIG_HW_STACK_PROTECTION=y # Enable GPIO CONFIG_GPIO=y -# Enable PWM (shared heartbeat/TX LED brightness) -CONFIG_PWM=y - # Enable UART driver CONFIG_SERIAL=y diff --git a/zephcore/src/server_main_common.cpp b/zephcore/src/server_main_common.cpp index 81068f5..3c1e703 100644 --- a/zephcore/src/server_main_common.cpp +++ b/zephcore/src/server_main_common.cpp @@ -71,16 +71,9 @@ extern "C" void bt_ctlr_assert_handle(char *file, uint32_t line) #include "wifi_ota.h" #endif -/* True when a board aliases its heartbeat or TX-activity LED to a PWM node. - * On ESP32, configuring led0 as plain GPIO after the PWM driver has already - * routed the pin takes the pin back from the LEDC peripheral (the GPIO - * matrix only serves one signal at a time), so on those boards led0 must be - * left to the PWM driver instead of reconfigured here. */ -#define ZEPHCORE_HAS_PWM_SHARED_LED \ - (DT_NODE_EXISTS(DT_ALIAS(heartbeat_pwm_led)) || DT_NODE_EXISTS(DT_ALIAS(lora_tx_pwm_led))) - /* LED configuration */ -#if DT_NODE_HAS_PROP(DT_ALIAS(led0), gpios) && !ZEPHCORE_HAS_PWM_SHARED_LED +/* With a PWM LED, led0 is the same pin and belongs to the PWM driver. */ +#if DT_NODE_HAS_PROP(DT_ALIAS(led0), gpios) && !IS_ENABLED(CONFIG_ZEPHCORE_LED_PWM) #define LED0_NODE DT_ALIAS(led0) static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(LED0_NODE, gpios); #endif @@ -618,7 +611,7 @@ int server_main(const ServerRole &role) #endif /* Configure LEDs */ -#if DT_NODE_HAS_PROP(DT_ALIAS(led0), gpios) && !ZEPHCORE_HAS_PWM_SHARED_LED +#if DT_NODE_HAS_PROP(DT_ALIAS(led0), gpios) && !IS_ENABLED(CONFIG_ZEPHCORE_LED_PWM) if (gpio_is_ready_dt(&led0)) { gpio_pin_configure_dt(&led0, GPIO_OUTPUT_INACTIVE); }