diy 多路监控_如何通过此DIY设置监控空气质量

diy 多路监控

借助Raspberry Pi,低成本气体传感器和遥控开关,您可以控制房屋的空气质量。 (With a Raspberry Pi, low-cost gas sensors, and a remote-controlled switch, you can control the air quality in your house.)

The air we breathe indoors is not always healthier than the air outside.

我们在室内呼吸的空气并不总是比室外的空气更健康。

According to a study of the E.U. Joint Research Centre, you can find a wide range of air pollutants indoors. Some of them are toxic, or can cause genetic mutations or cancer. Factors that influence indoor air quality are:

根据欧盟联合研究中心的一项研究 ,您可以在室内找到各种各样的空气污染物。 其中一些是有毒的,或者会导致基因突变或癌症。 影响室内空气质量的因素有:

  • ambient air, or the air outdoors

    环境空气或室外空气
  • air tightness and ventilation of the building

    建筑物的气密性和通风
  • indoor sources like tobacco smoke, heating gases, consumer products, etc.

    室内来源,例如烟草烟雾,加热气体,消费品等。

Do you know how much time you spend indoors? According to the Environmental Protection Agency, Americans spend 87% of their time indoors. In Europe, this average percentage is 90%, according to the JRC. And the more time we spent indoors, the more pollutants we inhale.

您知道您在室内呆了多少时间吗? 根据美国环境保护署的资料,美国人在室内度过了87%的时间 。 根据JRC的数据,在欧洲,这一平均百分比为90%。 而且,我们在室内呆的时间越长,我们吸入的污染物就越多。

So it would be interesting if we track the indoor air quality. In this article, I will explain how I did this with a Raspberry Pi, a GrovePi and some sensors. We will upload the sensor data to a Firebase database and visualize trends with Dash.

因此,如果我们跟踪室内空气质量将很有趣。 在本文中,我将说明如何使用Raspberry PiGrovePi和一些传感器来完成此操作。 我们将传感器数据上传到Firebase数据库,并使用Dash可视化趋势。

When pollutant levels reach an unhealthy level, we can send an alert notification to warn us.

当污染物水平达到不健康水平时,我们可以发送警报通知来警告我们。

Moreover, it would be great if we could automatically start up the ventilation to purify the air. This can be done with a remote-controlled switch from Energenie.

而且,如果我们能够自动启动通风来净化空气,那将是很好的。 这可以通过Energenie远程控制开关来完成。

Raspberry Pi和GrovePi (Raspberry Pi and GrovePi)

In this project, I will use a Raspberry Pi model 2B. The Raspberry Pi is a low-cost small computer. It enables programmers and makers to build anything they can imagine.

在这个项目中,我将使用Raspberry Pi 2B型。 Raspberry Pi是一款低成本的小型计算机。 它使程序员和开发人员可以构建他们可以想象的任何东西。

The GrovePi is an electronics board that we attach to the Raspberry Pi. It makes connecting a wide range of sensors easy. As such, you don’t need to bother about a breadboard, resistors, soldering or jumper wires. You can plug in a connector and start working with it.

GrovePi是我们连接到Raspberry Pi的电子板。 它使连接各种传感器变得容易。 这样,您就不必为面包板,电阻器,焊接或跨接线而烦恼。 您可以插入连接器并开始使用它。

数据流 (Data flow)

In the illustration below you see how the sensor data will flow from the sensors to the charts on a web page. We’ll be using Python to do all that.

在下图中,您将看到传感器数据如何从传感器流向网页上的图表。 我们将使用Python来完成所有这些工作。

The first step is to pull in the data from the Grove sensors. Then we process the data on the Raspberry Pi and send them to a Firebase database. This data is also used to switch on or off a ventilation unit via a remote controlled switch.

第一步是从Grove传感器中提取数据。 然后,我们在Raspberry Pi上处理数据,并将其发送到Firebase数据库。 该数据还用于通过遥控开关打开或关闭通风装置。

We can get the stored data with a script running on PythonAnywhere.com. With the Dash package, we can build a dashboard to follow up the indoor air quality.

我们可以使用在PythonAnywhere.com上运行的脚本来获取存储的数据。 借助Dash软件包,我们可以构建一个仪表板来跟踪室内空气质量。

气体传感器 (Gas sensors)

We will use a set of three different gas sensors for this project. I use Grove gas sensors and connect them to the GrovePi.

我们将在此项目中使用一组三个不同的气体传感器。 我使用Grove气体传感器并将其连接到GrovePi。

It is not necessary to use all three sensors in your own project. Feel free to choose sensors that fit your needs. You can do that in the config.py file by keeping the sensors you need in the Python dictionary MQ_SENSORS.

您不必在自己的项目中使用所有三个传感器。 随时选择适合您需求的传感器。 通过将所需的传感器保留在Python字典MQ_SENSORS.中,可以在config.py文件中执行此MQ_SENSORS.

Each sensor detects a specific set of gases. There is quite some overlap between the sensors with regard to the gases they detect. But, the range in which the sensors detect a gas can differ. You can find the ranges on the website of Seeedstudio.

每个传感器检测一组特定的气体。 关于传感器检测到的气体,传感器之间存在很多重叠。 但是,传感器检测到气体的范围可能会有所不同。 您可以在Seeedstudio网站上找到范围。

温湿度传感器 (Temperature and Humidity sensor)

Temperature and humidity influence the readings from the gas sensors. So, it is interesting to measure temperature and humidity as well. We use the Grove BME680 sensor for that.

温度和湿度会影响气体传感器的读数。 因此,测量温度和湿度也很有趣。 我们为此使用了Grove BME680传感器。

处理传感器数据 (Working with sensor data)

In short, a gas sensor will output a higher voltage when the concentration of a gas is higher. This is because the built-in resistor varies its resistance (Rs) according to the concentration of the gas.

简而言之,当气体浓度较高时,气体传感器将输出较高的电压。 这是因为内置电阻器会根据气体浓度改变其电阻(Rs)。

The sensor value only reflects an approximation of a trend in the gas concentration. This means that you can use it to show whether the gas concentration increases or decreases. It does not give the exact gas concentration. If you want to measure the actual concentration, you would need a more expensive sensor.

传感器值仅反映气体浓度趋势的近似值。 这意味着您可以使用它来显示气体浓度是增加还是减少。 它没有给出确切的气体浓度。 如果要测量实际浓度,则需要更昂贵的传感器。

For learning purposes, we will use the sensor value to approximate the gas concentration. You should keep that in mind. You should not use these scripts in real-life situations to prevent intoxication by these gases!

出于学习目的,我们将使用传感器值来估算气体浓度。 您应该记住这一点。 您不应在现实生活中使用这些脚本来防止这些气体引起的中毒!

On the datasheets listed below, you find a graph with the relation between the gas concentration and sensor resistor values. The gas concentration is expressed in parts per million (ppm). The resistor values as the ratio Rs/R0.

在下面列出的数据表上,您可以找到一张图表,其中包含气体浓度和传感器电阻值之间的关系。 气体浓度以百万分率(ppm)表示。 电阻值为Rs / R0。

The curves below correspond to the standard conditions of:

以下曲线对应于以下标准条件:

  • a temperature of 20°C

    温度20°C
  • a humidity of 65%

    湿度65%
  • an oxygen concentration of 21%

    氧气浓度为21%
  • a load resistance of 5 kilo-Ohm. The load resistance is the total resistance of an electronic circuit.

    负载电阻为5千欧姆。 负载电阻是电子电路的总电阻。

We can assume that the load resistance and oxygen concentration are stable over time. Yet, the temperature and humidity indoors can vary. Both factors have an influence on the sensor readings, as you can see in the graph below.

我们可以假设负载电阻和氧气浓度随时间稳定。 但是,室内的温度和湿度会有所不同。 如下图所示,这两个因素都会影响传感器的读数。

To get accurate readings, you should have a graph or look-up table for various temperature-humidity combinations. Unfortunately, these graphs are not provided by the manufacturer.

为了获得准确的读数,您应该具有用于​​各种温度-湿度组合的图形或查找表。 不幸的是,这些图表不是由制造商提供的。

Another approach is to correct the sensor readings for the temperature-humidity influence.

另一种方法是针对温度-湿度影响校正传感器读数。

We can do this with artificial neural networks, as proposed in the paper by Nenova & Dimchev. That approach requires ground-truth measurements of the gas concentrations. This is out of scope for this article.

正如Nenova&Dimchev论文中所提出的,我们可以使用人工神经网络来做到这一点 。 这种方法需要对气体浓度进行实地测量。 这超出了本文的范围。

定义R0值 (Defining the R0 value)

First, we need to compute the R0 value. R0 stands for the sensor resistance value of 1.000 ppm of hydrogen (H2)in clean air. The ratio for clean air (black line with blue crosses) is constant. It remains at 9.8 regardless of the concentration of clean air. So, we can compute R0 by reading the sensor value (Rs) in clean air and dividing it by 9.8.

首先,我们需要计算R0值。 R0代表清洁空气中氢气(H2)的传感器电阻值为1.000 ppm。 清洁空气的比例(黑线带有蓝色十字)是恒定的。 无论清洁空气的浓度如何,该指数均保持在9.8。 因此,我们可以通过读取清洁空气中的传感器值(Rs)并将其除以9.8来计算R0。

The value we get from the sensor is a value between 0 and 1.023 and has no measurement unit. So, to get the output voltage we divide the sensor value by 1.023. We multiply this value with the circuit voltage, which is usually 5V.

