// Version 0.14
// Now with servo and new neopixel library
// Two 'weapon' surport.
// More code tidyups
// Robot jumper mode select

// USB Host shield
#include <PS3BT.h>
#include <usbhub.h>
#include <SPI.h>

// Built in servo library
#include <Servo.h>

//Uncomment one of these
//#include <Adafruit_NeoPixel.h>
#include <WS2812.h>   // https://github.com/cpldcpu/light_ws2812

// Uncomment depending on style of bot
//#define SKIDBOT
//#define STEERBOT
// 0=Test only.   1=Skidsteer bot,   2=Steering style bot
char bottype=1;

//Setup serial port and debug timer
#define SERIAL_PORT_SPEED 115200
#define DEBUGVALUE 500  // Every DEBUGVALUE trigger debug code
unsigned long debugtimer;
unsigned long loopmills;

// Reduses controller jitter
#define DEADZONE 8

// H-Bridge motor controller for DC motors
// motor one
#define pwmA 5    // Must be a PWM pin
#define aIN1 2    // H-Bridge
#define aIN2 3    // Control pins
// motor two
#define pwmB 6   // PWM
#define bIN1 4
#define bIN2 7

// Servos
#define SERVOONEPIN 8   // Where are they connected
#define SERVOTWOPIN A4
Servo servo_one;        // create servo object to control a servo
Servo servo_two;  

// Battery monitoring analog input pin
#define VINPIN A0

int dirval,speedval,weppval,auxval;                // Inputs
int left_motor,right_motor,webb_motor,aux_motor;   // Outputs

int motcurrmax;               // Peek control value
int battvolt;                 // Battery voltage

// Spinner style weapon spinup delays. Prevents motor stripping and looks more dramatic
#define WEPPTIMERVALUE 100  // How often (in ms) to update speed
#define WEPPRATEDELTA 10   // Weapon spin up speed, units per WEPPTIMERVALUE
#define WEPPMAX 200   // Weapon max speed
unsigned long wepptimer;
int currweppspeed;

// Settings and timeouts for 'boost'.
// Motors are 6v and this allows tempory driving to full battery voltage
#define HALFPOWERTIMEOUT 10000    // In milis.
#define HALFPOWERCOOLDOWN -20000
#define HALFPOWER 170   //  (actually halfish)     Limit motor to 2/3 power:  (255/3)*2
long halfpowertimer;
int motormax=HALFPOWER;

// 0=Debug: No movement, all controlls displayed.
// 1=Single stick: Right stick, X axis Steer.  Y axis throttle.
// 2=Two stick: Left stick Y axis Throttle. Right stick X axis steering.
// 3=D-Pad: For crazy people. Digital only for now, May add preassure sense.  (Not working)
// 4=Hayden: Right trigger forward, left trigger backward, left stick X steering.
int controlmode=1;

// Main RGB LEDs
#define PIXELPIN  A1 
#define NUMPIXELS  2
//Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(NUMPIXELS, PIXELPIN, NEO_GRB + NEO_KHZ400);
WS2812 LED(NUMPIXELS); // light_ws2812 

// USB Host Shield object setup 
USB Usb;
BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so
PS3BT PS3(&Btd); // This will just create the instanceinstance

