#undef ARDUINO_M5STACK_FIRE #define ARDUINO_M5STACK_Paper #define YAML_DISABLE_CJSON #define LGFX_M5PAPER #define LGFX_USE_V1 #define LGFX_AUTODETECT #include #include #include #include #include #include "display.hpp" #include "keypad.hpp" #include "input.hpp" #include "status.hpp" #include "touch.hpp" #include "settings.hpp" #include "mqtt.hpp" static WiFiUDP ntpUDP; static NTPClient timeClient(ntpUDP); static MQTT *mqtt; static Display display; 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 const int WIFI_CONNECT_RETRY_MAX = 30; void mqtt_callback(char *topic, byte *payload, unsigned int length) { payload[length] = '\0'; String state((const char *)payload); is_disarmed = state == "disarmed"; state.replace("_", " "); state[0] = toupper(state[0]); Serial.println(state); status->set(state); status->draw(&display); keypad_state(!is_disarmed); } void lock() { if (is_disarmed) { mqtt->send("ARM_AWAY"); } } void unlock() { 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; void initWifi(Settings *settings) { if (!settings) { status->set("No settings"); return; } if (isConnecting) { status->set("Already connecting"); return; } isConnecting = true; status->set("Connecting"); WiFi.begin(settings->gettext("wifi:ssid"), settings->gettext("wifi:password")); if (WiFi.isConnected()) { WiFi.disconnect(); } Serial.print("Connecting to Wi-Fi network"); for (int cnt_retry = 0; cnt_retry < WIFI_CONNECT_RETRY_MAX && !WiFi.isConnected(); cnt_retry++) { delay(500); Serial.print("."); } Serial.println(""); if (WiFi.isConnected()) { Serial.print("Local IP: "); Serial.println(WiFi.localIP()); isConnecting = false; status->set("Connected"); } else { status->set("Failed to connect"); isConnecting = false; return; } } void initTOTP(Settings *settings) { uint8_t *base32_key = new uint8_t[20]; const char *hmac = ((String)settings->gettext("totp:hmac")).c_str(); for (int i = 0; i < 20; i++) { base32_key[i] = (uint8_t)hmac[i]; } 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() { M5.begin(); display.init(); touch = new Touch(M5.TP); 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(); if (!settings) { status->set("Settings Error"); return; } mqtt = new MQTT(settings, mqtt_callback); initWifi(settings); mqtt->connect(); timeClient.begin(); initTOTP(settings); } void loop() { timeClient.update(); mqtt->loop(); auto tap = touch->tap(); if (tap.has_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()); // }