출처 : 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()로 바로 송신할 수도 있다.
'임베디드 개발 > STM32 (ARM Cortex-M)' 카테고리의 다른 글
STM32 ] TCP Server, lwIP Raw API (13) | 2022.10.02 |
---|---|
STM32 ] lwIP UDP Echo Sever, 속도 테스트 (0) | 2022.09.25 |
STM32 ] LwIP 사용 초기설정 후 핑테스트 (9) | 2022.09.24 |
STM32 + MFC ] 델타 로봇 티칭 시스템 구현, 파일입출력 기능 사용 티칭 데이터 관리, 쓰레드 활용 반복작업 수행 (0) | 2022.09.19 |
STM32 + MFC ] 델타 로봇, RTOS 구조 변경 + 슬라이더 컨트롤을 통한 좌표 이동 제어 (7) | 2022.09.14 |