我们从传感器获得的值为0到1.023之间的值,并且没有测量单位。 因此,为了获得输出电压,我们将传感器值除以1.023。 我们将此值乘以电路电压,通常为5V。

From the sensor voltage, we can derive the sensor resistance by applying Ohm’s law. The sensor resistance is equal to

从传感器电压,我们可以通过应用欧姆定律得出传感器电阻。 传感器电阻等于

Let’s see how we do that with Python. The complete code and more documentation can be found on Github in the script get_R0_values.py.

让我们看看如何使用Python做到这一点。 完整的代码和更多文档可以在Github上的脚本get_R0_values.py

import config as cfg
import grovepi
import time

First, we import some packages. The config package is a Python script I made to store all configuration parameters.

首先,我们导入一些软件包。 config软件包是我用来存储所有配置参数的Python脚本。

Throughout the code, you’ll notice that I sometimes refer to cfg.PARAMETER_NAME. We set thePARAMETER_NAME value in that config.py file. It also contains some passwords and API tokens.

在整个代码中,您会发现我有时会引用cfg.PARAMETER_NAME 。 我们在该config.py文件中设置PARAMETER_NAME值。 它还包含一些密码和API令牌。

For security reasons, I will not save that file on Github. Instead, I’ll provide a clean template config_template.py that you can use for your own project.

出于安全原因,我不会将该文件保存在Github上 。 相反,我将提供一个干净的模板config_template.py ,您可以将其用于自己的项目。

Next, we import the grovepi package. You can install it from the Github page of Dexter Industries on your Raspberry Pi. It allows us to work with the GrovePi and the connected sensors.

接下来,我们导入grovepi包。 您可以从Raspberry Pi 上的Dexter IndustriesGithub页面安装它。 它使我们可以使用GrovePi和连接的传感器。

Finally, we use the time package to pause the program during sensor readings.

最后,我们使用time包在传感器读数期间暂停程序。

mq_values = {}

for sensor, data in cfg.MQ_SENSORS.items():
    grovepi.pinMode(data['pin'],"INPUT")
    mq_values[sensor] = 0

We will store the sensor values for the different sensors in the dictionary mq_values. But first, we initialize the values to zero in a loop over the sensors defined in MQ_SENSORS.

我们将在字典mq_values存储不同传感器的传感器值。 但是首先,我们在MQ_SENSORS定义的传感器上循环将值初始化为零。

With the pinMode method, you can define the pin as INPUT or OUTPUT. In our case, we’ll use it as INPUT.

使用pinMode方法,可以将引脚定义为INPUTOUTPUT. 在本例中,我们将其用作INPUT

The pin tells us to which pin on the GrovePi the sensor is connected. We use the analog pins (or ports) which are labeled as A0, A1 and A2 on the GrovePi. Make sure you connect the right sensor to the port described in the config.py file.

pin告诉我们传感器连接到GrovePi的哪个引脚。 我们使用在GrovePi上标记为A0,A1和A2的模拟引脚(或端口)。 确保将正确的传感器连接到config.py文件中描述的端口。

for i in range(cfg.NB_R0_READ):
    for sensor, value in mq_values.items():
        mq_values[sensor] += grovepi.analogRead(cfg.MQ_SENSORS[sensor]['pin'])
    time.sleep(cfg.R0_INTERVAL)

We then read the sensor value in a loop for cfg.NB_R0_READ times and sum it up in mq_value[sensor]. We read the sensor value with the analogRead method of the grovepi package.

然后,我们在循环中cfg.NB_R0_READ次的传感器值,并将其mq_value[sensor] 。 读与传感器值analogRead所述的方法grovepi包。

As described in the documentation, this will return a value between 0 and 1.023. In fact, it converts an analog sensor value to a digital value.

文档中所述,这将返回0到1.023之间的值。 实际上,它将模拟传感器值转换为数字值。

After one reading for all sensors, we pause the program at cfg.R0_INTERVAL seconds. To get the average value, we divide the cumulated value by cfg.NB_RO_READ.

对所有传感器读取一次后,我们在cfg.R0_INTERVAL秒处暂停程序。 为了获得平均值,我们将累计值除以cfg.NB_RO_READ.

for sensor, value in mq_values.items():
    mq_values[sensor] = mq_values[sensor]/cfg.NB_R0_READ
    mq_values[sensor] = mq_values[sensor]/cfg.AR_MAX * cfg.VC
    mq_values[sensor] = (cfg.VC - mq_values[sensor])/mq_values[sensor]
    mq_values[sensor] = mq_values[sensor]/cfg.MQ_SENSORS[sensor]['r0_rs_air']

We compute the sensor voltage by dividing the averaged sensor value by cfg.AR_MAX. Then we multiply it by the circuit voltage cfg.VC.

我们通过将平均传感器值除以cfg.AR_MAX来计算传感器电压。 然后,将其乘以电路电压cfg.VC

