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

 

 

 

 

 

 

 

 

tiistai 19. joulukuuta 2023

ASCII_65A

 Tällä ohjelmalla tulostetaan ASCII-merkkejä LCD-näytölle.

 

Taistelin epävarmana kehittäessäni yhtä sovellusta siitä, miten saisin ääkkösten isot kirjaimet (Ä ja Ö) tulostettua tuon pienelle kaksiriviselle LCD_näytölle. En netistä enkä muistakaan lähteistä löytänyt apua ongelmaani. Tuon puutteen ajamana syntyi tämä pieni ohjelma, jonka ajattelin ladata tänne sivullenikin. Toivottavasti joku hyötyisikin siitä.

 Ohjelmassa syötetään binäärimuodossa (byte) muuttuja väliltä B00000000 .. B11111111 ja tulostetaan LCD-näytön alariville. (Itse asiassa suurin luku menee hiukan tuon ylikin, sillä muuttujan kasvu ei pysähdy suurimpaan ASCII-numeroon, eli lukuun 255. Se on sentään mitätön haitta.)

 Painamalla painiketta muuttuja kasvaa aina kahdellakymmenellä ja jatkuu täten aina lukuun 260 saakka, minkä jälkeen se aloittaa uudelleen nollasta. Välillä riville ei tulostu mitään. Hyvin suuri osa merkeistä on kiinalaisia. Sieltä löytyvät kyllä kaikki standardin mukaiset ASCII-merkit ja myöskin pieni ä ja pieni ö, jotka pitää nyt sitten hyväksyä isojen kirjaimien tilalle.

 Sovelluksessa on hyödynnetty Rich UNO R3 kehityspohjaa, joten esimerkiksi painikevärähtelyä ei tarvitse itse ohjelmassa huomioida ja valvoa.

 

/***************************************
 *  Ohjelman nimi: LCD_AskiiB
 *  19.12.2023
 *  ASCII-bittien muunnos
 *  LCD-näytölle
 **************************************/
#include <Arduino.h>
// MÄÄRITTELYT:
// LCD-näyttö
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 13,12,11,10);
byte alku = B00000000;

// Liityntöjen määrittelyt
  const int Con_TCH1 = 3;       // Kosketuspainike 1
  boolean Bol_TCH1 = false;
  int Seq_TCH1 = 0;
  boolean Kep_TCH1 = false;     // Ohjausbitti
  int kerta = 0;
  int No = 1;
 
// ALIOHJELMAT
// Merkkien päivitys LCD-näytölle
   void Fun_Jakso(int kohta, int rivi, int lukema){
    lcd.setCursor(kohta, rivi);
    lcd.print(lukema);
   }// Numerotulostuksen loppu
  void Fun_Merkki(int kohta, int rivi, char merkki){
    lcd.setCursor(kohta, rivi);
    lcd.print(merkki);
  }// Teksti_ali loppu
  void Fun_Lause(int kohta, int rivi, String teksti){
   lcd.setCursor(kohta, rivi);
   lcd.print(teksti);}

// ASETUKSET:
void setup(){                  
 Serial.begin(9600);    
  lcd.begin(20,2);
  lcd.clear();     
  pinMode(Con_TCH1, INPUT);      // Kosketuspainike
}// Asetuksen loppu

// PÄÄLOOPPI                   
void loop(){
// Painikkeen luku
   Bol_TCH1 = digitalRead(Con_TCH1);
   if(Bol_TCH1 == true && Kep_TCH1 == false && Seq_TCH1 == 0){
    Kep_TCH1 = true;
    Seq_TCH1 = 1;
    }// Painike 1
    
// Painikesekvenssi
switch (Seq_TCH1) {
    case 1:
    if(Kep_TCH1 == true && Kep_TCH1 == true){
      Fun_Lause(0,0,"ASCII jakso:");
      Fun_Jakso(12,0,kerta);
      Fun_Merkki(15,0,'-');
      Fun_Jakso(17,0,kerta+19);      
      for(int i=0;i<20;i++){
       Fun_Merkki(i,1,alku+i+kerta);}
      Kep_TCH1 = false;}
    if(Bol_TCH1 == false){
      Seq_TCH1 = 2;}
    break;
    case 2:
     No++;
     Seq_TCH1 = 3;
    break;
    case 3:
      kerta = kerta + 20;
      if(kerta > 260){
       kerta = 0;  lcd.clear();};
     if(Bol_TCH1 == false){Seq_TCH1 = 1;}
       Seq_TCH1 = 0;
    break;
}// Painikesekvenssin loppu
delay(1);
} // Pääohjelma LOPPU

tiistai 26. syyskuuta 2023

CW_64

Morsetuksen alkeita.

 Tämä ohjelma on oikeastaan vain runko sähkötyksen opetteluun. Tämä sisältää ainoastaan aakkosmerkit, jotka muodostuvat yhdestä, kahdesta tai kolmesta, joko pisteestä tai viivasta. Pisteistä lyhin on E-kirjain, eli yksi lyhyt piste. Viivoista lyhyin on T-kirjain. Pisin merkki on O-kirjain, joka muodostuu kolmesta viivasta. Muut ovat niiden väliltä ja muodostuvat pisteistä ja viivoista.

 

Tavoite luonnollisesti oli tehdä tämä ohjelma, sekä luoda siihen erilaisia toimintoja. Kun ohjelma käynnistyy, tulostuu näytölle pyyntö painaa kosketuspainiketta TCH1 ja valita sähkötyksen nopeus. Nopeus voidaan valita potentiometrilla, ja ainoastaan silloin, kun sormi viipyy painikkeella. Kun sormi irrotetaan, tulostuu näytölle ilmoitus, että sähkötys alkaa kahden sekunnin kuluttua.


 Kun sähkötysääni kuuluu, on näyttö tyhjä. Tänä aikana kuuluu ainoastaan summerista sähkötysmerkit. Viiden merkin välein on hiukan pidempi tauko. Merkkejä arvotaan 30 kappaletta (rajoite johtuu tuosta pienestä kaksirivisestä näytöstä). Kun sähkötys on loppu, tulostetaan näytölle kahteen riviin nuo arvotut sähkötysmerkit. Jos merkit on kirjoitettu paperille kuuntelun aikana, voidaan niiden oikeellisuus ja oppimisen onnistuminen tarkastaa näytöltä.

 Kun painiketta TCH1 painetaan uudestaan, voidaan nopeus valita jälleen (ei välttämätöntä), ja suorittaa uusi harjoitus. Tuo nopeusasetus (15 .. 93 merkkiä minuutissa) on lähinnä symbolinen, sillä kaikkia sähkötysmerkkejä tämä harjoitus ei sisällä. Tavoite on ainoastaan, että voidaan muodostaa sekä hidasta että nopeaa sähkötystä.

 /***************************************

 *  Ohjelman nimi: Opi_CWF_64
 *  26.09.2023
 *  Lyhyt toimintaselostus:
 *  Tulostaa pillille 1, 2, ja 3
 *  merkkisiä sähkötyskirjaimia.
 *  Sähkötysnopeutta voidaan säätää käsin.
 **************************************/

// MÄÄRITTELYT:
#include <LiquidCrystal.h>
  LiquidCrystal lcd(7, 6, 13,12,11,10);
  const byte api = B11100001; // ä
  const byte opi = B11101111; // ö

// Taulukoita ja muuttujia
    const int Con_Tapit = 30;         // Merkkitaulukon pituus
    char Merkit[] = "EISTANMUDORKWG"; // Arvottavat merkkialkiot
    String Tulos = "";      
    int Int_TaulPit = 13;             // Merkkien lukumäärä
    int Int_Sattuma = 0;
    const int ViKe = 5;               // Viivekerroin    
    boolean Kep_Tulosta = false;      // Tulostus LCD-näytölle    
    int Int_Rivi = 0;                 // LCD-näytön rivi
    int Int_Kohta = 0;                // LCD-näytön kohta     

// Liityntöjen määrittelyt
// Digitaalinen tulo
    const int Con_TCH1 = 3;       // Kosketuspainike 1
// Digitaalinen lähtö
    const int Con_Pilli = 9;      // tämä on BUZZeri
// Analoginen tulo    
    const int Con_Nopeus = 0;     // Potentiometrin tulo
    int Int_NopRaaka = 0;
    int Int_Nopeus = 0;          // sähkötyksen nopeussäätö
    const int Con_Arpa = 1;

// Sähkötykseen ja ohjaukseen liittyvät
    int Int_Jakso = 0;            // Tähän lasketaan nopeus
    boolean Bol_TCH1 = false;
    boolean Diu_TCH1 = false;     // Painiketieto yhden kierroksen ajan
    boolean Kep_teksti_1 = true;  // Alkutekstin kirjoitus

// Sekvenssit
    int Seq_TCH1 = 1;             // Kosketuspainikkeen käsittely
    int Seq_CW_Merkit = 0;        // Merkkien valinta taulukosta

//********************************************************************
// ALIOHJELMAT
// Näytön aliohjelmat
void Fun_Tyhjenna(int rivi){
  lcd.setCursor(0, rivi);
  lcd.print("                    ");
}// LCD rivityhjennys loppu

// Tekstien päivitys
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);
}// Lause_ali loppu
//Numeroiden päivitys
void Fun_Nopeus(int kohta, int rivi, int nopeus){
  lcd.setCursor(kohta, rivi);
  lcd.print(nopeus);
}// Mittaus_ali loppu

// Äänimerkin aktivointi
    void Fun_Pilli(int kertaa, int kesto){
      int k = kertaa;
      int pituus = Int_Jakso * kesto;
      for(int i = 1; i <= k; i++){
      digitalWrite(Con_Pilli, true);
      delay(pituus);
      digitalWrite(Con_Pilli, false);
      delay(Int_Jakso);
     }// Pillin loppu
    }// foorin loppu


//********************************************************************
// ASETUKSET:
void setup(){                  
 Serial.begin(9600);
  lcd.begin(20,2);
  lcd.clear();
Int_TaulPit = sizeof(Merkit);
  randomSeed(analogRead(Con_Arpa));
  pinMode(Con_TCH1, INPUT);  // Kosketuspainike
  pinMode(Con_Nopeus, INPUT); // Potentiometrin tulo
  pinMode(Con_Pilli, OUTPUT);  
}// Asetuksen loppu


//********************************************************************
// PÄÄLOOPPI                   
void loop(){

// Tyhjennetään näyttö ja ladataan alkutekstit
if (Kep_teksti_1 == true){
    Fun_Lause(1, 0, "Paina TCH1 ja");
    Fun_Lause(1, 1, "valitse nopeus");
    Kep_teksti_1 = false;}

// Kosketuspainikkeen hallinta
Bol_TCH1 = digitalRead(Con_TCH1);
switch (Seq_TCH1) { // Kosketuspainikkeen sekvenssi
  case 1:
    if(Bol_TCH1 == true){
      Fun_Tyhjenna(0);Fun_Tyhjenna(1);
      Seq_TCH1 = 2;
      }
    break;
  case 2:
    Fun_Lause(1, 0, "Valitse nopeus:");
      Seq_TCH1 = 3;
  break;
  case 3: // Määritellään sähkötysnopeus
 Bol_TCH1 = digitalRead(Con_TCH1);
       // Määritellään sähkötyksen nopeus ja merkin kesto
          Int_NopRaaka = analogRead(Con_Nopeus);
             Int_Nopeus = Int_NopRaaka / 13 + 15; //13 + 20;
          Int_Jakso = 150 -  Int_Nopeus; // Sähkötyksen nopeus potikalta
            Fun_Nopeus(3,1,Int_Nopeus);
            Fun_Lause(6,1,"m/min");
     if(Bol_TCH1 == false){
        Fun_Tyhjenna(0);Fun_Tyhjenna(1);
        Seq_TCH1 = 4;}
   break;

  case 4: // Ilmoitus alkamisesta
    Fun_Lause(1,0,"S");
    Fun_Teksti(2,0,B11100001);
    Fun_Lause(3,0, "hk");
    Fun_Teksti(5,0,B11101111);    
    Fun_Lause(6,0, "tys alkaa");
    Fun_Lause(3,1, "2s kuluttua.");
      delay(2000);
      Fun_Tyhjenna(0);Fun_Tyhjenna(1);
    Seq_TCH1 = 5;
   break;

  case 5: // Aktivoidaan sähktyssekvenssi
      Diu_TCH1 = true;
      Seq_TCH1 = 6;
   break;   
  case 6: // Tulostetaan näytölle arvotut merkit
      Int_Rivi = 0;Int_Kohta = 0;
      for(int k = 0; k < 30; k++){
      Fun_Teksti(Int_Kohta, Int_Rivi, Tulos[k]);
        Int_Kohta++;
          if(Int_Kohta == 5 || Int_Kohta == 11){
           Int_Kohta++;}
          if(Int_Kohta == 17){Int_Rivi = 1; Int_Kohta = 0;}
      } // forin loppy
        Seq_TCH1 = 7;
  break;  
  case 7: // Paluu uuteen arvontaan
      Bol_TCH1 = digitalRead(Con_TCH1);
         if(Bol_TCH1 == true){Seq_TCH1 = 1;}
            Tulos = "";
   break;  
}// Sekvenssin loppu

if(Diu_TCH1 == true){// eka on lkm. ja toka kesto
   for(int i = 0; i < Con_Tapit;i++){
     Int_Sattuma = random(0,14);
     Seq_CW_Merkit  = Int_Sattuma;
     Tulos = Tulos + Merkit[Int_Sattuma];
   switch (Seq_CW_Merkit){
    case 0:Fun_Pilli(1, 1);delay(ViKe*Int_Jakso);break;  // E
    case 1:Fun_Pilli(2, 1);delay(ViKe*Int_Jakso);break;  // I                      
    case 2:Fun_Pilli(3, 1);delay(ViKe*Int_Jakso);break;  // S
    case 3:Fun_Pilli(1, 2);delay(ViKe*Int_Jakso);break;  // T
    case 4:Fun_Pilli(1, 1);Fun_Pilli(1, 2);delay(ViKe*Int_Jakso);break;  // A
    case 5:Fun_Pilli(1, 2);Fun_Pilli(1, 1);delay(ViKe*Int_Jakso);break;  // N
    case 6:Fun_Pilli(1, 2);Fun_Pilli(1, 2);delay(ViKe*Int_Jakso);break;  // M
    case 7:Fun_Pilli(2, 1);Fun_Pilli(1, 2);delay(ViKe*Int_Jakso);break;  // U    
    case 8:Fun_Pilli(1, 2);Fun_Pilli(2, 1);delay(ViKe*Int_Jakso);break;  // D
    case 9:Fun_Pilli(3, 2);delay(3*Int_Jakso);break;  // O     
    case 10:Fun_Pilli(1, 1);Fun_Pilli(1, 2);Fun_Pilli(1, 1);delay(ViKe*Int_Jakso);break;  // R
    case 11:Fun_Pilli(1, 2);Fun_Pilli(1, 1);Fun_Pilli(1, 2);delay(ViKe*Int_Jakso);break;  // K
    case 12:Fun_Pilli(1, 1);Fun_Pilli(1, 2);Fun_Pilli(1, 2);delay(ViKe*Int_Jakso);break;  // W
    case 13:Fun_Pilli(1, 2);Fun_Pilli(1, 2);Fun_Pilli(1, 1);delay(ViKe*Int_Jakso);break;  // G
  }// Sekvenssin loppu
  if(i > 0 && (i+1)%5 == 0){delay(10*Int_Jakso);}// Pidempi tauko viiden välein
  }// Merkkivalinnan loppu
}// Arvonnan loppu
Diu_TCH1 = false; // DifUpp nollataan joka kierroksella
Kep_Tulosta = false;
delay(1);
} // Pääohjelma LOPPU

sunnuntai 28. toukokuuta 2023

OH2A_63

Tuossa Rich UNO R3 on odottanut varsin pitkään saadakseen sisäänsä sähköä ja ohjelmalauseita. Se on hyvä alusta kokeiluihin ja ohjelman kehitykseen, koska siinä on valmiina useita liityntöjä, kuten kosketuspainikkeita, analogiapuolella potentiometri, summeri sekä neljän numeron näyttö. Tässä mainitsin vain muutamia.

Näiden toimintaan löytyy paljon kirjasto-ohjelmia, mutta halusin lähteä liikkeelle ja kokeilemaan hyvin alhaiselta perustasolta. Hyvin pian kokeilut sortuivatkin osaamattomuuteen ja tiedon puutteeseen. Pääsin kuitenkin alkuun ja sain jotain yksinkertaista aikaan.

Alla oleva ohjelma piipittää morseaakkosia. Signaali käynnistyy painamalla hipaisupainiketta ja summeri sähköttää tekstin kuuluvasti. Sähkötysnopeus on säädettävissä arviolta välillä 20 .. 100 merkkiä minuutissa. Se ei kuitenkaan ole tarkka, sillä lyhin merkki on yksi (1) ”piste” (E-kirjain) ja pisin merkki viisi (5) ”viivaa” (numero 0). Muut ovat siltä väliltä.

Tarkoitukseni oli tulostaa nopeus tuossa näytössä, mutta siihen tämän hetkinen tietoni ja osaamiseni ei riittänyt. Kaiketi jossain vaiheessa pitänee ladata kirjasto-ohjelmia, jotta pääsen aiheessa eteenpäin.

Tässä ohjelmassa ”sanoma” on CQ CQ DE OH2A ...K. Siinä on aluksi kaksi (2) yleistä kutsua (CQ), sen jälkeen tässä (DE) sekä kutsu OH2A ja lopuksi kuuntelen (K). Tuo kutsu on Suomen radioamatööriliiton kutsu, joten ohjelma kutsuu uusia jäseniä liittymään radioamatööriharrasteen pariin. Tervetuloa minunkin puolestani!

Liukukytkimillä voi aktivoida eri toimintoja sekä tulopuolelle että ohjauspuolelle. Tässä ohjelmassa on valittu hipaisukytkimellä (TCH1, D3) sähkötyksen käynnistys. Summerin ohjaus (BUZZER, D9) sekä potentiometrin analogiatulo (KNOB, A0).

Liukukytkimen luenta on ohjelmallisesti yksinkertainen, koska tässä ei tarvitse ottaa huomioon mitään kosketinvärähtelyitä tai muita häiriötekijöitä. Kun ohjelma huomaa, että kosketinta on painettu, se asettaa ykköseksi (1) bitin (Diu_TCH1), joka käynnistää sähkötyksen. Seuraavaksi ohjelma siirtyy odottamaan painikkeen vapautumista. Käynnistysbitti (Diu_TCH1) nollataan jokaisella ohjelmakierroksella ohjelman lopussa. Täten seuraava sähkötysjakso alkaa vasta uudesta painamisesta.

 


Alla oleva ohjelma on varsin yksinkertainen ja suoraviivainen. Painike on kaksiaskelinen sekvenssi. Summerin aliohjelmalle ladataan se, onko kyse pisteestä (1) vai viivasta (2). Viivan kesto on kaksi kertaa piste. Kokonainen merkki syntyy peräkkäisistä kutsuista. Merkkien väli on myös riippuvainen pisteen kestosta.

 Tässä ohjelmassa käytössä ovat seuraavat valinnat:

            10 liukukytkimet:   vasen 3 = painike 1, oikea 3 = summeri, oikea 6 = potikka

            9 hipaisupainike TCH1

            4 summeri

            12 potentiometri

 

 

/*****************************************************

* Ohjelman nimi: OH2A

* 28.05.2023

* Rich UNO kehitystä

* Piippaa amatöörikutsun painiketta painettaessa.

****************************************************/

#include <Arduino.h>

// MÄÄRITTELYT:

// Liityntöjen määrittelyt

// Digitaalinen tulo

const int Con_TCH1 = 3; // Kosketuspainike 1

const int Con_Ana_1 = 0; // Potentiometrin tulo


// Digitaalinen lähtö

const int Con_LEDiA = 5; // Merkkivalo

const int Con_Pilli = 9; // tämä on BUZZeri


// Muutujamäärittelyt

int Int_KierLasku = 0; // Jaksotetaan ohjelmakierroksilla

const int Con_Jakso = 140; // Perusjakso piste ja tauko

int Int_Jakso = 0; // Tähän lasketaan nopeus

boolean Bol_TCH1 = false;

boolean Kep_TCH1 = false; // Kosketuspainikkeen tilatieto

boolean Diu_TCH1 = false;

boolean Kep_Pilli = false; // Summerin tila

int Int_Nopeus = 0; // sähkötyksen nopeussäätö


// Sekvenssit

int Seq_TCH1 = 1; // Kosketuspainikkeen käsittely

int SeqPilli = 1; // Vingutuksen sekvenssi


// ALIOHJELMAT:

// Äänimerkin aktivointi

void Fun_Pilli(int kertaa, int kesto){

    int k = kertaa;

    int pituus = Int_Jakso * kesto;

        for(int i = 1; i <= k; i++){

            digitalWrite(Con_Pilli, true);

            delay(pituus);

            digitalWrite(Con_Pilli, false);

            delay(Int_Jakso);

        }//foorin loppu          

}// Pillin loppu


// ASETUKSET:

void setup(){

    Serial.begin(9600);

    pinMode(Con_TCH1, INPUT); // Kosketuspainike

    pinMode(Con_Ana_1, INPUT); // Potentiometrin tulo

    pinMode(Con_Pilli, OUTPUT);

}// Asetuksen loppu


// PÄÄLOOPPI

void loop(){ // Valvotaan painiketta ja muodostetaan ohjauskoodi

    Int_Nopeus = analogRead(Con_Ana_1);

    Int_Jakso = Con_Jakso - Int_Nopeus / 10; // Sähkötyksen nopeus potikalta


    Bol_TCH1 = digitalRead(Con_TCH1);

        switch (Seq_TCH1) { // Kosketuspainikkeen sekvenssi

            case 1:

                if(Bol_TCH1 == true){

                    Diu_TCH1 = true;

                    Seq_TCH1 = 2;

                }

        break;

        case 2:

                if(Bol_TCH1 == false){Seq_TCH1 = 1;}

        break;

    }// Sekvenssin loppu


// 1. montako merkkiä 2. kesto, 3. tauon pituus

if(Diu_TCH1 == true){

    for(int i = 1;i <= 2; i++){

        Fun_Pilli(1, 2);                  // C

        Fun_Pilli(1, 1);

        Fun_Pilli(1, 2);

        Fun_Pilli(1, 1);

        delay(3*Int_Jakso);

        Fun_Pilli(2, 2);                  // Q

        Fun_Pilli(1, 1);

        Fun_Pilli(1, 2);

    delay(3*Int_Jakso);

    } // kaksi yleistä kutsua

        Fun_Pilli(1, 2);                  // D

        Fun_Pilli(1, 1);

        Fun_Pilli(1, 1);

    delay(3*Int_Jakso);

        Fun_Pilli(1, 1);                 // E

    delay(3*Int_Jakso);             // tässä

        Fun_Pilli(3, 2);                 // O

    delay(3*Int_Jakso);

        Fun_Pilli(4, 1);                  // H

    delay(3*Int_Jakso);

        Fun_Pilli(2, 1);                 // 2

        Fun_Pilli(3, 2);

    delay(3*Int_Jakso);

        Fun_Pilli(1, 1);                  // A

        Fun_Pilli(1, 2);

    delay(7*Int_Jakso);               // OH2A

        Fun_Pilli(1, 2);                  // K

        Fun_Pilli(1, 1);

        Fun_Pilli(1, 2);

    delay(3*Int_Jakso);             // kuuntelen

}// kutsu loppu


Diu_TCH1 = false; // DifUpp nollataan joka kierroksella

delay(1);

} // Pääohjelma LOPPU

 

sunnuntai 27. helmikuuta 2022

HexNoppa_62

Lautapelit ovat nousseet jälleen suosioon. On siis näin digitaaliaikana syytä päivittää arpakuutio. Tässä tapauksessa ei tietenkään ole kuutio, vaan arvottavat luvut välillä 0 ..15, eli hexaluvut 0..F. 

Arpakuutiossa on mahdollista saada luvut välillä 1..6, mutta mikä estäisi vaeltamasta Afrikan tähdessä hiukan pidempiäkin matkoja samalla kertaa. Tai saamalla nollan (0), siirtää vuoro seuraavalle. Toki luvut 1..6 ovat tässäkin mahdollisia, ja silloin LEDejäkin tarvitaan ainoastaan kolme kappaletta. 

Itse asiassa tärkeämpänä tavoitteena oli harjoitella sitä, miten kokonaisluvun arvo voidaan muuntaa biteiksi ja osoittaa Arduinon lähdössä binäärinumerona. Näin digitaalivaltakautena on kunkin hyvä oppia myös tämä järjestelmä, millä meidän arkemme käytännössä toimii. Tietysti, ja varmaan varsinkin alussa, tämä keino herättää myös keskustelua siitä, minkä numeron arpa tällä kerralla tarjosi. Tuossa kuvassa on tietysti luku 11 eli oikealta vasemmalle laskien 1 + 2 + 8 = 11. 

 

/***************************************

* Ohjelman nimi: HexNopB

* 27.2.2022

* Lyhyt toimintaselostus:

* Arvotaan hexaluvut välillä 0 .. F, ja

* tulostetaan ne neljällä LEDillä

**************************************/


// MÄÄRITTELYT:

// Liityntöjen määrittelyt

const int Con_DO_0 = 11;

const int Con_DO_1 = 10;

const int Con_DO_2 = 9;

const int Con_DO_3 = 8;

const int Con_In = 2;

boolean Bol_In = 0;

// Muita määrittelyjä

const int Con_Vi = 100;

int Int_Vi = 0;

int Seq_Kytkin = 1;

int Int_Arpa = 0;

byte Bin_0 = 0;

byte Bin_1 = 0;

byte Bin_2 = 0;

byte Bin_3 = 0;

// ALIOHJELMAT

void Fun_Tulostus(){

Serial.print("Arpaluku: "); Serial.println(Int_Arpa);

}// Tulostuksen loppu

// ASETUKSET:

void setup(){

Serial.begin(9600);

pinMode(Con_DO_0, OUTPUT);

pinMode(Con_DO_1, OUTPUT);

pinMode(Con_DO_2, OUTPUT);

pinMode(Con_DO_3, OUTPUT);

pinMode(Con_In, INPUT_PULLUP);

}// Asetuksen loppu


// PÄÄLOOPPI

void loop(){

Bol_In = digitalRead(Con_In);

// Muodostetaan satunnaisluku

switch (Seq_Kytkin) {

case 1:

if (!Bol_In) {Int_Vi += 1;} // kosketinvärähtelyviive

if (Int_Vi > Con_Vi){

Int_Arpa = random(0, 16); // arvonta välillä 0 .. 15

// Fun_Tulostus();

// Luetaan bitit

Bin_0 = bitRead(Int_Arpa, 0); Bin_1 = bitRead(Int_Arpa, 1);

Bin_2 = bitRead(Int_Arpa, 2); Bin_3 = bitRead(Int_Arpa, 3);

// Kirjoitetaan bitit

digitalWrite(Con_DO_0, Bin_0); digitalWrite(Con_DO_1, Bin_1);

digitalWrite(Con_DO_2, Bin_2); digitalWrite(Con_DO_3, Bin_3);

Seq_Kytkin = 2;}

break;

case 2:

if (!Bol_In == 0){

Int_Vi = 0;

Seq_Kytkin = 1;

}

break;

} // Arvonnan loppu

delay(1);

} // Pääohjelma LOPPU

 

tiistai 13. huhtikuuta 2021

SHAKKIKELLO

 

Onkin ollut aikaa ajatella mitä sitä keksisi. Uutisista on tullut runsaasti ohjeita miten varoa, väistää, suojautua ja siivota. Kaikkea kertyy vuosien mittaan ja laatikoita siivotessa eteen sattui shakkilauta. En tosin enää pelaa, enkä ole koskaan ollutkaan kovin hyvä pelaaja, mutta ajattelin, että shakkikellon tekeminen Arduinolle olisi ehkä mielekäs haaste.

 Shakissa ja ohjelmoinnissa on se selkeä ero, että pelatessa jokainen virhe johtaa häviön suuntaan, mutta ohjelmoinnissa oppimiseen (kunhan onnistuu korjaamaan). Niin tämäkin pienin askelin ja virheiden kautta lähti ilokseni toimimaan suunnitellulla tavalla.

 Kotelo voisi olla hienompikin. On siinä kuitenkin se hyvä puoli, että se on kierrätyskamaa. Käyttöenergiansa kello saa 9 V:n neppariparistosta. LCD-näyttö saa syöttönsä Arduinon 5 V:n lähdöstä.

  Kello käynnistyy virtakytkimestä. Painamalla painiketta, lähtee vastapelaajan tikutus käyntiin. Kun vastapelaaja saa siirtonsa tehtyä ja painaa omaa nappiaan, pysähtyy hänen kellonsa ja toinen käynnistyy. Kellot laskevat sekunteja, minuutteja ja tunteja. Tunnit on rajoitettu yhdeksään (9). Maksimi peliaika on siis 2 * 9 tuntia, 59 minuuttia ja 59 sekuntia. Minuutit ja sekunnit kiertävät (59 => 0), niitä ei pysäytetä, mutta tuon jälkeen pitää kirjata käsin.


 OHJELMA 61

 /************************************************
 *  Ohjelman nimi: ShakkiKello61_v6
 *  11.04.2021
 *  Shakkikello LCD-näytöllä
 *  A-painike käynnistää B:n ajan laskun
 *  ja B-painike A:n ajanlaskun
 *  Molemmat painikkeet lyhyesti, pysäyttää ajan
 *  Molemmat yli 2s nollaa ajat.
 ***********************************************/

// MÄÄRITTELYT:
// LCD-näyttö
#include <LiquidCrystal.h>
LiquidCrystal lcd(3, 2, 7,6,5,4);

// Tekstit
    char tx1[] = "PELAAJA B";
    char tx2[] = "PELAAJA A";   

// Liityntöjen määrittelyt
    // Pelaajan A määrittelyt
    const int Con_PelA = 8; // Pelaajan A painike
    boolean Bol_PelA = false;
    boolean Bol_ValintaA = false;
    boolean Kep_PelA = false;
    int Seq_PelA = 1;
    int Int_SekA = 0;
    int Int_MinA = 0;
    int Int_TunA = 0;
    int Int_PainA = 0;
    unsigned long Ulo_PelA  = 0;

   // Pelaajan B määrittelyt
    const int Con_PelB = 9;// Pelaajan B painike
    boolean Bol_PelB = false;    
    boolean Bol_ValintaB = false;
    boolean Kep_PelB = false;    
    int Seq_PelB = 1;
    int Int_SekB = 0;
    int Int_MinB = 0;
    int Int_TunB = 0;
    int Int_PainB = 0;
    unsigned long Ulo_PelB  = 0;     

    // Pelaajien yhteiset määrittelyt
    boolean Bol_Tauko = false;
    unsigned long Ulo_Nollaus = 0;

// Kellon ja tahdistuksen määrittelyt
    unsigned long Ulo_MilliSek = 0;
    unsigned long Ulo_Tulostus = 0;
    int Int_Viive = 100;

// ALIOHJELMAT
// Tekstin päivitys
void Fun_Teksti(int kohta, int rivi, char teksti[]){
  lcd.setCursor(kohta, rivi);
  lcd.print(teksti);
}// Teksti_ali loppu

// Ajan päivitys
void Fun_Aika(int kohta, int rivi, int lukema){
  lcd.setCursor(kohta, rivi);
  lcd.print(lukema);
}// Aika_ali loppu

// ASETUKSET:
void setup(){                  
 Serial.begin(9600);    
  lcd.begin(20,2);
  lcd.clear();     
  Fun_Teksti(0, 0, tx1);
  Fun_Teksti(11, 0, tx2);
  pinMode(Con_PelA, INPUT_PULLUP);
  pinMode(Con_PelB, INPUT_PULLUP);  
}// Asetuksen loppu


