1785|8

281

帖子

7

TA的资源

一粒金砂(高级)

楼主
 

【得捷电子Follow me第1期】007:扩展任务-天气灯设计 [复制链接]

  本帖最后由 sipower 于 2023-6-8 23:43 编辑

一、规定任务的完成情况

首先,我汇总一下前面几贴对规定任务的完成情况。

第一帖,主要介绍了开箱、搭建开发环境和点亮LED的情况。展示了收到的开发套件。测试了Thonny和Mu Editor两种开发软件,并点亮了板上的LED。帖子链接如下:

https://bbs.eeworld.com.cn/thread-1244148-1-1.html

第二贴,介绍驱动两个外设:蜂鸣器和OLED。展示了这两个外设的综合应用:在屏幕上显示小星星,并且用蜂鸣器演奏出来,在演奏同时,LED灯按照节拍闪烁,发布了演示视频。帖子链接如下:

https://bbs.eeworld.com.cn/thread-1244149-1-1.html

第三贴,介绍网络连接和同步时钟。Pico W先是进行连网,然后访问NTP服务器校准RTC。其中我发现使用官方的ntptime.py不管怎么设置,获取到的时间都是UTC0时间,不能设置成UTC8的时间,经过反复查验,终于找到原因,并跟帖说明原因。帖子链接如下:

https://bbs.eeworld.com.cn/thread-1245498-1-1.html

第四贴,介绍GPS定位数据获取并把数据显示在OLED上。硬件上采用UART0连接GNSS模组。软件采用micropyGPS库进行定位信息的解析。实现了GPS模块数据接收,在OLED上显示定位的经纬度,在REPL窗口上输出相关的NIMA报文解析结果。帖子链接如下:

https://bbs.eeworld.com.cn/thread-1245499-1-1.html

第五贴,介绍利用3轴数字加速度计做一个简易水平仪。采用GROVE 3-AXIS DIGITAL ACCEL LIS3D模块读取位置信息,通过在OLED屏幕上显示一个可移动方块代表水平程度,实现基本水平仪功能。除了展示代码,还发布了演示视频。帖子链接如下:

https://bbs.eeworld.com.cn/thread-1245570-1-1.html

第六贴,介绍在水平仪基础上添加WS2812B灯珠做一个摇摇灯。通过读取加速度传感器的数据控制灯珠颜色。为了让颜色变化时,过渡的更丝滑、自然,我选用NeoPixel library的HSV颜色空间。在本贴中,对摇摇灯的工作原理和代码进行了详细介绍,并发布了演示视频。帖子链接如下:

https://bbs.eeworld.com.cn/thread-1245651-1-1.html

以上六篇帖子,除了完成规定任务之外,还测试了加速度传感器和数字灯珠。这些前期工作,都为我的扩展任务,设计一个天气灯打下了基础。

二、扩展任务

经过前面积累,我最终确定下来我的终极扩展任务,设计一个天气灯。下图是实物作品图片。

图1、天气灯实物

1、该作品要实现的功能

a、使用高德API通过网络获取公网IP地址,通过IP地址定位城市

b、利用获得的城市信息通过高德API查询天气信息并显示在OLED屏幕上

c、通过网络NTP服务校准时钟,并在OLED屏幕上显示实时时钟

d、使用加速度传感器进行动作识别,摇晃天气灯可以控制灯光变化,其中:

e、上下摇晃控制灯光开关;左右摇晃控制灯光变亮;前后摇晃控制灯光变暗。

f、每次动作识别成功,蜂鸣器都会进行声音提示。

g、频繁的晃动天气灯,会随机触发声光秀,演奏一曲小星星,并伴随灯光色彩变换。

2、该作品组成

a、PICO W主控模块

b、OLED显示模块

c、蜂鸣器模块

d、加速度传感器模块

e、WS2812B发光模块

f、整个作品由USB供电

g、程序使用microPython设计

3、系统框图

图2、系统框图

4、详细设计

在本设计中,基础的操作,如灯光控制,OLED显示,加速度传感器数值读取,联网,同步时钟等在前面帖子详细介绍过,此处略过,下面重点介绍新增的操作。

首先是获取天气设计。最开始计划采用GPS模块先获取经纬度,然后利用经纬度联网获取城市信息,再用城市信息联网获取天气信息。实际调试中,GPS模块不能确保在室内准确获取经纬度,这就造成后续的操作都不能实现,只好放弃GPS模块方案。

后来看到论坛里大佬发帖,采用公网IP地址方式获取城市信息,然后再获取天气信息实际效果很好,我就拿来主义,直接用上此方案了。大佬帖子链接如下:

https://bbs.eeworld.com.cn/thread-1243482-1-1.html

然后根据帖子中的介绍,我又查阅了高德API相关介绍,在编写程序时,我主要获取了城市名称,天气信息,温度和湿度这四个参数,最终显示在OLED屏幕上。高德接口链接如下:

天气查询

https://lbs.amap.com/api/webservice/guide/api/weatherinfo

为了实现不同天气显示不同颜色,我参照天气现象对照表,在程序中做了一个列表,为每一个天气现象分配了一个颜色数值,这样就实现了不同天气,发出不同颜色的灯光。具体信息可以参见下面程序代码。

天气现象对照表

https://lbs.amap.com/api/webservice/guide/tools/weather-code

然后是动作识别算法。大致思路是,每0.1秒读取一次加速传感器数值,连续读取15次,三个轴分别计算最大最小值,然后做差获得这1.5秒内的动作最大幅度变化值,然后把这个值和事先设定的基准值作比较,大于基准值则认为在对应轴向上有摇晃动作,产生调光信号。实际验证此算法基本可行,操作过程需要训练一下掌握好平衡,否则多个轴向都会触发调光动作。

接下来介绍获取UTC8时间,即北京时间。第三贴中,我发现使用官方库文件ntptime.py不管怎么设置,获取到的时间都是UTC0时间,不能设置成UTC8的时间,为了解决此问题,我修改了ntptime.py这个文件,在最后返回的数值基础上加了8小时,如下代码,另存为ntptime8.py,在放到PICO W板根目录,这样调用ntptime8.settime()校准时间,就是UTC8了。

return val - NTP_DELTA + 28800

代码1、设置UTC8时间

最后介绍声光秀实现。在第二贴中,有一段演奏小星星的蜂鸣器代码,我在驱动每个音节的时候,插入了WS2812B的代码,让其发光颜色跟声音频率关联起来,不同频率的音节对应不同颜色,就实现了声光秀表演。在主程序中,每次有效摇晃动作识别成功,都累计一次数,然后和一个随机数比较,如果大于随机数,就调用声光秀代码,这样就实现了随机触发灯光秀机制。随机触发代码段如下。

tk_num += 1
if tk_num > random.randint(5,20):#小星星触发次数
    tk_num = 0
    Twinkle()

代码2、随机触发代码

整体的主程序代码如下,其他引用的库文件见附件。

import network
import socket
import machine
import ubinascii
import urequests
import ujson
import time
import ntptime8
import random
from ssd1306 import SSD1306_I2C
import ufont
from neopixel import Neopixel
from micropython_lis3dh import lis3dh
from machine import Pin, PWM
from machine import RTC

TEMP = "25"
HUMI = "55"
CITY = "天街"
WEATH = "晴"

# 定义音调频率
tones = {'1': 262, '2': 294, '3': 330, '4': 349, '5': 392, '6': 440, '7': 494, '-': 0}
# 定义小星星旋律
melody = "1155665-4433221-5544332-5544332-1155665-4433221"

pwm = PWM(Pin(16))# Construct PWM object, with BEEP on Pin(16).
def beep(tone):
    pwm.freq(tones[tone]) # 调整PWM的频率,使其发出指定的音调
    pwm.duty_u16(10000)
    time.sleep_ms(100)
    pwm.duty_u16(0) 
def Twinkle():    
    for tone in melody:
        freq = tones[tone]
        if freq:
            pwm.freq(freq) # 调整PWM的频率,使其发出指定的音调
            pwm.duty_u16(10000)
            led.on()
            WS2812B(hue = freq*150, sat=255, val=255)
        else:
            pwm.duty_u16(0)  # 空拍时一样不上电
            led.off()
            WS2812B(hue = 150, sat=255, val=255)
        # 停顿一下 (四四拍每秒两个音,每个音节中间稍微停顿一下)
        time.sleep_ms(200)
        pwm.duty_u16(0)  # 设备占空比为0,即不上电
        led.off()
        #WS2812B(hue = 150, sat=255, val=255)
        time.sleep_ms(100)
    
strip = Neopixel(3, 0, 22, "GRB")  #RGB LED init
def WS2812B(hue=255, sat=255, val=255):
    color = strip.colorHSV(hue, sat, val)
    strip.fill(color)
    strip.show()
    
def numberMap(x, in_min, in_max, out_min, out_max):
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
i2c0 = machine.I2C(0,sda=machine.Pin("GP8"), scl=machine.Pin("GP9"), freq=400000)
lis = lis3dh.LIS3DH(i2c0)
lis.data_rate = lis3dh.DATARATE_200

led = machine.Pin("LED",machine.Pin.OUT)

i2c1 = machine.I2C(1, sda=machine.Pin("GP6"), scl=machine.Pin("GP7"), freq=400000)
display = SSD1306_I2C(128,64,i2c1)
display.fill(0)
font = ufont.BMFont("unifont-14-12917-16.v3.bmf")
font.text(display, "天气灯", 40, 15, show=True)
font.text(display, "Loading...", 24, 35, show=True)

ssid = 'ssid'
password = 'password '

wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(ssid, password)

# Wait for connect or fail
max_wait = 10
while max_wait > 0:
    if wlan.status() < 0 or wlan.status() >= 3:
        break
    max_wait -= 1
    print('waiting for connection...')
    time.sleep(1)

# Handle connection error
if wlan.status() != 3:
    raise RuntimeError('network connection failed')
else:
    print('connected')
    status = wlan.ifconfig()
    print( 'ip = ' + status[0] )
    ntptime8.host = 'ntp1.aliyun.com'  # 可选,ntp服务器,默认是"pool.ntp.org"
    ntptime8.settime()   # 修改设备时间,到这就已经设置好了
    print('NTP OK')
    
def open_socket():
    ip = urequests.get('http://ip.42.pl/raw')
    
    ip_str = ip.content.decode('ascii')
    print(ip_str)
    ip.close()
    
    str_data = 'https://restapi.amap.com/v3/ip?ip='+ip_str+'&output=JSON&key=1e459c41f6b32822efa6442e74ca09c0'
    city = urequests.get(str_data)
    
    city_str = city.content.decode('ascii')
    #print(city_str)
    parsed_city = ujson.loads(city_str)
    
    adcode = parsed_city["adcode"]
    city.close()
    r = urequests.get('https://restapi.amap.com/v3/weather/weatherInfo?key=1e459c41f6b32822efa6442e74ca09c0&city='+adcode)
    w_str = r.content.decode('ascii')
#     print(r.text)
    #print('111:'+ w_str)
    parsed_w = ujson.loads(w_str)
#     print (parsed_w)
    #w_f = parsed_w["temperature"]
    temp_str = parsed_w["lives"][0]["temperature_float"]
    humi_str = parsed_w["lives"][0]["humidity_float"]
    str_city = parsed_w["lives"][0]["city"]
    str_weather = parsed_w["lives"][0]["weather"]
    
    global TEMP
    TEMP = temp_str
    global HUMI
    HUMI = humi_str
    global CITY
    CITY = str_city
    global WEATH
    WEATH = str_weather

    print (str_city)
    print (str_weather)
    print (temp_str)
    print (humi_str)

    r.close()


try:
    open_socket()
    time.sleep(0.1)
    display.fill(0)
    TEMP_disp = "城市:"+CITY
    font.text(display, TEMP_disp, 0, 0*16, show=True)
    TEMP_disp = "天气:"+WEATH
    font.text(display, TEMP_disp, 0, 1*16, show=True)
    TEMP_disp = "T:"+TEMP+"℃"+"H:"+HUMI+"%"
    font.text(display, TEMP_disp, 0, 2*16, show=True)
#     TEMP_disp = "21:22:33"
#     font.text(display, TEMP_disp, 0, 3*16, show=True)

except KeyboardInterrupt:
    machine.reset()

C_V = 10  #动作比较值
sw = 0   #灯开关
t=0  #检测次数

hue = 0
sat = 0
val = 0

val_set = 255#亮度设置
en_set = 0#设置使能
tk_num = 0#小星星计数触发
rtc_num = 0#时间显示计数触发
x_max=0
x_min=0
y_max=0
y_min=0
z_max=0
z_min=0

weather_hue = {
    '晴':21845,'少云':21845,'晴间多云':21845,'多云':21845,'阴':32768,
    '有风':38228,'平静':38228,'微风':38228,'和风':38228,'清风':38228,
    '强风/劲风':38428,'疾风':38428,'大风':38428,'烈风':38428,'风暴':39428,'狂爆风':39428,'飓风':39428,'热带风暴':39428,
    '霾':10000,'中度霾':10000,'重度霾':10000,'严重霾':10000,
    '阵雨':30000,'雷阵雨':30000,'雷阵雨并伴有冰雹':30000,'小雨':30000,'中雨':30000,'大雨':30000,
    '暴雨':35000,'大暴雨':35000,'特大暴雨':35000,'强阵雨':35000,'强雷阵雨':35000,'极端降雨':35000,
    '毛毛雨/细雨':32500,'雨':32500,'小雨-中雨':32500,'中雨-大雨':32500,'大雨-暴雨':32500,'暴雨-大暴雨':32500,'大暴雨-特大暴雨':32500,
    '雨雪天气':40000,'雨夹雪':40000,'阵雨夹雪':40000,'冻雨':40000,
    '雪':40000,'阵雪':40000,'小雪':40000,'中雪':40000,'大雪':43690,'小雪-中雪':43690,'中雪-大雪':43690,'大雪-暴雪':43690,
    '浮尘':1000,'扬沙':1000,'沙尘暴':1000,'强沙尘暴':1000,'龙卷风':1000,
    '雾':13000,'浓雾':13000,'强浓雾':13000,'轻雾':13000,'大雾':13000,'特强浓雾':13000,
    '热':0,'冷':43690,'未知':54612
    }

while True:
    x,y,z = lis.acceleration
    if x>x_max:
        x_max=x
    if x<x_min:
        x_min=x
    if y>y_max:
        y_max=y
    if y<y_min:
        y_min=y
    if z>z_max:
        z_max=z
    if z<z_min:
        z_min=z
    
    t+=1
    if t > 15:#15次计算一下
        t=0
        d_x=x_max-x_min
        d_y=y_max-y_min
        d_z=z_max-z_min
        x_max=x
        x_min=x
        y_max=y
        y_min=y
        z_max=z
        z_min=z

#         print (d_x,d_y,d_z)
        if  d_x>C_V:
            en_set = 1
            val_set += 50
            if val_set > 255:
                val_set = 255
        if d_y>C_V:
            val_set -= 50
            en_set = 1
            if val_set < 1:
                val_set = 1        
        if d_z>C_V:
            en_set = 1
            sw ^= 1
#         print (val_set)

    if sw==1:
        val = val_set
    else:
        val = 0
    
    if en_set == 1:
        en_set = 0
        hue = weather_hue[WEATH]  #需要改成天气查表颜色
        sat = 255
        WS2812B(hue, sat, val)
        beep('6')
        tk_num += 1
        if tk_num > random.randint(5,20):#小星星触发次数
            tk_num = 0
            Twinkle()
            
    time.sleep(0.1)
    rtc_num += 1
    if rtc_num > 10:#每秒刷新一次
        rtc_num = 0
        rtc = RTC()
#         print(rtc.datetime())
        display.rect(0,3*16,8*8,16,0,True)
        TEMP_disp = "%02d:%02d:%02d" % (rtc.datetime()[4],rtc.datetime()[5],rtc.datetime()[6])
        font.text(display, TEMP_disp, 0, 3*16, show=True)

代码3、主程序

通过以上设计,最终完成天气灯这个作品,整个作品的介绍和演示视频如下,链接为高清视频。

bk

https://training.eeworld.com.cn/uploadcourse/67992/lesson

视频1、天气灯演示

三、总结

通过本次活动,我熟悉了树莓派PICO W的基本设计开发流程,对microPython更加了解,也在论坛里认识了更多技术大佬。感谢得捷电子和EEWORLD提供的这次机会,也希望在接下来的活动中,提供更多好玩的板子和题目,让大家一起快乐搞机。

007.docx

312.82 KB, 下载次数: 3

word格式报告

代码.zip

268.56 KB, 下载次数: 1

程序代码包

最新回复

我想在电脑上显示,做个定位的小程序,不知道如何实现。   详情 回复 发表于 2023-6-9 10:34
点赞 关注
 
 

回复
举报

6969

帖子

11

TA的资源

版主

沙发
 
6666s楼主大佬一个作品,让我们学习到了丰富的知识呀。

点评

哈哈哈哈,期待你的作品出炉  详情 回复 发表于 2023-6-9 08:33
 
 
 

回复

281

帖子

7

TA的资源

一粒金砂(高级)

板凳
 
lugl4313820 发表于 2023-6-9 07:21 6666s楼主大佬一个作品,让我们学习到了丰富的知识呀。

哈哈哈哈,期待你的作品出炉

 
 
 

回复

650

帖子

8

TA的资源

纯净的硅(初级)

4
 

楼主高德都不自己注册一个api么,才发现我的key。。。。。

点评

看你没有隐藏,我就直接拿来主义了,所以在文中直接引用你的帖子链接了  详情 回复 发表于 2023-6-9 17:29
大佬呀,能不能教我怎么在地图上标位置呀。  详情 回复 发表于 2023-6-9 09:58
 
 
 

回复

6969

帖子

11

TA的资源

版主

5
 
29447945 发表于 2023-6-9 09:23 楼主高德都不自己注册一个api么,才发现我的key。。。。。

大佬呀,能不能教我怎么在地图上标位置呀。

点评

你是要把gps上报到服务端然后在地图上显示位置是嘛,按照平台的协议报GPS数据就可以了,onenet平台我报过,高德的话没弄过  详情 回复 发表于 2023-6-9 10:09
 
 
 

回复

650

帖子

8

TA的资源

纯净的硅(初级)

6
 
lugl4313820 发表于 2023-6-9 09:58 大佬呀,能不能教我怎么在地图上标位置呀。

你是要把gps上报到服务端然后在地图上显示位置是嘛,按照平台的协议报GPS数据就可以了,onenet平台我报过,高德的话没弄过

点评

我开始也想上传阿里云位置信息,然后使用上面的应用显示实时位置,找来找去没找到现成的轮子,还得自己造,太费劲了,就没搞这个  详情 回复 发表于 2023-6-9 19:08
我想在电脑上显示,做个定位的小程序,不知道如何实现。  详情 回复 发表于 2023-6-9 10:34
 
 
 

回复

6969

帖子

11

TA的资源

版主

7
 
29447945 发表于 2023-6-9 10:09 你是要把gps上报到服务端然后在地图上显示位置是嘛,按照平台的协议报GPS数据就可以了,onenet平台我报过 ...

我想在电脑上显示,做个定位的小程序,不知道如何实现。

 
 
 

回复

281

帖子

7

TA的资源

一粒金砂(高级)

8
 
29447945 发表于 2023-6-9 09:23 楼主高德都不自己注册一个api么,才发现我的key。。。。。

看你没有隐藏,我就直接拿来主义了,所以在文中直接引用你的帖子链接了

 
 
 

回复

281

帖子

7

TA的资源

一粒金砂(高级)

9
 
29447945 发表于 2023-6-9 10:09 你是要把gps上报到服务端然后在地图上显示位置是嘛,按照平台的协议报GPS数据就可以了,onenet平台我报过 ...

我开始也想上传阿里云位置信息,然后使用上面的应用显示实时位置,找来找去没找到现成的轮子,还得自己造,太费劲了,就没搞这个


 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表