Forum: Mikrocontroller und Digitale Elektronik ESP - in der Schleife gefangen


von Kurt (Gast)


Lesenswert?

Nabend

Der ESP soll als Server (AP) gesteuert werden.
Über verschiedene URLs möchte ich vier Zustände aufrufen:

Motor aus, Motor rechts, Motor links -> klappt problemlos
In der vierten Variante soll der ESP zwei Endpunkte erkennen 
(digitalRead)
und die Laufrichtung ändern.

Dafür habe ich eine while-Schleife genutzt.
Leider komme ich aus dieser Schleife nicht mehr heraus:
1
void loop()
2
{
3
  WiFiClient client = server.available();
4
  if (!client) {
5
    return;
6
  }
7
  String req = client.readStringUntil('\r');
8
 // Serial.println(req.indexOf("/vier"));
9
  client.flush();
10
11
  Serial.println("xxx");  Serial.println(req);  Serial.println("ccc");
12
  
13
  if (req.indexOf("/eins") != -1)
14
  {
15
    digitalWrite(LS_out_0, LOW);
16
    digitalWrite(LS_out_1, LOW);
17
  }
18
  else if (req.indexOf("/zwei") != -1)
19
  {
20
    digitalWrite(LS_out_0, LOW);
21
    digitalWrite(LS_out_1, HIGH);
22
  }
23
  else if (req.indexOf("/drei") != -1)
24
  {
25
    digitalWrite(LS_out_0, HIGH);
26
    digitalWrite(LS_out_1, LOW);
27
  }
28
  else if (req.indexOf("/vier") != -1)
29
  {
30
    digitalWrite(LS_out_0, LOW);
31
    digitalWrite(LS_out_1, HIGH);
32
    while (req == "GET /vier HTTP/1.1")
33
    {
34
      if (digitalRead(LS_in_0) == 0)
35
      {
36
        digitalWrite(LS_out_1, LOW);
37
        delay(500);
38
        digitalWrite(LS_out_0, HIGH);
39
      }
40
      if (digitalRead(LS_in_1) == 0)
41
      {
42
        digitalWrite(LS_out_0, LOW);
43
        delay(500);
44
        digitalWrite(LS_out_1, HIGH);
45
      }
46
    }
47
  }

Hier nur der Code des Loops.

Und die Ausgabe des seriellen Monitors:
1
xxx
2
GET /vier HTTP/1.1
3
ccc
4
xxx
5
GET /favicon.ico HTTP/1.1
6
ccc
7
xxx
8
GET /favicon.ico HTTP/1.1
9
ccc


Meine Idee war, das die while-Schleife nur so lange durchlaufen wird,
wie "req" den entsprechenden Inhalt besitzt.

Sind die Favicon Abfragen des Browsers das Problem?
Oder gibt es eine elegantere Möglichkeit?

von Felix C. (felix_c13)


Lesenswert?

Kurt schrieb:
> Meine Idee war, das die while-Schleife nur so lange durchlaufen wird,
> wie "req" den entsprechenden Inhalt besitzt.

Tut es aber, wie auch anders, wenn du den Inhalt von req in der Schlaufe 
nicht veränderst.

Wieso erstellst du durch die Benutzung von millis() nicht ein 
Pseudo-Betriebssystem in dem bestimmte Routinen nur in bestimmten 
Intervallen ausgeführt werden? Dann könntest du Einerseits den Webserver 
handeln und auf der anderen Seite die Routine laufen lassen.

Gruss

Felix

von Kurt (Gast)


Lesenswert?

Das verstehe ich jetzt noch nicht.

Zur Erklärung:
Ich möchte den 4. Fall (der Schleife) durch das Aufrufen eines anderen 
Falles wieder abbrechen können.

von Michael U. (amiga)


Lesenswert?

Hallo,

dann vereinbare eine Statusvariable, mekr Dir den Zustand bis der sich 
ändert.

loop() mit
1
  WiFiClient client = server.available();
2
  if (!client) {
3
    return;
4
  }

ist ohnehin keine gute Idee, auch wenn es so jetzt scheinbar klappt.
1
void loop()
2
{
3
  WiFiClient client;
4
  char status = 0;
5
6
  while(1)
7
  {
8
    if (server.available())
9
    {
10
       ... hier Deine Abfragen
11
       if (req.indexOf("/eins") != -1)
12
       {
13
         status = 1;
14
       }
15
      ... usw.   
16
    } 
17
  if (status = 1)
18
  {
19
    digitalWrite(LS_out_0, LOW);
20
    digitalWrite(LS_out_1, LOW);
21
  }
22
23
...
24
}

als Ansatz. Eigentlich noch eine Variable old_status, weil Du die 
digitalWrite() Sachen ja nur setzen mußt, wenn status sich gegenüber dem 
vorigen Druchlauf verändert hat.

PS: warum nimmst Du nicht den ESP9266Webserver?
Wenn man das mehr als nur so nutzen will, macht er das Leben leichter, 
man sollte schließlich Webanfragen auch beantworten, der Nutzer will ja 
wissen, ob alles geklappt hat.

Gruß aus Bberlin
Michael

von Kurt (Gast)


Lesenswert?

Guten Morgen

Entweder verstehe ich deine Antwort einfach nicht,
oder ich habe es immer noch nicht geschafft mein Problem richtig zu 
beschreiben.

Michael U. schrieb:
> WiFiClient client = server.available();

Das ist doch nur eine Sicherheitsabfrage und hat mit meiner Schleife 
nichts zu tun, oder?

Michael U. schrieb:
> der Nutzer will ja
> wissen, ob alles geklappt hat.

Das kann der Nutzer sehen :-)
Der ESP steuert einen Motor und ich möchte diesen Motor, welcher eine 
Seilbahn bewegt, gerne mit dem Handy steuern.
Vor, zurück und stopp, klappt.

Fall vier:
An jedem Ende des Seils ist ein Stück schwarzes Klebeband umwickelt.
Dieses wird durch eine Reflexlichtschranke erkannt und dient somit als 
Endschalter.
Somit soll die Seilbahn immer hin und her fahren.
Aber ich muss diesen Fall ja auch wieder stoppen können.

von Daniel A. (daniel-a)


Lesenswert?

Der Arduino loop ist eine Endlosschleife. Das server.available() liefert 
einen WiFiClient zurück, falls einer Vorhanden ist. WiFiClient client = 
client.readStringUntil('\r') liefert dann die erste Zeile der HTTP GET 
Anfrage zurück, und speichert diese in req. In der while Schleife wird 
client.readStringUntil nie aufgerufen, und req nie gesetzt. Ich Empfehle 
die Whileschleife wegzulassen, die Schleife in Funktionen auszulagern, 
und Statusvariablen einzuführen.
1
 // ungetestet
2
enum e_move_state {
3
  STATE_NO_MOVE,
4
  STATE_MOVE_UP,
5
  STATE_MOVE_DOWN,
6
  STATE_MOVE_AUTO
7
};
8
9
enum e_move_state move_state = STATE_NO_MOVE;
10
11
void setState( enum move_state state ){
12
  if( move_state == state )
13
    return;
14
  switch( state ){
15
    case STATE_NO_MOVE;
16
      digitalWrite(LS_out_0, LOW);
17
      digitalWrite(LS_out_1, LOW);
18
      break
19
    case STATE_MOVE_AUTO:
20
    case STATE_MOVE_UP:
21
      digitalWrite(LS_out_0, LOW);
22
      digitalWrite(LS_out_1, HIGH);
23
      break;
24
    case STATE_MOVE_DOWN:
25
      digitalWrite(LS_out_0, HIGH);
26
      digitalWrite(LS_out_1, LOW);
27
      break;
28
    default:
29
      return;
30
  }
31
  move_state = state;
32
}
33
34
void handleRequest(){
35
36
  WiFiClient client = server.available();
37
  if( !client ) return;
38
39
  String req = client.readStringUntil('\r');
40
 // Serial.println(req.indexOf("/vier"));
41
  client.flush();
42
43
  Serial.println("xxx");  Serial.println(req);  Serial.println("ccc");
44
45
  if( req.indexOf("/eins") != -1 ){
46
    setState(STATE_NO_MOVE)
47
  }else if( req.indexOf("/zwei") != -1 ){
48
    setState(STATE_MOVE_UP);
49
  }else if( req.indexOf("/drei") != -1 ){
50
    setState(STATE_MOVE_DOWN);
51
  }else if( req.indexOf("/vier") != -1 ){
52
    setState(STATE_MOVE_AUTO);
53
  }
54
55
}
56
57
void controlCheckMovement(){
58
  if( move_state == STATE_NO_MOVE )
59
    return;
60
  if( move_state == STATE_MOVE_AUTO ){
61
    if( !digitalRead(LS_in_0) ){
62
      digitalWrite(LS_out_1, LOW);
63
      delay(500);
64
      digitalWrite(LS_out_0, HIGH);
65
    }
66
    if( !digitalRead(LS_in_1) ){
67
      digitalWrite(LS_out_0, LOW);
68
      delay(500);
69
      digitalWrite(LS_out_1, HIGH);
70
    }
71
  }else{
72
    if( !digitalRead(LS_in_0)
73
     || !digitalRead(LS_in_1)
74
    ) setState( STATE_NO_MOVE );
75
  }
76
}
77
78
void loop()
79
{
80
  
81
  handleRequest();
82
  controlCheckMovement();
83
84
}

von Michael U. (amiga)


Lesenswert?

Hallo,

Kurt schrieb:
> Entweder verstehe ich deine Antwort einfach nicht,
> oder ich habe es immer noch nicht geschafft mein Problem richtig zu
> beschreiben.
Naja, ich sollte wohl erst meinen Kaffee zuende trinken bevor ich 
schreibe...

> Michael U. schrieb:
>> WiFiClient client = server.available();
>
> Das ist doch nur eine Sicherheitsabfrage und hat mit meiner Schleife
> nichts zu tun, oder?
Stimmt, habe ich falsch interpretiert.

Ich versuche es nochmal...
  String req = client.readStringUntil('\r');
Du benutzt Streamread um einen Requst des Webclients zu bekommen.
Das kommt entweder zurück, wenn '\r' erkannt wurde oder wenn der Timeout 
zuschlägt.
Eigentlich gehört da noch eine Abfrage davor, ob überhaupt ein client 
was will, außerden ein Timeout, falls der Request nicht komplett ist.
Da verläßt Du Dich nur auf .readStringUntil

Auf einen ankommenden neuen Requst kann in Schleife vier nie reagiert 
werden, er wird ja nicht bearbeitet. Du müßtest also dort mit 
client.available()  schauen, ob ein neuer Request angekommen ist, diesen 
mit client.readStringUntil('\r'); abholen und auswerten.
Das geht real aber nicht wirklich. client-Abfrage, Reqest holen und 
auswerten und die Aktionen Deiner Seilbahn mußt Du trennen.
Es müssen ja beide "parallel" laufen, also client abfragen und 
gewünschten Status setzen, wenn was da ist, sonst weitermachen.
Seilbahn nach status-Wert bedienen, wenn nötig und wieder zur 
client-Abfrage.

> Fall vier:
> An jedem Ende des Seils ist ein Stück schwarzes Klebeband umwickelt.
> Dieses wird durch eine Reflexlichtschranke erkannt und dient somit als
> Endschalter.
> Somit soll die Seilbahn immer hin und her fahren.
> Aber ich muss diesen Fall ja auch wieder stoppen können.
1
  if (client.available())  // Daten da?
2
  {
3
    String req = client.readStringUntil('\r'); // holen
4
5
    ... hier Deine Abfragen
6
    if (req.indexOf("/eins") != -1)
7
    {
8
      status = 1;
9
    }
10
      ... usw.   
11
  } 
12
// jetzt um die Seilbahn kümmern
13
  if (status = 1)
14
  {
15
    digitalWrite(LS_out_0, LOW);
16
    digitalWrite(LS_out_1, LOW);
17
  }
18
19
...
20
}
Bei 4 müssen die delay() raus, das darf nicht blokierend sein.
Also z.B. per mills() abfragen, ob was gemacht werden muß.

