栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

基于STM32和阿里云的矿道环境监测系统(温湿度DHT117,NRF2401(2.4G模块),气体传感器(MQ-3),0.96寸OLED屏,wifi模块)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基于STM32和阿里云的矿道环境监测系统(温湿度DHT117,NRF2401(2.4G模块),气体传感器(MQ-3),0.96寸OLED屏,wifi模块)

基于STM32和阿里云的矿道环境监测系统(温湿度DHT117,NRF2401(2.4G模块),气体传感器(MQ-3)) 系统实现的功能

系统实现的是运用一块STM32F103C8T6作为发射端节点,上面搭载NRF2401(2.4G)并连上温湿度模块,外加一块STM32F103C8T6作为接收端节点,上面搭上气体传感器,0.96寸OLED显示屏,NRF2401(2.4G)作为接收端,通过ESP8266WiFi模块上传阿里云,并在阿里云上面的IOT Studio 显示自己的数据,当然我也自己写了自己的js界面,显示在自己本地上面的浏览器上。整体功能显示基本完成,NRF2401(2.4模块)上已经实现,过程还是挺充实




也可以在自己上面阿里云上面通过物理模型实现数据的可视化web界面设置。
简易的实现自己的一系列功能时,可能你以为就实现了?其实还有自己实现的可以自己web端实现的功能,自己运用的node.js架构后台,然后运用阿里云上面的AMQP服务端订阅功能实现对数据的下发和显示

实现的代码如下 一.发射端上面发送数据

温湿度DHT117+无线通信模块作为远节点的温湿度结点(发射端)主函数代码实现:
通过传感器采集模块,在监测的周遭环境上由模值转化为数据,通过OUT管脚输出数据,同时交一个无线传输模块作为一个远节点,
(1)main.c

#include "stm32f10x.h"
#include "usart.h"
#include "delay.h"
#include "DTH117.h"             
#include "sys.h"
#include "SPI_NRF.h"
#include 
#include 
#include 

u8 status;	//用于判断接收/发送状态
u8 txbuf[2];	 //发送缓冲
u8 rxbuf[4];//接收缓冲
int i=0;

void clock_init(void);
u8 temp = 0,humi = 0;
 int main(void)
 {
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	SPI_NRF_Init();
	delay_init();
    DHT11_Init();	 
    clock_init();	
    usart_init(115200);		//初始化串口
	printf("rn 这是一个 NRF24L01 无线传输实验 rn");
	printf("rn 这是无线传输 主机端 的反馈信息rn");
	printf("rn   正在检测NRF与MCU是否正常连接。。。rn");
	 
   	status = NRF_Check(); 

	  
   if(status == SUCCESS)	   
   		 printf("rn      NRF与MCU连接成功!rn");  
   else	  
   	     printf("rn  NRF与MCU连接失败,请重新检查接线。rn"); 
	 while(1)
	 {	
	DHT11_Read_Data(&temp,&humi);
	printf("rn 主机端 进入自应答发送模式rn"); 
	NRF_TX_Mode();
	txbuf[0]=temp;			
	txbuf[1]=humi;
	status = NRF_Tx_Dat(txbuf);//这里只设置为发给进入接收模式的无线模块 
		  

//		  switch(status)
//		  	{
//		  	  case MAX_RT:
//			 	 	printf("rn 主机端 没接收到应答信号,发送次数超过限定值,发送失败。 rn");
//				 break;
//	
//			  case ERROR:
//			  	 	printf("rn 未知原因导致发送失败。 rn");
//				 break;
//	
//			  case TX_DS:
//			  		printf("rn 主机端 接收到 从机端 的应答信号,发送成功! rn");	 		
//				 break;  								
//		  	}			   	
//	 	printf("rn 主机端 进入接收模式。 rn");	
//		NRF_RX_Mode();
//		status = NRF_Rx_Dat(rxbuf);	
//			
		switch(status)
			{
//			 case RX_DR:
//			 	for(i=0;i<300;i++)
//				{					
//					printf("rn 主机端 接收到 从机端 发送的数据为:%d rn",rxbuf[i]);
//					txbuf[i] =rxbuf[i];
//														
//				}
//				  break;
			 case ERROR:
				  	printf("rn 主机端 接收出错。   rn");
				  break;  		
			}
	 }
 }
// }
 

 void clock_init(void)
 {
	RCC->CR = 0x1010000;
	RCC->CFGR = 0x1DC402;
}

