最近用手机做了51单片机驱动8266APP控制火灾报警功能的板。下面给大家介绍一下,有需要的朋友可以私信。
由于时间有限,我会慢慢更新这部分。
先上整体图
手机app截图
1.功能描述。
a、使用DS18B读取温度,并在app上显示。超过26度报警,报警灯亮,蜂鸣器响,app报警灯变红。
b、使用MQ-2.阅读烟雾浓度,并在app超过40ppm报警,报警灯亮,蜂鸣器响,app报警灯变红。
c、用120度火焰传感器感受火焰app报警。检测到火焰后,报警灯亮,蜂鸣器响,app报警灯变红。
d、上电后,手动模式指示灯在板上和app同时点亮绿灯。这时,可以手动操作app上按上 下 左 右按钮,控制光源跟踪板旋转;可按水泵按钮,控制水泵继电器吸合,然后控制水泵动作。
e、在app按下手动按钮,切换到自动模式,自动模式指示灯在板上app同时点亮,为绿灯。此时,光源跟踪板自动跟踪光源旋转,可用手电筒模拟;对准光源后,打开水泵;水泵工作10s之后,自动关闭。之后,流程循环进行。
硬件设计。
直接上图。
没什么好说的,看图就行了。
3.软件设计。
#include <reg52.h> #include <stdio.h> #include "math.H" #include <intrins.h> #include "PCF8591.H" #define uchar unsigned char // 以后unsigned char就可以用uchar代替 #define uint unsigned int // 以后unsigned int 就可以用uint 代替 #define del 20//调速 #define ydl 20 //移动调节 #define PCF8591 0x90 //PCF8591 地址 sbit temp_led=P0^0; sbit smoke_led=P0^1; sbit fire_led=P0^2; sbit auto_led=P0^3; sbit manual_led=P0^4; sbit pump=P1^0; sbit fire1=P3^3; sbit fire2=P3^4; sbit fire3=P3^5; sbit fire4=P3^6; sbit fire5=P3^7; sbit DQ = P1^1; // 温度传感器的引脚 sbit ADC_CS = P1^2; // ADC0832的CS引脚 sbit ADC_CLK = P1^3; // ADC0832的CLK引脚 sbit ADC_DAT = P1^4; // ADC0832的DI/DO引脚 sbit Buzzer_P = P0^5; // 蜂鸣器 bit up_flag; bit down_flag; bit left_flag; bit right_flag; bit pump_flag; bit AM_flag=0; bit Auto_start; uchar Motor_Up[4]={0x08,0x04,0x02,0x01}; uchar Motor_Down[4]={0x01,0x02,0x04,0x08}; uchar Motor_Left[4]={0x10,0x20,0x40,0x80}; uchar Motor_Right[4]={0x80,0x40,0x20,0x10}; uchar AD_CHANNEL; uchar AD_Up,AD_Down,AD_Left,AD_Right; uint i; uchar dat[20]; uchar temp_flag; // 温度报警标志位 uchar smoke_flag; // 烟雾报警标志位 uchar fire_flag; // 火焰报警标志位 int temp; // 保存温度值 uchar smoke; // 保存烟雾值 uchar Smoke_Alarm=30; // 烟雾报警值 int Temp_Alarm=26; // 温度报警值 uint time_20ms=0;//定时器计数 bit disFlag ; //显示标志 unsigned char Recive_table[15]; /*********************************************************/ // 毫秒延迟函数,time毫秒数要延迟 /*********************************************************/ void DelayMs(uint time) { uint a,b; for(a=0;a<time;a ) for(b=0;b<112;b ); } /*********************************************************/ // 延时15微秒 /*********************************************************/ void Delay15us(void) { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); } /*********************************************************/ // 手动转动 /*********************************************************/ void manual_move(void) { uchar i,j; if(up_flag==1)//手动上转 { for(i=0;i<ydl;i ) { for(j=0;j<4;j ) { P2=Motor_Up[j]; DelayMs(del); } } } if(down_flag==1)//手动下转 { for(i=0;i<ydl;i ) { for(j=0;j<4;j ) { P2=Motor_Down[j]; DelayMs(del); } } } if(left_flag==1)//手动左转 { for(i=0;i<ydl;i ) { for(j=0;j<4;j ) { P2=Motor_Left[j]; DelayMs(del); } } } if(right_flag==1)//手动右转 { for(i=0;i<ydl;i ) { for(j=0;j<4;j ) { P2=Motor_Right[j]; DelayMs(del); } } } if(pump_flag==1)pump=0;else pump=1; } /*********************************************************/ // 自动转动 /*********************************************************/ void auto_move(void) { uchar i,j; for(i=0;i<8;i ) { switch(AD_CHANNEL) { case 0: PCF8591_ISendByte(PCF8591,0x41); AD_Up=PCF8591_IRcvByte(PCF8591)*2; //ADC0 模数转换1 break; case 1: PCF8591_ISendByte(PCF8591,0x42); AD_Down=PCF8591_IRcvByte(PCF8591)*2; //ADC1 模数转换2 break; case 2: PCF8591_ISendByte(PCF8591,0x43); AD_Left=PCF8591_IRcvByte(PCF8591)*2; //ADC2 模数转换3 break; case 3: PCF8591_ISendByte(PCF8591,0x40); AD_Right=PCF8591_IRcvByte(PCF8591)*2; //ADC3 模数转换4 break; } if( AD_CHANNEL>3) { AD_CHANNEL=0; } } if((AD_Up<AD_Down)&&(AD_Down-AD_Up>25)//上转 { for(i=0;i<ydl;i ) { for(j=0;j<4;j ) { P2=Motor_Up[j]; DelayMs(del); } } } // if((AD_Up>AD_Down)&&(AD_Up-AD_Down>25)//下转 { for(i=0;i<ydl;i ) { for(j=0;j<4;j ) { P2=Motor_Down[j]; DelayMs(del); } } } if((AD_Left<AD_Right)&&(AD_Right-AD_Left>25)//左转 { for(i=0;i<ydl;i ) { for(j=0;j<;j++)
{
P2=Motor_Left[j];
DelayMs(del);
}
}
}
//
if((AD_Left>AD_Right)&&(AD_Left-AD_Right>25))//右转
{
for(i=0;i<ydl;i++)
{
for(j=0;j<4;j++)
{
P2=Motor_Right[j];
DelayMs(del);
}
}
}
/
//if((AD_Left!=0)&&(AD_Right!=0)&&(AD_Up!=0)&&(AD_Down!=0)&&((abs(AD_Left-AD_Right)<5)&&(abs(AD_Up-AD_Down)<5)))//旋转停止判断
if(((AD_Left-AD_Right<=25)||(AD_Right-AD_Left<=25))&&((AD_Up-AD_Down<=25)||(AD_Down-AD_Up<=25)))//旋转停止判断
{
DelayMs(200);
pump=0;//当旋转停止后,自动开启水泵
DelayMs(10000);
pump=1;//水泵开启10S后,自动关闭水泵
}
}
/*********************************************************/
// 复位DS18B20(初始化)
/*********************************************************/
void DS18B20_ReSet(void)
{
uchar i;
DQ=0;
i=240;
while(--i);
DQ=1;
i=30;
while(--i);
while(~DQ);
i=4;
while(--i);
}
/*********************************************************/
// 向DS18B20写入一个字节
/*********************************************************/
void DS18B20_WriteByte(uchar dat)
{
uchar ii;
uchar btmp;
for(ii=0;ii<8;ii++)
{
btmp=0x01;
btmp=btmp<<ii;
btmp=btmp&dat;
if(btmp>0) // 写1
{
DQ=0;
Delay15us();
DQ=1;
Delay15us();
Delay15us();
Delay15us();
Delay15us();
}
else // 写0
{
DQ=0;
Delay15us();
Delay15us();
Delay15us();
Delay15us();
DQ=1;
Delay15us();
}
}
}
/*********************************************************/
// 读取温度值
/*********************************************************/
int DS18B20_ReadTemp(void)
{
uchar j;
int b,temp=0;
EA=0;
DS18B20_ReSet(); // 产生复位脉
DS18B20_WriteByte(0xcc); // 忽略ROM指令
DS18B20_WriteByte(0x44); // 启动温度转换指令
DS18B20_ReSet(); // 产生复位脉
DS18B20_WriteByte(0xcc); // 忽略ROM指令
DS18B20_WriteByte(0xbe); // 读取温度指令
for(j=0;j<16;j++) // 读取温度数量
{
DQ=0;
_nop_();
_nop_();
DQ=1;
Delay15us();
b=DQ;
Delay15us();
Delay15us();
Delay15us();
b=b<<j;
temp=temp|b;
}
temp=temp*0.0625; // 合成温度值
if(temp<0)
temp=0;
EA=1;
return (temp); // 返回检测到的温度值
}
/*********************************************************/
// ADC0832的时钟脉冲
/*********************************************************/
void WavePlus()
{
_nop_();
ADC_CLK = 1;
_nop_();
ADC_CLK = 0;
}
/*********************************************************/
// 获取指定通道的A/D转换结果
/*********************************************************/
uchar Get_ADC0832()
{
uchar i;
uchar dat1=0;
uchar dat2=0;
ADC_CLK = 0; // 电平初始化
ADC_DAT = 1;
_nop_();
ADC_CS = 0;
WavePlus(); // 起始信号
ADC_DAT = 1;
WavePlus(); // 通道选择的第一位
ADC_DAT = 0;
WavePlus(); // 通道选择的第二位
ADC_DAT = 1;
for(i=0;i<8;i++) // 第一次读取
{
dat1<<=1;
WavePlus();
if(ADC_DAT)
dat1=dat1|0x01;
else
dat1=dat1|0x00;
}
for(i=0;i<8;i++) // 第二次读取
{
dat2>>= 1;
if(ADC_DAT)
dat2=dat2|0x80;
else
dat2=dat2|0x00;
WavePlus();
}
_nop_(); // 结束此次传输
ADC_DAT = 1;
ADC_CLK = 1;
ADC_CS = 1;
if(dat1==dat2) // 返回采集结果
return (dat1);
else
return 0;
}
/*********************************************************/
// 报警判断
/*********************************************************/
void AlarmJudge(uchar dat1, int dat2)
{
/*火焰报警判断*/
if((fire1==1)||(fire2==1)||(fire3==1)||(fire4==1)||(fire5==1))
{
fire_led=0;
fire_flag=1;
}
else
{
fire_led=1;
fire_flag=0;
}
/*烟雾报警判断*/
if(dat1>Smoke_Alarm)
{
smoke_led=0;
smoke_flag=1;
}
else
{
smoke_led=1;
smoke_flag=0;
}
/*温度报警判断*/
if(dat2>Temp_Alarm)
{
temp_led=0;
temp_flag=1;
}
else
{
temp_led=1;
temp_flag=0;
}
/*蜂鸣器报警判断*/
if((temp_flag==1)||(smoke_flag==1)||(fire_flag==1))
{
Buzzer_P=0;
//Motor_P=0;
Auto_start=1;
}
else
{
Buzzer_P=1;
//Motor_P=1;
Auto_start=0;
}
}
/*********************************************************/
// ATK-ESP8266向上位机发送数据
/*********************************************************/
void SendDat(int temp,uchar smoke)
{
dat[0]=(char)(temp%100/10)+48;//发送温度值的十位
dat[1]=(char)(temp%10)+48;//发送温度值的个位
dat[2]=(char)temp_flag+48;//发送温度值报警标志位
dat[3]=(char)fire_flag+48;//发送火焰报警标志位
dat[4]=(char)smoke_flag+48;//发送烟雾浓度报警标志位
dat[5]=(char)(smoke%100/10)+48;//发送烟雾浓度值的十位
dat[6]=(char)(smoke%10)+48;//发送烟雾浓度值的个位
}
/*********************************************************/
// 初始化串口
/*********************************************************/
void Init_uart(void) {
TMOD = TMOD | 0x20; //???1?????2 8?????
SCON = SCON | 0x50; //??1?????1 10????? REN=1????
TH1 = 0xFd; //???1??
TL1 = TH1;
TR1 = 1; //???1????
EA =1; //????
ES =1; //
}
/*********************************************************/
// 串口发送一个字节数据
/*********************************************************/
void Uart_SendByteData(uchar a)
{
SBUF = a;
while(TI==0);
TI=0;
}
/*********************************************************/
// 串口发送字符串
/*********************************************************/
void Uart_SendStrData(uchar *s)
{
while(*s!='\0')
{
Uart_SendByteData(*s);
s++;
}
}
/*********************************************************/
// ATK-ESP8266初始化
/*********************************************************/
void Init_ESP01S(){
DelayMs(50);
Uart_SendStrData("AT+CIPMUX=1\r\n"); // ?????
DelayMs(5);
Uart_SendStrData("AT+CIPSERVER=1,8080\r\n"); // ?????,??8080??
DelayMs(5);
}
/*********************************************************/
// 定时器0初始化
/*********************************************************/
void Init_Timer0(void)
{
TMOD |= 0x01; //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响
TH0=(65536-20000)/256; //重新赋值 20ms
TL0=(65536-20000)%256;
EA=1; //总中断打开
ET0=1; //定时器中断打开
TR0=1; //定时器开关打开
}
/*********************************************************/
// 定时器0定时20ms
/*********************************************************/
void Timer0_isr(void) interrupt 1
{
TH0=(65536-20000)/256; //重新赋值 20ms
TL0=(65536-20000)%256;
time_20ms++;
if(time_20ms%5==0)//定时时间到
{
disFlag=1;//标志位置位
}
}
/*********************************************************/
// 主程序
/*********************************************************/
void main(){
Init_Timer0();
Init_uart();
Init_ESP01S();
while(DS18B20_ReadTemp()==85) // 等待传感器初始化完成
{
DelayMs(10);
}
while(1)
{
smoke=Get_ADC0832(); // 获取烟雾的浓度值
smoke=smoke/2.2; // 调整浓度值,使其在0-100之间变化
if(smoke>100)smoke=99;
temp=DS18B20_ReadTemp(); // 读取温度值
AlarmJudge(smoke,temp); // 报警判断
if(AM_flag==1)
{
manual_led=1;
auto_led=0;
if(Auto_start==1)auto_move();
}
else
{
manual_led=0;
auto_led=1;
manual_move();
}
//sprintf(dat,"%d%d%d%d%d%d",temp,(int)temp_flag,(int)fire_flag,(int)smoke_flag,(int)smoke);
SendDat(temp,smoke);
if(disFlag==1)
{
disFlag=0;
Uart_SendStrData("AT+CIPSEND=0,7\r\n");
DelayMs(5);
Uart_SendStrData(dat);
DelayMs(5);
}
}
}
/*********************************************************/
// 接收数据中断服务程序
/*********************************************************/
void UART_Interrupt(void) interrupt 4
{
if(RI == 1)
{
RI = 0; //清除串口接收标志位
Recive_table[i]=SBUF;
if(Recive_table[0]=='+')
{
i++;
}
else
{
i=0;
}
if(i==10)
{
i=0;
switch(Recive_table[9])//+IPD,0,1:0
{
case '1':{up_flag = 1;break;}
case '2':{down_flag = 1;break;}
case '3':{left_flag = 1;break;}
case '4':{right_flag = 1;break;}
case '5':{AM_flag=1;break;}//自动标志位,AM_flag=1
case '6':{AM_flag=0;break;}//手动标志位,AM_flag=0
case '7':{pump_flag=1;break;}//
case '9':{up_flag = 0;down_flag = 0;left_flag = 0;right_flag = 0;pump_flag=0;break;}
}
}
}
}
对于程序,我不做全部解释,有疑问的朋友可以私信或者评论。
我这里只把我遇到的问题说一下:
a、DS18B20读取温度程序。
如下
/*********************************************************/
// 读取温度值
/*********************************************************/
int DS18B20_ReadTemp(void)
{
uchar j;
int b,temp=0;
EA=0;
DS18B20_ReSet(); // 产生复位脉
DS18B20_WriteByte(0xcc); // 忽略ROM指令
DS18B20_WriteByte(0x44); // 启动温度转换指令
DS18B20_ReSet(); // 产生复位脉
DS18B20_WriteByte(0xcc); // 忽略ROM指令
DS18B20_WriteByte(0xbe); // 读取温度指令
for(j=0;j<16;j++) // 读取温度数量
{
DQ=0;
_nop_();
_nop_();
DQ=1;
Delay15us();
b=DQ;
Delay15us();
Delay15us();
Delay15us();
b=b<<j;
temp=temp|b;
}
temp=temp*0.0625; // 合成温度值
if(temp<0)
temp=0;
EA=1;
return (temp); // 返回检测到的温度值
}
在程序的开始和末尾最好加上EA=0和EA=1。就是禁用总中断和开启总中断。因为DS18B20对于时序很敏感,如果不加这两个命令,读取温度会出现乱码!
b、ATK-ESP8266发送数据时,组成字符串程序。
如下为组成字符串程序
void SendDat(int temp,uchar smoke)
{
dat[0]=(char)(temp%100/10)+48;//发送温度值的十位
dat[1]=(char)(temp%10)+48;//发送温度值的个位
dat[2]=(char)temp_flag+48;//发送温度值报警标志位
dat[3]=(char)fire_flag+48;//发送火焰报警标志位
dat[4]=(char)smoke_flag+48;//发送烟雾浓度报警标志位
dat[5]=(char)(smoke%100/10)+48;//发送烟雾浓度值的十位
dat[6]=(char)(smoke%10)+48;//发送烟雾浓度值的个位
}
如下为发送程序
SendDat(temp,smoke);
if(disFlag==1)
{
disFlag=0;
Uart_SendStrData("AT+CIPSEND=0,7\r\n");
DelayMs(5);
Uart_SendStrData(dat);
DelayMs(5);
}
组成字符串程序时,也可以用sprintf函数,比如:sprintf(dat,"%d%d%d%d%d%d",temp,(int)temp_flag,(int)fire_flag,(int)smoke_flag,(int)smoke);
如果这样写,不仅会增加代码总量大小,而且sprintf用不好的话会造成错误。所以建议使用如上的字符串组成程序。
c、初始化ATK-ESP8266时,在函数开头应该延时一段时间,让板子上电时,8266自己可以有个初始化过程,否则8266会出现错误,导致连接不上8266。
/*********************************************************/
// ATK-ESP8266初始化
/*********************************************************/
void Init_ESP01S(){
DelayMs(50);
Uart_SendStrData("AT+CIPMUX=1\r\n"); // ?????
DelayMs(5);
Uart_SendStrData("AT+CIPSERVER=1,8080\r\n"); // ?????,??8080??
DelayMs(5);
}
以上就是我的描述,想进一步了解的朋友私信我。谢谢!