cancel
Showing results for 
Search instead for 
Did you mean: 

Sending AT commands to GSM via STM32, Answer with characters omitted

MatheusMarkies
Associate III
I'm developing a system with a GMS module, where AT commands are sent to the module via an STM32L0.
In this case, I'm having problems with the AT+CSTT command, which was giving me an ERROR. After a lot of thought, I realized that the error was due to the fact that the command was going to the module with the T for AT missing (A+CSTT).
 
I'm working with the commands like this:
void sendAT(char command[], char answer[], int waitForAnswer) {
char ATcommand[500];
uint8_t buffer[500] = { 0 };
uint8_t ATisOK = 0;

sprintf(ATcommand, "%s\r\n", command);

    DEBUG_PRINT("Sending AT Command: ");
    DEBUG_PRINT(ATcommand);

while (!ATisOK) {
HAL_UART_Transmit(&huart2, (uint8_t*) ATcommand, strlen(ATcommand),
2000);


HAL_UART_Receive(&huart2, buffer, sizeof(buffer) - 1, 300);
buffer[sizeof(buffer) - 1] = '\0';

HAL_Delay(500);

ATisOK = !waitForAnswer;

if (strstr((char*) buffer, "OK")) {
ATisOK = 1;
}

DEBUG_PRINT("GSM: ");
DEBUG_PRINT((char* ) buffer);
HAL_Delay(1000);
memset(buffer, 0, sizeof(buffer));
}
}

