Reset projektu, jde se na to ručně. Zatím jen základ s konstantami a SerialMonitor
This commit is contained in:
@@ -1,62 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <EEPROM.h>
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "VaultConfig.h"
|
|
||||||
|
|
||||||
inline bool loadStoredCode(std::array<uint8_t, CODE_LENGTH> &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<int>(i));
|
|
||||||
if (digit > DIGIT_MAX) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
code[i] = digit;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void saveStoredCode(const std::array<uint8_t, CODE_LENGTH> &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<int>(i), code[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <LiquidCrystal_I2C.h>
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
#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<unsigned long>(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;
|
|
||||||
};
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <Servo.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
@@ -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
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
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<uint8_t, CODE_LENGTH> DEFAULT_CODE = {1, 2, 3, 4};
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#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<uint8_t, CODE_LENGTH> &code) {
|
|
||||||
_code = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::array<uint8_t, CODE_LENGTH> &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<uint8_t, CODE_LENGTH> _code;
|
|
||||||
std::array<uint8_t, CODE_LENGTH> _entry;
|
|
||||||
std::size_t _entryLength;
|
|
||||||
uint8_t _failedAttempts;
|
|
||||||
uint32_t _lockoutUntilMs;
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <LiquidCrystal_I2C.h>
|
||||||
|
#include <Servo.h>
|
||||||
|
#include <EEPROM.h>
|
||||||
|
|
||||||
|
// 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() {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
#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<uint8_t, CODE_LENGTH> 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<int16_t>(selectedDigit) + step;
|
|
||||||
if (next > DIGIT_MAX) {
|
|
||||||
next = DIGIT_MIN;
|
|
||||||
} else if (next < DIGIT_MIN) {
|
|
||||||
next = DIGIT_MAX;
|
|
||||||
}
|
|
||||||
selectedDigit = static_cast<uint8_t>(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<uint8_t, CODE_LENGTH> 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);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user