Wii nunchuck data logger

I wanted to use the wii nunchuck accelerometer for some measurements. I didn't really want to drag my computer around, so I built a little data logger. The nunchuck accelerometer data gets written to an eeprom. Then later I can download the data to my PC.

Nunchuck data logger

I used a Microchip 25AA512 eeprom. It holds 64000 bytes. Normally the accelerometer data for each axis is in a range of about 1 to 1000. I scaled that down to 1 to 255, so that I could fit one accelerometer reading to a byte. By doing that, the eeprom can hold over 21,000 nunchuck readings. Then I connected the eeprom and nunchuck to an arduino board.

Pressing the Z button starts writing accelerometer data out to the eeprom. The C button stops the data recording and will also write the data out over the USB/Serial connection. You can view the data in any serial terminal program like hyperterminal or minicom.

I used version .10 of the Arduino software. See my earlier entry on setting up an Arduino to read the nunchuck.

The schematics for the data logger are as follows:
nunchuck data logger schematic

Here are a few readings I got from swinging on a swingset:
Swing set G force graph

The X (red) axis measures the left to right G force. That line stays pretty stable. Just a little bit of movement. The Y (purple) axis measures front/back G force. You can see it move up down with the swing moving back and forth. I didn't keep the nunchuck level with the ground. So as the swing tilted up, the nunchuck would tilt up. That kind of mutes the G force on the Y axis. The Z (grey) axis shows the greatest range. When still, that the Z axis is at about 177 or 1 G. As the swing moves up and down, you can see quite a bit of range in the Z axis. I let the swing slowly come to a stop. By looking at the Z axis you can easily see when the swing started to slow down and came to a stop.

Code:

// eeprom program code by Heather Dewey-Hagborg, http://www.arduino.cc/en/Tutorial/SPIEEPROM
// Wii nunchuck code and all other code by Chad Phillips
#include 
#include 
#include 

int DEBUG = 1;

uint8_t outbuf[6];		// array to store arduino output
int ledPin = 8;

int startRecording = 1;

#define DATAOUT 11		//MOSI
#define DATAIN  12		//MISO
#define SPICLOCK  13		//sck
#define SLAVESELECT 10		//ss

//opcodes
#define WREN  6
#define WRDI  4
#define RDSR  5
#define WRSR  1
#define READ  3
#define WRITE 2

/*
Min, Max, Middle, range
X: 74, 177, 125.5, 103
Y: 72, 175, 123.5, 103
Z: 79, 187, 133  , 108
*/

byte clr;
int gCnt = 0;

char
spi_transfer (volatile char data)
{
  SPDR = data;			// Start the transmission
  while (!(SPSR & (1 << SPIF)))	// Wait the end of the transmission
    {
    };
  return SPDR;			// return the received byte
}


void
setup ()
{
  Serial.begin (19200);
  if (DEBUG)
    {
      Serial.println ("Setup start");
    }

  pinMode (DATAOUT, OUTPUT);
  pinMode (DATAIN, INPUT);
  pinMode (SPICLOCK, OUTPUT);
  pinMode (SLAVESELECT, OUTPUT);
  digitalWrite (SLAVESELECT, HIGH);	//disable device

  // SPCR = 01010000
  //interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
  //sample on leading edge of clk,system clock/4 rate (fastest)
  SPCR = (1 << SPE) | (1 << MSTR);
  clr = SPSR;
  clr = SPDR;
  Wire.begin ();		// join i2c bus with address 0x52
  nunchuck_init ();		// send the initilization handshake
  if (DEBUG)
    {
      Serial.println ("Setup End");
    }
}

void
start_eeprom_transfer (char xData, char yData, char zData, int gCnt)
{
  // Write to first two bytes of eeprom
  // what the count of acceleration readings is
  write_byte ((char) (gCnt >> 8), 0);
  write_byte (gCnt, 1);

  // Write acceleration data to eeprom
  write_byte (xData, gCnt * 3 + 2);
  write_byte (yData, gCnt * 3 + 3);
  write_byte (zData, gCnt * 3 + 4);
}

void
write_byte (char data, int address)
{
  digitalWrite (SLAVESELECT, LOW);
  spi_transfer (WREN);		//write enable
  digitalWrite (SLAVESELECT, HIGH);
  delay (10);
  digitalWrite (SLAVESELECT, LOW);
  spi_transfer (WRITE);		//write instruction
  spi_transfer ((char) (address >> 8));	//send MSByte address first
  spi_transfer ((char) (address));	//send LSByte address

  // start spi transfer
  spi_transfer (data);		//write data byte
  digitalWrite (SLAVESELECT, HIGH);	//release chip
  delay (20);
}

byte
read_eeprom (int EEPROM_address)
{
  //READ EEPROM
  int data;
  digitalWrite (SLAVESELECT, LOW);
  spi_transfer (READ);		//transmit read opcode
  spi_transfer ((char) (EEPROM_address >> 8));	//send MSByte address first
  spi_transfer ((char) (EEPROM_address));	//send LSByte address
  data = spi_transfer (0xFF);	//get data byte
  digitalWrite (SLAVESELECT, HIGH);	//release chip, signal end transfer
  return data;
}

void
loop ()
{
  int cnt = 0;
  Wire.requestFrom (0x52, 6);	// request data from nunchuck
  while (Wire.available ())
    {
      outbuf[cnt] = nunchuk_decode_byte (Wire.receive ());	// receive byte as an integer
      cnt++;
    }

  // If we recieved the 6 bytes, then go print them
  if (cnt >= 5)
    {
      process_nunchuck_data ();
    }

  cnt = 0;
  send_zero ();			// send the request for next bytes
  delay (10);
}

