💡 划重点
- 一眼知状态:红绿黄三色直观显示会议室“正在使用”“空闲”“即将使用”
- 零操作查询:无需打开手机或电脑,走到门口就能看清
- 自动同步飞书日历:会议预约、取消、变更实时生效
- 成本极低:全套物料 65 元以内,适合批量部署
- 可扩展上报:通过 MQTT 将占用数据推送至大屏、管理后台
需求洞察
目标用户
开放式办公室中的员工、行政前台、临时来访客人。
真实痛点
- 会议室预约多、变动快,门关着无法判断是有人占用还是忘了取消。
- 员工频繁打开飞书或 OA 查会议室日历,打断工作流。
- 常出现“以为空闲推门而入,却发现有人在开会”的尴尬。
- 小型公司无专业会议室管理系统,行政难以实时掌握使用率。
为什么值得做
市面上智能会议屏或占用传感器单价动辄数百元,布线复杂。本项目利用 ESP32 + WS2812 灯带,配合飞书日历 API 和 MQTT,用 65 元即可把一个普通会议室变得“可视化”,且安装极简。对于 3 间以上会议室的团队,成本优势巨大,同时可沉淀会议室利用率数据,为行政决策提供依据。
方案设计
感知层
- 飞书日历 API:定时拉取当日会议室日程,获取会议开始/结束时间、标题、组织者。
- 本地按键:手动切换显示模式(例如轮播全天占用概览)。
控制层
- ESP32 通过 Wi‑Fi 连接内网,每分钟请求一次飞书日历接口。
- 根据当前时间与最近一场会议的时间关系,决定灯色:
- 空闲:绿色
- 当前有会议:红色
- 距下一场开始 ≤ 10 分钟:黄色
- 按键触发中断,循环切换本地显示模式,可配合灯色闪动提示。
执行层
- WS2812 RGB LED 灯带/灯环,由 ESP32 GPIO 驱动,全彩控制,可调节亮度和颜色渐变,避免刺眼。
- 可加装亚克力外壳与磁吸支架,安装于会议室门口墙面。
日志/联网层
- 状态变化时通过 MQTT 发送 JSON 到本地 Broker(如 mosquitto 或 EMQX),上报会议室 ID、当前状态、下一场会议时间等。
- 后端或 Node‑RED 可消费该消息,推送到大屏、钉钉/飞书群或进行统计。
物料清单
| 物料 | 数量 | 用途 | 估算单价 (元) |
|---|---|---|---|
| ESP32-C3 迷你开发板 | 1 | 主控,Wi‑Fi | 15 |
| WS2812 灯环 (8 或 12 颗) | 1 | 状态指示灯 | 8 |
| 轻触按键 | 1 | 切换显示模式 | 1 |
| 电阻 10kΩ | 1 | 按键下拉 | 0.1 |
| 5V 1A USB 电源适配器 | 1 | 供电 | 8 |
| Micro‑USB 供电线 | 1 | 连接电源 | 2 |
| 杜邦线 + 排针 | 若干 | 接线 | 2 |
| 亚克力外壳 + 磁吸贴 | 1 | 装饰与安装 | 15 |
| 合计 | 51.1 |
总成本约 51 元,留有约 14 元余量应对器件价格波动或增加蜂鸣器、光敏电阻等扩展件。
接线与结构
- WS2812 数据线 → GPIO13
- 按键一端接 GND,另一端接 GPIO0
- 说明:GPIO0 内置上拉,按键按下为低电平,代码侦测下降沿,无需外加电阻即可使用。但若不放心可额外并联 10kΩ 下拉,保证悬空状态稳定。此处采用板载上拉,物料清单中不强制加电阻。
- WS2812 VCC 接 5V,GND 接 GND,且与 ESP32 共地
- ESP32 由 USB 供电,5V 同时供给灯环
结构示意
ESP32 与灯环固定在亚克力底板上,灯环面向前方,按键从侧面或底部引出。亚克力外壳半透明白色,灯光柔和扩散。
核心代码
(Arduino 骨架,可编译上传)
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Adafruit_NeoPixel.h>
#include <PubSubClient.h>
#define LED_PIN 13
#define NUM_LEDS 12
#define BUTTON_PIN 0
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASS";
const char* feishu_url = "https://open.feishu.cn/open-apis/calendar/v4/calendars/YOUR_CALENDAR_ID/events?start_time=...";
const char* mqtt_server = "192.168.1.100";
Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
WiFiClient espClient;
unsigned long lastFetch = 0;
int roomState = 0; // 0=空闲, 1=即将, 2=使用中
int displayMode = 0; // 按键切换模式
void setup() {
Serial.begin(115200);
strip.begin();
strip.setBrightness(30);
strip.show();
attachInterrupt(BUTTON_PIN, []() {
static unsigned long lastPress = 0;
if (millis() - lastPress > 300) {
displayMode = (displayMode + 1) % 3;
}
lastPress = millis();
}, FALLING);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) delay(500);
}
void setColor(int r, int g, int b) {
for(int i=0; i<NUM_LEDS; i++) strip.setPixelColor(i, r, g, b);
strip.show();
}
void fetchCalendar() {
HTTPClient http;
http.begin(feishu_url);
int code = http.GET();
if (code == 200) {
String payload = http.getString();
DynamicJsonDocument doc(4096);
deserializeJson(doc, payload);
// 解析 items, 判断当前会议状态
// 示例逻辑:
// if (currentMeeting) roomState = 2;
// else if (nextMeetingStart - now < 600) roomState = 1;
// else roomState = 0;
}
http.end();
}
void loop() {
if (!client.connected()) {
client.publish("room/state", "online");
}
client.loop();
if (millis() - lastFetch > 60000) {
fetchCalendar();
lastFetch = millis();
}
switch(roomState) {
case 0: setColor(0, 255, 0); break; // 绿
case 1: setColor(255, 200, 0); break; // 黄
case 2: setColor(255, 0, 0); break; // 红
}
// 按键模式可叠加闪烁效果,此处略
}
*注:飞书 URL 需要根据真实日历 ID 和鉴权 token 拼接,建议使用“自建应用”获取 tenant_access_token,并在请求头中带入。*
*完整获取 token 和解析逻辑可在实际项目中补充。*
调试步骤
- 硬件连通性测试
上传空程序,逐个点亮 WS2812 的 R、G、B,确认焊接无误。
- Wi‑Fi 连接
串口打印连接状态,确保能 ping 通外网。
- 飞书 API 连通
先用 Postman 或 curl 验证能否获取日历事件,再将 URL 填入代码,观察串口打印的 JSON。
- 时间解析与灯色切换
手动修改系统时间或使用 NTP 同步,测试三种状态灯色变化。
- 按键测试
按下按键是否正确改变 displayMode,并观察灯效变化。
- MQTT 上报
使用 MQTT 客户端订阅房间主题,确认状态消息准确。