Hi all,
I want to interface with a few sensors via UART. Examples of these sensors are SDS011 and MH-Z19. What's common of these devices is that they have "request-response" protocol model over serial port, such as:
send 0xff 0x01 0x86 0x00 0x00 0x00 0x00 0x00 0x79 (request measurement)
receive 0xff 0x86 0x02 0x60 0x47 0x00 0x00 0x00 0xd1 (response with measurement data)
Here, response starts with 0xff
and ends with checksum. So, the response has its own framing, to be able to detect that we read it from the right starting byte.
embedded_hal::serial
traints by itself allow to read and write only single character. Writing is straightforward: block!(rx.write(byte))
in loop. write_str
is implemented this way.
But reading is more involved. Naïve way to read response is:
for i in 0..9 {
buf[i] = block!(uart_rx.read())?;
}
This has a problem with desyncing of start of response. If something got wrong, it might be possible that reading loop will start in middle of response. Then it will block until the next response and will read first half of the next response. So, either program will stuck forever, or reading method will return erroneous messages until restart.
What is the best way to deal with this? Maybe someone had already solved this problem?
Is something like this ok?
let mut timeout = /* hal::timer::CountDown implementation */;
timeout.start();
'byte: for i in 0..9 {
loop {
if timeout.is_err() {
// a) Timeout tripped
return /*...*/;
}
match uart_rx.read() {
// b) We have a byte ready to read
Ok(c) => {
buf[i] = c;
continue 'byte;
},
// c) No bytes to read
Err(WouldBlock) => {
continue;
},
// d) Other error
Err(Other(e)) => {
return Err(e);
}
}
}
}