这是我本科毕业设计的主要内容,毕设已于2020年5月结束,后来就一直想着花了这么久辛辛苦苦做的毕设如果就这么结束了很亏,说不定会有更多的人也需要这些功能,所以就抽空在毕业以后的暑假花点时间写在这里。
整个系统功能主要是温湿度监测、传输和显示。
温湿度监测用的是dht11或dht12,11精度略低,同时只能使用单总线协议,12不仅可以用单总线(也就是跟11完全兼容)同时还能用iic,监测精度也高一点。
我用的是单总线,说好听点是为了兼容11和12,说难听点就是没把iic弄明白,不会用,到头来还是发现单总线好用,然后就用单总线了。
dht将数据接收以后,给单片机,单片机将它打包以后,用异步串行传输给需要显示的地方,可以用有线,也能用无线,我用的是zigbee的无线。zigbee真的特别特别好用,它的发送端接收到的有线数据,经过内部的一番操作,在100米以外的接收端,也就会把一样的内容用uart发出来,直接用另一台单片机来接收就OK
我用的是1602显示,如果有需要报警的地方,旁边LED和蜂鸣器会报警。至于报警,是在跟dht连接的单片机上面用define定义上去的,报警功能可有可无,算是拓展功能。
温湿度监测端的主控板包括主控芯片的最小系统(芯片、复位、振荡、电源、存储)、和一些必要的指示器,DHT的单总线DATA和P3.6引脚连接,P0.0接D1(绿色LED),P0.1接D2(红色LED),采用共阴极接法,两LED阳极经50Ω电阻接5V的VCC。当温度和湿度均正常时亮绿灯,其中一个不正常时绿灯和红灯都亮,当温度和湿度均不正常时仅亮红灯。这样的设置可以简单显示当前温湿度是否符合要求,用于发现异常后及时寻找故障点。P3.1为异步串行数据发送端口,用于传输数据给通信模块。
流程图如下:
需要四个自锁开关是选择当前监测点序号。四个按钮按照1234的顺序分别代表二进制从高到低的四位。
若需要进一步节约成本,可以直接将P1.0-P1.3这四个引脚与GND或VCC焊接,可以节约四个开关和四个电阻的使用,缺点是更换监测点序号将变得困难,适用于固定性强、每一个装置只针对一个监测点的情况。比如将P1.0、P1.1、P1.2均接VCC,P1.3接GND,则此监测点定义为1+2+4=7。
#include
#include
#define uchar unsigned char
#define uint unsigned int
sbit Data=P3^6; //DHT11Êý¾ÝÏß
sbit cjd0 = P1^0;
sbit cjd1 = P1^1;
sbit cjd2 = P1^2;
sbit cjd3 = P1^3; //P1µÄµÚËÄλÓÃÓÚÉèÖõ±Ç°²âÁ¿µãΪ¼¸ºÅ£¨¶þ½øÖÆÑ¡Ôñ£©
sbit greenlight = P0^0;
sbit redlight = P0^1;
uchar send[8]="!0000000"; //ÓÃÓÚ·¢Ë͵ÄÊý¾Ý
uchar Ready_dat[]=" All ready "; //ÕâÀïÃ涫Î÷¿ÉÒԸģ¬µ«²»Òª³öÏÖ¸Ð̾ºÅ£¬Ò»ÃæÓ°Ïì½ÓÊÕ
int caijidian;
int m;
#define tl 0 //¿¼ÂÇ׼ȷÐÔ£¬Î¶ȲâÁ¿×îºÃÔÚ-20~60ÉãÊ϶È
#define th 20 //¿¼ÂÇ׼ȷÐÔ£¬Î¶ȲâÁ¿×îºÃÔÚ-20~60ÉãÊ϶È
#define hl 30 //¿¼ÂÇ׼ȷÐÔ£¬Êª¶È×îºÃÔÚ20~95%
#define hh 90 //¿¼ÂÇ׼ȷÐÔ£¬Êª¶È×îºÃÔÚ20~95%
int t,h;
int R_H,R_L,T_H,T_L,RH,RL,TH,TL,revise;
void DHT11_delay_us(uchar n)
{
while(--n);
}
void DHT11_delay_ms(uint z)
{
uint i,j;
for(i=z;i>0;i--)
for(j=110;j>0;j--);
}
void DHT11_start()
{
Data=1;
DHT11_delay_us(2);
Data=0;
DHT11_delay_ms(20); //ÑÓʱ18msÒÔÉÏ
Data=1;
DHT11_delay_us(30);
}
uchar DHT11_rec_byte() //½ÓÊÕÒ»¸ö×Ö½Ú
{
uchar i,dat=0;
for(i=0;i<8;i++) //´Ó¸ßµ½µÍÒÀ´Î½ÓÊÕ8λÊý¾Ý
{
while(!Data); ////µÈ´ý50usµÍµçƽ¹ýÈ¥
DHT11_delay_us(8); //ÑÓʱ60us£¬Èç¹û»¹Îª¸ßÔòÊý¾ÝΪ1£¬·ñÔòΪ0
dat<<=1; //ÒÆλʹÕýÈ·½ÓÊÕ8λÊý¾Ý£¬Êý¾ÝΪ0ʱֱ½ÓÒÆλ
if(Data==1) //Êý¾ÝΪ1ʱ£¬Ê¹dat¼Ó1À´½ÓÊÕÊý¾Ý1
dat+=1;
while(Data); //µÈ´ýÊý¾ÝÏßÀµÍ
}
return dat;
}
void DHT11_receive() //½ÓÊÕ40λµÄÊý¾Ý
{
DHT11_start();
if(Data==0) //Õâ¸öifµÄ×÷ÓÃÊÇÔÚûÓд«¸ÐÆ÷ÐźÅʱ£¬·¢ËÍ¡°all ready¡±
{
while(Data==0); //µÈ´ýÀ¸ß(²»Ì«¶®Õâ¸öwhileÓÐʲôÓã¬ÏÈ·Å×Å£©
DHT11_delay_us(40); //À¸ßºóÑÓʱ80us
R_H=DHT11_rec_byte(); //½ÓÊÕʪ¶È¸ß°Ëλ
R_L=DHT11_rec_byte(); //½ÓÊÕʪ¶ÈµÍ°Ëλ
T_H=DHT11_rec_byte(); //½ÓÊÕζȸ߰Ëλ
T_L=DHT11_rec_byte(); //½ÓÊÕζȵͰËλ
revise=DHT11_rec_byte(); //½ÓÊÕУÕýλ
DHT11_delay_us(25); //½áÊø½ÓÊÕ
if((R_H+R_L+T_H+T_L)==revise) //УÕý¼ìÑé
{
RH=R_H;
RL=R_L;
TH=T_H;
TL=T_L;
}
send[4]='0'+(TH/10);
send[5]='0'+(TH%10);
send[6]='0'+(RH/10);
send[7]='0'+(RH%10);
if(TL>=128) //ζÈΪÁãÏ£¬uartµÚÈýλҪ±ä³É0£¬ÇÒT_HÒ²Òª¸Ä³É¸ºÊý£¬±ãÓÚÏÂÃæÅжÏ
{
send[3] = 0x31;
T_H = 0-T_H;
}
else
send[3] = 0x30;
}
}
void initSBUF() //É趨¶¨Ê±Æ÷1,ÓÃÓÚ´®¿ÚÖжÏ
{
TMOD=0x20; //É趨T1¶¨Ê±Æ÷¹¤×÷·½Ê½2
TH1=0xfd; //T1¶¨Ê±Æ÷×°³õÖµ
TL1=0xfd; //T1¶¨Ê±Æ÷×°³õÖµ
TR1=1; //Æô¶¯T1¶¨Ê±Æ÷
REN=1; //ÔÊÐí´®¿Ú½ÓÊÕ
SM0=0; //É趨´®¿Ú¹¤×÷·½Ê½1
SM1=1; //É趨´®¿Ú¹¤×÷·½Ê½1
EA=1; //¿ª×ÜÖжÏ
ES=1; //¿ª´®¿ÚÖжÏ
}
void chose(void)
{
DHT11_receive();
t=T_H;
h=R_H;
if(tl<=t&&t<=th&&hl<=h&&h<=hh)
{
send[2]=0x30; greenlight = 0; redlight = 1;
}
else
if(t>th&&hl<=h&&h<=hh)
{
send[2]=0x31; greenlight = 0; redlight = 0;
}
else
if(t<tl&&hl<=h&&h<=hh)
{
send[2]=0x32; greenlight = 0; redlight = 0;
}
else
if(tl<=t&&t<=th&&h>hh)
{
send[2]=0x33; greenlight = 0; redlight = 0;
}
else
if(tl<=t&&t<=th&&h<hl)
{
send[2]=0x34; greenlight = 0; redlight = 0;
}
else
if(t>th&&h>hh)
{
send[2]=0x35; greenlight = 1; redlight = 0;
}
else
if(t<tl&&h<hl)
{
send[2]=0x36; greenlight = 1; redlight = 0;
}
else
if(t<tl&&h>hh)
{
send[2]=0x37; greenlight = 1; redlight = 0;
}
else
if(t>th&&h<hl)
{
send[2]=0x38; greenlight = 1; redlight = 0;
}
else
{
send[2]=0x40; greenlight = 1; redlight = 0;
}
}
void main()
{
cjd0=0;cjd1=0;cjd2=0;cjd3=0;
greenlight = 0;
redlight = 0;
DHT11_delay_ms(200);
greenlight = 1;
redlight = 1;
DHT11_delay_ms(200);
greenlight = 0;
redlight = 0;
DHT11_delay_ms(200);
greenlight = 1;
redlight = 1; //¿ª»úÏÔʾ
caijidian = cjd0;
caijidian += cjd1*2;
caijidian += cjd2*4;
caijidian += cjd3*8;
Ready_dat[0]=caijidian+0x30;
send[1]=caijidian+0x30;
initSBUF(); //´®¿Ú³õʼ»¯
//ÿ´ÎʹÓÃprintfº¯ÊýTIÒªÏÈÖÃ1
ES=0;
for(m=0 ;m<12 ;m++) //ÐÞ¸ÄÊä³öµÄ×Ö·ûÊýÁ¿
{
SBUF=Ready_dat[m];
while(!TI);
TI=0;
}
while(1)
{
DHT11_delay_ms(2000); //DHT11ÉϵçºóÒªµÈ´ý1SÒÔÔ½¹ý²»Îȶ¨×´Ì¬ÔÚ´ËÆڼ䲻ÄÜ·¢ËÍÈκÎÖ¸Áî
chose();
ES=0;
for(m=0 ;m<8 ;m++) //ÐÞ¸ÄÊä³öµÄ×Ö·ûÊýÁ¿
{
SBUF=send[m];
while(!TI);
TI=0;
}
// for(m=0 ;m<12 ;m++) //ÐÞ¸ÄÊä³öµÄ×Ö·ûÊýÁ¿
// {
// SBUF=Ready_dat[m];
// while(!TI);
// TI=0;
// }
}
}
代码是我从论文直接复制过来的,不知道为什么中文注释显示不了,如果需要工程文件,我上传过压缩包资源,去那里找。
此监测端发送的uart是一个包,里面的内容是我自己编的协议,如下表,用这个协议来调试非常方便(当然这就导致了可能不是最简的,但我并不要求传输的性能有多高,所以就没有这么磨这个方面)
位数 | 作用 |
---|---|
第0位 | 固定为0x21,兼具数据首端标志与校验功能 |
第1位 | 采集点代号 |
第2位 | 温湿度均正常为0,仅温度偏高为1,仅温度偏低为2,仅湿度偏高为3,仅湿度偏低为4,温湿度均偏高为5,温湿度均偏低为6,温度偏低而湿度偏高为7,湿度偏低而温度偏高为8 |
第3位 | 1代表温度为零下,0代表温度为零上,其他数字留作备用 |
第4位 | 温度十位 |
第5位 | 温度个位 |
第6位 | 湿度十位 |
第7位 | 湿度个位 |
显示温、湿度需要较长的显示字码,人机交流界面中,常用的交互方式有以下几种:LED发光管阵列、LED数码管、LCD彩色显示器。经过综合考虑,本设计采用液晶显示器1602LCD作为主显示模块。我们在日常生活中对液晶显示器并不陌生,现如今小尺寸液晶显示器已经成为市场上很多电子产品的显示器件,其功能主要是显示数字、特殊符号和简单的图形。
1602LCD显示屏的使用很灵活,可以通过简单的设计让其显示数字与字母,所以将1602用于本设计的数据显示非常合适,1602的各个管脚功能百度很多。在设计中,1602将用于循环显示各采集点的温度和湿度值,并告知用户其值是否符合预定要求,如果不符合,可以显示出偏高或偏低,具有重要的实用意义。
程序如下:
#include
#include
int write_com(unsigned char);//???????
int write_date(unsigned char);//???????
int delay(unsigned char);//??????
unsigned char x,i;
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^2;
sbit tem_y = P2^0;
sbit tem_n = P2^1;
sbit hum_y = P2^2;
sbit hum_n = P2^3;
sbit beep = P2^4;
unsigned char table[]="Designed by jbr ";
unsigned char table1[]="Anhui university ";
unsigned char table2[]=" # tem: C ";
unsigned char table3[]=" hum: % ";
unsigned char b[8]="00000000";
signed char tem[10]="!!!!!!!!!!";
unsigned char hum[10]="!!!!!!!!!!";
unsigned char alarm[10]="!!!!!!!!!!";
char a0,a1,a2,a3,a4,a5,a6,abstem;
unsigned char re_i = 0;
#define jingzhen 11059200UL /*ʹÓÃ22.1184M¾§Ìå*/
#define botelv 9600UL /*²¨ÌØÂʶ¨ÒåΪ9600*/
void serial_init()
{
SCON = 0x50; //REN=1????????,??????1
TMOD|= 0x20; //???????2
PCON|= 0x80;
TH1 = 0xFA; //baud*2 /* reload value 19200????8????1????? (11.0592)
// TH1 = 0xF3; // //baud*2 /* ???4800????8????1????? (12M)
TL1 = 0xF3;
TR1 = 1;
EN = 0;
write_com(0X38);//??16*2??,5*7??,8?????
write_com(0X0C);//?????,?????
write_com(0X06);//??????,????
write_com(0X01);//????
}
void alarming(int a)
{
if(a==0)
{
table2[15]=0x52; //R
table3[15]=0x52;
tem_y = 0;
tem_n = 1;
hum_y = 0;
hum_n = 1;
beep = 1;
}
else
{
if(a==1)
{
table2[15]=0x48; //H
table3[15]=0x52;
tem_y = 1;
tem_n = 0;
hum_y = 0;
hum_n = 1;
beep = 0;
}
else
{
if(a==2)
{
table2[15]=0x4c; //L
table3[15]=0x52;
tem_y = 1;
tem_n = 0;
hum_y = 0;
hum_n = 1;
beep = 0;
}
else
{
if(a==3)
{
table2[15]=0x52;
table3[15]=0x48;
tem_y = 0;
tem_n = 1;
hum_y = 1;
hum_n = 0;
beep = 0;
}
else
{
if(a==4)
{
table2[15]=0x52;
table3[15]=0x4c;
tem_y = 0;
tem_n = 1;
hum_y = 1;
hum_n = 0;
beep = 0;
}
else
{
if(a==5)
{
table2[15]=0x48;
table3[15]=0x48;
tem_y = 1;
tem_n = 0;
hum_y = 1;
hum_n = 0;
beep = 0;
}
else
{
if(a==6)
{
table2[15]=0x4c;
table3[15]=0x4c;
tem_y = 1;
tem_n = 0;
hum_y = 1;
hum_n = 0;
beep = 0;
}
else
{
if(a==7)
{
table2[15]=0x4c;
table3[15]=0x48;
tem_y = 1;
tem_n = 0;
hum_y = 1;
hum_n = 0;
beep = 0;
}
else
{
if(a==8)
{
table2[15]=0x48;
table3[15]=0x4c;
tem_y = 1;
tem_n = 0;
hum_y = 1;
hum_n = 0;
beep = 0;
}
}
}
}
}
}
}
}
}
}
void main(void)//???
{
serial_init(); //??????????
ES = 1; //?????
EA = 1; // ????
write_com(0x80);
for(x=0;x<17;x++)
{
write_date(table[x]);
// delay(15);
}
write_com(0x80+0x40);
for(x=0;x<17;x++)
{
write_date(table1[x]);
// delay(15);
}
// delay(150);
while(1)
{
for(i=0;i<10;i++)
{
if(tem[i]!=0x21)
{
delay(20);
for(i=0;i<10;i++)
{
if(tem[i]!=0x21)
{
table2[0]=i+0x30;
// table2[8]=tem[i]/100+0x30;
if(tem[i]<0)
{
table2[8]=0x2d;
abstem=abs(tem[i]);
}
else
{
table2[8]=0x30;
abstem=tem[i];
}
table2[9]=(abstem/10)%10+0x30;
table2[10]=abstem%10+0x30;
table3[8]=abstem/100+0x30;
table3[9]=((hum[i]/10)%10)+0x30;
table3[10]=hum[i]%10+0x30;
alarming(alarm[i]);
// table2[14]=alarm[i]+0x30;
write_com(0x80);
table2[6]='m'; //ȨÒËÖ®¼Æ
for(x=0;x<17;x++)
{
write_date(table2[x]);
// delay(150);
}
write_com(0x80+0x40);
for(x=0;x<17;x++)
{
write_date(table3[x]);
}
for(x=0;x<150;x++)
{
delay(30);
}
}
}
}
}
}
}
int write_com(unsigned char com)//???????
{
RS = 0;
RW = 0;
P0 = com;
delay(5);
EN = 1;
delay(5);
EN = 0;
return 0;
}
int write_date(unsigned char date)//???????
{
RS = 1;
RW = 0;
P0 = date;
delay(5);
EN = 1;
delay(5);
EN = 0;
return 0;
}
int delay(unsigned char xms)
{
unsigned char x,y;
for(x=xms;x>0;x--)
for(y=110;y>0;y--);
return 0;
}
void serial_interrupt (void) interrupt 4 /*using 1*/
{
char d;
if(RI == 1) //RI??????
{
if (RI)
{
RI = 0;
d = SBUF;
if (d == 0x21)
{
re_i = 0;
}
b[re_i] = d;
re_i++;
if (re_i == 8)
{
re_i = 0;
}
}
a0=b[1]-0x30;
a1=b[2]-0x30;
a2=b[3]-0x30;
a3=b[4]-0x30;
a4=b[5]-0x30;
a5=b[6]-0x30;
a6=b[7]-0x30;
if(a0!=0)
{
if(a2==1)
{
tem[a0]=0-a3*10-a4;
}
else
tem[a0]=a3*10+a4;
hum[a0]=a5*10+a6;
alarm[a0]=a1;
}
}
}
按照上图来连线或者搭建仿真就行了,左边四个开关就是上文说的选监测点的开关。
P1用来跟zigbee连,我总觉得可以换成nbiot之类的东西也是可以的,但是没有能力验证。
U3仿真用的是dht11,如果实物是dnt12,就把SDA当做DATA用,SCL全程接地,dht12在上电的时候如果发现SCL是接地的,那就会自动用单总线模式。
附个图,proteus的截图
1602的驱动程序可能不太一样,但是其他的都还可以。
因为监测点通常情况下为多个(为了减少接收端的使用量,降低成本),所以对于接收到的多组数据,接收端应该循环显示,这在软件设计中也予以充分考虑。当接收到多于一个监测点发来的数据时,每个点数据依次存储于数组中的各个位,由低位至高位依次保存。在1602显示屏上显示1秒后指针加一,再次读取数组内容,自动跳入下一个数据点的数据显示,报警灯和蜂鸣器也同步进行,如此循环。软件设计在图12的基础上增加一个后台循环运行的分支,如图16。
因为显示zigbee信号接收端设备显示位数和兼容性的原因,舍弃了温度和湿度数据的小数位。这是有违绿色开发理念的,因为没有把传感器数据的利用率最大化。如果可以,应更换高分辨率、大尺寸的显示屏,显示更多数据,甚至将所有监测点的数据同时显示出来,让监测不留死角。
微信小程序的显示与报警机制实用且简洁,但是因为行业垄断和专业知识不足,此方案只停留在理论分析阶段,没有进行大量的实物和仿真实验,若日后进入市场,以企业为主体可以实现小程序的发布与使用。
因为太阳能电池板的大小由所需电量决定,所以限制了监测端体积减小的重要因素是功耗,其功耗必须尽一切可能降低,而本文在对主控板选型时并没有重视这一因素。若日后有能力,换用ATmega16低功耗单片机,有希望将监测端体积压缩更小,即更利于储存、运输和使用,也更符合环境友好、可持续发展的绿色理念。