空氣質量指數
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 服務器上的應用: