nunchuk.h
Note that it is nunchuk, not nunchuck. This library has been tested with the cheap Chinese made knockoffs of the Wii Nunchuk which are available for about $3 each. You can follow this guide for how to cut off the connector to wire it to an Arduino, or purchase an Adapter for about 30 cents each.
/**
 * @license Nunchuk Arduino library v0.0.1 16/12/2016
 * http://www.xarg.org/2016/12/arduino-nunchuk-library/
 *
 * Copyright (c) 2016, Robert Eisele (robert@xarg.org)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * Refactor: bkucenski@gmail.com 2/22/2019
 **/

#ifndef NUNCHUK_H
#define NUNCHUK_H

#include <Wire.h>

// The Nunchuk I2C address
#define NUNCHUK_ADDRESS 0x52

// the clock speed, 400,000 works
#define CLOCK_NUNCHUK 400000

// Calibration accelerometer values, depends on your Nunchuk
#define NUNCHUK_ACCEL_X_ZERO 512
#define NUNCHUK_ACCEL_Y_ZERO 512
#define NUNCHUK_ACCEL_Z_ZERO 512

// Calibration joystick values
#define NUNCHUK_JOYSTICK_X_ZERO 127
#define NUNCHUK_JOYSTICK_Y_ZERO 128


uint8_t nunchuk_data[6];
uint8_t nunchuk_cali[16];

#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) // Only Arduino UNO
/**
 * Use normal analog ports as power supply, which is useful if you want to have all pins in a row
 * Like for the famous WiiChuck adapter
 * @see https://todbot.com/blog/2008/02/18/wiichuck-wii-nunchuck-adapter-available/
 */
static void NunchukInitPower() {
    // Add power supply for port C2 (GND) and C3 (PWR)
    PORTC &= ~_BV(PORTC2);
    PORTC |= _BV(PORTC3);
    DDRC |= _BV(PORTC2) | _BV(PORTC3);
    delay(100);
}
#endif
/**
 * Initializes the Nunchuk communication by sending a sequence of bytes
 */
static void NunchukInit() {

    // Change TWI speed for nuchuk, which uses Fast-TWI (400kHz)
    Wire.setClock(CLOCK_NUNCHUK);

    // delay(1);

    Wire.beginTransmission(NUNCHUK_ADDRESS);
    Wire.write(0xF0);
    Wire.write(0x55);
    Wire.endTransmission();
    // delay(1);
    Wire.beginTransmission(NUNCHUK_ADDRESS);
    Wire.write(0xFB);
    Wire.write(0x00);
    Wire.endTransmission();
    // delay(1);

}


/**
 * Central function to read a full chunk of data from Nunchuk
 *
 * @return A boolean if the data transfer was successful
 */
static uint8_t NunchukRead() {

    uint8_t i;
    Wire.requestFrom(NUNCHUK_ADDRESS, 6);
    //delayMicroseconds(10);
    for (i = 0; i < 6 && Wire.available(); i++) {
        nunchuk_data[i] = Wire.read();
    }
    Wire.beginTransmission(NUNCHUK_ADDRESS);
    Wire.write(0x00);
    // delayMicroseconds(100);
    Wire.endTransmission();
    return i == 6;
}

/**
 * Checks the current state of button Z
 */
static uint8_t NunchukZ() {
    return (~nunchuk_data[5] >> 0) & 1;
}

/**
 * Checks the current state of button C
 */
static uint8_t NunchukC() {
    return (~nunchuk_data[5] >> 1) & 1;
}

/**
 * Retrieves the raw X-value of the joystick
 */
static uint8_t NunchukXRaw() {
    return nunchuk_data[0];
}

/**
 * Retrieves the raw Y-value of the joystick
 */
static uint8_t NunchukYRaw() {
    return nunchuk_data[1];
}

/**
 * Retrieves the calibrated X-value of the joystick
 */
static int16_t NunchukX() {
    return (int16_t) nunchuk_data[0] - (int16_t) NUNCHUK_JOYSTICK_X_ZERO;
}

/**
 * Retrieves the calibrated Y-value of the joystick
 */
static int16_t NunchukY() {
    return (int16_t) nunchuk_data[1] - (int16_t) NUNCHUK_JOYSTICK_Y_ZERO;
}

/**
 * Calculates the angle of the joystick
 */
static float NunchukAngle() {
    return atan2((float) NunchukX(), (float) NunchukY());
}

/**
 * Retrieves the raw X-value of the accelerometer
 */
static uint16_t NunchukXAccelRaw() {
    return ((uint16_t) nunchuk_data[2] << 2) | ((nunchuk_data[5] >> 2) & 3);
}

/**
 * Retrieves the raw Y-value of the accelerometer
 */
static uint16_t NunchukYAccelRaw() {
    return ((uint16_t) nunchuk_data[3] << 2) | ((nunchuk_data[5] >> 4) & 3);
}

/**
 * Retrieves the raw Z-value of the accelerometer
 */
static uint16_t NunchukZAccelRaw() {
    return ((uint16_t) nunchuk_data[4] << 2) | ((nunchuk_data[5] >> 6) & 3);
}

/**
 * Retrieves the calibrated X-value of the accelerometer
 */
static int16_t NunchukXAccel() {
    return (int16_t) NunchukXAccelRaw() - (int16_t) NUNCHUK_ACCEL_X_ZERO;
}

/**
 * Retrieves the calibrated Y-value of the accelerometer
 */
static int16_t NunchukYAccel() {
    return (int16_t) NunchukYAccelRaw() - (int16_t) NUNCHUK_ACCEL_Y_ZERO;
}

/**
 * Retrieves the calibrated Z-value of the accelerometer
 */
static int16_t NunchukZAccel() {
    return (int16_t) NunchukZAccelRaw() - (int16_t) NUNCHUK_ACCEL_Z_ZERO;
}

/**
 * Calculates the pitch angle THETA around y-axis of the Nunchuk in radians
 */
static float NunchukPitch() { // tilt-y
    return atan2((float) NunchukYAccel(), (float) NunchukZAccel());
}

/**
 * Calculates the roll angle PHI around x-axis of the Nunchuk in radians
 */
static float NunchukRoll() { // tilt-x
    return atan2((float) NunchukXAccel(), (float) NunchukZAccel());
}

/**
 * A handy function to print either verbose information of the Nunchuk or a CSV stream for Processing
 */
static void NunchukPrintDebug() {

    Serial.print("joy: ");
    Serial.print(NunchukX(), DEC);
    Serial.print(", ");
    Serial.print(NunchukY(), DEC);

    Serial.print("  acc:");
    Serial.print(NunchukXAccel(), DEC);
    Serial.print(", ");
    Serial.print(NunchukYAccel(), DEC);
    Serial.print(", ");
    Serial.print(NunchukZAccel(), DEC);

    Serial.print("  but:");
    Serial.print(NunchukZ(), DEC);
    Serial.print(", ");
    Serial.print(NunchukC(), DEC);
    Serial.print("\n");
}

#endif