implement iterator on reader (serial rx)
This commit is contained in:
parent
792eef13ba
commit
08c5cabe6d
2 changed files with 91 additions and 43 deletions
|
@ -271,7 +271,7 @@ impl Command {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn tcp_send(size: usize) -> Command {
|
||||
pub fn tcp_send_size(size: usize) -> Command {
|
||||
Command {
|
||||
text: format!("AT+CIPSEND={}", size),
|
||||
timeout: Duration::from_millis(3000),
|
||||
|
@ -279,11 +279,11 @@ impl Command {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn tcp_write(payload: &str) -> Command {
|
||||
pub fn tcp_send() -> Command {
|
||||
Command {
|
||||
text: payload.to_string(),
|
||||
timeout: Duration::from_millis(2000),
|
||||
contains: Some("> ".to_string()),
|
||||
text: "AT+CIPSEND".to_string(),
|
||||
timeout: Duration::from_millis(3000),
|
||||
contains: Some(">".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
120
src/modem.rs
120
src/modem.rs
|
@ -11,7 +11,7 @@ use esp_idf_hal::serial::{self, Rx, Tx};
|
|||
pub type Result<T> = std::result::Result<T, ModemError>;
|
||||
|
||||
pub struct Modem<UART: serial::Uart> {
|
||||
rx: Rx<UART>,
|
||||
rx: IterableRx<UART>,
|
||||
tx: Tx<UART>,
|
||||
}
|
||||
|
||||
|
@ -31,10 +31,51 @@ impl std::fmt::Display for ModemError {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct IterableRx<UART: serial::Uart> {
|
||||
inner: Rx<UART>,
|
||||
timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
impl<UART: serial::Uart> IterableRx<UART> {
|
||||
fn reset(&mut self, timeout: Duration) -> &mut Self {
|
||||
self.timeout = Some(timeout);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: serial::Uart> Iterator for IterableRx<UART> {
|
||||
type Item = u8;
|
||||
|
||||
/// `nb` returns Ok(byte), or one of Err(WouldBlock) and Err(Other) which isn't of anyone's
|
||||
/// interest, so the retry mechanism is triggered on _any_ error every 200ms until a byte is
|
||||
/// received, or the timeout is reached.
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let now = Instant::now();
|
||||
loop {
|
||||
let timeout = self.timeout.unwrap_or(Duration::from_millis(0));
|
||||
match self.inner.read() {
|
||||
Ok(b) => {
|
||||
break Some(b)
|
||||
},
|
||||
_ => {
|
||||
print!("got into retry loop in read iterator, ");
|
||||
if now.elapsed() > timeout {
|
||||
println!("exiting because timeout expired :(");
|
||||
break None
|
||||
}
|
||||
println!("waiting 200ms ...");
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
self.timeout = Some(timeout - now.elapsed());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: serial::Uart> Modem<UART> {
|
||||
pub fn new(tx: Tx<UART>, rx: Rx<UART>) -> Self {
|
||||
Self {
|
||||
rx,
|
||||
rx: IterableRx { inner: rx, timeout: None },
|
||||
tx,
|
||||
}
|
||||
}
|
||||
|
@ -70,32 +111,23 @@ impl<UART: serial::Uart> Modem<UART> {
|
|||
}
|
||||
|
||||
/// Reads a whole line (that ends with \\n) within the given `timeout` passed on input.
|
||||
/// `nb` returns Ok(byte), or one of Err(WouldBlock) and Err(Other) which isn't of anyone's
|
||||
/// interest, so the retry mechanism is triggered on _any_ error every 200ms until a byte is
|
||||
/// received, or the timeout is reached.
|
||||
///
|
||||
fn read_line(&mut self, timeout: Duration) -> Result<String> {
|
||||
let mut buf = String::new();
|
||||
let start = Instant::now();
|
||||
let line: String = self.rx.reset(timeout)
|
||||
.map(|b| char::from(b))
|
||||
.take_while(|c| *c != '\n')
|
||||
.collect();
|
||||
|
||||
loop {
|
||||
match self.rx.read() {
|
||||
Ok(b) => {
|
||||
print!("{}, ", b);
|
||||
buf.push(b as char);
|
||||
if b == '\n' as u8 {
|
||||
return Ok(buf);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
if Instant::now() > start + timeout {
|
||||
return Err(ModemError::ReadError);
|
||||
// A necessary check because the actual timeout is in the Iterator implementation. The
|
||||
// iterator exits when the returned Item is None, which happens when there's no data in
|
||||
// the serial port and the timeout is breached.
|
||||
//
|
||||
// This check here is tested on sim800l and works only because the modem has \r\n as CRLF.
|
||||
if line.ends_with("\r") {
|
||||
Ok(line)
|
||||
}
|
||||
else {
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(ModemError::ReadError)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,25 +140,24 @@ impl<UART: serial::Uart> Modem<UART> {
|
|||
fn read_response(&mut self, contains: Option<String>, timeout: Duration) -> Result<String> {
|
||||
let mut response = String::new();
|
||||
let start = Instant::now();
|
||||
let match_text: String = contains.unwrap_or("".to_string());
|
||||
let match_text: String = contains.unwrap_or("\n".to_string());
|
||||
|
||||
loop {
|
||||
if let Ok(line) = self.read_line(start + timeout - Instant::now()) {
|
||||
let rdln = self.read_line(start + timeout - Instant::now());
|
||||
if let Ok(line) = rdln {
|
||||
println!("Read {} bytes from serial ({})", line.len(), line);
|
||||
response.push_str(&line);
|
||||
if line.contains("ERROR") || line.contains(&match_text) {
|
||||
println!("Found match {} for line {} ... exiting response reader now!", match_text, line);
|
||||
println!("Found match {} for line {} ; exiting response reader now ...", match_text, line);
|
||||
println!("-----------------------------------------------------------");
|
||||
break;
|
||||
break Ok(response.to_string())
|
||||
}
|
||||
} else {
|
||||
if Instant::now() > start + timeout {
|
||||
return Err(ModemError::TimeoutError);
|
||||
}
|
||||
println!("got into retry loop in read_response");
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
println!("-----------------------------------------------------------");
|
||||
println!("Read line {:?}", rdln);
|
||||
break Err(ModemError::TimeoutError)
|
||||
}
|
||||
}
|
||||
Ok(response.to_string())
|
||||
}
|
||||
|
||||
fn send(&mut self, b: u8) -> Result<()> {
|
||||
|
@ -147,7 +178,24 @@ impl<UART: serial::Uart> Modem<UART> {
|
|||
println!("-----------------------------------------------------------");
|
||||
println!("Sending {} ...", cmd.text);
|
||||
let _ = self.send_bytes(cmd.text.as_bytes())?;
|
||||
self.read_response(cmd.contains.clone(), cmd.timeout.clone())
|
||||
self.read_response(cmd.contains, cmd.timeout)
|
||||
}
|
||||
|
||||
fn send_command_data(&mut self, cmd: Command, payload: &str) -> Result<String> {
|
||||
println!("-----------------------------------------------------------");
|
||||
println!("Sending {} ...", cmd.text);
|
||||
let _ = self.send_bytes(cmd.text.as_bytes())?;
|
||||
|
||||
let prompt: String = self.rx.reset(cmd.timeout)
|
||||
.map(|b| char::from(b))
|
||||
.take_while(|c| *c != ' ')
|
||||
.collect();
|
||||
|
||||
if prompt != ">".to_string() {
|
||||
println!("invalid prompt: {}", prompt);
|
||||
}
|
||||
let _ = self.send_bytes(payload.as_bytes())?;
|
||||
self.read_response(cmd.contains, cmd.timeout)
|
||||
}
|
||||
|
||||
pub fn get_ip_addr(&mut self) -> Result<String> {
|
||||
|
@ -214,8 +262,8 @@ impl<UART: serial::Uart> Modem<UART> {
|
|||
}
|
||||
|
||||
pub fn tcp_send(&mut self, payload: &str) -> Result<()> {
|
||||
self.send_command(Command::tcp_send(payload.len()))?;
|
||||
self.send_command(Command::tcp_write(payload))?;
|
||||
self.send_command(Command::tcp_send_size(payload.len()))?;
|
||||
self.send_command_data(Command::tcp_send(), payload)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue