ID.LODA

  • 2024-05-29
  • 回复了主题帖: 【FireBeetle 2 ESP32 C6 开发板】+ 显示天气和万年历

    wangerxian 发表于 2024-5-29 11:35 json的好用,每天能获取多少次? 今天上去看了眼是每天 1000 次

  • 回复了主题帖: 【FireBeetle 2 ESP32 C6 开发板】+ 显示天气和万年历

    wangerxian 发表于 2024-5-28 19:00 和风天气这个API好像很多人获取天气信息都在用。 api 内容相对完备一下,而且提供字体库、天气图标等。就是现在改了返回格式,强制 gzip,以前是可选 json 的,蛋疼

  • 2024-05-28
  • 发表了主题帖: 【FireBeetle 2 ESP32 C6 开发板】+ 显示天气和万年历

    本帖最后由 ID.LODA 于 2024-5-28 16:55 编辑 ## Previously 1. [【FireBeetle 2 ESP32 C6】+ 开箱及更新 Circuitpy 固件](https://bbs.eeworld.com.cn/thread-1281496-1-1.html) 1. [【FireBeetle 2 ESP32 C6 开发板】+ 使用 Web Workflow 开发](https://bbs.eeworld.com.cn/thread-1281597-1-1.html) 1. [【FireBeetle 2 ESP32 C6 开发板】+ 测试使用板载 core modules](https://bbs.eeworld.com.cn/thread-1282655-1-1.html) 1. [【FireBeetle 2 ESP32 C6 开发板】+ 驱动 DFR0664](https://bbs.eeworld.com.cn/thread-1282718-1-1.html) # 显示万年历和天气 ## NTP 网络授时 ### 导入 cpy 库 需要从 adafruit-circuitpython-bundle 获取驱动库 adafruit_ntp.mpy, 导入至板卡的 lib 目录 ### 示例代码 服务器选择了 `ntp.aliyun.com` ```python # SPDX-FileCopyrightText: 2024 id.loda # SPDX-License-Identifier: MIT import board import digitalio import rtc import time import wifi import socketpool import adafruit_ntp UTC_OFFSET = 8 led = digitalio.DigitalInOut(board.LED) led.direction = digitalio.Direction.OUTPUT # code handler while not wifi.radio.ipv4_address:     try:         print("Connecting to WiFi")         #  connect to your SSID         wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))     except ConnectionError as e:         print("connect error:{}, retry in 2s".format(e))     time.sleep(2) pool = socketpool.SocketPool(wifi.radio) def ntp_sync_time():     print("Local time before synchronization:%s" % str(time.localtime()))     try:         # ntp = adafruit_ntp.NTP(pool, tz_offset=UTC_OFFSET)         ntp = adafruit_ntp.NTP(pool, server='ntp.aliyun.com', tz_offset=UTC_OFFSET)         # ntp = adafruit_ntp.NTP(pool, server='ntp.ntsc.ac.cn', tz_offset=UTC_OFFSET)         # NOTE: This changes the system time so make sure you aren't assuming that time         # doesn't jump.         rtc.RTC().datetime = ntp.datetime         print("Local time after synchronization: %s" % str(time.localtime()))     except OSError as e:         print("ntp error:{}".format(e)) ntp_sync_time() while True:     time.sleep(0.5)     led.value = not led.value ``` ### 效果展示 ## 获取天气 天气服务器使用的是和风天气,调用的 [实时天气接口](https://dev.qweather.com/docs/api/weather/weather-now/),需要注意的是返回结果是 gzip 格式,可以使用自带的 ``zlib`` 模块进行解密 返回数据格式如下: ### 示例代码 在和风天气官网注册申请 Key,替换代码中的 **QWEATHER_KEY** ```python # SPDX-FileCopyrightText: 2024 id.loda # SPDX-License-Identifier: MIT import board import digitalio import time import wifi import socketpool import ssl import adafruit_requests import zlib import json QWEATHER_KEY = "Your-Key" CITYID = '101020100' led = digitalio.DigitalInOut(board.LED) led.direction = digitalio.Direction.OUTPUT # code handler while not wifi.radio.ipv4_address:     try:         print("Connecting to WiFi")         #  connect to your SSID         wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))     except ConnectionError as e:         print("connect error:{}, retry in 2s".format(e))     time.sleep(2) pool = socketpool.SocketPool(wifi.radio) requests = adafruit_requests.Session(pool, ssl.create_default_context()) def qweather_decompress(data):     FTEXT = 1     FHCRC = 2     FEXTRA = 4     FNAME = 8     FCOMMENT = 16     assert data[0] == 0x1f and data[1] == 0x8b     assert data[2] == 8     flg = data[3]     assert flg & 0xe0 == 0     i = 10     if flg & FEXTRA:         i += data[11] = 2:                 current_line = ""                 break         else:             current_line += char             current_length += char_length     # 添加最后一行     if current_line:         result.append(current_line)     return "\n".join(result) def display_update_laohuangli(info):     text_yi = split_text(info['yi'], 50)     text_ji = split_text(info['ji'], 50)     lable_laohuangli.text = "农历 {}\n宜 {}\n忌 {}".format(         info['yinli'], text_yi, text_ji) ntp_sync_time() display_update_time() qweather_now_info = qweather_weather_now() display_update_weather(qweather_now_info) laohuangli_info = juhe_loahuangli() display_update_laohuangli(laohuangli_info) time_update_start = time.time() # Loop forever so you can enjoy your image while True:     time.sleep(0.5)     time_current = time.time()     # sync ntp and weather     if time_current - time_update_start >= 2 * 60 * 60:         time_start = time.time_current         ntp_sync_time()         weather_info = qweather_weather_now()         display_update_weather(weather_info)         laohuangli_info = juhe_loahuangli()         display_update_laohuangli(laohuangli_info)         gc.collect()     display_update_time()     # led.value = not led.value ``` ### 效果展示 ## 总结 circuitpython 提供的 http 请求库 adafruit_requests 使用非常方便,配合内置的 zlib 和 json 模块,对于网络数据的解析非常友好。

  • 回复了主题帖: 【FireBeetle 2 ESP32 C6 开发板】+ 驱动 DFR0664 IPS LCD

    Jacktang 发表于 2024-5-24 07:24 应该是可以玩玩从 SD 卡中读取字体库和图片来显示的东西了 可以玩,circuitpython 的库还是比较完善的,我最后示例就是用的 sd 库的图片和字库,很方便

  • 回复了主题帖: 【FireBeetle 2 ESP32 C6 开发板】+ 驱动 DFR0664 IPS LCD

    chejm 发表于 2024-5-24 12:26 楼主分享的技术内容非常详实,开阔了眼界,有机会一定实践下 可以,circuitpython 的库还是比较好上手的

  • 2024-05-23
  • 发表了主题帖: 【FireBeetle 2 ESP32 C6 开发板】+ 驱动 DFR0664 IPS LCD

    ## Previously 1. [【FireBeetle 2 ESP32 C6】+ 开箱及更新 Circuitpy 固件](https://bbs.eeworld.com.cn/thread-1281496-1-1.html) 1. [【FireBeetle 2 ESP32 C6 开发板】+ 使用 Web Workflow 开发](https://bbs.eeworld.com.cn/thread-1281597-1-1.html) 1. [【FireBeetle 2 ESP32 C6 开发板】+ 测试使用板载 core modules](https://bbs.eeworld.com.cn/thread-1282655-1-1.html) # 驱动 DFR0664 TFT 显示屏 [DFR0664 显示屏](https://wiki.dfrobot.com.cn/_SKU_DFR0664_2.0_240x320_LCD) 采用 ST7789V 驱动芯片,其分辨率为320x240,采用 SPI 通信方式,并板载 SD 卡槽,可以轻松的从 SD 卡中读取全彩色位图。模块提供了两种接线方式,一种为普通排针接线方式;另一种为GDI(General Display interface)接口       ## 引脚说明 |  标号 | 名称  | 功能描述  | | ------------ | ------------ | ------------ | | 1  | VCC  | 电源正极  | | 2  | GND  | 电源负极  | | 3  | SCLK  | 时钟  | | 4  | MOSI  | 数据(主机发送从机接收)  | | 5  | MISO  | 数据(主机接收从机发送  | | 6  | CS  | 屏幕片选  | | 7  | RES  | 复位  | | 8  | DC  | 数据/命令  | | 9  | BL  | 背光。背光设定了默认值,用户不用连接背光引脚也可点亮;此外,连接背光引脚,输入高电平(1)是将背光亮度调到最大,输入低电平(0)是关闭背光  | | 10  | SDCS  | SD卡片选  | ## CircuitPython Displayio Quickstart ### 导入 cpy 库 需要从 adafruit-circuitpython-bundle 获取驱动库 `adafruit_st7789.mpy` 和 显示库(整个目录) `adafruit_display_text`,导入至板卡的 lib 目录,具体步骤可以参考上一章 ### 示例代码 实现了背景及上层画布的绘制和文本的显示。 > 引脚说明 > rst 引脚我没有赋值,因为 GDI 接口连到了板卡的 IO14 引脚,但是 circuitpython 并没有初始化该引脚,所以无法使用 > bl 引脚在 GDI 接口中连到了板卡的 IO15 引脚,和板载 LED 脚位共用,后续使用需要注意一下 ```python # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT """ This test will initialize the display using displayio and draw a solid green background, a smaller purple rectangle, and some yellow text. """ import board import busio import terminalio import displayio # Starting in CircuitPython 9.x fourwire will be a seperate internal library # rather than a component of the displayio library try:     from fourwire import FourWire except ImportError:     from displayio import FourWire from adafruit_display_text import label from adafruit_st7789 import ST7789 # led = digitalio.DigitalInOut(board.LED) # led.direction = digitalio.Direction.OUTPUT # Release any resources currently in use for the displays displayio.release_displays() # spi = board.SPI() spi = busio.SPI(board.D23, MOSI=board.D22, MISO=board.D21) tft_cs = board.D1 tft_dc = board.D8 display_bus = FourWire(spi, command=tft_dc, chip_select=tft_cs""", reset=board.D14""") display = ST7789(display_bus, width=320, height=240, rotation=90) # Make the display context splash = displayio.Group() display.root_group = splash color_bitmap = displayio.Bitmap(320, 240, 1) color_palette = displayio.Palette(1) color_palette[0] = 0x00FF00  # Bright Green bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0) splash.append(bg_sprite) # Draw a smaller inner rectangle inner_bitmap = displayio.Bitmap(280, 200, 1) inner_palette = displayio.Palette(1) inner_palette[0] = 0xAA0088  # Purple inner_sprite = displayio.TileGrid(inner_bitmap, pixel_shader=inner_palette, x=20, y=20) splash.append(inner_sprite) # Draw a label text_group = displayio.Group(scale=3, x=57, y=120) text = "Hello World!" text_area = label.Label(terminalio.FONT, text=text, color=0xFFFF00) text_group.append(text_area)  # Subgroup for text scaling splash.append(text_group) while True:     pass ``` ### 效果展示 ## 驱动 SD 卡 DFR0664 板载了 SD 卡槽,和 LCD 共用 SPI 驱动 ### 导入 cpy 库 需要从 adafruit-circuitpython-bundle 获取驱动库 `adafruit_sdcard.mpy `,并导入至板卡的 lib 目录 ### 示例代码 挂在 SDCard 到 /sd 目录,遍历打印该目录文件 ```python # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT """ This test will initialize the display using displayio and draw a solid green background, a smaller purple rectangle, and some yellow text. """ import os import time import board import digitalio import displayio import busio import sdcardio import storage led = digitalio.DigitalInOut(board.LED) led.direction = digitalio.Direction.OUTPUT displayio.release_displays() spi = busio.SPI(board.D23, MOSI=board.D22, MISO=board.D21) sdcard = sdcardio.SDCard(spi, board.D18) vfs = storage.VfsFat(sdcard) storage.mount(vfs, "/sd") def print_directory(path, tabs=0):     for file in os.listdir(path):         stats = os.stat(path + "/" + file)         filesize = stats[6]         isdir = stats[0] & 0x4000         if filesize < 1000:             sizestr = str(filesize) + " by"         elif filesize < 1000000:             sizestr = "%0.1f KB" % (filesize / 1000)         else:             sizestr = "%0.1f MB" % (filesize / 1000000)         prettyprintname = ""         for _ in range(tabs):             prettyprintname += "   "         prettyprintname += file         if isdir:             prettyprintname += "/"         print('{0:10}'.format(prettyprintname, sizestr))         # recursively print directory contents         if isdir:             print_directory(path + "/" + file, tabs + 1) print("Files on filesystem:") print("====================") print_directory("/sd") while True:     time.sleep(0.5)     led.value = not led.value ``` ### 显示效果 运行之后可以直接通过 web workflow 的文件目录访问,非常方便 ## sd 卡导入字库和图片显示 ### 导入 cpy 库 需要从 adafruit-circuitpython-bundle 获取图片库 `adafruit_imageload`,字体库 `adafruit_bitmap_font`,并导入至板卡的 lib 目录 ### 示例代码 从 sd 卡导入字体库和图片,显示 ```python # SPDX-FileCopyrightText: 2024 id.loda # SPDX-License-Identifier: MIT import board import busio import digitalio import displayio import sdcardio import storage import terminalio import time # Starting in CircuitPython 9.x fourwire will be a seperate internal library # rather than a component of the displayio library try:     from fourwire import FourWire except ImportError:     from displayio import FourWire from adafruit_display_text import label from adafruit_st7789 import ST7789 from adafruit_bitmap_font import bitmap_font import adafruit_imageload # Release any resources currently in use for the displays displayio.release_displays() spi1_bus = busio.SPI(board.D23, MOSI=board.D22, MISO=board.D21) display_bus = FourWire(spi1_bus, command=board.D8, chip_select=board.D1) display = ST7789(display_bus, width=320, height=240, rotation=90) sdcard = sdcardio.SDCard(spi1_bus, board.D18) vfs = storage.VfsFat(sdcard) storage.mount(vfs, "/sd") # Make the display context splash = displayio.Group() display.root_group = splash color_bitmap = displayio.Bitmap(320, 240, 1) color_palette = displayio.Palette(1) color_palette[0] = 0x000000 # 0xF8F8FF  # Ghost White bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0) splash.append(bg_sprite) # 180 * 125 with open("/sd/resources/pic/test_image.png", "rb") as f:     blinka_bitmap, blinka_palette = adafruit_imageload.load(f, bitmap=displayio.Bitmap, palette=displayio.Palette) blinka_sprite = displayio.TileGrid(blinka_bitmap, pixel_shader=blinka_palette, x=140, y=115) splash.append(blinka_sprite) # 160 * 56 with open("/sd/resources/pic/ee.bmp", "rb") as f:     eeworld_bitmap, eeworld_palette = adafruit_imageload.load(f, bitmap=displayio.Bitmap, palette=displayio.Palette) eeworld_sprite = displayio.TileGrid(eeworld_bitmap, pixel_shader=eeworld_palette, x=0, y=20) splash.append(eeworld_sprite) # Set text, font, and color font = bitmap_font.load_font("/sd/resources/font/opposans_m_12.pcf") # Create the text label lable_area_fw = label.Label(     font, x=10, y=96, text="FireBeetle 2 ESP32 C6", scale=1, color= 0x191970 ) lable_area_id = label.Label(     font, x=10, y=128, text="ID.LODA", scale=1, color=(0, 191, 255) ) splash.append(lable_area_fw) splash.append(lable_area_id) while True:     time.sleep(0.5)     # led.value = not led.value ``` ### 运行效果 # 总结 circuitpython 提供了比较丰富的库,可以方便的绘制转换需要的资源。但是没有继承类似与 emwin、lvgl 等成熟的图形库。

  • 发表了主题帖: 【FireBeetle 2 ESP32 C6 开发板】+ 测试使用板载 core modules

    本帖最后由 ID.LODA 于 2024-5-23 15:05 编辑 # Core Modules 的测试、使用 记上一章使用 Web Workflow 之后开发稳定性提高很多,今天来学习下 circuitpython core modules 的使用 ## 查看板载支持的 modules 可以在 repl 或者 code.py 中输入 ``help("modules")`` 即可查看 ## microcontroller 处理器模块的接口介绍可以查看以下章节 [microcontroller](https://docs.circuitpython.org/en/latest/shared-bindings/microcontroller/index.html),包含了 unique id、cpu、frequency、temperature 等参数的获取,以及终端复位等的控制 ### 示例演示 实现了 cpu 基础信息的获取 ```python # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT """ This test will initialize the display using displayio and draw a solid green background, a smaller purple rectangle, and some yellow text. """ import os import time import board import digitalio import microcontroller led = digitalio.DigitalInOut(board.LED) led.direction = digitalio.Direction.OUTPUT cpu = microcontroller.cpu print("cpu uid: ", [hex(i) for i in cpu.uid]) print("cpu frequency: ", cpu.frequency) print("cpu temperature: ", cpu.temperature) print("cpu voltage: ", cpu.voltage) print("cpu reset reason: ", cpu.reset_reason) while True:     time.sleep(0.5)     led.value = not led.value microcontroller.reset() ``` #### 结果展示 ## WIFI wifi 模块的接口介绍可以查看以下章节 [wifi](https://docs.circuitpython.org/en/latest/shared-bindings/wifi/index.html),该模块为管理 wifi 连接提供了必要的底层功能,以及使用套接字池通过网络进行通信。 ### 基础示例 实现了 WIFI 的连接,socket pool 的创建,域名解析以及 ping 包的实现 #### 示例代码 介于我在 settings.toml 里设置了 `CIRCUITPY_WIFI_SSID`、`CIRCUITPY_WIFI_PASSWORD`,通过 os.getenv 可以很方便的获取对应的数据 ```python # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT """ This test will initialize the display using displayio and draw a solid green background, a smaller purple rectangle, and some yellow text. """ import os import time import board import digitalio import socketpool import wifi import ipaddress led = digitalio.DigitalInOut(board.LED) led.direction = digitalio.Direction.OUTPUT # code handler while not wifi.radio.ipv4_address:     try:         print("Connecting to WiFi")         #  connect to your SSID         wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))     except ConnectionError as e:         print("connect error:{}, retry in 2s".format(e))     time.sleep(2) pool = socketpool.SocketPool(wifi.radio) #  prints IP address to REPL print("My IP address is", wifi.radio.ipv4_address) #  prints MAC address to REPL print("My MAC addr:", [hex(i) for i in wifi.radio.mac_address]) #  pings eeworld ip_address = pool.getaddrinfo("eeworld.com.cn", 80)[0][-1][0] ipv4 = ipaddress.ip_address(ip_address) print("Ping eeworld.com: %f ms" % (wifi.radio.ping(ipv4)*1000)) while True:     time.sleep(0.5)     led.value = not led.value ``` #### 运行结果 ### http 请求示例 进行 http 请求依赖第三方库,可以在 [library 官网](https://circuitpython.org/libraries) 根据对应的版本下载示例包,我这边使用的是 9.x,所以下载的 9.x 版本 #### 导入库 依赖 adafruit_connection_manager.mpy、adafruit_requests.mpy 库,将其导入 lib 目录即可 #### 示例代码 通过请求 https://www.adafruit.com/api/quotes.php,获取网页的内容,并打印 ```python # SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries # # SPDX-License-Identifier: MIT import os import time import ssl import wifi import socketpool import microcontroller import adafruit_requests #  adafruit quotes URL quotes_url = "https://www.adafruit.com/api/quotes.php" #  connect to SSID wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD')) pool = socketpool.SocketPool(wifi.radio) requests = adafruit_requests.Session(pool, ssl.create_default_context()) while True:     try:         #  pings adafruit quotes         print("Fetching text from %s" % quotes_url)         #  gets the quote from adafruit quotes         response = requests.get(quotes_url)         print("-" * 40)         #  prints the response to the REPL         print("Text Response: ", response.text)         print("-" * 40)         response.close()         #  delays for 1 minute         time.sleep(60)     # pylint: disable=broad-except     except Exception as e:         print("Error:\n", str(e))         print("Resetting microcontroller in 10 seconds")         time.sleep(10)         microcontroller.reset() ``` #### 运行结果 # 总结 circuitpython 内置了很多功能模块,官网也提供了对应的接口文档,相对还是比较上手使用

  • 2024-05-11
  • 发表了主题帖: 【FireBeetle 2 ESP32 C6 开发板】+ 使用 Web Workflow 开发

    # 搭建 Web Workflow 开发环境 介于 thonny ide 用 repl 连接不是很稳定,尝试使用 Web Workflow 在线开发 ## 配置需求 根据官网介绍,将 settings.toml 的文件添加到 CircuitPython 文件系统的根文件夹时,就会启用 Web 工作流,此文件需要包含本地wifi 信息和其他设置。[点击此处了解更多信息](https://docs.circuitpython.org/en/latest/docs/workflows.html#web)。 settings.toml 文件的基本内容如下: ```python CIRCUITPY_WIFI_SSID = "wifissid" CIRCUITPY_WIFI_PASSWORD = "wifipassword" CIRCUITPY_WEB_API_PASSWORD= "webpassword" ``` > 说明 > wifissid 替换为本地 wifi 网络名称 > wifipassword 替换为本地 wifi 网络密码 > webpassword 通过网络浏览器连接到板时使用,设置为任意值 ## 导入配置 1. 借助 thonny 工具,连接板卡 1. 打开文件视图,修改 setting.toml 文件         1. [官网](https://learn.adafruit.com/circuitpython-with-esp32-quick-start/setting-up-web-workflow) 还介绍了通过 repl 的方式,通过命令行写入文件即可。感兴趣的小伙伴可以关注下。         ```json         f = open('settings.toml', 'w')         f.write('CIRCUITPY_WIFI_SSID = "wifissid"\n')         f.write('CIRCUITPY_WIFI_PASSWORD = "wifipassword"\n')         f.write('CIRCUITPY_WEB_API_PASSWORD = "webpassword"\n')         f.close()         ``` 1. 重启看到 ip 地址之后,打开浏览器,使用 MDNS 地址 ``circuitpython.local`` 连接板卡,可以看到欢迎信息,包含了板卡的名称、版本等信息。失败时可以尝试使用 IP 地址打开         1. 点击欢迎页面的 ``full code editer`` 跳转至代码编辑页面,输入 settings.toml 设置的 CIRCUITPY_WEB_API_PASSWORD 密码即可,忽略用户名         1. 现在已经在线编辑器界面,打开文件进行编码                 ```python         # SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries         #         # SPDX-License-Identifier: MIT         """Example for ESP32-C6. Blinks the built-in LED."""         import time         import board         import digitalio         import wifi         led = digitalio.DigitalInOut(board.LED)         led.direction = digitalio.Direction.OUTPUT         print("My MAC addr: %02X:%02X:%02X:%02X:%02X:%02X" % tuple(wifi.radio.mac_address))         print("My IP address is", wifi.radio.ipv4_address)         while True:                 led.value = True                 time.sleep(0.5)                 led.value = False                 time.sleep(0.5)         ``` 1. 保存看到设备打印信息,板载 LED 间断闪烁 # 总结 web workflow 环境配置不是很复杂, 只需在 settings.toml 添加几项内容。而且相比 thonny 来看,修改、保存文件要更加稳定一些,是个不错的选择。

  • 发表了主题帖: 【FireBeetle 2 ESP32 C6】+ 开箱及更新 Circuitpy 固件

    # 开箱 板卡的包装和之前的 FireBeetle 类型一样比较简单,包含了板卡和排针 正反面 # 简介 FireBeetle 2 ESP32-C6是一款基于ESP32-C6芯片设计的低功耗物联网开发板,适用于智能家居项目。ESP32-C6搭载160MHz的高性能RISC-V 32位处理器,支持Wi-Fi 6、Bluetooth 5、Zigbee 3.0、Thread 1.3通讯协议,可接入多种通讯协议的物联网网络。FireBeetle 2 ESP32-C6支持Type-C、5V DC、太阳能对锂电池进行充电,部署时有更多的供电方式选择。 - 多种传输协议支持,扩展无线连接性 - 支持Wi-Fi 6,实现超低功耗物联网设备 - 优秀的电源系统,设备供电更方便 板卡的详细资源和入门文档可以在 [dfrobot 官方 wiki](https://wiki.dfrobot.com.cn/_SKU_DFR1075_FireBeetle_2_Board_ESP32_C6) 上找到。 ![引脚示意图](https://img.dfrobot.com.cn/wiki/5d57611a3416442fa39bffca/b9ef4c5ab7be64cd085be9cd424c87b2.png) # 更新 CircuitPython 固件 1. 下载 circuitpython 固件 [adafruit_feather_esp32c6_4mbflash_nopsram](https://circuitpython.org/board/adafruit_feather_esp32c6_4mbflash_nopsram) , 选择 adafruit_feather_esp32c6_4mbflash_nopsram 板卡的固件,因为和 FireBeetle 2 ESP32-C6 配置是一致的,除开引脚有一些区别,但是不影响 1. [点击下载 esptool Flash 烧录工具](https://www.espressif.com.cn/sites/default/files/tools/flash_download_tool_3.9.6_0.zip) 1. 运行 flash_download_tool_3.9.6.exe 1. 通过 USB 线连接电脑,选择 ESP32-C6 主控,然后按住 BOOT,点击 RST 1. 选择下载的固件,擦除flash后烧录固件 ## 指令方式更新 # 搭建 circuitpython 环境 1. 烧录完成之后,复位芯片即可,和其他芯片不同的是,因为 ESP32 不支持 native USB,所以不会弹 CIRCUITPY 的驱动,只能借助其他工具用 REPL 去开发 1. 下载安装 [thonny](https://thonny.org/) 1. 运行 thonny.exe 1. 输入测试指令 # 编写 circuitpython 代码 1. circuitpython 启动时自动运行 code.py,我们在 code.py 编写自己的代码 > 注意事项 >     这边我没找到 adafruit_feather_esp32c6 的原理图,其实不知道引脚的定义情况,好在用 board.led 能点亮FireBeetle_2_Board_ESP32_C6 的灯。后续测试下其他外设引脚。 > 查看板卡支持引脚,可以使用 print(dir(board)) 输出 # 总结 esp32-c6 因为自身 USB 的问题,运行 circuitpython 固件没有 CIRCUITPY 磁盘,需要借助其他工具,这点比较麻烦,而且个人使用下来感觉 thonny 连接不是很稳定,尬住了

  • 2024-04-18
  • 回复了主题帖: 测评入围:FireBeetle 2 ESP32 C6开发板

    个人信息无误,确认可以完成评测计划

  • 2024-03-29
  • 回复了主题帖: 【EEWorld发奖礼品--获奖名单】STM32 Summit全球在线大会

    好久没中奖了,该轮到我了

  • 2024-02-25
  • 发表了主题帖: 【得捷电子Follow me第4期】项目总结报告

    # 项目介绍 本次项目使用核心板 W5500-EVB-Pico,学习了网络相关的知识,熟悉了网络开发基础 API 的使用,配合 LCD 扩展版和按键控制器扩展板,使用 circuitpython 开发完成如下任务: 1. 入门任务:开发环境搭建,BLINK,驱动液晶显示器进行显示(没有则串口HelloWorld) 1. 基础任务一:完成主控板W5500初始化(静态IP配置),并能使用局域网电脑ping通,同时W5500可以ping通互联网站点;通过抓包软件(Wireshark、Sniffer等)抓取本地PC的ping报文,展示并分析。 1. 基础任务二:主控板建立TCPIP或UDP服务器,局域网PC使用TCPIP或UDP客户端进行连接并发送数据,主控板接收到数据后,送液晶屏显示(没有则通过串口打印显示);通过抓包软件抓取交互报文,展示并分析。(TCP和UDP二选一,或者全都操作) 1. 进阶任务:从NTP服务器(注意数据交互格式的解析)同步时间,获取时间送显示屏(串口)显示。 1. 终极任务: 使用外部存储器,组建简易FTP文件服务器,并能正常上传下载文件。 ## 功能实现 1. 驱动 W5500 网络控制器,并连接 NTP 服务器进行网络授时,实现简易 TCP 服务器及 FTP 服务器 1. 驱动 SD 卡,导入 LCD 显示的字体和背景图片,并为 FTP 服务器提供文件存储空间 1. 驱动 LCD 显示本地时间,网络相关参数及调试信息 1. 驱动按键控制器,进行服务器的切换 ## 演示视频 [演示视频](https://training.eeworld.com.cn/video/39302 "演示视频") # 任务/项目总结报告 本次活动,使用的硬件为: 1. 控制器:[W5500-EVB-Pico](https://www.digikey.cn/zh/products/detail/wiznet/W5500-EVB-PICO/16515824 "W5500-EVB-Pico")     W5500-EVB-Pico 是基于 Raspberry Pi RP2040 和完全硬连线 TCP/IP控制器 W5500 的微控制器评估板,其工作原理与Raspberry Pi Pico 板基本相同,但通过 W5500 增加了以太网。具体介绍参考 [wiki](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico)     ![w5500-evb-pico](https://docs.wiznet.io/assets/images/w5500-evb-pico-pinout-fe942a8c8d6e9d591c0ae8bc7312d592.png) 1. 显示屏:[DFR0664 2.0 320x240 IPS TFT LCD Display with MicroSD Card (Breakout)](https://www.digikey.cn/zh/products/detail/dfrobot/DFR0664/13166503)     具体介绍参考 [wiki](https://wiki.dfrobot.com.cn/_SKU_DFR0664_2.0_240x320_LCD)     ![DFR0664](https://img.dfrobot.com.cn/wiki/none/bbc55593d232a10fced56ab97637f646.jpg) 1. 键盘控制器:[MINI I2C GAMEPAD WITH SEESAW](https://www.digikey.cn/zh/products/detail/adafruit-industries-llc/5743/20370632)     具体介绍参考 [wiki](https://learn.adafruit.com/gamepad-qt)     ![gamepad-qt](https://cdn-learn.adafruit.com/assets/assets/000/122/121/medium800/adafruit_products_GPQT_top.jpg?1687817547) ## 入门任务:开发环境搭建,BLINK,驱动液晶显示器进行显示(没有则串口HelloWorld) 开发语言选择的是 circuit python,具体资料可以前往 adafruit 的 [circuit python 官网](https://learn.adafruit.com/welcome-to-circuitpython) 学习 ### circuit python 安装 1. 前往 [circuit python 板卡中心](https://circuitpython.org/downloads) 找到控制板 [W5500-EVB-PICO](https://circuitpython.org/board/wiznet_w5500_evb_pico/),下载对应的 UF2 固件 1. 按下 BOOT 按键不松手,上电模块之后松手即可进入下载模式 1. 打开电脑新增的驱动器,将上一步下载的 UF2 固件复制进 U 盘,指示灯应再次闪烁,启动将消失,并且您的计算机上将显示一个名为 CIRCUITPY 的新驱动器,此时已成功安装或更新 CircuitPython! 1. 打开新的驱动器,编辑 code.py 文件进行编程 1. circuit python 的库文件可以在 [libraries 官网](https://circuitpython.org/libraries) 找到,本节任务使用了 SPI 总线,LCD 显示器及 SD 卡,需要导入如下库文件进新的驱动器的 lib 目录 ``` bash adafruit_bus_device/ adafruit_display_text/ adafruit_bitmap_font/ adafruit_imageload/ adafruit_sdcard.mpy adafruit_st7789.mpy ``` circuitpython 驱动 st7789 LCD 的参考资料 https://learn.adafruit.com/2-0-inch-320-x-240-color-ips-tft-display 驱动 sd 卡的参考资料 https://learn.adafruit.com/micropython-hardware-sd-cards ### 任务代码 ```python # SPDX-FileCopyrightText: 2024 id.loda # SPDX-License-Identifier: MIT import board import busio import digitalio import displayio import sdcardio import storage import terminalio import time # Starting in CircuitPython 9.x fourwire will be a seperate internal library # rather than a component of the displayio library try:     from fourwire import FourWire except ImportError:     from displayio import FourWire from adafruit_display_text import label from adafruit_st7789 import ST7789 from adafruit_bitmap_font import bitmap_font import adafruit_imageload # SPI1 for tft SPI1_SCK = board.GP10 SPI1_TX = board.GP11 SPI1_RX = board.GP12 # tft lcd io TFT_CSn = board.GP13 TFT_RSTn = board.GP14 TFT_DC = board.GP15 # sdcard io SD_CSn = board.GP9 led = digitalio.DigitalInOut(board.LED) led.direction = digitalio.Direction.OUTPUT # Release any resources currently in use for the displays displayio.release_displays() spi1_bus = busio.SPI(SPI1_SCK, MOSI=SPI1_TX, MISO=SPI1_RX) display_bus = FourWire(spi1_bus, command=TFT_DC, chip_select=TFT_CSn, reset=TFT_RSTn) display = ST7789(display_bus, width=320, height=240, rotation=90) sdcard = sdcardio.SDCard(spi1_bus, SD_CSn) vfs = storage.VfsFat(sdcard) storage.mount(vfs, "/sd") # Make the display context splash = displayio.Group() display.root_group = splash color_bitmap = displayio.Bitmap(320, 240, 1) color_palette = displayio.Palette(1) color_palette[0] = 0x000000 # 0xF8F8FF  # Ghost White bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0) splash.append(bg_sprite) # 160 * 29 with open("/sd/resources/pic/dk.bmp", "rb") as f:     digikey_bitmap, digikey_palette = adafruit_imageload.load(f, bitmap=displayio.Bitmap, palette=displayio.Palette) digikey_sprite = displayio.TileGrid(digikey_bitmap, pixel_shader=digikey_palette, x=0, y=34) splash.append(digikey_sprite) # 160 * 56 with open("/sd/resources/pic/ee.bmp", "rb") as f:     eeworld_bitmap, eeworld_palette = adafruit_imageload.load(f, bitmap=displayio.Bitmap, palette=displayio.Palette) eeworld_sprite = displayio.TileGrid(eeworld_bitmap, pixel_shader=eeworld_palette, x=160, y=20) splash.append(eeworld_sprite) # Set text, font, and color font = bitmap_font.load_font("/sd/resources/font/opposans_m_12.pcf") # Create the text label lable_area_fw = label.Label(     font, x=10, y=120, text="Follow me 第四期!", scale=2, color=0xF8F8FF # 0x191970 ) lable_area_id = label.Label(     font, x=20, y=160, text="ID.LODA", scale=2, color=(0, 191, 255) ) splash.append(lable_area_fw) splash.append(lable_area_id) while True:     time.sleep(0.5)     led.value = not led.value ``` ### 任务效果 从 SD 卡导入字体库和背景图片,并通过 LCD 进行显示 ## 基础任务一:完成主控板W5500初始化(静态IP配置),并能使用局域网电脑ping通,同时W5500可以ping通互联网站点;通过抓包软件(Wireshark、Sniffer等)抓取本地PC的ping报文,展示并分析。 ### 实现步骤 1. 完成该任务需要导入 Wiznet5K 系列的库文件 `adafruit_wiznet5k/`, 参考文档 https://learn.adafruit.com/ethernet-for-circuitpython 1. 安装 wireshake 抓包工具 1. 程序运行之后,启动wireshake 抓取板卡 ip 1. windows 端打开控制台 ping 板卡 ### 任务代码 ```python # SPDX-FileCopyrightText: 2024 id.loda # SPDX-License-Identifier: MIT import board import busio import digitalio import displayio import sdcardio import storage import terminalio import time # Starting in CircuitPython 9.x fourwire will be a seperate internal library # rather than a component of the displayio library try:     from fourwire import FourWire except ImportError:     from displayio import FourWire from adafruit_display_text import label from adafruit_st7789 import ST7789 from adafruit_bitmap_font import bitmap_font import adafruit_imageload from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K # SPI0 for w5x00 SPI0_SCK = board.GP18 SPI0_TX = board.GP19 SPI0_RX = board.GP16 SPI0_CSn = board.GP17 ## w5x00 reset W5x00_RSTn = board.GP20 # SPI1 for tft SPI1_SCK = board.GP10 SPI1_TX = board.GP11 SPI1_RX = board.GP12 # tft lcd io TFT_CSn = board.GP13 TFT_RSTn = board.GP14 TFT_DC = board.GP15 # sdcard io SD_CSn = board.GP9 led = digitalio.DigitalInOut(board.LED) led.direction = digitalio.Direction.OUTPUT # Release any resources currently in use for the displays displayio.release_displays() spi1_bus = busio.SPI(SPI1_SCK, MOSI=SPI1_TX, MISO=SPI1_RX) display_bus = FourWire(spi1_bus, command=TFT_DC, chip_select=TFT_CSn, reset=TFT_RSTn) display = ST7789(display_bus, width=320, height=240, rotation=90) sdcard = sdcardio.SDCard(spi1_bus, SD_CSn) vfs = storage.VfsFat(sdcard) storage.mount(vfs, "/sd") # Make the display context splash = displayio.Group() display.root_group = splash color_bitmap = displayio.Bitmap(320, 240, 1) color_palette = displayio.Palette(1) color_palette[0] = 0x000000 # 0xF8F8FF  # Ghost White bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0) splash.append(bg_sprite) # 180 * 125 with open("/sd/resources/pic/test_image.png", "rb") as f:     blinka_bitmap, blinka_palette = adafruit_imageload.load(f, bitmap=displayio.Bitmap, palette=displayio.Palette) blinka_sprite = displayio.TileGrid(blinka_bitmap, pixel_shader=blinka_palette, x=140, y=115) splash.append(blinka_sprite) text_group = displayio.Group(scale=1, x=10, y=10) text_area = label.Label(terminalio.FONT, text="", color=0xFFFF00) text_group.append(text_area) # Subgroup for text scaling splash.append(text_group) # code handler eth_rst = digitalio.DigitalInOut(W5x00_RSTn) eth_rst.direction = digitalio.Direction.OUTPUT eth_cs = digitalio.DigitalInOut(SPI0_CSn) spi0_bus = busio.SPI(SPI0_SCK, MOSI=SPI0_TX, MISO=SPI0_RX) # Reset W5500 first eth_rst.value = False time.sleep(1) eth_rst.value = True # Initialize ethernet interface with DHCP # eth = WIZNET5K(spi_bus, cs) # Initialize ethernet interface without DHCP eth = WIZNET5K(spi0_bus, eth_cs, is_dhcp=False) # Setup your network configuration below IP_ADDRESS = (192, 168, 0, 3) #(10, 38, 180, 221) SUBNET_MASK = (255, 255, 255, 0) # (255, 255, 254, 0) GATEWAY_ADDRESS = (192, 168, 0, 1) # (10, 38, 180, 1) DNS_SERVER = (211, 136, 150, 66) # (10, 38, 116, 23) # IP_ADDRESS = (10, 38, 180, 221) # SUBNET_MASK = (255, 255, 254, 0) # GATEWAY_ADDRESS = (10, 38, 180, 1) # DNS_SERVER = (10, 38, 116, 23) eth.ifconfig = (IP_ADDRESS, SUBNET_MASK, GATEWAY_ADDRESS, DNS_SERVER) ifconfig = eth.ifconfig text_area.text = text_area.text + "mac addr: {}\n".format(eth.pretty_mac(eth.mac_address)) text_area.text = text_area.text + "ip4 addr: {}\n".format(eth.pretty_ip(ifconfig[0])) text_area.text = text_area.text + "subnet addr: {}\n".format(eth.pretty_ip(ifconfig[1])) text_area.text = text_area.text + "gateway addr: {}\n".format(eth.pretty_ip(ifconfig[2])) text_area.text = text_area.text + "dns addr: {}\n".format(eth.pretty_ip(ifconfig[3])) domain_address = eth.get_host_by_name("eeworld.com.cn") text_area.text = text_area.text + "eeworld addr: {}\n".format(eth.pretty_ip(domain_address)) domain_address = eth.get_host_by_name("digikey.cn") text_area.text = text_area.text + "digikey addr: {}\n".format(eth.pretty_ip(domain_address)) domain_address = eth.get_host_by_name("adafruit.com") text_area.text = text_area.text + "adafruit addr: {}\n".format(eth.pretty_ip(domain_address)) print(text_area.text) while True:     time.sleep(0.5)     led.value = not led.value print("Done!") ``` ### 任务效果 驱动 W5500 控制器完成网络的初始化,并配置 ip、gateway、subnet、dns 等参数 - 显示界面 - ping 包流程 - ping 包解析 ## 基础任务二:主控板建立TCPIP或UDP服务器,局域网PC使用TCPIP或UDP客户端进行连接并发送数据,主控板接收到数据后,送液晶屏显示(没有则通过串口打印显示);通过抓包软件抓取交互报文,展示并分析。(TCP和UDP二选一,或者全都操作) ### 实现步骤 1. 基于上一节任务的基础,完成 TCP Server 的代码搭建 1. 打开 wireshake 进行抓包 1. 打开网络调试工具,建立 tcp 连接,并通讯 ### 任务代码 ```python # SPDX-FileCopyrightText: 2024 id.loda # SPDX-License-Identifier: MIT import board import busio import digitalio import displayio import sdcardio import storage import terminalio import time # Starting in CircuitPython 9.x fourwire will be a seperate internal library # rather than a component of the displayio library try:     from fourwire import FourWire except ImportError:     from displayio import FourWire from adafruit_display_text import label from adafruit_st7789 import ST7789 from adafruit_bitmap_font import bitmap_font import adafruit_imageload from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket # SPI0 for w5x00 SPI0_SCK = board.GP18 SPI0_TX = board.GP19 SPI0_RX = board.GP16 SPI0_CSn = board.GP17 # w5x00 reset W5x00_RSTn = board.GP20 # SPI1 for tft SPI1_SCK = board.GP10 SPI1_TX = board.GP11 SPI1_RX = board.GP12 # tft lcd io TFT_CSn = board.GP13 TFT_RSTn = board.GP14 TFT_DC = board.GP15 # sdcard io SD_CSn = board.GP9 led = digitalio.DigitalInOut(board.LED) led.direction = digitalio.Direction.OUTPUT # Release any resources currently in use for the displays displayio.release_displays() spi1_bus = busio.SPI(SPI1_SCK, MOSI=SPI1_TX, MISO=SPI1_RX) display_bus = FourWire(spi1_bus, command=TFT_DC, chip_select=TFT_CSn, reset=TFT_RSTn) display = ST7789(display_bus, width=320, height=240, rotation=90) sdcard = sdcardio.SDCard(spi1_bus, SD_CSn) vfs = storage.VfsFat(sdcard) storage.mount(vfs, "/sd") # Make the display context splash = displayio.Group() display.root_group = splash color_bitmap = displayio.Bitmap(320, 240, 1) color_palette = displayio.Palette(1) color_palette[0] = 0x000000 # 0xF8F8FF  # Ghost White bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0) splash.append(bg_sprite) # 180 * 125 with open("/sd/resources/pic/test_image.png", "rb") as f:     blinka_bitmap, blinka_palette = adafruit_imageload.load(f, bitmap=displayio.Bitmap, palette=displayio.Palette) blinka_sprite = displayio.TileGrid(blinka_bitmap, pixel_shader=blinka_palette, x=140, y=115) splash.append(blinka_sprite) text_group = displayio.Group(scale=1, x=10, y=10) text_area = label.Label(terminalio.FONT, text="", color=0xFFFF00) text_group.append(text_area) # Subgroup for text scaling splash.append(text_group) # code handler eth_rst = digitalio.DigitalInOut(W5x00_RSTn) eth_rst.direction = digitalio.Direction.OUTPUT eth_cs = digitalio.DigitalInOut(SPI0_CSn) spi0_bus = busio.SPI(SPI0_SCK, MOSI=SPI0_TX, MISO=SPI0_RX) # Reset W5500 first eth_rst.value = False time.sleep(1) eth_rst.value = True # Initialize ethernet interface with DHCP # eth = WIZNET5K(spi0_bus, eth_cs) # Initialize ethernet interface without DHCP eth = WIZNET5K(spi0_bus, eth_cs, is_dhcp=False) # # Setup your network configuration below IP_ADDRESS = (192, 168, 0, 3) #(10, 38, 180, 221) SUBNET_MASK = (255, 255, 255, 0) # (255, 255, 254, 0) GATEWAY_ADDRESS = (192, 168, 0, 1) # (10, 38, 180, 1) DNS_SERVER = (211, 136, 150, 66) # (10, 38, 116, 23) eth.ifconfig = (IP_ADDRESS, SUBNET_MASK, GATEWAY_ADDRESS, DNS_SERVER) ifconfig = eth.ifconfig text_area.text = text_area.text + "mac addr: {}\n".format(eth.pretty_mac(eth.mac_address)) text_area.text = text_area.text + "ip4 addr: {}\n".format(eth.pretty_ip(ifconfig[0])) text_area.text = text_area.text + "subnet addr: {}\n".format(eth.pretty_ip(ifconfig[1])) text_area.text = text_area.text + "gateway addr: {}\n".format(eth.pretty_ip(ifconfig[2])) text_area.text = text_area.text + "dns addr: {}\n".format(eth.pretty_ip(ifconfig[3])) print(text_area.text) # Initialize a socket for our server socket.set_interface(eth) # socket.setdefaulttimeout(50) server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)  # Allocate socket for the server server_ip = eth.pretty_ip(eth.ip_address)  # IP address of server server_port = 5000  # Port to listen on server.bind((server_ip, server_port))  # Bind to IP and Port server.listen()  # Begin listening for incoming clients while True:     print(f"Accepting connections on {server_ip}:{server_port}")     text_area.text = text_area.text + "Accepting connections on {}:{}\n".format(server_ip, server_port)     conn, addr = server.accept()  # Wait for a connection from a client.     led.value = True     print(f"Connection accepted from {addr}, reading exactly 1024 bytes from client")     text_area.text = text_area.text + "Connection accepted from {}\n".format(addr)     conn.settimeout(100)     conn.send("Welcome to w5500 evb pico's tcp server")     with conn:         buffer = bytearray(1024)         length = conn.recv_into(buffer, len(buffer))         if length > 0:             data = str(buffer[0:length], "utf-8"))             print(f"recv: {data}")             text_area.text = text_area.text + f"recv data: {data}\n"             conn.send(data)             # data = str(conn.recv(1024), "utf-8")             # if data:  # Wait for receiving data             #     cplog(f"recv: {data}")             #     conn.send(data)  # Echo message back to client     led.value = False     print("Connection closed") print("Done!") ``` ### 任务效果 - 显示界面         - 网络调试助手 tcp 连接     - wireshark 抓包分析     ## 进阶任务:从NTP服务器(注意数据交互格式的解析)同步时间,获取时间送显示屏(串口)显示。 ### 实现步骤 1. 本节任务需要添加 ntp 库 `adafruit_ntp.mpy` ### 任务代码 ```python # SPDX-FileCopyrightText: 2024 id.loda # SPDX-License-Identifier: MIT import board import busio import digitalio import displayio import sdcardio import storage import rtc import terminalio import time # Starting in CircuitPython 9.x fourwire will be a seperate internal library # rather than a component of the displayio library try:     from fourwire import FourWire except ImportError:     from displayio import FourWire from adafruit_display_text import label from adafruit_st7789 import ST7789 from adafruit_bitmap_font import bitmap_font import adafruit_imageload from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket import adafruit_ntp UTC_OFFSET = 8 WEEK = ['一', '二', '三', '四', '五', '六', '日'] # SPI0 for w5x00 SPI0_SCK = board.GP18 SPI0_TX = board.GP19 SPI0_RX = board.GP16 SPI0_CSn = board.GP17 ## w5x00 reset W5x00_RSTn = board.GP20 # SPI1 for tft SPI1_SCK = board.GP10 SPI1_TX = board.GP11 SPI1_RX = board.GP12 # tft lcd io TFT_CSn = board.GP13 TFT_RSTn = board.GP14 TFT_DC = board.GP15 # sdcard io SD_CSn = board.GP9 led = digitalio.DigitalInOut(board.LED) led.direction = digitalio.Direction.OUTPUT # Release any resources currently in use for the displays displayio.release_displays() spi1_bus = busio.SPI(SPI1_SCK, MOSI=SPI1_TX, MISO=SPI1_RX) display_bus = FourWire(spi1_bus, command=TFT_DC, chip_select=TFT_CSn, reset=TFT_RSTn) display = ST7789(display_bus, width=320, height=240, rotation=90) sdcard = sdcardio.SDCard(spi1_bus, SD_CSn) vfs = storage.VfsFat(sdcard) storage.mount(vfs, "/sd") # Make the display context splash = displayio.Group() display.root_group = splash color_bitmap = displayio.Bitmap(320, 240, 1) color_palette = displayio.Palette(1) color_palette[0] = 0x000000 # 0xF8F8FF  # Ghost White bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0) splash.append(bg_sprite) # 180 * 125 with open("/sd/resources/pic/test_image.png", "rb") as f:     blinka_bitmap, blinka_palette = adafruit_imageload.load(f, bitmap=displayio.Bitmap, palette=displayio.Palette) blinka_sprite = displayio.TileGrid(blinka_bitmap, pixel_shader=blinka_palette, x=140, y=115) splash.append(blinka_sprite) text_group = displayio.Group() # (scale=1, x=10, y=10) font = bitmap_font.load_font("/sd/resources/font/opposans_m_12.pcf") time_area = label.Label(font, x=10, y=12, text='', scale=1, color=0x00BFFF) text_area = label.Label(terminalio.FONT, x=10, y=30, text="", scale=1, color=0xFFFF00) text_group.append(time_area) text_group.append(text_area) # Subgroup for text scaling splash.append(text_group) # code handler eth_rst = digitalio.DigitalInOut(W5x00_RSTn) eth_rst.direction = digitalio.Direction.OUTPUT eth_cs = digitalio.DigitalInOut(SPI0_CSn) spi0_bus = busio.SPI(SPI0_SCK, MOSI=SPI0_TX, MISO=SPI0_RX) # Reset W5500 first eth_rst.value = False time.sleep(1) eth_rst.value = True # Initialize ethernet interface with DHCP # eth = WIZNET5K(spi0_bus, eth_cs) # Initialize ethernet interface without DHCP eth = WIZNET5K(spi0_bus, eth_cs, is_dhcp=False) # # Setup your network configuration below IP_ADDRESS = (192, 168, 0, 3) #(10, 38, 180, 221) SUBNET_MASK = (255, 255, 255, 0) # (255, 255, 254, 0) GATEWAY_ADDRESS = (192, 168, 0, 1) # (10, 38, 180, 1) DNS_SERVER = (211, 136, 150, 66) # (10, 38, 116, 23) eth.ifconfig = (IP_ADDRESS, SUBNET_MASK, GATEWAY_ADDRESS, DNS_SERVER) ifconfig = eth.ifconfig text_area.text = text_area.text + "mac addr: {}\n".format(eth.pretty_mac(eth.mac_address)) text_area.text = text_area.text + "ip4 addr: {}\n".format(eth.pretty_ip(ifconfig[0])) text_area.text = text_area.text + "subnet addr: {}\n".format(eth.pretty_ip(ifconfig[1])) text_area.text = text_area.text + "gateway addr: {}\n".format(eth.pretty_ip(ifconfig[2])) text_area.text = text_area.text + "dns addr: {}\n".format(eth.pretty_ip(ifconfig[3])) print(text_area.text) # Initialize a socket for our server socket.set_interface(eth) def ntp_sync_time():     print("Local time before synchronization:%s" % str(time.localtime()))     try:         # ntp = adafruit_ntp.NTP(pool, tz_offset=UTC_OFFSET)         ntp = adafruit_ntp.NTP(socket, server='ntp.aliyun.com', tz_offset=UTC_OFFSET)         # NOTE: This changes the system time so make sure you aren't assuming that time         # doesn't jump.         rtc.RTC().datetime = ntp.datetime         print("Local time after synchronization: %s" % str(time.localtime()))     except OSError as e:         print("ntp error:{}".format(e)) ntp_sync_time() counter = 0 while True:     if counter % 2 == 0:         date = time.localtime()         # time_area.text = f"{date[0]}/{date[1]}/{date[2]} {date[3]}:{date[4]}:{date[5]} 星期{WEEK[date[6]]}"         time_area.text = "{}/{:0>2}/{:0>2} {:0>2}:{:0>2}:{:0>2} 星期{}".format(date[0], date[1], date[2], date[3], date[4], date[5], WEEK[date[6]])     led.value = not led.value     time.sleep(0.5)     counter += 1 print("Done!") ``` ### 任务效果 ## 终极任务二:使用外部存储器,组建简易FTP文件服务器,并能正常上传下载文件。 ### 实现步骤 1. FTP 的代码实现参考了 github 上基于 esp32 micropython 的 [uftpd](https://github.com/robert-hh/FTP-Server-for-ESP8266-ESP32-and-PYBD)库,进行了 circuit python 的适配,总体差别不大,主要是 wifi 和 w5500 的网络接口差异,造成了比较大的困扰 1. 打开 FTP 客户端,这边我使用的 FlashFXP,连接板卡即可 ### 任务代码 ```python # SPDX-FileCopyrightText: 2024 id.loda # SPDX-License-Identifier: MIT # from os import listdir, remove, getcwd, chdir, stat, mkdir, rmdir, rename import os import gc import board import busio import digitalio import displayio import sdcardio import storage import rtc import terminalio import time # Starting in CircuitPython 9.x fourwire will be a seperate internal library # rather than a component of the displayio library try:     from fourwire import FourWire except ImportError:     from displayio import FourWire from adafruit_display_text import label from adafruit_st7789 import ST7789 from adafruit_bitmap_font import bitmap_font import adafruit_imageload from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket import adafruit_ntp UTC_OFFSET = 8 WEEK = ["一", "二", "三", "四", "五", "六", "日"] MONTH = [     "",     "Jan",     "Feb",     "Mar",     "Apr",     "May",     "Jun",     "Jul",     "Aug",     "Sep",     "Oct",     "Nov",     "Dec", ] BUTTON_X = const(6) BUTTON_Y = const(2) BUTTON_A = const(5) BUTTON_B = const(1) BUTTON_SELECT = const(0) BUTTON_START = const(16) button_mask = const(     (1 > 8, DATA_PORT % 256                             )                         )                         dataclient, data_addr = datasocket.accept()                         dataclient.settimeout(1)                         cplog(f"FTP Data connection from:{data_addr}")                     elif command == "PORT":                         items = payload.split(",")                         if len(items) >= 6:                             data_addr = ".".join(items[:4])                             if data_addr == "127.0.1.1":                                 # replace by command session addr                                 data_addr = remote_addr                             DATA_PORT = int(items[4]) * 256 + int(items[5])                             cl.send("200 OK\r\n")                         else:                             cl.send("504 Fail\r\n")                     elif command == "LIST" or command == "NLST":                         if not payload.startswith("-"):                             place = path                         else:                             place = cwd                         try:                             cl.send("150 Here comes the directory listing.\r\n")                             send_list_data(                                 place, dataclient, command == "LIST" or payload == "-l"                             )                             cl.send("226 Listed.\r\n")                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send(msg_550_fail)                         if dataclient is not None:                             # dataclient.close()                             dataclient._disconnect()                             dataclient = None                     elif command == "RETR":                         try:                             cl.send("150 Opening data connection.\r\n")                             send_file_data(path, dataclient)                             cl.send("226 Transfer complete.\r\n")                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send(msg_550_fail)                         if dataclient is not None:                             # dataclient.close()                             dataclient._disconnect()                             dataclient = None                     elif command == "STOR":                         try:                             cl.send("150 Ok to send data.\r\n")                             save_file_data(path, dataclient, "w")                             cl.send("226 Transfer complete.\r\n")                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send(msg_550_fail)                         if dataclient is not None:                             # dataclient.close()                             dataclient._disconnect()                             dataclient = None                     elif command == "APPE":                         try:                             cl.send("150 Ok to send data.\r\n")                             save_file_data(path, dataclient, "a")                             cl.send("226 Transfer complete.\r\n")                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send(msg_550_fail)                         if dataclient is not None:                             # dataclient.close()                             dataclient._disconnect()                             dataclient = None                     elif command == "DELE":                         try:                             os.remove(path)                             cl.send(msg_250_OK)                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send(msg_550_fail)                     elif command == "RMD" or command == "XRMD":                         try:                             os.rmdir(path)                             cl.send(msg_250_OK)                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send(msg_550_fail)                     elif command == "MKD" or command == "XMKD":                         try:                             os.mkdir(path)                             cl.send(msg_250_OK)                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send(msg_550_fail)                     elif command == "RNFR":                         fromname = path                         cl.send("350 Rename from\r\n")                     elif command == "RNTO":                         if fromname is not None:                             try:                                 os.rename(fromname, path)                                 cl.send(msg_250_OK)                             except Exception as err:                                 cplog(f"command {command} err:{err}")                                 cl.send(msg_550_fail)                         else:                             cl.send(msg_550_fail)                         fromname = None                     elif command == "MDTM":                         try:                             tm = time.localtime(os.stat(path)[8])                             cl.send(                                 "213 {:04d}{:02d}{:02d}{:02d}{:02d}{:02d}\r\n".format(                                     *tm[0:6]                                 )                             )                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send("550 Fail\r\n")                     elif command == "STAT":                         if payload == "":                             cl.send(                                 "211-Connected to ({})\r\n"                                 "    Data address ({})\r\n"                                 "211 TYPE: Binary STRU: File MODE:"                                 " Stream\r\n".format(remote_addr[0], addr)                             )                         else:                             cl.send("213-Directory listing:\r\n")                             send_list_data(path, cl, True)                             cl.send("213 Done.\r\n")                     elif command == "SITE":                         try:                             # exec(payload.replace("\0", "\n"))                             cl.send("250 OK\r\n")                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send("550 Fail\r\n")                     else:                         cl.send("502 Unsupported command.\r\n")                         print(                             "Unsupported command {} with payload {}".format(                                 command, payload                             )                         )             except Exception as err:                 cplog(f"err:{err}")             finally:                 cl.close()                 cl = None             break     finally:         datasocket.close()         ftpsocket.close()         if dataclient is not None:             dataclient.close() # send_list_data("/", None, True) ftpserver(     netif=eth,     socketpool=socket,     ipaddr=eth.pretty_ip(eth.ip_address),     timeout=500, ) cplog_clear() print("Done!") ``` ### 任务效果 ## 演示任务 ### 实现步骤 1. 增加了按键控制器的库 `adafruit_seesaw`, 通过按键触发不同服务器的使能 ### 任务代码 ```python # SPDX-FileCopyrightText: 2024 id.loda # SPDX-License-Identifier: MIT # from os import listdir, remove, getcwd, chdir, stat, mkdir, rmdir, rename import os import gc import board import busio import digitalio import displayio import sdcardio import storage import rtc import terminalio import time # Starting in CircuitPython 9.x fourwire will be a seperate internal library # rather than a component of the displayio library try:     from fourwire import FourWire except ImportError:     from displayio import FourWire from adafruit_display_text import label from adafruit_st7789 import ST7789 from adafruit_bitmap_font import bitmap_font import adafruit_imageload from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket import adafruit_ntp from adafruit_seesaw.seesaw import Seesaw UTC_OFFSET = 8 WEEK = ["一", "二", "三", "四", "五", "六", "日"] MONTH = [     "",     "Jan",     "Feb",     "Mar",     "Apr",     "May",     "Jun",     "Jul",     "Aug",     "Sep",     "Oct",     "Nov",     "Dec", ] BUTTON_X = const(6) BUTTON_Y = const(2) BUTTON_A = const(5) BUTTON_B = const(1) BUTTON_SELECT = const(0) BUTTON_START = const(16) button_mask = const(     (1 > 8, DATA_PORT % 256                             )                         )                         dataclient, data_addr = datasocket.accept()                         dataclient.settimeout(1)                         cplog(f"FTP Data connection from:{data_addr}")                     elif command == "PORT":                         items = payload.split(",")                         if len(items) >= 6:                             data_addr = ".".join(items[:4])                             if data_addr == "127.0.1.1":                                 # replace by command session addr                                 data_addr = remote_addr                             DATA_PORT = int(items[4]) * 256 + int(items[5])                             cl.send("200 OK\r\n")                         else:                             cl.send("504 Fail\r\n")                     elif command == "LIST" or command == "NLST":                         if not payload.startswith("-"):                             place = path                         else:                             place = cwd                         try:                             cl.send("150 Here comes the directory listing.\r\n")                             send_list_data(                                 place, dataclient, command == "LIST" or payload == "-l"                             )                             cl.send("226 Listed.\r\n")                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send(msg_550_fail)                         if dataclient is not None:                             dataclient._disconnect()                             dataclient.close()                             dataclient = None                     elif command == "RETR":                         try:                             cl.send("150 Opening data connection.\r\n")                             send_file_data(path, dataclient)                             cl.send("226 Transfer complete.\r\n")                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send(msg_550_fail)                         if dataclient is not None:                             dataclient._disconnect()                             dataclient.close()                             dataclient = None                     elif command == "STOR":                         try:                             cl.send("150 Ok to send data.\r\n")                             save_file_data(path, dataclient, "w")                             cl.send("226 Transfer complete.\r\n")                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send(msg_550_fail)                         if dataclient is not None:                             dataclient._disconnect()                             dataclient.close()                             dataclient = None                     elif command == "APPE":                         try:                             cl.send("150 Ok to send data.\r\n")                             save_file_data(path, dataclient, "a")                             cl.send("226 Transfer complete.\r\n")                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send(msg_550_fail)                         if dataclient is not None:                             dataclient._disconnect()                             dataclient.close()                             dataclient = None                     elif command == "DELE":                         try:                             os.remove(path)                             cl.send(msg_250_OK)                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send(msg_550_fail)                     elif command == "RMD" or command == "XRMD":                         try:                             os.rmdir(path)                             cl.send(msg_250_OK)                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send(msg_550_fail)                     elif command == "MKD" or command == "XMKD":                         try:                             os.mkdir(path)                             cl.send(msg_250_OK)                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send(msg_550_fail)                     elif command == "RNFR":                         fromname = path                         cl.send("350 Rename from\r\n")                     elif command == "RNTO":                         if fromname is not None:                             try:                                 os.rename(fromname, path)                                 cl.send(msg_250_OK)                             except Exception as err:                                 cplog(f"command {command} err:{err}")                                 cl.send(msg_550_fail)                         else:                             cl.send(msg_550_fail)                         fromname = None                     elif command == "MDTM":                         try:                             tm = time.localtime(os.stat(path)[8])                             cl.send(                                 "213 {:04d}{:02d}{:02d}{:02d}{:02d}{:02d}\r\n".format(                                     *tm[0:6]                                 )                             )                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send("550 Fail\r\n")                     elif command == "STAT":                         if payload == "":                             cl.send(                                 "211-Connected to ({})\r\n"                                 "    Data address ({})\r\n"                                 "211 TYPE: Binary STRU: File MODE:"                                 " Stream\r\n".format(remote_addr[0], addr)                             )                         else:                             cl.send("213-Directory listing:\r\n")                             send_list_data(path, cl, True)                             cl.send("213 Done.\r\n")                     elif command == "SITE":                         try:                             # exec(payload.replace("\0", "\n"))                             cl.send("250 OK\r\n")                         except Exception as err:                             cplog(f"command {command} err:{err}")                             cl.send("550 Fail\r\n")                     else:                         cl.send("502 Unsupported command.\r\n")                         print(                             "Unsupported command {} with payload {}".format(                                 command, payload                             )                         )             except Exception as err:                 cplog(f"err:{err}")             finally:                 cl.close()                 cl = None             break     finally:         datasocket.close()         ftpsocket.close()         if dataclient is not None:             dataclient.close() # send_list_data("/", None, True) def tcpserver(socketpool, ipaddr, port):     server = socketpool.socket(         socketpool.AF_INET, socketpool.SOCK_DGRAM     )  # Allocate socket for the server     server_ip = ipaddr  # IP address of server     server_port = port  # Port to listen on     server.bind((server_ip, server_port))  # Bind to IP and Port     server.listen()  # Begin listening for incoming clients     while True:         cplog(f"Accepting connections on {server_ip}:{server_port}")         conn, addr = server.accept()  # Wait for a connection from a client.         led.value = True         cplog(             f"Connection accepted from {addr}, reading exactly 1024 bytes from client"         )         conn.settimeout(100)         conn.send("Welcome to w5500 evb pico's tcp server")         with conn:             buffer = bytearray(1024)             length = conn.recv_into(buffer, len(buffer))             if length > 0:                 cplog(str(buffer[0:length], "utf-8"))                 conn.send(buffer[0:length])             # data = str(conn.recv(1024), "utf-8")             # if data:  # Wait for receiving data             #     cplog(f"recv: {data}")             #     conn.send(data)  # Echo message back to client         led.value = False         cplog("Connection closed")         break counter = 0 while True:     buttons = seesaw.digital_read_bulk(button_mask)     if not buttons & (1

  • 2024-02-22
  • 加入了学习《【得捷Follow me第4期】项目任务提交》,观看 视频

  • 2024-02-19
  • 回复了主题帖: 2024开工大吉,你期待测评中心,能有哪些板卡或书籍等?

    牛的,大金主 许愿一个 STM32U5A9J-DK,正好刚接触穿戴,学习下

  • 2024-02-01
  • 加入了学习《【得捷电子Follow me第3期】项目总结报告》,观看 【得捷Follow me第3期】项目总结报告

  • 2024-01-18
  • 回复了主题帖: 领取审核名单(第五批): 辞旧,年底清仓,100+板卡来袭,有缘来领

    个人信息无误,已知晓需自己支付邮费 谢谢

  • 2024-01-11
  • 回复了主题帖: 辞旧:年底清仓,100+板卡来袭,有缘来领

    申请板卡:37 四色板组合式开发平台 Kinetis MCU 套件 领取理由:四色板的概念其实比较早了,后面一直没有更新比较可惜,用的芯片型号其实也已经是时代的弃儿了,但是胜在模块化的结构,带有非常丰富的外设资源,支持其他芯片进行 DIY,之前也是一直没有机会使用,借此机会玩一下。     计划是简单了解下 Kinetis 芯片的资料,根据板卡的外设硬件,学习下,I2C、SPI、ADC、CAN等相关外设的驱动,后续看下如何 DIY 下适配到主流的芯片上,或者 ESP32 这个适合 DIY 的小模块上

  • 2023-12-17
  • 发表了主题帖: 【得捷 Follow Me 第3期】+ 任务总结帖

    本帖最后由 ID.LODA 于 2023-12-17 22:44 编辑 # 项目介绍 本次项目实现的是多功能家用温湿度计,涉及核心控制器是 Seeed Studio XIAO ESP32C3, 主要功能如下: 1. 连接 WIFI 定时同步网络时间至本地,并更新在外置 RTC 模块 BM8563 1. 连接 AHTX0 Sensor,获取室内温湿度值, 连接 Light Sensor 获取当前环境光强度 1. 连接 Round Display for XIAO,显示日期和传感器值 1. 连接 SD 定时保存时间和对应的传感器值 演示视频如下: [localvideo]4bff6f382f880d791bc199cd4625d067[/localvideo] ## 任务/项目总结报告 主控芯片 [Seeed Studio XIAO ESP32C3](https://wiki.seeedstudio.com/XIAO_ESP32C3_Getting_Started/) 是一款基于express的物联网迷你开发板 ESP32-C3 WiFi/蓝牙双模芯片ESP32-C3是一款32位RISC-V CPU,包含一个FPU(浮点单元),用于32位单精度运算,具有强大的计算能力。 它具有出色的射频性能,支持IEEE 802.11 b/g/n WiFi和蓝牙5 (LE)协议。该电路板附带一个外部天线,以增加无线应用程序的信号强度。 它还具有小巧精致的外形,并结合了单面表面贴装设计。 它配备了丰富的接口,具有11个数字I/O,可作为PWM引脚和4个模拟I/O,可作为ADC引脚。支持**UART、I2C、SPI等4种串行接口。 ![Seeed Studio XIAO ESP32C3](https://files.seeedstudio.com/wiki/XIAO_WiFi/pin_map-2.png) ### 任务1:使用MicroPython系统 按下 BOOT 按键不松手,上电模块之后松手即可进入下载模式 #### 使用esptool升级固件 1. 从 github 下载最新的 esptool 工具 ```cmd https://github.com/espressif/esptool.git ``` 1. 下载最新固件 ```cmd https://micropython.org/download/esp32c3/ ``` 1. 把最新的 firm 放到这个文件位置,用CMD打开这个文件 ```cmd your own file location\esptool-master\esptool ``` 1. 在CMD中输入此命令刷新固件(在刷新前进入bootloader模式) ```cmd esptool.exe --chip esp32c3 --port COM10 --baud 921600 --before default_reset --after hard_reset --no-stub  write_flash --flash_mode dio --flash_freq 80m 0x0 esp32c3-usb-20230426-v1.20.0.bin ``` ![Download](https://files.seeedstudio.com/wiki/wiki-ranger/Contributions/C3-MicroPy/C3-MicroPython2.png) #### 其他方式 参考 https://wiki.seeedstudio.com/cn/XIAO_ESP32C3_MicroPython/ 该篇文章即可 #### 任务代码 ```python from machine import Pin import time print("Local time: %s" % str(time.localtime())) counter = 0 while True:     counter += 1 ``` #### 任务效果 ### 任务2:驱动扩展板上的OLED屏幕(必做任务) 扩展板选择的是原厂的 Round Dispaly For XIAO,相比于 Expansion Board Base for XIAO 资源更加丰富,但是接口扩展会比较麻烦,具体资料在如下地址 https://wiki.seeedstudio.com/get_start_round_display/ ![Round Dispaly For XIAO](https://files.seeedstudio.com/wiki/round_display_for_xiao/round-pinout.png) 开发使用的是 andruino 方式,需要安装对应的图形驱动库,步骤相对繁琐,直接参考官网教程即可 https://wiki.seeedstudio.com/get_start_round_display/ #### 任务代码 ```c /* An example analogue clock using a TFT LCD screen to show the time use of some of the drawing commands with the library. For a more accurate clock, it would be better to use the RTClib library. But this is just a demo. This sketch uses font 4 only. Make sure all the display driver and pin connections are correct by editing the User_Setup.h file in the TFT_eSPI library folder. ######################################################################### ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ###### ######################################################################### Based on a sketch by Gilchrist 6/2/2014 1.0 */ #include #include // Hardware-specific library #include #include "panda.h" // Image is stored here in an 8 bit array #define TFT_GREY 0x5AEB #define MAX_IMAGE_WIDTH 240 // Adjust for your images TFT_eSPI tft = TFT_eSPI();       // Invoke custom library PNG png; // PNG decoder inatance int16_t xpos = 0; int16_t ypos = 0; void setup(void) {      Serial.begin(115200);   // Initialise the TFT   tft.init();   tft.setRotation(0);   // tft.fillScreen(TFT_BLACK);   // tft.fillScreen(TFT_RED);   // tft.fillScreen(TFT_GREEN);   // tft.fillScreen(TFT_BLUE);   tft.fillScreen(TFT_BLACK);   // tft.fillScreen(TFT_GREY);   // tft.setTextColor(TFT_WHITE, TFT_GREY);  // Adding a background colour erases previous text automatically   tft.setTextColor(TFT_RED);      Serial.println("\r\nInitialisation done."); } void loop() {   int16_t rc = png.openFLASH((uint8_t *)panda, sizeof(panda), pngDraw);   if (rc == PNG_SUCCESS) {     Serial.println("Successfully opened png file");     Serial.printf("image specs: (%d x %d), %d bpp, pixel type: %d\n", png.getWidth(), png.getHeight(), png.getBpp(), png.getPixelType());     tft.startWrite();     uint32_t dt = millis();     rc = png.decode(NULL, 0);     Serial.print(millis() - dt); Serial.println("ms");     tft.endWrite();     // png.close(); // not needed for memory->memory decode   }   delay(1000);   tft.fillScreen(random(0x10000)); } //=========================================v========================================== //                                      pngDraw //==================================================================================== // This next function will be called during decoding of the png file to // render each image line to the TFT.  If you use a different TFT library // you will need to adapt this function to suit. // Callback function to draw pixels to the display void pngDraw(PNGDRAW *pDraw) {   uint16_t lineBuffer[MAX_IMAGE_WIDTH];   png.getLineAsRGB565(pDraw, lineBuffer, PNG_RGB565_BIG_ENDIAN, 0xffffffff);   tft.pushImage(xpos, ypos + pDraw->y, pDraw->iWidth, 1, lineBuffer); } ``` #### 任务效果 ### 任务3:控制蜂鸣器播放音乐 该任务用到了 [Grove - Buzzer](https://wiki.seeedstudio.com/Grove-Buzzer/) 模块,通过 DI 控制蜂鸣器的开关 根据音符与频率对照表,定义定所需的音符频率,参考 arduino 官方示例 https://docs.arduino.cc/built-in-examples/digital/toneMelody [音符与频率对照表](https://bbs.eeworld.com.cn/data/attachment/forum/202312/15/154956s1qp3p1z0wpqcoq7.png) #### 任务代码 ```c /* Play a melody */ #include "pitches.h" #define BUZZER_PIN    D2 // 记录曲子的音符 int melody[] = { 0, 0, NOTE_E5, NOTE_D5, NOTE_E5, NOTE_D5, NOTE_E5, NOTE_B4, NOTE_D5, NOTE_C5,                  NOTE_A4, 0, NOTE_C4, NOTE_E4, NOTE_A4, NOTE_B4, 0, NOTE_E4, NOTE_G4, NOTE_B4,                  NOTE_C5, 0, NOTE_E4, NOTE_E5, NOTE_D5, NOTE_E5, NOTE_D5, NOTE_E5, NOTE_B4, NOTE_D5, NOTE_C5,                  NOTE_A4, 0, NOTE_C4, NOTE_E4, NOTE_A4, NOTE_B4, 0, NOTE_E4, NOTE_C5, NOTE_B4,                  NOTE_A4                }; // 音符持续时间:4为四分音符,8为八分音符 int noteDurations[] = { 4, 4, 8, 8, 8, 8, 8, 8, 8, 8,                         4, 8, 8, 8, 8, 4, 8, 8, 8, 8,                         4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,                         4, 8, 8, 8, 8, 4, 8, 8, 8, 8,                         4, 4, 8, 8, 8, 8, 8, 8, 8, 8,                         4                       }; void setup() {   // iterate over the notes of the melody:   for(int thisNote = 0; thisNote < sizeof(melody)/sizeof(melody[0]); thisNote++) {     // to calculate the note duration, take one second divided by the note type.     //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.     int noteDuration = 1000/noteDurations[thisNote];     tone(BUZZER_PIN, melody[thisNote], noteDuration);     // to distinguish the notes, set a minimum time between them.     // the note's duration + 30% seems to work well:     int pauseBetweenNotes = noteDuration * 1.30;     delay(pauseBetweenNotes);     // stop the tone playing:     noTone(BUZZER_PIN);   }    } void loop() {    } ``` ```h /************************************************* * Public Constants *************************************************/ #define NOTE_B0  31 #define NOTE_C1  33 #define NOTE_CS1 35 #define NOTE_D1  37 #define NOTE_DS1 39 #define NOTE_E1  41 #define NOTE_F1  44 #define NOTE_FS1 46 #define NOTE_G1  49 #define NOTE_GS1 52 #define NOTE_A1  55 #define NOTE_AS1 58 #define NOTE_B1  62 #define NOTE_C2  65 #define NOTE_CS2 69 #define NOTE_D2  73 #define NOTE_DS2 78 #define NOTE_E2  82 #define NOTE_F2  87 #define NOTE_FS2 93 #define NOTE_G2  98 #define NOTE_GS2 104 #define NOTE_A2  110 #define NOTE_AS2 117 #define NOTE_B2  123 #define NOTE_C3  131 #define NOTE_CS3 139 #define NOTE_D3  147 #define NOTE_DS3 156 #define NOTE_E3  165 #define NOTE_F3  175 #define NOTE_FS3 185 #define NOTE_G3  196 #define NOTE_GS3 208 #define NOTE_A3  220 #define NOTE_AS3 233 #define NOTE_B3  247 #define NOTE_C4  262 #define NOTE_CS4 277 #define NOTE_D4  294 #define NOTE_DS4 311 #define NOTE_E4  330 #define NOTE_F4  349 #define NOTE_FS4 370 #define NOTE_G4  392 #define NOTE_GS4 415 #define NOTE_A4  440 #define NOTE_AS4 466 #define NOTE_B4  494 #define NOTE_C5  523 #define NOTE_CS5 554 #define NOTE_D5  587 #define NOTE_DS5 622 #define NOTE_E5  659 #define NOTE_F5  698 #define NOTE_FS5 740 #define NOTE_G5  784 #define NOTE_GS5 831 #define NOTE_A5  880 #define NOTE_AS5 932 #define NOTE_B5  988 #define NOTE_C6  1047 #define NOTE_CS6 1109 #define NOTE_D6  1175 #define NOTE_DS6 1245 #define NOTE_E6  1319 #define NOTE_F6  1397 #define NOTE_FS6 1480 #define NOTE_G6  1568 #define NOTE_GS6 1661 #define NOTE_A6  1760 #define NOTE_AS6 1865 #define NOTE_B6  1976 #define NOTE_C7  2093 #define NOTE_CS7 2217 #define NOTE_D7  2349 #define NOTE_DS7 2489 #define NOTE_E7  2637 #define NOTE_F7  2794 #define NOTE_FS7 2960 #define NOTE_G7  3136 #define NOTE_GS7 3322 #define NOTE_A7  3520 #define NOTE_AS7 3729 #define NOTE_B7  3951 #define NOTE_C8  4186 #define NOTE_CS8 4435 #define NOTE_D8  4699 #define NOTE_DS8 4978 ``` #### 任务效果 编译运行之后可以听到蜂鸣器在播放 ### 任务4:连接WiFi网络 该任务的实现用到了板载的 WIFI 功能以及 Round Display for XIAO 的外置 RTC 芯片 BM8563,采用 I2C 通讯 #### 任务代码 ```c #include "I2C_BM8563.h" #include #define TIME_ZONE   8 * 3600 I2C_BM8563 rtc(I2C_BM8563_DEFAULT_ADDRESS, Wire); const char* ntpServer = "time.cloudflare.com"; const char *ssid     = ""; // Set before Complie const char *password = ""; // Set before Complie void setup() {   // Init Serial   Serial.begin(115200);   delay(50);   // Connect to an access point   WiFi.begin(ssid, password);   Serial.print("Connecting to Wi-Fi ");   while (WiFi.status() != WL_CONNECTED) {     delay(500);     Serial.print(".");   }   Serial.println(" CONNECTED");   // Set ntp time to local   configTime(TIME_ZONE, 0, ntpServer);   // Init I2C   Wire.begin();   // Init RTC   rtc.begin();   // Get local time   struct tm timeInfo;   if (getLocalTime(&timeInfo)) {     // Set RTC time     I2C_BM8563_TimeTypeDef timeStruct;     timeStruct.hours   = timeInfo.tm_hour;     timeStruct.minutes = timeInfo.tm_min;     timeStruct.seconds = timeInfo.tm_sec;     rtc.setTime(&timeStruct);     // Set RTC Date     I2C_BM8563_DateTypeDef dateStruct;     dateStruct.weekDay = timeInfo.tm_wday;     dateStruct.month   = timeInfo.tm_mon + 1;     dateStruct.date    = timeInfo.tm_mday;     dateStruct.year    = timeInfo.tm_year + 1900;     rtc.setDate(&dateStruct);   } } void loop() {   I2C_BM8563_DateTypeDef dateStruct;   I2C_BM8563_TimeTypeDef timeStruct;   // Get RTC   rtc.getDate(&dateStruct);   rtc.getTime(&timeStruct);   // Print RTC #if defined(CONFIG_IDF_Td:\trunk\open-source\seeedstudio_xiao_esp32c3\arduino\chapter2\chapter02.inoARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)     Serial.printf("%04d/%02d/%02d %02d:%02d:%02d\n",                 dateStruct.year,                 dateStruct.month,                 dateStruct.date,                 timeStruct.hours,                 timeStruct.minutes,                 timeStruct.seconds                ); #else      Serial.print(dateStruct.year);      Serial.print(", ");      Serial.print(dateStruct.month);      Serial.print(", ");      Serial.print(dateStruct.date);      Serial.print(", ");      Serial.print(timeStruct.hours);      Serial.print(", ");      Serial.print(timeStruct.minutes);      Serial.print(", ");      Serial.print(timeStruct.seconds);      Serial.println(); #endif   // Wait   delay(1000); } ``` #### 任务效果 ### 任务5:使用外部传感器 该任务的实现用到了 [Adafruit AHT20](https://learn.adafruit.com/adafruit-aht20/overview)、[Grove - Light Sensor](https://wiki.seeedstudio.com/Grove-Light_Sensor/)、[Grove - Thumb Joystick](https://wiki.seeedstudio.com/Grove-Thumb_Joystick/#play-with-arduino) 三个传感器模块,前者使用的是 I2C 通讯,后两者均采用模拟量读取值 #### 任务代码 ```c /* An example analogue clock using a TFT LCD screen to show the time use of some of the drawing commands with the library. For a more accurate clock, it would be better to use the RTClib library. But this is just a demo. This sketch uses font 4 only. Make sure all the display driver and pin connections are correct by editing the User_Setup.h file in the TFT_eSPI library folder. ######################################################################### ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ###### ######################################################################### Based on a sketch by Gilchrist 6/2/2014 1.0 */ #include #include // Hardware-specific library #include "I2C_BM8563.h" #include Adafruit_AHTX0 aht; #define TFT_GREY 0x5AEB #define MAX_IMAGE_WIDTH 240 // Adjust for your images TFT_eSPI tft = TFT_eSPI();       // Invoke custom library unsigned long previousMillis = 0;  //will store last time LED was blinked const long period = 1000;         // period at which to blink in ms sensors_event_t humidity, temp; void setup(void) {      Serial.begin(115200);   // Initialise the TFT   tft.init();   tft.setRotation(0);   tft.fillScreen(TFT_BLACK);   // tft.fillScreen(TFT_GREY);   tft.setTextColor(TFT_WHITE, TFT_GREY);  // Adding a background colour erases previous text automatically   // tft.setTextColor(TFT_RED);   // Initialise the aht   if (!aht.begin()) {     Serial.println("Could not find AHT? Check wiring");     while (1) delay(10);   } else {     Serial.println("AHT10 or AHT20 found");   }      Serial.println("\r\nInitialisation done."); } void loop() {   unsigned long currentMillis = millis(); // store the current time   if (currentMillis - previousMillis >= period) { // check if 1000ms passed     previousMillis = currentMillis;   // save the last time you blinked the LED     aht.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data   }   char buffer[64];   sprintf(buffer, "Temperature: %.2f degrees C", temp.temperature);   Serial.println(buffer);   tft.drawString(buffer, 16, 64);   sprintf(buffer, "Humidity: %.2f% rH", humidity.relative_humidity);   Serial.println(buffer);   tft.drawString(buffer, 16, 76);   int value = analogRead(A0);   value = map(value, 0, 800, 0, 100);   sprintf(buffer, "Light Sensor: %d adv", value);   Serial.println(buffer);   tft.drawString(buffer, 16, 88);   int xValue = analogRead(A1);   int yValue = analogRead(A2);   sprintf(buffer, "The X and Y coordinate is: %d,%d", xValue, yValue);   Serial.println(buffer);   tft.drawString(buffer, 16, 100);   delay(200);   tft.fillScreen(random(0x10000)); } ``` #### 任务效果 ### 任务6:综合实践 - 分任务2:温湿度数据记录仪 #### 任务代码 ```c // Font file is stored on SD card #include "FS.h" #include "SD.h" // Graphics and font library #include #include #include "I2C_BM8563.h" #include #include #define TFT_GREY 0x5AEB #define MAX_IMAGE_WIDTH 240 // Adjust for your images #define TIME_ZONE   8 * 3600 #define SHOW_PERIOD_TIME    1000 #define SAVE_PERIOD_TIME    5000 #define NTP_SYNC_TIME       1000 * 60 * 60 Adafruit_AHTX0 aht; TFT_eSPI tft = TFT_eSPI();  // Invoke library I2C_BM8563 rtc(I2C_BM8563_DEFAULT_ADDRESS, Wire); const char *ssid     = "shanghai2014"; // Set before Complie const char *password = "zafu0828.samw"; // Set before Complie const char* ntpServer = "time.cloudflare.com"; I2C_BM8563_DateTypeDef dateStruct; I2C_BM8563_TimeTypeDef timeStruct; sensors_event_t humidity, temp; int illuminance = 0; int xPos = 0; int yPos = 0; unsigned long previousShowMillis = 0;  //will store last time unsigned long previousSaveMillis = 0;  //will store last time unsigned long previousSyncMillis = 0;  //will store last time // ------------------------------------------------------------------------- // Setup // ------------------------------------------------------------------------- void setup(void) {   Serial.begin(115200); // Used for messages   // Display initialization   tft.init();   pinMode(D2, OUTPUT);   // Initialise the SD library before the TFT so the chip select is defined   if (!SD.begin(D2)) {     Serial.println("Card Mount Failed 65");     return;   }   uint8_t cardType = SD.cardType();   if (cardType == CARD_NONE) {     Serial.println("No SD card attached");     return;   }   Serial.print("SD Card Type: ");   if (cardType == CARD_MMC) {     Serial.println("MMC");   } else if (cardType == CARD_SD) {     Serial.println("SDSC");   } else if (cardType == CARD_SDHC) {     Serial.println("SDHC");   } else {     Serial.println("UNKNOWN");   }   uint64_t cardSize = SD.cardSize() / (1024 * 1024);   Serial.printf("SD Card Size: %lluMB\n", cardSize);   // Initialise the TFT after the SD card!   tft.init();   tft.setRotation(0);   tft.fillScreen(TFT_BLACK);   // listDir(SD, "/", 0);   Serial.println("SD and TFT initialisation done.");   if (!aht.begin()) {     Serial.println("Could not find AHT? Check wiring");     while (1) delay(10);   } else {     Serial.println("AHT10 or AHT20 found");   }      // Connect to an access point   WiFi.begin(ssid, password);   Serial.print("Connecting to Wi-Fi ");   while (WiFi.status() != WL_CONNECTED) {     delay(500);     Serial.print(".");   }   Serial.println(" CONNECTED");   // Set ntp time to local   configTime(TIME_ZONE, 0, ntpServer);   // Init I2C   Wire.begin();   // Init RTC   rtc.begin();   Serial.println("Initialisation done.");   // Wrap test at right and bottom of screen   // tft.setTextWrap(true, true);   // Name of font file (library adds leading / and .vlw)   String fileName = "Final-Frontier-28";   // Font and background colour, background colour is used for anti-alias blending   // tft.setTextColor(TFT_BLUE, TFT_BLACK);   // Load the font   tft.loadFont(fileName, SD);       // Use font stored on SD   // Display all characters of the font   // tft.showFont(2000);   // If the data.txt file doesn't exist   // Create a file on the SD card and write the data labels   File file = SD.open("/data.txt");   if(!file) {     Serial.println("File doens't exist");     Serial.println("Creating file...");     writeFile(SD, "/data.txt", "Reading Time, Temperature, Humidity, Illuminance\r\n");   }   else {     Serial.println("File already exists");     }   file.close(); } // ------------------------------------------------------------------------- // Main loop // ------------------------------------------------------------------------- void loop() {   unsigned long currentMillis = millis(); // store the current time   if ((currentMillis - previousShowMillis) > SHOW_PERIOD_TIME) { // check if 1000ms passed     previousShowMillis = currentMillis;   // save the last time     updateTime();     updateSensor();     updateShow();   }   if ((currentMillis - previousSyncMillis) > SHOW_PERIOD_TIME) {     previousSyncMillis = currentMillis;   // save the last time     syncNTP();   }   if ((currentMillis - previousSaveMillis) > SAVE_PERIOD_TIME) {     previousSaveMillis = currentMillis;   // save the last time     logSDCard();   }   delay(200); } void syncNTP(void) {   // Get local time   struct tm timeInfo;   if (getLocalTime(&timeInfo)) {     // Set RTC time     I2C_BM8563_TimeTypeDef timeStruct;     timeStruct.hours   = timeInfo.tm_hour;     timeStruct.minutes = timeInfo.tm_min;     timeStruct.seconds = timeInfo.tm_sec;     rtc.setTime(&timeStruct);     // Set RTC Date     I2C_BM8563_DateTypeDef dateStruct;     dateStruct.weekDay = timeInfo.tm_wday;     dateStruct.month   = timeInfo.tm_mon + 1;     dateStruct.date    = timeInfo.tm_mday;     dateStruct.year    = timeInfo.tm_year + 1900;     rtc.setDate(&dateStruct);   } } void updateTime(void) {   // Get RTC   rtc.getDate(&dateStruct);   rtc.getTime(&timeStruct);   // Print RTC   Serial.printf("%04d/%02d/%02d %02d:%02d:%02d\n",                 dateStruct.year,                 dateStruct.month,                 dateStruct.date,                 timeStruct.hours,                 timeStruct.minutes,                 timeStruct.seconds                ); } void updateSensor(void) {   aht.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data   Serial.print("Temperature: "); Serial.print(temp.temperature); Serial.println(" degrees C");   Serial.print("Humidity: "); Serial.print(humidity.relative_humidity); Serial.println("% rH");   illuminance = analogRead(A0);   illuminance = map(illuminance, 0, 800, 0, 100);   Serial.print("Illuminance: "); Serial.print(illuminance); Serial.println(" lux");   // xPos = analogRead(A1);   // yPos = analogRead(A2); } void updateShow() {   // delay(200);   // tft.fillScreen(TFT_WHITE);   tft.fillRect(16, 60, 224, 84, TFT_BLACK);   tft.fillRect(50, 144, 140, 56, TFT_BLACK);   tft.setTextColor(TFT_BLUE, TFT_BLACK);   tft.drawString("temp:", 16, 60); tft.drawFloat(temp.temperature, 2, 112, 60);   tft.drawString("humi:", 16, 88); tft.drawFloat(humidity.relative_humidity, 2, 112, 88);   tft.setTextColor(TFT_GREEN, TFT_BLACK);   tft.drawString("illu:", 16, 116); tft.drawNumber(illuminance, 112, 116);   tft.setTextColor(TFT_WHITE, TFT_BLACK);   char dataMessage[128];   sprintf(dataMessage, "%04d/%02d/%02d", dateStruct.year, dateStruct.month, dateStruct.date);   tft.drawString(dataMessage, 50, 144);   sprintf(dataMessage, "%02d:%02d:%02d", timeStruct.hours, timeStruct.minutes, timeStruct.seconds);   tft.drawString(dataMessage, 50, 172); } // Write the sensor readings on the SD card void logSDCard() {   char dataMessage[256];   sprintf(dataMessage, "%04d/%02d/%02d %02d:%02d:%02d temp:%.2f humi:%.2f lux:%d\r\n",                 dateStruct.year, dateStruct.month, dateStruct.date,                 timeStruct.hours, timeStruct.minutes, timeStruct.seconds,                 temp.temperature, humidity.relative_humidity, illuminance);   Serial.print("Save data: ");   Serial.print(dataMessage);   appendFile(SD, "/data.txt", dataMessage); } // Write to the SD card (DON'T MODIFY THIS FUNCTION) void writeFile(fs::FS &fs, const char * path, const char * message) {   Serial.printf("Writing file: %s\n", path);   File file = fs.open(path, FILE_WRITE);   if(!file) {     Serial.println("Failed to open file for writing");     return;   }   if(file.print(message)) {     Serial.println("File written");   } else {     Serial.println("Write failed");   }   file.close(); } // Append data to the SD card (DON'T MODIFY THIS FUNCTION) void appendFile(fs::FS &fs, const char * path, const char * message) {   Serial.printf("Appending to file: %s\n", path);   File file = fs.open(path, FILE_APPEND);   if(!file) {     Serial.println("Failed to open file for appending");     return;   }   if(file.print(message)) {     Serial.println("Message appended");   } else {     Serial.println("Append failed");   }   file.close(); } ``` #### 任务效果 运行日志如下,效果如开头视频所见。 ```bash 21:22:24.135 -> ESP-ROM:esp32c3-api1-20210207 21:22:24.771 -> SD Card Type: SDHC 21:22:24.772 -> SD Card Size: 30436MB 21:22:24.772 -> SD and TFT initialisation done. 21:22:24.772 -> AHT10 or AHT20 found 21:22:24.772 -> Connecting to Wi-Fi .. CONNECTED 21:22:24.772 -> Initialisation done. 21:22:24.772 -> File already exists 21:22:24.772 -> 2023/12/17 21:22:24 21:22:24.772 -> Temperature: 15.56 degrees C 21:22:24.772 -> Humidity: 41.90% rH 21:22:24.772 -> Illuminance2023/12/17 21:22:26 21:22:24.807 -> Temperature: 15.55 degrees C 21:22:24.807 -> Humidity: 41.66% rH 21:22:24.807 -> Illuminance: 35 lux 21:22:25.806 -> 2023/12/17 21:22:27 21:22:25.845 -> Temperature: 15.56 degrees C 21:22:25.845 -> Humidity: 41.38% rH 21:22:25.845 -> Illuminance: 19 lux 21:22:26.608 -> Save data: 2023/12/17 21:22:27 temp:15.56 humi:41.38 lux:19 21:22:26.608 -> Appending to file: /data.txt 21:22:26.608 -> Message appended ``` ## 可编译下载的代码 示例代码如下,将 libraris 内容拷贝至 Arduino IDE 的库目录,或手动安装库。遇到使用 WIFI 的章节,需要填入对应的 SSID 和 Password。 ## 心得体会 首先非常感谢 EEWorld 和 Digikey 提供这么好的活动机会,让我有机会接触使用新的板卡,学习新的知识。本次任务的时间相比之前感觉更紧凑些,我选择了更有意思的显示模块,也更费时间去研究,但最终显示效果还是比较差,比较遗憾。 最后的最后,再次感谢社区,感谢得捷!

  • 2023-11-22
  • 回复了主题帖: 【得捷电子Follow me第2期】+ 多功能时钟(补充贴)

    chejm 发表于 2023-11-18 21:24 楼主辛苦了,感谢楼主提供的这么好技术分享,顶起来 感谢支持

  • 2023-11-14
  • 发表了主题帖: 【得捷电子Follow me第2期】+ 多功能时钟(补充贴)

    # 项目介绍 项目的完整介绍可以看之前的[提交贴](https://bbs.eeworld.com.cn/thread-1259461-1-1.html) 本章只对新增功能的内容进行了描述补充 ## 补充说明 ### SDCard 模块 主要增加了 SDCard 模块的使用,将字库文件存储在 sd 卡,减少内部 flash 的使用, 需要导入 adafruit_sdcard 库,示例参考如下: ```python import board import busio import digitalio import storage import adafruit_sdcard spi = busio.SPI(board.D12, MOSI=board.D11, MISO=board.D13) cs = digitalio.DigitalInOut(board.D10) sdcard = adafruit_sdcard.SDCard(spi, cs) vfs = storage.VfsFat(sdcard) storage.mount(vfs, "/sd") with open("/sd/test.txt", "w") as f:     f.write("Hello world!\r\n") with open("/sd/test.txt", "r") as f:     print("Read line from file:")     print(f.readline()) ``` ### LED Ring 模块 LED Ring 的使用其实和板载的 rgb 灯是一样的,只是增加了灯是数量,示例可以参考官方说明 https://learn.adafruit.com/adafruit-esp32-s3-tft-feather/multitasking-with-asyncio ### 完整的演示代码 需要设置自己连接的 WIFI 账户密码,以及和风天气的密钥 ```python import time import board import busio import digitalio import displayio import rtc import wifi import storage import ssl import socketpool import adafruit_requests import zlib import json import adafruit_ntp from adafruit_display_text import label from adafruit_bitmap_font import bitmap_font import adafruit_imageload import storage import adafruit_sdcard import weather_ico import neopixel from rainbowio import colorwheel import random # import asyncio # import keypad # import dfrobot_dfplayer_mini SSID = "" # NEED TO SET PASSWORD = "" NEED TO SET UTC_OFFSET = 8 QWEATHER_KEY = "" NEED TO SET CITYID = '101020100' WEATHER_ICO_SIZE = 64 # DISPLAY_WIDTH = 240 # DISPLAY_HEIGHT = 125 WEEK = ['一', '二', '三', '四', '五', '六', '日'] BUTTON_CLICK_NO = 0 BUTTON_CLICK_SIGNEL = 1 BUTTON_CLICK_DOUBLE = 2 BUTTON_CLICK_LONG = 3 last_state = False click_count = 0 last_click_time = 0 last_release_time = 0 button_status = 0 music_status = False spi = busio.SPI(board.D12, MOSI=board.D11, MISO=board.D13) cs = digitalio.DigitalInOut(board.D10) sdcard = adafruit_sdcard.SDCard(spi, cs) vfs = storage.VfsFat(sdcard) storage.mount(vfs, "/sd") # led = digitalio.DigitalInOut(board.LED) # led.direction = digitalio.Direction.OUTPUT button = digitalio.DigitalInOut(board.BUTTON) button.switch_to_input(pull=digitalio.Pull.UP) # pixel = neopixel.NeoPixel(board.NEOPIXEL, 1) # pixel.brightness = 0.3 num_pixels = 12  # The number of NeoPixels on a single ring. brightness = 0.2  # The LED brightness. # Set up NeoPixel rings. ring_one = neopixel.NeoPixel(board.A1, num_pixels, brightness=brightness, auto_write=False) # uart = busio.UART(board.TX, board.RX, baudrate=9600) display = board.DISPLAY # Create a Group main_group = displayio.Group() # Create a bitmap with two colors bitmap = displayio.Bitmap(display.width, display.height, 1) # Create a two color palette palette = displayio.Palette(2) palette[0] = 0x000000#0xb5b5b5 palette[1] = 0xFFFFFF tile_grid = displayio.TileGrid(bitmap, pixel_shader=palette) # Set text, font, and color font = bitmap_font.load_font("/sd/res/font/opposans_m_12.pcf") # Create the text label lable_wifi_area = label.Label(     font, x=60, y=12, text="", scale=1, color=(0, 0, 255) ) lable_music_area = label.Label(     font, x=142, y=12, text="", scale=1, color=(0, 0, 255) ) lable_time_area = label.Label(     font, x=0, y=118, text="", scale=1, color=(255, 69, 0) ) main_group.append(tile_grid) main_group.append(lable_wifi_area) main_group.append(lable_music_area) main_group.append(lable_time_area) # logo group_logo = displayio.Group(x=0, y=0) image_logo, palette_logo = adafruit_imageload.load(     "/res/index_mainlogo.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette ) # tile_grid_logo = displayio.TileGrid(image_logo, pixel_shader=palette_logo, #                                     tile_width=172, tile_height=60) # adafruit tile_grid_logo = displayio.TileGrid(image_logo, pixel_shader=palette_logo,                                     tile_width=60, tile_height=30) # eeworld group_logo.append(tile_grid_logo) main_group.append(group_logo) # weather group_weather= displayio.Group(x=0, y=20) bitmap_weather = displayio.Bitmap(WEATHER_ICO_SIZE, WEATHER_ICO_SIZE, 2) tile_grid_weather = displayio.TileGrid(bitmap_weather, pixel_shader=palette, x=160, y=12) label_weather= label.Label(font, x=6, y=20, text="", scale=1, color=(154, 255, 154)) group_weather.append(label_weather) group_weather.append(tile_grid_weather) main_group.append(group_weather) def ntp_sync_time():     print("Local time before synchronization:%s" % str(time.localtime()))     try:         ntp = adafruit_ntp.NTP(pool, tz_offset=UTC_OFFSET)         # ntp = adafruit_ntp.NTP(pool, server='ntp1.aliyun.com', tz_offset=UTC_OFFSET)         rtc.RTC().datetime = ntp.datetime         print("Local time after synchronization: %s" % str(time.localtime()))     except OSError as e:         print("ntp error:{}".format(e))     # NOTE: This changes the system time so make sure you aren't assuming that time     # doesn't jump. def qweather_decompress(data):     FTEXT = 1     FHCRC = 2     FEXTRA = 4     FNAME = 8     FCOMMENT = 16     assert data[0] == 0x1f and data[1] == 0x8b     assert data[2] == 8     flg = data[3]     assert flg & 0xe0 == 0     i = 10     if flg & FEXTRA:         i += data[11] > (7 - (x % 8))) & 1             # pixel_value = (weather_ico.ico['100'][index] >> (x % 8)) & 1             # set Bitmap point             bitmap_weather[x, y] = pixel_value def update_pixel_led(status: bool=True):     if status:         # # pixel[0] = colorwheel(random.randint(0, 255))         # pixel[0] = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))         for j in range(255, -1, -1):             for i in range(num_pixels):                 rc_index = (i * 256 // num_pixels) + j                 ring_one = colorwheel(rc_index & 255)             ring_one.show()     else:         # pixel[0] = (0, 0, 0)#colorwheel(255)         for i in range(num_pixels):             ring_one = (0, 0, 0)         ring_one.show() # def dfr_player_mini_setup(player: dfrobot_dfplayer_mini.dfplayer_mini): #     print("DFRobot DFPlayer Mini Demo") #     print("Initializing DFPlayer ... (May take 3~5 seconds)") #     # Use serial to communicate with mp3. #     if player.begin() != True: #         print("Unable to begin:") #         print("    1.Please recheck the connection!") #         print("    2.Please insert the SD card!") #         return False #     print("DFPlayer Mini online.") #     # Set volume value. From 0 to 30 #     player.volume(10) #     # Play the first mp3 #     # player.play(1) #     return True # # def update_music(player, status: bool=True): #     if status: #         player.start() #     else: #         player.stop() def button_handle():     global last_state     global click_count     global last_click_time     global last_release_time     global button_status     current_state = button.value     # button pressed     if (last_state != current_state) and (current_state == False):         button_status = 1         last_click_time = time.monotonic()         print("pressed: {}".format(last_click_time))     elif current_state == True:         # pressed before         if button_status == 1:             # calculate the time pressed             last_release_time = time.monotonic()             press_duration = last_release_time - last_click_time             if press_duration >= 0.05 and press_duration 1.5:                 print("long press")                 button_status = 0                 click_count = 0                 return BUTTON_CLICK_LONG             # print("released: {}".format(press_duration))         elif button_status == 2:             press_duration = time.monotonic() - last_release_time             if press_duration >= 0.2:                 button_status = 0                 if click_count > 1:                     click_count = 0                     print("double press")                     return BUTTON_CLICK_DOUBLE                 else:                     click_count = 0                     print("signal press")                     return BUTTON_CLICK_SIGNEL     last_state = current_state     return BUTTON_CLICK_NO # Show it display.show(main_group) # lable_wifi_area.text = "系统初始化" # player = dfrobot_dfplayer_mini.dfplayer_mini(uart) # if dfr_player_mini_setup(player): #     lable_music_area.text = "播放器已连接" # else: #     lable_music_area.text = "播放器未连接" lable_wifi_area.text = "wifi连接中" # code handler while not wifi.radio.ipv4_address:     try:         wifi.radio.connect(SSID, PASSWORD)     except ConnectionError as e:         print("connect error:{}, retry in 2s".format(e))     time.sleep(2) print("get ip:", wifi.radio.ipv4_address) lable_wifi_area.text = "wifi 已连接" pool = socketpool.SocketPool(wifi.radio) requests = adafruit_requests.Session(pool, ssl.create_default_context()) ntp_sync_time() weather_info = qweather_weather_now() display_update_time() display_update_weather(weather_info) time_start = time.monotonic() # Loop forever so you can enjoy your image while True:     time_diff = time.monotonic() - time_start     if time_diff > 0.6:         display_update_time()     # sync ntp and weather     if time_diff >= 6 * 60 * 60:         time_start = time.monotonic()         ntp_sync_time()         weather_info = qweather_weather_now()         display_update_weather(weather_info)     # button handle     button_event = button_handle()     if button_event == BUTTON_CLICK_SIGNEL:         update_pixel_led()     # elif button_event == BUTTON_CLICK_DOUBLE:     #     if music_status:     #         music_status = False     #     else:     #         music_status = True     #     update_music(player, music_status)     elif button_event == BUTTON_CLICK_LONG:         update_pixel_led(False) ``` 演示视频如下: [localvideo]1a0c46d7693c9985e4fb3a0abf25a600[/localvideo] 完整的代码

统计信息

已有132人来访过

  • 芯积分:785
  • 好友:2
  • 主题:45
  • 回复:71

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言