// nunchuk.ino
// Aaron Garcia
// 3/8/2020
// Collects data from nunchuk i2c device and prints digital values that corresponds to movements on the x and y axis'

#include <Wire.h>
#include <String.h>

#define USECALIBRATEDVALUES 0 // Set to 1 to use the factory calibrated values from the remote. NOTE: some remotes do not have saved values and therefore the program will not work

static uint8_t nunchuk_buf[6];
static uint8_t nunchuk_info[16];
static uint8_t xpos, ypos, zpress, cpress, xmin = 0, xmax = 255, xcenter = 127, ymin = 0, ymax = 255, ycenter = 127;
int x, y, xinc, yinc;

void nunchuk_init(){ // Initialize nunchuk
  Wire.begin();
  Wire.beginTransmission(0x52);
  Wire.write((uint8_t)0x40);
  Wire.write((uint8_t)0x00);
  Wire.endTransmission();
}

int nunchuk_get_device_info(){ // Retreive the 16-bytes of device facotry calibration values from the register at 0x20
  Wire.beginTransmission(0x52);
  Wire.write((uint8_t)0x20);
  Wire.endTransmission();
  delay(1000);
  Wire.requestFrom(0x52, 16);
  int counter=0;
  while(Wire.available()){
    nunchuk_info[counter]=Wire.read();
    counter++;
  }
  for(int i =0; i<16;i++){
    Serial.println(nunchuk_info[i]);
  }
  if(counter>=15){
    return 1;
  }
  return 0;
}

void nunchuk_store_data(){ // Signals to the nunchuk to store new sensor values
  Wire.beginTransmission(0x52);
  Wire.write((uint8_t)0x00);
  Wire.endTransmission();
}

char decrypt(char x){ // Decrypts information received from the nunchuk
  x = (x^0x17) + 0x17;
  return x;
}

int nunchuk_get_data(){ // Receives the 6-bytes of sensor data from the nunchuk
  int counter=0;
  Wire.requestFrom(0x52, 6);
  while(Wire.available()){
    nunchuk_buf[counter]=decrypt(Wire.read());
    counter++;
  }
  if(counter>=5){
    return 1;
  }
  return 0;
}

void print_data(){ // Prints the x and y increment and decrement information to the arduino's serial port
  xpos = nunchuk_buf[0];
  ypos = nunchuk_buf[1];
  if(xpos >= (((xmax-xcenter)/2)+xcenter)){
    x = 1;
    xinc = 1;
  }
  else if(xpos <= ((xcenter-xmin)/2)){
    x = 1;
    xinc = 0;
  }
  else{
    x = 0;
    xinc = 0;
  }
  if(ypos >= (((ymax-ycenter)/2)+ycenter)){
    y = 1;
    yinc = 1;
  }
  else if(ypos <= ((ycenter-ymin)/2)){
    y = 1;
    yinc = 0;
  }
  else{
    y = 0;
    yinc = 0;
  }
  zpress = ((nunchuk_buf[5] >> 0) & 1) ? 0 : 1;
  cpress = ((nunchuk_buf[5] >> 1)  & 1) ? 0 : 1;
  if(zpress){
    Serial.print((char)254);  // Signals to matlab to stop receiving values from the nunchuk
  }
  else{
    Serial.print((char)255);
  }
  Serial.print((char)x);
  Serial.print((char)xinc);
  Serial.print((char)y);
  Serial.print((char)yinc);
}

void setup() { // Initializes the nunchuk and reads calibration values if applicable
  Serial.begin(9600);
  delay(100);
  nunchuk_init();
  delay(10);
  if(USECALIBRATEDVALUES){
    nunchuk_get_device_info();
  }
  nunchuk_store_data();
}

void loop() { // Receives the saved nunchuk sensor data and prints operations to serial port, then requests new data be stored for the next read
  delay(10);
  int check = nunchuk_get_data();
  nunchuk_store_data();
  print_data();
}
