Mise en oeuvre sur un microcontrôleur STM32F429ZI (Nucleo-F429ZI) en UDP
Testé avec STM32CubeIDE 1.12.1
Objectif de ce tutoriel :
Etablir une communication ethernet entre un PC et une carte Nucleo-F429ZI en utilisant le protocole UDP.
Une fois la configuration hardware réalisée, nous aborderons deux cas d'utilisations :
- 1 La carte Nucleo-F429ZI dans le rôle de serveur UDP sur lequel vient se connecter le PC avec l'outil Netcat
- 2 La carte Nucleo-F429ZI dans le rôle de client UDP qui vient se connecter au serveur Netcat démarré sur le PC préalablement.
Step 0 : configuration de l'Ethernet histoire de communiquer avec le PC et Netcat
Step 1 : configuration des horloges du STM32
Step 1 : configuration du driver LWIP
Le schéma électronique de la nucleo-F429ZI nous renseigne sur le driver hardware (LAN8742), qu'on choisit donc pour notre projet.
Le checksum est calculé par le hardware :
On désactive le DHCP pour fixer une adresse IPV4 de notre choix. Ici 192.168.0.51
Gateway 192.168.0.50 (ce sera l'adresse IP du PC de test).
On alloue 10 KBytes pour la mémoire.
et on n'oublie pas d'activer les "interruptions" ethernet dans la section NVIC de System core dans le Pinout and configuration (IOC).
Serveur UDP
Dans cette partie nous allons traiter de la mise en oeuvre du code en C pour réaliser un serveur UDP sur la carte Nucleo-F429ZI. Il aura l'adresse IP 192.168.0.51 et le PC : 192.168.0.50
Serveur-Step 0 : que faire dans le main ?
On rajoute la bibliothèque udp.h ains
#include "lwip/udp.h"
void udp_receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
void udpServer_init(void);
extern struct netif gnetif; // déjà défini ailleurs mais on en a besoin donc on le met en extern
struct udp_pcb *upcb;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART3_UART_Init();
MX_LWIP_Init();
udpServer_init();
while (1)
{
ethernetif_input(&gnetif);
sys_check_timeouts();
}
} //fin main
Serveur-Step 1 : que faire ensuite ?
On rajoute les fonctions udbServer_init et udp_receive_callback
void udpServer_init(void)
{
// UDP Control Block structure
struct udp_pcb *upcb;
err_t err;
/* 1. Create a new UDP control block */
upcb = udp_new();
/* 2. Bind the upcb to the local port */
ip_addr_t myIPADDR;
IP_ADDR4(&myIPADDR, 192, 168, 0, 51);
err = udp_bind(upcb, &myIPADDR, 7000); // 7 is the server UDP port
/* 3. Set a receive callback for the upcb */
if(err == ERR_OK)
{
udp_recv(upcb, udp_receive_callback, NULL);
}
else
{
udp_remove(upcb);
}
}
//cette fonction sera exécutée lorsqu'un paquet de données UDP arrivera sur la nucleo-F429ZI
//Elle renvoie le contenu du paquet et un petit Hello xxx From UDP SERVER
void udp_receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
struct pbuf *txBuf;
/* Get the IP of the Client */
char *remoteIP = ipaddr_ntoa(addr);
char buf[100];
int len = sprintf (buf,"Hello %s From UDP SERVER\n", (char*)p->payload);
/* allocate pbuf from RAM*/
txBuf = pbuf_alloc(PBUF_TRANSPORT,len, PBUF_RAM);
/* copy the data into the buffer */
pbuf_take(txBuf, buf, len);
/* Connect to the remote client */
udp_connect(upcb, addr, port);
/* Send a Reply to the Client */
udp_send(upcb, txBuf);
/* free the UDP connection, so we can accept new clients */
udp_disconnect(upcb);
/* Free the p_tx buffer */
pbuf_free(txBuf);
/* Free the p buffer */
pbuf_free(p);
}
Serveur-Step 2 : c'est bien tout ça mais comment je teste ?
Pour les tests, la bonne manière est :
1 - de regarder si la carte répond à un ping
2 - de tester si le serveur répond
ping 192.168.0.51
Si la réponse est du genre :
➜ ozone ping 192.168.0.51
PING 192.168.0.51 (192.168.0.51) 56(84) bytes of data.
64 bytes from 192.168.0.51: icmp_seq=1 ttl=255 time=0.580 ms
64 bytes from 192.168.0.51: icmp_seq=2 ttl=255 time=0.686 ms
^C
--- 192.168.0.51 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1006ms
rtt min/avg/max/mdev = 0.580/0.633/0.686/0.053 ms
C'est que ça fonctionne, on peut passer au test du serveur UDP.
Pour ce faire, on utilise l'outils Netcat (nc), cela donne :
➜ ozone nc -u 192.168.0.51 7000
-u : UDP
192.168.0.51 adresse du PC
7000 port utilisé dans notre cas.
Rien ne se passe, c'est normal, il n'y a pas de données échangées pour le moment entre le PC et la nucleo.
On tape Alice dans le terminal et si on voit cela :
➜ ozone nc -u 192.168.0.51 7000
Alice
Hello Alice
From UDP SERVER
C'est gagné !
Client UDP
Dans cette partie nous allons traiter de la mise en oeuvre du code en C pour réaliser un client UDP sur la carte Nucleo-F429ZI. Il aura l'adresse IP 192.168.0.51 et le PC qui sera un serveur aura l'adresse : 192.168.0.50
Client-Step 0 : que faire dans le main ?
On rajoute la bibliothèque udp.h ains
#include "lwip/udp.h"
void udp_receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
void udpClient_connect(void);
static void udpClient_send(void);
extern struct netif gnetif; // déjà défini ailleurs mais on en a besoin donc on le met en extern
struct udp_pcb *upcb;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART3_UART_Init();
MX_LWIP_Init();
udpClient_connect();
while (1)
{
ethernetif_input(&gnetif);
sys_check_timeouts();
}
} //fin main
Client-Step 1 : que faire ensuite ?
On rajoute les fonctions udpClient_connect et udp_receive_callback
void udpClient_connect(void)
{
err_t err;
/* 1. Create a new UDP control block */
upcb = udp_new();
/* Bind the block to module's IP and port */
ip_addr_t myIPaddr;
IP_ADDR4(&myIPaddr, 192, 168, 0, 51);
udp_bind(upcb, &myIPaddr, 7000);
/* configure destination IP address and port */
ip_addr_t DestIPaddr;
IP_ADDR4(&DestIPaddr, 192, 168, 0, 50);
err= udp_connect(upcb, &DestIPaddr, 7000);
if (err == ERR_OK)
{
/* 2. Send message to server */
udpClient_send ();
/* 3. Set a receive callback for the upcb */
udp_recv(upcb, udp_receive_callback, NULL);
}
}
static void udpClient_send(void)
{
struct pbuf *txBuf;
char data[100];
int len = sprintf(data, "sending UDP client message\r\n");
/* allocate pbuf from pool*/
txBuf = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
if (txBuf != NULL)
{
/* copy data to pbuf */
pbuf_take(txBuf, data, len);
/* send udp data */
udp_send(upcb, txBuf);
/* free pbuf */
pbuf_free(txBuf);
}
}
//cette fonction sera exécutée lorsqu'un paquet de données UDP arrivera sur la nucleo-F429ZI
//Elle renvoie le contenu du paquet et un petit Hello xxx From UDP SERVER
void udp_receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
struct pbuf *txBuf;
/* Get the IP of the Client */
char *remoteIP = ipaddr_ntoa(addr);
char buf[100];
int len = sprintf (buf,"Hello %s From Nucleo-F429ZI UDP client\n", (char*)p->payload);
/* allocate pbuf from RAM*/
txBuf = pbuf_alloc(PBUF_TRANSPORT,len, PBUF_RAM);
/* copy the data into the buffer */
pbuf_take(txBuf, buf, len);
/* Connect to the remote client */
udp_connect(upcb, addr, port);
/* Send a Reply to the Client */
udp_send(upcb, txBuf);
/* free the UDP connection, so we can accept new clients */
udp_disconnect(upcb);
/* Free the p_tx buffer */
pbuf_free(txBuf);
/* Free the p buffer */
pbuf_free(p);
}
Client-Step 2 : c'est bien tout ça mais comment je teste ?
Pour les tests, la bonne manière est :
1 - de regarder si la carte répond à un ping
2 - de tester si le serveur répond
ping 192.168.0.51
Si la réponse est du genre :
➜ ozone ping 192.168.0.51
PING 192.168.0.51 (192.168.0.51) 56(84) bytes of data.
64 bytes from 192.168.0.51: icmp_seq=1 ttl=255 time=0.580 ms
64 bytes from 192.168.0.51: icmp_seq=2 ttl=255 time=0.686 ms
^C
--- 192.168.0.51 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1006ms
rtt min/avg/max/mdev = 0.580/0.633/0.686/0.053 ms
C'est que ça fonctionne, on peut passer au test de notre nucleo-F429ZI.
Ensuite on va lance le serveur UDP sur le PC
Pour ce faire, on utilise l'outils Netcat (nc), cela donne :
➜ ozone nc -ul 7000
-u : UDP
-l : listen (le PC est un serveur en écoute sur le port 7000)
7000 port utilisé dans notre cas.
On reset la nucleo
On tape Alice dans le terminal et si on voit cela :
➜ ozone nc -ul 7000
sending UDP client message
C'est gagné !
Et maintenant si c'est le PC qui envoie des données, cela donne :
➜ ozone nc -ul 7000
sending UDP client message
Bob
Hello Bob
From Nucleo-F429ZI UDP client
That's all folks!