#申请原创# @21小跑堂
上一篇文章(https://bbs.21ic.com/icview-3420436-1-1.html),我们已经移植好了MQTTClient源码,这篇文件将介绍实现MQTTClient源码,基于ESP8266模块的Socket函数接口,从而提供底层的数据收发接口给MQTTClient库。
1. 需要实现哪些函数
MQTTClient库我使用的是 jiejie 写的,GitHub 仓库:https://github.com/jiejieTop/mqttclient
其中,里面有一个与平台相关的目录下,platform_net_socket.c 这个C文件里面的一些函数是需要实现,才能真的的去连接 MQTT 服务器、收发数据等。
打开这个文件,里面已经使用了 LwIP 的接口实现了这些函数了,但是我这里是需要使用 ESP8266 WIFI 模块进行网络连接,数据收发等功能,所以我需要使用 ESP8266 的来实现这些平台 socket 相关的接口函数。
实现的代码中,是依赖前面写的 AT 命令收发解析器的代码的,《APM32F407+FreeRTOS实现AT模块的命令解析器》这篇文件中有介绍,链接:https://bbs.21ic.com/icview-3418740-1-1.html。
这个文件里面定义了有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;
}
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;
}
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;
}
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;
}
2.4 网络断开连接
int esp8266_socket_close(int fd)
{
int result = 0;
result = at_exce_cmd("AT+CIPCLOSE", NULL, 300);
return result;
}
|