下面代碼是隨手寫的,沒有嚴謹測試僅供參考測試
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