Skip to content

Commit 0ec07b9

Browse files
authored
Merge pull request #35 from esp-cpp/feature/keypad-input-driver
feat(input): reworked input stack
2 parents 7b478c0 + 6a4ba74 commit 0ec07b9

File tree

11 files changed

+460
-123
lines changed

11 files changed

+460
-123
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

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

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

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

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

239239
## Videos
240240

241+
https://user-images.githubusercontent.com/213467/220791336-eb24116d-0958-4ab7-88bd-f6a5bd6d7eb1.mp4
242+
241243
### Gameboy Color
242244

243245
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.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
idf_component_register(
22
INCLUDE_DIRS "include"
33
SRC_DIRS "src"
4-
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"
4+
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"
55
)

components/box-emu-hal/include/input.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#pragma once
22

3+
#include "lvgl.h"
4+
35
#ifdef __cplusplus
46
extern "C"
57
{
@@ -21,6 +23,7 @@ extern "C"
2123

2224
void init_input();
2325
void get_input_state(struct InputState *state);
26+
lv_indev_t *get_keypad_input_device();
2427
void touchpad_read(unsigned char *num_touch_points, unsigned short* x, unsigned short* y, unsigned char* btn_state);
2528

2629
#ifdef __cplusplus
Lines changed: 129 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,133 @@
1+
#include <mutex>
2+
13
#include "input.h"
24

35
#include "hal_i2c.hpp"
46

5-
#include "task.hpp"
6-
77
#include "mcp23x17.hpp"
8-
8+
#include "timer.hpp"
99
#include "touchpad_input.hpp"
10+
#include "keypad_input.hpp"
1011

1112
using namespace std::chrono_literals;
1213
using namespace box_hal;
1314

15+
struct TouchpadData {
16+
uint8_t num_touch_points = 0;
17+
uint16_t x = 0;
18+
uint16_t y = 0;
19+
uint8_t btn_state = 0;
20+
};
21+
1422
static std::shared_ptr<espp::Mcp23x17> mcp23x17;
1523
static std::shared_ptr<TouchDriver> touch_driver;
1624
static std::shared_ptr<espp::TouchpadInput> touchpad;
25+
static std::shared_ptr<espp::KeypadInput> keypad;
26+
static std::shared_ptr<espp::Timer> input_timer;
27+
static struct InputState gamepad_state;
28+
static std::mutex gamepad_state_mutex;
29+
static TouchpadData touchpad_data;
30+
static std::mutex touchpad_data_mutex;
1731

1832
/**
1933
* Touch Controller configuration
2034
*/
2135
void touchpad_read(uint8_t* num_touch_points, uint16_t* x, uint16_t* y, uint8_t* btn_state) {
22-
*num_touch_points = 0;
36+
std::lock_guard<std::mutex> lock(touchpad_data_mutex);
37+
*num_touch_points = touchpad_data.num_touch_points;
38+
*x = touchpad_data.x;
39+
*y = touchpad_data.y;
40+
*btn_state = touchpad_data.btn_state;
41+
}
42+
43+
void keypad_read(bool *up, bool *down, bool *left, bool *right, bool *enter, bool *escape) {
44+
InputState state;
45+
get_input_state(&state);
46+
*up = state.up;
47+
*down = state.down;
48+
*left = state.left;
49+
*right = state.right;
50+
51+
*enter = state.a || state.start;
52+
*escape = state.b || state.select;
53+
}
54+
55+
void update_touchpad_input() {
2356
// get the latest data from the device
2457
std::error_code ec;
2558
bool new_data = touch_driver->update(ec);
2659
if (ec) {
2760
fmt::print("error updating touch_driver: {}\n", ec.message());
61+
std::lock_guard<std::mutex> lock(touchpad_data_mutex);
62+
touchpad_data = {};
2863
return;
2964
}
3065
if (!new_data) {
66+
std::lock_guard<std::mutex> lock(touchpad_data_mutex);
67+
touchpad_data = {};
3168
return;
3269
}
33-
// now hand it off
34-
touch_driver->get_touch_point(num_touch_points, x, y);
35-
*btn_state = touch_driver->get_home_button_state();
70+
// get the latest data from the touchpad
71+
TouchpadData temp_data;
72+
touch_driver->get_touch_point(&temp_data.num_touch_points, &temp_data.x, &temp_data.y);
73+
temp_data.btn_state = touch_driver->get_home_button_state();
74+
// update the touchpad data
75+
std::lock_guard<std::mutex> lock(touchpad_data_mutex);
76+
touchpad_data = temp_data;
77+
}
78+
79+
void update_gamepad_input() {
80+
bool is_a_pressed = false;
81+
bool is_b_pressed = false;
82+
bool is_x_pressed = false;
83+
bool is_y_pressed = false;
84+
bool is_select_pressed = false;
85+
bool is_start_pressed = false;
86+
bool is_up_pressed = false;
87+
bool is_down_pressed = false;
88+
bool is_left_pressed = false;
89+
bool is_right_pressed = false;
90+
if (!mcp23x17) {
91+
fmt::print("cannot get input state: mcp23x17 not initialized properly!\n");
92+
return;
93+
}
94+
// pins are active low
95+
// start, select = A0, A1
96+
std::error_code ec;
97+
auto a_pins = mcp23x17->get_pins(espp::Mcp23x17::Port::A, ec);
98+
if (ec) {
99+
fmt::print("error getting pins from mcp23x17: {}\n", ec.message());
100+
return;
101+
}
102+
// d-pad, abxy = B0-B3, B4-B7
103+
auto b_pins = mcp23x17->get_pins(espp::Mcp23x17::Port::B, ec);
104+
if (ec) {
105+
fmt::print("error getting pins from mcp23x17: {}\n", ec.message());
106+
return;
107+
}
108+
is_a_pressed = !(b_pins & 1<<4);
109+
is_b_pressed = !(b_pins & 1<<5);
110+
is_x_pressed = !(b_pins & 1<<6);
111+
is_y_pressed = !(b_pins & 1<<7);
112+
is_start_pressed = !(a_pins & 1<<0);
113+
is_select_pressed = !(a_pins & 1<<1);
114+
is_up_pressed = !(b_pins & 1<<0);
115+
is_down_pressed = !(b_pins & 1<<1);
116+
is_left_pressed = !(b_pins & 1<<2);
117+
is_right_pressed = !(b_pins & 1<<3);
118+
{
119+
std::lock_guard<std::mutex> lock(gamepad_state_mutex);
120+
gamepad_state.a = is_a_pressed;
121+
gamepad_state.b = is_b_pressed;
122+
gamepad_state.x = is_x_pressed;
123+
gamepad_state.y = is_y_pressed;
124+
gamepad_state.start = is_start_pressed;
125+
gamepad_state.select = is_select_pressed;
126+
gamepad_state.up = is_up_pressed;
127+
gamepad_state.down = is_down_pressed;
128+
gamepad_state.left = is_left_pressed;
129+
gamepad_state.right = is_right_pressed;
130+
}
36131
}
37132

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

176+
fmt::print("Initializing keypad\n");
177+
keypad = std::make_shared<espp::KeypadInput>(espp::KeypadInput::Config{
178+
.read = keypad_read,
179+
.log_level = espp::Logger::Verbosity::WARN
180+
});
181+
182+
fmt::print("Initializing input task\n");
183+
input_timer = std::make_shared<espp::Timer>(espp::Timer::Config{
184+
.name = "Input timer",
185+
.period = 20ms,
186+
.callback = []() {
187+
update_touchpad_input();
188+
update_gamepad_input();
189+
return false;
190+
},
191+
.log_level = espp::Logger::Verbosity::WARN});
192+
81193
initialized = true;
82194
}
83195

84-
extern "C" void get_input_state(struct InputState* state) {
85-
bool is_a_pressed = false;
86-
bool is_b_pressed = false;
87-
bool is_x_pressed = false;
88-
bool is_y_pressed = false;
89-
bool is_select_pressed = false;
90-
bool is_start_pressed = false;
91-
bool is_up_pressed = false;
92-
bool is_down_pressed = false;
93-
bool is_left_pressed = false;
94-
bool is_right_pressed = false;
95-
if (!mcp23x17) {
96-
fmt::print("cannot get input state: mcp23x17 not initialized properly!\n");
97-
return;
196+
extern "C" lv_indev_t *get_keypad_input_device() {
197+
if (!keypad) {
198+
fmt::print("cannot get keypad input device: keypad not initialized properly!\n");
199+
return nullptr;
98200
}
99-
// pins are active low
100-
// start, select = A0, A1
101-
std::error_code ec;
102-
auto a_pins = mcp23x17->get_pins(espp::Mcp23x17::Port::A, ec);
103-
if (ec) {
104-
fmt::print("error getting pins from mcp23x17: {}\n", ec.message());
105-
return;
106-
}
107-
// d-pad, abxy = B0-B3, B4-B7
108-
auto b_pins = mcp23x17->get_pins(espp::Mcp23x17::Port::B, ec);
109-
if (ec) {
110-
fmt::print("error getting pins from mcp23x17: {}\n", ec.message());
111-
return;
112-
}
113-
is_a_pressed = !(b_pins & 1<<4);
114-
is_b_pressed = !(b_pins & 1<<5);
115-
is_x_pressed = !(b_pins & 1<<6);
116-
is_y_pressed = !(b_pins & 1<<7);
117-
is_start_pressed = !(a_pins & 1<<0);
118-
is_select_pressed = !(a_pins & 1<<1);
119-
is_up_pressed = !(b_pins & 1<<0);
120-
is_down_pressed = !(b_pins & 1<<1);
121-
is_left_pressed = !(b_pins & 1<<2);
122-
is_right_pressed = !(b_pins & 1<<3);
123-
state->a = is_a_pressed;
124-
state->b = is_b_pressed;
125-
state->x = is_x_pressed;
126-
state->y = is_y_pressed;
127-
state->start = is_start_pressed;
128-
state->select = is_select_pressed;
129-
state->up = is_up_pressed;
130-
state->down = is_down_pressed;
131-
state->left = is_left_pressed;
132-
state->right = is_right_pressed;
201+
return keypad->get_input_device();
202+
}
203+
204+
extern "C" void get_input_state(struct InputState* state) {
205+
std::lock_guard<std::mutex> lock(gamepad_state_mutex);
206+
*state = gamepad_state;
133207
}

components/espp

Submodule espp updated 106 files

components/gui/include/gui.hpp

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "task.hpp"
1111
#include "logger.hpp"
1212

13+
#include "input.h"
1314
#include "hal_events.hpp"
1415
#include "i2s_audio.h"
1516
#include "video_setting.hpp"
@@ -82,40 +83,14 @@ class Gui {
8283

8384
void pause() {
8485
paused_ = true;
86+
freeze_focus();
8587
}
8688
void resume() {
8789
update_shared_state();
8890
paused_ = false;
91+
focus_rommenu();
8992
}
9093

91-
void next() {
92-
// protect since this function is called from another thread context
93-
std::lock_guard<std::recursive_mutex> lk(mutex_);
94-
if (roms_.size() == 0) {
95-
return;
96-
}
97-
// focus the next rom
98-
focused_rom_++;
99-
if (focused_rom_ >= roms_.size()) focused_rom_ = 0;
100-
auto rom = roms_[focused_rom_];
101-
focus_rom(rom);
102-
}
103-
104-
void previous() {
105-
// protect since this function is called from another thread context
106-
std::lock_guard<std::recursive_mutex> lk(mutex_);
107-
if (roms_.size() == 0) {
108-
return;
109-
}
110-
// focus the previous rom
111-
focused_rom_--;
112-
if (focused_rom_ < 0) focused_rom_ = roms_.size() - 1;
113-
auto rom = roms_[focused_rom_];
114-
focus_rom(rom);
115-
}
116-
117-
void focus_rom(lv_obj_t *new_focus, bool scroll_to_view=true);
118-
11994
void set_haptic_waveform(int new_waveform) {
12095
if (new_waveform > 123) {
12196
new_waveform = 1;
@@ -141,7 +116,12 @@ class Gui {
141116
void init_ui();
142117
void deinit_ui();
143118

119+
void freeze_focus();
120+
void focus_rommenu();
121+
void focus_settings();
122+
144123
void load_rom_screen();
124+
void load_settings_screen();
145125

146126
void update_shared_state() {
147127
set_mute(is_muted());
@@ -151,6 +131,8 @@ class Gui {
151131

152132
VideoSetting get_video_setting();
153133

134+
void on_rom_focused(lv_obj_t *new_focus);
135+
154136
void on_mute_button_pressed(const std::vector<uint8_t>& data) {
155137
set_mute(is_muted());
156138
}
@@ -203,6 +185,7 @@ class Gui {
203185
case LV_EVENT_SHORT_CLICKED:
204186
break;
205187
case LV_EVENT_PRESSED:
188+
case LV_EVENT_CLICKED:
206189
gui->on_pressed(e);
207190
break;
208191
case LV_EVENT_VALUE_CHANGED:
@@ -211,6 +194,10 @@ class Gui {
211194
case LV_EVENT_LONG_PRESSED:
212195
break;
213196
case LV_EVENT_KEY:
197+
gui->on_key(e);
198+
break;
199+
case LV_EVENT_FOCUSED:
200+
gui->on_rom_focused(lv_event_get_target(e));
214201
break;
215202
default:
216203
break;
@@ -219,16 +206,23 @@ class Gui {
219206

220207
void on_pressed(lv_event_t *e);
221208
void on_value_changed(lv_event_t *e);
209+
void on_key(lv_event_t *e);
222210

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

217+
// style for buttons
218+
lv_style_t button_style_;
219+
229220
lv_anim_t rom_label_animation_template_;
230221
lv_style_t rom_label_style_;
231222

223+
lv_group_t *rom_screen_group_;
224+
lv_group_t *settings_screen_group_;
225+
232226
Jpeg decoder_;
233227

234228
play_haptic_fn play_haptic_;

0 commit comments

Comments
 (0)