Chapter 77: IPv4 Networking Fundamentals

Chapter Objectives

Upon completing this chapter, you will be able to:

  • Grasp the foundational concepts of Internet Protocol version 4 (IPv4) addressing.
  • Understand the role of subnet masks, network addresses, and broadcast addresses.
  • Differentiate between public and private IP addresses and the concept of Network Address Translation (NAT).
  • Configure ESP32 devices to obtain IP addresses dynamically using DHCP.
  • Configure ESP32 devices with static IP addresses.
  • Explain the function of a default gateway and DNS servers in an IPv4 network.
  • Troubleshoot common IP addressing and connectivity issues.

Introduction

In the vast landscape of interconnected devices that form the Internet and local networks, a fundamental mechanism is required to uniquely identify each device and enable communication between them. This mechanism is known as the Internet Protocol (IP). This chapter focuses on IPv4, the fourth version of the Internet Protocol, which has been the cornerstone of network communication for decades. Understanding IPv4 addressing, its components, and how devices acquire and use these addresses is crucial for any embedded systems developer working with networked devices like the ESP32. We will explore both dynamic (DHCP) and static IP configuration methods, essential for deploying ESP32 devices in various network environments.

Theory

1. What is Internet Protocol version 4 (IPv4)?

IPv4 is a connectionless protocol that operates at the Network Layer (Layer 3) of the OSI model. Its primary function is to provide a unique addressing system for devices on a network and to route packets of data between them. An IPv4 address is a 32-bit numerical label that uniquely identifies an interface on a network.

These 32 bits are typically represented in dotted-decimal notation, divided into four 8-bit segments (octets), each ranging from 0 to 255, separated by dots. For example, 192.168.1.100 is a common IPv4 address.

2. IP Address Classes (Historical Context)

Historically, IPv4 addresses were categorized into classes (A, B, C, D, E) based on the first few bits of the address. This classful addressing scheme defined fixed network and host portions.

  • Class A: Designed for very large networks. First octet 1-126. (e.g., 10.0.0.0/8)
  • Class B: For medium to large networks. First octet 128-191. (e.g., 172.16.0.0/16)
  • Class C: For small networks. First octet 192-223. (e.g., 192.168.1.0/24)

While these classes still define certain address ranges, the rigid classful system led to inefficient address allocation and is largely superseded by CIDR.

Class First Octet Range Default Subnet Mask Example Network Prefix Designed For
Class A 1 – 126 255.0.0.0 10.0.0.0/8 Very large networks (few networks, many hosts)
Class B 128 – 191 255.255.0.0 172.16.0.0/16 Medium to large networks
Class C 192 – 223 255.255.255.0 192.168.1.0/24 Small networks (many networks, few hosts)
Class D 224 – 239 N/A (Multicast) 224.0.0.1 Multicast groups
Class E 240 – 255 N/A (Reserved) N/A Experimental/Reserved for future use

3. Subnet Mask

An IP address alone isn’t enough to define a network. It must be combined with a subnet mask. A subnet mask is also a 32-bit number, represented in dotted-decimal notation, that divides an IP address into two parts:

  • Network Portion: Identifies the specific network segment to which the device belongs. All devices on the same local network must share the same network portion.
  • Host Portion: Uniquely identifies a specific device within that network segment.

The subnet mask works by having all 1s in the bits corresponding to the network portion and all 0s in the bits corresponding to the host portion. When you perform a bitwise AND operation between an IP address and its subnet mask, the result is the network address.

Example:

  • IP Address: 192.168.1.100 (binary: 11000000.10101000.00000001.01100100)
  • Subnet Mask: 255.255.255.0 (binary: 11111111.11111111.11111111.00000000)
  • Network Address (AND operation): 192.168.1.0 (binary: 11000000.10101000.00000001.00000000)
