Closed matthew-a-thomas closed 2 years ago
The operation needs to block to complete, but the blocking operation was requested to not occur.
I'm not really sure what that means in this context, though.
Also, it doesn't matter if I change std_in
's assignment to this:
let std_in = std::io::stdin();
let mut std_in = std_in.lock();
When I do this then this lock()
succeeds, but it still raises the error when .read_line()
is called.
Evidently std::io::stdin()
returns something configured for non-blocking IO. So to read characters from it you have to do something like this:
let mut buffer = [0u8; 100];
match std::io::stdin().read(&mut buffer) {
Ok(num_read) => println!("{}", num_read),
Err(error) => match error.kind() {
std::io::ErrorKind::WouldBlock => println!("0"),
_ => return Err(error),
},
};
The key takeaway is you have to (awkwardly) eat the WouldBlock
error and treat that case the same as if no characters were returned.
For example:
struct BlockingReader<R: std::io::Read> {
poll: core::time::Duration,
reader: R,
}
impl<R: Read> From<R> for BlockingReader<R> {
fn from(reader: R) -> Self {
Self {
poll: core::time::Duration::from_millis(250), // Or whatever. Just don't set this so low that you get bit by the watchdog timer
reader,
}
}
}
impl<R: Read> std::io::Read for BlockingReader<R> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
if buf.len() == 0 {
return Ok(0);
}
loop {
match self.reader.read(buf) {
Ok(num_bytes) => return Ok(num_bytes),
Err(error) => match error.kind() {
std::io::ErrorKind::WouldBlock => std::thread::sleep(self.poll),
_ => return Err(error),
}
}
}
}
}
let std_in = std::io::stdin();
let std_in = std_in.lock();
let std_in: BlockingReader<_> = std_in.into();
let mut std_in = std::io::BufReader::new(std_in);
// Now use std_in as normal
If you set the poll interval too low (and you don't type quickly enough) then the watchdog timer will bark at you:
E (7595) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (7595) task_wdt: - IDLE (CPU 0)
E (7595) task_wdt: Tasks currently running:
E (7595) task_wdt: CPU 0: main
Is there some way to configure stdin()
to return something a little more idiomatic?
If I remember correctly, the reason why processing stdin is in such an awkward state (and this is coming from ESP-IDF itself) is because your use case is extremely rare: the stdin/stdout are usually connected (via TTL+USB) to the host PC. This connection is only there for debugging purposes (as in looking at the output from the board while developing and testing the firmware), but this connection is not supposed to be there once your firmware is ready for deployment, as then the board is supposed to be functioning by itself, without connection to the PC. Also, the connection is supposed to be mostly a "one way street" where you can view the log on your PC, but you do not enter input.
Just recently, there was a related discussion in the Matrix channel on that topic, but I can't seem to remember the details.
But let me start from the following: why are you expecting user input from stdin in the first place, given the background details above?
@ivmarkov Thank you for helping me think through this, even though it's completely unrelated to rust-esp32-std-demo
!
why are you expecting user input from stdin in the first place, given the background details above?
I'm not expecting to require stdin
for the final form of my project. While I might expose an interactive menu/configuration over stdin/out, more likely I'll perform first time setup through an HTTP interface while running as an AP. But as I learn the ropes stdin
has proven to be very useful.
I was just surprised to find this friction, and for it to manifest in this way. I suppose I wouldn't have been surprised if there were an error acquiring stdin
. But since there isn't then it seems someone intended it to be available. And if it's supposed to be available I would have expected it to behave like stdin
in most other places.
It's not like there's a violation of Liskov Substitution Principle or anything... the error is being communicated through the API which I've now learned allows for this very error. And it's possible to hide the awkwardness behind a layer of abstraction like I've done. So it's really not a big deal, just odd to me :)
Please feel free to close this issue if you'd like. My little workaround has been enough to keep my proof of concept moving. I appreciate your time!
I apologize if this is not the best place to ask this beginner question. But I'm wondering how to read a line while my C3 is connected with espmonitor? Or if this isn't the best place to ask this, could you direct me to a better location?
I have a ESP32-C3-DevKitM-1. I have managed to piece together the below proof of concept project. But it panics when I try to read a line from
stdin()
https://github.com/matthew-a-thomas/hello_esp32c3/blob/fadceece017f99b7c934d3a80e0e169034d6c91b/src/main.rs#L34If I replace that line with
...then this error appears in the output:
My next thought is to attempt to use
esp-idf-hal
to instantiate aSerial
object from theUART0
port, but I feel like I must be missing something obvious.Thank you!!
I've attached my application binary.
Here is the output I'm seeing with the attached binary/the above linked code: