C-program using RS232 does only work when starting minicom - c

I'm using a program for RS232 communication. The program works fine so far, but i need to start and quit minicom (and eventually switch off hardware flow control) before starting the program.
Below is the code I use to initialize the port. I already had a look at
http://www.cmrr.umn.edu/~strupp/serial.html
and tried different further flags/options, such as explicitly set hardware flow control off, but it didn't help.
int open_port(void)
{
int fd;
#ifdef DEBUG
printf("open port\n");
#endif
fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
perror("open_port: Unable to open /dev/ttyS0");
}
else
{
if (port_set==0){
port_set=1;
struct termios options;
tcgetattr(fd, &options);
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
options.c_cflag |= (CLOCAL | CREAD);
tcsetattr(fd, TCSANOW, &options);
}
}
return (fd);
}

For non-canonical mode, you need to use the macro cfmakeraw to simplify various settings required. Otherwise the program performs an blocking read until a terminating character like 0x0A is detected, which may never be sent. Insert this line
cfmakeraw(&options);// make raw
right after the line
tcgetattr(fd, &options);

Related

POSIX read() call remains blocked forever regardless of what's set in VTIME

I need to configure the UART settings such that the read() call remains blocked until a certain time before it's unblocked again if it didn't receive any data within the timeout. So if the timeout is 5 seconds, it remains blocked till 5 seconds max if it doesn't receive any byte and then unblocks...
I tried using VMIN which should block the read() call until no character is read within the time allowed, after which the call to read() returns 0, but that doesn't seem to be the case for me: the read() call remains blocked forever and as soon as I enter stuff in a minicom serial session, read() unblocks and then goes back to getting blocked again.
I'm not sure if it's taking into account VTIME setting, or maybe I'm misconfiguring it.
Rather, would select() with a timeout be a better approach?
#define SERIAL_PORT "/dev/ttyUSB4"
pthread_t td;
int fd;
int SerialOpen()
{
struct termios term;
fd = open(SERIAL_PORT, O_RDWR);
if (fd < 0)
{
perror ("failed to open");
return -1;
}
bzero(&term, sizeof(term));
cfmakeraw(&term);
term.c_cflag |= CREAD;
tcgetattr(fd, &term);
term.c_iflag &= ~ICRNL;
term.c_iflag &= ~INLCR;
term.c_iflag &= ~IGNBRK;
term.c_oflag &= ~OCRNL;
term.c_oflag &= ~ONLCR;
term.c_oflag &= ~OPOST;
term.c_lflag &= ~ICANON;
term.c_lflag &= ~ISIG;
term.c_lflag &= ~IEXTEN;
term.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ECHOCTL|ECHOPRT|ECHOKE);
cfsetspeed(term, B115200); // set baud rate
term.c_cflag &= ~CSTOPB;
term.c_cflag |= CS8;
// disable flow control
term.c_cflag &= ~CRTSCTS;
term.c_iflag &= ~(IXON | IXOFF);
term.c_cflag &= ~PARENB; // no parity
term.c_cc[VTIME] = 50; // Wait for up to 5s (50 deciseconds), returning as soon as any data is received.
term.c_cc[VMIN] = 0;
if ( (tcsetattr(fd, TCSANOW, &term)) < 0)
{
perror ("Failed to set attr");
return -1;
}
return 1;
}
void *Rx(void *arg)
{
char buff[100] = {0};
while(1)
{
int sz = read(fd, buff, sizeof(buff)); // block until VTIME times out
if (sz < 0)
{
perror ("Read failed");
}
printf ("Received bytes %d: %s\n", sz, buff);
}
}
int main()
{
int ret = SerialOpen();
if (ret < 0)
{
return -1;
}
if (pthread_create(&td, NULL, Rx, NULL) != 0)
{
printf("Fail to create thread!\n");
}
pthread_join(td, 0);
return 0;
}
Caveat: Prefaced by the top comments.
Be sure you're applying the fixes I suggested in the comments above.
select may have the same issues as VMIN/VTIME. select operates at a level after the TTY layer [which is where VMIN/VTIME operate]. At a lower level is the USB driver. If the TTY layer isn't set up correctly, select may have the same issues as read (i.e. probably not the issue). Although, you might do: open(/dev/whatever,O_RDWR | O_NONBLOCK)
But, I suspect that the issue is at a more fundamental/lower level. I think the USB layer/level is part of the problem.
USB-to-RS232 cables are tricky. The cable may need active H/W flow control enabled to operate. So, just disabling H/W flow control via (e.g.) clearing CRTSCTS may not work too well.
Double check: Are you sure that you are getting all the way through the configuration/initialization of the device. Could the open be hanging? You could add debug printf statements throughout the code [easier than using gdb for this].
What is the specific manufacturer and device for the USB cable? What is the specific manufacturer/model of the end/UART device? Although the standard/generic USB serial driver (e.g. usbserial) should work, some vendors need a specific driver (e.g. FTDI has its own USB level driver). So, you may need to consult the manufacturer's website/datasheet for the specific devices in question.
Also, sometimes a USB cable shows up on two different /dev/tty* devices. You may have the wrong one. You may need ttyS* instead of ttyUSB* or the device can show up as (e.g.) ttyACM* !!! This can occur even if ttyS* or ttyUSB* are [also] present.
Look at:
ls -l /dev
/proc/devices
/sys/devices
lsmod
lsusb
lspci
dmesg [and, maybe, syslog output]
Did you [or the system] modprobe usbserial?
Specifically, the dmesg output should say which tty* the device is connected to.
Some resources (from a "all the words" websearch on linux /dev USB uart cable):
https://www.cyberciti.biz/faq/find-out-linux-serial-ports-with-setserial/
https://unix.stackexchange.com/questions/81754/how-to-match-a-ttyusbx-device-to-a-usb-serial-device

File descriptor valid, but call to read() returns -1

My project uses serial ports on an ARM dev board.
The older version of the board (TS-7800) continues to work. I must upgrade to the newer version of the board (TS-7800-V2) to complete the project.
On the new board, I can open a file descriptor for the serial port, but when I read from the port, a -1 is returned. Here is the open file code:
gps_fd = initPort("/dev/ttts4", "COM3", O_RDWR, B19200);
That is a function call in main.c. Here is
initPort()
int initPort( char *port, char *name, int oflags, speed_t baudRate ){
int fd; //File descriptor
fd = open(port, oflags); //Open the port like a file
printf("fd = %d\n", fd); //Print file descriptor to terminal
assert(fd > 0); //Open returns -1 on error
struct termios options; //Initialize a termios struct
tcgetattr(fd, &options); //Populate with current attributes
cfsetospeed (&options, baudRate); //Set baud rate out
cfsetispeed (&options, baudRate); //Set baud rate in (same as baud rate out)
options.c_cflag &= ~CSIZE; //Clear bit-length flag so it can be set
//8N1 Serial Mode
options.c_cflag |= CS8; //Set bit-length: 8
options.c_cflag &= ~PARENB; //Set parity: none
options.c_cflag &= ~CSTOPB; //Set stop bit: 1
options.c_cflag &= ~CRTSCTS; //Set flow control: none
options.c_iflag &= ~ICANON; //Enable canonical input
options.c_cflag |= (CLOCAL | CREAD); //Enable receiver, and set local mode
tcsetattr(fd, TCSANOW, &options); //Set new attributes to hardware
return fd;
}
The fd (gps_fd) returns a "3", which is a good fd. Another application I was working in returned "15" for the fd, which is also good. Performing a read of the serial port:
bytesRead = read(gps_fd, buf, sizeof(buf) - 1);
Causes "bytesRead" to be -1. Putting in a "perror" line printed "Input/output error".
I am running the same code that I was running on the earlier version of the board and the device names (/dev) have not changed. Is there anything else I should look for that would cause the port not to read?
Thanks, everybody who commented, for increasing my knowledge!
It turns out that the FPGA (supplies hardware UARTs to the board) was turned off. This is a new board that is still in development and this is a problem the designers are aware of and they have a fix in the works. If you find yourself in a similar situation, keep in contact with the maker of the board.
This single board computer (TS-7800-V2) from Technologic Systems is great and they provide excellent customer service.

Program hangs when closing serial port connection

