樹莓派上的溫濕度環(huán)境監(jiān)控

前言

前陣子入了一個樹莓派盔腔,作為一個盡責(zé)(苦逼)的IT運維狗闸翅,自然想到拿這玩意來做做看看溫濕度的環(huán)境監(jiān)控了贵扰。
想法很簡單,找點傳感器接上樹莓派奠衔,通過 GPIO 讀取到傳感器的數(shù)據(jù)谆刨。然后推送進監(jiān)控系統(tǒng)即刻(比如 Open-Falcon)

傳感器

溫濕度的傳感器種類很多,選了比較常見的3種來測試归斤。

  • DHT11
  • DHT22
  • DS18B20

先對比下參數(shù)

參數(shù) DHT11 DHT22 DS18B20
溫度測量范圍 0 ~ +50(°C) -40 ~ +80(°C) -55 ~ +125(°C)
溫度誤差 ±2°C ±0.5°C ±0.5°C(-10 ~ +85(°C)內(nèi))
濕度范圍 20 ~ 95(%RH) 0 ~ 100(%RH)
濕度誤差 ±5%RH ±2%RH
工作電壓 3.3 ~ 5(V) 3.3 ~ 5(V) 3.0 ~ 5.5(V)
模塊參考價格(淘寶) 5¥ 20¥ 6¥

所以基本上就是:
DHT11 最渣但是最便宜
DHT22 比較給力但是貴
DS18B20 便宜且給力痊夭,但只有溫度沒有濕度

因為傳感器連接時都需要接一個上拉電阻,所以直接買人家做好的模塊比較方便脏里,電阻給你內(nèi)置接好了她我,直接連線比較無腦

接線

這是樹莓派的 GPIO 圖:

image.png

更詳細的例圖:

image.png

既然用的是模塊,接線就很簡單了迫横,VCC 接電番舆,GND 接地,DATA 接 GPIO 就好了矾踱,這是示意圖恨狈,實際電阻已經(jīng)內(nèi)置在模塊里了

image.png

數(shù)據(jù)讀取

雖說樹莓派本身已經(jīng)集成了 RPi.GPIO,可以很方便的來操作 GPIO 獲取數(shù)據(jù)介返。但是直接通過 GPIO 讀取還是太麻煩了拴事,好在輪子總是會有的~

DHT 系列

輪子
https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code

有輪子了這事情就非常好辦,首先把輪子弄下來~

sudo apt-get update
sudo apt-get install build-essential python-dev

git clone https://github.com/adafruit/Adafruit_Python_DHT
cd Adafruit_Python_DHT

sudo python setup.py install

讀取數(shù)據(jù)超級簡單

import Adafruit_DHT

sensor1 = Adafruit_DHT.DHT11
humidity1, temperature1 = Adafruit_DHT.read_retry(sensor1, 26)#26 是 GPIO 的引腳編號
print humidity1,temperature1

sensor2 = Adafruit_DHT.DHT22
humidity2, temperature2 = Adafruit_DHT.read_retry(sensor2, 13)#13 是 GPIO 的引腳編號
print humidity2,temperature2
DS18B20

DS18B20 更加直接圣蝎,樹莓派已經(jīng)自帶了 1-Wire 的驅(qū)動刃宵,只要把他開起來就好了~
先更新下內(nèi)核

sudo apt-get update
sudo apt-get upgrade

檢查一下 1-Wire 模塊是否開啟

root@raspberrypi:/etc# lsmod | grep w1
w1_therm                6401  0
w1_gpio                 4818  0
wire                   32619  2 w1_gpio,w1_therm

如果沒有,開啟 1-Wire 模塊

sudo modprobe w1_gpio
sudo modprobe w1_therm

修改/boot/config.txt 配置文件徘公,增加 dtoverlay=w1-gpio,gpiopin=19,pullup=on
默認(rèn)用的是 4 號口牲证,如果你沒有接在 4 號口上的話,要人工指定关面,例如我這里寫的 19 號口坦袍。
這里的參數(shù)詳細可以看 /boot/overlays/README十厢,里面有詳細說明

Name:   w1-gpio
Info:   Configures the w1-gpio Onewire interface module.
        Use this overlay if you *don't* need a GPIO to drive an external pullup.
