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}