💡 划重点

  • 主控:ESP32,利用 Wi‑Fi 定时请求公交实时 API,低功耗待机。
  • 屏幕:ILI9341 TFT,显示线路、剩余站数、预计到达时间。
  • 唤醒:按键轻触点亮屏幕,超时自动息屏。
  • 目标:上班族出门前快速确认公交位置,告别反复刷手机。
  • 成本:整机 75 元以内,单件焊接即可完成。

需求洞察

目标用户

住在公交站点附近、每天固定时段通勤的上班族。用户需在出门前判断需要加快脚步还是可以慢走,避免错过刚走的那班车或长时间在站台等候。

真实痛点

手机查询公交信息需要解锁、打开 App、找到收藏线路、加载数据,步骤繁琐;冬天不愿掏手机,雨天操作不便;长时间亮屏耗电,频繁查询造成心理慌乱。用户真正需要的是一个摆在桌面或玄关、一瞥即知的固定设备。

为什么值得做

硬件成本极低,功能聚焦,只解决一件事:告诉用户下一班车何时到。相比手机查询,物理按键唤醒+大字体显示体验更好,也更接近传统闹钟或温湿度计的位置。对于非智能家居用户,这仍是增量市场,可 DIY 满足个性化需求。

方案设计

感知层

控制层

1. 上电后连接 Wi‑Fi,获取 NTP 时间。

2. 进入 light sleep 或 deep sleep,按设定的间隔(例如 60 秒)自动唤醒获取公交数据。

3. 按键唤醒时点亮屏幕并立即请求一次最新数据,显示 30 秒后自动息屏。

4. 若电池供电则更多使用 deep sleep,仅在唤醒时联网。

执行层

日志/联网层

物料清单

序号 物料名称 数量 用途 估算单价(元)
1 ESP32‑WROOM‑32 开发板 1 主控与联网 18
2 2.4″ ILI9341 SPI TFT 模块 1 显示公交信息 28
3 轻触按键 1 唤醒屏幕 0.5
4 10 kΩ 电阻 1 按键上拉 0.1
5 面包板 + 跳线 1 原型验证 5
6 Micro‑USB 线 1 供电与烧录 3
7 3D 打印外壳或瓦楞纸壳 1 桌面摆放 5 (自打印估算)
8 排针排母 若干 连接 2
合计 71.6

*成本控制在 75 元内,若使用自带 USB 供电的 ESP32 开发板,可省略额外电源模块。*

接线与结构

接线方案(基于 ESP32 开发板)

ESP32 引脚 信号 连接至
3.3V 电源正 TFT VCC
GND 电源地 TFT GND, 按键另端
GPIO18 SCK TFT SCK
GPIO23 MOSI TFT MOSI
GPIO15 CS TFT CS
GPIO2 DC TFT DC
GPIO0 按键输入 按键一脚

按键接线:轻触按键一脚接 GPIO0,另一脚接 GND;GPIO0 内部已通过 10 kΩ 上拉至 3.3V(多数开发板),若开发板未自带,需外部加 10 kΩ 上拉电阻,故清单中添加电阻一项。

理由:使用 GPIO0 作为唤醒源,ESP32 在 deep sleep 时可由 GPIO0 低电平唤醒,且不与其他 SPI 功能冲突。SPI 引脚沿用标准 VSPI 默认映射,保证 ILI9341 库的直接兼容。

结构建议:将 ESP32 与 TFT 堆叠或平置在壳体内,按键从前面板露出。屏幕用透明亚克力保护。USB 线从后方引出供电,方便摆在玄关柜。

核心代码

以下给出 MicroPython 骨架(ESP32 更适合快速原型,支持轻量级 sleep)。实际开发需根据公交 API 格式调整 JSON 解析。


import ili9341
import network
import urequests as requests
import time
import ntptime

# 硬件初始化
spi = SPI(2, baudrate=40000000, sck=Pin(18), mosi=Pin(23), miso=Pin(19))
cs = Pin(15, Pin.OUT)
dc = Pin(2, Pin.OUT)
display = ili9341.ILI9341(spi, cs=cs, dc=dc)
button = Pin(0, Pin.IN, Pin.PULL_UP)

WIFI_SSID = 'your_ssid'
WIFI_PASS = 'your_password'
API_URL = 'http://your-bus-api.com/line?stopId=123'

def connect_wifi():
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    if not wlan.isconnected():
        for _ in range(20):
            if wlan.isconnected():
                break
            time.sleep(0.5)
    return wlan.isconnected()

def sync_time():
    try:
        ntptime.settime()
    except:
        pass

def fetch_bus_data():
    try:
        r = requests.get(API_URL, timeout=5)
        data = r.json()
        # 解析示例:目标线路最近的车辆预计到站分钟
        eta_min = data.get('nextBusMinutes', 99)
        return eta_min
    except:
        return -1

def show_info(minutes):
    display.fill(0)
    if minutes >= 0:
        msg = f'Next: {minutes} min'
    else:
        msg = 'No data'
    # 紧急提示变红
    if 0 < minutes <= 2:
        display.text('HURRY UP!', 20, 200, ili9341.color565(255,0,0))

def try_display():
    if not button.value():          # 按键按下为低电平
        if connect_wifi():
            sync_time()
            eta = fetch_bus_data()
            show_info(eta)
            time.sleep(30)          # 显示30秒
            display.fill(0)
        else:
            time.sleep(5)
            display.fill(0)

def main():
    # 检查唤醒原因,button 唤醒或定时器唤醒
    import esp32
    wake_reason = esp32.wake_reason()
    if wake_reason == esp32.EXT0_WAKE:   # GPIO0电平唤醒
        try_display()
    else:  # 首次上电或定时器唤醒
        if connect_wifi():
            sync_time()
            eta = fetch_bus_data()
            show_info(eta)
            time.sleep(30)
            display.fill(0)
    # 配置deep sleep,120秒后定时器唤醒,同时设置GPIO0为唤醒源
    esp32.wake_on_ext0(pin=Pin(0), level=esp32.WAKEUP_ANY_LOW)
    esp32.wake_on_timer(120000)
    deepsleep()

main()

*该骨架实现按键唤醒显示、定时自动获取数据后睡眠的逻辑,可直接烧录到 ESP32。*

调试步骤

  1. 分步上电测试:仅连接 TFT,运行示例代码确保屏幕正常显示色块和文字。
  2. Wi‑Fi 联网:单独测试网络连接,打印获取到的 IP。
  3. API 对接:在 REPL 中手动调用 API 并打印 JSON,确认数据字段与显示内容匹配。
  4. 按键检测:短按按键,观察屏幕是否如期亮起与熄灭。
  5. 低功耗验证:进入 deep sleep 后测量电流,确保 USB 供电或移动电源可长时间运行。
  6. 整机联调:将所有功能整合,放在卧室模拟出门场景,检查数据更新频率与显示准确性。

设计图

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