/* 
--------------------------------------------  
-------- MECH1010 Coursework 2024 --------
-------- Name: Oluseye Arinoso
-------- Username:  201695440
--------------------------------------------
*/
// setting up pins
const int green_LED = 2;
const int yellow_LED = 3;
const int red_LED = 4;
const int direction_pin = 9;
const int enable_pin = 10; 
const int potentiometer = A0;

// setting up variables
float p_val = 0;
const float support_rod = 0.491;
float h_angle = 0 ;

const int ctrl_freq = 25;
 float ctrl_dt =  1000 * (1 / (float) ctrl_freq); // doesn't actually equal 40 for some reason

const int uncertainty = 20;
const float target_height = 0.05;

const int start_speed = 127; // high start speed to get the helicopter moving off the ground 
int motor_speed = 0;


float prev_error = 0;
float integralval = 0;

float Kp =600;
const float Ki = -0.05;
// const float Kd = 2;

const int size = ctrl_freq * 5; // max size of array created in code as readings are 25hz for 5 seconds

int counter = 0;
float target_time = 0;

float sum_errors= 0;

float MaxV= 0;
float max_post_error = 0 ;

int ting = 0;

void setup() {
  delay(500);
  Serial.begin(9600);
  pinMode(green_LED, OUTPUT);
  pinMode(red_LED, OUTPUT);
  pinMode(yellow_LED, OUTPUT);
  pinMode(potentiometer, INPUT);
  pinMode(direction_pin, OUTPUT);
  pinMode(enable_pin, OUTPUT);
  
  Serial.println("System started");
  // Lighting LEDs to max brightness for 0.5 secondss 
  analogWrite(green_LED, 255);
  analogWrite(red_LED, 255);
  analogWrite(yellow_LED, 255); 
  delay(500); 
  analogWrite(green_LED, 0);
  analogWrite(red_LED, 0);
  analogWrite(yellow_LED, 0); 

  motor_speed = start_speed;

  analogWrite(enable_pin, motor_speed); 
  Serial.println("System initiated");
  
}



void loop() {

Serial.println("Controller starting");
Serial.println("Time , Angle , Height , Error");

// reading start of whole control system
unsigned long start_time = millis();
unsigned long current_time = millis();
do{  
  
  // reading time to run loop at 25Hz
unsigned long loop_start = millis();  
unsigned long time = loop_start - start_time;

// reading angle 

 //calc height

 float height = getHeight(potentiometer, support_rod);
 
// control system
 
 float error = target_height - height;
 float mm_error = error *1000;
 
 integralval = controller(error, prev_error, enable_pin, motor_speed, Kp, Ki, ctrl_dt, integralval );

   
  float loop_time = (float) time/1000;
  Serial.print(loop_time,  2);
  Serial.print(",");
  Serial.print(h_angle), 2;
  Serial.print(",");
  Serial.print(height, 2);
  Serial.print(",");
  Serial.println(error, 2);
  

  // Finds max error 
  if(abs(error) > MaxV){
    MaxV = abs(error);
  }
  

  if( mm_error <= uncertainty && mm_error >= - uncertainty){ // in the 20mm limits set for the helicopter height
    analogWrite(green_LED, 255);
    analogWrite(red_LED, 0);
    analogWrite(yellow_LED, 0);

// Chnage the Kp value in here so that when it is close to Set point and error is smaller controller can still suitably change values
// Kp =20;

    if(counter == 0 ){ //checks that it is the first time target height is reached
     target_time = loop_time;
     
         max_post_error = abs(error); 
       sum_errors = sum_errors +abs(error);
     counter = counter + 1;
     
  }

  }
  else if(mm_error < - uncertainty ){
    analogWrite(yellow_LED, 255);
    analogWrite(red_LED, 0);
    analogWrite(green_LED, 0);
    // digital write used as direction only has 2 options, we only want up or down
    //digitalWrite(direction_pin, HIGH);
  //  Kp = 0.6;
  }
  else{
    analogWrite(red_LED, 255);
    analogWrite(green_LED, 0);
    analogWrite(yellow_LED, 0);
    // Kp = 2; // this Kp is bigger as there is more of a problem with other shooting the values, so when overshooting we want the controller to be more reactive
  }
  
  
  
  // IF counter is more than 0 it means target height hsa been reached 
  if(counter > 0 ){ 
    // find max error after target height is reached
    if(abs(error) > max_post_error){
    max_post_error = abs(error);
    
    
   
  }
   // sums all errors after target height is reached
    sum_errors = sum_errors +abs(error);
     counter = counter + 1; // counts through so we know how many data points there are after target height
  };


  // if(motor_speed > 215){
  //   ting = ting + 1;
  // }
  // else if (ting < 215){
  //   ting = 0;
  // }

  prev_error = error;
  
//This code makes sure loop is 25hz 
   unsigned long current_loop_time = millis();
   while (current_loop_time - loop_start < ctrl_dt) {
       current_loop_time = millis();
   }
  current_time = millis();
}while(current_time - start_time < 5000); // stops control system after 5 seconds
// switching all LEDs off briefly after control loop
  analogWrite(green_LED, 0);
  analogWrite(red_LED, 0);
  analogWrite(yellow_LED, 0); 


// Lighting LEDs to max brightness for 1 secondss 
  analogWrite(green_LED, 255);
  analogWrite(red_LED, 255);
  analogWrite(yellow_LED, 255); 
  delay(1000); 
  analogWrite(green_LED, 0);
  analogWrite(red_LED, 0);
  analogWrite(yellow_LED, 0); 

  //switching motor off 
  analogWrite(enable_pin, 0); 
   
   Serial.print("Max Error: ");
   Serial.println(MaxV, 2);
   
   
 
   float average_error= sum_errors/ counter; // counter is how many times target height is reached

  if(counter > 0){
   Serial.print("Time taken to reach target height = ");
   Serial.println(target_time, 2);
   Serial.print("Max error after target reached = ");
   Serial.println(max_post_error, 2);
   Serial.print("Average error after target reached = ");
   Serial.println(average_error, 2);
  }
  else{
    Serial.println("Target height was not reached");
  }
  
  Serial.println("Shutdown");
  
//making sure loop doesn't restart
delay(10000);
}


// function to get heigh of helicopter/drone
float getHeight(float potentiometerpin, float armLength){
  // reads potentiometer value then maps it to angle in radians
  float p_val = analogRead(potentiometerpin);
   h_angle  = map(p_val, 212,410, -68.5, 0);
  //converts angle from degrees to radians
  float rad = (h_angle * PI)/180;

  float helicopter_height = armLength*sin(rad);
 // helicopter_height = helicopter_height *1000; // to get height in mm
  return helicopter_height; 
}

//control system function
float controller(float controlError, float previousError, float output_pin, float output_speed, float KP, float KI, float T, float prevint){
  
  float intergral = (((KI*T)/2)*(controlError + previousError)) + prevint; // p.s. I know i spelt integral wrong it's too late
 Serial.println(intergral);
  
  //Adds to oirignal speed as when target height is reached and error is zero out speed stays the same
  float new_speed = (float) output_speed + (controlError * KP);
  
  
  if(new_speed > 255){ // This if structure used because helicopter blades don't work properly if PWM > 255 and if above propeller speed is too fast it will overshoot the target height by a lot
    new_speed = 255;
  }
  else if(new_speed < 0){
    new_speed = 0;
  }

  // intergrator limit so integral doesn't get too low or too high 
  if(intergral < -48){
    intergral = -48;
  }
  else if (intergral > 50){
    intergral = 50;
  }

  new_speed = new_speed + intergral;

  // if(ting > 50){   // to slow down accent  
  //   new_speed = new_speed - 10;
  // }
 
  Serial.println(new_speed);
  analogWrite(enable_pin, new_speed);
  //
  return intergral;
  //after reading schoalrs compass i realised for our control system we do not really want a derivative term as
  // retuning to set point from overshoot is not difficult or strictly undersiable
  
}

//finds max value of an array
float maxVal(float Array[], int sizeofarray){
  float Max = 0;
    
   for (int i = 0;i < sizeofarray ; i++) {
      if ((abs(Array[i])) > Max) {
         Max = Array[i];
      } 
      Serial.print("Array i : ");
      Serial.println(Array[i]);
      Serial.print("Max value : ");
      Serial.println(Max);
   }
   return Max;
}
74HC595
74HC595