1

I have a bluetooth IMU (seen as a serial device at /dev/rfcomm0) that returns the current angle in quaternions 50 times a second.

I did a test program to read data and it worked, but I have to integrate it in a while cycle where, after the reading is done, a thread is a created and a computationally expensive elaboration is done, so I cannot read keep the rate with the IMU data.

The IMU sends me a 54 bytes packet, and the first two bytes are always set to Ascii "tt".

This is the C code I'm using:

int set_interface_attribs(int fd, int speed)
{
        struct termios tty;

        if (tcgetattr(fd, &tty) < 0) {
              printf("Error from tcgetattr: %s\n", strerror(errno));
              return -1;
        }

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

        tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
        tty.c_cflag &= ~CSIZE;
        tty.c_cflag |= CS8;         /* 8-bit characters */
        tty.c_cflag &= ~PARENB;     /* no parity bit */
        tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
        tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

        /* setup for non-canonical mode */
        tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
        tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
        tty.c_oflag &= ~OPOST;

        /* fetch bytes as they become available */
        tty.c_cc[VMIN] = 54;
        tty.c_cc[VTIME] = 1;

        if (tcsetattr(fd, TCSANOW, &tty) != 0) {
           printf("Error from tcsetattr: %s\n", strerror(errno));
           return -1;
        }
        return 0;
}

int start_serial()
{
    char *portname = "/dev/rfcomm0";
    int fd;
    int wlen;
    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
        printf("Error opening %s: %s\n", portname, strerror(errno));
        return -1;
    }
    set_interface_attribs(fd, B115200);
    wlen = fwrite(fd, "?!CALIB050!?\n", 14);
    if (wlen != 14) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }

    tcflush(fd,TCIFLUSH);
    tcdrain(fd);    /* delay for output */
    return fd;
}

int read_serial(int fd)
{
    char buf[54];
    int rdlen;
    rdlen = read(fd, buf, 54);
    if (rdlen > 0)
    {
        struct CPacketCalib cc = CPacketCalibConvert(buf); 
        printf("%f %f %f %f\n",cc.q.I, cc.q.J, cc.q.K,cc.q.W);
    }
    else if (rdlen < 0)
    {
        printf("Error from read: %d: %s\r\n", rdlen, strerror(errno));
    }
    return 1;
}

int stop_serial(int fd)
{
    int wlen = write(fd, "?!STOPSEND!?\n", 14);
    if (wlen != 14)
    {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    fclose(fd);
    return 1;
}

How can I flush the serial buffer at every cycle and start to get correctly my packet?

Thank you for your help.

pm40
  • 19
  • 6
  • Can the data ever contain the bytes that represent 'tt'? – Martin James Feb 12 '18 at 19:35
  • A robust program would not rely on the non-canonical read syscall to provide byte-aligned messages. Disconnects and/or dropped bytes could cause the need to resysnc at any time. The program should parse all bytes and validate every message. – sawdust Feb 13 '18 at 02:31

1 Answers1

1

What your problem sounds like is that you start your read in the middle of your packet and get half of one packet and half of the other. In that case, you need to read until you get to the start of your packet, and only then feed it to your parsing function. So something like this:

char ch;
char[52] buf;
while(1){
    if(read(fd, &ch, sizeof(ch)) > 0 && ch == 't'){  
         if(read(fd, &ch, sizeof(ch)) > 0 && ch == 't'){
             if(read(fd, buf, sizeof(buf)) == sizeof(buf)){
                  struct CPacketCalib cc = CPacketCalibConvert(buf);
             }
         }
    }
}

So CPacketCalibConvert only gets called with a complete packet of data (minus the two 't's, perhaps add them back in inside the innermost block?).

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
C_Elegans
  • 1,113
  • 8
  • 15
  • Thank you very much, your answer has been precious to me. Now data is correct, but every 10 seconds output data is blank for a few seconds. I used the canonical read like sawdust suggested, but the problem remains – pm40 Feb 13 '18 at 08:35
  • *"I used the canonical read like sawdust suggested ..."* -- No, that's a misinterpretation of what I actually wrote. You need to use an intermediate buffer so that the program can parse all bytes and validate every message. See https://stackoverflow.com/questions/54392470/how-to-handle-buffering-serial-data/54394770#54394770 – sawdust Mar 13 '22 at 22:49