本产品是一款微雪 (Waveshare) 设计的高性能、高集成的微控制器开发板。 搭载 ESP32-S3 微控制器,支持 Wi-Fi 与 BLE 双模通信。板载 3.97 英寸电子墨水屏,功耗极低,环境光下可视,适合便携设备及长续航场景。 集成 RTC 实时时钟、SHTC3 温湿度传感器、Micro SD 卡槽、低功耗音频编解码芯片以及电源管理芯片。 为物联网终端、电子标签和便携显示器等应用提供灵活可靠的开发平台。



ESP32-S3-ePaper-3.97 支持 Arduino IDE 和 ESP-IDF 两种开发框架,为开发者提供灵活的选择,您可以根据项目需求和个人偏好选择合适的开发工具。
两种开发方式各有优势,开发者可根据自身需求和技能水平选择。Arduino 简单易学、上手快,适合初学者和非专业人士;ESP-IDF 则提供更高级的开发工具和更强的控制能力,适合有专业背景或对性能要求较高的开发者,更适用于复杂项目开发。
Arduino IDE 是一款便捷灵活、易于上手的开源电子原型平台。无需太多基础知识,简单学习后即可快速开发。Arduino 拥有庞大的全球用户社区,提供海量开源代码、项目示例和教程,以及丰富的库资源,封装了复杂功能,让开发者能够快速实现各种功能。您可以参考 Arduino IDE 开发环境搭建教程 完成初始设置,教程中同时提供了相关示例程序供参考。
ESP-IDF 全称 Espressif IoT Development Framework,是乐鑫科技为 ESP 系列芯片推出的专业开发框架。它基于 C 语言开发,包含编译器、调试器、烧录工具等,支持命令行或集成开发环境(如 Visual Studio Code 配合 Espressif IDF 插件)开发,插件提供代码导航、项目管理、调试等功能。我们推荐使用 VS Code 进行开发,具体配置过程可参考 ESP-IDF (VS Code) 开发环境搭建教程,教程中同时提供了相关示例程序供参考。
请参考 安装和配置 Arduino IDE 教程 下载安装 Arduino IDE 并添加 ESP32 支持。
| 库或文件名称 | 说明 | 版本 | 安装方式 |
|---|---|---|---|
| SensorLib | 传感器库 | v0.3.1 | "离线/在线 安装" |

Arduino 示例程序位于 示例程序包 的 Arduino/examples 目录中。
| 示例程序 | 基础例程说明 | 依赖库 |
|---|---|---|
| 01_Audio_Test | 驱动 ES8311 播放音乐 | - |
| 02_E-Paper_Example | 3.97inch e-Paper 墨水屏幕示例程序 | - |
| 03_I2C_PCF85063 | RTC 例程 | - |
| 04_I2C_SHTC3 | 温湿度传感器例程 | SensorLib |
| 05_SD_Test | SD 卡例程 | - |
| 06_QMI8658A | 六轴陀螺仪例程 | SensorLib |
【程序说明】
【硬件连接】
【代码分析】
配置 ES8311 音频编解码器初始化。
void setup() {
Serial.begin(115200);
Wire.begin(I2C_SDA, I2C_SCL);
pinMode(PA_CTRL, OUTPUT);
digitalWrite(PA_CTRL, HIGH);
es8311_codec_init();
setupI2S();
Serial.println("I2S Initialized");
}
持续向 I2S 总线写入内置音频数据,实现循环播放。
i2s.write((uint8_t *)audio_data, AUDIO_SAMPLES * 2);
【运行效果】
【程序说明】
【硬件连接】
【代码分析】
显示预设图片。
#if 1 // show bmp
EPD_3IN97_Init_Fast();
printf("show image for array\r\n");
EPD_3IN97_Display(gImage_image);
DEV_Delay_ms(1500);
#endif
绘制基础图形、中英文文字、数字,并刷新显示。
#if 1 // Drawing on the image
//1.Select Image
EPD_3IN97_Init();
printf("SelectImage:BlackImage\r\n");
Paint_SelectImage(BlackImage);
Paint_Clear(WHITE);
// 2.Drawing on the image
Debug("Drawing:BlackImage\r\n");
Paint_DrawPoint(10, 80, BLACK, DOT_PIXEL_1X1, DOT_STYLE_DFT);
Paint_DrawPoint(10, 90, BLACK, DOT_PIXEL_2X2, DOT_STYLE_DFT);
Paint_DrawPoint(10, 100, BLACK, DOT_PIXEL_3X3, DOT_STYLE_DFT);
Paint_DrawLine(20, 70, 70, 120, BLACK, DOT_PIXEL_1X1, LINE_STYLE_SOLID);
Paint_DrawLine(70, 70, 20, 120, BLACK, DOT_PIXEL_1X1, LINE_STYLE_SOLID);
Paint_DrawRectangle(20, 70, 70, 120, BLACK, DOT_PIXEL_1X1, DRAW_FILL_EMPTY);
Paint_DrawRectangle(80, 70, 130, 120, BLACK, DOT_PIXEL_1X1, DRAW_FILL_FULL);
Paint_DrawCircle(45, 95, 20, BLACK, DOT_PIXEL_1X1, DRAW_FILL_EMPTY);
Paint_DrawCircle(105, 95, 20, WHITE, DOT_PIXEL_1X1, DRAW_FILL_FULL);
Paint_DrawLine(85, 95, 125, 95, BLACK, DOT_PIXEL_1X1, LINE_STYLE_DOTTED);
Paint_DrawLine(105, 75, 105, 115, BLACK, DOT_PIXEL_1X1, LINE_STYLE_DOTTED);
Paint_DrawString_EN(10, 0, "waveshare", &Font16, BLACK, WHITE);
Paint_DrawString_EN(10, 20, "hello world", &Font12, WHITE, BLACK);
Paint_DrawNum(10, 33, 123456789, &Font12, BLACK, WHITE);
Paint_DrawNum(10, 50, 987654321, &Font16, WHITE, BLACK);
Paint_DrawString_CN(130, 0, " 你好 abc", &Font12CN, BLACK, WHITE);
Paint_DrawString_CN(130, 20, "微雪电子", &Font24CN, WHITE, BLACK);
printf("EPD_Display\r\n");
EPD_3IN97_Display_Base(BlackImage);
DEV_Delay_ms(3000);
#endif
【运行效果】
屏幕刷新显示

【程序说明】
【硬件连接】
【代码分析】
循环读取 RTC 时间,格式化字符串后局部刷新墨水屏显示。
void i2c_rtc_loop_task(void *arg)
{
char buff[80];
for(;;)
{
RtcDateTime_t datetime = rtc_dev->get_rtcTime();
printf("%d/%d/%d %d:%d:%d \n",datetime.year,datetime.month,datetime.day,datetime.hour,datetime.minute,datetime.second);
Paint_NewImage(BlackImage, Font20.Height *3, Font20.Width * 8, 270, WHITE);
Paint_SelectImage(BlackImage);
Paint_Clear(WHITE);
sprintf(buff, "%d-%d-%d", datetime.year,datetime.month,datetime.day);
Paint_DrawString_EN(0, 5, buff, &Font20, WHITE, BLACK);
sprintf(buff, "%d:%d:%d", datetime.hour,datetime.minute,datetime.second);
Paint_DrawString_EN(0, 35, buff, &Font20, WHITE, BLACK);
EPD_3IN97_Display_Partial(BlackImage, 50, 250, 50 + Font20.Height*3 , 250 + Font20.Width * 8);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
【运行效果】
墨水屏局部刷新显示时间

【程序说明】
【硬件连接】
【代码分析】
循环读取传感器数据,局部刷新墨水屏,显示温湿度数据。
void i2c_SHTC3_loop_task(void *arg)
{
char buff[80];
for(;;)
{
shtc3_data_t shtc3_data = shtc3_dev->readTempHumi();
printf("RH:%.2f%%,Temp:%.2f°C \n",shtc3_data.RH,shtc3_data.Temp);
Paint_NewImage(BlackImage, Font20.Height *3, Font20.Width * 8, 270, WHITE);
Paint_SelectImage(BlackImage);
Paint_Clear(WHITE);
sprintf(buff, "%.2f%%",shtc3_data.RH);
Paint_DrawString_EN(0, 5, buff, &Font20, WHITE, BLACK);
sprintf(buff, "%.2f", shtc3_data.Temp);
Paint_DrawString_EN(0, 35, buff, &Font20, WHITE, BLACK);
Paint_DrawString_CN(Font20.Width * 5, 30, "℃", &Font12CN, WHITE, BLACK);
EPD_3IN97_Display_Partial(BlackImage, 50, 250, 50 + Font20.Height*3, 250 + Font20.Width * 8);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
【运行效果】
墨水屏局部刷新显示温湿度

【程序说明】
【硬件连接】
【代码分析】
配置挂载 SD 卡
//sdcard init
Serial.begin(115200);
delay(1000);
SD_MMC.setPins(SD_CLK, SD_CMD, SD_D0, SD_D1, SD_D2, SD_D3);
if (!SD_MMC.begin("/sdcard", true)) {
printf("SD card failed to mount\r\n");
return;
}
printf("SD card success to mount\r\n");
显示图片
printf("show BMP-------------------------\r\n");
EPD_3IN97_Init_Fast();
Paint_Clear(WHITE);
GUI_ReadBmp("/sdcard/bmp/100x100.bmp", 50, 50);
EPD_3IN97_Display_Fast(Image);
printf("100*100 BMP Load OK!\r\n");
DEV_Delay_ms(1000);
Paint_Clear(WHITE);
GUI_ReadBmp("/sdcard/bmp/3in97.bmp",0 ,0);
EPD_3IN97_Display_Fast(Image);
printf("800*480 BMP Load OK!\r\n");
DEV_Delay_ms(3000);
显示 4 灰度 BMP 图片
EPD_3IN97_Init_4GRAY();
Paint_NewImage(Image, EPD_3IN97_WIDTH, EPD_3IN97_HEIGHT, 0, WHITE);
Paint_SetScale(4);
Paint_Clear(WHITE);
if(GUI_ReadBmp_4Gray("/sdcard/bmp/3in97_4Gray.bmp", 0, 0) == 0){
printf("4Gray BMP Load OK!\r\n");
}else{
printf("4Gray BMP Load Failed!\r\n");
}
EPD_3IN97_Display_4Gray(Image);
DEV_Delay_ms(3000);
【运行效果】
读取显示 sd 卡图片



【程序说明】
【硬件连接】
【代码分析】
检测数据就绪,读取并打印传感器数据,调用显示函数更新墨水屏显示数据
if (qmi.getDataReady()) {
if (qmi.getAccelerometer(acc.x, acc.y, acc.z)) {
Serial.print("ACCEL.x:"); Serial.print(acc.x); Serial.print(",");
Serial.print("ACCEL.y:"); Serial.print(acc.y); Serial.print(",");
Serial.print("ACCEL.z:"); Serial.print(acc.z); Serial.println();
}
if (qmi.getGyroscope(gyr.x, gyr.y, gyr.z)) {
Serial.print(" GYRO.x:"); Serial.print(gyr.x); Serial.print(",");
Serial.print(" GYRO.y:"); Serial.print(gyr.y); Serial.print(",");
Serial.print(" GYRO.z:"); Serial.print(gyr.z); Serial.println();
}
qmi8658_data_show();
}
【运行效果】
墨水屏局部刷新显示六轴陀螺仪传感器数据

以下环境设置适用于 Windows 10/11 系统,Mac/Linux 用户请参考 官方说明
下载并安装 Visual Studio Code。
在 VS Code 中,通过点击 VS Code 侧边活动栏中的 或使用快捷键(Ctrl+Shift+X)来打开 扩展 视图。然后,搜索 ESP-IDF 扩展并安装。

安装扩展后,VS Code 左侧活动栏中会出现 图标,点击该图标可查看 ESP-IDF 扩展的基本命令列表,在 Advanced 中选择 配置 ESP-IDF 扩展。

选择 Express 进入快速配置模式:

根据需要修改以下选项。

点击 Install 开始安装。你将看到一个显示安装进度的页面,包括 ESP-IDF 下载、ESP-IDF 工具下载安装以及 Python 虚拟环境创建的进度状态。

如果安装正确,你会看到所有设置已配置完成的提示,即可开始使用该扩展程序。

注意:如果 ESP-IDF 安装失败或需要重新安装,可以尝试删除 C:\Users\%Username%\esp 和 C:\Users\%Username%\.espressif 文件夹后重试。
ESP-IDF 示例程序位于 示例程序包 的 ESP-IDF 目录中。
| 示例程序 | 基础例程说明 |
|---|---|
| 01_E-Paper_Example | 3.97inch e-Paper 墨水屏幕示例程序 |
| 02_Mic_test | 扬声器播放麦克风录到的声音 |
| 03_Music | 挂载 SD 卡,读取播放 SD 卡中的音乐 |
| 04_SD_Test | 挂载 SD 卡,读取 SD 卡中的图片并刷新显示 |
| 05_QMI8658A | 六轴陀螺仪例程 |
| 06_I2C_PCF85063 | RTC 例程 |
| 07_I2C_SHTC3 | 温湿度传感器例程 |
| 08_ESP32-S3_e-Paper-3.97 | 集成了文件浏览、时钟、日历、闹钟、天气、网络配置、音频播放、电子阅读器功能 |
代码解释
显示预设图片 :
#if 1
ESP_LOGI(TAG,"2.show BMP");
EPD_Init_Fast();
EPD_Display(gImage_image);
vTaskDelay(pdMS_TO_TICKS(2000));
#endif
绘制基础图形、中英文文字、数字,并刷新显示 :
#if 1
ESP_LOGI(TAG,"3.Paint_NewImage");
EPD_Init();
Paint_NewImage(Image_Mono, EPD_WIDTH, EPD_HEIGHT, 0, WHITE);
Paint_SelectImage(Image_Mono);
Paint_Clear(WHITE);
// Drawing on the image
Paint_DrawPoint(5, 10, BLACK, DOT_PIXEL_1X1, DOT_STYLE_DFT);
Paint_DrawPoint(5, 25, BLACK, DOT_PIXEL_2X2, DOT_STYLE_DFT);
Paint_DrawPoint(5, 40, BLACK, DOT_PIXEL_3X3, DOT_STYLE_DFT);
Paint_DrawPoint(5, 55, BLACK, DOT_PIXEL_4X4, DOT_STYLE_DFT);
Paint_DrawLine(20, 10, 70, 60, BLACK, DOT_PIXEL_1X1, LINE_STYLE_SOLID);
Paint_DrawLine(70, 10, 20, 60, BLACK, DOT_PIXEL_1X1, LINE_STYLE_SOLID);
Paint_DrawLine(170, 15, 170, 55, BLACK, DOT_PIXEL_1X1, LINE_STYLE_DOTTED);
Paint_DrawLine(150, 35, 190, 35, BLACK, DOT_PIXEL_1X1, LINE_STYLE_DOTTED);
Paint_DrawRectangle(20, 10, 70, 60, BLACK, DOT_PIXEL_1X1, DRAW_FILL_EMPTY);
Paint_DrawRectangle(85, 10, 130, 60, BLACK, DOT_PIXEL_1X1, DRAW_FILL_FULL);
Paint_DrawCircle(170, 35, 20, BLACK, DOT_PIXEL_1X1, DRAW_FILL_EMPTY);
Paint_DrawCircle(170, 85, 20, BLACK, DOT_PIXEL_1X1, DRAW_FILL_FULL);
Paint_DrawNum(5, 180, 123456789, &Font24, BLACK, WHITE);
Paint_DrawString_CN(5, 100,"你好 abc", &Font16_UTF8, BLACK, WHITE);
Paint_DrawString_CN(5, 130, "微雪电子", &Font24_UTF8, WHITE, BLACK);
EPD_Display_Base(Image_Mono);
vTaskDelay(pdMS_TO_TICKS(2000));
#endif

代码分析
i2c_master_Init(); :初始化 I2C 总线。user_ui_init();:初始化全局 ui。user_button_init();:初始化 audio 接口。屏幕无显示效果
长按 BOOT 按键即可进入录音模式,对着 MIC 说话,3 秒后自动结束
单击 BOOT 按键,播放刚刚录的声音,(如果没有录音的情况下,播放的声音会很刺耳)
串口打印:

代码分析
_sdcard_init(); :初始化 SD 卡并读取 SD 卡音频文件。i2c_master_init();:初始化 I2C 总线,为 ES8311 音频编解码器的配置指令传输提供通信链路。audio_player_play(audio_fp);:启动音频播放器,读取打开的 MP3 文件并通过 ES8311 驱动扬声器播放。代码分析
_sdcard_init(); :初始化 SD 卡并读取 SD 卡音频文件。epaper_port_init();:初始化墨水屏底层硬件端口(引脚、通信总线等)EPD_Init();/EPD_Clear();:初始化墨水屏驱动参数,清屏显示白色背景GUI_ReadBmp("/sdcard/bmp/100x100.bmp", 50, 50);/EPD_Display_Fast(BlackImage);:读取 SD 卡中 BMP 图片,快速刷新显示图片GUI_ReadBmp_4Gray("/sdcard/bmp/3in97_4Gray.bmp", 0, 0);/EPD_Display_4Gray(BlackImage);:读取并显示 SD 卡中 4 灰度 BMP 图片读取显示 sd 卡图片



代码分析
epaper_port_init();/EPD_Init();/EPD_Clear();绘制数值后调用 EPD_Display_Partial 局部刷新墨水屏:初始化墨水屏底层硬件端口、驱动参数,清屏显示白色背景i2c_master_init();:初始化 I2C 总线,为 QMI8658 传感器的配置和数据读取提供通信链路xTaskCreate(qmi8658_test_task, "qmi8658_test_task", 4096, i2c_bus_handle, 5, NULL);:创建 FreeRTOS 任务执行 QMI8658 传感器数据读取逻辑qmi8658_is_data_ready(&dev, &ready);:检测传感器数据是否就绪,仅当数据就绪时读取数据。qmi8658_read_sensor_data(&dev, &data);:读取传感器的加速度、陀螺仪、温度、时间戳数据,串口打印数据draw_qmi8658_data_to_epaper(&data);:绘制数值后调用EPD_Display_Partial局部刷新显示数据墨水屏局部刷新显示六轴陀螺仪传感器数据

代码分析
epaper_port_init();/EPD_Init();/EPD_Clear();绘制数值后调用 EPD_Display_Partial 局部刷新墨水屏:初始化墨水屏底层硬件端口、驱动参数,清屏显示白色背景i2c_master_init();:初始化 I2C 总线,为 PCF85063 RTC 模块的配置和时间读取提供通信链路。PCF85063_init();:初始化 PCF85063 RTC 模块,完成时钟寄存器默认配置。PCF85063_GetTime();/PCF85063_SetTime();:读取 RTC 当前时间,设置默认时间。xTaskCreate(rtc_test_task, "rtc_display", 4096, NULL, 5, NULL);:创建 FreeRTOS 任务执行 RTC 时间读取和显示逻辑display_time_on_epaper():绘制数值后调用EPD_Display_Partial局部刷新显示数据墨水屏局部刷新显示时间

代码分析
epaper_port_init();/EPD_Init();/EPD_Clear();绘制数值后调用 EPD_Display_Partial 局部刷新墨水屏:初始化墨水屏底层硬件端口、驱动参数,清屏显示白色背景i2c_master_init();:初始化 I2C 总线。i2c_shtc3_init();:初始化 SHTC3 温湿度传感器 模块。xTaskCreatePinnedToCore(i2c_SHTC3_loop_task, "i2c_SHTC3_loop_task", 3 * 1024, NULL , 2, NULL,0);:创建 FreeRTOS 任务i2c_SHTC3_loop_task():获取温湿度数值并打印,调用EPD_Display_Partial局部刷新显示数据墨水屏局部刷新显示温湿度

代码分析
app_main()中完成 NVS、SD 卡、I2C 外设(传感器 / 时钟 / 音频)、按键、墨水屏的初始化,从 NVS 加载配置(WiFi 使能、工作模式),根据模式创建对应任务;void user_Task(void *arg)负责绘制墨水屏主菜单(8 个功能图标 + 文字)、响应按键事件(上下选择、确认进入子页、全局刷新、关机)、管理墨水屏低功耗(5 秒无操作睡眠、10 分钟无操作软关机)、刷新顶部状态栏(时间 / 电量 / WiFi 状态);file_browser_task(); 文件浏览模块page_clock_show(); 时钟模块page_calendar_show(); 日历模块page_alarm_menu(); 闹钟模块page_weather_city_select(); 天气模块page_handle_network_key_event(); 网络配置模块page_audio_main(); 音频播放模块page_fiction_file(); 电子阅读器模块烧录程序完成后进入配置网络界面,通过连接墨水屏显示的 wifi 名称进行网络配置

网络配置完成进入主页面,通过拨轮按钮与 BOOT 和 PWR 侧边按钮控制,60 秒无操作墨水屏自动睡眠,10 分钟无操作软关机

文件浏览模块

时钟模块


日历模块

闹钟模块
闹钟模块主界面,支持最多 5 组闹钟设置

删除闹钟后时间默认复位为 00:00 且状态为关闭,单击确认进入闹钟编辑模式,上下拨轮↑↓选择需编辑的内容(时 / 分 / 开关)

时 / 分编辑:单击确认进入数值调节,上下拨轮↑↓按键可单次调节也可长按连续调节;开关编辑:单击确认切换一次开启 / 关闭状态,双击确认 / Boot 退出编辑模式

闹铃触发:设置的闹钟时间到后自动播放内置音频,按下任一按键停止播放,双击确认 / Boot 退出闹钟模块返回主页面
天气模块(需要先开启 WIFI 连接,使用国内天气软件 API 与码表,国外无法使用)


网络配置模块

音频播放模块



电子阅读器模块
电子阅读器模块主界面

读取 SD 卡根目录 fiction 文件夹内的 txt 文本文件,上下拨轮↑↓选择文件,单击确认开始阅读,阅读时上下拨轮↑↓控制翻页

双击拨轮进入字体选择界面,单击确认选择字体并生效;

单击拨轮进入书签管理界面,再次单击执行书签操作,双击确认添加书签,阅读进度与书签自动保存至 SD 卡 bookmarks 文件夹

系统设置模块


系统开关机
开发板设计文件
ESP32-S3 芯片官方手册
板载器件数据手册
周一-周五(9:30-6:30)周六(9:30-5:30)
手机:13434470212
邮箱:services04@spotpear.cn
QQ:202004841
