FireBeetle 2 ESP32-S3:基于蓝牙和ESP-NOW的自组网灯控系统
本帖最后由 HonestQiao 于 2023-9-7 10:18 编辑在官方的 DFROBOT FireBeetle 2 ESP32-S3 Board进阶教程 中,提供了ESP-NOW数据传输 的教程。ESP-NOW是乐鑫开发的终端数据传输、无连接的快速通讯技术,适用于智能灯、遥控控制、传感器数据回传等场景。
通过ESP-NOW,多个ESP32设备之间,可以进行直接进项相互连接,而不依赖于WiFi等其他外部设备。
一、概要
这篇分享,选用了以下的3款有代表性的ESP32开发板,组成一个自组网灯控系统:
上图中的三块开办板分别为:
[*]最左边的,是咱们的主角,FireBeetle 2 Board ESP32-S3(N16R8)开发板(带摄像头),作为主控设备接入系统
[*]中间的,某友商CORE-ESP32S3开发板,有两个灯,可以用于交替闪烁,作为从设备1接入系统
[*]最右边的,是乐鑫官方的ESP32-C3-DevKitM-1,板载的是WS2812B灯,可以变换颜色,作为从设备2接入系统
要实现这个自组网灯控系统,需要完成的基本功能如下:
[*]设备可以自动发现
[*]设备自动发现以后,可以自动连接通信
[*]以上功能不依赖于其他设备;当然,开发的时候还是需要。
在这篇分享里面,设备自动发现,使用了蓝牙来实现。
[*]当从设备启动后,会自动设置自身的蓝牙广播名称,短名称为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))
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:", , ["%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: ", , ["%02X" % i for i in mac_addr])
print("ble_addr:", , ["%02X" % i for i in ble_addr])
ble_name = "LIGHT_GROUP_%s" % "".join(["%02X" % i for i in ble_addr])
ble_name_full = "LIGHT_GROUP_%s" % "".join(["%02X" % i for i in mac_addr])
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)
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: ", , ["%02X" % i for i in mac_addr])
print("ble_addr:", , ["%02X" % i for i in ble_addr])
ble_name = "LIGHT_GROUP_%s" % "".join(["%02X" % i for i in ble_addr])
ble_name_full = "LIGHT_GROUP_%s" % "".join(["%02X" % i for i in mac_addr])
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)
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), colors)
pixel.fill(colors)
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,已经同步起舞了:
https://www.bilibili.com/video/BV1K8411B7nS/?share_source=copy_web&vd_source=dd3afa784d4f6118125640815885322b
五、总结
这篇分享,只是对蓝牙和ESP-NOW的一点小小应用。
在自组网灯控系统的代码中,直接暴露了WiFi硬件地址,实际使用中,可以通过蓝牙scan_responses信息,来返回经过加密的WiFi硬件地址信息,提高安全性。
前面也说过,灯控的部分,只是做了简单的处理,以便演示。在实际使用中,可以根据实际需要,进行具体逻辑的设计。
另外,在实际使用时,可以使用锂电池供电,从而可以方便进行现场具体环境的布置,达到刚好的效果。
页:
[1]