Is there a better way to communicate via serial?

I’m making a robot that has 2 servos and 2 motors for wheels. I’m not the first person to work on this and this is the code I received. The idea is to use the serial connection to send information about wheel speed and servo angle from another computer on this robot. Time is being used to tell the servos how long to hold the position they’re in and there’s a queue that’s supposed to make the robot stop, but it doesn’t work.

Is there a better way to do this?

#include <Servo.h>
#include <QueueArray.h>

Servo Left;
Servo Right;
Servo augerMotor;
Servo chuteServo;

int LeftMotor = 90;
int RightMotor = 90;    //127
int input, number, Time, x, volume, timestamp;
int augerSpeed = 28;
unsigned long startTime;
unsigned long endTime;
boolean wrapAround = false;


class X_Stamp{
  public:
    int x;
    int timestamp;
};

X_Stamp temp_x;
QueueArray <X_Stamp> xqueue;

unsigned long time;
void setup() {
  Serial.begin(115200);
  Left.attach(5);                             // Left Wheel - Pin 5
  Right.attach(10);                           // Right Wheel - Pin 10
  Left.write(90);
  Right.write(90);
  chuteServo.attach(12);
  chuteServo.write(90);
  augerMotor.attach(13);
  augerMotor.write(90); 
}

void loop() {

  unsigned long currentTime = millis();

  if(Serial.available()){
    input=Serial.read();
    switch(input){
      case 82: RightMotor = number; break;    //R
      case 76: LeftMotor = number;  break;    //L
      case 88: x=number; 
        chuteServo.write(x);   
        break;    //X input is converted angle
      case 86: // V
        volume = number;
        startTime = currentTime;
        endTime = (volume * 5.5) + currentTime;
          augerMotor.write(augerSpeed);
        break;
      case 67:  // C
        Time=number;
        break;
      case 66: // B
        timestamp = number;
        
        temp_x.x=x;
        temp_x.timestamp=timestamp;
        xqueue.enqueue(temp_x);
        
        break;
      default: 
        number = input;
        break;    //0-180
    }
  }
  
  if(!xqueue.isEmpty()){
    temp_x = xqueue.front();
    if(temp_x.timestamp+1 <= Time){
      chuteServo.write(temp_x.x);
      xqueue.dequeue(); 
    }
  }
  if (currentTime >= endTime){
    augerMotor.write(90);
  }
  
  Left.write(LeftMotor);
  Right.write(RightMotor);
}

That statement conveys no useful information. Maybe not what you want, but the code does something. Tell us what the code actually does and how that differs from what you want the code to do.

To debug, insert some serial prints in the code to follow execution and monitor variable values.

For this to work the Serial communication needs to be in binary (for number) and very precise as there is no error management if you skip a byte for whatever reason.

If you control what is sending the commands, I’d recommend having a start and/or end marker for the communication

(+ what @groundFungus said)

The serial input basics tutorial proved very helpful to me and shows how to incorporate the start and end markers suggested by @J-M-L.

@groundFungus did beat me to it :wink:

Anyway, work your way through it, specifically the examples 3, 4 and 5; the latter will be the one that you will probably end up using, but the others explain how

Have a look at the SerialTransfer library.
Never tried it myself but the description is promising, it may help you get your communications working better.

1 Like
input = Serial.read(); 

I dont think that it works this way. try reading the input character by character and assemble it. Also try printing the input to see what u r getting.

if (mySerial.available())
  {
    ch = mySerial.read();
    if (ch != '\r')
    {
      receivedChars[i++] = ch;
    }
    else 
    {
      receivedChars[i] = '\0';
      i = 0; 
     }
}

something like this may be.

I don’t think this helps. It’s clearly not a full ASCII protocol as a value (between 0-180 apparently) is sent as a byte

      default: 
        number = input;
        break;    //0-180

there are a few ASCII characters used as commands:
82 is ‘R’ → to set the angle of the Right Servo
76 is ‘L’ → to set the angle of the Left Servo
88 is ‘X’ → to set the angle of the Chute Servo
86 is ‘V’ → to set the angle of the Auger Servo at a fixed value for a certain duration
67 is ‘C’ → to set the Chute Servo to the ‘X’ value at some point (?)
66 is ‘B’ → to record an event in the queue

and my guess is that the protocol is a stream of <byte value><command>, for example send the byte value 64 followed by ‘R’

serialCom.write(64);   // 64°
SerialCom.write('R');  // for the right motor

and you’ll set the Right Servo at 60°

Challenge being that the <byte value> can be one of the commands’ ASCII code and thus if this is sent then all bets are off. (for example try setting the Right motor to 82°, it would mean sending 82 82 and they both would be interpreted as setting the Right motor to whatever was the last number recognised.)

=> hence the need for a start byte or not using ASCII characters for the commands: since angles are 0-180, commands could be anywhere above 181 (to 255) and then the switch()/case would work as designed.