Files
meshtastic-device-ui/source/input/I2CKeyboardInputDriver.cpp
Manuel 7473151c8a feat: I2C keyboard scanner + cardkb (#183)
* I2C keyboard scanner + cardkb

* scan all keyboards

* request input from all keyboards

* CardKB Fn+ENTER: simulate LONG PRESS
2025-08-22 20:12:09 +02:00

292 lines
9.2 KiB
C++

#include "input/I2CKeyboardInputDriver.h"
#include "util/ILog.h"
#include <Arduino.h>
#include <Wire.h>
#include "indev/lv_indev_private.h"
I2CKeyboardInputDriver::KeyboardList I2CKeyboardInputDriver::i2cKeyboardList;
I2CKeyboardInputDriver::I2CKeyboardInputDriver(void) {}
void I2CKeyboardInputDriver::init(void)
{
keyboard = lv_indev_create();
lv_indev_set_type(keyboard, LV_INDEV_TYPE_KEYPAD);
lv_indev_set_read_cb(keyboard, keyboard_read);
if (!inputGroup) {
inputGroup = lv_group_create();
lv_group_set_default(inputGroup);
}
lv_indev_set_group(keyboard, inputGroup);
}
bool I2CKeyboardInputDriver::registerI2CKeyboard(I2CKeyboardInputDriver *driver, std::string name, uint8_t address)
{
auto keyboardDef = std::unique_ptr<KeyboardDefinition>(new KeyboardDefinition{driver, name, address});
i2cKeyboardList.push_back(std::move(keyboardDef));
ILOG_INFO("Registered I2C keyboard: %s at address 0x%02X", name.c_str(), address);
return true;
}
void I2CKeyboardInputDriver::keyboard_read(lv_indev_t *indev, lv_indev_data_t *data)
{
// Read from all registered keyboards
for (auto &keyboardDef : i2cKeyboardList) {
keyboardDef->driver->readKeyboard(keyboardDef->address, indev, data);
if (data->state == LV_INDEV_STATE_PRESSED) {
// If any keyboard reports a key press, we stop reading further
return;
}
}
}
// ---------- TDeckKeyboardInputDriver Implementation ----------
TDeckKeyboardInputDriver::TDeckKeyboardInputDriver(uint8_t address)
{
registerI2CKeyboard(this, "T-Deck Keyboard", address);
}
/******************************************************************
LV_KEY_NEXT: Focus on the next object
LV_KEY_PREV: Focus on the previous object
LV_KEY_ENTER: Triggers LV_EVENT_PRESSED, LV_EVENT_CLICKED, or LV_EVENT_LONG_PRESSED etc. events
LV_KEY_UP: Increase value or move upwards
LV_KEY_DOWN: Decrease value or move downwards
LV_KEY_RIGHT: Increase value or move to the right
LV_KEY_LEFT: Decrease value or move to the left
LV_KEY_ESC: Close or exit (E.g. close a Drop down list)
LV_KEY_DEL: Delete (E.g. a character on the right in a Text area)
LV_KEY_BACKSPACE: Delete a character on the left (E.g. in a Text area)
LV_KEY_HOME: Go to the beginning/top (E.g. in a Text area)
LV_KEY_END: Go to the end (E.g. in a Text area)
LV_KEY_UP = 17, // 0x11
LV_KEY_DOWN = 18, // 0x12
LV_KEY_RIGHT = 19, // 0x13
LV_KEY_LEFT = 20, // 0x14
LV_KEY_ESC = 27, // 0x1B
LV_KEY_DEL = 127, // 0x7F
LV_KEY_BACKSPACE = 8, // 0x08
LV_KEY_ENTER = 10, // 0x0A, '\n'
LV_KEY_NEXT = 9, // 0x09, '\t'
LV_KEY_PREV = 11, // 0x0B, '
LV_KEY_HOME = 2, // 0x02, STX
LV_KEY_END = 3, // 0x03, ETX
*******************************************************************/
void TDeckKeyboardInputDriver::readKeyboard(uint8_t address, lv_indev_t *indev, lv_indev_data_t *data)
{
char keyValue = 0;
uint8_t bytes = Wire.requestFrom(address, 1);
if (Wire.available() > 0 && bytes > 0) {
keyValue = Wire.read();
// ignore empty reads and keycode 224(E0, shift-0 on T-Deck) which causes internal issues
if (keyValue != (char)0x00 && keyValue != (char)0xE0) {
data->state = LV_INDEV_STATE_PRESSED;
ILOG_DEBUG("key press value: %d", (int)keyValue);
switch (keyValue) {
case 0x0D:
keyValue = LV_KEY_ENTER;
break;
default:
break;
}
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
data->key = (uint32_t)keyValue;
}
// ---------- TCA8418KeyboardInputDriver Implementation ----------
TCA8418KeyboardInputDriver::TCA8418KeyboardInputDriver(uint8_t address)
{
registerI2CKeyboard(this, "TCA8418 Keyboard", address);
}
void TCA8418KeyboardInputDriver::init(void)
{
// Additional initialization for TCA8418 if needed
I2CKeyboardInputDriver::init();
}
void TCA8418KeyboardInputDriver::readKeyboard(uint8_t address, lv_indev_t *indev, lv_indev_data_t *data)
{
// TODO
char keyValue = 0;
data->state = LV_INDEV_STATE_RELEASED;
data->key = (uint32_t)keyValue;
}
// ---------- TLoraPagerKeyboardInputDriver Implementation ----------
TLoraPagerKeyboardInputDriver::TLoraPagerKeyboardInputDriver(uint8_t address) : TCA8418KeyboardInputDriver(address)
{
registerI2CKeyboard(this, "TLora Pager Keyboard", address);
}
void TLoraPagerKeyboardInputDriver::init(void)
{
// Additional initialization for TLora-Pager if needed
TCA8418KeyboardInputDriver::init();
}
void TLoraPagerKeyboardInputDriver::readKeyboard(uint8_t address, lv_indev_t *indev, lv_indev_data_t *data)
{
// TODO
char keyValue = 0;
data->state = LV_INDEV_STATE_RELEASED;
data->key = (uint32_t)keyValue;
}
// ---------- TDeckProKeyboardInputDriver Implementation ----------
TDeckProKeyboardInputDriver::TDeckProKeyboardInputDriver(uint8_t address) : TCA8418KeyboardInputDriver(address)
{
registerI2CKeyboard(this, "T-Deck Pro Keyboard", address);
}
void TDeckProKeyboardInputDriver::init(void)
{
// Additional initialization for TLora-Pager if needed
TCA8418KeyboardInputDriver::init();
}
void TDeckProKeyboardInputDriver::readKeyboard(uint8_t address, lv_indev_t *indev, lv_indev_data_t *data)
{
// TODO
char keyValue = 0;
data->state = LV_INDEV_STATE_RELEASED;
data->key = (uint32_t)keyValue;
}
// ---------- BBQ10KeyboardInputDriver Implementation ----------
BBQ10KeyboardInputDriver::BBQ10KeyboardInputDriver(uint8_t address)
{
registerI2CKeyboard(this, "BBQ10 Keyboard", address);
}
void BBQ10KeyboardInputDriver::init(void)
{
I2CKeyboardInputDriver::init();
// Additional initialization for BBQ10 if needed
}
void BBQ10KeyboardInputDriver::readKeyboard(uint8_t address, lv_indev_t *indev, lv_indev_data_t *data)
{
char keyValue = 0;
uint8_t bytes = Wire.requestFrom(address, 1);
if (Wire.available() > 0 && bytes > 0) {
keyValue = Wire.read();
// ignore empty reads and keycode 224(E0, shift-0 on T-Deck) which causes internal issues
if (keyValue != (char)0x00 && keyValue != (char)0xE0) {
data->state = LV_INDEV_STATE_PRESSED;
ILOG_DEBUG("key press value: %d", (int)keyValue);
switch (keyValue) {
case 0x0D:
keyValue = LV_KEY_ENTER;
break;
default:
break;
}
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
data->key = (uint32_t)keyValue;
}
// ---------- CardKBInputDriver Implementation ----------
CardKBInputDriver::CardKBInputDriver(uint8_t address)
{
registerI2CKeyboard(this, "Card Keyboard", address);
}
void CardKBInputDriver::readKeyboard(uint8_t address, lv_indev_t *indev, lv_indev_data_t *data)
{
char keyValue = 0;
Wire.requestFrom(address, 1);
if (Wire.available() > 0) {
keyValue = Wire.read();
// ignore empty reads and keycode 224 which causes internal issues
if (keyValue != (char)0x00 && keyValue != (char)0xE0) {
data->state = LV_INDEV_STATE_PRESSED;
ILOG_DEBUG("key press value: %d", (int)keyValue);
switch (keyValue) {
case 0x0D:
keyValue = LV_KEY_ENTER;
break;
case 0xB4:
keyValue = LV_KEY_LEFT;
break;
case 0xB5:
keyValue = LV_KEY_UP;
break;
case 0xB6:
keyValue = LV_KEY_DOWN;
break;
case 0xB7:
keyValue = LV_KEY_RIGHT;
break;
case 0x99: // Fn+UP
keyValue = LV_KEY_HOME;
break;
case 0xA4: // Fn+DOWN
keyValue = LV_KEY_END;
break;
case 0x8B: // Fn+BS
keyValue = LV_KEY_DEL;
break;
case 0x8C: // Fn+TAB
keyValue = LV_KEY_PREV;
break;
case 0xA3: // Fn+ENTER
// simulate a long press on Fn+ENTER (see indev_keypad_proc() in indev.c)
indev->wait_until_release = 0;
indev->pr_timestamp = lv_tick_get() - indev->long_press_time - 1;
indev->long_pr_sent = 0;
indev->keypad.last_state = LV_INDEV_STATE_PRESSED;
indev->keypad.last_key = LV_KEY_ENTER;
keyValue = LV_KEY_ENTER;
break;
default:
break;
}
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
data->key = (uint32_t)keyValue;
}
// ---------- MPR121KeyboardInputDriver Implementation ----------
MPR121KeyboardInputDriver::MPR121KeyboardInputDriver(uint8_t address)
{
registerI2CKeyboard(this, "MPR121 Keyboard", address);
}
void MPR121KeyboardInputDriver::init(void)
{
I2CKeyboardInputDriver::init();
// Additional initialization for MPR121 if needed
}
void MPR121KeyboardInputDriver::readKeyboard(uint8_t address, lv_indev_t *indev, lv_indev_data_t *data)
{
// TODO
char keyValue = 0;
data->state = LV_INDEV_STATE_RELEASED;
data->key = (uint32_t)keyValue;
}