Publishing MQTT message using AT commands

I have a cellular modem connected to my micro-controller that uses AT commands for setting up its network and publishing MQTT messages to a remote broker server. But doing so requires a sequence of about a dozen or so AT commands. I will post them below. Many of these codes are modem specific, but each distinct command terminates with \r\n when using a serial connection to the microcontroller.

After all of the configuration is done PDP context, SSL, MQTT, credentials the message can be published to the remote broker server. Unlike the other AT commands though the manual says enter the publish command which looks something like this to me:

   //configure MQTT message AT+QMTPUB=<client_idx>,<msgID>,<qos>,<retain>,<topic>
  Serial2.write("AT+QMTPUBEX=1,0,0,0,\"motorbeta\"\r\n");

then the command line moves to a new line. Then enter the MQTT message and press control+Z to send. I have verified this works when connected serially to the modem from my computer and using the modem manufacturer's serial connection software to send the AT commands. Their route does work inside their software (which doesn't require \r\n\ at the end of each command from the user).

But what about when my microcontroller is issuing the AT command? How can I replicate control+Z?

So far all of my code works except when it's time to publish a MQTT message I just get "ERROR". I can publish when I connect to the modem directly and follow the manual's steps but just not with the micro-controller. Does anyone have any ideas on how to get this working?

  //checking SIM
  Serial2.write("AT+CPIN?\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char sim = Serial2.read();
    Serial.print(sim);
  }
  delay(2000);

  //"configure a PDP context" AT+QICSGP=<contextID>[,<context_type>,<APN>[,<username>,<password>[,<authentication>]]]
  Serial2.write("AT+QICSGP=1,2,\"carrier\",\"\",\"\",0\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char pdpcontext = Serial2.read();
    Serial.print(pdpcontext); 
  }
  delay(2000);

  //PDP activation
  Serial2.write("AT+QIACT=1\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char pdp = Serial2.read();
    Serial.print(pdp);
  }
  delay(15000);

  //check service
  Serial2.write("AT+CEREG?\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char service = Serial2.read();
    Serial.print(service);
  }
  delay(15000);

  //"TLS 1.2" AT+QSSLCFG="sslversion",<SSL_ctxID>[,<SSL_version>]
  Serial2.write("AT+QSSLCFG=\"sslversion\",1,3\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char sslversion = Serial2.read();
    Serial.print(sslversion);
  }
  delay(2000); 

  //AT+QSSLCFG="ciphersuite",<SSL_ctxID>[,<cipher_suites>] support all cipher suits
  Serial2.write("AT+QSSLCFG=\"ciphersuite\",1,0XFFFF\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char cipher = Serial2.read();
    Serial.print(cipher);
  }
  delay(2000);

  //AT+QSSLCFG="cacert",<SSL_ctxID>[,<cacertpath >]
  Serial2.write("AT+QSSLCFG=\"cacert\",1,\"ca.crt\"\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char cacrt = Serial2.read();
    Serial.print(cacrt);
  }
  delay(2000);

  //"server authentication only" AT+QSSLCFG="seclevel",<SSL_ctxID>[,<seclevel>]
  Serial2.write("AT+QSSLCFG=\"seclevel\",1,1\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char sslsecurity = Serial2.read();
    Serial.print(sslsecurity);
  }
  delay(2000);

  //AT+QSSLCFG="ignorelocaltime",<SSL_ctxID>[,<ignore_localtime>]
  Serial2.write("AT+QSSLCFG=\"ignorelocaltime\",1,1\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char sslignoretime = Serial2.read();
    Serial.print(sslignoretime);
  }
  delay(2000);

  //configure to MQTT protocol 3.1.1 AT+QMTCFG="version",<client_idx>[,<vsn>]
  Serial2.write("AT+QMTCFG=\"version\",1,4\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char mqttversion = Serial2.read();
    Serial.print(mqttversion);
  }
  delay(2000);

  //AT+QMTCFG="pdpcid",<client_idx>[,<cid>]
  Serial2.write("AT+QMTCFG=\"pdpcid\",1,1\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char mqttpdp = Serial2.read();
    Serial.print(mqttpdp);
  }
  delay(2000);

  //AT+QMTCFG="ssl",<client_idx>[,<SSL_enable>[,<ctx_index>]]
  Serial2.write("AT+QMTCFG=\"ssl\",1,1,1\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char mqttssl = Serial2.read();
    Serial.print(mqttssl);
  }
  delay(2000);

  //AT+QMTCFG="keepalive",<client_idx>[,<keep_alive_time>]
  Serial2.write("AT+QMTCFG=\"keepalive\",1,3600\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char mqttalive = Serial2.read();
    Serial.print(mqttalive);
  }
  delay(2000);

  //AT+QMTCFG="session",<client_idx>[,<clean_session>]
  Serial2.write("AT+QMTCFG=\"session\",1,1\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char mqttclean = Serial2.read();
    Serial.print(mqttclean);
  }
  delay(2000);

  
  Serial2.write("AT+QMTOPEN=1,\"server address\",8883\r\n"); //"This command opens a network for MQTT client." AT+QMTOPEN=<client_idx>,<host_name>,<port>
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char mqttopen = Serial2.read();
    Serial.print(mqttopen);
  }
  delay(10000);

    Serial2.write("AT+QMTOPEN?\r\n"); //"This command opens a network for MQTT client." AT+QMTOPEN=<client_idx>,<host_name>,<port>
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char mqttopenyn = Serial2.read();
    Serial.print(mqttopenyn);
  }
  delay(2000);

  //AT+QMTCONN=<client_idx>,<clientID>[,<username>[,<password>]]
  Serial2.write("AT+QMTCONN=1,\"test\",\"user\",\"password\"\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char mqttcredentials = Serial2.read();
    Serial.print(mqttcredentials);
  }
  delay(2000);

   //configure MQTT message AT+QMTPUB=<client_idx>,<msgID>,<qos>,<retain>,<topic>
  Serial2.write("AT+QMTPUBEX=1,0,0,0,\"topic\"\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char mqttpublishtopic = Serial2.read();
    Serial.print(mqttpublishtopic);
  }
  delay(2000);

   //publish MQTT message
  Serial2.write("\"test\"\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char mqttmessage = Serial2.read();
    Serial.print(mqttmessage);
  }
  delay(2000);


  
  //disconnect from MQTT server
  Serial2.write("AT+QMTDISC=1\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char mqttserverdisconnect = Serial2.read();
    Serial.print(mqttserverdisconnect);
  }
  delay(2000);

  //disconnect from MQTT network
  Serial2.write("AT+QMTCLOSE=1\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char mqttnetworkdisconnect = Serial2.read();
    Serial.print(mqttnetworkdisconnect);
  }
  delay(5000);

  //close SSL connection
  Serial2.write("AT+QSSLCLOSE=1,10?\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char closessl = Serial2.read();
    Serial.print(closessl);
  }
  delay(2000);

  //deactivate PDP context
  Serial2.write("AT+QIDEACT=1\r\n");
  while (!Serial2.available()) continue;
  while (Serial2.available()) {
    char pdpdisconnect = Serial2.read();
    Serial.print(pdpdisconnect);
  }
  delay(60000);