// PÄÄLOOPPI                   
void loop(){
// Sisäisen kellon päivitys
Ulo_MilliSek = millis();

// Pelaajat A & B
Bol_PelA = !digitalRead(Con_PelA);
Bol_PelB = !digitalRead(Con_PelB);
Bol_ValintaA = Bol_PelA == 1 && Bol_PelB == 0;
Bol_ValintaB = Bol_PelA == 0 && Bol_PelB == 1;
Bol_Tauko = Bol_PelA == 1 && Bol_PelB == 1;
if(Bol_Tauko == true){ Kep_PelA = false; Kep_PelB = false;}
if(Bol_Tauko == false){Ulo_Nollaus = Ulo_MilliSek;}
if(Bol_Tauko == true && Ulo_MilliSek > (Ulo_Nollaus + 1000)){
    Int_SekA = 0; Int_MinA = 0;  Int_TunA = 0;
    Int_SekB = 0; Int_MinB = 0;  Int_TunB = 0;  
}

// Pelaajan B valinta painikkeella A
switch (Seq_PelA) {
  case 1:
    if(Bol_ValintaA == false) {Int_PainA = 0;
    }else{Int_PainA++;}
    if(Bol_ValintaA == true &&  Int_PainA > Int_Viive){
      Kep_PelB = false; Kep_PelA = true;
      Ulo_PelB = Ulo_MilliSek;
      Seq_PelA = 2;}
    break;
  case 2:
    if(Bol_ValintaA == false){Seq_PelA = 1;}
    break;
}// Sekvenssin A loppu

// Pelaajan A valinta painikkeella B
switch (Seq_PelB) {
  case 1:
     if(Bol_ValintaB == false){Int_PainB = 0;
    }else{Int_PainB++;}     
    if(Bol_ValintaB == true &&  Int_PainB > Int_Viive){
      Kep_PelA = false; Kep_PelB = true;
      Ulo_PelA = Ulo_MilliSek;      
      Seq_PelB = 2;}
    break;
  case 2:
    if(Bol_ValintaB == false){Seq_PelB = 1;}
    break;
}// Sekvenssin B loppu

//A-pelaajan laskuri
if(Ulo_MilliSek > Ulo_PelA + 999 && Kep_PelB == true){
   Int_SekA++;
   Ulo_PelA = Ulo_MilliSek;}
   if(Int_SekA > 59){Int_SekA = 0; Int_MinA++;}
   if(Int_MinA > 59){Int_MinA = 0; Int_TunA++;}
   if (Int_TunA > 9){Int_TunA = 9;}

// B-pelaajan laskuri
if(Ulo_MilliSek > Ulo_PelB + 999 && Kep_PelA == true){
   Int_SekB++;
   Ulo_PelB = Ulo_MilliSek;}
   if(Int_SekB > 59){Int_SekB = 0; Int_MinB++;}
   if(Int_MinB > 59){Int_MinB = 0; Int_TunB++;}
   if (Int_TunB > 9){Int_TunB = 9;}

// Pelaajien ajan tulostus
if(Ulo_MilliSek > Ulo_Tulostus + 499){

  if(Int_TunB < 1){Fun_Teksti(1,1,"0");
  }else{Fun_Aika(1,1,Int_TunB);}
  Fun_Teksti(2,1,"t");
  if(Int_MinB < 10){Fun_Teksti(3,1,"0"); Fun_Aika(4,1,Int_MinB);
  }else{Fun_Aika(3,1,Int_MinB);}
  Fun_Teksti(5,1,"m");
  if(Int_SekB < 10){Fun_Teksti(6,1,"0"); Fun_Aika(7,1,Int_SekB);
  }else{Fun_Aika(6,1,Int_SekB);}
  Fun_Teksti(8,1,"s");

 if(Int_TunA < 1){Fun_Teksti(12,1,"0");
  }else{Fun_Aika(12,1,Int_TunA);}
  Fun_Teksti(13,1,"t");
  if(Int_MinA < 10){Fun_Teksti(14,1,"0"); Fun_Aika(15,1,Int_MinA);
  }else{Fun_Aika(14,1,Int_MinA);}
  Fun_Teksti(16,1,"m");
  if(Int_SekA < 10){Fun_Teksti(17,1,"0"); Fun_Aika(18,1,Int_SekA);
  }else{Fun_Aika(17,1,Int_SekA);}
  Fun_Teksti(19,1,"s");
 
  Ulo_Tulostus = Ulo_MilliSek;
}// Aikatulostuksen loppu
 delay(1);
} // Pääohjelma LOPPU