实战一:可观测性与交互——自定义配置、分级日志与 Shell 命令

构建复杂应用的第一步,是建立**编译期可配置参数(Kconfig)**与**运行期可观测/可交互通道(Logging + Shell)**。这样在后续开发外设驱动与多任务时,我们可以随时通过串口命令动态调参、触发动作并观察分级日志。

本节目标与架构

基于 应用构建、Kconfig 与 Devicetree 中介绍的独立工程结构,我们构建 smart_node 的第一阶段:

  1. 通过应用自定义 Kconfig 配置节点名称、默认采样周期与高温报警阈值,并拆分独立的 sensor_sim 模拟采样源文件。

  2. 启用 日志系统使用指南,实现模块级 INF/DBG/WRN 日志与二进制帧 LOG_HEXDUMP_INF 打印。

  3. 利用 qemu_cortex_a53 默认绑定的 PL011 &uart0 启用 Shell 系统使用指南,注册 node info 与 node threshold <C> 命令。

重要

避免内核调试日志刷屏:在 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

工程目录结构如下:

smart_node/
├── CMakeLists.txt
├── Kconfig
├── prj.conf
├── include/
│   └── sensor_sim.h
└── src/
    ├── main.c
    └── sensor_sim.c

创建 smart_node/Kconfig``(必须包含 ``source "Kconfig.zephyr" 以引入主配置树):

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:

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:

/* include/sensor_sim.h */
#ifndef SMART_NODE_SENSOR_SIM_H_
#define SMART_NODE_SENSOR_SIM_H_

#include <stdint.h>
int32_t sensor_sim_read_temp_c(uint32_t step);

#endif
/* 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 命令集:

#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/shell/shell.h>
#include <stdlib.h>
#include <errno.h>
#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 <C>",
                  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:

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 上构建与交互验证

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,实测输出如下:

*** Booting Zephyr OS build 321e39a45766 ***
[00:00:00.010,000] <inf> smart_node: Smart Node [QEMU-A53-LabNode] initialized
[00:00:00.010,000] <dbg> smart_node: main: Default temperature threshold = 30 C
[00:00:00.010,000] <inf> smart_node: Protocol Header:
                                     aa 55 01 04                                      |.U..
[00:00:00.010,000] <inf> smart_node: [Step 0] Temp=26 C (OK)
[00:00:00.520,000] <inf> smart_node: [Step 1] Temp=29 C (OK)
[00:00:01.030,000] <wrn> 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] <inf> 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

相关参考