Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Only able to read one byte via serial

I've got a C/Python setup on my machine, I'm doing some testing with serial communications and for some reason I'm never reading more than 1 byte back.

My set up: I have a windows 7 machine, running OpenSUSE in a virtual box. I have 2 USB-RS232 converters and an adaptor between them (so it's a loop from one usb port to the other).

On the Windows side I was able to get them to communicate with each other via Python-to-Python, and C-to-Python. Once I use the Linux VM, I can get data from the C (Linux) to the Python (Windows), but when I do it the other way around I only get 1 byte back. I'm thinking it's something wrong with how I open the file or execute the read on the Linux C code, but I'm not sure what could be the issue.

Python Code (using PySerial):

>>> import serial
>>> ser = serial.Serial(3)
>>> ser
Serial<id=0x2491780, open=True>(port='COM4', baudrate=9600, bytesize=8, 
parity='N', stopbits=1, timeout=None, xonxoff=False, rtscts=False, dsrdtr=False)
>>> ser.read(5)
'Hello'
>>> ser.write("hi you")
6L

The C code:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>

int open_port()
{
    int fd;
    fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
    if(fd < 0)
      perror("open_port: Unable to open /dev/ttyUSB0 - ");
    else
      fcntl(fd, F_SETFL, 0);
    return fd;
}

int swrite(int fd, char * str)
{
    int n;
    n = write(fd, str, strlen(str));
    if (n<0)
        printf("write() of %d bytes failed\n", strlen(str));
    return n;
}

int main()
{
    int fd, databytes;
    char buf[100] = {0};
    struct termios options;

    fd = open_port();

    //Set the baud rate to 9600 to match
    tcgetattr(fd, &options);
    cfsetispeed(&options, B9600);
    cfsetospeed(&options, B9600);
    tcsetattr(fd, TCSANOW, &options);
    tcgetattr(fd, &options);

    databytes = swrite(fd, "Hello");
    if(databytes > 0)
      printf("Wrote %d bytes\n", databytes);

    databytes = read(fd, buf, 100);
    if(databytes < 0)
      printf("Error! No bytes read\n");
    else
      printf("We read %d bytes, message: %s\n", databytes, buf);

    close(fd);

    return 0;
}

And I'm getting back:

mike@linux-4puc:~> gcc serial_com.c
mike@linux-4puc:~> ./a.out 
Wrote 5 bytes
We read 1 bytes, message: h

So the Linux->Windows write is working, python is showing the correct "Hello" string, but for some reason I'm only getting one byte back on the Windows->Linux side of things.

Anyone see anything wrong?

EDIT:
Based on the feedback that I've gotten, I've tried two tweaks to the code. Sounds like I can't guarantee that all the data will be there, so I've tried:

1) a sleep

    if(databytes > 0)
      printf("Wrote %d bytes\n", databytes);
    sleep(15);                 // Hack one to get the data there in time, worked
    databytes = read(fd, buf, 100);

2) a while loop

while(1){  // Hack two to catch the data that wasn't read the first time. Failed
           // this only saw 'h' like before then sat waiting on the read()
  databytes = read(fd, buf, 100);
  if(databytes < 0)
    printf("Error! No bytes read\n");
  else
    printf("We read %d bytes, message: %s\n", databytes, buf);
}

Seems the loop doesn't work, so does the data not read get trashed?? /EDIT

like image 325
Mike Avatar asked Oct 05 '12 13:10

Mike


People also ask

How many bits of data can a serial port transfer at one time?

Most serial ports use between five and eight data bits. Binary data is typically transmitted as eight bits. Text-based data is transmitted as either seven bits or eight bits.

How does the serial port transfer a byte?

To transmit a byte, the serial device driver program (running on the CPU) sends a byte to the serial port"s I/O address. This byte gets into a 1-byte "transmit shift register" in the serial port. From this shift register bits are taken from the byte one-by-one and sent out bit-by-bit on the serial line.

Can you read and write from the same serial port?

You can indeed simultaneously read and write through the serial port. At the hardware level, the serial port (the UART) is a transmitter and a receiver, which are almost independent. At the software level, they are both handled through interrupts that read from / write to a software buffer.

What is a serial port buffer?

The input buffer is computer memory allocated by the serial port object to store data that is to be read from the device. When reading data from your device, the data flow follows these two steps: The data read from the device is stored in the input buffer.


2 Answers

From the read(2) manual;

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)

In other words, since you read from the socket right after writing and your computer is quite a lot faster than the serial port, there is most likely only a single character available to read and read(2) returns only that character.

like image 68
Joachim Isaksson Avatar answered Sep 19 '22 18:09

Joachim Isaksson


The man page for read says

...attempts to read up to count bytes...

Your code looks like it assumes the full buffer will always be returned by a single read; its valid for the data to be returned over several calls.

It'd also be good practice to check for read returning -1 with errno == EINTR and retry after this (use TEMP_FAILURE_RETRY if running on a GNU system). read may return a transient error if it is interrupted by a signal.

like image 25
simonc Avatar answered Sep 22 '22 18:09

simonc