Aus RN-Wissen.de
Wechseln zu: Navigation, Suche


Projekt Internetradio mit dem Raspberry Pi

Anbei mein "kleines" Projekt, den Raspberry Pi als Internet Radio zu betreiben. Die Steuerung erfolgt über einen AVR, welcher im Frontend seines Gehäuses im Wohnzimmer sitzt.

Das Gehäuse ist vom MediaReceiver X300T von der Telekom, welche es nicht zurück haben wollte, obwohl bei mir kein IP-TV möglich war. So habe ich ich mich damit befasst, dem Atmega88 auf dem Frontend ein eigenes Programm zu schreiben, um ihn für eigene Zwecke einzusetzen...

Man kann den Atmega88 "direkt" per Pegelwandler mit dem Raspberry verbinden.

PiPegelwandler.png

Die Schaltung ist mit bis zu 115200 Baud getestet. Bei mir wird über den 5V PIN noch der Raspberry gespeist. Jedoch ist Flachbandleitung dafür dann nicht mehr geeignet!

Benötigt

1 Raspberry Pi
1 SD-Karte (2GB aufwärts)
FTP-Software auf dem PC (Totalcommander)
Linuxfähiger Texteditor auf dem PC (Notepadd++)
optional eine USB Soundkarte (Speedlink mit C-Media Chip)
Irgendwas, was mit dem Raspberry Pi seriell kommunizieren kann. (AVR, PIC, Terminalsoftware,...)

Einige Hinweise zu nano, dem Texteditor des Raspberry

Mittels den Pfeiltasten wird navigiert. Bild auf/Bild ab blätter eine ganze Seite. Zahlen nicht über den 10er Block (Ziffernblock) der Tastatur eingeben. STRG-X beendet nano.
Falls gewünschte Änderungen gemacht wurden, kann man mit y und anschließend ENTER diese bestätigen oder falls man sich vertan hat, mit n einfach ohne zu speichern raus. Wird Putty genutzt, lässt sich der Inhalt der Zwischenablage mit einem Rechtsklick an der aktuellen Cursorposition einfügen. Man kann auch den Cursor irgendo platzieren, mit gedrückter linken Maustaste etwas markieren und dann mit Rechtsklick an der Cursorposition einfügen. Markierter Text wird automatisch in die Zwischenablage kopiert und ist in anderen Programmen verfügbar.

Projektordner

Von Vorteil ist ein Projektordner auf dem PC, worin man arbeitet und Dateien ablegt. Nach jedem reboot muss man sich natürlich wieder einloggen. Mittels Putty und ssh einfach auf das Icon am Fensterrand klicken und restart Session auswählen. Wird dieses nicht angeboten, einfach mal im Fenster ein paar Tasten drücken und ENTER hinterher. Dann sollte Putty irgendwann merken, dass am anderen Ende keiner mehr reagiert. Die ganze Zeit über benötigt der RasPi eine Internetverbindung.

Allgemeine Hinweise

Installation des Wheezy Raspian (Version 2012-15-07) per Imagewriter wie öfter beschrieben.

Die Verbindung zum Raspberry läuft bei mir per FTDI USB - Seriell Wandler. Achtung! darauf achten dass die Pegel auf 3,3V eingestellt sind! Über die Software Putty (Comport wählen, 115200 Baud, 8N1) Oder man schließt einen TV/Monitor und eine Tastatur an.

Eine externe Soundkarte hat sich bei meinen Tests bewährt. Über den analogen OnBoard Sound des RasPi gibt es hin und wieder Knackser, sowie kurze Aussetzer. Über HDMI sind diese nicht vorhanden. Wer keine HDMI - fähige Audioanlage hat, dem empfehle ich einen USB-Sound Stick. Ich nutze im Produktivsystem einen von Speedlink mit C-Media Chip und am Testsystem einen von Medion, welcher mit externen Lautsprechern und dafür eingebautem Verstärker ideal für die Tests ist. Für die Stereoanlage ist dieser Stick dann nicht mehr geeignet, da der Ausganspegel zu groß ist und beim ersten Initialisieren voll ansteht, auch wenn man die Lautstärke auf 0 setzt. Auch ist der Sound nicht ganz so (oder gerade??) berauschend, was aber auch an den Lautsprechern liegen kann.

Grundeinrichtung des Linux auf dem RasPi

Nach dem Hochfahren einloggen mittels pi und raspberry.

Als erstes mittels

sudo -i

die shell in eine rootshell verwandeln.

anschließend

raspi-config

eingeben

Die Bedienung erfolgt per Tastatur. Mit den Pfeiltasten kann man wählen und mittels TAB springt man zu den Optionsfeldern. Dort die locale auf de_DE.UTF 8 umgestellt. Die Default locale im nächsten Fenster habe ich einfach mit ok auf en_GB gelassen. Anschließend auf Set Keyboard layout Danach dann Set Timezone auf EUROPE BERLIN Bei ssh sichergestellt, dass es aktiviert ist. zuletzt expand_rootfs ausgewählt, um die gesamte SD-Karte nutzen zu können.

NEUSTART durch Unterbrechung der Stromversorung oder Eingabe von

reboot

Nach dem Einloggen erscheint womöglich gleich wieder das Config Tool. Das beende ich dann gleich wieder per Auswahl fon FINISH. Die Netzwerkeinstellungen stehen in der Datei /etc/network/interfaces. Zum Anpassen

nano /etc/network/interfaces 

eingegeben, um auf eine statische IP-Adresse zu wechseln, damit ich ihn immer unter der selben Adresse erreiche. Alternativ kann man auch im Router einstellen, dass dem Raspi per DHCP immer die selbe Adresse zugewiesen werden soll.

auto lo
iface lo inet loopback
#iface eth0 inet dhcp
iface eth0 inet static
address 192.168.2.37
netmask 255.255.255.0
gateway 192.168.2.1 


Beendet wird nano mittels STRG+X Dann die Speichern Frage mit y bestätigen und nochmals Enter, um den Dateinamen beizubehalten. Dann mittels Befehl

reboot

neu gestartet.


Jetzt im Putty auf SSH gewechselt. Ein zweites Putty Fenster beobachtet die Ausgabe des Raspi auf dem Terminal, so kann man sehen, ob Fehler gemeldet werden und wann er fertig hochgefahren ist.

Dann

apt-get update 

um die Liste zu aktualisieren.

apt-get upgrade

um das System zu aktualisieren. Die nächste Frage mit Enter bejahen (Enter bedeutet, dass der Standard, welcher groß geschrieben ist, übernommen wird (Y/n). Zwischendurch evtl. Fragen bestätige ich einfach mit ENTER. Nach der Frage, ob aktualisiert werden soll, was etwas dauert, ist erstmal Zeit für eine Kaffeepause... Diese kann auch nutzen, indem man sich einen Linux - fähigen Texteditor installiert. (Ich nutze Notepad++)

reboot
raspi-config

Jetzt den MemorySplit auf 240 MB Arbeitsspeicher und 16 MB Videospeicher ändern.

Die Frage nach dem Reboot bejahen...

Sollte sich im Laufe der Zeit die SD-Karte füllen, so kann dies am Syslog, Kernellog und Debuglog liegen. Da gibt es mehrere Wege diese zu begrenzen. Ich selbst habe /var/log ins RAM verlagert. Dieses kann allerdings Probleme bereiten, wenn ein Programm ein Log schreiben möchte, bevor der /var/log gemountet ist. Bei mir war das der Apache2.


Diesen rufe ich jetzt später nochmal manuell auf, nachdem ich das Verzeichnis /var/log/apache2 erstellt habe. In /var/scripte/autostart.sh habe ich folgendes noch eingetragen:

mkdir /var/log/apache2
/etc/init.d/apache2 restart


Um /var/log ins RAM zu verschieben, habe ich die fstab angepasst. Dazu

nano /etc/fstab

dann folgende Zeile am Ende einfügen:

tmpfs           /var/log                 tmpfs    size=10M          0       0

gibt es Probleme, kann man diese einfach mit einer Raute deaktivieren.

#tmpfs           /var/log                 tmpfs    size=10M          0       0

Grundausrüstung Software

Jetzt brauchen wir noch ein paar Programme auf dem Raspberry Pi. GCC, um eigene Programm zu erstellen (könnte schon vorhanden sein), einen FTP-Server zur bequemen Dateiübertragung und natürlich eine Software für das Internetradio. Da habe ich mich für mpd entschieden.

apt-get install gcc

Einrichtung ftp

Herunterladen und installieren, Konfiguration anpassen:

apt-get install vsftpd
nano /etc/ftpusers

Dort die Zeile root auskommentieren.

#root

Dann STRG-X, y, ENTER

Jetzt

nano /etc/vsftpd.conf

Dort die # am Anfang der Zeile #local_enable=YES entfernen

local_enable=YES

und anonymous_enable=YES ändern auf

anonymous_enable=NO

und #write_enable=YES ändern auf

write_enable=YES

Dann STRG-X, y, ENTER

Jetzt mit

service vsftpd restart

den ftp Server neu starten.

Jetzt sollte der Zugriff per ftp möglich sein.

Mediaplayer Dämon

Herunterladen und installieren des mpd sowie mpc, welches den mpd dann für uns anspricht.

apt-get install mpd mpc

Als nächstes benötigen wir eine URL Liste von Internetradios. Viele Sender erwähnen ihre Livestream-URLs nicht direkt. Ich habe die Adressen herausgefunden, indem ich einfach in der Fritzbox den Verkehr mitgeloggt (fritz.box/html/capture.html) und per Wireshark durchsucht habe.

Es gibt aber auch Internetseiten, wo einige URLs aufgelistet sind.

Zum Anfang reicht uns eine Liste mit 3 URLs.

http://www.energyradio.de/rock
http://www.energyradio.de/berlin
http://www.energyradio.de/hot

Diese wird mit einem Linuxfähigen Editor erstellt, abgespeichert als radiosender.m3u und per ftp nach /var/lib/mpd/playlists/ kopiert.

Anschließend die Rechte der Datei ändern, damit mpd darauf zugreifen kann.

chmod 644 /var/lib/mpd/playlists/radiosender.m3u

Konfiguration des MPD

nano /etc/mpd.conf

Die Zeile

bind_to_address         "localhost" 

wurde zu

bind_to_address         "127.0.0.1"

Da er anscheinend localhost nicht auflösen kann.

Dort kann man auch einen anderen Adressbereich angeben, um z.B. aus dem Netzwerk darauf zugreifen zu können.

Auskommentiert (#) habe ich folgende Zeilen:

#log_file 
#state_file

so wird das loggen abgeschaltet, mit state_file spielt der mpd sofort nach dem Start dort weiter, wo er aufgehört hat, das wollte ich nicht.

Weiter unten noch unter audio_output habe ich für den USB-Sound-Stick folgendes stehen:

audio_output {
       type            "alsa"
       name            "My ALSA Device"
       device          "hw:1,0"        # optional für Headphone-Anschluss: "hw:0,0"
       format          "44100:16:2"    # optional
       mixer_device    "default"       # optional
       mixer_control   "PCM"           # optional
       mixer_index     "0"             # optional
}

noch weiter unten wird die auskommentierte Zeile

#mixer_type             "software" 

durch wegnehmen der # aktiviert. Für den analogen Anschluss des Raspberry wird der Hardware Mixer aktiviert.

Die Zeilen

#audio_buffer_size              "2048"
#buffer_before_play             "10%"

werden auch aktiviert.

Das Encoding für die Tags ändert man weiter unten. Mit folgender Einstellung passen bei mir die meisten Titel:

id3v1_encoding                  "ISO-8859-1" #UTF-8

STRG-X, y, ENTER

/etc/init.d/mpd restart

startet den mpd mit den neuen Einstellungen neu

Test des mpd

mpc

eingeben. Wenn die Verbindung steht, sollte da sowas in der Art rauskommen:

volume:100% repeat: off random: off single: off consume: off

jetzt laden wir die Playlist:

mpc load radiosender

Die erste URL öffnen geht mit

mpc play 1

Die Zweite URL mit

mpc play 2

und so weiter.

mpc stop

beendet die Wiedergabe.

mpc play

spielt dort weiter, wo aufgehört wurde.

Vorbereitungen eigener Programme

SD-Karte sichern

Wenn das alles klappt, können wir uns der Steuerung über UART widmen. Dazu habe ich ein Programmpaket erstellt, welches die Aufrufe des mpc automatisch nach empfangenen Daten macht.

Da ich die interne UART nutzen möchte, gebe ich diese als erstes frei.

Hier wäre ein guter Punkt, wo eine Datensicherung der SD-Karte durchgeführt werden sollte. Den Raspberry Pi abschalten Die SD-Karte in einen Kartenleser stecken. Den Win32DiskImager starten Bei Device sicherstellen, dass der richtige Buchstabe ausgewählt ist Den Dateinamen angeben, unter welchem gesichert werden soll (Blaues Ordnersymbol) Den Dateinamen angeben z.B. 2012-08-26RaspiRadio.img Dann den Button READ! auswählen

Audiodatei zur Initialisierung

Es kann vorkommen, dass beim ersten Start nichts vernünftiges aus den Lautsprechern herauskommt. Daher bietet es sich an, beim Start eine kleine Datei abspielen zu lassen. Diese kann man sich z.B. recht einfach bei Windows besorgen. Ich habe mich für c:\windows\media\Windows-Hardware hinzufügen.wav entschieden. Die gewählte wav Datei am besten in den Raspberry Arbeitsordner kopieren und in z.B. start.wav umbenennen. Zur Vorbereitung für später werden unter /var zwei Ordner erstellt.

Arbeitsordner auf der SD-Karte

mkdir /var/scripte
mkdir /var/sounds/

Danach muss die Datei noch auf die SD-Karte des Raspberry. Der Einfachheit wegen per ftp nach /var/sounds. Jetzt die Datei noch für alle lesbar freigeben.

chmod -r 644 /var/sounds/start.wav

Eigener Autostart

Damit das Soundsystem (und später noch andere Programme) initialisert wird, bietet es sich an, eine eigene Startdatei zu erstellen. Sozusagen die autoexec.bat von damals.

nano /var/scripte/autostart.sh

Der Inhalt sieht erstmal so aus:

#!/bin/bash
#
# Autostart
#
# 

##sound
amixer cset numid=3 1 
amixer -c 0 cset numid=2 on,off
amixer set PCM 96%
aplay /var/sounds/start.wav
nice -1 /var/scripte/empfangen &
nice -1 /var/scripte/mpcout &

STRG-X, y, ENTER

Damit diese Datei später auch ausgeführt wird, muss sie auch aufgerufen werden und ausführbar sein. Das lässt sich in der rc.local machen.

chmod 755 /var/scripte/autostart.sh
nano /etc/rc.local

Dort in der freien Zeile über exit 0 kommt der Aufruf rein.

/var/scripte/autostart.sh


STRG-X, y, ENTER

Wenn jetzt die rc.local nochmal gestartet wird, sollten die Mixereinstellungen gezeigt die Audiodatei abgespielt werden.

/etc/rc.local

Um zu testen, ob alles läuft:

reboot

Wer möchte, kann jetzt nochmal das Putty Log analysieren, denn Putty schreibt immer noch fleißig mit. Anschließend das Logging in Putty wieder abschalten, wenn man möchte.

Hintergrundanwendungen

Programme, welche im Hintergrund weiter ausgeführt werden sollen, wie unser Empfänger und Überwacher weiter unten, werden mit nice und dem & Parameter gestartet.

nice -1 /var/scripte/empfang &
nice -1 /var/scripte/mpcout &

Wird der & Parameter nicht angegeben, wartet die BASH, bis das aufgerufene Programm beendet wurde und dann werden die nachfolgenen Befehle nicht mehr ausgeführt. Nice sorgt dafür, dass die Programmpriorität geändert wird. negative Werte erhöhen die Priorität, positive Werte verringern diese. Möglich ist -20 bis 19.

Feigeben der UART für eigene Projekte

Um die interne UART auf den GPIOs freizugeben, müssen 2 Dateien bearbeitet werden. Diese sind für den Start von Relevanz und die Bearbeitung muss demnach auch gewissenhaft durchgeführt werden. Daher evtl. nochmal die SD Karte sichern, wie oben beschrieben.

Um das Syslog abzuschalten, geht es jetzt an die /boot/cmdline.txt

nano /boot/cmdline.txt

Wie der Name schon sagt, besteht die Datei aus einer Zeile.

dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

Die interne UART heisst ttyAMA0 und dieser Teil stört für das Projekt, deswegen kommt er weg. Die neue Zeile:

dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

STRG-X, y, ENTER

Testen, ob es noch hochfährt.

reboot

Warten, bis start.wav ertönt und dann neu einloogen.

Jetzt ist noch eine Konsole über UART offen, diese lässt sich in der inittab abschalten.

nano /etc/inittab

Dort wird alles, was ttyAMA0 beinhaltet mittels # deaktiviert. Normal in der letzten Zeile eine # voran gesetzt.

#T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100

STRG-X, y, ENTER

reboot

Ist alles richtig gemacht, dürfte sich auf dem Putty, welches über die UART verbunden ist, nichts mehr tun.

Zu Fuss Audio abspielen

Um den mpd und die Befehle, welcher mpc versteht, kennen zu lernen, kann man mittels

mpc help

sich alle Befehle auflisten lassen. Die wichtigsten für das Projekt:

mpc clear
mpc load 
mpc play
mpc volume
mpc lsplaylists
mpc stop
mpc current

"mpc clear" stoppt die Wiedergabe und leert die Playliste (Nicht die Datei radiosender, sondern die interne Playliste) "mpc load NAME" lädt die Playliste NAME und hängt diese an die interne dran, wenn schon eine vorhanden ist. Also 3 Mal

mpc load radiosender

erzeugt eine Liste, welche den Inhalt 3 Mal hintereinander enthält. "mpc play" setzt die Wiedergabe an der Stelle fort, wo mit "mpc stop" angehalten wurde. "mpc play x" spielt nummer x der Playliste ab, "mpc volume 50" setzt die Lautstärke auf 50%. "mpc lsplaylists" gibt die Namen der aktuellen Playliste(n) aus. "mpc current" gibt je nach Verfügbarkeit den aktuellen Titel, Sender, Interpret aus.

gcc

gcc ist der Compiler, welcher aus C - Quellcode ausführbare Programme erstellt. Interessant ist, dass er auch laufende Programme überschreiben kann.

Den Quellcode erstelle ich mittels Notepad++. Stellt man da die Sprache auf C oder lädt Dateien, welche *.c heißen, dann wird der Code farblich hervorgehoben. Anschließend lade ich den Code per ftp in das Verzeichnis /var/scripte, welches mein allgemeines Arbeitsverzeichnis auf dem Raspberry darstellt. Es bietet sich an, wenn man größere Änderungen an einem Code vorhat, die bisherige Version zu sichern. Manchmal möchte man dcoch mal was nachschlagen oder einfach nur auf den alten Stand zurück, da man sich vielleicht doch gerade in eine Sackgasse verfahren hat.

Ich nutze allgemein nur kleingeschriebene Dateinamen, da Linux Groß- Kleinbuchstaben darin unterscheidet. Dieses gilt auch für den gcc im Quellcode. Eine Variable, welche Text heißt, wird unter text nicht gefunden. Es ist von Vorteil, Variablen beim Definieren auch gleich einen Wert zuzuteilen, sonst behalten diese ihren Wert von dem letzten Durchgang der Subroutine bei.

C gehört nicht zu meinen Favoriten, dennoch habe ich mich etwas damit beschäftigt, um diese Programme hier zu erstellen. Möglich, dass hier und da die Codes optimiert werden können, aber hier erfüllen diese ihren Zweck. Mit den Includes habe ich mich noch nicht ausführlich beschäftigt. Werden nicht benötigte angegeben, werden die compilierten Programme trotzdem nicht größer. Nicht benötigtes wird also vom compiler ignoriert.

Die Quellcodes werden nach /var/scripte kopiert. Anschließend noch die Rechte auf 644 setzen. Die vom gcc erzeugten Programme besitzen automatisch Rechte zum Ausführen.

Compiliert wird der Quelltext dann mit

gcc /var/scripte/quellcodedatei.c -o /var/scripte/exedatei

gestartet werden die Programme dann mit

/var/scripte/exedatei

Wenn man sich den Pfad jedesmal sparen will, kann man vorher auch mit

cd /var/scripte

in das Verzeichnis wechseln und sich die Pfadangabe dann sparen. Ich habe mir angewöhnt, den kompletten Pfad mit anzugeben.

Hängt ein Programm fest, kann man es mit STRG-C beenden. Hat man es im Hintergrund gestartet, z.B.

nice -1 /var/scripte/exedatei &

dann lässt es sich mit

pkill exedatei

beenden.

pidof exedatei

zeigt die PID an. Wird da ein Wert zurückgegeben, dann läuft das entsprechende Programm noch.

kill PID

beendet auch das Programm.

Man kann auch per Putty mehrere Verbindungen gleichzeitig zu dem Raspberry offen halten und z.B. auf dem einen den Quellcode per nano direkt bearbeiten und mit dem anderen die Programme zum Testen starten. Der Vorteil daran ist, dass man mit der Pfeilhoch - Taste auf der Tastatur die letzten Befehle wiederholen kann.

Gerade für Tests mit der seriellen UART empfehle ich, diese per Terminalprogramm offen zu halten und so zu testen. Putty lässt mit New Session zu, dass mehrere Instanzen davon laufen. Man kann auch Daten über den Com-Port zum RasPi senden, diese werden in Putty zwar normal nicht angezeigt, kommen aber trotzdem an. Mit Local Echo in den Settings kann man diese Daten aber auch anzeigen lassen. Bei dem Testsystem nutze ich einen FTDI, welcher 3,3V auf VIO hat. Das Produktivsystem nutzt die oben gezeigte Schaltung. Es gibt auch "richtige" Pegelwandler, nur hatte ich grad keinen zur Hand, wie das so Nachts am Wochenende halt ist.

Programme für den Zugriff

Der Initiator

Ein BASH Script initialisiert den mpd. Ich habe mich für diese spezielle Aufgabe für ein BASH Script entschieden, da es sich recht schnell an andere Gegebenheiten anpassen lässt. Ein C-Programm könnte das gleiche, müsste halt jedesmal noch compiliert werden. Dieses Script wird nach dem Start des Empfängers einmal ausgeführt.

#!/bin/bash 
#
# Radio starten
#
# 
/etc/init.d/mpd restart #Versetzt den mpd in einen definierten Zustand
mpc -q clear		#Nicht nötig, wenn man kein Status File nutzt, schadet aber auch nicht, wenn man keines nutzt...
mpc -q load radiosender	#lädt die von uns erstellte Playlist
mpc -q volume 95	#setzt die Lautstärke auf 95 %
exit 0	

Empfänger

Der Empfänger läuft im Hintergrund und wartet auf Ereignisse, um diese auszuwerten. Das können Befehle über die UART, per I2C, IR/x10 Fernbedienung oder eine einfache Tastaturmatrix bzw. einzelne Tasten sein. Ich gehe in diesem Projekt auf die UART ein.

Dazu wird die Schnittstelle parametriert (19200 Baud, 1 Stopbit, keine Parität, 8 Datenbits), geöffnet und ständig belauscht. Kommen bekannte Befehle rein, werden diese ausgewertet und entsprechend reagiert. Der zuletzt empfangene Befehl wird zusätzlich in die Datei /tmp/empfang.txt gespeichert.

// Kompilieren mit: gcc /var/scripte/empfangen.c -o /var/scripte/empfangen

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>


#define BAUDRATE B19200
char MODEMDEVICE[]= "/dev/ttyAMA0";	// Schnittstelle

/*
Sx  		x=0 aktuellen Sender neu abspielen Nach Stop oder zum neu puffern, x>49 Sender x-48 abspielen
Bx  		x=0 abspielen stoppen, x=1 mpd neu starten
Px			x=0 einen Senderplatz zurück x=1 einen Senderplatz hoch
Tx			Taste x am Gerät gedrückt
resetreset 	Reboot des Raspi
*/
char eingang[255]="";
char d[1]="";
int anzahl=0;
	
int laenge=0;
int logg=0;
int	fd;				// File descriptor
int sender;
int lautst=95;
long int time_back;
long int time_ok;

struct	termios newtio={};

unsigned char eingangleer()		//Eingangstring leeren
{
	int i;
	for(i=0; i < laenge; i++){
		eingang[i] = 0;
	}
  laenge=0;	
}

unsigned char platzlautsenden()			//Lautstärke und Senderplatz auf den kleinen Zahlen ausgeben
{						//Lautstärke 100% und Senderplatz 3 wird zu 100:03
	char nummer[3];
	int i=0;
	char befehl[255]="";

	sprintf(nummer,"%d",lautst);
	strcpy(befehl,"/var/scripte/senden 2");
	if (strlen(nummer)<3){
		strcat(befehl,"0");
	}
	if (strlen(nummer)<2){
		strcat(befehl,"0");
	}
	strcat(befehl,nummer);
	strcat(befehl,":");
	i=sender;
	if(i>99){				//Die Anzeige ist 2-Stellig
		i=99;
	}
	sprintf(nummer,"%d",i);
	if (strlen(nummer)<2){
		strcat(befehl,"0");
	}
	strcat(befehl,nummer);
	system(befehl);
}

unsigned char radioein()			//gewählten Platz in der Playlist abspielen
{ 
	char nummer[3];
	char befehl[255]="";

	sprintf(nummer,"%d",sender);
	strcpy(befehl,"mpc -q play ");
	strcat(befehl,nummer);
	system(befehl);
	platzlautsenden();
}

unsigned char initmpd()				//Schnittstelle parametrieren und öffnen
{
	char befehl[30]="";
	char s[2]="";
	lautst=95;
	system("/var/scripte/radio2.sh");	//InitScript für mpd
	sleep(1);
	system("mpc playlist > /tmp/mpdlist");	//Playlist zwischenspeichern
	sleep(1);
	FILE *f;
	char Text[300]="";
	char Text1[70]="";
	char Text2[7]="volume";
	f = fopen("/tmp/mpdlist","r");
	anzahl=0;				//Anzahl Einträge der Senderliste zählen
	if(f!=NULL){
		fgets(Text, sizeof(Text), f);
		if(strlen(Text)<2){
			fclose(f);
			return;
		}else{
			anzahl=1;
			while( fgets(Text, sizeof(Text), f) !=0 ){
				if(strlen(Text)>2){
					anzahl++;
				}
				if(anzahl>199){		//Nicht mehr wie 200
					break;
				}
			}
		}
		fclose(f);
	}
	strcpy(befehl,"/var/scripte/senden 6");		//kleine Symbole im Display anzeigen lassen
	s[0]=208;					//Antennensymbol + FM anzeigen, als Zeichen für Bereit
	strcat(befehl,s);
	system(befehl);
	strcpy(befehl,"/var/scripte/senden 7");				
	s[0]=130;					//Stereo anzeigen
	strcat(befehl,s);
	system(befehl);	
}

unsigned char abspielen()				//Wiedergabe fortsetzen. Ist noch nichts abgespielt worden, Platz 1 abspielen
{
	if (anzahl>0){
		if (sender==0){
			sender=1;
		}else{ if(sender>anzahl){
			sender=anzahl;
			}
			radioein();
		}
	}
}

unsigned char platzplus()				//Senderplatz um 1 erhöhen
{							//Bei weniger Sendern in Liste auf Platz 1 zurück
	if (anzahl>0){
		sender++;									
		if (sender>anzahl){							
			sender=1;
		}
		radioein();
	}	
}

unsigned char platzminus()				//Senderplatz um 1 verringern
{							//Bei 0 auf letzten Platz springen
	if (anzahl>0){
		sender--;									
		if (sender<1){								
			sender=anzahl;
		}
		radioein();
	}	
}

unsigned char lautsetzen()				//Lautstärke einstellen
{
	char nummer[3];
	char befehl[255]="";

	sprintf(nummer,"%d",lautst);
	strcpy(befehl,"mpc -q volume ");
	strcat(befehl,nummer);
	system(befehl);
	platzlautsenden();
}

unsigned char lauter()					//Senderplatz um 5 Prozentpunkte erhöhen
{
	if (lautst<96){
		lautst+=5;									
		lautsetzen();
	}	
}

unsigned char leiser()					//Senderplatz um 5 Prozentpunkte verringern
{
	if (lautst>5){
		lautst-=5;									
	}else{
		lautst=0;
	}
	lautsetzen();
}

unsigned char radioaus()				//Wiedergabe anhalten
{
	system("mpc -q stop");
	system("/var/scripte/senden 45");
	sleep(1);
}

unsigned char auswerten()				//Angekommene Daten auswerten
{ int i;	
  int fd1;
  int zeile;
  char ret;
  char farbe[6]="";
  
  /* Zum Schreiben öffnen */
	if ((strcmp(eingang,"resetreset") ==0 )){
		system("reboot");
	}
	if (eingang[0] == 83){                         // S
		zeile=eingang[1]-48;		       //Werte von 48-255 = 207 mögliche Sender, wird auf 200 limitiert
		if (zeile == 0){		       // bei 0 einfach weiter abspielen oder Platz 1 beginnen
			abspielen();
		}else if (zeile<=anzahl){
			sender=zeile;
			radioein();
		}
	  }
	if (eingang[0] == 84){                          // T
		if (eingang[1]>49&&eingang[1]<57){	//Wert soweit OK, Taste 1 = Powertaste und wird vom AVR ausgewertet
			if (eingang[1]==50){		//2 - OK Taste
				abspielen();
			}
			if (eingang[1]==51){		//3 - Wippe rechts
				lauter();	
			}
			if (eingang[1]==52){		//4 - BACK Taste
				radioaus();
			}
			if (eingang[1]==53){		//5 - Menü Taste
				system("/var/scripte/senden");
			}
			if (eingang[1]==54){		//6 - Wippe hoch
				platzplus();
			}
			if (eingang[1]==55){		//7 - Wippe links
				leiser();
			}
			if (eingang[1]==56){		//8 - Wippe runter
				platzminus();
			}
		}
	}
	if (eingang[0] == 80){                          // P
		if (eingang[1]==49){ 			//1
			platzplus();
		}else{
			platzminus();
		}
	  }
	if (eingang[0] == 66){                          // B
		if (eingang[1]==48){ 			//0 Wiedergabe stoppen
			radioaus();
		}
		if (eingang[1]==49){ 			//1 mpd zurücksetzen
			initmpd();
		}
	}
	eingangleer();
 }

unsigned char receive()				        //Zeichen empfangen
{
    int res;
    unsigned char buffer;

    res = read(fd, &buffer, 1);
    return buffer;
}

int init()						//Schnittstelle parametrieren und öffnen
{
    //O_RDONLY, O_WRONLY or O_RDWR -
    //O_NDELAY (geht weiter, wenn keine Daten da sind und gibt "-1" zurueck)
    // man 2 open fuer mehr Infos - see "man 2 open" for more info
    // O_NOCTTY No ControllTeleType 

    fd = open(MODEMDEVICE, O_RDONLY | O_NOCTTY);
    if (fd < 0){
        printf("Fehler beim oeffnen von %s\n", MODEMDEVICE);
        exit(-1);
    }
    memset(&newtio, 0, sizeof(newtio));
    newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;	//setzt die neuen Porteinstellungen
    newtio.c_iflag = IGNPAR;
    newtio.c_oflag = 0;
    newtio.c_lflag = 0;         /* set input mode (non-canonical, no echo, ...) */
    newtio.c_cc[VTIME] = 0;     /* inter-character timer unused */
    newtio.c_cc[VMIN] = 1;	/* blocking read until 1 chars received */

    tcflush(fd, TCIFLUSH);
    tcsetattr(fd, TCSANOW, &newtio);
    return fd;
}

int main(int argc, char** argv)		//Programmstart
{
	char c;
 	init();					//Schnittstelle parametrieren und öffnen
	sleep(5); 				//warten bis mpd gestartet ist
 	initmpd();				//mpd auf definierten Zustand bringen
	while (1)
        {
	    c=receive();		        //Zeichen holen
		if((c==13)){			//CR als Abschluß einer Zeile
			auswerten();		
		}else if(c>13&&c<128){		//Alles von ASCii 14 bis 127 wird akzetpiert
			eingang[laenge]=c;		
			laenge++;				
			if (laenge >254){	//Bei 254 Zeichen im Puffer wird automatisch ausgewertet
				auswerten();
			}
        }
    } 
    close (fd);
    return 0;
}


Sender

Der Sender läuft nicht im Hintergrund. Er wird von anderen Programmen oder per Cron Job gestartet. Er versucht, alle Parameter, welche beim Start übergeben wurden, über die UART zu schicken. Die maximale Länge ist auf 76 Zeichen begrenzt, da der AVR einen Eingangsbuffer von 80 Zeichen hat. Mehrere Parameter werden aneinandergehängt und jeweils durch ein Leerzeichen getrennt. "Es empfiehlt sich trotzdem den Text in Häkchen zu setzen", da sonst einige Sonderzeichen für Ärger sorgen können. Als letztes wird immer ein CR (ASCii 13) gesendet. Wenn der AVR dieses bekommt, wertet er die ganze Zeile aus.

Wird kein Parameter übergeben, wie ich es beim Aufruf durch einen Cron-Job mache, wird die aktuelle Uhrzeit und das Datum übertragen. Wenn der AVR als ersten Zeichen ein U bekommt, geht er davon aus, dass die Uhrzeit folgt und danach seine Uhr stellt. Ein D voran, veranlasst ihn dazu, das Datum zu setzen. Das Format ist jeweils zweistellig. Ist die Uhrzeit z.B. 16 Uhr, 4 Minuten und 28 Sekunden, wird U16:04:28 geschickt. Das Datum 27. August 2012 kommt als D27.08.12 an.

Wenn z.B. der Cron-Job und der Überwacher gleichzeitig senden wollen, erkennt der Sender, dass er in mindestens einer anderen Instanz schon läuft und wartet eine Sekunde pro zusätzlicher Instanz, bevor er sendet. So wird verhindert, dass mitten in der Uhrzeit plötzlich ein Sendertitel reinplatzt.

// Kompilieren mit: gcc /var/scripte/senden.c -o /var/scripte/senden
 
//#include <iostream>
//using namespace std;
#include <sys/types.h>			//nicht benötigtes scheint keinen Platz zu verschwenden...
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>

#define BAUDRATE B19200
char MODEMDEVICE[]= "/dev/ttyAMA0";	// !!!

int	fd;				// File descriptor
struct	termios newtio={};

unsigned char send(char c)
{
    int res=write(fd, &c, 1);
    if (res<0) printf("Fehler beim Senden\n");
    return res;
}

int init()					//Schnittstelle parametrieren und öffnen
{
    /*** Init ***/
    
    //O_RDONLY, O_WRONLY or O_RDWR -
    //O_NDELAY (geht weiter, wenn keine Daten da sind und gibt "-1" zurueck)
    // man 2 open fuer mehr Infos - see "man 2 open" for more info
    // O_NOCTTY No ControllTeleType 
    fd = open(MODEMDEVICE, O_WRONLY | O_NOCTTY);
    if (fd < 0){
        printf("Fehler beim oeffnen von %s\n", MODEMDEVICE);
        exit(-1);
    }
    memset(&newtio, 0, sizeof(newtio));
    newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;	//setzt die neuen Porteinstellungen
    newtio.c_iflag = IGNPAR;
    newtio.c_oflag = 0;
    newtio.c_lflag = 0;         /* set input mode (non-canonical, no echo, ...) */
    newtio.c_cc[VTIME] = 0;     /* inter-character timer unused */
    newtio.c_cc[VMIN] = 1;	/* blocking read until 1 chars received */

    tcflush(fd, TCIFLUSH);
    tcsetattr(fd, TCSANOW, &newtio);
    return fd;
}
int einmal()					//Sucht bereits laufende Instanzen und zählt diese
{
	int i=0;
	int anzahl=0;
	int laenge=0;
	char *ptr;
	FILE *in;
	extern FILE *popen();
	char buff[30];

	if(!(in = popen("pidof senden", "r"))){
		exit(1);
	}
	
	fgets(buff, sizeof(buff), in);
	pclose(in);
	laenge=strlen(buff);
	for(i=0;i<laenge;i++)
	{
		if(buff[i]==' ') anzahl++;
	}
	return anzahl;	
}


int main(int argc, char** argv)			//Programmstart
{
	char vBuf[100]="",*pBuf;
	char buffer [100]="";
	char buffer1 [100]="";
	int i=0;
	int anzahl;
	anzahl=einmal();
	if (anzahl>0){
		anzahl++;
		sleep(anzahl);
	}
 	init();
    if (argc == 1) {			       //Wenn nichts angegeben, dann Uhrzeit und Datum senden
		time_t rawtime;
		
		struct tm * timeinfo;
		time ( &rawtime );
		timeinfo = localtime ( &rawtime );
		strftime (buffer,80,"U%H:%M:%S",timeinfo);	//Uhh:mm:ss
		pBuf=buffer;
		strcat(pBuf,"\r");
		while(pBuf && *pBuf)				//Zeichen einzeln senden
			send(*pBuf++);

		strftime (buffer,80,"D%d.%m.%y",timeinfo);	//Dtt.mm.yy
		pBuf=buffer;
		strcat(pBuf,"\r");
		sleep(1);
		while(pBuf && *pBuf)				//Zeichen einzeln senden
			send(*pBuf++);
	}else{							//Sonst Parameter einlesen und senden	
		if(strlen(argv[1])>75){				//Puffer im AVR ist auf 80 gestellt
			strncpy(buffer,argv[1],76);				
			buffer[76]='\0';
		}else{
			strcpy(buffer,argv[1]);
			if (argc >2){
				for (i=2;i<argc;i++){
					if(strlen(buffer)+strlen(argv[i])>75){
						strcat(buffer," ");								
						strncat(buffer,argv[i],(76-strlen(buffer)));
						buffer[76]='\0';
						break;				//exit for in c...
					}else{
						strcat(buffer," ");
						strcat(buffer,argv[i]);
					}
				}
			}
		}
		int o=0;					//Umlaute anpassen, ASCii 195 verwerfen
		for(i=0;i<strlen(buffer);i++){
			buffer1[o]=buffer[i];
			if (buffer1[o]==164){			//ä
				buffer1[o]= 228;
			}
			if (buffer1[o]==188){			//ü
				buffer1[o]= 252;
			}
			if (buffer1[o]==182){			//ö
				buffer1[o]= 246;
			}
			if (buffer1[o]==132){			//Ä
				buffer1[o]= 196;
			}
			if (buffer1[o]==156){			//Ü
				buffer1[o]= 220;
			}
			if (buffer1[o]==150){			//Ö
				buffer1[o]= 214;
			}
			if (buffer1[o]==159){			//ß
				buffer1[o]= 223;
			}
			if (buffer1[o]==138){			//&
				buffer1[o]= 38;
			}
			if (buffer1[o] != 195){			//Initialisierung Umlaut
				o++;
			}
		}
		pBuf=buffer1;
		strcat(pBuf,"\r");			        //CR (ASCii13) anfügen, damit der AVR auswertet		
		while(pBuf && *pBuf){				//Zeichen einzeln senden
			send(*pBuf++);
		}
	} 
    close (fd);
    return 0;
}




Überwacher

Der Überwacher fragt alle paar Sekunden per mpc den Status des mpd ab. Ändert sich dieser, wird der aktuelle Titel zum AVR gesendet. Wurde die Wiedergabe abgebrochen, wird das auch per 0 mitgeteilt, dass der AVR wieder den Wochentag im Textbereich, sowie das Datum auf den kleinen Ziffern anzeigt.


// Kompilieren mit: gcc /var/scripte/mpcout.c -o /var/scripte/mpcout

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


char Textalt[75]="";
int anzahl=0;

int wachhund()					//Sucht bereits laufende Instanzen und zählt diese
{
	char *ptr;
	FILE *in;
	extern FILE *popen();
	char buff[30];

	if(!(in = popen("pidof mpd", "r"))){
		exit(1);
	}
	
	fgets(buff, sizeof(buff), in);
	pclose(in);
	anzahl=(strlen(buff));
}

unsigned char aktualisieren()
{ 	
	int i=0;	
	int o=0;
	char befehl[255]="";
	FILE *f;

	if(!(f = popen("mpc current", "r"))){
		exit(1);
	}
	char Text[300]="";
	char Text1[75]="";
	fgets(Text, sizeof(Text), f);
	fclose(f);
	if(strlen(Text)<2){
		strcpy(Text1,"");
		if((strcmp(Text1, Textalt)) != 0){
			strcpy(Textalt, Text1);
			system("/var/scripte/senden 0");
			system("/var/scripte/senden 45");
		}
	}else{
		o=0;
		for (i=0;i<strlen(Text);i++){
			if (Text[i]==38){				//&
				Text1[o]=138;				//+
			}else{
				Text1[o]=Text[i];
			}
			o++;
			if (o>74){
				break;
			}
		}
		Text1[o]='\0';
		if((strcmp(Text1, Textalt)) != 0){
			strcpy(Textalt, Text1);
				strcpy(befehl,"/var/scripte/senden '1");	// \" funktioniert nicht, ' schon
				strcat(befehl,Text1);
				strcat(befehl,"'");
				system(befehl);
				system("/var/scripte/senden 44");
		}
	}
}

int main(int argc, char** argv)
{	
	while (1)
        { 	sleep(2);
			anzahl=0;
			wachhund();
			if (anzahl <2){
				system("/var/scripte/radio2.sh");
			}
			aktualisieren();
	} 
    return 0;
}


Weblinks

Siehe auch