Clogged Serial?

Hello,

I'm trying to write a program/interface combination (in Arduino and Processing) that will allow me to control several motors at different speeds. Each set of speeds is entered in Processing and sent via Serial to Arduino, where the speeds are assigned to the proper motors. Now, I've tested them and both the Arduino and the Processing sketches work fine independently. They work together fine for the first set of motor speeds that I send. When I send a new set of motor speeds from Processing, Arduino processes them correctly once, then uses the same set of speeds again and again, but somehow mangling them in the process.

When I enter the same sets of speeds using the Serial Monitor, I do not have this problem. When I enter the same set of speeds in Processing to write to the Serial, they are printed correctly and they have always been sent correctly to Arduino.

I have two theories:

  1. I'm using a button to write the speeds to the Serial in Processing. A single click ends up sending at least three copies of the same set. I've tried using a few flags to prevent this, but I could be doing it wrong or missing something.

  2. The Serial is clogged with garbage that is not "inputs". If this is the problem, it is my own fault. I have Arduino sending feedback back to Processing so I can see what's going on from a code point of view.

Which problem seems more likely? (I can post code upon request.) If the problem is the Serial, what are some possible ways I could go about fixing it?

Much appreciated!

  1. I'm using a button to write the speeds to the Serial in Processing. A single click ends up sending at least three copies of the same set. I've tried using a few flags to prevent this, but I could be doing it wrong or missing something.

Like not posting your code?

  1. The Serial is clogged with garbage that is not "inputs". If this is the problem, it is my own fault. I have Arduino sending feedback back to Processing so I can see what's going on from a code point of view.

The serial buffer on the Arduino only gets filled with what the other end sends it. The serial buffer on the PC only gets filled with what the Arduino sends it. There is no "garbage" being added by other processes.

We need to see what code you have on the Arduino, too.

Arduino code:

/**
 * input_2_0
 *
 * Interfaces with output_2_0
 *
 * Can take multiple inputs for 4 motors
 * Currently set to recieve 1 speed/direction input per motor
 *
 * Forward is ccw, reverse is cw
 */


#include <RoboClaw.h>
#include <BMSerial.h>

#define addressA 0x80
#define addressB 0x81
#define addressC 0x82

#define Kp 0x00000000      //constants get define
#define Ki 0x00000000
#define Kd 0x00000000
#define qpps 4490

RoboClaw roboclawA(5, 6);
RoboClaw roboclawB(7, 8);
RoboClaw roboclawC(9, 10);                               

//4 motors
//1 speed each
char rawSpeeds[16];    //4*number of motors*number of speeds
char rawDir[5];        //always one more than the number of inputs - drops the [0]
int speeds[4];
int prevSpeeds[] = {0, 0, 0, 0};
int numIn = 4;        //number of motors*number of speeds
int serialInS=0;
int serialInD=0;

boolean started = false;
boolean ended = false;


void setup(){
  Serial.begin(38400);
  roboclawA.begin(2400);

  roboclawA.SetM1Constants(addressA, Kd, Kp, Ki, qpps);
  roboclawA.SetM2Constants(addressA, Kd, Kp, Ki, qpps);

  roboclawB.SetM1Constants(addressB, Kd, Kp, Ki, qpps);
  roboclawB.SetM2Constants(addressB, Kd, Kp, Ki, qpps);

  roboclawC.SetM1Constants(addressC, Kd, Kp, Ki, qpps);
  roboclawC.SetM2Constants(addressC, Kd, Kp, Ki, qpps);
}

void loop(){
  char aChar;

  //Get input
  while(Serial.available()>0){
    aChar = Serial.read();
    if (aChar=='<'){
      started = true;
      ended = false;
    } 
    else if (aChar=='>'){
      ended = true;
      break;
    } 
    else if (aChar>='0' && aChar<='9'){
      rawSpeeds[serialInS] = aChar;
      serialInS++;
      rawSpeeds[serialInS] = '\0';      
    } 
    else if (aChar=='F' || aChar=='R'){
      rawDir[serialInD+1] = aChar;
      serialInD++;
      rawDir[serialInD+1] = '\0';
    }
  }

  //Process input
  if (started&&ended){
    char s[4];
    memset(s, '\0', 4);
    for (int i=0; i<numIn; i++){
      strncpy(s, rawSpeeds+4*i, 4);
      speeds[i] = atoi(s);
      Serial.println(" ");
      Serial.print("[");
      Serial.print("Directions: ");
      Serial.print(i);
      Serial.print(rawDir[i+1]);
      Serial.print("]");
      if(rawDir[i+1] == 'R'){
        speeds[i] = -speeds[i];
      }
    }
    for(int i = 0; i<numIn; i++){
      Serial.println(" ");
      Serial.print("[");
      Serial.print("Speeds: ");
      Serial.print(speeds[i]);
      Serial.print("]");
    }
    if(prevSpeeds!=speeds){
    for(int i = 0; i<numIn; i+=4){
      for (int j = 0; j<200; j++){
        roboclawA.SpeedM1(addressA, speeds[i]);
        roboclawA.SpeedM2(addressA, speeds[i+1]);
        // roboclawB.SpeedM1(addressB, speeds[i+2]);
        // roboclawC.SpeedM1(addressB, speeds[i+3]);
      }
    }
    }
    started = false;
    ended = false;
    serialInS = 0;
    serialInD = 0;
    for (int i = 0; i<numIn; i++){
      prevSpeeds[i] = speeds[i];
      speeds[i]=0;
    }
    roboclawA.SpeedM1(addressA, 0);
    roboclawA.SpeedM2(addressA, 0);
    Serial.println(" ");
    Serial.print("[");
    Serial.print("Next input: ");
    Serial.print("]");
    Serial.println(" ");
  }
}

