Předělání tlačítka aby používalo interupt

This commit is contained in:
Matěj Kubíček
2026-06-08 10:42:03 +02:00
parent 4f704b3b29
commit 31b496cef2
+53 -95
View File
@@ -4,12 +4,12 @@
#include <Servo.h> #include <Servo.h>
// piny // 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 ENCODER_DT_PIN = 12;
constexpr byte BUTTON_PIN = 13; constexpr byte BUTTON_PIN = 2; // ZMĚNA: přesunuto na pin 2 (INT0) kvůli přerušení
constexpr byte RGB_RED_PIN = 3; // pwm constexpr byte RGB_RED_PIN = 3;
constexpr byte RGB_GREEN_PIN = 5; // pwm constexpr byte RGB_GREEN_PIN = 5;
constexpr byte RGB_BLUE_PIN = 6; // pwm constexpr byte RGB_BLUE_PIN = 6;
constexpr byte SERVO_PIN = 9; constexpr byte SERVO_PIN = 9;
constexpr byte BUZZER_PIN = 8; constexpr byte BUZZER_PIN = 8;
@@ -21,46 +21,48 @@ Servo servo;
// LCD // LCD
LiquidCrystal_I2C lcd(0x27, 16, 2); LiquidCrystal_I2C lcd(0x27, 16, 2);
// proměnné int password = 0;
// příklad je zadávané heslo, stavy, apod.
int password = 0; // 0-9999
int passwordValue = 0; int passwordValue = 0;
String savedPassword = ""; String savedPassword = "";
// stav zámku
bool isOpen = false; bool isOpen = false;
// enkodér
int lastCLK = HIGH; int lastCLK = HIGH;
// blokace po špatných pokusech
constexpr byte MAX_WRONG_ATTEMPTS = 3; constexpr byte MAX_WRONG_ATTEMPTS = 3;
constexpr unsigned long LOCKOUT_DURATION_MS = 30000UL; constexpr unsigned long LOCKOUT_DURATION_MS = 30000UL;
byte wrongAttempts = 0; byte wrongAttempts = 0;
bool lockedOut = false; bool lockedOut = false;
unsigned long lockoutStart = 0; unsigned long lockoutStart = 0;
// maska po kolika ms nečinnosti se heslo maskuje
constexpr unsigned long MASK_DELAY_MS = 2000; constexpr unsigned long MASK_DELAY_MS = 2000;
unsigned long lastEncoderMove = 0; unsigned long lastEncoderMove = 0;
bool masked = false; bool masked = false;
// přeformátování kódu na 4 cifry // přerušení tlačítka
volatile bool buttonPressed = false;
constexpr unsigned long DEBOUNCE_MS = 50;
unsigned long lastButtonTime = 0;
void onButtonPress() {
if (const unsigned long now = millis(); now - lastButtonTime >= DEBOUNCE_MS) {
lastButtonTime = now;
buttonPressed = true;
}
}
String formatCode(const int value) { String formatCode(const int value) {
auto s = String(value); auto s = String(value);
while (s.length() < 4) s = "0" + s; while (s.length() < 4) s = "0" + s;
return 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); } void CloseLock() { servo.write(CLOSED_POS); }
// chování bzučáku
void OpenSound() { void OpenSound() {
const int melody[] = { 262, 330, 392, 523 }; // C4, E4, G4, C5 const int melody[] = { 262, 330, 392, 523 };
const int durations[] = { 150, 150, 150, 300 }; const int durations[] = { 150, 150, 150, 300 };
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
tone(BUZZER_PIN, melody[i], durations[i]); tone(BUZZER_PIN, melody[i], durations[i]);
delay(durations[i] + 30); delay(durations[i] + 30);
@@ -75,100 +77,73 @@ void CloseSound() {
} }
void ConfirmSound() { void ConfirmSound() {
tone(BUZZER_PIN, 440, 100); tone(BUZZER_PIN, 440, 100); delay(130);
delay(130); tone(BUZZER_PIN, 880, 200); delay(230);
tone(BUZZER_PIN, 880, 200);
delay(230);
noTone(BUZZER_PIN); noTone(BUZZER_PIN);
} }
// chování RGB LED
void OpenLED() { void OpenLED() {
analogWrite(RGB_GREEN_PIN, 120); analogWrite(RGB_GREEN_PIN, 120);
analogWrite(RGB_BLUE_PIN, 0); analogWrite(RGB_BLUE_PIN, 0);
analogWrite(RGB_RED_PIN, 0); analogWrite(RGB_RED_PIN, 0);
} }
void CloseLED() { void CloseLED() {
analogWrite(RGB_GREEN_PIN, 0); analogWrite(RGB_GREEN_PIN, 0);
analogWrite(RGB_BLUE_PIN, 0); analogWrite(RGB_BLUE_PIN, 0);
analogWrite(RGB_RED_PIN, 120); analogWrite(RGB_RED_PIN, 120);
} }
// když není uložené heslo, ukazuje se modrá
void NeutralLED() { void NeutralLED() {
analogWrite(RGB_GREEN_PIN, 0); analogWrite(RGB_GREEN_PIN, 0);
analogWrite(RGB_BLUE_PIN, 120); analogWrite(RGB_BLUE_PIN, 120);
analogWrite(RGB_RED_PIN, 0); analogWrite(RGB_RED_PIN, 0);
} }
// blikání červené během blokace
void BlockedLED() { void BlockedLED() {
// bliká každých 500 ms
const bool blink = millis() / 500 % 2 == 0; const bool blink = millis() / 500 % 2 == 0;
analogWrite(RGB_RED_PIN, blink ? 120 : 0); analogWrite(RGB_RED_PIN, blink ? 120 : 0);
analogWrite(RGB_GREEN_PIN, 0); analogWrite(RGB_GREEN_PIN, 0);
analogWrite(RGB_BLUE_PIN, 0); analogWrite(RGB_BLUE_PIN, 0);
} }
void ButtonPressed() {
if (savedPassword != "")
{
if (formatCode(password) == savedPassword) {
OpenLock();
OpenSound();
OpenLED();
password = 0;
}
else {
CloseLock();
CloseSound();
CloseLED();
password = 0;
}
}
}
// zobrazení zadávací obrazovky
void showEntryScreen(const bool showValue) { void showEntryScreen(const bool showValue) {
lcd.setCursor(0, 0); lcd.setCursor(0, 0);
lcd.printstr("Zadejte heslo: "); lcd.printstr("Zadejte heslo: ");
lcd.setCursor(0, 1); lcd.setCursor(0, 1);
if (showValue) { if (showValue) {
String code = formatCode(password); String code = formatCode(password);
const String display = code[0] + String(" ") + code[1] + String(" ") + code[2] + String(" ") + code[3] + String(" "); String display = code[0] + String(" ") + code[1] + String(" ") + code[2] + String(" ") + code[3] + String(" ");
lcd.printstr(display.c_str()); lcd.printstr(display.c_str());
} else { } else {
lcd.printstr("* * * * "); lcd.printstr("* * * * ");
} }
} }
// zobrazení setup obrazovky
void showSetupScreen(const bool showValue) { void showSetupScreen(const bool showValue) {
lcd.setCursor(0, 0); lcd.setCursor(0, 0);
lcd.printstr("Nastavte heslo: "); lcd.printstr("Nastavte heslo: ");
lcd.setCursor(0, 1); lcd.setCursor(0, 1);
if (showValue) { if (showValue) {
String code = formatCode(passwordValue); String code = formatCode(passwordValue);
const String display = code[0] + String(" ") + code[1] + String(" ") + code[2] + String(" ") + code[3] + String(" "); String display = code[0] + String(" ") + code[1] + String(" ") + code[2] + String(" ") + code[3] + String(" ");
lcd.printstr(display.c_str()); lcd.printstr(display.c_str());
} else { } else {
lcd.printstr("* * * * "); lcd.printstr("* * * * ");
} }
} }
// setup fáze blokující, dokud není heslo uloženo
void runPasswordSetup() { void runPasswordSetup() {
NeutralLED(); NeutralLED();
passwordValue = 0; passwordValue = 0;
masked = false; masked = false;
lastEncoderMove = millis(); lastEncoderMove = millis();
lastCLK = digitalRead(ENCODER_CLK_PIN); lastCLK = digitalRead(ENCODER_CLK_PIN);
buttonPressed = false; // vyprázdnit případný příznak z doby před setupem
showSetupScreen(true); showSetupScreen(true);
while (savedPassword == "") { while (savedPassword == "") {
// čtení enkodéru
const int currentCLK = digitalRead(ENCODER_CLK_PIN); const int currentCLK = digitalRead(ENCODER_CLK_PIN);
if (currentCLK != lastCLK && currentCLK == LOW) { if (currentCLK != lastCLK && currentCLK == LOW) {
if (const int dt = digitalRead(ENCODER_DT_PIN); dt != currentCLK) { if (const int dt = digitalRead(ENCODER_DT_PIN); dt != currentCLK) {
@@ -182,14 +157,15 @@ void runPasswordSetup() {
} }
lastCLK = currentCLK; lastCLK = currentCLK;
// maskování po nečinnosti
if (!masked && millis() - lastEncoderMove >= MASK_DELAY_MS) { if (!masked && millis() - lastEncoderMove >= MASK_DELAY_MS) {
masked = true; masked = true;
showSetupScreen(false); showSetupScreen(false);
} }
// potvrzení tlačítkem // přerušení konzumace příznaku místo přímého čtení pinu
if (digitalRead(BUTTON_PIN) == HIGH) { if (buttonPressed) {
buttonPressed = false;
savedPassword = formatCode(passwordValue); savedPassword = formatCode(passwordValue);
ConfirmSound(); ConfirmSound();
@@ -199,15 +175,10 @@ void runPasswordSetup() {
lcd.printstr("* * * * "); lcd.printstr("* * * * ");
delay(1500); delay(1500);
// přechod do normálního režimu
lcd.setCursor(0, 0); lcd.setCursor(0, 0);
lcd.printstr("Zadejte heslo: "); lcd.printstr("Zadejte heslo: ");
lcd.setCursor(0, 1); lcd.setCursor(0, 1);
lcd.printstr(" "); lcd.printstr(" ");
// debounce
while (digitalRead(BUTTON_PIN) == HIGH) {}
delay(50);
} }
} }
} }
@@ -224,41 +195,40 @@ void setup() {
lcd.printstr("Program startuje"); lcd.printstr("Program startuje");
lcd.setCursor(0, 1); lcd.setCursor(0, 1);
lcd.printstr("NastavujeSeMotor"); lcd.printstr("NastavujeSeMotor");
delay(100); delay(100);
Serial.println("Nastavování motoru."); Serial.println("Nastavování motoru.");
servo.attach(SERVO_PIN); servo.attach(SERVO_PIN);
servo.write(CLOSED_POS); // ve výchozím stavu je zamčeno servo.write(CLOSED_POS);
Serial.println("Motor nastaven."); Serial.println("Motor nastaven.");
lcd.setCursor(0, 1); lcd.setCursor(0, 1);
lcd.printstr("Motor nastaven. "); lcd.printstr("Motor nastaven. ");
delay(100); delay(100);
lcd.setCursor(0, 1); lcd.setCursor(0, 1);
lcd.printstr("NastavujiSePiny."); lcd.printstr("NastavujiSePiny.");
Serial.println("Nastavování pinů"); Serial.println("Nastavování pinů");
// Credit: formátování vymyšleno přes Claude Sonnet 4.6 pinMode(BUTTON_PIN, INPUT_PULLDOWN); Serial.println("Piny: 1/8");
pinMode(BUTTON_PIN, INPUT_PULLDOWN); Serial.println("Piny: 1/8"); pinMode(RGB_RED_PIN, OUTPUT); Serial.println("Piny: 2/8");
pinMode(RGB_RED_PIN, OUTPUT); Serial.println("Piny: 2/8"); pinMode(RGB_GREEN_PIN, OUTPUT); Serial.println("Piny: 3/8");
pinMode(RGB_GREEN_PIN, OUTPUT); Serial.println("Piny: 3/8"); pinMode(RGB_BLUE_PIN, OUTPUT); Serial.println("Piny: 4/8");
pinMode(RGB_BLUE_PIN, OUTPUT); Serial.println("Piny: 4/8");
pinMode(ENCODER_CLK_PIN, INPUT); Serial.println("Piny: 5/8"); pinMode(ENCODER_CLK_PIN, INPUT); Serial.println("Piny: 5/8");
pinMode(ENCODER_DT_PIN, INPUT); Serial.println("Piny: 6/8"); pinMode(ENCODER_DT_PIN, INPUT); Serial.println("Piny: 6/8");
pinMode(SERVO_PIN, OUTPUT); Serial.println("Piny: 7/8"); pinMode(SERVO_PIN, OUTPUT); Serial.println("Piny: 7/8");
pinMode(BUZZER_PIN, OUTPUT); Serial.println("Piny: 8/8"); pinMode(BUZZER_PIN, OUTPUT); Serial.println("Piny: 8/8");
Serial.println("Piny nastaveny."); Serial.println("Piny nastaveny.");
lcd.setCursor(0, 1); lcd.setCursor(0, 1);
lcd.printstr("Piny nastaveny. "); lcd.printstr("Piny nastaveny. ");
delay(1000); delay(1000);
// registrace přerušení RISING: stisk při HIGH (INPUT_PULLDOWN)
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), onButtonPress, RISING);
runPasswordSetup(); runPasswordSetup();
} }
void loop() { void loop() {
// Serial Monitor příkaz "unblock" zruší blokaci
if (Serial.available()) { if (Serial.available()) {
String cmd = Serial.readStringUntil('\n'); String cmd = Serial.readStringUntil('\n');
cmd.trim(); cmd.trim();
@@ -280,10 +250,8 @@ void loop() {
} }
} }
// blokace čeká odpočítávání, neblokuje loop
if (lockedOut) { if (lockedOut) {
if (const unsigned long elapsed = millis() - lockoutStart; elapsed >= LOCKOUT_DURATION_MS) { if (const unsigned long elapsed = millis() - lockoutStart; elapsed >= LOCKOUT_DURATION_MS) {
// konec blokace
lockedOut = false; lockedOut = false;
wrongAttempts = 0; wrongAttempts = 0;
Serial.println("Blokace skoncila."); Serial.println("Blokace skoncila.");
@@ -293,7 +261,6 @@ void loop() {
lastEncoderMove = millis(); lastEncoderMove = millis();
showEntryScreen(true); showEntryScreen(true);
} else { } else {
// odpočítávání na LCD aktualizuje jen každou sekundu
static unsigned long lastCountdownUpdate = 0; static unsigned long lastCountdownUpdate = 0;
if (millis() - lastCountdownUpdate >= 1000) { if (millis() - lastCountdownUpdate >= 1000) {
lastCountdownUpdate = millis(); lastCountdownUpdate = millis();
@@ -307,11 +274,10 @@ void loop() {
} }
BlockedLED(); BlockedLED();
} }
return; // zbytek loop se přeskočí buttonPressed = false; // zahazovat stisky během blokace
return;
} }
// čtení enkodéru jen pokud je zamčeno
// (při odemčeném stavu enkodér nemá smysl)
if (!isOpen) { if (!isOpen) {
const int currentCLK = digitalRead(ENCODER_CLK_PIN); const int currentCLK = digitalRead(ENCODER_CLK_PIN);
if (currentCLK != lastCLK && currentCLK == LOW) { if (currentCLK != lastCLK && currentCLK == LOW) {
@@ -326,24 +292,20 @@ void loop() {
} }
lastCLK = currentCLK; lastCLK = currentCLK;
// maskování po nečinnosti
if (!masked && millis() - lastEncoderMove >= MASK_DELAY_MS) { if (!masked && millis() - lastEncoderMove >= MASK_DELAY_MS) {
masked = true; masked = true;
showEntryScreen(false); showEntryScreen(false);
} }
} }
// tlačítko // konzumace příznaku z přerušení
if (digitalRead(BUTTON_PIN) == HIGH) { if (buttonPressed) {
// debounce buttonPressed = false;
while (digitalRead(BUTTON_PIN) == HIGH) {}
delay(50);
if (!isOpen) { if (!isOpen) {
// pokus o odemčení
if (formatCode(password) == savedPassword) { if (formatCode(password) == savedPassword) {
isOpen = true; isOpen = true;
wrongAttempts = 0; // reset po úspěšném odemčení wrongAttempts = 0;
OpenLock(); OpenLock();
OpenLED(); OpenLED();
OpenSound(); OpenSound();
@@ -353,7 +315,6 @@ void loop() {
lcd.setCursor(0, 1); lcd.setCursor(0, 1);
lcd.printstr("Stiskni pro zamk"); lcd.printstr("Stiskni pro zamk");
} else { } else {
// špatné heslo
wrongAttempts++; wrongAttempts++;
Serial.print("Spatny pokus: "); Serial.print("Spatny pokus: ");
Serial.print(wrongAttempts); Serial.print(wrongAttempts);
@@ -363,7 +324,6 @@ void loop() {
CloseSound(); CloseSound();
if (wrongAttempts >= MAX_WRONG_ATTEMPTS) { if (wrongAttempts >= MAX_WRONG_ATTEMPTS) {
// spustit blokaci
lockedOut = true; lockedOut = true;
lockoutStart = millis(); lockoutStart = millis();
Serial.println("Zamek zablokovan na 30 sekund. Pouzij 'unblock' pro zruseni."); Serial.println("Zamek zablokovan na 30 sekund. Pouzij 'unblock' pro zruseni.");
@@ -382,14 +342,12 @@ void loop() {
lcd.printstr(" "); lcd.printstr(" ");
delay(1500); delay(1500);
// návrat na zadávací obrazovku, hodnota zachována
masked = false; masked = false;
lastEncoderMove = millis(); lastEncoderMove = millis();
showEntryScreen(true); showEntryScreen(true);
} }
} }
} else { } else {
// zamčení
isOpen = false; isOpen = false;
CloseLock(); CloseLock();
CloseLED(); CloseLED();
@@ -401,4 +359,4 @@ void loop() {
showEntryScreen(true); showEntryScreen(true);
} }
} }
} }