【得捷电子Follow me第2期】ESP32-S3 TFT Feather 实现一个日历&时钟
[复制链接]
本帖最后由 ctrlshift12138 于 2023-11-15 23:48 编辑
第一部分
短视频
第二部分
任务1:控制屏幕显示中文
########################################################################
# 任务1:控制屏幕显示中文
########################################################################
import board
import displayio
from adafruit_display_text import label
from adafruit_bitmap_font import bitmap_font
import time
display = board.DISPLAY
group = displayio.Group()
oppo_sans_meduim = bitmap_font.load_font("font/OPlusSans3.0-Medium-20.bdf") # 载入字体
green_color = 0x00CA39 # 绿色
# 文本标签1
text_label_1 = label.Label(font = oppo_sans_meduim, text = "DigiKey得捷 × EEWorld", color = green_color)
text_label_1.x = 8
text_label_1.y = 19
# 文本标签2
text_label_2 = label.Label(font = oppo_sans_meduim, text = "Follow me", color = green_color)
text_label_2.x = 65
text_label_2.y = 55
# 文本标签3
text_label_3 = label.Label(font = oppo_sans_meduim, text = "任务1:控制屏幕显示中文", color = green_color)
text_label_3.x = 8
text_label_3.y = 93
# 显示文本标签
group.append(text_label_1)
group.append(text_label_2)
group.append(text_label_3)
display.show(group)
while True:
pass
ESP32 S3 TFT Feather 原生只有英文字库。想要显示中文,需要调用 adafruit_bitmap_font 库,来显示点阵格式的中文。如何生成adafruit_bitmap_font支持的字体格式文件可以看上面的视频。
驱动屏幕:
display = board.DISPLAY
CircuitPython库函数十分强大,只需要一行代码即可控制屏幕。
显示汉字:
oppo_sans_meduim = bitmap_font.load_font("font/OPlusSans3.0-Medium-20.bdf") # 载入字体
green_color = 0x00CA39 # 绿色
# 文本标签1
text_label_1 = label.Label(font = oppo_sans_meduim, text = "DigiKey得捷 × EEWorld", color = green_color)
text_label_1.x = 8
text_label_1.y = 19
使用 bitmap_font.load_font 函数来载入字体文件,label.Label 创建一个文本标签,font、text、color、x、y分别代表文本标签的字体、文本内容、颜色、x坐标、y坐标属性。
group = displayio.Group()
group.append(text_label_1)
display.show(group)
displayio.Group() 表示创建一个显示group,
group.append(text_label_1),将文本标签 text_label_1 添加到显示 group,
display.show(group),在屏幕上将group中的内容显示出来
任务2:网络功能使用
创建热点
CircuitPython 创建热点十分简单,只需要一行代码就能搞定。
########################################################################
# 任务2:网络功能使用
# part1:创建热点
########################################################################
import board
import displayio
from adafruit_display_text import label
from adafruit_bitmap_font import bitmap_font
import wifi
display = board.DISPLAY
group = displayio.Group()
my_ssid = "ESP32_S3" # 热点 SSID
my_password = "12345678" # 热点 password
wifi.radio.start_ap(ssid=my_ssid, password=my_password) # 创建热点
# 载入字体
oppo_sans_meduim = bitmap_font.load_font("font/OPlusSans3.0-Medium-20.bdf")
green_color = 0x00CA39 # 绿色
# 文本标签1
text_label_1 = label.Label(font = oppo_sans_meduim, text = "任务2:网络功能使用", color = green_color)
text_label_1.x = 24
text_label_1.y = 14
# 文本标签2
text_label_2 = label.Label(font = oppo_sans_meduim, text = "创建热点", color = green_color)
text_label_2.x = 80
text_label_2.y = 40
# 文本标签3
text_label_3 = label.Label(font = oppo_sans_meduim, text = "SSID:"+my_ssid, color = green_color)
text_label_3.x = 47
text_label_3.y = 66
# 文本标签4
text_label_4 = label.Label(font = oppo_sans_meduim, text = "password:"+my_password, color = green_color)
text_label_4.x = 24
text_label_4.y = 92
# 显示文本标签
group.append(text_label_1)
group.append(text_label_2)
group.append(text_label_3)
group.append(text_label_4)
display.show(group)
while True:
pass
wifi.radio.start_ap(ssid=my_ssid, password=my_password)
wifi.radio.start_ap()函数有两个入参,ssid是热点的名称,password是热点的密码。
连接WiFi
连接WiFi也十分简单,同样只需要一行代码就能搞定。
########################################################################
# 任务2:网络功能使用
# part1:连接WiFi
########################################################################
import board
import displayio
from adafruit_display_text import label
from adafruit_bitmap_font import bitmap_font
import time
import os
import wifi
import socketpool
import adafruit_requests
display = board.DISPLAY
group = displayio.Group()
# 使用 os.getenv 函数,从 setting.toml 文件里获取wifi ssid 、密码
my_ssid = os.getenv("CIRCUITPY_WIFI_SSID") # 获取 WiFi 的 SSID
my_password = os.getenv("CIRCUITPY_WIFI_PASSWORD") # 获取 WiFi 的 password
wifi.radio.connect(ssid=my_ssid, password=my_password) # 连接WiFi
oppo_sans_meduim = bitmap_font.load_font("font/OPlusSans3.0-Medium-20.bdf") # 载入字体
green_color = 0x00CA39 # 绿色
# 文本标签1
text_label_1 = label.Label(font = oppo_sans_meduim, text = "任务2:网络功能使用", color = green_color)
text_label_1.x = 24
text_label_1.y = 14
# 文本标签2
text_label_2 = label.Label(font = oppo_sans_meduim, text = "连接到WiFi网络", color = green_color)
text_label_2.x = 47
text_label_2.y = 50
# 显示文本标签
group.append(text_label_1)
group.append(text_label_2)
# 获取 bing 首页的内容
url = "http://www.bing.com"
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(socket_pool=pool)
while True:
try:
print("Fetching text from %s" % url)
response = requests.get(url) # 获取网页内容
print("-"*40)
print("Text Response: ", response.text) # 将网页内容通过串口打印出来
print("-"*40)
response.close()
time.sleep(60)
except Exception as e:
print("Error:\n",str(e))
time.sleep(10)
my_password = os.getenv("CIRCUITPY_WIFI_PASSWORD")
my_ssid = os.getenv("CIRCUITPY_WIFI_SSID")
os.getenv()函数是从根目录下的 setting.toml文件中获取配置参数。
setting.toml文件内容如下:
CIRCUITPY_WIFI_SSID = "1708"
CIRCUITPY_WIFI_PASSWORD = "tsl199725?"
任务3:控制WS2812B
########################################################################
# 任务3:控制WS2812B
########################################################################
import board
import displayio
import time
from adafruit_display_text import label
from adafruit_bitmap_font import bitmap_font
from digitalio import DigitalInOut, Direction,Pull
import neopixel
from adafruit_led_animation.animation.blink import Blink
from adafruit_led_animation.color import JADE, BLACK, ORANGE,GOLD,OLD_LACE,AQUA
display = board.DISPLAY
group = displayio.Group()
pixel_pin = board.NEOPIXEL # 初始化neopixel引脚
num_pixels = 1 # 设置灯珠数量为1
pixels = neopixel.NeoPixel(pin = pixel_pin, # 初始化 neopixel
n=num_pixels,
brightness=0.2,
auto_write=False,
pixel_order=neopixel.GRB)
blink = Blink(pixel_object=pixels,
speed=0.5,
color=BLACK) # 初始化 Blink 效果,默认为黑色
oppo_sans_meduim = bitmap_font.load_font("font/OPlusSans3.0-Medium-20.bdf")# 载入字体
green_color = 0x00CA39 # 绿色
# 文本标签1
text_label_1 = label.Label(font = oppo_sans_meduim, text = "任务3:控制WS2812B", color = green_color)
text_label_1.x = 24
text_label_1.y = 14
# 文本标签2
text_label_2 = label.Label(font = oppo_sans_meduim)
text_label_2.text = "按BOOT键开始"
text_label_2.x = 47
text_label_2.y = 50
# 显示文本标签
group.append(text_label_1)
group.append(text_label_2)
display.show(group)
btn = DigitalInOut(board.BOOT0) # 初始化 BOOT0 按钮
btn.direction = Direction.INPUT # BOOT0 引脚设置为输入模式
btn.pull = Pull.UP # BOOT0 引脚为上拉
index = 0 # 用于颜色切换
while True:
if not btn.value: # 按键被按下 value = 0
index = index + 1
if index % 5 == 0:
index = 0 # index 置零
blink = Blink(pixels, speed=0.5, color=JADE)
text_label_2.color = JADE
text_label_2.text = "LED is JADE"
elif index % 5 == 1:
blink = Blink(pixels, speed=0.5, color=ORANGE)
text_label_2.color = ORANGE
text_label_2.text = "LED is ORANGE"
elif index % 5 == 2:
blink = Blink(pixels, speed=0.5, color=GOLD)
text_label_2.text = "LED is GOLD"
text_label_2.color = GOLD
elif index % 5 == 3:
blink = Blink(pixels, speed=0.5, color=OLD_LACE)
text_label_2.color = OLD_LACE
text_label_2.text = "LED is OLD_LACE"
elif index % 5 == 4:
blink = Blink(pixels, speed=0.5, color=AQUA)
text_label_2.color = AQUA
text_label_2.text = "LED is AQUA"
display.show(group)
blink.animate()
time.sleep(0.2) # 按键防抖
else: # 按键未被按下
pass
LED is AQUA!
WS2812B
ws2812是一种可编程控制的LED灯,pin脚如下:
除了VDD和VSS电源脚,它还有DIN、DOUT脚,数据传输协议采用单 NZR 通信,多个WS2812首尾相连,可以组成一个灯带。
CircuitPython可以使用 neopixel 库来驱动这颗WS2812。
按键
板子上有2个按键,RST和BOOT0。RST用作复位,BOOT0用作LED控制。
btn = DigitalInOut(board.BOOT0)
btn.direction = Direction.INPUT
btn.pull = Pull.UP
Direction.INPUT 将BOOT0 对应的引脚设置为输入模式,Pull.UP 将引脚状态设置为上拉,while循环内轮询BOOT0状态,等于0时候,说明按键被按下。
任务4:日历&时钟
########################################################################
# 任务4:日历&时钟
# 完成一个可通过互联网更新的万年历时钟,并显示当地的天气信息
########################################################################
import board
import displayio
import adafruit_imageload
from adafruit_display_text import label
from adafruit_bitmap_font import bitmap_font
import os
import rtc # RTC 库,实现 RTC 时钟
import wifi
import time
import ssl
import socketpool
import adafruit_ntp
import adafruit_requests
def get_city_code(key):
"""
根据 IP 获取 城市的adcode编码
"""
# key = "9985bb2816f45f621228e7afe61fc79d" # 使用的是高德API,使用该API需要去注册账户,申请key。
get_city_code_url = "https://restapi.amap.com/v3/ip?key=" + key
response = requests.get(get_city_code_url)
json_resp = response.json()
response.close()
print("city_code", json_resp["adcode"])
return json_resp["adcode"]
def get_weather(key):
"""
获取天气信息
"""
city = get_city_code(key) # 设置城市id
key = "9985bb2816f45f621228e7afe61fc79d" # 使用的是高德API,使用该API需要去注册账户,申请key。
get_weather_url = "https://restapi.amap.com/v3/weather/weatherInfo?city=" + city + "&key=" + key
response = requests.get(get_weather_url) # 获取天气数据
json_resp = response.json() # 解析天气数据为 json 字典格式
response.close() # 关闭连接
for da in json_resp["lives"]:
return da["city"], da["weather"], da["windpower"], da["temperature"], da["humidity"]
def get_wday(wday):
"""
根据时间结构体获取到的tm_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 "周日"
display = board.DISPLAY
group = displayio.Group()
"""
===创建背景图片===
"""
bg_image, palette = adafruit_imageload.load(file_or_filename= "/pic/bg1.png")#加载图片
palette.make_transparent(100) # 设置图片透明度
bg_image_grid = displayio.TileGrid(bitmap=bg_image, pixel_shader=palette)
group.append(bg_image_grid)
display.show(group)
"""
===创建文本标签以显示文字===
"""
oppo_sans_16 = bitmap_font.load_font("/font/OPlusSans3.0-Medium-16.bdf") # 载入字体
oppo_sans_20 = bitmap_font.load_font("/font/OPlusSans3.0-Medium-20.bdf")
oppo_sans_64 = bitmap_font.load_font("/font/OPlusSans3.0-Medium-64.bdf")
black_color = 0x000000
date = label.Label(font=oppo_sans_20, color = black_color) # 日期标签
date.x = 17
date.y = 15
week = label.Label(font = oppo_sans_20, color = black_color) # 星期标签
week.x = 30
week.y = 42
timeL = label.Label(font = oppo_sans_64, color = black_color) # 时间标签
timeL.x = 80
timeL.y = 27
city = label.Label(font = oppo_sans_16, color = black_color) # 城市标签
city.x = 29
city.y = 67
weather = label.Label(font = oppo_sans_16, color = black_color) # 天气标签
weather.x = 29
weather.y = 98
wind = label.Label(font = oppo_sans_16, color = black_color) # 风力标签
wind.x = 104
wind.y = 98
temp = label.Label(font = oppo_sans_16, color = black_color) # 温度标签
temp.x = 29
temp.y = 123
humidity = label.Label(font = oppo_sans_16, color = black_color) # 湿度标签
humidity.x = 104
humidity.y = 123
# 将标签添加到 group 中
group.append(date)
group.append(week)
group.append(timeL)
group.append(city)
group.append(weather)
group.append(wind)
group.append(temp)
group.append(humidity)
display.show(group)
# 使用 os.getenv 函数,从 setting.toml 文件里获取wifi ssid 、密码和 key
ssid = os.getenv("CIRCUITPY_WIFI_SSID")
password = os.getenv("CIRCUITPY_WIFI_PASSWORD")
key = os.getenv("KEY")
# 连接 wifi
print("Conneccting to", ssid)
wifi.radio.connect(ssid, password, timeout = 10)
print("Connected to", ssid)
# 使用adafruit_ntp.NTP函数初始化ntp服务,第一个函数是确定网络连接端口,
# 使用阿里云的 NTP 服务器,时区是+8时区。
pool = socketpool.SocketPool(wifi.radio)
ntp = adafruit_ntp.NTP(socketpool= pool,
server= "ntp.aliyun.com",
port = 123,
tz_offset=8)
rtc.RTC().datetime = ntp.datetime # 使用ntp时间更新系统时间。
requests = adafruit_requests.Session(socket_pool=pool, # 初始化 requests 对象
ssl_context= ssl.create_default_context())
status = "boot" # 先创建一个status变量,用来在设备启动时获取天气信息
flash_screen = False # 刷新屏幕标志位
last_second = 61 # 记录上一秒的值,用来实现时间刷新
while True:
t = time.localtime() # 获取一次本地RTC时间
# 首次启动或者每隔10分钟,更新日期标签和天气标签
if (status == "boot" or t.tm_min%10 == 0):
date.text = "%2d/%2d" % (t.tm_mon, t.tm_mday) # 更新日期标签
week.text = get_wday(t.tm_wday) # 更新星期标签
city_str, weather_str, wind_str, temp_str, hunidity_str = get_weather(key) # 获取天气信息
city.text = city_str # 城市信息
weather.text = weather_str # 天气信息
wind.text = wind_str # 风力
temp.text = "%s℃" % (temp_str) # 温度
humidity.text = "%2s%%" % (hunidity_str) # 湿度
status = "updated" # 更新 status 为 updated
flash_screen = True # 刷新屏幕标志位置位
# 如果过了1秒,刷新屏幕
if (t.tm_sec is not last_second):
flash_screen = True # 刷新屏幕标志位置位
last_second = t.tm_sec # 更新last_second
if (t.tm_sec %2 == 0): # 每隔1秒 更新一次时钟标签,用于动态显示
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
if (flash_screen):
display.show(group) # 刷新屏幕
flash_screen = False
time.sleep(0.2) # 休眠0.2秒
实现思路是这样的:
1、板子先连接上WiFi网络;
2、联网后,使用NTP协议,同步网络时间;
3、通过网络ip,获取所在的城市,再获取所在城市的天气状况;
4、把时间和天气信息输出到屏幕上。
同步网络时间
pool = socketpool.SocketPool(wifi.radio)
ntp = adafruit_ntp.NTP(socketpool= pool,
server= "ntp.aliyun.com",
port = 123,
tz_offset=8)
rtc.RTC().datetime = ntp.datetime
使用adafruit_ntp.NTP函数初始化ntp服务,socketpool是网络连接端口,server是NTP服务器的域名、port是端口号(一般是123)、tz_offset是时区偏移,我们在东八区,所以是8;
rtc.RTC().datetime = ntp.datetime,将NTP时间,赋值给芯片中的RTC时钟。
获取天气
def get_city_code(key):
"""
根据 IP 获取 城市的adcode编码
"""
# key = "9985bb2816f45f621228e7afe61fc79d" # 使用的是高德API,使用该API需要去注册账户,申请key。
get_city_code_url = "https://restapi.amap.com/v3/ip?key=" + key
response = requests.get(get_city_code_url)
json_resp = response.json()
response.close()
print("city_code", json_resp["adcode"])
return json_resp["adcode"]
def get_weather(key):
"""
获取天气信息
"""
city = get_city_code(key) # 设置城市id
key = "9985bb2816f45f621228e7afe61fc79d" # 使用的是高德API,使用该API需要去注册账户,申请key。
get_weather_url = "https://restapi.amap.com/v3/weather/weatherInfo?city=" + city + "&key=" + key
response = requests.get(get_weather_url) # 获取天气数据
json_resp = response.json() # 解析天气数据为 json 字典格式
response.close() # 关闭连接
for da in json_resp["lives"]:
return da["city"], da["weather"], da["windpower"], da["temperature"], da["humidity"]
get_city_code()函数用来获取城市代码,get_weather()函数,根据get_city_code()的城市代码,获取本地的天气信息。两个函数都说调用的是高德API。
时间刷新
last_second = 61
flash_screen = False
while True:
t = time.localtime()
if (t.tm_sec is not last_second):
flash_screen = True
last_second = t.tm_sec
last_second 表示上一秒的秒数
flash_screen是刷新屏幕标志位,True表示需要刷新屏幕
while循环中,通过获取time.localtime()获取当前时间,if (t.tm_sec is not last_second)判断秒数t.tm_sec是否变化了,如果t.tm_sec与上一次不同,新屏幕标志位flash_screen置为True,并且last_second同步更新。
第三部分
源码
https://download.eeworld.com.cn/detail/ctrlshift12138/629892
|