Я пытаюсь заставить работать последовательность паролей из 3 кнопок.

Я пытаюсь создать систему паролей, в которой при нажатии 3 кнопок в определенном порядке (1, 2, 3, 3, 1) загорается светодиод. Но поскольку я собираюсь реализовать это в более крупном коде, я не могу позволить методу voidloop() вечно ждать этого.

Я думаю, что моя проблема в циклах while и заставляет их ждать нажатия одной из кнопок, но я не знаю, как заставить это работать.

спасибо

#define greenLED A0
#define redLED A1

int button1 = 5;
int button2 = 4;
int button3 = 3;

void setup() {
  // поместите сюда свой код установки для однократного запуска:
  pinMode(A0, OUTPUT);
  pinMode(A1, OUTPUT);
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(button3, INPUT_PULLUP);
}

void loop() {
  // поместите сюда свой основной код для многократного запуска:
  int total = 0;
  while(digitalRead(button1) == LOW && digitalRead(button2) == LOW && digitalRead(button3) == LOW){
    while(digitalRead(button1) == LOW && digitalRead(button2) == LOW && digitalRead(button3) == LOW){
      while(digitalRead(button1) == LOW && digitalRead(button2) == LOW && digitalRead(button3) == LOW){
        while(digitalRead(button1) == LOW && digitalRead(button2) == LOW && digitalRead(button3) == LOW){
          while(digitalRead(button1) == LOW && digitalRead(button2) == LOW && digitalRead(button3) == LOW){
            if(digitalRead(button1) == HIGH){
              total = total + 1;
            }
          }
          if(digitalRead(button2)==HIGH){
            total = total+1;
          }
        } 
        if(digitalRead(button3)==HIGH){
          total = total + 1;
        }
      }
      if(digitalRead(button3)==HIGH){
        total = total+1;
      }
    }
    if(digitalRead(button1) == HIGH){
      total = total+1;
    }
  }
  if(total == 5){
    digitalWrite(A0, HIGH);
    delay(2000);
    digitalWrite(A0, LOW);
  }else{
    digitalWrite(A1, HIGH);
    delay(2000);
    digitalWrite(A1, LOW);
  }
}

, 👍2

Обсуждение

вам нужно, чтобы каждая нажатая кнопка помещала что-то в стек, а затем исследовала стек на наличие закономерностей; никакого ожидания, никакой блокировки, никакой вложенности, никакой сложности. String может работать как стек, и у него есть встроенные инструменты для поиска шаблонов., @dandavis


4 ответа


2

Не программируя ничего, используйте что-то вроде этого:

  • Инициализация:

    • Создайте (глобальную) переменную строки («12331»), которая является кодом.
    • Создайте (глобальную) переменную типа int, которая показывает количество правильных чисел на данный момент (первоначально 0)
  • В цикле while:

    • Проверьте, какая кнопка нажата
    • Если это правильная кнопка
    • увеличьте переменную int
    • Если эта переменная равна 5 (что означает, что все цифры в порядке), зажгите светодиод.
    • Задержка
    • Снова установите переменную 0
    • Выключите светодиод.
    • Если нажата неправильная кнопка
    • Если оно равно первому значению («1»)
      • Присвойте переменной значение 1 (так как неправильное число — это первое правильное число)
    • Остальное
      • Установите переменную в 0
,

0

Для чего-то подобного, вероятно, будет полезен конечный автомат. Поскольку это программное обеспечение, мы можем пропустить назначение регистров и напрямую использовать таблицу состояний.

The table would look as follows:
1. If button 1 is pressed, 2. Else 1.
2. If button 2 is pressed, 3. Else 1.
3. If button 3 is pressed, 4. Else 1.
4. Run task, go to 1.

Код потребует от вас добавления целого числа или байта вне setup или loop. Тогда он будет содержать номер штата.

Внутри loop вы можете добавить операторы if для чтения контактов кнопок.

Если одна из кнопок нажата (срабатывает оператор if), вы можете сравнить состояние с условием, перечисленным выше. Если состояние и правильное условие совпадают, установите новое состояние.

В противном случае сбросьте его в исходное состояние (0 по умолчанию, если вы не измените его явно при программировании, и большинство вещей имеют индекс 0. Вы также можете использовать 1, но вы должны не забыть инициализировать его значением 1, когда объявление переменной состояния, т.е. byte state=1;)

В приведенном ниже примере кода предполагается, что кнопки подключены между GND и входным контактом GPIO на Arduino. Поскольку используются внутренние подтягивающие резисторы, внешние резисторы не нужны. Единственная незначительная проблема заключается в том, что он инвертирует состояние кнопок: 0 == нажата, 1 == отпущена (не нажата).

