589|1

1

帖子

1

TA的资源

一粒金砂(初级)

楼主
 

【得捷电子Follow me第2期】任务1-5的完成思路及代码方面的建议 [复制链接]

 

0.视频

follow me 3

 

1.介绍任务

大家好,我是TaoEngine,来自于安徽工业大学的一个专业与爱好不对口的软硬件爱好者。很高兴能参加本次由得捷电子举办的Follow me第2期活动。

在此我想先说抱歉,因为物流地址变迁的原因,导致物流进展缓慢,我直到10月末才收到货,而后又因为学校的安排及考试耽误到现在才完成任务并提交。在此非常感谢社区仍为我提供了这次机会!

Adafruit ESP32-S3 TFT Feather是一块非常值得把玩的一块开发板。它既保留了ESP32的强大可玩性,又不同于我之前一直在玩的nodemcu,这主要体现在它搭载了很多有意思的外设:一块高清tft彩屏,一颗板载的WS2812B灯珠,以及我非常喜欢的一个可以自定义功能的按键(任务的很多地方我都用到了这个按键来定义不同功能)。此外,它搭载的circuitpython又使我眼前一亮,它采用的上传代码的方式非常现代化,分区上传的代码既让我写代码有非常高的可读性,同时又让之前用过micropython的我感觉使用方式不割裂。

下面,就让我来介绍我的任务完成情况:

2.分任务点介绍

任务1:控制屏幕显示中文(必做任务)

先上代码:

import time
import board
import digitalio
import displayio
import terminalio
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text import label

print("ESP32-S3 Chinese Application")

#字体的加载
font = bitmap_font.load_font("zh_cn.pcf")

#字存放处
text_group = displayio.Group()
text_en = label.Label(terminalio.FONT, text="Follow Me 2", color=0xFFFFFF, x=20, y=20)
text_cn = label.Label(font, text="一起解锁开发板超能力", color=0xFFFFFF, x=20, y=20)

#先隐藏中文
text_cn.hidden = True
text_en.hidden = False

#放置字在屏幕上
text_group.append(text_cn)
text_group.append(text_en)
display = board.DISPLAY
display.show(text_group)

#定义按钮
button_switch_lang_pin= board.BUTTON
button_switch_lang = digitalio.DigitalInOut(button_switch_lang_pin)

#按钮事件
button_switch_lang.direction = digitalio.Direction.INPUT
button_switch_lang.pull = digitalio.Pull.UP
button_pressed = False


#按下按钮时
while True:
    #按钮第一次被按下
    if not button_switch_lang.value:
        button_pressed = True
        #视频介绍的消除抖动操作
        time.sleep(0.1)
        #按钮按下后的动作
        if not button_switch_lang.value:
            if text_cn.hidden and not text_en.hidden:
                text_cn.hidden = False
                text_en.hidden = True
            elif text_en.hidden and not text_cn.hidden:
                text_cn.hidden = True
                text_en.hidden = False
    else:
        button_pressed = False

注意:这里要提供字体文件,制作方法在视频up那里讲的很清楚。

在这次任务中,我想试着用过按钮完成中,英文的切换。先前看到“跟着做”视频中,视频up讲到了字是可以通过方法 text.hidden 来隐藏或显示的。这就非常迎合我想要实现的功能。但是我后来在开发任务4的时候,发现也可以使用 display.show 的方式来实现文字的显隐。最后我总结了下,简单的显示效果用 text.hidden 非常方便,但涉及界面ui的切换,使用 display.show 会更好管理各元素

任务2:网络功能使用(必做任务)

上代码:

import os
import time
import wifi
import board
import digitalio

print("ESP32-S3 WiFi Application")

#定义按钮
button_switch_ap_wifi_pin= board.BUTTON
button_switch_ap_wifi = digitalio.DigitalInOut(button_switch_ap_wifi_pin)

#按钮事件
button_switch_ap_wifi.direction = digitalio.Direction.INPUT
button_switch_ap_wifi.pull = digitalio.Pull.UP
button_pressed = False