%%{init: {"theme": "base", "themeVariables": {"primaryTextColor": "#333333", "lineColor": "#777777"}}}%%
graph LR;
    subgraph "Example: 192.168.1.100 / 255.255.255.0"
        IP["<b>IP Address:</b><br>192.168.1.100<br><br>Binary:<br>11000000.10101000.00000001.01100100"]
        Mask["<b>Subnet Mask:</b><br>255.255.255.0<br><br>Binary:<br>11111111.11111111.11111111.00000000"]
        
        IP -- "& (Bitwise AND)" --> Result;
        Mask -- "& (Bitwise AND)" --> Result;

        Result["<b>Network Address:</b><br>192.168.1.0<br><br>Binary:<br>11000000.10101000.00000001.00000000"]

        subgraph "Interpretation"
            NetPortion["<b>Network Portion (from Mask)</b><br>First 3 octets (24 bits)<br>192.168.1"]
            HostPortion["<b>Host Portion (from Mask)</b><br>Last octet (8 bits)<br>.100"]
        end
        Result --> NetPortion;
        IP --> HostPortion;
    end

    style IP fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF
    style Mask fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF
    style Result fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46
    style NetPortion fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E
    style HostPortion fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E

4. Network Address and Broadcast Address

Within any given network segment defined by an IP address and subnet mask, two special addresses are reserved:

  • Network Address: The first address in a subnet, where all host bits are 0. It represents the network itself and cannot be assigned to a device. (e.g., 192.168.1.0 for a 255.255.255.0 subnet).
  • Broadcast Address: The last address in a subnet, where all host bits are 1. Packets sent to this address are delivered to all devices within that specific network segment. (e.g., 192.168.1.255 for a 255.255.255.0 subnet).

5. CIDR (Classless Inter-Domain Routing)

CIDR replaced the classful addressing system to provide more flexible and efficient allocation of IP addresses. Instead of fixed octet boundaries, CIDR uses a prefix-length notation (e.g., /24, /16) appended to the IP address. This prefix length indicates the number of bits in the network portion of the address. The remaining bits are for the host portion.

Example:

  • 192.168.1.0/24: This means the first 24 bits are the network portion, and the last 8 bits are for hosts. This is equivalent to a 255.255.255.0 subnet mask.
  • 10.0.0.0/8: The first 8 bits are the network portion. Equivalent to 255.0.0.0.
%%{init: {"theme": "base", "themeVariables": {"primaryTextColor": "#333333", "lineColor": "#777777"}}}%%
graph LR;
    title["CIDR Notation: Network Prefix Length"]

    subgraph "Example 1: 192.168.1.0/24"
        direction LR
        N1["<b>Network: 24 bits</b><br>192.168.1"]
        H1["<b>Host: 8 bits</b><br>(254 usable IPs)"]
        N1 --- H1;
        Mask1["(Subnet Mask: 255.255.255.0)"]
    end
    
    subgraph "Example 2: 172.16.0.0/16"
        direction LR
        N2["<b>Network: 16 bits</b><br>172.16"]
        H2["<b>Host: 16 bits</b><br>(65,534 usable IPs)"]
        N2 --- H2;
        Mask2["(Subnet Mask: 255.255.0.0)"]
    end

    subgraph "Example 3: 10.0.0.0/8"
        direction LR
        N3["<b>Network: 8 bits</b><br>10"]
        H3["<b>Host: 24 bits</b><br>(16,777,214 usable IPs)"]
        N3 --- H3;
        Mask3["(Subnet Mask: 255.0.0.0)"]
    end

    style title fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6
    style N1 fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF
    style H1 fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E
    style N2 fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF
    style H2 fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E
    style N3 fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF
    style H3 fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E
    style Mask1 fill:#FEE2E2,stroke:#DC2626,stroke-width:1px,color:#991B1B
    style Mask2 fill:#FEE2E2,stroke:#DC2626,stroke-width:1px,color:#991B1B
    style Mask3 fill:#FEE2E2,stroke:#DC2626,stroke-width:1px,color:#991B1B

CIDR allows for the creation of subnets of varying sizes, optimizing IP address usage.

6. Private vs. Public IP Addresses and NAT

To conserve the limited supply of IPv4 addresses and enhance security, IP addresses are categorized as either public or private:

  • Public IP Addresses: These are globally unique and routable on the Internet. Every device directly connected to the Internet must have a public IP address.
  • Private IP Addresses: These are reserved for use within private networks (e.g., your home or office LAN) and are not routable on the Internet. They can be reused in different private networks.

Common Private IP Address Ranges:

