ESP8266与手机App通信(STM32)

news2024/11/15 11:19:34

认识模块

        ESP8266是一种低成本的Wi-Fi模块,可用于连接物联网设备,控制器和传感器等。它具有小巧、高度集成和低功耗的特点,因此在物联网应用中被广泛使用。ESP8266模块由Espressif Systems开发,具有单芯片的封装和多种功能,包括Wi-Fi网络连接、GPIO控制、PWM控制和模拟输入/输出等。ESP8266模块支持多种编程语言和开发工具,包括C语言、Lua脚本和Arduino开发环境。ESP8266模块的一个优点是它的成本相对较低,因此可以用于低成本的物联网项目。另外,由于它具有小巧的封装和低功耗的特性,因此它可以在嵌入式系统和移动设备中使用。

        总之,ESP8266模块是一种功能强大、低成本、小巧、低功耗的Wi-Fi模块,非常适合用于物联网和其他嵌入式应用中。

模块与单片机的连接

        我在本次开发中选择的是模块型号为ESP8266-01S,与ESP8266一样,他们都有8个引脚,我们只用得到其中的4个引脚,分别是:

  1. VCC:电源引脚,接收3.3V的直流电源。(也可以接5V)

  2. VCC:电源引脚,接收3.3V的直流电源。

  3. TXD:串行传输引脚,用于发送串行数据。

  4. RXD:串行接收引脚,用于接收串行数据。

        而唯一与ESP8266不同的是,esp8266需要接第5个引脚EN,接在高电平上。

        在与STM32连接时,VCC接3.3V或5V,GND接地,TX接在32单片机的RX上,RX接在32单片机的TX上。我使用的32单片机型号为STM32F407ze,通过查找资料可知,其内部的电路图大致如下图所示:

大致电路图

         我选择把WiFi模块连接到P6上,以下是我的实物连接图(下面那个是我连接的OLED模块,不用理会):

实物连接图

        接下来就是敲代码让他运行起来了。

代码部分

      串口的定义

        首先是定义串口,定义串口1是为了将串口3的收到数据发给电脑看。定义串口3用于WiFi模块的使用。

        uart1.c

        这段代码是用于配置STM32芯片上的UART1串口,并定义了一个函数 send_sring 用于通过UART1发送字符串。

#include "uart.h"

void UART1_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	//初始化GPIOA的时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

	//初始化串口1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	
	//通过结构体初始化串口引脚
	GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_9 | GPIO_Pin_10;			//配置的引脚
	GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_AF;				        //复用模式
	GPIO_InitStructure.GPIO_OType 	= GPIO_OType_PP;		            //推挽模式
	GPIO_InitStructure.GPIO_Speed 	= GPIO_Speed_100MHz;	            //速度为100MHz
	GPIO_InitStructure.GPIO_PuPd 	= GPIO_PuPd_NOPULL;		            //上下拉电阻:无上下拉电阻
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	//选择引脚复用功能
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
	
	//配置串口相关属性
	USART_InitStructure.USART_BaudRate = 115200;						//波特率
	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(USART1, &USART_InitStructure);
		
	
	//配置串口的中断(数据接收触发中断)
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
	//串口1工作
	USART_Cmd(USART1,ENABLE);

}

/* 参数:要发送的字符串,要求字符串末尾以 \0 结尾 */
void send_string(char *arr)
{
	
	while(*arr)	//判断字符串是否结束
	{
		//通过串口1发送数据到PC
		USART_SendData(USART1, *arr++);
		while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);//等待发送数据完毕
	}
	
}

        uart3.c与接受数据

        这里面定义了串口3的定义函数,中断函数和数据发送函数。在中断函数获取串口3收到的数据,其中包含单片机发给串口3的,也含括WiFi模块接收到消息并回传给串口3的。32单片机发给串口3的数据不需要我们获取,关键是如何获取WiFi模块收到手机的数据后回传给串口3的数据

        要知道如何获取WiFi模块收到手机的数据后回传给串口3的数据,首先要知道WiFi模块要如何工作。WiFi模块的配置工作在下面,可以先去看看。

        在多次调试与测试时我发现,WiFi模块在接收手机发来的数据时,会以“数据来源:数据”的格式将数据发给串口,所以我们只要在串口中断中,获取数据,然后在数据里找到带有“:”的数据,“:”往后几个字节就是手机发来的数据。

        在函数中,我定义了 USART3_RX_BUF [USART3_MAX_RECV_LEN] 用于保存所有串口接收到的数据,然后在 USART3_RX_BUF 中查询是否含有 “:”,如果有,就说明是手机发来的数据,我将 “:” 后面的11个字节保存到 get_data[11] 中(这个数组大小可变,因为我设定的是手机发送9个字节给WiFi模块,所以才定义了个大小为 11 的数组变量 get_data)。因为我手机发送的数据是以空格为间隔的三个数值,比如“ 40 50 20”,为了从 u8 类型的数组变量中获取这三个数值,我把 u8 类型的  get_data 转化为字符类型的 str_get_data[11],利用 strtok 函数把字符串  str_get_data[11] 以空格拆分,再把拆下来的部分转化为整型赋值给阈值参数。

