【ESP32-S3的开发】| 3.ESP32S3 设置DNS、静态IP实现域名解析

【ESP32-S3的开发】



前言

其实这个和 ESP 系列关系不大了,目前 ESP32 大部分都支持,我也就把文章定位在这个系列上吧!
在讲述ESP32S3设置DNS、静态IP,实现域名解析前,先了解一下域名解析的相关概念

可能会有人提出这么个疑问,DHCP自动获得服务器分配的lP地址和子网掩码不就可以了吗?为毛要设置另外的DNS、网关和静态IP,甚至是端口?
这里面介绍一种场景:公司的主机使用运营商的DNS,直接连接外网,但公司因为需要限制某部分IP的访问,对访问外网的出口做了部分限制,这时候设置了公司自己的DNS,其他主机通过公司的服务器连接外网时,就都能被管控了。在一些信息管控比较严格的地方会有这种情况出现,如果设备需要部署在这些地方时,对方只开放部分静态IP给设备,用以连接,从而让设备接受监管,这时候就需要设备设置DNS、静态IP等操作了


一、关闭DHCP,设置DNS、网关、子网掩码、静态IP

需要添加的头文件

#include "lwip/dns.h"
#include "lwip/err.h"
#include "lwip/netdb.h"
#include "lwip/sys.h"

1.关闭DHCP

tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); //在 Station 接口上停止 DHCP 客户端

2.设置 IP 的主要函数

IP4_ADDR(ipaddr, a,b,c,d)

为了简便解析以及配置,这边封装一个解析函数,匹配所有和 IP 设置相关的操作, 用户习惯性传入诸如:192.168.1.1 这种字符格式的参数,这里根据这种格式,解析并进行设置,当然,最终启用并且生效的函数仍然是上面讲到的 IP4_ADDR

/**************************************************************************************
****** Function	   	:	void SetDNS_GW_STATICIP(ip4_addr_t *ip4_usr_addr, char *ip4_str)
****** Detail IO   	:
****** Description 	:	设置 DNS 地址、网关、静态 IP 、 掩码
****** Step		   	:
@ Para1:    tcpip_adapter_dns_info_t 、 tcpip_adapter_ip_info_t 格式变量
@ Para2:    IPV4格式的地址
***************************************************************************************/
void SetDNS_GW_STATICIP(ip4_addr_t *ip4_usr_addr, char *ip4_str)
{
    char iptemp[4];
    uint8_t ip[4];
    char *ptemp1 = ip4_str, *ptemp2;
    uint8_t i = 0, comma_num = 0;
    while (ip4_str[i] != '\0')
    {
        if (ip4_str[i] == '.')
        {
            comma_num++;
        }
        i++;
    }
    if (comma_num == 3)
    {
        for (i = 0; i < comma_num; i++)
        {
            ptemp2 = ptemp1;
            ptemp1 = strchr(ptemp1, '.');
            memset(iptemp, 0, sizeof(iptemp));
            strncpy(iptemp, ptemp2, strlen(ptemp2) - strlen(ptemp1));
            ip[i] = atoi(iptemp);
            ptemp1 += 1;
        }
        memset(iptemp, 0, sizeof(iptemp));
        strncpy(iptemp, ptemp1, strlen(ptemp1));
        ip[i] = atoi(iptemp);
        IP4_ADDR(ip4_usr_addr, ip[0], ip[1], ip[2], ip[3]);
    }
}

3.设置 DNS

typedef struct __dns_gw_staticip
{
    char dns_ipaddr[16];
    char gw_ipaddr[16];
    char staticip_ipaddr[16];
    char netmask_ipaddr[16];
} dns_gw_static_st;

dns_gw_static_st NetRec_NetAddrConfigData = {0};

memset(NetRec_NetAddrConfigData.dns_ipaddr, 0, sizeof(NetRec_NetAddrConfigData.dns_ipaddr));
memcpy(NetRec_NetAddrConfigData.dns_ipaddr, "192.168.100.16", strlen("192.168.100.16"));

