加湿器程序

12 2025-12-11 22:47

把“低电平触发”改成“高电平触发”,并把工作时间常量改为 6 s 即可。
下面给出最终精简版(已合并前面所有修正,计时溢出安全、误差 < 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"));
}
 
改动点
  1. WORK_MS = 6000UL → 工作 6 s。
  2. 触摸判断 == HIGH → 高电平触发。
  3. 其余逻辑(剩余毫秒倒计时、溢出安全、单次 millis())保持不变,确保 20 min 总时长与 6 s/3 s 周期均准确。

 

20分钟之后我无论如何触摸,程序都不会再喷雾了?

是的,默认逻辑就是 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空转过热问题?

不会过热,原因有三点:
  1. 主循环里没有阻塞式等待
    loop() 唯一可能“卡死”的地方是
    cpp
     
  1. 复制
    if (!running) return;
     
    return 只是提前结束本次 loop(),Arduino 内核会立刻再次调用 loop(),所以实际行为是:
    • 20 min 结束后每 ~1 ms 就空跑一次 loop()
    • 空跑路径里只做一次 digitalRead 和一次 if 判断,指令数 < 20 条,执行时间不到 2 µs。
      对 16 MHz 的 AVR 来说,CPU 占用率 < 0.1 %,功耗与待机几乎无区别。
  2. 没有持续高电平驱动外设
    继电器/LED 在 running=false 时已被 digitalWrite(RELAY_PIN, LOW) 关闭,IO 口不再输出电流,不会额外发热。
  3. 芯片本身功耗极低
    ATmega328P 在 5 V、16 MHz 下空跑电流约 10 mA,芯片结温升仅 1-2 °C,远达不到“过热”程度。
结论
20 分钟后的“空转”只是正常的 idle 循环,不会导致 CPU 过热,也无需额外加 sleepwhile(1){} 死循环。

 

 

 

 

 

 

 

 

 

 

 

 

全部评论

·