u-blox USB-NORA-W2で始めるAWS IoT ExpressLink

## AWS IoT ExpressLinkとは

簡単にいうと、クラウド接続ができないようなマイコンと繋げることで、簡単にコネクテッドなデバイスが作れる[仕様](https://docs.aws.amazon.com/iot-expresslink/v1.2/programmersguide/elpg.html)と、その仕様に[対応したデバイス](https://devices.amazonaws.com/search?page=1&sv=iotxplnk)を指す。

https://aws.amazon.com/jp/iot-expresslink/

詳しくはJAWSUG IoT専門支部のこの動画が参考になります。

https://www.youtube.com/watch?v=tM9LC5gvuVE

マイコンとの接続はUARTを利用し、[ATコマンド](https://docs.aws.amazon.com/iot-expresslink/v1.2/programmersguide/elpg-commands.html)を使ってExpressLinkモジュールを操作することで、データの送受信などを行ことができるのが特徴となっている。

この記事では[USB-NORA-W2](https://www.u-blox.com/en/product/usb-nora-w2?legacy=Current#Documentation-&-resources)を使って、クラウド接続を試してみます。

## まずは繋げてみる

[User Guide](https://content.u-blox.com/sites/default/files/USB-NORA-W256_UserGuide_UBX-21045749.pdf)に手順が書かれているので、それを参考にセットアップしてみます。
試している環境は、
- MacBook Pro(2023 M2), macOS Sonoma 14.6.1


USB-NORA-W2はUSB端子がついたボードなので、Type-Cの変換アダプタとか使って、そのままMacに繋ぐことができます。ターミナルで確認したら以下のように認識されました。

```bash
ls -l /dev/tty.*
crw-rw-rw-  1 root  wheel  0x9000000  8 16 10:48 /dev/tty.Bluetooth-Incoming-Port
crw-rw-rw-  1 root  wheel  0x9000002  9  1 10:43 /dev/tty.usbserial-DP05GXGX
```

screenコマンドで繋ぎます

```bash
screen /dev/tty.usbserial-DP05GXGX 115200
```

iTermだとlocal echoの設定ができないため、入力したコマンドが確認できませんが、以降の例では入力したコマンドとその結果を示しています。

```bash
AT

OK
```

ExpressLinkを操作する際には基本的に `AT+コマンド` のような方法で指定します。[利用可能なコマンド](https://docs.aws.amazon.com/iot-expresslink/v1.2/programmersguide/elpg-commands.html)はドキュメントで確認することができます。

まず最初にExpressLinkのバージョンを確認したいので、[設定情報などを確認することができるコマンド](https://docs.aws.amazon.com/iot-expresslink/v1.2/programmersguide/elpg-configuration-dictionary.html)を実行します。

データの読み取りでは `AT+CONG? 対象のキー` というフォーマットで入力すると、対象のキーの値を確認することができます。

```bash
AT+CONF? Version

OK 1.1.14 - 001
```

2024/8現在だとExpressLinkの最新バージョンは1.2ですので、少し古いのが入っています。[u-bloxのドキュメント](https://content.u-blox.com/sites/default/files/NORA-W2_DataSheet_UBX-21046925.pdf)では1.2にバージョンアップ可能とあります。

## ExpressLink Evaluation Kits Quick Connect

ExpressLinkは初期状態でモジュールにThing名、証明書などがプロビジョニングされています。証明書などはセキュアエレメントに保存されており、安全に管理されています。

Thing名の確認

```bash
AT+CONF? ThingName

OK 長いユニークな文字列
```

証明書を確認

```bash
AT+CONF? Certificate

OK -----BEGIN CERTIFICATE-----\AMIIDRswfQ\A-----END CERTIFICATE-----\A
```

AWS IoT Coreのエンドポイントを確認

```bash
AT+CONF? Endpoint

OK
```

最初は設定されていないようです。

お試し用に[Quick Connectのアプリ](https://docs.aws.amazon.com/iot-expresslink/v1.2/programmersguide/elpg-provisioning.html#elpg-provisioning-evaluation-kits)が用意されているので、これを使って接続を試してみます。ダウンロードしたファイルを展開すると`config.txt`というファイルがあるので、必要な情報を設定します。

```
Serial_port:/dev/tty.usbserial-DP05GXGX
SSID:
Passphrase:
```

ファイルに実行権限をつけて、実行します。screenコマンドを実行したままだったので、途中で`Board not found`となりました。実行する前に停止しておきましょう。

```bash
chmod +x Start_Quick_Connect
```

Macだとセキュリティーのエラーが出るので、Finderでファイルを選んで右クリックから実行しました。

```bash
11:42:10 - This program will configure your Expresslink board to connect to the
           internet using Wi-Fi, and then to AWS. You will be asked to enter
           your Wi-Fi network credentials so that the device can connect. This
           information will be written to the device to allow it to connect to
           the internet. The information will not be transmitted to the internet
           or AWS.

11:42:10 - ---------------------------------------------------------------------

11:42:10 - Provisioning device to connect. This may take several minutes.

11:42:10 - Board not found. Please connect the board to your computer.

11:42:10 - Try again?

1 - [Y/N]: y

11:43:44 - Provisioning device to connect. This may take several minutes.

11:43:45 - Configuring SSID by sending AT+CONF SSID = hogehoge

11:43:46 - Configuring Passphrase by sending AT+CONF Passphrase = fugafuga
           


11:43:46 - Provisioning complete. Retrieving thing name from the device.

11:43:46 - Fetching ThingName from the device

11:43:46 - Sending serial command: AT+CONF? ThingName to the device

11:43:46 - Device thingName is hogehoge


11:43:46 - ---------------------------------------------------------------------

11:43:46 - Starting AWS Session

11:43:49 - AWS Session Initialized

11:43:56 - Something went wrong while processing the request. Response:
           {"message":"Thing doesn't exist"}, Status code: 400


Saving session...completed.
Deleting expired sessions...       4 completed.
```

このQuick Startはデバイスベンダーが用意したアカウントに接続するのですが、何か問題が起きているみたいです。

接続先の設定を確認すると設定されていたので、問題はなさそうですが、ちょっとわからず。

```bash
AT+CONF? Endpoint

OK u-bloxのAWS IoT Core エンドポイントっぽい値
```

一旦無視して、自分のアカウントに繋いでみます。

## Thing,証明書,IoTPolicyの登録

実際の製品に組み込む場合に取れるプロビジョニング方法は複数あります。
https://docs.aws.amazon.com/iot-expresslink/v1.2/programmersguide/elpg-provisioning.html#elpg-provisioning-onboarding

今回は手動で登録します。

必要になるのは、以下の情報です

- Thing名
- 証明書
- IoT Policy
- AWS IoT Coreのエンドポイント名

順に作業していきます。

AWS CLIを使うには権限を持ったクレデンシャルが事前に必要となります。 [AWS Cloud Shell](https://aws.amazon.com/jp/cloudshell/)を使うとサインインしている人の権限が利用できるので、私はAWS Cloud Shell上で作業しました。

### Thingの登録

`AT+CONF? ThingName` で確認した値をAWS CLIで登録します。

```bash
aws iot create-thing \
--thing-name 確認した値 \
--attribute-payload attributes={ExpressLink="u-blox_USB-NORA-W2"}

{
    "thingName": "確認した値",
    "thingArn": "arn:aws:iot:ap-northeast-1:1234567890:thing/確認した値",
    "thingId": "ユニークな値"
}
```

### 証明書の登録

証明書は `AT+CONF? Certificate` で確認した長い文字列です。証明書は以下のように決められたフォーマットである必要がありますが、取得できた文字列は `\A` で区切られてました。そのまま登録できませんでしたので、以下のコマンドで改行コードに置き換えます。

```bash
CERT=$(echo "-----BEGIN CERTIFICATE-----\AMIIDRzC〜〜〜EQBxwBswfQ\A-----END CERTIFICATE-----" | sed 's/\\A/\n/g')
```

そして、以下のコマンドで登録します。今回はCA証明書が無いので、[CA lessの方法](https://docs.aws.amazon.com/iot/latest/developerguide/manual-cert-registration.html#manual-cert-registration-noca-cli)で登録です。

```bash
aws iot register-certificate-without-ca \
--status ACTIVE \
--certificate-pem "$CERT"

{
    "certificateArn": "arn:aws:iot:ap-northeast-1:1234567890:cert/長いID",
    "certificateId": "長いID"
}
```

### IoT Policyの登録

テスト用ですので、ガバガバなPolicyですが、実際に利用する際には正しく制限しましょう。

```bash
POLICY=$(cat << EOS
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "iot:*",
            "Resource": "*"
        }
    ]
}
EOS
)

aws iot create-policy \
--policy-name expresslink-policy \
--policy-document "$POLICY"

{
    "policyName": "expresslink-policy",
    "policyArn": "arn:aws:iot:ap-northeast-1:1234567890:policy/expresslink-policy",
    "policyDocument": "{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": \"iot:*\",\n            \"Resource\": \"*\"\n        }\n    ]\n}",
    "policyVersionId": "1"
}
```

### Thing,証明書,IoT Policyを紐付け

登録したそれぞれの情報を紐付けます

```bash
aws iot attach-policy \
--target "登録した証明書のARN" \
--policy-name "expresslink-policy"

aws iot attach-thing-principal \
--thing-name 確認したThingName \
--principal "登録した証明書のARN"
```

これでAWS IoT Core側の準備ができました。最後に、デバイスが接続するためのエンドポイントを調べます。この値をExpressLinkのデバイスに登録します。

```bash
aws iot describe-endpoint \

--endpoint-type iot:Data-ATS
{
    "endpointAddress": "xxxxxxx-ats.iot.ap-northeast-1.amazonaws.com"
}
```

## ExpressLinkモジュールを接続

ここからのコマンドはscreenコマンドで繋いでいるターミナルで実行します。接続先のエンドポイントを登録します。

```bash
AT+CONF Endpoint=xxxxxxx-ats.iot.ap-northeast-1.amazonaws.com

OK
```

Wi-Fiの設定

```bash
AT+CONF SSID=<your router SSID>
AT+CONF Passphrase=<your router passphrase>
```

AWS IoT Coreのテストクライアントで、 `$aws/events/#` トピックにサブスクライブ。やり方は[こちらのページ](https://docs.aws.amazon.com/iot/latest/developerguide/view-mqtt-messages.html)を参照

AWS IoT Coreに接続

```bash
AT+CONNECT

OK 1 CONNECTED
```

AWS IoT Coreのテストクライアントに、以下のようなメッセージが出力されました。

```json
{
  "clientId": "Thing名",
  "timestamp": 1725162234536,
  "eventType": "connected",
  "sessionIdentifier": "2e10e9ba-3446-41d6-b10f-a5d72fe62547",
  "principalIdentifier": "証明書のID",
  "ipAddress": "123.123.123.123",
  "versionNumber": 0
}
```

```json
{
  "clientId": "Thing名",
  "timestamp": 1725162234612,
  "eventType": "subscribed",
  "sessionIdentifier": "2e10e9ba-3446-41d6-b10f-a5d72fe62547",
  "principalIdentifier": "証明書のID",
  "topics": [
    "$aws/things/Thing名/jobs/notify-next"
  ]
}
```

うまくいきました。

ExpressLinkのモジュールはOTA用のtopicにもsubscribeしているのが確認できますね。

次に、テストクライアントで `ublox/data` トピックにsubscribeします。

メッセージを送りましょう

```bash
AT+CONF Topic1=ublox/data

OK
```

```bash
AT+SEND1 Hello World

OK
```

メッセージを受信してみます

```bash
AT+CONF Topic2=ublox/recieve

OK

AT+SUBSCRIBE2

OK
```

AWS IoT Coreのテストクライアントから `ublox/recieve` にメッセージを送ります。そして以下のコマンドで、受信したデータを取得します。

```bash
AT+GET

OK1 ublox/recieve
{\A  "message": "Hello from AWS IoT console"\A}
```

無事に受け取れました。

Topicはリストで管理されており、ATコマンド経由でtopicを操作する際には、`AT+CONF Topic{番号}=Topic名`コマンドで指定した数字を `SEND{数字}` や `SUBSCRIBE{数字}` で指定することでどのTopicを使うかを指定しています。この数字が利用できるTopic数の数に依存しており、モジュールによって異なります。

NORA-W2だと試した感じでは、16個まで利用可能なようです。

```bash
AT+CONF Topic16=aa

OK

AT+CONF Topic17=aa

ERR7 OUT OF RANGE
```

以上で基本的な動きが確認できました。既存のマイコン側でATコマンドを使って操作することで、コネクテッドに必要な複雑な処理や、証明書などのクレデンシャルをセキュアに保つための仕組みを丸投げできるのはかなり便利ですね。

次の記事ではホストマイコンとしてRaspberry Pi picoを使って試してみています。
https://qiita.com/sparkgene/items/c2c4086ae4d098153f25