Skip to content

Commit 1a33fd3

Browse files
Merge branch 'develop'
2 parents 21d0a72 + ea5f53e commit 1a33fd3

File tree

9 files changed

+151
-3
lines changed

9 files changed

+151
-3
lines changed

.github/workflows/rust.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: Rust
22

3-
on: [push, pull_request]
3+
on: [pull_request]
44

55
env:
66
CARGO_TERM_COLOR: always

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rustypot"
3-
version = "1.2.0"
3+
version = "1.3.0"
44
edition = "2021"
55
license = "Apache-2.0"
66
authors = ["Pollen Robotics"]

Changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## Version 1.3.0
2+
3+
- Add reboot support in core library.
4+
- Add ping and reboot support in python bindings.
5+
16
## Version 1.2.0
27

38
- Add support for AX motors (see https://github.com/pollen-robotics/rustypot/pull/93, thanks to @kacper-uminski)

src/dynamixel_protocol/mod.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,17 @@ impl DynamixelProtocolHandler {
118118
}
119119
}
120120

121+
/// Send a reboot instruction.
122+
///
123+
/// Reboot the motor with specified `id`.
124+
/// Returns an [CommunicationErrorKind] if the communication fails.
125+
pub fn reboot(&self, serial_port: &mut dyn serialport::SerialPort, id: u8) -> Result<bool> {
126+
match &self.protocol {
127+
ProtocolKind::V1(p) => p.reboot(serial_port, id),
128+
ProtocolKind::V2(p) => p.reboot(serial_port, id),
129+
}
130+
}
131+
121132
/// Reads raw register bytes.
122133
///
123134
/// Sends a read instruction to the motor and wait for the status packet in response.
@@ -333,6 +344,12 @@ trait Protocol<P: Packet> {
333344
Ok(self.read_status_packet(port, id).is_ok())
334345
}
335346

347+
fn reboot(&self, port: &mut dyn SerialPort, id: u8) -> Result<bool> {
348+
self.send_instruction_packet(port, P::reboot_packet(id).as_ref())?;
349+
350+
Ok(self.read_status_packet(port, id).is_ok())
351+
}
352+
336353
fn read(&self, port: &mut dyn SerialPort, id: u8, addr: u8, length: u8) -> Result<Vec<u8>> {
337354
self.send_instruction_packet(port, P::read_packet(id, addr, length).as_ref())?;
338355
self.read_status_packet(port, id)

src/dynamixel_protocol/packet.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub trait Packet {
1010
fn get_payload_size(header: &[u8]) -> Result<usize>;
1111

1212
fn ping_packet(id: u8) -> Box<dyn InstructionPacket<Self>>;
13+
fn reboot_packet(id: u8) -> Box<dyn InstructionPacket<Self>>;
1314

1415
fn read_packet(id: u8, addr: u8, length: u8) -> Box<dyn InstructionPacket<Self>>;
1516
fn write_packet(id: u8, addr: u8, data: &[u8]) -> Box<dyn InstructionPacket<Self>>;

src/dynamixel_protocol/v1.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ impl Packet for PacketV1 {
2323
})
2424
}
2525

