SJKのアイコン画像
SJK 2024年05月22日作成 (2024年06月05日更新) © MIT
製作品 製作品 閲覧数 557
SJK 2024年05月22日作成 (2024年06月05日更新) © MIT 製作品 製作品 閲覧数 557

フリーザーの遠隔監視・警報システム

フリーザーの遠隔監視・警報システム

概要

メディカル・バイオ系の研究室には必ずあるフリーザーやディープフリーザー。特に、ディープフリーザーの温度上昇は今までの研究資産を失う可能性が有ります。それ故、巷では様々なメーカーからフリーザーの遠隔監視・警報システムが販売されていますが、それなりの価格がします。
そこで本記事では、比較的低価格(~6,500円)で簡易の温度遠隔監視・警報システムを構築しました。

  • (注)記事の最後にAtomS3 LiteとUnit-KmeterISOユニットを使ったはんだ付け不要(しかも安い~4,500円)バージョンも追記しました。
  • (注2)NanoC6とUnit-KmeterISOユニットを使った(~4,000円)バージョンも追記しました。

使用したモジュール

開発環境

Arduino IDE v2.2.1


実装したこと

K型熱電対+熱電対アンプモジュールで温度を計測し、M5AtomLiteでwifiで3分ごとにThingSpeakに温度データを送信・蓄積し、設定温度以上で警報をLineNotifyで通知するシステムを構築した。同時に、ThingSpeakが一定時間データを受信しなかったときの警報と毎日の定時連絡機能も付加した。

ThingSpeakの初期設定

  • ThingSpeakのページを開いて新しいアカウントを作成。Sign Upをクリック。ユーザID・メールアドレス・タイムゾーン・パスワードを設定しCreate Accountボタンをクリック。
  • チャンネルの作成。チャンネルにはデータ・場所・状態を登録できる(8個まで)。今回は2個(庫内温度・モジュール温度)のセンサデータを書き込むためのチャンネルを作成。
  1. メニューバーの Channels > MyChannels をクリック。
  2. New Channel ボタンをクリック。
  3. 以下の情報を入力。
  4. Name;(チャンネルの名前)
  5. Field1; Temp(フィード1の名前)
  6. Field2: AmbTemp(フィード2の名前)
  7. Make Public(一般公開しても良いならチェック)
  8. Save Channel ボタンで保存
  • 書き込み用のAPIキーの取得とチャンネルID
    スケッチにはチャンネルIDとAPIキーが必要なので、
  1. メニューバーの Channels > MyChannels をクリックして、先ほど作成したチャンネルを開く。
  2. Channel ID と書かれた7桁の数字がチャンネルID。
  3. API Keys をクリックして Write API Key と書かれた英数字の文字列が書き込み用のAPIキー。
    上記2つをメモしておく

結線

  • アンプモジュールに付属のターミナルブロック(熱電対接続部)をはんだ付けします。

  • ATOMICプロトキットの上半分に熱電対アンプモジュールをはめ込みますので、プロトキット付属の基盤の上部をニッパーなどで切り離します。

  • 下図のように結線します。点線部は基板下で配線。
    キャプションを入力できます

  • 熱電対の黄色のコネクタを外し、金属線をむき出しにしてアンプモジュールのターミナルブロックに繋げます。極性がありますので、コネクタを外すときにどちらがプラス(+)でどちらがマイナス(-)だったか分かるようにした方が後々楽です。もし分からなくなったら、中身の被覆に赤糸ある方がプラスです。
    キャプションを入力できます

プログラム

M5atom_MAX31855_SPI_ThingSpeak.ino

