内容介绍
内容介绍
大家好,我是忙碌的死龙,是个奔四的大叔,平时喜欢玩开发板和参加活动。
一、项目介绍
这是一个基于CircuitPython的天气时钟项目,使用CircuitPython的官方库完成对应的功能。整体难度比较低,功能也不复杂,就是使用ESP32S3模块驱动st7789屏幕,并在屏幕上显示背景图片,并获取网络时间和天气信息,然后显示在屏幕上。
二、市场应用介绍
目前市面上有很多成品的ESP8266天气小电视,大家也都很喜欢。但是这个产品是使用Arduino开发的。我不会Arduino,会一点C语言和python,所以还是倾向使用ESP32来做这个产品,ESP32S3性能强劲,也可以扩展更多的功能,只是需要投入很多时间来做。
三、项目设计思路
原先只是想做一个天气时钟,结果折腾来折腾去,做成手表开发板了。去掉了RTC模块,屏幕 从1.47寸升级到1.69寸触屏,增加了锂电池充电和姿态检测、一键开关机电路等内容。结果硬件搞完发现原来制定的软件功能没时间完成了,就只实现了基本的天气时钟功能。
项目整体还是用ESP32-S3模组作为主控,1.69寸触屏作为显示屏(触摸功能还在搞)显示图片和天气信息、时钟等内容。
四、软件框架
直接使用CircuitPython的自带库实现了目前的所有功能。
使用displayio和相关库完成了屏幕驱动和屏幕元素组的绘制,调用起来非常简单,代码也很容易看懂。使用ntp库实现了网络校时功能,用阿里云的ntp校时服务器,代码很少。使用request库从高德网页服务API获取到最新的天气信息,并更新屏幕元素内容。
五、主要器件介绍
ESP32-S3-N8R8是一款性能强大的Wi-Fi和蓝牙模组,适用于物联网和嵌入式应用。它采用RISC-V处理器,主频高达240MHz,能够实现高速稳定的无线连接。此外,它还集成了丰富的外设,如GPIO、UART、SPI、I2C等,提供了多种扩展接口。它还可以运行CircuitPython,快速实现创客的想法。
软件部分:为什么用 CircuitPython来开发
CircuitPython是一种用于嵌入式系统的Python解释器。它专门为微控制器和单片机设计,提供了一个简化的开发环境,使得使用Python语言编写硬件控制程序变得更加容易。CircuitPython支持许多不同的硬件平台,包括Adafruit的许多开发板和其他第三方开发板。它具有易于使用的API,使得与各种传感器、执行器和其他外设进行交互变得简单。CircuitPython还提供了许多内置的库,用于处理常见的硬件操作和通信协议。您可以使用CircuitPython来构建各种物联网设备、机器人、传感器节点等嵌入式系统。它是一个非常有用的工具,使得硬件编程对于初学者和专业人士来说都更加易于上手。
六、原理图解析以及实际制作是碰到的问题
6.1 电源系统
考虑到esp32和外围系统的功耗问题,使用了一键开关机电路。该方案参考网络上的一键开关机电路。使用按键和esp32的io控制LDO芯片的en引脚,完成电源开启和关闭的功能。
在关机状态下,Q2关断,POW_EN信号被R2拉低成低电平,LDO为关断状态,理论耗电低于2uA。当按下KEY后,Q2打开,将POW_EN拉高,开启LDO。LDO开启后,esp32的MCU_PWR_EN引脚给高电平信号给Q1(一直维持高电平,除非需要关机),Q1开启。此时完成电源开机。
在开机状态下,可以设置成长按KEY按键后,MCU_PWR_EN引脚给低电平信号给Q1,关闭Q2这个MOS,POW_EN信号被R2拉低成低电平,LDO为关断状态。
在开机状态下,KEY按键还可以配置单击和双击的功能,实现一个按键多种用途。
6.2 锂电池充电和电源切换电路
通过以下电路实现电源切换电路和防反接功能,VBUS 有5V输入时,Q3的VGS小于开启电压,关闭MOS。VBUS没电时通过VBAT供电,Q3寄生二极管导通,VINH=VBAT,开启MOS。D2实现VBUS和GND的防反接,Q3实现VBAT和GND的防反接功能。
锂电池充电使用常见的充电方案,电池电压检测建议添加一路MOS来控制,平时关闭,需要检测电池时打开。这样可以节省一些电。
关闭电源,拿掉ADC采样电阻的情况下,关断电流不到3uA,已经很舒服了。
6.3 触摸屏驱动部分
由于考虑做手表,淘宝上的1.69寸触摸屏就是最佳选择。ST7789V驱动IC,esp32的lv_driver带了驱动,可以直接使用,触摸屏是CST816D,需要移植驱动才可以用。分辨率是240x280彩色屏幕,接插座选FPC 18pin 前翻下接的0.5mm座子就行了。
就是这款触摸屏
由于屏幕和触摸大多数时候都是同时初始化的,所以触屏的RST引脚和屏幕驱动的RST引脚连在一起,使用一个引脚驱动就可以了。一般情况下,ST7789的CS引脚建议使用独立引脚控制,而不是直接接地,拉低CS脚。若引脚不够,可以CS引脚接地,但是SPI Mode需要切换到SPI Mode 3模式。常规的Mode 0模式,必须接CS脚控制屏幕。由于ESP32S3的IO电压是3.3V的,也懒得再折腾一个独立的1.8V电源轨道,屏幕电压和IO电压均使用3.3V。
背光直接使用数字三极管控制即可 。
6.4 抬腕亮屏部分
由于前期设计时未做好调查,姿态检测方案选的是lis3dh芯片,该芯片仅支持3轴加速度检测,想做抬腕亮屏比较困难。重新找便宜的方案后,选择LSM6DS3TR-C作为抬腕亮屏的最新解决方案,原PCB已打样,只能考虑飞线解决,由于时间关系,也没来得及做相关的驱动和代码。
6.5 USB和ESP32部分
没啥好说的,就是常规做法,USB TypeC 16pin座子,ESD保护,ESP32模组注意一下特殊IO的上电时序就行了。
6.6 PCB部分
PCB也没啥好说的,拉拉通而已,反正能用就行。
七、源代码讲解
7.1 绘制天气时钟的UI
由于这个屏幕用的是st7789驱动,我们只需要按原理图的配置,将屏幕的CS,DC,SCL,SDA,RST和背光信号设置成正确的引脚,然后使用adafruit_st7789库直接初始化屏幕即可。初始化屏幕时需要指定屏幕分辨率,偏移值,旋转角度等内容。然后将背景图片添加到图像组里,把时间信息和天气信息的标签添加到图像组里就可以了。
import board
# 导入displayio库(内置的)
import busio
import digitalio
import displayio
# 导入外部库adafruit_imageload,如果没有就在教程附件下载
import adafruit_imageload
# 导入外部库adafruit_display_text里的lable,用于显示标签
from adafruit_display_text import label
# 导入外部库adafruit_bitmap_font里的lable
from adafruit_bitmap_font import bitmap_font
from adafruit_st7789 import ST7789
import pwmio
displayio.release_displays()
spi = busio.SPI(board.IO10, MOSI=board.IO11)
tft_cs = board.IO9
tft_dc = board.IO46
pwm = pwmio.PWMOut(board.IO16)
pwm.duty_cycle = 28000
display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=board.IO12)
display = ST7789(display_bus, width=280, height=240, colstart=0, rowstart=20, rotation=270)
# 创建本例程里的唯一图像组
group = displayio.Group()
# 加载图片
image, palette = adafruit_imageload.load("/pic/tqbg2.png")
# 是否开启透明
palette.make_transparent(1)
# 创建图片布局
grid = displayio.TileGrid(image, pixel_shader=palette)
# 将图片布局添加到图像组,由于是第一个添加的,默认是最下层
group.append(grid)
# 显示当前图像组
display.show(group)
# 加载字体并定义字体颜色为黑色
font = bitmap_font.load_font("/font/sytq_16.pcf")
nun_font = bitmap_font.load_font("/font/DingTalk_ncn_60.pcf")
color = 0x000000
# 初始化日期标签并设置x,y轴绘图坐标,然后将标签添加到图像组
date = label.Label(font, text="10月3日", color=color)
date.x = 50
date.y = 30
group.append(date)
# 初始化星期标签并设置x,y轴绘图坐标,然后将标签添加到图像组
week = label.Label(font, text="周二", color=color)
week.x = 150
week.y = 30
group.append(week)
# 初始化时间标签并设置x,y轴绘图坐标,然后将标签添加到图像组
timeL = label.Label(nun_font, text="18:20", color=color)
timeL.x = 20
timeL.y = 96
group.append(timeL)
# 初始化温度标签并设置x,y轴绘图坐标,然后将标签添加到图像组
temp = label.Label(font, text="30°", color=color)
temp.x = 70
temp.y = 160
group.append(temp)
# 初始化天气标签并设置x,y轴绘图坐标,然后将标签添加到图像组
tempzh = label.Label(font, text="晴", color=color)
tempzh.x = 110
tempzh.y = 160
group.append(tempzh)
# 显示修改后的图像组
display.show(group)
7.2 连接wifi并获取ntp时间,天气信息
由于板子上没有硬件RTC时钟部分电路,esp32在完全关机后也会失去时间,我们使用网络校时的方式获取时间。直接使用
adafruit_ntp库更新RTC时间就行了。代码如下
# 导入os库,用来获取wifi信息
import os
# 导入rtc库,实现RTC时钟
import rtc
# 导入wifi、time库备用
import wifi
import time
# 导入网络库备用
import ssl
import socketpool
import adafruit_ntp
import adafruit_requests
# 使用os.getenv函数,从setting.toml文件里获取wifi ssid和密码
ssid = os.getenv("CIRCUITPY_WIFI_SSID")
password = os.getenv("CIRCUITPY_WIFI_PASSWORD")
# 连接到 wifi
print("Connecting to", ssid)
wifi.radio.connect(ssid, password)
print("Connected to", ssid)
# 使用adafruit_ntp.NTP函数初始化ntp服务,第一个函数是确定网络连接端口,
# 第二个函数设置时区,中国是+8时区,第三个函数用来指定ntp服务器地址
pool = socketpool.SocketPool(wifi.radio)
ntp = adafruit_ntp.NTP(pool, tz_offset=8, server="ntp.aliyun.com")
# 使用ntp时间更新系统时间
rtc.RTC().datetime = ntp.datetime
def get_wday(wday):
if (wday == 0):
return "周一"
elif (wday == 1):
return "周二"
elif (wday == 2):
return "周三"
elif (wday == 3):
return "周四"
elif (wday == 4):
return "周五"
elif (wday == 5):
return "周六"
elif (wday == 6):
return "周日"
7.3 获取天气信息并更新
获取天气信息使用的是高德API,需要自行去高德注册账户获取相关的key。
然后通过API返还json数据,并解析相关数据更新屏幕即可。
# 初始化requests对象
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())
def get_weather():
# 设置城市id
city = "450900"
# 这个函数使用的是高德API,使用该API需要先去注册相关账户,申请key。
key = "1xxx"
# 拼接天气链接url
getweather_url = "https://restapi.amap.com/v3/weather/weatherInfo?city=" + city + "&key=" + key
# 获取天气json数据
response = requests.get(getweather_url)
json_resp = response.json()
# 关闭连接
response.close()
# 解析json数据,并返回温度和天气信息
for da in json_resp["lives"]:
#print(da["temperature"])
return da["temperature"], da["weather"]
7.4 主循环代码负责更新时钟和天气信息
这就没啥好说的了,就是开启个主循环更新下时间和天气信息罢了。
# 先创建一个status变量,用来在设备启动时获取天气信息
status = "boot"
# 主循环
while True:
# 每秒获取一次本地RTC时间
t = time.localtime()
# 首次启动或者本地RTC时间的分钟属性为0时,更新日期标签和天气标签
if (status == "boot" or t.tm_min == 0):
# 更新日期标签
date.text = "%d月%d日" % (t.tm_mon, t.tm_mday)
week.text = get_wday(t.tm_wday)
# 获取天气信息
str_t, str_tz = get_weather()
# 更新温度标签
temp.text = "%s°" % (str_t)
# 更新天气标签
tempzh.text = str_tz
status = "updated"
# 每隔1秒 更新一次时钟标签,用于动态显示
if (t.tm_sec % 2 == 0):
timeL.text = "%02d:%02d" % ( t.tm_hour, t.tm_min)
timeL.color = 0x000000
else:
timeL.text = "%02d %02d" % ( t.tm_hour, t.tm_min)
timeL.color = 0xD9D7C9
# 刷新屏幕
display.show(group)
# 休眠1秒
time.sleep(1)
八、功能展示和说明
设备开机上电后会启动CircuitPython固件,然后再运行python代码。代码运行后先初始化屏幕,并显示背景图片和默认标签信息。正常联网后会先更新ntp校时信息,正确获得时间后更新屏幕上的时间标签。然后判断是否首次运行,是的则更新天气信息,天气信息正确获取后,就立即更新天气标签。然后每隔两秒闪烁一下时间标签并更新时间。
九、项目总结和活动体会
在这个活动中,我使用CircuitPython和ESP32-S3开发板构建了一个天气时钟界面项目。我配置好了wifi的连接,并使用API获取了实时天气数据。将天气信息显示在屏幕上,并添加了当前时间和日期的显示功能。
这个项目对于我们来说是一个很好的学习机会。我学到了如何使用CircuitPython的库和功能,以及如何与外部设备进行交互。我也学会了处理网络连接和调试代码的技巧。尽管在项目开发过程中遇到了一些挑战,但我通过搜索和社区提问成功地解决了问题。我对项目的成果感到满意,我期待着在未来继续进行更多有趣且具有挑战性的Esp32项目。
软硬件
电路图
附件下载
esp32_clock.kicad_sch
原理图
esp32_clock.zip
源码
adafruit-circuitpython-yd_esp32_s3_n16r8-en_US-8.2.7.uf2
使用的CircuitPython固件,需先刷BL
团队介绍
广西玉林市虾米科技有限公司
评论
0 / 100
查看更多