Overview
In this tutorial, you will learn how to use STM32 FreeRTOS DNS to resolve hostnames into IP addresses using a network-enabled STM32 system. This guide demonstrates how to perform a basic DNS lookup and retrieve the IP address associated with a domain name.
Building on the previous tutorial, STM32 FreeRTOS Network Interface – Ethernet Setup, this example assumes that the STM32 device is already connected to a network using FreeRTOS and DHCP. Once connected, the system can communicate with external servers, including DNS servers, to resolve domain names.
In this tutorial, we will perform a DNS lookup for a known hostname and observe how the STM32 retrieves and processes the returned IP address.
What You Will Learn
- How to configure DNS support on an STM32 running freeRTOS and lwIP
- How DHCP supplies the DNS server IP used by the example
- How the DNS test task performs a hostname lookup and waits for completion
- How the example project is organized and integrated with the earlier network initialization tutorial
Prerequisites
This tutorial assumes familiarity with STM32CubeIDE, freeRTOS, lwIP networking basics, and the earlier STM32 network initialization tutorial referenced in the overview.
Materials List
- FTDI to USB
- NUCLEO-F439ZI
- Breadboards Kit Include 2PCS 830 Point 2PCS 400 Point Solderless Breadboards
Project Structure

Hardware Configuration / Pinouts
Overview
This tutorial uses hardware. The original FTDI wiring, Nucleo board reference, schematic, and configuration material from the legacy post are preserved below.
FTDI Pinouts
FTDI to USB Pinout from right to left
- Pin 1 – GND
- Pin 2 – CTS
- Pin 3 – VCC
- Pin 4 – TX
- Pin 5 – RX
- Pin 6 – DTR
- USB Mini – Connect to PC via USB cable
Make sure the jumper is set for 5v or 3v which ever is being used to power the FTDI device. If we look below at the schematic, it is showing in this case as 5v.


Nucleo-F439ZI

Schematic

freeRTOS Task List

Project Setup
For this tutorial rather than trying to capture screenshots for each pinout & configuration screen, we generated a PDF of all of the settings within the IOC file, which is included below. Parameters that have changed from the default values are highlighted by being in BOLD with an asterisk “*”
Code Walkthrough
main.h
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.h
* @brief : Header for main.c file.
* This file contains the common defines of the application.
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */
/* USER CODE END ET */
/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */
/* USER CODE END EC */
/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */
/* USER CODE END EM */
/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);
/* USER CODE BEGIN EFP */
/* USER CODE END EFP */
/* Private defines -----------------------------------------------------------*/
#define RMII_MDC_Pin GPIO_PIN_1
#define RMII_MDC_GPIO_Port GPIOC
#define RMII_REF_CLK_Pin GPIO_PIN_1
#define RMII_REF_CLK_GPIO_Port GPIOA
#define RMII_MDIO_Pin GPIO_PIN_2
#define RMII_MDIO_GPIO_Port GPIOA
#define RMII_CRS_DV_Pin GPIO_PIN_7
#define RMII_CRS_DV_GPIO_Port GPIOA
#define RMII_RXD0_Pin GPIO_PIN_4
#define RMII_RXD0_GPIO_Port GPIOC
#define RMII_RXD1_Pin GPIO_PIN_5
#define RMII_RXD1_GPIO_Port GPIOC
#define RMII_TXD1_Pin GPIO_PIN_13
#define RMII_TXD1_GPIO_Port GPIOB
#define USB_PowerSwitchOn_Pin GPIO_PIN_6
#define USB_PowerSwitchOn_GPIO_Port GPIOG
#define USB_OverCurrent_Pin GPIO_PIN_7
#define USB_OverCurrent_GPIO_Port GPIOG
#define RMII_TX_EN_Pin GPIO_PIN_11
#define RMII_TX_EN_GPIO_Port GPIOG
#define RMII_TXD0_Pin GPIO_PIN_13
#define RMII_TXD0_GPIO_Port GPIOG
/* USER CODE BEGIN Private defines */
#define REDIRECT_PRINTF
/* USER CODE END Private defines */
#ifdef __cplusplus
}
#endif
#endif /* __MAIN_H */
main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"
#include "lwip.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "string.h"
#include "stdbool.h"
#include "dnsLookup.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;
/* Definitions for networkTask */
osThreadId_t networkTaskHandle;
const osThreadAttr_t networkTask_attributes = {
.name = "networkTask",
.stack_size = 768 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for TestDNSTask */
osThreadId_t TestDNSTaskHandle;
const osThreadAttr_t TestDNSTask_attributes = {
.name = "TestDNSTask",
.stack_size = 512 * 4,
.priority = (osPriority_t) osPriorityLow,
};
/* USER CODE BEGIN PV */
bool waitingOnNetwork = true;
bool dhcpReady = false;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
void networkInitializationTask(void *argument);
void TestDNSFunction(void *argument);
/* USER CODE BEGIN PFP */
void link_myCallBack(struct netif *netif);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#ifdef REDIRECT_PRINTF
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#endif
#ifdef REDIRECT_PRINTF
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
#endif
#define ETH_DEVICE 0
void link_myCallBack(struct netif *netif) {
uint32_t regValue = 0;
if (HAL_ETH_ReadPHYRegister(&heth, ETH_DEVICE, PHY_BSR, ®Value) == HAL_OK) {
if((regValue & PHY_LINKED_STATUS) == (uint16_t)RESET) {
if (!netif_is_link_up(netif)) { // Link status = disconnected
netif_set_down(netif);
printf("Network cable is now disconnected!!!\r\n");
netif_set_link_down(netif);
}
} else {
if (netif_is_link_up(netif)) { // Link status = connected
printf("Network cable is now connected!!!\r\n");
printf("Rebooting the system!!!\r\n");
osDelay(2000);
// We could try to write some sort of recovery logic to handle a reconnection, however.... (Boom!!!)
NVIC_SystemReset(); // Reboot the microprocessor and start the application over.
}
}
}
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
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_USART2_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Init scheduler */
osKernelInitialize();
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* creation of networkTask */
networkTaskHandle = osThreadNew(networkInitializationTask, NULL, &networkTask_attributes);
/* creation of TestDNSTask */
TestDNSTaskHandle = osThreadNew(TestDNSFunction, NULL, &TestDNSTask_attributes);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 180;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Activate the Over-Drive mode
*/
if (HAL_PWREx_EnableOverDrive() != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief USART2 Initialization Function
* @param None
* @retval None
*/
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 19200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(USB_PowerSwitchOn_GPIO_Port, USB_PowerSwitchOn_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : USB_PowerSwitchOn_Pin */
GPIO_InitStruct.Pin = USB_PowerSwitchOn_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(USB_PowerSwitchOn_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : USB_OverCurrent_Pin */
GPIO_InitStruct.Pin = USB_OverCurrent_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(USB_OverCurrent_GPIO_Port, &GPIO_InitStruct);
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/* USER CODE BEGIN Header_networkInitializationTask */
/**
* @brief Function implementing the networkTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_networkInitializationTask */
void networkInitializationTask(void *argument)
{
/* init code for LWIP */
MX_LWIP_Init();
/* USER CODE BEGIN 5 */
osDelay(2000);
extern struct netif gnetif;
while (ip4_addr_isany_val(*netif_ip4_addr(&gnetif))) {
osDelay(200);
}
printf("\x1b[2J\x1b[H"); // Clear the dumb terminal screen
printf("Starting Network Initialization......\r\n");
char buffer[1024];
printf("==============================================================================\n\r");
char *ipstr = ip4addr_ntoa(netif_ip4_addr(&gnetif));
sprintf(buffer, "Networking Ready!!!\r\n");
printf(buffer);
sprintf(buffer, "IP Address: %s assigned to this device by the DHCP Server\r\n", ipstr);
printf(buffer);
const ip_addr_t *ipaddr;
ipaddr = dns_getserver(0);
dhcpReady = !(ip4_addr_isany_val(*ipaddr));
while (!dhcpReady) {
osDelay(200);
ipaddr = dns_getserver(0);
dhcpReady = !(ip4_addr_isany_val(*ipaddr));
}
sprintf(buffer, "DHCP Assigned DNS SERVER IP: %s\r\n", ip4addr_ntoa(ipaddr));
printf(buffer);
// Here we override the default callback defined in the MX_LWIP_Init() function which defaults to:
// netif_set_link_callback(&gnetif, ethernet_link_status_updated);
//
// Setup our callback to handle ethernet cable disconnect/reconnect and override the default callback of: ethernet_link_status_updated
netif_set_link_callback(&gnetif, link_myCallBack);
sprintf(buffer, "Networking Initialization Complete!!!\r\n");
printf(buffer);
printf("Task Done!!!\r\n");
printf("==============================================================================\n\r");
waitingOnNetwork = false;
/* Infinite loop */
for(;;)
{
osDelay(1);
}
/* USER CODE END 5 */
}
/* USER CODE BEGIN Header_TestDNSFunction */
/**
* @brief Function implementing the TestDNSTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_TestDNSFunction */
void TestDNSFunction(void *argument)
{
/* USER CODE BEGIN TestDNSFunction */
dnsGetHostName_t getHostNameResp = {0};
while (waitingOnNetwork) {
osDelay(10);
}
printf("Starting DNS Testing......\r\n");
getHostNameResp.name = "www.facebook.com";
getHostNameResp.done = false;
printf("getHostByName: %s Should return: 157.240.3.35\r\n", getHostNameResp.name);
bool found = getHostByName(getHostNameResp.name, &getHostNameResp);
while (!found || getHostNameResp.done == false) {
osDelay(10);
}
printf("DNS Lookup Completed for %s,\r\n\twhich returned IP Address: %s\r\n", getHostNameResp.name, ip4addr_ntoa(&getHostNameResp.addr));
printf("Task Done!!!\r\n");
printf("==============================================================================\n\r");
/* Infinite loop */
for(;;)
{
osDelay(1);
}
/* USER CODE END TestDNSFunction */
}
/**
* @brief Period elapsed callback in non blocking mode
* @note This function is called when TIM6 interrupt took place, inside
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
* a global variable "uwTick" used as application time base.
* @param htim : TIM handle
* @retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM6) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
/* USER CODE END Callback 1 */
}
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
dnsLookup.h
/*
* dnsLookup.h
*
* Created on: Jun 7, 2024
* Author: johng
*/
#ifndef DNSLOOKUP_H_
#define DNSLOOKUP_H_
#include "cmsis_os.h"
#include "stm32f4xx_hal.h"
#include "lwip/dns.h"
#include "err.h"
#include "lwip.h"
#include "stdbool.h"
#include "lwip/apps/sntp.h"
typedef struct dnsGetHostName {
const char *name;
ip_addr_t addr;
bool done;
} dnsGetHostName_t;
//extern dnsGetHostName_t *getHostNameResp;
void hostNameFoundCallBack(const char *, const ip_addr_t *, void *);
bool getHostByName(const char *, dnsGetHostName_t *);
typedef void (*callBackFuncPtr)(char *, ip_addr_t *, void *);
typedef void (*dns_found_callback)(const char *name, const ip_addr_t *ipaddr, void *callback_arg);
#endif /* DNSLOOKUP_H_ */
dnsLookup.c
#include "dnsLookup.h"
dnsGetHostName_t *getHostNameRespPtr;
void hostNameFoundCallBack(const char *hostname, const ip_addr_t *ipaddr, void *arg)
{
char buffer[128];
if (ipaddr != NULL) {
/* Address resolved, send request */
getHostNameRespPtr->addr = *ipaddr;
getHostNameRespPtr->done = true;
} else {
sprintf(buffer, "DNS Failed to resolve. ipaddr == NULL\r\n");
printf(buffer);
Error_Handler();
}
}
bool getHostByName(const char *name, dnsGetHostName_t *response) {
bool retVal = false;
int counter = 0;
char buffer[128];
getHostNameRespPtr = response;
/*
* The following function call is used to get the a host name via a DNS server. The DNS server had to be previously setup during
* static setup or through DHCP initialization.
* Once a response from the DNS Server is received, the callback function specified in the 3rd parameter is invoked.
* In this case the callback function is called: hostNameFoundCallBack
*/
err_enum_t err = dns_gethostbyname(name, &getHostNameRespPtr->addr, (dns_found_callback) hostNameFoundCallBack, NULL);
if (err == ERR_INPROGRESS) {
while(getHostNameRespPtr->done != true) {
osDelay(100);
counter ++;
if (counter > 1000) {
sprintf(buffer, "getHosNameRespPtr never returned TRUE after X Number of tries.\r\n");
printf(buffer);
Error_Handler();
}
}
retVal = true;
} else if (err == ERR_OK) {
getHostNameRespPtr->done = true;
retVal = true;
} else {
sprintf(buffer, "dns_gethostbyname ERROR: %d", err);
printf(buffer);
Error_Handler();
}
return retVal;
}


