Strange USB serial data dropping - Ubuntu 16.04 - Teensy to PC

Status
Not open for further replies.

msek

New member
I run a primitive code on Teensy 2.0, which is just to blink a LED and transmit counter value to usb serial port every time it gets an 'r' command from the C program which sends the command and reads the value from usb.

I run those on two identical(?) Xubuntus (uname -a: Linux XXX 4.4.0-112-generic #135-Ubuntu SMP Fri Jan 19 11:48:36 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux) geting suprisingly different results.

Teensy code works perfect with Arduino IDE Serial Monitor on both machines as well as the commands: echo -n 'r' >/dev/teense and tail -f /dev/teensy.

The problem appears when to use a C application or the commands: echo -n 'r' >/dev/teense; cat /dev/teensy. They both work correctly on first machine and incorrectly (return only every second (!) value) on the second one.

Machine XXX1 (as expected):
HTML:
XXX1:~$ echo -n 'r' >/dev/teensy; cat /dev/teensy 
200
XXX1:~$ echo -n 'r' >/dev/teensy; cat /dev/teensy 
201
XXX1:~$ echo -n 'r' >/dev/teensy; cat /dev/teensy 
202

XXX1:~$ ./counter
Read bytes: 3 [ 0x32 0x30 0x33] [203]
XXX1:~$ ./counter
Read bytes: 3 [ 0x32 0x30 0x34] [204]
XXX1:~$ ./counter
Read bytes: 3 [ 0x32 0x30 0x35] [205]
XXX1:~$ ./counter

Machine XXX2 (unexpected behavior):
HTML:
XXX1:~$ echo -n 'r' >/dev/teensy; cat /dev/teensy 
200
XXX1:~$ echo -n 'r' >/dev/teensy; cat /dev/teensy 
XXX1:~$ echo -n 'r' >/dev/teensy; cat /dev/teensy 
202
XXX1:~$ echo -n 'r' >/dev/teensy; cat /dev/teensy 
XXX1:~$ echo -n 'r' >/dev/teensy; cat /dev/teensy 

XXX1:~$ ./counter
Read bytes: 3 [ 0x32 0x30 0x34] [204]
XXX1:~$ ./counter
Read bytes: 0 [] []
XXX1:~$ ./counter
Read bytes: 3 [ 0x32 0x30 0x36] [206]
XXX1:~$ ./counter
Read bytes: 0 [] []

I tried many serial port settings in the C code and/or set with stty (btw stty -a -F /dev/teensy looks the same on both mashines) with no success.

Can you point any reason in the code or system settings for this different behavior.



The Teensy code is as follows:

Code:
#define LEDPIN 11

int counter;


void setup()
{
  pinMode(LEDPIN, OUTPUT);
  counter = 200;

  Serial.begin(115200);
  while (!Serial) ;
}


void loop()
{
  if ( Serial.available() > 0 )
  {
    switch ( Serial.read() )
    {
      case 'r':
        Serial.print(counter++);
        break;

      default:
        Serial.print("error");
        break;
    }

    digitalWrite(LEDPIN, HIGH);
    delay(400);
    digitalWrite(LEDPIN, LOW);
    delay(100);
  }
}
The C code:

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



int set_interface_attribs(int fd)
{
    struct termios tty;

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

    tty.c_cflag |= (CLOCAL | CREAD);
    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
    tty.c_cflag &= ~(PARENB | CSTOPB | CRTSCTS);

    tty.c_lflag &= ~(ICANON | ECHO | ECHONL | ISIG | IEXTEN);
    tty.c_lflag &= ~(NOFLSH | FLUSHO);

    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY);

    tty.c_oflag &= ~OPOST;

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 0;
    tty.c_cc[VTIME] = 20;   // read czeka do 5 sekund na pojawienie się danych


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

    return 0;
}


#define MAXBUF 16


int main(int argc, char *argv[])
{
    char *portname = "/dev/teensy";
    int fd;
    char buf[MAXBUF];
    int rdlen;


    if ( (fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC)) < 0 )
    {
        printf("Error opening %s: %s\n", portname, strerror(errno));
        return(EXIT_FAILURE);
    }

    set_interface_attribs(fd);


    if ( write(fd, "r", 1) != 1 )
    {
        printf("Error from write: %d\n", errno);
        return(EXIT_FAILURE);
    }


    if ( (rdlen = read(fd, buf, sizeof(buf) - 1)) >= 0 )
    {
        buf[rdlen] = '\0';
        printf("Read bytes: %d [", rdlen);
        for (int i = 0; i < rdlen; i++) printf(" 0x%x", buf[i]);
        printf("] [%s]\n", buf);
    }
    else
    {
        printf("Error from read: %d: %s\n", rdlen, strerror(errno));
    }


    close(fd);


    return(EXIT_SUCCESS);
}
 
Thank you for the answer.
Of course, my intention is to use the C application to read the counter. The problem is that it shows the same magic strange behavior as cat command.
Using Arduino Serial Monitor, everything works perfect on both machines I use.
Thus I think the problem is not in this extremaly simple Teensy code but somewhere in the C app code (serial config) and/or system settings.
However, I can't find anything responsible for such behavior.
And the most strange is that it works or not, depending on the machine, which I thought were the same.
 
Usually you would need to call select() to wait for the reply to arrive. If you call read() too soon, it's going to return 0 if there's no data at that point.

Perhaps even use select() & read() in a loop, because the reply may arrive in pieces and select will only tell you when *something* has arrived, not how much. And to be more technically accurate, select() actually tells you whether a call to read() would block, but not necessarily whether it would succeed with actual data. If the device has disconnected or some other error happens, that counts as a case where read() will not block. Of course, at least some data available is the most common reason read() would not block, but you should check for the error cases.
 
I do not care about an immediate response, so I put a sleep(1) just before read(). However, this does not change anything.
The LED blinks immediately so I suppose Teensy sends the value immediately too but it still can be read only every second time.

Code:
	if ( write(fd, "r", 1) != 1 )
	{
		printf("Error from write: %d\n", errno);
		return(EXIT_FAILURE);
	}

	sleep(1);

	if ( (rdlen = read(fd, buf, sizeof(buf) - 1)) >= 0 )
	{
		buf[rdlen] = '\0';
		printf("Read bytes: %d [", rdlen);
		for (int i = 0; i < rdlen; i++) printf(" 0x%x", buf[i]);
		printf("] [%s]\n", buf);
	}
 
Ok. I found something. It was enough to put the plug into another usb socket, supported by another driver.
No changes to the code. The troubles arises when Teensy hangs on xhci. On ehci everything works correctly.

And now, is it "my fault" (program code, system settings, serial port settings, whatever else) or maybe a bug
in the Ubuntu 16.04 xhci driver?
 
Last edited:
Status
Not open for further replies.
Back
Top