基于STM32最小系统板—温湿度监测系统

功能概述

基于STM32F103C8T6最小系统板制作的温湿度监测系统,监测温度和湿度并通过WIFI模块将监测的数据传输到电脑上的Socket服务器端。

所用硬件

STM32F103C8T6最小系统板、DHT11温湿度传感器、DS18B20温度传感器、ESP8266-01SWIFI模块。

注:传感器可只用DHT11,DS18B20可有可无。

演示效果

实物图:
在这里插入图片描述

服务器测试图:
在这里插入图片描述

程序部分

DHT11温湿度的获取

关于DHT11温湿度传感器的使用,网上有很多详细的资料,这里只进行简单的说明。
在这里插入图片描述

这是DHT11的通讯过程,我们需要通过引脚先将DHT11置于低电平至少18ms,再置高电平20-40us。然后DHT11会先返回80us的低电平信号,再返回一个80us的高电平信号,之后开始传送数据。

代码部分

dht11.h

#ifndef __DHT11_H
#define __DHT11_H
#include "stm32f10x.h"
void DHT11_GPIO_OUT(void);
void DHT11_GPIO_IN(void);
uint8_t DHT_Read_Byte(void);
uint8_t DHT_Read(void);
#endif

dht11.c

#include "Delay.h"
#include "dht11.h"
uint8_t dat[5]={0x00,0x00,0x00,0x00,0x00};
uint8_t sum = 0;

/*
*初始化输出
*/
void DHT11_GPIO_OUT(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); 
		
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/*
*初始化输入
*/
void DHT11_GPIO_IN(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
		
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;	
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 
  	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/*
*读取一个字节
*/
uint8_t DHT_Read_Byte(void)
{
	uint8_t temp;
	uint8_t ReadDat = 0;
	
	uint8_t retry = 0;
	uint8_t i;
	for(i=0; i<8;i++)
	{	
		//读取一位数据时,会先收到低电平信号50us
		while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0 && retry< 100)
		{
			Delay_us(1);
			retry++;
		}
		retry = 0;
		Delay_us(30);
		temp = 0;
		//再接收高电平信号16us-28us(代表数据0)或高电平信号70us(代表数据1)
		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14)==1) temp=1;
		while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 1 && retry<100)
		{
			Delay_us(1);
			retry++;
		}
		retry = 0;
		ReadDat<<=1;
		ReadDat|=temp;
	}
	return ReadDat;
}

/*
*读取一次数据
*/
uint8_t DHT_Read(void)
{
	uint8_t i;
	uint8_t retry = 0;
	//总线设置为输出并拉低18ms
	DHT11_GPIO_OUT();
	GPIO_ResetBits(GPIOB, GPIO_Pin_14);
	Delay_ms(18);
	//总线再拉高40ms
	GPIO_SetBits(GPIOB, GPIO_Pin_14);
	Delay_us(40);
	//总线设置为输入
	DHT11_GPIO_IN();
	Delay_us(20);
	//等待响应信号为低电平
	if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)
	{	
		//等待响应信号为高电平,超时等待100us
		while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14)==0&&retry<100)
		{
			Delay_us(1);
			retry++;
		}
		retry=0;
		//等待数据传送,超时等待100us
		while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14)==1&&retry<100)
		{
			Delay_us(1);
			retry++;
		}
		retry=0;
		//循环读取数据,dat[0]为湿度整数数据,dat[1]为湿度小数数据,dat[2]为温度整数数据,dat[3]为温度小数数据,dat[4]为校验和数据
		for(i=0;i<5;i++)
		{
			dat[i] = DHT_Read_Byte();
		}
		Delay_us(50);
	}
	//当校验和数据=湿度整数数据+湿度小数数据+温度整数数据+温度小数数据,说明正确传送。
	sum = dat[0] + dat[1] + dat[2] + dat[3];
	if(dat[4]  == sum)
	{
		return 1;
	}
	else
		return 0;
}

DS18B20温度的获取

没有使用DS18B20可跳过这部分
关于DS18B20的初始化时序图以及读写时序图网上有很多详细的资料,这里不再赘述,直接上代码。

ds18b20.h

#ifndef __DS18B20_H
#define __DS18B20_H
#include "stm32f10x.h"
#include "stdio.h"

void DS18B20_GPIO_OUT(void);
void DS18B20_GPIO_IN(void);
void DS18B20_RST(void);
int DS18B20_Check(void);
uint8_t DS18B20_Read_Bit(void);
void DS18B20_Write_One(void);
void DS18B20_Write_Zero(void);
uint8_t DS18B20_Read_Byte(void);
void DS18B20_Write_Byte(uint8_t data);
void DS18B20_Start(void);
float DS18B20_Get_Temp(void);

#endif

ds18b20.c

#include "ds18b20.h"
#include "Delay.h"
/*
*初始化输出
*/
void DS18B20_GPIO_OUT(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); 
		
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/*
*初始化输入
*/
void DS18B20_GPIO_IN(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
		
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;	
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 
  	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/*
*复位ds18b20
*/
void DS18B20_RST(void)
{
	DS18B20_GPIO_OUT();
	GPIO_ResetBits(GPIOB, GPIO_Pin_13);
	Delay_us(750);
	GPIO_SetBits(GPIOB, GPIO_Pin_13);
	Delay_us(15);
}

/*
*检测ds18b20的存在
*/
int DS18B20_Check(void)
{
	uint8_t retry = 0;
	DS18B20_GPIO_IN();
	while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13) == 1 && retry<200)
	{
		retry++;
		Delay_us(1);
	}
	if(retry>=200) return 1;//等待超时
	else retry = 0;
	
	while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13) == 0 && retry<240)
	{
		retry++;
		Delay_us(1);
	}
	if(retry>=240) return 1;
	
	return 0;
}

/*
*读取一位数据
*/
uint8_t DS18B20_Read_Bit(void)
{
	uint8_t data;
	DS18B20_GPIO_OUT();
	GPIO_ResetBits(GPIOB, GPIO_Pin_13);
	Delay_us(2);
	GPIO_SetBits(GPIOB, GPIO_Pin_13);
	DS18B20_GPIO_IN();
	Delay_us(10);
	if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13) == 1)
		data = 1;
	else data =0;
	Delay_us(50);
	return data;
}

/*
*进行写数据1操作
*/
void DS18B20_Write_One(void)
{
	DS18B20_GPIO_OUT();
	GPIO_ResetBits(GPIOB, GPIO_Pin_13);
	Delay_us(2);
	GPIO_SetBits(GPIOB, GPIO_Pin_13);
	Delay_us(60);
}

/*
*进行写数据0操作
*/
void DS18B20_Write_Zero(void)
{
	DS18B20_GPIO_OUT();
	GPIO_ResetBits(GPIOB, GPIO_Pin_13);
	Delay_us(60);
	GPIO_SetBits(GPIOB, GPIO_Pin_13);
	Delay_us(2);
}

/*
*读取一个字节
*/
uint8_t DS18B20_Read_Byte(void)
{
	uint8_t data = 0;
	for(uint8_t i=0;i<8;i++)
	{
		data>>=1;
		if(DS18B20_Read_Bit())
		{
			data |=0x80;
		}
	}
	return data;
}

/*
*写入一个字节
*/
void DS18B20_Write_Byte(uint8_t data)
{
	DS18B20_GPIO_OUT();
	for(int i =1;i<=8;i++)
	{
		if(data&0x01)
			DS18B20_Write_One();
		else
			DS18B20_Write_Zero();
		data >>=1;
	}
}

/*
*初始化ds18b20
*/
void DS18B20_Start(void)
{
	DS18B20_RST();
	DS18B20_Check();
	DS18B20_Write_Byte(0xcc);
	DS18B20_Write_Byte(0x44);
}

