diff --git a/src/configuration.h b/src/configuration.h
index fb96430bc..e116854c2 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -101,6 +101,7 @@ along with this program. If not, see .
// I2C Keyboards (M5Stack, RAK14004, T-Deck)
#define CARDKB_ADDR 0x5F
#define TDECK_KB_ADDR 0x55
+#define BBQ10_KB_ADDR 0x1F
// -----------------------------------------------------------------------------
// SENSOR
diff --git a/src/detect/ScanI2C.cpp b/src/detect/ScanI2C.cpp
index 996bdf62a..bf206c190 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, RAK14004};
- return firstOfOrNONE(3, types);
+ ScanI2C::DeviceType types[] = {CARDKB, TDECKKB, BBQ10KB, RAK14004};
+ return firstOfOrNONE(4, types);
}
ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const
diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h
index 418a6bf5e..15d9b1342 100644
--- a/src/detect/ScanI2C.h
+++ b/src/detect/ScanI2C.h
@@ -17,6 +17,7 @@ class ScanI2C
RTC_PCF8563,
CARDKB,
TDECKKB,
+ BBQ10KB,
RAK14004,
PMU_AXP192_AXP2101,
BME_680,
diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp
index 30f9e7b7c..ced1e34dd 100644
--- a/src/detect/ScanI2CTwoWire.cpp
+++ b/src/detect/ScanI2CTwoWire.cpp
@@ -213,6 +213,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
break;
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");
#ifdef HAS_NCP5623
SCAN_SIMPLE_CASE(NCP5623_ADDR, NCP5623, "NCP5623 RGB LED found\n");
diff --git a/src/input/BBQ10Keyboard.cpp b/src/input/BBQ10Keyboard.cpp
new file mode 100644
index 000000000..f0459d8f7
--- /dev/null
+++ b/src/input/BBQ10Keyboard.cpp
@@ -0,0 +1,177 @@
+// Based on arturo182 arduino_bbq10kbd library https://github.com/arturo182/arduino_bbq10kbd
+
+#include
+
+#include "BBQ10Keyboard.h"
+
+#define _REG_VER 1
+#define _REG_CFG 2
+#define _REG_INT 3
+#define _REG_KEY 4
+#define _REG_BKL 5
+#define _REG_DEB 6
+#define _REG_FRQ 7
+#define _REG_RST 8
+#define _REG_FIF 9
+
+#define _WRITE_MASK (1 << 7)
+
+#define CFG_OVERFLOW_ON (1 << 0)
+#define CFG_OVERFLOW_INT (1 << 1)
+#define CFG_CAPSLOCK_INT (1 << 2)
+#define CFG_NUMLOCK_INT (1 << 3)
+#define CFG_KEY_INT (1 << 4)
+#define CFG_PANIC_INT (1 << 5)
+
+#define INT_OVERFLOW (1 << 0)
+#define INT_CAPSLOCK (1 << 1)
+#define INT_NUMLOCK (1 << 2)
+#define INT_KEY (1 << 3)
+#define INT_PANIC (1 << 4)
+
+#define KEY_CAPSLOCK (1 << 5)
+#define KEY_NUMLOCK (1 << 6)
+#define KEY_COUNT_MASK (0x1F)
+
+BBQ10Keyboard::BBQ10Keyboard() : m_wire(nullptr), m_addr(NULL), writeCallback(nullptr), readCallback(nullptr) {}
+
+void BBQ10Keyboard::begin(uint8_t addr, TwoWire *wire)
+{
+ m_addr = addr;
+ m_wire = wire;
+
+ m_wire->begin();
+
+ reset();
+}
+
+void BBQ10Keyboard::begin(i2c_com_fptr_t r, i2c_com_fptr_t w, uint8_t addr)
+{
+ m_addr = addr;
+ m_wire = nullptr;
+ writeCallback = w;
+ readCallback = r;
+ reset();
+}
+
+void BBQ10Keyboard::reset()
+{
+ if (m_wire) {
+ m_wire->beginTransmission(m_addr);
+ m_wire->write(_REG_RST);
+ m_wire->endTransmission();
+ }
+ if (writeCallback) {
+ uint8_t data = 0;
+ writeCallback(m_addr, _REG_RST, &data, 0);
+ }
+ delay(100);
+}
+
+void BBQ10Keyboard::attachInterrupt(uint8_t pin, void (*func)(void)) const
+{
+ pinMode(pin, INPUT_PULLUP);
+ ::attachInterrupt(digitalPinToInterrupt(pin), func, RISING);
+}
+
+void BBQ10Keyboard::detachInterrupt(uint8_t pin) const
+{
+ ::detachInterrupt(pin);
+}
+
+void BBQ10Keyboard::clearInterruptStatus()
+{
+ writeRegister(_REG_INT, 0x00);
+}
+
+uint8_t BBQ10Keyboard::status() const
+{
+ return readRegister8(_REG_KEY);
+}
+
+uint8_t BBQ10Keyboard::keyCount() const
+{
+ return status() & KEY_COUNT_MASK;
+}
+
+BBQ10Keyboard::KeyEvent BBQ10Keyboard::keyEvent() const
+{
+ KeyEvent event = {.key = '\0', .state = StateIdle};
+
+ if (keyCount() == 0)
+ return event;
+
+ const uint16_t buf = readRegister16(_REG_FIF);
+ event.key = buf >> 8;
+ event.state = KeyState(buf & 0xFF);
+
+ return event;
+}
+
+float BBQ10Keyboard::backlight() const
+{
+ return readRegister8(_REG_BKL) / 255.0f;
+}
+
+void BBQ10Keyboard::setBacklight(float value)
+{
+ writeRegister(_REG_BKL, value * 255);
+}
+
+uint8_t BBQ10Keyboard::readRegister8(uint8_t reg) const
+{
+ if (m_wire) {
+ m_wire->beginTransmission(m_addr);
+ m_wire->write(reg);
+ m_wire->endTransmission();
+
+ m_wire->requestFrom(m_addr, (uint8_t)1);
+ if (m_wire->available() < 1)
+ return 0;
+
+ return m_wire->read();
+ }
+ if (readCallback) {
+ uint8_t data;
+ readCallback(m_addr, reg, &data, 1);
+ return data;
+ }
+ return 0;
+}
+
+uint16_t BBQ10Keyboard::readRegister16(uint8_t reg) const
+{
+ uint8_t data[2] = {0};
+ // uint8_t low = 0, high = 0;
+ if (m_wire) {
+ m_wire->beginTransmission(m_addr);
+ m_wire->write(reg);
+ m_wire->endTransmission();
+
+ m_wire->requestFrom(m_addr, (uint8_t)2);
+ if (m_wire->available() < 2)
+ return 0;
+ data[0] = m_wire->read();
+ data[1] = m_wire->read();
+ }
+ if (readCallback) {
+ readCallback(m_addr, reg, data, 2);
+ }
+ return (data[1] << 8) | data[0];
+}
+
+void BBQ10Keyboard::writeRegister(uint8_t reg, uint8_t value)
+{
+ uint8_t data[2];
+ data[0] = reg | _WRITE_MASK;
+ data[1] = value;
+
+ if (m_wire) {
+ m_wire->beginTransmission(m_addr);
+ m_wire->write(data, sizeof(uint8_t) * 2);
+ m_wire->endTransmission();
+ }
+ if (writeCallback) {
+ writeCallback(m_addr, data[0], &(data[1]), 1);
+ }
+}
diff --git a/src/input/BBQ10Keyboard.h b/src/input/BBQ10Keyboard.h
new file mode 100644
index 000000000..da6de71d7
--- /dev/null
+++ b/src/input/BBQ10Keyboard.h
@@ -0,0 +1,46 @@
+// Based on arturo182 arduino_bbq10kbd library https://github.com/arturo182/arduino_bbq10kbd
+
+#include "configuration.h"
+#include
+
+class BBQ10Keyboard
+{
+ public:
+ typedef uint8_t (*i2c_com_fptr_t)(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint8_t len);
+
+ enum KeyState { StateIdle = 0, StatePress, StateLongPress, StateRelease };
+
+ struct KeyEvent {
+ char key;
+ KeyState state;
+ };
+
+ BBQ10Keyboard();
+
+ void begin(uint8_t addr = BBQ10_KB_ADDR, TwoWire *wire = &Wire);
+
+ void begin(i2c_com_fptr_t r, i2c_com_fptr_t w, uint8_t addr = BBQ10_KB_ADDR);
+
+ void reset(void);
+
+ void attachInterrupt(uint8_t pin, void (*func)(void)) const;
+ void detachInterrupt(uint8_t pin) const;
+ void clearInterruptStatus(void);
+
+ uint8_t status(void) const;
+ uint8_t keyCount(void) const;
+ KeyEvent keyEvent(void) const;
+
+ float backlight() const;
+ void setBacklight(float value);
+
+ uint8_t readRegister8(uint8_t reg) const;
+ uint16_t readRegister16(uint8_t reg) const;
+ void writeRegister(uint8_t reg, uint8_t value);
+
+ private:
+ TwoWire *m_wire;
+ uint8_t m_addr;
+ i2c_com_fptr_t readCallback;
+ i2c_com_fptr_t writeCallback;
+};
diff --git a/src/input/cardKbI2cImpl.cpp b/src/input/cardKbI2cImpl.cpp
index 44db1d952..e000f36eb 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 != CARDKB_ADDR && cardkb_found.address != TDECK_KB_ADDR) {
+ if (cardkb_found.address == 0x00) {
disable();
return;
}
diff --git a/src/input/kbI2cBase.cpp b/src/input/kbI2cBase.cpp
index f7fd62645..3289a665c 100644
--- a/src/input/kbI2cBase.cpp
+++ b/src/input/kbI2cBase.cpp
@@ -30,7 +30,7 @@ uint8_t read_from_14004(TwoWire *i2cBus, uint8_t reg, uint8_t *data, uint8_t len
int32_t KbI2cBase::runOnce()
{
- if (cardkb_found.address != CARDKB_ADDR && cardkb_found.address != TDECK_KB_ADDR) {
+ if (cardkb_found.address == 0x00) {
// Input device is not detected.
return INT32_MAX;
}
@@ -41,11 +41,19 @@ int32_t KbI2cBase::runOnce()
#ifdef I2C_SDA1
LOG_DEBUG("Using I2C Bus 1 (the second one)\n");
i2cBus = &Wire1;
+ if (cardkb_found.address == BBQ10_KB_ADDR) {
+ Q10keyboard.begin(BBQ10_KB_ADDR, &Wire1);
+ Q10keyboard.setBacklight(0);
+ }
break;
#endif
case ScanI2C::WIRE:
LOG_DEBUG("Using I2C Bus 0 (the first one)\n");
i2cBus = &Wire;
+ if (cardkb_found.address == BBQ10_KB_ADDR) {
+ Q10keyboard.begin(BBQ10_KB_ADDR, &Wire);
+ Q10keyboard.setBacklight(0);
+ }
break;
case ScanI2C::NO_I2C:
default:
@@ -53,7 +61,60 @@ int32_t KbI2cBase::runOnce()
}
}
- if (kb_model == 0x02) {
+ switch (kb_model) {
+ case 0x11: { // BB Q10
+ int keyCount = Q10keyboard.keyCount();
+ while (keyCount--) {
+ const BBQ10Keyboard::KeyEvent key = Q10keyboard.keyEvent();
+ if ((key.key != 0x00) && (key.state == BBQ10Keyboard::StateRelease)) {
+ InputEvent e;
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
+ e.source = this->_originName;
+ switch (key.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.key;
+ break;
+ case 0x12: // sym shift+2
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
+ e.kbchar = 0xb5;
+ break;
+ case 0x18: // sym shift+8
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN;
+ e.kbchar = 0xb6;
+ break;
+ case 0x14: // Left (sym shift+4)
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
+ e.kbchar = 0x00; // tweak for destSelect
+ break;
+ case 0x16: // Right (sym shift+6)
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
+ e.kbchar = 0x00; // tweak for destSelect
+ break;
+ case 0x0d: // Enter
+ case 0x0a: // apparently Enter on Q10 is a line feed instead of carriage return
+ 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.key;
+ break;
+ }
+
+ if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) {
+ this->notifyObservers(&e);
+ }
+ }
+ }
+ break;
+ }
+ case 0x02: {
// RAK14004
uint8_t rDataBuf[8] = {0};
uint8_t PrintDataBuf = 0;
@@ -74,9 +135,12 @@ int32_t KbI2cBase::runOnce()
e.kbchar = PrintDataBuf;
this->notifyObservers(&e);
}
- } else if (kb_model == 0x00 || kb_model == 0x10) {
- // m5 cardkb and T-Deck
- i2cBus->requestFrom(kb_model == 0x00 ? CARDKB_ADDR : TDECK_KB_ADDR, 1);
+ break;
+ }
+ case 0x00: // CARDKB
+ case 0x10: { // T-DECK
+
+ i2cBus->requestFrom((int)cardkb_found.address, 1);
while (i2cBus->available()) {
char c = i2cBus->read();
@@ -93,17 +157,19 @@ int32_t KbI2cBase::runOnce()
break;
case 0xb5: // Up
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
+ e.kbchar = 0xb5;
break;
case 0xb6: // Down
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN;
+ e.kbchar = 0xb6;
break;
case 0xb4: // Left
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
- e.kbchar = c;
+ e.kbchar = 0xb4;
break;
case 0xb7: // Right
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
- e.kbchar = c;
+ e.kbchar = 0xb7;
break;
case 0x0d: // Enter
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
@@ -121,7 +187,9 @@ int32_t KbI2cBase::runOnce()
this->notifyObservers(&e);
}
}
- } else {
+ break;
+ }
+ default:
LOG_WARN("Unknown kb_model 0x%02x\n", kb_model);
}
return 300;
diff --git a/src/input/kbI2cBase.h b/src/input/kbI2cBase.h
index a0a4dd608..59c26e16d 100644
--- a/src/input/kbI2cBase.h
+++ b/src/input/kbI2cBase.h
@@ -1,5 +1,6 @@
#pragma once
+#include "BBQ10Keyboard.h"
#include "InputBroker.h"
#include "Wire.h"
#include "concurrency/OSThread.h"
@@ -16,4 +17,6 @@ class KbI2cBase : public Observable, public concurrency::OST
const char *_originName;
TwoWire *i2cBus = 0;
+
+ BBQ10Keyboard Q10keyboard;
};
diff --git a/src/main.cpp b/src/main.cpp
index 5f0eaca30..09fa62896 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -387,6 +387,10 @@ void setup()
// assign an arbitrary value to distinguish from other models
kb_model = 0x10;
break;
+ case ScanI2C::DeviceType::BBQ10KB:
+ // assign an arbitrary value to distinguish from other models
+ kb_model = 0x11;
+ 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/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp
index 1e605656f..ad71a7112 100644
--- a/src/modules/CannedMessageModule.cpp
+++ b/src/modules/CannedMessageModule.cpp
@@ -65,8 +65,8 @@ CannedMessageModule::CannedMessageModule()
{
if (moduleConfig.canned_message.enabled || CANNED_MESSAGE_MODULE_ENABLE) {
this->loadProtoForModule();
- if ((this->splitConfiguredMessages() <= 0) && (cardkb_found.address != CARDKB_ADDR) &&
- (cardkb_found.address != TDECK_KB_ADDR) && !INPUTBROKER_MATRIX_TYPE && !CANNED_MESSAGE_MODULE_ENABLE) {
+ if ((this->splitConfiguredMessages() <= 0) && (cardkb_found.address == 0x00) && !INPUTBROKER_MATRIX_TYPE &&
+ !CANNED_MESSAGE_MODULE_ENABLE) {
LOG_INFO("CannedMessageModule: No messages are configured. Module is disabled\n");
this->runState = CANNED_MESSAGE_RUN_STATE_DISABLED;
disable();