乐鑫Esp32学习之旅⑨ esp32上实现本地 TCP 客户端和服务端角色,可断线重连原路返回数据。(附带Demo)
Posted on 2018-06-26 17:54 CoreHouse 阅读(1062) 评论(0) 收藏 举报- 本系列博客学习由非官方人员 半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途。如有不对之处,请留言,本人及时更改。
 1、 爬坑学习新旅程,虚拟机搭建esp32开发环境,打印 “Hellow World”。
  2、 巧用eclipes编辑器,官方教程在在Windows下搭建esp32开发环境,打印 “Hellow World”。
  3、 认识基本esp32的GPIO接口,开始点亮您的第一盏 LED和中断回调实现按键功能 。
  4、体会esp32的强大的定时器功能, 实现定时2s闪烁一盏LED灯。
  5、接触实践esp32的pwm宽度脉冲功能, 实现呼吸效果闪烁一盏LED灯。
  6、smartConfig和微信airKiss在esp32的实现,一键配网轻松快捷连接路由器。
  7、利用GPIO中断做一个按键的短按和长按的回调事件,再也无须担心触发源。
  8、esp32上实现本地 UDP 客户端和服务端角色,在局域网内实现通讯。
  9、esp32上实现本地 TCP 客户端和服务端角色,可断线重连原路返回数据。
  10、乐鑫esp32 SDK编程利用rmt驱动ws2812七彩灯,实现彩虹渐变效果。
  11、入门 乐鑫esp-adf 音频框架开发,esp32造一个蓝牙耳机,实现切换歌曲,获取歌曲信息等功能。
  12、开源一个微信公众号airkiss配网esp32以及局域网发现功能的工程,分享一个airkiss配网小工具。
  13、esp32 内置 dns 服务器,无需外网访问域名返回指定网页。
  14、esp32 sdk编程实现门户强制认证,连接esp32热点之后,自动强制弹出指定的登录界面。
  15、认识本地离线语音唤醒识别框架 esp-skainet ,实现较低成本的硬件语音本地识别控制。
  16、学习本地语音唤醒离线识别框架 esp-skainet ,如何修改唤醒词? 如何自定义命令词?如何做意图动作?
  17、全网首发,乐鑫esp32 sdk直连京东微联·小京鱼 · IoT开放平台,实现叮咚音响语音智能控制。
  18、入门京东微联·小京鱼的控制面板H5开发,读懂vue语法,做自己的控制页面。
  19、重磅开源,如何在微信小程序上ble蓝牙配网esp32,blufi的那些事!
  20、一篇好文,开发过程中编译esp32固件太大,无法正常启动?教你如何自定义分区表partitions.csv。
文章目录
本篇博文目录:
一. 前言;
-  2018年的高考分数线出来了,广东省的本科的理科分数线 375分,貌似比当年2014年高考低好多啊。。哈哈! 
-  回归正题, TCP基本协议,我就不多说了,可以看看我的前面的8266系列的,本博文通讯框架基于lwip,主要修改实现以下功能:
-  TCP服务端时候,客户端连接进来的数据原路返回,而且断开连接之后,允许重新连接。
-  TCP客户端时候,设置每三秒向服务器发送数据!
二. 服务端;
- 左边是 visual studio code编译器的终端显示esp32显示打印的数据,右边是手机通讯调试助手。
 
2.1 实现的过程:
- 首先根据配置信息来开启热点模式,等待设备的连接。
- 设备连接成功后,开启TCP server服务器端,然后通讯!
- 注意的是:当前的网关的IP地址就是服务器地址,默认是192.168.4.1这个是乐鑫在出厂时候写死的!知道就好!
2.2 核心的代码:
- ①:创建热点(和上篇的UDP一样):
void wifi_init_softap()
{
    tcp_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 = {
        .ap = {
            .ssid = SOFT_AP_SSID, //设置名字
            .ssid_len = 0,
            .max_connection = SOFT_AP_MAX_CONNECT, //最大连接数目
            .password = SOFT_AP_PAS, //密码
            .authmode = WIFI_AUTH_WPA_WPA2_PSK},//加密方式
    };
    if (strlen(EXAMPLE_DEFAULT_PWD) == 0)//如果密码为空,则设置开放性wifi热点
    {
        wifi_config.ap.authmode = WIFI_AUTH_OPEN; 
    }
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
    ESP_LOGI(TAG, "SoftAP set finish:%s pas:%s \n",EXAMPLE_DEFAULT_SSID, EXAMPLE_DEFAULT_PWD);
}
- ②:创建TCP服务器,等待客户端连接:
//形参的含义:true为创建服务器并且等待连接,false仅仅只是等待连接;返回值是是否成功接收到一个设备的连接
esp_err_t create_tcp_server(bool isCreatServer)
{
    if (isCreatServer)
    {
        ESP_LOGI(TAG, "server socket....,port=%d\n", TCP_PORT);
        server_socket = socket(AF_INET, SOCK_STREAM, 0);
        if (server_socket < 0)
        {
            show_socket_error_reason("create_server", server_socket);
            return ESP_FAIL;
        }
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(TCP_PORT);//指定的端口号
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        //开始创建
        if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
        {
            show_socket_error_reason("bind_server", server_socket);
            close(server_socket);
            return ESP_FAIL;
        }
    }
    //监听指定的端口
    if (listen(server_socket, 5) < 0)
    {
        show_socket_error_reason("listen_server", server_socket);
        close(server_socket);
        return ESP_FAIL;
    }
    connect_socket = accept(server_socket, (struct sockaddr *)&client_addr, &socklen);
    //判断是否连接成功
    if (connect_socket < 0)
    {
        show_socket_error_reason("accept_server", connect_socket);
        close(server_socket);
        return ESP_FAIL;
    }
    /*connection established,now can send/recv*/
    ESP_LOGI(TAG, "tcp connection established!");
    return ESP_OK;
}
- ③:接收数据:
void recv_data(void *pvParameters)
{
    int len = 0;
    char databuff[1024];
    while (1)
    {
     while (1){  
            //每次接收都要清空接收数组
            memset(databuff, 0x00, sizeof(databuff));
            len = recv(connect_socket, databuff, sizeof(databuff), 0);
            //打印接收到的数组
            ESP_LOGI(TAG, "recvData: %s\n", databuff);
            //原路返回,不指定某个客户端
            send(connect_socket, databuff , sizeof(databuff), 0);
        if (len > 0) {
                g_total_data += len;
                g_rxtx_need_restart = false;
            } else {
                show_socket_error_reason("recv_data", connect_socket);
                g_rxtx_need_restart = true;
            }
        }
    }
    g_rxtx_need_restart = true;
    vTaskDelete(NULL);
}
2.3 注意问题:
- 虽然乐鑫也提供了TCP的例程,但是发现是当客户端断开连接之后再次连接时候,客户端提示已经连接成功,但发现服务器并没有去监听这个设备的信息,原因在于没有去等待监听,还是要通过listen()和accept()方法来获取一个远程的设备地址。
三. TCP Client客户端;
 
