三色灯智能会议室状态

划重点

需求洞察

目标用户:行政后勤人员、频繁开会的团队负责人、以及所有需要快速确认会议室可用性的普通员工。

真实痛点:在多数办公环境中,会议预定系统虽然存在,但“预定”与“实际使用”严重脱节。员工到达会议室门口才发现房间被超时占用、临时取消后系统未更新,或空闲状态却无人进入。凑近屏幕看日历既不直观,也浪费了等待时间。更加常见的是,会议提前结束时,后续员工不知道房间已被释放。

为什么值得做:本项目用三色灯(红=占用,绿=空闲,蓝=即将开始)以低成本、高一致性、零学习成本的方式解决高频低效问题。同时通过飞书日历的同步,让“线上预定-线下物理状态”形成闭环,避免了人工敲门或提前抢占的尴尬。是一个实用、可落地、能展示的硬件编程项目。

方案设计

感知层:使用 1 个 DHT22 温湿度传感器(用于会议室环境监测,辅助判断室内是否有人),1 个机械自复位按键(用于手动切换状态)。感知层还包含 ESP32 内置的日历定时器(通过飞书 API 拉取未来 1 小时的预定事件)。

控制层:ESP32 通过 MQTT 协议连接公共 Broker(如 test.mosquitto.org 或自建 EMQX),订阅“会议室/状态”主题。控制逻辑包含状态机——空闲、占用、即将开始、维护中。

执行层:WS2812 灯条(1 米,30 灯/米),采用 GPIO 13 控制。根据状态机显示不同颜色,占用为红色常亮、空闲为绿色、即将开始为蓝色呼吸效果。

日志/联网层:ESP32 定时向 MQTT 发布设备心跳与当前状态。运行日志通过串口输出,并可选推送 Webhook 到飞书机器人,用于排障与历史统计。

物料清单

物料名称 数量 用途 估算单价(元) 合计(元)
ESP32 DevKit V1 开发板 1 主控 22 22
WS2812 灯条(1米/30灯) 1 状态显示 15 15
DHT22 温湿度传感器 1 环境感知 8 8
自复位轻触按键 2 状态切换/复位 1 2
5V 2A 电源适配器 1 供电 9 9
杜邦线与面包板 1套 接线 5 5
透明亚克力灯罩(A4尺寸) 1 外壳遮挡,使光线均匀 4 4
合计 65

接线与结构

接线方案

结构说明:将 WS2812 灯条贴在亚克力罩内侧一周,利用亚克力导光使整个门牌均匀发亮。亚克力罩以 3D 打印或双面胶固定于门框上。按键安装在门框侧面约 1.2m 高度。ESP32 用塑料盒保护,放置于灯条上方隐藏空间内。供电线从门框顶部走线,不遮挡视线。

核心代码


#include <WiFi.h>
#include <PubSubClient.h>
#include <Adafruit_NeoPixel.h>
#include <DHT.h>

#define LED_PIN 13
#define BUTTON_PIN 0
#define DHT_PIN 17

#define NUM_LEDS 30

const char* ssid = "OFFICE_WIFI";
const char* password = "PASSWORD";
const char* mqtt_server = "test.mosquitto.org";

WiFiClient espClient;
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
DHT dht(DHT_PIN, DHT22);

int state = 0; // 0=空闲, 1=占用, 2=即将开始
unsigned long lastPress = 0;

void setColor(uint8_t r, uint8_t g, uint8_t b) {
  for (int i = 0; i < NUM_LEDS; i++) strip.setPixelColor(i, strip.Color(r, g, b));
  strip.show();
}

void updateDisplay() {
  switch (state) {
    case 0: setColor(0, 255, 0); break;       // 绿色
    case 1: setColor(255, 0, 0); break;       // 红色
    case 2: setColor(0, 0, 255); break;       // 蓝色
  }
}

void publishState() {
  String payload = "{\"device\":\"room_01\",\"state\":" + String(state) + ",\"temp\":" + String(dht.readTemperature()) + "}";
  client.publish("meetingroom/status", payload.c_str());
}

void callback(char* topic, byte* message, unsigned int length) {
  String msg;
  for (int i = 0; i < length; i++) msg += (char)message[i];
  if (String(topic) == "meetingroom/set") {
    if (msg == "busy") state = 1;
    else if (msg == "free") state = 0;
    else if (msg == "soon") state = 2;
    updateDisplay();
    publishState();
  }
}

void reconnect() {
  while (!client.connected()) {
    if (client.connect("ESP32_Room_01")) client.subscribe("meetingroom/set");
    delay(2000);
  }
}

void setup() {
  Serial.begin(115200);
  strip.begin();
  strip.setBrightness(50);
  dht.begin();
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) { delay(500); }
  client.setCallback(callback);
  updateDisplay();
}

void loop() {
  if (!client.connected()) reconnect();
  client.loop();

  if (digitalRead(BUTTON_PIN) == LOW && millis() - lastPress > 300) {
    lastPress = millis();
    state = (state + 1) % 3;
    updateDisplay();
    publishState();
  }

  static unsigned long lastPublish = 0;
  if (millis() - lastPublish > 60000) {  // 每分钟上报一次环境数据
    lastPublish = millis();
    publishState();
  }
}

*说明:该代码为 PlatformIO + Arduino 框架骨架,实际部署时可加入飞书日历 API 拉取逻辑(需引入 HTTPClient 并以 JSON 解析)。*

调试步骤

  1. 硬件上电检查:连接 USB 供电,确认 ESP32 板载 LED 亮起,WS2812 灯条无异常发热。
  2. 烧录与串口监视:首次烧录后观察串口输出,确认 Wi-Fi 连接成功且 IP 已获取。
  3. MQTT 联调:使用 MQTTX 客户端连接同一 Broker,发送 busy,观察灯变红,串口返回上报日志。
  4. 按键功能:按压 GPIO 0 按键,状态依次切换,灯效同步变化。
  5. 状态异常模拟:断开 Wi-Fi,验证状态保持上电时的默认值,不出现闪烁或复位。重新联网后自动恢复订阅。
  6. 日历接口调试(可选):登录飞书开放平台,获取租户 Token,在代码中替换后测试实际事件拉取与状态机联动。

设计图

三色灯智能会议室状态结构flow
1
感知输入
2
控制处理
3
执行反馈
4
数据上报