模拟任务书
实现秒表和反应测试两种功能。秒表模式下短按开始,再次短按停止,长按清零但保留最佳成绩。Touch 启动反应测试:系统随机等待 1.5–4 秒后亮绿灯,参赛者尽快按键;提前按键判定失败。
- 计时精度达到毫秒级,计时期间串口和 Touch 保持响应。
- 最好成绩写入 NVS,断电后仍能查询。
- 不同状态用 RGB 和蜂鸣器明确提示。
建议评分点
| 评分项 | 分值 |
|---|---|
| 中断与可靠消抖 | 15 |
| 秒表开始、停止、复位 | 20 |
| Touch 启动随机反应测试 | 15 |
| 提前按键与有效反应判定 | 15 |
| RGB/蜂鸣器状态提示 | 10 |
| 最佳成绩 NVS 保存 | 15 |
| 串口控制和错误处理 | 10 |
总分:100
直接使用
工程:arduino_pio/mock_05_button_stopwatch。按键接 GPIO15 与 GND,Touch 导电片接 GPIO4。
START
STOP
RESET
REACTION
BEST
CLEARBEST
STATUS主要修改:src/task_logic.cpp中的 shortPress()、startReaction() 和状态枚举。
高概率变题
- 秒表改为按键计数,达到次数后报警。
- 反应测试改为抢答器,Touch 与按键代表两名选手。
- 最佳成绩改为累计次数或最长持续时间。
- 要求 10 秒无操作自动复位。
现场修改指南:先画状态图
当前代码已经实现秒表和单人反应测试。先把现场流程写成“什么状态 + 什么输入 → 下一个状态”,再改 Mode、shortPress()和 tick(),不要从灯光代码开始猜。
题目一句话对应哪里
| 题目关键词 | 函数/变量 | 作用 |
|---|---|---|
| 有哪些状态 | enum Mode | READY、RUNNING、STOPPED、REACTION_WAIT、REACTION_GO |
| 完成一次计时 | finishTiming() | 保存本次结果、更新最佳成绩并鸣叫 |
| 按键规则 | shortPress() | 根据当前状态开始、停止、判提前或记录反应时间 |
| 长按清零 | longPress() | 清本次结果但保留最佳成绩 |
| 随机等待 | startReaction() | 生成 1500..4000 ms 截止时刻 |
| 到时亮灯 | tick() | WAIT 到期后进入 GO,并控制各状态输出 |
| 串口 START/STOP | command() | 复用同一套计时状态 |
变量翻译
| 代码 | 含义 |
|---|---|
started_ms | 秒表或反应计时真正开始的时刻 |
reaction_ready_ms | 随机等待结束、允许按键的时刻 |
last_elapsed_ms | 最近一次完成成绩 |
user1 | NVS 中的历史最佳毫秒数 |
user2 | NVS 中的上次结果 |
now_ms - started_ms | 非阻塞计算已经经过多少毫秒 |
最安全:只改数字
| 现场要求 | 找到并修改 |
|---|---|
| 随机等待改 2..5 秒 | 1500 + (esp_random() % 2501)改为 2000 + (esp_random() % 3001) |
| 完成提示改 1800 Hz/300 ms | startBeep(2200, 160)改为 startBeep(1800, 300) |
| 提前按键提示改 800 Hz/200 ms | startBeep(500, 400)改为 startBeep(800, 200) |
| 运行灯翻转改 500 ms | now_ms / 250改为 now_ms / 500 |
随机公式:最小值 + random % (最大值 - 最小值 + 1)。单位都是毫秒。
四种高概率变题:可直接照着改
示例 A:只保留普通秒表,短按开始/停止
替换范围:完整替换 shortPress()。其他反应测试函数即使保留也不会影响普通秒表。
void shortPress(contest::ContestApp &app) {
const uint32_t now_ms = millis();
if (app.config().mode == RUNNING) {
finishTiming(app, now_ms - started_ms, "STOPWATCH");
} else {
started_ms = now_ms;
app.setMode(RUNNING);
app.emit("STATE STOPWATCH=RUNNING");
}
}如果题目要求 Touch 也开始/停止,把 touchChanged()中的 startReaction(app)改成 shortPress(app)。
示例 B:秒表运行 10 秒后自动停止
插入位置:在 tick()开头、switch之前加入:
if (app.config().mode == RUNNING &&
now_ms - started_ms >= 10000) {
finishTiming(app, 10000, "AUTO_STOP");
}这里不使用 delay(10000),所以 10 秒期间串口、Touch 和按键仍然响应。
示例 C:改成按键计数,达到 10 次报警
1. 在全局变量区加入:
uint32_t press_count = 0;2. 完整替换 shortPress():
void shortPress(contest::ContestApp &app) {
++press_count;
app.emit("COUNT VALUE=%lu",
static_cast<unsigned long>(press_count));
if (press_count >= 10) {
app.setMode(STOPPED);
app.setRgb(90, 0, 0);
app.startBeep(2200, 300);
}
}3. 完整替换 longPress()作为计数清零:
void longPress(contest::ContestApp &app) {
press_count = 0;
app.setMode(READY);
app.emit("COUNT RESET");
}同步修改:若 tick()仍按 STOPPED 显示黄色,它会覆盖上面设置的红色。应同时把 tick()的 STOPPED 颜色改红,或者只让 tick()负责颜色。
示例 D:反应测试等待改为固定 3 秒
替换范围:完整替换 startReaction()。
void startReaction(contest::ContestApp &app) {
reaction_ready_ms = millis() + 3000;
app.setMode(REACTION_WAIT);
app.emit("STATE REACTION=WAIT");
}题目要求随机时间时不要写固定值;题目要求精确 3 秒时固定值更容易验收。
改完后的最短验收
- 输入
START,等待约 1 秒后输入STOP,结果应接近 1000 ms。 - 按键短按开始/停止,串口结果与操作一致;长按只清本次结果。
- 输入
REACTION后提前按键,必须判失败;绿灯后按键才记录成绩。 - 输入
BEST,重启后再次查询,最佳成绩应保留。 - 计时期间连续输入
STATUS,计时不能暂停。
不要改:按键中断和消抖在公共库中。增加或删除状态时必须同步 Mode枚举、tick()的 switch以及 createTaskHooks()最大模式。