安価なCO2センサの比較(1)

Sensirion製センサも動くようにしたので、それぞれどんな関係にあるか、見たくなってきました。
ですので、以前作った電源周波数観測用のサーバプログラムを改造して、chart.jsでグラフを描かせるようにしてみました。

本当はリアルタイム更新のほうが見た目には面白いのですが、 asyncio を使いこなせていないせいか、うまく行かず、Javascriptで強制リロードする方式にしました。

各センサからのデータ収集はUSBシリアル経由で行っていますが、今までは数値だけだったり、デバッグ情報が混ざっていたりとバラバラだったので、フォーマットを以下のように規定しました。

$(センサ名) Co2:(CO2濃度値 ppm) ・・・(その他の情報)

各USBシリアルから入ってくるデータを読んで、蓄積、定期的にHTML出力するコードとしました。(とにかく動けばいいや、なのでテキトーです)

# -*- coding:utf-8 -*-
#
import os.path,sys
import serial,time,re

# webserverは
#   python -m http.server 8080
# で別途起動

hist = {}        # データを保持する変数
hlen = 60000       # データを保持する数

devs = (
    '/dev/ttyUSB0',
    '/dev/ttyUSB1',
    '/dev/ttyUSB2',
    '/dev/ttyUSB3',
    '/dev/ttyUSB4',
    '/dev/ttyACM0',
    '/dev/ttyACM1',
    '/dev/ttyACM2',
    '/dev/ttyACM3',
    '/dev/ttyACM4' 
  )

ports = []

header = """<!DOCTYPE HTML>
<html lang="ja">
 
<head>
  <meta charset="utf-8">
 <title>CO2センサ比較</title>
</head>
 
<body>
  <h1>CO2センサ比較</h1>
  時刻はサーバプログラムがデータを受信した時刻です。データは約1分ごとに自動更新しています。30秒で自動リロードしますが、リロードのタイミングとデータの自動更新のタイミングがぶつかると表示の更新が停止する場合があります。<BR>
  センサーのサンプル数は各1台です。入手経路も入手時期も取り付けの構造もまちまちです。また、気分で改造したりします。
  <HR>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.js"></script>
    <canvas id="Chart1" width="auto" height="auto"></canvas>
    <script>
      var context = document.getElementById('Chart1');
      var Chart1 = new Chart(context, {
        type: 'scatter',
          data: {
            datasets: [
"""
footer = """
              ]},
          options:{
            scales:{
              xAxes: [{
                gridLines: {
                  color: "rgba(255, 0, 0, 0.2)", 
                  zeroLineColor: "black"    },
                type: 'time',
                time: {
                  unit: 'minute',
                  displayFormats: {
                    minute: 'H:mm'
                  },
                },
              }],
              yAxes: [{
                ticks :{
                  userCallback: function(tick) {
                    return tick.toString() + 'ppm'
                  }
                },
                gridLines: {
                  color: "rgba(0, 0, 255, 0.2)",
                  zeroLineColor: "black"
                },
              }]
            },
            animation: false
          }
      });
    </script>
    <script>
    // 30秒に一回リロード
      setTimeout("location.reload(true)",30000);
    </script>
</body>
</html>
"""
prepare = """<!DOCTYPE HTML>
<html lang="ja">
 
<head>
  <meta charset="utf-8">
 <title>CO2センサ比較</title>
</head>
 
<body>
  <h1>準備中です。1分程度で開始します</h1>
  <script>
  // 1分=60秒に一回リロード
    setTimeout("location.reload(true)",60000);
  </script>
</body>
</html>
"""

m1 = re.compile('^\$([A-Z0-9]+)\sCo2:([0-9]+)')

ctbl = (
  'rgba(  0,  0,255,0.8)',
  'rgba(255,  0,  0,0.8)',
  'rgba(  0,128,  0,0.8)',
  'rgba(255,  0,255,0.8)',
  'rgba(  0,128,128,0.8)',
  'rgba(128,128,  0,0.8)',
  'rgba( 64, 64, 64,0.8)'
)

