Forum: Mikrocontroller und Digitale Elektronik Problem mit Projekt eines Empfangsprogramms für Besucher


von Klas Meyer (Gast)


Lesenswert?

Moin,

ich habe hier zusammen mit dem Vermieter im Haus ein Projekt laufen, 
welches auf Tastendruck festgelegte Texte, die auf Conrad Soundmodulen 
gespeichert sind abspielt. Dieses soll vorne an der Tür Gäste empfangen. 
Nach einigem hin und her habe ich nun auch endlich eine Möglichkeit 
gefunden, wie es zuverlässig läuft. Verwendet wurde dazu das myAVR MK1 
LPT Board mit Atmega8, sowie das 4 Kanal Relais Modul "myDigitalOut". 
Letzteres ist notwendig, da die Soundmodule eine sehr hohe 
Eingangsempfindlichkeit haben und somit jetzt über die Relais nur dann 
Strom erhalten, wenn sie auch wirklich an der Reihe sind.

Quelltext:
1
#define F_CPU 3686400
2
3
#include <avr/io.h>
4
#include <util/delay.h>
5
6
unsigned char tasterFrei;
7
8
// Funktionen
9
10
void initPorts (void)
11
{
12
  DDRB = 0xFF;                       // PORTB = Ausgang
13
  PORTB = 0x00;         // Ausgänge B auf low
14
  DDRC = 0xFF;         // PORTC = Ausgang
15
  PORTC = 0x00;         // Ausgänge C low
16
  DDRD = 0x00;                       // PORTD = Eingang
17
  PORTD = 0x04;                      // PORTD = PULL-UP  
18
}
19
20
void delayLong (unsigned char i)
21
{
22
  unsigned char j;
23
  for (j = 0; j < i; j++)
24
  {
25
    _delay_ms(1000);
26
  }
27
}
28
char alleAnsagen(char i)
29
{
30
  if (i == 1)
31
  {
32
    tasterFrei = 0;
33
    PORTC |= 0x01;
34
    _delay_ms(10);
35
    PORTB = 0x01;
36
    _delay_ms(100);
37
    PORTB = 0x00;
38
    delayLong(10);//dauer ansage nr.1 (in sekunden)
39
    PORTC &= ~0x01;
40
    tasterFrei = 1;
41
  }
42
else 
43
  {
44
    if (i == 2)
45
    {
46
      tasterFrei = 0;
47
      PORTC |= 0x02;
48
      _delay_ms(10);
49
      PORTB = 0x02;
50
      _delay_ms(100);
51
      PORTB = 0x00;
52
      delayLong(9);//dauer ansage nr.2
53
      PORTC &= ~0x02;      
54
      tasterFrei = 1;
55
    }
56
  else 
57
    {
58
      if (i == 3)
59
      {
60
        tasterFrei = 0;
61
        PORTC |= 0x04;
62
        _delay_ms(10);
63
        PORTB = 0x04;
64
        _delay_ms(100);
65
        PORTB = 0x00;
66
        delayLong(18);//dauer ansage nr.3
67
        PORTC &= ~0x04;
68
        delayLong(80); //letzte verzoegerung extra lang (in sek)
69
        tasterFrei = 1;
70
        i=1;
71
      }
72
    else 
73
      {
74
        tasterFrei = 1;
75
        i=1;
76
      }
77
    }
78
    return (i);
79
  }
80
81
}
82
83
// Haupt
84
85
main (void)
86
{
87
  initPorts();  
88
  unsigned char i;
89
  i=1;
90
  unsigned char fertig;
91
  fertig = 0;
92
  unsigned char tasterFrei;
93
  tasterFrei = 1;
94
  do 
95
  {
96
    if ((!(PIND&0x04)) && (tasterFrei == 1))
97
    {
98
      alleAnsagen(i);
99
      i++;
100
    }
101
102
  }
103
  while (fertig == 0);
104
}

Nun zu meinem Problem: Das System besteht aus 3 dieser Soundmodule (also 
3 Texte), die je nach Tastendruck in festgelegter Reihenfolge abgespielt 
werden. Nach Durchlauf ist das System für eine gewisse Zeit gesperrt und 
dann wieder einsatzbereit. Drückt jemand jetzt den Taster jedoch nur 1x 
oder 2x und geht danach weg, so soll sich das Programm nach einer 
gewissen Zeit zurück auf Anfang zurücksetzen. Leider sind meine 
Programmierkenntnisse nicht so ausgereift. Meine Vermutung ist es einen 
Timer zu setzen, der global über der gesamten Schleife läuft und nach 
einer festgelegten Zeit das System unabhängig von anderen Parametern 
zurücksetzt. Allerdings habe ich keine Ahnung wie ich es hinbekomme, 
dass der Timer sozusagen im Hintergrund mitläuft.

Ich hoffe, dass jemand mir hier helfen kann.


Gruß,
Klas Meyer

von Kai S. (hugstuart)


Lesenswert?

Hi,
mach dich mal im AVR-Tutorial mit Interrupts vertraut. Das ist dort sehr 
gut beschrieben, ich denke eine Erklärung hier ist dann gar nicht mehr 
nötig.

von Klas Meyer (Gast)


Lesenswert?

Hi,

vielen Dank für den Tipp, habe mich dort mal eingelesen, habe es auch 
grundsätzlich verstanden, nur scheints in der Anwendung bei mir nicht zu 
klappen. Habe aber auch lediglich Beispiele gefunden, die mir erlauben 
vor der Main Routine zu warten, nicht, wie ich möchte, die Hauptroutine 
zurücksetzen.

Ich vermute mal dass einfach meine Programmierkenntnisse zu gering sind 
für das Projekt, werde mir wohl einen versierteren C-Programmierer 
suchen müssen.

Gruß,
Klas

von Karl H. (kbuchegg)


Lesenswert?

Klas Meyer schrieb:
> Hi,
>
> vielen Dank für den Tipp, habe mich dort mal eingelesen, habe es auch
> grundsätzlich verstanden, nur scheints in der Anwendung bei mir nicht zu
> klappen. Habe aber auch lediglich Beispiele gefunden, die mir erlauben
> vor der Main Routine zu warten, nicht, wie ich möchte, die Hauptroutine
> zurücksetzen.
>
> Ich vermute mal dass einfach meine Programmierkenntnisse zu gering sind
> für das Projekt, werde mir wohl einen versierteren C-Programmierer
> suchen müssen.

So schwer ist das nicht.
Du brauchst:
einen Timer, der in regelmässigen Zeitabständen einen Interrupt auslöst.
Diese Zeitabstände kennst du, sagen wir mal alle 100 Millisekunden.
Dann brauchst du weiters eine globale Variable. Diese wird deine Uhr.
In der Interruptroutine des Timers zählst du diese Variable um 1 hoch.
Soweit so gut.

Wenn du nun in der Hauptschleife diese Variable immer wieder abfrägst 
(die Variable volatile machen nicht vergessen) und diese Variable hat 
den Wert 10 erreicht, dann bedeutet das, das 1 Sekunde vergangen ist, 
denn 10 mal 100 Millisekunden sind 1 Sekunde. Und ganz wichtig: 1 
Sekunde, seit dem du das letzte mal diese Varíable auf 0 gesetzt hast.
Wenn du daher bei einem erkannten Tastendruck die Variable auf 0 setzt, 
dann kannst du in der weiteren Behandlung der Hauptschleife die Variable 
zb auf größer 100 abfragen und wenn der Fall eintritt, dann weißt du, 
dass seit mindestens 10 Sekunden kein weiterer Tastendruck erfolgt ist.
1
volatile unsigned char Ticks;    // hier zählt die ISR hoch
2
3
ISR(  .... )
4
{
5
  Ticks ++;
6
}
7
8
main (void)
9
{
10
  initPorts();  
11
  unsigned char i;
12
  unsigned char fertig;
13
  unsigned char tasterFrei;
14
15
  i=1;
16
  fertig = 0;
17
  tasterFrei = 1;
18
19
  // Timer initialisieren
20
   .....
21
22
  ///
23
24
  do 
25
  {
26
    if ((!(PIND&0x04)) && (tasterFrei == 1))
27
    {
28
      alleAnsagen(i);
29
      i++;
30
      Ticks = 0;
31
    }
32
33
    if( Ticks > 100 ) {
34
      i = 1;         // Grundzustand
35
      Ticks = 0;
36
    }
37
38
  }
39
  while (fertig == 0);
40
}

