« 2013年1月 | トップページ | 2013年3月 »

【ArduinoUNO】HID USBキーボードシミュレータ?を作ってみた。(その3)

Arduinoで作ったUSBキーボードシミュレータ
お次は、シリアル信号を送る側(マスターPC)の作成で、会社で使うためにWindowsPCが対象。
んで手慣れたのC#を使った。

今回作成したArduinoには、Windowsよりシリアル(9600bps 8N1)でUSBキーボードのキーコード(※)を送る。

  • ※:【USBキーボードのキーコード表】(www.usb.orgの資料より抜粋)

    USB英語101のキー種+[SHIFT]
    04aA
    05bB
    06cC
    07dD
    08eE
    09fF
    0AgG
    0BhH
    0CII
    0DjJ
    0EkK
    0FlL
    10mM
    11nN
    12oO
    13pP
    14qQ
    15rR
    16sS
    17tT
    18uU
    19vV
    1AwW
    1BxX
    1CyY
    1DzZ
    1E1!
    1F2@
    203#
    214$
    225%
    236^
    247&
    258'
    269(
    270)
    28Enter
    29Esc
    2ABackspace
    2BTab
    2CSpace
    2D-_
    2E=+
    2F[{
    30]}
    31\|
    33;:
    34'
    35`~
    36,<
    37.>
    38/?
    3AF1
    3BF2
    3CF3
    3DF4
    3EF5
    3FF6
    40F7
    41F8
    42F9
    43F10
    44F11
    45F12
    46Print Screen
    47Scroll Lock
    48Pause
    49Insert
    4AHome
    4BPage Up
    4CDelete
    4DEnd
    4EPage Down
    4F
    50
    51
    52
    53NumLock
    5410Key[/]
    5510Key[*]
    5610Key[-]
    5710Key[+]
    5810Key[Enter]
    5910Key[1]
    5A10Key[2]
    5B10Key[3]
    5C10Key[4]
    5D10Key[5]
    5E10Key[6]
    5F10Key[7]
    6010Key[8]
    6110Key[9]
    6210Key[0]
    6310Key[.]
    6510Key[App]
    6610Key[Power]
    6710Key[=]
    68F13
    69F14
    6AF15
    6BF16
    6CF17
    6DF18
    6eF19
    6FF20
    70F21
    71F22
    72F23
    73F24
    74Execute
    75Help
    76Menu
    77Select
    78Stop
    79Again
    7AUndo
    7BCut
    7CCopy
    7DPaste
    7EFind
    7FMute
    80VolumeUp
    81VolumeDown
    82Locking CapsLock
    83Locking NumLock
    84Locking ScrollLock



    E0Left-Ctrl
    E1Left-Shift
    E2Left-Alt
    E3Left-Windows
    E4Right-Ctrl
    E5Right-Shift
    E6Right-Alt
    E7Right-Windows



■実行させてみる

操作としては
  1. COMポートの接続
  2. USBキーボードコードの入力。そして送信

だけ。
【一応C#の方も以下にリストを載っけてみる。】

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports; //port
namespace Serial_USBkeyboard
{
public partial class Form1 : Form
{
//デリゲート宣言
private delegate void Delegate_RcvDataToTextBox(string data);
//コンストラクタ
public Form1()
{
InitializeComponent(); //お約束
//シリアル情報を入手
string[] ports = SerialPort.GetPortNames();
foreach (string com in ports)
{
comboBox1.Items.Add(com); //リストに追加
}
}
//シリアル応答用のテキストボックスへの書込み
private void Append_TextBox(string data)
{
//! 受信データをテキストボックスの最後に追記する.
if (data != null)
{
textBox2.AppendText(data);
}
}
//シリアル受信割り込み処理
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//受信割り込み
//シリアル受信→テキスト表示
try
{
//! 受信データを読み込む.
string data = serialPort1.ReadExisting();
//! 受信したデータをテキストボックスに書き込む.
Invoke(new Delegate_RcvDataToTextBox(Append_TextBox), new Object[] { data });
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
//シリアル・バッファ送信
private void send_bytes(byte[] buf)
{
if (serialPort1.IsOpen == false)
{
MessageBox.Show("Port don't Open");
return;
}
int i = 0;
foreach (byte b in buf)
{
serialPort1.Write(buf, i, 1); //1Byte send
i++; //Shift
System.Threading.Thread.Sleep(80); //Wait
}
}
//キーコード文字列の送信
private void send_string(string s)
{
//送信ボタン押下
if (String.IsNullOrEmpty(s)) return;
string[] data = s.Split(','); //文字列','でバラす
byte[] buf = new byte[data.Length]; //同容量分バッファキープ
int i = 0;
foreach (string d in data)
{
if (d.Length != 2) continue;
buf[i] = Convert.ToByte(d, 16); //文字をHEX数字として変換
++i; //
}
send_bytes(buf);
}
//[接続]ボタン押下処理
private void button1_Click(object sender, EventArgs e)
{
//[接続]ボタン押下
if (String.IsNullOrEmpty(comboBox1.Text)) return;
serialPort1.PortName = comboBox1.Text;
serialPort1.Open(); //シリアルオープン
if (serialPort1.IsOpen) //ポートチェック
{
groupBox2.Enabled = true; //開けた→送信欄許可
}
else
{
MessageBox.Show("Port(" + comboBox1.Text + ")が開けませんでした。");
}
}
//[送信]ボタン押下処理
private void button2_Click(object sender, EventArgs e)
{
send_string(textBox1.Text); //入力した文字列送信
}
//テキストで[Enter]押し
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Enter) //[改行]チェック
{
button2_Click(sender, e); //送信ボタンと同じ処理へ
}
}
//[キーリリース]送信
private void button3_Click(object sender, EventArgs e)
{
send_string("ff,ff"); //オール0送信
}
//Macのスクリーンショット([SHIFT]+[Command]+'3')
private void button4_Click(object sender, EventArgs e)
{
send_string("ff,02,00,e7,00,ff"); //[SHIFT]+[Command]
send_string("ff,02,00,e7,20,ff"); //[SHIFT]+[Command]+'3'
send_string("ff,ff"); //Key Release
}
//Macの[全角/半角]切替
private void button5_Click(object sender, EventArgs e)
{
send_string("ff,00,00,e7,00,ff"); //[Command]
send_string("ff,00,00,e7,2c,ff"); //[Command]+' '
send_string("ff,00,00,e7,00,ff"); //[Command]
send_string("ff,ff"); //Key Release
}
//[PageUp]キー押下
private void button7_Click(object sender, EventArgs e)
{
send_string("4b");
}
//[PageDown]キー押下
private void button8_Click(object sender, EventArgs e)
{
send_string("4e");
}
//[Windows]キー押下
private void button6_Click(object sender, EventArgs e)
{
send_string("e7");
}
}
}

  • 'a'というキーを押したことにするなら入力欄にキーコード"04"と入れて送信。
  • スレーブPCにMacを対象として接続しているなら、ボリュームを上げたいときには、キーコード"80"を入力して送信。
  • 'A'であれば、[SHIFT]+'a'ということになるので、まずはSHIFTの"e0"
    ','を挟んで、'a'である"04"と入力して"e0,04"として送信する。
  • すると、スレーブ側のPCに'A'(=[SHIFT]+'a')とキーを押したことが伝えられる。
  • これらArduinoの動きはシリアル通信でマスターPC側にフィードバックしてくるようにした。
    これで実際にマスターPC側に送られたかどうかわかるようにした。
  • またベタに8バイト送れるように、前後を"FF"で囲んで入力すれば良いようにした。
    先ほどの'A'ならば、"FF,02,00,04,FF"と入力。
  • これを利用するとMacでスクリーンショットを取る("[Command]+[SHIFT]+[3]")等の複数キーによる動作も…
      "FF,00,00,e7,00,ff""FF,00,00,e7,20,ff""FF,FF"
    てな文字列を順次に送ると行える。
    ([Command]キーは[Windows]キーと同じなので、"e3"または"e7"
  • 後はこれらコードを適当にテキストフィルにまとめて、それを読み込ませて、任意の時間間隔で送信するようにすれば、最近流行(?)の「PC遠隔操作」ならぬ、「PC近隣操作」のできあがり〜♪。w


現在の構成の欠点は、めくら撃ちでキーを打つため、HDDのアクセスが遅くて動作に遅延が生じた場合、それ以降の動作がメタメタになってしまうこと。
そのため、キーの送信間隔を十分に取るようにした。

ということで、この治具を使った会社のWindowsPC(30台分)の設定自動検査も終わって、開発終了!。

| | コメント (4) | トラックバック (0)

【ArduinoUNO】HID USBキーボードシミュレータ?を作ってみた。(その2)

ArduinoUNOでの16U2に書込む"Arduino-keyboard-0.3.hex"は、偉いもので相方である328P側にとっては単なる通信シリアル相手で、16U2が「USBキーボード・モード」であっても「シリアル通信モード」であっても同じプログラムでかまわないようになっていた。


よく考えられているのぉ。

Wikipediaの「スキャンコード」の項で実際にデータとして用いるUSBのキーコードについてお勉強。
φ(・ω・ )

  • ほ〜8バイト送ることで、そのキーを押したことになるのか。
  • これだけだと押っぱなしなので、再度“全て0の値の8バイト”を送ることで、そのキーを離したことになるのね。
  • さらに詳細を追うと…この8バイト(buf[0]〜[7]とする)の内、1個のキーを送るなら使うのは[0]と[2]があればいいわけか。
  • buf[0]にはALTやらCTRLやらSHIFTの装飾コードを代入すると。
    内容値(HEX)
    左CTRL01(HEX)
    左SHIFT02(HEX)
    左ALT04(HEX)
    右CTRL10(HEX)
    右SHIFT20(HEX)
    右ALT40(HEX)

  • んで、buf[2]には実際のキーコードを代入っと。
まぁ大雑把にそんな流れであった。

そんなことをふまえてArduinoのプログラムを作成。
前回でPC同士のシリアル通信処理を改良して、HID USBキーボード向けに作り替えた。
//Arduino USB Keyboard simulator
//Using "Arduino-keyboard-0.3.hex" for ATMega 16U2
#include <SoftwareSerial.h>
SoftwareSerial softSerial(2,3); //SoftwareSerial(rxPin, txPin)

//Grobal value
uint8_t buf[8] = { 0 }; /* Keyboard report buffer 8Byte */

//【初期設定】
void setup()
{
//Hardware Serial PD0:RX,PD1:TX
Serial.begin(9600); //bitrate
//Software Serial port PD2:RX,PD3:TX
softSerial.begin(9600); //bitrate
softSerial.println("SoftwareSerial"); //Initial test word
delay(200);
}

//【ループ処理】
void loop()
{
//マスターからの命令待ち
if( softSerial.available() ){
//マスターからの命令
int key;
key = softSerial.read(); //命令受け取る
//どんな命令か、チェックする。
if( true == decode_keycode(key) )
{
//使えるデータならマスターにデータのfeedback
softSerial.print( "Key(0x" );
softSerial.print( key,HEX );
softSerial.print( ")..." );
softSerial.print( "[0]=0x" );
softSerial.print( buf[0],HEX );
softSerial.print( ",[2]=0x" );
softSerial.println( buf[2],HEX );
//16U2側にHID USBキーボードデータ(8バイト)送信
Serial.write(buf,8); //send 8byte data to serial port
releasekey(); //send 8byte(all null data) to serial port
}
}
}

//【キーを放す】
void releasekey()
{
buf[0] = 0; //[0] clear
buf[2] = 0; //[2] clear
//Send Buffer:8Byte([0]---[7])
Serial.write(buf, 8); // Release key
}

//【受け取った命令のデコード】
bool decode_keycode(int d)
{
// Attribute Code
// keycode [0].... 0:LeftCTRL, 1:LeftSHIFT, 2:LeftALT, 3:LeftGUI
// 4:RightCTRL,5:RightSHIFT,6:RightALT,7:RightGUI
// 0xE0〜0xFF....Attribute
if( d == 0xE0 ) buf[0]+=0x01; //0x40 is [CTRL]
if( d == 0xE1 ) buf[0]+=0x02; //0x80 is [SHIFT]
if( d == 0xE2 ) buf[0]+=0x04; //0xC0 is [ALT]
if( d>=0xE0 ) return false; //装飾データ→false
else buf[2]= d; //キーデータとしてそのまま採用→true
return true;
}


■【2013/2/24:追記】
前回のArduinoのスケッチは「1個のキー」にしか対応していなかったので、新たに「複数キーの同時押し」にも対応できるように、内容に変更した。
んで出来たのがこれ_。
#include <SoftwareSerial.h>
SoftwareSerial softSerial(2,3); //SoftwareSerial(rxPin, txPin)
//Grobal value
uint8_t buf[8] = { 0 }; /* Keyboard report buffer 8Byte */
//【初期設定】
void setup()
{
//Hardware Serial PD0:RX,PD1:TX
Serial.begin(9600); //bitrate
//Software Serial port PD2:RX,PD3:TX
softSerial.begin(9600); //bitrate
softSerial.println("SoftwareSerial"); //Initial test word
delay(200);
}
//【ループ処理】
void loop()
{
//マスターからの命令待ち
if( softSerial.available() ){
//マスターからの命令
int key;
key = softSerial.read(); //命令受け取る
if( key == 0xff )
{
//DEBUG Mode
uint8_t debug_buf[8] = { 0 };
int i=0;
while(1){
if( softSerial.available() ){
//マスターからの命令
int key;
key = softSerial.read(); //命令受け取る
if( key==0xff ) break;
debug_buf[i]=key;
++i;
if( i>7 ) break;
}
}
for( i=0;i<8;++i )
{
softSerial.print(debug_buf[i],HEX); //feedback
softSerial.print("-");
}
Serial.write(debug_buf,8); //send 8byte data to serial port
softSerial.println("");
delay(100);
}
else
{
//どんな命令か、チェックする。
if( true == decode_keycode(key) )
{
//使えるデータならマスターにデータのfeedback
softSerial.print( "[0]=0x" );
softSerial.print( buf[0],HEX );
softSerial.print( ",[2]=0x" );
softSerial.print( buf[2],HEX );
if( buf[3]!=0 )
{
softSerial.print( ",[3]=0x" );
softSerial.print( buf[3],HEX );
}
softSerial.println("");

//16U2側にHID USBキーボードデータ(8バイト)送信
Serial.write(buf,8); //send 8byte data to serial port
delay(100);
releasekey(); //send 8byte(all clear) to serial port
}
}
}
}
//【キーを放す】
void releasekey()
{
//Send Buffer:8Byte([0]---[7])
buf[0] = 0; //[0] clear
buf[1] = 0; //[1] clear
buf[2] = 0; //[2] clear
buf[3] = 0; //[3] clear
buf[4] = 0; //[4] clear
buf[5] = 0; //[5] clear
buf[6] = 0; //[6] clear
buf[7] = 0; //[7] clear
Serial.write(buf,8); //Release
}
//【受け取った命令のデコード】
bool decode_keycode(int d)
{
bool ret=false; //非送信
// Attribute Code
// keycode [0].... 0:LeftCTRL, 1:LeftSHIFT, 2:LeftALT, 3:LeftGUI
// 4:RightCTRL,5:RightSHIFT,6:RightALT,7:RightGUI
// 0xE*....Attribute keycode
switch( d )
{
case 0xE0:
buf[0]|=0x01; //[Left-CTRL]
break;
case 0xE4:
buf[0]|=0x10; //[Right-CTRL]
break;
case 0xE1:
buf[0]|=0x02; //[Left-SHIFT]
break;
case 0xE5:
buf[0]|=0x20; //[Right-SHIFT]
break;
case 0xE2:
buf[0]|=0x04; //[Left-ALT]
break;
case 0xE6:
buf[0]|=0x40; //[Right-ALT]
break;
default:
buf[2]= d; //1個目のキー代入
ret = true; //送信
break;
}
return ret;
}

これをArduinoに書込むことで、HID USBキーボードの心臓部が出来上がった。
お次ぎはマスターPC側のソフトを作らねば。>つづく

| | コメント (2) | トラックバック (1)

【ArduinoUNO】HID USBキーボードシミュレータ?を作ってみた。(その1)

会社では、業務以外に課のIT委員担当っていうのに任命されている。
何をするのかと言えば、課内で所有するPCの設定が、会社の基本設定になっているかどうか(「スクリーンセーバは5分。かつパスワード付き。」「フォルダの共有は禁止」「ウイルスバスターは最新のパターンファイルになっているか」等)のチェックを目視で行う役を担っている。

課では13人がいて、各人約2台保有。
なもんで30台弱のPC設定チェックを半期に一度行う。
が検査項目も24個もあって、これがまぁ〜時間がかかって結構面倒な作業。
さらにXPやら7やらまちまちのOSなPCらだから、それぞれOS別にチェックする場所が違ってきて手間ばっかりかかる。(;ω;)

なので、Arduinoを使って…

  • マスターPCからシリアル通信でArduinoを経由して検査対象(スレーブ)PCへキーボードの信号を送り、PC操作をマスター側からバッチ処理を行って、検査時間の短縮化を謀ろうと考えてArduinoの購入をしたのであった。



まぁ。「(HID)USBキーボードシミュレータ」というべきかな。

購入したArduino UNOには「ATMega16U2」、「ATMega328P」2つのチップが乗っていて、I/F部分を担当しているのが「16U2」というチップ。
コレは普段、シリアル通信処理を行っている。
これも「プログラマブルIC」なわけで、

FWを書換えることでHIDのUSBキーボードにとして振る舞う
ことが出来るのであった〜♪。ヽ(´▽`)/