byte state = 0;
bool pressed=false;//используется для предотвращения повторов

void setup(){
  pinMode(pin1,INPUT_PULLUP);
  //Повторяем для других контактов. Замените номер контакта номером.
}

void loop(){
  if (digitalRead(pin1)==0 && !pressed){
    pressed=true;
    if (state==0){
      state=1; //переходим в следующее состояние.
    }
    else{
      state=0; //сбрасываем состояние. Необходимо даже здесь, поскольку кнопка может быть
               //нажата позже, в неподходящее время. Добавьте «иначе, если (state==#)»
               //разделы, если кнопку необходимо повторно использовать в более поздних состояниях.
   }
   if (digitalRead(pin1) && pressed){
     pressed=false;
   }
   //Добавьте сюда еще операторы if, следуя шаблону, для дополнительных кнопок, которые вам нужны.
}

Дальнейшие улучшения могут включать получение независимых переменных обнаружения нажатия (по одной для каждой кнопки), но возможность регистрировать одновременное нажатие только одной кнопки, вероятно, на самом деле хорошая вещь.

,

0

Вы можете разделить это на две разные проблемы, и я предлагаю вам отслеживать их по одному.

Первая проблема — распознать нажатия кнопок. Это не так как бы тривиально это ни звучало. На этот раз digitalRead() сообщает вам, кнопка нажата, но это не совсем то, что вам нужно. Вы хотите обнаружить дискретные «события» прессы. Другими словами, вы должны помнить предыдущее состояние кнопки и записывать событие нажатия только тогда, когда состояние меняется с HIGH на LOW. Это называется «обнаружение края». Делая это с механическими кнопками, вы обязательно должны сделать некоторые своего рода отскок.

Я не буду подробно останавливаться на этой теме, так как существует несколько библиотек Arduino. доступен для устранения дребезга кнопок, которые также определяют края. Как только вы разобравшись с этим, каждая из ваших кнопок будет иметь метод .fell() это говорит вам, была ли эта кнопка только что нажата. Затем внутри loop() вы можете сделать что-то вроде

char key = 0;  // key pressed by the user, 0 = none
if (button1.fell()) key = '1';
else if (button2.fell()) key = '2';
else if (button3.fell()) key = '3';
if (key) {  // some key has been pressed
    // process key...
}

Часть «ключ процесса...» изначально должна быть простой Serial.write(c);. Таким образом, вы можете использовать последовательный монитор для проверки что эта часть программы работает правильно. Только когда это случае можно переходить ко второй части.

Вторая проблема — обработка нажатий клавиш для обнаружения был ли введен правильный код. Возможны различные подходы. Один из них — хранить в массиве 5 последних ключей. введенный пользователем, и сравните этот массив с правильным кодом, чтобы узнать, была ли запись верной. Это не очень элегантно, так как это будет требуют хранения большего количества информации, чем необходимо, и смещения массива каждый раз при вводе новой клавиши.

Более элегантное решение предлагает Мишель Кейзерс. Позвольте мне перефразировать это на С++. Внутри loop():

if (key) {  // была нажата какая-то клавиша
    if (check_code(key)) {  // введен правильный код
        digitalWrite(LED, HIGH);
        delay(1000);
        digitalWrite(LED, LOW);
    }
}

где логика проверки правильности набранного кода:

bool check_code(char key)
{
    const char code[] = "12331";
    const uint8_t length = sizeof code - 1;
    static uint8_t i = 0;  // количество правильных символов на данный момент

    if (key == code[i]) {  // правильный ключ
        ++i;               // еще один правильный символ
        if (i == length) {
            i = 0;         // перезагрузить
            return true;   // запись в отчете верна
        }
    } else {               // неправильный ключ
        if (key == '1')
            i = 1;
        else
            i = 0;
    }
    return false;
}

Обратите внимание, что часть, которая обрабатывает неправильное нажатие клавиши, специфична для этого конкретный код. Если вы хотите, чтобы программа была универсальной, чтобы вы могли измените секретный код, просто изменив строку code, он может получить более сложный. Например, если правильный код «12313» и пользователь набрал «1231», у него есть 4 правильных ключа. Затем

  • если он наберет 1, у него теперь будет 1 правильное нажатие клавиши.
  • если он наберет 2, у него будет 2 правильных ключа (а именно «12»)
  • если он наберет 3, значит, он завершил код.

Обычно для этого требуется вернуться к строке кода и поиск самого длинного совпадения. Вот мое мнение: заменить часть else в приведенной выше функции с помощью:

// ...
} else {               // неправильный ключ
    for (int j = i-1; j >= 0; j--) {
        if (key == code[j]) {
            int k;
            for (k = 0; k < j; k++) {
                if (code[k] != code[i-j+k])
                    break;
            }
            if (k == j) {  // найдено частичное совпадение
                i = j + 1;
                return false;
            }
        }
    }
    i = 0;  // нет частичного совпадения
}
// ...