#include "uart3.h"
#include "delay.h"
#include "stdarg.h"
#include "stdio.h"
#include "string.h"
#include "tim.h"

int i=0;
extern u8 Temperature_yu,Smog_yu,CO_yu;

//串口发送缓存区
__align(8) u8 USART3_TX_BUF[USART3_MAX_SEND_LEN];   //发送缓冲,最大USART3_MAX_SEND_LEN字节
#ifdef USART3_RX_EN                                //如果使能了接收
//串口接收缓存区
u8 USART3_RX_BUF [USART3_MAX_RECV_LEN];            //接收缓冲,最大USART3_MAX_RECV_LEN个字节.
//通过判断接收连续2个字符之间的时间差不大于100ms来决定是不是一次连续的数据.
//如果2个字符接收间隔超过100ms,则认为不是1次连续数据.也就是超过100ms没有接收到
//任何数据,则表示此次接收完毕.
//接收到的数据状态
//[15]:0,没有接收到数据;1,接收到了一批数据.
//[14:0]:接收到的数据长度

u16 USART3_RX_STA=0;

void USART3_IRQHandler(void)
{
	u8 res;
	int j,k,find;
	u8 get_data[11];//用于保存手机发送的数据
	char str_get_data[11];//用于把手机发送的数据转换为字符类型
	char* token;
	
	if(USART_GetITStatus(USART3,USART_IT_RXNE) != RESET)//接收到数据
	{
		res =USART_ReceiveData(USART3);
        if((USART3_RX_STA&(1<<15))==0)//接收完的一批数据,还没有被处理,则不再接收其他数据
		{
			if(USART3_RX_STA<USART3_MAX_RECV_LEN)//还可以接收数据
			{
				
				USART3_RX_BUF[USART3_RX_STA++]=res;
				
				// 判断接收到的数据是否为esp8266发送的数据
                if(res == '\n' && USART3_RX_BUF[USART3_RX_STA - 2] == '\r')
                {
                    // 提取接收缓存区中的数据并进行处理
					if(USART3_RX_BUF[0] == '\n' || USART3_RX_BUF[0] == '\r' ){} //舍弃换行符,以免输出空行
					else{
						if(i<47){	//过滤掉重置wifi模块时传给串口3的返回值
							i++;
						}
						else{
							printf("串口3收到%s",USART3_RX_BUF);

							/*寻找 USART3_RX_BUF 中的冒号*/
							for(j=0;j<20;j++){
								find = 0;
								if(USART3_RX_BUF[j] == ':')
								{
									find = 1;//找到后find = 1,退出循环
									break;
								}
							}
							
							if(find==1){
								for(k=0;k<10;k++){
									get_data[k] = USART3_RX_BUF[k+j];
								}
								printf("get_data%s\n",get_data);
								
								snprintf(str_get_data, sizeof(str_get_data), "%s", get_data);
								// 按空格分割字符串
								token = strtok(str_get_data, " ");
							
								if (token != NULL) {
																		
									// 第一个数值
									token = strtok(NULL, " ");
									if (token != NULL) {
										Temperature_yu = atoi(token);
            
										// 第二个数值
										token = strtok(NULL, " ");
										if (token != NULL) {
											Smog_yu = atoi(token);
											
											// 第三个数值
											token = strtok(NULL, " ");
											if (token != NULL){
												CO_yu = atoi(token);
											} else {
												// 如果没有第三个数值,可以给它赋一个默认值
												CO_yu = 20;
											}
										} else {
											// 如果没有第二个数值,可以给它赋一个默认值
											CO_yu = 20;
											}
									} else {
										// 如果没有任何数值,可以给它们都赋一个默认值
										Temperature_yu = 40;
										Smog_yu = 20;
										CO_yu = 20;
									}
								} else {
									// 如果没有任何数值,可以给它们都赋一个默认值
									Temperature_yu = 40;
									Smog_yu = 20;
									CO_yu = 20;
								}
							}
						}
					}
						
                    // 清空接收缓存区
                    USART3_RX_STA = 0;
                    memset(USART3_RX_BUF, 0, sizeof(USART3_RX_BUF));//用于清空接收缓存区USART3_RX_BUF,将其中的所有元素都设置为0
                }
			}
			else
			{
				USART3_RX_STA|=1<<15;
			}
		}
	}
}
#endif


