Serial port in C ++ on Linux. Can serial port read and write at the same time ..?

I'm working on a project that needs a Linux PC to get data from a microcontroller to a UART, for which I used the already available open source serial port code in C ++ for Linux. (Ros code (robotic operating system))

The code looks like this:

#define DEFAULT_BAUDRATE 115200
#define DEFAULT_SERIALPORT "/dev/ttyUSB0"

//Global data
FILE *fpSerial = NULL;   //serial port file pointer
ros::Publisher ucResponseMsg;
ros::Subscriber ucCommandMsg;
int ucIndex;          //ucontroller index number

int FileDesc;

unsigned char crc_sum=0;

//Initialize serial port, return file descriptor
FILE *serialInit(char * port, int baud)
{
  int BAUD = 0;
  int fd = -1;
  struct termios newtio, oldtio;
  FILE *fp = NULL;

 //Open the serial port as a file descriptor for low level configuration
 // read/write, not controlling terminal for process,
  fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);

  ROS_INFO("FileDesc : %d",fd);

 if ( fd<0 )
  {
    ROS_ERROR("serialInit: Could not open serial device %s",port);
   return fp;
  }

  // save current serial port settings
  tcgetattr(fd,&oldtio);

  // clear the struct for new port settings
  bzero(&newtio, sizeof(newtio));

  //Look up appropriate baud rate constant
  switch (baud)
  {
     case 38400:
     default:
        BAUD = B38400;
        break;
     case 19200:
        BAUD  = B19200;
        break;
    case 115200:
        BAUD  = B115200;
        break;
     case 9600:
       BAUD  = B9600;
        break;
     case 4800:
        BAUD  = B4800;
        break;
     case 2400:
        BAUD  = B2400;
        break;
     case 1800:
        BAUD  = B1800;
        break;
     case 1200:
        BAUD  = B1200;
        break;
  }  //end of switch baud_rate

  if (cfsetispeed(&newtio, BAUD) < 0 || cfsetospeed(&newtio, BAUD) < 0)
  {
    ROS_ERROR("serialInit: Failed to set serial baud rate: %d", baud);
    close(fd);
    return NULL;
  }

  // set baud rate, (8bit,noparity, 1 stopbit), local control, enable receiving characters.
  newtio.c_cflag  = BAUD | CRTSCTS | CS8 | CLOCAL | CREAD;

  // ignore bytes with parity errors
  newtio.c_iflag =  IGNPAR;

  // raw output
  newtio.c_oflag = 0;

  // set input mode to non - canonical
  newtio.c_lflag = 0;

  // inter-charcter timer 
  newtio.c_cc[VTIME] = 0;

  // blocking read (blocks the read until the no.of charcters are read
  newtio.c_cc[VMIN] = 0;

  // clean the line and activate the settings for the port
  tcflush(fd, TCIFLUSH);
  tcsetattr (fd, TCSANOW,&newtio);

  //Open file as a standard I/O stream
  fp = fdopen(fd, "r+");

 if (!fp) {
    ROS_ERROR("serialInit: Failed to open serial stream %s", port);
    fp = NULL;
  }

ROS_INFO("FileStandard I/O stream: %d",fp);

  return fp;
} //serialInit


//Process ROS command message, send to uController
void ucCommandCallback(const geometry_msgs::TwistConstPtr& cmd_vel)
{
  unsigned char msg[14];
  float test1,test2;
  unsigned long i;

 // build the message packet to be sent
 msg = packet to be sent;
 msg[13] = crc_sum;

   for (i=0;i<14;i++)
   {
     fprintf(fpSerial, "%c", msg[i]);
   }

tcflush(FileDesc, TCOFLUSH); 

} //ucCommandCallback


