[作品提交] 【得捷电子Follow me第2期】完成4个任务,继续加油

waq   2023-11-26 20:32 楼主

大家好,我是来自上海从事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第二期活动,由于工作繁忙时间不够(周六周日都在加班那种),就使用开发版本身完成了四个必做任务。

下面我来介绍一下完成任务的功能。

Snipaste_2023-11-26_20-22-25.png

第一个任务:控制屏幕显示中文。在这个任务中,我使用了小米的MiSans字体,通过FontForge工具将字体精简,包含了3500个常用中文、常用中文标点、英文标点以及必须包含的英文字母和数字,在转换成pcf格式压缩后共453KB,通过adafruit_display_text库中的wrap_text_to_pixels函数实现了长文本的跨行显示。

Snipaste_2023-11-26_20-24-48.png

代码片段

# 加载字体文件
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的连接能力,然后测试创建热点,并通过手机连接此热点。

Snipaste_2023-11-26_20-26-34.png
代码片段
# 连接到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的颜色会切换到下一个预定义的颜色。当所有颜色都被使用过后,颜色会重新开始循环。

IMG_20231126_191711.jpg

代码片段

# 初始化颜色
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

 

这次活动我完成的任务是非常简单的。我其实比较想尝试为此主板重新撰写固件,这会是个很好的练手机会。如果以后有时间的话,我会尝试做这件事,或许也会尝试其他更有趣的东西。

各位,再见!

回复评论

暂无评论,赶紧抢沙发吧
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复