Raspberry Pi Pico Wでにおいセンサー(TGS8100)を使用する方法

この記事では、Raspberry Pi Pico Wとセンサーを接続して、タバコの煙や調理臭などのにおいの強さを取得する方法を紹介します。

センサーには、秋月電子のAE-TGS8100を使用します。

このセンサーは、空気中のガス濃度が高くなる程、抵抗値が低くなり出力電圧が上昇します。

そのため、センサーから取得するデータは抵抗値となり、通常時(空気がきれいなとき)を基準とし、そこからの抵抗値の変化によって空気の汚れを検出します。

目次

必要なもの

必要なものは次の4つです。

画像で使っている物品は、秋月電子にて購入しました。実際に使用しているパーツにリンクを貼っています。

AE-TGS8100にはんだ付け

最初にAE-TGS8100にはんだ付けを行います。

はんだ付けするのは、ピンヘッダのみです。

完成系は画像のようになります。

ピンの接続方法

AE-TGS8100とRaspbeery Pi Pico Wを接続します。

使用する物理ピンは、18番から20番と31番ピンの4つです。

GP15は、測定パルスを送るために 1000ms 周期中、2msだけHIGHに設定します。

また、AE-TGS8100のOUTピンからはアナログ電圧が出力されるため、GP26(ADC0)と接続し、デジタル値に変換を行います。

対応は下記の通りです。Pico W側は、MicroPythonのコードで設定を行います。

AE-TGS8100の仕様をより詳しく知りたい方は、製品紹介資料を参考にしてください。

Raspberry Pi Pico WAE-TGS8100備考
18番ピン, GNDGND
19番ピン, GP14VDD3.3Vを入力
20番ピン, GP15PULSE測定パルス
31番ピン, GP26(ADC0)OUTADC入力
Raspberry Pi Pico W ピン配置

完成系は画像のようになります。

MicroPythonコード

最後にMicroPythonのコードを書き、Pico Wに転送します。

下記では、使い回しがしやすいように、TGS8100をClass化しています。

tgs8100.py

TGS8100のコードは、製品紹介資料に記載のArduino UNO用のコードを参考にしてRaspberry Pi Pico Wに合った形に書き換えました。

sensor_r = ((3.0 / (adval * 3.3 / 65535)) - 1) * 10でOUTから出力される電圧から抵抗値を計算しています。前述した通り、このセンサーでは抵抗値Rsの変化によって、空気の汚れ具合を検知しています(抵抗値が低いほど空気が汚れている)。

そして、抵抗値Rsは次の式で計算できます。

Rs = (Vc/Vout - 1)× R1 [ Ω ] ※Vc = 3.0V , R1=10kΩ


Voutは、参照電圧である3.3V(Raspberry Pi Pico Wの仕様)にセンサーから読み取った値advalを掛け算し、read_u16()の最大値65535で割った値になります。


import utime  # type: ignore
from machine import ADC, Pin  # type: ignore


class TGS8100:
    def __init__(self, pin_v: int, pin_pulse: int, pin_out: int):
        Pin(pin_v, Pin.OUT).on()  # V_DD
        self.adc = ADC(pin_out)  # OUT
        self.pulse_pin = Pin(pin_pulse, Pin.OUT)  # PULSE

    def read_sensor(self) -> float:
        # PULSEピンの設定
        self.pulse_pin.value(1)  # PULSEピンをHIGHに設定
        utime.sleep_ms(1)  # OUTが安定するまでの待ち時間

        adval = self.adc.read_u16()  # 16ビットADC値を読み取る

        utime.sleep_ms(1)  # 時間調整
        self.pulse_pin.value(0)  # PULSEピンをLOWに設定

        if adval != 0:
            # センサ抵抗値の計算
            ## read_u16()は16ビット(0-65535)で結果を返す。参照電圧は3.3V
            sensor_r = ((3.0 / (adval * 3.3 / 65535)) - 1) * 10
            print(f"{sensor_r:.1f}")
            return sensor_r
        else:
            return -999
        utime.sleep_ms(998) # 時間調整

ADCの解像度とスケーリング: Raspberry Pi PicoのADCチャンネルは12ビットの解像度を持ち、0から4095までの値を生成します。しかし、MicroPythonにてread_u16()でこの値を読み取る際は、この値は16ビットの範囲(0から65535)にスケーリングされるため、上記の式では、65535でadvalを割っています。

