/*
Program     : Percobaan Observasi Plant menggunakan Arduino Mega
DiBuat Oleh : EruP-RMK
Keluaran    : v1.00 / v2.00 / v3.00
Tanggal     : 12 September 2018 - 8 Agustus 2020 - 19 Juni 2023 - 6 Agustus 2023
Untuk       : Praktikum Kontrol Cerdas 2 POLITEKNIK ELEKTRONIKA NEGERI SURABAYA (PENS)

Perangkat   : Arduino Mega, Plant Emulasi, Koneksi ADC Emulasi dan Pin Port I/O
              (sebagai PWM) Emulasi
*/

float SettingPoint=0;     // Setting Point
float Kecepatan=0;        // Kecepatan Plant (Motor) saat ini
float PWM=0;              // nilai PWM yang dikeluarkan 8 bit (0-255)
float adc=0;              // nilai ADC hasil pembacaan sensor 10 bit (0-1023)

const int MOTOR=3;        // Nomor PIN untuk output PWM (emulasi PWM oleh Arduino)
const int SENSOR=A0;      // Nomot PIN ADC

float TmMotor=1;          // Motor naik dari 0% sampai 63% selama berapa detik
                          // Ganti nilai Tm dengan NomorUrut/10

float out_plant=0;        // Keluaran dari plant, dalam 0rpm sampai RpmMax
float oMotor=0;           // Keluaran Motor saat ini (0 rpm sampai RpmMax)

float BebanMotor=0;       // Pembebanan motor, 0% sampai 100%
float TeganganMotorMax=5; // Tegangan dari Motor, dianggap Motor 5V
float KecepatanMax=3000;  // Kecepatan tertinggi dari Sistem
float RpmMax=2000;        // Kecepatan tertinggi Diaplay
float RpmMin=0;           // Kecepatan terrendah Diaplay

#define PeriodeSampling 20  // waktu sampling 20ms

unsigned long t1, t2, S;    // menghitung interval antar sampling
                            // untuk perhitungan plant
unsigned long t_lalu;       // waktu sampling sebelumnya

float Lineritas(float x)    // Menghitung ketidak-linearan Km dari plant berdasarkan input
{                           // hasil dari regresi Polinomial pengujian Km plant real
  // y=-0.4541*I2^6-15.024*I2^5+49.092*I2^4-54.251*I2^3+21.936*I2^2-0.6329*I2+0.0057
  if(x < 0) x = 0;
  if( x > 1) x = 1;
  return -0.4541*pow(x,6)-15.024*pow(x,5)+49.092*pow(x,4)-54.251*pow(x,3)+21.936*x*x
         -0.6329*x+0.0057;
}

float plant(float Tegangan)         // Emulasi plant secara software
{                                   // Menggunakan pendekatan pengisian Kapasitor
                                    // Dengan formula Km Tm (lihat teori)
  t2=millis();                      // berapa waktu sekarang ? Dalam satuan ms
  S=t2-t1;                          // Hitung interval antar sampling (t1 & t2)
  float tSampling=float(S)/1000.0;  // Interval untuk menghitung waktu Sampling
  t1=t2;                            // untuk perhitungan berikutnya
  
  float KmMotor=(1-BebanMotor/100)                  // Km Plant akibat pembebanan
                *KecepatanMax/TeganganMotorMax;     // dan perbandingan rpm/volt plant
  float V=Tegangan/TeganganMotorMax;                // Perbandingan Vin dari Vin Max
  float KmLineritas=Lineritas(V);                   // Hitung Ketidak-linearan Km plant
  float iMotor=Tegangan*KmMotor*KmLineritas;        // Hitung rpm yang masuk plant
  oMotor=oMotor+(iMotor-oMotor)*tSampling/TmMotor;  // Hitung rpm keluaran plant
  if( oMotor < 0) oMotor = 0;                       // plant tidak bisa mundur      
  return oMotor;
}

int _analogRead(int ch)               // ADC Emulasi, input dari Tachometer, 0v - 5v
{                                     // sesuai dengan 0rpm sampai KecepatanMax Plant
  float Vi=out_plant*5/KecepatanMax;  // Sensor Tachometer, dari rmp ke Vi (0v-5v)
  return Vi*1023/5;                   // ADC 10 bit, Referensi 5v resolusi 10 bit
}                                     // Input 0v-5v diubah menjadi 0-1023

void _analogWrite(int pin, int PWM)   // DAC Emulasi, output ke Driver Plant
{                                     // Tegangan 0v-5v
  float Vo=PWM*5.0/255.0;             // dari PWM (0-255) diubah ke Vo (0v-5v)
  out_plant=plant(Vo);                // dari Vo (0v-5v) masuk ke Plant menjadi rpm
}

void InputOutput()                    // Proses utama dari sistem,
{                                     // hanya input output untuk observasi Plant
  adc=_analogRead(SENSOR);            // Baca tegangan sensor menggunakan ADC 
                                      // sebanding dengan kecepatan/keluaran Plant
                                      // dari Tachometer 0-MaxRpm menjadi Vin ADC 0v-5v
                                      // keluaran ADC 0-1023 (ADC 10 bit VRef 5v)
  Kecepatan=adc*KecepatanMax/1023;    // Konversi dari biner ADC ke Kecepatan rpm
  PWM=SettingPoint*255/KecepatanMax;  // Tentukan nilai PWM (data biner) berdasarkan
                                      // Setting Point yang telah ditentukan
  if(PWM>255) PWM=255;                // Nilai PWM Maksimal 255
  if(PWM<0) PWM=0;                    // Nilai PWM Minimal 0
  _analogWrite(MOTOR,PWM);            // Kirim ke PLANT melalui PORT I/O PWM emulasi
                                      // dalam bentuk program dalam Arduino)
  analogWrite(LED_BUILTIN,PWM);       // tampilkan juga dalam bentuk nyala LED,
                                      // untuk mengetahui besarnya sinyal PWM
}

float cut(float Data)           // Membatasi tampilan pada Plotter
{                               // harus diantara RpmMin dan RpmMax
  if(Data>RpmMax) Data=RpmMax;  // Jika melebihi RpmMax, batasi
  if(Data<RpmMin) Data=RpmMin;  // atau kurang dari RpmMin, batasi
  return Data;  
}

void Tampilkan()                  // Untuk mengirimkan data-data sistem ke PC
{                                 // melalui komunikasi Serial
  //Serial.print("SetPoint Kecepatan PWM Min Max WaktuSampling=");
  //Serial.print(S);
  //Serial.println("ms");
  
  Serial.print(SettingPoint,0);   // Kirim data ke program monitor
                                  // melalui komunikasi serial
  Serial.write(' ');              // tiap data harus dipisah dengan spasi " "
  Serial.print(cut(Kecepatan),0); // Kirim data Kecepatan (bilangan bulat)
  Serial.write(' ');              // tiap data harus dipisah dengan spasi " "
  Serial.print(PWM,0);            // tanda 0 artinya tidak ada nilai pecahan
  Serial.write(' ');              
  Serial.print(RpmMin);           // Berikan nilai Min dan Max
  Serial.write(' ');              // agar skala grafik tetap tidak berubah
  Serial.println(RpmMax);         // dan akhiri dengan karakter <LF>
}

void BacaSerial()                     // Menerima data perintah dari PC
{                                     // melalui komunikasi serial
  if(Serial.available())              // Apakah ada penerimaan data serial dari PC ?
  {
    if(Serial.read()=='s')            // Jika Ya, apakah dimulai huruf depan 's' ?
    {
      SettingPoint=Serial.parseInt(); // Jika Ya, baca data integer berikutnya
      if(SettingPoint>RpmMax)
        SettingPoint=RpmMax;          // Tidak boleh melebihi rpm Max
    }                                 // dan simpan sebagai Setting Point
  }                                   // Misal data yang diterima, "s1000<CR>"
                                      // Set Carriage Return pada terminal,
                                      // agar saat enter, lansung dikirimkan CR
}

void setup()                                // Bagian yang pertama dikerjakan oleh 
{                                           // Arduino, hanya satu kali
  Serial.begin(115200);                     // sesuaikan dengan COM: serial pada PC
  Serial.println("SetPoint Kecepatan PWM"); // Cetak Label (Legenda)
  t_lalu=t1=millis();                       // catat waktu mulai,
                                            // untuk perhitungan Periode Sampling
}

void loop()                     // Bagian yang dikerjakan secara
{                               // terus-menerus oleh Arduino
  BacaSerial();                 // Baca perintah serial dari PC
  unsigned long t=millis();     // Baca waktu saat ini, dalam ms
  if(t-t_lalu>=PeriodeSampling) // Apakah dengan waktu sebelumnya
  {                             // sudah terpaut 1 periode sampling ?
    InputOutput();              // Jika ya, lakukan operasi sampling
                                // untuk menjalankan kontrol
    Tampilkan();                // Tampilkan data-data sistem ke PC
                                // melalui komunikasi serial
    t_lalu=t;                   // dan catat waktu sekarang sebagai waktu lalu,
                                // untuk perhitungan 1 sampling berikutnya
  }
}
mega:SCL
mega:SDA
mega:AREF
mega:GND.1
mega:13
mega:12
mega:11
mega:10
mega:9
mega:8
mega:7
mega:6
mega:5
mega:4
mega:3
mega:2
mega:1
mega:0
mega:14
mega:15
mega:16
mega:17
mega:18
mega:19
mega:20
mega:21
mega:5V.1
mega:5V.2
mega:22
mega:23
mega:24
mega:25
mega:26
mega:27
mega:28
mega:29
mega:30
mega:31
mega:32
mega:33
mega:34
mega:35
mega:36
mega:37
mega:38
mega:39
mega:40
mega:41
mega:42
mega:43
mega:44
mega:45
mega:46
mega:47
mega:48
mega:49
mega:50
mega:51
mega:52
mega:53
mega:GND.4
mega:GND.5
mega:IOREF
mega:RESET
mega:3.3V
mega:5V
mega:GND.2
mega:GND.3
mega:VIN
mega:A0
mega:A1
mega:A2
mega:A3
mega:A4
mega:A5
mega:A6
mega:A7
mega:A8
mega:A9
mega:A10
mega:A11
mega:A12
mega:A13
mega:A14
mega:A15