char* sendATWithReturn(char command[]) {
char ATcommand[500];
uint8_t buffer[300] = { 0 };
char *returnBuffer = malloc(300 * sizeof(char));

if (returnBuffer == NULL) {
return NULL;
}

sprintf(ATcommand, "%s\r\n", command);
HAL_UART_Transmit(&huart2, (uint8_t*) ATcommand, strlen(ATcommand), 1000);
HAL_UART_Receive(&huart2, buffer, 300, 100);
HAL_Delay(500);

DEBUG_PRINT("GSM: ");
DEBUG_PRINT((char* ) buffer);

strncpy(returnBuffer, (char*) buffer, 300);

return returnBuffer;
}
And my command sequence goes like this:
void modemStart(char *apn, char *user, char *pwd) {
sendATWithReturn("AT");

HAL_Delay(1000);
sendATWithReturn("AT+CPIN?");

HAL_Delay(1000);
sendATWithReturn("AT+CREG?");

HAL_Delay(1000);
sendATWithReturn("AT+CGATT?");

HAL_Delay(1000);
sendATWithReturn("AT+CSQ");

HAL_Delay(1000);
    char command[500];
    snprintf(command, sizeof(command), "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
    sendAT(command, "OK", 1);
    memset(command, 0, sizeof(command));

    HAL_Delay(1000);
    sendAT("AT+CGATT=1", "OK", 0);
    HAL_Delay(1000);
    sendATWithReturn("AT+CSTT?");

    HAL_Delay(1000);
    snprintf(command, sizeof(command), "AT+CSTT=\"%s\",\"%s\",\"%s\"", apn, user, pwd);
    sendAT(command, "OK", 1);
    memset(command, 0, sizeof(command));

HAL_Delay(1000);
sendATWithReturn("AT+CIICR");

HAL_Delay(1000);
sendATWithReturn("AT+CIFSR");

HAL_Delay(1000);
sendATWithReturn("AT+CIPMUX=1");

/*
AT+CPIN?: Verifica o status do PIN do SIM.
AT+CREG?: Verifica o status de registro na rede.

Sequência de Inicialização:

AT+NETCLOSE: Fecha qualquer conexão de rede existente.
AT+CGATT=0: Desativa o GPRS.
AT+CGATT=1: Ativa o GPRS.
AT+CGDCONT: Define o contexto PDP.
AT+CSTT: Define o APN, usuário e senha.
AT+CIICR: Inicia a conexão sem fio.
AT+CIFSR: Obtém o endereço IP.
AT+CIPMUX=1: Habilita o modo de múltiplas conexões.
*/
}
SERIAL LOG:
19:37:54.671 -> Serial2: Send AT:
19:37:54.671 -> Sending AT Command: AT
19:37:54.671 -> GSM: AT

19:37:54.671 -> OK
19:37:54.671 -> 
19:37:56.282 -> Serial2: GSM: AT

19:37:56.282 -> OK
19:37:56.282 -> 
19:37:57.878 -> Serial2: GSM: AT+CPIN?

19:37:57.878 -> +CPIN: READY
19:37:57.878 -> 
19:37:57.878 -> OK
19:37:57.878 -> 
19:37:59.455 -> Serial2: GSM: AT+CREG?

19:37:59.455 -> +CREG: 0,1
19:37:59.455 -> 
19:37:59.455 -> OK
19:37:59.455 -> 
19:38:01.060 -> Serial2: GSM: AT+CGATT?

19:38:01.060 -> +CGATT: 1
19:38:01.060 -> 
19:38:01.060 -> OK
19:38:01.060 -> 
19:38:04.242 -> Serial2: GSM: AT+CSQ

19:38:04.242 -> +CSQ: 18,0
19:38:04.242 -> 
19:38:04.242 -> OK
19:38:04.242 -> Sending AT Command: AT+CGDCONT=1,"IP","zap.vivo.com.br"
19:38:04.287 -> GSM: AT+CGDCONT=1,"IP","zap.vivo.com.br"

19:38:04.287 -> OK
19:38:04.287 -> 
19:38:06.839 -> Serial2: Sending AT Command: AT+CGATT=1
19:38:06.839 -> GSM: AT+CGATT=1

19:38:06.885 -> OK
19:38:06.885 -> 
19:38:11.069 -> Serial2: GSM: AT+CSTT?

19:38:11.069 -> ERROR
19:38:11.069 -> Sending AT Command: AT+CSTT="zap.vivo.com.br","vivo","vivo"
19:38:11.069 -> GSM: A+CSTT="zap.vivo.com.br","vivo","vivo"

19:38:11.069 -> ERROR
19:38:11.069 -> 
19:38:12.670 -> Serial2: GSM: A+CSTT="zap.vivo.com.br","vivo","vivo"

19:38:12.670 -> ERROR
In other words, the error is when the command is sent to the module, more precisely in the byte buffer conversion line, but I'm not sure how to fix it.
 
Apparently with two characters in the password the command is sent correctly, but with more than two the AT loses its T, I tested it this way: 
 
19:28:09.621 -> Serial2: GSM: AT+CSTT=“zap.vivo.com.br”,“vivo”,"dd”
19:28:09.621 -> ERROR
 
Note that AT+CSTT is correct.
 
Doing some more tests, I realized that the error is in the part of sending and receiving the data by Serial, since even the DEBUG_PRINT that occurs before the while click to send the command is correct.
1 ACCEPTED SOLUTION

Accepted Solutions

@BarryWhit wrote: 

Can you share a link to the AT command manual you're using for this module? None of the ones I've found list AT+CSTT as a valid command. 

 

 

But At+CSTT is a listed command for the much older SIM900 module from the same company, which was used in older shields for arduino.

- If a post has answered your question, please acknowledge the help you received by clicking "Accept as Solution".
- Once you've solved your issue, please consider posting a summary of any additional details you've learned. Your new knowledge may help others in the future.

View solution in original post

13 REPLIES 13
Karl Yamashita
Lead II

Use the insert code sample to format your code.

Code Snippet.jpg

If you find my answers useful, click the accept button so that way others can see the solution.

Learn to use buffers, and interrupts.

The devices operate in Rx/Tx concurrently, not this send-blocking, receive-blocking, arbitrary delays, nonsense.

Cellular modems can have time-outs running into multiple minutes for interactions with the network.

The AT command protocol expects you to wait for a response, AT, ERROR, NO CARRIER, etc and not jam further input until the prior command has completed. It might also send you unsolicited responses like RING, when an inbound call comes in. You need to approach this in a much more stateful manner.

 

 

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
BarryWhit
Senior III

Apparently with two characters in the password the command is sent correctly, but with more than two the AT loses its T, 

You do realize none of us know which GSM modem your using nor its command set, right? which one's "the password" then?

 

 
I tested it this way: 
19:28:09.621 -> Serial2: GSM: AT+CSTT=“zap.vivo.com.br”,“vivo”,"dd”
19:28:09.621 -> ERROR
 
Note that AT+CSTT is correct.
 
Doing some more tests, I realized that the error is in the part of sending and receiving the data by Serial, since even the DEBUG_PRINT that occurs before the while click to send the command is correct.
[...]
After a lot of thought, I realized that the error was due to the fact that the command was going to the module with the T for AT missing (A+CSTT).

What is this "test"? it just looks like you sent a command, and the modem reported an error. How exactly is this a different "test" from the log output you posted?

I don't see any compelling evidence suggesting the issue is a mangled command being transmitted. All you know is that you send the modem a command and it signaled an error. That could be caused by any number of things, including a malformed command, malformed arguments, sending the command when the modem wasn't expecting it, or something else.

 

Use TX-RX Loopback to verify that everything you send comes back without error. Make the wiring of similar length to your modem connection, though that's unlikely to be the issue unless you're running the wires across the room.

 

For typical UART data rates, you can use a cheap 10$ USB logic analyzer to see exactly what goes on the line , in terms of both data and timing. They all come with libsigrok based software that does protocol decoding, including UART.

 

Delorean probably pointed out the issue for you., you need to implement this as a state machine instead of using hardcoded delays (do use response timeouts), you need to decouple RX reception from TX, and you need to condition TX on the modem having responded to the previous command.

 

- If a post has answered your question, please acknowledge the help you received by clicking "Accept as Solution".
- Once you've solved your issue, please consider posting a summary of any additional details you've learned. Your new knowledge may help others in the future.

How could I adapt the interrupts in this case? I tried to analyze the module's response via HAL_UART_RxCpltCallback but I couldn't get the answer.

You need to show that particular code you've tried to get working but failed to. That way we can see how you're trying to communicate to the GSM module and receiving from it. 

If you find my answers useful, click the accept button so that way others can see the solution.
MatheusMarkies
Associate III

@Karl Yamashita 

The problem is with the “HAL_UART_RxCpltCallback” function, which is never called.

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
	if(huart->Instance == huart2.Instance){
		DEBUG_PRINT("OLHA A MENSAGEM!\r\n");
		HAL_UART_Receive_IT(&huart2, buffer, sizeof(buffer) - 1);
		gsmState = 2;
	}
}

