Ashish Sharma
Ashish Sharma

Reputation: 148

Serial read() issues in linux C

I have an instrument which I need to talk to by RS232. I am using C and following is the code. The problem is that, when I try to read field value (reading from meter) which is 11 byte long, one read command is reading only 8 bytes and subsequently I have to issue another read command which gives me final 3 bytes. Finally I am concatenating both read buffers to make final meaningful value.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


#define MAXWAIT         30
#define BAUDRATE        B9600 
#define TESLAMETER      "/dev/ttyS0"
#define _POSIX_SOURCE   1 /* POSIX compliant source */
#define FALSE           0
#define TRUE            1
#define NOREAD          255

volatile int STOP = FALSE;
int fd;
struct termios oldtp, newtp;

int openComPort(void)
{
    fd = open(TESLAMETER, O_RDWR | O_NOCTTY |O_NDELAY );
    if (fd <0)
    {
        perror(TESLAMETER);     
        return fd;   
    }
    else
        fcntl(fd,F_SETFL,0);
    tcgetattr(fd,&oldtp); /* save current serial port settings */
    bzero(&newtp, sizeof(newtp));
    newtp.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
    newtp.c_iflag = IGNPAR | ICRNL;
    newtp.c_oflag = 0;
    newtp.c_lflag = 0;//ICANON;
    newtp.c_cc[VINTR]    = 0;     /* Ctrl-c */
    newtp.c_cc[VQUIT]    = 0;     /* Ctrl-\ */
    newtp.c_cc[VERASE]   = 0;     /* del */
    newtp.c_cc[VKILL]    = 0;     /* @ */
    newtp.c_cc[VEOF]     = 4;     /* Ctrl-d */
    newtp.c_cc[VTIME]    = 1;     /* inter-character timer unused, 0.5 seconds read timeout */
    newtp.c_cc[VMIN]     = 0;     /* blocking or non blocking read until 1 character arrives */

    tcflush(fd, TCIFLUSH);
    tcsetattr(fd,TCSANOW,&newtp);
    return fd;
}
float readMagField()
{
    unsigned char cmd[]="FA0\r"; // Read Field
    char buff2[11] = {0x00};
    char buff3[11] = {0x00};
    float fieldFloat = 0.00;
    int n_written= 0, spot = 0, res;
    do
    {
        n_written = write( fd, &cmd[spot], 1 );
        spot += n_written;
    } while (cmd[spot-1] != '\r' && n_written > 0);
    if (n_written < 0)
    {
            printf("write() of 4 bytes failed!\n");
        return FALSE;
    }
    else
    {
        //printf("Field Read Command sent successfully %d\n",n_written);
        res = read(fd,buff2,11);   // Reads 8 bytes
        res = read(fd,buff3,11);   // Reads remaining 3 bytes
        fieldFloat = atof(strcat(buff2,buff3)); // Final string of 11 bytes here
        return fieldFloat;
    }
}

Is there something that I am doing or setting wrong? Because, I can read the complete set of characters in one go using Python serial module, but not in C. I am working on Ubuntu 12.04 LTS.

Upvotes: 2

Views: 1119

Answers (1)

MikeCAT
MikeCAT

Reputation: 75062

read() may return without reading the specified length.

read(2) - Linux manual page

RETURN VALUE

   On success, the number of bytes read is returned (zero indicates end
   of file), and the file position is advanced by this number.  It is
   not an error if this number is smaller than the number of bytes
   requested; this may happen for example because fewer bytes are
   actually available right now (maybe because we were close to end-of-
   file, or because we are reading from a pipe, or from a terminal), or
   because read() was interrupted by a signal.  See also NOTES.

How about retrying until the desired length of data have been read?

ssize_t read2(int fd, void *buf, size_t count) {
    ssize_t read_length = 0;
    while (read_length < count) {
        ssize_t delta = read(fd, buf + read_length, count - read_length);
        if (delta == -1) return -1;
        read_length += delta;
    }
    return read_length;
}

Upvotes: 2

Related Questions