I am reading data through a USB connection as a serial port with the PL2303 driver. It returns successfully when doing an open and when I set they TTY options and non blocking. When I try to close the connection, it hangs. In this state it reads "�" instead of characters.
I can connect to the device perfectly fine with cutecom. Here is the strange part:
If I first connect to the device via cutecom (a serial monitor), my program will connect and close perfectly fine every time afterwards. It reads the characters as I expect them to be read. (No �).
If I disconnect and reconnect the hardware, my program will hang again until I run cutecom.
Since it works after I use cutecom, it makes me think that I am missing something in my initial connection, or connection settings. Here's what I use to connect:
baud_rate = 38400;
fd = open (device_path, O_RDONLY | O_NOCTTY );
In my set_tty_options function:
struct termios tty_options;
memset (&tty_options, 0, sizeof(tty_options));
tcgetattr (fd, &tty_options);
cfsetispeed(&tty_options, baud_rate); // set baud rate
tty_options.c_cflag = (tty_options.c_cflag & ~CSIZE) | CS8; // 8 bit msgs
tty_options.c_cflag |= (CLOCAL | CREAD); // enable reading
tty_options.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty_options.c_cflag |= parity;
tty_options.c_cflag &= ~CSTOPB;
tty_options.c_cflag &= ~CRTSCTS;
if (tcsetattr (fd, TCSANOW, &tty_options) != 0)
{
printf("error %d from tcsetattr\n", errno);
return TTY_ERROR;
}
In set_blocking function:
if (tcgetattr (fd, &tty) != 0)
{
printf("error %d from tggetattr", errno);
return FAILURE;
}
// 0 or 1 byte is enough to return from read
tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
if (tcsetattr (fd, TCSANOW, &tty) != 0)
{
printf("error %d setting term attributes", errno);
return FAILURE;
}
I think you want to add | O_SYNC to the open flags to insist on synchronous i/o. I doubt that is causing a problem though.
However, I think you want to ignore the break signal, which is reported as a NUL character like you are getting:
tty_settings.c_iflag &= ~IGNBRK; // ignore break signal
Also, you want to be sure the input processing is completely turned off, so that receipt of a backspace, ^C, ^\, etc. aren't triggering any reaction:
tty_settings.c_lflag = 0; // no signaling chars, no echo,
// no canonical processing
It looks like you are already using my set_blocking() function, so that should be okay.
Here's what I ended up doing. I figured this out by basically copying and pasting parts from cutecom's source code.
When opening...
int fd, n;
fd = open (device_path, O_RDONLY | O_NOCTTY | O_NDELAY);
... error check fd ...
n = fcntl(ail_info->ail_serial_fd, F_GETFL, 0);
fcntl(fd, F_SETFL, n & ~O_NDELAY);
You cannot set the baud rate as I was doing. You have to use the defined B38400;
baud = B38400;
Then, I added wallyk's answer.
tty_settings.c_lflag = 0;
Edit: As per sawdust comment, I found a better way to set this to raw input.
tty_options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
And it works.

C: read() hangs without getting input from serial port on unix

I'm having some issues getting data from a serial port using C on my Ubuntu 12 system.
I'm using open() and read(), and here is my code:
Fd = open("/dev/ttyUSB0", O_RDONLY | O_NOCTTY);
if (Fd == -1) {
printf("Could not open serial port: %s\n", strerror(errno));
return 1;
}
fcntl(Fd, F_SETFL, 0);
char buf;
while (1) {
read(Fd, &buf, 1);
printf("%c", buf);
}
However - my serial device is set to send "Boot.\r\n" followed by "To send: ", but when I attach the device and start the program, I only get the first line ("Boot.") and then no more. If I start gtkterm/picocom, I get both lines straight away.
I've also tried adding a signal handler for SIGTERM to get the port closed properly, using:
void signal_callback_handler(int signum) {
printf("Caught SIGTERM\n");
close(Fd);
exit(signum);
}
and
signal(SIGINT, signal_callback_handler);
Using this, I get the following when I press CTRL-C:
Boot.
^CTo send: Caught SIGTERM
I've also tried setting up the port first, using:
struct termios port_settings; // structure to store the port settings in
cfsetispeed(&port_settings, B115200); // set baud rates
cfsetospeed(&port_settings, B115200);
port_settings.c_cflag &= ~PARENB; // set no parity, stop bits, data bits
port_settings.c_cflag &= ~CSTOPB;
port_settings.c_cflag &= ~CSIZE;
port_settings.c_cflag |= CS8;
tcsetattr(Fd, TCSANOW, &port_settings);// apply the settings to the port
This only makes the situation worse - I get spammed with � :(
I'd be very appreciative of any help, thanks in advance!
Looks like your printf is just not being flushed until it hits a newline. That's why you get the first part of the output, but not the second. You could add fflush(stdout) after your printf to see the output immediately.

Linux - serial port read returning EAGAIN

I am having some trouble reading some data from a serial port I opened the following way. I've used this instance of code plenty of times and all worked fine, but now, for some reason that I cant figure out, I am completely unable to read anything from the serial port.
I am able to write and all is correctly received on the other end, but the replies (which are correctly sent) are never received (No, the cables are all ok ;) )
The code I used to open the serial port is the following:
fd = open("/dev/ttyUSB0", O_RDWR | O_NONBLOCK | O_NOCTTY);
if (fd == -1)
{
Aviso("Unable to open port");
return (fd);
}
else
{
//Get the current options for the port...
bzero(&options, sizeof(options)); /* clear struct for new port settings */
tcgetattr(fd, &options);
/*-- Set baud rate -------------------------------------------------------*/
if (cfsetispeed(&options, SerialBaudInterp(BaudRate))==-1)
perror("On cfsetispeed:");
if (cfsetospeed(&options, SerialBaudInterp(BaudRate))==-1)
perror("On cfsetospeed:");
//Enable the receiver and set local mode...
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~PARENB; /* Parity disabled */
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE; /* Mask the character size bits */
options.c_cflag |= SerialDataBitsInterp(8); /* CS8 - Selects 8 data bits */
options.c_cflag &= ~CRTSCTS; // disable hardware flow control
options.c_iflag &= ~(IXON | IXOFF | IXANY); // disable XON XOFF (for transmit and receive)
options.c_cflag |= CRTSCTS; /* enable hardware flow control */
options.c_cc[VMIN] = 0; //min carachters to be read
options.c_cc[VTIME] = 0; //Time to wait for data (tenths of seconds)
//Set the new options for the port...
tcflush(fd, TCIFLUSH);
if (tcsetattr(fd, TCSANOW, &options)==-1)
{
perror("On tcsetattr:");
}
PortOpen[ComPort] = fd;
}
return PortOpen[ComPort];
After the port is initializeed I write some stuff to it through simple write command...
int nc = write(hCom, txchar, n);
where hCom is the file descriptor (and it's ok), and (as I said) this works. But... when I do a read afterwards, I get a "Resource Temporarily Unavailable" error from errno.
I tested select to see when the file descriptor had something t read... but it always times out!
I read data like this:
ret = read(hCom, rxchar, n);
and I always get an EAGAIN and I have no idea why.
Update:
The HW is working fine! I can see that there is inbound data on the serial port because I've made a debug cable to read whats going on on another terminal. So...
I know what nonblocking should do. My question is... why isn't anything getting read!. The same setup works fine on windows, so all hardware is working fine...
This is driving me nuts! I'm sure it's something simple as hell! I even tried getting rid of O_NONBLOCK to see when I would receive something... but nothing...
Read this.
EAGAIN Non-blocking I/O has been
selected using O_NONBLOCK and no data
was immediately available for reading.
You need to check the serial terminal settings first.
use command - stty -F /dev/ttyUSB0 -a
Check that ctsrts is selected as -ctsrts
and do the other required settings with stty utility and you are done.
EAGAIN with O_NONBLOCK means there's been no data received on the port. Check that the port and cable are working properly (using minicom or some other known-good program), and that the remote really is sending some data.
see my code samples, if EAGAIN, you'd try to read again:
...
options.c_cflag &= ~PARENB;
options.c_iflag &= ~INPCK;
...
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // input
options.c_oflag &= ~OPOST; // output
...
fd = open("/dev/ttyUSB0", O_RDWR | O_NDELY | O_NOCTTY);
fcntl(fd, F_SETFL, 0);
...
int nc = write(hCom, txchar, n);
msleep(500); // wait 500ms
fcntl(hCom, F_SETFL, FNDELAY); // don't block serial read
ret = read(hCom, rxchar, n);
if (ret > 0) {
here had read n bytes or just partial data, read again if partial.
}
if (ret < 0) {
if (EAGAIN == errno) {
not a real error, just read again.
} else {
oops, errors.
}
}
...

Resources