PHP, HTTP und response

Hi Leute,
also erstmal so grob die Situation:
Hab mehrere ESP8266 im Einsatz. Die sollen über 'ESPAsyncWebServer.h' Daten / Befehle empfangen können.
Im ESP8266 ist folgendes:

AsyncWebServer webServer = AsyncWebServer(80);
webServer.on("/setIrgendwas", HTTP_GET, [](AsyncWebServerRequest *request) {
	request->send_P(200, "application/json", "{\"erg\":\"S_OK\"}");
});

Das funktioniert mit Firefox, Chrome, einem anderem ESP8266 und einem C# Sender ziemlich gut, allerdings bleibt der Apache/PHP8 leider irgendwie hängen.
Die Anfrage geht durch, der ESP tut das, was da drin steht, aber die Antwort passt nicht.
Die Antwort ist aber in dem Fall vollkommen egal - trotzdem bleibt der Apache/php in 'Warte auf Antwort' und blockiert damit alles andere. Obwohl der ESP das gewünschte tat.
Der Workarround bei php:

stream_context_create([
	"http" => [
		"method"=>"GET",
		"timeout" => 1
	]
]);

bei gleichzeitigen Abfragen summiert sich das jedoch und fühlt sich träge an, da die darauffolgende abfrage erst nach dem ablaufen des 1 sek Timers abgesendet wird.
Die http-Querkommunikation mit Shelly <=> C#, C# <=> ESP, ESP <=> Shelly, C# <=> PHP, Shelly <=> PHP funktioniert einwandfrei. Wo es hackt ist nur das: ESP <=> PHP.

Hat von euch schonmal jemand damit Probleme oder erfahrungen machen können?

Gruß

zum Analysieren wirst da einen kurzen (aber vollständigen) Sketch für den ESP benötigen und ein entsprechendes PHP Script . Dann können sich das andere eventuell auch ansehen.

Nur eine Zeile vom ESP und einem Zeile PHP - da werden sich auch die Spezialisten schwer tun nachzuvollziehen, was in deinen Systemen falsch läuft.

Wireshark starten http protokoll analysieren. Entweder ist dein Request kürzer als im Header angegeben oder deine Verbindung wird offen gehalten.

Connection: close

Hi @noiasca ,
das ganze hochladen ist ein wenig schwierig, da sich mehrere Dateien im kompletten sketch befinden. Hier mal die wichtigsten, zum Thema passenden Zeilen:
helperWebServer.h

#ifndef helperWebServer_h
#define helperWebServer_h
#include <Arduino.h>
#include <ESPAsyncWebServer.h>
class helperWebServer {
	public:
		helperWebServer();
		void setupWebServer();
		void cycle();
		AsyncWebServer webServer = AsyncWebServer(80);
		AsyncWebSocket webSocket = AsyncWebSocket("/ws");
	private:
		const int8 cmdDoNothing = 0;
		const int8 cmdBlink = 1;
		/** more stuff */
		const int8 cmdDebugWiFi = 9;
		void setCommand(int8 command);
		void doTheCommand();
};
#endif

helperWebServer.cpp

