Ethernet-lib - Connect/Connected/Im Netzwerk/Stop/Read

Hallo zusammen,

ich hab jetzt mal kurz (ne) Frage(n) zur Ethernet-lib. Bin gerade ein bisschen verwirrt.

Das ist ja der Code, der in dem Tutorial zu finden ist:

#include <SPI.h>
#include <Ethernet.h>

// assign a MAC address for the ethernet controller.
// fill in your address here:
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
// fill in an available IP address on your network here,
// for manual configuration:
IPAddress ip(10,0,0,20);

// fill in your Domain Name Server address here:
IPAddress myDns(1,1,1,1);

// initialize the library instance:
EthernetClient client;

char server[] = "www.arduino.cc";

unsigned long lastConnectionTime = 0;          // last time you connected to the server, in milliseconds
boolean lastConnected = false;                 // state of the connection last time through the main loop
const unsigned long postingInterval = 60*1000;  // delay between updates, in milliseconds

void setup() {
  // start serial port:
  Serial.begin(9600);
  // give the ethernet module time to boot up:
  delay(1000);
  // start the Ethernet connection using a fixed IP address and DNS server:
  Ethernet.begin(mac, ip, myDns);
  // print the Ethernet board/shield's IP address:
  Serial.print("My IP address: ");
  Serial.println(Ethernet.localIP());
}

void loop() {
  // if there's incoming data from the net connection.
  // send it out the serial port.  This is for debugging
  // purposes only:
  if (client.available()) {
    char c = client.read();
    Serial.print(c);
  }

  // if there's no net connection, but there was one last time
  // through the loop, then stop the client:
  if (!client.connected() && lastConnected) {
    Serial.println();
    Serial.println("disconnecting.");
    client.stop();
  }

  // if you're not connected, and ten seconds have passed since
  // your last connection, then connect again and send data:
  if(!client.connected() && (millis() - lastConnectionTime > postingInterval)) {
    httpRequest();
  }
  // store the state of the connection for next time through
  // the loop:
  lastConnected = client.connected();
}

// this method makes a HTTP connection to the server:
void httpRequest() {
  // if there's a successful connection:
  if (client.connect(server, 80)) {
    Serial.println("connecting...");
    // send the HTTP PUT request:
    client.println("GET /latest.txt HTTP/1.1");
    client.println("Host: www.arduino.cc");
    client.println("User-Agent: arduino-ethernet");
    client.println("Connection: close");
    client.println();

    // note the time that the connection was made:
    lastConnectionTime = millis();
  }
  else {
    // if you couldn't make a connection:
    Serial.println("connection failed");
    Serial.println("disconnecting.");
    client.stop();
  }
}

Mir geht’s jetzt um “connect();”, “connected();” und darum, ob ich erkennen kann, dass ich im Netzwerk bin.

Na dann von Anfang an.

connect():

Die Funktion brauch ich, um eine Verbindung zum Server aufzubauen und ihm eine URL zu schicken. Zum Ende kommt “client.println(“Connection: close”);”. Das beendet die Verbindung zum Server. Richtig?

connected():

Bei dem Wert bin ich mir jetzt nicht sicher: Soll der heißen, die Verbindung zum Server besteht noch (kein “client.println(“Connection: close”);”) oder der Arduino hat eine Verbindung zum Netzwerk? Ich glaub irgendwie ersteres, bin mir aber nicht ganz sicher.

Im Netzwerk:

Wenn connected() nicht anzeigt, ob ich mich im Netzwerk befinde, wie kann ich das dann anstellen? Den Server anfunken und wenn der Antwort bin ich drin? Oder gibt’s dazu was?

Stop:

Hier geht’s mir um “client.stop();”. Was stell ich damit an? Mach ich damit “Ethernet.begin();” rückgängig, also trenn ich mich wieder vom Netzwerk?

Read:

Das brauch ich doch, wenn ich eine vom Server gegebene Antwort in Form von HTML auswerten will, oder? Sollte ich doch dann so auswerten können (?) :

bool readEthernet()
{
  static byte index;

  if(client.available())
  {
    char c = client.read();

    if(c >= '0' && c <= '9' && index < MAX_NUMBERS)
    {
      numbers_e[index] = numbers_e[index] * 10 + c - '0';
    }
    else if(c == ',')
    {
      index++;
    }
    else if(c == '\n')
    {
      index = 0;
      return true;
    }
  }

  return false;
}

Liest der dann die Headerdaten vom HTTP mit aus? Oder nur rein die HTML-Daten?

Ich weiß, in der Reference steht zu allem etwas, aber ich bin mir bei allem nicht so wirklich sicher, ob ich das so richtig versteh. Da frag ich lieber nach, bevor ich teste, nichts geht und mir dann mein Ethernet-Controller abraucht.

Vielen Dank schon mal

LG

Fipsi

Fipsi: connect():

Die Funktion brauch ich, um eine Verbindung zum Server aufzubauen und ihm eine URL zu schicken. Zum Ende kommt "client.println("Connection: close");". Das beendet die Verbindung zum Server. Richtig?