26+
fn reboot_packet(id: u8) -> Box<dyn InstructionPacket<Self>> {
27+
Box::new(InstructionPacketV1 {
28+
id,
29+
instruction: InstructionKindV1::Reboot,
30+
params: vec![],
31+
})
32+
}
33+
2634
fn read_packet(id: u8, addr: u8, length: u8) -> Box<dyn InstructionPacket<Self>> {
2735
Box::new(InstructionPacketV1 {
2836
id,
@@ -222,6 +230,7 @@ pub(crate) enum InstructionKindV1 {
222230
Ping,
223231
Read,
224232
Write,
233+
Reboot,
225234
SyncWrite,
226235
SyncRead,
227236
}
@@ -232,6 +241,7 @@ impl InstructionKindV1 {
232241
InstructionKindV1::Ping => 0x01,
233242
InstructionKindV1::Read => 0x02,
234243
InstructionKindV1::Write => 0x03,
244+
InstructionKindV1::Reboot => 0x08,
235245
InstructionKindV1::SyncRead => 0x82,
236246
InstructionKindV1::SyncWrite => 0x83,
237247
}
@@ -261,6 +271,13 @@ mod tests {
261271
assert_eq!(bytes, [0xFF, 0xFF, 0x01, 0x02, 0x01, 0xFB]);
262272
}
263273

274+
#[test]
275+
fn create_reboot_packet() {
276+
let p = PacketV1::reboot_packet(2);
277+
let bytes = p.to_bytes();
278+
assert_eq!(bytes, [0xFF, 0xFF, 0x02, 0x02, 0x08, 0xF3]);
279+
}
280+
264281
#[test]
265282
fn create_read_packet() {
266283
let p = PacketV1::read_packet(1, 0x2B, 1);

src/dynamixel_protocol/v2.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ impl Packet for PacketV2 {
3939
})
4040
}
4141

42+
fn reboot_packet(id: u8) -> Box<dyn InstructionPacket<Self>> {
43+
Box::new(InstructionPacketV2 {
44+
id,
45+
instruction: InstructionKindV2::Reboot,
46+
params: vec![],
47+
})
48+
}
49+
4250
fn read_packet(id: u8, addr: u8, length: u8) -> Box<dyn InstructionPacket<Self>> {
4351
Box::new(InstructionPacketV2 {
4452
id,
@@ -214,6 +222,7 @@ pub(crate) enum InstructionKindV2 {
214222
Ping,
215223
Read,
216224
Write,
225+
Reboot,
217226
SyncRead,
218227
SyncWrite,
219228
}
@@ -224,6 +233,7 @@ impl InstructionKindV2 {
224233
InstructionKindV2::Ping => 0x01,
225234
InstructionKindV2::Read => 0x02,
226235
InstructionKindV2::Write => 0x03,
236+
InstructionKindV2::Reboot => 0x08,
227237
InstructionKindV2::SyncRead => 0x82,
228238
InstructionKindV2::SyncWrite => 0x83,
229239
}
@@ -310,6 +320,7 @@ mod tests {
310320

311321
assert_eq!(crc.to_le_bytes(), [0x16, 0xd2]);
312322
}
323+
313324
#[test]
314325
fn create_ping_packet() {
315326
let p = PacketV2::ping_packet(2);
@@ -320,6 +331,16 @@ mod tests {
320331
);
321332
}
322333

334+
#[test]
335+
fn create_reboot_packet() {
336+
let p = PacketV2::reboot_packet(2);
337+
let bytes = p.to_bytes();
338+
assert_eq!(
339+
bytes,
340+
[0xff, 0xff, 0xfd, 0x0, 0x2, 0x3, 0x0, 0x8, 0x2f, 0x72]
341+
);
342+
}
343+
323344
#[test]
324345
fn create_read_packet() {
325346
let p = PacketV2::read_packet(1, 0x2B, 2);

src/servo/feetech/sts3215.rs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ generate_servo!(
2828
reg: (ccw_dead_zone, rw, 27, u8, None),
2929
reg: (protection_current, rw, 28, u16, None),
3030
reg: (angular_resolution, rw, 30, u8, None),
31-
reg: (offset, rw, 31, i16, AnglePosition),
31+
reg: (offset, rw, 31, u16, Offset),
3232
reg: (mode, rw, 33, u8, None),
3333
reg: (protective_torque, rw, 34, u8, None),
3434
reg: (protection_time, rw, 35, u8, None),
@@ -76,3 +76,58 @@ impl Conversion for Velocity {
7676
value as u16
7777
}
7878
}
79+
80+
pub struct Offset;
81+
const MAX_MAGNITUDE: u16 = 2047;
82+
83+
impl Conversion for Offset {
84+
type RegisterType = u16;
85+
type UsiType = f64;
86+
87+
fn from_raw(raw: u16) -> f64 {
88+
use std::f64::consts::PI;
89+
let negative = (raw >> 11) == 1;
90+
let magnitude = raw % (MAX_MAGNITUDE + 1);
91+
92+
let float_magnitude = PI * f64::from(magnitude) / f64::from(MAX_MAGNITUDE);
93+
94+
if negative {
95+
-float_magnitude
96+
} else {
97+
float_magnitude
98+
}
99+
}
100+
101+
fn to_raw(value: f64) -> u16 {
102+
use std::f64::consts::PI;
103+
104+
let magnitude = (value.abs() * f64::from(MAX_MAGNITUDE) / PI) as u16;
105+
106+
if value.is_sign_negative() {
107+
magnitude | (1 << 11)
108+
} else {
109+
magnitude
110+
}
111+
}
112+
}
113+
114+
#[cfg(test)]
115+
mod tests {
116+
#[test]
117+
fn offset_conversions() {
118+
use crate::servo::{conversion::Conversion, feetech::sts3215::Offset};
119+
use std::f64::consts::{FRAC_PI_2, PI};
120+
121+
assert_eq!(Offset::to_raw(0.0), 0);
122+
assert_eq!(Offset::to_raw(PI), 2047);
123+
assert_eq!(Offset::to_raw(-PI), 4095);
124+
assert_eq!(Offset::to_raw(FRAC_PI_2), 1023);
125+
assert_eq!(Offset::to_raw(-FRAC_PI_2), 3071);
126+
127+
assert_eq!(Offset::from_raw(0), 0.0);
128+
assert_eq!(Offset::from_raw(2047), PI);
129+
assert_eq!(Offset::from_raw(4095), -PI);
130+
assert_eq!(Offset::from_raw(1023), 1.5700289617109715); // About PI/2
131+
assert_eq!(Offset::from_raw(3071), -1.5700289617109715); // About -PI/2
132+
}
133+
}

src/servo/servo_macro.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ macro_rules! generate_servo {
4141
use pyo3_stub_gen::derive::*;
4242

4343
$crate::generate_protocol_constructor!($servo_name, $protocol);
44+
$crate::generate_ping_reboot!($servo_name);
4445
$crate::generate_addr_read_write!($servo_name);
4546

4647
$(
@@ -117,6 +118,27 @@ macro_rules! generate_protocol_constructor {
117118
};
118119
}
119120

121+
#[macro_export]
122+
macro_rules! generate_ping_reboot {
123+
($servo_macro:ident) => {
124+
paste::paste! {
125+
impl [<$servo_macro:camel Controller>] {
126+
pub fn ping(&mut self, id: u8) -> $crate::Result<bool> {
127+
let dph = self.dph.as_ref().unwrap();
128+
let serial_port = self.serial_port.as_mut().unwrap().as_mut();
129+
dph.ping(serial_port, id)
130+
}
131+
132+
pub fn reboot(&mut self, id: u8) -> $crate::Result<bool> {
133+
let dph = self.dph.as_ref().unwrap();
134+
let serial_port = self.serial_port.as_mut().unwrap().as_mut();
135+
dph.reboot(serial_port, id)
136+
}
137+
}
138+
}
139+
};
140+
}
141+
120142
#[macro_export]
121143
macro_rules! generate_addr_read_write {
122144
($servo_name:ident) => {
@@ -230,6 +252,16 @@ macro_rules! generate_addr_read_write {
230252
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
231253
Ok(())
232254
}
255+
256+
pub fn ping(&self, id: u8) -> PyResult<bool> {
257+
self.0.lock().unwrap().ping(id)
258+
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
259+
}
260+
261+
pub fn reboot(&self, id: u8) -> PyResult<bool> {
262+
self.0.lock().unwrap().reboot(id)
263+
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
264+
}
233265
}
234266
}
235267
};

0 commit comments

Comments
 (0)