Du musst nur anfangen, deine geradlinige einfache Denkweise
"zuerst mach dieses, dann mach jenes, dann warte ein Weilchen und mach 
schlussendlich noch dieses" aufzugeben und in Ereignissen denken. In der 
Hauptschleife werden Ereignisse abgefragt
   ist eine Taste gedrückt
   ist eine Zeit abgelaufen
   ist ....

und dann musst du natürlich noch dafür sorgen, dass das Ereignis auch 
entsprechend eintreten kann.

von Klas Meyer (Gast)


Lesenswert?

Moin nochmal,

vielen Dank, der Quelltext hat mir schon sehr weiter geholfen, 
allerdings scheint es immer noch nicht korrekt zu sein, es lässt sich 
zwar ohne Fehler brennen, jedoch scheint er den Timer einfach zu 
ignorieren, liegt das im Ausführen innerhalb von "while(1)" ? Wenn ich 
dort allerdings nichts hineinschreibe würde er ja einfach warten bis der 
Timer am festgelegten Wert ankommt.

Mein derzeitiges Programm sieht jetzt so aus:
1
#define F_CPU 3686400
2
3
#include <avr/io.h>
4
#include <util/delay.h>
5
#include <avr/interrupt.h>
6
7
unsigned char tasterFrei;
8
9
// Funktionen
10
11
void initPorts (void)
12
{
13
  DDRB = 0xFF;                       // PORTB = Ausgang
14
  PORTB = 0x00;         // Ausgänge B auf low
15
  DDRC = 0xFF;         // PORTC = Ausgang
16
  PORTC = 0x00;         // Ausgänge C auf low
17
  DDRD = 0x00;                       // PORTD = Eingang
18
  PORTD = 0x04;                      // PORTD = PULL-UP  
19
}
20
21
22
void delayLong (unsigned char i)
23
{
24
  unsigned char j;
25
  for (j = 0; j < i; j++)
26
  {
27
    _delay_ms(1000);
28
  }
29
}
30
char alleAnsagen(char i)
31
{
32
  if (i == 1)
33
  {
34
    tasterFrei = 0;
35
    PORTC |= 0x01;
36
    _delay_ms(10);
37
    PORTB = 0x01;
38
    _delay_ms(100);
39
    PORTB = 0x00;
40
    delayLong(10);//dauer ansage nr.1 (in sekunden)
41
    PORTC &= ~0x01;
42
    tasterFrei = 1;
43
  }
44
else 
45
  {
46
    if (i == 2)
47
    {
48
      tasterFrei = 0;
49
      PORTC |= 0x02;
50
      _delay_ms(10);
51
      PORTB = 0x02;
52
      _delay_ms(100);
53
      PORTB = 0x00;
54
      delayLong(9);//dauer ansage nr.2
55
      PORTC &= ~0x02;
56
      tasterFrei = 1;
57
    }
58
  else 
59
    {
60
      if (i == 3)
61
      {
62
        tasterFrei = 0;
63
        PORTC |= 0x04;
64
        _delay_ms(10);
65
        PORTB = 0x04;
66
        _delay_ms(100);
67
        PORTB = 0x00;
68
        delayLong(18);//dauer ansage nr.3
69
        PORTC &= ~0x04;
70
        delayLong(80); //letzte verzoegerung extra lang (in sek)
71
        tasterFrei = 1;
72
        i=1;
73
      }
74
    else 
75
      {
76
        tasterFrei = 1;
77
        i=1;
78
      }
79
    }
80
    return (i);
81
  }
82
83
}
84
85
86
volatile unsigned char Ticks; //ISR zählt Variable "Ticks" in 1er Schritten hoch
87
88
ISR(TIMER0_OVF_vect)
89
{
90
Ticks ++;
91
}
92
93
// Haupt
94
95
main (void)
96
{
97
  initPorts();  
98
  unsigned char i;
99
  unsigned char fertig;
100
  unsigned char tasterFrei;
101
  
102
  i=1;
103
  fertig = 0;
104
  tasterFrei = 1;
105
  
106
  TCCR0 |= (1<<CS02)|(1<<CS00);    // ((3686400/1024)/256)*1000ms=71,11ms für 1 Overflow
107
  TIMSK |= (1<<TOIE0);
108
  sei();
109
  
110
  while(1)
111
  {
112
    
113
  do 
114
  {
115
    if ((!(PIND&0x04)) && (tasterFrei == 1))
116
    {
117
      alleAnsagen(i);
118
      i++;
119
    }
120
  
121
      if(Ticks>500){    //(71,11ms*500)/1000 = 35,55s für Reset durch Timer
122
      i=1;        //Grundzustand
123
      Ticks=0;      //Timer reset
124
      }
125
  }
126
  
127
  while (fertig == 0);
128
}

Ich bedanke mich schonmal im Voraus für die Antworten, ist wirklich sehr 
hilfreich hier.
Gruß,
Klas

von Karl H. (kbuchegg)


Lesenswert?

Klas Meyer schrieb:

> Ich bedanke mich schonmal im Voraus für die Antworten,

Jetzt fängst du als allererstes einmal an, eine äussere Form in deinen 
Source Code hineinzubringen.

Nach einer { wird alles darunteretehende um 2 Zeichen eingerückt. Bis 
zur zugehörigen }. Die kommt wieder 2 Leerzeichen nach links und dann 
geht es direkt unter dieser } in derselben Spalte weiter.

Deine Kraut und Rüben Einrückerei durchblickt doch kein Mensch mehr.

Und dann siehst du dir an, ob alle Anweisungen tatsächlich in der 
Einrückstufe sind und du sie auch tatsächlich von den entsprechenden 
Blockanweisungen (if, while, do) abhängen von denen du sie abhängig 
haben willst.

Und nein: Äussere Form wird nicht nachträglich in ein Programm 
eingebaut, sondern schon während man entwickelt. Gerade konsequent 
durchgezogenes Einrückschema macht sehr oft den Unterschied zwischen 
"Ich finde einen Fehler leicht" und "Ich finde den Fehler einfach nicht"

von der mechatroniker (Gast)


Lesenswert?

Was mir spontan auffällt: in main() existiert eine lokale Variable 
tasterFrei, die natürlich die gleichnamige globale verdeckt. D.h. in 
main() wird die lokale mal abgefragt und mal auf 1 gesetzt (und nie 
wieder auf 0), in alleAnsagen() dagegen die globale auf 0 gesetzt. Ich 
bin mir zu 99,9% sicher, dass das nicht das ist, was du willst.

Kleiner Schönheitsfehler außer der Formatierung: alleAnsagen() macht 
nicht alle Ansagen, sondern bei jedem Aufruf nur eine abhängig vom 
Übergabeparameter. Das mag gewollt sein, aber dann benenn die doch bitte 
anständig.

von Karl H. (kbuchegg)


Lesenswert?

1
volatile unsigned char Ticks; //ISR zählt Variable "Ticks" in 1er Schritten hoch
2
3
...
4
5
      if(Ticks>500){    //(71,11ms*500)/1000 = 35,55s für Reset durch Timer

und dann überlegst du dir, wie wohl eine unsigned char Variable mit 8 
Bit jemals größer als 500 werden kann :-)

von Klas Meyer (Gast)


Lesenswert?

Oh, 500 mit 8 bit ist selbstverständlich Mist :-), die Anmerkung mit der 
Variable tasterFrei ist auch durchaus berechtigt, finde es hingegen 
interessant das es überhaupt so funktioniert wie es momentan ist. 
Vermutlich lässt sich das lokale Setzen der Variable auf den Wert 1 auch 
einfach streichen, da sie ja sowieso in "alleAnsagen" was nun einfach 
"Ansage" heißt gesetzt wird auf den gewollten Wert.

Habe nun auch versucht die Anforderungen an die äußere Form zu erfüllen, 
ich denke es liegt einfach an meiner mangelnden Erfahrung und dem 
ständigen Verändern des Codes dass er immer unübersichtlicher wurde.

Ich vermute den Fehler darin, dass die letzte If-Bedingung mit in der 
while(1) steht, allerdings diese einfach rauszunehmen führt auch zu 
keinem Effekt, bzw. eher zu Fehlern des Compilers.

