空气质量指数
2015年8月從太空中看加州野火與濃煙,造成空气质量指数變差,影響居民健康。
空气质量指数(英語:Air Quality Index, AQI)是定量描述空气质量状况的非线性无量纲指数。其数值越大、级别和类别越高、表征颜色越深,说明空气污染状况越严重,对人体的健康危害也就越大。
由于颗粒物没有小时浓度标准,基于24小时平均浓度计算的AQI相对于空气质量的小时变化会存在一定的滞后性,因此,当首要污染物为PM2.5和PM10时,在看AQI的同时还要兼顾其实时浓度数据。相关单位为弥补滞后性,同时发布了“实时空气质量指数”,所有污染物均采用当前1小时平均浓度计算。要注意“实时空气质量指数”不是AQI。[1]
需要说明的是,AQI的计算结果很大程度上取决于相应地区空气质量分指数及对应的污染物项目浓度指数表,最终的计算结果需要参考相应的浓度指数表才具有实际意义。 对于中国,AQI与原来发布的空气污染指数(API)有着很大的区别。AQI分级计算参考的标准是GB 3095-2012《环境空气质量标准》(现行),参与评价的污染物为SO2、NO2、PM10、PM2.5、O3、CO等六项,每小时发布一次;而API分级计算参考的标准是GB 3095-1996《环境空气质量标准》(已作废),评价的污染物仅为SO2、NO2和PM10等三项,每天发布一次。因此,AQI采用的标准更严、污染物指标更多、发布频次更高,其评价结果也将更加接近公众的真实感受。
在这里插入图片描述
详细的参考:https://zh.wikipedia.org/wiki/%E7%A9%BA%E6%B0%94%E8%B4%A8%E9%87%8F%E6%8C%87%E6%95%B0

