針式為替計(直接アクセス版)
「円計」は、別途用意した自宅サーバーがインターネットから情報を取得、Arduinoは自宅サーバーにアクセスして為替データを得るものでしたが、できれば直接Arduinoがスタンドアロンでインターネットから情報を得たほうが面白い…ということで、DNSクライアントで動作確認。
仕様
ArduinoのDNSクライアントはいくつかあるようですが、ArduinoサイトのPlaygroundページからリンクのあったgkaindlで公開されているライブラリを使用してみました。Bonjour, DHCP, DNSがそれぞれ別のライブラリになっているので使いやすそうです。
製作
このEthernetDNSライブラリでIPアドレスを引けるものの、標準のEthernetライブラリのClientクラスにはコンストラクタ以外にサーバーのIPアドレスを指定する関数が用意されていません。ということでまずは標準のEthernetライブラリを修正。と言っても単にIPアドレス、ポートを設定するコマンドを追加するだけです。
ま、C++的には、アドレスが決まった時点でインスタンスを作るという事でしょうけど、この程度の事でnewとかdeleteとかするのもアレなので。
void Client::setServer(uint8_t *ip, uint16_t port) {
_ip = ip;
_port = port;
_sock = 255;
}
もちろんClient.hにも、 void setServer(uint8_t *, uint16_t); を追加しておきます。
あとは作るだけですが、このDNSクライアントは引いた結果としてcanonical nameを返されるとダメなようです。仕方が無いのであらかじめcanonical nameを解決しておき、それをソースの中に入れてしまいました。ちょっとインチキです。
コンパイルすると結構サイズが大きくなりますので、デバイスはATMega328に入れ替えました。
#include <Ethernet.h> #include <EthernetDNS.h> #include <Servo.h> Servo myServo; #define ETH_BUFF_MAX 127 byte mac[] = { 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- }; ←Macアドレス byte ip[] = { 192, 168, 0, 16 }; ←自機のIPアドレス byte gateway[] = { 192, 168, 0, 1 }; byte subnet[] = { 255, 255, 255, 0 }; uint8_t server[] = { 0,0,0,0 }; byte dnsServerIp[] = { 192, 168, 0, 1}; ←DNSサーバーのアドレス char read_buff[ETH_BUFF_MAX]; char buff[24]; Client client(server,80); // Ethernetから1バイト受信する char getCharFromEthernet(Client theClient) { while(1) { if(theClient.available()) { return(theClient.read()); } if(!theClient.connected()) { return 0; } } } // Ethernetからmaxlenバイトを受信し、*strに保存 int getStrFromEthernet(Client theClient, char *str, int maxlen) { int p = 0; char c; while(c = getCharFromEthernet(theClient)) { str[p++] = c; if(p >= maxlen) { str[p] = 0; return p; } } str[p] = 0; return p; } // Ethernetから、*tstrで示された文字列を受信するまで受信する int seekStrFromEthernet(Client theClient, char *tstr) { char c; int tp = 0; while(c = getCharFromEthernet(theClient)) { if(tstr[tp++] == c) { if(tstr[tp] == NULL) { return 1; } } else { tp = 0; } } return 0; } int getRate(Client theClient) { int len; int rcode; int rate,rate0; char buff[16]; // get responce code len = getStrFromEthernet(theClient,buff,15); if(len < 0) return -1; // check ressponce code sscanf(read_buff,"%*s %d",&rcode); if(!(rcode != 200)) return -1; // read target contents if (!seekStrFromEthernet(theClient,"<span id=\"yfs_l10_usdjpy=x\">")) { return 0; } if (!getStrFromEthernet(theClient,buff,15)) { return 0; } sscanf(buff,"%d.%03d",&rate,&rate0); rate0 += 5; rate0 /= 10; rate = rate * 100 + rate0; return rate; } int getServerAddress() { static char buf[16]; DNSError err = EthernetDNS.resolveHostName("finance.fy6.b.yahoo.com",server); if (err == DNSSuccess) { client.setServer(server,80); return 1; } else { server[0] = 0; return 0; } } void setup() { Ethernet.begin(mac, ip, gateway, subnet); EthernetDNS.setDNSServer(dnsServerIp); myServo.attach(2); } void loop() { int rate; int angle; int res; res = 1; do { // アドレスが未解決であれば調べる if(server[0] == 0) { if(!getServerAddress()) { break; } } if (client.connect()) { client.println("GET finance.yahoo.com/q?s=usdjpy=X HTTP/1.0"); client.println(); } else { server[0] = 0; break; } rate = getRate(client); if(rate <= 0) break; angle = 180- (((float)(rate - 8900) / 300) * 120 + 30); myServo.write(angle); } while(0); client.stop(); delay(10000); }
seekStrFromEthernet()はEthernetから次々に受信し、指定された文字列を受信するまで空読みする関数です。ここでコンテンツの中にある為替レートのデータの場所を探していますが、この関数は汎用的に言えば完璧ではありません。後戻りして検索をしないので、例えば「abc」という文字列を検索する時に「ababc」という文字列を受信すると検出できません。まぁ、あえて汎用性を求める意味も無いので。
という事で、これでとりあえずスタンドアロンで為替レートを得ることができました。