💡 划重点
- 主控:ESP32 低功耗双核芯片,板载 Wi‑Fi 方便后期升级联网提醒。 感知:PIR 人体传感器判断桌前是否有人,避免无人时误报。 执行:舵机带动小旗帜升降或轻敲桌面,蜂鸣器发出语音级提示音,OLED 显示作业项与倒计时。 逻辑:以番茄钟为核,作业 25 分钟提醒休息,休息 5 分钟提醒归位;人离开超时自动暂停计时。 **
需求洞察
目标用户:8~15 岁中小学生,尤其是自主管理能力较弱、需要家长远程提醒的孩子。
真实痛点:孩子一坐桌前就容易发呆、磨蹭,家长口头催促容易引起逆反。普通计时器只有声音,孩子离开书桌后仍在空跑,失去提醒意义。家长上班时无法了解孩子是否真正坐在书桌前。
为什么值得做:
- 把“被动催促”变成“机器提醒”,降低亲子摩擦。
- 结合人体感应与番茄钟,离开即暂停,更贴合真实作业情景。
- ESP32 可后续通过 MQTT 将桌前人状态发给家长手机,实现轻量远程陪伴。
- 硬件门槛低,小学生也能在家长协助下焊接完成,兼具教育属性。
方案设计
感知层
- PIR 人体传感器(GPIO18):检测桌前 1 米以内是否有人。人离开超过 20 秒,计时器自动暂停,蜂鸣器不再鸣响,舵机回至待机位。
- ESP32 内部霍尔/触摸(预留):未来可加装触摸按键,快速开启/关闭提醒模式。
控制层
- ESP32‑WROOM‑32E:运行 Arduino 框架。定时器使用
millis()非阻塞。Wi‑Fi 暂不开启,以降低功耗。 - OLED SSD1306(I²C,SDA=21/SCL=22):显示当前任务名、剩余时间、人体状态图标。
- 任务队列:支持预设 4 个作业科目,按下按键循环选择,或通过串口/后期 BLE 配置。
执行层
- SG90 舵机(GPIO13):倒计时归零时,摆臂抬起小旗或轻敲桌面木框,视觉+振动提醒。
- 有源蜂鸣器(GPIO27):驱动频率 2.3kHz 左右的间停声,5 秒后自动停止,避免噪声污染。
日志/联网层(选配)
- ESP32 在联网模式下可每 10 分钟上报一次“桌前状态”到 MQTT(如家长手机 App),但本 40 元方案先以本地独立运行为主,代码中预留 Wi‑Fi 接口。
物料清单
| 序号 | 物料 | 数量 | 用途 | 估算单价(元) |
|---|---|---|---|---|
| 1 | ESP32‑WROOM‑32E 开发板 | 1 | 主控与供电 | 15 |
| 2 | 0.96 寸 OLED SSD1306 (I²C) | 1 | 显示任务与时间 | 8 |
| 3 | HC‑SR501 PIR 人体传感器 | 1 | 桌前人体检测 | 5 |
| 4 | SG90 微型舵机 | 1 | 物理动作提醒 | 4 |
| 5 | 有源蜂鸣器模块 3.3~5V | 1 | 声音提醒 | 2 |
| 6 | 轻触按键 + 10kΩ 电阻 | 3 | 模式切换/确认 | 1 |
| 7 | 5V 1A Micro USB 电源线 | 1 | 供电 | 2 |
| 8 | 排针、杜邦线、洞洞板、小旗帜、热缩管 | 若干 | 连接与结构 | 2 |
| 合计 | 39 元 |
总价 39 元,略低于 40 元预算,剩余 1 元作为胶水扎带备用。
接线与结构
- OLED:VCC→3.3V,GND→GND,SDA→GPIO21,SCL→GPIO22。此处遵循默认 I²C 通道,线序整齐,无需微调。
- PIR:VCC→5V(或 3.3V,模块带稳压),GND→GND,OUT→GPIO18。微调理由:GPIO18 为 RTC 域引脚,支持从深度睡眠唤醒,未来可做超低功耗唤醒,因此选用此脚而非其他。
- 舵机:信号线→GPIO13,电源直接由开发板 5V 引脚供电(舵机堵转电流约 400mA,ESP32 的 USB 供电足够),GND 与板共地。
- 蜂鸣器:正极→GPIO27,负极→GND。GPIO27 是普通输出,驱动电流足够。
- 按键:一端接 GPIO32、33、25(内部上拉),另一端接地,用于“下一个任务/确认/暂停”。
结构安装:将全部模块焊接或用扎带固定在 50×70mm 洞洞板上。PIR 镜头朝前方稍向下,舵机安装在一个 L 型铝片上,带动纸质小旗。OLED 用尼龙柱垫高,方便阅读。底座用亚克力边角料或木块配重,避免倾倒。
核心代码
以下为 Arduino 骨架,可直接编译上传。省略非关键防抖与部分常量。
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ESP32Servo.h>
#define PIR_PIN 18
#define SERVO_PIN 13
#define BUZZER_PIN 27
#define BTN_NEXT 32
#define BTN_OK 33
#define BTN_PAUSE 25
Adafruit_SSD1306 oled(128, 64, &Wire;);
Servo flagServo;
enum State {IDLE, WORK, REST};
State state = IDLE;
unsigned long startTime = 0;
unsigned long pauseTime = 0;
bool paused = false;
int workSec = 25 * 60;
int restSec = 5 * 60;
int currentTimeLeft = workSec;
const char* tasks[] = {"语文", "数学", "英语", "科学"};
int taskIdx = 0;
void setup() {
pinMode(PIR_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(BTN_OK, INPUT_PULLUP);
flagServo.attach(SERVO_PIN);
flagServo.write(0);
Wire.begin(21, 22);
Serial.begin(115200);
}
void loop() {
bool person = digitalRead(PIR_PIN);
handleButtons();
if (state == IDLE) {
if (person && !digitalRead(BTN_OK)) {
startTimer(WORK);
}
} else {
if (!person) {
if (!paused) {
paused = true;
pauseTime = millis();
}
if (millis() - pauseTime > 20000) { // 离开超20秒暂停任务
state = IDLE;
paused = false;
}
} else {
if (paused) {
paused = false;
}
updateTimer();
}
}
updateDisplay();
delay(200);
}
void startTimer(State s) {
state = s;
startTime = millis();
currentTimeLeft = (s == WORK) ? workSec : restSec;
paused = false;
}
void updateTimer() {
unsigned long elapsed = (millis() - startTime) / 1000;
if (elapsed >= currentTimeLeft) {
triggerAlarm();
if (state == WORK) startTimer(REST);
else state = IDLE;
}
}
void triggerAlarm() {
for (int i = 0; i < 5; i++) {
}
}
void handleButtons() {
if (!digitalRead(BTN_NEXT)) {
taskIdx = (taskIdx + 1) % 4;
delay(300);
}
if (!digitalRead(BTN_PAUSE)) {
paused = !paused; delay(300);
}
}
void updateDisplay() {
oled.clearDisplay();
oled.setTextSize(2);
oled.setCursor(0,0);
oled.print(tasks[taskIdx]);
oled.setCursor(70,0);
oled.print(state==WORK?"工":"停");
oled.setTextSize(1);
oled.setCursor(0,30);
oled.print("剩余:");
long remain = currentTimeLeft - ((millis()-startTime)/1000);
oled.print(remain/60); oled.print("分");
oled.print(remain%60); oled.print("秒");
oled.setCursor(0,50);
oled.print(digitalRead(PIR_PIN)?"有人":"无人");
oled.display();
}
调试步骤
- 空板测试:先不安装舵机和蜂鸣器,上传代码后通过串口监视器查看 PIR 状态,手在传感器前晃动,确认 GPIO18 电平变化。
- OLED 显示检查:确认 I²C 地址为 0x3C,如果屏幕无显示,运行 I²C Scanner 脚本检查接线。
- 舵机角度校准:将
flagServo.write(80)逐步调整为适合旗帜抬起的角度,确保不卡住结构。 - 蜂鸣器响度:若太吵,可在回路中串接 100Ω 电阻或通过 PWM 降低占空比。
- 离开暂停机制:设定
pauseTime阈值为 5 秒进行快速测试,确认人走开 OLED 显示“停”且计时归零。 - 完整流程:从 IDLE 按下确认键 → 25 分钟作业 → 触发提醒 → 自动进入 5 分钟休息 → 休息结束归零。多次循环观察有无计时溢出。
设计图
桌面作业提醒机器人结构flow
1
感知输入 (PIR人体检测 | 按键输入)
→
2
控制处理 (ESP32 番茄钟逻辑 | 任务队列)
→
3
执行反馈 (OLED显示 | 舵机动作 | 蜂鸣器提示)
→
4
数据上报 (可选Wi-Fi MQTT 上报桌前状态)