2014年1月28日火曜日

ArduinoをWindowsにつないでGmailの未読メールの数を表示する (2)

前回記事
の続き.前回の記事ではlibcurlを使って,未読メール一覧を取得してchar型配列に格納するところまでやった.今回の記事では,文字列処理を行って未読メール数を取得するところまでをみていく.

以下にソースを示す.

#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>

#include <curl/curl.h>
#include <string.h>

typedef struct
{
    char* memory;
    size_t size;
} Memory;

size_t write_memory_callback(void* ptr, size_t size, size_t nmemb, void* data)
{
    if (size * nmemb == 0){
        return 0;
 }
    size_t realsize = size * nmemb;
    Memory* mem = (Memory*)data;
    
    mem->memory = (char*)realloc(mem->memory, mem->size + realsize + 1);
    if (mem->memory) {
        memcpy(&(mem->memory[mem->size]), ptr, realsize);
        mem->size += realsize;
        mem->memory[mem->size] = 0;
    }

    return realsize;
}

int main(void)
{
    CURL *curl;
    CURLcode res;
    char* start;
    char* end;
    //未読数が囲まれているタグを定義
    char start_str[] = "<fullcount>";
    char end_str[] = "</fullcount>";
    //タグ部分の切り出し
    char unread[100];
    Memory* mem = (Memory* )malloc(sizeof(Memory*));
    int startposition,endposition;
 int unreadNum = 0;
    
 while(1){
  mem->size = 0;
  mem->memory = NULL;
  curl = curl_easy_init();
 
  if(curl)
  {
       curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
       //ユーザ名・パスワード設定
       curl_easy_setopt(curl, CURLOPT_USERPWD, "username:password");
       curl_easy_setopt(curl, CURLOPT_URL,"https://mail.google.com/mail/feed/atom");
       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory_callback);
       curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)mem);

       res = curl_easy_perform(curl);

       //エラーチェック 
       if(res != CURLE_OK){
          fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
    }
       //クリーンアップ
       curl_easy_cleanup(curl);
  }

  start = strstr(mem->memory,start_str); 
  end = strstr(mem->memory,end_str);
  startposition = (int)(start-mem->memory);
  endposition = (int)(end-mem->memory);
  strncpy_s(unread, mem->memory+startposition,endposition-startposition);
  
  //数字の抜き出し
  int arrayIndex = 0;
  char unreadNumStr[10]={0};
  for(int i=0; i<strlen(unread); i++){
   if(isalnum((int)unread[i]) != 0){
    if(isalpha((int)unread[i]) == 0){
     unreadNumStr[arrayIndex] = unread[i];
     arrayIndex++;
    }
   }
  }

  unreadNum = atoi(unreadNumStr);
  printf("%d\n",unreadNum);
 }
    return 0;
}

簡単に説明すると,

1. strstr関数を使って,<fullcount>,</fullcount>を探し,そこから切り出しを始める.
 例えば,処理前の文字列が<fullcount>1</fullcount>asdfaklsufkashdf.....
だとすると,
startは<fullcount>1</fullcount>asdfaklsufkashdf.....
となり,
endは</fullcount>asdfaklsufkashdf.....
となる.この操作によって,欲しい文字列<fullcount>1</fullcount>の始まりと終わりのポインタがわかる.

2. 何文字目から何文字目までを切り出すのかを明確にする

3. strncpy_s関数を使って文字列を切り出す.
コピーのはじめと終わりの位置を指定してコピーすることで新しい文字列に目的の文字列を切り出します.

4. isalnum , isalpha関数を使って数字部分のみを取り出す.

5. atoi関数を使って文字列を数値に変換する.

という流れになっている.

2014年1月23日木曜日

ArduinoをWindowsにつないでGmailの未読メールの数を表示する (1)


