2012
|
Arduino als Radio - Radiuino
Si4735 - Digitales Analogradio
Ein ganzer Weltempfänger in einem Chip. Komplette digitale Verarbeitung der Signale vom Eingang bis zum Ausgang: DSP-Radio mit Si4735. Dadurch ergeben sich viele Möglichkeiten, die bisher nur in der oberen Preisklasse anzutreffen waren.
Für wenig Geld bekommt man z.B. die Fertiglösung TECSUN PL-390, ein Weltempfänger mit allem was man ohne Internet braucht. Sogar zwei Stereolautsprecher sind in dem typischen Reiseradio untergebracht. Aber RDS fehlt bei diesem Gerät, obwohl der Si4735 alles an Bord hat. Mit Hilfe des Arduino soll der Si4735 angesteuert werden.
Ein 3,3-Volt-Si4735-Shield von Sparkfun für den 5-Volt-Arduino |
|
Übersicht
Benutzte Hardware
Ein Arduino ATmega168 mit 16k Speicherplatz mit dem obigen Shield, sowie Kleinteile.
Die Liste:
- Arduino 5 Volt mit ATmega168/16 MHz (16k)
- Sparkfun-Si4735-Shield für Arduino
- Verbindungspfosten (werden nicht zum Shield geliefert!)
- Ein Lötstift für den Antennenanschluss
- Ein Antennendraht als Wurfantenne für FM/LW/MW/KW
- Eine Diode 1N4148 oder so (bei Erweiterung)
|
Benutzte Software
Zum Zeitpunkt dieser Niederschrift wurde auf arduino.cc die IDE arduino-1.0 bereit gestellt. Darin wurden einige Dinge umbenannt, so dass etwas ältere Bibliotheken aus dem Netz nicht sofort kompiliert werden können. Zunächst wird hier noch die Vorgängerversion arduino-0023 benutzt.
Die ersten Schritte erfolgen über den "Serial Monitor" in arduino-0023 mit dem leicht angepassten Quelltext der "Arduino Library" auf der Sparkfun-Seite (Stand 2012). Später erfolgt die Darstellung und Steuerung über VB in Excel oder Word mittels RSAPI.DLL.
Zusammengefasst:
- arduino-0023 als IDE
- Arduino Library von der Sparkfun-Seite
- Excel oder Word mit VB (optional)
- RSAPI.DLL (optional)
|
Excel steuert den Arduino, dieser steuert den Si4735. Dies ist ein Screenshot einer Radio 4 Version. Es ist die Testplattform für Erweiterungen, die später dem geplanten autonomen System zur Verfügung stehen sollen. Wenn denn der Hardwareversand mal wieder Drehencoder vorrätig hat, oder watter..? |
|
Radio 0 - Funktionstest
Sparkfun bietet/bot zum Artikel eine Si4735-Bibliothek an. Die Zip-Datei enthält die beiden Dateien Si4735.h und Si4735.cpp mit den Grundroutinen. Zwei Beispiele sind im "examples"-Verzeichnis. Diese Zip-Datei sollte in dem "library"-Verzeichnis unter der IDE arduino-0023 entpackt werden. Die Beispiele enden mit *.pde, die alte IDE-Endung.
Ist alles richtig entpackt, erscheinen die Beispiele nach dem Start der IDE im Beispielmenü. Das erste Beispiel dient als Funktionstest.
Folgende Kommandos können über den "Serial Monitor" dem Radio gesendet werden (jeweils abgeschlossen mit der Eingabetaste):
- 8 - Lautstärke rauf
- 2 - Läustärke runter
- 4 - Suchlauf runter
- 6 - Suchlauf rauf
- m - Stumm (mute)
- M - Unstumm (unmute)
- s - Statusabfrage
Das Radio startet auf UKW (FM) auf der Frequenz 97,3 MHz. Da könnte man den Ortssender voreinstellen, aber mit 6 und Enter wird der nächste starke Sender gesucht.
Ein angeschlossener Verstärker bzw. Aktivboxen lassen erstaunlichen Empfang hören. Stereo etwas verrauscht, aber kein Wunder bei dieser Antenne ...
Laut, leise, Suchlauf rauf und runter, stumm - alles funktioniert wunderbar.
Bis auf die Statusabfrage ...
|
|
Pegel, Speicher, Geschwindigkeit
Schön wäre eine Frequenzabfrage. Im Quelltext findet man:
//This function does not work yet!
int Si4735::getFrequency(void){
return 0;
}
Eine Suche im Netz ergab folgende Situation:
- Mein Arduino ist zu klein - Es gibt fertige Projekte, die die Si4735-Bibliothek erweitern, aber sie sind zu groß für 16k des ATmega168.
- Mein Arduino ist zu langsam - Die SPI-Übertragung läuft auf Hochtouren, zu schnell für den kleinen ATmega.
- Mein Arduino ist zu groß - Will sagen, die Spannung ist zu hoch. Das Radio arbeitet mit 3,3 Volt, der Arduino hier mit 5 Volt. Da entstehen Pegelprobleme.
Das Pegelproblem an Pin 12
Über Pin 12 laufen die Antworten vom Radio zum Arduino. Durch Pegelprobleme klemmen die Bits, wodurch nur wackelige bzw. keine Abfragen funktionieren. Eine Lösung wird auf TrunetRadio beschrieben. Eine Diode von Pin 12 des Arduino in Richtung Radio soll Abhilfe schaffen. Die Erläuterung "Spannungslifter-Diode" im Arduino-Forum klingt tatsächlich zunächst verwirrend. Bedenkt man jedoch, dass die Eingänge des Arduino softwaremäßig mit Pullup-Widerständen versehen werden können, wird die Sache durchschaubarer. Das Radio liefert bei '1'-Pegel 3,3 Volt. Es ergibt sich nach Einlöten der Diode und Unterbrechung der entsprechenden Leiterbahn eine Reihenschaltung von 5 Volt nach Masse über 20 k-Pullup, Diode mit ca. 0,7 V Schwellenspannung und 3,3 Volt Radio. Somit sieht der Arduino nun 4 Volt am hochohmigen Eingang von Pin 12, der ja zwischen Pullup und Diode liegt. Bei '0'-Pegel ist die Diode quasi mit der Spitze auf Masse gelegt und Pin 12 sieht 0,7 Volt. Das sind klarere Verhältnisse für 5-Volt-Logik.
Die Diode allein reicht also noch nicht, der Widerstand muss "eingeschaltet" werden:
pinMode(12, INPUT); // set pin to input
digitalWrite(12, HIGH); // turn on pullup resistors
Also etwa so:
void setup()
{Serial.begin(9600);
radio.begin(FM);
radio.tuneFrequency(9730);
pinMode(12, INPUT);
digitalWrite(12, HIGH);
//Ab hier auch Abfragen möglich
}
|
Das Geschwindigkeitsproblem
KA1KJZ fand ein fehlendes Delay in der Funktion getResponse, die bei jeder Abfrage bemüht wird. Richtig lautet die Funktion:
void Si4735::getResponse(char * response)
{digitalWrite(SS, LOW);
delay(1);
spiTransfer(0xE0); //Set up to read the long response
delay(1);
for(int i=0; i<16; i++)*response++ = spiTransfer(0×00);
digitalWrite(SS, HIGH);
}
Weiterhin fand der Funkamateur, dass die SPI-Übertragung - für manche Arduinos - zu schnell eingestellt ist. In der Routine void Si4735::begin(char mode) soll die Zeile
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1); mit
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);
ersetzt werden. Wenn alle diese Änderungen erfolgt sind, sollte die Abfrage funktionieren.
|
Das Speicherproblem
Ein sehr ausführliches und umfangreiches Si4735/Arduino-Projekt ist im Blog "The Carrier Frequency" zu finden. Dort wird auch mit LabView als Steuer und Auswertesoftware gearbeitet.
Die dortige Bibliothek für Nord Amerika ist sehr groß und komfortabel, passt aber nicht in den Speicher hier. Also müssen einige Dinge gekürzt oder/und weg gelassen werden. Bei der Kürzung und Modifikation wurden die Kommentare meist entfernt, da sie im Original ja bleiben und der geänderte Kode etwa die gleiche Funktion aufweist.
|
Die weiter unten bereitgestellten Quelltexte basieren auf den genannten Originalen und wurden geringfügig modifiziert, damit die Platine mit dem hier vorhandenen Arduino in Europa brauchbare Ergebnisse mit dem Shield von Sparkfun liefert. Sie dienen nur Versuchzwecken und sind in keiner Weise fehlerfrei oder vollständig.
|
Radio 1 - Frequenzanzeige
Nun kann der Si4735 auf Anfragen auch antworten. Mit "f" wird die Frequenz erfragt und seriell weiter gereicht. Das Kommando "0" gibt die Feldstärke und den SNR-Wert zurück. Das macht ja der TECSUN auch. Mono/Stereo-Umschaltung ("1"/"3") ist manchmal bei Wortbeiträgen nützlich. DLF Wesel immer zu schwach hier, wie man erkennt. Der Ausschnitt der Hauptschleife von Radio 1:
{case '1':radio.setMono(true);break;
case '3':radio.setMono(false);break;
case 'W':radio.tuneFrequency(LOCALRADIO);break;
case '8':radio.volume(true);break;
case '2':radio.volume(false);break;
case '4':radio.seek(false);break;
case '0':radio.getRSQ(&RSSI,&SNR);
sprintf(s,"%02d/",RSSI);
Serial.print(s);
sprintf(s,"%02d",SNR);
Serial.println(s);
break;
case 'm':radio.mute(true);break;
case 'u':radio.mute(false);break;
case '6':radio.seek(true);break;
case 'f':frequency=radio.getFrequency(refresh);
if(mode==FM)
{Serial.print((frequency/100));
Serial.print(",");
Serial.print((frequency%100)/10);
Serial.println(" MHz");
}
else
{Serial.print((frequency));
Serial.println(" kHz");
}
break;
default: break;
}
Hier der Sketch Si4735_Radio_1.pde für arduino-23. Modifiziert: Si4735.cpp/Si4735.h.
|
Radio 2 - RDS mit Sendername
Das Radio-Daten-System ist meist in Autoradios zu finden. Es werden verschiedene Informationen übertragen wie z.B. Sendername, Verkehrsfunksender, Verkehrsfunkdurchsage, TMC (Navigation), Alternativfrequenzen und Radiotext. Es ist viel mehr möglich. Der Datenstrom wird ab einer gewissen Qualität vom Radiochip dekodiert und digital bereit gestellt. Einzelheiten zu RDS findet man in der deutschen Wikipedia (zumindest 2012 noch).
Eine RDS-Gruppe besteht aus vier Blöcken und beinhaltet netto 8 Bytes, bzw. 4 Words oder eben 64 Bit pro Paket. Der RDS-Dekoder muss also nur noch die Bits richtig sortieren. Der Sendername befindet sich z.B. im 4. Block mit 2 Bytes und besteht aber aus 8 Zeichen. Welches Zeichen dran ist, wird in einigen Bits von Block 2 entschieden. Um den kompletten Sendernamen zu bekommen müssen also mindestens vier verschiedene Pakete empfangen werden. Genaueres findet man im Einzelnachweis im Wikipedia-Artikel "RDS groups list".
Der Sketch Radio 2 holt nur bei der Aufforderung "r" ein zufälliges Paket ab. Somit muss mindestens 4x "r" im Serial-Monitor eingegeben werden (also "rrrrrrrrrrr"), um den gesamten Namen zu lesen.
Der modifizierte Quelltext in Si4735.cpp trägt die Buchstaben in die lokale statische Variable "Station" ein und sieht an der entsprechenden Stelle wie folgt aus:
.
GT = (R[6]>>4)&0x0F; // Group Type 0 - 15
AB = (R[6]>>3)&0x01; // Group A/B -> 0/1
switch(AB)
{case 0:// Type A
switch(GT)// GroupType
{case 0: C12= R[7]&3;//4x2 Buchstaben
switch(C12) //Welches Duo ist dran.
{case 0:Station[0]=R[10]; Station[1]=R[11]; break;
case 1:Station[2]=R[10]; Station[3]=R[11]; break;
case 2:Station[4]=R[10]; Station[5]=R[11]; break;
case 3:Station[6]=R[10]; Station[7]=R[11]; break;
}
break;
}//switch GT
break;
.
Im Radiosketch selber kommt nun die Abfrage auf "r" hinzu. Die Zeichenkette s sollte mindestens 9 Bytes lang sein, da dort der Name hinein kopiert wird und eine 0 den String abschließt.
void loop()
{word frequency; bool refresh=true;
byte RSSI,SNR; char s[20],t[65];
if(Serial.available()>0)
{switch(Serial.read())
{case 'r':radio.readRDS(s,t);
Serial.println(s);//8
break;
case '1':radio.setMono(true);break;
Bei der Senderkennung "Funkhaus Europa" kam wohl das HA in der letzten Abfrage.
Hier der Sketch Si4735_Radio_2.pde für arduino-23. Modifiziert: Si4735.cpp/Si4735.h.
|
Radio 3 - RDS mit Radiotext
Der Radiotext besteht aus 64 Zeichen und kommt, wie der Sendername, häppchenweise. Allerdings gibt es vier Zeichen in einem Rutsch. Die RDS-Routine in der Radioklasse wird nur einmal durchlaufen und gibt den jeweiligen Zwischenempfang zurück. Die hier gewählte Methode wartet nicht, ob schon neue Daten vorliegen, oder irgendetwas nicht passt.
Bei intensiverer Auseinandersetzung mit dem RDS-Protokoll ist hier sicher ein schnelleres und zuverlässigeres Ergebnis möglich.
Nach mehreren Aufrufen kommt der Text immer deutlicher, oder eben es gibt ein Durcheinander, weil der Sender zwischen Texten wechselt.
Im Sketch sind das die Bytes der Zeichenkette t, die in der Routine radio.readRDS(s,t); sofort mit geholt werden. Sie stammen aus dieser Abfrage mit RadioText[65] als lokale statische Zeichenketten-Variable und RDS-Block C/D als Bytes R[8-9]/R[10/11]:
...
switch(AB) // Typ A oder B
{case 0: // Typ A
switch(GT)// Group Type
{case 0: // Sendername
C12= R[7]&3;
...
break;
case 2: T16 = R[7]&0x0F; //RadioText
RadioText[T16*4+00]=R[ 8];
RadioText[T16*4+01]=R[ 9];
RadioText[T16*4+02]=R[10];
RadioText[T16*4+03]=R[11];
break;
default: break;
}//switch GT
break;
case 1: //Type B
...
Zwei "hörerorientierte" Texte von Privatsendern aus der Region:
Der PI-code wird übrigens immer in Block A geliefert. Die zwei Bytes werden als hexadezimale 16-Bitzahl dargestellt. D steht für Deutschland (siehe hier). Mit dieser Kennung kann man entscheiden, ob der Sender gewechselt wurde. Ist PID eine statische Variable und A der aktuelle Block A, dann löschen die Zeilen
if(PID!=A) //Löschen der Zeichenpuffer
{for(i=0;i<64;i++)RadioText[i]=0;
for(i=0;i< 8;i++)Station[i]=0;
}
PID = A; //Merken
Sendername und Radiotext.
Nach solch ernüchterndem Kontent könnte man meinen Radiotext sei öde. Es gibt aber durchaus interessantere Texte. Der Deutschlandfunk liefert in seiner späten (23:00) Nachrichtenübersicht z.B. pro Beitrag die aktuelle Schlagzeile, quasi die Beitragsüberschrift. Andere Sender zeigen Interpret und Titel usw. Schade, dass der TESCUN das nicht kann.
Hier der Sketch Si4735_Radio_3.pde für arduino-23. Modifiziert: Si4735.cpp/Si4735.h.
|
Radio 4 - UKW, LW, MW, KW
|
Das Excel-Radio ist nicht aus Pappe |
Das vierte Radio ist ein Excel-Radio und enthält außer der Umschaltung der Wellenwahl noch die RDS-Daten:
RDS-Uhrzeit (Lokal)
RDS-Datum (UTC)
RDS-Alternativfrequenzen AF
Der Radiotext wurde in der Radioklasse weiter modifiziert. Wenn das Kommando "Zeile Löschen" ausgestrahlt wird, fragt die Routine nun mehrmals (loopcount) RDS-Daten ab, damit der Text etwas schneller erscheint. Die Bandbreite der Wellenlänge wird entsprechend angepasst, ect. Das Radio 4 läßt sich weiterhin auch ohne Excel über den Serial Monitor bedienen. Auf dem Weg bis hier war eine gewisse Benutzeroberfläche doch recht hilfreich. Es ist ein Scan-Radio, was sich leicht mit Senderspeichern erweitern läßt. Außerhalb des Sichtbereiches (Unten) befinden sich testweise zwei Senderschalter: Alt+D schaltet auf 102,8 MHz (DLF) und Alt+D auf 88,8 MHz (WDR 5).
Der Radiobausatz
Im Si4735 werkelt die Firmware, im Arduino die Radioklasse und obendrauf wird nun noch das VB in Excel gesetzt. Die Quelldateien sind in diesem ZIP-Archiv zusammen gepackt. Gebraucht wird noch Excel etwa ab Version 11 unter Windows 32Bit (XP/Vista/7) mit eingeschalteten Makros. Diese Zip-Datei mit Radio 4 als Arduino-023 und Excel-Blatt (pde/xls) und den dazugehörenden cpp/h-Dateien (Die weiter oben funktionieren hier nicht mehr).
Die beiden Dateien mit der Endung h/cpp gehören in das Si4735-Verzeichnis unter "libraries" im Arduino-Ordner (arduino-0023\libraries\Si4735). Der Ordner mit der *.pde-Datei kann ins Arduino-Verzeichnis, die DLL (hier unten auf der Seite) gehört ins Windows-Verzeichnis. Das Excelblatt (*.xls) enthält die Makros - die auch aktiviert sein müssen - , wobei die Schnittstelle COM1: voreingestellt ist. Der Hinweis auf dem Start-Knopf sollte beachtet werden.
|
|
Weitere Software
|