Nyko Kama Wireless Nunchuck - Intermittent sync problem

Hello,

I am using the Nyko Kama wireless nunchuck to control my electric longboard, and it works fairly well. There are several threads on this topic, but they are all over months old and do not address my question. I can read data from the nunchuck, map it to the correct values for the Servo library, and then use that library to control the ESC connected to the brushless motor.

However, I am having some problems with the initial sync between the nunchuck and its' receiver. When I reset the Arduino, sometimes it works just fine and I get meaningful data from the chuck, and I can ride the board around for miles. Other times my serial monitor starts and shows that the joystick is at its minimum value and the system is totally unresponsive, or I get the joystick at max value and no response (other than my board taking off at full throttle...).

This is my second arduino project, and I am very new to programming as a whole (some MatLab experience, but I don't think that really counts). I have tried to use a couple IF statements to see if the nunchuck is receiving good data, but I don't think they are working how I intended (if at all).

I am using Tim Hirzel's WiiChuck library, but I am not experienced enough to work through it and find what is not playing nice with my nunchuck. I think there is something in his 'void begin()' that my particular nunchuck does not like, but I don't know. I'm trying to figure out how the problem could be intermittent, and I don't know where to start. If not the code, it could be my $6 nunchuck, cheap-o breadboard/jumpers, or even the fake arduino clone I have until my replacement real one shows up. Any advice would be appreciated.

My sketch:

#include <Wire.h>         // Library for I2C communication
#include <WiiChuck.h>            // Library for reading data from Wii Nunchuck
#include <Servo.h>                   // Library for controlling Servo or ESC
 
WiiChuck chuck = WiiChuck();
Servo myservo;

int speed_val;                      // Speed value
int speed_val_cur = 90;  // Current speed value
int y = 0;                                // Joystick value
int time = 200;                    // Delay timer
int speed_val_hold;         // speed value for the throttle hold feature

// Setup.  This runs at startup, and initializes the nunchuck
void setup() {

Serial.begin(115200);  // Begin serial communication with baud rate at 115200.  For debugging
chuck.begin();               // Initialize nunchuck.  This function is located in the WiiChuck.h library
chuck.update();           // Read nunchuck.  This function is located in the WiiChuck.h library
myservo.attach(9);    // ESC attached to 9 pin of Arduino

// Print to serial monitor.  This tells us that the setup ran once.  Again, for debugging
Serial.println();
Serial.print("Initialization Sequence Complete");
Serial.println();
delay(1000);
}

// Main loop.  This is what actually does stuff.  The rest of the sketch is inside this
void loop() {

//Check if Z or C button is pressed
if ((chuck.buttonZ)||(chuck.buttonC)) {
  
  // If you hold down Z, the throttle hold is enabled and speed adjustments occur slowly (think cruise control buttons in a car)
  if (chuck.buttonZ) {
    Serial.print(" Z ");
    time = 50;
  }
  
  /*
  // If you press C, something will happen  -- NOTE: This feature is not yet implimented, so nothing will really happen
  if (chuck.buttonC) {
    do stuff
  }
  */
} 

// If no buttons are pressed, throttle response is set to maximum (think throttle/brake pedels in a car)
else {
  time = 5;
}

// Delay for the time amount specified above.  This determines how fast the throttle will react to input
delay(time);

// Request updated data from the nunchuck, and assign the Y axis position to variable y
chuck.update();
y = chuck.readJoyY();

// Map the joystick value onto the speed value (joystick at 0 = speed at 90.  This is neutral throttle)
speed_val = map(y, -128, 125, 30, 150);

// If the connection is lost, set all speeds to 90.  Remember, speed = 90 is neutral.  IE. the motor is stopped.
if ((y > 125) || (y < -129)) {
speed_val = 90;
speed_val_cur = 90;
speed_val_hold = 90;
setup();
}

if (chuck.buttonZ) {                         // If Z is pressed (throttle hold on):  
  
  if ((speed_val_hold < speed_val)           // Check if we are trying to accellerate:
    && (y > 5)) {
      
    speed_val_hold++;                        // If hold button is pressed and throttle stick moved up – increase speed
  }  
  if ((speed_val_hold > speed_val)           // Check if we are trying to decellerate:
    && (y < -5)) {
    
    speed_val_hold--;                        // If hold button is pressed and throttle stick moved down – decrease speed
  }    
  speed_val_cur = speed_val_hold;
}

else {                                  // If Z  is not pressed (throttle hold off):

  if (speed_val_cur < speed_val) {      // Check if we are trying to accellerate:
    speed_val_cur++;                    // If hold button is NOT pressed and throttle stick moved up – increase speed
  }
  if (speed_val_cur > speed_val) {      // Check if we are trying to decellerate:
    speed_val_cur--;                    // If hold button is NOT pressed and throttle stick moved down – decrease speed
  }
speed_val_hold = speed_val_cur;
}

// Write current speed value to the ESC
myservo.write(speed_val_cur);


//Print important variables to serial monitor.  For debugging purposes.
Serial.println();

Serial.print("Joy = ");
Serial.print(y);

Serial.print(" speed_val = ");
Serial.print(speed_val);

Serial.print(" speed_hold = ");
Serial.print(speed_val_hold);

Serial.print(" Speed = ");
Serial.print(speed_val_cur);
}

Tim Hirzel's WiiChuck Library:

/*
 * Nunchuck -- Use a Wii Nunchuck
 * Tim Hirzel http://www.growdown.com
 * 
 notes on Wii Nunchuck Behavior.
 This library provides an improved derivation of rotation angles from the nunchuck accelerometer data.
 The biggest different over existing libraries (that I know of ) is the full 360 degrees of Roll data
 from teh combination of the x and z axis accelerometer data using the math library atan2. 

 It is accurate with 360 degrees of roll (rotation around axis coming out of the c button, the front of the wii),
 and about 180 degrees of pitch (rotation about the axis coming out of the side of the wii).  (read more below)

 In terms of mapping the wii position to angles, its important to note that while the Nunchuck
 sense Pitch, and Roll, it does not sense Yaw, or the compass direction.  This creates an important
 disparity where the nunchuck only works within one hemisphere.  At a result, when the pitch values are 
 less than about 10, and greater than about 170, the Roll data gets very unstable.  essentially, the roll
 data flips over 180 degrees very quickly.   To understand this property better, rotate the wii around the
 axis of the joystick.  You see the sensor data stays constant (with noise).  Because of this, it cant know
 the difference between arriving upside via 180 degree Roll, or 180 degree pitch.  It just assumes its always
 180 roll.


 * 
 * This file is an adaptation of the code by these authors:
 * Tod E. Kurt, http://todbot.com/blog/
 *
 * The Wii Nunchuck reading code is taken from Windmeadow Labs
 * http://www.windmeadow.com/node/42
 * 
 * Conversion to Arduino 1.0 by Danjovic
 * http://hotbit.blogspot.com 
 * 
 */

#ifndef WiiChuck_h
#define WiiChuck_h


#include "Arduino.h"
#include <Wire.h>
#include <math.h>


// these may need to be adjusted for each nunchuck for calibration
#define ZEROX 510  
#define ZEROY 490
#define ZEROZ 460
#define RADIUS 210  // probably pretty universal

#define DEFAULT_ZERO_JOY_X 124
#define DEFAULT_ZERO_JOY_Y 130



class WiiChuck {
    private:
        uint8_t cnt;
        uint8_t status[6];		// array to store wiichuck output
        uint8_t averageCounter;
        //int accelArray[3][AVERAGE_N];  // X,Y,Z
        int i;
        int total;
        uint8_t zeroJoyX;   // these are about where mine are
        uint8_t zeroJoyY; // use calibrateJoy when the stick is at zero to correct
        int lastJoyX;
        int lastJoyY;
        int angles[3];

        bool lastZ, lastC;


    public:

        uint8_t joyX;
        uint8_t joyY;
        bool buttonZ;
        bool buttonC;
        void begin() 
        {
            Wire.begin();
            cnt = 0;
            averageCounter = 0;
            // instead of the common 0x40 -> 0x00 initialization, we
            // use 0xF0 -> 0x55 followed by 0xFB -> 0x00.
            // this lets us use 3rd party nunchucks (like cheap $4 ebay ones)
            // while still letting us use official oness.
            // only side effect is that we no longer need to decode bytes in _nunchuk_decode_byte
            // see http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1264805255
            //  
            Wire.beginTransmission(0x52);	// device address
            Wire.write(0xF0);
            Wire.write(0x55);
            Wire.endTransmission();
            delay(1);
            Wire.beginTransmission(0x52);
            Wire.write(0xFB);
            Wire.write((uint8_t)0x00);

            Wire.endTransmission();
			delay(1);
            update();            
            for (i = 0; i<3;i++) {
                angles[i] = 0;
            }
            zeroJoyX = DEFAULT_ZERO_JOY_X;
            zeroJoyY = DEFAULT_ZERO_JOY_Y;
        }


        void calibrateJoy() {
            zeroJoyX = joyX;
            zeroJoyY = joyY;
        }

        void update() {

            Wire.requestFrom (0x52, 6);	// request data from nunchuck
            while (Wire.available ()) {
                // receive byte as an integer
                status[cnt] = _nunchuk_decode_byte (Wire.read()); //
                cnt++;
            }
            if (cnt > 5) {
                lastZ = buttonZ;
                lastC = buttonC;
                lastJoyX = readJoyX();
                lastJoyY = readJoyY();
                //averageCounter ++;
                //if (averageCounter >= AVERAGE_N)
                //    averageCounter = 0;

                cnt = 0;
                joyX = (status[0]);
                joyY = (status[1]);
                for (i = 0; i < 3; i++) 
                    //accelArray[i][averageCounter] = ((int)status[i+2] << 2) + ((status[5] & (B00000011 << ((i+1)*2) ) >> ((i+1)*2))); 
                    angles[i] = (status[i+2] << 2) + ((status[5] & (B00000011 << ((i+1)*2) ) >> ((i+1)*2))); 

                //accelYArray[averageCounter] = ((int)status[3] << 2) + ((status[5] & B00110000) >> 4); 
                //accelZArray[averageCounter] = ((int)status[4] << 2) + ((status[5] & B11000000) >> 6); 

                buttonZ = !( status[5] & B00000001);
                buttonC = !((status[5] & B00000010) >> 1);
                _send_zero(); // send the request for next bytes

            }
        }


    // UNCOMMENT FOR DEBUGGING
    //byte * getStatus() {
    //    return status;
    //}

    float readAccelX() {
       // total = 0; // accelArray[xyz][averageCounter] * FAST_WEIGHT;
        return (float)angles[0] - ZEROX;
    }
    float readAccelY() {
        // total = 0; // accelArray[xyz][averageCounter] * FAST_WEIGHT;
        return (float)angles[1] - ZEROY;
    }
    float readAccelZ() {
        // total = 0; // accelArray[xyz][averageCounter] * FAST_WEIGHT;
        return (float)angles[2] - ZEROZ;
    }

    bool zPressed() {
        return (buttonZ && ! lastZ);
    }
    bool cPressed() {
        return (buttonC && ! lastC);
    }

    // for using the joystick like a directional button
    bool rightJoy(int thresh=60) {
        return (readJoyX() > thresh and lastJoyX <= thresh);
    }

    // for using the joystick like a directional button
    bool leftJoy(int thresh=60) {
        return (readJoyX() < -thresh and lastJoyX >= -thresh);
    }


    int readJoyX() {
        return (int) joyX - zeroJoyX;
    }

    int readJoyY() {
        return (int)joyY - zeroJoyY;
    }


    // R, the radius, generally hovers around 210 (at least it does with mine)
   // int R() {
   //     return sqrt(readAccelX() * readAccelX() +readAccelY() * readAccelY() + readAccelZ() * readAccelZ());  
   // }


    // returns roll degrees
    int readRoll() {
        return (int)(atan2(readAccelX(),readAccelZ())/ M_PI * 180.0);
    }

    // returns pitch in degrees
    int readPitch() {        
        return (int) (acos(readAccelY()/RADIUS)/ M_PI * 180.0);  // optionally swap 'RADIUS' for 'R()'
    }

    private:
        uint8_t _nunchuk_decode_byte (uint8_t x)
        {
            //decode is only necessary with certain initializations 
            //x = (x ^ 0x17) + 0x17;
            return x;
        }

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

};


#endif

Hi there,

Have you got an answer on your post yet.

So, I have the same issues as you. For the full speed problem I added the below code (since every time that happens the speed is always the same 90 90 90 90 90 or 180 180 180 180 180 180 180).

My main problem is that my esc stops the motor if the signal is lost. That throws me out of the board. Does yours do that to you, if not can you send me your ESC specifics.

//Handle loss of signal
if (sm == Previoussm && Previoussm !=90 && Previoussm !=180)
{
Serial.print("Dupplicate value");
Serial.println(DuplicateCounter);

DuplicateCounter++;
if (DuplicateCounter > DupplicateLimit)
{
Serial.println("Reducing Speed...");
//digitalWrite(resetPin, LOW);
sm = 100;
while (sm !=90)
{
Serial.println(sm, DEC);
Serial.print(' ');
//Serial.print(Previoussm);
eMotor.write(sm);
delay(50);
sm--;
}
delay(8000);
}

}
else
{
DuplicateCounter = 0;
}

I uploaded your code.

My code will not work for yours because of your cruising logic.

I found a way to reproduce it. If you accelerate and take the Nunchuck battery off the speed keeps at whatever you had it. Then when you put the battery back on, the speed goes all the way to 150.