From that voltage, we can apply Ohm’s law and compute the sensor resistance value. Dividing that by the ratio for clean air cfg.MQ_SENSORS[sensor]['r0_rs_air'] gives us R0.

根据该电压,我们可以应用欧姆定律并计算传感器电阻值。 除以清洁空气的比率cfg.MQ_SENSORS[sensor]['r0_rs_air']得出R0。

It is better to have the sensor working at least for 24 hours before you measure the R0 value. This will give more stable sensor readings.

在测量R0值之前,最好让传感器至少工作24小时。 这将使传感器读数更稳定。

气体浓度的线性插值 (Linear interpolation of gas concentration)

Now that we know the R0 value, we can compute the Rs/R0 ratio with the sensor value. With that ratio, we can derive the gas concentration with linear interpolation.

现在我们知道了R0值,我们可以使用传感器值计算Rs / R0比率。 通过该比率,我们可以通过线性插值法得出气体浓度。

For that, we assume that we are working in the standard conditions described for the first graph. In that case, the curves are nearly linear.

为此,我们假设我们正在为第一张图描述的标准条件下工作。 在这种情况下,曲线几乎是线性的。

For linear interpolation, we need two known points of each curve to calculate its slope. Suppose we have two points with the coordinates (x1, y1) and (x2, y2). The y-values stand for the Rs/R0 values and the x-values for the gas concentrations. For a linear curve, the slope is then calculated as:

对于线性插值,我们需要每个曲线的两个已知点来计算其斜率。 假设我们有两个坐标分别为(x1,y1)和(x2,y2)的点。 y值代表Rs / R0值,x值代表气体浓度。 对于线性曲线,斜率的计算公式为:

When we know the slope, we can find the gas concentration(x) for any given Rs/R0 value (y). The formula for this is:

当我们知道斜率时,我们可以找到任意给定Rs / R0值(y)的气体浓度(x)。 公式为:

The code snippets below come from the script get_sensor_values.py which you can find on Github.

下面的代码段来自脚本get_sensor_values.py ,您可以在Github上找到它。

We put the formula in a function get_ppm. curve['y'] and curve['x'] are the known points on the curve for a gas. curve['slope'] is what we calculated with the previous formula.

我们将公式放在函数get_ppm. curve['y']curve['x']是气体在曲线上的已知点。 curve['slope']是我们使用上一个公式计算得出的结果。

You can find these values in the config_template.py file. I derived these values with Webplotdigitizer from the graphs on the data sheets. As a result, these values are not completely accurate. Use them with caution.

您可以在config_template.py文件中找到这些值。 我使用Webplotdigitizer从数据表上的图形得出了这些值。 结果,这些值并不完全准确。 谨慎使用它们。

Note the use of np.log10 and np.power. This reason for this is that the axes on the graph are in a log-scale.

注意使用np.log10np.power. 这是因为图表上的轴为对数刻度。

def get_ppm(Rs_R0_ratio, curve):
    x_val = (np.log10(Rs_R0_ratio) - curve['y'])/curve['slope'] + curve['x']
    ppm_val = np.power(x_val, 10)
    return ppm_val

We calculate the Rs_R0_ratio in the same manner as when calculating the R0 value. So I will not repeat this. To calculate this ratio, we loop over all gases and sensors and store this in ppm_values[mq_sensor][gas].

我们以与计算R0值相同的方式计算Rs_R0_ratio 。 所以我不再重复。 为了计算该比率,我们遍历所有气体和传感器,并将其存储在ppm_values[mq_sensor][gas].

for gas, curve in cfg.CURVES[mq_sensor].items():
                ppm_values[mq_sensor][gas] = get_ppm(mq_values[mq_sensor], curve)
温度,湿度和压力 (Temperature, humidity and pressure)

Besides the gas sensors, we read the temperature, humidity and pressure with the BME680 sensor. The BME680 sensor is connected to the GrovePi via an I2C port. To use the sensor, we import the package which can be installed from the Pimoroni repo on Github.

除了气体传感器,我们还使用BME680传感器读取温度,湿度和压力。 BME680传感器通过I2C端口连接到GrovePi。 要使用传感器,我们导入可以从Github上的Pimoroni存储库安装的软件包。

import bme680

The set_..._oversample methods specify how many samples we take to calculate the average value. We also did that for the gases. With get_sensor_data we read the sensor values.

set_..._oversample方法指定我们要计算多少个样本来计算平均值。 我们还对气体进行了处理。 使用get_sensor_data可以读取传感器值。

bme680_sensor = bme680.BME680(bme680.I2C_ADDR_PRIMARY)
bme680_sensor.set_humidity_oversample(bme680.OS_2X)
bme680_sensor.set_pressure_oversample(bme680.OS_4X)
bme680_sensor.set_temperature_oversample(bme680.OS_8X)
bme680_sensor.get_sensor_data()
bme680_sensor.get_sensor_data()