uint16_t sendTick = 0;
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
	if(huart->Instance == huart2.Instance){
		DEBUG_PRINT("MENSAGEM ENVIADA!\r\n");
		gsmState = 1;
		sendTick = HAL_GetTick();
	}
}

void sendAT(char command[], char answer[], int waitForAnswer) {
	char ATcommand[200];
	uint8_t commandBuffer[200] = { 0 };

	uint8_t ATisOK = 0;

	snprintf(ATcommand, sizeof(ATcommand), "AT%s\r\n", command);

	memcpy(commandBuffer, (uint8_t*)ATcommand, strlen(ATcommand) + 1);

    DEBUG_PRINT("Sending AT Command: \r\n");
    DEBUG_PRINT(ATcommand);

    while (!ATisOK) {

    	if(gsmState == 0){
    		HAL_UART_Transmit_IT(&huart2, commandBuffer, sizeof(commandBuffer));
    	}

		if(gsmState == 1){
			if(HAL_GetTick() >= sendTick + 2000){
				DEBUG_PRINT("TimeOut\r\n");
				gsmState = 0;
			}
		}

		if(gsmState == 2){
			buffer[sizeof(buffer) - 1] = '\0';

			ATisOK = !waitForAnswer;

			if (strstr((char*) buffer, "OK")) {
				gsmState = 0;
				ATisOK = 1;
			}else{
				gsmState = 0;
			}

			DEBUG_PRINT("GSM: \r\n");
			DEBUG_PRINT((char*) buffer);
			memset(buffer, 0, sizeof(buffer));
		}
		HAL_Delay(1000);
	}
}
10:01:42.225 -> Serial2: Send AT:
10:01:42.225 -> Sending AT Command: 
10:01:42.225 -> AT
10:01:42.225 -> MENSAGEM ENVIADA!
10:01:42.225 -> 
10:01:45.221 -> Serial2: TimeOut
10:01:45.221 -> 
10:01:46.235 -> Serial2: MENSAGEM ENVIADA!
10:01:46.235 -> 
10:01:49.203 -> Serial2: TimeOut
10:01:49.203 -> 
10:01:50.205 -> Serial2: MENSAGEM ENVIADA!
10:01:50.205 -> 

@BarryWhit 

Sorry, I wrote in a hurry and ended up forgetting important details.

Well, the modem I use is a Simcom a7670sa 4g. I used this modem in my Arduino system, where the TinyGSM library did all the heavy lifting. Based on it, I built this sequence for initializing and connecting to the mobile internet network, and then making HTTP requests.

The tests I'm referring to are as follows: I was suspicious that the problem might be memory, so I reduced the number of characters by removing the apn password from the command and sending it to the module. And so the module received the command correctly, but returned the obvious error since the password was wrong.
I'm using a local operator here in my country, called vivo, in which its APN has the credentials:
zap.vivo.com.br
user: vivo
psw: vivo

When the command has all these credentials it returns the omitted character problem.
I then tested putting in one character at a time, and saw that up to 2 characters the command worked, more than that it gave a problem.

Pavel A.
Evangelist III

@MatheusMarkies 

I used this modem in my Arduino system

Arduino serial library behaves differently, it does buffering for you.

Other people who answered noted that your STM32 UART code is a bit naïve, so it isn't obvious that one character is missing in TX. Try to sniff the UART lines with external analyzer to capture the actual data. Try to find a library for "AT" device communication that handles concurrent TX and RX and various 'modem' responses.

The 'TX-RX Loopback' proposed by @BarryWhit  can be helpful as well. This requires two UARTs: one for the modem, another for a 'terminal' running on a PC. The software should simply send bytes received from the modem to the terminal and v.v. as fast as possible without delays. So you type commands on the terminal and get responses from the modem. Unfortunately the Cube library does not have a ready example for this.

 

Did you call HAL_UART_Receive_IT to initialize the interrupt? I only see where you call it inside the callback but not before hand.

Then did you enable the NVIC?

If you find my answers useful, click the accept button so that way others can see the solution.

@Pavel A. wrote:

Try to find a library for "AT" device communication...


@MatheusMarkies  Some examples here:

https://www.avrfreaks.net/s/topic/a5C3l000000UYptEAG/t147703?comment=P-1410260