tweak wheel sensitivity, improve AKS for centre button

custom
jacqueline 1 year ago
parent 09eacb71f0
commit 44fdf69674
  1. 2
      src/drivers/relative_wheel.cpp
  2. 6
      src/drivers/touchwheel.cpp
  3. 102
      src/ui/encoder_input.cpp
  4. 4
      src/ui/include/encoder_input.hpp

@ -47,7 +47,7 @@ auto RelativeWheel::Update() -> void {
int delta = 128 - last_angle_; int delta = 128 - last_angle_;
uint8_t rotated_angle = new_angle + delta; uint8_t rotated_angle = new_angle + delta;
int threshold = 15; int threshold = 10;
if (rotated_angle < 128 - threshold) { if (rotated_angle < 128 - threshold) {
ticks_ = 1; ticks_ = 1;

@ -49,8 +49,8 @@ TouchWheel::TouchWheel() {
WriteRegister(Register::KEY_CONTROL_BASE + 0, 0b100); WriteRegister(Register::KEY_CONTROL_BASE + 0, 0b100);
WriteRegister(Register::KEY_CONTROL_BASE + 1, 0b100); WriteRegister(Register::KEY_CONTROL_BASE + 1, 0b100);
WriteRegister(Register::KEY_CONTROL_BASE + 2, 0b100); WriteRegister(Register::KEY_CONTROL_BASE + 2, 0b100);
// Centre button. Also channel 1. // Centre button. No AKS channel, since we handle it in software.
WriteRegister(Register::KEY_CONTROL_BASE + 3, 0b100); WriteRegister(Register::KEY_CONTROL_BASE + 3, 0b0);
// Touch guard. Set as a guard, in channel 1. // Touch guard. Set as a guard, in channel 1.
WriteRegister(Register::KEY_CONTROL_BASE + 4, 0b10100); WriteRegister(Register::KEY_CONTROL_BASE + 4, 0b10100);
@ -58,6 +58,8 @@ TouchWheel::TouchWheel() {
// so that the user's finger isn't calibrated away. // so that the user's finger isn't calibrated away.
WriteRegister(Register::RECALIBRATION_DELAY, 0); WriteRegister(Register::RECALIBRATION_DELAY, 0);
WriteRegister(Register::CHARGE_TIME, 0x10);
// Unused extra keys. All disabled. // Unused extra keys. All disabled.
for (int i = 5; i < 12; i++) { for (int i = 5; i < 12; i++) {
WriteRegister(Register::KEY_CONTROL_BASE + i, 1); WriteRegister(Register::KEY_CONTROL_BASE + i, 1);

@ -25,7 +25,7 @@
[[maybe_unused]] static constexpr char kTag[] = "input"; [[maybe_unused]] static constexpr char kTag[] = "input";
constexpr int kDPadAngleThreshold = 20; constexpr int kDPadAngleThreshold = 10;
constexpr int kLongPressDelayMs = 500; constexpr int kLongPressDelayMs = 500;
constexpr int kRepeatDelayMs = 250; constexpr int kRepeatDelayMs = 250;
@ -49,7 +49,8 @@ EncoderInput::EncoderInput(drivers::IGpios& gpios, drivers::TouchWheel& wheel)
relative_wheel_(std::make_unique<drivers::RelativeWheel>(wheel)), relative_wheel_(std::make_unique<drivers::RelativeWheel>(wheel)),
scroller_(std::make_unique<Scroller>()), scroller_(std::make_unique<Scroller>()),
mode_(drivers::NvsStorage::InputModes::kRotatingWheel), mode_(drivers::NvsStorage::InputModes::kRotatingWheel),
is_locked_(false) { is_locked_(false),
is_scrolling_wheel_(false) {
lv_indev_drv_init(&driver_); lv_indev_drv_init(&driver_);
driver_.type = LV_INDEV_TYPE_ENCODER; driver_.type = LV_INDEV_TYPE_ENCODER;
driver_.read_cb = encoder_read; driver_.read_cb = encoder_read;
@ -70,7 +71,7 @@ auto EncoderInput::Read(lv_indev_data_t* data) -> void {
raw_wheel_.Update(); raw_wheel_.Update();
relative_wheel_->Update(); relative_wheel_->Update();
// GPIOs updating is handled by system_fsm. // GPIO (for volume buttons) updating is handled by system_fsm.
uint64_t now_ms = esp_timer_get_time() / 1000; uint64_t now_ms = esp_timer_get_time() / 1000;
@ -83,36 +84,50 @@ auto EncoderInput::Read(lv_indev_data_t* data) -> void {
} }
// Check each button. // Check each button.
HandleKeyState(Keys::kVolumeUp, now_ms, UpdateKeyState(Keys::kVolumeUp, now_ms,
!gpios_.Get(drivers::IGpios::Pin::kKeyUp)); !gpios_.Get(drivers::IGpios::Pin::kKeyUp));
HandleKeyState(Keys::kVolumeDown, now_ms, UpdateKeyState(Keys::kVolumeDown, now_ms,
!gpios_.Get(drivers::IGpios::Pin::kKeyDown)); !gpios_.Get(drivers::IGpios::Pin::kKeyDown));
drivers::TouchWheelData wheel_data = raw_wheel_.GetTouchWheelData(); drivers::TouchWheelData wheel_data = raw_wheel_.GetTouchWheelData();
HandleKeyState(Keys::kTouchWheel, now_ms, wheel_data.is_wheel_touched); UpdateKeyState(Keys::kTouchWheel, now_ms, wheel_data.is_wheel_touched);
HandleKeyState(Keys::kTouchWheelCenter, now_ms, wheel_data.is_button_touched); UpdateKeyState(Keys::kTouchWheelCenter, now_ms, wheel_data.is_button_touched);
HandleKeyState( UpdateKeyState(
Keys::kDirectionalUp, now_ms, Keys::kDirectionalUp, now_ms,
wheel_data.is_wheel_touched && wheel_data.is_wheel_touched &&
IsAngleWithin(wheel_data.wheel_position, 0, kDPadAngleThreshold)); IsAngleWithin(wheel_data.wheel_position, 0, kDPadAngleThreshold));
HandleKeyState( UpdateKeyState(
Keys::kDirectionalLeft, now_ms, Keys::kDirectionalLeft, now_ms,
wheel_data.is_wheel_touched && wheel_data.is_wheel_touched &&
IsAngleWithin(wheel_data.wheel_position, 63, kDPadAngleThreshold)); IsAngleWithin(wheel_data.wheel_position, 63, kDPadAngleThreshold));
HandleKeyState( UpdateKeyState(
Keys::kDirectionalDown, now_ms, Keys::kDirectionalDown, now_ms,
wheel_data.is_wheel_touched && wheel_data.is_wheel_touched &&
IsAngleWithin(wheel_data.wheel_position, 127, kDPadAngleThreshold)); IsAngleWithin(wheel_data.wheel_position, 127, kDPadAngleThreshold));
HandleKeyState( UpdateKeyState(
Keys::kDirectionalRight, now_ms, Keys::kDirectionalRight, now_ms,
wheel_data.is_wheel_touched && wheel_data.is_wheel_touched &&
IsAngleWithin(wheel_data.wheel_position, 189, kDPadAngleThreshold)); IsAngleWithin(wheel_data.wheel_position, 189, kDPadAngleThreshold));
// We now have enough information to give LVGL its update. // When the wheel is being scrolled, we want to ensure that other inputs
// involving the touchwheel don't trigger. This guards again two main issues:
// - hesitating when your thumb is on a cardinal direction, causing an
// unintentional long-press,
// - drifting from the outside of the wheel in a way that causes the centre
// key to be triggered.
if (is_scrolling_wheel_) {
UpdateKeyState(Keys::kTouchWheelCenter, now_ms, false);
UpdateKeyState(Keys::kDirectionalUp, now_ms, false);
UpdateKeyState(Keys::kDirectionalLeft, now_ms, false);
UpdateKeyState(Keys::kDirectionalDown, now_ms, false);
UpdateKeyState(Keys::kDirectionalRight, now_ms, false);
}
// Now that we've determined the correct state for all keys, we can start
// mapping key states into actions, depending on the current control scheme.
if (mode_ == drivers::NvsStorage::InputModes::kButtonsOnly) {
Trigger trigger; Trigger trigger;
switch (mode_) {
case drivers::NvsStorage::InputModes::kButtonsOnly:
data->state = LV_INDEV_STATE_RELEASED; data->state = LV_INDEV_STATE_RELEASED;
trigger = TriggerKey(Keys::kVolumeUp, KeyStyle::kLongPress, now_ms); trigger = TriggerKey(Keys::kVolumeUp, KeyStyle::kLongPress, now_ms);
@ -138,33 +153,9 @@ auto EncoderInput::Read(lv_indev_data_t* data) -> void {
data->state = LV_INDEV_STATE_PRESSED; data->state = LV_INDEV_STATE_PRESSED;
break; break;
} }
} else if (mode_ == drivers::NvsStorage::InputModes::kDirectionalWheel) {
break; Trigger trigger;
case drivers::NvsStorage::InputModes::kButtonsWithWheel: trigger = TriggerKey(Keys::kTouchWheelCenter, KeyStyle::kLongPress, now_ms);
trigger = TriggerKey(Keys::kTouchWheel, KeyStyle::kLongPress, now_ms);
data->state = trigger == Trigger::kClick ? LV_INDEV_STATE_PRESSED
: LV_INDEV_STATE_RELEASED;
trigger = TriggerKey(Keys::kVolumeUp, KeyStyle::kRepeat, now_ms);
if (trigger == Trigger::kClick) {
data->enc_diff = scroller_->AddInput(now_ms, -1);
}
trigger = TriggerKey(Keys::kVolumeDown, KeyStyle::kRepeat, now_ms);
if (trigger == Trigger::kClick) {
data->enc_diff = scroller_->AddInput(now_ms, 1);
}
// Cancel scrolling if the buttons are released.
if (!touch_time_ms_.contains(Keys::kVolumeDown) &&
!touch_time_ms_.contains(Keys::kVolumeUp)) {
data->enc_diff = scroller_->AddInput(now_ms, 0);
}
break;
case drivers::NvsStorage::InputModes::kDirectionalWheel:
trigger =
TriggerKey(Keys::kTouchWheelCenter, KeyStyle::kLongPress, now_ms);
data->state = trigger == Trigger::kClick ? LV_INDEV_STATE_PRESSED data->state = trigger == Trigger::kClick ? LV_INDEV_STATE_PRESSED
: LV_INDEV_STATE_RELEASED; : LV_INDEV_STATE_RELEASED;
@ -215,18 +206,22 @@ auto EncoderInput::Read(lv_indev_data_t* data) -> void {
case Trigger::kLongPress: case Trigger::kLongPress:
break; break;
} }
} else if (mode_ == drivers::NvsStorage::InputModes::kRotatingWheel) {
break;
case drivers::NvsStorage::InputModes::kRotatingWheel:
if (!raw_wheel_.GetTouchWheelData().is_wheel_touched) { if (!raw_wheel_.GetTouchWheelData().is_wheel_touched) {
// User has released the wheel.
is_scrolling_wheel_ = false;
data->enc_diff = scroller_->AddInput(now_ms, 0); data->enc_diff = scroller_->AddInput(now_ms, 0);
} else if (relative_wheel_->ticks() != 0) { } else if (relative_wheel_->ticks() != 0) {
// User is touching the wheel, and has just passed the sensitivity
// threshold for a scroll tick.
is_scrolling_wheel_ = true;
data->enc_diff = scroller_->AddInput(now_ms, relative_wheel_->ticks()); data->enc_diff = scroller_->AddInput(now_ms, relative_wheel_->ticks());
} else { } else {
// User is touching the wheel, but hasn't moved.
data->enc_diff = 0; data->enc_diff = 0;
} }
trigger = Trigger trigger =
TriggerKey(Keys::kTouchWheelCenter, KeyStyle::kLongPress, now_ms); TriggerKey(Keys::kTouchWheelCenter, KeyStyle::kLongPress, now_ms);
switch (trigger) { switch (trigger) {
case Trigger::kNone: case Trigger::kNone:
@ -264,15 +259,7 @@ auto EncoderInput::Read(lv_indev_data_t* data) -> void {
break; break;
} }
// Only trigger the directional long-press gestures if they trigger at the trigger = TriggerKey(Keys::kDirectionalLeft, KeyStyle::kLongPress, now_ms);
// same time as a trigger on the overall touchwheel. This means the
// gestures only trigger if it's your only interaction with the wheel this
// press; scrolling and then resting on a direction should not trigger
// them.
trigger = TriggerKey(Keys::kTouchWheel, KeyStyle::kLongPress, now_ms);
if (trigger == Trigger::kLongPress) {
trigger =
TriggerKey(Keys::kDirectionalLeft, KeyStyle::kLongPress, now_ms);
switch (trigger) { switch (trigger) {
case Trigger::kNone: case Trigger::kNone:
break; break;
@ -283,15 +270,12 @@ auto EncoderInput::Read(lv_indev_data_t* data) -> void {
break; break;
} }
} }
break;
}
} }
auto EncoderInput::HandleKeyState(Keys key, uint64_t ms, bool clicked) -> void { auto EncoderInput::UpdateKeyState(Keys key, uint64_t ms, bool clicked) -> void {
if (clicked) { if (clicked) {
if (!touch_time_ms_.contains(key)) { if (!touch_time_ms_.contains(key)) {
// Key was just pressed // Key was just clicked.
touch_time_ms_[key] = ms; touch_time_ms_[key] = ms;
just_released_.erase(key); just_released_.erase(key);
fired_.erase(key); fired_.erase(key);

@ -72,6 +72,8 @@ class EncoderInput {
// Set of keys that have had an event fired for them since being pressed. // Set of keys that have had an event fired for them since being pressed.
std::set<Keys> fired_; std::set<Keys> fired_;
bool is_scrolling_wheel_;
enum class Trigger { enum class Trigger {
kNone, kNone,
// Regular short-click. Triggered on release for long-pressable keys, // Regular short-click. Triggered on release for long-pressable keys,
@ -85,7 +87,7 @@ class EncoderInput {
kLongPress, kLongPress,
}; };
auto HandleKeyState(Keys key, uint64_t ms, bool clicked) -> void; auto UpdateKeyState(Keys key, uint64_t ms, bool clicked) -> void;
auto TriggerKey(Keys key, KeyStyle t, uint64_t ms) -> Trigger; auto TriggerKey(Keys key, KeyStyle t, uint64_t ms) -> Trigger;
}; };

Loading…
Cancel
Save