Wii Classic Controller
Wiiリモコンのオプションのヌンチャク/クラッシクコントローラ/モーションセンサーはI2C接続のようです。ヌンチャクをArduinoで使うスケッチを参考にクラッシクコントローラをArduino/RaspberryPi Picoで使える
WiiControllerクラスを作ってみました。
Wii Classic Controllerの接続
ヌンチャクと同じコネクタなので同じピオン配置の筈です。ヌンチャクの接続図から、こうなっていると思われます。
※RaspberryPi Picoはmbedベースのボードライブラリを使用しています。
Wii Classic Controllerの解析
I2C_Scannerではアドレスを取得できませんでしたので、アドレスと初期化手順(?)はヌンチャクのスケッチから丸パクリしました。アナログスティック2本とアナログボタン2個なので、少なくとも6個のアナログデータと、ボタンの数分の15ビットのデータが送られている筈です。
ヌンチャクのスケッチを真似て6バイトを受信、ビット列を表示した状態でスティックを操作し、変化したビットを並べ替えてスティックとデータの対応を調べました。
その結果得たデータフォーマットが▼です。
ヌンチャクのスケッチベースでArduinoでは問題なく動作しましたが、RaspberryPi Picoでは意味不明なデータが入りました。
意味不明なデータを観察してみると、定期的に0が入っている事が分かりましたので、受信データを6バイトから8バイトに変更。
初期化コマンド(?)の送信タイミングにも問題があるようなのでI2Cアドレスにダミーアクセスを入れて、バスの利用可否を確認するように修正する事でRaspberryPi Picoでも動作するようになりました。
-- Wii Classic Controller データ読み取りスケッチ --
WiiController.zip ▼下のリストのZIPファイル:読み込んだデータをシリアルモニタに表示しています。
WiiControllerDemo.zip 参考動画で使用している有機発光ダイオード SSD1306にコントローラの状態を表示するスケッチ
※adafruteのSSD1306ライブラリ(ArduinoIDEのライブラリ管理から検索してインストールできます)を使用しています。
※ <>を全角<>に置き換えています。コピペの際は注意してください。
--- WiiClassicController.h
/*
* Wii Classic Controller for Arduino
*
*/
#ifndef WiiClassicController_H
#define WiiClassicController_H
#include <Arduino.h>
class WiiClassicController
{
public:
uint8_t analogLX; // Left Side Anarog Stick X - 6Bit
uint8_t analogLY; // Left Side Anarog Stick Y - 6Bit
uint8_t analogRX; // Right Side Anarog Stick X - 5Bit
uint8_t analogRY; // Right Side Anarog Stick Y - 5Bit
uint8_t analogLB; // Left Side Anarog Buttob - 5Bit
uint8_t analogRB; // Right Side Anarog Button - 5Bit
uint16_t pushButton ;
void init();
void scan();
inline bool isLeft() { return pushButton & 0b0000001000000000 ;} // PAD LEFT
inline bool isRight(){ return pushButton & 0b0000000010000000 ;} // PAD RIGHT
inline bool isUp() { return pushButton & 0b0000000100000000 ;} // PAD UP
inline bool isDown() { return pushButton & 0b0000000001000000 ;} // PAD DOWN
inline bool isLBtn() { return pushButton & 0b0000000000100000 ;} // BUTTON L
inline bool isRBtn() { return pushButton & 0b0000000000000010 ;} // BUTTON R
inline bool isZLBtn(){ return pushButton & 0b1000000000000000 ;} // BUTTON ZL
inline bool isZRBtn(){ return pushButton & 0b0000010000000000 ;} // BUTTON ZR
inline bool isSelectBtn(){ return pushButton & 0b0000000000010000 ;} // BUTTON -
inline bool isHomeBtn() { return pushButton & 0b0000000000001000 ;} // BUTTON HOME
inline bool isStartBtn() { return pushButton & 0b0000000000000100 ;} // BUTTON +
inline bool isXBtn() { return pushButton & 0b0000100000000000 ;} // BUTTON X
inline bool isYBtn() { return pushButton & 0b0010000000000000 ;} // BUTTON Y
inline bool isABtn() { return pushButton & 0b0001000000000000 ;} // BUTTON a
inline bool isBBtn() { return pushButton & 0b0100000000000000 ;} // BUTTON b
private:
uint8_t values[8] ;
void writeReadyCheck();
void sendByte(byte data, byte location);
};
#endif
--- WiiClassicController.cpp
/*
* Wii Classic Controller for Arduino
*
* Copyright 2021 try3dcg@nifty.com, http://try3dcg.world.coocan.jp/
*
*/
#include <Arduino.h>
#include <Wire.h>
#include "WiiClassicController.h"
#define CONTROLLER_ADDRESS 0x52
// Init I2C Interface
void WiiClassicController::init() {
Wire.begin();
sendByte(0x55, 0xF0);
sendByte(0x00, 0xFB);
scan();
}
// SCAN Wii Classic Controller Status
void WiiClassicController::scan() {
// --- I2C Interface ---
int count = 0;
int readLen = Wire.requestFrom(CONTROLLER_ADDRESS, 8);
while(Wire.available()) {
values[count] = Wire.read();
count++;
}
sendByte(0x00, 0x00);
// --- DECODE RECIVE DATA ---
if (readLen == 8 && count == 8) {
// Analog data
analogLX = values[0] & 0b00111111 ;
analogLY = values[1] & 0b00111111 ;
analogRX = ((values[0] & 0b11000000) >> 3) | (( values[1] & 0b11000000) >> 5) | (( values[2] & 0b10000000) >> 7);
analogRY = values[2] & 0b00011111;
analogRB= values[3] & 0b00011111 ;
analogLB = ((values[2] & 0b01100000) >> 2) | ((values[3] & 0b11100000) >> 5) ;
// Digital data
((uint8_t *)&pushButton)[0] = ~values[4] ; // Reverse Logic
((uint8_t *)&pushButton)[1] = ~values[5] ;
}
}
// Wait to I2C Ready
void WiiClassicController::writeReadyCheck() {
int errCount = 0 ;
while(errCount < 100) {
Wire.beginTransmission(CONTROLLER_ADDRESS);
int ready = Wire.endTransmission(); // Check endTransmission is Nomal end.
if (ready == 0) {
break ;
}
errCount ++ ;
delay(10) ;
break;
}
}
// SEND BYTE FOR Wii Classic Conrtroller
void WiiClassicController::sendByte(byte data, byte location)
{
writeReadyCheck() ;
Wire.beginTransmission(CONTROLLER_ADDRESS);
Wire.write(location);
Wire.write(data);
Wire.endTransmission();
delay(10);
}
--- WiiController.ino
#include <Wire.h>
#include "WiiClassicController.h"
#define BAUDRATE 9600
char str[128] ;
uint8_t RX,RY,LX,LY,BL,BR ;
uint16_t BTN ;
WiiClassicController wiiCC ;
void setup() {
Serial.begin(BAUDRATE);
wiiCC.init( ) ;
}
void loop() {
wiiCC.scan( ) ;
if (LX != wiiCC.analogLX || LY != wiiCC.analogLY ||
RX != wiiCC.analogRY || RY != wiiCC.analogRY ||
BL != wiiCC.analogLB || BR != wiiCC.analogLB || BTN != wiiCC.pushButton) {
LX = wiiCC.analogLX ;
LY = wiiCC.analogLY ;
RX = wiiCC.analogRX ;
RY = wiiCC.analogRY ;
BL = wiiCC.analogLB ;
BR = wiiCC.analogRB ;
BTN = wiiCC.pushButton ;
sprintf(str,"L:%3d,%3d R:%3d,%3d B:%3d,%3d B:%04X ",LX,LY,RX,RY,BL,BR,BTN) ;
Serial.print(str);
if (wiiCC.isLeft()) { Serial.print("← "); }
if (wiiCC.isRight()){ Serial.print("→ "); }
if (wiiCC.isUp()) { Serial.print("↑ "); }
if (wiiCC.isDown()) { Serial.print("↓ "); }
if (wiiCC.isLBtn()) { Serial.print("LB "); }
if (wiiCC.isRBtn()) { Serial.print("RB "); }
if (wiiCC.isZLBtn()) { Serial.print("ZL "); }
if (wiiCC.isZRBtn()) { Serial.print("ZR "); }
if (wiiCC.isSelectBtn()) { Serial.print("- "); }
if (wiiCC.isHomeBtn()) { Serial.print("HOME "); }
if (wiiCC.isStartBtn()) { Serial.print("+ "); }
if (wiiCC.isXBtn()) { Serial.print("X "); }
if (wiiCC.isYBtn()) { Serial.print("Y "); }
if (wiiCC.isABtn()) { Serial.print("a "); }
if (wiiCC.isBBtn()) { Serial.print("b "); }
Serial.println() ;
}
}