Read only one byte through serial

I have a C / Python installation on my machine, I am testing with serial communication, and for some reason I never read more than 1 byte.

My setup: I have a Windows 7 machine running OpenSUSE in a virtual box. I have 2 USB-RS232 converters and an adapter between them (so this is a loop from one USB port to another).

On the Windows side, I was able to get them to communicate with each other through Python-to-Python and C-to-Python. When I use the Linux virtual machine, I can get data from C (Linux) to Python (Windows), but when I do it the other way around, I only get 1 byte. I think something is wrong with the way I open a file or read Linux C code, but I'm not sure what the problem is.

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 

Code C:

 #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 come back:

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

So Linux-> Windows write works, python shows the correct string "Hello", but for some reason I only get one byte on the Windows-> Linux side.

Does anyone see something wrong?

EDIT:
Based on the feedback I received, I tried two code settings. It seems like I cannot guarantee that all the data will be there, so I tried:

1) 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) 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); } 

It seems that the loop is not working, so that data that is not being read is failing ?? / EDIT

+4
source share
4 answers

Get good answers and incite to it (+1 around!), While all answers lead to the right conclusions (that you are not reading the data), none of the input data “fixed” the problem I encountered ,

Here is how I got it to work at last:

The settings in the termios structure killed me. While I set some of the flags, I did not set them all. So:

 tcgetattr(fd, &options); cfsetispeed(&options, B9600); cfsetospeed(&options, B9600); tcsetattr(fd, TCSANOW, &options); 

Modified by:

 tcgetattr(fd, &options); cfsetispeed(&options, B19200); cfsetospeed(&options, B19200); options.c_iflag = 0; // Disable Input flags options.c_lflag = 0; // Disable Local mode flags options.c_oflag = 0; // Disable Output flags options.c_cflag = (options.c_cflag & ~CSIZE) | CS8; //Data bits per character (8) options.c_cc[VMIN] = 50; // Wait for 50 characters or options.c_cc[VTIME] = 50; // Wait for 5 seconds options.c_cflag |= (CLOCAL | CREAD | HUPCL); // Ignore modem status lines, // enable receiver, // and hang up on last close options.c_cflag &= ~(PARENB | PARODD); //Clearing even and odd parity options.c_cflag &= ~CSTOPB; //Clear double stop bits tcsetattr(fd, TCSANOW, &options); 

With these changes, I now get the data in my Linux C code, which I write from Python.

I used these two sources for many “facts” about what parameters are for the termios structure:

+3
source

From the read (2) manual;

Upon successful completion, the number of bytes read is returned (zero indicates the end of the file), and the file position advances by this number. This is not an error if this number is less than the number of bytes requested; this can happen, for example, because fewer bytes are actually available (perhaps because we were close to the end of the file, or because we are reading from the pipe or from the terminal)

In other words, since you read the socket immediately after writing, and your computer is much faster than the serial port, most likely only one character is readable, and read(2) returns only that character.

+7
source

The man page for read says

... trying to read the number of bytes ...

Your code looks like this: it is assumed that the full buffer will always be returned by one read ; its valid for data to be returned on multiple calls.

It would also be nice to check read return -1 with errno == EINTR and try again after that (use TEMP_FAILURE_RETRY if it is running on a GNU system). read may return a transient error if it is interrupted by a signal.

+4
source

As others have answered, the C read () function executes its contract, returning only one byte.

The read () and Python read () functions are completely different.

PySerial says about read (),

Reading size bytes from the serial port. If a timeout is set, it can return fewer characters as requested. Without a timeout, it will be blocked until the requested number of bytes is read.

While API C does not provide such a guarantee. It will return any characters that are available in the buffer (even 0 if there is nothing else).

If you want the C side to behave like the Python side, you need to use another function. Something like that:

 int read_exact_chars(int fildes, char *buf, size_t nbyte) { ssize_t chars_read; int chars_left = nbyte; while (chars_left) { chars_read = read(fildes, buf, chars_left) if (chars_read == -1) { /* An error occurred; bail out early. The caller will see that we read fewer bytes than requested, and can check errno */ break; } else { buf += chars_read; chars_left -= chars_read; } } /* return the actual number of characters read */ return nbyte - chars_left; } 
+3
source

Source: https://habr.com/ru/post/1438010/


All Articles