2022-12-09 19:49:54 +00:00
|
|
|
//! This module contains all the structures and functions related to
|
|
|
|
//! interacting with the B15 on a high level. If you are writing code
|
|
|
|
//! for the B15, this is the module you want to use.
|
|
|
|
|
2022-12-15 23:02:22 +00:00
|
|
|
use std::{process::Command, time::Duration, fmt::Debug, thread::sleep};
|
|
|
|
use rand::Rng;
|
2022-12-13 00:38:50 +00:00
|
|
|
use serialport::SerialPort;
|
2022-12-17 18:01:47 +00:00
|
|
|
use crate::error::Error;
|
2022-12-13 00:38:50 +00:00
|
|
|
|
2022-12-15 23:02:22 +00:00
|
|
|
use crate::{request::Request, build_request};
|
2022-12-09 19:49:54 +00:00
|
|
|
|
|
|
|
macro_rules! log {
|
|
|
|
($text: literal, $($arg:tt)*) => (println!(concat!("[B15F] ", $text), $($arg)*));
|
|
|
|
($text: literal) => (println!(concat!("[B15F] ", $text)));
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! log_start {
|
|
|
|
($text: literal, $($arg:tt)*) => (print!(concat!("[B15F] ", $text, "... "), $($arg)*));
|
|
|
|
($text: literal) => (print!(concat!("[B15F] ", $text, "... ")));
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! log_end {
|
|
|
|
($text: literal, $($arg:tt)*) => (println!($text, $($arg)*));
|
|
|
|
($text: literal) => (println!($text));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Structure representing the driver for the board 15
|
|
|
|
pub struct B15F {
|
2022-12-13 00:38:50 +00:00
|
|
|
usart: Box<dyn SerialPort>
|
2022-12-09 19:49:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl B15F {
|
2022-12-15 23:02:22 +00:00
|
|
|
const MSG_OK: u8 = 0xFF;
|
|
|
|
|
2022-12-09 19:49:54 +00:00
|
|
|
/// Creates a new instance of the B15
|
|
|
|
///
|
|
|
|
/// This function will establish a connection to a connected B15 and return
|
|
|
|
/// a handle to interact with it. Only one such instance should exist per
|
|
|
|
/// program; calling `B15F::new()` more than once might lead to unexpected
|
|
|
|
/// behaviour.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// ```
|
|
|
|
/// use b15f::B15F;
|
|
|
|
///
|
|
|
|
/// let drv = B15F::new().unwrap();
|
|
|
|
/// ```
|
2022-12-17 18:01:47 +00:00
|
|
|
pub fn new() -> Result<B15F, Error> {
|
2022-12-13 00:38:50 +00:00
|
|
|
let port = B15F::init_connection()?;
|
2022-12-09 19:49:54 +00:00
|
|
|
|
2022-12-13 00:38:50 +00:00
|
|
|
let mut drv =B15F {
|
|
|
|
usart: port
|
2022-12-09 19:49:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
log_start!("Testing connection");
|
2022-12-15 23:02:22 +00:00
|
|
|
let mut tries = 3;
|
|
|
|
while tries > 0 {
|
2022-12-17 18:01:47 +00:00
|
|
|
drv.discard()?;
|
2022-12-15 23:02:22 +00:00
|
|
|
|
|
|
|
match drv.test_connection() {
|
|
|
|
Ok(()) => break,
|
|
|
|
Err(_) => {} // Do nothing
|
|
|
|
};
|
|
|
|
|
|
|
|
tries -= 1;
|
2022-12-13 00:38:50 +00:00
|
|
|
}
|
2022-12-15 23:02:22 +00:00
|
|
|
|
|
|
|
if tries == 0 {
|
2022-12-17 18:01:47 +00:00
|
|
|
return Err("Connection test failed. Are you using the newest version?".into());
|
2022-12-15 23:02:22 +00:00
|
|
|
}
|
|
|
|
|
2022-12-09 19:49:54 +00:00
|
|
|
log_end!("Ok!");
|
|
|
|
|
2022-12-17 18:01:47 +00:00
|
|
|
let info = drv.get_board_info()?;
|
2022-12-09 19:49:54 +00:00
|
|
|
log!("AVR firmware version: {} built at {} ({})", info[0], info[1], info[2]);
|
|
|
|
|
2022-12-15 23:02:22 +00:00
|
|
|
// let avr_commit_hash = info[3];
|
|
|
|
// if avr_commit_hash != COMMIT_HASH {
|
|
|
|
// log!("Different commit hashes: {} vs {}", avr_commit_hash, COMMIT_HASH);
|
|
|
|
// return Err("Versions incompatible. Please update the software!".into());
|
|
|
|
// }
|
2022-12-09 19:49:54 +00:00
|
|
|
|
2022-12-13 00:38:50 +00:00
|
|
|
Ok(drv)
|
|
|
|
}
|
|
|
|
|
2022-12-17 18:01:47 +00:00
|
|
|
fn init_connection() -> Result<Box<dyn SerialPort>, Error> {
|
2022-12-13 00:38:50 +00:00
|
|
|
let devices = B15F::get_devices();
|
|
|
|
|
|
|
|
let device = match devices.first() {
|
|
|
|
Some(item) => item,
|
|
|
|
None => return Err("Failed to find adapter".into())
|
|
|
|
};
|
|
|
|
|
|
|
|
log!("Using adapter: {}", device);
|
|
|
|
|
|
|
|
log_start!("Establish connection with adapter");
|
|
|
|
|
|
|
|
let port = serialport::new(device, 57_600)
|
|
|
|
.timeout(Duration::from_millis(1000))
|
2022-12-17 18:01:47 +00:00
|
|
|
.open()?;
|
2022-12-13 00:38:50 +00:00
|
|
|
log_end!("Ok!");
|
|
|
|
|
|
|
|
Ok(port)
|
2022-12-09 19:49:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Yields information about the installed firmware on the B15
|
|
|
|
///
|
|
|
|
/// Returns an array of strings, where each string contains a piece
|
|
|
|
/// of information stored on the B15
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// ```
|
|
|
|
/// use b15f::B15F;
|
|
|
|
///
|
2022-12-17 18:01:47 +00:00
|
|
|
/// let mut drv = B15F::new().unwrap();
|
2022-12-09 19:49:54 +00:00
|
|
|
///
|
|
|
|
/// // Print each bit of information on a new line
|
|
|
|
/// drv.get_board_info()
|
2022-12-17 18:01:47 +00:00
|
|
|
/// .unwrap()
|
2022-12-09 19:49:54 +00:00
|
|
|
/// .iter()
|
|
|
|
/// .for_each(|info| println!("{info}"));
|
|
|
|
/// ```
|
2022-12-17 18:01:47 +00:00
|
|
|
pub fn get_board_info(&mut self) -> Result<Vec<String>, Error> {
|
2022-12-16 16:27:20 +00:00
|
|
|
let mut info: Vec<String> = vec![];
|
|
|
|
|
2022-12-17 18:01:47 +00:00
|
|
|
self.usart.write(build_request!(Request::Info))?;
|
2022-12-16 16:27:20 +00:00
|
|
|
|
|
|
|
let mut data_count: [u8; 1] = [0;1];
|
2022-12-17 18:01:47 +00:00
|
|
|
self.usart.read(&mut data_count)?;
|
2022-12-16 16:27:20 +00:00
|
|
|
|
|
|
|
while data_count[0] > 0 {
|
|
|
|
let mut len: [u8; 1] = [0;1];
|
2022-12-17 18:01:47 +00:00
|
|
|
self.usart.read(&mut len)?;
|
2022-12-16 16:27:20 +00:00
|
|
|
|
|
|
|
let mut data: Vec<u8> = vec![0; len[0] as usize];
|
2022-12-17 18:01:47 +00:00
|
|
|
self.usart.read(data.as_mut_slice())?;
|
2022-12-16 16:27:20 +00:00
|
|
|
|
|
|
|
info.push(
|
|
|
|
data.into_iter()
|
|
|
|
.map(|c| char::from(c))
|
|
|
|
.collect::<String>()
|
|
|
|
);
|
|
|
|
|
2022-12-17 18:01:47 +00:00
|
|
|
sleep(Duration::from_millis(4)); // Add delay to give the board time to catch up with our requests
|
2022-12-16 16:27:20 +00:00
|
|
|
data_count[0] -= 1;
|
|
|
|
}
|
|
|
|
|
2022-12-17 18:01:47 +00:00
|
|
|
let mut aw: [u8; 1] = [0; 1];
|
|
|
|
self.usart.read(&mut aw)?;
|
|
|
|
|
|
|
|
if aw[0] != B15F::MSG_OK {
|
|
|
|
return Err(format!("Board info is faulty: code {}", aw[0]).into());
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(info)
|
2022-12-09 19:49:54 +00:00
|
|
|
}
|
|
|
|
|
2022-12-13 00:38:50 +00:00
|
|
|
/// Clears data in the USART buffers on this device and on the B15
|
2022-12-17 18:01:47 +00:00
|
|
|
pub fn discard(&mut self) -> Result<(), Error> {
|
|
|
|
self.usart.clear(serialport::ClearBuffer::Output)?;
|
2022-12-13 00:38:50 +00:00
|
|
|
|
2022-12-15 23:02:22 +00:00
|
|
|
for _ in 0..16 {
|
2022-12-17 18:01:47 +00:00
|
|
|
self.usart.write(build_request![Request::Discard])?;
|
2022-12-13 00:38:50 +00:00
|
|
|
sleep(Duration::from_millis(4));
|
|
|
|
}
|
|
|
|
|
2022-12-17 18:01:47 +00:00
|
|
|
self.usart.clear(serialport::ClearBuffer::Input)?;
|
|
|
|
|
|
|
|
Ok(())
|
2022-12-13 00:38:50 +00:00
|
|
|
}
|
|
|
|
|
2022-12-15 23:02:22 +00:00
|
|
|
/// Tests the connetion to the B15
|
2022-12-17 18:01:47 +00:00
|
|
|
///
|
|
|
|
/// To test the connection a `Request::Test` request will be sent
|
|
|
|
/// to the board together with a randomly generated value. If the
|
|
|
|
/// board returns that value the connection is working correctly.
|
|
|
|
///
|
|
|
|
/// ## Examples
|
|
|
|
/// ```
|
|
|
|
/// use b15f::B15F;
|
|
|
|
///
|
|
|
|
/// fn main() {
|
|
|
|
/// let mut drv = B15F::new().unwrap();
|
|
|
|
///
|
|
|
|
/// if let Err(err) = drv.test_connection() {
|
|
|
|
/// panic!("Connection is not working: {err}");
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
pub fn test_connection(&mut self) -> Result<(), Error> {
|
2022-12-15 23:02:22 +00:00
|
|
|
let dummy: u8 = rand::thread_rng().gen_range(0x00..=0xFF);
|
|
|
|
|
2022-12-17 18:01:47 +00:00
|
|
|
self.usart.write(build_request![Request::Test, dummy])?;
|
2022-12-15 23:02:22 +00:00
|
|
|
|
|
|
|
let mut buffer: [u8; 2]= [0; 2];
|
2022-12-17 18:01:47 +00:00
|
|
|
self.usart.read(&mut buffer)?;
|
2022-12-15 23:02:22 +00:00
|
|
|
|
|
|
|
if buffer[0] != B15F::MSG_OK || buffer[1] != dummy {
|
2022-12-17 18:01:47 +00:00
|
|
|
return Err("Test request failed".into());
|
2022-12-15 23:02:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-12-09 19:49:54 +00:00
|
|
|
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
|
|
|
fn get_devices() -> Vec<String> {
|
|
|
|
let output = Command::new("bash")
|
|
|
|
.args(["-c", "ls /dev/ttyAMA*"])
|
|
|
|
.output()
|
|
|
|
.expect("Failed to get serial interface");
|
|
|
|
|
|
|
|
String::from_utf8(output.stdout)
|
|
|
|
.expect("Failed to convert stdout to string")
|
|
|
|
.split_ascii_whitespace()
|
|
|
|
.map(|item| item.into())
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(target_arch = "arm"))]
|
|
|
|
#[cfg(not(target_arch = "aarch64"))]
|
|
|
|
fn get_devices() -> Vec<String> {
|
|
|
|
|
|
|
|
|
|
|
|
let output = Command::new("bash")
|
|
|
|
.args(["-c", "ls /dev/ttyUSB*"])
|
|
|
|
.output()
|
|
|
|
.expect("Failed to get serial interface");
|
|
|
|
|
|
|
|
String::from_utf8(output.stdout)
|
|
|
|
.expect("Failed to convert stdout to string")
|
|
|
|
.split_ascii_whitespace()
|
|
|
|
.map(|item| item.into())
|
|
|
|
.collect()
|
|
|
|
}
|
2022-12-13 00:38:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Debug for B15F {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
writeln!(f, "Baudrate: {}", self.usart.baud_rate().unwrap())?;
|
|
|
|
writeln!(f, "Data bits: {:?}", self.usart.data_bits().unwrap())?;
|
|
|
|
writeln!(f, "Parity: {:?}", self.usart.parity().unwrap())
|
|
|
|
}
|
2022-12-09 19:49:54 +00:00
|
|
|
}
|