443|1

34

帖子

4

TA的资源

一粒金砂(中级)

楼主
 

【得捷电子Follow me第2期】+任务汇总 [复制链接]

 

内容一:演示视频


 

内容二:项目总结报告


  • 任务四选择的是:分任务1:日历&时钟——完成一个可通过互联网更新的万年历时钟,并显示当地的天气信息


任务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十分的小巧像是一件艺术品,不愧是大厂出品。其次就是活动本身的组织方面,从我购买板子到发货再到收货,最后完成活动任务的过程中,一直十分顺畅。我在网页可以和客服沟通解决我的发货购买等问题,在微信联系工作人员可以解决活动中需要的问题。还有交流群里大家热心帮助下解决程序中需要到的问题。对于我个人来说我是十分的满意这个活动,第一次参加这样的活动也还是偶然机会下了解到。

 

建议:我没有遇到发货缓慢的问题,我比他们有的买的还很晚,我觉得他们可能是没有看邮件之类的信息,在活动中可以提醒大家多看看邮件,避免错过重要的信息,其他没有什么问题,从各方面来说都很好。也希望后面的两次也能越来越好。

 

项目代码.zip

373.07 KB, 下载次数: 4

最新回复

当ESP32在使用过程中,BOOT0按键可以作为普通按键使用,由此可以检测电平高低来判断按键是否按下,这个思路是正确的   详情 回复 发表于 2023-10-15 08:12
点赞 关注
 
 

回复
举报

6587

帖子

0

TA的资源

五彩晶圆(高级)

沙发
 

当ESP32在使用过程中,BOOT0按键可以作为普通按键使用,由此可以检测电平高低来判断按键是否按下,这个思路是正确的

 
 
 

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

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

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

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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

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

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

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