Tự đo chỉ số ô nhiễm không khí tại nhà với sensor và Raspberry Pi

Mở đầu

Tình hình ô nhiễm không khí có vẻ đã lắng xuống sau khi không thấy báo chí nhắc đến nhiều nhưng thực tế thì ô nhiễm vẫn còn đó. Trước khi suy nghĩ đến việc mua một máy lọc không khí cho gia đình thì có thể bạn sẽ thắc mắc, không biết chỉ số ô nhiễm không khí AQI (Air Quality Index) của nhà mình hiện đang ở mức nào 🤔. Mình cũng hoang mang như vậy đó 😂. May mắn có người bạn có ship hàng từ taobao về, mình đã đặt mua 1 cái sensor đo bụi và setup thử. Sau khi cặm cụi khoảng 5 tiếng, việc hoàn toàn không hề khó như mình tưởng, Mình xin chia sẻ quá trình cũng như công cụ thực hiện trong bài viết này. Hi vọng là ai cũng có thể tự đo cho bản thân và gia đình mình.

Toàn bộ source code trong bài viết được public tại: https://github.com/vigov5/aqi_meter

Kết quả thực tế tại điểm đo Phú Đô: http://aqi-phudo.surge.sh/

Chuẩn bị

Những thứ bạn cần chuẩn bị

https://images.viblo.asia/2ae97139-f192-4afa-95cd-d318944eef18.jpg

  • Một con Raspberry Pi để chạy và lập lịch đo cho sensor, đặt tại địa điểm cần đo. Mình dùng model Raspberry Pi 3 Model B+ (có sẵn Wifi + Ethernet). Bạn có thể cài sẵn hệ điều hành Raspbian.

Nếu không có Raspberry Pi, bạn có thể sử dụng máy tính cũ đặt tại nhà nhưng phải đảm bảo là nó chạy 24/7 😎

  • Một VPS server để lưu trữ dữ liệu đo và hosting cho trang hiển thị kết quả. Mình đang chạy blog trên server của Digital Ocean với phí 5$ một tháng (1 CPU, 2GB RAM) nên dùng luôn. Bạn có thể đăng ký tại đây
  • Một chút skill về chạy lệnh terminal, python, coding, docker 😂

Thực hiện

Cài đặt InfluxDB

Cài đặt DB ban đầu

Để tiện lợi cho việc truy vấn cũng như lưu trữ sau này, mình dùng InfluxDB, một hệ cơ sở dữ liệu chuyên dùng cho lưu trữ dữ liệu dạng time series.

Trước hết chúng ta sẽ tạo ra 3 user:

  • User root: full quyền
  • User sensor: sẽ do sensor quản lý, chịu trách nhiệm ghi dữ liệu vào trong DB
  • User readonly: nhiệm vụ truy vấn và hiển thị dữ liệu lên trang web (chỉ có duy nhất quyền đọc)

Mình sẽ dùng docker và docker-compose để quản lý việc chạy InfluxDB. Việc cài đặt thì coi như là bài tập cho người đọc 😄

Tạo thư mục db trên server Digital Ocean rồi tạo file docker-compose.yml như sau (nhớ thay đổi password cho phù hợp nhé):

version: "3.5"

services:
  influxdb:
    image: influxdb
    environment:
      - INFLUXDB_ADMIN_USER=root
      - INFLUXDB_ADMIN_PASSWORD=root_password
      - INFLUXDB_DB=aqi_mesurement
      - INFLUXDB_USER=sensor
      - INFLUXDB_USER_PASSWORD=sensor_password
    volumes:
      - ./db:/var/lib/influxdb
    ports:
      - 8086:8086

Và chạy lệnh docker-compose up -d. Vậy là chúng ta đã có 2 user.

Thêm DB

Chạy lệnh

pip install influxdb==5.2.3

để cài đặt package influxdb cho python3 và chạy (thay thế IP_SERVER bằng IP của server Digital Ocean):

$ python
>>> from influxdb import InfluxDBClient
>>> client = InfluxDBClient('IP_SERVER', 8086, 'root', 'root_password', 'aqi_mesurement')
>>> client.create_database('aqi_mesurement')

Thêm người dùng chỉ có quyền đọc

Chạy 2 lệnh đơn giản sau để thêm nhé:

curl 'http://IP_SERVER:8086/query?p=root_password&u=root&db=aqi_mesurement' --data-urlencode "q=CREATE USER readonly WITH PASSWORD 'readonly'"
curl 'http://IP_SERVER:8086/query?p=root_password&u=root&db=aqi_mesurement' --data-urlencode "q=GRANT READ ON aqi_mesurement TO readonly"

Cài đặt sensor

Kết nối sensor với Raspberry Pi

  • Sau khi bóc team sensor, thì bạn sẽ thấy gồm có 2 phần chính: Phần vuông vuồng là phần đo, gồm phần đỏ trong hình trên là quạt và một ống thoát khí, chú ý để 2 phần này thông thoáng, không bị bịt chắn. Phần thứ 2 là cổng kết nối với 1 đầu là USB (phần màu xanh), bạn cắn đầu này vào cổng USB trên Raspberry Pi
  • Tại màn hình terminal Raspberry gõ lệnh dmesg và kiểm tra kết quả:
$ dmesg 
[ 5.559802] usbcore: registered new interface driver usbserial 
[ 5.559930] usbcore: registered new interface driver usbserial_generic 
[ 5.560049] usbserial: USB Serial support registered for generic 
[ 5.569938] usbcore: registered new interface driver ch341 
[ 5.570079] usbserial: USB Serial support registered for ch341-uart 
[ 5.570217] ch341 1–1.4:1.0: ch341-uart converter detected 
[ 5.575686] usb 1–1.4: ch341-uart converter now attached to ttyUSB0

ta sẽ kiểm tra được thiết bị được kết nối vào ttyUSB0, nghĩa là đường dẫn của thiết bị sẽ là /dev/ttyUSB0

Ảnh thực tế:

Cài đặt script

Tạo file requirements.txt:

python-aqi==0.6.1
influxdb==5.2.3
git+git://github.com/ikalchev/py-sds011

Cài đặt môi trường cho script:

pip3 install -r requirements.txt

Tạo một scrip tên aqi_client.py để thực hiện việc đo (nhớ thay đổi các giá trị cấu hình ở đầu file cho phù hợp):

import time
import datetime
from sds011 import SDS011
import aqi
from influxdb import InfluxDBClient

DEV_PATH = '/dev/ttyUSB0'
HOST = 'IP_SERVER'
PORT = 8086
USER = 'sensor'
PASSWORD = 'sensor_password'
DATABASE = 'aqi_mesurement'
PLACE = 'home'


def mesure():
    sensor = SDS011(DEV_PATH, use_query_mode=True)
    print('Sleep device...')
    sensor.sleep(sleep=True)  # Turn off fan and diode
    print('Wake-up device...')
    sensor.sleep(sleep=False)  # Turn on fan and diode
    print('Mesure for 30 secs...')
    time.sleep(30)  # Allow time for the sensor to measure properly
    print('Query data...')
    result = sensor.query()
    print('Sleep device...')
    sensor.sleep()  # Turn off fan and diode

    return result if result else (0, 0)


if __name__ == '__main__':
    pm25, pm10 = mesure()
    aqi = int(aqi.to_aqi([
        (aqi.POLLUTANT_PM25, pm25),
        (aqi.POLLUTANT_PM10, pm10),
    ]))
    print('Result: AQI: {}, PM2.5: {}, PM10: {}'.format(aqi, pm25, pm10))

    now = datetime.datetime.now().replace(microsecond=0).isoformat()
    json_body = [
        {
            'measurement': 'aqi',
            'tags': {
                'place': PLACE,
            },
            'time': now,
            'fields': {
                'aqi': aqi,
                'pm25': pm25,
                'pm10': pm10,
            }
        }
    ]

    client = InfluxDBClient(HOST, PORT, USER, PASSWORD, DATABASE)
    client.write_points(json_body)
    print('Done...')

Script sẽ có nhiệm vụ:

  • Định kỳ bật sensor lên, để sensor chạy 30 giây sau đó lấy kết quả đo, rồi lại sleep sensor. Vì tuổi thọ sensor là 8000 giờ (theo file spec) và chúng ta cũng không cần đo liên tục nên làm như trên sẽ giúp kéo dài tuổi thọ thiết bị.
  • Gửi dữ liệu đo lên DB influxDB ở trên server

Chúng ta cần thiết lập thêm cron job để định kỳ chạy script mỗi 5 phút 1 lần. Ví dụ với đường dẫn của script ở /home/pi/aqi/aqi_client.py ta cấu hình như sau:

*/5 * * * * python3 /home/pi/aqi/aqi_client.py >> /tmp/aqi.log

Save lại và kiểm tra xem có log request gửi đến ko (chạy docker logs container_id_of_influxdb)

[httpd] 42.112.192.68 - sensor [04/Nov/2019:07:20:37 +0000] "POST /write?db=aqi_mesurement HTTP/1.1" 204 0 "-" "python-requests/2.21.0" 993f3ecf-fed3-11e9-88a3-0242ac120002 1261

Hiển thị kết quả lên web-app

Với dữ liệu đã có trong DB, việc query hoặc visualize là tuỳ ở từng người. Đơn giản nhất là chạy một docker Grafana với data source là Influx DB, ta có thể truy vấn kết quả như sau:

Mình thì viết nhanh một web-app bằng VueJS sử dụng Tailwind CSS để style lại cho đẹp 😍 để cứ 5 phút sẽ lấy dữ liệu từ server thông qua user readonly và hiển thị. Trước hết clone repo https://github.com/vigov5/aqi_meter:

git clone [email protected]:vigov5/aqi_meter.git

Cài đặt các package

yarn install

Copy file .env.sample thành .env và chỉnh sửa lại thông số

# config your URL to InfluxDB
VUE_APP_DB_URL=http://user:[email protected]:post/db
VUE_APP_PLACE=My Home
VUE_APP_LOCATION=Ciy, Country

Chạy yarn serve để xem kết quả trên local hoặc dùng 1 trang cho phép hosting các trang tĩnh như Surge để đẩy code local lên:

yarn build && surge dist aqi-phudo.surge.sh

Kết quả cuối cùng (như hình vẽ đầu bài viết): http://aqi-phudo.surge.sh/

Kết luận

Thiết bị mình đang đặt ở trong phòng tầng 2 (thường đóng kín cửa) mà chỉ số cũng không được tốt cho lắm, gấp 2 lần chỉ số khuyến nghị. Có lẽ đã đến lúc phải cân nhắc mua máy lọc không khí thật rồi 😰. Hi vọng sau bài này sẽ có thêm điểm đo nữa mọc lên, bổ sung thêm những điểm đo mới cho hệ thống của thành phố của chúng ta 😄

The End~

"Xung quanh anh toàn là bụi ! Êi ! Mênh mông toàn là bụi !"

Tham khảo