Ich habe im Moment keine Zeit, vielleicht macht es das trotzdem etwas 
verständlicher, ich melde mich nacher nochmal.

Gruß aus Berlin
Michael

von Kurt (Gast)


Lesenswert?

Moin Daniel

Danke für den ganzen Code.
Ich verstehe es zwar noch nicht, aber ich habe mal versucht ihn zu 
kompilieren.

Leider gab es folgende Fehlermeldung:

use of enum 'move_state' without previous declaration

bezieht sich wohl auf diese Zeile:

void setState( enum move_state state ){

Kannst du mir da nen Tipp geben?

von Daniel A. (daniel-a)


Lesenswert?

Kurt schrieb:
> use of enum 'move_state' without previous declaration

Ups, ich hätte:
1
void setState( enum e_move_state state ){
statt
1
void setState( enum move_state state ){

Schreiben müssen.

von Harald W. (wilhelms)


Lesenswert?


von Kurt (Gast)


Lesenswert?

Leider ist das egal:

use of enum 'e_move_state' without previous declaration

von Kurt (Gast)


Lesenswert?

@ Michael

Ich habe das jetzt mal so versucht umzusetzen:
1
void loop()
2
{
3
  WiFiClient client = server.available();
4
 
5
  if (client.available())  // Daten da?
6
  {
7
    String req = client.readStringUntil('\r'); // holen
8
    client.flush();
9
10
    if (req.indexOf("/eins") != -1)
11
    {
12
      status = 0;
13
    }
14
15
    else if (req.indexOf("/zwei") != -1)
16
    {
17
      status = 1;
18
    }
19
20
    else if (req.indexOf("/drei") != -1)
21
    {
22
      status = 2;
23
    }
24
25
    else if (req.indexOf("/vier") != -1)
26
    {
27
      status = 3;
28
    }
29
  }
30
31
32
  if (status == 0)
33
  {
34
    digitalWrite(LS_out_0, LOW);
35
    digitalWrite(LS_out_1, LOW);
36
  }
37
38
  else if (status == 1)
39
  {
40
    digitalWrite(LS_out_0, LOW);
41
    digitalWrite(LS_out_1, HIGH);
42
    //analogWrite(LS_out_1, 185);
43
  }
44
45
  else if (status == 2)
46
  {
47
    digitalWrite(LS_out_0, HIGH);
48
    //analogWrite(LS_out_0, 125);
49
    digitalWrite(LS_out_1, LOW);
50
  }
51
52
  else if (status == 3)
53
  {
54
    digitalWrite(LS_out_0, LOW);
55
    digitalWrite(LS_out_1, HIGH);
56
    while (1)
57
    {
58
59
      if (digitalRead(LS_in_0) == 0)
60
      {
61
        digitalWrite(LS_out_1, LOW);
62
        delay(500);
63
        digitalWrite(LS_out_0, HIGH);
64
      }
65
66
      if (digitalRead(LS_in_1) == 0)
67
      {
68
        digitalWrite(LS_out_0, LOW);
69
        delay(500);
70
        digitalWrite(LS_out_1, HIGH);
71
      }
72
73
    }
74
  }
75
76
  client.flush();
77
78
....
79
80
  client.print(s);
81
  delay(1);
82
}

Leider reagiert der ESP jetzt gar nicht mehr auf die Eingaben
und, so denke ich, komme ich doch so auch nicht aus der Schleife raus.

von Daniel A. (daniel-a)


Lesenswert?

Die einzigen fehler, die ich noch finde, sind 2 falsch gesetzte 
Semikolon:
so:
1
    case STATE_NO_MOVE:
2
      digitalWrite(LS_out_0, LOW);
3
      digitalWrite(LS_out_1, LOW);
4
      break;
statt so:
1
    case STATE_NO_MOVE;
2
      digitalWrite(LS_out_0, LOW);
3
      digitalWrite(LS_out_1, LOW);
4
      break

Wie diese Fehlermeldung zustande kommen kann, ist mir Rätselhaft: 
http://codepad.org/2AobVoAK

von Michael U. (amiga)


Lesenswert?

Hallo Kurt,

um erstmal schnell Deine 4. Schleife loszuwerden:
1
in setup()
2
3
unsigned long startSekunde = 0;
4
unsigned long warteZeit = 500;
5
6
...
7
8
  else if (status == 3)
9
  {
10
    digitalWrite(LS_out_0, LOW);
11
    digitalWrite(LS_out_1, HIGH);
12
13
    if (warten == false)
14
    {
15
      if (digitalRead(LS_in_0) == 0)
16
      {
17
        digitalWrite(LS_out_1, LOW);
18
      }
19
      else
20
      {
21
        digitalWrite(LS_out_0, LOW);
22
      }        
23
      startSekunde = millis();
24
      warten = true;
25
    }
26
    else
27
    {    
28
      if (millis() > (startSekunde + warteZeit) // Wartezeit vergangen?
29
      {    
30
        if (digitalRead(LS_in_0) == 0)
31
        {
32
          digitalWrite(LS_out_0, HIGH);
33
        }
34
        else
35
        {
36
        digitalWrite(LS_out_1, HIGH);
37
        warten = false;            // nicht warten
38
        }        
39
      }
40
    }
Ich weiß jetzt nicht, ob ich Deine digitalWrite() richtig sortiert habe, 
ansonsten schau Dir eben

http://playground.arduino.cc/Learning/BlinkWithoutDelayDe

mal an.

delay() sollte man sparsam verwenden und nur dann, wenn man in der Zeit 
wirklich nichts anderes zu tun hat. Es wartet schließlich einfach und 
damit
kann in der Zeit nichts anderes bearbeitet werden.
Dasselbe passiert natürlich in einer while(1) Schleife. Da ist das 
Programm dauerhaft wenn man die nicht gewollt wieder verläßt.
Normalerweise ist loop() eben diese Schleife und deren Inhalt muß 
möglichst schnell und ständig durchlaufen werden.

Gruß aus Berlin
Michael

: Bearbeitet durch User
von Kurt (Gast)


Lesenswert?

So, wieder zuhause.

Dieses funktioniert nicht:
1
void loop()
2
{
3
  WiFiClient client = server.available();
4
 
5
  if (client.available())  // Daten da?
6
  {
7
    String req = client.readStringUntil('\r'); // holen
8
    client.flush();
9
10
    if (req.indexOf("/eins") != -1)
11
    {
12
      status = 0;
13
    }

dies schon:
1
void loop()
2
{
3
  WiFiClient client = server.available();
4
  if (!client)
5
  {
6
    return;
7
  }
8
  String req = client.readStringUntil('\r');
9
10
  client.flush();
11
12
  if (req.indexOf("/eins") != -1)
13
  {
14
    fall = 0;
15
  }

Der Ersatz für die while-Schleife sieht vielversprechend aus, will aber 
noch nicht auf Anhieb.
Zum besseren Verständnis und vor allem zur einfacheren Handhabung habe 
ich mir daraus ein eigenes Sketch gemacht.

von Kurt (Gast)


Lesenswert?

So, es funktioniert wie gewünscht :-)

Danke euch Beiden für die Hilfe, auch wenn ich am Ende nicht direkt 
etwas von Euren Beispielen verwendet habe.

Zumindest hat mich amiga darauf gest0ßen, wie man die Schleife durch 
Bedingungen ersetzten kann.

Ganz fertig ist der Code noch nicht, die delay() könnte noch raus ;-)
Aber das stört in diesem Fall erst einmal nicht.

Jetzt wird etwas gelötet und der kleine Treiber (bzw. das Servo) durch 
einen Großen (bzw. einen richtigen Motor) ersetzt.

Hier mal der Code, falls jemand damit etwas anfangen kann:
1
#include <ESP8266WiFi.h>
2
WiFiServer server(80);
3
int PIN_Motor_rechts = D5;
4
int PIN_Motor_links = D6;
5
int PIN_Ende_rechts = D2;
6
int PIN_Ende_links = D1;
7
int ende_rechts = 0;
8
int ende_links = 0;
9
int fahrt_rechts = 1;
10
int fahrt_links = 0;
11
int fall = 0;
12
String req;
13
unsigned long zeit1 = 0;
14
unsigned long zeit2 = 0;
15
16
void setup()
17
{
18
  WiFi.mode(WIFI_AP);
19
  WiFi.softAP("Seilbahn", "12345678");
20
  server.begin();
21
  Serial.begin(115200);
22
  pinMode(PIN_Motor_rechts, OUTPUT);
23
  pinMode(PIN_Motor_links, OUTPUT);
24
  pinMode(PIN_Ende_rechts, INPUT);
25
  pinMode(PIN_Ende_links, INPUT);
26
  digitalWrite(PIN_Motor_rechts, LOW);
27
  digitalWrite(PIN_Motor_links, LOW);
28
}
29
30
void loop()
31
{
32
  //    //    //    //    //    //    Loopdauer messen
33
  Serial.print("Loopdauer: ");
34
  Serial.println(millis() - zeit1);
35
  zeit1 = millis();
36
37
  //    //    //    //    //    //    URL abfragen
38
  WiFiClient client = server.available();
39
  if (client.available()) {
40
    req = client.readStringUntil('\r');
41
    client.flush();
42
  }
43
44
  //    //    //    //    //    //    Fälle den URLs zuordnen
45
  if (req.indexOf("/null") != -1)  {
46
    fall = 0;
47
  }
48
  else if (req.indexOf("/eins") != -1)  {
49
    fall = 1;
50
  }
51
  else if (req.indexOf("/zwei") != -1)  {
52
    fall = 2;
53
  }
54
  else if (req.indexOf("/drei") != -1)  {
55
    fall = 3;
56
  }
57
58
  //    //    //    //    //    //    Fälle führen Funktionen aus
59
  if (fall == 0)  {
60
    motor_stop();
61
  }
62
63
  else if (fall == 1)  {
64
    motor_rechts();
65
  }
66
67
  else if (fall == 2)  {
68
    motor_links();
69
  }
70
71
  else if (fall == 3)  {
72
    if (fahrt_rechts == 1)
73
    {
74
      motor_rechts();
75
      if (digitalRead(PIN_Ende_rechts) == 0)
76
      {
77
        Serial.println("Ende rechts");
78
        motor_stop();
79
        fahrt_rechts = 0;
80
        fahrt_links = 1;
81
      }
82
    }//fahrt_rechts
83
84
    if (fahrt_links == 1)
85
    {
86
      motor_links();
87
      if (digitalRead(PIN_Ende_links) == 0)
88
      {
89
        Serial.println("Ende links");
90
        motor_stop();
91
        fahrt_links = 0;
92
        fahrt_rechts = 1;
93
      }
94
    } // fahrt_links
95
  } // Fall 3
96
97
  //    //    //    //    //    //    HTML erzeugen
98
  client.flush();
99
  String s = "HTTP/1.1 200 OK\r\n";
100
  s += "Content-Type: text/html\r\n\r\n";
101
  s += "<!DOCTYPE HTML>\r\n<html><center><H1>\r\n";
102
  s += "</br></br>";
103
104
xxxxxxxxxxxxxx
105
106
107
  s += "</H1></center></html>\n";
108
  client.print(s);
109
  delay(1);
110
111
} // Loop
112
113
//    //    //    //    //    //    Funktionen
114
void motor_stop()
115
{
116
  Serial.println("Motor stop");
117
  digitalWrite(PIN_Motor_rechts, LOW);
118
  digitalWrite(PIN_Motor_links, LOW);
119
  delay(500);
120
}
121
122
void motor_rechts()
123
{
124
  Serial.println("Motor rechts");
125
  digitalWrite(PIN_Motor_rechts, HIGH);
126
  digitalWrite(PIN_Motor_links, LOW);
127
}
128
129
void motor_links()
130
{
131
  Serial.println("Motor links");
132
  digitalWrite(PIN_Motor_rechts, LOW);
133
  digitalWrite(PIN_Motor_links, HIGH);
134
}

Kritik ist immer gerne gesehen.

Ach ja, die Loopzeit beträgt 500ms,
aber die Lichtschranken reagieren deutlich schneller.
Woran könnte das liegen?

von Kurt (Gast)


Lesenswert?

Anmerkung:

Was soll das denn???

In dem durch xxxxxxx ersetzten Teil stand der Quelltext für HTML-Links.
Denn kann ich aber nicht absenden, weil die Forensoftware das als Spam 
einordnet.

von Daniel A. (daniel-a)


Lesenswert?

Kurt schrieb:
> In dem durch xxxxxxx ersetzten Teil stand der Quelltext für HTML-Links.
> Denn kann ich aber nicht absenden, weil die Forensoftware das als Spam
> einordnet.

Das stört mich auch schon lange, das ist ein grundlegendes Problem der 
Forensoftware. Da die Links eigentlich von der Forensoftware escaped 
werden, macht diese Überprüfung in der Forensoftware eigentlich wenig 
Sinn. Der Forenbetreiber scheint jedoch keine Zeit oder keine Lust zu 
haben, dies zu Ändern.

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.