void UART3_Config(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);//使能GPIOB时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);//使能USART3时钟
	
	USART_DeInit(USART3);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_10;//GPIOB11和GPIOB10初始化
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF ;//复用功能
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    GPIO_Init(GPIOB,&GPIO_InitStructure);//初始化GPIOB11,和GPIOB10

	GPIO_PinAFConfig(GPIOB,GPIO_PinSource11,GPIO_AF_USART3);//GPIOB11复用为USART3
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource10,GPIO_AF_USART3);//GPIOB10复用为USART3

	
	USART_InitStructure. USART_BaudRate = 115200;//波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
    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(USART3, &USART_InitStructure);//初始化串口3
    

    USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);//开启中断
	
	NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);//根据指定的参数初始化VIC寄存器
//    TIM7_Config(1000-1,8400-1); //100ms中断
    USART3_RX_STA=0;//清零
    TIM_Cmd(TIM7,DISABLE);//关闭定时器7
	USART_Cmd (USART3,ENABLE);//使能串口

}

//串口3,printf 函数
//确保一次发送数据不超过USART3_MAX_SEND_LEN字节

void u3_printf(char* fmt, ...)
{
	u16 i,j;
	va_list ap;
	va_start(ap,fmt);
	vsprintf((char*)USART3_TX_BUF,fmt,ap);
	va_end(ap);
	i=strlen((const char*)USART3_TX_BUF);//此次发送数据的长度
	for(j=0;j<i;j++)//循环发送数据
	{
		while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET);//等待上次数据传输完成
		USART_SendData(USART3,(uint8_t)USART3_TX_BUF[j]);
	}
}

        uart3.h

#ifndef _UART3_H
#define _UART3_H

//C文件中需要的其他的头文件
#include "sys.h"
#include <stm32f4xx.h>

#define USART3_MAX_RECV_LEN    500
#define USART3_MAX_SEND_LEN    500
#define USART3_RX_EN           1

extern u8 USART3_RX_BUF[USART3_MAX_RECV_LEN];
extern u8 USART3_TX_BUF[USART3_MAX_SEND_LEN];
extern u16 USART3_RX_STA;

void UART3_Config(void);
void TIM7_Int_Init(u16 arr,u16 psc);
void u3_printf(char* fmt, ...);

#endif

        esp8266.c

        要使WiFi模块与手机通信,我的做法是让WiFi模块开启热点并作为TCP服务器,手机作为TCP客户端连接服务器,实现数据的传输。

        这里的代码使用 atk_8266_start_trans() 函数初始化WiFi模块,让其工作,这段代码主要作用是给WiFi模块发送各种AT指令。如“AT”用来确认模块是否可正常工作,“AT+RST” 用来重置模块,“AT+CWMODE=2” 用来让模块进入AP模式,就是开启热点,“AT+CWSAP=\"ESP8266-ly\",\"123456789\",11,0” 用于配置热点,“AT+CIPMUX=1” 用于开启多路连接模式,最后“AT+CIPSERVER=1” 用于开启TCP服务器,这样就可以通过WiFi使用TCP协议与TCP客户端也就是手机通信了。

#include "esp8266.h"

