はじめまして、りょーつといいます。高専出身の大学院2年生です。今週は,タタコンのデータをArduinoのI2Cで読む方法についてまとめてみようと思います.
目次
1.はじめに
2.コントローラの仕様
3.回路製作
4.プログラミング
5.おわりに
1. はじめに
先日ハードオフでタタコンのコントローラを入手しました.タタコンはWiiの太鼓の達人で使える太鼓の形をした専用コントローラです.Wiiの拡張デバイスをI2C経由で読むというのはヌンチャクでよくやられていると思いますが,そのほかのデバイスについての記事はあまり出回っていないです.
せっかくなのでArduinoを使ってタタコンの値を読んでみようと思い,いろいろやってみたところ,うまくいったので本記事を書くにいたりました.最終的なゴールとしては,Arduinoでコントローラの値を読み,太鼓の仙人をプレイするというところまでやりたいと思っています.システム構成としては図1に示すようなものを考えています.注意しないといけないのが,PCにキーボードとして認識させることができる(HIDデバイス)のは特定のArduinoボードであることです.電子工作でよく使用されるArduino UNOは対象外となります.ATmega32u4を搭載しているArduino Leonardo か Arduino Microを使ってください.私はArduino Microを使いました.
図1 システム構成
2. タタコンの仕様
まずはタタコンの仕様を説明します.とは言ったもののそれらがよく分かる資料を見つけることができなかったので,触っていて分かったことを列挙していこうと思います.ヌンチャクのように製造年数によって仕様が変わる可能性もあるのでいちおう明記しておくと,本記事で扱うのは「NP-110,2008 NBGI」です.
まずはタタコンの各配線の役割について説明します.タタコンからは4本の配線が出ていて,それぞれVCC,GND,SDL,SCLとなります.配線の色と役割の対応について表1にまとめました.注意すべきは,タタコンの電源が3.3Vであることです.Arduino Microは5V駆動のものと3.3V駆動のものの2種類があるので,注意してください.ただしこちらの記事を見る限りはSDAとSCLのレベル変換は必要なさそう(?)です.
表1 タタコンの各配線の役割
Wiiリモコンの拡張デバイスに使用されているI2Cは,Wiiリモコンをマスター,拡張デバイスをスレーブとしています.I2C通信をするためにはスレーブのアドレスと初期化コード,どのような形式のデータが返ってくるのか,の3つを明らかにする必要があります.
まずスレーブのアドレスは「0x52」です.これは,タタコンに限らず全てのWiiリモコン拡張デバイスに共通するみたいです.
次に初期化コードについてですが,いろいろ試した結果以下の3つを送信するとうまくいくことが分かりました.
①0x40,0x00
②0xF0,0x55
③0xFB,0x00
まず,①は旧型のヌンチャク(通称シロヌンチャク)の初期化に使用されるコードです.こちらの記事やこちらの記事を参考にしました.次に,②はI2Cの暗号化を解除するためのコードになります.Wiiの拡張デバイスから送られてくる情報は,デフォルトでは暗号化されているのですが,こちらのコードを使うことで,暗号が解除された状態の情報を取得できるようになります.こちらの記事が参考になります.最後に③は,新型の拡張デバイスを有効化するために必要なコードみたいです.新型のヌンチャク(通称クロヌンチャク?)の有効化とかにも使われるみたいです.こちらの記事を参考にしました.
これら3つのコードについてですが,「いろんな記事のいろんなコードを送っていたらこの3つを送ったときにうまくいったのでおそらくこれだろう」程度のものなので,もしかするともっと良い構成があるかもです.
最後にタタコンから送られてくるデータについて説明します.タタコンからは合計6バイトのデータが送られてきます.なお6バイトのデータが送られてくることはタタコン以外の全拡張デバイスに共通する特徴のようです.タタコンから得られるデータについては文献が見つからなかったので全てのデータを読んで太鼓のセンサに対応するビットを探索しました.以下の表を参考にしてください.
表2 タタコンから得られるデータ
どうやら叩く強さとかは取得しておらず,叩いたか叩いてないかの0or1情報しか得られないようです.なお,叩かれてない状態が1,叩かれると0になります.その他のビットにも何かしらの情報が埋め込まれているのかもしれませんが,少なくともタタコンを叩いただけではビットが変化することはありませんでした.
3. 回路製作
タタコンの仕様が分かったので,Arduinoと接続するための簡単な回路を作っていきたいと思います.まずはタタコンのコネクタをぶった切り,XA4ピンコネクタに付け替えます.ピンの配置は画像を参照ください.
図2 XAコネクタへの付け替え
また,タタコンとArduino Microを接続するためのユニバーサル基板を作ります.Arduino Microのデフォルト仕様として,D2ピン:SDA,D3ピン:SCLが割り当てられているので,そのとおりに配線します.回路図を参考に作ってみてください.Arduino Microの電源はUSB経由でPCから供給します.動作確認するだけならブレッドボードで組んでしまってもいいと思います.
図3 基板の回路図
4. プログラミング
第2章で述べた動作をArduinoのプログラムに落とし込みます.回路構成(使用するArduinoとピン番号)が私と同じ場合は以下のプログラムコードをコピペして使ってください.ボタンとキーの対応はKeyboard.press関数とKeyboard.release関数を用いて行っていますので,他のキーを使用したい場合はそこのコードをいじってください.キー入力の処理はこちらの記事を参考にしました.
とりあえずタタコンを使って太鼓の仙人がプレイできるようなボタン配置にしてあります.高速化するとデータを読み飛ばしてしまうことがあったのでところどころdelay()を入れています.
#include <Wire.h>
#include <Keyboard.h>
uint8_t signalTatacon[6] = {0}; //受け取ったデータを格納する配列.8bit × 6 = 6byte
uint8_t byteCount = 0; //配列のインデックスを管理する
//初期化------------------------------------------------------------------------------------
void setup() {
Wire.begin();
Keyboard.begin();
//I2C通信の初期設定
//旧ヌンチャク初期化コード
Wire.beginTransmission(0x52);
Wire.write((uint8_t)0x40);
Wire.write((uint8_t)0x00);
Wire.endTransmission();
//暗号化解除コード
Wire.beginTransmission(0x52);
Wire.write((uint8_t)0xF0);
Wire.write((uint8_t)0x55);
Wire.endTransmission();
//拡張機器有効化コード
Wire.beginTransmission(0x52);
Wire.write((uint8_t)0xFB);
Wire.write((uint8_t)0x00);
Wire.endTransmission();
}
void loop() {
//受信--------------------------------------------------------------------------
byteCount = 0;
//状態要求(通信のために必要)
Wire.beginTransmission(0x52);
Wire.write((uint8_t)0x00);
Wire.endTransmission();
delay(1); //通信の待ちのために必要,なくすとバグる
//状態取得
//signalTatacon[5] = 0b0(dl)(kl)(dr)(kr)000
Wire.requestFrom(0x52, 6); //6byteデータを受け取る
while (Wire.available()&& byteCount < 6) signalTatacon[byteCount++] = Wire.read();
if(byteCount > 5){
//キー入力-------------------------------------------------------------------------
//左カッ5
if (signalTatacon[5] & 0b00100000) Keyboard.release('d');
else Keyboard.press('d');
//左ドン6
if (signalTatacon[5] & 0b01000000)Keyboard.release('f');
else Keyboard.press('f');
//右ドン4
if (signalTatacon[5] & 0b00010000) Keyboard.release('j');
else Keyboard.press('j');
//右カッ3
if (signalTatacon[5] & 0b00001000) Keyboard.release('k');
else Keyboard.press('k');
}
}
プログラムの大まかな流れを説明しておきます.まず受け取った6バイトのデータを保存する配列signalTataconと,配列のインデックス(何バイト目か)を管理する変数byteCountを用意します.セットアップ関数で諸々の初期化を行った後,ループ関数の中にあるwhile文でデータを取得します.ここでwhileを使うのは,データが1バイトずつ送信されてくるためです.while文のなかでbyteCountをインクリメントしながら,送られてきたデータをsignalTataconに1バイトずつ格納しています.
while文の下にあるif文でPCへのキー入力を行っています.各キーの入力に関係するビット演算が少しややこしいと思うので解説しておきます.まずsignalTatacon[5]は表2における6バイト目を意味します.例えば左カッの状態を知りたい場合は,6バイト目のデータの5ビット目を見る必要があるため,これを取り出す必要があります.上記のコードではsignalTatacon[5]と0b00100000のANDを取ることでこれらを実現しています.0b00100000の"0b"は8bitのデータであることを意味します.
仮にsignalTatacon[5]の6バイト目のデータの5ビット目が"1"だった場合,(ignalTatacon[5] & 0b00100000)の演算結果は0b00100000となります.一方でsignalTatacon[5]の6バイト目のデータの5ビット目が"0"だった場合,(ignalTatacon[5] & 0b00100000)の演算結果は0b00000000となります.if文は中身が"0"かそれ以外かという基準で判定するので,(ignalTatacon[5] & 0b00100000)というビット演算でタタコンが叩かれているかどうかを判別することが可能になります.今回はビット演算を使いましたが,ビットを1つ取り出すという操作は「bitRead関数」をつかうと簡単にできるのでこちらを使うのもいいかなと思います.
5. おわりに
今週の記事では,Arduino Microを使ってタタコンの値を取得し,キー入力に変換する方法を紹介しました.実際に遊んでみた様子を動画におさめて共有したかったのですが,あまりにも近所迷惑になりそうだったのでやめました.ちゃんとセンサの値が取れることは確認済みです.私の手元にあるタタコンは左カッを入力すると左ドンも反応してしまうという不具合があったのですが,Arduinoを介することでそこらへんのエラー処理を実装することもできました.これからも定期的にコントローラの改造をしていきたいと思います.
今週も最後まで読んでいただきありがとうございました~.