1. 项目概述
最近在逛闲鱼时淘到一块2.9寸的汉塑E029A01墨水屏,这让我萌生了一个想法:能不能做一个通过蓝牙无线更新显示内容的低功耗信息牌?墨水屏最大的特点就是只在刷新时耗电,静态显示几乎不耗电,非常适合做这种需要长期显示但又需要偶尔更新的场景。
正好手头有块ESP32-C3-DevKitM-1开发板,它原生支持BLE 5.0,功耗表现也不错。于是决定用这个开发板作为主控,实现一个可以通过手机APP无线更新显示内容的低功耗墨水屏系统。
2. 硬件选型与系统设计
2.1 核心硬件组件
这个项目主要用到三个核心硬件:
-
ESP32-C3-DevKitM-1开发板:
- 基于ESP32-C3 RISC-V单核处理器
- 支持蓝牙5.0和Wi-Fi 4
- 内置4MB Flash
- 工作电压3.0V-3.6V
- 低功耗模式下电流可低至5μA
-
汉塑E029A01墨水屏:
- 2.9英寸黑白电子墨水屏
- 分辨率296×128
- 驱动芯片为SS1680
- 支持局部刷新和全局刷新
- 工作电压3.3V
-
手机APP:
- 开发了一个简单的Android应用
- 负责图片处理和蓝牙数据传输
2.2 系统架构设计
整个系统的数据流是这样的:
- 用户在手机APP上选择图片
- APP对图片进行灰度处理和抖动算法处理
- 处理后的图片数据通过BLE传输到ESP32
- ESP32接收完整图片数据后存储到Flash
- 最后通过SPI接口将图片数据发送到墨水屏刷新显示

3. 蓝牙低功耗通信实现
3.1 蓝牙协议栈选择
ESP32提供了两种蓝牙协议栈选择:
-
ESP-Bluedroid:
- 支持经典蓝牙和低功耗蓝牙
- 功能全面但资源占用较大
-
ESP-NimBLE:
- 仅支持低功耗蓝牙
- 资源占用小,更适合低功耗场景
考虑到我们只需要BLE功能,而且希望尽可能降低功耗,最终选择了ESP-NimBLE协议栈。
3.2 蓝牙广播配置
要让手机能发现设备,首先需要配置蓝牙广播。广播参数设置非常关键,直接影响设备的被发现率和功耗。
c复制void adv_init(void) {
// 配置蓝牙地址
ble_hs_util_ensure_addr(0);
// 配置最佳地址类型
ble_hs_id_infer_auto(0, &own_addr_type);
// 保存蓝牙地址
ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);
// 启动蓝牙广播
start_advertising();
}
广播参数设置:
c复制struct ble_gap_adv_params adv_params = {
.conn_mode = BLE_GAP_CONN_MODE_UND, // 非定向可连接
.disc_mode = BLE_GAP_DISC_MODE_GEN, // 通用发现模式
.itvl_min = BLE_GAP_ADV_ITVL_MS(200), // 最小广播间隔200ms
.itvl_max = BLE_GAP_ADV_ITVL_MS(220) // 最大广播间隔220ms
};
注意:广播间隔不宜设置过短,否则会增加功耗;也不宜过长,否则设备发现时间会变长。200-220ms是一个比较平衡的选择。
3.3 GATT服务设计
为了传输图片数据,我们设计了一个自定义的GATT服务:
c复制const struct ble_gatt_svc_def gatt_svr_svcs[] = {
{
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &svc_uuid.u,
.characteristics = (struct ble_gatt_chr_def[]) {
{
.uuid = &chr_uuid.u,
.access_cb = chr_access,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY,
.val_handle = &chr_val_handle
},
{ 0, }
}
},
{ 0, },
};
这个服务包含一个特征值,支持读写和通知功能,用于传输图片数据。
4. 图片数据传输协议
4.1 数据分包传输
由于BLE单次传输的数据量有限(MTU默认是23字节,最大可协商到247字节),而一张296×128的墨水屏图片即使经过压缩也有近4KB大小,必须采用分包传输的方式。
我们设计了一个简单的传输协议:
| 帧头 | 命令字 | 数据长度 | 数据 | 校验和 |
|---|---|---|---|---|
| 0xAA | 0x01 | N | ... | CRC8 |
每个数据包包含:
- 帧头:固定为0xAA
- 命令字:标识数据类型
- 数据长度:当前包的数据长度
- 数据:实际图片数据
- 校验和:CRC8校验
4.2 数据确认机制
为了保证传输可靠性,我们实现了简单的确认机制:
- 手机发送一个数据包
- ESP32收到后校验数据
- 校验通过后回复确认
- 手机收到确认后再发送下一个包
如果超时未收到确认,手机会重发当前数据包。
c复制typedef struct {
unsigned int position; // 当前数据在文件中的位置
unsigned char status; // 接收状态
} IMAGE_FILE_RESPONSE;
状态码定义:
- 0: 正常,可以发送下一个数据
- 1: 收到取消包,已取消发送
- 2: 当前数据校验出错,需重发
- 3: 数据位置不连续,需要从头开始
- 4: 收到的结束包文件长度不符
- 5: 数据文件超过限制(12KB)
- 6: 收到结束包,开始更新墨水屏
5. 墨水屏驱动实现
5.1 墨水屏初始化
墨水屏的初始化需要严格按照时序进行:
c复制void EPD_2IN9_Init(uint8_t mode) {
EPD_Reset(); // 复位墨水屏
// 发送初始化序列
EPD_SendCommand(0x12); // 软复位
EPD_WaitBusy();
// 更多初始化命令...
}
特别注意:墨水屏每次操作后都需要检查Busy引脚,必须等待上一次操作完成才能进行下一步。
5.2 图片显示流程
完整的图片显示流程如下:
- 初始化SPI和GPIO
- 发送初始化序列
- 清屏
- 发送图片数据
- 发送刷新命令
- 进入睡眠模式
c复制void EPD_2IN9_Display(const uint8_t *image) {
EPD_2IN9_Init(EPD_2IN9_FULL);
EPD_SendCommand(0x24); // 写入黑白数据
for(int i=0; i<4736; i++) {
EPD_SendData(image[i]);
}
EPD_2IN9_Refresh(); // 刷新显示
EPD_2IN9_Sleep(); // 进入睡眠
}
5.3 图片处理算法
由于墨水屏只能显示黑白两色,原始图片需要经过处理:
-
灰度化:
python复制gray = 0.299 * R + 0.587 * G + 0.114 * B -
抖动算法(Floyd-Steinberg):
python复制for each pixel: old_pixel = gray[x][y] new_pixel = 255 if old_pixel > 128 else 0 error = old_pixel - new_pixel gray[x+1][y] += error * 7/16 gray[x-1][y+1] += error * 3/16 gray[x][y+1] += error * 5/16 gray[x+1][y+1] += error * 1/16
处理前后的图片对比:

