NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Sensors, Actuators, and Robotics » I2C/TWI wii nunchuck help

September 08, 2010
by frankenclaw1
frankenclaw1's Avatar

I'm trying to read the data from a wii nunchuck with my atmega168 nerdkit. I've looked at a lot of guides online explaining to me how I2C works and tried reading the TWI information in the atmega168's datasheet but cannot get anything to work. I haven't been using my microcontroller very long and I'm just wondering if someone could at least show me a sample code that works with the atmega168 to check out and play around with. Anything will help!

September 09, 2010
by Ralphxyz
Ralphxyz's Avatar

A quick search of the Nerdkit forum for twi and I2C comes up with this plus others.

And Humberto gives this WikiPedia link

So it is not explicit but it is a start besides trying to decipher the datasheet.

I picked up a Wii Nunchuck at a yardsale for 50ยข and wanted to connect it to my Nerdkit so please keep us posted on how and what you do.

Ralph

September 18, 2010
by Rick_S
Rick_S's Avatar

Well, I played around with some code and after a days worth of research and trial and error, I finally got the nunchuk working without the use of the arduino libraries.

I've successfully run this on both my original OEM nunchuks. There are 3 files needed for my program to run.

Nunchuck.c twimaster.c i2cmaster.h

As well as a slightly modified make file and the NK libraries in their usual places.

Here is the code and make file.

1st Nunchuck.c

#include <avr/io.h>
#include <inttypes.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "i2cmaster.h"
#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"

#define Nunchk_ADR 0xA4

int main(void)
{
    uint8_t nc_data[6], btnc, btnz, temp,i;
    uint16_t accx, accy, accz;

    // initialize data array

    for(i=0;i<6;i++)
        {
            nc_data[i]=0;
        }

    i2c_init();                                // init I2C interface

    lcd_init();                                 // fire up the LCD
    FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
    lcd_home();

    // Setup the Nunchuk for first time providing address to read

    uint8_t ret=i2c_start(Nunchk_ADR+I2C_WRITE);
    if(ret==1) 
      {
        fprintf_P(&lcd_stream, PSTR("1st start bad"));
        }
    i2c_write(0x40);
    i2c_write(0x00);
    i2c_stop();

    while(1){

            // Start Nunchuck read by writing initiating a start then writing 0x00

            uint8_t ret=i2c_start(Nunchk_ADR+I2C_WRITE);
            if(ret==1) 
            {
                lcd_line_two();
                fprintf_P(&lcd_stream, PSTR("2nd start bad"));
            }
            i2c_write(0x00);                    // Send Address to read from 
            i2c_stop();
            delay_us(500);                      // Wait a bit before sending read request

            // begin read request

            i2c_start_wait(Nunchk_ADR+I2C_READ);

            // read 6 bytes

            for(i = 0; i < 5; i++)
            {
                nc_data[i]=i2c_readAck();
            }

            nc_data[5]= i2c_readNak();
            i2c_stop();

            // Decode data

            for (i = 0; i < 6; i++)
            {
                nc_data[i] = (nc_data[i] ^ 0x17) + 0x17;

            }

            //Display results

            lcd_line_one();
            btnz=0;
            btnc=0;
            if(nc_data[5] & 1) btnz=1;
            if(nc_data[5]&(1<<1))btnc=1;

            // Setup the 10 bit data for accelerometer readings.
            // First shift the MSB's 2 places

            accx = (nc_data[2]<<2);
            accy = (nc_data[3]<<2);
            accz = (nc_data[4]<<2);

            // Then add the least significant two bits
            // for X
            temp = nc_data[5];
            temp = ((temp&0b00001100)>>2);
            accx += temp;
            // for y
            temp = nc_data[5];
            temp = ((temp&0b00110000)>>4);
            accy += temp;
            // and for z
            temp = nc_data[5];
            temp = ((temp&0b11000000)>>6);
            accz += temp;

            // send the data to the LCD
            lcd_home();
            fprintf_P(&lcd_stream, PSTR("JoyX: %3u  JoyY: %3u"),nc_data[0],nc_data[1]);
            lcd_line_two();
            fprintf_P(&lcd_stream, PSTR("Acc X:%4d  Y:%4d"),accx,accy);
            lcd_line_three();
            fprintf_P(&lcd_stream, PSTR("Acc Z:%4d"),accz);
            lcd_line_four();
            fprintf_P(&lcd_stream, PSTR("Button Z:%2u  C:%2u"),btnz,btnc);

        }

    for(;;);

    return 0;

}

Next the two library files from Peter Fleury (modified a little for the NK and Nunchuck)

twimaster.c

/*************************************************************************
* Title:    I2C master library using hardware TWI interface

* Author:   Peter Fleury <pfleury@gmx.ch>  http://jump.to/fleury
* File:     $Id: twimaster.c,v 1.3 2005/07/02 11:14:21 Peter Exp $
* Software: AVR-GCC 3.4.3 / avr-libc 1.2.3
* Target:   any AVR device with hardware TWI 
* Usage:    API compatible with I2C Software Library i2cmaster.h
**************************************************************************/
#include <inttypes.h>
#include <compat/twi.h>

#include "i2cmaster.h"

/* define CPU frequency in Mhz here if not defined in Makefile */
#ifndef F_CPU
#define F_CPU 14745600UL
#endif

/* I2C clock in Hz */
#define SCL_CLOCK  300000L

/*************************************************************************
 Initialization of the I2C bus interface. Need to be called only once
*************************************************************************/
void i2c_init(void)
{
  /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */

  TWSR = (0<<TWPS1) | (0<<TWPS0);                  /* no prescaler */
  TWBR = ((F_CPU/SCL_CLOCK)-16)/2;  /* must be > 10 for stable operation */

}/* i2c_init */

/*************************************************************************  
  Issues a start condition and sends address and transfer direction.
  return 0 = device accessible, 1= failed to access device
*************************************************************************/
unsigned char i2c_start(unsigned char address)
{
    uint8_t   twst;

    // send START condition
    TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // wait until transmission completed

    while(!(TWCR & (1<<TWINT)));
    // check value of TWI Status Register. Mask prescaler bits.
    twst = TW_STATUS & 0xF8;
    if ( (twst != TW_START) && (twst != TW_REP_START)) return 1;

    // send device address
    TWDR = address;
    TWCR = (1<<TWINT) | (1<<TWEN);

    // wail until transmission completed and ACK/NACK has been received
    while(!(TWCR & (1<<TWINT)));

    // check value of TWI Status Register. Mask prescaler bits.
    twst = TW_STATUS & 0xF8;
    if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;

    return 0;

}/* i2c_start */

/*************************************************************************
 Issues a start condition and sends address and transfer direction.
 If device is busy, use ack polling to wait until device is ready

 Input:   address and transfer direction of I2C device
*************************************************************************/
void i2c_start_wait(unsigned char address)
{
    uint8_t   twst;

    while ( 1 )
    {
        // send START condition
        TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

        // wait until transmission completed
        while(!(TWCR & (1<<TWINT)));

        // check value of TWI Status Register. Mask prescaler bits.
        twst = TW_STATUS & 0xF8;
        if ( (twst != TW_START) && (twst != TW_REP_START)) continue;

        // send device address
        TWDR = address;
        TWCR = (1<<TWINT) | (1<<TWEN);

        // wail until transmission completed
        while(!(TWCR & (1<<TWINT)));

        // check value of TWI Status Register. Mask prescaler bits.
        twst = TW_STATUS & 0xF8;
        if ( (twst == TW_MT_SLA_NACK )||(twst ==TW_MR_DATA_NACK) ) 
        {           
            /* device busy, send stop condition to terminate write operation */
            TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);

            // wait until stop condition is executed and bus released
            while(TWCR & (1<<TWSTO));

            continue;
        }
        //if( twst != TW_MT_SLA_ACK) return 1;
        break;
     }

}/* i2c_start_wait */

/*************************************************************************
 Issues a repeated start condition and sends address and transfer direction

 Input:   address and transfer direction of I2C device

 Return:  0 device accessible
          1 failed to access device
*************************************************************************/
unsigned char i2c_rep_start(unsigned char address)
{
    return i2c_start( address );

}/* i2c_rep_start */

/*************************************************************************
 Terminates the data transfer and releases the I2C bus
*************************************************************************/
void i2c_stop(void)
{
    /* send stop condition */
    TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);

    // wait until stop condition is executed and bus released
    while(TWCR & (1<<TWSTO));

}/* i2c_stop */

/*************************************************************************
  Send one byte to I2C device

  Input:    byte to be transfered
  Return:   0 write successful 
            1 write failed
*************************************************************************/
unsigned char i2c_write( unsigned char data )
{   
    uint8_t   twst;

    // send data to the previously addressed device
    TWDR = data;
    TWCR = (1<<TWINT) | (1<<TWEN);

    // wait until transmission completed
    while(!(TWCR & (1<<TWINT)));

    // check value of TWI Status Register. Mask prescaler bits
    twst = TW_STATUS & 0xF8;
    if( twst != TW_MT_DATA_ACK) return 1;
    return 0;

}/* i2c_write */

/*************************************************************************
 Read one byte from the I2C device, request more data from device

 Return:  byte read from I2C device
*************************************************************************/
unsigned char i2c_readAck(void)
{
    TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
    while(!(TWCR & (1<<TWINT)));

    return TWDR;

}/* i2c_readAck */

/*************************************************************************
 Read one byte from the I2C device, read is followed by a stop condition

 Return:  byte read from I2C device
*************************************************************************/
unsigned char i2c_readNak(void)
{
    TWCR = (1<<TWINT) | (1<<TWEN);
    while(!(TWCR & (1<<TWINT)));

    return TWDR;

}/* i2c_readNak */

i2cmaster.h

#ifndef _I2CMASTER_H
#define _I2CMASTER_H   1
/************************************************************************* 
* Title:    C include file for the I2C master interface 
*           (i2cmaster.S or twimaster.c)
* Author:   Peter Fleury <pfleury@gmx.ch>  http://jump.to/fleury
* File:     $Id: i2cmaster.h,v 1.10 2005/03/06 22:39:57 Peter Exp $

* Software: AVR-GCC 3.4.3 / avr-libc 1.2.3
* Target:   any AVR device
* Usage:    see Doxygen manual
**************************************************************************/

#ifdef DOXYGEN
/**
 @defgroup pfleury_ic2master I2C Master library
 @code #include <i2cmaster.h> @endcode

 @brief I2C (TWI) Master Software Library

 Basic routines for communicating with I2C slave devices. This single master 
 implementation is limited to one bus master on the I2C bus.

 This I2c library is implemented as a compact assembler software implementation of the I2C protocol 
 which runs on any AVR (i2cmaster.S) and as a TWI hardware interface for all AVR with built-in TWI hardware (twimaster.c).
 Since the API for these two implementations is exactly the same, an application can be linked either against the
 software I2C implementation or the hardware I2C implementation.

 Use 4.7k pull-up resistor on the SDA and SCL pin.

 Adapt the SCL and SDA port and pin definitions and eventually the delay routine in the module 
 i2cmaster.S to your target when using the software I2C implementation !

 Adjust the  CPU clock frequence F_CPU in twimaster.c or in the Makfile when using the TWI hardware implementaion.

 @note 
    The module i2cmaster.S is based on the Atmel Application Note AVR300, corrected and adapted 
    to GNU assembler and AVR-GCC C call interface.
    Replaced the incorrect quarter period delays found in AVR300 with 
    half period delays.

 @author Peter Fleury pfleury@gmx.ch  http://jump.to/fleury

 @par API Usage Example
  The following code shows typical usage of this library, see example test_i2cmaster.c

 @code

 #include <i2cmaster.h>

 #define Dev24C02  0xA2      // device address of EEPROM 24C02, see datasheet

 int main(void)
 {
     unsigned char ret;

     i2c_init();                             // initialize I2C library

     // write 0x75 to EEPROM address 5 (Byte Write) 
     i2c_start_wait(Dev24C02+I2C_WRITE);     // set device address and write mode
     i2c_write(0x05);                        // write address = 5
     i2c_write(0x75);                        // write value 0x75 to EEPROM
     i2c_stop();                             // set stop conditon = release bus

     // read previously written value back from EEPROM address 5 
     i2c_start_wait(Dev24C02+I2C_WRITE);     // set device address and write mode

     i2c_write(0x05);                        // write address = 5
     i2c_rep_start(Dev24C02+I2C_READ);       // set device address and read mode

     ret = i2c_readNak();                    // read one byte from EEPROM
     i2c_stop();

     for(;;);
 }
 @endcode

*/
#endif /* DOXYGEN */

/**@{*/

#if (__GNUC__ * 100 + __GNUC_MINOR__) < 304
#error "This library requires AVR-GCC 3.4 or later, update to newer AVR-GCC compiler !"
#endif

#include <avr/io.h>

/** defines the data direction (reading from I2C device) in i2c_start(),i2c_rep_start() */
#define I2C_READ    1

/** defines the data direction (writing to I2C device) in i2c_start(),i2c_rep_start() */
#define I2C_WRITE   0

/**
 @brief initialize the I2C master interace. Need to be called only once 
 @param  void
 @return none
 */
extern void i2c_init(void);

/** 
 @brief Terminates the data transfer and releases the I2C bus 
 @param void
 @return none
 */
extern void i2c_stop(void);

/** 
 @brief Issues a start condition and sends address and transfer direction

 @param    addr address and transfer direction of I2C device
 @retval   0   device accessible 
 @retval   1   failed to access device 
 */
extern unsigned char i2c_start(unsigned char addr);

/**
 @brief Issues a repeated start condition and sends address and transfer direction

 @param   addr address and transfer direction of I2C device
 @retval  0 device accessible
 @retval  1 failed to access device
 */
extern unsigned char i2c_rep_start(unsigned char addr);

/**
 @brief Issues a start condition and sends address and transfer direction

 If device is busy, use ack polling to wait until device ready 
 @param    addr address and transfer direction of I2C device
 @return   none
 */
extern void i2c_start_wait(unsigned char addr);

/**
 @brief Send one byte to I2C device
 @param    data  byte to be transfered
 @retval   0 write successful
 @retval   1 write failed
 */
extern unsigned char i2c_write(unsigned char data);

/**
 @brief    read one byte from the I2C device, request more data from device 
 @return   byte read from I2C device
 */
extern unsigned char i2c_readAck(void);

/**
 @brief    read one byte from the I2C device, read is followed by a stop condition 
 @return   byte read from I2C device
 */
extern unsigned char i2c_readNak(void);

/** 
 @brief    read one byte from the I2C device

 Implemented as a macro, which calls either i2c_readAck or i2c_readNak

 @param    ack 1 send ack, request more data from device<br>
               0 send nak, read is followed by a stop condition 
 @return   byte read from I2C device
 */
extern unsigned char i2c_read(unsigned char ack);
#define i2c_read(ack)  (ack) ? i2c_readAck() : i2c_readNak();

/**@}*/
#endif

And Lastly the makeile

GCCFLAGS=-g -Os -Wall -mmcu=atmega168 
LINKFLAGS=-Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt -lm
AVRDUDEFLAGS=-c avr109 -p m168 -b 115200 -P com9
LINKOBJECTS=../libnerdkits/delay.o ../libnerdkits/lcd.o ../libnerdkits/uart.o twimaster.o

all:    Nunchuck-upload

Nunchuck.hex:   Nunchuck.c
    make -C ../libnerdkits
    avr-gcc ${GCCFLAGS} -o twimaster.o -c twimaster.c
    avr-gcc ${GCCFLAGS} ${LINKFLAGS} -o Nunchuck.o Nunchuck.c ${LINKOBJECTS}
avr-objcopy -j .text -O ihex Nunchuck.o Nunchuck.hex

Nunchuck.ass:   Nunchuck.hex

    avr-objdump -S -d Nunchuck.o > Nunchuck.ass

Nunchuck-upload:    Nunchuck.hex
    avrdude ${AVRDUDEFLAGS} -U flash:w:Nunchuck.hex:a

This code will just display the data from the nunchuck on the LCD wired as per the standard NK guide.

This is the end result

Photo

Have fun...

Rick

September 19, 2010
by Rick_S
Rick_S's Avatar

One other thing, I did try the code on a Non-Nintendo Nunchuk I have and it just gave me max values for all the data.

I have read there are minor differences that prevent some aftermarket hardware from working the same. My guess is it has something to do with either the I2C speed or the delay between certain stages of communication. For the OEM Nunchuks I changed the I2C speed from 400kHz to 300kHz and had to add a 500us delay at one spot to get them to work reliably. So if you have a non-OEM nunchuk that doesn't work with the code, try poking in some delays or changing the I2C speed. That may or may not get it working, but that's the fun part... figuring it all out! :D

Rick

September 20, 2010
by Rick_S
Rick_S's Avatar

If anyone else tries this, please let me know... If it does work for you, what are you planning for it? I'm thinking about trying 2 axis servo control. I saw a cool video on youtube with a guy controlling a web cam and it's mimicing his hand movements with the nunchuck.

I'm curious more than anything :D

Rick

September 21, 2010
by jbremnant
jbremnant's Avatar

Rick_S

I just came across this thread and it's now motivating me to complete my unfinished project. Thanks for posting the code. =) I had gotten as far as reliably reading the first 3 bytes, but the last 3 bytes were all 0xFF's for some reason. I also suspected timing issues, but didn't dig in further.

If I can reproduce the same result, I'll you know. I'll be also using OEM nunchuck.

September 21, 2010
by Rick_S
Rick_S's Avatar

Cool, let me know if it works out for you. I'd begun to think no-one was interested. The code performed flawlessly for me on both my OEM nunchucks. If you have any problems just let me know.

Rick

September 21, 2010
by Ralphxyz
Ralphxyz's Avatar

Well I also am very interested and hopefully will be doing this project, thank you.

I have not thought about this but my first question is wiring could you post a picture or schematic of how you have the nunchuk wired to the Nerdkit?

It probable will be this winter before I get around to doing this unless I can come up with a compelling need, maybe some other post about what others have done with the nunchuk will get me motivated.

Ralph

September 21, 2010
by Rick_S
Rick_S's Avatar

The Nunchuck was wired to the chip via the twi/i2c interface these are pins 27 & 28 it also connects to power and ground.

I found the wiring from this little adapter that is sold at a couple of electronics venues such as sparkfun.

photo photo2 photo2

Looking at those photos, you can see which connection is which from the indented side of the connector and figure what is what on the back side. I made my own connector like this so I didn't have to cut the plug off my nunchuck.

The clk line goes to pin 28, the dat line to pin 27, the gnd to ground and the pwr to (5v).

Keep in mind, the nunchuck is designed to run on 3.3v and while mine works fine at 5v, yours may not. I'm not responsible if something goes bad. Also, I've read the new black nunchucks will not work at 5v.

One other thing, be very careful if using an adapter like this as it would be very easy to get it upside down and reverse the voltage going into the nunchuck.

The only other wiring on the chip is the standard NK configuration for the initial setup with the LCD.

Try it... you'll like it :)

Rick

September 25, 2010
by Rick_S
Rick_S's Avatar

I thought I'd post some photo's of the wiring in case my descrptions aren't too good.

First, both sides of my home brew (pretty ugly but functional) connector to the Nunchuck plug.

Next Showing the connections from the Nunchuck to the Nerdkit ATMEGA168.

A Close Overhead View

A view from the pin 1 side of the chip

And lastly a view from overhead showing the LCD wiring

Hope that helps clarify if there were any questions.

Rick

September 25, 2010
by Ralphxyz
Ralphxyz's Avatar

Thanks Rick, I ordered a dual magnetic field device the other day that uses I2C I hope I can use your code. It seems as if I should be able to after all input is input, I'll probable have to make it more specific to the device but it seems as if your code should be a good start. I might only have to change the LCD readings.

Where did you get your wires from they look neat.

Ralph

September 25, 2010
by Rick_S
Rick_S's Avatar

Got them on e-bay. They are flexable with solid tips and make breadboarding much more user friendly... They are a bit more money than standard solid jumpers though.

September 26, 2010
by Rick_S
Rick_S's Avatar

Well, I have the Nunchuck controlling two servo's now. Here is the modified code. The Servo outputs are connected to Pins 15 and 16 respectively (PB1 and PB2). Thanks go to the NK guys for the servo squirter code and Ted (Phrank916) for the two servo mod part. The two made the additions to this code relatively painless :D

Nunchuck.c

#include <avr/io.h>
#include <inttypes.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "i2cmaster.h"
#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"

#define Nunchk_ADR 0xA4
#define PWM_MIN 1300
#define PWM_MAX 4450
#define PWM_START 2765

// Modified from servo squirter program

void pwm_set(uint16_t x, uint16_t y) {
  OCR1B = x;
  OCR1A = y;
}

// Modified from servo squirter program

void pwm_init() {
  // setup Timer1 for Fast PWM mode, 16-bit

  ICR1 = 36864; // sets PWM to repeat pulse every 20.0ms
  pwm_set(PWM_START,PWM_START);
  TCCR1A = (1<<COM1B1) | (1<<WGM11) | (1<<COM1A1);  
  TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);

  // each count is 8/14745600 = 0.5425us.
  // so 1.0ms = 1843.2
  //    1.5ms = 2764.8
  //    2.0ms = 3686.4
  //   20.0ms = 36864
}

int main(void)
{
    uint8_t nc_data[6], btnc, btnz, temp,i;
    uint16_t accx, accy, accz;
    uint16_t posx = PWM_START;
    uint16_t posy = PWM_START;

    // initialize data array

    for(i=0;i<6;i++)
        {
            nc_data[i]=0;
        }

    // set PB1,PB2 as output
    DDRB |= (1<<PB1) | (1<<PB2);

    i2c_init();                                // init I2C interface

    lcd_init();                                 // fire up the LCD

    // init PWM
    pwm_init();

    FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
    lcd_home();

    // Setup the Nunchuk for first time providing address to read

    uint8_t ret=i2c_start(Nunchk_ADR+I2C_WRITE);

    if(ret==1) 
      {
        fprintf_P(&lcd_stream, PSTR("1st start bad"));
        }

    i2c_write(0x40);        // Write the Address to the Nunchuck
    i2c_write(0x00);
    i2c_stop();             // Issue a stop on I2C bus

    while(1){

            // Start Nunchuck read by writing initiating a start then writing 0x00

            uint8_t ret=i2c_start(Nunchk_ADR+I2C_WRITE);
            if(ret==1) 
            {
                lcd_line_two();
                fprintf_P(&lcd_stream, PSTR("2nd start bad"));
            }

            i2c_write(0x00);        // Send Address to read from 
            i2c_stop();             // Issue a stop on I2C bus          
            delay_us(500);          // Wait a bit before sending read request

            // begin read request

            i2c_start_wait(Nunchk_ADR+I2C_READ);

            // read 6 bytes

            for(i = 0; i < 5; i++)
            {
                nc_data[i]=i2c_readAck();
            }

            nc_data[5]= i2c_readNak();
            i2c_stop();

            // Decode data

            for (i = 0; i < 6; i++)
            {
                nc_data[i] = (nc_data[i] ^ 0x17) + 0x17;

            }

            // Determine Button Positions

            btnz=0;
            btnc=0;
            if(nc_data[5] & 1) btnz=1;
            if(nc_data[5]&(1<<1))btnc=1;

            // Setup the 10 bit data for accelerometer readings.

            // First shift the MSB's 2 places

            accx = (nc_data[2]<<2);
            accy = (nc_data[3]<<2);
            accz = (nc_data[4]<<2);

            // Then add the least significant two bits
            // for X
            temp = nc_data[5];
            temp = ((temp&0b00001100)>>2);
            accx += temp;
            // for y
            temp = nc_data[5];
            temp = ((temp&0b00110000)>>4);
            accy += temp;
            // and for z
            temp = nc_data[5];
            temp = ((temp&0b11000000)>>6);
            accz += temp;

            //Display results

            lcd_home();
            fprintf_P(&lcd_stream, PSTR("JoyX: %3u  JoyY: %3u"),nc_data[0],nc_data[1]);
            lcd_line_two();
            fprintf_P(&lcd_stream, PSTR("Acc X:%4d  Y:%4d"),accx,accy);
            lcd_line_three();
            fprintf_P(&lcd_stream, PSTR("Acc Z:%4d"),accz);
            lcd_line_four();
            fprintf_P(&lcd_stream, PSTR("Button Z:%2u  C:%2u"),btnz,btnc);
            if(btnz==0)     // Only move if the button is pressed
            {
                posx=(accx*3)+PWM_MIN;
                posy=(accy*3)+PWM_MIN;
                pwm_set(posx,posy);
            }
        }

    for(;;);

    return 0;

}

I setup a short 2-1/2 minute video on youtube if you want to see the results...

Follow this link

Rick

September 26, 2010
by Ralphxyz
Ralphxyz's Avatar

Rick, that is great. Nice jump of combining the different projects.

Now what is the little board on the left with the capacitors?

Details please.

Ralph

September 26, 2010
by Rick_S
Rick_S's Avatar

That's just a 5V breadboard power supply. They come in very handy to power the projects.

Rick

September 28, 2010
by jbremnant
jbremnant's Avatar

Rick_S, the code you posted worked flawlessly. After some minor changes, it initialized the nunchuck correctly and read in 6 bytes without much trouble at every iteration.

This whole experiment spurred me to dig up my old project and try to figure out why the asynchronous (interrupt-based) TWI library didn't work. I had gotten a copy of it from arduino distribution and hacked around with it. After few hours of trial and error, I finally got it working. Here are some of the observations. (might not be entirely correct). I am not satisfied with the fix though. Putting in delays at specific places in the code to make I2C against nunchuck work seems rather hacky to me.

In any case, the details:

  • looks like asynchronous (interrupt-based) twi library is quirky to get it working correctly with Wii Nunchuck. The library does cover the entire gamut of I2C protocol features, but the interrupt handling routine is possibly too expensive/slow (massive switch statement) to keep up with TWI speed greater than 100kHz. Just a speculation though...

  • However, when twi_readFrom and twi_writeTo is invoked with the blocking "wait" option, it didn't detect the status variable change fast enough when it came out of the initial state. So I had to insert a bit of delay after this snippet of code:

    // wait for read operation to complete
    while(TWI_MRX == twi_state){
      continue;
    }
    delay_us(200);
    
  • I can't get this interrupt driven TWI to communicate reliably with TWI_FREQ above 100kHz. Better stick with this low speed.

  • I tried using 2 different ways to initialize the nunchuck: the old way vs the new way. Both methods seem to work fine. And decoding the bytes wasn't necessary.

  • twi_readFrom was buggy. Actually, it was the Master Receiver section of the interrupt handing routine that was busted. It was reading one more extra byte than necessary:

    // Master Receiver
    case TW_MR_DATA_ACK: // data received, ack sent
      // put byte into buffer
      twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
    case TW_MR_SLA_ACK:  // address sent, ack received
      // ack if more bytes are expected, otherwise nack
      // JB NOTE: you need -1 here because we want to send NACK _at_ the last byte, not after.
      if(twi_masterBufferIndex < twi_masterBufferLength-1){
        twi_reply(1);
      }else{
        twi_reply(0);
      }
      break;
    case TW_MR_DATA_NACK: // data received, nack sent
      // put final byte into buffer
      twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
      // printf_P(PSTR("TW_MR_DATA_NACK TWDR=%d\r\n"), twi_masterBuffer[twi_masterBufferIndex-1]);
    case TW_MR_SLA_NACK: // address sent, nack received
      twi_stop();
      break;
    

Here's the tarball of my rudimentary project files if you are interested. It's bare minimum to get your nunchuck working and displaying the values on LCD using interrupt-driven TWI.

And the twiasync code pasted for reference:

twiasync.h

#ifndef twi_h
#define twi_h

#include <inttypes.h>

// #define ATMEGA8

// for nerdkit

// for arduino
// #define CPU_FREQ 16000000L
#ifndef CPU_FREQ
#define CPU_FREQ 14745600L
#endif

#ifndef TWI_FREQ
// #define TWI_FREQ 400000L
#define TWI_FREQ 100000L
#endif

#ifndef TWI_BUFFER_LENGTH
#define TWI_BUFFER_LENGTH 16
#endif

#define TWI_READY 0
#define TWI_MRX   1
#define TWI_MTX   2
#define TWI_SRX   3
#define TWI_STX   4

void twi_init(void);
void twi_setAddress(uint8_t);
uint8_t twi_readFrom(uint8_t, uint8_t*, uint8_t);
uint8_t twi_writeTo(uint8_t, uint8_t*, uint8_t, uint8_t);
uint8_t twi_transmit(uint8_t*, uint8_t);
void twi_attachSlaveRxEvent( void (*)(uint8_t*, int) );
void twi_attachSlaveTxEvent( void (*)(void) );
void twi_reply(uint8_t);
void twi_stop(void);
void twi_releaseBus(void);

#endif

twiasync.c

// macro definitions are contained in : /usr/avr/include/util/twi.h
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <compat/twi.h>
#include "utils.h"
#include "twiasync.h"

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif

#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

uint8_t twi_slarw;
void (*twi_onSlaveTransmit)(void);
void (*twi_onSlaveReceive)(uint8_t*, int);
volatile uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH];
volatile uint8_t twi_masterBufferIndex;
volatile uint8_t twi_masterBufferLength;
volatile uint8_t twi_txBuffer[TWI_BUFFER_LENGTH];
volatile uint8_t twi_txBufferIndex;
volatile uint8_t twi_txBufferLength;
volatile uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH];
volatile uint8_t twi_rxBufferIndex;
volatile uint8_t twi_state;
volatile uint8_t twi_error;

/* 
 * Function twi_init
 * Desc     readys twi pins and sets twi bitrate
 * Input    none
 * Output   none
 */
void twi_init(void)
{
  // initialize state
  twi_state = TWI_READY;

  // initialize twi prescaler and bit rate
  cbi(TWSR, TWPS0);
  cbi(TWSR, TWPS1);
  TWBR = ((CPU_FREQ / TWI_FREQ) - 16) / 2;

  /* twi bit rate formula from atmega128 manual pg 204
  SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
  note: TWBR should be 10 or higher for master mode
  It is 72 for a 16mhz Wiring board with 100kHz TWI */

  // enable twi module, acks, and twi interrupt
  TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);

  // allocate buffers - dynamic allocation bad
  /*
  twi_masterBuffer = (uint8_t*) calloc(TWI_BUFFER_LENGTH, sizeof(uint8_t));
  twi_txBuffer = (uint8_t*) calloc(TWI_BUFFER_LENGTH, sizeof(uint8_t));
  twi_rxBuffer = (uint8_t*) calloc(TWI_BUFFER_LENGTH, sizeof(uint8_t));
  */
}
/* 
 * Function twi_slaveInit
 * Desc     sets slave address and enables interrupt
 * Input    none
 * Output   none
 */
void twi_setAddress(uint8_t address)
{
  // set twi slave address (skip over TWGCE bit)
  TWAR = address << 1;
}

/* 
 * Function twi_readFrom
 * Desc     attempts to become twi bus master and read a
 *          series of bytes from a device on the bus
 * Input    address: 7bit i2c device address
 *          data: pointer to byte array
 *          length: number of bytes to read into array
 * Output   number of bytes read
 */
uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length)
{
  uint8_t i;

  // ensure data will fit into buffer
  if(TWI_BUFFER_LENGTH < length){
    return 0;
  }

  // wait until twi is ready, become master receiver
  while(TWI_READY != twi_state){
    continue;
  }
  twi_state = TWI_MRX;
  // reset error state (0xFF.. no error occured)
  twi_error = 0xFF;

  // initialize buffer iteration vars
  twi_masterBufferIndex = 0;
  twi_masterBufferLength = length;

  // build sla+w, slave device address + w bit
  twi_slarw = TW_READ;
  twi_slarw |= address << 1;

  // printf_P(PSTR("start read: %d\r\n"), length);
  // send start condition
  TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);

  // wait for read operation to complete
  while(TWI_MRX == twi_state){
    continue;
  }

  delay_us(200);

  if (twi_masterBufferIndex < length)
    length = twi_masterBufferIndex;

  // printf_P(PSTR("read count: %d\r\n"), twi_masterBufferIndex);

  // copy twi buffer to data
  for(i = 0; i < length; i++){
    data[i] = twi_masterBuffer[i];
    // printf_P(PSTR("buf[%d] = %d\r\n"), i, data[i]);
  }
  return length;
}

/* 
 * Function twi_writeTo
 * Desc     attempts to become twi bus master and write a
 *          series of bytes to a device on the bus
 * Input    address: 7bit i2c device address
 *          data: pointer to byte array
 *          length: number of bytes in array
 *          wait: boolean indicating to wait for write or not
 * Output   0 .. success
 *          1 .. length to long for buffer
 *          2 .. address send, NACK received
 *          3 .. data send, NACK received
 *          4 .. other twi error (lost bus arbitration, bus error, ..)
 */
uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait)
{
  uint8_t i;

  // ensure data will fit into buffer
  if(TWI_BUFFER_LENGTH < length){
    return 1;
  }

  // wait until twi is ready, become master transmitter
  while(TWI_READY != twi_state){
    continue;
  }

  twi_state = TWI_MTX;
  // reset error state (0xFF.. no error occured)
  twi_error = 0xFF;

  // initialize buffer iteration vars
  twi_masterBufferIndex = 0;
  twi_masterBufferLength = length;

  // copy data to twi buffer
  for(i = 0; i < length; i++){
    twi_masterBuffer[i] = data[i];
  }

  // build sla+w, slave device address + w bit
  twi_slarw = TW_WRITE;
  twi_slarw |= address << 1;

  // printf_P(PSTR("sending TWI start condition\r\n"));
  // send start condition
  TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);

  // wait for write operation to complete
  while(wait && (TWI_MTX == twi_state)){
    continue;
  }

  // NOTE: wow, adding this delay here made nunchuck work.. wth?
  delay_us(200);
  // printf_P(PSTR("passed TWI start condition\r\n"));

  if (twi_error == 0xFF)
    return 0; // success
  else if (twi_error == TW_MT_SLA_NACK)
    return 2; // error: address send, nack received
  else if (twi_error == TW_MT_DATA_NACK)
    return 3; // error: data send, nack received
  else
    return 4; // other twi error
}

/* 
 * Function twi_transmit
 * Desc     fills slave tx buffer with data
 *          must be called in slave tx event callback
 * Input    data: pointer to byte array
 *          length: number of bytes in array
 * Output   1 length too long for buffer
 *          2 not slave transmitter
 *          0 ok
 */
uint8_t twi_transmit(uint8_t* data, uint8_t length)
{
  uint8_t i;

  // ensure data will fit into buffer
  if(TWI_BUFFER_LENGTH < length){
    return 1;
  }

  // ensure we are currently a slave transmitter
  if(TWI_STX != twi_state){
    return 2;
  }

  // set length and copy data into tx buffer
  twi_txBufferLength = length;
  for(i = 0; i < length; ++i){
    twi_txBuffer[i] = data[i];
  }

  return 0;
}

/* 
 * Function twi_attachSlaveRxEvent
 * Desc     sets function called before a slave read operation
 * Input    function: callback function to use
 * Output   none
 */
void twi_attachSlaveRxEvent( void (*function)(uint8_t*, int) )
{
  twi_onSlaveReceive = function;
}

/* 
 * Function twi_attachSlaveTxEvent
 * Desc     sets function called before a slave write operation
 * Input    function: callback function to use
 * Output   none
 */
void twi_attachSlaveTxEvent( void (*function)(void) )
{
  twi_onSlaveTransmit = function;
}

/* 
 * Function twi_reply
 * Desc     sends byte or readys receive line
 * Input    ack: byte indicating to ack or to nack
 * Output   none
 */
void twi_reply(uint8_t ack)
{
  // printf_P(PSTR("TWI replying\r\n"));
  // transmit master read ready signal, with or without ack
  if(ack){
    TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);
  }else{
    TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);
  }
}

/* 
 * Function twi_stop
 * Desc     relinquishes bus master status
 * Input    none
 * Output   none
 */
void twi_stop(void)
{
  // printf_P(PSTR("TWI stop\r\n"));
  // send stop condition
  TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO);

  // wait for stop condition to be exectued on bus
  // TWINT is not set after a stop condition!
  while(TWCR & _BV(TWSTO)){
    continue;
  }

  // update twi state
  twi_state = TWI_READY;
}

/* 
 * Function twi_releaseBus
 * Desc     releases bus control
 * Input    none
 * Output   none
 */
void twi_releaseBus(void)
{
  // printf_P(PSTR("TWI release bus\r\n"));
  // release bus
  TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);

  // update twi state
  twi_state = TWI_READY;
}

// SIGNAL(SIG_TWI)
// ISR(SIG_2WIRE_SERIAL,ISR_NAKED)
ISR(TWI_vect)
{
  // printf_P(PSTR("interrupt. status is: 0x%x\r\n"), TW_STATUS);

  switch(TW_STATUS) {
    // All Master
    case TW_START:     // sent start condition
    case TW_REP_START: // sent repeated start condition
      // copy device address and r/w bit to output register and ack
      TWDR = twi_slarw;
      twi_reply(1);
      break;

    // Master Transmitter
    case TW_MT_SLA_ACK:  // slave receiver acked address
    case TW_MT_DATA_ACK: // slave receiver acked data
      // if there is data to send, send it, otherwise stop 
      if(twi_masterBufferIndex < twi_masterBufferLength){
        // copy data to output register and ack
        TWDR = twi_masterBuffer[twi_masterBufferIndex++];
        twi_reply(1);
        // printf_P(PSTR("TW_MT_DATA_ACK TWDR=%d\r\n"), twi_masterBuffer[twi_masterBufferIndex-1]);
      }else{
        twi_stop();
      }
      break;
    case TW_MT_SLA_NACK:  // address sent, nack received
      twi_error = TW_MT_SLA_NACK;
      twi_stop();
      break;
    case TW_MT_DATA_NACK: // data sent, nack received
      twi_error = TW_MT_DATA_NACK;
      twi_stop();
      break;
    case TW_MT_ARB_LOST: // lost bus arbitration
      twi_error = TW_MT_ARB_LOST;
      twi_releaseBus();
      break;

    // Master Receiver
    case TW_MR_DATA_ACK: // data received, ack sent
      // put byte into buffer
      twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
      // printf_P(PSTR("TW_MR_DATA_ACK TWDR=%d\r\n"), twi_masterBuffer[twi_masterBufferIndex-1]);
    case TW_MR_SLA_ACK:  // address sent, ack received
      // ack if more bytes are expected, otherwise nack
      // printf_P(PSTR("bufferindex=%d, bufferlength=%d\r\n"), twi_masterBufferIndex, twi_masterBufferLength);
      if(twi_masterBufferIndex < twi_masterBufferLength-1){
        twi_reply(1);
      }else{
        twi_reply(0);
      }
      break;
    case TW_MR_DATA_NACK: // data received, nack sent
      // put final byte into buffer
      twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
      // printf_P(PSTR("TW_MR_DATA_NACK TWDR=%d\r\n"), twi_masterBuffer[twi_masterBufferIndex-1]);
    case TW_MR_SLA_NACK: // address sent, nack received
      twi_stop();
      break;
    // TW_MR_ARB_LOST handled by TW_MT_ARB_LOST case

    // Slave Receiver
    case TW_SR_SLA_ACK:   // addressed, returned ack
    case TW_SR_GCALL_ACK: // addressed generally, returned ack
    case TW_SR_ARB_LOST_SLA_ACK:   // lost arbitration, returned ack
    case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack
      // enter slave receiver mode
      twi_state = TWI_SRX;
      // indicate that rx buffer can be overwritten and ack
      twi_rxBufferIndex = 0;
      twi_reply(1);
      break;
    case TW_SR_DATA_ACK:       // data received, returned ack
    case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack
      // if there is still room in the rx buffer
      if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){
        // put byte in buffer and ack
        twi_rxBuffer[twi_rxBufferIndex++] = TWDR;
        twi_reply(1);
      }else{
        // otherwise nack
        twi_reply(0);
      }
      break;
    case TW_SR_STOP: // stop or repeated start condition received
      // put a null char after data if there's room
      if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){
        twi_rxBuffer[twi_rxBufferIndex] = '\0';
      }
      // callback to user defined callback
      twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex);
      // ack future responses
      twi_reply(1);
      // leave slave receiver state
      twi_state = TWI_READY;
      break;
    case TW_SR_DATA_NACK:       // data received, returned nack
    case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack
      // nack back at master
      twi_reply(0);
      break;

    // Slave Transmitter
    case TW_ST_SLA_ACK:          // addressed, returned ack
    case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack
      // enter slave transmitter mode
      twi_state = TWI_STX;
      // ready the tx buffer index for iteration
      twi_txBufferIndex = 0;
      // set tx buffer length to be zero, to verify if user changes it
      twi_txBufferLength = 0;
      // request for txBuffer to be filled and length to be set
      // note: user must call twi_transmit(bytes, length) to do this
      twi_onSlaveTransmit();
      // if they didn't change buffer & length, initialize it
      if(0 == twi_txBufferLength){
        twi_txBufferLength = 1;
        twi_txBuffer[0] = 0x00;
      }
      // transmit first byte from buffer, fall
    case TW_ST_DATA_ACK: // byte sent, ack returned
      // copy data to output register
      TWDR = twi_txBuffer[twi_txBufferIndex++];
      // if there is more to send, ack, otherwise nack
      if(twi_txBufferIndex < twi_txBufferLength){
        twi_reply(1);
      }else{
        twi_reply(0);
      }
      break;
    case TW_ST_DATA_NACK: // received nack, we are done 
    case TW_ST_LAST_DATA: // received ack, but we are done already!
      // ack future responses
      twi_reply(1);
      // leave slave receiver state
      twi_state = TWI_READY;
      break;

    // All
    case TW_NO_INFO:   // no state information
      break;
    case TW_BUS_ERROR: // bus error, illegal stop/start
      twi_error = TW_BUS_ERROR;
      twi_stop();
      break;
  }
}

And finally the picture of it running with twi interrupt: nerdkit

September 29, 2010
by Rick_S
Rick_S's Avatar

That's funny, if I dropped my TWI speed down to 100KHz, my nunchucks would not respond. At least the OEM ones, I didn't try the non oem at that speed.

I'm glad to see someone else was able to get one going. If I'm not being too bold, what kind of project (if any) do you have in mind for it? I'm half tempted to buy one of the wireless nunchucks to see if I can get it to work. It could make for an interesting wireless control device.

Rick

September 29, 2010
by Ralphxyz
Ralphxyz's Avatar

Oh come on Rick you don't need to "buy" a wireless nunchuck!

Just add a ZigBee module to your existing Nerdkit and have it communicate with another Nerdkit for LCD output/readings or maybe even send it to your PC (you can buy a Xstick for this part:-). Then you can learn Python and graphically display your movements. Like this arduino nunchuck.

Of course you could always get a couple of the ATmel ATAVR128RFA1 MCU's and have one piece ZigBee modules.

That way I could copy what you do and I would not have to work so hard to do this.

Ralph

September 29, 2010
by Rick_S
Rick_S's Avatar

LOL Ralph I haven't looked into the zigbee stuff much. I don't really have a need. For the most part what ive been doing has been just for fun.

Rick

September 29, 2010
by Ralphxyz
Ralphxyz's Avatar

Rick, well you were thinking of "buying" a wireless nunchuck.

I had previously gotten a accelerometer and had asked on the forum about getting started with that. I have been interested in wireless connectivity and ZigBee has looked really attractive to me so you mentioning a wireless nunchuck got me thinking. How much would a wireless nunchuck cost? I just googled wireless nunchuck and see between $12.00 to $24.00 but they all integrate to the wii controller and we want to go to the Nerdkit. I had code that tied the wii controller to a pc using bluetooth, that was cool. I "think" I could build a wireless nunchuck one for $30 - $35.00, as a first try that would be reasonable.

It would be cool to have a actual need, right now everything I am working on is mainly feed by curiosity and my just wanting to learn.

