环形缓冲区
因为环形缓冲区中会有很多数据包,而数据包之间是需要有分隔标志的,不然后就会无法分开数据包导致无法正确取出相应的数据包。
环形缓冲器的数据包分隔:
向环形缓冲区添加数据的代码:
//按照一定协议格式将JOSN(MQTT数据中)特殊的数据进行运算。然后解析函数中进行还原
I16 ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const U8 *packet, I16 len)
{
//按照一定协议格式将JOSN(MQTT数据中)特殊的数据进行运算。然后解析函数中进行还原
U16 i = 2;
if(RINGBUF_Put(rb, 0x7E) == -1) return -1; //添加数据包“开始标志”
while (len--) {
switch (*packet) {
case 0x7D: // } ? ---特殊字符
case 0x7E:
case 0x7F:
if(RINGBUF_Put(rb, 0x7D) == -1) return -1; //存储“}”号 ---特殊字符
if(RINGBUF_Put(rb, *packet++ ^ 0x20) == -1) return -1; //对"}"进行加工,在读取时反操作获取原数据"}"
i += 2; //执行了RINGBUF_Put两次
break;
default:
if(RINGBUF_Put(rb, *packet++) == -1) return -1;
i++;
break;
}
}
if(RINGBUF_Put(rb, 0x7F) == -1) return -1; 添加数据包“结束标志”
return i;
}
从环形缓冲区读取数据的代码:
I8 ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, U8 value)
{
switch(value){
case 0x7D: // }
parser->isEsc = 1;
break;
case 0x7E: // ~
parser->dataLen = 0;
parser->isEsc = 0;
parser->isBegin = 1;
break;
case 0x7F: // 特殊字符 ---表示MQTT数据结束.这个是在RINGBUF存储数据函数中添加的
// 因为callback为NULL
if (parser->callback != NULL)
parser->callback();
parser->isBegin = 0;
return 0;//表示解析完了
break;
default:
if(parser->isBegin == 0) break;
// }(0X7D)
if(parser->isEsc){
value ^= 0x20; //再次进行异或操作获取真正的0X7D数据
parser->isEsc = 0;
}
// 普通JOSN数据
if(parser->dataLen < parser->bufSize)
parser->buf[parser->dataLen++] = value; //获取vlaue值
break;
}
return -1; //表示没有解析完
}
ESP8266 SDK MQTT的PUBLISH消息发布工作流程图
往环形缓冲区中添加MQTT的数据
BOOL ICACHE_FLASH_ATTR
MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain)
{
uint8_t dataBuffer[MQTT_BUF_SIZE]; // 1024字节的缓冲区
uint16_t dataLen;
//将message结构体指针传递给MQTT_Client中的mqtt_state.outbound_message
client->mqtt_state.outbound_message = mqtt_msg_publish(&client->mqtt_state.mqtt_connection,
topic, data, data_length,
qos, retain,
&client->mqtt_state.pending_msg_id);
if (client->mqtt_state.outbound_message->length == 0) {
INFO("MQTT: Queuing publish failed\r\n");
return FALSE;
}
//输出整个PUBLISH报文长度
INFO("MQTT: queuing publish, length: %d, queue size(%d/%d)\r\n", client->mqtt_state.outbound_message->length, client->msgQueue.rb.fill_cnt, client->msgQueue.rb.size);
// 将数据放入“队列”中
while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) {
INFO("MQTT: Queue full\r\n");
//这个if代码只能在QUEUE_Puts执行失败的情况下才会执行。
if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) {
INFO("MQTT: Serious buffer error\r\n");
return FALSE;
}
}
// 调用MQTT任务来处理mqtt报文--并将client结构体传递给MQTT_Task任务函数
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
return TRUE;
}
MQTT任务:主要用于发送数据
void ICACHE_FLASH_ATTR
MQTT_Task(os_event_t *e)
{
MQTT_Client* client = (MQTT_Client*)e->par;
uint8_t dataBuffer[MQTT_BUF_SIZE];
uint16_t dataLen;
uint16_t test_len; //用于打印buffer中的数据
if (e->par == 0)
return;
switch (client->connState) {
case TCP_RECONNECT_REQ:
break;
case TCP_RECONNECT:
mqtt_tcpclient_delete(client);
MQTT_Connect(client);
INFO("TCP: Reconnect to: %s:%d\r\n", client->host, client->port);
client->connState = TCP_CONNECTING;
break;
case MQTT_DELETING:
case TCP_DISCONNECTING:
case TCP_RECONNECT_DISCONNECTING:
if (client->security) {
#ifdef MQTT_SSL_ENABLE
espconn_secure_disconnect(client->pCon);
#else
INFO("TCP: Do not support SSL\r\n");
#endif
}
else {
espconn_disconnect(client->pCon);
}
break;
case TCP_DISCONNECTED:
INFO("MQTT: Disconnected\r\n");
mqtt_tcpclient_delete(client);
break;
case MQTT_DELETED:
INFO("MQTT: Deleted client\r\n");
mqtt_client_delete(client);
break;
case MQTT_KEEPALIVE_SEND:
mqtt_send_keepalive(client);
break;
case MQTT_DATA:
if (QUEUE_IsEmpty(&client->msgQueue) || client->sendTimeout != 0) {
break;
}
//将msgQueue队列中的数据读取到dataBuffer中
if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == 0) {
client->mqtt_state.pending_msg_type = mqtt_get_type(dataBuffer);
client->mqtt_state.pending_msg_id = mqtt_get_id(dataBuffer, dataLen);
client->sendTimeout = MQTT_SEND_TIMOUT;
INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id);
//将打包好的mqtt数据通过esp8266的TCP协议发送到mqtt服务器
if (client->security) {
#ifdef MQTT_SSL_ENABLE
espconn_secure_send(client->pCon, dataBuffer, dataLen);
#else
INFO("TCP: Do not support SSL\r\n");
#endif
}
else {
//由于没有使用加密方式,所以将会采用这种方式发送
espconn_send(client->pCon, dataBuffer, dataLen);
//========打印将要通过TCP发送的数据包========
//不能打印buffer,因为buffer中数据包含0x00这样的数据。所以不能以字符串形式输出
//使用for循环逐次打印buffer中每一个数据,然后再将获取的数据分离出来。判断实际的MQTT数据
for(test_len = 0;test_len < dataLen;test_len++)
{
INFO("%x ",dataBuffer[test_len]);
}
}
client->mqtt_state.outbound_message = NULL;
break;
}
break;
}
}