LwIP UDP Callback

OP #5105849
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.
Gast #5106220
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.
Gast #5106222
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
  }
OP #5106348
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 :)
Gast #5106402
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.
Gast #5106449
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]
^^^^^^^^^^^^^^^^^^^^
.............
--------------------------------------------------
Gast #5106458
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).
OP #5106506
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
  }
OP #5106518
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?
Gast #5106522
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?
Gast #5106533
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.
OP #5106601
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.
OP #5106644
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.
Gast #5106651
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?
OP #5106680
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?
Gast #5106807
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren