1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
//! This is a `no_std` driver for the [TB6612FNG motor driver](https://www.sparkfun.com/datasheets/Robotics/TB6612FNG.pdf) as can e.g. be found on the corresponding [SparkFun module](https://www.sparkfun.com/products/14450).
//!
//! The motor driver itself supports two motors and has a standby pin which controls both motors at the same time.
//! The crate can be either used to control a single motor (using the [`Motor`] struct directly) or
//! to control both motors (using the [`Tb6612fng`] struct) - the latter also supports using the standby functionality.
//!
//! ## When to use what
//! * You plan on using both motors and the standby feature: use [`Tb6612fng`]
//! * You plan on using both motors without the standby feature: use two separate [`Motor`]s
//! * You plan on using a single motor with the standby feature: use [`Motor`] and control the standby pin manually
//! * You plan on using a single motor without the standby feature: use [`Motor`]
//!
//! ## Optional features
//! * `defmt`: you can enable the `defmt` feature to get a `defmt::Format` implementation for all structs & enums in this crate and a `defmt::debug` call for every speed change.

#![forbid(unsafe_code)]
#![forbid(warnings)]
#![forbid(missing_docs)]
#![forbid(missing_debug_implementations)]
#![forbid(unused)]
#![no_std]

#[cfg(feature = "defmt")]
use defmt::Format;
use embedded_hal::digital::v2::OutputPin;
use embedded_hal::PwmPin;

/// Defines errors which can happen while trying to set a speed.
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(Format))]
pub enum DriveError {
    /// An invalid speed has been defined. The speed must be given as a percentage value between 0 and 100 to be valid.
    InvalidSpeed,
}

/// Defines the possible drive commands.
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(Format))]
pub enum DriveCommand {
    /// Drive forward with the defined speed (in percentage)
    Forward(u8),
    /// Drive backwards with the defined speed (in percentage)
    Backwards(u8),
    /// Actively brake
    Brake,
    /// Coast, i.e. stop but don't actively brake.
    Stop,
}

/// Represents a TB6612FNG controller.
///
/// Use the [`Motor`] struct directly if you only have one motor.
/// See the crate-level comment for further details on when to use what.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(Format))]
pub struct Tb6612fng<MAIN1, MAIN2, MAPWM, MBIN1, MBIN2, MBPWM, STBY> {
    /// The first motor, labelled as 'A' on the chip
    pub motor_a: Motor<MAIN1, MAIN2, MAPWM>,
    /// The second motor, labelled as 'B' on the chip
    pub motor_b: Motor<MBIN1, MBIN2, MBPWM>,
    /// The standby pin used to put both motors on standby
    standby: STBY,
}

impl<MAIN1, MAIN2, MAPWM, MBIN1, MBIN2, MBPWM, STBY>
    Tb6612fng<MAIN1, MAIN2, MAPWM, MBIN1, MBIN2, MBPWM, STBY>
where
    MAIN1: OutputPin,
    MAIN2: OutputPin,
    MAPWM: PwmPin<Duty = u16>,
    MBIN1: OutputPin,
    MBIN2: OutputPin,
    MBPWM: PwmPin<Duty = u16>,
    STBY: OutputPin,
{
    /// Instantiate a new [`Tb6612fng`] with the defined pins.
    /// This also automatically enables the two PWM pins.
    /// The initial state of the motors will be [stopped](DriveCommand::Stop).
    ///
    /// Usage example:
    /// ```
    /// # use embedded_hal_mock::pin::Mock as PinMock;
    /// # use embedded_hal_mock::pin::Transaction as PinTransaction;
    /// # let motor_a_in1 = PinMock::new([]);
    /// # let motor_a_in2 = PinMock::new([]);
    /// # let motor_a_pwm_expectations = [PinTransaction::enable()];
    /// # let motor_a_pwm = PinMock::new(&motor_a_pwm_expectations);
    /// # let motor_b_in1 = PinMock::new([]);
    /// # let motor_b_in2 = PinMock::new([]);
    /// # let motor_b_pwm_expectations = [PinTransaction::enable()];
    /// # let motor_b_pwm = PinMock::new(&motor_a_pwm_expectations);
    /// # let standby = PinMock::new([]);
    /// use tb6612fng::Tb6612fng;
    ///
    /// let controller = Tb6612fng::new(
    ///     motor_a_in1,
    ///     motor_a_in2,
    ///     motor_a_pwm,
    ///     motor_b_in1,
    ///     motor_b_in2,
    ///     motor_b_pwm,
    ///     standby,
    /// );
    /// ```
    pub fn new(
        motor_a_in1: MAIN1,
        motor_a_in2: MAIN2,
        motor_a_pwm: MAPWM,
        motor_b_in1: MBIN1,
        motor_b_in2: MBIN2,
        motor_b_pwm: MBPWM,
        standby: STBY,
    ) -> Tb6612fng<MAIN1, MAIN2, MAPWM, MBIN1, MBIN2, MBPWM, STBY> {
        Tb6612fng {
            motor_a: Motor::new(motor_a_in1, motor_a_in2, motor_a_pwm),
            motor_b: Motor::new(motor_b_in1, motor_b_in2, motor_b_pwm),
            standby,
        }
    }

    /// Enable standby. This ignores any other setting currently done on the motors and puts them into standby.
    ///
    /// Note that this does not change any commands on the motors, i.e. the PWM signal will continue
    /// and once [`Tb6612fng::disable_standby`] is called the motor will pick up where it left off (unless the command was changed in-between).
    pub fn enable_standby(&mut self) {
        self.standby.set_low().ok();
    }

    /// Disable standby. Note that the last active commands on the motors will resume.
    pub fn disable_standby(&mut self) {
        self.standby.set_high().ok();
    }
}

/// Represents a single motor (either motor A or motor B) hooked up to a TB6612FNG controller.
/// This is unaware of the standby pin. If you plan on using both motors and the standby feature then use the [`Tb6612fng`] struct instead.
/// See the crate-level comment for further details on when to use what.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(Format))]
pub struct Motor<IN1, IN2, PWM> {
    in1: IN1,
    in2: IN2,
    pwm: PWM,
    current_drive_command: DriveCommand,
}

impl<IN1, IN2, PWM> Motor<IN1, IN2, PWM>
where
    IN1: OutputPin,
    IN2: OutputPin,
    PWM: PwmPin<Duty = u16>,
{
    /// Instantiate a new [`Motor`] with the defined pins.
    /// This also automatically enables the PWM pin.
    /// The initial state of the motor will be [stopped](DriveCommand::Stop).
    ///
    /// Usage example:
    /// ```
    /// # use embedded_hal_mock::pin::Mock as PinMock;
    /// # use embedded_hal_mock::pin::Transaction as PinTransaction;
    /// # let motor_in1 = PinMock::new([]);
    /// # let motor_in2 = PinMock::new([]);
    /// # let motor_pwm_expectations = [PinTransaction::enable()];
    /// # let motor_pwm = PinMock::new(&motor_pwm_expectations);
    /// use tb6612fng::Motor;
    ///
    /// let motor = Motor::new(
    ///     motor_in1,
    ///     motor_in2,
    ///     motor_pwm,
    /// );
    /// ```
    pub fn new(in1: IN1, in2: IN2, mut pwm: PWM) -> Motor<IN1, IN2, PWM> {
        pwm.enable();
        Motor {
            in1,
            in2,
            pwm,
            current_drive_command: DriveCommand::Stop,
        }
    }

    /// Drive forward with the defined speed. Note that the speed is a percentage between 0 and 100!
    pub fn drive_forward(&mut self, speed: u8) -> Result<(), DriveError> {
        self.drive(DriveCommand::Forward(speed))
    }

    /// Drive backwards with the defined speed. Note that the speed is a percentage between 0 and 100!
    pub fn drive_backwards(&mut self, speed: u8) -> Result<(), DriveError> {
        self.drive(DriveCommand::Backwards(speed))
    }

    /// Actively brake.
    pub fn brake(&mut self) {
        self.drive(DriveCommand::Brake)
            .expect("could set speed to brake");
    }

    /// Stop the motor but don't brake (let it coast).
    pub fn stop(&mut self) {
        self.drive(DriveCommand::Stop)
            .expect("could set speed to stop");
    }

    /// Drive with the defined speed (or brake or stop the motor).
    pub fn drive(&mut self, drive_command: DriveCommand) -> Result<(), DriveError> {
        let speed = match drive_command {
            DriveCommand::Forward(s) | DriveCommand::Backwards(s) => s,
            _ => 0,
        };

        if speed > 100 {
            return Err(DriveError::InvalidSpeed);
        }

        match drive_command {
            DriveCommand::Forward(_) => {
                self.in1.set_high().ok();
                self.in2.set_low().ok();
            }
            DriveCommand::Backwards(_) => {
                self.in1.set_low().ok();
                self.in2.set_high().ok();
            }
            DriveCommand::Brake => {
                self.in1.set_high().ok();
                self.in2.set_high().ok();
            }
            DriveCommand::Stop => {
                self.in1.set_low().ok();
                self.in2.set_low().ok();
            }
        }

        let max_duty = self.pwm.get_max_duty();

        let duty = (speed as f32 * (max_duty as f32 / 100.0)) as u16; // speed given in percentage

        #[cfg(feature = "defmt")]
        defmt::debug!(
            "driving {} with duty {} (max duty: {})",
            drive_command,
            duty,
            max_duty
        );

        self.pwm.set_duty(duty);

        self.current_drive_command = drive_command;

        Ok(())
    }

    /// Get the currently active drive command.
    ///
    /// If you only want to know the speed consider calling [`Motor::current_speed`] instead.
    pub fn current_drive_command(&self) -> &DriveCommand {
        &self.current_drive_command
    }

    /// Return the current speed of the motor (in percentage). Note that driving forward returns a positive number
    /// while driving backwards returns a negative number and both [`DriveCommand::Brake`] and [`DriveCommand::Stop`] return 0.
    ///
    /// If you need to know in more details what the current status is consider calling [`Motor::current_drive_command`] instead.
    pub fn current_speed(&self) -> i8 {
        match self.current_drive_command() {
            DriveCommand::Forward(s) => *s as i8,
            DriveCommand::Backwards(s) => -(*s as i8),
            DriveCommand::Brake => 0,
            DriveCommand::Stop => 0,
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::{DriveCommand, DriveError, Motor};
    use embedded_hal_mock::pin::State::{High, Low};
    use embedded_hal_mock::pin::Transaction as PinTransaction;
    use embedded_hal_mock::pin::{Mock as PinMock, PwmDuty};

    #[test]
    fn test_motor_stop() {
        let max_duty = 100;
        let motor_in1_expectations = [PinTransaction::set(Low)];
        let motor_in2_expectations = [PinTransaction::set(Low)];
        let motor_pwm_expectations = [
            PinTransaction::enable(),
            PinTransaction::get_max_duty(max_duty),
            PinTransaction::set_duty(0),
        ];
        let motor_in1 = PinMock::new(&motor_in1_expectations);
        let motor_in2 = PinMock::new(&motor_in2_expectations);
        let motor_pwm = PinMock::new(&motor_pwm_expectations);

        let mut motor = Motor::new(motor_in1, motor_in2, motor_pwm);

        motor.stop();

        assert_eq!(*motor.current_drive_command(), DriveCommand::Stop);
        assert_eq!(motor.current_speed(), 0);
    }

    #[test]
    fn test_motor_brake() {
        let max_duty = 100;
        let motor_in1_expectations = [PinTransaction::set(High)];
        let motor_in2_expectations = [PinTransaction::set(High)];
        let motor_pwm_expectations = [
            PinTransaction::enable(),
            PinTransaction::get_max_duty(max_duty),
            PinTransaction::set_duty(0),
        ];
        let motor_in1 = PinMock::new(&motor_in1_expectations);
        let motor_in2 = PinMock::new(&motor_in2_expectations);
        let motor_pwm = PinMock::new(&motor_pwm_expectations);

        let mut motor = Motor::new(motor_in1, motor_in2, motor_pwm);

        motor.brake();

        assert_eq!(*motor.current_drive_command(), DriveCommand::Brake);
        assert_eq!(motor.current_speed(), 0);
    }

    #[test]
    fn test_motor_drive_forward() {
        let max_duty = 100;
        let speed: u8 = 100;
        let motor_in1_expectations = [PinTransaction::set(High)];
        let motor_in2_expectations = [PinTransaction::set(Low)];
        let motor_pwm_expectations = [
            PinTransaction::enable(),
            PinTransaction::get_max_duty(max_duty),
            PinTransaction::set_duty(speed as PwmDuty),
        ];
        let motor_in1 = PinMock::new(&motor_in1_expectations);
        let motor_in2 = PinMock::new(&motor_in2_expectations);
        let motor_pwm = PinMock::new(&motor_pwm_expectations);

        let mut motor = Motor::new(motor_in1, motor_in2, motor_pwm);

        motor.drive_forward(speed).expect("speed can be set");

        assert_eq!(*motor.current_drive_command(), DriveCommand::Forward(100));
        assert_eq!(motor.current_speed(), speed as i8);
    }

    #[test]
    fn test_motor_drive_backwards() {
        let max_duty = 100;
        let speed: u8 = 100;
        let motor_in1_expectations = [PinTransaction::set(Low)];
        let motor_in2_expectations = [PinTransaction::set(High)];
        let motor_pwm_expectations = [
            PinTransaction::enable(),
            PinTransaction::get_max_duty(max_duty),
            PinTransaction::set_duty(speed as PwmDuty),
        ];
        let motor_in1 = PinMock::new(&motor_in1_expectations);
        let motor_in2 = PinMock::new(&motor_in2_expectations);
        let motor_pwm = PinMock::new(&motor_pwm_expectations);

        let mut motor = Motor::new(motor_in1, motor_in2, motor_pwm);

        motor.drive_backwards(speed).expect("speed can be set");

        assert_eq!(*motor.current_drive_command(), DriveCommand::Backwards(100));
        assert_eq!(motor.current_speed(), -(speed as i8));
    }

    #[test]
    fn test_motor_drive_invalid_speed() {
        let max_duty = 100;
        let motor_in1_expectations = [PinTransaction::set(Low)];
        let motor_in2_expectations = [PinTransaction::set(High)];
        let motor_pwm_expectations = [
            PinTransaction::enable(),
            PinTransaction::get_max_duty(max_duty),
            PinTransaction::set_duty(100),
        ];
        let motor_in1 = PinMock::new(&motor_in1_expectations);
        let motor_in2 = PinMock::new(&motor_in2_expectations);
        let motor_pwm = PinMock::new(&motor_pwm_expectations);

        let mut motor = Motor::new(motor_in1, motor_in2, motor_pwm);

        let current_drive_command = motor.current_drive_command().clone();
        let current_speed = motor.current_speed();

        assert_eq!(
            motor
                .drive_forward(101)
                .expect_err("Invalid speed must result in an exception"),
            DriveError::InvalidSpeed
        );

        // this should still be what was set before the invalid command
        assert_eq!(*motor.current_drive_command(), current_drive_command);
        assert_eq!(motor.current_speed(), current_speed);
    }
}