def recieve():
  mkht = time.time()

  for p in ports :
    p.rts = False
    p.reset_input_buffer()
    p.rts = True

  while True:
    for p in ports :
      data = None
      if p.inWaiting() > 0 :
        try :
          data = p.readline().decode().rstrip('\r\n')
        except :
          data = None
          pass
        finally :
          pass
        #print(time.time(),data)
        r1 = m1.match(data)
        if r1 :
          #print(r1.group(1),r1.group(2),time.time())
          sensor = r1.group(1)
          data = float(r1.group(2))
          tstamp = time.time()
        else :
          data = None

      # 配列にデータを追加
      if data is not None :
        if not sensor in hist  :
          hist[sensor] = []
        hist[sensor].append((tstamp,data))
        while len(hist[sensor]) > hlen :
          del(hist[sensor][0])

    # 定期的にHTMLファイルを生成する。
    if time.time() - mkht > 10 :  # 前回ファイル出力から10秒以上経過している場合
      mkht = time.time()
      s = ""
      cidx = 0
      for k in hist.keys():
        s += """
              {
                label: \"""" + k + """\",
                showLine: true ,
                lineTension: 0 ,
                fill: false ,
                borderColor: \"""" + ctbl[cidx] + """\",
                borderWidth: 1,
                pointRadius: 1,
                data :[
"""
        cidx += 1
        for i in hist[k]:
          if mkht - i[0] < 3600*6 :    # 過去20分以内のデータに限り出力する
            s += "{{ x: {:.0f} , y: {} }},".format(i[0]*1000,i[1])
        s = s.rstrip(',')
        s += "]},"
      s = s.rstrip(',')
      with open('index.html',mode='w') as f:
        f.write(header + s + footer)

  for p in ports :
    p.close()

#
if __name__ == "__main__":
  for p in devs :
    if os.path.exists(p) :
      ports.append(serial.Serial(p , baudrate=115200, parity=serial.PARITY_NONE, rtscts=False))
  with open('index.html',mode='w') as f:
    f.write(prepare)
  time.sleep(5)
  recieve()

これをRaspberry Pi 3上で動かして、データ収集しながらHTML生成をさせました。

すべてのセンサを自室内の10cmほど開けてある窓に近いところに置いて比較してみました。

SCD30は変動が大きく、自分がそちらを向くと1mくらいあっても呼気に反応するのか、あるいはちょっとした風向きの変化でCO2濃度が変化する(外気があたる or 室内の空気があたるかが変わる)のか、すぐに数値が上昇します。その他のものは概ね似たような傾向を示しています。

このあと、窓を開けたまま風呂に入って無人の時間がしばらく経過して戻ってきたところです。

やはり、SCD30が極めて敏感に反応しているのですが、400ppmを切ってしまっているので、まだオートキャリブレーションが正常に機能していないのだと思います。(SCD41もまだ稼働時間は24時間以下です)

しばらく様子見するしかなさそうです。

Sensirion製CO2センサ

テストしただけで使っていなかったSensirion製SCD30と新規に購入したSCD41を動かしてみました。SCD30の方はテスト時と同じ構成です。

写真ではわかりませんが、写真の上半分の側面と上面に通気用の穴をたくさん開けてあります。また、小さく作ることができたので、小さなモバイルバッテリーで持ち運びもできそうです。
ソフトウェアはArduino環境でSparkFunのSCD30用のライブラリと、液晶制御はtomozhさんのST7032iライブラリを使っています。

一方、SCD41は間欠動作する省電力動作モードがあるようなので、将来的にディープスリープ併用で電池で動かすことを考え、以前作った自作のESP-WROOM-02搭載ボードに載せようと思ったのですが、肝心のESP-WROOM-02がありませんでした。ですので、暫定で秋月のESP-WROOM-02ボードにつないでありますが、Wi-Fiは使っていません。

ソフトウェアの方はSensirionからRaspberry PiやArduino、組み込み用のライブラリが提供されているので、Arduinoのライブラリをそのまま使っています。プログラムもほぼサンプルそのままで、シリアルに出力するのみになっています。

CASIOの超古いラベルプリンタKL-E11をお手軽軽量化

ゴールデンウイークで自宅の片づけをしていたら、大昔に買ったCASIOのラベルプリンタKL-E11が未開封のラベルカートリッジと一緒に出てきました。いつ買ったかも覚えていないのですが、20年くらい前ではないでしょうか。

ACアダプタが電気用品安全法のPSEマークではなく、電気用品取締法の「▽+〒」マークというあたりからも、いかに古いかがわかります。ちなみに蛍光オレンジのテープは残り無しだった。

調べてみると、なんと、今でも印刷に必要なWindows10用のアプリケーション(ラベル印刷ソフトBA-100PCラベル印刷ユーティリティBA-P20の2種類)とプリンタドライバがCASIOのWebサイトで提供されています。(プリンタドライバはラベル印刷ソフトなどとは別にインストールする必要があることに注意)