Load:   dtoverlay=w1-gpio,<param>=<val>
Params: gpiopin                 GPIO for I/O (default "4")

        pullup                  Non-zero, "on", or "y" to enable the parasitic
                                power (2-wire, power-on-data) feature


Name:   w1-gpio-pullup
Info:   Configures the w1-gpio Onewire interface module.
        Use this overlay if you *do* need a GPIO to drive an external pullup.
Load:   dtoverlay=w1-gpio-pullup,<param>=<val>
Params: gpiopin                 GPIO for I/O (default "4")

        pullup                  Non-zero, "on", or "y" to enable the parasitic
                                power (2-wire, power-on-data) feature

        extpullup               GPIO for external pullup (default "5")

配好以后重啟,然后就可以看到我們的傳感器了

root@raspberrypi:/etc# ls /sys/bus/w1/devices/
28-0516a718e1ff  w1_bus_master1

查看傳感器的溫度

root@raspberrypi:/etc# cat /sys/bus/w1/devices/28-0516a718e1ff//w1_slave
f3 01 4b 46 7f ff 0c 10 17 : crc=17 YES
f3 01 4b 46 7f ff 0c 10 17 t=31187

31187/1000 就是當(dāng)前的溫度捂齐,也就是 31.187

讀取這個東西當(dāng)然是相當(dāng)容易的事情了蛮放,然而它還是有輪子的~

pip install w1thermsensor

有輪子又何必自己動手叻,讀取數(shù)據(jù)之~

from w1thermsensor import W1ThermSensor

sensor = W1ThermSensor(W1ThermSensor.THERM_SENSOR_DS18B20, "031561d43aff")
temperatur = sensor.get_temperature()
print temperature

納入監(jiān)控系統(tǒng)

傳感器能夠工作之后奠宜,我就要把他納入到我們的監(jiān)控系統(tǒng)里去了包颁。繪圖,告警這就是監(jiān)控系統(tǒng)的工作了压真,我們需要做的是把數(shù)據(jù)給它娩嚼。

監(jiān)控系統(tǒng)獲取數(shù)據(jù)通常可以分為 PULL(拉)和 PUSH(推)兩種模式滴肿。實際上就是看誰更主動一些岳悟,

  • PULL 模式里,我們把數(shù)據(jù)以接口方式暴露出來泼差,由監(jiān)控系統(tǒng)來主動拉走
  • PUSH 模式里贵少,監(jiān)控系統(tǒng)提供數(shù)據(jù)的推送接口,我們主動的對數(shù)據(jù)進行封裝拴驮,推送給監(jiān)控系統(tǒng)
PULL 模式

PULL 的模式會比較通用一些春瞬。無論是用哪一個監(jiān)控系統(tǒng),反正我數(shù)據(jù)就在這里套啤,拿走自己處理就是宽气。如果這個東西要做成個通用產(chǎn)品的話,那大抵是要做成 PULL 的模式來主動暴露接口的潜沦。

我們用 flask 簡單的封裝個 http 的接口萄涯,先裝一下 flask

pip install flask

因為讀取傳感器的數(shù)據(jù)還是要花點時間的,我們肯定不希望每次請求接口數(shù)據(jù)的時候都去讀一次傳感器唆鸡。所以先弄個腳本定期的把傳感器的數(shù)據(jù)讀出來涝影,json 格式存在本地就好了。

#!/usr/bin/python
import Adafruit_DHT
import json
import copy
from w1thermsensor import W1ThermSensor

sensor1 = Adafruit_DHT.DHT11
humidity_dht11, temperature_dht11 = Adafruit_DHT.read_retry(sensor1, 26)

sensor2 = Adafruit_DHT.DHT22
humidity_dht22, temperature_dht22 = Adafruit_DHT.read_retry(sensor2, 13)

sensor = W1ThermSensor(W1ThermSensor.THERM_SENSOR_DS18B20, "0516a718e1ff")
temperature_ds18b20 = sensor.get_temperature()

env = []
if humidity_dht11 is not None and temperature_dht11 is not None:
        data = {"metric":"humidity","tag":"module=dht11","value":humidity_dht11}
        env.append(copy.copy(data))
        data = {"metric":"temperature","tag":"module=dht11","value":temperature_dht11}
        env.append(copy.copy(data))
if humidity_dht22 is not None and temperature_dht22 is not None:
        data = {"metric":"humidity","tag":"module=dht22","value":humidity_dht22}
        env.append(copy.copy(data))
        data = {"metric":"temperature","tag":"module=dht22","value":temperature_dht22}
        env.append(copy.copy(data))
if temperature_ds18b20 is not None:
        data = {"metric":"temperature","tag":"module=ds18b20","value":temperature_ds18b20}
        env.append(copy.copy(data))
if len(env) > 0:
        with open("/opt/falcon-scripts/env.json", 'w') as f:
                f.write(json.dumps(env))

放入 crontab 里争占,這個腳本每分鐘運行一次燃逻,這樣我們的數(shù)據(jù)延遲也就是 1 分鐘而已,完全可以接受臂痕。

現(xiàn)在通過 flask 來封裝一個簡單的 http 接口

#!/usr/bin/python
# -*- coding: utf-8 -*-

from flask import Flask,jsonify
import json

app = Flask(__name__)

@app.route('/env', methods=['GET'])

def env():
    with open('/opt/falcon-scripts/env.json', 'r') as f:
        env_json = f.read()
    env_data = json.loads(env_json)
    return jsonify(env=env_data)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=80, debug=True)

跑起來

root@raspberrypi:/opt/flask# python env.py
 * Running on http://0.0.0.0:80/
 * Restarting with reloader


測試一下

PS C:\Users\qfeng> bash
44 packages can be updated.
29 updates are security updates.
qfeng@QFENG-PC:/mnt/c/Users/qfeng$
qfeng@QFENG-PC:/mnt/c/Users/qfeng$
qfeng@QFENG-PC:/mnt/c/Users/qfeng$
qfeng@QFENG-PC:/mnt/c/Users/qfeng$ curl http://192.168.2.221/env
{
  "env": [
    {
      "metric": "humidity",
      "tag": "module=dht11",
      "value": 68.0
    },
    {
      "metric": "temperature",
      "tag": "module=dht11",
      "value": 32.0
    },
    {
      "metric": "humidity",
      "tag": "module=dht22",
      "value": 70.9000015258789
    },
    {
      "metric": "temperature",
      "tag": "module=dht22",
      "value": 30.399999618530273
    },
    {
      "metric": "temperature",
      "tag": "module=ds18b20",
      "value": 31.125
    }
  ]
}qfeng@QFENG-PC:/mnt/c/Users/qfeng$

看起來不錯

PUSH 模式

PUSH 的模式需要根據(jù)我們所使用的監(jiān)控系統(tǒng)伯襟,來封裝數(shù)據(jù)格式進行主動的推送。對于特定的監(jiān)控系統(tǒng)而言握童,這種模式更為簡單一些姆怪。以 Open-Falcon 為例,我寫了 3 個腳本對應(yīng)不同的傳感器模塊,主動把數(shù)據(jù)推送給 Open-Falcon

  • dht11
import Adafruit_DHT
import time
import json
import requests
import copy

if __name__ == '__main__':
    sensor = Adafruit_DHT.DHT11
    humidity, temperature = Adafruit_DHT.read_retry(sensor, 26)
    ts = int(time.time())
    push_url = "http://127.0.0.1:1988/v1/push"
    payload = []
    if humidity is not None:
        humidity_data = {"endpoint":"home","metric":"room.humidity","timestamp":ts,"step":60,"value":humidity,"counterType":"GAUGE","tags":"module=dht11"}
        payload.append(copy.copy(humidity_data))
    if temperature is not None:
        temperature_data = {"endpoint":"home","metric":"room.temperature","timestamp":ts,"step":60,"value":temperature,"counterType":"GAUGE","tags":"module=dht11"}
        payload.append(copy.copy(temperature_data))
    r = requests.post(push_url, data=json.dumps(payload))
  • dht22
#!/usr/bin/python
import Adafruit_DHT
import time
import json
import requests
import copy

if __name__ == '__main__':
    sensor = Adafruit_DHT.DHT22
    humidity, temperature = Adafruit_DHT.read_retry(sensor, 13)
    ts = int(time.time())
    push_url = "http://127.0.0.1:1988/v1/push"
    payload = []
    if humidity is not None:
        humidity_data = {"endpoint":"home","metric":"room.humidity","timestamp":ts,"step":60,"value":humidity,"counterType":"GAUGE","tags":"module=dht22"}
        payload.append(copy.copy(humidity_data))
    if temperature is not None:
        temperature_data = {"endpoint":"home","metric":"room.temperature","timestamp":ts,"step":60,"value":temperature,"counterType":"GAUGE","tags":"module=dht22"}
        payload.append(copy.copy(temperature_data))
    r = requests.post(push_url, data=json.dumps(payload))
  • ds18b20