Private Address Range (CIDR) Full Range Typical Use / Class Association (Historical)
10.0.0.0/8 10.0.0.0 to 10.255.255.255 Large private networks (often in enterprises); historically a single Class A network.
172.16.0.0/12 172.16.0.0 to 172.31.255.255 Medium-sized private networks; historically 16 contiguous Class B networks.
192.168.0.0/16 192.168.0.0 to 192.168.255.255 Small private networks (common for home and small office routers); historically 256 contiguous Class C networks.

To allow devices with private IP addresses to access the Internet, Network Address Translation (NAT) is used. A router performs NAT by translating private IP addresses of devices on the local network to a single public IP address when communicating with the Internet, and vice-versa for incoming traffic.

graph TD
    subgraph LAN["Private Network (LAN)"]
        ESP32["ESP32<br/>192.168.1.101<br/>(Private IP)"]
        Laptop["Laptop<br/>192.168.1.102<br/>(Private IP)"]
        Phone["Phone<br/>192.168.1.103<br/>(Private IP)"]
    end
    
    Router["Router / NAT Device<br/>LAN IP: 192.168.1.1<br/>WAN IP: 203.0.113.45<br/>(Public IP)"]
    
    subgraph Internet["Internet"]
        WebServer["Web Server<br/>93.184.216.34<br/>(Public IP)"]
    end
    
    %% Connections
    ESP32 -.-> Router
    Laptop -.-> Router
    Phone -.-> Router
    Router -.-> WebServer
    
    %% Request flow (blue arrows)
    ESP32 -->|"1- Request from<br/>192.168.1.101:PortA"| Router
    Router -->|"2- NAT Translation<br/>192.168.1.101:PortA → 203.0.113.45:PortX<br/>To 93.184.216.34"| WebServer
    
    %% Response flow (green arrows)
    WebServer -->|"3- Response from<br/>93.184.216.34"| Router
    Router -->|"4- NAT Translation<br/>203.0.113.45:PortX → 192.168.1.101:PortA<br/>To 192.168.1.101"| ESP32
    
    %% Styling
    classDef lanDevice fill:#DBEAFE,stroke:#2563EB,stroke-width:2px
    classDef router fill:#FEF3C7,stroke:#D97706,stroke-width:2px
    classDef internetDevice fill:#D1FAE5,stroke:#059669,stroke-width:2px
    classDef lanArea fill:#E0F2FE,stroke:#A5D8FF
    classDef internetArea fill:#E0F2F1,stroke:#A7D7C5
    
    class ESP32,Laptop,Phone lanDevice
    class Router router
    class WebServer internetDevice

7. DHCP (Dynamic Host Configuration Protocol)

DHCP is a network protocol that enables a server to automatically assign IP addresses and other network configuration parameters (like subnet mask, default gateway, and DNS servers) to devices connected to a network. This eliminates the need for manual configuration, simplifying network management, especially in large networks.

  • DHCP Client: The device requesting an IP address (e.g., ESP32).
  • DHCP Server: The device providing IP addresses (e.g., a router).
%%{init: {"theme": "base", "themeVariables": {"primaryTextColor": "#333333", "lineColor": "#777777"}}}%%
sequenceDiagram;
    participant Client as ESP32 (DHCP Client);
    participant Server as Router (DHCP Server);

    Client->>+Server: 1. DHCPDISCOVER (Broadcast)<br>"Anyone have an IP for me?";
    Server->>-Client: 2. DHCPOFFER (Unicast or Broadcast)<br>"Here's an IP: 192.168.1.100";
    Client->>+Server: 3. DHCPREQUEST (Broadcast)<br>"I'll take 192.168.1.100 from you, Router_MAC";
    Server->>-Client: 4. DHCPACK (Unicast or Broadcast)<br>"OK, 192.168.1.100 is yours (lease time, gateway, DNS)";
    Note right of Client: IP Address Acquired!


The process typically involves a DORA (Discover, Offer, Request, Acknowledge) sequence.

8. Static IP Configuration

Static IP configuration involves manually assigning a fixed IP address, subnet mask, default gateway, and DNS server addresses to a device. This method is used when:

  • A device needs a consistent, unchanging IP address (e.g., a server, a network printer, or an embedded device that needs to be accessed reliably by other systems).
  • No DHCP server is available on the network.
