Question

I'm writing a simple C program that can read data from a USB port that is connected to my Arduino device. The Arduino outputs data at a baud rate of 9600 in chunks of 4 bytes.

I want the input from the Arduino to my computer to look something like this:

136.134.132.130.129.127.126.124.121.119.117.115.113.111.

However, I'm getting something like this:

271.274.281..2.4062.4022.40225.4021

Question: How do I get the input in my C program to neatly synchronize with out loosing data/ rereading data? Are there some kind of flags that could tell my program when the port has new data?

Code:

#include <stdio.h>   /* Standard input/output definitions */
#include <string.h>  /* String function definitions */
#include <unistd.h>  /* UNIX standard function definitions */
#include <fcntl.h>   /* File control definitions */
#include <errno.h>   /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <sys/types.h>


int open_port(void)
{
  int fd; /* File descriptor for the port */

  fd = open("/dev/tty.usbmodemfd121", O_RDWR | O_NOCTTY | O_NDELAY);
  if (fd == -1)
  {
    perror("open_port: Unable to open /dev/tty");
  }
  else
    fcntl(fd, F_SETFL, 0);

  struct termios options;
  tcgetattr(fd,&options);
  cfsetospeed(&options,B9600);
  options.c_cflag |=(CLOCAL | CREAD);
  tcsetattr(fd, TCSANOW, &options);

  return (fd);
}


int main (){

    int i;
    for(i=0; i<50; i++){

    fcntl(open_port(), F_SETFL, FNDELAY);
    char buf[5];
    size_t nbytes;
    ssize_t bytes_read;

    nbytes = sizeof(buf);
    bytes_read = read(open_port(), buf, nbytes);
    printf("%s ", buf);
    buf[0]=0;
    }

    return 0;

}
Was it helpful?

Solution

Your program does not properly open() the serial port for reading it.
In fact it repeatedly opens it two times every iteration of the for loop.
The device should be opened only once by your program.

Instead of

for (i=0; i<50; i++) {

   fcntl(open_port(), F_SETFL, FNDELAY);

   bytes_read = read(open_port(), buf, nbytes);

}

the main program should be structured like

fd = open_port();
if (fd < 0) {
    /* handle error condition */
}
rc = fcntl(fd, F_SETFL, FNDELAY);
if (rc < 0) {
    /* handle error condition */
}
for (i=0; i<50; i++) {


   bytes_read = read(fd, buf, nbytes);
   if (bytes_read < 0) {
        /* handle error condition */
    }

}
close(fd);

Your program is too "simple". It sets only a few attributes, and doesn't bother to check the return codes of system calls.

Is this supposed to be canonical or non-canonical (aka raw) mode (i.e. is the data ASCII text or binary)?
Refer to this Serial Programming Guide for proper setup of the serial port.

read data from a USB port

USB is a bus.
The device your program reads from is a serial port attached to that USBus.

Second coding issue

Your original code may print garbage data.

nbytes = sizeof(buf);
bytes_read = read(open_port(), buf, nbytes);
printf("%s ", buf);
buf[0]=0;

The bytes returned by the read() operation are not likely to be terminated by a NULL byte, so a string operation on that read buffer could exceed the bounds of the allocated array.
Code that would not misbehave would be something like:

nbytes = sizeof(buf) - 1;

bytes_read = read(fd, buf, nbytes);
if (bytes_read < 0) {
    /* handle error condition */
} else {
    buf[bytes_read] = 0; /* append terminator */
    printf("%s ", buf);
}

Note that nbytes is one less than the allocated size of the buffer.
This is to ensure that there is an available byte to store the string terminator byte when the read() operation returns a "full" buffer of nbytes.
For efficiency the assignment of nbytes should be performed before entering the for loop, rather than within the loop.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top