In memory of Ben “bushing” Byer, who passed away on Monday, February 8th, 2016.

Difference between revisions of "Wiimote/Extension Controllers/Nunchuck"

From WiiBrew
Jump to navigation Jump to search
Line 117: Line 117:
  
  
Snakebyte Wireless Motion X code Added By Ein
 
----------------------------------------------------
 
I made a code based on MikeT and adapted code by Michael Dreherfor (thanks guys!) for wireless Nunchuk controller from Snakebyte. Working fine.
 
The problem was in initialization handshake sequence addresses that is a little different for this device.
 
  
        Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR); // transmit to device 0x52
+
Arduino code for Snakebyte Wireless Motion X added by Ein
Wire.send (0xF0); // sends memory address Snakebyte Wireless Motion XS controller
+
---------------------------------------------------------------
Wire.send (0x55); // sends data.
+
:I made a code based on MikeT and adapted code by Michael Dreherfor (thanks guys!) for wireless Nunchuk controller from Snakebyte. Working fine.
        Serial.println("Handshake 1/2....");
+
:The problem was in initialization handshake sequence addresses that is a little different for this device.
        if(Wire.endTransmission() == 0) { // stop transmitting
 
            Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR); // transmit to device 0x52
 
    Wire.send (0xA5); // sends memory address for Snakebyte Wireless Motion XS controller
 
      Wire.send (0x00); // sends sent a zero.
 
        }
 
 
 
 
 
Below code to copy and pastle to two files: Nunchuk.pde and NunchukFunctions.h
 
 
 
File: Nunchuk.pde
 
--- START ---
 
//  Wireless Nunchuck soft for third party wireless nunchucks)
 
//  Created by Ein(bartosz.zydron@gmail.com), based on code by MikeT and adapted code by Michael Dreher
 
//  Dedicated version for Snakebyte Wireless Motion XS controller
 
 
 
//Arduino Duemilanove (ATMEGA 328) data wireing
 
//S1 - SCK
 
//S2 - SDA
 
 
 
//Arduino Duemilanove (ATMEGA 328) power wireing
 
//3.3V
 
//Ground
 
 
 
//1900 Ohm resistor between 3.3V and S1(SCK)
 
//1900 Ohm resistor between 3.3V and S2(SDA)
 
 
 
 
 
#include <Wire.h>
 
#include "NunchukFunctions.h"
 
 
 
void setup()
 
{
 
  Serial.begin(38400);
 
  nunchuck_pre_init();
 
  Serial.println("Finished setup");
 
}
 
 
 
void loop()
 
{
 
  delay(20);  // Change for speed/accuracy of results
 
 
 
  nunchuck_get_data();
 
  nunchuck_save_data();
 
 
 
  Serial.print (nunchuck_joy_x, DEC);
 
  Serial.print ("\t");
 
  Serial.print (nunchuck_joy_y, DEC);
 
  Serial.print ("\t");
 
  Serial.print (nunchuck_c_button, DEC);
 
  Serial.print ("\t");
 
  Serial.print (nunchuck_z_button, DEC);
 
  Serial.print ("\t");
 
  Serial.print (nunchuck_accel_x, DEC);
 
  Serial.print ("\t");
 
  Serial.print (nunchuck_accel_y, DEC);
 
  Serial.print ("\t");
 
  Serial.print (nunchuck_accel_z, DEC);
 
  Serial.print ("\t");
 
  Serial.print ("\r\n");
 
 
 
  delay(1);
 
}
 
--- END ----
 
 
 
File: NunchukFunctions.h
 
--- START ---
 
//  Wireless Nunchuck soft for third party wireless nunchucks)
 
//  Created by Ein(bartosz.zydron@gmail.com), based on code by MikeT and adapted code by Michael Dreher
 
//  Dedicated version for Snakebyte Wireless Motion XS controller
 
 
 
//Arduino Duemilanove (ATMEGA 328) data wireing
 
//S1 - SCK
 
//S2 - SDA
 
 
 
//Arduino Duemilanove (ATMEGA 328) power wireing
 
//3.3V
 
//Ground
 
 
 
//1900 Ohm resistor between 3.3V and S1(SCK)
 
//1900 Ohm resistor between 3.3V and S2(SDA)
 
 
 
#define USE_NEW_WAY_INIT 1   // use "The New Way" of initialization <http://wiibrew.org/wiki/Wiimote#The_New_Way>
 
#define WII_IDENT_LEN ((byte)6)
 
#define WII_TELEGRAM_LEN ((byte)6)
 
#define WII_NUNCHUCK_TWI_ADR ((byte)0x52)
 
 
 
