从零制作 ESP32 触摸屏状态看板

ESP32 · ILI9341 · XPT2046 · SHT40 · Cloudflare Workers / D1 · Windows / Mac 采集器

整理日期:2026 年 10 月 28 日。本文按本次制作过程整理,功能范围截至温湿度趋势、远程显示设置完成并验收。

这是一块放在桌面上的小看板:上方看 Codex 剩余额度、重置时间和可用额度重置卡,中间看近 30 分钟 Token,下方看网站访问情况;接入 SHT40 后,还能显示温湿度和趋势。手机负责配网,电脑负责采集,Cloudflare 负责汇总和网页管理,ESP32 负责显示与触摸交互。

阅读与隐私说明:所有域名、账号名称、额度、温湿度和画面中的数据均为示例;设备序列号、MAC、真实 Wi-Fi、密码、令牌和个人环境照片均未收录。成品摆放图为建议方案,网页图为功能结构示意,均不代表实拍。教程 ZIP 不包含项目源码、固件、采集器或私人配置。

建议成品布局:屏幕在左、开发板在右、传感器远离热源

1. 做完后能得到什么

部分完成后的能力
屏幕中文看板、四种配色、四种布局、额度和网站详情、触摸翻页
配网手机填写 2.4 GHz Wi-Fi、HTTPS 接口地址和设备令牌,成功后保存
Codex按实际返回的额度窗口显示剩余比例、重置时间、重置卡数量与可取得的到期信息
Token显示已接入设备近 30 分钟的用量,区分完整、部分和不可用
网站云端每分钟检查;网页可调整监控列表,板端随摘要更新
环境SHT40 每 5 秒本地采样;屏幕看近 1 小时趋势,网页看最多 7 天历史
管理网页修改配色、布局、自动翻页和刷新间隔,显示设备的真实应用结果
运行开机自动运行;Windows / Mac 登录后后台采集,每 60 秒上报

这次制作的顺序是:先确认串口和备份固件,再点亮屏幕、设置开机自启和手机配网;随后接通 Cloudflare 与电脑采集器,最后加入触摸、温湿度、趋势和远程设置。按这个顺序复刻,每一步都能单独判断是否成功。

复刻前要准备两类文件:一类是本文和配图,用于理解与接线;另一类是与本文对应的完整 ESP 项目源码、成品固件及采集器发行包。后者应从项目维护者取得。本文没有假定存在公开下载仓库,也没有把维护者本机的私人文件作为公共依赖。只有通用 MicroPython、最小测试代码的部分可以脱离整套成品独立完成。

命令默认在 macOS / Linux 的项目根目录 ESP/ 执行;Windows 的采集器安装命令另行标注。示例串口名必须替换成自己的实际串口。

阅读路线:第一次搭建按第 2~11 节顺序操作;已有成品的用户可直接看第 12~17 节;遇到故障先看第 18 节,再按现象回到对应测试阶段。

2. 物料怎么选

物料数量选购要点
经典 ESP32 开发板1本项目为 ESP32-D0WD-V3、4 MiB Flash;接口与板载 USB 转串口应可用
ILI9341 SPI 彩屏1本项目为 2.8 英寸、240×320、14 针模块,带 XPT2046 电阻触摸
SHT40 模块1选择已焊接排针、可查供电和 VCC/GND/SDA/SCL 定义的模块
400 孔面包板1~2单块可以做原型,但宽 ESP32 容易遮住取线孔;完整装配建议两块,或选 830 孔板
公母杜邦线一组连接排针与面包板;建议准备 20~30 根短线,留出分线余量
公公跳线 / 面包板硬跳线一组用于同一信号分线、供电与跨槽连接
USB 数据线1必须支持数据,不能只有充电功能
USB 电源1稳定的 5 V 电源,供电能力和线材质量都要合适
亚克力底板、螺柱、螺丝、脚垫可选最终固定时再按孔位和走线空间选尺寸
万用表建议排查面包板连通、供电和接触问题时很有帮助

不要只按“2.8 寸屏”购买。并口屏、无触摸屏、不同控制器和不同供电电路可能外观相似,但不能直接套用本教程接线。DHT11 曾作为候选讨论,实际成品采用的是 SHT40。

400 孔面包板够不够,取决于开发板宽度和屏幕摆放。屏幕适合单独用螺柱固定在底板上,排针只负责电气连接;ESP32 插在面包板上,USB 口朝外,SHT40 放在通风处。不要让较重的屏幕长期靠排针悬挑。

面包板背面的双面胶用于最终粘贴固定。先保持离型纸不撕,完成接线和测试后再定位;裸露焊点或排针底部应与导电表面隔开。亚克力方案属于后续整理建议,本次实物验证不以完成亚克力外壳为前提。

3. 先理解面包板和两个按钮

面包板同排五孔连通,中槽两边独立

普通面包板上,同一编号的 a~e 五孔相通,f~j 五孔也相通,但两组之间隔着中槽。相邻编号不相通。两侧红蓝电源轨可能中间断开,也不默认左右相通,应以实物测通结果为准。

因此,没有一分二杜邦线也能分线:将 ESP32 的一个 GPIO 接到某一组连通孔,再从该组其他孔分别引向屏幕和触摸。只能分出同一信号,不能把两个不同 GPIO 插在同一组连通孔里。

开发板上的 D18 指 GPIO18,不是第 18 个排针,也不是面包板第 18 行。面包板行号会随重新布局而变化,教程以模块丝印与 GPIO 为准。

按钮作用日常使用
EN / RESET重启 ESP32,保留已保存配置需要重启或重新开放开机配网窗口时短按
BOOT配合复位进入下载模式自动下载正常时不用按;不等同于“清除 Wi-Fi”按钮

自动烧录连不上时,才按工具提示尝试按住 BOOT、短按 EN,连接成功后松开 BOOT。误入下载模式时,松开 BOOT 后短按 EN。按键位置以板上丝印为准,不凭左右方向记忆。

4. 屏幕、触摸和 SHT40 的完整接线

4.1 先确认屏幕供电条件

本项目红色屏幕背板有 U1 稳压器和 J1 跳线位置。与其布局相符的 LCDWiki MSP2807 参考原理图规定:

屏幕模块条件屏幕 VCC 接法
确认具有对应的板载 3.3 V 稳压电路,且 J1 开路接 ESP32 的 VIN / 5V;ESP32 此时由 USB 供电
对应电路的 J1 已桥接接 3V3
模块电路或 J1 状态不明确先查商家资料或确认电路,再通电

这个条件不能推广到所有红色模块。VIN 名称也不保证每块开发板在 USB 供电时都能提供相同输出,要核对自己的开发板。

屏幕 LED、SHT40 和所有 ESP32 GPIO 均使用 3.3 V。5 V 只用于上表确认适用的屏幕 VCC。 每次改线先拔 USB;初次下载固件可先不接外设。

4.2 先点亮屏幕:8 根线

先跳过触摸,最容易缩小问题范围。

屏幕丝印ESP32说明
VCCVIN / 5V,或按上节改接 3V3必须先确认 J1 / 稳压电路
GNDGND共地
CSD27 / GPIO27显示片选
RESETD25 / GPIO25显示复位
DCD26 / GPIO26命令 / 数据选择
SDI / MOSID23 / GPIO23ESP32 向屏幕发送数据
SCKD18 / GPIO18SPI 时钟
LED3V3本模块背光控制输入

这个阶段,SDO/MISO、五根 T_… 触摸针和 SD 卡焊孔全部留空。屏幕能显示后再加触摸,不需要一开始就把所有线接完。

4.3 加入 XPT2046 触摸

触摸丝印ESP32与屏幕的关系
T_CLKD18 / GPIO18与屏幕 SCK 共用时钟
T_CSD32 / GPIO32独立片选,不能与 LCD CS 短接
T_DIND23 / GPIO23与屏幕 SDI/MOSI 共用发送数据
T_DOD19 / GPIO19触摸向 ESP32 返回数据
T_IRQD33 / GPIO33按下为低、松开为高,程序启用输入上拉

最终屏幕 14 针中连接 13 针,屏幕的 SDO/MISO 仍然空着。本驱动只写显示器,GPIO19 专供触摸 T_DO 使用,SDO 不接 GND。SD 卡接口本轮不用。

4.4 接入 SHT40:以最终实物验证为准

SHT40 丝印ESP32备注
VCC / +3V3与屏幕 LED 可共用 3V3 分线
GND / −GND与开发板、屏幕共地
SDAD22 / GPIO22本项目最终采用的 I²C 数据脚
SCLD21 / GPIO21本项目最终采用的 I²C 时钟脚

这里特别容易接反:网上常见 ESP32 示例使用 SDA21/SCL22;本项目最终实物验证的是 SDA22/SCL21,软件已同步适配。不要一边按本文接线,一边使用另一套默认引脚代码。

带外壳模块必须确认排针顺序。相似外壳、商家通用示意图和杜邦线颜色都不能代替实际丝印。本次排查在打开外壳确认 VCC/GND/SDA/SCL 后才取得可靠读数。若不便拆壳,应让商家提供与所购型号一致、明确观察方向的引脚定义。

传感器放远离 ESP32、稳压器和屏幕背光的位置,保留空气流通,避免把自身发热误认为室温变化。Sensirion SHT40 产品资料可用于核对芯片接口与供电范围;零售模块的外围电路仍以实物资料为准。

4.5 哪几个位置需要一对多

GPIO18、GPIO23、3V3 和 GND 的分线关系

分线点引向哪里
D18屏幕 SCK、触摸 T_CLK
D23屏幕 SDI/MOSI、触摸 T_DIN
3V3屏幕 LED、SHT40 VCC;只有 J1 桥接供电方案才再接屏幕 VCC
GND屏幕 GND、SHT40 GND

显示和触摸共用 SPI(2),但速率不同:显示 10 MHz、触摸 500 kHz,均为 mode 0。驱动在切换速率前释放两路片选,避免一个外设干扰另一个。

5. 配置项目环境、识别芯片和备份

5.1 工具隔离在项目中

本次使用项目内 .venv/ 和 .cache/,未改全局 Python、Shell 配置或系统执行策略。已验证的工具组合是 esptool 4.12.0、mpremote 1.29.0、mpy-cross 1.29.0.post2,固件基于 MicroPython 1.29.0。复现时优先使用项目锁定版本,升级工具后重新验证。

若拿到的是源码而没有虚拟环境,在项目根目录创建环境,安装锁定依赖;这是开发机操作,不是 ESP32 上的命令:

python3 -m venv .venv
mkdir -p .cache/pip .cache/tmp
PIP_CONFIG_FILE=/dev/null PIP_CACHE_DIR="$PWD/.cache/pip" \
  .venv/bin/python -m pip install --index-url https://pypi.org/simple -r requirements.lock

./tools/python.sh -m pip check
./tools/python.sh -m esptool version
./tools/mpremote.sh --version

Python 版本须与锁定依赖兼容;本项目初始环境为 Python 3.9。tools/python.sh 固定使用项目解释器,tools/mpremote.sh 使用项目串口工具。不要为了复刻而修改全局 pip 源或全局安装权限。

5.2 找串口、确认芯片

关闭其他串口监视器,同一个端口只交给一个工具使用。

./tools/python.sh -c 'from serial.tools import list_ports; [print(p.device) for p in list_ports.comports() if p.vid is not None]'

把实际端口赋给变量;下面是占位名,不能原样使用:

PORT=/dev/cu.usbserial-DEMO
./tools/python.sh -m esptool --chip auto --port "$PORT" --baud 115200 flash_id

本项目识别到经典 ESP32-D0WD-V3、4 MiB Flash,自动复位正常。Windows 对应端口通常显示为 COMx;如果连串口都没有,先确认 USB 是数据线,再查接口、转串口驱动和端口占用。

5.3 先完整备份,再检查安全状态

以下备份命令适用于已经确认的 4 MiB 经典 ESP32。新建自己的备份目录,不覆盖既有原厂镜像。

mkdir -p backups/first-board
chmod 700 backups/first-board

./tools/python.sh -m esptool --chip esp32 --port "$PORT" --baud 115200 \
  read_flash 0x0 0x400000 backups/first-board/factory-4MiB.bin

./tools/python.sh -m esptool --chip esp32 --port "$PORT" --baud 115200 \
  verify_flash 0x0 backups/first-board/factory-4MiB.bin

shasum -a 256 backups/first-board/factory-4MiB.bin \
  > backups/first-board/factory-4MiB.sha256

./tools/python.sh -m espefuse --chip esp32 --port "$PORT" summary

备份文件应为 4,194,304 字节,设备比对必须成功。完整 Flash 可能包含原程序、Wi-Fi 和其他凭据,应保存在私人目录,不能随教程公开。espefuse summary 只读查看安全启动、Flash 加密和下载状态;不烧写 eFuse、不使用强制绕过参数。

只有确认芯片、容量、安全状态符合普通刷写路径后,才继续。启用了安全启动或 Flash 加密的板子,需要适配原有安全配置的升级方案,不能照搬普通擦除流程。本项目初始板未启用这些功能。

本次曾遇到高波特率读取异常,降低到 115200 后取得并验证完整备份。备份慢几分钟可以接受;本地生成了 SHA-256,并不等于已经与板上内容比对,两个检查各有用途。

6. 首次安装 MicroPython,与日后升级分开

6.1 首次刷入官方通用固件

从 MicroPython ESP32_GENERIC 官方下载页取得与经典 ESP32 匹配的固件。不要把 ESP32-S3、C3 或其他板型镜像混用;本项目没有依赖额外 PSRAM。

以下擦除会替换原程序及板上文件。 仅在上一节备份、比对和安全检查完成后用于首次初始化。把下载文件命名为示例路径,先核对实际来源与版本:

./tools/python.sh -m esptool --chip esp32 image_info firmware/ESP32_GENERIC.bin

./tools/python.sh -m esptool --chip esp32 --port "$PORT" erase_flash

./tools/python.sh -m esptool --chip esp32 --port "$PORT" --baud 115200 \
  write_flash 0x1000 firmware/ESP32_GENERIC.bin

./tools/python.sh -m esptool --chip esp32 --port "$PORT" --baud 115200 \
  verify_flash 0x1000 firmware/ESP32_GENERIC.bin

官方通用合并镜像在本板的起始地址是 0x1000。这与后面的完整备份、应用单独升级地址不同。

6.2 运行第一个串口测试

源码中已有不操作外设的 examples/hello.py:

./tools/mpremote.sh connect "$PORT" run examples/hello.py

看到运行时信息和连续心跳,说明 USB 串口、下载、MicroPython 运行链路基本可用。这个命令在 RAM 中运行脚本,不等于把它设成开机自启。需要交互时使用:

