抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

功能介绍

  1. STM32单片机通过DHT11温湿度传感器采集温湿度

  2. 通过模数转换器ADC采集光敏电阻分压后的电压,然后转成光照强度

  3. 串口接收ESP8266的信息,提取出时间、天气信息,以及报警温度、最低光照信息(来自APP,用于控制蜂鸣器、led的开闭)

  4. 当室内温度超过报警温度时,蜂鸣器启动;当室内光照较暗(低于设定的最低光照)时,led点亮

总体框图如下:

image-20211116235243617

具体实现

完整代码开源地址-Gitee

完整代码开源地址-Github

采集温度、湿度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//从DHT11读取一次数据,存储到参数变量temp、humi中
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
u8 DHT11_Read_Data(u8 *temp,u8 *humi)
{
u8 buf[5];
u8 i;
DHT11_Rst();
if(DHT11_Check()==0)
{
for(i=0;i<5;i++)//读取40位数据
{
buf[i]=DHT11_Read_Byte();
}
if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
{
*humi=buf[0];
*temp=buf[2];
}

}else return 1;
return 0;
}
color:green DHT11相关代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//DHT11初始化 
//返回0:初始化成功,1:失败
u8 DHT11_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,ENABLE);

GPIO_InitStructure.GPIO_Pin=DHT11;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIO_DHT11,&GPIO_InitStructure);
GPIO_SetBits(GPIO_DHT11,DHT11); //拉高

DHT11_Rst();
return DHT11_Check();
}

//复位DHT11
void DHT11_Rst()
{
DHT11_IO_OUT(); //SET OUTPUT
DHT11_DQ_OUT=0; //拉低DQ
delay_ms(20); //拉低至少18ms
DHT11_DQ_OUT=1; //DQ=1
delay_us(30); //主机拉高20~40us
}
//等待DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
u8 DHT11_Check()
{
u8 retry=0;
DHT11_IO_IN();//SET INPUT
while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~50us
{
retry++;
delay_us(1);
};
if(retry>=100)return 1;
else retry=0;
while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~50us
{
retry++;
delay_us(1);
};
if(retry>=100)return 1;
return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//从DHT11读取一个位
//返回值:1/0
u8 DHT11_Read_Bit(void)
{
u8 retry=0;
while(DHT11_DQ_IN&&retry<100)//等待变为低电平 12-14us 开始
{
retry++;
delay_us(1);
}
retry=0;
while(!DHT11_DQ_IN&&retry<100)//等待变高电平 26-28us表示0,116-118us表示1
{
retry++;
delay_us(1);
}
delay_us(40);//等待40us
if(DHT11_DQ_IN)return 1;
else return 0;
}

//从DHT11读取一个字节
//返回值:读到的数据
u8 DHT11_Read_Byte(void)
{
u8 i,dat;
dat=0;
for (i=0;i<8;i++)
{
dat<<=1;
dat|=DHT11_Read_Bit();
}
return dat;
}

采集光照

通过ADC3模数转换器采集电压(为模拟量)

1
2
3
4
5
6
7
8
9
10
11
12
13
u8 Lsens_Get_Val(void)
{
u32 temp_val=0;
u8 t;
for(t=0;t<LSENS_READ_TIMES;t++)
{
temp_val+=Get_ADC3(ADC_Channel_6); //读取ADC值
delay_ms(5);
}
temp_val/=LSENS_READ_TIMES;//得到平均值
if(temp_val>4000)temp_val=4000;
return (u8)(100-(temp_val/40));
}
color:green ADC3初始化及读取数据代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//初始化光敏传感器
void Lsens_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);//使能PORTF时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE ); //使能ADC3通道时钟
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3, ENABLE);//ADC复位
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3, DISABLE);//复位结束

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;//PF8 anolog输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOF, &GPIO_InitStructure);

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

ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式: 独立模式
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(ADC3, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器

ADC_Cmd(ADC3, ENABLE); //使能指定的ADC3

ADC_ResetCalibration(ADC3); //使能复位校准

while(ADC_GetResetCalibrationStatus(ADC3)); //等待复位校准结束

ADC_StartCalibration(ADC3); //开启AD校准

while(ADC_GetCalibrationStatus(ADC3)); //等待校准结束
}

