近年、IoT(Internet of Things)が注目されている。 特に、センシングされたデータなどを集めて解析を行い、新たな発見を目指す取り組みが盛んに行われている。 センサによって取得されたデータは、主に無線通信によってやり取りされる場合が多い。 特に、多数のセンサを用いてデータを収集できるようにした無線ネットワークをWSN(Wireless Sensor Network)と呼ぶ。 WSNで用いられる通信規格として、WSN構築を主目的とするIEEE802.15.4がある。
IEEE802.15.4はOSI参照モデルで物理層・MAC層に位置する規格である。 通信の速度をWSNに最適な速度に抑える調整により低消費電力を達成し、小型の電池や小型のバッテリで駆動できる。 IEEE802.15.4の上部にあたるネットワーク層の実装は様々ある。 ZigBee Alliance(現Connectivity Standards Alliance)が策定したZigBeeや、IPv6をWSNで用いられるように設計された6LowPANなどが存在する。
これら規格のうちZigBeeは、Digi社より規格に対応した製品としてXBeeと呼ばれる小型の無線送受信機が販売されている。 XBeeは専用のArduinoシールドを用いることにより、Arduinoへ容易に接続可能である。 そのため、低価格でWSNを構築する際によく用いられている。 しかし、ZigBee規格で策定されているのはOSI参照モデルのネットワーク層までであり、アプリケーションを利用する部分は実装によって様々である。
そこで、インターネット通信に用いられるネットワーク層・トランスポート層のTCP/IPをアプリケーションとして実装することで、単なる通信のみならず、既存のプロトコルに沿った多くの機能を提供できるのではないかと考えた。 また、TCP/IPスタックを利用することで、理論上は既存のファイル転送プロトコルを使用可能である。その結果、効率的なファイル転送手段を提供できる可能性があると考えられた。
本研究では、ArduinoとZigBee(XBee)を用い、TCP/IP通信の実装を行う。その後、帯域幅測定などを行い、どの程度の通信が可能であるかを明らかにする。
Arduinoは、Microchip Technology社(合併前はAtmel社)のAVRマイコンを使用した開発キットの一種である。 AVRマイコンは、電子工作等に多く用いられる8bitアーキテクチャのRISCを用いており、比較的利用しやすい点が特徴である。 Arduinoはオープンソースハードウェアである。また、回路図が公開されているため、数多くの互換製品が存在する。
Arduinoの中で一般的なArduino Unoは、ATmega328PというAVRマイコンを使用したマイコンボードである。 プログラムメモリを32KB、RAMを2KB持ち、SPI・I2C・UARTなどの通信機能や16bit・8bitのタイマを持つ[1]。 電子工作等で基本的な制御を行う用途では十分な性能を持つ。
今回の研究では、Arduino UnoをATmega328Pとして使用している。 ArduinoにはArduino IDEと呼ばれる開発環境があり、Arduino言語と呼ばれるC++言語と同等の言語を用いてプログラミングを行う。 コンパイルから書き込みまでを一括で行えるため非常に便利なソフトウェアではあるが、レジスタを直接調整したり等の細かい部分の調整には不向きである。 よって、素のC言語を使用し、avr-gccによってコンパイルし、Avrdudeによって書き込む方法を取った。
マイコン間通信等に用いられるシリアル通信は、1本の伝送路を使用して1ビットずつデータを送受信する通信の方法である。RS-232やUSBはシリアル通信の規格の1つとなる。
Arduinoに搭載されるATmega328Pなどのマイコンには、UARTという周辺機能を持つ種類がある。 これは、プロセッサ内部で作成されたパラレルデータをシリアルデータに変換し、外部とやり取りを行うための機能を提供するものである。 UARTによって作成されたデータはArduinoボード上のTxピンから送信される。また、Rxピンを通して外部からシリアルデータを受信する。
図1 シリアルデータの構造
シリアル通信によって送受信されるデータの構造を図1に示す。8ビットのデータを送信するとき、前後のデータと区別をするため、スタートビットとストップビットと呼ばれる識別用データを付加する。また、パリティビットを追加してデータの誤りを検出可能な仕組みを持つ。 スタートビット、ストップビットやパリティを使用する場合、それぞれに1ビット必要となる。従って、8ビットのデータを送信する場合は、少なくとも10ビット以上のデータが送信される。
シリアル通信によってデータを送受信する際に、送信・受信側で決めた伝送速度を使用する。このとき、伝送速度の指標となる値がボーレートである。ボーレートは、デジタルデータを1秒間に変調可能な値を示す。例えば、ボーレートが9600[bps]であれば1秒あたりに9600回変調するので、シリアルデータ1ビットに対し約104[µs]程度掛かる計算になる。従って、8ビットデータとスタートビット・ストップビットの10ビットを送信する場合は1.04[ms]程度の時間が掛かる。
シリアル通信ではTx、Rxの2つの伝送線によってデータの送受信を行う。 シリアル受信やシリアル送信後の処理はマイコンの性能に左右される。マイコンがデータを処理している間にもデータが到着する場合があり、それらのデータを一時的に保管するバッファを持つデバイスが多い。しかし、そのバッファの大きさを超えるようなデータが到着した時や、マイコンの処理が間に合わずにデータが溜まる場合がある。このような状態が続くとバッファがオーバーフローする。オーバーフローした場合は、送信側が送信したデータを正しく受信できない事態につながる。そこで、バッファがオーバーフローする前に送信側にデータの送信を待機してもらうなどのフロー制御が用いられる。シリアル通信で用いられるフロー制御のうち、ハードウェア的に行う方法がRTS/CTSフロー制御である。
図2 RTS/CTSフロー制御
データの送受信で用いるTx/Rx線の他にRTS/CTSの2線を用意することでフロー制御が可能となる。RTS(Request To Send)は出力であり、CTS(Clear To Send)は入力にあたる。また、RTS/CTSは負論理となっており、CTSにHighが入力されるとデータの送信が止まる。
入力側にあたるCTS(Clear To Send)がLowで入力されているとき、送信側はシリアルデータを送信可能である。ここで、受信側の受信バッファがオーバーフローする手前で受信側はRTS(Request To Send)をHighとする。すると、送信側のCTSがHighになり、送信側はデータの送信を止める。受信側のバッファに余裕が生まれると、送信側はRTSをLowに戻すので、再びデータの送信を再開する。このようにフロー制御を用いることで、データのロスが発生しないようにシリアル通信を行える。
OSI参照モデルとは、異なるコンピュータ間での接続を行うために遵守されるべき階層型モデルのことである。OSI参照モデルを用いて各種プロトコルをレイヤ(層)ごとに管理することで、各レイヤごとのプロトコルを独立的に扱え、単純な仕組みや構造にできる。以下に、基本となる7層を表した図を示す。
図3 OSI参照モデル
図で示したように、OSI参照モデルではコンピュータ同士の通信を第1層の物理層から第7層のアプリケーション層に分類している。各レイヤではそれぞれのレイヤで定義・規定されたサービスを提供し、同階層同士であればプロトコルを用いて下位レイヤを気にせず通信が行える。また、上下の各層でサービスの呼び出しを行ったり応答を受け取るための機能をインターフェースという。
アプリケーション層やプレゼンテーション層といった上位レイヤで作成されたデータは、トランスポート層・インターネット層と順に下位レイヤへ送られる。その際に各レイヤで定義されたプロトコルに沿ったパケットになるように制御データが付けられる。そして物理層を通じて他のコンピュータへ送られる。
他のコンピュータは受け取ったデータを下位から順に処理し、制御データを取り外して上位レイヤへ渡す。この繰り返しで上位レイヤまでデータが送られるとアプリケーションが処理する。このような流れでコンピュータ間の通信を行う。
次節以降で述べるZigBeeやTCP/IPによる通信も、OSI参照モデルに則った階層型の構造を持っている。ただし、7層のうちいくつかの層がまとめられているなど、それぞれの特性に応じて最適化がされている場合がある。
ZigBeeは、WSNを主目的に策定されたIEEE802.15.4標準に基づく通信仕様標準の1つである。 低消費電力である代わりに転送速度が低速であったり、メッシュネットワークを構築可能である通信仕様がZigBeeプロトコルである。 このZigBeeプロトコルを用いて通信を行う無線通信端末がXBeeである。 XBeeはこれまでにいくつかの製品シリーズがリリースされており、S1(シリーズ1)からS2(シリーズ2)など、シリーズ番号が上がる際に若干の仕様変更がなされている。 S1とS2は仕様が異なり通信はできないが、S2、S2B、S2Cなどのリビジョン違いの製品は多少の差はあれど相互で使用できる。
図4 OSI参照モデルとZigBeeプロトコルの階層
OSI参照モデルとZigBeeプロトコルの階層構造は図4のように表される。 ZigBeeプロトコルは、物理層・データリンク層にあたる部分をIEEE802.15.4に則った実装としている。 そのため、無線通信に868MHz帯、815MHz帯、915MHz帯、2.4GHz帯を使用できる。 日本では2.4GHz帯のみ使用可能となっている。
ネットワーク層から上位レイヤに関してはZigBee独自の規格となっている。 ネットワーク層では、無線通信のデータパケットが複数のデバイスを経由して、正しく宛先にデータが送信できるようにルーティングを行う役割を持つ。
トランスポート層以上のレイヤはアプリケーションサポートサブレイヤ(APS)、アプリケーション層の2つに大別される。 アプリケーション層とネットワーク層以下を繋ぐインターフェースの役割を果たすのがAPSである。
ZigBeeには3つのデバイスタイプが存在する。 それぞれ、コーディネータ、ルータ、エンドデバイスである。コーディネータには、PAN(Personal Area Network)を構築する機能、チャネル管理などのネットワーク管理、ゲートウェイといった機能を持つ。 そのため、ZigBeeによって構成されるメッシュネットワークには必ず1台のコーディネータが必要である。 ルータは主に通信の中継をする機能を持ち、他デバイスからの通信を受け入れられる。 エンドデバイスはメッシュネットワークの最も端に位置するデバイスである。 スリープ機能を持っており、間欠動作で用いる際に用いる。 エンドデバイスがデータを送信するためには、ルータまたはコーディネータが必要である。 また、エンドデバイスは他デバイスから通信によってデータを受け入れる機能を持たないため、一方向のコミュニケーションをとるデバイスである。
図5 メッシュネットワーク構築例
ルータやコーディネータ、エンドデバイスを組み合わせることで、図5に示すようなメッシュネットワークを構築可能である。
ZigBeeでは、ATモードとAPIモードの2つの動作モードが用意されている。本実験ではATモードを用いて行う。以下に、それぞれの動作モードの特徴を示す。
XCTUは、Digi社が提供するXBee用設定・テストツールである。XBeeはATモードではコマンド送信する形でXBee自体の設定を行うが、XCTUを用いることでGUIを用いて容易に設定を行なえる。以下に示す図がXCTUのパラメータ設定画面である。この他に、送受信したシリアルデータの確認やネットワークトポロジーの確認を行う機能も持つ。
図6 XCTUのパラメータ設定画面
TCP/IPは様々なデバイスにおいて、インターネットによる通信を利用する際に用いられるプロトコルである。 TCP/IPはOSI参照モデルに対応するように実装されているが、効率化・最適化のために一部レイヤが統合されるなどされている。
図7 OSI参照モデルとTCP/IPプロトコルスタック
ネットワークインターフェース層は、OSI参照モデルでの物理層、データリンク層にあたる。 Ethernetや電波などを制御して通信を実現するための層である。
インターネット層は、OSI参照モデルでのネットワーク層にあたる。 このレイヤで用いられるプロトコルがIPである。IPはコネクションレス型の通信を提供し、インターネット上で接続されたデバイス同士でデータが含まれたパケットのやり取りに使われる。 ネットワーク上のデバイス識別のためにIPアドレスと呼ばれるアドレスを用いる。 IPによって作成されたパケットには送信先のIPアドレスが含まれており、このパケットはネットワーク上のルータを経由して目的の送信先デバイスに到達する。
IPはコネクションレス型である以上、実際に相手にパケットが届いたかどうかを保証しない。よって、信頼性が必要な通信の場合は、上位のレイヤの機能を用いて補う必要がある。
トランスポート層は、OSI参照モデルでのトランスポート層と同等である。 このレイヤではTCPやUDPといったプロトコルが用いられる。 TCPはコネクション型の通信を提供するプロトコルである。 他のデバイスから受け取ったパケットにエラーや不足がないことを確認する機能を持つ。% typo 必要応じて => 必要に応じて また、必要に応じて再送を要求する機能を持つ。 IPのコネクションレス型を補完し、信頼性が必要な通信で用いられる。
一方UDPはコネクションレス型のプロトコルである。 効率的な通信を優先しているためTCPのような信頼性を保つための機能は持たない。 そのため、比較的単純なパケット構造や処理の仕組みとなっている。
アプリケーション層は、OSI参照モデルでのセッション層からアプリケーション層にあたる。 TCP/IPやUDPから受け取ったデータを実際に処理してサービスを提供する部分である。 HTTPやFTPなどはアプリケーション層で用いられるプロトコルである。
µIPは、8ビットマイコンにおいてTCP/IPを使用した通信を行うことを目的に開発された、軽量TCP/IPスタックである[5]。Swedish Institute of Computer ScienceのAdam Dankelsらが中心となり開発されたプロジェクトである。Adam Dankelsはマイコンで動作するOSのContiki OSにも関与しており、Contiki OSのTCP/IPスタックにもµIPが採用されている。ソースコードのすべてがC言語で記述されているため、各マイコン向けに用意されているコンパイラとも相性が良い。また、Arduino向けに調整されたライブラリがいくつか存在する。µIP の特徴として、次の点が挙げられる [6]。
上記のように、8ビットマイコン上でTCP/IPによる通信を行うための機能は一通り揃えられている。ただし、実装サイズを抑えるために極僅かでしか用いられないような機能やインターフェースが省かれている。
SLIPは、従来のダイヤルアップ接続での通信において、シリアルラインでのTCP/IP接続ができるように開発されたプロトコルである。TCP/IPのパケットをシリアルラインで送受信するためにフレーム化するという機能のみを持つ。パケットがバイト列の中で区別できるよう、特殊な終端文字を追加してパケットの混雑を防いでいる。
SLIPによって、マイコンで用いられるUARTポートを用いたシリアル通信にTCP/IPのパケットを流せるようになる。これにより、シリアルデバイスをネットワークデバイスへ読み替える「slattach」コマンドなどが必要になるが、PC上のOSにあるターミナルなどを介して容易にTCP/IP通信が可能となる。
ただし、SLIPは1対1のシリアルラインでの通信を主目的としているため、動的な接続先の変更はできない。また、静的に接続するため互いが事前にアドレスなど必要な情報を知っている必要がある。これらの欠点を補うプロトコルが、後に開発されたPPP(Point to Point Protocol)である。
IPerfは、複数台のPCを使用してネットワーク回線の負荷試験・スループット計測などを行うツールである。WindowsだけでなくLinux系OSなどでも使用でき、環境によらずに使用できるという利点がある[8]。
IPerfのプログラムはクライアント側、サーバ側の2つに分かれている。予めサーバ側プログラムを起動してTCPの指定ポートで待機させ、そのポートにクライアント側プログラムで接続することでスループット計測を行う。
IPerfにはいくつかのバージョンがあり、2021年現在での最新はIPerf3である。今回はIPerf3でなく、Arduino上でIPerfサーバを試したWebページ(How to Measure Arduino Network Performance)に倣い、IPerf2を使用した。IPerf2を使用した理由は、バージョン2と3の間で互換性が無く、Webページで示された方法が使用できない事態を防ぐためである。
XBeeとµIPを用いたTCP/IP通信を実現するための基本的なアイデアは次の通りである。
ZigBeeレイヤには、ユーザが記述した処理を行わせることのできるアプリケーション層がある。XBeeの中には、通信機能の他にプログラマブルなマイコンを搭載することにより、1ボード内で完結させることのできるデバイスもある。今回はXBeeを単なる通信用デバイスとして扱い、アプリケーション層にあたる処理はArduino(AVRマイコン)へのプログラミングで定義する。従って、AVRマイコンがZigBeeレイヤのアプリケーション層としての動作を果たし、TCP/IPの処理並びに更に上位レイヤであるアプリケーション層の処理を行うことになる。
図8 TCP/IP階層モデルとZigBeeレイヤの対応及び実装
各機能をTCP/IPの4階層モデルに照らし合わせた図が図8である。ZigBeeレイヤでの物理層からAPSまでが、TCP/IP階層モデルにおけるネットワークインターフェース層にあたる。この層はXBeeの通信機能をそのまま利用する形とする。 また、ZigBeeレイヤでのアプリケーション層が、TCP/IP階層モデルにおけるインターネット層からアプリケーション層にあたる。この部分がAVRマイコンによる実装となる。
このように、ZigBeeレイヤのアプリケーション層部分にTCP/IPの機能を載せることによってXBeeを使用したTCP/IP通信を実現していく。
TCP/IPの4層モデルにおけるネットワークインターフェース層の部分は、XBee S2CによるZigBee通信を利用する形態とする。以下に、物理的な接続の概略図を示す。
図9 物理的な接続の概略図
以下、XBeeが有線接続されたPC側をデバイス1、XBeeがシールドによって接続されたマイコン側をデバイス2とする。
各XBee間の通信にはZigBeeでの無線通信を使用する。PC側のXBeeは、XBee単体ではPCと接続することはできない。従って、デバイス1側のXBeeは開発ボードに搭載し、USBポートを介したシリアル通信を行う。デバイス2側のXBeeはArduinoシールドに搭載し、マイコンとシリアル通信を行う。
ここで、デバイス間で通信するデータはSLIPを用いたデータ構造とし、生のシリアルデータを送受信する形とする。シリアルデバイスをネットワークデバイスとして変換・使用するSLIPを用いることで、PCにインストールされたOS上のターミナル等の既存アプリケーションを変更せずに使用できる利点がある。また、SLIPは生のシリアルデータに独自の終端文字を付与してパケット化し通信を行う。よって、XBeeの通信モードをATモードに限定することで実装を簡略化でき、SLIPを使用する恩恵を受けることができる。APIモードで実装する場合は、PC側で受け取ったZigBeeパケットを解釈するプログラムや複数のパケットのペイロードを1つにまとめる処理が必要になり、実装が複雑となる。
TCP/IPの4層モデルにおけるインターネット層、トランスポート層の部分は、AVRマイコンのプログラムとして実装する。本研究ではµIPによるTCP/IPスタックを利用する。
µIPでは、TCP/IPの処理を単一のメインプログラムとして実装して処理を行う。そのためには、各マイコンのアーキテクチャに対応したクロック設定や下位レイヤを利用するためのドライバ等を適切に実装する必要がある。
TCP/IPの処理を行う上でタイムアウト等の判定を行うには、正確な時間管理を行う必要がある。µIPでは、タイマーを用いて時間管理を容易にするタイマーライブラリが用意されている。このライブラリを有効にするため、使用するマイコンのタイマー割り込み等を用いて、タイマーライブラリに伝える処理を実装する。それがclock-arch.hとclock-arch.cのプログラムである。
#ifndef __CLOCK_ARCH_H__
#define __CLOCK_ARCH_H__
#include <stdint.h>
typedef uint16_t clock_time_t;
#define CLOCK_CONF_SECOND (clock_time_t)61
#include "clock.h"
#endif
ソースコード1 clock-arch.h
#include "global-conf.h"
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sfr_defs.h>
#include "clock-arch.h"
// カウンター
clock_time_t clock_datetime = 0;
ISR(TIMER0_OVF_vect)
{
clock_datetime += 1;
TIFR0 |= (1 << TOV0);
}
/**
* @brief
* clockの初期化
*/
void clock_init()
{
// timer0のオーバーフロー割り込みを有効化
TIMSK0 |= (1 << TOIE0);
// プリスケーラを1024とする
TCCR0B |= ((1 << CS12)|(1 << CS10));
// 割り込み許可
sei();
}
/**
* @brief
* timeを返却
* @return clock_time_t
*/
clock_time_t clock_time()
{
clock_time_t time;
cli();
time = clock_datetime;
sei();
return time;
}
ソースコード2 clock-arch.c
今回使用するマイコン(ATmega328P)はArduinoボード上に実装されている。そのため、AVRマイコンはボード上に実装されている水晶発振器により、16MHzのクロック周波数で動作する。ATmega328Pにはいくつかのタイマーが存在するが、8bitのTimer0を使用する。Timer0の設定は以下のようにした。
これらの設定より、1秒を計測するために必要なカウントの値を計算する。まず、分周比を1024と設定しているので、1024回に1回Timer0の値が1増えることになる。ここから、タイマー割り込み間隔が次の式によって求められる。
この式から、clock_time_tは約16[ms]毎に1カウントアップすることが分かる。したがって、clock_time_tによって1秒を計測するためには、上限を61とするとほぼ1秒を計測できる。ここから、ソースコード1のCLOCK\_CONF\_SECONDが61と定義される。 ソースコード2では、実際にTimer0を使用するための初期化処理や、現在のclock_time_tのカウント値を取得する処理が実装されている。これらのソースコードは、上位に存在するタイマーライブラリによって使用される。
µIPの処理系へ外部デバイスから受け取ったデータを渡すためにドライバが必要である。µIPバージョン1.0では、Ethernetを用いたデバイス用のドライバは用意されている。しかし、今回使用するSLIP(シリアル通信)用のドライバは含まれていない。従って、過去のµIPバージョンからSLIP用ドライバを移植し、AVRマイコンに対応するように改変することで対応する。
µIPを用いたTCP/IP通信を使用するため、ドライバ側とTCP/IPスタック・アプリケーションをつなげる役目を果たすのがメイン制御ループである。メイン制御ループの実装を疑似プログラムとして下記に示す。
// メイン関数 //
int main(void)
{
// 時間切れ(=タイムアウト)のタイマーを容易・初期化
struct timer periodic_timer;
clock_init();
timer_set(&periodic_timer, CLOCK_SECOND / 4);
// SLIPドライバの初期化 (m328のuart初期化を含む)
slipdev_init();
// uIP初期化
uip_init();
// IPアドレスを設定
uip_ipaddr(ipaddr, 192, 168, 5, 3);
uip_sethostaddr(ipaddr);
// デフォルトゲートウェイを設定
uip_ipaddr(ipaddr, 192, 168, 5, 1);
uip_setdraddr(ipaddr);
// サブネットマスクを設定
uip_ipaddr(ipaddr, 255, 255, 255, 0);
uip_setnetmask(ipaddr);
// アプリケーションを開始する //
// メイン制御ループ //
while (1)
{
// SLIPでバッファにデータを読み取る //
uip_len = slipdev_poll();
// データがあれば処理をする //
if (uip_len > 0)
{
// データを処理系に入力
// 処理後に送信するデータがあれば //
if (uip_len > 0)
{
// データをSLIPで送信する
}
}
// タイムアウトした場合 //
else if (timer_expired(&periodic_timer))
{
// タイマーをリセット
// タイムアウト時の処理をする
}
}
}
ソースコード3 メイン制御ループの疑似プログラム
ソースコード3に示すような処理をメインプログラムとして実装することによって、µIPを使用したTCP/IP通信を使用することが可能となる。この際に、前述したタイマーライブラリやドライバが必要となる。メイン制御ループでは以下のような処理を行う。
アプリケーション層の部分は、µIPアプリケーションとして実装することになる。µIPでアプリケーションを作成する場合、アプリケーションがTCP/IPスタックと対話する仕組みが二通り用意されている。一つは"プロトソケット"と呼ばれるものであり、TCPを使用する際に非常に容易に対話ができるAPIとなっている。もう一つは"µIPRaw API"と呼ばれるものである。こちらはプロトソケットより低レベルであるが、メモリ使用量を抑えることが可能である。今回はRaw APIを用いたアプリケーション層の実装を行う。
// アプリケーションの初期化を行う //
void example_myapp_init(void) {
// ポート開放など
}
// もう一方のデバイス側でリセットされた際に行う処理 //
static void aborted(void) {}
// 接続がタイムアウトした際に行う処理 //
static void timedout(void) {}
// もう一方のデバイス側で接続が閉じられた際に行う処理 //
static void closed(void){}
// 接続が開始された際に行う処理 //
static void connected(void) {}
// 新しいデータが到着した際に行う処理 //
static void newdata(void) {}
// ackを受け取った際に行う処理 //
static void acked(void) {}
// データを送信する処理 //
static void senddata(void) {}
void example_myapp_appcall(void) {
if (uip_aborted()) {
aborted();
}
if (uip_timedout()) {
timedout();
}
if (uip_closed()) {
closed();
}
if (uip_connected()) {
connected();
}
if (uip_acked()) {
acked();
}
if (uip_newdata()) {
newdata();
}
if (uip_rexmit() ||
uip_newdata() ||
uip_acked() ||
uip_connected() ||
uip_poll()) {
senddata();
}
}
ソースコード4 アプリケーションの基本構造
µIP Raw APIを使用したアプリケーションは上記のソースコードに従って定義される。このアプリケーションに目的のプロトコルを実装することにより、TCP/IPを用いる様々なアプリケーションを作成することが可能となる。今回の研究においてIPerfを用いた測定を行ったが、IPerfサーバを模倣するアプリケーションも上記のアプリケーション構造に従ったプログラムを作成した。
本計測では、3章にて設計・実装したArduinoとµIP、XBeeを用いたTCP/IP通信の特性を調べる。XBeeでの伝送速度の指標となるボーレートを変化させた際に、どれだけのスループットを得られるかの計測を行った。また、RTS/CTSフロー制御を組み込んだ際に通信の特性が変化するかを調べた。
本計測にて使用した機器は以下に示す通りである。
表1 計測に使用した機器一覧
使用機器名 | 型番等 | 個数 |
---|---|---|
XBee S2C | XBee ZigBee S2CTH | 2 |
Arduino 用 XBee シールド | DFR0015 | 1 |
XBee シールド (スイッチサイエンス製) | 1 | |
XBee 開発ボード | XBee THT Grove Development Board | 1 |
Arduino Uno | Arduino Uno Rev.3 | 2 |
ロジックレベル変換モジュール | FXMA108 | 1 |
ジャンパワイヤ | 数本 | |
ブレッドボード | 1 | |
USB ケーブル | A オス-マイクロ B オス | 1 |
USB ケーブル | A オス-B オス | 1 |
PC | Optiplex3060 | 1 |
使用したPCの構成を以下に示す。
表2 計測に使用したPCの構成
PC | Optiplex3060 SFF |
---|---|
ホストOS | Windows 11 Pro (Version 10.0.22000.376) |
CPU | Intel Core i5-8500 |
ゲストOS1 | Ubuntu 18.04 LTS (Windows Subsystem for Liunx 2) |
ゲストOS2 | Ubuntu 20.04 LTS (VirtualBox) |
今回の計測では、プログラムのコンパイル・マイコンへの書き込み・マイコンとの通信をそれぞれ同じPC上の別環境で行った。それぞれの関係性を以下に図で示す。
図10 PC上の環境概略図
Windowsでは、XBeeの設定やAVRマイコンへのプログラム書き込みを主に行った。WSL2上のUbuntu 18.04ではAVRマイコン向けプログラムのコンパイルを行った。また、Virtual Box(仮想マシン)上のUbuntu 20.04は、実際にマイコンとTCP/IP通信を行う実験環境として使用した。
表3 計測に使用したソフトウェア等
ソフトウェア名 | バージョン | 使用した状況など |
---|---|---|
Oracle VM VirtualBox | 6.1.28 r147628 | Ubuntuを仮想マシン上で動作させるため |
Windows Subsystem for Linux | バージョン2 | Ubuntuを軽量コンテナ上で動作させるため |
XCTU | Version: 6.5.6 | XBeeの設定を行うため |
Avrdude | Version 6.3.1.1-windows | AVRマイコンへ書き込みを行うため |
avr-gcc | 5.4.0 | C言語プログラムをコンパイルするため |
avr-objcopy | 2.26.20160125 | コンパイル後に使用する |
slattach | 2.10-alpha | SLIPを使用するため |
IPerf2 | 2.0.13 | スループット計測のため |
次に、XBeeやArduino等の接続図を以下に示す。
図11 接続図
また、実際に配線したときの画像を以下に示す。
図12 実際に接続した様子(ロジックレベル変換を含む)
TCP/IPを用いた通信を行うため、それぞれのデバイスへ次のようなアドレスを設定した。
表4 IPアドレス等の設定状況
PC側 | マイコン側 | |
---|---|---|
IPアドレス | 192.168.5.1 | 192.168.5.3 |
サブネットマスク | 255.255.255.0 | 255.255.255.0 |
デフォルトゲートウェイ | 192.168.5.1 | 192.168.5.1 |
最後に、今回の計測ではµIPの設定を以下のように行った。
表5 µIPの設定
設定 | 値 | 意味 |
---|---|---|
UIP_CONF_MAX_CONNECTIONS | 1 | TCP接続可能な最大値 |
UIP_CONF_MAX_LISTENPORTS | 1 | 待機できるTCPポートの最大値 |
UIP_CONF_BUFFER_SIZE | 128 | µIPバッファの大きさ |
UIP_CONF_BYTE_ORDER | LITTLE_ENDIAN | AVRマイコンであればこの値 |
UIP_CONF_LOGGING | 0 | ログ機能は使用しない |
UIP_CONF_UDP | 0 | UDPは使用しない |
UIP_CONF_UDP_CHECKSUMS | 0 | UDPチェックサムは使用しない |
UIP_CONF_STATISTICS | 0 | 統計を使用しない |
UIP_CONF_BROADCAST | 0 | ブロードキャストを使用しない |
UIP_CONF_UDP_CONNS | 0 | UDP接続の最大値 |
UIP_REASSEMBLY | 0 | IPパケットを再構成しない |
UIP_CONF_LLH_LEN | 0 | リンクレベルヘッダの長さ(SLIPなので0) |
表6 XBeeの設定値
設定値 | XBee1 | XBee2 | 意味 |
---|---|---|---|
ID | 2021 | 2021 | PAN ID |
JV | Enabled | Disabled | Channel Verification |
JN | Disabled | Disabled | Join Notification |
CE | Disabled | Enabled | Coordinator Enable |
DH | 13A200 | 13A200 | Destination Address High |
DL | 41531D4A | 41531EBF | Destination Address Low |
NI | ROUTER | COORD | Node Identifier |
BD | 1200 | 1200 | Baud Rate |
以下、XBee1をArduinoシールド側に接続したXBee、XBee2をPCに接続したXBeeとして扱う。
ping -c 10 192.168.5.3
これらを適用したIPerfのコマンドを以下に示す。
iperf -c 192.168.5.3 -i 1 -t 30
表7 XBeeの設定値
設定値 | XBee1 | XBee2 |
---|---|---|
D6 | nRTS flow control | Disable |
ping -c 10 192.168.5.3
これらを適用したIPerfのコマンドを以下に示す。
iperf -c 192.168.5.3 -i 1 -t 30
計測手順1において、フロー制御を使用していない場合のping結果は以下のようになった。なお、以下の結果が含まれるコンソールログ全文は付録に掲載した。
表8 ボーレートに対するping統計値
ボーレート[bps] | パケット損失[%] | RTT最小[ms] | RTT平均[ms] | RTT最大[ms] |
---|---|---|---|---|
1200 | 0 | 2895.047 | 2903.899 | 2911.118 |
2400 | 0 | 1462.325 | 1470.495 | 1481.756 |
4800 | 0 | 746.137 | 753.217 | 771.823 |
9600 | 0 | 388.704 | 394.371 | 400.673 |
19200 | 0 | 204.480 | 212.714 | 219.170 |
38400 | 0 | 116.488 | 124.927 | 134.118 |
57600 | 0 | 92.585 | 96.641 | 101.805 |
115200 | 100 | - | - | - |
図13 ボーレートに対する往復遅延時間の最小値・平均値・最大値の特性
フロー制御を使用しない場合、pingによるRTT(往復遅延時間)はボーレートが大きくなるにつれて減少する傾向が見られる。また、RTT最小・平均・最大の各値に大きな差がないと分かる。 なお、XBeeが使用できる標準最大ボーレートの115200[bps]では、パケット損失が100[%]となり、全く通信ができていないと確認された。
次に、計測手順1での、フロー制御を使用していない場合のIPerfによる帯域幅の結果を以下に示す。なお、以下の結果が含まれるコンソールログ全文は付録に掲載した。
表9 ボーレートに対するIPerfによる帯域幅
ボーレート[bps] | 帯域幅[kbps] |
---|---|
1200 | 0.699 |
2400 | 0.951 |
4800 | 1.453 |
9600 | 2.376 |
19200 | 4.003 |
38400 | 6.52 |
57600 | 8.18 |
115200 | - |
図14 ボーレートに対する帯域幅の特性
上記のグラフは、表9の結果を両対数グラフで示したものである。このグラフから、ボーレートが倍々に増加する2400[bps]から38400[bps]の間で、帯域幅が直線的な増加をしている傾向が分かる。なお、pingによる計測にて使用できなかったボーレート115200[bps]は、IPerfによる帯域幅計測でも測定不能であった。
計測手順2での、フロー制御を使用した場合のping結果は以下のようになった。なお、以下の結果が含まれるコンソールログ全文は付録に掲載した。
表10 ボーレートに対するping統計値
ボーレート[bps] | パケット損失[%] | RTT最小[ms] | RTT平均[ms] | RTT最大[ms] |
---|---|---|---|---|
1200 | 0 | 2779.340 | 2844.586 | 2926.622 |
2400 | 0 | 1390.903 | 1437.417 | 1476.864 |
4800 | 0 | 752.598 | 1085.977 | 1591.828 |
9600 | 0 | 391.921 | 1032.577 | 1313.606 |
19200 | 0 | 1131.868 | 1161.792 | 1188.682 |
38400 | 0 | 1069.943 | 1075.674 | 1084.303 |
57600 | 0 | 1049.754 | 1057.236 | 1060.496 |
115200 | 0 | 1035.020 | 1047.006 | 1082.609 |
図15 ボーレートに対する往復遅延時間の最小値・平均値・最大値の特性
フロー制御を使用した場合、ボーレートの115200[bps]にて通信ができると確認された。しかし、計測1のように単にRTTが減少していく結果では無かった。1200[bps]~2400[bps]の範囲では、単純にRTTは減少しているが、2400[bps]~19200[bps]の間ではRTT最小・最大に大きく差が見られる。また、19200[bps]以降では約1000[ms]付近でRTTが安定する傾向が見られる。
次に、計測手順2における、フロー制御を使用していない場合のIPerfによる帯域幅の結果を以下に示す。なお、以下の結果が含まれるコンソールログ全文は付録に掲載した。
表11 ボーレートに対するIPerfによる帯域幅
ボーレート[bps] | 帯域幅[kbps] |
---|---|
1200 | 0.687 |
2400 | 0.966 |
4800 | 1.460 |
9600 | 2.383 |
19200 | 4.010 |
38400 | 6.583 |
57600 | 8.576 |
115200 | 10.96 |
図16 ボーレートに対する帯域幅の特性
上記のグラフは、表11の結果を両対数グラフに表したものである。フロー制御無しの場合と同様な結果となった。加えてボーレートが115200[bps]にて通信が行え、実際に帯域幅を測定できた。
計測の結果から、フロー制御の有無が計測値に影響を与える場合と、概ね影響が無い場合の2通りが見て取れた。
フロー制御が影響を与えたと考えられる計測はpingである。フロー制御の有無の影響を比較するため、以下にpingによって測定できるRTT3値をグラフに表した図を示す。
図17 フロー制御の有無によるRTT最小値の比較
上記のグラフから見て取れるのは、ボーレートが19200[bps]以上であると、RTT最小値がフロー制御無しと比較して極端に悪化する傾向である。19200[bps]では約927[ms]、38400[bps]では約953[ms]、57600[bps]では約957[ms]と、1秒に近い悪化が見られる。この結果からして、ボーレートが19200[bps]以降ではシリアル通信の部分でフロー制御が発生していると考えるのが妥当である。RTS/CTSフロー制御では処理が間に合わない時にデータの送信を一時停止するため、今回見られた1秒近くの遅延は、マイコン側でのシリアル受信バッファが溢れた結果の遅延であると考えられる。
RTT最小値は、10回pingによる計測を行った中での最良値である。従って、ボーレート19200[bps]以降では、全てのpingの際にフロー制御が生じていたと考えるのが妥当である。
図18 フロー制御の有無によるRTT最大値の比較
上記のグラフは、RTT最大値の比較を行ったものである。RTT最小値を比較したグラフでは、フロー制御の有無による違いが見えたのはボーレートが19200[bps]以降であった。しかし、RTT最大値の比較では4800[bps]になった時点で差がある。フロー制御が無い場合は、ボーレートが上がるに従ってRTTは次第に減少するような曲線が見られる。フロー制御がある場合は、4800[bps]で一度悪化してから緩やかに減少していく形となっている。
RTT最大は、10回pingによる計測を行った中での最悪値であるので、ボーレート4800[bps]以降で、RTTが極端に悪化する事象が発生していると考えられる。2つの計測ではフロー制御の有無のみを変え、それ以外のパラメータやXBee間の物理的な距離は変更していないため、RTT悪化に関与した事象はフロー制御である可能性が高い。従って、ボーレート4800[bps]以降では少なくとも1回は、pingの際にフロー制御が掛かったと考えられる。
計測1・2やフロー制御の有無によるRTT最小・最大の比較結果から、次のように考えられる。
pingによる計測では、フロー制御が結果に大きく影響したと考えられる。しかし、IPerfではフロー制御の有無は結果に影響が無いように見られる。以下に、フロー制御の有無によるIPerfでの帯域幅測定の比較を示す。
図19 フロー制御の有無による帯域幅の比較
上記に示したように、フロー制御をしていない状態ではボーレート57600[bps]までの計測しか実行できなかったという差はあるが、フロー制御の有無に関わらずほぼ同じ値を取ると分かる。このような結果を示した理由は以下の様に考えられる。
RTS/CTSフロー制御の発動条件とパケットサイズである。今回、AVRマイコンのシリアル通信を行うために外部のライブラリを使用した。今回使用したライブラリでは、シリアル受信バッファが設定値の-1バイト(今回は128バイト設定なので127バイト)になった際に、RTS出力をHighにする制御が行われている。その後、追加で2バイト分を受信できるようになっている。これは、RTSをHighにした時に相手に伝達するまでの時間差を考慮した実装である。
ここで、今回のIPerfによる測定ではµIPバッファを128バイトとしている関係で、互いにやり取りされるパケットの最大値サイズが128バイトである制限を考慮すると、以下のような処理となっていた可能性がある。
上記のような処理になっているのであれば、IPerf計測でのフロー制御は高ボーレートでの安定性を担保する程度の役割しか果たさず、57600[bps]までの結果がほぼ同一になったと考えられる。
フロー制御の有無がIPerfでの帯域幅測定に影響を及ぼさないと結果から見て取れるが、ボーレートに対して帯域幅が小さい結果が見られる。この理由を考察するため、パケットキャプチャを行った。パケットキャプチャを行うにあたって、計測2の手順に則り、ボーレートを57600[bps]に設定した。 ボーレート57600[bps]での1パケット毎の往復遅延時間を、WireSharkというネットワーク解析ソフトウェアにより解析した結果を以下に示す。
図20 1パケット毎の往復遅延時間(フロー制御あり・57600[bps])
グラフ上に示された点が1パケット毎のRTTを示している。上記のグラフを見る限り、多少の上下があるものの、おおむねRTTが80[ms]程度でパケットのやり取りが行われていると考えられる。そこで、RTTを80[ms]として計算を行う。
1パケットの送受信で約80[ms]かかるので、
となり、1秒間で概ね12パケット程度のやり取りが行われている計算になる。ここで、1パケットあたり128バイトであり、IP/TCPヘッダが40バイトであるので、ペイロード部分は88バイトである。したがって12パケットで
と求められるので、計測された値は適当であったと考えられる。
本研究では、AVRマイコンとXBeeにおいて、効率的なファイル転送を行うための基礎として、µIPによるTCP/IP通信を用いる手法を提案した。実際のプロトコルを動作させるには至らなかったが、AVRマイコン上でTCP/IP通信をXBee(ZigBee)によるネットワーク上で行えたと考えられる。これにより、既存のTCP/IPを使用したアプリケーション層の資産を活かしつつ通信が可能となる。既存のIPerfというアプリケーションのサーバサイドの動作を実現できた結果から、IPerfに限らずとも、他のプロトコルを動作させ応用的な利用をし得ると考えられる。
また、IPerfによる計測より、シリアル通信フロー制御こそ必要であるが115200[bps]といった高いボーレートでは10[kbps]程度の帯域幅があると分かった。この計測上の数値によるが、10[kbps]は1.25[KB/s]程度となるため、2バイト文字換算で約500文字程度の送受信が行えると考えられる。
今後の課題は、これらTCP/IP通信の多デバイス対応と帯域幅向上・プロトコル処理の実現性が上げられる。本研究では、ZigBeeネットワークの動作モードとしてATモードを使用した。しかしながら、ATモードでは1対1での通信が主となってしまうため、センサネットワークを構築して利用するZigBeeの利点を生かし切れていない。他デバイス対応という観点ではAPIモードが理想的ではあるが、ZigBeeAPIパケットサイズが128バイトであるなど、Ethernet等の物理層と比較してリソース不足が否めない。センサネットワークとして利活用していくためには、この点を改善していくことが一つの課題となる。
帯域幅の向上については、長いパケットサイズに耐え得る性能を持つマイコンを使用するなどの工夫が必要である。特にプログラムメモリサイズ・RAM容量が重要である。今回実現するに至らなかったTFTP(Trivial File Transfer Protocol)は、512バイトのパケットサイズを基本にデータの送受信を行う。今回使用したATmega328Pは、プログラムメモリこそ潤沢ではあるがRAM容量が2[KB]と小さい。そこにµIPバッファ、シリアル送受信バッファをそれぞれ512バイト用意するだけでRAMの75%を使用してしまう。この点は、上位のマイコンを利用するなどして改善を図るべきである。 性能に余裕のあるマイコンを利用することは、センサネットワーク全体のコストが増加することにつながるが、それだけプロトコル処理の実現性向上につながるだろうと考えられる。
上記に示した課題を解決し、実際にセンサネットワーク上でプロトコルを用いたデータの転送を行えるように改善していきたい。
図21 µIPを構成するプログラムの全体図
avr向けに調整されたµIPとUARTライブラリは以下のページにて公開されている。
https://github.com/avr-uip/avr-uip
今回の研究にあたり追加した部分は、
の主に3フォルダである。
以下に、本研究にあたりµIPへ追加したプログラムを示す。
他に「usart_config.h」、「usart.h」、「usart.c」というAVR向けシリアル通信ライブラリがこのフォルダに含まれる。これらは https://github.com/jnk0le/AVR-UART-libを利用したものである。
次のように使用する
sudo sh SLIP_attach.sh -s 57600 -p /dev/ttyACM0
-sオプションでは使用したいボーレートを選択する。-pオプションでは接続されているポートを選択する。slattachコマンドの利用にsudoが必要となるので、シェルスクリプト実行時にsudoを付ける。
以下に本研究において計測したping・IPerfの結果のコンソールログを示す。