STM32连接阿里云物联网平台

文章目录

  • 引言
  • 一、STM32连接阿里云物联网平台思路
  • 二、ESP8266烧录固件
  • 三、使用AT指令连接阿里云物联网平台
  • 四、STM32环形串口缓冲区驱动程序
  • 五、STM32连接阿里云驱动程序

引言

连续写了两篇关于阿里云连接的文章,都是使用Arduino ESP8266 & Arduino ESP32的方式:
1、Arduino ESP8266&ESP32使用AliyunIoTSDK.h连接阿里云物联网平台
2、使用ArduinoMqttClient库连接阿里云,并实现发送接收数据(ESP8266)

但是,使用Arduino的方式处理阿里云的数据在简单的电子小制作、毕业设计完全可以胜任,但是Arduino的核心库都是对底层进行二次封装,运行效率难免会低一些。ESP8266与ESP32作为主控的MCU,功能有限,在这种情况下,就会想到使用STM32搭载一个可以联网的芯片来做数据物联网的开发了。
我在企业开发项目中,接触过新能源汽车充电桩,核心是使用STM32F103VET6进行主控,它通过搭载一个4G模块来对云端数据进行处理。设备上线时,后台有上线的提示;管理员可用户都可以查看到充电桩的实时状态,如下图所示,管理员坐在办公室便可以查看到有两个慢充桩处于空闲状态,有一个充电桩已连接车辆,但是并没有开始充电,我们就可以猜测出用户可能刚刚到达,已将插枪连接车辆,正在进行扫码的操作。
请添加图片描述
平台更会报一些简单的故障,如急停故障,充电桩安装的环境多种多样,有时会有小朋友误触到急停故障导致充电桩报故障,不能充电,这时用户打电话给运维的客服,便可以在运维客服的提示下把急停按键复原,充电桩便可以正常使用。
通过物联网技术来对新能源汽车充电桩进行控制大大的减轻的运营的成本,在其他行业如加油站无人值守的自动洗车机、学校教学楼的自动贩卖机等等,都是物联网控制的一个范例,如今物联网控制技术更是影响着我们生活的方方面面。

文章使用基础型STM32F1作为主控芯片,搭载ESP8266模块,使用环形串口缓冲区算法连接阿里云平台,实现数据的订阅和发布。文章有一点点的深度,我尽量把每一个细节每一个步都骤掰开来,嚼碎去详细说明。
有很多例程在数据收发这一块都使用数组进行数据的接收,对付一些简单的物联网开发完全是没有问题的,而环形串口缓冲区算法是在大规模数据接收时使用的,在这么简单的物联网数据收发中使用难免会影响效率。
是的,在这么简单的物联网数据的收发我还使用环形串口缓冲区,影响一定的运行的效率是一定。但是,长期做开发的一定要留有有方便后期升级维护的一个思维,一些升级接口需要多预留一些,产品都不是一次做出来的,是一代一代逐渐完善,逐渐更新换代的。

一、STM32连接阿里云物联网平台思路

连接思路其实很简单,只要两步就好了:
1、烧录MQTT的固件
2、STM32使能串口通过AT指令连接阿里云物联网平台

我使用的是ESP8266作为联网设备,连接阿里云物联网平台,之前我使用Arduino的方式连接阿里云平台也是同样的原理,但是Arduino将一些底层的函数进行了封装,我们只需要学会使用Arduino提供的一些库便可以连接阿里云物联网平台进行数据交换。
而STM32则没有相关的库,我们需要烧录好MQTT的固件,通过电脑串口助手先连接成功阿里云物联网平台,测试好订阅和发布的功能后,连接我们STM32串口,进行简单的串口数据收发。

二、ESP8266烧录固件

连接物联网云平台之前先需要烧录相关固件,固件地址如下:
安信可固件汇总
我选择第4个固件,固件的下载地址附有官方的使用说明。
因为技术更新迭代的速度非常快,可能我的这一篇文章发布几个月或半年后固件便开始更新,但都是换汤不换药,掌握了连接的原理,我们也可以自行查看官方的文档来进行连接,此篇文章可以作为非常重要的参考使用。
在这里插入图片描述
下载完成后便可以烧录固件,烧录固件的软件可以通过官网下载,我这里也提供一个网盘下载连接:
链接:https://pan.baidu.com/s/1H96ZbeG_ogslrJUz2Y162w
提取码:wslw
软件打开后,安装提示很容易就可以将固件烧录进去,下图是我烧录成功的截图界面:
请添加图片描述

三、使用AT指令连接阿里云物联网平台

固件烧录成功之后,首先需要使用电脑的串口助手软件,通过简单的AT指令连接WiFi,连接阿里云,然后完成订阅和发布数据,确认好AT指令代码没有问题后,我们才把相关的AT指令封装起来,写进程序中,连接的AT指令只需要熟悉常用的几个即可。

指令功能
AT测试
AT+RST重启
AT+CWMODE=1设置为无线终端模式,可以连接WiFi
AT+CWJAP=“wifi”,“passwd”连接热点
AT+CWSTATE?查询WiFi连接讲解状态
AT+MQTTUSERCFG=0,1,“clientId”,“username”,“password”,0,0,“”设置MQTT连接参数
AT+MQTTCONN=0,“Broker Address”,1883,1连接阿里云
AT+MQTTCONN?查询MQTT连接状态
AT+MQTTSUB=0,“topic”,0订阅主题
AT+MQTTPUB=0,“topic”,“msg”发布主题

AT指令按照如下通过串口助手发送到ESP8266即可完成阿里云的连接
请添加图片描述
(1)判断ESP8266是否存在
使用指令:AT\r\n
串口回复:OK

备注:使用串口助手和STM32发送指令的时候,结尾加上\r\n,表示指令结束

(2)设置ESP8266为接入点模式
使用指令:AT+CWMODE=1\r\n
串口回复:OK

