STM32实现MQTT及JSON包思路二

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);
        }
    }

运行结果:

相关推荐
✎ ﹏梦醒͜ღ҉繁华落℘3 天前
单片机基础知识---stm32单片机的优先级
stm32·单片机·mongodb
u152109648493 天前
S.S.Audio PRO A2音频隔离器
嵌入式硬件·音视频·实时音视频·视频编解码·视频
zd8451015003 天前
RS485 总线详解
单片机·嵌入式硬件
半条-咸鱼3 天前
【STM32】I2C协议原理、HAL读写与OLED显示操作
嵌入式硬件·c·信息与通信
牛根生同志3 天前
SPI数据收发的时候 TXE与RXNE标志位置位的时机
stm32·spi·transfer
wohoo_wangzi3 天前
苏州晟雅泰电子:关于W25Q128JVSIQ这个芯片物料的参数,规格及应用领域
嵌入式硬件
goldenrolan3 天前
学习型红外控制系统稳定性挂测工装专项总结
软件测试·python·stm32·嵌入式·红外
✎ ﹏梦醒͜ღ҉繁华落℘3 天前
编程基础 --高内聚,低耦合
c语言·单片机
科芯创展3 天前
1A,1MHz,30VIN,XZ4115,降压恒流LED驱动芯片
单片机·嵌入式硬件
集芯微电科技有限公司3 天前
四通道2A输出集成功率电感降压模块专为紧凑型方案设计
人工智能·单片机·嵌入式硬件·生成对抗网络·计算机外设