Számok, dátumok, időpontok kimondása magyarul

Tartalom:

  • Számadatok, dátumok , időpontok, mértékegységek kimondása magyarul mp3 lejátszó modul segítségével
  • A kimondáshoz szükséges hanganyagok letölthetők és felhasználhatók (női és férfi hang is)
  • A kimondott szavakat a soros porton keresztül le is írja
  • Leírt szavak tárolása az eeprom-ban, hogy maradjon hely a ram-ban
  • Teljes forráskód példa programmal, egyéb kiegészítő programokkal

————————————————————————————————–

Nagy fába vágtam a fejszémet! Pusztán szórakozásból (és nem azért, mert feltétlenül kell) egy beszélő mérőműszer építésbe kezdtem. Ehhez bizony elég sok komponens szükséges, önmagában mindegyik kihívás számomra. Most éppen a mért eredmények kimondásával értem el sikereket. A programok megírása nem volt nehéz feladat, bár az mp3 lejátszó hozott meglepetéseket. A számok (szövegek) között elég hosszú idő telik el, így nem lesz a felolvasott szöveg teljesen folyamatos és egybefüggő. Sajnos a „megszerzett” hangok sem tökéletesek. A Google és a Word felolvasó programját használtam fel, ezekkel olvastattam fel a szavakat. Sajnos a hangsúlyok nem minden szónál kerültek a helyükre, ezért nem teljesen emberi az elhangzó szöveg. Azonban a kimondott számok tökéletesen érthetőek, még kellemetlennek sem lehet nevezni, egyszerűen csak gépszerű, darabos mint a robot tánc! Sokat segít a most ismertetett program megértéséhez, ha előtte megnézed az mp3 lejátszóról szóló leírásomat a modulok menüpontban!

A beszélő mérőműszerhez nem csak a számok kimondásához szükséges szavak kellenek, szükség van néhány mértékegységre is, mint pl. „amper” „volt” stb. Evés közben jön meg az étvágy, mint azt mondani szokták, és több egyéb probléma miatt is többször újra kellett a szavakat felvenni, és minden alkalommal tovább bővítettem a szavakat, amiket felolvastattam. Egyre több ötletem támadt, hogy mire is használhatnám a felolvasó programomat. Lehetne csinálni órát, ami kimondja az időt és a dátumot, beszélhetne a hőmérő stb. Így aztán egyre több szó került a szótárba. Jelenleg a következő szavakat tartalmazza az a hanganyag, amit le tudtok tölteni innen !

A jelenlegi beszédszótár:
Számok:
nulla=1, egy=2, ketto=3, harom=4, negy=5, ot=6, hat=7, het=8, nyolc=9, kilenc=10, tiz=11, husz=12, harminc=13, negyven=14, otven=15, hatvan=16, hetven=17, nyolcvan=18, kilencven=19, szaz=20, ketszaz=21, haromszaz=22, negyszaz=23, otszaz=24, hatszaz=25, hetszaz=26, nyolcszaz=27, kielencszaz=28, elso=29, masodik=30, harmadik=31, negyedik=32, otodik=33, hatodik=34, hetedik=35, nyolcadik=36, kilencedik=37, tizedik=38, tizenegyedik=39, tizenkettedik=40, zero=41, tizen=42, huszon=43, plussz=44, minusz=45, ezer=46, millio=47, milliard=48, egesz=49, ezred=50, szazad=51, tized=52,
Hét napjai:
hetfo=53, kedd=54, szerda=55, csutortok=56, pentek=57, szombat=58, vasarnap=59,
Hónapok:
januar=60, februar=61, marcius=62, aprilis=63, majus=64, junius=65, julius=66, augusztus=67, szeptember=68, oktober=69, november=70, december=71,
Mértékegységek:
volt=72, milivolt=73, amper=74, miliamper=75, ohm=76, hertz=77, kilohertz=78, celsiusfok=79, milibar=80; kilometer=81,kilogramm=82,gramm=83,ora=84, perc=85, masodperc=86, honap=87, nap=88, napja=89,
Egyéb:
valto=90, egyen=91,feszültség=92,áramerősség=93,ellenállás=94,üres (0,3sec csend)=95

Nagyon fontos a sorrend, és a szavak mögötti sorszám! Sajnos az mp3 lejátszó akkor tudja leggyorsabban váltani a szavakat (egymást követő számok lejátszása közötti szünet akkor a legkisebb), ha nem a file-ok nevét adjuk meg, hanem egy sorszámot, ami kissé meglepő módon az sd kártyára másolás sorszáma. Tehát ha egy állományt pl. 16.-nak másoltunk fel az SD kártyára, akkor az mp3 lejátszó. „play” utasításának a 16 legyen a paramétere ahhoz, hogy az adott file szólaljon meg. Ez eléggé kényelmetlen használatot jelent, mert nem lehet csak úgy kicserélni a file-okat, ha valamin módosítunk. A kártyát érdemes formázni, és újra másolni mindent. Még az sem mindegy, milyen programmal írjuk fel. A Windows intéző például nem a látható sorrend szerint másol, hanem számomra megfejthetetlen logika szerinti. A Total Commander úgy tapasztaltam megtartja a látható sorrendet, így az használható. Végső esetben lehet egyenként kézzel is felmásolni az állományokat, nem olyan nagy munka, és nem kell minden nap megcsinálni.

A program lelke három önálló függvény. Az egyik dátumokat, a másik időpontokat, a harmadik pedig számokat alakít szöveg sorozatokká egy tömbben. Érthetőbben megfogalmazva, a fenti szavak index (sorszám, ahogy tetszik) értékeit teszi egy tömbbe. Ezt a tömböt elmésen „szo[]”-nak neveztem el. Amikor pl. ki kell mondani a ‘123’ számot, akkor a tömb első eleme a „száz”=20, második a „huszon”=43, és a harmadik pedig a „három”=4. A tömb többi eleme 0. Az mp3 lejátszónak aztán csak sorban elő kell venni a tömbben meghatározott értékeknek megfelelő „zenéket” és lejátszani. Ahhoz, hogy a loop() ciklus mással is tudjon foglalkozni, és ne álljon minden más esetleges programfunkció, kicsit gondolkodnom kellett. Végül azt a megoldást választottam, hogy egy „index” nevű globális változóval vezérlem a folyamatot. A szo[] tömböm 15 elemű, így ha az index értéke 15, akkor nem kell lejátszani semmit, nem foglalkozok az mp3 lejátszóval. Ha index értékét a programban valahol 0-ra állítom, akkor az mp3 lejátszó elindítja az első (0.) tömbelem szerinti szám lejátszását, és ettől kezdve minden loop() ciklusban, megnézem végzett-e már. Ha nem, akkor fut tovább a loop() és csinálja amit az egyéb programrészek megkívánnak. Ha végzett a szám lejátszásával, akkor elindítja a következő tömbben meghatározott sorszámú file lejátszását. Ha a tömbben 0 értéket talál, akkor index értékét 15-re állítja, mert vége a lejátszásnak, és bekövetkezik a nyugalmi helyzet, beáll a nagy „csend”. Megírtam a tömb értékeinek megfelelő file-ok lejátszását egy külön függvényben is, ennek hátránya, hogy amig beszél a kütyü, semmi mást nem tud csinálni a program. Viszont nagyon áttekinthető programot lehet írni, sorban egymás alá lehet írogatni a kimondásra szánt számokat, követhetők az események. Van is erre példa a setup() részben.

Sejthető, hogy a nehézségeket nem az mp3 vezérlése okozta, hanem a számok, dátumok és időpontok szöveggé, illetve szavak sorszámainak sorozatává alakítása. Különösen a számokkal kellett sokat szenvedni. A program egyszerű, rengeteg if sora van. Az ember nem tudatosan mondja a számokat, és ezért fel sem tűnik, mennyi logikátlan dolgot csinálunk beszéd közben. Pl. ha azt modom „harminc” és „harmincegy” láthatóan az egy elé a „harminc”-at kellett mondanom, ami nagyon logikus, hiszen a harminc önmagában is értelmes szám. Azonban, ha azt mondom „húsz” és „huszonegy” ott már „huszon” nem értelmes önmagában. Hogy alakult ki nem tudom, de ez van. Nem mondhatta a beszélő gépem huszonegy helyett azt, hogy „huszegy”! Ezeket a logikátlanságokat kellett feltárni és leprogramozni sok if beépítésével. Azt hiszem a programban ez az egyedüli érték, sok időm ment rá, míg jól kezdett működni.  Fejlesztés közben természetesen a szövegesítő algoritmus kipróbálását nem az mp3 lejátszóval végeztem. A UnoArdiSim Arduino szimulátort használtam eleinte, de ahogy nőtt a program mérete, kiderült, hogy ennek a szimulátor programnak is vannak korlátai. Elérkezett a pillanat, amikor már olyan hosszú volt a függvény, hogy a szimulátor már nem volt képes futtatani, meghatározhatatlan hibával kifeküdt. Szerencsére ez már csak a fejlesztési folyamat végén történt, így nagyon jól tudtam haladni, hiszen a belső változók értékét a szimulátorban látni lehet, lépésenként lehet végrehajtani a programot, ami nagyon meggyorsítja a munkát. Hálás köszönet az alkotónak!
Amikor elkészültem a jónak vélt algoritmussal, ki kellett próbálni. Ezt még mindig nem az mp3 lejátszóval csináltam. Kitaláltam magamnak, hogy a számok mögé kiírom szövegesen is a számot. Arduino környezetben nem tudom elképzelni, hogy erre szükség lehet, hiszen nem fogok számlázó rendszert készíteni, ami a jogszabályoknak megfelelően szövegesen is leírja a számla végösszegét, de a feladat érdekesnek tűnt. Első megoldásom az volt, hogy csináltam annyi string-et, ahány szót a fenti listában látni lehet. Azonban ez a mennyiség teljesen megette a memóriát. A program nem működött jól, egy idő után már nem írt semmit. Ennek oka, hogy a függvények paraméter átadását szintén a ram memóriában végzi, és ez a terület már beleírt az én string változóim tartalmába. Én azonban nem az az alkat vagyok, aki feladja az elképzelését, még ha az elképzelés megvalósítása felesleges is! Elhatároztam, hogy a szövegeket eeprom-ban fogom tárolni, és onnan írom ki, így nem fognak helyet foglalni a ram-ban. Meg is valósult a megoldás, erről szól a „Adatbázis kezelés EEPROM-ban” című írásom, illetve az ott elkészült program. Nem volt teljesen felesleges fejlesztés, fel fogom használni a jövőben az ott készült saját függvényeimet. Az eeprom-ba tárolása a szavaknak a következő forráskóddal lehetséges. Ezt egyszer kell lefuttatni, és lesznek szavak az eeprom-ban, ráadásul pont azok, amikre szükség lesz a későbbi forráskódhoz, illetve amiket a cikk elején olvashattál. Fontos a sorrend!! Persze a szövegek eepromba történő tárolásával akkor kell foglalkozni, ha le is akarjuk írni a kimondott szavakat. Ha erre nincs szükség, akkor ki kell hagyni a megfelelő programrészeket a cikk végén található forráskódból.

/********************************************************************************************************************
 * Ezzel a programmal a belső eeprom-ba töltök szavakat, hogy azokat később más programokban fel tudjam használni   *
 * szövegek kiírására. Így nem foglalnak ram területet a programban konstansként megadott szöveges változók.        *
 * Itt az eeprom_txt_ir() függvényt használom, más programokban az eeprom_txt_olvas() függvényt kell használni      *
 * a kiolvasáshoz. A példában látható szavakat a beszélű és mérőműszer fejlesztésekor használtam. Ezzel teszteltem  *
 * a kimondást, illetve kezdetben csak soros portra írtam a szavakat, hogy ellenőrizzem jó-e ez átalakító algoritmus*
 ********************************************************************************************************************/
#include <EEPROM.h> 

struct eeprom {               //Ebben a struktúrában adja vissza az adatokat az eeprom_txt..() függvény függvény csopprt, kivéve az eeprom_txt_olvas(), mert az string-et ad vissza.
                              //Az eeprom_txt_ir() függvény egyik paramétere a az a méret, amit elfoglalhatunk az eeprom-ból.
                              //Ennek maxmiális értéke ATmega328 esetén 1024 byte, mert ekkor a az eeprom méret, de használhatunk belőle kevesebbet is.
  int eeprom_meret=1024;       //ebben a konstansban tartjuk az adatbázis kezelésre fenntartott eeprom terület maximális méretét
  int foglalt_hely;          //ebben a változóban kapjuk meg a foglalt helyek számát byte-ban
  byte rekord_szam;           //az eddig beírt szavak száma
  bool sikeres;               //értéke 1, ha sikeres volt a tárolás, vagy lekérdezés, és 0, ha nem tudta beírni a megadott szót
};

eeprom x;                     //rögtön létre is hozunk egy adat nevű, és eeprom típusú változót

void setup()
{
  Serial.begin(9600);
  Serial.println("EEPROM törlés...");
  for (int i=0;i<512;i++) {
    EEPROM.write(i,0);
  }

  Serial.println("Szövegek tárolása:");
  x=eeprom_txt_ir("nulla",1);
  x=eeprom_txt_ir("egy",0);
  x=eeprom_txt_ir("kettő",0);
  x=eeprom_txt_ir("három",0);
  x=eeprom_txt_ir("négy",0);
  x=eeprom_txt_ir("öt",0);
  x=eeprom_txt_ir("hat",0);
  x=eeprom_txt_ir("hét",0);
  x=eeprom_txt_ir("nyolc",0);
  x=eeprom_txt_ir("kilenc",0);
  x=eeprom_txt_ir("tíz",0);
  x=eeprom_txt_ir("húsz",0);
  x=eeprom_txt_ir("harminc",0);
  x=eeprom_txt_ir("negyven",0);
  x=eeprom_txt_ir("ötven",0);
  x=eeprom_txt_ir("hatvan",0);
  x=eeprom_txt_ir("hetven",0);
  x=eeprom_txt_ir("nyolcvan",0);
  x=eeprom_txt_ir("kilencven",0);
  x=eeprom_txt_ir("száz",0);
  x=eeprom_txt_ir("kétszáz",0);
  x=eeprom_txt_ir("háromszáz",0);
  x=eeprom_txt_ir("négyszáz",0);
  x=eeprom_txt_ir("ötszáz",0);
  x=eeprom_txt_ir("hatszáz",0);
  x=eeprom_txt_ir("hetszáz",0);
  x=eeprom_txt_ir("nyolcszáz",0);
  x=eeprom_txt_ir("kilencszáz",0);
  x=eeprom_txt_ir("első",0);
  x=eeprom_txt_ir("második",0);
  x=eeprom_txt_ir("harmadik",0);
  x=eeprom_txt_ir("negyedik",0);
  x=eeprom_txt_ir("ötödik",0);
  x=eeprom_txt_ir("hatodik",0);
  x=eeprom_txt_ir("hetedik",0);
  x=eeprom_txt_ir("nyolcadik",0);
  x=eeprom_txt_ir("kilencedik",0);
  x=eeprom_txt_ir("tizedik",0);
  x=eeprom_txt_ir("tizenegyedik",0); 
  x=eeprom_txt_ir("tizenkettedik",0);
  x=eeprom_txt_ir("zéró",0);
  x=eeprom_txt_ir("tizen",0);
  x=eeprom_txt_ir("huszon",0);
  x=eeprom_txt_ir("plusz",0);
  x=eeprom_txt_ir("mínusz",0);
  x=eeprom_txt_ir("ezer",0);
  x=eeprom_txt_ir("millió",0);
  x=eeprom_txt_ir("milliárd",0);
  x=eeprom_txt_ir("egész",0);
  x=eeprom_txt_ir("ezred",0);
  x=eeprom_txt_ir("század",0);
  x=eeprom_txt_ir("tized",0);
  x=eeprom_txt_ir("hétfő",0);
  x=eeprom_txt_ir("kedd",0);
  x=eeprom_txt_ir("szerda",0);
  x=eeprom_txt_ir("csütörtök",0);
  x=eeprom_txt_ir("péntek",0);
  x=eeprom_txt_ir("szombat",0);
  x=eeprom_txt_ir("vasárnap",0);
  x=eeprom_txt_ir("január",0);
  x=eeprom_txt_ir("február",0);
  x=eeprom_txt_ir("március",0);
  x=eeprom_txt_ir("április",0);
  x=eeprom_txt_ir("május",0);
  x=eeprom_txt_ir("június",0);
  x=eeprom_txt_ir("július",0);
  x=eeprom_txt_ir("augusztus",0);
  x=eeprom_txt_ir("szeptember",0);
  x=eeprom_txt_ir("október",0);
  x=eeprom_txt_ir("november",0); 
  x=eeprom_txt_ir("december",0);
  x=eeprom_txt_ir("volt",0);
  x=eeprom_txt_ir("millivolt",0);
  x=eeprom_txt_ir("amper",0);
  x=eeprom_txt_ir("milliamper",0);
  x=eeprom_txt_ir("ohm",0);
  x=eeprom_txt_ir("hertz",0);
  x=eeprom_txt_ir("kilohertz",0); 
  x=eeprom_txt_ir("celsiusfok",0);
  x=eeprom_txt_ir("milibar",0);
  x=eeprom_txt_ir("kilométer",0);
  x=eeprom_txt_ir("kilogramm",0);
  x=eeprom_txt_ir("gramm",0);
  x=eeprom_txt_ir("óra",0);
  x=eeprom_txt_ir("perc",0);
  x=eeprom_txt_ir("másodperc",0);
  x=eeprom_txt_ir("hónap",0);
  x=eeprom_txt_ir("nap",0);
  x=eeprom_txt_ir("napja",0);
  x=eeprom_txt_ir("váltó",0);
  x=eeprom_txt_ir("egyen",0);
  x=eeprom_txt_ir("feszültség",0);
  x=eeprom_txt_ir("áramerősség",0);
  x=eeprom_txt_ir("ellenállás",0);
  x=eeprom_txt_ir(" ",0);
  x=eeprom_txt_hely();
  Serial.print("Állapot->");
  Serial.print("foglalt:");Serial.print(x.foglalt_hely);
  Serial.print(" byte, rekord:");Serial.print(x.rekord_szam);
  Serial.print("db Sikeres:");Serial.println(x.sikeres);
  Serial.println("Tárolt szavak:");
  for (byte i=1;i<x.rekord_szam+1;i++) {
    Serial.println(eeprom_txt_olvas(i));
  }
}


void loop()
{

}


eeprom eeprom_txt_ir(String szoveg,int index) 
/*********************************************************************************************
 * Ez a függvény a paraméterként átadott string-et beírja az eeprom-ba. Minden alkalommal    *
 * amikor meghívjuk, megkeresi a következő üres helyet az "adatbázisban". Egy szöveg elé     *
 * letárolja a szöveg karakterekben mért hosszát (az ékezetes betűk 2 byte-osak) és egy      *
 * 0-val zárja le a szöveget. A 0 azért kell a végére, mert a következő szöveg beírásakor    *
 * ez a 0 jelzi, hogy innen lehet írni, vagyis nincs több szöveg a sorban (az adott 0-val    *
 * lezárt szöveg az utolsó a sorban). Egy következő beíráskor azt a 0-át felülírja a         *
 * következő szöveg hosszával. A szövegek kiolvasásakor ezeknek a hosszoknak a               *
 * segítségével keresi mega  következő szöveget. A kiolvasás kizárólag elölről kezdve        *
 * sorrendben lesz lehetséges.                                                               *
 * A függvény paraméterei:                                                                   *
 *  szoveg       - az eepromba beírandó szting, maximum 255 byte hosszú lehet                *
 *  index        - egy olyan érték, ami lehetőséget ad az eepromba írás elkezdésére,         *
 *                 folytatására, illetve egy adott ponttól történő felülírására.             *
 *                 ha index=0, akkor folytatja a szöveg beírást a következő szabad           *
 *                 területtől. Ha az eeprom-ot előtte töröltük (vagy csak az első byte-ot)   *
 *                 akkor az adott szöveg lesz sorrendben az első, és innen folytatja a       *
 *                 többivel. Ha a megadott érték nagyobb mint nulla, akkor a megadott        *
 *                 értéknek megfelelő helyre írja a szöveget. Ha ez 1, akkor rögtön          *
 *                 legelsőnek. Így lehet egy meglévő szöveges adatbázis törölni, mert        *
 *                 elsőnek írja be a szöveget, és ezzel minden más előzményt eltüntet.       *
 *                 Ha pl 3, akkor ez a harmadik szövegnek írja be, és ha több szöveg         *
 *                 is volt, azok már nem lesznek elérhetők.                                  *
 *********************************************************************************************/
{
  eeprom adat;                   //ezt a változó struktúrát fogjuk visszaadni amikor befejeztük az írást
  adat.foglalt_hely=0;           //kezdetben a foglalt hely 0, ezt növelni fogjuk, ahogyan haladunk előre a következő rekord
                                 //keresésekor. Ez egyben a memória cím is, ahonnan adatokat olvasunk, vagy ahová adatoka írunk. 
                                 //Amikor beírtuk a szöveget és a lezáró nullát, értéke éppen az elfoglalt mennyisége byte-okban.
  adat.rekord_szam=0;            //ez jelzi, hogy éppen hányadik rekordnál járunk. A beírást követően ebben adjuk 
                                 //vissza, hogy éppen hány rekord van az adatbázisban
  adat.sikeres=0;                //ez is visszaadásra kerül a beírást követően, ha értéke 1, akkor sikeres volt a szöveg beírása
                                 //he nem sikerült beírni a szöveget, akkor 0.
  byte ertek;                    //ebbe olvassuk ki a szöveget megelőző egy byte-ot, ez lesz az aktuális szöveg hossza.
  byte meret=szoveg.length();    //a beírásra átadott szöveg hossza byte-okban. Ékezetes betűk 2 byte-ot foglalnak
  if (index!=1) {                //ha index nagyobb mint 1, akkor meg lett adva, hogy hányadik rekordnak írjuk be a szöveget
                                 //ha index=1, akkor kihagyjuk a keresést, mert rögtön az elsőnek kell beírni. 
                                 //ha index=0, akkor utolsónak kell beírni, tehát kell a keresés
    do  {
      ertek=EEPROM.read(adat.foglalt_hely);         //ez az aktuális szöveg hossza, ezzel növeljük majd a címet, ha tovább keressük a következő szöveget
      if (ertek==0) { break;}                       //megtaláltuk az utolsó szöveget, mert 0-val van lezárva, kiugrunk a ciklusból, mert ide lehet írni a szöveget
      adat.foglalt_hely=adat.foglalt_hely+ertek+1;  //a következő szöveg hosszát megadó byte címe
      adat.rekord_szam++;                           //a rekordszámot is növeljük, az első megtalált rekord után pont 1
      if (index==adat.rekord_szam) {break;}         //mivel meg lett adva beírási sorszám, és éppen annál járunk, kiugrunk a ciklusból, és 
                                                    //az adat.foglalt_hely változó által kijelölt címre fogjuk beírni a szöveget.
    } while (adat.foglalt_hely<adat.eeprom_meret);       //akkor is kiugrunk a ciklusból, ha elértük a kijelölt terület végét
  }
  
  if (adat.foglalt_hely+3>adat.eeprom_meret) {      //ha már 3 byte-nál kevesebb a szabad hely, akkor kilépünk sikertelen jelzéssel.
                                                    //ekkor előfordulhat, hogy nem tudjuk kiírni a teljes szöveget, hiszen csak 2 byte maradt.
    adat.sikeres=0;return(adat);
  }

  //elkezdjük beírni az adatot az eepromba
  EEPROM.write(adat.foglalt_hely,meret);            //első byte a szöveg mérete byte-okban (max. 255 lehet)
  adat.foglalt_hely++;                              //növeljük a kiírás címét, innen fogjuk írni a tényleges szöveget
  for(byte i=0;i<meret;i++)                         //sorban elővesszük a szöveg karaktereit
  {
    if (adat.foglalt_hely>adat.eeprom_meret) {      //itt is nézzük, hogy nem telt-e meg. Ha igen, akkor sikertelen jelzéssel kilépünk
      adat.sikeres=0;return(adat);
    }
    EEPROM.write(adat.foglalt_hely,szoveg[i]);     //ez itt a soron következő karakter beírása (ékezetes betűk két byte-ot foglalnak két lépésben)
    adat.foglalt_hely++;
  }
  EEPROM.write(adat.foglalt_hely,0);                //ezt azért nullázzuk, mert innen tudjuk legközelebb, hogy ez volt az utolsó rekord
                                                    //így nem kell törölni a teljes eeprom-ot, ha valahová középre írunk adatot
                                                    //az itt következő rekordok viszont automatikusan elvesznek
  adat.rekord_szam++;                               //rekordszám visszatérési érték növelése 1-el, hiszen hozzáírtunk egyet a meglévő szövegekhez
  adat.sikeres=1;                                   //sikeres írás jelzése
  return(adat);
}

