Forum: Mikrocontroller und Digitale Elektronik LwIP UDP Callback


von Matthis S. (reamonswat)


Lesenswert?

Hallo zusammen,

mit der Funktion udp_recv() kann man eine Callback-Funktion festlegen, 
die automatisch aufgerufen wird wenn ein UDP-Paket empfangen wird.

Das Problem ist, dass diese Callback-Funktion udp_echo_recv() nicht 
aufgerufen wird.
Hier mein Code (abgespeckt):

//Funktionen:

void udp_echo_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const 
ip_addr_t *addr, u16_t port)
{
  HAL_GPIO_WritePin(LD6_GPIO_Port, LD6_Pin, GPIO_PIN_SET); //Blaue LED 
an
}

void udpApp1_init(void){
  err_t         udpErr;
  ip_addr_t     ownIPaddr;

  udpPcb1_p = udp_new();

  if(udpPcb1_p != NULL)
  {
    IP4_ADDR(&ownIPaddr, 192, 168, 1, 3); //STM32-IP

    udp_bind(udpPcb1_p, &ownIPaddr, 65100);
  }
}

int main(void){

  udp_recv(udpPcb1_p, udp_echo_recv, NULL);
  udpApp1_init();

  while(1){}

}


Ich bedanke mich schon mal für die Hilfe.

von Jim M. (turboj)


Lesenswert?

Ich hätte bei der Bind Addresse einfach 0.0.0.0 genommen, aber 
ansonsten:

Fehler ist im nicht geposteten Code.

von STM Apprentice (Gast)


Lesenswert?

Matthis S. schrieb:
> udp_recv

Du rufst udp_recv(...) auf bevor du udp_new() gemacht hast.
Damit ist udpPcb1_p nicht gültig gesetzt und udp_recv(...)
wird damit nichts Vernünfiges anfangen können.

Also
1
  udpPcb1_p = udp_new();
2
  IP4_ADDR(&ownIPaddr, 192, 168, 1, 3); //STM32-IP
3
  udp_bind(udpPcb1_p, &ownIPaddr, 65100);
4
  udp_recv(udpPcb1_p, udp_echo_recv, NULL);

Das müsste dann funktionieren.

Für die Portnummer NULL anzugeben ist vielleicht nicht
die glücklichste Wahl.

von STM Apprentice (Gast)


Lesenswert?

STM Apprentice schrieb:
> Für die Portnummer NULL anzugeben ist vielleicht nicht
> die glücklichste Wahl.

ooops .... das war wohl ein falscher Fehler von mir.

STM Apprentice schrieb:
> Das müsste dann funktionieren.

Dann habe ich noch gelernt dass man als Minimum eine
wie auch immer getriggerte Event-Loop braucht damit
LwIP was tut.

Also:
1
  while (1)
2
  {
3
    /* check if any packet received */
4
    if (ETH_CheckFrameReceived())
5
    {
6
      LwIP_Pkt_Handle();
7
    }
8
    /* handle periodic timers for LwIP */
9
    LwIP_Periodic_Handle (LocalTime);
10
  }

von Albert F. (Gast)


Lesenswert?

Ich wuerde

LwIP_Pkt_Handle();

im while(1) loop lassen, nicht hinter ETH_CheckFrameReceived(). Bei 
pings und UDP oder mehrere Verbindungen geht das ganze durcheinander.

Das war meine Erfahrung

von Matthis S. (reamonswat)


Lesenswert?

STM Apprentice schrieb:
> Matthis S. schrieb:
Erstmal danke für die Antworten.

>> udp_recv
>
> Du rufst udp_recv(...) auf bevor du udp_new() gemacht hast.
> Damit ist udpPcb1_p nicht gültig gesetzt und udp_recv(...)
> wird damit nichts Vernünfiges anfangen können.


Sorry, ich habe es falsch eingefügt. Ich rufe also udp_recv() nach 
udpApp1_init() auf. Also wie du es geschrieben hast, aber funktioniert 
trotzdem nicht.

>Dann habe ich noch gelernt dass man als Minimum eine
>wie auch immer getriggerte Event-Loop braucht damit
>LwIP was tut.

