[作品提交] 【得捷电子Follow me第2期】综合任务提交

eew_QQpcgv   2023-10-23 22:08 楼主

【演示视频】
 

 

【项目总结】

1、前言

非常感谢EEWORLD和得捷电子组织本次follow me活动,本次follow me活动主要围绕规定的几个任务展开,完成了任务1~任务4的必做任务。

期间遇到不少困难,好在论坛大佬多资料不少,倒是勉强能完成本次活动。

项目中主要遇到困难有:

1.字库制作方法少且制作复杂,py库引用的文件字体里不能有小数点的像素

2.wifi连接报错,在环境复杂的情况下,可能找不到目标wifi,强行连接会报错。

2、介绍

这一次的板卡是Adafruit ESP32-S3 TFT Feather

Adafruit ESP32-S3 TFT Feather是由开源硬件行业知名公司Adafruit出品的一款富有特色的开源硬件,开发板使用乐鑫ESP32-S3芯片,支持WiFi和蓝牙能,自带高清TFT彩色显示屏。

特性

•  搭载 Xtensa® 32 位 LX7 双核处理器,主频高达 240 MHz,内置 512 KB SRAM (TCM),具有 45 个可编程 GPIO 管脚和丰富的通信接口,集成 2.4 GHz Wi-Fi 和 Bluetooth 5 (LE),配备原生 USB,可用作键盘/鼠标、MIDI 设备、磁盘驱动器等;
•  获得FCC / CE认证,带有4 MB闪存和2 MB PSRAM;
•  彩色 1.14“ IPS TFT,240x135 像素——采用 ST7789 芯片组的高清彩色显示屏,可从任何角度观看;
•  电源选项 - USB C 型或锂聚合物电池,通过 USB-C 供电时可为内置电池充电;
•  LiPoly 电池监控器 - LC709203 芯片主动监控电池的电压和充电状态/通过 I2C 报告电池电量百分比;
•  重置和DFU(BOOT0)按钮以进入ROM引导加载程序;
•  内置原生USB串行端口,不需要单独的电缆,串行调试输出引脚(可选,用于检查硬件串行调试控制台);
•  用于 I2C 设备的 STEMMA QT 连接器,具有可切换电源,可进入低功耗模式;
•  板载 NeoPixel 具有引脚控制电源,可实现低功耗,用于多种状态显示;
•  在深度睡眠模式下,可从Lipoly连接获得80~100uA的电流消耗;
•  可与Arduino或CircuitPython一起使用。

5483zst.jpg  

Adafruit_ESP32_S3_TFT_Feather_Pinout.png  

3.任务一:控制屏幕显示中文

CircuitPython显示文本可以使用官方右做好的的lib,但是没有提供中文字库,所以想要显示中文,额外要做的就提供中文的字库文件。

自己制作或直接从网上下载都可以,详细制作方法可以参考官方指导生成DBF(字体像素大小尽量使用整数,不然可能不能使用):

https://learn.adafruit.com/custom-fonts-for-pyportal-circuitpython-display

但是用BDF刷新速度会慢些,如果使用PCF文件会快很多。这里网站可以在线将BDF转PCF:https://adafruit.github.io/web-bdftopcf/

第一步,使用import导入相关的库

第二步,创建一个显示对象display

第三步,创建一个文本对象

第四步,创建一个图像对象(可选)

第五步,指定中文字库文件,文本内容,颜色,显示坐标灯相关参数

第六步,调用display.show接口进行显示即可

# 导入board内置库
import board

# 图形
import displayio
import adafruit_imageload
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text.label import Label



