Kleiner UDP Server für Arduino & Co Projekte

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
ChristianH
Beiträge: 8
Registriert: 21.06.2020 20:15

Kleiner UDP Server für Arduino & Co Projekte

Beitrag von ChristianH »

Hallo,

mit einem ESP32 (https://www.espressif.com/en/products/s ... 2/overview) und der Firmware ESP Easy (https://www.letscontrolit.com/wiki/inde ... le=ESPEasy) lese ich die Raumtemperatur aus. Der Sensor ist ein HDC1080, aber das nur am Rande.

Nun wollte ich gerne diese Daten per WLAN (per UDP) auf einen beliebigen Computer senden und dort anzeigen lassen. Da ich ja -Ironie on- der geborene Programmierer bin -Ironie off- habe ich mir ein kleines Programm erstellt welches diese Aufgabe erledigt. Im Endeffekt soll es ja Spaß machen.

Die Software läuft auf einem MAC und ich vermute auf Windows sollte das auch funktionieren. Allerdings habe ich das nicht getestet.

Konstruktive Vorschläge zur Verbesserung sind sehr willkommen. Bashing wird nach >/dev/null befördert.

Mir ist klar das dieses Programm kein Masterpice ist. Aber wenn ich mir die aktuellen Beispiele auf http://www.PureArea.net ansehe (ich mag diese Seite sehr) sind diese extrem alt und funktionieren teilweise nicht mehr mit der aktuellen PB Version. Vielleicht kann ein einfaches Programm wie dieses dem einen oder anderen eine Starthilfe geben.
Gruß Christian

Code: Alles auswählen

EnableExplicit

Define param = 0
Define EventID = 0
Define Quit = 0

Global Info$, T$, H$, Window_0, Button_0, String_0, Text_0, String_1, Text_1

Enumeration FormFont
  #Font_Window_0_0
EndEnumeration

LoadFont(#Font_Window_0_0,".SF NS", 14)


Procedure OpenWindow_0(x = 0, y = 0, width = 400, height = 180)
  Window_0 = OpenWindow(#PB_Any, x, y, width, height,  "Thermometer", #PB_Window_SystemMenu)
  Button_0 = ButtonGadget(#PB_Any, 280, 124, 100, 25, "Quit")
  String_0 = StringGadget(#PB_Any, 140, 42, 70, 25, "")
  SetGadgetFont(String_0, FontID(#Font_Window_0_0))
  Text_0 = TextGadget(#PB_Any, 50, 44, 80, 25, "Temperatur")
  String_1 = StringGadget(#PB_Any, 140, 120, 70, 25, "")
  SetGadgetFont(String_1, FontID(#Font_Window_0_0))
  Text_1 = TextGadget(#PB_Any, 50, 123, 80, 25, "Luftfeuchte")
  SetWindowColor(Window_0,  RGB(27,159,186))
EndProcedure

Procedure Window_0_Events(event)
  Select event
    Case #PB_Event_CloseWindow
      ProcedureReturn #False

    Case #PB_Event_Menu
      Select EventMenu()
      EndSelect

    Case #PB_Event_Gadget
      Select EventGadget()
      EndSelect
  EndSelect
  ProcedureReturn #True
EndProcedure





Procedure server(param)
  Define *buffer = 0
  Define server  = 0
  Define size = 0
  Define param = 0
  
    
*buffer = AllocateMemory(2048)
server = CreateNetworkServer(0, 6600, #PB_Network_UDP)

If *buffer And server
  Repeat
    Delay(200)
    If NetworkServerEvent() = #PB_NetworkEvent_Data
      size = ReceiveNetworkData(EventClient(),*buffer,2048)
      Info$ = (PeekS(*buffer,size,#PB_Ascii))
      
      
      If Left(Info$,11) = "Temperature"
          T$=Right(Info$,4)
        EndIf
        
        If Left(Info$,8) = "Humidity"
          H$=Right(Info$,4)
        EndIf
     
      
    EndIf
  ForEver
EndIf
EndProcedure


OpenWindow_0()
If  InitNetwork()<>0
  
  If  CreateNetworkServer(0, 6600, #PB_Network_UDP)<>0
       CreateThread(@server(), param)
    
  EndIf
EndIf




Repeat 
   Delay(100)
  EventID =  WaitWindowEvent(50)
 
  SetGadgetText(String_0, T$)
  SetGadgetText(String_1, H$)
 
  Select EventID
    Case #PB_Event_CloseWindow
      Quit = 1
      
      Case 3         
          Quit = 1
   
  EndSelect
 Until Quit = 1 Or EventID = #PB_Event_CloseWindow
  
Benutzeravatar
mk-soft
Beiträge: 3695
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Kleiner UDP Server für Arduino & Co Projekte

Beitrag von mk-soft »

Ein Arduino habe ich nicht,
aber wie man besser mit Threads umgeht schon und wie man dann aus dem Thread die Daten an die GUI bekommt.
Bei macOS und Linux darf man aus Thread nicht die Gadgets aktualisieren.

Siehe Mini Thread Control Beispiel 3
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
ChristianH
Beiträge: 8
Registriert: 21.06.2020 20:15

Re: Kleiner UDP Server für Arduino & Co Projekte

Beitrag von ChristianH »

Hallo Michael,

danke für den Hinweis. Wie Du sicher unschwer an meinem Code erkennen kannst wird das etwas dauern bis ich Dein Beispiel verstehen/begreifen kann.

"Bei macOS und Linux darf man aus Thread nicht die Gadgets aktualisieren."
Warum darf man das nicht?

Gruß Christian
Benutzeravatar
mk-soft
Beiträge: 3695
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Kleiner UDP Server für Arduino & Co Projekte

Beitrag von mk-soft »

ChristianH hat geschrieben:Hallo Michael,

danke für den Hinweis. Wie Du sicher unschwer an meinem Code erkennen kannst wird das etwas dauern bis ich Dein Beispiel verstehen/begreifen kann.

"Bei macOS und Linux darf man aus Thread nicht die Gadgets aktualisieren."
Warum darf man das nicht?

Gruß Christian
Führt zum Crash da diese vom der OS nicht Threadsafe sind.


Sorry, hatte etwas zu viel Langeweile

Noch nicht getestet

Code: Alles auswählen

;-TOP

;- Begin Mini Thread Control

;  by mk-soft, Version 1.08, 20.10.2019

CompilerIf Not #PB_Compiler_Thread
  CompilerError "Use Compiler-Option ThreadSafe!"
CompilerEndIf

Structure udtThreadControl
  ThreadID.i
  UserID.i
  Signal.i
  Pause.i
  Exit.i
EndStructure

Procedure StartThread(*Data.udtThreadControl, *Procedure) ; ThreadID
  If Not IsThread(*Data\ThreadID)
    *Data\Exit = #False
    *Data\Pause = #False
    *Data\ThreadID = CreateThread(*Procedure, *Data)
  EndIf
  ProcedureReturn *Data\ThreadID
EndProcedure

Procedure StopThread(*Data.udtThreadControl, Wait = 1000) ; Void
  If IsThread(*Data\ThreadID)
    *Data\Exit = #True
    If *Data\Pause
      *Data\Pause = #False
      SignalSemaphore(*Data\Signal)
    EndIf
    If Wait
      If WaitThread(*Data\ThreadID, Wait) = 0
        KillThread(*Data\ThreadID)
      EndIf
      *Data\ThreadID = 0
      *Data\Pause = #False
      *Data\Exit = #False
      If *Data\Signal
        FreeSemaphore(*Data\Signal)
        *Data\Signal = 0
      EndIf
    EndIf
  EndIf
EndProcedure

Procedure FreeThread(*Data.udtThreadControl, Stop = #True, Wait = 1000) ; True or False
  If IsThread(*Data\ThreadID)
    If Stop
      StopThread(*Data, Wait)
      FreeStructure(*Data)
      ProcedureReturn #True
    Else
      ProcedureReturn #False
    EndIf
  Else
    If *Data\Signal
      FreeSemaphore(*Data\Signal)
    EndIf
    FreeStructure(*Data)
    ProcedureReturn #True
  EndIf
EndProcedure

Procedure ThreadPause(*Data.udtThreadControl) ; Void
  If IsThread(*Data\ThreadID)
    If Not *Data\Signal
      *Data\Signal = CreateSemaphore()
    EndIf
    If Not *Data\Pause
      *Data\Pause = #True
    EndIf
  EndIf
EndProcedure

Procedure ThreadResume(*Data.udtThreadControl) ; Void
  If IsThread(*Data\ThreadID)
    If *Data\Pause
      *Data\Pause = #False
      SignalSemaphore(*Data\Signal)
    EndIf
  EndIf
EndProcedure

;- End Mini Thread Control

; ****

;- UDP Server
InitNetwork()

; ---- String Helper ----

Procedure AllocateString(String.s) ; Result = Pointer
  Protected *mem.string = AllocateStructure(String)
  If *mem
    *mem\s = String
  EndIf
  ProcedureReturn *mem
EndProcedure

Procedure.s FreeString(*mem.string) ; Result String
  Protected r1.s
  If *mem
    r1 = *mem\s
    FreeStructure(*mem)
  EndIf
  ProcedureReturn r1
EndProcedure

; ----


Enumeration #PB_Event_FirstCustomValue
  #MyEvent_ThreadSendString
  #MyEvent_ThreadFinished
EndEnumeration

Enumeration Window
  #Main
EndEnumeration

Enumeration Gadget
  #ListLogging
  #StringData1
  #StringData2
  #ButtonStart
  #ButtonPauseResume
  #ButtonStop
EndEnumeration


; Extends always own data structure with structure from thread control
Structure udtThreadData Extends udtThreadControl
  ; GUI Data
  Window.i
  Logging.i
  Data1.i
  Data2.i
  ; Server
  Server.i
  Port.i
EndStructure

Declare MyThread(*Data.udtThreadData)

; Create Data always with AllocateStructure  
Global *th1.udtThreadData = AllocateStructure(udtThreadData)
*th1\UserID = 1
*th1\Window = #Main
*th1\Logging = #ListLogging
*th1\Data1 = #StringData1
*th1\Data2 = #StringData2
*th1\Port = 6600

Procedure Main()
  Protected info.s
  
  If OpenWindow(#Main, 50, 50, 600, 400, "Mini Thread Control", #PB_Window_SystemMenu)
    ListViewGadget(#ListLogging, 5, 5, 590, 320)
    StringGadget(#StringData1, 5, 330, 290, 25, "", #PB_String_ReadOnly)
    StringGadget(#StringData2, 305, 330, 290, 25, "", #PB_String_ReadOnly)
    ButtonGadget(#ButtonStart, 5, 365, 120, 30, "Start")
    ButtonGadget(#ButtonPauseResume, 130, 365, 120, 30, "Pause")
    ButtonGadget(#ButtonStop, 255, 365, 120, 30, "Stop")
    
    DisableGadget(#ButtonPauseResume, #True)
    
    Repeat
      Select WaitWindowEvent() 
        Case #PB_Event_CloseWindow
          FreeThread(*th1)
          Break
        Case #PB_Event_Gadget
          Select EventGadget()
            Case #ButtonStart
              StartThread(*th1, @MyThread())
            Case #ButtonPauseResume
              If IsThread(*th1\ThreadID)
                If Not *th1\Pause
                  ThreadPause(*th1)
                  SetGadgetText(#ButtonPauseResume, "Resume")
                Else
                  ThreadResume(*th1)
                  SetGadgetText(#ButtonPauseResume, "Pause")
                EndIf
              EndIf
            Case #ButtonStop
              StopThread(*th1)
              SetGadgetText(#ButtonPauseResume, "Pause")
              
          EndSelect
          
        Case #MyEvent_ThreadSendString
          Select EventGadget()
            Case #ListLogging
              ; Receive string over event data
              AddGadgetItem(#ListLogging, -1, FreeString(EventData()))
              ; Small trick to move last item
              SetGadgetState(#ListLogging, CountGadgetItems(#ListLogging) - 1)
              SetGadgetState(#ListLogging, -1)
            Case #StringData1
              info = FreeString(EventData())
              SetGadgetText(#StringData1, info + " °C")
            Case #StringData2
              info = FreeString(EventData())
              SetGadgetText(#StringData2, info + " ??")
              
          EndSelect
          
      EndSelect
    ForEver
  EndIf
  
EndProcedure : Main()

End

Procedure MyThread(*Data.udtThreadData)
  Protected stringdata.s, cnt
  Protected *buffer, size
  Protected info.s
  
  
  ; Send string over PostEvent and parameter EventData
  
  With *Data
    stringdata = "Init Server " + \UserID
    PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
    ;TODO
    \Server = CreateNetworkServer(#PB_Any, \Port, #PB_Network_UDP)
    If \Server = 0
      stringdata = "Error: Create Server on Port " + \Port
      PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
      stringdata = "Finished Thread " + \UserID
      PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
      \ThreadID = 0
      ProcedureReturn 0
    EndIf
    
    *buffer = AllocateMemory(2048)
    
    stringdata = "Start Server " + \UserID
    PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
    ;TODO
    Repeat
      ; 1. Query on thread pause
      If \Pause
        stringdata = "Pause Thread " + \UserID
        PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
        WaitSemaphore(\Signal)
        stringdata = "Resume Thread " + \UserID
        PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
      EndIf
      ; 2. Query on thread cancel
      If \Exit
        Break
      EndIf
      ;TODO Cyle Process
      Select NetworkServerEvent()
        Case #PB_NetworkEvent_None
          Delay(100)
        Case #PB_NetworkEvent_Connect ; Only TCP/IP
          ;
        Case #PB_NetworkEvent_Disconnect ; Only TCP/IP
          ;                               ;
        Case #PB_NetworkEvent_Data
          size = ReceiveNetworkData(EventClient(), *buffer, 2048)
          info = (PeekS(*buffer, size, #PB_Ascii))
          If Left(info, 11) = "Temperature"
            stringdata = Right(info, 4)
            PostEvent(#MyEvent_ThreadSendString, \Window, \Data1, 0, AllocateString(stringdata))
          EndIf
          If Left(info, 8) = "Humidity"
            stringdata = Right(info, 4)
            PostEvent(#MyEvent_ThreadSendString, \Window, \Data2, 0, AllocateString(stringdata))
          EndIf
          
        EndSelect
          
    ForEver
    
    If \Exit
      ;TODO Thread Cancel
      stringdata = "Cancel Thread " + \UserID
      PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
    Else
      ;TODO Thread Finished
      stringdata = "Finished Thread " + \UserID
      PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
    EndIf
    
    stringdata = "Close Server " + \UserID
    PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
    
    CloseNetworkServer(\Server)
    FreeMemory(*buffer)
    
    ; 3. Clear ThreadID 
    \ThreadID = 0
  EndWith
EndProcedure

Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
ChristianH
Beiträge: 8
Registriert: 21.06.2020 20:15

Re: Kleiner UDP Server für Arduino & Co Projekte

Beitrag von ChristianH »

Hi Michael,

vielen Dank für eine Mühe. Der Code läuft auf dem Mac 1A und zeigt Temperatur und Luftfeuchte an. :allright:

Nun werde ich mir das ausdrucken und in Ruhe durchlesen und hoffen das mein Kopf schlau genug wird das zu verstehen. Vielleicht wird ja irgendwann
mal eine kleinen Wetterstation daraus. :lol:

Gruß Christian
Benutzeravatar
mk-soft
Beiträge: 3695
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Kleiner UDP Server für Arduino & Co Projekte

Beitrag von mk-soft »

Mach dir auch mal Gedanken bei der Übertragung der Daten.
Bei Netzwerk gilt das gleiche wie bei serieller Kommunikation. Es wird der Empfangsbuffer ausgelesen.
Wenn etwas mal länger dauert, kann es passieren das die Daten nicht zusammenhängend bekommst oder schon ein teil der nächsten Daten.
Eventuell mit Steuerzeichen in den Daten arbeiten. #STX$ (StartOfText) und #ETX$ (EndOfText)

Link zum studieren: UdpServer
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
ChristianH
Beiträge: 8
Registriert: 21.06.2020 20:15

Re: Kleiner UDP Server für Arduino & Co Projekte

Beitrag von ChristianH »

Ja danke für den Hinweis. Ich weiss das UDP ein verbindungsloses Protokoll ist und man da auf Anwendungsebene ein wenig Arbeit
reinstecken muss.

Aber dank Deiner Hilfe bin ich einen großen Schritt weiter und werde nun erst einmal meinen Wissensstand aufarbeiten. :lol:

Ich glaube ich habe PB seit ca. 20 Jahren registriert und komme ich immer wieder darauf zurück. Es ist eine tolle Sprache
und "eigentlich" nicht so sehr schwer. Aber Job und Familie und 1000 andere Dinge benötigen auch viel Zeit.

LG Christian
Antworten