void
unload_data ()
{
  if (DEBUG)
    {
      Serial.println ("=====Data dump start======");
    }

  int address = 0;
  byte eeprom_output_data;

  int aCnt = read_eeprom (address);
  address++;
  int bCnt = read_eeprom (address);
  address++;
  unsigned int cCnt = aCnt * 256 + bCnt;
  if (DEBUG)
    {
      Serial.print ("Count");
      Serial.println (gCnt, DEC);
      Serial.print ("Top bits: ");
      Serial.print (aCnt, DEC);
      Serial.print (" bottom bits: ");
      Serial.println (bCnt, DEC);
      Serial.print (" computed bits: ");
      Serial.println (cCnt, DEC);
      delay (3000);
    }

  while (address < (cCnt * 3 + 2))
    {
      int xData = read_eeprom (address);
      address++;
      int yData = read_eeprom (address);
      address++;
      int zData = read_eeprom (address);
      address++;

      Serial.print (xData, DEC);
      Serial.print (",");
      Serial.print (yData, DEC);
      Serial.print (",");
      Serial.print (zData, DEC);
      Serial.println ();

      delay (20);
    }
  if (DEBUG)
    {
      Serial.println ("=====Data dump end======");
    }
  delay (3000);
}

void
nunchuck_init ()
{
  if (DEBUG)
    {
      Serial.println ("Nunchuck init start.");
    }
  Wire.beginTransmission (0x52);	// transmit to device 0x52
  Wire.send (0x40);		// sends memory address
  Wire.send (0x00);		// sends sent a zero.  
  Wire.endTransmission ();	// stop transmitting
  if (DEBUG)
    {
      Serial.println ("Nunchuck init end.");
    }
}

void
send_zero ()
{
  Wire.beginTransmission (0x52);	// transmit to device 0x52
  Wire.send (0x00);		// sends one byte
  Wire.endTransmission ();	// stop transmitting
}

// 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
void
process_nunchuck_data ()
{
  // 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]
  int joy_x_axis = outbuf[0];
  int joy_y_axis = outbuf[1];

  int accel_x_axis = (outbuf[2] << 2) + ((outbuf[5] >> 2) & 0x03);
  int accel_y_axis = (outbuf[3] << 2) + ((outbuf[5] >> 4) & 0x03);
  int accel_z_axis = (outbuf[4] << 2) + ((outbuf[5] >> 6) & 0x03);

  int z_button = outbuf[5] & 1;
  int c_button = outbuf[5] & 2;

  if (startRecording < 1)
    {
      digitalWrite (ledPin, HIGH);	// sets the LED on
      start_eeprom_transfer (accel_x_axis / 4, accel_y_axis / 4,
			     accel_z_axis / 4, gCnt);
      gCnt++;
    }
  else
    {
      digitalWrite (ledPin, LOW);	// sets the LED off
    }

  if (c_button < 1)
    {
      digitalWrite (ledPin, LOW);	// sets the LED off
      unload_data ();
      gCnt = 0;
      startRecording = 1;
    }

  if (z_button < 1)
    {
      startRecording = 0;
      gCnt = 0;
      digitalWrite (ledPin, HIGH);	// sets the LED on
    }
  else
    {
      digitalWrite (ledPin, LOW);	// sets the LED off
    }
  if (DEBUG)
    {
      Serial.print (joy_x_axis / 4, DEC);
      Serial.print ("\t");

      Serial.print (joy_y_axis / 4, DEC);
      Serial.print ("\t");

      Serial.print (accel_x_axis / 4, DEC);
      Serial.print ("\t");

      Serial.print (accel_y_axis / 4, DEC);
      Serial.print ("\t");

      Serial.print (accel_z_axis / 4, DEC);
      Serial.print ("\t");

      Serial.print (z_button, DEC);
      Serial.print ("\t");

      Serial.print (c_button, DEC);
      Serial.print ("\t");
      Serial.print ("\r\n");
    }
}

// Encode data to format that most wiimote drivers except
// only needed if you use one of the regular wiimote drivers
char
nunchuk_decode_byte (char x)
{
  x = (x ^ 0x17) + 0x17;
  return x;
}

Comments

Efex Studios:

Didn't understand a few parts of it, but well done overall.

--
web design in plano

web design in plano:

Didn't understand a few parts of it, but well done overall.

Web Hosting Evaluation:

Very cool project. I'm getting into arduino projects myself.

--
web hosting reveiws

Luca D:

Hi Chad,

Very interesting works. If you have time I have two questions for you.

1st Configuration: Wii Motion Plus + Nunchuck
Do you think the Wii Motion Plus and the Nunchuck could be connected together via the I2C bus to Arduino?

2nd Configuratin: Wii Remote + Wii Motin Plus:
Do you think that I2C port present on the Motion Plus could be used to gather data from the remote?

Thanks,
-Luca

LEADS:

Wow great, Thanks for sharing such a valuable coding here, Me too was searching for such an example... Cheers man,

CanMac20:

Nice job Chad and Heather. Got my app to work like a dream 1st shot. Now we need to include an I2C LCD and a RTC, do u have any suggestions how to proceed?

davis:

You did a fantastic job on this! However, if you made a pcb for this project, you could get an smd atmega168, smd ftdi chip, the eeprom, and all the components on one small pcb. Then, you could cut the nunchuck's cord and place a female usb connector (or maybe a mini usb connector?) so you can download the data without everything else taking up space.

Post new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
This question is used to make sure you are a human visitor and to prevent spam submissions.
Image CAPTCHA
Enter the characters shown in the image.