Arduino Locker I.

inštalácia externých knižníc, LCD displej, maticová klávesnica, zbernica I2C, stavový stroj

Video pre cvičenie

About

Počas tohto cvičenia vytvoríme jednoduchý elektronický zámok. Pomocou tlačidiel bude možné zadať kód, ktorý sa zobrazí na LCD displeji. Ak bude zadaný správny kód, zámok sa „odomkne“, inak zostane zamknutý. Stav zámku (odomknutý/zamknutý) budeme signalizovať pomocou LED diódy.

Počas cvičenia si ukážeme, ako pracovať s LCD displejom, ako nainštalovať rozširujúce knižnice, ako spracovať vstupy od používateľa a ako pomocou stavového stroja riadiť správanie celého zariadenia.

Objectives

  1. Porozumieť základom práce zbernice I2C.

  2. Naučiť sa pracovať s LCD displejom pomocou knižnice I2C_LCD

  3. Naučiť sa pracovať s maticovou klávesnicou pomocou knižnice SimpleKeypad

Hardware Required

  • prototypovacia doska Arduino UNO
  • LCD displej s I2C kontrolérom
  • maticová klávesnica
  • 1x RGB LED dióda

Content

Introduction to I2C

Zbernica I²C (Inter-Integrated Circuit) poskytuje jednoduchý spôsob komunikácie medzi viacerými zariadeniami pomocou iba dvoch vodičov. Umožňuje napríklad pripojiť displej, senzory alebo iné moduly k mikrokontroléru bez potreby veľkého množstva pinov.

Komunikácia prebieha v režime master–slave:

  • master (v našom prípade Arduino) riadi komunikáciu a posiela požiadavky
  • slave (v našom prípade LCD displej) na tieto požiadavky reaguje

Zbernica používa dva signály:

  • SDA (Serial Data) – slúži na prenos dát
  • SCL (Serial Clock) – slúži ako hodinový signál, ktorý určuje tempo komunikácie

Ilustrácia toho, ako zbernica pracuje, sa nachádza na nasledujúcom obrázku:

V tomto kroku I2C zbernicu inicializujeme a zistíme adresu pripojeného LCD displeja.

Úloha

K doske Arduino UNO pripojte LCD displej s I2C kontrolérom.

I2C piny sú vyvedené na analógových pinoch A5 (pin SCL) a A4 (pin SDA), respektíve na digitálnych pinoch D19 (pin SCL) a D18 (pin SDA).

Arduino UNO: Rozloženie pinov

Pripojiť LCD displej k doske Arduino UNO môžete napr. takto:

Pripojenie LCD displeja k doske Arduino UNO

Úloha

Inicializujte I2C zbernicu pomocou knižnice Wire.h.

Aby bolo možné so zbernicou I2C pracovať, je potrebné ju inicializovať. Na to slúži volanie Wire.begin() z knižnice Wire.h. Zbernicu I2C teda inicializujeme čo najskôr po štarte - po volaní funkcie init().

#include <Arduino.h>
#include <Wire.h>

int main(){
    init(); // inicializácia hw mikrokontroléra pre Arduino SDK
    Wire.begin(); // inicializácia zbernice i2c
}

Úloha

Identifikujte adresu LCD displeja na I2C zbernici.

Identifikáciu spravíme pomocou nasledujúceho fragmentu kódu, ktorý funguje ako tzv. I2C Scanner. Arduino UNO používa na adresovanie I2C zariadení 7 bitov. To znamená, že na zbernici môže byť maximálne 128 zariadení s rôznymi adresami. V praxi je to však omnoho menej - zhruba 112 použiteľných adries.

I2C Scanner sa pokúsi komunikovať s každým zariadením na I2C zbernici.

Vytvorte aj súbor config.h, v ktorom sa budú nachádzať makrá, napr. pre rýchlosť prenosu na sériovej linke (makro BAUD_RATE).

#include <Arduino.h>
#include <Wire.h>

#include "config.h"

int main(){
    init();

    // i2c initialization
    Wire.begin();

    // serial initialization
    Serial.begin(BAUD_RATE);
    while (!Serial);


    Serial.println("I2C Scanner");
    Serial.println("Scanning...");

    uint8_t found = 0;
    for (uint8_t addr = 1; addr < 127; addr++) {
        Wire.beginTransmission(addr);
        uint8_t error = Wire.endTransmission();

        if (error == 0) {
            Serial.print("Device found at 0x");
            if (addr < 16) Serial.print("0");
            Serial.println(addr, HEX);
            found++;
        }
    }

    if (found == 0)
        Serial.println("No I2C devices found.");
    else
        Serial.print(found), Serial.println(" device(s) found.");

    delay(1000);
}

Program sa vykoná raz a potom skončí. Jeho výstup v prípade, ak je pripojený len LCD displej, môže vyzerať napríklad takto:

I2C Scanner
Scanning...
Device found at 0x27
1 device(s) found.

