[STM32U3] 【STM32U385RG 测评】——8.将NUCLEO-U385RG的板子温度值上传到MQTT服务器

[复制链接]
 楼主| 龙鳞铁碎牙 发表于 2025-8-7 18:32 | 显示全部楼层 |阅读模式
本帖最后由 龙鳞铁碎牙 于 2025-8-7 18:43 编辑

#申请开发板# #每日话题# #申请原创# 上一节我已经讲解了如何读取NUCLEO-U385RG的板子温度值,然后通过串口打印出来,就此机会,我将采用ESP8266连接NUCLEO-U385RG,通过MQTT协议将温度值上传到MQTT服务器上。
它的工作原理就是运用到了互联网IOT的MQTT协议,使用MQTT协议,只要设备和手机都在联网的情况下,实现无距离全世界控制读取数据。
29749689480c47b4e2.png

1.打开cubumx工程

上面是调试串口1
2.设置ESP8266串口5
98506894810310744.png
3.核心代码如下:
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 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"
#include "adc.h"
#include "icache.h"
#include "usart.h"
#include "gpio.h"

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

/* USER CODE END Includes */

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

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* Definitions of environment analog values */
  /* Value of analog reference voltage (Vref+), connected to analog voltage   */
  /* supply Vdda (unit: mV).                                                  */
  #define VDDA_APPLI                       (3300UL)

/* Definitions of data related to this example */
  /* Init variable out of expected ADC conversion data range */
  #define VAR_CONVERTED_DATA_INIT_VALUE    (__LL_ADC_DIGITAL_SCALE(LL_ADC_RESOLUTION_12B) + 1)

/* USER CODE END PD */

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

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */


/* USER CODE BEGIN PV */

/* Variables for ADC conversion data */
__IO uint16_t uhADCxConvertedData = VAR_CONVERTED_DATA_INIT_VALUE; /* ADC group regular conversion data */

/* Variables for ADC conversion data computation to physical values */
__IO  int16_t hADCxConvertedData_Temperature_DegreeCelsius = 0UL;  /* Value of temperature calculated from ADC conversion data (unit: degree Celsius) */
__IO  int16_t hADCxConvertedData_Temperature = 0UL;

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

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

  /* USER CODE BEGIN 1 */
  uint16_t time = 0;

  /* 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_ADC1_Init();
  MX_ICACHE_Init();
  MX_UART5_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  /* Perform ADC calibration */
  if (HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED) != HAL_OK)
  {
    /* Calibration Error */
    Error_Handler();
  }

  ESP8266_Init();




  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {

    /* Init variable containing ADC conversion data */
    uhADCxConvertedData = VAR_CONVERTED_DATA_INIT_VALUE;

    /* Start ADC group regular conversion */
    if (HAL_ADC_Start(&hadc1) != HAL_OK)
    {
      /* Error: ADC conversion start could not be performed */
      Error_Handler();
    }

    /* Poll for conversion completion */
    if (HAL_ADC_PollForConversion(&hadc1, 10) != HAL_OK)
    {
      /* Error: ADC conversion not completed set on time */
      Error_Handler();
    }

    /* Retrieve ADC conversion data */
    /* (data scale corresponds to ADC resolution: 12 bits) */
    uhADCxConvertedData = HAL_ADC_GetValue(&hadc1);

    /* Turn LED on after ADC conversion completion */
    HAL_Delay(200);
        //printf("Sensor: Adc = %u\r\n", uhADCxConvertedData);

    /* Computation of ADC conversions raw data to physical values             */
    /* using LL ADC driver helper macro.                                      */
    hADCxConvertedData_Temperature_DegreeCelsius = __LL_ADC_CALC_TEMPERATURE(VDDA_APPLI, uhADCxConvertedData, LL_ADC_RESOLUTION_12B);
        hADCxConvertedData_Temperature = hADCxConvertedData_Temperature_DegreeCelsius+15;

        printf("STM32U385板子温度是: %d℃\r\n", hADCxConvertedData_Temperature_DegreeCelsius);
        MQTT_PubUint16("STM32U385-Temperature", hADCxConvertedData_Temperature_DegreeCelsius);
        //HAL_Delay(100);
        

    /* 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};

  /** Enable Epod Booster
  */
  if (HAL_RCCEx_EpodBoosterClkConfig(RCC_EPODBOOSTER_SOURCE_MSIS, RCC_EPODBOOSTER_DIV1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_PWREx_EnableEpodBooster() != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure the main internal regulator output voltage
  */
  if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Set Flash latency before increasing MSIS
  */
  __HAL_FLASH_SET_LATENCY(FLASH_LATENCY_2);

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSIS;
  RCC_OscInitStruct.MSISState = RCC_MSI_ON;
  RCC_OscInitStruct.MSISSource = RCC_MSI_RC0;
  RCC_OscInitStruct.MSISDiv = RCC_MSI_DIV1;
  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_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_PCLK3;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSIS;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1;

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

/* 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 */

844526894815a2a7f4.png 40986894816354d8a.png
MQTT核心代码
#include "stm32u3xx.h"
#include "esp8266.h"
#include "usart.h"




uint8_t  esp8266_buf[RXBUFFLENGTH];
uint16_t esp8266_cnt = 0;
uint16_t esp8266_cntPre = 0;

uint8_t rx_data = 0;


void WIFI_Send_Byte(uint8_t ch)
{
    /* 发送一个字节数据到UART */
        HAL_UART_Transmit(UART_ESP8266, (uint8_t *)&ch, 1, 0xFFFF);

}

void WIFI_Send_Bytes(uint8_t *data, uint32_t len)
{
    /* 发送一个字节数据到UART */
        HAL_UART_Transmit(UART_ESP8266, data, len, 0xFFFF);
}

void WIFI_Send_String_Length(uint8_t *str,uint32_t strlen)
{
    uint32_t k=0;
    do
    {
        WIFI_Send_Byte (*(str + k));
        k++;
    } while(k < strlen);
}

void WIFI_Send_String(uint8_t *str)
{
    uint32_t k=0;
    do
    {
        WIFI_Send_Byte (*(str + k));
        k++;
    } while(*(str + k)!='\0');
}


//==========================================================
//        函数名称:        ESP8266_Clear
//
//        函数功能:        清空缓存
//
//        入口参数:        无
//
//        返回参数:        无
//
//        说明:               
//==========================================================
void ESP8266_Clear(void)
{

        memset(esp8266_buf, 0, sizeof(esp8266_buf));
        esp8266_cnt = 0;

}

//==========================================================
//        函数名称:        ESP8266_WaitRecive
//
//        函数功能:        等待接收完成
//
//        入口参数:        无
//
//        返回参数:        REV_OK-接收完成                REV_WAIT-接收超时未完成
//
//        说明:                循环调用检测是否接收完成
//==========================================================
_Bool ESP8266_WaitRecive(void)
{

        if(esp8266_cnt == 0)                                                         //如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数
                return REV_WAIT;
               
        if(esp8266_cnt == esp8266_cntPre)                                //如果上一次的值和这次相同,则说明接收完毕
        {
                esp8266_cnt = 0;                                                        //清0接收计数
                        
                return REV_OK;                                                                //返回接收完成标志
        }
               
        esp8266_cntPre = esp8266_cnt;                                        //置为相同
        
        return REV_WAIT;                                                                //返回接收未完成标志

}

//==========================================================
//        函数名称:        ESP8266_SendCmd
//
//        函数功能:        发送命令
//
//        入口参数:        cmd:命令
//                                res:需要检查的返回指令
//
//        返回参数:        0-成功        1-失败
//
//        说明:               
//==========================================================
_Bool ESP8266_SendCmd(char *cmd, char *res)
{
        
        unsigned char timeOut = 200;

        WIFI_Send_Bytes((uint8_t *)cmd, strlen((uint8_t *)cmd));
        
        while(timeOut--)
        {
                if(ESP8266_WaitRecive() == REV_OK)                                                        //如果收到数据
                {
                        if(strstr((const char *)esp8266_buf, res) != NULL)                //如果检索到关键词
                        {
                            printf("%s\r\n",esp8266_buf);//debug  ---
                                ESP8266_Clear();                                                                        //清空缓存
                                
                                return 0;
                        }
                }
               
                DelayXms(10);
        }
        
        return 1;

}

//==========================================================
//        函数名称:        ESP8266_SendData
//
//        函数功能:        发送数据
//
//        入口参数:        data:数据
//                                len:长度
//
//        返回参数:        无
//
//        说明:               
//==========================================================
void ESP8266_SendData(unsigned char *data, unsigned short len)
{

        char cmdBuf[32];
        
        ESP8266_Clear();                                                                //清空接收缓存
        sprintf(cmdBuf, "AT+CIPSEND=%d\r\n", len);                //发送命令
        if(!ESP8266_SendCmd(cmdBuf, ">"))                                //收到‘>’时可以发送数据
        {               
                WIFI_Send_Bytes(data, len);                 //发送设备连接请求数据
        }

}

//==========================================================
//        函数名称:        ESP8266_GetIPD
//
//        函数功能:        获取平台返回的数据
//
//        入口参数:        等待的时间(乘以10ms)
//
//        返回参数:        平台返回的原始数据
//
//        说明:                不同网络设备返回的格式不同,需要去调试
//                                如ESP8266的返回格式为        "+IPD,x:yyy"        x代表数据长度,yyy是数据内容
//==========================================================
unsigned char *ESP8266_GetIPD(unsigned short timeOut)
{

        char *ptrIPD = NULL;
        
        do
        {
                if(ESP8266_WaitRecive() == REV_OK)                                                                //如果接收完成
                {
                        ptrIPD = strstr((char *)esp8266_buf, "IPD,");                                //搜索“IPD”头
                        if(ptrIPD == NULL)                                                                                        //如果没找到,可能是IPD头的延迟,还是需要等待一会,但不会超过设定的时间
                        {
                                //UsartPrintf(USART_DEBUG, "\"IPD\" not found\r\n");
                        }
                        else
                        {
                                ptrIPD = strchr(ptrIPD, ':');                                                        //找到':'
                                if(ptrIPD != NULL)
                                {
                                        ptrIPD++;
                                        return (unsigned char *)(ptrIPD);
                                }
                                else
                                        return NULL;
                                
                        }
                }
               
                DelayXms(5);                                                                                                        //延时等待
        } while(timeOut--);
        
        return NULL;                                                                                                                //超时还未找到,返回空指针

}

//==========================================================
//        函数名称:        ESP8266_Init
//
//        函数功能:        初始化ESP8266
//
//        入口参数:        无
//
//        返回参数:        无
//
//        说明:               
//==========================================================
void ESP8266_Init(void)
{
        char str[256] = {0};

        ESP8266_Clear();
    HAL_UART_Receive_IT(UART_ESP8266, &rx_data, 1);

        ESP8266_SendCmd("AT+RST\r\n", "OK");
        DelayXms(3000);

        printf("ESP8266 重启\r\n");

        while(ESP8266_SendCmd("AT\r\n", "OK"))
        DelayXms(200);
        printf("ESP8266 测试\r\n");

        while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))//设置工作模式为STA
        DelayXms(200);

        while(ESP8266_SendCmd("AT+CWDHCP=1,1\r\n", "OK"))
        DelayXms(200);

        ESP8266_SendCmd(ESP8266_WIFI_INFO, "OK");
        DelayXms(5000);

        /*网络设备接入Broker*/
/*
        while(ESP8266_SendCmd("AT+MQTTUSERCFG=0,1,\"NULL\",\""ALI_USERNAME"\",\""ALI_PASSWD"\",0,0,\"\"\r\n","OK")){}
        DelayXms(1000);
        // 设置客服id
        while(ESP8266_SendCmd("AT+MQTTCLIENTID=0,\""ALICLIENTLD"\"\r\n","OK")){}
        DelayXms(500);
*/


        while(ESP8266_SendCmd("AT+MQTTUSERCFG=0,1,\"ALICLIENTLD\",\""ALI_USERNAME"\",\""ALI_PASSWD"\",0,0,\"\"\r\n","OK")){}
        DelayXms(1000);


        while(ESP8266_SendCmd("AT+MQTTCONN=0,\""ALI_MQTT_HOSTURL"\",1883,1\r\n","OK")){}
        DelayXms(1000);

        MQTT_Sub(MQTT_TOPIC_DEVICE_RECV, 0);

}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart == UART_ESP8266) {
        ESP8266_RxCallBack();
    }
}