#现在启动的WiFi模式
wifi_ap_mode = "wifi"
print("Press button to switch to wifi mode or AP mode")

#按下按钮时
while True:
    #按钮第一次被按下
    if not button_switch_ap_wifi.value:
        button_pressed = True
        #视频介绍的消除抖动操作
        time.sleep(0.1)
        #按钮按下后的动作
        if not button_switch_ap_wifi.value:
            #WiFi模式的操作
            if wifi_ap_mode == "wifi":
                wifi_ap_mode = "ap"
                #停止AP
                wifi.radio.stop_ap()
                print("="*3+"wifi mode"+"="*3+"\nAvailable WiFi:")
                #列出可用的WiFi
                for network in wifi.radio.start_scanning_networks():
                    print(str(network.ssid, "utf-8"))
                #省电,停用WiFi搜索
                wifi.radio.stop_scanning_networks()
                #连上WiFi
                print("Connecting to " + os.getenv('WIFI'))
                wifi.radio.connect(os.getenv('WIFI'), os.getenv('PWD'))
                #连上就停止阻塞
                print("Connected!")
            #AP模式的操作
            elif wifi_ap_mode == "ap":
                wifi_ap_mode = "wifi"
                print("="*3+"AP mode"+"="*3+"\nSSID ESP32\nPWD 12345678\n")
                #打开热点
                wifi.radio.start_ap("ESP32", "12345678")
                print("AP started!")
        #开关没有按下
        else:
            button_pressed = False

注意:这个代码需要在“settings.toml”填上“WIFI”和“PWD”,分别是WiFi名称和密码。

这里我想说下它这个WiFi连接方式和我之前用micropython的区别:micropython连上WiFi后可以直接提供套接字来给其他的库使用,但据我了解,micropython只能创建一个套接字。而在circuitpython中,连上网络是要采用socketpool这一新方法来在一个pool池子中获取套接字。这一方法看似复杂了些,但是这样能防止其他程序共用一个套接字而导致的网络混乱。虽然我不知道这样理解对不对,但是在micropython中我着实遇到了套接字被某一个函数占用后其他功能不能运行的bug。这确实能提高真的到了生产环境后程序的可靠性,我还是挺喜欢这种机制的。

任务3:控制WS2812B(必做任务)

继续上代码:

import time
import board
import digitalio
import neopixel

print("ESP32-S3 WS2812B Application")

#定义按钮
button_switch_WS2812B_pin= board.BUTTON
button_switch_WS2812B = digitalio.DigitalInOut(button_switch_WS2812B_pin)

#按钮事件
button_switch_WS2812B.direction = digitalio.Direction.INPUT
button_switch_WS2812B.pull = digitalio.Pull.UP
button_pressed = False

#定义灯
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1)
pixel.brightness = 0.3

#选择的颜色
COLORS = [
    (255,0,0),#红
    (0,255,0),#绿
    (0,0,255),#蓝
]

#灯模式
color_list = 0
color = COLORS[color_list]
print("Press button to switch WS2812B")

#按下按钮时
while True:
    #按钮第一次被按下
    if not button_switch_WS2812B.value:
        button_pressed = True
        #视频介绍的消除抖动操作
        time.sleep(0.1)
        #按钮按下后的动作
        if not button_switch_WS2812B.value:
            color_list += 1
            if color_list == 3:
                color_list -= 3
            #换颜色
            color = COLORS[color_list]
            pixel.fill(color)
    else:
        button_pressed = False

任务4:从下方5个分任务中选择1个感兴趣的完成即可(必做任务)

我选的是分任务1:日历&时钟(没时间学习其他扩展模块的对应功能了,不然我还是更倾向于做下面的分任务的)。

这次代码比较复杂,我会讲下其重点的代码部分:

import rtc
import ssl
import time
import wifi
import board
import digitalio
import displayio
import socketpool
import terminalio
import adafruit_ntp
import adafruit_requests
from adafruit_display_text import label
from adafruit_bitmap_font import bitmap_font

print("ESP32-S3 Calendar&Clock Application")

#字体的加载
font = bitmap_font.load_font("zh_cn.pcf")

#布局存放处
text_group = displayio.Group()
text_title = label.Label(font, text="现在是", color=0xFFFFFF, x=10, y=10)
text_group.append(text_title)

text_weather = label.Label(font, text="天气:?", color=0xFFFFFF, x=195, y=10)
text_group.append(text_weather)

text_calendar_year = label.Label(font, text="1900", color=0xFFFFFF, x=10, y=40)
text_group.append(text_calendar_year)
text_calendar = label.Label(font, text="01/01", color=0xFFFFFF, x=42, y=60, scale=5)
text_group.append(text_calendar)
text_calendar_nongli = label.Label(font, text="农历?年", color=0xFFFFFF, x=60, y=116)
text_group.append(text_calendar_nongli)

text_clock_day = label.Label(font, text="01/01", color=0xFFFFFF, x=50, y=10)
text_group.append(text_clock_day)
text_clock = label.Label(font, text="00:00", color=0xFFFFFF, x=42, y=60, scale=5)
text_group.append(text_clock)

#显示隐藏日历功能
def show_calendar(state:bool):
    text_calendar_year.hidden = not state
    text_calendar.hidden = not state
    text_calendar_nongli.hidden = not state

#显示隐藏时钟功能
def show_clock(state:bool):
    text_clock_day.hidden = not state
    text_clock.hidden = not state

#定义屏幕
display = board.DISPLAY

#关闭显示
show_calendar(False)
show_clock(False)

#定义按钮
button_switch_calendar_clock_pin= board.BUTTON
button_switch_calendar_clock = digitalio.DigitalInOut(button_switch_calendar_clock_pin)

#按钮事件
button_switch_calendar_clock.direction = digitalio.Direction.INPUT
button_switch_calendar_clock.pull = digitalio.Pull.UP
button_pressed = False

#True就是启动日历
calendar_clock = True
#第一次按按钮
first_button = False

#连上WiFi
print("Connecting to " + os.getenv('WIFI'))
wifi.radio.connect(os.getenv('WIFI'), os.getenv('PWD'))
#连上就停止阻塞
print("Connected!")

#调用ntp更新时间
print("Updating time...")
while True:
    try:
        pool = socketpool.SocketPool(wifi.radio)
        ntp = adafruit_ntp.NTP(pool, tz_offset=-4)
        rtc.RTC().datetime = ntp.datetime
        print("Updated!")
        break
    except:
        print("Fail to update time, retry...")
        time.sleep(1)


print("Press button to enter to calendar and clock...")

#一些api,包括农历获取,天气获取,还包含了防止过度访问api的保护措施
URL_nongli = "https://api.ahfi.cn/api/nlrq"
get_nongli = 6001
URL_tianqi = "https://www.haotechs.cn/ljh-wx/weather?adcode=340500"
get_tianqi = 3001
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())

