diff --git a/arch/stm32/stm32.ini b/arch/stm32/stm32.ini index dd190c9d4..60bc41d23 100644 --- a/arch/stm32/stm32.ini +++ b/arch/stm32/stm32.ini @@ -28,9 +28,8 @@ build_flags = -fmerge-all-constants -ffunction-sections -fdata-sections - build_src_filter = - ${arduino_base.build_src_filter} - - - - - - - - - - - - - - + ${arduino_base.build_src_filter} - - - - - - - - - - - - - - -<.pio/*/*/I2CKeyPad/*> board_upload.offset_address = 0x08000000 upload_protocol = stlink @@ -43,5 +42,6 @@ lib_deps = https://github.com/caveman99/Crypto/archive/eae9c768054118a9399690f8af202853d1ae8516.zip lib_ignore = - mathertel/OneButton@2.6.1 Wire + I2CKeyPad + mathertel/OneButton@2.6.1 diff --git a/platformio.ini b/platformio.ini index ecde59de2..4ed55ece0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -71,6 +71,7 @@ lib_deps = nanopb/Nanopb@0.4.91 # renovate: datasource=custom.pio depName=ErriezCRC32 packageName=erriez/library/ErriezCRC32 erriez/ErriezCRC32@1.0.1 + robtillaart/I2CKeyPad@0.5.0 ; Used for the code analysis in PIO Home / Inspect check_tool = cppcheck diff --git a/src/configuration.h b/src/configuration.h index 32d99295e..ab2143a03 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -114,11 +114,12 @@ 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 MPR121_KB_ADDR 0x5A +#define PCF8574A_ADDRESS 0x20 // ----------------------------------------------------------------------------- // SENSOR diff --git a/src/detect/ScanI2C.cpp b/src/detect/ScanI2C.cpp index 5bd5c0d12..881b75405 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, MPR121KB, TCA8418KB}; - return firstOfOrNONE(6, types); + ScanI2C::DeviceType types[] = {CARDKB, TDECKKB, BBQ10KB, RAK14004, PCF8574A, MPR121KB, TCA8418KB}; + return firstOfOrNONE(7, types); } ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h index 72184db69..72b2bda81 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -18,6 +18,7 @@ class ScanI2C TDECKKB, BBQ10KB, RAK14004, + PCF8574A, PMU_AXP192_AXP2101, // has the same adress as the TCA8418KB BME_680, BME_280, diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index e2ba78a92..f8d9ae907 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -210,6 +210,9 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) SCAN_SIMPLE_CASE(BBQ10_KB_ADDR, BBQ10KB, "BB Q10", (uint8_t)addr.address); SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "ST7567", (uint8_t)addr.address); +#ifndef HAS_TCA9535 + SCAN_SIMPLE_CASE(PCF8574A_ADDRESS, PCF8574A, "PCF8574A", (uint8_t)addr.address); +#endif #ifdef HAS_NCP5623 SCAN_SIMPLE_CASE(NCP5623_ADDR, NCP5623, "NCP5623", (uint8_t)addr.address); #endif diff --git a/src/input/cardKbI2cImpl.cpp b/src/input/cardKbI2cImpl.cpp index 21ecf381a..e3d1ae8de 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) && !defined(I2C_NO_RESCAN) if (cardkb_found.address == 0x00) { LOG_DEBUG("Rescan for I2C keyboard"); diff --git a/src/input/peMatrixBase.cpp b/src/input/peMatrixBase.cpp new file mode 100644 index 000000000..db96eae22 --- /dev/null +++ b/src/input/peMatrixBase.cpp @@ -0,0 +1,88 @@ +#include "peMatrixBase.h" + +#include "configuration.h" +#include "detect/ScanI2C.h" + +extern ScanI2C::DeviceAddress cardkb_found; +extern uint8_t kb_model; + +#if WIRE_INTERFACES_COUNT == 2 // defined in architecture.h +I2CKeyPad keyPad(cardkb_found.address, cardkb_found.port == ScanI2C::WIRE1 ? &Wire1 : &Wire); +#else +I2CKeyPad keyPad(cardkb_found.address, &Wire); +#endif + +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"); + 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 000000000..299135601 --- /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 000000000..57d1c03f6 --- /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 000000000..eba100166 --- /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 c12707cdb..46fe1e5f1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -636,6 +636,9 @@ void setup() // assign an arbitrary value to distinguish from other models kb_model = 0x11; break; + case ScanI2C::DeviceType::PCF8574A: + kb_model = 0x12; + break; case ScanI2C::DeviceType::MPR121KB: // assign an arbitrary value to distinguish from other models kb_model = 0x37; diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index fac2ca976..001db1969 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -11,6 +11,7 @@ #include "input/cardKbI2cImpl.h" #endif #include "input/kbMatrixImpl.h" +#include "input/peMatrixImpl.h" #endif #if !MESHTASTIC_EXCLUDE_ADMIN #include "modules/AdminModule.h" @@ -178,10 +179,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 && !HAS_TFT aLinuxInputImpl = new LinuxInputImpl(); diff --git a/variants/seeed-sensecap-indicator/variant.h b/variants/seeed-sensecap-indicator/variant.h index 1010e04c8..1be93f642 100644 --- a/variants/seeed-sensecap-indicator/variant.h +++ b/variants/seeed-sensecap-indicator/variant.h @@ -16,6 +16,9 @@ // #define ADC_CHANNEL ADC1_GPIO27_CHANNEL // #define ADC_MULTIPLIER 2 +// Portexpander +#define HAS_TCA9535 + // ST7701 TFT LCD #define ST7701_CS (4 | IO_EXPANDER) #define ST7701_RS -1 // DC