在Cloud Firestore中存储和检索传感器数据 (Storing and retrieving sensor data in Cloud Firestore)

Some sensors provide new readings very fast. Other (less expensive) sensors will take more time. For this project, we will read the data every minute. This is set in the config file with FIREBASE_INTERVAL = 60.

一些传感器可以非常快速地提供新的读数。 其他(价格便宜)的传感器将花费更多时间。 对于此项目,我们将每分钟读取一次数据。 这在FIREBASE_INTERVAL = 60的配置文件中设置。

In the free Spark plan of Firebase, the Cloud Firestore quota allow for 20K writes per day. With a one-minute interval, we will be well below that quota. The limit for reading documents in the Firestore is 50K per day.

在Firebase的免费Spark计划中, Cloud Firestore配额允许每天2万次写入。 每隔一分钟,我们将大大低于该配额。 在Firestore中读取文档的每天限制为50K。

You’ll need to create a Firebase project and create a Cloud Firestore. After that, make sure to generate the credentials to authenticate your application. Save the credentials file in a secure location.

您需要创建一个Firebase项目并创建一个Cloud Firestore 。 之后,请确保生成凭据以对您的应用程序进行身份验证。 将凭据文件保存在安全的位置。

To work with Firebase via Python, we need to import the firebase_admin package. This package needs to be installed on your Raspberry Pi first, if needed.

要通过Python使用Firebase,我们需要导入firebase_admin软件包。 如果需要,此软件包需要首先安装在Raspberry Pi上。

import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore

After that, we can initialize the Firebase app with credentials. I store the location to the credentials file in cfg.FIREBASE_CREDS_JSON. When the app is initialized, we create a Firestore object db.

之后,我们可以使用凭据初始化Firebase应用。 我将位置存储到cfg.FIREBASE_CREDS_JSON的凭据文件中。 初始化应用程序后,我们将创建一个Firestore对象db

firebase_path = Path.cwd() / cfg.FIREBASE_CREDS_JSON
cred = credentials.Certificate(str(firebase_path))
firebase_admin.initialize_app(cred)

After processing the gas values, it is time to store them in the Cloud Firestore. We will create a dictionary firebase_values to gather all the data. With a dict comprehension, we add the values for all gases for all MQ sensors. The BME680 values and timestamp are also added.

处理完气体值之后,是时候将它们存储在Cloud Firestore中了。 我们将创建一个字典firebase_values来收集所有数据。 通过dict理解,我们将所有MQ传感器的所有气体的值相加。 还添加了BME680值和时间戳。

With the add method of the Firestore object db, it is easy to store the data in the Firestore.

使用Firestore对象dbadd方法,可以很容易地将数据存储在Firestore中。

The name of the collection in the Firestore is cfg.FIREBASE_DB_NAME. Learn more about the data model of Firestore on the Firebase website.

Firestore中的集合名称为cfg.FIREBASE_DB_NAME 。 在Firebase网站上了解有关Firestore数据模型的更多信息。

firebase_values = {mq_sensor + '_' + gas + '_ppm': ppm
                            for mq_sensor, gases in ppm_values.items()
                            for gas, ppm in gases.items()
                          }
firebase_values['temperature'] = bme680_sensor.data.temperature
firebase_values['pressure'] = bme680_sensor.data.pressure
firebase_values['humidity'] = bme680_sensor.data.humidity
firebase_values['date'] = datetime.now()
db.collection(cfg.FIREBASE_DB_NAME).add(firebase_values)

After storing the data, we wait a minute to start over with the following line of code.

存储数据后,我们等待一分钟,以重新开始下面的代码行。

time.sleep(cfg.FIREBASE_INTERVAL)

改善空气质量 (Improving the air quality)

If the air quality indoors is not good we should take measures to improve it. Before we can do that, we need to be notified about critical gas concentrations.

如果室内空气质量不好,我们应采取措施加以改善。 在此之前,我们需要被告知临界气体浓度。

One possibility is to send an alert notification by email, which we’ll discuss below.

一种可能性是通过电子邮件发送警报通知,我们将在下面讨论。

Sometimes the source of indoor air pollution comes from outdoor air. For example, when your neighbors have wood-burning stoves or when you live near a factory.

有时室内空气污染源来自室外空气。 例如,当您的邻居有燃木火炉时,或者您住在工厂附近时。

In that case, you could install your measurement station outside and turn off the ventilation unit in your house if outdoor air quality is bad. With a remote-controlled switch, this can be done easily.

在这种情况下,如果室外空气质量较差,则可以将测量站安装在室外,并关闭室内的通风装置。 使用遥控开关,可以轻松完成此操作。

All code for this section is in improve_air_quality.py on Github.

对于本节所有的代码是在improve_air_quality.py Github上。

