M5StickC PLUSで非接触温度センサMLX90614(GY-906)

MLX90614とM5StickC PLUS
MLX90614とM5StickC PLUS

この記事では、Arduino互換機であるESP32をベースとしたM5StickC PLUSで、非接触温度センサMLX90614(GY-906)の使い方を解説します。コロナ禍なので、体温測定にと思って始めたのがきっかけでした。また、非接触温度センサなので、手で触れないような熱いものも測定可能です。

センサとマイコンとの通信は、I2C通信になります。I2Cと言っても何も難しいことはなく、Adafruitのライブラリを使えばカンタンですので、ぜひ参考にしてみて下さい。M5StickC PLUSの使い方はM5StickC PLUSでArduinoをはじめよう!をご参考になさってみてください。

M5StickC PLUSで扱いやすいようにHAT化もしてみました。▼普通のArduinoやArduino互換機でMLX90614を使いたい場合は、こちらの記事を参考にしてみてください。

M5StickC PLUSとGY-906の配線

M5StickC PLUSGY-906
GNDGND
3V3VIN
G0SDA
G26SCL

G32(SDA)、G33(SCL)を使ってGROVEケーブルで繋いでも良いのですが、今回はセンサーをHAT化したかったので、G0、G26を使ってI2C通信することにしました。

MLX90614センサの電圧は、5Vと3V3のどちらでもOKなようです。ここでは3V3に繋いでみました。

ライブラリのインストール

Adafruit-MLX90614-Libraryのインストール
Adafruit-MLX90614-Libraryのインストール

Arduino IDEをお使いの場合は、ライブラリマネージャで「Adafruit MLX90614」検索し「Adafruit-MLX90614-Library」のバージョン1.1.1をインストールしましょう。最新の2.0.0を使うと、もしかしたら本記事のプログラムでは動かないかもしれません。

M5StickC PLUSのプログラミング

メインプログラム

MLX90614では、環境温度(気温)対象物の温度を同時に測定できます。対象物の温度は赤外線を使用して測定されます。環境温度は、ケースの温度を測定しているようです。なのでセンサー自体が熱くなったりすると気温とは違う温度になってしまうので注意しましょう。

これらの温度を、Adafruit_MLX90614ライブラリではreadAmbientTempC()readObjectTempCでカンタンに取得できます。摂氏温度だけでなく華氏温度も測ることができます。

▼次のプログラムでは、Aボタンを押すことで環境温度と対象物の温度を瞬時に測定できるようにしました。測定が完了すると「ピッピ」とビープ音がなるようにしてます。

また、対象物の温度のみ、100回サンプルして中央値を取るようにメディアンフィルタをかけてみました。メディアンフィルタは、Filterの名前で外部クラス化してます。後ほど説明します。

#include <M5StickCPlus.h>
#include <Adafruit_MLX90614.h>
#include <Wire.h>
#include "Filter.h"

Adafruit_MLX90614 mlx = Adafruit_MLX90614();
Filter filter = Filter();

#define BUTTON_A 37

bool enableMesure = true;

void measureTemp() {
    Serial.println("Starting measure temp...");
    size_t s_len = 100;
    float s[s_len];

    float ambientTemp = mlx.readAmbientTempC();

    for (size_t i = 0; i < s_len; i++) {
        s[i] = mlx.readObjectTempC();
    }

    float objectTemp = filter.medianFilter(s, s_len);

    displayLCD(ambientTemp, objectTemp);
    playBeep();
}


void setup() {
    M5.begin();

    while(!setCpuFrequencyMhz(10)){ // ←必須(CPU: 240MHz, 160, 80, 40, 20, 10)
        ;
    }
    Wire.begin(0, 26); // ←必須(G0->SDA, G26->SCL)
    mlx.begin(); 

    M5.Axp.ScreenBreath(10);
    M5.Lcd.fillScreen(WHITE);
    M5.Lcd.setTextColor(BLACK);
    M5.Lcd.setTextSize(2);
    M5.Lcd.setCursor(5, 10);
    M5.Lcd.printf("Press the A button to start and end measurement.");

}

void loop(){
    M5.update();
    if(M5.BtnA.wasPressed()){
        Serial.println("M5.BtnA.wasPressed()");

        if (enableMesure) {
            enableMesure = false;
            measureTemp();
            enableMesure = true;
        }
    }
}



void displayLCD(float amb, float obj) {
    M5.Lcd.fillScreen(WHITE);

    M5.Lcd.setTextSize(1);
    M5.Lcd.setCursor(5, 10);
    M5.Lcd.printf("Ambient temperature", amb);

    M5.Lcd.setTextSize(3);
    M5.Lcd.setCursor(5, 25);
    M5.Lcd.printf("%.1f", amb);

    M5.Lcd.setTextSize(1);
    M5.Lcd.setCursor(5, 70);
    M5.Lcd.printf("Object temperature");

    M5.Lcd.setTextSize(5);
    M5.Lcd.setCursor(5, 85);
    M5.Lcd.printf("%.1f", obj);

}


void playBeep() {
    M5.Beep.tone(3000);
    delay(50);
    M5.Beep.mute();
    delay(50);
    M5.Beep.tone(3000);
    delay(50);
    M5.Beep.mute();
    delay(50);
}

このプログラムの中で、とくに注意点として次の二つをあげておきます。

  1. setCpuFrequencyMhz(10)でCPUのクロック周波数を10MHzに指定しないと動かない
  2. G0、G26をI2Cで使う場合は、Wire.begin(0, 26, 400000);のように指定しないと動かない

メディアンフィルタ

次に、メディアンフィルタのプログラムを紹介します。

メディアンフィルタとは、サンプルを昇順または降順にならべて、その中央値を採用するというものです。

メディアンフィルタ
メディアンフィルタ

平均値と違って、大幅にズレた値などを無視できるため、画像のノイズ除去として使われたりします。

メディアンフィルタの関数をメインプログラム中に書いても良かったのですが、今後ローパスフィルタなどのフィルタを追加することを考え、C++でクラス化してみました。次の二つのファイルをFilter.hFilter.cppを作成し、.inoと同じ場所に保存して使用して下さい。

Filter.h

#ifndef Filter_h
#define Filter_h
#include <Arduino.h>

class Filter {
    public:
        Filter();
        float medianFilter(float s[], size_t len);
};
#endif

Filter.cpp

#include "Filter.h"


Filter::Filter() {
}


float Filter::medianFilter(float s[], size_t len) {
    for (size_t i=0; i<len; ++i) {
        for (size_t j=i+1; j<len; ++j) {
            if (s[i] > s[j]) {
                float tmp =  s[i];
                s[i] = s[j];
                s[j] = tmp;
            }
        }
    }

    int m = len / 2;
    return s[m];
}

C言語では配列を引数で渡す時に要注意です。

▼ダメな例

void hoge(int array[]) {
  for (size_t i = 0; i < sizeof(array) / sizeof(array[0]); ++i) {
     doSomething();
   }
}

上記のプログラムではsizeof(array)で配列の要素数取得しようとしていますが、関数の引数に渡された配列はポインタ型になるため正しい要素数を計算できません。sizeof(array)sizeof(int *) になってしまいます。

▼正しい例

void hoge(int array[], size_t len) {
    for (size_t i = 0; i < len; i++) {
     doSomething();
  }
}

こちらのように、関数の呼び出し側で要素数を指定してあげましょう。実はこれに気づかず数時間も費やしてしまいました。

色々なものを測定してみた

さて、実際にHAT化してM5StickC PLUSで使うとなかなか便利ですね。気軽に色々なものの温度を測ることができます。一瞬で測定できてしまうところが何よりうれしいです。

体温の測定の場合は、場所によってだいぶ違ってくるので残念ながら難しかったです。調理器具や工具など、モノの温度測定にはたいへん向いてると思いました。触れたら危険なものも赤外線センサなら測りやすいですよね。

フライパンの温度測定
フライパンの温度測定

半田ごての温度測定
半田ごての温度測定

皆さんも、ArduinoやRaspberry Piなどで非接触温度センサを試してみて下さいね。

記事に関するご質問などがあればTwitterへお返事ください。
この記事で紹介した商品
M5Stackのオススメ参考書
M5Stack製品
M5StickCで使えるHat
関連記事