前置知识:了解 ESP32-C3 的基本能力(第 1 章),理解 Activity 框架(第 8 章),熟悉流式处理思想(第 10-11 章)。 本章目标:理解 CrossPoint Reader 如何利用 ESP32-C3 内置的 WiFi 实现 Web 传书、WebDAV 同步、在线书库浏览和跨设备阅读进度同步。
一台电子书阅读器如果只能通过 USB 传书,用起来会很不方便。CrossPoint Reader 充分利用了 ESP32-C3 内置的 WiFi 能力,把一颗 10 元的芯片变成了一台功能齐全的网络设备——它既是 Web 服务器,又是 WebDAV 服务器,还是 OPDS 客户端和 KOReader 同步客户端。
12.1 ESP32-C3 的 WiFi 能力
硬件基础
ESP32-C3 内置了完整的 WiFi 4(802.11 b/g/n)射频模块,工作在 2.4GHz 频段。“内置”意味着不需要额外的 WiFi 芯片或天线模块——射频收发器和基带处理器都集成在同一颗芯片内部。PCB 上只需要一小段走线天线就够了。两种工作模式
ESP32-C3 的 WiFi 支持两种模式:| 模式 | 全称 | 作用 | 类比 |
|---|---|---|---|
| STA | Station | 连接到一个已有的 WiFi 路由器 | 你的手机连家里的 WiFi |
| AP | Access Point | 自己变成一个 WiFi 热点 | 你手机开热点给别人用 |
- STA 模式:连接家里的 WiFi 路由器,访问互联网——下载书籍、检查更新、同步进度
- AP 模式:在没有 WiFi 路由器的环境下(比如旅途中),阅读器自己创建热点,手机连上后就能传书
WiFi 的内存开销
WiFi 不是免费的。ESP32-C3 的 WiFi 协议栈(包括 TCP/IP 栈)运行时大约需要 50KB RAM。在 380KB 的总内存中,这是一笔不小的开销——超过 13%。 这就是为什么 CrossPoint 不会一直开着 WiFi。只有在用户明确进入”WiFi 传书”或”在线书库”等需要网络的功能时,才会启动 WiFi 模块。阅读书籍时 WiFi 是完全关闭的,省电的同时也释放了这 50KB 内存给排版引擎使用。12.2 内置 Web 服务器
你可能觉得”Web 服务器”是 Nginx、Apache 那样的大型软件。但在嵌入式世界中,一个 Web 服务器可以非常轻量——CrossPoint 在芯片上运行了一个完整的 HTTP 服务器,占用很少的资源。服务器架构
HTML/JS 管理界面嵌入固件
用户通过浏览器访问阅读器时看到的管理页面(文件列表、设置面板),其 HTML 和 JavaScript 代码并不存放在 SD 卡上,而是直接编译进了固件里。 具体做法是在编译时,用构建脚本把 HTML/CSS/JS 文件转成 C 语言的字节数组:手机传书的完整流程
用户从手机上传一本书到阅读器的流程是这样的:12.3 AP 热点 + DNS 劫持:自动弹出管理页面
当你用手机连上公共 WiFi(比如机场或酒店的网络)时,通常会自动弹出一个登录页面。CrossPoint Reader 借用了同样的机制——当手机连上阅读器的 AP 热点后,会自动弹出管理页面。实现代码
DNS 劫持原理
这个”自动弹出”是怎么实现的?它利用了手机操作系统的 Captive Portal 检测机制:MDNS.begin(AP_HOSTNAME) 则注册了一个本地域名 crosspoint.local,在支持 mDNS 的设备上可以用这个域名代替 IP 地址访问。
12.4 WebDAV 服务器:让 Calibre 无线传书
什么是 WebDAV
WebDAV(Web Distributed Authoring and Versioning)是 HTTP 协议的扩展,增加了文件管理能力。如果说 HTTP 只能”看网页”,WebDAV 则能”像操作网盘一样管理远程文件”——创建目录、上传文件、下载文件、删除、重命名、移动。 你可能用过的坚果云、Box、部分 NAS 系统都支持 WebDAV 协议。为什么需要 WebDAV
CrossPoint Reader 的 Web 管理界面已经可以传书了,为什么还需要 WebDAV?因为 Calibre——全球最流行的电子书管理软件——支持通过 WebDAV 与设备同步书库。 有了 WebDAV,用户可以在 Calibre 中管理书籍(转格式、编辑元数据、整理分类),然后一键同步到阅读器,不需要手动一本一本上传。协议实现
CrossPoint 实现了 WebDAV 协议中最核心的操作:原子文件操作:防止写入半截
文件上传是最容易出问题的环节。想象一下:你正在上传一本 10MB 的 EPUB,传到 5MB 时突然 WiFi 断了。如果直接写入目标文件,现在 SD 卡上就有一个损坏的 5MB 文件——既不能读,又占空间。 CrossPoint 的解决方案是临时文件 + 原子重命名:rename() 操作。在大多数文件系统中,重命名是原子操作——要么完全成功,要么完全失败,不存在”重命名了一半”的中间状态。所以:
- 上传途中断连 → 只有
.davtmp文件受损,目标文件(如果存在旧版本)完好无损 - 上传完成 →
rename一步替换,不会出现”目标文件内容只有一半”的情况
LOCK/UNLOCK:善意的谎言
WebDAV 规范要求支持文件锁定——防止两个客户端同时修改同一个文件。但 CrossPoint 是单用户设备,不存在并发修改的问题。所以它选择了最简单的实现方式——返回假令牌:12.5 OPDS 在线书库
什么是 OPDS
OPDS(Open Publication Distribution System)是一种专为电子书设计的目录协议,基于 Atom XML 格式。你可以把它理解为”给电子书用的 RSS”——一个标准化的方式来发布和发现电子书资源。 很多在线书库(如 Project Gutenberg、Calibre 内容服务器等)都支持 OPDS。用户在阅读器上配置 OPDS 源的 URL,就可以直接浏览和下载书籍,不需要电脑中转。流式解析 HTTP 响应
OPDS 的响应是 XML 格式,可能很大(一个分类页面可能包含数百本书的信息)。CrossPoint 不会把整个响应下载到内存中再解析,而是边下载边解析。 它使用了一个巧妙的技巧——让 OPDS 解析器实现 Arduino 的Print 接口:
12.6 KOReader 跨设备同步
使用场景
KOReader 是另一个流行的开源电子书阅读软件,可以运行在 Kindle、Kobo、Android 等各种设备上。它有一个”阅读进度同步”功能:你在 Kindle 上读到第 42 页,切换到手机继续读时,KOReader 自动帮你翻到第 42 页。 CrossPoint Reader 实现了兼容 KOReader 的同步协议。这意味着你可以在 CrossPoint 和 KOReader 设备之间无缝切换阅读。同步协议
KOReader 的同步服务器是一个简单的 REST API。CrossPoint 作为客户端,只需要实现两个操作:获取进度和更新进度。阅读位置的表示方式
注意progress 字段使用的是 XPath 格式,例如 /body/div[3]/p[7]——表示”第 3 个 div 中的第 7 个段落”。
为什么不直接用页码?因为不同设备的屏幕大小不同,同一章节的分页结果也不同。CrossPoint 在 4.7 英寸墨水屏上可能把一章分成 20 页,而 Kindle Paperwhite 在 6 英寸屏幕上可能只分成 15 页。“第 10 页”在两台设备上对应的内容完全不同。
而 XPath 指向的是文档结构中的特定位置,与屏幕大小无关。收到同步进度后,CrossPoint 需要把 XPath 转换为本机的页码——这就用到了 section.bin 缓存中存储的”锚点到页码映射”(第 11 章介绍过)。
12.7 架构总览:网络功能如何共存
最后让我们用一张全景图来理解 CrossPoint 的网络功能是如何组织在一起的:- Web 服务器和 WebDAV 服务器在设备本地运行(阅读器是服务端),手机/电脑是客户端
- OPDS 和 KOReader 同步中阅读器是客户端,访问远程服务器
- 所有网络功能只在用户主动进入网络页面时启用,阅读时关闭 WiFi 节省内存和电量
- 所有网络数据传输都使用流式处理,不在内存中缓存完整响应
本章要点
- ESP32-C3 内置 WiFi 4(802.11 b/g/n),支持 AP(热点)和 STA(连接路由器)两种模式。WiFi 协议栈运行开销约 50KB RAM,仅在需要时启用。
- 内置 Web 服务器运行在 HTTP:80 端口,提供管理页面(HTML/JS 编译进固件)和 REST API。文件上传使用 4KB 分块接收,直接写入 SD 卡。
- AP 模式 + DNS 劫持实现了”连上热点自动弹出管理页面”的体验。原理是 DNS 服务器将所有域名解析到设备 IP,触发手机的 Captive Portal 检测。
- WebDAV 服务器实现了文件管理协议的核心操作(PROPFIND、GET、PUT、DELETE、MKCOL 等),使 Calibre 可以无线管理设备上的书库。上传使用临时文件加原子重命名确保数据安全,LOCK/UNLOCK 返回假令牌兼容客户端。
- OPDS 客户端通过流式解析 Atom XML 来浏览在线书库。OpdsParser 实现 Print 接口,HTTP 响应数据直接流入 XML 解析器,内存中只需 1KB 缓冲区。
- KOReader 同步通过 REST API 实现跨设备阅读进度同步。位置使用 XPath 表示以避免不同屏幕尺寸的分页差异。
- 所有网络功能遵循两个原则:按需启用(不用时关闭 WiFi)和流式处理(不在内存中缓存完整数据)。
下一章预告:第 13 章我们将探索电源管理与续航优化——CPU 如何动态调频、深度睡眠是怎么实现的、电池电量怎么检测,以及如何让一块小电池撑过数周的阅读时间。