// Runs just once
void setup() {

  // Test to see if Tx and Rx have been shorted by a jumper.
  // This sets the robot's mode:  Jumper off: Skid.  Jumper on: Steer
  pinMode(1, OUTPUT);        // Tx pin
  pinMode(0, INPUT_PULLUP);  // Rx pin
  setPixelRGB(0, 00, 30, 00);
  digitalWrite(1, LOW);  // Pull Tx LOW
  delay(10);
  if(digitalRead(0)==LOW) {  // Read Rx
    digitalWrite(1, HIGH);  // If low, take Tx HIGH
    delay(10);
    if(digitalRead(0)==HIGH) {  // See if state changed
      bottype=2;   // Change robot mode.
    } 
  }
  
  // Serial port setup with delay to connect
  Serial.begin(SERIAL_PORT_SPEED);
  delay(500);
  Serial.print("Starting... Bot Mode: ");  // Just so we know where we are
  Serial.println(bottype,DEC);
  
  // RGB led library setup
//  neopixel.begin();
  LED.setOutput(PIXELPIN);

// Lights depend on bot type to show we have started
switch (bottype){
  case 0:   // Test/debug
    setPixelRGB(0, 30, 30, 30);  // White
  break;
  case 1:  // Skidbot
    setPixelRGB(0, 30, 30, 00);  // Green
  break;
  case 2:   // Steerbot
    setPixelRGB(0, 00, 30, 30);  // Yellow
  break;
}
  setPixelRGB(1, 00, 00, 00);


  // Give the user a moment to see the LEDs
  delay(1000);

  // set all the motor control pins to outputs and make sure motors are off
  pinMode(pwmA, OUTPUT);
  pinMode(pwmB, OUTPUT);
  pinMode(aIN1, OUTPUT);
  pinMode(aIN2, OUTPUT);
  pinMode(bIN1, OUTPUT);
  pinMode(bIN2, OUTPUT);
  setmotor(0,0);
  setmotor(1,0);  

  // attach each servo to the servo object and set servos to halfway
  servo_one.attach(SERVOONEPIN); 
  servo_two.attach(SERVOTWOPIN);   
  setservo(0,0);
  setservo(1,0);

  // Start USB subsystem
  if (Usb.Init() == -1) {
    Serial.print("\r\nOSC did not start");   // Very bad. You will not robot today
    setPixelRGB(0, 255, 00, 00);  // both lights red
    setPixelRGB(1, 255, 00, 00);
    while (1); // halt - no point going further, hardware fault
  }
  Serial.print("\r\nWaiting for pairing..");  // Actually, just 'usb is working'
  
}

// Around and around and around
void loop() {
  // For debug and weapon timeouts
  loopmills=millis();

  // Uncomment only to test motors. This disables everthing else
// dcmot_test();

  // Make sure this runs fairly often. USB and bluetooth both need this
  Usb.Task();
  
  // No bluetooth connection
  if (!PS3.PS3Connected) {
    //turn off or center all motors
    setmotor(0,0);
    setmotor(1,0);
    setservo(0,0);
    setservo(1,0);
    // Turn on blue LED, others off
    setPixelRGB(0, 00, 00, 50);
    setPixelRGB(1, 00, 00, 00);
    // Wait (And around we go!)
    return;
  }

  // Boost on
  if (PS3.getButtonClick(TRIANGLE)) {
    if(halfpowertimer==0){
      motormax=255;
      PS3.setRumbleOn(RumbleLow);  // A DS3 will have fun here
      Serial.println(F("Boost! ON."));
      halfpowertimer=HALFPOWERTIMEOUT;
    }
  }

  // Manual Boost off
  if (PS3.getButtonClick(CROSS)) {
    motormax=HALFPOWER;
    if(halfpowertimer>0){  // Reward turning off boost before timeout
      halfpowertimer=-((HALFPOWERTIMEOUT-halfpowertimer)*2);     // Timer is twice what remaINs on the timer 
//      halfpowertimer=HALFPOWERCOOLDOWN;   // Disable timer 
    }
//    halfpowertimer=0;   // Uncomment to disable cooldown timer
    PS3.setRumbleOff();
    Serial.println(F("Boost! OFF."));
  }
 
  // Boost timeout
  if(halfpowertimer==1){
    halfpowertimer=HALFPOWERCOOLDOWN;   // Disable timer
    halfpowertimer=halfpowertimer*2;
    motormax=HALFPOWER;   
    PS3.setRumbleOff();
    Serial.println(F("Boost! AUTO OFF."));
  }else if(halfpowertimer>0){  // Boost timeout
    halfpowertimer--;
  }else if(halfpowertimer<0){  // Cooldown timeout
    halfpowertimer++;
  }

  // Select control mode and display on PS3's LEDs
  if (PS3.getButtonClick(SELECT)) {
    controlmode++;
    if(controlmode>4){ controlmode=0; }
    PS3.setLedOff();
    switch (controlmode){
      case 0:   
      break;
      case 1:
        PS3.setLedOn(LED1);
      break;
      case 2:
        PS3.setLedOn(LED2);
      break;
      case 3:
        PS3.setLedOn(LED3);
      break;
      case 4:
        PS3.setLedOn(LED4);
    }
  }



// Servo weapon stuff.
// And 'aux' stuff
  if (PS3.getButtonPress(UP)) {   // While button is being pressed, allow spin up to happen slowly
     weppval=WEPPMAX;
  } else if(PS3.getButtonPress (DOWN)) {  // Spin down fast
     weppval=0;
     webb_motor=0;
  } else {
    weppval=0;  // Spin down slow
   }

  // Spin weapons up towards full speed and down as needed to prevent gearbox damage
  if(wepptimer<loopmills){   // Timer just for updating this
    if(webb_motor<weppval){
      webb_motor=webb_motor+WEPPRATEDELTA;
    } else if(webb_motor>0) {
     webb_motor=webb_motor-WEPPRATEDELTA;
    }
    wepptimer=loopmills+WEPPTIMERVALUE;  //  reset timer
  }
      
// servo aux goes here!

// end

  // Input debugging, also check battery voltage
  if(debugtimer<loopmills){
    // Devider network:  GND---4.6k---A0---8.2k----Vbatt
    // Values: 9v = 671,  9.5v=712, 11.1v=833, 12.6v=941
    Serial.print(F("V:")); 
    battvolt= analogRead(VINPIN);
    battvolt = map(battvolt, 671, 941, 0,254 );
    Serial.print(battvolt);
//    battvolt = 20;    //   Override for hardware testing.   ** COMMENT OUT THIS LINE BEFORE USE **
    // Battery too low, disable motors, flash first neopixel red
    if(battvolt<10){  // Red flash
 //   while (PS3.getButtonClick(TRIANGLE)){    // <-- What is this for?  (Some old debug code?)
      setPixelRGB(0, 00, 00, 00);
      setPixelRGB(1, 00, 00, 00);
      delay(200);
      setPixelRGB(0, 60, 00, 00);
      setPixelRGB(1, 00, 00, 00);
      motormax=0;
    } else if(battvolt<50) {  // Battery just low: *Red*
      setPixelRGB(0, 60, 00, 00);

    } else if(battvolt<100) {  // Battery OKish: *Orange*
      setPixelRGB(0, 60, 30, 00);
    } else {  //  Battery is fine! So: *Green*
      setPixelRGB(0, 00, 30, 00);
    }
    Serial.print(F(" M:"));
    Serial.print(controlmode,DEC);  // Why does this not display!?!?
    switch (controlmode){
      case 0:
        Serial.print(F(" RHx:"));
        Serial.print(PS3.getAnalogHat(RightHatX));
        Serial.print(F(" RHy: "));
        Serial.print(PS3.getAnalogHat(RightHatY));
        Serial.print(F(" LHy:"));
        Serial.print(PS3.getAnalogHat(LeftHatY));
        Serial.print(F(" LHx:"));
        Serial.print(PS3.getAnalogHat(LeftHatX));
        Serial.print(F(" L2:"));
        Serial.print(PS3.getAnalogButton(L2));
        Serial.print(F(" R2:"));
        Serial.print(PS3.getAnalogButton(R2));
        Serial.print(F("\t"));      
      break;
      case 1:
        Serial.print(F(" RHx:"));
        Serial.print(PS3.getAnalogHat(RightHatX));
        Serial.print(F("\tRHy: "));
        Serial.print(PS3.getAnalogHat(RightHatY));
      break;
      case 2:
        Serial.print(F(" LHy:"));
        Serial.print(PS3.getAnalogHat(LeftHatY));
        Serial.print(F(" RHx:"));
        Serial.print(PS3.getAnalogHat(RightHatX));
      break;
      case 3:
      break;
      case 4:
        Serial.print(F(" LHx:"));
        Serial.print(PS3.getAnalogHat(LeftHatX));
        Serial.print(F(" L2:"));
        Serial.print(PS3.getAnalogButton(L2));
        Serial.print(F(" R2:"));
        Serial.print(PS3.getAnalogButton(R2));        
      break;  
  }
    Serial.print(F("\t "));
  }

// Actually read in values from controller
// And bring things into pos/neg
  switch (controlmode){ 
    case 1: // Default: 1=Right stick. Cent off, X=Steer.  Y=Speed.
      dirval=PS3.getAnalogHat(RightHatX)-128;
      speedval=PS3.getAnalogHat(RightHatY)-128;
      // Invert axis if needed
      speedval = -speedval;
     // dirval =-dirval;
    break; 
    case 2: // 2=Genric 2 stick: left stick Y axis Throttle. Right stick X axis steering.
      dirval=PS3.getAnalogHat(RightHatX)-128;
      speedval=PS3.getAnalogHat(LeftHatY)-128;
      // Invert axis if needed
      speedval = -speedval;
     // dirval =-dirval;   
    break;
    case 3:  // 3=D-Pad
      if (PS3.getButtonClick(UP)) { }
      if (PS3.getButtonClick(RIGHT)) { }
      if (PS3.getButtonClick(DOWN)) { }
      if (PS3.getButtonClick(LEFT)) { }
      dirval=0;
      speedval=0;
    break; 
    case 4: // 4=Hayden: left backward, Right forward, left stick X axis steering.
      dirval=PS3.getAnalogHat(LeftHatX)-128;
      speedval=PS3.getAnalogButton(L2);
      speedval=speedval-PS3.getAnalogButton(R2);
      speedval = map(speedval, -255, 255, 128,-128 );
     // speedval = -speedval;
     // dirval =-dirval;    
    break;
  }
 
  // Dead zone, to reduce twitching.
  if(abs(dirval)<DEADZONE){
    dirval=0;
  }
  if(abs(speedval)<DEADZONE){
    speedval=0;
  }

//  dirval = map(dirval, 128, -128, 50,-50 );

  // What do we have in the regs?
  if(debugtimer<loopmills){
    Serial.print("D:"); Serial.print(dirval); Serial.print("\t");
    Serial.print("S:"); Serial.print(speedval); Serial.print("\t");
    Serial.print("W:"); Serial.print(weppval); Serial.print("\t");
    Serial.print("A:"); Serial.print(auxval); Serial.print("\t");
  }



switch (bottype){

  case 0:   // Test/debug
  break;


  case 1:  // Skidbot
/*
  // Byron's janky Two channels to diffrential
  if(speedval>1){    // Forward
    left_motor=speedval-dirval;
    right_motor=speedval+dirval;
  }
  else if(speedval<-1){  // Backward
    left_motor=speedval+dirval;
    right_motor=speedval-dirval; 
  }
  else{   // Stopped
    left_motor=0;
    right_motor=0;
  }  
*/

  //https://www.reddit.com/r/arduino/comments/49nltm/differential_steering_mixingformula/d0tdibk/
  // Much better diffrential, bodged to work.
  // Still not great at low-but-non-zero speeds
  /*
  if (speedval == 0 && dirval !=0) {
    left_motor = dirval;
    right_motor = -dirval;
  } else {
    left_motor = speedval * ((-128 - dirval) / -128.0);
    right_motor = speedval * ((128 - dirval) / 128.0);

    if (speedval > 0 && left_motor > speedval)
      left_motor = speedval;
    if (speedval > 0 && right_motor > speedval)
      right_motor = speedval;
    if (speedval < 0 && left_motor < speedval)
      left_motor = speedval;
    if (speedval < 0 && right_motor < speedval)
      right_motor = speedval;
  }
*/


  /* Kell example:   Scale tune to be good.  Offsets should be zero
  motor_r = scale*(forward - forward_offset + turn - turn_offset);
  motor_l = scale*(-forward + forward_offset + turn - turn_offset);
  */
  right_motor = (speedval  + dirval );
  left_motor = (-speedval + dirval );
break;
case 2:   // Steerbot - servo steering.
  left_motor = speedval;
  right_motor = dirval;
break;

}
    
    // Show current motor staus on LEDs
    motcurrmax=(abs(speedval)*1.55);    // Scale 0-128 to 0-255. I think.
    if(motormax>HALFPOWER){  // Orange
      if(motcurrmax>113){ // In 'danger zone'
        setPixelRGB(1, motcurrmax+10, 248-(motcurrmax*1.25), 00);
      } else {  // 'Boost' enabled but not in zone
        setPixelRGB(1, motcurrmax+10, motcurrmax+10, 00);
      }
    } else{  // Green only
      setPixelRGB(1, 00, motcurrmax+10, 0);
    }     

  // rescale from +/- 128 to +/- motormax, to allow for boost stuff
  left_motor = map(left_motor, 128, -128, motormax,-motormax);
  right_motor = map(right_motor, 128, -128, motormax,-motormax);

  // Lets see what is going to the motors
  if(debugtimer<loopmills){
    debugtimer=loopmills+DEBUGVALUE;  // Last 'debug if': reset timer
    Serial.print("LED:"); Serial.print(motcurrmax); Serial.print("\t");
    Serial.print("L:"); Serial.print(left_motor); Serial.print("\t");
    Serial.print("R:"); Serial.print(right_motor); Serial.print("\t");
    Serial.print("W:"); Serial.print(webb_motor); Serial.print("\t");
    Serial.print("A:"); Serial.print(aux_motor); Serial.print("\t");
    Serial.print("B:"); Serial.print(halfpowertimer); Serial.print("\n");
  }


// Different motor configs

switch (bottype){

  case 0:   // Test/debug
  break;
  case 1:  // Skidbot
    // Send to motors, 
    setmotor(0,left_motor);
    setmotor(1,right_motor);   // Oooh! This is why it was this way!
    // And servo(s)
    setservo(0,webb_motor);
    setservo(1,aux_motor);
  break;
  case 2:   // Steerbot
    // Send to motors, 
    setmotor(0,left_motor);  // Actually 'forward'
    setmotor(1,webb_motor);
    // And servo(s)
    setservo(0,right_motor);   // Odd but hey, this is how it's written
    setservo(1,aux_motor);
  break;
}



}  // loop() end


