diff --git a/src/configuration.h b/src/configuration.h
index 0269daba3..6b9fc67a6 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -126,7 +126,7 @@ along with this program. If not, see .
#endif
// -----------------------------------------------------------------------------
-// OLED
+// OLED & Input
// -----------------------------------------------------------------------------
#define SSD1306_ADDRESS 0x3C
@@ -143,6 +143,12 @@ along with this program. If not, see .
// Define if screen should be mirrored left to right
// #define SCREEN_MIRROR
+// The m5stack I2C Keyboard (also RAK14004)
+#define CARDKB_ADDR 0x5F
+
+// The older M5 Faces I2C Keyboard
+#define FACESKB_ADDR 0x88
+
// -----------------------------------------------------------------------------
// GPS
// -----------------------------------------------------------------------------
diff --git a/src/debug/i2cScan.h b/src/debug/i2cScan.h
index c1d1de7c9..94f3ef4db 100644
--- a/src/debug/i2cScan.h
+++ b/src/debug/i2cScan.h
@@ -47,6 +47,14 @@ void scanI2Cdevice(void)
DEBUG_MSG("unknown display found\n");
}
}
+ if (addr == CARDKB_ADDR) {
+ cardkb_found = addr;
+ DEBUG_MSG("m5 cardKB found\n");
+ }
+ if (addr == FACESKB_ADDR) {
+ faceskb_found = addr;
+ DEBUG_MSG("m5 Faces found\n");
+ }
if (addr == ST7567_ADDRESS) {
screen_found = addr;
DEBUG_MSG("st7567 display found\n");
diff --git a/src/input/cardKbI2cImpl.cpp b/src/input/cardKbI2cImpl.cpp
new file mode 100644
index 000000000..b4adc9038
--- /dev/null
+++ b/src/input/cardKbI2cImpl.cpp
@@ -0,0 +1,27 @@
+#include "cardKbI2cImpl.h"
+#include "InputBroker.h"
+
+CardKbI2cImpl *cardKbI2cImpl;
+
+CardKbI2cImpl::CardKbI2cImpl() :
+ KbI2cBase("cardKB")
+{
+}
+
+void CardKbI2cImpl::init()
+{
+ if (cardkb_found != CARDKB_ADDR)
+ {
+ // Input device is not detected.
+ return;
+ }
+
+ DEBUG_MSG("registerSource\n");
+ inputBroker->registerSource(this);
+}
+
+void CardKbI2cImpl::handlePressed()
+{
+ DEBUG_MSG("handlePressed\n");
+ cardKbI2cImpl->PressHandler();
+}
diff --git a/src/input/cardKbI2cImpl.h b/src/input/cardKbI2cImpl.h
new file mode 100644
index 000000000..a13998e59
--- /dev/null
+++ b/src/input/cardKbI2cImpl.h
@@ -0,0 +1,21 @@
+#pragma once
+#include "kbI2cBase.h"
+#include "main.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 CardKbI2cImpl :
+ public KbI2cBase
+{
+ public:
+ CardKbI2cImpl();
+ void init();
+ static void handlePressed();
+};
+
+extern CardKbI2cImpl *cardKbI2cImpl;
\ No newline at end of file
diff --git a/src/input/facesKbI2cImpl.cpp b/src/input/facesKbI2cImpl.cpp
new file mode 100644
index 000000000..348bfcf13
--- /dev/null
+++ b/src/input/facesKbI2cImpl.cpp
@@ -0,0 +1,25 @@
+#include "facesKbI2cImpl.h"
+#include "InputBroker.h"
+
+FacesKbI2cImpl *facesKbI2cImpl;
+
+FacesKbI2cImpl::FacesKbI2cImpl() :
+ KbI2cBase("facesKB")
+{
+}
+
+void FacesKbI2cImpl::init()
+{
+ if (faceskb_found != FACESKB_ADDR)
+ {
+ // Input device is not detected.
+ return;
+ }
+
+ inputBroker->registerSource(this);
+}
+
+void FacesKbI2cImpl::handlePressed()
+{
+ facesKbI2cImpl->PressHandler();
+}
diff --git a/src/input/facesKbI2cImpl.h b/src/input/facesKbI2cImpl.h
new file mode 100644
index 000000000..5510a5c34
--- /dev/null
+++ b/src/input/facesKbI2cImpl.h
@@ -0,0 +1,21 @@
+#pragma once
+#include "kbI2cBase.h"
+#include "main.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 FacesKbI2cImpl :
+ public KbI2cBase
+{
+ public:
+ FacesKbI2cImpl();
+ void init();
+ static void handlePressed();
+};
+
+extern FacesKbI2cImpl *facesKbI2cImpl;
\ No newline at end of file
diff --git a/src/input/kbI2cBase.cpp b/src/input/kbI2cBase.cpp
new file mode 100644
index 000000000..62719efee
--- /dev/null
+++ b/src/input/kbI2cBase.cpp
@@ -0,0 +1,50 @@
+#include "configuration.h"
+#include "kbI2cBase.h"
+#include
+
+KbI2cBase::KbI2cBase(const char *name) : concurrency::OSThread(name)
+{
+ this->_originName = name;
+}
+
+int32_t KbI2cBase::runOnce()
+{
+ InputEvent e;
+ e.inputEvent = InputEventChar_KEY_NONE;
+ e.source = this->_originName;
+
+ Wire.requestFrom(CARDKB_ADDR, 1);
+
+ while(Wire.available()) {
+ char c = Wire.read();
+ switch(c) {
+ case 0x1b: // ESC
+ e.inputEvent = InputEventChar_KEY_CANCEL;
+ break;
+ case 0x08: // Back
+ e.inputEvent = InputEventChar_KEY_BACK;
+ break;
+ case 0xb5: // Up
+ e.inputEvent = InputEventChar_KEY_UP;
+ break;
+ case 0xb6: // Down
+ e.inputEvent = InputEventChar_KEY_DOWN;
+ break;
+ case 0xb4: // Left
+ e.inputEvent = InputEventChar_KEY_LEFT;
+ break;
+ case 0xb7: // Right
+ e.inputEvent = InputEventChar_KEY_RIGHT;
+ break;
+ case 0x0d: // Enter
+ e.inputEvent = InputEventChar_KEY_SELECT;
+ break;
+ }
+ }
+
+ if (e.inputEvent != InputEventChar_KEY_NONE)
+ {
+ this->notifyObservers(&e);
+ }
+ return 500;
+}
diff --git a/src/input/kbI2cBase.h b/src/input/kbI2cBase.h
new file mode 100644
index 000000000..c793811a1
--- /dev/null
+++ b/src/input/kbI2cBase.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "SinglePortModule.h" // TODO: what header file to include?
+#include "InputBroker.h"
+
+class KbI2cBase :
+ public Observable,
+ private concurrency::OSThread
+{
+ public:
+ explicit KbI2cBase(const char *name);
+ void PressHandler();
+
+ protected:
+ virtual int32_t runOnce() override;
+
+ private:
+ const char *_originName;
+};
diff --git a/src/main.cpp b/src/main.cpp
index 0ec89a01d..d90bce00d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -72,6 +72,12 @@ meshtastic::NodeStatus *nodeStatus = new meshtastic::NodeStatus();
uint8_t screen_found;
uint8_t screen_model;
+// The I2C address of the cardkb or RAK14004 (if found)
+uint8_t cardkb_found;
+
+// The I2C address of the Faces Keyboard (if found)
+uint8_t faceskb_found;
+
bool axp192_found;
Router *router = NULL; // Users of router don't care what sort of subclass implements that API
diff --git a/src/main.h b/src/main.h
index 6617cd770..c5fc62f32 100644
--- a/src/main.h
+++ b/src/main.h
@@ -7,6 +7,9 @@
extern uint8_t screen_found;
extern uint8_t screen_model;
+extern uint8_t cardkb_found;
+extern uint8_t faceskb_found;
+
extern bool axp192_found;
extern bool isCharging;
extern bool isUSBPowered;
diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp
index 3c90243cb..181cea1c8 100644
--- a/src/modules/Modules.cpp
+++ b/src/modules/Modules.cpp
@@ -2,6 +2,8 @@
#include "input/InputBroker.h"
#include "input/RotaryEncoderInterruptImpl1.h"
#include "input/UpDownInterruptImpl1.h"
+#include "input/cardKbI2cImpl.h"
+#include "input/facesKbI2cImpl.h"
#include "modules/AdminModule.h"
#include "modules/CannedMessageModule.h"
#include "modules/ExternalNotificationModule.h"
@@ -40,6 +42,10 @@ void setupModules()
rotaryEncoderInterruptImpl1->init();
upDownInterruptImpl1 = new UpDownInterruptImpl1();
upDownInterruptImpl1->init();
+ cardKbI2cImpl = new CardKbI2cImpl();
+ cardKbI2cImpl->init();
+ facesKbI2cImpl = new FacesKbI2cImpl();
+ facesKbI2cImpl->init();
cannedMessageModule = new CannedMessageModule();
#ifndef PORTDUINO
new TelemetryModule();