本帖最后由 龙鳞铁碎牙 于 2025-8-7 18:43 编辑
#申请开发板# #每日话题# #申请原创# 上一节我已经讲解了如何读取NUCLEO-U385RG的板子温度值,然后通过串口打印出来,就此机会,我将采用ESP8266连接NUCLEO-U385RG,通过MQTT协议将温度值上传到MQTT服务器上。
它的工作原理就是运用到了互联网IOT的MQTT协议,使用MQTT协议,只要设备和手机都在联网的情况下,实现无距离全世界控制读取数据。
1.打开cubumx工程
上面是调试串口1
2.设置ESP8266串口5
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 */
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 ;
}
}
}
编译烧录代码
先打开串口助手,查看发送数据
打开MQTT.fx软件
可以看到,温度值已经上传到MQTT服务器了
最后,打开手机APP。
也能查看到NUCLEO-U385RG的板子温度值。
非常的直观,方便!!!!!
|