#include <Wire.h>
 
#include <string.h>
 
#include </usr/share/arduino/libraries/Wire/utility/twi.h>
 
#undef int
 
#include <stdio.h>
 
#include <WProgram.h>
 
 
 
static uint8_t outbuf[WII_TELEGRAM_LEN]; // array to store arduino output
 
static int cnt = 0;
 
static int ledPin = 13;
 
static int nunchuck_joy_x, nunchuck_joy_y, nunchuck_c_button, nunchuck_z_button, nunchuck_accel_x, nunchuck_accel_y, nunchuck_accel_z;
 
static int nunchuck_calibrate_joy_x = 128, nunchuck_calibrate_joy_y = 130;    //subtract these values from nunchuck_joy values to get 0 as centered value
 
 
 
static byte readControllerIdent(byte* pIdent)
 
{
 
  static byte rc = 1;
 
  
  // read identification
+
        <nowiki>Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR)
  Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR); // transmit to device 0x52
+
Wire.send (0xF0);
  Wire.send (0xFA); // sends memory address of ident in controller
+
Wire.send (0x55);
  if(Wire.endTransmission () == 0) // stop transmitting
 
  {
 
    static byte i;
 
    Wire.requestFrom (WII_NUNCHUCK_TWI_ADR, WII_TELEGRAM_LEN); // request data from nunchuck
 
    for (i = 0; (i < WII_TELEGRAM_LEN) && Wire.available (); i++)
 
    {
 
pIdent[i] = Wire.receive(); // receive byte as an integer
 
    }
 
    if(i == WII_TELEGRAM_LEN)
 
    {
 
rc = 0;
 
    }
 
  }
 
  return rc;
 
}
 
 
 
 
 
// params:
 
//  timeout: abort when timeout (in ms) expires, 0 for unlimited timeout
 
//  return:  0 == ok, 1 == timeout
 
static byte nunchuck_init (unsigned short timeout)
 
{
 
  static byte rc = 1;
 
  unsigned long time = millis();
 
    do
 
    {
 
        Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR); // transmit to device 0x52
 
Wire.send (0xF0); // sends memory address Snakebyte Wireless Motion XS controller
 
Wire.send (0x55); // sends data.
 
 
         Serial.println("Handshake 1/2....");
 
         Serial.println("Handshake 1/2....");
         if(Wire.endTransmission() == 0) { // stop transmitting
+
         if(Wire.endTransmission() == 0) {
             Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR); // transmit to device 0x52
+
             Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR);
    Wire.send (0xA5); // sends memory address for Snakebyte Wireless Motion XS controller
+
    Wire.send (0xA5);
       Wire.send (0x00); // sends sent a zero.
+
       Wire.send (0x00);
            Serial.println("Handshake 2/2....");
+
         }</nowiki>
            if(Wire.endTransmission () == 0){
 
          rc = 0;
 
    }
 
         }
 
    }     
 
  while (rc != 0 && (!timeout || ((millis() - time) < timeout)));
 
  Serial.println("OK.");
 
  delay(3000);
 
  return rc;
 
}
 
 
 
 
 
static void nunchuck_pre_init()
 
{
 
  Wire.begin(); // initialize i2c
 
  // we need to switch the TWI speed, because the nunchuck uses Fast-TWI
 
  // normally set in hardware\libraries\Wire\utility\twi.c twi_init()
 
  // this is the way of doing it without modifying the original files
 
  #define TWI_FREQ_NUNCHUCK 400000L
 
  TWBR = ((CPU_FREQ / TWI_FREQ_NUNCHUCK) - 16) / 2;
 
 
 
  nunchuck_init(0); // send the initialization handshake
 
 
 
  static byte i;
 
  if(readControllerIdent(outbuf) == 0)
 
  {
 
    Serial.print("Ident=");
 
    for (i = 0; i < WII_TELEGRAM_LEN; i++)
 
    {
 
Serial.print(outbuf[i], HEX);
 
Serial.print(' ');
 
    }
 
    Serial.println();
 
  }
 
}
 
 
 
 
 
static void clearTwiInputBuffer(void)
 
{
 
  // clear the receive buffer from any partial data
 
  while( Wire.available ())
 
    Wire.receive ();
 
}
 
 
 
 
 
static void send_zero ()
 
{
 
  // I don't know why, but it only works correct when doing this exactly 3 times
 
  // otherwise only each 3rd call reads data from the controller (cnt will be 0 the other times)
 
  for(byte i = 0; i < 3; i++)
 
  {
 
    Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR); // transmit to device 0x52
 
    Wire.send (0x00); // sends one byte
 
    Wire.endTransmission (); // stop transmitting
 
    delay(5);
 
  }
 
}
 
 
 
 
 
