针对嵌入式硬件 ESP32-S3 的 AstroBox-NG 客户端
- 同时连接多台小米穿戴设备(最多 5 台,通过
CONFIG_BT_NIMBLE_MAX_CONNECTIONS配置) - 支持设备型号:
- 小米手环系列: Mi Band 10 Pro / 10 / 9 Pro / 9
- Redmi Watch 系列: Redmi Watch 6 / 5 / 5 eSIM
- 小米 Watch 系列: Xiaomi Watch S5 / S4 / S3
- 基于 FE95 服务 UUID 的通用匹配 + 设备名称关键字模糊匹配
- 按设备地址(MAC)稳定追踪,避免索引偏移导致的错误断开
- 单台设备断开仅重连该设备,其他设备保持连接
- 断连事件使用 mpsc 队列排队,多设备同时断连不会丢事件
- BLE 扫描结果按 MAC 去重,同一设备不会被记录数十次
- 快应用安装: 向设备推送
.pk格式快应用包(基于MassDataType::ThirdPartyApp) - 表盘安装: 向设备推送
.bin格式表盘文件(基于MassDataType::Watchface) - 安装进度: 通过 BLE Mass 协议分块传输,自带 MD5 校验和 CRC32 完整性验证
- 列表查询: 查询设备已安装的快应用列表和表盘列表
- 快应用管理: 启动、卸载快应用
- 表盘管理: 切换当前表盘、卸载表盘
- 电话消息桥接: 向设备上的快应用发送电话端消息
- 周期性(30秒)自动同步各设备的已安装项目状态
// 快应用
install::install_quick_app(addr, package_name, data).await?;
install::install_quick_app_from_file(addr, package_name, file_path).await?;
install::uninstall_quick_app(addr, package_name).await?;
install::launch_quick_app(addr, package_name).await?;
install::list_installed_quick_apps(addr).await?;
install::send_phone_message(addr, package_name, payload).await?;
// 表盘
install::install_watchface(addr, data).await?;
install::install_watchface_from_file(addr, file_path).await?;
install::uninstall_watchface(addr, watchface_id).await?;
install::set_watchface(addr, watchface_id).await?;
install::list_installed_watchfaces(addr).await?;- 快应用跨设备复制: 从设备 A 读取快应用包,安装到设备 B
- 表盘跨设备复制: 从设备 A 读取表盘数据,安装到设备 B
- 应用消息桥接: 将一个设备上的快应用消息转发到另一个设备的同名快应用
- 互联消息中继: 实时监听设备的互联消息事件,自动中继到目标设备
- 拒绝
src == dst,自带 FNV-1a payload 去重,不会形成 A↔B 转发死循环
- 拒绝
- 数据广播: 向所有已连接设备发送同一份数据
- 设备资源管理: 查询设备列表、获取设备名称
- 传输进度回调支持(Mass 文件分块传输)
// 文件/数据传输
transfer::send_data_to_device(addr, data_type, data).await?;
transfer::send_data_to_device_with_progress(addr, data_type, data, cb).await?;
transfer::broadcast_data_to_all_devices(data_type, data).await?;
// 内容复制
transfer::transfer_quick_app_between_devices(src, dst, pkg).await?;
transfer::transfer_watchface_between_devices(src, dst, face_id).await?;
// 消息桥接
transfer::forward_app_message(src, dst, pkg, payload).await?;
transfer::relay_interconnect_message(src, dst).await?; // 返回 JoinHandle
// 设备管理
transfer::list_connected_devices().await;
transfer::get_device_info(addr).await?;- Wi-Fi 重连看门狗:阻塞调用移至独立 OS 线程(
wifi-wd),不会冻结单线程 Tokio 运行时,因此重连期间 UI/触摸/BLE 保持响应 - Wi-Fi 凭据 NVS 持久化存储(SSID / 密码跨重启保存)
- Wi-Fi 初始化重试(最多 5 次,线性退避)
- BLE (MiWear) 自动重连,支持指数退避(5s → 10s → 20s → ... → 最长 120s)
- ST7789 240×320 方形屏幕显示(GC9A01 圆屏引脚兼容,可回切)
- CST816S 触摸支持(I²C 400kHz,INT 上拉,RST 引脚)
- 电量、充电状态(实时刷新)、网络速度实时监控
- 多设备连接计数 UI
- 安装资源面板:长按 ⚙ 设置按钮 600 ms 呼出安装资源面板,两个 Tab 可切换
本地 (SD) / AstroBox 官方源,5 行列表 + 上一页/下一页分页,点击行即触发安装,顶部显示实时进度文本。
- 滚动日志输出到 SD 卡:同时输出 USB 串口与
/sdcard/logs/astrobox_YYYYMMDD_NNNN.log;单文件 ≥ 512 KB 自动切分,总占用 > 4 MB 或 mtime > 7 天自动清理;连续写失败 ≥ 5 次自动禁用 SD 日志(仅 warn 一次,不 panic,不影响主流程)。 - 从 MicroSD 安装快应用 / 表盘:把安装包放入
/sdcard/astrobox/packages/,目录不存在会自动创建;支持扩展名:.rpk→ 快应用(自动尝试从 ZIP 包manifest.json取package_name,失败用文件名兜底).mwz/.face→ 表盘.bin→ 资源二进制(预留入口)
- 空闲空间保护:写缓存 / 安装前查询
statvfs,剩余 < 32 MB 警告,< 8 MB 拒绝写入,避免写坏 FAT 表。 - SNTP 时间:Wi-Fi 连接成功后 best-effort 同步一次
pool.ntp.org(sdkconfig 已开CONFIG_LWIP_SNTP_ENABLED);失败时日志文件名退化为epoch-秒数,不阻塞。
- AstroBox 官方源:直连
AstralSightStudios/AstroBox-Repo的index.csv(主源 GitHub Raw,失败自动回退 CDN jsDelivr),解析 CSV 列name, icon, cover, restype, tags, devices, path, paid_type。 - 关于「米坛社区(BandBBS)」源:此前曾计划"公开 HTML 抓取 + 关键词过滤",但 BandBBS 用户协议 / 服务条款明确禁止未授权的自动化抓取与爬虫访问,因此相关实现已从本仓库彻底移除(UI Tab、Rust 枚举、Rust 函数全部删除;无保留 stub,避免误用)。请不要在这个固件项目的 issue / PR 里提出接入米坛的需求。
- 三层付费过滤(合规,绝不泄露付费内容):
- 索引解析阶段:
paid_type ∈ {paid, force_paid, ¥xxx, VIP, …}立即丢弃(AstroBox 源执行,标题再叠加关键词过滤兜底); - AstroBox CSV 标题二次过滤:命中
付费 / 购买 / ¥ / 大会员 / paid / force_paid / price / money / vip关键词直接丢弃(避免paid_type列漏填); - 安装前
install_from_repo入口再检查一次item.paid.is_free(),付费条目直接拒绝,HTTP 不发起。
- 索引解析阶段:
- 设备型号过滤:若当前已通过 BLE 连接小米设备,按"设备名 → 型号 code"映射(Mi Band 9→n67、Redmi Watch 6→o72…)仅展示目标设备支持的资源;未连接时显示所有免费条目,方便离线浏览。
- 下载 → 缓存 → 安装 流水线:
- 下载占进度 0%–50%,安装占 50%–100%;
- 流式 chunk(4–8 KB buffer)写 SD 卡
/sdcard/astrobox/cache/<slug>_<version>.<ext>,不会一次性把 >4 MB 表盘装进 RAM,保护 PSRAM; - 命中缓存(文件存在且大小 ≥ manifest
filesize的 99%)时跳过 HTTP 直接安装; - 未插 SD 卡时 fallback 为内存下载(≤ 16 MB 上限)。
- HTTP 客户端:基于
embedded-svc::http::client::Client + EspHttpConnection + esp-tls,不关闭证书校验(sdkconfig 启用CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y);15s 超时、2 次指数退避重试、每次请求独立 OS 线程 + oneshot 回 async,绝不阻塞 Slint/Tokio 事件循环。
- 伪 ANCS (Apple Notification Center Service) BLE 服务(使用 KeyboardDisplay 配对能力)
- OTA 更新能力 stub(预留接口、日志为 debug 级别,生产环境不刷屏)
- ESP32-S3 PSRAM 优化
- Slint UI 渲染框架
本项目只针对 ESP32-S3 设计(使用了 Xtensa LX7 双核 + 八线 PSRAM + NimBLE 多连接)。其他 ESP32 型号(C3 / C6 / S2 / H2)不在支持范围内。
可以点亮固件并跑完整功能的下限:
| 项目 | 最低规格 | 说明 |
|---|---|---|
| MCU | ESP32-S3(双核 Xtensa LX7,240 MHz) | 不兼容 ESP32-C3 / C6 / H2 / S2 / 初代 ESP32 |
| Flash | 8 MB | factory 分区 0xE00000 ≈ 14 MB,低于 8 MB 必须改 partitions.csv |
| PSRAM | Quad 4 MB (40 MHz) | CONFIG_SPIRAM_MODE_OCT 若强行关闭可降级到 Quad,但 RAM 压力很大 |
| 显示屏驱动 | SPI RGB 屏 + mipidsi 支持的控制器 |
官方默认 ST7789(240×320),GC9A01 圆屏引脚兼容可回切 |
| 显示屏分辨率 | ≥ 240 × 320 | UI 按 3:4 矩形布局,240×240 圆屏需回切驱动 + Slint |
| 触摸屏 | 可选,无也可启动 | 不接 CST816S 时 UI 只显示,无法交互 |
| BLE | 必须(NimBLE,5 路并发连接) | |
| 编译环境 | esp-idf v5.3.3 + ldproxy + cmake + ninja | 详见 编译 章节 |
⚠️ 注意:如果选择 8 MB Flash / Quad PSRAM 的组合,建议:
- 在
partitions.csv中把factory减小到约0x600000(6 MB);- 把
sdkconfig.defaults里CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y改为对应值;save_image_rel.sh里--flash-size 16mb也要同步改掉。
按照本仓库开箱即用的硬件配置,推荐直接买以下模块和屏幕:
| 项目 | 推荐型号 / 参数 | 备注 |
|---|---|---|
| SoC 模组 | ESP32-S3-WROOM-1-N16R8V(16 MB Flash + 8 MB Octal PSRAM, V 版本) | 与 sdkconfig.defaults 一一对应,零改动 |
| 屏幕 | ST7789 240×320 方形 IPS 屏,带电容触摸(CST816S) | 常见型号:淘宝搜"ST7789 240×320 SPI 触摸";GC9A01 圆屏引脚兼容但需回切驱动 |
| 屏幕接口 | SPI + 6 线(CS / DC / RST / SCLK / MOSI / BL),I²C 触摸 | |
| 其他 | 1 个 LEDC 通道用于背光 PWM;USB OTG 下载口 / USB-UART 桥 | 背光默认 50% 占空比,25 kHz PWM |
推荐开发板:同等规格也可以选以下现成 SBC 做前期验证,再转自焊模组:
- S3 N16R8 核心板(淘宝常见的"ESP32-S3 N16R8 小系统板",直接引出 SPI/I²C/LEDC 全部引脚)
- LilyGO T-Embed / T-Display-S3 AMOLED(需手动改 pinmux 到下方 BOM)
- 官方 ESP32-S3-DevKitC-1-N16R8V(调试首选)
自搭 PCB 所需的核心元器件(参考 AstroBox-NG 模块原理图):
| 序号 | 元器件 | 型号 / 规格 | 数量 | 连接 / 引脚(默认) | 备注 |
|---|---|---|---|---|---|
| 1 | SoC 模组 | ESP32-S3-WROOM-1-N16R8V (16MB Flash + 8MB Octal PSRAM) | 1 | — | 核心 |
| 2 | SPI 方形 LCD 模组 | ST7789, 240×320, SPI, 40 MHz | 1 | SPI2 | 见下方引脚;GC9A01 圆屏引脚兼容 |
| 3 | LCD SPI SCLK | GPIO7 | 1 | SPI2 CLK | |
| 4 | LCD SPI MOSI | GPIO6 | 1 | SPI2 D (MOSI) | LCD 侧叫 SDA / SDI |
| 5 | LCD CS | GPIO5 | 1 | 片选,推挽输出 | |
| 6 | LCD D/C | GPIO4 | 1 | 数据 / 命令选择 | |
| 7 | LCD RST | GPIO3 | 1 | 复位,低有效 | |
| 8 | LCD 背光(BL) | GPIO2 | 1 | LEDC Channel 0,25 kHz PWM | 默认 50% 亮度 |
| 9 | 电容触控芯片 | CST816S (I²C, 地址 0x15 或 0x2A) | 1 | I2C0 | |
| 10 | I²C SDA | GPIO18 | 1 | I2C0 SDA,4.7 kΩ 上拉到 3V3 | |
| 11 | I²C SCL | GPIO16 | 1 | I2C0 SCL,4.7 kΩ 上拉到 3V3 | |
| 12 | TP INT | GPIO1 | 1 | 输入,内部上拉 | 低电平有效 |
| 13 | TP RST | GPIO0 | 1 | 推挽输出 | 复位时序见 CST816S datasheet |
| 14 | 电源 | 3.3 V LDO,≥ 500 mA 峰值 | 1 | — | ST7789 亮背光瞬时可达 ~180 mA |
| 15 | 去耦电容 | 100 nF 0402(每颗 IC 旁),10 µF 0603×2 | 若干 | — | |
| 16 | 下载口 | USB Type-C(USB OTG 直连 GPIO19/20)或 CH340K | 1 | — | espflash 推荐 USB OTG 方式 |
| 17 | MicroSD 卡槽(SPI 模式,FAT32) | TF 卡座 / MicroSD 破板,3.3V 逻辑 | 1 | SPI2(与 LCD 共享总线) | 卡规格建议 ≤ 32 GB FAT32(Cluster ≥ 8 KB);大容量卡可格式化为 FAT32(exFAT 不支持) |
| 18 | SD SPI MISO | → ESP32 GPIO8(SPI2 MISO) | 1 | 直连卡座 DO | SD 侧常叫 DO / DAT0 |
| 19 | SD SPI CS | → ESP32 GPIO9 | 1 | 推挽输出,10 kΩ 上拉到 3V3(推荐) | 低有效 |
| 20 | 上拉电阻(SD CS) | 10 kΩ 0402(可选但推荐) | 1 | GPIO9 ↔ 3V3 | 防止启动时 SD 误应答 SPI 总线 |
| 21 | 去耦电容(SD 卡座 3V3 旁) | 100 nF 0402 + 10 µF 0603 | 1 对 | 卡座 VCC 旁 | 峰值读写电流 ~80 mA |
| 22 | LED(可选) | 红色 0805 + 1 kΩ | 1 | 任意空闲 GPIO |
ESP32-S3 GPIO → 外设
──────────────────────────────────────
GPIO0 → CST816S RST (OUT)
GPIO1 → CST816S INT (IN, PU)
GPIO2 → LCD BL (LEDC CH0) (PWM, 25 kHz)
GPIO3 → LCD RST (OUT)
GPIO4 → LCD D/C (OUT)
GPIO5 → LCD CS (OUT)
GPIO6 → LCD SPI MOSI/SDI (SPI2 D) ← MicroSD 共用:SD DAT2/CMD 方向此脚不接,仅 SDI
GPIO7 → LCD SPI SCLK (SPI2 CLK) ← MicroSD 共用 CLK
GPIO8 → MicroSD MISO / DO (SPI2 MISO)★ 新增
GPIO9 → MicroSD CS (OUT, PU) ★ 新增
GPIO16 → I2C0 SCL (400 kHz, PU 4.7k)
GPIO18 → I2C0 SDA (400 kHz, PU 4.7k)
GPIO19, GPIO20 → USB-OTG D-, D+ (espflash / JTAG 下载)
──────────────────────────────────────
SPI2 总线说明:
LCD → CS=GPIO5, SPI mode 0, 40 MHz
SD → CS=GPIO9, SPI mode 0, 20 MHz(SD 默认慢,可上探 40 MHz)
两者共用 SCLK=GPIO7 / MOSI=GPIO6 / MISO=GPIO8;ESP-IDF SPI host 按 CS 内部串行化。
──────────────────────────────────────
问:是否支持 ST7789 240×320 长方形屏幕?(#21 @dm1366631)
答: 已完成适配,ST7789 240×320 现为默认屏幕。三阶段工作全部落地:
| 阶段 | 内容 | 状态 |
|---|---|---|
| 1 | 驱动切换:src/gui/display.rs 中 GC9A01 → ST7789,display_size(240, 320),ColorInversion::Normal,ColorOrder::Bgr。SPI pinmux 不变。 |
✅ 已完成 |
| 2 | 分辨率常量同步:slint_ui.rs 中 DISPLAY_HEIGHT 240 → 320,触摸 clamp 自动跟随。 |
✅ 已完成 |
| 3 | Slint UI 矩形重排:app.slint 去掉 border-radius: 120px 圆形 clip,Window 改 240×320;电量/充电移至顶部状态栏,设备名居中,网速下行下方,设置按钮底部;断连呼吸点居中 y=160。 |
✅ 已完成 |
回切 GC9A01 圆屏:如需用回圆形屏,在 src/gui/display.rs 把 ST7789 改回 GC9A01、display_size(240, 240)、ColorInversion::Inverted、ColorOrder::Rgb,并把 slint_ui.rs 的 DISPLAY_HEIGHT 改回 240、app.slint 的 height 改回 240px 即可。引脚完全一致,无需改 PCB。
以下教程基于 macOS Tahoe 26 (aarch64)
先安装:cmake、ninja、dfu-util、ldproxy(通过 cargo install)
必须使用 esp-idf v5.3.3,执行编译时会自动下载,但推荐你先自己装好 idf v5.3.3,然后使用 zed 编辑器并 install cli,在终端中先执行 idf 的 export.sh,接着直接 zed -n <folder path> 打开项目以节省时间
注意:该模块依赖独立的交叉编译工具链,已经从
src-tauri顶层 Cargo workspace 中剥离。请直接进入该目录后再运行 Cargo 命令。为了让 rust-analyzer 正常工作,你通常也需要在编辑器中单独打开该模块的文件夹。
# Debug
cargo build
# Release
cargo build --release
# 烧录
espflash flash -B 1500000 ./target/xtensa-esp32s3-espidf/release/app_esp32s3
# 生成固件镜像 (含分区表)
bash save_image_rel.shWiFi 凭据可通过编译时环境变量或运行时 NVS 存储配置:
# 编译时设置(在 .env 或环境变量中)
# 注意:从 2026-08 起默认值已移除(修复硬编码凭据安全问题),未设置
# 时启动会打印 warn,等你通过 NVS 在运行时写入
export DEFAULT_WIFI_SSID="your_ssid"
export DEFAULT_WIFI_PASSWORD="your_password"
export MIWEAR_AUTH_KEY="0123456789abcdef0123456789abcdef" # 32 char hex
cargo build --release首次成功连接后,凭据会自动保存到 NVS,下次启动直接读取。后续可通过调用
crate::nvs_config::save_wifi_credentials(ssid, password) 在运行时动态写入。
此库使用 AGPL 3.0 授权
This library is licensed under AGPL 3.0
根据 AGPL 3.0 所述可选附加条款,本项目额外附加署名要求,使用此项目需在遵守 AGPL 3.0 条款后额外为此项目添加署名,署名包括但不限于本项目仓库地址,作者名等。
注:附加条款以中文版为准,其他语言仅供参考!
According to the optional additional terms stated in AGPL 3.0, this project includes an additional attribution requirement. When using this project, after complying with the terms of AGPL 3.0, you must also add attribution for this project, which includes but is not limited to the project repository address, the author's name, etc.
Note: The additional terms are based on the Chinese version. Other languages are for reference only!