-2

If I use UART to send sensor data from a Pi Pico to another computer (received via /dev/tinyUSB0), does the "default protocol" have any sort of error correction? I guess I don't need to worry over flipped bits, but potentially bytes could be dropped? So how would I go about this. Is there a simple protocol layer that I can add on top? Do I just define my own start/stop words to make sure the receiver can recover from lost data?

Like, the simplest idea I can come up with is to encode the data so that it only uses the 7 least significant bits in each byte, and then I can mark frame start and frame stop with 0x80 and 0x81. But I wonder if I'm reinventing the wheel? Or I just encode numbers as ASCII string representations and insert newlines?

For instance, here is my attempt with a frame bound 0x80, 0x81:

00000000  80 2e 50 29 2d 81 80 33  5a 2b 64 81 80 30 5d 2c  |..P)-..3Z+d..0],|
00000010  25 81 80 2f 4c 29 14 81  80 33 7e 2d 49 81 80 2e  |%../L)...3~-I...|
00000020  1e 29 34 81 80 32 80 2f  35 2c 46 81 80 2d 27 28  |.)4..2./5,F..-'(|
00000030  81 80 37 0e 30 2a 81 80  2b 25 27 2d 81 80 35 0b  |..7.0*..+%'-..5.|
00000040  2c 57 81 80 32 39 2d 3f  81 80 2c 4a 26 78 81 80  |,W..29-?..,J&x..|
00000050  39 6c 2f 73 81 80 2c 55  29 01 81 80 32 5f 2a 1e  |9l/s..,U)...2_*.|
00000060  81 80 34 53 2d 71 81 80  2b 73 26 6c 81 80 38 2a  |..4S-q..+s&l..8*|
00000070  2f 41 81 80 2d 09 28 73  81 80 32 6e 2a 10 81 80  |/A..-.(s..2n*...|
00000080  35 0c 2e 25 81 80 2b 46  26 4f 81 80 38 25 2f 2e  |5..%..+F&O..8%/.|
00000090  81 80 2e 26 29 21 81 80  31 2a 29 81 80 35 53 2e  |...&)!..1*)..5S.|
000000a0  53 81 80 2a 6f 26 69 81  80 38 81 80 2b 26 76 81  |S..*o&i..8..+&v.|
000000b0  80 35 73 2c 72 81 80 31  2b 39 81 80 30 2c 2c 7e  |.5s,r..1+9..0,,~|
000000c0  81 80 2e 02 28 55 81 80  33 66 2e 40 81 80 2d 52  |....([email protected]|
000000d0  28 72 81 80 32 5e 2b 40  81 80 2f 79 2c 12 81 80  |(r..2^+@../y,...|
000000e0  2e 2d 28 75 81 80 33 3a  2d 61 81 80 2d 7b 28 64  |.-(u..3:-a..-{(d|
000000f0  81 80 32 6b 2b 54 81 80  30 0a 81 80 33 33 2d 3c  |..2k+T..0...33-<|
00000100  81 80 2e 4b 29 39 81 80  33 0f 2b 28 81 80 2f 29  |...K)9..3.+(../)|
00000110  47 81 80 33 2d 57 81 80  2e 2a 29 22 81 80 33 31  |G..3-W...*)"..31|

If you look at the hexdump carefully, you'll note that the payload between 0x80 and 0x81 is usually 4 bytes -- which it should be -- but occasionally bytes are missing. What could be the cause of this? Also if I follow the hexdump on the screen, it stutters a lot, the speed is very irregular. In comparison, if I output ASCII text, it is very fluent. Perhaps my Python code is bad? Hex output:

    numTouch = 2
    # 2 bytes per sensor value, one start byte,
    # one stop byte
    bufSz = numTouch * 2 + 2
    buf = bytearray(bufSz) 
    buf[0]         = 0b10000000
    buf[bufSz - 1] = 0b10000001
    touchVals      = [0, 0] # polled later

    # loop
    while True:
      bi = 1
      for idx in range(numTouch):
        touchVal = ...
        senseHi  = (touchVal >> 7) & 0b1111111
        senseLo  =  touchVal       & 0b1111111
        buf[bi] = senseHi
        bi += 1
        buf[bi] = senseLo
        bi += 1

      uart.write(buf)

versus this, which outputs fluently:

    text = "A: {}, B: {}\n".format(touchVals[0], touchVals[0])
    buf1 = bytes(text, "ascii")
    uart.write(buf1)

Here is a visual comparison of the dataflow, showing the problematic binary data vs the straight forward text data:

enter image description here

enter image description here


Edit: This difference led me to believe that UART (or CircuitPython) expects newline characters flushing. If I change the stop byte from 0x81 to 0x0A, suddenly the hex dump reception is smooth. Could somebody explain this, please? Can I explicitly flush after a write? The API doesn't seem to provide such function.

10
  • 1
    I don't need to worry over flipped bits ... why not?
    – jsotola
    Commented Jan 6, 2022 at 20:37
  • @jsotola well, wouldn't that be the minimum requirement of the serial bus to work at all?
    – 0__
    Commented Jan 6, 2022 at 20:46
  • 2
    Not Pi specific.
    – joan
    Commented Jan 6, 2022 at 20:52
  • @joan can you stop voting to close every second question of mine? It's very annoying. I'm trying to solve a problem with CircuitPython, a Pi Pico and UART. Look, I also have 64K reputation on stackoverflow, I know how the Q&A sites work. Thanks
    – 0__
    Commented Jan 6, 2022 at 20:56
  • Perhaps related topic (seems similar problems of dropped data): forums.raspberrypi.com/viewtopic.php?f=33&t=134752#p897104
    – 0__
    Commented Jan 6, 2022 at 23:00

1 Answer 1

1

The "dropping" of bytes happens because not all byte values are directly passed through when reading from /dev/tty.... This depends on the line discipline.

$ stty -F /dev/ttyACM1
speed 9600 baud; line = 0;
min = 0; time = 0;
-icrnl -imaxbel
-opost
-isig -icanon -iexten -echo

Here line = 0 indicates the canonical mode, in which many of the ASCII characters below 0x20 (and some above) have special meaning for controlling the editing buffer.

  • 0x03 : intr = ^C
  • 0x04 : eof = ^D
  • 0x05 : enquiry = ^E
  • 0x0D : CR, carriage return
  • 0x11 : start = ^Q, device control 1
  • 0x12 : rprnt = ^R, device control 2
  • 0x13 : stop = ^S, device control 3
  • 0x15 : kill = ^U
  • 0x16 : lnext = ^V
  • 0x17 : werase = ^W
  • 0x1A : susp = ^Z
  • 0x1C : quit = ^\
  • 0x7F : DEL, delete

To avoid that, when configuring stty, one has to choose the raw (pass through) line discipline.

A better approach is to use a dedicated serial communication library. For example, in my case, rather than open the stream as

val device = "/dev/ttyACM1" 
new java.io.FileInputStream(device)

One can get a serial connection that avoids tty processing, for example using the jSerialComm library:

val device = "/dev/ttyACM1" 
val ports  = com.fazecast.jSerialComm.SerialPort.getCommPorts()
val port   = ports.find(_.getSystemPortPath == device).get
val opened = port.openPort()
require (opened)
port.getInputStreamWithSuppressedTimeoutExceptions

This again wraps in a java.io.InputStream with read-blocking behaviour. As a result, no bytes are dropped, no newline is needed to flush.

fluently running input with binary stream

Not the answer you're looking for? Browse other questions tagged or ask your own question.