//==========================================================
//        函数名称:        USART2_IRQHandler
//
//        函数功能:        串口2收发中断
//
//        入口参数:        无
//
//        返回参数:        无
//
//        说明:               
//==========================================================
void ESP8266_RxCallBack(void)
{
        if(esp8266_cnt >= sizeof(esp8266_buf))        esp8266_cnt = 0; //防止串口被刷爆
        esp8266_buf[esp8266_cnt++] = rx_data;
        HAL_UART_Receive_IT(UART_ESP8266, &rx_data, 1);

}




char str_send[1024];

//Publish MQTT message in string
void MQTT_Pub(char *topic, char *payload)
{
        sprintf(str_send, "AT+MQTTPUB=0,\"%s\",\"%s\",0,0\r\n", topic, payload);
        WIFI_Send_Bytes((uint8_t *)str_send, strlen(str_send));
        //HAL_Delay(500);
        printf("publish  %s end\r\n", str_send);
}

void MQTT_PubUint8(char *topic, uint8_t payload)
{
    char str[32] = {0};
    sprintf(str, "%u", payload);
    MQTT_Pub(topic, str);
}

void MQTT_PubUint16(char *topic, uint16_t payload)
{
    char str[32] = {0};
    sprintf(str, "%u", payload);
    MQTT_Pub(topic, str);
}

void MQTT_PubUint32(char *topic, uint32_t payload)
{
    char str[32] = {0};
    sprintf(str, "%lu", payload);
    MQTT_Pub(topic, str);
}

void MQTT_PubFloat(char *topic, float payload)
{
    char str[32] = {0};
    sprintf(str, "%f", payload);
    MQTT_Pub(topic, str);
}

void MQTT_PubDouble(char *topic, double payload)
{
    char str[32] = {0};
    sprintf(str, "%f", payload);
    MQTT_Pub(topic, str);
}

void MQTT_Sub(char *topic, uint8_t Qos)
{
        char str[256];
        sprintf(str, "AT+MQTTSUB=0,\"%s\",%d\r\n", topic, Qos);
        //printf("\r\nsubscribe topic: %s\r\n", topic);
        WIFI_Send_Bytes((uint8_t *)str, strlen(str));
}

void MQTT_UnSub(char *topic)
{
        char str[256];
        sprintf(str, "AT+MQTTUNSUB=0,\"%s\"\r\n", topic);
        WIFI_Send_Bytes((uint8_t *)str, strlen(str));
}

//Close the MQTT Connection
void MQTT_Close(char *topic)
{
        WIFI_Send_Bytes((uint8_t *)"AT+MQTTCLEAN=0\r\n", 16);
}





mqtt_recv_t mqtt_recv = {
        .len = 0,
        .json = {0},
};


/*接受到broker数据后对数据的处理函数*/
void sys_data_recv(void)
{
        printf("recv :%s\r\n", esp8266_buf);
        if (strstr((const char *)esp8266_buf, MQTT_TOPIC_DEVICE_RECV))
    {
                printf("recv :%s\r\n", esp8266_buf);
                /*获取发来的信息*/
                char msg[256];
                /*获取json字符串*/
                sscanf((const char *)esp8266_buf, "+MQTTSUBRECV:0,\"iotled\",%d,%s", \
                        &mqtt_recv.len, mqtt_recv.json);
            printf("mqtt_recv.len :%d\r\n", mqtt_recv.len);

                if (strstr(mqtt_recv.json, "led1=1"))
                {
                   HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
                  
                   printf("led1 on\r\n");
                   return ;
                }
                if (strstr(mqtt_recv.json, "led1=0"))
                {
                   HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
                   printf("led1 off\r\n");
                   return ;
                }
                if (strstr(mqtt_recv.json, "led=1"))
                {
                   HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
                   printf("led on\r\n");
                   return ;
                }
                if (strstr(mqtt_recv.json, "led=0"))
                {
                   HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
                   printf("led off\r\n");
                   return ;
                }


        }
}

编译烧录代码

753866894818f43b95.png
先打开串口助手,查看发送数据
91067689481b74fcf7.png
打开MQTT.fx软件
80219689481f7862a0.png
可以看到,温度值已经上传到MQTT服务器了
936068948225b7d39.png


最后,打开手机APP。
也能查看到NUCLEO-U385RG的板子温度值。
58597689483268f61e.jpg
非常的直观,方便!!!!!
69557689480d21e437.png
74121689480e460feb.png
您需要登录后才可以回帖 登录 | 注册

本版积分规则

17

主题

47

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部

17

主题

47

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部