tcpip_adapter_dns_info_t dns_info = {0};
SetDNS_GW_STATICIP((ip4_addr_t *)(&dns_info.ip), NetRec_NetAddrConfigData.dns_ipaddr);

ESP_ERROR_CHECK(tcpip_adapter_set_dns_info(TCPIP_ADAPTER_IF_STA, TCPIP_ADAPTER_DNS_FALLBACK, &dns_info));

上面用到的 tcpip_adapter_dns_info_t 结构体来源于 esp-idf/components/tcpid_adapter/include 路径下的头文件

3.设置 网关、子网掩码、静态IP

tcpip_adapter_ip_info_t ip_info = {0};
//具体IP、掩码、网关是多少大家自己视情况而定
SetDNS_GW_STATICIP(&ip_info.ip, NetRec_NetAddrConfigData.staticip_ipaddr);
SetDNS_GW_STATICIP(&ip_info.gw, NetRec_NetAddrConfigData.gw_ipaddr);
SetDNS_GW_STATICIP(&ip_info.netmask, NetRec_NetAddrConfigData.netmask_ipaddr);

ESP_ERROR_CHECK(tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info));

二、域名解析API

1.getaddrinfo

指定域名、端口号,根据指定返回协议的协议簇、返回地址的类型,创建出域名解析的结果,并被 result 指向,可以通过 result 协议簇解析得到 域名对应的 IP 地址和端口号

int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result );

2.完整的示例代码

/* WiFi station Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "nvs_flash.h"
#include <string.h>

#include "lwip/dns.h"
#include "lwip/err.h"
#include "lwip/ip_addr.h"
#include "lwip/netdb.h"
#include "lwip/sys.h"
/* The examples use WiFi configuration that you can set via 'make menuconfig'.

   If you'd rather not, just change the below entries to strings with
   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/

#define ENABLE_STATIC_IP 1

#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_RETRY

/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;

/* The event group allows multiple bits for each event, but we only care about one event
 * - are we connected to the AP with an IP? */
const int WIFI_CONNECTED_BIT = BIT0;

static const char *TAG = "wifi station";

static int s_retry_num = 0;
const struct addrinfo hints = {
    .ai_family = AF_INET, /* 指定返回地址的协议簇,AF_INET(IPv4)、AF_INET6(IPv6)、AF_UNSPEC(IPv4 and IPv6)*/
    .ai_socktype = SOCK_STREAM, /* 设定返回地址的socket类型,流式套接字 */
};
uint32_t ipv4_cnt;
int error;
ip_addr_t getIP = {0};
bool bDNSFound = 0;

static esp_err_t event_handler(void *ctx, system_event_t *event)
{
    switch (event->event_id)
    {
        case SYSTEM_EVENT_STA_START:
            esp_wifi_connect();
            break;
        case SYSTEM_EVENT_STA_GOT_IP:
            ESP_LOGI(TAG, "got ip:%s", ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip));
            s_retry_num = 0;
            xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
            break;
        case SYSTEM_EVENT_STA_DISCONNECTED:
        {
            if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY)
            {
                esp_wifi_connect();
                xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
                s_retry_num++;
                ESP_LOGI(TAG, "retry to connect to the AP");
            }
            ESP_LOGI(TAG, "connect to the AP fail\n");
            break;
        }
        default:
            break;
    }
    return ESP_OK;
}

void wifi_init_sta()
{
    s_wifi_event_group = xEventGroupCreate();

    tcpip_adapter_init();
    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    wifi_config_t wifi_config = {
        .sta = {.ssid = EXAMPLE_ESP_WIFI_SSID, .password = EXAMPLE_ESP_WIFI_PASS},
    };

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));

#if ENABLE_STATIC_IP
    tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA);
    tcpip_adapter_dns_info_t dns_info = {0};
    tcpip_adapter_ip_info_t ip_info = {0};
	//可以用手机开的热点来测试
    IP4_ADDR(&ip_info.ip, 172, 20, 10, 3);
    IP4_ADDR(&ip_info.gw, 172, 20, 10, 1);
    IP4_ADDR(&ip_info.netmask, 255, 255, 255, 240);
    IP_ADDR4(&dns_info.ip, 172, 20, 10, 1);
    ESP_ERROR_CHECK(tcpip_adapter_set_dns_info(TCPIP_ADAPTER_IF_STA, TCPIP_ADAPTER_DNS_FALLBACK, &dns_info));
    ESP_ERROR_CHECK(tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info));
#else

#endif
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "wifi_init_sta finished.");
    ESP_LOGI(TAG, "connect to ap SSID:%s password:%s", EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
}

void dns_found_cb(const char *name, const ip_addr_t *ipaddr, void *callback_arg)
{
    getIP = *ipaddr;
    ESP_LOGE(TAG, "baidu.com ip is = %d", getIP.u_addr.ip4.addr);
    ESP_LOGI(TAG, "internet connection : %s and ip : %d", error ? "false" : "true", getIP.u_addr.ip4.addr);
}

void app_main()
{

    // Initialize NVS
    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);

    ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
    wifi_init_sta();
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
    if (bits & WIFI_CONNECTED_BIT)
    {
        printf("Ready to Anasis ~\r\n");
        struct addrinfo *result;
        int err;
        err = getaddrinfo("www.baidu.com", "80", &hints, &result);
        if (err != 0) /* 返回值不为0,函数执行失败*/
            printf("getaddrinfo err: %d \n", err);
        // 3、将获取到的信息打印出来
        char buf[100]; /* 用来存储IP地址字符串 */
        struct sockaddr_in *ipv4 = NULL; /* IPv4地址结构体指针 */
        if (result->ai_family == AF_INET)
        {
            ipv4 = (struct sockaddr_in *)result->ai_addr;
            inet_ntop(result->ai_family, &ipv4->sin_addr, buf, sizeof(buf));
            printf("[IPv4-%d]%s [port]%d \n", ipv4_cnt, buf, ntohs(ipv4->sin_port));
        }
        else
            printf("got IPv4 err !!!\n");
        // 4、释放addrinfo 内存
        freeaddrinfo(result);
    }
}

总结

代码改编至示例,上面的 API 也是自己瞎封装,但也能实现功能,可能有其他更好的方法,有空一起探讨

  • 3
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ESP32-S3开发板有多种选择,以下是几款常见的开发板: 1. ESP32-S3-DevKitC-1:这是官方正品开发板,价格较高,但质量有保证,适合入门学习ESP32S3。\[1\] 2. WeAct ESP32-S3核心板:这款开发板价格较低,约为官方开发板的一半,但引脚兼容性较好,性价比较高。\[1\] 3. ESP32-S3-DevKitC-1 ESP32-S3核心板:这款开发板价格与WeAct相同,引脚兼容性也很好,性价比较高。\[1\] 4. ESP32-S3 LVGL 核心板SP 开发板:这款开发板与官方价格相近,但集成了SD卡槽、数字麦克风和一个1.3寸TFT屏幕,性价比较高。作者还提供了不少IDF开发方式的例程和视频,方便学习。\[1\] 5. Freenove ESP32 S3 WROOM开发板:这款开发板与官方价格相近,但集成了SD卡槽和摄像头,并配备了一个1G的SD卡和一个读卡器,性价比较高。适合使用Arduino和Micropython进行开发,对初学者比较友好。\[1\] 以上是一些常见的ESP32-S3开发板选择,具体选择可以根据自己的需求和预算来决定。\[1\] #### 引用[.reference_title] - *1* [第二章 ESP32S3介绍](https://blog.csdn.net/weixin_38476200/article/details/130248490)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [启明智显分享|基于ESP32-S3方案的4寸86盒开发板快速开发及烧录](https://blog.csdn.net/ami82/article/details/126874117)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值