Skip to content

Commit 76d1a35

Browse files
Merge pull request #85 from pollen-robotics/scs0009
Scs0009
2 parents d82207f + db7f901 commit 76d1a35

File tree

11 files changed

+648
-243
lines changed

11 files changed

+648
-243
lines changed

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.0.0"
3+
version = "1.1.0"
44
edition = "2021"
55
license = "Apache-2.0"
66
authors = ["Pollen Robotics"]

README.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,21 @@ fn main() {
6565
.with_protocol_v1()
6666
.with_serial_port(serial_port);
6767

68-
let pos = c.read_present_position(&vec![1, 2]).unwrap();
68+
let pos = c.sync_read_present_position(&vec![1, 2]).unwrap();
6969
println!("Motors present position: {:?}", pos);
7070

71-
c.write_goal_position(&vec![1, 2], &vec![1000, 2000]).unwrap();
71+
c.sync_write_goal_position(&vec![1, 2], &vec![1000, 2000]).unwrap();
7272
}
7373
```
7474

75+
## Tools
76+
77+
Simple bus scanning tool:
78+
79+
```bash
80+
cargo run --bin=scan -- --serialport=/dev/ttyUSB0 --baudrate=1000000 --protocol=v1
81+
```
82+
7583
## Documentation
7684

7785
See https://docs.rs/rustypot for more information on APIs and examples.
@@ -88,7 +96,7 @@ To build them locally, you can use [maturin](https://www.maturin.rs).
8896
maturin build --release --features python
8997
```
9098

91-
or, if you want to install them in your local python environment:
99+
or, if you want to install them in your local python environment:
92100

93101
```bash
94102
maturin develop --release --features python
@@ -108,4 +116,4 @@ This library is licensed under the [Apache License 2.0](./LICENSE).
108116
## Support
109117

110118
Rustypot is developed and maintained by [Pollen-Robotics](https://pollen-robotics.com). They developed open-source hardware and tools for robotics.
111-
Visit https://pollen-robotics.com to learn more or join the [Discord community](https://discord.com/invite/Kg3mZHTKgs) if you have any questions or want to share your projects.
119+
Visit https://pollen-robotics.com to learn more or join the [Discord community](https://discord.com/invite/Kg3mZHTKgs) if you have any questions or want to share your projects.

examples/feetech_controller.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ fn main() -> Result<(), Box<dyn Error>> {
2121

2222
while start_overall.elapsed() < duration {
2323
let start_time = std::time::Instant::now();
24-
let x = c.read_present_position(&ids);
24+
let x = c.sync_read_present_position(&ids);
2525
let elapsed_time = start_time.elapsed();
2626

2727
println!("present pos: {:?}", x);

python/README.md

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,44 @@ maturin develop --release --features python
1818

1919
## Usage
2020

21-
The Python bindings exposes the same API as the Controller API in the rust crate.
21+
The Python bindings exposes the same API as the Controller API in the rust crate.
2222

2323
You first need to create a Controller object. For instance, to communicate with a serial port to Feetech STS3215 motors, you can do the following:
2424

2525
```python
26-
from rustypot.servo import Sts3215SyncController
26+
from rustypot.servo import Sts3215PyController
2727

28-
c = Sts3215SyncController(serial_port='/dev/ttyUSB0', baudrate=100000, timeout=0.1)
28+
c = Sts3215PyController(serial_port='/dev/ttyUSB0', baudrate=100000, timeout=0.1)
2929
```
3030

31-
Then, you can directly read/write any register of the motor. For instance, to read the present position of the motors with id 1 and 2, you can do:
31+
32+
Then, you can directly read/write any register of the motor. For instance, to read the present position of the motor with id 1, you can do:
33+
34+
```python
35+
36+
pos = c.read_present_position(1)
37+
print(pos)
38+
```
39+
40+
You can also write to the motors. For instance, to set the goal position of the motors with id 1 to 90° you can do:
3241

3342
```python
43+
import numpy as np
44+
c.write_goal_position(1, np.deg2rad(90.0))
45+
```
46+
47+
48+
Then, you can also sync_read any registers on multiple motors in a single operations. For instance, to read the present position of the motors with id 1 and 2, you can do:
3449

35-
pos = c.read_present_position([1, 2])
50+
```python
51+
52+
pos = c.sync_read_present_position([1, 2])
3653
print(pos)
3754
```
3855

39-
You can also write to the motors. For instance, to set the goal position of the motors with id 1 and 2 to 0.0 and 90° respectively, you can do:
56+
Same with sync_write. For instance, to set the goal position of the motors with id 1 and 2 to 0.0 and 90° respectively, you can do:
4057

4158
```python
4259
import numpy as np
43-
c.write_goal_position([1, 2], [0.0, np.deg2rad(90.0)])
44-
```
60+
c.sync_write_goal_position([1, 2], [0.0, np.deg2rad(90.0)])
61+
```

python/examples/feetech_sinus.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import time
22
import numpy as np
33

4-
from rustypot.servo import Sts3215SyncController
4+
from rustypot.servo import Sts3215PyController
55

66
def main():
77
c = Sts3215SyncController(
8-
serial_port='/dev/tty.usbmodem58FA0822621',
9-
baudrate=1000000,
8+
serial_port='/dev/tty.usbmodem58FA0822621',
9+
baudrate=1000000,
1010
timeout=0.1,
1111
)
1212

@@ -24,4 +24,4 @@ def main():
2424
time.sleep(0.01)
2525

2626
if __name__ == '__main__':
27-
main()
27+
main()

src/bin/scan.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ fn main() -> Result<(), Box<dyn Error>> {
4747
let model = dph.read(serial_port.as_mut(), id, 0, 2).unwrap();
4848
let model = u16::from_le_bytes([model[0], model[1]]);
4949
let model = ServoKind::try_from(model);
50-
51-
println!("Found motor with id {id} and model {model:?}");
50+
match model {
51+
Ok(m) => println!("Found motor with id {id} and model: {m:?}"),
52+
Err(e) => println!("Found motor with id {id} with {e:?}"),
53+
}
5254
}
5355
}
5456
Err(e) => eprintln!("Error: {e}"),

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@
5050
//! .with_protocol_v1()
5151
//! .with_serial_port(serial_port);
5252
//!
53-
//! let pos = c.read_present_position(&vec![1, 2]).unwrap();
53+
//! let pos = c.sync_read_present_position(&vec![1, 2]).unwrap();
5454
//! println!("Motors present position: {:?}", pos);
5555
//!
56-
//! c.write_goal_position(&vec![1, 2], &vec![0.0, 90.0_f64.to_radians()]).unwrap();
56+
//! c.sync_write_goal_position(&vec![1, 2], &vec![0.0, 90.0_f64.to_radians()]).unwrap();
5757
//! ```
5858
5959
pub mod servo;

src/servo/feetech/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
pub mod scs0009;
12
pub mod sts3215;

