下面代码是随手写的,没有严谨测试仅供参考测试
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