PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Internetradio



peterfido
20.08.2012, 18:30
Ich arbeite gerade an einem Internetradio über den Raspberry.

Nach dieser Seite (http://blog.ziade.org/2012/07/01/a-raspberry-pi-juke-box-how-to/) die Treiber installiert.

apt-get install alsa-utils ntpdate mpg123 python-virtualenv libshout3 libshout3-dev pkg-config python-dev

In der /etc/rc.local noch den Audioausgang und die Lautstärke gesetzt.

amixer cset numid=3 1
amixer -c 0 cset numid=2 on,off
amixer set PCM 96%
aplay /usr/games/start.wav


Der letzte Befehl spielt eine kleine Wavedatei (aus dem Windows/media Ordner geklaut, die kleinste Datei) ab, so ist das Grundrauschen nach dem Start weg.

Dann habe ich mit ein paar Scripten experimentiert.

/var/scripte/mp3

#!/bin/bash
#
# MP3 Datei abspielen
#
#

text="$*"
/var/scripte/mp3stop
/var/scripte/mp3play "$text" &

exit 0
Spielt eine MP3 Datei ab.


#!/bin/sh
#
# MP3 stoppen
#
#
pkill -9 mpg123

exit 0
Beendet die akuelle MP3

Dann habe ich mir ein paar Radio URLs herausgesucht und in der Datei radiosender gespeichert

"ffn comedy"=http://rs26.stream24.org:80/stream
"Radio ffn"=http://rs35.stream24.org:80/stream
"NDR2"=http://ndrstream.ic.llnwd.net/stream/ndrstream_ndr2_hi_mp3

Aufgerufen werden diese (Hier als Beispiel ffn Comedy) per
/var/scripte/radio comedy

Das Script radio sieht so aus

#!/bin/bash
#
# Radio starten
#
#
sender="$*";
logdatei="/var/scripte/radiosender";
# Prüfen ob Konfigurationsdatei vorhanden

if [ -e $logdatei ]; then
# loginhalt=$(cat $logdatei)
text=$(grep -i "$sender" $logdatei |tail -1 | cut -d= -f1);
echo Sender: $text;
grep -i "$sender" $logdatei |tail -1 | cut -d= -f2 > "/tmp/radiourl";
/var/scripte/mp3stop
/var/scripte/radioplay "-q -@ /tmp/radiourl" &
fi

exit 0
Hier wird nach dem Teil des Sendernamens gesucht und die SenderURL in die Datei /tmp/radiourl geschrieben.

Die radioplay sieht so aus:


#!/bin/bash
#
# MP3-Radio abspielen
#
#

text="$*"
mpg123 -q -b 1024 -h 400 -d 399 $text

exit 0


Den Aufruf an sich konnte ich bisher nicht mit der mp3play kombinieren, da ich nicht herausgefunden habe, wie ich prüfe, ob im Übergabeparameter "" sind.
Das -h 400 -d 399 verlangsamt die Ausgabe minimal, da bei meinen beiden Raspberrys sonst nach einiger Zeit kurze Aussetzer kommen, da wohl etwas schneller abgespielt wird, als Daten reinkommen.

Die anderen Zwischenscripte habe ich gewählt, damit im Hintergrund abgespielt wird und der Raspberry so weitere Befehle annimmt.

Ich teste alles als User root.

Setze ich die Lautstärke auf 0 (amixer set PCM 0%), ist trotzdem noch ganz leise was zu hören. Auch sind bei voller Lautstärke Nebengeräusche vorhanden, damit kann ich aber leben.

Jetzt suche ich noch nach einer Lösung, wie ich das per RS232 steuern kann, da ich noch einen Atmega als Frontend für die Fernbedienung und das Display nutzen möchte.
Zum AVR senden ist kein Problem. Das Empfangen bereitet mir noch etwas Kopfzerbrechen. Zur Zeit öffne ich die serielle Schnittstelle mittels


#!/bin/bash
#
# Serielle Ausgabe zum AVR
#
#

#text="$*"
#echo $text > /dev/ttyAMA0

echo "17" > /sys/class/gpio/export
echo "in" > /sys/class/gpio/gpio17/direction
chmod 555 /sys/class/gpio/gpio17/value
chmod 555 /sys/class/gpio/gpio17/direction

stty 38400 -F /dev/ttyAMA0

echo "" >> /dev/ttyAMA0
echo "" >> /dev/ttyAMA0
echo "" >> /dev/ttyAMA0
echo "" >> /dev/ttyAMA0
echo "" >> /dev/ttyAMA0
echo "" >> /dev/ttyAMA0


echo "Bereit" >> /dev/ttyAMA0

(cat /dev/ttyAMA0) > /tmp/empfang

#ersetzt durch
nice -1 /var/scripte/empfangen &


exit 0
So bleibt die Schnittstelle offen und die Übertragung zum AVR ist sauber. Lasse ich den cat Befehl weg, wird jedesmal die serielle Schnittstelle geöffnet, wenn per echo gesendet werden soll und am AVR kommt meist ein CHR(255) an.

/* erledigt: Im Prinzip müsste ich jetzt statt des cat Befehls per loop die serielle immer wieder einlesen, bis CHR(13) kommt und dann auswerten. Falls da jemand helfen kann, würde ich mich freuen.


Später soll die radiosender URL Datei noch per Webbrowser änderbar sein, ist im Moment aber nicht so wichtig und stellt soweit auch kein Problem für mich dar.

peterfido
21.08.2012, 12:35
So, ich habe es in der Zwischenzeit hinbekommen.
Ich habe mir mittels gcc ein Programm erstellt, welches die serielle Schnittstelle einliest und nach dem Empfang von chr(13) in eine Textdatei schreibt und anschließend ein BashScript ausführt, welches ich nach und nach erweitere. Das BashScript löscht dann die Eingangsdatei wieder.

Gesendet wird weiterhin einfach per echo.

Kampi
24.08.2012, 09:19
Schön das es nun funktioniert :)
Ich mach mal ein Sticky dran, dann steht es oben und verschwindet nicht so schnell. Dann können es die anderen User auch mal ausprobieren.

peterfido
24.08.2012, 13:21
Hallo, danke für Sticky. Ist dann schon mein Zweiter neben dem im T-Hack Forum, wo ich das Frontend für mein jetziges Internetradio im X300T programmiert habe.

Ich bin wieder weg vom mpg123. Denn der Trick mit dem verzögerten abspielen klappt nur bei wenigen Sendern, ist mir aber erst später aufgefallen, da ich beim Testen immer ffn comedy gehört hatte...

Jetzt nutze ich den mpd, welchen ich per mpc steuere.:



apt-get update
apt-get upgrade

apt-get install mpd mpc


Das upgrade war ~118MB und benötigt, einiges an Platz. Ich habe dafür dann die 2GB SD-Karte gegen eine 8GB SD-Karte ausgetauscht..

Meine Scripte und Programme liegen alle unter /var/scripte.

Nach der Installation von mpd und mpc wird eine playlist benötigt. Diese habe ich nach /var/lib/mpd/playlists kopiert. Die Rechte gleich wieder auf 644.

chmod 644 /var/lib/mpd/playlists/radiosender.m3u
Diese sieht zur Zeit so aus:


http://rs26.stream24.org:80/stream
http://rs35.stream24.org:80/stream
http://stream.hoerradar.de:80/mp3-128_hra-nds
http://ndrstream.ic.llnwd.net/stream/ndrstream_ndr2_hi_mp3
http://ndrstream.ic.llnwd.net/stream/ndrstream_n-joy_hi_mp3
http://rbb.ic.llnwd.net/stream/rbb_fritz_mp3_m_a
http://d85195728.i.tis.core005.cdn.streamfarm.net:80/22005mdrsputnik/live/3087mdr_sputnik_live/de_96.mp3
http://www.energyradio.de/rock
http://www.energyradio.de/berlin

In mpd dann noch die Konfiguration anpassen.

nano /etc/mpd.conf
Bei audio_output habe ich folgendes angepasst:

audio_output {
type "alsa"
name "My ALSA Device"
auto_resample "no"
device "hw:0,0" # optional
format "44100:16:2" # optional
mixer_device "default" # optional
mixer_control "PCM" # optional
mixer_index "0" # optional
period_time "5000" # Soll Knackser minimieren, experimentiere noch mit dem Wert
}



Hinweis: Laut meinen Recherchen soll der analoge Audioausgang nur aktiv sein, wenn keine HDMI Verbindung steht.

Anschließend schonmal per
mpc load radiosender die Liste in mpd einlesen. Diese Liste merkt sich mpd auch über einen Neustart hinaus, bzw hängt sie bei jedem Aufruf zusätzlich hinten dran.
Testen, ob das abspielen läuft:

mpc play 1
spielt den ersten Eintrag in der Liste ab.

mpc stop
beendet die Wiedergabe, bzw. pausiert, denn mit

mpc play
wird die Wiedergabe des zuletzt gewählten Senders fortgesetzt.

Wenn das alles läuft, kann man sich der externen Ansteuerung widmen. Welche bei mir aus 3 Programmen besteht.
1. Entgegennahme der Befehle vom AVR:

// Compile with: 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>


#define BAUDRATE B19200
char MODEMDEVICE[]= "/dev/ttyAMA0"; // Schnittstelle
char FILEDEVICE[]= "/tmp/empfang.txt"; // letztes Empfangenen in Datei schreiben, für evtl. BASH-Auswertung
//char AUSFUEHREN[32]= "/var/scripte/seriell.sh"; // BASH Script zum Auswerten der Daten
//char RADIOSPIELEN[32]="nice -1 /var/scripte/radio2.sh &"; // BASH Script zum abspielen
//char RADIODEVICE[32]= "/var/scripte/radiosender"; // Senderliste
char FARBDEVICE[]="/tmp/webif.farbe"; // Dateiname für php Farben
//char SENDERDEVICE[32]="/tmp/radiourl"; // URL zum abspielen für mpg123 ablegen



/*
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 Senderliste neu laden, x=2 mpd neu starten
Px x=0 einen Senderplatz zurück x=1 einen Senderplatz hoch
Frrggbb gesetzte Farben für php in Datei ablegen
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;

struct termios newtio={};

unsigned char send()
{ int i;
int fd1;
char ret;
/* Zum Schreiben öffnen */

fd1 = open (FILEDEVICE, O_WRONLY|O_CREAT|O_TRUNC);
if (fd1 == -1)
exit (-1);
write (fd1, eingang, laenge);
close (fd1);
system("chmod 644 /tmp/empfang.txt");
if (logg==1){
printf("AVR: %s\n",eingang);
}
//system(AUSFUEHREN);
for(i=0; i < laenge; i++)
{
eingang[i] = 0;
}
laenge=0;
}

