HTTP Webserver - Best practice ?

Hallo,

ich hab eine Wetterstation (Temperatur, Feuchtigkeit, Luftdruck, Regen) die im 10-Minuten-Takt ihre Messwerte auf einen Webserver schickt - funktioniert alles.
Zusätzlich möchte ich, dass mein Arduino in dem Zeitraum dazwischen als Webserver fungiert und auf Anfrage die aktuellen Messwerte an einen Client als Webseite ausgibt.

Leider funktioniert das mal sehr gut, mal wieder gar nicht.
"Sehr gut" funktionierts wenn ich nur mit dem PC auf die Seite zugreife (die sich im Minutentakt aktualisiert).
"Gar nicht" funktionierts wenn ich mit dem Handy zugreife, dann bei offener Seite das Handy abschalte und es nochmals versuche. Dann ist der Server (bzw. die Seite) nicht mehr erreichbar.

Gelöst hab ich die wesentlichen Teile so:

// Create an instance of the server
// specify the port to listen on as an argument
WiFiServer server(80);

// wird für den HTTP-Post verwendet, nicht für den Webserver
 WiFiClient client;

void setup()
{
// Start the server
  Serial.print("Start WebServer ... ");
  server.begin();
  Serial.println("Success");
}

void loop()
{

// Abwicklung des HTTP-Post läuft alle 10 Minuten - ist hier im Detail nicht relevant, daher nur der andere Abschnitt:

 // Check if a client has connected
 // leave loop() if !client

    WiFiClient client = server.available();
    if (!client) {
      return;
    }

    // Wait until the client sends some data
    Serial.print(String(counter) + " New client connected   ");
    counter += 1;


// if client is not available, leave loop() after 10 sec  
    int start = millis();
    while(!client.available()){
      delay(1);
      if(millis() - start >= 10000)
      {
          break;
      }
    }
  
    client.flush();

    // Prepare the response        
    String s = "response-string"
  
    // Send the response to the client
    client.print(s);
    delay(1);
    client.stop();
    Serial.println("Client disonnected");
    // The client will actually be disconnected 
}

Ein Verdacht den ich jetzt habe ... ich benutze WiFiClient client; zweimal.
Einmal global - damit baue ich die Verbindung zum Server auf client.connect(host, httpPort) und poste meine Daten.

Und einmal in dem Abschnitt in loop() der mir den Webserver macht.

Kann das Probleme verursachen ?

lG
Gawan

Hast du das selbst programmiert? Hast du einen Programmablaufplan gemacht?

Poste mal den ganzen Code.

Hallo,

ja - selbst programmiert und die einzelnen Teile vorher zusammengesucht :slight_smile:

Ist aber erst eine Version 0.9 - die Lesbarkeit lässt sich noch verbessern.

Grundsätzlich:

Das Ding läuft und alle 10 Minuten postet er die Daten an den Datenbankserver.
Dazwischen soll er sich einfach als Webserver verhalten und bei Bedarf den Client mit Daten versorgen:

Der Code ist leider zu lang für ein Posting hier - ich versuchs auf zweimal:

#include <dht.h>
#include <ESP8266WiFi.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP085_U.h>

dht DHT;

const char* ssid = "meinWLAN";
const char* password = "meinPW";

const char* versionID = "Arduino Environmental Project  0.9";
const char* typeID = "NodeMCU 1.0 (ESP-12E Module) DevKit V2";
//const char* typeID = "ESP8266 ESP-01";
const char* deviceID = "ESP001";
const char* location = "Garage";

const char* host = "10.0.0.100";
const int httpPort = 890;

unsigned long prev_millis = 0;

int counter = 0;
String ipaddress = "";

float Humidity_DHT11 = 0.0;
float Temperature_DHT11 = 0.0;
float Temperature_DS18B20 = 0.0;
float Pressure_BMP180 = 0.0;
float Temperature_BMP180 = 0.0;
float RainSensor = 0.0;

int SQLExec_Counter = 0;

// Inititate Barometric Sensor
Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10085);

// Create an instance of the server
// specify the port to listen on as an argument
WiFiServer server(80);

//Pin for DHT11 - equals D5 on NodeMCU
#define DHT11_PIN 14

//Pin for DS18B20 - equals D6 on NodeMCU
#define ONE_WIRE_BUS 12
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

 WiFiClient client;

void setup() { 
 
  Serial.begin(115200);
  delay(10);

// Set I2C parameters
// SDA = 0  Pin D3
// SDA = 2  Pin D4
  Wire.begin(0, 2);
  bmp.begin();
  
  Serial.println("");
  Serial.println("");
  Serial.println(String(versionID));
  Serial.println(String(typeID));
  Serial.println(String(deviceID));
  
  // Connect to WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Connecting to WLAN   ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
  Serial.print("Start at ");
  uptime();
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(50);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Success at ");
  uptime();
  Serial.println("WiFi connected");


  //Serial.println(WiFi.getNetworkInfo());
  Serial.print ("Signal Strength: ");
  Serial.println (WiFi.RSSI());
  
// Print the IP address
  Serial.print("My IP is:  ");
  Serial.println(WiFi.localIP());

  IPAddress myAddr = WiFi.localIP();

  ipaddress = String(myAddr[0])+ "." +String(myAddr[1]) + "." +String(myAddr[2]) + "." +String(myAddr[3]);
  
  // Start the server
  Serial.print("Start WebServer ... ");
  server.begin();
  Serial.println("Success");
  Serial.println();
  Serial.println();

  // Read DHT11 Sensor
  DHT.read11(DHT11_PIN);
  // Read DS18B20
  sensors.requestTemperatures(); 
  // Read BMP180
  sensors_event_t event;
  bmp.getEvent(&event);
  
  // Read Rain-Sensor
  RainSensor = analogRead(A0);
    
  delay(2000);
   
  Humidity_DHT11 = DHT.humidity;
  Temperature_DHT11 = DHT.temperature;
  Temperature_DS18B20 = sensors.getTempCByIndex(0);

  if (event.pressure)
  {
    Pressure_BMP180 = event.pressure;
    bmp.getTemperature(&Temperature_BMP180);
  }
  
  Serial.println("DHT11:\t\tHumidity:   "+String(Humidity_DHT11)+"   \t Temperature:  "+String(Temperature_DHT11));
  Serial.println("DS18B10:\t\t\t\t Temperature:  "+ String(Temperature_DS18B20));
  Serial.println("BMP180:\t\tPressure:   "+String(Pressure_BMP180)+"   \t Temperature:  "+String(Temperature_BMP180));  
  Serial.println("RainSensor:\tRainValue:  "+String(RainSensor));
}