Aktueller Code:
1
#define F_CPU 3686400
2
3
#include <avr/io.h>
4
#include <util/delay.h>
5
#include <avr/interrupt.h>
6
7
unsigned char tasterFrei;
8
9
// Funktionen
10
11
void initPorts (void)
12
{
13
    DDRB = 0xFF;            // PORTB = Ausgang
14
    PORTB = 0x00;            // Ausgänge B auf low
15
    DDRC = 0xFF;            // PORTC = Ausgang
16
    PORTC = 0x00;            // Ausgänge C auf low
17
    DDRD = 0x00;            // PORTD = Eingang
18
    PORTD = 0x04;            // PORTD = PULL-UP  
19
}
20
21
void delayLong (unsigned char i)
22
{
23
    unsigned char j;
24
    for (j = 0; j < i; j++)
25
    {
26
        _delay_ms(1000);
27
    }
28
}
29
30
char Ansage(char i)
31
{
32
    if (i == 1)
33
    {
34
        tasterFrei = 0;
35
        PORTC |= 0x01;
36
        _delay_ms(10);
37
        PORTB = 0x01;
38
        _delay_ms(100);
39
        PORTB = 0x00;
40
        delayLong(10);      //dauer ansage nr.1 (in sekunden)
41
        PORTC &= ~0x01;
42
        tasterFrei = 1;
43
    }
44
    else 
45
    {
46
        if (i == 2)
47
        {
48
            tasterFrei = 0;
49
            PORTC |= 0x02;
50
            _delay_ms(10);
51
            PORTB = 0x02;
52
            _delay_ms(100);
53
            PORTB = 0x00;
54
            delayLong(9);      //dauer ansage nr.2
55
            PORTC &= ~0x02;
56
            tasterFrei = 1;
57
        }
58
        else 
59
        {
60
            if (i == 3)
61
            {
62
                tasterFrei = 0;
63
                PORTC |= 0x04;
64
                _delay_ms(10);
65
                PORTB = 0x04;
66
                _delay_ms(100);
67
                PORTB = 0x00;
68
                delayLong(18);      //dauer ansage nr.3
69
                PORTC &= ~0x04;
70
                delayLong(80);       //letzte verzoegerung extra lang (in sek)
71
                tasterFrei = 1;
72
                i=1;
73
            }
74
            else 
75
            {
76
                tasterFrei = 1;
77
                i=1;
78
            }
79
        }
80
          return (i);
81
    }
82
}
83
84
85
volatile unsigned char Ticks;       //ISR zählt Variable "Ticks" in 1er Schritten hoch
86
87
ISR(TIMER0_OVF_vect)
88
{
89
    Ticks ++;
90
}
91
92
93
main (void)
94
{
95
    initPorts();  
96
    unsigned char i;
97
    unsigned char fertig;
98
    unsigned char tasterFrei;
99
  
100
    i=1;
101
    fertig = 0;
102
    tasterFrei = 1;
103
  
104
    TCCR0 |= (1<<CS02)|(1<<CS00);          // ((3686400/1024)/256)*1000ms=71,11ms für 1 Overflow
105
    TIMSK |= (1<<TOIE0);
106
    sei();
107
  
108
    while(1)
109
    {    
110
        do 
111
        {
112
            if ((!(PIND&0x04)) && (tasterFrei == 1))
113
            {
114
                Ansage(i);
115
                i++;
116
                Ticks=0;
117
            }  
118
            
119
            if(Ticks>100)          //(71,11ms*500)/1000 = 35,55s für Reset durch Timer
120
            {    
121
                i=1;          //Grundzustand
122
                Ticks=0;      
123
            }
124
        }
125
    }
126
while (fertig == 0);
127
}

von Karl H. (kbuchegg)


Lesenswert?

Klas Meyer schrieb:

> Habe nun auch versucht die Anforderungen an die äußere Form zu erfüllen,

In deiner main steht (Übrigens: der return type von main ist int. Du 
solltest Compiler Warnings ernster nehmen)
1
int main()
2
{
3
 ....
4
5
     while(1)
6
     {
7
         do
8
         {
9
           ....
10
         }
11
     }
12
  while (fertig == 0);
13
}


