Intel Galileo / Arduino IDE 1.5.3 の EthernetUDP クラスで remoteIP(), remotePort() を有効にする対策
Intel Galileo は Linuxの上に 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; }