Skip to content

Commit

Permalink
Merge pull request #35 from esp-cpp/feature/keypad-input-driver
Browse files Browse the repository at this point in the history
feat(input): reworked input stack
  • Loading branch information
finger563 authored Nov 12, 2023
2 parents 7b478c0 + 6a4ba74 commit 0ec07b9
Show file tree
Hide file tree
Showing 11 changed files with 460 additions and 123 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

Emulator(s) running on ESP BOX with custom pcb and 3d printed enclosure :)

https://user-images.githubusercontent.com/213467/236730090-56c3bd64-86e4-4b9b-a909-0b363fab4fc6.mp4
https://github.com/esp-cpp/esp-box-emu/assets/213467/3b77f6bd-4c42-417a-9eb7-a648f31b4008

https://user-images.githubusercontent.com/213467/220791336-eb24116d-0958-4ab7-88bd-f6a5bd6d7eb1.mp4
https://user-images.githubusercontent.com/213467/236730090-56c3bd64-86e4-4b9b-a909-0b363fab4fc6.mp4

As of https://github.com/esp-cpp/esp-box-emu/pull/34 I am starting to add
initial support for the new ESP32-S3-BOX-3.
Expand Down Expand Up @@ -238,6 +238,8 @@ I2C Pinout (shared with touchscreen chip above):

## Videos

https://user-images.githubusercontent.com/213467/220791336-eb24116d-0958-4ab7-88bd-f6a5bd6d7eb1.mp4

### Gameboy Color

This video shows settings page (with audio control and video scaling control), and then Links Awakening DX. While running the ROMs, the video scaling can be toggled through the three options (Original, Fit, and Fill) using the BOOT button on the side of the ESP-S3-BOX and the audio output can be toggled on / off using the MUTE button on the top.
Expand Down
2 changes: 1 addition & 1 deletion components/box-emu-hal/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
idf_component_register(
INCLUDE_DIRS "include"
SRC_DIRS "src"
REQUIRES "driver" "heap" "fatfs" "esp_lcd" "esp_psram" "spi_flash" "nvs_flash" "codec" "display" "display_drivers" "mcp23x17" "input_drivers" "tt21100" "gt911" "drv2605" "event_manager" "i2c"
REQUIRES "driver" "heap" "fatfs" "esp_lcd" "esp_psram" "spi_flash" "nvs_flash" "codec" "display" "display_drivers" "mcp23x17" "input_drivers" "tt21100" "gt911" "drv2605" "event_manager" "i2c" "task" "timer"
)
3 changes: 3 additions & 0 deletions components/box-emu-hal/include/input.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include "lvgl.h"

