calibrating_steering/
calibrating_steering.rs

1// This example function calibrates the range of a steering motor and preset the middle (0)
2// It is assumed that it is easy to steer the motor and there are physical barriers at the extremes.
3//
4//
5// 
6
7use std::{
8    str::FromStr, 
9    time::Duration
10};
11
12use tokio::time;
13
14use anyhow::{Result};
15use btleplug::api::BDAddr;
16use rust_powered_lego::{
17    hub::{
18        Hub
19    },
20    connection_manager::ConnectionManager, 
21    HubType,
22    MotorType,
23    lego::message_parameters::{
24        StartupAndCompletionInfo,
25    },
26    lego::{
27        consts::{
28            TechnicHubPorts,
29            EndState, 
30            Profile, 
31            MotorModes,
32        },
33    },
34};
35
36
37#[tokio::main]
38async fn main() -> Result<()> {
39    // Hub "MAC" address can be found in several ways. 
40    // Connect it to a computer and continue from there...
41    let hub_mac_address = "90:84:2b:4e:5b:96";
42    let port_id = TechnicHubPorts::B;
43    let hub = get_hub(hub_mac_address).await?;
44    let motor = hub.get_motor(port_id as u8).await?;
45
46    //Setting notifications. It's important - otherwise the port value won't update
47    _ = hub.setup_port_input_format(port_id as u8, MotorModes::Pos as u8, 1, true).await?;
48
49    let mut width = get_degree_width_avg(&hub, port_id as u8, &motor).await?;
50    
51    // Just in case, let's do it again!
52    width = (width + get_degree_width_avg(&hub, port_id as u8, &motor).await?) / 2;
53
54    // Set width to half of it
55    width /= 2;
56
57    move_motor_to_extreme(&motor, 1).await?;
58
59    _ = motor.set_abs_position(width as i32, StartupAndCompletionInfo::BufferAndNoAction).await?;
60
61    println!("Done! Calibrated!");
62
63    // Checking what we've done.
64    // Please, check if the wheels in the end, are at the middle...
65    motor.go_to_abs_position(
66        -(width as i32),
67        10, 
68        15, 
69        EndState::HOLD, 
70        Profile::AccDec, 
71        StartupAndCompletionInfo::ExecuteImmediatelyAndNoAction
72    ).await?;
73
74    time::sleep(Duration::from_secs(5)).await;
75
76    motor.go_to_abs_position(
77        0,
78        10, 
79        15, 
80        EndState::HOLD, 
81        Profile::AccDec, 
82        StartupAndCompletionInfo::ExecuteImmediatelyAndNoAction
83    ).await?;
84
85    time::sleep(Duration::from_secs(5)).await;
86
87    Ok(())
88}
89
90async fn get_hub(address:  &str) -> Result<Hub> {
91    
92    // Converting the MAC string to btleplug::api::BDAddr type
93    let address = BDAddr::from_str(address)?;
94
95    // The ConnectionManager connects stuff - so ask it for the hub...
96    let cm = ConnectionManager::new();
97
98    // It is possible to use the name of the hub or its MAC address. That's why it's Option<>
99    // Here, only address is implemented
100    let hub = cm.get_hub(None, Some(address), 5).await?;
101    
102    // Great! Let's get on with this...
103    Ok(hub)
104}
105
106
107// This is only in an example. So, be aware of setting up the notification prior to this function
108// Otherwise it is all in vein...
109// The point here is to move to one side, set it as zero, 
110// then move to the other side and measure how much it moved
111async fn get_degree_width_avg<T, U>(hub: &U, port_id: u8, motor: &T) -> Result<u32> 
112where
113    T: MotorType,
114    U: HubType,
115{
116    move_motor_to_extreme(motor, 1).await?;
117    
118    _ = motor.set_abs_position(0, StartupAndCompletionInfo::BufferAndNoAction).await?;
119    
120    move_motor_to_extreme(motor, -1).await?;
121    
122    let mut width = hub.get_port_info_raw_value(port_id).await?.abs() as u32;
123    
124    _ = motor.set_abs_position(0, StartupAndCompletionInfo::BufferAndNoAction).await?;
125    
126    move_motor_to_extreme(motor, 1).await?;
127    
128    width += width;
129    
130    Ok(width / 2)
131}
132
133async fn move_motor_to_extreme<T>(motor: &T, sign: i8) -> Result<()> 
134where
135    T: MotorType,
136{
137    let speed  = sign.signum() * 10;
138    let max_power = 15;
139    let end_state = EndState::HOLD;
140    let use_profile = Profile::AccDec;
141    let start_up_info = StartupAndCompletionInfo::ExecuteImmediatelyAndNoAction;
142    let degrees: i32 = 200;
143    let sleep_time = 5;
144    
145    motor.start_speed_for_deg(
146        degrees, 
147        speed, 
148        max_power, 
149        end_state, 
150        use_profile, 
151        start_up_info).await?;
152    
153    time::sleep(Duration::from_secs(sleep_time)).await;
154
155    Ok(())
156}