Falsch.

Die Funktion brauchst Du, um die Verbindung zum Server aufzubauen. Soweit richtig. Aber mit "client.println("Connection: close");" sendest Du zu dem Server nur eine Anweisung, dass er selbsttätig die Verbindung wieder schließen soll, wenn er seine Antwort zu dieser Anfrage komplett gesendet hat. Damit wird also von Dir etwas an den Server gesendet, wie er sich verhalten soll.

Fipsi: connected():

Bei dem Wert bin ich mir jetzt nicht sicher: Soll der heißen, die Verbindung zum Server besteht noch

Damit stellst Du fest, ob die Verbindung zum Server noch offen ist, oder ob der Server die Verbindung beendet hat. Sobald "connected()" nicht mehr erfüllt ist, hat der Server auf seiner Seite bereits dichtgemacht und Du kannst auch auf Deiner Seite mit "client.stop();" die Verbindung beenden.

Fipsi: Im Netzwerk:

Wenn connected() nicht anzeigt, ob ich mich im Netzwerk befinde, wie kann ich das dann anstellen?

Du bist im Netzwerk erfolgreich "drin", wenn die Anweisung "Ethernet.begin(mac, ip, myDns);" im Setup fehlerfrei abgearbeitet wurde und ab diesem Zeitpunkt bist Du dann Teilnehmer am Netzwerk.

Fipsi: Stop:

Hier geht's mir um "client.stop();". Was stell ich damit an? Mach ich damit "Ethernet.begin();" rückgängig, also trenn ich mich wieder vom Netzwerk?

Nein.

Damit schließt Du bei einem Protokoll, dass eine "Verbindung" benötigt, z.B. beim HTTP-Protokoll, die Verbindung auf Deiner Seite. Wenn Du das Netzwerken mit dem Telefonieren vergleichen wolltest, dann wären die Funktionen vergleichbar mit

Ethernet.begin() ==> eine Telefonanlage am Telefonanschluss installieren/anschließen connect() ==> Hörer nehmen, wählen, der Angerufene nimmt das Gespräch an connected() ==> Feststellen, ob der Angerufene wieder aufgelegt hat stop() ==> selbst den Hörer auflegen

Deine Lese-Funktion kann nur Zahlen einlesen. Nicht Text.

Ah, okay, vielen Dank für die Erklärungen. Hat mir jetzt schon gut weiter geholfen :)

Aber wie kann ich testen, ob der Telefonanschluss installiert ist? Es kann ja sein, dass der Router die Verbindung zum Arduino verweigert (warum sei mal dahin gestellt) oder dass der Router ausgeschaltet wird (bzw. die Telefonleitung durchgeschnitten) oder wie auch immer, dass der Arduino jedenfalls nicht mehr im Netzwerk ist. Wie kann man das feststellen?

Und dann fällt mir jetzt gerade noch was ein:

Die Datei, auf die der Arduino zugreifen soll, soll später in einem Ordner sein, der mit einer .htaccess-Datei geschützt ist. Wie komm ich dann mit dem Arduino trotzdem drauf? Also wie kann ich da den Benutzernamen und das Passwort schicken? EIngabefelder gehen ein bisschen schlecht^^.

Serenifly:

Die braucht auch nur Zahlen. Die Daten sind alles nur Zahlen und die Befehle sind als Zahlen codiert. Wie auch bei meiner Serial-Verbindung.

LG

Fipsi

Edit:

Zum "connection: close" noch was: muss ich danach am Arduino auch noch stop sagen? Ist es wie am Festnetz, dass jeder selber auflegen muss oder wie am Smartphone, wenn der andere aufgelegt hat, ist auch bei dir aufgelegt?

Fipsi: Aber wie kann ich testen, ob der Telefonanschluss installiert ist? Es kann ja sein, dass der Router die Verbindung zum Arduino verweigert (warum sei mal dahin gestellt) oder dass der Router ausgeschaltet wird

Brauchst Du Fehlerbehandlung ==> Machst Du Fehlerbehandlung.

Beispielsweise

  if (Ethernet.begin(mac, ip, myDns))
    Serial.println("Ich bin drin");
  else
    Serial.println("Netzwerkfehler!");

Ob dann aber eine konkrete Netzwerkverbindung möglich ist, erst Recht zu einem Server im Internet, kannst Du allerdings nur feststellen, wenn Du versuchst eine konkrete Verbindung zum Zielserver aufzubauen und feststellst, ob das funktioniert oder nicht.

Fipsi: Die Datei, auf die der Arduino zugreifen soll, soll später in einem Ordner sein, der mit einer .htaccess-Datei geschützt ist. Wie komm ich dann mit dem Arduino trotzdem drauf? Also wie kann ich da den Benutzernamen und das Passwort schicken?

