[作品提交] 【得捷电子Follow me第1期】熊孩子防丢暴闪报警器

nemon   2023-6-30 23:17 楼主

一、视频镇楼,绘声绘色有真相

472be1caef8d6ab6862dbe77515616f2

二、项目总结报告
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采集测试.png
    这个报警器的用法和原理是这样的:到了某个地方之后,把报警器打开,会自动记录当前的经纬度坐标作为原点。我研究了一下GPS模块的精度,南北方向大约可以精确到1米,东西方向大约可以精确到2米。那么只要不停地获取最新经纬度坐标,并与原点比较,就可以估算出距离,当距离超过阈值时,启动报警机制就可以了。

20181122090959233.jpeg
    我查了下,根据《汽车鸣笛声、雨声和警笛声的频谱分析》(https://blog.csdn.net/qq_30229253/article/details/84333375)的分析,警车的频率从600Hz到1.4KHz之间随时间线性变化。再加上用WS2812 LED交替显示红色、蓝色形成警报灯的效果,报警部分也完成了。
    现在设计的机制是,一旦开始报警,除非关掉电源,否则不会停止。这样刺眼的灯光效果和刺耳的警报声,对于找到乱跑的熊孩子应该有比较大的帮助。以后可以考虑在启动这个报警器时,允许设置距离,用来应对场地大小不同的情况。
    感谢EEWorld和得捷电子,以及先行的各位网友!

 

2. 各功能对应的主要代码片段及说明

2.1 模块(ssd1315为了调试方便,其实可以去掉)

爆闪防丢报警-模块.PNG 2.2 流程图

爆闪防丢报警-流程图.PNG 2.3 锲而不舍的加载函数(只要执行异常就再执行一遍)

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 成品原型

效果1.png 3.2 使用方法

到了某个地方之后,把报警器打开,会自动记录当前的经纬度坐标作为原点。“报警器”会不停地获取最新经纬度坐标,并与原点比较,如果距离超过阈值时,开始有声有色的发出警报。

 

3.3 闪起来的照片。很耀眼,建议还是看视频。

效果2.png
4. 对本活动的心得体会(包括意见或建议)
学习Pico W的过程在 http://bbs.eeworld.com.cn/thread-1249646-1-1.html 里,此处不再赘言。
感谢专家的讲解和网友们的讨论,使我几乎不费力气做完了这个作品。这也从侧面证实microPython确实好上手。
希望以后继续参加这类活动,多学多练多动手。
感谢EEWorld和得捷电子,以及各位网友!

 

三、代码

task_1 ~ task_6 对应各个任务,需要的话改成 main就能自动运行了。

prj_.rar (13.37 KB)
(下载次数: 0, 2023-7-1 00:34 上传)

 

回复评论 (2)

恭喜完成作业,希望能拿大奖回家,辛苦了。
点赞  2023-7-1 09:04

这个想法相当不错,重点还是做个显示软件,对方位和距离都显示一下

在爱好的道路上不断前进,在生活的迷雾中播撒光引
点赞  2023-7-1 17:00
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复