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 {
|
Command {
|
||||||
text: format!("AT+CIPSEND={}", size),
|
text: format!("AT+CIPSEND={}", size),
|
||||||
timeout: Duration::from_millis(3000),
|
timeout: Duration::from_millis(3000),
|
||||||
|
@ -279,11 +279,11 @@ impl Command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tcp_write(payload: &str) -> Command {
|
pub fn tcp_send() -> Command {
|
||||||
Command {
|
Command {
|
||||||
text: payload.to_string(),
|
text: "AT+CIPSEND".to_string(),
|
||||||
timeout: Duration::from_millis(2000),
|
timeout: Duration::from_millis(3000),
|
||||||
contains: Some("> ".to_string()),
|
contains: Some(">".to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
124
src/modem.rs
124
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 type Result<T> = std::result::Result<T, ModemError>;
|
||||||
|
|
||||||
pub struct Modem<UART: serial::Uart> {
|
pub struct Modem<UART: serial::Uart> {
|
||||||
rx: Rx<UART>,
|
rx: IterableRx<UART>,
|
||||||
tx: Tx<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> {
|
impl<UART: serial::Uart> Modem<UART> {
|
||||||
pub fn new(tx: Tx<UART>, rx: Rx<UART>) -> Self {
|
pub fn new(tx: Tx<UART>, rx: Rx<UART>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
rx,
|
rx: IterableRx { inner: rx, timeout: None },
|
||||||
tx,
|
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.
|
/// 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> {
|
fn read_line(&mut self, timeout: Duration) -> Result<String> {
|
||||||
let mut buf = String::new();
|
let line: String = self.rx.reset(timeout)
|
||||||
let start = Instant::now();
|
.map(|b| char::from(b))
|
||||||
|
.take_while(|c| *c != '\n')
|
||||||
|
.collect();
|
||||||
|
|
||||||
loop {
|
// A necessary check because the actual timeout is in the Iterator implementation. The
|
||||||
match self.rx.read() {
|
// iterator exits when the returned Item is None, which happens when there's no data in
|
||||||
Ok(b) => {
|
// the serial port and the timeout is breached.
|
||||||
print!("{}, ", b);
|
//
|
||||||
buf.push(b as char);
|
// This check here is tested on sim800l and works only because the modem has \r\n as CRLF.
|
||||||
if b == '\n' as u8 {
|
if line.ends_with("\r") {
|
||||||
return Ok(buf);
|
Ok(line)
|
||||||
}
|
}
|
||||||
},
|
else {
|
||||||
_ => {
|
Err(ModemError::ReadError)
|
||||||
if Instant::now() > start + timeout {
|
|
||||||
return Err(ModemError::ReadError);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
thread::sleep(Duration::from_millis(200));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,25 +140,24 @@ impl<UART: serial::Uart> Modem<UART> {
|
||||||
fn read_response(&mut self, contains: Option<String>, timeout: Duration) -> Result<String> {
|
fn read_response(&mut self, contains: Option<String>, timeout: Duration) -> Result<String> {
|
||||||
let mut response = String::new();
|
let mut response = String::new();
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let match_text: String = contains.unwrap_or("".to_string());
|
let match_text: String = contains.unwrap_or("\n".to_string());
|
||||||
|
|
||||||
loop {
|
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);
|
response.push_str(&line);
|
||||||
if line.contains("ERROR") || line.contains(&match_text) {
|
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!("-----------------------------------------------------------");
|
println!("-----------------------------------------------------------");
|
||||||
break;
|
break Ok(response.to_string())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if Instant::now() > start + timeout {
|
println!("-----------------------------------------------------------");
|
||||||
return Err(ModemError::TimeoutError);
|
println!("Read line {:?}", rdln);
|
||||||
}
|
break Err(ModemError::TimeoutError)
|
||||||
println!("got into retry loop in read_response");
|
|
||||||
thread::sleep(Duration::from_millis(200));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(response.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send(&mut self, b: u8) -> Result<()> {
|
fn send(&mut self, b: u8) -> Result<()> {
|
||||||
|
@ -147,7 +178,24 @@ impl<UART: serial::Uart> Modem<UART> {
|
||||||
println!("-----------------------------------------------------------");
|
println!("-----------------------------------------------------------");
|
||||||
println!("Sending {} ...", cmd.text);
|
println!("Sending {} ...", cmd.text);
|
||||||
let _ = self.send_bytes(cmd.text.as_bytes())?;
|
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> {
|
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<()> {
|
pub fn tcp_send(&mut self, payload: &str) -> Result<()> {
|
||||||
self.send_command(Command::tcp_send(payload.len()))?;
|
self.send_command(Command::tcp_send_size(payload.len()))?;
|
||||||
self.send_command(Command::tcp_write(payload))?;
|
self.send_command_data(Command::tcp_send(), payload)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue