I have written some C++ code to talk to my arduino via serial. It just tries to make oscillations on two servo motors 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. I was using fstream and usleep() to send data before and it was working, but I’d like to use a better method than delaying by some arbitrary number.
Thanks for any help or guidance.
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;
}
The “processIncomingByte” loop inside the device may have suffered a speed problem as you are processing the previous state (handlePreviousState) immediately after you receive a new mode.
The problem may be caused by doing a Serial.print in the corresponding function while the value-data bytes are still incoming continuously from the PC. Serial print is a relatively slow process in micro-controller logic.
I am not familiar with Arduino hardware, but some lower end micro-controller board is performing software serial interface using bitbanging method, so when you transmit, the receiving is completely stopped. To verify this you can remark the Serial.print to see whether it helps.
Anyway, doing lengthy processing in the middle of incoming data stream is alway problematic, unless you have a hardware serial interface in the device with lots of FIFO buffers.
A proper way to this problem is to receive the whole message inside a buffer first and then process it only when a end-of-message marker is received. For example, insert your message inside the [] pair like [V180H90]. Reset the buffer upon the “[” and process the buffer after you receive the “]”. When you are collecting bytes into the buffer, make sure you also check for buffer overflow.