void atk_8266_start_trans(void)
{
	u8 ret = 0;
	
	delay_ms(1000);
	delay_ms(1000);
	
	ret = atk_8266_send_cmd("AT","OK", 100);
	if(ret == 0 ) printf("AT成功\n");
	else printf("AT fail\n");
	delay_ms(1000);
		
	ret = atk_8266_send_cmd ("AT+RST" , NULL ,100);
	if(ret == 0 ) printf("AT+RST成功\n");
	else printf("AT+RST fail\n");
    delay_ms(2000);
		
    ret = atk_8266_send_cmd("AT+CWMODE=2","OK",200);//开启热点
	if(ret == 0 ) printf("AT+CWMODE=2\n");
	else printf("AT+CWMODE fail\n");
	delay_ms(1000);	
	
	ret = atk_8266_send_cmd("AT+CWSAP=\"ESP8266-ly\",\"123456789\",11,0","OK",100);//设置热点
    if(ret == 0 ) printf("AT+CWSAP成功\n");
	else printf("AT+CWSAP fail\n");
	delay_ms(1000);

	ret = atk_8266_send_cmd("AT+CIPMUX=1","OK",20);//=0:单路连接模式=1:多路连接模式
    if(ret == 0 ) printf("AT+CIPMUX=1\n");
	else printf("AT+CIPMUX=1 fail\n");
	delay_ms(1000);
	
	ret = atk_8266_send_cmd("AT+CIPSERVER=1","OK",200);//启动TCP服务器
	if(ret == 0 ) printf("AT+CIPSERVER=1\n");
	else printf("AT+CIPSERVER fail\n");
	delay_ms(1000);

}

u8 atk_8266_check_cmd(char *str)
{
	
	if(USART3_RX_STA&0x8000)//接收到一次数据
	{
		USART3_RX_BUF[USART3_RX_STA&0x7fff]=0;//添加结束符
		if(strstr((const char*)USART3_RX_BUF,(const char*)str))
			return 1;
		else
			return 0;
		

	}
	return 0;
}

//向atk_8266发送命令
//cmd:发送的命令字符串; ack:期待的应答结果,如果为空,则表示不需要等待应答;waittime:等待时间(单位:10ms)
//返回值:0,发送成功(得到了期待的应答结果);1,发送失败
u8 atk_8266_send_cmd(char *cmd, char *ack,u16 waittime)
{
	u8 res=0;
	USART3_RX_STA=0;
	u3_printf("%s\r\n",cmd);//发送命令
	
	if(ack && waittime)//需要等待应答
	{
		while(--waittime)//等待倒计时
		{
			delay_ms(100);
		    if(USART3_RX_STA&0x8000)//接收到期待的应答结果
		    {
				if(atk_8266_check_cmd(ack))
					{
						printf("ack:%s\r\n",(u8*)ack);
						break;//得到有效数据
					}
			}
			USART3_RX_STA=0;
		}
		if(waittime==0)res=1;
	}
	return res;
}

//向atk_8266发送数据
//cmd:发送的命令字符串;waittime:等待时间(单位:10ms)
//返回值:发送数据后,服务器的返回验证码
u8* atk_8266_send_data(char  *cmd,u16 waittime)
{
	char temp[5];
	char *ack=temp;
	USART3_RX_STA=0;
	u3_printf("%s\r\n",cmd);//发送命令
	if(waittime)//需要等待应答
	{
		while(--waittime)//等待倒计时
		{
			delay_ms(10);
			if(USART3_RX_STA&0X8000)//接收到期待的应答结果
			{
				USART3_RX_BUF[USART3_RX_STA&0X7fff]=0;//添加结束符
				ack=(char*)USART3_RX_BUF;
				printf("ack:%s\r\n",(u8*)ack);
				USART3_RX_STA=0;
				break;
			}
		}
	}
	return (u8*)ack;
}

        esp8266.h

#ifndef _ESP8266_H
#define _ESP8266_H

//C文件中需要的其他的头文件
#include <stm32f4xx.h>
#include "sys.h"
#include "delay.h"
#include "uart3.h"
#include "uart.h"
#include "string.h"
#include <stdio.h>

extern char restart[];
extern char cwmode[];
extern char cwlap[];
extern char cwjap[];
extern char cifsr[];
extern char cipmux[];
extern char cipstart[];
extern char cipsend[];
extern char cipserver[];
extern char cwlif[];
extern char cipstatus[];
extern char cipsto[];
extern char cipmode[];
extern char test[];

u8 atk_8266_send_cmd(char *cmd,char *ack,u16 waittime);
u8* atk_8266_send_data(char *cmd,u16 waittime);
u8 atk_8266_check_cmd(char *str);
void atk_8266_start_trans(void);
u8 atk_8266_quit_trans(void);

