Arduino筆記(92):Wii Nunchcuk搖桿控制伺服馬達

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連接的引腳與按鍵代碼,請參考下圖:

[材料]

  • Arduino Uno x1
  • WiiChuck 轉接板 x1
  • Wii 擴充搖桿 x1
  • 麵包板 x1
  • 伺服馬達 MG996R x1
  • 18650電池 x2
  • 18650電池座 x1
  • 可調變壓轉換器 x1
  • 排線 x n 條

[接線圖]

Aruino UnoWii NunchuckMG996R伺服馬達
3V3+N/A
5VN/A外接5V正極
GND-外接5V負極
A4(SDA)D(Data)N/A
A5(SCL)C(Clock)N/A
D7N/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;
}

[結果]


[參考資料]

Post a Comment

較新的 較舊