[158] Gadający woltomierz cz. 4
Skoro już opanowaliśmy generację dźwięków z plików znajdujących się na karcie SD, wróćmy do edytora audio, załadujmy plik z poprzedniego artykułu i mozolnie wyeksportujemy wszystkie 38 plików – przeczytanych liczebników - zaznaczając uprzednio podzielone fragmenty. Przypomnę całą listę dla liczb od zera do tysiąca:
0.afm - zero
1.afm - jeden
2.afm - dwa
3.afm - trzy
4.afm - cztery
5.afm - pięć
6.afm - sześć
7.afm - siedem
8.afm - osiem
9.afm - dziewięć
10.afm - dziesięć
11.afm - jedenaście
12.afm - dwanaście
13.afm - trzynaście
14.afm - czternaście
15.afm - piętnaście
16.afm - szesnaście
17.afm - siedemnaście
18.afm - osiemnaście
19.afm - dziewiętnaście
20.afm - dwadzieścia
30.afm - trzydzieści
40.afm -czterdzieści
50.afm - pięćdziesiąt
60.afm - sześćdziesiąt
70.afm - siedemdziesiąt
80.afm - osiemdziesiąt
90.afm - dziewięćdziesiąt
100.afm - sto
200.afm - dwieście
300.afm - trzysta
400.afm - czterysta
500.afm - pięćset
600.afm - sześćset
700.afm - siedemset
800.afm - osiemset
900.afm - dziewięćset
1000.afm - tysiąc
Ważne, by miejsca podziału pliku, który nagrałem na potrzeby poprzedniego artykułu, miały zerową głośność, inaczej usłyszymy stuki między cyframi. Zbiorek taki nazwałem według widocznego wyżej klucza, za pomocą cyfr i można go ściągnąć z załącznika pod artykułem. Jest w tym chyba tak oczywista logika, że tłumaczyć tego nie muszę. A równocześnie bardzo to uprości działania algorytmu wybierającego składowe do wypowiadania fraz.
Skoro tylko przegramy wszystko na kartę, pozostanie nam stworzyć nowy szkic. I – nie ukrywam – jest on dość skomplikowany, ale przez to w miarę niewielki na to, co robi. Bierzmy się więc za analizę. Najpierw wstęp i główna pętla.
#include <SimpleSDAudio.h> // Biblioteka odtwarzania sampli z karty SD.
int numberToSpeak; // Liczba, którą należy odtworzyć.
byte digitIndex = 0; // Pozycja aktualnie odtwarzanej cyfry odtwarzanej liczby (0...3)
bool playing = false; // Ustawiony, jeśli trwa odtwarzanie.
bool pressed = false; // Ustawiony, jeśli przycisk wyzwolił odtwarzanie.
const byte button = 8; // Port przycisku.
const byte voltmeter = A1; // Adres portu pomiaru napięcia.
char file[12]; // Tablica dla bieżącej nazwy odtwarzanego pliku.
void setup() {
pinMode(button, INPUT_PULLUP); // Deklaruj port przycisku jako wejście.
SdPlay.init(SSDA_MODE_FULLRATE | SSDA_MODE_MONO | SSDA_MODE_AUTOWORKER); // Inicjacja biblioteki.
}
void loop() {
if (digitalRead(button) == HIGH && !pressed) { // Jeśli wciśnięto przycisk i nie był on wciśnięty wcześniej...
speakNumber(analogRead(voltmeter)); // Przejdź do procedury przeczytania zmierzonej wartości.
pressed = true; // Ustaw flagę wciśnięcia przycisku.
}
if (digitalRead(button) == LOW) { // Jeśli przycisk został puszczony...
pressed = false; // Czyść flagę wciśnięcia przycisku.
}
if (playing && SdPlay.isStopped()) { // Jeśli zakończyło się odtwarzanie kolejnej cyfry z liczby oraz...
if (++digitIndex < 4) { // Jeśli wciąż pozostały jakieś liczby do odtworzenia...
playNextDigit(); // Odtwórz kolejne cyfry.
} else { // Jeśli wszystko zostało odtworzone...
playing = false; // Zeruj flagę odtwarzania.
}
}
}Oto lista zmiennych, kolejno: numberToSpeak – liczba, którą Arduino będzie miało wypowiedzieć. digitIndex – Kolejna cyfra z tej liczby (od jednej, dla liczb mniejszych od dziesięciu do czterech, dla liczb większych lub równych tysiąc). Dwie flagi: playing – ustawiona podczas odtwarzania sampli i pressed – po wciśnięciu przycisku odtwarzania. W końcu port przycisku button – tutaj jest to środkowy mikroswitch na płytce edukacyjnej TME i voltmeter – wejście napięcia mierzonego. Na potrzeby testowania programu będę mierzył napięcie ślizgacza pokładowego potencjometru. Na końcu mamy tablicę file, która przechowuje nazwy wybieranych do odtwarzania plików.
W części wstępnej nie ma nic ciekawego, inicjacja biblioteki i deklaracja portu przycisku. Główna pętla rozpoczyna się od analizy przycisku button. Jeśli go wciśnięto, a był wcześniej puszczony, należy zmierzyć napięcie i z wynikiem udać się do podprogramu speakNumber obsługującego odtwarzanie dźwięków (listing znajduje się niżej). Zaraz tam przejdziemy, dokończmy analizę pętli głównej. Najpierw ustawia się flagę wciśnięcia przycisku pressed, żeby nie doszło do lawinowego autopowielania, gdyby przycisk był trzymany dłużej.
Jeśli odtwarzanie dźwięku skończy się – o czym informuje flaga playing oraz funkcja biblioteki, należy przejść do odtworzenia kolejnej cyfry liczby zmierzonej, których mogą być cztery (od zera do trzech). Jeśli wszystko zostanie odtworzone, możemy mogli wyzerować flagę zezwolenia obsługi przycisku pressed, czyli pozwolić na kolejny odczyt.
void speakNumber(int x) {
numberToSpeak = x; // Liczba, którą należy odtworzyć.
digitIndex = 0; // Pozycja aktualnie odtwarzanej cyfry to setki.
playNextDigit(); // Odtwórz aktualną cyfrę.
playing = true; // Ustaw flagę odtwarzania.
}
void playNextDigit() {
SdPlay.stop(); // Zatrzymaj bieżące odtwarzanie.
file[0] = '\\0'; // Wyczyść tablicę z nazwą odtwarzanego pliku.
byte digit000 = numberToSpeak / 100; // Wyciągnij z liczby do odtworzenia: setki...
byte digit00 = (numberToSpeak / 10) % 10; // Dziesiątki...
byte digit0 = numberToSpeak % 10; // Jednostki.
switch (digitIndex) { // Dopasuj plik do cyfry i pozycji w liczbie do odtworzenia.
case 0: // Setki.
if (digit000) // Jeśli są...
sprintf(file, "%u00.afm", digit000); // Odtwarzaj odpowiedni plik.
break;
case 1: // Dziesiątki oraz "nastki".
if (digit00) { // Jeśli są...
byte val = (digit00 == 1 && digit0) ? 10 + digit0 : digit00 * 10; // Ustaw pułapkę na tzw. "nastki" (11-19).
sprintf(file, "%u.afm", val); // Odtwarzaj odpowiedni plik.
}
break;
case 2: // Jednostki.
if (digit0 && (digit00 > 1 || !digit00)) // Jeśli liczba >20 lub <10
sprintf(file, "%u.afm", digit0); // Odtwarzaj odpowiedni plik.
break;
case 3: // Zero
if (!numberToSpeak) // Jeśli liczba do otworzenia to zero...
strcpy(file, "0.afm"); // Odtwórz zero.
break;
}
if (!file[0] || !SdPlay.setFile(file)) { // Jeśli nie ma pliku lub pojawia się błąd jego odczytu...
if (++digitIndex < 4) // I jeśli jest coś do odczytania...
playNextDigit(); // Odczytaj kolejną cyfrę.
return;
}
SdPlay.play(); // Odtwarzaj bieżący plik.
}Jak wygląda konwersja liczby na składowe? Podprogram odzyskuje liczbę, ładując ją do zmiennej x, przy czym wchodzi tu tylko na początku analizy. Raz jeszcze zerowany jest adres cyfry digitIndex, gdyby ktoś wciskał przycisk zbyt często. Dzięki temu czytanie komunikatu jest przerywane natychmiast, a nowy czytany jest od początku. Ustawiamy flagę aktywności odtwarzania playing i przechodzimy do wspólnego podprogramu playNextDigit dla kolejnych cyfr. Ten zaczyna się zatrzymaniem odtwarzania – gdyby przycisk został wciśnięty przed czasem. Następnie czyści się tablicę z nazwą pliku file, by nie zostały w niej żadne śmieci. Teraz tworzą się trzy zmienne zaczynające się od słowa digit, w które wchodzą cyfry setek, dziesiątek i jednostek. Przechodzimy do algorytmu dobierającego pliki do liczby. Mamy tu cztery etapy.
switch (digitIndex) { // Dopasuj plik do cyfry i pozycji w liczbie do odtworzenia.
case 0: // Setki.
if (digit000) // Jeśli są...
sprintf(file, "%u00.afm", digit000); // Odtwarzaj odpowiedni plik.
break;Pierwszy to odczyt setek. Jak widać, plik pobieramy, manipulując jego nazwą, czyli podmieniając pierwszy znak pliku, który jest zgodny z wyłuskaną przed chwilą cyfrą. To jest proste, schody zaczynają się na kolejnym etapie.
case 1: // Dziesiątki oraz "nastki".
if (digit00) { // Jeśli są...
byte val = (digit00 == 1 && digit0) ? 10 + digit0 : digit00 * 10; // Ustaw pułapkę na tzw. "nastki" (11-19).
sprintf(file, "%u.afm", val); // Odtwarzaj odpowiedni plik.
}
break;Tutaj musimy ustawić zawiły wzór, który osobno potraktuje dziesiątki od dwudziestu w górę, a inaczej zakres od 11 do 19, odpowiednio przekształcając nazwę pliku do pobrania i odtworzenia.
case 2: // Jednostki.
if (digit0 && (digit00 > 1 || !digit00)) // Jeśli liczba >20 lub <10
sprintf(file, "%u.afm", digit0); // Odtwarzaj odpowiedni plik.
break;W końcu jednostki czytane są, o ile nie ma zera ani liczb od 11 do 19.
case 3: // Zero
if (!numberToSpeak) // Jeśli liczba do otworzenia to zero...
strcpy(file, "0.afm"); // Odtwórz zero.
break;Ostatnim etapem jest czytanie zera i ma to miejsce tylko wtedy, gdy liczba do przeczytania równa jest zero.
if (!file[0] || !SdPlay.setFile(file)) { // Jeśli nie ma pliku lub pojawia się błąd jego odczytu...
if (++digitIndex < 4) // I jeśli jest coś do odczytania...
playNextDigit(); // Odczytaj kolejną cyfrę.
return;
}
SdPlay.play(); // Odtwarzaj bieżący plik.Ostatnimi elementami są: pułapka na popsuty bądź nieistniejący plik. Dzięki temu program nie zawiesi się, gdyby nie mógł czegoś odczytać. W końcu zwiększa się adres bieżącej pozycji w liczbie do przeczytania i odczytuje ją – jeśli to nie koniec. Ostatnia instrukcja to uruchomienie właściwego odczytywania uprzednio załadowanego pliku.
Uff, po uruchomieniu możemy sobie kręcić potencjometrem i słuchać, jakie napięcie zostało wybrane. Jeśli załadujemy zestaw z załącznika, zauważmy, że tempo odczytów jest niespieszne. Jeśli ktoś chciałby to przyspieszyć, można oczywiście nagrać szybsze komunikaty, ale można też włączyć „tryb krasnala” zamieniając słowo MONO w STEREO w linii inicjacji biblioteki. Oczywiście nie będzie stereofonii, ale biblioteka rozdzieli strumień na dwa porty, wysyłając na nasz co drugi bajt, co da nam efekt czytającego szybko krasnoludka. Miłej zabawy.

Inne artykuły z tej kategorii
Fotograficzny kalkulator raz jeszcze
































































