見出し画像

ArduinoでHID

肢体不自由者がPCを操作する際にキーボードやマウスの代替として、外部スイッチで入力できるインターフェースを使う選択肢があり、市販もされている。
これまではジョイスティックの基板からスイッチのバイパスを取り、フリーソフトで機能を割り当てて使っていたが、浅田先生(北神戸スイッチルーム)がHID機能をもったスイッチBOXを作られた。この方法はWindows、iOsにかかわらず使用できるので汎用性が高い。
そこでわたしも作ってみた。今回初めてHID機能を持つ32U4を積んだArduinoProMicro(Leonardo)を使用した。
併せてこれまで採用してきたジョイスティックの基板とフリーソフトの組み合わせによる方法を記録として残す。(他の選択肢→自作)


パーツ

Arduino Pro Micro(AliExpress、送料込み691円)
タクトスイッチ、ジョイスティック(デジットのジャンク品)など

回路

写真の通り。使用ポートの詳細はsketchを参照のこと。

JobsのわがままでiPad、iPhoneは長い間キーボードやマウス、記憶媒体と有線で接続する手段が
なかったが、Lightning - USBカメラアダプタを使ってやっとUSB接続ができるようになった。

実装

    (救急車のサイレンは愛敬ということで。。)

スケッチ

ほとんどサンプルスケッチそのままである。ライブラリに感謝!

/* HID_for_Pro_Micro_04.ino
   
       Arduino Pro Micro
               TX/1・     ・RAW  
               RX/0・     ・GND
                GND・     ・Reset
                GND・     ・VCC              *PWM
              SDA/2・     ・21/A3
              SCL/3*      ・20/A2
     left -->  A6/4・     ・19/A1   <== y_Axis
       up -->     5*     ・18/A0   <== x_Axis
     down -->  A7/6*     ・15/SCLK <== mouseButton
    space -->     7・     ・14/MISO <-- LED
    right -->  A8/8・     ・16/MOSI <-- bs
    return --> A9/9*     *10/A10  <-- tab
*/

#include <Keyboard.h>
#include <Mouse.h>

const int LED = 14;
const int up = 5;
const int down = 6;
const int right = 8;
const int left = 4;
const int tab = 10;
const int space = 7;
const int bs = 16;
const int enter = 9;

const int mouseButton = 15;   // mouse button
const int x_axis = A0;        // joystick X axis
const int y_axis = A1;        // joystick Y axis
int mouse_x = 0;
int mouse_y = 0;

int range = 12; // output range of X or Y movement
int threshold = range / 4; // resting threshold
int center = range / 2; // resting position value

int responseDelay = 10; // response delay of the mouse, in ms
int lastSwitchState = LOW; // previous switch state

void setup() {
  pinMode(LED, OUTPUT);       // for switch indicater
  pinMode(up, INPUT_PULLUP);
  pinMode(down, INPUT_PULLUP);
  pinMode(right, INPUT_PULLUP);
  pinMode(left, INPUT_PULLUP);
  pinMode(tab, INPUT_PULLUP);
  pinMode(space, INPUT_PULLUP);
  pinMode(bs, INPUT_PULLUP);
  pinMode(enter, INPUT_PULLUP);
  pinMode(mouseButton, INPUT_PULLUP);
  Serial.begin(9600);
  Keyboard.begin();
  Mouse.begin();
}

void loop() {
  mouse_control();
  mouse_button_control();
  keyboard_control();
  delay(responseDelay);
}

void mouse_control() {
  mouse_x = readAxis(x_axis);
  mouse_y = readAxis(y_axis);
  Mouse.move(mouse_x, mouse_y, 0);
  Serial.print("mouse position : ");
  Serial.print(mouse_x, DEC);
  Serial.print(" , ");
  Serial.println(mouse_y, DEC);
}

int readAxis(int thisAxis) {  // analog input to mouse pointer output
  int reading = analogRead(thisAxis);
  reading = map(reading, 0, 1023, 0, range);
  int distance = reading - center;
  if (abs(distance) < threshold) {
    distance = 0;
  }
  return distance;
}

void mouse_button_control() {
  if (digitalRead(mouseButton) == LOW) {
    if (!Mouse.isPressed(MOUSE_LEFT)) {
      Mouse.press(MOUSE_LEFT);
      digitalWrite(LED, HIGH);
    }
  } else {
    if (Mouse.isPressed(MOUSE_LEFT)) {
      Mouse.release(MOUSE_LEFT);
      digitalWrite(LED, LOW);
    }
  }
}

void keyboard_control() {
  if (digitalRead(up) == LOW) {
    digitalWrite(LED, HIGH); delay(100); digitalWrite(LED, LOW);
    Keyboard.press(KEY_UP_ARROW);
    delay(10);
    Keyboard.releaseAll();
    while (digitalRead(up) == LOW);
  }
  if (digitalRead(down) == LOW) {
    digitalWrite(LED, HIGH); delay(100); digitalWrite(LED, LOW);
    Keyboard.press(KEY_DOWN_ARROW);
    delay(10);
    Keyboard.releaseAll();
    while (digitalRead(down) == LOW);
  }
  if (digitalRead(right) == LOW) {
    digitalWrite(LED, HIGH); delay(100); digitalWrite(LED, LOW);
    Keyboard.press(KEY_RIGHT_ARROW);
    delay(10);
    Keyboard.releaseAll();
    while (digitalRead(right) == LOW);
  }
  if (digitalRead(left) == LOW) {
    digitalWrite(LED, HIGH); delay(100); digitalWrite(LED, LOW);
    Keyboard.press(KEY_LEFT_ARROW);
    delay(10);
    Keyboard.releaseAll();
    while (digitalRead(left) == LOW);
  }
  if (digitalRead(tab) == LOW) {
    digitalWrite(LED, HIGH); delay(100); digitalWrite(LED, LOW);
    Keyboard.press(KEY_TAB);
    delay(10);
    Keyboard.releaseAll();
    while (digitalRead(tab) == LOW);
  }
  if (digitalRead(space) == LOW) {
    digitalWrite(LED, HIGH); delay(100); digitalWrite(LED, LOW);
    Keyboard.press(0x20);
    delay(100);
    Keyboard.releaseAll();
    while (digitalRead(space) == LOW);
  }
  if (digitalRead(bs) == LOW) {
    digitalWrite(LED, HIGH); delay(100); digitalWrite(LED, LOW);
    Keyboard.press(KEY_BACKSPACE);
    delay(10);
    Keyboard.releaseAll();
    while (digitalRead(bs) == LOW);
  }
  if (digitalRead(enter) == LOW) {
    digitalWrite(LED, HIGH); delay(100); digitalWrite(LED, LOW);
    Keyboard.press(KEY_RETURN);
    delay(10);
    Keyboard.releaseAll();
    while (digitalRead(enter) == LOW);
  }
}

その他の選択肢

自作  (→ 代替マウスとしてのスイッチボックス

手指の拘縮と麻痺のある人用に作ったが、上手に使いこなしていた。
この人は卒業後も使っている。                 
(黄)8方向、(緑)左右クリック、PageUp、Down
(ロッカースイッチ)ポインタ速度の切替、ドラッグ
4方向、左右クリック
ダブルクリックはWindowsアクセシビリティのシングルクリック設定を利用した。

手順
USB対応のジョイスティックをバラして基板から各スイッチのバイパスを取りだす。(ジョイスティックはアマゾンで数百円で買える)
基板には動作確認用の露出部があり、念のためピンセットをあててショートさせて確認しながらバイパスを取る。(基板によっては対応するスイッチがプリントされている)作業が単調なのでビールを飲みながらハンダ付けしたら火傷をしたことがあった(笑)

スイッチへの機能の割り当てはJoyToKeyjoypointerなどのフリーソフトを使う。これらのソフトはゲーマー用に開発されたもので非常に高性能であり、設定も簡便である。Windowsでしか使えないのは残念だ。

REVIVE USB

市販の基板である。12ポート、各ポートへの機能の割り付けは付属のアプリをダウンロードして行う。アプリはWindows用だが、設定してしまえば他のOSでも使える。

らくらくマウス

市販品。現在はテクノツールが引き継いでいるが、以前は基板の販売もあった。
テクノツールに移譲前に注文製作を依頼したことがあった。ボタンの配置、ケースの選定から始まり、防水対策、外部スイッチ用のジャックの追加の提案など非常に丁寧に良心的に対応して戴いた。

できマウス。

市販。4ポート。8ポートに拡張も可。
サイトに上がっている優れもののアプリたちには大変お世話になりました。ありがとうございました。

ver.2 を作った

各スイッチの機能割り当てが変更出来るようになった。


この記事が気に入ったらサポートをしてみませんか?