ただ、面倒なことにHIDのUSBキーボードのFWに書換えると今度はシリアル通信機能ができなくなるので、Arduinoのメイン側(328P側)のFWを書き込むことが出来なくなってしまう。その時は元のFWに書き戻して使わなければならない。

ってことで、手始めにインターネットからそれぞれのFWをダウンロード。

  • USBキーボード用FW
    Arduino-keyboard-0.3.hex
  • UNO用シリアル通信FW
    Arduino-COMBINED-dfu-usbserial-atmega16u2-Uno-Rev3.hex

で、この2つのFWを書き込むための環境を整える。
どうすりゃ簡単か、ネットで調べてあれこれ考えたあげく…

  • 秋月でその昔に買っていたAE-UM232RをICSP書き込み治具として使う
    やり方にした。
  • 治具と言っても線を引っ張っただけだし、書き込みソフト(Windows)も既に「ardude-GUI」がある。
    いろんな人がこの方法で、運用しているのでまず壁に打ち当たることもないでしょう。
  • 案の定書込むことが出来て、「16U2」のFW書換が可能となった。




お次は、制御ソフトする側を作らねば…。>つづく

| | コメント (0) | トラックバック (0)

展示品のコンデジを買ってみた。

購入品をブログにアップするために、撮影に使っていたデジカメが壊れた。

このデジカメ、以前二個一で作り上げたヤツ。
そのキャノンのデジカメが、「レンズエラーを検知しました」と逝ってしまった。
キャノンのデジカメお得意のこの現象が発生。
とうとうこの時が来たか。

さて、デジカメをどう調達する??
直すか?。
…最近はデジカメもものすごく安くなって、わざわざジャンクを直しても、さほど優越感がわかない。
ヤフオクで落札するにしても送料1000円以上するし…。

先日エヴァを観た帰りに、せっかくなので横浜ビブレのビックカメラのアウトレット店に寄ってみた。
このお店、展示品を数量限定品として売っている。
そのなかで展示品だった「SONY WX100」が9480円を発見。
邪魔にならない大きさはもちろんのこと、ネットで調べた時「手ぶれや、動画に強い」とのこと。
2世代前だが、現行売られているWX170,WX200とスペック的にもさほど変わらない。
近くの店員に声をかけ、ケースから出してもらってじっくり眺める。

やっぱり、展示品だけあって正面の筐体の金属に傷・ヨゴレが目立っていた。
ここがおいしい。
ここからは腕の見せ所。

新品ではないので、こうゆうウイークポイントを突いて、店員との値切交渉の会話開始♪。

さすがに「9000円は無理」と言うことで、結果9080円となった。


