IoT 爱的抱枕:基于 Arduino MKR 的远程拥抱传递装置
项目初衷
异地恋、探亲受限、独居老人等场景下,人们渴望跨越距离传递拥抱的温度。本项目利用 Arduino MKR IoT Bundle (MKR WiFi 1010 + 传感器扩展板),结合 DIY 电容式触摸传感器(铝箔纸)、蜂鸣器心跳模拟、Telegram Bot 即时通讯,制作一款“拥抱即发送表情包”的智能抱枕。拥抱时长映射不同表情符号(❤️/🧡/💛/💚/💙/💜),以轻量、趣味的方式缓解分离焦虑。
硬件实现
核心模块
| 组件 | 规格 | 用途 |
|---|---|---|
| 主控板 | Arduino MKR WiFi 1010 (SAMD21 + u-blox NINA-W102) | WiFi/BLE 双模、低功耗、充电管理、板载加密芯片 |
| 触摸传感 | 铝箔纸 (约 15×15cm) + 5MΩ 电阻 | CapacitiveSensor 库,两引脚 (发射/接收) 形成电容耦合,人体接近即增大电容 |
| 心跳反馈 | 无源蜂鸣器 (5V) | PWM 播放心跳音型 (双音节: 短-长-停) |
| 电源 | MKR 板载锂电池充电电路 + 500mAh LiPo | 充电电流 350mA,待机 >7 天 |
| 封装 | 抱枕芯 + 可拆洗套、铝箔内衬、导线走线槽 | 美观、可洗、传感器贴合胸口位置 |
电容传感器原理与布线
// CapacitiveSensor 接线
// Pin 4 (Send) ---- 5MΩ ---- Pin 5 (Receive) ---- 铝箔纸
// 原理:Send 输出高频方波,Receive 通过电阻采样充放电时间
// 人体接地形成并联电容,使充放电时间常数增大,读数升高
CapacitiveSensor cs(4, 5); // 发射引脚, 接收引脚
cs.set_CS_AutocaL_Millis(0xFFFFFFFF); // 关闭自动校准,保持基线稳定
铝箔纸置于枕芯表层下,覆盖薄棉布防止直接接触氧化。导线沿缝隙引出接扩展板排针。建议在铝箔边缘折叠封边,避免锐角放电干扰。
软件系统设计
Telegram Bot 架构
- Bot 创建:与 @BotFather 对话
/newbot获取 Token,设置头像/简介。 - 证书上传:WiFi101 固件更新器烧录
api.telegram.org根证书,启用 TLS 1.2 安全通信。 - 消息格式:JSON 文本,含
chat_id、text(Unicode emoji)、parse_mode。 - 群组模式:关闭 Privacy Mode,Bot 可读取群内所有消息,适合家庭群共享拥抱。
主程序状态机
// 核心逻辑框架
#include
#include
#include
#include
#include // 低功耗定时
CapacitiveSensor hugSensor(4, 5);
TelegramBot bot(BOT_TOKEN, wifiClient);
RTCZero rtc;
enum HugState { IDLE, HUGGING, SENDING };
HugState state = IDLE;
unsigned long hugStart = 0;
int hugLevel = 0; // 0-5 对应 6 种表情
void setup() {
Serial.begin(115200);
WiFi.begin(SSID, PASS);
while (WiFi.status() != WL_CONNECTED) delay(500);
bot.begin();
rtc.begin();
// 看门狗 30s
Watchdog.enable(30000);
}
void loop() {
Watchdog.reset();
long capVal = hugSensor.capacitiveSensor(30); // 30μs 采样窗口
switch (state) {
case IDLE:
if (capVal > HUG_THRESHOLD) { // 触发拥抱
state = HUGGING;
hugStart = millis();
playHeartbeat(); // 开始播放心跳
}
break;
case HUGGING:
if (capVal <= HUG_THRESHOLD) { // 拥抱结束
state = SENDING;
hugLevel = constrain(map(millis() - hugStart, 0, 10000, 0, 5), 0, 5);
sendHugEmoji(hugLevel);
state = IDLE;
} else {
playHeartbeat(); // 持续播放
}
break;
case SENDING:
// 发送中非阻塞,由 TelegramBot 库内部处理
break;
}
// 定期处理 Bot 消息队列
bot.getUpdates(); // 必须周期性调用
delay(50);
}
心跳声合成
void playHeartbeat() {
// Lub-Dub 模式: 低频短促 + 稍长 + 间歇
tone(BUZZER_PIN, 60, 80); // Lub ~60Hz
delay(120);
tone(BUZZER_PIN, 45, 150); // Dub ~45Hz
delay(200);
noTone(BUZZER_PIN);
delay(600); // 周期约 1s, 60bpm
}
表情映射表
| 拥抱时长 | 等级 | Unicode Emoji | 含义 |
|---|---|---|---|
| < 2s | 0 | \U0001F49B (💛) | 浅拥抱/问候 |
| 2-4s | 1 | \U0001F49C (💜) | 友谊/安慰 |
| 4-6s | 2 | \U0001F499 (💙) | 深情/信任 |
| 6-8s | 3 | \U0001F49A (💚) | 治愈/成长 |
| 8-10s | 4 | \U0001F497 (💗) | 心动/爱意 |
| >10s | 5 | \U00002764 \U0000FE0F (❤️) | 深沉的爱 |
发送函数
void sendHugEmoji(int level) {
const char* emojis[] = {
"\U0001F49B", "\U0001F49C", "\U0001F499",
"\U0001F49A", "\U0001F497", "\U00002764\U0000FE0F"
};
StaticJsonDocument<200> doc;
doc["chat_id"] = TARGET_CHAT_ID; // 预存或从最后一条消息获取
doc["text"] = emojis[level];
String payload; serializeJson(doc, payload);
bot.sendMessage(payload); // 封装 HTTP POST
}
低功耗与稳健性设计
- 深度睡眠:无拥抱时 SAMD21 进入 Standby 模式,仅 RTC 运行,电流 < 100μA。
- 唤醒源:电容传感器中断 (需外部中断引脚) 或 RTC 闹钟 (每小时心跳保活)。
- 看门狗:Adafruit SleepyDog 30s 复位,防止 WiFi/TCP 卡死。
- 网络重连:WiFi 断开自动重连指数退避 (1s,2s,4s...max 60s),Telegram 长轮询超时 25s 自动重建。
- 消息去重:同一次拥抱仅发送一次,防抖时间 5s。
制作与调试步骤
- 传感器标定:串口绘图器观察静态/拥抱时电容读数分布,设定
HUG_THRESHOLD为 (基线+峰值)/2,迟滞 ±10%。 - Bot 功能测试:手机发送任意表情,确认 Bot 回显正常,验证 Unicode 编码正确。
- 心跳音调试:调整频率/时长/间隔,使听感最接近真实心跳 (参考 60-80bpm, Lub 比 Dub 短)。
- 封装测试:装入枕套,不同体型/衣物厚度测试触发可靠性,微调阈值。
- 长时运行:满电放置 7 天,验证待机电流、自动重连、按时发送心跳保活消息。
隐私与伦理考量
- 数据最小化:仅传输拥抱事件与表情,不上传原始电容波形、时间戳精度降至分钟级。
- 用户控制:物理开关一键断开 WiFi,LED 指示连接状态;App 端可随时撤销 Bot 权限。
- 情感安全:明确告知接收方“这是自动发送的表情包,不代表实时生理状态”,避免过度解读。
- 开源透明:固件代码开源 (MIT 许可),接受社区审计,无隐藏遥测。
拓展创意
- 双向拥抱:双方各持一枕,收到表情包时本地震动/发热/发光回应,形成闭环。
- 拥抱日历:云端聚合生成年度拥抱热力图,导出为纪念海报。
- 语音留言:长按预存键录制 5s 语音,随表情包以语音消息形式发送。
- 生理信号融合:接入 MAX30101 心率/血氧,拥抱时同步传输实时心率变异性 (HRV),量化“心跳同步”现象。
- 宠物版:改造为宠物项圈/垫,监测分离焦虑行为,主人收到“想你了”推送。
制造工艺