Processing Code:

/**
 *
 * output_1_3
 *
 * Get inputs from a GUI and displays outputs at bottom of screen. 
 *
 * Enter the appropriate number in the field. Hit enter to add it to the array. The speed is displayed to the right of the 
 * direction radial buttons. If the string is empty a speed of 0 is displayed.
 *
 */

import processing.serial.*;
import controlP5.*;

ControlP5 cp5;
Serial myPort;

Textlabel myTLA, myTLB, myTLC, myTLD;
RadioButton rA, rB, rC, rD;

String[] out = new String[8];
String inputs;
String prevStr;
String prevIn;
int inCount = 0;
int numOut = 8;

boolean freshInput = false;
boolean isClicked = false;
boolean ready = true;
boolean firstSend = true;

PFont f;

void setup() {
  size(700, 875);
  cp5 = new ControlP5(this);
  int c1x = 90;

  PFont labels = createFont("arial", 16);
  PFont in = createFont("arial", 12); 
  f = createFont("arial", 20); 

  myTLA = cp5.addTextlabel("M1").setText("Motor 1").setPosition(20, 50).setColorValue(0xffFFFFFF).setFont(labels);

  myTLB = cp5.addTextlabel("M2").setText("Motor 2").setPosition(20, 100).setColorValue(0xffFFFFFF).setFont(labels);

  myTLC = cp5.addTextlabel("M3").setText("Motor 3").setPosition(20, 150).setColorValue(0xffFFFFFF).setFont(labels);

  myTLD = cp5.addTextlabel("M4").setText("Motor 4").setPosition(20, 200).setColorValue(0xffFFFFFF).setFont(labels);

  rA = cp5.addRadioButton("m1D").setPosition(c1x+120, 50).setSize(20, 20).setColorForeground(color(120)).setColorActive(color(255))
    .setColorLabel(color(255)).setItemsPerRow(2).setSpacingColumn(60).addItem("M1 Forward", 1).addItem("M1 Reverse", 2);

  rB = cp5.addRadioButton("m2D").setPosition(c1x+120, 100).setSize(20, 20).setColorForeground(color(120)).setColorActive(color(255))
    .setColorLabel(color(255)).setItemsPerRow(2).setSpacingColumn(60).addItem("M2 Forward", 1).addItem("M2 Reverse", 2);

  rC = cp5.addRadioButton("m3D").setPosition(c1x+120, 150).setSize(20, 20).setColorForeground(color(120)).setColorActive(color(255))
    .setColorLabel(color(255)).setItemsPerRow(2).setSpacingColumn(60).addItem("M3 Forward", 1).addItem("M3 Reverse", 2);

  rD = cp5.addRadioButton("m4D").setPosition(c1x+120, 200).setSize(20, 20).setColorForeground(color(120)).setColorActive(color(255))
    .setColorLabel(color(255)).setItemsPerRow(2).setSpacingColumn(60).addItem("M4 Forward", 1).addItem("M4 Reverse", 2);
  
  cp5.addTextfield("m1Sinput").setPosition(c1x, 50).setSize(100, 20).setFont(in).setFocus(true).setColor(color(255, 255, 255));

  cp5.addTextfield("m2Sinput").setPosition(c1x, 100).setSize(100, 20).setFont(in).setFocus(false).setColor(color(255, 255, 255));

  cp5.addTextfield("m3Sinput").setPosition(c1x, 150).setSize(100, 20).setFont(in).setFocus(false).setColor(color(255, 255, 255));

  cp5.addTextfield("m4Sinput").setPosition(c1x, 200).setSize(100, 20).setFont(in).setFocus(false).setColor(color(255, 255, 255));
  
  myPort = new Serial(this, Serial.list()[0], 38400);
}