(2).无线通信模块(管脚定义)SPI_NRF.c

#include "SPI_NRF.h"
#include "usart.h"
#include "delay.h"

 u8 RX_BUF[RX_PLOAD_WIDTH];		//接收数据缓存
 u8 TX_BUF[TX_PLOAD_WIDTH];		//发射数据缓存
 u8 TX_ADDRESS[TX_ADR_WIDTH] = {0x34,0x43,0x10,0x10,0x01};  // 定义一个静态发送地址
 u8 RX_ADDRESS[RX_ADR_WIDTH] = {0x34,0x43,0x10,0x10,0x01};


void SPI_NRF_Init(void)
{
  SPI_InitTypeDef  SPI_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
  
 
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE|RCC_APB2Periph_AFIO, ENABLE);

 
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);



   
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用功能
  GPIO_Init(GPIOA, &GPIO_InitStructure);  

  
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_1;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);


   
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;  //上拉输入
  GPIO_Init(GPIOA, &GPIO_InitStructure); 
		  
  
  NRF_CSN_HIGH(); 
 
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线全双工
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;	 					//主模式
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;	 				//数据大小8位
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;		 				//时钟极性,空闲时为低
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;						//第1个边沿有效,上升沿为采样时刻
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		   					//NSS信号由软件产生
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;  //8分频,9MHz
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;  				//高位在前
  SPI_InitStructure.SPI_CRCPolynomial = 7;
  SPI_Init(SPI1, &SPI_InitStructure);

  
  SPI_Cmd(SPI1, ENABLE);
}
二.接收端接收数据+上传阿里云

接收端接收的数据就是来自发射端上面的数据,通时自己在接收端上还实现了对MQ-3气体传感器的处理,同时自己在考虑这个数据的直观性还是用了一个OLED显示屏实现数据的客观处理。
(1)气体传感器ADC.c

 #include "adc.h"
 #include "delay.h"
 #include "stm32f10x_adc.h"
//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//All rights reserved									  
// 		   
//初始化ADC
//这里我们仅以规则通道为例
//我们默认将开启通道0~3																	   
void Adc_Init(void)
{ 	
	ADC_InitTypeDef ADC_InitStructure; 
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE );	  //使能ADC1通道时钟
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
	//PA1 作为模拟通道输入引脚                         
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;		//模拟输入引脚
	GPIO_Init(GPIOA, &GPIO_InitStructure);	

	ADC_DeInit(ADC1);  //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值

	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	//ADC工作模式:ADC1和ADC2工作在独立模式
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;	//模数转换工作在单通道模式
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;	//模数转换工作在单次转换模式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//转换由软件而不是外部触发启动
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//ADC数据右对齐
	ADC_InitStructure.ADC_NbrOfChannel = 1;	//顺序进行规则转换的ADC通道的数目
	ADC_Init(ADC1, &ADC_InitStructure);	//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器   
	ADC_Cmd(ADC1, ENABLE);	//使能指定的ADC1
	
	ADC_ResetCalibration(ADC1);	//使能复位校准  
	 
	while(ADC_GetResetCalibrationStatus(ADC1));	//等待复位校准结束
	
	ADC_StartCalibration(ADC1);	 //开启AD校准
 
	while(ADC_GetCalibrationStatus(ADC1));	 //等待校准结束
 
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);		//使能指定的ADC1的软件转换启动功能

}				  
//获得ADC值
//ch:通道值 0~3
u16 Get_Adc(u8 ch)   
{
  	//设置指定ADC的规则组通道,一个序列,采样时间
	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );	//ADC1,ADC通道,采样时间为239.5周期	  			    
  
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);		//使能指定的ADC1的软件转换启动功能	
	 
	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束

	return ADC_GetConversionValue(ADC1);	//返回最近一次ADC1规则组的转换结果
}

u16 Get_Adc_Average(u8 ch,u8 times)
{
	u32 temp_val=0;
	u8 t;
	for(t=0;t
		temp_val+=Get_Adc(ch);
		Delay_Ms(5);
	}
	return temp_val/times;
} 	 


(2).main.c















