esp8266搭建智能家居系统

一 背景

以前一直想着做一个智能家居的监控系统,说没时间是虚伪了,舍不得钱买服务器是真的…,后来想过内网穿透,使用树莓派或者pc本机跑服务程序,感觉太麻烦也一直没搞,所以人嘛,不想搞事情可以找一千个、一万个借口。

那现在为什么做了呢???

我这么抠,肯定是因为钱咯,难道是接了个外包的项目做?那肯定不是!

原因是经朋友推荐买了个国外的服务器做代理,上上网(你懂的),一个月才18元!虽然配置有点低吧,对我来说足够了。

我就想,钱都交了,只是用于上网,那不就太浪费了,为何不多装几个服务程序呢?

本着节省的精神,回想之前的智能家居理想,于是说干就干!…然后半夜3点才睡觉,第二天早上9点起床就写文章!

看来看去,最后选择了比较简单的 MQTT 协议,服务程序选的mosquitto

MQTT 是一个基于发布/订阅模式的消息传输协议。它具有轻量级、开放、简单,易于实现,通信带宽要求低等特点。这些特点使得它对机器与机器的通信(M2M)以及物联网应用(IoT)来说是很好的选择。

Mosquitto是一个开源消息代理,实现了MQTT协议版本3.1和3.1.1。Mosquitto轻量,适用于低功耗单板计算机到完整服务器的所有设备。Mosquitto项目还提供了用于实现MQTT客户端的C库以及非常受欢迎的mosquitto_pub和mosquitto_sub命令行MQTT客户端。

记住上面说的MQTT 协议 和 mosquitto 服务程序。

最后,忽然想到,只要能连接代理服务器,这样岂不是在全球任何地方都能控制家里的电器!!!

二 系统组成及原理

想要实现数据的远程监控,要有三部分,终端、服务端、客户端。

1、esp8266端

需要对终端(esp8266)进行监控,esp8266端不仅需要发送数据,还需要接收(订阅)数据,比如手机控制家里的电灯。
数据上传(publish): 不断采集各种传感器数据集,然后使用MQTT协议按一定时间间隔publish到服务端。
数据订阅(subscribe): 在publish数据的间隔期间,这个时间全部用于MQTT监听数据。

我写的这个软件还不够智能,其中传感器的类型、个数在设计的时候必须确定好,或者空留出位置。如果后面加了新的传感器,将需要修改源代码。
后续可以的话,重新设计一下esp8266的软件,然后再写一个上位机用于设定esp8266传感器的接口和类型。不知道这样的方案有没有开源的代码了,我猜可能有,毕竟程序员都是聪明人!谁还没有个脑子,哈哈哈哈哈…

系统关系

在这里插入图片描述

二 准备说明

0、知识积累

mqtt菜鸟教程:https://www.runoob.com/w3cnote/mqtt-intro.html

MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。

下面,我们将数据主要发布者称为采集终端,主要订阅者称为客户端,代理称为服务端,分别介绍。

1、采集终端

采集终端,也就是传感器数据采集端、输出控制端,即 单片机端。MQTT协议是基于TCP传输的,所以要求单片机+网络模块(W5500)+网线连接?不不不,使用esp32、esp8266 wifi连接、无线传输,不香吗?我这esp32、8266都有,虽然esp32性能要强一些,但是之前esp8266有硬件连接,而且刚好可以用上,所以使用8266了,毕竟编程上两者区别不大。

这里说明一下,我是使用arduino IDE编程,是可以直接下载MQTT库的(不然软件开发也不会那么快)
arduino IDE 下载MQTT库,点击菜单栏的 工具->管理库选项,弹出下面弹窗,按标记内容操作下载。
说明,需要“科学上网”下载才会无阻碍,否则可能会下载无响应。
在这里插入图片描述

硬件连接图:
占位!!!

2、服务器端:

服务器端,我安装的是CentOS 7,服务程序是mosquitto,mosquitto也可以说是代理程序,它帮你将消息由发布端(published)转发到订阅端(subsription)

CentOS 7安装mosquitto: 直接使用命令:yum install mosquitto
或者参考链接:https://blog.csdn.net/qq_34301871/article/details/93617204
mosquitto使用教程:https://www.cnblogs.com/chen1-kerr/p/7258487.html

3、客户端

客户端就是监控采集终端数据的,客户端可以是手机、电脑,或者其他联网设备,也可以是esp8266,用于展示家居状态数据或者获取设定的状态,不过我们一般客户端只讲PC端,下面介绍各平台的客户端软件。
因为比较简单,下面的软件就不上使用教程了,自己摸索,这就是该死的技术人的乐趣。
Android软件: 我用的是MQTT Dash(科学上网,google商店下载的,不得不说,里面好用的软件还是非常多的,而且免费无广告)
在这里插入图片描述
上图中的所有控件都是可以编辑自定义的,比如开灯是绿色,关灯设为灰色,检测到火灾烟雾显示红色等等。

windows软件: MQTTX
windows端目前没有找到可以自定义编辑的图形化界面监控软件(如上面的安卓一样)。
下面的软件只能用于程序开发阶段的调试使用,毕竟谁没事用这种界面看数据。
在这里插入图片描述
其实在我的系统设计中,想的是使用qt写一个通用的可编辑的图像化客户端软件,就像下图:
在这里插入图片描述
可编辑背景图,然后可添加各种终端并可设置终端图片,移动位置、设置终端控件大小等等功能。
但是奈何工作量不小,平时要工作,下班要玩游戏,没有动力啊。(说来说去就是懒)

其他平台客户端
其他平台如linux,没有使用过。不过MQTTX支持很多平台,可以下载对应的软件。
在这里插入图片描述
开发的时候使用windows的MQTTX软件调试挺好的,当系统差不多了,可以使用手机调试,毕竟手机携带方便。

四 软件开发

代码内容如下:
1、wifi连接,只有连接成功才会继续执行。
2、初始化各种传感器、和执行部件,如温湿度、超声波、led等。
3、设置MQTT订阅内容(依据 话题-Topic ),用于监听客户端发过来的指令。
4、循环监听、发送数据。

esp8266端代码(arduino IDE开发):

#include <ESP8266WiFi.h>
#include <Adafruit_NeoPixel.h>
#include <DHTesp.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"

//====================================================================================
#if 1   //1:使用串口打印输出   0:禁止串口输出
  #define coutInit(x) Serial.begin(x)
  #define cout Serial.print
  #define coutln Serial.println
#else
  #define coutInit(x) 
  #define cout(x)      
  #define coutln(x)
#endif 
//====================================================================================
#define WLAN_SSID       "差点让你知道的wifi名字了"
#define WLAN_PASS       "差点让你知道的wifi密码了"
//====================================================================================
WiFiClient client;
#define AIO_SERVER      "mosquitto服务端地址,可以是ip地址"
#define AIO_SERVERPORT  1883                   // use 8883 for SSL
#define AIO_USERNAME    "mosquitto的用户名,不会就去看mosquitto使用教程"
#define AIO_KEY         "mosquitto的用户密码"
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);

Adafruit_MQTT_Publish updataCount = Adafruit_MQTT_Publish(&mqtt,"esp_updataCount",0);
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt,"esp_Rx_1",0);

Adafruit_MQTT_Publish pub_temp = Adafruit_MQTT_Publish(&mqtt,"esp_temp",0);
Adafruit_MQTT_Publish pub_hum = Adafruit_MQTT_Publish(&mqtt,"esp_hum",0);
Adafruit_MQTT_Publish pub_distance = Adafruit_MQTT_Publish(&mqtt,"esp_distance",0);
Adafruit_MQTT_Subscribe sub_led = Adafruit_MQTT_Subscribe(&mqtt,"esp_led",0);

//====================================================================================
void MQTT_connect(int reconnect = 0)     //失联情况下会自动重新连接
{
  int8_t ret;
  if(!reconnect)
  {
    if (mqtt.connected()) 
      return;
  }

  cout("Connecting to MQTT... ");
  uint8_t retries = 20;  //自动重连20次,20次后连不上进入死循环,等待看门狗重启
  while ((ret = mqtt.connect()) != 0)   // connect will return 0 for connected
  { 
       coutln(mqtt.connectErrorString(ret));
       coutln("Retrying MQTT connection in 5 seconds...");
       mqtt.disconnect();
       delay(5000);  
       retries--;
       if (retries == 0) 
       {
         while (1); 
       }
  }
  coutln("MQTT Connected!");
}
//====================================================================================                                        
#define dhtPin 2
DHTesp dht;
float dht_temp;
float dht_hum;

void dht11Init()
{
  dht.setup(dhtPin, DHTesp::DHT11);
}
void getDHT11Data()
{
  TempAndHumidity dhtValues = dht.getTempAndHumidity();
  dht_temp = dhtValues.temperature;
  dht_hum = dhtValues.humidity;
}
//=======================================================================
#define  trigPin  12
#define  echoPin  13