しかし、このACアダプタがとんでもなく重いのです。そうです。今となっては絶滅したトランスと整流回路だけが入っている、スイッチング電源ではないACアダプタです。ですので、出力7.5Vと書いてありますが、無負荷だと11.5Vくらい電圧があります。重さも本体より重い約400gです。一方で、本体側はDC9V 9Wと書いてあります。
ということは、適当にこのあたりの電圧を突っ込んでやれば動きそうです。そこで手元にあった秋月のDC9VのスイッチングACアダプタを挿そうとしたのですが大きさが合いません。でも、この形状はどこかで見たことがあると思ってジャンク箱を探したら、JEITAの電圧区分3のDCプラグのようで手持ちの変換ケーブルのプラグと同じ大きさでした。

さらによく考えると、Φ2.1mmのプラグのついた9V出力のUSB電源ケーブルを先日買ったことを思い出しました。

これらを組み合わせて(おそらく)15年くらいの時を超えて無事に出力ができました。しかも重たいACアダプタともおさらばできて万々歳です。なお、このワンダーキットの変換ケーブルWK-J3は値段が倍くらいになってますが未だに現役で売っているようです。また、秋月電子でもJEITAの電圧区分3の統一極性プラグを打っているようですので、秋月電子で9VのACアダプタと一緒に買ってきてプラグを付け替えるという手もありそうです。

セットできるテープの幅は18mmまでで、印刷できる幅は12mmくらいまでのようですが、自宅でたまに使う分にはまだまだ使えそうです。もちろん、自宅の中で圧倒的に現役最古のPC周辺機器になりました(笑)

変な挙動

電源周波数計測が変な変動を捉えた・・・といっても、本当に周波数が変動したのではなく、ちょうどスレッショルド付近にノイズが乗っただけだと思う。

ただ、これが特異なのは45秒近くの間ノイズがのり続けたこと。

いったい、どんな波形だったのか気になります。

Epoch.jsを動かしてみる

リアルタイムチャートといえば、Epoch.jsということらしいのですが、なかなか動かせずにいました。しかし、今回サンプルプログラムを動かすことができましたので、そのメモです。

参考になったのはこちらのヒサオメモさんのページです(大感謝)。こちらに沿って試させていただきました。同じようにチュートリアルのリアルタイムデモのページにそって動かすことを狙っていきます。

“Epoch.jsを動かしてみる” の続きを読む

鬱陶しい通知許可ポップアップを禁止する

Webを見ていて最近多いのが、デスクトップへの通知許可を求めるポップアップです。はっきり言って、許可したら鬱陶しいし、そもそも許可を求めてくる事自体が鬱陶しいです。

また、IPAからも案内されているように巧妙にデスクトップ通知を許可させようとする不正なサイトも横行しています。

IPAのサイトでの案内

なので、このポップアップを禁止できないのか調べてみました。なお、調べてみたのはLinux版のみです。Windows版もきっとだいたい同じだと思いますが、確認してません。

“鬱陶しい通知許可ポップアップを禁止する” の続きを読む

電源周波数観測のサーバプログラム更新(リアルタイム表示化)

1日に1回くらい起きる「突然51Hzくらいの瞬間がある」という現象に悩んで進まなかったサーバプログラム更新ですが、やっと更新しました。(51Hzくらいの異常データは単にフォトカプラのLEDのスレッショルド付近を跨ぐノイズだろうと思います。冷静に考えれば、本来1周期に1回のインプットキャプチャ発動が稀にノイズで1周期に2回起きてしまう=1秒間に50回のものが51回になる=51Hz付近に見えるというだけでしょう)

新バージョンでは毎回ページを全リロードするのではなく、増えたデータだけ受け取ってデータが増えたらchart.jsで描画しているグラフの部分だけを再描画しています。なので、転送量も減っているはず・・・だと思います。

“電源周波数観測のサーバプログラム更新(リアルタイム表示化)” の続きを読む

ATTINY版ソフトウェア修正

[2021/4/3] 再度修正しました。 平均値tAveVをuint32_tで保持していたのをfloatに変更しました。妙な桁落ち感が減った気がします。32bitの整数を50とかそんな値で割っていたのであまり変化はないはずだと思うのですが、グラフを描くと結構違います。

ATTINY版の電源監視ですが、フラッシュメモリの少なさからすべて整数演算にしていたのですが、若干誤差が大きいように思いますので、計算をfloatに変更してもフラッシュメモリ容量に収まるようなので、変更しました。(あと、何かの拍子に大きい値が出ます。原因は不明・・・というか、調べてません・・・が、それが治ることを期待して・・・ですが関係ないような気がするんだよなぁ・・・単に電圧がフォトカプラ内部のLEDの点灯/消灯のスレッショルドを跨ぐノイズが乗ってるだけだと思います。だとすると、治す方法は簡単ではないので諦めます。)

修正版は以下です。

“ATTINY版ソフトウェア修正” の続きを読む