./tools/mpremote.sh connect "$PORT" repl

按 Ctrl-] 退出。维护工具会中断正在运行的应用,操作结束后短按 EN,恢复正常启动。

6.3 三种镜像不能混用地址

内容本项目对应地址使用条件
官方 ESP32_GENERIC 合并安装镜像0x1000首次安装,按官方说明执行
本项目冻结版应用镜像0x10000只适用于已核对的相同分区布局和该应用产物
从 0x0 读取的 4 MiB 完整备份0x0恢复完整 Flash,会覆盖当前系统与文件

成品日常升级不再执行整片擦除。 本项目后期采用冻结应用,把代码放入固件,保留文件系统中的配网、令牌、触摸校准和界面设置。应用区升级需要核对发布清单、分区边界、SHA-256,写后回读,并确认受保护文件仍一致。不能仅凭扩展名 .bin 推断地址。

7. 点亮屏幕、校准触摸、设置自启

7.1 手动显示测试

先按 8 线方案接屏幕,再运行源码中的显示测试:

./tools/mpremote.sh connect "$PORT" mount peripherals run peripherals/display_test.py

应依次出现纯色,再显示三色色条、白色边框、对角线和 TL/TR/BL/BR 四角文字。此处要看实物;串口显示“写入完成”不能证明屏幕已经响应。

mount peripherals 只把外设子目录临时挂载给板上测试,避免挂载整个项目及私人备份。挂载运行不会安装程序;拔掉电脑后,主机上的脚本不会自动跟着运行。

7.2 原始触摸、校准和画点

补齐五根触摸线后依次运行,每项结束再做下一项:

./tools/mpremote.sh connect "$PORT" mount peripherals run peripherals/touch_raw_test.py

轻压、滑动、松开,应能取得随位置变化的原始坐标。电阻屏需要轻微压力,使用圆润塑料笔尖,不要用尖锐金属。原始轴互换或反向可以由校准修正,但完全没有有效采样不能靠校准解决。

./tools/mpremote.sh connect "$PORT" mount peripherals run peripherals/touch_calibrate.py

按提示完成 3 个校准点,再完成 2 个独立检查点。每点按住约半秒并松开,等待目标换位。两个检查点都通过后才保存,因此总共看到五个十字,但不是五点拟合。

./tools/mpremote.sh connect "$PORT" mount peripherals run peripherals/touch_draw_test.py

在中央与四边划线,绿色点应跟随笔尖,松手后停止新增。本次实物通过了这个检查。

校准工具首先把文件写到主机的 peripherals/touch-calibration.json。正式运行还须安装到板上的 /touch-calibration.json:先备份旧文件,上传暂存文件,验证哈希、屏幕宽高与旋转,再原子替换,重启后复核。不要把另一个人的校准系数当作通用值。更改屏幕方向或更换面板后应重新校准。

7.3 开机自启与成品安装

MicroPython 正常启动会执行板上根目录的 /main.py。早期我们用它自动显示色条;看板完成后,已关闭每次开机色条自检,改为直接进入配网和看板。诊断测试保留为手动入口。

对于刚刷好的空白板,可先做一个仅显示测试图的自启练习。先在主机新建 examples/main_display_demo.py:

from display_test import main
main()

在尚无 /lib/ 的空白板上创建目录,再安装显示依赖与入口;已有该目录时跳过创建,不要删除重建:

./tools/mpremote.sh connect "$PORT" fs mkdir :/lib
for module in board board_config ili9341 spi_bus; do
  ./tools/mpremote.sh connect "$PORT" fs cp "peripherals/lib/$module.py" ":/lib/$module.py"
done
./tools/mpremote.sh connect "$PORT" fs cp peripherals/display_test.py :/lib/display_test.py
./tools/mpremote.sh connect "$PORT" fs cp examples/main_display_demo.py :/main.py

重启后不连主机挂载也能显示,才算持久安装成功。该练习只说明自启原理,不适合覆盖已有成品的入口。

完整成品还需要字体、HTTPS 信任材料、触摸校准、网络与看板模块。本项目最终使用与 MicroPython 版本匹配的冻结版固件,其入口源码为 peripherals/main.py。复刻完整成品应取得对应发布清单和安装流程,不能只把这一份入口复制到空板,就期待所有功能出现。

已运行冻结版时,往 /lib/ 复制同名 .mpy 也不一定覆盖冻结模块;应按该版本的加载顺序和升级方式操作,避免源码、字节码和固件混用。

8. 手机配置 Wi-Fi 和接口地址

正常开机显示网络状态,设备会开放配置热点。最新版本也可从触摸菜单“设置 → 手机配网”重新开放热点,换网络环境时不需要拆线。

  1. 手机连接屏幕显示的 ESP-Setup-XXXXXX,密码使用屏幕 KEY,不要把它当作家中 Wi-Fi 密码。
  2. 提示“无互联网”时继续连接,在浏览器地址栏输入 http://192.168.4.1。没有自动弹窗也能手动打开。
  3. 扫描或输入自己的 2.4 GHz Wi-Fi,填写密码。不要把 5 GHz 网络当成这块经典 ESP32 的可用目标。
  4. 填写完整摘要地址,例如 https://api.example.com/api/v1/dashboard。云端未部署时可先留空,先验证 Wi-Fi。
  5. 在手机本地填写这块开发板专用的设备令牌。
  6. 点击“保存并连接”,等页面明确显示连接并保存成功,屏幕出现 CONNECTED / IP 后,再切回日常 Wi-Fi。

试连成功才保存,错误密码或超时不会覆盖原先可用配置。设备试连可能切换无线频道,手机临时离开热点时需重新连接。

对于已有配置,同一接口地址下令牌留空会保留原值;换接口地址而令牌留空会清除原令牌,因此迁移服务时要填写新服务的设备凭据。带令牌的公网接口必须用 HTTPS,令牌不放在 URL 查询参数里。

连接成功后的配置热点会按窗口关闭;错过窗口可短按 EN,或从触摸菜单重新开启。旧网络找不到时也可重新配网。BOOT 不负责清除网络。

Wi-Fi 在线 只说明接入了无线网络;还要取得合法业务摘要,才能认为接口正常。页面显示“地址已保存”也不代表云端鉴权、数据库或采集已经通过。

9. 云端架构:让 ESP32 只取小摘要

电脑采集、Cloudflare 汇总、ESP32 显示的完整数据流

最初考虑过 VPS,最后采用 Cloudflare Workers + D1 + 静态网页。它把几类工作分开:

角色主要职责
Windows / Mac 采集器使用当前用户的 Codex 登录状态,读取额度与 Token 元数据并上报
Worker鉴权、去重、选择有效来源、检查网站、生成小摘要、处理设置与环境数据
D1保存最新状态、网站配置、版本、温湿度历史等
登录网页展示详细数据、修改网站列表、查看趋势和远程设置
ESP32拉取摘要、显示、触摸、读取传感器、上传读数和设置回执

板端摘要限制在 4 KiB 内,避免把网页历史、完整重置卡明细或大型响应直接塞进开发板。人看的网页可以有更多信息,设备用的 API 保持紧凑。

这块板的 4 MiB 是 Flash,不是可以随意分配的运行内存。完整 240×320 RGB565 帧缓冲就要 240×320×2 = 153,600 字节,约 150 KiB,还没算 TLS、Wi-Fi、Python 对象和字体。本项目使用分块绘制、位图字库和冻结模块,HTTPS 请求串行执行,避免同时申请多份大缓冲。