void draw() {
  background(0);
  //grid();
  myButton(); 
  showSpeeds(); 
  display();
  send();
  receive();
}


//-----------------------------------------------------------------------------------
// OTHER METHODS
//-----------------------------------------------------------------------------------

//working on what this does
void controlEvent(ControlEvent theEvent) {
  if (theEvent.isAssignableFrom(Textfield.class)) {
    if (theEvent.getStringValue()!="") {
      if (theEvent.getName()=="m1Sinput") {
        out[0] = theEvent.getStringValue();
      } 
      else if (theEvent.getName()=="m2Sinput") {
        out[2] = theEvent.getStringValue();
      } 
      else if (theEvent.getName()=="m3Sinput") {
        out[4] = theEvent.getStringValue();
      } 
      else if (theEvent.getName()=="m4Sinput") {
        out[6] = theEvent.getStringValue();
      }
    }
  }

  else if (theEvent.isFrom(rA)) {    
    if (theEvent.getValue() == 1.0) {
      out[1] = "F";
    } 
    else if (theEvent.getValue() == 2.0) {
      out[1] = "R";
    }
  }

  else if (theEvent.isFrom(rB)) {
    if (theEvent.getValue() == 1.0) {
      out[3] = "F";
    } 
    else if (theEvent.getValue() == 2.0) {
      out[3] = "R";
    }
  }

  else if (theEvent.isFrom(rC)) {
    if (theEvent.getValue() == 1.0) {
      out[5] = "F";
    } 
    else if (theEvent.getValue() == 2.0) {
      out[5] = "R";
    }
  }

  else if (theEvent.isFrom(rD)) {
    if (theEvent.getValue() == 1.0) {
      out[7] = "F";
    } 
    else if (theEvent.getValue() == 2.0) {
      out[7] = "R";
    }
  inCount++;
  freshInput = true;
}

//------------------------------------------------------------------------------
// My Methods
//------------------------------------------------------------------------------

//draws a white grid on the GUI, for helping set up visually
void grid() {
  stroke(255, 255, 255);
  for (int x = 0; x<700; x+=50) {
    line(x, 0, x, 850);
  }
  for (int y = 0; y<875; y+=50) {
    line(0, y, 700, y);
  }
}

void myButton() {
  fill(color(173, 255, 47));
  rect(550, 50, 100, 50);
  stroke(0);
  fill(255);
  textFont(f, 20);
  text("START", 568, 82);

  if (mouseX>=550&&mouseX<=650&&mouseY>=50&&mouseY<=100&&mousePressed) {
    isClicked = true;
  }
}

void showSpeeds() {
  fill(255);
  for (int i = 0; i<numOut; i+=2) {
    if (out[i]==null||out[i].isEmpty()) {
      text("0", 400, 70+25*i);
    } 
    else {
      text(out[i], 400, 70+25*i);
    }
  }
}

void display() {
  if (freshInput) {
    for (int i=0; i<numOut; i++) {
      print("["+i+"]"+out[i]+" ");
    }
    println("The current value of inCount is: "+ inCount);
    freshInput = false;
  }
}

void send() { 
 if (ready||firstSend){ 
  boolean full = true;
  for (int j =0; j<numOut; j++) {
    if (out[j]==null||out[j].isEmpty()) {
      full = false;
    }
  }
  if ((inCount>=numOut||isClicked)&&full) {
    String theStr = "";
    for (int i = 0; i<numOut; i++) {
      theStr = theStr+out[i];
    }    
    if (theStr != prevStr){
      myPort.write("<"+theStr+">");
      println(theStr);
      prevStr = theStr;
    }
    inCount = 0;
    isClicked = false;
  }
   ready = false;
 }
}

void receive(){
  char[] inB = new char[100];
    int serialIn = 0;
    boolean start = false;
    boolean end = false;
    
  while (myPort.available()>0&&serialIn<100){
    char inBuffer = myPort.readChar();
    if (inBuffer=='['){
      start = true;
      end = false;
    } else if (inBuffer == ']'){
      end = true;
      break;
    } else {
      inB[serialIn] = '\0';
      inB[serialIn] = inBuffer;
      serialIn++;
    }
  }
  
  if(start&&end){
    for (int i = 0; i<100; i++){
      inputs = inputs+inB[i];
    }
    println(inputs);
  }
  
  if (inputs == "Next input: "){
    ready = true;
    firstSend = false;
  }
  
  start = false;
  end = false;
  serialIn = 0;
}

The serial buffer on the Arduino only gets filled with what the other end sends it. The serial buffer on the PC only gets filled with what the Arduino sends it. There is no "garbage" being added by other processes.

Ok. I'll rephrase. I think I might be accidentally sending stuff into the serial that doesn't need to be there each time I click the button. The stuff that would be send would be extraneous copies of the speed set. Is that more accurate?

Ok. I'll rephrase. I think I might be accidentally sending stuff into the serial that doesn't need to be there each time I click the button. The stuff that would be send would be extraneous copies of the speed set. Is that more accurate?

I don't know if it is more accurate, in terms of what is happening, but it is certainly easier to test.

      myPort.write("<"+theStr+">");
      println(theStr);

This is the only code that writes to the serial port. It also shows the same data on the Processing console, so it should be easy to see that what is sent matches what you expect/see on the Arduino side. Although, I'd prefer to see:

print("Serial data being sent: <";
print(theStr);
println(">");

so that you are certain that what is sent matches what is printed.

Where's the Arduino code?

An example of the Processing output would be useful, too.

The Arduino code is in the post above the Processing code.

Here's an example of feedback from Processing. This is what I get for the first speed set.

[0]3200 [1]null [2]null [3]null [4]null [5]null [6]null [7]null The current value of inCount is: 1
[0]3200 [1]null [2]4500 [3]null [4]null [5]null [6]null [7]null The current value of inCount is: 2
[0]3200 [1]null [2]4500 [3]null [4]0000 [5]null [6]null [7]null The current value of inCount is: 3
[0]3200 [1]null [2]4500 [3]null [4]0000 [5]null [6]0000 [7]null The current value of inCount is: 4
[0]3200 [1]F [2]4500 [3]null [4]0000 [5]null [6]0000 [7]null The current value of inCount is: 5
[0]3200 [1]F [2]4500 [3]R [4]0000 [5]null [6]0000 [7]null The current value of inCount is: 6
[0]3200 [1]F [2]4500 [3]R [4]0000 [5]R [6]0000 [7]null The current value of inCount is: 7
[0]3200 [1]F [2]4500 [3]R [4]0000 [5]R [6]0000 [7]F The current value of inCount is: 8
3200F4500R0000R0000F

Directions: 0F
Directions: 1R
Directions: 2R
Directions: 3F
Speeds: 3200
Speeds: -4500
Speeds: 0
Speeds: 0
Next input:

I've added more print statements to the Arduino code. It seems that the processing interface is sending fragments of the speed set to the Arduino and that's what's messing up the motor control. Would clearing the serial after each send help remedy this problem, or would that just make it worse?

It seems that the processing interface is sending fragments of the speed set to the Arduino and that's what's messing up the motor control.

Have you updated the Processing code to print data in the same format as it is send to the serial port (between the < and >)? It does not appear that you have. Therefore, it is difficult to know exactly what is being sent to the serial port.

Would clearing the serial after each send help remedy this problem, or would that just make it worse?

You have no control over the outgoing serial buffer in Processing, so you can't "clear" anything there. You are reading everything sent the Arduino, so there is nothing to "clear" there.

Have you updated the Processing code to print data in the same format as it is send to the serial port (between the < and >)? It does not appear that you have. Therefore, it is difficult to know exactly what is being sent to the serial port.

I have the Arduino code printing data in a similar format to the way Processing sends the data but using the characters [ and ].

//within if(started&&ended)
for(int i = 0; i<numIn; i++){
      Serial.print("[");
      Serial.print("Direction: ");
      Serial.print(i);
      Serial.print(rawDir[i+1]);
      Serial.print("]");
      Serial.println();
    }
    
    for (int i = 0; i<numIn; i++){
      Serial.print("[");
      Serial.print("Speeds: ");
      Serial.print(speeds[i]);
      Serial.print("]");
      Serial.println();
    }

// at the end of the if
    Serial.println();
    Serial.print("[");
    Serial.print("Next input: ");
    Serial.print("]");
    Serial.println();

This is an example of output I get from Processing. The first speed set entered was <9876F4321R0000F0000R>. The second speed set entered was <6789R1234R0000F0000R>. The top "Speeds received" in this image reads <6340> and one of the directions is missing. The second section reads speeds of <61234> with the same speed missing. The third set has all the speeds and directions that were send.

The same thing occurs in reverse order if I scroll up to look at previous inputs:
Speeds 1: <6789123400000000> (all 4 speeds)
Speeds 2: <6123> with one direction missing
Speeds 3: <6340> with one direction missing

So basically, the last 6 sets of speeds received or sent are:
<6789123400000000> with directions RFFR
<6123> with directions R_FR
<6340> with directions F_FR
<6340> with directions F_FR
<61234> with directions F_FR
<6789123400000000> with directions RFFR

The variance in the string lengths makes me think that one side of the interface, either Processing or Arduino is sending/receiving incorrectly. That's why I am curious about clearing the Serial before sending/after receiving.