unsigned char radioein()
{
//int i=0;
char nummer[1];
// char sender_name[30]="";
char befehl[255]="";
/* FILE *f;
char Text[300]="";
f = fopen(RADIODEVICE,"r");
if(f==NULL)
{
printf("Fehler beim Oeffnen! Bitte ueberpruefe deinen Pfad!\n");
}
i=0;
while( fgets(Text, sizeof(Text), f) !=0 ){
i++;
if (i==sender){

strcpy(sender_name,"1");
strcat(sender_name,Text);
if (logg==1){
printf("Sendername: %s\n",sender_name);
}
// system(RADIOSPIELEN);

strcpy(befehl,"/var/scripte/senden ");
strcat(befehl,sender_name);
system(befehl);
}
}
fclose(f);
*/
sprintf(nummer,"%d",sender);
strcpy(befehl,"mpc -q play ");
strcat(befehl,nummer);
system(befehl);

}


unsigned char initmpd()
{
int i=0;

system("mpc stop");
system("mpc playlist > /tmp/mpdlist");
sleep(1);
FILE *f;
char Text[300]="";
char Text1[70]="";
char Text2[7]="volume";
f = fopen("/tmp/mpdlist","r");
anzahl=0;
if(f!=NULL){
fgets(Text, sizeof(Text), f);
if(strlen(Text)<2){
fclose(f);
system("mpc clear");
system("mpc load radiosender");
initmpd();
return;
}else{
anzahl=1;
while( fgets(Text, sizeof(Text), f) !=0 ){
anzahl++;
}
}
fclose(f);
if(anzahl>200){
initmpd();
return;
}
}
}

unsigned char auswerten()
{ int i;
int fd1;
int zeile;
char ret;
char farbe[6]="";

/* Zum Schreiben öffnen */
if ((strcmp(eingang,"resetreset") ==0 )){
system("reboot");
}
if ((strcmp(eingang,"B1") ==0 )){
initmpd();
}
if (eingang[0] == 70){ // F
fd1 = open (FARBDEVICE, O_WRONLY|O_CREAT|O_TRUNC); //|O_TRUNC, S_IRUSR|S_IWUSR
if (fd1 == -1)
exit (-1);

for(i=0; i<=6; i++)
{
farbe[i]=eingang[i+1];
}

write (fd1, farbe, 6);
close (fd1);
system("chmod 644 /tmp/webif.farbe");

}
if (eingang[0] == 83){ // S
zeile=eingang[1]-48; //Werte von 48-255 = 207 Sender
if (zeile == 0){ // 0
system("mpc -q play");
}else if (zeile<=anzahl){
sender=zeile;
radioein();
}
}
if (eingang[0] == 80){ // P
if (eingang[1]==49){ //1
sender++;
if (sender>anzahl){
sender=1;
}
}else{
sender--;
if (sender<1){
sender=anzahl;
}
}
radioein();
}
if (eingang[0] == 66){ // B
if (eingang[1]==48){ //0
system("mpc stop");
sleep(1);
// system("/var/scripte/senden 0"); // Macht jetzt mpcout für tatsächlichen Zustand
}
if (eingang[1]==49){ //1 Senderliste neu laden
system("mpc clear");
initmpd();
}
if (eingang[1]==50){ //2 mpd neu starten
system("/etc/init.d/mpd restart");
}
}
eingang[laenge]=10; //Zeilenvorschub
laenge++;
send();
}

unsigned char receive()
{
int res;
unsigned char buffer;

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

int init()
{
/*** 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
/*fd1 = open(FILEDEVICE, O_WRONLY);
if (fd1 < 0){
printf("Fehler beim oeffnen von %s\n", FILEDEVICE);
exit(-1);
}*/

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)
{
char c;

init();
sleep(8); //warten bis mpd gestartet ist
initmpd();

while (1)
{
c=receive();
if((c==13)){
auswerten();
}else if(c>13){
eingang[laenge]=c;
laenge++;
if (laenge >254){
auswerten();
}
}
}
close (fd);
return 0;
}



2. Befehle zum AVR senden:

// Compile with: 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()
{
/*** 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 main(int argc, char** argv)
{
char c;
char vBuf[80]="",*pBuf;
char buffer [80];
int i=0;
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]);
}
}
}
}
pBuf=buffer;
strcat(pBuf,"\r"); //CHR(13) anfügen, damit der AVR auswertet
while(pBuf && *pBuf){ //Zeichen einzeln senden
send(*pBuf++);
}
}
close (fd);
return 0;
}



3. mpd überwachen und Status, bzw Titel zum AVR senden.


// Compile with: gcc /var/scripte/mpcout.c -o /var/scripte/mpcout

//#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>


char Textalt[75]="";
//char SENDERDEVICE[32]="/tmp/mpcout";

unsigned char aktualisieren()
{
int i=0;
int o=0;
char befehl[255]="";
system("mpc current > /tmp/mpcout");
sleep(1);
FILE *f;
char Text[300]="";
char Text1[75]="";
// char Text2[7]="volume"; //Ausgabe, wenn nichts gespielt wird - wegen Parameter current nicht mehr nötig.
f = fopen("/tmp/mpcout","r");
if(f!=NULL){
fgets(Text, sizeof(Text), f);
if(strlen(Text)<2){
strcpy(Text1,"");
if((strcmp(Text1, Textalt)) != 0){
strcpy(Textalt, Text1);
system("/var/scripte/senden 0");
}
}else{
o=0;
for (i=0;i<strlen(Text);i++){
Text1[o]=Text[i];
//if (Text[i]==32||Text[i]==45||Text[i]==46||Text[i]>=48&&Text[i]<=59||Text[i]>=65&&Text[i]<=90||Text[i]>=97&&Text[i]<=122||Text[i]>=127&&Text[i]<=252){ //Parsen
o++;
//}
if (o>74){
break;
}
}
Text1[o]='\0';
if((strcmp(Text1, Textalt)) != 0){
strcpy(Textalt, Text1);
// if(strncmp(Text,Text2,6) != 0){ //wegen Parameter current nicht mehr nötig.
strcpy(befehl,"/var/scripte/senden '1"); // \" funktioniert nicht, ' schon
strcat(befehl,Text1);
strcat(befehl,"'");
system(befehl);
// } //wegen Parameter current nicht mehr nötig.
}
}
}
fclose(f);
}

int main(int argc, char** argv)
{
char c;
/*f (argc > 1){
logg=1;
printf("Logging Modus ein %s\n",LOGDEVICE);
}*/

while (1)
{ sleep(2);
aktualisieren();
}
return 0;
}


Kompiliert habe ich die Programme per gcc. Wenn der Code für den einen oder anderen optimierungsbedürftig aussieht, kann er das gerne machen. Diskussionen über C-Programmierung bitte in einem anderen Thread. Ich bin froh, dass es bisher fehlerfrei läuft. C gehört nicht zu meinen Favoriten.

Aufgerufen werden diese in der /etc/rc.local: (Ausschnitt)


nice -1 /var/scripte/empfangen &
nice -1 /var/scripte/mpcout &
/etc/init.d/mpd restart
/etc/init.d/apache2 restart
aplay /usr/games/start.wav

Der vorletzte Befehl startet den Apache nochmal neu, was hier eigentlich nicht von nutzen ist, und der letzte siganlisiert dann über die Lautsprecher, dass der Raspi fast fertig hochgefahren ist.


Hinweis:
Ich nutze die UART auf der Stiftleiste. Für einen USB-Seriell Adapter sind folgende Schritte nicht notwendig!
Damit die UART frei ist, müssen 2 Dateien angepasst werden. Achtung: Vorher sichern. Wenn was schief geht, kann ich die SD-Karte in meinem NAS mounten und die Sicherungen zurückspielen...
/boot/cmdline.txt:


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


/ect/inittab: Folgende Zeile auskommentieren


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

zu


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

Und natürlich auch alle anderen Zeilen, welche auf ttyAMA0 zugreifen wollen.

ePyx
24.08.2012, 13:23
Warum schreibst du eigentlich keinen Wiki-Artikel über dein Projekt? Den Sticky-Thread kannst du dann ja als Möglichkeit zur Diskussion nutzen.

Kampi
24.08.2012, 13:35
Warum schreibst du eigentlich keinen Wiki-Artikel über dein Projekt? Den Sticky-Thread kannst du dann ja als Möglichkeit zur Diskussion nutzen.

Dem muss ich zustimmen. Wäre toll wenn du es ins RN-Wiki zum Thema "Embedded Linux Einstieg" integrieren könntest (neuer Wikiartikel und beim Embedded Linux Einstieg nen Link o.ä. zu dem Thema).
Natürlich nur wenn du Zeit und Lust dazu hast :)

peterfido
24.08.2012, 13:40
Im Prinzip wäre das dann diesen Thread nochmal zusammenzufassen?

Das sehe ich mir demnächst mal näher an.

Kampi
24.08.2012, 14:04
Ja genau. Ein paar Erklärungen noch was du wo gemacht hast, so das ein Anfänger das problemlos nachmachen kann :)

peterfido
24.08.2012, 14:08
Ja, das geht. Allerdings wird es noch etwas dauern, da ich dann dafür auf einer jungfräulichen SD-Karte alles nochmal neu machen werde, damit ich nichts vergesse.

peterfido
24.08.2012, 22:21
Da die Software jetzt soweit steht, kümmere ich mich grad um die Hardware. Den Bufferunderrun habe ich wie erwartet bei HDMI nicht. Da das Signal nicht durch den DAC geschickt wird. Das Rauschen ist auch bei HDMI vorhanden, bis die Mixer Settings greifen. Danach absolute Stille und Radio kommt sauber rüber. Da ich jedoch nicht den ganzen Tag den Fernseher anhaben möchte, habe ich mir eben einen HDMI VGA Adapter mit Audioausgang und Netzteil bestellt. Allerdings nicht den billigen, sondern den, der soviel kostet wie der Raspi selbst auch. Wegen dem beigelegten Netzteil und der überwiegend guten Bewertungen bei Amazon.

Man könnte natürlich auch Softwareseitig dem Bufferunderrun im Analogbetrieb entgegenkommen und einen Player mit Pitch control wählen oder, so wie ich es bisher mache, einfach den Play Befehl nochmal ausführen, dann wird neu gepuffert. Dass der Puffer leer war, habe ich daran erkannt, dass beim neustarten fast keine Verluste waren, also minimal nach der Stelle des neustarten weiterging. Man könnte den Leerlauf - Effekt herauszögern, wenn man einen großen Buffer sowie Mindestgröße einstellt. Dann dauert es aber auch jedesmal beim Erststart, Senderwechsel oder halt Neustart. Das war mir dann auf Dauer zu blöd.

Ich frage mich gerade, wie ihr das genau mit dem Wiki Artikel meint. Soll es da mehr um Embedded Linux und Software Entwicklung gehen, oder viel mehr auf das spezielle Internetradio eingehen? Ich würde da eher das Internetradio wählen oder halt die Kombination des Raspi mit einem AVR, bzw. allgemein die Kommunikation per UART, da dem Raspi egal ist, wer tatsächlich die Daten abschickt.

Internetradio wäre mehr ein spezieller Anwendungsfall, wobei er auch ohne Internet als MP3 Player dienen kann. MP3 über LAN (NAS) spielt er schonmal ab. Man könnte auch eine große SD-Karte, einen USB Stick oder USB-Festplatte nehmen. Wobei bei MP3 keine Probleme mit leerlaufenden Puffern auftreten.

Da braucht es dann nur noch ein Display, ein paar Tasten und natürlich einen Verstärker. Schon hat man eine mobile Jukebox. OK - Die Software fehlt dann auch noch. Oder man bedient den mpd per Smartphone-App oder Browser. Beides habe ich nicht probiert, bin aber bei meinen Recherchen auf die Möglichkeiten gestossen.

Die Tasten kann man auch durch eine Funktastatur ersetzen. Ich habe hier eine kleine weiße mit Touchpad und einem USB-Empfänger vom Lidl (vor 2 Jahren oder so gekauft), welches unter der gui (startx) sogar funktioniert. So braucht es dann nur noch ein Display für den komfortableren mobilen Betrieb.

peterfido
25.08.2012, 14:31
Es geht voran. Den HDMI Analog Adapter habe ich storniert. Die Audioausgabe erfolgt jetzt per ~14 EUR Speedlink USB Stick (Meldet sich als C-Media an), welchen ich noch hinten in meinem NAS stecken hatte. Der Ton ist frei von Knacksern und spielt seit heute Morgen stabil durch. Ich habe noch einen anderen USB Stick probiert (von Medion, mit Lautsprechern bei, etwa 4EUR von ebay), dieser erzeugt nicht ganz so guten Sound. Dadurch, dass er aktiv ist (für die Lautsprecher einen Verstärker integriert hat), ist er eh nicht für den Anschluß an den Line-In Eingang der HiFi - Anlage geeignet.

Um die Tonausgabe über den USB-Sound-Stick zu schicken, habe ich die /etc/mpd.conf angepasst:



audio_output {
type "alsa"
name "My ALSA Device"
auto_resample "no"
device "hw:1,0" # optional
format "44100:16:2" # optional
mixer_device "default" # optional
mixer_control "PCM" # optional
mixer_index "0" # optional
}
sowie



mixer_type "software"


Die Software habe ich um ein paar Befehle erweitert. Die mpd Initialisierung erfolgt jetzt per BASH Script, dass lässt sich schneller anpassen.

Kampi
25.08.2012, 14:51
Das hört sich ja schon richtig cool an :D
Ein Radio würde mir auch gut gefallen....habe im Moment auch das "Problem", dass ich den Fernseher und meine HD-Box anhaben müsste um Radio zu hören. Eventuell steige ich auch mal auf ein Raspberry Pi als Radio um. Von den Möglichkeiten her bietet das natürlich um einiges mehr.
Kannst du damit auch die Radiosendung aufnehmen? Gibt es da eine entsprechende Funktion?