Здесь j — это позиция в строке кода, где находится ключ, введенный пользователь может соответствовать.

,

0

Как упоминает Эдгар Боне в своем ответе, устранение дребезга кнопок является критически важным компонентом, и существуют библиотеки, упрощающие эту часть требования. Я использовал библиотеку Bounce2 с тремя кнопочными переключателями NO. Следующий скетч работает, но он все еще нуждается в одном или двух улучшениях.

Одна проблема возникает, если пользователь нажимает 4 кнопки, затем уходит и возвращается через 1 час. Все, что им нужно сделать, это нажать последнюю правильную кнопку в последовательности, после чего они аутентифицируются. Это можно легко исправить, введя таймер на основе millis(), который запускается, например, при первом нажатии кнопки и останавливается через 5 секунд. Этот таймер сбросит переменную counter и массив байтов buttonsPressed[].

Возможно, будет полезна кнопка «ввести» код и/или кнопка «отмена».

#include <Bounce2.h>

const byte sequenceLength = 5;
byte sequence[sequenceLength] = {1, 2, 3, 3, 1};

byte buttonsPressed[sequenceLength];
byte counter = 0;
const byte ledPinCodeAccepted = 2;
const byte ledPinCodeDenied = 3;
const byte button1Pin = 10;
const byte button2Pin = 11;
const byte button3Pin = 12;
const unsigned long debouncerInterval = 50;

Bounce button1 = Bounce();
Bounce button2 = Bounce();
Bounce button3 = Bounce();

void setup(){
  pinMode(ledPinCodeAccepted, OUTPUT);
  pinMode(ledPinCodeDenied, OUTPUT);
  button1.attach(button1Pin, INPUT_PULLUP);
  button1.interval(debouncerInterval);
  button2.attach(button2Pin, INPUT_PULLUP);
  button2.interval(debouncerInterval);
  button3.attach(button3Pin, INPUT_PULLUP);
  button3.interval(debouncerInterval);
}

void loop(){

  if(button1.update()){
    if(button1.read() == 0){
      buttonsPressed[counter] = 1;
      counter++;
    }
  }

  if(button2.update()){
    if(button2.read() == 0){
      buttonsPressed[counter] = 2;
      counter++;
    }
  }

  if(button3.update()){
    if(button3.read() == 0){
      buttonsPressed[counter] = 3;
      counter++;
    }
  }

  if(counter == sequenceLength){
    counter = 0;
    digitalWrite(ledPinCodeAccepted, LOW);
    digitalWrite(ledPinCodeDenied, LOW);
    for(byte i = 0; i < sequenceLength; i++){
      if(sequence[i] == buttonsPressed[i]){counter++;}
      else{break;}
    }
    if(counter == sequenceLength){
      digitalWrite(ledPinCodeAccepted, HIGH);
    }
    else{
      digitalWrite(ledPinCodeDenied, HIGH);
    }
    counter = 0;
  }
}
,

Здесь есть небольшая проблема: если пользователь вводит «1231», упс! Тогда «12331», эта программа _не_ примет его ввод как действительный. Ожидаемое поведение любого вида блокировки клавиатуры — принять ее., @Edgar Bonet

Возможно, будет полезна кнопка «ввести» код и/или кнопка «отмена»., @VE7JRO

Блокировки клавиатуры — очень распространенная форма пользовательского интерфейса. Такая повсеместность порождает ожидания пользователей, в том числе простоту и удобство использования. Если вы требуете, чтобы пользователь нажимал кнопку «Отмена», когда он «упс», вы нарушаете это ожидание и, таким образом, ухудшаете пользовательский опыт., @Edgar Bonet

ОП говорит, что в этом вопросе они создают «систему паролей». Это то же самое, что и блокировка клавиатуры? Я не знаю. Я даже не знаю, что такое «система паролей» :) Кнопки ввода/отмены очень распространены здесь, в Канаде. Посмотрите, например, на банковские «банкоматы». Пользователь вынужден нажать кнопку ввода после завершения ввода пароля. Существует также кнопка отмены и период «тайм-аута», когда бездействие клавиатуры приводит к извлечению вашей банковской карты из автомата., @VE7JRO

Я не знаю, заставляет ли банкомат банка нажимать кнопку отмены, а затем снова вводить пароль, или вы можете просто ввести пароль после неправильных цифр и нажать Enter., @VE7JRO