2

I have connected my XBee module (which is communicating on UART) to my PC. From a distant node I have transmitted data wirelessly to my XBee.

I have a problem receiving bytes on my Linux terminal. Though I am able to transmit data to the distant node. Find my below code which I am writing on my Linux terminal to serial communication. But for debugging I have my XBee configured as loop-back.(TXD-RXD shorted)

#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */

//Initialize serial port
int initport(int fd)
{
    int portstatus = 0;

    struct termios options;

    // Get the current options for the port...
    tcgetattr(fd, &options);

    // Set the baud rates to 9600...
    cfsetispeed(&options, B9600);
    cfsetospeed(&options, B9600);

    // Enable the receiver and set local mode...
    options.c_cflag |= (CLOCAL | CREAD);

    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    //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] = 1;   //Minimum characters to be read
    options.c_cc[VTIME] = 2;    //Time to wait for data (tenths of seconds)
options.c_oflag &=~OPOST;
options.c_iflag &=~(ICANON | ECHO | ECHOE | ISIG);
    // Set the new options for the port...
    tcsetattr(fd, TCSANOW, &options);

    //Set the new options for the port...
    tcflush(fd, TCIFLUSH);
    if (tcsetattr(fd, TCSANOW, &options) == -1)
    {
        perror("On tcsetattr:");
        portstatus = -1;
    }
    else
        portstatus = 1;

    return (portstatus);
}

int open_port(void)
{
    int fd; /* File descriptor for the port */
    fd = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY | O_NONBLOCK);

    if (fd == -1)
    {
        /*      Could not open the port.        */
        perror("Open_port: Unable to open /dev/ttyUSB0 --- \n");
    }
    else
{message print....};

    return (fd);
}

int main(void)
{
    int i;
    unsigned char write_buf[] =
        {0x7E, 0x00, 0x16, 0x10, 0x01, 0x00, 0x13,
         0xA2, 0x00, 0x40, 0xE4, 0x22, 0x64, 0xFF,
         0xFE, 0x00, 0x00, 0x41, 0x42, 0x43, 0x44,
         0x41, 0x42, 0x43, 0x43, 0x7F};

    int serial_fd = open_port();

    if (serial_fd == -1)
        printf("Error opening serial port /dev/ttyUSB0 \n");
    else
    {
        printf("Serial Port /dev/ttyUSB0 is Open\n");
        if (initport(serial_fd) == -1)
        {
            printf("Error Initializing port");
            close(serial_fd);
            return 0;
        }
        int n = write(serial_fd, &write_buf, sizeof write_buf);
        if (n < 0)
            fputs("write() failed!\n", stderr);
        else
        {
            printf("Successfully wrote %d bytes\n", sizeof write_buf);
            for (i=0; i<n; i++)
            {
                printf("%c ",write_buf[i]);
            }
        }
    }
usleep(200000);
     char read_buf[128];
     int n1 = read(serial_fd, read_buf, sizeof read_buf);

     // sleep(1);

     if (n1 < 0)
         fputs("Read failed!\n", stderr);
     else
     {
         printf("Successfully read from serial port -- %s\n With %d Bytes", read_buf,n1);
     }

     // sleep(.5);
     // usleep(500000);

     printf("\n\nNow closing serial port /dev/ttyUSB0 \n\n");
     close(serial_fd);

     return 0;
}

When I execute this code it does show 1 byte received. I have introduced delay before read().but it is showing on the Tera Term (sort of hyperterminal). Also I have to send/receive Hex strings on Xbee to transmit because they are communicating in API mode.

How can I fix this problem?

abhi1610
  • 721
  • 13
  • 28
  • That is a well-written question. I wish we had more of those. – Peter Mortensen Oct 31 '15 at 22:35
  • Do you run Tera Term at the same time as the program? – Peter Mortensen Oct 31 '15 at 22:37
  • @sawdust can you explain what `cfmakeraw()` would do precisely? – abhi1610 Nov 02 '15 at 04:38
  • @PeterMortensen Yes. My test-setup as the one module is connected on the linux and another one is connected on windows with Teraterm. When I am sending from linux connected the stream has been received at the Teraterm. But vice versa is not working. – abhi1610 Nov 03 '15 at 04:45
  • @sawdust Would it not more suitable to wait for reception of stream at the receiver? As byte stream would come at any time. – abhi1610 Nov 03 '15 at 04:48
  • @sawdust OK. So I have to flush a read buffer after the read syscall. – abhi1610 Nov 04 '15 at 07:44
  • @sawdust I am currently getting 1 byte reception and returned to the terminal. Think of as I am sending bytes but port only receives single byte and stopped. I am not flushing buffer after `read()` syscall. So it should display exact no of bytes I have transmitted. What would be problem? – abhi1610 Nov 04 '15 at 10:56

1 Answers1

0

You're not waiting long enough for bytes to come in from the XBee module. Try moving your sleep(1) to before the read(), and maybe increase it to a few seconds. You probably haven't even finished sending your data out at 9600 baud before you try calling read! Remember that the write() is handing data off to the OS and it's buffered to go out over the serial connection.

In a typical application, you'd have a loop where you try to read() periodically and then process whatever has come in from the remote end. Keep in mind that you'll see it in chunks, and you can't just print it as a string. You'll often be missing a null terminator on the string, and if you have binary data it can garble your terminal session.

While you're at it, consider configuring your XBee module for 115,200bps instead of 9600. It will be much more responsive.

And you might want to take a look at this Open Source ANSI C XBee Host Library I've put a lot of work into. It can target POSIX systems and includes many layers for communication from raw serial, to API mode, and even some ZigBee processing (ZDP endpoint and ZCL).

tomlogic
  • 11,489
  • 3
  • 33
  • 59
  • *"Try moving your sleep(1) to before the read()"* -- Useless advice, since the **read()** will block anyway, so a preceding **sleep()** would be redundant. – sawdust Nov 02 '15 at 07:07
  • @sawdust: Wouldn't the serial port be opened non-blocking? Therefore `read()` will return -1 and set `errno` to `EAGAIN` if there aren't any bytes available. – tomlogic Nov 03 '15 at 00:52
  • If your assertion was actually true, then why doesn't the OP report that scenario that you describe? The OP does not report that the *"Read failed!"* message appeared. Instead the OP reports that nothing happens, i.e. *"it doesn't show any bytes"*. That is consistent with a blocked read that has not returned. So yes, the port is opened nonblocking. But inspection of the code also shows that there is a `fcntl(fd, F_SETFL, 0)` after a successful open to restore blocking mode. – sawdust Nov 03 '15 at 18:35
  • @tomlogic I have introduced `usleep()` before making `read()` call. Now after some modification with I have posted new code. Now I am getting only single byte receive. What would be cause? – abhi1610 Nov 04 '15 at 10:49
  • @abhi1610, the read succeeds with the first character that comes in. You need to continue calling `read()` to get the remaining characters. – tomlogic Nov 04 '15 at 19:33
  • @sawdust, thanks for the detailed explanation. I didn't recognize the `fcntl()` call as putting it back into blocking mode, and I wouldn't design a single-threaded program to use blocking reads on a serial port, so I wasn't expecting that behavior. – tomlogic Nov 04 '15 at 19:53
  • @abhi1610: a typical setup would be to have a big loop where you repeatedly call `read()` and process the bytes read. If `read()` returns -1 and errno is `EAGAIN`, sleep for a few milliseconds and try again to see if more data has arrived. – tomlogic Nov 04 '15 at 19:55