懸垂お楽しみデバイス「挙上挙上(アゲアゲ)くん」の制作

#概要
電子工作の定番Arduinoを使用し，懸垂トレーニング用カウンタを制作．
本デバイスを懸垂台や鉄棒，梁，藤棚などに固定し，ボタンを顎で押下するごとに懸垂のカウントを行う．

![IMG_3822.JPG](https://qiita-image-store.s3.amazonaws.com/0/174492/1be20640-150a-829d-f217-3bda4f67bc7c.jpeg)
懸垂台に設置した様子



#目的
SASUKEか筋肉番付にあった懸垂カウンタのようなものを作って，懸垂を楽しくしてモチベーションアップにつなげる．
また，懸垂トレーニングでは，最初の内はフォームも正しくやるが，疲れてくるとちゃんと身体を下げずにやってしまうという問題がある．そこで，懸垂１回１回を「ちゃんとやる/ズルしない」ための機構も組み込む．

#製作
パーツはすべて秋葉原で揃えた．

##・パーツ
Arduino Uno
ブレッドボード
千石電商で買ったアーケードゲーム用ボタン
ラジオデパートの２Fで買ったケース
秋月で買った[２桁７セグ](http://akizukidenshi.com/catalog/g/gI-00214/)
秋月で買った[測距センサ](http://akizukidenshi.com/catalog/g/gI-02551/)

##・仕様
__【各お楽しみモード】__
各モードの切替はデバイス側面のタクトスイッチを押下するごとにループして切替可能．
・5回カウントダウン：
　　5回懸垂後，終了おめでとうメロディが流れる
・10回カウントダウン
　　10回懸垂後，終了おめでとうメロディが流れる
・20回カウントダウン
　　20回懸垂後，終了おめでとうメロディが流れる
・30秒チャレンジ
　　30秒で何回懸垂ができるかのチャレンジ
　　5秒の準備時間の後30秒チャレンジスタート．30秒間のみ懸垂カウントを受け付ける．カウントはカウントアップ．

__【チート判定】__
身体を一定以上下げてからでないと，次の懸垂カウントを受け付けない．
デバイス下部のLEDが点灯中は次のカウント入力ができない．身体を一定以上下げることでLEDが消灯し，次のカウントを受け付ける．


##・配線/組み立て
チート判定は測距センサで実装．顎で押下するボタンのすぐ下に測距センサを取り付けることで，懸垂者の頭が測距センサの前から消える(=身体が下がった)ことを検出する．
配線図は[Fritzing](http://fritzing.org/home/)を使用して作図．

![pullupper_ブレッドボード.png](https://qiita-image-store.s3.amazonaws.com/0/174492/604a8948-27ac-a78d-75fa-b66250e58acb.png)
配線図

![IMG_3813.JPG](https://qiita-image-store.s3.amazonaws.com/0/174492/2f923bab-8d11-6afc-20c9-f48371ca8631.jpeg)
配線が完了した製作途中の様子



##・コード
```c:pullupper.ino
const int BUTTON = 14;  // 懸垂入力用プッシュボタンが接続されているピン
const int input_pin_sw = 15; // タクトスイッチが接続されているピン
const int range = 16; // 距離センサが接続されているピン

const int led_1  = 12;  // 7segLED　カウンタ用　1の位
const int led_10 = 13;  // 7segLED　カウンタ用　10の位

const int time_led_1  = 11;  // 7segLED　カウンタ用　1の位
const int time_led_10 = 10;  // 7segLED　カウンタ用　10の位

const int pullup_wait_led = 9; 

const int PINNO = 8;   // 圧電スピーカを接続したピン番号

int button_val = LOW;            // 入力ピンの状態がこの変数(val)に記憶される
int button_old_val = HIGH;        // valの前の値を保存しておく変数
int state = 20;          // LEDの状態(0ならオフ、1ならオン)
int state_1 = 0;          // the one's digit 
int state_10 = 2;        // the ten's digit

int range_val = 0;

int time_count = 0;
int time_state = 0;          // LEDの状態(0ならオフ、1ならオン)
int time_1 = 0;          // the one's digit 
int time_10 = 0;        // the ten's digit

const int PUSH_SHORT = 100;

boolean pullup_wait = 1;

boolean twenty_trial = 1;
boolean twenty_trial_finish = 0;

boolean ten_trial = 0;
boolean ten_trial_finish = 0;

boolean five_trial = 0;
boolean five_trial_finish = 0;

boolean count_trial = 0;
boolean count_trial_before = 1;
boolean count_trial_finish = 0;

unsigned long old_time = 0;
unsigned long now_time = 0;


void setup(){
  //Serial.begin(9600) ;     // 9600bpsでシリアル通信のポートを開きます
  
  //1～8番ピン　デジタル出力へセット
  for (int i=1; i<=8; i++){
    pinMode(i,OUTPUT);
  }
  pinMode(BUTTON, INPUT);  // BUTTONは入力に設定
  pinMode(led_10, OUTPUT);
  pinMode(led_1, OUTPUT);
  pinMode(time_led_10, OUTPUT);
  pinMode(time_led_1, OUTPUT);
  pinMode(pullup_wait_led, OUTPUT);

  old_time = millis();
}

//LEDレイアウトを定義
boolean Num_Array[11][7]={
  {1,1,1,1,1,0,1},//0
  {0,0,1,1,0,0,0},//1
  {1,1,0,1,1,1,0},//2
  {0,1,1,1,1,1,0},//3
  {0,0,1,1,0,1,1},//4
  {0,1,1,0,1,1,1},//5
  {1,1,1,0,1,1,1},//6
  {0,0,1,1,1,0,0},//7
  {1,1,1,1,1,1,1},//8
  {0,0,1,1,1,1,1},//9
  {0,0,0,0,0,0,0}//none
};
//LED表示関数を定義

void NumPrint(int Number){
  for (int w=0; w<=6; w++){
  digitalWrite(w+1,Num_Array[Number][w]);
  }
}

void loop(){
   button_val = digitalRead(BUTTON); // 懸垂入力用プッシュボタン入力を読み取りvalに格納
   range_val = analogRead(range); // 距離センサの入力を読み取りvalに格納
   //Serial.println(range_val) ;    // シリアルモニターに表示させる
   
   if (pullup_wait==0){
    digitalWrite(pullup_wait_led,HIGH);
    if (range_val < 300){
      pullup_wait = 1;
    }
   }else{
    digitalWrite(pullup_wait_led,LOW);
   }
   
   
   if ((count_trial == 1)&&(count_trial_finish == 0)){
    now_time = millis();
    if (count_trial_before == 1){
      if (now_time - old_time > 1000){
        time_count = time_count-1;
        time_state = time_state - 1;
        time_10 = time_state /10 % 10;  //十の位を切り出し
        time_1 = time_state %10;        // 一の位を切り出し
        old_time = now_time;
      }
      if (time_count == 0){
        time_count = 0;
        time_state = 0;
        time_10 = 0;
        time_1 = 0;
        count_trial_before = 0;
      }
    }else{
     if (now_time - old_time > 1000){
      time_count = time_count+1;
      time_state = time_state + 1;
      time_10 = time_state /10 % 10;  //十の位を切り出し
      time_1 = time_state %10;        // 一の位を切り出し
      old_time = now_time;
  
      if (time_count >= 30){
        count_trial_finish = 1;
      }
     }
    }
   }

   // 7セグの表示　ダイナミック制御
   if (count_trial == 1){
     digitalWrite(led_10,0);
     digitalWrite(led_1,1);
     digitalWrite(time_led_10,1);
     digitalWrite(time_led_1,1);
     NumPrint(state_10);
     delay(5);
     digitalWrite(led_10,1);
     digitalWrite(led_1,0);
     digitalWrite(time_led_10,1);
     digitalWrite(time_led_1,1);
     NumPrint(state_1);
     delay(5);
     digitalWrite(led_10,1);
     digitalWrite(led_1,1);
     digitalWrite(time_led_10,0);
     digitalWrite(time_led_1,1);
     NumPrint(time_10);
     delay(5);
     digitalWrite(led_10,1);
     digitalWrite(led_1,1);
     digitalWrite(time_led_10,1);
     digitalWrite(time_led_1,0);
     NumPrint(time_1);
     delay(5);
   }
   else{
     digitalWrite(time_led_10,1);
     digitalWrite(time_led_1,1);

     digitalWrite(led_10,0);
     digitalWrite(led_1,1);
     NumPrint(state_10);
     delay(5);
     digitalWrite(led_10,1);
     digitalWrite(led_1,0);
     NumPrint(state_1);
     delay(5);
   }

   //　タクトスイッチの入力判定
   unsigned long gauge = 0;
   while (!digitalRead(input_pin_sw))
   {
     gauge++;
   }
   if (gauge > PUSH_SHORT)
   {
     if (count_trial == 1){
       state = 20;
       state_1 = 0;
       state_10 = 2;
       count_trial = 0;
       five_trial = 0;
       ten_trial = 0;
       twenty_trial = 1;
       twenty_trial_finish = 0;
     }
     else if (twenty_trial == 1){
       state = 10;
       state_1 = 0;
       state_10 = 1;
       count_trial = 0;
       five_trial = 0;
       ten_trial = 1;
       twenty_trial = 0;
       ten_trial_finish = 0;
     }
     else if (ten_trial == 1){
       state = 5;
       state_1 = 5;
       state_10 = 0;
       
       count_trial = 0;
       five_trial = 1;
       ten_trial = 0;
       twenty_trial = 0;
       
       five_trial_finish = 0;
     }
     else if (five_trial == 1){
       state = 0;
       state_1 = 0;
       state_10 = 0;
       time_state = 6;
       time_1 = 6;
       time_10 = 0;
       time_count = 6;
       
       count_trial = 1;
       five_trial = 0;
       ten_trial = 0;
       twenty_trial = 0;
       count_trial_finish = 0;
       count_trial_before = 1;
     }
   }

   //各終了判定
   if ((twenty_trial == 1) && (twenty_trial_finish == 0) && (state == 0)) {
    finish();
    state = 20;
    state_1 = 0;
    state_10 = 2;
    twenty_trial_finish = 0;
   }
   if ((ten_trial == 1) && (ten_trial_finish == 0) && (state == 0)) {
    finish();
    state = 10;
    state_1 = 0;
    state_10 = 1;
    ten_trial_finish = 0;
   }
   if ((five_trial == 1) && (five_trial_finish == 0) && (state == 0)) {
    finish();
    state = 5;
    state_1 = 5;
    state_10 = 0;
    five_trial_finish = 0;
   }
   
   //懸垂入力用プッシュボタンが押されたかどうかチェック
   if ((button_val == HIGH) && (button_old_val == LOW)) {
    if (pullup_wait == 1){
      if (count_trial == 1){
        if ((count_trial_finish == 0)&&(count_trial_before == 0)){
          state = (state+1) ;
          }
      }else{
        state = (state-1) ;
      }
      if (state < 0){
        state = 0;
      }
      
      state_10 = state /10 % 10;  //十の位を切り出し
      state_1 =state %10;        // 一の位を切り出し
    }
    pullup_wait = 0;
    
   }
   button_old_val = button_val; 
}

void finish(){
    //終了の音楽
    //実機は某RPGのレベルアップ音にしていますが，ここでは著作権のため適当に
    tone(PINNO,698,100) ;  // ファ
    delay(150) ;
    delay(50) ;
    tone(PINNO,622,100) ;  // ミ
    delay(150) ;
    delay(50) ;
    tone(PINNO,784,100) ;  // ソ
    delay(150) ;
    delay(50) ;
    tone(PINNO,698,300) ;  // ファ
    delay(400) ;
}
```

#結果
懸垂が楽しくなった．懸垂仲間に笑顔が増えた．

#その他
[7セグ用シリアルドライバモジュール](http://akizukidenshi.com/catalog/g/gM-12642/)なるものがあることを制作後に知る．これを使えばダイナミック制御というめんどくさいこともしなくてよかったし，配線もスッキリしたはずで，光量も増えるはず．