#include "RoArm-M2_config.h"
#include <math.h>
#define ANG2DEG 0.017453292

// Instantiate a servo control object.
SMS_STS st;

// place holder.
void serialCtrl();

// Used to store the feedback information from the servo.
struct ServoFeedback {
  bool status;
  int pos;
  int speed;
  int load;
  float voltage;
  float current;
  float temper;
  int Torquestatus;
  byte mode;
};

ServoFeedback servoFeedback[5];
// [0] BASE_SERVO_ID
// [1] SHOULDER_DRIVING_SERVO_ID
// [2] SHOULDER_DRIVEN_SERVO_ID
// [3] ELBOW_SERVO_ID
// [4] GRIPPER_SERVO_ID



// input the angle in radians, and it returns the number of servo steps.
double calculatePosByRad(double radInput) {
  return round((radInput / (2 * M_PI)) * ARM_SERVO_POS_RANGE);
}

// input the number of servo steps and the joint name
// return the joint angle in radians.
double calculateRadByFeedback(int inputSteps, int jointName) {
  double getRad;
  switch(jointName){
  case BASE_JOINT:
    getRad = -(inputSteps * 2 * M_PI / ARM_SERVO_POS_RANGE) + M_PI;
    break;
  case SHOULDER_JOINT:
    getRad = (inputSteps * 2 * M_PI / ARM_SERVO_POS_RANGE) - M_PI;
    break;
  case ELBOW_JOINT:
    getRad = (inputSteps * 2 * M_PI / ARM_SERVO_POS_RANGE) - (M_PI / 2);
    break;
  case EOAT_JOINT:
    getRad = inputSteps * 2 * M_PI / ARM_SERVO_POS_RANGE;
    break;
  }
  return getRad;
}


// input the ID of the servo,
// and get the information saved in servoFeedback[5].
// returnType: false - return everything.
//              true - return only when failed.
bool getFeedback(byte servoID, bool returnType) {
  if(st.FeedBack(servoID)!=-1) {
    servoFeedback[servoID - 11].status = true;
  	servoFeedback[servoID - 11].pos = st.ReadPos(-1);
    servoFeedback[servoID - 11].speed = st.ReadSpeed(-1);
    servoFeedback[servoID - 11].load = st.ReadLoad(-1);
    servoFeedback[servoID - 11].voltage = st.ReadVoltage(-1);
    servoFeedback[servoID - 11].current = st.ReadCurrent(-1);
    servoFeedback[servoID - 11].temper = st.ReadTemper(-1);
    servoFeedback[servoID - 11].mode = st.ReadMode(servoID);
    servoFeedback[servoID - 11].Torquestatus=st.readByte(servoID,SMS_STS_TORQUE_ENABLE);
    if(!returnType){
      if(InfoPrint == 1){Serial.print("Servo ID:");Serial.print(servoID);
                    Serial.print(" status: checked. pos:");
                    Serial.println(servoFeedback[servoID - 11].pos);}
    }
    else{
      return true;
    }
    return true;
  } else{
    servoFeedback[servoID - 11].Torquestatus=0;
    servoFeedback[servoID - 11].status = false;
    if(InfoPrint == 1){Serial.print("Servo ID:");Serial.print(servoID);
                  Serial.println(" status: failed.");}
  	return false;
  }
}


// input the old servo ID and the new ID you want it to change to.
bool changeID(byte oldID, byte newID) {
  if(!getFeedback(oldID, true)) {
    if(InfoPrint == 1) {Serial.print("change: ");Serial.print(oldID);Serial.println("failed");}
    return false;
  }
  else {
    st.unLockEprom(oldID);
    st.writeByte(oldID, SMS_STS_ID, newID);
    st.LockEprom(newID);

    if(InfoPrint == 1) {Serial.print("change: ");Serial.print(oldID);Serial.println("succeed");}
    return true;
  }
}


// ctrl the torque lock of a servo.
// input the servo ID and command: 1-on : produce torque.
//                                 0-off: release torque.
void servoTorqueCtrl(byte servoID, u8 enableCMD){
  st.EnableTorque(servoID, enableCMD);
}


// set the current position as the middle position of the servo.
// input the ID of the servo that you wannna set middle position. 
void setMiddlePos(byte InputID){
  st.CalibrationOfs(InputID);
}


// to release all servos' torque for 10s.
void emergencyStopProcessing() {
  st.EnableTorque(254, 0);
  delay(10000);
  st.EnableTorque(254, 1);
}


// position check.
// it will wait for the servo to move to the goal position.
void waitMove2Goal(byte InputID, s16 goalPosition, s16 offSet){
  while(servoFeedback[InputID - 11].pos < goalPosition - offSet || 
        servoFeedback[InputID - 11].pos > goalPosition + offSet){
    if (!servoFeedback[InputID - 11].status) {
      servoTorqueCtrl(254, 0);
      break;
    }
    getFeedback(InputID, true);
    delay(10);
  }
}


// initialize bus servo libraris and uart2ttl.
void RoArmM2_servoInit(){
  Serial1.begin(1000000, SERIAL_8N1, S_RXD, S_TXD);
  st.pSerial = &Serial1;
  while(!Serial1) {}
  if(InfoPrint == 1){Serial.println("ServoCtrl init succeed.");}
}


// check the status of every servo,
// if all status are ok, set the RoArmM2_initCheckSucceed as 1.
// 0: used to init check, print everything.
// 1: used to check while working, print when failed.
void RoArmM2_initCheck(bool returnType) {
  RoArmM2_initCheckSucceed = false;
  RoArmM2_initCheckSucceed = getFeedback(BASE_SERVO_ID, true) &&
                             getFeedback(SHOULDER_DRIVING_SERVO_ID, true) &&
                             getFeedback(SHOULDER_DRIVEN_SERVO_ID, true) &&
                             getFeedback(ELBOW_SERVO_ID, true);
  if(!returnType){
    if(InfoPrint == 1 || RoArmM2_initCheckSucceed){Serial.println("All bus servos status checked.");}
    else if(InfoPrint == 1 || !RoArmM2_initCheckSucceed){Serial.println("Bus servos status check: failed.");}
  }
  else if(returnType && RoArmM2_initCheckSucceed){}
  else if(returnType && !RoArmM2_initCheckSucceed){
    if(InfoPrint == 1){Serial.println("Check failed.");}
  }
}


// set all servos PID as the RoArm-M2 settings.
bool setServosPID(byte InputID, byte InputP) {
  if(!getFeedback(InputID, true)){return false;}
  st.unLockEprom(InputID);
  st.writeByte(InputID, ST_PID_P_ADDR, InputP); 
  st.LockEprom(InputID);
  return true;
}


// move every joint to its init position.
// it moves only when RoArmM2_initCheckSucceed is 1.
void RoArmM2_moveInit() {
  if(!RoArmM2_initCheckSucceed){
    if(InfoPrint == 1){Serial.println("Init failed, skip moveInit.");}
    return;
  }
  else if(InfoPrint == 1){Serial.println("Stop moving to initPos.");}

  // move BASE_SERVO to middle position.
  if(InfoPrint == 1){Serial.println("Moving BASE_JOINT to initPos.");}
  st.WritePosEx(BASE_SERVO_ID, ARM_SERVO_MIDDLE_POS, ARM_SERVO_INIT_SPEED, ARM_SERVO_INIT_ACC);

  // release SHOULDER_DRIVEN_SERVO torque.
  if(InfoPrint == 1){Serial.println("Unlock the torque of SHOULDER_DRIVEN_SERVO.");}
  servoTorqueCtrl(SHOULDER_DRIVEN_SERVO_ID, 0);

  // move SHOULDER_DRIVING_SERVO to middle position.
  if(InfoPrint == 1){Serial.println("Moving SHOULDER_JOINT to initPos.");}
  st.WritePosEx(SHOULDER_DRIVING_SERVO_ID, ARM_SERVO_MIDDLE_POS, ARM_SERVO_INIT_SPEED, ARM_SERVO_INIT_ACC);

  // check SHOULDER_DRIVEING_SERVO position.
  if(InfoPrint == 1){Serial.println("...");}
  waitMove2Goal(SHOULDER_DRIVING_SERVO_ID, ARM_SERVO_MIDDLE_POS, 120);

  // wait for the jitter to go away.
  delay(1200);

  // set the position as the middle of the SHOULDER_DRIVEN_SERVO.
  if(InfoPrint == 1){Serial.println("Set this pos as the middle pos for SHOULDER_DRIVEN_SERVO.");}
  setMiddlePos(SHOULDER_DRIVEN_SERVO_ID);

  // SHOULDER_DRIVEN_SERVO starts producing torque.
  if(InfoPrint == 1){Serial.println("SHOULDER_DRIVEN_SERVO starts producing torque.");}
  servoTorqueCtrl(SHOULDER_DRIVEN_SERVO_ID, 1);
  delay(10);

  // move ELBOW_SERVO to middle position.
  if(InfoPrint == 1){Serial.println("Moving ELBOW_SERVO to middle position.");}
  st.WritePosEx(ELBOW_SERVO_ID, ARM_SERVO_MIDDLE_POS, ARM_SERVO_INIT_SPEED, ARM_SERVO_INIT_ACC);
  waitMove2Goal(ELBOW_SERVO_ID, ARM_SERVO_MIDDLE_POS, 80);

  if(InfoPrint == 1){Serial.println("Moving GRIPPER_SERVO to middle position.");}
  st.WritePosEx(GRIPPER_SERVO_ID, ARM_SERVO_MIDDLE_POS, ARM_SERVO_INIT_SPEED, ARM_SERVO_INIT_ACC);

  delay(1000);
}


// // // single joint ctrl for simple uses, base on radInput // // //

// use this function to compute the servo position to ctrl base joint.
// returnType 0: only returns the base joint servo position and save it to goalPos[0],
//               servo will NOT move.
//            1: returns the base joint servo position and save it to goalPos[0],
//               servo moves.
// input the angle in radius(double), the speedInput(u16) is servo steps/second,
// the accInput(u8) is the acceleration of the servo movement.
// radInput increase, move to left.
int RoArmM2_baseJointCtrlRad(byte returnType, double radInput, u16 speedInput, u8 accInput) {
  radInput = -constrain(radInput, -M_PI, M_PI);
  s16 computePos = calculatePosByRad(radInput) + ARM_SERVO_MIDDLE_POS;
  goalPos[0] = computePos;

  if(returnType){
    st.WritePosEx(BASE_SERVO_ID, goalPos[0], speedInput, accInput);
  }
  return goalPos[0];
}


// use this function to compute the servo position to ctrl shoudlder joint.
// returnType 0: only returns the shoulder joint servo position and save it to goalPos[1] and goalPos[2],
//               servo will NOT move.
//            1: returns the shoulder joint servo position and save it to goalPos[1] and goalPos[2],
//               servo moves.
// input the angle in radius(double), the speedInput(u16) is servo steps/second,
// the accInput(u8) is the acceleration of the servo movement.
// radInput increase, it leans forward.
int RoArmM2_shoulderJointCtrlRad(byte returnType, double radInput, u16 speedInput, u8 accInput) {
  radInput = constrain(radInput, -M_PI/2, M_PI/2);
  s16 computePos = calculatePosByRad(radInput);
  goalPos[1] = ARM_SERVO_MIDDLE_POS + computePos;
  goalPos[2] = ARM_SERVO_MIDDLE_POS - computePos;
  
  if(returnType == 1){
    st.WritePosEx(SHOULDER_DRIVING_SERVO_ID, goalPos[1], speedInput, accInput);
    st.WritePosEx(SHOULDER_DRIVEN_SERVO_ID, goalPos[2], speedInput, accInput);
  }
  else if(returnType == SHOULDER_DRIVING_SERVO_ID){
    return goalPos[1];
  }
  else if(returnType == SHOULDER_DRIVEN_SERVO_ID){
    return goalPos[2];
  }
}


// use this function to compute the servo position to ctrl elbow joint.
// returnType 0: only returns the elbow joint servo position and save it to goalPos[3],
//               servo will NOT move.
//            1: returns the elbow joint servo position and save it to goalPos[3],
//               servo moves.
// input the angle in radius(double), the speedInput(u16) is servo steps/second,
// the accInput(u8) is the acceleration of the servo movement.
// angleInput increase, it moves down.
int RoArmM2_elbowJointCtrlRad(byte returnType, double radInput, u16 speedInput, u8 accInput) {
  s16 computePos = calculatePosByRad(radInput) + 1024;
  goalPos[3] = constrain(computePos, 1024, 3071);

  if(returnType){
    st.WritePosEx(ELBOW_SERVO_ID, goalPos[3], speedInput, accInput);
  }
  return goalPos[3];
}


// use this function to compute the servo position to ctrl grab/hand joint.
// returnType 0: only returns the hand joint servo position and save it to goalPos[4],
//               servo will NOT move.
//            1: returns the hand joint servo position and save it to goalPos[4],
//               servo moves.
// ctrl type 0: status ctrl. - cmd 0: release 
//                                 1: grab
//           1: position ctrl. - cmd: input angle in radius.
int RoArmM2_handJointCtrlRad(byte returnType, double radInput, u16 speedInput, u8 accInput) {
  s16 computePos = calculatePosByRad(radInput);
  goalPos[4] = constrain(computePos, 700, 3396);

  if (returnType) {
    st.WritePosEx(GRIPPER_SERVO_ID, goalPos[4], speedInput, accInput);
  }
  return goalPos[4];
}


// use this function to ctrl the max torque of base joint.
void RoArmM2_baseTorqueCtrl(int inputTorque) {
  st.unLockEprom(BASE_SERVO_ID);
  st.writeWord(BASE_SERVO_ID, SMS_STS_TORQUE_LIMIT_L, constrain(inputTorque, ST_TORQUE_MIN, ST_TORQUE_MAX));
  st.LockEprom(BASE_SERVO_ID);
}


// use this function to ctrl the max torque of shoulder joint.
void RoArmM2_shoulderTorqueCtrl(int inputTorque) {
  st.unLockEprom(SHOULDER_DRIVING_SERVO_ID);
  st.writeWord(SHOULDER_DRIVING_SERVO_ID, SMS_STS_TORQUE_LIMIT_L, constrain(inputTorque, ST_TORQUE_MIN, ST_TORQUE_MAX));
  st.LockEprom(SHOULDER_DRIVING_SERVO_ID);

  st.unLockEprom(SHOULDER_DRIVEN_SERVO_ID);
  st.writeWord(SHOULDER_DRIVEN_SERVO_ID, SMS_STS_TORQUE_LIMIT_L, constrain(inputTorque, ST_TORQUE_MIN, ST_TORQUE_MAX));
  st.LockEprom(SHOULDER_DRIVEN_SERVO_ID);
}


// use this function to ctrl the max torque of elbow joint.
void RoArmM2_elbowTorqueCtrl(int inputTorque) {
  st.unLockEprom(ELBOW_SERVO_ID);
  st.writeWord(ELBOW_SERVO_ID, SMS_STS_TORQUE_LIMIT_L, constrain(inputTorque, ST_TORQUE_MIN, ST_TORQUE_MAX));
  st.LockEprom(ELBOW_SERVO_ID);
}


// use this function to ctrl the max torque of hand joint.
void RoArmM2_handTorqueCtrl(int inputTorque) {
  st.unLockEprom(GRIPPER_SERVO_ID);
  st.writeWord(GRIPPER_SERVO_ID, SMS_STS_TORQUE_LIMIT_L, constrain(inputTorque, ST_TORQUE_MIN, ST_TORQUE_MAX));
  st.LockEprom(GRIPPER_SERVO_ID);
}


// dynamic external force adaptation.
// mode: 0 - stop: reset every limit torque to 1000.
//       1 - start: set the joint limit torque. 
// b, s, e, h = bassJoint, shoulderJoint, elbowJoint, handJoint
// example:
// starts. input the limit torque of every joint.
// {"T":112,"mode":1,"b":50,"s":50,"e":50,"h":50}
// stop
// {"T":112,"mode":0,"b":1000,"s":1000,"e":1000,"h":1000}
void RoArmM2_dynamicAdaptation(byte inputM, int inputB, int inputS, int inputE, int inputH) {
  if (inputM == 0) {
    RoArmM2_baseTorqueCtrl(ST_TORQUE_MAX);
    RoArmM2_shoulderTorqueCtrl(ST_TORQUE_MAX);
    RoArmM2_elbowTorqueCtrl(ST_TORQUE_MAX);
    RoArmM2_handTorqueCtrl(ST_TORQUE_MAX);
  } else if (inputM == 1) {
    RoArmM2_baseTorqueCtrl(inputB);
    RoArmM2_shoulderTorqueCtrl(inputS);
    RoArmM2_elbowTorqueCtrl(inputE);
    RoArmM2_handTorqueCtrl(inputH);
  }
}


// this function uses relative radInput to set a new X+ axis.
// dirInput: 
//              0
//              X+
//        -90 - ^ - 90
//              |
//          -180 180 
void setNewAxisX(double angleInput) {
  double radInput = (angleInput / 180) * M_PI;
  RoArmM2_shoulderJointCtrlRad(1, 0, 500, 20);
  waitMove2Goal(SHOULDER_DRIVING_SERVO_ID, goalPos[1], 20);

  RoArmM2_elbowJointCtrlRad(1, 0, 500, 20);
  waitMove2Goal(ELBOW_SERVO_ID, goalPos[3], 20);

  RoArmM2_baseJointCtrlRad(1, 0, 500, 20);
  waitMove2Goal(BASE_SERVO_ID, goalPos[0], 20);

  delay(1000);

  RoArmM2_baseJointCtrlRad(1, -radInput, 500, 20);
  waitMove2Goal(BASE_SERVO_ID, goalPos[0], 20);

  delay(1000);

  setMiddlePos(BASE_SERVO_ID);

  delay(5);
}


// Simple Linkage IK:
// input the position of the end and return angle.
//   O----O
//  /
// O
// ---------------------------------------------------
// |       /beta           /delta                    |
//        O----LB---------X------                    |
// |     /       omega.   |       \LB                |
//      LA        .                < ----------------|
// |alpha     .          bIn         \LB -EP  <delta |
//    /psi.                           \LB -EP        |
// | /.   lambda          |                          |
// O- - - - - aIn - - - - X -                        |
// ---------------------------------------------------
// alpha, beta > 0 ; delta <= 0 ; aIn, bIn > 0
void simpleLinkageIkRad(double LA, double LB, double aIn, double bIn) {
  double psi, alpha, omega, beta, L2C, LC, lambda, delta;

  if (fabs(bIn) < 1e-6) {
    psi = acos((LA * LA + aIn * aIn - LB * LB) / (2 * LA * aIn)) + t2rad;
    alpha = M_PI / 2.0 - psi;
    omega = acos((aIn * aIn + LB * LB - LA * LA) / (2 * aIn * LB));
    beta = psi + omega - t3rad;
  } else {
    L2C = aIn * aIn + bIn * bIn;
    LC = sqrt(L2C);
    lambda = atan2(bIn, aIn);
    psi = acos((LA * LA + L2C - LB * LB) / (2 * LA * LC)) + t2rad;
    alpha = M_PI / 2.0 - lambda - psi;
    omega = acos((LB * LB + L2C - LA * LA) / (2 * LC * LB));
    beta = psi + omega - t3rad;
  }

  delta = M_PI / 2.0 - alpha - beta;

  SHOULDER_JOINT_RAD = alpha;
  ELBOW_JOINT_RAD    = beta;
  EOAT_JOINT_RAD_BUFFER  = delta;

  nanIK = isnan(alpha) || isnan(beta) || isnan(delta);
}


// AI prompt:
// *** this function is written with AI. ***
// '''
// 我需要一个C语言函数，在一个平面直角坐标系中，输入一个坐标点(x,y)，返回值有两个：
// 1. 这个坐标点距离坐标系原点的距离。
// 2. 这个点与坐标系原点所连线段与x轴正方向的夹角，夹角范围在(-PI, PI)之间。
// '''

// AI prompt:
// I need a C language function. In a 2D Cartesian coordinate system, 
// input a coordinate point (x, y). The function should return two values:

// The distance from this coordinate point to the origin of the coordinate system.
// The angle, in radians, between the line connecting this point and the origin 
// of the coordinate system and the positive direction of the x-axis. 
// The angle should be in the range (-π, π).
void cartesian_to_polar(double x, double y, double* r, double* theta) {
    *r = sqrt(x * x + y * y);
    *theta = atan2(y, x);
}


// AI prompt:
// *** this function is written with AI. ***
// 我现在需要一个功能与上面函数相反的函数：
// 输入机械臂三个关节的轴的角度（弧度制），返回当前机械臂末端点的坐标点。

// 你在回答的过程中可以告诉我你还有什么其它需要的信息。
// '''
// use this two functions to compute the position of coordinate point
// by inputing the jointRad.
// 这个函数用于将极坐标转换为直角坐标
void polarToCartesian(double r, double theta, double &x, double &y) {
  x = r * cos(theta);
  y = r * sin(theta);
}


// this function is used to compute the position of the end point.
// input the angle of every joint in radius.
// compute the positon and save it to lastXYZ by default.
void RoArmM2_computePosbyJointRad(double base_joint_rad, double shoulder_joint_rad, double elbow_joint_rad, double hand_joint_rad) {
  if (EEMode == 0) {
    // the end of the arm.
    double r_ee, x_ee, y_ee, z_ee;

    // compute the end position of the first linkage(the linkage between baseJoint and shoulderJoint).
    double aOut, bOut, cOut, dOut, eOut, fOut;

    polarToCartesian(l2, ((M_PI / 2) - (shoulder_joint_rad + t2rad)), aOut, bOut);
    polarToCartesian(l3, ((M_PI / 2) - (elbow_joint_rad + shoulder_joint_rad+ t3rad)), cOut, dOut);

    r_ee = aOut + cOut;
    z_ee = bOut + dOut;
    
    polarToCartesian(r_ee, base_joint_rad, eOut, fOut);
    x_ee = eOut;
    y_ee = fOut;

    lastX = x_ee;
    lastY = y_ee;
    lastZ = z_ee;
  }
  else if (EEMode == 1) {
    double aOut, bOut,   cOut, dOut,   eOut, fOut,   gOut, hOut;
    double r_ee, z_ee;

    polarToCartesian(l2, ((M_PI / 2) - (shoulder_joint_rad + t2rad)), aOut, bOut);
    polarToCartesian(l3, ((M_PI / 2) - (elbow_joint_rad + shoulder_joint_rad + t3rad)), cOut, dOut);
    polarToCartesian(lE, -((hand_joint_rad + tErad) - M_PI - (M_PI/2 - shoulder_joint_rad - elbow_joint_rad)), eOut, fOut);

    r_ee = aOut + cOut + eOut;
    z_ee = bOut + dOut + fOut;

    polarToCartesian(r_ee, base_joint_rad, gOut, hOut);

    lastX = gOut;
    lastY = hOut;
    lastZ = z_ee;
    lastT = hand_joint_rad - (M_PI - shoulder_joint_rad - elbow_joint_rad) + (M_PI / 2);
  }
}


// EEmode funcs change here.
// get position by servo feedback.
void RoArmM2_getPosByServoFeedback() {
  getFeedback(BASE_SERVO_ID, true);
  getFeedback(SHOULDER_DRIVING_SERVO_ID, true);
  getFeedback(ELBOW_SERVO_ID, true);
  getFeedback(GRIPPER_SERVO_ID, true);

  radB = calculateRadByFeedback(servoFeedback[BASE_SERVO_ID - 11].pos, BASE_JOINT);
  radS = calculateRadByFeedback(servoFeedback[SHOULDER_DRIVING_SERVO_ID - 11].pos, SHOULDER_JOINT);
  radE = calculateRadByFeedback(servoFeedback[ELBOW_SERVO_ID - 11].pos, ELBOW_JOINT);
  radG = calculateRadByFeedback(servoFeedback[GRIPPER_SERVO_ID - 11].pos, EOAT_JOINT);

  RoArmM2_computePosbyJointRad(radB, radS, radE, radG);
  if (EEMode == 0) {
    lastT = radG;
  }
}


// feedback info in json.
void RoArmM2_infoFeedback() {
  jsonInfoHttp.clear();
  jsonInfoHttp["T"] = 1051;
  jsonInfoHttp["x"] = lastX;
  jsonInfoHttp["y"] = lastY;
  jsonInfoHttp["z"] = lastZ;
  jsonInfoHttp["b"] = radB;
  jsonInfoHttp["s"] = radS;
  jsonInfoHttp["e"] = radE;
  jsonInfoHttp["t"] = lastT;
  // jsonInfoHttp["goalX"] = goalX;
  // jsonInfoHttp["goalY"] = goalY;
  // jsonInfoHttp["goalZ"] = goalZ;
  // jsonInfoHttp["goalT"] = goalT;
  jsonInfoHttp["torB"] = servoFeedback[BASE_SERVO_ID - 11].load;
  jsonInfoHttp["torS"] = servoFeedback[SHOULDER_DRIVING_SERVO_ID - 11].load - servoFeedback[SHOULDER_DRIVEN_SERVO_ID - 11].load;
  jsonInfoHttp["torE"] = servoFeedback[ELBOW_SERVO_ID - 11].load;
  jsonInfoHttp["torH"] = servoFeedback[GRIPPER_SERVO_ID - 11].load;

  jsonInfoHttp["torswitchB"] = servoFeedback[BASE_SERVO_ID - 11].Torquestatus;
  jsonInfoHttp["torswitchS"] = servoFeedback[SHOULDER_DRIVING_SERVO_ID - 11].Torquestatus;
  jsonInfoHttp["torswitchE"] = servoFeedback[ELBOW_SERVO_ID - 11].Torquestatus;
  jsonInfoHttp["torswitchH"] = servoFeedback[GRIPPER_SERVO_ID - 11].Torquestatus;
  inaDataUpdate();
  int v_int = (int)(loadVoltage_V * 100);
	jsonInfoHttp["v"] = v_int;
  String getInfoJsonString;
  serializeJson(jsonInfoHttp, getInfoJsonString);
  Serial.println(getInfoJsonString);
}


// AI prompt:
// 在平面直角坐标系中，有一点A，输入点A的X,Y坐标和角度参数theta(弧度制)，点A绕直角坐标系原点
// 逆时针转动theta作为点B，返回点B的XY坐标值，我需要C语言的函数。
// AI prompt:
// In a 2D Cartesian coordinate system, there is a point A.
// Input the X and Y coordinates of point A and an angle parameter theta (in radians).
// Point A rotates counterclockwise around the origin of the Cartesian coordinate 
// system by an angle of theta to become point B. Return the XY coordinates of point B.
// I need a C language function.
void rotatePoint(double theta, double *xB, double *yB) {
  double alpha = tErad + theta;

  *xB = lE * cos(alpha);
  *yB = lE * sin(alpha);
}


// AI prompt:
// 在平面直角坐标系种，有一点A，输入点A的X,Y坐标值，输入一个距离参数S，
// 点A向原点方向移动S作为点B，返回点B的坐标值。我需要C语言的函数。
void movePoint(double xA, double yA, double s, double *xB, double *yB) {
  double distance = sqrt(pow(xA, 2) + pow(yA, 2));  
  if(distance - s <= 1e-6) {
    *xB = 0; 
    *yB = 0;
  }
  else {
    double ratio = (distance - s) / distance;
    *xB = xA * ratio;
    *yB = yA * ratio;
  }
}


// ---===< Muti-assembly IK config here >===---
// change this func and goalPosMove()
// Coordinate Ctrl: input the coordinate point of the goal position to compute
// the goalPos of every joints.
void RoArmM2_baseCoordinateCtrl(double inputX, double inputY, double inputZ, double inputT){
  if (EEMode == 0) {
    cartesian_to_polar(inputX, inputY, &base_r, &BASE_JOINT_RAD);
    simpleLinkageIkRad(l2, l3, base_r, inputZ);
    RoArmM2_handJointCtrlRad(0, inputT, 0, 0);
  }
  else if (EEMode == 1) {
    rotatePoint((inputT - M_PI), &delta_x, &delta_y);
    movePoint(inputX, inputY, delta_x, &beta_x, &beta_y);
    cartesian_to_polar(beta_x, beta_y, &base_r, &BASE_JOINT_RAD);
    simpleLinkageIkRad(l2, l3, base_r, inputZ + delta_y);
    EOAT_JOINT_RAD = EOAT_JOINT_RAD_BUFFER + inputT;
  }
}


// update last position for later use.
void RoArmM2_lastPosUpdate(){
  lastX = goalX;
  lastY = goalY;
  lastZ = goalZ;
  lastT = goalT;
}


// use jointCtrlRad functions to compute goalPos for every servo,
// then use this function to move the servos.
// cuz the functions like baseCoordinateCtrl is not gonna make servos move.
void RoArmM2_goalPosMove(){
  RoArmM2_baseJointCtrlRad(0, BASE_JOINT_RAD, 0, 0);
  RoArmM2_shoulderJointCtrlRad(0, SHOULDER_JOINT_RAD, 0, 0);
  RoArmM2_elbowJointCtrlRad(0, ELBOW_JOINT_RAD, 0, 0);
  if (EEMode == 1) {
    RoArmM2_handJointCtrlRad(0, EOAT_JOINT_RAD, 0, 0);
  }
  st.SyncWritePosEx(servoID, 5, goalPos, moveSpd, moveAcc);
}


// ctrl a single joint abs angle(rad).
// joint: 1-BASE_JOINT + ->left
//        2-SHOULDER_JOINT + ->down
//        3-ELBOW_JOINT + ->down
//        4-EOAT_JOINT + ->grab/down
// inputRad: input the goal angle in radius of the joint.
// inputSpd: move speed, steps/second.
// inputAcc: acceleration, steps/second^2.
void RoArmM2_singleJointAbsCtrl(byte jointInput, double inputRad, u16 inputSpd, u8 inputAcc){
  switch(jointInput){
  case BASE_JOINT:
    RoArmM2_baseJointCtrlRad(1, inputRad, inputSpd, inputAcc);
    BASE_JOINT_RAD = inputRad;
    break;
  case SHOULDER_JOINT:
    RoArmM2_shoulderJointCtrlRad(1, inputRad, inputSpd, inputAcc);
    SHOULDER_JOINT_RAD = inputRad;
    break;
  case ELBOW_JOINT:
    RoArmM2_elbowJointCtrlRad(1, inputRad, inputSpd, inputAcc);
    ELBOW_JOINT_RAD = inputRad;
    break;
  case EOAT_JOINT:
    RoArmM2_handJointCtrlRad(1, inputRad, inputSpd, inputAcc);
    EOAT_JOINT_RAD = inputRad;
    break;
  }
  RoArmM2_computePosbyJointRad(BASE_JOINT_RAD, SHOULDER_JOINT_RAD, ELBOW_JOINT_RAD, EOAT_JOINT_RAD);
}


// ctrl all joints together.
// when all joints in initPos(middle position), it looks like below.
//    -------L3------------O==L2B===
//                         ^       |
//                         |       |
//                   ELBOW_JOINT   |
//                                L2A
//                                 |
//                                 |
//           ^                     |
//           |   SHOULDER_JOINT -> O
//           Z+                    |
//           |                    L1
//           |                     |
//   <---X+--Y+      BASE_JOINT -> X
//
//
//    -------L3------------O==L2B==O  <- BASE_JOINT
//                         ^       
//   <---X+--Z+            |       
//           |       ELBOW_JOINT   
//           Y+
//           |
//           v
void RoArmM2_allJointAbsCtrl(double inputBase, double inputShoulder, double inputElbow, double inputHand, u16 inputSpd, u8 inputAcc){
  RoArmM2_baseJointCtrlRad(0, inputBase, inputSpd, inputAcc);
  RoArmM2_shoulderJointCtrlRad(0, inputShoulder, inputSpd, inputAcc);
  RoArmM2_elbowJointCtrlRad(0, inputElbow, inputSpd, inputAcc);
  RoArmM2_handJointCtrlRad(0, inputHand, inputSpd, inputAcc);
  for (int i = 0;i < 5;i++) {
    moveSpd[i] = inputSpd;
    moveAcc[i] = inputAcc;
  }
  st.SyncWritePosEx(servoID, 5, goalPos, moveSpd, moveAcc);
  for (int i = 0;i < 5;i++) {
    moveSpd[i] = 0;
    moveAcc[i] = 0;
  }
}


// ctrl the movement in a smooth way.
// |                 ..  <-numEnd
// |             .    |
// |           .    
// |         .        |
// |        .
// |      .           |
// |. . <-numStart
// ----------------------
// 0                  1 rateInput
double besselCtrl(double numStart, double numEnd, double rateInput){
  double numOut;
  numOut = (numEnd - numStart)*((cos(rateInput*M_PI+M_PI)+1)/2) + numStart;
  return numOut;
}
   