eeprom eeprom_txt_hely() 
/************************************************************************************
 * Ez a függvény paraméter nélkül működik. Megkeresi az utolsó rekordot és          *
 * visszaadja a szavak számát, az elfoglalt és a még rendelkező helyet byte-okban.  *
 ************************************************************************************/
{
  eeprom adat;
  adat.foglalt_hely=0;           //Kezdetben a foglalt hely 0, ezt növelni fogjuk, ahogyan haladunk előre a következő rekord
                                 //keresésekor. Ez egyben a memória cím is, ahonnan adatokat olvasunk. Az utolsó rekordnál 
                                 //értéke éppen az elfoglalt mennyisége byte-okban.
  adat.rekord_szam=0;            //Ez jelzi, hogy éppen hányadik rekordnál járunk. A beírást követően ebben adjuk 
                                 //vissza, hogy éppen hány rekord van az adatbázisban
  adat.sikeres=0;                //Ennek az értéke csak 1 lehet, mert a lekérdezés nem tud nem sikeres lenni
  byte ertek;                    //ebbe olvassuk ki a szöveget megelőző egy byte-ot, ez lesz az aktuális szöveg hossza.
  do  {
    ertek=EEPROM.read(adat.foglalt_hely);            //Kiolvassuk az aktuális szöveg hosszát byte-okban
    if (ertek==0) { break;}                          //Ha nulla, akkor elértük az utolsó rekordot
    adat.foglalt_hely=adat.foglalt_hely+ertek+1;     //Ha nem nulla volt az értek, akkor kiszámoljuk a következő rekord kezdőcímét
    adat.rekord_szam++;                              //Növeljük a rekordok darabszám számlálóját
  } while (adat.foglalt_hely<adat.eeprom_meret);     //Akkor is kiugrunk a ciklusból, ha elértük a tárolásra használt epprom terület legvégét
  adat.sikeres=1;                                    //Nem tud nem sikeres lenni
  return(adat);                                      //Visszaadjuk a megállapított adatokat
}


String eeprom_txt_olvas(int index) 
/*********************************************************************************************
 * Ez a függvény visszaadja a paraméterben megkapott sorszámon tárolt szöveget. A szöveget   *
 * az eeprom megfelelő pontjainak végig olvasásával keresi meg. Minél nagyobb a paraméterben *
 * magadott sorszám, annál lassabban találja meg a keresett szöveget. Ha nagyobb sorszámot   *           
 * adunk meg paraméterként mint a szövegek darabszáma, akkor üres stringet ad vissza         *
 *********************************************************************************************/
{
  eeprom adat;
  String szo="";                       //Ebbe a változóba olvassuk vissza szöveget, ha megtaláltuk
  int akt_cim=0;                       //Ebben a változóban tároljuk az aktuális olvasási címet
  int akt_index=0;                     //Ebben a változóban számoljuk a rekordokat, folyamatosan növekszik.
                                       //Ha értéke megegyezik a paraméterben megkapott index-el (sorszámmal)
                                       //akkor megtaláltuk a szöveget
  byte ertek;                          //Ebbe olvassuk be a szövegek hosszának étékét a rekordok legelejéről
  do  {
    ertek=EEPROM.read(akt_cim);          //Kiolvassuk a szöveg hosszát
    if (ertek==0) {return("");}          //ha ez nulla, akkor az utolsó rekordhoz értünk, és üres stringet adunk vissza.
                                         //Ez csak akkor fordulhat elő, he a paraméterben megadott sorszám nagyobb, mint a rekordok száma.
    akt_index++;                         //Növeljük a darabszámot, mert érvényes szót találtunk
    if (akt_index==index) {              //Ha a paraméterben megkapott darabszám megegyezik az aktuális szöveg sorszámával, akkor ezt kerestük
    for (byte i=0;i<ertek;i++) {         //A kezdő byte-on megadott karakterszámot olvassuk végig
        szo=szo+" ";                     //Növeljük a szo nevű string változó hosszát azzal, hogy hozzáadunk egy karaktert
        szo[i]=EEPROM.read(akt_cim+1+i); //A szo nevű stringünk i-dik karakterére úgy tudunk hivatkozni, mintha egy tömb lenne, és így rögtön bele is tudjuk 
                                         //olvasni a tárolt számot, ami a karakter kódja
      }
      return(szo);                       //Az utolsó byte kiolvasása után visszatérünk a string-el 
    }
    akt_cim=akt_cim+ertek+1;             //Ha sorszám még kisebb volt mint a paraméterben kapott kiolvasandó sorszám, akkor kiszámítjuk a következő szó kezdő címét
  } while (akt_cim<adat.eeprom_meret);        //Akkor is kiugrunk a ciklusból, ha a tárolásra rendelkezésre álló eeprom terület végére értünk. Ez akkor fordulhat elő,
                                         //ha olyan nagy értéket adtunk meg a paraméterben, ami nagyobb, mint a tárolt szavak száma, és az eeprom is tele van.
  return("");                            //Visszatérési érték, ha nem találtuk meg a szöveget.
}