void loop() {

  // Establish WiFi if disconnected
  
  if (WiFi.status() != WL_CONNECTED) 
  {
    uptime();
    Serial.println("WLAN Connection lost - try to reconnect ..."); 
    WiFi.begin(ssid, password);
  }

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }

  SQLExec_Counter = millis() - prev_millis;
  
  // Falls es bei den Millis zu einem Überlauf kommt - rücksetzen auf 0
  if (SQLExec_Counter < 0) 
  {
    SQLExec_Counter = 0;
    prev_millis = 0;
  }
  
  delay(100);


    
  if (SQLExec_Counter > 600000)
  //alle 10 Minuten werden Werte geschrieben
  {
       
    // Be the HTTP Client and POST data to SQL
    SQLExec_Counter = 0;
    prev_millis = millis();

    Serial.println();
    uptime();
    Serial.println("Start reading environmental Parameters"); 
    
    // Read DHT11 Sensor
    DHT.read11(DHT11_PIN);
    // Read DS18B20
    sensors.requestTemperatures(); 
    // Read BMP180
    sensors_event_t event;
    bmp.getEvent(&event);
    // Read Rain-Sensor
    RainSensor = analogRead(A0);

  
    delay(2000);
   
    Humidity_DHT11 = DHT.humidity;
    Temperature_DHT11 = DHT.temperature;
    Temperature_DS18B20 = sensors.getTempCByIndex(0);

    if (event.pressure)
    {
      Pressure_BMP180 = event.pressure;
      bmp.getTemperature(&Temperature_BMP180);
    }

weiter gehts:

    Serial.println("DHT11:    Humidity:  "+String(Humidity_DHT11)+"   \t Temperature:  "+String(Temperature_DHT11));
    Serial.println("DS18B10:\t\t\t Temperature:  "+ String(Temperature_DS18B20));
    Serial.println("BMP180:   Pressure:  "+String(Pressure_BMP180)+"   \t Temperature:  "+String(Temperature_BMP180));  
    Serial.println("RainSensor:\t RainValue:  "+String(RainSensor));
    
    if (!client.connect(host, httpPort)) {
      Serial.println("Connection failed");
      Serial.println();
      return;
    }
    else
    {
      Serial.println("Verbunden ...");
    }

   // use GET-Method to send data to the webservice
   // obsolete since IOAnalytics v1.0.2
   /*
    String url = "/api/values/?param=Hose&user="+ ipaddress +"&value="+ String(WiFi.RSSI()) +"&p1=a&p2=c&p3=c";
    client.print(String("POST ") + url + " HTTP/1.1\r\n" +
                 "Host: " + host + "\r\n" + 
                 "Connection: close\r\n\r\n");
  */

    String data = "";
  // use POST-Method
    //String data = "param=Test&user="+ String(ipaddress) +"&value="+WiFi.RSSI()+"&p1=" + String(Humidity) + "&p2= "+ String(Temperature) +" &p3=c";
    data = "param=Hum_DHT11&user="+ String(ipaddress) +"&value="+String(Humidity_DHT11)+"&p1="+String(deviceID) + "&p2="+WiFi.RSSI()+"&p3="+String(millis()/1000);
    Serial.println("POST1:   "+String(data));
    client.println("POST /api/values HTTP/1.1");
    client.println("Host: " + String(host));
    client.println("Content-Type: application/x-www-form-urlencoded");
    client.print("Content-Length: ");
    client.println(data.length());
    client.println();
    client.println(data);
    Serial.println();


    data = "param=Temp_DHT11&user="+ String(ipaddress) +"&value="+String(Temperature_DHT11)+"&p1="+String(deviceID) + "&p2="+WiFi.RSSI()+"&p3="+String(millis()/1000);
    Serial.println("POST2:   "+String(data));
    client.println("POST /api/values HTTP/1.1");
    client.println("Host: " + String(host));
    client.println("Content-Type: application/x-www-form-urlencoded");
    client.print("Content-Length: ");
    client.println(data.length());
    client.println();
    client.println(data);
    Serial.println();


    data = "param=Temp_DS18B20&user="+ String(ipaddress) +"&value="+String(Temperature_DS18B20)+"&p1="+String(deviceID) + "&p2="+WiFi.RSSI()+"&p3="+String(millis()/1000);
    Serial.println("POST3:   "+String(data));
    client.println("POST /api/values HTTP/1.1");
    client.println("Host: " + String(host));
    client.println("Content-Type: application/x-www-form-urlencoded");
    client.print("Content-Length: ");
    client.println(data.length());
    client.println();
    client.println(data);
    Serial.println();
    

    data = "param=Temp_BMP180&user="+ String(ipaddress) +"&value="+String(Temperature_BMP180)+"&p1="+String(deviceID) + "&p2="+WiFi.RSSI()+"&p3="+String(millis()/1000);
    Serial.println("POST4:   "+String(data));
    client.println("POST /api/values HTTP/1.1");
    client.println("Host: " + String(host));
    client.println("Content-Type: application/x-www-form-urlencoded");
    client.print("Content-Length: ");
    client.println(data.length());
    client.println();
    client.println(data);
    Serial.println();


    data = "param=Press_BMP180&user="+ String(ipaddress) +"&value="+String(Pressure_BMP180)+"&p1="+String(deviceID) + "&p2="+WiFi.RSSI()+"&p3="+String(millis()/1000);
    Serial.println("POST5:   "+String(data));
    client.println("POST /api/values HTTP/1.1");
    client.println("Host: " + String(host));
    client.println("Content-Type: application/x-www-form-urlencoded");
    client.print("Content-Length: ");
    client.println(data.length());
    client.println();
    client.println(data);
    Serial.println();


    data = "param=RainSensor&user="+ String(ipaddress) +"&value="+String(RainSensor)+"&p1="+String(deviceID) + "&p2="+WiFi.RSSI()+"&p3="+String(millis()/1000);
    Serial.println("POST6:   "+String(data));
    client.println("POST /api/values HTTP/1.1");
    client.println("Host: " + String(host));
    client.println("Content-Type: application/x-www-form-urlencoded");
    client.print("Content-Length: ");
    client.println(data.length());
    client.println();
    client.println(data);
    Serial.println();

    Serial.println("Done");
  }



  else

  {
    // Be the HTTP Server and wait for requests
    
    // Check if a client has connected
    WiFiClient client = server.available();
    if (!client) {
      return;
    }
    // Wait until the client sends some data
    Serial.print(String(counter) + " New client connected   ");
    counter += 1;
  
    int start = millis();
    while(!client.available()){
      delay(1);
      if(millis() - start >= 10000)
      {
          break;
      }
    }
  
    client.flush();

    // Prepare the response            user  \r\n   as a linebreak
    String s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\n";
    //s+= versionID;
  
    s+= "<head><meta http-equiv='refresh' content='60'/><title>" + String(typeID) + "</title>";
    s+= "<link rel='shortcut icon' type='image/x-icon' href='http://arduino.cc/en/favicon.png' />";
    s+= "<style>";
    s+= "body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; } ";
    s+= "system { font-size: 8pt; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; } ";
    s+= "</style>";
    s+= "</head>";
    s+= "<body>    <h2>" + String(versionID) + " </h2>";
    s+= "<system> TypeID: "+ String(typeID) + "
 DeviceID: " + String(deviceID) + "
 Location: "+String(location)+"</system>
";
    s+= "

Signal Strength:  ";
    s+= WiFi.RSSI();
    s+= " dbm

";
    s+= "DHT22
Temperature:   "+String(Temperature_DHT11)+"&deg;C    Humidity:   "+String(Humidity_DHT11)+"%

";
    s+= "DS18B20
Temperature:   "+String(Temperature_DS18B20)+"&deg;C

";
    s+= "BMP180
Temperature:   "+String(Temperature_BMP180)+"&deg;C    Pressure:   "+String(Pressure_BMP180)+"hPa

";
    s+= "RainSensor
RainValue::   "+String(RainSensor);
    s+= "

";
    s+= "Uptime:  "+String(millis()/1000) +"s";
    s+= "</body>";
    s+= "\n</html>\n";
  
  
    // Send the response to the client
    client.print(s);
    delay(1);
    Serial.println("Client disonnected");
    client.stop();
    // The client will actually be disconnected 
    // when the function returns and 'client' object is detroyed
  
  }
}