#include <helperWebServer.h>
helperWebServer::helperWebServer() {
	setupWebServer();
}
void helperWebServer::cycle() {
	doTheCommand();
}
void helperWebServer::setupWebServer() {
	webServer.addHandler(&webSocket);
	webServer.onNotFound([](AsyncWebServerRequest *request){ 
		request->send(404, "text/plain", "Link was not found!");  
	});
	webServer.on("/setCmd", HTTP_GET, [](AsyncWebServerRequest *request) {
		if(request->hasParam("cmd")) {
			if(request->getParam("cmd")->value() == "doBlink") {
				wpWebServer.setCommand(wpWebServer.cmdBlink);
			}
			if(request->getParam("cmd")->value() == "changeDebugWiFi") {
				wpWebServer.setCommand(wpWebServer.cmdDebugWiFi );
			}
		}
		request->send(200, "application/json", "{\"erg\":\"S_OK\"}");
	}
	webServer.begin();
}
void helperWebServer::setCommand(int8 command) {
	doCommand = command;
}
void helperWebServer::doTheCommand() {
	if(doCommand > 0) {
		if(doCommand == cmdBlink) {
			wpFZ.doBlink();
		}
		if(doCommand == changeDebugWiFi) {
			wpWiFi.setDebug();
		}
		doCommand = cmdDoNothing;
	}
}

Das PHP Script wird von jquery aufgerufen:

$('.setDebugWiFi').on('click', function() {
	const debugWiFi= {
		ip: d1Mini.ip
	};
	$.post(d1Mini.target + '.setDebugWiFi.req', debugWiFi, function(data) {
		console.log(data);
	}, 'json');
});

und das PHP Script:

namespace request\system;
use system\std;
use Exception;
d1Mini::$ip = std::posts('ip');
class d1Mini {
	public static $ip;
	const c_setDebugWiFi = 'setDebugWiFi';
	public static function setDebugWiFi() {
		try {
			$cmd = 'cmd=changeDebugWiFi';
			file_get_contents('http://' . d1Mini ::$ip . '/setCmd?' . $cmd, 0, self::getContext());
		} catch(Exception $e) {
			// Fail silently
		}
	}
	private static function getContext() {
		return stream_context_create([
			"http" => [
				"method"=>"GET",
				"timeout" => 1
			]
		]);
	}
}
switch(std::gets('param1')) {
	//###################################################################################
	case d1Mini ::c_setDebugWiFi :
		d1Mini ::setDebugWiFi();
		break;
}

'file_get_contents' läuft hier ins leere / timeout. Bei std Einstellungen 30 sec, deshalb der workarround mit dem 'getContext()'. Der ESP fürhrt die Aktion auch aus...

Hi, @zwieblum ,
Wiresharp meldet:

Hypertext Transfer Protocol
    GET /setNeoPixelBrightness?brightness=58.65 HTTP/1.1\r\n
        Request Method: GET
        Request URI: /setNeoPixelBrightness?brightness=58.65
            Request URI Path: /setNeoPixelBrightness
            Request URI Query: brightness=58.65
                Request URI Query Parameter: brightness=58.65
        Request Version: HTTP/1.1
    Host: 172.17.80.99\r\n
    Connection: close\r\n
    \r\n
    [Response in frame: 18393]
    [Full request URI: http://172.17.80.99/setNeoPixelBrightness?brightness=58.65]

und die Antwort ist:

Hypertext Transfer Protocol
    HTTP/1.1 200 OK\r\n
        Response Version: HTTP/1.1
        Status Code: 200
        [Status Code Description: OK]
        Response Phrase: OK
    Content-Length: 14\r\n
        [Content length: 14]
    Content-Type: application/json\r\n
    Connection: close\r\n
    Accept-Ranges: none\r\n
    \r\n
    [Request in frame: 18390]
    [Time since request: 0.084706000 seconds]
    [Request URI: /setNeoPixelBrightness?brightness=58.65]
    [Full request URI: http://172.17.80.99/setNeoPixelBrightness?brightness=58.65]
    File Data: 14 bytes
JavaScript Object Notation: application/json
    Object
        Member: erg
            [Path with value: /erg:S_OK]
            [Member with value: erg:S_OK]
            String value: S_OK
            Key: erg
            [Path: /erg]

PHP (über FF Entwicklertools) gibt das folgende aus:

hierbei habe ich das Script aus Post PHP, HTTP und response - #4 by checkers angepasst:

//file_get_contents('http://' . d1Mini ::$ip . '/setCmd?' . $cmd, 0, self::getContext());
file_get_contents('http://' . d1Mini ::$ip . '/setCmd?' . $cmd);

also ohne das Timeout.

Poste mal als Vergeich das Protokoll von Wireshark, wenn's funktioniert.

Also eine funtionierende Verbindung zum ESP findet über C# statt:

string returns = "S_ERROR";
string url = $"http://{_ip}/status";
try {
	WebClient webClient = new WebClient();
	returns = webClient.DownloadString(new Uri(url));
} catch(Exception ex) {
	wpDebug.WriteError(ex, $"{_ip}: '{returns}'");
}

'WebClient' ist von System.Net
Die Anfrage:

Hypertext Transfer Protocol
    GET /status HTTP/1.1\r\n
        Request Method: GET
        Request URI: /status
        Request Version: HTTP/1.1
    Host: 172.17.80.99\r\n
    Connection: Keep-Alive\r\n
    \r\n
    [Response in frame: 79109]
    [Full request URI: http://172.17.80.99/status]

Die Antwort:

Hypertext Transfer Protocol
    HTTP/1.1 200 OK\r\n
        Response Version: HTTP/1.1
        Status Code: 200
        [Status Code Description: OK]
        Response Phrase: OK
    Content-Length: 1005\r\n
        [Content length: 1005]
    Content-Type: application/json\r\n
    Connection: close\r\n
    Accept-Ranges: none\r\n
    \r\n
    [Request in frame: 79085]
    [Time since request: 0.047083000 seconds]
    [Request URI: /status]
    [Full request URI: http://172.17.80.99/status]
    File Data: 1005 bytes

Und eine weitere funktionierende Verbindung FF zu PHP:

Hypertext Transfer Protocol
    GET /std.request.activealarm.514.req HTTP/1.1\r\n
        Request Method: GET
        Request URI: /std.request.activealarm.514.req
        Request Version: HTTP/1.1
    Host: automation.freakazone.com\r\n
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0\r\n
    Accept: application/json, text/javascript, */*; q=0.01\r\n
    Accept-Language: de,en-US;q=0.7,en;q=0.3\r\n
    Accept-Encoding: gzip, deflate\r\n
    X-Requested-With: XMLHttpRequest\r\n
    Connection: keep-alive\r\n
    Referer: http://automation.freakazone.com/std.d1minicfg.htm\r\n
    Cookie: wps=5; PHPSESSID=5\r\n
        Cookie pair: wps=5
        Cookie pair: PHPSESSID=5
    \r\n
    [Response in frame: 5333]
    [Full request URI: http://automation.freakazone.com/std.request.activealarm.514.req]

und die Antwort:

Hypertext Transfer Protocol, has 2 chunks (including last chunk)
    HTTP/1.1 200 OK\r\n
        Response Version: HTTP/1.1
        Status Code: 200
        [Status Code Description: OK]
        Response Phrase: OK
    Date: Thu, 12 Sep 2024 14:16:22 GMT\r\n
    Server: Apache\r\n
    X-Powered-By: PHP/8.3.9\r\n
    Expires: Thu, 19 Nov 1981 08:52:00 GMT\r\n
    Cache-Control: no-store, no-cache, must-revalidate\r\n
    Pragma: no-cache\r\n
    Keep-Alive: timeout=5, max=92\r\n
    Connection: Keep-Alive\r\n
    Transfer-Encoding: chunked\r\n
    Content-Type: application/json\r\n
    \r\n
    [Request in frame: 5273]
    [Time since request: 0.239796000 seconds]
    [Request URI: /std.request.activealarm.514.req]
    [Full request URI: http://automation.freakazone.com/std.request.activealarm.514.req]
    HTTP chunked response
    File Data: 13449 bytes

Ich bin verwirrt. Warum antwortet im 2. Beispiel der Apache, aber nicht der ESP?

Nun ja, das sind die Beispiele, bei denen es funktioniert. Das zweite Beispiel zeigt, dass php / Apache korrekt läuft.

Also ...

Apache + ESP: geht nicht
irgend was anderes + ESP: nicht getestet
irgend was anderes + irgend was anders: geht.

Was ist das für ein Test?

1 Like

Also:

  1. Apache <=> ESP geht nicht.
    zum Vergleich:
  2. C# <=> ESP geht.
    und als Apache Test:
  3. Apache <=> C# geht.

Da streickt nur die Verbindung Apache <=> ESP
und du meintest doch,

Ich kann also die ESP's per http performant kommandieren, wenn ich den Umweg:
Apache <=> C# <=> ESP nehme, nicht aber den direkten weg: Apache <=> ESP. Das kommando geht zwar durch, aber der Apache wartet danach dann noch 30 sek. bis das timeout greift ohne eine antwort auswerten können.

Irgendwie habe ich bisher nicht verstanden, was Du eigentlich machen willst.
ESP als HTTP-Client greift auf Apache Webserver zu? Funktioniert problemlos, ebenso auf PHP auf Apache.

Gruß Tommy

Der ESP ist HTTP Listener.

Der bekommt was geschickt. mit

soll der auf commands reagieren. das tut der auch. allerdings läuft der apache dann beim warten auf eine antwort in ein timeout.

http://[ip]/[cmd]

mein workarround daraufhin war, dass ich einen c# server aufsetzte, der auf die anfrage:

http://[c#server]/set?ip=[ip]&cmd=[cmd]

reagiert, das dann weiter schickt:

http://[ip]/[cmd]

die antwort wird vom C# server entgegen genommen und weiter geleitet.

das ist aber für den zweck umständlich, da an zwei stellen programmcode geschrieben werden muss und zwei diesnte laufen müssen.
sodenn versuchte ich es wieder mit der direktverbindung von apache/php zu ESP.
Der Apache / webseite bleibt aber bis zum Timeout hängen.
da mir die antwort auf den cmd egal ist, reduzierte ich mit dem context die zeit des timeouts auf 1 sek.
bei 20 Lichtern, 'alles aus' sind das jedoch auch noch 20 Sekunden wartezeit, was sich extrem träge anfühlt.

Du willst also die Website des ESP in eine Website des Apache einbinden? Als Iframe?

Wenn er da auf einen Timeout läuft, ist Deine Antwort vom ESP falsch/unvollständig.

Gruß Tommy

Nö, ich möchte nur per http einen einfachen befehl übermitteln.
der ESP tut daraufhin was.
eigentlich fertig, aber das PHP Script wartet auf Antwort und bleibt hängen.
die antwort ist:

und kein iframe nur kommunikation:

ich hoffe du wartest nicht auf eine Reaktion von mir - denn ich habe nicht den notwendig Input von dir erhalten.

Nur so als Anmerkung:
Wenn ich nicht weiterkomme, dann baue ich einen MRE für den ESP der ein

http://172.17.80.99/setNeoPixelBrightness?brightness=58.65

verarbeiten kann (oder was auch immer).

Und einen MRE fürs php.

In 50% der Fälle finde ich dann selber meinen Fehler wenn ich mich auf das eigentliche Problem konzentrieren kann.

Wenn nicht, poste ich Dinge die "jeder" hier einfach ausprobieren kann.
So bin ich hier die letzten Jahre gut gefahren und 8 Tage hat kaum eine Lösung gedauert.

Und wie sendest Du den Request?

Gruß Tommy

Ist der mit request->send_P() noch nicht gesendet?

Was ist ein MRE?

das Forum how to verlinkt auf diesen Artikel: