【ESP32】OTA update:MQTTとAWS IoTデバイスシャドウを利用したファームウェアアップデート
はじめに
製品をユーザーに提供後に新機能をリリースした際に、開発者がリモートでファームウェアの更新を行えるようにするために、OTAアップデート機能を実装中。
こちらの記事で「デバイス単体でファームウェア更新機能」を実装。
また、こちらの記事で、「新しいFirmwareを開発者がアップロードした時に、ユーザーに対してアプリ通知行う機能」を記載した。
今回は、OTAアップデート機能の最後の実装として、「新しいFirmwareをユーザーがインストールリクエストしたら、新しいFirmwareをダウンロードする」部分を記載する。
これにて、OTAアップデート機能の実装が完了する予定。
実装手順
-
サーバー側(Go)の手順:
- データベースのユーザーテーブルを更新して、新しいFirmwareバージョンを反映させる。
- AWS IoTのDevice Shadowのdesiredを、新しいバージョンに更新する。AWS IoTのDevice Shadowで使用される通信形式は、JSON。
-
デバイス側(ESP32)の操作:
- AWS IoTからdesired状態の更新をMQTT経由で受信する。
- 受信データ(JSON形式)をデシリアライズし、現在の状態(repoted)との差分を検出する。
- 差分がある場合(=Firmwareの更新が必要な場合)、OTAで更新を実行する。
- クラウドに成功した更新を報告するために、Device Shadowのreported状態を更新する。
サーバー側(Go)
DBとDevice Shadow Desiredを更新
データベースの更新
- **
UpdateUser(h.db, user)
**関数を呼び出して、データベースのユーザー情報を更新する。具体的には、DatabaseにアクセスしてUPDATE文でUserテーブルを更新。
Go構造体 → Protocol Buffers
-
ToDeviceConfig
関数は、Go言語で定義されたUser
構造体のインスタンスからpb.DeviceConfig
構造体へとデータを変換するためのメソッド。pb.DeviceConfig
構造体はプロトコルバッファ(Protocol Buffers)定義に基づいたデータ構造で、AWS IoTのデバイスシャドウのdesired設定のために利用される。 -
User
構造体のFirmwareVersion
フィールドは、データベースのuser
テーブルにあるfirmware_version
カラムの値をマッピングして読み込んでいる。
// user.go
type User struct {
// 他のフィールド...
FirmwareVersion string `db:"firmware_version" json:"firmware_version"`
}
func (u *User) ToDeviceConfig() pb.DeviceConfig {
return pb.DeviceConfig{
FirmwareVersion: &u.FirmwareVersion,
}
}
デバイスシャドウの更新:
- AWS IoTのDevice Shadowで使用される通信形式は、JSONなので**
h.sm.UpdateShadow()
**関数で、Protocol Buffers形式のデータをJSONに変換し、AWS IoTのデバイスシャドウのdesiredステートを更新する。
// user_handler.go
type UserHandler struct {
db *sqlx.DB
sm ShadowManager
}
func (h *UserHandler) HandleUpdateUser(c echo.Context) error {
uid := c.Get("uid").(string)
user := &User{}
if err := c.Bind(user); err != nil {
return err
}
user.UID = uid
// DB更新
updatedUser, err := UpdateUser(h.db, user)
if err != nil {
c.Logger().Errorf("failed to update user: %v", err)
return echo.NewHTTPError(http.StatusInternalServerError, "failed to update user")
}
// デバイスIDが存在するとき
if updatedUser.DeviceID.Valid {
dc := updatedUser.ToDeviceConfig()
desired := &pb.PublishShadowDesired{
State: &pb.ShadowDesiredState{
Desired: &pb.ShadowDesired{
Config: &dc,
},
},
}
// DeviceShadowを反映
if err = h.sm.UpdateShadow(updatedUser.DeviceID.String, desired); err != nil {
c.Logger().Errorf("failed to update device shadow: %v", err)
return echo.NewHTTPError(http.StatusInternalServerError, "failed to update device shadow")
}
}
return c.JSON(http.StatusOK, updatedUser)
}
デバイス側(ESP32)
全体の流れ
まず、全体の流れから。個人的に、Device Shadowのdesiredステートと、デバイス内部のdesired設定が混乱してたので整理含めて記載。
AWS IoT Device Shadow の desired
ステート
AWS IoT Device Shadow の desired
ステートは、デバイスが達成すべき目標状態を示す。これはサーバー側(クラウド)で設定され、デバイスへと送信される。このステートは、デバイスがオフラインのときでもクラウド側で管理され、デバイスがオンラインに戻った時に同期される。
デバイス内部での desired
設定
デバイス内部での desired
設定は、デバイスが実際に操作を行うための内部パラメータや設定。これは、受け取った Device Shadow の desired
ステートに基づいて更新されるが、直接的にはデバイスの動作に影響を与える設定値。デバイスはこの内部 desired
設定に基づいて実際に操作を行い、その結果を reported
ステートに反映させる。
同期と更新のプロセス
同期:
- デバイスはクラウドから、Device Shadowの
desired
ステートを受信する。 - 受信した
desired
ステートをもとに、デバイス内部のdesired
設定を更新する。
実行:
- デバイスは更新された内部
desired
設定に基づいて動作を調整する。
報告:
- 調整後の実際の状態をデバイスの
reported
ステートとしてクラウドに報告する。
desired
ステート更新
MQTT通信経由でのデバイスシャドウの - MQTTクライアントが新しいメッセージを受信し、設定された
onMessage
コールバック関数が呼び出される。 - デバイスは、MQTTプロトコルを使用してAWS IoTから
desired
ステートの更新を購読する。新しいdesired
ステートのデータがトピック(例**$aws/things/THING_NAME/shadow/update/delta
**)を通じて送信される
// src/mqtt_client.cpp
void MqttClient::onMessage(const std::function<void(const String &topic, const String &payload)> &callback) {
client->onMessage([callback](const String &topic, const String &payload) {
Serial.println("MQTTPubSubClient::onMessage: " + topic + " " + payload);
callback(topic, payload);
});
}
MQTTメッセージの解析(トピックとPayload)
- MQTTメッセージはデバイスシャドウの
delta
トピックから送信され、デバイスの現在のreported
ステートと異なる**desired
**ステートが含まれている。 - Payloadは通常、JSON 形式のデータで、デバイスの状態に関する情報を含み、特定のデバイスシャドウの
desired
ステートやreported
ステートを表す。
トピック: $aws/things/XXXXXXXXXXXXXXXXXXXXXXXX/shadow/update/delta
- これは、特定のデバイス(Thing)のDevice Shadowのdeltaトピック。ここで、**
XXXXXXXXXXXXXXXXXXXXXXXX
**はデバイスのThing名(またはThing ID)
Payload
-
version
: デバイスシャドウのバージョン番号。シャドウの、、、
続きは、こちらで記載しています。
Discussion