【得捷电子Follow me第2期】CircuitPython完成任务1-5作业提交
[复制链接]
本帖最后由 ttxiaotaoge 于 2023-10-11 21:30 编辑
根据要求本项目完成如下功能:
完成任务:
任务1:控制屏幕显示中文(必做任务)----- 完成屏幕的控制,并且能显示中文
任务2:网络功能使用(必做任务)----- 完成网络功能的使用,能够创建热点和连接到WiFi
任务3:控制WS2812B(必做任务)---- 使用按键控制板载Neopixel LED的显示和颜色切换
任务4:从下方5个分任务中选择1个感兴趣的完成即可(必做任务)
■ 分任务1:日历&时钟——完成一个可通过互联网更新的万年历时钟,并显示当地的天气信息
■ 分任务2:WS2812B效果控制——完成一个Neopixel(12灯珠或以上)控制器,通过按键和屏幕切换展示效果
任务5:通过网络控制WS2812B(可选任务,非必做)----- 结合123,在手机上通过网络控制板载Neopixel LED的显示和颜色切换,屏幕同步显示状态
在未收到板子之前,我有写个一篇介绍如何实现上述功能的文档,热度还蛮高的,贴在下方供大家参考。
【得捷电子Follow me第2期】CircuitPython入门到完成任务1-5详细教程 - DigiKey得捷电子技术专区 - 电子工程世界-论坛 (eeworld.com.cn)
本篇文章为任务提交贴,下面将按照要求进行任务完成的说明介绍与建议。
第一部分
任务一:3-5分钟短视频
视频链接如下:
follow me第二期任务演示-follow me第二期任务演示-EEWORLD大学堂
第二部分
任务二:任务/项目总结报告
任务1:控制屏幕显示中文(必做任务)----- 完成屏幕的控制,并且能显示中文
功能对应的主要代码片段及说明
上述为软件的整体结构,由于我们需要显示中文,所以我们需要添加字库文件,按照我之前的文章,我们创建了一款16尺寸的测试字体,并存放在fonts文件夹下,待代码调用。
#通用库
import board
#屏幕显示库
import displayio
#字体导入库
from adafruit_bitmap_font import bitmap_font
#显示标签库
from adafruit_display_text.label import Label
显示图片库
import adafruit_imageload
# displayDriver_Begin 显示驱动
display = board.DISPLAY
# 创建一个显示组件
my_display_group = displayio.Group()
print("Display Init Success\n")
# displayDriver_End
# 添加中文字体
font_file = "fonts/test16-16.pcf"
font_Chinese = bitmap_font.load_font(font_file)
print("fonts Init Success\n")
# DrawTimeandTemp_Beign 文字显示
# 标签中文字的内容
time_label_text = "{}-{:02}-{:02}\n"\
"时间:{:02}:{:02}:{:02}\n"\
"地区:{}\n"\
"温度:{}\t"\
"天气:{}\n".format(r.datetime.tm_year, r.datetime.tm_mon, r.datetime.tm_mday, \
r.datetime.tm_hour, r.datetime.tm_min, r.datetime.tm_sec, \
current_area, current_temp, current_weather)
# 标签的属性:字体,文字内容,颜色,位置
time_label = Label(font = font_Chinese, text=time_label_text, color = 0xff00ff)
time_label.x = 10
time_label.y = 60
# 将标签添加到显示组件中
my_display_group.append(time_label)
# DrawTimeandTemp_End
功能展示及说明
根据代码的功能,我们实现了在屏幕上使用我们设定的字体显示中文汉字,如下图所示。
心得体会建议
本任务最大的困难点,可能是创建中文字库了,建议官方可以提供一些不同语言的字库文件,便于开发者使用。
|
任务2:网络功能使用(必做任务)----- 完成网络功能的使用,能够创建热点和连接到WiFi
功能对应的主要代码片段及说明
本任务要求我们创建热度和连接wifi,circuitpython固件的优势就是本身boot支持连接wifi,因此我们在seting中配置宏定义即可,同时我们在代码中进行判断,如果未连接wifi,我们就创建一个esp32名称的热点,同时我们将它的相关信息打印在屏幕上,相关代码如下:
settings.toml中的配置
CIRCUITPY_WIFI_SSID = "ttxiaotaoge"
CIRCUITPY_WIFI_PASSWORD = "zt123321"
CIRCUITPY_WEB_API_PASSWORD= "123"
#网络相关库
import wifi
# WiFi状态查询,未连接WIFI则不联网更新数据,开启AP模式
wificonnect_State = wifi.radio.connected
print("wificonnect_State",wificonnect_State)
if(wificonnect_State == False):
wifi.radio.start_ap(ssid = 'Esp32', password = '')
Wifi_Mode = 'STA' if(wificonnect_State == True) else 'AP'
Wifi_ip = wifi.radio.ipv4_address if(wificonnect_State == True) else wifi.radio.ipv4_address_ap
# 显示WiFi IP 和 Mode
text_Wifi = "WiFi:{} web:ip:1080/client\n{}\n".format(Wifi_Mode,Wifi_ip)#更新RGB状态
text_Wifi_title = Label(terminalio.FONT, text=text_Wifi, color = 0xffffff)
text_Wifi_title.x = 5
text_Wifi_title.y = 5
my_display_group.append(text_Wifi_title)
功能展示及说明
连接wifi状态:
未连接wifi状态:
心得体会建议
circuitpython固件支持wifi自动连接,这种性质非常不错,同时我们可以直接访问ip来进入在线编程模式,非常方便使用,相关界面如下:
|
任务3:控制WS2812B(必做任务)---- 使用按键控制板载Neopixel LED的显示和颜色切换
功能对应的主要代码片段及说明
根据该要求,我们需要控制2个东西,第一个是按键,第二个是LED,按键本身就是输入,我们直接使用输入状态读取即可;NeopixelLED我们需要导入相关的库来实现控制,相关代码如下:
# RGBLED显示库
import neopixel
#构造函数——按键控制RGB, 实现每次按键让LED的rgb属性提高,直到最亮再复位至0
i = 0
Enter_flag = False
def Button0_Work():
global i
global Enter_flag
global RGBr, RGBg, RGBb
if(Button0.value == False and Enter_flag == False):
time.sleep(0.05)#延时消抖
if(Button0.value == False and Enter_flag == False):
i+=1
RGBr, RGBg, RGBb = 8*i, 8*i, 8*i
pixels.fill((RGBr, RGBg, RGBb))
Enter_flag = True
print("pixels change\n",i)
if(i>20):
i = 0
elif(Button0.value == True and Enter_flag == True):
time.sleep(0.05)#延时消抖
if(Button0.value == True and Enter_flag == True):
Enter_flag = False
# 板载RGB
pixels = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2, auto_write=True)
# 拓展RGB16路灯珠
pixels16 = neopixel.NeoPixel(board.A0, 16, brightness=0.2, auto_write=True)
# pixels.fill((8, 8, 8))
pixels[0] = 0xff0000
pixels.show()
print("RGBDriver Init Success\n")
# RGBDriver_End
while True:
# 按键控制LED始终循环使能
Button0_Work()
功能展示及说明
相关展示如下:
心得体会建议
Neopixel的库让我们可以非常容易的驱动rgbLED,但我们最好也要了解下RGB是如何实现驱动,一串带有编码性质的方波可以让RGB为我闪耀。
|
任务4:分任务1:日历&时钟——完成一个可通过互联网更新的万年历时钟,并显示当地的天气信息
功能对应的主要代码片段及说明
该任务为本期任务最核心的任务,相关代码如下:
代码运行后将自动联网,如联网成功会自动同步网络时间和获取天气信息并显示在LCD屏幕上,时间显示为经过同步的RTC内部时钟,天气地区配置在代码中设定,使用知心天气API
若未成功联网将会开启AP模式,默认为SSID为ESP32无密码。显示屏会显示当前网络模式和访问ip,同时显示本地时间(RTC内部时钟)
其中API获取网络天气和时间,需要根据自身的需求进行修改,包含知心天气的KEY和地区
TIME_DATA_SOURCE = "http://worldtimeapi.org/api/timezone/Asia/Shanghai"
WEATHER_DATA_SOURCE = "http://api.seniverse.com/v3/weather/now.json?key=SBmmNRnXdiTFi8UQw&location=nanjing&language=zh-Hans&unit=c"
#网络相关库
import wifi
import socketpool
import ssl
import adafruit_requests
from adafruit_httpserver import Server, Request, Response, Websocket, GET
#构造函数——解码天气
def parse_weather(weather_str):
# Extract the time part from the datetime string
area = weather_str['results'][0]['location']['name']
temp = weather_str['results'][0]['now']['temperature']
weather = weather_str['results'][0]['now']['text']
return area, temp, weather
#构造函数——解码时间
def parse_time(datetime_str):
# Extract the time part from the datetime string
date_str = datetime_str.split("T")[0]
time_str = datetime_str.split("T")[1].split(".")[0]
hour, minute, second = map(int, time_str.split(":"))
date = date_str
return hour, minute, second, date
#构造函数——更新网络数据
def Update_Data():
global r
global current_area, current_temp, current_weather
current_area = current_temp = current_weather = None
r = rtc.RTC()
if(wificonnect_State == True):
#WeatherUpdate Begin
requests = adafruit_requests.Session(pool, ssl.create_default_context())
response_Weather = requests.get(WEATHER_DATA_SOURCE)
data_Weather = response_Weather.json()
response_Weather= requests.get(WEATHER_DATA_SOURCE)
response_Weather.socket.close()
current_area, current_temp, current_weather = parse_weather(data_Weather)
print("WeatherUpdate Success\n")
#WeatherUpdate End
#ClockTimeUpdate Begin
requests = adafruit_requests.Session(pool, ssl.create_default_context())
response_clock = requests.get(TIME_DATA_SOURCE)
data_clock = response_clock.json()
response_clock= requests.get(WEATHER_DATA_SOURCE)
response_clock.socket.close()
current_hour, current_minute, current_second, current_date = parse_time(data_clock["datetime"])
r.datetime = time.struct_time((int(current_date[0:4]), int(current_date[5:7]), int(current_date[8:10]), \
current_hour, current_minute, current_second, 0, -1, -1))
print("ClockTimeUpdate Success\n")
#ClockTimeUpdate Begin
# GetandSetHttp_Begin
TIME_DATA_SOURCE = "http://worldtimeapi.org/api/timezone/Asia/Shanghai"
WEATHER_DATA_SOURCE = "http://api.seniverse.com/v3/weather/now.json?key=SBmmNRnXdiTFi8UQw&location=nanjing&language=zh-Hans&unit=c"
pool = socketpool.SocketPool(wifi.radio) # 网络管理,触发http操作
server = Server(pool, debug=True)
websocket: Websocket = None
next_message_time = time.monotonic()#数据更新时钟,以秒为单位变化
# GetTimeandTemp_Beign
Update_Data()
# GetTimeandTemp_End
# DrawTimeandTemp_Beign
time_label_text = "{}-{:02}-{:02}\n"\
"时间:{:02}:{:02}:{:02}\n"\
"地区:{}\n"\
"温度:{}\t"\
"天气:{}\n".format(r.datetime.tm_year, r.datetime.tm_mon, r.datetime.tm_mday, \
r.datetime.tm_hour, r.datetime.tm_min, r.datetime.tm_sec, \
current_area, current_temp, current_weather)
time_label = Label(font = font_Chinese, text=time_label_text, color = 0xff00ff)
time_label.x = 10
time_label.y = 60
my_display_group.append(time_label)
# DrawTimeandTemp_End
#UI show Begin
display.show(my_display_group)
print("display show Success\n")
#UI show End
#判断网络模式,设定location.host
if(wificonnect_State == True):
server.start(str(wifi.radio.ipv4_address), port = 1080)
else:
server.start(str(wifi.radio.ipv4_address_ap), port = 1080)
# GetandSetHttp_Begin
time.sleep(2)#等待2秒进入循环
while True:
#httpSeverLoop_Begin
server.poll()
# Check for incoming messages from client
if websocket is not None:
if (data := websocket.receive(True)) is not None:
RGBr, RGBg, RGBb = int(data[1:3], 16), int(data[3:5], 16), int(data[5:7], 16)
pixels.fill((RGBr, RGBg, RGBb))
# Send a message every second
if websocket is not None and next_message_time < time.monotonic():
time.sleep(0.2)
if websocket is not None:
Button_count = i
websocket.send_message(str(Button_count))
next_message_time = time.monotonic() + 1
#httpSeverLoop_End
#Other_Loop
text_RGB_title.color = (RGBr, RGBg, RGBb)
text_RGB_title.text = "RGB:{:03}:{:03}:{:03}\nKEY:{:02}".format(RGBr, RGBg, RGBb, i)#更新RGB状态
time_label.text = "{}-{:02}-{:02}\n"\
"时间:{:02}:{:02}:{:02}\n"\
"地区:{}\n"\
"温度:{}\t"\
"天气:{}\n".format(r.datetime.tm_year, r.datetime.tm_mon, r.datetime.tm_mday, \
r.datetime.tm_hour, r.datetime.tm_min, r.datetime.tm_sec, \
current_area, current_temp, current_weather)
Button0_Work()
上述代码中,我们通过访问对应网站,获取其json数据,并通过json库,解析其中的数据。
功能展示及说明
相关演示如下:
心得体会建议
本任务的重点是从网络上获取对应的天气和时间信息,以及通过json解析其中的关键数据。
|
任务4:分任务2:WS2812B效果控制——完成一个Neopixel(12灯珠或以上)控制器,通过按键和屏幕切换展示效果
功能对应的主要代码片段及说明
相关代码如下:
#构造函数——屏幕跑马灯
set_colour = 0
set_colour1 = 0
set_colour2 = 1
set_colour3 = 2
time_count = 0
def ColourRun_Work():
global set_colour, set_colour1, set_colour2, set_colour3
global time_count
time_count += 1
if(time_count >= 5):
time_count = 0
if(i%2 == 0):
for x in range(150, 170):
for y in range(100, 110):
bitmap[x, y] = set_colour1 # Set pixel at (x, y) to blue
if (set_colour1 < 4):
set_colour1 += 1
else:
set_colour1 = 0
for x in range(175, 195):
for y in range(100, 110):
bitmap[x, y] = set_colour2 # Set pixel at (x, y) to blue
if (set_colour2 < 4):
set_colour2 += 1
else:
set_colour2 = 0
for x in range(200, 220):
for y in range(100, 110):
bitmap[x, y] = set_colour3 # Set pixel at (x, y) to blue
if (set_colour3 < 4):
set_colour3 += 1
else:
set_colour3 = 0
else:
for x in range(150, 170):
for y in range(100, 110):
bitmap[x, y] = set_colour # Set pixel at (x, y) to blue
for x in range(175, 195):
for y in range(100, 110):
bitmap[x, y] = set_colour # Set pixel at (x, y) to blue
for x in range(200, 220):
for y in range(100, 110):
bitmap[x, y] = set_colour # Set pixel at (x, y) to blue
if (set_colour < 4):
set_colour += 1
else:
set_colour = 0
pixels16[0 + set_colour] = 0xff0000
pixels16[1 + set_colour] = 0x00FF00
pixels16[2 + set_colour] = 0x0000FF
pixels16.show()
我们在代码中实现了一个屏幕上的跑马灯,以及让灯珠也按照上述方式进行跑动闪烁
功能展示及说明
通过按键实现切换不同的炫彩流水灯效果如下:
心得体会建议
该功能同任务3,不过我们增加了对多个灯珠的驱动显示效果,实际看起来非常优雅。
|
任务5:通过网络控制WS2812B(可选任务,非必做)----- 结合123,在手机上通过网络控制板载Neopixel LED的显示和颜色切换,屏幕同步显示状态
功能对应的主要代码片段及说明
该功能我们使用网页端来完成,该方法有多个优势,既让代码更佳见解,也同时适配了多端访问的问题,本质上我们是使用websocket来访问esp32s3
相关代码如下:
websocket: Websocket = None
next_message_time = time.monotonic()#数据更新时钟,以秒为单位变化
HTML_TEMPLATE = """
<html lang="en">
<head>
<!--注释:强制html使用utf-8编码-->
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Web控制中心</title>
</head>
<body>
<p>按键状态: <strong>-</strong></p>
<p>RGB 颜色: <input type="color"></p>
<script>
const Button_count = document.querySelector('strong');
const colorPicker = document.querySelector('input[type="color"]');
let ws = new WebSocket('ws://' + location.host + '/connect-websocket');
ws.onopen = () => console.log('WebSocket connection opened');
ws.onclose = () => console.log('WebSocket connection closed');
ws.onmessage = event => Button_count.textContent = event.data;
ws.onerror = error => Button_count.textContent = error;
colorPicker.oninput = debounce(() => ws.send(colorPicker.value), 200);
<!--注释:创建一个延时函数,消除RGB调整抖动-->
function debounce(callback, delay = 1000) {
let timeout
return (...args) => {
clearTimeout(timeout)
timeout = setTimeout(() => {
callback(...args)
}, delay)
}
}
</script>
</body>
</html>
"""
#创建Http服务器
@server.route("/client", GET)
def client(request: Request):
return Response(request, HTML_TEMPLATE, content_type="text/html")
#创建websocket服务
@server.route("/connect-websocket", GET)
def connect_client(request: Request):
global websocket # pylint: disable=global-statement
if websocket is not None:
print("websocket.close")
websocket.close() # Close any existing connection
websocket = Websocket(request)
return websocket
功能展示及说明
相关展示如下,我们可以很轻松的获取按键的状态,以及控制esp32的板载RGBLED
心得体会建议
由于circuitpython本身带了网页端,为了避免冲突,我们需要重新定义网页的端口号,避开使用http默认的80端口
|
第三部分
任务三:可编译下载的代码
代码下载地址链接:
followme第二期任务代码-嵌入式开发相关资料下载-EEWORLD下载中心
感谢得捷电子提供的本次follow me活动,上述为本次活动的全部完成内容,感谢观看。
|