0

So I have some data going through a serial port at regular intervals. Eight bytes are sent every 2 seconds.

I would read this data. Because transmission is constant, I can't just start reading at any moment. I need to align the data. So clearly, the way to do this is to send a header or some sort of separator bytes.

With the Read trait, I can read a certain number of bytes into my buffer. It might look like this:

let mut serial_buf: Vec<u8> = vec![0; 16];
loop {
    match port.read(serial_buf.as_mut_slice()) {
        Ok(t) => print_data(&serial_buf[..t]),
        Err(ref e) if e.kind() == std::io::ErrorKind::TimedOut => (),
        Err(e) => eprintln!("{:?}", e),
    }
}

But the output I get will look something like this without alignment (with bytes 'abcd' being sent every 2 seconds):

abcd
a
bcd
abc
d
a
bcd
ab
cd

So what's the most practical way of reading and discarding until an alignment bit is found, then making sure that all subsequent reads are aligned?

Thanks,

Thor Correia
  • 1,159
  • 1
  • 12
  • 20
  • What do you mean by alignment bit? Can you provide an example of the expected output? – Coder-256 Jun 19 '20 at 04:39
  • Since you know the size of the data packets, you can use [`read_exact`](https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact) to read a full packet at a time. – Jmb Jun 19 '20 at 06:41
  • Take a look at [this answer](https://stackoverflow.com/questions/16177947/identification-of-packets-in-a-byte-stream/16180135#16180135), and see if you can adapt it. *"making sure that all subsequent reads are aligned"* -- Actually you cannot. Your scheme can confirm that the bytes received are part on one message. Unless you have sometype of checksum or CRC, you are unable to validate that the data is the actual message.Just because you achieve alignment for the current message, that does not mean that the next message is also aligned. – sawdust Jun 19 '20 at 07:09
  • @Coder-256 a "magic number" so to speak, that will never be used as a part of the data, and can be used to find the alignment. For example, if I know the sequence 0x0000 will never be used, then I can add 0x0000 to the beginning of every packet to align it. – Thor Correia Jun 19 '20 at 07:59
  • @Jmb I don't expect this will work if transmission begins in the middle of a packet. Instead of "abcd, abcd, abcd" we might get "cdab, cdab, cdab" – Thor Correia Jun 19 '20 at 08:00
  • @sawdust Are you saying in terms of dropped or lost data? Does the serial protocol not already account for this? – Thor Correia Jun 19 '20 at 08:01
  • Then read one byte at a time until you find your magic number, and then use `read_exact` to get the following packets – Jmb Jun 19 '20 at 08:18
  • *"Does the serial protocol not already account for this?"* -- Exactly what *"serial protocol"* are you referring to? What data integrity mechanism do you think is already in place? – sawdust Jun 19 '20 at 20:25

1 Answers1

0

You need a way to recognize a packet. For this you need to define your protocol. I guess you want to keep it really simple and low-overhead, so I'll give an example.

+----------+-------+------+------+------+------+------+------+------+------+-----------+
| Byte     | 0     | 1    | 2    | 3    | 4    | 5    | 6    | 7    | 8    | 9         |
+==========+=======+======+======+======+======+======+======+======+======+===========+
| Function | Start | Data | Data | Data | Data | Data | Data | Data | Data | CheckSum  |
+----------+-------+------+------+------+------+------+------+------+------+-----------+
| Value    | 0     | X    | X    | X    | X    | X    | X    | X    | X    | XOR 0..=8 |
+----------+-------+------+------+------+------+------+------+------+------+-----------+

So what do we have here? Our message always starts with a 0. This way we can recognize the start of the message. The data can still contain 0's because we don't rely on it, it just makes our job easier. (This could be any value, really, but 0 will do the job just fine)

After our start byte, we get our data. This is just the ABCD you used in your example. Our last byte is a very simple checksum. We simple XOR together all the bytes.

How does this all work then?

  1. Collect some data.
  2. If data < 10 bytes, go to 1
  3. If data[0] != 0 (our start byte), then remove the first byte from our buffer. Go to 2.
  4. XOR together all bytes to get our calculated checksum.
  5. If data[9] != calculated_checksum, then remove the first byte from our buffer. Go to 2.
    (We did detect a start byte, but the checksum is incorrect. Therefore we know that the current data is not a packet)
  6. We've got the packet! (Probably*) Read bytes 1..=8 for processing. Then remove the first 10 bytes from the buffer. Go to 2.

That's it! But there's a lot of things to improve for performance and reliability.

  1. Better start header that supports dynamic lengths and multiple message types.
  2. Better checksum. XOR is not very good for reliability. A CRC is one of the best.
  3. Buffer management. You could use a cyclical buffer so you don't have to remove any bytes.
Geoxion
  • 448
  • 1
  • 4
  • 14