• 实现ESP8266模块的socket接口,以提供MQTTClient库底层数据收发接口


    1. 需要实现哪些函数

    MQTTClient库我使用的是 jiejie 写的,GitHub 仓库:

    https://github.com/jiejieTop/mqttclient

    其中,里面有一个与平台相关的目录下,platform_net_socket.c 这个C文件里面的一些函数是需要实现,才能真的的去连接 MQTT 服务器、收发数据等。
    在这里插入图片描述

    打开这个文件,里面已经使用了 LwIP 的接口实现了这些函数了,但是我这里是需要使用 ESP8266 WIFI 模块进行网络连接,数据收发等功能,所以我需要使用 ESP8266 的来实现这些平台 socket 相关的接口函数。

    实现的代码中,是依赖前面写的 AT 命令收发解析器的代码的,这篇文章有介绍《基于FreeRTOS编写ESP8266的AT命令收发解析器》

    这个文件里面定义了有8个函数,但实际其实只要实现4个函数即可,分别是:socket 连接、数据收发、socket 关闭这四个函数,如下:

    int platform_net_socket_connect(const char *host, const char *port, int proto)
    {
        return 0;
    }
    
    int platform_net_socket_recv_timeout(int fd, unsigned char *buf, int len, int timeout)
    {
        return 0;
    }
    
    int platform_net_socket_write_timeout(int fd, unsigned char *buf, int len, int timeout)
    {
        return 0;
    }
    
    int platform_net_socket_close(int fd)
    {
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2. 各个函数的具体实现

    把实现的代码单独写为一个C文件,at_socket_esp8266.c ,然后按照上面 mqtt 平台相关的那4个函数类型,实现4个与 esp8266 模块的 socket 函数,从而给上面的平台网络层代码调用。

    2.1 网络连接函数

    int esp8266_socket_connect(const char *host, const char *port, int proto)
    {
    	int result = 0;
    	char cmd[64] = {0};
    	unsigned char retry_num = 5;
    
    	esp8266_init();
    	
    	while (retry_num--)
    	{
    		/* disconnect to WiFi AP */
    		result = at_exce_cmd("AT+CWQAP", NULL, 1000);
    		if (result)
    		{
    			printf("disconnect WiFi AP fail, status %d\r\n", result);
    			continue;
    		}
    		
    		/* send AT commands to connect to WiFi AP */
    		/* 命令格式:AT+CWJAP="SSID","password" */
    		result = at_exce_cmd("AT+CWJAP=\"" ESP8266_WIFI_SSID "\",\"" ESP8266_WIFI_PASSWORD "\"", NULL, 10*1000);
    		if (result)
    		{
    			printf("esp8266 device connect to WiFi AP fail, check ssid(%s) and password(%s).\r\n", ESP8266_WIFI_SSID, ESP8266_WIFI_PASSWORD);
    			continue;
    		}
    
    		/* send AT commands to connect to server  */
    		/* 命令格式:AT+CIPSTART="TCP","192.168.3.116",8080 */
    		switch (proto)
            {
    	        case PLATFORM_NET_PROTO_TCP:
    				sprintf(cmd, "AT+CIPSTART=\"TCP\",\"%s\",%s", host, port);
    	            break;
    
    	        case PLATFORM_NET_PROTO_UDP:
    				sprintf(cmd, "AT+CIPSTART=\"UDP\",\"%s\",%s", host, port);
    	            break;
    
    	        default:
    	            printf("not supported socket connect type %d.\r\n", proto);
    	            goto __exit;
            }
    				
    		result = at_exce_cmd(cmd, NULL, 5*1000);
    		if (result == AT_RESP_OK)
    		{
    			break;
    		}
    		else
    		{
    			printf("esp8266 device socket connect fail, the socket was not be closed and now will connect retry.\r\n");
    			at_exce_cmd("AT+CIPCLOSE", NULL, 300);
    			continue;
    		}
    	}
    
    __exit:
        return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    2.2 网络数据接收

    int esp8266_socket_recv_timeout(int fd, unsigned char *buf, int len, int timeout)
    {
    	int result = 0;
    	char ch = 0;
    	unsigned int read_index = 0;
    
    	
    	for (read_index = 0; read_index < len; read_index++)
    	{
    		result = at_client_recvchar(&ch, timeout);
    		if (result)
    		{		
    			//printf("esp8266 socket receive fail, return status %d.\r\n", result);
    			return result;
    		}
    		buf[read_index] = ch;
    	}
    
        return read_index;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.3 网络数据发送

    int esp8266_socket_write_timeout(int fd, unsigned char *buf, int len, int timeout)
    {
    	int result = 0;
    	unsigned int cur_package_size = 0, send_size = 0;
    	char cmd[32] = {0};
    		
    	while (send_size < len)
    	{
    		/* 超过最大长度,分包 */
    		if (len - send_size < ESP8266_MODULE_SEND_MAX_SIZE)
    		{
    			cur_package_size = len - send_size;
    		}
    		else
    		{
    			cur_package_size = ESP8266_MODULE_SEND_MAX_SIZE;
    		}
    
    		/* 发送 AT+CIPSEND=7 命令,等待回复 '>' 这个字符( 测试发现回复的是 "OK\r\n>" ) */
    		/* 如果断开了和服务器的网络连接之后,调用此命令回复的是:      "link is not valid\r\nERROR" */
    		sprintf(cmd, "AT+CIPSEND=%d", cur_package_size);
    		result = at_exce_cmd(cmd, NULL, timeout);
    		if (result)
    		{
    			printf("send cmd(%s) error or timeout.\r\n", cmd);
    			return result;
    		}
    
    		/* 发送真正数据,回复 Recv n bytes\r\nSEND OK\r\n 说明发送成功 */
    		result = at_client_senddata((char *)buf + send_size, cur_package_size, timeout);
    		if (result)
    		{
    			printf("send data fali or timeout, return status %d.\r\n", result);
    			return result;
    		}
    
    		send_size += cur_package_size;
    	}
    	
        return result ? result : send_size;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    2.4 断开连接

    int esp8266_socket_close(int fd)
    {
    	int result = 0;
    
    	result = at_exce_cmd("AT+CIPCLOSE", NULL, 300);
    	
        return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    完整的工程代码可以去下面的链接下载。

    https://download.csdn.net/download/luobeihai/86403607?spm=1001.2014.3001.5503

  • 相关阅读:
    .NET8 Identity Register
    java 处理日期的方法
    js防抖、节流以及他们的应用场景
    二叉搜索树
    掌握语义内核(Semantic Kernel):如何精进你的提示词工程
    nginx主备+keepalived实现nginx服务的高可用
    【C++】多态“别太离谱!”
    Python 08学习之文件操作
    Radiobutton 点击选中之后再点击取消选中最简单的实现方式
    python之Numpy(二)
  • 原文地址:https://blog.csdn.net/luobeihai/article/details/126431276