//获得ADC3某个通道的值
//ch:通道值 0~16
//返回值:转换结果
u16 Get_ADC3(u8 ch)
{
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannelConfig(ADC3, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC3,ADC通道,采样时间为239.5周期

ADC_SoftwareStartConvCmd(ADC3, ENABLE); //使能指定的ADC3的软件转换启动功能

while(!ADC_GetFlagStatus(ADC3, ADC_FLAG_EOC ));//等待转换结束

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


//读取Light Sens的值
//0~100:0,最暗;100,最亮
u8 Lsens_Get_Val(void)
{
u32 temp_val=0;
u8 t;
for(t=0;t<LSENS_READ_TIMES;t++)
{
temp_val+=Get_ADC3(ADC_Channel_6); //读取ADC值
delay_ms(5);
}
temp_val/=LSENS_READ_TIMES;//得到平均值
if(temp_val>4000)temp_val=4000;
return (u8)(100-(temp_val/40));
}

串口(USART2)获取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
char USART2_RX_BUF[RX_BUF_MAX_LEN];
u16 USART2_RX_STA = 0;

void USART2_IRQHandler(void){
u8 r;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断
{
r = USART_ReceiveData(USART2);//(USART2->DR); //读取接收到的数据
if((USART2_RX_STA&0x8000)==0)//接收未完成
{
if(USART2_RX_STA&0x4000)//接收到了0x0d(即'\r')
{
if(r!=0x0a)USART2_RX_STA=0;//接收错误(即没有接收到'\n'),重新开始

else {

//USART2_RX_STA|=0x8000; //接收完成了 (接收到'\r'后接收到'\n')
USART2_RX_STA = 0; //接收到\n, 重新等待下一次接收
//PC_Usart("%s", USART2_RX_BUF);

}
}
else //还没收到0X0D(即'\r')
{
if(r==0x0d)USART2_RX_STA|=0x4000; //如果接收到\r, 让USART2_RX_STA的14位置1
else
{
USART2_RX_BUF[USART2_RX_STA&0X3FFF]=r;
USART2_RX_STA++;
if(USART2_RX_STA>(RX_BUF_MAX_LEN-1))USART2_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}

}
color:green USART2初始化等代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void usart2_init( void )
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA2
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA

//USART2 初始化设置
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(USART2, &USART_InitStructure); //初始化串口2

USART_ClearFlag(USART2, USART_FLAG_TC);

USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启串口接受和总线空闲中断
//USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);

USART_Cmd(USART2, ENABLE); //使能串口2

//USART2 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
}


串口数据处理

提取时间、天气信息;为了方便,ESP8266发给STM32的数据都是用逗号隔开的,如 时间,天气,气温,湿度,……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

struct WeatherData processWdata3(char data[]){
//char data[] = "2022-22-22 33:33:33,yin,47,48,<=7";
u8 i=0, j=0, i0=0, k=0;
u8 ind=0, jnd=0;

int slen = strlen(data);
struct WeatherData weather;

//初始化
for(ind=0; ind<8; ind++){
switch(ind){
case 0: {
for(jnd=0; jnd<20; jnd++){
weather.datetime[jnd]='\0';
}
};break;
case 1: {
for(jnd=0; jnd<10; jnd++){
weather.city[jnd]='\0';
}
};break;
case 2: {
for(jnd=0; jnd<10; jnd++){
weather.humi[jnd]='\0';
}
};break;

case 3: {
for(jnd=0; jnd<10; jnd++){
weather.temp[jnd]='\0';
}
};break;
case 4: {
for(jnd=0; jnd<10; jnd++){
weather.weather[jnd]='\0';
}
};break;
case 5: {
for(jnd=0; jnd<10; jnd++){
weather.windpower[jnd]='\0';
}
};break;
case 6: {
for(jnd=0; jnd<10; jnd++){
minLsens_str[jnd]='\0';
}
};break;
case 7: {
for(jnd=0; jnd<10; jnd++){
alarmTemp_str[jnd]='\0';
}
};break;

}


}
strcpy(weather.city, "西安");
for(i=0; i<slen; i++){
if(data[i]==',') {
i0++;
for(j=k; j<i; j++){

if(i0==1) weather.datetime[j-k]=data[j];
else if(i0==2) weather.weather[j-k]=data[j];
else if(i0==3) weather.temp[j-k]=data[j];
else if(i0==4) weather.humi[j-k]=data[j];
else if(i0==5) weather.windpower[j-k]=data[j];
else if(i0==6) alarmTemp_str[j-k]=data[j];
else if(i0==7) minLsens_str[j-k]=data[j];
}
k=i+1;
}

}
return weather;
}

发送数据

通过定时器TIM2定时1s发送一次数据(json字符串格式)给ESP8266模块

1
2
3
4
5
6
7
8
9
10
void TIM2_IRQHandler(void){

if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET){

ESP8266_Usart("{\"temp\":\"%d\",\"humi\":\"%d\",\"light\":\"%d\",\"ledsta\":\"%d\",\"beepsta\":\"%d\"}\r\n", dhtData.temp, dhtData.humi, lsens, ledSta, beepSta);//输出【光照、温度、湿度、led、beep状态】到esp8266,再发送给手机

TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}

color:green 初始化TIM2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//初始化TIM2
void TIM2_init(u16 arr, u16 psc){
TIM_TimeBaseInitTypeDef TIM_Structure;
NVIC_InitTypeDef NVIC_Structure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

//延时时间(单位:s):arr/(72000000/psc)
TIM_Structure.TIM_Period = arr - 1; //装载值
TIM_Structure.TIM_Prescaler = psc-1; //分频系数
TIM_Structure.TIM_ClockDivision = 0;
TIM_Structure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_Structure);

TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //开启定时器TIM2中断
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
NVIC_Structure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_Structure.NVIC_IRQChannelPreemptionPriority = 6; //抢占优先级
NVIC_Structure.NVIC_IRQChannelSubPriority = 4; //子优先级
NVIC_Structure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_Structure);

TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_Cmd(TIM2, ENABLE);
}

LED与蜂鸣器控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void ledBeepOnOrNot(u8 lsens, u8 mlsens, u8 t, u8 alarmT){  //是否启动LED、BEEP
if(lsens < mlsens){
LED1 = 0; //点亮
ledSta = 1;
}

else{
LED1 = 1; //熄灭
ledSta = 0;
}

if(t>alarmT){
BEEP=1; //叫
beepSta = 1;
}
else{
BEEP=0; //不叫
beepSta = 0;
}
}

效果预览

image-20211117003640156

评论