实战一:可观测性与交互——自定义配置、分级日志与 Shell 命令
构建复杂应用的第一步,是建立**编译期可配置参数(Kconfig)**与**运行期可观测/可交互通道(Logging + Shell)**。这样在后续开发外设驱动与多任务时,我们可以随时通过串口命令动态调参、触发动作并观察分级日志。
本节目标与架构
基于 应用构建、Kconfig 与 Devicetree 中介绍的独立工程结构,我们构建 smart_node 的第一阶段:
通过应用自定义
Kconfig配置节点名称、默认采样周期与高温报警阈值,并拆分独立的sensor_sim模拟采样源文件。启用 日志系统使用指南,实现模块级
INF/DBG/WRN日志与二进制帧LOG_HEXDUMP_INF打印。利用
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