diff --git a/src/command.rs b/src/command.rs index 2ec20d8..2473187 100644 --- a/src/command.rs +++ b/src/command.rs @@ -178,6 +178,88 @@ impl<'a> Command<'a> { } } +/// Indicates specific error conditions or hardware failures. +/// +/// Fault codes are typically retrieved as part of the [`Values`] struct when +/// calling [`Command::GetValues`] or [`Command::GetValuesSelective`]. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +#[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 From 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, + } + } +} + /// Telemetry data returned by the motor controller. /// /// Contains temperatures, currents, voltages, rpm, and so on. Returned by @@ -203,7 +285,7 @@ pub struct Values { pub watt_hours_charged: f32, pub tachometer: i32, pub tachometer_abs: i32, - pub fault_code: u8, + pub fault_code: FaultCode, pub pid_pos: f32, pub controller_id: u8, pub temp_mosfet1: f32, @@ -257,7 +339,7 @@ impl CommandReply { watt_hours_charged: unpacker.unpack_f32(10000.0)?, tachometer: unpacker.unpack_i32()?, tachometer_abs: unpacker.unpack_i32()?, - fault_code: unpacker.unpack_u8()?, + 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)?, @@ -320,7 +402,7 @@ impl CommandReply { values.tachometer_abs = unpacker.unpack_i32()?; } if mask.contains(ValuesMask::FAULT_CODE) { - values.fault_code = unpacker.unpack_u8()?; + values.fault_code = unpacker.unpack_u8()?.into(); } if mask.contains(ValuesMask::PID_POS) { values.pid_pos = unpacker.unpack_f32(1000000.0)?; diff --git a/src/lib.rs b/src/lib.rs index 4371a76..9570217 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,15 @@ mod decoder; mod packer; pub use command::{ - Command, CommandReply, DecodeError, EncodeError, Values, ValuesMask, decode, encode, + // + Command, + CommandReply, + DecodeError, + EncodeError, + FaultCode, + Values, + ValuesMask, + decode, + encode, }; pub use decoder::Decoder; diff --git a/tests/command_reply.rs b/tests/command_reply.rs index 1280503..3ddc797 100644 --- a/tests/command_reply.rs +++ b/tests/command_reply.rs @@ -1,6 +1,6 @@ use googletest::prelude::*; -use vesc::{CommandReply, DecodeError, Values}; +use vesc::{CommandReply, DecodeError, FaultCode, Values}; #[test] fn decode_get_values_zero_rpm() { @@ -28,7 +28,7 @@ fn decode_get_values_zero_rpm() { watt_hours_charged: approx_eq(0.0), tachometer: eq(-31936), tachometer_abs: eq(174334), - fault_code: eq(0), + fault_code: eq(FaultCode::None), pid_pos: approx_eq(302.39996), controller_id: eq(20), temp_mosfet1: approx_eq(27.7), @@ -69,7 +69,7 @@ fn decode_get_values_forward_rpm() { watt_hours_charged: approx_eq(0.0), tachometer: eq(-37045), tachometer_abs: eq(171975), - fault_code: eq(0), + fault_code: eq(FaultCode::None), pid_pos: approx_eq(74.08746), controller_id: eq(1), temp_mosfet1: approx_eq(27.7), @@ -110,7 +110,48 @@ fn decode_get_values_reverse_rpm() { watt_hours_charged: approx_eq(0.0), tachometer: eq(-28230), tachometer_abs: eq(133952), - fault_code: eq(0), + fault_code: eq(FaultCode::None), + pid_pos: approx_eq(233.10513), + controller_id: eq(20), + temp_mosfet1: approx_eq(26.9), + temp_mosfet2: approx_eq(-90.9), + temp_mosfet3: approx_eq(-94.8), + avg_voltage_d: approx_eq(0.23), + avg_voltage_q: approx_eq(-3.967), + status: eq(0), + }))), + ); + assert_that!(vesc::decode(&input), ok(expected)); +} + +#[test] +fn decode_get_values_motor_fault() { + let input = [ + 2, 74, 4, 1, 13, 0, 0, 0, 0, 0, 92, 0, 0, 0, 12, 0, 0, 0, 0, 255, 255, 255, 169, 255, 19, + 255, 255, 247, 94, 1, 117, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 1, 18, 0, 0, 0, 0, 255, 255, 145, + 186, 0, 2, 11, 64, 2, 13, 228, 230, 240, 20, 1, 13, 252, 115, 252, 76, 0, 0, 0, 230, 255, + 255, 240, 129, 0, 183, 254, 3, + ]; + + let expected = ( + eq(&79), + pat!(&CommandReply::GetValues(pat!(Values { + temp_mosfet: approx_eq(26.9), + temp_motor: approx_eq(0.0), + avg_current_motor: approx_eq(0.92), + avg_current_input: approx_eq(0.12), + avg_current_d: approx_eq(0.0), + avg_current_q: approx_eq(-0.87), + duty_cycle: approx_eq(-0.237), + rpm: approx_eq(-2210.0), + voltage_in: approx_eq(37.3), + amp_hours: approx_eq(0.0007), + amp_hours_charged: approx_eq(0.0), + watt_hours: approx_eq(0.0274), + watt_hours_charged: approx_eq(0.0), + tachometer: eq(-28230), + tachometer_abs: eq(133952), + fault_code: eq(FaultCode::UnderVoltage), pid_pos: approx_eq(233.10513), controller_id: eq(20), temp_mosfet1: approx_eq(26.9), @@ -138,7 +179,7 @@ fn decode_get_values_selective_zero_rpm() { rpm: approx_eq(0.0), voltage_in: approx_eq(38.4), tachometer: eq(-25018), - fault_code: eq(0), + fault_code: eq(FaultCode::None), controller_id: eq(1), .. }))), @@ -160,7 +201,7 @@ fn decode_get_values_selective_forward_rpm() { rpm: approx_eq(989.0), voltage_in: approx_eq(37.5), tachometer: eq(-21973), - fault_code: eq(0), + fault_code: eq(FaultCode::None), controller_id: eq(20), .. }))), @@ -182,7 +223,29 @@ fn decode_get_values_selective_reverse_rpm() { rpm: approx_eq(-2347.0), voltage_in: approx_eq(37.4), tachometer: eq(-18982), - fault_code: eq(0), + fault_code: eq(FaultCode::None), + controller_id: eq(20), + .. + }))), + ); + assert_that!(vesc::decode(&input), ok(expected)); +} + +#[test] +fn decode_get_values_selective_fault_code() { + let input = [ + 2, 23, 50, 0, 2, 161, 138, 0, 0, 0, 0, 0, 10, 255, 255, 246, 213, 1, 118, 255, 255, 181, + 218, 4, 20, 146, 70, 3, + ]; + + let expected = ( + eq(&28), + pat!(&CommandReply::GetValuesSelective(pat!(Values { + avg_current_input: approx_eq(0.1), + rpm: approx_eq(-2347.0), + voltage_in: approx_eq(37.4), + tachometer: eq(-18982), + fault_code: eq(FaultCode::AbsOverCurrent), controller_id: eq(20), .. }))), diff --git a/tests/decoder.rs b/tests/decoder.rs index 185f80b..dab4cd4 100644 --- a/tests/decoder.rs +++ b/tests/decoder.rs @@ -1,5 +1,5 @@ use googletest::prelude::*; -use vesc::{CommandReply, Decoder, Values}; +use vesc::{CommandReply, Decoder, FaultCode, Values}; #[test] fn decoder_decodes_single_packet() { @@ -16,7 +16,7 @@ fn decoder_decodes_single_packet() { rpm: approx_eq(989.0), voltage_in: approx_eq(37.5), tachometer: eq(-21973), - fault_code: eq(0), + fault_code: eq(FaultCode::None), controller_id: eq(20), .. }))); @@ -53,7 +53,7 @@ fn decoder_decodes_packet_fed_in_chunks() { watt_hours_charged: approx_eq(0.0), tachometer: eq(-37045), tachometer_abs: eq(171975), - fault_code: eq(0), + fault_code: eq(FaultCode::None), pid_pos: approx_eq(74.08746), controller_id: eq(1), temp_mosfet1: approx_eq(27.7), @@ -99,7 +99,7 @@ fn decoder_returns_none_until_packet_is_complete() { watt_hours_charged: approx_eq(0.0), tachometer: eq(-37045), tachometer_abs: eq(171975), - fault_code: eq(0), + fault_code: eq(FaultCode::None), pid_pos: approx_eq(74.08746), controller_id: eq(1), temp_mosfet1: approx_eq(27.7), @@ -127,7 +127,7 @@ fn decoder_decodes_two_packets_from_single_feed() { rpm: approx_eq(0.0), voltage_in: approx_eq(38.4), tachometer: eq(-25018), - fault_code: eq(0), + fault_code: eq(FaultCode::None), controller_id: eq(1), .. }))); @@ -138,7 +138,7 @@ fn decoder_decodes_two_packets_from_single_feed() { rpm: approx_eq(989.0), voltage_in: approx_eq(37.5), tachometer: eq(-21973), - fault_code: eq(0), + fault_code: eq(FaultCode::None), controller_id: eq(20), .. }))); @@ -165,7 +165,7 @@ fn decoder_decodes_two_packets_from_separate_feeds() { rpm: approx_eq(0.0), voltage_in: approx_eq(38.4), tachometer: eq(-25018), - fault_code: eq(0), + fault_code: eq(FaultCode::None), controller_id: eq(1), .. }))); @@ -176,7 +176,7 @@ fn decoder_decodes_two_packets_from_separate_feeds() { rpm: approx_eq(989.0), voltage_in: approx_eq(37.5), tachometer: eq(-21973), - fault_code: eq(0), + fault_code: eq(FaultCode::None), controller_id: eq(20), .. }))); @@ -231,7 +231,7 @@ fn decoder_skips_junk_bytes_between_packets() { rpm: approx_eq(0.0), voltage_in: approx_eq(38.4), tachometer: eq(-25018), - fault_code: eq(0), + fault_code: eq(FaultCode::None), controller_id: eq(1), .. }))); @@ -242,7 +242,7 @@ fn decoder_skips_junk_bytes_between_packets() { rpm: approx_eq(989.0), voltage_in: approx_eq(37.5), tachometer: eq(-21973), - fault_code: eq(0), + fault_code: eq(FaultCode::None), controller_id: eq(20), .. }))); @@ -264,7 +264,7 @@ fn decoder_recovers_from_false_start_byte() { rpm: approx_eq(989.0), voltage_in: approx_eq(37.5), tachometer: eq(-21973), - fault_code: eq(0), + fault_code: eq(FaultCode::None), controller_id: eq(20), .. }))); @@ -321,7 +321,7 @@ fn decoder_iterator_collects_all_valid_packets() { watt_hours_charged: approx_eq(0.0), tachometer: eq(-37045), tachometer_abs: eq(171975), - fault_code: eq(0), + fault_code: eq(FaultCode::None), pid_pos: approx_eq(74.08746), controller_id: eq(1), temp_mosfet1: approx_eq(27.7), @@ -336,7 +336,7 @@ fn decoder_iterator_collects_all_valid_packets() { rpm: approx_eq(-2347.0), voltage_in: approx_eq(37.4), tachometer: eq(-18982), - fault_code: eq(0), + fault_code: eq(FaultCode::None), controller_id: eq(20), .. }))),