Kodlås med Arduino

Ett enkelt kodlås består av en knappsats, en kontroller och ett relä som aktiverar ett elektriskt lås. Här visar vi hur man bygger ihop och programmerar ett väldigt enkelt sådant med ett Arduino-kort.

Kopplingen

Här nedan kan du se hur du kopplar ihop modulerna. Knappsatsen kopplas till digitala anslutningarna 2 till 8. Reläet kopplas till anslutning 13 samt till +5V och GND för att ge det ström. Låset kräver mycket ström och högre spänning än vad Arduino-kortet kan ge ut så det behöver ha separat strömmatning från en nätadapter av något slag. Arduino-kortet kan få ström från samma nätaggregat genom att dukopplar in Vin och GND till plus och minus på nätaggregatet. Det är mycket viktigt att du kopplar in plus från nätaggregatet till Vin på Arduino-kortet och inte någon annan anslutning för då kommer du skada eller helt förstöra kortet.

Beskrivning av koden

Konstanter

Först behöver vi sätta upp konstanter för diverse värden som vi kommer behöva använda i resten av koden. De vi behöver är följande:

  • code_length – Antal siffror i koden.
  • lock_code – Den hemliga koden som öppnar låset.
  • button_timeout – Hur lång tid det får ta som mest mellan knapptryckningar i millisekunder.
  • lock_open_time – Hur länge låset hålls öppet när rätt kod slagits in i millisekunder.
  • relay_pin – pinne som relämodulen är inkopplad till.
  • row_pins – pinnar som rad-anslutningarna på knappsatsen är inkopplade till.
  • col_pins – pinnar som kolumn-anslutningarna på knappsatsen är inkopplade till.
const int code_length = 4;
const int lock_code[code_length] = { 2, 14, 3, 10 };
const int code_timeout = 5000;
const int lock_open_time = 5000;
const int relay_pin = 13;

const int row_pins[4] = {
  5, 4, 3, 2
};

const int col_pins[4] = {
  6, 7, 8, 9
};

Setup

I setup() ställer vi in alla pinnar som vi använder till utgångar eller ingångar. Vi aktiverar även seriekommunikationen så att vi kan skicka statusmeddelanden till datorn för felsökning.

void setup() {
  Serial.begin(115200);
  for (int row = 0; row < 4; row++) {
    pinMode(row_pins[row], OUTPUT);
  }
  for (int col = 0; col < 4; col++) {
    pinMode(col_pins[col], INPUT_PULLUP);
  }
  pinMode(relay_pin, OUTPUT);
}

Läsa av knappsatsen

Vi skapar sedan en funktion kallad readKeyboard() som vi använder för att läsa av knappsatsen. Funktionen skannar av knapparna rad för rad, kolumn för kolumn. Exakt hur detta fungerar kan du läsa mer om i vår guide för hur man använder ett matris-tangentbord med Arduino. I grova drag så sätter koden en radutgång till låg och övriga till hög och läser sedan av alla kolumner. Om en kolumn är låg så betyder det att knappen som är kopplad till just den raden och kolumnen är nertryckt. Eftersom den här funktionen kommer att anropas om och om igen väldigt snabbt så behöver vi kontrollera om vi redan reagerat på en knapptryckning så att vi bara reagerar en enda gång. Vi lagrar därför numret på den knapp som tryckts in i en global variabel ”last_button”. Varje gång vi ser att en knapp är nertryckt kontrollerar vi om det är samma som är lagrat i ”last_button”.

int last_button = 0;

int readKeyboard() {
  int button = 0;
  // Sätt alla radutgångar till höga innan vi börjar.
  for (int row = 0; row < 4; row++) {
    digitalWrite(row_pins[row], HIGH);
  }

  // Scanna av tangentbordet.
  for (int row = 0; row < 4; row++) {
    digitalWrite(row_pins[row], LOW);
    for (int col = 0; col < 4; col++) {
      if (digitalRead(col_pins[col]) == LOW) {
        // En knapp är nertryckt!
        button = (row * 4) + col + 1;
        if (button == last_button) {
          // Det är fortfarande samma knapp som förra gången.
          return 0;
        }
        else {
          // En ny knapptryckning - vänta lite för att undvika kontaktstuds
          // och kom sedan ihåg vilken knapp det är så att vi inte reagerar
          // på den igen nästa gång.
          delay(200);
          last_button = button;
          return button;
        }
      }
    }
    digitalWrite(row_pins[row], HIGH);
  }
  // Ingen knapp nertryckt. Nollställ last_button.
  last_button = 0;
  return 0;
}

Globala variabler

För vår huvudloop behvöver vi ett antal variabler för att hålla koll på status etc. Eftersom loop() anropas om och om igen, och avslutas mellan varje varv så måste vi ha dessa variabler globala utanför funktionen istället för inne i den som annars är standard.

  • press_timeout – Håller reda på när en en ny kod började tryckas in.
  • button_presses – Denna lista är till för att lagra de senaste fyra knapptryckningarna.
  • press_count – Håller koll på hur många knapptryckningar som finns i listan ovan.
unsigned long press_timer = 0;
int button_presses[code_length];
int press_count = 0;

Huvud-loopen

