Compare commits

...
9 Commits
+219 -84
View File
@@ -4,12 +4,12 @@
#include <Servo.h>
// piny
constexpr byte ENCODER_CLK_PIN = 11; // pwm pin, ale nepoužívá se
constexpr byte ENCODER_CLK_PIN = 11;
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 BUTTON_PIN = 2;
constexpr byte RGB_LED[] = {2, 5, 6};
constexpr byte SERVO_PIN = 9;
constexpr byte BUZZER_PIN = 8;
@@ -21,35 +21,47 @@ Servo servo;
// LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);
// proměnné
// příklad je zadávané heslo, stavy, apod.
int password = ""; // 0-9999
int password = 0;
int passwordValue = 0;
String savedPassword = "";
// enkodér
bool isOpen = false;
int lastCLK = HIGH;
// maska po kolika ms nečinnosti se heslo maskuje
constexpr byte MAX_WRONG_ATTEMPTS = 3;
constexpr unsigned long LOCKOUT_DURATION_MS = 30000UL;
byte wrongAttempts = 0;
bool lockedOut = false;
unsigned long lockoutStart = 0;
constexpr unsigned long MASK_DELAY_MS = 2000;
unsigned long lastEncoderMove = 0;
bool masked = false;
// přeformátování kódu na 4 cifry
String formatCode(int value) {
String s = String(value);
// přerušení tlačítka
volatile bool buttonPressed = false;
constexpr unsigned long DEBOUNCE_MS = 50;
unsigned long lastButtonTime = 0;
volatile unsigned long buttonPressTime = 0;
void onButtonPress() {
buttonPressTime = millis();
buttonPressed = true;
}
String formatCode(const int value) {
auto s = String(value);
while (s.length() < 4) s = "0" + s;
return s;
}
// chování zámku
void OpenLock() { servo.write(OPEN_POS); }
void OpenLock() { servo.write(OPEN_POS); }
void CloseLock() { servo.write(CLOSED_POS); }
// chování bzučáku
void OpenSound() {
int melody[] = { 262, 330, 392, 523 }; // C4, E4, G4, C5
int durations[] = { 150, 150, 150, 300 };
const int melody[] = { 262, 330, 392, 523 };
const int durations[] = { 150, 150, 150, 300 };
for (int i = 0; i < 4; i++) {
tone(BUZZER_PIN, melody[i], durations[i]);
delay(durations[i] + 30);
@@ -58,65 +70,61 @@ void OpenSound() {
}
void CloseSound() {
tone(BUZZER_PIN, 262, 150);
delay(500);
tone(BUZZER_PIN, 200, 200); delay(230);
tone(BUZZER_PIN, 150, 400); delay(430);
noTone(BUZZER_PIN);
}
void ConfirmSound() {
tone(BUZZER_PIN, 440, 100);
delay(130);
tone(BUZZER_PIN, 880, 200);
delay(230);
tone(BUZZER_PIN, 440, 100); delay(130);
tone(BUZZER_PIN, 880, 200); delay(230);
noTone(BUZZER_PIN);
}
// chování RGB LED
void OpenLED() {
analogWrite(RGB_GREEN_PIN, 120);
analogWrite(RGB_BLUE_PIN, 0);
analogWrite(RGB_RED_PIN, 0);
analogWrite(RGB_LED[1], 120);
analogWrite(RGB_LED[2], 0);
analogWrite(RGB_LED[0], 0);
}
void CloseLED() {
analogWrite(RGB_GREEN_PIN, 0);
analogWrite(RGB_BLUE_PIN, 0);
analogWrite(RGB_RED_PIN, 120);
analogWrite(RGB_LED[1], 0);
analogWrite(RGB_LED[2], 0);
analogWrite(RGB_LED[0], 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);
analogWrite(RGB_LED[1], 0);
analogWrite(RGB_LED[2], 120);
analogWrite(RGB_LED[0], 0);
}
void ButtonPressed() {
if (savedPassword != "")
{
if (password == savedPassword) {
OpenLock();
OpenSound();
OpenLED();
password = "";
}
else {
CloseLock();
CloseSound();
CloseLED();
password = "";
}
}
void BlockedLED() {
const bool blink = millis() / 500 % 2 == 0;
analogWrite(RGB_LED[0], blink ? 120 : 0);
analogWrite(RGB_LED[1], 0);
analogWrite(RGB_LED[2], 0);
}
// setup hesla
void showSetupScreen(bool showValue) {
void showEntryScreen(const bool showValue) {
lcd.setCursor(0, 0);
lcd.printstr("Nastavte heslo: ");
lcd.printstr("Zadejte heslo: ");
lcd.setCursor(0, 1);
if (showValue) {
String code = formatCode(password);
String display = code[0] + String(" ") + code[1] + String(" ") + code[2] + String(" ") + code[3] + String(" ");
lcd.printstr(display.c_str());
} else {
lcd.printstr("* * * * ");
}
}
void showSetupScreen(const bool showValue) {
lcd.setCursor(0, 0);
lcd.printstr("Nastavte heslo: ");
lcd.setCursor(0, 1);
if (showValue) {
String code = formatCode(passwordValue);
// mezery okolo pro vizuální oddělení číslic
String display = code[0] + String(" ") + code[1] + String(" ") + code[2] + String(" ") + code[3] + String(" ");
lcd.printstr(display.c_str());
} else {
@@ -124,22 +132,20 @@ void showSetupScreen(bool showValue) {
}
}
// setup fáze blokující, dokud není heslo uloženo
void runPasswordSetup() {
NeutralLED();
passwordValue = 0;
masked = false;
lastEncoderMove = millis();
lastCLK = digitalRead(ENCODER_CLK_PIN);
buttonPressed = false; // vyprázdnit případný příznak z doby před setupem
showSetupScreen(true);
while (savedPassword == "") {
// čtení enkodéru
int currentCLK = digitalRead(ENCODER_CLK_PIN);
const int currentCLK = digitalRead(ENCODER_CLK_PIN);
if (currentCLK != lastCLK && currentCLK == LOW) {
int dt = digitalRead(ENCODER_DT_PIN);
if (dt != currentCLK) {
if (const int dt = digitalRead(ENCODER_DT_PIN); dt != currentCLK) {
passwordValue = (passwordValue + 1) % 10000;
} else {
passwordValue = (passwordValue - 1 + 10000) % 10000;
@@ -150,14 +156,15 @@ void runPasswordSetup() {
}
lastCLK = currentCLK;
// maskování po nečinnosti
if (!masked && millis() - lastEncoderMove >= MASK_DELAY_MS) {
masked = true;
showSetupScreen(false);
}
// potvrzení tlačítkem
if (digitalRead(BUTTON_PIN) == HIGH) {
// přerušení konzumace příznaku místo přímého čtení pinu
if (buttonPressed) {
buttonPressed = false;
savedPassword = formatCode(passwordValue);
ConfirmSound();
@@ -167,15 +174,10 @@ void runPasswordSetup() {
lcd.printstr("* * * * ");
delay(1500);
// přechod do normálního režimu
lcd.setCursor(0, 0);
lcd.printstr("Zadejte heslo: ");
lcd.setCursor(0, 1);
lcd.printstr(" ");
// debounce
while (digitalRead(BUTTON_PIN) == HIGH) {}
delay(50);
}
}
}
@@ -192,43 +194,176 @@ void setup() {
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
servo.write(CLOSED_POS);
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)
// formátování vymyšleno přes Claude Sonnet 4.6
pinMode(BUTTON_PIN, INPUT_PULLDOWN); Serial.println("Piny: 1/8");
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");
pinMode(BUTTON_PIN, INPUT); Serial.println("Piny: 1/8");
pinMode(RGB_LED[0], OUTPUT); Serial.println("Piny: 2/8");
pinMode(RGB_LED[1], OUTPUT); Serial.println("Piny: 3/8");
pinMode(RGB_LED[2], OUTPUT); Serial.println("Piny: 4/8");
pinMode(ENCODER_CLK_PIN, INPUT); Serial.println("Piny: 5/8");
pinMode(ENCODER_DT_PIN, INPUT); Serial.println("Piny: 6/8");
pinMode(SERVO_PIN, OUTPUT); Serial.println("Piny: 7/8");
pinMode(BUZZER_PIN, OUTPUT); Serial.println("Piny: 8/8");
pinMode(ENCODER_DT_PIN, INPUT); Serial.println("Piny: 6/8");
pinMode(SERVO_PIN, OUTPUT); Serial.println("Piny: 7/8");
pinMode(BUZZER_PIN, OUTPUT); Serial.println("Piny: 8/8");
Serial.println("Piny nastaveny.");
lcd.setCursor(0, 1);
lcd.printstr("Piny nastaveny. ");
delay(1000);
// registrace přerušení
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), onButtonPress, RISING);
runPasswordSetup();
}
void loop() {
if (buttonPressed) {
if (millis() - buttonPressTime >= DEBOUNCE_MS) {
buttonPressed = false;
} else {
buttonPressed = false;
}
}
if (Serial.available()) {
String cmd = Serial.readStringUntil('\n');
cmd.trim();
if (cmd == "unblock") {
if (lockedOut) {
lockedOut = false;
wrongAttempts = 0;
Serial.println("[SERIAL] Blokace zrusena. Zamek je stale zamcen.");
CloseLED();
password = 0;
masked = false;
lastEncoderMove = millis();
showEntryScreen(true);
} else {
Serial.println("[SERIAL] Zamek neni zablokovan.");
}
} else {
Serial.println("[SERIAL] Nezname prikazani. Pouzij: unblock");
}
}
if (lockedOut) {
if (const unsigned long elapsed = millis() - lockoutStart; elapsed >= LOCKOUT_DURATION_MS) {
lockedOut = false;
wrongAttempts = 0;
Serial.println("Blokace skoncila.");
CloseLED();
password = 0;
masked = false;
lastEncoderMove = millis();
showEntryScreen(true);
} else {
static unsigned long lastCountdownUpdate = 0;
if (millis() - lastCountdownUpdate >= 1000) {
lastCountdownUpdate = millis();
const int remaining = static_cast<int>((LOCKOUT_DURATION_MS - elapsed + 999) / 1000);
lcd.setCursor(0, 0);
lcd.printstr("Zablokovano! ");
lcd.setCursor(0, 1);
String line = "Zbyva: " + String(remaining) + "s ";
line = line.substring(0, 16);
lcd.printstr(line.c_str());
}
BlockedLED();
}
buttonPressed = false; // zahazovat stisky během blokace
return;
}
if (!isOpen) {
const int currentCLK = digitalRead(ENCODER_CLK_PIN);
if (currentCLK != lastCLK && currentCLK == LOW) {
if (const int dt = digitalRead(ENCODER_DT_PIN); dt != currentCLK) {
password = (password + 1) % 10000;
} else {
password = (password - 1 + 10000) % 10000;
}
masked = false;
lastEncoderMove = millis();
showEntryScreen(true);
}
lastCLK = currentCLK;
if (!masked && millis() - lastEncoderMove >= MASK_DELAY_MS) {
masked = true;
showEntryScreen(false);
}
}
// konzumace příznaku z přerušení
if (buttonPressed) {
buttonPressed = false;
if (!isOpen) {
if (formatCode(password) == savedPassword) {
isOpen = true;
wrongAttempts = 0;
OpenLock();
OpenLED();
OpenSound();
lcd.setCursor(0, 0);
lcd.printstr("Odemceno! ");
lcd.setCursor(0, 1);
lcd.printstr("Stiskni pro zamk");
} else {
wrongAttempts++;
Serial.print("Spatny pokus: ");
Serial.print(wrongAttempts);
Serial.print("/");
Serial.println(MAX_WRONG_ATTEMPTS);
CloseSound();
if (wrongAttempts >= MAX_WRONG_ATTEMPTS) {
lockedOut = true;
lockoutStart = millis();
Serial.println("Zamek zablokovan na 30 sekund. Pouzij 'unblock' pro zruseni.");
lcd.setCursor(0, 0);
lcd.printstr("Zablokovano! ");
lcd.setCursor(0, 1);
lcd.printstr("Zbyva: 30s ");
BlockedLED();
} else {
lcd.setCursor(0, 0);
String warn = "Spatne! (" + String(wrongAttempts) + "/" + String(MAX_WRONG_ATTEMPTS) + ") ";
warn = warn.substring(0, 16);
lcd.printstr(warn.c_str());
lcd.setCursor(0, 1);
lcd.printstr(" ");
delay(1500);
masked = false;
lastEncoderMove = millis();
showEntryScreen(true);
}
}
} else {
isOpen = false;
CloseLock();
CloseLED();
CloseSound();
password = 0;
masked = false;
lastEncoderMove = millis();
showEntryScreen(true);
}
}
}