Pages: [1]   Go Down
Author Topic: Read serial data using C++ (in Linux)  (Read 9765 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 19
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello,

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

Code:

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:

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

 
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 653
Posts: 50883
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
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:
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:
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.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 19
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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


Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 19
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.


Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 653
Posts: 50883
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

0
Offline Offline
Newbie
*
Karma: 0
Posts: 19
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Code:
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:

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:

Quote
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaattttttttttttttttttttttttttttttttttttttttttttttttaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:::::::::::::::::::::::::::

« Last Edit: February 17, 2011, 12:00:22 pm by Zac » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 19
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:

Code:
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.

Code:


 // 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
}

Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 653
Posts: 50883
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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?
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 19
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.

Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 653
Posts: 50883
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Pages: [1]   Go Up
Jump to: