2

I am encountering some troubles trying to communicate through USB between a teensy and a Raspberry Pi3.

When I send unsigned int, I receive wrong numbers for all the numbers containing the byte of value 13 which is changed to the value 10

For example :

13 becomes 10;
269 becomes 266;
525 becomes 522;
781 becomes 778;
1037 1034;
1293 1290; 
1549 1546; 
1805 1802; 
etc...

but not for the others...

Whatever if I use my program on the pi or if I send a cat command over /dev/ttyACMO. However, when I do the same on another computer, everything works fine.

I got the same problem on another computer so I think the problem come from a bad library I have installed.

Do you have any idea?

PS: the code on the teensy is as simple as :

    unsigned int i = 0;
    while(true){
        Serial.write((char*) &i, 4);
        ++i;
        delay(500);
    }
halfer
  • 19,824
  • 17
  • 99
  • 186
Alex
  • 79
  • 6
  • problem solved : http://stackoverflow.com/questions/42209980/serial-port-binary-transfer-changes-carriage-return thank you ! – Alex May 04 '17 at 18:43

1 Answers1

1

ASCII decimal 10 is line feed (<LF>). ASCII decimal 13 is carrige return (<LF>).

You're most likely victium of implicit <CR><LF><LF> conversion; Wikipedia explains it in detail but in summary it's rooted in technological history:

  • When computers were given text input/output capabilities for the first time, this was not done through displays, but teletypes. A teletype is essentially a mechanical typewriter, where each "key" can be hit also by an electrical signal.

  • Typewriters, and thereby teletypes distinguished between advancing the paper a line (line feed), and returning the carrige (the typing head) back to the start of a line.

  • The ASCII encoding was created as essentially a 1:1 mapping of the functions of a typewriter to binary patterns.

The net effect was, that in order to start a new line on a teletype you'd have to send a line feed (<LF>) together with a carrige return (<CR>), the order of the two doesn't exactly matter, the physical effect is practically the same.

However different operating systems settled on different default conventions how to store and transmit newlines:

  • MS-DOS and by inheritance Windows chose to explicitly store both <CR> and <LF> in that order.

  • Unix chose to store the <LF> only and translate it into a <CR><LF> sequence when sending it to a teletype, and do it vice-versa on reception. Linux adopted this Unix convention.

When it comes to the transmission over a serial link, the default however always has been, that for a newline a (<CR>,<LF>) pair should be transmitted, in expectation of the other end being a teletype. And this is, what's likely going on here! The actual data transmitted over the wire is a <CR><LF> but the receiving OS having inherited the ways of Unix silently converts it into a plain <LF>. And for any number bigger than 255 this conversion will happen to its lower 8 byte, together with dropping a whole byte.

Of course this default behavior can be reconfigured, namely through termios/ioctl_tty on the opened serial port, specifically the output and input option flags (namely the flags OPOST, ONLCR, ONLCR, OCRNL, ONLRET, INLCR, IGNCR, ICRNL).

However I strongly suggest you don't fiddle with these flags and instead take the following two lessions to heart:

  • Plain text is the universal exchange format! (i.e. don't submit raw binary over a wire, if you can avoid it, but send it as – also human readable – text)

  • Be liberal in reading what you receive; be conservative in what you send! (i.e. write your program in a way, that reading data is not dependent on a strict order and format of the data, i.e. may tolerate any kind of newline sequence or number of whitespace delimiting field; but also send data in a strict regular way so that less liberal receivers don't choke on it)

datenwolf
  • 159,371
  • 13
  • 185
  • 298