Feature DHCP (Dynamic Host Configuration Protocol) Static IP Configuration
IP Address Assignment Automatic, by a DHCP server. Manual, by an administrator on the device.
Configuration Parameters IP address, subnet mask, default gateway, DNS servers typically provided. All parameters (IP, mask, gateway, DNS) must be manually entered.
Ease of Management Simpler for large networks; plug-and-play for clients. Requires careful planning and record-keeping; prone to human error.
IP Address Consistency IP address can change (unless DHCP reservation is used). IP address is fixed and predictable.
Risk of IP Conflicts Low, as DHCP server manages the IP pool. Higher, if addresses are duplicated or misconfigured.
Typical Use Cases Client devices (laptops, phones, most IoT devices), temporary connections. Servers, printers, network devices, specific embedded systems requiring a fixed address for access.
ESP32 Implementation Default for esp_netif_create_default_wifi_sta() or esp_netif_create_default_eth(). Requires stopping DHCP client (e.g., esp_netif_dhcpc_stop()) and setting IP info (esp_netif_set_ip_info()).

Warning: When using static IP, ensure the chosen address is not already in use by another device on the network to avoid IP conflicts. Also, the static IP must be within the same subnet as other devices you wish to communicate with on the local network.

9. Default Gateway

The default gateway is the IP address of the router or device that connects your local network to other networks (including the Internet). When a device needs to send a packet to an IP address outside its local subnet, it forwards the packet to its default gateway. The gateway then handles the routing of that packet to its destination.

10. DNS (Domain Name System)

The DNS is a hierarchical and decentralized naming system for computers, services, or other resources connected to the Internet or a private network. It translates human-readable domain names (e.g., www.example.com) into numerical IP addresses (e.g., 93.184.216.34) that computers use to identify each other on the network. Without DNS, you would have to remember the IP addresses of all websites you want to visit.

Parameter Example Role / Function
IP Address 192.168.1.100 Unique 32-bit numerical identifier for a device’s interface on a network.
Subnet Mask 255.255.255.0 (or /24) Divides the IP address into network and host portions. Defines the local network segment.
Network Address 192.168.1.0 The first address in a subnet, representing the network itself. Not assignable to devices. (Result of IP AND Mask).
Broadcast Address 192.168.1.255 The last address in a subnet. Packets sent here reach all devices on that local network segment.
Default Gateway 192.168.1.1 The IP address of the router that connects the local network to other networks (e.g., the Internet).
DNS Server(s) 8.8.8.8 (Primary)
8.8.4.4 (Secondary)
Translates human-readable domain names (e.g., www.google.com) into numerical IP addresses.

11. ARP (Address Resolution Protocol)

ARP is a protocol used to resolve IP addresses to MAC (Media Access Control) addresses within a local network segment. When a device wants to send data to another device on the same local network, it first needs to know the destination’s MAC address. It sends an ARP request (a broadcast) asking “Who has this IP address? Tell me your MAC address.” The device with that IP address responds with its MAC address.

%%{init: {"theme": "base", "themeVariables": {"primaryTextColor": "#333333", "lineColor": "#777777"}}}%%
sequenceDiagram;
    participant DeviceA as Device A (ESP32)<br>IP: 192.168.1.100<br>MAC: AA:AA:AA:AA:AA:AA;
    participant Network as Local Network Segment;
    participant DeviceB as Device B (Target)<br>IP: 192.168.1.105<br>MAC: BB:BB:BB:BB:BB:BB;

    Note over DeviceA: Wants to send data to 192.168.1.105.<br>Needs MAC address.

    DeviceA->>Network: 1. ARP Request (Broadcast)<br>"Who has IP 192.168.1.105?<br>Tell MAC AA:AA:AA:AA:AA:AA";
    
    Network-->>DeviceB: ARP Request reaches all devices;
    
    Note over DeviceB: "That's my IP!";

    DeviceB->>DeviceA: 2. ARP Reply (Unicast)<br>"IP 192.168.1.105 is at<br>MAC BB:BB:BB:BB:BB:BB";

    Note over DeviceA: ARP cache updated:<br>192.168.1.105 -> BB:BB:BB:BB:BB:BB.<br>Can now send Ethernet frame.

Practical Examples

