blk_box_tc/main/drivers/inputs.hpp
2026-04-10 12:19:28 -05:00

275 lines
7.1 KiB
C++

#ifndef INPUTS_H
#define INPUTS_H
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
#include <freertos/semphr.h>
#include <optional>
#define EXPANDER_I2C_ADDR (0x7E)
#define EXPANDER_I2C_SPEED (400000)
// the actual transaction takes ~0.3ms, but for some reason a timout of ~10 or lower causes issues.
#define EXPANDER_TIMEOUT_MS (100)
#define EXPANDER_WHOAMI_VALUE (0x85)
// queue sizes
#define EXPANDER_EVENT_QUEUE_SIZE 4
#define EXPANDER_KEYPAD_QUEUE_SIZE 16
void init_expander();
/// The four buttons on the bottom half.
enum class Button: uint8_t {
B1 = 0,
B2 = 1,
B3 = 2,
B4 = 3,
GREEN = 0,
RED = 1,
YELLOW = 2,
BLUE = 3,
};
constexpr uint8_t raw_value(Button v) { return static_cast<uint8_t>(v); }
constexpr Button button_from_raw(uint8_t raw) { return static_cast<Button>(raw & 0b11); }
/// The four switches on the bottom half.
enum class Switch: uint8_t {
S1 = 0,
S2 = 1,
S3 = 2,
S4 = 3,
};
constexpr uint8_t raw_value(Switch v) { return static_cast<uint8_t>(v); }
constexpr Switch switch_from_raw(uint8_t raw) { return static_cast<Switch>(raw & 0b11); }
enum class TouchedReleased: uint8_t {
Released = 0,
Touched = 1,
};
constexpr uint8_t raw_value(TouchedReleased v) { return static_cast<uint8_t>(v); }
constexpr TouchedReleased touched_released_from_raw(uint8_t raw) { return static_cast<TouchedReleased>(raw & 0b1); }
/// One of the keys on the keypad.
enum class InputKeypadKey: uint8_t {
K0 = 0,
K1 = 1,
K2 = 2,
K3 = 3,
K4 = 4,
K5 = 5,
K6 = 6,
K7 = 7,
K8 = 8,
K9 = 9,
A = 10,
B = 11,
C = 12,
D = 13,
STAR = 14,
POUND = 15,
};
constexpr uint8_t raw_value(InputKeypadKey v) { return static_cast<uint8_t>(v); }
constexpr InputKeypadKey keypad_key_from_raw(uint8_t raw) { return static_cast<InputKeypadKey>(raw & 0b1111); }
constexpr char keypad_key_to_char(InputKeypadKey key) {
static constexpr char lookup[16] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', '*', '#'
};
return lookup[static_cast<uint8_t>(key) & 0b1111];
}
struct SwitchFlip {
private:
// [bit2: up] [bit1-0: switch]
uint8_t data;
public:
// Constructor
SwitchFlip(Switch sw, bool up)
: data((static_cast<uint8_t>(sw) & 0b11) |
((up ? 1 : 0) << 2)) {}
// Default constructor
SwitchFlip() : data(0) {}
// Raw value constructor
explicit SwitchFlip(uint8_t raw_data) : data(raw_data) {}
// Raw value getter
uint8_t raw() const { return data; }
// Getters
Switch get_switch() const {
return static_cast<Switch>(data & 0b11);
}
bool is_up() const {
return (data >> 2) & 1;
}
// Setters
void set_switch(Switch sw) {
data = (data & ~0b11) | (static_cast<uint8_t>(sw) & 0b11);
}
void set_up(bool up) {
data = (data & ~(1 << 2)) | ((up ? 1 : 0) << 2);
}
};
static_assert(sizeof(SwitchFlip) == 1);
struct SwitchTouch {
private:
// [bit2: touched] [bit1-0: switch]
uint8_t data;
public:
// Constructor
SwitchTouch(Switch sw, bool touched)
: data((static_cast<uint8_t>(sw) & 0b11) |
((touched ? 1 : 0) << 2)) {}
// Default constructor
SwitchTouch() : data(0) {}
// Raw value constructor
explicit SwitchTouch(uint8_t raw_data) : data(raw_data) {}
// Raw value getter
uint8_t raw() const { return data; }
// Getters
Switch get_switch() const {
return static_cast<Switch>(data & 0b11);
}
bool is_touched() const {
return (data >> 2) & 1;
}
// Setters
void set_switch(Switch sw) {
data = (data & ~0b11) | (static_cast<uint8_t>(sw) & 0b11);
}
void set_touched(bool touched) {
data = (data & ~(1 << 2)) | ((touched ? 1 : 0) << 2);
}
};
static_assert(sizeof(SwitchTouch) == 1);
struct ButtonOrSwitch {
private:
// [bit2: is_switch] [bit1-0: number]
uint8_t data;
public:
// Constructor
ButtonOrSwitch(uint8_t number, bool is_switch)
: data((number & 0b11) |
((is_switch ? 1 : 0) << 2)) {}
ButtonOrSwitch() : data(0) {}
// Raw value constructor
explicit ButtonOrSwitch(uint8_t raw_data) : data(raw_data) {}
// Raw value getter
uint8_t raw() const { return data; }
// Getters
uint8_t number() const {
return data & 0b11;
}
bool is_switch() const {
return (data >> 2) & 1;
}
// Setters
void set_number(uint8_t number) {
data = (data & ~0b11) | (number & 0b11);
}
void set_is_switch(bool is_switch) {
data = (data & ~(1 << 2)) | ((is_switch ? 1 : 0) << 2);
}
};
static_assert(sizeof(ButtonOrSwitch) == 1);
/// @brief The state of the bottom half of the box.
struct InputsState {
/// The touch state of the switches in the lower 4 bits.
/// The touch pad state in bit 4.
uint8_t touch_state;
/// The current state of the buttons in the lower 4 bits.
uint8_t button_state;
/// The current state of the switches. Up switches are stored
/// in the lower 4 bits, switches that are down are stored in
/// the upper 4 bits. If switches are in the middle, the
/// corresponding bit will be `0` in the upper and lower 4.
uint8_t switch_state;
/// The state of the keypad.
uint16_t keypad_state;
/// The sensitivity of the `hal` value to auto update.
uint16_t hal_sense;
/// The sensitivity of the `close_hal` value to auto update.
uint16_t close_hal_sense;
/// A non-exact hal value reading.
/// This only gets updated when it changes by `hal_sense`
uint16_t hal;
/// A non-exact hal value reading.
/// This only gets updated when it changes by `close_hal_sense`
uint16_t close_hal;
/// The RFID card that was presented last.
uint32_t rfid_state;
InputsState() : touch_state(0), button_state(0), switch_state(0), keypad_state(0), hal_sense(0), close_hal_sense(0), hal(0), close_hal(0), rfid_state(0) {}
};
class InputsController {
public:
static void clear_all_events();
static InputsState get_input_state();
static bool has_button_press();
static std::optional<Button> get_button_press();
static Button wait_button_press();
static uint8_t button_state();
static bool has_button_release();
static std::optional<Button> get_button_release();
static Button wait_button_release();
static bool has_switch_flip();
static std::optional<SwitchFlip> get_switch_flip();
static SwitchFlip wait_switch_flip();
static uint8_t switch_state();
static bool has_switch_touch();
static std::optional<SwitchTouch> get_switch_touch();
static SwitchTouch wait_switch_touch();
static uint8_t switch_touch_state();
static bool has_keypad_press();
static std::optional<InputKeypadKey> get_keypad_press();
static InputKeypadKey wait_keypad_press();
static bool has_keypad_release();
static std::optional<InputKeypadKey> get_keypad_release();
static InputKeypadKey wait_keypad_release();
static uint16_t keypad_state();
// TODO: impl and add the hal and RFID stuff
};
#endif // INPUTS_H