はにゃぶろぐ はじめました

物欲情報とかTipsとかいろいろ載せていけたらいいなぁ

Intel Galileo / Arduino IDE 1.5.3 の EthernetUDP クラスで remoteIP(), remotePort() を有効にする対策

Intel GalileoLinuxの上に Arduino互換を実現しているのですが、
Arduino IDEの上でコンパイルしているものが、実は互換ライブラリとスタブをリンクしたLinuxアプリケーションという素敵仕様なのです。

ですから、Arduinoスケッチに見せかけて libcもほかのシステムコールでも使えます。

それを前提として、表題の問題を解決しましょう。

なお、現在配布されている Galileo用のArduino-1.5.3については動作を確認しています。

課題

そもそも、Galileo への portingで問題となってるのは、UDPパケットを受信するアプリケーションを作成した場合に、
送信元IPアドレス, 送信元ポート番号が取得できないということです。

原因はとても簡単な話で、Arduinoのシールドでは
parsePacket()すると、read()する前にヘッダを覗いて 内部メンバ変数を書き換えていますが、
socketインターフェイスを使って実装した Galileo でトリッキーな芸当はできなかったのです。

対策

parsePacket() で内部メンバを初期化して、read(unsigned char* buffer, size_t len)を呼び出した際に
recvfrom関数を使ってますが、ここで sockaddr_in 構造体を渡して書き込んでもらい、内部メンバを設定しただけです。

なにせ socket インターフェイスの話ですから、普通のunixアプリケーションだと思って考えるだけでいいわけですが、
read()でしか判定できないし、portingされた parsePacket() の処理内容は替えたくないので、

使用するコード側で、
parsePacket() と
read(unsigned char* buffer, size_t len) を呼び出した後で、
EthernetUDP::remoteIP(), remotePort() を呼び出してください。
そうすれば読み取れます。

修正すべきソース

arduino-1.5.3/hardware/arduino/libraries/Ethernet/EthernetUDP.cpp の read(unsigned char* buffer, size_t len) と parsePacket() です。

int EthernetUDP::read(unsigned char* buffer, size_t len)
{

	if (_remaining > 0) {

		int got;
		// quick hack for _remoteIP and _remotePort
		struct sockaddr_in _saddr;
		socklen_t _saddr_len;
		_saddr_len = sizeof (struct sockaddr_in);

		if (_remaining <= len) {
			// data should fit in the buffer
			got = recvfrom(_sock, buffer, _remaining, 0, (struct sockaddr *)&_saddr, &_saddr_len);
		} else {
			// too much data for the buffer,
			// grab as much as will fit
			got = recvfrom(_sock, buffer, len, 0, (struct sockaddr *)&_saddr, &_saddr_len);
		}

		if (got > 0) {
                	// set _remoteIP and _remotePort 
			_remoteIP = ntohl(_saddr.sin_addr.s_addr);
			_remotePort = ntohs(_saddr.sin_port);

			_remaining -= got;
			return got;
		}

	}

	// If we get here, there's no data available or recv failed
	return -1;

}

同じソース内の parsePacketも修正します。

int EthernetUDP::parsePacket()
{
	// discard any remaining bytes in the last packet
	flush();
	_remaining = 0;
	_remaining = available();

	// reinitialize _remoteIP, _remotePort
	_remoteIP = INADDR_NONE;
	_remotePort = 0;

	return _remaining;
}