Z výstupu vyplýva, že adresa LCD displeja je 0x27 alebo v desiatkovej sústave 39. Do súboru config.h pridáme makro LCD_I2C_ADDRESS s adresou:

#define LCD_I2C_ADDRESS 0x27

Okrem toho vytvoríme ešte dve makrá opisujúce vlastnosti LCD displeja:

  • LCD_ROWS - počet riadkov displeja, a
  • LCD_COLS - počet stĺpcov displeja.

Súbor config.h teda ubsahovať o displeji tieto makrá:

#define LCD_I2C_ADDRESS 0x27
#define LCD_ROWS 2
#define LCD_COLS 16

Liquid Crystal Display

Dôležitý komponent bude LCD displej. Nainštalujeme ho.

LCD displej: Rozloženie pinov

Úloha

Nainštalujte knižnicu I2C_LCD pre podporu LCD displeja.

[env:uno]
lib_deps = robtillaart/I2C_LCD@^0.2.6

Úloha

Vypíšte na displej na prvom riadku a nultom stĺpci text "Hello, world!".

Jednoduchý výpis textu na displej môže vyzerať takto:

int main(){
  // setup/init
  init();
  Wire.begin();

  // init lcd display
  I2C_LCD lcd(LCD_I2C_ADDRESS);
  lcd.begin(LCD_COLS, LCD_ROWS);

  // print
  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print("Hello world!");
}

Matrix Keypad

Maticová klávesnica je jednoduché vstupné zariadenie, ktoré sa často používa v mikrokontrolérových projektoch na zadávanie údajov používateľom. Tlačidlá nie sú zapojené samostatne, ale sú usporiadané do riadkov a stĺpcov, čím sa výrazne znižuje počet potrebných pinov na ich pripojenie.

Maticová klávesnica

Pri stlačení tlačidla sa prepojí konkrétny riadok so stĺpcom, čo umožňuje mikrokontroléru zistiť, ktoré tlačidlo bolo stlačené. Tento spôsob zapojenia sa nazýva maticové skenovanie a patrí medzi bežné techniky používané napríklad v kalkulačkách, telefónoch alebo rôznych ovládacích paneloch.

Maticová klávesnica: princíp skenovania stlačenej klávesy

V tomto kroku si ukážeme, ako takúto klávesnicu pripojiť k doske Arduino UNO a ako čítať stlačené tlačidlá v programe.

Úloha

Pripojte klávesnicu k doske Arduino UNO.

Rozloženie jednotlivých pinov klávesnice sa nachádza na nasledujúcom obrázku:

Maticová klávesnica 4x4: rozloženie pinov

Mapovanie pinov z klávesnice a doskou Arduino UNO je opísané nasledujúcou tabuľkou:

pin na klávesnici pin na Arduino UNO význam tlačidlá
R1 D2 riadok S4, S8, S12, S16
R2 D3 riadok S3, S7, S11, S15
R3 D4 riadok S2, S6, S10, S14
R4 D5 riadok S1, S5, S9, S13
C1 D6 stĺpec S4, S3, S2, S1
C2 D7 stĺpec S8, S7, S6, S5
C3 D8 stĺpec S12, S11, S10, S9
C4 D9 stĺpec S16, S15, S14, S13

Úloha

Do projektu pridajte knižnicu SimpleKeypad.

Knižnicu pridajte a nainštalujte podľa toho, aké IDE používate.

Po nainštalovaní knižnice sa v prostredí PlatformIO aktualizujú závislosti v súbore platformio.ini:

[env:uno]
lib_deps =
    robtillaart/I2C_LCD @ ^0.2.6
    maximebohrer/SimpleKeypad@^2.0.0

Úloha

Inicializujte klávesnicu.

Pre inicializáciu klávesnice je potrebné nastaviť nasledovné:

  • rozloženie klávesnice - ktoré klávesy, resp. znaky, sa budú nachádzať na ktorých tlačítkach
  • zoznam pinov, ku ktorým sú pripojené riadky
  • zoznam pinov, ku ktorým sú pripojené stĺpce

V našom prípade môže inicializácia klávesnice vyzerať napríklad takto:

// rozloženie klávesnice
char kb_layout[KB_ROWS][KB_COLS] = {
    {'1', '2', '3', 'A'},
    {'4', '5', '6', 'B'},
    {'7', '8', '9', 'C'},
    {'*', '0', '#', 'D'}
};

// piny riadkov a stĺpcov
byte kb_row_pins[KB_ROWS] = {9, 8, 7, 6};
byte kb_col_pins[KB_COLS] = {5, 4, 3, 2};

// vytvorenie novej klávesnice
SimpleKeypad keypad((char*)kb_layout, kb_row_pins, kb_col_pins, KB_ROWS, KB_COLS);

Úloha

Otestujte správnosť vášho riešenia.

Pre otestovanie môžete použiť nasledujúci fragment kódu, ktorý do sériovej linky vypíše stlačený kláves:

while(true){
    char key = keypad.getKey();
    if (key != 0) {
        Serial.println(key);
    }
}

Locker as a State Machine

  • stavovy stroj pre locker
  • stavy budeme volat obrazovky, teda screens
  • zoznam stavov:
    • INIT_SCR - na inicializaciu hw
    • IDLE_SCR - bude cakat na stlacenie klavesy
    • INPUT_SCR - pouzivatel zadava kod
    • CHECK_SCR - overenie kodu a rozhodnutie, ci dobre alebo nie
    • OPEN_SCR - otvorenie
    • DENIED_SCR - odmietnutie pristupu
  • vsetky stavy sa budu nachadzat v priecinku screens
  • kazdy stav bude reprezentovany samostatnou funkciou, ktora sa bude nachadzat v samostnom subore v priecinku screens
    • funkcia reprezentujuca stav bude mat parameter typu referencie na kontext
    • navratovou hodnotou bude novy stav

Úloha

V hlavičkovom súbore context.h vytvorte štruktúru context, ktorá bude obsahovať referencie na LCD displej a maticovú klávesnicu.

struct context {
    I2C_LCD *lcd;
    SimpleKeypad *keypad;
};

Úloha

V hlavičkovom súbore screens.h vytvorte enumaračný typ screen, ktorý bude obsahovať všetky stavy.

enum screen {
    INIT_SCR,
    IDLE_SCR,
    INPUT_SCR,
    CHECK_SCR,
    OPEN_SCR,
    DENIED_SCR
}

Úloha

V priečinku src/screens/ vytvorte súbor init.cpp, v ktorom sa bude nachádzať funkcia init_screen().

Každý stav, resp. obrazovka, bude reprezentovaná samostatnou funkciou. O tejto funkcii bude platiť:

  • bude sa nachádzať v samostatnom súbore v priečinku src/screens/
  • bude mať jeden parameter, ktorým bude referencia na kontext,
  • bude vracať nový stav, a
  • deklarácia funkcie sa bude nachádzať v hlavičckovom súbore screens.h.
enum screen init_screen(struct context *ctx){
  init();

  // i2c init
  Wire.begin();

  // serial init
  Serial.begin(BAUD_RATE);
  while(!Serial);
  Serial.println("> Init Screen");

  // init lcd display
  ctx->lcd = new I2C_LCD(LCD_I2C_ADDRESS);
  ctx->lcd->begin(LCD_COLS, LCD_ROWS);

  // rozloženie klávesnice
  char kb_layout[KB_ROWS][KB_COLS] = {
      {'1', '2', '3', 'A'},
      {'4', '5', '6', 'B'},
      {'7', '8', '9', 'C'},
      {'*', '0', '#', 'D'}
  };

  // piny riadkov a stĺpcov
  byte kb_row_pins[KB_ROWS] = {9, 8, 7, 6};
  byte kb_col_pins[KB_COLS] = {5, 4, 3, 2};

  // vytvorenie novej klávesnice
  ctx->keypad = new SimpleKeypad(
    (char*)kb_layout,
    kb_row_pins, kb_col_pins,
    KB_ROWS, KB_COLS
  );

  return IDLE_SCR;
}

Úloha

V súbore src/screens/idle.cpp vytvorte funkciu idle_screen(), ktorá bude reprezentovať správanie zariadenia v stave IDLE_SCR.

Na tejto obrazovke len vypíšte text Press any key a čakajte na stlačenie ľubovoľného klávesu. Po stlačení klávesy prejdite do stavu INPUT_SCR.

enum screen idle_screen(struct context *ctx){
    // on enter
    Serial.println("> Idle Screen");

    // main
    ctx->lcd->clear();
    ctx->lcd->setCursor(0, 1);
    ctx->lcd->print("Press any key...");

    while(true){
        char key = ctx->keypad->getKey();
        if (key != 0) {
            break;
        }
    }

    // on leave
    return INPUT_SCR;
}

Úloha

Vo funkcii main() vyriešte prepínanie obrazoviek.

int main(){
  struct context context;
  enum screen screen = INIT_SCR;

  while(true){
    switch(screen){
      case INIT_SCR:
        screen = init_screen(&context);
        break;

      case IDLE_SCR:
        screen = idle_screen(&context);
        break;
    }
  }
}

Additional Tasks

  1. asdf

Additional Resources

  1. Interface an I2C LCD with Arduino - Kompletný návod, ako pripojiť LCD displej k doske Arduino UNO cez I2C kontrolér.

  2. I2C_LCD - Arduino library for I2C LCD displays e.g. 20x4

  3. Interface 4×3 & 4×4 Membrane Keypad with Arduino -

  4. SimpleKeypad - Arduino library for using matrix keypads.

  5. Co je to I2C sběrnice? - V tomto článku se vám pokusím vysvětlit, co to I2C sběrnice je, a hlavně si ukážeme, proč ji u Arduina využíváme.