今回は、Arduinoを使って、外部につけたスイッチが押されたかどうか?を判定してみます。
まず、一番簡単な基本形は、以下図の様な回路になります(LEDはオプションです。なくても良い)。
スイッチSW1が押されていないとき、IOポートD3はプルアップされてHIGHが読めます。SW1が押されると、今度はGNDに落ちて、LOWが読めます。結果、digitalRead()で、「HIGHが読めたら押されていない、LOWが読めたら押されている」と、判定できます。
まあ、これで良いのですが、loop()の中で、常に判定していないといけないし、一度押されたことがわかったら、一旦離されるまで待って、ようやく次の押下を受け付けられるので、ちょっと複雑なプログラムになっています。
プログラム:switch_iop.ino
スイッチを押すと、シリアルモニタにメッセージが届くようになっていますので、シリアルモニタを開いて確認してください。
あまり検出間隔を狭めると、チャタリングを検出してしまうみたいです。ダブルクリックみたいに、短い間隔で2回プッシュすると、数回(2回以上)の応答が返ってきます。loop()内のdelay()を調整すると、短いプッシュへの反応は悪くなりますが、チャタリングは無視できるようですので、使い勝手については要調整、です。
もう少し別な観点で、外部割り込みを使ってみると、どうなるでしょうか?Arduinoでは、attachInterrupt()関数で外部割り込みを設定することができます。
attachInterrupt (digitalPinToInterrupt (pin_switch), isr_switch, FALLING);
まず、外部割り込みに指定できる端子番号は、機種によって異なるので、リファレンスを見てどの端子を使うか決めて、pin_switchに設定しておきます。isr_switchは、割り込みが起きた時に呼び出される関数で、割り込みサービスルーチン(ISR;Interrupt Service Routine)と呼びます。「何も引数を持たず、何も返さない」と、リファレンスに記載されていますので、関数のプロトタイプは、「void isr_switch (void)」と、なるはずです。
ISR内では、他の割り込みが保留されてしまうため、様々な制約があります。よくリファレンスを読み、理解して使いましょう。他の割り込みを保留して処理するので、なるべく速やかに処理を終了して、抜ける必要があります。今回は、スイッチが押されたことだけがわかれば良いので、以下のような感じになります。
static int psh_switch=0;
void isr_switch (void)
{
psh_switch++;
}
psh_switchには、pin_switchが押下された回数がカウントされていますので、あとは、loop()の中でpsh_switchを参照して、スイッチが押された時の動作を記述します。
void loop (void)
{
:
if (psh_switch>0)
{
// スイッチが押された
psh_switch--;
// 押された時の処理
:
}
:
}
attachInterrupt()の最後の引数FALLINGは、指定した端子がどういう状態になったら割り込みをかけるか?という条件で、FALLINGは、「立ち下がりエッジ」が発生したとき、です。立ち下がりエッジは、一度HIGHレベルに戻らない限り、連続して発生しませんので、I/Oポートを監視する方法と違って、LOWになった(押された)→HIGHに戻るのを待つ→次のLOWをまつ、みたいなプログラムは不要になっています。
プログラム:switch_int.ino
I/Oポートを監視することによる検出時と同様に、チャタリングっぽい動きをする時があります。こちらは検出間隔では調整できないので、loop()の中で、psh_switchを判定したタイミングで、キャンセルする必要があるようです。
if (psh_switch>0)
{
// chattering cancel
delay (100); // この時間内に起きた割り込みによるpsh_switchのカウントを
psh_switch=0; // キャンセル(=0でクリア)する。
}
割り込みは、用途によっては非常に重要(有効)ですが、色々と制約も多く、よほどのことでない限りお勧めしません。使用する場合は、機種によって、割り込み端子として使えるポート番号が決まっていたり、ISR内の制約が沢山あったりしますので、よく理解して使うようにしてください。
さて、I/Oポートを使って、LOW/HIGHを判定する方法では、1スイッチに1本のI/Oポートを割り当てる必要が出てきます。もし、独立動作でLCDなどの表示装置を使う場合、ユーザインターフェイス(U/I)の構築では、あっという間にスイッチが4個くらいは必要になってきます。
たったこれだけのインターフェイスで、スイッチ4個必要です。周辺に接続するモジュールによっては、I2Cでの通信が必要になって2本、別なモジュールはSPIで3〜4本、サーボnケでPWM端子がn本、……。I/Oポートが足りるうちは良いですが、たかがスイッチ1つに、1本のI/Oポートを割り当てられないケースもあります。
そのような場合に時々目にするのが、アナログ端子でスイッチの押下を判定する方法です。ここでは、その方法を試してみます。
最初に回路図(完成版)を示してしまいますが、以下図のようになります。
右半分のLEDは、誤判定していないことを視覚的に確認するために使います、オプションです。
左半分の回路を使い、アナログ端子1本(A0)で、4つのスイッチのどれが押されたか?を判定します。
どのスイッチも押されていなかった場合、SW4で回路が遮断されているので、回路に電流は流れず、結果としてA0端子はプルアップされ、Vccレベルとなります。
A0の電位(SW0)=Vcc
便宜上、全てのスイッチが押されていない状態をSW0と表現します。
もし、SW3が押された場合、電流はVcc→R0→R1→R2まで流れた後で、SW3を通ってGNDに落ちてしまうので、この時のA0の電位は以下の様になります。
A0の電位(SW3)=(R1+R2)÷(R0+R1+R2)×Vcc
他のスイッチが押された場合も、同様にA0の電位を計算することができます。改めてすべての組み合わせを示します。
A0の電位(SW1)=0×Vcc
A0の電位(SW2)=(R1)÷(R0+R1)×Vcc
A0の電位(SW3)=(R1+R2)÷(R0+R1+R2)×Vcc
A0の電位(SW4)=(R1+R2+R3)÷(R0+R1+R2+R3)×Vcc
A0の電位(SW0)=Vcc
うまく抵抗値R0〜R3を選んでやると、A0の電位はすべて異なる値を示すように調整できるはずです。
上図で考えて、各スイッチを押したときの電位が互いに一番離れるように、5Vを4等分するには、各抵抗値に以下の関係を持たせるのがベストだと、オームの法則から導き出すことができます。
適切にR0を決定すると、そこからR1〜R3の抵抗値が決定できます。SW1を押し続けた時に流れる電流Imax=5V/R0が最大値となるので、システム(ボード)の消費電流の観点から、許容すべき電流値になるようにR0を決めます。ここでは、R0=1kΩにしておきます。この時、最大の電流Imax=5mAです。
1kΩとか、2kΩの抵抗はまだしも、333Ωとか667Ωとかいう抵抗を探してくるのは大変かもしれませんね。今回の例では、スイッチが4個程度で比較的余裕があるので、多少、異なる値(近い抵抗)を使っても、大勢に影響は無いと思います。
筆者は、333Ωの代わりに330Ωの抵抗、667Ωの代わりに1kΩと2kΩの抵抗を並列にして使いました。ほぼ、理想通りに構成できました。
多少、理想と異なる抵抗値を使った時でも、最適な閾値を使って判定するために、判定値をテーブルにしておきます。
const int n_switch=4; // number of switch
const int r0_switch=1000; // 実際の回路に使った抵抗値をセットする
const int r1_switch=333;
const int r2_switch=667;
const int r3_switch=2000;
const int ana_switch[n_switch + 1]=
{
0,
int (1024L*r1_switch/(r0_switch + r1_switch)),
int (1024L*(r1_switch + r2_switch)/(r0_switch + r1_switch + r2_switch)),
int (1024L*(r1_switch + r2_switch + r3_switch)/
(r0_switch + r1_switch + r2_switch + r3_switch)),
1023
};
int detect_switch_1 (void)
{
// detect switch state
int idx;
int val;
val=analogRead (pin_switch);
for (idx=1; idx<=n_switch; idx++)
if (val<(ana_switch[idx - 1] + ana_switch[idx])/2)
break;
return (idx%(n_switch + 1));
}
ana_switch[ ]は、analogRead()で読み出すことができる、10ビットAD変換器の読み取り値です。10ビットですので、読み出せる数値は0〜1,023の1,024(=10ビット)段階です。Vcc=5Vがフルスケールですので1,023に相当し、0Vが0と読み出されます。その間の中間的な電圧は、リニアに0→1,023の値を取ります。各スイッチが押された時に観測されるべき電圧値を、AD変換器から読み出せる値に換算した数値をana_switch[ ]に保持しています。
蛇足ですが、5Vを1,024分割しているので、analogRead()で読み出した値の"1"は、5V/1,024≒4.88mVを指します。この値を「1LSB」と呼びます。ATmega328のデータシートによると、AD変換器の絶対精度は「±2LSB」ですので、実際の電圧に対して、上下にそれぞれ4.88×2≒9.76mVの誤差を含んでますよ、という意味に取れます。読み出した値が仮に2.5Vだった場合、2.49〜2.51[V]の可能性があります、ってことですね。
※ATmega328のデータシートによると、正確には、VREF(デフォルト設定では5V)に等しい電圧は、1,024に相当します。従って、デジタル値1,023は「VREF−1LSB」に相当する電圧であることになります。さらに補足すると、「VREF−1LSB」以上のアナログ電圧は全て1,023と読み出されます。
※ADC=Vin/VREF×1,024という関係式が記載されているので、Vinについて解くと、Vin=ADC×VREF/1,024となります(ADC=0〜1,023の読み出し値)。
通常は、detect_switch_1()を読み出せば、どのスイッチが押されているか?を知ることができます。しかし、もし、いずれかのスイッチを押した/離した瞬間に、アナログ値を読みに行ったら、どうなるでしょうか?スイッチの状態が変わって、回路が変化すると、その状態に合わせて電流値が変化、A0の電位が変わります。その、まさに変わっている途中の状態を読み出してしまったら?押してもいないスイッチが「押された」と誤判定してしまうかもしれません。
このような誤判定を回避するには、2回連続で同じスイッチ状態が読み出されるまで、読み出し続ければOKです。少し考えるとわかりますが、最大4回読み出せば、そのうちの2回は連続して同じ状態が読み出せるはずです。
int detect_switch (void)
{
// detect switch state, until same result 2 times
int psh1;
int psh2;
psh1=detect_switch_1 ();
psh2=detect_switch_1 ();
if (psh1!=psh2)
{
psh1=detect_switch_1 ();
if (psh1!=psh2)
{
psh2=detect_switch_1 ();
if (psh1!=psh2)
{
// ERROR:
psh1=PshSwE;
}
}
}
return (psh1);
}
もし、4回読み直しても、4回とも異なる結果になった時には、回路に問題が生じている可能性がありますので、あきらめてエラー状態を返します。
まとめると、プログラムは以下の様になります。
プログラム:switch_ana.ino
今回のプログラムは、LEDでも動作確認できるようになっています。
例題として4スイッチで試していますが、Arduino UNO(ATmega328)やMicro(ATmega32U4)の10ビットAD変換器は変換精度±2LSBですので、もう少し増やしても、誤判定せずに検出できると思います。限界値については、基板のノイズ対策や、部品の精度などに依存するので、確かなことは言えませんが……。
アナログ端子1本で、4個のスイッチ入力を検知できるようになりました。もちろん、アナログ入力端子も、用途によっては重要なので、1本といえどもスイッチ入力なんかに使っていられないかも、知れません。用途次第で最適な方法を選べるように、色々な方法を頭に入れておくと、応用が利きますね。
どうでも良いこと(?)かもしれませんが、スタータキットに入っていた、4端子のプッシュスイッチは紛らわしくて良くないです。4端子の内、2端子毎に内部で接続されていて、スイッチを押すと、全4端子がショートします。どの端子とどの端子が、初めからショートしているのか、足の出ている方向や、裏の模様で判断しないとならないのですが、何回か方向を間違えて、思った通りに動かず、やり直す羽目になりました。はんだ付けした回路でこんなことになっていたら、きっと泣いてました。
前節のアナログ入力による方法だと、複数のスイッチを同時に押したことを検知することができません。前節で示した回路図から明確なように、複数のスイッチを同時に押すと、より番号の小さいSWが押されたと判定されてしまいます。
同時に押したことを検知できるように、スイッチと抵抗を並列にしてみたらどうか?と考えてみました。
しかし、これはだいぶ難しい。各抵抗値の選択もさることながら、スイッチ4個に対して、同時に押されることまで検知しようとすると、識別すべき状態数は24=16通りになります。前節の、直列に並べた場合は、高々5状態でしたので、5V(1,024段階)を4分割すれば良かったのですが、今度は、16分割しないとなりません。さすがに、AD変換器の精度、電源の揺らぎ、抵抗値のバラツキ、(既成)配線抵抗値、スイッチのON抵抗値、等々、考え合わせると、正しく判定できないケースが出てくるかもしれません。
今回は、そこまで頑張るモチベーションが無いので、ここで検討は打ち切りますm(1__)m。
もし、挑戦する場合は、直列だった時と異なり、並列になったことで各抵抗値を独立に設定できなくなっており、最適な抵抗値を探すのが一番大変な作業だと思われます。
ここまで、スイッチの検出について試してきましたが、最終的な目的は、Arduino Microにマクロキーボードのまねごとをさせること、なのでした。「#2-1 Arduinoを使った電子工作の部屋(2nd Season)」で述べたように、2機目にMicroを選んだのは、この機体をPCにUSB接続したときにHID(Human Interface Device)として機能する、という、UNOには無い特徴を持っていたからです。
HIDには、大きく、キーボードとマウスの機能があります。マウスにするには、Arduino側に入力デバイスが不足していますので、機会があれば、追々、実験してみます。今回は、キーボードとしての機能を検証します。
ArduinoのREFERENCEによると、Keyboardクラスを使用するには、「32U4またはSIMDベースの機体(Leonardo、Esplora、Zero、Due、MKR、ファミリ)が必要」なので、UNO(やMEGA)では動作しません。
回路は、スイッチ4つのアナログ入力で判定する物を使います。並列の方は最後まで完成させていないので、直列の方です。各スイッチを押したときに、Arduinoにプログラムした、特定のキーストロークをPCに送信するようにして見たいと思います。
#include <Keyboard.h>
void setup (void)
{
Keyboard.begin ();
}
Serialの時と同じで、初期化と使用宣言は、これで終わり。簡単(1^−^)。この状態で、一度、コンパイルしてプログラムをArduino Microに書き込みます。
すると、いつものように「デバイスの切断・接続サウンド」が鳴ったハズです。そうしたら、Arduino IDEで、「ツールメニュー ⇒ シリアルポート」を確認してみてください。少なくとも、筆者の所では、今まで使っていた(COMx)のと異なる、新しいポート番号(COMy)が接続されています。HIDを有効にしたことで、シリアルポートの番号が変わってしまったようです。IDEで使用するポート番号を、新しいもの(COMy)に切り替えておかないと、シリアルモニタが開かなかったり、次回のプログラム転送が失敗したりします。
では、押されたスイッチ番号に従って、適切なキーストロークをPCに送ってみましょう。
キーを制御するには、2つの方法があります。
適切なキーを一回押す動作をしたい場合は、Keyboard.write()を使うのが楽です。さらに、決まった文字列を連続して押下したい場合には、Keyboard.print()を使うと、もっと楽になります。
例)
Keyboard.write ('A'); // 大文字のキー"A"を押す(押して、一定時間後に離す)
Keyboard.print ("hello, world"); // 連続して、対象のキーを押す。
Keyboard.println ("I have a pen."); // Serialと同様、最後に改行キーを押す。
Keyboard.write()だと、1キーだけですが、Keyboard.print()を使うと、文字列を構成する文字に対応するキーを連続して押してくれます。
ときどき、複数のキーを同時に押したいケースがあります。SHIFT+'a'とか、CTRL+'w'等、モディファイキーとの組み合わせを送りたい場合などです。
例)
Keyboard.press (KEY_LEFT_CTRL);
Keyboard.write ('w');
Keyboard.release (KEY_LEFT_CTRL);
// すべてpress/releaseを使う
Keyboard.press (KEY_LEFT_SHIFT);
Keyboard.press ('a');
delay (50);
Keyboard.releaseAll ();
すべてのキーを離すときには、Keyboard.releaseAll()が使用可能です。キーを押している時間(delay()のパラメータ)が長すぎると、PC側でオートリピートがかかってしまうので、注意が必要です。
上のサンプルコードで使ったモディファイキーのコード(KEY_LEFT_CTRLなど)については、ArduinoのHP ⇒ REFERENCE ⇒ Keyboardから見つけることができます。どうやら、モディファイキー以外のキーコードは、基本的にASCIIコードみたいですね。ASCIIコード以外には、REFERENCEで参照できるモディファイキーだけがサポートされているみたい。
本物のキーボードは、「USBコード」とか、「スキャンコード」とか言うコードでPCに送信されるみたいなので、一枚皮が被ってしまっています。お陰で、「スキャンコード」みたいな、馴染みのないコードを記述する必要なく、簡単にキーを送信できるのは良いのですが、少し特殊(OS依存)な、「再生・停止などのマルチメディアキー」、「日本語の半角/全角キー」などの送信ができないのは、残念でなりません。
マルチメディアキーを備えていないキーボードは沢山ありますので、マルチメディアキー用のキースイッチだけ持たせて、Microでマルチメディアキーを押させる、と言う使い道も良いな、と思っていたので……。
※後日、「Winキー」は、KEY_LEFT_GUIとKEY_RIGHT_GUIで押せることがわかりましたので、訂正しました。
では、4つのスイッチに、それぞれ機能を割り当ててみましょう。
switch (psh)
{
case PshSw1:
Keyboard.write ('A');
break;
case PshSw2:
Keyboard.println ("hello, world");
break;
case PshSw3:
Keyboard.press (KEY_LEFT_CTRL);
Keyboard.press ('a');
delay (50);
Keyboard.releaseAll ();
break;
case PshSw4:
Keyboard.press (KEY_LEFT_SHIFT);
break;
default:
break;
}
プログラム(全体):switch_kb.ino
テストの方法ですが、Arduino Microにプログラムを転送したら、メモ帳などのテキストエディタを開いて、Arduinoの外付けブレッドボードに構成したスイッチを押してみてください。
何か、お決まりのキーを押すようなことがそれほどあるか?ということですけど、まあ、正直、あんまりないですね(1^^;)。昔、EWSでviと言うエディタを使って、回路をコーディングしていた時は、viに与える一連のコマンドを別ウインドウで打ち込んでおき、それをviにコピー&ペーストして即席のマクロみたいにして使ってました。
そういう用途があれば、使い道があるかもしれませんが……。そもそも、今時の気の利いたエディタだと、マクロ機能が備わっていたりしますからね、必要ないですね。
ちなみに、筆者はvi使いなので、Windowsでもたまにvi使いますよ。変に高機能だったり、勝手に括弧を閉じたりするエディタは、正直、大きなお世話で、邪魔にさえ見えるので……。
寄り道が長くなりました。さて、キーボードのテストは終了です。
この後、Keyboardを使用しないプログラムをArduino Microに転送すると、再び元のポート番号(COMx)に戻るので、IDEの設定を変更するのをお忘れなく。
さて、Windowsを使っている間は、あまり使い道が思いつかないのですが、ゲームだと、どうでしょうか?アクションゲームの最中に、アイテム欄を開いて、ちょちょっとアイテムの整理したりとか、決まった操作をする場合に、それをマクロにしておいて、スイッチ一つで一連のコマンドを入力できたら楽ですよね?
あまり沢山ゲームを持っていないので、かなり昔のゲームですが、「Biohazard 6」の「マーセナリーズ」で、赤と緑ハーブを拾ったときに、アイテム欄を開いて、ハーブを合成し、タブレットに変換するところまでのコマンドをマクロにしてみます。
あっ、もしかしたら、筆者のキー入力がカスタマイズされているかもしれないので、皆さんの所では、一部のキーが違うかも?です。
要は、一連のキーを次々押していけば良いので、まずは、以下を試してみました。
Keyboard.print ("grr g");
これは、ダメでした。
アクションゲームって、リアルタイムにキー状態を読んで判定することが多く、「押された」というイベントを記憶しておいて、後からその動作を発動する、というような作りになっていないことが多いのです。たぶん、Keyboard.print()が、各キーを押している時間が短すぎて、ゲームのサンプリングに引っかかっていないためだと思われます。
つまり、ちゃんとKeyboard.press()とKeyboard.release()の間にdelay()で適切な時間を確保しながら、ストロークを再現していく必要があるということです。
以下の様にして見ます。もしかしたら、ゲームやPCの性能によって、delay()の数値は多少前後するかもしれないので、皆さんの環境でうまくいかなかった場合は、時間を調整してみてください。
Keyboard.press ('g'); delay (50); Keyboard.releaseAll (); delay (50);
Keyboard.press ('r'); delay (50); Keyboard.releaseAll (); delay (50);
Keyboard.press ('r'); delay (50); Keyboard.releaseAll (); delay (50);
Keyboard.press (' '); delay (50); Keyboard.releaseAll (); delay (50);
Keyboard.press (' '); delay (50); Keyboard.releaseAll (); delay (50);
Keyboard.press ('g'); delay (50); Keyboard.releaseAll (); delay (50);
今度は、反応しました。しましたが、「タブレットに変換する」所が、反応はしているのですが、うまく変換してくれません。手で入力すると、スペースキーを2回押すだけでOKなんですが……。途中まで反応しているみたいなので、試しに、スペースキーの押下を1回分多く、3回にしたところ、期待通りの動きになりました。
???なんでか?は、よくわかりませんでした。Microの挙動が怪しいのか、うまくタイミングが合っていないだけなのか?
まあ、ひとまず、一番利用シーンが多いであろう、ゲームでのマクロ機能が実現できそうな感触を得たので、ひとまずOKとしておきます。
ここから、ちょっと脇道にそれます。暫くArduino Microを使ってみて感じた雑感です。
「#2-1 Arduinoを使った電子工作の部屋(2nd Season)」でも触れたように、ATmega32U4ベースのMicroは、MCUが2個搭載されていたUNOと違って、1個しかMCUが搭載されていないため、ちょっと使い勝手が変わっています。
Arduino IDEでプログラムをボードに書き込もうとすると、一旦シリアルポートが切断されます。その後、ボードへの書き込みが終了して、Arduino側のプログラムが動作し始めると、再びシリアルポートが接続されるようになっています。よって、Windowsでボードへの書き込みを実行すると、「デバイスが取り外されたサウンド」と、しばらくして「デバイスが接続されたサウンド」が、一々鳴り響きます。
void setup (void)
{
Serial.begin (9600);
while (!Serial)
;
}
Microは、「while (!Serial) ;」というおまじないが必要な機種に分類されます。ただし、必ず必要か?というと、そうでもありません。なくても動きます。ただし、以下の記述は、たぶん失敗します。
void setup (void)
{
Serial.begin (9600);
Serial.println ("hello, world");
}
この記述は、Arduinoが起動してすぐに、Serialに文字列を送るのですが、Windows側では、このころ、ようやくデバイスの再接続処理が行われていて、まだデータを受け取る準備ができていないのです。Windows側でシリアルポートが再接続されたころに、のこのことシリアルモニタを開いても、モニタに"hello, world"は表示されません。
void setup (void)
{
Serial.begin (9600);
delay (3000);
Serial.println ("hello, world");
}
Serialに文字列を送る前に、Windows側でのデバイスの再接続に十分な時間を待ってやれば良いでしょうか?上の例では、3s待ってから文字列を送信しています。このケースだと、あらかじめシリアルモニタを開いていれば、受信できるかもしれません(3sが十分な時間だった場合)。しかし、シリアルモニタを開いていない時に、Serial.println()が実行されると、後からシリアルモニタを開いても、文字列は表示されません。
void setup (void)
{
Serial.begin (9600);
while (!Serial)
;
Serial.println ("hello, world");
}
そこで、上リストの様に、おまじないが登場するのですが、これもまた良し悪しで、「while (!Serial) ;」があると、PC側でシリアルモニタを開くまで、この部分でブロックされてしまいます。PC側でシリアルモニタを開かない限り、プログラムが先に進まないのです。
デバッグ用にシリアルモニタを使う場合、常にシリアルモニタを表示しておかないと、いろいろと問題が生じます。
恐らく、何バイトかPCにデータを送ると、それ以上受け取ってもらえず、その時にSerialにデータを送ろうとすると、Arduino側がプチフリーズ(?)するみたいです。その上、送ろうとしたデータは送られておらず、どこかに消えてしまいます。まあ、シリアルモニタを閉じている以上、受け取る意思が無いわけで、データが消えるのは構わないのですが、プチフリーズが起きるのは好ましくありません。
たぶん、UNOでも同じ様にプチフリーズしているのでしょうね……、ただし、ATmega16U2(USB⇔UART変換マイコン)の方で。プログラムを実行しているATmega328には影響が出ないので、気にならなかったものと推測します。
PCに接続したときにHIDとして認識されるMicroを使ってみたくて、購入したのですが、UNOとMicroを同時に使おうとすると、IDEのターゲットを一々変更しなくてはならなかったり、シリアルの使い勝手が異なっていたり、色々と面倒だった、と言うのが正直な感想です。