Best Way to Control Servos via Python to Arduino

All, This is great! I love this forum! My apologies, for not getting back sooner. I was out all last week due to a horrible stomach flu! It kicked me hard! :face_with_thermometer:

Here’s some more details on where I’m and what I am seeing to help clarify some questions.

So right now I’m trying to send the positions over at usec. Per your feedback, I can send 4-digit numbers easily. For debugging, I’m sending over six values starting at 1500, then adding 1 to each and sending again every 40ms in a For Loop. I’m doing this for 80 times.

Below is the main part of my Python code so you can see how I’m constructing the string and sending it out.

#Random initial angles for the 6 servos
pos1 = 1500
pos2 = 1500
pos3 = 1500
pos4 = 1500
pos5 = 1500
pos6 = 1500

#Loop 10 times.  Increase positions 5deg each loop as a test
for i in range(80):
    #Put Positions in array
    int_array = [pos1, pos2, pos3, pos4, pos5, pos6]

    #Update positions
    pos1 += 1
    pos2 += 1
    pos3 += 1
    pos4 += 1
    pos5 += 1
    pos6 += 1

    # Convert integers to strings and join them with a space
    string_data = ' '.join(map(str, int_array))

    # Add a newline character to the end of the string data
    string_data += '\n'

    # Encode the string to bytes (e.g., using UTF-8 encoding)
    byte_data_with_spaces = string_data.encode('utf-8')

    #Print everything here in Python Console to See what it Looks like
    print(f"Original integers: {int_array}")
    print(f"String with spaces and newline: '{string_data.replace('\n', '\\n')}'")
    print(f"Encoded byte data with spaces and newline (hex): {byte_data_with_spaces.hex()}")

    # Send the byte data over the serial port
    ser.write(byte_data_with_spaces)
    print(f"Sent {len(byte_data_with_spaces)} bytes to {ser.name}")

    #Small time delay between messages
    time.sleep(0.04)

This results in the following output per the python’s terminal window. It shows the original integers, then with spaces and the ‘/n’ added, then the raw ASCII. This shows the first 2 loops of the for loop.

Original integers: [1500, 1500, 1500, 1500, 1500, 1500]
String with spaces and newline: '1500 1500 1500 1500 1500 1500\n'
Encoded byte data with spaces and newline (hex): 31353030203135303020313530302031353030203135303020313530300a
Sent 30 bytes to COM6
Original integers: [1501, 1501, 1501, 1501, 1501, 1501]
String with spaces and newline: '1501 1501 1501 1501 1501 1501\n'
Encoded byte data with spaces and newline (hex): 31353031203135303120313530312031353031203135303120313530310a
Sent 30 bytes to COM6

Below is my Arduino code with all the servo stuff removed so I can just get serial data coming in cleanly and robustly.:

  #include <Servo.h>

  char c;
  char buf[80];
  int pos = 0;
  int pos1, pos2, pos3, pos4, pos5, pos6 = 0;

  void loop()
  {
    while (Serial.available())
    {
      c = Serial.read();
      if (c == '\n')
      {
        buf[pos] = '\0';  // Null-terminate the buffer
        Serial.println(buf);
        sscanf(buf, "%d %d %d %d %d %d", &pos1, &pos2, &pos3, &pos4, &pos5, &pos6);
        Serial.println(pos1);
        Serial.println(pos2);
        Serial.println(pos3);
        Serial.println(pos4);
        Serial.println(pos5);
        Serial.println(pos6);

        pos = 0;    //reset pos pointer for buffer
      }
      else if (pos < sizeof(buf) - 1)
      {
        buf[pos++] = c;
      }
    }

NOTE: I did go back to 9600 baud rate. It seems like if I go faster, I cannot get clean data in the Serial Monitor window in Arduino IDE. I also added the ‘\0’ to the buffer as noted.

In the Ardunio Serial Monitor window, I print the entire buffer, then parse out the individual servo positions and print them. Initially, things look good but then I start getting bad. You can see this on the 7th integration as shown below. I bolded the area,

  1. Does this look like a bad connection, buffer overflow, or is the serial monitor window not able to keep up?
  2. I’m using an Uno so does writing to the serial port causing issues since its the same port I’m reading data in on?
  3. Where should I put the Serial purge()?

1503 1503 1503 1503 1503 1503
1503
1503
1503
1503
1503
1503
1504 1504 1504 1504 1504 1504
1504
1504
1504
1504
1504
1504
1505 1505 1505 1505 1505 1505
1505
1505
1505
1505
1505
1505
1506 1506 1506 1506 1506 1506
1506
1506
1506
1506
1506
1506
150507
19435
1506

1506
1506
1506
1506

A) Why “while“? Your code executes VERY quickly. How about “if”? Think about how your code will process an incoming stream when a character is only available every 100 us (presumes 115200 baud) - your loop will iterate faster than that, let alone 9600.

B) Suggest you consider whether sscanf returns any value of interest(hint - it does!)

C) What happens if, somehow, your ‘\n’ is missing, or early? When I look at that listing, I suspect the source data was corrupted first, not the serial stream or the Arduino code

1 - that’s three questions, so:
1a - see 2, below, 1b - no, 1c - not at 9600, not at 115200
2 - show us how it’s wired to your Uno, in detail. You have a serial stream of data from the PC wired to RX somehow, and a Serial response on TX going to Serial Monitor; that implies to me two serial ports in use on the PC, so…
3 - may not be necessary, now that you’re sync’ing on the ‘\n’, other than putting it at the end of setup() to get rid of leading rubbish, a perennial serial problem.

Finally, testing systems is all about good test data. Yours isn’t stellar. Try using unique values that will never overlap in your test sequence - like starting with 800, 900, 1000, 1100, 1200, 1300 as your initial values, since you’re only running 80 times.

if on the arduino side you run this (go back to 115200 on the python side) - what do you see in the Serial monitor ?

char buf[100];
int pos1, pos2, pos3, pos4, pos5, pos6;

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  if (Serial.available())
  {
    size_t len = Serial.readBytesUntil('\n', buf, sizeof buf - 1);
    buf[len] = '\0';

    int n = sscanf(buf, "%d %d %d %d %d %d", &pos1, &pos2, &pos3, &pos4, &pos5, &pos6);
    if (n == 6)
    {
      Serial.println(pos1);
      Serial.println(pos2);
      Serial.println(pos3);
      Serial.println(pos4);
      Serial.println(pos5);
      Serial.println(pos6);
    }
    else
    {
      Serial.print("Error decoding, got ");
      Serial.print(n);
      Serial.println(" values");
    }
  }
}