peterfido
25.08.2012, 15:06
Die Idee mit dem USB Adapter kam mir heute Morgen nach dem Aufwachen. Es lohnt sich also manchmal doch eine Nacht darüber zu schlafen. Die Bestellung des HDMI Adapters konnte ich noch rechtzeitig stornieren, was mit einer freundlichen Bestätigungsmail quittiert wurde.

Gute Frage, das habe ich nicht probiert. Es gibt aber für den mpd einige Plugins (http://mpd.wikia.com/wiki/Music_Player_Daemon_Wiki). Man kann auch die Ausgabe per RAW Daten an ein Device schicken. Aber auch das habe ich nicht getestet. Radioaufnahmen mache ich mit dem Phonostar Player Plus am PC.

Jetzt stöbere ich erstmal durchs Wiki und entscheide, wie weit ich da aushole. Eine 2GB SD-Karte habe ich schon bereit liegen. Bin ja mal gespannt, ob alles darauf passt, wenn man es neu macht. Den Artikel selbst werde ich frühestens Morgen angehen, da ich dafür dann doch etwas mehr Ruhe brauche und heute Abend bin ich schon gesellschaftlich verpflichtet.

Kampi
25.08.2012, 15:16
Die Idee mit dem USB Adapter kam mir heute Morgen nach dem Aufwachen. Es lohnt sich also manchmal doch eine Nacht darüber zu schlafen. Die Bestellung des HDMI Adapters konnte ich noch rechtzeitig stornieren, was mit einer freundlichen Bestätigungsmail quittiert wurde.

Gute Frage, das habe ich nicht probiert. Es gibt aber für den mpd einige Plugins (http://mpd.wikia.com/wiki/Music_Player_Daemon_Wiki). Man kann auch die Ausgabe per RAW Daten an ein Device schicken. Aber auch das habe ich nicht getestet. Radioaufnahmen mache ich mit dem Phonostar Player Plus am PC.

Jetzt stöbere ich erstmal durchs Wiki und entscheide, wie weit ich da aushole. Eine 2GB SD-Karte habe ich schon bereit liegen. Bin ja mal gespannt, ob alles darauf passt, wenn man es neu macht. Den Artikel selbst werde ich frühestens Morgen angehen, da ich dafür dann doch etwas mehr Ruhe brauche und heute Abend bin ich schon gesellschaftlich verpflichtet.

Keine Eile! :D
Ja so eine Mütze Schlaf wirkt manchmal wahre Wunder. Mir sind auch schon oft Lösungen für diverse Probleme mitten in der Nacht oder auf der Arbeit wenn ich gerade nicht daran gedacht habe, eingefallen.

peterfido
27.08.2012, 17:49
So, der Wiki Artikel (http://www.rn-wissen.de/index.php/Raspberry_PI:_Internetradio) ist erstmal fertig. Jetzt lass ich mich überraschen, was da noch geändert wird. ;)
Damit nichts verloren geht, stelle ich jetzt hier auch nochmal die aktuellen Quellcodes ein.

empfangen


// 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;
}



senden


// 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;
}


mpcout (Der Überwacher)


// 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;
}


radio2.sh (Initialisert den mpd)


#!/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


autostart.sh (wird von rc.local aufgerufen)


#!/bin/bash
#
# Autostart
#
#

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

peterfido
21.09.2012, 14:17
Nach dem neuesten Update (apt-get update && apt-get upgrade) vom 18.09.2012 sind die Audioaussetzer am analogen Anschluß verwschwunden. Allerdings hört man den Raspi über den Anschluß noch arbeiten, also leises Pulsen. Weiter kommt ein lautes Knacken (auch wenn per "mpc volume 10" die Lautstärke auf gering gesetzt wurde) durch den Anschluß, wenn man den mpd stoppt. Ist also nicht so für den Anschluß an die Hifianlage geeignet. Über eine USB-Soundkarte hat sich, soweit icht das beobachten konnte, nichts geändert.

Kampi
21.09.2012, 15:31
Wie ist den der Sound so?
Ist die Qualität gut?

peterfido
21.09.2012, 18:22
Wenn man laut hört, sind die Nebengeräusche gering. Stellt man allerdings die Lautstärke des Raspi niedrig ein und dafür den Verstärker hoch, dann stören die Nebengeräusche doch. Dieses allerdings, wie gesagt, über den analogen Ausgang. Per USB Soundkarte ist die Qualität wesentlich besser als per Analogausgang, hat sich aber mit dem Update für mich nicht verändert. Per HDMI übrigens auch.

Ich bin übrigens grad nur am PC, da ich das Backup zurück auf die SD-Karte spiele, denn das gestern aktualisierte System hat sich eben zum 2. Mal aufgehangen. Zuerst war es heute morgen nicht erreichbar und eben gerade ist im Wohnzimmer das Radio ausgegangen und auch die Weboberfläche war nicht mehr erreichbar. Davor lief der Raspi über eine Woche ohne Neustart durch. Da mit dem "alten" System alles lief, spiele ich es grad zurück. Der Raspi im Schlafzimmer (Radiowecker-Projekt) läuft seit gestern noch durch. Wenn sich da nichts ändert, bleibt dort das aktualisierte System drauf.

Der Raspi im Wohnzimmer war mein erster und ich habe da viel dran getestet. Den im Schlafzimmer hatte ich komplett neu aufgesetzt, als ich den Wiki-Eintrag erstellte. Möglich, dass sich irgendein Modul, welches nur auf Wohnzimmer-Raspi installiert ist, nicht mit den Neuerungen von gestern verträgt.

Allerdings weichen auch die Programme voneinander ab. Im Schlafzimmer ist ein anderes Gehäuse und vor allem Display verbaut, so dass sich die Ansteuerung und Auswertung der Daten vom AVR unterscheidet. Im Schlafzimmer ist der AVR der Chef und überwacher. Sollte mal die Internetverbindung nicht stehen, wird zuerst versucht eine MP3 abzuspielen. Scheitert auch dieses (wird erkannt, wenn der Raspi sich nicht meldet), dann wird einfach ein Summer aktiviert, also in jedem Fall versucht, der Weckpflicht nachzukommen. Einzig bei Stromausfall bleibt das Gerät stumm. Das ist dann aber auch nicht soo schlimm, denn zusätzlich weckt noch ein Handy, welches seit dem Kauf des Smartphones plötzlich übrig war...

Der dritte Raspi im Arbeitszimmer, welcher Radio über den analogen Ausgang ausgibt, läuft seit dem Einschalten heute Mittag mit dem aktualisiertem System durch. Diesen nutze ich zu Test- und Bastelzwecken.

Die Wohn- und Schlafzimmer-Raspis sind Model B Revision 1 (0002). Der dritte ist Model B Revision 2 (0004).

peterfido
10.01.2013, 21:48
Seit dem 1.1. stimmen die Adressen für ffn nicht mehr. Dafür läuft bei mir im Arbeitszimmer ein 3. Raspi zuverlässig mit akzeptablem Klang per Line.Out ohne Knackser seit der Raspbian Version vom September (Oder war es doch schon Oktober??).

Ich versuche gerade günstig ein LCD zu "schießen" um dann per LCD4Linux dieses Display anzusteuern. Zur Zeit steuere ich den 3. Raspi per Webbrowser oder SSH. Wenn das Display fertig ist, kommt evtl. eine Fernbedienung dazu. Diese wird dann per AtTiny 2313 ausgewertet und sendet nur die empfangenen Codes an den Raspi. Das sind aber mittelfristige Projekte, da noch zwei mit höherer Priorität anstehen. Wobei die Fernbedienung (die Empfängerschaltung) nur noch zusammengebaut werden muss, da ich schon 2 davon einsetze.

Kampi
10.01.2013, 22:46
Hey,

schau mal hier:

http://kampis-elektroecke.de/?page_id=2631

Wäre das keine schöne alternative zu einem LCD?
Den kannst du wohl auch direkt mittels USB versorgen.

peterfido
11.01.2013, 23:28
Danke für den Tip!
Im Prinzip wohl schon. Allerdings möchte ich LCD4Linux nutzen und das LCD soll auch an andere Geräte wie z.B. das NAS angeschlossen werden können.

gesamtplan
13.01.2013, 22:52
Hallo,

ein sehr gelungenes Projekt und mein erster tieferer Kontakt mit dem Raspberry Pi. Ich hab mir gleich den MPD installiert und mal losgelegt. Jetzt habe ich ein WWW Radio für meine Stereoanalage, welchen ich nicht ganz so elegant nur mit dem Smartphone steuere.