//Receive command responses from robot uController
//and publish as a ROS message
void *rcvThread(void *arg)
{
  int rcvBufSize = 200;
  char ucResponse[10];//[rcvBufSize];   //response string from uController
  char *bufPos;
  std_msgs::String msg;
  std::stringstream ss;
  int BufPos,i;
  unsigned char crc_rx_sum =0;

  while (ros::ok()) {

     BufPos = fread((void*)ucResponse,1,10,fpSerial);

for (i=0;i<10;i++)
{
 ROS_INFO("T: %x ",(unsigned char)ucResponse[i]);
 ROS_INFO("NT: %x ",ucResponse[i]);
}

          msg.data = ucResponse;
          ucResponseMsg.publish(msg);
}

  return NULL;
} //rcvThread


int main(int argc, char **argv)
{
  char port[20];    //port name
  int baud;     //baud rate 

  char topicSubscribe[20];
  char topicPublish[20];

  pthread_t rcvThrID;   //receive thread ID
  int err;

  //Initialize ROS
  ros::init(argc, argv, "r2SerialDriver");
  ros::NodeHandle rosNode;
  ROS_INFO("r2Serial starting");

  //Open and initialize the serial port to the uController
  if (argc > 1) {
    if(sscanf(argv[1],"%d", &ucIndex)==1) {
      sprintf(topicSubscribe, "uc%dCommand",ucIndex);
      sprintf(topicPublish, "uc%dResponse",ucIndex);
    }
    else {
      ROS_ERROR("ucontroller index parameter invalid");
      return 1;
    }
  }
  else {
    strcpy(topicSubscribe, "uc0Command");
    strcpy(topicPublish, "uc0Response");
  }

  strcpy(port, DEFAULT_SERIALPORT);
  if (argc > 2)
     strcpy(port, argv[2]);

  baud = DEFAULT_BAUDRATE;
  if (argc > 3) {
    if(sscanf(argv[3],"%d", &baud)!=1) {
      ROS_ERROR("ucontroller baud rate parameter invalid");
      return 1;
    }
  }

  ROS_INFO("connection initializing (%s) at %d baud", port, baud);

   fpSerial = serialInit(port, baud);

 if (!fpSerial )
  {
    ROS_ERROR("unable to create a new serial port");
    return 1;
  }
  ROS_INFO("serial connection successful");

  //Subscribe to ROS messages
  ucCommandMsg = rosNode.subscribe("cmd_vel" /*topicSubscribe*/, 100, ucCommandCallback);

  //Setup to publish ROS messages
  ucResponseMsg = rosNode.advertise<std_msgs::String>(topicPublish, 100);

  //Create receive thread
  err = pthread_create(&rcvThrID, NULL, rcvThread, NULL);

  if (err != 0) {
    ROS_ERROR("unable to create receive thread");
    return 1;
  }

  //Process ROS messages and send serial commands to uController
  ros::spin();

  fclose(fpSerial);
  ROS_INFO("r2Serial stopping");
  return 0;
}

      

You can leave the ROS part aside, but the problem is with the serial port code.

When I run this code, I am receiving data from the controller correctly, but even when the controller stops sending data, I can also see the same data arriving at printf

. Is this a problem of not erasing input buffers?

But I can't send data from Linux PC to controller, don't know what's going on, can read and write be done at the same time on serial port in Linux?

Strange observation, when I open a port in H-term (uART renderer, similar to hyperlink renderer) with my serial port code running on the back, still H-term gives no error, but ideally H-term should give an error saying "the port cannot be opened, it is blocked" but it doesn't, my code doesn't get a block on the serial port?

And when I connect the port using H-term with mu running serial port code, can I see data coming from UART from Linux PC to microcontroller?

Can anyone know about the issues I'm having here?

Thanks in advance.

+3


source to share


1 answer


One problem:

BufPos = fread((void*)ucResponse,1,10,fpSerial);

      

because there is no check if BufPos is zero or less than 10



Instead of ros :: ok, use feof () (after receiving 0 bytes) to check for a closed connection, and ferror () to check for errors. Or, stop calling fread when you know (by protocol) that a data packet has been received.

It is possible to use the serial port in full duplex mode (ie read and write) not exactly "simultaneously" but as an alternative. Partners must follow protocol to avoid misunderstandings.

Don't mix fprintf and fread / fwrite. Fwrite is specified for sending.

0


source







All Articles