Servo's spasm while sending code via serial with C++. Skipping data?

Hi there
I have written some C++ code to talk to my arduino via serial. It just tries to make oscillations right now using sine and cosine, but it is skipping data. I'm not sure why this is happening. I am using the termios.h for the serial stuff. The output from C++ is something like "V180H90" i.e. Vertical 180, Horizontal 90. Thanks for any help!

My arduino code

#include <Servo.h>

typedef enum { NONE, GOT_V, GOT_H } states;
states state = NONE;
Servo pan;
Servo tilt;
int laser = 11;
unsigned int currentValue;

int v_pan = 0;
int v_tilt = 0;

void setup()
{
  pan.attach(10);
  tilt.attach(9);
  
  Serial.begin(9600);
  state = NONE;
}

void processVertical(const unsigned int value)
{
  Serial.print("Vertical = ");
  Serial.println(value);
  int result = 1300 + (value - 90) * 2;
  //Serial.println(result);
  tilt.writeMicroseconds(result);
}

void processHorizontal(const unsigned int value)
{
  Serial.print("Horizontal = ");
  Serial.println(value);
  int result = 1500 + (value - 180) * 1;
  //Serial.println(result);
  pan.writeMicroseconds(result);
}

void handlePreviousState()
{
  switch(state)
  {
    case GOT_V:
      processVertical(currentValue);
      break;
    case GOT_H:
      processHorizontal(currentValue);
      break;
  }
  currentValue = 0;
}

void processIncomingByte (const byte c)
{
  if (isdigit(c))
  {
    currentValue *=10;
    currentValue += c - '0';
  }
  else
  {
    handlePreviousState();

    switch (c)
    {
      case 'V':
        state = GOT_V;
        break;
      case 'H':
        state = GOT_H;
        break;
      default:
        state = NONE;
        break;
    }
  }
}

void loop()
{
  if(Serial.available() > 0) 
  { 
    processIncomingByte(Serial.read());
  } 
  digitalWrite(laser, HIGH);
}

//check out writeMicroseconds

My C++ Code

// Program for sending data to serial

#include <iostream>
#include <sstream>
#include <string>
#include <termios.h>
#include <fcntl.h>
#include <math.h>

using namespace std;

//open serial port
int openPort(string path)
{
  int fd; //file descriptor for port
  fd = open(path.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
  if (fd == -1)
    cerr << "Cannot open port" << endl;
  else
    fcntl(fd, F_SETFL, 0);
  return (fd);
}

//set options for an open serial port
void setOptions(int fd)
{
  struct termios options;
  tcgetattr(fd, &options);
  cfsetispeed(&options, B9600);
  cfsetospeed(&options, B9600);

  //No parity 8N1
  options.c_cflag &= ~PARENB;
  options.c_cflag &= ~CSTOPB;
  options.c_cflag &= ~CSIZE;
  options.c_cflag |= CS8;

  //No flow control
  options.c_cflag &= ~CRTSCTS;

  //Turn off s/w flow control
  options.c_iflag &= ~(IXON | IXOFF | IXANY);
  
  //Turn on read and ignore ctrl lines
  options.c_cflag |= (CLOCAL | CREAD); 

  if( tcsetattr(fd, TCSANOW, &options) < 0) { 
    cerr << "Could not set attributes" << endl; 
  }
}

//write to serial port
void writePort(int fd, string data)
{
  int n = write(fd, data.c_str(), 9);
  if (n < 0)
    cerr << "Cannot write to port" << endl;
}

int main() {
  string path = "/dev/tty.usbmodemfd131";
  //string path = "/dev/tty.usbmodemfa141";
  int fd = openPort(path);
  setOptions(fd);

  stringstream ss;
  string output;
  unsigned short vertical = 0;
  unsigned short horizontal = 0; 
  unsigned short freq = 10;
  
  for(int i = 0; i < 360; i++) {
    vertical = ((cos(i * freq * ((M_PI)/180))) + 1) * 90;
    horizontal = ((sin(i * freq * ((M_PI)/180))) + 1) * 90;
    ss << "V" << vertical << "H" << horizontal << endl; 
    output = ss.str();
    ss.str("");
    writePort(fd, output);
//    cout << output; //DEBUG
  }

  close(fd);
  return 0;
}

Servo test code for use with the serial monitor.

// zoomkat 10-22-11 serial servo test
// type servo position 0 to 180 in serial monitor
// or for writeMicroseconds, use a value like 1500
// for IDE 0022 and later
// Powering a servo from the arduino usually *DOES NOT WORK*.

String readString;
#include <Servo.h> 
Servo myservo;  // create servo object to control a servo 

void setup() {
  Serial.begin(9600);
  myservo.writeMicroseconds(1500); //set initial servo position if desired
  myservo.attach(7);  //the pin for the servo control 
  Serial.println("servo-test-22-dual-input"); // so I can keep track of what is loaded
}

void loop() {
  while (Serial.available()) {
    char c = Serial.read();  //gets one byte from serial buffer
    readString += c; //makes the string readString
    delay(2);  //slow looping to allow buffer to fill with next character
  }

  if (readString.length() >0) {
    Serial.println(readString);  //so you can see the captured string 
    int n = readString.toInt();  //convert readString into a number

    // auto select appropriate value, copied from someone elses code.
    if(n >= 500)
    {
      Serial.print("writing Microseconds: ");
      Serial.println(n);
      myservo.writeMicroseconds(n);
    }
    else
    {   
      Serial.print("writing Angle: ");
      Serial.println(n);
      myservo.write(n);
    }

    readString=""; //empty for next input
  } 
}

It just tries to make oscillations right now using sine and cosine, but it is skipping data.

You are echoing data back to the C++ application. What is it receiving? What proof do you have that the Arduino is skipping data?

More importantly, how much time is the C++ application allowing for the Arduino to receive the data, deal with it, and reply before it shoves more data down the serial port?

Your code for the Arduino needs to ignore the endl that you are adding to the string being sent.

Mark

PS a flush of the PC's output way also help (flush forces anything in the output buffer to be written to the device).

M

mightcouldb1:
it is skipping data. I'm not sure why this is happening.

There is no rate limiting in the C++ application so it will spew out the sequence of command strings as fast as the serial port will take them. Your Arduino sketch sends out debug trace statements that are longer than the incoming command strings, so it will quickly fill up its serial transmit buffer and then be rate limited by the serial transmission. This means that it won't be able to handle the incoming commands as fast as they are being received. This in turn means that the Arduino's receive buffer will overflow, resulting in some of the incoming commands being discarded. Because your parser is relatively naive about the incoming message format, it's quite likely that this could result in parts of several command strings being smashed together and recognised as a valid command, although the values received would be completely wrong. I'd expect this to cause missed and wrong commands.

I'd suggest that you fix the problem at source by changing your C++ code to send the commands at a fixed rate that is less than the capacity of the serial port so that the commands are sent in 'real time' rather than having your timing defined simply by the serial line speed, and reduce the amount of debug output from your Arduino so that it sends less than it receives.

Once you sort out the flow control issues, the rest of your logic looks sound.

Thanks for the replies guys! I was able to get things working reliably just by removing the Serial.println() statements from my arduino code. I am going to implement a better solution for this. Probably by writing to a buffer that expects and open and close square brackets as start and end. I'll also check for overflow. I thought I might be able to do something by using the CTS/RTS signals in termios.h but I don't think I grasp how flow control works in that respect. Any suggestions/info?

I will also play around with two way communication using simpler data so that I can understand this in more detail as it seems quite important. Little bits of waste seem like they can turn into a great deal when you can only send and receive data through one channel.

I would appreciate any links, information, or resources you can provide me to better understand how this all works.

If you want you can do flow control in software using XON/XOFF, but there are very few situations where that should be necessary.

This does not seem like one of them - a far better approach in this case would be to limit the rate of sending commands so that they are sent in real time. The timing of your command sequences should be controlled explicitly (by controlling the rate at which the commands are sent) rather than have them controlled by congestion on the serial link.

@Peter:
What do you suggest the best way to send commands in real time is? I was using a delay function previously, but I wasn't sure that was an ideal solution. I am fairly new to serial port communication. The C++ code will eventually be sending data that has been processed from a live webcam stream so real time processing will be fairly important.

Thanks!

I was using a delay function previously, but I wasn't sure that was an ideal solution.

Real-time and delay do not belong in the same paragraph.

The C++ code will eventually be sending data that has been processed from a live webcam stream so real time processing will be fairly important.

Then delay() is absolutely out of the question.

Writing the code to end commands with terminator eg VT102;HT100; will work much better. Using the start of the next command will cause a delay in the execution to the command. Eg Sending

VT102HT100

will delay execution of the HT100 until you send the next line of commands.

Mark

So if made a function that sends something like [V100H200]
Execute when ']' is received, then clear buffer
How can I tell that servos have executed full movement? Can I check for that and send an ASCII character once rotation is complete?
For instance a rotation of 180 degrees will take longer than 10 degrees, and if I have a lot of 180 degree rotations one after another, won't my buffer start to get clogged?

mightcouldb1:
@Peter:
What do you suggest the best way to send commands in real time is? I was using a delay function previously, but I wasn't sure that was an ideal solution. I am fairly new to serial port communication. The C++ code will eventually be sending data that has been processed from a live webcam stream so real time processing will be fairly important.

Thanks!

Design your protocol so that the serial stream does not ever get congested. In your case where all the transmission is initiated on the PC side, this means rate-limiting the transmission. In your final system where the coordinates are derived from a video stream I suppose the maximum update rate will be the frame rate of the video stream, which is going to be very slow relative to the serial port speed so you are unlikely to have any problems. In your test system where you are generating commands artificially, write your test code to send the commands at regular intervals (ideally somewhere similar to the frequency you expect in your final system) - using delays to achieve this is fine.

Don't allow your pc to spit data at its whim, force it to wait for an acknowledge that the command currently executing has completed.

ajofscott:
Don't allow your pc to spit data at its whim, force it to wait for an acknowledge that the command currently executing has completed.

I don't like that approach. It adds unnecessary latency, and introduces the possibility of a livelock if any communication gets lost or corrupted. Just rate limiting the transmission is sufficient.

So if made a function that sends something like [V100H200]
Execute when ']' is received, then clear buffer

Yes, that the idea, but no need to clear "the buffer" in fact what buffer. Reading a char/byte removes it for Serials buffer.

Mark

You could take a look at the very advanced method of "single char look ahead" but ... It makes use of peek().

M