This example demonstrates how to configure an ESP32 for both DHCP and Static IP addressing, allowing you to switch between them via menuconfig. It uses the Wi-Fi interface for simplicity, but the esp_netif concepts are identical for Ethernet.

1. Project Setup

Create a new ESP-IDF project using VS Code. You can use the “ESP-IDF: New Project” command. Name it ipv4_config_example.

2. main/ipv4_config_example_main.c

C
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "sdkconfig.h"

static const char *TAG = "IPV4_CONFIG";

// Event handler for Wi-Fi and IP events
static void event_handler(void *arg, esp_event_base_t event_base,
                          int32_t event_id, void *event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        ESP_LOGI(TAG, "Wi-Fi STA started, connecting...");
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        ESP_LOGI(TAG, "Wi-Fi STA disconnected, retrying connection...");
        esp_wifi_connect(); // Retry connection
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
        ESP_LOGI(TAG, "Got IP address: " IPSTR, IP2STR(&event->ip_info.ip));
        ESP_LOGI(TAG, "Netmask: " IPSTR, IP2STR(&event->ip_info.netmask));
        ESP_LOGI(TAG, "Gateway: " IPSTR, IP2STR(&event->ip_info.gw));
    }
}

void app_main(void)
{
    // Initialize NVS (Non-Volatile Storage) for Wi-Fi configuration
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    // Initialize TCP/IP stack
    ESP_ERROR_CHECK(esp_netif_init());

    // Create default event loop
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    // Create Wi-Fi station interface
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

    // Register event handlers
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));

    // Initialize Wi-Fi
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

#if CONFIG_USE_STATIC_IP
    ESP_LOGI(TAG, "Configuring static IP address.");
    esp_netif_dhcpc_stop(sta_netif); // Stop DHCP client

    esp_netif_ip_info_t ip_info;
    // Set static IP, netmask, and gateway from menuconfig
    IP4_ADDR(&ip_info.ip, CONFIG_STATIC_IP_ADDR_0, CONFIG_STATIC_IP_ADDR_1, CONFIG_STATIC_IP_ADDR_2, CONFIG_STATIC_IP_ADDR_3);
    IP4_ADDR(&ip_info.netmask, CONFIG_STATIC_NETMASK_0, CONFIG_STATIC_NETMASK_1, CONFIG_STATIC_NETMASK_2, CONFIG_STATIC_NETMASK_3);
    IP4_ADDR(&ip_info.gw, CONFIG_STATIC_GW_0, CONFIG_STATIC_GW_1, CONFIG_STATIC_GW_2, CONFIG_STATIC_GW_3);

    ESP_ERROR_CHECK(esp_netif_set_ip_info(sta_netif, &ip_info));

    // Optionally set static DNS servers
    esp_netif_dns_info_t dns_info[2];
    IP4_ADDR(&dns_info[0].ip.addr, CONFIG_STATIC_DNS1_ADDR_0, CONFIG_STATIC_DNS1_ADDR_1, CONFIG_STATIC_DNS1_ADDR_2, CONFIG_STATIC_DNS1_ADDR_3);
    IP4_ADDR(&dns_info[1].ip.addr, CONFIG_STATIC_DNS2_ADDR_0, CONFIG_STATIC_DNS2_ADDR_1, CONFIG_STATIC_DNS2_ADDR_2, CONFIG_STATIC_DNS2_ADDR_3);
    ESP_ERROR_CHECK(esp_netif_set_dns_info(sta_netif, ESP_NETIF_DNS_MAIN, &dns_info[0]));
    ESP_ERROR_CHECK(esp_netif_set_dns_info(sta_netif, ESP_NETIF_DNS_BACKUP, &dns_info[1]));

#else // CONFIG_USE_DHCP
    ESP_LOGI(TAG, "Configuring IP address via DHCP.");
    // DHCP is enabled by default for esp_netif_create_default_wifi_sta()
#endif

    // Configure Wi-Fi station mode
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = CONFIG_WIFI_SSID,
            .password = CONFIG_WIFI_PASSWORD,
            .threshold.authmode = WIFI_AUTH_WPA2_PSK,
            .sae_pwe_h2e = WIFI_SAE_MODE_OWE, // For WPA3, if supported by AP
            .sae_h2e_identifier = "",
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "Waiting for IP address...");
}

