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!
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!
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!
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);
}
}
}
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 ?
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.
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!
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 :
// 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
}