#![cfg_attr(not(feature = "std"), no_std)]
use embedded_io::{Read, Write};
use crate::comm::{
CURRENT_REGISTER, Command, LOAD_REGISTER, MOVING_REGISTER, POSITION_REGISTER, SPEED_REGISTER,
STATUS_REGISTER, TEMPERATURE_REGISTER, VOLTAGE_REGISTER, send_ping, write_position,
};
mod comm;
#[cfg(feature = "ui")]
pub mod info;
pub mod lerobot;
#[derive(Debug, thiserror::Error)]
pub enum ServoError {
#[error("Serial port write error")]
WriteError,
#[error("Serial port read error")]
ReadError,
#[error("Servo returned error status: {0}")]
StatusError(u8),
#[error("Failed to parse servo response")]
ResponseParseError,
#[error("Invalid header bytes: {0:#X}, {1:#X}")]
InvalidHeader(u8, u8),
#[error("Checksum mismatch: calculated {0:#X}, received {1:#X}")]
ChecksumMismatch(u8, u8),
#[error("Command overflow")]
CommandOverflow,
#[error("IO Error")]
IOError,
}
pub fn read_temperature<P: Write + Read>(
port: &mut P,
buffer: &mut [u8],
servo_id: u8,
) -> Result<u8, ServoError> {
read_u8_register(port, buffer, servo_id, TEMPERATURE_REGISTER)
}
pub fn read_voltage<P: Write + Read>(
port: &mut P,
buffer: &mut [u8],
servo_id: u8,
) -> Result<u8, ServoError> {
read_u8_register(port, buffer, servo_id, VOLTAGE_REGISTER)
}
pub fn read_current<P: Write + Read>(
port: &mut P,
buffer: &mut [u8],
servo_id: u8,
) -> Result<u16, ServoError> {
read_u16_register(port, buffer, servo_id, CURRENT_REGISTER)
}
pub fn is_moving<P: Write + Read>(
port: &mut P,
buffer: &mut [u8],
servo_id: u8,
) -> Result<bool, ServoError> {
read_u8_register(port, buffer, servo_id, MOVING_REGISTER).map(|value| value != 0)
}
pub fn has_error<P: Write + Read>(
port: &mut P,
buffer: &mut [u8],
servo_id: u8,
) -> Result<bool, ServoError> {
read_u8_register(port, buffer, servo_id, STATUS_REGISTER).map(|value| value != 0)
}
pub fn read_position<P: Write + Read>(
port: &mut P,
buffer: &mut [u8],
servo_id: u8,
) -> Result<u16, ServoError> {
read_u16_register(port, buffer, servo_id, POSITION_REGISTER)
}
pub fn read_speed<P: Write + Read>(
port: &mut P,
buffer: &mut [u8],
servo_id: u8,
) -> Result<u16, ServoError> {
read_u16_register(port, buffer, servo_id, SPEED_REGISTER)
}
pub fn read_load<P: Write + Read>(
port: &mut P,
buffer: &mut [u8],
servo_id: u8,
) -> Result<u16, ServoError> {
read_u16_register(port, buffer, servo_id, LOAD_REGISTER)
}
pub fn read_u8_register<P: Write + Read>(
port: &mut P,
buffer: &mut [u8],
servo_id: u8,
register_id: u8,
) -> Result<u8, ServoError> {
let result = Command::Read(servo_id, register_id, 1).send_command(port, buffer)?;
result.data_as_u8().ok_or(ServoError::ReadError)
}
pub fn read_u16_register<P: Write + Read>(
port: &mut P,
buffer: &mut [u8],
servo_id: u8,
register_id: u8,
) -> Result<u16, ServoError> {
let result = Command::Read(servo_id, register_id, 2).send_command(port, buffer)?;
result.data_as_u16().ok_or(ServoError::ReadError)
}
pub fn enable_torque<P: Write + Read>(
port: &mut P,
buffer: &mut [u8],
servo_id: u8,
) -> Result<(), ServoError> {
let torque_enable_addr = 0x30;
let enable_value = [0x01]; Command::Write(servo_id, torque_enable_addr, &enable_value)
.send_command(port, buffer)
.and_then(|response| response.is_error())
}
pub fn disable_torque<P: Write + Read>(
port: &mut P,
buffer: &mut [u8],
servo_id: u8,
) -> Result<(), ServoError> {
let torque_enable_addr = 0x30;
let enable_value = [0x00]; Command::Write(servo_id, torque_enable_addr, &enable_value)
.send_command(port, buffer)
.and_then(|response| response.is_error())
}
pub fn move_to_position<P: Write + Read>(
port: &mut P,
buffer: &mut [u8],
servo_id: u8,
position: u16,
speed: Option<u16>,
acc: Option<u16>,
) -> Result<(), ServoError> {
write_position(port, buffer, servo_id, position, speed, acc)?.is_error()
}
pub fn ping_servo<P: Write + Read>(
port: &mut P,
buffer: &mut [u8],
servo_id: u8,
) -> Result<(), ServoError> {
send_ping(port, buffer, servo_id)?.is_error()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_command() {
let cmd = Command::Ping(0x01);
let mut buffer = [0u8; 10];
let len = cmd.write_buffer(&mut buffer);
println!("Command buffer: {:02X?}", &buffer[..len]);
}
}