M5StackでAmazon FreeRTOSを使用する６（OTA編）

# はじめに

Amazon FreeRTOSのデバイス管理、次はOTAを試してみましょう。AWS S3に新しいファームウェアを置いてデバイスに配布することができます。

AWSのドキュメント [Amazon FreeRTOS 無線による更新](https://docs.aws.amazon.com/ja_jp/freertos/latest/userguide/freertos-ota-dev.html) の流れに沿って作業を行います。

# 前提条件

無線による更新（OTA）の前提条件として次のものがあります

１．更新を保存する Amazon S3 バケットを作成する.

　　新しいファームウェアを格納する場所を作成します。バージョニングを有効にする必要があります。

２．OTA 更新サービスロールの作成.

　　OTA 更新ジョブを実行する権限です。OTA 用のロールを作成して「**AmazonFreeRTOSOTAUpdate**」ポリシーをアタッチします。
　　「AWSIoTThingsRegistration 」のほうは一緒に割り当てられました。

３．OTA ユーザーポリシーの作成.

　　AWS IoTコンソールから配布する人の権限。（おすすめできませんが、ルートアカウントでテストする場合はスキップ可能）

４．コード署名証明書の作成.

　　ファームウェアへの署名用。
　　OpenSSL、AWS CLI がインストールされた環境で、[Espressif ESP32 のコード署名証明書の作成](https://docs.aws.amazon.com/ja_jp/freertos/latest/userguide/ota-code-sign-cert-esp.html) を実行します。

５．Code Signing for AWS IoT を使用している場合は、Code Signing for AWS IoT へのアクセスの許可.

　　AWS IoTコンソールから配布するファームに署名する人の権限（おすすめできませんが、ルートアカウントでテストする場合はスキップ可能）

６．OTA ライブラリで Amazon FreeRTOS をダウンロードする.

　　アプリケーション作成用。すでにサンプルアプリケーションを作成している場合は不要。

# アプリケーション

[前回](https://qiita.com/fukuebiz/items/7b3d9d8185d183f2bd5b) 作成したファームウェアのバージョン、接続時刻をシャドウに送信するアプリケーションにOTAを追加します。
ネットワーク、MQTTに接続してOTA_AgentInitを呼び出します。

```cpp:
void vRunOTAUpdateDemo( IotMqttConnection_t mqttConnection,
                        const IotNetworkInterface_t * pNetworkInterface,
                        void * pNetworkCredentialInfo )
{
//    IotMqttConnectInfo_t xConnectInfo = IOT_MQTT_CONNECT_INFO_INITIALIZER;
    OTA_State_t eState;
    OTA_ConnectionContext_t xOTAConnectionCtx = { 0 };

    configPRINTF( ( "OTA demo version %u.%u.%u\r\n",
                    xAppFirmwareVersion.u.x.ucMajor,
                    xAppFirmwareVersion.u.x.ucMinor,
                    xAppFirmwareVersion.u.x.usBuild ) );
    configPRINTF( ( "Creating MQTT Client...\r\n" ) );

    xOTAConnectionCtx.pvControlClient = mqttConnection;
    xOTAConnectionCtx.pxNetworkInterface = ( void * ) pNetworkInterface;
    xOTAConnectionCtx.pvNetworkCredentials = pNetworkCredentialInfo;

    OTA_AgentInit( ( void * ) ( &xOTAConnectionCtx ), ( const uint8_t * ) ( clientcredentialIOT_THING_NAME ), App_OTACompleteCallback, ( TickType_t ) ~1 );

    while( ( eState = OTA_GetAgentState() ) != eOTA_AgentState_Stopped )
    {
        /* Wait forever for OTA traffic but allow other tasks to run and output statistics only once per second. */
        vTaskDelay( myappONE_SECOND_DELAY_IN_TICKS );
        configPRINTF( ( "State: %s  Received: %u   Queued: %u   Processed: %u   Dropped: %u\r\n", pcStateStr[ eState ],
                        OTA_GetPacketsReceived(), OTA_GetPacketsQueued(), OTA_GetPacketsProcessed(), OTA_GetPacketsDropped() ) );
    }

}
```

[Espressif ESP32 にファームウェアの初期バージョンをインストールする](https://docs.aws.amazon.com/ja_jp/freertos/latest/userguide/burn-initial-firmware-esp.html) の通り、無線による更新の前提条件 で生成した SHA-256/ECDSA PEM 形式のコード署名証明書（サンプル通りであれば、**ecdsasigner.crt** の内容）を demos/include/aws_ota_codesigner_certificate.h にコピーします。これは、次の方法でフォーマットする必要があります。 

```:
static const char signingcredentialSIGNING_CERTIFICATE_PEM[] = "-----BEGIN CERTIFICATE-----\n"
   ...base64 data...\n"
   -----END CERTIFICATE-----\n"; 
```

ビルドできたら、フラッシュします。

[Espressif ESP32 にファームウェアの初期バージョンをインストールする](https://docs.aws.amazon.com/ja_jp/freertos/latest/userguide/burn-initial-firmware-esp.html) のように、ネットワークに接続し、shadowを更新した後に、OTA待ちになります。

# アプリケーションのバージョンアップ

１．demos/include/aws_application_version.h を開き、APP_VERSION_BUILD トークン値を増やします。今回の例では0.9.2 → 0.9.3になります。

２．アプリケーションをビルドします。

３．ファームウェアをS3にアップロードします。サンプルであれば、build/freertos-ex.bin ファイルをアップロードします。


# OTA 更新の作成 (AWS IoT コンソール)

AWS IoT コンソールから、対象デバイスの指定、更新するファイル（ファームウェア）の選択、署名を指定して、更新ジョブを作成します。

まだジョブがない場合はこんな画面。すでにジョブがある場合は右上にボタンが表示されます。

![awsota1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555397/6a3761d3-a751-c72a-695b-e29598f98c80.png)

Amazon FreeRTOS 無線通信経由 (OTA) の更新ジョブの作成

![awsota2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555397/74b6ac84-e8e4-c514-e986-5fd5b709b765.png)

更新するデバイスを選択

![awsota3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555397/1c055806-d5e2-917a-03ed-bff9458f41de.png)

今回はプロトコルにMQTTを選択、新しいファームウェアイメージに署名します、を選択


![awsota4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555397/a982ae1d-4098-94d6-6c12-c0cc47e105b0.png)

コード署名プロファイルを作成します。作成済の証明書、鍵を指定します。テストでは、aws_ota_codesigner_certificate.h が使用されるため、デバイスのコード署名証明書のパス名は、指定した値は実行時に無視されます。

![awsota5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555397/5d776159-ab46-8a2e-c8d2-b97f2ef7785b.png)

S3 のファームウェアイメージを選択します。デバイスのファームウェアイメージのパス名はupdatesとしました。（このパス名でダウンロードされる？？？）

![awsota6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555397/f7fe2426-a94f-6acc-e1f2-df9efafead25.png)

ジョブを実行すると、次のようなログが表示されて更新が始まり、終了するとデバイスが再起動されます。

```:
109 7334 [iot_thread] OTA demo version 0.9.2
110 7334 [iot_thread] Creating MQTT Client...
111 7335 [iot_thread] [OTA_AgentInit_internal] OTA Task is Ready.
112 7335 [OTA Agent Task] [prvOTAAgentTask] Called handler. Current State [Ready] Event [Start] New state [RequestingJob]
113 7335 [OTA Agent Task] [INFO ][MQTT][73350] (MQTT connection 0x3ffe1edc) SUBSCRIBE operation scheduled.
114 7335 [OTA Agent Task] [INFO ][MQTT][73350] (MQTT connection 0x3ffe1edc, SUBSCRIBE operation 0x3ffe895c) Waiting for operation completion.
115 7341 [OTA Agent Task] [INFO ][MQTT][73410] (MQTT connection 0x3ffe1edc, SUBSCRIBE operation 0x3ffe895c) Wait complete with result SUCCESS.
116 7341 [OTA Agent Task] [prvSubscribeToJobNotificationTopics] OK: $aws/things/myfreertosthing2/jobs/$next/get/accepted
117 7341 [OTA Agent Task] [INFO ][MQTT][73410] (MQTT connection 0x3ffe1edc) SUBSCRIBE operation scheduled.
118 7341 [OTA Agent Task] [INFO ][MQTT][73410] (MQTT connection 0x3ffe1edc, SUBSCRIBE operation 0x3ffe8c18) Waiting for operation completion.
119 7346 [OTA Agent Task] [INFO ][MQTT][73460] (MQTT connection 0x3ffe1edc, SUBSCRIBE operation 0x3ffe8c18) Wait complete with result SUCCESS.
120 7346 [OTA Agent Task] [prvSubscribeToJobNotificationTopics] OK: $aws/things/myfreertosthing2/jobs/notify-next
121 7346 [OTA Agent Task] [prvRequestJob_Mqtt] Request #0
122 7348 [OTA Agent Task] [INFO ][MQTT][73460] (MQTT connection 0x3ffe1edc) MQTT PUBLISH operation queued.
123 7348 [OTA Agent Task] [INFO ][MQTT][73480] (MQTT connection 0x3ffe1edc, PUBLISH operation 0x3ffe895c) Waiting for operation completion.
124 7351 [OTA Agent Task] [INFO ][MQTT][73510] (MQTT connection 0x3ffe1edc, PUBLISH operation 0x3ffe895c) Wait complete with result SUCCESS.
125 7351 [OTA Agent Task] [prvOTAAgentTask] Called handler. Current State [RequestingJob] Event [RequestJobDocument] New state [WaitingForJob]
126 7352 [OTA Agent Task] [prvParseJobDoc] Size of OTA_FileContext_t [64]
127 7353 [OTA Agent Task] [prvParseJSONbyModel] Extracted parameter [ clientToken: 0:myfreertosthing2 ]
128 7353 [OTA Agent Task] [prvParseJSONbyModel] parameter not present: execution
129 7353 [OTA Agent Task] [prvParseJSONbyModel] parameter not present: jobId
130 7353 [OTA Agent Task] [prvParseJSONbyModel] parameter not present: jobDocument
131 7353 [OTA Agent Task] [prvParseJSONbyModel] parameter not present: afr_ota
132 7353 [OTA Agent Task] [prvParseJSONbyModel] parameter not present: protocols
133 7353 [OTA Agent Task] [prvParseJSONbyModel] parameter not present: files
134 7353 [OTA Agent Task] [prvParseJSONbyModel] parameter not present: filepath
135 7353 [OTA Agent Task] [prvParseJSONbyModel] parameter not present: filesize
136 7353 [OTA Agent Task] [prvParseJSONbyModel] parameter not present: fileid
137 7353 [OTA Agent Task] [prvParseJSONbyModel] parameter not present: certfile
138 7353 [OTA Agent Task] [prvParseJSONbyModel] parameter not present: sig-sha256-ecdsa
139 7353 [OTA Agent Task] [prvDefaultCustomJobCallback] Received Custom Job inside OTA Agent which is not supported.
140 7353 [OTA Agent Task] [prvParseJobDoc] Ignoring job without ID.
141 7354 [OTA Agent Task] [prvOTA_Close] Context->0x0x3fff8c40
I (73859) ota_pal: prvPAL_SetPlatformImageState, 4
W (73959) ota_pal: Set image as aborted!
I (73969) esp_ota_ops: aws_esp_ota_get_boot_flags: 1
I (73979) esp_ota_ops: [0] aflags/seq:0x2/0x1, pflags/seq:0xffffffff/0x0
W (73979) ota_pal: Image not in self test mode 2
I (73989) esp_ota_ops: aws_esp_ota_get_boot_flags: 1
I (73989) esp_ota_ops: [0] aflags/seq:0x2/0x1, pflags/seq:0xffffffff/0x0
142 7368 [OTA Agent Task] [prvOTAAgentTask] Handler failed. Current State [WaitingForJob] Event  [ReceivedJobDocument] Error Code [603979776]
143 7435 [iot_thread] State: Ready  Received: 1   Queued: 0   Processed: 0   Dropped: 0
144 7535 [iot_thread] State: WaitingForJob  Received: 1   Queued: 0   Processed: 0   Dropped: 0
145 7635 [iot_thread] State: WaitingForJob  Received: 1   Queued: 0   Processed: 0   Dropped: 0
146 7735 [iot_thread] State: WaitingForJob  Received: 1   Queued: 0   Processed: 0   Dropped: 0
147 7835 [iot_thread] State: WaitingForJob  Received: 1   Queued: 0   Processed: 0   Dropped: 0
148 7935 [iot_thread] State: WaitingForJob  Received: 1   Queued: 0   Processed: 0   Dropped: 0
149 8035 [iot_thread] State: WaitingForJob  Received: 1   Queued: 0   Processed: 0   Dropped: 0
150 8135 [iot_thread] State: WaitingForJob  Received: 1   Queued: 0   Processed: 0   Dropped: 0
151 8235 [iot_thread] State: WaitingForJob  Received: 1   Queued: 0   Processed: 0   Dropped: 0
152 8335 [iot_thread] State: WaitingForJob  Received: 1   Queued: 0   Processed: 0   Dropped: 0
153 8435 [iot_thread] State: WaitingForJob  Received: 1   Queued: 0   Processed: 0   Dropped: 0
154 8535 [iot_thread] State: WaitingForJob  Received: 1   Queued: 0   Processed: 0   Dropped: 0
155 8635 [iot_thread] State: WaitingForJob  Received: 1   Queued: 0   Processed: 0   Dropped: 0
156 8735 [iot_thread] State: WaitingForJob  Received: 1   Queued: 0   Processed: 0   Dropped: 0
157 8835 [iot_thread] State: WaitingForJob  Received: 1   Queued: 0   Processed: 0   Dropped: 0
158 8935 [iot_thread] State: WaitingForJob  Received: 1   Queued: 0   Processed: 0   Dropped: 0
159 8941 [OTA Agent Task] [prvParseJobDoc] Size of OTA_FileContext_t [64]
160 8941 [OTA Agent Task] [prvParseJSONbyModel] Extracted parameter [ jobId: AFR_OTA-20200106_4 ]
161 8941 [OTA Agent Task] [prvParseJSONbyModel] Extracted parameter [ protocols: ["MQTT"] ]
162 8941 [OTA Agent Task] [prvParseJSONbyModel] Extracted parameter [ streamname: AFR_OTA-c30cb685-9965-47e0-9da2-36ec32d4a205 ]
163 8941 [OTA Agent Task] [prvParseJSONbyModel] Extracted parameter [ filepath: updates ]
164 8941 [OTA Agent Task] [prvParseJSONbyModel] Extracted parameter [ filesize: 1011632 ]
165 8941 [OTA Agent Task] [prvParseJSONbyModel] Extracted parameter [ fileid: 0 ]
166 8942 [OTA Agent Task] [prvParseJSONbyModel] Extracted parameter [ certfile: aws_ota_codesigner_certificate.h ]
167 8942 [OTA Agent Task] [prvParseJSONbyModel] Extracted parameter [ sig-sha256-ecdsa: MEQCIHCs4rIMpRX9/y+AWApczDaCvd5w... ]
168 8942 [OTA Agent Task] [prvParseJobDoc] Job was accepted. Attempting to start transfer.
I (89739) ota_pal: prvPAL_GetPlatformImageState
I (89819) esp_ota_ops: aws_esp_ota_get_boot_flags: 1
I (89819) esp_ota_ops: [0] aflags/seq:0x2/0x1, pflags/seq:0xffffffff/0x0
I (89829) ota_pal: Writing to partition subtype 17 at offset 0x1a0000
169 9049 [iot_thread] State: WaitingForJob  Received: 2   Queued: 0   Processed: 0   Dropped: 0
170 9164 [iot_thread] State: WaitingForJob  Received: 2   Queued: 0   Processed: 0   Dropped: 0
171 9278 [iot_thread] State: WaitingForJob  Received: 2   Queued: 0   Processed: 0   Dropped: 0
I (93829) ota_pal: aws_esp_ota_begin succeeded
172 9351 [OTA Agent Task] [prvSetDataInterface] Data interface is set to MQTT.
173 9351 [OTA Agent Task] [prvProcessJobHandler] Setting OTA data inerface.
174 9351 [OTA Agent Task] [prvOTAAgentTask] Called handler. Current State [WaitingForJob] Event [ReceivedJobDocument] New state [CreatingFile]
175 9351 [OTA Agent Task] [INFO ][MQTT][93510] (MQTT connection 0x3ffe1edc) SUBSCRIBE operation scheduled.
176 9351 [OTA Agent Task] [INFO ][MQTT][93510] (MQTT connection 0x3ffe1edc, SUBSCRIBE operation 0x3fff3dc4) Waiting for operation completion.
177 9355 [OTA Agent Task] [INFO ][MQTT][93550] (MQTT connection 0x3ffe1edc, SUBSCRIBE operation 0x3fff3dc4) Wait complete with result SUCCESS.
178 9355 [OTA Agent Task] [prvInitFileTransfer_Mqtt] OK: $aws/things/myfreertosthing2/streams/AFR_OTA-c30cb685-9965-47e0-9da2-36ec32d4a205/data/cbor
179 9355 [OTA Agent Task] [prvOTAAgentTask] Called handler. Current State [CreatingFile] Event [CreateFile] New state [RequestingFileBlock]
180 9357 [OTA Agent Task] [INFO ][MQTT][93570] (MQTT connection 0x3ffe1edc) MQTT PUBLISH operation queued.
181 9357 [OTA Agent Task] [prvRequestFileBlock_Mqtt] OK: $aws/things/myfreertosthing2/streams/AFR_OTA-c30cb685-9965-47e0-9da2-36ec32d4a205/get/cbor
182 9357 [OTA Agent Task] [prvOTAAgentTask] Called handler. Current State [RequestingFileBlock] Event [RequestFileBlock] New state [WaitingForFileBlock]
183 9378 [iot_thread] State: WaitingForJob  Received: 2   Queued: 0   Processed: 0   Dropped: 0
184 9396 [OTA Agent Task] [prvIngestDataBlock] Received file block 3, size 4096
185 9397 [OTA Agent Task] [prvIngestDataBlock] Remaining: 246
186 9397 [OTA Agent Task] [prvOTAAgentTask] Called handler. Current State [WaitingForFileBlock] Event [ReceivedFileBlock] New state [WaitingForFileBlock]
187 9404 [OTA Agent Task] [prvIngestDataBlock] Received file block 0, size 4096
188 9405 [OTA Agent Task] [prvIngestDataBlock] Remaining: 245
189 9405 [OTA Agent Task] [prvOTAAgentTask] Called handler. Current State [WaitingForFileBlock] Event [ReceivedFileBlock] New state [WaitingForFileBlock]
190 9410 [OTA Agent Task] [prvIngestDataBlock] Received file block 2, size 4096
191 9410 [OTA Agent Task] [prvIngestDataBlock] Remaining: 244
192 9410 [OTA Agent Task] [prvOTAAgentTask] Called handler. Current State [WaitingForFileBlock] Event [ReceivedFileBlock] New state [WaitingForFileBlock]
193 9417 [OTA Agent Task] [prvIngestDataBlock] Received file block 1, size 4096
194 9418 [OTA Agent Task] [prvIngestDataBlock] Remaining: 243
195 9418 [OTA Agent Task] [prvOTAAgentTask] Called handler. Current State [WaitingForFileBlock] Event [ReceivedFileBlock] New state [WaitingForFileBlock]
196 9425 [OTA Agent Task] [prvIngestDataBlock] Received file block 6, size 4096
197 9426 [OTA Agent Task] [prvIngestDataBlock] Remaining: 242
198 9426 [OTA Agent Task] [prvOTAAgentTask] Called handler. Current State [WaitingForFileBlock] Event [ReceivedFileBlock] New state [WaitingForFileBlock]
199 9431 [OTA Agent Task] [prvIngestDataBlock] Received file block 4, size 4096
200 9432 [OTA Agent Task] [prvIngestDataBlock] Remaining: 241
201 9432 [OTA Agent Task] [prvOTAAgentTask] Called handler. Current State [WaitingForFileBlock] Event [ReceivedFileBlock] New state [WaitingForFileBlock]
202 9438 [OTA Agent Task] [prvIngestDataBlock] Received file block 7, size 4096
203 9439 [OTA Agent Task] [prvIngestDataBlock] Remaining: 240
```

シャドウでも、ファームウェアのバージョンが更新されています。

![awsota8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555397/2ce85635-9c50-e6a3-7feb-1f5956aff5c6.png)
取得した時刻や、ファームウェアのバージョンでシャドウを更新します。


# サンプルソースとビルド

サンプルソースは [こちら](https://github.com/fukuen/m5stack-freertos-ota) を参照してください。

ビルドは、

```bash:
git clone https://github.com/fukuen/m5stack-freertos-ota --recursive
```

ダウンロードしたディレクトリーに移動して、アプリケーションをビルドします。

```bash:
cd m5stack-freertos-tft
cmake -DCMAKE_TOOLCHAIN_FILE=amazon-freertos/tools/cmake/toolchains/xtensa-esp32.cmake -GNinja -S . -B build
cd build
ninja
```


# メモ

[第１回 Amazon FreeRTOSインストール](https://qiita.com/fukuebiz/items/54f765d12c084ceea392)
[第２回 Lチカ](https://qiita.com/fukuebiz/items/3b803912bf51b1f46d79)
[第３回 TFT](https://qiita.com/fukuebiz/items/732296b84e68a1a08d41)
[第４回 SDカード](https://qiita.com/fukuebiz/items/e7010b1d84131e93a0a3)
[第５回 AWS Shadows](https://qiita.com/fukuebiz/items/7b3d9d8185d183f2bd5b)