#endif

       发送数据

        使用WiFi模块发送数据,其AT指令为“AT+CIPSEND=0,21”(其中0表示第0个客户端,21表示发送21个字节,所以这句话意思为向第0个客户端发送21个字节的数据),当配置好WiFi模块并发送这一串指令后,WiFi模块便会做出应答,并将我们接下来传给他的数据中的前21个字节发送给第0个客户端。具体代码可以写入主函数,其代码如下:

ret = atk_8266_send_cmd("AT+CIPSEND=0,21",NULL,200);
if(ret == 0 ) 
{
	delay_ms(1000);
	ret1 = atk_8266_send_cmd(str, NULL, 200);//str即为要发送给App的数据
	if(ret1 == 0 ) printf("SEND OK\n");
	else printf("SEND fail\n");
}
else printf("AT+CIPSEND fail\n");

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux--磁盘存储管理 分区工具 fdisk 了解

对于磁盘存储&#xff0c;既然要管理&#xff0c;要分区&#xff0c;那必然就少不了要 分区的工具对于分区工具&#xff0c;常用的有三个&#xff0c;fdisk 、gdisk、parted ~&#xff01;&#xff01;本次&#xff0c;先介绍下 fdisk 分区工具 在介绍 fdisk 工具之前&#xff…

【UE4 RTS游戏】05-自定义日期和时间

效果步骤打开项目设置&#xff0c;重新设置玩家状态类为“MyGameState”打开“MyGameState”&#xff0c;点击类设置&#xff0c;选中父类为“GameStateBase”接着创建一些变量&#xff1a;&#xff08;1&#xff09;“TimeUnit”&#xff0c;浮点型&#xff0c;私有&#xff0…

线程同步——管程

管程是允许线程具有互斥、等待&#xff08;堵塞&#xff09;某个条件为false的能力的同步结构。具有通知其他线程他们特定条件已经满足的机制。管程为线程提供了可以暂时放弃独占访问&#xff0c;以便在重新获得独占访问并恢复任务之前等待满足某些条件。 管程有互斥锁以及特定…

趣味三角——第15章——傅里叶定理

第15章 傅里叶定理(Fourier’s Theorem) Fourier, not being noble, could not enter the artillery, although he was a second Newton. (傅立叶出生并不高贵&#xff0c;因此按当时的惯例进不了炮兵部队&#xff0c;虽然他是第二个牛顿。) —Franois Jean Dominique Arag…

Mini Pupper在ROS中的仿真

Mini Pupper在ROS中的仿真 课程概述 在这个课程中&#xff0c;你将会学习Mini Pupper结合Rivz和Gazebo的实例应用。你将使用Rviz可视化机器人模型&#xff0c;并在Gazebo仿真环境中实现Mini Pupper的键盘移动控制&#xff0c;然后带着Mini Pupper在你搭建的虚拟世界中散步。 关…

dorcker与vlu靶场搭建

dorcker与vlu靶场搭建 dorcker安装 以kali linux 为例 安装必要的一些系统工具 apt update apt -y install apt-transport-https ca-certificates curl software-properties-common 添加Docker PGP key curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg …

【JavaSE】数组的定义与使用详解

目录 1.数组的基本概念 1.1数组的好处 1.2什么是数组 1.3数组的定义及初始化 1.3.1数组的创建 1.3.2数组的初始化 1.4数组的使用 1.4.1访问数组中的元素 1.4.2遍历数组 2.数组的类型 2.1认识JVM的内存分布 2.2基本类型变量与引用类型变量 2.3认识null 3.数组的应…

(蓝桥真题)分果果(动态规划)

题目链接&#xff1a;P8746 [蓝桥杯 2021 省 A] 分果果 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 样例1输入&#xff1a; 5 2 6 1 2 7 9 样例1输出&#xff1a; 0 样例2输入&#xff1a; 5 5 6 1 2 7 9 样例2输出&#xff1a; 2 分析&#xff1a;这道题的状态表…

第十二章 opengl之模型加载(Assimp)

OpenGLAssimp模型加载库构建Assimp网格网格渲染Assimp 我们不太能够对像是房子、汽车或者人形角色这样的复杂形状手工定义所有的顶点、法线和纹理坐标。我们要的是将这些模型(Model)导入(Import)到程序当中。模型通常都由3D艺术家在Blender、3DS Max或者Maya这样的工具中精心制…

备份时间缩短为原来 1/4,西安交大云数据中心的软件定义存储实践

XEDP 统一数据平台为西安交通大学云平台业务提供可靠的备份空间和强大的容灾能力&#xff0c;同时确保数据安全。西安交通大学&#xff08;简称“西安交大”&#xff09;是我国最早兴办、享誉海内外的著名高等学府&#xff0c;是教育部直属重点大学。学校现有兴庆、雁塔、曲江和…

strace 用法介绍

strace 是什么 strace 是一个可用于诊断和调试的 Linux 用户空间跟踪器。我们用它来监控用户空间进程和内核的交互&#xff0c;比如系统调用、信号传递、进程状态变更等。 strace 作为一种动态跟踪工具&#xff0c;能够帮助我们高效地定位进程和服务故障。它像是一个侦探&…

企业电子招投标采购系统源码之系统的首页设计

​​ 功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 描述&#xff1a; 全过程数字化采购管理&#xff0c;打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&#xff0c;为…

ThreadLocal解析

ThreadLocal是一个存储线程本地变量的对象&#xff0c;在ThreadLocal中存储的对象在其他线程中是不可见的&#xff0c;本文介绍ThreadLocal的原理。 1、threadLocal使用 有如下代码&#xff1a; Slf4j public class TestThreadLocal {public static void main(String[] args…

干货!设备管理包括哪些内容?

本文将为您介绍&#xff1a;1、设备管理的内容有哪些2、好用的设备管理软件&#xff1b; 设备管理在企业管理中非常重要&#xff1a;设备是企业安全生产的前提&#xff0c;没有安全生产&#xff0c;一切工作都可能是无用功&#xff1b; 而好的设备管理可以极大地提高生产效率…

【数据库】MySQL数据库约束(六大约束)

目录 1.数据库约束 1.1约束类型 1.2 非空约束&#xff08;NOT NULL &#xff09; 1.3 唯一约束&#xff08;UNIQUE&#xff09; 1.4默认值约束&#xff08;DEFAULT &#xff09; 1.5主键约束&#xff08;PRIMARY KEY&#xff09; 1.6外键约束&#xff08;FOREIGN KEY &…

安全漏洞修复帖

对于项目漏洞都是一堆又不重要又很重要的事情一、修复HTTP 响应头缺失NginxTomcat响应头&#xff1a;值二、会话 Cookie 中缺少 secure 属性三、Html form 表单没有 CSRF 防护Security的示例Shiro的示例四、Host 头攻击NginxTomcatApache五、开启 options 方法一、修复HTTP 响应…

【设计模式】适配器模式和桥接模式

适配器模式 适配器模式 : 就是将一个类的接口变成客户端所期望的另一种接口&#xff0c;使得原本因为接口不匹配而无法一起工作的接口可以正常工作。属于结构型模式 比方说我有一个A牌子的奶瓶&#xff0c;然后买了个B牌子的奶嘴&#xff0c;不能匹配怎么办? 再买一个转换器…

Python3-输入和输出

Python3 输入和输出 输出格式美化 Python两种输出值的方式: 表达式语句和 print() 函数。 第三种方式是使用文件对象的 write() 方法&#xff0c;标准输出文件可以用 sys.stdout 引用。 如果你希望输出的形式更加多样&#xff0c;可以使用 str.format() 函数来格式化输出值。…

2020蓝桥杯真题洁净数 C语言/C++

题目描述 小明非常不喜欢数字 2&#xff0c;包括那些数位上包含数字 2 的数。如果一个数的数位不包含数字 2&#xff0c;小明将它称为洁净数。 请问在整数 1 至 n 中&#xff0c;洁净数有多少个&#xff1f; 输入描述 输入的第一行包含一个整数 n(1≤n≤10^6)。 输出描述 输…

VS Code 解决 SpringBoot 项目启动时报 Failed to refresh live data from process **** 的问题

问题 SpringBoot 启动后 &#xff0c;VS Code 报错 Failed to refresh live data from process ****。 现场是&#xff0c;SpringBoot 项目启动时&#xff0c;VS Code 将进行如下刷新&#xff0c;图片如下所示 当刷新 10 次以后&#xff0c;如果还是失败&#xff0c;则会抛出…