Die .htaccess Datei ist eine Konfigurationsdatei vom Apache-Webserver. Mit einem Arduino wirst Du wohl kaum einen Apache-Webserver komplett nachbilden können, schau Dir mal die Hardware Requirements vom Apache Webserver an: https://cloudstack.apache.org/docs/en-US/Apache_CloudStack/4.0.2/html/Installation_Guide/minimum-system-requirements.html

Was Du meinst, nennt sich "HTTP Basic Authentification". Und wie das funktioniert ist in RFC-Standard 2617 erklärt: http://tools.ietf.org/html/rfc2617

Die "Basic Authentication" mit der unverschlüsselten Passwortübertragung kannst Du auch mit Arduino leicht realisieren. Für die "Digest Access Authentication" hat ein Arduino allerdings zu wenig RAM-Speicher für die notwendige hochkomplexe Datenverschlüsselung.

Edit/Nachtrag: Sehe gerade, der Arduino soll der Client sein und der Server ist ein Apache-Internetserver. In dem Fall klappt das mit der .htaccess natürlich, wenn diese auf dem Apache-Webserver liegt. Konfiguriert sein muss allerdings "BASIC Authentification" und der Arduino-Client muss dann nur im Header der Anfrage Username und Passwort mitsenden.

Also muss ich doch den Server immer wieder anfunken und wenn keine Antwort kommt Terror machen. Okay.

Ich glaube, das hast du falsch verstanden. Ich bilde mit dem Arduino keinen Server nach. Der Server sitzt auf meinem PC, XAMPP, mit dem sich Arduino verbindet. Und im Zielordner ist eben eine .htaccess, damit niemand sonst in den Ordner kann. Wäre eine große Sicherheitslücke und da es ja um eine Turnieranwendung geht schließ ich die lieber. Ich hatte irgendwo mal was gelesen, dass ich den Arduino Benutzernamen und Passwort im Header schicken lassen muss. Aber ich hab keinen Dunst wie und find des nicht mehr. Google spuckt nur mal wieder was aus, mit dem ich nichts anfangen kann (weil Englisch und so).

LG

Fipsi

Edit auch: Ich sehe, wir verstehen uns ;)

Fipsi:
Ich hatte irgendwo mal was gelesen, dass ich den Arduino Benutzernamen und Passwort im Header schicken lassen muss. Aber ich hab keinen Dunst wie und find des nicht mehr. Google spuckt nur mal wieder was aus, mit dem ich nichts anfangen kann (weil Englisch und so).

Du sendest im Header eine zusätzliche Zeile nach der Art:

client.println("Authorization: Basic dXNlcm5hbWU6cGFzc3dvcnQ=");

Und da wo die kryptischen Zeichen stehen, sendest Du in der Form “username:passwort” den unverschlüsselten Usernamen und das unverschlüsselte Passwort in einer BASE64-Codierung.

Falls Du kein BASE64-Tool auf Deinem PC hast, gibt es auch diverse Seiten im Internet, wo Du online die Codierung machen kannst, z.B.:

Oder Du machst Dir eine Arduino-Funktion zum BASE-64 Encoding.

Klassööö :) Danke dir ;)

Und ich hoffe aber in UTF-8? Bei ASCII spuckt der mir grad gar nichts mehr aus^^.

LG

Fipsi

So.. jetzt hab ich doch noch ne Frage:

// Ethernet Verbindung überprüfen
bool checkEthernet()
{
  if (client.connect(server_ip,80))
  {
    client.println("GET archers_results/entw/templates/terminal_communication.php?do=check HTTP/1.1");
    
  }
  else
  {
    ethernet_error = 1;
  }
}

Kann ich da jetzt in den if-Teil das "client.available()" stecken oder wartet mein Board gar nicht so lange, also muss ich das wo anders hinstecken?

LG

Fipsi

Fipsi:
Kann ich da jetzt in den if-Teil das “client.available()” stecken oder wartet mein Board gar nicht so lange, also muss ich das wo anders hinstecken?

Da wartet gar nichts.

Mit “client.available()” mußt Du in der loop-Funktion abfragen, was Du vom Client bekommst. HTTP arbeitet mit recht langen Timeouts, da kann die vollständige Antwort vom Server auch mal 30 oder 45 Sekunden dauern, wenn es Engpässe auf dem Server oder im Netzwerk gibt.

Hast Du Dir das “WebClient” Beispiel zur Ethernet-Library nicht angesehen, wo es hingehört, nach einer Client-Anfrage auf die Serverantwort zu warten?

Doch, hab ich schon.

Aber ich hab das Problem, wie auch bei Serial, dass ich viele Anfragen abzuarbeiten hab, die in einer Schleife hängen.

Heißt: Terminal frägt Steuergerät -> Steuergerät frägt Server -> Server antwortet Steuergerät -> Steuergerät antwortet Terminal. Ich hab das bisher alles in einer Schleife. Gut, das beim Terminal könnte man gut ausklinken, aber für's Steuergerät da müsste ich doch einiges umbauen.. aber gut.. ich hab schon ne Idee wie ich das mach.. Danke.

LG

Fipsi