在空气质量达到关键水平时发送通知 (Sending notifications when air quality reaches a critical level)

We do not want to send an email each time the sensor outputs critical values (here, each minute).

我们不想每次传感器输出关键值(此处是每分钟)时都发送电子邮件。

Let’s say we want to check each hour whether there were critical values in the last hour. For that, we need to keep track of a reference_time. We initialize this when the program for readings sensor values starts. The interval at which we check again for critical gas concentrations is defined in cfg.ALERT_INTERVAL.

假设我们要每小时检查一次过去一小时是否有关键值。 为此,我们需要跟踪reference_time. 当读取传感器值的程序启动时,我们将对此进行初始化。 cfg.ALERT_INTERVAL定义了我们再次检查临界气体浓度的时间间隔。

reference_time = datetime.now()

When an hour has passed since reference_time, we can start to check if there were critical air pollutant values. We update reference_time with the current time.

reference_time一个小时过去了,我们可以开始检查是否存在关键的空气污染物值。 我们用当前时间更新reference_time

With the pytz package, we can take into account our timezone. In my case, that is Europe/Brussels. We compute one_hour_ago by subtracting 60 minutes from the current time.

使用pytz包,我们可以考虑时区。 就我而言,那就是Europe/Brussels. 我们通过从当前时间中减去60分钟来计算one_hour_ago

if datetime.now() > reference_time + timedelta(minutes=cfg.ALERT_INTERVAL):
    reference_time = datetime.now()
    brussels_tz = pytz.timezone('Europe/Brussels')
    prev_check_time = brussels_tz.localize(datetime.now()) - timedelta(minutes=cfg.ALERT_INTERVAL)

With prev_check_time we extract the sensor readings from Firebase of the last hour. We do that by applying a where clause to the data that we get from the Cloud Firestore.

使用prev_check_time我们可以从上一小时的Firebase中提取传感器读数。 为此,我们对从Cloud Firestore get的数据应用where子句。

In this script, we will only use one gas sensor and a limited set of gases. The sensor is selected in cfg.ALERT_SENSOR. The gases are selected in cfg.ALERT_GASES. The data per gas is appended to ppm_vals as well as the timestamps.

在此脚本中,我们将仅使用一个气体传感器和一组有限的气体。 在cfg.ALERT_SENSOR.选择了cfg.ALERT_SENSOR.cfg.ALERT_GASES.中选择气体cfg.ALERT_GASES. 每种气体的数据都附加到ppm_vals以及timestamps.

timestamps = []
ppm_vals = {}
for gas in cfg.ALERT_GASES:
    ppm_vals[gas] = []
            
docs = db.collection(cfg.FIREBASE_DB_NAME).order_by(u'date').where(u'date', '>=', one_hour_ago).get()
            
for doc in docs:
    data = doc.to_dict()
    for gas in cfg.ALERT_GASES:
        ppm_vals[gas].append(data[cfg.ALERT_SENSOR + gas + '_ppm'])

    timestamps.append(data['date'].strftime('%H:%M:%S'))

We look for critical values with the function find_crit_val. We will only check if the value surpassed an upper bound ubound. These upper bounds need to be specified in the config file.

我们使用功能find_crit_val.寻找关键值find_crit_val. 我们将仅检查值是否超过上限ubound 。 这些上限需要在配置文件中指定。

The data are in ascending chronological order. Thus, we can use the next method to find the first timestamp for which v > ubound. We return a tuple containing the timestamp of the critical value and the critical value itself.

数据按时间升序排列。 因此,我们可以使用next方法找到v > ubound的第一个时间戳。 我们返回一个包含临界值时间戳和临界值本身的元组。

If there is no critical value, we return the tuple (None, None).

如果没有临界值,则返回元组(None, None).

def find_crit_val(timestamps, val_list, ubound):
    try:
        (crit_time, crit_value) = next(((i,v) for i, v in zip(timestamps, val_list) if v > ubound))          
    except:
        (crit_time, crit_value) = (None,None)
    return (crit_time, crit_value)

The critical tuples are stored in a dictionary crit_dict. As the key, we use the gas name. We then check for gas-sensor combinations with a critical timestamp and critical value. In that case, we add an alert message to critical_msg.

关键元组存储在字典crit_dict. 作为关键字,我们使用气体名称。 然后,我们检查具有关键时间戳和关键值的气体传感器组合。 在这种情况下,我们将警报消息添加到critical_msg.

crit_dict = {}
for gas in cfg.ALERT_GASES:
    (crit_time, crit_value) = find_crit_val(timestamps, ppm_vals[gas], cfg.UPPERBOUNDS[gas])
    crit_dict[gas] = (crit_time, crit_value)
critical_msg = ''
    for k, v in crit_dict.items():
        if v[0] is not None and v[1] is not None:
            critical_msg += '\nCritical value for ' + k + ' of ' + str(v[1]) + cfg.UNITS[k] + ' at ' + str(v[0])