// Print the input data we have recieved
 
// accel data is 10 bits long
 
// so we read 8 bits, then we have to add
 
// on the last 2 bits.  That is why I
 
// multiply them by 2 * 2
 
static void nunchuck_save ()
 
{
 
  nunchuck_joy_x = outbuf[0] - nunchuck_calibrate_joy_x;
 
  nunchuck_joy_y = outbuf[1] - nunchuck_calibrate_joy_y;
 
  nunchuck_accel_x = outbuf[2] * 2 * 2;
 
  nunchuck_accel_y = outbuf[3] * 2 * 2;
 
  nunchuck_accel_z = outbuf[4] * 2 * 2;
 
 
 
  nunchuck_z_button = 0;
 
  nunchuck_c_button = 0;
 
 
 
  // byte outbuf[5] contains bits for z and c buttons
 
  // it also contains the least significant bits for the accelerometer data
 
  // so we have to check each bit of byte outbuf[5]
 
  if ((outbuf[5] >> 0) & 1)
 
  {
 
    nunchuck_z_button = 1;
 
  }
 
  if ((outbuf[5] >> 1) & 1)
 
  {
 
    nunchuck_c_button = 1;
 
  }
 
 
 
  if ((outbuf[5] >> 2) & 1)
 
  {
 
    nunchuck_accel_x += 2;
 
  }
 
  if ((outbuf[5] >> 3) & 1)
 
  {
 
    nunchuck_accel_x += 1;
 
  }
 
 
 
  if ((outbuf[5] >> 4) & 1)
 
  {
 
    nunchuck_accel_y += 2;
 
  }
 
  if ((outbuf[5] >> 5) & 1)
 
  {
 
    nunchuck_accel_y += 1;
 
  }
 
 
 
  if ((outbuf[5] >> 6) & 1)
 
  {
 
    nunchuck_accel_z += 2;
 
  }
 
  if ((outbuf[5] >> 7) & 1)
 
  {
 
    nunchuck_accel_z += 1;
 
  }
 
 
 
}
 
 
 
 
 
static void nunchuck_save_data()
 
{
 
  clearTwiInputBuffer();
 
 
 
  // If we recieved the 6 bytes, then go print them
 
  if (cnt >= WII_TELEGRAM_LEN)
 
  {
 
    nunchuck_save ();
 
  }
 
}
 
 
 
 
 
// Decode data format that original Nunchuck uses with old init sequence. This never worked with
 
// other controllers (e.g. wireless Nunchuck from other vendors)
 
static char nunchuk_decode_byte (char x)
 
{
 
  x = (x ^ 0x17) + 0x17;
 
  return x;
 
}
 
 
 
 
 
static void nunchuck_get_data()
 
{
 
  send_zero (); // send the request for next bytes
 
  Wire.requestFrom (WII_NUNCHUCK_TWI_ADR, WII_TELEGRAM_LEN); // request data from nunchuck
 
  
  for (cnt = 0; (cnt < WII_TELEGRAM_LEN) && Wire.available (); cnt++)
+
:File: Nunchuk.pde [[Media:Nunchuk.pde]]
  {
+
:File: NunchukFunctions.h [[Media:NunchukFuncktions.h]]
    outbuf[cnt] = nunchuk_decode_byte (Wire.receive ()); // receive byte as an integer
 
    digitalWrite (ledPin, HIGH); // sets the LED on
 
  }
 
}
 
--- END ---
 
  
 
=== Datel Blade-FX ===
 
=== Datel Blade-FX ===

Revision as of 23:03, 20 November 2011

Wii nunchuk

The Nunchuk is identified by the 16-bit constant 0x0000 (0xFEFE encrypted) at register address 0xa400fe. It provides three-axis acceleration data, two digital buttons, and an X-Y analog stick.

Data Format

The Nunchuk reports its information as 6 bytes of data, readable at 0xa40008 and streamable using Data Reporting Modes that include Extension bytes (unused bytes are filled with 0x00). The data is packed into the six bytes as follows (after decryption):

  Bit
Byte 7 6 5 4 3 2 1 0
0 SX<7:0>
1 SY<7:0>
2 AX<9:2>
3 AY<9:2>
4 AZ<9:2>
5 AZ<1:0> AY<1:0> AX<1:0> BC BZ

SX,SY are the Analog Stick X and Y positions, while AX, AY, and AZ are the 10-bit accelerometer data (in the same format as described in Wiimote#Accelerometer).

The values returned by the analog stick in the nunchuk enclosure do not encompass the full possible range, but rather have upper and lower bounds. These bounds seem to be in the same range across Nunchuks, but there is some variation. Analog stick X returns data from around 35 (fully left) to 228(fully right), while analog stick Y returns from around 27 to 220. Center for both is around 128.

The accelerometer data uses the full range of 0-1024. However, the full range is only seen when moving or rotating the Nunchuk sharply. To measure still Nunchuk rotation in space, the following approximate bounds apply: X goes from around 300 (fully tilted left) to 740 (tilted right), turning further starts bringing the value closer to 512 (neutral position). Similarly, Y goes from around 280 (tilted backwards) to 720 (forwards). Z goes from 320 (upside-down) to 760 (right-side up).

BC and BZ are the state of the C and Z buttons (0=pressed).

Nintendo games calibrate the center position of the Analog Stick upon power-up or insertion of the Nunchuk. The mechanism for that is unknown.

Hardware

Wii nunchuk circuit board, top surface
Wii nunchuk circuit board, bottom surface
Wii Nunchuck Board Pinout for use without the cable

The official Nintendo wired nunchuk appears to be electrically much simpler than the other controller extensions. It has a single joystick comprised of a 30KΩ potentiometer per axis, two switches, an accelerometer chip, and a microcontroller.

Function Hardware Circuit board surface and mounting
C membrane switch daughterboard, through-hole
Z membrane switch daughterboard, through-hole
Joystick X axial potentiometer, 30KΩ through-hole
Joystick Y axial potentiometer, 30KΩ through-hole
Accelerometer ST 8XRJ 3L02AE 820 MLT surface mount, top
Microcontroller FNURVL 405 849KM (48-pin QFP) surface mount, bottom

Like the classic controller, the joystick potentiometers appear to be wired in parallel. The accelerometer chip is in the LIS3L02 series from STMicroelectronics. The AE variant is not on their web site; their closest public product is their LIS3L02AT, in the same LGA-8 package, but possibly with a different pinout.

The microcontroller chip appears to be a relabeled NEC (now Renesas) uDP78F05 microcontroller, or a pin-compatible equivalent.

Wireless Nunchuks

It looks like third-party Wireless Nunchuks operate differently than the original Nunchuk-Extension. Anyone have a clue?

Nyko Kama

The Nyko Kama is just a simple wireless Nunchuk, with a Sync and Power button. It uses two AAA batteries.

The Nyko Kama does not work with current homebrew or PC Wiimote software. It is unknown how to make it work.

I was able to initialize the Kama and get data from it. My procedure is as follows: Write 0x55 to a400f0, write 0x00 to a400fb; then set up the encryption key (without it, the Nunchuk doesn't work) at a40040 (16 bytes). As a last step, set Data Reporting Mode to 0x37 (my Kama does not work when I set Data Reporting Mode prior to setting up the registers).
This way, buttons are reported correctly, but the joystick values seem to be wrong. Do I need to decrypt the values? The original Nunchuk still works with this altered initialization sequence (unencrypted!).--Andyboeh 23:45, 19 April 2009 (UTC)

Snakebyte Wireless Motion XS

Snakebyte is a German company that makes 3rd party Wii Remotes, Nunchuks, Classic Controllers, and Sensor Bars.

On the Discussion page, the user "Grid" supplied the following information: (Abridged. Spelling and grammar have been corrected.)

"I am using a wireless Nunchuk controller by some third party company (Snakebyte), and I was trying many, many times to get the wireless Nunchuk running on any lib out there. But anytime I plug it into my WiiMote my application slows down and I don't get any response from the Nunchuk. I've got no idea, my Wii accepts the wireless Nunchuk without any problem.
I don't see any problem with any Wii game. Some WiiMote libs, which support the original Nunchuk perfectly, toggle between "nothing inserted" and "partially inserted" when the wireless Nunchuk is plugged in. Also no Data is streamed from the Nunchuk. It's extremely strange!!!! I've got no clue!
I'm still trying to run a Wireless Nunchuk. One problem is that the address areas where the calibration data is normally stored is write-only on the wireless Nunchuks. But the weird thing about this wireless Nunchuk from Snakebyte [1] is that I got no response at all until I played Metroid Prime 3 or Zelda with it. I am not kidding!!! After playing one of these games, I get all the data, only without the calibration, so I can't really use the data because I don't know how to calibrate the accelerometers and the joystick."

That appears to be similar behaviour to what I observed with the Datel Blade-FX as documented below. It returns 0xFF 0xFF as the extension type, but then when you try to read the calibration data it disconnects and then reconnects later, thus producing the observed "toggle between "nothing inserted" and "partially inserted" when the wireless Nunchuk is plugged in". CarlKenner 04:20, 28 November 2008 (UTC)


Arduino code for Snakebyte Wireless Motion X added by Ein


I made a code based on MikeT and adapted code by Michael Dreherfor (thanks guys!) for wireless Nunchuk controller from Snakebyte. Working fine.
The problem was in initialization handshake sequence addresses that is a little different for this device.
       Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR)
	Wire.send (0xF0);
	Wire.send (0x55);
        Serial.println("Handshake 1/2....");
        if(Wire.endTransmission() == 0) {
            Wire.beginTransmission (WII_NUNCHUCK_TWI_ADR);
	    Wire.send (0xA5);
  	    Wire.send (0x00);
        }
File: Nunchuk.pde Media:Nunchuk.pde
File: NunchukFunctions.h Media:NunchukFuncktions.h

Datel Blade-FX

This wireless Nunchuk can emulate either a Nunchuk or a Classic Controller. At startup it behaves like a Nunchuk, but pressing the "Classic" button puts it into Classic Controller mode. While in Classic Controller mode, the FUNCTION LED will be solidly lit (red). Classic controller emulation allows it to be used in the Wii system menu, with the joystick controlling the cursor, and the Z button selecting, and the C button having no visible effect. It also allows it to work (according to the instructions) with some Virtual Console games.

This wireless Nunchuk also has a cheat feature that remembers a motion that you do and repeats it continuously without you having to keep doing the motion. That is done with the "FX" button.

There is also a "SYNC" button where the cord would be. Pressing it once connects the wireless nunchuk. While connected, the STATUS LED will slowly flash.

When the wireless adapter is plugged in, but the wireless nunchuk has not been synced or turned on, then it behaves as though nothing was plugged in. No status report is generated when the wireless adapter is plugged in or unplugged, unless the nunchuk is synced. Querying the status report returns 0 for the extension flag (instead of 2).

But when the wireless nunchuk is synced, then it sends a status report indicating that an extension is connected. Trying to read the 16 bytes from 0xF0 of the expansion (to identify the expansion) successfully returns "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF", which isn't particularly helpful. Trying to read any 16 byte block has the same effect, it returns success, but it is all FFs.

BUT... trying to read the calibration data from 0x20 or 0x30 (although it still returns success as above) causes the Nunchuk to disconnect, sending a status report to indicate no extension is plugged in. It will automatically reconnect 0.5 seconds later. While disconnected, attempting to read from the Nunchuk returns error 7.

Expansion data (in the input reports that include expansion data) is all 0xFF, just like trying to read the Nunchuk memory.

As soon as the "Classic" button is pressed, a status message is sent saying that the extension has been unplugged. It will stay "unplugged" until the status button is released again, however long they hold down the button for. Once the "Classic" button has been released, it will wait about half a second and then send a status message saying the extension has been plugged in again. If the Classic button is pressed twice before it sent the released message, then it may send another disconnect and connect message after the connect message. It is not currently possible to tell whether it is in Classic Controller or Nunchuk mode. But we can use this method to read when the "Classic" button is held down (although it will always look like it was held down longer than it really was).

Actually unplugging the adapter will give the same disconnect message, so after a few seconds one should assume that they did a real disconnect instead of holding down the "Classic" button. In one case, after unplugging and replugging the adapter, the BladeFX started returning all 00 instead of FF when reading the expansion type from 0xF0. The data returned from report 0x34 also had 00 00 00 as the last 3 extension bytes, even though the first 16 (starting from 0x08 in extension memory) were all FF. Trying to read the calibration from 0x20 still returned all 0xFF and then disconnected as usual. Pressing buttons on the BladeFX had no effect on the data returned in report 0x34. I have not done any additional experiments with this state yet.

Clearly the current method of turning on an expansion doesn't work for the Datel Blade-FX, and we need some way to turn it on. Perhaps then the calibration can be read without an error, and we can get real data.

It is not yet known how to make the Datel Blade-FX work, although it can be detected when it is synced, due to its strange behaviour, and the "Classic" button can be read. Nothing else is readable yet.

Datel Wireless Duo-FX

Using the standard initalization sequence, this nunchuck does not work. However, it works perfectly well with the sequence posted above for the Nyko Kama. The data is streamed unencrypted and the Datel can be fully read. However, the Nunchuk must be synced before initalizing the Extension Controller, otherwise, strange errors occur. For now, simply repeat the initialization sequence. After some tries, the Nunchuk works correctly (can be up to 10).