From b4f2334b99c9b850cb8968421104e7ea70be5306 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Mon, 28 Sep 2026 16:57:00 +0200 Subject: [PATCH] [nrf52] Use full newlib when the logger is enabled, disable unused serial and printk (#19766) Co-authored-by: J. Nick Koston --- esphome/components/ble_nus/__init__.py | 1 + esphome/components/debug/__init__.py | 1 + esphome/components/logger/__init__.py | 1 + esphome/components/logger/logger_zephyr.cpp | 2 ++ esphome/components/nrf52/__init__.py | 25 +++++++++++++------ esphome/components/zephyr/__init__.py | 1 + .../components/zephyr_mcumgr/ota/__init__.py | 1 + .../components/logger/test.nrf52-mcumgr.yaml | 1 + tests/unit_tests/test_nrf52_config.py | 12 +++++++++ 9 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 tests/unit_tests/test_nrf52_config.py diff --git a/esphome/components/ble_nus/__init__.py b/esphome/components/ble_nus/__init__.py index 55a2f9bd18..40983c4dbb 100644 --- a/esphome/components/ble_nus/__init__.py +++ b/esphome/components/ble_nus/__init__.py @@ -62,6 +62,7 @@ CONFIG_SCHEMA = cv.All( async def to_code(config: ConfigType) -> None: var = cg.new_Pvariable(config[CONF_ID]) zephyr_add_prj_conf("BT_NUS", True) + zephyr_add_prj_conf("RING_BUFFER", True) request_ble_l2cap_mtu(247) # DLE sweet spot: one NUS frame per LL PDU expose_log = config[CONF_TYPE] == CONF_LOGS cg.add(var.set_expose_log(expose_log)) diff --git a/esphome/components/debug/__init__.py b/esphome/components/debug/__init__.py index e4692dfa9a..898d7d6c0e 100644 --- a/esphome/components/debug/__init__.py +++ b/esphome/components/debug/__init__.py @@ -60,6 +60,7 @@ async def to_code(config: ConfigType) -> None: zephyr_add_prj_conf("LOG_BLOCK_IN_THREAD", True) zephyr_add_prj_conf("LOG_BUFFER_SIZE", 4096) zephyr_add_prj_conf("SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL", True) + zephyr_add_prj_conf("PRINTK", True) var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) cg.add_define("USE_DEBUG") diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index eb1f34565a..d6b9bca38b 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -504,6 +504,7 @@ async def _late_logger_init(config: ConfigType) -> None: zephyr_add_prj_conf("RESET_ON_FATAL_ERROR", False) zephyr_add_prj_conf("THREAD_LOCAL_STORAGE", True) if has_serial_logging: + zephyr_add_prj_conf("SERIAL", True) if config[CONF_HARDWARE_UART] == UART0: zephyr_add_overlay("""&uart0 { status = "okay";};""") if config[CONF_HARDWARE_UART] == UART1: diff --git a/esphome/components/logger/logger_zephyr.cpp b/esphome/components/logger/logger_zephyr.cpp index b7884b702b..69ef6167da 100644 --- a/esphome/components/logger/logger_zephyr.cpp +++ b/esphome/components/logger/logger_zephyr.cpp @@ -54,6 +54,7 @@ void Logger::cdc_loop_() { #endif void Logger::pre_setup() { +#ifdef CONFIG_SERIAL if (this->baud_rate_ > 0) { static const struct device *uart_dev = nullptr; switch (this->uart_) { @@ -87,6 +88,7 @@ void Logger::pre_setup() { #endif } } +#endif global_logger = this; ESP_LOGI(TAG, "Log initialized"); #ifdef USE_LOGGER_EARLY_MESSAGE diff --git a/esphome/components/nrf52/__init__.py b/esphome/components/nrf52/__init__.py index b32d4f075e..0d1cfd6c1e 100644 --- a/esphome/components/nrf52/__init__.py +++ b/esphome/components/nrf52/__init__.py @@ -175,6 +175,8 @@ def _detect_bootloader(config: ConfigType) -> ConfigType: """Detect the bootloader for the given board.""" config = config.copy() bootloaders: list[str] = [] + if CONF_BOARD not in config: + raise cv.Invalid("'board' is a required option for [nrf52].") board = config[CONF_BOARD] if board in BOARDS_ZEPHYR and KEY_BOOTLOADER in BOARDS_ZEPHYR[board]: @@ -249,7 +251,7 @@ CONFIG_SCHEMA = cv.All( ): cv.Schema( { cv.Optional(CONF_VERSION): cv.string_strict, - cv.Optional(CONF_LIBC_NANO, default=True): cv.boolean, + cv.Optional(CONF_LIBC_NANO): cv.boolean, cv.Optional( CONF_ADVANCED, default={}, visibility=cv.Visibility.YAML_ONLY ): cv.Schema( @@ -295,7 +297,7 @@ def _final_validate(config): conf = config[CONF_FRAMEWORK] advanced = conf[CONF_ADVANCED] - if conf[CONF_LIBC_NANO] and "logger" in CORE.loaded_integrations: + if conf.get(CONF_LIBC_NANO, False) and "logger" in CORE.loaded_integrations: _LOGGER.warning( "Logger is enabled with newlib-nano (libc_nano: true). Some format specifiers " "such as %%zu are not supported and will print incorrectly. " @@ -401,7 +403,10 @@ async def to_code(config: ConfigType) -> None: cg.add_define("USE_OTA_ROLLBACK") zephyr_add_prj_conf("NEWLIB_LIBC", True) zephyr_add_prj_conf("NEWLIB_LIBC_FLOAT_PRINTF", True) - zephyr_add_prj_conf("NEWLIB_LIBC_NANO", conf[CONF_LIBC_NANO]) + zephyr_add_prj_conf( + "NEWLIB_LIBC_NANO", + conf.get(CONF_LIBC_NANO, "logger" not in CORE.loaded_integrations), + ) # c++ support if framework_ver < cv.Version(2, 9, 2): zephyr_add_prj_conf("CPLUSPLUS", True) @@ -412,9 +417,6 @@ async def to_code(config: ConfigType) -> None: # watchdog zephyr_add_prj_conf("WATCHDOG", True) zephyr_add_prj_conf("WDT_DISABLE_AT_BOOT", False) - # disable console - zephyr_add_prj_conf("UART_CONSOLE", False) - zephyr_add_prj_conf("CONSOLE", False, False) # use NFC pins as GPIO if framework_ver < cv.Version(2, 9, 2): zephyr_add_prj_conf("NFCT_PINS_AS_GPIOS", True) @@ -428,9 +430,17 @@ async def to_code(config: ConfigType) -> None: ) zephyr_add_prj_conf("REBOOT", True) - # some boards enable USB by default. + # some boards enable USB and UART by default. # disable it to prevent extra current consumption. zephyr_add_prj_conf("USB_DEVICE_STACK", False, False) + zephyr_add_prj_conf("SERIAL", False, False) + + # disable stuff to make image smaller by default + zephyr_add_prj_conf("NCS_BOOT_BANNER", False, False) + zephyr_add_prj_conf("BOOT_BANNER", False, False) + zephyr_add_prj_conf("PRINTK", False, False) + zephyr_add_prj_conf("CONSOLE", False, False) + zephyr_add_prj_conf("UART_CONSOLE", False) @coroutine_with_priority(CoroPriority.DIAGNOSTICS) @@ -445,6 +455,7 @@ async def _dfu_to_code(dfu_config): zephyr_add_prj_conf("USB_DEVICE_STACK", True) zephyr_add_prj_conf("USB_CDC_ACM", True) zephyr_add_prj_conf("CDC_ACM_DTE_RATE_CALLBACK_SUPPORT", True) + zephyr_add_prj_conf("SERIAL", True) await cg.register_component(var, dfu_config) diff --git a/esphome/components/zephyr/__init__.py b/esphome/components/zephyr/__init__.py index 2bcdf0380c..49654736cc 100644 --- a/esphome/components/zephyr/__init__.py +++ b/esphome/components/zephyr/__init__.py @@ -210,6 +210,7 @@ def zephyr_add_cdc_acm(config: ConfigType, id: int) -> None: zephyr_add_prj_conf("CONFIG_USB_DEVICE_STACK_NEXT", False) zephyr_add_prj_conf("USB_DEVICE_STACK", True) zephyr_add_prj_conf("USB_CDC_ACM", True) + zephyr_add_prj_conf("SERIAL", True) # prevent device to go to susspend, without this communication stop working in python # there should be a way to solve it zephyr_add_prj_conf("USB_DEVICE_REMOTE_WAKEUP", False) diff --git a/esphome/components/zephyr_mcumgr/ota/__init__.py b/esphome/components/zephyr_mcumgr/ota/__init__.py index 9fc02d8708..9aa78db8b1 100644 --- a/esphome/components/zephyr_mcumgr/ota/__init__.py +++ b/esphome/components/zephyr_mcumgr/ota/__init__.py @@ -147,6 +147,7 @@ async def to_code(config: ConfigType) -> None: zephyr_add_prj_conf("NCS_SAMPLE_MCUMGR_BT_OTA_DFU_SPEEDUP", True) request_ble_l2cap_mtu(498) # matches NCS_SAMPLE_MCUMGR_BT_OTA_DFU_SPEEDUP if CONF_HARDWARE_UART in transport: + zephyr_add_prj_conf("SERIAL", True) uart = UARTS[transport[CONF_HARDWARE_UART]] uart_name = uart[0] cdc_id = uart[1] diff --git a/tests/components/logger/test.nrf52-mcumgr.yaml b/tests/components/logger/test.nrf52-mcumgr.yaml index 70b485daac..e37a18a519 100644 --- a/tests/components/logger/test.nrf52-mcumgr.yaml +++ b/tests/components/logger/test.nrf52-mcumgr.yaml @@ -5,3 +5,4 @@ esphome: logger: level: DEBUG + hardware_uart: UART0 diff --git a/tests/unit_tests/test_nrf52_config.py b/tests/unit_tests/test_nrf52_config.py new file mode 100644 index 0000000000..4d9d281ebf --- /dev/null +++ b/tests/unit_tests/test_nrf52_config.py @@ -0,0 +1,12 @@ +"""Tests for the nRF52 configuration validation.""" + +import pytest + +from esphome.components.nrf52 import _detect_bootloader +import esphome.config_validation as cv + + +def test_detect_bootloader_reports_a_missing_board() -> None: + """The bootloader check runs before the schema, so it reports the missing key.""" + with pytest.raises(cv.Invalid, match="'board' is a required option"): + _detect_bootloader({})