Leigh Smith
Leigh Smith

Reputation: 11

Sending and Receiving AT Commands with a NUCLEO-F042K6 to a SIM7600G-H Modem

Let me preface this question with the statement that I am not, by any means, an expert in embedded systems engineering but a humble student trying to learn on the fly for a project that will eventually involve transmitting CAN data. As a proof of concept I have attempted to setup a NUCLEO-F042K6 with an analogue fluid detection sensor that transmits the sensor data using MQTT over a SIM7600G-H 4G HAT. Currently the code is succesflly reading the sensor value, detecting when the modem is initialised, sending an AT command, receiving the "OK" response but then falling over when entering the main loop. Combing through the posts on Stack Overflow has me thinking it has something to do with not timing the transmit and receive functions correctly but I can't seems to figure this out, no matter what I try. The code is as follows:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdio.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc;

UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */
int __io_putchar(int ch)
{
  HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
  return ch;
}
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
const char apn[]  = "telstra.internet"; // Change this to your Provider details
const char host[] = "tcp://test.mosquitto.org"; // Change this to your host
const int  port = 1883;
const char username[] = "";
const char password[] = "";
const char topic[] = "pot/adc/1";
const uint32_t timeOut = 10000;
char ATcommand[80];
uint8_t buffer[160] = {0};
uint8_t ATisOK = 0;
uint8_t CGREGisOK = 0;
uint32_t previousTick;
uint16_t readValue;
char charData[15];

void SIMTransmit(char *cmd)
{
  memset(buffer, 0, sizeof(buffer)); //Clears the buffer by setting all of its bytes to 0.
  HAL_UART_Transmit(&huart2, (uint8_t *)cmd, strlen(cmd), HAL_MAX_DELAY); //Sends the AT command (pointed to by cmd) to the SIM7600 modem via the UART2 interface.
  printf("Sending command: %s\n", cmd); //Prints the command that was sent to the modem,
  HAL_UART_Receive(&huart2, buffer, 100, 1000); //Waits to receive a response from the modem and stores it in the buffer.
  printf("Received response: %s\n", buffer); //Prints the response received from the modem
}

void waitForModemInitialization(void)
{
    printf("Waiting for modem initialization (PB DONE)...\n");
    int modemReady = 0;

    while (!modemReady)
    {
        // Receive modem output
        HAL_UART_Receive(&huart2, buffer, sizeof(buffer), 1000);
        printf("Received line: %s\n", buffer);

        // Check for "PB DONE" message
        if (strstr((char *)buffer, "PB DONE"))
        {
            modemReady = 1;
            printf("Modem initialized (PB DONE).\n");
        }
        HAL_Delay(1000);
    }

    // After modem is ready, send the AT command and wait for "OK"
    int ATisOK = 0;
    while (!ATisOK)
    {
        SIMTransmit("AT\r\n");
        HAL_Delay(1000);

        if (strstr((char *)buffer, "OK"))
        {
            ATisOK = 1;
            printf("AT command OK.\n");
        }
        else
        {
            printf("Waiting for AT command response. Buffer: %s\n", buffer);
        }
    }
}

void mqttPublish(void)
{
  ATisOK = 0;
  CGREGisOK = 0;

  // Check for OK response for AT
  previousTick = HAL_GetTick();
  while (!ATisOK && previousTick + timeOut > HAL_GetTick())
  {
    SIMTransmit("AT\r\n");
    HAL_Delay(1000);
    if (strstr((char *)buffer, "OK"))
    {
      ATisOK = 1;
      printf("AT command OK.\n");
    }
    else
    {
      printf("AT command failed. Buffer: %s\n", buffer);
    }
  }

  // Check for network registration.
  if (ATisOK)
  {
    previousTick = HAL_GetTick();
    while (!CGREGisOK && previousTick + timeOut > HAL_GetTick())
    {
      SIMTransmit("AT+CGREG?\r\n");
      if (strstr((char *)buffer, "+CGREG: 0,1")) // Use 0,5 For Roaming
      {
        CGREGisOK = 1;
        printf("Network registration successful.\n");
      }
      else
      {
        printf("Network registration failed. Buffer: %s\n", buffer);
      }
    }
  }

  // If registered
  if (CGREGisOK)
  {
    printf("Proceeding with MQTT connection and publish...\n");

    sprintf(ATcommand, "AT+CGSOCKCONT=1,\"IP\",\"%s\"\r\n", apn); // Specify the value of PDP context
    SIMTransmit(ATcommand);
    SIMTransmit("AT+CMQTTSTART\r\n"); // Start MQTT Service
    SIMTransmit("AT+CMQTTACCQ=0,\"client01\"\r\n"); // Acquire a Client
    sprintf(ATcommand, "AT+CMQTTCONNECT=0,\"%s:%d\",60,1\r\n", host, port); // Connect to a MQTT Server
    SIMTransmit(ATcommand);
    sprintf(ATcommand, "AT+CMQTTTOPIC=0,%d\r\n", strlen(topic)); // Set the topic for publish message
    SIMTransmit(ATcommand);
    sprintf(ATcommand, "%s\r\n", topic);
    SIMTransmit(ATcommand);
    sprintf(ATcommand, "AT+CMQTTPAYLOAD=0,%d\r\n", strlen(charData) - 2); // Set the payload
    SIMTransmit(ATcommand);
    SIMTransmit(charData);
    SIMTransmit("AT+CMQTTPUB=0,1,60\r\n"); // Publish
    SIMTransmit("AT+CMQTTDISC=0,120\r\n"); // Disconnect from Server
    SIMTransmit("AT+CMQTTREL=0\r\n"); // Release the Client
    SIMTransmit("AT+CMQTTSTOP\r\n"); // Stop MQTT Service

    printf("MQTT publish process completed.\n");
  }
  else
  {
    printf("MQTT publish failed: no network registration.\n");
  }
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_ADC_Start(&hadc);
  printf("Initialization complete, entering main loop.\n");

  // Wait for modem to initialize
  waitForModemInitialization();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    HAL_ADC_PollForConversion(&hadc, 1000);
    readValue = HAL_ADC_GetValue(&hadc);
    sprintf(charData, "%d\r\n", readValue);
    printf("ADC Value: %d\n", readValue);
    mqttPublish();
    HAL_Delay(1000); // Delay to avoid spamming MQTT broker
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSI14;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSI14State = RCC_HSI14_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.HSI14CalibrationValue = 16;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief ADC Initialization Function
  * @param None
  * @retval None
  */
static void MX_ADC_Init(void)
{

  /* USER CODE BEGIN ADC_Init 0 */

  /* USER CODE END ADC_Init 0 */

  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC_Init 1 */

  /* USER CODE END ADC_Init 1 */

  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
  */
  hadc.Instance = ADC1;
  hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  hadc.Init.Resolution = ADC_RESOLUTION_12B;
  hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
  hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc.Init.LowPowerAutoWait = DISABLE;
  hadc.Init.LowPowerAutoPowerOff = DISABLE;
  hadc.Init.ContinuousConvMode = ENABLE;
  hadc.Init.DiscontinuousConvMode = DISABLE;
  hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc.Init.DMAContinuousRequests = DISABLE;
  hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  if (HAL_ADC_Init(&hadc) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure for the selected ADC regular channel to be converted.
  */
  sConfig.Channel = ADC_CHANNEL_8;
  sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
  sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC_Init 2 */

  /* USER CODE END ADC_Init 2 */

}

/**
  * @brief USART2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{

  /* USER CODE BEGIN USART2_Init 0 */

  /* USER CODE END USART2_Init 0 */

  /* USER CODE BEGIN USART2_Init 1 */

  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART2_Init 2 */

  /* USER CODE END USART2_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

The serial output is as follows:

Received line:
Received line:
Received line:
Received line:
RDY

+CPIN: READY

SMS DONE

PB DONE

Modem initialized (PB DONE).
                            AT
Sending command: AT

Received response: AT
OK

AT command OK.
              ADC Value: 0
                          AT
Sending command: AT

Received response: A
                    AT command failed. Buffer: A
                                                AT
Sending command: AT

Received response: A
                    AT command failed. Buffer: A
                                                AT
Sending command: AT

Received response: A
                    AT command failed. Buffer: A
                                                AT
Sending command: AT

Received response: A
                    AT command failed. Buffer: A
                                                AT
Sending command: AT

Received response: A
                    AT command failed. Buffer: A
                                                AT
Sending command: AT

Received response: A
                    AT command failed. Buffer: A
                                                AT
Sending command: AT

Received response:
                   AT command failed. Buffer:
                                              AT
Sending command: AT

Received response:
                   AT command failed. Buffer:
                                              MQTT publish failed: no network registration.
                                                                                           ADC Value: 0

There is likely a lot wrong here so I do not expect anyone to give a line-by-line analysis but a point in the right direction would be very much appreciated.

This is explained in detail above.


I have added the ATE0 command as suggested @hcheung and the serial output is as follows:

Received line: R
                                                                                
Received line: R
                                                                                                
Received line: R
               Received line:
RDY

+CPIN: READY

SMS DONE

PB DONE

Modem initialized (PB DONE).
                            ATE0
Sending command: ATE0

Received response: ATE0
OK

AT
Sending command: AT

Received response:
OK

AT command OK.
              ADC Value: 0
                          AT
Sending command: AT

Received response:
ERROR

AT command failed. Buffer:
ERROR

AT
Sending command: AT

Received response:
ERROR

AT command failed. Buffer:
ERROR

The relevant code is below. As you can see the modem is successfully receiving and executing AT commands until it moves into 'mqttPublish(void)' function.

    void SIMTransmit(char *cmd)
{
  memset(buffer, 0, sizeof(buffer)); //Clears the buffer by setting all of its 
bytes to 0.
  HAL_UART_Transmit(&huart2, (uint8_t *)cmd, strlen(cmd), HAL_MAX_DELAY); 
//Sends the AT command (pointed to by cmd) to the SIM7600 modem via the UART2 
interface.
  printf("Sending command: %s\n", cmd); //Prints the command that was sent to 
the modem,
  HAL_UART_Receive(&huart2, buffer, 100, 1000); //Waits to receive a response 
from the modem and stores it in the buffer.
  printf("Received response: %s\n", buffer); //Prints the response received 
from the modem
}

void waitForModemInitialization(void)
{
    printf("Waiting for modem initialization (PB DONE)...\n");
    int modemReady = 0;

    while (!modemReady)
    {
        // Receive modem output
        HAL_UART_Receive(&huart2, buffer, sizeof(buffer), 1000);
        printf("Received line: %s\n", buffer);

        // Check for "PB DONE" message
        if (strstr((char *)buffer, "PB DONE"))
        {
            modemReady = 1;
            printf("Modem initialized (PB DONE).\n");
        }
        HAL_Delay(1000);
    }

    // Turn off echo
    SIMTransmit("ATE0\r\n");

    // After modem is ready, send the AT command and wait for "OK"
    int ATisOK = 0;
    while (!ATisOK)
    {
        SIMTransmit("AT\r\n");
        HAL_Delay(1000);

        if (strstr((char *)buffer, "OK"))
        {
            ATisOK = 1;
            printf("AT command OK.\n");
        }
        else
        {
            printf("Waiting for AT command response. Buffer: %s\n", buffer);
        }
    }
}

void mqttPublish(void)
{
  ATisOK = 0;
  CGREGisOK = 0;

  // Check for OK response for AT
  previousTick = HAL_GetTick();
  while (!ATisOK && previousTick + timeOut > HAL_GetTick())
  {
    SIMTransmit("AT\r\n");
    HAL_Delay(1000);
    if (strstr((char *)buffer, "OK"))
    {
      ATisOK = 1;
      printf("AT command OK.\n");
    }
    else
    {
      printf("AT command failed. Buffer: %s\n", buffer);
    }
  }

  // Check for network registration.
  if (ATisOK)
  {
    previousTick = HAL_GetTick();
    while (!CGREGisOK && previousTick + timeOut > HAL_GetTick())
    {
      SIMTransmit("AT+CGREG?\r\n");
      if (strstr((char *)buffer, "+CGREG: 0,1")) // Use 0,5 For Roaming
      {
        CGREGisOK = 1;
        printf("Network registration successful.\n");
      }
      else
      {
        printf("Network registration failed. Buffer: %s\n", buffer);
      }
    }
  }

  // If registered
  if (CGREGisOK)
  {
    printf("Proceeding with MQTT connection and publish...\n");

    sprintf(ATcommand, "AT+CGSOCKCONT=1,\"IP\",\"%s\"\r\n", apn); // Specify 
the value of PDP context
    SIMTransmit(ATcommand);
    SIMTransmit("AT+CMQTTSTART\r\n"); // Start MQTT Service
    SIMTransmit("AT+CMQTTACCQ=0,\"client01\"\r\n"); // Acquire a Client
    sprintf(ATcommand, "AT+CMQTTCONNECT=0,\"%s:%d\",60,1\r\n", host, port); // 
Connect to a MQTT Server
    SIMTransmit(ATcommand);
    sprintf(ATcommand, "AT+CMQTTTOPIC=0,%d\r\n", strlen(topic)); // Set the 
topic for publish message
    SIMTransmit(ATcommand);
    sprintf(ATcommand, "%s\r\n", topic);
    SIMTransmit(ATcommand);
    sprintf(ATcommand, "AT+CMQTTPAYLOAD=0,%d\r\n", strlen(charData) - 2); // 
Set the payload
    SIMTransmit(ATcommand);
    SIMTransmit(charData);
    SIMTransmit("AT+CMQTTPUB=0,1,60\r\n"); // Publish
    SIMTransmit("AT+CMQTTDISC=0,120\r\n"); // Disconnect from Server
    SIMTransmit("AT+CMQTTREL=0\r\n"); // Release the Client
    SIMTransmit("AT+CMQTTSTOP\r\n"); // Stop MQTT Service

    printf("MQTT publish process completed.\n");
  }
  else
  {
    printf("MQTT publish failed: no network registration.\n");
  }
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration-------------------------------------------------------- 
  */

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. 
  */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_ADC_Start(&hadc);
  printf("Initialization complete, entering main loop.\n");

  // Wait for modem to initialize
  waitForModemInitialization();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    //HAL_ADC_PollForConversion(&hadc, 1000);
    //readValue = HAL_ADC_GetValue(&hadc);
    sprintf(charData, "%d\r\n", readValue);
    printf("ADC Value: %d\n", readValue);
    mqttPublish();
    HAL_Delay(1000); // Delay to avoid spamming MQTT broker
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

Upvotes: 1

Views: 133

Answers (0)

Related Questions