とどめとして支払いにはビックのポイント588円が残っていたので、これに追加して、結局8500円で購入することが出来た。

これからのブログの写真はこれを使って撮影すっどー。

| | コメント (2) | トラックバック (0)

映画の日に“エヴァQ”観に行ってきた。

2月1日は映画の日。
と同時に、会社での健康診断(午前中)の日でもあった。
バリウムを飲んで、レントゲンを撮った後下剤をもらうのだが、その効き目で午後はお腹が急降下
って言うことで午後は半休を取って、早退

しかし本来の目的はただ一つ。
映画の日なので1000円で“エヴァQ”を観るのだ!。w

会社の帰りでもある桜木町駅到着(横浜市)、映画館へGO〜。
そろそろ、映画としての効力も失せたのか、客はまばらであった。
しかも男性率9割。w
ま。観客を見に来ている訳じゃないんだからいいんだけど。

個人的には満足だったぁ。(1000円っていう成果もあってか)
今までの謎を解きながら、かつ(観客を突っ放すように)更なる謎を振りまきながら、物語は進む。

監督が特撮好きだから、エヴァらもMJ(マイティージャック)号みたいになるやら、バロム1みたいになるやら次回に至ってはキカイダーみたいになっていく。w

あれ、あと1話で完結できんのか??
興行的にも儲かりそうだから、もっと続けりゃ良いのに。

あ〜それにしても、薬が効いて未だにお腹は下り坂。
何食っても、白くなってすぐに出てきちゃう…。

| | コメント (0) | トラックバック (0)

« 2013年1月 | トップページ | 2013年3月 »