一、视频镇楼,绘声绘色有真相
二、项目总结报告
1. 项目描述(800字左右,注意格式整洁)
“熊孩子防丢暴闪报警器”这个小制作,是在英明神武的EEWorld和慷慨豪爽的得捷电子共同组织的Follow me活动中诞生的,它的问世,是楼主个人的一小步,也是楼主使用树莓派PicoW的第一步。这一步虽小,但标志着……(此处略去800字)
平时带孩子出去玩,小孩子喜欢跑来跑去,所以经常需要小规模、有限制的放养——允许他在一定区域内自由活动。但是要是周围人多或者有各种障碍物,这活就太刺激了——孩子喜欢和你捉迷藏,总担心跑丢了。这次参加EEWorld和得捷电子共同组织的Follow me活动,终于有机会把这个“熊孩子防丢暴闪报警器”的设想做成现实作品了。
我首先学了Mu Editor和Thonny,感觉还是后边这个编辑器好用。然后把老师教程里讲的LED、OLED、蜂鸣器3个例程,混合成了一个——就是视频里那个OLED显示microPython 的logo,蜂鸣器响的同时,板载LED还在闪的效果。之后学习network模块用法,调RESTful服务,获取网络时间。然后实验UART连GPS,从网上找到了micropyGPS库,解决了GNSS返回数据看不懂的问题。最后从《raspberry pi pico python sdk》的apendex A里找到驱动WS2812 LED序列的方法。这些准备工作都做好之后,就把各项功能组合起来,做出了这个“熊孩子防丢暴闪报警器”。
这个报警器的用法和原理是这样的:到了某个地方之后,把报警器打开,会自动记录当前的经纬度坐标作为原点。我研究了一下GPS模块的精度,南北方向大约可以精确到1米,东西方向大约可以精确到2米。那么只要不停地获取最新经纬度坐标,并与原点比较,就可以估算出距离,当距离超过阈值时,启动报警机制就可以了。
我查了下,根据《汽车鸣笛声、雨声和警笛声的频谱分析》(https://blog.csdn.net/qq_30229253/article/details/84333375)的分析,警车的频率从600Hz到1.4KHz之间随时间线性变化。再加上用WS2812 LED交替显示红色、蓝色形成警报灯的效果,报警部分也完成了。
现在设计的机制是,一旦开始报警,除非关掉电源,否则不会停止。这样刺眼的灯光效果和刺耳的警报声,对于找到乱跑的熊孩子应该有比较大的帮助。以后可以考虑在启动这个报警器时,允许设置距离,用来应对场地大小不同的情况。
感谢EEWorld和得捷电子,以及先行的各位网友!
2. 各功能对应的主要代码片段及说明
2.1 模块(ssd1315为了调试方便,其实可以去掉)
def do_until_no_exception(func,args=(),vars={}):
notSuccess = True
rtn = None
while notSuccess:
notSuccess = False
try:
rtn = func(*args,**vars)
except Exception as e:
notSuccess = True
return rtn
# 测试:do_until_no_exception(min,(1,0))
# 测试:do_until_no_exception(min))
2.4 初始化部分:所有全局变量一次都声明,在各函数体内用global来引用
i2c,oled,gps_module,my_gps,lat_0,lng_0,lat,lng,led_onboard,buzzer_pin,pwm=[None]*11
def init():
global i2c,oled,gps_module,my_gps,lat_0,lng_0,lat,lng,led_onboard,buzzer_pin,pwm
import machine
from machine import Pin,UART,I2C,PWM
from ssd1306 import SSD1306_I2C
import framebuf
import utime,array, time
from micropyGPS import MicropyGPS
import rp2
i2c=I2C(0,sda=Pin(8),scl=Pin(9),freq=400000)
oled = SSD1306_I2C(128,64,i2c)
gps_module = UART(0, baudrate=9600,tx=Pin(0), rx=Pin(1))
my_gps = MicropyGPS(8)#东八区
led_onboard = machine.Pin("LED",machine.Pin.OUT)
buzzer_pin = machine.Pin(16,machine.Pin.OUT)
pwm = PWM(Pin(16))
pwm.freq(32000)
pwm.duty_u16(32768)
do_until_no_exception(init)
2.5 初始化WS2813(厚脸皮几乎不动原文地“借鉴”自官方python sdk 手册)
# Configure the number of WS2813 LEDs.
NUM_LEDS = 10
PIN_NUM = 20
brightness = 0.2
# Display a pattern on the LEDs via an array of LED RGB values.
ar = array.array("I", [0 for _ in range(NUM_LEDS)])
@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=True, pull_thresh=24)
def ws2813():
T1 = 2
T2 = 5
T3 = 3
wrap_target()
label("bitloop")
out(x, 1) .side(0) [T3 - 1]
jmp(not_x, "do_zero") .side(1) [T1 - 1]
jmp("bitloop") .side(1) [T2 - 1]
label("do_zero")
nop() .side(0) [T2 - 1]
wrap()
def init_ws2813():
# Create the StateMachine with the ws2813 program, outputting on pin
sm = rp2.StateMachine(0, ws2813, freq=8_000_000, sideset_base=Pin(PIN_NUM))
# Start the StateMachine, it will wait for data on its FIFO.
sm.active(1)
do_until_no_exception(init_ws2813)
2.6 蜂鸣器发警笛声效果
BUZZ_MIN,BUZZ_MAX,BUZZ_DELTA = (600,1400,20)
buzz_freq,buzz_dir = (BUZZ_MIN ,1)
def do_buzz_police():
global buzz_freq,buzz_dir
if buzz_dir>0 :
buzz_freq+=BUZZ_DELTA
if buzz_freq>BUZZ_MAX:
buzz_freq,buzz_dir = (BUZZ_MAX ,-1)
else:
buzz_freq-=BUZZ_DELTA
if buzz_freq<BUZZ_MIN:
buzz_freq,buzz_dir = (BUZZ_MIN ,1)
pwm.freq(buzz_freq)
pwm.duty_u16(32768)
2.7 警灯的红蓝爆闪(感谢群友)
def pixels_show():
dimmer_ar = array.array("I", [0 for _ in range(NUM_LEDS)])
for i,c in enumerate(ar):
r = int(((c >> 8) & 0xFF) * brightness)
g = int(((c >> 16) & 0xFF) * brightness)
b = int((c & 0xFF) * brightness)
dimmer_ar[i] = (g<<16) + (r<<8) + b
sm.put(dimmer_ar, 8)
time.sleep_ms(10)
def pixels_set(i, color):
ar[i] = (color[1]<<16) + (color[0]<<8) + color[2]
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
PIX_SEQ = ( (RED,BLUE),(BLACK,BLACK), (BLUE,RED),(BLACK,BLACK) )
pix_stat = 0
def do_shine_all(color):
for i in range(NUM_LEDS):
pixels_set(i, color)
pixels_show()
def do_shine_police():
global pix_stat
if pix_stat>=len(PIX_SEQ) :
pix_stat = 0
pixels_set(0, PIX_SEQ[pix_stat][0])
pixels_set(1, PIX_SEQ[pix_stat][0])
pixels_set(2, PIX_SEQ[pix_stat][0])
pixels_set(3, PIX_SEQ[pix_stat][0])
pixels_set(4, BLACK)
pixels_set(5, BLACK)
pixels_set(6, PIX_SEQ[pix_stat][1])
pixels_set(7, PIX_SEQ[pix_stat][1])
pixels_set(8, PIX_SEQ[pix_stat][1])
pixels_set(9, PIX_SEQ[pix_stat][1])
pixels_show()
pix_stat+=1
2.7 GPS获取坐标
def get_GPS_values(com):
time.sleep_ms(200)
cc = com.readline()
if cc and cc.decode:
cc1=cc.decode()
for x in cc:
my_gps.update(chr(x))
lat = my_gps.latitude[0] + (my_gps.latitude[1] / 60)
lng = my_gps.longitude[0] + (my_gps.longitude[1] / 60)
return lat,lng#,s_time,rtc
else:
return None,None
def get_init_GPS_vlaue():
global lat,lng,lat_0,lng_0
lat,lng = get_GPS_values(gps_module)
while not ( lat and lng ):
lat,lng = get_GPS_values(gps_module)
lat_0,lng_0 = lat,lng
do_until_no_exception(get_init_GPS_vlaue)
2.8 判断是否在范围内,远了就报警(此处判断函数围起来的是个菱形)
DIS_ALERT = 50 # 10 #
while True:
lat,lng = get_GPS_values(gps_module)
print(n,lat,lng)
if lat and lng :
oled.contrast(50)
oled.fill(0)
oled.text("Lat:", 2, 23)
oled.text("lng:", 2, 41)
oled.text(str(lat), 40, 23)
oled.text(str(lng), 40, 41)
oled.show()
time.sleep(0.5)
distance = 100000 * ( abs(lat-lat_0)+abs(lng-lng_0) )
if distance > DIS_ALERT:
do_police_shine_buzz()
3. 功能展示及说明(图文并茂)
3.1 成品原型
到了某个地方之后,把报警器打开,会自动记录当前的经纬度坐标作为原点。“报警器”会不停地获取最新经纬度坐标,并与原点比较,如果距离超过阈值时,开始有声有色的发出警报。
3.3 闪起来的照片。很耀眼,建议还是看视频。
4. 对本活动的心得体会(包括意见或建议)
学习Pico W的过程在 http://bbs.eeworld.com.cn/thread-1249646-1-1.html 里,此处不再赘言。
感谢专家的讲解和网友们的讨论,使我几乎不费力气做完了这个作品。这也从侧面证实microPython确实好上手。
希望以后继续参加这类活动,多学多练多动手。
感谢EEWorld和得捷电子,以及各位网友!
三、代码
task_1 ~ task_6 对应各个任务,需要的话改成 main就能自动运行了。