2288|3

324

帖子

5

TA的资源

纯净的硅(中级)

楼主
 

【行空板 Python编程学习主控板】手把手教你制作基于行空板的实时天气信息牌 [复制链接]

本帖最后由 HonestQiao 于 2022-11-11 10:39 编辑

行空板板载的丰富接口,能够让我们很方便的接入多种传感器。简单的网络连接方式,能够让我们快捷的连接到网络。而自带的GUI屏,可以很方便我们进行显示。

 

今天的分享,就是基于行空板的实时天气牌,具体如下:

 

下面,就给大家分享制作步骤。

 

目录:

一、硬件材料

二、原理图

三、实物连线

四、SHT31数据读取

五、实时天气信息获取

六、屏幕操作

七、代码编写

八、最终作品

九、总结

 

 

一、硬件材料

  • 主控板:行空板
  • 温湿度传感器:DFRobot SHT31
  • 连接线:I2C连接线

我直接使用的是DFRobot版本的SHT31,配送专用连接线。

使用其他的I2C的SHT3X传感器,也完全可以。

 

二、原理图

 

 

行空板使用标准PH2.0接口,也不用原理图了,找对接口插上就行了。

 

三、实物接线

如果是自己的传感器的话,注意:

  • D:SDA
  • C:SCL
  •  +:VCC
  • -:GND

 

四、SHT31数据读取

以前也在Linux下面使用过SHT31传感器,有使用到的Python库。

经过仔细研读行空板Python开发的教程后,得了,不用自己弄了,行空板自带了。

行空板使用的Python硬件操作库为 pinpong,具体资料可见:行空板官方文档 - pinpong库 (unihiker.com)

pinpong库的使用资料,可以查看:欢迎访问 pinpong python库教程文档! — pinpong 0.1 文档

查看pinpong的更新记录,发现已经支持了SHT31:

 只是教程里面,并没有使用案例;

既然没有使用案例,那就自己看库好了。

 

https://pypi.org/ 搜索到 pinpong库:

 

注意,不是:https://pypi.org/project/pingpong/

 

在pypi的pinpong页面,下载该扩展库,解压后,找到如下文件:

 这个就是SHT31的支持库。

 

在其中,有几个关键调用,可以方便我们快捷的使用:

  • sht31 = SHT31(i2c_addr=0x45):初始化

  • sht31.readTemperatureAndHumidity(0):预读取温湿度数据

  • temp = sht31.temp_c():获得温度值

  • humi = sht31.humidity():获得湿度值

五、实时天气信息获取

通过SHT31,我们可以获取当前环境的温湿度。但是,我们还想获得更多的天气相关的信息,这就需要通过网络获取了。

在网上查找天气信息API的时候,通过 https://www.cnblogs.com/dummersoul/p/12174601.html 了解到了

和风天气,可以提供天气API,并且免费用户每天有1000次的调用次数,使用没有障碍。

 要使用该免费天气API,需要注册账户,获取数据请求的加密key,以及了解对应的接口,具体步骤如下:

1. 访问 和风天气 注册账号并登陆:

网址:https://dev.qweather.com

 

2. 创建免费项目

 一个用户,只有一个免费项目名额。

 

3. 查看项目

 

4. 创建key

 注意,此处应该选择Web Api

 

创建完成后会显示生成的Key:

 复制该Key备用,我们的Python程序中,将要使用到。

 

5. 了解使用方式

天气信息的具体调用接口,可以查看:https://dev.qweather.com/docs/api/weather/

因为要显示实时天气信息,所以使用的实时天气Api接口如下:

 调用该接口后,将会返回对应的天气信息。

 

例如:

  • 北京的LocationID为:101010100
  • 天津的LocationID为:101030100

那么,通过下面的方式,就能获取对应的测试数据了:

注意:测试前,需要先使用ssh连接到行空板,可参考官方教程:https://wiki.unihiker.com/ssh

# 获取北京的实时天气信息
curl -L -X GET --compressed 'https://devapi.qweather.com/v7/weather/now?location=101010100&key=上一步生成的Key'

# 获取天津的实时天气信息
curl -L -X GET --compressed 'https://devapi.qweather.com/v7/weather/now?location=101030100&key=上一步生成的Key'

实际返回如下:

 上述命令中的Key,要换成你在上一步中生成的Key。

关于返回的信息中,各字段的具体含义,可以查看:https://dev.qweather.com/docs/api/weather/weather-now/

 

然后,在Python中,使用request来获取上述网址返回的信息,然后使用json进行界面,就能得到对应的数据了:

  • response = requests.get(url):请求指定网址获取数据

  • data = json.loads(response.text):将返回的json文本,解析为python的dict,方便我们查询