>Also:
>while (1)
>  {
>    /* check if any packet received */
>    if (ETH_CheckFrameReceived())
>    {
>     LwIP_Pkt_Handle();
>    }
>    /* handle periodic timers for LwIP */
>    LwIP_Periodic_Handle (LocalTime);
>  }


Ohne den Inhalt dieser While-Schleife kann ich UDP-Pakete senden. Nur 
empfangen klappt nicht.
Die drei Funktionen sind außerdem bei mir nirgendwo deklariert. Ich 
benutze die HAL Treiber, die von STM32CubeMX generiert sind. Wo sind bei 
dir diese Funktionen deklariert?

Ich bin ziemlich neu in dem Gebiet. Entschuldigt meine Fragen :)

von Matthis S. (reamonswat)


Lesenswert?

Welche andere Möglichkeiten gibt es denn UDP-Pakete zu empfangen?
Ich habe von der Funktion ip_input gelesen, aber daraus werde ich nicht 
schlau.

von whip (Gast)


Lesenswert?

Matthis S. schrieb:
> Ohne den Inhalt dieser While-Schleife kann ich UDP-Pakete senden. Nur
> empfangen klappt nicht.

UDP Pakete senden geht ohne ein Pollen in der Schleife. In der Funktion 
udp_send werden die Daten direkt an den Ethernet MAC Treiber uebergeben. 
Fuer das Empfangen muss dem lwIP Stack der empfangen Ethernet-Frame 
uebergeben werden. Deshalb brauchst du da sehr wohl eine Poll-Funktion 
in der while-Schleife.

von STM Apprentice (Gast)


Lesenswert?

Matthis S. schrieb:
> Welche andere Möglichkeiten gibt es denn UDP-Pakete zu empfangen?

Bevor wir endgültig ins Rätselraten geraten solltest du
vielleicht doch mal etwas vollständigeren Code zu deinem
Problem posten.

Das Prinzip von meinem geposteten Vorschlag funktioniert
bei mir jedenfalls.

Zudem solltest du dir angewöhnen:

--------------------------------------------------
Wichtige Regeln - erst lesen, dann posten!
Formatierung (mehr Informationen...)

    [c]C-Code[ /c]
^^^^^^^^^^^^^^^^^^^^
.............
--------------------------------------------------

von STM Apprentice (Gast)


Lesenswert?

Matthis S. schrieb:
> Nur empfangen klappt nicht.

Womit sendest du denn dass du weisst dass dein LwIP was
empfangen soll?

Und was sendest du? Adresse und Port ok?
Firewall blockings?

UDP senden kann man immer, da sich kein Schwein darum
kümmert ob das Zeugs irgendwo ankommt (im Gegensatz zu TCP).

von Matthis S. (reamonswat)


Lesenswert?

> Womit sendest du denn dass du weisst dass dein LwIP was
> empfangen soll?
>
> Und was sendest du? Adresse und Port ok?
> Firewall blockings?
>
> UDP senden kann man immer, da sich kein Schwein darum
> kümmert ob das Zeugs irgendwo ankommt (im Gegensatz zu TCP).

Firewall gibt es nicht. Zum empfangen der von meinem PHY gesendeten 
Pakete benutze ich das VN5610 Ethernet Interface von Vector.

Welche Infos noch hättest du gerne?

Mit dem Code unten sendet der PHY jede Sekunde ein Paket.
vollständiger Code:

