Hatena::ブログ(Diary)

Okiraku Programming このページをアンテナに追加 RSSフィード

2012-01-18 「Arduinoスーパーナビゲーション」でTwitterライブラリを紹介頂いた

「Arduinoスーパーナビゲーション」でTwitterライブラリを紹介頂きました

書籍Arduinoスーパーナビゲーション しくみと応用テクニック」で拙作のTwitterライブラリを紹介していただきました。

Arduino電気回路プログラミングの細かい事柄に気を回さなくても、気軽に電子工作を楽しめることから、最近いろいろな作品に使われているマイコン+開発環境です。

初めてマイコンを使った作品にトライするときには、仕様の読み方が分からなかったり、ちょっとした設定のミスでつまずいたりしがちです。この本では、Arduinoで作品作りを始めるための最初の一歩(必要なモノから環境の作り方など)から始めて、いろいろな基本動作(LEDを光らせたり、センサで計測したり、喋らせたり通信したり)をさせるための方法を丁寧に解説していて、スムーズにArduinoの使い方を覚えられるように道しるべになってくれます。


この基本動作の一つ「通信する」の中で、拙作のライブラリTwitterにつぶやく方法を説明していただいています。

また、その応用として、自宅の侵入者を検知してTwitterにつぶやく「自宅警備員」という作品の作り方を紹介。

他にもAndroidとの接続などを含めた8つの作品の作り方が紹介されていて、眺めているだけでいろいろ試してみたくなってきます。


Twitterライブラリもセンサの値などのアウトプット先として手軽に応用できますので、ぜひいろいろなアイデアにトライしてみてください。

2011-12-25 hidden symbolの関数呼び出しをフックする

hidden symbolの関数呼び出しをフックする

Linuxでは一般的なライブラリ関数の呼び出しはLD_PRELOADを使ってフックして、別の動作をさせる(オーバーライドする)ことができます。

(参考: LD_PRELOADでBrainf*ck - Okiraku Programming


しかし、フックできる関数には条件があって、PLT(procedure linkage table)と呼ばれるテーブルを経由して呼び出されているものに限られます。

ライブラリによってはhidden symbolといって、同一ライブラリ内での関数呼び出しがstatic linkになっている場合があり、こうしたものはフック対象外になります。(リンカスクリプトでそういう指定が可能になっている。)

一例としては、libcに含まれる malloc(3) 関数は内部で mmap(2) や brk(2) を呼び出しますが、こうしたライブラリ内部から自身の関数呼び出しする場合は一部static linkとなっておりPLTを経由しないので、LD_PRELOADではオーバーライドできません。

(普通にプログラムmmap(2) や brk(2) を読んでいる場合にはPLT経由になるのでフック可能です。)


こうしたstaticな関数を元のプログラムライブラリを改変せずにオーバーライドしたい場合があります。

(例えば他のベンダが作っているライブラリの動作がマズいのだが既にメンテされてなくてソースもない場合とか)

そんなときは、無理やりコードの一部を書き換えてフックを挿入してしまうという手があります。


具体的には、jmp命令をフックしたいライブラリ関数の先頭に埋め込み、LD_PRELOADで挿入したフック用のライブラリ関数飛ばしてしまえばOK。

シングルスレッド限定ではありますが、フック内部で埋め込んだjmp命令を元の命令列に戻してから元の関数を呼び出せば、前処理/後処理をすることも可能です。


いくつかの関数をフックするために何度も同じような処理を書くのが面倒だったため、C++マクロでフックを簡単に埋め込めるようにする仕掛けを作ってみました。

このコードはx86_64でのみ動作します(命令列で jmp *(%rip); <ADDR> を埋め込んでいるため)。また、左記の命令は14byteあるため、14byte以上の関数でないと正常にオーバーライドできません(でないと隣にある関数破壊してしまう。。)


例えばbrkをフックするコードは以下。ヘッダやMakefileを含む全体は以下のgistに。

https://gist.github.com/1519275

hook_brk.cpp

#include "LibHook.h"    // 面倒なことを全部押し込んだヘッダ
#include <stdio.h>
#include <unistd.h>

// フック関数
int brk_hook(void *addr)
{
	int ret = brk(addr);
	printf("brk(%p) => %d\n", addr, ret);
	return ret;
}
HookInstaller(brk, brk_hook);  // brk(2) を brk_hook()でオーバーライド

このように書くと、対象プログラムのmain関数の実行前(つまりconstructor実行時)に brk の先頭にjmp命令が埋め込まれ、brkを呼び出すとjmp命令を外した後に brk_hook が呼ばれるようになります。brk_hook内でbrkを呼び出しても無限ループになったりせずにオリジナルのbrkが実行されます。brk_hookからreturnすると、jmp命令が再び埋め込まれたのち、hookからの返り値がbrkの返り値であるかのように返されます。*1

これをビルドするには、

g++ -fPIC -shared -o hook_brk.so hook_brk.cpp -std=c++0x -ldl

のようにします。なおC++11 なのはフックの自動挿入/解除の記述を簡単にするためにvariadic templateを使って関数型の指定を自動やらせているため。フック挿入自体*2とは本質的には関係ありません。


何度か malloc するプログラムを、

malloc_hook.c

#include <stdlib.h>
int main(void)
{
	free(malloc(256<<10));
	free(malloc(256<<10));
	return 0;
}

これを

gcc malloc_hook.c && LD_PRELOAD=hook_brk.so ./a.out

のようにビルド & 実行すると、

brk((nil)) => 0
brk(0x240d000) => 0

とフック関数内でprintfしているメッセージが表示され、mallocからの呼び出しでもフックできていることが確認できます。*3


なおフックは

    NSHook_brk::hook.uninstall();

とかやると途中で解除することも可能です。


一種の自己書き換えのような無茶をしているので安全とは言いがたいですが、いざとなったらこのようなことも可能だという例として。


コード

Hook hidden symbol call (x86_64) — Gist

ところで

C++関数ポインタを扱うテンプレートを書く際、template引数に指定された関数型から、その返り値や引数の型をtemplate引数にもつテンプレートクラスAをインスタンス化したい場合って、一般的な方法ってあるのでしょうか。

よくわからなかったので、しかたなくダミーのtemplate関数を用意して、

// インスタンス化したいテンプレートクラス
template <class RET, class... ARGS>
class A {
};

// ダミー関数
template <class RET, class... ARGS>
A<RET,ARGS...> resolve_ftype(RET(*f)(ARGS...))
{
	throw "This function must not be called.";
	return A<RET,ARGS...>();
}

int func(const char*);

typedef decltype(resolve_ftype(func)) A_T;  // => A_T は A<int, const char*> のこと

などとしているのですが、もっといい方法はないのかな。

*1マルチスレッドだと他スレッド関数実行中にjmp命令で上書きする恐れがあるので不可。jmp命令の自動挿入/解除を止めれば大丈夫ですが、この場合はオリジナル関数破壊されてしまうため、自分で1から再実装する必要があります。

*2:要するにdlopen/dlsymで関数アドレスを取得して、オリジナルの命令列を保存した後、フックへのjmp命令列を構築しmemcpyで上書き、という処理です。

*3:ちなみにフック内のprintf内でもバッファ確保のためmallocが呼ばれることがありますので、brkに再突入することがありますが、その場合はjmp命令がはずれているのでフックは呼ばれません。

2011-12-04 Arduino 1.0対応Twitterライブラリを公開

NeoCat2011-12-04

Arduino 1.0対応Twitterライブラリを公開

Arduino 1.0が2011/11/30に正式リリースされました。

新しいArduinoに対応したり、ライブラリの強化などが図られています。(詳しい変更点はRelease Noteを。)

個人的にはEthernetライブラリが強化され、DNSDHCP機能を標準でサポートした点が嬉しいところ*1


さて、これに伴ってTwitterライブラリアップデートしました。

使い方は今までと同様で、簡単にArduinoからTwitterツイートを送信できます。

今まではEthernetDNSが別途必要でしたが、標準でDNS対応したことで導入の手間が不要となりました。

なお0022以前のIDEでも新しいバージョンのライブラリを利用することも可能となっています。その場合は従来通りEthernetDNSライブラリが必要です。


ダウンロードや使い方の説明は、こちら(英語)から。ライブラリに付属しているExampleも参考にしてください。

*1:EthernetDNSやEthernetDHCPと比べると若干バイナリサイズが大きく消費するROMが増加するのが玉にきず?

2011-11-16 AndroidのAutomateIt設定メモ

AndroidのAutomateItで自動で省電力設定etc.

Androidアプリ AutomateItは、電源や無線LANの接続/切断や位置を移動したなど、様々な条件を満たした時に指定したときに指定したアクション(マナーモードにするとかWi-Fiをon/offするとか)を実行できる自動アプリです。しかも無料。複雑な条件が指定できる有料のPro版もあります。


無料板の方でも、がんばって複雑な条件指定をすれば、それなりに高度な自動オペレーションが可能です。

というわけで自分で普段使ってる設定のメモ。省電力のためにマメにWi-Fi設定などを弄るのが面倒なので、充電などをトリガにほぼ完全に自動化しています。

ちなみに表はCSVエクスポート機能で出力した内容です。全部の設定をimportするためのファイルこちらに。


充電スタンドに立てた際にマナーモード解除/外すと自動マナーモード

まずはシンプルなところから。

ルールTrigger(条件)Action(実行内容)
マナー解除[Power Connected Trigger]: External power is connected to the device[Set Sound Mode Action]: Set device' sound mode to Normal (No vibrate)
マナー設定[Power Disconnected Trigger]: External power is disconnected from device[Set Sound Mode Action]: Set device' sound mode to Vibrate

電源接続中は30分バックライトを消さない/外したら30秒で消灯するように

ルールTrigger(条件)Action(実行内容)
スクリーン30分後off[Power Connected Trigger]: External power is connected to the device[Set Screen Timeout Action]: Set screen timeout to 30 minutes
スクリーン30秒後off[Power Disconnected Trigger]: External power is disconnected from device[Set Screen Timeout Action]: Set screen timeout to 30 seconds

ついでに音も鳴らしてみる。

MEDIAS WPですが、スタンドの接触が甘いらしく、立てたつもりがまれに充電されていなかったり、ふいに外れたりするので音を鳴らして確認。

ルールTrigger(条件)Action(実行内容)
電源接続音[Power Connected Trigger]: External power is connected to the device[Play Sound Action]: Play a sound (Missed It)
電源解除音[Power Disconnected Trigger]: External power is disconnected from device[Play Sound Action]: Play a sound (Highwire)

Wi-Fi on/offを自動

複雑な設定の例。家ではWi-Fiを使いたいけど、外に出る時は省電力のためにWi-Fiはoffにしておきたい。けどスタンドから外したら勝手にoffにされると不便だし、GPSを使った位置条件指定も可能ですがそれ自体が電池を食いそう。

というわけで、電源を接続→Wi-Fi on / 電源切断かつWi-Fiアクセスポイント(AP)非接続はWi-Fiをoff、という設定に。

これで家で充電開始した時に勝手にAPにつながり、家を出てAP圏外に出れば勝手にWi-Fiがoffになってくれます。

電源切断/Wi-Fi切断の順序を任意にするために、ルールenable/disableするなどやや複雑な設定になってます。

ルールTrigger(条件)Action(実行内容)
Wi-Fi on[Power Connected Trigger]: External power is connected to the device[Set Wifi State Action]: Set Wi-Fi adapter status to Wi-Fi Enabled
Wi-Fi off<電源切断>[Power Disconnected Trigger]: External power is disconnected from device[Set Wifi State Action]: Set Wi-Fi adapter status to Wi-Fi Disabled
Wi-Fi off<AP切断>[Wifi Network Connection State Trigger]: Network [<Any Network>] is Disconnected[Set Wifi State Action]: Set Wi-Fi adapter status to Wi-Fi Disabled
電源接続→AP切断時Wi-Fi off無効[Power Connected Trigger]: External power is connected to the device[Enable/Disable Rule Action]: Disable rule (Wi-Fi Off<AP切断>)
電源切断→AP切断時Wi-Fi off有効[Power Disconnected Trigger]: External power is disconnected from device[Enable/Disable Rule Action]: Enable rule (Wi-Fi Off<AP切断>)
AP接続→電源切断時Wi-Fi off無効[Wifi Network Connection State Trigger]: Network [<Any Network>] is Connected[Enable/Disable Rule Action]: Disable rule (Wi-Fi off<電源切断>)
AP切断→電源切断時Wi-Fi off有効[Wifi Network Connection State Trigger]: Network [<Any Network>] is Disconnected[Enable/Disable Rule Action]: Enable rule (Wi-Fi off<電源切断>)

Wi-Fi on/off設定にBluetoothも連動してon/off

これはBluetoothイヤホンをたまに使う時があるので、Bluetooth offにしわすれないよう何となく

ルールTrigger(条件)Action(実行内容)
Bluetooth on[Wifi State Trigger]: Wifi adapter status set to Wi-Fi Enabling[Set Bluetooth State Action]: Set Bluetooth adapter status to Bluetooth ON
Bluetooth off[Wifi State Trigger]: Wifi adapter status set to Wi-Fi Disabling[Set Bluetooth State Action]: Set Bluetooth adapter status to Bluetooth OFF

2011-10-22 Arduino用のLSM303DLH(デジタルコンパス+加速度センサ)ライブラリ

ArduinoとProcessingでLSM303DLH

Arduino用のLSM303DLH(デジタルコンパス+加速度センサ)ライブラリ

1つのチップに3軸の地磁気センサ(デジタルコンパス)と3軸の加速度センサを内蔵した、LSM303DLHというチップを搭載したSparkfunのボードがスイッチサイエンスなどから販売されています。お値段も円高効果か3000円を切っていてお買い得。

スイッチサイエンス/商品詳細 LSM303DLH搭載傾き補償付きデジタルコンパス・加速度センサモジュール

f:id:NeoCat:20111022140404p:image:medium

ちなみに地磁気センサ単体だと、水平から傾けた場合に正しく方位がとれません。これは地磁気ベクトルは水平ではなく傾いているため(この傾きを伏角と言います。関東だと49°らしい。結構斜めですよね)。傾きの成分を拾ってしまい、誤差が出ます。そこで加速度センサ重力の方向を検知して、傾きを補償することで、正確な方角を計算する必要があります。


例によって、このセンサをArduinoで使うためのライブラリを作ってみました。

http://neocat.jp/arduino/Library-LSM303DLH.zip


展開してできたLSM303DLHフォルダを、Macならホーム/Documents/Arduino/libraries/ 、WindowsならMy Documents\Arduino\libraries\ に入れ、ArduinoIDE再起動すれば準備完了です。

File→Examples→LSM303DLH→LSM303DLH_sample を開くと、サンプルスケッチを見られます。


このコードをArudinoにアップロードし、ソース中にある通りにLSM303DLHとArduinoを配線してSerial monitorを見ると、加速度センサX/Y/Z、地磁気センサX/Y/Zの値(計6軸)の数値が転送されてくるのが見られます。


f:id:NeoCat:20111022140403p:image:w254:left

また、センサの出力からボードの姿勢を計算して3DでビジュアライズするProcessingコードも、LSM303DLH/samples/LSM303DLH_sample/Processing_SampleProject/RotateXYZ/RotateXYZ.pde に入っています。


プロジェクトファイルProcessingで開いて、シリアルポートの名前を自分Arduinoボードに合わせて修正してから実行すると、冒頭のようなボードの絵が表示されます。そして、実物を回転させると同じように3Dで回転するはずです。

フォント周りでエラーがでる場合は、Monaco-24 をTools → Create Fontメニューから生成してあげてください。


表示されている数値は、6軸の値、およびX軸の方角(裏面にSparkfunと書かれている方向が北だと0°、時計回りに-180°〜179°で表示)です。ちゃんと補償された方角になっているはず。(計算間違ってたらすんません><)



なお、感度などの設定は全てデフォルト値になっています。変更したい場合は、データシートに従ってレジスタの値を

LSM303DLH.write_reg(アドレス, 値);

として書き換えてください。アドレスが00h〜12hは磁気センサ、20h〜は加速度センサレジスタを書き換えることができます。

更新速度は begin() に渡す定数を変えることで変更できます。デフォルトでは、加速度センサはlow-powerモードの10Hz (ローパスフィルタは37Hz)、地磁気センサは15Hzで更新される設定になっています。詳しくはライブラリのヘッダファイルを見てください。