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

运行结果:

相关推荐
風清掦1 小时前
【STM32学习笔记-14】WDG看门狗 - 14.2 WWDG窗口看门狗
笔记·stm32·单片机·嵌入式硬件·学习·fpga开发
czhaii2 小时前
单片机伺服电机加减速控制子程序
c语言·单片机
熠速2 小时前
基于PolarLabBox的永磁同步电机(PMSM)零速带载闭环+全速域无感:8小时从理论到落地
单片机·嵌入式硬件·硬件在环半实物仿真
深圳市晶科鑫实业有限公司3 小时前
5G与AIoT时代:如何选择晶振常用频率?
服务器·单片机·物联网·5g·智能路由器·健康医疗·信息与通信
JNX_SEMI3 小时前
EG2031L:220V半桥驱动,1.5A灌流,宽压5V供电
单片机·嵌入式硬件
m0_377108143 小时前
stm32-SPI
stm32·单片机·嵌入式硬件
QiLinkOS4 小时前
从技术到资产的跃迁:企业专利布局的深层逻辑
c语言·数据结构·c++·单片机·嵌入式硬件·算法·开源
夜听莺儿鸣4 小时前
201_002 Zynq7000 SoC PS资源介绍
嵌入式硬件·硬件架构
wohoo_wangzi4 小时前
苏州晟雅泰电子:关于汽车领域会用到的5类存储芯片,容量参数、设计方案和主要应用场景
嵌入式硬件·汽车