Go Down

Topic: Read serial data using C++ (in Linux) (Read 9 times) previous topic - next topic

Zac

Hello,

I've got my arduino spitting out a test string over and over again:

Code: [Select]


void setup() {               
Serial.begin(9600);
}

void loop() {
  Serial.println("Data : Here is Some data");
}



And I want to read this as a char array into a program I have written in C++ (compiled using the GNU compiler set in netbeans).

So far I have been using this code:

Code: [Select]


#include "./include/serial.h"

Serial::Serial() {

// Set up initial values
dev = "/dev/ttyS0";
baud = 9600;
dataBits = 8;
bufferSize = 1000;
parity = PARITY_8N1;
bufferIndex = 0;
blocking = 0;
}

Serial::~Serial() {

// Kills the port
closePort();
}

int Serial::initPort() {

// Try to open serial port with r/w access
printf("SERIAL: Opening %s at %i baud...", dev, baud);
fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY);

        // As long as port is actually open...
if(fd != -1) {

// Share the good news
printf("OK\n");

// Display blocking info
if (blocking == 1) printf("SERIAL: Blocking enabled\n");
else printf("SERIAL: Blocking disabled\n");

// Configure port settings
fcntl(fd, F_SETFL, FNDELAY);

// Save current port settings so we don't corrupt anything on exit
struct termios options;
tcgetattr(fd, &options);

// Convert integer baud to Baud type
// Default to 9600 baud if none specified
switch (baud) {
case 4800:
cfsetispeed(&options, B4800);
cfsetospeed(&options, B4800);
break;
case 9600:
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
break;
case 38400:
cfsetispeed(&options, B38400);
cfsetospeed(&options, B38400);
break;
case 57600:
cfsetispeed(&options, B57600);
cfsetospeed(&options, B57600);
break;
case 115200:
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
break;
default:
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
break;
}

// Set options for proper port settings
// ie. 8 Data bits, No parity, 1 stop bit
options.c_cflag |= (CLOCAL | CREAD);

switch (parity) {
case PARITY_7E1:
// Parity, odd, 1 stop bit (7E1)
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
options.c_cflag &= ~CSTOPB;
printf("SERIAL: Parity set to 7E1\n");
break;
case PARITY_8N1:
// No parity, 1 stop bit (8N1)
options.c_cflag |= CS8;
printf("SERIAL: Parity set to 8N1\n");
break;
default:
// No parity, 1 stop bit (8N1)
options.c_cflag |= CS8;
printf("SERIAL: No parity set\n");
break;
}

// Set number of data bits.  Default is 8.
switch (dataBits) {
case 7:
options.c_cflag |= CS7;
printf("SERIAL: Databits set to 7\n");
break;
case 8:
options.c_cflag |= CS8;
printf("SERIAL: Databits set to 8\n");
break;
default:
options.c_cflag |= CS8;
printf("SERIAL: Databits not set!\n");
break;
}

// Turn off hardware flow control
options.c_cflag &= ~CRTSCTS;

options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);


// Write our changes to the port configuration
tcsetattr(fd, TCSANOW, &options);
}

// There was a problem, let's tell the user what it was
else {
printf("FAIL\n");
perror("Error opening port:");
closePort();
exit(1);
}

// Send back the public port file descriptor
return fd;
}

void Serial::flushPort() {

// If the port is actually open, flush it
if (fd != -1) ioctl(fd, TCFLSH, 2);
}

int Serial::getData(char* data) {

// If the port is actually open, read the data
if (fd != -1) {
// Grab the data and return the nubmer of bytes actually read
return read(fd, data, sizeof(data));
}
// Port is closed!
else return -1;
}

int Serial::sendData(char* data) {

// If the port is actually open, send the data
if (fd != -1) {
// Send the data and return the nubmer of bytes actually written
//printf("Writing %s...\n", data);
return write(fd, data, sizeof(data));
}
// Port is closed!
else return -1;
}

char Serial::getChar() {
int delay = 0;
switch (blocking) {
// Non-blocking mode enable, so use the buffer
case 0:
if (bufferIndex > 0) {
// Yes, so grab the byte we need and shift the buffer left
char c = buffer[1];
for (int j = 1; j < (bufferIndex + 1); j++) {
buffer[j] = buffer[j + 1];
}
bufferIndex--;
// Send back the firt byte in the buffer
return c;
}
// If the port is actually open, grab a byte
if (fd != -1) {
// Return the byte received if it's there
int n = read(fd, &temp[0], 1);
if (n > -1) {
// Just 1 byte, so return it right away
if (n == 1) return temp[0];
// More than 1 byte there, so buffer them and return the first
else {
for (int i = 0; i < (n - 1); i++) {
bufferIndex++;
buffer[bufferIndex] = temp[i + 1];
}
return temp[0];
}
}
else return -1;
}
break;
// Blocking mode enabled, so wait until we get a byte
case 1:
if (bufferIndex > 0) {
// Yes, so grab the byte we need and shift the buffer left
char c = buffer[1];
for (int j = 1; j < (bufferIndex + 1); j++) {
buffer[j] = buffer[j + 1];
}
bufferIndex--;
// Send back the firt byte in the buffer
return c;
}
// If the port is actually open, grab a byte
if (fd != -1) {
// Return the byte received if it's there
int n = -1;
while (n < 1) {
n = read(fd, &temp[0], 1);
if (n == -1) usleep(10000); // wait 10ms
delay++;
if (delay == 5) return -1;
}
if (n > -1) {
// Just 1 byte, so return it right away
if (n == 1) return temp[0];
// More than 1 byte there, so buffer them and return the first
else {
for (int i = 0; i < (n - 1); i++) {
bufferIndex++;
buffer[bufferIndex] = temp[i + 1];
}
return temp[0];
}
}
else return -1;
}
// Port is closed!
else return -1;
break;
// Blocking variable is messed up
default:
printf("SERIAL: Error with blocking setting!\n");
return -1;
break;
}
// Should never get here
return -1;
}

int Serial::sendChar(char data) {
switch (blocking) {
// Non-blocking mode
case 0:
// If the port is actually open, send a byte
if (fd != -1) {
// Send the data and return number of bytes actually written
write(fd, &data, 1);
return 1;
}
else return -1;
break;
// Blocking mode
case 1:
// If the port is actually open, send a byte
if (fd != -1) {
// Send the data and return number of bytes actually written
write(fd, &data, 1);
return 1;
}
else return -1;
break;
// Blocking variable is messed up
default:
printf("SERIAL: Error with blocking setting!\n");
return -1;
break;
}
}

void Serial::closePort() {

// If the port is actually open, close it
if (fd != -1) {
close(fd);
printf("SERIAL: Device %s is now closed.\n", dev);
}
}



In summary the Serial object can be created and used to read char values using .getChar().

This seems to work fine however calling .getChar() only sometimes contains the chars that I'm interested in and most of the time contains junk characters. Also the message send from the arduino is frequently chopped up with junk characters inbetween.

Can someone direct me to the best way to read the serial data so that I only recieve the string I'm interested in.

Thanks.


PaulS

Code: [Select]
if (bufferIndex > 0) {
// Yes, so grab the byte we need and shift the buffer left
char c = buffer[1];

Why are you getting the 2nd element in buffer, instead of the first?

I guess because of this, where you never actually store anything in the first element.
Code: [Select]
for (int i = 0; i < (n - 1); i++) {
bufferIndex++;
buffer[bufferIndex] = temp[i + 1];
}

bufferIndex should be incremented AFTER storing the value, not before. Also, after storing the value, you should add a NULL to the buffer;
Code: [Select]
for (int i = 0; i < (n - 1); i++) {
buffer[bufferIndex] = temp[i + 1];
bufferIndex++;
buffer[bufferIndex] = '\0';
}


Are you using blocking mode, or non-blocking mode?

The above are issues I see in the non-blocking section. There are similar issues in the blocking code, too.

Zac

I am using non blocking:

Essentially that serial coms code was not written by me but rather found by google.

To use it I did the following:

Code: [Select]

#include "./include/serial.h"
#include <string.h>

int main(int argc, char** argv) {


char xd= 'a';

Serial port;

port.dev = "/dev/ttyUSB0";

port.baud=9600;
port.dataBits = 8;
        port.blocking = 1;

int result = port.initPort();

       
printf("Init returned: %i\n", result);


   
while (result != -1) {

xd = port.getChar();
        if (xd != 'p') {printf("%c", xd);}


}

port.closePort();
return -1;
}




Zac

I'm essentially looking for a way to re-create the function of the serial monitor in C++ on Linux (netbeans) so I can take the char array sent from the arduino and pass it into a char array in my C program that I can then tokenize and do further processing on.



PaulS

Quote
Essentially that serial coms code was not written by me but rather found by google.

You are using it. That makes it yours. It has some issues that I pointed out. Fix those, and see if that doesn't make your problem go away.

Go Up