【保姆级IDF】ESP32最强WIFI模式:AP+STA,实现数据中继站

Tips:

抛砖引玉,本文记录ESP32学习过程中遇到的收获。如有不对的地方,欢迎指正。

目录

1. 前言

2. 实现功能

3. 代码思路

4. 代码讲解

4.1 AP_STA模式下的其他设备接入以及接入其他设备的WIFI配置

4.2 自身作为AP和STA模式下的不同套接字的创建及配置

4.3 WIFI事件中被调用的接受连接函数

4.4 数据转发实现中继功能函数

4.5 主函数初始化WIFI外设

4.6 头文件以及宏定义等内容

5.成果展示

6. 总结


1. 前言

        本文介绍ESP32使用WIFI时设置为AP+STA模式下的数据通信,并进行两台设备的数据中继服务。WIFI的基础知识以及ESP32用作单一STA模式或AP模式的文章在我的专栏中可以查看到,这里顺便贴上链接:

【保姆级IDF】ESP32使用WIFI作为AP模式TCP通信:连接客户端+一对多通信

【保姆级IDF】ESP32使用WIFI作为STA模式与其他设备进行TCP通信

【保姆级IDF】ESP32使用WIFI作为STA模式实现:WIFI扫描串口输出+串口输入指定WIFI名称和密码+连接WIFI

读者自行查阅即可,不再赘述。

博主使用合宙的ESP32C3开发板:

2. 实现功能

        1.可作为WIFI热点被其他设备连接并进行通信,可作为STA站点连接其他设备并进行通信

        2.连接到自身的两台设备可以互传数据,自身作为STA站点所连接到的AP热点发来的消息可以广播给所有连接到自身热点的STA站点设备

        3.实现数据中继站的功能,将一台设备发送数据发送到另一台设备,两台设备数据可互相转发

3. 代码思路

        1.使用ESP32的示例代码:softap_sta为基础开发,该代码提供让设备接入自身热点以及让自身连接其他热点的功能。

        2.当自身作为STA模式时,连接上其它WIFI后配置套接字,建立TCP连接。

        3.当自身作为AP模式时,创建套接字并进行相关配置,在WIFI事件:设备接入事件中进行accept接受连接。

        4.创建一个任务去处理两台设备发来的数据。

4. 代码讲解

4.1 AP_STA模式下的其他设备接入以及接入其他设备的WIFI配置

static void wifi_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_AP_STACONNECTED) {
        wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t *) event_data;
        ESP_LOGI(TAG_AP, "Station "MACSTR" joined, AID=%d",
                 MAC2STR(event->mac), event->aid);
        for (int i = 0; i < 10; i++)//遍历连接设备信息表,查找是否有空闲位置
    {
        if (sta_info[i].sock == 0)//如果查找到空闲位置
        {
            memcpy(sta_info[i].deveci_mac, event->mac, 6);//将接入设备的mac地址存入连接设备信息表
            accept_func(ap_scok,i,(void *)&sta_info);//将连接成功后返回的套接字描述符存入连接设备信息表
            break;
        }
    } 
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STADISCONNECTED) {
        wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *) event_data;
        ESP_LOGI(TAG_AP, "Station "MACSTR" left, AID=%d",
                 MAC2STR(event->mac), event->aid);
        for(int j = 0; j < 10; j++)//遍历连接设备信息表,查找退出设备的mac地址
        {
            if(strcmp((char*)sta_info[j].deveci_mac, (char*)event->mac) == 0)//如果找到则:
            {
                shutdown(sta_info[j].sock, 0);//停止连接设备的套接字描述符的工作
                close(sta_info[j].sock);//关闭连接设备的套接字描述符
                sta_info[j].sock = 0;//将连接设备信息表中的套接字描述符置为0
                memset(&sta_info[j].deveci_mac, 0, sizeof(sta_info[j].deveci_mac));//将连接设备信息表中的mac地址置为0
                break;
            }
        }
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();//连接wifi
        ESP_LOGI(TAG_STA, "Station started");
    } 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_STA, "Got IP:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);//设置wifi连接标志位
    }
}


