
描述
项目简介
本项目设计了一个基于 ESP32-S3-WROOM-1-N8R8 模组 的番茄钟,集成了完整的 图形用户界面、本地音频播放 和 SNTP 网络时间同步功能,为你提供一个专注工作与休息管理的小工具!
设备采用 22pin 2 英寸 8 位并口 LCD 屏幕 作为显示屏幕,UI 界面设计基于 LVGL 图形库 进行设计与开发。通过 6 个实体按键 进行交互,其中包含一个连接到 IP5306 电源管理芯片的电源按键。内置 SD卡槽 用于存储音频文件,通过 I2S 协议与 ES8311 音频编解码器 驱动扬声器播放提示音与音乐。
gitee 仓库:https://gitee.com/LinYIN592/esp32s3-pomodoroclock-project/
硬件说明
主控制器 - ESP32-S3-WROOM-1-N8R8 模组
ESP32-S3-WROOM-1 模组简介
- 核心:搭载 双核 Xtensa® 32 位 LX7 处理器,主频最高 240 MHz。
- 存储:内置 384 KB ROM,512 KB SRAM,合封 8 MB Flash 和 8 MB PSRAM,为屏幕显示提供充足缓冲空间。
- 无线:集成 2.4 GHz Wi-Fi (802.11 b/g/n) 与 蓝牙 5.0 模块,内置 PCB 天线,简化射频设计。
- 外设:多达 45 个 GPIO,支持 SPI、I2S、I2C、UART、USB、PWM 等多种接口,满足多外设连接需求。
引脚功能列表
嗯哼哼,ESP32-S3 相对 ESP32-C3 来说,能使用的引脚数量多了不少哦!(不过价格也很贵就是了),因此本设计的引脚功能列表也会列得超长~
| 引脚 | 名称 | 功能 |
|---|---|---|
| IO18 | LCD_DB0 | LCD 8 位并口数据引脚 BIT0 |
| IO17 | LCD_DB1 | LCD 8 位并口数据引脚 BIT1 |
| IO16 | LCD_DB2 | LCD 8 位并口数据引脚 BIT2 |
| IO15 | LCD_DB3 | LCD 8 位并口数据引脚 BIT3 |
| IO7 | LCD_DB4 | LCD 8 位并口数据引脚 BIT4 |
| IO6 | LCD_DB5 | LCD 8 位并口数据引脚 BIT5 |
| IO5 | LCD_DB6 | LCD 8 位并口数据引脚 BIT6 |
| IO4 | LCD_DB7 | LCD 8 位并口数据引脚 BIT7 |
| IO42 | LCD_BLK | LCD 背光控制引脚 |
| IO8 | LCD_RD | LCD 读允许信号引脚 |
| IO46 | LCD_WR | LCD 写允许信号引脚 |
| IO3 | LCD_RS | LCD 显示数据 / 命令信号引脚 |
| IO39 | LCD_CS | LCD 片选信号引脚 |
| ESP_EN | LCD_RST | LCD 复位信号引脚 |
| IO14 | SD_CD | SD 插入检测引脚 |
| IO10 | SD_CS | SD 片选信号引脚 |
| IO13 | SD_MOSI | SD 主出从入数据引脚 |
| IO12 | SD_CLK | SD 时钟信号引脚 |
| IO11 | SD_MISO | SD 主入从出数据引脚 |
| IO1 | ES8311_SCL | ES8311 I2C 时钟信号引脚 |
| IO2 | ES8311_SDA | ES8311 I2C 数据信号引脚 |
| IO38 | ES8311_MCLK | ES8311 I2S 主时钟信号引脚 |
| IO45 | ES8311_SCLK | ES8311 I2S 串行数据位时钟信号引脚 |
| IO47 | ES8311_LRCK | ES8311 I2S 串行数据左右声道帧时钟信号引脚 |
| IO21 | ES8311_DIN | ADC 串行数据输入信号引脚 |
| IO48 | ES8311_DOUT | DAC 串行数据输出信号引脚 |
| IO9 | BAT_ADC | Battery 锂电池电压信号采集引脚 |
| IO0 | KEY1 | KEY 按键信号引脚 #1 |
| IO40 | KEY2 | KEY 按键信号引脚 #2 |
| IO41 | KEY3 | KEY 按键信号引脚 #3 |
| IO44 | KEY4 | KEY 按键信号引脚 #4 |
| IO43 | KEY5 | KEY 按键信号引脚 #5 |
| IO19 | ESP_DN | USB DN 差分信号引脚 |
| IO20 | ESP_DP | USB DP 差分信号引脚 |
电源管理 - IP5306 锂电池充放电电路
IP5306 电源管理 SOC 简介
- 充电:最大 2.1A 同步开关充电,充电效率高达 91%,支持 3.7V 锂电池充电。
- 放电:最大 2.4A 同步升压转换,升压效率高达 92%,支持边充边放。
- 保护:自带过充、过放、过流、短路与过温保护,ESD 耐压 4 kV。
- 电量显示:支持 1~4 颗 LED 电量指示灯(本设计中使用 2 灯电量指示模式)
外围电路设计
作为一个设计目标是能随时随地使用的番茄钟设备,可靠的电源管理必不可少~官方技术手册提供的 4LED 电量指示参考电路原理图 如下所示~
考虑到本设计已配备了一块 LCD 屏幕,IP5306 原本的 LED 电量指示功能就显得有些冗余了?因此本设计最终采用 IP5306 的 2 灯模式,仅对充电与放电状态进行基本指示,既保留了必要的状态反馈,又简化了电路设计~
基于上述参考设计,我们对电路进行了相应调整:修改 LED1、LED2、LED3 的连接方式,移除 R3 与 D5 元件,并省略了虚线框内的辅助电路。这样就得到了 本设计采用的 IP5306 锂电池充放电电路~
🎨 显示系统 - 2寸 LCD 屏幕接口
LCD 屏幕规格
本设计使用一块 上接 22pin 2.0 英寸 IPS 彩色 LCD 屏,分辨率为 240(H)×320(V) 像素,通过 8bit 8080 MCU 接口 进行通信,驱动芯片则采用 ST7789,工作电压为 3.3V。
外围电路设计
这部分实在是没什么好说的了......直接看图~值得一提的是,屏幕的供电与 ESP32-S3 模组的 3.3V 供电共用。
软件代码
■ 麻烦你先看看这里!
遇到大危机了呢?
在检查代码的时候发现了一个大问题,目前项目的 UI 界面是通过 GUI-Guider 生成的,检查了它的 License 之后,我发现通过 GUI-Guider 生成的代码只能在包含 NXP 芯片的硬件上进行使用?NXP 论坛上有一个相关的问答。这样一来,如果我想把这个番茄钟的代码开源,就必须放弃这套 UI 界面,改用 LVGL 框架从头重写界面才行......所以代码暂时还不能放出来了......不过不涉及这部分的代码还是可以介绍一下的!
简单修改一下
简单修改了一下代码......只能先这样解决了,等之后看看有没有其他的 LVGL 可视化编辑工具再重新设计一个界面吧?
gitee 仓库:https://gitee.com/LinYIN592/esp32s3-pomodoroclock-project/
■ 整体架构与多任务设计
代码编译环境与框架
本项目基于 ESP-IDF v5.4.1 框架开发,使用 FreeRTOS 实时操作系统管理多任务。代码采用 C/C++ 进行编写,主要模块封装在独立的类中,便于维护和扩展~
FreeRTOS 多任务架构
- GUI 更新任务:
app_gui_update_entry()运行在 Core 1,负责处理 LVGL 界面更新和消息队列,任务优先级为 8。 - 按键输入任务:
app_input_entry()运行在 Core 0,处理5个物理按键的事件响应,任务优先级为 8。 - 系统节拍任务:
app_tick_entry()运行在 Core 0,处理定时事件(1s、1min 等),任务优先级为 6。 - 系统初始化任务:
system_init_entry()一次性任务,完成所有硬件初始化和 NVS 数据加载。
事件驱动通信机制
APP_EventGroup 全局事件组
-
菜单按键中断事件
APP_KEY0_ISR_BIT -
模式按键中断事件
APP_KEY1_ISR_BIT -
开始按键中断事件
APP_KEY2_ISR_BIT -
增加按键中断事件
APP_KEY3_ISR_BIT -
减少按键中断事件
APP_KEY4_ISR_BIT -
Wi-Fi连接触发事件
APP_WIFI_CONNECT_STATUS -
Wi-Fi启用状态事件
APP_WIFI_ENABLE_STATUS -
Wi-Fi获取IP事件
APP_WIFI_GET_IP_STATUS -
SD卡挂载检测事件
APP_SDCARD_MOUNT_STATUS -
SNTP获取时间事件
APP_SNTP_GETTIME_STATUS -
1秒节拍事件
APP_TICK_1SEC_STATUS -
1分钟节拍事件
APP_TICK_1MIN_STATUS -
电池更新事件
APP_BATTERY_UPDATE_STATUS
GUI::updater 消息队列
-
时间显示更新消息
GUI_TIME_UPDATE_INFO -
菜单选项更新消息
GUI_MENU_UPDATE_INFO -
音频音量更新消息
GUI_AUDIO_UPDATE_INFO -
Wi-Fi状态更新消息
GUI_WIFI_UPDATE_INFO -
电池电量更新消息
GUI_BATTERY_UPDATE_INFO -
SD卡状态更新消息
GUI_SDCARD_UPDATE_INFO -
工作模式切换消息
GUI_MODE_UPDATE_INFO -
按键长按数据更新消息
GUI_STARTPRESS_DATA_UPDATE_INFO -
按键长按数据最大值定义
GUI_STARTPRESS_DATA_UPDATE_INFO_MAX
■ 状态机设计与 GUI 控制
状态模式 (State Pattern) 的应用
GUI 控制器采用状态模式设计,定义了 cpp_gui_controller_state 抽象基类,派生出四种具体状态:
cpp_gui_controller_state_normal:正常时钟显示状态cpp_gui_controller_state_pomodoro:番茄钟计时状态cpp_gui_controller_state_setting_pomodoro:番茄钟设置状态cpp_gui_controller_state_setting_audio_volume:音量设置状态
每个状态独立实现 enter()、exit()、time_update() 等方法,通过 GUI_Controller::change_state() 动态切换,确保UI行为与当前模式严格对应。
GUI 更新消息机制
通过 GUI::updater::send_xxx_update() 系列函数发送更新请求,由 app_gui_update_entry() 任务统一处理:
GUI_TIME_UPDATE_INFO:时间显示更新GUI_MENU_UPDATE_INFO:菜单选项更新GUI_WIFI_UPDATE_INFO:Wi-Fi状态更新GUI_BATTERY_UPDATE_INFO:电池电量更新GUI_SDCARD_UPDATE_INFO:SD卡状态更新GUI_MODE_UPDATE_INFO:工作模式切换GUI_STARTPRESS_DATA_UPDATE_INFO:长按进度条显示
设置界面的闪烁提示效果
在番茄钟设置状态中,通过回调函数指针 __filp_handler 实现当前编辑项的闪烁提示:
GUI::clock::toggle_flag_opacity():切换工作/休息标志透明度GUI::clock::toggle_hour_opacity():切换小时数字透明度GUI::clock::toggle_min_opacity():切换分钟数字透明度GUI::clock::toggle_sec_opacity():切换秒钟数字透明度
每次时间更新时调用当前编辑项对应的闪烁函数,通过改变LVGL对象的 LV_OPA_COVER/LV_OPA_TRANSP 透明度实现视觉反馈。
■ 番茄钟
状态机工作模式切换
番茄钟定义了 8 种工作模式,通过 iceclock_mode_t 枚举管理:
ICECLOCK_MODE_CLOCK:普通时钟模式ICECLOCK_MODE_SET_WORK_REST:设置工作/休息目标ICECLOCK_MODE_SET_HOUR_TIME:设置小时值ICECLOCK_MODE_SET_MIN_TIME:设置分钟值ICECLOCK_MODE_SET_SEC_TIME:设置秒钟值ICECLOCK_MODE_WORK:番茄工作计时ICECLOCK_MODE_REST:休息计时ICECLOCK_MODE_AUDIO_SET_VOLUME:音频音量设置
通过 IceClock::set_mode() 切换模式,进入工作/休息模式时会自动计算结束时间并播放提示音。
时间管理与自动切换
番茄钟计时核心逻辑:
- 计时开始:进入
ICECLOCK_MODE_WORK或ICECLOCK_MODE_REST时,记录当前时间到__run_time,计算__end_time。 - 每秒更新:
IceClock::update_run_time()在 1 秒节拍中递增运行时间。 - 超时检测:
IceClock::check_timeout()比较当前运行时间与结束时间,返回剩余秒数。 - 自动切换:当剩余时间为 0 时,自动从工作模式切换到休息模式(或反之),形成循环。
时间设置与编辑缓冲区
采用编辑缓冲区设计,避免直接修改实际时间值:
- 目标选择:
__edit_target_ptr指向当前编辑的时间结构(工作或休息时间)。 - 缓冲区:
__edit_target_buf作为编辑暂存区,所有修改先作用于缓冲区。 - 确认保存:通过
IceClock::save_edit_time(true)将缓冲区内容复制回目标,并写入 NVS。 - 防误触:分钟和秒钟值以 5 为单位增减(
±5),小时值以 1 为单位增减。
■ 按键交互与长按检测
五按键功能分配
五个物理按键通过中断触发事件,在 app_input_entry() 任务中统一处理:
- KEY0(菜单键):循环切换菜单选项(时钟→番茄钟设置→音频设置)
- KEY1(模式键):在不同设置页面间切换,或进入/退出设置模式
- KEY2(开始键):长按 1.5 秒开始/结束番茄钟,短按在音频模式切换播放/暂停
- KEY3(增加键):增加时间值/音量/切换上一首音频
- KEY4(减少键):减少时间值/音量/切换下一首音频
■ 音频系统与 SD 卡管理
音频播放器状态机机制
音频播放器通过 audio_player_callback() 回调函数响应状态变化:
AUDIO_PLAYER_CALLBACK_EVENT_IDLE:播放器空闲时,自动播放下一首(非番茄钟模式)AUDIO_PLAYER_CALLBACK_EVENT_PLAYING:开始播放音频AUDIO_PLAYER_CALLBACK_EVENT_PAUSE:暂停播放
在番茄钟工作/休息模式下,关闭音乐播放功能,仅在模式切换时播放系统提示音 S:/system/ding.mp3。
SD 卡插拔检测与播放列表
在 app_tick_callback()中,每 10 秒触发一次 APP_SDCARD_MOUNT_STATUS 事件。在 app_tick_entry() 任务中检查该事件位,检测到该事件后读取 SD_CD 引脚电平判断 SD 卡的插拔状态,若检测到 SD 卡则尝试挂载 SD 卡。挂载成功后,将会初始化音频播放器并检查位于 SD 卡根目录 S:/ 下的 MP3 / WAV 音频文件并更新播放列表。
音量分级控制
音量控制以 5% 为步进单位,范围 0-80%:
- 增加音量:
Audio::voice_volume(volume + 5),上限为 80%。 - 减少音量:
Audio::voice_volume(volume - 5),下限为 0%。
音量值保存到 NVS,重启后自动恢复上次设置。
■ Wi-Fi 连接与时间同步
Wi-Fi STA 模式状态管理
通过事件回调 wifi_sta_event_callback() 管理连接状态:
- 连接失败重试:最多重试
EXAMPLE_ESP_MAXIMUM_RETRY次,失败后停止 Wi-Fi - 连接成功:获取 IP 地址后设置
APP_WIFI_GET_IP_STATUS事件位 - 断线重连:每 30 秒检测一次,若未连接则触发
APP_WIFI_CONNECT_STATUS重新初始化 Wi-Fi
在 app_input_entry() 任务中,如果检测到 APP_WIFI_CONNECT_STATUS 事件且 Wi-Fi 功能未使能,则重新初始化 Wi-Fi 并启动,实现自动重连机制。
SNTP 网络时间同步
获取 IP 地址后自动启动 SNTP 时间同步:
- 初始化 SNTP:
SNTP::init(sntp_networktime_callback)设置回调函数 - 启动同步:
SNTP::start()开始获取网络时间 - 回调处理:
sntp_networktime_callback()收到时间后保存到 NVS - 系统时区:设置
setenv("TZ", "CST-8", 1)为北京时间
每 1 分钟将当前系统时间备份到 NVS,确保断电后仍有相对准确的时间。
NVS 配置数据加载
系统启动时从 NVS 加载所有配置:
- 时间数据:从
"sntp"命名空间加载最后保存的时间戳。 - 番茄钟设置:从
"iceclock"加载工作和休息时间。 - Wi-Fi配置:从
"wifi"加载 SSID 与密码。 - 用户偏好:从
"audio"和"gui"加载音量和亮度设置。
只有配置存在时才覆盖默认值,确保首次启动正常运行。
■ 其他杂项
自适应亮度调节
屏幕亮度目前仅能根据 USB 串口进行配置或由系统自动调整:
- 正常亮度:用户操作后,屏幕渐变为
GUI::get_default_light()(该默认值为初始化值或 NVS 保存值) - 休眠亮度:30 秒无操作后,通过
GUI::fade(5, 1000)实现屏幕渐暗 - 渐变效果:所有亮度变化使用 1 秒渐变动画(
GUI::fade(value, 1000)) - 亮度保存:用户调整的默认亮度保存到 NVS,重启后自动恢复
内存使用监控
可通过 log_get_free_heap_size() 函数监控内存使用:
- DRAM 内存:ESP32-S3内部 RAM,用于任务堆栈与数据存储。
- PSRAM 内存:外部 8MB PSRAM,主要用于 LVGL 帧缓冲和 Audio 缓冲区。
- 内存分配策略:音频播放列表使用
MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM标志,确保分配到 PSRAM。
开屏动画播放
通过 预先解码 + 二进制缓存 + 双缓冲渲染 策略,在首次启动时对 user_op.h 下的 USER_OP_DIR 地址中指定的 OP_PLAYER_IMAGE_DECODER_NUM 张 JPEG 图片进行重编码(图片命名格式为 P%04lu.jpg),生成 cache.bin 与 cache.info 缓存文件。后续启动直接播放缓存文件与 USER_OP_DIR 路径下的 USER_OP_VIDEO_NAME mp3/wav 格式的音频文件实现一个简单的开屏动画播放功能。帧率大概能稳定在 20 FPS 左右?播放完毕通过回调进入 LVGL 界面。
这个功能基本上可以说是灵机一动搓出来的,本来是想实现 mp4 视频 的播放功能,但似乎这样的库大部分都不是开源库?只能自己简单手搓一个了,目前看起来还是蛮简陋的......
实物图
惯例的实物展示环节!
正面图
背面图
开机动画(bushi)
碎碎念
水不动字数了......你可能注意到这个项目简介没有介绍组装流程呢......这段时间整理代码整理到头痛,这个番茄钟的亚克力外壳也相对简单,就先省略这部分先?等我歇会后续再补充(挖坑)
如你所见,当前设计里好像没有直接检测 Type-C 是否供电的方法?不过我在编写文档时想到了一个方法:保存一段时间的电池电压数据,对比它的变化趋势,不就能判断现在是电池供电还是 Type-C 供电了吗!(我真聪明.jpg)。这个功能也许大概可能会于不久的将来实现?大概?感觉调试的工作量会很多呢......
设计图
未生成预览图,请在编辑器重新保存一次BOM
暂无BOM3D模型
| 序号 | 文件名称 | 下载次数 |
|---|---|---|
暂无数据 | ||
附件
| 序号 | 文件名称 | 下载次数 |
|---|---|---|
暂无数据 | ||
克隆工程知识产权声明&复刻说明
本项目为开源硬件项目,其相关的知识产权归创作者所有。创作者在本平台上传该硬件项目仅供平台用户用于学习交流及研究,不包括任何商业性使用,请勿用于商业售卖或其他盈利性的用途;如您认为本项目涉嫌侵犯了您的相关权益,请点击上方“侵权投诉”按钮,我们将按照嘉立创《侵权投诉与申诉规则》进行处理。
请在进行项目复刻时自行验证电路的可行性,并自行辨别该项目是否对您适用。您对复刻项目的任何后果负责,无论何种情况,本平台将不对您在复刻项目时,遇到的任何因开源项目电路设计问题所导致的直接、间接等损害负责。