schau doch einfach mal in welcher Einrückstufe das untere while steht. 
und dann gehst du von der } aus solange die Spalte hoch, bis du die 
öffnende { findest. Sieh dir mal an, wie das alles ineinander 
verschachtelt ist. Du trickst dich hier auf Dauer selber aus. Wozu die 
ganzen verschachtelten Schleifen? Du brauchst genau 1 Endlosschleife und 
nicht mehr!
1
int main()
2
{
3
  ....
4
5
  while( 1 )
6
  {
7
     ...
8
     Programmlogik
9
     ...
10
  }
11
}



PS:

So etwas
1
  if( Bedingung1 )
2
  {
3
  }
4
  else
5
  {
6
    if( Bedingung2 )
7
    {
8
    }
9
    else
10
    {
11
      if( Bedingung3 )
12
      {
13
      }
14
    }
15
  }

kannst du guten Gewissens so schreiben
1
  if( Bedingung1 )
2
  {
3
  }
4
5
  else if( Bedingung2 )
6
  {
7
  }
8
9
  else if( Bedingung3 )
10
  {
11
  }

letzteres ist durch die kleineren Einrückungen übersichtlicher.
Und dann siehst du plötzlich auch, dass in deiner Funktion Ansage der
1
 return i;
nur dann gemacht wird, wenn die erste if Bedingung zutrifft (auch 
erkennbar daran, dass nach dem return noch eine } kommt, die nicht ganz 
links am linken Rand steht.

PS2: return ist kein Funktionsaufruf! Kein Grund da Klammern rund um den 
Ausdruck zu machen. Du schreibst ja auch nicht
1
     i = (5);


laut Simulator tickt dein Timer. Die ISR wird auch aufgerufen.

von Karl H. (kbuchegg)


Lesenswert?

Klas Meyer schrieb:

> zwar ohne Fehler brennen, jedoch scheint er den Timer einfach zu
> ignorieren

woher weißt du das eigentlich?
Mach dir doch mal eine Ausgabe rein, die dir den Fall Timer abgelaufen 
anzeigen kann (LED brennen lassen oder sowas)

Mit 'scheint' kann man nicht arbeiten.
Also musst du dir was einfallen lassen, wie du deinem Programm auf die 
Finger sehen kannst während es arbeitet. Das kann sein 1 LED die an 
bestimmten Code-Stellen eingeschaltet/ausgeschaltet wird oder blinkt. 
Das kann eine Ausgabe auf ein LCD sein. Das könnte bei dir zb das 
Abspielen eines kleinen 'Pieep' Sounds sein, etc.
Denk dir was aus. Hauptsache du hast eine Rückmeldung vom Programm "Bin 
hier angekommen" oder "Variable hat jetzt den und den Wert" oder ....
Was immer du eben an Informationen benötigst um aus dem Zustand "es 
scheint so zu sein, dass ..." rauszukommen und in den Zustand "Ich weiß, 
dass .." überzugehen.

von Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:
> Und nein: Äussere Form wird nicht nachträglich in ein Programm
> eingebaut, sondern schon während man entwickelt. Gerade konsequent
> durchgezogenes Einrückschema macht sehr oft den Unterschied zwischen
> "Ich finde einen Fehler leicht" und "Ich finde den Fehler einfach nicht"

Genau DAS ist ja gerade die Stärke von Python. Dort werden die 
Block-/Schachtelungsebenen ausschließlich durch Einrückungen 
dargestellt.

In der meisten anderen Sprachen, die spezielle Blockbegrenzungen 
besitzen, überliest man diese nämlich auch sehr schnell, wenn der 
Quelltext ansonsten ordentlich formatiert ist. Eine schöne äußere Form 
verleitet beim Lesen und Fehlersuchen zu ziemlicher Nachlässigkeit.

Gerade in C passiert ja auch leicht der folgende Fehler:
1
if (a > 100)
2
  a = 100;

wird "einmal schnell" ergänzt um eine Debug-Ausgabe:
1
if (a > 100)
2
  printf("Bereichsbegrenzung fuer a!\n");
3
  a = 100;

Oh, b muss ja auch noch angepasst werden:
1
if (a > 100)
2
  printf("Bereichsbegrenzung fuer a!\n");
3
  a = 100;
4
  b = 0;

Und plötzlich stellt man fest, dass a und b immer verändert werden, 
wohingegen die Debug-Ausgabe nur zu den richtigen Zeitpunkten erscheint.

von Klas Meyer (Gast)


Lesenswert?

vielen Dank für die Ratschläge und kaum zu glauben, es scheint 
tatsächlich nun zu funktionieren :-).

Jetzt nur noch die Zeiten anpassen und dann kann es in einen ersten 
Praxistest gehen, bin gespannt.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.