- 3D-Druck Einstieg und Tipps         
Seite 4 von 5 ErsteErste ... 2345 LetzteLetzte
Ergebnis 31 bis 40 von 46

Thema: Internetradio

  1. #31
    Benutzer Stammmitglied
    Registriert seit
    24.06.2007
    Alter
    44
    Beiträge
    61
    Anzeige

    Powerstation Test
    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.
    Wenns brennt, dann brennts....

  2. #32
    Erfahrener Benutzer Roboter Experte Avatar von ePyx
    Registriert seit
    14.05.2008
    Ort
    Falkensee
    Beiträge
    700
    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.
    Grüße,
    Daniel

  3. #33
    Benutzer Stammmitglied
    Registriert seit
    24.06.2007
    Alter
    44
    Beiträge
    61
    Hallo Daniel,

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

    Code:
    // 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
    Wenns brennt, dann brennts....

  4. #34
    Erfahrener Benutzer Roboter Experte Avatar von ePyx
    Registriert seit
    14.05.2008
    Ort
    Falkensee
    Beiträge
    700
    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-Pro...OWTO/x115.html , Beispiel 3.3 Asynchronous Input

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

    Als Tipp kann ich nur noch geben, dass man die manpages auch auf deutsch installieren kann.
    Grüße,
    Daniel

  5. #35
    Benutzer Stammmitglied
    Registriert seit
    24.06.2007
    Alter
    44
    Beiträge
    61
    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
    Wenns brennt, dann brennts....

  6. #36
    Erfahrener Benutzer Roboter Experte Avatar von ePyx
    Registriert seit
    14.05.2008
    Ort
    Falkensee
    Beiträge
    700
    Zitat Zitat von gesamtplan Beitrag anzeigen
    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.
    Grüße,
    Daniel

  7. #37
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    18.05.2007
    Ort
    Berlin
    Alter
    53
    Beiträge
    765
    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.
    Wenn das Herz involviert ist, steht die Logik außen vor! \/

  8. #38
    Benutzer Stammmitglied
    Registriert seit
    24.06.2007
    Alter
    44
    Beiträge
    61
    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/...u/addendum.htm
    nicht kompilieren und bricht mit einem Fehler ab, bei saio.sa_mask = 0; ab, den ich leider noch nicht interpretieren kann.

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

    Code:
    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.
    Wenns brennt, dann brennts....

  9. #39
    Benutzer Stammmitglied
    Registriert seit
    24.06.2007
    Alter
    44
    Beiträge
    61
    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...

    Code:
    // 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...
    Wenns brennt, dann brennts....

  10. #40
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    18.05.2007
    Ort
    Berlin
    Alter
    53
    Beiträge
    765
    Playlist löschen: mpc clear
    Playlist laden: mpc load NameDerPlaylist
    Playlisten auflisten: mpc lsplaylists
    .
    .
    .

    Hilfe unter: mpc help
    Wenn das Herz involviert ist, steht die Logik außen vor! \/

Seite 4 von 5 ErsteErste ... 2345 LetzteLetzte

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

LiFePO4 Speicher Test