M5Stack + MHZ19C + WiFi (Local/http)

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がエラー&リブートを繰り返した。結構はまった。

試験用の配線は前のブログ参照。まだ、試験用でMHZへの電力がたぶんたりてないはず。

MH-Z19CとM5Stack(basic)の接続

(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はプログラムで指定しているので変更可能。

  • 5V からVIN
  • G から GND
  • 16 から TX
  • 17 から RX

プログラム。いつもどおり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);
}

CO2モニター用基板EPEA-CO2-NDIR-04で他とシリアル接続するときのイメージ

ハードシリアルの0,1番ピンをセンサーモジュールとの接続に使っているのでソフトシリアルで2,3番ピンあたりを開く。

あとはTX,RXとGNDを対向のものにつなぐ。PCだと(普通は)シリアル接続の口がないのでUSBシリアル変換モジュールを間にはさむ。なお、USBシリアル変換モジュールでも多少の給電はできるけどNDIRセンサーを動かすには電力不足なので主電源は通常のUSB差込口になる。

なお、MHZ19のエラーが出たときにライブラリはハードシリアルにメッセージを出すようになっています。ハードシリアル上書きしているのでライブラリ書き換えないとメッセージは拾えません。

#include <Wire.h>
#include <SoftwareSerial.h>
#include <MHZ19.h>

#define SRX 2
#define STX 3

MHZ19 myMHZ19;

SoftwareSerial softSerial(SRX, STX);


void setup()
{
  Wire.begin();
  delay(1000);
  
  softSerial.begin(9600);
  delay(100);
  Serial.begin(9600);
  delay(100);

  myMHZ19.begin(softSerial);
  myMHZ19.autoCalibration(false);
  delay(1000);
}

int co2 = 0;
void loop()
{
  co2 = myMHZ19.getCO2();
  Serial.println(co2);
  delay(1000);
}

赤外線バリアモジュールの使い方

安くて簡単に使える衝突防止センサー

電圧は3.3v-5V。

VCCを3.3Vから5VにつなぎGNDをグランドにつなぐ。OUTから出てくるものはセンサーの近くにものがあると0,無いと1。つなぎ方もシンプルだし袋空けて10分もすれば大隊使えると思う。

Arduinoのサンプルソース

int PIN_IN = 2;
int PIN_OUT = 3;

void setup() {
  pinMode(PIN_IN, INPUT);
  pinMode(PIN_OUT, OUTPUT);
  Serial.begin(9600);
  Serial.println("start");
}

void loop() {
  Serial.println("loop");
  // ものが近く
  if( digitalRead(PIN_IN) == 0 ){
    Serial.println("PIN_IN == 0");
    digitalWrite(PIN_OUT, LOW); 
  } else {
    // ものが遠く
    Serial.println("PIN_IN == 1");
    digitalWrite(PIN_OUT, HIGH); 
  }

  delay(1000);                      
}

ON/OFF判定は楽なので、近づいたら光る消すぐらいならマイコンなくても簡単。

EPEA-CO2-NDIR-04 Rev3.0.9のソース

基本、自分用メモ

~1/22 出荷分

#include <FaBoLCDmini_AQM0802A.h>
#include <Wire.h>
#include <SoftwareSerial.h>
#include <MHZ19.h>

#define SRX 0
#define STX 1
#define INTERVAL 6

MHZ19 myMHZ19;

SoftwareSerial softSerial(SRX, STX);
FaBoLCDmini_AQM0802A lcd;

void setup()
{

  Wire.begin();
  lcd.begin();
  displayLCD("Start");
  delay(1000);
  
  softSerial.begin(9600);
  delay(100);
  myMHZ19.begin(softSerial);
  myMHZ19.autoCalibration(false);
  delay(1000);
}

int count = 0;
int co2 = 0;
void loop()
{
  if ((count % INTERVAL) == 0 ) {
    co2 = myMHZ19.getCO2();
  }
  displayCo2(co2, (count % 2));
  count++;
  delay(1000);
}

void displayLCD(String message) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(message);
}

void displayCo2(int co2, bool isPresiod) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("CO2 ppm");
  lcd.setCursor(0, 1);
  lcd.print(co2);
  if (isPresiod) {
    lcd.print(" .");
  }
}

1/22〜

#include <FaBoLCDmini_AQM0802A.h>
#include <Wire.h>
#include <SoftwareSerial.h>
#include <MHZ19.h>

#define SRX 0
#define STX 1
#define INTERVAL 6

MHZ19 myMHZ19;

SoftwareSerial softSerial(SRX, STX);
FaBoLCDmini_AQM0802A lcd;

void setup()
{
  checkLED(1000);
  
  Wire.begin();
  lcd.begin();
  displayLCD("Start");
  delay(1000);
  
  checkLED(500);
  
  softSerial.begin(9600);
  delay(100);
  myMHZ19.begin(softSerial);
  myMHZ19.autoCalibration(false);
  delay(1000);
}

void checkLED(int mills) {
  int PIN = 13;
  pinMode(PIN, OUTPUT);
  digitalWrite(PIN, HIGH);
  delay(1000);
  digitalWrite(PIN, LOW);
  delay(mills);
}

int count = 0;
int co2 = 0;
void loop()
{
  if ((count % INTERVAL) == 0 ) {
    co2 = myMHZ19.getCO2();
  }
  displayCo2(co2, (count % 2));
  count++;
  delay(1000);
}

void displayLCD(String message) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(message);
}

void displayCo2(int co2, bool isPresiod) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("CO2 ppm");
  lcd.setCursor(0, 1);
  lcd.print(co2);
  if (isPresiod) {
    lcd.print(" .");
  }
}

2/22~

#include <FaBoLCDmini_AQM0802A.h>
#include <Wire.h>
#include <SoftwareSerial.h>
#include <MHZ19.h>

#define SRX 0
#define STX 1
#define INTERVAL 6

MHZ19 myMHZ19;

SoftwareSerial softSerial(SRX, STX);
FaBoLCDmini_AQM0802A lcd;

void setup()
{
  checkLED(1000);
  
  Wire.begin();
  delay(100);
  lcd.begin();
  delay(100);
  displayLCD("Start");
  delay(1000);
  
  checkLED(500);
  
  softSerial.begin(9600);
  delay(100);
  myMHZ19.begin(softSerial);
  myMHZ19.autoCalibration(false);
  delay(1000);
}

void checkLED(int mills) {
  int PIN = 13;
  pinMode(PIN, OUTPUT);
  digitalWrite(PIN, HIGH);
  delay(1000);
  digitalWrite(PIN, LOW);
  delay(mills);
}

int count = 0;
int co2 = 0;
void loop()
{
  if ((count % INTERVAL) == 0 ) {
    co2 = myMHZ19.getCO2();
  }
  displayCo2(co2, (count % 2));
  count++;
  delay(1000);
}

void displayLCD(String message) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(message);
}

void displayCo2(int co2, bool isPresiod) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("CO2 ppm");
  lcd.setCursor(0, 1);
  lcd.print(co2);
  if (isPresiod) {
    lcd.print(" .");
  }
}

CO2センサー用基板EPEA-CO2-NDIR-04 Rev3.0.9の使い方

このページのライセンス

MITライセンス。すきにしてください。あわよくばセンサーをショップ及びAmazon(MHZ19B/MHZ19C)で売っているのでそちらから買ってください。

(12/27追記) サンプルで付けているソースのライセンスは各ライブラリのライセンス(MHZ19のライブラリはLGPL等)に従ってください。

はじめに

EPEA-CO2-NDIR-04 Rev3.0.9MH-Z19BMH-Z19CをAtmega328PU(内部クロック8Mhz/3.3v)で動かすための基盤です。マイクロUSBのDIP化基板のみAliexpressで調達したもので入手に時間がかかりますが他の部品は秋月電子等ですぐに手に入ると思います。

