1カ月集中講座
Intelの開発ボード「Galileo 2」の楽しみ方 第2回
~LinuxとArduinoの混在コードを書いてみる
(2014/10/16 06:00)
「Galileo」(以下、Gen.2を含む)は完全互換かどうかは別にして「Arduino」と互換性を持ち、「Arduino IDE」を使って開発できることが特徴となっている。Arduino IDEはシンプルで扱いやすいので、それが開発に使えるというのが利点だ。
もっとも、Galileoを単なるArduinoと考えると、普通のArduinoを買った方がいいのでは? という気にもなる。Galileoは標準的なArduinoの2倍以上の価格が設定されているので、Arduinoでできることをやっている程度ではコストパフォーマンスが悪すぎるだろう。
そこで今回は、Galileoの利点の1つとして誰もが思いつ付くであろうLinuxと混在のコードを試してみたい。
最新のArduino IDEをインストールする~Galileo用Arduino IDEの正体
前回で紹介したようにGalileo Gen.2を利用するにはIntelが配布している最新のArduino IDE 1.5.3を利用する必要がある。それがどこにあるのか、どうやってインストールするのかを簡単に説明しておこう。
Galileo向けのツールやドライバはIntelのダウンロードセンターからも入手できるが、分かりにくい上に全て揃っていない。フォーラムに投稿されたこのページがまとまっていて分かりやすいので、ここから必要なファイルをダウンロードするといいだろう。
最低でも必要なのは「Arduino IDE」である。Arduino IDEにはWindows版、Linux版(32bitと64bit)、MacOS版が用意されている。本稿ではWindows版を使って説明していくことにしたい。
Windows版は7-Zip形式のアーカイブなので、展開できるツールをあらかじめ用意しておく。ダウンロードしたアーカイブ(執筆時点ではarduino-windows-1.0.3.7z)を展開するとarduino-1.5.3-Intel.1.0.3という名前のフォルダが得られるので、フォルダごとC:ドライブのルートディレクトリなど分かりやすいところに展開してしまえばOKだ。また、展開後にはフォルダ名をarduino-1.5.3に変えておくといいだろう。
続いて、GalileoにACアダプタを接続して起動させた後(所要時間は1~2分)、GalileoのMicro USBポートとPCをUSBケーブルで接続する。不明なデバイスが発見された場合は先に展開したフォルダ以下の「arduino-1.5.3\hardware\arduino\x86\tools」にあるドライバをインストールしよう(arduino-1.5.3\drivers以下は標準のArduino用ドライバなのでGalileoには対応しない点に注意)。
ドライバの導入に成功するとGalileoがシリアルポートとして認識される。
シリアルポートとして認識されたことを確認したらArduino IDEを起動させる。先に展開したディレクトリにある実行ファイルarudinoがIDEの本体なので、これを起動させればいい。
起動後、まず前回に紹介したようにメニューから「ツール」→「マイコンボード」を選び「Intel Galileo Gen2」を選択しておく。
さらに「ツール」→「シリアルポート」を選び、先に認識されたCOMポートを選択しよう。これで利用する準備が整う。
なお、メニューの「ヘルプ」→「Firmware Update」でGalileo本体のファームウェアアップデートが可能だ。Galileo Gen.2に関しては市販されている機体のファームウェアが今のところ最新なのでアップデートの必要はないが、将来的にアップデートが行なわれることはあり得るので覚えておいて欲しい。
さて、以上のようにしてインストールしたArduino IDEでGalileo上で動作するスケッチ(Arduinoにおけるアプリケーションのこと)を開発するわけだ。
Arduinoは「Arduino言語と呼ばれる簡易な言語でプログラミングができる組み込みマイコン」と説明されることがある。ただ、特別な言語が存在するわけではなく、Arduino言語の実態は、C++言語のクラスライブラリとプリプロセッサを使ってC言語やC++言語の少しハードルが高い面を緩和した(隠した)言語である。
実際、Galileo用に配布されているArduino IDEのディレクトリツリーを調べてみるとgcc(コンパイラ)が格納されていて、スケッチのビルドに利用されている。そして前回述べたように、Arduino IDEで作成するGalileoのスケッチはLinux上で動作するユーザープログラムに過ぎない。つまり、Galileo用のIDEはLinux上で動くユーザープログラムを作成する開発環境とも言えるわけで、その点が標準的なArduino(AVRマイコンを積んだマイコンボード)との大きな違いといえる。
というわけなので、Galileo用に配布されているArduino IDEでは、Linuxで使われている極めて豊富なライブラリをほぼそのまま利用できる。それをArduinoの簡易なハードウェア制御コードが混在できるのがGalileoの特徴で、また標準的なArduinoにはない利点になると思われる。
マルチスレッド(pthread)を使ってみる
一般的なArduinoではマルチスレッドはサポートされていない。タイマー割り込みを利用するなどしてマルチスレッド的なことは工夫次第で可能だが、込み入ったことをしようとするとマルチスレッドに必要な機能を自力で作り込む必要が出てくるので、大変だろう。
一方、Linux上で動作するGalileoのスケッチでは、標準的なスレッドの機能……POSIXスレッドを利用できる。これは標準的なArduinoに対する無視できないアドバンテージになるかもしれない。
ところで、ご存じの方も多いだろうがPOSIXスレッドを利用するには「libpthread」(POSIXスレッドライブラリ)をリンクする必要がある。Arduino IDEのコンパイラオプションは「arduino-1.5.3\hardware\arduino\x86」の下にある「plartform.プラットフォーム名.txt」というファイルに設定されていて、Windows版であればplatform.win.txtを参照するとどういったライブラリがリンクされているかが分かる。
コンパイル時のオプションは「# X86 compile patterns」というセクションに記されているが、執筆時点のバージョンの設定ではMathライブラリ(算術演算ライブラリ)とlibpthreadがリンクされていた。従って、特別な設定なしにPOSIXスレッドの利用が可能だ。
というわけで、早速POSIXスレッドのテストコードを実行してみよう。
【リスト】POSIXスレッドのテストコード
#include <pthread.h> #define LED 13 void *loop_thread(void *param) { static int blink = 0; while(1) { blink ^= 1; digitalWrite( LED, blink); delay(500); } return NULL; } void setup() { pthread_t thread; pinMode(LED, OUTPUT); pthread_create( &thread, NULL, loop_thread, NULL ); Serial.begin(9600); } void loop() { Serial.println("main thread"); delay(1000); }
このテストコードはpthreadを使って起動したスレッド側でDigital 13に接続されているLED(Galileo上のオンボードに装備されているLED)を500msおきに点滅させ、メインスレッド側ではシリアルポートに対して“main thread”というメッセージを1秒おきに出力させている。
このスケッチをGalileoで実行してみると、ボード上のLEDが点滅することが確認できるだろう。同時にArduino IDEのメニューから「ツール」→「シリアルモニタ」を開くと“main thread”というメッセージも確認できるはずだ。
つまりメインスレッドとは別にスレッドが1つ動いていることを目で確認できるわけだ。
説明は不要かもしれないが、このサンプルコードではloop_thread()という関数を別スレッドとしてpthread_create()を使って起動させている。ptheadの使い方はLinux上のそれと変わりがないので、pthreadに慣れている人なら違和感なくArduinoのコードと混在できると思う。簡単な手続きで複数のスレッドを操れるというのはArduinoに対するGalileoの利点の1つになるだろう。
ネットワークソケットを使った受信メールチェック
サンプルコードを少し発展させて、メールサーバー(POP3サーバー)にアクセスして未読メールの数に合わせてLEDの点滅を変えるという例を紹介してみたい。
POP3サーバーに対するアクセスはArduinoのEthernetライブラリを用いればさほど難しくない。さらにGalileoでは、UNIXのSocket APIを用いたコードも利用できる。
どちらが楽かと問われると悩ましいところだが、長い歴史を持ちWindowsなどにも移植されたSocket APIは利用例やライブラリが大量にあるので、使い回しやすく、参考にしやすいといった利点はあるかもしれない。
そこで本稿でもPOP3サーバーへのアクセスにSocket APIを利用してみることにする。といっても、古典的なSocketのコードをArduinoの洗練された(?)コードに混ぜるのは気が進まない(というか汚い)ので、POP3サーバーのアクセス部分をGalileo用Arduino IDEのライブラリとしてまとめてしまうことにする。
Arduino用のライブラリは、本体のコード(C++コード)とヘッダファイルを1つのディレクトリにまとめて格納しておくだけで作成できる。そのディレクトリがArduino IDEにインストール可能なライブラリになる。
本稿のためにSocket APIを使ってPOP3サーバーにアクセスし未読メール数を返す「SimplePOP3」というライブラリを作成してみた。コード本体は少々長く、また古典的なSocketコードでArduinoっぽくないので、アーカイブ(SimplePOP3.zip)として提供する。本稿にはヘッダファイルだけ掲載しておきたい。
【リスト】SimplePOP3.h
#ifndef _SIMPLEPOP3_H #define _SIMPLEPOP3_H 1 #define MY_TRACE_PREFIX "SimplePOP3" #define POP3_PORT 110 #define BUFSIZE 256 #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <trace.h> #include "Arduino.h" class SimplePOP3 { private: char hostname[BUFSIZE]; char username[BUFSIZE]; char password[BUFSIZE]; char s_buf[BUFSIZE]; char r_buf[BUFSIZE]; struct hostent *host; struct servent *srv; struct sockaddr_in sa; int sock; int transmit( char *); char *receive(void); public: SimplePOP3(void); int begin(String, String, String, int); int begin(String, String, String ); String sendCommand(String); int stat(void); void end(void); ~SimplePOP3(void); }; #endif
SimplePOP3は残メール数を調べるためだけに作成したライブラリなので使い方は簡単だ。
【リスト】SimplePOP3の使い方
SimplePOP3 pop3; // 接続する pop3.begin("your_pop3_host", "yourname", your_password"); // 残メール数を取得する int n_mails = pop3.stat(); // 切断 pop3.end();
begin()メソッドで指定されたサーバーに指定されたユーザー名とパスワードで接続し、stat()メソッドで残メール数を得ることができる。終わったらend()メソッドで切断という手続きだ。
やや余談になるが、Arduinoではインスタンスの生成や消滅にnew/delete演算子を使わない習慣があるようだ(Arduinoのリファレンスにnew/delete演算子の記述がない)。Arduinoコードの実体はC++なのでnew/delete演算子は使えるが、おそらくポインタのようなことをあまり考えずにコードが書けるように、という配慮から無視しているのだろう。
従って、Arduinoの標準的なコードではインスタンスの生成と消滅が変数スコープで行なわれることになる。Arduino用のライブラリを作成する際にはその事情を多少、考慮する必要があると思われる。
具体的には、例えばコンストラクタにリソースの確保などを記述するのは慎重にした方が良さそうだ。変数スコープが外れるまでリソースを握りっぱなしになるからで、begin()メソッドなどを作ってそこで明示的に行を確保し、end()メソッドで開放する方がArduinoでは使い勝手が良くなると思われる。
もう1つ、「SimplePOP3.h」では「trace.h」というヘッダファイルをインクルードしているが、これはGalileo用Arudinoのトレース機能だ。trace_debug()、trace_err()といったメソッドを使ってトレースをLinux上のファイル(具体的には後述)に出力できる。使い方はソースコード本体のSimplePOP3.cppを見てほしい。
SimplePOP3.zipをダウンロード
残メール数に応じてLEDの点滅を変える
では、残りを仕上げることにしよう。まず、ライブラリをインストールしなければならない。ダウンロードしたSimplePOP3.zipを展開してSimplePOP3フォルダを取り出しておく。Arduino IDEでメニューから「スケッチ」→「ライブラリを使用」→「ライブラリをインストール」を選択してSimplePOP3フォルダを選べばインストールできる。
その後、「スケッチ」→「ライブラリを使用」のプルダウンメニューからSimplePOP3を選択しておく。
メインのArduinoコードは次のようなものだ。
【リスト】シンプルメールチェッカー
#include <SimplePOP3.h> #include <pthread.h> #define LED 13 #define DELAY_MAX 500 int n_mails = 0; void *loop_thread(void *param) { static int blink = 0; int msec = DELAY_MAX; while(1) { if( n_mails > 0 ) { blink ^= 1; if(n_mails > 4 ) msec = 100; else { msec /= n_mails; } } else blink = 1; digitalWrite( LED, blink); delay(msec); } return NULL; } void setup() { pthread_t thread; pinMode(LED, OUTPUT); pthread_create( &thread, NULL, loop_thread, NULL ); } void loop() { SimplePOP3 pop3; if( pop3.begin("your_pop3_server", "yourname", "yourpassword" ) > -1) { n_mails = pop3.stat(); pop3.end(); } delay(1000*60*5); }
SimplePOP3を使ってメールの残数を取り、残数に従ってLEDの点滅の頻度を変える(残数がゼロなら点灯しっぱなし)というコードである。LEDの点滅は別スレッドで行なっているのでメインのloop()内部ではPOP3から残数を取るだけという簡単な処理で済む。
実際に使う際にはbegin()メソッドのパラメータに実際のメールサーバーのホスト名、ユーザー名、パスワードに変えて試して欲しい。そしてもちろん、GalileoのEthernetをルーターに繋がったHubに接続しておく必要がある。
先にも少し触れたが、SimplePOP3ではtrace_debug()関数を使ってPOP3サーバーとのやりとりをトレース出力している。Gallileoのトレース機能は、標準では「/tmp/log.txt」にtrace_debug()の出力、「/tmp/log_er.txt」にtrace_err()の出力が書き込まれている。
前回紹介したFTDI製のシリアルケーブルを使ってシリアルコンソールを開き、Galileoにrootでログインして「/tmp/log.txt」を見るとPOP3サーバーとのやりとりが正常に行なわれているか調べることができる。
画面例のようにプリフィックスとしてMY_TRACE_PREFIXに設定したプリフィックスが付加され、どのコードのデバッグ出力かが区別できる仕組みだ。デバッグの1つの方法として覚えておくと役に立つだろう。
もちろん、「Serial」を使ってシリアルコンソールにデバッグ出力を出すというArduino的なやり方も悪くないと思う。トレース機能を使うかどうかはお好み次第だが、トレース機能はtrace_enable()という関数を呼ぶことで簡単に出力を止めることができるので、いろいろな用途で使えると思う。
なお、今回は電子工作的な煩雑さを避けるためにオンボードのLEDを出力として使ったが、もちろんLCDを繋いでメール残数などを表示するといったことも可能だ。今回取り上げたようなPOSIXスレッドやSocket APIといったUNIX/Linuxでお馴染みのコードと、Ardunoのシンプルなハードウェア制御コードを混在できるのはGalileoならではだ。
無論そうしたコードはArduinoとの互換性が失われるが、冒頭でも述べたようにGalileoをArduino的に使っていてもGalileoのメリットは得られない。今回の例をGalileoを活かす方向性の1つとして参考にしていただければ幸いだ。
さて次回はGalileoのファームウェアに目を向けたい。Galileoの標準のファームウェアは極めてシンプルだが、Linuxとして見ると機能がかなり乏しいのが難点。それを強化する方法を紹介する予定だ。