写点什么

树莓派上的温湿度环境监控

用户头像
冯骐
关注
发布于: 2021 年 03 月 01 日
树莓派上的温湿度环境监控

前言


前阵子入了一个树莓派,作为一个尽责(苦逼)的 IT 运维狗,自然想到拿这玩意来做做看看温湿度的环境监控了。

想法很简单,找点传感器接上树莓派,通过 GPIO 读取到传感器的数据。然后推送进监控系统即刻(比如 Open-Falcon)


传感器


温湿度的传感器种类很多,选了比较常见的 3 种来测试。


  • DHT11

  • DHT22

  • DS18B20


先对比下参数


参数


所以基本上就是:

DHT11 最渣但是最便宜

DHT22 比较给力但是贵

DS18B20 便宜且给力,但只有温度没有湿度


因为传感器连接时都需要接一个上拉电阻,所以直接买人家做好的模块比较方便,电阻给你内置接好了,直接连线比较无脑


接线


这是树莓派的 GPIO 图:



更详细的例图:



既然用的是模块,接线就很简单了,VCC 接电,GND 接地,DATA 接 GPIO 就好了,这是示意图,实际电阻已经内置在模块里了



数据读取


虽说树莓派本身已经集成了 RPi.GPIO,可以很方便的来操作 GPIO 获取数据。但是直接通过 GPIO 读取还是太麻烦了,好在轮子总是会有的~


DHT 系列


轮子

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


有轮子了这事情就非常好办,首先把轮子弄下来~


sudo apt-get updatesudo apt-get install build-essential python-dev
git clone https://github.com/adafruit/Adafruit_Python_DHTcd Adafruit_Python_DHT
sudo python setup.py install
复制代码

读取数据超级简单


import Adafruit_DHT
sensor1 = Adafruit_DHT.DHT11humidity1, temperature1 = Adafruit_DHT.read_retry(sensor1, 26)#26 是 GPIO 的引脚编号print humidity1,temperature1
sensor2 = Adafruit_DHT.DHT22humidity2, temperature2 = Adafruit_DHT.read_retry(sensor2, 13)#13 是 GPIO 的引脚编号print humidity2,temperature2
复制代码
DS18B20


DS18B20 更加直接,树莓派已经自带了 1-Wire 的驱动,只要把他开起来就好了~

先更新下内核


sudo apt-get updatesudo apt-get upgrade
复制代码

检查一下 1-Wire 模块是否开启


root@raspberrypi:/etc# lsmod | grep w1w1_therm                6401  0w1_gpio                 4818  0wire                   32619  2 w1_gpio,w1_therm
复制代码

如果没有,开启 1-Wire 模块


sudo modprobe w1_gpiosudo modprobe w1_therm
复制代码

修改/boot/config.txt 配置文件,增加 dtoverlay=w1-gpio,gpiopin=19,pullup=on

默认用的是 4 号口,如果你没有接在 4 号口上的话,要人工指定,例如我这里写的 19 号口。

这里的参数详细可以看 /boot/overlays/README,里面有详细说明


Name:   w1-gpioInfo:   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-pullupInfo: 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_slavef3 01 4b 46 7f ff 0c 10 17 : crc=17 YESf3 01 4b 46 7f ff 0c 10 17 t=31187
复制代码

31187/1000 就是当前的温度,也就是 31.187


读取这个东西当然是相当容易的事情了,然而它还是有轮子的~


pip install w1thermsensor
复制代码

有轮子又何必自己动手叻,读取数据之~


from w1thermsensor import W1ThermSensor
sensor = W1ThermSensor(W1ThermSensor.THERM_SENSOR_DS18B20, "031561d43aff")temperatur = sensor.get_temperature()print temperature
复制代码

纳入监控系统


传感器能够工作之后,我就要把他纳入到我们的监控系统里去了。绘图,告警这就是监控系统的工作了,我们需要做的是把数据给它。


监控系统获取数据通常可以分为 PULL(拉)和 PUSH(推)两种模式。实际上就是看谁更主动一些,


  • PULL 模式里,我们把数据以接口方式暴露出来,由监控系统来主动拉走

  • PUSH 模式里,监控系统提供数据的推送接口,我们主动的对数据进行封装,推送给监控系统


PULL 模式


PULL 的模式会比较通用一些。无论是用哪一个监控系统,反正我数据就在这里,拿走自己处理就是。如果这个东西要做成个通用产品的话,那大抵是要做成 PULL 的模式来主动暴露接口的。


我们用 flask 简单的封装个 http 的接口,先装一下 flask


pip install flask
复制代码

因为读取传感器的数据还是要花点时间的,我们肯定不希望每次请求接口数据的时候都去读一次传感器。所以先弄个脚本定期的把传感器的数据读出来,json 格式存在本地就好了。


#!/usr/bin/pythonimport Adafruit_DHTimport jsonimport copyfrom w1thermsensor import W1ThermSensor
sensor1 = Adafruit_DHT.DHT11humidity_dht11, temperature_dht11 = Adafruit_DHT.read_retry(sensor1, 26)
sensor2 = Adafruit_DHT.DHT22humidity_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 里,这个脚本每分钟运行一次,这样我们的数据延迟也就是 1 分钟而已,完全可以接受。


现在通过 flask 来封装一个简单的 http 接口


#!/usr/bin/python# -*- coding: utf-8 -*-
from flask import Flask,jsonifyimport 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> bash44 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 的模式需要根据我们所使用的监控系统,来封装数据格式进行主动的推送。对于特定的监控系统而言,这种模式更为简单一些。以 Open-Falcon 为例,我写了 3 个脚本对应不同的传感器模块,主动把数据推送给 Open-Falcon


  • dht11


import Adafruit_DHTimport timeimport jsonimport requestsimport 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/pythonimport Adafruit_DHTimport timeimport jsonimport requestsimport 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/pythonimport timeimport jsonimport requestsimport copyfrom 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: 你大概已经发现了,这里主动 Push 的地址是本地的 127.0.0.1~,也就是说这里的 Open-Falcon 其实也是装在树莓派上的~~这事下回再说


看下三个传感器的数据绘图



dht11 的误差确实可能要大一些,也没有到无法接受的程度,便宜嘛


生产环境


目前为止这个还只是个玩具,要进入生产环境真的拿来用的话,还需要解决一些问题


PoE 供电

可以通过 PoE 分离器来搞定,淘宝上 20~30 块钱一个

console tty

安装地方可能没有 dhcp,你得静态给树莓派配地址。总不能出门都带个屏幕和 hdmi 线吧。得让他支持 console,出去串口一接完事。可以用 PL2303 这样的 USB 转 TTL 芯片,4-5 块钱一个

外壳

拖着一堆杜邦线在外面肯定是太丑了,壳子得把线藏一藏,弄整洁一点。这得费点功夫

走线

如果考虑监控机柜温度的话。在多机柜的机房里,肯定是要 1 台树莓派拖多个传感器挂在机柜里面。走线或许可以考虑直接用网线拉走,焊在针脚上(或者直接绝缘胶布一缠~)


这些问题,留到下回做个原型机出来再说吧~


以上


原文于 2017 年 8 月首发于简书,搬家存档。

行文有微调。

发布于: 2021 年 03 月 01 日阅读数: 20
用户头像

冯骐

关注

教育行业码农 2020.06.19 加入

一个教育行业的码农

评论

发布
暂无评论
树莓派上的温湿度环境监控