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 getToneFromButtonStates().
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 getToneFromButtonStates() 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 getSymbolFromBuffer() 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 getSymbolFromBuffer() 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 extractActionFromTonesBuffer(). 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 110x (4.19 kB)
Aplikace je včetně zdrojových kódů