💡 划重点
- 主控选用 ESP32,自带 WiFi,直接对接公交实时 API,无需额外联网模块。
- 屏幕采用 ILI9341(2.4 寸 TFT),显示下一班公交到站倒计时及线路状态。
- 按键唤醒:不插电时 ESP32 进入深度睡眠,按一下立即启动并刷新数据,功耗极低。
- 全部物料成本控制在 75 元以内,适合上班族放在办公桌或玄关使用。
- 核心难点在于 HTTP API 的解析与断网重试,以及按键唤醒后的快速加载。
需求洞察
目标用户:每天固定乘坐 1-2 路公交通勤的上班族,尤其在早晚高峰期对到站时间有强刚需。
真实痛点:手机查公交需要解锁、打开 App、输入线路,操作步骤多且容易忘记;候车亭电子屏信息不全,步行到站后才发现车刚走,白白浪费时间。
为什么值得做:桌面播报器固定在视野范围内,抬手即看,无需任何操作。ESP32 每次唤醒后自动拉取最新到站数据,屏幕显示“下一班 3 分钟后到达”,比手机更直观、更省心。而且这个项目成本低、技术栈成熟,非常适合作为入门级智能家居硬件练手,既能巩固嵌入式网络编程,又能产出每天真正用得上的工具。
方案设计
感知层:通过 HTTP GET 请求访问公交实时 API(如高德或公交公司开放接口),获取指定站点、指定线路下一班车的预计到达分钟数。ESP32 内置 WiFi 负责网络连接,按键作为物理感知触发信号。
控制层:ESP32 主循环解析 JSON 数据,提取到站时间、车辆距离、是否进站等关键字段;根据剩余时间切换显示颜色(>5 分钟绿色、2-5 分钟黄色、<2 分钟红色)并驱动蜂鸣器短鸣提醒。
执行层:ILI9341 TFT 屏幕绘制“线路号 + 倒计时 + 提示语”,同时可选控制 LED 灯闪烁。采用 TFT_eSPI 库,SPI 通信刷新帧率足够流畅。
日志/联网层:每次唤醒后自动记录本次请求状态(成功/失败、响应耗时、到站时间)到 ESP32 内置 NVS 闪存,可通过串口导出分析。断网时自动重试 3 次,失败则屏幕显示“网络异常”。
物料清单
| 物品 | 数量 | 用途 | 估算单价 |
|---|---|---|---|
| ESP32 DevKit 开发板 | 1 | 主控,WiFi 与 GPIO 控制 | ¥20 |
| ILI9341 2.4寸 TFT 屏 | 1 | 信息显示 | ¥30 |
| 轻触按键 | 1 | 唤醒/手动刷新 | ¥1 |
| 蜂鸣器模块(带三极管驱动) | 1 | 到站提醒 | ¥2 |
| 面包板+杜邦线若干 | 1套 | 电路连接 | ¥5 |
| USB 数据线 | 1 | 供电 + 烧录 | ¥3 |
| 亚克力透明支架/小盒子 | 1 | 桌面固定 | ¥10 |
| 合计 | ¥71(控制在 75 元内) |
接线与结构
| 传感器/模块 | ESP32 引脚 |
|---|---|
| TFT SCK | GPIO18 |
| TFT MOSI | GPIO23 |
| TFT CS | GPIO15 |
| TFT DC | GPIO2 |
| TFT RST | 接 3.3V |
| TFT 背光 | 接 3.3V(常亮) |
| 按键 | GPIO0(板载 Boot 按键复用,轻触即可) |
| 蜂鸣器 | GPIO4(PWM 输出) |
按键直接复用 GPIO0 是有意为之:ESP32 在深度睡眠时,GPIO0 可作为 EXT1 唤醒源,且板载按键无需重新焊接,按下即进入 ESP32 的下载/唤醒模式,省去额外布线。蜂鸣器接 GPIO4 通过 PWM 输出不同频率提示音。TFT 的 RST 直接拉高,减少 GPIO 占用。整体结构为:屏幕背面用双面胶固定到亚克力支架,ESP32 与面包板藏在底座内,按键露出底座边缘,方便单手点按。
核心代码
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <TFT_eSPI.h>
const char* ssid = "your_wifi";
const char* password = "your_pass";
const char* apiUrl = "http://your_bus_api?line=127&station;=xx";
TFT_eSPI tft = TFT_eSPI();
void drawBusInfo(int minutes, bool isArriving) {
tft.fillScreen(TFT_BLACK);
tft.setTextSize(2);
tft.setTextSize(4);
if (isArriving) {
} else {
tft.drawString(String(minutes) + " min", 20, 80);
}
}
void fetchAndDisplay() {
HTTPClient http;
http.begin(apiUrl);
int httpCode = http.GET();
if (httpCode == 200) {
String payload = http.getString();
DynamicJsonDocument doc(1024);
deserializeJson(doc, payload);
int minutes = doc["minutes"] | -1;
bool arriving = doc["arriving"] | false;
if (arriving) {
ledcWrite(0, 128); // GPIO4 PWM 蜂鸣器提醒
delay(200);
ledcWrite(0, 0);
}
} else {
tft.fillScreen(TFT_BLACK);
tft.setTextSize(2);
}
http.end();
}
void setup() {
tft.init();
tft.setRotation(1);
pinMode(4, OUTPUT);
ledcSetup(0, 5000, 8);
ledcAttachPin(4, 0);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) delay(100);
fetchAndDisplay();
esp_deep_sleep_start(); // 显示完成后进入睡眠
}
void loop() {
// 唤醒后在 setup 中执行完毕后会自动进入下一次睡眠
}
调试步骤
- 硬件摸底:先用示例代码跑通 TFT 屏幕显示,确认 SCK/MOSI/CS/DC 接线顺序正确,屏幕背光亮且无花屏。若花屏,检查 CS 是否接错或 DC 电压。
- WiFi 联网:单独烧录 WiFi 扫描程序,确认 ESP32 能连接路由器。在串口监视器观察 IP 获取情况。
- API 数据解析:先用电脑浏览器访问 apiUrl,确认返回 JSON 格式;然后在 Arduino 中通过
deserializeJson实验解析,串口打印 minutes 字段。 - 显示联调:将解析结果传给
drawBusInfo,观察不同数值下颜色变化是否符合预期。重点测试 0 分钟(进站)时的红色闪烁和蜂鸣器。 - 按键唤醒:烧录完整程序后断电。按一下 GPIO0 按键,观察是否在 2-3 秒内完成 WiFi 连接并刷新屏幕。若长时间无反应,检查
esp_sleep_enable_ext0_wakeup是否误接高电平。