ESP32のMatterプログラムをOTA対応にする

DIYする

arduino-esp32を使って作ったMatter対応ESP32プログラムを、Arduino OTAに対応させました。Arduino IDEからWiFi経由 (Over The Air) でプログラムをアップデートできます。プログラムに5行足すだけで、簡単です。

ESP32で作ったMatterデバイス

前回の記事では、Arduino IDEとarduino-esp32ライブラリを使ったMatterデバイスを作りました。

Arduino IDEとESP32でMatterデバイスを作る
以前、有志の方が作った、Arduino IDEで使えるMatterライブラリesp32-arduino-matterを使用して、ESP32でMatter対応プログラムを作りました。今は、本家のespressif社が提供しているarduino...

iPhone, Macのホームに、Matterデバイスとして登録し、ホーム画面の操作や、

ESP32の物理ボタン操作でオンボードLEDがOn/Offします。

Matterプログラムは、サイズが大きく、フラッシュメモリが4MBのESP32の場合、デフォルトのパーティショニングでは、メモリ超過します。不揮発メモリ用の領域を削減することで、パーティションに収まりました。それでもフラッシュメモリの割り当てパーティションの87%を占めています。この残り容量で、OTA対応できるのかどうか、確認したいと思いました。

MatterデバイスのOTA(使いません)

MatterにはOTAの機能が備わっているようです。仕組みを詳しく調べることはしてませんが、特定のサーバでファームウェアアップデートが準備された場合に、自動的にアップデートされるようです。ただ、正式に認証されているMatterデバイスじゃないと対応しないような説明もありました。さらに、実際にはMatterのOTAを使わずに、従来通りメーカー提供のアプリの中でファームウェアアップデートする方式のデバイスも多いようです。ということで、難しそうですし、可能かどうかも不明なので、MatterのOTAは諦めます。

Arduino IDEのOTAを使う

Arduino IDEが提供しているOTAの機能です。下の記事で説明しました。詳しくはこちらをご覧ください。

Arduino IDEからESP32をOTAアップデート(改訂版)
OTA (Over The Air)を使ってESP32のスケッチをアップデートする方法の改訂版です。以前、試したESP32のスケッチ例は、もっと簡単にしても良いことを他の方のブログで知りました。おかげさまで活用できるようになりました。という...

シリアルポートのメニューに、WiFiの選択肢が現れるので、それを選択して、プログラムをESPにアップロードします。Arduino IDEでDIYしたデバイスをOTAする時は、Arduino IDEを使っている状態なので、これを使うのが理にかなってます。

プログラムをOTA対応

前回のプログラムをOTA対応させました。追加したプログラムは、//———-OTA————-でコメントした5行です。手動でWiFi設定する場合と、BLEでコミッションする場合で、WiFi確立のタイミングが異なるので、手動WiFi設定の場合のみOTA対応させました。BLEコミッションの場合もOTA可能だと思いますので、後日挑戦したいと思います。

#include <Matter.h>
#if !CONFIG_ENABLE_CHIPOBLE
//もしBLEでコミッションするならフラッシュメモリの無駄を防ぐためWiFiはインクルードしない
#include <WiFi.h>
#include <ArduinoOTA.h> //-----------OTA-----------
#endif
#include <Preferences.h>

// BLEでコミッションするならCONFIG_ENABLE_CHIPOBLEを定義しておく
#if !CONFIG_ENABLE_CHIPOBLE
// BLEでコミッションしない場合は、手動でWiFi指定してWiFiでコミッションする
const char *ssid = "your-ssid"; // Change this to your WiFi SSID
const char *password = "your-password"; // Change this to your WiFi password
#endif

// このNodeのEndpointsのリスト ここではOn/Off Light Endpoint
MatterOnOffLight OnOffLight;

// 最後のOnOff状態をPreferencesを使って記憶
Preferences matterPref;
const char *onOffPrefKey = "OnOff";

// オンボードのLEDのピン番号。
const uint8_t ledPin = 2; // ESP32では2番が青色LEDなのでそれを書いておく

// bootボタンを入力に使う場合0
const uint8_t buttonPin = 0;

// Button control
uint32_t button_time_stamp = 0; // チャッタリング制御用
bool button_state = false; // ボタン状態 false = released | true = pressed
const uint32_t debouceTime = 250; // チャッタリング防止時間 (ms)
const uint32_t decommissioningTimeout = 5000; // 5秒長押しで設定モードへ

// ライトをOnOffする命令が来ると、このcallback関数が呼ばれる
bool setLightOnOff(bool state) {
  Serial.printf("User Callback :: New Light State = %s\r\n", state ? "ON" : "OFF");
  if (state) {
    digitalWrite(ledPin, HIGH); //OnならLEDを点灯して
  } else {
  digitalWrite(ledPin, LOW); //OffならLEDを消灯
  }
  // store last OnOff state for when the Light is restarted / power goes off
  matterPref.putBool(onOffPrefKey, state); //電源落とされても復帰するようバックアップ
  // This callback must return the success state to Matter core
  return true; //必ずtrueを返す
}

void setup() {
  // ボタンとLEDを初期化
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  
  Serial.begin(115200);
  
// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network
// BLEでコミッションしないなら、手動でWiFi起動
#if !CONFIG_ENABLE_CHIPOBLE
  // We start by connecting to a WiFi network
  Serial.print("Connecting to ");
  Serial.println(ssid);
  // Manually connect to WiFi
  WiFi.begin(ssid, password);
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\r\nWiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  delay(500);

  //OTA setup
  ArduinoOTA.setHostname("matterLight"); //-----------OTA-----------
  ArduinoOTA.setPasswordHash("12345678901234567890123456789012"); //-----------OTA-----
  ArduinoOTA.begin(); //-----------OTA-----------
#endif

  // Matter EndPointを初期化。不揮発メモリ(?)から過去の状態を読んで設定
  matterPref.begin("MatterPrefs", false);
  bool lastOnOffState = matterPref.getBool(onOffPrefKey, true);
  OnOffLight.begin(lastOnOffState);
  OnOffLight.onChange(setLightOnOff);//変化した場合のcallback関数を設定

  // Matter beginning - 全てのEndPointsが初期化できたら開始
  Matter.begin();
  // すでにコミッション済みのアクセサリの再起動かもしれない、その場合
  if (Matter.isDeviceCommissioned()) {
    Serial.println("Matter Node is commissioned and connected to the network. Ready for use.");
    Serial.printf("Initial state: %s\r\n", OnOffLight.getOnOff() ? "ON" : "OFF");
    OnOffLight.updateAccessory(); // configure the Light based on initial state
  }
}

void loop() {
  // コミッション済みかどうかをチェック(loop中にコミッション状態変化することもある)
  if (!Matter.isDeviceCommissioned()) { // コミッション終わってなかったら
    Serial.println(""); // ペアリング情報を流す
    Serial.println("Matter Node is not commissioned yet.");
    Serial.println("Initiate the device discovery in your Matter environment.");
    Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
    Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str());
    Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str());
    // waits for Matter Light Commissioning.
    uint32_t timeCount = 0;
    while (!Matter.isDeviceCommissioned()) { //コミッション終わるまで待ち、5秒ごとにメッセージ出す
      delay(100);
      if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec
        Serial.println("Matter Node not commissioned yet. Waiting for commissioning.");
      }
    }
    Serial.printf("Initial state: %s\r\n", OnOffLight.getOnOff() ? "ON" : "OFF");
    OnOffLight.updateAccessory(); // configure the Light based on initial state
    Serial.println("Matter Node is commissioned and connected to the network. Ready for use.");
  }

  // ボタンはライトの制御にも使う。ボタンが押されたかどうかチェック
  if (digitalRead(buttonPin) == LOW && !button_state) { //前回HIGH今回LOWなら
    // チャッタリング防止
    button_time_stamp = millis(); // ボタン押下時刻を記録
    button_state = true; // pressed.
  }

  // ボタン短押しでライトをOnOff(チャッタリング期間を過ぎて、ボタンHIGHなら、短押し)
  uint32_t time_diff = millis() - button_time_stamp;
  if (button_state && time_diff > debouceTime && digitalRead(buttonPin) == HIGH) {
    button_state = false; // released
    // Toggle button is released - toggle the light
    Serial.println("User button released. Toggling Light!");
    OnOffLight.toggle(); // Matter Controller also can see the change
  }

  // ボタンを5秒長押しなら、ライトを消して、つぎのloopでコミッションやり直しへ
  if (button_state && time_diff > decommissioningTimeout) {
    Serial.println("Decommissioning the Light Matter Accessory. It shall be commissioned again.");
    OnOffLight.setOnOff(false); // turn the light off
    Matter.decommission();
    button_time_stamp = millis(); // avoid running decommissining again, reboot takes a second or so
  }
#if !CONFIG_ENABLE_CHIPOBLE
  ArduinoOTA.handle(); //-----------OTA-----------
#endif
}

動作確認

OTAの動作確認をしました。このプログラムを、まずは有線でアップロードして動作させます。するとArduino IDEのポートのメニューに、WiFi接続先が現れます。下図では、プログラム内で指定したmatterLightという名前で表れています。

これを選択して、プログラムをアップロードすると、パスワード入力ダイアログが現れます。

プログラムの中で指定したハッシュに対応するパスワードを入力すると、無事にアップロードされました。

OTA化によるサイズ変化

OTA改造前後のプログラムサイズを比較します。改造前後のIDEウィンドウのスクリーンショットを、アニメーションGIFにしたのが、下図です。Outputの部分を見ると、増加は35kB程度でした。保存領域の87%を占めていたプログラムが、89%に増加する程度です。

空き容量が少ないことには変わりありませんが、OTAに改造しても大差ないようです。

まとめ

Arduino IDEで作ったESP32用のMatterプログラムを、OTA対応に改造しました。プログラムサイズの増加は2%程度でした。改造は5行追加するだけですし、Arduino IDEの機能で簡単にOTAアップデートできますので、DIYプログラマーに最適ではないかと思います。

コメント

  1. 大根足 より:

    なんと、IKEAの新製品の発売が始まっています、早いですねー。価格も安いです。さっそく注文しなくちゃ。あとは、アップルのスマートディスプレイ待ちです。

  2. diysmartmatter より:

    センサ類が発売されているようですね。Matter over Threadでしょうか

タイトルとURLをコピーしました