blinksy/control.rs
1//! # Control System
2//!
3//! [`Control`] is the central control system for Blinksy: connecting a layout, pattern,
4//! and driver together to form a complete LED control pipeline.
5//!
6//! As [`Control`] has a complex generic type signature, [`ControlBuilder`] is a builder to help
7//! you create [`Control`] instances.
8
9use core::marker::PhantomData;
10
11use crate::{
12 color::{ColorCorrection, FromColor},
13 dimension::{Dim1d, Dim2d, LayoutForDim},
14 driver::Driver as DriverTrait,
15 pattern::Pattern as PatternTrait,
16};
17
18/// Central LED control system.
19///
20/// A [`Control`] is made up of:
21///
22/// - A [`layout`](crate::layout)
23/// - A [`pattern`](crate::pattern)
24/// - A [`driver`](crate::driver)
25///
26/// You can use [`Control`] to
27///
28/// - Set a global brightness
29/// - Set a global color correction.
30/// - Send a frame of colors from the pattern to the driver.
31///
32/// Tip: Use [`ControlBuilder`] to build your [`Control`] struct.
33///
34/// # Type Parameters
35///
36/// * `Dim` - The dimension marker ([`Dim1d`] or [`Dim2d`])
37/// * `Layout` - The [`layout`](crate::layout) type
38/// * `Pattern` - The [`pattern`](crate::pattern) type
39/// * `Driver` - The LED [`driver`](crate::driver) type
40///
41/// # Example
42///
43/// ```rust,ignore
44/// use blinksy::{
45/// ControlBuilder,
46/// layout1d,
47/// patterns::rainbow::{Rainbow, RainbowParams}
48/// };
49///
50/// // Define a 1d layout of 60 LEDs
51/// layout1d!(Layout, 60);
52///
53/// // Create a control system
54/// let mut control = ControlBuilder::new_1d()
55/// .with_layout::<Layout>()
56/// .with_pattern::<Rainbow>(RainbowParams::default())
57/// .with_driver(/* LED driver */)
58/// .build();
59///
60/// // Use the control system
61/// control.set_brightness(0.5);
62///
63/// // Main control loop
64/// loop {
65/// control.tick(/* current time in milliseconds */).unwrap();
66/// }
67/// ```
68pub struct Control<Dim, Layout, Pattern, Driver> {
69 dim: PhantomData<Dim>,
70 layout: PhantomData<Layout>,
71 pattern: Pattern,
72 driver: Driver,
73 brightness: f32,
74 correction: ColorCorrection,
75}
76
77impl<Dim, Layout, Pattern, Driver> Control<Dim, Layout, Pattern, Driver> {
78 /// Creates a new control system.
79 ///
80 /// # Arguments
81 ///
82 /// * `pattern` - The pattern to use
83 /// * `driver` - The LED driver to use
84 ///
85 /// # Returns
86 ///
87 /// A new Control instance with default brightness
88 pub fn new(pattern: Pattern, driver: Driver) -> Self {
89 Self {
90 dim: PhantomData,
91 layout: PhantomData,
92 pattern,
93 driver,
94 brightness: 1.,
95 correction: ColorCorrection::default(),
96 }
97 }
98
99 /// Sets the overall brightness level.
100 ///
101 /// # Arguments
102 ///
103 /// * `brightness` - Brightness level from 0.0 (off) to 1.0 (full)
104 pub fn set_brightness(&mut self, brightness: f32) {
105 self.brightness = brightness;
106 }
107
108 /// Sets a color correction.
109 ///
110 /// # Arguments
111 ///
112 /// * `correction` - Color correction factors
113 pub fn set_color_correction(&mut self, correction: ColorCorrection) {
114 self.correction = correction;
115 }
116}
117
118impl<Dim, Layout, Pattern, Driver> Control<Dim, Layout, Pattern, Driver>
119where
120 Layout: LayoutForDim<Dim>,
121 Pattern: PatternTrait<Dim, Layout>,
122 Driver: DriverTrait,
123 Driver::Color: FromColor<Pattern::Color>,
124{
125 /// Updates the LED state based on the current time.
126 ///
127 /// This method:
128 /// 1. Calls the pattern to generate colors
129 /// 2. Passes the colors and brightness to the driver
130 ///
131 /// # Arguments
132 ///
133 /// * `time_in_ms` - Current time in milliseconds
134 ///
135 /// # Returns
136 ///
137 /// Result indicating success or an error from the driver
138 pub fn tick(&mut self, time_in_ms: u64) -> Result<(), Driver::Error> {
139 let pixels = self.pattern.tick(time_in_ms);
140 self.driver.write(pixels, self.brightness, self.correction)
141 }
142}
143
144///
145/// The builder allows your to build up your [`Control`] system one-by-one
146/// and handles the combination of generic types and contraints that [`Control`] expects.
147pub struct ControlBuilder<Dim, Layout, Pattern, Driver> {
148 dim: PhantomData<Dim>,
149 layout: PhantomData<Layout>,
150 pattern: Pattern,
151 driver: Driver,
152}
153
154impl ControlBuilder<(), (), (), ()> {
155 /// Starts building a one-dimensional control system.
156 ///
157 /// # Returns
158 ///
159 /// A builder initialized for 1D
160 pub fn new_1d() -> ControlBuilder<Dim1d, (), (), ()> {
161 ControlBuilder {
162 dim: PhantomData,
163 layout: PhantomData,
164 pattern: (),
165 driver: (),
166 }
167 }
168}
169
170impl ControlBuilder<(), (), (), ()> {
171 /// Starts building a two-dimensional control system.
172 ///
173 /// # Returns
174 ///
175 /// A builder initialized for 2D
176 pub fn new_2d() -> ControlBuilder<Dim2d, (), (), ()> {
177 ControlBuilder {
178 dim: PhantomData,
179 layout: PhantomData,
180 pattern: (),
181 driver: (),
182 }
183 }
184}
185
186impl<Dim, Pattern, Driver> ControlBuilder<Dim, (), Pattern, Driver> {
187 /// Specifies the layout type for the control system.
188 ///
189 /// # Type Parameters
190 ///
191 /// * `Layout` - The layout type implementing Layout that corresponds to Dim
192 ///
193 /// # Returns
194 ///
195 /// Builder with layout type specified
196 pub fn with_layout<Layout>(self) -> ControlBuilder<Dim, Layout, Pattern, Driver>
197 where
198 Layout: LayoutForDim<Dim>,
199 {
200 ControlBuilder {
201 dim: PhantomData,
202 layout: PhantomData,
203 pattern: self.pattern,
204 driver: self.driver,
205 }
206 }
207}
208
209impl<Dim, Layout, Driver> ControlBuilder<Dim, Layout, (), Driver>
210where
211 Layout: LayoutForDim<Dim>,
212{
213 /// Specifies the pattern and its parameters.
214 ///
215 /// # Type Parameters
216 ///
217 /// * `Pattern` - The pattern type implementing Pattern<Dim, Layout>
218 ///
219 /// # Arguments
220 ///
221 /// * `params` - The pattern parameters
222 ///
223 /// # Returns
224 ///
225 /// Builder with pattern specified
226 pub fn with_pattern<Pattern>(
227 self,
228 params: Pattern::Params,
229 ) -> ControlBuilder<Dim, Layout, Pattern, Driver>
230 where
231 Pattern: PatternTrait<Dim, Layout>,
232 {
233 let pattern = Pattern::new(params);
234 ControlBuilder {
235 dim: self.dim,
236 layout: self.layout,
237 pattern,
238 driver: self.driver,
239 }
240 }
241}
242
243impl<Dim, Layout, Pattern> ControlBuilder<Dim, Layout, Pattern, ()> {
244 /// Specifies the LED driver for the control system.
245 ///
246 /// # Arguments
247 ///
248 /// * `driver` - The LED driver instance
249 ///
250 /// # Returns
251 ///
252 /// Builder with driver specified
253 pub fn with_driver<Driver>(self, driver: Driver) -> ControlBuilder<Dim, Layout, Pattern, Driver>
254 where
255 Driver: DriverTrait,
256 {
257 ControlBuilder {
258 dim: self.dim,
259 layout: self.layout,
260 pattern: self.pattern,
261 driver,
262 }
263 }
264}
265
266impl<Dim, Layout, Pattern, Driver> ControlBuilder<Dim, Layout, Pattern, Driver>
267where
268 Layout: LayoutForDim<Dim>,
269 Pattern: PatternTrait<Dim, Layout>,
270 Driver: DriverTrait,
271 Driver::Color: FromColor<Pattern::Color>,
272{
273 /// Builds the final [`Control`] struct.
274 ///
275 /// # Returns
276 ///
277 /// A fully configured Control instance
278 pub fn build(self) -> Control<Dim, Layout, Pattern, Driver> {
279 Control::new(self.pattern, self.driver)
280 }
281}