Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for Chatter keypad #4022

Merged
merged 7 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 187 additions & 0 deletions src/input/SerialKeyboard.cpp
Original file line number Diff line number Diff line change
@@ -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
25 changes: 25 additions & 0 deletions src/input/SerialKeyboard.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

#include "InputBroker.h"
#include "concurrency/OSThread.h"

class SerialKeyboard : public Observable<const InputEvent *>, 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;
};
21 changes: 21 additions & 0 deletions src/input/SerialKeyboardImpl.cpp
Original file line number Diff line number Diff line change
@@ -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
19 changes: 19 additions & 0 deletions src/input/SerialKeyboardImpl.h
Original file line number Diff line number Diff line change
@@ -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;
5 changes: 5 additions & 0 deletions src/modules/Modules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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();
Expand Down
9 changes: 8 additions & 1 deletion variants/chatter2/variant.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 //
Expand Down
Loading