SC16IS750+MH-Z19B+Python/Go(Raspberry Pi)でCO2観測

在宅勤務になったので、会社よりも自宅のCO2濃度をちゃんと観測しなきゃな、って思うようになりました。

CO2観測用のセンサは昔やったことあるしいけるやろ、ということで挑戦。

UART直結

www2.hatenadiary.jp

今回はWio Nodeでなく、Raspberry Pi Zero W(以下RasPi ZW)でいきます。

www.raspberrypi.org

RasPi ZWはUARTをふたつ持っていて、デフォルトだとフル機能UART(PL011)は内部でBluetoothインタフェイスに繋がっていてminiUARTがGPIO側に出てくるようです。

www.raspberrypi.org

書いてあるとおりデフォルトではminiUARTがdisableなので、sudo raspi-config あたりで有効化させて繋げば一発で動く。高々9600bpsでフロー制御もないので、PL011にせずともminiUARTでちゃんと疎通します。

ここまでのことはぐぐったら沢山出てくる。
dev.classmethod.jp

I2C経由

BME280を使って温度湿度気圧を一緒に取りたいので、I2Cだけで疎通させてみました。Wio Nodeのときに買ったSC16IS750が余ってるので、今回も使います。

www.nxp.com

ざっと組むとこんな感じ。

f:id:W53SA:20210421234713p:plain

デバドラで会話編(失敗)

で、SC16IS750との会話です。ぐぐると、LinuxカーネルにSC16IS7xxシリーズ向けI2Cドライバ実装が入っていて、Raspberry OSのDevice Treeにも存在してる。

www.raspberrypi.org
firmware/README at master · raspberrypi/firmware · GitHub
linux/sc16is7xx.c at master · torvalds/linux · GitHub

/boot/config.txtdtoverlay=sc16is750-i2c って書いて再起動すると/dev/ttySC0が生えるので、これに対してコマンドを送れば出来そう。

注意事項としてはフォーラムやDeviceTree overlayに書いてあるとおり、SC16IS750チップのIRQとRasPi ZWのGPIOを繋ぐ必要があります。
ドライバ実装を見ると、データ読み込み実装sc16is7xx_handle_rxはチップからGPIO経由で割り込みが入ったときに動くのでIRQを繋がないと返事が取れない。

しかし、疎通失敗。しかも、最新のRaspberry Pi OSだとドライバ読ませた段階でI2C通信が死んでしまう。なぜだ???

ユーザランドで会話編

デバドラなんかうまくいかんのでユーザーランドでI2C喋ってなんとかすることに。

Python

取り敢えずPythonで書いたろうとぐぐって調べると、できかけの実装を見つけました。それを元にUART読み書きだけ追加で実装。

github.com

で、雑にMH-Z19Bと会話する。

from SC16IS750 import Serial
s=Serial.Serial()

s.write(b"\xff\x01\x86\x00\x00\x00\x00\x00\x79")

while True:
    r = s.read()
    if r != None:
        break

if len(r) >= 4 and r[0] == 0xff and r[1] == 0x86:
    print("co2:{}".format(r[2]*256 + r[3]))
Go

Goの実装はインターネット上では見つけられなかったので、勉強がてら前出のPython実装を移植してみました。

github.com

io.ReadWriteCloserの実装を書いて*1、既存のMH-Z19実装と組み合わせるとシュッと動いた。

github.com

conf := &sc16is7x0.Config{Address: 0x48, XtalFreq: 14745600, Baud: 9600}
dev, _ := sc16is7x0.Open(conf)
concentration, _ := z19.TakeReading(dev)
fmt.Printf("co2=%d ppm\n", concentration)

*1:Goのinterfaceまわり、独特なんで書いて理解するのが一番だなという感想。