下面代码是随手写的,没有严谨测试仅供参考测试
uint8_t msgBuf[200]={"msg from mcu"};
uint8_t txBuf[250]={0};
uint16_t msgid=0;
uint16_t mqttTaskState=0;
uint16_t t100msCount=0;
uint8_t sendFlag1=0;
uint8_t sendFlag2=0;
void t100msTask1(void) { //100ms执行一次该函数switch(mqttTaskState) {case 0: {if (++t100msCount >= 10) {t100msCount=0;//4g模块有一段上电时间,发AT指令一直等待上电完成HAL_UART_Transmit(&huart2, (uint8_t *)"AT\r", strlen("AT\r"), 0xffffff);}}break;case 1: {if (++t100msCount >= 10) {t100msCount=0;//配置接收到订阅主题数据时,也把数据长度输出HAL_UART_Transmit(&huart2, (uint8_t *)"AT+QMTCFG=\"recv/mode\",1,0,1\r", strlen("AT+QMTCFG=\"recv/mode\",1,0,1\r"), 0xffffff);}}break;case 2: {if (++t100msCount >= 10) {t100msCount=0;//建立连接前先关闭一下,确保是一个新的连接,指令原型:AT+QMTCLOSE=<client_idx>HAL_UART_Transmit(&huart2, (uint8_t *)"AT+QMTCLOSE=1\r", strlen("AT+QMTCLOSE=1\r"), 0xffffff);}}break;case 3: {if (++t100msCount >= 50) {t100msCount=0;//打开通道1,后面两组引号是ip和端口号HAL_UART_Transmit(&huart2, (uint8_t *)"AT+QMTOPEN=1,\"141.11.136.7\",1883\r", strlen("AT+QMTOPEN=1,\"141.11.136.7\",1883\r"), 0xffffff);}}break;case 4: {if (++t100msCount >= 20) {t100msCount=0;//建立mqtt连接,clientMCU是名称可以任意名称,后面两组引号是账号和密码,留空,上一篇文章搭建的mqtt服务端,设置了允许无账号连接HAL_UART_Transmit(&huart2, (uint8_t *)"AT+QMTCONN=1,\"clientMCU\",\"\",\"\"\r", strlen("AT+QMTCONN=1,\"clientMCU\",\"\",\"\"\r"), 0xffffff);}}break;case 5: {if (++t100msCount >= 20) {t100msCount=0;//订阅主题,test/topic是主题名,其中msgid绝对不能为0,否则会返回错误,指令原型:AT+QMTSUB=<client_idx>,<msgid>,<topic1>,<qos1>HAL_UART_Transmit(&huart2, (uint8_t *)"AT+QMTSUB=1,1,\"test/topic\",1\r", strlen("AT+QMTSUB=1,1,\"test/topic\",1\r"), 0xffffff);}}break;case 6: {if (sendFlag1==0) {sendFlag1=1;if (msgid==0)msgid++;uint8_t qos=1;uint16_t xlen=snprintf((char*)txBuf, 250, "AT+QMTPUBEX=%d,%d,%d,0,\"%s\",%d\r", 1, msgid++, qos, "test/topic", strlen((char*)&msgBuf[0]));HAL_UART_Transmit(&huart2, (uint8_t *)txBuf, xlen, 0xffffff);}}break;case 7: {if (sendFlag2==0) {sendFlag2=1;HAL_UART_Transmit(&huart2, (uint8_t *)msgBuf, strlen((char*)&msgBuf[0]), 0xffffff);}}break;}}//把从4g模块接收buff传进来,p是buff,len是buff长度
void mqttTaskRecv(char *p, u32 len) {char *q;u16 tmp=0,tcpstatus=0;HAL_UART_Transmit(&huart1, (const uint8_t*)p, len, 0xffffff); //从4g模块接收到的内容,通过调试串口打印一下switch(mqttTaskState) {case 0: {if((strstr(p,"AT\r\n"))||(strstr(p,"\r\nOK\r\n"))){ //收到模块回应mqttTaskState++;t100msCount=10;}}break;case 1: {if(strstr(p,"OK")){mqttTaskState++;t100msCount=10;}else if(strstr(p,"ERROR")){}}break;case 2: {if(strstr(p,"OK")){mqttTaskState++;t100msCount=50;}else if(strstr(p,"ERROR")){mqttTaskState++;t100msCount=50;}}break;case 3: {q=strstr(p,"+QMTOPEN:");if(q){q=q+9;tmp=strtol(q,&q,10); //连接通道q++;tcpstatus=strtol(q,&q,10); //状态值if(tcpstatus==0){if(tmp==1){//通道1连接成功mqttTaskState++;t100msCount=20;}}}}break;case 4: {if(strstr(p,"+QMTCONN: 1,0,0")){ //mqtt连接成功mqttTaskState++;t100msCount=20;}else if(strstr(p,"ERROR")){}}break;case 5: {if(strstr(p,"+QMTSUB: 1,")){ //订阅完成mqttTaskState++;t100msCount=0;}else if(strstr(p,"ERROR")){}}break;case 6: {if(strstr(p,"ERROR")){ //发送错误,不允许发送mqttTaskState=100;t100msCount=0;}else{if(strstr(p,">")){ //可以开始发送mqttTaskState++;t100msCount=0;}}}break;case 7: {q=strstr(p,"+QMTPUBEX:");if (q) {mqttTaskState=100;t100msCount=0;}}break;}
}
流程就是,
1.先发送"AT"这个指令,等待4g模块上电后回应
2.发送AT+QMTCFG指令配置参数,配置内容是+QMTRECV:收到消息,把消息体的长度也附带输出
3.发送AT+QMTCLOSE=1,关闭通道1,确保是一个新的连接
4.AT+QMTOPEN=1,打开通道1
5.AT+QMTCONN=1,通道1的mqtt连接
6.AT+QMTSUB,连接上了,就订阅主题topic
7.AT+QMTPUBEX,订阅上了,就向这个主题发送一条信息
8.+QMTRECV,收到了自己发的信息
因为订阅了test/topic这个主题,所以向这个主题发送信息,自己也马上接收到了信息,也就完成回环测试
+QMTRECV: 1,1,"test/topic",12,"msg from mcu"
整个过程还是比较简单的。
mqtt的服务器搭建参考上一篇:
(一)腾讯云(debian)上搭建MQTT服务端(mosquitto)
附带Quectel_LTE_Standard(A)系列_MQTT_应用指导_V1.4.pdf:
Quectel_LTE_Standard(A)系列_MQTT_应用指导_V1.4.pdf