A futási eredmény (az utolsó sor egy szóközt tartalmaz, ez nem látszik világosan:

EEPROM törlés...
Szövegek tárolása:
Állapot->foglalt:768 byte, rekord:95db Sikeres:1
Tárolt szavak:
nulla
egy
kettő
három
négy
öt
hat
hét
nyolc
kilenc
tíz
húsz
harminc
negyven
ötven
hatvan
hetven
nyolcvan
kilencven
száz
kétszáz
háromszáz
négyszáz
ötszáz
hatszáz
hetszáz
nyolcszáz
kilencszáz
első
második
harmadik
negyedik
ötödik
hatodik
hetedik
nyolcadik
kilencedik
tizedik
tizenegyedik
tizenkettedik
zéró
tizen
huszon
plusz
mínusz
ezer
millió
milliárd
egész
ezred
század
tized
hétfő
kedd
szerda
csütörtök
péntek
szombat
vasárnap
január
február
március
április
május
június
július
augusztus
szeptember
október
november
december
volt
millivolt
amper
milliamper
ohm
hertz
kilohertz
celsiusfok
milibar
kilométer
kilogramm
gramm
óra
perc
másodperc
hónap
nap
napja
váltó
egyen
feszültség
áramerősség
ellenállás
 

Az is látszik, hogy a szavak tárolása egy kicsit kevesebb mint 768byte eeprom memóriát használt el a rendelkezésre álló 1kbyte-ból. Ha a programban konstansokba rakom a szavakat, akkor ennyivel kevesebb ram maradt volna!

Szóval végre elkészülhetett egy olyan program, ami a bemenetére adott számot szöveggé alakítja, és leírja a soros porton betűkkel. Frankón működik, szerintem hibátlan. Még egy kis rövidítést is beleprogramoztam! Bekapcsolható és kikapcsolható, hogy a tizedesjegy értékek végére kimondja-e a „tized”, „század” vagy „ezred” szavakat. Persze csak ott, ahol ez kihagyható. Pl. 0,12-nél nem kell oda tenni a végére, hogy nulla-egész-tizenkét-század, a nulla-egész-tizenkettő is egyértelmű. Azonban a 0,001 esetében már nem ennyire egyszerű! Nem mondhatom azt, hogy nulla-egész-egy, csak azt, hogy nulla-egész-egy-ezred. Mondhatnám pl. azt is, hogy nulla-egész-nulla-nulla-egy, de ez hosszabb.
Ezt követték a dátum és az időpont kimondó függvények. A dátum kimondását kicsit megvariáltam, a hónapokat a nevükkel és sorszámmal is ki tudja a mondani. Pl. „január hó” vagy „első hó”. További extra szolgáltatás, hogy megadható a hét napja is paraméterben, ekkor azt is meg fogja mondani, hogy pl. az a nap „csütörtök”. Majd elfelejtem, a számokat szavakká alakító függvény képes a szám után a mértékegységet is hozzá tenni a tömb sorszám sorozatához. Így simán lehet azt leírni, hogy „egy egész tizenkét század amper”.

Amikor vére működött a szavak leírása, és a példa programban megadott példákat helyesen tudta leírni a program (eeprom-ból szedve a szövegeket), akkor kapcsoltam be az mp3 lejátszóval a felolvasást. Nem lett jó elsőre az eredmény. Kiderült, hogy az mp3 állományok között volt egy hibás, amit nem tudott lejátszani, helyette a süket csend hangzott. Igyekeztem a szavakat a lehető legrövidebbre vágni a hanganyag készítésekor, és ez nem volt jó ötlet. Annyira szorosan vágtam az audió editoron látható hanghullámokat, hogy nem vettem észre, hogy egy kicsike lecsengést is levágtam a szavak végéről, és az elejéről is időnként. A beszéd így időnként nehezen volt érhető. Így megcsináltam újra az egészet, és egy kicsit több „üres” csendet hagytam a szavak elején és végén. Bár így összességében hosszabb idő a számok kimondása, de kellemesebb és jobban érthető a hangzás. Mondanom sem kell, hogy tized másodpercekről van szó. Egy-egy hanganyag készítés napokig tartott, mert csak egy órát tudtam vele foglalkozni esténként.

Íme a sokat emlegetett program forráskódja! Jó sok példát raktam bele, és alaposan felkommenteztem. A beszélő mérőműszer megalkotása még hónapokig fog tartani, így addigra mindent el fogok felejteni. Megéri dokumentálni!

/**************************************************************************************
 * MP3 lejátszóval számok (mértékegységgel) és dátum kimondását megvalósító program.  *
 * A program nem mondja ki a számokat, közvetlenül, de tartalmaz minden kimondáshoz   *
 * szükséges hang file definíciót (hang file-ok külön mellékelve), és tartalmaz egy   *
 * demót ami képes vezérelni a hang file részek lapján a folyamatot.                  *
 * Úgy lett megírva a file lejátszást vezérlő függvény, hogy közben más folyamtok is  *
 * futhatnak a loop()-ban. Egy suo[] nevű tömbben kell megadni a file sorszámokat,    *
 * beállítani egy index nevű változót 0-ra, valamint egy kezd nevű változót 1-re.     *
 * Ennek hatására elindul a file-ok lejátszása a tömb által meghatározott sorrendben. *
 * Amikor egy állomány le lett játszva, a függvény ezt észreveszi az MP3 lejátszó     *
 * folyamatos lekérdezésével, és elindítja a következő file lejátszását. Arra kell    *
 * vigyázni, hogy a loop()-ban ne legyen más nagyon hossz ideig futó programrész.     *
 * azaz a függvény meghívására, különben kihagyhatja egy-egy rész lejátszását.        *
 * A demó a 12,5V számot mondja ki 10 másodpercenként                                 *
 * MP3 lejátszó bekötése:                                                             *
 * Az MP+ lejátszó felülről nézve (led felül, kártya bedugás alulról) az IC-knél      *
 * megszokott körbejárási sorszámozási iránnyal a következő kivezetésekkel            *
 * rendelkezik a bal oldalon:                                                         *
 *   1 - VCC (5V, 4,2V ajánlott) Arduino-tól kapta a tápfeszt                         *
 *   2 - RX  Arduino 11-es kivezetéshez kötve egy 1Kohm ellenálláson keresztül        *
 *   3 - TX  Arduino 10-es kivezetéshez kötve                                         *
 *   4 - DAC_R fejhallgató kivezetés, ide kötöttem a fejhallgató egyik kivezetését    *
 *   5 - DAC_L fejhallgató kivezetés (ezt nem használtam)                             *
 *   6 - SPK_1 3W-os hangszóró kimenet (ezt nem használtam)                           *
 *   7 - GND föld pont, Arduino GND-vel összekötve valamint a fejhallgató             *
 *           másik kivezetése                                                         *
 *   8 - SPK_2 3W-os hangszóró kimenet (ezt sem használtam)                           *
 * Jobb oldalon csak a legfelsőt használtam, a busy jelvezetéket, ami 1-el jelzi      *
 * ha szabad az mp3, és éppen nem csinál semmit. Ha ez 0-ról 1-re vált, akkor         *
 * befejeződött egy lejátszás.                                                        *
 *                                                                                    *
 * A leírások az RX bemenet és az Arduino kivezetés közé egy soros ellenállást        *
 * javasolnak. Ellenállás nélkül is működik, de nagyon zajos a lejátszás, behallatszik*
 * a soros port zaja nagyon erősen, szinte elnyomja a szöveget. 1Kohm-al már sokkal   *
 * halkabb a zaj (élvezhető a hang), de még ott van a háttérben. Egyes leírásokban    *
 * 10Kohm-ot írtak, de azzal már nekem nem működött. 4,7K-val még igen, de a zaj      *
 * nem lett jelentősen kevesebb                                                       *
 * Átkötöttem az mp3 lejátszó VCC-jét egy másik tápegységre, ami szintén 5V de ez egy *
 * kapcsoló üzemű táp. A zaj nem változott, nem nőtt és nem csökkent.                 *
 * Megpróbáltam a TX vezetékre is tenni egy soros 1K ellenállást, ez ugyan nem lenne  *
 * logikus, ha csökkenne a zaj, nem is csökkent.                                      *
 * Átkötöttem a fülhallgatómat a 3W-os kimenetre egy 1K soros ellenállással, és így   *
 * már nem tapasztaltam zajt. Tökéletes volt a hang.                                  *
 * Levettem a hangerőt 1-re, és kivettem a soros ellenállást. Kicsit halkabb lett     *
 * így a hangerőt felemeltem 10-re, ez eredményezett hasonló hangerőt. A zaj újra     *
 * megjelent, bár alig hallhatóan                                                     *
 * Ezt követően levettem a fejemről a fejhallgatót és felemeltem a hangerőt 30-ra     *
 * így hangszórónak használtam a fejhallgatót. A zajt nem halottam, de a szöveg       *
 * szépen tisztán hallható lett. Az eredmény kielégítő. A fejhallgatóm kb 40ohm-os.   *
 *                                                                                    *
 * DFRobotDFPlayerMini legfontosabb függvényei:                                       *
 *  myDFPlayer.play(int fájlszám); - bármely könyvtárba másolt bármely fájlokat       *
 *                                    lejátsza de az állományok másolási sorrendjét   *
 *                                    veszi figyelembe!                               *
 *  myDFPlayer.playMp3Folder (int fájlszám) - az mp3 könyvtárban lévő fájlokat játssza*
 *                                    le, és a file első négy karaktere nullákkal     *
 *                                    feltöltött szám kell legyen (max. 10000db)      *
 *                                    pl:0012 a fájl név bármivel folytatódhat        *
 *  myDFPlayer.playFolder (int mappaszám, int fájlszám); - numerikus nevű mappában    *                                      
 *                                    lévő fájlokat játszik le. a mappa neve "01",    *
 *                                    "02" stb., a file első három karaktere nullákkal*
 *                                    feltöltött szám kell legyen (max. 1000db)       *
 *                                    pl:015 a fle név bármivel folytatódhat          *
 *  myDFPlayer.playLargeFolder (int mappaszám, int fájlszám); - ugyanaz mint előző    *
 *                                    vak a mappában 9999db file lehet.               *
 **************************************************************************************/
#include <EEPROM.h>               //a számok szöveges kiírásához kell csak, mert eepromban tároltam a szövegeket
#include "Arduino.h"
#include "SoftwareSerial.h"       //használunk egy szoftveres soros portot, hogy megmaradjon a valódi soros port a fejlesztéshez, hibakereséshez
#include "DFRobotDFPlayerMini.h"  //MP3 lejátszó programkönyvtára

SoftwareSerial mySoftwareSerial(10, 11); // RX, TX   //beállítjuk a szoftveres soros portot
DFRobotDFPlayerMini myDFPlayer;                      //myDFPlayer néven fogunk hivatkozni erre az mp3 lejátszó példányra

//a szövegek kimondását tartalmazó fájlokat egy SD kártya gyökerébe másoltam
//és mindegyik file neve egy sorszámmal kezdődik három jegyre feltöltve 0-ákkal pl. "016",
//bár a neveknek nincs jelentősége, mert a másolás sorendje dönt, ha a play() függvénnyel
//játsszuk le az állományokat.
//az alábbi változó értékeket csak a fejlesztéshez kellenek, hogy lássam, melyik számnak, mennyi az indexértéke:
//az alábbi változó értékeket is csak a program fejlesztésekor használtam, hogy lássam, melyik számnak, mennyi az indexértéke:
//nulla=1,egy=2,ketto=3,harom=4,negy=5,ot=6,hat=7,het=8,nyolc=9,kilenc=10,tiz=11,husz=12,harminc=13,negyven=14,otven=15,hatvan=16,hetven=17,
//nyolcvan=18,kilencven=19,szaz=20,ketszaz=21,haromszaz=22,negyszaz=23,otszaz=24,hatszaz=25,hetszaz=26,nyolcszaz=27,kielencszaz=28,elso=29,
//masodik=30,harmadik=31,negyedik=32,otodik=33,hatodik=34,hetedik=35,nyolcadik=36,kilencedik=37,tizedik=38,tizenegyedik=39,tizenkettedik=40,
//zero=41,tizen=42,huszon=43,plussz=44,minusz=45,ezer=46,millio=47,milliard=48,egesz=49,ezred=50,szazad=51,tized=52,hetfo=53,kedd=54,szerda=55,
//csutortok=56,pentek=57,szombat=58,vasarnap=59,januar=60,februar=61,marcius=62,aprilis=63,majus=64,junius=65,julius=66,augusztus=67,szeptember=68,
//oktober=69,november=70,december=71,volt=72,milivolt=73,amper=74,miliamper=75,ohm=76,hertz=77,kilohertz=78,celsiusfok=79,milibar=80,kilometer=81,
//kilogramm=82,gramm=83,ora=84,perc=85,masodperc=86,honap=87,nap=88,napja=89,valto=90,egyen=91,feszültség=92,áramerősség=93,ellenállás=94,szünet (0,3sec csend)=95

//ezekre a változókra sincs igazán szükség a programban. Számok, és dátumok használom az első ("egy", "tiz" vagy "tizen", és a "száz", "január")
//változók indexét, amit direktben is beírhattam volna a programba. Így viszont a program követhetőbb lett. Pl a "négy"-et úgy képzem, hogy "egy"+index-1. Ekkor a "egy" értéke
//az alábbi sorokban láthatóan 2, ehhez hozzáadok négyet (amit ki kellene mondani), ami 5, és még levonok egyet, mert így fog stimmelni.
//Arra természetesen figyeltem, hogy az egymást követő számok index értéke folyamtaosan növekvő sorszám legyen, azaz egymás után legyenek: pl.: egy=2, ketto=3, harom=4 stb.
const byte minusz=45,nulla=1,egy=2,ketto=3,tiz=11,husz=12,szaz=20,tizen=42,huszon=43,ezer=46,millio=47,milliard=48,egesz=49,ezred=50,szazad=51,tized=52;
const byte januar=60,elso=29,honap=87,hetfo=53,ora=84,perc=85;
//mértékegységek indexei, szintén az áttekinthetőség miatt léteznek ezek a változók
const byte volt=72,milivolt=73,amper=74,miliamper=75,ohm=76,hertz=77,kilohertz=78,celsiusfok=79,milibar=80,kilometer=81,kilogramm=82,gramm=83,szunet=95;

//csak a szövegek eepromból történő kiolvasásához használom, he nem kell szövegesen is leírni a számokat, akkor erre a struktúrára és a 
//a szoveg_alakit() és az eeprom_txt_olvas() függvényekre sincs szükség
struct eeprom {               //Ebben a struktúrában adja vissza az adatokat az eeprom_txt..() függvény függvény csopprt, kivéve az eeprom_txt_olvas(), mert az string-et ad vissza.
                              //Az eeprom_txt_ir() függvény egyik paramétere a az a méret, amit elfoglalhatunk az eeprom-ból.
                              //Ennek maxmiális értéke ATmega328 esetén 1024 byte, mert ekkor a az eeprom méret, de használhatunk belőle kevesebbet is.
  int eeprom_meret=1024;       //ebben a konstansban tartjuk az adatbázis kezelésre fenntartott eeprom terület maximális méretét
  int foglalt_hely;          //ebben a változóban kapjuk meg a foglalt helyek számát byte-ban
  byte rekord_szam;           //az eddig beírt szavak száma
  bool sikeres;               //értéke 1, ha sikeres volt a tárolás, vagy lekérdezés, és 0, ha nem tudta beírni a megadott szót
};


long szunet_tmp=millis();  //10 másodpercenként egyszer indítom el a kimondást, ehhez kellett

byte szo[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //ebben a tömbben gyűjtöm össze a kimondandó szó részek indexeit.
                           //Maximum 15 állományt fogunk tudni lejátszani, ha már nincs több lejátszandó, 
                           //akkor a következő tömbelem 0. Ha van 15 lejátszandó, akkor az index érték lesz
                           //15, és innen tudjuk, hogy le kell állítani a lejátszást
byte index=0;              //ez mondja meg, hogy éppen melyik állományt kell lejátszani. Ha értéke 15, akkor
                           //nincs lejátszás illetve már befelyeztük. 15-re állítjuk az utolsó lejátszott állomány esetén is.
                           //A lejátszás indításakor 0-ra kell állítani a kezd nevű változót pedig 1-re.
long lejatszas_ido;        //megmérem a demó szöveg kimondásának idejét is, ehhez használom
long varakozas=millis();   //amikor elindítom a lejátszást, a lejátszón a busy jelvezeték nem vált azonnal 1-re, illetve
                           //rövid ideig még változik is. Ezért várok egy kis időt, amíg figyelni kezdem. Ehhez kell!
byte pelda_index=1;        //a kimondott példákat ezzel a változóval választom ki, minden kimondáskor növelem az értékét, 
                           //és egy maximális érték elérésekor 1-re állítom
bool ferfi_hang=0;         //ha ennek értéke 1, akkor az mp3 által lejátszandó file indexéhez 95-öt hozzáadok, mert onnan kezdődnek ugyanazok a szavak, csak férfi hangon



void setup()
{
  pinMode(3,INPUT);        //ide kötöttem az mp3 lejátszó busy jelét
  
  mySoftwareSerial.begin(9600);               //inicializáljuk a szoftveres sorosportot
  Serial.begin(9600);                         //a hardveres sorosport megmaradt a program eseményeinek nyomon követésére   
  if (!myDFPlayer.begin(mySoftwareSerial)) {  //mp3 lejátszó inicializálása. Ha nem sikerült, akkor 0
    Serial.println("MP3 player hiba!");       //valami hiba volt, nincs jól csatlakoztatva az mp3 lejátszó a 10 és 11-es kimenetekhez, vagy nincs benne sd kártya
  }
  Serial.println("MP3 player OK!");           //mp3 lejátszó kész a működésre
  myDFPlayer.volume(19);                      //Hangerőt beállítjuk állítjuk (0-30)

  //annak bemutatása, hogyan lehet sorban áttekinthetően kimondatni a szavakat, de közben a vezérlő nem csinál semmi mást (delay()-t is használ
  //az összes szó kimondása férfi és női hangon. Csak ellenőrzésre használom, ha felmásoltam az összes file-et
  //így lehet megnézni, hogy miden szót jó index értéken talál-e meg az SD kártyán, Windows intézővel történő
  //másolás esetén előfordult, hogy rossz lett a sorrend, és nem a megfelelő szót mondta ki egy index értékhez
  //Unalmas végighallgatni mind a 95 file-t, ezért i a példában csak 5-ig növekszik, ha minden szót végig akarunk
  //hallgatni, akkor i<96 legyen a for-ban a feltétel.
  //kimondás előtt törlöm a szo[] tömböt, mert a szam_sz() függvényt most nem használom, így nem törlöm a artalmát, 
  //hanem direktben beleírok egy index értéket az első tömbelembe (nem kellene most sem törölni, csak ezzel jelzem, hogy kell ilyen szituban)
  for (byte i=0;i<15;i++) {szo[i]=0;}
  Serial.println("Női hangon az egyes mp3 állományok lejátszása az index érték alapján:");
  for (byte i=1;i<6;i++) {
    Serial.print(i);Serial.print("=");          
    szo[0]=i;                                   //ezt az indexű file-t akarjuk lejátszani
    szoveg_alakit();Serial.println();           //ez a függvény kiírja szöveggel a szo[] tömb elemeit, hogy lássuk mi történik
    beszel(0);                                   //ez a függvény felolvassa a szo[] tömb elemeit (amit a soros monitoron leírva is látunk), ez most női hangon szólel meg
    delay(100);
  }
  //ferfi hang
  Serial.println("Férfi hangon az egyes mp3 állományok lejátszása az index érték alapján:");
  for (byte i=1;i<6;i++) {
    Serial.print(i);Serial.print("=");          
    szo[0]=i;                                   //ezt az indexű file-t akarjuk lejátszani
    szoveg_alakit();Serial.println();           //ez a függvény kiírja szöveggel a szo[] tömb elemeit, hogy lássuk mi történik
    beszel(1);                                  //ez a függvény felolvassa a szo[] tömb elemeit (amit a soros monitoron leírva is látunk), ez most férfi hangon szólel meg
    delay(100);
  }
  
  Serial.println("Példák a beszel() függvény használatával:");
  Serial.println("Negatív és pozitív számok:");
  //női hang
  Serial.print("-12.25=");szam_szo(-1225,2,0,0);szoveg_alakit();Serial.println();
  beszel(0);
  Serial.print("-0.12=");szam_szo(-12,2,0,0);szoveg_alakit();Serial.println();
  beszel(0);
  Serial.print("0=");szam_szo(-0,0,0,0);szoveg_alakit();Serial.println();
  beszel(0);
  Serial.println("Időpont kimondása:");
  Serial.print("12.25=");ido_szo(12,25);szoveg_alakit();Serial.println();
  beszel(0);
  Serial.println("Dátum kimondása:");
  Serial.print("2021.06.20=");datum_szo(2021,6,20,0,0);szoveg_alakit();Serial.println();
  beszel(0);
  
  Serial.println("Példák a rövidítés használatára:");
  Serial.print("0.1=");szam_szo(1,1,0,0);szoveg_alakit();Serial.println();
  beszel(0);
  Serial.print("0.1 rövidítve=");szam_szo(1,1,0,1);szoveg_alakit();Serial.println();
  beszel(0);
  Serial.print("0.01=");szam_szo(1,2,0,0);szoveg_alakit();Serial.println();
  beszel(0);
  Serial.print("0.01 rövidítve=");szam_szo(1,2,0,1);szoveg_alakit();Serial.println();
  beszel(0);
  Serial.print("0.12=");szam_szo(12,2,0,0);szoveg_alakit();Serial.println();
  beszel(0);
  Serial.print("0.12 rövidítve=");szam_szo(12,2,0,1);szoveg_alakit();Serial.println();
  beszel(0);
  Serial.print("0.001=");szam_szo(1,3,0,0);szoveg_alakit();Serial.println();
  beszel(0);
  Serial.print("0.001 rövidítve=");szam_szo(1,3,0,1);szoveg_alakit();Serial.println();
  beszel(0);
  Serial.print("0.301=");szam_szo(301,3,0,0);szoveg_alakit();Serial.println();
  beszel(0);
  Serial.print("0.301 rövidítve=");szam_szo(301,3,0,1);szoveg_alakit();Serial.println();
  beszel(0);

  //a loop()-ban példát látunk arra, hogyan lehet úgy felolvasni a szövegekből összerakott számot
  //dátumot és időpontot, hogy közben a vezérlő más feladatokkal is foglalkozhat, mert a loop() folyamatosan fut. 
  //Ekkor a felolvasás akkor kezdődik, ha az index változót nullára állítjuk, és akkor van vége, ha értéke 15.
  index=15;                                    //nem szeretnénk, ha rögtön elkezdne beszélni, ez a nyugalmi állapot
}

void loop()
//ebben a demo programban ciklikusan mondatok ki és írok le számokat
//minden kimondás végén egy új számot veszek elő, amit ki kell mondani.
{
  if (millis()>szunet_tmp+2000) {   //2 másodperccel az után, hogy kimondtuk az utolsó szöveget, indítjuk a következő kimondását 
    index=0;  

    //nyugalmi állapotban ez 15, 0-val jelezzük, hogy az első állomány felolvasásával indul a folyamat
    switch (pelda_index) {
      case 1:
        ferfi_hang=0;
        Serial.print("12.25=");ido_szo(12,25);szoveg_alakit();Serial.println();pelda_index++;break;
      case 2:
        ferfi_hang=1;
        Serial.print("2021. június 04 péntek=");datum_szo(2021,6,4,1,5);szoveg_alakit();Serial.println();pelda_index++;break;
      case 3:
        ferfi_hang=0;
        Serial.print("2021.06.04 péntek=");datum_szo(2021,6,4,0,5);szoveg_alakit();Serial.println();pelda_index++;break;
      case 4:
        Serial.print("2021.06.04=");datum_szo(2021,6,4,0,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 5:
        Serial.print("0=");szam_szo(0,3,0,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 6:
        Serial.print("0.001=");szam_szo(1,3,0,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 7:
        Serial.print("0.01=");szam_szo(1,2,0,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 8:
        Serial.print("0.1=");szam_szo(1,1,0,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 9:
        Serial.print("-0.23=");szam_szo(-23,2,0,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 10:
        Serial.print("0.513=");szam_szo(513,3,0,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 11:
        ferfi_hang=1;
        Serial.print("0.702=");szam_szo(702,3,0,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 12:
        Serial.print("0.017=");szam_szo(17,3,0,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 13:
        Serial.print("1.23=");szam_szo(123,2,0,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 14:
        Serial.print("1.76=");szam_szo(176,2,0,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 15:
        Serial.print("12=");szam_szo(12,0,10,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 16:
        Serial.print("25=");szam_szo(25,0,9,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 17:
        Serial.print("39=");szam_szo(39,0,8,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 18:
        Serial.print("239=");szam_szo(239,0,kilohertz,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 19:
        Serial.print("1.239=");szam_szo(1239,0,7,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 20:
        Serial.print("17.324=");szam_szo(17324,0,6,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 21:
        Serial.print("122.609=");szam_szo(122609,0,5,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 22:
        Serial.print("3.022.609=");szam_szo(3102609,0,4,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 23:
        Serial.print("17.222.250=");szam_szo(17222250,0,0,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 24:
        Serial.print("221.999.111=");szam_szo(221999111,0,3,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 25:
        Serial.print("1.221.999.111=");szam_szo(1221999111,0,2,0);szoveg_alakit();Serial.println();pelda_index++;break;
      case 26:
        Serial.print("1.221.999,111=");szam_szo(1221999111,3,1,0);szoveg_alakit();Serial.println();pelda_index=1;break;
    }
  } 
  //Serial.println(index);                                                 

  //az itt következő programrészek a szo[] tömbben összeállított szavak végig mondását végzik el
  //amikor az index változót 0-ra állítjuk, akkor indul a szöveg kimondása. Közben a ciklus fut 
  //tovább, csak akkor vesz el egy kis időt a következő szöveg elindítása az mp3 lejátszón, amikor 
  //éppen befejeződött az előző szöveg lejátszása 
  if (digitalRead(3)==1 and index<15 and millis()>varakozas+200) {   //éppen szabad az mp3 lejátszó, lehet neki parancsot küldeni, ha van mit lejátszani és
                                                                     //eltelt legalább 200msec az utolsó indítás óta, mert a busy jelnek be kell állni 0-ra
                                                                     //a lejátszás indítása után, hogy figyelhessem mikor fejezte be a lejátszást (1-re vált)
                                                                     //Kezdetben 100msec várakozást használtam, de egyes szavak kimondását kihagyta, így 200msec-re
                                                                     //kelet növelni a várakozási időt. 
    szunet_tmp=millis(); //elindítunk egy kis várakozást, mielőtt elkezdnénk kimondani a következő szöveg kimondását
    if (index==0) {lejatszas_ido=millis();}           //elindítjuk az időmérést
    varakozas=millis();                               //elindítjuk a várakozást, amivel várunk egy kicsit mielőtt a busy jelet ellenőriznénk (tranziensek 
                                                      //vannak a lejátszás elindítása után egy ideig a busy jelen)
    //így kell elindítani a lejátszást, ha a hangfile-okat az mp3 nevű mappába rakjuk, és a neve alapján fogja a lejátszó kikeresni                                                  
    //if (ferfi_hang==1) {myDFPlayer.playMp3Folder(szo[index]+89);} //elindítjuk a soron következő rész lejátszását
    //else {myDFPlayer.playMp3Folder(szo[index]);}
        
    if (ferfi_hang==1) {myDFPlayer.play(szo[index]+95);}
    else {myDFPlayer.play(szo[index]);}
    index++;
    if (szo[index]==0) {                              //ha az index értékhez tartozó tömbelem 0, akkor azt jelenti, hogy vége a lejátszható részleteknek
      index=15;                                       //index=15 jelzi, hogy éppen nincs lejátszani való
      Serial.print("Lejátszási idő:");Serial.println(millis()-lejatszas_ido);
    }
  }                     
                                     
}

void beszel(bool ferfi) 
/*************************************************************************************************
 * Ez a függvény mp3 lejátszóval kimondja a szo[] tömbben összekészített szövegkódok alapján
 * a számokat, dátumokat, időpontokat. A függvény a kimondás ideje alatt semmilyen más
 * müveletet nem tesz lehetővé, mert csak azmp3 lejátszó vezérélésével foglalkozik.
 * Akkor lehet hasznos, ha az mp3 lejátszót "egybeépítjük" egy ATmega vezérlővel és 
 * pl. I2C biszon keresztül megmondjuk, hogy mit kell kimondania. Ebbe az irányba 
 * fogom fejleszteni a későbbiekben. A loop() átvesz egy számot, dátumot vagy időpontot
 * és önálló egységként kimondja a megkapott értéket. Ekkor nem probléma, ha közben 
 * a vezérlő semmi mással nem foglalkozik. Akkor is hasznos, ha olyan a program felépítése
 * hogy nem probléma, ha a kimondás ideje alatt nem csinál semmi mást a program
 * Ha a bemenő paraméter 1, akkor a file index-hez 95-öt hozzáad, hogy férfi hang file-okat
 * játssza le az mp3 lejátszó.
 *************************************************************************************************/
 {
  index=0;
  do {
    if (digitalRead(3)==1) {                          //éppen szabad az mp3 lejátszó, lehet neki parancsot küldeni
      if (ferfi==0) {myDFPlayer.play(szo[index]);}    //elindítjuk a lejátszást a női hangokkal (1-95 index érték)
      else {myDFPlayer.play(szo[index]+95);}          //elindítjuk a lejátszást a férfi hangokkal (96-190 index érték)
      delay(200);                      //200msec-et várakozunk, hogy a busy jel beálljon, különben a busy kezdeti ugrálása miatt, 
                                       //kiküldenék lejátszóra a következő állományt is, pedig még nem fejeződött be az aktuális.
                                       //Ebben a függvényben 100msec várakozás is elég volt ugyanazoknak a számoknak a hibátlan
                                       //kimondásához, amit a loop()-ban használt megoldás csak 200msec várakozással tudott kimondani.
                                       //Ezért 100msec-is elegendő, de inkább megnöveltem a várakozást itt is.
      index++;                         //Ha legközelebb szabad lesz az mp3 lejátszó, akkor a következőt kell kimondani (kivéve, ha szo[index]=0) 
    }
  } while (szo[index]!=0);             //ha a következő tömbelem 0, akkor vége a lejátszásnak, mert kiugrunk a ciklusból
  
  //meg kell várnunk az utolsó file lejátszásának végét is, mielőtt visszatérünk a függvényből.
  //Erre nem feltétlenül van szükség, közben már futhatna tovább a program, de nekem így áttekinthetőbb
  //volt a működés.
  do {                                 //ezzel a ciklussal várjuk az utolsú állomány lejátszásának végét
    if (digitalRead(3)==1) {           //vége a lejátszásnak
      break;
    }
  } while (true);             
}

void ido_szo(int xora, byte xperc)
{
/********************************************************************************************************************
 * Ez a függvény a szo[] tömbbe helyezi az idő kimondásához szükséges szavak index értékét kimondáshoz 
 * szükséges sorrendben.
 * Paraméterek:
 * ora        - az időpont órája
 * perc       - az időpont perce
 *******************************************************************************************************************/
  for (byte i=0;i<15;i++) {szo[i]=0;}
  byte index=0;
  byte h_tiz;
  byte h_egy;
  //óra ************************************
  h_tiz=xora/10;
  h_egy=xora-((long)h_tiz*10);
  if ((h_tiz==1) & (h_egy==0)) {szo[index]=tiz;index++;}
  if ((h_tiz==1) & (h_egy>0)) {szo[index]=tizen;index++;}
  if ((h_tiz==2) & (h_egy==0)) {szo[index]=husz;index++;}
  if ((h_tiz==2) & (h_egy>0)) {szo[index]=huszon;index++;}
  if (h_tiz>2) {szo[index]=tiz+h_tiz-1;index++;}
  if (h_egy>0) {szo[index]=egy+h_egy-1;index++;}
  szo[index]=ora;index++;
  szo[index]=szunet;index++;    //az óra kimondása után beteszünk 0,2sec várakozást a szöveg tagolása érdekében
  //perc*************************************************
  h_tiz=xperc/10;
  h_egy=xperc-((int)h_tiz*10);
  if ((h_tiz==1) & (h_egy==0)) {szo[index]=tiz;index++;}
  if ((h_tiz==1) & (h_egy>0)) {szo[index]=tizen;index++;}
  if ((h_tiz==2) & (h_egy==0)) {szo[index]=husz;index++;}
  if ((h_tiz==2) & (h_egy>0)) {szo[index]=huszon;index++;}
  if (h_tiz>2) {szo[index]=tiz+h_tiz-1;index++;}
  if (h_egy>0) {szo[index]=egy+h_egy-1;index++;}
  szo[index]=perc;index++;
  //het napja*******************************************
}

void datum_szo(int ev, byte ho, byte nap, bool ho_nev, byte het_napja)
{
/********************************************************************************************************************
 * Ez a függvény a szo[] tömbbe helyezi a dátum kimondásához szükséges szavak index értékét kimondáshoz 
 * szükséges sorrendben.
 * Paraméterek:
 * ev        - a dátumban megadott év, évszázadokkal együtt (pl. 2021)
 * ho        - a dátum hónapja
 * nap       - a dátum napja
 * ho_nev    - ha értéke 1, akkor a hónapokat a nevükkel mondja ki pl. "február", ha értéke 0, akkor a hónap 
 *             sorszámával pl. "második hó"
 * het_napja - ha 0, akkor nem kerül kimondásra a hét napja, ha értéke 1-7 közötti, akkor a dátum végén 
 *             kimondja a hét napjának nevét is pl. ha 1, akkor "hétfő"
 *******************************************************************************************************************/
  for (byte i=0;i<15;i++) {szo[i]=0;}
  byte index=0;
  byte h_egyezer;
  byte h_egyszaz;
  byte h_tiz;
  byte h_egy;
  int maradek;
  //év ************************************
  h_egyezer=ev/1000;
  maradek=ev-((long)h_egyezer*1000);
  h_egyszaz=maradek/100;
  maradek=maradek-((long)h_egyszaz*100);
  h_tiz=maradek/10;
  h_egy=maradek-((long)h_tiz*10);
  //ezer  
  if (h_egyezer>1) {szo[index]=egy+h_egyezer-1;index++;szo[index]=ezer;index++;}
  if (h_egyezer==1) {szo[index]=ezer;index++;}
  //ezernél kissebb rész  
  if (h_egyszaz!=0) {szo[index]=szaz+h_egyszaz-1;index++;}
  if ((h_tiz==1) & (h_egy==0)) {szo[index]=tiz;index++;}
  if ((h_tiz==1) & (h_egy>0)) {szo[index]=tizen;index++;}
  if ((h_tiz==2) & (h_egy==0)) {szo[index]=husz;index++;}
  if ((h_tiz==2) & (h_egy>0)) {szo[index]=huszon;index++;}
  if (h_tiz>2) {szo[index]=tiz+h_tiz-1;index++;}
  if (h_egy>0) {szo[index]=egy+h_egy-1;index++;}
  if (ev==0) {szo[index]=nulla;index++;}   //ha a kimondandó szám 0, akkor legalább egy 0-át mondani kell
  szo[index]=szunet;index++;    //az év kimondása után beteszünk 0,2sec várakozást a szöveg tagolása érdekében
  //hónap ***************************************
  h_tiz=ho/10;
  h_egy=maradek-((long)h_tiz*10);
  if (ho_nev==1) {
    //hónap nevekkel kimondva
    szo[index]=januar+ho-1;index++;
  }
  else {
    //hónap sorszámmal kimondva
    szo[index]=elso+ho-1;index++;
    szo[index]=honap;index++;
  }
//  szo[index]=szunet;index++;    //a hónap kimondása után beteszünk 0,2sec várakozást a szöveg tagolása érdekében
  //nap*************************************************
  h_tiz=nap/10;
  h_egy=nap-((int)h_tiz*10);
  if ((h_tiz==1) & (h_egy==0)) {szo[index]=tiz;index++;}
  if ((h_tiz==1) & (h_egy>0)) {szo[index]=tizen;index++;}
  if ((h_tiz==2) & (h_egy==0)) {szo[index]=husz;index++;}
  if ((h_tiz==2) & (h_egy>0)) {szo[index]=huszon;index++;}
  if (h_tiz>2) {szo[index]=tiz+h_tiz-1;index++;}
  if (h_egy>0) {szo[index]=egy+h_egy-1;index++;}
  //het napja*******************************************
  if (het_napja>0) {
    szo[index]=hetfo+het_napja-1;index++;
  }  
}

void szam_szo(long szam, byte tizedes, byte mertekegyseg,bool rovidit) {
/********************************************************************************************************************
* Ez a függvény a bemenő paraméterben megadott long típusú számot alakítja át a szo[] nevű tömbbe. A tömbbe a       *
* kimondáshoz szükséges sorrendben elhelyezi a szükséges szavak index értékét                                       *
* Paraméterek:                                                                                                      *
* szam         - a kimondásra kerülő szám egész értékre alakítva                                                    *
* tizedes      - a kimondásra kerülő szám utolsó jegyeiből mennyit kell tizedes értéknek értelmezni                 *
* mertekegyseg - a kimondott szám mögé egy mértékegységet is lehet kimondatni ezzel a paraméterrel. Értékei:        *
*       0 - nem mond semmilyen mértékegységet                                                                       *                                   
*       1 - volt                                                                                                    *
*       2 - millivolt                                                                                               *
*       3 - amper                                                                                                   *
*       4 - milliamper                                                                                              *
*       5 - ohm                                                                                                     *
*       6 - hertz                                                                                                   *
*       7 - kilohertz                                                                                               *
*       8 - Celsius fok                                                                                             *
*       9 - milibar                                                                                                 *
*       10 - kilométer                                                                                              *
*       11 - kilogramm                                                                                              *
*       12 - gramm                                                                                                  *
* rovidit    - a tizedes érték végéről kihagyja a "tized", "század" vagy "ezred" szó kimondását, ha ez lehetséges   *
* A long bemenő szám utáni paraméter a tizedesjegyek számát adja meg, ha az átadott számot nem egész számként kell  *
* értelmezni. Ha tizedes jegyeket is akarunk kimondani, akkor adjuk meg a tizedes jegyek számát, és szorozzuk meg   *
* az eredeti számot 10,100 vagy 1000-el (amennyi tizedes jegyet szeretnénk.                                         *
* A kimondható egész szám maximuma: 2.147.483.647, negatív szám esetén:-2.147.483.648                               *
* egy tizedes jegy esetén a kimondható szám maximuma:214.748.364,7, negatív száma esetén:-214.748.364,8             *
* két tizedes jegy esetén a kiomndható szám maximuma:21.474.836,47, negatív száma esetén:-21.474.836,48             *
* három tizedes jegy esetén a kimondható szám maximuma:2.147.483,647, negatív száma esetén:-2.147.483,648           *
*********************************************************************************************************************/
  for (byte i=0;i<15;i++) {szo[i]=0;}
  byte index=0;
  byte h_egymilliard;
  byte h_szazmillio;
  byte h_tizmillio;
  byte h_egymillio;
  byte h_szazezer;
  byte h_tizezer;
  byte h_egyezer;
  byte h_egyszaz;
  byte h_tiz;
  byte h_egy;
  byte h_tized;
  byte h_szazad;
  byte h_ezred;
  if (szam<0) {szo[index]=minusz;index++; szam=szam*-1;}
  unsigned long maradek;
  switch (tizedes) {
    case 0:                                            //a tizedesjegyek száma 0
      h_egymilliard=szam/1000000000;
      maradek=szam-((long)h_egymilliard*1000000000);
      h_szazmillio=maradek/100000000;
      maradek=maradek-((long)h_szazmillio*100000000);
      h_tizmillio=maradek/10000000;
      maradek=maradek-((long)h_tizmillio*10000000);
      h_egymillio=maradek/1000000;
      maradek=maradek-((long)h_egymillio*1000000);
      h_szazezer=maradek/100000;
      maradek=maradek-((long)h_szazezer*100000);
      h_tizezer=maradek/10000;
      maradek=maradek-((long)h_tizezer*10000);
      h_egyezer=maradek/1000;
      maradek=maradek-((long)h_egyezer*1000);
      h_egyszaz=maradek/100;
      maradek=maradek-((long)h_egyszaz*100);
      h_tiz=maradek/10;
      h_egy=maradek-((long)h_tiz*10);
      h_tized=0;h_szazad=0;h_ezred=0;
      break;
    case 1:                                           //a tizedesjegyek száma 1
      h_egymilliard=0;
      h_szazmillio=szam/1000000000;
      maradek=szam-((long)h_szazmillio*1000000000);
      h_tizmillio=maradek/100000000;
      maradek=maradek-((long)h_tizmillio*100000000);
      h_egymillio=maradek/10000000;
      maradek=maradek-((long)h_egymillio*10000000);
      h_szazezer=maradek/1000000;
      maradek=maradek-((long)h_szazezer*1000000);
      h_tizezer=maradek/100000;
      maradek=maradek-((long)h_tizezer*100000);
      h_egyezer=maradek/10000;
      maradek=maradek-((long)h_egyezer*10000);
      h_egyszaz=maradek/1000;
      maradek=maradek-((long)h_egyszaz*1000);
      h_tiz=maradek/100;
      maradek=maradek-((long)h_tiz*100);
      h_egy=maradek/10;
      h_tized=maradek-((long)h_egy*10);
      h_szazad=0;h_ezred=0;
      break;
    case 2:                                           //a tizedesjegyek száma 2
      h_egymilliard=0;
      h_szazmillio=0;
      h_tizmillio=szam/1000000000;
      maradek=szam-((long)h_tizmillio*1000000000);
      h_egymillio=maradek/100000000;
      maradek=maradek-((long)h_egymillio*100000000);
      h_szazezer=maradek/10000000;
      maradek=maradek-((long)h_szazezer*10000000);
      h_tizezer=maradek/1000000;
      maradek=maradek-((long)h_tizezer*1000000);
      h_egyezer=maradek/100000;
      maradek=maradek-((long)h_egyezer*100000);
      h_egyszaz=maradek/10000;
      maradek=maradek-((long)h_egyszaz*10000);
      h_tiz=maradek/1000;
      maradek=maradek-((long)h_tiz*1000);
      h_egy=maradek/100;
      maradek=maradek-((long)h_egy*100);
      h_tized=maradek/10;
      h_szazad=maradek-((long)h_tized*10);
      h_ezred=0;
      break;
    case 3:                                        //a tizedesjegyek száma 3
      h_egymilliard=0;
      h_szazmillio=0;
      h_tizmillio=0;
      h_egymillio=szam/1000000000;
      maradek=szam-((long)h_egymillio*1000000000);
      h_szazezer=maradek/100000000;
      maradek=maradek-((long)h_szazezer*100000000);
      h_tizezer=maradek/10000000;
      maradek=maradek-((long)h_tizezer*10000000);
      h_egyezer=maradek/1000000;
      maradek=maradek-((long)h_egyezer*1000000);
      h_egyszaz=maradek/100000;
      maradek=maradek-((long)h_egyszaz*100000);
      h_tiz=maradek/10000;
      maradek=maradek-((long)h_tiz*10000);
      h_egy=maradek/1000;
      maradek=maradek-((long)h_egy*1000);
      h_tized=maradek/100;
      maradek=maradek-((long)h_tized*100);
      h_szazad=maradek/10;
      h_ezred=maradek-((long)h_szazad*10);
      break;
  }

  //milliárd 
  if (h_egymilliard!=0) {
    if (h_egymilliard==1) {szo[index]=egy;}
    else {szo[index]=ketto;}
    index++;szo[index]=milliard;index++;
  }

  //millió
  if (h_szazmillio!=0) {szo[index]=szaz+h_szazmillio-1;index++;}
  if ((h_tizmillio==1) & (h_egymillio==0)) {szo[index]=tiz;index++;}
  if ((h_tizmillio==1) & (h_egymillio>0)) {szo[index]=tizen;index++;}
  if ((h_tizmillio==2) & (h_egymillio==0)) {szo[index]=husz;index++;}
  if ((h_tizmillio==2) & (h_egymillio>0)) {szo[index]=huszon;index++;}
  if (h_tizmillio>2) {szo[index]=tiz+h_tizmillio-1;index++;}
  if (h_egymillio>0) {szo[index]=egy+h_egymillio-1;index++;}
  if (h_szazmillio>0 | h_tizmillio>0 | h_egymillio>0) {szo[index]=millio;index++;}

  //ezer  
  if (h_szazezer!=0) {szo[index]=szaz+h_szazezer-1;index++;}
  if ((h_tizezer==1) & (h_egyezer==0)) {szo[index]=tiz;index++;}
  if ((h_tizezer==1) & (h_egyezer>0)) {szo[index]=tizen;index++;}
  if ((h_tizezer==2) & (h_egyezer==0)) {szo[index]=husz;index++;}
  if ((h_tizezer==2) & (h_egyezer>0)) {szo[index]=huszon;index++;}
  if (h_tizezer>2) {szo[index]=tiz+h_tizezer-1;index++;}
  if (h_egyezer>0) {szo[index]=egy+h_egyezer-1;index++;}
  if (h_szazezer>0 | h_tizezer>0 | h_egyezer>0) {szo[index]=ezer;index++;}

  //ezernél kissebb rész  
  if (h_egyszaz!=0) {szo[index]=szaz+h_egyszaz-1;index++;}
  if ((h_tiz==1) & (h_egy==0)) {szo[index]=tiz;index++;}
  if ((h_tiz==1) & (h_egy>0)) {szo[index]=tizen;index++;}
  if ((h_tiz==2) & (h_egy==0)) {szo[index]=husz;index++;}
  if ((h_tiz==2) & (h_egy>0)) {szo[index]=huszon;index++;}
  if (h_tiz>2) {szo[index]=tiz+h_tiz-1;index++;}
  if (h_egy>0) {szo[index]=egy+h_egy-1;index++;}
  if (szam==0) {szo[index]=nulla;index++;}   //ha a kimondandó szám 0, akkor legalább egy 0-át mondani kell
  if ((h_egymilliard+h_szazmillio+h_tizmillio+h_egymillio+h_szazezer+h_tizezer+h_egyezer+h_egyszaz+h_tiz+h_egy)==0 & (h_tized+h_szazad+h_ezred)>0) {szo[index]=nulla;index++;} //ha ez egész érték nulla, és a tizedes érték nem nulla

  //nullánál kissebb rész 
  if ((h_tized>0) | (h_szazad>0) | (h_ezred>0)) {szo[index]=egesz;index++;}  //betesszük az egész szót, ha vannak tizedes értékek
  if (h_tized!=0 & h_szazad==0 & h_ezred==0) {  //van tized, de nincs század és ezred, akkor egyjegyű számot kell kimondani, végént a tized szóval
    szo[index]=egy+h_tized-1;index++;
    if (rovidit==0) {szo[index]=tized;index++;} //csak ha nem kell rövidíteni a kimondást, akkor mondjuk ki a "tized" szót
  }
  if (h_tized==0 & h_szazad!=0 & h_ezred==0) {  //van század, de nincs tized és ezred, akkor egyjegyű számot kell kimondani, végént a század szóval
    szo[index]=egy+h_szazad-1;index++;
    szo[index]=szazad;index++;                  //nem lehet rövidíteni, mert nincs tizedes érték
  }
  if (h_tized==0 & h_szazad==0 & h_ezred!=0) {  //van ezred, de nincs tized és század, akkor egyjegyű számot kell kimondani, végént a ezred szóval
    szo[index]=egy+h_ezred-1;index++;
    szo[index]=ezred;index++;                   //nem lehet rövidíteni, mert nincs tized és század érték
  }
  if (h_tized!=0 & h_szazad!=0 & h_ezred==0) {  //van tized és század és nincs ezred, kétjegyű számot kell kimondani, végén a század szóval
    if (h_tized==1) { szo[index]=tizen;index++;}
    if (h_tized==2) { szo[index]=huszon;index++;}
    if (h_tized>2) {  szo[index]=tiz+h_tized-1;index++;}
    szo[index]=egy+h_szazad-1;index++;
    if (rovidit==0) {szo[index]=szazad;index++;} //csak ha nem kell rövidíteni a kimondást, akkor mondjuk ki a "század" szót
  }
  if (h_tized!=0 & h_szazad==0 & h_ezred!=0) {  //van tized, és van ezred, de nincs század, akkor háromjegyű számot kell kimondani, végént a ezred szóval
    szo[index]=szaz+h_tized-1;index++;
    szo[index]=egy+h_ezred-1;index++;
    if (rovidit==0) {szo[index]=ezred;index++;} //csak ha nem kell rövidíteni a kimondást, akkor mondjuk ki a "ezred" szót
  }
  if (h_tized==0 & h_szazad!=0 & h_ezred!=0) {  //nincs tized, de van század és van ezred, kétjegyű számot kell kimondani, végén a ezred szóval
    if (h_szazad==1 ) { szo[index]=tizen;index++;}
    if (h_szazad==2) {  szo[index]=huszon;index++;}
    if (h_szazad>2) { szo[index]=tiz+h_szazad-1;index++;}
    szo[index]=egy+h_ezred-1;index++;
    szo[index]=ezred;index++;                   //nem lehet rövidíteni, mert nincs tized érték
  }

  if (h_tized!=0 & h_szazad!=0 & h_ezred!=0) {  //van tized, van század és van ezred, háromjegyű számot kell kimondani, végén a ezred szóval
    szo[index]=szaz+h_tized-1;index++;
    if (h_szazad==1) {szo[index]=tizen;index++;}
    if (h_szazad==2) {szo[index]=huszon;index++;}
    if (h_szazad>2) {szo[index]=tiz+h_tiz-1;index++;}
    szo[index]=egy+h_ezred-1;index++;
    if (rovidit==0) {szo[index]=ezred;index++;} //csak ha nem kell rövidíteni a kimondást, akkor mondjuk ki a "ezred" szót
  }
  //volt=72,milivolt=73,amper=74,miliamper=75,ohm=76,hertz=77,kilohertz=78,celsiusfok=79,milibar=80,kilometer=81,kilogramm=82,gramm=83,szunet=95;
  switch (mertekegyseg){
    case 1: szo[index]=volt;break;
    case 2: szo[index]=milivolt;break;
    case 3: szo[index]=amper;break;
    case 4: szo[index]=miliamper;break;
    case 5: szo[index]=ohm;break;
    case 6: szo[index]=hertz;break;
    case 7: szo[index]=kilohertz;break;
    case 8: szo[index]=celsiusfok;break;
    case 9: szo[index]=milibar;break;
    case 10: szo[index]=kilometer;break;
    case 11: szo[index]=kilogramm;break;
    case 12: szo[index]=gramm;break;
  }
}

String eeprom_txt_olvas(int index) 
/*********************************************************************************************
 * Ez a függvény visszaadja a paraméterben megkapott sorszámon tárolt szöveget. A szöveget   *
 * az eeprom megfelelő pontjainak végig olvasásával keresi meg. Minél nagyobb a paraméterben *
 * magadott sorszám, annál lassabban találja meg a keresett szöveget. Ha nagyobb sorszámot   *           
 * adunk meg paraméterként mint a szövegek darabszáma, akkor üres stringet ad vissza         *
 *********************************************************************************************/
{
  eeprom adat;
  String szo="";                       //Ebbe a változóba olvassuk vissza szöveget, ha megtaláltuk
  int akt_cim=0;                       //Ebben a változóban tároljuk az aktuális olvasási címet
  int akt_index=0;                     //Ebben a változóban számoljuk a rekordokat, folyamatosan növekszik.
                                       //Ha értéke megegyezik a paraméterben megkapott index-el (sorszámmal)
                                       //akkor megtaláltuk a szöveget
  byte ertek;                          //Ebbe olvassuk be a szövegek hosszának étékét a rekordok legelejéről
  do  {
    ertek=EEPROM.read(akt_cim);          //Kiolvassuk a szöveg hosszát
    if (ertek==0) {return("");}          //ha ez nulla, akkor az utolsó rekordhoz értünk, és üres stringet adunk vissza.
                                         //Ez csak akkor fordulhat elő, he a paraméterben megadott sorszám nagyobb mint a rekordok száma.
    akt_index++;                         //Növeljük a darabszámot, mert érvényes szót találtunk
    if (akt_index==index) {              //Ha a paraméterben megkapott darabszám megegyezik az aktuális szöveg sorszámával, akkor ezt kerestük
    for (byte i=0;i<ertek;i++) {         //A kezdő byte-on megadott karakterszámot olvassuk végig
        szo=szo+" ";                     //Növeljük a szo nevű string változó hosszát azzal, hogy hozzáadunk egy karaktert
        szo[i]=EEPROM.read(akt_cim+1+i); //A szo nevű stringünk i-dik karakterére úgy tudunk hivatkozni, mintha egy tömb lenne, és így rögtön bele is tudjuk 
                                         //olvasni a tárolt számot, ami a karakter kódja
      }
      return(szo);                       //Az utolsó byte kiolvasása után visszatérünk a string-el 
    }
    akt_cim=akt_cim+ertek+1;             //Ha sorszám még kisebb volt mint a paraméterben kapott kiolvasandó sorszám, akkor kiszámítjuk a következő szó kezdő címét
  } while (akt_cim<adat.eeprom_meret);        //Akkor is kiugrunk a ciklusból, ha a tárolásra rendelkezésre álló eeprom terület végére értünk. Ez akkor fordulhat elő,
                                         //ha olyan nagy értéket adtunk meg a paraméterben, ami nagyobb mint a tárolt szavak száma, és az eeprom is tele van.
  return("");                            //Visszatérési érték, ha nem találtuk meg a szöveget.
}

void szoveg_alakit() {
  for (byte i=0;szo[i]>0;i++) {         //addig megyünk, amíg a szo[] tömbben 0-nál nagyobb szám van (van mit kimondani, vagy leírni szeretnénk)
    Serial.print(szo[i]);               //az a kimondani vagy leírni szánt szó kódja
    Serial.print(",");                  //minden kód után egy vesszőt teszünk
  }
  Serial.print(" -> ");
  for (byte i=0;szo[i]>0;i++) {         //addig megyünk, amíg a szo[] tömbben 0-nál nagyobb szám van (van mit kimondani, vagy leírni szeretnénk)
    Serial.print(".");                  //minden szó elé "."-t teszek elválasztónak
    if (szo[i]>95) {Serial.print(eeprom_txt_olvas(szo[i]-95));}       //ez a kóddal kiválasztott szó leírva, de férfi hangnak megfelelő index-el
    else {Serial.print(eeprom_txt_olvas(szo[i]));}                    //ez a kóddal kiválasztott szó leírva, de női hangnak megfelelő index-el
  }
}

Íme egy minta a futási eredményből, ami a soros porton kijön! Ebből látható, hogyan működik a szövegesítés a kimondással párhuzamosan. A kimondási idők mérése nem lett tökéletes, mert az utolsó kimondott szót nem méri hozzá helyesen, de többet már nem küzdöttem vele. Az viszont látható, hogy egyes számadatok kimondása idegesítően hosszú. Viszont amikor hadarva kimondtam magamnak ugyanazokat a számokat, bizony nekem sem ment sokkal gyorsabban.

MP3 player OK!
Női hangon az egyes mp3 állományok lehátszása az index érték alapján:
1=1, -> .nulla
2=2, -> .egy
3=3, -> .kettő
4=4, -> .három
5=5, -> .négy
Férfi hangon az egyes mp3 állományok lehátszása az index érték alapján:
1=1, -> .nulla
2=2, -> .egy
3=3, -> .kettő
4=4, -> .három
5=5, -> .négy
Példák a beszel() függvény használatával:
Negatív és pozitív számok:
-12.25=45,42,3,49,43,6,51, -> .mínusz.tizen.kettő.egész.huszon.öt.század
-0.12=45,1,49,42,3,51, -> .mínusz.nulla.egész.tizen.kettő.század
0=1, -> .nulla
Időpont kimondása:
12.25=42,3,84,95,43,6,85, -> .tizen.kettő.óra. .huszon.öt.perc
Dátum kimondása:
2021.06.20=3,46,43,2,95,34,87,12, -> .kettő.ezer.huszon.egy. .hatodik.hónap.húsz
Példák a rövidítés használatára:
0.1=1,49,2,52, -> .nulla.egész.egy.tized
0.1 rövidítve=1,49,2, -> .nulla.egész.egy
0.01=1,49,2,51, -> .nulla.egész.egy.század
0.01 rövidítve=1,49,2,51, -> .nulla.egész.egy.század
0.12=1,49,42,3,51, -> .nulla.egész.tizen.kettő.század
0.12 rövidítve=1,49,42,3, -> .nulla.egész.tizen.kettő
0.001=1,49,2,50, -> .nulla.egész.egy.ezred
0.001 rövidítve=1,49,2,50, -> .nulla.egész.egy.ezred
0.301=1,49,22,2,50, -> .nulla.egész.háromszáz.egy.ezred
0.301 rövidítve=1,49,22,2, -> .nulla.egész.háromszáz.egy
12.25=42,3,84,95,43,6,85, -> .tizen.kettő.óra. .huszon.öt.perc
Lejátszási idő:4573
2021. június 04 péntek=3,46,43,2,95,65,5,57, -> .kettő.ezer.huszon.egy. .június.négy.péntek
Lejátszási idő:4975
2021.06.04 péntek=3,46,43,2,95,34,87,5,57, -> .kettő.ezer.huszon.egy. .hatodik.hónap.négy.péntek
Lejátszási idő:6308
2021.06.04=3,46,43,2,95,34,87,5, -> .kettő.ezer.huszon.egy. .hatodik.hónap.négy
Lejátszási idő:5626
0=1, -> .nulla
Lejátszási idő:15
0.001=1,49,2,50, -> .nulla.egész.egy.ezred
Lejátszási idő:2292
0.01=1,49,2,51, -> .nulla.egész.egy.század
Lejátszási idő:2292
0.1=1,49,2,52, -> .nulla.egész.egy.tized
Lejátszási idő:2294
-0.23=45,1,49,43,4,51, -> .mínusz.nulla.egész.huszon.három.század
Lejátszási idő:4472
0.513=1,49,24,42,4,50, -> .nulla.egész.ötszáz.tizen.három.ezred
Lejátszási idő:4476
0.702=1,49,26,3,50, -> .nulla.egész.hetszáz.kettő.ezred
Lejátszási idő:3090
0.017=1,49,42,8,50, -> .nulla.egész.tizen.hét.ezred
Lejátszási idő:2747
1.23=2,49,43,4,51, -> .egy.egész.huszon.három.század
Lejátszási idő:2766
1.76=2,49,17,7,51, -> .egy.egész.hetven.hat.század
Lejátszási idő:2525
12=42,3,81, -> .tizen.kettő.kilométer
Lejátszási idő:1456
25=43,6,80, -> .huszon.öt.milibar
Lejátszási idő:1275
39=13,10,79, -> .harminc.kilenc.celsiusfok
Lejátszási idő:1554
239=21,13,10, -> .kétszáz.harminc.kilenc
Lejátszási idő:1791
1.239=2,46,21,13,10,78, -> .egy.ezer.kétszáz.harminc.kilenc.kilohertz
Lejátszási idő:3749
17.324=42,8,46,22,43,5,77, -> .tizen.hét.ezer.háromszáz.huszon.négy.hertz
Lejátszási idő:4517
122.609=20,43,3,46,25,10,76, -> .száz.huszon.kettő.ezer.hatszáz.kilenc.ohm
Lejátszási idő:4440
3.022.609=4,47,20,3,46,25,10,75, -> .három.millió.száz.kettő.ezer.hatszáz.kilenc.milliamper
Lejátszási idő:5221
17.222.250=42,8,47,21,43,3,46,21,15, -> .tizen.hét.millió.kétszáz.huszon.kettő.ezer.kétszáz.ötven
Lejátszási idő:6103
221.999.111=21,43,2,47,28,19,10,46,20,42,2,74, -> .kétszáz.huszon.egy.millió.kilencszáz.kilencven.kilenc.ezer.száz.tizen.egy.amper
Lejátszási idő:7860
1.221.999.111=2,48,21,43,2,47,28,19,10,46,20,42,2,73, -> .egy.milliárd.kétszáz.huszon.egy.millió.kilencszáz.kilencven.kilenc.ezer.száz.tizen.egy.millivolt
Lejátszási idő:9396
1.221.999,111=2,47,21,43,2,46,28,19,10,49,20,42,2,50,72,21,1, -> .egy.millió.kétszáz.huszon.egy.ezer.kilencszáz.kilencven.kilenc.egész.száz.tizen.egy.ezred.volt.kétszáz.nulla

Terveim szerint egy külön egységként fogom megépíteni a „beszélő” modult. Veszek egy ATmega328 chip-et, és nemes egyszerűsíéggel kihajtogatott lábakkal (felesleg lecsipkedésével) ráforrasztom az mp3 lejátszóra. Annak a gyárilag beforrasztott lábait el is lehet csipkedni, hogy laposabb legyen az egész. Az így kialakított modul mérete 15x15x10mm. Az a terv, hogy I2C buszon keresztül fogom átadni a kimondásra szánt számot (esetleg dátumot vagy időpontot és a hangerő értékét), és ez a komplett kis modul önállóan fog beszélni. Valószínűleg egy kb 3 cm átmérőjű kis hangszóróra szerelem az egészet, és lesz egy beszélő hangszóróm! Ehhez a megvalósításhoz minden rendelkezésre áll. Tudom már, hogy hogyan kell egy ATmega328-ba programot égetni, esetleg még kvarcz kristály sem kell a működéshez, mert mehet belső órajelről. Az I2C busz órajelét mindig a master eszköz állítja elő, így a belső órajel instabilitása nem okozhat gondot, mint pl. egy aszinkron soros port esetén. Azt, hogy hogyan lehet összekötni két eszközt (pl. Arduino és ATmega328) I2C buszon keresztül, már régóta tudom és használtam is már. Tekintettel arra, hogy nem akarom leírni a számokat, a „szövegesítő” programrészek teljesen kihagyhatók akkor pedig már olyan kicsi a program, hogy ATmega328 vezérlő sem kell, elegendő az ATmega8, ami Kínából hajóval és repülővel és a magyar Posta részvételével összesen 300Ft darabáron meg is érkezik egy hónap múlva. Csak arra kell vigyázni, hogy a „P” jelzésű chip-et vegyem meg (Pl.ATmega328P), mert azt még normál barkács eszközökkel is lehet kezelni, beforrasztani. Az mp3 lejátszó ehhez képest nagyon drága, 1000Ft-ba kerül.

Mennyire volt hasznos amit olvastál? Értékelés után szövegesen is leírhatod megjegyzéseidet és véleményedet!

Kattints egy csillagra az értékeléshez!

Szövegesen is leírhatod véleményedet! Ha kérdésed van, ne felejtsd el megadni az email címedet!