I like the articulated camera mount and could put that together, of course that leads to more questions, like how big should the servos be to handle a actual camera? My camera weighs .5kg (1.125#). I would really want wireless control and would just have to figure out a way for my nerdkit to control my camera just servo pushing the shutter would be enough (for now).

Glad you got the humor, some people get very sensitive.

Ralph

September 29, 2010
by Rick_S
Rick_S's Avatar

Technically, the nunchuck hooks up to the wii controller as well. That's why I thought the wireless version would be neat because I could interface the reciever with the nerdkit communicating the same way I do with the wired version and it would handle all the wireless communications between the nunchuck and itself.

Rick

October 02, 2010
by esoderberg
esoderberg's Avatar

Rick,

Thanks for the code, very helpful. Got it working as advertised in no time using an OEM nunchuck.

Eric S.

October 03, 2010
by Rick_S
Rick_S's Avatar

Glad to hear it worked out for you as well Eric. Do you have something specific in mind you want to do with it?

Rick

October 03, 2010
by Rick_S
Rick_S's Avatar

Ralph,

Like you said earlier... "right now everything I am working on is mainly fed by curiosity and my just wanting to learn."

I have to agree I'm pretty much the same. When I saw this thread it caught my attention. I thought, Hey, I have a few Wii Nunchucks around why not try and see if I can make it work.

That's how almost all my projects have been. Something catches my eye, whether here or some other place online, and I dig into it until I can make it work. Not always the most straightforward way, not always the cleanest code, but often figured out.

Then if it's applicable I try to pass what I learned along the way to the community. I visit here nearly every day... even on vacation sometimes. I've grown to love this community and really hope to see it continue to grow.

Rick

October 04, 2010
by Ralphxyz
Ralphxyz's Avatar

Rick, I can not get your code to compile.

I get a error from the makefile:

    miniMac:nunchuck Me$ make
    makefile:9: *** missing separator.  Stop.
    miniMac:nunchuck Me$

I copied your makefile from this thread.

The first time I ran it the "missing separator" was on line 11 subsequent tries are on line 9.

I am able to compile other programs so I do not "think" it is a board wiring problem.

Any ideas?

Ralph

October 04, 2010
by jbremnant
jbremnant's Avatar

Ralphxyz,

Seems like Makefile error. If you copy and pasted the Makefile content, you might have problems with tabs getting translated to spaces.

Makefile needs tabs in front of the commands. For example, you will need tab in the 4 lines following Nunchuck.hex: Nunchuck.c.

Nunchuck.hex:   Nunchuck.c
    make -C ../libnerdkits
    avr-gcc ${GCCFLAGS} -o twimaster.o -c twimaster.c
    avr-gcc ${GCCFLAGS} ${LINKFLAGS} -o Nunchuck.o Nunchuck.c ${LINKOBJECTS}
    avr-objcopy -j .text -O ihex Nunchuck.o Nunchuck.hex

Hope that helps.

October 04, 2010
by Ralphxyz
Ralphxyz's Avatar

Thanks jbremnant, I made sure there were tabs. now the missing separator is from line 5.

I believe others have run Rick's code without complaint, strange.

Ralph

October 04, 2010
by Ralphxyz
Ralphxyz's Avatar

I modified a working makefile copying Rick's modifications and using a 328p mcu:

# Nunchuck328
GCCFLAGS=-g -Os -Wall -mmcu=atmega328p 
LINKFLAGS=-Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt -lm
AVRDUDEFLAGS= -c avr109 -p m328p -F -b 115200 -P /dev/cu.PL2303-0000101D # USB1
#AVRDUDEFLAGS= -vvv -c avr910 -p m328p -F -b 115200 -P /dev/cu.PL2303-0000205D  # USB2
LINKOBJECTS=../libnerdkits/delay.o ../libnerdkits/lcd.o ../libnerdkits/uart.o twimaster.o

all:    Nunchuck-upload

Nunchuck.hex:   Nunchuck.c
    make -C ../libnerdkits
    avr-gcc ${GCCFLAGS} -o twimaster.o -c twimaster.c
    avr-gcc ${GCCFLAGS} ${LINKFLAGS} -o Nunchuck.o Nunchuck.c ${LINKOBJECTS}
    avr-objcopy -j .text -O ihex Nunchuck.o Nunchuck.hex

Nunchuck.ass:   Nunchuck.hex
    avr-objdump -S -d Nunchuck.o > Nunchuck.ass

tempsensor-upload:  Nunchuck.hex
    avrdude ${AVRDUDEFLAGS} -U flash:w:Nunchuck.hex:a

With this makefile I get this error:

miniMac:nunchuck328 Me$ make
make: *** No rule to make target `Nunchuck-upload', needed by `all'.  Stop.
miniMac:nunchuck328 Me$

Ralph

October 04, 2010
by Ralphxyz
Ralphxyz's Avatar

Duh I had missed renaming tempsensor to Nunchuck on the next to last line.

It looks like it compiled now to see it running.

Ralph

October 04, 2010
by Ralphxyz
Ralphxyz's Avatar

Yahoo!! It works, it actually works. After getting a working makefile it works out of the box!

Here is the makefile for a ATmega328p mcu working with a Mac mini:

# Nunchuck328
GCCFLAGS=-g -Os -Wall -mmcu=atmega328p 
LINKFLAGS=-Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt -lm
AVRDUDEFLAGS= -c avr109 -p m328p -F -b 115200 -P /dev/cu.PL2303-0000101D # USB1
#AVRDUDEFLAGS= -vvv -c avr910 -p m328p -F -b 115200 -P /dev/cu.PL2303-0000205D  # USB2
LINKOBJECTS=../libnerdkits/delay.o ../libnerdkits/lcd.o ../libnerdkits/uart.o twimaster.o

all:    Nunchuck-upload

Nunchuck.hex:   Nunchuck.c
    make -C ../libnerdkits
    avr-gcc ${GCCFLAGS} -o twimaster.o -c twimaster.c
    avr-gcc ${GCCFLAGS} ${LINKFLAGS} -o Nunchuck.o Nunchuck.c ${LINKOBJECTS}
    avr-objcopy -j .text -O ihex Nunchuck.o Nunchuck.hex

Nunchuck.ass:   Nunchuck.hex
    avr-objdump -S -d Nunchuck.o > Nunchuck.ass

Nunchuck-upload:    Nunchuck.hex
    avrdude ${AVRDUDEFLAGS} -U flash:w:Nunchuck.hex:a

In studying this project and the i2c/twi protocol a lot of new projects using i2c come to mind.

First up would be a led cube with the nunchuck controlling the leds.

Of course building a wireless nunchuck would be cool, I would like to use the nunchuck connected to the Nerdkit and then connect to another Nerdkit or a pc using zigbee or any RF method for that matter.

Thanks so much Rick for posting this project. Now I will do the interrupt driven method.


What does

1st start bad

2nd start bad

mean?

I just let the program run with out moving the nunchuck while I was typing this out.

Ralph

October 04, 2010
by Rick_S
Rick_S's Avatar

I put those there as messages in case for some reason the I2C didn't initialize properly either in the 1st initial writing of the address stage, or the setup for reading stage. They were mainly put there for trouble shooting so I could better locate where the failure was.

Rick

October 04, 2010
by Ralphxyz
Ralphxyz's Avatar

jbremnant, I tried your interrupt code but it fails to compile.

miniMac:nunchuck328 Me$ cd ../nunchuck_async
miniMac:nunchuck_async Me$ make
avr-gcc -g -Os -Wall -mmcu=atmega168 -DCPU_FREQ=14745600 -DTWI_FREQ=100000  -DF_CPU=14745600 -Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt -lm -c -o utils.o utils.c
avr-gcc -g -Os -Wall -mmcu=atmega168 -DCPU_FREQ=14745600 -DTWI_FREQ=100000  -DF_CPU=14745600 -Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt -lm -c -o twiasync.o twiasync.c
twiasync.c: In function '__vector_24':
twiasync.c:429: warning: passing argument 1 of 'twi_onSlaveReceive' discards qualifiers from pointer target type
avr-gcc -g -Os -Wall -mmcu=atmega168 -DCPU_FREQ=14745600 -DTWI_FREQ=100000  -DF_CPU=14745600 -Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt -lm -o wiink.o wiink.c utils.o twiasync.o 
avr-objcopy -j .text -j .data -O ihex wiink.o wiink.hex
miniMac:nunchuck_async Me$

That's all I get.

Ralph

October 06, 2010
by jbremnant
jbremnant's Avatar

Hi Ralph,

Glad to know you got the original Makefile running. My Makefile, however, is different from Rick's. Seeing the output, looks like the code compiled correctly. What you see is just a warning. I think there's some implicit casting going on somewhere, but the code did compile. You can verify it by looking for wiink.hex in the same dir.

Now you just have to upload it to the chip by running:

make upload

October 07, 2010
by Ralphxyz
Ralphxyz's Avatar

jbremnant, thanks your makefile is really different than Ricks but there is a similar flow to it so I think my modifications are complete, here is the modified makefile for a Mac compile:

TARGET=wiink
PORT=/dev/cu.PL2303-0000101D

MCU = atmega328p
CPU_FREQ = 14745600
# CPU_FREQ = 16000000
# wiimote does 400kHz, but nunchuck as a slave should be ok with 100kHz
TWI_FREQ = 100000 
UPLOAD_RATE = 115200  # 57600
AVRDUDE_PROGRAMMER = stk500

GCCFLAGS=-g -Os -Wall -mmcu=$(MCU) -DCPU_FREQ=$(CPU_FREQ) -DTWI_FREQ=$(TWI_FREQ) -DF_CPU=$(CPU_FREQ)
LINKFLAGS=-Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt -lm

AVRDUDE = avrdude
AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex
AVRDUDE_FLAGS=-V -F -c avr109 -p m328p -P $(PORT) -b $(UPLOAD_RATE) $(AVRDUDE_WRITE_FLASH)
AVRDUDE_FLAGS_ISP=-V -F -P usb -c avrispmkII -p m328p $(AVRDUDE_WRITE_FLASH)
# -c $(AVRDUDE_PROGRAMMER) -b $(UPLOAD_RATE)

all:    $(TARGET).hex
upload: $(TARGET)-upload 
uploadisp: $(TARGET)-uploadisp

utils.o: utils.c
    avr-gcc ${GCCFLAGS} ${LINKFLAGS} -c -o utils.o utils.c

twiasync.o: twiasync.c utils.o
    avr-gcc ${GCCFLAGS} ${LINKFLAGS} -c -o twiasync.o twiasync.c

$(TARGET).hex: $(TARGET).c twiasync.o utils.o
    avr-gcc ${GCCFLAGS} ${LINKFLAGS} -o $(TARGET).o $(TARGET).c utils.o twiasync.o 
    avr-objcopy -j .text -j .data -O ihex $(TARGET).o $(TARGET).hex

$(TARGET).ass:  $(TARGET).hex
    avr-objdump -S -d $(TARGET).o > $(TARGET).ass

$(TARGET)-upload:   $(TARGET).hex
    ./pulsedtr.py $(PORT)
    $(AVRDUDE) $(AVRDUDE_FLAGS)

$(TARGET)-uploadisp:    $(TARGET).hex
    $(AVRDUDE) $(AVRDUDE_FLAGS_ISP)

One of these days I am going to start a thread about makefile so that I can learn what is going on and the options.

Now I have to get python running on my Mac before I can run your code.

Thanks again,

Ralph

October 07, 2010
by jbremnant
jbremnant's Avatar

Ralph,

Sorry about that python piece. I had put it there because arduino bootloader required it. Yeah, I was using the same makefile to program an arduino. But since we are working with Nerdkit, you can simply ignore the pulsedtr.py and delete out that line.

October 07, 2010
by Ralphxyz
Ralphxyz's Avatar

WhooooWeeee, fantastic I have your interrupt driven code working also.

Hotdog, here is the modified makefile if one is using a Mac:

TARGET=wiink
PORT=/dev/cu.PL2303-0000101D

MCU = atmega328p
CPU_FREQ = 14745600
# CPU_FREQ = 16000000
# wiimote does 400kHz, but nunchuck as a slave should be ok with 100kHz
TWI_FREQ = 100000 
UPLOAD_RATE = 115200  # 57600
AVRDUDE_PROGRAMMER = stk500

GCCFLAGS=-g -Os -Wall -mmcu=$(MCU) -DCPU_FREQ=$(CPU_FREQ) -DTWI_FREQ=$(TWI_FREQ) -DF_CPU=$(CPU_FREQ)
LINKFLAGS=-Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt -lm

AVRDUDE = avrdude
AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex
AVRDUDE_FLAGS=-V -F -c avr109 -p m328p -P $(PORT) -b $(UPLOAD_RATE) $(AVRDUDE_WRITE_FLASH)
AVRDUDE_FLAGS_ISP=-V -F -P usb -c avrispmkII -p m328p $(AVRDUDE_WRITE_FLASH)
# -c $(AVRDUDE_PROGRAMMER) -b $(UPLOAD_RATE)

all:    $(TARGET).hex
upload: $(TARGET)-upload 
uploadisp: $(TARGET)-uploadisp

utils.o: utils.c
    avr-gcc ${GCCFLAGS} ${LINKFLAGS} -c -o utils.o utils.c

twiasync.o: twiasync.c utils.o
    avr-gcc ${GCCFLAGS} ${LINKFLAGS} -c -o twiasync.o twiasync.c

$(TARGET).hex: $(TARGET).c twiasync.o utils.o
    avr-gcc ${GCCFLAGS} ${LINKFLAGS} -o $(TARGET).o $(TARGET).c utils.o twiasync.o 
    avr-objcopy -j .text -j .data -O ihex $(TARGET).o $(TARGET).hex

$(TARGET).ass:  $(TARGET).hex
    avr-objdump -S -d $(TARGET).o > $(TARGET).ass

$(TARGET)-upload:   $(TARGET).hex
    #./pulsedtr.py $(PORT)
    $(AVRDUDE) $(AVRDUDE_FLAGS)

$(TARGET)-uploadisp:    $(TARGET).hex
    $(AVRDUDE) $(AVRDUDE_FLAGS_ISP)

This is so interesting now I can compare Ricks code to yours in order to get some comprehension of exactly what is going on.

I have some more i2c/twi projects in mind plus a neat (at least I think so) project using the Nunchuck and a LED cube, I would like to get a visual representation of the Nunchuck's movement in 3D. Of course I'd like to strap on a gyro and maybe a compass to see how all that might work also.

This is so great, just yesterday as part of a thread on AVRfreaks I asked how one would set the crystal speed using the makefile instead of hardcoding it in your code (having a generic speed set routine) and now I see:

CPU_FREQ = 14745600

in your make file, so there ya go.

Thanks again to you and Rick.

Ralph

October 07, 2010
by bretm
bretm's Avatar

It's important to note that

CPU_FREQ = 14745600

in a makefile does nothing by itself. It's actually the -D flag on the avr-gcc command line that does the trick. The makefile just uses CPU_FREQ as an arbitrary variable name to make it easier to find and edit the value. It inserts it into GCCFLAGS as -DF_CPU, and then GCCFLAGS gets appended to the avr-gcc command-line options. The avr-gcc compiler sees the -DF_CPU command and assigns the value to the F_CPU pre-processor symbol.

October 07, 2010
by Ralphxyz
Ralphxyz's Avatar

Ah once again bretm, Thank You!! With my very limited knowledge about makefile that makes sense.

Ralph

October 07, 2010
by Rick_S
Rick_S's Avatar

jbremnant, I'm not sure about the build of avrdude for mac, but the build I'm using for PC has an arduino programmer option. If this is used for stk500 it does the dtr toggle before and after automatically. I've been playing around with this a bit on a couple of arduino boards I have.

Rick

October 08, 2010
by jbremnant
jbremnant's Avatar

@Ralphxyz,bretm

That's right. The defines are declared directly using -D flag via avr-gcc. And the source will check the existence of the define var by doing:

#ifndef F_CPU
#define F_CPU 14745600
#endif

It should come in handy when you want to override some defines and control the source behavior from your Makefile. You can create a whole system of machine dependent code compilation using this technique.

@Rick_S

I use linux almost exclusively for avr dev, and I haven't noticed the arduino programmer option. I might have been using older version of avrdude... Thanks for the note. :-)

October 12, 2010
by esoderberg
esoderberg's Avatar

Rick et al.,

Made a few minor adjustments to the code you posted, the most useful of which is the init portion.
With this init, the data is unencrypted, so the decryption loop is no longer needed.
It is posted below in case anyone wants the modification. It works even when wired through the motion plus module with nunchuck attached, although it seems to hang on the init occasionally.

Below that I've posted code that still needs some work. It is supposed to read data from the wii motion plus module (3 axis gyro) interleaved with nunchuck data. My understanding of the TWI is still a little shaky and I've tried to follow the gouge from several sites:

http://wiibrew.org/wiki/Wiimote/Extension_Controllers#Wii_Motion_Plus

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1248889032/all
user name krulkip 1/4 way down first page has arduino wire code.

but either the init hangs up or I get constant data in all of the fields. Any guidance would be appreciated.

Eric

Program for Nunchuck read only:

#define F_CPU 16000000
#include <inttypes.h>

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "i2cmaster.h"

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

#define Nunchk_ADR 0xA4

int main(void)
{
    uint8_t nc_data[6], btnc, btnz, temp, i;
    int16_t accx, accy, accz, jx, jy;

    // initialize data array

    for(i=0;i<6;i++)
        {
            nc_data[i]=0;
        }
    // init I2C interface
    i2c_init();

      // start up the serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

        printf_P(PSTR("\r\nSerial Comm Test "));

    // Setup the Nunchuk for first time providing address to read

//The old way to initialize the extension was by writing the single encryption byte 0x00 to 0x(4)A40040, 
//but that only works on Nintendo's own brand extensions 
//With this method you must decrypt the extension bytes to read them.

//The new way to initialize the extension is by writing 0x55 to 0x(4)A400F0, then writing 0x00 to 0x(4)A400FB. It  
//makes the extension type bytes unencrypted. This means that you no longer have to decrypt the extension bytes.
//**Above comment direct from wiibrew - code seems to work just the same with or without
// write of 0x00 to 0xFB

    if(~(i2c_start(0xA4))) printf_P(PSTR("\r\nwrite 0xA4 OK")); else printf_P(PSTR("\r\ncrap, bad start"));

    printf_P(PSTR("\r\n Initialising Wii Nunchuck ......."));

    if(~(i2c_write(0xF0))) printf_P(PSTR("\r\nwrite 0xF0 OK")); else printf_P(PSTR("\r\ncrap"));
    if(~(i2c_write(0x55))) printf_P(PSTR("\r\nwrite 0x55 OK")); else printf_P(PSTR("\r\ncrap"));

if(~(i2c_write(0xFB))) printf_P(PSTR("\r\nwrite 0xFB OK")); else printf_P(PSTR("\r\ncrap"));
if(~(i2c_write(0x00))) printf_P(PSTR("\r\nwrite 0x00 OK")); else printf_P(PSTR("\r\ncrap"));

    i2c_stop();

    while(1){

            // Start Nunchuck read by writing initiating a start then writing 0x00

                i2c_start(0xA4);

                i2c_write(0x00); // Send Address to read from

            i2c_stop();
            delay_us(500);                      // Wait a bit before sending read request

            // begin read request

            i2c_start_wait(0xA4+1);

            // read 6 bytes

            for(i = 0; i < 5; i++)
            {
                nc_data[i]=i2c_readAck();
            }

            nc_data[5]= i2c_readNak();
            i2c_stop();

            //Display results

            lcd_line_one();
            btnz=0;
            btnc=0;
            if(nc_data[5] & 1) btnz=1;
            if(nc_data[5]&(1<<1))btnc=1;

            jx = nc_data[0] -133;

            jy = nc_data[1]-129;

            // Setup the 10 bit data for accelerometer readings.
            // First shift the MSB's 2 places

            accx = (nc_data[2]<<2);
            accy = (nc_data[3]<<2);
            accz = (nc_data[4]<<2);

            // Then add the least significant two bits
            // for X
           temp = nc_data[5];
           temp = ((temp&0b00001100)>>2);
           accx += (temp - 481);
           accx = accx/2;
           // for y
           temp = nc_data[5];
           temp = ((temp&0b00110000)>>4);
           accy += (temp - 506);
            accy = accy/2;
           // and for z
            temp = nc_data[5];
            temp = ((temp&0b11000000)>>6);
            accz += (temp - 505);
            accz = accz/2;

            // send the data to the LCD
            //lcd_home();
            //fprintf_P(&lcd_stream, PSTR("JoyX: %3u  JoyY: %3u"),nc_data[0],nc_data[1]);
            //lcd_line_two();
            //fprintf_P(&lcd_stream, PSTR("Acc X:%4d  Y:%4d"),accx,accy);
            //lcd_line_three();
            //fprintf_P(&lcd_stream, PSTR("Acc Z:%4d"),accz);
            //lcd_line_four();
            //fprintf_P(&lcd_stream, PSTR("Button Z:%2u  C:%2u"),btnz,btnc);

   // Print the current Joyx position to the serial port.
    printf_P(PSTR("\r\nJoyX: %4d  JoyY: %4d"), jx, jy);

    printf_P(PSTR("  Acc X:%5.4d  Y:%5.4d  Z:%5.4d 1/100 g"),accx,accy,accz);

    printf_P(PSTR("  Button Z:%2u  C:%2u"),btnz,btnc);

   }

    for(;;);

    return 0;

}

wiichuckplus program:

#define F_CPU 16000000
#include <inttypes.h>

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "i2cmaster.h"

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

#define Nunchk_ADR 0xA4

//nn

int main(void)
{
    uint8_t nc_data[6], btnc, btnz, temp, i;
    int16_t accx, accy, accz, yaw, roll, pitch, yawinit, rollinit, pitchinit;
    int16_t jx, jy;

    // initialize data array

    for(i=0;i<6;i++)
        {
            nc_data[i]=0;
        }

        pitch = 0;
        roll = 0;
        yaw = 0;
        accx=0;
        accy=0;
        accz=0;

    i2c_init();                                // init I2C interface

      // start up the serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

        printf_P(PSTR("\r\nSerial Comm Test "));

    // Setup the Nunchuk for first time providing address to read

    if(~(i2c_start(0xA6))) printf_P(PSTR("\r\nStart OK ")); else printf_P(PSTR("\r\ncrap"));

        printf_P(PSTR("\r\nPutting Wii in Motion plus/nunchuck pass through mode = 05 ........"));
    delay_ms(500);
    if(~(i2c_write(0xFE))) printf_P(PSTR("\r\nwrite 0xFE OK ")); else printf_P(PSTR("\r\ncrap"));
    if(~(i2c_write(0x05))) printf_P(PSTR("\r\nwrite 0x05 OK ")); else printf_P(PSTR("\r\ncrap"));// 05 for pass through

delay_ms(500);

    printf_P(PSTR("\r\n Initialising Wii Motion plus ......."));
    if(~(i2c_write(0xF0))) printf_P(PSTR("\r\nwrite 0xF0 OK")); else printf_P(PSTR("\r\ncrap"));
    if(~(i2c_write(0x55))) printf_P(PSTR("\r\nwrite 0x55 OK")); else printf_P(PSTR("\r\ncrap"));

    i2c_stop();

        delay_ms(500);

     // Setup the Nunchuk for first time providing address to read
    printf_P(PSTR("\r\n Set reading address ......."));

    if(~(i2c_start(0xA4))) printf_P(PSTR("\r\n Start at 0xA4 address OK ")); else printf_P(PSTR("\r\ncrap"));

    if(~(i2c_write(0x08))) printf_P(PSTR("\r\nwrite 0x08 OK ")); else printf_P(PSTR("\r\ncrap"));

    i2c_stop();

    // pending init routine, use these values for zero rotation rate data points
     yawinit = 8063;
     rollinit = 8063;
     pitchinit = 8063;

    while(1){

        delay_us(5000);
        printf_P(PSTR("\rtest"));

            // Start Nunchuck read by writing initiating a start then writing 0x00

            i2c_start(Nunchk_ADR);

            i2c_write(0x08);                    // Send Address to read from 
            i2c_stop();
            delay_us(500);                      // Wait a bit before sending read request

            // begin read request

            i2c_start_wait(Nunchk_ADR+I2C_READ);

            // read 6 bytes

            for(i = 0; i < 5; i++)
            {
                nc_data[i]=i2c_readAck();
            }

            nc_data[5]= i2c_readNak();
            i2c_stop();

            //Display results

if (~(nc_data[5]&0b10)) { // if bit one of byte 5 = 0 then getting wiichuch pass through data
printf_P(PSTR("joystick/accell data"));
            lcd_line_one();
            btnz=0;
            btnc=0;
            if(nc_data[5] & (1<<2)) btnz=1;
            if(nc_data[5]&(1<<3))btnc=1;

            jx = nc_data[0] -133;

            jy = nc_data[1]-129;

            // Setup the 10 bit data for accelerometer readings.
            // First shift the MSB's 2 places

            accx = (nc_data[2]<<2);
            accy = (nc_data[3]<<2);
            accz = (nc_data[4]<<2);

            // Then add the least significant bits
            // for X
           temp = nc_data[5];
           temp = ((temp&0b00010000)>>3);

           accx += (temp - 478);
           accx = accx/2;
           // for y
           temp = nc_data[5];
           temp = ((temp&0b00100000)>>4);

           accy += (temp - 506);
           accy = accy/2;
           // and for z
            temp = nc_data[5];
            temp = ((temp&0b11000000)>>5);

            accz = (accz&0b1111111110);
            accz += (temp - 505);
            accz = accz/2;

            // end of pass through wiichuck data read
            }

            // send the data to the LCD
            //lcd_home();
            //fprintf_P(&lcd_stream, PSTR("JoyX: %3u  JoyY: %3u"),nc_data[0],nc_data[1]);
            //lcd_line_two();
            //fprintf_P(&lcd_stream, PSTR("Acc X:%4d  Y:%4d"),accx,accy);
            //lcd_line_three();
            //fprintf_P(&lcd_stream, PSTR("Acc Z:%4d"),accz);
            //lcd_line_four();
            //fprintf_P(&lcd_stream, PSTR("Button Z:%2u  C:%2u"),btnz,btnc);

   // Print the current Joyx position to the serial port.
    printf_P(PSTR("\r\nJoyX: %3d  JoyY: %3d"), jx, jy);

    printf_P(PSTR(" Acc X:%4d  Y:%4d  Z:%4d"),accx,accy,accz);

    printf_P(PSTR(" Button Z:%2u  C:%2u"),btnz,btnc);

    printf_P(PSTR(" Pitch:%4d  Roll:%4d  Yaw:%4d"), pitch, roll, yaw);

    if (nc_data[5]&0b10) { // if bit one of byte 5 = 1 then getting motion plus data

        printf_P(PSTR("rotation data"));
        yaw     = (((nc_data[3]<<6)&0b11111100) + nc_data[0]);
        roll    = (((nc_data[4]<<6)&0b11111100) + nc_data[1]);
        pitch   = (((nc_data[5]<<6)&0b11111100) + nc_data[2]);

        yaw = ((yaw-yawinit)/13.768);
            if (~(nc_data[3]&(1<<1))) yaw = yaw*(50/11);

        roll =  ((roll-rollinit)/13.768);
            if (~(nc_data[4]&(1<<1))) roll = roll*(50/11);

        pitch = ((pitch-pitchinit)/13.768);
            if (~(nc_data[3]&1)) pitch = pitch*(50/11);

//end of motion plus data read  
}

   }

    for(;;);

    return 0;

}
October 13, 2010
by jbremnant
jbremnant's Avatar

Nice, thanks for the post esoderberg! It's good to see folks contributing and spreading the knowledge on a particular topic. I checked out the arduino forum. Ironically, I found the use of Kalman filter more interesting than getting the raw data out of motion plus sensor. :-)

Perhaps the nerdkits staff can expand/elaborate on the use of Kalman filter in smoothing out the noise from sensor readings - maybe a new tutorial?

October 13, 2010
by Ralphxyz
Ralphxyz's Avatar

esoderberg, how are you connecting the wii_motion_plus to the nerdkit?

Ralph

October 13, 2010
by Rick_S
Rick_S's Avatar

The motion plus would connect the same as the nunchuck. I don't have one of those to play with so I couldn't experiment with code for it. I'm glad to see someone has though.

Rick

October 13, 2010
by Ralphxyz
Ralphxyz's Avatar

Oh that's right, I have a couple of the motion plus but have not played with them for a year or so, duh.

In thinking about connecting the nunchuck to a led cube to illustrate 3D motion I thought it would be cool to have a gyro so that I could also show direction. So apparently that part is done now I just need to build the cube and then do the code. I am going to do the led array first, I'll be able to get 2D from that.

Ralph

October 15, 2010
by Ralphxyz
Ralphxyz's Avatar

esoderberg, I installed your nunchukplus code but after programing all I get is the two black bars when switching back to run mode.

I am using a ATmega328 with the modified make file listed above.

I am able to load other programs.

Ralph

October 15, 2010
by esoderberg
esoderberg's Avatar

Ralph,

I still haven't been able to get the pass through mode to work either. What is complicating my trouble shooting is that the program hangs up during the init portion, but not in a reliably repeatable fashion. Even the nunchuck only code, which is basically working, seems to hang up occasionally (and not always at the same point in the code) during the init portion as well, although after a few resets it usually works fine, and once the init is complete and the rest of the program is running, all of the data comes without interrupt and is valid. One top of that, while I fully understand the protocol for pulling the data off the 6 bytes once it is loaded onto nc_data, I'm still not so clear on the TWI setup and wiimotion plus init. I'll post more code if I manage to get it working.

Eric

October 16, 2010
by esoderberg
esoderberg's Avatar

Finally got the pass through mode to work, but it is still a little fickle, ie I have to reset the chip usually a half dozen times or so for the init to work, but when it does work, all the data is good. I'm using a wiichuck to plug into the motion plus module so the connections to the chip seem like they should be pretty solid, but un-plugging and re-plugging the module in seems to change the init results so there must be an issue there somewhere. Any how, the code below does work. Any suggestions on making it more robust welcome:

#define F_CPU 16000000
#include <inttypes.h>

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "i2cmaster.h"

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

#define Nunchk_ADR 0xA4

int main(void)
{
    uint8_t nc_data[6], btnc, btnz, temp, i, init;
    int16_t accx, accy, accz, yaw, roll, pitch, yawinit, rollinit, pitchinit;
    int16_t jx, jy;

     // init I2C interface
 i2c_init();

    // initialize data array
    for(i=0;i<6;i++)
        {
            nc_data[i]=0;
        }
        temp = 0;
        init=0;
        pitch = 0;
        roll = 0;
        yaw = 0;
        accx=0;
        accy=0;
        accz=0;
        jx = 0;
        jy = 0;
        btnc=0;
        btnz=0;

        DDRC = 0;

        DDRC |= (1<<PC2) | (1<<PC3);// set up PC2 and 3 as output to power nunchuck

        PORTC |= (1<<PC3) | (1<<PC4) | (1<<PC5);// turn on PC3 for + and set pull up for PC4,5 for clk and data input

        PORTC &= ~(1<<PC2);// turn off PC2 for ground

    delay_ms(100);

      // start up the serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

        printf_P(PSTR("\r\nSerial Comm Test "));

            i2c_start_wait(0xA6);

        printf_P(PSTR("\r\nPutting Wii in Motion plus/nunchuck pass through mode = 05 ........"));

        if(i2c_write(0xFE)) printf_P(PSTR("\r\ncrap 0xFE")); else printf_P(PSTR("\r\n0xFE OK"));
    delay_ms(100);
        if(i2c_write(0x05)) printf_P(PSTR("\r\ncrap 0x05")); else printf_P(PSTR("\r\n0x05 OK"));
    i2c_stop();

    i2c_start_wait(0xA6);

    printf_P(PSTR("\r\n Initialising Wii Motion plus ......."));

        if(i2c_write(0xF0)) printf_P(PSTR("\r\ncrap 0xF0")); else printf_P(PSTR("\r\nwrite 0xF0 OK"));
    delay_ms(100);  
        if(i2c_write(0x55)) printf_P(PSTR("\r\ncrap 0x55")); else printf_P(PSTR("\r\nwrite 0x55 OK"));

    i2c_stop();

            //routine to see if reading nunchuck and motion plus

            i2c_start_wait(0xA4);

            if(i2c_write(0xFA)) printf_P(PSTR("\r\nwrite 0xFA Crap")); else printf_P(PSTR("\r\nwrite 0xFA OK"));

            i2c_stop();

                delay_ms(100);

                   i2c_start_wait(Nunchk_ADR+I2C_READ);

                // read 6 bytes
                    delay_us(500);

                    for(i = 0; i < 5; i++)
                    {
                        nc_data[i]=i2c_readAck();   }

                    nc_data[5]= i2c_readNak();

                    i2c_stop();

                    for(i = 0; i < 6; i++)
                    {
                    temp += nc_data[i];  }
                    printf_P(PSTR("\r\nextension controller xID = 0x %4x"), temp);

                    delay_ms(5000);

                    if (temp==0xCB) printf_P(PSTR("\r\nmotion plus connected but not activated"));

                    if (temp==0xCE) printf_P(PSTR("\r\nmotion plus connected and activated"));

                    if (temp==0x00) printf_P(PSTR("\r\nmotion plus not connected"));

                    // end of configuration check
                    delay_ms(5000);

     // Setup the Nunchuk for actual data address to read

    printf_P(PSTR("\r\n Set data reading address ......."));

    i2c_start_wait(0xA4);

    if(i2c_write(0x08)) printf_P(PSTR("\r\ncrap 0x08")); else printf_P(PSTR("\r\n0x08 OK"));

    i2c_stop();

    // pending init routine, use these values for zero rotation rate data points
     yawinit = 0;
     rollinit = 0;
     pitchinit = 0;

    while(1){

        delay_ms(1);

            // Start Nunchuck read by writing initiating a start then writing 0x00

            i2c_start(Nunchk_ADR);

                      // Send zero to request data
            i2c_write(0x00);
            i2c_stop();
            delay_us(500);                      // Wait a bit before sending read request

            // begin read request

            i2c_start_wait(Nunchk_ADR+I2C_READ);

            // read 6 bytes

            for(i = 0; i < 5; i++)
            {
                nc_data[i]=i2c_readAck();
            }

            nc_data[5]= i2c_readNak();
            i2c_stop();

            //Display results

if ((nc_data[5]&0x03)==0x00) { // if bit one of byte 5 = 0 then getting wiichuck pass through data

            btnz=((nc_data[5] >> 2) & 1);
            btnc=((nc_data[5] >> 3) & 1);

            jx = nc_data[0] -133;

            jy = nc_data[1]-129;

            // Setup the 10 bit data for accelerometer readings.
            // First shift the MSB's 2 places, then add the least significant bits

            accx = (nc_data[2]<<2) + ((nc_data[5] >> 3) & 2);
            accy = (nc_data[3]<<2) + ((nc_data[5] >> 4) & 2);
            accz = (nc_data[4]<<2) + ((nc_data[5] >> 5) & 6);

            accx = (accx-478)/2;

            accy = (accy-506)/2;

            accz = (accz - 505)/2;

            // end of pass through wiichuck data read

            // Print the current data to the serial port.

    printf_P(PSTR("\r\n JoyX: %3d  JoyY: %3d"), jx, jy);

    printf_P(PSTR(" Acc X:%4d  Y:%4d  Z:%4d"),accx,accy,accz);

    printf_P(PSTR(" Button Z:%2u  C:%2u"),btnz,btnc);
            }

            // send the data to the LCD
            //lcd_home();
            //fprintf_P(&lcd_stream, PSTR("JoyX: %3u  JoyY: %3u"),nc_data[0],nc_data[1]);
            //lcd_line_two();
            //fprintf_P(&lcd_stream, PSTR("Acc X:%4d  Y:%4d"),accx,accy);
            //lcd_line_three();
            //fprintf_P(&lcd_stream, PSTR("Acc Z:%4d"),accz);
            //lcd_line_four();
            //fprintf_P(&lcd_stream, PSTR("Button Z:%2u  C:%2u"),btnz,btnc);

    if ((nc_data[5]&3)==2) { // if bit one of byte 5 = 1 then getting motion plus data

        yaw     = (((nc_data[3]<<6)&0xFC) + nc_data[0]);
        roll    = (((nc_data[4]<<6)&0xFC) + nc_data[1]);
        pitch   = (((nc_data[5]<<6)&0xFC) + nc_data[2]);

        if (init<10) {  yawinit += yaw/10;
                        rollinit += roll/10;
                        pitchinit += pitch/10; }

                        if (init<10) init += 1;

        yaw = ((yaw-yawinit)/13.768);
            if (~(nc_data[3]&2)) yaw = yaw*(50/11);

        roll =  ((roll-rollinit)/13.768);
            if (~(nc_data[4]&2)) roll = roll*(50/11);

        pitch = ((pitch-pitchinit)/13.768);
            if (~(nc_data[3]&1)) pitch = pitch*(50/11);

//end of motion plus data read

    printf_P(PSTR(" Pitch:%4d  Roll:%4d  Yaw:%4d"), pitch, roll, yaw);  
}

   }

    for(;;);

    return 0;

}
October 16, 2010
by Ralphxyz
Ralphxyz's Avatar

esoderberg, in looking at a thread over on AVRfreaks I see mentioned in passing that twi_init() returns a error code. I have no idea how to read the error code or what the error codes are but thought I might mention the fact. The AVRfreaks thread has noting to do with twi, the twi_init() reference just was mentioned by a very knowledgeable regular on the AVRfreaks forum.

Sorry I am just learning all of this so I cannot help with the error codes I just know you have been having problems with init.

Ralph

October 16, 2010
by esoderberg
esoderberg's Avatar

I think I've finally run out of steam on this thread, but will post the latest and last version of my code for reading the wiichuck and motion plus. Its a little more robust than last posted version, as it simply repeats the init portion until the wiichuck is set up correctly, plus I started to play around with integrating and differentiating the gyro outputs to give rotational displacement and accel as well as rotation rate.

#define F_CPU 16000000
#include <inttypes.h>

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "i2cmaster.h"

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

#define Nunchk_ADR 0xA4

volatile uint32_t time=0;
//volatile uint32_t timecounter=0;
volatile uint32_t t1=0;
volatile uint32_t t2=0;
int32_t dt=0;

// Clock set up

void realtimeclock_setup() {
  // setup Timer0:
  // CTC (Clear Timer on Compare Match mode)
  // TOP set by OCR0A register
  TCCR0A |= (1<<WGM01);
  // clocked from CLK/
  // which is 16000000/64, or 250000 increments per second
  TCCR0B = 0;
  TCCR0B |= (1<<CS01) | (1<<CS00);
  TCCR0B &= ~(1<<CS02);
  // set TOP to 4
  // because it counts 0, 1, 2,..23,24, 0, 1, 2 ...
  // so 0 through 24 equals 25 events
  OCR0A = 24;
  // enable interrupt on compare event
  // (250000 / 25 = 10000 per second)
  TIMSK0 |= (1<<OCIE0A);
}

// the_time will store the elapsed time
// (10000 = 1 second)
// 
// note that this will overflow eventually
//
// This variable is marked "volatile" because it is modified
// by an interrupt handler.  Without the "volatile" marking,
// the compiler might just assume that it doesn't change in 
// the flow of any given function (if the compiler doesn't
// see any code in that function modifying it -- sounds 
// reasonable, normally!).
//
// But with "volatile", it will always read it from memory 
// instead of making that assumption.

ISR(TIMER0_COMPA_vect){ // when Timer0 gets to its Output Compare value,
  // elapsed time (0.0001 seconds per count).
  time++;

  if (time>32000) {time=0;
                    t2=0;}
 }

int main(void)
{
    uint8_t nc_data[6], btnc, btnz, temp, i, init, initj, wii_init=1;
    int16_t accx, accy, accz;
    int32_t yaw[5], roll[5], pitch[5], pitchaccel, yawd, rolld, pitchd, yawinit, rollinit, pitchinit;
    int16_t jx, jy, jxinit, jyinit;

     // init I2C interface
 i2c_init();

      // start up the serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

realtimeclock_setup();

    printf_P(PSTR("\r\nSerial Comm Test1 "));

    // initialize data array
    for(i=0;i<6;i++)
        {
            nc_data[i]=0;
        }
        temp = 0;
        init=0;
        initj=0;
         for(i=0;i<5;i++)
        {
            yaw[i]=0;
            roll[i]=0;
            pitch[i]=0;
        }
        accx=0;
        accy=0;
        accz=0;
        jx = 0;
        jy = 0;
        jxinit=0;
        jyinit=0;
        btnc=0;
        btnz=0;
        yawd=0;
        rolld=0;
        pitchd=0;

        DDRC = 0;

        DDRC |= (1<<PC2) | (1<<PC3);// set up PC2 and 3 as output to power nunchuck

        PORTC |= (1<<PC3) | (1<<PC4) | (1<<PC5);// turn on PC3 for + and set pull up for PC4,5 for clk and data input

        PORTC &= ~(1<<PC2);// turn off PC2 for ground

        printf_P(PSTR("\r\nSerial Comm Test2 "));

    while (wii_init) {  
    // Setup the Nunchuk.  Keeps trying until nunchuck and motion plus indicates connected and activated

        i2c_start_wait(0xA6);

        printf_P(PSTR("\r\nPutting Wii in Motion plus/nunchuck pass through mode = 05 ........"));

        if(i2c_write(0xFE)) printf_P(PSTR("\r\ncrap 0xFE")); else printf_P(PSTR("\r\n0xFE OK"));

        if(i2c_write(0x05)) printf_P(PSTR("\r\ncrap 0x05")); else printf_P(PSTR("\r\n0x05 OK"));

    i2c_stop();

    i2c_start_wait(0xA6);

    printf_P(PSTR("\r\n Initialising Wii Motion plus ......."));

        if(i2c_write(0xF0)) printf_P(PSTR("\r\ncrap 0xF0")); else printf_P(PSTR("\r\nwrite 0xF0 OK"));
    delay_ms(100);  
        if(i2c_write(0x55)) printf_P(PSTR("\r\ncrap 0x55")); else printf_P(PSTR("\r\nwrite 0x55 OK"));

    i2c_stop();

            //routine to see if reading nunchuck and motion plus

            i2c_start_wait(0xA4);

            if(i2c_write(0xFA)) printf_P(PSTR("\r\nwrite 0xFA Crap")); else printf_P(PSTR("\r\nwrite 0xFA OK"));

            i2c_stop();

                delay_ms(200);

                   i2c_start_wait(Nunchk_ADR+I2C_READ);

                // read 6 bytes
                    delay_us(500);

                    for(i = 0; i < 5; i++)
                    {
                        nc_data[i]=i2c_readAck();   }

                    nc_data[5]= i2c_readNak();

                    i2c_stop();

                    temp = 0;

                    for(i = 0; i < 6; i++)
                    {
                    temp += nc_data[i];  }
                    printf_P(PSTR("\r\nextension controller xID = 0x %4x"), temp);

                    if (temp==0xCB) printf_P(PSTR("\r\nmotion plus connected but not activated"));

                    if (temp==0xCE) printf_P(PSTR("\r\nYES!!!!!!!!! motion plus connected and activated"));

                    if (temp==0x00) printf_P(PSTR("\r\nmotion plus not connected"));

                    if (temp==0xCE) wii_init = 0; else wii_init = 1;

                    // end of configuration check

        delay_ms(100);
     // Setup the Nunchuk for address to read output data

    printf_P(PSTR("\r\n Set data reading address ......."));

    i2c_start_wait(0xA4);

    if(i2c_write(0x08)) printf_P(PSTR("\r\ncrap 0x08")); else printf_P(PSTR("\r\n0x08 OK"));

    i2c_stop();}

    // pending init routine, use these values for zero rotation rate data points
     yawinit = 0;
     rollinit = 0;
     pitchinit = 0;

 // turn on interrupt handler
  sei();

    while(1){

            // Start Nunchuck read by writing initiating a start then writing 0x00

            i2c_start(Nunchk_ADR);

                      // Send zero to request data
            i2c_write(0x00);
            i2c_stop();
            delay_us(500);                      // Wait a bit before sending read request

            // begin read request

            i2c_start_wait(Nunchk_ADR+I2C_READ);

            // read 6 bytes

            for(i = 0; i < 5; i++)
            {
                nc_data[i]=i2c_readAck();
            }

            nc_data[5]= i2c_readNak();
            i2c_stop();

            //Display results

if ((nc_data[5]&0x03)==0x00) { // if bit one of byte 5 = 0 then getting wiichuck pass through data

//init center position of joystick
            if (initj<13) { if (initj>2) {  jxinit += nc_data[0];
                                jyinit += nc_data[1]; }
                                initj += 1;
                         }

            btnz=((nc_data[5] >> 2) & 1);
            btnc=((nc_data[5] >> 3) & 1);

            jx = nc_data[0] - (jxinit/10);

            jy = nc_data[1]- (jyinit/10);

            // Setup the 10 bit data for accelerometer readings.
            // First shift the MSB's 2 places, then add the least significant bits

            accx = (nc_data[2]<<2) + ((nc_data[5] >> 3) & 2);
            accy = (nc_data[3]<<2) + ((nc_data[5] >> 4) & 2);
            accz = (nc_data[4]<<2) + ((nc_data[5] >> 5) & 6);

            accx = (accx-517)/2.1;

            accy = (accy-506)/2;

            accz = (accz - 505)/2;

            // end of pass through wiichuck data read

            }

            // send the data to the LCD
            //lcd_home();
            //fprintf_P(&lcd_stream, PSTR("JoyX: %3u  JoyY: %3u"),nc_data[0],nc_data[1]);
            //lcd_line_two();
            //fprintf_P(&lcd_stream, PSTR("Acc X:%4d  Y:%4d"),accx,accy);
            //lcd_line_three();
            //fprintf_P(&lcd_stream, PSTR("Acc Z:%4d"),accz);
            //lcd_line_four();
            //fprintf_P(&lcd_stream, PSTR("Button Z:%2u  C:%2u"),btnz,btnc);

    if ((nc_data[5]&2)==2) { // if bit one of byte 5 = 1 then getting motion plus data

        yaw[0] = (((nc_data[3]&0xFC)<<6) + nc_data[0]);

        roll[0] = (((nc_data[4]&0xFC)<<6) + nc_data[1]);

        pitch[0] = (((nc_data[5]&0xFC)<<6) + nc_data[2]);

//find zero values at first go or hit of c and z buttons

        if (init<93) { if (init>2) {    yawinit += yaw[0];
                                        rollinit += roll[0];
                                        pitchinit += pitch[0];} 
                                            init += 1;
                        }

        yaw[0] = ((yaw[0]) -(yawinit/90));
            if (((nc_data[3]&0x02)==2));else { yaw[0] = yaw[0]*(50/11);
                                    printf_P(PSTR("\r\n Yaw high speed mode"));}

        roll[0] = ((roll[0])-(rollinit/90));
            if (((nc_data[4]&0x02)==2)); else { roll[0] = roll[0]*(50/11);
                                    printf_P(PSTR("\r\n Roll high speed mode"));}

        pitch[0] = ((pitch[0])-(pitchinit/90));
            if (((nc_data[3]&0x01)==1)); else{ pitch[0] = pitch[0]*(50/11);
                                    printf_P(PSTR("\r\n Pitch high speed mode"));}

            //update and shift array to hold latest 5 readings
                         for(i=0;i<4;i++)
        {
          yaw[4-i]=yaw[3-i];
            pitch[4-i]=pitch[3-i];
        }
         //average last 5 readings

         for(i=1;i<5;i++)
      {

        yaw[0] += yaw[i];

        roll[0] += roll[i];

        pitch[0] += pitch[i];}

        yaw [0] = yaw[0]/5;
        roll[0] = roll[0]/5;
        pitch[0] = pitch[0]/5;

    // calculate displacement

if (init>90){   
        t1 = time;
        dt=(t1-t2);//in .1 ms increments
        yawd += dt*yaw[0]/10000;
        rolld += dt*roll[0]/10000;
        pitchd += dt*pitch[0]/10000;
        t2 = t1; }

        else t2=time;

        pitchaccel = (pitch[0]-((pitch[4]+pitch[3]+pitch[2]+pitch[1])/4))*10/dt;

//end of motion plus data read

}
        // Print the current data to the serial port.

    printf_P(PSTR("\r\n JX: %3d JY: %3d"), jx, jy);

    printf_P(PSTR(" AX:%4d  AY:%4d"),accx,accy);

    printf_P(PSTR(" Button C: %2u"), btnc);

    printf_P(PSTR(" Yaw:%4d"), yaw[0]);

    printf_P(PSTR(" Paccel: %4d mrad/s*s"), pitchaccel);

    printf_P(PSTR(" Pitch:%4d mrad/sec"), pitch[0]);

    printf_P(PSTR(" Pitch displacement:%4d mrad"), pitchd);

    printf_P(PSTR(" dt: %8d sec"), time);

   }

    for(;;);

    return 0;

}
May 14, 2011
by kle8309
kle8309's Avatar

Thanks you esoderberg posting and wiibrew for the decryption code! Thanks everyone else for their contribution also.
This makes my life easier !(However, somewhat less fun)

April 24, 2012
by theomalon
theomalon's Avatar

I was wondering if you can us this and instead of controlling the servos, can you use the Wii remote to control an RC car instead?

April 25, 2012
by Ralphxyz
Ralphxyz's Avatar

Short answer yes, essentially instead of directing a servo (which is what you have on your RC car) directly, you just put your transmitter and receiver in between.

That would be fun keep us posted.

Ralph

April 25, 2012
by theomalon
theomalon's Avatar

Thank you Ralph. I would like to use the Z button to go forward and C to go backwards. I'll post a new thread if I get it working and/or need more help.

April 25, 2012
by Ralphxyz
Ralphxyz's Avatar

Why not tilt it forward and back for forward and reverse and then tilt left and right for turning?

The buttons will work, maybe you are doing something else with the tilt?

Of course you might find your self accelerating when you turn but you could do a override in code.

Ralph

April 25, 2012
by theomalon
theomalon's Avatar

Actually I thought about that a few mintues after I made that post so that is what I think I will do.

April 30, 2012
by theomalon
theomalon's Avatar

So I am having problems with this makefile

GCCFLAGS=-g -Os -Wall -mmcu=atmega168
LINKFLAGS=-Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt -lm
AVRDUDEFLAGS=-c avr109 -p m168 -b 115200 -P COM3
LINKOBJECTS=../libnerdkits/delay.o ../libnerdkits/lcd.o ../libnerdkits/uart.o twimaster.o

all:    Nunchuck-upload

Nunchuck.hex:   Nunchuck.c
    make -C ../libnerdkits
    avr-gcc ${GCCFLAGS} -o twimaster.o -c twimaster.c
    avr-gcc ${GCCFLAGS} ${LINKFLAGS} -o Nunchuck.o Nunchuck.c ${LINKOBJECTS}
    avr-objcopy -j .text -O ihex Nunchuck.o Nunchuck.hex

Nunchuck.ass:   Nunchuck.hex

    avr-objdump -S -d Nunchuck.o > Nunchuck.ass

Nunchuck-upload:    Nunchuck.hex
    avrdude ${AVRDUDEFLAGS} -U flash:w:Nunchuck.hex:a

I keep getting makefile:9: *** missing separator. Stop. Don't understand why I get this error when it is the same on other makefiles. I have also tired the other ones on this thread. I am using a windows 7 computer.

Thanks

May 01, 2012
by Rick_S
Rick_S's Avatar

All the command lines in a makefile begin with tab characters not spaces. If you edited the makefile (or sometimes copied) and either used spaces instead of tabs or the editor you used did it by itself, you will get this error.

makefile:9 *** missing separartor

means the error is on line 9 which is your 1st command line. I'm not sure what you used to edit the makefile, but don't use notepad in windows. It will mess it up. I usually use programmers notepad which is part of the WINAVR install.

Just remember, any indented line should begin with tabs not spaces, any blank line should be just that... blank with no spaces or tabs.

Hope this gets you going,

Rick

May 01, 2012
by theomalon
theomalon's Avatar

Thanks Rick. That is what it was. I am not getting anything on my LCD though. I am using the 9V battery that comes with it. Do I need a better power supply?

May 01, 2012
by Ralphxyz
Ralphxyz's Avatar

Redo your wiring, after the forth or fifth time you will be amazed how fast and easy that is to do.

If you still have problems post some pictures.

Ralph

May 01, 2012
by theomalon
theomalon's Avatar

Now it is outputting 1st start bad and 2nd start bad on the LCD. What does that mean?

May 01, 2012
by Ralphxyz
Ralphxyz's Avatar

I think if you look at the code that is rather self explanatory, but hopefully Rick will jump in and explain the specifics:

    uint8_t ret=i2c_start(Nunchk_ADR+I2C_WRITE);
    if(ret==1) 
      {
        fprintf_P(&lcd_stream, PSTR("1st start bad"));
        }
    i2c_write(0x40);
    i2c_write(0x00);
    i2c_stop();

    while(1){

            // Start Nunchuck read by writing initiating a start then writing 0x00

            uint8_t ret=i2c_start(Nunchk_ADR+I2C_WRITE);
            if(ret==1) 
            {
                lcd_line_two();
                fprintf_P(&lcd_stream, PSTR("2nd start bad"));
            }

You are not communicating with the NunChuck.

So was it re-wiring that fixed the LCD problem or something else you did?

Ralph

May 01, 2012
by theomalon
theomalon's Avatar

Yes I rewired it. Thank you. Now I'll have to figure out why it is not communicating with the nunchuck. Thanks again

May 01, 2012
by theomalon
theomalon's Avatar

Even though I do not have a servo connected, it should still output the data on the LCD right?

May 01, 2012
by Rick_S
Rick_S's Avatar

That means it isn't communicating with the Nunchuk like Ralph said. Either it isn't initializing correctly or there is a wiring problem preventing communication. You could try the other code posted by esoderberg to see if it works, he initializes it differently. My code works on a genuine nunchuk but hasn't been tested on aftermarket.. If your's is genuine, it should work.

Rick

May 01, 2012
by theomalon
theomalon's Avatar

I'm using the black oem nunchuck. Does that make a difference?

May 02, 2012
by Rick_S
Rick_S's Avatar

I don't have a black one, but I wouldn't think color would matter.

May 02, 2012
by esoderberg
esoderberg's Avatar

Theomalon,

I was running the posted code on both an original Nunchuck and an after market; it worked great on both. I also used an original Motion Plus module and an after market motion plus - in this case the code still worked but the actual gyro output was significantly different. The after market gyros I got were just not very consistent and required a new re-calibration for each one.

Eric

May 03, 2012
by theomalon
theomalon's Avatar

So I got it to display what it should on the LCD but none of the data is changing when you move the joystick, controller, or push the buttons. Thanks again for everyone's help.

Post a Reply

Please log in to post a reply.

Did you know that our USB NerdKit works on Windows, Linux, and Mac OS X? Learn more...