use bitflags::bitflags;
use core::ffi::CStr;
use super::packer::{Packer, Unpacker};
const CRC16: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_XMODEM);
const FRAME_END: u8 = 3;
const FRAME_START_SHORT: u8 = 2;
const FW_VERSION_NAME_MAX_LEN: usize = 39;
const FW_INFO_COMMIT_HASH_MAX_LEN: usize = 47;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum EncodeError {
#[error("the output buffer provided for encoding is too small")]
BufferTooSmall,
}
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum DecodeError {
#[error("the input buffer for decoding does not contain enough data for a complete frame")]
IncompleteData,
#[error("checksum mismatch: expected 0x{expected:X}, but found 0x{actual:X}")]
ChecksumMismatch { expected: u16, actual: u16 },
#[error("unrecognized or unsupported packet: {id}")]
UnknownPacket { id: u8 },
#[error("the frame structure is frame")]
InvalidFrame,
}
#[repr(u8)]
enum CommandId {
FwVersion = 0,
GetValues = 4,
SetDuty = 5,
SetCurrent = 6,
SetCurrentBrake = 7,
SetRpm = 8,
SetPos = 9,
SetHandbrake = 10,
Reboot = 29,
Alive = 30,
ForwardCan = 34,
GetValuesSelective = 50,
GetValuesSetupSelective = 51,
SetCurrentRel = 84,
SetOdometer = 110,
GetStats = 128,
ResetStats = 129,
Shutdown = 156,
FwInfo = 157,
MotorEstop = 159,
}
impl TryFrom<u8> for CommandId {
type Error = DecodeError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
id if id == CommandId::FwVersion as u8 => Ok(CommandId::FwVersion),
id if id == CommandId::GetValues as u8 => Ok(CommandId::GetValues),
id if id == CommandId::SetDuty as u8 => Ok(CommandId::SetDuty),
id if id == CommandId::SetCurrent as u8 => Ok(CommandId::SetCurrent),
id if id == CommandId::SetCurrentBrake as u8 => Ok(CommandId::SetCurrentBrake),
id if id == CommandId::SetRpm as u8 => Ok(CommandId::SetRpm),
id if id == CommandId::SetPos as u8 => Ok(CommandId::SetPos),
id if id == CommandId::SetHandbrake as u8 => Ok(CommandId::SetHandbrake),
id if id == CommandId::Reboot as u8 => Ok(CommandId::Reboot),
id if id == CommandId::Alive as u8 => Ok(CommandId::Alive),
id if id == CommandId::ForwardCan as u8 => Ok(CommandId::ForwardCan),
id if id == CommandId::GetValuesSelective as u8 => Ok(CommandId::GetValuesSelective),
id if id == CommandId::GetValuesSetupSelective as u8 => {
Ok(CommandId::GetValuesSetupSelective)
}
id if id == CommandId::SetCurrentRel as u8 => Ok(CommandId::SetCurrentRel),
id if id == CommandId::SetOdometer as u8 => Ok(CommandId::SetOdometer),
id if id == CommandId::GetStats as u8 => Ok(CommandId::GetStats),
id if id == CommandId::ResetStats as u8 => Ok(CommandId::ResetStats),
id if id == CommandId::Shutdown as u8 => Ok(CommandId::Shutdown),
id if id == CommandId::FwInfo as u8 => Ok(CommandId::FwInfo),
id if id == CommandId::MotorEstop as u8 => Ok(CommandId::MotorEstop),
id => Err(DecodeError::UnknownPacket { id }),
}
}
}
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ValuesMask(u32);
bitflags! {
impl ValuesMask: u32 {
const TEMP_MOSFET = 1 << 0;
const TEMP_MOTOR = 1 << 1;
const AVG_CURRENT_MOTOR = 1 << 2;
const AVG_CURRENT_INPUT = 1 << 3;
const AVG_CURRENT_D = 1 << 4;
const AVG_CURRENT_Q = 1 << 5;
const DUTY_CYCLE = 1 << 6;
const RPM = 1 << 7;
const VOLTAGE_IN = 1 << 8;
const AMP_HOURS = 1 << 9;
const AMP_HOURS_CHARGED = 1 << 10;
const WATT_HOURS = 1 << 11;
const WATT_HOURS_CHARGED = 1 << 12;
const TACHOMETER = 1 << 13;
const TACHOMETER_ABS = 1 << 14;
const FAULT_CODE = 1 << 15;
const PID_POS = 1 << 16;
const CONTROLLER_ID = 1 << 17;
const TEMP_MOSFET_ALL = 1 << 18;
const AVG_VOLTAGE_D = 1 << 19;
const AVG_VOLTAGE_Q = 1 << 20;
const STATUS = 1 << 21;
}
}
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ValuesSetupMask(u32);
bitflags! {
impl ValuesSetupMask: u32 {
const TEMP_MOSFET = 1 << 0;
const TEMP_MOTOR = 1 << 1;
const AVG_CURRENT_MOTOR = 1 << 2;
const AVG_CURRENT_INPUT = 1 << 3;
const DUTY_CYCLE = 1 << 4;
const RPM = 1 << 5;
const SPEED = 1 << 6;
const VOLTAGE_IN = 1 << 7;
const BATTERY_LEVEL = 1 << 8;
const AMP_HOURS = 1 << 9;
const AMP_HOURS_CHARGED = 1 << 10;
const WATT_HOURS = 1 << 11;
const WATT_HOURS_CHARGED = 1 << 12;
const DISTANCE = 1 << 13;
const DISTANCE_ABS = 1 << 14;
const PID_POS = 1 << 15;
const FAULT_CODE = 1 << 16;
const CONTROLLER_ID = 1 << 17;
const NUM_VESCS = 1 << 18;
const WATT_HOURS_BATTERY_LEFT = 1 << 19;
const ODOMETER = 1 << 20;
const UPTIME_MS = 1 << 21;
}
}
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct StatsMask(u16);
bitflags! {
impl StatsMask: u16 {
const SPEED_AVG = 1 << 0;
const SPEED_MAX = 1 << 1;
const POWER_AVG = 1 << 2;
const POWER_MAX = 1 << 3;
const CURRENT_AVG = 1 << 4;
const CURRENT_MAX = 1 << 5;
const TEMP_MOSFET_AVG = 1 << 6;
const TEMP_MOSFET_MAX = 1 << 7;
const TEMP_MOTOR_AVG = 1 << 8;
const TEMP_MOTOR_MAX = 1 << 9;
const COUNT_TIME = 1 << 10;
}
}
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Command<'a> {
FwVersion,
GetValues,
SetDuty(f32),
SetCurrent(f32),
SetCurrentBrake(f32),
SetRpm(i32),
SetPos(f32),
SetHandbrake(f32),
Reboot,
Alive,
ForwardCan(
u8,
#[cfg_attr(feature = "defmt", defmt(Debug2Format))] &'a Command<'a>,
),
GetValuesSelective(ValuesMask),
GetValuesSetupSelective(ValuesSetupMask),
SetCurrentRel(f32),
SetOdometer(u32),
GetStats(StatsMask),
ResetStats(bool),
Shutdown(bool, bool),
FwInfo,
MotorEstop(u16),
}
impl<'a> Command<'a> {
fn pack_into(&self, packer: &mut Packer) -> Result<(), EncodeError> {
match self {
Self::FwVersion => {
packer.pack_u8(CommandId::FwVersion as u8)?;
}
Self::GetValues => {
packer.pack_u8(CommandId::GetValues as u8)?;
}
Self::SetDuty(duty) => {
packer.pack_u8(CommandId::SetDuty as u8)?;
packer.pack_f32(*duty, 100000.0)?;
}
Self::SetCurrent(current) => {
packer.pack_u8(CommandId::SetCurrent as u8)?;
packer.pack_f32(*current, 1000.0)?;
}
Self::SetCurrentBrake(current) => {
packer.pack_u8(CommandId::SetCurrentBrake as u8)?;
packer.pack_f32(*current, 1000.0)?;
}
Self::SetRpm(rpm) => {
packer.pack_u8(CommandId::SetRpm as u8)?;
packer.pack_i32(*rpm)?;
}
Self::SetPos(pos) => {
packer.pack_u8(CommandId::SetPos as u8)?;
packer.pack_f32(*pos, 1000000.0)?;
}
Self::SetHandbrake(current) => {
packer.pack_u8(CommandId::SetHandbrake as u8)?;
packer.pack_f32(*current, 1000.0)?;
}
Self::Reboot => {
packer.pack_u8(CommandId::Reboot as u8)?;
}
Self::Alive => {
packer.pack_u8(CommandId::Alive as u8)?;
}
Self::ForwardCan(controller_id, command) => {
packer.pack_u8(CommandId::ForwardCan as u8)?;
packer.pack_u8(*controller_id)?;
command.pack_into(packer)?;
}
Self::GetValuesSelective(mask) => {
packer.pack_u8(CommandId::GetValuesSelective as u8)?;
packer.pack_u32(mask.bits())?;
}
Self::GetValuesSetupSelective(mask) => {
packer.pack_u8(CommandId::GetValuesSetupSelective as u8)?;
packer.pack_u32(mask.bits())?;
}
Self::SetCurrentRel(current_rel) => {
packer.pack_u8(CommandId::SetCurrentRel as u8)?;
packer.pack_f32(*current_rel, 100000.0)?;
}
Self::SetOdometer(odometer) => {
packer.pack_u8(CommandId::SetOdometer as u8)?;
packer.pack_u32(*odometer)?;
}
Self::GetStats(mask) => {
packer.pack_u8(CommandId::GetStats as u8)?;
packer.pack_u16(mask.bits())?;
}
Self::ResetStats(ack) => {
packer.pack_u8(CommandId::ResetStats as u8)?;
packer.pack_u8(*ack as u8)?;
}
Self::Shutdown(force, restart) => {
packer.pack_u8(CommandId::Shutdown as u8)?;
packer.pack_u8(*force as u8)?;
packer.pack_u8(*restart as u8)?;
}
Self::FwInfo => {
packer.pack_u8(CommandId::FwInfo as u8)?;
}
Self::MotorEstop(ignore_time_ms) => {
packer.pack_u8(CommandId::MotorEstop as u8)?;
packer.pack_u16(*ignore_time_ms)?;
}
}
Ok(())
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum FaultCode {
#[default]
None = 0,
OverVoltage,
UnderVoltage,
Drv,
AbsOverCurrent,
OverTempFet,
OverTempMotor,
GateDriverOverVoltage,
GateDriverUnderVoltage,
McuUnderVoltage,
BootingFromWatchdogReset,
EncoderSpi,
EncoderSinCosBelowMinAmplitude,
EncoderSinCosAboveMaxAmplitude,
FlashCorruption,
HighOffsetCurrentSensor1,
HighOffsetCurrentSensor2,
HighOffsetCurrentSensor3,
UnbalancedCurrents,
Brk,
ResolverLot,
ResolverDos,
ResolverLos,
FlashCorruptionAppCfg,
FlashCorruptionMcCfg,
EncoderNoMagnet,
EncoderMagnetTooStrong,
PhaseFilter,
EncoderFault,
LvOutputFault,
Unknown = 255,
}
impl FaultCode {
pub fn as_str(&self) -> &'static str {
use FaultCode::*;
match self {
None => "FAULT_CODE_NONE",
OverVoltage => "FAULT_CODE_OVER_VOLTAGE",
UnderVoltage => "FAULT_CODE_UNDER_VOLTAGE",
Drv => "FAULT_CODE_DRV",
AbsOverCurrent => "FAULT_CODE_ABS_OVER_CURRENT",
OverTempFet => "FAULT_CODE_OVER_TEMP_FET",
OverTempMotor => "FAULT_CODE_OVER_TEMP_MOTOR",
GateDriverOverVoltage => "FAULT_CODE_GATE_DRIVER_OVER_VOLTAGE",
GateDriverUnderVoltage => "FAULT_CODE_GATE_DRIVER_UNDER_VOLTAGE",
McuUnderVoltage => "FAULT_CODE_MCU_UNDER_VOLTAGE",
BootingFromWatchdogReset => "FAULT_CODE_BOOTING_FROM_WATCHDOG_RESET",
EncoderSpi => "FAULT_CODE_ENCODER_SPI",
EncoderSinCosBelowMinAmplitude => "FAULT_CODE_ENCODER_SINCOS_BELOW_MIN_AMPLITUDE",
EncoderSinCosAboveMaxAmplitude => "FAULT_CODE_ENCODER_SINCOS_ABOVE_MAX_AMPLITUDE",
FlashCorruption => "FAULT_CODE_FLASH_CORRUPTION",
HighOffsetCurrentSensor1 => "FAULT_CODE_HIGH_OFFSET_CURRENT_SENSOR_1",
HighOffsetCurrentSensor2 => "FAULT_CODE_HIGH_OFFSET_CURRENT_SENSOR_2",
HighOffsetCurrentSensor3 => "FAULT_CODE_HIGH_OFFSET_CURRENT_SENSOR_3",
UnbalancedCurrents => "FAULT_CODE_UNBALANCED_CURRENTS",
Brk => "FAULT_CODE_BRK",
ResolverLot => "FAULT_CODE_RESOLVER_LOT",
ResolverDos => "FAULT_CODE_RESOLVER_DOS",
ResolverLos => "FAULT_CODE_RESOLVER_LOS",
FlashCorruptionAppCfg => "FAULT_CODE_FLASH_CORRUPTION_APP_CFG",
FlashCorruptionMcCfg => "FAULT_CODE_FLASH_CORRUPTION_MC_CFG",
EncoderNoMagnet => "FAULT_CODE_ENCODER_NO_MAGNET",
EncoderMagnetTooStrong => "FAULT_CODE_ENCODER_MAGNET_TOO_STRONG",
PhaseFilter => "FAULT_CODE_PHASE_FILTER",
EncoderFault => "FAULT_CODE_ENCODER_FAULT",
LvOutputFault => "FAULT_CODE_LV_OUTPUT_FAULT",
Unknown => "UNKNOWN",
}
}
}
impl From<u8> for FaultCode {
fn from(value: u8) -> Self {
use FaultCode::*;
match value {
v if v == None as u8 => None,
v if v == OverVoltage as u8 => OverVoltage,
v if v == UnderVoltage as u8 => UnderVoltage,
v if v == Drv as u8 => Drv,
v if v == AbsOverCurrent as u8 => AbsOverCurrent,
v if v == OverTempFet as u8 => OverTempFet,
v if v == OverTempMotor as u8 => OverTempMotor,
v if v == GateDriverOverVoltage as u8 => GateDriverOverVoltage,
v if v == GateDriverUnderVoltage as u8 => GateDriverUnderVoltage,
v if v == McuUnderVoltage as u8 => McuUnderVoltage,
v if v == BootingFromWatchdogReset as u8 => BootingFromWatchdogReset,
v if v == EncoderSpi as u8 => EncoderSpi,
v if v == EncoderSinCosBelowMinAmplitude as u8 => EncoderSinCosBelowMinAmplitude,
v if v == EncoderSinCosAboveMaxAmplitude as u8 => EncoderSinCosAboveMaxAmplitude,
v if v == FlashCorruption as u8 => FlashCorruption,
v if v == HighOffsetCurrentSensor1 as u8 => HighOffsetCurrentSensor1,
v if v == HighOffsetCurrentSensor2 as u8 => HighOffsetCurrentSensor2,
v if v == HighOffsetCurrentSensor3 as u8 => HighOffsetCurrentSensor3,
v if v == UnbalancedCurrents as u8 => UnbalancedCurrents,
v if v == Brk as u8 => Brk,
v if v == ResolverLot as u8 => ResolverLot,
v if v == ResolverDos as u8 => ResolverDos,
v if v == ResolverLos as u8 => ResolverLos,
v if v == FlashCorruptionAppCfg as u8 => FlashCorruptionAppCfg,
v if v == FlashCorruptionMcCfg as u8 => FlashCorruptionMcCfg,
v if v == EncoderNoMagnet as u8 => EncoderNoMagnet,
v if v == EncoderMagnetTooStrong as u8 => EncoderMagnetTooStrong,
v if v == PhaseFilter as u8 => PhaseFilter,
v if v == EncoderFault as u8 => EncoderFault,
v if v == LvOutputFault as u8 => LvOutputFault,
_ => Unknown,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum HwType {
Vesc = 0,
VescBms = 1,
CustomModule = 2,
Unknown = 255,
}
impl From<u8> for HwType {
fn from(value: u8) -> Self {
match value {
v if v == HwType::Vesc as u8 => HwType::Vesc,
v if v == HwType::VescBms as u8 => HwType::VescBms,
v if v == HwType::CustomModule as u8 => HwType::CustomModule,
_ => HwType::Unknown,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum QmlHw {
None = 0,
Embedded = 1,
Fullscreen = 2,
Unknown = 255,
}
impl From<u8> for QmlHw {
fn from(value: u8) -> Self {
match value {
v if v == QmlHw::None as u8 => QmlHw::None,
v if v == QmlHw::Embedded as u8 => QmlHw::Embedded,
v if v == QmlHw::Fullscreen as u8 => QmlHw::Fullscreen,
_ => QmlHw::Unknown,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct QmlAppFlags(u8);
bitflags! {
impl QmlAppFlags: u8 {
const EMBEDDED = 1 << 0;
const FULLSCREEN = 1 << 1;
const _ = !0;
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct NrfFlags(u8);
bitflags! {
impl NrfFlags: u8 {
const _ = !0;
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FwVersion {
pub major: u8,
pub minor: u8,
pub hw_name: [u8; FW_VERSION_NAME_MAX_LEN],
pub uuid: [u8; 12],
pub pairing_done: bool,
pub test_version_number: u8,
pub hw_type: HwType,
pub custom_config_num: u8,
pub has_phase_filters: bool,
pub qml_hw: QmlHw,
pub qml_app: QmlAppFlags,
pub nrf_flags: NrfFlags,
pub fw_name: [u8; FW_VERSION_NAME_MAX_LEN],
pub hw_crc: u32,
}
impl FwVersion {
pub fn hw_name(&self) -> Option<&str> {
let cstr = CStr::from_bytes_until_nul(&self.hw_name).ok()?;
cstr.to_str().ok()
}
pub fn fw_name(&self) -> Option<&str> {
let cstr = CStr::from_bytes_until_nul(&self.fw_name).ok()?;
cstr.to_str().ok()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FwInfo {
pub major: u8,
pub minor: u8,
pub test_version_number: u8,
pub commit_hash: [u8; FW_INFO_COMMIT_HASH_MAX_LEN],
pub user_commit_hash: [u8; FW_INFO_COMMIT_HASH_MAX_LEN],
}
impl FwInfo {
pub fn commit_hash(&self) -> Option<&str> {
let cstr = CStr::from_bytes_until_nul(&self.commit_hash).ok()?;
cstr.to_str().ok()
}
pub fn user_commit_hash(&self) -> Option<&str> {
let cstr = CStr::from_bytes_until_nul(&self.user_commit_hash).ok()?;
cstr.to_str().ok()
}
}
#[derive(Debug, Copy, Clone, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Values {
pub temp_mosfet: f32,
pub temp_motor: f32,
pub avg_current_motor: f32,
pub avg_current_input: f32,
pub avg_current_d: f32,
pub avg_current_q: f32,
pub duty_cycle: f32,
pub rpm: f32,
pub voltage_in: f32,
pub amp_hours: f32,
pub amp_hours_charged: f32,
pub watt_hours: f32,
pub watt_hours_charged: f32,
pub tachometer: i32,
pub tachometer_abs: i32,
pub fault_code: FaultCode,
pub pid_pos: f32,
pub controller_id: u8,
pub temp_mosfet1: f32,
pub temp_mosfet2: f32,
pub temp_mosfet3: f32,
pub avg_voltage_d: f32,
pub avg_voltage_q: f32,
pub status: u8,
}
#[derive(Debug, Copy, Clone, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SetupValues {
pub temp_mosfet: f32,
pub temp_motor: f32,
pub avg_current_motor: f32,
pub avg_current_input: f32,
pub duty_cycle: f32,
pub rpm: f32,
pub speed: f32,
pub voltage_in: f32,
pub battery_level: f32,
pub amp_hours: f32,
pub amp_hours_charged: f32,
pub watt_hours: f32,
pub watt_hours_charged: f32,
pub distance: f32,
pub distance_abs: f32,
pub pid_pos: f32,
pub fault_code: FaultCode,
pub controller_id: u8,
pub num_vescs: u8,
pub watt_hours_battery_left: f32,
pub odometer: u32,
pub uptime_ms: u32,
}
#[derive(Debug, Copy, Clone, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Stats {
pub speed_avg: f32,
pub speed_max: f32,
pub power_avg: f32,
pub power_max: f32,
pub current_avg: f32,
pub current_max: f32,
pub temp_mosfet_avg: f32,
pub temp_mosfet_max: f32,
pub temp_motor_avg: f32,
pub temp_motor_max: f32,
pub count_time: f32,
}
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CommandReply {
FwVersion(FwVersion),
GetValues(Values),
GetValuesSelective(Values),
GetValuesSetupSelective(SetupValues),
GetStats(Stats),
ResetStats,
FwInfo(FwInfo),
}
impl CommandReply {
fn unpack_from(unpacker: &mut Unpacker) -> Result<Self, DecodeError> {
Ok(match unpacker.unpack_u8()?.try_into()? {
CommandId::FwVersion => Self::unpack_fw_version(unpacker)?,
CommandId::GetValues => Self::unpack_get_values(unpacker)?,
CommandId::GetValuesSelective => Self::unpack_get_values_selective(unpacker)?,
CommandId::GetValuesSetupSelective => {
Self::unpack_get_values_setup_selective(unpacker)?
}
CommandId::GetStats => Self::unpack_get_stats(unpacker)?,
CommandId::ResetStats => Self::unpack_reset_stats(),
CommandId::FwInfo => Self::unpack_fw_info(unpacker)?,
id => return Err(DecodeError::UnknownPacket { id: id as u8 }),
})
}
fn unpack_fw_version(unpacker: &mut Unpacker) -> Result<Self, DecodeError> {
let fw_version = FwVersion {
major: unpacker.unpack_u8()?,
minor: unpacker.unpack_u8()?,
hw_name: unpacker.unpack_c_string::<FW_VERSION_NAME_MAX_LEN>()?,
uuid: unpacker.unpack_uuid()?,
pairing_done: unpacker.unpack_u8()? != 0,
test_version_number: unpacker.unpack_u8()?,
hw_type: unpacker.unpack_u8()?.into(),
custom_config_num: unpacker.unpack_u8()?,
has_phase_filters: unpacker.unpack_u8()? != 0,
qml_hw: unpacker.unpack_u8()?.into(),
qml_app: QmlAppFlags::from_bits_retain(unpacker.unpack_u8()?),
nrf_flags: NrfFlags::from_bits_retain(unpacker.unpack_u8()?),
fw_name: unpacker.unpack_c_string::<FW_VERSION_NAME_MAX_LEN>()?,
hw_crc: unpacker.unpack_u32()?,
};
Ok(CommandReply::FwVersion(fw_version))
}
fn unpack_get_values(unpacker: &mut Unpacker) -> Result<Self, DecodeError> {
let values = Values {
temp_mosfet: unpacker.unpack_f16(10.0)?,
temp_motor: unpacker.unpack_f16(10.0)?,
avg_current_motor: unpacker.unpack_f32(100.0)?,
avg_current_input: unpacker.unpack_f32(100.0)?,
avg_current_d: unpacker.unpack_f32(100.0)?,
avg_current_q: unpacker.unpack_f32(100.0)?,
duty_cycle: unpacker.unpack_f16(1000.0)?,
rpm: unpacker.unpack_f32(1.0)?,
voltage_in: unpacker.unpack_f16(10.0)?,
amp_hours: unpacker.unpack_f32(10000.0)?,
amp_hours_charged: unpacker.unpack_f32(10000.0)?,
watt_hours: unpacker.unpack_f32(10000.0)?,
watt_hours_charged: unpacker.unpack_f32(10000.0)?,
tachometer: unpacker.unpack_i32()?,
tachometer_abs: unpacker.unpack_i32()?,
fault_code: unpacker.unpack_u8()?.into(),
pid_pos: unpacker.unpack_f32(1000000.0)?,
controller_id: unpacker.unpack_u8()?,
temp_mosfet1: unpacker.unpack_f16(10.0)?,
temp_mosfet2: unpacker.unpack_f16(10.0)?,
temp_mosfet3: unpacker.unpack_f16(10.0)?,
avg_voltage_d: unpacker.unpack_f32(1000.0)?,
avg_voltage_q: unpacker.unpack_f32(1000.0)?,
status: unpacker.unpack_u8()?,
};
Ok(CommandReply::GetValues(values))
}
fn unpack_get_values_selective(unpacker: &mut Unpacker) -> Result<Self, DecodeError> {
let mut values = Values::default();
let mask = ValuesMask::from_bits_retain(unpacker.unpack_u32()?);
if mask.contains(ValuesMask::TEMP_MOSFET) {
values.temp_mosfet = unpacker.unpack_f16(10.0)?;
}
if mask.contains(ValuesMask::TEMP_MOTOR) {
values.temp_motor = unpacker.unpack_f16(10.0)?;
}
if mask.contains(ValuesMask::AVG_CURRENT_MOTOR) {
values.avg_current_motor = unpacker.unpack_f32(100.0)?;
}
if mask.contains(ValuesMask::AVG_CURRENT_INPUT) {
values.avg_current_input = unpacker.unpack_f32(100.0)?;
}
if mask.contains(ValuesMask::AVG_CURRENT_D) {
values.avg_current_d = unpacker.unpack_f32(100.0)?;
}
if mask.contains(ValuesMask::AVG_CURRENT_Q) {
values.avg_current_q = unpacker.unpack_f32(100.0)?;
}
if mask.contains(ValuesMask::DUTY_CYCLE) {
values.duty_cycle = unpacker.unpack_f16(1000.0)?;
}
if mask.contains(ValuesMask::RPM) {
values.rpm = unpacker.unpack_f32(1.0)?;
}
if mask.contains(ValuesMask::VOLTAGE_IN) {
values.voltage_in = unpacker.unpack_f16(10.0)?;
}
if mask.contains(ValuesMask::AMP_HOURS) {
values.amp_hours = unpacker.unpack_f32(10000.0)?;
}
if mask.contains(ValuesMask::AMP_HOURS_CHARGED) {
values.amp_hours_charged = unpacker.unpack_f32(10000.0)?;
}
if mask.contains(ValuesMask::WATT_HOURS) {
values.watt_hours = unpacker.unpack_f32(10000.0)?;
}
if mask.contains(ValuesMask::WATT_HOURS_CHARGED) {
values.watt_hours_charged = unpacker.unpack_f32(10000.0)?;
}
if mask.contains(ValuesMask::TACHOMETER) {
values.tachometer = unpacker.unpack_i32()?;
}
if mask.contains(ValuesMask::TACHOMETER_ABS) {
values.tachometer_abs = unpacker.unpack_i32()?;
}
if mask.contains(ValuesMask::FAULT_CODE) {
values.fault_code = unpacker.unpack_u8()?.into();
}
if mask.contains(ValuesMask::PID_POS) {
values.pid_pos = unpacker.unpack_f32(1000000.0)?;
}
if mask.contains(ValuesMask::CONTROLLER_ID) {
values.controller_id = unpacker.unpack_u8()?;
}
if mask.contains(ValuesMask::TEMP_MOSFET_ALL) {
values.temp_mosfet1 = unpacker.unpack_f16(10.0)?;
values.temp_mosfet2 = unpacker.unpack_f16(10.0)?;
values.temp_mosfet3 = unpacker.unpack_f16(10.0)?;
}
if mask.contains(ValuesMask::AVG_VOLTAGE_D) {
values.avg_voltage_d = unpacker.unpack_f32(1000.0)?;
}
if mask.contains(ValuesMask::AVG_VOLTAGE_Q) {
values.avg_voltage_q = unpacker.unpack_f32(1000.0)?;
}
if mask.contains(ValuesMask::STATUS) {
values.status = unpacker.unpack_u8()?;
}
Ok(CommandReply::GetValuesSelective(values))
}
fn unpack_get_values_setup_selective(unpacker: &mut Unpacker) -> Result<Self, DecodeError> {
let mut values = SetupValues::default();
let mask = ValuesSetupMask::from_bits_retain(unpacker.unpack_u32()?);
if mask.contains(ValuesSetupMask::TEMP_MOSFET) {
values.temp_mosfet = unpacker.unpack_f16(10.0)?;
}
if mask.contains(ValuesSetupMask::TEMP_MOTOR) {
values.temp_motor = unpacker.unpack_f16(10.0)?;
}
if mask.contains(ValuesSetupMask::AVG_CURRENT_MOTOR) {
values.avg_current_motor = unpacker.unpack_f32(100.0)?;
}
if mask.contains(ValuesSetupMask::AVG_CURRENT_INPUT) {
values.avg_current_input = unpacker.unpack_f32(100.0)?;
}
if mask.contains(ValuesSetupMask::DUTY_CYCLE) {
values.duty_cycle = unpacker.unpack_f16(1000.0)?;
}
if mask.contains(ValuesSetupMask::RPM) {
values.rpm = unpacker.unpack_f32(1.0)?;
}
if mask.contains(ValuesSetupMask::SPEED) {
values.speed = unpacker.unpack_f32(1000.0)?;
}
if mask.contains(ValuesSetupMask::VOLTAGE_IN) {
values.voltage_in = unpacker.unpack_f16(10.0)?;
}
if mask.contains(ValuesSetupMask::BATTERY_LEVEL) {
values.battery_level = unpacker.unpack_f16(1000.0)?;
}
if mask.contains(ValuesSetupMask::AMP_HOURS) {
values.amp_hours = unpacker.unpack_f32(10000.0)?;
}
if mask.contains(ValuesSetupMask::AMP_HOURS_CHARGED) {
values.amp_hours_charged = unpacker.unpack_f32(10000.0)?;
}
if mask.contains(ValuesSetupMask::WATT_HOURS) {
values.watt_hours = unpacker.unpack_f32(10000.0)?;
}
if mask.contains(ValuesSetupMask::WATT_HOURS_CHARGED) {
values.watt_hours_charged = unpacker.unpack_f32(10000.0)?;
}
if mask.contains(ValuesSetupMask::DISTANCE) {
values.distance = unpacker.unpack_f32(1000.0)?;
}
if mask.contains(ValuesSetupMask::DISTANCE_ABS) {
values.distance_abs = unpacker.unpack_f32(1000.0)?;
}
if mask.contains(ValuesSetupMask::PID_POS) {
values.pid_pos = unpacker.unpack_f32(1000000.0)?;
}
if mask.contains(ValuesSetupMask::FAULT_CODE) {
values.fault_code = unpacker.unpack_u8()?.into();
}
if mask.contains(ValuesSetupMask::CONTROLLER_ID) {
values.controller_id = unpacker.unpack_u8()?;
}
if mask.contains(ValuesSetupMask::NUM_VESCS) {
values.num_vescs = unpacker.unpack_u8()?;
}
if mask.contains(ValuesSetupMask::WATT_HOURS_BATTERY_LEFT) {
values.watt_hours_battery_left = unpacker.unpack_f32(1000.0)?;
}
if mask.contains(ValuesSetupMask::ODOMETER) {
values.odometer = unpacker.unpack_u32()?;
}
if mask.contains(ValuesSetupMask::UPTIME_MS) {
values.uptime_ms = unpacker.unpack_u32()?;
}
Ok(CommandReply::GetValuesSetupSelective(values))
}
fn unpack_get_stats(unpacker: &mut Unpacker) -> Result<Self, DecodeError> {
let mut stats = Stats::default();
let mask = unpacker.unpack_u32()?;
if mask & (StatsMask::SPEED_AVG.bits() as u32) != 0 {
stats.speed_avg = unpacker.unpack_f32_auto()?;
}
if mask & (StatsMask::SPEED_MAX.bits() as u32) != 0 {
stats.speed_max = unpacker.unpack_f32_auto()?;
}
if mask & (StatsMask::POWER_AVG.bits() as u32) != 0 {
stats.power_avg = unpacker.unpack_f32_auto()?;
}
if mask & (StatsMask::POWER_MAX.bits() as u32) != 0 {
stats.power_max = unpacker.unpack_f32_auto()?;
}
if mask & (StatsMask::CURRENT_AVG.bits() as u32) != 0 {
stats.current_avg = unpacker.unpack_f32_auto()?;
}
if mask & (StatsMask::CURRENT_MAX.bits() as u32) != 0 {
stats.current_max = unpacker.unpack_f32_auto()?;
}
if mask & (StatsMask::TEMP_MOSFET_AVG.bits() as u32) != 0 {
stats.temp_mosfet_avg = unpacker.unpack_f32_auto()?;
}
if mask & (StatsMask::TEMP_MOSFET_MAX.bits() as u32) != 0 {
stats.temp_mosfet_max = unpacker.unpack_f32_auto()?;
}
if mask & (StatsMask::TEMP_MOTOR_AVG.bits() as u32) != 0 {
stats.temp_motor_avg = unpacker.unpack_f32_auto()?;
}
if mask & (StatsMask::TEMP_MOTOR_MAX.bits() as u32) != 0 {
stats.temp_motor_max = unpacker.unpack_f32_auto()?;
}
if mask & (StatsMask::COUNT_TIME.bits() as u32) != 0 {
stats.count_time = unpacker.unpack_f32_auto()?;
}
Ok(CommandReply::GetStats(stats))
}
fn unpack_reset_stats() -> Self {
CommandReply::ResetStats
}
fn unpack_fw_info(unpacker: &mut Unpacker) -> Result<Self, DecodeError> {
let fw_info = FwInfo {
major: unpacker.unpack_u8()?,
minor: unpacker.unpack_u8()?,
test_version_number: unpacker.unpack_u8()?,
commit_hash: unpacker.unpack_c_string::<FW_INFO_COMMIT_HASH_MAX_LEN>()?,
user_commit_hash: unpacker.unpack_c_string::<FW_INFO_COMMIT_HASH_MAX_LEN>()?,
};
Ok(CommandReply::FwInfo(fw_info))
}
}
pub fn encode(command: Command, buf: &mut [u8]) -> Result<usize, EncodeError> {
let mut packer = Packer::new(buf);
packer.pack_u8(FRAME_START_SHORT)?;
packer.pack_u8(0)?;
command.pack_into(&mut packer)?;
let payload_len = packer.pos - 2;
packer.buf[1] = payload_len as u8;
packer.pack_u16(CRC16.checksum(&packer.buf[2..2 + payload_len]))?;
packer.pack_u8(FRAME_END)?;
Ok(packer.pos)
}
pub fn decode(buf: &[u8]) -> Result<(usize, CommandReply), DecodeError> {
let mut unpacker = Unpacker::new(buf);
let frame_start = unpacker.unpack_u8()?;
if frame_start != FRAME_START_SHORT {
return Err(DecodeError::InvalidFrame);
}
let payload_len = unpacker.unpack_u8()? as usize;
let reply = CommandReply::unpack_from(&mut unpacker)?;
if payload_len != unpacker.pos - (frame_start as usize) {
return Err(DecodeError::InvalidFrame);
}
let payload = &unpacker.buf[(frame_start as usize)..unpacker.pos];
let checksum_expected = unpacker.unpack_u16()?;
if unpacker.unpack_u8()? != FRAME_END {
return Err(DecodeError::InvalidFrame);
}
let checksum_actual = CRC16.checksum(payload);
if checksum_actual != checksum_expected {
return Err(DecodeError::ChecksumMismatch {
expected: checksum_expected,
actual: checksum_actual,
});
}
Ok((unpacker.pos, reply))
}