本帖最后由 eew_sqNFJw 于 2023-11-3 01:19 编辑
一 任务汇总视频
二 项目介绍
这次任务主要完成了五个任务分别是控制屏幕显示中文,网络功能使用,控制WS2812B,日历&时钟,通过网络控制WS2812B。使用到的硬件是Adafruit ESP32-S3 TFT Feather 开发板使用乐鑫ESP32-S3芯片,支持WiFi和蓝牙,内置 IPS TFT 彩色显示屏 ESP32-S3 开发板,4 MB闪存和2 MB PSRAM 。
三 项目任务详细介绍
任务一 控制屏幕显示中文
在Adafruit ESP32-S3 TFT Feather开发版上显示中文其实很简单只要引用相应的库,然后引用字体文件就可以很容易的显示中文
效果图
先把代码贴上来
- import board
- from io import BytesIO
- import displayio
- import busio
- from digitalio import DigitalInOut
- import adafruit_imageload
- from adafruit_display_text import label
- from adafruit_bitmap_font import bitmap_font
-
- display = board.DISPLAY
- group = displayio.Group(scale=1)
- color = 0xffffff
-
- font = bitmap_font.load_font("/font/font.pcf")
- date = label.Label(font, text="欢迎参加得捷电子", color=color)
- date.x = 0
- date.y = 30
- group.append(date)
- date = label.Label(font, text="Follow me第2期", color=color)
- date.x = 0
- date.y = 60
- group.append(date)
- display.show(group)
- while True:
- pass
现在给大家详细讲解一下代码
创建display
display = board.DISPLAY
创建group布局容器并设置放大倍率
group = displayio.Group(scale=1)
设置字体颜色为白色
color = 0xffffff
加载字体文件
font = bitmap_font.load_font("/font/font.pcf")
创建文本框并且设置在屏幕中显示的位置
label = label.Label(font, text="欢迎参加得捷电子", color=color)
label.x = 0
label.y = 30
#将文本框添加到group中
group.append(label)
屏幕显示
display.show(group)
任务二 网络功能使用
这个任务主要分为2个子任务,一个是连接wifi并且通过http请求当前网络动态公网ip还有一个是在开发版上起一个热点,现在我们讲解一下第一个任务
效果图
- import board
- from io import BytesIO
- import displayio
- import busio
- from digitalio import DigitalInOut
- import adafruit_imageload
- from adafruit_display_text import label
- from adafruit_bitmap_font import bitmap_font
-
- import socketpool
- import adafruit_requests
- import ssl
- import wifi
- import time
- from adafruit_datetime import datetime, date,timezone,timedelta
- import json
-
-
-
- TEXT_URL= "http://vv.video.qq.com/checktime?otype=json"
- pool = socketpool.SocketPool(wifi.radio)
- requests = adafruit_requests.Session(pool, ssl.create_default_context())
- response = requests.get(TEXT_URL)
-
- timestamp = time.time()
- txt = response.text.split("QZOutputJson=")[1]
- print(txt)
-
- data = json.loads(txt.replace(";",""))
- print(data["ip"])
- display = board.DISPLAY
- group = displayio.Group(scale=1)
- color = 0xffffff
-
- font = bitmap_font.load_font("/font/font.pcf")
- date = label.Label(font, text=data["ip"], color=color)
- date.x = 0
- date.y = 30
- group.append(date)
- display.show(group)
- while True:
- pass
该代码主要实现了从网络时间服务器获取时间戳数据,解析出IP信息,并在显示器上显示这个IP,初始化网络请求会话session,用于发送HTTP请求。具体流程如下
a. 向时间服务器的URL发送GET请求,获取包含时间戳数据的响应。
b. 从响应中提取出JSON字符串,并解析为JSON数据。
c. 从JSON数据中获取IP信息。
d. 初始化显示库,创建显示组group,设置显示参数。
e. 使用显示库的Label组件显示取得的IP信息。
f. 循环显示这个Label。
总体上,利用了网络请求库、JSON解析和显示库,实现了获取网络数据,提取信息,并在显示器上输出的完整流程。主要运用了CircuitPython的网络编程、JSON处理和GUI显示等功能。接着我们讲解第二个子任务起网络热点功能
- import board
- from io import BytesIO
- import displayio
- import busio
- from digitalio import DigitalInOut
- import adafruit_imageload
- from adafruit_display_text import label
- from adafruit_bitmap_font import bitmap_font
-
- import socketpool
- import adafruit_requests
- import ssl
- import wifi
-
- ssid = "eeworld"
- password = "12345678"
- print("ssid:"+ssid)
- print("pwd:"+password)
- wifi.radio.start_ap(ssid, password)
-
-
- while True:
- pass
这段代码是用于创建一个 WiFi 访问点(AP)的,首先定义了ssid和password两个变量,分别表示要创建的WiFi网络的名称和密码。然后打印出这两个变量的值,用于调试和确认。
关键的一行代码是:
wifi.radio.start_ap(ssid, password)
这个start_ap方法属于wifi模块中的radio对象,它可以用来创建一个软AP。方法的参数就是前面定义的ssid和password。这样就会用这两个参数创建一个名称为"eeworld",密码为"12345678"的WiFi网络。最后一个while True空循环语句,目的是保持代码持续运行不退出。
任务三 WS2812B效果控制
在adafruit esp32 开发版中控制WS2812B也非常容易只要使用neopixel库和adafruit_led_animation库便可轻松控制
- import board
- import neopixel
- from adafruit_led_animation.animation.blink import Blink
- import adafruit_led_animation.color as color
-
-
- pixel_pin = board.NEOPIXEL
-
- num_pixels = 1
-
- pixels = neopixel.NeoPixel(pixel_pin, num_pixels)
- blink = Blink(pixels, 0.5, color.RED)
-
- while True:
- blink.animate()
- pass
这段代码的主要功能是利用NeoPixel库控制RGB LED,并在while循环中调用Blink对象的animate方法来实现闪烁效果。首先导入了neopixel库,并创建了一个NeoPixel对象pixels,连接到板子上的NEOPIXEL引脚,指定了LED数量为1个。然后创建了一个Blink类的对象blink,传入参数为刚刚创建的pixels对象,设置点亮时间0.5秒,颜色为红色。在while True循环中,调用blink对象的animate方法来点亮LED。这段代码会让连接在NEOPIXEL引脚上的一个RGB LED以0.5秒的间隔闪烁红色。
任务四 日历&时钟
这个任务主要实现了一个时钟日历功能,可以查看当前日期时间还有查看当前的天气,温度,湿度,pm2.5
- import board
- from io import BytesIO
- import displayio
- import busio
- from digitalio import DigitalInOut
- import adafruit_imageload
- from adafruit_display_text import label
- from adafruit_bitmap_font import bitmap_font
-
- import socketpool
- import adafruit_requests
- import ssl
- import wifi
- import time
- from adafruit_datetime import datetime, date,timezone,timedelta
- import json
- import re
- from adafruit_display_shapes.roundrect import RoundRect
- import asyncio
- import rtc
-
- display = board.DISPLAY
- group = displayio.Group(scale=1)
- color = 0xffffff
-
- font = bitmap_font.load_font("/font/font.pcf")
-
- TEXT_URL= "http://vv.video.qq.com/checktime?otype=json"
- pool = socketpool.SocketPool(wifi.radio)
- requests = adafruit_requests.Session(pool, ssl.create_default_context())
- response = requests.get(TEXT_URL)
-
- timestamp = time.time()
- txt = response.text.split("QZOutputJson=")[1]
- print(txt)
-
- data = json.loads(txt.replace(";",""))
- local_time = datetime.fromtimestamp(data["t"])
- rtc.RTC().datetime=time.localtime(data["t"] + 8*3600)
-
- token = ""
- headers = {"token":token}
-
- image, palette = adafruit_imageload.load("/images/bg1.png")
-
-
-
-
- grid = displayio.TileGrid(image, pixel_shader=palette)
-
- grid.x = display.width // 2 - grid.tile_width // 2
- grid.y = display.height // 2 - grid.tile_height // 2
-
-
-
-
- group.append(grid)
-
-
-
-
-
- time1 = label.Label(font, text="00:00:00", color=0xcad0b4, scale=2)
- time1.x = 10
- time1.y = 50
-
- date = label.Label(font, text="1967年01月01日", color=0xcad0b4, scale=1)
- date.x = 10
- date.y = 15
-
-
-
- weather = label.Label(font, text="晴", color=0x4B4B4B, scale=1)
- weather.x = 20
- weather.y = 85
- roundrect = RoundRect(10, 70,weather.bounding_box[2]+113, 30,5,fill=0x33CC66, outline=0x33CC66)
-
-
- group.append(roundrect)
-
- wind = label.Label(font, text="东北风1级", color=0x4B4B4B, scale=1)
- wind.x = 20
- wind.y = 120
-
- roundrect1 = RoundRect(10, 105,wind.bounding_box[2]+40, 30,5,fill=0xFF9966, outline=0xFF9966)
-
- group.append(roundrect1)
- group.append(date)
- group.append(time1)
-
- group.append(wind)
- group.append(weather)
-
-
- image, palette = adafruit_imageload.load("/images/6.png")
-
- palette.make_transparent(0)
-
- grid = displayio.TileGrid(image, pixel_shader=palette)
-
- grid.x = 160
- grid.y = 70
- group.append(grid)
-
- image, palette = adafruit_imageload.load("/images/3.png")
-
- palette.make_transparent(0)
-
- grid = displayio.TileGrid(image, pixel_shader=palette)
-
- grid.x = 160
- grid.y = 90
- group.append(grid)
-
-
- image, palette = adafruit_imageload.load("/images/7.png")
-
- palette.make_transparent(0)
-
- grid = displayio.TileGrid(image, pixel_shader=palette)
-
- grid.x = 160
- grid.y = 110
- group.append(grid)
-
-
- temp = label.Label(font, text="18.5°", color=0x1296db, scale=1)
- temp.x = 190
- temp.y = 80
- group.append(temp)
-
- humidity = label.Label(font, text="81", color=0x1296db, scale=1)
- humidity.x = 190
- humidity.y = 100
- group.append(humidity)
-
- pm25 = label.Label(font, text="68", color=0x1296db, scale=1)
- pm25.x = 190
- pm25.y = 120
- group.append(pm25)
-
- display.show(group)
-
- def updateWeather():
- url= "https://api.ip138.com/weather/?code=310000&token="+token
- response = requests.get(url)
- data = response.json()
- weather.text = data['data']['weather']
- wind.text = data['data']['wind']
- humidity.text = data['data']['humidity']
- temp.text = data['data']['temp']
- pm25.text = data['data']['pm25']
-
- async def updateWeatherTask():
- val = 1
- while True:
- updateWeather()
-
- await asyncio.sleep(3600)
-
- async def updateTimeTask():
- val = 1
- while True:
- t = time.localtime(time.time())
- date.text='%d年%02d月%02d日'%(t.tm_year, t.tm_mon, t.tm_mday)
- time1.text='%02d:%02d:%02d'%(t.tm_hour, t.tm_min,t.tm_sec)
-
- await asyncio.sleep(1)
-
- async def main():
- task1 = asyncio.create_task(updateWeatherTask())
- task2 = asyncio.create_task(updateTimeTask())
- await asyncio.gather(
- task2,
- task1
- )
- asyncio.run(main())
-
-
这段代码主要是使用了网络请求、时间、天气API、GUI和异步编程,实现了一个可以实时显示时间和天气的程序,并实时更新,具体流程如下
a.初始化显示器,创建显示组group。
b.发送网络请求获取时间戳数据,解析出时间信息,同步到RTC。
c.加载图片作为背景,添加到group。
d. 创建多个Label组件显示时间、天气等信息,添加到group。
e. 将group显示到显示器上。
f.定义了updateWeather()函数从网络获取最新天气数据,更新显示。
g.定义了两个异步任务updateTimeTask()和updateWeatherTask(),分别每秒和每3600秒更新时间和天气。
h.在main()异步函数中同时执行这两个任务。
i.使用asyncio.run启动主异步逻辑。
任务五 通过网络控制WS2812B
要实现网络控制WS2812B的功能,我选择的方案是通过微信小程序来控制rgb led,通讯协议使用mqtt,因此还需要一个mqtt的服务端,我没有选择公共的mqtt服务,而是使用.net core 搭建了一套简单的mqtt服务,同时支持mqtt和socket两种方式
mqtt服务端的实现代码
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- using Microsoft.AspNetCore.Builder;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.AspNetCore.HttpsPolicy;
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.Extensions.Configuration;
- using Microsoft.Extensions.DependencyInjection;
- using Microsoft.Extensions.Hosting;
- using Microsoft.Extensions.Logging;
- using Microsoft.OpenApi.Models;
- using MQTTnet;
- using MQTTnet.AspNetCore;
- using MQTTnet.Server;
-
- namespace mqtt
- {
- public class Startup
- {
- public Startup(IConfiguration configuration)
- {
- Configuration = configuration;
- }
-
- public IConfiguration Configuration { get; }
-
-
- public void ConfigureServices(IServiceCollection services)
- {
- services
- .AddHostedMqttServer(mqttServer => mqttServer.WithoutDefaultEndpoint())
- .AddMqttConnectionHandler()
- .AddConnections();
- }
-
-
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- app.UseRouting();
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapConnectionHandler<MqttConnectionHandler>(
- "/mqtt",
- httpConnectionDispatcherOptions => httpConnectionDispatcherOptions.WebSockets.SubProtocolSelector =
- protocolList =>
- protocolList.FirstOrDefault() ?? string.Empty);
- });
-
- app.UseMqttServer(server =>
- {
-
- });
-
- }
- }
- }
-
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- using Microsoft.AspNetCore;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.Extensions.Configuration;
- using Microsoft.Extensions.Hosting;
- using Microsoft.Extensions.Logging;
- using MQTTnet;
- using MQTTnet.AspNetCore;
- using MQTTnet.Client;
- using MQTTnet.Server;
-
-
- namespace mqtt
- {
- public class Program
- {
- public static Task Main(string[] args)
- {
-
- return BuildWebHost(args).RunAsync();
-
- }
- private static IWebHost BuildWebHost(string[] args)
- {
- return WebHost.CreateDefaultBuilder(args)
- .UseKestrel(o =>
- {
- o.ListenAnyIP(1883, l => l.UseMqtt());
- o.ListenAnyIP(5000);
- })
- .UseStartup<Startup>()
- .Build();
- }
-
-
- }
-
-
- }
-
该代码构建了一个支持MQTT和HTTP双协议的ASP.NET Core主机,可以用来开发集成两种服务的Web应用程序,主要工作是利用.NET Core框架搭建主机并配置服务端口
小程序代码
-
- import mqtt from "../../mqtt4.1.0.js"
-
- const options = {
- keepalive: 30,
-
- connectTimeout: 4000,
- clientId: 'sdfkskr43',
- port: 5000,
- username: '',
- password: '',
- }
-
- var client = mqtt.connect('wx://127.0.0.1/mqtt', options)
- client.on('connect', (e) => {
- console.log('服务器连接成功', e)
- })
-
- Page({
- data:{
- colorData: {
-
- hueData: {
- colorStopRed: 255,
- colorStopGreen: 0,
- colorStopBlue: 0,
- },
-
- pickerData: {
- x: 0,
- y: 480,
- red: 0,
- green: 0,
- blue: 0,
- hex: '#000000'
- },
-
- barY: 0
- },
- rpxRatio: 1
-
- }
- ,
- onLoad() {
- var _this = this
- wx.getSystemInfo({
- success(res) {
- _this.setData({
- rpxRatio: res.screenWidth / 750
- })
- }
- })
- },
-
- onChangeColor(e) {
-
- this.setData({
- colorData: e.detail.colorData
- })
- var rgbdata = {}
- rgbdata.R = this.data.colorData.pickerData.red
- rgbdata.G = this.data.colorData.pickerData.green
- rgbdata.B = this.data.colorData.pickerData.blue
- client.publish('rgbTopic',JSON.stringify(rgbdata), { qos: 2 }, function (err) {
- console.log('send', err)
- })
- }
- })
-
这段代码使用微信小程序的MQTT库实现了一个简单的RGB颜色控制示例,主要功能有:
a. 初始化并连接MQTT客户端
使用mqtt.connect创建MQTT客户端,指定连接参数如客户端id、服务器地址等,并绑定connect事件回调打印连接成功日志。
b.小程序页面初始化
在onLoad回调中获取系统信息,设置rpx转换比例。
c.实现颜色选择组件
页面json中引入微信提供的color-picker组件,在wxml中渲染。在js中通过onChangeColor回调获取选中的颜色值。
d.通过MQTT发布RGB数据
在onChangeColor中将颜色值转换为RGB对象,通过MQTT的publish方法发布到rgbTopic主题上,实现和服务端的通信。
e.MQTT配置
使用ES6语法导入MQTT包,指定连接和订阅选项如keepalive时间、用户名密码等。
该代码演示了微信小程序中通过MQTT与后端通信的方式,实现了一个简单的RGB颜色控制和同步的示例,主要涉及小程序的页面开发、MQTT的使用以及组件的绑定应用。
- import board
- from io import BytesIO
- import displayio
- import busio
- from digitalio import DigitalInOut
- import adafruit_imageload
- from adafruit_display_text import label
- from adafruit_bitmap_font import bitmap_font
-
- import socketpool
- import adafruit_requests
- import ssl
- import wifi
- import time
- from adafruit_datetime import datetime, date,timezone,timedelta
- import json
- import re
- from adafruit_display_shapes.roundrect import RoundRect
- import asyncio
- import rtc
- from adafruit_esp32spi import adafruit_esp32spi
- import adafruit_esp32spi.adafruit_esp32spi_socket as socket
- import adafruit_minimqtt.adafruit_minimqtt as MQTT
- import neopixel
- from adafruit_led_animation.animation.blink import Blink
- import adafruit_led_animation.color as color
-
-
- pixel_pin = board.NEOPIXEL
- num_pixels = 1
- pixels = neopixel.NeoPixel(pixel_pin, num_pixels)blink = Blink(pixels, 0.5, color.ORANGE)
-
-
- def connect(mqtt_client, userdata, flags, rc):
- mqtt_client.subscribe("rgbTopic")
-
-
-
- def disconnect(mqtt_client, userdata, rc):
- print("Disconnected")
-
-
- def subscribe(mqtt_client, userdata, topic, granted_qos):
- print("Subscribed to {0} with QOS level {1}".format(topic, granted_qos))
-
-
- def unsubscribe(mqtt_client, userdata, topic, pid):
- print("Unsubscribed from {0} with PID {1}".format(topic, pid))
-
-
- def publish(mqtt_client, userdata, topic, pid):
- print("Published to {0} with PID {1}".format(topic, pid))
-
-
- def message(client, topic, message):
- if(topic == "rgbTopic"):
- jsonData = json.loads(message)
- r = int(jsonData["R"])
- g = int(jsonData["G"])
- b = int(jsonData["B"])
- global blink
- blink = Blink(pixels, speed=0.5, color=(r,g,b))
-
-
- pool = socketpool.SocketPool(wifi.radio)
- mqtt_client = MQTT.MQTT(
- broker="192.168.0.111",
- port=1883,
- username='',
- password='',
- socket_pool=pool,
- ssl_context=ssl.create_default_context()
- )
-
- mqtt_client.on_connect = connect
- mqtt_client.on_disconnect = disconnect
- mqtt_client.on_subscribe = subscribe
- mqtt_client.on_unsubscribe = unsubscribe
- mqtt_client.on_publish = publish
- mqtt_client.on_message = message
- mqtt_client.connect()
-
- while True:
- blink.animate()
- mqtt_client.loop()
-
这段代码实现了一个通过MQTT控制RGB彩灯的示例,主要功能包括:
a. 初始化WiFi、MQTT客户端对象。
使用socketpool、SSL等模块建立网络连接,创建MQTT客户端并绑定相关回调函数。
b.初始化彩灯对象。
创建一个NeoPixel对象代表彩灯,并创建一个Blink对象用来控制闪烁。
c.实现MQTT的消息处理。
绑定on_message回调,在收到mqtt信息时将JSON解析为RGB值,更新Blink对象的颜色。
d.主循环。
轮询调用blink.animate()更新彩灯状态,并调用mqtt_client.loop()监听消息。
该示例演示了如何利用MQTT协议实现RGB彩灯的远程控制。主要步骤是建立网络连接,解析MQTT消息设置彩灯状态,并循环更新。利用MQTT可以实现设备的远程监控和控制。
四 任务的心得体会
CircuitPython生态已经很成熟,Adafruit提供了大量针对自己开发板的CircuitPython库。我在使用时也发现CircuitPython的图形界面很方便,适合开发UI交互较丰富的项目。该开发板集成度高,很多外设接口直接可用。开发时需要留意一些细节,如显示屏的刷新模式、内存使用等。需要用一些技巧来优化代码,提高性能。总体来说,这个开发板硬件强大,软件易用性也高。Adafruit提供的开发资源丰富,可以让开发效率大大提升。后续我也会继续使用这个板子做些有趣的项目。也感谢得捷电子和eeworld能给我这么机会来参与这个活动。
源码下载链接
|