Help with serial from Processing

Hey guys, this is my first time doing serial and I need another set of eyes.

I connected a PCA9685 board to the Arduino and to that I have 5V coming from my power supply, and 6 servo motors all connected into a 3D printed robot arm.

The first issue I had when connecting 6 Pots, were that the PCA9685 shares the A4 & A5 ports so I only have 4 connections to work with. (My ARDUINO MEGA 2560 is on it's way).

In the meantime I thought I'd try controlling the Arduino's servos with Processing from my PC, so I connected it all up through the serial port but it's slow.

Here is how my program should work:
Button is pressed in Processing and a string is sent through the serial port -> 0:90\n
Servo 0 : position 90 and the end of line.

First char is the servo number 0 - 5, a colon then the position.

The Arduino takes the string apart and runs the servo. This all works but it lags a lot so I tried putting in a multiplier and adding that. Still slow.

Here is my code:

PROCESSING

import processing.serial.*;

Serial myPort;  // Create object from Serial class
int val;        // Data received from the serial port

boolean down = false;
boolean bover = false;

// Multiplier
int inc0 = 10;
int inc1 = 10;
int inc2 = 10;
int inc3 = 10;
int inc4 = 10;
int inc5 = 10;

// Buttons
Button bUp0, bDn0, bUp1, bDn1, bUp2, bDn2, bUp3, bDn3, bUp4, bDn4, bUp5, bDn5, bReset;

// Init value for servo numbers
int num0 = 90;
int num1 = 140;
int num2 = 50;
int num3 = 100;
int num4 = 110;
int num5 = 140;


void setup() {
  size(650, 400);

  String portName = Serial.list()[0];
  myPort = new Serial(this, portName, 500000); // I tried several speeds which make no difference

  textSize(24);
  fill(0, 80, 150);

  // Buttons
  bUp0 = new Button(25, 25, 100, 50, "Up 0");
  bDn0 = new Button(25, 125, 100, 50, "Down 0");

  bUp1 = new Button(125, 25, 100, 50, "Up 1");
  bDn1 = new Button(125, 125, 100, 50, "Down 1");

  bUp2 = new Button(225, 25, 100, 50, "Up 2");
  bDn2 = new Button(225, 125, 100, 50, "Down 2");

  bUp3 = new Button(325, 25, 100, 50, "Up 3");
  bDn3 = new Button(325, 125, 100, 50, "Down 3");

  bUp4 = new Button(425, 25, 100, 50, "Up 4");
  bDn4 = new Button(425, 125, 100, 50, "Down 4");

  bUp5 = new Button(525, 25, 100, 50, "Up 5");
  bDn5 = new Button(525, 125, 100, 50, "Down 5");

  bReset = new Button(25, 200, 100, 50, "Reset");
}


void draw() {
  background(204);
  text(num0, 50, 110);
  text(num1, 150, 110);
  text(num2, 250, 110);
  text(num3, 350, 110);
  text(num4, 450, 110);
  text(num5, 550, 110);

  bUp0.update();
  bDn0.update();

  bUp1.update();
  bDn1.update();

  bUp2.update();
  bDn2.update();

  bUp3.update();
  bDn3.update();

  bUp4.update();
  bDn4.update();

  bUp5.update();
  bDn5.update();

  bReset.update();

  // Button clicks
  if (bUp0.clicked) {
    num0 = num0 + inc0;
    myPort.write("0:" + num0 + "\n");
  }
  if (bDn0.clicked) {
    num0 = num0 - inc0;
    myPort.write("0:" + num0 + "\n");
  }

  if (bUp1.clicked) {
    num1 = num1 + inc1;
    myPort.write("1:" + num1 + "\n");
  }
  if (bDn1.clicked) {
    num1 = num1 - inc1;
    myPort.write("1:" + num1 + "\n");
  }

  if (bUp2.clicked) {
    num2 = num2 + inc2;
    myPort.write("2:" + num2 + "\n");
  }
  if (bDn2.clicked) {
    num2 = num2 - inc2;
    myPort.write("2:" + num2 + "\n");
  }

  if (bUp3.clicked) {
    num3 = num3 + inc3;
    myPort.write("3:" + num3 + "\n");
  }
  if (bDn3.clicked) {
    num3 = num3 - inc3;
    myPort.write("3:" + num3 + "\n");
  }

  if (bUp4.clicked) {
    num4 = num4 + inc4;
    myPort.write("4:" + num4 + "\n");
  }
  if (bDn4.clicked) {
    num4 = num4 - inc4;
    myPort.write("4:" + num4 + "\n");
  }

  if (bUp5.clicked) {
    num5 = num5 + inc5;
    myPort.write("5:" + num5 + "\n");
  }
  if (bDn5.clicked) {
    num5 = num5 - inc5;
    myPort.write("5:" + num5 + "\n");
  }

  // This sends 0:0 which the Arduino knows to reset the arm to its default position
  if (bReset.clicked) {
    num0 = 90;
    num1 = 140;
    num2 = 50;
    num3 = 100;
    num4 = 110;
    num5 = 140;
    myPort.write("0:0\n");
  }
}
























class Button {
  int xpos, ypos, wid, hei;
  int colorUp = 255;
  int colorOver = 100;
  int colorDown = 200;
  String text;
  boolean over = false;
  boolean down = false; 
  boolean clicked = false;

  Button(
    int tx, int ty, 
    int tw, int th, 
    String tlabel
    ) {
    xpos = tx;
    ypos = ty;
    wid = tw;
    hei = th;
    text = tlabel;
  }

  void update() {
    //it is important that this comes first
    if (down && over && !mousePressed) {
      clicked=true;
    } else {
      clicked=false;
    }

    if (mouseX > xpos && mouseY > ypos && mouseX < xpos + wid && mouseY < ypos + hei) {    
      over=true;
      if (mousePressed) {
        down=true;
      } else {
        down=false;
      }
    } else {
      over=false;
    }

    smooth();
    fill(colorUp);

    if (!over) {
      fill(colorUp);
    } else {
      if (!down) {
        fill(colorDown);
      } else {
        fill(0, 100, 230);
      }
    }

    stroke(0);
    rect(xpos, ypos, wid, hei, 10); //draws the rectangle, the last param is the round corners
    fill(0);
    textSize(24); 
    text(text, xpos+wid/2-(textWidth(text)/2), ypos+hei/2+(textAscent()/2)); 
    //all of this just centers the text in the box
  }
}

ARDUINO

// Include Wire Library for I2C Communications
#include <Wire.h>

// Include Adafruit PWM Library
#include <Adafruit_PWMServoDriver.h>

// For the servos
#define MIN_PULSE_WIDTH       650
#define MAX_PULSE_WIDTH       2350
#define FREQUENCY             50

// Calculate based on max input size expected for one command
#define INPUT_SIZE 30

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();


void setup() {
  Serial.begin(500000);

  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);

  // Setup motor board
  pwm.begin();
  pwm.setPWMFreq(FREQUENCY);

}

