#include <math.h>


// Robot link lengths

const double L1 = 0;   // Vertical link length (distance between joints 1 and 2)

const double L2 = 10;  // Length of link 2

const double L3 = 10;  // Length of link 3


// Function for forward kinematics

void forward_kinematics(double theta1, double theta2, double theta3, double &x, double &y, double &z) {

    // Convert angles from degrees to radians

    theta1 = theta1 * M_PI / 180.0;

    theta2 = theta2 * M_PI / 180.0;

    theta3 = theta3 * M_PI / 180.0;


    // Compute the position of the end-effector

    x = (L2 * cos(theta2) + L3 * cos(theta2 + theta3)) * cos(theta1);

    y = (L2 * cos(theta2) + L3 * cos(theta2 + theta3)) * sin(theta1);

    z = L1 + L2 * sin(theta2) + L3 * sin(theta2 + theta3);

}


// Function for inverse kinematics

bool inverse_kinematics(double x, double y, double z, double &theta1, double &theta2, double &theta3) {

    // Solve for theta1 (rotation around the z-axis)

    theta1 = atan2(y, x);


    // Compute the horizontal distance to the wrist center

    double r = sqrt(x * x + y * y);

    double s = z - L1;


    // solve for theta3

    double D = (r * r + s * s - L2 * L2 - L3 * L3) / (2 * L2 * L3);


    if (fabs(D) > 1) {

        return false;  // The position is unreachable

    }


    theta3 = acos(D);


    // Solve for theta2 

    double phi1 = atan2(s, r);

    double phi2 = atan2(L3 * sin(theta3), L2 + L3 * cos(theta3));

    theta2 = phi1 - phi2;


    // Convert radians to degrees

    theta1 = theta1 * 180.0 / M_PI;

    theta2 = theta2 * 180.0 / M_PI;

    theta3 = theta3 * 180.0 / M_PI;


    return true;

}


void setup() {

    Serial.begin(115200);

    while (!Serial) {

        ; 

    }


    Serial.println("Enter '1' for forward kinematics or '2' for inverse kinematics:");

}


void loop() {

    if (Serial.available() > 0) {

        String input = Serial.readString();


        if (input.startsWith("1")) {

            // Forward kinematics: input joint angles, output x, y, z

            double theta1, theta2, theta3, x, y, z;


            Serial.println("Enter theta1 (base angle in degrees):");

            while (Serial.available() == 0);

            theta1 = Serial.parseFloat();

           

            Serial.println("Enter theta2 (shoulder angle in degrees):");

            while (Serial.available() == 0);

            theta2 = Serial.parseFloat();


            Serial.println("Enter theta3 (elbow angle in degrees):");

            while (Serial.available() == 0);

            theta3 = Serial.parseFloat();


            // Calculate forward kinematics

            forward_kinematics(theta1, theta2, theta3, x, y, z);

           

            Serial.print("End-effector position: x = ");

            Serial.print(x);

            Serial.print(", y = ");

            Serial.print(y);

            Serial.print(", z = ");

            Serial.println(z);

        } else if (input.startsWith("2")) {

            // Inverse kinematics: input x, y, z, output joint angles

            double x, y, z, theta1, theta2, theta3;


            // Prompt for end-effector position

            Serial.println("Enter x coordinate:");

            while (Serial.available() == 0);

            x = Serial.parseFloat();

           

            Serial.println("Enter y coordinate:");

            while (Serial.available() == 0);

            y = Serial.parseFloat();

           

            Serial.println("Enter z coordinate:");

            while (Serial.available() == 0);

            z = Serial.parseFloat();


            // Calculate inverse kinematics

            if (inverse_kinematics(x, y, z, theta1, theta2, theta3)) {

                // Display the result

                Serial.print("Joint angles: theta1 = ");

                Serial.print(theta1);

                Serial.print(" degrees, theta2 = ");

                Serial.print(theta2);

                Serial.print(" degrees, theta3 = ");

                Serial.print(theta3);

                Serial.println(" degrees");

            } else {

                Serial.println("The position is unreachable.");

            }

        } else {

            Serial.println("Invalid option. Please enter '1' or '2'.");

        }


        Serial.println("\nEnter '1' for forward kinematics or '2' for inverse kinematics:");

    }

}