本地 60 点趋势使用定长数组,数据区约 480 字节;这不包含对象和绘图等额外开销。动态数字、翻页、小曲线能运行,连续全屏动画、大图和多路并行 TLS 则会明显提高内存压力。

三种凭据各管各的事

凭据放在哪里用来做什么
采集器令牌Windows / Mac 本机私人配置上报该设备的 Codex 数据
ESP32 设备令牌手机配网输入,板上配置保存读取摘要;显式授权后可上传自身环境数据和设置回执
网页用户名 / 密码本人密码管理器或本地私人文件登录人类使用的管理网页

Cloudflare 的 Wrangler 登录授权属于部署权限,与上述业务凭据不同。屏幕不保存 Cloudflare API Token,也不保存 Codex 登录凭据。历史文件里的 reader / “只读令牌”命名表示设备角色;环境写入、设置回执需要额外明确授权,不会因此获得任意管理权限。

10. 在自己的 Cloudflare 部署网页和 API

这一章操作的是复刻者自己的账号和服务。需要完整项目中的 cloudflare/、tools/ 和相关发行文件;命令不是只有本 Markdown 就能独立执行。不要复制原作者的账户 ID、域名配置、OAuth 缓存或 private/。

10.1 准备账号、域名和本地工具

准备一个自己控制、已接入 Cloudflare 的域名,例如以 example.com 表示它;这只是保留示例域名,实际操作必须替换成自己的。

建议拆成两个入口:

示例入口用途
https://dashboard.example.com/登录网页,手机和电脑浏览
https://api.example.com/ESP32 和桌面采集器使用的稳定 API

两者可以路由到同一个 Worker。修改网页布局或入口时,设备 API 地址可以保持稳定。

在项目根目录执行;tools/npm.sh 会自动进入 cloudflare/,将 npm 和 Wrangler 缓存放在项目中:

./tools/npm.sh ci
./tools/npm.sh exec -- wrangler login

按官方 OAuth 流程在正常浏览器中登录并授权。内置浏览器打不开时,可以使用已登录的 Chrome 完成官方授权,无需提取或复制浏览器 Cookie。缺少 Workers Scripts 等必要权限时,在官方授权页补齐。

10.2 创建 D1 并修改自己的部署配置

./tools/npm.sh exec -- wrangler d1 create esp-status-board

将返回的数据库标识填入自己项目的 cloudflare/wrangler.jsonc。配置重点如下;它展示本教程所需内容,项目若还有其他功能配置应按自己的需求保留。

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "esp-status-board",
  "account_id": "YOUR_ACCOUNT_ID",
  "main": "src/worker.mjs",
  "compatibility_date": "2026-07-01",
  "workers_dev": false,
  "preview_urls": false,
  "assets": {
    "directory": "./public",
    "binding": "ASSETS",
    "run_worker_first": true
  },
  "triggers": { "crons": ["* * * * *"] },
  "routes": [
    { "pattern": "dashboard.example.com", "custom_domain": true },
    { "pattern": "api.example.com", "custom_domain": true }
  ],
  "d1_databases": [{
    "binding": "DB",
    "database_name": "esp-status-board",
    "database_id": "YOUR_D1_DATABASE_ID",
    "migrations_dir": "migrations"
  }],
  "vars": {
    "BOARD_CONFIG": "{\"selected_account\":\"personal\",\"selected_bucket\":\"codex\",\"sites\":[]}"
  }
}

personal 是逻辑账号名,不是登录邮箱。codex 是本项目选定的额度桶标识,应与实际采集和服务配置一致,不能为了让屏幕出现数字而静默换成别的桶。

自定义域名要使用自己账号中有效的域名区域;先处理同名冲突记录。可通过 Wrangler 的 custom_domain 配置或控制台的 Domains & Routes 添加,按 Cloudflare Custom Domains 文档核对要求。

接手已有项目时,还要检查源码中的域名重定向与生成配置文件的默认地址。例如网页迁移跳转、设备初始化工具的默认端点都应改为自己的入口,不能只改 DNS。

10.3 生成独立业务凭据

node tools/init-dashboard-credentials.mjs init
node tools/init-web-dashboard-access.mjs

工具使用随机值生成新安装的凭据,已有凭据则保留,不应靠反复删除文件来“修复”连接问题。

本地文件含义与处理
private/credentials.json原始设备 / 采集器令牌,严禁公开
private/auth-config.json服务端的令牌哈希及绑定,也按私人配置保管
private/worker-secrets.json提交 AUTH_CONFIG 的 Secret 文件
private/web-dashboard-credentials.json网页原始凭据和会话材料,严禁公开
private/web-dashboard-secret.json网页密码派生值和会话 Secret
private/web-dashboard-access.txt本人查看网页登录信息的文件,严禁公开

初始化后必须在本机编辑器把 credentials.json 的 endpoint 改成自己的 API 根地址,例如 https://api.example.com,然后重新执行 init 更新派生文件。当前项目工具保留了原部署的默认地址,复制到新账号时不能沿用。网页访问说明中的地址也应改为自己的网页入口。不要将含真实值的文件内容粘贴到命令行、聊天或教程。

原始业务令牌保留在对应客户端;服务端 AUTH_CONFIG 只保存其哈希和权限绑定。网页使用独立密码派生值与会话密钥,不把业务令牌嵌入公开 JavaScript。

10.4 迁移、部署并配置 Secret

先执行测试与构建预览,再迁移自己的数据库:

./tools/npm.sh test
./tools/npm.sh exec -- wrangler deploy --dry-run --outdir ../.cache/worker-build
./tools/npm.sh exec -- wrangler d1 migrations apply esp-status-board --remote

首次服务需要先存在 Worker 才能上传 Secret,可先部署一次,再提交 Secret 并确认部署结果;缺少认证配置时业务接口应拒绝服务,不能临时去掉鉴权:

./tools/npm.sh exec -- wrangler deploy
./tools/npm.sh exec -- wrangler secret bulk ../private/worker-secrets.json
./tools/npm.sh exec -- wrangler secret bulk ../private/web-dashboard-secret.json

后续只改代码时继续使用正常部署流程;新增表时先执行对应迁移。不要删除 D1 来处理上报异常。D1 入门、数据库迁移和 Workers Secrets可用于核对平台步骤。

10.5 打开环境上传和远程设置权限

对于沿用本项目默认逻辑绑定 esp32 / personal 的安装,已有工具会先迁移、部署,再为这一设备启用环境写入与设置回执能力:

./tools/python.sh tools/deploy-sht40.py --settings

它复用已有令牌,不重新生成;迁移失败则停止,不升级套餐。若修改了设备 ID、逻辑账号或服务名称,先适配工具与配置,再运行,不能将此命令当成任意安装的一键脚本。手工配置时对应能力字段为 environment_write: true 和 settings_sync: true,仅授予需要的设备。

10.6 设置网站监控与确认部署成功

Cron 使用 * * * * *,每分钟运行网站检查;新触发器传播可能需要时间。平台使用 UTC,规则应按 Cron Triggers 文档核对。

登录网页后,在“网站管理”添加自己的 HTTPS 首页或专用健康检查地址,设置名称、预期 HTTP 状态;需要业务级判断时再配置明确的 JSON 契约。当前最多管理 8 个站点。保存到 D1 后以数据库配置为准,初始化 BOARD_CONFIG.sites 不会不断覆盖网页保存的列表。