esp_netif_t *wifi_init_softap(void)
{
    esp_netif_t *esp_netif_ap = esp_netif_create_default_wifi_ap();//创建默认的wifi热点

    wifi_config_t wifi_ap_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_AP_SSID,
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_AP_SSID),
            .channel = EXAMPLE_ESP_WIFI_CHANNEL,
            .password = EXAMPLE_ESP_WIFI_AP_PASSWD,
            .max_connection = EXAMPLE_MAX_STA_CONN,
            .authmode = WIFI_AUTH_WPA2_PSK,
            .pmf_cfg = {
                .required = false,
            },
        },
    };

    if (strlen(EXAMPLE_ESP_WIFI_AP_PASSWD) == 0) {
        wifi_ap_config.ap.authmode = WIFI_AUTH_OPEN;
    }

    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_ap_config));

    ESP_LOGI(TAG_AP, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
             EXAMPLE_ESP_WIFI_AP_SSID, EXAMPLE_ESP_WIFI_AP_PASSWD, EXAMPLE_ESP_WIFI_CHANNEL);

    return esp_netif_ap;
}

esp_netif_t *wifi_init_sta(void)
{
    esp_netif_t *esp_netif_sta = esp_netif_create_default_wifi_sta();//创建默认的wifi站

    wifi_config_t wifi_sta_config = {
        .sta = {
            .ssid = EXAMPLE_ESP_WIFI_STA_SSID,
            .password = EXAMPLE_ESP_WIFI_STA_PASSWD,
            .scan_method = WIFI_ALL_CHANNEL_SCAN,
            .failure_retry_cnt = EXAMPLE_ESP_MAXIMUM_RETRY,
            /* Authmode threshold resets to WPA2 as default if password matches WPA2 standards (pasword len => 8).
             * If you want to connect the device to deprecated WEP/WPA networks, Please set the threshold value
             * to WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK and set the password with length and format matching to
            * WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK standards.
             */
            .threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD,
            .sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
        },
    };

    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_sta_config) );

    ESP_LOGI(TAG_STA, "wifi_init_sta finished.");

    return esp_netif_sta;
}

        WIFI的两种模式的配置,等待主函数的调用。代码中有注释,且是例程代码,很好理解。

4.2 自身作为AP和STA模式下的不同套接字的创建及配置

uint8_t sta_socket_init(void)//自身作为STA模式时套接字的创建以及配置
{
    /* 初始化sta套接字 */
    sta_scok = socket(AF_INET, SOCK_STREAM, 0);
    if (sta_scok < 0) {//若返回值为-1,则表示创建失败,处理创建失败的情况
        perror("STA_socket");//以socket:形式输出错误信息
        exit(EXIT_FAILURE);//退出
        return -1; //返回-1
    }
    struct sockaddr_in server_addr;//创建一个结构体变量,用于存储服务器的地址信息
    memset(&server_addr, 0, sizeof(server_addr));//清零初始化
    server_addr.sin_family = AF_INET;//设置地址族为IPv4
    server_addr.sin_port = htons(8080);//设置端口号为8080
    inet_pton(AF_INET, "替换成要连接的目标IP地址如:192.168.0.0", &server_addr.sin_addr); //将点分十进制的IP地址转换为二进制形式,并存储在server_addr.sin_addr中
 
    if (connect(sta_scok, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {//连接服务器
        perror("connect");//以connect:形式输出错误信息
        closesocket(sta_scok);//关闭套接字
        exit(EXIT_FAILURE);//退出
        vTaskDelay(6 / portTICK_PERIOD_MS);//延时6ms
    }
    return 0;
}

uint8_t ap_socket_init(void)//自身作为AP模式时套接字的创建以及配置
{
    struct sockaddr_storage dest_addr;//目标地址
    struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;//IPv4地址
        dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);//设置本机IP
        dest_addr_ip4->sin_family = AF_INET;//IPv4协议
        dest_addr_ip4->sin_port = htons(PORT);//端口号
    /* 初始化ap套接字 */
    ap_scok = socket(AF_INET, SOCK_STREAM, 0);
    if (ap_scok < 0) {//创建失败
        ESP_LOGE(TAG_AP, "Unable to create socket: errno %d", errno);//打印错误信息
        vTaskDelete(NULL);//删除任务
        return -1;
    }
    int opt = 1;
    setsockopt(ap_scok, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));//设置socket选项为:可重用
#if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)
    // Note that by default IPV6 binds to both protocols, it is must be disabled
    // if both protocols used at the same time (used in CI)
    setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
#endif
    ESP_LOGI(TAG_AP, "Socket created");
    int err = bind(ap_scok, (struct sockaddr *)&dest_addr, sizeof(dest_addr));//绑定socket到目标地址
    if (err != 0) {//绑定失败
        ESP_LOGE(TAG_AP, "Socket unable to bind: errno %d", errno);//打印错误信息
        //ESP_LOGE(TAG_AP, "IPPROTO: %d", addr_family);//打印IP协议
        goto CLEAN_UP;//跳转到CLEAN_UP标签处
    }
    ESP_LOGI(TAG_AP, "Socket bound, port %d", PORT);
 
    err = listen(ap_scok, 3);//监听socket
    if (err != 0) {//监听失败
        ESP_LOGE(TAG_AP, "Error occurred during listen: errno %d", errno);//打印错误信息
        goto CLEAN_UP;//跳转到CLEAN_UP标签处
    }
    ESP_LOGI(TAG_AP, "Socket listening");
    return 0;
CLEAN_UP:
    close(ap_scok);//关闭socket
    return -1;
}

        根据不同模式下的工作特性,进行合理的配置。该部分内容许多都在前几期文章中详细解释过,读者可借助代码中的注释进行理解。

