#include <Servo.h>
#include <math.h>

// Deklarasi servo motor
Servo servo1;
Servo servo2;

// Panjang link
const float L1 = 10.0; // Panjang link 1 dalam cm
const float L2 = 10.0; // Panjang link 2 dalam cm

// Fungsi untuk menghitung Jacobian
void calculateJacobian(float theta1, float theta2, float &j11, float &j12, float &j21, float &j22) {
  j11 = -L1 * sin(theta1) - L2 * sin(theta1 + theta2);
  j12 = -L2 * sin(theta1 + theta2);
  j21 = L1 * cos(theta1) + L2 * cos(theta1 + theta2);
  j22 = L2 * cos(theta1 + theta2);

Serial.print(j11); Serial.print(" ");
Serial.print(j12); Serial.print(" ");
Serial.print(j21); Serial.print(" ");
Serial.println(j22);


}

// Fungsi untuk menghitung invers Jacobian
bool calculateInverseJacobian(float j11, float j12, float j21, float j22, float &ij11, float &ij12, float &ij21, float &ij22) {
  float det = j11 * j22 - j12 * j21;
  if (fabs(det) < 1e-6) return false; // Cek untuk singularitas

  ij11 = j22 / det;
  ij12 = -j12 / det;
  ij21 = -j21 / det;
  ij22 = j11 / det;
  return true;
}

// Fungsi untuk menghitung kinematika balik
void inverseKinematics(float x, float y, float &theta1, float &theta2) {
  // Inisialisasi sudut sendi
  theta1 = 0.0001;
  theta2 = 0.0001;

  // Tentukan parameter iterasi
  const int maxIter = 100;
  const float tol = 0.01;
  
  for (int i = 0; i < maxIter; i++) {
    // Hitung posisi ujung efektor

  Serial.print(i);
  Serial.print("] ");
  Serial.print("Theta1:");
  Serial.print(theta1*180/PI); // Konversi ke derajat
  Serial.print(" Theta2:");
  Serial.print(theta2*180/PI); // Konversi ke derajat


    float xe = L1 * cos(theta1) + L2 * cos(theta1 + theta2);
    float ye = L1 * sin(theta1) + L2 * sin(theta1 + theta2);

  Serial.print(" X:");
  Serial.print(xe); // Konversi ke derajat
  Serial.print(" Y:");
  Serial.println(ye); // Konversi ke derajat

    // Hitung error
    float ex = x - xe;
    float ey = y - ye;
    
    if (sqrt(ex * ex + ey * ey) < tol) break; // Cek konvergensi

    // Hitung Jacobian
    float j11, j12, j21, j22;
    calculateJacobian(theta1, theta2, j11, j12, j21, j22);

Serial.print(j11); Serial.print(" ");
Serial.print(j12); Serial.print(" ");
Serial.print(j21); Serial.print(" ");
Serial.println(j22);

    // Hitung invers Jacobian
    float ij11, ij12, ij21, ij22;
    if (!calculateInverseJacobian(j11, j12, j21, j22, ij11, ij12, ij21, ij22)) {
      Serial.println("Singular");
      break; // Jika Jacobian singular
    }
    // Hitung perubahan sudut sendi
    float dtheta1 = ij11 * ex + ij12 * ey;
    float dtheta2 = ij21 * ex + ij22 * ey;

    // Perbarui sudut sendi
    theta1 += dtheta1; 
    theta2 += dtheta2;
    theta1 = fmod(theta1, TWO_PI);
    theta2 = fmod(theta2, TWO_PI);
  }

  // Konversi sudut dari radian ke derajat
  theta1 = theta1 * 180.0 / PI;
  theta2 = theta2 * 180.0 / PI;
}

void setup() {
  Serial.begin(9600);
  // Menetapkan pin servo
  servo1.attach(9);
  servo2.attach(10);
}

void loop() {
  // Posisi yang diinginkan untuk ujung efektor
  float x = 20.0; // cm
  float y = 0.0; // cm

  // Variabel untuk menyimpan sudut sendi
  float theta1, theta2;
  
  // Menghitung sudut sendi
  inverseKinematics(x, y, theta1, theta2);

  // Cetak hasil
  Serial.println();
  Serial.println("Hasil Akhir");
  Serial.print("Theta1:");
  Serial.print(theta1); // Konversi ke derajat
  Serial.print(" Theta2:");
  Serial.println(theta2); // Konversi ke derajat
  Serial.println();

  // Menggerakkan servo ke sudut yang dihitung
  servo1.write(theta1);
  servo2.write(theta2);
  
  // Menunggu sejenak untuk melihat pergerakan
  while(1);
  delay(2000);
}
uno:A5.2
uno:A4.2
uno:AREF
uno:GND.1
uno:13
uno:12
uno:11
uno:10
uno:9
uno:8
uno:7
uno:6
uno:5
uno:4
uno:3
uno:2
uno:1
uno:0
uno:IOREF
uno:RESET
uno:3.3V
uno:5V
uno:GND.2
uno:GND.3
uno:VIN
uno:A0
uno:A1
uno:A2
uno:A3
uno:A4
uno:A5