IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 20 - Zapisovátko Morseovky s Arduinem

V minulé lekci, Arduino - Využití I2C sběrnice, jsme si vysvětlili princip a možnosti I2C sběrnice.

Zapisovátko morseovky, které jsem na Arduinu vytvořil, využívá tři tlačítka (čárka, tečka a oddělovač) a vypisuje text na dvouřádkový display.

Na prvním řádku je zapisovaný text, na druhém buffer tónů, tedy aktuálně naťukané čárky a tečky. Tlačítkem oddělovače se buffer ukončí a napíše se odpovídající písmeno. Pokud je stisknutý oddělovač 2x po sobě, do textu se zapíše mezera. Kromě čísel a písmen anglické abecedy jsou definovány speciální znaky pro smazání písmena (6x tečka) a slova (6x čárka).

Morseova abeceda je určena pro přenos informací s pomocí několika symbolů. Mohlo by se zdát, že symboly jsou dva - tečka a čárka. Nicméně informaci přenáší ještě různě dlouhá mezera, nejkratší pro oddělení písmen, delší pro slova a nejdelší pro věty. Symbolů tak máme pět a všechny je možné vytvořit pomocí jednoho tlačítka. Já jsem pro zjednodušení použil tři. Jak wikipedie praví, morseovkou můžeme přenášet rychlostí 60 - 250 znaků za minutu, rychlost řeči je přibližně pětinásobná.

Hardware není nikterak složitý, postačí Arduino (testováno na UNO R3), LCD display (já jsem použil celkem běžný, dvouřádkový display s řadičem hd44780 a I2C sběrnicí), 3 tlačítka, k nim 3 odpory a pár drátů.

Sketch, který to celé řídí, je na 170 řádků a to včetně definice 36 znakové abecedy. Kód popíši po jednotlivých logických blocích, celistvý sketch je ke stažení níže.

Nejprve je třeba sepsat pár počátečních řádků:

Importovat nezbytné knihovny.

#include <Wire.h> // pro práci s I2C
#include <LiquidCrystal_I2C.h> // pro práci s displayem

Definovat PINy, se kterými pracují tlačítka, definovat velikost displaye a tento display nastavit.

#define CARKABUTTONPIN 12
#define TECKABUTTONPIN 8
#define ODDELBUTTONPIN 10
#define DISPLAY_NUMOFCOLUMNS 16 //Pracuji s displayem 16x2
LiquidCrystal_I2C lcd(0x27, DISPLAY_NUMOFCOLUMNS, 2);

Vytvořit proměnné pro ukládání aktuálního a předchozího stavu tlačítek, tak aby se zajistilo, že se tón při stisku tlačítka zapíše jen jednou.

int carkaButtonState = 0;
int carkaButtonLastState = 0;
int teckaButtonState = 0;
int teckaButtonLastState = 0;
int oddelButtonState = 0;
int oddelButtonLastState = 0;

Vytvořit proměnné pro ukládání aktuální sekvence tónů a aktuálního textu. Tónem mám na mysli čárku, tečku, nebo oddělovač. Symbol je v tomto programu číslo nebo písmeno. Ze symbolů se skládá zobrazovaný text.

String tonesBuffer;
String text;

A poslední inicializovaná proměnná je samotná abeceda symbolů, kterou jsem uložil do dvourozměrného pole.

String symbolsAlphabet[][2] =
{
    { ".-","A" },
    { "-...","B" },
    { "-.-.","C" },
    { "-..","D" },
    { ".","E" },
    { "..-.","F" },
    { "--.","G" },
    { "....","H" },
    { "..","I" },
    { ".---","J" },
    { "-.-","K" },
    { ".-..","L" },
    { "--","M" },
    { "-.","N" },
    { "---","O" },
    { ".--.","P" },
    { "--.-","Q" },
    { ".-.","R" },
    { "...","S" },
    { "-","T" },
    { "..-","U" },
    { "...-","V" },
    { ".--","W" },
    { "-..-","X" },
    { "-.--","Y" },
    { "--..","Z" },
    { ".----","1" },
    { "..---","2" },
    { "...--","3" },
    { "....-","4" },
    { ".....","5" },
    { "-....","6" },
    { "--...","7" },
    { "---..","8" },
    { "----.","9" },
    { "-----","0"}
};

Ve funkci setup(), která se spouští při startu, se inicializuje display, zapne se podsvícení, zobrazí se nápis a jednoduchá nápověda. Také zde musíme nastavit pinMode na INPUT pro digitální komunikaci s tlačítky.

void setup() {
    lcd.init();
    lcd.backlight();
    lcd.print("Morseovkovnitko");
    lcd.setCursor(0, 1);
    lcd.print("6x.Smaze1 6x-Vse");

    pinMode(CARKABUTTONPIN, INPUT);
    pinMode(TECKABUTTONPIN, INPUT);
    pinMode(ODDELBUTTONPIN, INPUT);
}

Nekonečná smyčka, tedy funkce loop(), začíná čtením stavů tlačítek a voláním funkce getToneFromBut­tonStates().

void loop() {
    //načtení stavů tlačítek
    carkaButtonState = digitalRead(CARKABUTTONPIN);
    teckaButtonState = digitalRead(TECKABUTTONPIN);
    oddelButtonState = digitalRead(ODDELBUTTONPIN);
    char tone = getToneFromButtonStates(); //zjistí jestli a jaké tlačítko je stisklé

Funkce getToneFromBut­tonStates() z různého stavu tlačítek vyčte které (a jestli nějaké) bylo stisknuto a vrátí daný tón. Za stisknutí se ve skutečnosti považuje uvolnění tlačítka, tedy chvíle, kdy tlačítko v předchozím okamžiku bylo stisknuté a nyní není. Pokud takový okamžik nenastane, vrátí se prázdný char.

char getToneFromButtonStates()
{
    //vrátí v případě uvolnění tlačítka
    //tedy když nynější stav je 0, předchozí 1

    if (!carkaButtonState&& carkaButtonLastState)
        return '-';
    if (!teckaButtonState && teckaButtonLastState)
        return '.';
    if (!oddelButtonState && oddelButtonLastState)
        return ' ';

    return (char)0;
}

Ve funkci loop() následuje kontrola tónu, pokud je prázdný (uživatel nestiskl tlačítko), nic se neděje, jen se na konci přepíše předchozí stav stisknutí tlačítek tím aktuálním. Pokud tón není prázdný, přichází další kontrola, program se větví na část „stisknutý oddělovač“ a „stisknutý jiný tón (tečka nebo čárka)“. Obsahy těchto bloků jsou popsány v dalších částech.

if (tone != (char)0) {
    if (tone == ' ')//ukončuji sekvenci tónů, hledám symbol
    {
    //Zdejší kód je níže
    }
    else//čárka nebo tečka
    {
    //Zdejší kód je ještě nížeji
    }

    //psaní na display se provádí pouze v případě, že bylo zmačknuté nějaké tlačítko
    lcd.clear();//vyčistí se display
    lcd.print(text);//napíše se text
    lcd.setCursor(0, 1);
    lcd.print(tonesBuffer);//napíše se sled tónů
}

//aktualizuje se předchozí stav
carkaButtonLastState = carkaButtonState;
teckaButtonLastState = teckaButtonState;
oddelButtonLastState = oddelButtonState;

Pokud je stisknutým symbolem mezera, znamená to, že ukončuji sekvenci naťukaných tónů a chci z nich vytvořit symbol, na konci bloku se vymaže tonesBuffer.

if (tone == ' ') { //ukončuji sekvenci tónů, hledám symbol
    char symbol = getSymbolFromBuffer();

    if (symbol != (char)0) { //Sled tónů nalezl nějaký symbol, zapíše se do textu
        text += symbol;
        if (text.length() > DISPLAY_NUMOFCOLUMNS) { //Pokud přesáhne počet znaků velikost displaye,
            // napíše se nový znak na první místo. Ostatní se vymažou.
            text = (String)symbol;
        }
    } else { //Sled tónů nedává žádný symbol, ale možná nějakou akci (například vymazání      znaku)
        extractActionFromTonesBuffer();
    }
    tonesBuffer = ""; //vymaže se buffer (čárky a tečky)
}

Funkce getSymbolFrom­Buffer() vrací jedu ze tří možností. Pokud uživatel stiskne jednou tlačítko oddělovače, pouze se ukončí buffer. Pokud je buffer ukončený (prázdný) a je stisknutý oddělovač, funkce getSymbolFrom­Buffer() vrátí mezeru, pokud buffer prázdný není, prohledá se abeceda a vrátí se nalezený symbol. V případě nenalezení symbolu se vrátí prázdný char.

char getSymbolFromBuffer()
{
    if (tonesBuffer == "")
        return ' '; //udělá mezeru, pokud předtím nejsou žádné znaky

    for (int i = 0; i < sizeof symbolsAlphabet / sizeof symbolsAlphabet[0]; i++)
        //Projdu všechny symboly a porovnávám buffer s abecedou
        if (tonesBuffer == symbolsAlphabet[i][0])
            return symbolsAlphabet[i][1][0];//pokud se rovna vrátím daný symbol

    //Buffer neodpovídá žádnému symbolu, pošlu tedy nic
    return (char)0;
}

Vrácení prázdného charu zapříčiní volání funkce extractAction­FromTonesBuffer(). Tato funkce znovu porovnává buffer a ptá se, zdalipak jeho obsah neodpovídá nějaké akci, kterou už je v tomto případě mazání symbolu, či celého textu.

void extractActionFromTonesBuffer()
{
    if (tonesBuffer == "......") //6x tečka
        text.remove(text.length() - 1, 1); //umaže jedno písmeno
    if (tonesBuffer == "------") //6x čárka
        text = "";//smaže celý text
}

Výše popsané bloky kódu se vykonávají, pokud uživatel stiskl oddělovač, pokud uživatel stiskl jiné tlačítko (tečku nebo čárku), tak se tón připíše k bufferu a vykoná se ověření, jestli velikost bufferu nepřesahuje velikost display. V tom případě se buffer pouze přepíše na stisknutý tón.

else { //čárka nebo tečka
    tonesBuffer += tone;
    if (tonesBuffer.length() > DISPLAY_NUMOFCOLUMNS) { //pokud je tónů více než velikost displaye, vymaže se buffer
        tonesBuffer = (String)tone;
    }
}

Toť vše!

Kompletní spustitelný sketch je v přiloženém souboru.

V příští lekci, Arduino - Elektronická hrací kostka, se naučíme na LCD displeji simulovat hrací kostku.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 104x (4.19 kB)
Aplikace je včetně zdrojových kódů

 

Předchozí článek
Arduino - Využití I2C sběrnice
Všechny články v sekci
Arduino - Hardware
Přeskočit článek
(nedoporučujeme)
Arduino - Elektronická hrací kostka
Článek pro vás napsal tesař.tech
Avatar
Uživatelské hodnocení:
9 hlasů
Autor se věnuje dýchání přibližně celý život
Aktivity