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用インストーラ」を探してインストールします。
ルネサスのアカウントが必要です。
ソースコードのダウンロード
先ほどのサイトから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での操作が要らなくなります。
<option id="toolchain.version" value="8.3.0.202102"/>
ビルド
e2studioを起動します。
ワークスペースの選択は上記の例でいうところのD:\Workspace\Azure_RTOS
を指定します。
e2studioが起動すると下記のような画面が表示されます。
「Import existing projects」を選択して、「ルート・ディレクトリの選択」にD:\Workspace\Azure_RTOS
を指定します。
下記のように、各フォルダのプロジェクトが表示されチェックが付いていれば、「終了」ボタンを押します。
それぞれのプロジェクトのツールチェーン・バージョンを合わせます。
「rose_sketch_azure」は「threadx」などのライブラリがビルドされていることが必要なので、プロジェクト参照を設定します。
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
ファイルを作るための、手順が入っています。
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
を右クリックし、「デバッグ構成」を選択します。
デバッグ構成の「Debugger」タブの中の「Connection Setting」を選択します。
「Debug hardware」を手持ちのデバッガに変更します。
「デバッグ」ボタンを押すとデバッグが開始します。
DHCP問い合わせ前のリンク確認
rose_sketch_azure_20210911.zip
では、DHCP問い合わせ前に、Ethernetのリンク確認を行っていますが、上手く行かないようです。
これは、「netxduo」側のフラグがリンク状態になっていないためのようです。
改善策を考えたので、下記に載せておきます。
下記にある空のコールバック関数を変更して、リンク状態の変化を「netxduo」から貰うようにします。
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
という関数を用意しておきます。
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
の呼び出しに変えます。
while(1)
{
R_ETHER_LinkProcess( 0 );
if(ETHER_SUCCESS == R_ETHER_CheckLink_ZC( 0 ))
{
break;
}
}
まず、sketch.cpp
の適当なところにプロトタイプ宣言します。
extern "C" int rx_ether_link_process(UINT chan);
先程のコードを下記のように書き換えます。
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ではローカルポートは自動採番しているようですので、下記のように別のポート番号を使うように変えました。
# define NX_SNTP_CLIENT_UDP_PORT 49153
これでもまだ時刻同期は失敗しますので調べると、下記のコードで受信したパケットが弾かれているようです。
/* 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
な気がしますので、下記のように書き換えます。
/* 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;
}
これで、時刻同期も出来るようになりました。