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
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.
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With