def font_show():

    font25 = bitmap_font.load_font("/font/SourceHanSansCN-Bold-25.pcf")
    font50 = bitmap_font.load_font("/font/SourceHanSansCN-Bold-50.pcf")

    def image_init(path):
        image, palette = adafruit_imageload.load(path)
        return displayio.TileGrid(image, pixel_shader=palette )

    def lable_init(x, y, text="", color=0, font=font25 ):
        return Label(x=x, y=y, text=text, color=color, font=font)

    week_day = {0:"周一", 1:"周二", 2:"周三", 3:"周四", 4:"周五", 5:"周六", 6:"周日", }
    


    font25 = bitmap_font.load_font("/font/SourceHanSansCN-Bold-25.pcf")
    font50 = bitmap_font.load_font("/font/SourceHanSansCN-Bold-50.pcf")
    time_lable = lable_init(0, 90, font= font50, text="%02d:%02d" % ( 19, 23) )
    data_lable = lable_init(0, 50, text="%02d月%02d日" % (1, 2) )
    week_lable = lable_init(0, 20, text=week_day[0] )
    str_temp,st_weather = "80","晴"
    weather_lable = lable_init(55, week_lable.y, text= st_weather)
    temp_lable = lable_init(110, weather_lable.y, text="%s°" % str_temp)

    # 加入画板
    group = displayio.Group()
    grid=image_init("/pic/bg.png")
    group.append(grid)

    group.append(time_lable)
    group.append(week_lable)
    group.append(data_lable)
    group.append(weather_lable)
    group.append(temp_lable)

    board.DISPLAY.show(group)

    while True:
        pass

font_show()

13f9651facad4df6a7ce52de2526c1d6.jpg  

4、任务二:网络功能使用

网络功能主要实现:1、显示可连接WIFI;2、连接到路由器WIFI(如果连接不上会重连);3、设置为AP模式,供其它设备连接;

在wifi连接的过程中,会输出一些重要的信息到控制台。

注意,如果附件wifi信号太强,有可能找不到手机的热点wifi,导致连接不上,最好看esp可连接的wifi再进行连接。

这里我用VSCODE中的CircuitPython插件调试打印,也可以用浏览器调试打印。

# 导入board内置库
import wifi
# 网络
import time


def task_wifi():

    WIFI_SSID       = "123456789"# os.getenv("CIRCUITPY_WIFI_SSID")
    WIFI_PASSWORD   = "88888888"# os.getenv("CIRCUITPY_WIFI_PASSWORD")

    AP_SSID         = "AP_PMI"
    AP_PASSWORD     = "77777778"
    
    def wifi_init(ssid=WIFI_SSID, password=WIFI_PASSWORD ):
        # 开启热点
        wifi.radio.start_ap(AP_SSID, AP_PASSWORD)
        print("AP_SSID =", AP_SSID)
        print("AP_PASSWORD =", AP_PASSWORD)
        print("AP_IP:", wifi.radio.ipv4_address_ap)
        
        # 连接目标WIFI
        print("WIFI_SSID =", ssid)
        print("WIFI_PASSWORD =", password)
        print("WIFI_IP:", wifi.radio.ipv4_address)
        
        for n in range(20):
            
            if wifi.radio.ipv4_address is not None:
                break     
            
            # 显示可连接网络
            for net in wifi.radio.start_scanning_networks():
                print("%d:%s\t\t\t\t\tRSSI:%d\tChannel:%d" %(n, str(net.ssid,"utf-8"), net.rssi, net.channel) )
                # 连接符合目标
                if net.ssid == ssid:
                    try :
                        print("wifi connect...",n)
                        wifi.radio.connect(ssid, password)
                    except ConnectionError as e:
                        print("wifi connect err:", e)
            
            wifi.radio.stop_scanning_networks()
            time.sleep(2)
        
        
        if(wifi.radio.ipv4_address is None ) :
            print("wifi unkonw")
        else :
            print("WIFI_IP:", wifi.radio.ipv4_address)

    wifi_init()

    while True:
        pass

task_wifi()



1.连接目标wifi

图片.png  

2.连接ESP热点

图片.png  

5、任务三:控制WS2812B

本任务实现对WS2812的控制从而实现对颜色的控制。

本任务中基于官方代码稍作扩展,实现如下功能:

1、使用异步线程编程

2、使用异步线程控制Boot按键短按改变WS2812B等颜色的切换

3、使用异步线程控制board.D13的灯珠,用于板子心跳并打印log

板载有2个按键,其中Rst按键,无法当作用户按键使用,只有Boot按键可以当作用户按键使用,本次就直接使用Boot按键作为颜色切换的按键。

# 导入board内置库
import board



# 导入外置库
import asyncio
import neopixel


# 从digitalio内置库导入DigitalInOut, Direction, Pull
from digitalio import DigitalInOut, Direction, Pull


async def task_btn():

    def WS2812B_init():
        # 初始化对象且设置灯珠数量
        pixel = neopixel.NeoPixel(board.NEOPIXEL, 1)
        # 取消写入后马上刷新
        pixel.auto_write = False
        # 灯珠格式
        pixel.pixel_order = neopixel.GRB
        return pixel

    def btn_init():
        # 初始化btn按钮对象
        btn = DigitalInOut(board.BOOT0)
        # 设置btn引脚为输入
        btn.direction = Direction.INPUT
        # 设置btn引脚为上拉
        btn.pull = Pull.UP
        return btn
    pixel = WS2812B_init()
    btn = btn_init()

    btn_state = 0
    btn_state_old = btn_state

    while True:
        if not btn.value:
            btn_state+=1

        if( btn_state_old == btn_state ):
            await asyncio.sleep(0.2)
            continue

        # 按键触发了

        # 处理按键状态
        if( btn_state > 4 ) :
            btn_state = 1
        btn_state_old = btn_state

        print("btn val ",btn_state_old)
        # 根据按键状态做出动作
        if (btn_state_old == 1):
            pixel.fill((255, 0, 0))     # 所有灯珠颜色改变

        elif (btn_state_old == 2):
            pixel[0] = (0, 255, 0)     # 单个灯珠颜色改变

        elif (btn_state_old == 3):
            pixel.fill((0, 0, 255))
            
        elif (btn_state_old == 4):
            pixel.fill((0, 0, 0))

        # 执行led闪烁动态效果
        pixel.show()

        if not btn.value:
            await asyncio.sleep(0.2)
        await asyncio.sleep(0.2)


        
        
        

async def task_tick():

    def led_init():
        # 初始化btn按钮对象
        led = DigitalInOut(board.D13)
        # 设置btn引脚为输入
        led.direction = Direction.OUTPUT
        return led
    led = led_init()

    cnt = 0
    while True:
        cnt+=1
        print("tick",cnt)
        led.value = not led.value
        await asyncio.sleep(0.8)



async def main():

    print("loop start")
    
    btn_task = asyncio.create_task(task_btn())
    tick_task = asyncio.create_task(task_tick())

    # This will run forever, because no tasks ever finish.
    await asyncio.gather(btn_task, tick_task)

asyncio.run(main())


69730350d52438f1b58d5e4ae08bd29c.jpg  

3c585975b63d311e8046fbee8f76e472.jpg  

 6、任务4:日历&时钟

 

此任务用到了任务二的代码,完成一个可通过互联网更新的万年历时钟,并显示当地的天气信息

# 导入board内置库
import wifi
# 网络
import time
import rtc
import socketpool

# 网络
import adafruit_ntp
import ssl
import adafruit_requests


# 从digitalio内置库导入DigitalInOut, Direction, Pull
from digitalio import DigitalInOut, Direction, Pull


