【ESP32】PMS5003で空気中の粒子状物質を測定してみた

# はじめに
空気清浄機の謳い文句で「PM2.5を除去」といったものを見かけることがありませんか？
今回はESP32とPMS5003を使ってPM0.3～PM10を一挙に測定してみました．


# PMS5003について
PMS5003は粒子状物質（Particulate Matter）を測定することができるセンサで，いわゆるPM○○の濃度がわかります．正確には，単位体積あたりの粒子を直径ごとに個数単位で取得できる優れものです．
![PMS5003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/690612/ff613940-c249-89ee-e22c-9d32304d2197.png)
上の図はPMS5003の[データシート](http://www.aqmd.gov/docs/default-source/aq-spec/resources-page/plantower-pms5003-manual_v2-3.pdf)から引用した内部の仕組みの構成図です．PMS5003には5Vで動作するファンが内蔵されていて，そこから外気を取り込んでいます．そして取り込んだ外気をレーザー光で照射し，その散乱光の時間変化から粒子の個数を特定しているみたいです．（cf. [MIE散乱](https://ja.wikipedia.org/wiki/%E3%83%9F%E3%83%BC%E6%95%A3%E4%B9%B1)）

# 回路
PMS5003はシリアルインターフェースとしてUARTを採用しているため，一般的なUARTのクロス結線に基づいて回路を作成しました．
また，ファンを稼働させるために5Vの電圧が必要なので，PMS5003のVCCをESP32の5V電圧のピンと接続しています．

|ピン |役割 |説明 |
|--- |--- |--- |
|PIN1 |VCC |Positive power 5V |
|PIN2 |GND |Negative power |
|PIN3 |SET |Set pin /TTL level<span>@</span>3.3V，high level or suspending is normal working status, while low level is sleeping mode. |
|PIN4 |RX |Serial port receiving pin/TTL level<span>@</span>3.3V | 
|PIN5 |TX |Serial port sending pin/TTL level<span>@</span>3.3V |
|PIN6 |RESET |Module reset signal /TTL level<span>@</span>3.3V，low reset. |
|PIN7/8 |NC | |

![circuit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/690612/c1a1c45e-0929-e134-0981-3544c4b7ab97.png)


# ソースコード（ESP32）
ソースコードは[martonz](https://github.com/martonz)氏の[pms5003.py](https://github.com/martonz/pms5003-MicroPython/blob/master/pms5003.py)を参考にさせていただきました．
一分間隔で10回取得してます．

```pm_test.py
import utime
try:
    import struct
except ImportError:
    import ustruct as struct
from machine import UART

ACQ_NUM = 10
SLEEP_TIME = 60

uart = UART(1, baudrate=9600, tx=1, rx=3, timeout=2000)
buffer = []

for i in range(ACQ_NUM):
    data = uart.read(32)
    data = list(data)
    
    buffer += data

    while buffer and buffer[0] != 0x42:
        buffer.pop(0)

    if len(buffer) > 200:
        buffer = []
    if len(buffer) < 32:
        continue

    if buffer[1] != 0x4d:
        buffer.pop(0)
        continue

    frame_len = struct.unpack(">H", bytes(buffer[2:4]))[0]
    if frame_len != 28:
        buffer = []
        continue

    frame = struct.unpack(">HHHHHHHHHHHHHH", bytes(buffer[4:]))

    pm10_standard, pm25_standard, pm100_standard, pm10_env, \
        pm25_env, pm100_env, particles_03um, particles_05um, particles_10um, \
        particles_25um, particles_50um, particles_100um, skip, checksum = frame

    check = sum(buffer[0:30])

    if check != checksum:
        buffer = []
        continue
        
    print("---------------------------------------")
    print("Particles > 0.3um / 0.1L air:", particles_03um)
    print("Particles > 0.5um / 0.1L air:", particles_05um)
    print("Particles > 1.0um / 0.1L air:", particles_10um)
    print("Particles > 2.5um / 0.1L air:", particles_25um)
    print("Particles > 5.0um / 0.1L air:", particles_50um)
    print("Particles > 10 um / 0.1L air:", particles_100um)
    print("---------------------------------------")
    
    utime.sleep(SLEEP_TIME)
    
print("DONE!")
```

# 実行結果
![スクリーンショット (153).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/690612/e6e1d89e-0b18-46e0-52b8-511b42d51adf.png)



#おわりに
今回はESP32とPMS5003を使って空気中のPMを測定してみました．
どちらも比較的安価なので皆さんも試してみてはいかがでしょうか．