前提条件:
1.树莓派设备(3代或4代)1个 (python2.7.5, 本来想用python3.7.3,结果serial 总是报错,无奈之下,先用python2.7.5 实现.

2.SDS011 激光测量PM2.5模块一个 可以从淘宝购买 https://item.taobao.com/item.htm?spm=a1z09.2.0.0.56a42e8d8ACbpD&id=526375973012&_u=fdmuca12e

3.部署ThingsBoard的虚拟机一个,如果已安装docker环境,这样部署简单.

4.一个部署好的Thingsboard (请参考 https://blog.csdn.net/happyfreeangel/article/details/102473547)

5.工作用电脑一台,支持ssh, python开发。

SDS011使用激光检测原理,能够得到空气中0.3 ~ 10 微米悬浮颗粒物浓度,数据稳定可靠;内置风扇,数字化输出,集成度高。

第一步: 连接设备
在这里插入图片描述
第二步: 在Thingsboard上创建一个SDS011设备对象

在这里插入图片描述
在这里插入图片描述
复制下设备ID和token 后面的代码编写会用到.

第三步: 编写程序代码,用来运行在树莓派设备上,周期性的采集SDS011设备的数据,然后上传Thingsboard.

#!/usr/bin/python -u
# coding=utf-8
# "DATASHEET": http://cl.ly/ekot
# https://gist.github.com/kadamski/92653913a53baf9dd1a8
from __future__ import print_function
import serial, struct, sys, time, json, subprocess

DEBUG = 0
CMD_MODE = 2
CMD_QUERY_DATA = 4
CMD_DEVICE_ID = 5
CMD_SLEEP = 6
CMD_FIRMWARE = 7
CMD_WORKING_PERIOD = 8
MODE_ACTIVE = 0
MODE_QUERY = 1
PERIOD_CONTINUOUS = 0

JSON_FILE = '/var/server/pm2.5/www/aqi.json'

MQTT_HOST = '10.6.0.5'
MQTT_PORT = 1883
MQTT_TOPIC = 'v1/devices/me/telemetry'
DEVICE_ACCESS_TOKEN="SDS011_DEMO_TOKEN"


ser = serial.Serial()
ser.port = "/dev/ttyUSB0"
ser.baudrate = 9600

ser.open()
ser.flushInput()

byte, data = 0, ""

import paho.mqtt.client as mqtt
from time import sleep
import random
import math

# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, rc, *extra_params):
   print('connection success code= '+str(rc))


# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
   print('topic: ' + msg.topic + '\nmessage: ' + str(msg.payload))


def dump(d, prefix=''):
    print(prefix + ' '.join(x.encode('hex') for x in d))

def construct_command(cmd, data=[]):
    assert len(data) <= 12
    data += [0,]*(12-len(data))
    checksum = (sum(data)+cmd-2)%256
    ret = "\xaa\xb4" + chr(cmd)
    ret += ''.join(chr(x) for x in data)
    ret += "\xff\xff" + chr(checksum) + "\xab"

    if DEBUG:
        dump(ret, '> ')
    return ret

def process_data(d):
    r = struct.unpack('<HHxxBB', d[2:])
    pm25 = r[0]/10.0
    pm10 = r[1]/10.0
    checksum = sum(ord(v) for v in d[2:8])%256
    return [pm25, pm10]
    #print("PM 2.5: {} μg/m^3  PM 10: {} μg/m^3 CRC={}".format(pm25, pm10, "OK" if (checksum==r[2] and r[3]==0xab) else "NOK"))

def process_version(d):
    r = struct.unpack('<BBBHBB', d[3:])
    checksum = sum(ord(v) for v in d[2:8])%256
    print("Y: {}, M: {}, D: {}, ID: {}, CRC={}".format(r[0], r[1], r[2], hex(r[3]), "OK" if (checksum==r[4] and r[5]==0xab) else "NOK"))

def read_response():
    byte = 0
    while byte != "\xaa":
        byte = ser.read(size=1)

    d = ser.read(size=9)

    if DEBUG:
        dump(d, '< ')
    return byte + d

def cmd_set_mode(mode=MODE_QUERY):
    ser.write(construct_command(CMD_MODE, [0x1, mode]))
    read_response()

def cmd_query_data():
    ser.write(construct_command(CMD_QUERY_DATA))
    d = read_response()
    values = []
    if d[1] == "\xc0":
        values = process_data(d)
    return values

def cmd_set_sleep(sleep):
    mode = 0 if sleep else 1
    ser.write(construct_command(CMD_SLEEP, [0x1, mode]))
    read_response()

def cmd_set_working_period(period):
    ser.write(construct_command(CMD_WORKING_PERIOD, [0x1, period]))
    read_response()

def cmd_firmware_ver():
    ser.write(construct_command(CMD_FIRMWARE))
    d = read_response()
    process_version(d)

def cmd_set_id(id):
    id_h = (id>>8) % 256
    id_l = id % 256
    ser.write(construct_command(CMD_DEVICE_ID, [0]*10+[id_l, id_h]))
    read_response()

def pub_mqtt(jsonrow):
    #mosquitto_pub -d -h "127.0.0.1" -t "v1/devices/me/telemetry" -u "$ACCESS_TOKEN" -f "telemetry-data-with-ts.json"
    cmd = ['mosquitto_pub', '-d' '-h', MQTT_HOST, '-t', MQTT_TOPIC, '-u',ACCESS_TOKEN,'-f',JSON_DATA, '-s']
    #cmd = ['mosquitto_pub', '-h', MQTT_HOST, '-t', MQTT_TOPIC, '-s']
    print('Publishing using:', cmd)
    with subprocess.Popen(cmd, shell=False, bufsize=0, stdin=subprocess.PIPE).stdin as f:
        json.dump(jsonrow, f)


if __name__ == "__main__":
    cmd_set_sleep(0)
    cmd_firmware_ver()
    cmd_set_working_period(PERIOD_CONTINUOUS)
    cmd_set_mode(MODE_QUERY);

    MQTT_HOST = '10.6.0.5'
    MQTT_PORT = 1883
    MQTT_TOPIC = 'v1/devices/me/telemetry'
    DEVICE_ACCESS_TOKEN = "SDS011_DEMO_TOKEN"

    client = mqtt.Client()
    client.on_connect = on_connect
    client.on_message = on_message
    # client.publish('v1/devices/me/attributes/request/1', "{\"clientKeys\":\"model\"}", 1)

    client.username_pw_set(DEVICE_ACCESS_TOKEN)

    # connect(self, host, port=1883, keepalive=60, bind_address=""
    client.connect(THINGSBOARD_HOST, MQTT_PORT, 1)

    # client.loop_forever()

    client.loop_start();
    sleep(1)

    while True:
        cmd_set_sleep(0)
        for t in range(15):
            values = cmd_query_data();
            if values is not None and len(values) == 2:
              print("PM2.5: ", values[0], ", PM10: ", values[1])
              time.sleep(2)

        # open stored data
        try:
            with open(JSON_FILE) as json_data:
                data = json.load(json_data)
        except IOError as e:
            data = []
            print(str(e))

        # check if length is more than 100 and delete first element
        if len(data) > 100:
            data.pop(0)

        # append new values
        jsonrow = {'pm25': values[0], 'pm10': values[1], 'time': time.strftime("%Y-%m-%d %H:%M:%S")}
        data.append(jsonrow)

        # save it
        with open(JSON_FILE, 'w') as outfile:
            json.dump(data, outfile)

        if MQTT_HOST != '':
            #pub_mqtt(jsonrow)
            #TELEMETRY = '{\"device_status\":'+str(deviceStatus)+',\"electric_quantity\":'+str(electric_quantity)+'}'
            sampleData = '{\"pm25\":'+ str(values[0])+', \"pm10\":'+ str(values[1])+', \"time\":'+ time.strftime("%Y-%m-%d %H\:%M\:%S")+'}'
            client.publish('v1/devices/me/telemetry', sampleData);
            
        print("Going to sleep for 1 min...")
        cmd_set_sleep(1)
        time.sleep(60)


第四步: 发布服务
把设备设置为 public
在这里插入图片描述
点击左侧菜单:在这里插入图片描述 仪表板库, 然后点击右下角的 在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后点击添加

然后点击 PM2.5 图标,进入

点击添加新的部件
在这里插入图片描述
选择部件 Charts 在这里插入图片描述
选择时间系列:
在这里插入图片描述

然后添加 数据源
在这里插入图片描述
输入 pm数据, 点击 旁边的 创建新别名,
在这里插入图片描述
然后,
在这里插入图片描述
在这里插入图片描述
点击右下角的 中间的橘色圆圈(白色勾)的这个应用更改。

点击 编辑–>设置–>图例设置 勾选 如下图所示
同时把标题修改为你自己的标题, 然后应用修改(橘色的带白色勾的这个圆圈)点击一下。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
点击下图的中间这个图标在这里插入图片描述
将仪表盘设置为公开,
在这里插入图片描述
在这里插入图片描述

点击右边的复制按钮(那个带往左边箭头的)

然后打开浏览器:
URL 如下:
http://10.6.0.5:9090/dashboard/842e8880-efd7-11e9-abb4-1507bf9e26ec?publicId=dd0aeaf0-ef16-11e9-abb4-1507bf9e26ec

浏览器打开如下:

在这里插入图片描述

至此 服务发布成功.

这时你可以查看AQI 探测器的属性和遥测数据
在这里插入图片描述
在这里插入图片描述

下面是一个web 应用接收来自刚才的Thingsboard上的实时PM2.5数据,展示效果截图。

实例:web 服务器上的应用:
在这里插入图片描述

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