感谢 EEWORLD 和 得捷电子,提供了这次让我能获得提升的机会
作为一名高中牲,对单片机有些许涉猎,这次有幸接触到了 ESP-S3 板子,不得不说 CircuitPython 确实比直接用 C 编写节省了不少时间。这次探索的过程也让我了解了不少 ESP32-S3 的知识,还有吃灰许久的电烙铁。
内容一:3-5分钟短视频
视频地址:【得捷电子Follow me第2期】任务提交
内容二:任务/项目总结报告
任务1:控制屏幕显示中文
首先导入核心库,字体库以及文本显示库
import os
import board
import displayio
from adafruit_display_text import label
from adafruit_bitmap_font import bitmap_font
font = bitmap_font.load_font(os.getenv('FONT_FILE_PATH')) # 加载字体
使用核心库初始化显示屏
# 初始化显示屏
board.DISPLAY.brightness = 0.45
board.DISPLAY.rotation = 0
# 创建显示文本的组
text_group = displayio.Group()
为了更简洁的设计界面UI,我编写了一个将 adafruit_display_text.label 添加到 text_group 的函数。关于 adafruit_display_text 的具体使用,可以参考 官方文档
def append_label_to_group(text_group, font, x, y, text = '', color = 0x00FFFF, line_spacing = 0.8, scale = 1):
text_area = label.Label(font, text=text, color=color)
text_area.x = x
text_area.y = y
text_area.line_spacing = line_spacing
text_area.scale = scale
text_group.append(text_area)
向 text_group 添加要显示的内容
append_label_to_group(text_group, font, 10, 20, "任务1: 实现屏幕控制与中文显示", color)
append_label_to_group(text_group, font, 10, 60, "1234567890.ABCDEFGHIJKLMN\nOPQRSTUVWXYZ", color)
append_label_to_group(text_group, font, 10, 100, "Follow Me 第二期 @YSChain", color)
刷新屏幕,无限等待
board.DISPLAY.show(text_group)
while True:
pass
上电,看效果:
任务2:网络功能使用
首先,你需要打开 CircuitPython 根目录下的 settings.toml 文件,向内加入这些设置。
SSID 为 WiFi 和 热点 的名字
AP_* 为热点的配置
WIFI_SSID = "ChinaNet-2.4G-****"
WIFI_PASSWORD = "********"
AP_SSID = "ESP32-S3-YS01"
AP_PASSWORD = "YSBot-TEST"
导入 WIFI 和请求发送相关库,初始化
import wifi, ssl
import socketpool
import adafruit_requests
import os
TEST_URL = 'http://wifitest.adafruit.com/testwifi/index.html'
print("ESP32-S3 网络连接测试")
print(f"MAC 地址: {[hex(i) for i in wifi.radio.mac_address]}")
连接 WIFI
print(f"正连接到 {os.getenv('WIFI_SSID')}")
wifi.radio.connect(os.getenv("WIFI_SSID"), os.getenv("WIFI_PASSWORD"))
print(f"已连接到 {os.getenv('WIFI_SSID')}")
print(f"内网 IP 地址: {wifi.radio.ipv4_address}")
发送请求,输出结果
pool = socketpool.SocketPool(wifi.radio)
req = adafruit_requests.Session(pool, ssl.create_default_context())
res = req.get(TEST_URL)
print(f"测试请求返回内容: \n \
{'-' * 40} \n \
{res.text} \n \
{'-' * 40} \n \
\
'完毕!")
效果图(由于板子没有自带中文字库,REPL 输出会忽略中文,故采用 VSCode 展示)
热点创建:
我试图在板子上搭建一个小型的服务器,无奈实在找不到资料,或许它只能使用 MQTT 来实现互联?希望能有大佬解答这个疑惑。
wifi.radio.start_ap(os.getenv("AP_SSID"), os.getenv("AP_PASSWORD"))
wifi.radio.start_dhcp_ap()
(显然它不可能可以上网
任务3:控制WS2812B
由于按键数量的限制,此处使用一个按键来轮换各个颜色
导入相关库,并初始化 D5 Pin,将其设置为上拉模式,将按键的两个引脚分别接到 3.3V 和 D5
import time
import board
import digitalio
import neopixel
button_pin = board.D5
button = digitalio.DigitalInOut(button_pin)
button.direction = digitalio.Direction.INPUT
button.pull = digitalio.Pull.UP
设置内置颜色,并启动板载 WS2812B
# 颜色数组
COLORS = [
(255, 0, 0), # 红
(180, 120, 0), # 橙
(180, 180, 0), # 黄
(0, 255, 0), # 绿
(0, 0, 255), # 蓝
(75, 0, 130), # 靛
(215, 110, 215) # 紫
]
# 默认显示蓝色
Color_Num = 4
Color = COLORS[4]
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1)
pixel.brightness = 0.2
随后是一个简单的按键判断和软件消抖
debounce_delay = 0.8 # 消抖延迟,单位s
while True:
if not button.value: # 按键被按下
time.sleep(debounce_delay) # 延迟一段时间进行消抖
if not button.value: # 确认按键仍然被按下
Color = COLORS[Color_Num] # 设置led颜色
pixel.fill(Color)
Color_Num = (Color_Num + 1) % 7
任务4:分任务1:日历&时钟
1. 连接WiFi同上,此处省略
2. 发送http请求,此处需要前往 这里 获取自己城市的天气代码,替换链接中的数据
import ssl
import socketpool
import adafruit_requests
# 请求获取JSON
## api文档 https://www.sojson.com/blog/305.html
TARGET_URL = "http://t.weather.itboy.net/api/weather/city/101210404"
# 发送请求并解析 JSON 数据
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())
response = requests.get(TARGET_URL)
weather = response.json()
3. 初始化屏幕,定义“任务一”中出现过的那个函数
import board
import displayio
from adafruit_display_text import label
from adafruit_bitmap_font import bitmap_font
from adafruit_display_shapes.rect import Rect
display = board.DISPLAY
board.DISPLAY.brightness = float(os.getenv('DISPLAY_BRIGHTNESS'))
board.DISPLAY.rotation = 0
font = bitmap_font.load_font(os.getenv('FONT_FILE_PATH')) # 加载字体
text_group = displayio.Group()
def append_label_to_group(text_group, font, x, y, text = '', color = 0xC8D2F8, line_spacing = 0.9, scale = 1):
text_area = label.Label(font, text=text, color=color)
text_area.x = x
text_area.y = y
text_area.line_spacing = line_spacing
text_area.scale = scale
text_group.append(text_area)
divider_color = 0x001FDD # 分界线的颜色
4. 检查天气数据是否获取成功
if weather['message'][0:7] != 'success':
print('[FATAL] 日历数据获取失败!')
text_area = label.Label(font, text='[FATAL] 日历数据获取失败!', color=0xff7e00)
text_area.x = 30
text_area.y = 50
text_area.line_spacing = 0.9
text_area.scale = 1
text_group.append(text_area)
display.show(text_group)
while True:
pass
5. 解析天气数据并显示(明日的数据同理)
cityInfo = weather['cityInfo']
city_weather = weather['data']
forecast = city_weather['forecast']
### 今日的数据
str1 = f' {forecast[0]["ymd"][5:]} RH:{"0" if len(city_weather["shidu"])==2 else ""}{city_weather["shidu"]} [{forecast[0]["sunrise"]}-{forecast[0]["sunset"]}] {forecast[0]["type"]}'
aqi = f" AQI: {'0' if forecast[0]['aqi'] < 100 else ''}{forecast[0]['aqi']}"
if forecast[0]['aqi'] <= 50:
aqi_color = 0x00e400
elif forecast[0]['aqi'] <= 100:
aqi_color = 0xf0f000
elif forecast[0]['aqi'] <= 150:
aqi_color = 0xff7e00
elif forecast[0]['aqi'] <= 200:
aqi_color = 0xf00000
elif forecast[0]['aqi'] <= 300:
aqi_color = 0x99004c
else:
aqi_color = 0x7e0023
wind_str = f"{' ' if len(forecast[0]['fx']) == 2 else ''}{forecast[0]['fx']} {'0' if len(forecast[0]['fl']) == 2 else ''}{forecast[0]['fl']}"
tem_str = f"{forecast[0]['low'][3:]}~{forecast[0]['high'][3:]}"
append_label_to_group(text_group, font, 0, 10, str1)
append_label_to_group(text_group, font, 0, 30, aqi, aqi_color)
append_label_to_group(text_group, font, 70, 30, f"{wind_str} {tem_str}")
text_group.append(Rect(0, 43, 240, 1, fill=divider_color))
### END 今日的数据
6. 获取万年历数据并解析显示。这里使用到了 RoolToolsApi,请前往 这里 注册获取自己的 Key。随后将这段内容加入你的 settings.toml 文件中:
mxnzp_APP_ID = "n****b"
mxnzp_APP_SECRET = "3***d"
接下来和获取天气数据相似:发送请求,解析JSON,显示。不再赘述
TARGET_URL = f"https://www.mxnzp.com/api/holiday/single/{weather['date']}?ignoreHoliday=false&app_id={os.getenv('mxnzp_APP_ID')}&app_secret={os.getenv('mxnzp_APP_SECRET')}"
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())
print(f"Fetching and parsing json from 'https://www.mxnzp.com/api/holiday/single/'")
response = requests.get(TARGET_URL)
calendar = response.json()
if calendar['code'] != 1:
append_label_to_group(text_group, font, 30, 100, '日历数据获取失败', 0xff7e00)
else:
append_label_to_group(text_group, font, 10, 100, f"{calendar['data']['yearTips']}年 [{calendar['data']['chineseZodiac']}] {calendar['data']['lunarCalendar']} - {calendar['data']['typeDes']}")
append_label_to_group(text_group, font, 0, 120, f"宜:{calendar['data']['suit'][0:8]} | 忌:{calendar['data']['avoid'][0:8]}")
效果展示:(AQI 指数的颜色会随着空气质量的情况而变化,天气预报中位于方括号内的内容是日出日落时间
心得体会:
曾经试图用 Arduino UNO R3 制作一个桌面时钟,加入对电灯的自动控制等功能,最终因为 adafruit 的 GFX 库占用内存过多而放弃了那块TFT显示屏。这次一定程度上也是奔着这块不错的显示屏来的。利用 Python 开发确实有着非常大的优势,烧录代码更是方便迅速。
未来计划利用它制作一个简易的热成像仪,这块屏幕刚好可以用来实时监视,同时用WiFi将图像传输到树莓派,省去了再拿个设备记录的痛。虽然分辨率很低,但是作为一个测量温度的小工具,想必还是很不错的。
最后,再次感谢得捷电子,感谢 EEWORLD!
内容三:可编译下载的代码
|