maanantai 26. helmikuuta 2024

MorseKoulu

 

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