Ich bin gerade dabei eine Anbindung an mein Autoradio zu basteln. Da ich auch den MPD dafür benutzen will und die originalen Radiotasten zum steuern haben will, habe ich eine Frage zum "empfangen" Code.

Ich habe leider eine etwas komplexere Schnittstelle mit Umsetzer von 1-Draht Bus auf RS232. Ich bekomme die Daten schon mal ins Board rein mit einem etwas angepassten "empfangen.c", wo ich schon mal heilfroh bin.

Senden geht auch schon zumindest über Shell.

Leider kommen immer mehrere Zeichen auf einmal an und dann ist kurzzeitig wieder Ruhe auf der Schnittstelle.
Ein Beispiel in Hex
ungefähr so
18 04 FF 02 00 E1
0.1s Pause
18 04 FF 02 00 E1
2s Pause
FF FF FF FF FF FF (irgendein Code)
Pause
etc.

Als Kommunikation habe ich 9600,8e,1

Gibt es eine Möglichkeit mit der im Code verwendeten UART Kommunikation über einen Status zu erkennen wann ein Datensatz übertragen wurde bzw. gerade kein neues Zeichen am UART ankommt? Wenn das geht konnte ich die Zeichen immer sauber auslesen, da sich ja nicht kontinuierlich gesendet werden sondern Paketweise.

Ich habe auch schon versucht über einen aus meinem Chip ausgegeben Sendungsstatus (via wiringPi) ans Ziel zu kommen. Doch ist das leider zu ungenau und es werden immer weider Zeichen verschluckt. Heißt entweder werden die GPIOs zu langsam erkannt oder der Chip ist hier ungenau.

Über einen kleinen Anstoß meiner Grüzkiste wäre ich sehr froh.

Viele Grüße

Rainer

ePyx
14.01.2013, 06:22
Könntest ja blockweise a 6 Byte lesen in dem du beim read eine Struktur/einen Array und als maximale Größe 6 (bzw die Größe der Struktur/des Arrays) übergibst.
Wenn alle Bytes 0xFF sind definierst du dann, dass die Übertragung vorbei ist.

gesamtplan
14.01.2013, 06:54
Danke ePyx für deine Antwort. Blockweise auslesen kann ich leider nicht machen, da die Bytelänge und die Ruhezeiten variieren. Ich hätte gestern schon einen Datenmitschnitt posten sollen, damit es klarer wird. Abschlusszeichen ala CR hab ich auch nicht. Der letzte Wert ist zwar immer die XOR Checksumme von den empfangenen Bytes pro Block, welche man zur Plausibilitätsprüfung heranziehen kann, doch weiß ich nicht wie man das während des empfangens schon verwerten soll.



2013-01-14 06:41:53.390 18 04 FF 02 00 E1
2013-01-14 06:41:53.390 76 0C 68 39 00 82 00 10 00 05 01 00 01 BC
2013-01-14 06:41:53.405 68 04 FF 3B 00 A8
2013-01-14 06:41:53.421 68 05 18 38 01 00 4C
2013-01-14 06:41:53.421 68 10 C0 23 41 30 00 20 20 20 20 20 20 20 20 20 20 EA
2013-01-14 06:41:53.436 C8 05 80 23 42 20 0C
2013-01-14 06:41:53.436 18 0A 68 39 00 02 00 10 00 05 01 55
2013-01-14 06:41:53.670 68 04 BF 02 01 D0
2013-01-14 06:41:53.686 68 05 80 41 01 01 AC
2013-01-14 06:41:53.780 68 03 D0 5D E6
2013-01-14 06:41:57.290 18 04 FF 02 00 E1
2013-01-14 06:42:01.174 18 04 FF 02 00 E1
2013-01-14 06:42:02.843 68 05 18 38 03 00 4E
2013-01-14 06:42:02.874 18 0A 68 39 02 09 00 10 00 05 01 5C
2013-01-14 06:42:02.968 68 03 18 01 72
2013-01-14 06:42:03.015 18 04 FF 02 00 E1
2013-01-14 06:42:03.108 18 0A 68 39 02 09 00 10 00 05 15 48
2013-01-14 06:42:03.826 18 0A 68 39 09 09 00 10 00 08 99 C2
2013-01-14 06:42:05.417 18 04 FF 02 00 E1
2013-01-14 06:42:06.977 68 05 18 38 01 00 4C
2013-01-14 06:42:06.993 C8 05 80 23 42 20 0C
2013-01-14 06:42:07.040 68 10 C0 23 41 30 00 20 20 20 20 20 20 20 20 20 20 EA
2013-01-14 06:42:07.040 C8 05 80 23 42 20 0C
2013-01-14 06:42:07.055 18 0A 68 39 00 02 00 10 00 08 99 C0
2013-01-14 06:42:09.676 18 04 FF 02 00 E1

ePyx
14.01.2013, 07:34
Hast du dort irgendwelche Events ausgelöst? Also Tasten gedrückt oder so? Ein wenig Redundanz ist ja schon enthalten : Die Sequenzen 18 04 FF 02 00 E1, 68 05 18 38 01 00 4C und 68 10 C0 23 41 30 00 20 20 20 20 20 20 20 20 20 20 EA tauchen ja schon öfter auf.

Würde halt gucken was sich bei einem Tastendruck oder einer anderen Einstellung bei den gesendeten Daten ändert und das Protokoll weiter analysieren.

hans99
14.01.2013, 10:28
Hallo,da Linux ein Time sharing System ist, kann es zu diesen Pausen kommen. Du kannst versuchen, die Priorität deines Programmes zu erhöhen.Mit "nice -n15 Deinprogramm" starten, oder bei laufendem Programm mit "renice -n15 Deinprogramm".Vielleicht hilft das etwas.

gesamtplan
14.01.2013, 17:32
Danke für eure Antworten.

das Timing wann die Blöcke kommen ist nicht kritisch. Sie werden alle vom RasPi erkannt.
Die Events kommen von den Steuergeräten auf dem Bus, heißt dem Radio und dem CD-Wechsler Emulator.
Das passt. Das einzige was ich im Moment noch nicht hinbekomme ist, das "empfangen.c" so anzupassen, dass wenn ein Block übertragen ist, dass heiß Ruhe auf dem Bus einkehrt die Daten auszulesen und in einen String zu schreiben.

Es müsste doch irgendwie möglich sein das der UART vom Raspberry merkt, wann ein Zeichen ankommt und wann kein Zeichen mehr ankommt und dementsprechend einen Status setzt. Wenn dieser Status auf 1 geht und dann wieder auf 0 ist der Block übertragen und damit kann man dann einen String bauen. Diesen Datensatz kann man dann auswerten.

Viele Grüße

Rainer

ePyx
14.01.2013, 17:55
Kannst du mal bitte den Quellcode für die Parametrisierung des tty-Devices und für das Empfangen posten?

Mich interessiert dabei eigentlich mehr wie du den filedescriptor erstellst und wie du die Daten einliest. Ob du SIGIO etc benutzt oder ob du nur pollst etc.

gesamtplan
14.01.2013, 18:43
Hab im Moment eigentlich noch einen fast unverändertes empfangen.c. Habe nur ein paar Änderungen an der Konfiguration der UART Schnittstelle gemacht. Und das beste ist, ich weiß nicht mal ob ich polle, da ich die Config des UART noch nicht gerafft hab und leider auch kein super C-Programmierer bin. Also verzeiht bitte meine Unwissenheit.

Den Rest des Codes und den Aufbau von peterfido habe ich mittlerweile verstanden. Von Zeile 45 bis 210 passiert erstmal nichts für mich interessantes, da dort ja nur die empfangenen Zeichen vom Atmel interpretiert werden. Das muss ich sowieso komplett umschreiben, da ich andere Codes verwende.

Ich hab folgendes geändert:
Die UART configuration also 9600,8e,1 und ein paar Dinge damit ich was auf dem Bildschirm zu sehen bekomme. Derzeit wird immer nur ein Zeichen abgeholt, was auf jeden fall noch geändert werden muss.

Wenn ich den Code richtig interpretiere lässt das Ding den UART komplett offen, da er in der while Schleife bleibt und der fd(close) danach kommt. Mhm...

Ziel ist wie im vorigen Beitrag beschrieben irgendwie hinzubekommen, dass die Blöcke erkannt werden und das in einem Puffer (255 Zeichen) abgeholt werden können. Den String für den Puffer könnte ich ja dann in der receive Funktion mittels schleife zusammenbauen und zurückgeben oder in eine globale Variable schreiben.

Nur leider stehe ich da im Moment einfach etwas auf dem Schlauch, weil ich nicht ganz verstehe was in der int init() Funktion passiert.



gcc -o empfangen_gpio_wiring -l rt empfangen_gpio_wiring.c -lwiringPi


#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>


//WiringPi include für SEN/STA Erkennung
#include <wiringPi.h>


#include <stdio.h>
//Konfig RASPI USART
#define BAUDRATE B9600 // 9600 Baud definieren für 8E1 I-Bus
char MODEMDEVICE[]= "/dev/ttyAMA0"; // Schnittstelle
int getit; //Nachrichtenblock angekommen
/*
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);

getit = fd;
return buffer;
}

int init() //Schnittstelle parametrieren und öffnen für I-Bus 9600 8E1
{
//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 | PARENB; //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 old 1 */

tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &newtio);
//getit = O_NDELAY;
return fd;
}

int main(int argc, char** argv) //Programmstart
{
//new
int pin = 7; //GPIO 4
int value = 0; // Deklaration Eingangswert GPIO4
getit = 0;
printf ("Raspberry Pi wiringPi test program\n") ;

if (wiringPiSetup () == -1)
exit (1) ;
//new end Check wiring pi

pinMode(pin,INPUT);



char c;
init(); //Schnittstelle UART parametrieren und öffnen
sleep(5); //warten bis mpd gestartet ist
initmpd(); //mpd auf definierten Zustand bringen
while (1)
{

value = digitalRead (pin);
printf("Status Schnittstelle %d \n", getit );

//printf("SEN/STA: %d\n", value); // BUSRUHE?

c=receive();
//if((value==1)){
printf("MESSAGE %X\n", c); // c in Hex ausgeben %d = Dezimal %H = Hex

//}


/*
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();
}
}
printf("The answer is %X\n", c); // c in Hex ausgeben %d = Dezimal %H = Hex
*/




}

close (fd);
return 0;
}




Grüße Rainer

ePyx
14.01.2013, 19:09
Der Code sieht sehr nach zusammen kopiert und entsprechend erweitert aus. Was schon recht doof ist, der bei termios der aktuelle Zustand des Terminals nicht gespeichert, sondern einfach überschrieben wird.

Das TTY-Device kann man als O_NONBLOCK deklarieren. Um zu erkennen ob Bytes im Puffer der seriellen Schnittstelle sind, kann man Signale benutzen. Das einfachste wäre das SIGIO-Signal, welches aufgerufen wird, sobald Daten eintreffen. Eine andere Möglichkeit um zu Prüfen ob Daten verfügbar sind ist :




int bytes_avaiable;ioctl(serial_file_descriptor, FIONREAD, &bytes_available);

Wobei das kein komplettes Beispiel ist, daher sollte man sich die manpages ansehen.

PS : Das Ausführen der Befehle mit System kann im Übrigen hunds-gefährlich werden, da sollte man eher einen bestehenden Prozess forken.

gesamtplan
14.01.2013, 19:27
Danke für deine Info. Dein PS verstehe ich leider nicht, da ich bin ich wohl zu wenig bewandert. Das mit der Deklaration von O_NONBLOCK habe ich zusätzlich vom MODEMDEVICE auch schon probiert. Da kommt aber dann erstmal immer eine 0 und keine Daten mehr an. Ich muss mir das wohl oder übel noch näher ansehen.

ePyx
14.01.2013, 19:39
Mit meinem PS meinte ich, dass es sehr gefährlich sein kann Befehle per System auszuführen. Beispielsweise durch Pufferüberläufe etc. . Daher gibt es die Möglichkeit Prozesse mit fork zu starten und ihnen Parameter zu übergeben bzw. sie während der Laufzeit zu steuern.

gesamtplan
15.01.2013, 19:05
Hallo Daniel,

ich hab den Code jetzt mal aufgeräumt und alles rausgeworfen was ich im Moment nicht brauche.



// Compile gcc -o serial_wiring -l rt serial_wiring.c -lwiringPi

#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>
//WiringPi include für SEN/STA Erkennung
#include <wiringPi.h>
//Konfig RASPI USART
#define BAUDRATE B9600 // 9600 Baud definieren für 8E1 I-Bus
char MODEMDEVICE[]= "/dev/ttyAMA0"; // Schnittstelle wählen
int getit; //Nachrichtenblock angekommen
int fd; // File descriptor
struct termios newtio={};

unsigned char receive() //Zeichen empfangen
{

int res;
unsigned char buffer;

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

getit = fd;
return buffer;
}

int init() //Schnittstelle parametrieren und öffnen für I-Bus 9600 8E1
{
//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 | PARENB; //setzt Einstellungen vom UART
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 old 1 */
newtio.c_cc[VSTART] = 0; /* Ctrl-q startet Datenübertragung */
newtio.c_cc[VSTOP] = 0; /* Ctrl-s stoppt Datenübertragung*/

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

int main(int argc, char** argv) //Programmstart
{
//new
int pin = 7; //GPIO 4
int value = 0; // Deklaration Eingangswert GPIO4
getit = 0;
printf ("Raspberry Test UART+wiringPi\n") ;

if (wiringPiSetup () == -1)
exit (1) ;
//Fehlerrückgabe wiringPi

pinMode(pin,INPUT); //wiringPi GPIO4 Pin7 deklarieren


char c;
init(); //Schnittstelle UART parametrieren und öffnen
//sleep(5); //nix tun

while (1)
{

value = digitalRead (pin);
printf("Status SEN/STA %d \n", value );

//printf("SEN/STA: %d\n", value); // BUSRUHE?

c=receive();
//if((value==1)){
printf("MESSAGE %X\n", c); // c in Hex ausgeben %d = Dezimal %H = Hex


}

close (fd);
return 0;
}




Welche manpages meinst du? In der "open" hab ich nichts entdeckt. Wie vielleicht schon erwähnt ich bin leider kein Profi deshalb wohl auch die dummen Fragen.

Wie geht das genau mit SIGIO bzw in welchem man finde ich das?

Grüße

Rainer

ePyx
15.01.2013, 19:45
Gibt keine dummen Fragen. Nur zusammen kopieren, sich danach wundern das nichts geht und dann fragen ist der falsche Weg. Bin auch kein Profi und habe mir das auch selbst beibringen müssen, da ich es benutzen wollte.

Bei SIGIO handelt es sich um ein Signal, welches an den Prozess (also dein Programm) gesendet wird, sobald auf einem Filedescriptor ein I/O-Event stattfindet. Ähnlich funktioniert es bspw. wenn man CTRL+C sendet, dass wird
auch als Signal an den Prozess geschickt. Damit ist es dann möglich trotz des Abbruchs das Programm sauber zu beenden. Signale haben Nichts mit open oder anderen Syscalls zu tun, daher wirst du dort auch nichts finden.

Beispiel : http://www.tldp.org/HOWTO/Serial-Programming-HOWTO/x115.html , Beispiel 3.3 Asynchronous Input

Ein Beispiel auf Deutsch findet sich beispielsweise hier : http://www.cs.hs-rm.de/~linn/vpdv01/robzilla/doku/addendum.htm

Als Tipp kann ich nur noch geben, dass man die manpages auch auf deutsch installieren kann.

gesamtplan
15.01.2013, 20:04
Naja ich dachte bei Fragen zu dem hier geposteten Code auf dessen Basis ich aufsetzen wollte ist keine so schlechte Idee, da ich bis auf die serielle Com eigentlich dachte alles verstanden zu haben. Dass das gleich so ans eingemachte geht, wenn man Anfang und Ende bestimmen will, war mir dann doch nicht Bewusst. Aber hey werde das schon irgendwie hinbekommen.

Immerhin funktioniert schon mal die Hardware und die Umsetzung auf den K-Line Bus des Autos. Da werde ich die Komm auch noch hinkriegen...

Und auf den Lerneffekt freu ich mich schon. Will ja nicht das jemand für mich mein Programm schreibt. Naja senden wird dann hoffentlich einfacher ;-).

Danke für die Links und viele Grüße

Rainer

ePyx
15.01.2013, 20:37
Und auf den Lerneffekt freu ich mich schon. Will ja nicht das jemand für mich mein Programm schreibt. Naja senden wird dann hoffentlich einfacher ;-).


Wollte ich damit auch nicht sagen, nur für klare Verhältnisse sorgen. Das Thema serielle Schnittstelle ist unter Linux halt um einiges gewichtiger, da sich dort früher halt alles abgespielt hat und es auch nichts anderes gab. Senden ist wirklich um einiges einfacher, von daher brauchst dir da nicht allzu viele Gedanken machen. Aber auch beim empfangen gibt es nicht so viel zu beachten, mit dem Gröbsten habe ich dich ja nun beworfen. :)

Im Übrigen ist die Netzwerkprogrammierung noch um einiges trickreicher, da man sehr oft fragmentierte Datenströme hat und das Empfangen in der Regel nicht so schön einfach geht. Besonders wenn Daten möglichst synchron sein sollen.

peterfido
16.01.2013, 18:35
In die serielle unter Linux musste ich mich auch erst reinlesen. Bei mir läuft das alles aber ohne bemerkbare Verzögerung.

Ich habe in der Zwischenzeit ein Projekt fertiggestellt, welches auf dieses hier aufbaut. Dabei handelt es sich um einen Radiowecker. Den Radioteil übernimmt der Raspberry Pi. Der Chef ist aber der AVR. Dieser steuert das LCD an und wertet eine Tastaturmatrix sowie Fernbedienung aus. Das Protokoll AVR<>Raspbi habe ich etwas optimiert. Es wird zuerst ein Startzeichen gesendet und dann der eigentliche Befehl, zuletzt noch ein Abschlußzeichen. Dann wird gewartet, oder der andere jeweils entsprechend antwortet.

Z.B. wenn der AVR die Weckzeit erreicht, "sagt" er zum Raspbi: Sender 1. Dieser stellt dann den Sender ein und antwortet mit dem aktuellen Sendernamen sowie der Senderplatznummer. Bleibt diese aus, versucht es der AVR nochmal. passiert immmer noch nichts, dann kommt der "Notfallplan" und ein lapidarer Summer wird angesteuert. Dieses war bisher aber nur bei den Tests vorgekommen, wo ich die Internetverbindung gekappt hatte.

So ähnlich könntest Du es auch lösen. Ansonsten läuft der Empfänger wie aufm Seite 1 beschrieben. Kommt ein Zeichen größer chr(13) an, dann wird es dem Einganspuffer angehängt. Kommt das "Habe fertig-Zeichen" (Im Urzustand chr(13)) dann wird auch sofort ausgewertet. Das geht alles so schnell, das keine Verzögerung bemerkbar ist.

Bei der neuen Version ist das Start und Endezeichen außerhalb der normal vorkommenen Zeichen (<13). Wenn natürlich bei Dir alle Zeichen vorkommen können, dann brauchst Du ein anderes Protokoll. Um die Kommunikation zu testen, habe ich den Raspbi während der Entwicklungsphase per Terminalprogramm (Putty) angesprochen und die Antworten kamen auch ohne Verzögerung. Da könntest Du mit testen, wo die Pausen entstehen.

Negative Erfahrungen habe ich gemacht, als ich die ttyAMA0 nach jedem Sendevorgang wieder geschlossen hatte. Da kamen dann undefinierte Zeichen an. Wird der Port offen gelassen, läuft es ohne diese Probleme. Meine Vermutung ist, dass beim Öffnen der Schnittstelle am Raspbi irgendwelche Signal(flanken) erzeugt.

Wenn statt der ttyAMA0 ein USB-Seriell Wandler genutzt wird, treten die Probleme auch nicht auf. So gehe ich in einem anderen Projekt vor, welches jede Minute einmal die Uhrzeit zu einer externen großen LED Anzeige schickt.

gesamtplan
17.01.2013, 17:55
Wenn ich ein Abschlusszeichen hätte wäre alles in Butter. Dann könnte man die Routine so machen. Leider schickt der I-Bus keine Abschlusszeichen. Als Abschlusszeichen kommt immer die XOR Checksumme vom Datensatz der empfangen wurde. Auf den kann ich aber schlecht auswerten, da es auch passieren kann dass der HEX Wert der Checksumme irgendwo sonst vorkommt und dann läuft der Auswerter los. Ich brauche wie Daniel oben schon erwähnt hat entweder ein SGIO Event.

Da lässt sich aber leider das Beispiel
http://www.cs.hs-rm.de/~linn/vpdv01/robzilla/doku/addendum.htm
nicht kompilieren und bricht mit einem Fehler ab, bei saio.sa_mask = 0; ab, den ich leider noch nicht interpretieren kann.



aserial.c:46:22: error: incompatible types when assigning to type â__sigset_tâ from type âintâ


und das



ioctl(fd,FIONREAD, &bytes_avaiable);


hab ich wohl an der falschen Stelle eingefügt. Ich bin gerade mal am wursten woran das liegen kann. Canonical Mode habe ich ausprobiert. das wäre was wenn man wieder ein Abschlusszeichen hat. Aber das geht ja wieder nicht. Mhm...

Muss mal weiterchecken.

gesamtplan
19.01.2013, 18:54
So Lösung. Ist zwar nicht ganz sauber, doch das liegt am CD-Wechsler Emulator, der mir im Moment noch ein Signal liefert, damit das Radio die Tasten frei gibt. Ich hab hier immer wieder schlechtes Timing vom Emulator, da sich das Mistding nicht an die Konventionen hält und es in herzlich wenig interessiert, ob am Bus was los ist oder nicht.

Aber hey. Ich hab den Code soweit fertig, dass er zumindest mal zulässt, das ich mit den vorwärts rückwärts Tasten des Autoradios den MPD bedienen kann. Die Grundfunktion ist geschaffen und jetzt geht die Arbeit los.

Ich poste den Code einfach und hoffe den Thread nicht unnötig aufzublasen. So und jetzt heißts erstmal wieder die Nase in meine C-Bücher zu vergraben, um das ganze noch etwas eleganter hinzubekommen. Irgendwie muss noch der aktuelle ID-Tag in Laufschrift auf das Radiodisplay und schaltbare Playlisten wären auch noch nett. Wie ich das machen soll weiß ich allerdings noch garnicht. Das gibt der mpc nämlich nicht so einfach her...



// Compile gcc -o serial_wiring -l rt serial_wiring6.c -lwiringPi

#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>
//WiringPi include für SEN/STA Erkennung
#include <wiringPi.h>
//Konfig RASPI USART
#define BAUDRATE B9600 // 9600 Baud definieren für 8E1 I-Bus
char MODEMDEVICE[]= "/dev/ttyAMA0"; // Schnittstelle wählen
//int fd; // File descriptor
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={};
int bytes_avaiable;
int rx_buffer_cnt = 0;
#include <sys/ioctl.h> //IO

unsigned char initmpd() //Schnittstelle parametrieren und öffnen
{
char befehl[30]="";
char s[2]="";
lautst=95;
system("mpc play"); //InitScript für mpd
sleep(1);
system("mpc play > /tmp/current"); // ID-Tags von aktuellem Titel ablegen
sleep(1);
system("mpc lsplaylists > /tmp/lsplaylists"); //Playlist zwischenspeichern
sleep(1);
FILE *f;
char Text[300]="";
char Text1[70]="";
char Text2[7]="volume";
f = fopen("/tmp/lsplaylists","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);
}
}


int init() //Schnittstelle parametrieren und öffnen für I-Bus 9600 8E1
{
//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 | PARENB; //setzt Einstellungen vom UART auf 8E1
newtio.c_iflag = IGNPAR | IGNCR | IXON ;
newtio.c_oflag = 0;
newtio.c_lflag = 0;//ICANON; /* 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 old 1 */
newtio.c_cc[VSTART] = 0; /* Ctrl-q startet Datenübertragung */
newtio.c_cc[VSTOP] = 0; /* Ctrl-s stoppt Datenübertragung*/

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

int main(int argc, char** argv) //Programmstart
{
//new
int pin = 7; //GPIO 4
int value = 0; // Deklaration Eingangswert GPIO4

printf ("Control Raspberry Pi via BMW Headunit E46 wiringPi\n") ;

if (wiringPiSetup () == -1)
exit (1) ;
//Fehlerrückgabe wiringPi

pinMode(pin,INPUT); //wiringPi GPIO4 Pin7 deklarieren


//char c;
init(); //Schnittstelle UART parametrieren und öffnen
initmpd(); //MPC starten
sleep(3); //nix tun



while (1)
{


//value = digitalRead (pin);
//printf("Status SEN/STA %d \n", bytes_avaiable );

//printf("SEN/STA: %d\n", value); // BUSRUHE?
//printf("RX_Länge %d",rx_length);


// Filedescriptor für BMW Radiotasten
if (fd != -1)
{
unsigned char rx_buffer[9]; //8 Bytes aus UART Puffer holen
//unsigned char i_bus_buffer[25]; // String zum zusammenführen von 4 rx_buffer
//i_bus_buffer Nullen
rx_buffer_cnt = 0;
for (rx_buffer_cnt; rx_buffer_cnt < 9; rx_buffer_cnt++)
{
rx_buffer[rx_buffer_cnt] = 0;
}
int rx_length = read(fd, (void*)rx_buffer, 8); //Filestream, buffer to store in, number of bytes to read (max)


if (rx_length < 0)
{
//An error occured
printf("UART RX error\n %i", rx_length);
}
else if (rx_length == 0)
{

//No data waiting
//I-Bus Puffer Nullen
rx_buffer_cnt = 0;
for (rx_buffer_cnt; rx_buffer_cnt < 9; rx_buffer_cnt++)
{
rx_buffer[rx_buffer_cnt] = 0;
}
}
else
{
//Bytes empfangen --> Darstellung




// int i = 0; // Zähler für Stringverknüpfer
// int j = 0; // + 8 nehmen um rx_buffer nach inten zu kriegen
// for(i=0; i<1; i++){

// i_bus_buffer [0 + j] = rx_buffer [0];
// i_bus_buffer [1 + j] = rx_buffer [1];
// i_bus_buffer [2 + j] = rx_buffer [2];
// i_bus_buffer [3 + j] = rx_buffer [3];
// i_bus_buffer [4 + j] = rx_buffer [4];
// i_bus_buffer [5 + j] = rx_buffer [5];
// i_bus_buffer [6 + j] = rx_buffer [6];
// i_bus_buffer [7 + j] = rx_buffer [7];

// j = j + 8; // für nächste Runde um 8 verschieben
//mach nix
//
//}

// Integer Arry mit 8 Charaktären
unsigned int rx_buffer_int [9];

rx_buffer_int [0] = (int)rx_buffer [0];
rx_buffer_int [1] = (int)rx_buffer [1];
rx_buffer_int [2] = (int)rx_buffer [2];
rx_buffer_int [3] = (int)rx_buffer [3];
rx_buffer_int [4] = (int)rx_buffer [4];
rx_buffer_int [5] = (int)rx_buffer [5];
rx_buffer_int [6] = (int)rx_buffer [6];
rx_buffer_int [7] = (int)rx_buffer [7];
rx_buffer_int [8] = (int)rx_buffer [8];

if (rx_buffer_int[0] == 104 && rx_buffer_int[1] == 5 && rx_buffer_int[2] == 24 && rx_buffer_int[3] == 56 && rx_buffer_int[4] == 10 && rx_buffer_int[5] == 0 && rx_buffer_int[6] == 71)
{
//An error occured
system("mpc next");
}

if (rx_buffer_int[0] == 104 && rx_buffer_int[1] == 5 && rx_buffer_int[2] == 24 && rx_buffer_int[3] == 56 && rx_buffer_int[4] == 10 && rx_buffer_int[5] == 1 && rx_buffer_int[6] == 70)
{
//An error occured
system("mpc prev");
}

//char dest='a';
//int c=(int)dest;



printf("%i bytes read : %X %X %X %X %X %X %X %X %X \n", rx_length,rx_buffer[0],rx_buffer[1],rx_buffer[2],rx_buffer[3],rx_buffer[4],rx_buffer[5],rx_buffer[6],rx_buffer[7],rx_buffer[8]);

printf("%i bytes read : %i %i %i %i %i %i %i %i %i \n", rx_length,rx_buffer_int[0],rx_buffer_int[1],rx_buffer_int[2],rx_buffer_int[3],rx_buffer_int[4],rx_buffer_int[5],rx_buffer_int[6],rx_buffer_int[7],rx_buffer_int[8]);

//printf("%i bytes read : %s\n", rx_length, rx_buffer);
//I-Bus Puffer Nullen
rx_buffer_cnt = 0;
for (rx_buffer_cnt; rx_buffer_cnt < 9; rx_buffer_cnt++)
{
rx_buffer[rx_buffer_cnt] = 0;
}


//printf("%i bytes read : %X %X %X %X %X %X %X %X %X %X \n", rx_length, rx_buffer[0],rx_buffer[1],rx_buffer[2],rx_buffer[3],rx_buffer[4],rx_buffer[5],rx_buffer[6],rx_buffer[7],rx_buffer[8],rx_buffer[9]);
}


}

}
close (fd);

return 0;
}




Und zum Schluss mein Raspberry Pi spricht jetzt BMW I-Bus! Mal sehen ob das Pi auch das Fenster öffnen darf... :-)

peterfido
19.01.2013, 21:37
Playlist löschen: mpc clear
Playlist laden: mpc load NameDerPlaylist
Playlisten auflisten: mpc lsplaylists
.
.
.

Hilfe unter: mpc help

gesamtplan
25.01.2013, 21:55
So es geht vorwärts.

Das Radiodisplay zeigt was an Tasten werden getriggert und machen dem mpc Beine. Jetzt habe ich nur noch eine Frage zum Filedescriptor im senden.c

Da funktioniert prima so lange ich keine 0x00 senden will. Alles nach 0x00 wird ignoriert. Liegt wohl daran, dass der Filedesprictor die 0x00 als Anlass nimmt die Übertragung einzustellen. Macht ja meistens auch Sinn.

Muss aber leider folgende Nachricht schicken:



18 04 FF 02 00 E1


Kann mir vielleicht jemand sagen, welche Einstellung ich im filedescriptor setzen muss?



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 | PARENB; //setzt Einstellungen vom UART auf 8E1
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 */
newtio.c_cc[VSTART] = 0; /* Ctrl-q startet Datenübertragung */
newtio.c_cc[VSTOP] = 0; /* Ctrl-s stoppt Datenübertragung*/

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

Ich hab in der man nachgesehen aber leider nichts in der Richtung "ignore 0x00" beim senden gefunden.

Kann mir da vielleicht wer helfen?

Viele Grüße

Rainer

peterfido
28.01.2013, 10:26
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 | PARENB; //setzt Einstellungen vom UART auf 8E1
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 1; /* ***********RAW OUTPUT*************** */ <<<<<<<
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 */
newtio.c_cc[VSTART] = 0; /* Ctrl-q startet Datenübertragung */
newtio.c_cc[VSTOP] = 0; /* Ctrl-s stoppt Datenübertragung*/

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


Versuch mal RAW Output auf 1 zu setzen. Probiert habe ich es nicht. :cool:

gesamtplan
08.04.2013, 21:43
Hallo Peter,

nochmals vielen Dank für deine Unterstützung. Mein MPD-Server fürs Auto ist nun fertig. Ohne deinen RN-Wissen Artikel wäre es nicht so schön gegangen.

Hier eine kurze Beschreibung.
https://www.roboternetz.de/community/threads/61227-RaspberryPi-macht-BMW-E46-unsicher?highlight=bmw+unsicher



Grüße

Rainer

axgp
24.05.2015, 15:08
Einen guten Beitrag zum Thema "Internet Radio" findet man unter https://www.raspberrypi.org/magpi/issues/ in dem Heft "Issue SE1 Special" auf S. 52. Ich habe diesen Artikel als Vorlage benutzt und mir ein Internet Radio (USB-Wlan-Stick mit Antenne und Aktivlautsprecherboxen) mit dem Raspberry Pi 2B gebaut. Funktioniert ausgezeichnet in meinem lokalen Netz.

ThSteinbauer76
09.11.2018, 16:16
Geil, dankee :)

HaWe
09.11.2018, 18:35
Geil, dankee :)
das Topic ist aus 2013-2015, bitte lass die Toten ruhen - vor allem mit solchen absolut nichtssagenden Posts!