ギタコンの自作 試作4

試作4の内容

ごちゃごちゃしてきた配線をすっきりさせます。 また、ジャイロセンサーを追加して、wailing操作に対応します。 ただし、ジャイロセンサーをいくつか当たってみたのですが、はんだ付け無しで利用できるものを見つけられませんでした。 従い、ジャイロセンサーでのWailing対応は、はんだ付け作業が必須となります。ごめんなさい。

もしも、はんだ付け不要のジャイロセンサー(加速度センサーではない)をご存知の方がいらっしゃいましたら、教えてください。 (例えば、秋月電子さんのAE-BMX055がはんだ付け不要で使えそうに見えますが、実は電源設定をジャンパーのはんだ付けで行う必要があるため、はんだ付け不要とはなりませんでした。そこで、どうせはんだ付けしなければならないのなら・・・ということで、ここでは超安いジャイロセンサーを使っています)

試作4で使うもの一覧

試作1,3で使ったものに加えて、以下を使います。

追加費用は、合計で数百円+発送費用くらい。(別の方法で配線を整理するのであれば、線材の追加は不要)

名称値段通販コード
ジャイロセンサーモジュール (MPU-6050) x1 IMG_3750_.jpg 1個数百円程度 Amazon: (中国からの発送品になります) MPU-6050 使用 3軸ジャイロスコープ・3軸加速度センサー モジュール (2018年9月4日時点で1個229円)
各種長さが揃ったジャンパワイヤー IMG_3643.JPG 400円程度 秋月電子: P-05160

配線をすっきりさせる

新しく入手した「短いジャンパーワイヤー」を使って、GNDや5Vの配線を置き換えました。 ジョイスティックのところへの信号線は適当な長さのケーブルが無かったので、やむなく反対側(手前側)のL/R端子に接続を迂回しました。 これで、ケーブルを手前側でまとめれば、そこそこすっきりするかなと思います。

wailingに対応させる

