use embassy_time::Duration;
use heapless::String;
use crate::at::command;
use crate::at::processor::AtProcessor;
use crate::at::AtResponse;
use crate::bus::SpiTransport;
use crate::error::{Error, Result};
use crate::sync::TmMutex;
use crate::types::Ipv4Address;
pub struct DnsResolver {
processor: &'static AtProcessor,
timeout: Duration,
}
impl DnsResolver {
pub const fn new(processor: &'static AtProcessor, timeout: Duration) -> Self {
Self { processor, timeout }
}
pub async fn resolve<SPI, CS>(
&self,
spi: &'static TmMutex<SpiTransport<SPI, CS>>,
hostname: &str,
) -> Result<Ipv4Address>
where
SPI: embedded_hal_async::spi::SpiDevice,
CS: embedded_hal::digital::OutputPin,
{
let cmd = command::network::dns_lookup(hostname)?;
let response = self
.processor
.send_command(spi, cmd.as_bytes(), self.timeout)
.await?;
if let AtResponse::Data { prefix, content } = response {
if prefix.as_str() == "+CIPDOMAIN" {
let ip_str = crate::at::parser::unquote(&content);
return crate::at::parser::parse_ip(ip_str);
}
}
Err(Error::InvalidResponse)
}
pub async fn set_dns_servers<SPI, CS>(
&self,
spi: &'static TmMutex<SpiTransport<SPI, CS>>,
primary: &str,
secondary: Option<&str>,
) -> Result<()>
where
SPI: embedded_hal_async::spi::SpiDevice,
CS: embedded_hal::digital::OutputPin,
{
let cmd = command::network::set_dns(true, primary, secondary)?;
let response = self
.processor
.send_command(spi, cmd.as_bytes(), self.timeout)
.await?;
if response == AtResponse::Ok {
Ok(())
} else {
Err(Error::AtCommandFailed)
}
}
pub async fn get_dns_servers<SPI, CS>(
&self,
spi: &'static TmMutex<SpiTransport<SPI, CS>>,
) -> Result<(Ipv4Address, Option<Ipv4Address>)>
where
SPI: embedded_hal_async::spi::SpiDevice,
CS: embedded_hal::digital::OutputPin,
{
let cmd = command::network::get_dns()?;
let response = self
.processor
.send_command(spi, cmd.as_bytes(), self.timeout)
.await?;
if let AtResponse::Data { prefix, content } = response {
if prefix.as_str().starts_with("+CIPDNS") {
let parts = crate::at::parser::parse_csv(&content);
if !parts.is_empty() {
let primary_str = crate::at::parser::unquote(&parts[0]);
let primary = crate::at::parser::parse_ip(primary_str)?;
let secondary = if parts.len() > 1 {
let secondary_str = crate::at::parser::unquote(&parts[1]);
Some(crate::at::parser::parse_ip(secondary_str)?)
} else {
None
};
return Ok((primary, secondary));
}
}
}
Err(Error::InvalidResponse)
}
}
pub struct SntpClient {
processor: &'static AtProcessor,
timeout: Duration,
}
impl SntpClient {
pub const fn new(processor: &'static AtProcessor, timeout: Duration) -> Self {
Self { processor, timeout }
}
pub async fn configure<SPI, CS>(
&self,
spi: &'static TmMutex<SpiTransport<SPI, CS>>,
enable: bool,
timezone: i8,
server: &str,
) -> Result<()>
where
SPI: embedded_hal_async::spi::SpiDevice,
CS: embedded_hal::digital::OutputPin,
{
let cmd = command::network::configure_sntp(enable, timezone, server)?;
let response = self
.processor
.send_command(spi, cmd.as_bytes(), self.timeout)
.await?;
if response == AtResponse::Ok {
Ok(())
} else {
Err(Error::AtCommandFailed)
}
}
pub async fn get_time<SPI, CS>(
&self,
spi: &'static TmMutex<SpiTransport<SPI, CS>>,
) -> Result<String<64>>
where
SPI: embedded_hal_async::spi::SpiDevice,
CS: embedded_hal::digital::OutputPin,
{
let cmd = command::network::get_sntp_time()?;
let response = self
.processor
.send_command(spi, cmd.as_bytes(), self.timeout)
.await?;
if let AtResponse::Data { prefix, content } = response {
if prefix.as_str() == "+CIPSNTPTIME" {
let time_str = crate::at::parser::unquote(&content);
let mut result = String::new();
result
.push_str(time_str)
.map_err(|_| Error::BufferTooSmall)?;
return Ok(result);
}
}
Err(Error::InvalidResponse)
}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PingResult {
pub rtt_ms: u32,
pub success: bool,
}
pub struct Ping {
processor: &'static AtProcessor,
#[allow(dead_code)]
timeout: Duration,
}
impl Ping {
pub const fn new(processor: &'static AtProcessor, timeout: Duration) -> Self {
Self { processor, timeout }
}
pub async fn ping<SPI, CS>(
&self,
spi: &'static TmMutex<SpiTransport<SPI, CS>>,
host: &str,
) -> Result<PingResult>
where
SPI: embedded_hal_async::spi::SpiDevice,
CS: embedded_hal::digital::OutputPin,
{
let cmd = command::network::ping(host)?;
let response = self
.processor
.send_command(spi, cmd.as_bytes(), Duration::from_secs(10))
.await?;
if let AtResponse::Data { prefix, content } = response {
if prefix.as_str() == "+PING" {
if let Ok(rtt) = crate::at::parser::parse_int(&content) {
return Ok(PingResult {
rtt_ms: rtt as u32,
success: true,
});
}
}
}
Ok(PingResult {
rtt_ms: 0,
success: false,
})
}
}