環境參數智能監測站設計(軟件篇)
系統總體設計
本系統具有對環境的溫度、溼度、光照、空氣質量、土壤水分、雨情的檢測和控制等功能。系統運用STM32F103C8T6作爲最小系統的中央處理器。整個系統主要從硬件電路設計和軟件程序設計兩部分來實現。可以將環境監控系統的硬件分爲幾大模塊進行設計,分別爲:信號採集模塊、主控制模塊、人機互動模塊。本系統設計用DS18B20、DHT11、光敏二極管、MQ-135對溫度、溼度、光照、環境質量等參數進行採集。再把採集的數據輸送到STM32F103C8T6中進行處理。處理後的信息輸送到OLED上進行顯示。STM32F103C8T6根據鍵盤輸入的設置參數值進行對比和判斷是否有參數超過設置的範圍,如有參數超標就發出聲響進行警報,把環境參數通過NRF24L01模塊發送到終端上。
檢測節點結構框圖:
總程序流程圖
本系統採用單片機作爲主控制部分,主程序是一個無限循環的程序,通過keil開發環境下載到單片機中工作。系統開始工作時,主程序運行,先對系統的硬件進行初始化,然後判斷是否有鍵盤摁下,運用鍵盤進行環境參數的設置,然後通過傳感器採集環境中的溫度、溼度、光照、有害氣體的參數。在OLED上顯示採集到的環境參數。在與我們所設置的環境參數進行比較看是否有參數超標。
總程序流程圖:
DS18B20程序流程圖
溫度監控的子程序也是一個循環的程序。當單片機接上電之後,單片機向DS18B20傳感器發出指令,DS18b20傳感器採集環境中溫度參數,把溫度值傳輸到液晶顯示屏上顯示出來。同時在STM32中將採集的溫度參數的實際值與我們設置的參數範圍進行比較。如果實際的參數值在設置的範圍內,則返回重新採集。如果不在範圍之內就發出警報並把數值通過藍牙傳輸到終端上,同時開啓相應的設備控制溫度,並返回重新採集數據。
DS18B20流程圖:
DHT11程序流程圖
溼度監控也是一個循環的子程序。當STM32通上電之後,STM32向DHT11發出採集指令,它就採集養殖舍內的溼度參數值,把該參數值傳送到OLED上顯示出來。同時在STM32中將採集的溼度參數的實際值與我們設置的參數範圍進行比較。如果實際的參數值在設置的範圍內,則返回重新採集。如果不在範圍之內就發出警報並把數值通過藍牙傳輸到終端上,同時開啓相應的設備控制溼度,並返回重新採集數據。
DHT11流程圖:
MQ-135程序流程圖
有害氣體監控的子程序也是一個循環的程序。當單片機接上電之後,單片機向MQ-135傳感器發出指令,MQ-135傳感器採集環境中有害氣體濃度,把有害氣體濃度值傳輸到液晶顯示屏上顯示出來。同時有害氣體濃度值在單片機中與我們設定的參數濃度值進行對比是否在設定的濃度範圍之內。如果在設定的範圍之內,則返回重新採集數據。如果不在範圍之內發出警報把數值通過NRF傳輸到終端上,提醒管理人員進行人工處理降低有害氣體濃度,並返回重新採集數據。
MQ-135流程圖:
光照強度程序流程圖
當STM32通上電之後,STM32向光照傳感器發出工作指令,其通過光敏電阻採集養殖舍內的光照參數,並把該參數值傳送到OLED上顯示出來。同時實際的光照與系統內設定的光照範圍進行比較,看實際值在哪個範圍內,系統會對不同的範圍開啓不同的燈光數值,並返回重新採集數值。
光照流程圖:
NRF 程序流程圖
監測終端上電,首先NRF檢測配對,檢測環境檢測節點是否工作正常,隨後進行環境參數採集,分析處理。
土壤水分程序流程圖
土壤水分傳感器,AD採集傳感器採集的電壓信號,公式計算可得土壤水分百分比,隨即傳輸給監測終端,並在OLED顯示信息。
雨情程序流程圖
雨滴傳感器採集雨情信息,同其他傳感器類似,單片機AD採集電壓信號,並通過NRF傳輸給監測終端,公式計算之後得雨情信息。
核心代碼
檢測節點主程序
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_GeneralTim.h"
#include "bsp_adc.h"
#include "OLED_I2C.h"
#include "delay.h"
#include "bsp_key.h"
#include "beep.h"
#include "nrf24l01.h"
#include "sys.h"
#include "bsp_usart.h"
#include "bsp_dht11.h"
#include "ds18b20.h"
#define uint unsigned int
#define uchar unsigned char
///////////////////////////////
void num_char(uint x);
////////////////////////////////
uchar adc1_num[ ]="12345";
uint ADC_ConvertedValueLocal[NOFCHANEL];
extern __IO uint16_t ADC_ConvertedValue[NOFCHANEL];
char *reverse(char *s)
{
char temp;
char *p = s; //p指向s的頭部
char *q = s; //q指向s的尾部
while(*q)
++q;
q--;
//交換移動指針,直到p和q交叉
while(q > p)
{
temp = *p;
*p++ = *q;
*q-- = temp;
}
return s;
}
// //檢測到的傳感器ID存數組
extern unsigned char DS18B20_ID[8][8];
extern unsigned char DS18B20_SensorNum;
int main(void)
{
char *string = my_itoa(12700);
uchar TEMP[]="TEMP:";/*溫度 ℃*/
uchar HUM[]="HUM:"; /*溼度 %Rh*/
uchar CDQ[]="CDQ:"; /*光強 lux*/
uchar SHUM[]="SHUM:";/*土壤水分 %*/
uchar PSI[]="PSI:";/*空氣質量 ppm*/
uchar RCD[]="RCD:";/*雨情 mm*/
u8 rx_buf[40];
u16 TEMP1=0,HUM1=0,CDQ1=0,SHUM1=0,PSI1=0,RCD1=0;
int adc_A5_i,adc_A7_i,adc_A1_U,adc_B0_U;
int duty_set=10,fre_set=100;
int set_point=5000;
float Temp;
u8 num=0,i;
SystemInit();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
LED_GPIO_Config();
//BEEP_Init();
ADCx_Init();
SPI2_Init();
NRF24L01_Init();
/* 高級定時器初始化 */
GENERAL_TIM_Init(duty_set , fre_set);
DelayInit();
I2C_Configuration();
OLED_Init();
OLED_CLS();
Key_GPIO_Config();
pid_text_init();
OLED_Fill(0xFF);//全屏點亮
DelayMs(1000);
OLED_Fill(0x00);//全屏滅
DHT11_Data_TypeDef DHT11_Data;
/* 配置Systick 爲1us中斷一次 */
USART_Config();//初始化串口2
/*初始化DT11的引腳*/
DHT11_Init ();
/*初始化DS18b20的引腳*/
u8 DS18B20_Init();
while(NRF24L01_Check()) //檢測NRF24L01是否存在
{
GPIO_SetBits(GPIOC,GPIO_Pin_13);
OLED_ShowStr(0,1,(unsigned char*)"NRF Error",1);
OLED_ShowStr(0,2,(unsigned char*)"Please chect NRF",1);
}
NRF24L01_TX_Mode();
while (DS18B20_Init()) //檢測DS18B20是否存在
{
OLED_ShowStr(0,1,(unsigned char*)"Ds18B20 Error",1);
}
while(1)
{
LED1_TOGGLE;
num=DS18B20_Search_Rom(1);
for(i=0;i<num;i++)
{
TEMP1=DS18B20_Get_Temp1(i);
}
if( DHT11_Read_TempAndHumidity ( & DHT11_Data ) == SUCCESS)
{
HUM1=DHT11_Data.humi_int;
}
if(NRF24L01_TxPacket(rx_buf)==TX_OK)
{
GPIO_ResetBits(GPIOC,GPIO_Pin_13);
}
else
{
GPIO_SetBits(GPIOC,GPIO_Pin_13);
DelayMs(100);
GPIO_ResetBits(GPIOC,GPIO_Pin_13);
DelayMs(100);
}
rx_buf[0]=TEMP1>>8;//溫度
rx_buf[1]=TEMP1;
rx_buf[2]=HUM1>>8;//溼度
rx_buf[3]=HUM1;
rx_buf[4]=adc_B0_U>>8;//光照強度
rx_buf[5]=adc_B0_U;
rx_buf[6]=adc_A5_i>>8;//土壤水分
rx_buf[7]=adc_A5_i;
rx_buf[8]=adc_A7_i>>8;//空氣質量
rx_buf[9]=adc_A7_i;
rx_buf[10]=adc_A1_U>>8;//雨情
rx_buf[11]=adc_A1_U;
adc_A5_i = ADC_ConvertedValue[0]*33000/4096;
adc_A7_i = ADC_ConvertedValue[1]*33000/4096;
adc_A1_U = ADC_ConvertedValue[2]*33000/4096;
adc_B0_U = ADC_ConvertedValue[3]*33000/4096;
///////////溫度顯示//////////////////////
string = my_itoa(TEMP1);
OLED_ShowStr(0,0,TEMP,1);
OLED_ShowStr(40,0,string,1);
///////////溼度顯示//////////////////////
string = my_itoa(HUM1);
OLED_ShowStr(0,1,HUM,1);
OLED_ShowStr(40,1,string,1);
///////////光強顯示//////////////////////
// adc_A5_i=adc_A5_i*30.3/50;
string = my_itoa(adc_B0_U);
OLED_ShowStr(0,2,CDQ,1);
OLED_ShowStr(40,2,string,1);
///////////土壤水分顯示//////////////////////
// adc_A7_i= adc_A7_i*30.3/50;
string = my_itoa(adc_A5_i);//32991:最乾燥 (32991-採集值)/32991=水分 %
OLED_ShowStr(0,3,SHUM,1);
OLED_ShowStr(40,3,string,1);
///////////空氣質量顯示////////////////////// 10--1000ppm
string = my_itoa(adc_A7_i); //
OLED_ShowStr(0,4,PSI,1);
OLED_ShowStr(40,4,string,1);
///////////雨情顯示//////////////////////
string = my_itoa(adc_A1_U);//32991:雨情最小即未下雨 (32991-採集值)/32991=雨情 %
OLED_ShowStr(0,5,RCD,1);
OLED_ShowStr(40,5,string,1);
OLED_ShowStr(25,6,"First node",2);
LED2_TOGGLE;
}
}
監測終端主程序
#include "sys.h"
#include "sysitck.h"
#include "led.h"
#include "nrf24l01.h"
#include "adc.h"
#include "OLED_I2C.h"
#include "beep.h"
#include "bsp_adc.h"
#include "bsp_GeneralTim.h"
#include "key.h"
#define uint unsigned int
#define uchar unsigned char
void num_char(uint x);
////////////////////////////////
uchar adc1_num[ ]="12345";
uint ADC_ConvertedValueLocal[NOFCHANEL];
extern __IO uint16_t ADC_ConvertedValue[NOFCHANEL];
char *reverse(char *s)
{
char temp;
char *p = s; //p指向s的頭部
char *q = s; //q指向s的尾部
while(*q)
++q;
q--;
//交換移動指針,直到p和q交叉
while(q > p)
{
temp = *p;
*p++ = *q;
*q-- = temp;
}
return s;
}
// //檢測到的傳感器ID存數
int main(void)
{
char *string = my_itoa(12700);
uchar TEMP[]="TEMP:";/*溫度 ℃*/
uchar HUM[]="HUM:"; /*溼度 %Rh*/
uchar CDQ[]="CDQ:"; /*光強 lux*/
uchar SHUM[]="SHUM:";/*土壤水分 %*/
uchar PSI[]="PSI:";/*空氣質量 ppm*/
uchar RCD[]="RCD:";/*雨情 mm*/
int adc_A5_i,adc_A7_i,adc_B0_U,adc_B1_U;
int duty_set=10,fre_set=100;
int m=0;//節點數
int i=0;
int T=0;
u8 rx_buf[40];
u16 TEMP1=0,HUM1=0,CDQ1=0,SHUM1=0,PSI1=0,RCD1=0;//
u16 TEMP2=0,HUM2=0,CDQ2=0,SHUM2=0,PSI2=0,RCD2=0;//
SystemInit();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
LED_GPIO_Config();
Key_GPIO_Config();
NRF24L01_Init();
adc_init();
I2C_Configuration();
OLED_Init();
OLED_Fill(0xFF);
delay_ms(1000);
OLED_Fill(0x00);
while(NRF24L01_Check()) //檢測NRF24L01是否存在
{
GPIO_SetBits(GPIOC,GPIO_Pin_13);
OLED_ShowStr(0,1,(unsigned char*)"NRF Error",1);
OLED_ShowStr(0,2,(unsigned char*)"Please chect NRF",1);
}
while(1)
{
// key_scan();
digitalToggle(LED3_GPIO_PORT,LED3_GPIO_PIN);
delay_ms(10);
digitalToggle(LED2_GPIO_PORT,LED2_GPIO_PIN);
////////第一節點環境信息接收///////
TEMP1=rx_buf[0]<<8|rx_buf[1];//溫度
HUM1=rx_buf[2]<<8|rx_buf[3];//溼度
CDQ1=rx_buf[4]<<8|rx_buf[5];//光照強度
SHUM1=rx_buf[6]<<8|rx_buf[7];//土壤水分
PSI1=rx_buf[8]<<8|rx_buf[9];//空氣質量
RCD1=rx_buf[10]<<8|rx_buf[11];//雨情
////////第二節點環境信息接收///////
TEMP2=rx_buf[0]<<8|rx_buf[1];//溫度
HUM2=rx_buf[2]<<8|rx_buf[3];//溼度
CDQ2=rx_buf[4]<<8|rx_buf[5];//光照強度
SHUM2=rx_buf[6]<<8|rx_buf[7];//土壤水分
PSI2=rx_buf[8]<<8|rx_buf[9];//空氣質量
RCD2=rx_buf[10]<<8|rx_buf[11];//雨情
if(TEMP1>30|TEMP2>30)
{
BEEP_Init();
Sound(50);
}
if(HUM1>70|HUM2>70)
{
BEEP_Init();
Sound(50);
}
if(m==0)
{
for(i=0;i<6;i++)
{
OLED_ShowCN(18+i*16,0,i);//個人信息
}
for(i=0;i<4;i++)
{
OLED_ShowCN1(26+i*16,2,i);//
}
for(i=0;i<3;i++)
{
OLED_ShowCN2(36+i*16,4,i);//
}
OLED_ShowStr(24,6,"1605260141",2);
}
//////第一個節點環境信息////////
if(m==1)
{
NRF24L01_RX_Mode();
///////////溫度顯示//////////////////////
string = my_itoa(TEMP1);
OLED_ShowStr(0,0,TEMP,1);
OLED_ShowStr(40,0,string,1);
OLED_ShowCN3(90, 0, 0);
///////////溼度顯示//////////////////////
string = my_itoa(HUM1);
OLED_ShowStr(0,1,HUM,1);
OLED_ShowStr(40,1,string,1);
OLED_ShowStr(90,1,"%Rh",1);
///////////光照強度顯示//////////////////////
string = my_itoa(CDQ1);
OLED_ShowStr(0,2,CDQ,1);
OLED_ShowStr(40,2,string,1);
OLED_ShowStr(90,2,"lux",1);
///////////土壤水分顯示//////////////////////
string = my_itoa((32991-SHUM1)/32991.0*100);//32991:最乾燥(32991-採集值)/32991=水分 %值顯示
OLED_ShowStr(0,3,SHUM,1);
OLED_ShowStr(40,3,string,1);
OLED_ShowStr(90,3,"%",1);
///////////空氣質量顯示//////////////////////
string = my_itoa(990/32991.0*PSI1); //32991:最大采集電壓 10-1000ppm 990/32991*採集電壓值= ppm
OLED_ShowStr(0,4,PSI,1);
OLED_ShowStr(40,4,string,1);
OLED_ShowStr(90,4,"ppm",1);
///////////雨情顯示//////////////////////
string = my_itoa((32991-RCD1)/32991.0*100);//32991:未下雨 (32991-採集值)/32991=雨情 %值顯示
OLED_ShowStr(0,5,RCD,1);
OLED_ShowStr(40,5,string,1);
OLED_ShowStr(90,5,"mm",1);
OLED_ShowStr(25,6,"First node",2);
}
//////第二個節點環境信息////////
if(m==2)
{
NRF24L01_RX_Mode1();
///////////溫度顯示//////////////////////
string = my_itoa(TEMP2);
OLED_ShowStr(0,0,TEMP,1);
OLED_ShowStr(40,0,string,1);
OLED_ShowCN3(90, 0, 0);
///////////溼度顯示//////////////////////
string = my_itoa(HUM2);
OLED_ShowStr(0,1,HUM,1);
OLED_ShowStr(40,1,string,1);
OLED_ShowStr(90,1,"%Rh",1);
///////////光照強度顯示//////////////////////
string = my_itoa((CDQ2));
OLED_ShowStr(0,2,CDQ,1);
OLED_ShowStr(40,2,string,1);
OLED_ShowStr(90,2,"lux",1);
///////////土壤水分顯示//////////////////////
string = my_itoa((32991-SHUM2)/32991.0*100);
OLED_ShowStr(0,3,SHUM,1);
OLED_ShowStr(40,3,string,1);
OLED_ShowStr(90,3,"%",1);
///////////空氣質量顯示//////////////////////
string = my_itoa(990/32991.0*PSI2);
OLED_ShowStr(0,4,PSI,1);
OLED_ShowStr(40,4,string,1);
OLED_ShowStr(90,4,"ppm",1);
///////////雨情顯示//////////////////////
string = my_itoa((32991-RCD2)/32991.0*100);
OLED_ShowStr(0,5,RCD,1);
OLED_ShowStr(40,5,string,1);
OLED_ShowStr(90,5,"mm",1);
OLED_ShowStr(25,6,"Second node",2);
}
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)==0)
{
delay_ms(20);
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)==0)
m++;
if(m>2)m=1;
OLED_Fill(0x00);
while(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6));
}
if(NRF24L01_RxPacket(rx_buf)==0)
{
GPIO_ResetBits(GPIOC,GPIO_Pin_13);
}
}
}
由於程序篇幅較長,這裏不一一展示,後續會將完整代碼上傳此博客。
最後,大家對程序有什麼疑問或建議均可留言,感謝大家支持!!!