Arduinoでクイズボタンを作る

[Arduino Leonardo (Pro micro)](https://twitter.com/shigezoneakiba/status/1167649926425141248)を使ってクイズボタンを作ります。
ボタンを押すとキー入力が行われます（押し下し続けてもリピートせず一回のみ押されます）。
ボタン（ピン）は設定したピンとGNDにつないで使用します。
![pose_button_osu_s.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/22e7381d-0742-5654-7842-5d94b4e27676.png)

不公平のないよう、各ボタンのループ中には```delay()```を入れず、
ボタンの数や割当てピン、割り当てキーを指定できるようにしています。
公平な早押しを実現したい場合は、
・loop()内で->isPressed()をチェック
・複数ボタンが同時に押されたら（->isPressed()がtrueになったら）そのうちの1つをランダムで取得
・取得したもの以外の->loop(deltaMillis)をやめる（キーが押されたのをキャンセル）
で実現できます。

```
#include <Arduino.h>
#include <Keyboard.h>

class TmButtonKey{
  public:
  TmButtonKey(uint8_t _pin, uint8_t _key, long _wait=100){
    mPin = _pin;
    mKey = _key;
    mWait = _wait;
    mTimer=0;
    mIsPressed=false;
    mIsOn=mPreOn=false;
    pinMode(_pin,INPUT_PULLUP);
  }
  bool isPressed(){ return mIsPressed;}
  bool loop(unsigned int _deltaMillis){
    bool ret = false;
    if(!mIsPressed){
      mIsOn = !digitalRead(mPin);
      if(mIsOn & !mPreOn){
        push();
      }
      mPreOn = mIsOn;
    }else{ // mIsPressed
      mTimer += _deltaMillis;
      if(mTimer>=mWait){
        ret = true;
        mIsPressed = false;
        Keyboard.release(mKey);
      }
    }
    return ret;
  }
  private:
  uint8_t mPin;
  uint8_t mKey;
  long mWait;
  long mTimer;
  bool mIsPressed;
  bool mIsOn,mPreOn;

  bool push(){
    bool ret = false;
    if(!mIsPressed){
      ret = true;
      mIsPressed=true;
      mTimer = 0;
      Keyboard.press(mKey);
    }
    return ret;
  }
};

#define BTN1PL (PIN2)
#define BTN1PR (PIN3)
#define BTN1PU (PIN4)
#define BTN2PL (PIN5)
#define BTN2PR (PIN6)
#define BTN2PU (PIN7)
#define LED1PL (8)
#define LED1PR (9)
#define LED1PU (10)
#define LED2PL (14)
#define LED2PR (15)
#define LED2PU (16)
TmButtonKey* pKey1R;
TmButtonKey* pKey1L;
TmButtonKey* pKey1U;
TmButtonKey* pKey2R;
TmButtonKey* pKey2L;
TmButtonKey* pKey2U;
unsigned long mymillis;

void setup() {
  // put your setup code here, to run once:
  Keyboard.begin();
  pKey1L = new TmButtonKey(BTN1PL,KEY_LEFT_ARROW);
  pKey1R = new TmButtonKey(BTN1PR,KEY_RIGHT_ARROW);
  pKey1U = new TmButtonKey(BTN1PU,KEY_UP_ARROW);
  pKey2L = new TmButtonKey(BTN2PL,'a');
  pKey2R = new TmButtonKey(BTN2PR,'s');
  pKey2U = new TmButtonKey(BTN2PU,'w');
  pinMode(LED1PL,OUTPUT);
  pinMode(LED1PR,OUTPUT);
  pinMode(LED1PU,OUTPUT);
  pinMode(LED2PL,OUTPUT);
  pinMode(LED2PR,OUTPUT);
  pinMode(LED2PU,OUTPUT);
  mymillis = millis();
}

void loop() {
  // put your main code here, to run repeatedly:
  unsigned long nowMillis = millis();
  unsigned long deltaMillis = nowMillis - mymillis;
  mymillis = nowMillis;
  pKey1L->loop(deltaMillis);
  pKey1R->loop(deltaMillis);
  pKey1U->loop(deltaMillis);
  pKey2L->loop(deltaMillis);
  pKey2R->loop(deltaMillis);
  pKey2U->loop(deltaMillis);
  
  digitalWrite(LED1PL,pKey1L->isPressed()?HIGH:LOW);
  digitalWrite(LED1PR,pKey1R->isPressed()?HIGH:LOW);
  digitalWrite(LED1PU,pKey1U->isPressed()?HIGH:LOW);
  digitalWrite(LED2PL,pKey2L->isPressed()?HIGH:LOW);
  digitalWrite(LED2PR,pKey2R->isPressed()?HIGH:LOW);
  digitalWrite(LED2PU,pKey2U->isPressed()?HIGH:LOW);
  delay(5);
}
```

お作法通りやりたい場合は下記のようにファイルを分けます。

```TmButtonKey.h
#include <Arduino.h>

class TmButtonKey{
  public:
  TmButtonKey(uint8_t _pin, uint8_t _key, long _wait=100);
  bool isPressed();
  bool loop(unsigned int _deltaMillis);
  private:
  uint8_t mPin;
  uint8_t mKey;
  long mWait;
  long mTimer;
  bool mIsPressed;
  bool mIsOn,mPreOn;
  bool push();
};
```

```TmButtonKey.cpp
#include <Arduino.h>
#include <Keyboard.h>
#include "TmButtonKey.h"

TmButtonKey::TmButtonKey(uint8_t _pin, uint8_t _key, long _wait){
    mPin = _pin;
    mKey = _key;
    mWait = _wait;
    mTimer=0;
    mIsPressed=false;
    mIsOn=mPreOn=false;
    pinMode(_pin,INPUT_PULLUP);
}
bool TmButtonKey::isPressed(){ return mIsPressed;}
bool TmButtonKey::loop(unsigned int _deltaMillis){
    bool ret = false;
    if(!mIsPressed){
        mIsOn = !digitalRead(mPin);
        if(mIsOn & !mPreOn){
            push();
        }
        mPreOn = mIsOn;
    }else{ // mIsPressed
        mTimer += _deltaMillis;
        if(mTimer>=mWait){
            ret = true;
            mIsPressed = false;
            Keyboard.release(mKey);
        }
    }
    return ret;
}
bool TmButtonKey::push(){
    bool ret = false;
    if(!mIsPressed){
        ret = true;
        mIsPressed=true;
        mTimer = 0;
        Keyboard.press(mKey);
    }
    return ret;
}
```

```main.cpp
#include <Arduino.h>
#include <Keyboard.h>
#include "TmButtonKey.h"

#define BTN1PL (PIN2)
#define BTN1PR (PIN3)
#define BTN1PU (PIN4)
#define BTN2PL (PIN5)
#define BTN2PR (PIN6)
#define BTN2PU (PIN7)
#define LED1PL (8)
#define LED1PR (9)
#define LED1PU (10)
#define LED2PL (14)
#define LED2PR (15)
#define LED2PU (16)
TmButtonKey* pKey1R;
TmButtonKey* pKey1L;
TmButtonKey* pKey1U;
TmButtonKey* pKey2R;
TmButtonKey* pKey2L;
TmButtonKey* pKey2U;
unsigned long mymillis;

void setup() {
  // put your setup code here, to run once:
  Keyboard.begin();
  pKey1L = new TmButtonKey(BTN1PL,KEY_LEFT_ARROW);
  pKey1R = new TmButtonKey(BTN1PR,KEY_RIGHT_ARROW);
  pKey1U = new TmButtonKey(BTN1PU,KEY_UP_ARROW);
  pKey2L = new TmButtonKey(BTN2PL,'a');
  pKey2R = new TmButtonKey(BTN2PR,'s');
  pKey2U = new TmButtonKey(BTN2PU,'w');
  pinMode(LED1PL,OUTPUT);
  pinMode(LED1PR,OUTPUT);
  pinMode(LED1PU,OUTPUT);
  pinMode(LED2PL,OUTPUT);
  pinMode(LED2PR,OUTPUT);
  pinMode(LED2PU,OUTPUT);
  mymillis = millis();
}

void loop() {
  // put your main code here, to run repeatedly:
  unsigned long nowMillis = millis();
  unsigned long deltaMillis = nowMillis - mymillis;
  mymillis = nowMillis;

  digitalWrite(LED1PL,pKey1L->isPressed()?HIGH:LOW);
  digitalWrite(LED1PR,pKey1R->isPressed()?HIGH:LOW);
  digitalWrite(LED1PU,pKey1U->isPressed()?HIGH:LOW);
  digitalWrite(LED2PL,pKey2L->isPressed()?HIGH:LOW);
  digitalWrite(LED2PR,pKey2R->isPressed()?HIGH:LOW);
  digitalWrite(LED2PU,pKey2U->isPressed()?HIGH:LOW);

  pKey1L->loop(deltaMillis);
  pKey1R->loop(deltaMillis);
  pKey1U->loop(deltaMillis);
  pKey2L->loop(deltaMillis);
  pKey2R->loop(deltaMillis);
  pKey2U->loop(deltaMillis);
 
  delay(5);
}
```