/*
* File : wiinunchuk.h V0.9
* Author: Tim Teatro
* Date : Feb 2012
*
* Description:
*
* Library to set up and poll a Wii nunchuk with Arduino. There are
* many libraries available to do this, none of which I really liked.
* I was fond of Tod Kurt's, but his was incomplete as it did not work
* with knockoff nunchuks, it did not consider the least significant
* bits of accelerometer data and didn't have any advanced functions
* for processing the data such as calculating pitch and roll angles.
*
*
* Provides functions:
* void nunchuk_setpowerpins()
* void nunchuk_init()
* int nunchuk_get_data()
* void nunchuk_calibrate_joy()
* inline unsigned int nunchuk_zbutton()
* inline unsigned int nunchuk_cbutton()
* inline int nunchuk_joy_x()
* inline int nunchuk_cjoy_x()
* inline int nunchuk_cjoy_y()
* inline uint16_t nunchuk_accelx()
* inline uint16_t nunchuk_accely()
* inline uint16_t nunchuk_accelz()
* inline int nunchuk_caccelx()
* inline int nunchuk_caccely()
* inline int nunchuk_caccelz()
* inline int nunchuk_joyangle()
* inline int nunchuk_rollangle()
* inline int nunchuk_pitchangle()
* void nunchuk_calibrate_accelxy()
* void nunchuk_calibrate_accelz()
*
* This library is inspired by the work of Tod E. Kurt,
* (http://todbot.com/blog/bionicarduino/)
*
* (c) 2012 by Tim Teatro
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#if (ARDUINO >= 100)
#include
#else
#include
#endif
//
// These are suitable defaults for most nunchuks, including knockoffs.
// If you intend to use the same nunchuk all the time and demand accu-
// racy, it is worth your time to measure these on your own.
// If you may want to use various nunchuks, you may want to
// calibrate using functions
// nunchuk_calibrate_joy()
// nunchuk_calibrate_accelxy()
// nunchuk_calibrate_accelz()
//
#define DEFAULT_CENTRE_JOY_X 124
#define DEFAULT_CENTRE_JOY_Y 132
#define ACCEL_ZEROX 490
#define ACCEL_ZEROY 500
#define ACCEL_ZEROZ 525
//
// Global vars are kept to a minimum.
//
uint8_t ctrlr_type[6]; // Used externally?
uint8_t nunchuk_buf[6]; // Keeps data payload from nunchuk
// Accelerometer values and callibration centres:
uint16_t accel_zerox, accel_zeroy, accel_zeroz;
// Joystick values and calibration centres:
int joy_x, joy_y, joy_zerox, joy_zeroy;
//
//
// Uses port C (analog in) pins as power & ground for nunchuk
//
void nunchuk_setpowerpins()
{
#define pwrpin PORTC3
#define gndpin PORTC2
DDRC |= _BV(pwrpin) | _BV(gndpin);
PORTC &=~ _BV(gndpin);
PORTC |= _BV(pwrpin);
delay(100); // wait for things to stabilize
}
//
//
// Initialize and join the I2C bus, and tell the nunchuk we're
// talking to it. This function will work both with Nintendo
// nunchuks, or knockoffs.
//
// See http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1264805255
//
void nunchuk_init()
{
Wire.begin();
delay(1);
Wire.beginTransmission(0x52); // device address
#if (ARDUINO >= 100)
Wire.write((uint8_t)0xF0); // 1st initialisation register
Wire.write((uint8_t)0x55); // 1st initialisation value
Wire.endTransmission();
delay(1);
Wire.beginTransmission(0x52);
Wire.write((uint8_t)0xFB); // 2nd initialisation register
Wire.write((uint8_t)0x00); // 2nd initialisation value
#else
Wire.send((uint8_t)0xF0); // 1st initialisation register
Wire.send((uint8_t)0x55); // 1st initialisation value
Wire.endTransmission();
delay(1);
Wire.beginTransmission(0x52);
Wire.send((uint8_t)0xFB); // 2nd initialisation register
Wire.send((uint8_t)0x00); // 2nd initialisation value
#endif
Wire.endTransmission();
delay(1);
//
// Set default calibration centres:
//
joy_zerox = DEFAULT_CENTRE_JOY_X;
joy_zeroy = DEFAULT_CENTRE_JOY_Y;
accel_zerox = ACCEL_ZEROX;
accel_zeroy = ACCEL_ZEROY;
accel_zeroz = ACCEL_ZEROZ;
}
//
//
// T.T.
// Standard nunchuks use a byte-wise encryption using bit-wise XOR
// with 0x17. This function decodes a byte.
//
// This function is not needed since the way we initialize the nunchuk
// does not XOR encrypt the bits.
//
//static inline char nunchuk_decode_byte (char x)
// {
// x = (x ^ 0x17) + 0x17;
// return x;
// }
static void nunchuk_send_request()
{
Wire.beginTransmission(0x52);// transmit to device 0x52
#if (ARDUINO >= 100)
Wire.write((uint8_t)0x00);// sends one byte
#else
Wire.send((uint8_t)0x00);// sends one byte
#endif
Wire.endTransmission();// stop transmitting
}
//
//
// Gets data from the nunchuk and packs it into the nunchuk_buff byte
// aray. That array will be processed by other functions to extract
// the data from the sensors and analyse.
//
int nunchuk_get_data()
{
int cnt=0;
// Request six bytes from the chuck.
Wire.requestFrom (0x52, 6);
while (Wire.available ())
{
// receive byte as an integer
#if (ARDUINO >= 100)
nunchuk_buf[cnt] = Wire.read();
#else
nunchuk_buf[cnt] = Wire.receive();
#endif
cnt++;
}
Wire.beginTransmission(0x52);// transmit to device 0x52
#if (ARDUINO >= 100)
Wire.write((uint8_t)0x00);// sends one byte
#else
Wire.send((uint8_t)0x00);// sends one byte
#endif
Wire.endTransmission();// stop transmitting
if (cnt >= 5)
{
return 1; // success
}
return 0; // failure
}
//
//
//
// Calibrate joystick so that we read the centre position as (0,0).
// Otherwise, we use the default values from the header.
//
void nunchuk_calibrate_joy()
{
joy_zerox = joy_x;
joy_zeroy = joy_y;
}
// Returns c and z button states: 1=pressed, 0=not
// The state is in the two least significant bits of the 6th byte.
// In the data, a 1 is unpressed and 0 is pressed, so this will be
// reversed. These functions use a bitwise AND to determine the value
// and then the () ? true : false; conditional structure to pass out
// the appropriate state.
//
static inline unsigned int nunchuk_zbutton()
{
return ((nunchuk_buf[5] >> 0) & 1) ? 0 : 1;
}
static inline unsigned int nunchuk_cbutton()
{
return ((nunchuk_buf[5] >> 1) & 1) ? 0 : 1;
}
//
//
// Returns the raw x and y values of the the joystick, cast as ints.
//
static inline int nunchuk_joy_x()
{
return (int) nunchuk_buf[0];
}
static inline int nunchuk_joy_y()
{
return (int) nunchuk_buf[1];
}
//
//
// Return calibrated x and y values of the joystick.
//
static inline int nunchuk_cjoy_x()
{
return (int)nunchuk_buf[0] - joy_zerox;
}
static inline int nunchuk_cjoy_y()
{
return (int)nunchuk_buf[1] - joy_zeroy;
}
//
//
// Returns the raw 10-bit values from the 3-axis accelerometer sensor.
// Of the six bytes recieved in a data payload from the nunchuk, bytes
// 2, 3 and 4 are the most significant 8 bits of each 10-bit reading.
// The final two bits are stored in the 6th bit along with the states
// of the c and z button. These functions take the most significant
// 8-bits and stacks it into a 16 bit unsigned integer, and then tacks
// on the least significant bits from the 6th byte of the data
// payload.
//
// Load the most sig digits into a blank 16-bit unsigned int leaving
// two bits in the bottom ( via a 2-bit shift, << 2) for the least sig
// bits:
// 0x0000 | nunchuk_buff[*] << 2
// Add to the above, the least sig bits. The code for x:
// nunchuk_buf[5] & B00001100
// for example selects the 3rd and 4th bits from the 6th byte of the
// payload to be concatinated with nunchuk_buff[2] to complete the 10-
// bit datum for a given axis.
//
static inline uint16_t nunchuk_accelx()
{
return ( 0x0000 | ( nunchuk_buf[2] << 2 ) +
( ( nunchuk_buf[5] & B00001100 ) >> 2 ) );
}
static inline uint16_t nunchuk_accely()
{
return ( 0x0000 ^ ( nunchuk_buf[3] << 2 ) +
( ( nunchuk_buf[5] & B00110000 ) >> 4 ) );
}
static inline uint16_t nunchuk_accelz()
{
return ( 0x0000 ^ ( nunchuk_buf[4] << 2 ) +
( ( nunchuk_buf[5] & B11000000 ) >> 6 ) );
}
//
//
// Returns the x,y and z accelerometer values with calibration values
// subtracted.
//
static inline int nunchuk_caccelx()
{
return (int)(nunchuk_accelx() - accel_zerox);
}
static inline int nunchuk_caccely()
{
return (int)(nunchuk_accely() - accel_zeroy);
}
static inline int nunchuk_caccelz()
{
return (int)(nunchuk_accelz() - accel_zeroz);
}
//
//
// Returns joystick angle in degrees. It uses the ratio of calibrated
// x and y potentiometer readings to find the angle, zero being direct
// right (positive x) and measured counter-clockwise from there.
//
// If the atan2 function returns a negative angle, it is rotated back
// into a positive angle. For those unfamiliar, the atan2 function
// is a more inteligent atan function which quadrant the vector
// is in, and returns the appropriate angle.
//
static inline int nunchuk_joyangle()
{
double theta;
theta = atan2( nunchuk_cjoy_y(), nunchuk_cjoy_x() );
while (theta < 0) theta += 2*M_PI;
return (int)(theta * 180/M_PI);
}
//
//
// Returns roll angle in degrees. Under the assumption that the
// only acceleration detected by the accelerometer is acceleration due
// to gravity, this function uses the ratio of the x and z
// accelerometer readings to gauge pitch. This only works while the
// nunchuk is being held still or at constant velocity with zero ext-
// ernal force.
//
static inline int nunchuk_rollangle()
{
return (int) ( atan2( (double) nunchuk_caccelx(),
(double) nunchuk_caccelz() ) * 180 / M_PI );
}
//
//
// Returns pitch angle in degrees. Under the assumption that the
// only acceleration detected by the accelerometer is acceleration due
// to gravity, this function uses the ratio of the y and z
// accelerometer readings to gauge pitch. This only works while the
// nunchuk is being held still or at constant velocity with zero ext-
// ernal force.
//
static inline int nunchuk_pitchangle()
{
return (int) ( atan2( (double) nunchuk_caccely(),
(double)nunchuk_caccelz() ) * 180 / M_PI );
}
//
//
// Because gravity pulls down on the z-accelerometer while the nunchuk
// is upright, we need to calibrate {x,y} and {z} separately. Execute
// this function while the nunchuk is known to be upright and then
// execute nunchuk_calibrate_accelz() when the nunchuk is on its side.
//
void nunchuk_calibrate_accelxy()
{
accel_zerox = nunchuk_accelx();
accel_zeroy = nunchuk_accely();
}
//
//
// See documentation for nunchuk_calibrate_xy()
//
void nunchuk_calibrate_accelz()
{
accel_zeroz = nunchuk_accelz();
}
//
//
// EOF