MHZ19をはじめとするMHZ14やその他Winsen社製のターミナルタイプですがコネクターがかなり固いです。
また、19はコネクターのメスとセンサー本体の間が折り取れるようになっているのでコネクター部分をしっかり押さえて差し込まないと折れてしまうことがあります。
向きは以下の写真の通りです。

alieで買ったOLEDの動作確認のメモ。
買ったのもはこちら。商品のレビューが星一個だったけどストアの全般的な評価は悪くない。

秋月で売っているこれの一回り大きいものだと思う。(持っていないので多分)
かったとこにデータ乗っていないけどたぶん同じ物あつかっているとこ(こことか)みると3v-5vでSH1106ドライバで動くとのこと。
接続はしたな感じ

LED-Arduinoで
ライブラリはこいつを使ってExampleのsh1106_128x64_i2c.inoで動作。
既存のCO2センサー(EPEA-CO2-NDIR-04)の画面を変えたバージョンを作りつつちょっと触ってみる。(といってもtextでればよいのでさらっと)
わかりにくい場合、画像等を追加します。購入モールのメッセージ、twitter等でご連絡ください。(本ブログは数週間に1度程度しかチェックしていません)
半田付けは「MHZ19用基板にMHZ19及びピンヘッダーを付けつ手順」と「LEDと抵抗をつける手順」の2ステップです。
ピンヘッダーを10+5個分基盤につけます。上面側にピンも来るようにつけてください。水平が狂うとM5Stackに差し込んだ時の接触が悪くなります。(ある程度は付属のワッシャ等で調整可能)
抵抗をLEDの足につけます。アノード/カソード好みで大丈夫です。長さはケースにおさまる程度に修正してください。付け終わったらビニテ等でそれぞれ接触しないように絶縁してください。
LEDはAliexpresで購入したこれを使っています。
底面(アクリルの板)、ナット、M5StackのM3ボルト取り付け穴の順になるように取り付けます。ボルト/底面/ナットを手で大体しめた後にM5Stackの背面取りつけ穴に合わせてドライバーでしめるとやりやすいです。
ワッシャ、底面(アクリルの板)、ナット(及び必要であれば高さ調整ワッシャ)、基板、ナットの順に取り付けます。使用するのは短めのプラスチックボルトです。
ナットを少しゆるめてピンヘッダーをスライドさせてM5Stackに差し込みます。差し込んだ後に改めて締め直してください。
M5Stack上面の22番にアノード、Gにカソードを差し込みます。
それぞれ上からはめてボルトとナットで固定します。
キットの組み立て方はこちらから(MHZ19Cも同一手順)
M5StackとCO2センサーモジュールMHZ19Bを使って通信機能を詰め込んだCO2センサーを作成した。一応1000ppm警告のLEDも付いている。使える通信(と表示)は以下。
画面とシリアル出力は常に起動。Wifiとbluetoothは起動時に選択した場合のみ。
画面はこんな感じ。
シリアル出力はこんな感じ。teraterm等で確認するのが楽かも。転送速度は9600。Windows用のCO2閲覧ソフトでも(これに限らずJSONできたら)みられる用にする予定。
[yoshitake@localhost ~]$ stty -F /dev/ttyUSB0 raw 9600
[yoshitake@localhost ~]$ sudo cat /dev/ttyUSB0
{"CO2":402}
{"CO2":401}
WifiはM5Stack起動時にAボタン(画面向かって左側)が押されていたら起動。繋がった際に画面にipアドレスが出力されるのでそのアドレスにアクセスするとJSONが返ってくる。192.168.1.15がM5StackのIPだとするとhttp://192.168.1.15/で値がJSONで返却される。呼び出し元のページがあるとIoTなこといろいろできる。なお、Wifiに10秒以上つながらなかったら設定モードに入る。設定はスマホアプリのESPTouchというものを使用。英語だけれどもパスワード入れさえすれば動く。注意点はスマホのWIFIが2.4Gのアクセスポイントに繋がっていないといけない。
bluetoothは起動時にBボタン(画面真ん中)が押されていたら起動。CO2Sensorという名前のデバイスが現れるのでそちらに接続すると数値のみが定期的(いまは6秒)で送られる。AndroidであればSerial Bluetooth Terminalというもので取り敢えず値がとれる。
なお、起動後にAボタンとCボタン(画面向かって右側)を長押しするとキャリブレーション(今の濃度を400とみなして基準値のリセット)を実行する。「日光の当たらない場所(できれば夜間)」で「20分以上きれいな外気」にあててから実行する。補正する為の比較対照が無い状態でも通常±50ppm以内には収まる。
ソースはこちら
ハードはM5StackBasicとこちらのキット。
表題の組み合わせでハマったのでメモ
adafruit_displayio_ssd1306.mpy他(adafruit_scd_30.mpyはセンサーのライブラリで不要)
~/develop/scd30_pico/lib$ ls
adafruit_bus_device adafruit_displayio_ssd1306.mpy adafruit_scd30.mpy
adafruit_display_text adafruit_framebuf.mpy
コード。
# SPDX-FileCopyrightText: 2020 by Bryan Siepert, written for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
import time
import board
import busio
import displayio
import terminalio
from adafruit_display_text import label
import adafruit_displayio_ssd1306
import adafruit_scd30
displayio.release_displays()
i2c_oled = busio.I2C(scl=board.GP19, sda=board.GP18)
display_bus = displayio.I2CDisplay(i2c_oled, device_address=0x3C)
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=32)
scd = adafruit_scd30.SCD30(i2c_oled)
try:
while True:
CO2 = scd.CO2
print(CO2)
text_group = displayio.Group(max_size=10)
# Draw a label
text = str(CO2)
text_area = label.Label(terminalio.FONT, text=text, color=0xFFFFFF, x=0, y=4)
text_group.append(text_area)
display.show(text_group)
time.sleep(2)
except Exception as e:
print(type(e))
print(e.args)
print(e)
adafruit_ssd1306
初期化すると、PCにUSBとして認識されなくなる
i2c = busio.I2C(board.GP21, board.GP20)
display = adafruit_ssd1306.SSD1306_I2C(128, 32, i2c)
上記で下の感じ
[4936305.202012] usb 2-2: new full-speed USB device number 74 using xhci_hcd
[4936305.202193] usb 2-2: Device not responding to setup address.
[4936305.410209] usb 2-2: Device not responding to setup address.
[4936305.618018] usb 2-2: device not accepting address 74, error -71
[4936305.746021] usb 2-2: new full-speed USB device number 75 using xhci_hcd
[4936305.746221] usb 2-2: Device not responding to setup address.
[4936305.954217] usb 2-2: Device not responding to setup address.
[4936306.162031] usb 2-2: device not accepting address 75, error -71
[4936306.162095] usb usb2-port2: unable to enumerate USB device
バグなのか使い方か不明。まだissueの流れが早すぎて見るの諦めた。
MHZ19CとPCをUSBで接続する。多分最小構成。
少しだけしゅっとさせたハード stores(少し安め) / amazon
Windows向けの閲覧アプリ EPEA-CO2-NDIR-05(USB接続CO2センサー)を指してこちらをインストールすれば使えます。(ドライバーが無い場合はこちらのドライバーも必要です。分からない場合は両方入れておけば間違いはないです。)
2021/04/18公開 ダウンロード先
新しいもの13万件分(連続稼働させている場合約1週間分)のデータをCSVとしてダウンロードします。
暖機中の500やエラー、起動待ちの-1/-9もダウンロードされます。
日時のデータにはミリ秒まで入っています。エクセルの標準だと細かいデータまでは表示されませんが必要性に応じて適宜、書式を設定してください。
2021/04/17公開 ダウンロード先
このバージョンで使用されているライブラリ(※)に環境依存のものが含まれています。おおよその環境で動くものを選択したつもりですがこのバージョンにあげたら動かなくなった場合はライブラリの可能性が高いです。このバージョンから動作しない場合はご連絡ください。
※ C:\Program Files (x86)\Epea\CO2Viewer\SQLite.Interop.dll です。これで伝わる人は適宜入れ替えてください。
現在の濃度を400ppm(おおよそ外気平均)として補正する機能です。USBを刺したまま換気をし綺麗な外気に20分以上さらした状態で実行してください。
C:\Users\ユーザ名\AppData\Roaming\epea\co2viewer にall.logが出力されるようになりました。設定はC:\Program Files (x86)\Epea\CO2Viewer\CO2View.exe.config 内にあります。
C:\Users\ユーザ名\AppData\Roaming\epea\co2viewer\co2viewerdb.sqlite に出力されるようになりました。出力機能はこれから作りますがとり急ぎDB Browser(SQlite)等で閲覧可能です。
2021/04/15公開 ダウンロード先
CO2センサーから読み取った値を画面上に表示します。USBを刺したポートを選んで接続を押すと値を読み込みます。なお、誤差が100ppm程度ある目安で使用してください。(それでも1万円未満で市販されているCO2センサーの中ではおそらく最高精度です。)
0は初期状態、-1は読み込み準備中、-9はエラー、その他値はセンサーが返した値です。
USBを刺してから2分ほど暖機にかかるのでその後から正確になります。
数値800未満は背景が緑、800以上1000未満は背景が黄色、1000以上は赤になります。自治体等の基準がまだ不明瞭ですがおおよその目安にしてください。
基準値補正を自動で行うか否かを選択する機能です。24時間以内に観測された最低値を外気基準値として補正する機能です。自動キャリブレーションをONにしていると換気ができていない場合や、電圧が不安定な場合等に値が不正確になることが多いのでOFFにした利用を推奨しております。(選べるようにしておいて欲しいという声がありましたので機能を付けています。。。)
Windowsアプリの話が長くなってきましたがやっとここからMHZ19CとPCをUSBで接続する。多分最小構成の話再開。
USBとシリアルを変換するやつ。MHZ19Cで200mAぐらい必要なのでPCから給電されたものがそのままピンに出てくるタイプでないといけない。CH380GというICのものなら大体ドライバーがPCに入っていると思うけどなければ自分でインストール。
参照 動作確認できたもの Amazonの購入元KKHMF 3個 CH340モジュール STC マイクロ コントローラー ダウンロード USBターンTTLシリアル
定番のCO2センサーモジュール。
秋月電子(送料抜きだと最安値。他のものも一緒に買う人はこっちが安い)
しゅっとさせたやつ(EPEA-CO2-NDIR-05)
stores amazon 手数料の関係でstoresの方が安い。
USBがあるもの。電源が弱いやつだと不安定になると思う。試しているOSはCentOS7
以下のように接続(前がモジュール後ろがMHZ19)
軽く探したところ使いやすそうなライブラリはまだ見つからなかった。ラズパイ想定のライブラリから抜粋して動作確認。python3.7.1でpySerialいれてある。/dev/ttyUSB0は環境によって(都度)変わるので適宜読みかえ
[yoshitake@localhost usbpy]$ pyenv version
3.7.1 (set by /home/yoshitake/develop/usbpy/.python-version)
pip install pyserial
import serial
import traceback
import time
serial_dev = '/dev/ttyUSB0'
def connect_serial():
return serial.Serial(serial_dev,
baudrate=9600)
def mh_z19():
try:
ser = connect_serial()
result = ser.write(b"\xff\x01\x86\x00\x00\x00\x00\x00\x79")
s = ser.read(9)
return {'co2': s[2]*256 + s[3]}
except:
traceback.print_exc()
finally:
ser.close()
def main():
while 1:
value = mh_z19()
co2 = value["co2"]
print('CO2:', co2)
time.sleep(5)
if __name__ == '__main__':
main()
出力はしたみたいな感じで、暖機が終わるまで少しかかるはず。(ただ、MHZのメーカーはちょいちょい動きをかえてくるのでだいたいの目安。)
yoshitake@localhost usbpy]$ python test2.py
CO2: 500
CO2: 500
CO2: 500
CO2: 500
CO2: 500
CO2: 500
CO2: 500
CO2: 500
CO2: 500
CO2: 500
CO2: 500
CO2: 500
CO2: 500
CO2: 500
CO2: 500
CO2: 500
CO2: 500
CO2: 500
CO2: 500
CO2: 465
CO2: 451
アンドロイドの野良アプリ。動作確認レベルの出来だけど値は取得できる。自分でインストールできる人向け。 こちら
UARTが有効になっていない。
pi@raspberrypi:~ $ sudo cat /boot/config.txt |grep uart
enable_uart=0
pi@raspberrypi:~ $ sudo python3 -m mh_z19
Traceback (most recent call last):
File "/usr/local/lib/python3.7/dist-packages/serial/serialposix.py", line 265, in open
self.fd = os.open(self.portstr, os.O_RDWR | os.O_NOCTTY | os.O_NONBLOCK)
FileNotFoundError: [Errno 2] No such file or directory: '/dev/ttyAMA0'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.7/dist-packages/mh_z19/__init__.py", line 62, in mh_z19
ser = connect_serial()
File "/usr/local/lib/python3.7/dist-packages/mh_z19/__init__.py", line 58, in connect_serial
timeout=1.0)
File "/usr/local/lib/python3.7/dist-packages/serial/serialutil.py", line 240, in __init__
self.open()
File "/usr/local/lib/python3.7/dist-packages/serial/serialposix.py", line 268, in open
raise SerialException(msg.errno, "could not open port {}: {}".format(self._port, msg))
serial.serialutil.SerialException: [Errno 2] could not open port /dev/ttyAMA0: [Errno 2] No such file or directory: '/dev/ttyAMA0'
A dependency job for serial-getty@ttyAMA0.service failed. See 'journalctl -xe' for details.
null
EPEA-CO2-NDIR-04と書いていますが、マイコンはAtmega328P-PU(内部クロック3.3v/8MHz)なので、一般的なAtmega328Pへの書き込みと同じになります。
マイコンを基盤につけたままだと書き込みが成功しにくいので、基盤からマイコンを外して書き込むことが基本形となります。PC側のArduinoIDEの設定はこちらを参考にしてください
注 基盤にマイコンをつけたまま書き込もうとすると(少なくとも2021/1月時点では)TX,RXの1,2番ピンがMHZの信号と競合してなかなか書き込みに成功しません。
Atmega328P-PU(内部クロック3.3v/8MHz) 。ブートローダー書き込み済みのもの。壊してしまったら秋月電子等で購入できます。
普通のブレッドボード
オスメスで7本
0.1uF
スイッチサイエンスのこれをつかっていますが、機能的にはほかでもいけると思います。
ピンの番号はこちらを参照
アダプターのDTRを0.1uFのコンデンサを挟んでマイコンのPIN1に※下記のtwetterリンク参照
アダプターのRX をマイコンのPIN3(TXD)に ※PIN2(RXD)でないので注意
アダプターのTX をマイコンのPIN”(RXD)に
アダプターのVCCをマイコンのPIN7(VCC)に、そこから分岐してさらにPIN20(AVCC)に ※下記のtwetterリンク参照
アダプターのGNDをマイコンのPIN8(GND)に、そこから分岐してさらにPIN22(GND)に ※下記のtwetterリンク参照
アダプターのマイクロUSBを書き込み用のPC(Arduino IDE)に
— 北村@CO2センサー等製造の株式会社エペア (@YoshitakeKitamu) February 4, 2021
書き込み側設定はこれでいけています pic.twitter.com/r6i5lpIRzq
— 北村@CO2センサー等製造の株式会社エペア (@YoshitakeKitamu) February 4, 2021
外部ライブラリとしてMHZ19(CO2センサー用)とFaBoLCDmini_AQM0802A(LCD用)を使っています。IDEのツール、ライブラリを管理からAQM0802A,MHZ19(似た名前がかかるけどMH-Z19のほう)あたりで検索するとでてくると思います。
TODO あとでちゃんと書きなおす(余裕があれば)
飛ばす方 ソース
#include <M5Stack.h>
#include <MHZ19.h>
#include <WiFi.h>
#include <HTTPClient.h>
#define RX 16
#define TX 17
#define INTERVAL 6
const char* ssid = "XXXXXXX";
const char* password = "XXXXXXX";
MHZ19 myMHZ19;
HardwareSerial mySerial(1);
void setup(){
M5.begin();
M5.Power.begin();
M5.Lcd.setTextSize(5);
M5.Lcd.print("start");
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
M5.Lcd.print(".");
}
M5.Lcd.println("\r\n connected");
M5.Lcd.print(WiFi. localIP());
mySerial.begin(9600,SERIAL_8N1,RX,TX);
delay(100);
myMHZ19.begin(mySerial);
myMHZ19.autoCalibration(false);
delay(2000);
}
int count = 0;
int co2 = 0;
void loop()
{
if ((count % INTERVAL) == 0 ) {
co2 = myMHZ19.getCO2();
displayCo2(co2);
doPost(co2);
} else {
M5.Lcd.print(".");
}
count++;
delay(1000);
}
void displayCo2(int co2) {
M5.Lcd.clear();
M5.Lcd.setCursor(0, 0);
M5.Lcd.println("CO2 ppm");
M5.Lcd.print(co2);
}
void doPost(int co2){
char json[30];
sprintf(json, "{\"co2\": \"%d\"}", co2);
Serial.println(json);
HTTPClient http;
http.begin("http://192.168.1.3:8081/");
http.addHeader("Content-Type", "application/json");
int httpCode = http.POST(json);
delay(1000);
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
http.end();
}
受けるほう ソース index.ts (雰囲気)
import express from 'express'
import moment from 'moment'
const app: express.Express = express()
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(express.static('public'))
app.listen(8081, () => {
console.log('Running at Port 8081...')
})
app.post('/', (req, res)=> {
console.log("call")
// console.log(moment().format())
console.log(req.body )
res.send('OK')
})
app.use((req, res) => {
res.sendStatus(404)
})
URLの指定で末尾の/が抜けているとM5Stackがエラー&リブートを繰り返した。結構はまった。
(2021.4.27追記)MHZ19というライブラリを使っていますがMHZ19B用に調整された処理が入っているのでCを使う場合は、MHZ19_uartというライブラリの方が良さそうです。
githubにソースあげておきました。(github)
とり急ぎ、MH-Z19CとM5Stackを直結で動かす。機能は表示のみ。電圧は満たしていないのでソフトがプログラムが動く確認までの目的。電源まわりはきちんと作り直さないと数値は大体。(それでも多分うちのかDM306以外の一万円未満のセンサーよりはよいきがする。これで5/1時点でCO2-miniかうちの以外の1万円未満センサーよりは性能良い)
MH-Z19Cは5Vプラマイ0.1Vを要求して守らないと値が不安定になる。「ゆうても大丈夫だろ」と雑にやるとずれます。
配線は、左がM5Stackで右がMH-Z19Cで以下のようにつなぐ。16,17はプログラムで指定しているので変更可能。
プログラム。いつもどおりMITライセンスです(ライブラリはLGPL使っているので書き込んだらLGPL)
#include <M5Stack.h>
#include <MHZ19.h>
#define RX 16
#define TX 17
#define INTERVAL 6
MHZ19 myMHZ19;
HardwareSerial mySerial(1);
void setup(){
M5.begin();
M5.Power.begin();
M5.Lcd.setTextSize(5);
M5.Lcd.print("start");
mySerial.begin(9600,SERIAL_8N1,RX,TX);
delay(100);
myMHZ19.begin(mySerial);
myMHZ19.autoCalibration(false);
}
int count = 0;
int co2 = 0;
void loop()
{
if ((count % INTERVAL) == 0 ) {
co2 = myMHZ19.getCO2();
}
displayCo2(co2, (count % 2));
count++;
delay(1000);
}
int count = 0;
int co2 = 0;
void loop()
{
if ((count % INTERVAL) == 0 ) {
co2 = myMHZ19.getCO2();
displayCo2(co2 );
} else {
M5.Lcd.print(".");
}
count++;
delay(1000);
}
void displayCo2(int co2) {
M5.Lcd.clear();
M5.Lcd.setCursor(0, 0);
M5.Lcd.println("CO2 ppm");
M5.Lcd.print(co2);
}