历史上的今天
返回首页

历史上的今天

今天是:2025年07月27日(星期日)

正在发生

2021年07月27日 | 51单片机DHT11温湿度ESP8266WiFi手机APP显示设计

2021-07-27 来源:eefocus

温湿度检测设计。基于51单片机、ESP8266WiFi模块、温湿度DHT11传感器、Android APP完成。首先先展示一下设计好的实物,接下来将从系统方案、硬件设计、软件设计这三个方面来阐述。

1、系统方案 

DHT11温湿度传感器采集数据传送给单片机,单片机将数据处理之后通过ESP8266WiFi模块将数据发送给手机App。WiFi模块有两个作用:一是串口转WiFi,单片机通过串口将数据发送给Wifi模块,对于单片机编程而言,与Wifi模块通信就相当于与串口通信,不必知道WiFi协议;二是WiFi模块当做WiFi 热点AP,手机搜索8266建立的WiFi名称就能与其连接。显然这种方式只是局域网通信,不能进行远程连接,远程连接需要服务器端的支持,现在常采用的方法是通过阿里云、机智云等,远程连接比较复杂,我们以后再研究。

2、硬件设计

整个设计电路图如下所示:

ESP8266WiFi模块我们采用的是ESP-01S模组,安信可公司出品的,注意ESP-01S的电源是3.3V,而单片机电源是5V,所以需要一个5V转3.3V的模块,我们选用的是LM1117T-3.3V这个器件, LM1117这个器件管脚一定不要接错,否则会发热非常严重然后烧毁。我第一次焊接的时候把LM1117管脚焊错了,上电后用手触碰了一下差点把手烫伤,赶紧把电源拔掉。LM1117的接线如下所示:

ESP8266的接线如下图所示:

RX:模块串口通信的接收引脚,接到单片机的TX引脚。

GND:接地

TX:模块的发射端,接单片机的RX接口。

CH_PD / EN:接3.3v高电平。

VCC:接3.3V的高电平。


DHT11温湿度传感器负责采集环境中的温湿度数据,在单片机软件设计部分会详细的介绍该传感器的使用步骤。

引脚说明:

VDD 供电3.3~5.5V DC

DATA 串行数据,单总线

NC 空脚

GND 接地,电源负极


3.单片机软件设计

单片机程序主要是两个点,一是读取DHT11传感器的温湿度数据,二是串口通信。DHT11的官方文档写的很规范,有关于读取数据的详细步骤,文档更新也比较及时,最新的更新日期是2017年3月31号,官网的下载地址:http://www.aosong.com/products-21.html


DHT11采用单总线通信,单总线即只有一根数据线,系统中的数据交换、控制均由单总线完成。


传送数据位定义

DATA 管脚用于DHT11与单片机之间的通讯和同步,采用单总线数据格式,一次传送40 位数据,高位先出。

数据格式:

8bit 湿度整数数据+ 8bit 湿度小数数据+ 8bit 温度整数数据+ 8bit 温度小数数据+ 8bit 校验位。

注:其中湿度小数部分为0。


校验位数据定义

8bit 湿度整数数据 +  8bit 湿度小数数据 +  8bit 温度整数数据 +  8bit 温度小数数据 = 8bit 校验位

如果以上等式成立,则本次传感器采集的数据有效,否则无效。


先看采集数据有效的示例,接收到的40 位数据为:

0011 0101     0000 0000     0001 1000     0000 0100      0101 0001

湿度高8 位     湿度低8 位     温度高8 位     温度低8 位    校验位

计算: 0011 0101 + 0000 0000 + 0001 1000 + 0000 0100 = 0101 0001,接收数据正确。

湿度:0011 0101(整数)=35H=53%RH 0000 0000(小数)=00H=0.0%RH =>53%RH + 0.0%RH = 53.0%RH

温度:0001 1000(整数)=18H=24℃ 0000 0100(小数)=04H=0.4℃ =>24℃ + 0.4℃ = 24.4℃


采集数据无效的示例,接收到的40 位数据为:

0011 0101     0000 0000     0001 1000    0000 0100     0100 1001

湿度高8 位    湿度低8 位     温度高8 位    温度低8 位    校验位

计算: 0011 0101 + 0000 0000 + 0001 1000 + 0000 0100 不等于0100 1001,本次接收的数据不正确,放弃,重新接收数据。


通过以上两个示例可以清楚DHT11数据格式以及数据如何去校验有效性。


数据时序图

用户主机(MCU)发送一次开始信号后,DHT11 从低功耗模式转换到高速模式,待主机开始信号结束后,DHT11 发送响应信号,送出40bit 的数据,并触发一次信采集。信号发送如图所示。这里的主机是指单片机,从机是指DHT11传感器。

下面这个图表罗列了时序图相关的参考时间,在读取数据的详细步骤中会用到这些数值。

根据时序图和表中的参考时间,我们可以得出读取传感器数据的步骤。


step1:单片机输出低电平保持20ms


step2:单片机拉高电平保持13us等待DHT11传感器的低电平响应信号


step3:判断DHT11是否给出低电平响应,如果有低电平响应则进入步骤4,否则等待下一轮的尝试。


step4:通过while语句等待83us的低电平响应时间结束


step5:通过while语句等待87us的高电平响应时间结束


step6:计算温湿度数据


step7:单片机输出高电平结束一次数据采集的读取


step8:校验数据


在时序图中可以看到,数据读取是每次一位进行的,数据0位和数据1位的低电平时间是相同的,即54us。数据0位的高电平时间是24us,而数据1为的高电平时间是71us,通过高电平时间的差异我们就可以判断出是数据0还是数据1。所以单独写了一个函数用来计算数据0位和1位,由于温湿度的整数和小数部分分别是由8位表示的,我们定义该函数得到8位数据之后给出返回值。步骤6对应的函数computeData() 用来完成上述工作。我们对步骤6进行详细的描述:


step 6.1:等待54us低电平结束


step 6.2:延时30us判断高电平是否结束,因为数据0位的电平最大时长是27us,如果超过27us之后高电平结束,则为数据0位,否则为数据1位。


step 6.3:通过while语句等待高电平结束


step 6.4:通过移位和或与的方式保存一个数据位


step 6.5:循环6.1到6.4步骤8次,得到一个字节的数据


//--------------------------------

//-----湿度读取子程序 ------------

//--------------------------------

//----以下变量均为全局变量--------

//----温度高8位== temperature_H------

//----温度低8位== temperature_L------

//----湿度高8位== humidity_H-----

//----湿度低8位== humidity_L-----

//----校验 8位 == checkdata-----

//--------------------------------

void readData()

{

    U8  humidity_H_temp,humidity_L_temp,temperature_H_temp,temperature_L_temp,checkdata_temp;

    //step1:单片机输出低电平保持20ms

    P2_0=0;

    delayms(20);

    //step2:单片机拉高电平保持13us等待DHT11传感器的低电平响应信号

    P2_0=1;

    delay13us();

    //step3:判断DHT11是否给出低电平响应,如果有低电平响应则进入步骤4,否则等待下一轮的尝试。

    if(P2_0==0)

    {

        //step4:通过while语句等待83us的低电平响应时间结束

        while(P2_0==0);

        //step5:通过while语句等待87us的高电平响应时间结束

        while(P2_0==1);

        //step6:计算温湿度数据

        humidity_H_temp = computeData();

        humidity_L_temp = computeData();

        temperature_H_temp = computeData();

        temperature_L_temp = computeData();

        checkdata_temp = computeData();

        //step7:单片机输出高电平结束一次数据采集的读取

        P2_0 = 1;

        //step8:校验数据

        if(checkdata_temp == humidity_H_temp + humidity_L_temp + temperature_H_temp + temperature_L_temp)

        {

            humidity_H = humidity_H_temp;

            humidity_L = humidity_L_temp;

            temperature_H = temperature_H_temp;

            temperature_L = temperature_L_temp;

            checkdata = checkdata_temp;

        }

    }

 

}

 

/**

*根据时序计算温湿度值

*/

U8 computeData()

{

    U8 i,U8comdata;

    for(i=0; i<8; i++)

    {

        //step 6.1:等待54us低电平结束

        while(P2_0==0);

        //step 6.2:延时30us判断高电平是否结束

        Delay_10us();

        Delay_10us();

        Delay_10us();

        U8temp=0;

        if(P2_0==1)

        {

            U8temp=1;

        }

        //step 6.3:通过while语句等待高电平结束

        while(P2_0==1);

        //step 6.4:通过移位和或与的方式保存一个数据位

        U8comdata<<=1;

        U8comdata|=U8temp;

    }

    return U8comdata;

}

温湿度数据读取完毕,接下来就是通过串口发送出去,串口发送数据的代码相对简单了,我们在主函数中对串口通信进行初始化,然后在一个while语句中每隔2s读取数据然后发送。


//----------------------------------------------

//main()功能描述:  STC89C52RC  11.0592MHz   串口发送温湿度数据,波特率 9600

//----------------------------------------------

void main()

{

    U8  i;

    TMOD=0x20; //定时器1工作在方式2

    TH1 = 0xfd; //波特率9600

    TL1 = 0xfd;

    SM0=0; //串口工作在方式1

    SM1=1;

    EA = 1; //开总中断

    REN = 1; //使能串口

    TR1 = 1; //定时器1开始计时

    delayms(1000);

sendString("AT+CWMODE=2rn"); //ap模式

delayms(1000);

sendString("AT+CIPMUX=1rn"); //允许多连接

delayms(1000);

sendString("AT+CIPSERVER=1rn"); //建立TCP Server

delayms(1000);

    ES = 1; //开串口中断

    while(1)

    {

        //调用温湿度读取子程序

        readData();

        str[0]=humidity_H;

        str[1]=humidity_L;

        str[2]=temperature_H;

        str[3]=temperature_L;

        str[4]=checkdata;

        //发送到串口

        delayms(2); //发送温度数据

        sendString( "AT+CIPSTART=1,"TCP","192.168.4.2",5000rn");

        delayms(2);

        sendString("AT+CIPSEND=1,5rn");

        delayms(2);

        for(i=0; i<5; i++)

        {

            sendOneChar(str[i]);

        }

        //读取模块数据周期不易小于 2S

        delayms(2000);

    }

}

至此,单片机端的主要代码就讲解完了,可以看到核心代码是如何读取DHT11的数据。


4.手机APP软件设计

APP是用Android Studio(AS)开发的,不建议初学者学习Eclipse结合ADT(Android Eclipse Tools)插件的方式开发Android APP,这种方式已经过时并且以后会被淘汰,Google在2016年底已经停止了对ADT的更新,我之前所在的公司已经将Eclispe的代码全部迁移到AS平台了,推荐使用Google自家的AS集成开发环境。AS有很多优点,但是在使用时也有问题,AS借助gradle进行项目构建,至于为什么Google利用gradle进行Android app项目构建,读者可以自行上网搜索。gradle插件版本要和AS版本相对应,不同的开发者的gradle版本可能不同,所以当你拿到另外一个开发者的代码在自己的AS运行时时有可能会构建失败。这个现象对于国外开发者而言不是一个问题,AS可以自动去下载所需要的gradle插件版本,但是在国内,由于众所周知的原因,如果不会科学上网那么AS直接尝试下载gradle插件时会失败,会令很多初学者不知所措。在以后有时间我会单独写一篇blog来讲解如何去解决这个问题。最近听到Google要重返中国市场,如果能回归成功,对于国内的很多开发者和学术研究者而言是个好消息。


言归正传,本设计APP的代码主要分成两个部分,一是WiFi数据的接收,二是图表显示。


在OnCreate()方法中进行控件的初始化,并开启一个温度数据接收线程来接收数据。


 @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        //实例化控件

        mTemperatureTv = findViewById(R.id.tv_temperature);

        mHumidityTv = findViewById(R.id.tv_humidity);

        mTemperatureAlert = findViewById(R.id.tv_temperature_alert);

        mHumidityAlert = findViewById(R.id.tv_humidity_alert);

        mTemperatureRangeTv = findViewById(R.id.tv_temperature_range);

        mHumidityRangeTv = findViewById(R.id.tv_humidity_range);

        mTemperatureRangeTv.setText("(" + mTempLow + " - " + mTempHigh + "℃)");

        mHumidityRangeTv.setText("(" + mHumidityLow + " - " + mHumidityHigh + "%)");

        mLineChart = findViewById(R.id.chart);

        //初始化图表属性

        initChart();

        //开启温度显示线程

        new ReceiveDataThread().start();

    }

单独开辟一个线程用来循环读取WiFi模块传输过来的数据,对于App而言单片机通过Wifi模块传输的数据相当与网络数据,用网络编程相关的就可以,线程的相关定义如下所示:


/**

     * 温湿度数据接收线程

     */

    private class ReceiveDataThread extends Thread {

        private DataInputStream in;

        private byte[] receive;

 

        @Override

        public void run() {

            try {

                //在手机端建立一个ServerSocket,负责接收ESP8266发送的数据,端口为5000

                serverSocket = new ServerSocket(5000);

推荐阅读

史海拾趣

Alpha-Micro Electronics公司的发展小趣事

Alpha-Micro Electronics是一家提供嵌入式系统解决方案的公司,专注于设计和制造微控制器、传感器和其他嵌入式设备。以下是关于Alpha-Micro Electronics公司发展的五个相关故事:

  1. 公司成立:Alpha-Micro Electronics公司成立于1982年,总部位于德国斯图加特。公司的创始人是一群电子工程师,他们致力于为各种应用领域提供高性能的嵌入式系统解决方案。

  2. 技术创新:在公司创立之初,Alpha-Micro Electronics就开始致力于技术创新。他们不断投资于研发,推出了一系列先进的微控制器和传感器产品,以满足客户对功能强大和性能稳定的需求。

  3. 解决方案定制:Alpha-Micro Electronics公司注重与客户的紧密合作,提供定制化的解决方案。他们的工程团队具有丰富的经验和专业知识,能够根据客户的具体要求设计和制造符合其需求的嵌入式系统。

  4. 国际扩展:随着业务的不断发展,Alpha-Micro Electronics逐渐拓展了国际市场。除了在德国的总部和研发中心外,公司还在全球范围内建立了销售和服务网络,包括在美国、亚洲和欧洲的分支机构和合作伙伴。

  5. 行业合作与创新:Alpha-Micro Electronics公司积极参与行业合作和创新项目,与其他企业、研究机构和学术界合作,共同推动嵌入式技术的发展。通过与行业领先者合作,公司不断探索新的技术和应用领域,为客户提供更具竞争力的解决方案。

这些故事展示了Alpha-Micro Electronics公司在技术创新、解决方案定制、国际扩展和行业合作方面的发展历程,以及其在电子行业中的重要地位和影响力。

Caddell-Burns Manufacturing公司的发展小趣事

在竞争激烈的电子行业中,质量管理是企业生存和发展的关键。Caddell-Burns Manufacturing公司深知这一点,因此他们始终将质量放在首位。公司建立了严格的质量管理体系,从原材料采购到产品生产、检验、包装等各个环节都进行严格把关。同时,公司还定期对员工进行质量培训,提高他们的质量意识和操作技能。这些措施确保了公司产品的稳定性和可靠性,赢得了客户的信赖。

Dongguan City Niuhang Electronics Co.LTD公司的发展小趣事

Dongguan City Niuhang Electronics Co.LTD成立于2012年,当时的中国电子行业正处于快速发展期。创始人李小芹凭借对半导体技术的深刻理解和市场需求的敏锐洞察,决定专注于二极管、桥式整流器等产品的研发与封装。公司成立初期,面临着资金短缺、技术储备不足等挑战,但李小芹凭借坚定的信念和不懈的努力,带领团队攻克了一个又一个技术难关,逐步在市场上树立了良好的口碑。

AirBorn公司的发展小趣事

在航空航天领域,AirBorn的产品广泛应用于飞机、导弹系统、通信设备等关键领域。为了满足航空航天领域对连接器的严苛要求,AirBorn不断投入研发力量,推出了一系列具有高性能、高可靠性的连接器产品。这些产品不仅满足了航空航天领域的需求,还为公司带来了可观的收益。

Cal Test Electronics公司的发展小趣事

随着全球市场的不断变化和公司规模的扩大,Cal Test Electronics开始实施全球化战略。公司积极拓展海外市场,通过参加国际展会、建立海外销售渠道等方式,提高品牌知名度和市场份额。同时,公司还加强与国际同行的合作与交流,引进先进的技术和管理经验,不断提升自身的竞争力。这些努力使得Cal Test Electronics逐渐成为一个具有全球影响力的电子配件和测试产品制造商。


这些故事旨在展示Cal Test Electronics公司可能的发展轨迹和成就,但并非基于实际发生的事件。对于该公司的真实发展历程和故事,建议查阅相关官方资料或行业报告以获取更准确的信息。

EA Elektro-Automatik公司的发展小趣事

近年来,中国市场对EA Elektro-Automatik来说变得越来越重要。公司高层认识到了中国市场的巨大潜力和重要性,并制定了在中国市场继续投资和实现增长的明确计划。EA Elektro-Automatik的代表在多个场合与中国政府和行业领导进行交流和合作,努力拓展中国市场。目前,中国市场已经占据EA Elektro-Automatik全球业务的相当一部分份额,并有望在未来继续保持增长。

问答坊 | AI 解惑

晶体振荡器的工作原理

本帖最后由 paulhyde 于 2014-9-15 09:11 编辑 关键字: 晶振 振荡器 石英晶体 石英晶体振荡器是高精度和高稳定度的振荡器,被广泛应用于彩电、计算机、遥控器等各类振荡电路中,以及通信系统中用于频率发生器、为数据处理设备产生时钟信号和为特 ...…

查看全部问答>

困扰许久的数码管显示问题!

用两个传统的573 控制 数码管显示,奇怪的是单个显示时,一切正常。待到多位扫描显示时,就出现了奇怪的顺序和段码,小弟不才, 望大虾们,不吝赐教! #include <reg52.h> sbit dula=P2^0;sbit wela=P2^1; void delay(int i){ int j=1 ...…

查看全部问答>

鼠标指针的刷新问题

加了USB鼠标,能够显示鼠标指针。只移动鼠标,显示正常。点击鼠标时,不论左右键,鼠标指针背景就不对了,看起来是上一帧的图案。在右键菜单上下移动鼠标就会留下一行轨迹。 平时移动鼠标,没有异常,那么显示驱动里SetPointerShape应该是对的吧。 ...…

查看全部问答>

CE6.0中如何将串口配置信息保存到注册表?

WIN32下,CE6.0中如何将串口配置信息保存到注册表?大概过程是怎样的?应该保存在哪个主键中?…

查看全部问答>

谈菜鸟的FGPA学习方法

结合本人最近FGPA学习谈一下学习方法。由于本人也是FPGA菜鸟一枚,所以在做设计的时候总是会遇到各种问题,即使这种问题对于老手都会觉得“本来就 是这样!”这么简单,但对新手来说就是想觉得很奇怪。在学习的过程中,我们一般会遇到几类问题,一 ...…

查看全部问答>

STM32 USART串口中断

 main(void)  {    /*PC4  PC13 PB10 PA0 */  USART_InitTypeDef USART_InitStructure;  USART_InitStructure.USART_BaudRate = 9600;  USART_InitStructure.USART_WordLength = USART_WordLengt ...…

查看全部问答>

求430和PT100连接最简单的低功耗电路。请指点

求430和PT100连接最简单的低功耗电路。请指点 要能I/O控制节能的~…

查看全部问答>

使用usb(ch376或者ch375)完成PC和FPGA的通信

只是读写。   写,pc上的信息通过USB写如FPGA在数码管上显示。   读,读FPGA,ram里的信息,传给PC。 现在编verilog代码出现问题了。 谁要是有这个代码更好。 如果没有我想问一下,CH376并行通信的芯片中断管脚是怎么 ...…

查看全部问答>

点亮一个数码管

  之前虽然在开发板上学习了74HC573,但是当时连它的datasheet都没怎么看,就照着程序来写,所以对很多的芯片都不是很了解,对芯片很迷茫。终于决定要把以前做的试验在面包板上搭一遍体验体验。所以就有了今天的实验。经过几个小时的努力 ...…

查看全部问答>

工程师经验

工程师经验       人才的范围很宽泛,我的知识使我无法涵盖之。因此,在这里我还是仅以技术人员为主来跟大家探讨一下。下面我从发展方向、自我定位、过程实 现三个方面去分析,发展方向是目标,自我定位是基础,过程实现是 ...…

查看全部问答>