0

I'm using the following C code borrowed from here: how to open, read, and write from serial port in C

with some slight modifications so that the program waits for keyboard input, then writes this data to a serial port and waits to read it back. Here's my code:

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



int
set_interface_attribs (int fd, int speed, int parity)
{
        struct termios tty;
        memset (&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0)
        {
                error_message ("error %d from tcgetattr", errno);
                return -1;
        }

        cfsetospeed (&tty, speed);
        cfsetispeed (&tty, speed);

        tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
        // disable IGNBRK for mismatched speed tests; otherwise receive break
        // as \000 chars
        tty.c_iflag &= ~IGNBRK;         // ignore break signal
        tty.c_lflag = 0;                // no signaling chars, no echo,
                                        // no canonical processing
        tty.c_oflag = 0;                // no remapping, no delays
        tty.c_cc[VMIN]  = 0;            // read doesn't block
        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

        tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

        tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
                                        // enable reading
        tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
        tty.c_cflag |= parity;
        tty.c_cflag &= ~CSTOPB;
        tty.c_cflag &= ~CRTSCTS;

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
        {
                error_message ("error %d from tcsetattr", errno);
                return -1;
        }
        return 0;
}

void
set_blocking (int fd, int should_block)
{
        struct termios tty;
        memset (&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0)
        {
                error_message ("error %d from tggetattr", errno);
                return;
        }

        tty.c_cc[VMIN]  = should_block ? 1 : 0;
        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
                error_message ("error %d setting term attributes", errno);
}


int main{

    char *portname = "/dev/tty0"
    int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0)
    {
            error_message ("error %d opening %s: %s", errno, portname, strerror (errno));
            return;
    }

    set_interface_attribs (fd, B115200, 0);  // set speed to 115,200 bps, 8n1 (no parity)
    set_blocking (fd, 0);                // set no blocking

    while (1){
        char data[20];
        if (fgets(data, sizeof data, stdin)){
            int m = write(fd, data, sizeof data);
            printf("%d chars written\n", m);
            char buf[100];
            sleep(10);
            int n = read (fd, buf, sizeof buf);
            printf("%d chars read", n);
        }
    }
}

The problem is that while the keyboard input is successfully written to serial file, after sleeping for 10 seconds the program still reports that it has read 0 chars. Why is this? Do I need to actually have a physical device waiting on the other end of the port which receives the data and returns something, or is it fine to write to and read from the serial port with the same program? Thanks in advance for any help!

EDIT:

Ok so now I've created a second program which waits for data on the serial port sent by the above program, but this program is still unable to read the data sent to it. Here's the code:

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



int
set_interface_attribs (int fd, int speed, int parity)
{
        struct termios tty;
        memset (&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0)
        {
                error_message ("error %d from tcgetattr", errno);
                return -1;
        }

        cfsetospeed (&tty, speed);
        cfsetispeed (&tty, speed);

        tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
        // disable IGNBRK for mismatched speed tests; otherwise receive break
        // as \000 chars
        tty.c_iflag &= ~IGNBRK;         // ignore break signal
        tty.c_lflag = 0;                // no signaling chars, no echo,
                                        // no canonical processing
        tty.c_oflag = 0;                // no remapping, no delays
        tty.c_cc[VMIN]  = 0;            // read doesn't block
        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

        tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

        tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
                                        // enable reading
        tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
        tty.c_cflag |= parity;
        tty.c_cflag &= ~CSTOPB;
        tty.c_cflag &= ~CRTSCTS;

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
        {
                error_message ("error %d from tcsetattr", errno);
                return -1;
        }
        return 0;
}

void
set_blocking (int fd, int should_block)
{
        struct termios tty;
        memset (&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0)
        {
                error_message ("error %d from tggetattr", errno);
                return;
        }

        tty.c_cc[VMIN]  = should_block ? 1 : 0;
        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
                error_message ("error %d setting term attributes", errno);
}


int main{

    char *portname = "/dev/tty0"
    int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0)
    {
            error_message ("error %d opening %s: %s", errno, portname, strerror (errno));
            return;
    }

    set_interface_attribs (fd, B115200, 0);  // set speed to 115,200 bps, 8n1 (no parity)
    set_blocking (fd, 0);                // set no blocking


    char buf[20];
    int n = 0;
    while (n == 0){
        n = read(fd, buf, sizeof buf);
    }
    printf("%d chars read\n", n);

    return 0;

}

When I run this and right afterwards run the first program and write some data to the serial port, this program just stays in the while loop forever and never reads in any data. Help?

Community
  • 1
  • 1
kjakeb
  • 6,810
  • 5
  • 20
  • 34
  • "Do I need to actually have a physical device waiting on the other end of the port"? Are you saying that now the serial port isn't connected to anything? – Thomas Padron-McCarthy Oct 17 '13 at 19:45
  • That's correct. I was under the impression that the serial port acts similar to a file in that you can simply write to it and read from it. – kjakeb Oct 17 '13 at 19:59
  • 1
    Aha. No, the serial port doesn't have a memory chip where it stores data so you can read it back. It's used to send data to a device at the other end of the cable, and to read data from it. – Thomas Padron-McCarthy Oct 17 '13 at 20:04
  • That clarified it, thank you! – kjakeb Oct 17 '13 at 20:17
  • Suggest using a known terminal program to verify that data is truly being received. _Then_ run your program to see if it reads OK. – chux - Reinstate Monica Oct 17 '13 at 20:57
  • Change in sender `write(fd, data, sizeof data)` to `write(fd, data, strlen(data))`. – chux - Reinstate Monica Oct 17 '13 at 21:01
  • Minor idea: Is it known that `"/dev/tty0"` and `"/dev/ttyUSB1"` are _different_ serial interfaces? If there are the same, you should not run both programs at the same time. (But then I suspect one of your programs will present an error message in that case.) – chux - Reinstate Monica Oct 17 '13 at 21:05
  • 1) It is very easy to use the wrong serial cable. If you have a 9-pin cable the pin 2-3 short is a nifty trick for debug. 2) Also, if not using your own code to send data, insure the alternate program that is sending is using the correct, baud, parity, handshake, etc. – chux - Reinstate Monica Oct 17 '13 at 21:11
  • I made changes to the second program: the serial interface is supposed to say "/dev/tty0" in both programs. Is that a problem? How would that even work if they were different? – kjakeb Oct 17 '13 at 22:37
  • `/dev/tty0` on Linux isn't a serial port at all. It's an alias for the currently displayed virtual console. You can't use it to communicate with anything but the local keyboard and screen. –  Oct 18 '13 at 00:10

1 Answers1

0

No, you do not need to actually have a physical device waiting on the other end of the port which receives the data.

Yes, you need to actually have a physical device waiting on the other end of the port to return something. (It could be a wire looping transmit and receive (pins 2 & 3 on 9-pin.)

Yes, it fine to write to and read from the serial port with the same program.

As to "Why is this?": there is nothing sending data to your program via the serial interface.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256