I vår huvudloop börjar vi med att kontrollera vår timer för att se om det har gått för lång tid sedan en ny kod började matas in och i så fall nollställer vi vår räknare som håller koll på antal knapptryckningar. Därefter läser vi av knappsatsen. Så länge funktionen lämnar tillbaka noll har inget hänt, men om det inte är noll har vi en ny knapptryckning. Vi lagrar knappen i vår lista och ökar räknaren med ett. Vi kollar sedan om detta var första knapptryckningen och startar i så fall vår timer genom att lagra aktuell tid i vår timer-variabel.

void loop() {
  if (millis() - press_timer > code_timeout) {
    press_count = 0;
  }

  int button = readKeyboard();

  if (button > 0) {
    button_presses[press_count] = button;
    press_count++;

    if (press_count == 1) {
      press_timer = millis();
    }

Kontrollera inslagen kod

Vi kollar sedan om vi har fått lika många siffror inmatade som det är i koden genom att jämföra vår räknare med längden på koden. Om de är lika stora så är det dags att jämföra de siffror som matas in med de i vår kod. Det gör vi med en for-loop. Men först skapar vi en ny variabel ”match” som vi sätter till sant. Vi går sedan igenom de inmatade siffrorna och jämför dem en och en med motsvarande siffra i koden. Om någon av dem inte är samma så sätter vi ”match” till falskt. Det betyder att när for-loopen är slut så kommer ”match” vara sann enbart om rätt kod blivit inmatad.

När en korrekt kod slagits in så återstår bara att öppna låset. Här använder vi delay() för att pausa medans låset hålls öppet en stund. Normalt vill man undvika delay() när man använder timers eftersom det stoppar upp hela koden, men i vårt fall så är det precis det vi vill. Det förhindrar att nya siffror matas in innan låset stängts igen.

    if (press_count >= code_length) {
      press_count = 0;
      Serial.print("Inmatade kod: ");
      for (int i = 0; i < code_length; i++) {
        Serial.print(button_presses[i]);
        Serial.print(" ");
      }
      Serial.println();
      bool match = true;
      for (int i = 0; i < code_length; i++) {
        if (button_presses[i] != lock_code[i]) {
          match = false;
        }
      }
      if (match) {
        Serial.println("Rätt kod");
        Serial.println("Lås öppnas");
        digitalWrite(relay_pin, HIGH);
        delay(lock_open_time);
        digitalWrite(relay_pin, LOW);
        Serial.println("Lås stängs");
      }
      else {
        Serial.println("Fel kod");
      }
    }

Hela koden

const int code_length = 4;
const int lock_code[code_length] = { 2, 14, 3, 10 };
const int code_timeout = 5000;
const int lock_open_time = 5000;
const int relay_pin = 13;

const int row_pins[4] = {
  5, 4, 3, 2
};

const int col_pins[4] = {
  6, 7, 8, 9
};

void setup() {
  Serial.begin(115200);
  // Ställ in anslutningar som ingångar eller utgångar
  for (int row = 0; row < 4; row++) {
    pinMode(row_pins[row], OUTPUT);
  }
  for (int col = 0; col < 4; col++) {
    pinMode(col_pins[col], INPUT_PULLUP);
  }
  pinMode(relay_pin, OUTPUT);
}


int last_button = 0;

int readKeyboard() {
  int button = 0;
  // Sätt alla radutgångar till höga innan vi börjar.
  for (int row = 0; row < 4; row++) {
    digitalWrite(row_pins[row], HIGH);
  }

  // Scanna av tangentbordet.
  for (int row = 0; row < 4; row++) {
    digitalWrite(row_pins[row], LOW);
    for (int col = 0; col < 4; col++) {
      if (digitalRead(col_pins[col]) == LOW) {
        // En knapp är nertryckt!
        button = (row * 4) + col + 1;
        if (button == last_button) {
          // Det är fortfarande samma knapp som förra gången.
          return 0;
        }
        else {
          // En ny knapptryckning - vänta lite för att undvika kontaktstuds
          // och kom sedan ihåg vilken knapp det är så att vi inte reagerar
          // på den igen nästa gång.
          delay(200);
          last_button = button;
          return button;
        }
      }
    }
    digitalWrite(row_pins[row], HIGH);
  }
  // Ingen knapp nertryckt. Nollställ last_button.
  last_button = 0;
  return 0;
}


unsigned long press_timer = 0;
int button_presses[code_length];
int press_count = 0;

void loop() {
  if (millis() - press_timer > code_timeout) {
    press_count = 0;
  }

  int button = readKeyboard();

  if (button > 0) {
    button_presses[press_count] = button;
    press_count++;

    if (press_count == 0) {
      // Första knapptryckningen - starta timern.
      press_timer = millis();
    }
    Serial.print("Button pressed: ");
    Serial.println(button);

    // Kolla om vi har en full kod inmatad.
    if (press_count >= code_length) {
      press_count = 0;
      Serial.print("Inmatade kod: ");
      for (int i = 0; i < code_length; i++) {
        Serial.print(button_presses[i]);
        Serial.print(" ");
      }
      Serial.println();
      // Jämför den inmatade koden med korrekt kod.
      bool match = true;
      for (int i = 0; i < code_length; i++) {
        if (button_presses[i] != lock_code[i]) {
          match = false;
        }
      }
      // Om rätt kod är inmatad så öppnar vi låset en stund.
      if (match) {
        Serial.println("Rätt kod");
        Serial.println("Lås öppnas");
        digitalWrite(relay_pin, HIGH);
        delay(lock_open_time);
        digitalWrite(relay_pin, LOW);
        Serial.println("Lås stängs");
      }
      else {
        Serial.println("Fel kod");
      }
    }
  }
}

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *