Skip to main content

device_envoy/ir/
kepler.rs

1//! A device abstraction for the SunFounder Kepler Kit IR remote control.
2//!
3//! See [`IrKepler`] for usage examples.
4
5use embassy_executor::Spawner;
6use embassy_rp::Peri;
7use embassy_rp::gpio::Pin;
8use embassy_rp::pio::PioPin;
9
10use crate::Result;
11use crate::ir::IrPioPeripheral;
12use crate::ir::mapping::{IrMapping, IrMappingStatic};
13
14/// Button types for the SunFounder Kepler Kit remote control.
15#[derive(defmt::Format, Clone, Copy, PartialEq, Eq)]
16pub enum KeplerButton {
17    /// Power button.
18    Power,
19    /// Mode button.
20    Mode,
21    /// Mute button.
22    Mute,
23    /// Play/Pause button.
24    PlayPause,
25    /// Previous track button.
26    Prev,
27    /// Next track button.
28    Next,
29    /// Equalizer button.
30    Eq,
31    /// Minus/Decrease volume button.
32    Minus,
33    /// Plus/Increase volume button.
34    Plus,
35    /// Numbered button (0-9).
36    Num(u8),
37    /// Repeat button.
38    Repeat,
39    /// USB/SD card mode button.
40    USd,
41}
42
43/// Static resources for Kepler IR remote events.
44///
45/// See [`IrKepler`] for usage examples.
46pub struct IrKeplerStatic(IrMappingStatic);
47
48impl IrKeplerStatic {
49    /// Create static resources for the Kepler remote.
50    #[must_use]
51    pub const fn new() -> Self {
52        Self(IrMappingStatic::new())
53    }
54
55    pub(crate) const fn inner(&self) -> &IrMappingStatic {
56        &self.0
57    }
58}
59
60/// Type alias for the Kepler button mapping.
61///
62/// See [`IrKepler`] for usage examples.
63type IrKeplerMapping<'a> = IrMapping<'a, KeplerButton, 21>;
64
65/// Button mapping for the SunFounder Kepler Kit remote (ordered to match physical layout).
66const KEPLER_MAPPING: [(u16, u8, KeplerButton); 21] = [
67    // Row 1: Power, Mode, Mute
68    (0x0000, 0x45, KeplerButton::Power),
69    (0x0000, 0x46, KeplerButton::Mode),
70    (0x0000, 0x47, KeplerButton::Mute),
71    // Row 2: PlayPause, Prev, Next
72    (0x0000, 0x44, KeplerButton::PlayPause),
73    (0x0000, 0x40, KeplerButton::Prev),
74    (0x0000, 0x43, KeplerButton::Next),
75    // Row 3: EQ, Minus, Plus
76    (0x0000, 0x07, KeplerButton::Eq),
77    (0x0000, 0x15, KeplerButton::Minus),
78    (0x0000, 0x09, KeplerButton::Plus),
79    // Row 4: 0, Repeat, U/SD
80    (0x0000, 0x16, KeplerButton::Num(0)),
81    (0x0000, 0x19, KeplerButton::Repeat),
82    (0x0000, 0x0D, KeplerButton::USd),
83    // Row 5: 1, 2, 3
84    (0x0000, 0x0C, KeplerButton::Num(1)),
85    (0x0000, 0x18, KeplerButton::Num(2)),
86    (0x0000, 0x5E, KeplerButton::Num(3)),
87    // Row 6: 4, 5, 6
88    (0x0000, 0x08, KeplerButton::Num(4)),
89    (0x0000, 0x1C, KeplerButton::Num(5)),
90    (0x0000, 0x5A, KeplerButton::Num(6)),
91    // Row 7: 7, 8, 9
92    (0x0000, 0x42, KeplerButton::Num(7)),
93    (0x0000, 0x52, KeplerButton::Num(8)),
94    (0x0000, 0x4A, KeplerButton::Num(9)),
95];
96
97/// A device abstraction for the SunFounder Kepler Kit IR remote.
98///
99/// This provides a simple interface for the Kepler remote with built-in button mappings.
100///
101/// # Examples
102/// ```rust,no_run
103/// # #![no_std]
104/// # #![no_main]
105/// # use panic_probe as _;
106/// use device_envoy::ir::{IrKepler, IrKeplerStatic};
107///
108/// async fn example(
109///     p: embassy_rp::Peripherals,
110///     spawner: embassy_executor::Spawner,
111/// ) -> device_envoy::Result<()> {
112///     static IR_KEPLER_STATIC: IrKeplerStatic = IrKepler::new_static();
113///     let ir_kepler = IrKepler::new(&IR_KEPLER_STATIC, p.PIN_15, p.PIO0, spawner)?;
114///
115///     loop {
116///         let button = ir_kepler.wait_for_press().await;
117///         defmt::info!("Button: {:?}", button);
118///     }
119/// }
120/// ```
121pub struct IrKepler<'a> {
122    mapping: IrKeplerMapping<'a>,
123}
124
125impl<'a> IrKepler<'a> {
126    /// Create static channel resources for IR events.
127    ///
128    /// See [`IrKepler`] for usage examples.
129    #[must_use]
130    pub const fn new_static() -> IrKeplerStatic {
131        IrKeplerStatic::new()
132    }
133
134    /// Create a new Kepler remote handler.
135    ///
136    /// # Parameters
137    /// - `ir_kepler_static`: Static reference to the channel resources
138    /// - `pin`: GPIO pin connected to the IR receiver
139    /// - `pio`: PIO peripheral to use (PIO0, PIO1, or PIO2)
140    /// - `spawner`: Embassy spawner for background task
141    ///
142    /// See [`IrKepler`] for usage examples.
143    ///
144    /// # Errors
145    /// Returns an error if the background task cannot be spawned.
146    pub fn new<P, PIO>(
147        ir_kepler_static: &'static IrKeplerStatic,
148        pin: Peri<'static, P>,
149        pio: Peri<'static, PIO>,
150        spawner: Spawner,
151    ) -> Result<Self>
152    where
153        P: Pin + PioPin,
154        PIO: IrPioPeripheral,
155    {
156        let mapping = IrMapping::new(ir_kepler_static.inner(), pin, pio, &KEPLER_MAPPING, spawner)?;
157        Ok(Self { mapping })
158    }
159
160    /// Wait for the next button press.
161    ///
162    /// Ignores button presses that are not recognized by the Kepler remote.
163    ///
164    /// See [`IrKepler`] for usage examples.
165    pub async fn wait_for_press(&self) -> KeplerButton {
166        self.mapping.wait_for_press().await
167    }
168}