curl --fail --show-error https://api.example.com/healthz

/healthz 只检查服务存活。完整验收还包括:能网页登录;未授权业务请求被拒绝;采集器首次上报成功;网页能读到更新;ESP32 用自己的令牌获得摘要;网站检查时间前进;环境上传及设置回执成功。healthz=200 不代表 D1、认证、采集和板端都正常。

Cloudflare 的请求、读写和存储额度按实际套餐管理。温湿度历史每 5 分钟才存代表样本,但最新状态、心跳等仍有数据库开销,不能据此认为“每设备只写 288 次/天”。遇到配额限制应查看具体错误和用量,不自动开通付费服务。

10.7 主要接口速查

接口使用者用途
GET /api/v1/dashboardESP32小摘要与待应用的显示设置
POST /api/v1/collectors/codex桌面采集器额度、重置时间和重置卡
POST /api/v1/collectors/tokens桌面采集器设备 Token 元数据
POST /api/v1/device/environment授权的 ESP32温湿度与采样时间
POST /api/v1/device/settings/ack授权的 ESP32设置应用结果
POST /api/v1/web/session浏览器创建网页登录会话
GET /api/v1/web/devices已登录浏览器设备连接和环境状态
GET / PUT /api/v1/web/sites已登录浏览器管理网站列表

所有公网业务访问使用 HTTPS。客户端令牌使用 Authorization 请求头;网页登录使用安全会话 Cookie。不要通过 URL 携带令牌,也不要将带真实认证信息的请求原文作为公开排障截图。

11. Windows / Mac 采集器:登录后后台运行

11.1 选择正确的包

电脑选择的架构
普通 Intel / AMD Windowswindows-amd64,也常称 x64
Windows ARMwindows-arm64
Apple Silicon Macdarwin-arm64
Intel Macdarwin-amd64

发行包包含采集程序、安装管理脚本和说明,不包含 Codex 程序、账号登录态、业务令牌或真实服务配置。仅做交叉编译不能代替目标系统实测。

维护者有匹配源码与 Go 环境时,可以在项目根目录构建四种发行包:

./tools/build-dashboard.sh collector

产物放在 dist/codex-collector/。构建本身不会替开发机注册后台任务。

11.2 Windows 第一次安装

  1. 在当前 Windows 用户下安装并登录官方 Codex CLI。
  2. 完整解压匹配架构的采集器 ZIP,不在压缩包预览窗口内运行。
  3. 双击 start.cmd,填写自己服务的上报地址,例如 https://api.example.com/api/v1/collectors/codex。
  4. 填写对应设备 ID、逻辑账号和专属上报令牌。本项目默认用 windows-main / personal;它们须与服务端绑定一致。
  5. Codex 路径先让向导查找;手动选择时必须是实际的 codex.exe,不能选择 npm 生成的 codex.cmd 或 codex.ps1。
  6. 等待首次读取、上传和后台运行检查成功,再确认安装完成。

专属令牌从自己服务生成的 private/credentials.json 对应 collector 条目取得,通过本地向导输入。不要使用 ESP32 的设备令牌、Cloudflare 部署令牌或其他账号的凭据。也不要让所有电脑共用一份设备身份。

安装器会注册当前用户的登录任务 ESP-Codex-Collector。可以在 Windows“任务计划程序 → 任务计划程序库”看到它,触发器为当前用户登录;无需再手动创建每分钟启动一次的任务。

程序与脚本安装在 %LOCALAPPDATA%\ESP-Codex-Collector\bin,私人配置保存在上一级目录。使用已经登录 Codex 的同一个普通用户即可,不以管理员运行作为默认解决办法。

11.3 查看状态、升级与停止

在完整解压的发行目录打开 PowerShell:

powershell -NoProfile -ExecutionPolicy Bypass -File .\status.ps1

额度部分 code 为 ok、last_upload_at 持续前进,才说明最近上报正常。Token 还要单独查看 token_usage 的状态及覆盖情况,不能用额度成功代替 Token 完整。

已有安装升级时,完整解压新版到新目录,使用原用户双击:

upgrade.cmd

升级器会保留原配置、令牌与身份绑定,暂存新程序、备份旧版、停止旧实例,再验证新版首次读取和上传。失败时按升级器结果处理恢复,不直接覆盖正在运行的 EXE。start.cmd 是首次安装 / 启动入口,不能代替已有安装的升级入口。

其他常用操作:

# 停止当前实例,保留登录自启配置
powershell -NoProfile -ExecutionPolicy Bypass -File .\stop.ps1

# 重新配置
powershell -NoProfile -ExecutionPolicy Bypass -File .\install.ps1 -Configure

# 卸载自身任务与程序;按脚本说明处理保留的私人配置
powershell -NoProfile -ExecutionPolicy Bypass -File .\uninstall.ps1

这里的 ExecutionPolicy Bypass 只影响这次 PowerShell 进程,不要求修改全局执行策略。若旧版出现 Set-Acl / SeSecurityPrivilege 错误,使用已修正的新版安装包;新版只设置所需目录访问权限,不申请审计特权。

11.4 每分钟上报,锁屏和睡眠有什么差别

当前发行版的周期固定为 60 秒:进程启动立即采集一次,之后由内部循环每 60 秒开始一轮;不会并发叠加多轮,当前没有向导中的自定义间隔选项。上传失败通常等下一轮重新采集并上传,不在本轮无限重试。

电脑状态能否继续采集
锁屏,但系统仍醒着、用户未退出可以
仅关闭显示器,系统仍醒着可以
睡眠、休眠、关机不可以
退出该 Windows 用户当前用户后台任务不持续运行
唤醒后读取当前状态,不补跑每一轮积压任务

因此屏幕显示“几十分钟前上报”时,要先检查采集电脑是否睡眠。不要把“登录任务”理解为无人登录时也持续运行的 Windows 系统服务。

11.5 Mac 作为偶尔使用的补充来源

Mac 对应入口为 start.command,状态为 status.command,停止为 stop.command,卸载为 uninstall.command。安装后通过用户 LaunchAgent local.esp.codex-collector 运行,同样依赖登录用户和系统清醒状态。

Windows 为优先来源,Mac 为后备来源:服务端选择新鲜且成功的数据;优先来源失效或超过配置有效期后,才使用新鲜后备数据。两台电脑必须确认为同一实际 Codex 账号才能绑定同一逻辑账号。额度不相加,账号身份改变也不自动合并。

12. 额度、Token 和“上报时间”如何正确显示

12.1 额度窗口由实际数据决定

本项目曾固定放置五小时和七天两个区域,后改成按真实返回窗口展示:

接口实际情况屏幕行为
只有七天窗口只显示七天额度,不占用五小时区域
返回两个窗口显示对应的两个窗口及各自重置时间
没有可用窗口 / 读取失败显示等待、未知或保留的最近有效值,并说明状态

没有返回五小时窗口,只能说明此次没有该窗口信息,不能据此写成“无限额度”。额度大字显示的是剩余百分比,不是剩余 Token 数量。后来按实际观看体验移除了进度条,把空间给数字、时间、Token 和网站。

重置卡数量使用实际返回的可用数量。没有取得数量时显示未知,不写成零;有数量但没有完整到期信息时,应明确“时间未提供”或只展示已知部分。到期时间到达后等待新数据确认,不在看板里自动消费卡或擅自扣减。

12.2 近 30 分钟 Token 的范围

采集器读取可确认账号归属的本机 token_count 元数据,不上传对话正文。服务端按事件时间汇总最近 1800 秒滚动窗口,并去重已接入设备上报的事件。

这表示“已接入设备、能读取到的记录”,不是整个账号所有客户端的实时账单。只安装 Windows 采集器,不会自动取得 Mac 上未上报的活动。

数值含义
输入 Token包含缓存输入
输出 Token包含推理 Token
缓存输入输入的子集
推理 Token输出的子集
总量输入 + 输出,不能把四项全部再相加

首次接入从身份绑定后的记录逐步建立覆盖,前 30 分钟可能标记“部分记录”;无法读取、未安装新版或无有效数据时显示等待 / 不可用。成功上传一个包,并不代表时间窗口已经完整。

如果对应版本还提供官方账号用量快照,应与设备日志明细分别理解,不能把账号汇总和设备明细相加,也不能用额度百分比变化倒算 Token。

12.3 三个时间要分开

时间含义
sampled_at采集器取得这份数据的时间
reported_at服务端收到当前保留成功样本的时间
server_time这一次服务端生成响应的时间

屏幕用成功样本的 reported_at 显示“X 分钟前上报”。即使 ESP32 刚刚成功请求接口,只要服务端仍返回旧样本,这个年龄也不能归零。重复提交和失败心跳也不能刷新最后成功样本的时间。

按本次选择,过期后保留最近额度数值,同时显示真实年龄和接口状态。这样能看见最后状态,也能判断数据是否值得信任。

13. 配色、布局和触摸如何融入看板

四种配色与布局,均为真实渲染器输出的演示数据

四种配色与四种布局可独立组合,图中只展示其中四种搭配:

配色布局更适合的场景
深海蓝 ocean分栏 split额度与重置卡分区明确
纸白绿 paper总览 ledger浅色、文字信息集中
炭黑琥珀 amber大字 focus抬眼看剩余额度
雾蓝灰 mist清单 list同时看更多网站

页面不再保留大块“状态看板”标题。顶部留给温湿度与重置卡,额度使用醒目的数字,中间放 Token,下方放网站;底栏用小字展示 Wi-Fi、API 与上报年龄。

触摸承担进一步查看和设置,不要求把所有信息挤在 240×320 的主页:

触摸位置 / 入口操作
额度区域查看额度、重置等详情
近 30 分钟 Token 区域查看输入、输出及统计覆盖等明细
网站相关页面选择站点,查看 HTTP 状态和延迟
上一页 / 下一页手动翻页
设置预览配色、布局并应用,修改自动翻页等
设置 → 手机配网重新开放本地配网入口
设置 → 温湿度趋势查看本地双曲线

触摸按下和松开需要可靠识别,避免长按期间重复触发。测试和正式运行应使用同一几何方向及校准文件。新布局不能只验证“画面画出来了”,还要验证点击区域与视觉位置一致。

14. 读取 SHT40,并让面板与网页一起显示

14.1 先用独立小程序确认真实通信

断电核对 SDA22/SCL21、3V3/GND,再连接电脑。本项目最终在 0x44 取得应答,序列号与测量数据的 CRC 校验均通过。扫描出地址只是第一步,不能代替有效测量。

下面是教学用的独立检查脚本,运行时会中断看板。新建 examples/sht40_check.py,写入以下内容;它不修改配网或校准文件:

from machine import I2C, Pin
import time


def crc8(data):
    crc = 0xFF
    for value in data:
        crc ^= value
        for _ in range(8):
            crc = ((crc << 1) ^ 0x31) & 0xFF if crc & 0x80 else (crc << 1) & 0xFF
    return crc


i2c = I2C(0, sda=Pin(22), scl=Pin(21), freq=100000, timeout=20000)
address = 0x44
addresses = i2c.scan()
print("I2C:", [hex(item) for item in addresses])
if address not in addresses:
    raise OSError("SHT40 0x44 not found; check power and wiring")

for _ in range(6):
    i2c.writeto(address, b"\xfd")
    time.sleep_ms(10)
    frame = i2c.readfrom(address, 6)
    if crc8(frame[:2]) != frame[2] or crc8(frame[3:5]) != frame[5]:
        raise ValueError("SHT40 CRC mismatch")

    raw_t = frame[0] * 256 + frame[1]
    raw_rh = frame[3] * 256 + frame[4]
    t100 = -4500 + (17500 * raw_t + 32767) // 65535
    rh100 = -600 + (12500 * raw_rh + 32767) // 65535
    rh100 = max(0, min(10000, rh100))
    print("T: %.2f C, RH: %.2f %%" % (t100 / 100, rh100 / 100))
    time.sleep(5)

运行命令:

./tools/mpremote.sh connect "$PORT" run examples/sht40_check.py

这是针对本项目接线和地址的小程序,不是对所有温湿度模块的通用驱动。不能取得应答时,不继续在未知供电下轮换大量 GPIO;先查物理接线。读数完成后短按 EN,恢复成品看板。

14.2 正式程序的采集与上传

正式程序使用 peripherals/lib/sht40.py,配置集中在 board_config.py:

SHT40_SDA = 22
SHT40_SCL = 21

正式驱动使用有界 I²C 操作、CRC 和数据校验,转换等待期间让出执行机会。每 5 秒读取,只有有效数据才刷新温湿度。断线、CRC 错误、总线超时分别记录状态,不把异常读数写成 0°C / 0%。

环节节奏与行为
本地测量每 5 秒,失败不覆盖上次有效值
本地过期30 秒没有有效样本时标记过期
环境上传目标每 30 秒一次,网络失败按策略退避
网页最新值显示采样时间与状态,超过对应新鲜期标记过期
上传范围当前读数,不建立离线历史补传队列

环境 POST 与看板 GET 复用一个 TLS 通道,各自有调度和退避,不同时握手。目标 30 秒不等于任何网络环境下都保证精确 30 秒到账。

屏幕左上显示当前读数;网页登录后在“设备连接”中查看设备温湿度与采样年龄。已有公开分享视图不新增这些私人环境数据。

15. 温湿度趋势:本地一小时、网页七天

主页面与近一小时双曲线,缺样处不连线

15.1 屏幕的本地趋势

点“设置 → 温湿度趋势”,可以看温度与湿度两张图、当前读数和样本情况;点返回回到看板。进入趋势页后仍持续采集。

本地每分钟保留第一个有效点,最多 60 点,放在 RAM 环形数组里:

  • 温度、湿度分别使用自己的纵轴,避免把不同单位混在同一比例上。
  • 某分钟没有有效样本就留空,不补零,也不跨缺口画出连续曲线。
  • 断电或重启后重新积累,本地一小时记录不写入 Flash。
  • Wi-Fi 掉线后仍能读传感器、看本地趋势;云端数据可能停止更新。

15.2 网页的历史趋势

网页登录后选择对应设备的“温湿度趋势”,支持近 1 小时、24 小时、7 天。云端每 5 分钟保存一个有效代表样本,保留 7 天;7 天视图按小时汇总,并标注有效样本数量。

视图数据密度
近 1 小时最多 12 个五分钟代表点
近 24 小时最多 288 个五分钟代表点
近 7 天最多 168 个小时聚合桶,取决于已有有效记录

它不是每 5 秒保存一次的完整原始曲线,也不会补造启用前或设备断网期间的历史。刚部署时只有少量数据是正常现象,等运行后逐步积累。