3. main/CMakeLists.txt

The default CMakeLists.txt should be sufficient. Ensure idf_component_register is present.

Plaintext
# In the main/CMakeLists.txt file
idf_component_register(SRCS "ipv4_config_example_main.c"
                       INCLUDE_DIRS ".")

4. main/Kconfig.projbuild

This file allows you to configure project-specific settings via idf.py menuconfig.

Create main/Kconfig.projbuild and add the following:

Plaintext
menu "Network Configuration"
    config WIFI_SSID
        string "Wi-Fi SSID"
        default "YOUR_SSID"
        help
            SSID of your Wi-Fi network.

    config WIFI_PASSWORD
        string "Wi-Fi Password"
        default "YOUR_PASSWORD"
        help
            Password of your Wi-Fi network.

    config USE_STATIC_IP
        bool "Use Static IP Address"
        default n
        help
            Enable to use a static IP address instead of DHCP.

    if USE_STATIC_IP
        menu "Static IP Configuration"
            config STATIC_IP_ADDR_0
                int "Static IP Address Octet 0"
                range 0 255
                default 192

            config STATIC_IP_ADDR_1
                int "Static IP Address Octet 1"
                range 0 255
                default 168

            config STATIC_IP_ADDR_2
                int "Static IP Address Octet 2"
                range 0 255
                default 1

            config STATIC_IP_ADDR_3
                int "Static IP Address Octet 3"
                range 0 255
                default 100

            config STATIC_NETMASK_0
                int "Static Netmask Octet 0"
                range 0 255
                default 255

            config STATIC_NETMASK_1
                int "Static Netmask Octet 1"
                range 0 255
                default 255

            config STATIC_NETMASK_2
                int "Static Netmask Octet 2"
                range 0 255
                default 255

            config STATIC_NETMASK_3
                int "Static Netmask Octet 3"
                range 0 255
                default 0

            config STATIC_GW_0
                int "Static Gateway Octet 0"
                range 0 255
                default 192

            config STATIC_GW_1
                int "Static Gateway Octet 1"
                range 0 255
                default 168

            config STATIC_GW_2
                int "Static Gateway Octet 2"
                range 0 255
                default 1

            config STATIC_GW_3
                int "Static Gateway Octet 3"
                range 0 255
                default 1

            config STATIC_DNS1_ADDR_0
                int "Primary DNS Server Octet 0"
                range 0 255
                default 8

            config STATIC_DNS1_ADDR_1
                int "Primary DNS Server Octet 1"
                range 0 255
                default 8

            config STATIC_DNS1_ADDR_2
                int "Primary DNS Server Octet 2"
                range 0 255
                default 8

            config STATIC_DNS1_ADDR_3
                int "Primary DNS Server Octet 3"
                range 0 255
                default 8

            config STATIC_DNS2_ADDR_0
                int "Secondary DNS Server Octet 0"
                range 0 255
                default 8

            config STATIC_DNS2_ADDR_1
                int "Secondary DNS Server Octet 1"
                range 0 255
                default 8

            config STATIC_DNS2_ADDR_2
                int "Secondary DNS Server Octet 2"
                range 0 255
                default 4

            config STATIC_DNS2_ADDR_3
                int "Secondary DNS Server Octet 3"
                range 0 255
                default 4
        endmenu # Static IP Configuration
    endif # USE_STATIC_IP
endmenu # Network Configuration

5. Build Instructions

  1. Open VS Code: Open your ipv4_config_example project folder in VS Code.
  2. Configure with menuconfig:
    • Press Ctrl+Shift+P (or Cmd+Shift+P on macOS) to open the command palette.
    • Type and select “ESP-IDF: SDK Configuration Editor (menuconfig)”.
    • Navigate to “Network Configuration”.
    • Enter your Wi-Fi SSID and Password.
    • To use DHCP (default), ensure Use Static IP Address is unchecked.
    • To use Static IP, check Use Static IP Address and then configure the IP address, Netmask, Gateway, and DNS servers according to your network.
    • Save the configuration and exit menuconfig.
  3. Build the Project:
    • Open the VS Code Terminal (Terminal > New Terminal).
    • Run idf.py build. This will compile the project.

