资讯详情

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

基于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 <stdarg.h> #include <string.h> #include <stdio.h>  u8 status; ///用于判断接收/发送状态 u8 txbuf[2];  //发送缓冲 u8 rxbuf[4];//接收缓冲 int i=0; /* *读取温湿度传感器DHT11值,用串口打印 */ 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("\r\n 这是一个 NRF24L01 无线传输实验 \r\n"); printf("\r\n 这是无线传输 主机端 的反馈信息\r\n"); printf("\r\n 正在检测NRF与MCU是否正常连接。。。\r\n"); /*检测NRF模块与MCU的连接*/ status = NRF_Check(); /*判断连接状态*/ if(status == SUCCESS) printf("\r\n NRF与MCU连接成功!\r\n"); else printf("\r\n NRF与MCU连接失败,请重新检查接线。\r\n"); while(1) { 
          DHT11_Read_Data(&temp,&humi); printf("\r\n 主机端 进入自应答发送模式\r\n"); NRF_TX_Mode(); txbuf[0]=temp; txbuf[1]=humi; status = NRF_Tx_Dat(txbuf);//这里只设置为发给进入接收模式的无线模块  /* 以下注释的代码是对无线通信模块相互收发一体模式,我在这里没有对接收端下发指令给接收端,所以 这部分我注释了,就是没有实现将这里模块实现最大程度上面的收发一体,仅仅是将一个作为发射端,一个 作为接收端 */ // switch(status) // { 
          // case MAX_RT: // printf("\r\n 主机端 没接收到应答信号,发送次数超过限定值,发送失败。 \r\n"); // break; //  // case ERROR: // printf("\r\n 未知原因导致发送失败。 \r\n"); // break; //  // case TX_DS: // printf("\r\n 主机端 接收到 从机端 的应答信号,发送成功! \r\n");  // break;  // }  // printf("\r\n 主机端 进入接收模式。 \r\n");  // NRF_RX_Mode(); // status = NRF_Rx_Dat(rxbuf);  //  switch(status) { 
          // case RX_DR: // for(i=0;i<300;i++) // {  // printf("\r\n 主机端 接收到 从机端 发送的数据为:%d \r\n",rxbuf[i]); // txbuf[i] =rxbuf[i]; //  // } // break; case ERROR: printf("\r\n 主机端 接收出错。 \r\n"); break; } } } // } /************************************************************************** 函数名:void clock_init(void) 参数说明:无 返回值:无 函数作用:开启高速外部时钟, ADCCLK设置为12MHZ, SYSCLK设置为72Mhz,PCLK1设置为36MHZ,PKLC2设置为72mhz ***************************************************************************/ void clock_init(void) { 
          RCC->CR = 0x1010000; RCC->CFGR = 0x1DC402; } 

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

/************************************************************ * 文件名 :SPI_NRF.c * 描述 :SPI_NRF24L01+无线模块应用函数库 * 实验平台: STM32开发板 * 硬件连接:-----------------------------| * | | * | PA1 : NRF- CSN | | PA5-SPI1-SCK : NRF -SCK | | PA6-SPI1-MISO : NRF -MISO| | PA7-SPI1-MOSI : NRF -MOSI| | PA3 : NRF-IRQ | | PA2 : NRF-CE | * | | * ----------------------------- * 管脚没有特别的要求需要这样的定义 **********************************************************************************/
#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};

/* * 函数名:SPI_NRF_Init * 描述 :SPI的 I/O配置用 */
void SPI_NRF_Init(void)
{ 
        
  SPI_InitTypeDef  SPI_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
  
 /*使能GPIOB,GPIOD,复用功能时钟*/
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE|RCC_APB2Periph_AFIO, ENABLE);

 /*使能SPI1时钟*/
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);



   /*配置 SPI_NRF_SPI的 SCK,MISO,MOSI引脚,GPIOA^5,GPIOA^6,GPIOA^7 */
  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);  

  /*配置SPI_NRF_SPI的CE引脚,GPIOA^2和SPI_NRF_SPI的 CSN 引脚: NSS GPIOA^1*/
   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);


   /*配置SPI_NRF_SPI的IRQ引脚,GPIOA^3*/
  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); 
		  
  /* 这是自定义的宏,用于拉高csn引脚,NRF进入空闲状态 */
  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);

  /* Enable SPI1 */
  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<times;t++)
	{ 
        
		temp_val+=Get_Adc(ch);
		Delay_Ms(5);
	}
	return temp_val/times;
} 	 