// use this function to get the max deltaSteps.
// get the max offset between [goal] and [last] position.
double maxNumInArray(){
  if (EEMode == 0) {
    double deltaPos[4] = {abs(goalX - lastX),
                          abs(goalY - lastY),
                          abs(goalZ - lastZ),
                          abs(goalT - lastT)*10};
    double maxVal = deltaPos[0];
    for(int i = 0; i < (sizeof(deltaPos) / sizeof(deltaPos[0])); i++){
      maxVal = max(deltaPos[i],maxVal);
    }
    return maxVal;
  } else if (EEMode == 1) {
    double deltaPos[4] = {abs(goalX - lastX),
                          abs(goalY - lastY),
                          abs(goalZ - lastZ),
                          abs(goalT - lastT)*200};
    double maxVal = deltaPos[0];
    for(int i = 0; i < (sizeof(deltaPos) / sizeof(deltaPos[0])); i++){
      maxVal = max(deltaPos[i],maxVal);
    }
    return maxVal;
  }
}


// use this function to move the end of the arm to the goal position.
void RoArmM2_movePosGoalfromLast(float spdInput){
  double deltaSteps = maxNumInArray();

  double bufferX;
  double bufferY;
  double bufferZ;
  double bufferT;

  static double bufferLastX;
  static double bufferLastY;
  static double bufferLastZ;
  static double bufferLastT;

  for(double i=0;i<=1;i+=(1/(deltaSteps*1))*spdInput){
    bufferX = besselCtrl(lastX, goalX, i);
    bufferY = besselCtrl(lastY, goalY, i);
    bufferZ = besselCtrl(lastZ, goalZ, i);
    bufferT = besselCtrl(lastT, goalT, i);
    RoArmM2_baseCoordinateCtrl(bufferX, bufferY, bufferZ, bufferT);
    if(nanIK){
      // IK failed
      goalX = bufferLastX;
      goalY = bufferLastY;
      goalZ = bufferLastZ;
      goalT = bufferLastT;
      RoArmM2_baseCoordinateCtrl(goalX, goalY, goalZ, goalT);
      RoArmM2_goalPosMove();
      RoArmM2_lastPosUpdate();
      return;
    }
    else{
      // IK succeed.
      bufferLastX = bufferX;
      bufferLastY = bufferY;
      bufferLastZ = bufferZ;
      bufferLastT = bufferT;
    }
    RoArmM2_goalPosMove();
    delay(2);
  }
  RoArmM2_baseCoordinateCtrl(goalX, goalY, goalZ, goalT);
  RoArmM2_goalPosMove();
  RoArmM2_lastPosUpdate();
}


// ctrl a single axi abs pos(mm).
// the init position is
// axiInput: 1-X, posInput:initX
//           2-Y, posInput:initY
//           3-Z, posInput:initZ
//           4-T, posInput:initT
// initX = l3+l2B
// initY = 0
// initZ = l2A
// initT = M_PI
// default inputSpd = 0.25
void RoArmM2_singlePosAbsBesselCtrl(byte axiInput, double posInput, double inputSpd){
  switch(axiInput){
    case 1: goalX = posInput;break;
    case 2: goalY = posInput;break;
    case 3: goalZ = posInput;break;
    case 4: goalT = posInput;break;
  }
  RoArmM2_movePosGoalfromLast(inputSpd);
}


// ctrl all axis abs position.
// initX = l3+l2B
// initY = 0
// initZ = l2A
// initT = M_PI
// default inputSpd = 0.36
void RoArmM2_allPosAbsBesselCtrl(double inputX, double inputY, double inputZ, double inputT, double inputSpd){
  goalX = inputX;
  goalY = inputY;
  goalZ = inputZ;
  goalT = inputT;
  RoArmM2_movePosGoalfromLast(inputSpd);
}


// ChatGPT prompt:
// '''
// 我需要一个函数，输入圆心坐标点、半径和比例，当比例从0到1变化时，函数输出的坐标点可以组成一个完整的圆。
// '''
// I need a function that inputs the center coordinate point, 
// radius and scale(t), and when the scale(t) changes from 0 to 1, 
// the coordinate points output by the function can form a complete circle.
// '''
//
// example:
// for(float i=0;i<=1;i+=0.001){
//   getCirclePointYZ(0, initZ, 100, i);
//   RoArmM2_goalPosMove();
//   delay(5);
// }
void getCirclePointYZ(double cx, double cy, double r, double t) {
  double theta = t * 2 * M_PI;
  goalY = cx + r * cos(theta);
  goalZ = cy + r * sin(theta);
}


// delay cmd.
void RoArmM2_delayMillis(int inputTime) {
  delay(inputTime);
}


// set the P&I/PID of a joint.
void RoArmM2_setJointPID(byte jointInput, float inputP, float inputI) {
  switch (jointInput) {
  case BASE_JOINT:
        st.writeByte(BASE_SERVO_ID, ST_PID_P_ADDR, inputP);
        st.writeByte(BASE_SERVO_ID, ST_PID_I_ADDR, inputI);
        break;
  case SHOULDER_JOINT:
        st.writeByte(SHOULDER_DRIVING_SERVO_ID, ST_PID_P_ADDR, inputP);
        st.writeByte(SHOULDER_DRIVING_SERVO_ID, ST_PID_I_ADDR, inputI);

        st.writeByte(SHOULDER_DRIVEN_SERVO_ID, ST_PID_P_ADDR, inputP);
        st.writeByte(SHOULDER_DRIVEN_SERVO_ID, ST_PID_I_ADDR, inputI);
        break;
  case ELBOW_JOINT:
        st.writeByte(ELBOW_SERVO_ID, ST_PID_P_ADDR, inputP);
        st.writeByte(ELBOW_SERVO_ID, ST_PID_I_ADDR, inputI);
        break;
  case EOAT_JOINT:
        st.writeByte(GRIPPER_SERVO_ID, ST_PID_P_ADDR, inputP);
        st.writeByte(GRIPPER_SERVO_ID, ST_PID_I_ADDR, inputI);
        break;
  }
}


// reset the P&I/PID of RoArm-M2.
void RoArmM2_resetPID() {
  RoArmM2_setJointPID(BASE_JOINT, 16, 0);
  RoArmM2_setJointPID(SHOULDER_JOINT, 16, 0);
  RoArmM2_setJointPID(ELBOW_JOINT, 16, 0);
  RoArmM2_setJointPID(EOAT_JOINT, 16, 0);
}


// input the angle in deg, and it returns the number of servo steps.
int calculatePosByDeg(double degInput) {
  return round((degInput / 360) * ARM_SERVO_POS_RANGE);
}


double ang2deg(double inputAng) {
  return (inputAng / 180) * M_PI;
}


// ctrl a single joint abs angle.
// jointInput: 1-BASE_JOINT
//             2-SHOULDER_JOINT
//             3-ELBOW_JOINT
//             4-HAND_JOINT
// inputRad: input the goal angle in deg of the joint.
// inputSpd: move speed, angle/second.
// inputAcc: acceleration, angle/second^2.
void RoArmM2_singleJointAngleCtrl(byte jointInput, double inputAng, u16 inputSpd, u8 inputAcc){
  Serial.println("---");
  Serial.print(jointInput);Serial.print("\t");Serial.print(inputAng);Serial.print("\t");
  Serial.print(inputSpd);Serial.print("\t");Serial.print(inputAcc);Serial.println();

  inputSpd = abs(inputSpd);
  inputAcc = abs(inputAcc);
  switch(jointInput){
  case BASE_JOINT:
    BASE_JOINT_ANG = inputAng;
    Serial.println(inputAng);
    BASE_JOINT_RAD = ang2deg(inputAng);
    Serial.println(BASE_JOINT_RAD);
    RoArmM2_baseJointCtrlRad(1, BASE_JOINT_RAD, calculatePosByDeg(inputSpd), calculatePosByDeg(inputAcc));
    break;
  case SHOULDER_JOINT:
    SHOULDER_JOINT_ANG = inputAng;
    SHOULDER_JOINT_RAD = ang2deg(inputAng);
    RoArmM2_shoulderJointCtrlRad(1, SHOULDER_JOINT_RAD, calculatePosByDeg(inputSpd), calculatePosByDeg(inputAcc));
    break;
  case ELBOW_JOINT:
    ELBOW_JOINT_ANG = inputAng;
    ELBOW_JOINT_RAD = ang2deg(inputAng);
    RoArmM2_elbowJointCtrlRad(1, ELBOW_JOINT_RAD, calculatePosByDeg(inputSpd), calculatePosByDeg(inputAcc));
    break;
  case EOAT_JOINT:
    EOAT_JOINT_ANG = inputAng;
    EOAT_JOINT_RAD = ang2deg(inputAng);
    RoArmM2_handJointCtrlRad(1, EOAT_JOINT_RAD, calculatePosByDeg(inputSpd), calculatePosByDeg(inputAcc));
    break;
  }
  RoArmM2_computePosbyJointRad(BASE_JOINT_RAD, SHOULDER_JOINT_RAD, ELBOW_JOINT_RAD, EOAT_JOINT_RAD);
}


// ctrl all joints together.
// when all joints in initPos(middle position), it looks like below.
//    -------L3------------O==L2B===
//                         ^       |
//                         |       |
//                   ELBOW_JOINT   |
//                                L2A
//                                 |
//                                 |
//           ^                     |
//           |   SHOULDER_JOINT -> O
//           Z+                    |
//           |                    L1
//           |                     |
//   <---X+--Y+      BASE_JOINT -> X
//
//
//    -------L3------------O==L2B==O  <- BASE_JOINT
//                         ^       
//   <---X+--Z+            |       
//           |       ELBOW_JOINT   
//           Y+
//           |
//           v
void RoArmM2_allJointsAngleCtrl(double inputBase, double inputShoulder, double inputElbow, double inputHand, u16 inputSpd, u8 inputAcc){
  BASE_JOINT_ANG = inputBase;
  BASE_JOINT_RAD = ang2deg(inputBase);

  SHOULDER_JOINT_ANG = inputShoulder;
  SHOULDER_JOINT_RAD = ang2deg(inputShoulder);

  ELBOW_JOINT_ANG = inputElbow;
  ELBOW_JOINT_RAD = ang2deg(inputElbow);
  
  EOAT_JOINT_ANG = inputHand;
  EOAT_JOINT_RAD = ang2deg(inputHand);

  RoArmM2_baseJointCtrlRad(0, BASE_JOINT_RAD, 0, 0);
  RoArmM2_shoulderJointCtrlRad(0, SHOULDER_JOINT_RAD, 0, 0);
  RoArmM2_elbowJointCtrlRad(0, ELBOW_JOINT_RAD, 0, 0);
  RoArmM2_handJointCtrlRad(0, EOAT_JOINT_RAD, 0, 0);
  inputSpd = abs(calculatePosByDeg(inputSpd));
  inputAcc = abs(calculatePosByDeg(inputAcc));
  for (int i = 0;i < 5;i++) {
    moveSpd[i] = inputSpd;
    moveAcc[i] = inputAcc;
  }
  st.SyncWritePosEx(servoID, 5, goalPos, moveSpd, moveAcc);
}


void constantCtrl(byte inputMode, byte inputAxis, byte inputCmd, byte inputSpd) {
  const_mode = inputMode;
  if (const_mode == CONST_ANGLE) {
    const_spd = abs(inputSpd) * 0.0005;
  } else if (const_mode == CONST_XYZT) {
    const_spd = abs(inputSpd) * 0.1;
  }

  switch (inputAxis) {
  case BASE_JOINT:
          const_cmd_base_x = inputCmd;
          break;
  case SHOULDER_JOINT:
          const_cmd_shoulder_y = inputCmd;
          break;
  case ELBOW_JOINT:
          const_cmd_elbow_z = inputCmd;
          break;
  case EOAT_JOINT:
          const_cmd_eoat_t = inputCmd;
          break;
  }
}


// RoArmM2_infoFeedback()
void constantHandle() {
  if (!const_cmd_base_x && !const_cmd_shoulder_y && !const_cmd_elbow_z && !const_cmd_eoat_t) {
    const_goal_base = radB;
    const_goal_shoulder = radS;
    const_goal_elbow = radE;
    const_goal_eoat = radG;

    goalX = lastX;
    goalY = lastY;
    goalZ = lastZ;
    goalT = lastT;
    return;
  }

  if (const_cmd_base_x == MOVE_INCREASE) {
    if (const_mode == CONST_ANGLE) {
      const_goal_base += const_spd;
      if (const_goal_base > M_PI) {
        const_goal_base = M_PI;
        const_cmd_base_x = MOVE_STOP;
      }
    }
    else if (const_mode == CONST_XYZT) {
      goalX += const_spd;
    }
  } else if (const_cmd_base_x == MOVE_DECREASE) {
    if (const_mode == CONST_ANGLE) {
      const_goal_base -= const_spd;
      if (const_goal_base < -M_PI) {
        const_goal_base = -M_PI;
        const_cmd_base_x = MOVE_STOP;
      }
    }
    else if (const_mode == CONST_XYZT) {
      goalX -= const_spd;
    }
  }


  if (const_cmd_shoulder_y == MOVE_INCREASE) {
    if (const_mode == CONST_ANGLE) {
      const_goal_shoulder += const_spd;
      if (const_goal_shoulder > M_PI/2) {
        const_goal_shoulder = M_PI/2;
        const_cmd_shoulder_y = MOVE_STOP;
      }
    }
    else if (const_mode == CONST_XYZT) {
      goalY += const_spd;
    }
  } else if (const_cmd_shoulder_y == MOVE_DECREASE) {
    if (const_mode == CONST_ANGLE) {
      const_goal_shoulder -= const_spd;
      if (const_goal_shoulder < -M_PI/2) {
        const_goal_shoulder = -M_PI/2;
        const_cmd_shoulder_y = MOVE_STOP;
      }
    }
    else if (const_mode == CONST_XYZT) {
      goalY -= const_spd;
    }
  }


  if (const_cmd_elbow_z == MOVE_INCREASE) {
    if (const_mode == CONST_ANGLE) {
      const_goal_elbow += const_spd;
      if (const_goal_elbow > M_PI) {
        const_goal_elbow = M_PI;
        const_cmd_elbow_z = MOVE_STOP;
      }
    }
    else if (const_mode == CONST_XYZT) {
      goalZ += const_spd;
    }
  } else if (const_cmd_elbow_z == MOVE_DECREASE) {
    if (const_mode == CONST_ANGLE) {
      const_goal_elbow -= const_spd;
      if (const_goal_elbow < 0) {
        const_goal_elbow = 0;
        const_cmd_elbow_z = MOVE_STOP;
      }
    }
    else if (const_mode == CONST_XYZT) {
      goalZ -= const_spd;
    }
  }


  if (const_cmd_eoat_t == MOVE_INCREASE) {
    if (const_mode == CONST_ANGLE) {
      const_goal_eoat += const_spd;
      if (const_goal_eoat > M_PI*7/4) {
        const_goal_eoat = M_PI*7/4;
        const_cmd_eoat_t = MOVE_STOP;
      }
    }
    else if (const_mode == CONST_XYZT) {
      goalT += const_spd/200;
    }
  } else if (const_cmd_eoat_t == MOVE_DECREASE) {
    if (const_mode == CONST_ANGLE) {
      const_goal_eoat -= const_spd;
      if (const_goal_eoat < -M_PI/4) {
        const_goal_eoat = -M_PI/4;
        const_cmd_eoat_t = MOVE_STOP;
      }
    }
    else if (const_mode == CONST_XYZT) {
      goalT -= const_spd/200;
    }
  }

  if (const_mode == CONST_ANGLE) {
    RoArmM2_allJointAbsCtrl(const_goal_base, const_goal_shoulder, const_goal_elbow, const_goal_eoat, 0, 0);
  } else if (const_mode == CONST_XYZT) {

    static double bufferLastX;
    static double bufferLastY;
    static double bufferLastZ;
    static double bufferLastT;

    RoArmM2_baseCoordinateCtrl(goalX, goalY, goalZ, goalT);
    if (nanIK) {
      // IK failed
      goalX = bufferLastX;
      goalY = bufferLastY;
      goalZ = bufferLastZ;
      goalT = bufferLastT;
      RoArmM2_baseCoordinateCtrl(bufferLastX, bufferLastY, bufferLastZ, bufferLastT);
      RoArmM2_goalPosMove();
      RoArmM2_lastPosUpdate();
      return;
    }
    else  {
      bufferLastX = goalX;
      bufferLastY = goalY;
      bufferLastZ = goalZ;
      bufferLastT = goalT;
    }
    RoArmM2_goalPosMove();
    RoArmM2_lastPosUpdate();
  }
}



// // // // // // // // // // // // // // // //
// // // <TEST FUNCTIONS for RoArm-M2> // // //
// // // // // // // // // // // // // // // //
// example:
// RoArmM2_Test_drawSqureYZ(-100, 0, 50, 300);
// void RoArmM2_Test_drawSqureXZ(int squre_x, int squre_y, int squre_l){
//   for(double i=0;i<=squre_l;i+=0.1){
//     simpleLinkageIkRad(l2, l3, l3+l2B + squre_x, l2A-i+squre_y);
//     RoArmM2_goalPosMove();
//     delay(3);
//   }
//   delay(1500);

//   for(double i=0;i<=squre_l;i+=0.1){
//     simpleLinkageIkRad(l2, l3, l3+l2B-i + squre_x , l2A- squre_l+squre_y);
//     RoArmM2_goalPosMove();
//     delay(3);
//   }
//   delay(1500);

//   for(double i=0;i<=squre_l;i+=0.1){
//     simpleLinkageIkRad(l2, l3, l3+l2B- squre_l +squre_x, l2A- squre_l +i+squre_y);
//     RoArmM2_goalPosMove();
//     delay(3);
//   }
//   delay(1500);

//   for(double i=0;i<=squre_l;i+=0.1){
//     simpleLinkageIkRad(l2, l3, l3+l2B- squre_l +i +squre_x, l2A +squre_y);
//     RoArmM2_goalPosMove();
//     delay(3);
//   }
//   delay(1500);
// }


// void RoArmM2_Test_drawSqureYZ(int squre_x, int squre_y, int squre_z, int squre_l){
//   for(double i=0;i<=squre_l;i+=0.1){
//     RoArmM2_baseCoordinateCtrl(l3+l2B+squre_x, squre_y-squre_l/2+i, l2A+squre_z);
//     RoArmM2_goalPosMove();
//     delay(2);
//   }
//   delay(1250);

//   for(double i=0;i<=squre_l;i+=0.1){
//     RoArmM2_baseCoordinateCtrl(l3+l2B+squre_x, squre_y+squre_l/2, l2A+squre_z-i);
//     RoArmM2_goalPosMove();
//     delay(2);
//   }
//   delay(1250);

//   for(double i=0;i<=squre_l;i+=0.1){
//     RoArmM2_baseCoordinateCtrl(l3+l2B+squre_x, squre_y+squre_l/2-i, l2A+squre_z-squre_l);
//     RoArmM2_goalPosMove();
//     delay(2);
//   }
//   delay(1250);

//   for(double i=0;i<=squre_l;i+=0.1){
//     RoArmM2_baseCoordinateCtrl(l3+l2B+squre_x, squre_y-squre_l/2, l2A+squre_z-squre_l+i);
//     RoArmM2_goalPosMove();
//     delay(2);
//   }
//   delay(1250);
// }


// void RoArmM2_Test_drawCircleYZ(){
//   for(float i=0; i<=1; i+=0.001){
//     getCirclePointYZ(initY, initZ-100, 100, i);
//     RoArmM2_baseCoordinateCtrl(initX-100, goalY, goalZ);
//     RoArmM2_goalPosMove();
//     delay(3);
//   }
// }