16. 远程管理:保存成功不等于设备已应用

远程配置从网页保存到设备真实回执的过程

ESP32 位于家中局域网,网页无需直接连入它。网页将期望设置保存在 Cloudflare;设备下一次主动拉取摘要时取得新版本,应用并上传回执。

可远程设置范围
配色深海蓝、纸白绿、炭黑琥珀、雾蓝灰
布局分栏、总览、大字、清单
网站自动翻页开 / 关
看板拉取间隔15 / 30 / 60 秒

这里的刷新间隔只改变正常状态下板端摘要拉取节奏,不改变 Windows 的 60 秒采集、SHT40 的 5 秒采样、环境上传目标或网站云端检查周期。

操作步骤:

  1. 登录网页,进入对应设备的“设备设置”。
  2. 修改配色、布局等,保存后先显示“待同步”。
  3. 等待设备拉到新版本;设备完成持久保存和显示应用后提交回执。
  4. 网页显示该版本“已应用”,再观察实物是否一致。

设备离线时保持待同步;保存或应用失败时保留原设置并反馈失败。不能在网页请求返回 200 时就显示“设备已应用”。

版本控制避免两处编辑互相覆盖;重复收到同一版本不会反复覆盖后来在屏幕上做的本地修改,网页再次保存新版本才会再下发。触摸预览期间也不会突然强行切换界面。回执带启动代次与序号,迟到结果不能覆盖较新的状态。

本轮远程管理的范围是显示设置和现有网站管理,没有实现任意远程命令、远程改 Wi-Fi 密码、远程改校准或 OTA 固件升级。网站列表保存在云端,设备读取更新后的摘要即可,不需要为改一个网站重新烧录。

17. 成品使用与展示

登录网页的功能结构示意:额度、网站、环境趋势与设备设置

这张图是为了说明网页的信息组织,使用重绘布局和演示数据,不是生产后台截图。屏幕配色图和趋势图则由本项目渲染器使用合成数据生成,保留了真实 240×320 排版。

建议最终摆放:屏幕靠左,ESP32 与面包板靠右,USB 向外;SHT40 放在远离热源、能接触室内空气的位置。电源线和 SPI 短线沿底板边缘整理,信号线不要被屏幕或螺柱压住。先电气验收,再粘贴面包板背胶、固定螺柱和底板。

日常抬眼看额度、Token、网站与温湿度;需要细节时触摸进入。外出换网络用手机配网;在电脑上维护网站和显示偏好。屏幕不承担 Codex 登录和大规模历史查询,复杂工作留在电脑与云端。

本次对话中已经确认的验收

阶段实际确认结果
串口与固件识别芯片、自动复位、原固件备份比对、MicroPython 心跳通过
显示色条、边框与四角文字正常;之后开机自启验证正常
手机配网用户完成连接并保存,屏幕显示 CONNECTED 和 IP
中文布局真实数据及中文正常,额度和网站新布局文字完整
触摸校准及画点跟随正常,松开后停止;主题切换和应用正常
SHT40拆壳核对引脚后取得有效 CRC 读数,用户确认温湿度与原看板正常
趋势与管理曲线、读数和返回正常;远程设置完成真实应用与恢复的往返验证

温湿度趋势版本在 2026 年 10 月 27 日的历史验收还包括:Python 与 Node 检查、字节码与原生镜像编译通过;正常运行观察有连续有效采样和多次摘要同步。带只读观察器的运行测试中,最低观测空闲堆约 66 KiB,这只是当时场景的测量值,不是可供任意新功能消耗的固定余量。

本节记录历史验收,不表示文档生成时重新对开发板或云服务执行了测试。本次整理没有刷写设备、修改生产配置或重新部署服务。

18. 故障排查:按现象缩小范围

18.1 屏幕白屏,但背光亮

白屏只说明背光和部分供电可能存在,不证明 SPI 初始化成功。按以下顺序检查:

  1. 断电核对屏幕 VCC、J1 和 GND;LED 亮不能证明屏幕逻辑供电正确。
  2. 核对 CS27 / RESET25 / DC26 / MOSI23 / SCK18,按丝印追到对应 GPIO。
  3. 检查面包板是否跨了中槽、错了一行,开发板排针下方是否接触可靠。
  4. 暂时断开新增模块,恢复已经验证过的最小显示接线。
  5. 运行手动显示测试;必要时降低 SPI 速率做对照,每次只改一个因素。

本次重新布局后即使拔下 SHT40,屏幕也曾持续白屏,因此不能单凭“刚加传感器”就认定它是原因。应分别验证显示链路和 I²C 链路。

18.2 触摸没反应,显示却正常

显示和触摸是两个片选不同的设备。检查 T_CS32 / T_DO19 / T_IRQ33,再检查 T_CLK18 / T_DIN23 的分线;确认 LCD CS 没有与 T_CS 短接。原始测试先要有按下、松开和坐标,再校准;不要直接用大量坐标映射常数掩盖无数据问题。

18.3 SHT40 扫不到或 CRC 错误

先核对实际丝印、观察方向、供电与共地,再核对 SDA22 / SCL21。壳上没有丝印时查匹配资料,不凭线色猜。

若怀疑模块把总线拉低,断电后断开模块再测试空载引脚,区分 ESP32 / 面包板 / 线材与模块。没有万用表时可以做受控连通验证,但必须由明确的输入模式、限流或供电检查方案指导,不能任意把电源和 GPIO 短接。无法确认供电时,借用万用表比继续猜线更有效。

有地址但 CRC 错误,要检查接触、线长和总线干扰;不能忽略 CRC 后把随机字节当温湿度。SHT40 接好不代表立刻有云端历史,先看本地有效测量,再看上传与历史。

18.4 Wi-Fi 在线,但“接口失联”

先把问题分成两段:电脑 → 云端,以及 ESP32 → 云端。

观察下一步
网页上的最后采集也在变旧检查电脑是否睡眠、采集器状态、Codex 登录和上传错误
网页采集持续更新,只有屏幕失联查板端 DNS、TCP、TLS、HTTP、鉴权与响应处理
/healthz 正常,业务仍失败查 Secret、D1、迁移、配额与具体业务错误
401 / 403检查对应角色令牌和权限,不靠高频重试解决
429 / 503查看限流或平台状态,遵守 Retry-After
网站仍在翻页、时间年龄继续增加主界面循环仍在运行,重点查网络任务和数据新鲜度

本次“接口失联但网站继续翻页”的现象,说明不能直接归因于整个主循环卡死。也不能只凭 1 A 充电头就认定供电故障。可保持位置、Wi-Fi 和 USB 线不变,仅切换供电,记录复现情况;多变量同时变化会失去判断依据。

最终网络方案使用分阶段超时、整轮截止、DNS 短缓存和备用解析、至多两个地址顺序尝试,以及独立退避。临时失败大致按 3、8、15、30 秒等待并加入少量抖动;持续失败维持退避,权限或证书问题采用较长等待。请求取消后先完成清理再启动下一轮,不堆积握手或不断重启整块板。

网络恢复后会再次同步,但不会把旧成功样本伪装成刚刚上报。

18.5 明明一直采样,却出现时间顺序冲突

这是本次长期测试遇到的一处有代表性的细节:经典 ESP32 的该 MicroPython 构建使用单精度浮点,直接把约十亿量级的 Unix 秒转换为浮点运算,会丢失秒级精度。不同采样可能得到相同时间,服务端因而拒绝乱序 / 冲突数据。