6. 低功耗优化
6.1 蓝牙连接参数优化
BLE连接参数对功耗影响很大:
c复制struct ble_gap_conn_params params = {
.scan_itvl = 16, // 扫描间隔
.scan_window = 16, // 扫描窗口
.itvl_min = 24, // 最小连接间隔
.itvl_max = 40, // 最大连接间隔
.latency = 0, // 从机延迟
.supervision_timeout = 400, // 监控超时
.min_ce_len = 0, // 最小CE长度
.max_ce_len = 0 // 最大CE长度
};
经验:连接间隔设置24-40ms,从机延迟设为0,可以在保证响应速度的同时降低功耗。
6.2 系统睡眠策略
当没有数据传输时,系统应进入低功耗模式:
c复制void enter_light_sleep() {
// 配置唤醒源
esp_sleep_enable_timer_wakeup(1000000); // 1秒后唤醒
// 进入轻度睡眠
esp_light_sleep_start();
}
实测功耗:
- 活跃模式:~25mA
- 轻度睡眠:~0.8mA
- 深度睡眠:~5μA
7. 实际应用效果
7.1 手机APP界面
APP主要功能:
- 扫描和连接蓝牙设备
- 选择图片并处理
- 传输图片到墨水屏

7.2 最终显示效果
图片传输完成后,墨水屏刷新显示:

8. 开发经验与注意事项
8.1 蓝牙传输稳定性
-
MTU设置:默认MTU只有23字节,建议协商到更大的值(如247字节),可以显著提高传输效率。
-
数据校验:每个数据包都要做校验,防止传输错误。
-
流控机制:实现简单的确认重传机制,确保数据可靠传输。
8.2 墨水屏使用技巧
-
刷新频率:尽量减少全刷次数,局部刷新可以延长屏幕寿命。
-
温度影响:低温下刷新时间会变长,设计时要考虑这个因素。
-
残影处理:定期做全刷可以消除残影。
8.3 功耗优化建议
-
合理设置广播间隔:广播间隔越长越省电,但设备发现时间会变长。
-
快速进入睡眠:数据传输完成后尽快进入低功耗模式。
-
外设电源管理:不使用时关闭墨水屏电源。
9. 项目总结
这个项目成功实现了通过蓝牙无线更新墨水屏显示内容的功能。ESP32-C3的表现非常出色,蓝牙连接稳定,功耗控制也很好。墨水屏的显示效果虽然只有黑白两色,但通过抖动算法也能获得不错的表现。
整个开发过程中,最大的挑战是蓝牙数据传输的稳定性和墨水屏的驱动时序。通过设计简单的传输协议和严格的校验机制,最终实现了可靠的数据传输。
未来可以考虑增加的功能:
- 支持多语言文本显示
- 增加温湿度传感器,实现环境信息显示
- 优化APP界面,提供更多图片处理选项
这个项目的完整代码已经开源,有兴趣的朋友可以参考实现。ESP32系列芯片在物联网应用中确实非常强大,值得深入学习和使用。