(2).main.c

/*-----------------------------------------------------*/
/* 超纬电子STM32系列开发板 */
/*-----------------------------------------------------*/
/* 程序结构 */
/*-----------------------------------------------------*/
/*USER :包含程序的main函数,是整个程序的入口 */
/*HARDWARE :包含开发板各种功能外设的驱动程序 */
/*CORE :包含STM32的核心程序,官方提供,我们不修改 */
/*STLIB :官方提供的库文件,我们不修改 */
/*-----------------------------------------------------*/
/* */
/* 程序main函数,入口函数源文件 */
/* */
/*-----------------------------------------------------*/

#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)                        //主循环
	{ 
        		
		/*--------------------------------------------------------------------*/
		/* Connect_flag=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%x\r\n",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("接收到数据:");
				/*-----------------------------------------------------*/
				/* 处理CONNACK报文 */
				/*-----------------------------------------------------*/				
				//if判断,如果第一个字节是0x20,表示收到的是CONNACK报文
				//接着我们要判断第4个字节,看看CONNECT报文是否成功
				if(MQTT_RxDataOutPtr[2]==0x20){ 
                     			
				    switch(MQTT_RxDataOutPtr[5]){ 
        					
						case 0x00 : u1_printf("CONNECT报文成功\r\n");                            //串口输出信息 
								    ConnectPack_flag = 1;                                        //CONNECT报文成功,订阅报文可发
									break;                                                       //跳出分支case 0x00 
						case 0x01 : u1_printf("连接已拒绝,不支持的协议版本,准备重启\r\n");     //串口输出信息
									Connect_flag = 0;                                            //Connect_flag置零,重启连接
									break;                                                       //跳出分支case 0x01 
						case 0x02 : u1_printf("连接已拒绝,不合格的客户端标识符,准备重启\r\n"); //串口输出信息
									Connect_flag = 0;                                            //Connect_flag置零,重启连接
									break;                                                       //跳出分支case 0x02 
						case 0x03 : u1_printf("连接已拒绝,服务端不可用,准备重启\r\n");         //串口输出信息
									Connect_flag = 0;                                            //Connect_flag置零,重启连接
									break;                                                       //跳出分支case 0x03
						case 0x04 : u1_printf("连接已拒绝,无效的用户名或密码,准备重启\r\n");   //串口输出信息
									Connect_flag = 0;                                            //Connect_flag置零,重启连接 
									break;                                                       //跳出分支case 0x04
						case 0x05 : u1_printf("连接已拒绝,未授权,准备重启\r\n");               //串口输出信息
									Connect_flag = 0;                                            //Connect_flag置零,重启连接 
									break;                                                       //跳出分支case 0x05 
						default   : u1_printf("连接已拒绝,未知状态,准备重启\r\n");             //串口输出信息 
									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("订阅成功\r\n");            //串口输出信息
							        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("订阅失败,准备重启\r\n");  //串口输出信息 
									Connect_flag = 0;                     //Connect_flag置零,重启连接
									break;                                //跳出分支 
					}					
				}
				//if判断,第一个字节是0xD0,表示收到的是PINGRESP报文
				else if(MQTT_RxDataOutPtr[2]==0xD0){ 
         
					u1_printf("PING报文回复\r\n"); 		  //串口输出信息 
					if(Ping_flag==1){ 
                             //如果Ping_flag=1,表示第一次发送

标签: 传感器归位300pa2pa传感器传感器qs30无线传感器模块sys

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台