ESP8266 vs ESP32 ´, formatiertes 'print' mit %s %

Probleme bei der Installation?
Antworten
der_kps
User
Beiträge: 13
Registriert: Samstag 22. Februar 2020, 19:42

Hallo,
ich versuche immer noch einen AP/STA-Sketch, der auf dem ESP32 problemlos lief, auf dem D1mini Pro laufen zu lassen.
Im Momnent hängt es an einem formatierten 'print' mit %s %

In allen scripts zu AP/STA steht am Ende dieser Code, der nach der Verbindung mit ssid/passwort ( .isconnected() ) abgearbeitet wird:

Code: Alles auswählen

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)
while True:
  try:    
    print('waiting for client request')
    conn, addr = s.accept() 
    print('Got a connection from %s' % str(addr)) #old Syntax 
    #print('Got a connection from {}'.format((addr))) #new Syntax
    request = conn.recv(1024)  
   print('Content = %s' % str(request)) #old Syntax
    #print('Content = {}' .format(str(request))) #new Syntax		
	
    response = web_page() #storing html-data from web_page():
    conn.send('HTTP/1.1 200 OK\n')
    conn.send('Content-Type: text/html\n')
    conn.send('Connection: close\n\n')
    conn.sendall(response) #send html to client
    conn.close()
    print('Connection closed', '\n')
  except OSError as e:
    conn.close()
Diese beiden Anweisungen werden auf beiden Umgebungen (alte und neue Syntax) korrekt ausgeführt

Code: Alles auswählen

print('Got a connection from %s' % str(addr))  #alte Syntax
#print('Got a connection from {}' .format( str(addr)))  #neue Syntax
Korrrekte Antwort: Got a connection from ('192.168.4.2', 59356)


Diese Anweisung wird auf ESP8266 NICHT ausgeführt (alte und neue Syntax) !

Code: Alles auswählen

print('Content = %s' % str(request))
#print('Content = {}' .format( str(request)))
Unvollständige Antwort: C
sonst nichts, C stammt von Content...

Erwartete Antwort: Content = b'GET / HTTP/1.1\r\nHOST: 192.168.4.1\r\nUpgrade- ... \r\nConnection: keep-alive\r\n\r\n'

By the way: der weitere Code wird abgearbeitet, der letzte 'print' bleibt aber aus.

Hat jemand eine Idee, was hier falsch läuft?

mfg
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@der_kps: Hach ja, Socket-Programmierung. `recv(1024)` bedeutet nicht das was Du denkst was es bedeutet. Das bedeutet lese irgendwas zwischen 1 und 1024 Bytes aus dem Datenstrom. Wenn Du eine komplette ”Nachricht“ empfangen willst, dann musst Du dafür sorgen das `recv()` so oft aufgerufen wird, bis eine komplette Nachricht beisammen ist. Und aufgepasst: Falls mehr als eine Nachricht über diese Verbindung rein kommen kann, kann es passieren, dass Du schon einen Teil der nächsten Nachricht mit liest. Für's trennen und aufbewahren bist Du selbst verantwortlich.

Gegenstück `send(b"something")` bedeutet *nicht* das da die Bytefolge b"something" gesendet wird, sondern das irgendetwas zwischen b"s" und b"something" gesendet wird. Wieviele Bytes tatsächlich rausgegangen sind verrät Dir der Rückgabewert von `send()`. Das heisst da müsstest Du auch `send()` so oft aufrufen bis tatsächlich alles gesendet wurde. Es gibt aber zum Glück `sendall()` was das schon für Dich macht.

Edit: Um das noch mal ganz deutlich zu sagen: Der Code ist krass kaputt beim Empfangen und beim Senden. Dass das auf dem ESP32 lief ist Glück, auch dort kann der irgendwann mal über diese beiden Fehler auf die Nase fallen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Die `str()`-Aufrufe sind übrigens sowohl bei %s und % als auch bei `format()` überflüssig.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
der_kps
User
Beiträge: 13
Registriert: Samstag 22. Februar 2020, 19:42

Ich beziehe mich i.w. auf die Tutorials und den Erklärungen von 'randomnerdtutorials.com'.
Auf dem ESP32 läuft es. Nur der d1mini mit ESP8266 zickt rum.
Wenn ich den Wert von 1024 hoch setze ändert sich auch nichts.

