Morse_Trainer_LiquidCrystal.ino
Morsho abecedu, ako metódu prenosu textových informácií cez sériu tónov krátky, dlhý považujem za most medzi hravým skúmaním a serióznym vzdelávaním. Pre väčšinu ľudí je to ako tajný jazyk, ktorý zohral významnú úlohu v histórii a stále nachádza svoj význam aj v dnešnom digitálnom veku.
Takže pre zábavku a nostalgickú spomienku na moju základnú vojenskú službu som zbastlil toto:
Materiál:
Arduino Nano, štvor riadkový LCD displej Liquid Crystal s I2C interfejsom, 25 až 50Ω reproduktorček, alebo staré telofónne slúchadlo, telegrafný kľúč – kúpený, alebo vyrobený zo štipca na prádlo.
Nižšie uvedený kód zabezpečí rozoznanie bodiak a čiarok, zvuk pre morse tón a prepis morse kódu na abecedné znaky. Dorobil som tam aj taký veľmi jednoduchý odpovedač pre ilúziu komunikácie s iným radistom. Program sa automaticky prispôsobí rýchlosti kódu, ktorý budete posielať. Prvých pár znakov môže byť nesprávnych. Softvér sleduje rýchlosť čiarky(tá) odosielateľa, aby sa prispôsobil.
Na začiatku pošleme jeden z Q-kódov a to QRA čo je pre radistu otázka „Aký je Tvoj volací znak?".
Skús teda posielať QRA teda "--.- .-. .-", dovtedy dokiaľ Ti "Morse Tréner" neodpovie aký má volací znak a ako sa volá.
Po tomto už môžeš trénovať. V druhom riadku sa vypisuje aktuálne odoslaný morse znak, vedľa neho znak z abecedy a v zátvorkách tvoj čas pre bodku / čas pre čiarku. Napríklad: .-. R(94ms/143ms). V dvoch spodných riadkoch displeja sa bude zobrazovať odpoveď od Morse Trénera.
Kód pre Arduino:
/*
Celé to robím pre zábavku a nostalgickú spomienku na moju vojenskú službu.
MiroR november 2019 až marec 2020
Doplnil som odpovedač a LCD LIQUID CRYSTAL s I2C zbernicou.
SCL sa pripája do A5 a SDA do A4
Morse časť "tréner" som čerpal od Barnacle Budd Morse Code Decoder v. 0.1 z roku 2011, Budd Churchward - WB7FHC
Ostatné internetové fórum Arduino a já :))
MiroR január 2024
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
String tab = " ";
String morseCode = "";
int myKey = 7; // Kľúč je pripojený na D7 bez odporu (je tam interný PULLUP)
int speaker = 11; // Repro (min. 16ohm), alebo cez odpor na GND
int val = 0; // Klúč hore dole
int myTone = 640; // Frekvencia tónu
boolean ditOrDah = true; // Máme plnú bodku(ti) alebo čiarku(tá)
int dit = 50; // Ak je slučka kratšia ako klúč dole je to bodka(ti) inak čiarky(tá) 100
int averageDah = 100; // Štartovacia hodnota zakaždým keď sa odošle čiarky(tá) 150
boolean characterDone = true; // Všetky znaky boli odoslané
int myBounce = 2; // Rieši odskok klúča potrebuje neskôr
int downTime = 0; // Počítanie cyklov slučky počas toho keď je kľúč dole
long FullWait = 1000; // Táto hodnota bude nastavená podľa rýchlosti odosielateľa - medzera medzi písmenami 10000
long WaitWait = FullWait; // Čakanie pre medzery medzi bodkami a čiarkami
long newWord = 0; // Pre medzery medzi slovami
int nearLineEnd=20; // Počet znakov na okno svojho monitora
int letterCount = 0; // sleduje počet znakov podľa nearLineEnd
int morseCount = 0;
int myNum = 0; // Budeme otáčať TY a TÁ do dátového toku a analyzovať , hodnoty budeme ukladať tu
// Miesto, na ktorom sa zobrazuje písmeno, sa zhoduje s hodnotou, ktorú analyzujeme z kódu
char mySet[] = "##TEMNAIOGKDWRUS##QZYCXBJP#L#FVH09#8###7#:###/#61#######2###3#45"; //posledný je 5, to je 63 znak bitovo ?00111111?
// char vyber[] = "";
//Q kódy
const char* ansv[] = {//použi max. 35 znakov a #
"KLUCUJES VYBORNE ARDUINO#",
"VOLAM SA ARDUINO OPERATOR MIROR#",
"MAM SA SKVELE, NEOTRAVUJ!#",
"AHOJ MIRO#",
"AHOJ PEGO#",
"AHOJ EMKA#",
"GRATULUJEM SI MAZAK VELKY SEF#",
"AHOJ JA SOM ARDUINO A TY?#",
"LEBO JA ODPOVEDAM AK LEN SA MI CHCE#",
"NEROZUMIEM!#",
"AK POTREBUJES POMOC VOLAJ 112!#",
"OPAKUJ!#",
"TIEZ POZDRAVUJEM, PRAJEM CISTY ETER#"
};
int led = 13; // LED connected to digital pin 13
int unit = 30; // rýchlosť dávania znakov odpovede
String Znaks;
const char* morsecode[] = { //používam pre auto odpoveď ?
"-----", // 0
".----", // 1
"..---", // 2
"...--", // 3
"....-", // 4
".....", // 5
"-....", // 6
"--...", // 7
"---..", // 8
"----.", // 9
// "---...", // :
// "-.-.-.", // ;
// "", // < (there's no morse for this simbol)
// "-...-", // =
// "", // > (there's no morse for this simbol)
// "..--..", // ?
// ".--._.", // @
".-", // 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
};
/*** encoder ***/
// int val;
int encoder0PinA = 2; //ak chceš zmenu smeru vymeň encoder0Pos--; encoder0Pos++;
int encoder0PinB = 3;
int encoder0Pos = 1;
int encoder0PinALast = LOW;
int n = LOW;
//#define encoderButton 4
int valA;
int valB;
int valC;
byte clk;
byte itemValue = 1;
bool runState = false;
/*** end encoder ***/
void setup() {
Wire.begin();
Serial.begin(115200);
Serial.println(F("Morse_Trainer2_LiquidCrystal.ino\n"));
lcd.begin();
lcd.backlight();
lcd.clear();
lcd.setCursor(1,1);
lcd.print("Morse test MiroR");
delay(3000);
pinMode(myKey, INPUT_PULLUP);//pre A0 je to z odporom bez _PULLUP
pinMode(speaker, OUTPUT);
//HelpMorse();
lcd.setCursor(1,0);
lcd.print("MORSE READY MiroR");
clsLine(0);clsLine(1);
delay(1000);
lcd.setCursor(1,0);
lcd.print("Daj QRA --.- .-. .-");//QRA - aký je Tvoj volací znak?
morse_state();
}
void loop() {
val = digitalRead(myKey); // Kluč je hore, alebo je to dole?
if (!val) keyIsDown(); // Akákoľvek hodnota tu znamená, že je dole.
if (val) { // By mala byť 1, keď je kluč hore.
keyIsUp();
}
}
void keyIsDown() {
tone(speaker, myTone); //Zapni zvuk
WaitWait = FullWait; // Reset klúča v hornej polohe - odpočítavanie
downTime++; // Počítanie ako dlho je klúč dole
if (myNum == 0) { // myNum sa rovná nule na začiatku znaku
myNum = 1; // Štartovací bit - len raz za písmeno
}
characterDone = false; // neskončili sme so znakom, mohlo by ich byť viac
ditOrDah = false; // nevieme stav - kľúč je stále dole
delay(myBounce); // krátke oneskorenie, pre synchronizáciu s Arduino
}
void keyIsUp() {
noTone(speaker); // vypni zvuk
if (newWord > 0) newWord--; // Počítanie dole na mieste medzery medzi slovami
if (newWord == 1) printSpace(); // Vytlačí jednu medzeru
if (!ditOrDah) { // Nevieme, či to bol bodka(ti) alebo čiarky(tá) doteraz, takže ...
shiftBits(); // Poďme zistiť! A robiť mágiu s bitmi
}
if (!characterDone) {
WaitWait--; // Počítame čas dole
if (WaitWait == 0) { // Bingo, čas keyUp vypršal! A všetko bolo odoslané
WaitWait = FullWait; // Reset keyUp počítadlo
printCharacter(); // Vytlačiť
characterDone = true; // Dostali sme ho, tu končíme
myNum = 0; // Nastavenie pre získanie ďalšieho štart bitu
pocuvaj(); //odskok na automatické odpovede
}
downTime = 0; // Reset keyDown počítadlo
}
}
void printSpace() {
letterCount++; // Počíta počet znakov na riadok
if (letterCount > nearLineEnd) { // ak dosiahne koniec riadku:
Serial.print(F("\n")); // skok na nový riadok
letterCount = 0; // reset počítadla znakov
return; // Návrat do slučky
}
Serial.print(F(" ")); // Tlač medzeru
}
void printCharacter() {
FullWait = averageDah * 100; // Počítadlo keyUp bude vynulované v závislosti na rýchlosti odosielateľa
newWord = FullWait * 5; // Medzera tiež upravuje rýchlosť odosielateľa
letterCount++; // počítadlo znakov na riadok
morseCount=morseCount+morseCode.length()+1;//pripočítavam tam aj morse znaky
if (myNum > 63) { //všetko čo nieje v poli mySet[]
printPunctuation(); // Analyzujeme hodnotu bodov, či je väčšia ako pole znakov mySet[]
Serial.println(F(" Mimo zakladnu sadu!"));
digitalWrite(led, HIGH);
return; // Návrat do slučky
}
Serial.print(mySet[myNum]); // Echo písmen zo znakovej sady
morse_state();
//*** koniec pôvodnej tlače moja nadstasvba
if (morseCount > nearLineEnd) { // ak dosiahne koniec riadku:
clsLine(1);
lcd.setCursor(0,1);
morseCount = 0;
}
//***Serial.print(" ");
clsLine(1);clsLine(2);clsLine(3);//zmaž starú odpoveď
lcd.setCursor(1,1);//pre výpis aktuálne stlačeného kľúča
lcd.print(morseCode);//vypisuje pri vysielaní cez key
lcd.print(" ");
lcd.print(mySet[myNum]);//výpis znakov na LCD pri pípaní cez key
lcd.print("("+String(dit)+"ms/"+String(averageDah)+"ms)");//pridá info o čase stlačenia bodka/čiarka
morseCode = "";
Znaks += String(mySet[myNum]); //Ukladám doteraz zachytené pre auto odpoveď(vyhodnotí sa v pocuvaj()
}
void printPunctuation() { //*** sem dám procedúry pre nižšie uvedené znaky ***
String pMark = "#"; // V prípade, že sa nič nepodobá
if (myNum == 71) pMark = ":";
if (myNum == 76) pMark = ",";
if (myNum == 84) pMark = "!";
if (myNum == 94) pMark = "-";
if (myNum == 101) pMark = "@";
if (myNum == 106) pMark = ".";
if (myNum == 115) pMark = "?";
if (myNum > 115) pMark = "ERR";
Serial.print(pMark);
Znaks = pMark;
}
void shiftBits() {
ditOrDah = true; // budeme vedieť, ktorý z dvoch stavov
if (downTime < dit / 3) return; // ignoruje odskok kluča
if (downTime < dit) {
// Máme bodka(ti)
myNum = myNum << 1; // posuň bit vľavo
myNum++; // pridať, pretože sa jedná o bodka(ti)
morseCode += ".";
}
else {
// Máme čiarky(tá)
myNum = myNum << 1; // posuň bit vľavo
// Ďalšie tri riadky nastavujú automatickú rýchlosť:
averageDah = (downTime + averageDah) / 2; // priemer z čiarky(tá)
dit = averageDah / 3; // normálny bodka(ti)
dit = dit * 2; // zdvojnásobiť hranicu medzi bodka(ti) a čiarky(tá)
morseCode += "-";
}
}
void say_morse_word(const char * msg) { //pre automatické vysielanie
int index = 0;
while (msg[index] != '\0') {
// say a dash
if (msg[index] == '-') {
dash();
}
// say a dot
if (msg[index] == '.') {
dot();
}
// gap beetween simbols
intragap();
index++;
}
}
void beep(int time) { // do pípania
int i;
/*
int t1[5] = {420,440,430,450,410}; //pole možných tónov
int t10 = random(1, 5); //náhodná hodnota pre simuláciu vzdialenej vysielačky
int t = t1[t10]; //simulácia kolísania tónu
*/
int t = 600; //100 tón zvuku automatu, vyššia hodnota je nižší tón
int beepduration = (int)((float)time / t * 800); //1800
digitalWrite(led, HIGH);
for (i = 0; i < beepduration; i++) {
digitalWrite(speaker, HIGH);
delayMicroseconds(t);
digitalWrite(speaker, LOW);
delayMicroseconds(t);
}
delay(time);
}
void silence(int time) { //vypni zvuk
digitalWrite(led, LOW);
delay(time);
}
void dot() {// procedúra pre .(bodku)
beep(unit);
//lcd.print(".");//vypisuje pri automatickej odpovedi
}
void dash() { // procedúra pre -(čiarku)
beep(unit * 3);
//lcd.print("-");//vypisuje pri automatickej odpovedi
}
void intragap() { // medzera medzi bodkami a čiarkami
silence(unit);
}
void shortgap() { // medzera medzi písmenami
silence(3 * unit);
//lcd.print(" ");//vypisuje pri automatickej odpovedi
}
void mediumgap() { // medzera medzi slovami
silence(7 * unit);
}
void say_char(char letter) { // pípaj znaky pri automatickej odpovedi
if ((letter >= '0') && (letter <= 'Z') && (letter != '<') && (letter != '>')) {
// lcd.print(morsecode[letter-'0']); // tlač morse znakov pri odpovedi
// lcd.print(' '); // medzera medzi písmenami
say_morse_word(morsecode[letter - '7']);
shortgap();
} else {
if (letter == ' ') {
mediumgap();
}
}
}
/*** Automatické odpovede ***/
void pocuvaj() {
if (Znaks.indexOf("HELP") >= 0) {
Help();
}
/*** Tu sú možné otázky na ktoré sa spúšťa automatická odpoveď ***/
if (Znaks.indexOf("QSD") >= 0) {//--.- / ... / -..
posli(ansv[0]); //Klúčujem správne?
}
if (Znaks.indexOf("QRA") >= 0) {//--.- / .--. / .-
posli(ansv[1]); //Aká je Tvoja volačka
clsLine(0);
lcd.setCursor(1,0);
lcd.print("MORSE READY MiroR");
}
if (Znaks.indexOf("MAS") >= 0) {//-- / .- / ...
posli(ansv[2]); //Srandička
}
if (Znaks.indexOf("MIRO") >= 0) {
posli(ansv[3]);
}
if (Znaks.indexOf("PEGO") >= 0) {
posli(ansv[4]);
}
if (Znaks.indexOf("EMA") >= 0) {
posli(ansv[5]);
}
if (Znaks.indexOf("ZOBAK") >= 0) {
posli(ansv[6]); //srandička z vojenčiny
}
if (Znaks.indexOf("AHOJ") >= 0) {
posli(ansv[7]); //pozdrav
}
if (Znaks.indexOf("CAU") >= 0) {
posli(ansv[8]);
}
if (Znaks.indexOf("73") >= 0) {//--... / ...--
posli(ansv[12]);
}
if (Znaks.indexOf("PRECO") >= 0) {
posli(ansv[10]);
delay(100);
posli(ansv[2]);
}
if (Znaks.indexOf("SOS") >= 0) {
posli(ansv[10]); //SOS
}
if (Znaks.indexOf("?") >= 0) {
Znaks = "";
//*** cls(8);
delay(100);
Help();
}
if (Znaks.indexOf(":") >= 0) {
Znaks = "";
//*** cls(0);
delay(100);
//*** Menu1();
}
if (Znaks.indexOf("ERR") >= 0) {
posli(ansv[11]);
delay(100);
clsLine(2);
morseCode="";
}
return;
}
void posli(const char vyber[]) { //prenesie sa pole automatickej odpovede
//for(i=8;i<=15;i++){cls(i);}//výmaz
lcd.setCursor(0,2);//začiatok výpisu automatickej odpovede
int pp = 0; int flag = 0;
while ( flag < 1) {
if (pp==20){
clsLine(3);
lcd.setCursor(0,3);//pokračovanie výpisu automatickej odpovede
}
if (pp==40){
clsLine(2);
lcd.setCursor(0,2);
}
lcd.print(vyber[pp]);
say_char(vyber[pp]);
if (vyber[pp] == '#')flag = 1; //# ukončuje posielaný reťazec
//*** Serial.println("PP="+String(pp));
pp++;
}
Znaks = "";
lcd.print(" QRV");
lcd.flush();
//***lcd.setCursor(0,1);//kurzor na príjem
clsLine(1);//zmazať riadok a kurzor sem
return;
}
/*** END Automatická odpoveď ***/
void Help() {
// lcd.print();
lcd.print("Morse pouziva len velke pismena");
delay(100);
lcd.print("Skus Q kody. Napr. QRA - aka je vasa volacka, QSD - klucujem spravne?");
delay(100);
lcd.print("Na koniec textu daj K -.- (to je standard z voj. stanic)");
delay(100);
lcd.print("Skus napisat AHOJ K");
delay(100);
lcd.print("Napis H.... E. L.-.. P.--. K-.-");
delay(100);
lcd.print("Chces Morse abecedu? Napis ? ..--..");
return;
}
void HelpMorse() {
Serial.print(F("\n"));
//vypis morse znakov pre ozivenie pamäte (nie stroja, tvojej :))
/*
int abc = 65;
for (int n = 10; n < 36; n++) {
Serial.print(char(abc)); Serial.print(F(" "));
Serial.print(morsecode[n]);
Serial.print(F(" "));
abc++;
delay(100);
}
*/
//pre serial port monitora
int abc = 65;Serial.print("*\n");
for (int n = 10; n < 23; n++) {
Serial.print(char(abc));Serial.print(" ");
Serial.print(morsecode[n]);
Serial.print(char(9));
Serial.print(char(abc+13));
Serial.print(" ");
Serial.println(morsecode[n+13]);
abc++;
delay(100);
}
Serial.println();
//vypis čísiel v morse znakoch pre ozivenie
int cis=48;
for (int n1 = 0; n1 < 10; n1++) {
Serial.print(char(cis));Serial.print(" ");Serial.print(morsecode[n1]);Serial.print(" ");
cis++;
delay(100);
}
Serial.println();
}
void clsLine(int line)
{
lcd.setCursor(0,line);
for(int n = 0; n < 20; n++) // 20 indicates symbols in line. For 2x16 LCD write - 16
{
lcd.print(" ");
}
lcd.setCursor(0,line);//aby sa nemuselo opakovať nast. kurzora
}
void morse_state(){
Serial.print(" Cas bodka = "+String(dit));
Serial.println(" ciarka = "+String(averageDah));
}