Проблемы проекта Arduino с операторами IF

#c #if-statement #arduino

#c #if-statement #arduino

Вопрос:

В настоящее время я создаю проект для своей школы, и у меня возникает проблема, когда код выполняется несколько раз. Я использую Arduino Mega 2560. Проект, который я делаю, — это создание сейфа / ящика, для открытия которого требуется PIN-код. В порядке того, как все должно работать, следует:

  1. ИК-приемник получает сигнал от пульта дистанционного управления, который активирует сигнал. Когда система активна, загорается синий светодиод. Когда система неактивна, она ничего не делает.
  2. Когда система активна, она может считывать входные данные с контактной площадки 4×4. Если он получает неправильный код, он загорается красным светодиодом, а затем очищает данные, чтобы снова использовать код. Если задан правильный код, включается зеленый светодиод и поворачивает серводвигатель на 90 градусов.
  3. После включения правильного кода нажимается кнопка для возврата двигателя в положение 0 градусов и деактивации системы.

Проблема в том, что после нажатия кнопки я не могу повторно активировать систему. По сути, это становится 1 и готово. Код для проекта можно найти ниже.

 #include <Keypad.h>
#include <Servo.h>
#include <IRremote.h>

Servo servo;

const byte ROWS = 4; 
const byte COLS = 4; 

#define Passcode_L 5

char Data[Passcode_L];
char Master[Passcode_L]="14B6";

char hexaKeys[ROWS][COLS] = {
   {'1', '2', '3', 'A'},
   {'4', '5', '6', 'B'},
   {'7', '8', '9', 'C'},
   {'*', '0', '#', 'D'}
 };
  
byte rowPins[ROWS] = {22, 24, 26, 28}; 
byte colPins[COLS] = {30, 32, 34, 36}; 
byte data_count = 0;
char customKey;
  
Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS); 

const int led_blue = 23;
const int led_green = 25;
const int led_red = 27;


const int BUTTON = 47;
int BUTTONstate = 0;


const int RECV_PIN = 44;
int togglestate = 0;
IRrecv irrecv(RECV_PIN);
decode_results results;

void setup(){
  Serial.begin(9600);
  irrecv.enableIRIn();
  servo.attach(38);
  servo.write(0);
  pinMode(led_blue, OUTPUT);
  pinMode(led_green, OUTPUT);
  pinMode(led_red, OUTPUT);
  pinMode(BUTTON, INPUT);
}
    
void loop(){
  char customKey = customKeypad.getKey();

  BUTTONstate = digitalRead(BUTTON);
  if (irrecv.decode(amp;results)){
    switch(results.value){
      case 0xFFA25D:
      // Toggle BLUE LED On or Off
      if(togglestate==0){
        digitalWrite(led_blue, HIGH);
        togglestate=1;
      }
      else {
        digitalWrite(led_blue, LOW);
        togglestate=0;
      }
    break;  
    }
    irrecv.resume();
  }
  
  if (customKey){
    Data[data_count] = customKey;
    data_count  ;
  }
  
  if (data_count == Passcode_L - 1 amp;amp; togglestate==1) {
     if (!strcmp(Data, Master)) {
      //password is correct
      digitalWrite(led_green, HIGH);
      delay(500);
      servo.write(90);
      delay(1500);
      digitalWrite(led_green, LOW);
     }
     else {
      // Password is incorrect
      digitalWrite(led_red, HIGH);
      delay(1000);
      digitalWrite(led_red, LOW);
    }
    clearData();
 }
 
 if (BUTTONstate !=0){
  servo.write(0);
  togglestate=0;
  digitalWrite(led_blue, LOW);
  }


  
}

void clearData() {
  // Go through array and clear data
  while (data_count != 0) {
    Data[data_count--] = 0;
  }
  return;
}
 

Комментарии:

1. Вы составили блок-схему того, как ожидается, что элемент будет работать? Если это так, сравните его с написанным вами кодом. В противном случае построение блок-схемы, которая документирует ожидаемый поток программы, значительно упростит оценку того, где в вашем коде вы попросили микропроцессор сделать что-то, чего вы бы предпочли, чтобы он не делал..

Ответ №1:

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