main.py

main.pyでは、TGS8100 Classをインスタンス化して使用しています。

pin_v, pin_pulse, pin_outにはそれぞれピン番号を指定します。

ここで指定するピン番号は、物理ピンの番号ではなくGPIOピンの番号であることに注意してください。

その他のコードのポイントは、次の2点です。

  • 最初に10秒のスリープをしている: Firmwareが起動するまで待つために10秒間スリープしています。これがないと外部電源で起動した時にうまく動作しないことがあります。
  • whileループの中でgc.collect()を行なっている: Pico Wはメモリが少ないので、ループの度にGC(Garbage Collection)を行っています。このコードがないと動作途中からメモリエラーになります。
import gc
import time

from machine import Pin  # type: ignore
from tgs8100 import TGS8100

time.sleep(10)

LOG_FILE = "./log.txt"
LED = Pin("LED", Pin.OUT)

def main()
    bme680 = BME680(pin_v=4, pin_sda=2, pin_scl=3, i2c_id=1)

    while True:
        try:
            sensor_r = tgs8100.read_sensor()
            print(f"{sensor_r:.1f}")
        except Exception as e:
            print(f"Error: {e}")
        finally:
            gc.collect()


while True:
    try:
        main()
    except Exception as e:
        LED.on()
        print(f"Error: {e}")
        with open(LOG_FILE, "a") as f:
            print(f"{time.time()} Error: {e}", file=f)
        time.sleep(10)
        LED.off()

このコードを実行すると、次のような出力が得られます。

初めて使用する時や、長く通電しなかった場合には、周囲の空気の状況に関わらず 抵抗値が低下します。AE-TGS8100の抵抗値は電源投入後、ゆっくりと上昇し、数分から 20 分程度で安定します。

sensor_r: 16.3
sensor_r: 16.2
sensor_r: 16.1
sensor_r: 16.0
sensor_r: 16.1

どのようにして空気の汚れを検出するか?

TGS8100から抵抗値の値を取得することに成功しましたが、この値そのものにはあまり意味がありません。そのため、実際にこのセンサーを使う時は、値の変化に注目する必要があります。

いわゆる、異常検知(Anomaly Detection)を行います。

例えば、次のような方法で空気が汚れていることを検出できます。

  1. しばらく測定を行って正常値を測定し、そこから大きく乖離しているかを判定する
  2. 過去の抵抗値の移動平均からの乖離を判定する

1の方法で検知する場合は次のように考えられるでしょう。下記の値は実際に試した時の値を使っています。

測定結果から、データ数(サンプル数): 200, 平均値: 15.8965 , 標準偏差: 0.126164 だったので、測定データが正規分布に従うことを仮定すると、-2σは15.64417となる。

※抵抗値の値が安定しないため、最初の20分のデータは捨てています。

この結果から、空気が汚れていると抵抗値が低くなるため、抵抗値が-2σ以下(≒15.6442)になった時に検知し、アラートを出す設定を追加すれば良いでしょう。

-2σ〜2σは測定されたデータの95%が入る区間ですので、これを外れるということは異常が発生している可能性が高いです。

シンプルなチェックのため、アラートはPico W内でIF文で追加しても、データを収集しているクラウド側で追加してもOKです。

最後に、実際に油性ペンを近づけて、インクのにおいに反応してセンサの抵抗値が低下することを確認していきます。

インクを近づけるとRsが急低下し、明確に閾値を割っていることが分かります。下記、300-325の区間です。

ただし、インクを離した後、抵抗値の水準が16から18に引き上がったように見えます。秋月電子のサンプルでも同様の傾向が見えているため、実運用する際にはもう少しセンサーの癖を観測する必要があるでしょう。

まとめ

本記事では、AE-TGS8100を使用して、空気の汚れをRaspberry Pi Pico Wで検出する方法を紹介しました。

抵抗値の計算では、MicroPythonのread_u16()の仕様上、16bitで値が取得される点は覚えておくと他のセンサーでも応用が効きます。

また、抵抗値については、におい検知後に水準が変化するなど、気になる点がありました。この点は、実際の用途に応じて実測値から、異常検知の方法をより洗練させていくと良いでしょう。

よかったらシェアしてね!

CD

目次