#!/usr/bin/python
import time
import json
import requests
import copy
from w1thermsensor import W1ThermSensor

if __name__ == '__main__':
    sensor = W1ThermSensor(W1ThermSensor.THERM_SENSOR_DS18B20, "0516a718e1ff")
    temperature = sensor.get_temperature()
    ts = int(time.time())
    push_url = "http://127.0.0.1:1988/v1/push"
    payload = []
    if temperature is not None:
        temperature_data = {"endpoint":"home","metric":"room.temperature","timestamp":ts,"step":60,"value":temperature,"counterType":"GAUGE","tags":"module=ds18b20"}
        payload.append(copy.copy(temperature_data))
    r = requests.post(push_url, data=json.dumps(payload))

PS: 你大概已經(jīng)發(fā)現(xiàn)了稽揭,這里主動 Push 的地址是本地的 127.0.0.1~俺附,也就是說這里的 Open-Falcon 其實也是裝在樹莓派上的~~這事下回再說

看下三個傳感器的數(shù)據(jù)繪圖

image.png

dht11 的誤差確實可能要大一些,也沒有到無法接受的程度溪掀,便宜嘛

生產(chǎn)環(huán)境

目前為止這個還只是個玩具事镣,要進入生產(chǎn)環(huán)境真的拿來用的話,還需要解決一些問題

  1. PoE 供電
    可以通過 PoE 分離器來搞定膨桥,淘寶上 20~30 塊錢一個
  2. console tty
    安裝地方可能沒有 dhcp蛮浑,你得靜態(tài)給樹莓派配地址≈幌總不能出門都帶個屏幕和 hdmi 線吧。得讓他支持 console艺沼,出去串口一接完事册舞。可以用 PL2303 這樣的 USB 轉(zhuǎn) TTL 芯片障般,4-5塊錢一個
  3. 外殼
    拖著一堆杜邦線在外面肯定是太丑了调鲸,殼子得把線藏一藏,弄整潔一點挽荡。這得費點功夫
  4. 走線
    如果考慮監(jiān)控機柜溫度的話藐石。在多機柜的機房里,肯定是要 1 臺樹莓派拖多個傳感器掛在機柜里面定拟。走線或許可以考慮直接用網(wǎng)線拉走于微,焊在針腳上(或者直接絕緣膠布一纏~)

這些問題,留到下回做個原型機出來再說吧~

以上

轉(zhuǎn)載授權(quán)

CC BY-SA

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末青自,一起剝皮案震驚了整個濱河市株依,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌延窜,老刑警劉巖恋腕,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異逆瑞,居然都是意外死亡荠藤,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門获高,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哈肖,“玉大人,你說我怎么就攤上這事谋减∧党梗” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長庄吼。 經(jīng)常有香客問我缎除,道長,這世上最難降的妖魔是什么总寻? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任器罐,我火速辦了婚禮,結(jié)果婚禮上渐行,老公的妹妹穿的比我還像新娘轰坊。我一直安慰自己,他們只是感情好祟印,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布肴沫。 她就那樣靜靜地躺著,像睡著了一般蕴忆。 火紅的嫁衣襯著肌膚如雪颤芬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天套鹅,我揣著相機與錄音站蝠,去河邊找鬼。 笑死卓鹿,一個胖子當(dāng)著我的面吹牛菱魔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播吟孙,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼澜倦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了拔疚?” 一聲冷哼從身側(cè)響起肥隆,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎稚失,沒想到半個月后栋艳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡句各,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年吸占,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凿宾。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡矾屯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出初厚,到底是詐尸還是另有隱情件蚕,我是刑警寧澤孙技,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站排作,受9級特大地震影響牵啦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜妄痪,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一哈雏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧衫生,春花似錦裳瘪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至泪酱,卻和暖如春历等,著一層夾襖步出監(jiān)牢的瞬間降狠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工炉爆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留憋活,地道東北人岂津。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像悦即,于是被迫代替她去往敵國和親吮成。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容