// Test to adjust the K parameters at runtime and plot the setpoint and PID values.
// This simulates a 20W heater block driven by the PID
// Wokwi https://wokwi.com/projects/356437164264235009
#include "PID_RT.h"
// https://github.com/RobTillaart/PID_RT
constexpr byte PWM_PIN = 9;  // NANO PWM servo pin
int output_power = 0;
float input_temp = 0;
float Kp = 0.1;
float Ki = 0.1;
float Kd = 0.1;
constexpr byte numChars = 64;
char readCstring[numChars];
boolean finished_reading_serial = false;
PID_RT PID;
void setup()
{
  Serial.begin(115200);
  Serial.println(__FILE__);
  PID.setPoint(125);
  PID.setOutputRange(0, 255);  // PWM range
  PID.setInterval(50);
  PID.setK(0.1, 0.1, 0.1);
  PID.start();
}
void loop()
{
  check_and_read_1_char_serial();
  if (finished_reading_serial == true) {
    Serial.print("Recieved:");
    Serial.println(readCstring);
    update_variables_from_serial();
    update_PID();
    finished_reading_serial = false;
  }
  float heaterWatts = ((int)output_power)*20.0/255; // 20W heater
  float blockTemp = simPlant(heaterWatts); // simulate heating
  input_temp = blockTemp;
  //Kp=10;
  //PID.setKp(Kp);
  //PID.setKi(Ki);
  //PID.setKd(Kd);
  //PID.setK(2, 0, 0);
  if (PID.compute(input_temp))
  {
    output_power = PID.getOutput();
  }
  report();
}
void check_and_read_1_char_serial()
// Respeta lo que dice:
// https://forum.arduino.cc/t/serial-input-basics-updated/382007/2
// .
{
  static byte i = 0;
  char endMarker = '\n';      // endMarker = New Line (NL). En Matlab es el LF (line feed).
  char recievedChar;
  while (Serial.available() > 0 && finished_reading_serial == false) {
        recievedChar = Serial.read();
        if (recievedChar != endMarker) {
            readCstring[i] = recievedChar;
            i++;
            if (i >= numChars) {
                i = numChars - 1;
                Serial.println("Error, buffer overflow.");
            }
        }
        else {
            readCstring[i] = '\0'; // terminate the string
            i = 0;
            finished_reading_serial = true;
        }
  }
}
void update_variables_from_serial()
{
  if(readCstring[0] == 'K') {
    switch(readCstring[1]) {
      case 'p':
        Kp = atof(readCstring + 2);
        break;
      case 'i':
        Ki = atof(readCstring + 2);
        break;
      case 'd':
        Kd = atof(readCstring + 2);
        break;
      default:
        Serial.println("Error: not Kp, Ki, or Kd.");
        break;
    }
  }
  else {
    Serial.println("Error: no K.");
  }
  //finished_reading_serial = false;
}
void update_PID() {  
  PID.setK(Kp, Ki, Kd);
  Serial.print("Kp=");
  Serial.print(PID.getKp());
  Serial.print(" , Ki=");
  Serial.print(PID.getKi());
  Serial.print(" , Kd=");
  Serial.println(PID.getKd());
}
void report(void){
  static uint32_t last = 0;
  const int interval = 1000;
  if (millis() - last > interval){
    last += interval;
//    Serial.print(millis()/1000.0); 
    Serial.print("SP = ");
    Serial.print(PID.getSetPoint());
    Serial.print(", In temp = ");
    Serial.print(input_temp);
    Serial.print(", Out power = ");
    Serial.println(output_power);
  }
}
float simPlant(float Q){ // heat input in W (or J/s)
   // simulate a 1x1x2cm aluminum block with a heater and passive ambient cooling
   float C = 237; // W/mK thermal conduction coefficient for Al
   float h = 5 ; // W/m2K thermal convection coefficient for Al passive
   float Cps = 0.89; // J/g°C
   float area = 1e-4; // m2 area for convection
   float mass = 10 ; // g
   float Tamb = 25; // °C
   static float T = Tamb; // °C
   static uint32_t last = 0;
   uint32_t interval = 100; // ms
   if(millis() - last >= interval){
     last += interval;
     T = T + Q*interval/1000/mass/Cps - (T-Tamb)*area*h;
   }
   return T;
}