#include <Arduino.h>
#include "Keypad.h"
#include <LiquidCrystal_I2C.h>
#include <Streaming.h>
Print& cout {Serial};
namespace gc {
constexpr uint8_t rows {4};
constexpr uint8_t cols {4};
constexpr char hexaKeys[rows][cols] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
constexpr const char password[] {"A0815DA1"};
} // namespace gc
enum class States : uint8_t { input, check };
class InputBufferInterface {
public:
virtual bool operator()(const char) = 0;
virtual void clear() = 0;
virtual const char* getBuffer() const = 0;
virtual size_t getIndex() const = 0;
};
template <size_t buffersize, char bsChar = '\0'> class InputBuffer : public InputBufferInterface {
public:
virtual bool operator()(const char key) override {
if (!key) { return false; }
// delete a char from buffer if a backspace iChar is defined
if (key == bsChar) {
index = (index && index <= buffersize) ? index - 1 : 0;
buffer[index] = '\0';
return true;
}
// insert a char into buffer
bool isNewChar = {index < buffersize};
if (isNewChar) {
buffer[index] = key;
++index;
buffer[index] = '\0';
} else {
index = buffersize;
}
return isNewChar;
}
virtual void clear() {
memset(buffer, '\0', index);
index = 0;
}
virtual const char* getBuffer() const override { return buffer; }
virtual size_t getIndex() const override { return index; }
private:
char buffer[buffersize + 1] {'\0'};
size_t index {0};
};
uint8_t rowPins[gc::rows] = {12, 11, 10, 9};
uint8_t colPins[gc::cols] = {8, 7, 6, 5};
Keypad kpInput = Keypad(makeKeymap(gc::hexaKeys), rowPins, colPins, gc::rows, gc::cols);
LiquidCrystal_I2C lcd(0x27, 16, 2);
InputBuffer<16, '*'> input;
void lcdDeleteRow(LiquidCrystal_I2C& disp, uint8_t row = 0) {
lcd.setCursor(0, row);
disp.print(F(" "));
lcd.setCursor(0, row);
}
void setup() {
Serial.begin(115600);
kpInput.setDebounceTime(20);
lcd.init();
lcd.backlight();
lcd.print("Preparing...");
delay(1000);
lcdDeleteRow(lcd, 0);
lcd.cursor_on();
}
States fsm(LiquidCrystal_I2C& disp, InputBufferInterface& iBuffer, char iChar, States iState) {
switch (iState) {
case States::input:
switch (iChar) {
case '#': iState = States::check; break; // it's like a Enter keystroke
default: // Any other keys
if (iBuffer(iChar)) {
disp.cursor_off();
lcdDeleteRow(disp, 0);
size_t num = iBuffer.getIndex();
char hideStr[num + 1] {'\0'};
memset(hideStr, '*', num);
disp.printstr(hideStr);
disp.cursor_on();
}
break;
}
break;
case States::check:
disp.cursor_off();
if (!strcmp(gc::password, iBuffer.getBuffer())) {
cout << "Password OK\r\n";
} else {
cout << "Password wrong\r\n";
}
iBuffer.clear();
lcdDeleteRow(disp, 0);
iState = States::input;
disp.cursor_on();
break;
}
return iState;
}
void loop() {
static States state {States::input};
state = fsm(lcd, input, kpInput.getKey(), state);
}
* = Backspace
# = Enter