1. 背景
首先非常感谢 iceasy 商城和萤火工场(Firefly Workshop)提供的 24GHz 毫米波雷达模块 CEM5826-M11(9.9 购买链接),我非常荣幸的获得了测评机会。本文在 ESP32C3 上通过 micropython 读取雷达数据,结合三色灯和蜂鸣器实现一套人体感应迎宾系统。
2. 将 CEM5826-M11 接入 ESP32C3
用的是这款 ESP32C3,CEM5826-M11 需要串口读取雷达数据,我选用 ESP32C3 的TX=GPIO0
和RX=GPIO1
这组 UART 口。接线后这样
CEM5826-M11 模块未带焊针,这里用杜邦线连接有点丑,无奈之举,请忽略!!
3. 迎宾系统设计思路
从接线图可看到除了雷达模块外,还接入了一个蜂鸣器 + 一个三色 LED 灯,这套迎宾系统的第一目标是一定要酷炫。
3.1 系统状态定义
- 迎宾检测
当检测范围里第一次出现人时
- 三色灯以红/绿/蓝交替闪烁
- 蜂鸣器播放《小星星》歌曲欢迎
2. 活动检测
根据雷达检测到的人体不同活动幅度,分别亮灯
- 小幅度活动:蓝灯亮
- 中幅度活动:绿灯亮
- 大幅度活动:红灯亮
3. 离开检测
检测范围里人离开后
- 红灯闪烁
- 蜂鸣器播放《敢问路在何方》歌曲欢送
3.2 活动幅度定义
这里不得不夸下CEM5826-M11
,雷达数据获取方法如喝水般无比简单,只需周期性在 uart 上读取数据即可,读取到的数据格式:v=**,str=** 。(V 代表目标速度大小,str 代表信号强度)
。在 micropython 操作下,没有比这种接口更简单的了。
根据雷达测试数据,我将活动幅度定义如下
1. 无人噪声信号 < 800
v=-1.1 km/h, str=370
v=1.0 km/h, str=394
v=1.1 km/h, str=325
v=-1.0 km/h, str=303
v=1.1 km/h, str=293
v=1.0 km/h, str=324
v=-1.0 km/h, str=317
v=1.1 km/h, str=364
v=1.2 km/h, str=387
v=-1.4 km/h, str=395
2. 可忽略的活动 < 1000
v=0.8 km/h, str=989
v=-0.9 km/h, str=746
v=0.9 km/h, str=629
v=0.9 km/h, str=630
v=-0.8 km/h, str=636
v=-0.9 km/h, str=543
v=0.8 km/h, str=611
v=0.5 km/h, str=697
v=0.5 km/h, str=843
3. 1000 <= 小幅度活动 < 2000
v=-0.2 km/h, str=1106
v=-0.1 km/h, str=1168
v=-0.1 km/h, str=1205
v=-0.1 km/h, str=1392
v=-0.1 km/h, str=1279
v=0.1 km/h, str=1343
v=-0.1 km/h, str=1289
v=-0.0 km/h, str=1329
v=-0.0 km/h, str=1401
v=-0.0 km/h, str=1333
v=-0.0 km/h, str=1198
v=-0.0 km/h, str=1133
v=-0.0 km/h, str=1005
v=-0.1 km/h, str=1005
v=-0.2 km/h, str=1037
v=0.2 km/h, str=1548
4. 2000 <= 中幅度活动 < 3000
v=0.4 km/h, str=2192
v=0.5 km/h, str=2319
v=0.6 km/h, str=2549
v=0.9 km/h, str=2832
v=1.0 km/h, str=2836
v=1.1 km/h, str=2816
v=1.1 km/h, str=2816
v=-1.1 km/h, str=2724
5. 大幅度活动 >= 3000
v=-1.0 km/h, str=4074
v=-1.1 km/h, str=7792
v=-1.0 km/h, str=10723
v=-1.0 km/h, str=12217
v=-0.9 km/h, str=12513
v=0.8 km/h, str=13748
v=0.8 km/h, str=13757
v=-0.8 km/h, str=13799
v=0.6 km/h, str=13506
v=0.5 km/h, str=10242
v=-0.4 km/h, str=7675
v=-0.4 km/h, str=4697
v=-0.2 km/h, str=3407
3.3 蜂鸣器唱歌
经过一番尝试最终用蜂鸣器模拟出不同音调,感兴趣的读者可参考:micropython 驱动蜂鸣器唱歌
4. 最终效果展示
5. 完整代码
https://github.com/tinnfu/CEM5826-M11
from machine import UART, Pin, PWM
import time
import re
import asyncio
class Buzzer:
# 定义音调频率
TONES = {
'1-': 262,
'2-': 294,
'3-': 330,
'4-': 349,
'5-': 392,
'6-': 440,
'7-': 494,
'1=': 523,
'2=': 587,
'3=': 659,
'4=': 698,
'5=': 784,
'6=': 880,
'7=': 988,
'1+': 1046,
'2+': 1175,
'3+': 1318,
'4+': 1397,
'5+': 1568,
'6+': 1760,
'7+': 1976,
'__': 0
}
def __init__(self, pin = 6):
self.buzzer = PWM(Pin(pin, Pin.OUT), freq=1000, duty=0)
def __del__(self):
self.buzzer.deinit()
async def play(self, melody, duty = 10):
i = 0
keep = False
while i < len(melody):
if melody[i] == '(':
i += 1
keep = True
continue
elif melody[i] == ')':
i += 1
keep = False
# 连音结束后稍微停顿下
self.buzzer.duty(0)
await asyncio.sleep(0.05)
continue
tone, level = melody[i], melody[i+1]
i += 2
freq = Buzzer.TONES[tone+level]
if freq:
self.buzzer.init(duty=duty, freq=freq)
else:
self.buzzer.duty(0) # 空拍时静音
# 停顿一下 (四四拍每秒两个音,每个音节中间稍微停顿一下)
await asyncio.sleep(0.2)
if not keep:
self.buzzer.duty(0) # 设备占空比为0,即不上电
await asyncio.sleep(0.05)
class Bye:
def __init__(self, buzzer):
self.buzzer = buzzer
async def Song(self):
# 《路在何方》
melody = \
"(6-1=1=6-)(3=3=3=)2=(2=1=1=1=1=1=1=1=)(7-6-6-7-)(2=2=2=)3=(1=6-6-6-6-6-6-6-)" \
"(3=3=3=3=)(6=6=6=3=)(6=6=5=4=)(3=3=3=3=)(1=1=1=)2=(3=3=4=3=)(2=2=2=2=2=2=2=2=)" \
"(6-6-)(3=3=)(2=3=6-6-)(1=1=1=1=1=1=3=3=)(2=7-7-3=)(2=6-1=2=)(3=3=3=3=3=3=3=3=)" \
"(3=3=3=3=)(6=6=6=3=)(6=6=5=4=)(3=3=3=3=)(5=2=2=4=)(3=2=1=1=)(2=2=2=2=2=2=3=3=)" \
"(2=7-7-3=)(7-6-5-5-)(6-6-6-6-6-6-)(3=3=)(5=5=5=5=5=5=)(3=5=)(6=6=6=)1+(7=6=)(5=5=)" \
"(6=6=6=6=6=6=6=6=)(1+1+1+1+)(7=7=7=)6-(5=6=)(5=5=5=5=)(5=6=)(3=3=3=3=3=3=3=3=)" \
"(1+1+1+1+)(7=7=7=)6=(5=6=)(5=5=5=5=)(5=6=)(3=3=3=3=3=3=3=3=)(5-6-)1=(3=3=3=)1=" \
"(2=3=)(2=2=2=2=2=2=)(2=7-7-)3=(7-6-5-5-)(6-6-6-6-6-6-6-6-)(5-6-6-)1=(3=3=3=)1=" \
"(2=3=)(2=2=2=2=2=2=)(3=3=5=5=5=5=)(3=3=)(7=7=7=1+7=6=5=5=)(6=6=6=6=6=6=6=6=6=6=6=6=6=6=6=6=)" \
"(3=3=5=5=5=5=)(3=3=)(7=7=7=1+7=6=5=5=)(6=6=6=6=6=6=6=6=6=6=6=6=6=6=6=6=)"
await self.buzzer.play(melody)
class Welcome:
def __init__(self, buzzer):
self.buzzer = buzzer
async def Song(self):
# 《小星星》
melody = "1=1=5=5=6=6=5=__4=4=3=3=2=2=1=__5=5=4=4=3=3=2=__5=5=4=4=3=3=2=__1=1=5=5=6=6=5=__4=4=3=3=2=2=1="
await self.buzzer.play(melody)
class Warning:
def __init__(self, buzzer):
self.buzzer = buzzer
async def Song(self):
melody = "(7=7=)(5=5=)(7=7=)(5=5=)(7=7=)(5=5=)(7=7=)(5=5=)"
await self.buzzer.play(melody)
class Led:
def __init__(self, r = 2, g = 3, b = 10):
self.r_led = Pin(r, Pin.OUT)
self.g_led = Pin(g, Pin.OUT)
self.b_led = Pin(b, Pin.OUT)
self._flash_done = False
def off(self, excludes = []):
if 'r' not in excludes:
self.r_led.off()
if 'g' not in excludes:
self.g_led.off()
if 'b' not in excludes:
self.b_led.off()
def on(self, signal):
if 'r' == signal:
self.r_led.on()
elif 'g' == signal:
self.g_led.on()
elif 'b' == signal:
self.b_led.on()
async def flash_begin(self, signal = ['r', 'g', 'b']):
self._flash_done = False
self.off()
while not self._flash_done:
if 'r' in signal:
self.on('r')
await asyncio.sleep(0.2)
self.off()
if 'g' in signal:
self.on('g')
await asyncio.sleep(0.2)
self.off()
if 'b' in signal:
self.on('b')
await asyncio.sleep(0.2)
self.off()
await asyncio.sleep(0.2)
def flash_end(self):
self._flash_done = True
class Monitor:
def __init__(self, buzzer, led):
self.buzzer = buzzer
self.led = led
self.incoming = False
self.agitation_begin = 0
self.idle_begin = 0
self.last_warning = 0
self.no_signal_begin = 0
async def bye(self):
self.led.off()
if self.incoming:
# 闪红灯+一首歌欢送
led = asyncio.create_task(self.led.flash_begin(['r']))
await asyncio.create_task(Bye(self.buzzer).Song())
self.led.flash_end()
await led
self.incoming = False
# 清理所有状态,等下个状态机
self.agitation_begin = 0
self.idle_begin = 0
self.last_warning = 0
self.no_signal_end = 0
async def welcome(self):
self.incoming = True
# 第一次来人,用酷炫的灯光+唱首歌欢迎
led = asyncio.create_task(self.led.flash_begin(['r', 'g', 'b']))
await asyncio.create_task(Welcome(self.buzzer).Song())
self.led.flash_end()
await led
async def warning(self):
led = asyncio.create_task(self.led.flash_begin(['r', 'g', 'b']))
await asyncio.create_task(Warning(self.buzzer).Song())
self.led.flash_end()
await led
self.last_warning = time.time()
async def process(self, info):
if not info:
if self.no_signal_begin == 0:
self.no_signal_begin = time.time()
# 去噪
if time.time() - self.no_signal_begin > 5:
await self.bye()
self.led.off()
return
self.no_signal_begin = 0
try:
lines = info.decode().split('\n')
except Exception as e:
print(e)
self.led.off()
return
# v=-0.8 km/h, str=1083
signal = 0
count = 0
for line in lines:
if not line.strip():
continue
print(line)
ret = re.search('str=(\d+)', line)
if not ret:
continue
signal += int(ret.group(1))
count += 1
if count == 0:
self.led.off()
return
signal /= count
if signal < 700:
self.agitation_begin = 0
if self.idle_begin == 0:
self.idle_begin = time.time()
if time.time() - self.idle_begin > 10:
await self.bye()
self.led.off()
return
if not self.incoming:
await self.welcome()
self.idle_begin = 0
if signal < 1000:
self.agitation_begin = 0
self.led.off()
return
if signal < 2000:
self.led.off(['b'])
self.led.on('b')
elif signal < 3000:
self.led.off(['g'])
self.led.on('g')
else:
self.led.off(['r'])
self.led.on('r')
if self.agitation_begin == 0:
self.agitation_begin = time.time()
# 检测到持续3s的大幅动作并且距上次报警超过5s,提示安静下来
if (time.time() - self.agitation_begin > 3) and (time.time() - self.last_warning > 5):
await self.warning()
async def main():
uart = UART(1, rx=1, tx=0)
buzzer = Buzzer()
led = Led()
monitor = Monitor(buzzer, led)
while 1:
info = uart.read()
await monitor.process(info)
await asyncio.sleep(1)
time.sleep(5)
asyncio.run(main())
6. 总结
本文在 ESP32C3 上用 micropython 驱动 CEM5826-M11 雷达检测模块,实现了一套酷炫的人体感应迎宾系统
。CEM5826-M11 模块对外输出简单的数据接口大大节省了开发调试时间,点赞!
一点建议:CEM5826-M11 模块没有焊接好也没有送焊针,无奈之下只能用杜邦线插入邮票口连线做开发调试。要是能提供焊接好的模块可选就更完美了。

