Ethernet shield メール受信…か?

キャンプに行く予定はないですが....

前回Ardunoからメールを打つことができましたので、今回はメールの受信です。通常のメールソフトみたいな物はとても作る技術がないので、いま何通メールが届いているかシリアルモニタに表示させてみましょう。

前回、Telnetを使いSMTPサーバにアクセスしました。今回も前回と同様にArduino君にやってもらう前に、手動でメール(POP3)サーバにアクセスして試してみます。なおPOP3コマンドはwww.atmarkit.co.jpさんのWebに詳しく書いてあります。

[POP3 サーバにtelnetで接続してメールを読み込む]

telnet POP3サーバアドレス 110  

Connected to mail.hogehoge.co.jp.
Escape character is '^]'.
+OK Hello there. <24680.1326193375@mail.hogehoge.co.jp>

USER ユーザー名 

+OK Password required.

PASS メールパスワード 

+OK logged in.

STAT                   ;メールの数とサイズを尋ねる。 

+OK 2 2632            ;2通あることがわかります。 

RETR n                 ;n番目のメールを表示する。 
 
+OK message follows 
 Return-Path: baz@foobar.co.jp
Received: from deredere006.hogehoge.co.jp (LHLO deredere006.hogehoge.co.jp)
 (118.23.178.10) by daradara001.hogehoge.co.jp with LMTP; Tue, 10 Jan 2012
 19:23:01 +0900 (JST)

        ・・・ 以下内容表示 ・・・
        
QUIT                   ;通信終了 

+OK Bye-bye.
Connection closed by foreign host.

私のプロバイダのPOP3サーバでは、このようなやり取りになりました。黒字はPOP3サーバが表示した文字ですが、POP3サーバの種類によって異なる場合があります。さてさて手動では問題なくつながりました。でも、これからどうしたものでしょう。今回も諸先輩の知恵をお借りしました。

そこで、ようやくみつけたのがこのMy Open Source Projects さんのArduino POP3 Email Checker  です。(Thx Mr. Torchris!!)

こちらの作品は、LEDを使ってメールの数を知らせていますね。さすがです。でも、私は当初の予定通りシリアルモニタに表示させるだけにして置きましょう。実を言うと、ここにたどり着くまでに精魂使い果たしてしまいました。もう、余力がありません。ほんとです。

こちらに書かれていたコードを見て、おぼろげながら分かってきました。POP3サーバから表示されるデータは全部POP3コマンドを送信してから取り込めばよいみたいです。この参考スケッチではQUIT(終了)まで指示を行った後にデータを取り込んでいます。

だいたい理解できました、この作者が参考にしたお手本を元にしてスケッチを作ります。ほぼそのまんまです。

/* 
  Simple POP3 Email Checker
  Arduino 1.0 version
 */
  
#include <Ethernet.h>
#include <SPI.h>
 

 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
 byte ip[] = { 192, 168, xxx, xxx };     // Arduino EtherShield IP address
 byte gateway[] = { 192,168, xxx, xxx };  // Default Gateway IP address
 byte subnet[] = { 255, 255, 255, 0 };  // Subnet Mask
 
 byte server[] = { xxx,xxx,xxx,xxx };    // POP3 server address
 
 EthernetClient client;
 long updateTimer;
 boolean clientConnected = false;

void setup()
{
   Serial.begin(9600);
}
 
void loop()
{
  if ((millis() - updateTimer) > 10000)
  {
    Ethernet.begin(mac, ip ,subnet,gateway ); //Arduino 1.0 see http://arduino.cc/forum/index.php?topic=83800.0
    
    delay(1000);
 
    Serial.println("connecting...");
 
    if (client.connect(server, 110))        //POP3 port 110
    {
       Serial.println("connected");
       client.println("user  ユーザー名");
       client.println("pass  パスワード");
       client.println("stat");
       client.println("retr 1");
       client.println("QUIT"); 
       clientConnected = true;       
                                                   // **** (2) **** 
     } else {
       Serial.println("connection failed");
     }
     updateTimer = millis();
  }

  if (clientConnected)
  {
     if (client.available()) 
     {                    
       char c = client.read();                    // ***** (1) *****
       Serial.print(c);                           // ***** (1) *****
      }

      if (!client.connected())
      {
      Serial.println("disconnecting.");
      client.stop();
      clientConnected = false;
     }  
   } 
}
 

まず試しに、上記のスケッチでメールの数と、1番目のメールを表示させることができました。

これで、サーバから受け取ったすべての文字の表示をすることができました。さて、STATで表示されるメール数を取り出すにはどうすればよいでしょうか?

こちらもいろいろ検索してわかりました。find()関数parseInt()関数を使って取り出します。先程の例で、サーバから送られてくる文字列は以下の通りです


Connected to mail.hogehoge.co.jp.
Escape character is '^]'.
+OK Hello there. <24680.1326193375@mail.hogehoge.co.jp>
+OK Password required.
+OK logged in.
+OK 2 2632               ;この2をゲットしたい

・・・

ストリームクラスで使うfind()関数ですが、この場合ちょっとだけ工夫が必要です。4番目の”+OK”にヒットさせてparseInt()関数でメールの数を取り込みますが、今回は同じ文字列が複数あるので2段階にしなければなりません。


       if ( client.find("+OK logged in."))
       {
         client.find("+OK ");
         int count = client.parseInt(); 
         Serial.print(" Your mail = ");
         Serial.println(count);       
       }

(****** (1) ***** の場所を書き換えます)

これで良いはずだと思っていたのですが実際動かしてみると、うまくいきませんでした。結局のところ、理由不明ですがfind()関数を使う前にディレイを入れないと動かない事が分かりました。 苦労をしたので、この事を発見した時は少し涙が出てきてしまいました。この事象はArduino 1.0 に限った、特有の事かもしれません。

    delay(1000);       //この数値が適切か否かはわかりませんが、必要でした

(****** (2) ***** の場所に書き加えます)

ようやく完成しました。(・・・・と、喜んでいたのですが、しばらく動かしていると止まってしまいます。残念。原因が分かりません。前途多難です。)