2011/12/20

小型液晶ディスプレイ e-DispをArduinoで使う

MTM07に行った際、桜パーツという北海道のお店が出店していて、
シリアル通信で制御できる液晶ディスプレイが売っていました。

e-Disp3
値段は4,500円とホイホイ買える値段ではないのですが、1000円ぐらいで売っているLCDディスプレイよりもかなり多くの情報量を出せそうなこと、カラー表示が可能なこと、SDカードもおまけで付いてくる、ということなので買ってしまいました。

ソフトウェアマニュアルなどを読んだところ、
  • 日本語が表示できる(文字コードはUTF-8,SJIS,EUCJPに対応)
  • フォントサイズを3段階で切り替え可能(1画面内で異なるフォントサイズは不可)
  • 文字色/背景色は黒、白、青、赤、黄色、緑、水色、紫から選択可能
  • マイクロSDカードにあらかじめ保存しておいた画像を、ファイル名を指定して表示することができる(320x240サイズにリサイズしておいた方がよい)
  • 背景バッファは4面分ある。それぞれに画像を読み込むことも可能
  • 長方形、楕円、ドット、1ピクセル太さの線を描画できる
ということがわかりました。
文字の色などを制御するには”エスケープシーケンス”と呼ばれる特殊コードをそれぞれ入力する必要がありますが、エスケープシーケンスを毎回出力するのは面倒なので、Arduino用のライブラリを作ってみました。

ソフトウェアマニュアルに記載されているエスケープシーケンスの8割ぐらいはカバーしているので、ある程度の役には立つと思います。

ライブラリは別館に保存してますので、必要な方はご自由にどうぞ。

ではまた。

2011/12/15

ArduinoでGPSモジュールのNMEAメッセージを解析する

ママチャリグランプリ用に購入したGPSモジュール(GT-723F)ですが、生のデータは数字の羅列だけなので、人間が読むには厳しい状態となっています。また、いろいろな種類の行が出てくるので、必要なところだけフィルタリングする必要があります。
そこで、ArduinoでGPSモジュールの出力を解析してみました。

NMEAデータにはいろいろな行がありますが、時間と緯度・経度の情報を含んでいるGPGGA行のみ取り出して処理することにしました。
カンマ区切りなのでプログラム自体はそんなに難しくはなかったのですが、肝心の緯度・経度が現在地と大幅に違うところを指していました。

調べてみたところ、google mapに表示するためには座標を少し変換してあげなければならないようです。
下記ページを元に座標変換を行うと、見事に現在地がgoogle mapに表示されました。

GT-730F/LとNMEAデータから得られる経度緯度

次はXBee Proの通信距離の実験です。
土曜日晴れたら見通しのよい公園などで実験してみたいと思います。




/*
 GT-723から取り出したNMEAデータを解析するプログラム
 緯度・経度を取り出します。
 arduino用。
 2011-12 lhaplus 8888
 This code is in the public domain.
 */

//シリアル接続開始してバッファ初期化
void setup()
{
  Serial.begin(9600);
  ClearBuffer;
}

//バッファなど
const int arrayLength=128;  //max buffer size.
char gpsRecvArray[arrayLength];

//GPGGAフィールド定義
const byte C_UTC=1;  //UTC時間フィールド
const byte C_LATITUDE=2;//緯度
const byte C_LATITUDE_HEMISPHERE=3;//北緯・南緯フィールド
const byte C_LONGITUDE=4;
const byte C_LONGITUDE_HEMISPHERE=5;
const byte C_QUALITY=6;  //品質
const byte C_NUMOFSAT=7;//衛星の数
const byte C_PRECISION=8;//精度?
const byte C_ANTENNA_HEIGHT=9;
const byte C_GEOIDAL_HEIGHT=11;
const byte C_NORTH='N';
const byte C_SOUTH='S';
const byte C_EAST='E';
const byte C_WEST='W';
int indexLoc=0;
 
void loop() 
{
  char recvChar=0;//受信バッファ
  while(Serial.available())
  {
    recvChar=Serial.read();
    //check first $ mark.
   if((36==recvChar))
    {
      //start gps log line.
      //clear all buffer.
      ClearBuffer();
      indexLoc=0;
    }

    if(10==recvChar)
    {
      Serial.println("Receive LF");
      //check gps log buffer.
      CheckGPS();
      break;
    }
    else
    {
      //copy to buffer
      gpsRecvArray[indexLoc]=recvChar;
      indexLoc++;
      //限界超えたらバッファクリア
      if(indexLoc>=arrayLength)ClearBuffer();

    }
  }  

  delay(10);

}

//受信バッファのクリア
void ClearBuffer()
{
  memset(gpsRecvArray,0,sizeof(gpsRecvArray));
  Serial.println("Buffer cleared.");
}


//check GPS char.
void CheckGPS()
{
  /*  
   GT-723FのNMEA出力の中で、緯度・経度に関係しているのはGPGGAの行。
   フォーマット: $GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,M,<10>,M,<11>,<12>,*<13><CR><LF> 
    意味:
       1  104549.04  UTC time in hhmmss.ss format, 000000.00 ~ 235959.99 
       2  2447.2038  Latitude(緯度)  ddmm.mmmm format 読み取り用の0が入れられる場合もあり
       3  N  Latitude hemisphere indicator, 窶朦窶・= North, 窶朶窶・= South 
       4  12100.4990  Longitude(経度) dddmm.mmmm format 読み取り用の0が入れられる場合もあり
       5  E  Longitude hemisphere indicator, 'E' = East, 'W' = West 
       6  1  Position fix quality indicator 
           0: position fix unavailable 
           1: valid position fix, SPS mode  
           2: valid position fix, differential GPS mode 
       7  06  Number of satellites in use, 00 ~ 12 
       8  01.7  Horizontal dilution of precision, 00.0 ~ 99.9 
       9  00078.8  Antenna height above/below mean sea level, -9999.9 ~ 17999.9     
       10  0016.3  Geoidal height, -999.9 ~ 9999.9 
       11  Age of DGPS data since last valid RTCM transmission in xxx format (seconds) NULL when DGPS not used 
       12  Differential reference station ID, 0000 ~ 1023 NULL when DGPS not used 
       13  5C  Checksum
       //時刻、緯度、経度を取ってくるには、1,2,4のフィールドを回収すればOK
   */

  //まずはGPGGAの行かをチェック。
  if(('G'==gpsRecvArray[4])&&('A'==gpsRecvArray[5]))
  {

      Serial.println("GPGGA DATA.Start process");

    //受信バッファを1個づつ探していく
    int charIndex=0;
    int colNumber=0;//列番号。取説にあわせて1から始める
    int colCharIdx=0;//対象列の文字配列のインデックス

    int i=0;
    //フィールドごとに区切っていく
    //とりあえず受信バッファ 20文字。
    char buff[20];
    memset(buff,0,sizeof(buff));


    double degreeLAT;
    double degreeLONG;
    char c=0;    
    for(charIndex=0;charIndex<arrayLength;charIndex++)
    {
      c=gpsRecvArray[charIndex];
      //NULL文字にあたったら終了
      if(0==c) break;

      if(44==c)//区切り文字の カンマ
      {
        //列切り替え
        //切り替える前に、今の行バッファを出力しておく

        switch(colNumber)
        {
        case C_UTC://UTC時間フィールド
          Serial.print("UTC:");
          Serial.println(buff);
          break;  
        case C_LATITUDE://緯度
          Serial.print("LAT:");
          degreeLAT=DMmtoDegree(buff,sizeof(buff));
          Serial.println(degreeLAT,8);
          break;
        case C_LATITUDE_HEMISPHERE://北緯・南緯フィールド
          Serial.print("N/S:");
          break;
        case  C_LONGITUDE:
          Serial.print("LONG:");
          degreeLONG=DMmtoDegree(buff,sizeof(buff));
          Serial.println(degreeLONG,8);
          break;
        case C_NUMOFSAT://衛星の数
          Serial.print("Num of SAT:");
          Serial.println(buff);
          break;


          default:break;
        }
        colNumber++;
        colCharIdx=0;
        memset(buff,0,sizeof(buff));
        //次の行へ切り替える。文字インデックスも初期化
      }
      else
      {
        buff[colCharIdx]=c;
        colCharIdx++;
      }
    }
  }
  else
  {
         Serial.println("NOT GPGGA DATA");
         Serial.println(gpsRecvArray);
         
  }
}


//GT-723はDMM形式というものになっている模様。
//GoogleMapに表示するにはDegree形式にしなければならないようです。
//参考:http://gps.meblog.biz/article/2761601.html
//Charの配列から、degree表示に変換する。
//2447.2038 とか
//12100.4990 の形式。
//Dの部分が3桁か2桁、
//Mの部分は2桁固定、
//mの部分は4桁固定。
double DMmtoDegree(const char* buff,const int bufflen)
{
  int d=0;
  int m=0;
  int s=0;

  double tmp=0;
  int i;
  int period;
  char atoibuff[4];
  double value=0;

  period=-1;
  for (i=0;i<bufflen;i++)
  {
    //まずはピリオドを探す。
    if('.'==buff[i])
      period=i;
  }

  if(period<0)
  {  
    return value;
  }
  else
  {
    //ピリオドの場所がわかったので、コピーしてく
    //dの部分。
    memset(atoibuff,0,sizeof(atoibuff));
    for(i=0;i<period-2;i++)
    {
      atoibuff[i]=buff[i];   
    }
    d=atoi(atoibuff);

    //mの部分。
    memset(atoibuff,0,sizeof(atoibuff));
    for(i=0;i<4;i++)
    {
      if(buff[period-2+i]=='.') break;
      atoibuff[i]=buff[period-2+i];
    }
    m=atoi(atoibuff);
    //Serial.println(*m);
    memset(atoibuff,0,sizeof(atoibuff));
    for(i=0;i<4;i++)
    {
      atoibuff[i]=buff[period+1+i];
    }
    s=atoi(atoibuff);
    value=(double)d;
    value=value+(double)m/60;
   //小数点部分。  
   tmp=(double)s/(double)60;
   tmp=tmp*0.0001;
   value=value+tmp;
  } 
  return value;
}

ではまた。

2011/12/10

ママチャリグランプリ用GPSシステムの構築

来年1月の上旬に、スーパーママチャリグランプリが富士スピードウェイで開催されます。
今年も友人たちと参加する予定です。

レース中は1〜2周おきにドライバーを交代するのですが、ドライバーがどこを走っているかわからないため、交代準備などの時間がなかなか読めません。ピットロードは混雑しているので、なるべくピットロードでの待ち時間を減らしたいところです。
去年はAndroid搭載の携帯を2個使って場所把握を行いそこそこ役に立ったのですが、電波状態が安定せず、現在地を受信できないこともありました。

そこで今年は携帯電話を使用するのではなく、XBee Proを使って通信を行うことにしました。
XBee Proの見通し通信距離は1500m程あるので、レーシングコース全体はカバーできる計算です。

今回のママチャリグランプリ用のGPSシステムのハードウェア構成は以下のとおりです。

送信側

  • Xbee Pro ZB (End device構成)
  • GPSモジュール(GT-723F)
  • DC-DCコンバータ CE-1004-TP (3.3V出力設定)
  • OpenLog+2GB MicroSDカード
  • Eneloop 6本直列


受信側

  • Arduino
  • XBee Pro ZB (Coodinator構成)
  • レースコース上にLEDを配置した基板
  • モバイルブースターなどの5V電源

システムの概要

  • GPSモジュールの出力をXBeeのVInとOpenLogのRxIに接続。
  • 自転車側XBeeからピット側XBeeに向けて、GPSモジュールの出力(NMEA形式)をそのまま送信。
  • ピット側のXBeeにつながっているArduinoがNMEAを解釈し、自転車の位置に一番近いLEDを光らせる。
  • 大体の場所がわかるので、ピットでのんびりできる


GPSモジュールの予備実験を家の周りでしたところ、10~20mぐらいの誤差内で捕捉できることを確認できました。GPSモジュールはOK。
OpenLogの実験もやってみたところ、GPSモジュールの生データを10分間記録するとデータ量が大体300kBだったので、電池が切れなければ2GBのMicroSDに1000時間ぐらい記録できそうです。なのでOpenLogもOK。

残る予備実験はXBeeの長距離通信と駆動時間の実験です。

2011/12/07

XBee Product Manualのまとめ

先週末にあったMTM07でXBee Pro ZB(Programmableじゃないほう)と変換基盤を買ってきました。
Arduinoとつなげて遊ぶにも資料が無いと厳しいので、Digi.comから「Product Manual: XBee / XBee-PRO ZB RF Modules」をダウンロードして読んでみました。

XBee Product Manualのまとめ

ハードウェア
 Xbee/XBee-PRO(日本用) の比較
通信距離(屋内/壁有り) 40m/60m
通信距離(見通し)   120m/1500m
出力   2mW/10mW
通信速度  最大1Mbps/最大1Mbps
電源電圧 2.1~3.6V/3.0~3.4V
値段  1,700円/3,000円

モードについて
透過モード
デフォルトのモード。シリアルケーブルと等価のものとして動作します。DINに入ってきたデータを電波で送信します。また、受信下データはDOUTに出力されます。

APIモード
 
TCP/IPのように使えるモード。複数の送信先を切り替えたり、受信したデータの送信元を判断したいときに使用します。

デバイスタイプについて
Coordinator
ZigBeeネットワークの親玉的な役割をします。一つのネットワークに、必ずCoodinatorが一つ必要です。PCでいうとDHCPサーバ?
PAN ID(ネットワークの識別番号)を指定します。
RouterとEnd devicesをネットワークに参加することを許可することができます。
データのルーティングを補助することができます。
Sleepモードは使用できません

Router
まずはZigBeeネットワークに参加する必要があります。
ネットワーク参加後は、RouterやEnd devicesをネットワークに参加させることができます。
データのルーティングを補助することができます。
Sleepモードは使用できません。

End device
まずはZigBeeネットワークに参加する必要があります。
ZigBeeネットワークの一番外側にいるデバイスとなります。
親デバイス(Coordinator/Router)との間でのみ通信することができます。
データのルーティングは行えません。
低消費電力モードが使えます。

上記のデバイスタイプを切り替えるには、X-CTUというWindows用のソフトを使用し、対象のファームウェアを書き込む必要があります。なので遅くてもいいのでWindows機が必要となります。

その他
PAN ID:ネットワークの識別番号。64ビットまたは16ビットの識別番号が使えます。PANIDが同じものは同じネットワークに所属します。16ビットでも6万5千通りのPAN IDが使えますが、64ビットを使用したほうがPAN IDが重複しにくいため、64ビットを使うことが推奨されます。
64ビットデバイスアドレス:XBee1つずつに固有のアドレス。PCのMACアドレスに相当します。
16ビットデバイスアドレス:ZigBeeネットワーク内のアドレス。PCだとIPアドレスに相当します。

ATコマンド 使いそうなやつ一覧
ATDH:Destination Address High. 宛先アドレス(64ビット)の上位32ビット。
ATDL:宛先アドレスの下位32ビット。ATDHとATDLの組み合わせには、2つの特殊なアドレスがあります。(0x0000->Coordinatior, 0xFFFF->BroadCast)
ATMY:16ビットネットワークアドレス。現在参加しているZigBeeネットワーク内でのアドレスです。特殊なアドレスは0xFFFEで、この場合はどのネットワークにも参加していないことになります。
ATMP:16ビット親ネットワークアドレス。親デバイスのZigBeeネットワークアドレスを指します。0xFFFEの場合は、親がないことを意味します。
ATNC:Number of Remaining Children。あと何台の子デバイスをネットワークに追加できるかを示します。0が返ってくると、これ以上追加することができません。読み取り専用。
ATSH:Serial Number High。 端末のシリアルナンバーの上位32ビット。読み取り専用
ATSL:Serial Number Low。端末のシリアルナンバー下位32ビット。読み取り専用。
ATNI:Node Identifier。ノードの名前。ASCIIコードで20文字まで設定可能。
ATCH:Operating Channel。通信で使用しているチャンネル番号を表します。読み取り専用。
ATID:Extended PAN ID。 64ビットのPAN ID。0を設定したら、CoordinatorがランダムなIDを使用します。
ATOP:Operating Extended PAN ID。現在使用中のPAN IDを表します。読み取り専用。IDが1以上であれば、IDと同じになります。
ATOI:Operating 16-bit PAN ID。16ビットの PAN IDを返します。
ATPL:Power Level。電波強度を変更することができます。ただし、日本国内で使えるバージョンでは変更できません。
ATDB:Received Signal Strength。受信した信号の強度を出力します(最後のHopのみ評価対象)
ATAP:API Enabe。 APIモードを有効にします。
ATAO:API Options。 APIモードのオプションを変更/確認します。
ATBD:Interface Data Rate。 シリアルポートの通信速度を設定します。デフォルトは9600bps。
ATNB:Serial Parity。 シリアル通信のパリティを変更/確認します。デフォルトはパリティなし。
ATSB:Stop Bits。 シリアル通信のストップビットを変更/確認します。デフォルトは1ストップビット
ATV+:Voltage Supply Monitoring。供給電圧を読み取ります。
ATTP:Module temperature。精度+-7℃で端末の温度を読み取ることができます。
ATVR:Firmware version。ファームウェアのバージョンを表示します。
ATHV:Hardware version。モジュールのバージョンを表示します。
ATCT:Command mode timeout。コマンドモードになっているときに、何も入力がなかったらIdlモードに戻る時間。100ms単位。デフォルトは100(=10秒)
ATCN:Exit Command mode。コマンドモードを終了します。

ATGT:Guard Times. ATコマンドモードに入るためのガードタイムを確認/変更します。
ATCC:Command Sequence Character。コマンドモードに入るための文字を指定します。デフォルトは'+'。
ATSM:Sleep Mode。スリープモードの設定を確認/変更できます。
ATAC:Apply Changes。ATコマンドモードで設定した変更内容をすぐに反映させます。
ATWR:Write。不揮発性メモリに設定を書き込みます。ATWRコマンドを送った後は、'OK'が返ってくるまでなにも文字を送らないほうがよいです。
ATRE:Restore Defaults。設定を工場出荷状態に戻します。
ATFR:Software Reset。モジュールをリセットします。'OK'が直ちに返り、2秒程度で端末がリセットされます。
ATNR:Network Reset。PANのネットワークパラメータをリセットします。 ATNR0でコマンドを送っている端末のみ、ATNR 1でネットワーク全体に対してリセットをPAN内にブロードキャストします。
ATSI:Sleep Immediately。このコマンドを発行すると、End deviceは直ちにスリープモードに入ります。


2011/12/01

コールバック関数のテスト

特定のタイミングで何らかの処理を行わせたいときに使用する、コールバック関数のテストコードを書いてみました。
コールバック関数の形式にしておけば、渡す関数を変えるだけで異なる処理ができるようになります。
pthreadの作成時や、signal関数を使うときも、このコールバック関数の方式がとられています。

//
//コールバック関数のテスト
//

#include <stdio.h>


void *callBackFunction(void *data)
{
        printf("コールバック関数が呼ばれました。\n何かの処理を行います。\n");
}

//コールバック関数を引数とする関数。
void somefunc(void* (*callbackFunc)(void*))
{
        printf("some process...\n");
        printf("some process2...\n");
    
        //コールバック関数を呼ぶ。
        callbackFunc(NULL);
    
        printf("finish somefunc\n");
}


int main(void)
{
        somefunc(callBackFunction);
        return -1;
}


実行例:

iMac:~ lhaplus8888$ ./a.out
some process...
some process2...
コールバック関数が呼ばれました。
何かの処理を行います。
finish somefunc
iMac:~ lhaplus8888 $ 

2011/11/24

zlibでメモリ上のデータを圧縮(IplImage)

ネットワーク通信の部分もそこそこできたので、
次はArduinoタンクで撮影した画像を転送する部分に取り掛かりたいと思います。

Arduinoタンク側のBeagleBoard-xmで撮影した画像(IplImage)を、そのまま転送するとかなりのデータ量になりそうです。そこで、zlibを使って圧縮してから転送しようと考え、zlibの使いかたを調べてみました。

参考にしたサイト: zlib入門

今回作ったコードは別館の方においてあります(compress.zip)。

さて、早速VGAサイズの画像データをzlibを使って圧縮してみます。

//------------------IplImageをzlibで圧縮するサンプルプログラム
/mnt/ramdisk/compress$ output_compress cat011.jpg 
width: 480  height:640
widthStep:1440  channel:3
buffer size is 921600 bytes
compbuf:1013760 bytes allocated.
res:0, size:755.9023 kByte.
成功
圧縮にかかった時間: 36.81 ms 圧縮比:0.84
解凍 成功
res:0, decomp size:921600


。。。ん?
あまり小さくなってない。。
複雑な画像だと、可逆圧縮の場合は圧縮比がそんなに良くならないようです。

また、処理時間がデスクトップ(Phenom 2.6GHz)で40msかかるので、BeagleBoardだともっとかかると思います。回線がある程度速い場合は、圧縮しないほうが良いかもしれません。


ではまた。

2011/11/20

telnet経由でArduinoにコマンド送信

前回、前々回とLinuxのソケット通信とシリアル通信のプログラムをコピペして動作確認してみましたが、コピペだけだと応用が効かないのでLinuxのプログラミングについて書籍で勉強することにしました。

我が家の本棚はもう満タンなのでKindleに放り込める書籍を探したところ、
The Linux Programming Interface
という本を見つけました。
2010年の10月に出版された本で、レビューもほとんど星5つだったので買ってみました。
紙の本だと7300円しますが、Kindleだと57ドル(4500円ぐらい?)で買えたのでお得でした。

わからない単語はかなり飛ばして読みましたが、
forkやシグナル、プロセス管理などについて学ぶことができました。
学んだことを生かし、telnet経由でArduinoにコマンドを送信するものを作りました。

概要図
サーバー側PCでこのアプリケーションを起動すると、クライアントからの接続を待ち受けます。
クライアントからTelnetで接続し文字を送ると、そのままArduinoにシリアル通信で文字を転送します。
現時点ではArduinoからの応答はサーバー側にしか返されませんが、もう少しいじればクライアント側に応答を返すことができそうです。
Arduino用に作ってはありますが、シリアルポートに接続するものであれば、ArduinoでなくてもなんでもOKです。

クライアント側もtelnetが使える端末ならば何でも良いので、XoomやIS03などからも操作ができそうです。

ソースコードは別館の方においてあります。

ではまた。

2011/11/14

BeagleBoardのベアボーン・BeagleBone

BeagleBoardのサイトに行ってみたら、何やら新しい製品の案内があるではありませんか。

手のひらサイズのBeagleBoard,BeagleBoneです。
Arduinoみたいな感じですが、CPUはA8を積んでおり、Arduinoよりかなり速いです。



BeagleBoneのリファレンスマニュアルからざっくり抜粋してみました。
CPU:AM3359プロセッサ(550MHz〜720MHz)
メモリ:DDR2 256MB
電源:USBまたは5V DCジャック
LED:電源用1個 、ユーザがいじれるLED4個
USB:USB2.0Host 1ポート(5V500mA供給可能)
        miniUSB 1ポート(USBクライアント用)
イーサネット:10M/100M RJ45コネクタ(つまり普通のLANコネクタ)
ユーザインターフェース:リセットボタン 1個
ストレージ:4GBのマイクロSDカード付き
お値段:89ドル
通信:I2C 2ポート UART 4ポート


miniUSBからの給電で動作できるようなので、モバイルブースターとかでも動作させることができるかも?
説明文にはBeagleBoneの中に開発環境があり、ブラウザでアクセスしてコーディングするスタイルをとれるとのことです。なんだかとても面白そう。

今年の年末頃にアメリカで販売されるようです。
日本のDigikeyで扱ってくれるのはいつ頃になるかなぁ。。

2011/11/08

Linuxのシリアル通信プログラム

前回はLinux上でのネットワークプログラミングをやって(コピペして)みましたが、
今回はシリアル通信のプログラミングをやってみたいと思います。

参考元:The Linux Serial Programming HOWTO(日本語版)
時々見れない場合もあるので、そんなときは英語版の方を参照してください。
検索すれば他の日本語訳のサイトも見つかるかもしれません。


上記ページにあったサンプルコードをいじって、受信と送信でプロセスを分けて処理する形に変更してみました。
実験したところ、無事Arduinoからのデータの受信と、送信ができるようになりました。



#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <time.h>
#include <sys/wait.h>

/* <asm/termbits.h> で定義されているボーレートの設定.これは
 <termios.h>からインクルードされる. */
#define BAUDRATE B9600
//Arduinoのソフトウェアシリアルでも使えるように9600bpsにする

/* 適切なシリアルポートを指すように,この定義を変更
 * 我が家の環境ではArduinoは/dev/ttyACM0になってました*/
#define MODEMDEVICE "/dev/ttyACM0"

#define BUFFSIZE 256
#define COULDNOTFORK -1

#define FALSE 0
#define TRUE 1

volatile int STOP = FALSE;
static int fd = -1;

/* Functions */
void serial_init(int fd);
void child_process();
void parent_process(pid_t result_pid);

/* Main */
int main(void);

// シリアルポートの初期化
void serial_init(int fd) {
    struct termios tio;
    memset(&tio, 0, sizeof(tio));
    tio.c_cflag = CS8 | CLOCAL | CREAD;
    /*
     BAUDRATE: ボーレートの設定.cfsetispeed と cfsetospeed も使用できる.
     CS8     : 8n1 (8 ビット,ノンパリティ,ストップビット 1)
     CLOCAL  : ローカル接続,モデム制御なし
     CREAD   : 受信文字(receiving characters)を有効にする.
     */

    tio.c_cc[VTIME] = 0; /* キャラクタ間タイマは未使用 */

    /*
     ICANON  : カノニカル入力(行単位入力)を有効にする
     */
    tio.c_lflag = ICANON;

    /*
     IGNPAR  : パリティエラーのデータは無視する
     ICRNL   : CR を NL に対応させる(これを行わないと,他のコンピュータで
     CR を入力しても,入力が終りにならない)
     それ以外の設定では,デバイスは raw モードである(他の入力処理は行わない)
     */
    tio.c_iflag = IGNPAR | ICRNL;

    // ボーレートの設定
    cfsetispeed(&tio, BAUDRATE);
    cfsetospeed(&tio, BAUDRATE);
    // デバイスに設定を行う
    tcsetattr(fd, TCSANOW, &tio);
}

void child_process() {

    char buf[BUFFSIZE];
    char input[BUFFSIZE];
    int count;
    int i;
    int writecount = 0;

    fprintf(stdout, "CHILD:RCV Start\n");

    //STOPになるまで無限ループ
    while (FALSE == STOP) {
        memset(&buf, 0, sizeof(buf));
        count = read(fd, &buf, BUFFSIZE);
        if (count < 0) {
            fprintf(stdout, "CHILD:Could not read from serial port\n");
            STOP = TRUE;
        } else {
            fprintf(stdout, "CHILD:RCVD CHAR %s %d", buf, count);

        }
    }

    fprintf(stdout, "CHILD:BYE!\n");
}

void parent_process(pid_t result_pid) {

    char input[BUFFSIZE];
    int writecount = 0;
    fprintf(stdout, "Parent:Waiting for Input\n");
    int i = 0;
    int inputcount = 0;
    while (1) {
        memset(&input, 13, sizeof(input));

        fgets(input, sizeof(input), stdin);
        fflush(stdin);

        //改行コード埋め込み
        for (i = 0; i < BUFFSIZE; i++) {
            if (input[i] == 0) {
                inputcount = i;
                input[i] = 13;
                input[i + 1] = 10;
                break;
            }
        }

        writecount = write(fd, &input, inputcount);
        if (writecount < 0) {
            fprintf(stdout, "Could not write to serial port %d\n", writecount);
            break;
        } else {
            fprintf(stdout, "Send %d bytes\n", writecount);

        }

    }
}

int main(void) {
    pid_t result_pid;

    struct termios oldtio, newtio;
    char buf[255];
    /*
     読み書きのためにモデムデバイスをオープンする.ノイズによって CTRL-C
     がたまたま発生しても接続が切れないように,tty 制御はしない.
     */

    fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY);
    if (fd < 0) {
        perror(MODEMDEVICE);
        return (-1);
    }

    tcgetattr(fd, &oldtio); /* 現在のシリアルポートの設定を待避させる*/
    memset(&newtio, 0, sizeof(newtio));/* 新しいポートの設定の構造体をクリアする */

    //シリアルポート準備
    serial_init(fd);

    //forkして受信用と送信用に分ける
    result_pid = fork();

    if (result_pid == -1) {
        fprintf(stderr, "fork failed.\n");
        return COULDNOTFORK;
    }

    if (result_pid == 0) {
        child_process();
    } else {
        fprintf(stderr, "fork completed");

        parent_process(result_pid);
    }
    STOP = TRUE;

    return 0;
}


ではまた。

2011/11/03

USB接続LEDライト制作

最近は暗くなるのが非常に早くなりました。
18時ぐらいになったらもう明かりが必要になってきます。

というわけでLEDライトを作ることにしました。

LEDを使うとなると5V電源が必要になってきますが、今回は市販のUSB電源を使うことにしました。基板ごとに電源回路をつけるとお金がかかる為です。

USBコネクタはDCジャックに比べ設置面積が必要ですが、コネクタ形状が長方形なのでDCジャックのようにクルクル回転しないで済みます。


L字型 DCジャックだったものを改造


eneloopで点灯した様子
5V200mA使用

ハイパワーLEDだと明るくて良いのですが、電池の減りが速いため、電力を抑えた角形LED版も作ってみました。



角形LED使用版
5V 80mA
モバイルブースターの大容量版を使えば、1日ぐらいならつけっぱなしでも大丈夫かもしれません。

2011/10/25

手作りレーザーレンジファインダー

レーザーレンジファインダーは、相手までの距離をレーザー光を使って計測する機器です。
工事現場などで使用されていたり、軍用で使用されていたりします。

また、ロボットカーなどに搭載して周りの障害物への距離を調べたりもします。

面白そうなセンサーなので、遊びで使ってみたいな〜とは思うのですが、いかんせんブツの値段が高い。数万円〜数百万円と、遊びで買うには無理な金額設定です(遊ぶためのものではないので当然ですが)

んで、そのレーザーレンジファインダーを手作りしてしまおう、というサイトがありました。

Webcam Based DIY Laser Rangefinder


レーザーを斜めに照射して、カメラのどの位置に来るかで距離を計算するという素晴らしいアイデア。
これならレーザーポインタかスポット照射できる光源があれば、個人でもレーザーレンジファインダーを作ることができそうです。



面白そうなので、「そのうち作りたいリスト」に追加することにしました。

Linuxネットワークプログラミング(TCPサーバー・pthread)

今日はLinuxのネットワークプログラミングについて調査、というか読書してました。
参考にしたのはTCP/IP Sockets in C という本です。
Kindle Storeで20ドルぐらいでした。紙の本で買うとすると、4週間ぐらい待たないとダメなようです。


ざ〜っと見た感じでは、Linuxのネットワークプログラミングも、Windowsのネットワークプログラミングとそんなに変わらないようなので安心。

ただ、スレッドの作り方がちょっとめんどそうな感じです。
スレッドの調査はまた後でするとして、まずは単純動作のサーバーアプリを作る(コピペする)ことにしました。

 
下記のサイトにあるサンプルコードを、まとめて1つのソースファイルにしています。
TCP/IP Sockets in C: Practical Guide for Programmers


/*
    TCPEchoServer
    Original:
    http://cs.baylor.edu/~donahoo/practical/CSockets/textcode.html
*/


#include <pthread.h>
/*
スレッド使うときは、ライブラリにpthreadを追加しないと
undefined reference to `pthread_create
とビルドするときに怒られます。
*/
#include <stdio.h>      /* for printf() and fprintf() */

#include <sys/socket.h> /* for socket(), bind(), and connect() */
#include <arpa/inet.h>  /* for sockaddr_in and inet_ntoa() */
#include <stdlib.h>     /* for atoi() and exit() */
#include <string.h>     /* for memset() */
#include <unistd.h>     /* for close() */


void *ThreadMain(void *arg);            /* Main program of a thread */


/* Structure of arguments to pass to client thread */
struct ThreadArgs
{
    int clntSock;                      /* Socket descriptor for client */
};

//エラーメッセージ出して終了する関数
void DieWithError(char *errorMessage)
{
    perror(errorMessage);
    exit(1);
}


//バッファサイズ(byte)
#define RCVBUFSIZE 256   /* Size of receive buffer */


//TCPクライアントに対する応答
void HandleTCPClient(int clntSocket)
{
    char echoBuffer[RCVBUFSIZE];        /* Buffer for echo string */
    int recvMsgSize;                    /* Size of received message */

    //受け取った文字列をそのまま送り返して、こちらからソケットを閉じる。
    //クライアントが1回なにか送ってきたら、そこで通信は終わります。

    /* Receive message from client */
    if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0)
        DieWithError("recv() failed");

    /* Send received string and receive again until end of transmission */
    while (recvMsgSize > 0)      /* zero indicates end of transmission */
    {
        /* Echo message back to client */
        if (send(clntSocket, echoBuffer, recvMsgSize, 0) != recvMsgSize)
            DieWithError("send() failed");

        /* See if there is more data to receive */
        if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0)
            DieWithError("recv() failed");
    }

    close(clntSocket);    /* Close client socket */
}


