pamoja_actuators/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]
2
3//! Concrete actuator drivers for the pamoja SDK.
4//!
5//! Where [`pamoja-sensors`](https://docs.rs/pamoja-sensors) decodes what a part
6//! reports, this crate encodes what a part should do: it turns a desired output (a
7//! PWM frequency and duty, a servo angle, a motor step) into the exact register bytes
8//! or coil pattern a driver writes to the hardware. Each module is two layers. The
9//! encode layer is pure logic with no I/O, so it runs on a microcontroller, on a
10//! gateway, and in a test with nothing plugged in. The driver layer (the
11//! `embedded-hal` feature, on by default) is a type named after the part that owns
12//! a bus or a set of pins from [`pamoja-hal`](https://docs.rs/pamoja-hal), performs
13//! the datasheet's transfer sequence, and implements the core
14//! [`Actuator`](https://docs.rs/pamoja-core/latest/pamoja_core/trait.Actuator.html)
15//! trait, so a `Pca9685` drives its channels through any `embedded-hal` I2C
16//! implementation and a stepper steps through any four output pins. Every driver is
17//! tested against its datasheet's own sequence over a scripted bus, and a driver's
18//! error converts into the core error so the part slots into a profile or a node
19//! unchanged.
20//!
21//! - [`pca9685`] - the NXP PCA9685 16-channel 12-bit PWM controller, the common way
22//! to drive servos, dimmable LEDs, and motor-driver inputs over I2C. Its register
23//! map, prescale formula, and channel words follow the datasheet.
24//! - [`stepper`] - coil sequencing for four-wire stepper motors: the wave, full-step,
25//! and half-step drive patterns, plus a step-and-direction position model for
26//! driver chips that take a step pulse and a direction level.
27//!
28//! Simple on/off actuators (relays, solenoid valves, a pump switched through a
29//! transistor) need no driver of their own: they are a GPIO line, modeled by the pin
30//! and logic-level types in [`pamoja-gpio`](https://docs.rs/pamoja-gpio).
31//!
32//! # Examples
33//!
34//! The encode layer works out what a part is to be told, with nothing plugged in: the
35//! prescale that runs a PCA9685 at the 50 Hz a servo wants, the pulse that centers the
36//! servo, and the steps a geared stepper takes to swing a greenhouse vent a quarter turn.
37//!
38//! ```
39//! use pamoja_actuators::pca9685::{
40//! frequency_for_prescale, prescale_for_frequency, Pwm, INTERNAL_OSC_HZ,
41//! };
42//! use pamoja_actuators::stepper::{steps_for_degrees, Direction, Drive, Sequencer};
43//!
44//! // The PCA9685 divides its 25 MHz clock by a prescale, so 50 Hz is as close as it gets.
45//! let prescale = prescale_for_frequency(50, INTERNAL_OSC_HZ);
46//! assert!((frequency_for_prescale(prescale, INTERNAL_OSC_HZ) - 50.0).abs() < 0.5);
47//!
48//! // A 1.5 ms pulse in each 20 ms period centers a hobby servo: 307 of the 4096 counts.
49//! let center = Pwm::servo(1_500, 50);
50//! assert_eq!(center.off() - center.on(), 307);
51//!
52//! // A stepper geared to 2048 full steps a turn needs 512 for the quarter, a whole number
53//! // of coil cycles, so the coils end where they started.
54//! let steps = steps_for_degrees(90.0, 2_048);
55//! assert_eq!(steps, 512);
56//! let mut coils = Sequencer::new(Drive::FullStep);
57//! for _ in 0..steps {
58//! coils.step(Direction::Forward);
59//! }
60//! assert_eq!(coils.coils(), Drive::FullStep.pattern()[0]);
61//! ```
62
63#[cfg(feature = "embedded-hal")]
64extern crate alloc;
65
66#[cfg(feature = "embedded-hal")]
67mod error;
68pub mod pca9685;
69pub mod stepper;
70
71#[cfg(feature = "embedded-hal")]
72pub use error::DriverError;