Wii 在多年前是個熱門的遊戲明星產品,透過無線搖桿跟電腦螢幕上的人物進行互動遊戲,當時推出時吸引很多人來玩,就連平常不玩遊戲的我,也買了一部 Wii 遊戲機當作健身用。後來看到有網路上的高手將 Arduino 連接 Wii 的搖桿手把,讀取搖桿的訊號,可以當作控制周邊設備用,這個實作就來看看搖桿有哪些功能,並控制一個伺服馬達 MG996R,下一篇再來實作控制機器手臂。
WiiChuck介面卡是一種小型PCB,主要是能插入 Nintendo Wii Nunchuck的連接器中,用來連接搖桿上的 4 根電線。Nunchuck本身是一個利用I2C進行通訊,只需 4 條線-GND、VDD(3.3v)、SDA 和 SCL。SDA 和 SCL只需連接 Arduini Uno 上對應的 I2C 引腳(A4 和 A5)即可。Wii Nunchuck具有 2 軸操縱桿、兩個按鈕和 3 軸 ±2g 加速度計,任何具有I2C功能的設備都可以與其通信。與WiiChuck連接的引腳與按鍵代碼,請參考下圖:
WiiChuck介面卡是一種小型PCB,主要是能插入 Nintendo Wii Nunchuck的連接器中,用來連接搖桿上的 4 根電線。Nunchuck本身是一個利用I2C進行通訊,只需 4 條線-GND、VDD(3.3v)、SDA 和 SCL。SDA 和 SCL只需連接 Arduini Uno 上對應的 I2C 引腳(A4 和 A5)即可。Wii Nunchuck具有 2 軸操縱桿、兩個按鈕和 3 軸 ±2g 加速度計,任何具有I2C功能的設備都可以與其通信。與WiiChuck連接的引腳與按鍵代碼,請參考下圖:
[材料]
- Arduino Uno x1
- WiiChuck 轉接板 x1
- Wii 擴充搖桿 x1
- 麵包板 x1
- 伺服馬達 MG996R x1
- 18650電池 x2
- 18650電池座 x1
- 可調變壓轉換器 x1
- 排線 x n 條
[接線圖]
| Aruino Uno | Wii Nunchuck | MG996R伺服馬達 |
|---|---|---|
| 3V3 | + | N/A |
| 5V | N/A | 外接5V正極 |
| GND | - | 外接5V負極 |
| A4(SDA) | D(Data) | N/A |
| A5(SCL) | C(Clock) | N/A |
| D7 | N/A | 控制Pin |
[程式]
Arduino 和 Nunchunk 通訊時,會先送出一個交互協定的訊號,一開始送 2 個位元「0x40」和「0x00」,每次從 Nunchuck 要資料時,會先送一個 Byte「0x00」,Nunchuck 會回傳一個 6 位元的資料。#include <Wire.h>
#include <Servo.h>
#define TWI_FREQ 400000L
Servo servo1;
// 從nunchuck回傳的資料是一個 6位元的值
#define BUFFERSIZE 6
uint8_t outbuf[BUFFERSIZE];
int bufPos = 0;
long refreshTime = 1000 / 30; // 更新時間,以為秒計 millisecs
long lastTime = 0;
void setup() {
Serial.begin (57600);
Wire.begin ();
servo1.attach(7);
nunchuck_init ();
Serial.println("\r\n");
Serial.print ("Start Wii Nunchuk demo\n");
Serial.print ("joy_x\tjoy_y\tacc_x\tacc_y\tacc_z\tz_but\tc_but\r\n");
}
void loop() {
long now = millis(); // get the current time
// 最後一次取得資料到這次取得的時間大於更新時間
if ( now - lastTime >= refreshTime) {
lastTime = now; //儲存現在時間
Wire.requestFrom (0x52, BUFFERSIZE); // request data from the nunchuck
// 將收到的資料放到 buffer
while (Wire.available () && bufPos < BUFFERSIZE) {
outbuf[bufPos] = nunchuk_decode_byte (Wire.read());
bufPos++;
}
if (bufPos == BUFFERSIZE) { // 收到 buffer 滿了
printNunchuckData();
}
// 更新 buffer 位址
bufPos = 0;
send_zero();
}
}
void nunchuck_init() {
Wire.beginTransmission (0x52);
Wire.write (0x40);
Wire.write (0x00);
Wire.endTransmission ();
}
void send_zero() {
Wire.beginTransmission (0x52);
Wire.write (0x00);
Wire.endTransmission ();
}
void printNunchuckData() {
int joy_x_axis = outbuf[0];
int joy_y_axis = outbuf[1];
int accel_x_axis = outbuf[2] * 2 * 2;
int accel_y_axis = outbuf[3] * 2 * 2;
int accel_z_axis = outbuf[4] * 2 * 2;
int z_button = 0;
int c_button = 0;
// byte outbuf[5] 包含 z 和 c 按鍵的 bits,也包含加速度的值
if ((outbuf[5] >> 0) & 1) {
z_button = 1;
}
if ((outbuf[5] >> 1) & 1) {
c_button = 1;
}
if ((outbuf[5] >> 2) & 1) {
accel_x_axis += 2;
}
if ((outbuf[5] >> 3) & 1) {
accel_x_axis += 1;
}
if ((outbuf[5] >> 4) & 1) {
accel_y_axis += 2;
}
if ((outbuf[5] >> 5) & 1) {
accel_y_axis += 1;
}
if ((outbuf[5] >> 6) & 1) {
accel_z_axis += 2;
}
if ((outbuf[5] >> 7) & 1) {
accel_z_axis += 1;
}
// 顯示搖桿資料,顯示搖桿上方方向鍵的 X, Y座標
Serial.print(joy_x_axis, DEC);
Serial.print("\t");
Serial.print(joy_y_axis, DEC);
Serial.print("\t");
// 顯示X,Y,Z三軸的加速度值
Serial.print(accel_x_axis, DEC);
Serial.print("\t");
Serial.print(accel_y_axis, DEC);
Serial.print("\t");
Serial.print(accel_z_axis, DEC);
Serial.print("\t");
// 顯示前方的兩個按鍵狀態
Serial.print(z_button, DEC);
Serial.print("\t");
Serial.print(c_button, DEC);
Serial.print("\t");
// 使用搖桿 X軸,控制伺服馬達,依照實測的 X 值範圍設定 map 值,轉成介於 0 到 180 的值
int value = map(joy_x_axis, 25, 220, 180, 0);
// 顯示傳給 Servo1 的值
Serial.print(value);
Serial.print("\r\n");
servo1.write(value);
}
char nunchuk_decode_byte (char x) {
x = (x ^ 0x17) + 0x17;
return x;
}



張貼留言