《ESP-IDF入门指南》----->LwIP(tcp client 通信)

《ESP-IDF入门指南》----->LwIP(tcp client 通信)



一名在校学生,不定时分享和总结一些嵌入式和物联网相关的知识,欢迎大家提出关于本文章相关意见。


简介


ESP-IDF 使用开源的LwIP轻量级TCP/IP协议栈,并在原始的LwIP上做了修改和补充。

支持的API:
  1. BSD套接字API。
  2. Netconn API 已启用,但暂无对 ESP-IDF 应用程序的官方支持。

warning:

  1. 在使用除BSD套接字API以外的任意 lwIP API 时,请确保所用 API 为线程安全。请启用 CONFIG_LWIP_CHECK_THREAD_SAFETY 配置选项并运行应用程序,检查所用 API 是否线程安全。
  2. lwIP 断言 TCP/IP 核心功能可以正确访问。如果未能从正确的 lwIP FreeRTOS 任务 访问,或没有正确锁定,则执行中止。
  3. 建议使用 ESP-NETIF 组件与 lwIP 交互。

注:

BSD套接字 :(伯克利套接字 Internet Berkeley sockets) API是一种应用程序接口(API),用于网络套接字( socket)与Unix域套接字,包括了一个用C语言写成的应用程序开发库,主要用于实现进程间通讯,在计算机网络通讯方面被广泛使用。

NetconnAPI :是lwIP协议栈提供的一套方便易用的网络编程接口。

BSD套接字API


  • socket()

  • bind()

  • accept()

  • shutdown()

  • getpeername()

  • getsockopt()setsockopt():请参阅 套接字选项

  • close()

  • read()readv()write()writev()

  • recv()recvmsg()recvfrom()

  • send()sendmsg()sendto()

  • select()

  • poll():ESP-IDF 通过在内部调用 select() 实现 poll(),因此,建议直接调用 select()

  • fcntl():

    非标准函数:

    ioctl()

应用实例


#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "lwip/sockets.h"
//print information symbol
const static char* TAG="MYWIFI";


//create two task handle with taskNotify function
TaskHandle_t task1;
TaskHandle_t task2;

