[103] Radio z RDS-em
![[103] Radio z RDS-em](/_next/image?url=https%3A%2F%2Farduino.pl%2Fimgproxy%2Fm172jVvI3oO5SpBbKslue5eJ0XartdER2GjO5vihPTA%2Ff%3Awebp%2Fw%3A1200%2FbG9jYWw6Ly8vaW1hZ2VzLzAtNjdlL2IxNzQxLzdiODAyLzAtNjdlYjE3NDE3YjgwMjEwODQyNDQyNi5wbmc%3D.webp&w=3840&q=75)
Mając już radio z wyświetlaczem, koniecznie trzeba oprogramować możliwość prezentowania napisów RDS. Cóż to takiego? Nie będę wyjaśniał dokładnie jak działa ten system, bo to materiał na osobny artykuł. W skrócie powiem tylko, że w zasadzie tekst sprowadza się do ośmiu znaków widocznych jednocześnie i jest traktowany bardzo niekonsekwentnie. Teoretycznie mając dłuższy wyświetlacz, można by go składać w bardziej czytelne, dłuższe komunikaty, lecz w praktyce co stacja, to własny pomysł i czasem ta czytelność stawałaby się dyskusyjna. Dlatego postanowiłem się ograniczyć do ośmiu znaków, wyświetlanych w dolnym wierszu wyświetlacza użytego w poprzednim projekcie.
#include <RDSParser.h> // Biblioteka obsługująca protokół RDS
RDSParser rds;
Do dekodowania tekstów użyjemy kolejnej biblioteki, która nie jest zbyt czytelna w opisach, ale działa. Jak to zwykle bywa, musimy nazwać obiekt, więc użyłem oczywistej nazwy: rds
void rdsOdbierz(unsigned int rds1, unsigned int rds2, unsigned int rds3, unsigned int rds4) { // Procedura pozyskująca dane RDS
rds.processData(rds1, rds2, rds3, rds4);
}
void rdsWyswietl(const char* rdsDane) { // Pozyskaj dane RDS z tunera.
lcd.setCursor(0, 1); // Ustaw kursor na dole wyświetlacza.
lcd.print(rdsDane); // Wyświetl pozyskane dane.
}
Procedura pracuje nietypowo, nie odwołując się prost w programie do kolejnych elementów. Tutaj definiujemy parę podprogramów – jeden odbierający dane, drugi – formujący je.
W głównej pętli znajduje się tylko niejawne odwołanie do podprogramów. Dostęp do danych RDS jest zawiły i prawdę powiedziawszy nie znalazłem opisu, dlaczego akurat w ten sposób należy to robić. Ale nie wszystko trzeba w życiu wiedzieć, jeśli coś działa dobrze – może pozostać czarną skrzynką. Najważniejsze, że w ostatniej linii możemy wysłać już uformowany tekst na wyświetlacz.
Prawdopodobnie komplikacja tej procedury wynika z rozbudowanego systemu komunikatów RDS, które w praktyce nie mają znaczenia, ponieważ nadawcy wysyłają co chcą, bez ładu i składu. Z tego powodu system ten pełni bardziej formę bajeru niż użyteczną, choć z pewnością dałoby się to wszystko uporządkować i – jak w przypadku programu drugiego polskiego radia – uzyskać istotne informacje o emitowanej muzyce. Ale to niestety jest wyjątek w regule.
W zasadzie wszystko jest już gotowe, lecz wypadałoby jeszcze zapiąć ostatni guzik. Spójrzmy na procedurę zapisu stacji w pamięci. Dopiszmy do niej rozkazy, które wyślą komunikat o dokonaniu zapisu stacji w pamięci, po czym, po sekundzie przywrócą standardowy wygląd wyświetlacza.
if (digitalRead(programZapisz) == HIGH) { // Jeśli wciśnięto przycisk "programZapisz"...
EEPROM.put(program * 2, radio.getFrequency()); // Zapisz w pamięci EEPROM aktualną częstotliwość.
lcd.clear(); // Wyczyść wyświetlacz i ustaw kursor na początku.
lcd.setCursor(1, 0); // Wyświetl komunikat.
lcd.print(F("Stacja zostala"));
lcd.setCursor(4, 1);
lcd.print(F("zapisana"));
delay(1000); // Zaczekaj chwilę...
lcd.clear(); // Odśwież wyświetlacz.
wyswietlCzestotliwosc();
wyswietlProgram();
wyswietlGlosnosc();
while (digitalRead(programZapisz) == HIGH) {} // Czekaj aż przycisk zostanie puszczony.
}
I to już wszystko. Teraz mamy już radyjko pełnoprawne, które będzie wyświetlało teksty, czasem użyteczne, a czasem nie. I jak zwykle pragnę przypomnieć, że jest to tylko pomysł wstępny, który winien nie być kopiowany, a adaptowany do własnych potrzeb. A już na koniec – raz jeszcze, kompletny i sprawdzony listing radia z RDS-em
#include <radio.h> // Biblioteka obsługi serii tunerów FM
#include <RDA5807FP.h> // Biblioteka obsługi konkretnej wersji tunera.
RDA5807FP radio; // Nadaj układowi nazwę "radio"
// #include <RDA5807M.h>
// RDA5807M radio;
// #include <SI4705.h>
// SI4705 radio;
// #include <SI47xx.h>
// SI47xx radio;
// #include <TEA5767.h>
// TEA5767 radio;
#include <RDSParser.h> // Biblioteka obsługująca protokół RDS
RDSParser rds;
#include <EEPROM.h> // Biblioteka obsługi pamięci nieulotnej.
#include <Wire.h> // Biblioteka obsługująca magistralę I2C
#include <hd44780.h> // Biblioteka obsługująca wyświetlacze 44780
#include <hd44780ioClass/hd44780_I2Cexp.h> // Dodatek obsługujący wyświetlacze podłączone do ekspandera I2C
hd44780_I2Cexp lcd(0x20, I2Cexp_MCP23008, 7, 6, 5, 4, 3, 2, 1, HIGH); // Konfiguracja połączeń wyświetlacza LCD
const byte poprzednia = 4; // Pstryczek "Poprzednia stacja"
const byte nastepna = 7; // Pstryczek "Następna stacja"
const byte programMinus = 5; // Pstryczek "Zmniejsz numer programu"
const byte programPlus = 6; // Pstryczek "Zwiększ numer programu"
const byte programZapisz = 8; // Pstryczek "Zapisz program"
const byte potencjometr = A1; // Potencjometr głośności.
int napiecieStare = 0; // Poprzednie zmierzone napięcie ustawione potencjometrem.
int napiecieNowe = 1023; // Aktualne zmierzone napięcie ustawione potencjometrem.
byte program = 1; // Numer aktualnego programu (1-29)
int czestotliwosc; // Aktualna częstotliwość stacji.
char czestotliwoscWyswietlana[11]; // Zmienna tekstowa, która reprezentuje częstotliwość pobraną z płytki tunera.
byte glosnosc; // Poziom aktualnej głośności.
void setup() {
radio.initWire(Wire); // Inicjuj tuner.
radio.setup(RADIO_FMSPACING, RADIO_FMSPACING_100); // Ustaw krok strojenia równy 100 kHz.
radio.setup(RADIO_DEEMPHASIS, RADIO_DEEMPHASIS_50); // Ustaw deemfazę obowiązującą w Europie.
radio.setBandFrequency(RADIO_BAND_FM, 9800); // Ustaw częstotliwość początkową na środek zakresu.
radio.setMono(false); // Ustaw tryb stereo.
radio.attachReceiveRDS(rdsOdbierz); // Zdefiniuj podprogram odbierający dane RDS z płytki tunera.
rds.attachServiceNameCallback(rdsWyswietl); // Zdefiniuj podprogram wyświetlający dane RDS
pinMode(poprzednia, INPUT_PULLUP); // Deklaruj linie pstryczków jako wejścia podciągnięte wewnętrznie do wysokiego stanu.
pinMode(nastepna, INPUT_PULLUP);
pinMode(programMinus, INPUT_PULLUP);
pinMode(programPlus, INPUT_PULLUP);
pinMode(programZapisz, INPUT_PULLUP);
lcd.begin(16, 2); // Inicjalizacja wyświetlacza LCD
lcd.clear(); // Wyczyść wyświetlacz i ustaw kursor na początku.
zmienProgram(); // Pobierz częstotliwość pierwszego programu z pamięci EEPROM i wyślij ją do tunera.
}
void loop() {
if (digitalRead(poprzednia) == HIGH) { // Jeśli wciśnięto przycisk "Poprzednia stacja"...
radio.seekDown(0); // Przestrój się w górę o 100 kHz (jedynka skacze do najbliższej stacji).
wyswietlCzestotliwosc(); // Wyświetl częstotliwość.
wyswietlKreski(); // Wyświetl kreski zamiast numeru programu.
while (digitalRead(poprzednia) == HIGH) {} // Czekaj aż przycisk zostanie puszczony.
}
if (digitalRead(nastepna) == HIGH) { // Jeśli wciśnięto przycisk "Następna stacja"...
radio.seekUp(0); // Przestrój się w dół o 100 kHz (jedynka skacze do najbliższej stacji).
wyswietlCzestotliwosc(); // Wyświetl częstotliwość.
wyswietlKreski(); // Wyświetl kreski zamiast numeru programu.
while (digitalRead(nastepna) == HIGH) {} // Czekaj aż przycisk zostanie puszczony.
}
if (digitalRead(programMinus) == HIGH) { // Jeśli wciśnięto przycisk "Zmniejsz numer programu"...
--program; // Zmniejsz numer programu.
if (program < 1) { // Jeśli numer programu jest mniejszy od 1...
program = 29; // Numer programu będzie równy 29
}
zmienProgram(); // Reszta procedury.
while (digitalRead(programMinus) == HIGH) {} // Czekaj aż przycisk zostanie puszczony.
}
if (digitalRead(programPlus) == HIGH) { // Jeśli wciśnięto przycisk "Zwiększ numer programu"...
++program; // Zwiększ numer programu.
if (program > 29) { // Jeśli numer programu jest większy od 29...
program = 1; // Numer programu będzie równy 1
}
zmienProgram(); // Reszta procedury.
while (digitalRead(programPlus) == HIGH) {} // Czekaj aż przycisk zostanie puszczony.
}
if (digitalRead(programZapisz) == HIGH) { // Jeśli wciśnięto przycisk "programZapisz"...
EEPROM.put(program * 2, radio.getFrequency()); // Zapisz w pamięci EEPROM aktualną częstotliwość.
lcd.clear(); // Wyczyść wyświetlacz i ustaw kursor na początku.
lcd.setCursor(1, 0); // Wyświetl komunikat.
lcd.print(F("Stacja zostala"));
lcd.setCursor(4, 1);
lcd.print(F("zapisana"));
delay(1000); // Zaczekaj chwilę...
lcd.clear(); // Odśwież wyświetlacz.
wyswietlCzestotliwosc();
wyswietlProgram();
wyswietlGlosnosc();
while (digitalRead(programZapisz) == HIGH) {} // Czekaj aż przycisk zostanie puszczony.
}
napiecieNowe = analogRead(potencjometr); // Mierzymy napięcie ustawione ślizgaczem potencjometru.
if (abs(napiecieNowe - napiecieStare) > 3) { // Odejmujemy tę wartość od wartości zmierzonej poprzednio i jeśli wartość bezwzględna wyniku jest większa od 3, to...
napiecieStare = napiecieNowe; // Przepisujemy wartość zmierzonego napięcia do zmiennej napiecieStare.
if (napiecieNowe < 4) { // Jeśli napięcie jest mniejsze od 4...
radio.setMute(1); // Wycisz radio.
glosnosc = 0; // Na potrzeby wyświetlacza ustaw wartość głośności równą zero.
} else { // W przeciwnym razie...
radio.setMute(0); // Wyłącz wyciszenie oraz...
glosnosc = map(napiecieNowe, 4, 1020, 0, 15); // Przemapuj pozycję potencjometru do zakresu (0-15)
radio.setVolume(glosnosc); // Wyślij tę wartość do rejestru głośności.
glosnosc++; // Na potrzeby wyświetlacza przesuń wartość głośności o jeden, bo zero to cisza.
}
wyswietlGlosnosc(); // Odśwież wyświetlacz.
}
radio.checkRDS(); // Odśwież dane RDS
}
void zmienProgram() {
EEPROM.get(program * 2, czestotliwosc); // Pobierz z pamięci EEPROM częstotliwość aktualnego kanału.
radio.setBandFrequency(RADIO_BAND_FM, czestotliwosc); // Wyślij tę częstotliwość do tunera.
wyswietlCzestotliwosc(); // Odśwież wyświetlacz.
wyswietlProgram();
}
void wyswietlCzestotliwosc() {
lcd.setCursor(0, 0); // Ustaw kursor.
delay(100); // Niezbędne opóźnienie celem ustabilizowania się układu tunera po zmianie częstotliwości.
radio.formatFrequency(czestotliwoscWyswietlana, 11); // Pobierz wartość częstotliwości z tunera.
lcd.print(czestotliwoscWyswietlana); // Wyświetl ją.
}
void wyswietlProgram() {
lcd.setCursor(11, 0); // Ustaw kursor.
lcd.print(F("Pr:")); // Wyświetl "PR:"
if (program < 10) { // Gdy numer programu jest mniejszy od 10...
lcd.print(F("0")); // Wyświetl zero wiodące.
}
lcd.print(String(program)); // Wyświetl numer programu.
}
void wyswietlGlosnosc() {
lcd.setCursor(10, 1); // Ustaw kursor.
lcd.print(F("Vol:")); // Wyświetl "VOL:"
if (glosnosc < 10) { // Gdy poziom głośności jest mniejszy od 10...
lcd.print(F("0")); // Wyświetl zero wiodące.
}
lcd.print(String(glosnosc)); // Wyświetl wartość głośności.
}
void wyswietlKreski() {
lcd.setCursor(14, 0); // Ustaw kursor.
lcd.print(F("--")); // Wyświetl "--"
}
void rdsOdbierz(unsigned int rds1, unsigned int rds2, unsigned int rds3, unsigned int rds4) { // Procedura pozyskująca dane RDS
rds.processData(rds1, rds2, rds3, rds4);
}
void rdsWyswietl(const char* rdsDane) { // Pozyskaj dane RDS z tunera.
lcd.setCursor(0, 1); // Ustaw kursor na dole wyświetlacza.
lcd.print(rdsDane); // Wyświetl pozyskane dane.
}