0

I'm trying to read data from an Xbee over serial. If I initialize the port by calling xbee_init() then I call xbee_read() For some reason I always read no data and get an error of -1. However the moment I use some third party program like gtkterm or the Arduino serial monitor to look at the port I suddenly start reading data from my program. It will then work properly until I end my program and restart it, then it's back to reading no data.

Does anyone have an idea why this is happening? I've tested this with both an Xbee and Arduino and it's the same behavior. I'm calling these functions from a larger C++ program. Thanks!

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <termios.h>
#include "../include/m545_wireless_communication/readXbee.h"

int xbee_init (char *port, struct termios *tty) {

    int fd=open(port,O_RDWR | O_NOCTTY | O_NONBLOCK);

    if(fd == -1){return 0;}
    else {
        if(tcgetattr(fd, tty)!=0){return 0;}
        else{
                cfsetospeed(tty, B57600);
                cfsetispeed(tty, B57600);

                tty->c_cflag &= ~PARENB;
                tty->c_cflag &= ~CSTOPB;
                tty->c_cflag &= ~CSIZE;
                tty->c_cflag |= CS8;
                tty->c_cflag &= ~CRTSCTS; 
                tty->c_cflag |= CLOCAL | CREAD;

                tty->c_iflag |= IGNPAR | IGNCR;
                tty->c_iflag &= ~(IXON | IXOFF | IXANY);
                tty->c_lflag |= ICANON;
                tty->c_oflag &= ~OPOST;
                tcsetattr(fd, TCSANOW, tty);

                }
            }

    return fd;
}


char* xbee_read (int fd, char *buffer) {

    if(fd){
        int n=read(fd,buffer,11);
        printf("%d\n", n);
        return buffer;
    }else{
        return NULL;
    }
}

void xbee_close(int fd){
    close(fd);
}

Here is the main function that calls my xbee functions. This is part of a ROS project and this node is designed to simply read data from the xbee over serial and publish it to the /xbee topic at 10Hz.

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
#include <termios.h>
#include "m545_wireless_communication/readXbee.h"


int main(int argc, char **argv)
{

  ros::init(argc, argv, "Broadcaster");

  ros::NodeHandle broadcasterHandle;
  ros::Publisher broadPub = broadcasterHandle.advertise<std_msgs::String>("xbee", 1);

  char *port = "/dev/ttyUSB1";
  struct termios tty;
  char buffer[11];

  int fd = xbee_init(port,&tty);

  ros::Rate loop_rate(10);

  while (ros::ok())
  {
    char *message = xbee_read(fd, buffer);

    std_msgs::String msg;

    std::stringstream ss;
    ss << message;
    msg.data = ss.str();

    ROS_INFO("I heard: %s", msg.data.c_str());

    broadPub.publish(msg);

    ros::spinOnce();

    loop_rate.sleep();
  }

  ROS_INFO("Closing Port");
  xbee_close(fd);

  return 0;
}
raab
  • 1
  • 1
  • 1) Note: If code's `c_cflag` field is wider than `unsigned`, then `&= ~various_int_constants;` may be a problem. `tty->c_cflag |= PARENB; tty->c_cflag ^= PARENB;` avoids that rare problem. 2) Post the code that calls `xbee_read()` and the other 2 functions. – chux - Reinstate Monica Nov 01 '16 at 16:44
  • Thanks @chux you put me on the path of double checking all the c_cflags – raab Nov 01 '16 at 18:04
  • 1
    *"I always read no data and get an error of -1."* -- Then you need to access the global variable **errno** to get details of the error condition. The root problem is that you are using non-blocking mode. See http://stackoverflow.com/questions/1613916/linux-serial-port-read-returning-eagain – sawdust Nov 01 '16 at 18:07

1 Answers1

0

I was able to figure out that the issue was with how I was declaring the c_cflags. Xbee needs to be read in raw mode so rather than set the flags myself I just used cfmakeraw(). However I'm still not sure what exactly I was doing wrong or how opening the port with third party programs was helping the connection.

int xbee_init (char *port, struct termios *tty) {

    int fd=open(port,O_RDWR | O_NOCTTY | O_NONBLOCK);

    if(fd == -1){return 0;}//should be something better
    else {
        if(tcgetattr(fd, tty)!=0){return 0;}
        else{
                // cfsetospeed(tty, B57600);
                // cfsetispeed(tty, B57600);

                // tty->c_cflag &= ~PARENB;
                // tty->c_cflag &= ~CSTOPB;
                // tty->c_cflag &= ~CSIZE;
                // tty->c_cflag |= CS8;
                // tty->c_cflag &= ~CRTSCTS; 
                // tty->c_cflag |= CLOCAL | CREAD;
                // // tty->c_cflag |= PARENB;
                // // tty->c_cflag ^= PARENB;

                // tty->c_iflag |= IGNPAR | IGNCR;
                // tty->c_iflag &= ~(IXON | IXOFF | IXANY);
                // tty->c_lflag |= ICANON;
                // tty->c_oflag &= ~OPOST;

                cfmakeraw(&tty);
                cfsetispeed(&tty, B57600);

                tcsetattr(fd, TCSANOW, &tty);

                }
            }

    return fd;
}
raab
  • 1
  • 1
  • 1
    You are only masking the root problem, and have an incomplete termios configuration. For sample code see http://stackoverflow.com/questions/12437593/how-to-read-a-binary-data-over-serial-terminal-in-c-program/12457195#12457195. Otherwise study [Serial Programming Guide for POSIX Operating Systems](http://www.cmrr.umn.edu/~strupp/serial.html) – sawdust Nov 01 '16 at 18:11