Grove 赤外線サーマルカメラ IRアレイ MLX90640 110度 を M5Stack で動かしたメモです。この記事は 2021年 ゆるくすすめる ( ワンフットシーバス ) GWアドベントカレンダー の7日目の記事でもあります。
Grove 赤外線サーマルカメラ/ IRアレイ MLX90640 110度
Grove 赤外線サーマルカメラ/ IRアレイ MLX90640 110度 – Grove Thermal Imaging Camera / IR Array – Seeed Studio は、以前別件で比較的扱いやすそうな MLX90640 のセンサーを見つけていて、なんと Grove でも使えるということで、ちょっと実験や仕事面でもやってみたいことがあり、購入したものです。
MLX90640サーマルカメラ というのを使って、Raspberry Pi で比較的安価(6000円くらい?)にサーマルカメラを試した事例。以前の安いカメラだと解像度が 8×8 だったけど 24×32 になって、しかも各マス目で 768 階調?の温度測定値が得られるとのことで、画像を見ても結構高… https://t.co/09M0nsLoIw
— Tanaka Seigo (@1ft_seabass) March 5, 2021
使い方の情報
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で、ぶつけ本番です。(ほんとはこういう状況からスタートするとハマりやすいので避けたい)
ライブラリと一緒に使える 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); }
ちょっと、不必要なところや冗長なところがあるかもしれませんが、ひとまず動作出来てよかったです。
1つ1つのマス目の温度が表示されました。これだけでも楽しい。実際、手をセンサー前で動かすと 35.6 とか 33.4 みたいな温度も出ていたのでちゃんと反応しているようです。
これを、さらにオンライン上にデータを送ったり、ヒートマップに流し込んでみます!