本文代码绝大部分都通过 AI 完成

基于 ESP-IDF 框架和 LVGL 图形库开发的 ESP32-S3 智能时钟,集成了网络对时、温湿度检测和电池电量显示等功能。下载链接:https://higa.teracloud.jp/share/11e2ac0228256c16

功能特点

  • 实时时钟显示:使用等宽字体显示时间(时:分: 秒 年 - 月 - 日)
  • 网络对时:通过 GPIO18 按键触发 WiFi 连接,使用 SNTP 协议从 NTP 服务器同步时间,同步完成后自动断开 WiFi 节省电量
  • 温湿度监测:通过 SHTC3 温湿度传感器实时监测环境温度和湿度,并在界面顶部显示
  • 电池电量显示:通过 ADC 采样电池电压,计算并显示电量百分比,配有电池图标
外观
外观

硬件平台

  • 主控芯片:ESP32-S3
  • 显示屏:电阻式 LCD(RLCD),分辨率 400×300,黑白双色
  • 温湿度传感器:SHTC3,I2C 接口(SDA=GPIO14,SCL=GPIO13)
  • 功能按键:GPIO18(触发网络对时)
  • 电池电压检测:ADC 通道采样

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
demo/
├── CMakeLists.txt # 根级CMake构建文件
├── main/
│ ├── CMakeLists.txt # main组件构建文件
│ ├── main.cpp # 程序入口
│ ├── idf_component.yml # 组件依赖声明
│ └── user_config.h # 用户配置(屏幕尺寸等)
├── components/
│ ├── app_bsp/ # LVGL BSP 封装
│ │ ├── lvgl_bsp.h/.cpp # LVGL初始化与任务管理
│ │ └── CMakeLists.txt
│ ├── esp_wifi_bsp/ # WiFi驱动封装
│ │ ├── esp_wifi_bsp.h/.c # WiFi连接/断开/状态检测
│ │ └── CMakeLists.txt
│ ├── port_bsp/ # 底层硬件抽象层
│ │ ├── display_bsp.h/.cpp # RLCD显示屏驱动
│ │ ├── i2c_bsp.h/.cpp # I2C总线封装
│ │ ├── i2c_equipment.h/.cpp # SHTC3传感器驱动
│ │ ├── adc_bsp.h/.cpp # ADC电池电压采样
│ │ ├── gpio_bsp.h/.cpp # GPIO按键检测
│ │ └── CMakeLists.txt
│ └── user_app/ #用户应用层(核心)
│ ├── user_app.h/.cpp #主时钟应用逻辑
│ ├── cour.c/h #Courier等宽字体(50px)
│ └── CMakeLists.txt
└── managed_components/
└── lvgl__lvgl/ # LVGL图形库(V9)

代码原理详解

程序启动流程 (main.cpp)

1
2
3
4
5
6
7
8
9
10
11
12
13
app_main()
├── UserApp_AppInit() # 初始化外设和传感器
│ ├── 设置时区 CST-8
│ ├── espwifi_Init() # WiFi协议栈初始化(不连接)
│ ├── Adc_PortInit() # ADC初始化(电池采样)
│ ├── Gpio_PortInit() # GPIO18按键初始化
│ ├── I2cbus.Init() # I2C总线初始化 (SDA=14, SCL=13)
│ ├── new Shtc3Port() # 挂载SHTC3传感器
│ └── settimeofday() # 设置初始时间
├── RLCD_Init() # 显示屏初始化
├── Lvgl_PortInit() # LVGL初始化(400×300)
├── UserApp_UiInit() # 构建LVGL界面
└── UserApp_TaskInit() # 启动时钟更新定时器

LVGL 界面布局 (user_app.cpp)

屏幕分辨率为 400×300,使用 6 个 lv_label 标签组成界面:

1
2
3
4
5
6
7
8
9
10
11
┌─────────────────────────────────────────────────┐
│ [WiFi off] [25.3°C] [58.6%] [🔋 87%] │ ← 状态栏(顶部)
│ │
│ │
│ 14:28:35 │ ← 时间(Courier字体,50px)
│ │
│ │
│ 2024-07-01 │ ← 日期
│ │
│ │
└─────────────────────────────────────────────────┘
标签位置说明
status_label(10, 10)WiFi 状态(WiFi off/Connecting/Syncing/Sync done)
temp_label(120, 10)温度显示(SHTC3 读取)
humi_label(220, 10)湿度显示(SHTC3 读取)
battery_label(310, 10)电量 + 电池图标(LV_SYMBOL_BATTERY_FULL
time_label居中时间显示(Courier 等宽字体 50px)
date_label(0, 160)日期显示(居中)

时钟更新机制

使用 LVGL 定时器 lv_timer_create() 每秒执行一次回调 clock_update_cb

1
2
// 1s周期触发
clock_timer = lv_timer_create(clock_update_cb, 1000, NULL);

回调函数内部逻辑:

  1. 调用 time() 获取系统时间戳 → localtime_r() 解析为时分秒年月日
  2. snprintf() 格式化字符串后写入各 lv_label
  3. 读取 SHTC3 传感器 → 更新温湿度标签
  4. ADC 采样 → 更新电池电量(附带 LV_SYMBOL_BATTERY_FULL 图标)
  5. 按键检查 → 网络对时状态机

按键触发网络对时流程(状态机)

核心设计原则:WiFi 按需连接,同步完成立即断开以降低功耗。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
GPIO18按键按下


sync_in_progress = true
status = "Connecting..."
espwifi_Connect() → SNTP 初始化

├-> WiFi连接成功(got IP)
│ status = "Syncing time..."
│ 等待约5秒(确保 SNTP 同步完成)
│ │
│ ∨
│ status = "Sync done"
│ espwifi_Disconnect() ← 立即断开WiFi
│ 等待2秒显示成功
│ status = "WiFi off"
│ sync_in_progress = false

└-> 10秒超时未连接
status = "Connect failed"
espwifi_Disconnect()
等待2秒显示失败
status = "WiFi off"
sync_in_progress = false
网络校时
网络校时

SNTP 配置sntp_time_sync):

  • NTP 服务器:ntp.aliyun.com(阿里云国内节点)
  • 时区:CST-8(东八区 北京时间)
  • 模式:SNTP_OPMODE_POLL(轮询模式)