#include "stm32f10x.h"  //包含需要的头文件
#include "main.h"       //包含需要的头文件
#include "delay.h"      //包含需要的头文件
#include "usart1.h"     //包含需要的头文件
#include "usart2.h"     //包含需要的头文件
#include "timer1.h"     //包含需要的头文件
#include "timer2.h"     //包含需要的头文件
#include "timer3.h"     //包含需要的头文件
#include "timer4.h"     //包含需要的头文件
#include "wifi.h"	    //包含需要的头文件
#include "led.h"        //包含需要的头文件
#include "mqtt.h"       //包含需要的头文件
#include "key.h"        //包含需要的头文件
#include "dht11.h"      //包含需要的头文件
#include "oled.h"
#include "sys.h"
#include "adc.h"
#include "SPI_NRF.h"

int main(void) 
{	
	Delay_Init();                   //延时功能初始化              
	Usart1_Init(9600);              //串口1功能初始化,波特率9600
	Usart2_Init(115200);            //串口2功能初始化,波特率115200	
	TIM4_Init(300,7200);            //TIM4初始化,定时时间 300*7200*1000/72000000 = 30ms
	LED_Init();	                    //LED初始化
	OLED_Init();
	Adc_Init();
	SPI_NRF_Init();
	WiFi_ResetIO_Init();            //初始化WiFi的复位IO
    MQTT_Buff_Init();               //初始化接收,发送,命令数据的 缓冲区 以及各状态参数
	AliIoT_Parameter_Init();	    //初始化连接阿里云IoT平台MQTT服务器的参数	
	 OLED_Clear();
	OLED_Display_On();
	OLED_ShowCHinese(40,0,0);
	OLED_ShowCHinese(60,0,1);
	OLED_ShowCHinese(80,0,2);
	OLED_ShowCHinese(100,0,3);
		 //显示各个参数的中文
    OLED_ShowCHinese(0,2,4);
	OLED_ShowCHinese(18,2,5);	 
	OLED_ShowString(35,2, ":",16);//显示温度中文
	
	OLED_ShowCHinese(0,4,6);
	OLED_ShowCHinese(18,4,7);	 
	OLED_ShowString(35,4, ":",16);//显示温度中文	
	
	while(1)                        //主循环
	{		
		
		
		
		if(Connect_flag==1){     
			
			
			
				if(MQTT_TxDataOutPtr != MQTT_TxDataInPtr){                //if成立的话,说明发送缓冲区有数据了
				//3种情况可进入if
				//第1种:0x10 连接报文
				//第2种:0x82 订阅报文,且ConnectPack_flag置位,表示连接报文成功
				//第3种:SubcribePack_flag置位,说明连接和订阅均成功,其他报文可发
				if((MQTT_TxDataOutPtr[2]==0x10)||((MQTT_TxDataOutPtr[2]==0x82)&&(ConnectPack_flag==1))||(SubcribePack_flag==1)){    
					u1_printf("发送数据:0x%xrn",MQTT_TxDataOutPtr[2]);  //串口提示信息
					MQTT_TxData(MQTT_TxDataOutPtr);                       //发送数据
					MQTT_TxDataOutPtr += BUFF_UNIT;                       //指针下移
					if(MQTT_TxDataOutPtr==MQTT_TxDataEndPtr)              //如果指针到缓冲区尾部了
						MQTT_TxDataOutPtr = MQTT_TxDataBuf[0];            //指针归位到缓冲区开头
				} 				
			}//处理发送缓冲区数据的else if分支结尾
			
			
			
			
			if(MQTT_RxDataOutPtr != MQTT_RxDataInPtr){  //if成立的话,说明接收缓冲区有数据了														
				u1_printf("接收到数据:");
				
				
								
				//if判断,如果第一个字节是0x20,表示收到的是CONNACK报文
				//接着我们要判断第4个字节,看看CONNECT报文是否成功
				if(MQTT_RxDataOutPtr[2]==0x20){             			
				    switch(MQTT_RxDataOutPtr[5]){					
						case 0x00 : u1_printf("CONNECT报文成功rn");                            //串口输出信息	
								    ConnectPack_flag = 1;                                        //CONNECT报文成功,订阅报文可发
									break;                                                       //跳出分支case 0x00                                              
						case 0x01 : u1_printf("连接已拒绝,不支持的协议版本,准备重启rn");     //串口输出信息
									Connect_flag = 0;                                            //Connect_flag置零,重启连接
									break;                                                       //跳出分支case 0x01   
						case 0x02 : u1_printf("连接已拒绝,不合格的客户端标识符,准备重启rn"); //串口输出信息
									Connect_flag = 0;                                            //Connect_flag置零,重启连接
									break;                                                       //跳出分支case 0x02 
						case 0x03 : u1_printf("连接已拒绝,服务端不可用,准备重启rn");         //串口输出信息
									Connect_flag = 0;                                            //Connect_flag置零,重启连接
									break;                                                       //跳出分支case 0x03
						case 0x04 : u1_printf("连接已拒绝,无效的用户名或密码,准备重启rn");   //串口输出信息
									Connect_flag = 0;                                            //Connect_flag置零,重启连接						
									break;                                                       //跳出分支case 0x04
						case 0x05 : u1_printf("连接已拒绝,未授权,准备重启rn");               //串口输出信息
									Connect_flag = 0;                                            //Connect_flag置零,重启连接						
									break;                                                       //跳出分支case 0x05 		
						default   : u1_printf("连接已拒绝,未知状态,准备重启rn");             //串口输出信息 
									Connect_flag = 0;                                            //Connect_flag置零,重启连接					
									break;                                                       //跳出分支case default 								
					}				
				}			
				//if判断,第一个字节是0x90,表示收到的是SUBACK报文
				//接着我们要判断订阅回复,看看是不是成功
				else if(MQTT_RxDataOutPtr[2]==0x90){ 
						switch(MQTT_RxDataOutPtr[6]){					
						case 0x00 :
						case 0x01 : u1_printf("订阅成功rn");            //串口输出信息
							        SubcribePack_flag = 1;                //SubcribePack_flag置1,表示订阅报文成功,其他报文可发送
									Ping_flag = 0;                        //Ping_flag清零
   								    TIM3_ENABLE_30S();                    //启动30s的PING定时器
									TIM2_ENABLE_30S();                    //启动30s的上传数据的定时器
						            TempHumi_State();                     //先发一次数据
									break;                                //跳出分支                                             
						default   : u1_printf("订阅失败,准备重启rn");  //串口输出信息 
									Connect_flag = 0;                     //Connect_flag置零,重启连接
									break;                                //跳出分支 								
					}					
				}
				//if判断,第一个字节是0xD0,表示收到的是PINGRESP报文
				else if(MQTT_RxDataOutPtr[2]==0xD0){ 
					u1_printf("PING报文回复rn"); 		  //串口输出信息 
					if(Ping_flag==1){                     //如果Ping_flag=1,表示第一次发送
						 Ping_flag = 0;    				  //要清除Ping_flag标志
					}else if(Ping_flag>1){ 				  //如果Ping_flag>1,表示是多次发送了,而且是2s间隔的快速发送
						Ping_flag = 0;     				  //要清除Ping_flag标志
						TIM3_ENABLE_30S(); 				  //PING定时器重回30s的时间
					}				
				}	
				//if判断,如果第一个字节是0x30,表示收到的是服务器发来的推送数据
				//我们要提取控制命令
				else if((MQTT_RxDataOutPtr[2]==0x30)){ 
					u1_printf("服务器等级0推送rn"); 		   //串口输出信息 
					MQTT_DealPushdata_Qs0(MQTT_RxDataOutPtr);  //处理等级0推送数据
				}				
								
				MQTT_RxDataOutPtr += BUFF_UNIT;                     //指针下移
				if(MQTT_RxDataOutPtr==MQTT_RxDataEndPtr)            //如果指针到缓冲区尾部了
					MQTT_RxDataOutPtr = MQTT_RxDataBuf[0];          //指针归位到缓冲区开头                        
			}//处理接收缓冲区数据的else if分支结尾
			
			
			
			
			if(MQTT_CMDOutPtr != MQTT_CMDInPtr){                             //if成立的话,说明命令缓冲区有数据了			       
				u1_printf("命令:%srn",&MQTT_CMDOutPtr[2]);                 //串口输出信息
				
				MQTT_CMDOutPtr += BUFF_UNIT;                             	 //指针下移
				if(MQTT_CMDOutPtr==MQTT_CMDEndPtr)           	             //如果指针到缓冲区尾部了
					MQTT_CMDOutPtr = MQTT_CMDBuf[0];          	             //指针归位到缓冲区开头				
			}//处理命令缓冲区数据的else if分支结尾	
		}//Connect_flag=1的if分支的结尾
		
		
		
		
		else{ 
			u1_printf("需要连接服务器rn");                 //串口输出信息
			TIM_Cmd(TIM4,DISABLE);                           //关闭TIM4 
			TIM_Cmd(TIM3,DISABLE);                           //关闭TIM3  
			WiFi_RxCounter=0;                                //WiFi接收数据量变量清零                        
			memset(WiFi_RX_BUF,0,WiFi_RXBUFF_SIZE);          //清空WiFi接收缓冲区 
			if(WiFi_Connect_IoTServer()==0){   			     //如果WiFi连接云服务器函数返回0,表示正确,进入if
				u1_printf("建立TCP连接成功rn");            //串口输出信息
				Connect_flag = 1;                            //Connect_flag置1,表示连接成功	
				WiFi_RxCounter=0;                            //WiFi接收数据量变量清零                        
				memset(WiFi_RX_BUF,0,WiFi_RXBUFF_SIZE);      //清空WiFi接收缓冲区 
				MQTT_Buff_ReInit();                          //重新初始化发送缓冲区                    
			}				
		}
	}
}