If critical_msg is not empty, we send an email. Sending an email is done with the smtplib package. How to send an email with Python is explained on AutomateThe BoringStuff.com.

如果critical_msg不为空,我们将发送一封电子邮件。 使用smtplib软件包发送电子邮件。 有关如何使用Python发送电子邮件的信息,请参见AutomateThe BoringStuff.com

You need to generate an application-specific password for your email if you are using Google’s two-factor authentication.

如果您使用的是Google的两因素身份验证,则需要为电子邮件生成应用程序专用密码

if critical_msg != '':
    try:
        msg = MIMEText(critical_msg, _charset='utf-8')
        msg['Subject'] = Header('Air Quality Alert', 'utf-8')
        smtpObj = smtplib.SMTP('smtp.gmail.com', 587)
        smtpObj.ehlo()
        smtpObj.starttls()
        smtpObj.login(cfg.EMAIL, cfg.EMAIL_PW)
        smtpObj.sendmail(cfg.EMAIL, cfg.EMAIL, msg.as_string())
        smtpObj.quit()  
    except smtplib.SMTPException:
        print('Something went wrong while sending the email')
自动控制您的通风装置 (Automatically control your ventilation unit)

With a remote-controlled switch, we can turn on or off any device that is connected to it. Thus, also a ventilation unit. Energenie creates the PiMote add-on specifically for the Raspberry Pi. To control the Energenie switch, you need to install the energenie package and import it.

使用遥控开关,我们可以打开或关闭与其连接的任何设备。 因此,还有通风装置。 Energenie专为Raspberry Pi创建PiMote附加组件。 要控制Energenie开关,您需要安装energenie软件包并导入它。

Note that you can not attach the PiMote on top of the GrovePi. So you’ll need a second Raspberry Pi.

请注意,您不能将PiMote连接到GrovePi的顶部。 因此,您需要第二台Raspberry Pi。

When starting the script, the first thing we do is define a boolean variable ventilation_on. We set it to False because this is the first time we run the script.

启动脚本时,我们要做的第一件事是定义一个布尔变量ventilation_on. 我们将其设置为False因为这是我们第一次运行脚本。

import energenie
ventilation_on = False

If critical_msg is not empty, there was a critical gas concentration in the last alert interval. In that case, we turn on the ventilation with the switch_on method of the energenie package.

如果critical_msg不为空,则在上一个警报间隔中存在临界气体浓度。 在这种情况下,我们使用energenie软件包的switch_on方法打开通风。

If there was no critical gas concentration and the ventilation was switched on in the last alert interval, we can switch it off.

如果没有严重的气体浓度,并且在上一个警报间隔内打开了通风,我们可以将其关闭。

You might need to set another interval before switching your ventilation off. That depends on the flow rate of your ventilation unit, the gases measured and whether the pollution source has been disabled.

您可能需要设置另一个间隔才能关闭通风。 这取决于您的通风设备的流量,所测量的气体以及是否禁用了污染源。

if critical_msg != '':
    if not ventilation_on:
        energenie.switch_on(1)
        ventilation_on = True
else:
    if ventilation_on:
        energenie.switch_off(1)
        ventilation_on = False

使用Dash可视化空气质量 (Visualization of air quality with Dash)

A notification with critical gas concentrations can help to take immediate action. But it is also interesting to track the gas concentrations over time. By visualizing the sensor values in a dashboard, we can look at the trend of the gas concentrations. This can be done with Dash. On the website of Dash, you can find a great tutorial on how to get started.

带有临界气体浓度的通知可以帮助您立即采取措施。 但是跟踪随时间变化的气体浓度也很有趣。 通过在仪表板上可视化传感器值,我们可以查看气体浓度的趋势。 这可以通过Dash完成。 在Dash的网站上,您可以找到有关入门的出色教程

In this project, we will build a dashboard and host it on PythonAnyWhere.com. To use Dash on PythonAnywhere, you need to create a virtual environment. You can follow the steps of this demo on how to set-up a Dash app on PythonAnyWhere.

在此项目中,我们将构建一个仪表板并将其托管在PythonAnyWhere.com上 。 要在PythonAnywhere上使用Dash,您需要创建一个虚拟环境 。 您可以按照本演示的步骤操作,了解如何在PythonAnyWhere上设置Dash应用程序。

Below I will show how I built the dashboard for our air quality station. The full script can be found in plot_sensor_values.py on Github.

下面,我将展示如何为空气质量站构建仪表板。 完整脚本可以在Github上的 plot_sensor_values.py中找到。

First of all, you need to import the dash package.

首先,您需要导入dash包。

import dash
import dash_core_components as dcc
import dash_html_components as html

In the demo on the Dash website, they use a link to a Cascading Style Sheet (CSS) to provide a nice page design. If you want to use a local CSS on your laptop or web server, you can add an assets folder. In that folder you can add your CSS and Dash will pick it up from there.

在Dash网站上的演示中,他们使用指向级联样式表(CSS)的链接来提供漂亮的页面设计。 如果要在便携式计算机或Web服务器上使用本地CSS ,则可以添加资产文件夹。 在该文件夹中,您可以添加CSS,Dash将从此处拾取它。

Then you’ll need to get the data from Firebase. This can be done similarly as we did for sending the alert notifications. So we will not go over that again.

然后,您需要从Firebase获取数据。 可以像我们发送警报通知一样进行此操作。 因此,我们不再赘述。

With the data collected from Firebase, we can fill the graphs in our dashboard. We first create a Dash object app and give it a title.

利用从Firebase收集的数据,我们可以在仪表板上填充图形。 我们首先创建一个Dash对象app并为其title.

app = dash.Dash(__name__)
app.title = 'Indoor Air Quality Dashboard'

Then we create the layout of the dashboard. A H1 heading component, a container div and a div containing the graphs.

然后,我们创建仪表板的layoutH1标题组件, container div和包含graphs.的div graphs.

app.layout = html.Div([
    html.H1(style={'textAlign':'center'}, children='Indoor Air Quality Dashboard'),
    html.Div(id='container'),
    html.Div(graphs)
])

graphs is a list that contains the info per graph. Below you can see how the graph for temperature is set up. You can add the dcc.Graph for humidity and pressure as well by appending them to graphs.

graphs是一个包含每个图信息的列表。 在下面您可以看到如何设置温度图。 您还可以通过将dcc.Graph附加到graphs.来添加湿度和压力graphs.

graphs = [
    dcc.Graph(
        id='temperature',
        figure={
            'data':[{
                'x':timestamps,
                'y':temperatures,
                'type':'line',
                'name': 'Temperature',
                'line': {'width':2, 'color': '#542788'}
                }],
            'layout':{
                'title': 'Temperature',
                'yaxis': {'title': 'Celsius'},
                'xaxis': {'title': 'Timestamp', 'tickvals':timestamps}
            }
        }
    )
]

The graphs for the MQ sensors can be appended in a for loop.

MQ传感器的图形可以附加在for循环中。

for mq_sensor in cfg.MQ_SENSORS.keys():
    for gas in cfg.CURVES[mq_sensor].keys():
        sensor_gas_key = mq_sensor + '_' + gas + '_ppm'
        title = gas + ' concentration on '+ mq_sensor + ' sensor'
        data = ppm_values[mq_sensor][gas]
        data.reverse()

        graphs.append(dcc.Graph(
            id=sensor_gas_key,
            figure={
                'data': [{
                    'x': timestamps,
                    'y': data,
                    'type':'line',
                    'name': title,
                    'line': {'width':2}
                }],
                'layout': {
                    'title': title,
                    'yaxis': {'title': 'ppm'},
                    'xaxis': {'title': 'Timestamp', 'tickvals':timestamps}
                }
            }
        ))

As a result, you will have a dashboard with graphs like the one below.

结果,您将获得一个仪表盘,其中包含如下图所示的图形。

结论 (Conclusion)

With a few low-cost gas sensors and a Raspberry Pi (and GrovePi) it is easy to build an air quality measurement station. You can then act on the data by sending alert notifications when air quality is bad or switch on the ventilation. With Dash you can build beautiful visualizations to monitor the air quality over time.

借助一些低成本的气体传感器和Raspberry Pi(和GrovePi),可以轻松地构建空气质量测量站。 然后,您可以通过在空气质量不好时发送警报通知或打开通风来处理数据。 借助Dash,您可以建立漂亮的可视化效果以随时间监视空气质量。

Below I noted some ideas to take this project further.

下面,我指出了一些进一步推进该项目的想法。

  • make a mobile app for the visualizations and receiving notifications

    制作用于可视化和接收通知的移动应用
  • add LEDs, a buzzer and an OLED display to the Raspberry Pi to get instant feedback on air quality

    在Raspberry Pi上添加LED,蜂鸣器和OLED显示屏,以获得空气质量的即时反馈
  • install measurement stations at your family and friends' place and visualize it on an interactive map.

    在家人和朋友的位置安装测量站,并在交互式地图上将其可视化

  • once you have enough data, build a time-series model to predict the indoor air quality

    一旦有了足够的数据,就可以建立一个时间序列模型来预测室内空气质量

Hopefully, this story motivates you to start building your own measurement station. If you have questions or suggestions let me know.

希望这个故事能激励您开始构建自己的测量站。 如果您有任何问题或建议,请告诉我。

翻译自: https://www.freecodecamp.org/news/how-to-monitor-your-air-quality-with-this-diy-setup-3399793137c3/

diy 多路监控

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值