Grove 赤外線サーマルカメラ IRアレイ MLX90640 110度 を M5Stack で動かしたメモ

Grove 赤外線サーマルカメラ IRアレイ MLX90640 110度 を M5Stack で動かしたメモです。この記事は 2021年 ゆるくすすめる ( ワンフットシーバス ) GWアドベントカレンダー の7日目の記事でもあります。

Grove 赤外線サーマルカメラ/ IRアレイ MLX90640 110度

image

Grove 赤外線サーマルカメラ/ IRアレイ MLX90640 110度 – Grove Thermal Imaging Camera / IR Array – Seeed Studio は、以前別件で比較的扱いやすそうな MLX90640 のセンサーを見つけていて、なんと Grove でも使えるということで、ちょっと実験や仕事面でもやってみたいことがあり、購入したものです。

使い方の情報

Grove だといつも通り公式の Wiki が分かりやすいです。

Grove – Thermal Imaging Camera IR-Array MLX90641 – Seeed Wiki

Seeed_Arduino_MLX9064x のライブラリをダウンロードしてインストールします。

Arduino UNO ではメモリ不足で動かない

MLX90640 は結構パワーを使うセンサーのようで、いつも最初に試す Arduino UNO では以下のエラーに。

data section exceeds available space in board
最大32256バイトのフラッシュメモリのうち、スケッチが16452バイト(51%)を使っています。
最大2048バイトのRAMのうち、グローバル変数が14552バイト(710%)を使っていて、ローカル変数で-12504バイト使うことができます。
スケッチが使用するメモリが足りません。メモリを節約する方法については、以下のURLのページを参照してください。http://www.arduino.cc/en/Guide/Troubleshooting#size
ボードArduino Unoに対するコンパイル時にエラーが発生しました。

実際、先ほどの購入ページにも書いてあります。

このモジュールは、I2Cインターフェイス経由でMCUを接続しますが、カメラを駆動するには、20000バイトを超えるRAMを備えたMCUが必要ですので、Arduino Unoのような計算能力が低い開発ボードはこのカメラでは使用できません。IRカメラからの複雑なデータを処理できて、優れたパフォーマンスがあるArch Mixをお勧めします。

ということで、いきなり M5Stack

こうなると、M5Stackで、ぶつけ本番です。(ほんとはこういう状況からスタートするとハマりやすいので避けたい)

image

ライブラリと一緒に使える BasicReading がちゃんと M5Stack Basic で動いたのでホッとしました。

Wiki にある Software Code 1 というのは、いくつかある判定で動作が相性良くないのか、すんなり動かなかったので BasicReading を元に、Software Code 1 の 32 x 24 = 768 マスの情報取得の部分だけ抽出して、合体させたところ、うまく動きました。

購入ページにも説明されてますが、結構なデータ量ですよね。32 x 24。

このカメラのFOV(視野)は55°x35°で、温度測定範囲は-40℃〜300℃です。32 x 24配列の熱センサー(MLX90640)が搭載されて、±1.5℃の精度で離れた場所から物体の温度を検出できます。 熱画像を簡単に取得するために、I2Cプロトコルを使用してカメラから低解像度画像を取得します。

その動作したコードが以下です。

#include <M5Stack.h>

/*
    Read the temperature pixels from the MLX90640 IR array
*/

#include <Wire.h>

#include "MLX90640_API.h"
#include "MLX9064X_I2C_Driver.h"

#if defined(ARDUINO_ARCH_AVR)
    #define debug  Serial

#elif defined(ARDUINO_ARCH_SAMD) ||  defined(ARDUINO_ARCH_SAM)
    #define debug  Serial
#else
    #define debug  Serial
#endif

const byte MLX90640_address = 0x33; //Default 7-bit unshifted address of the MLX90640

#define TA_SHIFT 8 //Default shift for MLX90640 in open air

static float mlx90640To[768];
paramsMLX90640 mlx90640;

void setup() {
  Wire.begin();
  Wire.setClock(400000); //Increase I2C clock speed to 400kHz

  M5.begin();
  
  // debug.begin(9600);
  debug.begin(115200);

  // START
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setTextSize(2);
  // M5.Lcd.printf("START");
  M5.Lcd.println("<<<< MLX90640 Sensor >>>>");
  
  while (!debug); //Wait for user to open terminal
  debug.println("MLX90640 IR Array Example");

  if (isConnected() == false) {
      debug.println("MLX90640 not detected at default I2C address. Please check wiring. Freezing.");
      while (1);
  }
  debug.println("MLX90640 online!");
  M5.Lcd.println("MLX90640 online!");

  //Get device parameters - We only have to do this once
  int status;
  uint16_t eeMLX90640[832];
  status = MLX90640_DumpEE(MLX90640_address, eeMLX90640);
  if (status != 0) {
      debug.println("Failed to load system parameters");
  }

  status = MLX90640_ExtractParameters(eeMLX90640, &mlx90640);
  if (status != 0) {
      debug.println("Parameter extraction failed");
  }

  //Once params are extracted, we can release eeMLX90640 array

}

 
void loop() {

  M5.update();

  // Wiki のソース取得
  long startTime = millis();
  for (byte x = 0 ; x < 2 ; x++) {
      uint16_t mlx90640Frame[834];
      int status = MLX90640_GetFrameData(MLX90640_address, mlx90640Frame);

      float vdd = MLX90640_GetVdd(mlx90640Frame, &mlx90640);
      float Ta = MLX90640_GetTa(mlx90640Frame, &mlx90640);

      float tr = Ta - TA_SHIFT; //Reflected temperature based on the sensor ambient temperature
      float emissivity = 0.95;

      MLX90640_CalculateTo(mlx90640Frame, &mlx90640, emissivity, tr, mlx90640To);
  }
  long stopTime = millis();
  
  int x;
  for (x = 0 ; x < 768 ; x++) {
      if(x % 8 == 0) debug.println();
      debug.print(mlx90640To[x], 2);
      debug.print(",");
  }
  debug.println("");
  
}

//Returns true if the MLX90640 is detected on the I2C bus
boolean isConnected() {
    Wire.beginTransmission((uint8_t)MLX90640_address);
    if (Wire.endTransmission() != 0) {
        return (false);    //Sensor did not ACK
    }
    return (true);
}

ちょっと、不必要なところや冗長なところがあるかもしれませんが、ひとまず動作出来てよかったです。

image

1つ1つのマス目の温度が表示されました。これだけでも楽しい。実際、手をセンサー前で動かすと 35.6 とか 33.4 みたいな温度も出ていたのでちゃんと反応しているようです。

これを、さらにオンライン上にデータを送ったり、ヒートマップに流し込んでみます!