Servo motor turns to max angle & initia;l angle when using serial

Hi,
I want to post here once again, I have a serial smart servo motor (provides feedback), when I use this servo motor with arduino serial it works great but when I try to use it with SIMULINK/MATLAB, everytime I start simulation the servo goes to it's initial position 0 once I close the simulation the servo goes again to the max position 300°. please find attached Arduino code :


#include <SCServo.h>
SCSCL sc;


//Estructura Union
typedef union {
  float number;
  uint8_t bytes[4];
} valor;
valor velocidad;

unsigned long timeold;
int vel = 0;
float value;
void setup() {

  Serial1.begin(1000000);
  sc.pSerial = &Serial1;
  Serial.begin(9600);
}

void loop() {
  //Pregunta si tenemos un dato a recibir
  if (Serial.available() > 0) {
    value = recepcion();
  }

  //Transforma el valor del voltaje (0-5) a velocidad
  vel = map(value, 0, 5, 0, 1023);
  //Activa el motor dirección Backward con la velocidad
  sc.WritePos(3,  value, 0);

  velocidad.number = sc.ReadPos(3);

  Serial.write('V');
  for (int i = 0; i < 4; i++) {
    Serial.write(velocidad.bytes[i]);
  }
  Serial.write('\n');
  delay(50);
}



//Recibir Flotante
float recepcion() {
  int i;
  valor buf;
  for (i = 0; i < 4; i++)
    buf.bytes[i] = Serial.read();
  return buf.number;
}
//Función para la lectura del encoder

}```
Servo motor uses Serial 1 to communicate with Arduino while I use Serial 0 to Communicate with SIMULINK.
If you have any proposition I would be greatfull!

Your serial protocol (binary transfer of float values) is sub-optimal as it doesn't offer any synchronization.

Additionally you should check what your PC application sends at start and end over the serial interface.

Can you suggest a better serial protocol than the one I am working with?
I'll try to use serial reader and I'll tell you back!
Thank you!

If you don't need the speed, use an ASCII based protocol and use start and end characters.

I just have to send position and get it as feedback!

Please find here the Simulink control code :

since you are having some issues with the communication, I would suggest that you take a step back and try to see what the arduino is ACTUALLY receiving.

If you have a USB-UART convert and putty software you could try something like this (assuming u are using a MEGA):

void setup() {

  //Serial1.begin(1000000);
  //sc.pSerial = &Serial1;
  Serial2.begin(115200); //connect to UART - USB interface (PUTTY COM connection)
  Serial.begin(9600); //MATLAB COM11 connection
}

void loop() {

  if (Serial.available() > 0) {
    while (Serial.available()) {
      char c = Serial.read();
      Serial2.println(c); //printed to PUTTY console
      Serial.print(c); //feedback to MATLAB
    }
    Serial2.println(""); //printed to PUTTY console
  }
}

once you can confirm what your arduino is reading, it will make it easier to debug! :wink:

hope that help...

That's an image and not a link to the code. Don't expect us to type in lengthier URLs from an image!

Please find Processing: Comunicacion_Arduino_2015.slx...
Processing: Comunicacion_Arduino_2015.slx...
This is because I can't add slx file here, here is a link to the code :

Please the concerned file is the slx one

@zaki_HDne

Please try the code offered in post #7 (assuming you have the additional usb-to-uart converter)

If will help you debug both the simulik model AND your servo code!!!

I've got the impression that english may not be native for you, so please lets take baby steps and HOPEFULLY we can help you solve your problem,

spamming us with all sorts for code you have found does not really help us help you!

Actually, I would like to try it, but unortunately I do not have an USB-UART converter....
Yes, I am not native egnlish. Is there anything that I can try without an USB-UART converter?
There is something that I would like to add, When i Try to send Servo actual position from arduino to Simulink trough serial, it works great, the servo doesn't move at all but when I add the servo command from Simulink to arduino, the servo starts turning randomly!

Pity... you could always try blinking an LED where the number of flashed would be the value receive.

I did not expect an issue there! :wink:

yeah... replace the servo by an LED that you would blink and send a couple of known values (I would suggest you pick 'small' values) to the arduino via simulink and see if they match

something like this:

void setup() {

  //Serial1.begin(1000000);
  //sc.pSerial = &Serial1;
  Serial2.begin(115200); //connect to UART - USB interface (PUTTY COM connection)
  Serial.begin(9600); //MATLAB COM11 connection
}

void loop() {

  if (Serial.available() > 0) {
    while (Serial.available()) {
      int c = Serial.read();

      Serial.print(c); //feedback to MATLAB

      for (int i = 0; i < c+1 ; ++i) { //blink led  if zero is received, will blink once(blocking code)
        digitalWrite(LED_BUILTIN, HIGH); //using mega's builtin led
        delay(1000);
        digitalWrite(LED_BUILTIN, LOW);
        delay(1000);
      }
      digitalWrite(LED_BUILTIN, HIGH); //a 2s LED ON time to indicate end of byte read
      delay(2000);
      digitalWrite(LED_BUILTIN, LOW);
      delay(1000);
    }
  }
}

hope that helps...

I don't know how but I think that it works now, I just added a delay of 100us after if (serial.available()>0) loop & it works now!
Anyone has an explanation of this ?

lucky really!

is probably coz within that delay all for bytes were received and ready to be processed.

//Recibir Flotante
float recepcion() {
  int i;
  valor buf;
  for (i = 0; i < 4; i++)
    buf.bytes[i] = Serial.read();
  return buf.number;
}

as opposed to without the delay its was very possible that would read '4 bytes' when not all of those were ACTUALLY received.

As I said LUCKY!

I would suggest you work on a more robust method of transmitting you data packet so that your arduino RELIABLY and CORRECTLY reads the data it receives.

have a look at this tutorial for some ideas. It for string based commands but the in principle, if you want to do the same with raw data it is very possible.

Good luck!

I tried to modify one of the provided codes so it can feet to my requirements.
Please find attached the pic showing the Simulink code :

The servo is not moving at all, it seems like the arduino is not receiving the expected data...
here is the code :

// Example 4 - Receive a number as text and convert it to an int
#include<SCServo.h> 
SCSCL   sc;
const byte numChars = 7;
char receivedChars[numChars];   // an array to store the received data

boolean newData = false;

int dataNumber = 0;             // new for this version

void setup() {
     Serial.begin(9600);
   // Serial.println("<Arduino is ready>");
     Serial1.begin(1000000);
     sc.pSerial = &Serial1;
}

void loop() {
    recvWithEndMarker();
    showNewNumber();
}

void recvWithEndMarker() {
    static byte ndx = 0;
    char endMarker = '\n';
    char rc;
    
    if (Serial.available() > 0) {
        rc = Serial.read();

        if (rc != endMarker) {
            receivedChars[ndx] = rc;
            ndx++;
            if (ndx >= numChars) {
                ndx = numChars - 1;
            }
        }
        else {
            receivedChars[ndx] = '\0'; // terminate the string
            ndx = 0;
            newData = true;
        }
    }
}

void showNewNumber() {
    if (newData == true) {
        dataNumber = 0;             // new for this version
        dataNumber = atoi(receivedChars);   // new for this version
        sc.WritePos(1, dataNumber, 0);//舵机(ID1),运行至1023位置,运行时间T=2100ms
    }
}```

yeah... because you did not heed my caution about the link I shared!

tbh not sure how the simulink COM block works but I strongly suspect that it transmits out raw value, ie when you tell it send '20' for example, it sends out the BINARY representation of '20' and NOT the ASCII value (ie string values) of '2' and '0'.

have a look as the ascii table for binary values of alphanumerics if you dont know it already.

if you still intend to transmit the actual integer value, one way I would do it is,
since 'single' is 4 byte wide and your servo value is not expected to exceed 65,535, I would set the UPPER 2 bytes of your value to 0xFF