(3)查询WiFi是否连接成功
使用指令:AT+CWSTATE?
串口回复:

+CWSTATE:2"CMCC-H3qz"	//备注:CMCC-H3qz是我的WiFi名

OK

(4)如果没有连接WiF,则连接WiFi
使用指令:AT+CWJAP=“CMCC-H3qz”,“7xf47uxf”\r\n
备注:CMCC-H3qz是我的WiFi名,7xf47uxf是我的WiFi密码

串口回复:

AT+CWJAP="CMCC-H3qz""7xf47uxf"
WIFI DISCONNECT
WIFI CONNECTED
WIFI GOT IP

OK

(5)设置MQTT参数
使用指令:
AT+MQTTUSERCFG=0,1,“clientId”,“username”,“password”,0,0,“”\r\n
示例:
AT+MQTTUSERCFG=0,1,"k0efkfcSwlt.Device_Demo|securemode=2\,signmethod=hmacsha256\,timestamp=1705149361131|","Device_Demo&k0efkfcSwlt","ea343ac1d6a4eec7b057bcab39a2ffdf88abc8b810ec0dc7ca3de91a56292ff9",0,0,""\r\n
备注:
1、指令中,遇到“,”字符时,使用“\”隔开,避免报错。
2、clientId,username,password这些数据从阿里云物联网平台获取
在这里插入图片描述
串口回复:

AT+MQTTUSERCFG=01"k0efkfcSwlt.Device_Demo|securemode=2\signmethod=hmacsha256\timestamp=1705149361131|""Device_Demo&k0efkfcSwlt""ea343ac1d6a4eec7b057bcab39a2ffdf88abc8b810ec0dc7ca3de91a56292ff9"00""

OK

(6)连接阿里云
使用指令:AT+MQTTCONN=0,“Broker Address”,1883,1\r\n
示例:
AT+MQTTCONN=0,"k0efkfcSwlt.iot-as-mqtt.cn-shanghai.aliyuncs.com",1883,1\r\n
备注:
Broker Address为MQTT域名,阿里云物联网平台开发者文档有详细的解释,我在以前的文章也对这方面进行了详细的说明,大家可以尝试一下使用MQTT.fx连接阿里云平台就能明白MQTT域名的作用了。
附上这部分云文档的地址:
使用MQTT.fx接入物联网平台
在这里插入图片描述
串口回复:

AT+MQTTCONN=0"k0efkfcSwlt.iot-as-mqtt.cn-shanghai.aliyuncs.com"18831
+MQTTCONNECTED:01"k0efkfcSwlt.iot-as-mqtt.cn-shanghai.aliyuncs.com""1883"""1

OK

这时,我们登陆阿里云物联网平台,可以看到设备已成功连接阿里云物联网平台。
在这里插入图片描述
(7)发布数据
使用指令:AT+MQTTPUB=0,“topic”,“msg”\r\n
示例:
AT+MQTTPUB=0,"/sys/k0efkfcSwlt/Device_Demo/thing/event/property/post","{\"params\": {\"temperature\": 10.5}\,\"version\": \"1.0\"}",0,0\r\n
备注:
1、topic从阿里云平台获取
在这里插入图片描述
2、msg有一定的格式,在阿里云平台的开发者文档里也有详细的说明,附上文档连接:
阿里云物联网平台设备上报属性
示例中上报的属性为:温度10.5。
3、使用这个指令遇到" “ ”的时候,学会使用专业字符“\”。

串口回复:

AT+MQTTPUB=0"/sys/k0efkfcSwlt/Device_Demo/thing/event/property/post""{\"params\": {\"temperature\": 10.5}\\"version\": \"1.0\"}"00

OK

我们查看阿里云物联网平台物模型数据,可以看到阿里云已成功接收数据。
在这里插入图片描述

(8)订阅数据
使用指令:AT+MQTTSUB=0,“topic”,0\r\n
示例:AT+MQTTSUB=0,"/sys/k0efkfcSwlt/Device_Demo/thing/service/property/set",0
备注:topic从阿里云物联网平台获取
在这里插入图片描述
串口回复:

AT+MQTTSUB=0"/sys/k0efkfcSwlt/Device_Demo/thing/service/property/set"0

OK

这时,我们通过在线调试测试订阅数据是否成功。
在这里插入图片描述
我们在阿里云平台自定义了一盏LED灯,通过在线调试的方式,发送开启和关闭的命令,如果订阅没有问题的话,可以看到如下串口回复:

+MQTTSUBRECV:0"/sys/k0efkfcSwlt/Device_Demo/thing/service/property/set"100{"method":"thing.service.property.set""id":"2001671834""params":{"LEDSwitch":0}"version":"1.0.0"}
+MQTTSUBRECV:0"/sys/k0efkfcSwlt/Device_Demo/thing/service/property/set"100{"method":"thing.service.property.set""id":"2046606498""params":{"LEDSwitch":1}"version":"1.0.0"}

可以看到json数据里LEDSwitch的变换,这时,我们就可以在单片机上,处理这条指令来实现LED的亮灭,实现阿里云物联网平台控制led。

如果顺利到这一步,那么恭喜你,现在距离成功仅剩一半的距离了,接下来只需将AT指令封装到STM32的代码中,进行简单的STM32串口操作,就可以实现使用STM32连接阿里云的操作了。

四、STM32环形串口缓冲区驱动程序

环形串口缓冲区的原理可以查看这篇文章:环形串口缓冲区

明白了环形串口缓冲区的原理,在STM32开发中,先写好串口的驱动程序,然后再编写环形串口缓冲区程序,下面是驱动程序,我都有注释,学过STM32的都能看得懂。

usart.c

/**
  ******************************************************************************
  * @file    usart.c
  * @author  小途
  * @version V1.0
  * @date    2022-2-21
  * @brief   STM32F1的串口初始化设置
  ******************************************************************************
  * @attention
  * 串口2配置环形串口缓冲区
  *
  ******************************************************************************
  */

#include "usart.h"

#define UART_RX_MAX 255		//数组最大值

//环形缓冲区结构体
typedef struct
{
	uint8_t head_count;					//头计数
	uint8_t tail_count;					//尾计数
	uint8_t buf[UART_RX_MAX];		//缓冲区数组
}UART_S;

UART_S uart;//定义串口缓冲区结构体变量

//串口环形缓冲区初始化
void uart_Buffer_Init(void)
{
	int i;
	uart.head_count = 0;
	uart.tail_count = 0;
	for(i=0; i<UART_RX_MAX; i++)
	{
		uart.buf[i] = 0x00;
	}
}

//往缓冲器里放数据
void uart_buf_put(uint8_t ch)
{
	uart.buf[uart.head_count++] = ch;
	if(uart.head_count == UART_RX_MAX)
	{
		uart.head_count = 0;
	}
}

//判断缓冲区是否有数据
int fifo_is_empty(void)
{
	if(uart.head_count != uart.tail_count)//判断如果头不等于尾
	{
		return 0;//有数据
	}
	else return 1;//无数据
}

//从缓冲区取数据
int uart_buf_get(uint8_t *ch)
{
	if(fifo_is_empty() == 0)//如果有数据
	{
		*ch = uart.buf[uart.tail_count++];//取数据
		if(uart.tail_count == UART_RX_MAX)
		{
			uart.tail_count = 0;
		}	
		return 1;//返回成功
	}
	else return 0;//返回失败
}

//读数据,将数据通过串口发送出去
void debug_read(void)
{
	uint8_t ch;
	/*如果有数据就一直取,直到取完为至*/
	while(!fifo_is_empty())
	{
		if(uart_buf_get(&ch))
		{
			putchar(ch);
		}
	}
}

 /**
  * @brief  配置嵌套向量中断控制器NVIC
  * @param  无
  * @retval 无
  */
void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 嵌套向量中断控制器组选择 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}

//对串口2配置嵌套向量中断控制器NVIC
void NVIC_USART2_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 嵌套向量中断控制器组选择 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART2_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}

 /**
  * @brief  USART GPIO 配置,工作参数配置
  * @param  无
  * @retval 无
  */
void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
	
	// 打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	// 配置串口的工作参数
	// 配置波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 配置 针数据字长
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	// 配置停止位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	// 配置校验位
	USART_InitStructure.USART_Parity = USART_Parity_No ;
	// 配置硬件流控制
	USART_InitStructure.USART_HardwareFlowControl = 
	USART_HardwareFlowControl_None;
	// 配置工作模式,收发一起
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 完成串口的初始化配置
	USART_Init(DEBUG_USARTx, &USART_InitStructure);
	
	// 串口中断优先级配置
	//NVIC_Configuration();
	
	// 使能串口接收中断
	//USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);	
	
	// 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);	    
}

 /**
  * @brief  USART2 GPIO 配置,工作参数配置,配置环形串口缓冲区
  * @param  无
  * @retval 无
  */
void USART2_Config(void)
{
	//在串口2配置环形串口缓冲区
	//初始化环形串口缓冲区
	uart_Buffer_Init();
	
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 打开串口GPIO的时钟
	DEBUG_USART2_GPIO_APBxClkCmd(DEBUG_USART2_GPIO_CLK, ENABLE);
	
	// 打开串口外设的时钟
	DEBUG_USART2_APBxClkCmd(DEBUG_USART2_CLK, ENABLE);

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART2_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART2_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART2_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART2_RX_GPIO_PORT, &GPIO_InitStructure);
	
	// 配置串口的工作参数
	// 配置波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART2_BAUDRATE;
	// 配置 针数据字长
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	// 配置停止位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	// 配置校验位
	USART_InitStructure.USART_Parity = USART_Parity_No ;
	// 配置硬件流控制
	USART_InitStructure.USART_HardwareFlowControl = 
	USART_HardwareFlowControl_None;
	// 配置工作模式,收发一起
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 完成串口的初始化配置
	USART_Init(DEBUG_USART2, &USART_InitStructure);	
	
	//串口2中断优先级配置
	NVIC_USART2_Configuration();
	
	//使能串口2接收中断
	USART_ITConfig(DEBUG_USART2, USART_IT_RXNE, ENABLE);
	
	// 使能串口
	//USART_Cmd(DEBUG_USART2, ENABLE);	    
}

/*****************  发送一个字符 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
	/* 发送一个字节数据到USART */
	USART_SendData(pUSARTx,ch);
		
	/* 等待发送数据寄存器为空 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

/*****************  发送字符串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
	unsigned int k=0;
  do 
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='\0');
  
  /* 等待发送完成 */
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}

//发送整形数据字符串(发送长字符串)
void Usart_SendIntString( USART_TypeDef * pUSARTx, int *str)
{
	int k=0;
  do 
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='\0');
  
  /* 等待发送完成 */
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}

//重定向c库函数printf到串口,重定向后可使用printf函数,默认使用串口1
int fputc(int ch, FILE *f)
{
		/* 发送一个字节数据到串口 */
		USART_SendData(DEBUG_USART1, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(DEBUG_USART1, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

//重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数,默认使用串口1
int fgetc(FILE *f)
{
		/* 等待串口输入数据 */
		while (USART_GetFlagStatus(DEBUG_USART1, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(DEBUG_USART1);
}

usart.h

#ifndef __USART_H
#define __USART_H

#include "stm32f10x.h"
#include <stdio.h>

/*
 * 串口宏定义,不同的串口挂载的总线不一样,移植时需要修改这几个宏
 */
 
 
 // 串口1-USART1
#define  DEBUG_USARTx                   USART1
#define  DEBUG_USART_CLK                RCC_APB2Periph_USART1
#define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10

#define  DEBUG_USART_IRQ                USART1_IRQn
#define  DEBUG_USART_IRQHandler         USART1_IRQHandler
 
 
 
 
 
 //------------------------------------------------------------------------

// 串口1-USART1
#define  DEBUG_USART1                    USART1
#define  DEBUG_USART1_CLK                RCC_APB2Periph_USART1
#define  DEBUG_USART1_APBxClkCmd         RCC_APB2PeriphClockCmd
#define  DEBUG_USART1_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART1_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  DEBUG_USART1_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART1_TX_GPIO_PORT       GPIOA   
#define  DEBUG_USART1_TX_GPIO_PIN        GPIO_Pin_9
#define  DEBUG_USART1_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART1_RX_GPIO_PIN        GPIO_Pin_10

#define  DEBUG_USART1_IRQ                USART1_IRQn
#define  DEBUG_USART1_IRQHandler         USART1_IRQHandler

//------------------------------------------------------------------------

// 串口2-USART2
#define  DEBUG_USART2                   	USART2
#define  DEBUG_USART2_CLK                	RCC_APB1Periph_USART2
#define  DEBUG_USART2_APBxClkCmd         	RCC_APB1PeriphClockCmd
#define  DEBUG_USART2_BAUDRATE           	115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART2_GPIO_CLK           	(RCC_APB2Periph_GPIOA)
#define  DEBUG_USART2_GPIO_APBxClkCmd    	RCC_APB2PeriphClockCmd
   
#define  DEBUG_USART2_TX_GPIO_PORT        GPIOA   
#define  DEBUG_USART2_TX_GPIO_PIN         GPIO_Pin_2
#define  DEBUG_USART2_RX_GPIO_PORT       	GPIOA
#define  DEBUG_USART2_RX_GPIO_PIN        	GPIO_Pin_3

#define  DEBUG_USART2_IRQ                	USART2_IRQn
#define  DEBUG_USART2_IRQHandler         	USART2_IRQHandler

//使能串口2
#define USART2_ON													USART_Cmd(DEBUG_USART2, ENABLE);
//失能串口2
#define USART2_OFF												USART_Cmd(DEBUG_USART2, DISABLE);


//环形串口缓冲区相关功能函数
void uart_buf_put(uint8_t ch);
void debug_read(void);
int fifo_is_empty(void);
int uart_buf_get(uint8_t *ch);

//STM32串口相关功能函数
void USART_Config(void);
void USART2_Config(void);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart_SendIntString( USART_TypeDef * pUSARTx, int *str);

#endif


stm32f10x_it.c

//串口2中断服务函数
//将串口2接收到的数据回传到串口1
void USART2_IRQHandler(void)
{
	uint8_t ucTemp;
	if(USART_GetITStatus(DEBUG_USART2,USART_IT_RXNE)!=RESET)
	{		
		ucTemp = USART_ReceiveData(DEBUG_USART2);
		uart_buf_put(ucTemp);//往环形串口缓冲区存数据
	}	 
}

五、STM32连接阿里云驱动程序

STM32连接阿里云程序逻辑与串口助手的一致,之前使用串口助手与我的电脑进行数据交换,现在将STM32当成一个小型的“电脑”,使用STM32来对ESP8266的数据进行处理,达到物联网控制的目的。
我使用的是STM32霸道的开发板做的测试例程, 使用杜邦线连接ESP8266模块,以下的厂程序供大家参考,如有问题望指摘。
esp8266.c

/**
  ******************************************************************************
  * @file    esp8266.c
  * @author  fire
  * @version V1.0
  * @date    2024-1-16
  * @brief   esp8266连接阿里云物联网平台
  ******************************************************************************
**/

#include "./ESP8266/esp8266.h"

//测试esp8266是否存在
//向串口2发送“AT”,esp8266收到后会回复一个“Ok”
//STM32会将串口2收到的数据转发到串口1,用于监视串口2状态
//函数需要提前初始化串口1,串口2,串口2中断接收
//成功读取到esp8266返回1,否则返回0
char esp8266_text(void)
{
	uint8_t ch;
	uint8_t str[20], i=0;
	char* pch;
	
	//发送测试命令
	Usart_SendString(DEBUG_USART2, "AT\r\n");
	
	//延时一段时间等待接收
	Delay_ms(50);
	
	//向串口环形缓冲区取数据
	while(!fifo_is_empty())
	{
		if(uart_buf_get(&ch))
		{
			str[i] = ch;
			i++;
		}
	}
	
	//STM32将连接在串口2上ESP8266发送过来的数据转发到串口1
//	Usart_SendString(DEBUG_USART1, "收到ESP8266传来的数据:\r\n");
//	Usart_SendString(DEBUG_USART1, (char *)str);
//	Usart_SendString(DEBUG_USART1, "\r\n");
	
	pch = strstr((char *)str, "OK");
	if(pch != NULL)
	{
		Usart_SendString(DEBUG_USART1, "esp8266真实存在\r\n");
		return 1;
	}
	else
		return 0;
}

//esp8266重启
void esp8266_restart(void)
{
	uint8_t ch;
	uint8_t str[255], i=0;
	
	//发送命令
	Usart_SendString(DEBUG_USART2, "AT+RST\r\n");
	
	//延时一段时间等待接收
	Delay_ms(50);
	
	//向串口环形缓冲区取数据
	while(!fifo_is_empty())
	{
		if(uart_buf_get(&ch))
		{
			str[i] = ch;
			i++;
		}
	}
	
	//STM32将连接在串口2上ESP8266发送过来的数据转发到串口1
	Usart_SendString(DEBUG_USART1, "收到ESP8266传来的数据:\r\n");
	Usart_SendString(DEBUG_USART1, (char *)str);
	Usart_SendString(DEBUG_USART1, "\r\n");
}

//将esp8266设置为接入点模式
//设置成功返回1,失败返回0
char esp8266_setStation(void)
{
	uint8_t ch;
	uint8_t str[20], i=0;	
	char* pch;
	
	//发送命令
	Usart_SendString(DEBUG_USART2, "AT+CWMODE=1\r\n");
	
	//延时一段时间等待接收
	Delay_ms(50);
	
	//向串口环形缓冲区取数据
	while(!fifo_is_empty())
	{
		if(uart_buf_get(&ch))
		{
			str[i] = ch;
			i++;
		}
	}
	
	//STM32将连接在串口2上ESP8266发送过来的数据转发到串口1
//	Usart_SendString(DEBUG_USART1, "收到ESP8266传来的数据:\r\n");
//	Usart_SendString(DEBUG_USART1, (char *)str);
//	Usart_SendString(DEBUG_USART1, "\r\n");
	
	pch = strstr((char *)str, "OK");
	if(pch != NULL)
		return 1;
	else
		return 0;
}

//查询WiFi连接信息
//如果成功连接WiFi返回1,否则返回0
char esp8266_inquireWiFi_Data(void)
{
	uint8_t ch;
	uint8_t str[255], i=0;
	char* pch;
	
	//发送命令
	Usart_SendString(DEBUG_USART2, "AT+CWSTATE?\r\n");
	
	//延时一段时间等待接收
	Delay_ms(50);
	
	//向串口环形缓冲区取数据
	while(!fifo_is_empty())
	{
		if(uart_buf_get(&ch))
		{
			str[i] = ch;
			i++;
		}
	}
	
	//STM32将连接在串口2上ESP8266发送过来的数据转发到串口1
//	Usart_SendString(DEBUG_USART1, "收到ESP8266传来的数据:\r\n");
//	Usart_SendString(DEBUG_USART1, (char *)str);
//	Usart_SendString(DEBUG_USART1, "\r\n");
	
	pch = strchr((char *)str, '2');
	if(pch != NULL)
		return 1;
	else
		return 0;
}

//连接WiFi
void esp8266_connectWiFi(void)
{
	uint8_t ch, i=0;
	char str[255];
	
	//将要发送的数据拼接起来
	char wifi_data[255] = "AT+CWJAP=\"";
	strcat(wifi_data, WiFi_Name);
	strcat(wifi_data, "\",\"");
	strcat(wifi_data, WiFi_Password);
	strcat(wifi_data, "\"\r\n");
	
	//发送命令
	Usart_SendString(DEBUG_USART2, wifi_data);
	
	//延时一段时间等待接收
	Delay_ms(50);
	
	//向串口环形缓冲区取数据
	while(!fifo_is_empty())
	{
		if(uart_buf_get(&ch))
		{
			str[i] = ch;
			i++;
		}
	}
	
	//STM32将连接在串口2上ESP8266发送过来的数据转发到串口1
	Usart_SendString(DEBUG_USART1, "收到ESP8266传来的数据:\r\n");
	Usart_SendString(DEBUG_USART1, str);
	Usart_SendString(DEBUG_USART1, "\r\n");
}

//设置MQTT连接参数
//设置成功返回1,否则返回0
char esp8266_setMQTTData(void)
{
	uint8_t ch;
	uint8_t str[255], i=0;
	char* pch;
	
	//发送命令
	Usart_SendString(DEBUG_USART2, "AT+MQTTUSERCFG=0,1,\"");
	Usart_SendString(DEBUG_USART2, Aliyun_ClientId);
	Usart_SendString(DEBUG_USART2, "\",\"");
	Usart_SendString(DEBUG_USART2, Aliyun_Username);
	Usart_SendString(DEBUG_USART2, "\",\"");
	Usart_SendString(DEBUG_USART2, Aliyun_Password);
	Usart_SendString(DEBUG_USART2, "\",0,0,\"\"\r\n");
	
	//延时一段时间等待接收
	Delay_ms(50);
	
	//向串口环形缓冲区取数据
	while(!fifo_is_empty())
	{
		if(uart_buf_get(&ch))
		{
			str[i] = ch;
			i++;
		}
	}
	
//	Usart_SendString(DEBUG_USART1, "收到ESP8266传来的数据:\r\n");
//	Usart_SendString(DEBUG_USART1, (char *)str);
//	Usart_SendString(DEBUG_USART1, "\r\n");
	
	pch = strstr((char *)str, "OK");
	if(pch != NULL)
		return 1;
	else
		return 0;
}

//连接阿里云
//连接成功回复1,失败回复0
char connect_Aliyun(void)
{
	uint8_t ch;
	uint8_t str[255], i=0;
	char* pch;
	
	//检查esp8266是否存在
	if(esp8266_text() != 1)
	{
		printf("连接错误,代码1:请检测esp8266与芯片连接线路!\r\n");
		return 0;
	}
	
	//设置esp8266为接入点模式
	if(esp8266_setStation() != 1)
	{
		printf("连接错误,代码2:esp8266 接入点模式设置失败,请检查!\r\n");
		return 0;
	}else{
		printf("接入点模式设置成功!\r\n");
	}
		
	
	//连接WiFi网络
	//连接之前先检查WiFi网络是否连接
	//如果以连接则跳过此步骤
	if(esp8266_inquireWiFi_Data() != 1)
	{
		//连接WiFi
		esp8266_connectWiFi();
		printf("正在连接WiFi");
		
		//5(s)*15=60(s)=1(min)
		//等待一段时间,待WiFi连接成功
		i=15;
		while(1)
		{
			Delay_ms(5000);
			printf(".");
			
			//如果WiFi成功退出循环
			if(esp8266_inquireWiFi_Data() == 1)
			{
				printf("WiFi连接成功!\r\n");
				break;
			}
				
			
			//超时连接退出程序
			i--;
			if(i == 0)
			{
				printf("\r\n错误代码3:WiFi连接超时,已退出!\r\n");
				return 0;
			}
		}
	}else{
		printf("WiFi连接成功!\r\n");
	}
	
	//设置MQTT连接参数
	if(esp8266_setMQTTData() != 1)
	{
		printf("连接错误,代码4:esp8266 接入点模式设置失败,请检查!\r\n");
		return 0;
	}else{
		printf("MQTT连接参数设置成功!\r\n");
	}
	
	//-------------------------------------------------------------------------------------------
	for(i=0; i<255; i++)
	{
		str[i] = '\0';
	}
	
	i=0;
	
	//下面开始连接阿里云
	//发送命令
	Usart_SendString(DEBUG_USART2, "AT+MQTTCONN=0,\"");
	Usart_SendString(DEBUG_USART2, BrokerAddress);
	Usart_SendString(DEBUG_USART2, "\",1883,1\r\n");
	
	Delay_ms(50);
	
	//向串口环形缓冲区取数据
	while(!fifo_is_empty())
	{
		if(uart_buf_get(&ch))
		{
			str[i] = ch;
			i++;
		}
	}
	
//	Usart_SendString(DEBUG_USART1, "收到ESP8266传来的数据:\r\n");
//	Usart_SendString(DEBUG_USART1, (char *)str);
//	Usart_SendString(DEBUG_USART1, "\r\n");
	
//	for(i=0; i<255; i++)
//	{
//		str[i] = '\0';
//	}
//	
//	i=0;
	
	//查询阿里云连接状态
	Usart_SendString(DEBUG_USART2, "AT+MQTTCONN?");
	
	Delay_ms(50);
	
	//向串口环形缓冲区取数据
	while(!fifo_is_empty())
	{
		if(uart_buf_get(&ch))
		{
			str[i] = ch;
			i++;
		}
	}
	
	Usart_SendString(DEBUG_USART1, "阿里云的连接状态为:\r\n");
	Usart_SendString(DEBUG_USART1, (char *)str);
	Usart_SendString(DEBUG_USART1, "\r\n");
	
	pch = strstr((char *)str, BrokerAddress);
	if(pch != NULL)
	{
		printf("阿里云物联网平台连接成功!\r\n");
		return 1;
	}else{
		printf("错误代码6:阿里云物联网平台连接失败,请检查!\r\n");
		return 0;
	}
}

//向阿里云上传温度数据
void AliyunSetData(float temperature)
{
	uint8_t ch;
	uint8_t str[255], i=0;
	
	//发送数据
	Usart_SendString(DEBUG_USART2, "AT+MQTTPUB=0,\"");
	Usart_SendString(DEBUG_USART2, IssueTOPIC);
	Usart_SendString(DEBUG_USART2, "\",\"{\\\"params\\\": {\\\"temperature\\\":");
	
	//将浮点型数据转换成字符串并发送出去
	char str2[10];
	sprintf(str2,"%f", temperature);
	Usart_SendString(DEBUG_USART2, str2);
	
	Usart_SendString(DEBUG_USART2, "}\\,\\\"version\\\": \\\"1.0\\\"}\",0,0\r\n");
	
	Delay_ms(50);
	
	//向串口环形缓冲区取数据
	while(!fifo_is_empty())
	{
		if(uart_buf_get(&ch))
		{
			str[i] = ch;
			i++;
		}
	}
	
	Usart_SendString(DEBUG_USART1, "收到ESP8266传来的数据:\r\n");
	Usart_SendString(DEBUG_USART1, (char *)str);
	Usart_SendString(DEBUG_USART1, "\r\n");
}

//阿里云订阅数据
void AliyunSubscriptionData(void)
{	
	uint8_t ch;
	uint8_t str3[255], i=0;
	
	Usart_SendString(DEBUG_USART2, "AT+MQTTSUB=0,\"");
	Usart_SendString(DEBUG_USART2, SubscriptionTOPIC);
	Usart_SendString(DEBUG_USART2, "\",0\r\n");
	
	Delay_ms(50);
	
	//向串口环形缓冲区取数据
	while(!fifo_is_empty())
	{
		if(uart_buf_get(&ch))
		{
			str3[i] = ch;
			i++;
		}
	}
	
	Usart_SendString(DEBUG_USART1, "\r\n");
	Usart_SendString(DEBUG_USART1, "esp8266设置订阅数据回复:\r\n");
	Usart_SendString(DEBUG_USART1, (char *)str3);
	Usart_SendString(DEBUG_USART1, "\r\n");
}

esp8266.h

#ifndef __ESP8266_H
#define	__ESP8266_H

#include "stm32f10x.h"
#include "usart.h"
#include "delay.h"
#include <string.h>

#define WiFi_Name 				"CMCC-H3qz"
#define WiFi_Password			"7xf47uxf"
#define Aliyun_ClientId		"k0efkfcSwlt.Device_Demo|securemode=2\\,signmethod=hmacsha256\\,timestamp=1705149361131|"
#define Aliyun_Username		"Device_Demo&k0efkfcSwlt"
#define Aliyun_Password		"ea343ac1d6a4eec7b057bcab39a2ffdf88abc8b810ec0dc7ca3de91a56292ff9"
#define BrokerAddress			"k0efkfcSwlt.iot-as-mqtt.cn-shanghai.aliyuncs.com"

//发布数据topic
#define IssueTOPIC				"/sys/k0efkfcSwlt/Device_Demo/thing/event/property/post"
//订阅数据topic
#define SubscriptionTOPIC	"/sys/k0efkfcSwlt/Device_Demo/thing/service/property/set"


char esp8266_text(void);
void esp8266_restart(void);
char esp8266_setStation(void);
char esp8266_inquireWiFi_Data(void);
void esp8266_connectWiFi(void);
char esp8266_setMQTTData(void);
char connect_Aliyun(void);
void AliyunSetData(float temperature);
void AliyunSubscriptionData(void);

#endif


main.c

void RCC_Configuration(void)
{   
  //Install system time  is 72MHZ
	SystemInit();
}

int main(void)
{	
	uint8_t ch, bufferFlag=0;
	char str[255], i=0;
	char *property = 0;
	char led_switch;
	
	RCC_Configuration();	//系统时钟初始化
	Delay_Timer_Init();		//延时函数定时器初始化
	USART_Config();				//串口1初始化
	USART2_Config();			//串口2初始化
	
	//向串口1 发送一段数据,测试串口1初始化成功
	printf("这是一个STM32挂载ESP8266连接阿里云的例程!\r\n");
	
	//初始化PB5为输出
	gpio_OutputConfig(RCC_APB2Periph_GPIOB, GPIO_Pin_5, GPIOB);
	//并将LED灯熄灭(将引脚拉高)
	GPIO_SetBits(GPIOB, GPIO_Pin_5);
	//如下同理...
	gpio_OutputConfig(RCC_APB2Periph_GPIOB, GPIO_Pin_0, GPIOB);
	GPIO_SetBits(GPIOB, GPIO_Pin_0);
	
	//初始化PA0为按键功能(SW1)
	Key_GPIO_Config(RCC_APB2Periph_GPIOA, GPIO_Pin_0, GPIOA);
	//初始化PC13为按键功能(SW2)
	Key_GPIO_Config(RCC_APB2Periph_GPIOC, GPIO_Pin_13, GPIOC);
	
  while(1)
	{
		//sw1按键按下功能
		if(Key_Scan(GPIOA, GPIO_Pin_0) == 1)
		{
			//使能串口2
			USART2_ON;
			
			//连接阿里云
			if(connect_Aliyun() == 1)
			{
				if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_5) == 1)
				{
					GPIO_ResetBits(GPIOB, GPIO_Pin_5);		//红灯亮起
					AliyunSubscriptionData();							//订阅数据
				}	else{
					GPIO_SetBits(GPIOB, GPIO_Pin_5);			//灯熄灭
					USART2_ON;														//使能串口2
				}
					
			}
		}
		
		//sw2按键按下功能
		//判断esp8266是否存在
		if(Key_Scan(GPIOC, GPIO_Pin_13) == 1)
		{
			USART2_ON;
			esp8266_text();
		}
		
		//检测到红灯亮起时,发布和订阅数据
		if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_5) == 0)
		{
			//每隔5秒发布数据
			Delay_ms(1000);
			AliyunSetData(1.2);
			
			
			//接收阿里云发送过来的数据
			for(int j=0; j<5; j++)
			{
				//每隔1秒查看串口环形缓冲区是否有数据
				Delay_ms(1000);
				
				for(i=0; i<127; i++)
				{
					str[i] = '\0';
				}
				
				i=0;
				
				//向串口环形缓冲区取数据
				while(!fifo_is_empty())
				{
					bufferFlag = 1;
					if(uart_buf_get(&ch))
					{
						str[i] = ch;
						i++;
					}
				}
				
				if(bufferFlag == 1)
				{
					bufferFlag = 0;
					
					//STM32将连接在串口2上ESP8266发送过来的数据转发到串口1
					Usart_SendString(DEBUG_USART1, "收到阿里云服务器发送过来的数据:\r\n");
					Usart_SendString(DEBUG_USART1, str);
					Usart_SendString(DEBUG_USART1, "\r\n");
					
					
					//以下是通过阿里云控制一盏led灯例程
					property = strstr(str, "LEDSwitch");
					if(property != NULL)
					{
						led_switch = *(property + 11);
						//Usart_SendByte(DEBUG_USART1, led_switch);
						
						led_switch = led_switch - 48;
						if(led_switch == 0)
						{
							GPIO_SetBits(GPIOB, GPIO_Pin_0);
						}else if(led_switch == 1){
							GPIO_ResetBits(GPIOB, GPIO_Pin_0);
						}
					}
				}
			}
		}
	}
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/355978.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MG7050VAN 基于声表的差分多输出 晶体振荡器(LVDS)

MG7050VAN的LVDS输出是一款差分多输出晶体振荡器&#xff0c;具有极低的抖动和超强的稳定性。该振荡器适用于多种应用场景&#xff0c;如服务器、存储器和网络仪器等。作为一款高端的LVDS输出设备&#xff0c;该振荡器在输出性能上具有明显优势。其超低抖动水平可以达到0.3ps M…

子窗口的qss应该放在,它构造函数里

子窗口 构造函数 加载qss QFile qss(QString(":/simulator/resources/main_theme.qss")); if (qss.open(QIODevice::ReadOnly)) {auto content qss.readAll();this->setStyleSheet(content);qss.close(); }黑色样式 放在main_theme.qss #init_scene{ backgro…

NPDP证书:让你的职业生涯飞升!

&#x1f31f;没错&#xff01;NPDP证书正在成为产品经理们的“新宠”&#xff01;越来越多的同行们纷纷选择考取NPDP证书&#xff0c;为什么这么火爆&#xff1f;一起来探究下吧&#xff01; &#x1f680;NPDP认证&#xff1a;产品经理的国际通行证 &#x1f4cd;NPDP&#x…

Java 数据结构篇-实现二叉搜索树的核心方法

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 二叉搜索树的概述 2.0 二叉搜索树的成员变量及其构造方法 3.0 实现二叉树的核心接口 3.1 实现二叉搜索树 - 获取值 get(int key) 3.2 实现二叉搜索树 - 获取最小…

MyBatis框架-ResultMap

文章目录 ResultMapsqlUser.java解决方案**方案一&#xff1a;为列名指定别名 , 别名和java实体类的属性名一致(自动映射)**UserMapper.xmlUserTest.java测试结果 **方案二&#xff1a;使用结果集映射->ResultMap 【推荐】&#xff08;手动映射&#xff09;**测试结果 Resul…

linux安装 黑方容灾备份与恢复系统软件v6.0 代理

1.环境准备 1.1硬件环境 内存>4G&#xff0c;cpu最低双核 1.2把SElinux状态改为Disabled &#xff08;1&#xff09;查看SElinux状态 输入getenforce命令 SELinux共有3个状态&#xff1a; enforcing &#xff08;执行中&#xff09;、permissive &#xff08;不执行但…

如何在Win系统安装Jupyter Notbook并实现无公网ip远程访问本地笔记

文章目录 1.前言2.Jupyter Notebook的安装2.1 Jupyter Notebook下载安装2.2 Jupyter Notebook的配置2.3 Cpolar下载安装 3.Cpolar端口设置3.1 Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 在数据分析工作中&#xff0c;使用最多的无疑就是各种函数、图表、…

YoloV8改进策略:Conv改进|DCNv4最新实践|高效涨点|多种改进教程|完整论文翻译

摘要 涨点效果:在我自己的数据集上,mAP50 由0.986涨到了0.993,mAP50-95由0.737涨到0.77,涨点明显! DCNv4是可变形卷积的第四版,速度和v3相比有了大幅度的提升,但是环境搭建有一定的难度,对新手不太友好。如果在使用过程遇到编译的问题,请严格按照我写的环境配置。 …

论文笔记:TimeGPT-1

时间序列的第一个基础大模型 1 方法 最basic的Transformer架构 采用了公开可用的最大时间序列数据集进行训练&#xff0c;包含超过1000亿个数据点。 训练集涵盖了来自金融、经济、人口统计、医疗保健、天气、物联网传感器数据、能源、网络流量、销售、交通和银行业等广泛领域…

自定义BeanNameGenerator生成规则

通过点进ComponentScan注解进入源码可以看到 追随BeanNameGenerator进入源码可以看到该类是个借口且只有一个方法 点击上面黑色箭头出现两个实现方法 点击第一个方法 进入determineBeanNameFromAnnotation方法中 通过上诉自定义一个生成beanName方法 先创建一个CustomeBeanN…

Docker本地部署APITable结合内网穿透实现公网访问

文章目录 前言1. 部署APITable2. cpolar的安装和注册3. 配置APITable公网访问地址4. 固定APITable公网地址 前言 vika维格表作为新一代数据生产力平台&#xff0c;是一款面向 API 的智能多维表格。它将复杂的可视化数据库、电子表格、实时在线协同、低代码开发技术四合为一&am…

QT+VS实现Kmeans++

1、Kmeans的原理如下&#xff1a; &#xff08;1&#xff09;首先选取样本中任一数据点作为第一个聚类中心&#xff1b; &#xff08;2&#xff09;计算样本每一个数据点至现所有聚类中心的最近距离&#xff0c;并记录下来&#xff1b; &#xff08;3&#xff09;逐一挑选所…

【C++】引用、内联函数、auto关键字等

前言&#xff1a;在前面我们讲解了C入门基础的一些学习例如命名空间、缺省参数、函数重载等。今天我们将进一步的学习&#xff0c;跟着博主的脚步再次往前迈一步吧。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:高质量&#xff23;学习…

SpringBoot 源码解析 - 持续更新

开始 spring initilizer&#xff1a;根据依赖构建工具、springboot 版本等生成 Java 工程。手把手教你手写一个最简单的 Spring Boot Starter Starter 命名规则 Spring 官方定义的 Starter 通常命名遵循的格式为 spring-boot-starter-{name}&#xff0c;例如 spring-boot-star…

目标检测数据集制作(VOC2007格式数据集制作和处理教程)

VOC2007数据集结构&#xff08;目标检测图像分割&#xff09; #VOC2007数据集结构如下&#xff1a; VOC2007|-Annotations#里面存放的是每一张图片对应的标注结果&#xff0c;为XML文件&#xff0c;#标注完成后JPEGImages每张图片在此都有一一对应的xml文件|-ImageSets#存放的是…

uniapp v3组合式语法-项目创建

打开uni编译器HbuildX,右键新建项目,出现如下界面 输入完项目名称和地址后.选择红色箭头所指处的vue版本3 然后点击确认,即可创建uniapp,v3项目 创建完项目后,可以看到如下项目结构 项目结构uniapp官网上已经写的很详细了,这里就使用uniapp官网的介绍 一个uni-app工程&…

用BufferedReader快速输入有巨坑!!大佬们都是这么输入的

刷洛谷题单第二天&#xff0c;今天第一道题还是挺简单的&#xff0c;但是又被现实给了一级大逼兜子&#xff0c;做这题关键还是想熟悉Java的快速输入。还有做这道题很容易出现RE错误&#xff0c;下面总结了常见的RE错误导致情况&#xff08;点击跳转下文&#xff09; 我的上篇…

增存量市场大爆发!国产通信中间件「反攻」

梳理2023年智能驾驶的发展脉络可见&#xff0c;消费者对智能驾驶的认可度和接受度越来越高&#xff0c;带动高速NOA迈向了规模化普及新阶段&#xff0c;城市NOA初露锋芒。 从更长远的行业变革周期来看&#xff0c;智能驾驶的技术迭代还在继续&#xff0c;叠加电子电气架构的深…

软件测试人员常用的功能测试方法分享

功能测试就是对产品的各功能进行验证&#xff0c;根据功能测试用例&#xff0c;逐项测试&#xff0c;检查产品是否达到用户要求的功能。 常用的测试方法如下&#xff1a; 1. 页面链接检查 每一个链接是否都有对应的页面&#xff0c;并且页面之间切换正确。 2. 相关性检查 删除/…

基于springboot的房屋交易系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…