得捷FollowMe第二期速通记录 - ESP32S3-Feather-TFT入门
任务 1 | 控制屏幕显示中文
要在板子上显示中文和其他自定义的字体,就需要往板子上上传对应的点阵字体,并利用相应的库来支持字体渲染。所幸Adafruit已经提供了现成的字体库,我们只需要安装相应的adafruit_bitmap_font库即可。
由于板子的存储空间受限,存储完整英文字体尚可,但存储完整中文字体则不太现实。我们需要先在电脑上制作字体的子集,随后转换成点阵字体上传到板子上。
制作用到了两个工具,分别是fonttools和otf2bdf。fonttools是一个Python库,可以用来处理字体文件,otf2bdf是一个命令行工具,可以将otf/ttf字体转换成bdf字体。
$ fonttools subset --text-file=subset.txt --output-file=target.otf font.otf
$ otf2bdf -p 32 -r 72 -o target.bdf target.otf
即可根据输入的字库产生需要的中文字体子集。这次任务中,subset.txt中包含了天气API涉及的天气现象,和一些界面用到的字符。接下来是代码部分。
from adafruit_bitmap_font import bitmap_font
# 初始化并导入字体
f32 = bitmap_font.load_font("font/sf32.bdf")
f16 = bitmap_font.load_font("font/sf16.bdf")
fcn = bitmap_font.load_font("font/source.bdf")
# 显示中文
temp_label = label.Label(font=fcn, color = 0xaaaaaa, text="温度 ")
显示其他字体同理。笔者制作了苹果San Francisco字体的子集,用于显示界面上的英文字符。
任务 2 | 网络功能使用
使用网络同样只需要调用自带的WiFi库。
作为热点
作为热点在视频中没有演示,在这里另起一个项目展示这个功能。
连接Wifi
# network setup
print("connecting to wifi")
while not wifi.radio.connected:
wifi.radio.connect(ssid="ssid", password="pwd")
if wifi.radio.connected:
break
time.sleep(0.5)
print("retrying...")
print("connected.")
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())
连接wifi并创建requests对象后,便可以发起相应的网络请求。
任务 3 | 控制WS2812B
同样,调用board库和neopixel库即可方便的控制WS2812B。
首先,需要创建一个neopixel对象。
np = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
然后,只需要利用fill方法,向对象传入一个(r,g,b)三元组,即可控制颜色。
if press is False:
np.fill((0, 64, 0))
任务 4.1 | 日历&时钟
要显示日历和天气,需要寻找相应的开放API,查询相应的时间和天气信息,并将其显示在屏幕上。实现这些功能只需要用前面任务用到的知识。
查询信息
笔者用到了两个接口来查询。一个是worldtimeapi.org提供的时间查询,另一个是高德开放平台提供的天气查询。高德平台提供温度以及天气描述等信息,能够很方便地用于显示。当然,高德平台需要注册认证,有一定的调用限制,所以在编程时需要注意代码正确,避免意外滥用API.
首先,将板子连接到网络,初始化requests对象。然后根据API手册书写对应的查询函数。为了节约API调用次数,笔者创建了一个全局的dict对象,用于存储已经查询的相应字段。这样,在刷新屏幕的时候就不会发生过多的API调用。
调用API的查询代码如下
lut = "日一二三四五六"
def queryTime():
r = json.loads(
requests.get("http://worldtimeapi.org/api/timezone/Asia/Shanghai").text
)
s = r["datetime"].split("T")
content["date"] = s[0]
content["week"] = lut[r["day_of_week"]]
time_tuple = s[1].split(":")
content["time"] = time_tuple[0] + " : " + time_tuple[1]
def queryWeather(city : int):
baseurl = "https://restapi.amap.com/v3/weather/weatherInfo"
apikey = "secret!"
url = baseurl + "?" + "city="+ str(city) + "&key=" + apikey + "&extensions=base"
r = requests.get(url)
response = json.loads(r.text)
response = response["lives"][0]
content["temp"] = response["temperature_float"]
content["desc"] = response["weather"]
显示内容
将提前制作好的16px, 32px San Fransisco字体,以及16px思源黑体子集传送到板子上。为了让显示界面更美观,笔者还制作了一张背景图片。
利用display_text
库和imageload
库,可以很方便地在板子上显示文字和图片。
首先,要创建对象组,这是我们界面的画布。
text_group = displayio.Group()
然后,在最底层插入背景图
# back
image, palette = adafruit_imageload.load(
"/img/background.bmp",
bitmap = displayio.Bitmap,
palette= displayio.Palette)
tile_grid = displayio.TileGrid(image, pixel_shader=palette)
text_group.append(tile_grid)
然后,逐个插入要显示的文字。标签较多,这里只展示一部分代码。
# tmp lable
temp_label = label.Label(font=fcn, color = 0xaaaaaa, text="温度 ")
temp_label.x = 10
temp_label.y = 20 + 32 + 10
tnum = label.Label(font=f16, color= 0xeeeeee, text=content["temp"] + "°")
tnum.y = temp_label.y
tnum.x = temp_label.x + temp_label.width + 10
text_group.append(temp_label)
text_group.append(tnum)
最后调用display.show(text_group)
即可显示。
按钮控制
为节约API调用,笔者设置了每20秒刷新一次时间,每10分钟查询一次天气。在延迟较大时,笔者还设计了按按钮马上更新的功能。
# button
button = digitalio.DigitalInOut(board.BUTTON)
button.switch_to_input(pull=digitalio.Pull.UP)
def is_pressed():
return button.value == False
...
if is_pressed():
...
这些基本就是完成任务所需要的内容。