lilkaoxide/
controller.rs

1use esp_hal::gpio::{Input, InputConfig, Pull};
2use esp_hal::peripherals::{
3    GPIO0, GPIO4, GPIO5, GPIO6, GPIO9, GPIO10, GPIO38, GPIO39, GPIO40, GPIO41,
4};
5
6/// State of all physical input buttons on the Lilka game console.
7///
8/// Each field is an `esp-hal` [`Input`] pin configured with an internal pull-up
9/// resistor.  The logic is **active-low**:
10/// * `is_low()` → button is **pressed**.
11/// * `is_high()` → button is **released**.
12///
13/// # Layout
14///
15/// ```text
16///                 [UP  GPIO38]
17/// [LEFT GPIO39]  [DOWN GPIO41]  [RIGHT GPIO40]
18///
19/// [A GPIO5]  [B GPIO6]  [C GPIO10]  [D GPIO9]
20///
21///      [SELECT GPIO0]   [START GPIO4]
22/// ```
23///
24/// # Example — D-pad and buttons in a game loop
25///
26/// ```rust,no_run
27/// use lilkaoxide::prelude::*;
28///
29/// # fn game_loop(mut lilka: Lilka) {
30/// loop {
31///     if lilka.controller.up.is_low() {
32///         // move player up
33///     }
34///     if lilka.controller.a.is_low() {
35///         // action A
36///     }
37///     if lilka.controller.start.is_low() {
38///         // pause menu
39///     }
40/// }
41/// # }
42/// ```
43pub struct ControllerState {
44    /// D-pad up — GPIO38, active-low.
45    pub up: Input<'static>,
46    /// D-pad down — GPIO41, active-low.
47    pub down: Input<'static>,
48    /// D-pad left — GPIO39, active-low.
49    pub left: Input<'static>,
50    /// D-pad right — GPIO40, active-low.
51    pub right: Input<'static>,
52    /// Action button A — GPIO5, active-low.
53    pub a: Input<'static>,
54    /// Action button B — GPIO6, active-low.
55    pub b: Input<'static>,
56    /// Action button C — GPIO10, active-low.
57    pub c: Input<'static>,
58    /// Action button D — GPIO9, active-low.
59    pub d: Input<'static>,
60    /// Select button — GPIO0, active-low.
61    pub select: Input<'static>,
62    /// Start button — GPIO4, active-low.
63    pub start: Input<'static>,
64}
65
66impl ControllerState {
67    /// Initialises all controller GPIO pins as pull-up inputs.
68    ///
69    /// This function is called internally by [`crate::Lilka::init`] — you should
70    /// not need to call it directly.  It moves the concrete GPIO peripheral tokens
71    /// into [`Input`] handles with `Pull::Up` so that pressing a button drives the
72    /// pin to ground (active-low).
73    ///
74    /// # Pin assignment
75    ///
76    /// | Button | GPIO | Field |
77    /// |---|---|---|
78    /// | A | 5 | [`ControllerState::a`] |
79    /// | B | 6 | [`ControllerState::b`] |
80    /// | C | 10 | [`ControllerState::c`] |
81    /// | D | 9 | [`ControllerState::d`] |
82    /// | Up | 38 | [`ControllerState::up`] |
83    /// | Down | 41 | [`ControllerState::down`] |
84    /// | Left | 39 | [`ControllerState::left`] |
85    /// | Right | 40 | [`ControllerState::right`] |
86    /// | Select | 0 | [`ControllerState::select`] |
87    /// | Start | 4 | [`ControllerState::start`] |
88    pub fn init_controller(
89        gpio5: GPIO5<'static>,
90        gpio6: GPIO6<'static>,
91        gpio10: GPIO10<'static>,
92        gpio9: GPIO9<'static>,
93        gpio38: GPIO38<'static>,
94        gpio41: GPIO41<'static>,
95        gpio39: GPIO39<'static>,
96        gpio40: GPIO40<'static>,
97        gpio0: GPIO0<'static>,
98        gpio4: GPIO4<'static>,
99    ) -> Self {
100        let controller = ControllerState {
101            a: Input::new(gpio5, InputConfig::default().with_pull(Pull::Up)),
102            b: Input::new(gpio6, InputConfig::default().with_pull(Pull::Up)),
103            c: Input::new(gpio10, InputConfig::default().with_pull(Pull::Up)),
104            d: Input::new(gpio9, InputConfig::default().with_pull(Pull::Up)),
105            up: Input::new(gpio38, InputConfig::default().with_pull(Pull::Up)),
106            down: Input::new(gpio41, InputConfig::default().with_pull(Pull::Up)),
107            left: Input::new(gpio39, InputConfig::default().with_pull(Pull::Up)),
108            right: Input::new(gpio40, InputConfig::default().with_pull(Pull::Up)),
109            select: Input::new(gpio0, InputConfig::default().with_pull(Pull::Up)),
110            start: Input::new(gpio4, InputConfig::default().with_pull(Pull::Up)),
111        };
112        controller
113    }
114}