#![allow(dead_code)]
extern crate embedded_hal as hal;
use hal::blocking::i2c::{Read, Write, WriteRead};
use std::thread::sleep;
use std::time::{Duration, Instant};
const I2C_ADDRESS: u8 = 0x44;
const PWM_MAX: f32 = 255.0;
const I2C_MAX_LEN: u8 = 4;
const I2C_ID_PICOBORG_REV: u8 = 0x15;
enum Command {
SetLed = 1, GetLed = 2, SetAFwd = 3, SetARev = 4, GetA = 5, SetBFwd = 6, SetBRev = 7, GetB = 8, AllOff = 9, ResetEpo = 10, GetEpo = 11, SetEpoIgnore = 12, GetEpoIgnore = 13, GetDriveFault = 14, SetAllFwd = 15, SetAllRev = 16, SetFailsafe = 17, GetFailsafe = 18, SetEncMode = 19, GetEncMode = 20, MoveAFwd = 21, MoveARev = 22, MoveBFwd = 23, MoveBRev = 24, MoveAllFwd = 25, MoveAllRev = 26, GetEncMoving = 27, SetEncSpeed = 28, GetEncSpeed = 29, GetId = 0x99, SetI2CAdd = 0xAa, }
const FORWARD: u8 = 1; const REVERSE: u8 = 2;
const VALUE_ON: u8 = 1; const VALUE_OFF: u8 = 0;
pub struct PicoBorgRev<T: Read + Write + WriteRead> {
device: T,
}
impl<T: Read + Write + WriteRead> PicoBorgRev<T> {
pub fn new(device: T) -> Result<PicoBorgRev<T>, <T as WriteRead>::Error> {
let mut picoborg = PicoBorgRev { device };
let id = picoborg.read_u8(Command::GetId)?;
if id != I2C_ID_PICOBORG_REV {
println!(
"Found a device at {}, but it is not a PicoBorg Reverse (Id {} instead of {})",
I2C_ADDRESS, id, I2C_ID_PICOBORG_REV
);
} else {
println!("Found PicoBorg Reverse at {}", I2C_ADDRESS);
}
Ok(picoborg)
}
fn write_bool(&mut self, command: Command, value: bool) -> Result<(), <T as Write>::Error> {
self.device.write(
I2C_ADDRESS,
&[command as u8, if value { VALUE_ON } else { VALUE_OFF }],
)
}
fn write_u8(&mut self, command: Command, value: u8) -> Result<(), <T as Write>::Error> {
self.device.write(I2C_ADDRESS, &[command as u8, value])
}
fn write_u16(&mut self, command: Command, value: u16) -> Result<(), <T as Write>::Error> {
let mut data = vec![command as u8];
data.extend(&u16_to_vu8(value));
self.device.write(I2C_ADDRESS, &data)
}
fn read_bool(&mut self, command: Command) -> Result<bool, <T as WriteRead>::Error> {
let mut recv: [u8; 2] = [0; 2];
self.device
.write_read(I2C_ADDRESS, &[command as u8], &mut recv)?;
Ok(recv[1] == VALUE_ON)
}
fn read_u8(&mut self, command: Command) -> Result<u8, <T as WriteRead>::Error> {
let mut recv: [u8; 2] = [0; 2];
self.device
.write_read(I2C_ADDRESS, &[command as u8], &mut recv)?;
Ok(recv[1])
}
fn read_u8_u8(&mut self, command: Command) -> Result<(u8, u8), <T as WriteRead>::Error> {
let mut recv: [u8; 3] = [0; 3];
self.device
.write_read(I2C_ADDRESS, &[command as u8], &mut recv)?;
Ok((recv[1], recv[2]))
}
pub fn set_motor_1(&mut self, power: f32) -> Result<(), <T as Write>::Error> {
if power < 0.0 {
self.write_u8(Command::SetARev, (PWM_MAX * -power) as u8)
} else {
self.write_u8(Command::SetAFwd, (PWM_MAX * power) as u8)
}
}
pub fn set_motor_2(&mut self, power: f32) -> Result<(), <T as Write>::Error> {
if power < 0.0 {
self.write_u8(Command::SetBRev, (PWM_MAX * -power) as u8)
} else {
self.write_u8(Command::SetBFwd, (PWM_MAX * power) as u8)
}
}
pub fn set_motors(&mut self, power: f32) -> Result<(), <T as Write>::Error> {
if power < 0.0 {
self.write_u8(Command::SetAllRev, (PWM_MAX * -power) as u8)
} else {
self.write_u8(Command::SetAllFwd, (PWM_MAX * power) as u8)
}
}
pub fn motors_off(&mut self) -> Result<(), <T as Write>::Error> {
self.write_u8(Command::AllOff, 0)
}
pub fn get_motor_1(&mut self) -> Result<f32, <T as WriteRead>::Error> {
let (direction, power) = self.read_u8_u8(Command::GetA)?;
let power = power as f32 / PWM_MAX;
match direction {
FORWARD => Ok(power),
REVERSE => Ok(-power),
_ => Ok(0.0),
}
}
pub fn get_motor_2(&mut self) -> Result<f32, <T as WriteRead>::Error> {
let (direction, power) = self.read_u8_u8(Command::GetB)?;
let power = power as f32 / PWM_MAX;
match direction {
FORWARD => Ok(power),
REVERSE => Ok(-power),
_ => Ok(0.0),
}
}
pub fn set_led(&mut self, state: bool) -> Result<(), <T as Write>::Error> {
self.write_bool(Command::SetLed, state)
}
pub fn get_led(&mut self) -> Result<bool, <T as WriteRead>::Error> {
self.read_bool(Command::GetLed)
}
pub fn reset_epo(&mut self) -> Result<(), <T as Write>::Error> {
self.write_u8(Command::ResetEpo, 0)
}
pub fn get_epo(&mut self) -> Result<bool, <T as WriteRead>::Error> {
self.read_bool(Command::GetEpo)
}
pub fn set_epo_ignore(&mut self, state: bool) -> Result<(), <T as Write>::Error> {
self.write_bool(Command::SetEpoIgnore, state)
}
pub fn get_epo_ignore(&mut self) -> Result<bool, <T as WriteRead>::Error> {
self.read_bool(Command::GetEpoIgnore)
}
pub fn set_comms_failsafe(&mut self, state: bool) -> Result<(), <T as Write>::Error> {
self.write_bool(Command::SetFailsafe, state)
}
pub fn get_comms_failsafe(&mut self) -> Result<bool, <T as WriteRead>::Error> {
self.read_bool(Command::GetFailsafe)
}
pub fn get_drive_fault(&mut self) -> Result<bool, <T as WriteRead>::Error> {
self.read_bool(Command::GetDriveFault)
}
pub fn set_encoder_move_mode(&mut self, state: bool) -> Result<(), <T as Write>::Error> {
self.write_bool(Command::SetEncMode, state)
}
pub fn get_encoder_move_mode(&mut self) -> Result<bool, <T as WriteRead>::Error> {
self.read_bool(Command::GetEncMode)
}
pub fn encoder_move_motor_1(&mut self, counts: i16) -> Result<(), <T as Write>::Error> {
if counts < 0 {
self.write_u16(Command::MoveARev, -counts as u16)
} else {
self.write_u16(Command::MoveAFwd, counts as u16)
}
}
pub fn encoder_move_motor_2(&mut self, counts: i16) -> Result<(), <T as Write>::Error> {
if counts < 0 {
self.write_u16(Command::MoveBRev, -counts as u16)
} else {
self.write_u16(Command::MoveBFwd, counts as u16)
}
}
pub fn encoder_move_motors(&mut self, counts: i16) -> Result<(), <T as Write>::Error> {
if counts < 0 {
self.write_u16(Command::MoveAllRev, -counts as u16)
} else {
self.write_u16(Command::MoveAllFwd, counts as u16)
}
}
pub fn is_encoder_moving(&mut self) -> Result<bool, <T as WriteRead>::Error> {
self.read_bool(Command::GetEncMoving)
}
pub fn wait_while_encoder_moving(
&mut self,
timeout: Duration,
) -> Result<bool, <T as WriteRead>::Error> {
let now = Instant::now();
while self.is_encoder_moving()? && now.elapsed() < timeout {
sleep(Duration::from_millis(100));
}
Ok(now.elapsed() < timeout)
}
pub fn set_encoder_speed(&mut self, power: f32) -> Result<(), <T as Write>::Error> {
self.write_u8(Command::SetEncSpeed, (PWM_MAX * power) as u8)
}
pub fn get_encoder_speed(&mut self) -> Result<f32, <T as WriteRead>::Error> {
let data = self.read_u8(Command::GetEncMode)?;
Ok(data as f32 / PWM_MAX)
}
}
impl<T: Read + Write + WriteRead> Drop for PicoBorgRev<T> {
fn drop(&mut self) {
let _ = self.motors_off();
let _ = self.set_led(false);
}
}
fn u16_to_vu8(input: u16) -> [u8; 2] {
let one: u8 = ((input >> 8) & 0xFf) as u8;
let two: u8 = (input & 0xFf) as u8;
[one, two]
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}