6. 获取指定地区的LocationID

在上面的API请求中,有一个LocationID参数,这个需要我们提前获取。

而和风天气,也提供了GeoAPI,可以方便我们获取指定城市的Geo信息,具体说明如下:

 

在行空板上,实际操作如下:

# 获取北京的Geo信息
curl -L -X GET --compressed 'https://geoapi.qweather.com/v2/city/lookup?location=beijing&key=上一步生成的Key'

# 获取天津的Geo信息
curl -L -X GET --compressed 'https://geoapi.qweather.com/v2/city/lookup?location=tianjin&key=上一步生成的Key'

 

实际返回如下:

 

 同样的,可以在Python中,通过request和json扩展,来获取并解析。

 

7. 天气图标获取

和风天气,也提供了天气相关的图标,但是是svg格式的,不能在行空板上直接使用,需要转换为png或者jpg格式。

Iconv库地址:https://github.com/qwd/Icons

要转换为png格式,使用如下的方法:

# 创建实时天气信息牌的工作目录
mkdir ~/projects/weather
cd ~/projects/weather

# 获取天气图标库
git clone https://github.com/qwd/Icons.git
cd Icons/icons

# 转换天气图标
mkdir ../../imgs/
for i in $(ls *.svg);do convert $i $i.png;done
for i in $(ls *.svg);do convert $i.png -fuzz 2% -transparent white ../../imgs/$i.png;done

# 查看转换结果
ls ../../imgs/

 

最后的结果如下:

 

另外,还需要在/root/projects/weather/imgs/下面,放一张bg.jpg,大小为240x320,做为背景图片,可以用下图:

 

 

六、屏幕操作

行空板自身的系统,提供了屏幕操作的库,使用很方便。

制作这个实时天气信息牌,使用到的调用如下:

  • gui=GUI():实例化屏幕对象

  • gui.on_key_click('a', lambda: exit()):绑定按键,按A键,调用exit()函数

  • gui.draw_text(x=60, y=0, text='实时天气情况'):在屏幕指定位置(60,0)输出文字

  • gui.draw_image(x=0, y=0, w=240, h=320, image=Image.open(bg_file)):在屏幕指定位置(0,0),显示图像(路径为bg_file),大小为240x320

  • gui.draw_digit(x=20, y=y, color="red", font_size=12, text=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())):使用数码管字体,显示当前时间

  • temp_obj.config(text="%s℃" % ret['temp']):更新指定对象的值

关于上述调用的具体说明,可以查看行空板的教程:https://wiki.unihiker.com/unihiker_python_lib_2

其中,按键调用的作用,是为了方便按键,退出程序的运行。

 

七、编写代码

现在,各项准备工作就绪,就可以编写实际的代码了。

具体代码如下:

# -*- coding: UTF-8 -*- 
import os,sys
import time
from pinpong.board import Board
from pinpong.libs.dfrobot_sht31 import SHT31
from unihiker import GUI
from PIL import Image
import requests
import json

# 和风天气API服务:https://dev.qweather.com/docs/api/weather/
weather_type = ['now', '3d', '7d', '24h', '48th']  # 实时、天-3天、天-7天、小时-24小时、小时48小时
weather_icon_dir = "/root/projects/weather/imgs"
key = '*******前面生成的Key*********'

LocationName = "天津"
LocationID = 0

def get_location(where):
    url = 'https://geoapi.qweather.com/v2/city/lookup?location=%s&key=%s' \
        % (where, key)  # 和风天气平台GeoAPI
    response = requests.get(url)
    if response.status_code == 200:
        data = json.loads(response.text)
        if "code" in data and data["code"] == '200':
            if len(data["location"])>0:
                # 获取到地址,取第一个
                LocationName = data["location"][0]["adm1"]
                LocationID = data["location"][0]["id"]
                return LocationName, LocationID
    
    return [False, False]