6. Run/Flash/Observe Steps

  1. Connect Hardware:
    • Connect your ESP32 development board to your computer via USB.
  2. Flash the Firmware:
    • In the VS Code Terminal, run idf.py flash. This will erase the chip and flash your compiled firmware.
  3. Monitor Serial Output:
    • After flashing, run idf.py monitor. This will open the serial monitor and display the logs from your ESP32.

Expected Output (DHCP):

Plaintext
I (XXX) IPV4_CONFIG: Waiting for IP address...
I (XXX) IPV4_CONFIG: Wi-Fi STA started, connecting...
I (XXX) IPV4_CONFIG: Got IP address: 192.168.1.105
I (XXX) IPV4_CONFIG: Netmask: 255.255.255.0
I (XXX) IPV4_CONFIG: Gateway: 192.168.1.1

Expected Output (Static IP):

Plaintext
I (XXX) IPV4_CONFIG: Configuring static IP address.
I (XXX) IPV4_CONFIG: Waiting for IP address...
I (XXX) IPV4_CONFIG: Wi-Fi STA started, connecting...
I (XXX) IPV4_CONFIG: Got IP address: 192.168.1.100
I (XXX) IPV4_CONFIG: Netmask: 255.255.255.0
I (XXX) IPV4_CONFIG: Gateway: 192.168.1.1

(Assuming you configured 192.168.1.100 as the static IP)

Variant Notes

The fundamental concepts of IPv4 networking, including IP addressing, subnet masks, gateways, DHCP, and DNS, are universal across all networking devices, including all ESP32 variants (ESP32, ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6, ESP32-H2).

The ESP-IDF’s esp_netif component provides a unified abstraction layer for network interfaces. Whether you are using Wi-Fi, Ethernet (with internal EMAC on ESP32 classic, or external SPI/SDIO controllers on other variants), or any other network interface, the API calls for configuring IP addresses (DHCP or static) and handling IP events remain consistent. The underlying hardware differences are handled by the specific Wi-Fi or Ethernet drivers, allowing your application code to interact with the network stack in a consistent manner. Therefore, no specific variant notes are required for this chapter on IPv4 fundamentals.

Common Mistakes & Troubleshooting Tips

Mistake / Issue Symptom(s) Troubleshooting / Solution
IP Address Conflicts (Static IP) Device gets IP, but intermittent connectivity or other devices on network lose connection. Log messages may not indicate an error on ESP32.
  • Ensure static IP is outside router’s DHCP range.
  • Check router’s DHCP lease table and connected devices list.
  • Use network scanning tools (e.g., nmap, Angry IP Scanner) to identify used IPs.
  • Consider DHCP reservation on the router instead of static IP on ESP32.
Incorrect Subnet Mask or Gateway (Static IP) ESP32 gets IP but cannot communicate with devices outside its local subnet (e.g., internet) or sometimes even locally. Ping to gateway fails.
  • Verify subnet mask and gateway by checking a working device on the same network (ipconfig, ifconfig).
  • Gateway is typically the router’s IP address.
  • Ensure ESP32’s IP is within the network defined by the gateway and subnet mask.
DHCP Server Unavailability / Misconfiguration ESP32 fails to get an IP address. IP_EVENT_STA_GOT_IP (or IP_EVENT_ETH_GOT_IP) never triggers. Device keeps trying to connect.
  • Ensure router’s DHCP server is enabled and has available leases.
  • Reboot router.
  • Test DHCP with another device on the same network segment/cable.
  • Check for MAC filtering on the router.
Underlying Connectivity Issues (Wi-Fi/Ethernet) No IP address obtained. Symptoms might seem like IP issues, but the root cause is lower level.
  • Wi-Fi: Check WIFI_EVENT_STA_CONNECTED / WIFI_EVENT_STA_DISCONNECTED. Verify SSID, password, signal strength.
  • Ethernet: Check ETHERNET_EVENT_CONNECTED / ETHERNET_EVENT_DISCONNECTED. Verify cable, PHY initialization, link lights. (Refer to Chapter 76 troubleshooting).
DNS Resolution Failures ESP32 has an IP and can ping other IP addresses (e.g., 8.8.8.8), but cannot access websites by domain name (e.g., www.google.com). HTTP client errors related to host not found.
  • DHCP: Ensure router provides valid DNS server addresses.
  • Static IP: Verify manually configured DNS servers are correct and reachable (e.g., 8.8.8.8, 1.1.1.1).
  • Try pinging the DNS server IP directly.
  • Check for firewall rules blocking DNS traffic (port 53).
Firewall Blocking ESP32 ESP32 connects to Wi-Fi/Ethernet, gets an IP, but cannot access local network resources or internet. Pings to gateway or other local devices might fail.
  • Check firewall settings on the router or any network security appliances.
  • Temporarily disable firewall for testing (if safe to do so).
  • Ensure ESP32’s MAC address is not blacklisted or that MAC filtering allows it.

Exercises

Exercise 1: Basic Ping Utility

Modify the provided example to include a simple ping functionality. Once the ESP32 obtains an IP address (either via DHCP or static), have it periodically ping a well-known public IP address (e.g., Google’s DNS 8.8.8.8) and report the success or failure.

Steps:

  1. Include Ping Headers: Add #include "lwip/sockets.h" and #include "lwip/inet.h" for socket operations.
  2. Create Ping Task: Implement a FreeRTOS task (ping_task) that uses socket(), sendto(), and recvfrom() to send ICMP echo requests and receive replies. You’ll need to construct simple ICMP packets.
  3. Trigger Ping: In the event_handler for IP_EVENT_STA_GOT_IP, create and start the ping_task.
  4. Log Results: Print whether the ping was successful and any round-trip time.

Exercise 2: Display Network Information on Boot

Enhance the application to display all obtained network information (IP address, netmask, gateway, and DNS servers) immediately after the IP_EVENT_STA_GOT_IP event, and also after a short delay (e.g., 5 seconds) to demonstrate persistence.

Steps:

  1. Retrieve DNS Info: After IP_EVENT_STA_GOT_IP, use esp_netif_get_dns_info() to retrieve the configured DNS servers.
  2. Print All Info: Extend the ip_event_handler to print the primary and secondary DNS server IP addresses in addition to the existing IP, netmask, and gateway.
  3. Delayed Print Task: Create a separate FreeRTOS task that waits for a few seconds using vTaskDelay and then calls esp_netif_get_ip_info() and esp_netif_get_dns_info() again to re-print the network details. Start this task after the initial IP event.

Exercise 3: Web Server with IP Display

Create a very simple HTTP web server on the ESP32 that, when accessed from a web browser, displays the ESP32’s currently assigned IP address, subnet mask, and gateway.

Steps:

  1. Include HTTP Server: Add #include "esp_http_server.h" and related headers.
  2. Create HTTP Handler: Implement an HTTP GET handler function that generates an HTML page containing the network information. You’ll need to retrieve the IP info within this handler.
  3. Start HTTP Server: In the event_handler for IP_EVENT_STA_GOT_IP, initialize and start the HTTP server using httpd_start().
  4. Register URI Handler: Register your GET handler for the root path (/).
  5. Test: After flashing, open a web browser and navigate to the ESP32’s IP address (e.g., http://192.168.1.100). Verify the displayed network information matches the serial monitor output.

Summary

  • IPv4 uses 32-bit addresses, commonly represented in dotted-decimal notation (e.g., 192.168.1.100), to uniquely identify devices on a network.
  • A subnet mask (e.g., 255.255.255.0 or /24 in CIDR) divides an IP address into a network portion and a host portion.
  • The network address is the first address in a subnet, and the broadcast address is the last; both are reserved.
  • Private IP addresses are used within local networks and are translated to public IP addresses by NAT for Internet access.
  • DHCP (Dynamic Host Configuration Protocol) automatically assigns IP addresses and network settings, simplifying network management.
  • Static IP configuration involves manually assigning fixed IP addresses, suitable for devices requiring consistent access.
  • The default gateway is the router’s IP address, used for routing traffic outside the local network.
  • DNS (Domain Name System) translates human-readable domain names into numerical IP addresses.
  • ARP (Address Resolution Protocol) resolves IP addresses to MAC addresses on a local network segment.
  • The ESP-IDF esp_netif component provides a consistent API for IP configuration across all ESP32 variants and network interfaces (Wi-Fi, Ethernet).

Further Reading

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top