目前,科技发展迅速,渗透到各行各业和生活的方方面面,室内设计与高科技相结合,出现了智能家居。所谓智能家居,就是利用物联网、传感器、智能控制技术将各种家电连接起来,实现智能化、自动化、人性化的生活环境。通过语音控制、远程控制、预约控制、个性化设计、一键控制等功能,进一步提高生活的舒适性、便利性和安全性。
该系统利用物联网的便利性,实现了家电的远程操作。具体来说,空调、加湿器、灯具、家庭影院等电器都是通过各种传感器监测室内温度、湿度、光强、烟雾浓度、室内是否有人等信息自动调整的。智能电器的语音控制采用人机交互设备和语音识别设备。智能电器的远程控制是通过移动终端和计算机终端实现的。家用电器的运行、监控屏幕、智能捕捉、报警等信息将实时反馈给用户。用户可以随时随地了解家中的具体情况,让用户体验离家如在家的感觉。与传统家居相比,智能家居有利于提高家居生活的便利性、舒适性、安全性,众多新建小区多多少少收配备了部分分智能家居设备,但是距离真正的智能家居仍有一段距离。未来,智能家居将成为现代生活的标准。
该系统主要分为传感器监控模块、自动报警模块和远程控制模块三个模块。传感器监测模块包括环境温湿度和空气质量监测,用户可以在微信小程序上查看实时传感数据;自动报警模块在传感数据超过设定阈值时报警,提醒用户注意火灾;远程控制模块可通过微信小程序远程控制LED灯、风扇和加湿器的开关可以远程控制家用电器的开关。项目的整体功能是实时收集数据,并将数据上传到云平台,通过云平台浏览历史数据。项目主要分为两部分:硬件部分ESP32开发板数据采集与通信模块;软件部分是OneNET物联网平台端和微信小程序端。具体实现如下: 根据室内监控和家电操作的需要,设计系统实现的思路:通过传感器获取数据, 单片机处理数据,通信模块传输数据,平台端存储和处理数据,小程序端查看数据。
3.硬件总体设计
如下图所示。
3.2单片机选型及传感器选型
3.2.1ESP32模块
SP32-WROOM-32 一般型Wi-Fi BT BLE MCU该模块功能强大,用途广泛,可用于语音编码、音频流量、MP3解码等。ESP32还集成了电容式触摸传感器、霍尔传感器、低噪声传感放大器等丰富的外设,SD卡接口,以太网接口,高速SDIO/SPI、UART、I2S 和I2C等。
3.2.2温湿度模块
本系统采集环境的温湿度选择 DHT11 温湿度传感器。DHT11 采用数字温湿度传感器 采用校准数字信号输出,可同时检测温度和湿度,采用专用数字模块采集 温湿度传感技术,能保证产品的高可靠性和稳定性。 该传感器包括一个电阻式感湿元件和一个 NTC 测温元件,利用元件的电气特性 温湿度的变化测量了环境的温湿度、元件和高性能 8 单片机相连。该产品具有 高品质、响应快、抗干扰强、性价比高。低功耗和非常小的体积使其应对 用于各种复杂场景。DHT11 为 4 针单排引脚封装,连接方便。DHT11 温湿度模块实物图如下图所示。
4.1.1.如何连接设备上网
#include <WiFi.h> const char* ssid = "baby"; //wifi名称 const char* password = "13456789";//wifi密码 void setupWifi(){ delay(10); Serial.println("连接WIFI"); WiFi.begin(ssid, password); while (!WiFi.isConnected()) { Serial.print("."); delay(500); } Serial.println("OK"); Serial.println("Wifi连接成功"); }
4.1.2、MQTT接入地址
1、mqtt该协议的接口端号为6002,mqtts该协议的接口端号为1883
const char *mqtt_server = "183.230.40.96"; //onenet 的 IP地址 const int port = 6002; //端口号
2.如何发送数据?onenet
我们需要在这里查阅onenet的相关文档
OneNET - 中国移动物联网开放平台
#define mqtt_devid "945965335" //设备ID #define mqtt_pubid "577632" //产品ID ////鉴权信息 #define mqtt_password "123456" ////鉴权信息 char msg_buf[200]; //发送信息缓冲区 char msgJson[75]; //发送json格式的数据 unsigned short json_len = 0; //json长度 //信息模板 char dataTemplate[] = "{\"temp\":%.2f,\"humi\":%.2f,\"led\":%d}"; // temp humi led要与onenet相对应
void sendTempAndHumi() { if (client.connected()) { //dht.readHumidity() snprintf(msgJson,75,dataTemplate,dht.readTemperature(),dht.readHumidity(),god); json_len = strlen(msgJson); //msgJson的长度 msg_buf[0] = char(0x03); ///发送的数据必须遵循ONENET要求发送, 根据要求,第一个数据是3 msg_buf[1] = char(json_len >> 8); ///数据的第二位是要发送的数据长度的高八位 msg_buf[2] = char(json_len & 0xff); ///数据第三位是发送数据长度低的八位 memcpy(msg_buf 3, msgJson, strlen(msgJson)); //从msg_buf第四个开始,放入要传输的数据msgJson msg_buf[3 strlen(msgJson)] = 0; //作为最后一个添加一个0, 这样发送msg_buf准备好了 Serial.print("public the data:"); Serial.print(msgJson); client.publish("$dp", (uint8_t *)msg_buf, 3 strlen(msgJson)); //将数据发送到主题 delay(500); } }
在setup()函数每5秒定义一次信息OneNET平台
tim1.attach(5, sendTempAndHumi); ///定期每5秒调用一次数据函数sendTempAndHumi
3、在 setup()函数订阅命令下发主题
client.setCallback(callback); ////订阅命令下发主题
//收到主题发布的回调, 注意这个回调要实现三个形参 1:topic 主题, 2: payload: 传递的信息 3: length: 长度 void callback(char *topic, byte *payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); for (int i = 0; i < length; i ) { Serial.print((char)payload[i]); } Serial.println(); if ((char)payload[0] == '0') { digitalWrite(led, LOW); //
god=0;
} if ((char)payload[0] == '1') {
digitalWrite(led, HIGH); //
god=1;
}
else{}
}
4.1.3、测温湿度
使用DHT11模块,调用库
#include "DHT.h"
定义DHT11,数据引脚我接在IO13口
#define DHTPIN 13 // io13
#define DHTTYPE DHT11 // DHT 11
DHT dht(DHTPIN, DHTTYPE);
5、总代码(当然了这个是新手必备,只需要一个DHT11和一个LED灯就可以实现的)
#include <WiFi.h>
#include "DHT.h"
#include "PubSubClient.h"
#include "Ticker.h"
#define DHTPIN 13
#define DHTTYPE DHT11 // DHT 11
#define led 18 //灯的接口
DHT dht(DHTPIN, DHTTYPE);
int god=0;
const char *ssid = "baby"; //wifi名称
const char *password = "12345671";//wifi密码
const char *mqtt_server = "183.230.40.96"; //onenet 的 IP地址
const int port = 6002; //端口号
#define mqtt_devid "999999735" //设备ID
#define mqtt_pubid "599995" //产品ID
//鉴权信息
#define mqtt_password "123456" //鉴权信息
WiFiClient espClient; //创建一个WIFI连接客户端
PubSubClient client(espClient); // 创建一个PubSub客户端, 传入创建的WIFI客户端
char msg_buf[200]; //发送信息缓冲区
char msgJson[75]; //要发送的json格式的数据
unsigned short json_len = 0; //json长度
//信息模板
char dataTemplate[] = "{\"temp\":%.2f,\"humi\":%.2f,\"led\":%d}"; // temp humi要与onenet相对应
Ticker tim1; //定时器,用来循环上传数据
//连接WIFI
void setupWifi(){
delay(10);
Serial.println("连接WIFI");
WiFi.begin(ssid, password);
while (!WiFi.isConnected())
{
Serial.print(".");
delay(500);
}
Serial.println("OK");
Serial.println("Wifi连接成功");
}
void setup() {
Serial.begin(115200);
pinMode(led,OUTPUT);//输出
setupWifi(); //调用函数连接WIFI
Serial.print(F("DHT11 test!"));
dht.begin();
client.setServer(mqtt_server, port); //设置客户端连接的服务器,连接Onenet服务器, 使用6002端口
client.connect(mqtt_devid, mqtt_pubid, mqtt_password); //客户端连接到指定的产品的指定设备.同时输入鉴权信息
if (client.connected())
{
Serial.print("OneNet is connected!");//判断以下是不是连好了.
}
//client.setCallback(callback); //设置好客户端收到信息是的回调
client.setCallback(callback); //订阅命令下发主题
tim1.attach(5, sendTempAndHumi); //定时每5秒调用一次发送数据函数sendTempAndHumi
}
void loop() {
delay(5000);
float h = dht.readHumidity();
float t = dht.readTemperature();
float f = dht.readTemperature(true);
if (isnan(h) || isnan(t) || isnan(f)) {
Serial.println(F("Failed to read from DHT sensor!\n"));
return;
}
//可以让我们通过串口查看数据
Serial.print(F("Humidity: "));
Serial.print(h);
Serial.print(F("% Temperature: "));
Serial.print(t);
Serial.print(F("℃ \n "));
if (!WiFi.isConnected()) //先看WIFI是否还在连接
{
setupWifi();
}
if (!client.connected()) //如果客户端没连接ONENET, 重新连接
{
clientReconnect();
delay(100);
}
client.loop(); //客户端循环检测
}
void sendTempAndHumi()
{
if (client.connected())
{
//dht.readHumidity()
snprintf(msgJson,75,dataTemplate,dht.readTemperature(),dht.readHumidity(),god);
json_len = strlen(msgJson); //msgJson的长度
msg_buf[0] = char(0x03); //要发送的数据必须按照ONENET的要求发送, 根据要求,数据第一位是3
msg_buf[1] = char(json_len >> 8); //数据第二位是要发送的数据长度的高八位
msg_buf[2] = char(json_len & 0xff); //数据第三位是要发送数据的长度的低八位
memcpy(msg_buf + 3, msgJson, strlen(msgJson)); //从msg_buf的第四位开始,放入要传的数据msgJson
msg_buf[3 + strlen(msgJson)] = 0; //添加一个0作为最后一位, 这样要发送的msg_buf准备好了
Serial.print("public the data:");
Serial.print(msgJson);
client.publish("$dp", (uint8_t *)msg_buf, 3+strlen(msgJson));
//发送数据到主题
delay(500);
}
}
//收到主题下发的回调, 注意这个回调要实现三个形参 1:topic 主题, 2: payload: 传递过来的信息 3: length: 长度
void callback(char *topic, byte *payload, unsigned int length)
{
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
if ((char)payload[0] == '0') {
digitalWrite(led, LOW); //
god=0;
} if ((char)payload[0] == '1') {
digitalWrite(led, HIGH); //
god=1;
}
else{}
}
void clientReconnect()
{
while (!client.connected()) //再重连客户端
{
Serial.print("reconnect MQTT...");
if (client.connect(mqtt_devid, mqtt_pubid, mqtt_password))
{
Serial.print("connected");
}
else
{
Serial.print("failed");
Serial.print(client.state());
Serial.print("try again in 5 sec");
delay(5000);
}
}
}
6、微信开发者工具
自己创建一个项目,直接替换index.js,index.wxml,index.wxss
index.js
// index.js
// 获取应用实例
const app = getApp()
const mqtt=require('../../utils/mqtt')
Page({
data: {
motto: 'Hello World',
userInfo: {},
hasUserInfo: false,
canIUse: wx.canIUse('button.open-type.getUserInfo'),
canIUseGetUserProfile: false,
canIUseOpenData: wx.canIUse('open-data.type.userAvatarUrl') && wx.canIUse('open-data.type.userNickName') // 如需尝试获取用户信息可改为false
},
// 事件处理函数
bindViewTap() {
wx.navigateTo({
url: '../logs/logs'
})
},
onLoad() {
if (wx.getUserProfile) {
this.setData({
canIUseGetUserProfile: true
})
}
},
getUserProfile(e) {
// 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认,开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
wx.getUserProfile({
desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
success: (res) => {
console.log(res)
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
},
getUserInfo(e) {
// 不推荐使用getUserInfo获取用户信息,预计自2021年4月13日起,getUserInfo将不再弹出弹窗,并直接返回匿名的用户个人信息
console.log(e)
this.setData({
userInfo: e.detail.userInfo,
hasUserInfo: true
})
},
points:function(e) {
var that = this
wx.request({
//设备ID
//api-key
url: 'http://api.heclouds.com/devices/xxxxxxxxxx/datapoints?', //xxxxxxxxxx这里填写你的设备id
header:{
"api-key":"5oXSCXaNrBBlsQm6YNWU3wtHu1U=" //这里写你的api-key
},
data:{
limit:1
},
method :"GET",
//获取成功
success:function(res){
that.setData({
light:res.data.data.datastreams[0].datapoints[0].value,
wendu:res.data.data.datastreams[1].datapoints[0].value,
shidu:res.data.data.datastreams[3].datapoints[0].value, //这里的shidu要跟wxml{
{shidu}} 名字相同
})
}
})
},
openled:function(e){
let pafload
if(e.detail.value==true)
pafload = 1
else
pafload =0
console.log(pafload)
wx.request({
url: 'http://api.heclouds.com/cmds?device_id=xxxxxxxxxx',
//*号这里写你设备id//设备ID//api-key
header:{
'content-type':'application/json',
"api-key":"5oXSCXaNrQm6YNWU3wtHu1U=" //这里写你的api-key
},
method :"POST",
data:pafload,//数据1是为灯开
success(res){
console.log("控制成功,已开灯")
console.log(res)
console.log(res.data);
}
})
},
/**
* 生命周期函数--监听页面加载 */
onLoad: function (options) {
this.points() //这个是我获取onenet数据的函数
var that=this
setInterval(function(){
that.points();
},3000 //这里我设置3秒刷新一次
)
},
})
index.wxml
<!--pages/index/index.wxml-->
<view class= "content">
<view class= "zm">
<text class="zm1">照明开关</text>
<switch class="kai" id='1' bindchange="openled"/>
</view>
<view style="flex:1;width:100%">
<label class="xia">
<text class="zm1">当前温度:{
{wendu}}°C</text>
</label>
</view>
<view style="flex:1;width:100%">
<label class="xia">
<text class="zm1">当前湿度:{
{shidu}} %</text>
</label>
</view>
</view>
index.wxss
/* pages/index/index.wxss */
page {
background: #f6f6f6;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.headTitle{
width: 100%;
height: 80rpx;
background-color: #ffffff;
overflow:hidden ;/** 设置超出内容隐藏 */
white-space:nowrap; /*设置超出不换行 */
border-bottom :1px solid #F3F3F3;
}
.headTitle .titleItem{
display: inline-block;
line-height:80rpx;
color: #889999;
font-size:34rpx;
margin: 0 20rpx;
}
.headTitle .selctItem{
color: #000000;
font-weight: bold;
}
.itemView{
width: 100%;
height:180rpx;
position: relative;
border-bottom: 1px solid #F3F3F3;
}
.zm{
margin-top: 20rpx;
border:1px solid#ebe4e286;
width:750rpx;
height: 100rpx;
border-radius: 5px;
font-size: 36;
font-weight: bold;
line-height: 80rpx;
color: #32d5e0;
text-align: center;
display: flex;
position: relative;/*父元素位置要设置为相对*/
}
.login-btn{
width: 40%!important;
background-color: #33ff33;
color: white;
font-weight: normal;
}
.content{
margin-top: 20rpx;
border:1px solid#ebe4e286;
width:750rpx;
height: 600rpx;
border-radius: 5px;
font-size: 36;
font-weight: bold;
line-height: 80rpx;
color: #32d5e0;
text-align: center;
flex-direction: column;
display: flex;
}
.xia{
justify-content: space-between;
}
.zm1{
position: absolute; /* 要约束所在位置的子元素的位置要设置成绝对 */
left: 30rpx; /* 靠右调节 */
}
.radio{
display:inline-block; /* 横向布局*/
}
.kai{
position: absolute; /* 要约束所在位置的子元素的位置要设置成绝对 */
right: 100rpx; /* 靠右调节 */
}
.mos{
left: 120rpx; /* 靠右调节 */
height: 200rpx;
}
微信小程序链接
百度网盘
链接:https://pan.baidu.com/s/1v-M8HJ4UTib-ku59SYu-KA?pwd=1v99 提取码:1v99