加湿器程序
12 2025-12-11 22:47
把“低电平触发”改成“高电平触发”,并把工作时间常量改为 6 s 即可。
下面给出最终精简版(已合并前面所有修正,计时溢出安全、误差 < 1 ms)。
下面给出最终精简版(已合并前面所有修正,计时溢出安全、误差 < 1 ms)。
cpp
复制
// ---------- 硬件引脚 ----------
const uint8_t RELAY_PIN = 7; // LED/继电器
const uint8_t TOUCH_PIN = 2; // 触摸传感器(高电平触发)
// ---------- 时间常量 ----------
const uint32_t WORK_MS = 6000UL; // 工作 6 s
const uint32_t REST_MS = 3000UL; // 休息 3 s
const uint32_t TOTAL_MS = 20UL * 60 * 1000; // 20 min
// ---------- 状态机 ----------
enum State { ST_WORK, ST_REST };
State currentState;
uint32_t stateRemainMs; // 当前状态剩余 ms
uint32_t totalRemainMs; // 整机剩余 ms
bool running;
inline uint32_t timeNow() { return millis(); }
void setup() {
pinMode(RELAY_PIN, OUTPUT);
pinMode(TOUCH_PIN, INPUT_PULLUP); // 触摸高电平触发
Serial.begin(115200);
while (!Serial) {}
Serial.println(F("\n[Humidifier] === System Start ==="));
restartMachine(); // 进入初始状态
}
void loop() {
if (!running) return;
uint32_t now = timeNow();
static uint32_t lastMs = now;
uint32_t elapsed = now - lastMs; // 本拍走过的 ms
lastMs = now;
// 1. 触摸检测——高电平触发
if (digitalRead(TOUCH_PIN) == HIGH) {
Serial.println(F("[Humidifier] Touch -> restart 20 min"));
delay(200); // 简单防抖
restartMachine();
return;
}
// 2. 倒计时(剩余毫秒,溢出安全)
if (elapsed >= totalRemainMs) elapsed = totalRemainMs;
totalRemainMs -= elapsed;
if (elapsed >= stateRemainMs) elapsed = stateRemainMs;
stateRemainMs -= elapsed;
// 3. 状态切换
if (stateRemainMs == 0) {
if (currentState == ST_WORK) {
currentState = ST_REST;
stateRemainMs = REST_MS;
digitalWrite(RELAY_PIN, LOW);
Serial.println(F("[Humidifier] REST 3 s"));
} else {
currentState = ST_WORK;
stateRemainMs = WORK_MS;
digitalWrite(RELAY_PIN, HIGH);
Serial.println(F("[Humidifier] WORK 6 s"));
}
}
// 4. 整机结束
if (totalRemainMs == 0) {
digitalWrite(RELAY_PIN, LOW);
running = false;
Serial.println(F("[Humidifier] 20 min reached. STOP."));
}
}
// 把机器恢复到“刚上电”状态
void restartMachine() {
totalRemainMs = TOTAL_MS;
currentState = ST_WORK;
stateRemainMs = WORK_MS;
digitalWrite(RELAY_PIN, HIGH);
running = true;
Serial.print(F("[Humidifier] Restart @ "));
Serial.print(timeNow() / 1000);
Serial.println(F(" s"));
}
改动点
-
WORK_MS = 6000UL→ 工作 6 s。 -
触摸判断
== HIGH→ 高电平触发。 -
其余逻辑(剩余毫秒倒计时、溢出安全、单次
millis())保持不变,确保 20 min 总时长与 6 s/3 s 周期均准确。
20分钟之后我无论如何触摸,程序都不会再喷雾了?
是的,默认逻辑就是 20 min 一到永久停机,不管你怎么触摸都不会再喷雾了。
如果你希望“20 min 结束后仍可再次触摸重启”,只要把
如果你希望“20 min 结束后仍可再次触摸重启”,只要把
running 标志重新打开即可。下面给出最小改动的两种方案,你挑一个就行。方案 A:触摸立即强制重启(最简单) 把触摸检测里的代码提前,先处理触摸、再判断 running,触摸成功就
restartMachine(),这样即使 running==false 也能被重新唤醒。cpp
复制
void loop() {
uint32_t now = timeNow();
static uint32_t lastMs = now;
uint32_t elapsed = now - lastMs;
lastMs = now;
/* 1. 触摸检测——永远有效 */
if (digitalRead(TOUCH_PIN) == HIGH) {
delay(200); // 防抖
restartMachine(); // 里面已经把 running=true 了
return; // 本拍直接重新开始
}
/* 2. 如果还在运行,才继续倒计时 */
if (!running) return; // 20 min 走完了也没关系,上面触摸已能唤醒
...
}
方案 B:触摸只重置 20 min,不立即切换状态 适合“不希望一碰就打断当前 6 s/3 s 周期”的场景,把
totalRemainMs 重新灌满即可:在
loop() 的触摸处把cpp
复制
restartMachine();
换成
cpp
复制
totalRemainMs = TOTAL_MS;
running = true;
Serial.println(F("[Humidifier] 20 min reset by touch"));
即可。整机倒计时从头开始,但当前 WORK/REST 周期继续走完。
总结
-
默认 20 min 后
running=false,任何触摸都被return挡掉。 -
只要把触摸判断提前,或手动把
running重新置true,就能实现“永久可重启”。
该程序是否存在20分钟之后cpu空转过热问题?
不会过热,原因有三点:
-
主循环里没有阻塞式等待
loop()唯一可能“卡死”的地方是cpp
-
复制
if (!running) return;但return只是提前结束本次loop(),Arduino 内核会立刻再次调用loop(),所以实际行为是:-
20 min 结束后每 ~1 ms 就空跑一次
loop(); -
空跑路径里只做一次
digitalRead和一次if判断,指令数 < 20 条,执行时间不到 2 µs。
对 16 MHz 的 AVR 来说,CPU 占用率 < 0.1 %,功耗与待机几乎无区别。
-
-
没有持续高电平驱动外设
继电器/LED 在running=false时已被digitalWrite(RELAY_PIN, LOW)关闭,IO 口不再输出电流,不会额外发热。 -
芯片本身功耗极低
ATmega328P 在 5 V、16 MHz 下空跑电流约 10 mA,芯片结温升仅 1-2 °C,远达不到“过热”程度。
结论
20 分钟后的“空转”只是正常的 idle 循环,不会导致 CPU 过热,也无需额外加
20 分钟后的“空转”只是正常的 idle 循环,不会导致 CPU 过热,也无需额外加
sleep 或 while(1){} 死循环。
全部评论