use core::fmt::Display;
use crate::messages::{
Instruction, InstructionPacket, ReplyPacket, ServoId, ServoIdOrBroadcast, ServoStatusErrors,
};
use crate::registers::Register;
use embassy_time::{Duration, TimeoutError, WithTimeout};
static SERVO_RESPONSE_TIMEOUT: Duration = Duration::from_millis(100);
#[derive(Debug)]
pub enum ServoBusError {
ServoStatus(ServoStatusErrors),
Timeout,
Other(&'static str),
}
impl From<TimeoutError> for ServoBusError {
fn from(_value: TimeoutError) -> Self {
Self::Timeout
}
}
impl From<&'static str> for ServoBusError {
fn from(value: &'static str) -> Self {
Self::Other(value)
}
}
impl Display for ServoBusError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{self:?}")
}
}
impl core::error::Error for ServoBusError {}
pub struct ServoBusAsync<U: embedded_io_async::Read + embedded_io_async::Write> {
uart: U,
}
impl<U: embedded_io_async::Read + embedded_io_async::Write> ServoBusAsync<U> {
pub fn from_uart(uart: U) -> Self {
Self { uart }
}
pub async fn ping_servo(
&mut self,
servo_id: ServoIdOrBroadcast,
) -> Result<ServoId, ServoBusError> {
let command = InstructionPacket {
id: servo_id,
instruction: Instruction::Ping,
};
command.write(&mut self.uart).await.unwrap();
self.uart.flush().await.unwrap();
let reply = ReplyPacket::read_async(&mut self.uart)
.with_timeout(SERVO_RESPONSE_TIMEOUT)
.await?
.map_err(|_| "read failed")?;
Ok(reply.id)
}
pub async fn rotate_servo(
&mut self,
servo_id: ServoId,
increment: i16,
) -> Result<u16, ServoBusError> {
let current = self
.read_register(servo_id.into(), Register::TargetLocation)
.await?;
let next = ((current as i16) + increment) as u16;
self.write_register(servo_id.into(), Register::TargetLocation, next)
.await?;
Ok(next)
}
pub async fn read_register(
&mut self,
servo_id: ServoIdOrBroadcast,
register: Register,
) -> Result<u16, ServoBusError> {
let command = InstructionPacket {
id: servo_id,
instruction: Instruction::read_register(register),
};
command.write(&mut self.uart).await.unwrap();
self.uart.flush().await.unwrap();
let reply = ReplyPacket::read_async(&mut self.uart)
.with_timeout(SERVO_RESPONSE_TIMEOUT)
.await?
.map_err(|_| "read failed")?;
if !reply.servo_status_errors.is_empty() {
return Err(ServoBusError::ServoStatus(reply.servo_status_errors));
}
let parsed = reply.interpret_as_register(register);
Ok(parsed)
}
pub async fn write_register(
&mut self,
servo_id: ServoIdOrBroadcast,
register: Register,
value: u16,
) -> Result<(), ServoBusError> {
let command = InstructionPacket {
id: servo_id,
instruction: Instruction::write_register(register, value),
};
command.write(&mut self.uart).await.unwrap();
self.uart.flush().await.unwrap();
let reply = ReplyPacket::read_async(&mut self.uart)
.with_timeout(SERVO_RESPONSE_TIMEOUT)
.await?
.map_err(|_| "read failed")?;
if !reply.servo_status_errors.is_empty() {
return Err(ServoBusError::ServoStatus(reply.servo_status_errors));
}
Ok(())
}
}