まずは、ジャイロのモジュールに、ヘッダピンをはんだ付けします。注意が必要な点が2つあります。

  1. L字型のピンではなく、ストレートタイプのピンを使うこと。
    (L字型のピンを使うと、ジャイロモジュールはブレッドボードに対して垂直に立てて挿すことになりますが、そうするとウエイリングの激しい動作についていけない懸念があります。
    また、以下の説明では、ジャイロモジュールのZ軸(ジャイロモジュールを水平に置いたときに、ジャイロモジュールの上面に垂直に立てた法線)を中心にした回転をWailingとして検出する前提で記載しているため、ジャイロモジュールを立ててブレッドボードにつなぐと、Wailingの回転を検出できなくなります。
  2. 8つのピンをすべてはんだ付けするのではなく、VCCから数えて4つ分だけピンをはんだ付けします。
    8つすべてピンをはんだ付けしてしまうと、ブレッドボードの端と干渉して、ジャイロボードをブレッドボードに挿せなくなります。
    一方で、ヘッダピンの方は8つ(以上)連結してつながっていると思いますので、4つのところでパキッと折って使ってください。

次に配線です。今回使用するジャイロモジュール (MPU-6050) は、I2Cという通信規格でArduino等他の機器と接続します。(I2CはInter-Integrated Circuitの略で、アイスクエアシーと読みますが、アイツーシーでも通じます。)
Arduino Microでは、D2ピンとD3ピンをI2Cでの通信に使用し、D2ピンをデータ送受信(SDA)、D3ピンがデータクロック(SCL)に用います。ですから、Arduino MicroのD2ピンとMPU-6050のSDAを、Arduino MIcroのD3ピンとMPU-6050のSCLを、それぞれつなぎます。また、MPU-6050のVCCとGNDは、それぞれArduino Microの5VとGNDにつなぎます。(このモジュールのVCCは、5Vと3.3Vのどちらをつないでもよいため、接続が簡単な5Vを使っています。) MPU-6050の他のピン(INTなど)は、何も接続しません。

配線をすっきりさせるために、MPU-6050の下側に、SDAとSCLの配線を通すようにしています。詳しくは、以下の図を見てください。このような通し方をしたくない場合は、後ほど出てくる配線図を見てください。

スケッチ修正

この辺りを参考にして、スケッチを作成しました。

ちょっと長くなりますが、まずはスケッチ全体を引用します。 (スケッチの詳細に興味がない場合は、このページの最後にzipでまとめたものを置いておくので、そこまでは読み飛ばしていただいて結構です)

  1. // Simple example application that shows how to read four Arduino
  2. // digital pins and map them to the USB Joystick library.
  3. //
  4. // Ground digital pins 9, 10, 11, and 12 to press the joystick
  5. // buttons 0, 1, 2, and 3.
  6. //
  7. // NOTE: This sketch file is for use with Arduino Leonardo and
  8. // Arduino Micro only.
  9. //
  10. // by Matthew Heironimus
  11. // 2015-11-20
  12. //--------------------------------------------------------------------
  13. #define DEBUG // シリアルへのデバッグ情報出力用
  14. #include <Joystick.h>
  15. const int BUTTONS = 6; // RGBYP + Wailing (analogのPickは別)
  16. const int PIN_STICK = A0; // Pick用のジョイスティックを接続するピン
  17. const int BUTTON_WAILING = BUTTONS-1;
  18. // BUTTONの最後1つを仮想的なWailingボタンにする
  19. // 物理ボタンは無いが、ボタンとしては管理する
  20. Joystick_ Joystick(
  21. JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD,
  22. BUTTONS, 0, // Buttons, Hat Switch Count
  23. true, false, false, // X,Y,Z Axis
  24. false, false, false, // Rx, Ry, Rz Axis
  25. false, false, // rudder, throttle
  26. false, false, false // accelerator, break, steering
  27. );
  28. const int TH_WAILING = 400; // WAILINGと見なす角速度の閾値
  29. // MPU-6050 Accelerometer + Gyro
  30. #include <Wire.h>
  31. #define MPU6050_SMPLRT_DIV 0x19 // R/W
  32. #define MPU6050_CONFIG 0x1A // R/W
  33. #define MPU6050_GYRO_CONFIG 0x1B // R/W
  34. #define MPU6050_GYRO_ZOUT_H 0x47 // R
  35. #define MPU6050_WHO_AM_I 0x75 // R
  36. #define MPU6050_PWR_MGMT_1 0x6B // R/W
  37. #define MPU6050_I2C_ADDRESS 0x68
  38. typedef union accel_t_gyro_union{
  39. struct{
  40. uint8_t z_gyro_h;
  41. uint8_t z_gyro_l;
  42. }
  43. reg;
  44. struct{
  45. int16_t z_gyro;
  46. }
  47. value;
  48. };
  49. unsigned long last_t;
  50. void setup() {
  51. // Initialize Button Pins
  52. // Wailingの実ボタンはないのでBUTTONS-1すること
  53. for (int i = 12; i > 12-(BUTTONS-1); i--)
  54. {
  55. pinMode(i, INPUT_PULLUP);
  56. }
  57. // Initialize Joystick Library (AutoSendState=OFF)
  58. Joystick.begin(false);
  59. //Serial通信初期化
  60. Serial.begin(9600);
  61. // loop時間基準
  62. last_t = millis();
  63. // MPU-6050初期化
  64. Wire.begin();
  65. int error;
  66. uint8_t c;
  67. Serial.print("InvenSense MPU-6050");
  68. Serial.print("June 2012");
  69. error = MPU6050_read (MPU6050_WHO_AM_I, &c, 1);
  70. Serial.print("WHO_AM_I : ");
  71. Serial.print(c,HEX);
  72. Serial.print(", error = ");
  73. Serial.println(error,DEC);
  74. error = MPU6050_read (MPU6050_PWR_MGMT_1, &c, 1);
  75. Serial.print("PWR_MGMT_1 : ");
  76. Serial.print(c,HEX);
  77. Serial.print(", error = ");
  78. Serial.println(error,DEC);
  79. MPU6050_write_reg (MPU6050_PWR_MGMT_1, 0);
  80. // サンプルレートを最大化
  81. MPU6050_write_reg (MPU6050_SMPLRT_DIV, 0);
  82. // FSYNC input disabled, Gyrometer bandwidth=256Hz
  83. MPU6050_write_reg (MPU6050_CONFIG, 0);
  84. // FS_SELの設定
  85. error = MPU6050_read (MPU6050_GYRO_CONFIG, &c, 1);
  86. c |= 0x18; // FS_SEL 3
  87. MPU6050_write_reg (MPU6050_GYRO_CONFIG, c);
  88. }
  89. // Constant that maps the phyical pin to the joystick button.
  90. // Wailingは実ボタンではないので、ピンマップには含めない
  91. const int pinToButtonMap = 13 - (BUTTONS-1);
  92. // Last state of the button
  93. // 6個目のボタンは、Wailingボタンとして扱う
  94. int lastButtonState[BUTTONS] = {0,0,0,0,0,0};
  95. int lastAnalogInput[2] = {0,0};
  96. unsigned long lastButtonChangedTime[BUTTONS] = {0,0,0,0,0,0};
  97. float last_gyro_z = 0;
  98. void loop() {
  99. bool buttonStateChanged = false;
  100. unsigned long t = millis();
  101. // 1ms間隔でloop内処理が実行されるようにする
  102. // (前回の処理から1msに満たない時間しか経っていない場合は
  103. // 門前払いする)
  104. if (last_t >= t)
  105. {
  106. // 門前払いの前に、mills()のオーバーフロー対策もしてしまう
  107. if (last_t > (unsigned long)0xFFFF0000 &&
  108. t < (unsigned long)0x00001000)
  109. {
  110. for (int index = 0; index < BUTTONS; index++)
  111. {
  112. // 最終ボタン状態更新時刻を繰り上げて、正負が
  113. // 破綻しないようにする(この程度の対策で問題ないはず)
  114. lastButtonChangedTime[index] = 0;
  115. }
  116. }
  117. return;
  118. }
  119. else
  120. {
  121. last_t = t;
  122. }
  123. // Read digital pin values
  124. for (int index = 0; index < BUTTONS-1; index++)
  125. {
  126. int currentButtonState = !digitalRead(index + pinToButtonMap);
  127. if (currentButtonState != lastButtonState[index])
  128. {
  129. if (t > lastButtonChangedTime[index] + 10)
  130. {
  131. Joystick.setButton(index, currentButtonState);
  132. buttonStateChanged = true;
  133. lastButtonState[index] = currentButtonState;
  134. lastButtonChangedTime[index] = t;
  135. #ifdef DEBUG
  136. Serial.print("*");
  137. #endif
  138. }
  139. #ifdef DEBUG
  140. Serial.print(t);
  141. Serial.print(":");
  142. Serial.print(index);
  143. Serial.print("=");
  144. Serial.print(currentButtonState);
  145. Serial.print("/");
  146. Serial.print(lastButtonState[index]);
  147. Serial.println("");
  148. #endif
  149. }
  150. }
  151. // Read analog stick values
  152. {
  153. int currentAngularVelocity = analogRead(PIN_STICK);
  154. // ±5以下の入力ブレは無視する (LPF)
  155. if (abs(currentAngularVelocity - lastAnalogInput[0]) > 5)
  156. {
  157. Joystick.setXAxis(currentAngularVelocity);
  158. buttonStateChanged = true;
  159. lastAnalogInput[0] = currentAngularVelocity;
  160. #ifdef DEBUG
  161. Serial.print(t);
  162. Serial.print(":");
  163. Serial.print("A0");
  164. Serial.print("=");
  165. Serial.print(currentAngularVelocity);
  166. Serial.println("");
  167. #endif
  168. }
  169. }
  170. // ジャイロセンサー対応(MPU-6050)
  171. // 参考: http://cranberrytree.blogspot.com/2014/06/gy-521mpu-6050.html
  172. // (MPU6050へのアクセス方法の参考として。実際にはジャイロ情報しか使わなかったですが)
  173. // https://www.robotshop.com/jp/ja/mpu6050-6-dof-gyro-accelerometer-imu.html
  174. // から、ZIPでダウンロードできるPDFマニュアル。その中にレジストリマップあり
  175. // 追加のモード設定の参考にした
  176. //
  177. // また、I2C通信の高速化(100kHz -> 400kHz)のため、
  178. // C:\Program Files (x86)\Arduino\hardware\arduino\avr\
  179. // libraries\wire\src\utility\twi.h の最初にある
  180. // #define TWI_FREQ 100000L
  181. // の100000Lを400000Lに変更しておくこと。
  182. // 参考: https://sites.google.com/site/yamagajin/home/mpu6050
  183. int error;
  184. accel_t_gyro_union accel_t_gyro;
  185. error = MPU6050_read (MPU6050_GYRO_ZOUT_H, (uint8_t *) &accel_t_gyro, sizeof(accel_t_gyro));
  186. uint8_t swap;
  187. #define SWAP(x,y) swap = x; x = y; y = swap
  188. SWAP (accel_t_gyro.reg.z_gyro_h, accel_t_gyro.reg.z_gyro_l);
  189. //FS_SEL_3 16.4 LSB / (°/s)
  190. float gyro_z = accel_t_gyro.value.z_gyro / 16.4;
  191. if (gyro_z > TH_WAILING)
  192. {
  193. #ifdef DEBUG
  194. Serial.print(t);
  195. Serial.print("\t");
  196. Serial.print("[");
  197. Serial.print(gyro_z, 2);
  198. Serial.print("]");
  199. Serial.println("");
  200. #endif
  201. if (!lastButtonState[BUTTON_WAILING])
  202. {
  203. Joystick.setButton(BUTTON_WAILING, true);
  204. lastButtonState[BUTTON_WAILING] = true;
  205. lastButtonChangedTime[BUTTON_WAILING] = t;
  206. buttonStateChanged = true;
  207. #ifdef DEBUG
  208. Serial.print(t);
  209. Serial.println(":WAILING ON");
  210. #endif
  211. }
  212. }
  213. // WailingボタンをONにした後200ms経過したら、自動でオフにする
  214. if (lastButtonState[BUTTON_WAILING] &&
  215. t > lastButtonChangedTime[BUTTON_WAILING] + 200)
  216. {
  217. Joystick.setButton(BUTTON_WAILING, false);
  218. lastButtonState[BUTTON_WAILING] = false;
  219. lastButtonChangedTime[BUTTON_WAILING] = t;
  220. buttonStateChanged = true;
  221. #ifdef DEBUG
  222. Serial.print(t);
  223. Serial.println(":WAILING OFF");
  224. #endif
  225. }
  226. // ボタンの状態変化があった時にだけ、PCにHID入力を通知する
  227. // 同時に、ボタン状態変化の最終時刻を更新する
  228. if (buttonStateChanged)
  229. {
  230. Joystick.sendState();
  231. }
  232. //delay(1); // loop頭のtとlast_tの比較で代用
  233. }
  234. // MPU6050_read
  235. int MPU6050_read(int start, uint8_t *buffer, int size){
  236. int i, n, error;
  237. Wire.beginTransmission(MPU6050_I2C_ADDRESS);
  238. n = Wire.write(start);
  239. if (n != 1)
  240. return (-10);
  241. n = Wire.endTransmission(false); // hold the I2C-bus
  242. if (n != 0)
  243. return (n);
  244. // Third parameter is true: relase I2C-bus after data is read.
  245. Wire.requestFrom(MPU6050_I2C_ADDRESS, size, true);
  246. i = 0;
  247. while(Wire.available() && i<size){
  248. buffer[i++]=Wire.read();
  249. }
  250. if ( i != size)
  251. return (-11);
  252. return (0); // return : no error
  253. }
  254. // MPU6050_write
  255. int MPU6050_write(int start, const uint8_t *pData, int size){
  256. int n, error;
  257. Wire.beginTransmission(MPU6050_I2C_ADDRESS);
  258. n = Wire.write(start); // write the start address
  259. if (n != 1)
  260. return (-20);
  261. n = Wire.write(pData, size); // write data bytes
  262. if (n != size)
  263. return (-21);
  264. error = Wire.endTransmission(true); // release the I2C-bus
  265. if (error != 0)
  266. return (error);
  267. return (0); // return : no error
  268. }
  269. // MPU6050_write_reg
  270. int MPU6050_write_reg(int reg, uint8_t data){
  271. int error;
  272. error = MPU6050_write(reg, &data, 1);
  273. return (error);
  274. }

以下、スケッチのキーポイントを説明していきます。

Wailingの判定方法

まず、Wailingの判定は、単純にMPU-6050のZ軸の回転 (つまり、ブレッドボードをギターっぽく持ってWailingしたときの、ブレッドボードの回転) で、回転の角速度が一定値以上かどうかで判定しています。もしもジャイロセンサーではなく加速度センサーを使って同じことをしようとすると、慣性もセンサーが拾ってしまって、回転の判定が非常に大変なことになりますが、ジャイロセンサーを使う場合は単純にZ軸の角速度が一定値以上かどうかを判定するだけで済みます。

具体的には、Z軸の角速度取得と、角速度の判定を、loop()の214行目付近で行っています。 なおMPU-6050から取得した16bitの値は、エンディアンを変換する必要があるため、それも合わせて行っています。

  1. int error;
  2. accel_t_gyro_union accel_t_gyro;
  3. error = MPU6050_read (MPU6050_GYRO_ZOUT_H, (uint8_t *) &accel_t_gyro, sizeof(accel_t_gyro));
  4. uint8_t swap;
  5. #define SWAP(x,y) swap = x; x = y; y = swap
  6. SWAP (accel_t_gyro.reg.z_gyro_h, accel_t_gyro.reg.z_gyro_l);
  7. //FS_SEL_3 16.4 LSB / (°/s)
  8. float gyro_z = accel_t_gyro.value.z_gyro / 16.4;
  9. if (gyro_z > TH_WAILING)
  10. {
  11. //以下略

なお判定の閾値は、33行目のマクロで定義しています。反応が敏感すぎるようなら、400を500くらいに上げてみてください。 逆に、より敏感にするためには、300とか350とかにしてみて下さい。

  1. const int TH_WAILING = 400; // WAILINGと見なす角速度の閾値

Wailingボタンを離す処理の追加

Wailingが発生したと判断された場合は、内部でBUTTON5を押す動作をします。BUTTON5に相当する物理ボタンは接続していないので、これは仮想的なボタンとなります。 Wailingもボタンとして扱う以上は、押すだけでなく、離す処理も必要になります。離す処理がないと、Wailingボタンが押しっぱなしの状態になり、 次のWailingが動作しなくなります。

loop()の249行目あたりで、この処理を入れています。 具体的には、(最初にWailingが発生したときにボタンが押され、) そこから200ms経過したらボタンを離すようにしています。 1度のWailing操作をすると、内部では1-2ms毎に複数のWailing判定が連続して発生しますが、それらはWailingボタンの追加押しになるので全て無視されます。 200ms経過しWailingボタンが離されると、再度Wailing操作でWailingボタンを押すことができるようになります。

  1. // WailingボタンをONにした後200ms経過したら、自動でオフにする
  2. if (lastButtonState[BUTTON_WAILING] &&
  3. t > lastButtonChangedTime[BUTTON_WAILING] + 200)
  4. {
  5. Joystick.setButton(BUTTON_WAILING, false);
  6. lastButtonState[BUTTON_WAILING] = false;
  7. lastButtonChangedTime[BUTTON_WAILING] = t;
  8. buttonStateChanged = true;
  9. #ifdef DEBUG
  10. Serial.print(t);
  11. Serial.println(":WAILING OFF");
  12. #endif
  13. }

処理の高速化

MPU-6050の情報はI2C通信を経由して取得するため、ほかのボタンやアナログスティックと比べると情報取得がどうしても遅くなります。 またMPU-6050のZ軸の角速度以外の情報 (X/Y/Z軸方向の加速度など) も取得して処理を行うと、1回の処理で5~6ms必要となるようでした。

一方で、ギタコンのボタン状態の読み出しは1ループあたり1~2ms程度で済ませたいですよね。そこで、処理の高速化が必要になります。

以下の高速化を一通り施すことで、1ループ当たりの時間が1~2ms程度となりました。

MPU-6050への余計なアクセスや、処理の削除

スケッチのベースにさせていただいた 加速度+ジャイロのGY-521(MPU-6050)を使ってみた -1- (特に縛りなく) や MPU-6050 Accelerometer + Gyro (The Arduino Playground) から、ギタコンで使う情報(Z軸の角速度)に関係しないところを全て削除しました。

そして、更に高速化するために、setup()の96行目近辺で以下の設定をしています。

    • MPU-6050のサンプリングレートを最大化
    • ジャイロセンサーの帯域幅を最大化
  1. // サンプルレートを最大化
  2. MPU6050_write_reg (MPU6050_SMPLRT_DIV, 0);
  3. // FSYNC input disabled, Gyrometer bandwidth=256Hz
  4. MPU6050_write_reg (MPU6050_CONFIG, 0);
I2C通信の帯域幅の拡張

通常、Arduino(のWireライブラリ)を使うと、I2C通信のクロックは100kHzとなります。しかし、I2C通信は規格上400kHzのクロックを使うことができ、MPU-6050もそれに対応しています。そこで、I2C通信のクロックを400kHzに変更することで、I2C通信の速度を4倍にします。

具体的には、C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\wire\src\utility\twi.h を編集し、28行目あたりにあるTWI_FREQの値を100000Lから400000Lに変更します。(参考: MPU6050 (備忘録) )

なお通常、Program Files (x86) 以下にあるファイルを編集しようとすると、権限がなくファイルを開けなかったり書き込めなかったりします。いったんデスクトップにファイルをコピーして編集して元の場所に上書きコピーするとか、そのコピーをするにも管理者としてエクスプローラーを開いてやらないといけないとか、いくつかの工夫が必要です。

  1. #ifndef TWI_FREQ
  2. #define TWI_FREQ 100000L
  3. #endif
delay(1); の削除

試作3までは、loop()の最後で必ずdelay(1);を実行し、1msのウエイトを入れていました。
しかし、1ループ当たりの処理が例えば2ms掛かっているような状況でdelay(1)すると、合計で1ループあたり3ms掛かることになります。
そのため、直前のループ開始時刻と今回のループ開始時刻を比較し、1ms経過していない場合のみ、1ms経過するまで待つ、といった処理に変更しました。

そして、この部分で、ついでにmills()のオーバーフローの対策を加えています。mills()のオーバーフローはArduinoに通電して49~50日程度連続して通電しないと発生しないので通常はオーバーフローに遭遇しない(、だからオーバーフロー対策を端折っちゃおう)、と考えますが、一方でギタコンをUSBでつなぎっぱなしにしているとそのうちいつか必ず発生するということでもあるので、ボタン動作が破綻しない程度の簡単な対策を入れています。

  1. void loop() {
  2. bool buttonStateChanged = false;
  3. unsigned long t = millis();
  4. // 1ms間隔でloop内処理が実行されるようにする
  5. // (前回の処理から1msに満たない時間しか経っていない場合は
  6. // 門前払いする)
  7. if (last_t >= t)
  8. {
  9. // 門前払いの前に、mills()のオーバーフロー対策もしてしまう
  10. if (last_t > (unsigned long)0xFFFF0000 &&
  11. t < (unsigned long)0x00001000)
  12. {
  13. for (int index = 0; index < BUTTONS; index++)
  14. {
  15. // 最終ボタン状態更新時刻を繰り上げて、正負が
  16. // 破綻しないようにする(この程度の対策で問題ないはず)
  17. lastButtonChangedTime[index] = 0;
  18. }
  19. }
  20. return;
  21. }
  22. else
  23. {
  24. last_t = t;
  25. }
複数の入力をまとめて1回で出力する

試作3までは、ボタンやスティックの状態変化を検出したときは、「検出したタイミングでその都度」ギタコンのボタンやスティックの状態を一式、ごっそりとUSB経由でPCに出力していました。

しかしこれだと、スティック操作をしながらボタンを押すなどといった場合に、1回の状態確認ループの中でスティックとボタンの状態変化を検出する都度、ギタコン全体のボタン/スティックの状態をPCに送るので、USBの通信時間分、1回ループの処理時間が長くなり、1回のループ当たり1~2msよりもっと処理時間がかかるようになってしまっていました。

そのため、ボタン等の状態変化を検出してもすぐにはUSB経由で送信せず、ループの終りにまとめて送信するようにしました。そうすれば、1回のループ当たりのUSB送信は1回で済みます。

このため、まずsetup()の70行目付近で、Joysthick_のライブラリを開始する際に、「状態変化時にUSBで自動送信する」機能(AutoSendState)を無効化します。

  // Initialize Joystick Library (AutoSendState=OFF)
  Joystick.begin(false);

その上で、loop()の263行目付近(loop()の最後)で、ボタンの状態変化があった時にだけ、USB経由で現在のボタン等の状態を送信する処理を追加しています。

  1. // ボタンの状態変化があった時にだけ、PCにHID入力を通知する
  2. // 同時に、ボタン状態変化の最終時刻を更新する
  3. if (buttonStateChanged)
  4. {
  5. Joystick.sendState();
  6. last_button_state_changed_t = t;

ジャイロセンサーの取得値の最大値の最大化 (Full-scaleの最大化)

MPU-6050は、初期状態では、ジャイロセンサーで取得できる角速度の最大値は250度/秒です。しかしこれだと、それほど激しくWailingしてなくてもこの上限値になってしまい、回転の強弱を正しく判別できません。そのためこの値の最大値を最大化することで、取得値のレンジを広げ、回転の強弱を区別できるようにします。
具体的には、FS_SELというパラメータを3に設定することで、取得できる角速度の最大値を2000度/秒に拡大します。
setup()の処理がこれに該当します。

設定に必要な資料は、MPU6050 6 DOF ジャイロ加速度計 IMU (の中にある製品マニュアル) (ロボショップ) から得ました。

  1. // FS_SELの設定
  2. error = MPU6050_read (MPU6050_GYRO_CONFIG, &c, 1);
  3. c |= 0x18; // FS_SEL 3
  4. MPU6050_write_reg (MPU6050_GYRO_CONFIG, c);

まとめ

試作4では、配線をすっきりさせ、Wailingに対応しました。

個人的には、PlayStation1用のKONAMI製ギターコントローラーと比べて、Wailingの反応が相当よく、きびきびと反応してくれるので気に入っています。

試作3のスケッチ全体のダウンロード: gtcon-07_.zip (Wailing対応+アナログスティック対応あり)

注意: 高速化のため、このスケッチをArduinoのIDEで開く前に、C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\wire\src\utility\twi.h を編集し、28行目あたりにあるTWI_FREQの値を100000Lから400000Lに変更することをお忘れなく。