新型コロナウイルスが猛威を振るっている武漢で、かざすだけで(非接触で)体温が分かる装置を使って建物に入る前に体温を検査しているビデオを見ました。その時は特に注目もしていなかったのですが、たまたまスイッチサイエンスのサイトでM5StickC用の非接触温度センサHatを見つけて、「ああ、こんなのを使っていたんだ!」って興味が湧いたんです。
しかも、この製品の公開日が「2020年2月12日」で見つけた日は13日で出たばっかり!さらに、お値段も1,200円程度でとても安かったんです。
「これは買うしかない!」と思って、さっそく秋葉原の千石電商やマルツに行ってみたんですが、どこにも置いてなかったんです。仕方なく部屋に戻って製品ぺージを見たら、「在庫多数」と表示されていたハズの在庫が「7個」ぐらいになっていたんです。あせりました。
スイッチサイエンスのサイトでは既に在庫「0」で次回入荷が「未定」となっていましたが、この記事を書いている17日の昼は千石電商の店頭に並んでいるのを見かけたので、欲しい方はダッシュでお店に行きましょう!
お手軽非接触温度計の写真
M5StickCに非接触温度センサHatを付けるとこんな感じです。とっても簡単で、非常にコンパクトです!
でも、非接触温度センサHatは下に付いているピンだけで支えているために、本体との上の接触部分に隙間ができて少しぐらぐらするんです。
2020年3月2日修正
そのため、下の写真のように赤枠部分に両面テープを貼って本体とピッタリ付くようにしました。
赤枠の長方形の窪みはなんだろうと思っていたら、箱の中に長方形の窪みにピッタリ合う切り込み付きの両面テープが入っていました。これで留めるのだと思います。
M5StickCは小さいですがバッテリーを内蔵しているので、少しの間であればこれだけで色々なもの温度を測ることができるんですよ。
お手軽非接触温度計のビデオ
動作時のビデオを撮ったので、まずはご覧ください。
センサーを向けた物体表面の温度を表示する単純なものですが、スマホのようにM5StickCの向きに応じて表示が回転するようにしました。
また、Aボタンに押した時の画面に固定されるスクリーンロック機能と、Bボタンにローテーションを禁止するローテーションロック機能を付けています。
プログラム
UIFlowを使ってPythonでプログラムを書きたいと思っているのですが、使い慣れているArduinoでまずは作って見ることにしました。
「M5StickC」ライブラリの「NCIR_HAT」スケッチ例をベースとして、表示のローテーション機能やローテーションのロック機能、スクリーンロック機能などを付け加えています。
M5StickC_NCIR.ino
/* * NCIR Temperature Sensor * February 17, 2020 * By Hiroyuki ITO * https://intellectualcuriosity.hatenablog.com/ * MIT Licensed. */ #include <M5StickC.h> #include <Wire.h> // Screen Orientation #define TOP_LEFT 1 #define TOP_RIGHT 3 // Global Variables int rotation = TOP_LEFT; // Screen Orientation boolean lock_rotation = false; // Rotation Lock boolean lock_screen = false; // Screen Lock // Rotate Screen void rotated() { M5.Lcd.fillScreen(BLACK); M5.Lcd.setRotation(rotation); } // Temperature Measurement float measurement() { uint16_t result; Wire.beginTransmission(0x5A); // Send Initial Signal and I2C Bus Address Wire.write(0x07); // Send data only once and add one address automatically. Wire.endTransmission(false); // Stop signal Wire.requestFrom(0x5A, 2); // Get 2 consecutive data from 0x5A, and the data is stored only. result = Wire.read(); // Receive DATA result |= Wire.read() << 8; // Receive DATA return result * 0.02 - 273.15; } void setup() { // Initialize M5StickC M5.begin(); M5.MPU6886.Init(); // Initialize 6-Axis IMU M5.Lcd.setRotation(rotation); // Initialize Screen M5.Lcd.setTextColor(WHITE, BLACK); // Initialize I2C PIN for NCIR MLX90614 hat Wire.begin(0, 26); } void loop() { M5.update(); // Toggle Screen Lock if (M5.BtnA.wasPressed()) { if (lock_screen) { lock_screen = false; } else { lock_screen = true; } } // Toggle Rotation Lock if (M5.BtnB.wasPressed()) { if (lock_rotation) { lock_rotation = false; } else { lock_rotation = true; } } if (lock_screen) { M5.Lcd.drawString("Screen Lock", 4, 2, 1); } else { M5.Lcd.drawString(" ", 4, 2, 1); if (lock_rotation) { M5.Lcd.drawString("Rotation Lock", 4, 70, 1); } else { M5.Lcd.drawString(" ", 4, 70, 1); // Change screen orientation with 6-axis IMU float accX = 0; float accY = 0; float accZ = 0; M5.MPU6886.getAccelData(&accX, &accY, &accZ); //Serial.println(accX); if (accX < 0 && rotation == TOP_LEFT) { rotation = TOP_RIGHT; rotated(); } if (accX > 0 && rotation == TOP_RIGHT) { rotation = TOP_LEFT; rotated(); } } // Temperature measurement and display float temp = measurement(); String strTemp = String(temp, 1); if (-10 < temp && temp < 10) { strTemp = " " + strTemp; } if (10 < temp && temp < 100) { strTemp = " " + strTemp; } M5.Lcd.drawRightString(strTemp, 150, 16, 7); //Serial.println(strTemp); } delay(100); }
温度の取得(26行目から38行目)
非接触温度測定ができるMLX90614赤外線温度センサーはI2Cで温度を取得することができます。
Adafruitなどからライブラリが提供されていますが、M5StickCのスケッチ例「NCIR_HAT」はライブラリを使わずに直接I2Cを扱うようになっていたので、温度を取得するコードを「measurement」ファンクションにまとめてそのまま使いました。
すごく短いコードなので理解しやすいと思います。
MLX90614のI2Cアドレス「0x5A」に「0x07」を送信した後、「0x5A」アドレスから2バイト受信した値に係数0.02を掛けて絶対零度273.15を引くと摂氏温度になるようです。
画面のローテーション制御
慣性センサーのMPU6886は「M5StickC」ライブラリから使えるようになっていて、M5.MPU6886.Init()
で初期化した後はM5.MPU6886.getAccelData(&accX, &accY, &accZ)
でX軸・Y軸・Z軸の加速度が取得できます。
M5StickCの横向きの加速度はX軸で取得できるので、X軸が0より大きかったら上側が左向き、X軸が0より小さかったら上側が右向きで文字が読めるように画面をローテイションさせています。
また、実際のローテーションは毎回のループで行うのではなく、上下の向きの変化があったときだけ行うようにしています。
文字表示に関するチョットしたこと
スケッチ例は文字色だけで文字の背景色を指定せずに表示を行っています。
でも、それだと同じ場所に文字を表示すると、前の文字が消えずに上書きしてしまうため文字が読めなくなってしまいます。そのため、文字を表示する前に背景色の長方形で塗りつぶしてから、新しい文字を表示するようなコードになっていました。
でも、そのコードだと文字がチラついて表示されるんです。
そのため、文字の背景色を黒にして、いちいち文字を表示するまえに表示する部分を長方形で塗りつぶさなくてもよくして、表示がちらつかないようにしました。
しかし、その弊害がコードの99行目から104行目に出ています。
これは、表示桁数が減った時にマイナス表示や前の数値が残ってしまうのを防ぐために、空白で背景色だけの文字を表示するためのコードなんです。
UIFlowのBlocklyで作ってたらこうなった
M5Stackが用意しているブロックプログラミング環境UIFlowのBlocklyで同様のプログラムを作ってみました。(図をクリックすると拡大表示します)
しかし、緑色の「画面の向きのモードを[]に設定する」ブロックが正常に動作しなくて、M5StickCをX軸方向に傾けても画面のローテーションが行われませんでした。
また、使用できる文字のフォントセットがArduinoと違っていて、Arduinoで使用していたカッコいい「48ピクセル7セグ風フォント」を使うことができませんでした。
作られたPythonコード
Blocklyで作ったプログラムはPythonでも見ることができます。
それで、Pythonのコードを確認したら、57行目と59行目の後にあるはずの画面をローテーションするコードlcd.setRotation()
がありませんでした。
コードがなければ動作する訳はないですよね。
from m5stack import * from m5ui import * from uiflow import * lcd.setRotation(0) import imu import hat lcd.setRotation(2) import hat setScreenColor(0x000000) hat_ncir0 = hat.get(hat.NCIR) imu0 = imu.IMU() label_temperature = M5TextBox(59, 46, "Temp", lcd.FONT_DejaVu40,0xffffff, rotate=90) label_screen = M5TextBox(79, 5, "Screen Lock", lcd.FONT_Default,0xffffff, rotate=90) label_rotation = M5TextBox(15, 5, "Rotation Lock", lcd.FONT_Default,0xffffff, rotate=90) rotation = None lock_screen = None lock_rotation = None accX = None def buttonA_wasPressed(): global rotation, lock_screen, lock_rotation, accX if lock_screen: lock_screen = False label_screen.hide() else: lock_screen = True label_screen.show() pass btnA.wasPressed(buttonA_wasPressed) def buttonB_wasPressed(): global rotation, lock_screen, lock_rotation, accX if not lock_screen: if lock_rotation: lock_rotation = False label_rotation.hide() else: lock_rotation = True label_rotation.show() pass btnB.wasPressed(buttonB_wasPressed) rotation = 'TOP_LEFT' lock_rotation = False lock_screen = False label_rotation.hide() label_screen.hide() while True: if not lock_screen: if not lock_rotation: accX = imu0.acceleration[0] if accX < 0 and rotation == 'TOP_LEFT': rotation = 'TOP_RIGHT' if accX > 0 and rotation == 'TOP_RIGHT': rotation = 'TOP_LEFT' label_temperature.setText(str("%.1f"%float((hat_ncir0.temperature)))) wait_ms(100) wait_ms(2)
よくよくPythonのコードを見ると、最終行になぜかBlocklyにはないwait_ms(2)
があったりとちょこちょこおかしなところがあります。
今回使用したUIFlowのバージョンはV1.4.4ですが、まだ少し問題が残っているようです。
でも、UIFlowで大まかなプログラムを組んでから、作られたPythonコードを修正する方が、最初からPythonでコードを書くより早いと感じました。
特に非接触温度センサーや慣性センサーなどのデバイスの使い方を調べる必要がないのはいいです!