본문 바로가기
DSP, MCU/STM32 (ARM Cortex-M)

STM32 ] UDP Server, lwIP Raw API

by eteo 2022. 9. 25.

 

 

udpServerRAW.h
0.00MB
udpServerRAW.c
0.00MB

출처 : https://controllerstech.com/stm32-ethenret-2-udp-server/

 

 

 

 

위 헤더파일과 소스파일을 추가하고 while 문 전에 udpServer_init(); 함수를 추가한다. 그밖에는 이전과 설정이 똑같다.

 

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "udpServerRAW.h"
/* USER CODE END Includes */

//...

/* USER CODE BEGIN PV */
extern struct netif gnetif;
/* USER CODE END PV */

//...

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART3_UART_Init();
  MX_RTC_Init();
  MX_LWIP_Init();
  /* USER CODE BEGIN 2 */
  udpServer_init();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  MX_LWIP_Process();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

 

udpServer_init() 에서 데이터가 들어오면 콜백될 함수를 설정하고 while 문 내 MX_LWIP_Process() 함수 안의 ethernetif_input(&gnetif) 함수는 들어오는 패킷들을 low level 에서 처리한다.

 

 

ethernetif_input() 함수의 설명이다.

/**
 * @brief This function should be called when a packet is ready to be read
 * from the interface. It uses the function low_level_input() that
 * should handle the actual reception of bytes from the network
 * interface. Then the type of the received packet is determined and
 * the appropriate input function is called.
 *
 * @param netif the lwip network interface structure for this ethernetif
 */
void ethernetif_input(struct netif *netif)
{
  struct pbuf *p = NULL;

  do
  {
    p = low_level_input( netif );
    if (p != NULL)
    {
      if (netif->input( p, netif) != ERR_OK )
      {
        pbuf_free(p);
      }
    }
  } while(p!=NULL);
}

 

 

 

 

 

udpServer_init() 함수를 살펴보자

 

udpServerRAW.c

#include "lwip/pbuf.h"
#include "lwip/udp.h"
#include "lwip/tcp.h"

#include "stdio.h"
#include "udpServerRAW.h"


void udp_receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);


/* IMPLEMENTATION FOR UDP Server :   source:https://www.geeksforgeeks.org/udp-server-client-implementation-c/

1. Create UDP socket.
2. Bind the socket to server address.
3. Wait until datagram packet arrives from client.
4. Process the datagram packet and send a reply to client.
5. Go back to Step 3.
*/

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, 200);

   err = udp_bind(upcb, &myIPADDR, 11111);  //  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);
   }
}

// udp_receive_callback will be called, when the client sends some data to the server
/* 4. Process the datagram packet and send a reply to client. */

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);
}

 

먼저 udp_new() 로 UDP control block을 생성한다.

struct udp_pcb *upcb;
upcb = udp_new();

 

udp_bind() 함수를 통해 생성된 블락과 local IP 주소 및 Port 를 바인드한다. IP 주소는 MX에서 static으로 설정한 IP주소이고 이 IP 주소를 네트워크 오더 방식으로 바꿔주는 과정이 필요하다. udp_bind() 함수의 세번째 매개변수로는 사용할 포트번호를 적는다.

err = udp_bind(upcb, &myIPADDR, 11111);

 

바인드가 성공하면 서버는 클라이언트가 데이터를 전송할 때까지 기다리게 된다. 그리고 서버가 데이터를 수신할 때마다 호출되는 콜백함수를 설정한다.

udp_recv(upcb, udp_receive_callback, NULL);

 

 

 

 

 

 

Hercules 프로그램으로 테스트 해보자

UDP 탭을 선택하고 연결할 상대(STM32) IP 주소를 적고 Port 명을 적는다. Local port는 지금은 아무렇게나 지정해도 상관없다.

 

 

 

 

 

 

 

 

클라이언트가 보낸 데이터를 서버가 수신하면 콜백함수가 호출될 거고 이 안에서 수신된 데이터그램 패킷을 처리하고 클라이언트에 응답을 보낼 수도 있다.

 

아래는 수신된 문자열 대신 클라이언트의 IP 를 출력하게 바꿨다.

 

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);
	int len = sprintf (buf,"Hello %s From UDP SERVER\n", remoteIP);

	/* 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);
}

 

 

  • 먼저 데이터 전송을 위해 txBuf 를 만든다.
  • Middlewares - Third_Party - LwIP - src - include - lwip - ip_addr.h 파일에 선언된 ipaddr_ntoa() 함수를 통해 IP 주소를 int 형에서 char* 문자열로 바꾼다.
  • pbuf_alloc() 함수를 통해 패킷 버퍼를 위한 메모리를 할당한다.
  • pbuf_take() 함수를 통해 데이터를 패킷 버퍼에 복사한다.
  • udp_connect() 함수를 통해 클라이언트와 connect 한다. 콜백함수로 들어온 파라미터를 그대로 활용해 connect 할 수 있다.
  • udp_send() 함수를 통해데이터를 전송한다. 여기서 upcb는 local 과 remote 커넥션의 정보를 갖고 있는 컨트롤 블락이고 txBuf는 전송될 데이터에 대한 정보가 담긴 패킷 버퍼이다.
  • udp_disconnect() 함수를 통해 새로운 클라이언트를 받을 수 있도록 UDP 커넥션을 끊는다.
  • pbuf_free(txBuf) 와 pbuf_free(p)를 통해 수신버퍼와 송신버퍼의 메모리를 다 할당 해제한다.

 

참고로 여기서는 udp_connect() 한 뒤 udp_send()하는데 UDP는 비연결 지향이라 ip랑 port만 알면 upd_sendto()로 바로 송신할 수도 있다.