use serial2::SerialPort;
use std::str;
use std::str::FromStr;
use std::thread;
use std::time::Duration;
pub mod error;
use error::LoRaError as Lerr;
pub mod rcv;
use rcv::Rcv;
pub mod transfer;
use transfer::{Context, Transfer};
const LORA_MSG_BUF: usize = 512;
#[derive(Debug)]
pub struct LoRa {
pub serial: SerialPort,
pub config: Config,
pub buf: [u8; LORA_MSG_BUF],
pub verbose: bool,
pub msg_queue: Vec<Rcv>,
}
#[derive(Debug, Default)]
pub struct Config {
pub addr: u16,
pub dest: u16,
pub net: u8,
pub param: Param,
}
#[derive(Debug)]
pub struct Param {
pub spread: u8,
pub bandwidth: u8,
pub coderate: u8,
pub preamble: u8,
}
impl Default for Param {
fn default() -> Self {
Self {
spread: 5,
bandwidth: 9,
coderate: 1,
preamble: 4,
}
}
}
impl LoRa {
pub fn open(port: &str, baud: u32, millis: u64) -> Result<Self, Lerr> {
let mut serial = SerialPort::open(port, baud)?;
serial.set_read_timeout(Duration::from_millis(millis))?;
Ok(Self::new(serial))
}
pub fn new(serial: SerialPort) -> Self {
let config = Config::default();
let msg_queue = Vec::default();
Self {
serial,
config,
buf: [0u8; LORA_MSG_BUF],
verbose: true,
msg_queue,
}
}
pub fn configure(&mut self) -> Result<(), Lerr> {
self.query(&format!("AT+ADDRESS={}", self.config.addr))?;
self.query(&format!("AT+NETWORKID={}", self.config.net))?;
self.query(&format!(
"AT+PARAMETER={},{},{},{}",
self.config.param.spread,
self.config.param.bandwidth,
self.config.param.coderate,
self.config.param.preamble,
))?;
Ok(())
}
fn str_chunk(mut input: &str) -> Vec<String> {
let mut out = Vec::new();
while input.len() > 240 {
out.push(input[..240].to_string());
input = &input[..240];
}
out.push(input.to_string());
out
}
pub fn send_msg(&mut self, msg: &str) -> Result<(), Lerr> {
for chunk in Self::str_chunk(msg) {
let command = format!("AT+SEND={},{},{chunk}", self.config.dest, chunk.len());
self.query(&command)?;
}
Ok(())
}
pub fn send_msg_to(&mut self, dest: u16, msg: &str) -> Result<(), Lerr> {
for chunk in Self::str_chunk(msg) {
let command = format!("AT+SEND={},{},{chunk}", dest, chunk.len());
self.query(&command)?;
}
Ok(())
}
pub fn send_data(&mut self, ctx: &mut Context) -> Result<(), Lerr> {
let dts = ctx.begin_transfer()?;
'begin: loop {
self.send_msg(&String::from(dts.clone()))?;
let msg = self.get_messages()?;
for m in msg {
if m.data == "DTR" {
break 'begin;
}
}
}
'end: loop {
if let Some(next) = ctx.get_next()? {
println!(
"sending {} chunk: {} / {}",
ctx.name, ctx.next_chunk, ctx.total_chunks
);
self.send_msg(&String::from(next))?;
} else {
let dte = ctx.end_transfer()?;
self.send_msg(&String::from(dte))?;
}
for m in self.get_messages()? {
let data = m.data.clone();
let recv = Transfer::try_from(data)?;
ctx.process(&recv)?;
if m.data == "DTD" {
break 'end;
}
}
}
println!("{} transfer complete", ctx.name);
Ok(())
}
pub fn recv_data(&mut self, msg: &Transfer) -> Result<Context, Lerr> {
let mut ctx = Context::new_recv(msg)?;
loop {
if ctx.data.is_empty() {
let dtr = ctx.begin_transfer_ack()?;
self.send_msg(&String::from(dtr))?;
}
if let Err(Lerr::ResponseError(e)) = self.check_read() {
println!("{e} - retrying");
continue;
}
for m in self.get_messages()? {
if let Ok(transfer) = Transfer::try_from(m.data) {
ctx.process(&transfer)?;
println!(
"received {} chunk: {} / {}",
ctx.name,
ctx.next_chunk + 1,
ctx.total_chunks
);
}
}
if ctx.is_transfer_complete() {
break;
}
if let Ok(Some(req)) = ctx.req_data_chunks() {
println!("requesting {:?} chunks", req);
self.send_msg(&String::from(req))?;
}
}
loop {
let dtd = ctx.end_transfer_ack()?;
self.send_msg(&String::from(dtd))?;
self.check_read()?;
let messages = self.get_messages()?;
if messages.is_empty() {
break;
}
for m in messages {
let data = m.data.clone();
let recv = Transfer::try_from(data)?;
ctx.process(&recv)?;
}
}
println!("{} transfer complete", ctx.name);
Ok(ctx)
}
pub fn query(&mut self, query: &str) -> Result<(), Lerr> {
let data = format!("{query}\r\n");
self.write(data.as_bytes())?;
self.read()?;
Ok(())
}
pub fn write(&self, out: &[u8]) -> Result<(), Lerr> {
self.serial.write_all(out)?;
if self.verbose {
Self::internal_print('>', out)?;
}
thread::sleep(Duration::from_millis(75));
Ok(())
}
pub fn read(&mut self) -> Result<(), Lerr> {
self.read_line()?;
if self.verbose {
self.print_buf()?;
}
self.handle_result()?;
Ok(())
}
fn read_line(&mut self) -> Result<(), Lerr> {
self.buf = [0u8; LORA_MSG_BUF];
let mut count = 0;
while count < LORA_MSG_BUF {
self.serial.read_exact(&mut self.buf[count..count + 1])?;
if self.buf[count] == b'\n' {
break;
}
count += 1;
}
Ok(())
}
pub fn check_read(&mut self) -> Result<(), Lerr> {
self.buf = [0u8; LORA_MSG_BUF];
if self.read_line().is_err() {
return Ok(());
};
if self.verbose {
self.print_buf()?;
}
self.handle_result()?;
Ok(())
}
pub fn get_messages(&mut self) -> Result<Vec<Rcv>, Lerr> {
let out = self.msg_queue.clone();
self.msg_queue = Vec::new();
Ok(out)
}
pub fn print_msg_queue(&mut self) -> Result<(), Lerr> {
for msg in &self.msg_queue {
println!("{}", msg.fmt());
}
self.msg_queue = Vec::new();
Ok(())
}
pub fn print_buf(&self) -> Result<(), Lerr> {
Self::internal_print('<', &self.buf)?;
Ok(())
}
fn internal_print(header: char, buf: &[u8]) -> Result<(), Lerr> {
let s = str::from_utf8(buf)?;
println!("{header} {s}");
Ok(())
}
fn internal_error(line: &str) -> Result<(), Lerr> {
let s = line[5..].lines().next().ok_or(Lerr::InternalError)?;
let err = u8::from_str(s)?;
let msg = match err {
1 => "There is no line ending (0x0d 0x0a) at the end of the AT Command",
2 => "The AT Command does not start with \"AT\"",
4 => "Unknown command",
5 => "The data to be sent does not match the actual length",
10 => "TX timeout",
12 => "CRC error",
13 => "TX data exceeds 240 bytes",
14 => "Failed to write flash memory",
15 => "Unknown failure",
17 => "Last TX was incomplete",
18 => "Preamble value is invalid",
19 => "RX failed, Header error",
20 => "The time setting value of the \"Smart receiving power saving mode\" is invalid",
_ => "Unknown error",
};
let err_msg = format!("Error {err}: {msg}");
Err(Lerr::ResponseError(err_msg))
}
fn handle_result(&mut self) -> Result<(), Lerr> {
let buf = str::from_utf8(&self.buf)?;
if buf.len() < 4 {
return Ok(());
}
match &buf[..4] {
"+ERR" => return Self::internal_error(buf),
"+RCV" => match Rcv::try_from(buf) {
Ok(rcv) => self.msg_queue.push(rcv),
Err(e) => println!("RCV error {e}"),
},
&_ => (),
}
Ok(())
}
}