Read serial data using C++ (in Linux)

Hello,

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

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:

#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.

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.

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;

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.

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:

#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;
}

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.

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.

Ok I cut all the things out the the getChar function that I didn't need and I'm left with:

char Serial::getChar() {
      
			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 first 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);
		
						return temp[0];
					
				}
				else return -1;
			
		}

When I run this code:

#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;
	
	int result = port.initPort();


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


    
	while (result != -1) {

	xd = port.getChar();
        printf("%c" ,xd);
				
			}

	port.closePort();			
	return -1;
}

I get all the letters sent from my arduino repeated like the following:

DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaattttttttttttttttttttttttttttttttttttttttttttttttaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:::::::::::::::::::::::::::

Ok this is strange:

if I add in a delay into the getChar() command it seems to function but there is a big delay on what is being transmitted and what is recieved:

char Serial::getChar() {

			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 first 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);
		usleep(10000); // delay to allow transmission of new byte 
						return temp[0];
                                                
				}
				else return -1;
			
}

I know this if I change my arduino code to send a potentiometer value that I vary and then look at the reported value: there is a significant delay between turning the knob and the serial report.

 // Global Variables
 int LEDPin1 = 2;
 int LEDPin2 = 3;
 int PotPin1 = 0;
 
 
void setup() {                
  // initialize the digital pin as an output.
  pinMode(LEDPin1, OUTPUT);
  pinMode(LEDPin2, OUTPUT); 
 Serial.begin(9600); 
}

void loop() {
  int delayValue = analogRead(0);
  Serial.println("Data:Here is Some data");
  Serial.print(Delay : );
  Serial.println(delayValue);
  digitalWrite(LEDPin1, HIGH);  // set the LED on
  digitalWrite(LEDPin2, LOW);    // set the LED off
  delay(delayValue);              // wait for a second
  digitalWrite(LEDPin1, LOW);    // set the LED off
  digitalWrite(LEDPin2, HIGH);   // set the LED on
  delay(delayValue);              // wait for a second
}

if I add in a delay into the getChar() command it seems to function but there is a big delay on what is being transmitted and what is recieved

Well, now there's a surprise.

If you don't want to fix the code properly, why are you posting here?

I'm a biochemist and I have a PhD in electrochemistry, I am not an expert in electronics or programming but I can just about get by, and I'm doing this amature programming in an attempt to get serveral pieces of instrumention to talk to each other (A peristatic pump, a signal generator and a relay board).

I'm posting here to ask for help from people who have a better understanding of electronics and programming than I do. I'm sorry if you don't think that is a good enough reason to post here. I was under the impression that this is what this forum is for (asking for help). If not maybe you could point me in the direction of a place where I can get some help.

This IS a great place to get help. Help was offered. You chose to ignore it.

There is no need to add delays to make serial data transmission work. The code you first posted had blocking and non-blocking methods that had some issues. I suggested you should fix those issues. I still think you should go back to that code, and fix the issues.

If you would rather add delays, that will slow things down, as you've observed. If you can live with that, fine. If not, get rid of them and fix the original code.