大家好,我是来自上海从事IC设计的打工人,日常工作就是设计CPU。我的业余兴趣很广泛,机缘巧合之下我了解到了FollowMe 第二期活动,并认识了Adafruit ESP32-S3 TFT Feather这款开发板。当我拿到这块板子的时候,我真是眼前一亮。首先,它的外观非常小巧,其次,在两枚硬币大小的尺寸内,它竟然集成了IPS TFT 彩色显示屏、原生USB、4MB flash、WIFI和蓝牙微控制器等模块。更多信息可以参考官方网页Features部分(Overview | Adafruit ESP32-S3 TFT Feather | Adafruit Learning System)。由于它丰富的硬件支持,它可以用于开发各种低功耗的应用。我特别喜欢它自带的ROM和Flash两种引导方式,用户可以随意尝试不同的固件。
我这次参加FollowMe第二期活动,由于工作繁忙时间不够(周六周日都在加班那种),就使用开发版本身完成了四个必做任务。
下面我来介绍一下完成任务的功能。
第一个任务:控制屏幕显示中文。在这个任务中,我使用了小米的MiSans字体,通过FontForge工具将字体精简,包含了3500个常用中文、常用中文标点、英文标点以及必须包含的英文字母和数字,在转换成pcf格式压缩后共453KB,通过adafruit_display_text库中的wrap_text_to_pixels函数实现了长文本的跨行显示。
# 加载字体文件
font = bitmap_font.load_font("assets/font/MiSans-Normal-19.pcf")
# 设置文本颜色
color = 0xFFFF00
# 创建一个显示组
text_group = displayio.Group()
# 创建一个标签,用于显示文本
# 使用wrap_text_to_pixels函数将文本分割成多行,每行的宽度不超过屏幕的宽度
text_area = label.Label(font, text="\n".join(wrap_text_to_pixels(text, display.width, font)), color=color)
其中wrap_text_to_pixels函数的第三个参数是通过传入字体来计算每个字符的占用宽度,对于长文字换行比较重要,自己写函数来实现手动换行总是效果不好。
第二个任务:网络功能使用。在这个任务中,我把连接的WIFI信息和用于创建热点的AP信息都放在了settings.toml文件中,通过获取环境变量的方式拿到这些信息。首先测试了WIFI的连接能力,然后测试创建热点,并通过手机连接此热点。
# 连接到WiFi网络
wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD"))
# 创建WiFi热点
wifi.radio.start_ap(os.getenv("AP_SSID"), os.getenv("AP_PASSWORD"))
使用WIFI模块中的radio.connect连接wifi网络,使用radio.start_ap创建wifi热点。
第三个任务:控制Neopixel LED。在这个任务中,我实现了Neopixel LED的颜色显示和切换功能。代码中首先初始化按键和LED,然后进入一个无限循环。在循环中,当按键被按下时,LED的颜色会切换到下一个预定义的颜色。当所有颜色都被使用过后,颜色会重新开始循环。
代码片段
# 初始化颜色
Color = RAINBOW_COLORS[0]
pixel.fill(Color)
# 定义颜色循环变量
Color_Num = 1
# 主循环
while True:
# 按键被按下且之前未被按下
if not button.value and not button_pressed:
button_pressed = True
# 延迟一段时间进行消抖
time.sleep(debounce_delay)
# 确认按键仍然被按下
if not button.value:
Color = RAINBOW_COLORS[Color_Num]
# 设置led颜色
pixel.fill(Color)
Color_Num += 1
if Color_Num == 7:
Color_Num -= 7
# 按键未被按下
elif button.value:
button_pressed = False
其中延迟debounce_delay指定的时间来处理按键按下电压抖动特征,然后颜色循环变量作为按键后才会使用的值,需要设置为初始颜色的下一个值。
第四个任务:日历和时钟。我完成了一个可通过互联网更新的时钟,并显示上海本地的天气信息。这些信息在显示屏上用中文展示。代码定义了一个函数用于获取网络时间和网络天气信息,为可能发生的错误提供了重试手段,加入了延迟代码方便展示代码执行过程。
代码片段
# 定义一个函数,用于在显示屏上显示文本
def screen(print_text):
# 创建一个文本区域,将文本分行以适应显示屏宽度
text_area = label.Label(font, text="\n".join(wrap_text_to_pixels(print_text, display.width, font)), scale=1)
# 设置文本区域的位置
text_area.x = 5
text_area.y = 15
# 设置文本区域的缩放和颜色
text_area.scale = 1
text_area.color = color
# 在显示屏上显示文本区域
display.show(text_area)
# 定义一个函数,用于获取网络时间
def get_data():
try:
# 发送请求,获取系统时间
response = requests.get(JSON_TIME_URL)
except ConnectionError as e:
# 如果连接失败,打印错误信息并在2秒后重试
screen("联网获取网络时间出错:\n{}\n\n2秒后重试".format(e))
# 解析响应数据,获取系统时间
time_data = response.json()['sysTime2'][:-3]
try:
# 城市代码
city_code = "101020100"
# 构造请求URL
url = JSON_weather_URL.format(city_code)
# 发送请求
response = requests.get(url)
except ConnectionError as e:
# 如果连接失败,打印错误信息并在2秒后重试
screen("联网获取本地天气信息出错:\n{}\n\n2秒后重试".format(e))
# 解析响应数据
weather_data = response.json()
cityInfo = weather_data['cityInfo']
city_weather = weather_data['data']
forecast = city_weather['forecast']
# 构造显示字符串
dis_str = cityInfo['city'] + ' ' + forecast[0]['week']
dis_str += "\n时间:"+ time_data
dis_str += "\n温度:"+ city_weather['wendu'] + "摄氏度"
dis_str += "\n湿度:" + city_weather['shidu']
dis_str += '\n空气质量:' + city_weather['quality']
# 返回显示字符串
return dis_str
# 如果没有获取到IP地址,尝试连接WiFi
while not wifi.radio.ipv4_address:
try:
screen("连接网络中...")
wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD"))
except ConnectionError as e:
screen("连接网络失败:{}, retry in 2s".format(e))
time.sleep(2)
# 显示网络连接成功和IP地址
screen("网络连接成功!\n我的IP地址:{}".format(wifi.radio.ipv4_address))
time.sleep(2)
screen("获取数据中...")
time.sleep(2)
这段代码主要是从网络api中获取获取时间和上海的天气信息,中间对获取失败做了2秒重试,在打印过程加了延迟,方便显示整个执行过程。
在做任务的工程中,我发现一些小问题,比如使用git管理项目代码后,经常导致代码reload到板子,不受控制,经过搜索有一篇文章(https://forum.acronis.com/forum/acronis-true-image-2018-forum/acronis-ati-2018-contantly-touch-usb-port-causing-issue-adafruit-circuitpython-folder?ckattempt=1)讲了类似的问题,但也不能很好的解决。
下面附上我的相关资源地址。
视频:https://training.eeworld.com.cn/video/38562
源代码:https://download.eeworld.com.cn/detail/eew_7v2EZX/629945
这次活动我完成的任务是非常简单的。我其实比较想尝试为此主板重新撰写固件,这会是个很好的练手机会。如果以后有时间的话,我会尝试做这件事,或许也会尝试其他更有趣的东西。
各位,再见!