GR-ROSEでAzure RTOSを使ってAzure IoT Centralに接続

# GR-ROSEでAzure RTOSを使ってAzure IoT Centralに接続

IoT ALGYANのイベントに参加し、GR-ROSEでAzure RTOSを使ったAzure IoT Centralに接続する作品を作ったときに、つまづいた点があったので記事に残しておきます。

<https://algyan.connpass.com/event/224849/>

下記のサイトにコードがあります。

<https://japan.renesasrulz.com/gr_user_forum_japanese/f/gr-rose-iot/7439/meet-up>

Azure RTOSのソースコードを含めてビルドとデバッグ実行するまでの手順と、気付いた点のコード変更の内容を書いておきます。

## ツールのインストール

コンパイラのRX向けGCCと統合開発環境のe2studioをインストールします。

### RX向けGCCのインストール

下記のサイトからダウンロードします。

<https://llvm-gcc-renesas.com/ja>

CyberTHORのアカウントが必要です。

GCCのバージョンは、最新の「8.3.0.202102」で動きました。
ちなみにGR-ROSEスケッチは「4.8.4.201801」で、Azure RTOSは「8.3.0.202004」に設定されています。

### e2studioのインストール

下記のサイトからダウンロードします。

<https://www.renesas.com/jp/ja/software-tool/e-studio>

「統合開発環境e² studio 2021-10 Windows用インストーラ」を探してインストールします。
ルネサスのアカウントが必要です。

## ソースコードのダウンロード

[先ほどのサイト](https://japan.renesasrulz.com/gr_user_forum_japanese/f/gr-rose-iot/7439/meet-up)から``rose_sketch_azure_20210911.zip``をダウンロードします。

こちらは、Azure RTOSがコンパイルされライブラリ形式になっているので、Azure RTOSもデバッグできるようソースコードを入手します。

下記のサイトから``Azure_RTOS_6.1_Renesas_RX65N_RSK_2MB_e2studio_gnurx_Sample_2021_09_15.zip``を探してダウンロードします。

<https://github.com/azure-rtos/samples>

## ソースコードの展開

まず、``Azure_RTOS_6.1_Renesas_RX65N_RSK_2MB_e2studio_gnurx_Sample_2021_09_15.zip``を作業用フォルダに展開します。

``Azure_RTOS_6.1_RSK_RX65N_2MB_e2studio_gnurx_Sample_2021_09_14``フォルダが出来ますが、長いので適当な名前に変更します。

ここでは``D:\Workspace\Azure_RTOS``に展開したこととして説明します。
展開後は以下のようなフォルダ構成になります。

```
D:\Workspace\Azure_RTOS
├filex
├guix
├netxduo
├netxduo_addons
├sample...
└threadx
```

次に``rose_sketch_azure_20210911.zip``を``D:\Workspace\Azure_RTOS``に展開すると、以下のようなフォルダ構成になります。

```
D:\Workspace\Azure_RTOS
├filex
├guix
├netxduo
├netxduo_addons
├rose_sketch_azure
├sample...
└threadx
```

ここで、「sample...」と「guix」は今回のサンプルアプリを動かす分には要らないので、削除しておいても大丈夫です。

各フォルダの``.cproject``はXML形式のテキストファイルで、コンパイラの設定が書かれています。テキストエディタでツールチェーンのバージョンを下記のように書き換えておくと、後で紹介するGUIでの操作が要らなくなります。

```xml
<option id="toolchain.version" value="8.3.0.202102"/>
```

## ビルド

e2studioを起動します。
ワークスペースの選択は上記の例でいうところの``D:\Workspace\Azure_RTOS``を指定します。

![ワークスペースの選択](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242320/eb2061ef-b61c-36af-03f8-78ad6af76513.png)

e2studioが起動すると下記のような画面が表示されます。
![スタートページ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242320/1902789d-f7c0-6a12-1028-b85964f1c5b3.png)

「Import existing projects」を選択して、「ルート・ディレクトリの選択」に``D:\Workspace\Azure_RTOS``を指定します。
下記のように、各フォルダのプロジェクトが表示されチェックが付いていれば、「終了」ボタンを押します。
![プロジェクトのインポート](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242320/5afdbb66-c841-9a37-1c94-a1bd4498332f.png)

それぞれのプロジェクトのツールチェーン・バージョンを合わせます。
![ツールチェーン・バージョン](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242320/f619f3a0-9249-dfe6-04b1-3e886dbc3fcb.png)

「rose_sketch_azure」は「threadx」などのライブラリがビルドされていることが必要なので、プロジェクト参照を設定します。
![プロジェクト参照](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242320/0447ecff-e8de-1d86-365a-5776fd873b6c.png)

すべてビルドします。
![すべてビルド](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242320/1e05c925-6795-297b-da0f-fe5502da5810.png)

``rose_sketch_azure\Debug\rose_sketch_azure.elf``が出来ていたら成功です。

ちなみに、``rose_sketch_azure\libraries``の中に``threadx``や``netxduo``のヘッダーファイルとライブラリがありますが、上記の方法では必要ないので、削除しても大丈夫です。
Azure RTOSのコードを含めずにビルドする場合は、インクルードパスをこちらに変更する必要があります。

「rose_sketch_azure」のデバッグ構成には「ReleaseBin」というのがあり、こちらはGR-ROSEにUSBストレージから書き込む場合の``rose_sketch_azure.bin``ファイルを作るための、手順が入っています。

```cmd
rx-elf-objcopy -O binary --gap-fill 0xff "${workspace_loc:/${ProjName}}/Debug/${ProjName}.elf" ${ProjName}.bin
```

``bin``ファイルが必要なら、プロジェクトのプロパティで、「C/C++ビルド」「設定」にある「ビルド・ステップ」「ビルド後のステップ」の「コマンド」に入力します。

## デバッグ

「E1 Emulator」や「E2 Emulator」を持っていればソースコード・デバッグすることが出来ます。

GR-ROSEのデバッグには配線が必要なので、下記を参考にして配線します。

<https://qiita.com/Kosuke_Matsui/items/312c522dc2244dc809c2>

``rose_sketch_azure.elf.launch``を右クリックし、「デバッグ構成」を選択します。
![デバッグの構成メニュー](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242320/db278e48-cdb8-0e82-4d36-8d80bfd28dc5.png)


デバッグ構成の「Debugger」タブの中の「Connection Setting」を選択します。
![デバッグ構成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242320/2245e4c4-9220-96b6-4813-342fb6448da1.png)

「Debug hardware」を手持ちのデバッガに変更します。

「デバッグ」ボタンを押すとデバッグが開始します。

## DHCP問い合わせ前のリンク確認

``rose_sketch_azure_20210911.zip``では、DHCP問い合わせ前に、Ethernetのリンク確認を行っていますが、上手く行かないようです。
これは、「netxduo」側のフラグがリンク状態になっていないためのようです。
改善策を考えたので、下記に載せておきます。

下記にある空のコールバック関数を変更して、リンク状態の変化を「netxduo」から貰うようにします。

```rose_sketch_azure\src\nx_driver_rx_fit.c
static VOID _rx_ether_cb(VOID *p_arg)
{
}
```

下記のように変更し、リンク状態に変化があったら``rx_ether_link``に保存します。
「netxduo」がリンク状態を確認するためには、``NX_DRIVER_DEFERRED_LINK_STATE_CHANGE``を指定して、``_nx_ip_driver_deferred_processing``を呼び出す必要があるので、``rx_ether_link_process``という関数を用意しておきます。

```rose_sketch_azure\src\nx_driver_rx_fit.c
int rx_ether_link[1];

static VOID _rx_ether_cb(VOID *p_arg)
{
  ether_cb_arg_t *cb_arg = (ether_cb_arg_t *)p_arg;

  if (cb_arg->channel != 0)
    return;

  switch (cb_arg->event_id) {
  case ETHER_CB_EVENT_ID_LINK_ON:
    rx_ether_link[0] = 1;
    break;
  case ETHER_CB_EVENT_ID_LINK_OFF:
    rx_ether_link[0] = 0;
    break;
  }
}

int rx_ether_link_process(UINT chan)
{
    netx_driver_rx_fit_data[chan].deferred_events_flags |= NX_DRIVER_DEFERRED_LINK_STATE_CHANGE;
    _nx_ip_driver_deferred_processing(netx_driver_rx_fit_data[chan].netx_ip_ptr);
    return rx_ether_link[chan];
}
```

次に、元々のリンク確認している以下のコードを、用意した``rx_ether_link_process``の呼び出しに変えます。

```rose_sketch_azure\arduino\sketch.cpp
  while(1)
  {
    R_ETHER_LinkProcess( 0 );
    if(ETHER_SUCCESS == R_ETHER_CheckLink_ZC( 0 ))
    {
      break;
    }
  }
```

まず、``sketch.cpp``の適当なところにプロトタイプ宣言します。

```rose_sketch_azure\arduino\sketch.cpp
extern "C" int rx_ether_link_process(UINT chan);
```

先程のコードを下記のように書き換えます。

```rose_sketch_azure\arduino\sketch.cpp
  while(1)
  {
    tx_thread_sleep(NX_IP_PERIODIC_RATE);
    if (rx_ether_link_process(0))
    {
      break;
    }
  }
```

## NTPのローカルポート

WindowsのICS（インターネット接続の共有）を使って接続する場合、NTPの時刻同期が失敗します。
たぶん、クライアントとしてのWindowsはNTPポート123が塞がれているためだと思います。
「netxduo」のデフォルトでは、NTPのローカルポートも123となっているからです。

時刻要求は送信されるが、時刻応答もUDP 123のためWindowsが止めているので、GR-ROSEには届きません。

```
Internet <- UDP 123 -- Windows <- UDP 123 -- GR-ROSE
Internet -- UDP 123 -x Windows -- UDP 123 -> GR-ROSE
```

lwIPではローカルポートは自動採番しているようですので、下記のように別のポート番号を使うように変えました。

```netxduo\nx_user.h
#define NX_SNTP_CLIENT_UDP_PORT    49153
```

これでもまだ時刻同期は失敗しますので調べると、下記のコードで受信したパケットが弾かれているようです。

```netxduo_addons\addons\sntp\nxd_sntp_client.c
        /* Check if this is the server port the Client expects.  */
        if (sender_port != NX_SNTP_CLIENT_UDP_PORT) 
        {

            /* No, reject the packet.  */
            nx_packet_release(receive_packet);

            continue;
        }
```

NTPサーバーが送ってくるパケットなので、``sender_port``は``NX_SNTP_SERVER_UDP_PORT``な気がしますので、下記のように書き換えます。

```netxduo_addons\addons\sntp\nxd_sntp_client.c
        /* Check if this is the server port the Client expects.  */
        if (sender_port != NX_SNTP_SERVER_UDP_PORT)
        {

            /* No, reject the packet.  */
            nx_packet_release(receive_packet);

            continue;
        }
```

これで、時刻同期も出来るようになりました。