您当前的位置: 首页 >  智能家居

Autism_Huang

暂无认证

  • 7浏览

    0关注

    27博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

智能家居—基于STM32的温湿度控制系统(WIFI模块)

Autism_Huang 发布时间:2022-06-23 00:33:10 ,浏览量:7

基于App控制的STM32温湿度控制系统(WIFI模块)
  • ESP8266WIFI模块
    • 模块使用说明
    • 常用AT指令
  • DHT11温湿度传感器
    • 简介
    • 模块数据的发送流程
    • 代码实现
    • μs级的延时配置
      • HAL库配置
      • 代码实现
  • 项目总框架
    • 总框架图
    • 单片机控制流程图
    • APP控制流程图
  • 项目代码实现
    • WIFI模块
    • STM32单片机(主机)
      • 初始化WIFI模块
      • 控制任务
      • DHT11数据发送给WIFI模块
    • APP的编写
    • 布局文件
    • 逻辑代码
  • BUG记录
    • WIFI模块的发送
    • 开任务之后main.c里的while不跑了

ESP8266WIFI模块

在这个项目中我选用的正点原子的WIFI模块(ATK-ESP8266),这个模块采用串口(LVTTL)与MCU(或其他串口设备)通信,内置了TCP/IP的协议栈,可以实现串口与WIFI直接的转换,通过ATK-SEP8266模块,传统的串口设备只是需要简单的串口配置,即可通过网络(WIFI)传输自己的数据。

模块使用说明

其实挺简单的,拿出你的模块看,VCC就接3.3V或者5V,GND就接GND啦,然后WIFI模块与串口设备TX接RX,RX接TX,RST的复位是低电平有效。IO-0适用于固件烧写的,低电平就是烧写模式,高电平时运行模式(默认态) WIFI模块的硬件连接

RST和IO-0一般是不用接的(一般使用只用接前4个就好了)!

常用AT指令 指令描述AT+CWMODE选择 WIFI 应用模式AT+CWJAP加入 APAT+CWLAP列出当前可用 APAT+CWQAP退出与 AP 的连接AT+CWSAP设置 AP 模式下的参数AT+CWLIF查看已接入设备的 IPAT+CWDHCP设置 DHCP 开关AT+CWAUTOCONN设置 STA 开机自动连接到 wifiAT+CIPSTAMAC设置 STA 的 MAC 地址AT+CIPAPMAC设置 AP 的 MAC 地址AT+CIPSTA设置 STA 的 IP 地址AT+CIPAP设置 AP 的 IP 地址AT+CWSTARTSMART启动智能连接AT+CWSTOPSMART停止智能连接AT+WPS设置 WPS 功能AT+MDNS设置 MDNS 功能AT+CWHOSTNAME设置 ATK-ESP-01 Station 的主机名字

具体AT指令的使用还是得去看下模块的用户手册,这里就不做过多的赘述了。

DHT11温湿度传感器 简介

DHT11温湿度传感器

参数Value工作电压3.3V~5V湿度测量范围20%~95%温度测量范围0°~50°

传感器数据输出的是未编码的二进制数据。数据(湿度、温度、整数、小数)之间应该分开处理。(如下图所示) 二进制图

模块数据的发送流程

在这里插入图片描述 首先主机发送开始信号,即:拉低数据线,保持t1(至少18ms)时间,然后拉高数据线t2(20-40us)时间,然后读取DHT11的响应,正常的话,DHT11会拉低数据线,保持t3(40-50us)时间,作为响应信号,然后DHT11拉高数据线,保持t4(40-50us)时间后,开始输出数据。

代码实现
/*更改IO模式的封装函数*/
void DHT11_PIN_MODE(uint8_t mode)                        
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(mode==Intput)
  {
		GPIO_InitStruct.Pin = DHT11_DATA_Pin;
		GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
		GPIO_InitStruct.Pull = GPIO_PULLUP;
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(DHT11_DATA_GPIO_Port, &GPIO_InitStruct);
		
   }else
   {
    
		GPIO_InitStruct.Pin = DHT11_DATA_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(DHT11_DATA_GPIO_Port, &GPIO_InitStruct);
		 
    }
}

static uint8_t DHT11_ReadByte ( void )
{
  DHT11_PIN_MODE(Intput);
  uint8_t i, temp=0;
  for(i=0;itemp_low8bit = DHT11_ReadByte();
		DHT11_Data->check_sum    = DHT11_ReadByte();
		/*读取结束,引脚改为输出模式*/
		DHT11_PIN_MODE(Output);
		/*主机拉高*/
		DHT11_DATA_SET();
		/* 对数据进行处理 */                
		humi_temp=DHT11_Data->humi_high8Bit*100+DHT11_Data->humi_low8bit;
		DHT11_Data->humidity =(float)humi_temp/100;
		humi_temp=DHT11_Data->temp_high8bit*100+DHT11_Data->temp_low8bit;
		DHT11_Data->temperature=(float)humi_temp/100;   
		/*检查读取的数据是否正确*/
		temp = DHT11_Data->humi_high8Bit + DHT11_Data->humi_low8bit +
		DHT11_Data->temp_high8bit+ DHT11_Data->temp_low8bit;
		if(DHT11_Data->check_sum==temp)
		{
			return SUCCESS;
		}else
		{
			return ERROR;
		}
	}else
	{
		return ERROR;
	}
}