#按下按钮时
while True:
    #按钮第一次被按下
    if not button_switch_calendar_clock.value:
        button_pressed = True
        #视频介绍的消除抖动操作
        time.sleep(0.1)
        #按钮按下后的动作
        cal = rtc.RTC().datetime
        year = cal[0]
        text_calendar_year.text = str(year)
        if not button_switch_calendar_clock.value:
            if not first_button:
                #显示屏幕
                display.show(text_group)
                first_button = True
                #预先响应界面
                response = requests.get(URL_nongli)
                text_calendar_nongli.text = response.json()["data"]["lunar"]
                response = requests.get(URL_tianqi)
                text_weather.text = "天气:"+response.json()["result"]["weather"]

            if calendar_clock:
                show_calendar(True)
                show_clock(False)
                #ntp获取日期
                while True:
                    try:
                        cal = rtc.RTC().datetime
                        year = cal[0]
                        mon = cal[1]
                        day = cal[2]
                        hour = cal[3]
                        minute = cal[4]
                        text_calendar.text = str(mon)+"/"+str(day)
                        time.sleep(0.1)
                        if get_nongli >= 6000:
                            #十分钟更新一次
                            response = requests.get(URL_nongli)
                            text_calendar_nongli.text = response.json()["data"]["lunar"]
                            get_nongli = 0
                        if get_tianqi >= 3000:
                            #五分钟更新一次
                            response = requests.get(URL_tianqi)
                            text_weather.text = "天气:"+response.json()["result"]["weather"]
                            get_tianqi = 0
                        get_nongli += 1
                        get_tianqi += 1
                    except:
                        pass
                    if not button_switch_calendar_clock.value:
                        break
            else:
                show_calendar(False)
                show_clock(True)
                #ntp获取时间
                while True:
                    try:
                        cal = rtc.RTC().datetime
                        year = cal[0]
                        mon = cal[1]
                        day = cal[2]
                        hour = cal[3]
                        minute = cal[4]
                        text_clock_day.text = str(mon)+"/"+str(day)
                        text_clock.text = str(hour+12)+":"+str(minute)
                        time.sleep(0.1)
                        if get_nongli >= 6000:
                            #十分钟更新一次
                            response = requests.get(URL_nongli)
                            text_calendar_nongli.text = response.json()["data"]["lunar"]
                            get_nongli = 0
                        if get_tianqi >= 3000:
                            #五分钟更新一次
                            response = requests.get(URL_tianqi)
                            text_weather.text = "天气:"+response.json()["result"]["weather"]
                            get_tianqi = 0
                        get_nongli += 1
                        get_tianqi += 1
                    except:
                        pass
                    if not button_switch_calendar_clock.value:
                        break

            calendar_clock = not calendar_clock
    else:
        button_pressed = False

注意:这里要提供字体文件,制作方法在视频up那里讲的很清楚。

注意:这个代码需要在“settings.toml”填上“WIFI”和“PWD”,分别是WiFi名称和密码。

注意:我使用的是免费API,不保证其可用性和可靠性,如果不能用了请自行修改。

这个代码我主要用到了ntp功能来获取网络时间(由adafruit提供的ntp模块个人感觉不是怎么太稳定,建议adafruit能不能添加一个可以自定义ntp服务器的功能)。网上关于使用circuitpython连接ntp服务器的教程很少,我本来是想自己写一个实现连接ntp服务器并获取时间的功能,但奈何自己技术有限不能有效格式化服务器推送的时间。最后我找到了adafruit官方能够实现该功能的核心代码(这里需要在lib文件夹里拷一个adafruit_ntp.mpy文件)

while True:
    try:
        pool = socketpool.SocketPool(wifi.radio)
        ntp = adafruit_ntp.NTP(pool, tz_offset=-4)
        rtc.RTC().datetime = ntp.datetime
        print("Updated!")
        break
    except:
        print("Fail to update time, retry...")
        time.sleep(1)
...
datetime = rtc.RTC().datetime

这里需要让ESP32的内置时钟同步ntp时间后,用python内置的标准方法 time.datetime 格式化时间(即 rtc.RTC().datetime )即可。

然后是对API的JSON数据格式转为dict字典的方法:

response = requests.get(URL_nongli)
text_calendar_nongli.text = response.json()["data"]["lunar"]

我本来是准备想找到能够解释JSON格式的库,后来从别人的实例学习发现request方法自带解析JSON功能(其实我还是希望这个方法还是独立比较好)。

最后,字体是可以通过scale参数进行放大的,但是放大倍速越大,像素化现象越明显,如果想要消除这种现象,建议还是生成一个像素大小比较小的字体(我用的是10pt),text_group也能通过scale放大,同样有像素化问题。

任务5:通过网络控制WS2812B(可选任务,非必做)

这里我将使用巴法云平台将ESP32与米家连接起来并使用米家控制灯的颜色,基于任务2改写而来。

import os
import ssl
import time
import wifi
import board
import neopixel
import displayio
import terminalio
import socketpool
import adafruit_imageload
from adafruit_display_text import label
import adafruit_minimqtt.adafruit_minimqtt as MQTT

print("ESP32-S3 mqtt MIJIA Application")

#连上WiFi
print("Connecting to " + os.getenv('WIFI'))
wifi.radio.connect(os.getenv('WIFI'), os.getenv('PWD'))
#连上就停止阻塞
print("Connected!")
print("="*3+"Please enter to MIJIA"+"="*3)
time.sleep(3)

