MicroPython Daten per VPN senden

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
einfachTobi
User
Beiträge: 491
Registriert: Mittwoch 13. November 2019, 08:38

FastAPI verwendet pydantic um das Format der akzeptierten Requests festzulegen. Dazu musst du also ein Model erstellen:
Hier ein Beispiel:

Code: Alles auswählen

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class BasicPost(BaseModel):
    sensor_id: int
    value: float

@app.post("/")
async def root(message: BasicPost):
    print(message.sensor_id, message.value)
    return message

# von deinem ESP schickst du dann ab:
#...
import ujson
import urequests

post_data = ujson.dumps({"sensor_id": 42, "value": 0.815})
response = urequests.post("http://217.160.51.17:8000", data=post_data)
#...
Benutzeravatar
Dennis89
User
Beiträge: 1124
Registriert: Freitag 11. Dezember 2020, 15:13

Edit: Vielen Dank auch an "einfachTobi" hatte den folgenden Beitrag schon geschrieben, als ich deine Antwort noch nicht sah. Deinen Code habe ich gerade probiert und erhalte im Terminal auch

Code: Alles auswählen

INFO:     Started reloader process [25507] using WatchFiles
INFO:     Started server process [25511]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
42 0.815
INFO:     78.43.40.238:32606 - "POST / HTTP/1.0" 200 OK
INFO:     78.43.40.238:49082 - "GET / HTTP/1.1" 405 Method Not Allowed
Folgender Text entstand schon vor dem Beispiel von "einfachTobi". Ich lasse den trotzdem stehen, das ihr seht was in meinem Kopf so vor sich geht. :)
einfachTobi hat geschrieben: Freitag 7. Oktober 2022, 09:32 Du hast im FastAPI-Code @app.get("/") geschrieben. Damit erlaubst du nur GET-Requests. Wenn du POST-Requests auf diese Route zulassen willst, musst du @app.post("/") verwenden.
Danke, das habe ich geändert.
Das führte mich dann leider schon zum nächsten Fehler:

Code: Alles auswählen

INFO:     Started reloader process [25387] using WatchFiles
INFO:     Started server process [25389]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     78.43.40.238:32604 - "POST / HTTP/1.0" 422 Unprocessable Entity
INFO:     78.43.40.238:49036 - "GET / HTTP/1.1" 405 Method Not Allowed
Die Art der Daten die ich sende stimmt wohl nicht. Mein letzter Versuch war mit 'json'.

Code: Alles auswählen

import socket
import urequests

def is_connected():
    try:
        connection = socket.socket()
        connection.connect(socket.getaddrinfo('www.micropython.org', 80)[0][-1])
        return True
    except Exception as error:
        print(eroor)
        return False


def main():
    if is_connected():
        response = urequests.post("http://217.160.51.17:8000", json={'temperature': '3'})
        print(response)
        print('verbunden')
            
            
if __name__ == '__main__':
    main()
Und auf dem Server:

Code: Alles auswählen

from fastapi import FastAPI

app = FastAPI()

@app.post("/")
async def root(data):
    return data
Es gab hier auch so einen Fehler. Da werden dann Klassen erstellt. Bevor ich jetzt weiter rumrate und Code durch die Gegend kopiere ohne zu wissen was da los ist. Wisst ihr was da los ist? Wie bzw. wo ist definiert wie die Daten auszusehen haben? 'urequest.post' sendet nur bestimmte Datenstrukturen: Bytes, Tuple,... . Dann sehe ich die Fehlersuche weiter bei FastAPI, bzw ich muss dem Server irgendwie sagen, in welcher Struktur die Daten kommen?

Danke und Grüße
Dennis

P.S. Für heute Mittag bin ich mal an den Vorbereitungen für das Gewächshaus in dass der ESP nachher reinkommt.
"When I got the music, I got a place to go" [Rancid, 1993]
einfachTobi
User
Beiträge: 491
Registriert: Mittwoch 13. November 2019, 08:38

Dennis89 hat geschrieben: Freitag 7. Oktober 2022, 12:23 Edit: Vielen Dank auch an "einfachTobi" hatte den folgenden Beitrag schon geschrieben, als ich deine Antwort noch nicht sah. Deinen Code habe ich gerade probiert und erhalte im Terminal auch

Code: Alles auswählen

INFO:     Started reloader process [25507] using WatchFiles
INFO:     Started server process [25511]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
42 0.815
INFO:     78.43.40.238:32606 - "POST / HTTP/1.0" 200 OK
INFO:     78.43.40.238:49082 - "GET / HTTP/1.1" 405 Method Not Allowed
Das ist ja auch korrekt. Offenbar hast du nach deinem Post noch ein GET-Request an der Server geschickt, ohne eine Route dafür zu haben. Das ist dann das "Method Not Allowed". Aber in der Zeile nach "INFO: Application startup complete." wird ja genau das ausgegeben, was du mit dem ESP an den Server geschickt hast. Das Konzept von FastAPI sieht halt vor, dass jeder eingehene Request gegen ein Model formal geprüft wird und erst dann überhaupt in die Verarbeitung kommt.
Benutzeravatar
Dennis89
User
Beiträge: 1124
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