so on simulink side I would:

  • adding a summing block to add 4294901760 (ie 0xFFFF0000) to servo val.

  • set you transmitting byte to BigEndian with no header or terminator.

on the arduino side

  • if receive 2 consecutive 0xFF

  • then if 3rd byte receive should be <0xFF (upper byte value)

  •          then receive 4th byte (lower byte value)
              combine 3rd and 4th received byte to get servo position value.
    

There would be a few more bits I would also add but your original code was a good start! I dont wanna spoil all the fun! :wink:

just need to make it abit more robust!

hope that helps....

Hi, I tried the described steps but in vain, The servo motor doesn't turn at all, Please correct me if I am wrong, here is the modified simulink code :


image
image

here is the arduino code :

// Example 4 - Receive a number as text and convert it to an int
#include<SCServo.h>
SCSCL   sc;



const byte numChars = 32;
char receivedChars[numChars];   // an array to store the received data

boolean newData = false;

int dataNumber = 0;             // new for this version
int foundValue = 0;
void setup() {
  Serial.begin(9600);
  // Serial.println("<Arduino is ready>");
  Serial1.begin(1000000);
  sc.pSerial = &Serial1;


}

void loop() {
  recv();
  sendData();
  delay(10);
}

void recv() {
  static byte ndx = 0;
  byte rc;

  if (Serial.available() > 0) {

    rc = Serial.read();
    if (rc == 0xFF ) {    //First test & Second test
      receivedChars[ndx] = rc;  
      ndx++;
      if (ndx == 2) {   // If both first bytes are 0xFF 
        rc = Serial.read();   //Read the third one
        ndx++;
        receivedChars[ndx] = rc;   //Save it!
        if (rc < 0xFF) {     // Test if we third byte is < 0xFF 
          rc = Serial.read();   //Read the 4th one 
          ndx++;
          receivedChars[ndx] = rc;   //SAVE it
          int foundValue =  (receivedChars[3] << 8) | receivedChars[4] ;   //Combine both bytes...
        }
      }
      else {
        ndx = 0;
        newData = true;
      }
    }
  }
}



void sendData() {
  if (newData == true) {
    dataNumber = 0;            
    sc.WritePos(1,  foundValue, 0);  //Send the FoundValue to the servo motor
  }
delay(500);    //Wait for 0.5s
}
}```

again... we are debugging here so PLEASE try to implement something that will allow you figure out what is going on.

would be useful to know what 'foundValue' was calculated!

without anyway for further debugging, it is hard to know where it could have gone wrong...

This is my take on how I would receive and read the servo value:
(NOT tested!)

#include<SCServo.h>
SCSCL   sc;

int ndx = 0;
int foundValue = 0;

void setup() {
  Serial.begin(9600);
  // Serial.println("<Arduino is ready>");
  Serial1.begin(1000000);
  sc.pSerial = &Serial1;
}

void loop() {

  if (Serial.available() > 0) { //at least 4 bytes were received
    ndx++;
    byte rc;
    rc = Serial.read();

    switch (ndx) {
      case 1 ... 2:
        if (rc != 0xFF) --ndx; //reset index count if first 2 received bytes is NOT 0xFF
        if (ndx < 0) ndx = 0;
      break;

      case 3:
        if (rc == 0xFF){
            --ndx;
        }
       else{
           foundValue = rc;
           foundValue <<= 8;
       }
      break;

      case 4:
        foundValue |= rc;
        ndx = 0;
        sc.WritePos(1,  foundValue, 0);  //Send the FoundValue to the servo motor
      break;
    }
  }
  delay(500);    //Wait for 0.5s
}

hope that helps...

Thank you for correcting me, unfortunately it is not working... The servo motor doesn't turn at all

did not ask you to try the code but to implement some way of debugging what your arduino is receiving and reading.

until you do that we cannot help further I'm afraid.

you can always go back to your original code, not robust IMHO but at least is was working for you!

btw, in you simulink model, the 'single' block I would have put AFTER the the summing block, not before!