ESP8266 SDK开发——MQTT例程源码分析之PUBLISH报文发送

环形缓冲区 

因为环形缓冲区中会有很多数据包,而数据包之间是需要有分隔标志的,不然后就会无法分开数据包导致无法正确取出相应的数据包。

环形缓冲器的数据包分隔:

 

向环形缓冲区添加数据的代码:

//按照一定协议格式将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;
    }
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值