(1/16追記) 3端子レギュレータは半田をしやすいように意図的に幅を広めています。そのため奥迄は入りません。

必要部品

最低限動作スケッチ

最低限の動作をさせるためのスケッチです。基盤は0,1をソフトシリアルで使用していますが、ハードシリアルのピンなのでその内別のピンに変更します。なお、下記はMHZ19B用(外観)です。MHZ19C(外観)の場合はこちら(github)になります。

#include <FaBoLCDmini_AQM0802A.h>
#include <Wire.h>
#include <SoftwareSerial.h>
#include <MHZ19.h>

#define SRX 0
#define STX 1
#define INTERVAL 6

MHZ19 myMHZ19;

SoftwareSerial softSerial(SRX, STX);
FaBoLCDmini_AQM0802A lcd;

void setup()
{
  softSerial.begin(9600);
  delay(100);
  myMHZ19.begin(softSerial);

  Wire.begin();
  lcd.begin();
}
int count = 0;
int co2 = 0;
void loop()
{
  if ((count % INTERVAL) == 0 ) {
    co2 = myMHZ19.getCO2();
  }
  displayCo2(co2, (count % 2));
  count++;
  delay(1000);
}

void displayCo2(int co2, bool isPresiod) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("CO2 ppm");
  lcd.setCursor(0, 1);
  lcd.print(co2);
  if (isPresiod) {
    lcd.print(" .");
  }
}

回路図

応用例

プログラムの書き換え方法はこちら

数値が一定以上だったら音をならす。

数値が一定以上だったらLEDを光らせる。

数値が一定以上だったら何かを動作させる。

別のものとシリアル通信する。 サンプル

ネットワーク系は部品が足りないので苦手です。ネットワーク対応モジュール搭載基板がその内でるかもしれません。

ケース用SVG/Scad

3mmアクリル板をレーザーカッターで作る用のデータ github

その他必要資材は 

  • ボルトm3の30mm4本
  • 1mm厚程度のナット 6個(基盤のスペーサー替わり4,基板押えるよう対角線に計2つ)
  • ふたを抑える用のナット4個
  • ワッシャ任意

CO2センサー用基板EPEA-CO2-NDIR-04 Rev3.0.8の使い方

このページのライセンス

MITライセンス。すきにしてください。あわよくばセンサーをショップで売っているのでそちらから買ってください。

(12/27追記) サンプルで付けているソースのライセンスは各ライブラリのライセンス(MHZ19のライブラリはLGPL等)に従ってください。

はじめに

EPEA-CO2-NDIR-04 Rev3.0.8はMH-Z19B、MH-Z19CをAtmega328PU(内部クロック8Mhz/3.3v)で動かすための基盤です。マイクロUSBのDIP化基板のみAliexpressで調達したもので入手に時間がかかりますが他の部品は秋月電子等ですぐに手に入ると思います。3端子レギュレータの半田はかなり狭いです。次の3.0.9では広めにしました。

注意事項

LCD(液晶)は秋月電子様取り扱いのAE-AQM1248基盤を使用していますが、配線間違えによりSCL/SDAが逆になっています。当該部品を使用する場合はSCL/SDAを手作業で修正する必要があります。

必要部品

最低限動作スケッチ

最低限の動作をさせるためのスケッチです。基盤は0,1をソフトシリアルで使用していますが、ハードシリアルのピンなのでその内別のピンに変更します。

#include <FaBoLCDmini_AQM0802A.h>
#include <Wire.h>
#include <SoftwareSerial.h>
#include <MHZ19.h>

#define SRX 0
#define STX 1
#define INTERVAL 6

MHZ19 myMHZ19;

SoftwareSerial softSerial(SRX, STX);
FaBoLCDmini_AQM0802A lcd;

void setup()
{
  softSerial.begin(9600);
  delay(100);
  myMHZ19.begin(softSerial);

  Wire.begin();
  lcd.begin();
}
int count = 0;
int co2 = 0;
void loop()
{
  if ((count % INTERVAL) == 0 ) {
    co2 = myMHZ19.getCO2();
  }
  displayCo2(co2, (count % 2));
  count++;
  delay(1000);
}

void displayCo2(int co2, bool isPresiod) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("CO2 ppm");
  lcd.setCursor(0, 1);
  lcd.print(co2);
  if (isPresiod) {
    lcd.print(" .");
  }
}

本来の回路図

実際の基板(Rev3.0.8)はLCDのSCLとSDAが逆になっています。

応用例

数値が一定以上だったら音をならす。

数値が一定以上だったらLEDを光らせる。

数値が一定以上だったら何かを動作させる。

ネットワーク系は部品が足りないので苦手です。ネットワーク対応モジュール搭載基板がその内でるかもしれません。

CO2センサーCCS811のエラー

先日から稼動し始めた店のCO2センサーで使っているモジュールのCCS811だけど、エラーでモジュールの電源入れ直せというメッセージと共に落ちていた。

pi@raspberrypi:~/ccs811 $ sudo systemctl status ccs811.service
● ccs811.service - ccs811 moniter Daemon
Loaded: loaded (/home/pi/ccs811/ccs811.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2020-10-21 13:12:40 JST; 4s ago
Main PID: 22651 (python3)
CGroup: /system.slice/ccs811.service
└─22651 /home/pi/ccs811/.env/bin/python3 /home/pi/ccs811/ccs811.py
Oct 21 13:12:45 raspberrypi python3[22651]: Traceback (most recent call last):
Oct 21 13:12:45 raspberrypi python3[22651]: File "/home/pi/ccs811/ccs811.py", line 79, in
Oct 21 13:12:45 raspberrypi python3[22651]: main()
Oct 21 13:12:45 raspberrypi python3[22651]: File "/home/pi/ccs811/ccs811.py", line 60, in main
Oct 21 13:12:45 raspberrypi python3[22651]: wrapper = CCSWrapper()
Oct 21 13:12:45 raspberrypi python3[22651]: File "/home/pi/ccs811/ccs811.py", line 18, in init
Oct 21 13:12:45 raspberrypi python3[22651]: self.ccs811 = adafruit_ccs811.CCS811(self.i2c)
Oct 21 13:12:45 raspberrypi python3[22651]: File "/home/pi/ccs811/.env/lib/python3.7/site-packages/adafruit_ccs811.py", line 121, in init
Oct 21 13:12:45 raspberrypi python3[22651]: "Device returned a error! Try removing and reapplying power to "
Oct 21 13:12:45 raspberrypi python3[22651]: RuntimeError: Device returned a error! Try removing and reapplying power to the device and running the code agai

自動でonn/offしようと考えると、データシートみると最大で54 mA(常時観測のモード1だと30 mAなので大抵はこっちで考えてもOKかも)なのでGPIOのoutput直でon/offきりかえは無理。

無人で再起動かけようとするとトランジスタも必要かな。

docker-letsencrypt-nginx-proxy-companionが古かった

ACME server returned an error: urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Error creating new authz :: Validations for new domains are disabled in the V1 API (https://community.letsencrypt.org/t/end-of-life-plan-for-acmev1/88430)

新しく追加したサブドメイン&コンテナで上記エラー。

V1 API使えなくなるよ新しいの使えと公式がいっていたのは知ってたけど、docker-compose build –pullしていたので油断していた。docker-letsencrypt-nginx-proxy-companionはbuildしていないからdocker-compose pullしないといけなかった。

新しくなっていると思って別の場所から探っていったのでだいぶ時間をとかしてしまった。docker-compose pullは普段してないから、手を全く入れていないコンテナで古くなっているの残ってそう。

UVC除菌ロボ試作機のメモ

作ったUVC除菌ロボ試作機のメモ。

本体

Raspberry Pi Model B+

cat /proc/device-tree/model
Raspberry Pi Model B Plus Rev 1.2

OS

pi@raspberrypi:~$ cat /etc/debian_version
9.11

ネットワーク回り

基本はwifi。ハードは余っていた古いトングル。無防備にカメラを動かしていると熱暴走でつながらなくなる。トングルに扇風機で風を当てている分には動作する。ファンをつけないとカメラを実運用にのせるには厳しいけれども取り敢えずそのまんま。

なお、移動させないときは有線接続もつかっている。

lsusb
Bus 001 Device 004: ID 0789:0168 Logitec Corp. LAN-W150N/U2 Wireless LAN Adapter

電源回り

使用する電圧はラズベリーパイの5v、UVC殺菌灯の20V(17v以上ぐらいでギリギリ点灯可能)、モーターの6v(定格オーバーなのでPMWで調整)。モバイルバッテリーから全て取りたかったが選定ミスのため取り敢えずあるもので間に合わせた。

ラズパイ用+UVC用電力

PDマークが付いていたので20V引っ張れると思って買ったモバイルバッテリーから。USBの口が2つ付いているので、一つはそのままラズパイの電源へ。

もうひとつは、PDのトリガーケーブルで20VのUVC用電力をとりたかった。仕様確認ミスで20Vの出力はされなかった。20V指定のトリガーケーブルをさすとモバイルバッテリーの仕様上最大の15Vがとれた。(15vまでの仕様のUSB PDに20v指定をしていした場合に15vがとれるのは仕様で決められた動作かに関して未確認。)秋月のDCDCコンバーターキットで20vに昇圧している。

モーター用電力

モータードライバーが程よいものが無かったので取り敢えずBD6211F-E2。定格が1Aで130モーターが最大2.2Aぐらい使うので不足。いずれにせよ変更をしないといけないので取り敢えずで、単三電池4本の電池ボックスで6Vで動かしている。

カメラ回り

mjpeg_streamerのシェルを叩き読み込んだカメラ画像をiframeで表示。カメラは普通のUSBカメラ。いま外しているけどラズパイ本体のUSBポートにさして使う。

何かのsoファイルがデフォルトだと読み込めなくてシェルないでパス指定したはず。(記録とるのが遅れたので細かくは忘れた。)カメラ動かすとUSBポートがかなり熱をもつ。扇風機で風を当てていれば動くけれども、実運用はファンをつけないといけない。

動力回り

構成

前輪

コーナンで売っていた100円ぐらいのキャスターで動力はない。

後輪

タミヤのダブルギアボックススポーツタイヤのセット。ダブルギアボックスの中に入っているモーターはデータシートが見つからない。130モーターという企画らしく大体2.2Aが最大定格みたいなのでそれを目安にする。(が、取り敢えず手持ち部品がないのでBD6211F-E2で代用。)

電源

単三電池4本(1.5 v * 4 本 =6 v)を制御用のラズベリーパイ、殺菌灯本体とは別に引っ張っている。

配線

モータードライバーの使い方はこちら。電源から取っている6vフルで動かすとモータードライバーが耐えられないのでpwmで40%まで落として使っている。右車輪用と左車輪用の二つのモータードライバーを使用。使用しているGPIOはソース参照。

UVCライト回り

点灯機能の制御

配線はこちらの通り。GPIOはソース参照。使用している部品は秋月で売っていた2SK4017。

電源

前述のとおりモバイルバッテリーから引き出した15Vを20Vに昇圧したもの。18vぐらいでギリギリ点灯するぐらい。

UVCライト本体

基本的にこちらのとおり。E17殺菌灯はAliExpressがやすいけど日数がかかるしどうせ数100円なのでアマゾンあたりにあるもので良いと思う。

LEDドライバーはAliで探した。国内でもあると思うけど探していない。

E17ソケットはホームセンターで数100円。送料抜きならモノタロウが一番やすかった。モノタロウでもの買う人は一緒に発注しておくといいかも。

UVC作成キットは需要ありそうなのでキットで売り出すかもしれません。手持ち資材があれば、ツイッター(@YoshitakeKitamu)に連絡もらえれば計1000円+送料+消費税ぐらいで譲れます。