src/servo/feetech/scs0009.rs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
use crate::generate_servo;
2+
use crate::servo::conversion::Conversion;
3+
4+
generate_servo!(
5+
SCS0009, v1,
6+
reg: (model, r, 3, u16, None),
7+
reg: (id, rw, 5, u8, None),
8+
reg: (baudrate, rw, 6, u8, None),
9+
reg: (return_delay_time, rw, 7, u8, None), //RESERVED?
10+
reg: (response_status_level, rw, 8, u8, None),
11+
reg: (min_angle_limit, rw, 9, i16, AnglePosition),
12+
reg: (max_angle_limit, rw, 11, i16, AnglePosition),
13+
reg: (max_temperature_limit, rw, 13, u8, None),
14+
reg: (max_voltage_limit, rw, 14, u8, None),
15+
reg: (min_voltage_limit, rw, 15, u8, None),
16+
reg: (max_torque_limit, rw, 16, u16, TorqueLimit),
17+
reg: (phase, rw, 18, u8, None), //SPECIAL REG
18+
reg: (unloading_condition, rw, 19, u8, None),
19+
reg: (led_alarm_condition, rw, 20, u8, None),
20+
reg: (p_coefficient, rw, 21, u8, None),
21+
reg: (d_coefficient, rw, 22, u8, None),
22+
reg: (i_coefficient, rw, 23, u8, None),
23+
reg: (minimum_startup_force, rw, 24, u16, BigEndian_u16),
24+
reg: (cw_dead_zone, rw, 26, u8, None),
25+
reg: (ccw_dead_zone, rw, 27, u8, None),
26+
reg: (hysteresis_loop, rw, 27, u8, None),
27+
28+
reg: (protective_torque, rw, 37, u8, None),
29+
reg: (protection_time, rw, 38, u8, None),
30+
reg: (overload_torque, rw, 39, u8, None),
31+
32+
reg: (torque_enable, rw, 40, u8, None),
33+
34+
reg: (goal_position, rw, 42, i16, AnglePosition),
35+
reg: (goal_time, rw, 44, u16, BigEndian_u16),
36+
reg: (goal_speed, rw, 46, u16, Velocity),
37+
38+
reg: (lock, rw, 48, u8, bool),
39+
reg: (present_position, r, 56, i16, AnglePosition),
40+
reg: (present_speed, r, 58, u16, Velocity),
41+
reg: (present_load, r, 60, u16, BigEndian_i16),
42+
43+
reg: (present_voltage, r, 62, u8, None),
44+
reg: (present_temperature, r, 63, u8, None),
45+
46+
reg: (status, r, 65, u8, None),
47+
48+
reg: (moving, r, 66, u8, bool),
49+
50+
);
51+
52+
pub struct Velocity;
53+
54+
impl Conversion for Velocity {
55+
type RegisterType = u16;
56+
type UsiType = f64;
57+
58+
fn from_raw(raw: u16) -> f64 {
59+
// println!("DEBUG SPEED: {:?}", raw.to_be());
60+
if raw.to_be() > (1 << 15) {
61+
-300.0_f64.to_radians() / 1024.0 * (raw.to_be() & 0x3ff) as f64
62+
} else {
63+
300.0_f64.to_radians() / 1024.0 * (raw.to_be() & 0x3ff) as f64
64+
}
65+
}
66+
67+
fn to_raw(value: f64) -> u16 {
68+
((value / (300.0_f64.to_radians() / 1024.0)) as u16).to_be()
69+
}
70+
}
71+
72+
pub struct AnglePosition;
73+
74+
impl Conversion for AnglePosition {
75+
type RegisterType = i16;
76+
type UsiType = f64;
77+
78+
fn from_raw(raw: i16) -> f64 {
79+
300.0_f64.to_radians() * (((raw.to_be() & 0x3ff) - 511) as f64) / 1024.0
80+
}
81+
82+
fn to_raw(value: f64) -> i16 {
83+
let a = (1024.0 * (value) / (300.0_f64.to_radians()) + 511.0) as i16;
84+
a.to_be()
85+
}
86+
}
87+
88+
#[allow(non_camel_case_types)]
89+
pub struct BigEndian_u16;
90+
impl Conversion for BigEndian_u16 {
91+
type RegisterType = u16;
92+
type UsiType = u16;
93+
94+
fn from_raw(raw: u16) -> u16 {
95+
raw.to_be()
96+
}
97+
98+
fn to_raw(value: u16) -> u16 {
99+
value.to_be()
100+
}
101+
}
102+
103+
#[allow(non_camel_case_types)]
104+
pub struct BigEndian_i16;
105+
impl Conversion for BigEndian_i16 {
106+
type RegisterType = u16;
107+
type UsiType = i16;
108+
109+
fn from_raw(raw: u16) -> i16 {
110+
if raw.to_be() > (1 << 10) {
111+
-((raw.to_be() & 0x3ff) as i16)
112+
} else {
113+
(raw.to_be() & 0x3ff) as i16
114+
}
115+
}
116+
117+
fn to_raw(value: i16) -> u16 {
118+
value.to_be() as u16
119+
}
120+
}
121+
122+
pub struct TorqueLimit;
123+
impl Conversion for TorqueLimit {
124+
type RegisterType = u16;
125+
type UsiType = f64;
126+
127+
fn from_raw(raw: u16) -> f64 {
128+
raw.to_be() as f64 * 0.1
129+
}
130+
131+
fn to_raw(value: f64) -> u16 {
132+
(value.clamp(0.0, 100.0) as u16 * 10).to_be()
133+
}
134+
}

src/servo/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ crate::register_servo!(
2525
servo: (feetech, STS3215,
2626
(STS3215, 2307)
2727
),
28+
servo: (feetech, SCS0009,
29+
(SCS0009, 1280)
30+
),
2831
servo: (orbita, orbita2d_poulpe,
2932
(orbita2d_poulpe, 10020)
3033
),

0 commit comments

Comments
 (0)