large refactor
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@
|
|||||||
.vscode/c_cpp_properties.json
|
.vscode/c_cpp_properties.json
|
||||||
.vscode/launch.json
|
.vscode/launch.json
|
||||||
.vscode/ipch
|
.vscode/ipch
|
||||||
|
.vscode/settings.json
|
||||||
|
@@ -17,6 +17,7 @@ lib_deps =
|
|||||||
https://github.com/m5stack/M5EPD
|
https://github.com/m5stack/M5EPD
|
||||||
https://github.com/arduino-libraries/NTPClient
|
https://github.com/arduino-libraries/NTPClient
|
||||||
https://github.com/tobozo/YAMLDuino
|
https://github.com/tobozo/YAMLDuino
|
||||||
|
https://github.com/josephlarralde/ArduinoEventEmitter
|
||||||
LovyanGFX
|
LovyanGFX
|
||||||
bblanchon/ArduinoJson
|
bblanchon/ArduinoJson
|
||||||
knolleary/PubSubClient
|
knolleary/PubSubClient
|
||||||
|
142
src/button.hpp
Normal file
142
src/button.hpp
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
#ifndef _KEYPADBUTTON_H_
|
||||||
|
#define _KEYPADBUTTON_H_
|
||||||
|
|
||||||
|
#define LGFX_AUTODETECT
|
||||||
|
#include <LovyanGFX.hpp>
|
||||||
|
|
||||||
|
class Keypad;
|
||||||
|
|
||||||
|
typedef void (*BUTTON_ON_PRESSED_CALLBACK)(void *, int);
|
||||||
|
|
||||||
|
class KeypadButton
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KeypadButton(String label, int value, bool enabled, BUTTON_ON_PRESSED_CALLBACK on_pressed) : label{label}, value{value}, enabled{enabled}, on_pressed{on_pressed} {}
|
||||||
|
KeypadButton(String label, int value, bool enabled, float x, float y, BUTTON_ON_PRESSED_CALLBACK on_pressed) : label{label}, value{value}, enabled{enabled}, x{x}, y{y}, on_pressed{on_pressed} {}
|
||||||
|
KeypadButton(String label, int value, bool enabled, float x, float y, int width, int height, BUTTON_ON_PRESSED_CALLBACK on_pressed) : label{label}, value{value}, enabled{enabled}, x{x}, y{y}, width{width}, height{height}, on_pressed{on_pressed} {}
|
||||||
|
|
||||||
|
void draw(LGFX *canvas)
|
||||||
|
{
|
||||||
|
if (!dirty)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dirty = false;
|
||||||
|
|
||||||
|
float MARGIN = 10.0;
|
||||||
|
float HALF_MARGIN = MARGIN / 2.0;
|
||||||
|
|
||||||
|
float new_x = x + HALF_MARGIN;
|
||||||
|
float new_y = y + HALF_MARGIN;
|
||||||
|
|
||||||
|
float new_width = width - MARGIN;
|
||||||
|
float new_height = height - MARGIN;
|
||||||
|
|
||||||
|
int bg_color = enabled ? TFT_WHITE : TFT_DARKGRAY;
|
||||||
|
int fg_color = enabled ? TFT_BLACK : TFT_WHITE;
|
||||||
|
|
||||||
|
canvas->setEpdMode(epd_fastest);
|
||||||
|
canvas->setColor(bg_color);
|
||||||
|
canvas->fillRect(new_x + 1, new_y + 1, new_width - 2, new_height - 2);
|
||||||
|
|
||||||
|
canvas->setTextSize(4.0);
|
||||||
|
canvas->setBaseColor(bg_color);
|
||||||
|
canvas->setTextColor(fg_color);
|
||||||
|
canvas->setCursor(x + (width / 2) - (canvas->fontWidth() / 2), y + (height / 2) - (canvas->fontHeight() / 2));
|
||||||
|
canvas->print(label);
|
||||||
|
|
||||||
|
canvas->setColor(TFT_BLACK);
|
||||||
|
canvas->drawFastHLine(new_x, new_y, new_width);
|
||||||
|
canvas->drawFastHLine(new_x, new_y + new_height, new_width);
|
||||||
|
canvas->drawFastVLine(new_x, new_y, new_height);
|
||||||
|
canvas->drawFastVLine(new_x + new_width, new_y, new_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
~KeypadButton() = default;
|
||||||
|
|
||||||
|
String get_label()
|
||||||
|
{
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
void set_label(String value)
|
||||||
|
{
|
||||||
|
label = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_value()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
void set_value(int value)
|
||||||
|
{
|
||||||
|
this->value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_width()
|
||||||
|
{
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
void set_width(int value)
|
||||||
|
{
|
||||||
|
width = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_x()
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
void set_x(int value)
|
||||||
|
{
|
||||||
|
x = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_y()
|
||||||
|
{
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
void set_y(int value)
|
||||||
|
{
|
||||||
|
y = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable()
|
||||||
|
{
|
||||||
|
if (!enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
enabled = false;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
void enable()
|
||||||
|
{
|
||||||
|
if (enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
enabled = true;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(Keypad *keypad, uint16_t touch_x, uint16_t touch_y)
|
||||||
|
{
|
||||||
|
if (enabled && touch_x > x && touch_x < x + width && touch_y > y && touch_y < y + height)
|
||||||
|
{
|
||||||
|
on_pressed(keypad, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
String label;
|
||||||
|
BUTTON_ON_PRESSED_CALLBACK on_pressed = NULL;
|
||||||
|
bool enabled = true;
|
||||||
|
bool dirty = true;
|
||||||
|
|
||||||
|
int value = 0;
|
||||||
|
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
|
||||||
|
float x = 0;
|
||||||
|
float y = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
28
src/display.hpp
Normal file
28
src/display.hpp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#ifndef _DISPLAY_H_
|
||||||
|
#define _DISPLAY_H_
|
||||||
|
|
||||||
|
#define LGFX_M5PAPER
|
||||||
|
#define LGFX_USE_V1
|
||||||
|
#define LGFX_AUTODETECT
|
||||||
|
|
||||||
|
#include <LovyanGFX.hpp>
|
||||||
|
|
||||||
|
class Display : public LGFX
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
LGFX::init();
|
||||||
|
setRotation(1);
|
||||||
|
|
||||||
|
setEpdMode(epd_mode_t::epd_quality);
|
||||||
|
fillScreen(TFT_WHITE);
|
||||||
|
waitDisplay();
|
||||||
|
fillScreen(TFT_WHITE);
|
||||||
|
setEpdMode(epd_mode_t::epd_fast);
|
||||||
|
|
||||||
|
setColor(TFT_BLACK);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
132
src/input.hpp
Normal file
132
src/input.hpp
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
#ifndef _INPUT_H_
|
||||||
|
#define _INPUT_H_
|
||||||
|
|
||||||
|
#define LGFX_AUTODETECT
|
||||||
|
#include <LovyanGFX.hpp>
|
||||||
|
|
||||||
|
class Input
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Input(String value) : value{value}, old_value{value} {}
|
||||||
|
Input(String value, float x, float y) : value{value}, x{x}, y{y} {}
|
||||||
|
Input(String value, float x, float y, int width, int height) : value{value}, x{x}, y{y}, width{width}, height{height} {}
|
||||||
|
|
||||||
|
~Input() = default;
|
||||||
|
|
||||||
|
String get_value()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
void set_value(String value)
|
||||||
|
{
|
||||||
|
this->value = value;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(String value)
|
||||||
|
{
|
||||||
|
this->value += value;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(int value)
|
||||||
|
{
|
||||||
|
this->append(String(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
this->value = "";
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned int length()
|
||||||
|
{
|
||||||
|
return this->value.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_width()
|
||||||
|
{
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
void set_width(int value)
|
||||||
|
{
|
||||||
|
width = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_x()
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
void set_x(int value)
|
||||||
|
{
|
||||||
|
x = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_y()
|
||||||
|
{
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
void set_y(int value)
|
||||||
|
{
|
||||||
|
y = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(LGFX *canvas)
|
||||||
|
{
|
||||||
|
if (!dirty)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dirty = false;
|
||||||
|
|
||||||
|
float MARGIN = 10.0;
|
||||||
|
float HALF_MARGIN = MARGIN / 2.0;
|
||||||
|
|
||||||
|
float new_x = x + HALF_MARGIN;
|
||||||
|
float new_y = y + HALF_MARGIN;
|
||||||
|
|
||||||
|
float new_width = width - MARGIN;
|
||||||
|
float new_height = height - MARGIN;
|
||||||
|
|
||||||
|
int bg_color = TFT_WHITE;
|
||||||
|
int fg_color = TFT_BLACK;
|
||||||
|
|
||||||
|
canvas->setTextSize(8.0);
|
||||||
|
|
||||||
|
canvas->setEpdMode(epd_fastest);
|
||||||
|
canvas->setColor(bg_color);
|
||||||
|
canvas->fillRect(new_x + 1, new_y + 1, new_width - 2, new_height - 2);
|
||||||
|
|
||||||
|
canvas->setBaseColor(bg_color);
|
||||||
|
canvas->setTextColor(fg_color);
|
||||||
|
canvas->setCursor(x + (width / 2) - ((canvas->fontWidth() * value.length()) / 2), y + (height / 2) - (canvas->fontHeight() / 2));
|
||||||
|
canvas->setEpdMode(epd_text);
|
||||||
|
canvas->print(value);
|
||||||
|
|
||||||
|
if (length() == 0)
|
||||||
|
{
|
||||||
|
canvas->setEpdMode(epd_fastest);
|
||||||
|
canvas->setColor(TFT_BLACK);
|
||||||
|
canvas->drawFastHLine(new_x, new_y, new_width);
|
||||||
|
canvas->drawFastHLine(new_x, new_y + new_height, new_width);
|
||||||
|
canvas->drawFastVLine(new_x, new_y, new_height);
|
||||||
|
canvas->drawFastVLine(new_x + new_width, new_y, new_height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
LGFX *canvas;
|
||||||
|
String value;
|
||||||
|
String old_value;
|
||||||
|
|
||||||
|
bool dirty = true;
|
||||||
|
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
|
||||||
|
float x = 0;
|
||||||
|
float y = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
198
src/keypad.cpp
198
src/keypad.cpp
@@ -1,198 +0,0 @@
|
|||||||
#define LGFX_M5PAPER
|
|
||||||
#define LGFX_USE_V1
|
|
||||||
#define LGFX_AUTODETECT
|
|
||||||
|
|
||||||
#include "keypad.hpp"
|
|
||||||
#include <M5EPD.h>
|
|
||||||
#include <LovyanGFX.hpp>
|
|
||||||
|
|
||||||
static LGFX gfx;
|
|
||||||
|
|
||||||
static const float_t BUTTON_WIDTH = 115;
|
|
||||||
static const float_t BUTTON_HEIGHT = 115;
|
|
||||||
|
|
||||||
int Keypad::get_button_index_from_touch(int x, int y)
|
|
||||||
{
|
|
||||||
for (int index = 0; index <= 11; index++)
|
|
||||||
{
|
|
||||||
float_t cx = (125 * (index % 3)) + 15;
|
|
||||||
float_t cy = (125 * (index / 3)) + 20;
|
|
||||||
|
|
||||||
if (x > cx && x < cx + BUTTON_WIDTH && y > cy && y < cy + BUTTON_HEIGHT)
|
|
||||||
{
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Keypad::draw_button(int i)
|
|
||||||
{
|
|
||||||
gfx.setTextSize(FONT_SIZE_NORMAL);
|
|
||||||
|
|
||||||
String label = String(i + 1);
|
|
||||||
if (i == 9)
|
|
||||||
{
|
|
||||||
label = String("<");
|
|
||||||
}
|
|
||||||
if (i == 10)
|
|
||||||
{
|
|
||||||
label = String("0");
|
|
||||||
}
|
|
||||||
if (i == 11)
|
|
||||||
{
|
|
||||||
label = String(">");
|
|
||||||
}
|
|
||||||
|
|
||||||
float_t x = (125 * (i % 3)) + 15;
|
|
||||||
float_t y = (125 * (i / 3)) + 20;
|
|
||||||
|
|
||||||
gfx.setCursor(x + (115 / 2) - (gfx.fontWidth() / 2), y + (115 / 2) - (gfx.fontHeight() / 2));
|
|
||||||
gfx.print(label);
|
|
||||||
|
|
||||||
gfx.setColor(TFT_BLACK);
|
|
||||||
gfx.drawFastHLine(x, y, BUTTON_WIDTH);
|
|
||||||
gfx.drawFastHLine(x, y + BUTTON_HEIGHT, BUTTON_WIDTH);
|
|
||||||
gfx.drawFastVLine(x, y, BUTTON_HEIGHT);
|
|
||||||
gfx.drawFastVLine(x + BUTTON_WIDTH, y, BUTTON_HEIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Keypad::draw_input_field()
|
|
||||||
{
|
|
||||||
int x = 405;
|
|
||||||
int y = 20;
|
|
||||||
|
|
||||||
gfx.setColor(TFT_BLACK);
|
|
||||||
gfx.drawFastHLine(x, y, 520);
|
|
||||||
gfx.drawFastHLine(x, y + 115, 520);
|
|
||||||
gfx.drawFastVLine(x, y, 115);
|
|
||||||
gfx.drawFastVLine(x + 520, y, 115);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Keypad::draw_input()
|
|
||||||
{
|
|
||||||
int x = 405;
|
|
||||||
int y = 20;
|
|
||||||
|
|
||||||
gfx.setEpdMode(epd_mode_t::epd_text);
|
|
||||||
gfx.setColor(TFT_BLACK);
|
|
||||||
gfx.setTextSize(FONT_SIZE_LARGE);
|
|
||||||
|
|
||||||
if (input.length() < oldinput.length())
|
|
||||||
{
|
|
||||||
gfx.setColor(TFT_WHITE);
|
|
||||||
gfx.fillRect(x + 1, y + 1, 519, 114);
|
|
||||||
gfx.setColor(TFT_BLACK);
|
|
||||||
}
|
|
||||||
|
|
||||||
gfx.setCursor(x + (520 / 2) - ((gfx.fontWidth() * input.length()) / 2), y + (115 / 2) - (gfx.fontHeight() / 2));
|
|
||||||
String keypad = "";
|
|
||||||
for (int i = 0; i < input.length(); i++)
|
|
||||||
{
|
|
||||||
keypad += "*";
|
|
||||||
}
|
|
||||||
gfx.print(keypad);
|
|
||||||
gfx.setEpdMode(epd_mode_t::epd_fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Keypad::draw()
|
|
||||||
{
|
|
||||||
gfx.setEpdMode(epd_mode_t::epd_quality);
|
|
||||||
gfx.fillScreen(TFT_WHITE);
|
|
||||||
gfx.waitDisplay();
|
|
||||||
gfx.fillScreen(TFT_WHITE);
|
|
||||||
gfx.setEpdMode(epd_mode_t::epd_fast);
|
|
||||||
|
|
||||||
gfx.setColor(TFT_BLACK);
|
|
||||||
gfx.waitDisplay();
|
|
||||||
|
|
||||||
for (int i = 0; i <= 11; i++)
|
|
||||||
{
|
|
||||||
this->draw_button(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
draw_input_field();
|
|
||||||
draw_input();
|
|
||||||
}
|
|
||||||
|
|
||||||
Keypad::Keypad()
|
|
||||||
{
|
|
||||||
gfx.init();
|
|
||||||
gfx.setRotation(1);
|
|
||||||
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Keypad::write(String payload)
|
|
||||||
{
|
|
||||||
payload.replace("_", " ");
|
|
||||||
|
|
||||||
gfx.setTextSize(FONT_SIZE_NORMAL);
|
|
||||||
|
|
||||||
gfx.setColor(TFT_WHITE);
|
|
||||||
gfx.fillRect(405, 155, gfx.fontWidth() * 10, gfx.fontHeight());
|
|
||||||
gfx.setColor(TFT_BLACK);
|
|
||||||
|
|
||||||
gfx.setCursor(405, 155);
|
|
||||||
payload[0] = toupper(payload[0]);
|
|
||||||
gfx.printf("%s", payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<String> Keypad::check_input()
|
|
||||||
{
|
|
||||||
if (M5.TP.available())
|
|
||||||
{
|
|
||||||
M5.TP.update();
|
|
||||||
if (M5.TP.isFingerUp())
|
|
||||||
{
|
|
||||||
last_index = -1;
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
tp_finger_t fingerItem = M5.TP.readFinger(0);
|
|
||||||
|
|
||||||
int i = get_button_index_from_touch(fingerItem.x, fingerItem.y);
|
|
||||||
if (last_index != i)
|
|
||||||
{
|
|
||||||
last_index = i;
|
|
||||||
if (i == 9)
|
|
||||||
{
|
|
||||||
// revert
|
|
||||||
input = "";
|
|
||||||
}
|
|
||||||
else if (i == 10)
|
|
||||||
{
|
|
||||||
if (input.length() >= 6)
|
|
||||||
return std::nullopt;
|
|
||||||
input += String(0);
|
|
||||||
}
|
|
||||||
else if (i == 11)
|
|
||||||
{
|
|
||||||
// submit
|
|
||||||
String retval = input;
|
|
||||||
input = "";
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (input.length() >= 6)
|
|
||||||
return std::nullopt;
|
|
||||||
input += String(i + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<String> Keypad::get_input()
|
|
||||||
{
|
|
||||||
auto value = check_input();
|
|
||||||
if (oldinput != input)
|
|
||||||
{
|
|
||||||
draw_input();
|
|
||||||
oldinput = input;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
109
src/keypad.hpp
109
src/keypad.hpp
@@ -1,31 +1,104 @@
|
|||||||
#ifndef _KEYPAD_H_
|
#ifndef _KEYPAD_H_
|
||||||
#define _KEYPAD_H_
|
#define _KEYPAD_H_
|
||||||
|
|
||||||
#include <WString.h>
|
#include <EventEmitter.h>
|
||||||
#include <optional>
|
#include "button.hpp"
|
||||||
|
|
||||||
constexpr float FONT_SIZE_NORMAL = 4.0;
|
constexpr int BUTTON_ROWS = 4;
|
||||||
constexpr float FONT_SIZE_LARGE = 8.0;
|
constexpr int BUTTON_COLS = 3;
|
||||||
|
constexpr int BUTTON_COUNT = BUTTON_ROWS * BUTTON_COLS;
|
||||||
|
|
||||||
class Keypad
|
class Keypad : public EventEmitter<int>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Keypad();
|
Keypad(LGFX *canvas) : EventEmitter<int>(), canvas{canvas}
|
||||||
int get_button_index_from_touch(int x, int y);
|
{
|
||||||
void draw();
|
int BUTTON_SIZE = canvas->height() / BUTTON_ROWS;
|
||||||
void write(String);
|
width = BUTTON_SIZE * BUTTON_COLS;
|
||||||
std::optional<String> get_input();
|
|
||||||
|
for (int i = 0; i < BUTTON_COUNT; i++)
|
||||||
|
{
|
||||||
|
float x = BUTTON_SIZE * (i % BUTTON_COLS);
|
||||||
|
float y = BUTTON_SIZE * (i / BUTTON_COLS);
|
||||||
|
|
||||||
|
String lbl = String(i + 1);
|
||||||
|
bool enabled = true;
|
||||||
|
|
||||||
|
if (i == 9)
|
||||||
|
{
|
||||||
|
lbl = String("<");
|
||||||
|
}
|
||||||
|
if (i == 10)
|
||||||
|
{
|
||||||
|
lbl = String("0");
|
||||||
|
}
|
||||||
|
if (i == 11)
|
||||||
|
{
|
||||||
|
lbl = String(">");
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
buttons.push_back(new KeypadButton(lbl, i, enabled, x, y, BUTTON_SIZE, BUTTON_SIZE, Keypad::on_button_pressed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~Keypad() = default;
|
||||||
|
|
||||||
|
inline unsigned int get_width()
|
||||||
|
{
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(tp_finger_t touch)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < BUTTON_COUNT; i++)
|
||||||
|
{
|
||||||
|
buttons[i]->update(this, touch.x, touch.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < BUTTON_COUNT; i++)
|
||||||
|
{
|
||||||
|
buttons[i]->draw(canvas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable(int index)
|
||||||
|
{
|
||||||
|
if (index >= 0 && index < BUTTON_COUNT)
|
||||||
|
{
|
||||||
|
buttons[index]->disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_enabled(int index, bool enabled)
|
||||||
|
{
|
||||||
|
if (index >= 0 && index < BUTTON_COUNT)
|
||||||
|
{
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
buttons[index]->enable();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buttons[index]->disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int last_index = -1;
|
LGFX *canvas;
|
||||||
String oldinput = "";
|
std::vector<KeypadButton *> buttons;
|
||||||
String input = "";
|
bool touching = false;
|
||||||
|
unsigned int width = 0;
|
||||||
|
|
||||||
void draw_button(int index);
|
static void on_button_pressed(void *context, int value)
|
||||||
void draw_input_field();
|
{
|
||||||
void draw_input();
|
auto c = static_cast<Keypad *>(context);
|
||||||
|
c->emit("pressed", c->buttons[value]->get_value());
|
||||||
std::optional<String> check_input();
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
176
src/main.cpp
176
src/main.cpp
@@ -3,21 +3,42 @@
|
|||||||
|
|
||||||
#define YAML_DISABLE_CJSON
|
#define YAML_DISABLE_CJSON
|
||||||
|
|
||||||
#include <WiFi.h>
|
#define LGFX_M5PAPER
|
||||||
|
#define LGFX_USE_V1
|
||||||
|
#define LGFX_AUTODETECT
|
||||||
|
|
||||||
#include <M5EPD.h>
|
#include <M5EPD.h>
|
||||||
|
#include <WiFi.h>
|
||||||
#include <NTPClient.h>
|
#include <NTPClient.h>
|
||||||
#include <HTTPClient.h>
|
#include <HTTPClient.h>
|
||||||
|
|
||||||
#include <totp.hpp>
|
#include <totp.hpp>
|
||||||
|
|
||||||
|
#include "display.hpp"
|
||||||
|
#include "keypad.hpp"
|
||||||
|
#include "input.hpp"
|
||||||
|
#include "status.hpp"
|
||||||
|
#include "touch.hpp"
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
#include "mqtt.hpp"
|
#include "mqtt.hpp"
|
||||||
#include "keypad.hpp"
|
|
||||||
|
|
||||||
static WiFiUDP ntpUDP;
|
static WiFiUDP ntpUDP;
|
||||||
static NTPClient timeClient(ntpUDP);
|
static NTPClient timeClient(ntpUDP);
|
||||||
static MQTT *mqtt;
|
static MQTT *mqtt;
|
||||||
|
|
||||||
|
static Display display;
|
||||||
static Keypad *keypad;
|
static Keypad *keypad;
|
||||||
|
static Status *status;
|
||||||
|
static Input *input;
|
||||||
|
static Touch *touch;
|
||||||
|
|
||||||
|
void keypad_state(bool enabled)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 11; i++)
|
||||||
|
{
|
||||||
|
keypad->set_enabled(i, enabled);
|
||||||
|
}
|
||||||
|
keypad->set_enabled(11, !enabled);
|
||||||
|
}
|
||||||
|
|
||||||
static bool is_disarmed = false;
|
static bool is_disarmed = false;
|
||||||
|
|
||||||
@@ -29,13 +50,13 @@ void mqtt_callback(char *topic, byte *payload, unsigned int length)
|
|||||||
String state((const char *)payload);
|
String state((const char *)payload);
|
||||||
is_disarmed = state == "disarmed";
|
is_disarmed = state == "disarmed";
|
||||||
|
|
||||||
if (state == "armed_away")
|
state.replace("_", " ");
|
||||||
{
|
state[0] = toupper(state[0]);
|
||||||
keypad->draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.println(state);
|
Serial.println(state);
|
||||||
keypad->write(state);
|
status->set(state);
|
||||||
|
status->draw(&display);
|
||||||
|
|
||||||
|
keypad_state(!is_disarmed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lock()
|
void lock()
|
||||||
@@ -51,23 +72,37 @@ void unlock()
|
|||||||
mqtt->send("DISARM");
|
mqtt->send("DISARM");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void submit(String code)
|
||||||
|
{
|
||||||
|
uint32_t newCode = getCodeFromTimestamp(timeClient.getEpochTime());
|
||||||
|
uint32_t input = code.toInt();
|
||||||
|
|
||||||
|
if (newCode != input)
|
||||||
|
{
|
||||||
|
status->set("Invalid");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
bool isConnecting = false;
|
bool isConnecting = false;
|
||||||
void initWifi(Settings *settings)
|
void initWifi(Settings *settings)
|
||||||
{
|
{
|
||||||
if (!settings)
|
if (!settings)
|
||||||
{
|
{
|
||||||
keypad->write("no settings");
|
status->set("No settings");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isConnecting)
|
if (isConnecting)
|
||||||
{
|
{
|
||||||
keypad->write("already connecting");
|
status->set("Already connecting");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
isConnecting = true;
|
isConnecting = true;
|
||||||
keypad->write("connecting");
|
status->set("Connecting");
|
||||||
|
|
||||||
WiFi.begin(settings->gettext("wifi:ssid"), settings->gettext("wifi:password"));
|
WiFi.begin(settings->gettext("wifi:ssid"), settings->gettext("wifi:password"));
|
||||||
|
|
||||||
@@ -89,11 +124,11 @@ void initWifi(Settings *settings)
|
|||||||
Serial.print("Local IP: ");
|
Serial.print("Local IP: ");
|
||||||
Serial.println(WiFi.localIP());
|
Serial.println(WiFi.localIP());
|
||||||
isConnecting = false;
|
isConnecting = false;
|
||||||
keypad->write("connected");
|
status->set("Connected");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
keypad->write("failed to connect");
|
status->set("Failed to connect");
|
||||||
isConnecting = false;
|
isConnecting = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -112,49 +147,116 @@ void initTOTP(Settings *settings)
|
|||||||
TOTP(base32_key, 20, 30);
|
TOTP(base32_key, 20, 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void p(int v)
|
||||||
|
{
|
||||||
|
switch (v)
|
||||||
|
{
|
||||||
|
case 9:
|
||||||
|
input->clear();
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
input->append("0");
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
lock();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
input->append(v + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input->length() >= 6)
|
||||||
|
{
|
||||||
|
// verify
|
||||||
|
input->draw(&display); // force one more redraw for the last character
|
||||||
|
submit(input->get_value());
|
||||||
|
input->clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
M5.begin();
|
M5.begin();
|
||||||
|
display.init();
|
||||||
keypad = new Keypad();
|
touch = new Touch(M5.TP);
|
||||||
keypad->write("loading");
|
status = new Status("Loading");
|
||||||
|
keypad = new Keypad(&display);
|
||||||
|
keypad->addListener("pressed", p);
|
||||||
|
input = new Input("", keypad->get_width(), 0, display.width() - keypad->get_width(), 115);
|
||||||
|
|
||||||
auto settings = new Settings();
|
auto settings = new Settings();
|
||||||
|
|
||||||
if (!settings)
|
if (!settings)
|
||||||
{
|
{
|
||||||
keypad->write("unable to load settings");
|
status->set("Settings Error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mqtt = new MQTT(settings, mqtt_callback);
|
mqtt = new MQTT(settings, mqtt_callback);
|
||||||
|
|
||||||
initWifi(settings);
|
initWifi(settings);
|
||||||
mqtt->connect();
|
mqtt->connect();
|
||||||
timeClient.begin();
|
timeClient.begin();
|
||||||
initTOTP(settings);
|
initTOTP(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
void submit(String code)
|
|
||||||
{
|
|
||||||
uint32_t newCode = getCodeFromTimestamp(timeClient.getEpochTime());
|
|
||||||
uint32_t input = code.toInt();
|
|
||||||
|
|
||||||
if (newCode == input)
|
|
||||||
{
|
|
||||||
unlock();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
timeClient.update();
|
timeClient.update();
|
||||||
mqtt->loop();
|
mqtt->loop();
|
||||||
|
|
||||||
auto value = keypad->get_input();
|
auto tap = touch->tap();
|
||||||
if (value.has_value())
|
if (tap.has_value())
|
||||||
submit(value.value());
|
keypad->update(tap.value());
|
||||||
|
|
||||||
|
keypad->draw();
|
||||||
|
input->draw(&display);
|
||||||
|
status->draw(&display);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static Keypad *keypad;
|
||||||
|
|
||||||
|
// void setup()
|
||||||
|
// {
|
||||||
|
// M5.begin();
|
||||||
|
|
||||||
|
// keypad = new Keypad();
|
||||||
|
// status->set("loading");
|
||||||
|
|
||||||
|
// auto settings = new Settings();
|
||||||
|
|
||||||
|
// if (!settings)
|
||||||
|
// {
|
||||||
|
// status->set("unable to load settings");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// mqtt = new MQTT(settings, mqtt_callback);
|
||||||
|
|
||||||
|
// initWifi(settings);
|
||||||
|
// mqtt->connect();
|
||||||
|
// timeClient.begin();
|
||||||
|
// initTOTP(settings);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void submit(String code)
|
||||||
|
// {
|
||||||
|
// uint32_t newCode = getCodeFromTimestamp(timeClient.getEpochTime());
|
||||||
|
// uint32_t input = code.toInt();
|
||||||
|
|
||||||
|
// if (newCode == input)
|
||||||
|
// {
|
||||||
|
// unlock();
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// lock();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void loop()
|
||||||
|
// {
|
||||||
|
// timeClient.update();
|
||||||
|
// mqtt->loop();
|
||||||
|
|
||||||
|
// auto value = keypad->get_input();
|
||||||
|
// if (value.has_value())
|
||||||
|
// submit(value.value());
|
||||||
|
// }
|
||||||
|
65
src/status.hpp
Normal file
65
src/status.hpp
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#ifndef _STATUS_H_
|
||||||
|
#define _STATUS_H_
|
||||||
|
|
||||||
|
#define LGFX_AUTODETECT
|
||||||
|
#include <LovyanGFX.hpp>
|
||||||
|
|
||||||
|
class Status
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Status(String value) : value{value}
|
||||||
|
{
|
||||||
|
old_length = value.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
~Status() = default;
|
||||||
|
|
||||||
|
void set(String value)
|
||||||
|
{
|
||||||
|
old_length = this->value.length();
|
||||||
|
this->value = value;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(LGFX *canvas)
|
||||||
|
{
|
||||||
|
if (!dirty)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dirty = false;
|
||||||
|
|
||||||
|
float MARGIN = 10.0;
|
||||||
|
|
||||||
|
canvas->setTextSize(3.0);
|
||||||
|
|
||||||
|
int bg_color = TFT_WHITE;
|
||||||
|
int fg_color = TFT_BLACK;
|
||||||
|
|
||||||
|
float old_x = canvas->width() - (canvas->fontWidth() * old_length) - MARGIN;
|
||||||
|
float new_x = canvas->width() - (canvas->fontWidth() * value.length()) - MARGIN;
|
||||||
|
float new_y = canvas->height() - canvas->fontHeight() - MARGIN;
|
||||||
|
|
||||||
|
canvas->setEpdMode(epd_fastest);
|
||||||
|
canvas->setColor(bg_color);
|
||||||
|
|
||||||
|
canvas->fillRect(old_x, new_y, canvas->width() - old_x, canvas->height() - new_y);
|
||||||
|
canvas->waitDisplay();
|
||||||
|
canvas->setColor(fg_color);
|
||||||
|
|
||||||
|
canvas->setEpdMode(epd_text);
|
||||||
|
canvas->setBaseColor(bg_color);
|
||||||
|
canvas->setTextColor(fg_color);
|
||||||
|
canvas->setCursor(new_x, new_y);
|
||||||
|
canvas->print(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
LGFX *canvas;
|
||||||
|
String value;
|
||||||
|
int old_length = 0;
|
||||||
|
|
||||||
|
bool dirty = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
42
src/touch.hpp
Normal file
42
src/touch.hpp
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#include <utility/GT911.h>
|
||||||
|
#ifndef _TOUCH_H_
|
||||||
|
#define _TOUCH_H_
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
class Touch
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Touch(GT911 TP) : TP{TP} {}
|
||||||
|
|
||||||
|
std::optional<tp_finger_t> tap()
|
||||||
|
{
|
||||||
|
if (!TP.available())
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
TP.update();
|
||||||
|
|
||||||
|
if (TP.isFingerUp())
|
||||||
|
{
|
||||||
|
touching = false;
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (touching)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
touching = true;
|
||||||
|
return TP.readFinger(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GT911 TP;
|
||||||
|
tp_finger_t finger;
|
||||||
|
bool touching;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Reference in New Issue
Block a user