delay(100);
 // }
}

Write your self a little program that reads characters from the Serial Monitor and prints their numeric value back to the Serial Monitor. Run the program and type control+Z then 'enter'. That should tell you the numeric ASCII code for control+Z.

Just tried it. Interestingly, ctrl-Z (and others) don't send anything when using standard Arduino Serial Monitor. But, it works find with PuTTY.

Yea I tried to send Ctr-Z over arduino serial monitor and got the result you mentioned. Now I am looking at this link: other example and they are using:

 void SendMessage()
{
  Serial.println ("Sending Message");
  SIM900A.println("AT+CMGF=1");    //Sets the GSM Module in Text Mode
  delay(1000);
  Serial.println ("Set SMS Number");
  SIM900A.println("AT+CMGS=\"+6281542787536\"\r"); //Mobile phone number to send message
  delay(1000);
  Serial.println ("Set SMS Content");
  SIM900A.println("Good morning, how are you doing?");// Messsage content
  delay(100);
  Serial.println ("Finish");
  SIM900A.println((char)26);// ASCII code of CTRL+Z
  delay(1000);
  Serial.println ("Message has been sent ->SMS Selesai dikirim");
}

I think this may work

Hello man, I was interested in this project since I am doing something similar but I am complete beginner so I wanted to ask you if you could send me the whole code of yours? So i know how to send the commands properly.

This is the function that I use to send AT commands and read the feedback. It is difficult for me to send entire code as it is full of ip addresses and other stuff. The function seems to work good for my use case.

//AT command sequences
void atsendbasic(String command, int fallout3, int fallout4){
   unsigned long initialtime4 = millis();
   Serial2.print(command);
   Serial2.flush();  // Wait until it is all sent
    while ((!Serial2.available() && millis() - initialtime4 <= fallout3) || millis() - initialtime4 <= fallout4) continue;
    while ((Serial2.available()&& millis() - initialtime4 <= fallout3) || millis() - initialtime4 <= fallout4) {
      char feedback = Serial2.read();
      Serial.print(feedback);
    }
}

But I will send a command by calling this function like so:

 Serial.println("connecting to MQTT broker");
 //"This command opens a network for MQTT client." AT+QMTOPEN=<client_idx>,<host_name>,<port>
 atsendbasic("AT+QMTOPEN=1,\"yourserverip\",8883\r\n",60000,5000);
 delay(500);

Yeah I understand that. Thank you very kindly for this function tho. I have to send AT commands via Arduino to Quectel modul and use MQTT for data transfer in the end so this thread is very helpful. Also what exactly is Serial2? Is that a Software serial or what communicatiopn exactly is that.

Ah I see, it is very relevant then. I am using Quectel BG95 module. To answer your question about Serial2, you will need two serial connections. The first will be for display purposes, so you know what's going on, the second will be for sending commands back and forth. So in my case the main serial connection is the connection used for firmware upload. The Serial2 is a hardwired connection to different TXD and RXD gpios

Serial.begin(115200);  // Open main serial port
Serial2.begin(115200,SERIAL_8N1); //open modem serial port

Thank you for explanation. The problem is that I need to send AT commands via Arduino UNO to Quectel BC68 and I have only one Serial so I am using Software serial but I have already tried your function at it is working. Will also try something similar with the MQTT publish since I need to input Ctrl+Z as well. Anyways thank you once again.

Check out the QMTPUBEX command at least for me it does not require the ctr+z

publishdata("AT+QMTPUBEX=1,0,0,0,\"motorbeta\",\"{\"temperature\":" + String(temperature) + ",\"pressure\":" + String(pressure) + "}\"\r\n");

There are two main publish mqtt commands with my model.
image

image

I always use the latter the first I believe will require ctr+z. Also if you connect directly to the quectel module using their software tool (Qcom or Qflash can't remember the name) you will need to do the ctr+z

I think that BC68 does not have the second command but I could be wrong. Will try it.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.