4.3 WIFI事件中被调用的接受连接函数

void accept_func(int sockfd,int offset,STA_info *sta_info)
{
    int keepAlive = 1;
    int keepIdle = KEEPALIVE_IDLE;
    int keepInterval = KEEPALIVE_INTERVAL;
    int keepCount = KEEPALIVE_COUNT;
    socklen_t addr_len = sizeof(sta_info[offset].source_addr);
    sta_info[offset].sock = accept(sockfd, (struct sockaddr *)&sta_info[offset].source_addr, &addr_len);//接受连接
    if (sta_info[offset].sock < 0) {
        ESP_LOGE(TAG_AP, "Unable to accept connection: errno %d", errno);
    }
        // Set tcp keepalive option
        setsockopt(sta_info[offset].sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int));//设置socket选项为:保持连接
        setsockopt(sta_info[offset].sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int));//设置socket选项为:保持空闲时间
        setsockopt(sta_info[offset].sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int));//设置socket选项为:保持间隔时间
        setsockopt(sta_info[offset].sock, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int));//设置socket选项为:保持计数
}

        只有在有设备接入自己时才去接受连接,accept会有阻塞,所以需要在连接到ESP32的设备立即建立TCP连接。

4.4 数据转发实现中继功能函数

static void Task_do_retransmit()
{
    int AP_len;//ap接收到的数据长度
    int STA_len;//sta接收到的数据长度
    char rx_buffer[128];//接收缓冲区
    while(1){
        vTaskDelay(200 / portTICK_PERIOD_MS);//延时200ms
        STA_len = recv(sta_scok, rx_buffer, sizeof(rx_buffer) - 1, 0x08);//接收数据
        if(STA_len > 0){//如果接收到了数据,注意WIFi接收到的数据是小端模式,我们单片机内存使用大端模式存储数据!
            for(int x = 0; x < 10; x++){//遍历所有连接的设备
            if(sta_info[x].sock != 0){//如果设备已连接
                send(sta_info[x].sock, rx_buffer, STA_len, 0);//转发数据
            }
        }
        }
        for(int k = 0; k < 10; k++){//遍历所有连接的设备
            if(sta_info[k].sock != 0){//如果设备已连接
                AP_len = recv(sta_info[k].sock, rx_buffer, sizeof(rx_buffer) - 1, 0x08);//接收数据
                if(AP_len > 0){//如果接收到了数据
                    int err = send(sta_scok, rx_buffer, AP_len, 0);//转发数据
                    printf("\nrecvive data: %s\n",rx_buffer);//打印接收到的数据
                    if(err > 0)
                    printf("\ntransaction OK!\n");//完成转发
                }
            }
        }
    }
}

        代码注释解释的很清楚了,唯一要注意的是WIFI发送数据过程中使用的是小端模式,而在单片机内存中一般都以大端模式存储。说白了就是你想发0x1234,那么WIFI转发出去后设备收到的是0x3412(以1字节对齐的情况下)。这里我们没有对大小端模式进行处理,因为中继数据需要转发,ESP32第一次收到WIFI数据后原封不动的转发成WIFI数据,那么数据的存储方式就会这样转化:

        当收到其他设备的数据后,这个数据在缓冲区以小端模式存放,ESP32并不做处理,原封不动的在用WIFI转发给其他设备之后,WIFI又会再一次的将这个"小端模式数据"作处理,在进行一次小端模式的发送。此时这个数据会被转化为大端模式,WIFI发送数据方式并不会准确识别数据的意义,而是直接进行字节序的转换。所以在经过WIFI两次发送之后,原本的大端模式数据会被小端模式倒两次变回最原本的数据。

4.5 主函数初始化WIFI外设

void app_main(void)
{
    ESP_ERROR_CHECK(esp_netif_init());//初始化网络接口
    ESP_ERROR_CHECK(esp_event_loop_create_default());//创建事件循环

    //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());//清除flash
        ret = nvs_flash_init();//重新初始化
    }
    ESP_ERROR_CHECK(ret);

    /* Initialize event group */
    s_wifi_event_group = xEventGroupCreate();//创建事件组

    /* Register Event handler */
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,//注册WIFI事件
                    ESP_EVENT_ANY_ID,
                    &wifi_event_handler,
                    NULL,
                    NULL));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,//注册IP事件
                    IP_EVENT_STA_GOT_IP,
                    &wifi_event_handler,
                    NULL,
                    NULL));

    /*Initialize WiFi */
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));//初始化wifi

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));//设置wifi模式为AP+STA

    /* Initialize AP */
    ESP_LOGI(TAG_AP, "ESP_WIFI_MODE_AP");
    esp_netif_t *esp_netif_ap = wifi_init_softap();//初始化AP

    /* Initialize STA */
    ESP_LOGI(TAG_STA, "ESP_WIFI_MODE_STA");
    esp_netif_t *esp_netif_sta = wifi_init_sta();//初始化STA

    /* Start WiFi */
    ESP_ERROR_CHECK(esp_wifi_start() );//启动wifi

    /*
     * Wait until either the connection is established (WIFI_CONNECTED_BIT) or
     * connection failed for the maximum number of re-tries (WIFI_FAIL_BIT).
     * The bits are set by event_handler() (see above)
     */
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,//等待事件组
                                           WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
                                           pdFALSE,
                                           pdFALSE,
                                           portMAX_DELAY);

    /* xEventGroupWaitBits() returns the bits before the call returned,
     * hence we can test which event actually happened. */
    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG_STA, "connected to ap SSID:%s password:%s",
                 EXAMPLE_ESP_WIFI_STA_SSID, EXAMPLE_ESP_WIFI_STA_PASSWD);
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(TAG_STA, "Failed to connect to SSID:%s, password:%s",
                 EXAMPLE_ESP_WIFI_STA_SSID, EXAMPLE_ESP_WIFI_STA_PASSWD);
    } else {
        ESP_LOGE(TAG_STA, "UNEXPECTED EVENT");
        return;
    }

    /* Set sta as the default interface */
    esp_netif_set_default_netif(esp_netif_sta);//设置sta为默认接口

    /* Enable napt on the AP netif */
    if (esp_netif_napt_enable(esp_netif_ap) != ESP_OK) {//启用NAPT
        ESP_LOGE(TAG_STA, "NAPT not enabled on the netif: %p", esp_netif_ap);
    }
    sta_socket_init();//初始化作为sta模式的套接字
    ap_socket_init();//初始化作为ap模式套接字
    xTaskCreate(Task_do_retransmit, "Task_do_retransmit", 4096 * 5, NULL, 5, NULL);//创建任务
}

4.6 头文件以及宏定义等内容

#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_mac.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif_net_stack.h"
#include "esp_netif.h"
#include "nvs_flash.h"
#include "lwip/inet.h"
#include "lwip/netdb.h"
#include "lwip/sockets.h"
#include "lwip/lwip_napt.h"
#include "lwip/err.h"
#include "lwip/sys.h"

#define EXAMPLE_ESP_WIFI_STA_SSID           "替换成要连接的热点名称"//CONFIG_ESP_WIFI_REMOTE_AP_SSID
#define EXAMPLE_ESP_WIFI_STA_PASSWD         "替换成要连接的热点密码"//CONFIG_ESP_WIFI_REMOTE_AP_PASSWORD
#define EXAMPLE_ESP_MAXIMUM_RETRY           CONFIG_ESP_MAXIMUM_STA_RETRY

#if CONFIG_ESP_WIFI_AUTH_OPEN
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD   WIFI_AUTH_OPEN
#elif CONFIG_ESP_WIFI_AUTH_WEP
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD   WIFI_AUTH_WEP
#elif CONFIG_ESP_WIFI_AUTH_WPA_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD   WIFI_AUTH_WPA_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA2_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD   WIFI_AUTH_WPA2_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA_WPA2_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD   WIFI_AUTH_WPA_WPA2_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA3_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD   WIFI_AUTH_WPA3_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA2_WPA3_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD   WIFI_AUTH_WPA2_WPA3_PSK
#elif CONFIG_ESP_WIFI_AUTH_WAPI_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD   WIFI_AUTH_WAPI_PSK
#endif

/* AP Configuration */
#define EXAMPLE_ESP_WIFI_AP_SSID            "创建的热点名称"//CONFIG_ESP_WIFI_AP_SSID
#define EXAMPLE_ESP_WIFI_AP_PASSWD          "创建的热点密码"//CONFIG_ESP_WIFI_AP_PASSWORD
#define EXAMPLE_ESP_WIFI_CHANNEL            CONFIG_ESP_WIFI_AP_CHANNEL
#define EXAMPLE_MAX_STA_CONN                CONFIG_ESP_MAX_STA_CONN_AP


/* The event group allows multiple bits for each event, but we only care about two events:
 * - we are connected to the AP with an IP
 * - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1

static const char *TAG_AP = "WiFi SoftAP";
static const char *TAG_STA = "WiFi Sta";

static int s_retry_num = 0;

/* FreeRTOS event group to signal when we are connected/disconnected */
static EventGroupHandle_t s_wifi_event_group;
int sta_scok = 0;//STA模式的套接字
int ap_scok = 0;//AP模式的套接字

typedef struct _STA_info{
    uint8_t deveci_mac[6];//连接设备的mac地址
    int sock;             //连接设备的套接字描述符
    struct sockaddr_storage source_addr;//存储连接设备的IP与端口号
}STA_info;
void accept_func(int sockfd,int offset,STA_info *sta_info);
STA_info sta_info[10];//连接设备信息表
#define PORT                        3333
#define KEEPALIVE_IDLE              5
#define KEEPALIVE_INTERVAL          5
#define KEEPALIVE_COUNT             3
#define INADDR_ANY          ((u32_t)0xc0a80401UL)//192.168.4.1

        以上函数中没找到的变量和宏都可以在这里找到。

5.成果展示

        电脑端作为ESP32所连接的热点,收到手机发来的数据,这个数据由ESP32中继转发出来,电脑也可以向手机发送数据,ESP32同样可以中继转发。

6. 总结

        以上就是本次分享的全部内容,用好AP+STA模式的基础是将单独的AP和STA模式使用好,包括WIFI的配置和套接字的熟练使用,融会贯通之后便会更上一层楼。如何使用AP模式、STA模式在前几期文章中有详细解释。链接放在上文了。大家如果觉得有用,还请多多点赞收藏关注,你的支持就是对我最大的肯定。谢谢大家!

        如有疑问欢迎留言交流。以上观点为个人理解,只想抛砖引玉,如有不对的地方欢迎指正,不喜轻喷。


2024.10.26-18:00

WIFI部分完结撒花,下期开始更新蓝牙的使用

以下是一个简单的 ESP8266 IDF 使用 AP STA 模式的代码示例: ``` #include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_wifi.h" #include "esp_event_loop.h" #include "esp_log.h" #define WIFI_SSID "my_wifi_ssid" #define WIFI_PASS "my_wifi_password" static const char *TAG = "wifi station"; static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (event_id == WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_id == WIFI_EVENT_STA_DISCONNECTED) { esp_wifi_connect(); ESP_LOGI(TAG, "retry to connect to the AP"); } } void wifi_init_sta(void) { wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); esp_wifi_init(&cfg); esp_event_handler_instance_t instance_any_id; esp_event_handler_instance_t instance_got_ip; esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, &instance_any_id); esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, &instance_got_ip); wifi_config_t wifi_config = { .sta = { .ssid = WIFI_SSID, .password = WIFI_PASS, }, }; esp_wifi_set_mode(WIFI_MODE_STA); esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config); esp_wifi_start(); ESP_LOGI(TAG, "wifi_init_sta finished."); ESP_LOGI(TAG, "connect to ap SSID:%s password:%s", WIFI_SSID, WIFI_PASS); } void wifi_init_softap(void) { wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); esp_wifi_init(&cfg); wifi_config_t wifi_config = { .ap = { .ssid = "my_ap_ssid", .ssid_len = 0, .password = "my_ap_password", .max_connection = 4, .authmode = WIFI_AUTH_WPA_WPA2_PSK, }, }; esp_wifi_set_mode(WIFI_MODE_AP); esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config); esp_wifi_start(); ESP_LOGI(TAG, "wifi_init_softap finished."); ESP_LOGI(TAG, "ssid:%s password:%s", "my_ap_ssid", "my_ap_password"); } void app_main() { ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); wifi_init_softap(); wifi_init_sta(); } ``` 这个示例代码中,我们使用了 ESP8266 IDF 的 Wi-Fi 库来实现 AP STA 模式。在这个模式下,ESP8266 既可以作为一个 Wi-Fi 接入点(AP),也可以作为一个 Wi-Fi 客户端(STA)连接到其他 Wi-Fi 接入点。在这个示例中,我们首先初始化了一个 Wi-Fi 接入点,然后初始化了一个 Wi-Fi 客户端,连接到了一个指定的 Wi-Fi 接入点。在连接过程中,我们还注册了一些 Wi-Fi 事件处理程序,以便在连接状态发生变化时进行处理。
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_山岚_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值