2020年在M0+单片机上实现过MQTT协议(请看:STM32实现MQTT及JSON包思路),当时实现了MQTT登入、发送数据、心跳三个功能;现在有的新的需求,用户要求可远程配置和升级网管设备,这就要实现订阅和JSON包解析功能。
mqtt协议请看:MQTT报文详细解析
一、MQTT订阅
格式:0x82+剩余长度+可变报头(MSB+LSB)+主题长度+主题+Qos(0x00/0x01/0x02)
objectivec
bool mqtt_subscribe(uint8_t* pchBuffer,uint16_t* phwSize,uint16_t hwBufferSize,const char* pcSubscribeTopic,type_mqtt_qos tMqttQoS)
{
uint16_t j=0;
// static union{
// uint8_t chFrameNum[2];
// uint16_t hwFrameNum;
// }s_tFrameNum = {0};
union{
uint8_t chTemp[2];
int16_t iTemp;
}tTemp = {0};
if(NULL == pchBuffer || NULL == phwSize || !hwBufferSize || NULL == pcSubscribeTopic){
return false;
}
tTemp.iTemp = 0;
j = strlen(pcSubscribeTopic);
if(tTemp.iTemp+j+8 > hwBufferSize){
TRACE_ERROR("mqtt_subscribe size error\r\n");
return false;
}
//固定包头
pchBuffer[0] = 0x82;
//剩余长度
if(tTemp.iTemp+j+5 > 127){
pchBuffer[1] = 0x00;
pchBuffer[2] = 0x00;
//低字节在前,高字节在后
pchBuffer[1] = (tTemp.iTemp+j+5) & 0x007F;
pchBuffer[1] = pchBuffer[1] | 0x80;
pchBuffer[2] = (tTemp.iTemp+j+5) >> 7;
//可变包头
pchBuffer[3] = 0x00;
pchBuffer[4] = 0x01;
//主题长度
pchBuffer[5] = (j >> 8) & 0x00FF;
pchBuffer[6] = (j >> 0) & 0x00FF;
//主题
memcpy(pchBuffer+7,pcSubscribeTopic,j);
//QoS等级
pchBuffer[j+7+0] = tMqttQoS;
*phwSize = tTemp.iTemp+j+8;
}else{
pchBuffer[1] = tTemp.iTemp+j+5;
//可变包头
pchBuffer[2] = 0x00;
pchBuffer[3] = 0x01;
//主题长度
pchBuffer[4] = (j >> 8) & 0x00FF;
pchBuffer[5] = (j >> 0) & 0x00FF;
//主题
memcpy(pchBuffer+6,pcSubscribeTopic,j);
//QoS等级
pchBuffer[j+6] = tMqttQoS;
*phwSize = tTemp.iTemp+j+7;
}
//s_tFrameNum.hwFrameNum++;
return true;
}
二、取消订阅
格式:0xA2+剩余长度+可变报头(MSB+LSB)+主题长度+主题
objectivec
bool mqtt_unsubscribe(uint8_t* pchBuffer,uint16_t* phwSize,uint16_t hwBufferSize,const char* pcSubscribeTopic)
{
uint16_t j=0;
union{
uint8_t chTemp[2];
int16_t iTemp;
}tTemp = {0};
if(NULL == pchBuffer || NULL == phwSize || !hwBufferSize || NULL == pcSubscribeTopic){
return false;
}
tTemp.iTemp = 0;
j = strlen(pcSubscribeTopic);
if(tTemp.iTemp+j+7 > hwBufferSize){
TRACE_ERROR("mqtt_unsubscribe size error\r\n");
return false;
}
//固定包头
pchBuffer[0] = 0xA2;
//剩余长度
if(tTemp.iTemp+j+4 > 127){
pchBuffer[1] = 0x00;
pchBuffer[2] = 0x00;
//低字节在前,高字节在后
pchBuffer[1] = (tTemp.iTemp+j+4) & 0x007F;
pchBuffer[1] = pchBuffer[1] | 0x80;
pchBuffer[2] = (tTemp.iTemp+j+4) >> 7;
//可变包头
pchBuffer[3] = 0x00;
pchBuffer[4] = 0x10;
//主题长度
pchBuffer[5] = (j >> 8) & 0x00FF;
pchBuffer[6] = (j >> 0) & 0x00FF;
//主题
memcpy(pchBuffer+7,pcSubscribeTopic,j);
*phwSize = tTemp.iTemp+j+7;
}else{
pchBuffer[1] = tTemp.iTemp+j+4;
//可变包头
pchBuffer[2] = 0x00;
pchBuffer[3] = 0x10;
//主题长度
pchBuffer[4] = (j >> 8) & 0x00FF;
pchBuffer[5] = (j >> 0) & 0x00FF;
//主题
memcpy(pchBuffer+6,pcSubscribeTopic,j);
*phwSize = tTemp.iTemp+j+6;
}
return true;
}
三、JSON解析
首先根据不同数据包,接收相应数据:
objectivec
static fsm_rt_t service_protocol_check(service_protocol_t* tpServiceProtocol)
{
uint8_t* pchBuffer = NULL;
uint16_t hwSize = 0;
uint16_t i=0,j=0;;
if(NULL == tpServiceProtocol->tGetBuffer){
TRACE_ERROR("tGetBuffer=NULL\r\n");
return fsm_rt_err;
}
if(NULL == tpServiceProtocol->tGetSize){
TRACE_ERROR("tGetSize=NULL\r\n");
return fsm_rt_err;
}
if(NULL == tpServiceProtocol){
return fsm_rt_err;
}
pchBuffer = tpServiceProtocol->tGetBuffer();
hwSize = tpServiceProtocol->tGetSize();
if(NULL == pchBuffer|| !hwSize){
return fsm_rt_on_going;
}
switch(eeprom_read_4g_application_layer_protocol_type()){
case HM_4G_MQTT_PROTOCOL://MQTT协议
switch(pchBuffer[0]){
case 0x20://登入回应帧,4个字节
if(hwSize >= 4){
tpServiceProtocol->tServicProtocolRxInfo.pchHead = pchBuffer;
tpServiceProtocol->tServicProtocolRxInfo.hwFrameSize= 4;
tpServiceProtocol->tServicProtocolRxInfo.chFrameType= (tpServiceProtocol->tServicProtocolRxInfo.pchHead[0] >> 4) & 0x0F;
return fsm_rt_cpl;
}
break;
case 0x40://PUBACK 报文(QoS 1应答),4字节
if(hwSize >= 4){
tpServiceProtocol->tServicProtocolRxInfo.pchHead = pchBuffer;
tpServiceProtocol->tServicProtocolRxInfo.hwFrameSize= 4;
tpServiceProtocol->tServicProtocolRxInfo.chFrameType= (tpServiceProtocol->tServicProtocolRxInfo.pchHead[0] >> 4) & 0x0F;
return fsm_rt_cpl;
}
break;
case 0x90://SUBCK 报文(应答),5字节
if(hwSize >= 5){
tpServiceProtocol->tServicProtocolRxInfo.pchHead = pchBuffer;
tpServiceProtocol->tServicProtocolRxInfo.hwFrameSize= 5;
tpServiceProtocol->tServicProtocolRxInfo.chFrameType= (tpServiceProtocol->tServicProtocolRxInfo.pchHead[0] >> 4) & 0x0F;
return fsm_rt_cpl;
}
break;
case 0xB0://UNSUBCK 报文(应答),4字节
if(hwSize >= 4){
tpServiceProtocol->tServicProtocolRxInfo.pchHead = pchBuffer;
tpServiceProtocol->tServicProtocolRxInfo.hwFrameSize= 4;
tpServiceProtocol->tServicProtocolRxInfo.chFrameType= (tpServiceProtocol->tServicProtocolRxInfo.pchHead[0] >> 4) & 0x0F;
return fsm_rt_cpl;
}
break;
case 0xD0://PINGRESP 报文(应答),2字节
if(hwSize >= 2){
tpServiceProtocol->tServicProtocolRxInfo.pchHead = pchBuffer;
tpServiceProtocol->tServicProtocolRxInfo.hwFrameSize= 2;
tpServiceProtocol->tServicProtocolRxInfo.chFrameType= (tpServiceProtocol->tServicProtocolRxInfo.pchHead[0] >> 4) & 0x0F;
return fsm_rt_cpl;
}
break;
case 0x32://PUBACK QoS1报文,变长
service_protocol_publish_qos1_ack_frame(tpServiceProtocol);
case 0x30://PUBACK QoS0报文,变长
if(pchBuffer[1] & 0x80){
i = pchBuffer[1] & 0x7F;
j = pchBuffer[2];
j = j* 128 + 3;
i = i+j;
//TRACE_DEBUG("service_protocol_check wait1 = %d,%d\r\n",hwSize,i);
if(i > SERVICE_BUFFER_SIZE){
tpServiceProtocol->tDeQueue(hwSize);
}
}else{
i = pchBuffer[1] +2;
}
if(hwSize >= i){
tpServiceProtocol->tServicProtocolRxInfo.pchHead = pchBuffer;
tpServiceProtocol->tServicProtocolRxInfo.hwFrameSize= i;
tpServiceProtocol->tServicProtocolRxInfo.chFrameType= (tpServiceProtocol->tServicProtocolRxInfo.pchHead[0] >> 4) & 0x0F;
return fsm_rt_cpl;
}else{
TRACE_DEBUG("service_protocol_check wait2 = %d,%d\r\n",hwSize,i);
return fsm_rt_waiting;
}
//break;
default:
//MY_PRINTF("service_protocol_check\r\n",pchBuffer,hwSize);
tpServiceProtocol->tDeQueue(tpServiceProtocol->tGetSize());
TRACE_ERROR("未知协议\r\n");
return fsm_rt_on_going;
}
break;
default:
tpServiceProtocol->tDeQueue(tpServiceProtocol->tGetSize());
TRACE_ERROR("协议错误\r\n");
return fsm_rt_on_going;
}
return fsm_rt_on_going;
}
然后根据不同数据包,调用不同解析函数:
objectivec
static fsm_rt_t service_protocol_mqtt_analysis(service_protocol_t* tpServiceProtocol)
{
if(NULL == tpServiceProtocol){
return fsm_rt_err;
}
//tpServiceProtocol->tServicProtocolRxInfo.tIsAnalysis = false;
switch((((service_protocol_user_memory_t*)tpServiceProtocol->tServiceProtocolAnalysis.ptUserAnalysisListItem->ptUserStruct)->chBuffer[0] >> 4) & 0x0F){
case MQTT_CONNACK:
TRACE_DEBUG("MQTT 连接确认\r\n");
tpServiceProtocol->tIsRevACK = true;
break;
case MQTT_PUBLISH:
//TRACE_DEBUG("MQTT 收到消息\r\n");
service_protocol_mqtt_analysis_frame_head(tpServiceProtocol);
tpServiceProtocol->tIsRevACK = true;
timer_set(&tpServiceProtocol->tServiceProtocolDataReporting.tTimer,DELAY_TIMERS(20000));//收到消息后
if(NULL != strstr((const char*)(tpServiceProtocol->tServicProtocolFrame.pcTopic),(const char*)eeprom_read_mqtt_subscribe_param_config_topic())){
//参数配置
TRACE_DEBUG("MQTT 收到消息=参数配置\r\n");
return service_protocol_mqtt_analysis_param_config(tpServiceProtocol);
}else if(NULL != strstr((const char*)(tpServiceProtocol->tServicProtocolFrame.pcTopic),(const char*)eeprom_read_mqtt_subscribe_param_query_topic())){
//参数查询
TRACE_DEBUG("MQTT 收到消息=参数查询\r\n");
return service_protocol_mqtt_analysis_param_query(tpServiceProtocol);
}else if(NULL != strstr((const char*)(tpServiceProtocol->tServicProtocolFrame.pcTopic),(const char*)eeprom_read_mqtt_subscribe_ota_upgrade_topic())){
//程序升级
TRACE_DEBUG("MQTT 收到消息=程序升级\r\n");
return service_protocol_mqtt_analysis_ota_upgrade(tpServiceProtocol);
}else if(NULL != strstr((const char*)(tpServiceProtocol->tServicProtocolFrame.pcTopic),(const char*)eeprom_read_mqtt_subscribe_broadcast_topic())){
//广播
TRACE_DEBUG("MQTT 收到消息=广播帧\r\n");
return service_protocol_mqtt_analysis_broadcast(tpServiceProtocol);
}else{
TRACE_DEBUG("MQTT 收到消息=未知主题\r\n");
}
break;
case MQTT_PUBACK:
TRACE_DEBUG("MQTT QoS1消息发布确认\r\n");
SET_EVENT(&tpServiceProtocol->tServiceProtocolDataReporting.tEvent4gRevConfirm);
tpServiceProtocol->tIsRevACK = true;
break;
case MQTT_SUBACK:
TRACE_DEBUG("MQTT 订阅响应\r\n");
tpServiceProtocol->tIsRevACK = true;
break;
case MQTT_UNSUBACK:
TRACE_DEBUG("MQTT 取消订阅响应\r\n");
tpServiceProtocol->tIsRevACK = true;
break;
case MQTT_PINGRESP:
TRACE_DEBUG("MQTT 心跳响应\r\n");
tpServiceProtocol->tIsRevACK = true;
break;
default:
return fsm_rt_cpl;
}
return fsm_rt_cpl;
}
参数配置,根据不同字符串命令,执行操作:
objectivec
pcPosition = strstr((const char*)pchBuffer,"\"GWAddr\":");
if(NULL != pcPosition){
j=strlen("\"GWAddr\":");
m=j;
for(;j<wSize;j++){
if(pcPosition[j] < '0' || pcPosition[j] > '9'){
break;
}
}
n = j;
if(j < wSize && m!=n){
j=ascii_to_dec_v1((uint8_t*)&pcPosition[m],n-m);
if(j){
i++;//记录修改参数个数
eeprom_write_modbus_communication_addr(j);
}
}
}
参数查询,根据JSON数组中命令字符串,执行操作:
objectivec
pcPosition = strstr((const char*)pchBuffer,"\"GWSubscriptionPeriod\"");
if(NULL != pcPosition){
i = sprintf((char*)chTemp,"\"GWSubscriptionPeriod\":%d",eeprom_read_subscription_period_min());
if(i>0){
//判断JSON剩余长度
if(i > get_byte_pipe_size(&tPipe) - get_byte_pipe_num(&tPipe)-150){
i = sprintf((char*)chTemp,"},\"GatewayID\":\"%s\"}",eeprom_read_device_id());
if(i > 0){
enpipe_bytes(&tPipe,chTemp,i);
mqtt_publish_qos1(chTemp,&m,sizeof(chTemp),(const char*)tem_mqtt_merging_topic(eeprom_read_mqtt_publish_param_query_topic()),get_byte_pipe_buffer(&tPipe),get_byte_pipe_num(&tPipe));
service_protocol_user_send(tpServiceProtocol,chTemp,m,false,false,30000,false);
}
reset_byte_pipe(&tPipe);
enpipe_bytes(&tPipe,(uint8_t*)("{\"Command\":1,\"ack\":{"),sizeof("{\"Command\":1,\"ack\":{"));
j=0;
i = sprintf((char*)chTemp,"\"GWSubscriptionPeriod\":%d",eeprom_read_subscription_period_min());
}
if(j){
enpipe_byte (&tPipe,',');
}
j++;
enpipe_bytes(&tPipe,chTemp,i);
}
}
运行结果:
