💡 划重点

  • 用 ESP32 驱动 ILI9341 彩屏,通过 HTTP API 拉取公交实时到站信息。
  • 按键唤醒 + 定时唤醒,平时深度睡眠,出门前按一下就能看最近两班车。
  • 目标成本 75 元内,适合上班族放在玄关、办公桌或床头。
  • 显示线路、站名、到站分钟、下一班和更新时间,首班车 3 分钟内蜂鸣提醒。
  • 结构简单:屏幕 + 主控 + 按键 + 蜂鸣器,接线少,调试快。

需求洞察

目标用户是依赖公交通勤的上班族和学生。真实痛点很具体:早上出门前不知道车还有几分钟,频繁解锁手机查 App,容易分心;有时刚走到站台车刚走,有时等十几分钟不来。手机通知不可靠,智能音箱又太贵、太占地方。为什么值得做?因为公交实时数据已经公开,ESP32 联网成本很低,一块小屏就能把“查手机”变成“看一眼”。桌面播报器放在玄关,出门前按一下,屏幕直接显示最近两班车;放在办公桌,下班前按一下就知道要不要立刻走。它不替代手机,而是把关键信息从手机里“抠”出来,变成零操作、低干扰的桌面设备。

方案设计

感知层:GPIO0 按键唤醒,按下后从深度睡眠启动;同时支持 RTC 定时唤醒,每 60 秒自动刷新一次。Wi-Fi 联网后请求公交 HTTP API,获取线路、站名、到站分钟。

控制层:ESP32 解析 JSON,计算最近两班车,判断是否小于 3 分钟,决定是否蜂鸣。状态机管理“连接 Wi-Fi、请求 API、刷新屏幕、进入睡眠”四个状态,异常时显示错误码。

执行层:ILI9341 显示线路、站名、到站倒计时、下一班和更新时间;无源蜂鸣器在首班车小于 3 分钟时短鸣两声。深度睡眠时关闭背光,降低功耗。

日志/联网层:串口输出 Wi-Fi 状态、HTTP 状态码、JSON 解析结果;可选把刷新记录上报到 MQTT 或私有服务器,方便后续统计。整体用 3.3V 供电,USB 线接 5V 电源即可。

物料清单

物料 数量 用途 估算单价
ESP32 开发板 1 主控、Wi-Fi、深度睡眠 22 元
ILI9341 2.4 寸 SPI 屏 1 显示到站信息 28 元
轻触按键 1 GPIO0 唤醒 0.5 元
无源蜂鸣器 1 到站提醒 1.5 元
杜邦线 20 根 接线 5 元
面包板 1 原型搭建 8 元
USB 数据线 1 供电与烧录 5 元
外壳/亚克力 1 桌面摆放 5 元
合计 75 元

接线与结构

模块 引脚 ESP32 GPIO 说明
ILI9341 VCC 3.3V 供电
ILI9341 GND GND 共地
ILI9341 CS 15 片选
ILI9341 RESET 4 复位,微调新增
ILI9341 DC 2 数据/命令
ILI9341 MOSI 23 SPI 数据
ILI9341 SCK 18 SPI 时钟
ILI9341 LED 3.3V 背光常亮
按键 一端 GPIO0 另一端接 GND
蜂鸣器 正极 GPIO25 负极接 GND

GPIO0 是 RTC GPIO,可用于深度睡眠唤醒,但上电时不要按住,否则会进入下载模式。RST 微调到 GPIO4,是因为原建议未给复位脚,单独占用一个普通 IO 更稳定。

核心代码

以下为 Arduino 骨架,需安装 TFT_eSPI、ArduinoJson、HTTPClient。API 字段请按实际返回修改。


#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <TFT_eSPI.h>
#include <esp_sleep.h>

TFT_eSPI tft = TFT_eSPI();
const char* ssid = "YOUR_WIFI";
const char* password = "YOUR_PASS";
const char* apiUrl = "http://api.example.com/bus?station=xxx&line;=xxx";
#define BTN_PIN 0
#define BUZZER_PIN 25

void drawUI(String line, String station, int min1, int min2) {
  tft.fillScreen(TFT_BLACK);
  tft.setTextSize(2);
  tft.setTextSize(1);
  tft.setTextSize(3);
  tft.setTextSize(2);
}

void fetchBus() {
  if (WiFi.status() != WL_CONNECTED) return;
  HTTPClient http;
  http.begin(apiUrl);
  int code = http.GET();
  if (code == 200) {
    String payload = http.getString();
    StaticJsonDocument<1024> doc;
    deserializeJson(doc, payload);
    int min1 = doc["data"]["arrivals"][0]["minutes"];
    int min2 = doc["data"]["arrivals"][1]["minutes"];
    String line = doc["data"]["line"].as<String>();
    String station = doc["data"]["station"].as<String>();
    if (min1 <= 3) {
      tone(BUZZER_PIN, 2500, 200);
    }
  }
  http.end();
}

void setup() {
  Serial.begin(115200);
  pinMode(BUZZER_PIN, OUTPUT);
  tft.init();
  tft.setRotation(1);
  tft.fillScreen(TFT_BLACK);
  tft.setTextColor(TFT_WHITE);
  tft.setTextSize(2);
  WiFi.begin(ssid, password);
  unsigned long t0 = millis();
  while (WiFi.status() != WL_CONNECTED && millis() - t0 < 15000) delay(500);
  if (WiFi.status() == WL_CONNECTED) fetchBus();
  delay(30000);
  esp_sleep_enable_timer_wakeup(60ULL * 1000000ULL);
  esp_deep_sleep_start();
}

void loop() {}

调试步骤

  1. 屏幕测试:只接 ILI9341,运行 TFT_eSPI 示例,确认引脚、旋转方向和背光。
  2. Wi-Fi 测试:串口打印 IP,确认能连上热点。
  3. API 测试:浏览器打开接口,核对 JSON 字段,替换代码中的路径。
  4. 按键唤醒:测 GPIO0 按下拉低,深度睡眠后按一下能重新启动并刷新。
  5. 蜂鸣器:在首班车 3 分钟内触发,确认声音和时长合适。
  6. 整机联调:设置 60 秒定时唤醒,观察刷新是否稳定、屏幕是否闪屏。
  7. 封装:放入外壳,开屏幕窗和按键孔,背面留 USB 供电口。

设计图

公交到站桌面播报器结构flow
1
感知输入
→
2
控制处理
→
3
执行反馈
→
4
数据上报