void distanceInit()
{
  digitalWrite(trigPin, LOW);
  delayMicroseconds(1000);
  digitalWrite(trigPin, HIGH);
  pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
  pinMode(echoPin, INPUT);  // Sets the echoPin as an Input
}
float getDistance()
{
  digitalWrite(trigPin, LOW);  // Clears the trigPin
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);  // Sets the trigPin on HIGH state for 10 micro seconds
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  long duration = pulseIn(echoPin, HIGH);  // Reads the echoPin, returns the sound wave travel time in microseconds
  float distance = duration*0.034/2;  // Calculating the distance
  return distance;
}

//=======================================================================
#define ledPIN   4    //定义LED点阵 IO

void RGB_LED_Init(void)
{
  pinMode(ledPIN, OUTPUT);      
}
void openLED()
{
  digitalWrite(ledPIN, HIGH);
}
void closeLED()
{   
  digitalWrite(ledPIN, LOW);
}
//=======================================================================
void ledCallback(double led)
{
  cout(F("Get LED: "));
  coutln(led);
  if(led > 0.5)
    openLED();
  else
    closeLED();
}
void stringCallback(char *data, uint16_t len) {
  cout("Hey we're in a onoff callback, the button value is: ");
  coutln(data);
}

void setup() {
  coutInit(115200);
  delay(20);

  //===== 连接wifi =====
  cout("\nConnecting to ");
  coutln(WLAN_SSID);
  WiFi.begin(WLAN_SSID, WLAN_PASS);
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
    cout(".");
  }
  coutln();
  cout("WiFi connected,IP address: ");
  coutln(WiFi.localIP());

  //===== 初始化各种传感器 =====
  coutln("DHT11 initialization...");
  dht11Init();
  coutln("Ultrasonic initialization...");
  distanceInit();
  coutln("LED initialization...");
  RGB_LED_Init();
  
  //===== 设置subscribe =====
  //mqtt.subscribe(&onoffbutton);
  sub_led.setCallback(ledCallback);
  mqtt.subscribe(&sub_led);
}


void loop() 
{
  MQTT_connect();
  mqtt.processPackets(5000);

  int succes_count = 0;   //发送成功计数,用于判断连接状态,MQTT_connect函数短时间无法判断。
  //===== 发送计数 =====
  static unsigned int runCount;
  cout(F("Sending updataCount val ")); cout(runCount); cout("...");
  if (! updataCount.publish(runCount++))
  { 
    coutln(F("Failed"));
  }
  else 
  {
    coutln(F("OK!"));
    succes_count ++;
  }
    
  //===== 发送温度、湿度(DHT11) =====
  getDHT11Data();  //获取温湿度
  cout(F("Sending DHT11 - temp:"));
  cout(dht_temp);cout("...");
  if (! pub_temp.publish(dht_temp)) //发送温度
  {
    coutln(F("Failed"));
  }
  else 
  {
    coutln(F("OK!"));
    succes_count ++;
  }
    
  cout(F("Sending DHT11 - hum:"));
  cout(dht_hum);cout("...");
  if (! pub_hum.publish(dht_hum)) //发送湿度
  {
    coutln(F("Failed"));
  }
  else 
  {
    coutln(F("OK!"));
    succes_count ++;
  }
  
  //===== 发送距离 =====
  float distance = getDistance(); //获取距离
  cout(F("Sending distance:"));
  cout(distance);cout("...");
  if (! pub_distance.publish(distance))
  {
    coutln(F("Failed"));
  } 
  else 
  {
    coutln(F("OK!"));
    succes_count ++;
  }
  
  coutln(F("==========================="));
  
  //===== 发送失败处理方式 =====
  static int sendFaildCount = 0;  //连续全部发送失败次数
  if(succes_count == 0) //表示全部发送失败
  {
    sendFaildCount ++;
    if(sendFaildCount >= 3)
    {
      MQTT_connect(1);  //强制重连
      sendFaildCount = 0;
    }
  }
  else
    sendFaildCount = 0;
}

版权声明:本文为CSDN博主「求知者先」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42887343/article/details/122009160

生成海报
点赞 0

求知者先

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

阿达的红外射频遥控盒子(二)

功能描述 1、这个版本是在阿达的红外射频遥控盒子(一)的基本上修改,增加了USB口自动下载电路,更改了阿达的Q1连线问题,全部贴片原件用0805,USB增加了一个封装,更加