void TempHumi_State(void)
{
	int humidity;           //定义一个变量,保存湿度值
	int temperature;        //定义一个变量,保存温度值
	int ch4;
	u16 TVOC_AD;
	int avalue;
	char temp[256]; 
	u8 status;	//用于判断接收/发送状态
    u8 txbuf[4];	 //发送缓冲
    u8 rxbuf[4];//接收缓冲
    u8 i;
	SPI_NRF_Init();
	u1_printf("rn   正在检测NRF与MCU是否正常连接。。。rn");
	status = NRF_Check(); 
	
	if(status == SUCCESS)	   
   		 u1_printf("rn      NRF与MCU连接成功!rn");  
   else	  
   	     u1_printf("rn  正在检测NRF与MCU是否正常连接。。。rn"); 
   u1_printf("rn 从机端 进入接收模式rn");
   NRF_RX_Mode();     //这边的是在收
	status = NRF_Rx_Dat(rxbuf);

	   
		 if(status == RX_DR)
	{
		u1_printf("rn 从机端 接收到 主机端 发送的数据 rn");
	  for(i=0;i<2;i++)
				{	
					if(i%2==0)
					{
						u1_printf("rn 温度的数据为:%d rn",rxbuf[i]);
						OLED_ShowNum(45,2,rxbuf[i],3,16);//显示温度的码值
						temperature=rxbuf[i];        //定义一个变量,保存温度值
						txbuf[i] =1;
			        }
			        else
			       {
						u1_printf("rn 湿度的数据为:%d rn",rxbuf[i]);
					    OLED_ShowNum(45,4,rxbuf[i],3,16);//显示湿度的码值
						 humidity=rxbuf[i];          //定义一个变量,保存湿度值
					   txbuf[i] =1;
			       }
					TVOC_AD=Get_Adc_Average(ADC_Channel_0,4);
					OLED_ShowNum(0,6,TVOC_AD,4,16);//显示ADC的值
					avalue=(float)TVOC_AD*2*(3.3/4095);//测量值是在没有气体的环境下测得,同时测得的模值在进行这样的运算下是一个小数,又应为是avalue是一个int类型,所以值为0
					ch4=avalue;
					u1_printf("%.3dV--这个是烟雾数据",ch4);
				   OLED_ShowNum(60,6,avalue,4,16);
					
				}						
	}   
//	DHT11_Read_Data(&temperature,&humidity);	               //读取温湿度值
	u1_printf("温度:%d  湿度:%d   甲醛:%drn",temperature,humidity,ch4);  //串口输出信息
	
	sprintf(temp,"{"method":"thing.event.property.post","id":"203302322","params":{"CurrentHumidity":%d,"Temperature":%d,"ch4":%d},"version":"1.0.0"}",humidity,temperature,ch4);  //构建回复湿度温度数据
	MQTT_PublishQs0(P_TOPIC_NAME,temp,strlen(temp));   //添加数据,发布给服务器	
}

三.踩坑处理

1.如果对整体的代码还不是非常熟悉的可以看看超纬电子STM32系列课程。
2.wifi模块在用之前就需要烧录设置为station模式
3.wifi的频段设置为2.4G
5.代码运用的二个串口,串口1用来串口输出,串口二用来上传数据到服务器
6.需要改动的地方时


7.如果使用的是这个代码,阿里云上面的产品设计需要设置的连接方式是蜂窝模式。

这里是代码下载地点
这个是我其他博客里面的资料

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/954934.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号