diff --git a/src/input/SerialKeyboard.cpp b/src/input/SerialKeyboard.cpp new file mode 100644 index 000000000..b3d11b0fc --- /dev/null +++ b/src/input/SerialKeyboard.cpp @@ -0,0 +1,187 @@ +#include "SerialKeyboard.h" +#include "configuration.h" + +#ifdef INPUTBROKER_SERIAL_TYPE +#define CANNED_MESSAGE_MODULE_ENABLE 1 // in case it's not set in the variant file + +#if INPUTBROKER_SERIAL_TYPE == 1 // It's a Chatter +// 3 SHIFT level (lower case, upper case, numbers), up to 4 repeated presses, button number +unsigned char KeyMap[3][4][10]= {{{'.','a','d','g','j','m','p','t','w',' '}, + {',','b','e','h','k','n','q','u','x',' '}, + {'?','c','f','i','l','o','r','v','y',' '}, + {'1','2','3','4','5','6','s','8','z',' '}}, // low case + {{'!','A','D','G','J','M','P','T','W',' '}, + {'+','B','E','H','K','N','Q','U','X',' '}, + {'-','C','F','I','L','O','R','V','Y',' '}, + {'1','2','3','4','5','6','S','8','Z',' '}}, // upper case + {{'1','2','3','4','5','6','7','8','9','0'}, + {'1','2','3','4','5','6','7','8','9','0'}, + {'1','2','3','4','5','6','7','8','9','0'}, + {'1','2','3','4','5','6','7','8','9','0'}}}; // numbers + +#endif + + +SerialKeyboard::SerialKeyboard(const char *name) : concurrency::OSThread(name) +{ + this->_originName = name; +} + + +void SerialKeyboard::erase(){ + InputEvent e; + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK; + e.kbchar = 0x08; + e.source = this->_originName; + this->notifyObservers(&e); +} + + +int32_t SerialKeyboard::runOnce() +{ + if (!INPUTBROKER_SERIAL_TYPE) { + // Input device is not requested. + return disable(); + } + + if (firstTime) { + // This is the first time the OSThread library has called this function, so do port setup + firstTime = 0; + pinMode(KB_LOAD, OUTPUT); + pinMode(KB_CLK, OUTPUT); + pinMode(KB_DATA, INPUT); + digitalWrite(KB_LOAD, HIGH); + digitalWrite(KB_CLK, LOW); + prevKeys = 0b1111111111111111; + LOG_DEBUG("Serial Keyboard setup\n"); + } + + if (INPUTBROKER_SERIAL_TYPE == 1) { //Chatter V1.0 & V2.0 keypads + // scan for keypresses + // Write pulse to load pin + digitalWrite(KB_LOAD, LOW); + delayMicroseconds(5); + digitalWrite(KB_LOAD, HIGH); + delayMicroseconds(5); + + // Get data from 74HC165 + byte shiftRegister1 = shiftIn(KB_DATA, KB_CLK, LSBFIRST); + byte shiftRegister2 = shiftIn(KB_DATA, KB_CLK, LSBFIRST); + + keys = (shiftRegister1 << 8) + shiftRegister2; + + // Print to serial monitor + //Serial.print (shiftRegister1, BIN); + //Serial.print ("X"); + //Serial.println (shiftRegister2, BIN); + + if (millis()-lastPressTime > 500){ + quickPress = 0; + } + + if (keys < prevKeys) { // a new key has been pressed (and not released), doesn't works for multiple presses at once but shouldn't be a limitation + InputEvent e; + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE; + e.source = this->_originName; + // SELECT OR SEND OR CANCEL EVENT + if (!(shiftRegister2 & (1 << 3))) { + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP; + } + else if (!(shiftRegister2 & (1 << 2))) { + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT; + e.kbchar = 0xb7; + } + else if (!(shiftRegister2 & (1 << 1))) { + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT; + } + else if (!(shiftRegister2 & (1 << 0))) { + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL; + } + + // TEXT INPUT EVENT + else if (!(shiftRegister1 & (1 << 4))) { + keyPressed = 0; + } + else if (!(shiftRegister1 & (1 << 3))) { + keyPressed = 1; + } + else if (!(shiftRegister2 & (1 << 4))) { + keyPressed = 2; + } + else if (!(shiftRegister1 & (1 << 5))) { + keyPressed = 3; + } + else if (!(shiftRegister1 & (1 << 2))) { + keyPressed = 4; + } + else if (!(shiftRegister2 & (1 << 5))) { + keyPressed = 5; + } + else if (!(shiftRegister1 & (1 << 6))) { + keyPressed = 6; + } + else if (!(shiftRegister1 & (1 << 1))) { + keyPressed = 7; + } + else if (!(shiftRegister2 & (1 << 6))) { + keyPressed = 8; + } + else if (!(shiftRegister1 & (1 << 0))) { + keyPressed = 9; + } + // BACKSPACE or TAB + else if (!(shiftRegister1 & (1 << 7))) { + if (shift == 0 || shift ==2){ // BACKSPACE + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK; + e.kbchar = 0x08; + } else { // shift = 1 => TAB + e.inputEvent = ANYKEY; + e.kbchar = 0x09; + } + } + // SHIFT + else if (!(shiftRegister2 & (1 << 7))) { + keyPressed = 10; + } + + + if (keyPressed < 11){ + if (keyPressed == lastKeyPressed && millis()-lastPressTime < 500){ + quickPress += 1; + if (quickPress > 3){ + quickPress = 0; + } + } + if (keyPressed != lastKeyPressed){ + quickPress = 0; + } + if (keyPressed < 10){ // if it's a letter + if (keyPressed == lastKeyPressed && millis()-lastPressTime < 500){ + erase(); + } + e.inputEvent = ANYKEY; + e.kbchar = char(KeyMap[shift][quickPress][keyPressed]); + } + else { //then it's shift + shift += 1; + if (shift > 2){ + shift = 0; + } + } + lastPressTime = millis(); + lastKeyPressed = keyPressed; + keyPressed = 13; + } + + if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) { + this->notifyObservers(&e); + } + } + prevKeys = keys; + + } + return 50; +} + + +#endif // INPUTBROKER_SERIAL_TYPE \ No newline at end of file diff --git a/src/input/SerialKeyboard.h b/src/input/SerialKeyboard.h new file mode 100644 index 000000000..1480c4d58 --- /dev/null +++ b/src/input/SerialKeyboard.h @@ -0,0 +1,25 @@ +#pragma once + +#include "InputBroker.h" +#include "concurrency/OSThread.h" + +class SerialKeyboard : public Observable, public concurrency::OSThread +{ + public: + explicit SerialKeyboard(const char *name); + + protected: + virtual int32_t runOnce() override; + void erase(); + + private: + const char *_originName; + bool firstTime = 1; + int prevKeys = 0; + int keys = 0; + int shift = 0; + int keyPressed = 13; + int lastKeyPressed = 13; + int quickPress = 0; + unsigned long lastPressTime = 0; +}; \ No newline at end of file diff --git a/src/input/SerialKeyboardImpl.cpp b/src/input/SerialKeyboardImpl.cpp new file mode 100644 index 000000000..579356f47 --- /dev/null +++ b/src/input/SerialKeyboardImpl.cpp @@ -0,0 +1,21 @@ +#include "configuration.h" +#include "InputBroker.h" +#include "SerialKeyboardImpl.h" + +#ifdef INPUTBROKER_SERIAL_TYPE + +SerialKeyboardImpl *aSerialKeyboardImpl; + +SerialKeyboardImpl::SerialKeyboardImpl() : SerialKeyboard("serialKB") {} + +void SerialKeyboardImpl::init() +{ + if (!INPUTBROKER_SERIAL_TYPE) { + disable(); + return; + } + + inputBroker->registerSource(this); +} + +#endif // INPUTBROKER_SERIAL_TYPE \ No newline at end of file diff --git a/src/input/SerialKeyboardImpl.h b/src/input/SerialKeyboardImpl.h new file mode 100644 index 000000000..7f62aa420 --- /dev/null +++ b/src/input/SerialKeyboardImpl.h @@ -0,0 +1,19 @@ +#pragma once +#include "SerialKeyboard.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 SerialKeyboardImpl : public SerialKeyboard +{ + public: + SerialKeyboardImpl(); + void init(); +}; + +extern SerialKeyboardImpl *aSerialKeyboardImpl; \ No newline at end of file diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 5a0e36fea..40352a56e 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/SerialKeyboardImpl.h" #endif #if !MESHTASTIC_EXCLUDE_ADMIN #include "modules/AdminModule.h" @@ -149,6 +150,10 @@ void setupModules() kbMatrixImpl = new KbMatrixImpl(); kbMatrixImpl->init(); #endif // INPUTBROKER_MATRIX_TYPE +#ifdef INPUTBROKER_SERIAL_TYPE + aSerialKeyboardImpl = new SerialKeyboardImpl(); + aSerialKeyboardImpl->init(); +#endif // INPUTBROKER_MATRIX_TYPE #endif // HAS_BUTTON #if ARCH_PORTDUINO aLinuxInputImpl = new LinuxInputImpl(); diff --git a/variants/chatter2/variant.h b/variants/chatter2/variant.h index 90faa1d7b..52aceafcd 100644 --- a/variants/chatter2/variant.h +++ b/variants/chatter2/variant.h @@ -34,7 +34,7 @@ // Buzzer #define PIN_BUZZER 19 // Buttons -#define BUTTON_PIN 36 // Use the WAKE button as the user button +//#define BUTTON_PIN 36 // Use the WAKE button as the user button // I2C // #define I2C_SCL 27 // #define I2C_SDA 26 @@ -91,6 +91,13 @@ #define GPS_TX_PIN 13 #define GPS_RX_PIN 2 +// keyboard +#define INPUTBROKER_SERIAL_TYPE 1 +#define KB_LOAD 21 // load values from the switch and store in shift register +#define KB_CLK 22 // clock pin for serial data out +#define KB_DATA 23 // data pin +#define CANNED_MESSAGE_MODULE_ENABLE 1 + ///////////////////////////////////////////////////////////////////////////////// // // // You should have no need to modify the code below, nor in pins_arduino.h //