oh die Zeile dazwischen ging irgendwie unter.
Jetzt habe ich das versucht und im Terminal kommt immer die "42 0.815" an. Sobald ich dann die Seite "http://SERVERIP:8000" aktualisiere, dann bekomme ich die "405 Method Not Allowed" - Meldung und das steht auch im Browser.
Dachte das ich vielleicht etwas anderes zurück geben muss, ein Dictonary wie in dem Beispiel aus dem Tutorial:

Code: Alles auswählen

@app.post("/")
async def root(message: BasicPost):
    print(message.sensor_id, message.value)
    return {message.sensor_id: message.value}
Das machte aber keinen Unterschied. Aber im Großen und Ganzen kann mir das ja eigentlich auch egal sein. Wenn ich jetzt eine Datenbank installiere, dann kann Python die ankommenden Sensordaten in die Datenbank schreiben und ich kann die dann mit Grafana visualisieren und wäre an meinem Ziel :)

Vielen Dank und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
einfachTobi
User
Beiträge: 491
Registriert: Mittwoch 13. November 2019, 08:38

Wenn du die Seite mit dem Browser aufrufst, wird ja auch ein GET-Request gemacht. Mit @app.post sagt du aber, dass diese Route für POST zuständig ist. Daher das Method Not Allowed. Du kannst eine weitere Funktion erstellen und mit einem app.get-Dekorator versehen (also @app.get("/")) Die ist dann für GET-Requests auf den Pfad zuständig.

Aber genau wie du sagst: jetzt hast du alles beisammen, um die Daten in Empfang zu nehmen und musst sie nur noch in einer Datenbank ablegen.
Benutzeravatar
Dennis89
User
Beiträge: 1124
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Morgen,

na klar, das leuchtet mir ein. Danke für die Erklärungen.
Dann müsste ich den Rest hinbekommen, hoffentlich :)


Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1124
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

zur Zeit läuft der ESP im Gewächshaus und sendet fleißig die Temperaturwerte. Vielen Dank für eure Hilfe.

Falls das jemand nachbauen will oder ein ähnliches Problem hat:
Ich lese die Sensordaten und sende sie an den Server in einer Dauerschleife. Immer im viertelstunden Intervall.
Dabei habe ich mich an das Codebeispiel von @einfachTobi gehalten

Code: Alles auswählen

import ujson
import urequests

post_data = ujson.dumps({"sensor_id": 42, "value": 0.815})
response = urequests.post("http://XXX.XXX.XXX:YYYY", data=post_data)
Allerdings habe ich nach einer gewissen Zeit immer ein "OSError 23" erhalten.
Das Problem war, dass 'response' nicht geschlossen wird und somit der Speicher voll gelaufen ist(?),

Auf jeden Fall wollte ich das Thema damit abschließen, dass das bis jetzt problemlos läuft, seit dem ich nach dem senden ein 'response.close()' eingefügt habe.

Viele Grüße
Dennis
Zuletzt geändert von Dennis89 am Sonntag 16. Oktober 2022, 20:00, insgesamt 1-mal geändert.
"When I got the music, I got a place to go" [Rancid, 1993]
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Danke, dass Du uns hier Deine Zugangsdaten schickst, so kann ich auch noch ein paar Temperaturdaten schicken. Wenn man einen Server öffentlich im Internet betreibt, sollte man den irgendwie absichern, indem man z.B. geheime Zugangsdaten braucht.
Benutzeravatar
Dennis89
User
Beiträge: 1124
Registriert: Freitag 11. Dezember 2020, 15:13

Dann muss ich noch eine Gast-Temperatur-Spalte in der Datenbank einrichten :D

Aber klar du hast Recht, es wäre möglich das jeder Daten senden kann. Wie würde so eine Absicherung denn aussehen? Geht das auch mit Username und Passwort?

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Ja, der klassische Weg ist über einen Authentication-Header. Natürlich sollten die Daten per HTTPS verschlüsselt übertragen werden.
Benutzeravatar
Dennis89
User
Beiträge: 1124
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für die Antwort.

Dann habe ich doch noch ein Stück Arbeit vor mir. Mal schauen wie und wann ich das unter kriege.
Wenn ich es habe, gebe ich wieder Rückmeldung und wenn nicht, dann muss ich eh wieder nach Unterstützung fragen. Also ihr hört so oder so wieder von mir. (Ein anderes Problem steckt auch schon in der Pipline)

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Antworten