// Set H bridge motor output controls and PWM speeds.
// 255 = full forward.  -255 full reverse
void setmotor(char mot,int mot_speed){
  constrain(mot_speed,-255,255);   // Prevent values from going out of range
  // Left motor
  if(mot==0){   
    if(mot_speed>0){           // Forward
      digitalWrite(aIN1, LOW);
      digitalWrite(aIN2, HIGH);
      analogWrite(pwmA, abs(mot_speed));   // PWM output for motor speed
    } else if(mot_speed<0) {   // Backward
      digitalWrite(aIN1, HIGH);
      digitalWrite(aIN2, LOW);
      analogWrite(pwmA, abs(mot_speed));  // PWM output for motor speed
    } else {  // Break/stop
      digitalWrite(aIN1, LOW);
      digitalWrite(aIN2, LOW);
      analogWrite(pwmA, 0);
    }
  }
  // Left motor
  if(mot==1){           
    if(mot_speed>0){               // Forward
      digitalWrite(bIN1, LOW);
      digitalWrite(bIN2, HIGH);
      analogWrite(pwmB, abs(mot_speed));  // PWM output for motor speed
    } else if(mot_speed<0) {       // Backward
      digitalWrite(bIN1, HIGH);
      digitalWrite(bIN2, LOW);
      analogWrite(pwmB, abs(mot_speed));  // PWM output for motor speed
    } else {  // Break/stop
      digitalWrite(bIN1, LOW);
      digitalWrite(bIN2, LOW);
      analogWrite(pwmB, 0);
    }
  }
}