Arduino Yún (http://arduino.cc/en/Main/ArduinoBoardYun)という,Wifi対応のLinuxを搭載したArduinoがある.そして,このArduino Yúnを使った作例に「Gmailに届くメールの数を表示」がある.この作例を普通のAruidnoを使って再現した.


なにをしたか
 以前書いた,
を組み合わせて,Gmailの未読メール数をSPI LED Moduleに表示した.どこに新しい(自分にとっての)技術勉強の要素があったかというと,C言語からGmailの未読メール数を取得することだった.実現にはlibcurlを使っている.

 ためしに,https://mail.google.com/mail/feed/atom にブラウザからアクセスすると,未読メールの一覧が表示される.このページをC言語で読み込み,文字列処理をすることによって未読メール数を取得する.といっても処理自体はかんたんで,未読メール数は<fullcount> </fullcount>というタグに囲まれているので,その部分を抜き出してやればよかった.


VisualStudio C++ 2012 でlibcurlを使う
この通りにやればいい.簡単に説明すると,
  1. http://curl.haxx.se/latest.cgi?curl=win32-ssl-devel-msvc からZipファイルをダウンロードし,好きなところに解凍する
  2. VisualStudioでプロジェクトを開き,解凍したlibcurlのIncludeディレクトリ,Libraryディレクトリを設定する.
  3. リンカー > 入力 > 追加の依存ファイルにcurllib.libを追記
  4. curllib.dll, libeay32.dll, openldap.dll, and ssleay32.dllをプロジェクトの適切なディレクトリに入れる

HTMLを取得して表示するプログラムは以下のようになる.

#include <stdio.h>
#include <curl/curl.h>

int main(void)
{
    CURL *curl;
    CURLcode res;
    
    curl = curl_easy_init();
    if(curl)
    {
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_easy_setopt(curl, CURLOPT_URL, "https://www.google.co.jp/");
        res = curl_easy_perform(curl);
        /* Check for errors */ 
        if(res != CURLE_OK)
        fprintf(stderr, "curl_easy_perform() failed: %s\n",
               curl_easy_strerror(res));
        /* always cleanup */ 
        curl_easy_cleanup(curl);
    }
 system("PAUSE");
    return 0;
}

このようにcurl_easy_performを使うと,printf等を使わなくても標準出力に表示されるが,結果を利用してなにかしたいときには,char型配列に保存しなければいけない.その部分は,
を参考にさせていただきました.結果,無事char型配列に保存することが出来ました.

今日はここまで.
次回は文字列処理をして未読メール数を取得するところまでを書きます.

2014年1月18日土曜日

秋月電子のSPI(3線式)LED Module 8 Digital を使う

こんにちは.
今日は研究室に転がっていた秋月電子のSPI(3線式)LED Module 8 Digital を使ってみました.
商品URL:http://akizukidenshi.com/catalog/g/gM-06681/

 使い方とサンプルプログラムがhttp://www.dfrobot.com/wiki/index.php?title=SPI_LED_Module_(SKU:DFR0090)#Pinout_Diagram に示されています.どうやらbyte配列を送信することで表示が可能となるようです.
 サンプルプログラムではシリアル通信を使うことが前提となっています.ですが時計を作ったり,Arduinoに付いているセンサの値を表示する時には数値データをそのまま表示したいですね.ですので今回は,8桁(負数は7桁)までの数値をこのモジュールで表示するサンプルプログラムを書きました.

以下ソースコード

//Pin connected to latch pin (ST_CP) of 74HC595
const int latchPin = 8;
//Pin connected to clock pin (SH_CP) of 74HC595
const int clockPin = 3;
//Pin connected to Data in (DS) of 74HC595
const int dataPin = 9;
//7SegLEDの数
const int ledArray = 8;

byte byteData[ledArray] = {0};
char strData[ledArray] = {0};

//表示させたい数字ledArrayの桁数以内
//8桁の場合-9999999から99999999
long number = 20140119;

void setup() {
  //set pins to output because they are addressed in the main loop
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);  
  pinMode(clockPin, OUTPUT);
}

void loop() {
 sprintf(strData, "%ld", number);
 //char型文字をbyte配列に置き換え、右揃えで表示できる形に整形
 setDataFormat(strData, byteData);
 
 digitalWrite(latchPin, LOW);
 for(int i=ledArray-1; i>=0; i--){
     shiftOut(dataPin, clockPin, MSBFIRST, byteData[i]);
 }
 digitalWrite(latchPin, HIGH);
 
}

void setDataFormat(char* strData,byte *byteData){
  int numofNull=0;
  // char型数字をbyteコードに置き換えはじめ
  for(int i=0; i<ledArray; i++){
    switch(strData[i]){
      case '\0':
        byteData[i]=0xff;
        numofNull++;
        break;
      case '0': 
        byteData[i]=0xc0;
        break;
      case '1':
        byteData[i]=0xf9;
        break;
      case '2':
        byteData[i]=0xa4;
        break;
      case '3':
        byteData[i]=0xb0;
        break;
      case '4':
        byteData[i]=0x99;
        break;
      case '5':
        byteData[i]=0x92;
        break;
      case '6':
        byteData[i]=0x82;
        break;
      case '7':
        byteData[i]=0xf8;
        break;
      case '8':
        byteData[i]=0x80;
        break;
      case '9':
        byteData[i]=0x90;
        break;
      case '-':
        byteData[i]=0xbf;
        break;
    }
  }
  // char型数字をbyteコードに置き換えおわり
  //配列の入れ替えはじめ
  for(int i=0; i<numofNull; i++){
    byte tmp[ledArray];
    int j;
    for(int i =0; i<ledArray; i++){
      tmp[i]=byteData[i];
    }
    for(int i =0; i<ledArray; i++){
      if(i==0){
        j = ledArray-1;
      }else{
        j = i-1;
      }
      byteData[i] = tmp[j];
    }
  }
  //配列の入れ替えおわり
}


今回のポイント
  1. 送信するbyte配列:byteDataを読み込ませる順番
  2. 表示の右揃え
  3. マイナスを表示
送信するbyte配列:byteDataを読み込ませる順番
 このモジュールでは読み込んだbyteデータを右から順番に並べていきます.例えば,20140119を配列の0番目から7番目まで読み込ませた場合,91104102と表示されます.これを解消するには配列の7番目から0番目に向かって読み込ませていけば大丈夫です.つまり,配列を右から読んで右から詰めるということです.整理整頓!(?)

表示の右揃え
 このプログラムでは数値データをsprintfという関数をつかって文字型数字配列に変換しています.ここでもしも,桁数が7桁の数値1234567を変換すると'1','2','3','4','5','6','7','\0'というようになります.これを表示すると
というようになります.数字データなのに右端が開くのは違和感があります.そこで,送信前に配列を入れ替えてすべての'\0'を左端に持ってくることで表示を右揃えにします.
具体的には82行目から94行目の部分です.まず,配列の複製を行います.そして入れ替えを行います.入れ替えでは,入れ替えれる側(元の配列)の先頭に\0を入れたいので,0番目の要素の時だけ,複製された配列の最後の要素つまり,7番目の要素を代入します.残りは一つづつ右にずらしていきます.これを\0の数だけ繰り返すとどんなに桁数が減っても右揃えして表示できるようになります.

マイナスを表示
 数字以外のアルファベットやハイフン、ピリオドなどを表示したくなった時どうしたらよいかです.サンプルプログラムでは文字型数字からバイトコードへの変換を40行目から79行目で行っています.処理は単純なswitch-case文です.このモジュールの場合,LEDの各セグメントと8ビットの関係は次の様になっています.例えば,ハイフンを表示したいときには10111111となります.
次に,得た10111111を16進数にします.自分で計算してもいいのですが,めんどくさい人は,http://www.binary-code.org/ のサイトを使いましょう.

主なポイントは以上です.
もっとよく知りたい方はこのモジュールを実現しているシフトレジスタ74HC595について調べたり,小数表示を実装してみたりするといいと思います.