💡 划重点

  • 目标:做一个能判断你在不在桌前、一旦开小差就“摇头+蜂鸣”提醒你的学习伙伴。
  • 主控:ESP32,自带 Wi-Fi,方便后续把学习时长上传云端。
  • 感知:人体红外传感器(PIR)检测桌前是否有人。
  • 输出:OLED 显示状态与学习计时,舵机模拟“摇头提醒”,蜂鸣器发出警告音。
  • 成本:全部物料控制在 40 元以内(不含电池)。
  • 接线基准:OLED SDA=GPIO21、SCL=GPIO22,PIR=GPIO18,舵机=GPIO13,蜂鸣器=GPIO27。

需求洞察

目标用户:小初中学生、考研考公备考党,尤其是自制力不足、需要外部监督的学习者。

真实痛点

  1. 坐上书桌就想刷手机,一刷半小时过去毫无知觉。
  2. 传统番茄钟只靠时间提醒,根本不关心你“人在不在”;人去厕所半小时它还以为你在学习。
  3. 家长或自习室需要低成本、物理直观的“是否在座”判断工具,而不是装摄像头侵犯隐私。

为什么值得做

方案设计

整个系统划分为四层结构:

  1. 感知层

- 人体红外传感器(HC-SR505 或 mini PIR)贴在显示器下方或桌面前方,检测桌前是否有人。

- 当人体离开超过设定时长(如 5 分钟),判定为“离座”。

- OLED 显示实时状态:“学习中”“离开中”以及计时。

  1. 控制层

- 主控 ESP32-WROOM-32 开发板,负责逻辑判断、模块驱动、Wi-Fi 通信。

- 状态机分为:WAITING(待机)、STUDYING(正常学习)、AWAY(暂时离开)、WARNING(警告提醒)。

- 学习时段内若检测到持续离座,则进入 WARNING 状态。

  1. 执行层

- OLED (SSD1306 128×64):显示当前状态、学习累计时间、离开倒计时。

- SG90 舵机:固定在 3D 打印或瓦楞纸支架上,在警告时快速左右摆动,模拟“摇头”。

- 有源蜂鸣器:发出短促滴声,配合舵机提醒。

  1. 日志/联网层

- 利用 ESP32 内置 Wi-Fi,每 30 秒将学习状态、离开次数通过 HTTP GET 发送至 Blynk 或自建 MQTT 服务器。

- 手机端可看是否在座。该层为可选扩展,不影响基础功能,且不增加额外硬件成本。

物料清单

名称 数量 用途 估算单价(元) 小计(元)
ESP32 开发板(WROOM) 1 主控、Wi-Fi 通信 15.0 15.0
0.96 寸 OLED (I2C) 1 状态与计时显示 8.0 8.0
SG90 舵机 1 摇头提醒 5.0 5.0
HC-SR505 人体红外传感器 1 检测桌前是否有人 3.5 3.5
有源蜂鸣器模块 1 声音提醒 1.5 1.5
面包板 + 杜邦线若干 1 套 接线 5.0 5.0
Micro USB 供电线 1 供电 2.0 2.0
合计 40.0

*注:价格参考电商零售价,如已有部分材料则总成本更低。若需固定支架可使用废纸板或 3D 打印边角料,不计入成本。*

接线与结构

采用 I2C OLED 可节省 GPIO,方便后续扩展。接线基本沿用给定方案:

微调说明:给定 GPIO 已经合理,未作改动。唯一注意的是舵机电源取自开发板的 VIN 引脚(5V),而不是 3.3V,以保证舵机扭力。若使用 USB 供电,VIN 通常直接输出 USB 5V,足够驱动一个 SG90。

结构建议:将 PIR 固定在屏幕下方或书桌挡板上,检测区域对准坐姿位置。舵机装在摇臂上,摇头幅度约 ±30°,必要时可用橡皮筋固定两个小纸板模拟耳朵。ESP32 与面包板放在桌角收纳盒里,仅露出 OLED 和舵机。

核心代码

下面给出 Arduino/PlatformIO 骨架,可直接编译。需要安装 U8g2ESP32ServoWiFi 库。代码中 WiFi 与日志部分注释掉,可根据需要启用。


#include <Wire.h>
#include <U8g2lib.h>
#include <ESP32Servo.h>

// OLED
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 22, /* data=*/ 21);

// PIR
const int PIR_PIN = 18;
// 蜂鸣器
const int BUZZER_PIN = 27;
// 舵机
Servo myservo;
const int SERVO_PIN = 13;

unsigned long lastPresence = 0;
const unsigned long AWAY_TIMEOUT = 5 * 60 * 1000UL; // 5分钟
bool isPresent = false;
unsigned long studyStart = 0;
int warningCount = 0;

void setup() {
  Serial.begin(115200);
  pinMode(PIR_PIN, INPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  myservo.attach(SERVO_PIN);
  myservo.write(90); // 居中

  u8g2.begin();
  studyStart = millis();

  // 可选 WiFi 初始化
  // WiFi.begin("ssid", "password");
}

void loop() {
  // 读取 PIR,高电平有人
  int pirState = digitalRead(PIR_PIN);
  bool currentPresence = (pirState == HIGH);

  if (currentPresence) {
    lastPresence = millis();
    if (!isPresent) {
      // 刚回来
      isPresent = true;
    }
  }

  unsigned long now = millis();
  unsigned long awayDuration = (now - lastPresence);
  if (isPresent && awayDuration > AWAY_TIMEOUT) {
    isPresent = false; // 判定离开
    warningCount++;
  }

  // 更新 OLED
  u8g2.clearBuffer();

  unsigned long studyMinutes = (now - studyStart) / 60000;
  char buf[32];
  sprintf(buf, "学习 %lu 分钟", studyMinutes);
  u8g2.drawStr(0, 10, buf);

  if (!isPresent && awayDuration > AWAY_TIMEOUT) {
    // 提醒
    u8g2.drawStr(0, 25, "状态:开小差!");
    u8g2.drawStr(0, 40, "快回来学习!");
    u8g2.sendBuffer();

    // 舵机摇动 + 蜂鸣
    for (int angle = 60; angle <= 120; angle += 30) {
      myservo.write(angle);
      delay(100);
      digitalWrite(BUZZER_PIN, LOW);
      delay(100);
    }
    myservo.write(90);
  } else if (isPresent) {
    u8g2.drawStr(0, 25, "状态:专注中");
    u8g2.sendBuffer();
    digitalWrite(BUZZER_PIN, LOW);
    myservo.write(90);
    // 可选定时上传学习日志
  } else {
    u8g2.drawStr(0, 25, "状态:暂时离开");
    u8g2.sendBuffer();
    digitalWrite(BUZZER_PIN, LOW);
    myservo.write(90);
  }

  delay(500);
}

调试步骤

  1. 单独验证 OLED:仅连接 OLED,运行 I2C 扫描器示例确保地址 0x3C 可见,再用 U8g2 的 HelloWorld 点亮屏幕。
  2. PIR 灵敏度调节:接上 PIR,串口打印电平变化,调整传感器上两个旋钮(延时、灵敏度)直到人离开约 30 秒输出变低即可。
  3. 舵机中位校准:写入 servo.write(90),观察舵机臂是否水平,必要时调整机械安装角度。
  4. 蜂鸣器测试:直接让 GPIO27 高低翻转,确认有滴声。若为无源蜂鸣器则需 PWM,本项目采用有源模块更简单。
  5. 联合运行:上传完整代码,坐在桌前看 OLED 是否跳转“专注中”,起身离开 5 分钟后检查舵机是否摇头并蜂鸣。
  6. 外壳组装:用纸板折叠一个矩形“小机器人”外壳,将 OLED 嵌在面部,舵机装在头部,增加趣味性。

设计图

桌面作业提醒机器人结构flow
1
感知输入
2
控制处理
3
执行反馈
4
数据上报