Monien mielestä sähkötysmerkeillä
tapahtuva viestintä on vanhanaikaista. Se toki nykyisenä digi- ja
internetaikana pitää hyvin paikkansa. Silti, jos kuuntelee lyhyitä
aaltoja, on sieltä edelleen useilta taajuuksilta ja jatkuvasti
kuultavissa myös sähkötysmerkkejä. Se on myös yksi
radioamatöörien kilpailulaji.
Tässä ohjelmassa nuo morsemerkit
eivät olleet pääasia, vaan hyvä keino käsitellä erilaisia
taulukoita, ja ennen kaikkea yritys tehdä mahdollisimman vähän
virheitä sisältävä ohjelma, jonka kanssa käyttäjä voi
kommunikoida: syöttää toimintokäskyjä ja saada vastauksia,
menemättä silti sekaisin tai lukkoon. Ohjelmateknisesti ja
muuttujavalinnoilla tuskin olen päässyt optimitulokseen, sillä
niin paljon tässä oli vääntämistä ohjelman eri osia luodessa ja
testatessa, törmäämistä erilaisiin virheisiin. (Täydellisyyteen
ei pääse!)
Tuo kuva antaa jonkinlaisen yleiskäsityksen ohjelma toiminnasta. Käynnistyksen jälkeen tulee näyttöön kehotus valita merkkiryhmä ja sähkötyksen nopeus. Merkkiryhmiä on neljä: aakkosten alkuosa (A .. M), aakkosten loppuosa (N .. Z), numerot (0 .. 9) sekä erikoismerkit ((! .. )+ ä ja ö). Kunkin ryhmän valinta tapahtuu yllä mainitussa järjestyksessä painamalla painiketta TCH2.
Toinen kehotus on valita sähkötysnopeus potentiometrilla. Miniminopeus on 20 merkkiä minuutissa ja maksiminopeus 93 merkkiä. Tämä on tietysti vain suuntaa antava arvo, sillä lyhin merkki on piste (E) plus saman mittainen tauko, ja pisin merkki on (0 nolla), mikä muodostuu viidestä viivasta ja pisteen mittaisesta tauosta. Viivan pituus on kolme kertaa pisteen kesto. Näillä ääripäillä on siis 15 kertainen ero. Muiden merkkien kesto sijoittuu tälle välille. Nopeuden muuttaminen on mahdollista ainoastaan silloin, kun mitään merkkiryhmää ei ole vielä valittu. Uusi mahdollisuus nopeuden muuttamiseen on, kun on poistuttu viimeisestä, eli erikoismerkkien valinnasta
Kun jokin merkkiryhmä on valittu (TCH2-painike) ja toiminto käynnistetty (TCH1-painike), tulee näytön alemmalle riville tulostus siitä, monesko merkki kuuluu seuraavana summerista. Merkkejä on kussakin jaksossa 30 kappaletta. Kunkin ryhmän merkit arvotaan sattumanvaraisessa järjestyksessä ja näytetään näytöllä kahdella rivillä viiden merkin ryhminä. Nyt voi halutessa tarkastaa, mitä on saanut kirjoitettua paperille. Tällä ohjelmalla on siis mahdollista myös opetella morseaakkosia.
Tästä näytöstä poistutaan painamalla painiketta TCH1. Tämän jälkeen tulostuu uusi näyttö, jossa kysytään, haluaako samasta ryhmästä uuden arvonnan (TCH1), vai haluaako vaihtaa merkkiryhmää (TCH2)?
Tätä voi toistaa ja jatkaa, kunnes pystyy helposti lukemaan sähkötysviestejä, tai mikä todennäköisempää, on ärsyttänyt tuolla summerin vinkumisella läheisensä raivon partaalle.
Toivottavasti ohjelmaratkaisuista olisi jollekin jotain apua ja iloa. Oleellisin osa on tuon valintapainikkeen TCH2 käyttö. Kukin valinta koostuu kolmesta sekvenssiaskeleesta. Ensimmäinen painallus käynnistää valittuun toimintaan liittyvät tehtävät ja siirtyy välittömästi seuraavaan askeleeseen. Siinä ohjelma odottaa, kunnes painike on vapautettu ja siirtyy kolmanteen askeleeseen. Uusi painallus käynnistää seuraavan merkkiryhmän valinnan.
Käynnistyspainikkeella TCH1 on kaikki varsinaiset toiminnot ja valinnat askeleessa yksi. Siinä tutkitaan, mikä merkkiryhmä on valittu (Seq_Ohjaus) ja ohjelman suoritus jatkuu sen mukaan. Muut tämän sekvenssin askeleet tulostavat lähinnä toimintaohjeita käyttäjälle.
/***************************************
* Ohjelman nimi: MorseKoulu
* 26.02.2024
* Lyhyt toimintaselostus:
* Ohjelma arpoo eri merkkiryhmi,
* sähköttää ne äänimerkillä sekä
* tulostaa kaksiRiviselle näytölle.
**************************************/
// MÄÄRITTELYT:
// LCD-näyttö
#include <Arduino.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 13,12,11,10);
// Merkkitaulukko sähkötykseen v = viiva, p = piste
char MerkitA[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // Arvottavat kirjaimet
String MorsetA[] = {"pv","vppp","vpvp","vpp","p", "ppvp","vvp","pppp","pp","pvvv",
"vpv","pvpp","vv"};
String MorsetB[] = {"vp","vvv","pvvp","vvpv","pvp","ppp","v","ppv","pppv","pvv",
"vppv","vpvv","vvpp"};
char Numerot[] = "0123456789"; // Arvottavat numerot
String Num[] = {"vvvvv","pvvvv","ppvvv","pppvv","ppppv",
"ppppp","vpppp","vvppp","vvvpp","vvvvp"};
char ErikoisM [] = "!?/=:,.-()"; // Arvottavat merkit lisäksi vielä ä ja ö
char ae = 225; // ä
char ao = 239; // ö
String Erik[] = {"ppvvp","ppvvpp","vppvp","vpppv","vvvppp","vvppvv",
"pvpvpv","vppppv","vpvvp","vpvvpv","pvpv","vvvp"};
// Liityntöjen ja muuttujien määrittelyt
const int Con_TCH1 = 3; // Kosketuspainike 1
boolean Bol_TCH1 = false;
int Seq_TCH1 = 0; // Ykköspainikkeen sekvenssi
const int Con_TCH2 = 4; // Kosketuspainike 2
boolean Bol_TCH2 = false; // Painikkeen tilatieto
boolean Diu_Kerta = false; // Kertaohjausbitti
const int Con_Pilli = 9; // tämä on BUZZeri
const int Con_Nopeus = 0; // Potentiometrin tulo
int Int_NopRaaka = 0;
int Int_Nopeus = 0; // sähkötyksen nopeussäätö
String M_Sana = "";
int Int_Aika = 0;
int Int_Kesto = 0;
char M_KirjOsa = '?';
int Int_Pit = 0; // Sanan pituus
String Merkit = "";
int Paikka = 0;
int Kohta = 0;
int Tulos[31];
int Arpa = 0; // Tähän arvotaan teulukon numero
int Rivi = 0;
int Erm = 0; // Erikoismerkkien pointteri
// Sekvenssit
int Seq_Ohjaus = 0; // Valintasekvenssi
//*******************************************************************
// ALIOHJELMAT
// Suoritetaan sähkötysmerkkikutsut
void Fun_Morse(String Kirjain){
Int_Pit = Kirjain.length();
for(int i=0;i<Int_Pit;i++){// Käsitellään merkkiä
M_KirjOsa = Kirjain[i];
if(M_KirjOsa == 'v'){Int_Aika = 3*Int_Kesto;Fun_Pilli(Int_Aika);};
if(M_KirjOsa == 'p'){Int_Aika = Int_Kesto;Fun_Pilli(Int_Aika);};
delay(Int_Kesto);
}// Merkin käsittelyn loppu
delay(500);
}// morsetuksen loppu
// Tekstien päivitys LCD-näytölle
void Fun_Teksti(int kohta, int Rivi, char teksti){
lcd.setCursor(kohta, Rivi);
lcd.print(teksti);
}// Teksti_ali loppu
void Fun_Lause(int kohta, int Rivi, String teksti){
lcd.setCursor(kohta, Rivi);
lcd.print(teksti);}
void Fun_Numero(int kohta, int Rivi, int no){
lcd.setCursor(kohta, Rivi);
lcd.print(no);}
// Merkistön tulostus näytölle
void Fun_LataaMerkit(int alk, int lop, char kirj[]){
int alku = alk; int loppu = lop;
String sana = "";
for(int i=alku; i<loppu;i++){
sana = sana + kirj[i];
}//foorin loppu
Merkit = sana;
return Merkit;
} //latauksen loppu
void Fun_Pilli(int aika){
digitalWrite(Con_Pilli, true);
delay(aika);
digitalWrite(Con_Pilli, false);
}// Pillin loppu
void Fun_Tyhjenna(int Rivi){
lcd.setCursor(0, Rivi);
lcd.print(" ");
}// LCD Rivityhjennys loppu
//*******************************************************************
// ASETUKSET:
void setup(){
Serial.begin(9600);
lcd.begin(20,2);
lcd.clear();
randomSeed(analogRead(A1));
pinMode(Con_TCH1, INPUT); // Kosketuspainike
pinMode(Con_TCH2, INPUT); // Kosketuspainike 2
pinMode(Con_Nopeus, INPUT); // Potentiometrin tulo
Fun_Lause(0,0,"Paina TCH2:");
pinMode(Con_Pilli, OUTPUT);
}// Asetuksen loppu
//*******************************************************************
// PÄÄLOOPPI
void loop(){
if(Seq_TCH1 == 0 && Seq_Ohjaus == 0){
Int_NopRaaka = analogRead(Con_Nopeus);
Int_Nopeus = Int_NopRaaka/7;
Int_Kesto = 179 - Int_Nopeus;
Fun_Lause(0,1,"Nopeus m/min:");
Fun_Numero(13,1,(Int_Nopeus/2+20));
}// Nopeuden säädön loppu
Bol_TCH1 = digitalRead(Con_TCH1); // Käynnistuspainike
if(Seq_TCH1 == 0 && Bol_TCH1 == true){
Diu_Kerta = true;
Seq_TCH1 = 1;}
//****************** KÄYNNISTYKSEN SEKVENSSI ******************
// Käynnistuksen sekvenssi
switch (Seq_TCH1) {
case 1:
if(Seq_Ohjaus == 0){
lcd.clear();
Seq_TCH1 = 20;}
else if(Seq_Ohjaus == 3){
lcd.clear();Fun_Lause(0,0,"Aakkosten alkuosa:");
Fun_Lause(0,1,"Merkki no:");
for(int i=0;i<30;i++){ // Arvotaan 30 merkkiä
Arpa = random(0,13);
Tulos[i] = Arpa;}
delay(1500);
for(int i=1;i<31;i++){
Fun_Numero(11,1,i);
Kohta = Tulos[i-1];
M_Sana = MorsetA[Kohta];
Fun_Morse(M_Sana);
if(i%5 == 0){delay(1000);}
}//foorin loppu
lcd.clear();Fun_Tulosta(MerkitA, Tulos);
Seq_TCH1 = 19;
}// A .. M loppu
else if(Seq_Ohjaus == 6){
lcd.clear();Fun_Lause(0,0,"Aakkosten loppuosa:");
Fun_Lause(0,1,"Merkki no:");
for(int i=0;i<30;i++){ // Arvotaan 30 merkkiä
Arpa = random(13,26);
Tulos[i] = Arpa;}
delay(1500);
for(int i=1;i<31;i++){
Fun_Numero(11,1,i);
Kohta = Tulos[i-1];
M_Sana = MorsetB[Kohta-13];
Fun_Morse(M_Sana);
if(i%5 == 0){delay(1000);}
}//foorin loppu
lcd.clear();Fun_Tulosta(MerkitA, Tulos);
Seq_TCH1 = 19;
}// N .. Z loppu
else if(Seq_Ohjaus == 9){
lcd.clear();Fun_Lause(0,0,"Numerot:");
Fun_Lause(0,1,"Merkki no:");
for(int i=0;i<30;i++){ // Arvotaan 30 merkkiä
Arpa = random(0,10);
Tulos[i] = Arpa;}
delay(1500);
for(int i=1;i<31;i++){
Fun_Numero(11,1,i);
Kohta = Tulos[i-1];
M_Sana = Num[Kohta];
Fun_Morse(M_Sana);
if(i%5 == 0){delay(1000);}
}//foorin loppu
lcd.clear();Fun_Tulosta(Numerot, Tulos);
Seq_TCH1 = 19;
}// 0 .. 9 loppu
else if(Seq_Ohjaus == 12){
lcd.clear();Fun_Lause(0,0,"Erikoismerkit:");
Fun_Lause(0,1,"Merkki no:");
for(int i=0;i<30;i++){ // Arvotaan 30 merkkiä
Arpa = random(0,12);
Tulos[i] = Arpa;}
delay(1500);
for(int i=1;i<31;i++){
Fun_Numero(11,1,i);
Kohta = Tulos[i-1];
M_Sana = Erik[Kohta];
Fun_Morse(M_Sana);
if(i%5 == 0){delay(1000);}
}//foorin loppu
Paikka = 1;Rivi = 0; lcd.clear();
for(int i=1;i<31;i++){
Erm = Tulos[i-1];
if(Erm == 10){Fun_Teksti(Paikka, Rivi,ae);}
else if(Erm == 11){Fun_Teksti(Paikka, Rivi,ao);}
else {Fun_Teksti(Paikka, Rivi,ErikoisM[Erm]);}
Paikka++;
if(i%5 == 0){Paikka++;}
if(i == 15){Paikka = 1; Rivi = 1;}
}// foorin loppu
Seq_TCH1 = 19;}
break;
case 19:
if(Bol_TCH1 == true){delay(700);
Seq_TCH1 = 21;}
break;
case 20:
Fun_Lause(0,0,"Odota hetki! ");
delay(800);
Fun_Lause(0,0,"Valitse merkit! ");
Fun_Lause(0,1,"Painike TCH2:");
delay(800);
Seq_TCH1 = 0;
break;
case 21:
Fun_Lause(0,0,"Sama uudestaan TCH1: ");
Fun_Lause(0,1,"Tai valinta TCH2: ");
Seq_TCH1 = 0;
break;
}// Käynnistyssekvenssi loppu
//******************** VALINTASEKVENSSI ************************
Bol_TCH2 = digitalRead(Con_TCH2); // Merkistön valintapainike
if(Seq_Ohjaus == 0 && Bol_TCH2 == true){
lcd.clear();
Seq_Ohjaus = 1;}
// Merkkiryhmän valintasekvenssi
switch (Seq_Ohjaus) {
case 1:
lcd.clear();
Fun_Lause(0,0,"Aakkosten alkuosa:");
Fun_LataaMerkit(0,13,MerkitA);
Fun_Lause(2,1,Merkit);
Seq_Ohjaus = 2;
break;
case 2:
if(Bol_TCH2 == false){ delay(300);Seq_Ohjaus = 3;}
break;
case 3:
if(Bol_TCH2 == true){ lcd.clear(); delay(300); Seq_Ohjaus = 4;}
break;
case 4:
Fun_Lause(0,0,"Aakkosten loppuosa:");
Fun_LataaMerkit(13,26,MerkitA);
Fun_Lause(2,1,Merkit);
Seq_Ohjaus = 5;
break;
case 5:
if(Bol_TCH2 == false){ delay(300);Seq_Ohjaus = 6;}
break;
case 6:
if(Bol_TCH2 == true){ lcd.clear(); delay(300); Seq_Ohjaus = 7;}
break;
case 7:
Fun_Lause(0,0,"Numerot:");
Fun_LataaMerkit(0,10,Numerot);
Fun_Lause(2,1,Merkit);
Seq_Ohjaus = 8;
break;
case 8:
if(Bol_TCH2 == false){ delay(300);Seq_Ohjaus = 9;}
break;
case 9:
if(Bol_TCH2 == true){ lcd.clear(); delay(300); Seq_Ohjaus = 10;}
break;
case 10:
Fun_Lause(0,0,"Erikoismerkit:");
Fun_LataaMerkit(0,10,ErikoisM);
Fun_Lause(2,1,Merkit);
Fun_Teksti(12,1,ae);
Fun_Teksti(13,1,ao);
Seq_Ohjaus = 11;
break;
case 11:
if(Bol_TCH2 == false){ delay(300);Seq_Ohjaus = 12;}
break;
case 12:
if(Bol_TCH2 == true){lcd.clear(); delay(300); Seq_Ohjaus = 13;}
break;
case 13:
lcd.clear();
Fun_Lause(0,0,"Paina TCH2:");
if(Bol_TCH2 == false){ delay(300);Seq_Ohjaus = 12;
Seq_Ohjaus = 0;}
break;
}// Valintasekvensssin loppu
delay(1);
} // Pääohjelma LOPPU