Arduino Locker II.

EPROM, perzistencia dát, práca s RGB LED diódou

Videá pre cvičenie

About

Objectives

  1. Naučiť sa pracovať s RGB LED diódou.

  2. Naučiť sa pracovať s perzistentnou pamäťou EEPROM pomocou knižnice EEPROM.h

Helper Functions

Úloha

Do modulu helpers presuňte funkciu waitForKey().

Túto funkciu použíjeme na viacerých miestach.

char waitForKey(SimpleKeypad *keypad){
    while(true){
        char key = keypad->getKey();
        if(key != 0){
            return key;
        }

        delay(50);
    }
}

Úloha

Vytvorte funkciu printAt(), ktorá vypíše na LCD displej text na zadanú pozíciu.

Funkcia bude mať nasledujúce parametre:

  • lcd - referencia na LCD displej
  • col - číslo stĺpca na displeji
  • row - číslo riadku na displeji
  • text - referencia na reťazec, ktorý má byť vypísaný

Funkcia nič nevracia.

void printAt(I2C_LCD *lcd, int col, int row, char* text){
    lcd->setCursor(col, row);
    lcd->print(text);
}

Status (RGB) LED

Najjednoduchší spôsob, ako poskytovať spätnú väzbu, je pomocou LED diódy. Keďže na zámok sa môžeme pozerať ako na zamknutý a odomknutý, budeme na tieto dva stavy reagovať dvoma farbami:

  • ak bude zámok zamknutý, stavová LED dióda bude svietiť na červeno
  • ak bude zámok odomknutý, stavová LED dióda bude svietiť na zeleno

Úloha

Červenú LED diódu pripojte na digitálny pin 15 a zelenú LED diódu pripojte na digitálny pin 14 a inicializujte ich v stave INIT_SCR.

Po pripojení nezabudnite aktualizovať aj konfiguračný súbor config.h.

// config.h
#define RGB_RED_PIN 15
#define RGB_GREEN_PIN 14

Oba digitálne piny budú inicializované vo funkcii init_screen() pomocou funkcie pinMode():

// init status led
pinMode(RGB_GREEN_PIN, OUTPUT);
pinMode(RGB_RED_PIN, OUTPUT);

Úloha

Aktualizujte kód tak, aby LED dióda vždy pri zamknutí alebo odomknutí zmenila farbu.

LED diódu rozsvietite alebo zhasnete pomocou funkcie digitalWrite().

Úloha

Overte správanie zariadenia.

Changing the Secret Pin

Zámok, ktorého kód sa nedá zmeniť, nie je veľmi bezpečný. V tomto kroku teda rozšírime jeho funkcionalitu o možnosť zmeniť kód na nový. Pre jednoduchosť vytvoríme samostatný stav s názvom CHANGE_PIN_SCR, do ktorého sa používateľ dostane stlačením tlačidla * po odomknutí zámku.

Úloha

V súbore change_pin.cpp vytvorte funkciu change_pin_screen(), ktorá bude reprezentovať stav CHANGE_PIN_SCR.

Na zmenu pinu vytvoríme funkciu change_pin_screen(), ktorá bude čiastočnou úpravou funkcie input_screen(). Nový načítaný kód sa uloží do premennej ctx->buffer a až po prečítaní 4 znakov sa jej obsah uloží do premennej ctx->secret.

enum screen change_pin_screen(struct context *ctx){
    // on enter
    ctx->lcd->clear();
    printAt(ctx->lcd, 0, 0, "New PIN:");
    printAt(ctx->lcd, 0, 1, "____");

    // read new code
    ctx->lcd->setCursor(0, 1);

    for(int idx = 0; idx < PIN_LENGTH; idx++){
        char key = wait_for_key(ctx->keypad);
        ctx->buffer[idx] = key;
        ctx->lcd->print("*");
    }

    // copy new pin from buffer to secret
    Serial.println(ctx->secret);
    for(int idx = 0; idx < PIN_LENGTH; idx++){
        ctx->secret[i] = ctx->buffer[i];
    }

    // on exit
    ctx->lcd->clear();
    printAt(ctx->lcd, 0, 0, "Pin has changed");
    delay(5 * 1000);

    return IDLE_SCR;
}

Úloha

Zabezpečte, aby bolo možné do tohto stavu prejsť stlačením tlačidla * po odomknutí zámku.

Na tento účel upravíme funkciu open_screen():

ctx->lcd->clear();
printAt(ctx->lcd, 0, 0, "You Shall Pass");
printAt(ctx->lcd, 0, 1, "Press * to Change Pin");

// loop / job
int timeout = 5 * 1000;
while(timeout > 0){
    char key = ctx->keypad->getKey();

    if(key != 0){
        Serial.println(key);
        if(key == '*')
            return CHANGE_PIN_SCR;
    }

    delay(50);
    timeout -= 50;
}

Úloha

Overte správnosť svojej implementácie.

Ak ste postupovali správne, tak po odomknutí zámku bude možné stlačiť tlačidlo *, po stlačení ktorého bude možné zmeniť číselnú kombináciu zámku pre jej odomknutie. Po úspešnej zmene bude možné zámok odomknúť zadaním novej kombinácie.

Persistence with EEPROM

Vytvorený elektronický zámok má aktuálne jeden vážny problém. Tým je, že číselná kombinácia na odomknutie zámku sa vynuluje pri reštartovaní dosky. To je dané tým, že kód udržavame v dočasnej pamäti SRAM, ktorá sa zmaže pri resetovaní alebo odpojení dosky od napájania.

Doska Arduino UNO používa Harvardskú architektúru, ktorá sa vyznačuje najmä tým, že pamäť programu je oddelená od pamäte dát. Organizácia pamäte tejto dosky je zobrazená na nasledujúcom obrázku.

Mapa pamäte AVR

Ak teda chceme zabezpečiť, aby zmena číselnej kombinácia prežila reštart dosky, musíme ju uložiť mimo pamäte SRAM. A presne na temto účel slúži pamäť EEPROM. Na doske Arduino UNO má veľkosť 1kB. Limitom je však počet zápisov na jedno pamäťové miesto - tento limit je zhruba ~100000 zápisov. Na čítanie sa žiadne obmedzenie nevzťahuje.

Na prácu s pamäťou EEPROM je možné použiť knižnicu EEPROM.h, ktorá je súčasťou Arduino SDK. V tomto kroku pomocou tejto knižnice zabezpečíme, aby bola číselná kombinácia na odomknutie zámku uložená v pamäti EEPROM, čím sa tento údaj stane trvácnym.

Úloha

Upravte funkciu change_pin_screen() tak, aby nový číselný kód uložila do pamäte EEPROM od adresy 0.

Do konfiguračného súboru config.h uložíme adresu, na ktorej bude uložený číselný kód zámku. Táto adresa sa bude nachádať v makre PIN_ADDR:

// config.h
#define PIN_ADDR 0

Následne môžeme upraviť implementáciu funkcie, kde po úspešnom načítaní nového číselného kódu tento uložíme do pamäte EEPROM:

// change_pin.cpp
for(int i = 0; i < PIN_LENGTH; i++){
    EEPROM.update(PIN_ADDR + i, ctx->buffer[i]);
}

Úloha

Upravte funkciu check_screen() tak, aby skontrolovala zadanú číselnú kombináciu od používateľa s tou, ktorá je uložená v pamätei EEPROM.

// check.cpp
for(int i = 0; i < PIN_LENGTH; i++){
    if(ctx->buffer[i] != EEPROM.read(PIN_ADDR + i)){
        return DENIED_SCR;
    }
}

Úloha

Overte správnosť vašej implementácie.

Aktuálne je možné spraviť aj refaktoring a teda odstrániť napríklad položku .secret zo štruktúry context, ktorá už nie je potrebná.

Additional Tasks

  1. Pridajte k zariadeniu aj bzučiak a vymyslite, ako bude pípať vzhľadom na to, či bol kód zadaný správne alebo nie. Napr. tiež môže poskytovať akustickú späťnú väzbu pri stlačení klávesy.

  2. Vytvorte samostatný stav LOCKOUT_SCR, ktorý zamkne zariadenie po troch neúspešných pokusoch.

  3. Zabezpečte exponential backoff pri neúspešnom zadaní kódu. To znamená, že čakanie na opätovné zadanie kódu sa bude po každom neúspešnom pokuse postupne predlžovať.

  4. Zabezpečte, aby pri zmene číselnej kombinácie bolo možné zadať len číslice. Akýkoľvek iný znak nech je ignorovaný.

Additional Resources

  1. Arduino Memory Guide - Learn about the built-in memory blocks of Arduino boards in this article.

  2. Light Emitting Diode (LED)