Why is my servo motor not moving with the following code?

127 Views Asked by At

I am trying to control two servo motors using serial connection between unity and arduino.

However, I have noticed that whilst the arduino code works and I can independently power the servo motors based on the "anticlockwise","clockwise","anticlockwise joint 2", and "clockwise joint 2" commands, when I try to send two commands to the serial monitor within one update loop neither of my servo's move.

Below is the arduino code:


`#include <Servo.h> // servo library

String input;
int count;
int countSecond;
int rotationAngleStep;

Servo servo1; // servo control object
Servo servo2;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  servo1.attach(13);
  servo1.write(0);
  servo2.attach(10);
  servo2.write(0);
  count = 0;
  countSecond = 0;
  rotationAngleStep = 10;
}

void loop() 
{
  input = Serial.readString();
  Serial.print(input);
      
  if(input == "anticlockwise")
  {
    Serial.println("turn motor");
    count = count + 1;
    servo1.write(count*rotationAngleStep);  
   }

  if(input == "clockwise")
  {
    Serial.println("turn motor");
    if(count > 0)
    {
    count = count - 1;
    servo1.write(count*rotationAngleStep);
    }   
  }


    if(input == "anticlockwise joint 2")
  {
    Serial.println("turn motor");
    countSecond = countSecond + 1;
    servo2.write(countSecond*rotationAngleStep);
   }

    if(input == "clockwise joint 2")
  {
    Serial.println("turn motor");
    if(countSecond > 0)
    {
    countSecond = countSecond - 1;
    servo2.write(countSecond*rotationAngleStep);
    }   
  }

  delay(1000);


}
    
    //}if(data == 'servo1'){
      //servo1.write(10);
    //}if(data == 'servo2'){
      //servo2.write(10);
    
 `

Below is the unity code:

``using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using Unity.VisualScripting;
using UnityEngine;
using System.IO.Ports;

public class IKManager : MonoBehaviour
{


    public SerialPort serial = new SerialPort("COM4",9600);
    //basically want to evaluate how close we are to target by assessing end effector value in the y dimension, against target value in the y dimension.
    //by evaluating distance between two iterative frames (and attaining a slope value) can tell if moving further or closer to end effector. Gradient descent.

    //root of the armature
    public Joint m_root;

    //End effector
    public Joint m_end;

    public GameObject m_target;

    public float m_threshold = 20f;

    public float m_rate = 1.0f;

    private int counter = 2;

    private float timer;

    private float slope1;
    private float slope2;

    private float intermediary;

    void Start(){
        if(serial.IsOpen == false){
            serial.Open();
        }

        serial.Write("hey");
    }

    // Calculate slope - i.e distance between points divided by step (delta theta)
    float CalculateSlope(Joint _joint)
    {
        float deltaTheta = 10f;
        float distance1 = GetDistance(m_end.transform.position, m_target.transform.position);

        _joint.transform.Rotate(0f,0f,deltaTheta,Space.World);

        float distance2 = GetDistance(m_end.transform.position, m_target.transform.position);

        _joint.transform.Rotate(0f,0f,-deltaTheta,Space.World);

        return (distance2 - distance1) / deltaTheta;

    }

    async void Update()
    {
        timer = Time.realtimeSinceStartup;
        //Debug.Log(timer);
        //Debug.Log("counter " + counter);
        if (timer > 4*counter)
        {
            Debug.Log("can move now");
            counter = counter + 1;
            Debug.Log(counter);
            if (GetDistance(m_end.transform.position, m_target.transform.position) > m_threshold)
            {
                Joint current = m_root;
                float slope1 = CalculateSlope(current);
                if (slope1 > 0){
                    current.transform.Rotate(0f,0f,-10f,Space.World);
                    serial.Write("clockwise");
                    Debug.Log("clockwise");
                }if(slope1 < 0){
                    current.transform.Rotate(0f,0f,10f,Space.World);
                    serial.Write("anticlockwise");
                    Debug.Log("anticlockwise");                             
                }
                //Debug.Log("Slope " + slope);
                
                //Debug.Log("Move Servo");

                current = current.GetChild();
                slope2 = CalculateSlope(current);
                if (slope2 > 0){
                    current.transform.Rotate(0f,0f,-10f,Space.World);
                    //serial.Write("clockwise joint 2");
                    Debug.Log("clockwise joint 2");
                }if(slope2 < 0){
                    current.transform.Rotate(0f,0f,10f,Space.World);
                    //serial.Write("anticlockwise joint 2");
                    Debug.Log("anticlockwise joint 2");                               
                }
                //Debug.Log("Slope " + slope);




                
                // if(slope != 0){
                //     serial.Write("servo2");
                // }else{
                //     serial.Write("no change");
                // } 
                // serial.Close() ;
            
            }
        }
        
    }
    float GetDistance(Vector3 _point1, Vector3 _point2)
    {
        return Vector3.Distance(_point1, _point2);
    }

}`

I was thinking it would be because the signals might be registering on the same line, but then I introduce a delay after the serial.read() function in arduino and had the same issue. Any help would be greatly appreciated.

1

There are 1 best solutions below

6
On BEST ANSWER

you need to update your Arduino loop

From Documentation

Serial.readString() reads characters from the serial buffer into a String. The function terminates if it times out (see setTimeout()).

void loop() {
  while (Serial.available() == 0) {}   // wait for data available
  input = Serial.readString();         // read until timeout
  input.trim();                        // remove any \r \n whitespace at the end of the String
  Serial.print(input);

  if (input == "anticlockwise") {
    Serial.println("turn motor");
    count = count + 1;
    servo1.write(count * rotationAngleStep);
  } else if (input == "clockwise") {
    Serial.println("turn motor");
    if (count > 0) {
      count = count - 1;
      servo1.write(count * rotationAngleStep);
    }
  } else if (input == "anticlockwise joint 2") {
    Serial.println("turn motor");
    countSecond = countSecond + 1;
    servo2.write(countSecond * rotationAngleStep);
  } else if (input == "clockwise joint 2") {
    Serial.println("turn motor");
    if (countSecond > 0) {
      countSecond = countSecond - 1;
      servo2.write(countSecond * rotationAngleStep);
    }
  }
  delay(1000);
}

Extra Solution / Idea - Change the communication protocal

Monitor the output of Serial.print(input); and see if what its value is then post back here, as it could be being merged when writing like such

anticlockwise
anticlockwiseanticlockwise joint 2
anticlockwise joint 2anticlockwise

if it is then i suggest you append a null byte at the end of your Serial write (in your unity code) and then in your Arduino just split your trimed string by the null bytes and move it into a loop so that you can just loop through it and then do the said commands

like from unity code

serial.Write("anticlockwise joint 2" + char.MinValue);
serial.Write("anticlockwise" + char.MinValue);

then in Arduino code do something like this

void loop() {
  while (Serial.available() == 0) {} // wait for data available
  input = Serial.readString(); // read until timeout
  input.trim(); // remove any \r \n whitespace at the end of the String
  Serial.print(input);

  // Use strtok to split the string into tokens
  char * token = strtok(input, "\0");
  // Continue until no more tokens are found
  while (token != NULL) {
    if (token == "anticlockwise") {
      Serial.println("turn motor");
      count = count + 1;
      servo1.write(count * rotationAngleStep);
    } else if (token == "clockwise") {
      Serial.println("turn motor");
      if (count > 0) {
        count = count - 1;
        servo1.write(count * rotationAngleStep);
      }
    } else if (token == "anticlockwise joint 2") {
      Serial.println("turn motor");
      countSecond = countSecond + 1;
      servo2.write(countSecond * rotationAngleStep);
    } else if (token == "clockwise joint 2") {
      Serial.println("turn motor");
      if (countSecond > 0) {
        countSecond = countSecond - 1;
        servo2.write(countSecond * rotationAngleStep);
      }
    }
    token = strtok(NULL, "\0");
  }
}