#定义灯
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1)
pixel.brightness = 0.3

#显示“已接入米家”
MIJIAimg, palette = adafruit_imageload.load("mijia.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette)
tile_grid = displayio.TileGrid(MIJIAimg, pixel_shader=palette)
#显示状态
CMD = label.Label(terminalio.FONT, text="Ready", color=0xFFFFFF, x=20, y=120)
#放置画布
group = displayio.Group()
group.append(tile_grid)
group.append(CMD)
board.DISPLAY.show(group)

#定义mqtt客户端
def connect(mclient, userdata, flags, rc):
    #连上mqtt后
    mclient.subscribe(os.getenv('SUBSCRIBE_NAME'))

def message(mclient, feed_id, payload):
    #接收到消息后
    global CMD
    if payload == "on":
        #灯变红色
        pixel.fill((255,0,0))
        CMD.color = 0xFF0000
        CMD.text = "Red Light"
    elif payload == "on#100":
        #灯变绿色
        pixel.fill((0,255,0))
        CMD.color = 0x00FF00
        CMD.text = "Green Light"
    elif payload == "on#1":
        #灯变蓝色
        pixel.fill((0,0,255))    
        CMD.color = 0x0000FF
        CMD.text = "Blue Light"
    elif payload == "off":
        #关灯
        pixel.fill((0,0,0))    
        CMD.color = 0x000000
        CMD.text = "Ready"

pool = socketpool.SocketPool(wifi.radio)
mqtt_client = MQTT.MQTT(
    broker="bemfa.com",
    port=9501,
    client_id=os.getenv('ID'),
    username="",
    password="",
    socket_pool=pool,
    ssl_context=ssl.create_default_context(),
)
 
#连上mqtt绑定的请求
mqtt_client.on_connect = connect
mqtt_client.on_message = message

#连接mqtt
CMD.color = 0xFF0000
CMD.text = "Connecting to MQTT Cloud..."
mqtt_client.connect()
CMD.color = 0x00FF00
CMD.text = "Connected!"

#一些主进程
while True:
    try:
        mqtt_client.loop()
    except:
        CMD.color = 0xFF0000
        CMD.text = "Failed! Retry to connect..."
        mqtt_client.connect()
        CMD.color = 0x00FF00
        CMD.text = "Connected!"

注意:这个代码需要在“settings.toml”填上“WIFI”,“PWD”,“ID”和“SUBSCRIBE_NAME”,分别是WiFi名称,密码,用户私钥和订阅名单。

注意:我使用的是巴法云API,需要自己注册,点击进入巴法云mqtt控制台(非恰饭内容)。

点击这里看接入教程

这里我想讲一下mqtt接入代码里非常关键的一步。

mqtt_client = MQTT.MQTT(
    broker="bemfa.com",
    port=9501,
    client_id="用户私钥",
    username="",
    password="",
    socket_pool=pool,
    ssl_context=ssl.create_default_context(),
)

巴法云API的mqtt接入是不需要用户名和密码的,但网上关于circuitpython的mqtt接入教程大多数都是用的adafruit.io接入的,所以client_id常常被博主忽略。这里需要将巴法云给的用户私钥填入client_id中(就这步我踩坑了)

这一次,ESP32已接入米家(

(代码中的mijia.bmp,保存在分区中的根目录即可)

3.心得体会

这次Follow me第2期活动让我受益很多,让我了解到一个新的开发体系circuitpython,并让我在了解这一全新的开发平台的同时学到了很多新工具,这让我锻炼自己编程能力的同时,又增强了我对代码的理解能力和提出新解决方案的能力。感谢得捷电子,为我提供了一个能够让我了解到新技术和提升能力的一个平台。

附:任务代码

最新回复

看来搭载circuitpython是个亮点   详情 回复 发表于 2023-11-24 07:28
点赞 关注
 
 

回复
举报

6807

帖子

0

TA的资源

五彩晶圆(高级)

沙发
 

看来搭载circuitpython是个亮点

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表