一、硬件介绍(Hardware)
1.1 主控板
Arduino Nano RP2040 ConnectMCU:Raspberry Pi RP2040(双核 ARM Cortex-M0+,133 MHz)
封装:Arduino Nano 经典尺寸(18 × 45 mm),兼容 Grove 扩展板
工作电压:3.3 V(逻辑电平 3.3 V,Grove 模块同样为 3.3 V 域,无需电平转换)
数字 IO:20 个(含 A0..A5 复用为 D14..D19),全部支持 INPUT_PULLUP
模拟输入:8 个 12-bit ADC(本次项目把 A2 作为数字口 D16 使用)
开发框架:ArduinoCore-mbed(PlatformIO platform = raspberrypi)
1.2 Grove 模块清单
板载 LED + 保护电阻 + 电位器,可旋转调节亮度;
数字输入:HIGH 点亮,LOW 熄灭;
本项目不使用电位器(直接给满电平),符合"指示电源或信号存在"的典型用法。
主体是一个压电蜂鸣器;
数字输出 HIGH 时发声,LOW 时静音;
也可以接 PWM 输出产生不同音调,本项目直接用 2 Hz 方波驱动,足以达到"报警"目的。
迷你 SPDT 滑动开关,质量可靠;
本项目用其单刀单掷功能:开关另一端接 GND,公共端接 D2,配合 INPUT_PULLUP 使用;
开关断开 → D2 被内部上拉 → 读 HIGH → OFF;
开关闭合 → D2 被拉到 GND → 读 LOW → ON。
高灵敏、无方向性振动传感器(弹簧 + 套管式结构);
模块在静止时电路导通,输出 HIGH;
发生振动时弹簧短暂脱离接触,输出 LOW;
板载电位器可调节灵敏度(顺时针变灵敏);
本项目把信号线接到丝印 A2(即 Arduino D16),用数字读法即可:LOW ⟹ 振动。
1.3 接线总览
Arduino Nano RP2040 Connect ┌────────────────────────────────┐ │ D2 ──┼──► Switch(P) ──► GND │ D4 ──┼──► Red LED (VCC 来自板载 3V3) │ D6 ──┼──► Buzzer (VCC 来自板载 3V3) │ A2 ──┼──► Vibration SW-420 └────────────────────────────────┘

二、软件设计(Software Design)
程序结构总览
整个固件只有一对 setup() / loop(),扁平结构:main.cpp ├── 文件头注释(接线、行为、SW-420 极性说明) ├── 引脚 / 参数常量 ├── 状态机定义 ├── setup() 引脚初始化 + 串口启动 + 启动信息打印 ├── 内联工具函数 switchIsOn() / vibrationDetected() / alarmOutputs() └── loop() ├── 1) 开关 OFF 分支:立即清零 + 退出 ├── 2) 开关 ON + 状态机(IDLE / ALARM) └── 3) 日志节流打印 状态机设计
enum AlarmState : uint8_t {
STATE_IDLE, // 开关 OFF 或 未触发
STATE_ALARM, // 报警中
};只有两态,覆盖所有路径。转移条件bool heldLongEnough = (now - alarmStartMs) >= ALARM_HOLD_MS;
bool quietLongEnough = (now - alarmLastTriggerMs) >= ALARM_HOLD_MS;
if (!vibrating && heldLongEnough && quietLongEnough) { ... }三条同时满足才退出:!vibrating — 真的解除了振动;
heldLongEnough — 至少报警 2 s,避免闪一下就灭;
quietLongEnough — 解除后再延 2 s。
关键算法:2 Hz 闪烁(绝对相位法)
bool phase = ((now - alarmStartMs) / ALARM_HALF_MS) % 2 == 0;不在 delay(250) 后再切电平,而是基于"距离进入报警态多久"的绝对相位切电平;
因此 loop() 周期抖动(10–20 ms)只影响翻转时刻,不影响长期频率;
抗漂移、首次翻转延迟 = 0。
时间参数
三、功能展示

#include <Arduino.h>
const uint8_t LED_PIN = 4; // 丝印 D4
const uint8_t BUZZER_PIN = 6; // 丝印 D6
const uint8_t SWITCH_PIN = 2; // 丝印 D2 (滑动开关,使用 INPUT_PULLUP)
const uint8_t VIBRATION_PIN = 16; // 丝印 A2 (作为数字口 D16 使用)
const uint8_t ALARM_FREQ_HZ = 2;
const uint16_t ALARM_PERIOD_MS = 1000UL / ALARM_FREQ_HZ; // 500
const uint16_t ALARM_HALF_MS = ALARM_PERIOD_MS / 2; // 250
const uint32_t ALARM_HOLD_MS = 2000;
const uint32_t LOG_INTERVAL_MS = 100;
enum AlarmState : uint8_t {
STATE_IDLE, // 开关 OFF 或 未触发
STATE_ALARM, // 报警中
};
AlarmState state = STATE_IDLE;
uint32_t alarmStartMs = 0; // 进入报警态的时间 (用于 "至少 2 s")
uint32_t alarmLastTriggerMs = 0; // 最后一次检测到振动的时间 (用于 "解除后再延 2 s")
bool alarmBlinkPhase = false; // 当前半周期是高还是低
void setup() {
pinMode(LED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
pinMode(SWITCH_PIN, INPUT_PULLUP);
pinMode(VIBRATION_PIN, INPUT);
Serial.begin(115200);
uint32_t t0 = millis();
while (!Serial && (millis() - t0) < 2000) { /* 等待 USB CDC 枚举或超时 */ }
Serial.println();
Serial.println("=== Vibration Alarm ready ===");
Serial.println(" Switch OFF -> 全部停止");
Serial.println(" Switch ON -> 检测 A2 振动;触发后 LED+蜂鸣器 2 Hz 闪烁");
Serial.println(" 至少报警 2 s;解除后再延 2 s 退出");
}
inline bool switchIsOn() {
return digitalRead(SWITCH_PIN) == LOW;
}
inline bool vibrationDetected() {
return digitalRead(VIBRATION_PIN) == LOW;
}
inline void alarmOutputs(bool on) {
digitalWrite(LED_PIN, on ? HIGH : LOW);
digitalWrite(BUZZER_PIN, on ? HIGH : LOW);
}
// ---------------------------------------------------------------------
// loop
// ---------------------------------------------------------------------
uint32_t lastLogMs = 0;
void loop() {
uint32_t now = millis();
if (!switchIsOn()) {
if (state != STATE_IDLE) {
state = STATE_IDLE;
alarmOutputs(false);
Serial.println("[SW] switch OFF -> alarm cleared, sensing disabled");
}
delay(10);
return;
}
bool vibrating = vibrationDetected();
switch (state) {
case STATE_IDLE: {
if (vibrating) {
state = STATE_ALARM;
alarmStartMs = now;
alarmLastTriggerMs = now;
alarmBlinkPhase = true;
alarmOutputs(true); // 第一半周期:高
Serial.println("[ALARM] vibration detected -> enter ALARM state");
}
break;
}
case STATE_ALARM: {
if (vibrating) {
alarmLastTriggerMs = now;
}
bool phase = ((now - alarmStartMs) / ALARM_HALF_MS) % 2 == 0;
if (phase != alarmBlinkPhase) {
alarmBlinkPhase = phase;
alarmOutputs(alarmBlinkPhase);
}
bool heldLongEnough = (now - alarmStartMs) >= ALARM_HOLD_MS;
bool quietLongEnough = (now - alarmLastTriggerMs) >= ALARM_HOLD_MS;
if (!vibrating && heldLongEnough && quietLongEnough) {
state = STATE_IDLE;
alarmOutputs(false);
Serial.println("[ALARM] quiet for 2 s -> back to IDLE");
}
break;
}
}
// ----- 3) 节流日志 -----
if (now - lastLogMs >= LOG_INTERVAL_MS) {
lastLogMs = now;
Serial.print("sw=");
Serial.print(switchIsOn() ? "ON " : "OFF");
Serial.print(" vib=");
Serial.print(vibrating ? "YES" : "no ");
Serial.print(" state=");
Serial.print(state == STATE_ALARM ? "ALARM" : "IDLE");
if (state == STATE_ALARM) {
Serial.print(" sinceLastTrig=");
Serial.print(now - alarmLastTriggerMs);
Serial.print("ms");
}
Serial.println();
}
delay(10);
}
我要赚赏金
