.. _tutorial_step1: 实战一:可观测性与交互——自定义配置、分级日志与 Shell 命令 ############################################################ 构建复杂应用的第一步,是建立**编译期可配置参数(Kconfig)**与**运行期可观测/可交互通道(Logging + Shell)**。这样在后续开发外设驱动与多任务时,我们可以随时通过串口命令动态调参、触发动作并观察分级日志。 本节目标与架构 ============== 基于 :doc:`/develop/application` 中介绍的独立工程结构,我们构建 ``smart_node`` 的第一阶段: #. 通过应用自定义 ``Kconfig`` 配置节点名称、默认采样周期与高温报警阈值,并拆分独立的 ``sensor_sim`` 模拟采样源文件。 #. 启用 :doc:`/develop/subsys/logging/logging_usage`,实现模块级 ``INF/DBG/WRN`` 日志与二进制帧 ``LOG_HEXDUMP_INF`` 打印。 #. 利用 ``qemu_cortex_a53`` 默认绑定的 PL011 ``&uart0`` 启用 :doc:`/develop/subsys/shell/shell_usage`,注册 ``node info`` 与 ``node threshold `` 命令。 .. important:: **避免内核调试日志刷屏**:在 ``prj.conf`` 中开启 ``CONFIG_LOG=y`` 时,全局默认等级 ``CONFIG_LOG_DEFAULT_LEVEL`` 应保持默认值 ``3``(``INF``)。若想输出应用自身的 ``DBG`` 级日志,只需在应用源文件中使用 ``LOG_MODULE_REGISTER(smart_node, LOG_LEVEL_DBG)`` 单独声明即可;切勿在 ``prj.conf`` 中将全局默认等级设为 ``4``,否则内核 ``os`` 模块的互斥锁调试日志会在终端持续刷屏。 Step 1:编写自定义 ``Kconfig`` 与 ``prj.conf`` ============================================== 工程目录结构如下: .. code-block:: text smart_node/ ├── CMakeLists.txt ├── Kconfig ├── prj.conf ├── include/ │ └── sensor_sim.h └── src/ ├── main.c └── sensor_sim.c 创建 ``smart_node/Kconfig``(必须包含 ``source "Kconfig.zephyr"`` 以引入主配置树): .. code-block:: kconfig mainmenu "Smart Node Application Configuration" source "Kconfig.zephyr" menu "Smart Node Settings" config SMART_NODE_NAME string "Node identifier string" default "A53-Edge-Node-01" config SMART_NODE_SAMPLE_INTERVAL_MS int "Sensor sampling interval in milliseconds" default 1000 range 100 60000 config SMART_NODE_TEMP_THRESHOLD int "Default high temperature alarm threshold (Celsius)" default 30 endmenu 创建 ``smart_node/prj.conf``: .. code-block:: ini CONFIG_LOG=y CONFIG_SHELL=y CONFIG_SMART_NODE_NAME="QEMU-A53-LabNode" CONFIG_SMART_NODE_SAMPLE_INTERVAL_MS=500 CONFIG_SMART_NODE_TEMP_THRESHOLD=30 Step 2:编写模拟传感器模块与主程序 ================================== 创建 ``smart_node/include/sensor_sim.h`` 与 ``smart_node/src/sensor_sim.c``: .. code-block:: c /* include/sensor_sim.h */ #ifndef SMART_NODE_SENSOR_SIM_H_ #define SMART_NODE_SENSOR_SIM_H_ #include int32_t sensor_sim_read_temp_c(uint32_t step); #endif .. code-block:: c /* src/sensor_sim.c */ #include "sensor_sim.h" int32_t sensor_sim_read_temp_c(uint32_t step) { static const int32_t pattern[] = { 26, 29, 32, 28 }; return pattern[step % 4]; } 创建 ``smart_node/src/main.c``,集成分级日志与 ``node`` 命令集: .. code-block:: c #include #include #include #include #include #include "sensor_sim.h" LOG_MODULE_REGISTER(smart_node, LOG_LEVEL_DBG); static int32_t g_temp_threshold = CONFIG_SMART_NODE_TEMP_THRESHOLD; static int cmd_node_info(const struct shell *sh, size_t argc, char **argv) { ARG_UNUSED(argc); ARG_UNUSED(argv); shell_print(sh, "Smart Node [%s] Status:", CONFIG_SMART_NODE_NAME); shell_print(sh, " Board : %s", CONFIG_BOARD); shell_print(sh, " Uptime : %lld ms", k_uptime_get()); shell_print(sh, " Temp Threshold : %d C", g_temp_threshold); return 0; } static int cmd_node_threshold(const struct shell *sh, size_t argc, char **argv) { char *end; long val; ARG_UNUSED(argc); errno = 0; val = strtol(argv[1], &end, 10); if (errno != 0 || end == argv[1] || *end != '' || val < -40 || val > 125) { shell_error(sh, "Invalid threshold '%s' (expected -40..125)", argv[1]); return -EINVAL; } g_temp_threshold = (int32_t)val; LOG_INF("Alarm threshold updated to %d C via Shell", g_temp_threshold); shell_print(sh, "Threshold set to %d C", g_temp_threshold); return 0; } SHELL_STATIC_SUBCMD_SET_CREATE(sub_node, SHELL_CMD_ARG(info, NULL, "Show node status", cmd_node_info, 1, 0), SHELL_CMD_ARG(threshold, NULL, "Set alarm threshold: node threshold ", cmd_node_threshold, 2, 0), SHELL_SUBCMD_SET_END ); SHELL_CMD_REGISTER(node, &sub_node, "Smart Node control commands", NULL); int main(void) { uint8_t frame_hdr[4] = { 0xAA, 0x55, 0x01, 0x04 }; LOG_INF("Smart Node [%s] initialized", CONFIG_SMART_NODE_NAME); LOG_DBG("Default temperature threshold = %d C", g_temp_threshold); LOG_HEXDUMP_INF(frame_hdr, sizeof(frame_hdr), "Protocol Header:"); for (uint32_t step = 0; step < 3; step++) { int32_t temp = sensor_sim_read_temp_c(step); if (temp > g_temp_threshold) { LOG_WRN("[Step %u] Temp=%d C exceeds threshold %d C!", step, temp, g_temp_threshold); } else { LOG_INF("[Step %u] Temp=%d C (OK)", step, temp); } k_msleep(CONFIG_SMART_NODE_SAMPLE_INTERVAL_MS); } return 0; } 创建 ``smart_node/CMakeLists.txt``: .. code-block:: cmake cmake_minimum_required(VERSION 3.20.5) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(smart_node) target_include_directories(app PRIVATE include) target_sources(app PRIVATE src/main.c src/sensor_sim.c ) Step 3:在 ``qemu_cortex_a53`` 上构建与交互验证 =============================================== .. code-block:: bash west build -p always -b qemu_cortex_a53 -d build_step1 smart_node west build -d build_step1 -t run 在 ``uart:~$`` 提示符下依次输入 ``node info``、``node threshold 28``、``node info``,实测输出如下: .. code-block:: console *** Booting Zephyr OS build 321e39a45766 *** [00:00:00.010,000] smart_node: Smart Node [QEMU-A53-LabNode] initialized [00:00:00.010,000] smart_node: main: Default temperature threshold = 30 C [00:00:00.010,000] smart_node: Protocol Header: aa 55 01 04 |.U.. [00:00:00.010,000] smart_node: [Step 0] Temp=26 C (OK) [00:00:00.520,000] smart_node: [Step 1] Temp=29 C (OK) [00:00:01.030,000] smart_node: [Step 2] Temp=32 C exceeds threshold 30 C! uart:~$ node info Smart Node [QEMU-A53-LabNode] Status: Board : qemu_cortex_a53 Uptime : 1540 ms Temp Threshold : 30 C uart:~$ node threshold 28 Threshold set to 28 C [00:00:01.990,000] smart_node: Alarm threshold updated to 28 C via Shell uart:~$ node info Smart Node [QEMU-A53-LabNode] Status: Board : qemu_cortex_a53 Uptime : 2490 ms Temp Threshold : 28 C 相关参考 ======== * :doc:`/develop/application` * :doc:`/develop/subsys/logging/logging_usage` * :doc:`/develop/subsys/shell/shell_usage`