2018年4月7日土曜日

Digispark(ATtiny85)で一発スクリーンショット・ボタン

この前、噛じったDigispark(超小型Arduino)で遊んみます。

新マーティーPCは、富士通のLifeBookのお下がりですが
スクリーンショットが、[Windows]+[Fn]+[Prt Sc]と同時3Keyなのです。
押すのが面倒なのです。
そのスクリーンショットをワン・タッチでやりたいのです。

まずは、Digisparkにタクトスイッチをつけて
そのスイッチをUSB KEYとして定義してみます。

Qiitaのここの下の方に
Digisparkにボタンを追加してタスクマネージャーを起動するという
スケッチの紹介があったのでGitHubのリンクに飛んでいきます。
そのスケッチをコピペしてきます。m(_ _)m
*****
#include <DigiKeyboard.h>
#define KEY_ESCAPE 0x29
const int inputButton = 0;
const int outputLed = 1;
bool outputState = true;
void setup() {
   pinMode (inputButton, INPUT);
   pinMode(outputLed, OUTPUT);
}
void loop() {
   if (digitalRead(inputButton) == HIGH) {
      digitalWrite(outputLed, 1);
      if (!outputState) {
         outputState = true;
         DigiKeyboard.update();
         DigiKeyboard.sendKeyStroke(KEY_ESCAPE, MOD_CONTROL_LEFT | MOD_SHIFT_LEFT);
      }
   } else {
      digitalWrite(outputLed, 0);
      outputState = false;
   }
   DigiKeyboard.delay(10);
}

*****
Arduino IDE v.1.8.3を起動します。
「Digisparkの製作元Digistump LLC(合同会社)のパッケージ」は
この前インストールしています。
まだ、Digispark(ATtiny85)は、接続しないで
[ツール]-[ボード]-[Digispark(Default - 16.5mhz)]を選択します。
[ツール]-[書込装置]-[Micronucleus]を選択します。
[ファイル]-[新規ファイル]すると新しく窓がでてくるので
上記のスケッチを丸ごと貼り付けます。
一旦、[ファイル]-[名前を付けて保存]で適当な名前で保存します。

さて、貼り付けたスケッチは、
 pinMode(0, INPUT);
 pinMode(1, OUTPUT);
とあるので、Digispark(ATtiny85)でのDigital Pinは、
 PB0(pin5):INPUT
 PB1(pin6):OUTPUT
ということになり
DigisparkボードのPB1には、Highで点灯するようにLEDが付いています。

しかし、ボタンでINPUTを直接5Vに釣り上げるのはどうも嫌なので
INPUTは、PULL-UPしておき、ボタン押下でLOWにして動作させたいのです。
そのために、一箇所(赤文字部)、「LOW」に書き換えます。
*****
#include <DigiKeyboard.h>
#define KEY_ESCAPE 0x29
const int inputButton = 0;
const int outputLed = 1;
bool outputState = true;
void setup() {
   pinMode (inputButton, INPUT);
   pinMode(outputLed, OUTPUT);
}
void loop() {
   if (digitalRead(inputButton) == LOW) {
      digitalWrite(outputLed, 1);
      if (!outputState) {
         outputState = true;
         DigiKeyboard.update();
         DigiKeyboard.sendKeyStroke(KEY_ESCAPE, MOD_CONTROL_LEFT | MOD_SHIFT_LEFT);
      }
   } else {
      digitalWrite(outputLed, 0);
      outputState = false;
   }
   DigiKeyboard.delay(10);
}

*****

では書き込みです。
まだ、Digisparkは接続しません。つい、先に繋ぎそうになるんですよね~
[スケッチ]-[マイコンボードに書き込む]します。
コンパイルが終わり、下の欄に
Plug in device now...(will timeout in 60 seconds)」とでたら
Digispark(ATtiny85)をUSBに接続します。
書き込み時は、Digisparkは、何も配線していない方がいいかと思われます。
ボードへの書き込みが完了しました。」と
Micronucleus done. Thank you!」の表示がでます。
「ピポン、ピポン、ピポン」と音が出ますが
5秒後位にアップロードしたスケッチが動き出します。
最初の起動では、右下にデバイスドライバのセットアップが始まり
ほどなく完了します。
デバイスマネージャーを見ると
[キーボード]の所に[HIDキーボードデバイス]が出現しています。
ここで一旦、DigisparkをUSBから取り外して、配線します。
実は、こんな配線ですが(-_-;)
再度、USBに繋いで、デバイスマネージャーに[HIDキーボードデバイス]が出てから
ボタンを押すとタスクマネージャーが起動しました!
何の変哲もないタスクマネージャーですが。
これでボタン1つで複数キー同時押しが実現できることが確認できました!
本題のスクリーンショットに進みます。

まずは、USB KeyboardのKEY CODEを調べます。
内蔵KeyboardとUSB KeyboardのCodeは異なるのです。
USB.orgのPDFの仕様書「USB HID USAGE TABLES」や
この「USBキーボードのキーコード」を参考にさせていただきm(_ _)m

Key Codeは、
・PrintScreen:0x46
・Left-Windows:0xE3(Keyboard Left GUI)
・Fn(Function):0x65(Application?ちょっと自信がないなあ)

最初のオリジナルからの変更点は、赤文字部
DigiKeyboard.sendKeyStroke()の使い方がわからないので
まずは、感で入れてみます。
*****
#include <DigiKeyboard.h>
// #define KEY_ESCAPE 0x29 

#define KEY_LEFT_WINDOWS 0xE3
#define KEY_FN 0x65
#define KEY_PRINT_SCREEN 0x46

const int inputButton = 0;
const int outputLed = 1;
bool outputState = true;
void setup() {
   pinMode (inputButton, INPUT);
   pinMode(outputLed, OUTPUT);
}
void loop() {
   if (digitalRead(inputButton) == LOW) {
      digitalWrite(outputLed, 1);
      if (!outputState) {
         outputState = true;
         DigiKeyboard.update();
//       DigiKeyboard.sendKeyStroke(KEY_ESCAPE, MOD_CONTROL_LEFT | MOD_SHIFT_LEFT); 

          DigiKeyboard.sendKeyStroke(KEY_PRINT_SCREEN, KEY_LEFT_WINDOWS | KEY_FN); 
      }
   } else {
      digitalWrite(outputLed, 0);
      outputState = false;
   }
   DigiKeyboard.delay(10);
}

*****

ん~ん?何も起こりませんね~(´-﹏-`;)

ひょっとして「Print Screen」キーだけでいいのだろうか?
 DigiKeyboard.sendKeyStroke(0x46)
を試しますが、何も起こりません。

それから、かなり色んな組み合わせをやりました.....どれもダメ。

仕方ないのでSourceを見てみることにしました。
「DigiKeyboard.h」は、次のフォルダにありました。

C:\Users\marty\AppData\Local\Arduino15\packages\digistump\hardware\avr\1.6.7\libraries\DigisparkKeyboard

USB仕様書でのKey Codeは、これ。
「Left GUI」が左Windowsキーのことです。
*****
 Keyboard LeftControl  :0xE0:1110 0000
 Keyboard LeftShift      :0xE1:1110 0001 
 Keyboard LeftAlt         :0xE2:1110 0010
 Keyboard Left GUI      :0xE3:1110 0011
 Keyboard RightControl :0xE4:1110 0100 
 Keyboard RightShift     :0xE5:1110 0101
 Keyboard RightAlt        :0xE6:1110 0110
 Keyboard Right GUI     :0xE7:1110 0111
*****

ところが、「DigiKeyboard.h」の中を見ると
これらのキーは、名前にMOD_ がついて、式で定義されています。
数値にしてみると「// =0x」の16進の値が入ることになります。
「MOD_GUI_LEFT」が左Windowsキーのハズです。
なぜ、こんなややこしいことをしているのだろう?
USB仕様書をかなり眺めましたが、わかりません。
*****
#define MOD_CONTROL_LEFT    (1<<0)  // =0x00
#define MOD_SHIFT_LEFT      (1<<1)  // =0x02
#define MOD_ALT_LEFT        (1<<2)   // =0x04
#define MOD_GUI_LEFT        (1<<3)   // =0x08
#define MOD_CONTROL_RIGHT   (1<<4)   // =0x10
#define MOD_SHIFT_RIGHT     (1<<5)   // =0x20
#define MOD_ALT_RIGHT       (1<<6)   // =0x40
#define MOD_GUI_RIGHT       (1<<7)   // =0x80
*****

通常のUSB Keyboardの左WindowsキーのKey Code:0xE3ですが
名前に「MOD」がついて 0x08になっているのです。

ちなみに
Arduino v.1.8.3 オリジナルのKeyboard.hは、
C:\Program Files (x86)\Arduino1.8.3\libraries\Keyboard\src
にありますが、通常の内蔵KeyboardのKey Codeになっています。
*****
#define KEY_LEFT_CTRL 0x80
#define KEY_LEFT_SHIFT 0x81
#define KEY_LEFT_ALT 0x82
#define KEY_LEFT_GUI 0x83
#define KEY_RIGHT_CTRL 0x84
#define KEY_RIGHT_SHIFT 0x85
#define KEY_RIGHT_ALT 0x86
#define KEY_RIGHT_GUI 0x87
*****

ではと、MOD_GUI_LEFTだけを試してみます。
 DigiKeyboard.sendKeyStroke(MOD_GUI_LEFT);
うまくいけば、スタートメニューの表示に切り替わるのでしょうが、
「e」が出てきます。

 DigiKeyboard.println(MOD_GUI_LEFT);
では、「8」が出てきました。
まあ当然の結果ですが、左Windowsキーの動作になりません。
USB Keyboardでは、KeyCode:8 なので「E」Keyになるわけです。

USB仕様書のKey Codeで、左Windowsキーだけを試してみます。
Keyboard Left GUI:0xE3(左Windowsキー)なので
 DigiKeyboard.sendKeyStroke(0xE3);
としても、何も起こりません。

SHIFT、CTRL、ALT、GUIキーは、そのままではダメなようですね~
「scancode-ascii-table.h」でテーブルを参照しているようですが
マーティーは、その仕掛けを紐解くことができません。

どうやら
DigiKeyboard.sendKeyStroke(KEY_AMOD_B);
の構文で「MOD_B」は、Key Codeのままでは、いけないのですね。


 KEY_Aに、PRINT SCREEN:0x46
MOD_Bに、MOD_GUI_LEFT(左Windowsキー用のMOD_B):0x08
として
 DigiKeyboard.sendKeyStroke(0x46, 0x08);
にすると

お~っ!スクリーンショットしてくれました!
やっと完成です!

【完成スケッチ】
結局、1ボタンでスクリーンショットするスケッチは、これです。
「MOD_GUI_LEFT」は、DigiKeyboard.h で定義されているので
「KEY_PRINT_SCREEN」だけ定義しています。
*****
#include <DigiKeyboard.h>
#define KEY_PRINT_SCREEN 0x46

const int inputButton = 0;
const int outputLed = 1;
bool outputState = true;
void setup() {
   pinMode (inputButton, INPUT);
   pinMode(outputLed, OUTPUT);
}
void loop() {
   if (digitalRead(inputButton) == LOW) {
      digitalWrite(outputLed, 1);
      if (!outputState) {
         outputState = true;
         DigiKeyboard.update();
         DigiKeyboard.sendKeyStroke(KEY_PRINT_SCREEN, MOD_GUI_LEFT);
      }
   } else {
      digitalWrite(outputLed, 0);
      outputState = false;
   }
   DigiKeyboard.delay(10);
}
*****

複数キーの仕掛けは、なんとなくの使い方しかわかりませんでしたが、
まあ、やりたい動作ができたので、これでよしとします。

今の所、この状態です。
その内、ちゃんとケースを作らねば!
一応、動くのですが、
DigisparkのKeyboardドライバ、ちょっと変な挙動がありました。
マーティーPCのUSB端子は、左側面に2つ、右側面に1つあります。


左側面のUSB2.0とUSB3.0は、問題ないのですが
右側面のUSB 2.0に先のスケッチを書き込んだDigisparkを挿すと
「USBデバイスが認識されません」となるのです。
デバイスマネージャーを見ると
[キーボード]の所に[HIDキーボードデバイス]はなく
[ユニバーサル シリアル バス コントローラー]の一番下に
[不明なUSBデバイス(アドレスの設定失敗)]と出ています。
無効にしたり、ドライバ入れ直したり、色々やってもダメなのです。
そして、左側面に付けていたUSBマウスを外してからやると
無事[HIDキーボードデバイス]として認識されました。
その後で、USBマウスを繋ぐと大丈夫なのです。
間にUSB HUBを入れると問題なく認識してくれます。
Digisparkがうまく認識してくれない時は、
一旦、全てのUSBデバイスを取り外すといいようです。
この端子だけは、毎回こうしないと認識してくれませんね~
1ボタン・スクリーンショットは、思いの外、便利です!
探せば、ソフトでできるんでしょうが...

0 件のコメント: