【STM32训练—WiFi模块】第二篇、STM32驱动ESP8266WiFi模块获取天气

news2024/11/26 16:41:27

目录

第一部分、前言

1、获取心知天气API接口

2、硬件准备

第二部分、电脑串口助手调试WIFI模块获取天气

1、ESP8266获取天气的流程

2、具体步骤

第三部分、STM32驱动ESP8266模块获取天气数据

1、天气数据的解析

1.1、什么函数来解析天气数据?

2.1、解析后的数据如何使用?

 2、相关代码

2.1、main.c文件

2.2、esp8266.c文件

第四部分、总结

1、效果展示

2、完整的工程

3、补充


第一部分、前言

        这篇博客拖了很久很久,本来是打算和前面一篇一起发出来的,但是那段时间因为一些事情耽搁了,这篇博客写了一半,剩下的一直都没有写,其次就是人也有点懒,也不太想动🤪🤪。

        这几天考试周来了,复习又不想复习,不如把这篇内容更新完整,这篇更新完之后,STM32专栏应该不会再更新了。

        然后,后面我打算出一期C语言的学习笔记专栏,再后面就是FPGA的学习笔记专栏。想是这么想的,不知道能不能做好,哈哈🤭。

1、获取心知天气API接口

        这里还是希望大家先去看我的第一篇博客:【STM32训练—WiFi模块】第一篇、STM32驱动ESP8266WiFi模块获取网络时间通过这篇博客你会明白WIFI模块获取网络流程是什么样子的,搞懂了这个,你会发现获取天气和获取时间的步骤完全一样,代码也没有什么大的变化。

        接着再来说一下心知天气,上一篇文章提到时间的接口是由苏宁后台提供的“quan.suning.com/getSysTime.do”,那么这里想获取天气,那么也需要一个API的接口,这里的API接口由心知天气给我们提供。

        因此需要注册一个心知天气,获取自己的密钥。关于注册的过程可以参考心知天气提供的文档:注册与登陆 | 心知天气文档 (seniverse.com)

        注册完成后,如何获取属于自己的API接口呢,参考文档如下:查看/修改你的API密钥 (yuque.com)

        这是我的API接口,点进去之后就会看到目前杭州的天气数据:https://api.seniverse.com/v3/weather/now.json?key=SwLQ3i0Q5TNa6NSKT&location=hangzhou&language=zh-Hans&unit=c

        点击上面的链接,就会获取到天气数据,接下来的步骤就和前面获取时间一样,主要区别就是将时间的API接口换成刚刚注册得到的心知天气的API接口即可是不是发现原来也就这么回事。

2、硬件准备

         STM32选用核心板F103C8T6,然后再加一个ESP8266 WiFi模块(任何型号应该都可以,我这次用的ESP-01s),最后需要一个USB-TTL模块用来打印串口数据。

        需要注意的是:我的这个ESP-01S,有一个EN使能端,必须要给高电平才能用,上一篇博客用的那个WIFI模块没有EN使能端。所以希望大家注意自己的模块。

第二部分、电脑串口助手调试WIFI模块获取天气

1、ESP8266获取天气的流程

        流程和获取时间的流程大致一样,只不过这里获取的为天气数据。

2、具体步骤

 第一步、AT指令集

0:AT
1:AT+RST
2:AT+CWMODE=1
3:AT+CIPMUX=0
4:AT+CWJAP="你的WiFi名称","你的WiFi密码"
5:AT+CIPMODE=1
6:AT+CIPSTART="TCP","api.seniverse.com",80
7:AT+CIPSEND
8:GET https://api.seniverse.com/v3/weather/now.json?key=SwLQ3i0Q5TNa6NSKT&location=hangzhou&language=en&unit=c
9:+++

        注意:所有串口步骤同前一篇文章,这里直接有区别的步骤为第八步,因此这里直接跳转到第八步

 第八步、连接目标的服务器,TCP是传输协议,api.seniverse.com是心知天气服务器的IP地址,80是服务器端口。

 第十步、第九步和前文一样,接着再发送获取数据的请求,得到天气数据

 第十一步、关于退出透传的方式也和前面博客完全一样,这里就不再展示,所以一定要看第一篇文章,那个文章介绍的太详细了。

第三部分、STM32驱动ESP8266模块获取天气数据

1、天气数据的解析

        这里天气的解析我调用了cJSON的库,你不用管这个库怎么写的,原理是啥,知道它是怎么用的就可以了,例如调用什么函数来解析天气数据?解析后的数据如何使用?弄明白这两个问题,就够了。

        1.1、什么函数来解析天气数据?

        时间太久了,我都不知道我哪里弄来的这个函数,反正挺好用的,侵权联系我删除!

        这个函数我放在我工程的"cJSON.c"文件最下面。

/*********************************************************************************
* Function Name    : cJSON_WeatherParse,解析天气数据
* Parameter		   : JSON:天气数据包  results:保存解析后得到的有用的数据
* Return Value     : 0:成功 其他:错误
* Function Explain : 
* Create Date      : 2017.12.6 by lzn
**********************************************************************************/
int cJSON_WeatherParse(char *JSON, Results *results)
{
	cJSON *json,*arrayItem,*object,*subobject,*item;
	
	json = cJSON_Parse(JSON); //解析JSON数据包
	if(json == NULL)		  //检测JSON数据包是否存在语法上的错误,返回NULL表示数据包无效
	{
		printf("Error before: [%s] \r\n",cJSON_GetErrorPtr()); //打印数据包语法错误的位置
		return 1;
	}
	else
	{
		if((arrayItem = cJSON_GetObjectItem(json,"results")) != NULL); //匹配字符串"results",获取数组内容
		{
			int size = cJSON_GetArraySize(arrayItem);     //获取数组中对象个数
			printf("cJSON_GetArraySize: size=%d \r\n",size); 
			
			if((object = cJSON_GetArrayItem(arrayItem,0)) != NULL)//获取父对象内容
			{
				/* 匹配子对象1 */
				if((subobject = cJSON_GetObjectItem(object,"location")) != NULL)
				{
					printf("---------------------------------subobject1-------------------------------\r\n");
					if((item = cJSON_GetObjectItem(subobject,"id")) != NULL)   //匹配子对象1成员"id"
					{
						printf("cJSON_GetObjectItem: type=%d, string is %s,valuestring=%s \r\n",item->type,item->string,item->valuestring);
						memcpy(results[0].location.id,item->valuestring,strlen(item->valuestring));
					}
					if((item = cJSON_GetObjectItem(subobject,"name")) != NULL) //匹配子对象1成员"name"
					{
						printf("cJSON_GetObjectItem: type=%d, string is %s,valuestring=%s \r\n",item->type,item->string,item->valuestring);
						memcpy(results[0].location.name,item->valuestring,strlen(item->valuestring));
					}
					if((item = cJSON_GetObjectItem(subobject,"country")) != NULL)//匹配子对象1成员"country"
					{
						printf("cJSON_GetObjectItem: type=%d, string is %s,valuestring=%s \r\n",item->type,item->string,item->valuestring);
						memcpy(results[0].location.country,item->valuestring,strlen(item->valuestring));
					}
					if((item = cJSON_GetObjectItem(subobject,"path")) != NULL)  //匹配子对象1成员"path"
					{
						printf("cJSON_GetObjectItem: type=%d, string is %s,valuestring=%s \r\n",item->type,item->string,item->valuestring);
						memcpy(results[0].location.path,item->valuestring,strlen(item->valuestring));
					}
					if((item = cJSON_GetObjectItem(subobject,"timezone")) != NULL)//匹配子对象1成员"timezone"
					{
						printf("cJSON_GetObjectItem: type=%d, string is %s,valuestring=%s \r\n",item->type,item->string,item->valuestring);
						memcpy(results[0].location.timezone,item->valuestring,strlen(item->valuestring));
					}
					if((item = cJSON_GetObjectItem(subobject,"timezone_offset")) != NULL)//匹配子对象1成员"timezone_offset"
					{
						printf("cJSON_GetObjectItem: type=%d, string is %s,valuestring=%s \r\n",item->type,item->string,item->valuestring);
						memcpy(results[0].location.timezone_offset,item->valuestring,strlen(item->valuestring));
					}
				}
				/* 匹配子对象2 */
				if((subobject = cJSON_GetObjectItem(object,"now")) != NULL)
				{
					printf("---------------------------------subobject2-------------------------------\r\n");
					if((item = cJSON_GetObjectItem(subobject,"text")) != NULL)//匹配子对象2成员"text"
					{
						printf("cJSON_GetObjectItem: type=%d, string is %s,valuestring=%s \r\n",item->type,item->string,item->valuestring);
						memcpy(results[0].now.text,item->valuestring,strlen(item->valuestring));
					}
					if((item = cJSON_GetObjectItem(subobject,"code")) != NULL)//匹配子对象2成员"code"
					{
						printf("cJSON_GetObjectItem: type=%d, string is %s,valuestring=%s \r\n",item->type,item->string,item->valuestring);
						memcpy(results[0].now.code,item->valuestring,strlen(item->valuestring));
					}
					if((item = cJSON_GetObjectItem(subobject,"temperature")) != NULL) //匹配子对象2成员"temperature"
					{
						printf("cJSON_GetObjectItem: type=%d, string is %s,valuestring=%s \r\n",item->type,item->string,item->valuestring);
						memcpy(results[0].now.temperature,item->valuestring,strlen(item->valuestring));
					}
				}
				/* 匹配子对象3 */
				if((subobject = cJSON_GetObjectItem(object,"last_update")) != NULL)
				{
					printf("---------------------------------subobject3-------------------------------\r\n");
					printf("cJSON_GetObjectItem: type=%d, string is %s,valuestring=%s \r\n",subobject->type,subobject->string,subobject->valuestring);
					memcpy(results[0].last_update,item->valuestring,strlen(subobject->valuestring));
				}
			} 
		}
	}
	
	cJSON_Delete(json); //释放cJSON_Parse()分配出来的内存空间
	
	return 0;
}

        2.1、解析后的数据如何使用?

        调用上面那个函数后,解析的数据存放在一个双层结构体当中,因此只需访问结构体的成员就可以,每个成员的名称与心知天气返回的名称相匹配。

        具体的信息参考心知天气的文档:极速实况 (yuque.com)

 2、相关代码

        2.1、main.c文件

#include "sys.h"
#include "led.h"
#include "delay.h"
#include "usart.h"
#include "esp8266.h"
#include "cJSON.h" //解析天气

//天气数据
extern unsigned char Weather_buff[300];
//天气解析
Results Weather_results[] = {{0}};

int main()
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//中断控制器分组设置
	Usart1_Init(115200);
	Usart2_Init(115200);

	LED_Init();
	delay_init();//初始化很重要//用不了的函数一般都是没有初始化
														
	ESP8266_Init();
	Get_current_weather();//获取天气
	while(1)
	{
		LED0 = 0;
		delay_ms(500);
		delay_ms(500);
		delay_ms(500);
		delay_ms(500);
		LED0 = 1;
		delay_ms(500);
		delay_ms(500);
		delay_ms(500);
		delay_ms(500);
		cJSON_WeatherParse((char *)Weather_buff, Weather_results);
		printf("%s",Weather_buff);	
		//打印结构体内内容
		printf("\r\n 打印结构体内内容如下: \r\n");
		printf("%s \r\n",Weather_results[0].now.text);
		printf("%s \r\n",Weather_results[0].now.temperature);
		printf("%s \r\n",Weather_results[0].location.path);
		printf("%s \r\n",Weather_results[0].location.country);

	}

}

        2.2、esp8266.c文件

#include "stm32f10x.h"
#include "sys.h"
#include "string.h"
#include "stdlib.h"
#include "esp8266.h"

#include "usart.h"
#include "delay.h"
#include "led.h"

//WIFI和密码·
#define ESP8266_WIFI_INFO		"AT+CWJAP=\"iPhone111\",\"123456789\"\r\n"
//心知天气的API
#define Weather_TCP		"AT+CIPSTART=\"TCP\",\"api.seniverse.com\",80\r\n"
//心知天气GET报文
/*这里城市  恩施    语言为  英文*/
#define Weather_GET		"GET https://api.seniverse.com/v3/weather/now.json?key=SwLQ3i0Q5TNa6NSKT&location=enshi&language=en&unit=c\r\n"


//ESP8266数据存放
unsigned char esp8266_buf[300] = {0};
unsigned short esp8266_cnt = 0, esp8266_cntPre = 0;
//存放天气数据
unsigned char Weather_buff[300];   //位数是随机确定的



/**************************************************************************/
//函数作用:ESP8266_Init初始化函数
//函数名称:ESP8266_Init(void);
//内部参数:
//修改日期:2022年4月18日  下午16:18
//作者:    大屁桃
/**************************************************************************/
void ESP8266_Init(void)
{
		
	ESP8266_Clear();
	
 /*让WIFI推出透传模式*/
	while(ESP8266_SendCmd("+++", ""))  
	delay_ms(500);
	
	UsartPrintf(USART_DEBUG, "1.AT\r\n");
	while(ESP8266_SendCmd("AT\r\n", "OK"))
		delay_ms(500);
	
	//
	//加一步ESP8266复位操作
	
	UsartPrintf(USART_DEBUG, "2.RST\r\n");
	ESP8266_SendCmd("AT+RST\r\n", "");
	delay_ms(500);
	ESP8266_SendCmd("AT+CIPCLOSE\r\n", "");
	delay_ms(500);
	/
	
	UsartPrintf(USART_DEBUG, "3.CWMODE\r\n");
	while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))
		delay_ms(500);
	
	UsartPrintf(USART_DEBUG, "4.AT+CIPMUX\r\n");
	while(ESP8266_SendCmd("AT+CIPMUX=0\r\n", "OK"))
		delay_ms(500);
	
	UsartPrintf(USART_DEBUG, "5.CWJAP\r\n");
	while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "WIFI GOT IP"))
	delay_ms(500);
	delay_ms(500);
	delay_ms(500);
	UsartPrintf(USART_DEBUG, "ESP8266_Init OK\r\n");
}


/*获取网络天气数据*/
/**************************************************************************/
//函数作用:获取心知天气函数
//函数名称:Get_current_weather(void);
//内部参数:
//修改日期:2022年4月18日  下午16:18
//作者:    大屁桃
/**************************************************************************/
void Get_current_weather(void)
{
	ESP8266_Clear();
	
	UsartPrintf(USART_DEBUG, "6.Weather_TCP OK\r\n");	
	while(ESP8266_SendCmd(Weather_TCP, "CONNECT"))
	delay_ms(500);
	delay_ms(500);
	
	UsartPrintf(USART_DEBUG, "7.AT+CIPMODE=1 OK\r\n");		
	while(ESP8266_SendCmd("AT+CIPMODE=1\r\n", "OK"))
	delay_ms(500);
	delay_ms(500);
	delay_ms(500);
/*sizeof(Weather_GET),必须用sizeof函数,用strlen没有用*/  	
	ESP8266_SendData((u8 *)Weather_GET, sizeof(Weather_GET)); //发送AT+CIPSEND  以及 Weather_GET
	
	ESP8266_GetIPD_GET(200,Weather_buff);
	ESP8266_Clear();//清除缓存数据	

	delay_ms(500);
	delay_ms(500);
	while(ESP8266_SendCmd("+++", ""))      /*退出透传模式*/
	delay_ms(500);
	UsartPrintf(USART_DEBUG, "+++ OK\r\n");	

	while(ESP8266_SendCmd("AT\r\n", "OK"))   //验证是否退出透传模式
		delay_ms(500);
	UsartPrintf(USART_DEBUG, "1.AT\r\n");
}




/**************************************************************************/
//函数作用:串口二中断函数
//函数名称:USART2_IRQHandler();
//内部参数:
//修改日期:2022年4月18日  下午4:18
//作者:    大屁桃
/**************************************************************************/
void USART2_IRQHandler(void)
{

	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断
	{

		if(esp8266_cnt >= sizeof(esp8266_buf))	esp8266_cnt = 0; //防止串口被刷爆				
		esp8266_buf[esp8266_cnt++] = USART2->DR;  

//		USART_SendData(USART1,USART2->DR);      //让接收到的数据打印在串口一上
		
		USART_ClearFlag(USART2, USART_FLAG_RXNE);
	}
}



//==========================================================
//	函数名称:	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_GetIPD
//
//	函数功能:	copy天气数据到Weather_buff数组里面
//
//	返回参数:	平台返回的原始数据
//
//	说明:		copy天气数据到Weather_buff
//==========================================================

unsigned char *ESP8266_GetIPD_GET(unsigned short timeOut,u8 *buff)//这里我用了一个全局变量将esp8266buf储存到这个全局变量里面
{
	do
	{
		delay_ms(5);													
	} while(timeOut--);
	strcpy((char*)buff,(char*)esp8266_buf);
	return buff;														
}

/*还未用到*/
//==========================================================
//	函数名称:	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;
				
			}
		}
		
		delay_ms(5);													//延时等待
	} while(timeOut--);
	
	return NULL;														//超时还未找到,返回空指针

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

	Usart_SendString(USART2, (unsigned char *)cmd, strlen((const char *)cmd));
	
	while(timeOut--)
	{
		if(ESP8266_WaitRecive() == REV_OK)							//如果收到数据
		{		
			if(strstr((const char *)esp8266_buf, res) != NULL)		//如果检索到关键词
			{
				ESP8266_Clear();									//清空缓存
				
				return 0;
			}
		}
		
		delay_ms(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\r\n");		//发送命令
	if(!ESP8266_SendCmd(cmdBuf, ">"))				//收到‘>’时可以发送数据
	{
		UsartPrintf(USART_DEBUG, "8.AT+CIPSEND\r\n");
		/*发送请求数据*/
		Usart_SendString(USART2, data, len);		//发送设备连接请求数据		
	}
}


第四部分、总结

1、效果展示

        这是连接单片机后,单片机通过串口返回的数据,这里串口只展示了当前天气、当前温度,所在地区和所在国家的数据,若想增加在主程序中增加即可。

        单片机通过串口返回的数据和网址上显示的一致。

 2、完整的工程

        完整的工程代码点击该链接下载,无需积分,直接下载:STM32工程文件​​​​​​​

3、补充

        这篇博客说明了天气的获取方式,天气的读取,后面想做天气显示等应用,只需要加上LCD的驱动程序即可,花里胡哨的功能要靠自己去琢磨噻。

        最后希望博客对你有帮助,喜欢的话可以点个赞呢👍👍👍。

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

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

相关文章

【MySQL数据库】MySQL索引

MySQL索引、事务与存储引擎 一、索引1.1索引的概念1.2索引的作用1.3索引的工作原理1.4索引的副作用1.5索引的应用场景 二、索引的创建与分类2.1普通索引,最基本的索引类型,没有唯一性之类的限制。2.2唯一索引2.3 主键索引2.4组合索引2.5全文索引 三、删除…

JMeter源码解析之结果收集器

目录 前言: 一、JMeter结果收集器概述 二、单机模式 三、分布式模式 四、总结 前言: JMeter是一款著名的开源性能测试工具,JMeter提供了多种结果收集器,以帮助用户对性能测试结果数据进行收集、整理和分析。 一、JMeter结果…

Mysql数据库初体验及管理

Mysql数据库初体验及管理 一、数据库相关概念1.数据库的组成2.数据库管理系统3.数据库系统 二、数据库的发展1.第一代数据库2.第二代数据库3.第三代数据库 三、主流的数据库介绍四、关系型数据库1、介绍2、实体3、关系4、属性非关系型数据库 五、Mysql数据库管理1、库和表2、常…

Excel 2021入门指南:详细解读常用功能

软件安装:办公神器office2021安装教程,让你快速上手_正经人_____的博客-CSDN博客 一、 新建工作表 打开Excel 2021后,可以看到左上角的“文件”选项,在弹出的菜单中选择“新建”选项,然后可以选择使用空白工作表或者…

工具-自动获取/校对XpathHelper/XpathHelperPlus/XPathHelperWizard插件轻松配置xpath规则

目录 一、xpath插件功能对比1.1、应用的范围1.2、不依赖插件,依赖谷歌开发者工具1.3、XpathHelper/XpathHelperPlus/XPathHelperWizard插件功能特点 二、安装XpathHelper插件以及用法2.1、安装XpathHelperPlus插件2.2、XpathHelperPlus插件使用方法 三、安装XPathHe…

自然语言处理: 第二章Word2Vec

一. 理论基础 维度很高(与语料库有关),计算复杂稀疏性,浪费计算效率,只有一个元素是1 其他都是0缺乏语义信息,无法衡量语义相似度无法处理未知单词 而在One-Hot的基础上,Word2Vec 是一种分布式表达字/词的方式&#x…

Java|注解之定义注解

Java语言使用interface语法来定义注解(Annotation),它的格式如下: public interface Report {int type() default 211;String level() default "211";String value() default "211"; } 注解的参数类似无参数…

华为OD机试真题 JavaScript 实现【比赛评分】【2023 B卷 100分】,附详细解题思路

一、题目描述 一个有N个选手参加比赛&#xff0c;选手编号为1~N&#xff08;3<N<100&#xff09;&#xff0c;有M&#xff08;3<M<10&#xff09;个评委对选手进行打分。打分规则为每个评委对选手打分&#xff0c;最高分10分&#xff0c;最低分1分。 请计算得分最…

Android 音视频开发核心知识点笔记整合

很多开发者都知道Android音视频开发这个概念&#xff0c;音视频开发不仅需要掌握图像、音频、视频的基础知识&#xff0c;并且还需要掌握如何对它们进行采集、渲染、处理、传输等一系列的开发和应用&#xff0c;因此&#xff0c;音视频开发是一门涉及到很多内容的领域。 随着5G…

开战在即!与全球伙伴一起打造你的数据应用,TiDB Future App Hackathon 2023 来啦!

2023 TiDB Future App Hackathon 来啦&#xff01;本届 Hackathon 的主题为&#xff1a;Code, Innovate & Build Amazing Data Applications —— 释放你的创造力、构建突破性的应用、在全球范围内寻找你的队友、体验最新最 in 的 Serverless 技术&#xff0c;更有 总计 $3…

马原第一章复习1.

一.物质的存在方式 《德法年鉴》 完成从唯物到唯心 从革命主义等到共产主义的过度 为创立马克思理论提供了根本前提《德意志形态》 首次阐述了历史唯物主义的基本观点《共产党宣言》标志着马克思主义的公开问世 也是第一个无产阶级政党的党纲《资本论》阐述剩余价值学说 解释生…

【推荐】Oracle Live SQL——在线 Oracle SQL 测试工具

最近回答了几个 CSDN “学习”功能里“问答”区的一些专业相关问题&#xff0c;回答过程中采用严谨的方式&#xff0c;在 Oracle Live SQL 上进行验证测试。这个很好用的 Oracle APEX 应用我使用好几年了&#xff0c;虽然近年来已转行 MySQL 和国产数据库领域&#xff0c;但仍然…

链表与顺序表的区别以及扩展计算机硬件的存储体系

好久没有更新文章了&#xff0c;在忙学校的事情时我还是比较怀念大家一直以来对我的关注和鼓励&#xff0c;接下来我会继续更新数据结构相关的文章&#xff0c;也请大家多多支持&#xff0c;十分感谢。正文来了&#xff1a; 首先说明一点&#xff0c;我在举例和比较时所使用的是…

【2023,学点儿新Java-02】计算机硬件与软件 | CPU、内存、硬盘概览 | 科学使用键盘——“指法” | 软件——计算机的灵魂 | 人机交互方式

前情回顾&#xff1a; 【2023&#xff0c;学点儿新Java-01】从查看本机 jdk版本 开始 | Java基础全程脉络图、Java工程师全程技术路线、Java职业晋升路线图 我们见到的太阳 是八分钟前的太阳&#xff0c;见到的月亮 是一点三秒之前的月亮&#xff0c;见到一英里之外的建筑&…

【Docker 安装 Zipkin】—— 每天一点小知识

&#x1f4a7; D o c k e r 安装 Z i p k i n \color{#FF1493}{Docker 安装 Zipkin} Docker安装Zipkin&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 《数据结构与算法》…

解决vue打包一次部署到不同的服务器的问题

1. 问题描述 在工作的时候&#xff0c;往往碰到同一套vue前端代码程序需要部署到很多的服务器上&#xff0c;每次更改完程序都需要打包部署到各个服务器上&#xff0c;因为每个服务器的访问地址和端口都不一样&#xff0c;如果用的若依自带的框架&#xff0c;需要每次都需要打…

Spring Security --- 自定义登录逻辑

目录 UserDetailsService详解 返回值 方法参数 异常 PasswordEncoder密码解析器详解 接口介绍 内置解析器介绍 BCryptPasswordEncoder简介 代码演示 自定义登录逻辑 编写配置类 自定义逻辑 UserDetailsService详解 当什么也没有配置的时候&#xff0c;账号和密码是…

Vue3:计算属性、监听器

computed 计算属性 计算属性是指 基于现有状态派生 (演变) 出新的状态&#xff0c;现有状态发生变化&#xff0c;派生状态重新计算。 computed 接收回调函数作为参数&#xff0c;基于回调函数中使用的响应式数据进行计算属性的创建&#xff0c;回调函数的返回值就是基于现有状态…

软件测试什么样的技术栈才能进入大厂

我们知道&#xff0c;能在一线大厂工作是大多数人的目标&#xff0c;不仅薪酬高&#xff0c;技能提升快&#xff0c;而且能得到公司影响力背书&#xff0c;将来就算跳槽也能带来光环加持。 最近疫情的影响&#xff0c;网上也爆出了一些裁员新闻&#xff0c;可以说这个疫情确实…

【深入浅出密码学】RSA

RSA密码体制 引言&#xff1a; RSA加密的本意并不是为了取代对称密码&#xff0c;而且它比诸如AES的密码要慢很多&#xff0c;因为RSA当中涉及许多数学计算&#xff0c;RSA通常和类似AES的对称密码一起使用&#xff0c;真正用来加密大量数据的是对称密码。而RSA主要保护对称密…