这里需要说明的是,如果用的是FreeRTOS的话就需要开多一个定时器用来进行μs级别的计时,同时不能放在任务中跑,这样任务很容易阻塞导致系统的暂停运行。但是实际上测量温度可以通过开关中断的方式进行读取,将读取速率放在一个任务里面也可以,主要是检测的时候用的us级的上拉下拉要注意下。

μs级的延时配置

这里选用的TIM6用来做计时器

HAL库配置

由于TIM6是挂载在APB1上的,所以要APB1的值 Clock Configue TIM6配置 因为我APB1Timer clocks是90MHz 所以89+1/90=1MHz,也就是1μs计数一次。

代码实现
void HAL_Delay_us(uint16_t us)
{
//	uint16_t startCount = __HAL_TIM_GET_COUNTER(&htim6);
//	
//	while((__HAL_TIM_GET_COUNTER(&htim6) - startCount) = TASK_DHT11)
		{
			DHT11_Read_TempAndHumidity(&DHT11_Data);
			sprintf(str,"%.2f",DHT11_Data.temperature);
			strcat(str,fuhao);//这里利用一个符号可以在App的逻辑代码中对字符串进行分割
			sprintf(humi,"%f",DHT11_Data.humidity);
			strcat(str,humi);
			if(Init_FLAG == 1)
			{
				HAL_UART_Transmit(&huart7,(uint8_t *)cipsend,strlen(cipsend),0xFFFF);
				HAL_Delay(200);
				HAL_UART_Transmit(&huart7,(uint8_t *)str,strlen(str),0xFFFF);
			}
			Count_Data.dht11_count = 0;
		}
APP的编写 布局文件

内容偏长,需要的可以复制,不需要的直接跳过就好了




    

    

    

        

        
    

    

        

        

    

    

    

        

        
    

    

        

        
        

    

    

    

    

        

        
    

    


        

        

    


    

        
        
        

    

    

        

        


    



效果图 APP界面效果图 小黄鸭除外😿!!

逻辑代码
package com.example.smart_homeapp;

import androidx.appcompat.app.AppCompatActivity;

import android.icu.util.Output;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ThreadLocalRandom;

public class MainActivity extends AppCompatActivity {

    Client_Thread client;

    Socket ClientSocket;
    InputStream is;
    OutputStream os;

    EditText et_ip,et_port;
    TextView tv_online,tv_music,tv_temperature,tv_humidity,tv_fuhao;
    Button btn_online,btn_lighton,btn_lightclose,btn_music1,btn_music2,btn_music3,btn_sd;

    ButtonListener buttonListener;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Viewinit();
        Listenerinit();


    }

    public void Viewinit(){
        et_ip = (EditText) findViewById(R.id.ip);
        et_port = (EditText)findViewById(R.id.port);

        tv_online = (TextView) findViewById(R.id.status);
        tv_music = (TextView)findViewById(R.id.musicname);
        tv_temperature = (TextView)findViewById(R.id.temperature);
        tv_humidity = (TextView)findViewById(R.id.humidity);
        tv_fuhao = (TextView)findViewById(R.id.fuhao);

        btn_online = (Button) findViewById(R.id.online);

        btn_lighton = (Button) findViewById(R.id.lighton);
        btn_lightclose = (Button) findViewById(R.id.lightclose);

        btn_music1 = (Button) findViewById(R.id.music1);
        btn_music2 = (Button) findViewById(R.id.music2);
        btn_music3 = (Button) findViewById(R.id.music3);

        btn_sd = (Button) findViewById(R.id.shutdown);
    }

    public void Connect(View view) {

        String ip = et_ip.getText().toString();
        int port = Integer.parseInt(et_port.getText().toString());
        Toast.makeText(MainActivity.this,"Wait a second...",Toast.LENGTH_LONG).show();
        client = new Client_Thread(ip,port);
        client.start();
        Toast.makeText(MainActivity.this, "Connect Successfully", Toast.LENGTH_SHORT).show();
    }
//灯 - music - judge
    class ButtonListener implements View.OnClickListener{
        @Override
        public void onClick(View view) {
            switch (view.getId()){
                case R.id.online:
                    send("##C");
                    tv_online.setText("Already done");
                    Toast.makeText(MainActivity.this, "Please..Wait...", Toast.LENGTH_SHORT).show();
                    break;
                case R.id.lighton:
                    send("Q##");
                    Toast.makeText(MainActivity.this, "The Light is openned", Toast.LENGTH_SHORT).show();
                    break;
                case R.id.lightclose:
                    send("W##");
                    Toast.makeText(MainActivity.this, "The Light is closed", Toast.LENGTH_SHORT).show();
                    break;
                case R.id.music1:
                    send("#A#");
                    Toast.makeText(MainActivity.this, "Little_Start Music Begin", Toast.LENGTH_SHORT).show();
                    tv_music.setText("Little_Start");
                    break;
                case R.id.music2:
                    send("#S#");
                    Toast.makeText(MainActivity.this, "Happy_Song Music Begin ", Toast.LENGTH_SHORT).show();
                    tv_music.setText("Happy_Song");
                    break;
                case R.id.music3:
                    send("#D#");
                    Toast.makeText(MainActivity.this, "Mario_Song Music Begin", Toast.LENGTH_SHORT).show();
                    tv_music.setText("Mario_Song");
                    break;
                case R.id.shutdown:
                    send("###");
                    Toast.makeText(MainActivity.this, "Shut down All!!", Toast.LENGTH_SHORT).show();
                    tv_music.setText("No Music");
            }
        }
    }


    public void Listenerinit(){

        buttonListener = new ButtonListener();

        btn_online.setOnClickListener(buttonListener);

        btn_lighton.setOnClickListener(buttonListener);
        btn_lightclose.setOnClickListener(buttonListener);

        btn_music1.setOnClickListener(buttonListener);
        btn_music2.setOnClickListener(buttonListener);
        btn_music3.setOnClickListener(buttonListener);

        btn_sd.setOnClickListener(buttonListener);

    }


    public void send(String msg){
        new SendThread(msg).start();
    }

    class SendThread extends Thread{

        String msg;

        public SendThread(String msg){
            this.msg = msg;
        }


        @Override
        public void run() {
            try {
                if(os == null)
                {
                    //Toast.makeText(MainActivity.this,"TCP has no Connected",Toast.LENGTH_LONG).show();
                    return;
                }
                os.write(msg.getBytes());
                os.flush();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }

    String str;
    class Client_Thread extends Thread{
        String ip;
        int port;
        public Client_Thread(String ip,int port){
            this.ip = ip;
            this.port = port;
        }
        public void run() {
            try {
                ClientSocket = new Socket(ip,port);
                is = ClientSocket.getInputStream();
                os = ClientSocket.getOutputStream();
                while(true){
                    final byte[] buf = new byte[1024];
                    final int len = is.read(buf);
                    str = new String(buf,0,len);

//                    runOnUiThread(new Runnable() {
//                        public void run() {
//                            tv_temperature.setText(str);
                            tv_humidity.setText(str);
//                        }
//                    });
                    runOnUiThread(()->{

                        String[] strs = str.split(",");
                        tv_temperature.setText(strs[0]+"°");
                        tv_humidity.setText(strs[1]);
                        tv_fuhao.setText("%");
                        Log.e("TAG",str);

                    });

                }
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}




这里创建了两个线程,一个是客户端线程,用来接收服务器发来的数据,一个是发送线程,用来发送APP点击触发的事件需要发送的信息。在客户端线层中用了split来进行分隔接受回来的数据。类似于去除帧头帧尾。

BUG记录 WIFI模块的发送

这个真的很坑!如果你用WIFI模块作为服务器的话,也就是说WIFI模块被设置成AP模式的话,那么就得先把WIFI模块的透传模式关了,不然的话CIPSEND发送一直都是返回ERROR的。 解决方法:按照我上图的初始化逻辑就好了,至于CIPSEND,看个人需求放不同的位置就好了。

开任务之后main.c里的while不跑了

md这个问题就很灵性,由于在任务里面没有us级的延时(可能有,但是我不知道),但是处理DHT11模块返回来的数据需要用us级的延时等待脉冲的产生再进行置位。因为潜意识里在任务里面除了用osDelay外其他的Delay类似于HAL_Delay这些会对任务造成阻塞然后程序就死掉了。 出现问题的原因:一般来说开启调度器之后while的确是不跑的,所以把调度器注释掉就好了,跑轮询不跑任务。 解决方法: 摆烂法:跑裸机,把main.c里的任务创建注释了然后全部把代码放在while里面利用delay计时跑个假的任务处理。(虽然是在摆烂,但是确实能解决ddl带来的焦虑) 认真解决法:可以用32的硬件__NOP来进行us级的计时,像我用的F427,我的时钟频率在168M,那么我一个NOP其实就是6ns左右,也就是1/168us,那我们就可以根据这个汇编指令一个空指令NOP来进行us级的计时对DHT11上拉下拉进行读取电平数据。

关注
打赏
1655915590
查看更多评论
立即登录/注册

微信扫码登录

0.3098s