def get_weather():
    
    if (wifi.radio.ipv4_address is None ) :
        return "---","未知"
    
    # 设置城市id https://www.cnblogs.com/trigger-cn/p/17463196.html
    city = "441900"     
    #这个函教使用的是高德API,使用该API需要先去注册相关账户,中请key。
    key = "8027d9b28a93e1fdc1bd9f8076041e0e"
    #接天气链接url
    getweather_url = "https://restapi.amap.com/v3/weather/weatherInfo?city=" + city + "&key=" + key
    # 获取天气json微据
    pool = socketpool.SocketPool(wifi.radio)
    requests = adafruit_requests.Session(pool,ssl.create_default_context() )
    try :
        response = requests.get(getweather_url)
    except :
        #关闭连接
        response.close()
        return "---","未知"
    
    json_resp = response.json()
    #关闭连接
    response.close()
    # 解析json微据,并返回温度和天气信息
    print(json_resp)
    for da in json_resp["lives"]:
        return da["temperature"], da["weather"]


def task_wifi():

    WIFI_SSID       = "Luxshare-VIP"# os.getenv("CIRCUITPY_WIFI_SSID")
    WIFI_PASSWORD   = "Luxsh@re-1ct2o2100"# os.getenv("CIRCUITPY_WIFI_PASSWORD")
    # AP_SSID         = "AP_PMI"
    # AP_PASSWORD     = "77777778"
    
    def wifi_init(ssid=WIFI_SSID, password=WIFI_PASSWORD ):
        # # 开启热点
        # wifi.radio.start_ap(AP_SSID, AP_PASSWORD)
        # print("AP_SSID =", AP_SSID)
        # print("AP_PASSWORD =", AP_PASSWORD)
        # print("AP_IP:", wifi.radio.ipv4_address_ap)
        
        # 连接目标WIFI
        print("WIFI_SSID =", ssid)
        print("WIFI_PASSWORD =", password)
        print("WIFI_IP:", wifi.radio.ipv4_address)
        
        for n in range(20):
            
            if wifi.radio.ipv4_address is not None:
                break     
            
            # 显示可连接网络
            for net in wifi.radio.start_scanning_networks():
                print("%d:%s\t\t\t\t\tRSSI:%d\tChannel:%d" %(n, str(net.ssid,"utf-8"), net.rssi, net.channel) )
                # 连接符合目标
                if net.ssid == ssid:
                    try :
                        print("wifi connect...",n)
                        wifi.radio.connect(ssid, password)
                    except ConnectionError as e:
                        print("wifi connect err:", e)
            
            wifi.radio.stop_scanning_networks()
            time.sleep(2)
        
        
        if(wifi.radio.ipv4_address is None ) :
            print("wifi unkonw")
        else :
            print("WIFI_IP:", wifi.radio.ipv4_address)



    def ntc_init():
        if (wifi.radio.ipv4_address is not None ) :
            ntp_pool = socketpool.SocketPool(wifi.radio)
            ntp = adafruit_ntp.NTP(ntp_pool, tz_offset= 8, server= "ntp.aliyun.com")
            try :
                rtc.RTC().datetime = ntp.datetime
                return time.localtime
            except ConnectionError as e:
                print( "ntp init error" ,e)
                pass
        time.struct_time((2019, 5, 29, 15, 14, 15, 0, -1, -1))
        print( "unkonw ntp" )
        return time.localtime

    week_day = {0:"周一", 1:"周二", 2:"周三", 3:"周四", 4:"周五", 5:"周六", 6:"周日", }
    
    wifi_init()
    rtc_time = ntc_init()



    tim_text="%02d月%02d日" % ( rtc_time().tm_mon, rtc_time().tm_mday)
    dat_text=week_day[rtc_time().tm_wday]
    str_temp,str_weather = get_weather()

    print(tim_text, dat_text, str_temp, str_weather)
    while True:
        pass

task_wifi()




效果展示:

图片.png

7、综合应用:

结合前面的几个任务做了一个综合应用。

364bc08cc341857457a62186c7cc33e1.jpg  

【源代码】

FollowMe第二期任务.zip (109.07 KB)
(下载次数: 2, 2023-11-8 15:51 上传)

 

回复评论

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