本帖最后由 HonestQiao 于 2023-9-7 10:18 编辑
在官方的 DFROBOT FireBeetle 2 ESP32-S3 Board 进阶教程 中,提供了ESP-NOW数据传输 的教程。ESP-NOW是乐鑫开发的终端数据传输、无连接的快速通讯技术,适用于智能灯、遥控控制、传感器数据回传等场景。
通过ESP-NOW,多个ESP32设备之间,可以进行直接进项相互连接,而不依赖于WiFi等其他外部设备。
一、概要
这篇分享,选用了以下的3款有代表性的ESP32开发板,组成一个自组网灯控系统:
上图中的三块开办板分别为:
要实现这个自组网灯控系统,需要完成的基本功能如下:
- 设备可以自动发现
- 设备自动发现以后,可以自动连接通信
- 以上功能不依赖于其他设备;当然,开发的时候还是需要。
在这篇分享里面,设备自动发现,使用了蓝牙来实现。
- 当从设备启动后,会自动设置自身的蓝牙广播名称,短名称为LIGHT_GROUP_????,最后的????,根据设备自身蓝牙硬件地址前4位来设置,长名称为LIGHT_GROUP_????,最后的????,根据设备自身WiFi硬件地址来设置
- 当主设备启动后,会自动扫描周边的蓝牙设备,发现LIGHT_GROUP_开头的设备,就会自动连接上去套近乎。
- 从设备收到主设备的连接信息后,就自动停止蓝牙广播,并开启ESP-NOW接受,等待主设备发送控制指令。
ESP-NOW相互之间发送数据,需要发送方知道接收方的WiFi硬件地址地址,而通过上述的蓝牙扫描过程,能够自动获得从设备的WiFi硬件地址,从而供ESP-NOW通信使用。
如果是近距离的自组网,可以直接使用蓝牙。但蓝牙毕竟通信距离有限,ESP-NOW在没有阻碍环境中,轻松达到100米以上的通信距离,使用场景会扩展很多。
二、实现环境
这篇分享的自组网灯控系统,基于circuitpython环境来完成。只要是安装circuitpython的ESP32,都可以接入这个自组网灯控系统。
circuitpython的官方为:CircuitPython,它是micropython的一个分支。
要使用蓝牙功能,还需要安装adafruit_ble模块,可以从 Libraries (circuitpython.org) 下载了,解压其中的 adafruit_ble 模块文件放到开发板的 /lib/ 目录中。
如果是要驱动板载WS2812B,则还需要 neopixel 模块,同样从上述 Libraries 中解压了放到开发板上。
三、实现代码
主控 DFROBOT FireBeetle 2 ESP32-S3 Board 代码:espnow_light_group_master.py:
-
- from adafruit_ble import BLERadio
-
- from adafruit_ble.advertising import Advertisement
- from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
- import binascii
- import time
- import espnow
- import board
- import digitalio
-
- ble = BLERadio()
- print("scanning")
- found = set()
- scan_responses = set()
- peers = set()
-
- scan_times = 0
- while True:
- scan_times += 1
- if scan_times>3:
- break
- print("scan_times: %d" % scan_times)
- for advertisement in ble.start_scan(ProvideServicesAdvertisement, Advertisement, timeout=3):
- # 检查scan_response是否以LIGHT_GROUP开头
- if not advertisement.complete_name:
- continue
- elif not advertisement.complete_name.startswith("LIGHT_GROUP"):
- continue
-
- #dir(advertisement.complete_name)
- #print(advertisement.complete_name)
- #print(advertisement.complete_name.startswith("LIGHT_GROUP"))
- # break
- # 检查scan_responses 或者 addr 是否已经扫描过
- addr = advertisement.address
- if advertisement.scan_response and addr not in scan_responses:
- scan_responses.add(addr)
- elif not advertisement.scan_response and addr not in found:
- found.add(addr)
- else:
- continue
-
- # 输出相关信息
- dir(advertisement)
- print(addr, advertisement, advertisement.scan_response)
- print("\t" + repr(advertisement))
- print("\t" + repr(advertisement.scan_response))
- print()
-
- # 连接然后断开
- conn = ble.connect(advertisement)
- while not conn.connected:
- pass
- conn.disconnect()
-
- peers.add(binascii.unhexlify(advertisement.complete_name[12:]))
-
- time.sleep(1)
-
- print("scan done")
-
- time.sleep(1)
-
- led = digitalio.DigitalInOut(board.GPIO21)
- led.direction = digitalio.Direction.OUTPUT
-
- e = espnow.ESPNow()
- for peer_mac in peers:
- print("peer_mac add:", [hex(i) for i in peer_mac], ["%02X" % i for i in peer_mac], peer_mac)
- peer = espnow.Peer(mac=peer_mac)
- e.peers.append(peer)
-
- count = 0
- while True:
- print("espnow send: %d" % count)
- e.send("data: %d" % count)
- if count%2:
- led.value = True
- else:
- led.value = False
- time.sleep(1)
- count+=1
-
-
复制代码
上述代码中,核心就是通过 ble.start_scan(ProvideServicesAdvertisement, Advertisement, timeout=3) 扫描蓝牙设备,然后检查 返回蓝牙设备信息中的complete_name,是不是以LIGHT_GROUP开头,如果是的,就会记录下来对应的信息,表示用于通信的设备。
扫描完成后,就开始通过ESP-NOW发送数据【data: 发送次数】
从设备会根据发送次数,来确定点灯闪烁的顺序,或者WS2812B颜色切换的顺序。
DFROBOT FireBeetle 2 ESP32-S3 Board开发板上的板载LED,连接到了GPIO21:
所以上述代码中,这个LED作为领头的灯,其他从设备跟着它起舞。
从设备1代码:espnow_light_group_receiver.py【板载普通LED设备】
-
- from adafruit_ble import BLERadio
- from adafruit_ble.advertising import Advertisement
- import wifi
- import time
- import espnow
- import board
- import digitalio
-
- ble = BLERadio()
-
- mac_addr = wifi.radio.mac_address
- ble_addr = ble.address_bytes
-
- print("mac_addr: ", [hex(i) for i in mac_addr], ["%02X" % i for i in mac_addr])
- print("ble_addr:", [hex(i) for i in ble_addr], ["%02X" % i for i in ble_addr])
-
- ble_name = "LIGHT_GROUP_%s" % "".join(["%02X" % i for i in ble_addr[0:3]])
- ble_name_full = "LIGHT_GROUP_%s" % "".join(["%02X" % i for i in mac_addr[0:]])
- scan_response = b""
-
- print(ble_name)
- print(repr(scan_response))
-
- advertisement = Advertisement()
- advertisement.complete_name = ble_name_full
- advertisement.name = ble_name
- #advertisement.short_name = ble_name
- advertisement.connectable = True
-
-
- while True:
- print(advertisement, scan_response)
- ble.start_advertising(advertisement, scan_response=scan_response)
- if True:
- while not ble.connected:
- pass
- print("connected")
- while ble.connected:
- pass
- print("disconnected")
- ble.stop_advertising()
- break
-
- LED_AB = hasattr(board, 'LEDA') and hasattr(board, 'LEDB')
-
- if LED_AB:
- leda = digitalio.DigitalInOut(board.LEDA)
- leda.direction = digitalio.Direction.OUTPUT
-
- ledb = digitalio.DigitalInOut(board.LEDB)
- ledb.direction = digitalio.Direction.OUTPUT
- else:
- led = digitalio.DigitalInOut(board.LED)
- led.direction = digitalio.Direction.OUTPUT
-
- e = espnow.ESPNow()
- while True:
- if e:
- packet = e.read()
- print(packet)
-
- data = packet.msg.decode()
- if data.startswith("data: "):
- val = int(data[6:])
- if val %2 :
- if LED_AB:
- leda.value = True
- ledb.value = False
- else:
- led.value = True
- else:
- if LED_AB:
- leda.value = False
- ledb.value = True
- else:
- led = False
- else:
- pass
-
-
-
-
-
复制代码
在上述代码中,首先开启自身蓝牙广播,然后等待蓝牙连接。
一旦有蓝牙连接,就停止蓝牙广播,转入ESP-NOW接收状态。
CORE-ESP32S3开发板有两个LED,所以上述代码中,会自动根据LED的情况进行设置。
如果是单LED的设备,就跟主设备一样,亮灭亮灭闪烁。
如果是有LEDA、LEDB,则会交替闪烁。
因此,上述代码,不只是用于CORE-ESP32S3开发板,其他型号的也可以,通用的。
从设备2代码:espnow_light_group_receiver_ws2812b.py【板载WS2812B设备】
-
- from adafruit_ble import BLERadio
- from adafruit_ble.advertising import Advertisement
- import wifi
- import time
- import espnow
- import board
- import digitalio
- import neopixel
-
- ble = BLERadio()
-
- mac_addr = wifi.radio.mac_address
- ble_addr = ble.address_bytes
-
- print("mac_addr: ", [hex(i) for i in mac_addr], ["%02X" % i for i in mac_addr])
- print("ble_addr:", [hex(i) for i in ble_addr], ["%02X" % i for i in ble_addr])
-
- ble_name = "LIGHT_GROUP_%s" % "".join(["%02X" % i for i in ble_addr[0:3]])
- ble_name_full = "LIGHT_GROUP_%s" % "".join(["%02X" % i for i in mac_addr[0:]])
- scan_response = b""
-
- print(ble_name)
- print(repr(scan_response))
-
- advertisement = Advertisement()
- advertisement.complete_name = ble_name_full
- advertisement.name = ble_name
- #advertisement.short_name = ble_name
- advertisement.connectable = True
-
-
- while True:
- print(advertisement, scan_response)
- ble.start_advertising(advertisement, scan_response=scan_response)
- if True:
- while not ble.connected:
- pass
- print("connected")
- while ble.connected:
- pass
- print("disconnected")
- ble.stop_advertising()
- break
-
- # WS2812B设置
- pixel = neopixel.NeoPixel(board.NEOPIXEL, 1)
- # 亮度
- pixel.brightness = 0.5
-
- colors = [
- "red", (255, 0, 0),
- "green", (0, 255, 0),
- "blue", (0, 0, 255),
- "cyan", (0, 255, 255),
- "purple", (255, 0, 255),
- "yellow", (255, 255, 0),
- "white", (255, 255, 255),
- "black", (0, 0, 0),
- ]
-
- color_num = int(len(colors)/2) - 1
- black_index = int(len(colors)/2)-1
- color_index = 0
-
- e = espnow.ESPNow()
- while True:
- if e:
- packet = e.read()
- print(packet)
-
- data = packet.msg.decode()
- if data.startswith("data: "):
- val = int(data[6:])
- run_index = 0
- if val % 2 == 1:
- run_index = color_index*2
- color_index += 1
- if color_index>=color_num:
- color_index = 0
- else:
- run_index = black_index*2
-
- print("colors[%d] is %s" % (run_index, colors[run_index]), colors[run_index+1])
- pixel.fill(colors[run_index+1])
- else:
- pass
-
-
-
-
-
复制代码
上述代码的逻辑,和板载普通LED设备类似,就是LED控制改成了WS2812B控制,自动切换颜色。注意,驱动WS2812B需要neopixel lib文件。
在上述三部分代码中,灯控的部分,只是做了简单的处理,以便演示。
在实际使用中,可以根据实际需要,进行具体逻辑的设计。
将上述代码文件,以及需要的lib文件,到开发板上,就可以准备运行了。
四、运行效果:
为了观察运行状态,使用了串口工具连接三个开发板进行控制。
实际调试完成后,可以放到circuitpython的启动脚本中,开机自动运行。
上图中,最上面的为主设备,下面的为从设备。
DFROBOT FireBeetle 2 ESP32-S3 Board的固件,用了类似的YD-ESP32-S3的,可以通用,有空了我再定制一个。
然后,先依次运行从设备的代码:
可以看到,输出了蓝牙和wifi设备硬件地址,以及对应的蓝牙广播名称。
然后再运行主设备的代码:
运行后,自动扫描到了从设备,并进行连接。
扫描完毕后,开始ESP-NPW数据发送。
从设备上面,也输出了连接和连接关闭的日志,以及接收到ESP-NOW数据的信息。
然后,我们可以看三个开发板上的板载LED,已经同步起舞了:
五、总结
这篇分享,只是对蓝牙和ESP-NOW的一点小小应用。
在自组网灯控系统的代码中,直接暴露了WiFi硬件地址,实际使用中,可以通过蓝牙scan_responses信息,来返回经过加密的WiFi硬件地址信息,提高安全性。
前面也说过,灯控的部分,只是做了简单的处理,以便演示。在实际使用中,可以根据实际需要,进行具体逻辑的设计。
另外,在实际使用时,可以使用锂电池供电,从而可以方便进行现场具体环境的布置,达到刚好的效果。
|