// Turn -128 128   range into servo output
void setservo(char mot,int servpos){
    constrain(servpos,-128,128);   // Prevent values from going out of range
//    servpos = map(servpos, -128, 128, 5,175 );  // And convert to servo range
    servpos = map(servpos, -128, 128, 50,130 );  // And convert to servo range

  if(mot==0){   
    servo_one.write(servpos);
  }
  if(mot==1){   
    servo_two.write(servpos);
  }

  
}
    
// Motor test function,
void dcmot_test(){
  
  //Mot one
  setPixelRGB(0, 00, 30, 00);  // Forward
  setmotor(0,128);
  delay(1000);
  setPixelRGB(0, 00, 00, 00); 
  setmotor(0,0);
  delay(500);
  setPixelRGB(0, 30, 00, 00);  //Backward
  setmotor(0,-128);
  delay(1000);
  setPixelRGB(0, 00, 00, 00); 
  setmotor(0,0);
  delay(1000);
   
  // Mot two
  setPixelRGB(1, 00, 30, 00);
  setmotor(1,128);
  delay(1000);
  setPixelRGB(1, 00, 00, 00); 
  setmotor(1,0);
  delay(500);
  setPixelRGB(1, 30, 00, 00);
  setmotor(1,-128);
  delay(1000);
  setPixelRGB(1, 00, 00, 00); 
  setmotor(1,0);

  delay(1000);   
}

// Sets RGB leds to current RGB value, regardless of what library
void setPixelRGB(int Pixel, byte red, byte green, byte blue) {
  
  // light
  cRGB value;
  value.b = blue; value.g = green; value.r = red; // RGB Value
  LED.set_crgb_at(Pixel, value); // Set value at LED found at index 0
  LED.sync(); // Sends the value to the LED
  
  // Neopixel
//   strip.setPixelColor(Pixel, strip.Color(red, green, blue));
//   neopixel.show();

}