on_off_sequence_output/lib.rs
1//! Output of a sequence of on/off states
2//!
3//! * the state sequence can be modified
4//! * the sequence can be repeated
5//! * An `update()` should be called periodically progress the output
6//! in time.
7//!
8//! # Implementation
9//!
10//! * The OutputLED Structure aggregates a GPIO, (bound at init)
11//! * Output Pattern can be changed, while the GPIO binding is fixed
12//! * A trait (for update) is used to cope with propagate GPIO errors
13//! * A prelude simplifies getting the trait in scope
14//!
15//! # Example
16//!
17//! For examples check the examples directory
18//! directory in the repository.
19//!
20//! ```rust,ignore
21//! use on_off_sequence_output::prelude::*;
22//!
23//! // This is up to the implementation details of the embedded_hal you are using.
24//! let led_pin: OutputPin = hal_function_which_returns_output_pin();
25//!
26//! const UPDATE_SCALE: u16 = 500;
27//! let mut led = OnOffSequenceOutput::new(led_pin, UPDATE_SCALE);
28//!
29//! let output_states = 0b10011101;
30//! let number_of_output_states = 8;
31//! led.set(output_states, number_of_output_states, Repeat::Never)
32//! loop {
33//! if led.update().unwrap() { break; };
34//! wait(1.ms());
35//! }
36//!
37//! led.set(0b010, 3, Repeat::Times(2))
38//! loop {
39//! if led.update().unwrap() { break; };
40//! wait(1.ms());
41//! }
42//!
43//! led.set(0b10, 2, Repeat::Forever)
44//! loop {
45//! led.update().unwrap();
46//! wait(1.ms());
47//! }
48
49//! ```
50
51#![no_std]
52
53pub mod prelude;
54
55pub mod morse;
56
57use embedded_hal::digital::v2::OutputPin;
58use morse::{str_to_morse, MorseError};
59// use bitset_core::BitSet;
60
61/// How often shall the output repeated
62#[derive(Clone, Copy, Debug, PartialEq)]
63pub enum Repeat {
64 Never,
65 Times(u16),
66 Forever,
67}
68
69/// OutputUpdate Trait which provides an `update()` method
70pub trait OutputUpdate {
71 type Error;
72
73 /// Updates the output logic and potentially switches the LED state
74 ///
75 /// # Returns
76 ///
77 /// * Error - if the hardware GPIO switch to on/off failed
78 /// * true - if no further update repetitions are necessary to complete the
79 /// the output
80 /// * false - otherwise
81 ///
82 /// # Notes
83 ///
84 /// * Needs to be called periodically.
85 /// * Side Effect: calls the aggregated GPIO pin to switch on/off
86 fn update(&mut self) -> Result<bool, Self::Error>;
87}
88
89/// Output of blinking patterns on an LED
90pub struct OnOffSequenceOutput<T: OutputPin> {
91 /// The wrapped output pin.
92 pub pin: T,
93
94 /// The update scaler: the clock rate at wich the output state changes
95 /// is equivalent the frequency of the update calls times *update_scale*
96 update_scale: u16,
97
98 /// The repeat configuration
99 repeat: Repeat,
100
101 /// The output states are represented by the bits of an unsigned 128-bit integer
102 output_states: u128,
103
104 /// How many bits are considered (min 1, max: 128)
105 number_of_output_states: u16,
106
107 /// Internal state: Manage scaling
108 scale_index: u16,
109
110 /// internal state: Manage next output state
111 state_index: u16,
112
113 /// Internal state: Run output indicator
114 ///
115 /// # Values
116 ///
117 /// * true - either a run is not completed or there are more repetitions to do
118 /// * false - run is completed (intermediate) and no more repetitions are
119 /// needed.
120 run_output: bool,
121}
122
123impl<T: OutputPin> OnOffSequenceOutput<T> {
124 /// Initializes a new led output
125 ///
126 /// # Arguments
127 ///
128 /// * `pin` - An as output initialized GPIO pin
129 /// * `update_scale` - Scale factor:
130 /// state change frequency = update frequency * update_scale
131 ///
132 /// # Notes
133 ///
134 /// * Default is symmetrically blinking forever
135 pub fn new(pin: T, update_scale: u16) -> Self {
136 Self {
137 pin,
138 update_scale,
139 output_states: 0b_10_u128,
140 number_of_output_states: 2,
141 repeat: Repeat::Forever,
142 scale_index: 0u16,
143 state_index: 0u16,
144 run_output: true,
145 }
146 }
147
148 fn reinitialize_internal_state(&mut self) {
149 self.scale_index = 0u16;
150 self.state_index = 0u16;
151 self.run_output = true;
152 }
153
154 /// Set a new output
155 ///
156 /// # Arguments
157 ///
158 /// * `output_states` - bits of a unsigned number: 1 equals on; 0 equals off
159 /// The bits are processes from lsb to msb.
160 /// * `number_of_output_states` - how many bits of the fixed number are
161 /// considered to for the output state sequence counted from lsb
162 /// * `repeat` - How often is the pattern repeated
163 ///
164 pub fn set(&mut self, output_states: u128, number_of_output_states: u16, repeat: Repeat) {
165 if number_of_output_states > 127 {
166 panic!("Must be less than 128 output states");
167 };
168 if number_of_output_states == 0 {
169 panic!("Zero output states do not make sense");
170 };
171 self.output_states = output_states;
172 self.number_of_output_states = number_of_output_states;
173 self.repeat = repeat;
174 self.reinitialize_internal_state();
175 }
176
177 /// Set a new morse code as output
178 ///
179 /// # Arguments
180 ///
181 /// * `morse_text` - Short text to be output as morse code sequence
182 /// * `repeat` - How often the morse text is repeated
183 ///
184 /// # Returns
185 ///
186 /// A result structure
187 ///
188 /// * with empty value if Ok()
189 /// * or Err(MorseError)
190 pub fn set_morse(&mut self, morse_text: &str, repeat: Repeat) -> Result<(), MorseError> {
191 let t = str_to_morse(morse_text)?;
192 self.output_states = t.0;
193 self.number_of_output_states = t.1;
194 self.reinitialize_internal_state();
195 self.repeat = repeat;
196 Ok(())
197 }
198}
199
200/// check if a certain position is set
201fn state_at_position(states: u128, position: u16) -> bool {
202 let mask: u128 = 1 << position;
203 if (states & mask) == 0 {
204 return false;
205 }
206 true
207}
208
209impl<T: OutputPin> OutputUpdate for OnOffSequenceOutput<T> {
210 type Error = T::Error;
211
212 /// Updates the output logic and potentially switches the LED state
213 fn update(&mut self) -> Result<bool, Self::Error> {
214 // handle the update scale
215 self.scale_index += 1;
216 if self.update_scale > self.scale_index {
217 return Ok(!self.run_output);
218 }
219 self.scale_index = 0;
220
221 // handle the output sequence
222 if self.run_output {
223 // if we get here, always some output has to happen
224 if state_at_position(self.output_states, self.state_index) {
225 self.pin.set_high()?;
226 } else {
227 self.pin.set_low()?;
228 }
229 self.state_index += 1;
230 if self.state_index >= self.number_of_output_states {
231 // all states are "printed"
232 self.run_output = false;
233 self.state_index = 0;
234 } else {
235 }
236 }
237
238 // handle the repetitions
239 if !self.run_output {
240 self.repeat = match self.repeat {
241 Repeat::Never => Repeat::Never,
242 Repeat::Forever => Repeat::Forever,
243 Repeat::Times(n) => {
244 if n > 0 {
245 Repeat::Times(n - 1)
246 } else {
247 Repeat::Never
248 }
249 }
250 };
251 self.run_output = match self.repeat {
252 Repeat::Never => false,
253 Repeat::Forever => true,
254 Repeat::Times(_) => true,
255 };
256 }
257
258 Ok(!self.run_output)
259 }
260}
261
262/// Determine at position of the most left one in the bitfield represented as u128
263///
264/// # Arguments
265///
266/// * `bitfield` - The bitfield to find the most left bit that is set to one
267///
268/// # Returns
269///
270/// *
271pub fn position_of_highest_one(bitfield: u128) -> u16 {
272 const MSB_ONE: u128 = 1 << 127;
273
274 let mut position = 127_u16;
275 let mut bitfield = bitfield;
276 while (bitfield & MSB_ONE) == 0 && position > 0 {
277 bitfield = bitfield << 1;
278 position -= 1;
279 }
280 position
281}
282
283pub mod macros {
284 /// Simplified setting of the output without repetitions
285 ///
286 /// The number of the output states is automatically computed
287 /// It requires that the last output state equals to one
288 ///
289 /// # Arguments
290 ///
291 /// * `Instance of OnOffSequenceOutput`
292 /// * `bitfield (u128)` - MSB of the Output sequence must be one
293 ///
294 /// # Examples
295 ///
296 /// ```rust,ignore
297 /// set_output_once!(ledout, 0b1100);
298 /// // ... is equivalent to ...
299 /// // ledout.set(0b1100, 4, Repeat::Never);
300 /// ```
301 #[macro_export]
302 macro_rules! set_output_once {
303 ($a:expr, $b:expr) => {
304 $a.set($b, position_of_highest_one($b), Repeat::Never)
305 };
306 }
307
308 /// Simplified setting of the output with infinite repetitions
309 ///
310 /// The number of the output states is automatically computed
311 /// It requires that the last output state equals to one
312 ///
313 /// # Arguments
314 ///
315 /// * `Instance of OnOffSequenceOutput`
316 /// * `bitfield (u128)` - MSB of the Output sequence must be one
317 ///
318 /// # Examples
319 /// ```rust,ignore
320 /// set_output_forever!(ledout, 0b1000);
321 /// // ... is equivalent to ...
322 /// // ledout.set(0b1000, 4, Repeat::Forever);
323 /// ```
324
325 #[macro_export]
326 macro_rules! set_output_forever {
327 ($a:expr, $b:expr) => {
328 $a.set($b, position_of_highest_one($b), Repeat::Forever)
329 };
330 }
331}
332
333#[cfg(test)]
334mod tests;