Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 85 additions & 3 deletions src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<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,
}
}
}

/// Telemetry data returned by the motor controller.
///
/// Contains temperatures, currents, voltages, rpm, and so on. Returned by
Expand All @@ -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,
Expand Down Expand Up @@ -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)?,
Expand Down Expand Up @@ -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)?;
Expand Down
11 changes: 10 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
77 changes: 70 additions & 7 deletions tests/command_reply.rs
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
..
}))),
Expand All @@ -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),
..
}))),
Expand All @@ -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),
..
}))),
Expand Down
26 changes: 13 additions & 13 deletions tests/decoder.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use googletest::prelude::*;
use vesc::{CommandReply, Decoder, Values};
use vesc::{CommandReply, Decoder, FaultCode, Values};

#[test]
fn decoder_decodes_single_packet() {
Expand All @@ -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),
..
})));
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
..
})));
Expand All @@ -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),
..
})));
Expand All @@ -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),
..
})));
Expand All @@ -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),
..
})));
Expand Down Expand Up @@ -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),
..
})));
Expand All @@ -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),
..
})));
Expand All @@ -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),
..
})));
Expand Down Expand Up @@ -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),
Expand All @@ -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),
..
}))),
Expand Down