void uptime()
{
 long days=0;
 long hours=0;
 long mins=0;
 long secs=0;
 long mill = millis();
 //Serial.println(mill);
 secs = mill/1000; //convect milliseconds to seconds
 mins=secs/60; //convert seconds to minutes
 hours=mins/60; //convert minutes to hours
 days=hours/24; //convert hours to days
 mill = mill-(secs*1000);
 secs=secs-(mins*60); //subtract the coverted seconds to minutes in order to display 59 secs max 
 mins=mins-(hours*60); //subtract the coverted minutes to hours in order to display 59 minutes max
 hours=hours-(days*24); //subtract the coverted hours to days in order to display 23 hours max
 //Display results
 Serial.println("Uptime: " + String(days) + " d  " + String(hours) + " hrs  " + String(mins) + " min  " + String(secs) + " sec  " + String(mill) + " mi");
}

Gawan:
... Leider funktioniert das mal sehr gut, mal wieder gar nicht.
"Sehr gut" funktionierts wenn ich nur mit dem PC auf die Seite zugreife (die sich im Minutentakt aktualisiert).
"Gar nicht" funktionierts wenn ich mit dem Handy zugreife, dann bei offener Seite das Handy abschalte und es nochmals versuche. Dann ist der Server (bzw. die Seite) nicht mehr erreichbar.
...

Vorweg: Deinen Code habe ich mir nicht angeguckt.

Kannst Du beim Testen den Arduino im Blick haben?

Wenn ja: Baue testweise Zeilen ein, die die LED an Pin 13 leuchten lassen. Je nachdem, wo Du diese Zeilen einbaust, kannst Du eingrenzen, wo der Fehler genau auftritt. Wenn Du das weißt, ist die Fehlerbehebung wahrscheinlich nur noch ein Klacks.

HTH

Gregor

Ja, im Notfall kann ich das machen.
Aber ich verstehe die Befehle im Detail nicht gut genug um überhaupt sagen zu können wie die Reaktion sein soll.
Ich hab wie gesagt den Code halt nach bestem Wissen und Gewissen zusammengesucht.

Mich verwirrt

  1. das doppelte WiFiClient client;

  2. WiFiClient client = server.available();
    if (!client) {
    return;
    }
    Damit prüfe ich ob der Arduino Webserver verfügbar ist !? Wozu !?
    Oder prüfe ich da schon ob sich ein Client verbunden hat ?

  3. Falls der Client nicht mehr verfügbar (while(!client.available())) ist erzwinge ich ein BREAK
    Was aber wenn der Client genau nach dieser Schleife und dem Vorbereiten der Response aussteigt ?
    Müsste ich da nicht erst kurz vor dem client.print(s) prüfen ?

Ich gehe aber davon aus, dass der Fehler im Webserver-Abschnitt auftaucht (der kurze Code im ersten Posting).
Daher meine Frage ob es da irgendeine Art von Best-Practice gibt.

Damit der Webserver die Client-Verbindungen zeitgerecht wieder kappt.

Hab das bei mir im ESP auch so drin.

Habe das für 2. so verstanden:
Wenn kein client da ist gehe zum Anfang von loop.

Also wartet er an dieser Stelle auf einen clienten.

Lies dir auch mal das hier durch.

Hatte ähnliche Probleme mit Verbindungsabbrüchen.

War zum Schluß ein Eintrag der Time.Alarm lib welcher in der Wifi-client-Schleife stänkerte.

Der hier läuft seeehr stabil. Hab mich daran orientiert.

Gawan:
Aber ich verstehe die Befehle im Detail nicht gut genug ...

... was die die Ursache hierfür ist:

Gawan:
Mich verwirrt ...

Wie ich sehe, wirst Du geholfen. Prima.

Gruß

Gregor

Ich spiele jetzt grad mit dem Webserver von stoni99 rum, der werkelt wirklich sehr stabil vor sich hin.

Sobald ich die beiden Versionen im Detail verglichen und die Unterschiede verstanden hab sollte das Problem behoben sein :slight_smile: