Arduinoで非接触温度センサGY-906

こんなこと、やります。

  • Arduinoで非接触温度センサGY-906を使う
  • GY-906で物体の温度と環境温度の測定
  • M5StickC PLUSでHATモジュール化
  • メディアンフィルタで測定精度を上げる

Arduinoで非接触温度センサGY-906
Arduinoで非接触温度センサGY-906

つかうもの

つかうものは、Arduinoと非接触温度センサGY-906です。

非接触温度センサGY-906

非接触温度センサGY-906は、赤外線センサーのMLX90614を搭載したセンサモジュールです。I2C対応のデジタルセンサです。電源電圧は、5Vと3V3のどちらでもOKです。

Arduinoボード

Arduino Uno Rev3を使用しました。

ほかのArduinoでも構いません。

まだ、Arduinoをお持ちでないようでしたら、オススメArduinoどれを選べばいい?Arduinoで電子工作をはじめる方へをご参考になさってみてください。

開発環境

この記事で使用した開発環境は次のとおりです。私はPlatform IOを使っていますが、Arduino IDEで構いません。

項目バージョン
統合開発環境Visual Studio Code x Platform IO
パソコンmacOS Big Sur 11.0.1

準備

GY-906ライブラリのインストール

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

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

Arduinoで非接触温度センサGY-906を使う

Arduinoで非接触温度センサGY-906を使ってターゲットの温度と、外気温を取得してみます。

ArduinoとGY-906の配線

ArduinoとGY-906の配線図です。

ArduinoとGY-906の配線
ArduinoとGY-906の配線

Arduinoの3.3Vをセンサモジュールへ供給し、SCLとSDAをそれぞれつなぎます。GY-906センサモジュールは、プルアップ抵抗が内蔵されているのでそのまま繋ぐことが可能です。

ソースコード(Arduino版)

こちらがGY-906を使った基本的なプログラムになります。

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_MLX90614.h>

Adafruit_MLX90614 mlx = Adafruit_MLX90614();

void setup() {
  Serial.begin(9600);
  mlx.begin();  
}

void loop() {
  Serial.print("Ambient = "); Serial.print(mlx.readAmbientTempC()); 
  Serial.print("*C\tObject = "); Serial.print(mlx.readObjectTempC()); Serial.println("*C");
  // Serial.print("Ambient = "); Serial.print(mlx.readAmbientTempF()); 
  // Serial.print("*F\tObject = "); Serial.print(mlx.readObjectTempF()); Serial.println("*F");

  Serial.println();
  delay(1000);
}

ソースコードの解説

プログラム中のObjectが「対象物の赤外線から得た温度」です。Ambientは環境温度、つまり外気温を意味します。センサー自体が熱せられてしまうと、外気温も上がってしまうので注意しましょう。

プログラムでは摂氏温度を取得しましたが、華氏温度で取得したい場合は、readObjectTempFを使ってください。

体温を測定してみた

実際に指を近づけて体温を測定してみました。こちらの映像のとおり、かなり接近させないと正確な温度測定は難しいです。

Arduinoで非接触温度センサGY-906
Arduinoで非接触温度センサGY-906

赤外線センサですので、あらかじめケースで覆われている方が扱いやすく、精度も高いかもしれません。

▼ また、体温測定が目的でしたら、素直に製品を買われることをオススメしておきます。

M5StickC PLUS改良版

ここからはM5StickC PLUSを使った改良版をご紹介します。GY-906はHATモジュールにしてみました。

MLX90614とM5StickC PLUS
MLX90614とM5StickC PLUS

用意するもの

M5StickC PLUSの使い方はM5StickC PLUSでArduinoをはじめよう!をご参考になさってみてください。

HAT化について

M5StickC PLUSでセンサを扱いやすくするため、エッチングで基板製作してセンサをHAT化しました。もちろんユニバーサル基板でも作れますし、スイッチサイエンスさんから出ているユニバーサル基板内蔵のHATモジュールを使ってもよさそうです。

M5StickC PLUSとGY-906の配線

M5StickC PLUSGY-906
GNDGND
3V3VIN
G0SDA
G26SCL

センサをHAT化したかったので、G0、G26を使ったI2C通信を行います。

GROVEケーブルを使って、G32(SDA)、G33(SCL)に接続しても可能です。

プログラムの仕様

これからつくるプログラムは、次のとおりです。

  • M5StickC PLUSのAボタンを押して、温度測定の開始
  • 測定が完了したらビープ音を鳴らし、測定を停止する
  • 対象物の温度を100回サンプルし、メディアンフィルタで中央値を計算
  • ディスプレイに、対象物の温度と外気温を表示する

ソースコード

こちらが、M5StickC PLUS版に改良されたソースコードとなります。ただし、Filter.hは自作ライブラリですので、後述を参考に実装してください。

#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);
}

ソースコードの注意点

setCpuFrequencyMhz(10)でCPUのクロック周波数を10MHzに指定しないと動きませんでした。

また、G0、G26をI2Cとして使う場合は、Wire.begin(0, 26, 400000);のように指定します。

メディアンフィルタの実装

先ほどのプログラム中のFilterクラスについて解説します。このクラスは自作ライブラリで、メディアンフィルタの関数が実装されています。

メディアンフィルタとは

メディアンフィルタとは、サンプルを昇順または降順にならべて、その中央値を採用するフィルタです。平均値と違い、大幅にズレた値などを無視できるため、画像のノイズ除去などに使われます。

並べ替えて、真ん中の値を採用する

図のように、周りの数字が1、2、3のように低いのに、明らかに不自然な7という大きなセンサノイズが入った場合を考えてみます。

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

メディアンフィルタでは、サンプル(周囲の値)を小さい順に並べ、真ん中の値を採用します。至ってシンプルです。

平均値の場合はノイズ値も加算されてしまいますが、メディアンフィルタですとセンサノイズをキレイにに除去できます。この利点を活かして、画像のフィルタリングなどでよく使われます。

今回の温度測定でも、手ブレなどによりセンサノイズが混入しますので、メディアンフィルタで少しでも正確な温度を測定できないか試してみました。

ソースコード

次に示すのが、メディアンフィルタを実装しているFilterクラスのソースコードです。

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言語の配列の注意点

メディアンフィルタのライブラリをつくっている時に、大変つまづいたことがありますので述べておきます。

それは、C言語で配列を引数に渡す時のことです。

ダメな例

こちらのプログラムでは、sizeof(array)の要素数を正しく取得できません。理由は、関数の引数に渡された配列がポインタ型になるためです。

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

sizeof(array)sizeof(int *) と同じことになってしまいます。

正しい例

こちらのように、関数の呼び出し側であらかじめ要素数を計算し、引数へ渡しましょう。

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

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

HAT化した非接触温度センサとM5StickC PLUSを使って、色々なものを測定して遊んでみました。

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

はんだごての温度測定
はんだごての温度測定

フライパンの温度や、はんだごての温度など、直接さわれない高温なモノの温度測定も可能です。正確さはそれほどないですが、興味のある時にサクッと温度測定できるという手軽さはなかなかの魅力ではないでしょうか。

記事に関するご質問などがあればTwitterへお返事ください。
この記事で紹介した商品
人気のArduino互換機
Arduinoで人気の周辺パーツ
Arduinoのオススメ参考書

▼ Arduino初心者向きの内容となっています。ほかのArduino書籍と比べて図や説明がとてもていねいで、読みやすかったです。Arduinoで一通りのセンサーが扱えるようになります。

▼ 外国人が書いた本を翻訳したものです。この手の書籍は、目からうろこな発見をすることが多いです。

▼ Arduinoの入門書を既に読んでいる方で、次のステップを目指したい人向きの本です。C言語のプログラミングの内容が中心です。ESP32だけでなく、ふつうのArduinoにも役立つ内容でした。

関連記事