1
#include "main.h"
2
#include "stm32f4xx_hal.h"
3
#include "lwip.h"
4
#include "gpio.h"
5
#include "udp.h"
6
#include <string.h>
7
8
void SystemClock_Config(void);
9
void Error_Handler(void);
10
11
extern struct netif gnetif;
12
static struct udp_pcb *udpPcb1_p;
13
14
#define CLIENT1_MAC_0 0x9C
15
#define CLIENT1_MAC_1 0x5C
16
#define CLIENT1_MAC_2 0x66
17
#define CLIENT1_MAC_3 0x10
18
#define CLIENT1_MAC_4 0x46
19
#define CLIENT1_MAC_5 0x93
20
21
static const char clientMagicPacket_c[] = 
22
{
23
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
24
  CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5,
25
  CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5,
26
  CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5,
27
  CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5,
28
  CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5,
29
  CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5,
30
  CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5,
31
  CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5,
32
  CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5,
33
  CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5,
34
  CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5,
35
  CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5,
36
  CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5,
37
  CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5,
38
  CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5,
39
  CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5,
40
};
41
42
43
//Funktionen:
44
45
void udpApp1_sendMagicPacket(void){
46
  ip_addr_t       client1IpAddr;
47
  struct pbuf     *ethTxBuffer_p;
48
  
49
  IP4_ADDR(&client1IpAddr, 192, 168, 1, 255); 
50
                                             
51
  ethTxBuffer_p = pbuf_alloc(PBUF_TRANSPORT, sizeof(clientMagicPacket_c), PBUF_RAM);
52
  if (ethTxBuffer_p == NULL){}
53
  
54
  memcpy(ethTxBuffer_p->payload, clientMagicPacket_c, sizeof(clientMagicPacket_c));
55
    
56
  udp_sendto(udpPcb1_p, ethTxBuffer_p, &client1IpAddr,9);
57
58
  pbuf_free(ethTxBuffer_p);
59
}
60
61
void udp_echo_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
62
{
63
  HAL_GPIO_WritePin(LD6_GPIO_Port, LD6_Pin, GPIO_PIN_SET); //Blaue LED an
64
}
65
66
void udpApp1_init(void){
67
  err_t         udpErr;
68
  ip_addr_t     ownIPaddr;
69
  
70
  udpPcb1_p = udp_new();
71
  
72
  if(udpPcb1_p != NULL)
73
  {
74
    IP4_ADDR(&ownIPaddr, 192, 168, 1, 3); //STM32-IP
75
    
76
    udpErr = udp_bind(udpPcb1_p, &ownIPaddr, 65100); 
77
    
78
    udp_recv(udpPcb1_p, udp_echo_recv, NULL);
79
    
80
    if (udpErr ==ERR_OK){} 
81
  }
82
}
83
84
int main(void)
85
{
86
87
  /* MCU Configuration----------------------------------------------------------*/
88
89
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
90
  HAL_Init();
91
92
  /* Configure the system clock */
93
  SystemClock_Config();
94
95
  /* Initialize all configured peripherals */
96
  MX_GPIO_Init();
97
  MX_LWIP_Init();
98
99
  /* USER CODE BEGIN 2 */
100
  
101
  udpApp1_init();
102
  
103
  /* USER CODE END 2 */
104
105
  /* Infinite loop */
106
  /* USER CODE BEGIN WHILE */
107
  while (1)
108
  {
109
    
110
  /* USER CODE END WHILE */
111
112
  /* USER CODE BEGIN 3 */
113
   
114
    udpApp1_sendMagicPacket(); 
115
    HAL_Delay(1000);
116
    
117
118
  ethernetif_input(&gnetif);
119
  sys_check_timeouts();
120
  }

von Matthis S. (reamonswat)


Lesenswert?

> Das Prinzip von meinem geposteten Vorschlag funktioniert
> bei mir jedenfalls.

Kannst du bitte deinen vollständigen Code posten, damit ich direkt 
vergleichen kann.

>UDP Pakete senden geht ohne ein Pollen in der Schleife. In der Funktion
>udp_send werden die Daten direkt an den Ethernet MAC Treiber uebergeben.
>Fuer das Empfangen muss dem lwIP Stack der empfangen Ethernet-Frame
>uebergeben werden. Deshalb brauchst du da sehr wohl eine Poll-Funktion
>in der while-Schleife.

Ah ok. Daran wirds wohl liegen.
Aber die drei funktionen sind in meiner Lib nicht drin.

ETH_CheckFrameReceived()
LwIP_Pkt_Handle();
LwIP_Periodic_Handle (LocalTime);

Kannst du mir bitte sagen wo die bei dir zu finden sind?

>Wichtige Regeln - erst lesen, dann posten!

Was habe ich überlesen?

von STM Apprentice (Gast)


Lesenswert?

Matthis S. schrieb:
> Mit dem Code unten sendet der PHY jede Sekunde ein Paket.
> vollständiger Code:

Matthis S. schrieb:
> Welche Infos noch hättest du gerne?

Die Problemstellung zum geposteten Code.

Ausserdem die Antwort auf meine Frage:

STM Apprentice schrieb:
> Womit sendest du denn dass du weisst dass dein LwIP was
> empfangen soll?

Welche Hardware benutzt du? Eval Board oder selbstgestrickte
Ethernet-Anbindung?

von STM Apprentice (Gast)


Lesenswert?

Matthis S. schrieb:
> Was habe ich überlesen?

Beim Posten des ersten Codes hast du was überlesen.
Beim Posten des letzten Codes hast du es dann kapiert.
Vergleiche selbst.

von STM Apprentice (Gast)


Lesenswert?

Matthis S. schrieb:
> Aber die drei funktionen sind in meiner Lib nicht drin.
>
> ETH_CheckFrameReceived()

Müsste in <stm32f4x7_eth.c> vorhanden sein.

> LwIP_Pkt_Handle();

findet sich bei mir in <netconf.c>
Macht nichts anderes als  ethernetif_input(&gnetif);


> LwIP_Periodic_Handle (LocalTime);

findet sich bei mir in <netconf.c> und bedient DHCP Timer,
TCP Timer und ARP Timer.

Ich rede vom LwIP 1.4.1, kann sein dass du eine Version 2.xx
verwendest.

von Matthis S. (reamonswat)


Lesenswert?

STM Apprentice schrieb:

> Die Problemstellung zum geposteten Code.
Die Problemstellung steht im ersten Post:
>mit der Funktion udp_recv() kann man eine Callback-Funktion festlegen,
>die automatisch aufgerufen wird wenn ein UDP-Paket empfangen wird.
>Das Problem ist, dass diese Callback-Funktion udp_echo_recv() nicht
>aufgerufen wird.


> Ausserdem die Antwort auf meine Frage:
> STM Apprentice schrieb:
>> Womit sendest du denn dass du weisst dass dein LwIP was
>> empfangen soll?
Steht im vollständigen Code. Mit udp_sendto().
Oder meinst du was anderes?


> Welche Hardware benutzt du? Eval Board oder selbstgestrickte
> Ethernet-Anbindung?
STM32F407-Discoveryboard verbunden über RMII mit dem PHY DP83848.
Das PHY ist mit dem VN5610 Interface über Ethernet verbunden.
VN5610 kennt ihr vielleicht nicht wenn ihr nicht in der 
Automotiv-Branche beschäftigt seid. Das ist aber nicht so wichtig. 
Tatsache ist, die VN5610 empfängt die vom PHY kommende Pakete.
Nur wenn ich von VN5610 was an mein PHY sende, wird die Funktion 
udp_echo_recv() nicht aufgerufen.

von Matthis S. (reamonswat)


Lesenswert?

Ich bin nicht grad der Experte wenn es um LwIP geht.
Ich beschäftige mich erst seit ein paar Wochen damit.

von Matthis S. (reamonswat)


Lesenswert?

> Ich rede vom LwIP 1.4.1, kann sein dass du eine Version 2.xx
> verwendest.

Ich weiss gar nicht wo ich die Version finde. Muss erstmal schauen...

von Matthis S. (reamonswat)


Lesenswert?

STM Apprentice schrieb:
> Matthis S. schrieb:
>> Aber die drei funktionen sind in meiner Lib nicht drin.
>>
>> ETH_CheckFrameReceived()
>
> Müsste in <stm32f4x7_eth.c> vorhanden sein.
Bei mir sind u.a. nur die Funktionen:
HAL_ETH_GetReceivedFrame();
HAL_ETH_GetReceivedFrame_IT();


>> LwIP_Pkt_Handle();
>
> findet sich bei mir in <netconf.c>
> Macht nichts anderes als  ethernetif_input(&gnetif);
ethernetif_input(&gnetif) ist bei mir in <ethernetif.c> vorhanden. Also 
kann ich die stattdessen benutzen?


>> LwIP_Periodic_Handle (LocalTime);
>
> findet sich bei mir in <netconf.c> und bedient DHCP Timer,
> TCP Timer und ARP Timer.
Bei mir ist weder LwIP_Periodic_Handle (LocalTime) noch <netconf.c> 
vorhanden.

> Ich rede vom LwIP 1.4.1, kann sein dass du eine Version 2.xx
> verwendest.
Die Version finde ich leider nicht.

von whip (Gast)


Lesenswert?

Matthis S. schrieb:
> ethernetif_input(&gnetif);

In dieser Funktion muesste eine Funktion - z.B. low_level_input, falls 
der lwIP Ethernet Skeleton verwendet wird - aufgerufen werden, die aus 
dem Deskriptor des Ethernet MACs den Frame in einen lwIP pbuf kopiert. 
Dann muesste weiter in der o.g. Funktion ethernet_input mit dem pbuf 
aufgerufen werden. In der Funktion wird dann ueber ip_input etc im 
Idealfall die receive callback Funktion aufgerufen.

Wie sieht die Funktion bei dir aus? Empfaengt dein Ethernet MAC den 
Frame? Gibt es da evtl. einen Receive Interrupt?

von Matthis S. (reamonswat)


Lesenswert?

Jetzt wirds schwierig für mich.

whip schrieb:
> Matthis S. schrieb:
>> ethernetif_input(&gnetif);
>
> In dieser Funktion muesste eine Funktion - z.B. low_level_input, falls
> der lwIP Ethernet Skeleton verwendet wird - aufgerufen werden, die aus
> dem Deskriptor des Ethernet MACs den Frame in einen lwIP pbuf kopiert.

Bisher kapier ich das. low_level_input() ist auch vorhanden.

Kannst du bitte das etwas genauer erklären:

> Dann muesste weiter in der o.g. Funktion ethernet_input mit dem pbuf
> aufgerufen werden. In der Funktion wird dann ueber ip_input etc im
> Idealfall die receive callback Funktion aufgerufen.
>
> Wie sieht die Funktion bei dir aus? Empfaengt dein Ethernet MAC den
> Frame? Gibt es da evtl. einen Receive Interrupt?

von whip (Gast)


Lesenswert?

Matthis S. schrieb:
> Kannst du bitte das etwas genauer erklären

Der Funktionkopf von low_level_input:
1
static struct pbuf *
2
low_level_input(struct netif *netif)

Es wird ein Pointer auf einen pbuf zurueckgegeben. struct pbuf ist der 
Datentyp, in dem lwIP die Frames speichert.

Jetzt hast du ja einen Treiber fuer den Ethernet MAC. Der sollte eine 
Funktion readFrame haben. Diese muss in low_level_input aufgerufen 
werden, damit der empfangene Frame von dem Speicherbereich des Ethernet 
MACs, typischerweise wird da mit so genannten Deskriptoren gearbeitet, 
in den Speicherbereich des lwIP Stacks kopiert werden kann. Der 
Rueckgabewert von low_level_input ist der empfangen Frame, der dann vom 
lwIP Stack weiterverarbeitet werden kann. Da wo low_level_input 
aufgerufen wird, wird zur Weiterverarbeitung durch lwIP typischerweise 
eine input Funktion aufgerufen, in der dann der Frame ausgewertet wird 
und die Callback Funktion aufgerufen wird. So ungefaehr:
1
static struct pbuf *p = low_level_input(struct netif *netif); 
2
3
if (p != NULL)
4
{
5
   netif->input(p, netif);
6
}

Idealerweise setzt du mal einen Breakpoint auf den input-Aufruf, dann 
kannst du sicher sein, dass Hardware seitig der Frame empfangen wird und 
korrekt an lwIP weitergeleitet wird. Den Frameinhalt kann man mit sich 
mit p->payload anschauen und z.B. mit Wireshark vergleichen.

Beitrag #5107797 wurde von einem Moderator gelöscht.
von Matthis S. (reamonswat)


Lesenswert?

Es hat funktioniert. Als ich in Wireshark das Paket gesendet habe, habe 
ich vergessen die MAC-Adresse auf Broadcast einzustellen. Dämlicher 
Fehler :)

Ich bedanke mich sehr für eure tolle Hilfe.

Edit: Das Destination Port war auch falsch

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.