模拟任务书
- 使用板载 USB-UART,通信参数 115200、8N1,文本命令以 CR 或 LF 结束。
- 命令不区分大小写,单行最长 159 字符;超长、未知命令和越界参数必须回复 ERR。
- 支持控制 RGB、蜂鸣器、普通 GPIO,读取 ADC、Touch、按键与运行模式。
- 支持设置 ADC 阈值、采样周期、闪烁周期、自动上报周期和 Touch 灵敏度。
- 按设置周期自动输出一行状态,不得阻塞接收命令。
- 支持 SAVE、LOAD、FACTORY;NVS 数据必须有魔数、版本、长度和校验。
- 输入异常不得导致复位;中断内禁止打印和写 NVS。
建议评分点
| 项目 | 分值 | 验收 |
|---|---|---|
| UART 初始化与行接收 | 15 | CR、LF、CRLF 均能正确结束 |
| 命令表和大小写 | 15 | HELP 显示完整;小写命令可用 |
| 外设控制和状态读取 | 25 | RGB、蜂鸣器、GPIO、ADC、Touch、按键均可验收 |
| 参数范围与错误回复 | 15 | 无参数、非数字、越界、超长均不崩溃 |
| 自动上报与非阻塞 | 15 | 自动上报期间仍能立即执行 LED 命令 |
| NVS 数据完整性 | 10 | 版本或校验错误时使用默认值 |
| 连续运行稳定性 | 5 | 连续发送 100 条错误命令不复位 |
直接使用
Arduino 工程:arduino_pio/mock_03_uart_controller。主要修改 include/contest_user_config.h 和 src/task_logic.cpp。
help
LED 20 0 80
BEEP 1500 200
GPIO WRITE 21 1
ADC READ
TOUCH READ
SET REPORT 500
SAVE
STATUS
LED 999 0 0
THIS_IS_WRONG最可能的变题
- 文本命令改为固定帧头、长度、命令字和校验和。
- 上位机要求 CSV 或 JSON 样式输出。
- UART0 改 UART1,需要外接 USB-TTL。
- 自动上报改成仅状态变化时上报。
现场修改指南:先判断要不要改
HELP、LED、BEEP、GPIO、ADC、TOUCH、MODE、SET、SAVE、LOAD、FACTORY、STATUS 都已由公共库实现。现场只是改这些命令的参数时,不要重写 command();只有出现新的命令名字或新的回复格式才改本题代码。
题目一句话对应哪里
| 题目关键词 | 函数 | 作用 |
|---|---|---|
| 上电默认模式 | beginTask() | 检查 mode,打开自动上报并记录起点 |
| 上报内容 | emitReport() | 统一生成一行 ADC、Touch、按键和模式 |
| 上报周期/灯光 | tick() | 每 10 ms 判断是否到达 report_ms |
| 新增命令 | command() | 识别命令、检查参数、回复 OK/ERR |
| HELP 新增一行 | printHelp() | 列出题型专属命令 |
| STATUS 附加字段 | status() | 打印自动上报开关 |
变量翻译
| 代码 | 含义 |
|---|---|
command | 已经转成大写的命令名,例如 AUTO |
arguments | 命令名之后的参数正文;没有参数时可能是 nullptr |
auto_report | 是否周期自动发送报告 |
last_report_ms | 上一次报告时刻,用于非阻塞计时 |
app.config().report_ms | 自动上报间隔,允许 100..60000 ms |
return true | 表示命令已经处理;false会让公共库回复未知命令 |
先复用现成命令
| 要求 | 直接使用 |
|---|---|
| 设置 RGB | LED 20 0 80 |
| 蜂鸣器发声 | BEEP 1500 200 |
| 设置模式 | MODE 2 |
| 设置上报周期 | SET REPORT 500 |
| 读 ADC/Touch | ADC READ、TOUCH READ |
| 保存配置 | SAVE |
不要重复实现:公共命令在 ContestToolkit.cpp里。题目仍使用同名命令时,直接验收即可。
四种高概率变题:可直接照着加
示例 A:新增无参数命令 PING,回复 OK PONG
插入位置:在 command()函数开头、现有 REPORT判断之前加入:
if (strcmp(command, "PING") == 0) {
if (arguments != nullptr && *arguments != '\0') {
app.emit("ERR USAGE PING");
} else {
app.emit("OK PONG");
}
return true;
}再把 printHelp()中的提示改成包含 PING,否则功能存在但 HELP 看不到。
示例 B:新增带数字参数 RATE 100..60000
1. 在文件顶部 #include <cstring>下一行加入:
#include <cstdlib>2. 在 command()返回 false之前加入:
if (strcmp(command, "RATE") == 0) {
char *end = nullptr;
const long value = arguments == nullptr
? -1 : strtol(arguments, &end, 10);
while (end != nullptr && (*end == ' ' || *end == '\t')) ++end;
const bool valid_number = arguments != nullptr &&
end != arguments && end != nullptr && *end == '\0';
if (!valid_number || value < 100 || value > 60000) {
app.emit("ERR USAGE RATE <100..60000>");
} else {
app.config().report_ms = static_cast<uint32_t>(value);
app.emit("OK RATE_MS=%ld", value);
}
return true;
}需要掉电保存时,设置后再发送 SAVE。不要在每次命令中自动写 NVS,频繁写入会降低 Flash 寿命。
示例 C:自动上报改为 CSV 格式
假设上位机要求固定字段:REPORT,毫伏,触摸,按键,模式。
替换范围:完整替换 emitReport()。
void emitReport(contest::ContestApp &app) {
app.emit("REPORT,%lu,%lu,%u,%u",
static_cast<unsigned long>(app.adcMillivolts()),
static_cast<unsigned long>(app.touchRaw()),
app.buttonPressed() ? 1U : 0U,
app.config().mode);
}字段顺序必须按任务书:上位机按逗号拆分时,多一个空格、少一个字段或顺序不同都可能扣分。
示例 D:不再周期上报,只在模式变化时上报
1. 在全局变量区加入:
uint8_t last_reported_mode = 255;2. 在 beginTask()末尾加入:
last_reported_mode = 255;3. 把 tick()末尾原来的周期判断替换为:
if (app.config().mode != last_reported_mode) {
last_reported_mode = app.config().mode;
emitReport(app);
}这种写法不会在 ADC 值变化时上报;如果题目要求“任何字段变化都上报”,还需要分别保存并比较上一次 ADC、Touch 和按键值。
改完后的最短验收
- 先发送小写命令,确认公共层仍会自动转大写。
- 对新命令分别测试:正确参数、无参数、非数字、越界、多余参数和未知命令。
- 开启自动上报时连续发送 20 条命令,确认接收不会被报告阻塞。
- 输入
HELP确认新命令出现;输入STATUS确认配置与实际一致。 - 发送一条 160 字符以上的错误行,程序应回复错误而不是复位。
不要改:公共串口收行、大小写转换和超长保护。除非现场改成二进制协议,否则不要动 ContestToolkit.cpp;二进制协议直接选第 6 套。