//待ち行列に待たせておく数
#define MAXPENDING 5    /* Maximum outstanding connection requests */


int CreateTCPServerSocket(unsigned short port)
{
    int sock;                        /* socket to create */
    struct sockaddr_in echoServAddr; /* Local address */

    /* Create socket for incoming connections */
    if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
        DieWithError("socket() failed");
      
    /* Construct local address structure */
    memset(&echoServAddr, 0, sizeof(echoServAddr));   /* Zero out structure */
    echoServAddr.sin_family = AF_INET;                /* Internet address family */
    echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
    echoServAddr.sin_port = htons(port);              /* Local port */

    /* Bind to the local address */
    if (bind(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0)
        DieWithError("bind() failed");

    /* Mark the socket so it will listen for incoming connections */
    if (listen(sock, MAXPENDING) < 0)
        DieWithError("listen() failed");

    return sock;
}

//クライアントが接続してきた時の処理.
int AcceptTCPConnection(int servSock)
{
    int clntSock;                    /* Socket descriptor for client */
    struct sockaddr_in echoClntAddr; /* Client address */
    unsigned int clntLen;            /* Length of client address data structure */

    /* Set the size of the in-out parameter */
    clntLen = sizeof(echoClntAddr);
    
    /* Wait for a client to connect */
    if ((clntSock = accept(servSock, (struct sockaddr *) &echoClntAddr, 
           &clntLen)) < 0)
        DieWithError("accept() failed");
    
    /* clntSock is connected to a client! */
    
    printf("Handling client %s\n", inet_ntoa(echoClntAddr.sin_addr));

    return clntSock;
}


int main(int argc, char *argv[])
{
    int servSock;                    /* Socket descriptor for server */
    int clntSock;                    /* Socket descriptor for client */
    unsigned short echoServPort;     /* Server port */
    pthread_t threadID;              /* Thread ID from pthread_create() */
    struct ThreadArgs *threadArgs;   /* Pointer to argument structure for thread */

    if (argc != 2)     /* Test for correct number of arguments */
    {
        fprintf(stderr,"Usage:  %s <SERVER PORT>\n", argv[0]);
       echoServPort=80; 
        //exit(1);
    }
    else{

    echoServPort = atoi(argv[1]);  /* First arg:  local port */
}
    servSock = CreateTCPServerSocket(echoServPort);

    for (;;) /* run forever */
    {
    clntSock = AcceptTCPConnection(servSock);

        /* Create separate memory for client argument */
        if ((threadArgs = (struct ThreadArgs *) malloc(sizeof(struct ThreadArgs))) 
               == NULL)
            DieWithError("malloc() failed");
        threadArgs -> clntSock = clntSock;

        /* Create client thread */
        if (pthread_create(&threadID, NULL, ThreadMain, (void *) threadArgs) != 0)
            DieWithError("pthread_create() failed");
        printf("with thread %ld\n", (long int) threadID);
    }
    /* NOT REACHED */
}

void *ThreadMain(void *threadArgs)
{
    int clntSock;                   /* Socket descriptor for client connection */

    /* Guarantees that thread resources are deallocated upon return */
     pthread_detach(pthread_self()); 

    /* Extract socket file descriptor from argument */
    clntSock = ((struct ThreadArgs *) threadArgs) -> clntSock;
    free(threadArgs);              /* Deallocate memory for argument */

    HandleTCPClient(clntSock);

    return (NULL);
}


これでechoサーバーを作ることができたので、あとはこのソース内のHandleTCPClient部分をいじれば、カメラの画像をクライアントに送りつけることもできるようになると思います。

ではまた。

2011/10/21

SSD化&メモリ増設

前からSSDが欲しくて買おう買おうと思っていたのですが、
最近SSDが安くなってきているようなのでとうとう買ってしまいました。

購入したのはIntelのSSD 320シリーズの80GBモデルです。
DDR3のメモリも8GBで4000円と安かったので2セット購入。


まずは現在の環境を保存するため、11.10についているバックアップで
home以下をハードディスクにバックアップしました。

SSDに交換したあとWindowsと同居させようと思い、HPのリカバリディスクで起動してみると
「出荷時のディスク容量よりも少ないため、続行できません」
と出てリカバリできず。無念。
最近のリカバリディスクはディスク容量もチェックするんですね。


というわけで大容量のSSDに換装するまでWindows7とお別れすることにしました。

気を取り直してUbuntu11.10 amd64版をCDからインストール。
インストールと初回アップデートは20分程度で完了。

システム情報を見たら、きちんと16GBのメモリを認識していました。64bit万歳。




バックアップを使用し、先ほど退避させておいたものから復元。
デスクトップとHomeフォルダは復元できたものの、メールの設定などは初期状態のままでした。
メールなどは個別でバックアップするもののようです。


アプリケーションもクリーンな状態になっているので、今まで使っていたものを再度インストールする必要はありますが、起動時間が15秒ぐらいになったので非常に快適になりました。

さてさて、開発環境の構築をし直しますかね。。
ではまた。

2011/10/18

Ubuntu11.10にアップグレード

1ヶ月ぶりの更新。

先月末からDARK SOULSをやっていたのでこっちは放置してましたが、
2週目に入って竜のボスが倒せなくなったので戻ってきました。

さて、以前やっていたArduinoタンクの続きでもやろうかと、Ubuntuを立ち上げたら

「新しいディストリビューションが利用できます」
と表示されるではありませんか。

早速アップグレードを開始。
何やらエラーが出てsambaのアップグレードが出来なかったようですが、
他は問題なくアップグレードできました。

再起動後、sambaを削除しました。(いまのところ必要ではないので)

ソフトウェアセンターが新しくなって、Android Market風になっていました。
他にもアイコンとかがかっこ良くなっている気がします。

Dashメニューもアプリの他に、最近使ったファイルや音楽も探せるようになっているので
結構便利です。


さて、Ubuntuはさておき、プロジェクトの続きを考えることに。
先月まではBeagleBoard-xmの電力測定や無線LAN化をしていたので、
次は取得した画像をネットワーク経由で送信する必要があります。


Arduinoタンクブロック図

既存のストリーミングソフトを使用すればカメラで取得した画像を簡単に送信することができますが、カメラの画像を加工することはできません。
OpenCVでカメラの画像を加工してから使用したいので、自分でネットワーク部分のプログラムを書く必要がありそうです。

というわけで、今後のタスクリストを作ってみました。
・ソケットプログラミングを調べる(まずはブロッキング接続で動かしてみる)
・OpenCVの画像情報をネットワーク経由で送れるか確認する
・ソケット通信でOpenCVで取得したカメラ画像を送受信する
・タンク操作用コマンドをソケット通信で送る
・受信したコマンドをArduinoにシリアル通信で送る

う〜ん。。通信系が結構ありますね。
とりあえず動くように頑張って調査&コーディングしてみたいと思います。

ではまた。

2011/09/19

BeagleBoard-xmの消費電力

ArduinoタンクにBeagleBoard-xm(以下BB)を載せて遊ぶ予定なのですが、
BBの消費電力はどれぐらいなのか、という疑問が出てきました。

Webで調べてみると、0.75A程度消費するとのこと。
浜町庄金 研究開発

常に0.75A消費するのか、最大負荷時に必要なのかがわからなかったので、
現物で測ってみることにしました。

構成

  • BeagleBoard-xm
  • I-O Data WN-G150U(Wifiアダプタ)
  • シリアルポートにUSB/シリアル変換器

調査した状況と値:

  • 電源投入直後 0.25A
  • SDカードからカーネルを読み込んで起動するまで 0.5A~0.74A
  • ログイン可能になったとき 0.57A
  • WifiアダプタをifDownで切断したとき 0.51A
  • apt-get upgradeを実行中 0.68A程度
  • OpenCVのソースコードのパッケージを解凍しているとき 0.63A〜0.82A
  • Webカメラ(Logicool C910)をつないで、luvcviewで動かしてみたとき 0.92A

WifiアダプタとWebカメラの消費電力も気になっていたのですが、Wifi利用時でも0.7A、Webカメラをつけても1Aに達しなかったので、手元にあるUSB電源のPanasonic QE-PL201 (1.5A供給可能)で十分動作させることができそうです。

ではまた。


2011/09/11

BeagleBoard-xm(ubuntu11.04)の無線LAN化

Arduinoタンクの組み立てのほうはある程度出来たので、
タンクにつむBeagleBoard-xmの無線LAN化をすることにしました。


基本的には下記のサイトを参考にして設定をすればOKです。
Beagleboard-xmで遊ぼうのコーナー(ソフト編)
BeagleBoard-xM + Ubuntuで無線LAN@STUDIO-K PONTAの日記
Linuxで無線LAN@ゆうちくりんの備忘録


。。。。と言いたいところですが、無線LANアダプタの種類によっては
設定が正しくても動作しないことがあるようです。

無線LAN親機 バッファロー WHR-HP-G300N

無線LAN子機(1)
pci GW-USValue-EZ

  • iwconfigで、wlan0として認識はされた。lsmodでもrtl8192cuのモジュールがロードされていた。
  • ただし、wpa_supplicantを実行すると、 "Bad file descriptor" というメッセージがでて進まない。もちろん接続ができない。親機の設定を変えたり(WEPにしたりパスワードなしにしたり)したものの、進展しなかったので諦めた。PC側に挿した場合は、ドライバをダウンロードしてきてコンパイルすると普通に動いた。BeagleBoard-xmではファイルが足りない?ようなのでコンパイルできなかった。
無線LAN子機(2)
I-O Data WN-G150U
  • pciの無線LAN子機は諦めて、新しく電気屋で買ってきた。
  • iwconfigで、wlan2として認識された。
  • wpa_supplicantも無事実行された。
  • 作業時間 5分。。。。。 数日間の苦労はなんだったんだろう。
  • 親機の設定を、WPA/WPA2 mixedmode - PSK にしても問題なく動作。

数日かかりましたが、ようやくBeagleBoard-xmを無線LAN化することが出来ました。
次はカメラをつないで消費電力のテストでもしようかと思います。

ではまた。

2011/08/28

Arduinoタンク ピラミッド型ミラーの実験

昨日作ったピラミッドミラーをタンクに取り付けて、取得した画像をOpenCVで前後左右に分割するプログラムを作ってみました。

タンクの前方にキャタピラセットの空き箱、右方向に工具箱とギアボックスの空き箱、左方向にデジタルマルチメータの箱を置いています。

俯瞰図


撮影した生のデータ


4方向に分割、再配置


感覚的には以下の画像のような視野になりました。
分割したので仕方ないとはいえ、前方の視野がかなり減ってしまいました。




ひとまずこれを使ったタンクを再度組み立ててみたいと思います。
ではまた。

Arduinoタンク 周囲確認用ピラミッドミラー

前にArduinoタンクの前方にWebカメラをつけてみたのですが、前方しかカメラで確認できず(当たり前ですが)、左右・後方の確認が必要だな~と感じました。

大さじを使って周囲を撮影し、全方位カメラにしようと少し実験してみたのですが、
大さじでは像がぼやけてしまい使い物になりませんでした。

手元にある素材ではダメだったので、下記のサイトにあるようにステンレスのミラー球を使うことにしました。

そして今日、東急ハンズにミラー球を捜しに行ったわけですが、在庫がありませんでした。
わざわざ取り寄せるのも面倒なので、プランBを実行することにしました。

プランBは、鏡でピラミッドみたいな感じのものを作り真上からカメラで映す、というものです。

プランB
光り輝くピラミッド方式

これならミラー球で撮影したときに必要な球面補正など、面倒な処理をしなくてもよくなります。

ミラーピラミッドには鏡が必要なのですが、さすがにガラス製の鏡は危なくて加工したくはありません。
今回はハンズの樹脂素材コーナーにあったミラー加工のアクリル板を買ってきました。2mm厚なのである程度強度が出そうです。加工もアクリルカッターで簡単にできます。

ハサミで切れる0.5mmのミラー加工ポリカーボネイト板も気になりましたが、くみ上げたときにたわみそうだったので今回はやめておきました。(土台を作った後に貼り付ける感じにすれば、こっちのほうがキレイにできるかも)


150mm x 300mm x 2mmのアクリル板。
もっと小さいのでも良かった。。。

買い物を終えた後、早速CADソフトを使いピラミッドを作ったときに角度が45度になるような三角形(底辺:高さ=2:√2)を作成し、レーザープリンタラベル用紙に印刷してアクリル板に貼り付けました。 こうすると加工が楽になります。
裏面にシール貼り付け

表面は傷がつかないよう
マスキングテープで養生
あとはアクリルカッターで削り、1辺ずつポキポキ割っていきます。アクリルの厚さが2mmなのでそんなに苦労しませんでした。

切り出した後はピラミッド状に組んで固定していきます。アクリル用の接着剤がちょっと高かったのと今後そんなに使わなさそうだったので、ホットボンドで代用しました。
今回は隙間を埋めていく感じで使ったので、ホットボンドのほうが接着剤よりも適役だったようです。


ホットボンドで塗り固めます

ユニバーサルプレートに固定できるよう、
M3のネジをつけておきました

ホットボンドで固定した後は、余分な部分を切り取り、マスキングテープをはがします。

周囲確認用ピラミッドミラー 完成!
Webカメラでテスト撮影してみたところ、クリアな画像が得られました。
明日はこれをタンクに取り付けて、どれぐらい使えるかの実験をしてみようと思います。

ではまた。



2011/08/23

メインPC 復活

修理に出していたメインPCですが、マザーボードの交換がされた状態で戻ってきました。
HDDの中身はフォーマットされてるだろうな~と思いましたが、幸いなことにHDDはそのままでした。

ようやくOpenCVでの開発が再開出来そうです。

まずは大匙を使った全方位カメラの開発をして、Arduinoタンクに搭載したいと思います。


2011/08/16

自作Sanguino

自作Arduinoの兄貴分?にあたる、Sanguinoを作ってみました。

sanguino.cc

Sanguinoとは@マイクロファン ラボ

SanguinoとArduinoの違いは、下記の通りです
(公式のものを翻訳しました)

・atmega644Pを使用しています。
・合計で32本のIOピンがあります。
・8本のアナログ入力ピンがあります。
・6本のPWM対応ピンがあります。
・64KBのフラッシュメモリーを利用できます。
・4KBのRAMがあります。
・2KBのEEPROMがあります。
・ブレッドボードで作れます。
・100%オープンソースです。

ブレッドボードにICや水晶振動子を取り付けた後、ブートローダーを書き込もうとしましたが、AVR ISP MkIIがUbuntuで認識されていないようです。

いろいろと探してみたところ、ここに解決策がありました。

AVR ISP MkIIをUbuntu11.04で使用する方法;
1./etc/udev/avrisp.rules を作成します。
2.avrisp.rules内に、以下のように記載します。

SUBSYSTEM!="usb_device", ACTION!="add", GOTO="avrisp_end"

# Atmel Corp. JTAG ICE mkII
ATTR{idVendor}=="03eb", SYSFS{idProduct}=="2103", MODE="660", GROUP="dialout"
# Atmel Corp. AVRISP mkII
ATTR{idVendor}=="03eb", SYSFS{idProduct}=="2104", MODE="660", GROUP="dialout"
# Atmel Corp. Dragon
ATTR{idVendor}=="03eb", SYSFS{idProduct}=="2107", MODE="660", GROUP="dialout"


LABEL="avrisp_end"


3.保存した後は、下記コマンドを実行します。

cd /etc/udev/rules.d
sudo ln ../avrisp.rules 60-avrisp.rules
groups
Restart udev
sudo restart service udev


4.念のため再起動しておきます。



これでAVR ISP MkIIが使えるようになったので、次はブートローダーの書き込みです。
Burning the Sanguino Bootloader@replapwiki

上記のサイトにあるように、下記コマンドを実行すると書き込みができます。


wget http://sanguino.googlecode.com/files/Sanguino-0018r1_1_4.zip
unzip Sanguino*zip
sudo avrdude -patmega644P -cavrispmkII -Pusb -U flash:w:Sanguino/bootloaders/atmega644p/ATmegaBOOT_644P.hex


Sanguinoのブートローダーが書き込めたので、次にArduinoのソフトのほうでスケッチを書き込めるようにします。

参考にしたのは下記ページ。
Software For Linux@sanguino.cc

1.googleCodeからSanguinoのファイルをダウンロードして展開します。
2.ArduinoのHardwareフォルダに放り込みます。(私はapt-getからArduinoをインストールしたので、/usr/share/arduino/hardware/ に放り込みました)
3.Ubuntuの場合は、board.txtを若干修正する必要があります。

     sanguino.upload=stk500
    を
    sanguino.upload=arduino

そのあと、Arduinoを再起動すればOKです。

Sanguino


今はまだそんなにIOピンが必要な訳ではないんですが、とりあえず作ってみました。
Arduino用のラベルが便利だったので、Sanguino用のラベルも作りました。
ラベルは別館に保存してますので、ご自由にどうぞ。

ではまた。


2011/08/13

Arduino Uno互換機の作成

私はArduino Unoを1台持っていますが、センサとArduinoを使ったものを作るときに
Ardunoが1個だけだと何かと不便です。
必要な数だけArduino Unoを購入するのも手軽な方法ですが、1台あたり3,000円するので
ちょっと考えてしまいます。

いろいろなサイトを見てみた結果、自分で互換機を作ることにしました。

メインとなるチップ ATMEGA328P−PUは、秋月電子で購入しました。1個250円ぐらいでした。Arduino化するのにはブートローダーという小さいソフトを書き込む必要があるので、AVRライターも購入。こっちは3,300円。
秋月電子のは素のチップですが、スイッチサイエンスでブートローダー書き込み済みのものも購入できます。こっちは1つ350円。


Arduino UnoとAVRライターを使って、素のチップにブートローダを書き込みました。
(参照:DIGIらいふ

Arduinoの素。
チップだけだとピンを間違えそう
Arduinoには基板上にピンアサインの印刷があるので配線するときに間違えにくいのですが、自作版には何もないので、何かしらわかりやすい方法を考える必要があります。

Arduino chip sticker label に、Arduinoのチップに貼るラベルが公開されてたので、svg形式のをInkscapeで読み込み、少し修正を加えて印刷してみました。



ラベルをペタリ
何もない状態よりも、かなりわかりやすくなりました。
(いちいちピン番号を数えるのは面倒でした)

次に、ブレッドボードと同じレイアウトのユニバーサル基板(80円)に実装してみました。
ICソケットを基板にハンダ付けして、Arduinoのチップは交換できるようにしてあります。
16MHzの水晶振動子を置くところがなかったので、チップの上の方に置きました。
それ以外はほとんどジャンパ線をはわせないで作成できました。

普通のユニバーサル基板だと、GNDラインなども1カ所ずつリード線などをはわせる必要があるので面倒だったのですが、ブレッドボード形式のものだとその辺は楽でした。
便利な反面、自由度は減ってしまうので、目的に応じて今までのとブレッドボード形式のものを使い分けるようにしたいと思います。
自作Arduino Uno。上の方の余白にも
ピンソケットを実装して、実験しやすくする予定

自作Arduinoへのプログラムの書き込みは、USB-シリアル変換アダプタを使用します。
秋月電子で売っていたFT232RL USBシリアル変換モジュールを使う場合は、以下のような配線になります。

Arduino USBシリアル
Vcc USB
GND GND
RXD TXD
TXD RXD
RST DTR#  (間に0.1uFのコンデンサを挟む)  RST-0.1uF-DTR#


Arduino Unoに比べるといろいろと面倒ですが、やはり安く作れるのはありがたいです。

ではまた。

Arduinoに温度センサーをつなぐ

節電の夏。暑いです。
皆さんの勤務先は暑いですか?
私の勤務先は暑いです。エアコンが効いてません。

「暑いからなんとかして〜!」と訴えるためには、データが必要です。でも、ロギングは面倒です。

そういう面倒な作業はPCとマイコンにまかしてしまおう、ということで
Arduinoに温度センサーをつけて温度ロガー代わりにしてみたいと思います。

使うもの
1.PC 1台
2.Arduino 1個
3.温度センサー(LM35DZなど。) 1〜6個

秋月電子で買ったLM35DZ(100円)は、0.1℃=1mVという出力特性を持っています。
30℃だったら300mV。室温でプラスマイナス0.25℃の精度。ほか温度ICに比べると少し高めですが、精度を考えれば納得です。

LM35DZのVOutピン(真ん中)をArduinoのアナログ入力ピンにつなぎ、analogReadで読むと、AD変換されたあとの値が取得できます。
通常はAD変換の基準電圧が電源電圧と同じ(5V)になっているので、何も設定しない状態だと0.5℃刻みのデータしか取得できないことになります。
(5000mV/1024=4.88mV≒0.49℃)

0.1℃刻みぐらいにはしたいので、基準電圧(AREF)を1Vぐらいに設定してみます。
ツェナーダイオードとか定電圧ICとか使う手もありますが、そんな部品は持っていないので手軽に抵抗分圧することにしました。

5V-AREF間に40kオーム、AREF-GND側に10kΩをつければ、基準電圧を1Vにできますが、手元にあった51kΩと10kΩで分圧することにしました。

AREFの電圧をコードに組み込むことによって、AREFが適当でも問題なく動作するようにしました。

部屋の温度は30℃。。暑い
ターミナルソフト側でタイムスタンプを埋め込めば、温度ログとして使えると思います。
また、イーサネットシールドを取り付けて、Syslogサーバとかに温度を送りつけたりするのも面白そうです。イーサネットシールドは少し値段が高いですが。

ソースコードは別館に保存してあります。
ではまた。

2011/08/11

BeagleBoard-xm の 日本語化

メインPCが壊れたので修理に出す予定ですが、引き取りが今週の土曜日となっているので、
その後の修理期間も考えるとしばらくはメインPCが使えないな~、と考えています。

その間はサブ機のiMac(現在は家族共有)を使うことも考えましたが、リビングから自分の部屋に毎回持ってくるのが少し面倒です。
なのでBealgeBoard-xmを日本語化して、メインPCが戻ってくるまでしのぐことにしました。

日本語化の作業といっても、"Ubuntu GUI 日本語化" で検索して、言語の追加とIMEの設定を行っただけです。

現在は8GBのマイクロSDカードにUbuntuをインストールしているのですが、すでに4.5GB程度使用しています(残り3.5GB)。
BeagleBoard-xmでUbuntuのデスクトップ環境を構築する場合は、8GBか16GBのマイクロSDカードにしたほうがよさそうです。


というわけでBeagleBoardをタンクに積む計画は延期になりました。
代わりにステンレス製の大さじ・小さじを使った全方位カメラの実験をしたいと思います。

ではまた。





2011/08/07

メインPC 故障

今日、PCの電源を入れたところ、画面に何も映らない現象が発生しました。
BIOSの画面も出てきません。また、キーボードやマウスのLEDも光りませんでした。

マザーボードあたりがお亡くなりになった模様です。

保証期間内なのでメーカーに修理を依頼するつもりですが、
その前にデータの救出を行わないと。。

SATAハードディスクを外付けにするアダプタは会社においてあるので、明日回収してきて
データのバックアップをしようと思います。

この際なので、メインPCもSSD化してしまおうかなぁ。。。

2011/08/06

Arduino/BeagleBoard用のバッテリーを探す

BeagleBoard-xmを完全無線化すべく、BeagleBoardを駆動できそうなバッテリーを探しています。

DC-DCコンバータとバッテリーを使うのが常套手段のようですが、バッテリーも結構値段がしますし、もちろん充電器も一緒に買わなければいけません。

5V出力で電流がとれるやつは無いかなぁ~と考えていたら、
以前購入したeneloopのモバイル充電キット?を思い出しました。
eneloop2本でUSB充電できるやつです。

早速引き出しから引っぱりだしてみると、5V500mAまで出力できるとのこと。
BeagleBoardは1Aぐらい必要だそうなのでこれだと足りない。

ものは試し、ということで、USBケーブルとACアダプタをそれぞれ切断し、USBオス=2mmコネクタオスというケーブルを作りました。

USB-DCジャックケーブル

昔作った白色LED光源でテスト

BeagleBoardにつないで、電源をONにしたら、
コンソールに文字が表示されました。

ブートしてくれるかな~と見守っていたら、
再起動の連続になりました。
コンソールのログを見てみると、USBハブを認識した後、再起動がかかっている模様です。



[    4.335327] SmartReflex Class3 initialized
[    4.344848] clock: disabling unused clocks to save power
[    4.351379] registered taskstats version 1
[    4.355926] fbcvt: 1280x720@60: CVT Name - .921M9-R
[    4.364959] usb 1-2: new high speed USB device number 2 using ehci-omap
[    4.381256] Console: switching to colour frame buffer device 160x45
[    4.398223] regulator_init_complete: VDAC: incomplete constraints, leaving on
[    4.406250] Freeing init memory: 360K
[    4.522827] hub 1-2:1.0: USB hub found
[    4.531463] hub 1-2:1.0: 5 ports detected


ACアダプタに変更してから、USBの次のログを見てみると、mmc。。。。。と表示されていました。
SDカードから読み取ろうとしているところあたりで落ちているようです。
やはり500mAでは足りないみたいです。

他の機種をネットで調べてみたら、Panasonicから今年の春に出たQE-PL201というモバイル電源パックの仕様が出力5V1.5Aとなっていました。


これなら動作できるかも??


というわけで明日電器屋さんに買いに行ってきます。

ではまた。

自宅エクスプローラ(仮) 試作品作成

自宅エクスプローラの続き。

Arduinoでモーターを制御できるようになったので、
まずはArduinoとタンクだけで遊んでみました。
ついでなのでWebカメラもテープで固定してつけてみることに。
もちろんまだ有線です。


タンク用のプレートだけだとギアボックスで半分埋まっているため、
タンクの上にユニバーサルプレート(大)を取り付けて設置面積を増やしました。
Arduino、電池ボックス、カメラ、ブレッドボードを置くといっぱいいっぱいになりました。


ユニバーサルプレート(大)が込み合っています
BeagleBoardはどこに置こうかな・・


メインカメラ。こんな感じの仕事ばっかりさせられてます


まだ有線式なので動ける範囲に限りはありますが、
モニタでメインカメラの画像を見ながら操作するのは楽しいです。


無線LANをつんだBeagleBoard-xmとバッテリーをつければ、家の中をいろいろと探検できそうな感じです。


Youtubeに車載カメラと固定カメラの動画を載せました。
下の動画を再生しはじめた直後に上の動画を再生し始めると、ほぼ同じタイミングの
映像になります。
(箱が転がっていますが、撮影用に転がしたものです)





メインカメラだけでも操作はできますが、後方や側方も確認したくなりました。
360度カメラが安く売っていれば、つけてみたいと思います。


Arduinoのコードは以下のようになります。
結構簡単なコードで、上記のようなラジコンができます。

/*
 自宅エクスプローラ用
 タンクコントローラ
 
 TA7291Pを2個使って、タンクを操作します
 TA7291PのVRefにanalogWriteで電圧を与えると、
 モーターにかける電圧を変えることができます。

 */

//後でキーアサインを変更できるようにしておく

#define GO '8'  //前進
#define STOP '5' //停止
#define LEFT '4' //左旋回
#define RIGHT '6' //右旋回
#define BACK '2' //後ろ

#define LEFTIN1 2
#define LEFTIN2 3
#define RIGHTIN1 4
#define RIGHTIN2 5

#define VREF  9  //出力電圧のリファレンス PWMが使えるポートにしてください


void setup() 
{
  Serial.begin(9600);
  pinMode(LEFTIN1, OUTPUT);//左側のモータードライバのIN1
  pinMode(LEFTIN2, OUTPUT);//左側のモータードライバのIN2
  pinMode(RIGHTIN1, OUTPUT);//右側の(以下同文)
  pinMode(RIGHTIN2, OUTPUT);

  Serial.println("MOTOR");
  //出力電圧のリファレンス用
  pinMode(VREF,OUTPUT);

  //モーターは1.5V~3Vが動作電圧
  analogWrite(VREF,128);//とりあえず2.5V

  //最初は停止している状態
  setLeftMotor(LOW,LOW);
  setRightMotor(LOW,LOW);

}


//左側のモータを制御します。
void setLeftMotor(int in1,int in2)
{
  digitalWrite(LEFTIN1,in1);
  digitalWrite(LEFTIN2,in2);
  //デバッグ用
  Serial.println('LightMOTOR'+ in1 +' '+ in2);
}
void setRightMotor(int in1,int in2)
{
  digitalWrite(RIGHTIN1,in1);
  digitalWrite(RIGHTIN2,in2);
  //デバッグ用
  Serial.println('RightMOTOR'+ in1 +' '+ in2);

}

void loop() 
{

  delay(100);
  char r=Serial.read();

  if(r== GO)
  {  //全速前進
    setLeftMotor(HIGH,LOW);
    setRightMotor(HIGH,LOW);
    Serial.println("GO");
  }

  if(r==STOP)
  {
    setLeftMotor(LOW,LOW);
    setRightMotor(LOW,LOW);
    Serial.println("STOP");
  }

  if(LEFT==r)
  {
   //左方向へ旋回
    setLeftMotor(LOW,HIGH);
    setRightMotor(HIGH,LOW);
    Serial.println("LEFT");
  }

  if(RIGHT==r)
  {
    //右方向へ旋回
    setLeftMotor(HIGH,LOW);
    setRightMotor(LOW,HIGH);
    Serial.println("RIGHT");
  }
  if(BACK==r)
  {
    //退却。後方確認はできません
    setLeftMotor(LOW,HIGH);
    setRightMotor(LOW,HIGH);
    Serial.println("BACK");
  }

}



ではまた。

2011/08/04

Arduinoでモータードライバを制御

昨日、今日で自宅エクスプローラ(仮)の駆動部分を組み立てていました。

駆動部分のベースは、タミヤ模型の「楽しい工作シリーズ タンク基本セット」と、「ダブルギヤボックス」を組み合わせたものです。
ダブルギアボックスは一番トルクを出せる組み合わせ(344:1)で組みました。

プレートにギアボックスを固定した後、コネクタつきのケーブルにハンダ付けしました。
コネクタはPC用のATX電源ボックスの5V/12Vラインによくあるタイプのものです。



コネクタ再利用

キャタピラ車の方がひとまず組みあがったので、Arduinoでモータードライバを制御するプログラムと回路を作りました。


回路は至って簡単で、Arduinoからは2本ずつ、2組の信号ラインを2つあるモータードライバのIN1とIN2にそれぞれつなぎます。あとはVccやGNDを配線して、Out1とOut2をモーターにつなげるだけです。

Arduino側のプログラムも、2本の出力ポートにHIGH/LOWの組み合わせを送るだけです。
例: 前進   HIGH/LOW  HIGH/LOW
  後退   LOW/HIGH  LOW/HIGH
     右回転  LOW/HIGH  HIGH/LOW
   左回転  HIGH/LOW  LOW/HIGH
   停止     HIGH/HIGH HIGH/HIGH

プログラムもざっくりですが出来たので、週末は筐体の方の組み立てに入りたいと思います。


完成予想図に使える写真を探していたときに、キネクたんというページを見つけました。
Arduino+タンク工作セット+Kinectという組み合わせです。
ソースコードも公開されているので、これをコピーさせてもらえば良かったかな・・・・
まぁ、まずは自分のコードで進めたいと思います。

ではまた。

2011/08/02

Arduino用ブレッドボードシールドの作成

秋月電子に注文していた部品がいろいろと届きました。

早速、下記ページを参考に、Arduino用ブレッドボードシールドを作成することにしました。

もういっそのこと… で、『ブレッドボードシールド作ってみた』

買った後に気づいたのですが、自分が買ったピンソケットは、基盤側のピンが若干短い。。。
安いからこっちの方がよいかな~と思ったのですが、きちんと長いピンのピンソケット(秋月だと通販コードC-04046)の方が良いです。今度はこっちを買おう。。。


ピンがちょっと短い
ピンヘッダをArduinoに合わせて切断したあと、ブレッドボードにアロンアルファでくっつけたのをArduinoに載せてみたら、案の定2ヶ所で接触不良が起きていました。

丸ピンソケットも一緒に買ってあったので、仕方なくゲタを履かせる感じで丸ピンソケットをつけました。

苦肉の策
丸ピンソケットを取り付けたあとは、接触も良好になりました。
材料をケチると良いことがありませんね。


完成した状態が下記の写真です。
基板上のピンアサインがまったく見えなくなるので、ソケットの横にシールでピンアサインを
貼っておく必要がありそうです。




ではまた。

-------------------------------------------------------------
Arduino用ブレッドボードシールド 材料
 レッドボード EIC-801 250円
ピンソケット(メス) 1x20 2本  (本当は長ピンソケットの方がよいです)
カッター
アロンアルファ等の接着剤
---------------------------------------------------------------

Fritzingでブレッドボード配線図を書く

Ardiunoを使って、ラジコン戦車的なものを作ろうとしているのですが、
配線図をどうやって作ろうかな〜と考えていました。

ネットでいろいろ調べていると、Fritzingというソフトが配線図を作るのに良さそうとのこと。
Fritzing.org から各OSに適したパッケージをダウンロードして解凍します。
Fritzingを起動すると、下記のような画面が表示されます。



ブレッドボードモードと、回路図、PCBの3つのモードを切り替えることができ、
それぞれリンクされているのがなかなか便利です。

PCBのモードには、自動配線がついていたり、両面基板が選べたりと高機能です。

専用基板が作りたくなったら、Fritzing Fabに注文すると、Arduino用シールドだと1枚あたり$36で作ってくれるようです。(ちょっと高い気が。。)

ではまた。

2011/07/30

Arduino Uno到着

Amazonで注文しておいた、「Arduino Unoをはじめようキット」が到着しました。

Arduinoと小型のブレッドボード、ジャンパワイヤとLED3個、CdSセル1個、抵抗数個のセットでした。
電源はACアダプタと、USBバスパワーのどちらでもOKです。
私はちょうどよいACアダプタを持っていなかったので、USBバスパワーで駆動させることにしました。


右上の箱がArduino。結構小さいです

箱を開けると、Arduinoの基板がそのまま入っていました。
小さな冊子と(右側)、シール(左側・小さいのが6枚)が付属品のようです。




PCにUSBケーブルでつなぐと、Mac/Linuxの場合はそのまま動作し、Windowsの場合はドライバを入れるとCOMポートに接続されます。

Arduino用のプログラムを書くためのIDEを、Arduinoのサイトからダウンロードして解凍します。


少し触ってみた感想:
Arduinoにはシリアル通信機能が標準でついているため、簡単に他の機器との
やり取りができることがわかりました。
送信するときはSerial.print()またはSerial.write()、読み取るときはSerial.read()と非常にシンプルです。
IDEも使いやすく、電子工作やプログラミングに慣れていない人でもすぐにとりかかれそうな感じでした。

LEDも問題なく光らすことができたので、早速シリアル通信プログラムを作って、ArduinoとBeagleBoard-xmをつなげてみたいと思います。

ではまた。

2011/07/28

BeagleBoard-xmを購入

久しぶりの更新です。

7月は新しいゲームが出たり、その他もろもろでブログのほうは放置してました。
ゲームもクリアしたのでOpenCV/電子工作のほうにまた戻ってきました。

8月からはBeagleBoard-xmとArdiunoを使って、個人向けのマーズパスファインダーみたいなのを作る予定です。

材料のBeagleBoardは6月末に発注、7月上旬に到着済みです。
Ardiunoはスタートキットが今週末に届く予定です。

その他タミヤのキャタピラ工作セットなども必要ですが、ひとまずArdiunoやBeagleBoardがあらかた動くようになってから発注しようかと思います。


BeagleBoard-xm + 犬小屋
自宅パスファインダー(仮称)の構想

駆動部分:タミヤのタンク工作キット+ツインモーターギア(左右個別制御)
モータ制御部分:Ardiuno+モータードライバ
その他:ArdiunoとBeagleBoardをUSBかなにかで接続して、BeagleBoardからArdiunoに移動命令などを出す
BeagleBoardにはUSBカメラと無線LANアダプタをつけて、他のPCで制御&画像受信


まずはArdiunoでLEDを光らすところから始めていきたいと思います。

ではまた。

2011/06/26

Android2.2+OpenCV2.3の実験

OpenCV2.3をAndroidで動かせるようになったので、
実際にどれぐらいの速度で処理できるかを試してみました。

処理の内容:
Java側でVGAサイズのカメラプレビューのバイト配列を取得(YUV420のデータ)
C側でバイト配列からCvMat型に変換する
CvMat型の画像をFAST特徴量検出器にかける
結果の画像を今度はintの配列にして返す
Java側で表示

結果:1フレームあたり80~90msで処理できた。
(CvMatに変換した後、FAST検出器にかけない場合は50ms程度だったので、FAST検出器部分の処理は30~40ms)

動作中の映像は以下の通りです。





最初はもっと遅かったのですが、Application.mkのCPP_FLAGSというところに最適化オプションスイッチの-O3を加えたらかなり速くなりました。
NDKのデフォルトでは、コンパイラの最適オプションがついていないようです。


また、JNIついて書かれているPDFがOracleのページにあったのでリンクを貼っておきます。
JNIって何なの?と思っていたので、大変参考になりました。
Java Native Interface


ではまた。

2011/06/23

Android+OpenCV2.3の環境構築に挑戦

OpenCV2.3が出たので、また懲りずにAndroid+OpenCV2.xの環境構築に挑みます。
(今日はちゃんとサンプル以外のも動かせたので成果ありです)

参考にするページはいつものOpenCV公式Wikiです。
Android
Android2.3.0というページが増えてたので、そこを参考にしています。

まずはOpenCV2.3.0をSourceForgeからダウンロードしてきます。
私はOpenCV2.3.0rc.tar.bz2をとってきて解凍しました。
また、その他必要なCMakeと公式AndroidNDKもダウンロードしておきます。

材料が揃ったので、Android用OpenCVのビルドに入ります。

1.ANDROID_NDKとANDROID_NDK_TOOLCHAIN_ROOTをExportする。
ビルドの間だけ有効であれば良いので、./bashrcに書く必要はありません。
これは環境に合わせて設定してください。
(android-toolchainの設定方法は、Android+OpenCV2.2を参考にしてみてください)

2.ライブラリをビルドします。
  • cd opencv/android
  • sh ./scripts/cmake_android_armeabi.sh
  • cd build_armeabi
  • make -j6
  • make install
注意:Wikiに載っているとおりにcmake_android.shを実行してその後ビルドすると、
エミュレータでは動作しないAndroid用OpenCVライブラリが出来上がります。
(cmake_android.shでは、ターゲットがarmeab-v7aで、cmake_android_armeabi.shの場合はターゲットがarmeabiになります。Androidのエミュレータはarmeabiになるので、せっかくビルドが通るようになっても動きませんでした(これでかなりの時間ハマりました))


3.インストールが完了すると、NDK-TOOLCHAIN-ROOTに設定したフォルダの中に、ライブラリがコピーされると思います。
私の場合は、ライブラリは/opt/android-toolchain/user/armeabi/lib、インクルードファイルは/opt/android-toolchain/user/armeabi/includeとなりました。

4.サンプルのHelloAndroidを作成します。
 HelloAndroidというプロジェクトは、Android端末内のコンソールアプリケーションを作るプロジェクトです。Javaは一切出てきません。
また、サンプルの説明ではCMakeを使ってビルドするようですが、CMakeでビルド出来なかったので、ndk-buildでビルド出来ないかいろいろとやってみました。

4.1 まずはopencv/apps/HelloAndroidフォルダ内に、jniフォルダを作ってmain.cppのコピーを放り込みます。
4.2 そのあとjniフォルダの中に、Application.mkファイルを作成します。
Application.mkファイルの中身は以下のとおりです。
#STLを使うのと、ターゲットがarmeabiだということを伝える
#http://opencv.willowgarage.com/wiki/OpenCVAndroidBinariesBuild にApplication.mkのお手本があったのでコピペしました
APP_STL := gnustl_static 
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi


4.3 同じくjniフォルダ内にAndroid.mkファイルを作って、以下の内容を書き込みます。
中身は長時間の試行錯誤の上、動作したものです。いらない行があるかもしれません。

LOCAL_PATH := $(call my-dir)
#Android.mkファイルも、Makefileの一種なので、
#タブで整形するとコマンド行と認識されてしまってエラーが出ます。。

include $(CLEAR_VARS)

#ライブラリのフォルダは、各自変更してください。
ANDROID_OPENCV_LIBS:=-L/opt/android-toolchain/user/armeabi/lib -lopencv_contrib -lopencv_calib3d -lopencv_objdetect \
                   -lopencv_features2d -lopencv_imgproc -lopencv_video -lopencv_highgui -lopencv_ml -lopencv_legacy -lopencv_flann -lopencv_core

#ここも同じ。
OPENCV_3RDPARTY_LIBS:=-L/opt/android-toolchain/user/armeabi/share/opencv/3rdparty/lib \
 -llibjpeg -llibpng -llibtiff -llibjasper -lzlib

#includeフォルダも各自変更してください。
LOCAL_C_INCLUDES:=  /opt/android-toolchain/user/armeabi/include

LOCAL_LDLIBS+= $(ANDROID_OPENCV_LIBS) 
LOCAL_LDLIBS+= $(OPENCV_3RDPARTY_LIBS)

LOCAL_MODULE    := helloandroid
LOCAL_SRC_FILES := main.cpp

#OpenCVで使うライブラリ一覧
LOCAL_SHARED_LIBRARIES:= opencv_core opencv_highgui opencv_contrib opencv_calib3d opencv_objdetect \
   opencv_features2d opencv_imgproc opencv_video \
    opencv_ml opencv_legacy opencv_flann libjpeg libpng libtiff libjasper zlib
#include $(BUILD_SHARED_LIBRARY)

#コンソールアプリとして実行するので以下のオプションをつける。
#公式ドキュメントには載っていないので、そのうちなくなってしまうかも?
include $(BUILD_EXECUTABLE)


あとはndk-buildを実行すると、アプリケーションが出来上がります。


kouji@kouji-ubuntu:~/OpenCV-2.3.0/android/apps/HelloAndroid$ ndk-build
Compile++ thumb  : helloandroid <= main.cpp
Executable     : helloandroid
/opt/android-toolchain/user/armeabi/lib/libopencv_core.a(system.o): In function `cv::tempfile(char const*)':
system.cpp:(.text._ZN2cv8tempfileEPKc+0x22): warning: warning: mktemp() possibly used unsafely; consider using mkstemp()
/opt/android-toolchain/user/armeabi/share/opencv/3rdparty/lib/liblibjasper.a(jas_stream.o): In function `jas_stream_tmpfile':
jas_stream.c:(.text.jas_stream_tmpfile+0x44): warning: warning: tmpnam() possibly used unsafely; consider using mkstemp()
Install        : helloandroid => libs/armeabi/helloandroid


あとはAndroid2.1などのエミュレータを起動し、以下のような感じで転送して実行するだけです。

adb push libs/armeabi/helloandroid /data/helloandroid
adb shell
#cd /data
#chmod 755 helloandroid
#./helloandroid
Hello Android!

そのあと、画像が出来て入ればOK。
こんな感じの画像になります。



これで念願のAndroid+OpenCV2.3+ndk-buildの環境が出来ました。

ではまた。

OpenCV2.3.0rcのパノラマ写真作成ソフトを試してみました

6月22日にOpenCV2.3がリリースされました。

変更点は以下のとおりです。
OpenCV2.2からOpenCV2.3rcの変更点@OpenCV.jp


新機能の中に、stitchingというのがありました。
パノラマ写真を作ることが出来るアプリケーションのベータ版だそうです。

stitchingを実行するために、早速SourceForgeからダウンロードしてインストール。
この辺は2.2と変わって無いので説明は省略します。



makeしたあとmake installを実行すると、ライブラリのインストールと/usr/local/binにいろいろなアプリケーションが登録されます。

opencv_stitchingも/usr/local/binにありました。

早速家のまわりの写真を放り込んだら、30秒ぐらいで見事パノラマ写真が出来ました。

opencv_stitchingのソースコードもあるので、そのうちどんな処理を行っているかを探ってみたいです。

2011/06/21

XOOMをAndroid3.1へアップデート

au版のXOOMにも、アップデートが来たので早速インストール。

3.1になってからは、ウィジェットのサイズを変えることが出きるようになりました。
今までGmailのウィジェットがちょっと小さい気がしていたのですが、
これで好きなサイズに設定することが出きるようになりました。

また、マイクロSDカードも使えるようになりました。
今までは内部ストレージがデフォルトのSDカード領域として
認識されていたのですが、3.1からは
  • /sdcard   :本体
  • /sdcard-ext :マイクロSD
といった感じでアクセスできるようになりました。

また、「ファイラ」というアプリケーションが増えていました。

ファイラを実行してみると、以下のような画面になりました。
起動時にアクセス先を決めれるようです。



今作っているファイラよりも機能が豊富だった場合は、開発を止めてしまおうかと少し思いましたが、そんなに機能は多くないので、このままファイラの開発を進めようと思います。

ではまた。

2011/06/19

Honeycomb用ファイラ(α版)をリリース

四月ぐらいから少しずつ作っていたHoneycomb用のファイラですが、
ファイルの閲覧、移動、コピー、削除ができるようになったのでα版としてリリースしました。

アプリケーション名は、「HCエクスプローラー」です。

アイコンもGIMPで作ってみました。 やっぱり絵心が無いとこういうのはキツいですね。。

HCエクスプローラのアイコン。
蜂の巣の中にファイル等が放り込まれてる感じにしています

このアプリケーションのウリは、PC用のOSのエクスプローラにある、
「ツリー表示」機能を持っているというところ(だけ)です。

今後追加する機能(優先度順):

  • ファイルのリネーム
  • ファイル移動時に発生する、ファイル名衝突への対応(今は問答無用で上書きしてます)
  • ファイルの検索機能
  • 日付によるファイルのソート機能(現在はファイル名、ファイルサイズのみ)
  • ツリーの領域と、ファイル一覧の領域のサイズを可変にする(今は固定です)
  • SMBフォルダへのアクセス
  • ZIPファイルの解凍、圧縮
ではまた。

2011/06/18

Android+JCIFS

twitterを久しぶりに見に行ったら、
質問メッセージが届いていることに気づきました。



質問の回答と備忘録を兼ねて、記事を書いておきます。


JCIFSでSMB接続する時の使い方。

まずはJCIFSのソースコードをダウンロード。
http://jcifs.samba.org/

私はjcifs-1.3.15.zipをダウンロードしてきました。

Androidのプロジェクトを作って、
srcフォルダ内に、jcifs1.3.5/srcのjcifsフォルダをコピーします。
するとjcifs.httpとhttpsクラスで色々警告がでるので、httpとhttpsクラスをまるごと削除します。
(今はhttpとhttpsは使う予定なし)

smbクラスだけ使えればいいのですが、smbクラスも他のクラスに依存しているようなので
ひとまず他のクラスも消さずにとっておきます。


また、AndroidManifest.xmlにを追加しておきます。

SMBFileの使い方は、Java.ioのファイルとほとんど同じ感じで使えます。

こんな感じ。

//SMBファイルを使うサンプル
 //例外処理コードは省いてます
    public SmbFile[] getSmbFileList(String aSmbURL) throws IOException
    {
       SmbFile file = new SmbFile(aSmbURL);
       SmbFile[] fileList=file.listFiles();
 
   System.out.println(file.getName());
       int i=0;
       for (i=0;i>fileList.length;i++)
       {
        System.out.println(fileList[i].getName());
        }
       return fileList;
 
 return null;
    }



JCIFSのsampleフォルダに、わらわらとサンプルコードがあるので、
目的別にサンプルコードを漁ると良さそうです。

注意点
SMBに限ったことでは無いのですが、ファイル操作系やネットワーク通信系の処理は、
メインスレッドで行わずに、Threadを継承したクラスか、AsyncTaskを使った方が良いです。

私の場合は、ファイル/ネットワーク系の操作はThreadを継承させたクラスを作り、
Javaのコンソールアプリとしてある程度作った後にAndroidに組み込む方式をとっています。


ではまた。

2011/06/17

Android ndk-buildの小ネタ

昨日、今日とAndroid+OpenCV2.2についていろいろとチャレンジしてましたが、
結局サンプルを動かす以外のことは出来ませんでした。

サンプル自体もかなり複雑なことをやっているようで、
私には何が何だか分からず。

というわけで、Android+OpenCV1.1のバージョンに戻すことにしました。

Android+OpenCV1.1の場合は、ここのサイトから、android_opencv.tar.gzをダウンロードし、
好きなプロジェクトフォルダを作成した後jniフォルダを作り、その中に展開したものを放り込みます。

あとはプロジェクトフォルダに移動して ndk-buildを実行するだけです。
早い・簡単・安定の3拍子。(関数はOpenCV2.2に比べれば減りますが)



昨日、今日と色々いじった結果、ndk-buildも中でmakeを呼んでいることが分かりました。
また、make用のオプションコマンドもある程度渡せることが分かりました。

んで、ndk-build時の小ネタは、 -j オプションをつけるということです。
-jオプションを指定することで、複数のジョブを同時に走らせることができるようになります。

-jオプションの数値をいじったときの、Android+OpenCV1.1のプロジェクトのビルドにかかる時間を測ってみたので書いておきます。

  • なし: 2分45秒
  • -j3:1分  
  • -j6:32秒 
  • -j8:35秒
  • -j20:38秒

私のPCに載っているCPUは6コアなので、 -j6オプションが一番効率が良さそうです。
ジョブを増やしすぎると、スレッドの切り替えによるオーバーヘッドかなにかで
処理時間が少しずつ長くなっているようです。

マルチコアCPUが載っているPCを使っている方は、ndk-buildの際は-jオプションをつけると
ビルド待ちの時間が減るのでは無いでしょうか。


ではまた。

2011/06/15

Android用 OpenCV2.2のインストール

この前のOpenCVのインストールガイドを翻訳していた時に、
Androidも対象になっていたことに気がつきました。

なので今日はAndroid用のOpenCV2.2をセットアップしたいと思います。

svnクライアントとCmakeは既にインストールされていたので、
NDKとOpenCVのソースコードを取得することに。

NDKをAndroid developersからダウンロード。
現時点ではr5cになってました。
/home/kouji/Google/android-ndk-r5c に解凍。

ソースコードをダウンロード。
svn co https://code.ros.org/svn/opencv/trunk/opencv

SWIGをUbuntuソフトウェアセンターで検索。
swigをインストール。

Ant,JDK,AndroidSDKは既にあります。

NDKへのパスをエクスポート。
export ANDROID_NDK=~/Google/android-ndk-r5c 



kouji@kouji-ubuntu:~/Google/opencv/android$ sudo sh ./scripts/cmake_android.sh
loading initial cache file ../CMakeCache.android.initial.cmake
-- Using default path for toolchain /opt/android-toolchain
-- If you prefer to use a different location, please define the environment variable: ANDROID_NDK_TOOLCHAIN_ROOT
CMake Error at android/android.toolchain.cmake:187 (message):
  neither nor /opt/android-toolchain does not exist!
略)

と、いろいろとメッセージが出てきました。

ANDROID_NDK_TOOLCHAIN_ROOT も設定しないとダメみたいですね。
でも、Android-toolchainはどこにあるのやら。


OpenCVのWikiを調べていると、Android experimentalというもの中に、
cd android-cmake/scripts
./get_ndk_toolchain_linux.sh $WORK
という記載がありました。

ファイルをダウンロードして、
get_ndk_toolchain_linux.shの中身を見てみると、

cd $destination/android-ndk-r5b
NDK=`pwd`
$NDK/build/tools/make-standalone-toolchain.sh --platform=android-5 --install-dir=./android-toolchain

となっていました。
Android NDKのbuildフォルダ内にstandalone-toolchainを作るスクリプトがあるみたいです。
コマンドをちょっといじって、

cd ~/Google
./android-ndk-r5c/build/tools/make-standalone-toolchain.sh --platform=android-8 --install-dir=./android-toolchain

とやってみると、
Copying prebuilt binaries...
Copying sysroot headers and libraries...
Copying libstdc++ headers and libraries...
Copying files to: ./android-toolchain
Cleaning up...
Done.

toolchainができたようです。

このパスをExportします。

export ANDROID_NDK_TOOLCHAIN_ROOT=~/Google/android-toolchain

OpenCVのAndroidフォルダに移動して再チャレンジ。
もう一回

-- Using default path for toolchain /opt/android-toolchain
-- If you prefer to use a different location, please define the environment variable: ANDROID_NDK_TOOLCHAIN_ROOT
CMake Error at android/android.toolchain.cmake:187 (message):
  neither nor /opt/android-toolchain does not exist!

まだtoolchainが見つからないようです。

ヘルプメッセージに載っていた、シンボリックリンクを作る方式に変更。
sudo ln -s ~/Google/android-ndk-r6c /opt/android-ndk-r5b
sudo ln -s ~/Google/android-toolchain /opt/android-toolchain


またチャレンジ

kouji@kouji-ubuntu:~/Google/opencv/android$ sudo sh ./scripts/cmake_android.sh
loading initial cache file ../CMakeCache.android.initial.cmake
-- Using default path for android NDK /opt/android-ndk-r5b
-- If you prefer to use a different location, please define the environment variable: ANDROID_NDK
-- Using default android API level android-8


~略~
--   GUI: 
--     GTK+ 2.x:                   FALSE
--     GThread:                    FALSE
-- 


~略~
--     Install path:               /opt/android-ndk-r5b/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/user/armeabi-v7a
-- 
--     cvconfig.h is in:           /home/kouji/Google/opencv/android/build
-- -----------------------------------------------------------------
-- 
-- Configuring done
-- Generating done
-- Build files have been written to: /home/kouji/Google/opencv/android/build
kouji@kouji-ubuntu:~/Google/opencv/android$ 

Makefileができたー

cd build
sudo make -j8

5分ぐらいでビルド完了。
(ビルド中はPCの全コアが100%近く使われてました。makeはマルチコア対応のようです)

インストールします。
sudo make install

Installing: /opt/android-ndk-r5b/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/user/armeabi-v7a/share/opencv/3rdparty/lib/liblibtiff.a
等と表示され、ライブラリがインストールされました。

インストールは無事終わったようなので、今度サンプルを動かしてみたいと思います。

ではまた。



2011-06-21 追記
Android+OpenCV2.2の組み合わせで、独自のプロジェクトを作った場合の
使い方がよく分からず、結局Android+OpenCV1.1の組み合わせに戻ってしまいました。
残念。。。

2011-06-23
OpenCV2.3でもう一度挑戦したら、何とか出来ました。