WiFi 驱动设计 (esp_wifi_bsp.c)

采用 两阶段初始化 分离初始化和连接:

函数作用是否耗电
espwifi_Init()初始化 NVS/Netif/WiFi 协议栈,写入 SSID + 密码初始化阶段运行
espwifi_Connect()调用 esp_wifi_start() 启动射频并连接 AP开启射频(耗电)
espwifi_Disconnect()调用 esp_wifi_stop() 关闭射频关闭射频(省电)

WiFi 状态通过事件回调 event_handler 维护:

  • IP_EVENT_STA_GOT_IPwifi_connected = true
  • WIFI_EVENT_STA_DISCONNECTEDwifi_connected = false

SHTC3 温湿度读取 (i2c_equipment.cpp)

SHTC3 通过 I2C 通信,设备地址 0x70,时钟速度 400kHz:

1
2
3
4
5
6
7
8
读取流程:
1. 发送测量命令 0x7866(Clock Stretching Enabled, Temp First)
2. 等待测量完成(Clock Stretching 由硬件自动等待)
3. 读取6字节:[温度高字节] [温度低字节] [CRC] [湿度高字节] [湿度低字节] [CRC]
4. CRC-8校验(多项式0x31,初始值0xFF)
5. 转换公式:
温度(℃) = -45 + 175 × rawValue / 65535
湿度(%) = 100 × rawValue / 65535

电池电量检测

通过 ADC 采样电池电压,Adc_GetBatteryLevel() 内部将 ADC 采样值映射为百分比。

界面显示时使用 LVGL 内置符号 LV_SYMBOL_BATTERY_FULL 作为电池图标前缀:

1
2
3
snprintf(battery_buf, sizeof(battery_buf),
LV_SYMBOL_BATTERY_FULL " %d%%", level);
// 输出示例:"🔋87%"

显示驱动与 LVGL 渲染

  • 显示屏类型:电阻式 LCD(RLCD),黑白双色
  • 颜色转换:LVGL 输出的 RGB565 颜色值通过阈值 0x7fff 二值化(< 0x7fff 为黑色,否则为白色)
  • 渲染流程
    1. LVGL 内部将界面渲染到 color_map 缓冲区
    2. Lvgl_FlushCallback 遍历像素 → 调用 RLCD_SetPixel() 写入屏内缓存
    3. RLCD_Display() 一次性刷新整屏 → lv_disp_flush_ready() 通知 LVGL 完成

字体说明

项目使用自定义等宽字体 cour.c

  • 来源:Courier New(等宽字体,适合时间显示,数字宽度一致避免抖动)
  • 尺寸:50px
  • 位深:1 bpp(黑白,匹配 RLCD 双色屏,最小化 Flash 占用)
  • 字符范围:ASCII 32-127(空格到~,覆盖数字、字母、标点)
  • 生成参数--bpp 1 --size 50 --no-compress --stride 1 --align 1

编译与烧录

1
2
3
4
5
6
7
8
9
10
11
# 配置ESP-IDF环境
cd demo

# 选择目标芯片
idf.py set-target esp32s3

# 编译
idf.py build

# 烧录+监视串口
idf.py flash monitor

低功耗优化

  1. WiFi 按需连接:只有按键触发才启动射频,同步完成后立即 esp_wifi_stop() 关闭
  2. SNTP 单次同步:使用 POLL 模式而非持续监听,避免周期性唤醒
  3. I2C 按需读取:每秒读取一次 SHTC3,而非持续采样
  4. 1bpp 字体:最小化 Flash 占用和渲染开销

下步目标

利用股票和天气 api 获取信息并显示
通过 WiFi 或者蓝牙与附近通信,更新同步信息

网站地图 | 状态监测 | 全球节假日日历 | 皮带张力测试 | File Server | 博友圈 | 博客说
Copyright 2022-2026 | Powered by Hexo 7.3.0 & Stellar 1.33.1
总访问量次 |