diff --git a/platformio.ini b/platformio.ini index e437b572e9..e300c205db 100644 --- a/platformio.ini +++ b/platformio.ini @@ -96,6 +96,7 @@ lib_deps = https://github.com/meshtastic/ArduinoThread.git#1ae8778c85d0a2a729f989e0b1e7d7c4dc84eef0 nanopb/Nanopb@^0.4.9 erriez/ErriezCRC32@^1.0.1 + robtillaart/I2CKeyPad@^0.4.0 ; port extender with keymatrix ; Used for the code analysis in PIO Home / Inspect check_tool = cppcheck diff --git a/src/configuration.h b/src/configuration.h index 10a4e0a990..955c2ea195 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -110,10 +110,11 @@ along with this program. If not, see . // Define if screen should be mirrored left to right // #define SCREEN_MIRROR -// I2C Keyboards (M5Stack, RAK14004, T-Deck) +// I2C Keyboards (M5Stack, RAK14004, T-Deck, PCF8574A passive) #define CARDKB_ADDR 0x5F #define TDECK_KB_ADDR 0x55 #define BBQ10_KB_ADDR 0x1F +#define PCF8574A_ADDRESS 0x20 // ----------------------------------------------------------------------------- // SENSOR diff --git a/src/detect/ScanI2C.cpp b/src/detect/ScanI2C.cpp index a9d70edaa9..3daf6f1912 100644 --- a/src/detect/ScanI2C.cpp +++ b/src/detect/ScanI2C.cpp @@ -31,8 +31,8 @@ ScanI2C::FoundDevice ScanI2C::firstRTC() const ScanI2C::FoundDevice ScanI2C::firstKeyboard() const { - ScanI2C::DeviceType types[] = {CARDKB, TDECKKB, BBQ10KB, RAK14004}; - return firstOfOrNONE(4, types); + ScanI2C::DeviceType types[] = {CARDKB, TDECKKB, BBQ10KB, RAK14004, PCF8574A}; + return firstOfOrNONE(5, types); } ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h index 07db3fd572..18a7bdbcdf 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -19,6 +19,7 @@ class ScanI2C TDECKKB, BBQ10KB, RAK14004, + PCF8574A, PMU_AXP192_AXP2101, BME_680, BME_280, diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index af94290d26..cb16a371f6 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -244,6 +244,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) SCAN_SIMPLE_CASE(TDECK_KB_ADDR, TDECKKB, "T-Deck keyboard found\n"); SCAN_SIMPLE_CASE(BBQ10_KB_ADDR, BBQ10KB, "BB Q10 keyboard found\n"); SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "st7567 display found\n"); + SCAN_SIMPLE_CASE(PCF8574A_ADDRESS, PCF8574A, "PCF8574A based keyboard found\n"); #ifdef HAS_NCP5623 SCAN_SIMPLE_CASE(NCP5623_ADDR, NCP5623, "NCP5623 RGB LED found\n"); #endif diff --git a/src/input/cardKbI2cImpl.cpp b/src/input/cardKbI2cImpl.cpp index f1df6b1377..fc32034068 100644 --- a/src/input/cardKbI2cImpl.cpp +++ b/src/input/cardKbI2cImpl.cpp @@ -9,6 +9,10 @@ CardKbI2cImpl::CardKbI2cImpl() : KbI2cBase("cardKB") {} void CardKbI2cImpl::init() { + if (kb_model == 0x12) { + disable(); + return; + } #if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO) if (cardkb_found.address == 0x00) { LOG_DEBUG("Rescanning for I2C keyboard\n"); diff --git a/src/input/peMatrixBase.cpp b/src/input/peMatrixBase.cpp new file mode 100644 index 0000000000..5340898a93 --- /dev/null +++ b/src/input/peMatrixBase.cpp @@ -0,0 +1,84 @@ +#include "peMatrixBase.h" + +#include "configuration.h" +#include "detect/ScanI2C.h" + +extern ScanI2C::DeviceAddress cardkb_found; +extern uint8_t kb_model; + +I2CKeyPad keyPad(cardkb_found.address); + +PeMatrixBase::PeMatrixBase(const char *name) : concurrency::OSThread(name) +{ + this->_originName = name; +} + +int32_t PeMatrixBase::runOnce() +{ + if (kb_model != 0x12) { + // Input device is not detected. + return disable(); + } + + if (firstTime) { + // This is the first time the OSThread library has called this function, so do port setup + firstTime = 0; + if (!keyPad.begin()) { + LOG_ERROR("Failed to initialize I2C keypad\n"); + return disable(); + } + keyPad.loadKeyMap(keymap); + } else { + if (keyPad.isPressed()) { + key = keyPad.getChar(); + // debounce + if (key != prevkey) { + if (key != 0) { + LOG_DEBUG("Key 0x%x pressed\n", key); + // reset shift now that we have a keypress + InputEvent e; + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE; + e.source = this->_originName; + switch (key) { + case 0x1b: // ESC + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL; + break; + case 0x08: // Back + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK; + e.kbchar = key; + break; + case 0xb5: // Up + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP; + break; + case 0xb6: // Down + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN; + break; + case 0xb4: // Left + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT; + e.kbchar = key; + break; + case 0xb7: // Right + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT; + e.kbchar = key; + break; + case 0x0d: // Enter + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT; + break; + case 0x00: // nopress + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE; + break; + default: // all other keys + e.inputEvent = ANYKEY; + e.kbchar = key; + break; + } + if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) { + this->notifyObservers(&e); + } + } + prevkey = key; + } + } + } + return 100; // Keyscan every 100msec to avoid key bounce +} diff --git a/src/input/peMatrixBase.h b/src/input/peMatrixBase.h new file mode 100644 index 0000000000..2991356012 --- /dev/null +++ b/src/input/peMatrixBase.h @@ -0,0 +1,22 @@ +#pragma once + +#include "InputBroker.h" +#include "concurrency/OSThread.h" +#include + +class PeMatrixBase : public Observable, public concurrency::OSThread +{ + public: + explicit PeMatrixBase(const char *name); + + protected: + virtual int32_t runOnce() override; + + private: + const char *_originName; + bool firstTime = 1; + // char keymap[19] = "123A456B789C*0#DNF"; // N = NoKey, F = Fail + char keymap[19] = {0x1b, 0xb5, '3', 'A', 0xb4, 0x0d, 0xb7, 'B', '7', 0xb6, '9', 'C', 0x09, '0', 0x08, 'D', 'N', 'F'}; + char key = 0; + char prevkey = 0; +}; \ No newline at end of file diff --git a/src/input/peMatrixImpl.cpp b/src/input/peMatrixImpl.cpp new file mode 100644 index 0000000000..57d1c03f6f --- /dev/null +++ b/src/input/peMatrixImpl.cpp @@ -0,0 +1,16 @@ +#include "peMatrixImpl.h" +#include "InputBroker.h" + +PeMatrixImpl *peMatrixImpl; + +PeMatrixImpl::PeMatrixImpl() : PeMatrixBase("matrixPE") {} + +void PeMatrixImpl::init() +{ + if (kb_model != 0x12) { + disable(); + return; + } + + inputBroker->registerSource(this); +} diff --git a/src/input/peMatrixImpl.h b/src/input/peMatrixImpl.h new file mode 100644 index 0000000000..eba1001663 --- /dev/null +++ b/src/input/peMatrixImpl.h @@ -0,0 +1,19 @@ +#pragma once +#include "main.h" +#include "peMatrixBase.h" + +/** + * @brief The idea behind this class to have static methods for the event handlers. + * Check attachInterrupt() at RotaryEncoderInteruptBase.cpp + * Technically you can have as many rotary encoders hardver attached + * to your device as you wish, but you always need to have separate event + * handlers, thus you need to have a RotaryEncoderInterrupt implementation. + */ +class PeMatrixImpl : public PeMatrixBase +{ + public: + PeMatrixImpl(); + void init(); +}; + +extern PeMatrixImpl *peMatrixImpl; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 8387392feb..0d136bd66e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -523,6 +523,9 @@ void setup() // assign an arbitrary value to distinguish from other models kb_model = 0x11; break; + case ScanI2C::DeviceType::PCF8574A: + kb_model = 0x12; + break; default: // use this as default since it's also just zero LOG_WARN("kb_info.type is unknown(0x%02x), setting kb_model=0x00\n", kb_info.type); diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index ad3f0ace45..bfd45f81c0 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -9,6 +9,7 @@ #include "input/UpDownInterruptImpl1.h" #include "input/cardKbI2cImpl.h" #include "input/kbMatrixImpl.h" +#include "input/peMatrixImpl.h" #endif #if !MESHTASTIC_EXCLUDE_ADMIN #include "modules/AdminModule.h" @@ -168,10 +169,15 @@ void setupModules() kbMatrixImpl = new KbMatrixImpl(); kbMatrixImpl->init(); #endif // INPUTBROKER_MATRIX_TYPE + + peMatrixImpl = new PeMatrixImpl(); + peMatrixImpl->init(); + #ifdef INPUTBROKER_SERIAL_TYPE aSerialKeyboardImpl = new SerialKeyboardImpl(); aSerialKeyboardImpl->init(); #endif // INPUTBROKER_MATRIX_TYPE + #endif // HAS_BUTTON #if ARCH_PORTDUINO aLinuxInputImpl = new LinuxInputImpl();