Попробуйте это: у вас есть блокировка, у нее есть эти разные состояния:

  • заблокировано (стабильное состояние)
  • ввод пароля (переходный -> ошибка или разблокировка) (требуется прерванный тайм-аут ввода?)
  • показать ошибку пароля (переходный -> заблокировано)
  • разблокировка (переходный -> разблокированный)
  • разблокировано (стабильно)
  • блокировка (переходный -> заблокирован)

Если вы будете отслеживать состояние своего устройства и разделять логику для каждого состояния, управлять логикой и отлаживать ее станет намного проще. Это также делает добавление функций и дополнительной логики почти тривиальным. Я думаю, вам следует попробовать что-то вроде этого:

 #include <Keypad.h>
#include <Servo.h>
#include <IRremote.h>

const byte ROWS = 4; 
const byte COLS = 4; 
  
// IO pins

byte rowPins[ROWS] = {22, 24, 26, 28}; 
byte colPins[COLS] = {30, 32, 34, 36}; 
  
const int led_blue = 23;
const int led_green = 25;
const int led_red = 27;
const int BUTTON = 47;
const int RECV_PIN = 44;

// devices

Servo servo;
IRrecv irrecv(RECV_PIN);

char hexaKeys[ROWS][COLS] = {
   {'1', '2', '3', 'A'},
   {'4', '5', '6', 'B'},
   {'7', '8', '9', 'C'},
   {'*', '0', '#', 'D'}
 };

Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS); 

// lock states    
class enum LockState
{
    locked,
    passcodeEntry,
    passcodeError,
    unlocking,
    unlocked,
    locking,
};

// lock data
LockState lockState = locked;
byte data_count = 0;
bool passcodeOK = false;
unsigned int timeoutTS;

const char Master[] = "14B6";
#define Passcode_L (sizeof(Master) - 1)
#define TIMEOUT (30000)  // 30 seconds timeout for data entry

void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn();
  servo.attach(38);
  servo.write(0);
  pinMode(led_blue, OUTPUT);
  pinMode(led_green, OUTPUT);
  pinMode(led_red, OUTPUT);
  pinMode(BUTTON, INPUT);
}
    
void loop() 
{
  switch (lockState) 
  {
  default:
  case LockState::locked:
    {
      // locked, resting.  turn LEDs off, and clear received key code.
      digitalWrite(led_blue, LOW);
      digitalWrite(led_red, LOW);
      digitalWrite(led_green, LOW);
      data_count = 0;

      decode_results results;
      if (irrecv.decode(amp;results) amp;amp; results.value == 0xFFA25D) 
      {
        timeoutTS = (unsigned int)millis();
        lockState = LockState::passcodeEntry;
      }
    }
    break;
  
  case LockState::passcodeEntry:
    {
      // read keypad, blue LED is on.  
      digitalWrite(led_blue, HIGH);

      // this assumes customKeypad debounces the key and returns a non-zero
      // value only once for each key, when it is initially pressed.
      char customKey = customKeypad.getKey(); 
      if (customKey)
      {
        timeoutTS = (unsigned int)millis();

        if (data_count == 0)
          passcodeOK = true;

        // check passcode.
        passcodeOK amp;= (customKey == Master[data_count  ]);
        if (data_count >= Passcode_L) 
        {
          if (passcodeOK)
            lockState = LockState::unlocking;
          else
            lockState = LockState::passcodeError;
        }

        // check for timeout, we wouldn't want to let anyone enter codes
        // if the owner leaves the room now with the IR remote in his pocket
        if ((unsigned int)millis() - timeoutTS > TIMEOUT) 
            lockState = LockState::locked;
    } 
    break;

  case LockState::passcodeError:
    {
      // turn on red LED for 1 second. then let use retry a passcode. 
      digitalWrite(led_red, HIGH);
      delay(1000);
      digitalWrite(led_red, LOW);
      data_count = 0;
      lockState = LockState::passcodeEntry;
    }
    break;

  case LockState::unlocking:
    {
      digitalWrite(led_green, HIGH);
      servo.write(90);
      delay(1500);
      digitalWrite(led_green, LOW);
      lockState = LockState::unlocked;
    }
    break;

  case LockState::unlocked:
    {
      // wait for button press.
      if (digitalRead(BUTTON))
        lockState = LockState::locking;
    }
    break;

  case LockState::locking:
    {
       // turn the lock back, go back to locked state
       servo.write(0);
       loackState = LockState::locked;
    }
    break;
  }
}
 

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