Comments (6)
@craigkai Windows COMMTIMEOUTS are extremely cursed, and I'm nearly certain they're at fault here.
On Windows, if you try to read 'n' bytes from a serial port no data will be returned until**:
- A timeout has occurred
- Your buffer has been filled
In theory, you could set your read timeout to be 1ms to get only kind of bad latency, but in practice this just doesn't work and you're looking at a minimum timeout of 10+ milliseconds. So. What can you do?
-
Read one byte at time, and just loop until you read your terminator. You can pretty much do this by using a BufReader with capacity of 1.
-
Know how much data your responses will contain, and don't try to
read_until
until at least 'n' bytes are available. This results in a lot of syscalls while you busy-poll the available bytes, but lower latency. You could achieve this a few ways:
a. Looking up the response length base on passed command from a table
b. Adding some fixed-length header to your response that contains the message length
c. Probably some other ways... -
Number 1, but use async so that you don't need to busy wait for each byte. (This is actually how PuTTy waits for data). You can make this work with
mio-serial
andtokio-serial
, but not without hackingNamedPipe
to have a 1-byte buffer
**Actually, when I told you this I was lying; Windows only behaves like this for certain configurations of COMMTIMEOUTS. Feel free to refer to this table (credit: Carsten Andrich)
Denomination | termios |
COMMTIMEOUTS |
POSIX behavior | Windows behavior |
---|---|---|---|---|
Polling read | MIN = 0 and TIME = 0 |
ReadIntervalTimeout = MAXDWORD , ReadTotalTimeoutMultiplier = 0 , and ReadTotalTimeoutConstant = 0 |
Always returns immediately | Like POSIX |
Blocking read | MIN = 1 and TIME = 0 |
ReadIntervalTimeout = 0 , ReadTotalTimeoutMultiplier = 0 , and ReadTotalTimeoutConstant = 0 |
Returns when at least 1 byte is available | Returns only when buffer is full |
Blocking read | MIN = 1 and TIME = 0 |
ReadIntervalTimeout = 1 , ReadTotalTimeoutMultiplier = 0 , and ReadTotalTimeoutConstant = 0 |
Returns when at least 1 byte is available | Like POSIX, but presumably extra delay |
Read with timeout | MIN = 0 and TIME > 0 |
ReadIntervalTimeout = MAXDWORD , ReadTotalTimeoutMultiplier = MAXDWORD , and ReadTotalTimeoutConstant > 0 |
Returns when at least 1 byte is available or timeout occurs | Similar to POSIX, but first ReadFile() returns only 1 byte and second ReadFile() returns the remainder |
Read with timeout | MIN = 0 and TIME > 0 |
ReadIntervalTimeout = 1 , ReadTotalTimeoutMultiplier = 0 , and ReadTotalTimeoutConstant > 0 |
Returns when at least 1 byte is available or timeout occurs | Like POSIX, but presumably extra delay |
from serialport-rs.
This has been so helpful, thank you so much for all the assistance!!
from serialport-rs.
Fast BufReader
example (loopback port):
use std::io::{BufReader, BufRead};
fn main() {
let mut port = serialport::new("COM3", 9600)
.timeout(std::time::Duration::from_millis(500))
.open()
.expect("failed to open port");
let reader = port.try_clone()
.expect("failed to clone port");
port.write_all(&[0xDE, 0xAD, 0xBE, 0xEF, 0x00])
.expect("failed to write to port");
// Create the slow bufreader
let mut buf = vec![];
let mut b_reader = BufReader::with_capacity(100, reader);
let now = std::time::Instant::now();
if let Err(e) = b_reader.read_until(0x00, &mut buf) {
println!("Slow reader error: {:?}", e);
}
println!("slow read time: {}ms", now.elapsed().as_millis());
// Fast bufreader
port.write_all(&[0xDE, 0xAD, 0xBE, 0xEF, 0x00])
.expect("failed to write to port");
let mut b_reader = BufReader::with_capacity(1, b_reader.into_inner());
let now = std::time::Instant::now();
if let Err(e) = b_reader.read_until(0x00, &mut buf) {
println!("Fast reader error: {:?}", e);
}
println!("fast read time: {}ms", now.elapsed().as_millis());
}
Which gives me:
slow read time: 515ms
fast read time: 19ms
from serialport-rs.
To me this perhaps implies we need to refactor things so this is more apparent to the user. Windows behavior is apparently commonly counterintuitive.
That or the default windows serial port config needs to be changed to better mirror POSIX behavior and this information added to the docs.
from serialport-rs.
I have suggested a change to use the POSIX-like blocking read (with early return on ANY data) and timeout in #79. This fixes the general case of of read blocking until the buffer is full. It only makes it slightly more CPU intensive as it will return as much as in the FIFO any time the read call is invoked, but for serial port speeds, this is irrelevant.
This change is necessary for any serial port protocol that uses request/response scheme where the size of packets is unknown in advance, or when using mio-seriel/tokio-serial where you can't tell the underlying library how much data you want to read.
from serialport-rs.
I've just started using serialport-rs crate, but I also require the POSIX-like (new) blocking read() behavior that larsch mentions in his post. I would hope this could be incorporated as a new Windows specific feature that a developer can explicitly enable (opt into). Ideally this could be added as a non-breaking change, by defining additional functions or methods to activate it. Thus it would only be 'new' user code that activates the feature.
from serialport-rs.
Related Issues (20)
- Add supported architecture HOT 2
- windows: UsbPortInfo: Provide the same info about Manufacturer and Product name as POSIX HOT 2
- Auto reconnect HOT 1
- use read_to_end always return TimedOut err HOT 2
- Stuck when writing data to the serial port if paired port is not open [Windows 11] HOT 2
- USB ports detected as unknown in docker HOT 1
- Print to COM port via USB to SERIAL converter cable on Windows HOT 1
- Set a Custom Baudrate HOT 1
- Unplug serial device cause system shutdown on windows HOT 5
- Fast receive missing beginning of packet HOT 3
- Tracking issue for WebSerial support. HOT 1
- Can Add Mark and Space to Parity? HOT 1
- Configurable buffer size HOT 1
- Update dependency on nix to 0.28 HOT 3
- Potentially confusing read_exact timeout semantics HOT 1
- wrong serial number for FTDI modules ? HOT 1
- Timeout reading SerialUSB from Arduino DUE or GIGA HOT 3
- Outdated documentation for TTYPort HOT 2
- Opening a COM port on Windows fails HOT 5
- Cannot list virtual serial ports on Windows 10 HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from serialport-rs.