From b24bd64f3a3cce10e8e339eec6bf2884ca5ab2e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 26 Mar 2024 00:18:23 +0100 Subject: [PATCH] add PCF8574(A) port expander as passive 4x4 keyboard --- platformio.ini | 1 + src/configuration.h | 3 +- src/detect/ScanI2C.cpp | 4 +- src/detect/ScanI2C.h | 1 + src/detect/ScanI2CTwoWire.cpp | 1 + src/input/cardKbI2cImpl.cpp | 2 +- src/input/peMatrixBase.cpp | 84 +++++++++++++++++++++++++++++++++++ src/input/peMatrixBase.h | 22 +++++++++ src/input/peMatrixImpl.cpp | 16 +++++++ src/input/peMatrixImpl.h | 19 ++++++++ src/main.cpp | 3 ++ src/modules/Modules.cpp | 3 ++ 12 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 src/input/peMatrixBase.cpp create mode 100644 src/input/peMatrixBase.h create mode 100644 src/input/peMatrixImpl.cpp create mode 100644 src/input/peMatrixImpl.h diff --git a/platformio.ini b/platformio.ini index a6db1c76e9..fd42a0efc6 100644 --- a/platformio.ini +++ b/platformio.ini @@ -77,6 +77,7 @@ lib_deps = jgromes/RadioLib@~6.5.0 https://github.com/meshtastic/esp8266-oled-ssd1306.git#ee628ee6c9588d4c56c9e3da35f0fc9448ad54a8 ; ESP8266_SSD1306 mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce + robtillaart/I2CKeyPad@^0.4.0 ; port extender with keymatrix https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159 https://github.com/meshtastic/TinyGPSPlus.git#71a82db35f3b973440044c476d4bcdc673b104f4 https://github.com/meshtastic/ArduinoThread.git#1ae8778c85d0a2a729f989e0b1e7d7c4dc84eef0 diff --git a/src/configuration.h b/src/configuration.h index 4934497643..bd945db5c6 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -105,10 +105,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 149bb95f05..395de5162d 100644 --- a/src/detect/ScanI2C.cpp +++ b/src/detect/ScanI2C.cpp @@ -30,8 +30,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 20f22040c7..874873b130 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 562a94c1fa..3b17715936 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -229,6 +229,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port) 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 e000f36eb0..0533235cd3 100644 --- a/src/input/cardKbI2cImpl.cpp +++ b/src/input/cardKbI2cImpl.cpp @@ -7,7 +7,7 @@ CardKbI2cImpl::CardKbI2cImpl() : KbI2cBase("cardKB") {} void CardKbI2cImpl::init() { - if (cardkb_found.address == 0x00) { + if ((cardkb_found.address == 0x00) || (kb_model == 0x12)) { disable(); return; } diff --git a/src/input/peMatrixBase.cpp b/src/input/peMatrixBase.cpp new file mode 100644 index 0000000000..af7d7b7996 --- /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 50; // Keyscan every 50msec 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 a6c1dd9fb9..5952ca1caa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -486,6 +486,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 15b356b059..4cffb3028e 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -6,6 +6,7 @@ #include "input/UpDownInterruptImpl1.h" #include "input/cardKbI2cImpl.h" #include "input/kbMatrixImpl.h" +#include "input/peMatrixImpl.h" #endif #include "modules/AdminModule.h" #if !MESHTASTIC_EXCLUDE_ATAK @@ -125,6 +126,8 @@ void setupModules() kbMatrixImpl = new KbMatrixImpl(); kbMatrixImpl->init(); #endif // INPUTBROKER_MATRIX_TYPE + peMatrixImpl = new PeMatrixImpl(); + peMatrixImpl->init(); #endif // HAS_BUTTON #if ARCH_PORTDUINO aLinuxInputImpl = new LinuxInputImpl();