/*
*********************************************************************************************************
* 函 数 名: event_handler
* 功能说明: wifi and ip event
* 形    参:无
* 返 回 值: 无
*********************************************************************************************************
*/
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, "wifi connecting\n");
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        ESP_LOGI(TAG, "wifi connected\n");
        vTaskDelay(500);
        ESP_LOGI(TAG, "wifi connecting\n");
        esp_wifi_connect();
    } 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:" IPSTR, IP2STR(&event->ip_info.ip));
        xTaskNotifyGive(task2);
    }
}
/*
*********************************************************************************************************
* 函 数 名: my_wifi_nvs_flash_init
* 功能说明: init nvs-flash
* 形    参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void my_wifi_nvs_flash_init()
{
    esp_err_t err=ESP_OK;
    err=nvs_flash_init();
    if(err!=ESP_OK){
        ESP_ERROR_CHECK(nvs_flash_erase());
        nvs_flash_init();
    }
}
/*
*********************************************************************************************************
* 函 数 名: my_wifi_netif_event_config
* 功能说明: init netif and event
* 形    参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void my_wifi_netif_and_event_config()
{

    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

    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));


}

void my_wifi_wifi_init(void* param)
{
    my_wifi_nvs_flash_init();
    my_wifi_netif_and_event_config();

    wifi_init_config_t cfg=WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    const wifi_config_t conf={
        .sta={
            .ssid="",//wifi id
            .password=""//wifi password
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA,&conf));
    ESP_ERROR_CHECK(esp_wifi_start());
    vTaskDelete(NULL);
}

void tcp_client_task(void* param){
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    int sock;
    while(1){
        sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
        if(sock<0){
            ESP_LOGE("TAG","socket connect is error\n");
        }
        struct sockaddr_in dest_addr;
        dest_addr.sin_addr.s_addr = inet_addr("");//ip地址
        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(8080);//端口号
        int err=connect(sock,&dest_addr,sizeof(struct sockaddr));
        if(err!=0){
            ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
        }
        while (1) {
            char* msg="message from esp32";
            int err = send(sock, msg, strlen(msg), 0);
            if (err < 0) {
                ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                break;
            }
            char rex_buf[100];
            //默认情况下,该函数会进行阻塞.
            //ret = lwip_recv_tcp(sock, mem, len, flags);在这个函数中进行阻塞.
            //如果将flags==MSG_DONTWAIT则非阻塞
            err=recv(sock, rex_buf, sizeof(rex_buf) - 1,MSG_DONTWAIT);
            if(err>=0){
                rex_buf[err]=0;
                printf("received message:%s\n",rex_buf);
            }
            vTaskDelay(2000 / portTICK_PERIOD_MS);
        }

        if (sock != -1) {
            ESP_LOGE(TAG, "Shutting down socket and restarting...");
            shutdown(sock, 0);
            close(sock);
        }
    }
}

void app_main(void)
{

    xTaskCreate(my_wifi_wifi_init, "wifi connect", 4096, NULL, 5, &task1);
    xTaskCreate(tcp_client_task, "tcp_client", 4096, NULL, 5, &task2);
}

参考文档


  1. (https://docs.espressif.com/projects/esp-idf/zh_CN/stable/esp32/api-guides/lwip.html#bsd-api)
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
你可以使用 ESP-IDF 中提供的 FreeRTOS API 和 LWIP API 来实现 ESP32-C3 上的非阻塞式 TCP client。以下是一个简单的实现示例: ```c #include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" #include "lwip/err.h" #include "lwip/sys.h" #include "lwip/netdb.h" #include "lwip/api.h" static const char *TAG = "tcp_client"; void tcp_client_task(void *pvParameters) { const char *server_ip = "192.168.1.100"; const int server_port = 12345; struct sockaddr_in dest_addr; dest_addr.sin_addr.s_addr = inet_addr(server_ip); dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(server_port); int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { ESP_LOGE(TAG, "Failed to create socket"); vTaskDelete(NULL); } // Set socket to non-blocking mode int flags = fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); // Connect to server int ret = connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); if (ret < 0 && errno != EINPROGRESS) { ESP_LOGE(TAG, "Failed to connect to server"); vTaskDelete(NULL); } // Wait for connection to complete fd_set write_fds; FD_ZERO(&write_fds); FD_SET(sockfd, &write_fds); ret = select(sockfd + 1, NULL, &write_fds, NULL, NULL); if (ret < 0) { ESP_LOGE(TAG, "Failed to wait for connection to complete"); vTaskDelete(NULL); } // Check if connection was successful int error = 0; socklen_t error_len = sizeof(error); getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &error_len); if (error != 0) { ESP_LOGE(TAG, "Failed to connect to server: %d", error); vTaskDelete(NULL); } ESP_LOGI(TAG, "Connected to server"); char rx_buffer[128]; while (1) { // Receive data int len = recv(sockfd, rx_buffer, sizeof(rx_buffer), 0); if (len < 0 && errno != EAGAIN) { ESP_LOGE(TAG, "Failed to receive data"); break; } else if (len > 0) { ESP_LOGI(TAG, "Received %d bytes: %.*s", len, len, rx_buffer); } // Send data const char *tx_buffer = "Hello, server!"; len = send(sockfd, tx_buffer, strlen(tx_buffer), MSG_DONTWAIT); if (len < 0 && errno != EAGAIN) { ESP_LOGE(TAG, "Failed to send data"); break; } else if (len > 0) { ESP_LOGI(TAG, "Sent %d bytes: %s", len, tx_buffer); } vTaskDelay(pdMS_TO_TICKS(1000)); } close(sockfd); vTaskDelete(NULL); } void app_main() { xTaskCreate(tcp_client_task, "tcp_client_task", 4096, NULL, 5, NULL); } ``` 在这个示例中,我们首先创建了一个非阻塞式的 socket,并设置其连接状态为非阻塞式。在连接服务器时,我们使用了 `select()` 函数来等待连接完成。在主循环中,我们使用 `recv()` 函数接收数据,并使用 `send()` 函数发送数据。注意,在发送数据时,我们使用了 `MSG_DONTWAIT` 标志来确保数据发送不会阻塞。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不知名小白11

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

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

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

打赏作者

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

抵扣说明:

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

余额充值