def get_weather(weather_type, LocationID, where):
    url = "https://devapi.qweather.com/v7/weather/%s?location=%s&key=%s" \
          % (weather_type, LocationID, key)  # 和风天气平台天气API
    response = requests.get(url)
    if response.status_code == 200:
        data = json.loads(response.text)
        if weather_type == 'now':
            if "code" in data and data["code"]=='200':
                print("当前时间:%s" % time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
                print("%s的实况天气:当前气温为%s摄氏度,体感温度为%s摄氏度,天气状况%s(%s),%s风向,风力%s级,风速每%s公里/时,相对湿度%%%s,大气压强为%s,能见度%s公里,云量%s"
                    % (where,
                    data["now"]['temp'],
                    data["now"]['feelsLike'],
                    data["now"]['text'],
                    data["now"]['icon'],
                    data["now"]['windDir'],
                    data["now"]['windScale'],
                    data["now"]['windSpeed'],
                    data["now"]['humidity'],
                    data["now"]['pressure'],
                    data["now"]['vis'],
                    data["now"]['cloud']
                    )
                    )
                return data["now"]
    
    return False

def exit():
    global isEnd
    isEnd = True
    

if __name__ == '__main__':
    # 初始化板子
    Board("UNIHIKER").begin()

    # 初始化显示屏
    gui=GUI()
    isEnd = False
    gui.on_key_click('a', lambda: exit())
    gui.on_key_click('b', lambda: exit())

    bg_file = "%s/bg.jpg" % weather_icon_dir
    bg_obj = gui.draw_image(x=0, y=0, w=240, h=320, image=Image.open(bg_file))

    title_obj = gui.draw_text(x=60, y=0, text='实时天气情况')


    # 读取温湿度信息
    sht31 = SHT31(i2c_addr=0x45)
    sht31.readTemperatureAndHumidity(0)
    temp = sht31.temp_c()
    humi = sht31.humidity()
    print("环境温度:%f 湿度:%f\n" % (temp, humi))

    # 获取地理位置信息
    LocationName, LocationID = get_location(LocationName)
    if LocationName == False:
        print("城市Geo信息获取失败")
    else:
        print("城市:%s ID:%s\n" % (LocationName, LocationID))
        ret = get_weather(weather_type[0], LocationID, LocationName)
        if ret == False:
            print("城市天气信息获取失败")
        else:
            # %s的实况天气:当前气温为%s摄氏度,体感温度为%s摄氏度,天气状况%s,%s风向,风力%s级,风速每%s公里/时,相对湿度%%%s,大气压强为%s,能见度%s公里,云量%s
            x1 = 20
            x2 = 120

            y = 20
            y_hight = 20

            gui.draw_text(x=x1, y=y, text="当前地区", font_size=13)
            name_obj = gui.draw_text(x=x2, y=y, text=LocationName, font_size=13)

            y = y + y_hight
            gui.draw_text(x=x1, y=y, text='当前气温', font_size=13)
            temp_obj = gui.draw_text(x=x2, y=y, text="%s℃" % ret['temp'], font_size=13)

            y = y + y_hight
            gui.draw_text(x=x1, y=y, text='体感温度', font_size=13)
            feelsLike_obj = gui.draw_text(x=x2, y=y, text="%s℃" % ret['feelsLike'], font_size=13)

            y = y + y_hight
            gui.draw_text(x=x1, y=y, text='环境温度', font_size=13)
            temp_here_obj = gui.draw_text(x=x2, y=y, text="%0.1f℃" % temp, font_size=13)

            y = y + y_hight
            gui.draw_text(x=x1, y=y, text='环境湿度', font_size=13)
            humi_here_obj = gui.draw_text(x=x2, y=y, text="%0.1f%%" % humi, origin='top_left', font_size=13)

            y = y + y_hight + 10
            gui.draw_text(x=x1, y=y, text='天气状况', font_size=13)
            text_obj = gui.draw_text(x=x2, y=y, text=ret['text'], font_size=13)

            # 天气图标
            icon_file = "%s/%s.svg.png" % (weather_icon_dir, ret['icon'])
            icon_obj = gui.draw_image(x=180, y=y, w=32, h=32, image=Image.open(icon_file))

            y = y + y_hight
            gui.draw_text(x=x1, y=y, text='风向', font_size=13)
            windDir_obj = gui.draw_text(x=x2, y=y, text=ret['windDir'], font_size=13)

            y = y + y_hight
            gui.draw_text(x=x1, y=y, text='风力', font_size=13)
            windScale_obj = gui.draw_text(x=x2, y=y, text="%s级" % ret['windScale'], font_size=13)

            y = y + y_hight
            gui.draw_text(x=x1, y=y, text='风速', font_size=13)
            windSpeed_obj = gui.draw_text(x=x2, y=y, text="%sm/s" % ret['windSpeed'], font_size=13)

            y = y + y_hight
            gui.draw_text(x=x1, y=y, text='相对湿度', font_size=13)
            humidity_obj = gui.draw_text(x=x2, y=y, text="%s%%" % ret['humidity'], font_size=13)

            y = y + y_hight
            gui.draw_text(x=x1, y=y, text='大气压强', font_size=13)
            pressure_obj = gui.draw_text(x=x2, y=y, text="%0.1fkPa" % (float(ret['pressure'])/1000), font_size=13)

            y = y + y_hight
            gui.draw_text(x=x1, y=y, text='能见度', font_size=13)
            vis_obj = gui.draw_text(x=x2, y=y, text="%s米" % ret['vis'], font_size=13)

            y = y + y_hight
            gui.draw_text(x=x1, y=y, text='云量', font_size=13)
            cloud_obj = gui.draw_text(x=x2, y=y, text="%s%%" % ret['cloud'], font_size=13)

            y = y + y_hight + 10
            time_obj = gui.draw_digit(x=20, y=y, color="red", font_size=12, text=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))

            weather_api_time = time.time()
            while True:
                if isEnd:
                    sys.exit(0)
                    break

                time_obj.config(text=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
                # 读取环境温湿度信息
                sht31.readTemperatureAndHumidity(0)
                temp = sht31.temp_c()
                humi = sht31.humidity()
                temp_here_obj.config(text="%0.1f℃" % temp)
                humi_here_obj.config(text="%0.1f%%" % humi)

                if time.time() - weather_api_time > 60:
                    # 获取网络天气信息
                    weather_api_time = time.time()
                    ret = get_weather(weather_type[0], LocationID, LocationName)
                    if not ret == False:
                        icon_file = "%s/%s.svg.png" % (weather_icon_dir, ret['icon'])
                        icon_obj.config(image=Image.open(icon_file))

                        temp_obj.config(text="%s℃" % ret['temp'])
                        feelsLike_obj.config(text="%s℃" % ret['feelsLike'])
                        text_obj.config(text=ret['text'])
                        windDir_obj.config(text=ret['windDir'])
                        windScale_obj.config(text="%s级" % ret['windScale'])
                        windSpeed_obj.config(text="%sm/s" % ret['windSpeed'])
                        humidity_obj.config(text="%s%%" % ret['humidity'])
                        pressure_obj.config(text="%0.1fkPa" % (float(ret['pressure'])/1000))
                        vis_obj.config(text="%s米" % ret['vis'])
                        cloud_obj.config(text="%s%%" % ret['cloud'])

                time.sleep(1)

 

上述代码的逻辑,从上往下,比较明了,主要为:

  • 调用相关的库
  • 定义天气信息相关参数
  • 定义函数:
    • 定义获取geo信息的函数
    • 定义获取天气信息的函数
    • 定义按键处理函数
  • 主调用:
    • 初始化板子
    • 初始化显示屏,设置按键监听
    • 在屏幕上显示背景图片,以及标题信息
    • 读取SHT31的数据并显示
    • 获取设定地区的地理信息
    • 获取对应LocationID地区的实时天气信息并显示
    • 循环
      • 更新时间
      • 更新SHT31的温湿度信息并更新环境温湿度信息
      • 检测当前时间,如果与上一次获取天气信息相比,如果超过1分钟,则再次获取
        • 获取成功后,更新显示的天气信息
      • 延时1秒

八、实际作品

将上述代码保存到/root/projects/weather/weather.py中,就可以在行空板上,通过屏幕操作,直接使用了。

 

1. 在行空板上操作,通过切换应用程序,进入实时天气信息牌的工作目录:

 

2. 在目录中,运行weather.py

 

  

3. 执行成功后的界面

 

 

4. 退出程序

按A或者B键,程序会退出,并显示命令行输出的信息:

 

5 视频操作演示

行空板实时天气信息牌

 

九、总结

这个实时天气信息牌,通过网络与本地传感器的结合,通过行空板pinpng硬件库的支持和GUI图形库的支持,得以简单而又完美的实现。

行空板,真的是系出名门。不仅是学习Python物联网编程的神器,同样也是Python学习的神器,人人都值得拥有一块!!!

最新回复

这不管是排版还是应用都是一级棒,不过也得是好板子才能做的这么好,是一款学习的好开发板!   详情 回复 发表于 2022-11-11 16:10
点赞 关注(2)

回复
举报

1万

帖子

25

TA的资源

版主

沙发
 

这个不错

 
 

回复

6960

帖子

11

TA的资源

版主

板凳
 

行空板,真的是系出名门。不仅是学习Python物联网编程的神器,同样也是Python学习的神器,人人都值得拥有一块!!!

非常优秀的帖子!

 
 
 

回复

7158

帖子

2

TA的资源

版主

4
 

这不管是排版还是应用都是一级棒,不过也得是好板子才能做的这么好,是一款学习的好开发板!

 
 
 

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

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

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

 
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
快速回复 返回顶部 返回列表