#ifdef __cplusplus
extern "C"
{
Expand All @@ -21,6 +23,7 @@ extern "C"

void init_input();
void get_input_state(struct InputState *state);
lv_indev_t *get_keypad_input_device();
void touchpad_read(unsigned char *num_touch_points, unsigned short* x, unsigned short* y, unsigned char* btn_state);

#ifdef __cplusplus
Expand Down
184 changes: 129 additions & 55 deletions components/box-emu-hal/src/input.cpp
Original file line number Diff line number Diff line change
@@ -1,38 +1,133 @@
#include <mutex>

#include "input.h"

#include "hal_i2c.hpp"

#include "task.hpp"

#include "mcp23x17.hpp"

#include "timer.hpp"
#include "touchpad_input.hpp"
#include "keypad_input.hpp"

using namespace std::chrono_literals;
using namespace box_hal;

struct TouchpadData {
uint8_t num_touch_points = 0;
uint16_t x = 0;
uint16_t y = 0;
uint8_t btn_state = 0;
};

static std::shared_ptr<espp::Mcp23x17> mcp23x17;
static std::shared_ptr<TouchDriver> touch_driver;
static std::shared_ptr<espp::TouchpadInput> touchpad;
static std::shared_ptr<espp::KeypadInput> keypad;
static std::shared_ptr<espp::Timer> input_timer;
static struct InputState gamepad_state;
static std::mutex gamepad_state_mutex;
static TouchpadData touchpad_data;
static std::mutex touchpad_data_mutex;

/**
* Touch Controller configuration
*/
void touchpad_read(uint8_t* num_touch_points, uint16_t* x, uint16_t* y, uint8_t* btn_state) {
*num_touch_points = 0;
std::lock_guard<std::mutex> lock(touchpad_data_mutex);
*num_touch_points = touchpad_data.num_touch_points;
*x = touchpad_data.x;
*y = touchpad_data.y;
*btn_state = touchpad_data.btn_state;
}

void keypad_read(bool *up, bool *down, bool *left, bool *right, bool *enter, bool *escape) {
InputState state;
get_input_state(&state);
*up = state.up;
*down = state.down;
*left = state.left;
*right = state.right;

*enter = state.a || state.start;
*escape = state.b || state.select;
}

void update_touchpad_input() {
// get the latest data from the device
std::error_code ec;
bool new_data = touch_driver->update(ec);
if (ec) {
fmt::print("error updating touch_driver: {}\n", ec.message());
std::lock_guard<std::mutex> lock(touchpad_data_mutex);
touchpad_data = {};
return;
}
if (!new_data) {
std::lock_guard<std::mutex> lock(touchpad_data_mutex);
touchpad_data = {};
return;
}
// now hand it off
touch_driver->get_touch_point(num_touch_points, x, y);
*btn_state = touch_driver->get_home_button_state();
// get the latest data from the touchpad
TouchpadData temp_data;
touch_driver->get_touch_point(&temp_data.num_touch_points, &temp_data.x, &temp_data.y);
temp_data.btn_state = touch_driver->get_home_button_state();
// update the touchpad data
std::lock_guard<std::mutex> lock(touchpad_data_mutex);
touchpad_data = temp_data;
}

void update_gamepad_input() {
bool is_a_pressed = false;
bool is_b_pressed = false;
bool is_x_pressed = false;
bool is_y_pressed = false;
bool is_select_pressed = false;
bool is_start_pressed = false;
bool is_up_pressed = false;
bool is_down_pressed = false;
bool is_left_pressed = false;
bool is_right_pressed = false;
if (!mcp23x17) {
fmt::print("cannot get input state: mcp23x17 not initialized properly!\n");
return;
}
// pins are active low
// start, select = A0, A1
std::error_code ec;
auto a_pins = mcp23x17->get_pins(espp::Mcp23x17::Port::A, ec);
if (ec) {
fmt::print("error getting pins from mcp23x17: {}\n", ec.message());
return;
}
// d-pad, abxy = B0-B3, B4-B7
auto b_pins = mcp23x17->get_pins(espp::Mcp23x17::Port::B, ec);
if (ec) {
fmt::print("error getting pins from mcp23x17: {}\n", ec.message());
return;
}
is_a_pressed = !(b_pins & 1<<4);
is_b_pressed = !(b_pins & 1<<5);
is_x_pressed = !(b_pins & 1<<6);
is_y_pressed = !(b_pins & 1<<7);
is_start_pressed = !(a_pins & 1<<0);
is_select_pressed = !(a_pins & 1<<1);
is_up_pressed = !(b_pins & 1<<0);
is_down_pressed = !(b_pins & 1<<1);
is_left_pressed = !(b_pins & 1<<2);
is_right_pressed = !(b_pins & 1<<3);
{
std::lock_guard<std::mutex> lock(gamepad_state_mutex);
gamepad_state.a = is_a_pressed;
gamepad_state.b = is_b_pressed;
gamepad_state.x = is_x_pressed;
gamepad_state.y = is_y_pressed;
gamepad_state.start = is_start_pressed;
gamepad_state.select = is_select_pressed;
gamepad_state.up = is_up_pressed;
gamepad_state.down = is_down_pressed;
gamepad_state.left = is_left_pressed;
gamepad_state.right = is_right_pressed;
}
}

static std::atomic<bool> initialized = false;
Expand Down Expand Up @@ -78,56 +173,35 @@ void init_input() {
.log_level = espp::Logger::Verbosity::WARN
});

fmt::print("Initializing keypad\n");
keypad = std::make_shared<espp::KeypadInput>(espp::KeypadInput::Config{
.read = keypad_read,
.log_level = espp::Logger::Verbosity::WARN
});

fmt::print("Initializing input task\n");
input_timer = std::make_shared<espp::Timer>(espp::Timer::Config{
.name = "Input timer",
.period = 20ms,
.callback = []() {
update_touchpad_input();
update_gamepad_input();
return false;
},
.log_level = espp::Logger::Verbosity::WARN});

initialized = true;
}

extern "C" void get_input_state(struct InputState* state) {
bool is_a_pressed = false;
bool is_b_pressed = false;
bool is_x_pressed = false;
bool is_y_pressed = false;
bool is_select_pressed = false;
bool is_start_pressed = false;
bool is_up_pressed = false;
bool is_down_pressed = false;
bool is_left_pressed = false;
bool is_right_pressed = false;
if (!mcp23x17) {
fmt::print("cannot get input state: mcp23x17 not initialized properly!\n");
return;
extern "C" lv_indev_t *get_keypad_input_device() {
if (!keypad) {
fmt::print("cannot get keypad input device: keypad not initialized properly!\n");
return nullptr;
}
// pins are active low
// start, select = A0, A1
std::error_code ec;
auto a_pins = mcp23x17->get_pins(espp::Mcp23x17::Port::A, ec);
if (ec) {
fmt::print("error getting pins from mcp23x17: {}\n", ec.message());
return;
}
// d-pad, abxy = B0-B3, B4-B7
auto b_pins = mcp23x17->get_pins(espp::Mcp23x17::Port::B, ec);
if (ec) {
fmt::print("error getting pins from mcp23x17: {}\n", ec.message());
return;
}
is_a_pressed = !(b_pins & 1<<4);
is_b_pressed = !(b_pins & 1<<5);
is_x_pressed = !(b_pins & 1<<6);
is_y_pressed = !(b_pins & 1<<7);
is_start_pressed = !(a_pins & 1<<0);
is_select_pressed = !(a_pins & 1<<1);
is_up_pressed = !(b_pins & 1<<0);
is_down_pressed = !(b_pins & 1<<1);
is_left_pressed = !(b_pins & 1<<2);
is_right_pressed = !(b_pins & 1<<3);
state->a = is_a_pressed;
state->b = is_b_pressed;
state->x = is_x_pressed;
state->y = is_y_pressed;
state->start = is_start_pressed;
state->select = is_select_pressed;
state->up = is_up_pressed;
state->down = is_down_pressed;
state->left = is_left_pressed;
state->right = is_right_pressed;
return keypad->get_input_device();
}

extern "C" void get_input_state(struct InputState* state) {
std::lock_guard<std::mutex> lock(gamepad_state_mutex);
*state = gamepad_state;
}
2 changes: 1 addition & 1 deletion components/espp
Submodule espp updated 106 files
50 changes: 22 additions & 28 deletions components/gui/include/gui.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "task.hpp"
#include "logger.hpp"

#include "input.h"
#include "hal_events.hpp"
#include "i2s_audio.h"
#include "video_setting.hpp"
Expand Down Expand Up @@ -82,40 +83,14 @@ class Gui {

void pause() {
paused_ = true;
freeze_focus();
}
void resume() {
update_shared_state();
paused_ = false;
focus_rommenu();
}

void next() {
// protect since this function is called from another thread context
std::lock_guard<std::recursive_mutex> lk(mutex_);
if (roms_.size() == 0) {
return;
}
// focus the next rom
focused_rom_++;
if (focused_rom_ >= roms_.size()) focused_rom_ = 0;
auto rom = roms_[focused_rom_];
focus_rom(rom);
}

void previous() {
// protect since this function is called from another thread context
std::lock_guard<std::recursive_mutex> lk(mutex_);
if (roms_.size() == 0) {
return;
}
// focus the previous rom
focused_rom_--;
if (focused_rom_ < 0) focused_rom_ = roms_.size() - 1;
auto rom = roms_[focused_rom_];
focus_rom(rom);
}

void focus_rom(lv_obj_t *new_focus, bool scroll_to_view=true);

void set_haptic_waveform(int new_waveform) {
if (new_waveform > 123) {
new_waveform = 1;
Expand All @@ -141,7 +116,12 @@ class Gui {
void init_ui();
void deinit_ui();

void freeze_focus();
void focus_rommenu();
void focus_settings();

void load_rom_screen();
void load_settings_screen();

void update_shared_state() {
set_mute(is_muted());
Expand All @@ -151,6 +131,8 @@ class Gui {

VideoSetting get_video_setting();

void on_rom_focused(lv_obj_t *new_focus);

void on_mute_button_pressed(const std::vector<uint8_t>& data) {
set_mute(is_muted());
}
Expand Down Expand Up @@ -203,6 +185,7 @@ class Gui {
case LV_EVENT_SHORT_CLICKED:
break;
case LV_EVENT_PRESSED:
case LV_EVENT_CLICKED:
gui->on_pressed(e);
break;
case LV_EVENT_VALUE_CHANGED:
Expand All @@ -211,6 +194,10 @@ class Gui {
case LV_EVENT_LONG_PRESSED:
break;
case LV_EVENT_KEY:
gui->on_key(e);
break;
case LV_EVENT_FOCUSED:
gui->on_rom_focused(lv_event_get_target(e));
break;
default:
break;
Expand All @@ -219,16 +206,23 @@ class Gui {

void on_pressed(lv_event_t *e);
void on_value_changed(lv_event_t *e);
void on_key(lv_event_t *e);

// LVLG gui objects
std::vector<std::string> boxart_paths_;
std::vector<lv_obj_t*> roms_;
std::atomic<int> focused_rom_{-1};
lv_img_dsc_t focused_boxart_;

// style for buttons
lv_style_t button_style_;

lv_anim_t rom_label_animation_template_;
lv_style_t rom_label_style_;

lv_group_t *rom_screen_group_;
lv_group_t *settings_screen_group_;

Jpeg decoder_;

play_haptic_fn play_haptic_;
Expand Down
Loading

0 comments on commit 0ec07b9

Please sign in to comment.