/*
*温度获取
*/
float DS18B20_Get_Temp(void)
{
	float data;
	uint8_t TH,TL;
	uint16_t TB;
	DS18B20_Start();
	DS18B20_RST();
	DS18B20_Check();
	DS18B20_Write_Byte(0xCC);
	DS18B20_Write_Byte(0xBE);
	TL = DS18B20_Read_Byte();
	TH = DS18B20_Read_Byte();
	
	TB = TH;
	TB <<=8;
	TB = TB|TL;
	data = TB * 0.0625;
	return data;
}

ESP8266的使用

同样的esp8266的工作模式的使用,AT指令的调试这里不再介绍,网上有相关的AT指令介绍。

我是通过stm32上的串口和esp8266进行通信,我们使用的是AT指令中的STA工作模式(通过让esp8266连接其他wifi进行工作),通过透传的方式让笔记本电脑和esp8266连接同一wifi进行通信。

注:esp8266的工作对工作电压有着一定要求,如果使用stm32进行供电可能会因为供电不足导致,esp8266发送乱码的现象出现。

代码部分

UART.h

#ifndef __UART_H
#define __UART_H
#include "stm32f10x.h"
#include "stdio.h"
void USART_Init_Config(u32 bound);
#endif

UART.c

#include "UART.h"
/*
*初始化串口PA9和PA10
*u32 bound设置串口波特率
*/
void USART_Init_Config(u32 bound)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	USART_InitTypeDef USART_InitStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1,ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStruct.USART_Parity = USART_Parity_No;
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	USART_InitStruct.USART_BaudRate = bound;
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_Init(USART1,&USART_InitStruct);
	
	USART_Cmd(USART1,ENABLE);
	
}

/*
*重写fputc,这样我们可以直接使用printf()来发送指令
*/
int fputc(int ch,FILE *f)
{
	
	USART_SendData(USART1,(u8)ch);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
	return ch;
}

esp8266.h

#ifndef __ESP8266_H
#define __ESP8266_H
#include "stm32f10x.h"
#include "string.h"
void ESP_Init(void);
void ESP_SendMeassage(u8 hum, float tem);
#endif

esp8266.c

#include "esp8266.h"
#include "UART.h"
#include "Delay.h"
/*
*初始化esp8266
*/
void ESP_Init(void)
{	
	//设置工作模式为STA模式
	printf("AT+CWMODE=1\r\n");
	//延时500ms指令有足够时间操作
	Delay_ms(500);
	//设置esp8266要连接的wifi信息
	printf("AT+CWJAP=\"WIFI名称\",\"WIFI密码\"\r\n");
	Delay_ms(3000);
	//设置连接的服务端的信息,这里的ip地址和端口号与后面socket服务端使用的ip地址和端口号是一致的
	printf("AT+CIPSTART=\"TCP\",\"ip地址例如192.168.1.9\",端口号例如8080\r\n");
	Delay_ms(3000);
}
/*
*发送温度湿度信息
*hum是dht11发送的,temp是ds18b20发送的
*/
void ESP_SendMeassage(u8 hum, float tem)
{
	//进入透传模式
	printf("AT+CIPMODE=1\r\n");
	Delay_ms(500);
	//开始发送数据
	printf("AT+CIPSEND\r\n");
	Delay_ms(500);
	
	printf("湿度:%d%%,温度:%f度\r\n",hum, tem);
	
	//退出发送数据
	printf("+++");
	Delay_ms(100);
	//退出透传模式
	printf("AT+CIPMODE=0\r\n");
	Delay_ms(100);
}

Socket服务器的创建

关于套机字通信可以看看这个作者写的: 套接字编程.

先来看看这里的socket通信过程
在这里插入图片描述

与一般的socket编程不同的地方在于,我们的服务器端只需要接收数据,不需要向客户端发送数据,同时我们只需要进行服务器端的socket编程,无需写客户端部分的代码,只需交给esp8266来完成即可。

服务端代码
注意这部分是单独在vs中进行编程的,并不在项目工程当中。

server.cpp

#include <iostream>
#include <WinSock2.h>
#include <WS2tcpip.h>
using namespace std;
int main()
{
	//服务器ip地址,端口号
	const char* host = "192.168.1.9";
	unsigned int port = 8080;

	WSADATA wsaData;
	int len;
	int recv_len;
	SOCKET  sock_server, sock_client;
	SOCKADDR_IN server_addr, client_addr;
	server_addr.sin_family = AF_INET;
	inet_pton(AF_INET, host, &server_addr.sin_addr);
	server_addr.sin_port = htons(port);

	//初始化套接字
	WORD w_req = MAKEWORD(2, 2);
	if (WSAStartup(w_req, &wsaData) != 0)
	{
		WSACleanup();
		cout << "初始化套接字失败" << endl;
	}
	
	//创建一个套接字
	sock_server = socket(AF_INET, SOCK_STREAM, 0);
	if (sock_server == INVALID_SOCKET)
	{
		cout << "套接字创建失败" << endl;
		WSACleanup();
		exit(1);
	}

	//绑定套接字
	if (bind(sock_server, (SOCKADDR*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR)
	{
		cout << "套接字绑定失败" << endl;
		WSACleanup();
		exit(1);
	}
	else
	{
		cout << "套接字绑定成功" << endl;
		cout << "IP:" << host << endl;
		cout << "端口:" << port << endl;
	}

	//监听套接字
	if (listen(sock_server, SOMAXCONN) < 0)
	{
		cout << "套接字设置监听失败" << endl;
		WSACleanup();
		exit(1);
	}
	else
	{
		cout << "套接字设置监听成功" << endl;
	}

	cout << "等待连接建立" << endl;

	//接收连接请求
	len = sizeof(client_addr);
	sock_client = accept(sock_server, (SOCKADDR*)&client_addr, &len);
	if (sock_client == SOCKET_ERROR)
	{
		cout << "套接字连接失败" << endl;
		WSACleanup();
		return 0;
	}

	cout << "连接建立,等待数据接收" << endl;
	//接收数据
	while (1)
	{
		char recv_buf[100] = { "\0" };
		recv_len = recv(sock_client, recv_buf, 100, 0);
		if (recv_len < 0)
		{
			cout << "接收失败" << endl;
		}
		else
		{
			cout << recv_buf << endl;
		}
	}

	closesocket(sock_server);
	closesocket(sock_client);
	return 0;
}

主函数部分

main.c

#include "stm32f10x.h"
#include "UART.h"
#include "dht11.h"
#include "Delay.h"
#include "ds18b20.h"
#include "esp8266.h"

//dat读取dht11数据
extern u8 dat[5];
int main(void)
{	
	//data用于读取ds18b20的数据
	float data;
	//初始化串口,波特率设置为115200
	USART_Init_Config(115200);
	//初始化esp8266
	ESP_Init();
	//进行温湿度的读取
	while(1)
		{
				//当DHT11得到数据时
				if(DHT_Read())
			{	
				//ds18b20也读取一次数据
				data = DS18B20_Get_Temp();
				ESP_SendMeassage(dat[0], data);
				/*
				若不使用ds18b20,则使用这段代码,将ESP_SendMeassge()注释掉。
				printf("湿度:%d%%,温度:%d度\r\n",dat[0],dat[2]);
				*/
				Delay_ms(3000);	
			}
			
		}		
			
}

调试及下载

将程序烧录到stm32中,串口PA9和PA10与esp8266的RX和TX连接,PB14与dht11数据端连接,PB13与ds18b20数据端连接,将其余的VCC和GND接好。电脑上先运行socket服务端,再将stm32上电,等待数据发送至服务端。

有需要可自行下载: 项目链接

  • 32
    点赞
  • 404
    收藏
    觉得还不错? 一键收藏
  • 18
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值