-  下图可以看到,服务器的地址为 192.168.1.104,端口:8266,此配置信息可在demo里面的头文件修改!
-  发送数据原路返回和上面的服务器代码一样,只是当与服务器断开连接时候,需要关闭 socket重新连接,而我们的服务器是不需要关闭socket只是继续等待客户端连接,这一点需要注意!
3.1 实现的过程:
- 上电开启station模式来连接指定的路由器,连接成功之后创建socket连接tcp server服务器。
- 连接成功后,需要开启收发的任务,并且在收到数据之后原路返回。
- 如果连接成功之后,服务器突然断开,那么esp32需要断开socket,毕竟服务器已经断开,留着也没用,之后尝试重新创建socket来连接服务器,直到成功为止!
3.2 核心代码:
- ① 连接指定的路由器:
void wifi_init_sta()
{
    tcp_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 = GATEWAY_SSID, //路由器名字
            .password = GATEWAY_PAS},//路由器密码
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
    ESP_LOGI(TAG, "wifi_init_sta finished.");
    ESP_LOGI(TAG, "connect to ap SSID:%s password:%s \n",
             GATEWAY_SSID, GATEWAY_PAS);
}
- ② 创建客户端并且指定连接到服务器端:
//创建TCP客户端连接到指定的服务器
esp_err_t create_tcp_client()
{
    ESP_LOGI(TAG, "will connect gateway ssid : %s port:%d\n",
             TCP_SERVER_ADRESS, TCP_PORT);
    connect_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (connect_socket < 0)
    {
        show_socket_error_reason("create client", connect_socket);
        return ESP_FAIL;
    }
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(TCP_PORT);
    server_addr.sin_addr.s_addr = inet_addr(TCP_SERVER_ADRESS);
    ESP_LOGI(TAG, "connectting server...");
    if (connect(connect_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        show_socket_error_reason("client connect", connect_socket);
        ESP_LOGE(TAG, "connect failed!");
        return ESP_FAIL;
    }
    ESP_LOGI(TAG, "connect success!");
    return ESP_OK;
}
四 demo的配置信息:
 
- 下面的配置宏定义需要在头文件修改!
//true为开启热点并且创建tcp服务器,fasle为连接到指定的路由器并且连接到指定的tcp服务器
#define TCP_SERVER_CLIENT_OPTION FALSE 
//打印的tag
#define TAG "XuHongTCP-->" 
//以下是softAP热点模式的配置信息
#define SOFT_AP_SSID "XuHongTCP2018"
#define SOFT_AP_PAS "xuhong123456" //如果密码设置为空,则配置的热点是开放的,没有密码的。
#define SOFT_AP_MAX_CONNECT 1 //作为AP热点时候,最大的连接数目
//以下是station模式配置信息,是您家里的路由器的信息
#define GATEWAY_SSID "AliyunOnlyTest"
#define GATEWAY_PAS "aliyun#123456"
#define TCP_SERVER_ADRESS "192.168.1.104" //要连接TCP服务器地址
//统一的端口号,包括TCP客户端或者服务端
#define TCP_PORT 8266
五 下载:
- 注意运行demo前务必要修改配置信息,保证路由器名字密码正确!
- 本博文硬件代码下载:https://download.csdn.net/download/xh870189248/10502414
- esp32汇总工程,欢迎star,收到第一更新信息:https://github.com/xuhongv/StudyInEsp32
 
                    
                     
                    
                 
                    
                 
 
         
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号