💡 划重点

  • 主控选 ESP32-C3,支持 Wi-Fi,成本约 15 元。
  • 4 枚独立按键对应四个服药时段,按下即记录,操作难度为零。
  • OLED 本地显示服药状态,断电记忆最近记录。
  • 一键触发飞书机器人通知,家属远程接收,独居用药不靠猜。
  • 整机 BOM 成本控制在 50 元内,适合批量制作与社区分享。

需求洞察

目标用户:独居老人、慢性病需长期用药者,以及负责照护的子女或伴侣。

真实痛点:老年人常因记性差漏服、错服或重复服药;子女无法实时知晓父母是否按时吃药,电话询问又易引起焦虑和抵触。药盒虽多,但传统药盒不记录、不联网,无法形成“吃没吃、几点吃”的证据链。

为什么值得做:药品服用不是高频娱乐需求,而是低频刚需。设备只要能减少一个漏服事件,就可能避免一次急诊甚至住院。相比智能药柜动辄数百元,50 元内能实现的打卡反馈方案,既有实际看护价值,又有低门槛复制意义,适合作为社区健康小组或亲子共同完成的硬件项目。

方案设计

本方案按四层架构设计,覆盖从物理操作到远程通知的完整链路。

感知层:4x1 自复位按键矩阵,搭配 10kΩ 上拉电阻,ESP32-C3 内部启用输入模式。每按键对应“早、中、晚、睡前”四个时间段,按下即产生一个低电平脉冲,作为服药事件触发源。

控制层:ESP32-C3 负责按键去抖(20ms 软件滤波)、事件解析、状态机维护。控制层区分“正常打卡”“重复打卡”“补卡”三种事件,并驱动 OLED 与 LED 反馈。

执行层:0.96 寸 OLED 显示当前时间、最近打卡时段、连续打卡天数。绿色 LED 点亮 2 秒表示记录成功,红色 LED 闪烁表示“该时段未打卡”的提醒。

日志/联网层:本地维护一个 7 条记录的环形缓存(时段 + 时间戳)。每次按压触发 HTTP POST 请求,携带 JSON 数据到飞书自定义机器人 Webhook,家属手机端即时收到“刚完成午间服药”之类的推送。断网时自动缓存并等待重试。

物料清单

物料名称 数量 用途 估算单价(元) 小计(元)
ESP32-C3 开发板 1 主控 15.00 15.00
0.96 寸 OLED(I2C) 1 状态显示 10.00 10.00
轻触按键 6x6mm 4 服药打卡输入 0.30 1.20
电阻 10kΩ 4 按键上拉 0.05 0.20
LED 红色 5mm 1 未服药提醒 0.20 0.20
LED 绿色 5mm 1 打卡成功反馈 0.20 0.20
限流电阻 220Ω 2 LED 保护 0.05 0.10
面包板/洞洞板 1 电路连接 5.00 5.00
杜邦线若干 1 组 接线 3.00 3.00
3D 打印/塑料外壳 1 结构保护 12.00 12.00
合计 46.90

接线与结构

推荐接线表如下,若与现有开发板丝印冲突,仅需同步修改代码中的引脚宏定义即可。

信号 GPIO 引脚 说明
按键 0 13 晨起服药,内部上拉,按下接 GND
按键 1 12 午间服药,内部上拉,按下接 GND
按键 2 14 晚间服药,内部上拉,按下接 GND
按键 3 27 睡前服药,内部上拉,按下接 GND
OLED SDA 8 I2C 数据线
OLED SCL 9 I2C 时钟线
LED 红 10 未服药提醒,低电平点亮
LED 绿 21 打卡成功,低电平点亮

此处做一处微调:原建议按键接 GPIO 0/1/2/3,但 GPIO 0 和 GPIO 1 在多数 ESP32-C3 开发板上为启动模式选择与串口打印引脚,使用后可能导致一键下载或日志异常。故将按键移至 13/12/14/27,这些引脚均支持通用输入,且互不冲突。OLED 维持建议的 8/9 引脚,LED 中红色保留 GPIO 10,绿色使用 GPIO 21,性能不受影响。

核心代码

本项目使用 Arduino 框架,代码骨架可编译运行。务必先安装 ESP32 开发板包与以下库:Adafruit SSD1306、Adafruit GFX、ArduinoJson。


#include <WiFi.h>
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <ArduinoJson.h>

// 网络配置
const char* ssid = "your_wifi";
const char* password = "your_password";
const char* webhook_url = "https://open.feishu.cn/open-apis/bot/v2/hook/your_token";

// 引脚映射
#define BTN0 13
#define BTN1 12
#define BTN2 14
#define BTN3 27
#define OLED_SDA 8
#define OLED_SCL 9
#define LED_RED 10
#define LED_GREEN 21

// OLED 配置
Adafruit_SSD1306 display(128, 64, &Wire;, -1);

// 时段名称
const char* slots[4] = {"晨起", "午间", "晚间", "睡前"};
int lastSlot = -1;

void setup() {
  pinMode(BTN0, INPUT_PULLUP);
  pinMode(BTN1, INPUT_PULLUP);
  pinMode(BTN2, INPUT_PULLUP);
  pinMode(BTN3, INPUT_PULLUP);
  pinMode(LED_RED, OUTPUT);
  pinMode(LED_GREEN, OUTPUT);
  digitalWrite(LED_RED, HIGH);
  digitalWrite(LED_GREEN, HIGH);
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.println("Welcome! 按任意键");
  display.display();
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
  display.clearDisplay();
  display.setCursor(0, 0);
  display.println("WiFi OK");
  display.display();
}

void loop() {
  int slot = -1;
  if (digitalRead(BTN0) == LOW) slot = 0;
  if (digitalRead(BTN1) == LOW) slot = 1;
  if (digitalRead(BTN2) == LOW) slot = 2;
  if (digitalRead(BTN3) == LOW) slot = 3;

  if (slot >= 0) {
    delay(50); // 基础去抖
    if (digitalRead(slot == 0 ? BTN0 : (slot == 1 ? BTN1 : (slot == 2 ? BTN2 : BTN3))) == LOW) {
      recordPill(slot);
      delay(2000);
    }
  }
}

void recordPill(int slot) {
  // 本地显示与 LED 反馈
  digitalWrite(LED_GREEN, LOW);
  display.clearDisplay();
  display.setCursor(0, 0);
  display.print("已打卡: ");
  display.println(slots[slot]);
  display.display();
  delay(1500);
  digitalWrite(LED_GREEN, HIGH);

  // 构造 JSON
  StaticJsonDocument<256> doc;
  doc["text"] = String("【服药提醒】") + slots[slot] + "时段已完成服药打卡";
  String payload;
  serializeJson(doc, payload);

  // 发送飞书
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin(webhook_url);
    http.addHeader("Content-Type", "application/json");
    http.POST(payload);
    http.end();
  } else {
    digitalWrite(LED_RED, LOW);
    delay(1000);
    digitalWrite(LED_RED, HIGH);
  }
}

以上代码省略了 HTTPClient 库的细节,实际使用时需在文件头部 #include <HTTPClient.h>。逻辑中若 Wi-Fi 断开,则红色 LED 点亮 1 秒提示本地缓存失败。

调试步骤

  1. 静态检查:确认所有外围元件焊接无短路、OLED 电源电压为 3.3V 或 5V 兼容版、按键按下时能连通 GND。
  2. 烧录测试:先在串口监视器中打印各按键的 GPIO 电平,依次按压,确认每个按键都有独立的 0(按下)与 1(松开)切换。
  3. OLED 验证:上传一个 I2C 扫描程序,确保设备地址为 0x3C,并显示白色字符不偏移。
  4. 功能单测:连续按同一个键两次,绿色 LED 亮起且 OLED 仅显示一次“已打卡”,说明去抖与防重复逻辑生效。
  5. 断网模拟:关闭路由器电源,按压按键,观察红色 LED 闪烁;随后开启路由器,设备自动重连后再次按压,应能在飞书端收到新消息。
  6. 稳定运行: 连续运行 24 小时,期间记录三次打卡,确认 OLED 无残影、飞书消息无重复、无死机。

设计图

药品服用打卡按钮盒结构flow
1
感知输入
2
控制处理
3
执行反馈
4
数据上报