Gerne würde ich es jetzt ausprobieren. Aber er zickt: Was gestern und heute Mittag noch lief, hängt jetzt.
Blink läuft, aber alles mit z.B. import bme280 oder i2c für OLED läuft jetzt nicht mehr.
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@der_kps: Natürlich ändert das nichts, wie sollte es auch, dann ist das eben das Empfangen von 1 bis mehr als 1024 Bytes. Wenn vorher schon weniger bei dem Aufruf kamen werden das doch nicht automagisch mehr wenn man theoretisch auch mehr nehmen würde. Noch mal: das ist ernsthaft kaputt. Das das irgendwo ”läuft” ändert nichts daran. Wenn das in irgendwelchen Tutorials so steht, dann sind die halt auch kaputt. Das steht an sehr vielen Stellen im Netz so und ist trotzdem falsch. Siehst Du ja gerade.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
der_kps
User
Beiträge: 13
Registriert: Samstag 22. Februar 2020, 19:42

Ok, die Tutorials haben kaputten Code.
So'n Pech. Das hilft uns allen weiter.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Ja, viele Tutorials haben kaputten Code. Jeder, der glaubt etwas verstanden zu haben, kann sich berufen fühlen, ein Tutorial zu schreiben.

Was Dir weiterhilft hat __blackjack__ aber auch geschrieben: Du musst eben selbst prüfen, ob die erwartete Menge an Bytes per recv() angekommen ist (oder möglicherweise auch zu viel). Dafür musst Du das Protokoll kennen. Für send() gilt ähnliches.
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wobei das Problem bei `send()` viel einfacher zu lösen ist: `sendall()` verwenden. Faustregel: `send()` braucht man *nie*, beziehungsweise nur in so exotischen Sonderfällen das man sagen kann das braucht man *nie*.

Das Protokoll wäre in diesem Fall HTTP. Da muss man bis zum ersten Zeilenende lesen um die Anfrage zu bekommen, und dann bis zur ersten Leerzeile um die Headerinformationen zu lesen. Oder man liest bis zur ersten Leerzeile und trennt sich dann die erste Zeile mit der Anfrage davon ab. Für zeilenweise Verarbeitung ist es am einfachsten wenn man sich mit `socket.makefile()` ein Dateiobjekt geben lässt. Das hat eine `readline()`-Methode die das ganze lesen bis Zeilenende und ggf. Puffern falls bei einem `recv()` mehr als die Zeile kam für einen erledigt. Und die `write()`-Methode schreibt auch alles raus was man ihr übergibt.

Eigentlich müsste man dann noch die Header auswerten ob da was über die Byteanzahl eines Bodies drin steht und den dann auch noch lesen. Keine Ahnung wie entspannt Browser/andere Clients darauf reagieren wenn sie einen Body schicken, der aber nicht gelesen wird, aber trotzdem eine HTTP-Antwort kommt. Also ob die schon vorher auf eine Antwort warten oder erst wenn all ihre Daten die sie gesendet haben vom Server gelesen wurden. An der Stelle könnte das dann klemmen. Und ob das in dem Fall passiert oder nicht, könnte auch wieder von Puffergrössen und anderen Variablen auf dem Weg zwischen Client und Server liegen. Das heisst falls das scheinbar funktioniert, ist nicht gesagt, dass das immer und überall funktioniert.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

In diesem Fall ist sendall nicht die Loesung, sondern write: https://docs.micropython.org/en/latest/ ... cket.write

Und es gibt readline: https://docs.micropython.org/en/latest/ ... t.readline
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Man kann aber auch `makefile()` benutzen, dann kann man Code schreiben der sowohl mit Micropython als auch mit anderen Python-Implementierungen läuft.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich wuerde das(*) nicht machen.

(*) Ich habe die Erfahrung gemacht, dass man mit uPy durchaus anders vorgehen muss, aufgrund beschraenkter Resourcen. ZB mit globalen Variablen mit Listen oder ggf. sogar arrays. Ich wuerde darum das nicht als Entwurfskriterium anlegen.
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Okay, ich habe noch nichts mit uPy gemacht. Klingt so ähnlich wie C und C++ auf „embedded“ Plattformen, wo ja meistens auch ein anderer Satz an Richtlinien verwendet wird als bei ”normalen” Anwendungen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten