最終更新日: 2010年4月25日

針式為替計(直接アクセス版)

「円計」は、別途用意した自宅サーバーがインターネットから情報を取得、Arduinoは自宅サーバーにアクセスして為替データを得るものでしたが、できれば直接Arduinoがスタンドアロンでインターネットから情報を得たほうが面白い…ということで、DNSクライアントで動作確認。

仕様

ArduinoのDNSクライアントはいくつかあるようですが、ArduinoサイトのPlaygroundページからリンクのあったgkaindlで公開されているライブラリを使用してみました。Bonjour, DHCP, DNSがそれぞれ別のライブラリになっているので使いやすそうです。

製作

このEthernetDNSライブラリでIPアドレスを引けるものの、標準のEthernetライブラリのClientクラスにはコンストラクタ以外にサーバーのIPアドレスを指定する関数が用意されていません。ということでまずは標準のEthernetライブラリを修正。と言っても単にIPアドレス、ポートを設定するコマンドを追加するだけです。
ま、C++的には、アドレスが決まった時点でインスタンスを作るという事でしょうけど、この程度の事でnewとかdeleteとかするのもアレなので。

Client.cpp
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に入れ替えました。

Sketch
#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」という文字列を受信すると検出できません。まぁ、あえて汎用性を求める意味も無いので。

という事で、これでとりあえずスタンドアロンで為替レートを得ることができました。



home | Elec top

inserted by FC2 system