#include <M5Atom.h> #include <ThingSpeak.h> #include <SPI.h> #include <WiFi.h> #include <Adafruit_MAX31855.h> // Wifi設定(Wifi環境に合わせて変更) const char ssid[] = "your_SSID"; const char pass[] = "your_password"; // ネットワーク設定(必要ならネットワーク環境に合わせて変更) // IPAddress ip(192, 168, 100, 100); // IPAddress gateway(192, 168, 100, 1); // IPAddress subnet(255, 255, 255, 0); // ThingSpeak設定(チャンネルIDと書き込み用APIキーを変更) const unsigned long myChannelNumber = your_Ch_##; const char * myWriteAPIKey = "your_API_Key"; // ThingSpeak接続用 WiFiClient client; // MAX31855のソフトウエアSPIのIOピン番号を指定 // M5Atom lite用 #define MAXDO 23 #define MAXCS 19 #define MAXCLK 22 // initialize the Thermocouple Adafruit_MAX31855 thermocouple(MAXCLK, MAXCS, MAXDO); void setup() { M5.begin(true, false, true); // 起動メッセージ出力 //Serial.begin(9600); //Serial.println("Hello M5atom!"); // 温度データ取得 float temperature = thermocouple.readCelsius() - 14; //測定値-14℃, 適宜変更 float int_temperature = thermocouple.readInternal(); //温度取得に失敗したら再起動 if (isnan(temperature)) { ESP.restart(); } //LED制御 if (temperature < -65) { M5.dis.drawpix(0, 0x00ff00); // green } else { M5.dis.drawpix(0, 0xff0000); // red } // ステーションモードに設定 WiFi.mode(WIFI_STA); // IPアドレス設定 // WiFi.config(ip, gateway, subnet); // 接続開始(繋がるまで40回繰り返す) int lpcnt = 0 ; WiFi.disconnect(true, true); delay(500); WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { M5.dis.drawpix(0, 0xFF00FF); // purple delay(2 * 1000); M5.dis.clear(); //LED消灯 delay(3 * 1000); lpcnt += 1 ; if (lpcnt > 40) { ESP.restart(); } } // センサーデータアップロード開始 ThingSpeak.begin(client); M5.dis.drawpix(0, 0x0000ff); // blue // フィードに値を設定 ThingSpeak.setField(1, temperature); ThingSpeak.setField(2, int_temperature); // ThingSpeakに送信 //Serial.println(); //Serial.print("Write fields..."); int x = ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey); // Check the return code if (x == 200) { //Serial.println("done"); M5.dis.clear(); //LED消灯 delay(1000); //これ入れないとLED消えないでdeepsleepする // 2.5 min のディープスリープタイマー設定(usec) esp_sleep_enable_timer_wakeup(150 * 1000 * 1000); // GPIO39(M5atomのHOMEボタン)がLOWになったらディープスリープから復帰 pinMode(GPIO_NUM_39, INPUT_PULLUP); esp_sleep_enable_ext0_wakeup(GPIO_NUM_39, LOW); // ディープスリープ esp_deep_sleep_start(); } else { ESP.restart(); } } void loop () { }

確認

  • ここまでできたら、ThingSpeakのMyChannelのウェブページを開くと熱電対で読み取った温度データが見れるはずです。

設置

  • 裸の熱電対をそのまま超低温冷凍庫に入れると、霜がついて正確な温度計測ができないので下図のように1.5mLチューブの蓋に穴を開けて熱電対を差し込んだものを超低温冷凍庫に設置します。
    キャプションを入力できます
  • 設置方法は、フリーザー購入時であれば取り込み口から、すでに稼働中であれば扉の隙間から奥に差し込みます。
  • 冷凍庫の表示温度と値がずれている場合には
    float temperature = thermocouple.readCelsius() - 14
    の補正値の値を変更してプログラムを再度書き込みます。

LINE Notifyの設定

異常検知や定時連絡をLINEで受け取るためのLINE Notifyの設定をします。
やるべきことは、LINE Notifyアカウントと友達になって、アクセストークンを発行することです。
LINE Notifyの初め方等を参照してやってみてください。
(注)このアクセストークンは二度と表示されないので、必ずメモしてください。再発行するとトークン番号が変わります。

ThingSpeakでのイベントトリガーとThingHTTPの設定

温度が閾値を超えたときの設定(ThingHTTP)

  • ThingSpeakページ上部の「Apps」プルダウンから「ThingHTTP」を選びます。
  • 緑色の「New ThingHTTP」ボタンを押します。
    Name: 適当な名前を入れます
    URL: https://notify-api.line.me/api/notify
    Method: POST
    Content Type: application/x-www-form-urlencoded
    HTTP Version: 1.1
    Headers (Name: Authorization
    Headers (Value: Bearer your_LINE Notify_token (Bearer[半角スペース]先ほど発行されたLINE Notifyのアクセストークン)
    Body: message=に続きここに書いた内容がそのままLINE Notifyに通知されます。%%で挟まれた部分は置き換えキーでメッセージ送信時のデータを引数にできます。日本語でもOK。
    キャプションを入力できます
  • 入力が終わったら、「Save ThingHTTP」ボタンを押して保存します。

温度が閾値を超えたときの設定(React)

  • ThingSpeakページ上部の「Apps」プルダウンから「React」を選びます。
  • 緑色の「New React」ボタンを押します。
    React Name: 適当な名前を入れます
    Condition Type: Numeric
    Test Frequency: On Data Insertion
    Condition (If channel: 温度検知したいChannelを選びます
    Condition (field: 温度検知したいfield番号を選びます
    Condition (field: is greater than
    Condition (field: これ以上で知らせて欲しい温度(私は-65℃にしています)
    Action: ThingHTTP
      then perform ThingHTTP: 先ほど作成したThingHTTPを指定します。
    Options: Run action only the first time the condition is met(上記設定した閾値を超えたら1回だけ通知がきます)
    キャプションを入力できます
  • 入力が終わったら、「Save React」ボタンを押して保存します。
  • ここまでで、温度異常時の通知設定は終了です。

定時連絡設定(ThingHTTP)

  • ThingSpeakページ上部の「Apps」プルダウンから「ThingHTTP」を選びます。
  • 緑色の「New ThingHTTP」ボタンを押します。
  • 「Body」部分以外は温度異常時の設定と変わりません。
    Body: 定時連絡用のメッセージを記入します。
    例

定時連絡設定(TimeControl)

  • ThingSpeakページ上部の「Apps」プルダウンから「TimeControl」を選びます。
  • 緑色の「New TimeControl」ボタンを押します。
    Name: 適当な名前を入れます
    Frequency: Recurring
    Recurrence: Day
    Time: 好みの時刻(例えば、10:00 pm)
    Action: ThingHTTP
      then perform ThingHTTP: 先ほど作成したThingHTTPを指定します。
  • 入力が終わったら、「Save TimeControl」ボタンを押して保存します。
  • ここまでで、定時連絡の通知設定は終了です。

ThingSpeakへのデータ不達時の通知設定(ThingHTTP)

  • ThingSpeakページ上部の「Apps」プルダウンから「ThingHTTP」を選びます。
  • 緑色の「New ThingHTTP」ボタンを押します。
  • 「Body」部分以外は温度異常時の設定と変わりません。
    Body: データ不達連絡用のメッセージを記入します。

ThingSpeakへのデータ不達時の通知設定(React)

  • ThingSpeakページ上部の「Apps」プルダウンから「React」を選びます。
  • 緑色の「New React」ボタンを押します。
    React Name: 適当な名前を入れます
    Condition Type: No Data Check
    Test Frequency: Every 10 minutes(好みでどうぞ)
    Condition (If channel: データ不達検知したいChannelを選びます
    Condition (has not been updated for: 15min(データ不達時間, 好みでどうぞ)
    Action: ThingHTTP
      then perform ThingHTTP: 先ほど作成したThingHTTPを指定します。
    Options: Run action only the first time the condition is met

LINE Notifyに通知が来るかの確認

  • ThingSpeakページ上部の「Apps」プルダウンから「ThingHTTP」を選びます。
  • 通知テストをしたいThingHTTPの「View」ボタンを押します。
  • 右上部に四角で囲まれたGETから始まるhttps://....(下図)がありますので、GETを含めず「https:」以下をコピーしてブラウザで開くと設定した通知が送られて、ブラウザには「{"status":200,"message":"ok"}」と表示されるはずです。
    キャプションを入力できます

実際に使ってみて

コロナ禍が始まりラボに頻繁に行けなくなった時期の2020年4月からシステムの稼働を始めました、2024年1月に超低温冷凍庫が故障し、アラーム発報のおかげで中身を迅速に移し替えることができました。現在は、新しい超低温冷凍庫で順調に稼働しています。

追記

この記事を書きながらM5Stack社のページを閲覧してたら、MAX31855モジュールに熱電対のソケットが付いたUnitを見つけました。M5Stack社の製品は開発の回転が早く当該ユニットがEOPになる可能性もありますが、煩わしいはんだ付けが必要ないので需要はあると思いESP32マイコンも最新のAtomS3 Liteに変更したバージョンを以下に記します。(変更が必要な部分のみ追記しました)

(追記版)使用したモジュール

(追記版)開発環境

Arduino IDE v2.3.2


(追記版)結線

  • Unit-KmeterISOに付属のGroveケーブル(黒赤黄白のケーブル)でAtomS3 LiteのGroveポートとUnit-KmeterISOのGroveポートを接続します。(ノッチがあるので挿し間違いは無いです)
  • Unit-KmeterISOの熱電対ソケットに熱電対を挿し込みます。(+が細くて-が太いです)
    キャプションを入力できます

(追記版)プログラム

  • 下記、AtomS3_KmeterISO_ThingSpeak.inoの
    WiFi設定部分
    const char ssid[] = "your_SSID";
    const char pass[] = "your_password";

    ThingSpeak設定部分
    const unsigned long myChannelNumber = your_Ch_##;
    const char * myWriteAPIKey = "your_API_Key";
    を各自の環境に書き換えてAtomS3 Liteに書き込みます。

AtomS3_KmeterISO_ThingSpeak.ino

#include <M5AtomS3.h> #include <ThingSpeak.h> #include <WiFi.h> #include <M5UnitKmeterISO.h> // Wifi設定(Wifi環境に合わせて変更) const char ssid[] = "your_SSID"; const char pass[] = "your_password"; // ネットワーク設定(固定IP用, ネットワーク環境に合わせて変更) // IPAddress ip(192, 168, 100, 100); // IPAddress gateway(192, 168, 100, 1); // IPAddress subnet(255, 255, 255, 0); // ThingSpeak設定(チャンネルIDと書き込み用APIキーを変更) const unsigned long myChannelNumber = your_Ch_##; const char* myWriteAPIKey = "your_API_Key"; // ThingSpeak接続用 WiFiClient client; //KmeterISOユニットと通信 M5UnitKmeterISO kmeter; void setup() { AtomS3.begin(true); // Init M5AtomS3Lite. AtomS3.dis.setBrightness(50); // Serial.begin(115200); //Unit KmeterISOの接続確認 while (!kmeter.begin(&Wire, KMETER_DEFAULT_ADDR, 2, 1, 100000L)) { //Serial.println("Unit KmeterISO not found"); AtomS3.dis.drawpix(0XFFFFFF); // white AtomS3.update(); } AtomS3.dis.clear(); //LED消灯 // 温度データ取得 float temperature = (kmeter.getCelsiusTempValue() / 100.0) - 12.0; //測定値-12℃, 適宜変更 float int_temperature = kmeter.getInternalCelsiusTempValue() / 100.0; //温度取得に失敗したら再起動 if (isnan(temperature)) { ESP.restart(); } //LED(-65C以上なら赤, 以下なら緑) if (temperature < -65) { AtomS3.dis.drawpix(0x00ff00); // green AtomS3.update(); } else { AtomS3.dis.drawpix(0xff0000); // red AtomS3.update(); } // ステーションモードに設定 WiFi.mode(WIFI_STA); // IPアドレス設定(固定IP用) // WiFi.config(ip, gateway, subnet); // WiFi接続開始 int lpcnt = 0; WiFi.disconnect(true, true); delay(500); WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { AtomS3.dis.drawpix(0xFF00FF); // purple delay(2 * 1000); AtomS3.update(); AtomS3.dis.clear(); //LED消灯 delay(3 * 1000); lpcnt += 1; if (lpcnt > 40) { ESP.restart(); } } // センサーデータアップロード開始 ThingSpeak.begin(client); AtomS3.dis.drawpix(0x0000ff); // blue AtomS3.update(); // フィールドに値を設定 ThingSpeak.setField(1, temperature); ThingSpeak.setField(2, int_temperature); // ThingSpeakに送信 //Serial.println(); //Serial.print("Write fields..."); int x = ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey); // Check the return code if (x == 200) { //Serial.println("done"); AtomS3.dis.clear(); //LED消灯 AtomS3.update(); //これ入れないとLED消えないでdeepsleepする // 2.5 min のディープスリープタイマー設定(usec) esp_sleep_enable_timer_wakeup(150 * 1000 * 1000); // ディープスリープ esp_deep_sleep_start(); } else { ESP.restart(); } } void loop() { }
  • 冷凍庫の表示温度と値がずれている場合には
    float temperature = (kmeter.getCelsiusTempValue() / 100.0) - 12.0
    の補正値の値を変更してプログラムを再度書き込みます。

追記2

M5 NanoC6対応のArduino core for the ESP32のstableバージョン3(3.0.0)がリリースされましたので、NanoC6版のスケッチも追記します。

(追記2版)使用したモジュール

(追記2版)開発環境

Arduino IDE v2.3.2


(追記2版)プログラム

  • 下記、NanoC6_KmeterISO_ThingSpeak.inoの
    WiFi設定部分
    const char ssid[] = "your_SSID";
    const char pass[] = "your_password";

    ThingSpeak設定部分
    const unsigned long myChannelNumber = your_Ch_##;
    const char * myWriteAPIKey = "your_API_Key";
    を各自の環境に書き換えてNanoC6に書き込みます。

NanoC6_KmeterISO_ThingSpeak.ino

#include <ThingSpeak.h> #include <WiFi.h> #include <M5UnitKmeterISO.h> #include <Wire.h> // Wifi設定(Wifi環境に合わせて変更) const char ssid[] = "your_SSID"; const char pass[] = "your_password"; // ネットワーク設定(固定IP用, ネットワーク環境に合わせて変更) // IPAddress ip(192, 168, 100, 100); // IPAddress gateway(192, 168, 100, 1); // IPAddress subnet(255, 255, 255, 0); // ThingSpeak設定(チャンネルIDと書き込み用APIキーを変更) const unsigned long myChannelNumber = your_Ch_##; const char* myWriteAPIKey = "your_API_Key"; // ThingSpeak接続用 WiFiClient client; //KmeterISOユニットと通信 M5UnitKmeterISO kmeter; #define RGB_BRIGHTNESS 32 // Change brightness (max 255) void setup() { // Serial.begin(115200); pinMode(GPIO_NUM_19, OUTPUT); // EN(RGBPWR) pinMode(GPIO_NUM_20, OUTPUT); // RGBLED digitalWrite(GPIO_NUM_19, HIGH); Wire.begin(2, 1); while (!kmeter.begin(&Wire, KMETER_DEFAULT_ADDR, 2, 1, 100000L)) { //Serial.println("Unit KmeterISO not found"); neopixelWrite(GPIO_NUM_20, RGB_BRIGHTNESS, RGB_BRIGHTNESS, RGB_BRIGHTNESS); // white delay(100); } neopixelWrite(GPIO_NUM_20, 0, 0, 0); //LED消灯 delay(100); // 温度データ取得 float temperature = (kmeter.getCelsiusTempValue() / 100.0) - 12.0; float int_temperature = kmeter.getInternalCelsiusTempValue() / 100.0; delay(100); //温度取得に失敗したら再起動 if (isnan(temperature)) { ESP.restart(); } //LED制御 if (temperature < -65.0) { neopixelWrite(GPIO_NUM_20, 0, RGB_BRIGHTNESS, 0); // green delay(100); } else { neopixelWrite(GPIO_NUM_20, RGB_BRIGHTNESS, 0, 0); // red delay(100); } // ステーションモードに設定 WiFi.mode(WIFI_STA); // IPアドレス設定 // WiFi.config(ip, gateway, subnet); // 接続開始 int lpcnt = 0; WiFi.disconnect(true, true); delay(500); WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { neopixelWrite(GPIO_NUM_20, RGB_BRIGHTNESS, 0, RGB_BRIGHTNESS); // purple delay(1 * 1000); neopixelWrite(GPIO_NUM_20, 0, 0, 0); //LED消灯 delay(1 * 1000); lpcnt += 1; if (lpcnt > 40) { ESP.restart(); } } neopixelWrite(GPIO_NUM_20, 0, 0, 0); //LED消灯 delay(100); // センサーデータアップロード開始 ThingSpeak.begin(client); neopixelWrite(GPIO_NUM_20, 0, 0, RGB_BRIGHTNESS); // blue delay(100); // フィードに値を設定 //ThingSpeak.setField(1, String(temperature, 2)); // 小数点以下2桁まで //ThingSpeak.setField(2, String(int_temperature, 2)); // 小数点以下2桁まで ThingSpeak.setField(1, temperature); ThingSpeak.setField(2, int_temperature); // ThingSpeakに送信 //Serial.println(); //Serial.print("Write fields..."); int x = ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey); // Check the return code if (x == 200) { //Serial.println("done"); neopixelWrite(GPIO_NUM_20, 0, 0, 0); //LED消灯 digitalWrite(GPIO_NUM_19, LOW); delay(100); // 2m50s のディープスリープタイマー設定(usec) esp_sleep_enable_timer_wakeup(170 * 1000 * 1000); // ディープスリープ esp_deep_sleep_start(); } else { ESP.restart(); } } void loop() { }
  • 冷凍庫の表示温度と値がずれている場合には
    float temperature = (kmeter.getCelsiusTempValue() / 100.0) - 12.0
    の補正値の値を変更してプログラムを再度書き込みます。
  • SJK さんが 2024/05/22 に 編集 をしました。 (メッセージ: 初版)
  • SJK さんが 2024/05/22 に 編集 をしました。
  • SJK さんが 2024/05/24 に 編集 をしました。
  • SJK さんが 2024/05/24 に 編集 をしました。
  • SJK さんが 2024/05/24 に 編集 をしました。
  • SJK さんが 2024/06/05 に 編集 をしました。 (メッセージ: M5 NanoC6版スケッチ追加)
ログインしてコメントを投稿する