Válí se ti projekty v šuplíku? Dostaň je mezi lidi a získej cool tričko a body na profi IT kurzy v soutěži ITnetwork summer 2017!
Přidej si svou IT školu do profilu a najdi spolužáky zde na síti :)

Zapisovátko Morseovky s Arduinem

Hardware PC Arduino Zapisovátko Morseovky s Arduinem

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

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.


 

Stáhnout

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

 

 

Článek pro vás napsal 5věT.cz
Avatar
Jak se ti líbí článek?
4 hlasů
Miniatura
Předchozí článek
Arduino a I2C sběrnice
Miniatura
Všechny články v sekci
Arduino
Miniatura
Následující článek
Elektronická hrací kostka s Arduinem
Aktivity (3)

 

 

Komentáře

Avatar
Karel Záviský:12. dubna 20:03

Mám tam nějakou chybu: 'class LiquidCrystal_I2C' has no member named 'unit'. Co to znamená?

 
Odpovědět 12. dubna 20:03
Avatar
Karel Záviský:12. dubna 20:04

pardon init.

 
Odpovědět 12. dubna 20:04
Avatar
Odpovídá na Karel Záviský
Michal Žůrek (misaz):12. dubna 20:29

že třída LiquidCrystal_I2C nemá žádnou metodu init, kterou bys mohl zavolat. Nejmenuje se náhodou begin?

Odpovědět 12. dubna 20:29
Nesnáším {}, proto se jim vyhýbám.
Avatar
Odpovídá na Michal Žůrek (misaz)
Karel Záviský:12. dubna 20:33

Máte naprostou pravdu. Hned co jsem dopsal předcházející příspěvek jsem si něco přečetl a doplnil begin. Každopádně děkuji za rychlou reakci :)

 
Odpovědět 12. dubna 20:33
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 4 zpráv z 4.