【得捷电子Follow me第2期】+任务汇总
[复制链接]
内容一:演示视频
内容二:项目总结报告
任务1:控制屏幕显示中文
完成屏幕的控制,并且能显示中文
搭配器件:Adafruit ESP32-S3 TFT
字体文件是群友发一级字库,具体想要自己做字库,可以参考官方的教程自己制作字库
Adafruit官方学习指导:自定义字体
创建文本标签对象text_area 以后,可以通过修改对象属性来改变文字显示以及颜色等属性
例如:text_area.text="张三" text_area.color=color2
import board # 导入板级定义库
import displayio # 导入显示IO库
from adafruit_bitmap_font import bitmap_font # 导入位图字体库
from adafruit_display_text import label # 导入文本标签库
# 获取设备的显示屏对象
display = board.DISPLAY
# 加载字体文件(一级汉字中文字库)
font = bitmap_font.load_font("fonts/opposans_m_12.bdf")
# 设置文本颜色(白色)16进制颜色
color = 0x49D57C
# 创建一个显示组(用于容纳文本标签)
text_group = displayio.Group()
# 创建一个文本标签对象,设置字体、文本内容和颜色
text_area = label.Label(font, text="Follow me活动”是DigiKey\n联合EEWORLD发起的\n为期一年的“跟技术大咖学技术,\n完成任务返现”活动。\n2023年共有4期", color=color)
# 设置文本标签的位置(x和y坐标)
text_area.x = 10
text_area.y = 10
# 将文本标签添加到显示组中
text_group.append(text_area)
# 在显示设备上显示文本组
display.show(text_group)
# 进入无限循环以保持程序运行
while True:
pass # 空循环,无限等待
任务2:网络功能使用
完成网络功能的使用,能够创建热点和连接到WiFi
搭配器件:Adafruit ESP32-S3 TFT Feather
连接WIFI、创建热点是两个不同的函数,为了实现切换网络功能,我定义了一个初始化的函数来实现不同模式的网络模式,当mode_user=None时,自动使用mode_default=1的模式启用网络模式。
import time
import wifi
import microcontroller
mode_default = 0
mode_user=1
def wifi_init(mode, ssid, password):
if mode == 0:
"""
连接WIFI
"""
print("Connecting to %s" % secrets["ssid"])
wifi.radio.connect(ssid=ssid, password=password)
if wifi.radio.ipv4_address is not None:
print("Connected!")
print("IP Address:", wifi.radio.ipv4_address)
else:
print("Failed to connect.....")
time.sleep(30)
microcontroller.reset()
elif mode == 1:
"""
创建热点
"""
# 初始化 Wi-Fi 模块
wifi.radio.start_ap(ssid=ssid, password=password)
"""
ap_info:不知为何这个变量是None
"""
# 检查是否成功创建热点
if wifi.radio.ipv4_address_ap is not None:
print("Access Point created!")
print("SSID:",ssid)
print("IP Address:", wifi.radio.ipv4_address_ap)
else:
print("SSID:",ssid)
print("IP Address:", wifi.radio.ipv4_address_ap)
print("----------------------")
print("Failed to create Access Point.")
print("Access Point created!")
else:
print("please input the correct mode and retry")
try:
from secrets import secrets
except ImportError:
print("WiFi and Adafruit IO credentials are kept in secrets.py - please addthem there!")
raise
if mode_user is not None:
wifi_init(mode_user,"Feather ESP32S-S3 TFT","12345678")
else:
wifi_init(mode_default,secrets["ssid"],secrets["password"])
while True:
pass
任务3:控制WS2812B
使用按键控制板载Neopixel LED的显示和颜色切换
搭配器件:Adafruit ESP32-S3 TFT Feather
首先导入到官方的neopixel 库,然后初始化板载的Neopixel LED 因为python移除了switch,所以使用了对象来做做按键选择
大致思路:当ESP32在使用过程中,BOOT0按键可以作为普通按键使用,由此可以检测电平高低来判断按键是否按下
import board
import neopixel
import digitalio
key_times=0
# Configure the setup
PIXEL_PIN = board.NEOPIXEL # pin that the NeoPixel is connected to
ORDER = neopixel.RGB # pixel color channel order
COLOR = (100, 50, 150) # color to blink
CLEAR = (20, 70, 50) # clear (or second color)
COLOR_LIST={
0:(100, 50, 150),
1:(20, 70, 50),
2:(255, 0, 255),
3:(0, 0, 000),
4:(250, 250, 250),
5:(20, 70, 250),
}
DELAY = 0.25 # blink rate in seconds
# 这一部分代码是设置 按键IO口的输出模式
BUTTON_PIN=digitalio.DigitalInOut(board.BOOT0)
BUTTON_PIN.direction=digitalio.Direction.INPUT
BUTTON_PIN.pull=digitalio.Pull.UP
# Create the NeoPixel object
pixel = neopixel.NeoPixel(PIXEL_PIN, 1, pixel_order=ORDER)
# Loop forever and blink the color
while True:
print(not BUTTON_PIN.value)
if not BUTTON_PIN.value:
# 本来想做防抖的结果发现circuitpython 的延时会阻塞
while not BUTTON_PIN.value:
print("正在按下")
key_times+=1
print('按键加一',key_times)
if key_times>5:
key_times=0
pixel[0] = COLOR_LIST.get(key_times)
任务4:从下方5个分任务中选择1个感兴趣的完成即可
■ 分任务1:日历&时钟——完成一个可通过互联网更新的万年历时钟,并显示当地的天气信息
建议搭配器件:Adafruit ESP32-S3 TFT Feather
■ 分任务2:WS2812B效果控制——完成一个Neopixel(12灯珠或以上)控制器,通过按键和屏幕切换展示效果
建议搭配器件:Adafruit ESP32-S3 TFT Feather、可寻址RGB LED
■ 分任务3:数据检测与记录——按一定时间间隔连续记录温度/亮度信息,保存到SD卡,并可以通过按键调用查看之前的信息,并在屏幕上绘图
建议搭配器件:Adafruit ESP32-S3 TFT Feather、光传感器、温度传感器、微型 SD卡模块
■ 分任务4:音乐播放功能——实现音乐播放器功能,可以在屏幕上显示列表信息和音乐信息,使用按键进行切换,使用扬声器进行播放
建议搭配器件:Adafruit ESP32-S3 TFT Feather、音频放大器、扬声器
■ 分任务5:AI功能应用——结合运动传感器,完成手势识别功能,至少要识别三种手势(如水平左右、前后、垂直上下、水平画圈、垂直画圈,或者更复杂手势
建议搭配器件:Adafruit ESP32-S3 TFT Feather、运动传感器
大致思路: 1.首先需要获取天气和时间信息 2.其次将信息渲染到屏幕上 3.通过定时来不断刷新时间与天气
这一部分是项目的主要代码:因为要实现定时更新天气、时间等信息,而简单的time.sleep()会导致程序阻塞,此时就需要使用官方的syncio 库来实现类似于多线程的协程来调度任务,从而实现多任务并行。
在回去天气信息的代码里面,我为了实现自动获取本地信息,使用了百度的一个IP查询接口,当向它发请求的时候会返回一个具体的城市名字,获取名字以后再通过请求天气接口就可以回去实时天气信息了。具体可详细查看代码。
使用syncio 进行协程的示例代码在官方的教学里面可以找得到
EEWORLDLINKTK1
import gc
import time
import ssl
import json
import wifi
import socketpool
import adafruit_requests
import adafruit_imageload
import board
import digitalio
import asyncio
import displayio
import adafruit_imageload
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text import label
try:
from secrets import secrets
except ImportError:
print("Secrets File Import Error")
raise
# 连接WIFI函数
def connect_to_wifi():
# Connect to Wi-Fi
print("\n===============================")
print("Connecting to WiFi...")
while not wifi.radio.ipv4_address:
try:
wifi.radio.connect(secrets["ssid"], secrets["password"])
except ConnectionError as e:
print("Connection Error:", e)
print("Retrying in 10 seconds")
time.sleep(10)
gc.collect()
print("Connected!\n")
# 获取城市名字
def get_city():
print(
"\nAttempting to GET DISCORD SHIELD JSON!"
) # --------------------------------
print("===============================")
try:
ada_response = requests.get(ADA_DISCORD_JSON).json()
except ConnectionError as e:
print("Connection Error:", e)
print("Retrying in 10 seconds")
city = ""
ada_users = ada_response["data"]
print("JSON Value: ", ada_users)
city = ada_users["city"].replace("市", "")
print(city)
print("Monotonic: ", time.monotonic())
print("\nFinished!")
print("===============================")
return city
# 获取天气信息
def get_weather(city):
# 发送请求
response = requests.get(weather_url + "/weather", json={"city_name": city})
# 解析响应JSON数据
city_info = response.json()
if city_info:
print(f"查询结果:{city_info}")
return city_info
else:
print(f"未找到城市:{city}")
return None
# 获取实时天气信息
def get_weather_now(city_name):
# 发送请求
response = requests.get(weather_url + "/weather_now", json={"city_name": city_name})
# 解析响应JSON数据
weather_info = response.json()
if weather_info:
print(f"查询结果:{weather_info}")
return weather_info
else:
print(f"未找到城市:{weather_info}")
return None
# 获取实时时间
def get_time():
# 获取时间
response = requests.get(weather_url + "/time")
# 解析响应JSON数据
time_info = response.json().get("time")
if time_info:
print(f"当前时间:{time_info}")
return time_info
else:
print(f"更新时间失败")
return None
# 格式化时间为指定的时间
def format_time(timestamp):
time_tuple = time.localtime(timestamp)
formatted_time = "{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}".format(
time_tuple.tm_year, time_tuple.tm_mon, time_tuple.tm_mday,
time_tuple.tm_hour, time_tuple.tm_min, time_tuple.tm_sec
)
return formatted_time
# LED灯设置
led1 = digitalio.DigitalInOut(board.LED)
led1.direction = digitalio.Direction.OUTPUT
# LED灯的任务函数
async def blink_led(led, delay):
while True:
led.value = not led.value
await asyncio.sleep(delay)
# 测试计数的函数
async def draw_text(number=None):
while True:
number_label.text = f"DesignBygalaxy{number}"
# Increment the number
number = (number + 1) % 101
await asyncio.sleep(1)
# 更新天气的函数
async def update_weather():
while True:
city_name = get_city()
weather = get_weather(city_name)
weather_now = get_weather_now(city_name)
print(weather_now)
temperature = weather_now["data"]["now"]["temperature"]
humidity = weather_now["data"]["now"]["humidity"]
print(weather)
weather_str = f"天气:{weather['weather']} 气温:{temperature} \n湿度:{humidity}"
print(weather_str)
weather_label.text = weather_str
await asyncio.sleep(60) # Update every 1 minute
# 更新时间信息
async def update_time():
time_str = get_time()
current_time = float(time_str) + 5 # 校准时间
# 时间定时获取6小时
time_interval = 6 * 60 * 60
# 转化为时间戳
while True:
# 如果current_time和time_str之间相差6小时,就更新时间
if time.time() - current_time > time_interval:
current_time = time.time()
current_time += 1
local_time = format_time(current_time)
time_label.text = f"时间:{local_time}"
await asyncio.sleep(1)
# 定义主任务
async def main():
connect_to_wifi()
task1 = asyncio.create_task(blink_led(led1, 0.5)) # Blink LED every 0.5 seconds
task2 = asyncio.create_task(draw_text(number=0))
task3 = asyncio.create_task(update_weather())
task4 = asyncio.create_task(update_time())
while True:
await asyncio.gather(task1, task2, task3, task4)
await asyncio.sleep(1) # Update every 1 minute
if __name__ == "__main__":
# Initialize WiFi Pool
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())
# 这个网址是获取城市信息的,是百度的官方接口,放心食用!
ADA_DISCORD_JSON = "https://qifu-api.baidubce.com/ip/local/geo/v1/district"
# Weather API URL
weather_url = "http://内网地址:9004" # 服务器请求地址 ->将内网地址替换为你的局域网IP就可以
# Initialize display
display = board.DISPLAY
# Load font
font = bitmap_font.load_font("fonts/opposans_m_12.bdf")
bitmap, palette = adafruit_imageload.load("images/122.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette)
tile_grid = displayio.TileGrid(bitmap, pixel_shader=palette)
color = 0xFFFFFF
group = displayio.Group()
group.append(tile_grid)
# 创建一个显示的对象
number_label = label.Label(font, text="DesignBygalaxy", color=color)
number_label.x = 10
number_label.y = 60
group.append(number_label)
# Create a label for displaying the number
weather_label = label.Label(font, text="天气:", color=color)
weather_label.line_spacing = 1
weather_label.x = 10
weather_label.y = 80
group.append(weather_label)
# Create a label for displaying the number
time_label = label.Label(font, text="时间:", color=color)
time_label.x = 10
time_label.y = 120
group.append(time_label)
display.show(group)
asyncio.run(main())
这个就是主要的后端代码,后端使用的是flask来做的后端的,本来我想着也是使用现成API去获取天气信息,我本人也用过许多的API但是局限性都是很大的,得不到自己想要的信息,也不知随心所欲的获取想要的信息。于是,花了两个小时的时间,通过查资料查、查网站来做了一个简单的后端接口,写得很匆忙,但是基本还是能用滴。
首选是get_weather这个函数,通过requests来获取天气信息
import requests
import time
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"
}
weather_url = "https://weather.cma.cn/api/map/weather/1?" + str(int(time.time() * 1000))
def get_weather(city):
# 发起HTTP请求
response = requests.get(weather_url, headers=headers)
# 解析响应JSON数据
json_data = response.json()
# 获取城市数组
city_array = json_data["data"]["city"]
# 创建一个字典以加速查询
city_dict = {city_info[1]: city_info for city_info in city_array}
# 查询城市信息
city_info = city_dict.get(city)
weather_info = {
"city_id": city_info[0],
"city_name": city_info[1],
"country": city_info[2],
"level": city_info[3],
"latitude": city_info[4],
"longitude": city_info[5],
"temperature": city_info[6],
"weather": city_info[7],
"weather_code": city_info[8],
"wind_direction": city_info[9],
"wind_power": city_info[10],
"wind_speed": city_info[11],
"weather_tomorrow": city_info[12],
"weather_code_tomorrow": city_info[13],
"wind_direction_tomorrow": city_info[14],
"wind_power_tomorrow": city_info[15],
"city_code": city_info[16],
"city_code2": city_info[17]
}
if city_info:
print(f"查询结果:{city_info}")
return weather_info
else:
print(f"未找到城市:{city}")
return None
def get_get_weather_now(city_id):
# https: // weather.cma.cn / api / now / 54319
# 发起HTTP请求
response = requests.get("https://weather.cma.cn/api/now/" + str(city_id), headers=headers)
# 解析响应JSON数据
json_data = response.json()
return json_data
后端的主要的代码,首先是使用Flask作为的主要后端框架。确保ESP32和电脑在同一WIFI下就可以更改天气强求的接口,来实时获取天气信息。
import json
import time
from flask import Flask, request
from weather.get_weather import get_weather, get_get_weather_now
app = Flask(__name__)
@app.route('/')
def hello_world(): # put application's code here
return 'App is running!'
@app.route('/weather', methods=['GET'])
def weather():
# 获取城市名称
city = request.json.get("city_name")
city_info = get_weather(city)
print(city_info)
# 发送请求
return city_info
@app.route('/weather_now', methods=['GET'])
def weather_now():
city_name = request.json.get("city_name")
# city_name = "平泉"
# 打开本地文件
with open('cities.json', 'r', encoding='utf-8') as json_file:
data = json.load(json_file)
# 遍历城市列表,提取所有城市的名称和 ID
city_id = data.get(city_name)
print(city_id)
# 发送请求
weather_info = get_get_weather_now(city_id)
return weather_info
@app.route('/time', methods=['GET'])
def get_time():
# 返回时间戳
now = str(time.time())
# 转化为json格式
return {"time": now}
@app.route('/ip')
def get_client_ip():
client_ip = request.remote_addr
return f"Your IP address is: {client_ip}"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=9004, debug=True) # 使用合适的端口
任务5:通过网络控制WS2812B
结合123,在手机上通过网络控制板载Neopixel LED的显示和颜色切换,屏幕同步显示状态
搭配器件:Adafruit ESP32-S3 TFT Feather
这一部分我本来也想要自己写一个后端的代码的,但是刚收到板子那几天突然在文档里面看到了官方adafruit 自己提供的功能,于是就跟着就教程来做了一下。使用情况非常的好,官方本身也提供了其他在线功能,像实时上传数据转化为可视化数据,还有远程控制等多个功能。非常好用!
import time
import ssl
import microcontroller
import socketpool
import wifi
import board
import neopixel
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from adafruit_io.adafruit_io import IO_MQTT
import terminalio
from adafruit_display_text import label
display = board.DISPLAY
# Set text, font, and color
text = "HELLO WORLD"
font = terminalio.FONT
color = 0x0000FF
# Create the text label
text_area = label.Label(font, text=text, color=color)
# Set the location
text_area.x = 30
text_area.y = 60
text_area.scale=3
# Show it
display.show(text_area)
try:
from secrets import secrets# pylint: disable=no-name-in-module
except ImportError:
print("WiFi and Adafruit IO credentials are kept in secrets.py - please addthem there!")
raise
# Add your Adafruit IO Username and Key to secrets.py
# (visit io.adafruit.com if you need to create an account,
# or if you need to obtain your Adafruit IO key.)
aio_username = secrets["aio_username"]
aio_key = secrets["aio_key"]
# WiFi
try:
print("Connecting to %s" % secrets["ssid"])
wifi.radio.connect(secrets["ssid"], secrets["password"])
print("Connected to %s!" % secrets["ssid"])
# Wi-Fi connectivity fails with error messages, not specific errors, so this exceptis broad.
except Exception as e: # pylint: disable=broad-except
print("Failed to connect to WiFi. Error:", e, "\nBoard will hard reset in 30seconds.")
time.sleep(30)
microcontroller.reset()
# Initialise NeoPixel
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.3)
# Define callback functions which will be called when certain events happen.
def connected(client):
print("Connected to Adafruit IO! Listening for NeoPixel changes...")
# Subscribe to Adafruit IO feed called "neopixel"
client.subscribe("neopixel")
def message(client, feed_id, payload): # pylint: disable=unused-argument
print("Feed {0} received new value: {1}".format(feed_id, payload))
if feed_id == "neopixel":
text_area.text='#'+payload[1:]
text_area.color=int(payload[1:], 16)
#text_area.color='0x'+payload[1:]
pixel.fill(int(payload[1:], 16))
# Create a socket pool
pool = socketpool.SocketPool(wifi.radio)
# Initialize a new MQTT Client object
mqtt_client = MQTT.MQTT(
broker="io.adafruit.com",
username=secrets["aio_username"],
password=secrets["aio_key"],
socket_pool=pool,
ssl_context=ssl.create_default_context(),
)
# Initialize Adafruit IO MQTT "helper"
io = IO_MQTT(mqtt_client)
# Set up the callback methods above
io.on_connect = connected
io.on_message = message
timestamp = 0
while True:
try:
# If Adafruit IO is not connected...
if not io.is_connected:
# Connect the client to the MQTT broker.
print("Connecting to Adafruit IO...")
io.connect()
# Explicitly pump the message loop.
io.loop()
# Adafruit IO fails with internal error types and WiFi fails with specificmessages.
# This except is broad to handle any possible failure.
except Exception as e: # pylint: disable=broad-except
print("Failed to get or send data, or connect. Error:", e,"\nBoard will hard reset in 30 seconds.")
time.sleep(30)
microcontroller.reset()
内容三:可编译下载的代码
得捷大学堂源码地址
活动心得
通过本次活动也算体验了一次新的开发方式吧,circuitpython相对于micropyrhon来说更简单了,对于新手入门来说门槛大大降低,即使是没怎么接触过硬件开发的人也能开发出自己的小创意,很适应大众的需求。此次的板子esp32s3十分的小巧像是一件艺术品,不愧是大厂出品。其次就是活动本身的组织方面,从我购买板子到发货再到收货,最后完成活动任务的过程中,一直十分顺畅。我在网页可以和客服沟通解决我的发货购买等问题,在微信联系工作人员可以解决活动中需要的问题。还有交流群里大家热心帮助下解决程序中需要到的问题。对于我个人来说我是十分的满意这个活动,第一次参加这样的活动也还是偶然机会下了解到。
建议:我没有遇到发货缓慢的问题,我比他们有的买的还很晚,我觉得他们可能是没有看邮件之类的信息,在活动中可以提醒大家多看看邮件,避免错过重要的信息,其他没有什么问题,从各方面来说都很好。也希望后面的两次也能越来越好。
|