不应这样计算大时间戳:

# unix_now 是大整数,age 是浮点时,会引入大数的浮点舍入
sampled_at = int(unix_now - age)

本项目将小范围经过时间先处理成整数,再与整数 Unix 时间相减。示意如下:

age = mono_now - sampled_mono
age_seconds = int(age)
if age > age_seconds:
    age_seconds += 1
sampled_at = int(unix_now) - age_seconds

这里假定 unix_now 本来就是校验过的整数,且 age 非负。完整程序还要处理时钟回退、重启和错误状态。温湿度显示中的小数运算范围很小,并不等于所有浮点运算都不能使用。

18.6 网页修改后设备没有变化

先看“待同步 / 已应用 / 失败”,再查设备是否在线、是否拉取了新版本、是否有设置同步权限,以及是否完成了对应版本回执。查看触摸预览是否仍未退出。本地修改后同一云端版本不会反复覆盖,这是保留本地操作的设计;需要网页再次保存新版本。

19. 验收、维护和公开分享

19.1 从最小功能逐步验收

  • 物料型号、供电和 GPIO 定义已确认,USB 串口能识别。
  • 原固件完整备份、设备比对与安全状态检查完成。
  • 官方 MicroPython 和串口心跳通过。
  • 仅接屏幕时,颜色、四角和边框正常。
  • 触摸有原始数据,校准和画点正常,校准文件已持久安装。
  • 无需主机挂载,重启后自动进入配网 / 看板,日常不再跑色条自检。
  • 手机配网成功,错误配置不覆盖旧设置。
  • Cloudflare 认证、数据库、网站定时检查和业务接口逐项验证。
  • 采集器首次上传、登录后台运行、状态查询与升级验证通过。
  • 没有返回的额度窗口隐藏,未知值不变零,上报年龄真实。
  • SHT40 有连续 CRC 有效样本,面板和登录网页都能看到。
  • 本地趋势能进入和返回,网页历史逐步积累,缺样不补造。
  • 网页设置先待同步,设备应用回执后才显示已应用。
  • 最后再做固定、理线和更长时间的独立供电观察。

19.2 升级前保留哪些东西

内容为什么要留
完整 Flash 备份与 SHA-256出现升级问题时有可核对的原始恢复点
/config/network.json、/config/setup.jsonWi-Fi、接口和配置热点身份,包含私人信息
/touch-calibration.json这块面板的几何校准
/dashboard-ui.json本地显示偏好与已应用的远程版本
/dashboard-ui.json.generation设置回执使用的持久启动代次,不手工删改或跨设备覆盖
已安装版本与发布清单判断固件、字体、字节码和分区是否匹配
云端 D1 与本地 private/数据、身份、权限和客户端凭据分别备份

冻结版升级前做当前状态备份,升级后校验应用区及保留文件,不只保留很早的原厂备份。不要靠清空文件系统解决普通网络故障,也不要把配置 / 状态文件从另一块设备直接覆盖过来。

凭据轮换采用“增加新凭据 → 更新对应设备 → 验证成功 → 撤销旧凭据”的顺序。共享源代码时检查 .gitignore 并不够,打 ZIP 也必须使用明确文件清单,不能直接压缩整个工作目录。

19.3 教程公开前的隐私检查

  • 使用保留示例域名,移除真实主页、API 与后台地址。
  • 不公开 SSID、热点 KEY、Wi-Fi 密码、网页登录信息或任何令牌。
  • 不包含 Cloudflare account ID、D1 database ID、OAuth 缓存和私钥。
  • 实拍去掉设备 MAC / 二维码、电脑用户名、桌面通知、订单和其他个人环境信息;无法确认时使用重绘示意。
  • 额度、Token、重置卡和温湿度都使用明确的演示数据,不能把画面当作实时账号状态。
  • 排除 private/、backups/、配置快照、原始调试响应与含真实数据的报告。

本教程采用重绘接线 / 布局图和合成数据渲染,未收录原始个人照片;公开范围仅为这份 Markdown 与配套八张 PNG。

附录 A:项目文件地图

以下是完整项目中的主要入口,帮助找到代码;它们不是教程 ZIP 的额外附件。

ESP/
├── requirements.lock              # 项目 Python 依赖
├── examples/hello.py              # 最小串口测试
├── peripherals/
│   ├── main.py                    # 正式启动入口源码
│   ├── display_test.py            # 手动显示诊断
│   ├── touch_raw_test.py
│   ├── touch_calibrate.py
│   ├── touch_draw_test.py
│   ├── touch-calibration.json     # 本机生成,勿当通用标定
│   └── lib/
│       ├── board_config.py        # GPIO 与屏幕几何
│       ├── board.py / spi_bus.py  # 共用 SPI 与设备初始化
│       ├── ili9341.py / xpt2046.py
│       ├── calibration.py
│       ├── dashboard_model.py     # 状态和显示语义
│       ├── dashboard_async.py     # 异步 HTTPS 与超时
│       ├── dashboard_screen.py    # 绘制
│       ├── dashboard_ui.py        # 布局与页面状态
│       ├── sht40.py               # CRC 温湿度驱动
│       ├── environment_history.py # 本地趋势
│       └── device_settings.py     # 远程显示设置
├── dashboard/                    # Go 桌面采集器与安装器
│   └── packaging/
├── cloudflare/
│   ├── wrangler.jsonc             # 使用自己的部署配置
│   ├── src/worker.mjs             # API、鉴权和路由
│   ├── migrations/               # D1 版本迁移
│   └── public/                   # 登录网页资源
├── tools/                        # 隔离启动器、构建与部署工具
├── private/                      # 私人凭据,不分发
├── backups/                      # 私人备份,不分发
└── dist/                         # 发行产物

源码包含上游驱动时保留其许可证与来源信息。本项目显示驱动来自 rdagger/micropython-ili9341,项目内锁定了具体版本;不要用随手下载的最新版替换后跳过兼容性验证。

附录 B:八张配图与文件打包方式

文件用途
assets/finished-layout.png亚克力底板上的建议摆放,非成品实拍
assets/breadboard.png面包板连通关系
assets/shared-buses.pngSPI 与电源一对多分线
assets/architecture.png电脑、云端与设备数据流
assets/screen-themes.png四种配色 / 布局的演示渲染
assets/screen-trends.png本地温湿度趋势演示渲染
assets/remote-settings-flow.png设置下发与真实回执过程
assets/web-dashboard-demo.png网页功能结构示意

保存、转移或发布时,将 Markdown 与 assets/ 放在一起,保留相对位置:

ESP32看板教学文档/
├── ESP32触摸屏看板制作教程.md
└── assets/
    └── 八张配图.png

大多数 Markdown 编辑器会自动显示相对图片;发布到代码托管平台或博客时,连同图片目录上传。教程中的域名、串口和配置占位值需要替换后才能用于实际部署。

附录 C:官方资料与进一步阅读

以下外部页面用于核对平台和硬件步骤,平台界面及版本可能变化;项目已经验证的引脚与版本以本文对应源码和发布说明为准。

  1. MicroPython ESP32_GENERIC 固件下载与安装
  2. LCDWiki MSP2807 参考原理图:J1 与供电
  3. Sensirion SHT40 产品资料
  4. Cloudflare Workers 自定义域名
  5. Cloudflare D1 入门
  6. Cloudflare D1 迁移
  7. Cloudflare Workers Secrets
  8. Cloudflare Cron Triggers