From 18db6b24dac5b0d221bcbc5a2de9efc055d09435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Kub=C3=AD=C4=8Dek?= Date: Mon, 8 Jun 2026 09:47:15 +0200 Subject: [PATCH] =?UTF-8?q?Reset=20projektu,=20jde=20se=20na=20to=20ru?= =?UTF-8?q?=C4=8Dn=C4=9B.=20Zat=C3=ADm=20jen=20z=C3=A1klad=20s=20konstanta?= =?UTF-8?q?mi=20a=20SerialMonitor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/include/ButtonInterrupt.h | 62 ----------- src/include/CodeStorage.h | 38 ------- src/include/Display.h | 73 ------------- src/include/Encoder.h | 35 ------ src/include/Feedback.h | 84 -------------- src/include/Pins.h | 16 --- src/include/README | 37 ------- src/include/VaultConfig.h | 21 ---- src/include/VaultState.h | 96 ---------------- src/src/main.cpp | 144 ++++++++++++++++++++++++ src/src/main.ino | 200 ---------------------------------- 11 files changed, 144 insertions(+), 662 deletions(-) delete mode 100644 src/include/ButtonInterrupt.h delete mode 100644 src/include/CodeStorage.h delete mode 100644 src/include/Display.h delete mode 100644 src/include/Encoder.h delete mode 100644 src/include/Feedback.h delete mode 100644 src/include/Pins.h delete mode 100644 src/include/README delete mode 100644 src/include/VaultConfig.h delete mode 100644 src/include/VaultState.h create mode 100644 src/src/main.cpp delete mode 100644 src/src/main.ino diff --git a/src/include/ButtonInterrupt.h b/src/include/ButtonInterrupt.h deleted file mode 100644 index 84310de..0000000 --- a/src/include/ButtonInterrupt.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include - -class InterruptButton { -public: - explicit InterruptButton(byte pin) - : _pin(pin), _lastPressMs(0) { - } - - void begin() { - pinMode(_pin, INPUT_PULLUP); - _instance = this; - attachInterrupt(digitalPinToInterrupt(_pin), InterruptButton::isrRouter, FALLING); - } - - bool consumePress() { - bool pending = false; - noInterrupts(); - if (_pendingInterrupt) { - pending = true; - _pendingInterrupt = false; - } - interrupts(); - - if (!pending) { - return false; - } - - const uint32_t now = millis(); - if (now - _lastPressMs < BUTTON_DEBOUNCE_MS) { - return false; - } - _lastPressMs = now; - return true; - } - - void clearPending() { - noInterrupts(); - _pendingInterrupt = false; - interrupts(); - } - - bool isPressedRaw() const { - return digitalRead(_pin) == LOW; - } - -private: - static constexpr uint32_t BUTTON_DEBOUNCE_MS = 170; - - static void isrRouter() { - if (_instance != nullptr) { - _pendingInterrupt = true; - } - } - - inline static InterruptButton *_instance = nullptr; - inline static volatile bool _pendingInterrupt = false; - - byte _pin; - uint32_t _lastPressMs; -}; diff --git a/src/include/CodeStorage.h b/src/include/CodeStorage.h deleted file mode 100644 index 8b33727..0000000 --- a/src/include/CodeStorage.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include "VaultConfig.h" - -inline bool loadStoredCode(std::array &code) { - constexpr int STORAGE_MAGIC_ADDR = 0; - constexpr int STORAGE_CODE_ADDR = 1; - constexpr uint8_t STORAGE_MAGIC = 0x5A; - - if (EEPROM.read(STORAGE_MAGIC_ADDR) != STORAGE_MAGIC) { - return false; - } - - for (std::size_t i = 0; i < CODE_LENGTH; ++i) { - const uint8_t digit = EEPROM.read(STORAGE_CODE_ADDR + static_cast(i)); - if (digit > DIGIT_MAX) { - return false; - } - code[i] = digit; - } - return true; -} - -inline void saveStoredCode(const std::array &code) { - constexpr int STORAGE_MAGIC_ADDR = 0; - constexpr int STORAGE_CODE_ADDR = 1; - constexpr uint8_t STORAGE_MAGIC = 0x5A; - - EEPROM.update(STORAGE_MAGIC_ADDR, STORAGE_MAGIC); - for (std::size_t i = 0; i < CODE_LENGTH; ++i) { - EEPROM.update(STORAGE_CODE_ADDR + static_cast(i), code[i]); - } -} diff --git a/src/include/Display.h b/src/include/Display.h deleted file mode 100644 index bd4c7c3..0000000 --- a/src/include/Display.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include -#include - -#include - -#include "VaultConfig.h" - -class DisplayController { -public: - DisplayController() - : _lcd(LCD_ADDR, LCD_COLS, LCD_ROWS) { - } - - void begin() { - _lcd.init(); - _lcd.backlight(); - showMessage("Elektronicky", "trezor ready"); - } - - void showEntry(uint8_t selectedDigit, std::size_t enteredLength) { - char line1[17]; - char line2[17]; - - snprintf(line1, sizeof(line1), "Kod: %c%c%c%c", - enteredLength > 0 ? '*' : '_', - enteredLength > 1 ? '*' : '_', - enteredLength > 2 ? '*' : '_', - enteredLength > 3 ? '*' : '_'); - snprintf(line2, sizeof(line2), "Cislice: %u", selectedDigit); - - writePadded(0, line1); - writePadded(1, line2); - } - - void showSetup(uint8_t selectedDigit, std::size_t enteredLength) { - char line1[17]; - char line2[17]; - - snprintf(line1, sizeof(line1), "Novy kod: %c%c%c%c", - enteredLength > 0 ? '*' : '_', - enteredLength > 1 ? '*' : '_', - enteredLength > 2 ? '*' : '_', - enteredLength > 3 ? '*' : '_'); - snprintf(line2, sizeof(line2), "Vyber: %u", selectedDigit); - - writePadded(0, line1); - writePadded(1, line2); - } - - void showLockout(uint32_t remainingSeconds) { - char line2[17]; - writePadded(0, "LOCKOUT"); - snprintf(line2, sizeof(line2), "Cekej %lus", static_cast(remainingSeconds)); - writePadded(1, line2); - } - - void showMessage(const char *line1, const char *line2) { - writePadded(0, line1); - writePadded(1, line2); - } - -private: - void writePadded(uint8_t row, const char *text) { - _lcd.setCursor(0, row); - _lcd.print(" "); - _lcd.setCursor(0, row); - _lcd.print(text); - } - - LiquidCrystal_I2C _lcd; -}; diff --git a/src/include/Encoder.h b/src/include/Encoder.h deleted file mode 100644 index 511ed0f..0000000 --- a/src/include/Encoder.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include - -class RotaryEncoder { -public: - RotaryEncoder(byte clkPin, byte dtPin) - : _clkPin(clkPin), _dtPin(dtPin), _lastClk(HIGH) { - } - - void begin() { - pinMode(_clkPin, INPUT_PULLUP); - pinMode(_dtPin, INPUT_PULLUP); - _lastClk = digitalRead(_clkPin); - } - - // -1 = vlevo, +1 = vpravo, 0 = bez změny - int8_t readStep() { - const int clkState = digitalRead(_clkPin); - int8_t step = 0; - - if (clkState != _lastClk && clkState == LOW) { - const int dtState = digitalRead(_dtPin); - step = (dtState != clkState) ? 1 : -1; - } - - _lastClk = clkState; - return step; - } - -private: - byte _clkPin; - byte _dtPin; - int _lastClk; -}; diff --git a/src/include/Feedback.h b/src/include/Feedback.h deleted file mode 100644 index f8042e8..0000000 --- a/src/include/Feedback.h +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once - -#include -#include - -class FeedbackController { -public: - FeedbackController(byte servoPin, byte redPin, byte greenPin, byte bluePin, byte buzzerPin) - : _servoPin(servoPin), _redPin(redPin), _greenPin(greenPin), _bluePin(bluePin), _buzzerPin(buzzerPin) { - } - - void begin(uint8_t lockedAngle) { - pinMode(_redPin, OUTPUT); - pinMode(_greenPin, OUTPUT); - pinMode(_bluePin, OUTPUT); - pinMode(_buzzerPin, OUTPUT); - - _servo.attach(_servoPin); - lock(lockedAngle); - ledOff(); - } - - void lock(uint8_t angle) { - _servo.write(angle); - } - - void unlock(uint8_t angle) { - _servo.write(angle); - } - - void ledOff() { - setRgb(0, 0, 0); - } - - void onCorrectCode(uint8_t unlockAngle) { - unlock(unlockAngle); - setRgb(0, 255, 0); - playSuccessMelody(); - } - - void onWrongCode() { - for (uint8_t i = 0; i < 3; ++i) { - setRgb(255, 0, 0); - delay(140); - ledOff(); - delay(110); - } - playAlarm(); - } - -private: - void setRgb(uint8_t red, uint8_t green, uint8_t blue) { - analogWrite(_redPin, red); - analogWrite(_greenPin, green); - analogWrite(_bluePin, blue); - } - - void playSuccessMelody() { - tone(_buzzerPin, 784, 100); - delay(130); - tone(_buzzerPin, 988, 100); - delay(130); - tone(_buzzerPin, 1319, 170); - delay(190); - noTone(_buzzerPin); - } - - void playAlarm() { - for (uint8_t i = 0; i < 2; ++i) { - tone(_buzzerPin, 220); - delay(250); - tone(_buzzerPin, 180); - delay(250); - } - noTone(_buzzerPin); - } - - byte _servoPin; - byte _redPin; - byte _greenPin; - byte _bluePin; - byte _buzzerPin; - Servo _servo; -}; diff --git a/src/include/Pins.h b/src/include/Pins.h deleted file mode 100644 index 4f98037..0000000 --- a/src/include/Pins.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -constexpr byte BTN_PIN = 6; // tlačítko, druhý pin na GND -constexpr byte ENCODER_CLK_PIN = 8; -constexpr byte ENCODER_DT_PIN = 9; -constexpr byte ENCODER_SW_PIN = 12; // volitelný SW na enkodéru - -// RGB LED common cathode -constexpr byte RGB_G_PIN = 3; -constexpr byte RGB_R_PIN = 5; -constexpr byte RGB_B_PIN = 11; - -constexpr byte SERVO_PWM_PIN = 10; -constexpr byte BUZZER_PIN = 7; diff --git a/src/include/README b/src/include/README deleted file mode 100644 index 49819c0..0000000 --- a/src/include/README +++ /dev/null @@ -1,37 +0,0 @@ - -This directory is intended for project header files. - -A header file is a file containing C declarations and macro definitions -to be shared between several project source files. You request the use of a -header file in your project source file (C, C++, etc) located in `src` folder -by including it, with the C preprocessing directive `#include'. - -```src/main.c - -#include "header.h" - -int main (void) -{ - ... -} -``` - -Including a header file produces the same results as copying the header file -into each source file that needs it. Such copying would be time-consuming -and error-prone. With a header file, the related declarations appear -in only one place. If they need to be changed, they can be changed in one -place, and programs that include the header file will automatically use the -new version when next recompiled. The header file eliminates the labor of -finding and changing all the copies as well as the risk that a failure to -find one copy will result in inconsistencies within a program. - -In C, the convention is to give header files names that end with `.h'. - -Read more about using header files in official GCC documentation: - -* Include Syntax -* Include Operation -* Once-Only Headers -* Computed Includes - -https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/src/include/VaultConfig.h b/src/include/VaultConfig.h deleted file mode 100644 index 48129f4..0000000 --- a/src/include/VaultConfig.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include - -constexpr std::size_t CODE_LENGTH = 4; -constexpr uint8_t MAX_FAILED_ATTEMPTS = 3; -constexpr uint32_t LOCKOUT_MS = 30'000; - -constexpr uint8_t DIGIT_MIN = 0; -constexpr uint8_t DIGIT_MAX = 9; - -constexpr uint8_t SERVO_LOCKED_ANGLE = 5; -constexpr uint8_t SERVO_UNLOCKED_ANGLE = 95; -constexpr uint32_t UNLOCK_VISIBLE_MS = 5000; - -constexpr uint8_t LCD_ADDR = 0x27; -constexpr uint8_t LCD_COLS = 16; -constexpr uint8_t LCD_ROWS = 2; - -constexpr std::array DEFAULT_CODE = {1, 2, 3, 4}; diff --git a/src/include/VaultState.h b/src/include/VaultState.h deleted file mode 100644 index 0c838a3..0000000 --- a/src/include/VaultState.h +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include -#include - -#include "VaultConfig.h" - -enum class SubmitResult : uint8_t { - DigitAccepted, - CorrectCode, - WrongCode, - LockedOut, - InvalidDigit -}; - -class VaultState { -public: - VaultState() : _code(DEFAULT_CODE), _entry{}, _entryLength(0), _failedAttempts(0), _lockoutUntilMs(0) { - } - - void setCode(const std::array &code) { - _code = code; - } - - const std::array &code() const { - return _code; - } - - SubmitResult pushDigit(uint8_t digit, uint32_t nowMs) { - if (isLockedOut(nowMs)) { - return SubmitResult::LockedOut; - } - if (digit < DIGIT_MIN || digit > DIGIT_MAX) { - return SubmitResult::InvalidDigit; - } - if (_entryLength < CODE_LENGTH) { - _entry[_entryLength++] = digit; - } - - if (_entryLength < CODE_LENGTH) { - return SubmitResult::DigitAccepted; - } - - const bool ok = (_entry == _code); - clearEntry(); - - if (ok) { - _failedAttempts = 0; - return SubmitResult::CorrectCode; - } - - ++_failedAttempts; - if (_failedAttempts >= MAX_FAILED_ATTEMPTS) { - _failedAttempts = 0; - _lockoutUntilMs = nowMs + LOCKOUT_MS; - } - return SubmitResult::WrongCode; - } - - bool isLockedOut(uint32_t nowMs) const { - return nowMs < _lockoutUntilMs; - } - - uint32_t lockoutRemainingMs(uint32_t nowMs) const { - if (!isLockedOut(nowMs)) { - return 0; - } - return _lockoutUntilMs - nowMs; - } - - void clearEntry() { - _entry.fill(0); - _entryLength = 0; - } - - std::size_t enteredLength() const { - return _entryLength; - } - - uint8_t failedAttempts() const { - return _failedAttempts; - } - - void resetLockout() { - _lockoutUntilMs = 0; - _failedAttempts = 0; - clearEntry(); - } - -private: - std::array _code; - std::array _entry; - std::size_t _entryLength; - uint8_t _failedAttempts; - uint32_t _lockoutUntilMs; -}; diff --git a/src/src/main.cpp b/src/src/main.cpp new file mode 100644 index 0000000..893eb19 --- /dev/null +++ b/src/src/main.cpp @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include + +// piny +constexpr byte ENCODER_CLK_PIN = 11; // pwm pin, ale nepoužívá se +constexpr byte ENCODER_DT_PIN = 12; +constexpr byte BUTTON_PIN = 13; +constexpr byte RGB_RED_PIN = 3; // pwm +constexpr byte RGB_GREEN_PIN = 5; // pwm +constexpr byte RGB_BLUE_PIN = 6; // pwm +constexpr byte SERVO_PIN = 9; +constexpr byte BUZZER_PIN = 8; + +// Servo +constexpr int CLOSED_POS = 150; +constexpr int OPEN_POS = 15; +Servo servo; + +// LCD +LiquidCrystal_I2C lcd(0x27, 16, 2); + +// proměnné +// příklad je zadávané heslo, stavy, apod. +String password = ""; +String savedPassword = ""; + +// chování zámku +void OpenLock() { + servo.write(OPEN_POS); +} + +void CloseLock() { + servo.write(CLOSED_POS); +} + +// chování bzučáku +void OpenSound() { + // TODO: melodie při otevření +} + +void CloseSound() { + // TODO: ověřit tento zvuk + digitalWrite(BUZZER_PIN, HIGH); + delay(100); + digitalWrite(BUZZER_PIN, LOW); +} + +// chování RGB LED +void OpenLED() { + analogWrite(RGB_GREEN_PIN, 120); + analogWrite(RGB_BLUE_PIN, 0); + analogWrite(RGB_RED_PIN, 0); +} + +void CloseLED() { + analogWrite(RGB_GREEN_PIN, 0); + analogWrite(RGB_BLUE_PIN, 0); + analogWrite(RGB_RED_PIN, 120); +} + +// když není uložené heslo, ukazuje se modrá +void NeutralLED() { + analogWrite(RGB_GREEN_PIN, 0); + analogWrite(RGB_BLUE_PIN, 120); + analogWrite(RGB_RED_PIN, 0); +} + +void ButtonPressed() { + if (savedPassword == "") { + savedPassword = password; + } + else { + if (password == savedPassword) { + OpenLock(); + OpenSound(); + OpenLED(); + password = ""; + } + else { + CloseLock(); + CloseSound(); + CloseLED(); + password = ""; + } + } +} + +void setup() { + Serial.begin(9600); + + Serial.println("Nastavování displeje."); + lcd.init(); + lcd.backlight(); + Serial.println("Displej nastaven."); + + lcd.setCursor(0, 0); + lcd.printstr("Program startuje"); + lcd.setCursor(0, 1); + lcd.printstr("NastavujeSeMotor"); + + delay(100); + + Serial.println("Nastavování motoru."); + servo.attach(SERVO_PIN); + servo.write(CLOSED_POS); // ve výchozím stavu je zamčeno + Serial.println("Motor nastaven."); + lcd.setCursor(0, 1); + lcd.printstr("Motor nastaven. "); + + delay(100); + + lcd.setCursor(0, 1); + lcd.printstr("NastavujiSePiny."); + Serial.println("Nastavování pinů"); + // nastavení pinu na tlačítko + pinMode(BUTTON_PIN, INPUT_PULLDOWN); // nezmáčknuto = 0 + Serial.println("Piny: 1/8"); + + // nastavení pinů pro RGB LED (bude přes pwm, není to nutné přes pinmode, ale lepší to udělat) + pinMode(RGB_RED_PIN, OUTPUT); Serial.println("Piny: 2/8"); + pinMode(RGB_GREEN_PIN, OUTPUT); Serial.println("Piny: 3/8"); + pinMode(RGB_BLUE_PIN, OUTPUT); Serial.println("Piny: 4/8"); + + // nastavení pinů pro enkodér + pinMode(ENCODER_CLK_PIN, INPUT); Serial.println("Piny: 5/8"); + pinMode(ENCODER_DT_PIN, INPUT); Serial.println("Piny: 6/8"); + + // nastavení pinu pro servo + pinMode(SERVO_PIN, OUTPUT); Serial.println("Piny: 7/8"); + + // nastavení pinu pro bzučák + pinMode(BUZZER_PIN, OUTPUT); Serial.println("Piny: 8/8"); + Serial.println("Piny nastaveny."); + lcd.setCursor(0, 1); + lcd.printstr("Piny nastaveny. "); + delay(1000); +} + +void loop() { + +} \ No newline at end of file diff --git a/src/src/main.ino b/src/src/main.ino deleted file mode 100644 index de06899..0000000 --- a/src/src/main.ino +++ /dev/null @@ -1,200 +0,0 @@ -#include - -#include - -#include "ButtonInterrupt.h" -#include "CodeStorage.h" -#include "Display.h" -#include "Encoder.h" -#include "Feedback.h" -#include "Pins.h" -#include "VaultConfig.h" -#include "VaultState.h" - -enum class Mode : uint8_t { - EnterCode, - SetupCode, - Unlocked, - Lockout -}; - -VaultState vault; -RotaryEncoder encoder(ENCODER_CLK_PIN, ENCODER_DT_PIN); -InterruptButton button(BTN_PIN); -DisplayController display; -FeedbackController feedback(SERVO_PWM_PIN, RGB_R_PIN, RGB_G_PIN, RGB_B_PIN, BUZZER_PIN); - -Mode mode = Mode::EnterCode; -uint8_t selectedDigit = 0; -std::array setupCode = {0, 0, 0, 0}; -std::size_t setupDigitsEntered = 0; -uint32_t unlockedUntil = 0; - -void handleSerialCommands() { - if (!Serial.available()) { - return; - } - - String cmd = Serial.readStringUntil('\n'); - cmd.trim(); - cmd.toUpperCase(); - - if (cmd == "RESET") { - vault.resetLockout(); - mode = Mode::EnterCode; - display.showMessage("Reset lockout", "Zadej kod"); - delay(550); - } -} - -void moveSelectedDigit(int8_t step) { - if (step == 0) { - return; - } - - int16_t next = static_cast(selectedDigit) + step; - if (next > DIGIT_MAX) { - next = DIGIT_MIN; - } else if (next < DIGIT_MIN) { - next = DIGIT_MAX; - } - selectedDigit = static_cast(next); -} - -void setupModeTick() { - if (button.consumePress()) { - setupCode[setupDigitsEntered++] = selectedDigit; - } - - if (setupDigitsEntered >= CODE_LENGTH) { - saveStoredCode(setupCode); - vault.setCode(setupCode); - vault.resetLockout(); - - feedback.lock(SERVO_LOCKED_ANGLE); - feedback.ledOff(); - - display.showMessage("Kod ulozen", "Zadej novy kod"); - delay(1000); - - setupDigitsEntered = 0; - selectedDigit = 0; - mode = Mode::EnterCode; - return; - } - - display.showSetup(selectedDigit, setupDigitsEntered); -} - -void enterCodeModeTick(uint32_t nowMs) { - if (vault.isLockedOut(nowMs)) { - mode = Mode::Lockout; - return; - } - - if (button.consumePress()) { - const SubmitResult result = vault.pushDigit(selectedDigit, nowMs); - - if (result == SubmitResult::CorrectCode) { - feedback.onCorrectCode(SERVO_UNLOCKED_ANGLE); - display.showMessage("Kod spravny", "Trezor odemcen"); - unlockedUntil = nowMs + UNLOCK_VISIBLE_MS; - mode = Mode::Unlocked; - return; - } - - if (result == SubmitResult::WrongCode) { - feedback.onWrongCode(); - if (vault.isLockedOut(nowMs)) { - mode = Mode::Lockout; - return; - } - display.showMessage("Spatny kod", "Zkus to znovu"); - delay(800); - } - } - - display.showEntry(selectedDigit, vault.enteredLength()); -} - -void unlockedModeTick(uint32_t nowMs) { - if (nowMs >= unlockedUntil) { - feedback.lock(SERVO_LOCKED_ANGLE); - feedback.ledOff(); - mode = Mode::EnterCode; - return; - } - - display.showMessage("Trezor odemcen", "Pristup povolen"); -} - -void lockoutModeTick(uint32_t nowMs) { - if (!vault.isLockedOut(nowMs)) { - feedback.lock(SERVO_LOCKED_ANGLE); - feedback.ledOff(); - mode = Mode::EnterCode; - return; - } - - const uint32_t remainingMs = vault.lockoutRemainingMs(nowMs); - const uint32_t remainingSec = (remainingMs + 999) / 1000; - display.showLockout(remainingSec); -} - -void setup() { - Serial.begin(115200); - - pinMode(BTN_PIN, INPUT_PULLUP); - const bool setupRequestedAtBoot = (digitalRead(BTN_PIN) == LOW); - - encoder.begin(); - button.begin(); - button.clearPending(); - - feedback.begin(SERVO_LOCKED_ANGLE); - display.begin(); - - std::array loadedCode = {0, 0, 0, 0}; - if (loadStoredCode(loadedCode)) { - vault.setCode(loadedCode); - } else { - vault.setCode(DEFAULT_CODE); - saveStoredCode(DEFAULT_CODE); - } - - if (setupRequestedAtBoot) { - mode = Mode::SetupCode; - display.showMessage("SETUP rezim", "Nastav novy kod"); - delay(900); - } else { - mode = Mode::EnterCode; - display.showMessage("Zadej kod", "Otoc + tlacitko"); - delay(800); - } - - Serial.println("Prikaz pro reset lockoutu: RESET"); -} - -void loop() { - handleSerialCommands(); - - moveSelectedDigit(encoder.readStep()); - const uint32_t nowMs = millis(); - - switch (mode) { - case Mode::SetupCode: - setupModeTick(); - break; - case Mode::EnterCode: - enterCodeModeTick(nowMs); - break; - case Mode::Unlocked: - unlockedModeTick(nowMs); - break; - case Mode::Lockout: - lockoutModeTick(nowMs); - break; - } - - delay(20); -} \ No newline at end of file