void loop() {

  GetData();
}

void DoCommand(int servoId, int position)
{
  // Do something with servoId and position
  //Serial.print("servoId "); Serial.print(servoId);
  //Serial.print(" - position "); Serial.println(position);

  if (servoId == 0 && position == 0) {
    moveMotor(0, 90);
    moveMotor(1, 140);
    moveMotor(2, 50);
    moveMotor(3, 100);
    moveMotor(4, 110);
    moveMotor(5, 140);
  }
  else
  {
    moveMotor(servoId, position);
  }
}


void moveMotor(int motorOut, int position)
{
  int posMapped, pulse_width;

  // Convert to pulse width
  posMapped = map(position, 0, 180, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
  pulse_width = int(float(posMapped) / 1000000 * FREQUENCY * 4096);

  //Control Motor
  pwm.setPWM(motorOut, 0, pulse_width);

}


// This gets the data and splits up the values
void GetData() {
  // Get next command from Serial (add 1 for final 0)
  char input[INPUT_SIZE + 1];
  byte size = Serial.readBytes(input, INPUT_SIZE);
  // Add the final 0 to end the C string
  input[size] = 0;

  // Read each command pair
  char* command = strtok(input, "&");
  while (command != 0)
  {
    // Split the command in two values
    char* separator = strchr(command, ':');
    if (separator != 0)
    {
      // Actually split the string in 2: replace ':' with 0
      *separator = 0;
      int servoId = atoi(command);
      ++separator;
      int position = atoi(separator);

      // Do something with servoId and position
      DoCommand(servoId, position);

    }
    // Find the next command in input string
    command = strtok(0, "&");
  }

}

If you put debug statements in your program but don't actually run the motors, does the program run faster?

ieee488:
If you put debug statements in your program but don't actually run the motors, does the program run faster?

I can't use debug statements because the Arduino throws an error saying the serial port is in use.

GaryParkin:
I can't use debug statements because the Arduino throws an error saying the serial port is in use.

You would need TTL to USB adapter and use SoftwareSerial.

It is good to get the adapter which is very helpful with a Uno which has only one hardware serial port.

.

ieee488:
You would need TTL to USB adapter and use SoftwareSerial.

It is good to get the adapter which is very helpful with a Uno which has only one hardware serial port.

.

Cool, thank you for the help. Just to be sure I have the right info, is it this?
https://www.amazon.com/DSD-TECH-Adapter-FT232RL-Compatible/dp/B07BBPX8B8/ref=sr_1_11?keywords=USB+to+TTL+Adapter

Or is it like a cable?
https://www.amazon.com/Serial-Adapter-Signal-Prolific-Windows/dp/B07R8BQYW1/ref=sr_1_1_sspa?keywords=USB+to+TTL+Adapter

And this library?
https://www.arduino.cc/en/Reference/softwareSerial

I've been a software guy for years, but I've never had to do serial communication.

I assume the usb plugs into my PC and connects to the arduino with jumpers?

Most info I found is how to use the arduino AS a ttl converter.

Either of the cables will work for what you want.
Notice that the FT232 cable breaks out more pins if you need it versus the Prolific cable.

Yes that library.
There are other libraries too.
But for debugging purposes the SoftwareSerial libary is adequate. Highest baud rate is 38400.

If you do happen to have another Arduino, you can certainly use it as a TTL converter without having to wait for the cable.

.

ieee488:
Either of the cables will work for what you want.
Notice that the FT232 cable breaks out more pins if you need it versus the Prolific cable.

Yes that library.
There are other libraries too.
But for debugging purposes the SoftwareSerial libary is adequate. Highest baud rate is 38400.

If you do happen to have another Arduino, you can certainly use it as a TTL converter without having to wait for the cable.

.

OMG! I do have a second Arduino. I can use that!

After searching and trying things, I have a working solution.

For the sake of others, I added the second Arduino which connects on com4. My original Arduino connects on com3.

Next I uploaded a blank sketch to the second Arduino.

I connected pins 10 and 11 of the Arduino on com3 to pins RX and TX on the second Arduino on com4.

Then I added this code to the Arduino on com3:

#include <SoftwareSerial.h>
SoftwareSerial softSerial(10, 11); // RX, TX

In setup() I added:

  softSerial.begin(9600);
  softSerial.println("SoftwareSerial ready");

Then down in the program where I needed debug, I added:

  softSerial.print("servoId "); softSerial.print(servoId);
  softSerial.print(" - position "); softSerial.println(position);

Note: This NOW prints to the com4 port, so you have to open the serial monitor for the Arduino which connects on com4.

And wow, I have debug working on com4 for com3.

Hey ieee488! Cheers for getting me on the right path so I can learn something new.
Thanks a lot.

I forgot to mention that now I know it takes 1 second from the time I press the button to the time it takes to print the debug message. That's why the motors are not getting the second click to go faster.

So it seems the lag is on the com port or the code I used to wrap/de-wrap the data.


Update Edit: So when I remove the part of code that breaks apart the servo number from the ":" and the angle, and hard code the servo and just pass in the angle, the servo moves as fast as I can press the button. Himmm.

RATS! Copy and corrupt happened again. I should have seen that when I copied the code from the website.
There was a flaw in the GetData(), so I rewrote it as a simple function, along with a simpler way to de-construct the data. In case any future peeps find this, I'll post my working code.

void GetData() {
  static byte ndx = 0;
  char endMarker = '\n';
  char rc;

  while (Serial.available() > 0 && newData == false) {
    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 processNewData() {
  if (newData == true) {
    softSerial.print("This just in ... ");
    softSerial.println(receivedChars);

    // Split the command in two values
    char* separator = strchr(receivedChars, ':');
    if (separator != 0)
    {
      // Actually split the string in 2: replace ':' with 0
      *separator = 0;
      int servoId = atoi(receivedChars);
      ++separator;
      int position = atoi(separator);

      // Do something with servoId and position
      DoCommand(servoId, position);
    }
    
    newData = false;
  }
}

void DoCommand(int servoId, int position)
{
  softSerial.print("servoId "); softSerial.print(servoId);
  softSerial.print(" - position "); softSerial.println(position);

  // Do something with servoId and position
  // If I send it 0:0 then reset all of the servos to their "rest" position.
  if (servoId == 0 && position == 0) {
    moveMotor(0, 90);
    moveMotor(1, 140);
    moveMotor(2, 50);
    moveMotor(3, 100);
    moveMotor(4, 110);
    moveMotor(5, 140);
  }
  else
  {
    moveMotor(servoId, position);
  }
}

So it is working?

The printing to the Serial Monitor can possibly help indicate where the slowness is coming from, but no guarantees.

Yes it is working but currently I have to keep clicking the button. After work I'll check out how to make it repeat send on a mouse down.

Good. Progress.

I altered the code to check for down and not clicked and now as long as the mouse is held, the serial sends out data.
I had to add a small delay so the arm would not jerk. It still isn't smooth but I'll call it down for now.
I'm going to plug in my xbox controller and see if that will work better.

Processing Code:

// Button clicks
  if (bUp0.down) {
    if (num0 < 200) {
      num0 = num0 + inc0;
      myPort.write("0:" + num0 + "\n");
      delay(50);
    }
  }
  if (bDn0.down) {
    if (num0 > 5) {
      num0 = num0 - inc0;
      myPort.write("0:" + num0 + "\n");
      delay(50);
    }
  }

...