1use deku::prelude::*;
2use embedded_can::{Id, StandardId};
3
4use crate::sdo::ConfigValue;
5
6#[derive(Debug, Clone, DekuRead, DekuWrite, PartialEq)]
7#[deku(id_type = "u8", bits = 5)]
8pub enum CanMessage {
9 #[deku(id = "0x00")]
10 GetVersion {
11 protocol_version: u8,
12 hw_version_major: u8,
13 hw_version_minor: u8,
14 hw_version_variant: u8,
15 fw_version_major: u8,
16 fw_version_minor: u8,
17 fw_version_revision: u8,
18 fw_version_unreleased: u8,
19 },
20 #[deku(id = "0x01")]
21 Heartbeat {
22 #[deku(endian = "little")]
23 axis_error: u32,
24 axis_state: AxisState,
25 procedure_result: u8,
26 trajectory_done_flag: u8,
27 _reserved: u8,
28 },
29 #[deku(id = "0x02")]
30 Estop,
31 #[deku(id = "0x03")]
32 GetError {
33 #[deku(endian = "little")]
34 active_errors: u32,
35 #[deku(endian = "little")]
36 disarm_reason: u32,
37 },
38 #[deku(id = "0x04")]
39 RxSdo {
40 opcode: SdoOpcode,
41 #[deku(endian = "little")]
42 endpoint_id: u16,
43 reserved: u8,
44 #[deku(read_all)]
45 value: Vec<u8>,
46 },
47 #[deku(id = "0x05")]
48 TxSdo {
49 reserved0: u8,
50 #[deku(endian = "little")]
51 endpoint_id: u16,
52 reserved1: u8,
53 #[deku(read_all)]
54 value: Vec<u8>,
55 },
56 #[deku(id = "0x06")]
57 Address {
58 node_id: u8,
59 #[deku(bytes = "6")]
60 serial_number: [u8; 6],
61 connection_id: u8,
62 },
63 #[deku(id = "0x07")]
64 SetAxisState {
65 #[deku(pad_bytes_after = "3")]
66 axis_requested_state: AxisState,
68 },
69 #[deku(id = "0x09")]
70 GetEncoderEstimates {
71 #[deku(endian = "little")]
73 pos_estimate: f32,
74 #[deku(endian = "little")]
76 vel_estimate: f32,
77 },
78 #[deku(id = "0x0B")]
79 SetControllerMode {
80 #[deku(pad_bytes_after = "3")]
81 control_mode: ControlMode,
82 #[deku(pad_bytes_after = "3")]
83 input_mode: InputMode,
84 },
85 #[deku(id = "0x0C")]
86 SetInputPos {
87 #[deku(endian = "little")]
89 input_pos: f32,
90 #[deku(endian = "little")]
92 vel_ff: i16,
93 #[deku(endian = "little")]
95 torque_ff: i16,
96 },
97 #[deku(id = "0x0D")]
98 SetInputVel {
99 #[deku(endian = "little")]
101 input_vel: f32,
102 #[deku(endian = "little")]
104 input_torque_ff: f32,
105 },
106 #[deku(id = "0x0E")]
107 SetInputTorque {
108 #[deku(endian = "little")]
110 input_torque: f32,
111 },
112 #[deku(id = "0x0F")]
113 SetLimits {
114 #[deku(endian = "little")]
116 velocity_limit: f32,
117 #[deku(endian = "little")]
119 current_limit: f32,
120 },
121 #[deku(id = "0x11")]
122 SetTrajVelLimit {
123 #[deku(endian = "little")]
125 traj_vel_limit: f32,
126 },
127 #[deku(id = "0x12")]
128 SetTrajAccelLimits {
129 #[deku(endian = "little")]
131 traj_accel_limit: f32,
132 #[deku(endian = "little")]
134 traj_decel_limit: f32,
135 },
136 #[deku(id = "0x13")]
137 SetTrajInertia {
138 #[deku(endian = "little")]
140 traj_inertia: f32,
141 },
142 #[deku(id = "0x14")]
143 GetIq {
144 #[deku(endian = "little")]
146 iq_setpoint: f32,
147 #[deku(endian = "little")]
149 iq_measured: f32,
150 },
151 #[deku(id = "0x15")]
152 GetTemperature {
153 #[deku(endian = "little")]
155 fet_temperature: f32,
156 #[deku(endian = "little")]
158 motor_temperature: f32,
159 },
160 #[deku(id = "0x16")]
161 Reboot { action: RebootAction },
162 #[deku(id = "0x17")]
163 GetBusVoltageCurrent {
164 #[deku(endian = "little")]
166 bus_voltage: f32,
167 #[deku(endian = "little")]
169 bus_current: f32,
170 },
171 #[deku(id = "0x18")]
172 ClearErrors {
173 #[deku(bits = 8)]
174 identify: bool,
175 },
176 #[deku(id = "0x19")]
177 SetAbsolutePosition {
178 #[deku(endian = "little")]
180 position: f32,
181 },
182 #[deku(id = "0x1A")]
183 SetPosGain {
184 #[deku(endian = "little")]
186 pos_gain: f32,
187 },
188 #[deku(id = "0x1B")]
189 SetVelGains {
190 #[deku(endian = "little")]
192 vel_gain: f32,
193 #[deku(endian = "little")]
195 vel_integrator_gain: f32,
196 },
197 #[deku(id = "0x1C")]
198 GetTorques {
199 #[deku(endian = "little")]
201 torque_target: f32,
202 #[deku(endian = "little")]
204 torque_estimate: f32,
205 },
206 #[deku(id = "0x1D")]
207 GetPowers {
208 #[deku(endian = "little")]
210 electrical_power: f32,
211 #[deku(endian = "little")]
213 mechanical_power: f32,
214 },
215 #[deku(id = "0x1F")]
216 EnterDfuMode,
217}
218
219#[derive(Debug, Clone, DekuRead, DekuWrite, PartialEq)]
220pub struct CanMessageWithId {
221 #[deku(bits = 6, pad_bits_before = "5")]
222 pub node_id: u8,
224 pub message: CanMessage,
225}
226
227impl CanMessageWithId {
228 pub fn id(&self) -> StandardId {
229 let bytes: Vec<u8> = self.to_bytes().unwrap();
230 Self::id_from_bytes(&bytes)
231 }
232
233 pub fn data(&self) -> Vec<u8> {
234 let bytes: Vec<u8> = self.to_bytes().unwrap();
235 Self::data_from_bytes(&bytes).to_vec()
236 }
237
238 fn id_from_bytes(bytes: &[u8]) -> StandardId {
239 StandardId::new(u16::from_be_bytes(bytes[..2].try_into().unwrap())).unwrap()
240 }
241
242 fn data_from_bytes(bytes: &[u8]) -> &[u8] {
243 &bytes[2..]
244 }
245
246 pub fn from_frame<F: embedded_can::Frame>(frame: &F) -> Result<Self, DekuError> {
247 let id = match frame.id() {
248 Id::Standard(std_id) => std_id.as_raw(),
249 _ => unreachable!(), };
251 let data = frame.data();
252
253 Self::from_id_and_data(id, data)
254 }
255
256 pub fn from_id_and_data(id: u16, data: &[u8]) -> Result<Self, DekuError> {
257 let full_data = [&id.to_be_bytes(), data].concat();
259
260 let (_, message_with_id) = CanMessageWithId::from_bytes((&full_data, 0))?;
262
263 Ok(message_with_id)
264 }
265
266 pub fn to_frame<F: embedded_can::Frame>(self) -> F {
267 let bytes = self.to_bytes().unwrap();
268 F::new(Self::id_from_bytes(&bytes), Self::data_from_bytes(&bytes)).unwrap()
269 }
270
271 pub fn to_rtr_frame<F: embedded_can::Frame>(self) -> F {
272 F::new_remote(self.id(), 0).unwrap()
273 }
274
275 pub fn get_version(node_id: u8) -> Self {
276 CanMessageWithId {
277 node_id,
278 message: CanMessage::GetVersion {
279 protocol_version: 0,
280 hw_version_major: 0,
281 hw_version_minor: 0,
282 hw_version_variant: 0,
283 fw_version_major: 0,
284 fw_version_minor: 0,
285 fw_version_revision: 0,
286 fw_version_unreleased: 0,
287 },
288 }
289 }
290
291 pub fn heartbeat(node_id: u8) -> Self {
292 CanMessageWithId {
293 node_id,
294 message: CanMessage::Heartbeat {
295 axis_error: 0,
296 axis_state: AxisState::Undefined,
297 procedure_result: 0,
298 trajectory_done_flag: 0,
299 _reserved: 0,
300 },
301 }
302 }
303
304 pub fn estop(node_id: u8) -> Self {
305 CanMessageWithId {
306 node_id,
307 message: CanMessage::Estop,
308 }
309 }
310
311 pub fn get_error(node_id: u8) -> Self {
312 CanMessageWithId {
313 node_id,
314 message: CanMessage::GetError {
315 active_errors: 0,
316 disarm_reason: 0,
317 },
318 }
319 }
320
321 pub fn rx_sdo(node_id: u8, opcode: SdoOpcode, endpoint_id: u16, value: ConfigValue) -> Self {
322 CanMessageWithId {
323 node_id,
324 message: CanMessage::RxSdo {
325 opcode,
326 endpoint_id,
327 reserved: 0,
328 value: value.to_le_byte_vec(),
329 },
330 }
331 }
332
333 pub fn set_address(
334 node_id: u8,
335 set_node_id: u8,
336 serial_number: [u8; 6],
337 connection_id: u8,
338 ) -> Self {
339 CanMessageWithId {
340 node_id,
341 message: CanMessage::Address {
342 node_id: set_node_id,
343 serial_number,
344 connection_id,
345 },
346 }
347 }
348
349 pub fn get_address(node_id: u8) -> Self {
350 CanMessageWithId {
352 node_id,
353 message: CanMessage::Address {
354 node_id: 0,
355 serial_number: [0; 6],
356 connection_id: 0,
357 },
358 }
359 }
360
361 pub fn get_all_addresses() -> Self {
362 CanMessageWithId {
364 node_id: 0x3f,
365 message: CanMessage::Address {
366 node_id: 0,
367 serial_number: [0; 6],
368 connection_id: 0,
369 },
370 }
371 }
372
373 pub fn set_axis_state(node_id: u8, state: AxisState) -> Self {
374 CanMessageWithId {
375 node_id,
376 message: CanMessage::SetAxisState {
377 axis_requested_state: state,
378 },
379 }
380 }
381
382 pub fn get_encoder_estimates(node_id: u8) -> Self {
383 CanMessageWithId {
384 node_id,
385 message: CanMessage::GetEncoderEstimates {
386 pos_estimate: 0.0,
387 vel_estimate: 0.0,
388 },
389 }
390 }
391
392 pub fn set_controller_mode(
393 node_id: u8,
394 control_mode: ControlMode,
395 input_mode: InputMode,
396 ) -> Self {
397 CanMessageWithId {
398 node_id,
399 message: CanMessage::SetControllerMode {
400 control_mode,
401 input_mode,
402 },
403 }
404 }
405
406 pub fn set_input_pos(node_id: u8, input_pos: f32, vel_ff: i16, torque_ff: i16) -> Self {
407 CanMessageWithId {
408 node_id,
409 message: CanMessage::SetInputPos {
410 input_pos,
411 vel_ff,
412 torque_ff,
413 },
414 }
415 }
416
417 pub fn set_input_vel(node_id: u8, input_vel: f32, input_torque_ff: f32) -> Self {
418 CanMessageWithId {
419 node_id,
420 message: CanMessage::SetInputVel {
421 input_vel,
422 input_torque_ff,
423 },
424 }
425 }
426
427 pub fn set_input_torque(node_id: u8, input_torque: f32) -> Self {
428 CanMessageWithId {
429 node_id,
430 message: CanMessage::SetInputTorque { input_torque },
431 }
432 }
433
434 pub fn set_limits(node_id: u8, velocity_limit: f32, current_limit: f32) -> Self {
435 CanMessageWithId {
436 node_id,
437 message: CanMessage::SetLimits {
438 velocity_limit,
439 current_limit,
440 },
441 }
442 }
443
444 pub fn set_traj_vel_limit(node_id: u8, traj_vel_limit: f32) -> Self {
445 CanMessageWithId {
446 node_id,
447 message: CanMessage::SetTrajVelLimit { traj_vel_limit },
448 }
449 }
450
451 pub fn set_traj_accel_limits(
452 node_id: u8,
453 traj_accel_limit: f32,
454 traj_decel_limit: f32,
455 ) -> Self {
456 CanMessageWithId {
457 node_id,
458 message: CanMessage::SetTrajAccelLimits {
459 traj_accel_limit,
460 traj_decel_limit,
461 },
462 }
463 }
464
465 pub fn set_traj_inertia(node_id: u8, traj_inertia: f32) -> Self {
466 CanMessageWithId {
467 node_id,
468 message: CanMessage::SetTrajInertia { traj_inertia },
469 }
470 }
471
472 pub fn get_iq(node_id: u8) -> Self {
473 CanMessageWithId {
474 node_id,
475 message: CanMessage::GetIq {
476 iq_setpoint: 0.0,
477 iq_measured: 0.0,
478 },
479 }
480 }
481
482 pub fn get_temperature(node_id: u8) -> Self {
483 CanMessageWithId {
484 node_id,
485 message: CanMessage::GetTemperature {
486 fet_temperature: 0.0,
487 motor_temperature: 0.0,
488 },
489 }
490 }
491
492 pub fn reboot(node_id: u8, action: RebootAction) -> Self {
493 CanMessageWithId {
494 node_id,
495 message: CanMessage::Reboot { action },
496 }
497 }
498
499 pub fn get_bus_voltage_current(node_id: u8) -> Self {
500 CanMessageWithId {
501 node_id,
502 message: CanMessage::GetBusVoltageCurrent {
503 bus_voltage: 0.0,
504 bus_current: 0.0,
505 },
506 }
507 }
508
509 pub fn clear_errors(node_id: u8, identify: bool) -> Self {
510 CanMessageWithId {
511 node_id,
512 message: CanMessage::ClearErrors { identify },
513 }
514 }
515
516 pub fn set_absolute_position(node_id: u8, position: f32) -> Self {
517 CanMessageWithId {
518 node_id,
519 message: CanMessage::SetAbsolutePosition { position },
520 }
521 }
522
523 pub fn set_pos_gain(node_id: u8, pos_gain: f32) -> Self {
524 CanMessageWithId {
525 node_id,
526 message: CanMessage::SetPosGain { pos_gain },
527 }
528 }
529
530 pub fn set_vel_gains(node_id: u8, vel_gain: f32, vel_integrator_gain: f32) -> Self {
531 CanMessageWithId {
532 node_id,
533 message: CanMessage::SetVelGains {
534 vel_gain,
535 vel_integrator_gain,
536 },
537 }
538 }
539
540 pub fn get_torques(node_id: u8) -> Self {
541 CanMessageWithId {
542 node_id,
543 message: CanMessage::GetTorques {
544 torque_target: 0.0,
545 torque_estimate: 0.0,
546 },
547 }
548 }
549
550 pub fn get_powers(node_id: u8) -> Self {
551 CanMessageWithId {
552 node_id,
553 message: CanMessage::GetPowers {
554 electrical_power: 0.0,
555 mechanical_power: 0.0,
556 },
557 }
558 }
559
560 pub fn enter_dfu_mode(node_id: u8) -> Self {
561 CanMessageWithId {
562 node_id,
563 message: CanMessage::EnterDfuMode,
564 }
565 }
566}
567
568#[repr(u8)]
569#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuWrite, DekuRead)]
570#[deku(id_type = "u8")]
571pub enum AxisState {
572 Undefined = 0,
573 Idle = 1,
574 StartupSequence = 2,
575 FullCalibration = 3,
576 MotorCalibration = 4,
577 EncoderIndexSearch = 6,
578 EncoderOffsetCalibration = 7,
579 ClosedLoopControl = 8,
580 LockinSpin = 9,
581 EncoderDirFind = 10,
582 Homing = 11,
583 EncoderHallPolarityCalibration = 12,
584 EncoderHallPhaseCalibration = 13,
585 AnticoggingCalibration = 14,
586 HarmonicCalibration = 15,
587 HarmonicCalibrationCommutation = 16,
588}
589
590impl std::fmt::Display for AxisState {
591 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
592 match self {
593 AxisState::Undefined => write!(f, "UNDEFINED"),
594 AxisState::Idle => write!(f, "IDLE"),
595 AxisState::StartupSequence => write!(f, "STARTUP_SEQUENCE"),
596 AxisState::FullCalibration => write!(f, "FULL_CALIBRATION"),
597 AxisState::MotorCalibration => write!(f, "MOTOR_CALIBRATION"),
598 AxisState::EncoderIndexSearch => write!(f, "ENCODER_INDEX_SEARCH"),
599 AxisState::EncoderOffsetCalibration => write!(f, "ENCODER_OFFSET_CALIBRATION"),
600 AxisState::ClosedLoopControl => write!(f, "CLOSED_LOOP_CONTROL"),
601 AxisState::LockinSpin => write!(f, "LOCKIN_SPIN"),
602 AxisState::EncoderDirFind => write!(f, "ENCODER_DIR_FIND"),
603 AxisState::Homing => write!(f, "HOMING"),
604 AxisState::EncoderHallPolarityCalibration => {
605 write!(f, "ENCODER_HALL_POLARITY_CALIBRATION")
606 }
607 AxisState::EncoderHallPhaseCalibration => write!(f, "ENCODER_HALL_PHASE_CALIBRATION"),
608 AxisState::AnticoggingCalibration => write!(f, "ANTICOGGING_CALIBRATION"),
609 AxisState::HarmonicCalibration => write!(f, "HARMONIC_CALIBRATION"),
610 AxisState::HarmonicCalibrationCommutation => {
611 write!(f, "HARMONIC_CALIBRATION_COMMUTATION")
612 }
613 }
614 }
615}
616
617#[repr(u8)]
618#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuWrite, DekuRead)]
619#[deku(id_type = "u8")]
620pub enum RebootAction {
621 Reboot = 0,
622 SaveConfiguration = 1,
623 EraseConfiguration = 2,
624 EnterDfuMode2 = 3,
625}
626
627#[repr(u8)]
628#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuWrite, DekuRead)]
629#[deku(id_type = "u8")]
630pub enum ControlMode {
631 VoltageControl = 0,
632 TorqueControl = 1,
633 VelocityControl = 2,
634 PositionControl = 3,
635}
636
637#[repr(u8)]
638#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuWrite, DekuRead)]
639#[deku(id_type = "u8")]
640pub enum InputMode {
641 Inactive = 0,
642 Passthrough = 1,
643 VelRamp = 2,
644 PosFilter = 3,
645 MixChannels = 4,
646 TrapTraj = 5,
647 TorqueRamp = 6,
648 Mirror = 7,
649 Tuning = 8,
650}
651
652#[repr(u8)]
653#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuWrite, DekuRead)]
654#[deku(id_type = "u8", endian = "little")]
655pub enum SdoOpcode {
656 Read = 0,
657 Write = 1,
658}
659
660#[cfg(test)]
661mod tests {
662 use super::*;
663
664 #[test]
665 pub fn test_set_closed_loop_control() {
666 let msg = CanMessageWithId::set_axis_state(1, AxisState::ClosedLoopControl);
667 let id = msg.id().as_raw();
668 let data = msg.data();
669 assert_eq!(id, 1 << 5 | 0x07);
670 assert_eq!(data, vec![0x08, 0x00, 0x00, 0x00]);
671 assert_eq!(msg, CanMessageWithId::from_id_and_data(id, &data).unwrap());
672 }
673
674 #[test]
675 pub fn test_docs_example_read_vel_integrator_limit() {
676 let msg = CanMessageWithId::rx_sdo(0, SdoOpcode::Read, 0x0182, ConfigValue::Empty);
677 let id = msg.id().as_raw();
678 let data = msg.data();
679 assert_eq!(id, 0x04);
680 assert_eq!(data, vec![0x00, 0x82, 0x01, 0x00]); assert_eq!(msg, CanMessageWithId::from_id_and_data(id, &data).unwrap());
682 }
683
684 #[test]
685 pub fn test_docs_example_read_vel_integrator_limit_response() {
686 let resp_id = 0x05;
687 let resp_data = vec![0x00, 0x82, 0x01, 0x00, 0x00, 0x00, 0x80, 0x7f];
689 let resp_msg = CanMessageWithId::from_id_and_data(resp_id, &resp_data).unwrap();
690 match resp_msg.message {
691 CanMessage::TxSdo {
692 endpoint_id, value, ..
693 } => {
694 assert_eq!(endpoint_id, 0x0182);
695 let config_value = ConfigValue::from_le_bytes(&value, "float").unwrap();
696 match config_value {
697 ConfigValue::Float(f) => {
698 assert!(f.is_infinite() && f.is_sign_positive());
699 }
700 _ => panic!("Expected float ConfigValue"),
701 }
702 }
703 _ => panic!("Expected TxSdo message"),
704 }
705 }
706
707 #[test]
708 pub fn test_docs_example_function_call() {
709 let msg = CanMessageWithId::rx_sdo(0, SdoOpcode::Write, 0x0253, ConfigValue::List(vec![]));
710 let id = msg.id().as_raw();
711 let data = msg.data();
712 assert_eq!(id, 0x04);
713 assert_eq!(data, vec![0x01, 0x53, 0x02, 0x00]);
714 assert_eq!(msg, CanMessageWithId::from_id_and_data(id, &data).unwrap());
715 }
716
717 #[test]
718 pub fn test_docs_example_write_vel_integrator_limit() {
719 let msg = CanMessageWithId::rx_sdo(0, SdoOpcode::Write, 0x0182, ConfigValue::Float(1.234));
720 let id = msg.id().as_raw();
721 let data = msg.data();
722 assert_eq!(id, 0x04);
723 assert_eq!(data, vec![0x01, 0x82, 0x01, 0x00, 0xb6, 0xf3, 0x9d, 0x3f]); assert_eq!(msg, CanMessageWithId::from_id_and_data(id, &data).unwrap());
725 }
726
727 #[test]
728 pub fn test_set_control_mode() {
729 let msg = CanMessageWithId::set_controller_mode(
730 2,
731 ControlMode::TorqueControl,
732 InputMode::Passthrough,
733 );
734 let id = msg.id().as_raw();
735 let data = msg.data();
736 assert_eq!(id, 2 << 5 | 0x0B);
737 assert_eq!(data, vec![0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00])
738 }
739}