站内搜索
更多
【ESP32-S3】番茄钟
专业版

【ESP32-S3】番茄钟

简介

本项目是一个基于ESP32S3的番茄钟设计,配备一块2.0寸的LCD屏幕,支持本地音频播放和SNTP网络校时,为你提供一个专注工作与休息管理的小工具!

简介:本项目是一个基于ESP32S3的番茄钟设计,配备一块2.0寸的LCD屏幕,支持本地音频播放和SNTP网络校时,为你提供一个专注工作与休息管理的小工具!

开源协议

GPL 3.0

创建时间:2026-02-04 04:20:28更新时间:2026-02-13 23:04:47

描述

项目简介

本项目设计了一个基于 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 Flash8 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_INFOWi-Fi状态更新
  • GUI_BATTERY_UPDATE_INFO:电池电量更新
  • GUI_SDCARD_UPDATE_INFOSD卡状态更新
  • 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_WORKICECLOCK_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 时间同步:

  1. 初始化 SNTPSNTP::init(sntp_networktime_callback) 设置回调函数
  2. 启动同步SNTP::start() 开始获取网络时间
  3. 回调处理sntp_networktime_callback() 收到时间后保存到 NVS
  4. 系统时区:设置 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.bincache.info 缓存文件。后续启动直接播放缓存文件与 USER_OP_DIR 路径下的 USER_OP_VIDEO_NAME mp3/wav 格式的音频文件实现一个简单的开屏动画播放功能。帧率大概能稳定在 20 FPS 左右?播放完毕通过回调进入 LVGL 界面。

这个功能基本上可以说是灵机一动搓出来的,本来是想实现 mp4 视频 的播放功能,但似乎这样的库大部分都不是开源库?只能自己简单手搓一个了,目前看起来还是蛮简陋的......

实物图

惯例的实物展示环节!

正面图

背面图

开机动画(bushi

碎碎念

水不动字数了......你可能注意到这个项目简介没有介绍组装流程呢......这段时间整理代码整理到头痛,这个番茄钟的亚克力外壳也相对简单,就先省略这部分先?等我歇会后续再补充(挖坑

如你所见,当前设计里好像没有直接检测 Type-C 是否供电的方法?不过我在编写文档时想到了一个方法:保存一段时间的电池电压数据,对比它的变化趋势,不就能判断现在是电池供电还是 Type-C 供电了吗!(我真聪明.jpg)。这个功能也许大概可能会于不久的将来实现?大概?感觉调试的工作量会很多呢......

设计图

未生成预览图,请在编辑器重新保存一次

BOM

暂无BOM

3D模型

序号文件名称下载次数
暂无数据

附件

序号文件名称下载次数
暂无数据
克隆工程
添加到专辑
0
0
分享
侵权投诉
知识产权声明&复刻说明

本项目为开源硬件项目,其相关的知识产权归创作者所有。创作者在本平台上传该硬件项目仅供平台用户用于学习交流及研究,不包括任何商业性使用,请勿用于商业售卖或其他盈利性的用途;如您认为本项目涉嫌侵犯了您的相关权益,请点击上方“侵权投诉”按钮,我们将按照嘉立创《侵权投诉与申诉规则》进行处理。

请在进行项目复刻时自行验证电路的可行性,并自行辨别该项目是否对您适用。您对复刻项目的任何后果负责,无论何种情况,本平台将不对您在复刻项目时,遇到的任何因开源项目电路设计问题所导致的直接、间接等损害负责。

底部导航