65.9K
CodeProject 正在变化。 阅读更多。
Home

HASSI - 基于 Arduino 的物联网家居自动化、安全与安防

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.98/5 (17投票s)

2016年8月7日

CPOL

14分钟阅读

viewsIcon

42889

downloadIcon

2542

基于面部识别的安全家居,基于 ThingSpeak 的监控,基于 Mqtt 的控制,基于 Gmail 的通知,搭配低成本 Arduino UNO 和 C#

概述

有人说“创新始于家庭”(好吧,我把“慈善”改成了“家庭”。但话说回来!)

每一个 DIY 爱好者至少都会尝试创造一些自己能在家中使用的解决方案、产品或应用程序。因此,家居自动化一直是嵌入式开发者和 DIY 爱好者热门的研究领域。

当早期物联网开始普及时,公司们接触到了这些 DIY 爱好者,也就是我们通常所说的创客社区。所以,创客社区是那些最早接触物联网设备的人。不用说,家居自动化再次成为创新、开发和早期解决方案的核心。作为一名 DIY 爱好者,我的妻子 Moumita 也是一位电路制作高手,所以如果我们要在 DIY 类别中选择一个项目,这对我们来说并不是一个艰难的决定。它必须是家居自动化。

对历史感兴趣的人可能知道,在上次 CodeProject 举办的物联网教程中,我主要使用 Arduino,创建了自己的服务,并主要使用 HTTP 协议进行连接。这次,我们联手打造一个更强大、更完善的解决方案,以解决更关键的问题。

Moumita 一直在制作我过去所有项目所需的电路。但今年,她开始写作和编程。这给了我巨大的力量去承担更复杂的项目。

所以,当我们决定从事家居自动化项目时,我们必须头脑风暴一个我们想要着手的好产品。我们首先详细列出了家居自动化的各个方面。它们是:

  • 家庭监控 - 在家中部署传感器,用于监测光照、温度等各种参数。
  • 家庭控制 - 远程控制空调、风扇、灯光。
  • 家庭自动化 - 自动化设备,例如天黑时自动打开灯泡。
  • 家庭安全 - 保护家庭免受盗贼和入侵者的侵害。
  • 家庭消防安全 - 通过预防火灾使家庭更安全。

我们没有只关注一个概念,而是决定构建一个演示这些方面解决方案的系统。在编写初学者教程时,最好从涵盖某个领域几乎所有方面的内容入手。

因此,在本教程中,我们将与您分享一个强大的家居自动化、监控、安全与安防框架,这一切都通过一个应用程序实现。

过去,使用 GSM 系统的家居控制系统已经被用于远程监控和控制。每次控制都生成短信是不切实际的,因为短信费用很高。此外,GSM 也不提供任何可视化支持,这意味着无法以图形格式观察数据的变化。

物联网是硬件技术中近期日益增长的趋势。物联网使设备能够连接到互联网。因此,可以通过手机、平板电脑、PC 等各种设备进行控制和监控。此外,由于此教程是为 CodeProject 的物联网教程竞赛而计划的,因此物联网必须是其核心部分。

因此,我们通过与 Google 和 ThingSpeak 的完整云集成,以及与 Mqtt 的消息网关集成,扩展了我们的框架,并将此框架命名为 **HASSI**。

详细功能

该项目提供基于 Eigen PCA 面部识别的面部识别身份验证。家庭的 `锁定` 和 `解锁` 命令与面部身份验证相关联。如果未经身份验证的用户尝试在未进行面部身份验证的情况下开门,则会生成一条带有其面部照片的 Gmail 通知。万一发生火灾,系统还会生成一条 Gmail 消息,可以通过 IFTTT 进一步转发。

因此,该项目有三个功能:

  • 家庭监控:在家庭监控系统中,我们有一个温度传感器,可以通过 ThinkSpeak 频道远程查看温度传感器值。因此,可以在远程以图表形式查看数值。
  • 家庭安全与报警系统:当温度传感器值超过 40 度时,会向您的 Gmail 发送通知,同时本地也会发出警报。收到此邮件后,您可以做出适当的决定,还可以使用 IFTTT 将此邮件转发到许多其他频道。
  • 家庭控制:我们通过我们的手机使用 Mqtt 协议实现了照明和风扇控制系统(小型直流风扇和小型 LED 系列灯!)。

家庭安全

图:项目框图

HASSI 框架提供了通过面部识别打开门锁的功能。因此,用户需要进行面部验证才能打开门锁。任何试图在未进行面部验证的情况下解锁房屋的入侵者都会被抓拍,其照片和警报将发送到所有者的 Gmail 帐户。

我们将使用 C# 作为用户与物理世界以及物理世界与云的接口的前端。我们将使用 Arduino C 程序在 Arduino 中通过串行通信与此 C# UI 进行通信。

现在,这是软件工作的几种方式。

当您坐在系统前时,软件将通过面部识别进行用户身份验证。为此,您需要注册您的面部,并且还需要管理员密码。只有您输入正确的管理员密码,您的面部才会被注册。它会显示您在摄像头范围内。因此,在管理员身份验证后,您可以进行面部验证。面部识别是打开您家门锁所必需的。

如果您的面部未被识别,则软件会显示“未知”消息。软件会发出语音提示,告知有未知人员试图开门。软件还会通过添加未经授权人员的照片向 Gmail 发送消息。因此,该软件不仅会发出语音提示,还会同时拍摄照片。

在通过软件启动任何家居控制系统之前,您需要提供 Arduino 端口。Arduino 通过虚拟端口连接到 PC。会有不同的虚拟端口,但软件会显示已连接的端口。

当您开始监控温度时,软件将在您的 C# UI 界面上用简单的折线图显示温度。

数据也可在 ThingSpeak 频道中找到,该频道以图表形式显示温度值,以便您可以从世界任何地方远程监控它。

如果感应到的温度超过 40 摄氏度(可以通过点燃蜡烛并将其靠近温度传感器来模拟),则软件会在本地显示消息,同时还会向 Gmail 帐户发送消息。本地会触发蜂鸣器,以便在家中的任何人都能了解情况。

我们使用 MQTT 协议进行通信。它是一种发布/订阅协议。您可以从任何地方将消息发布到一个消息代理,然后该消息会异步推送到所有订阅了消息发布频道的对等方。

我们的 C# 客户端将接收消息,并根据消息向 Arduino 发送有关灯光、风扇的开/关控制信号,以及门的打开和关闭控制信号。

硬件制作

我们的工作是硬件和软件的结合。硬件组件规格如下:

  1. LED 灯阵列。功率为 27 瓦,需要电池供电。我们使用 12V 4.5A 电池为 LED 阵列供电。
  2. 在风扇模块中,我们使用 CPU 风扇。
  3. 一个齿轮电机,用于模拟门的打开和关闭。
  4. 为了生成通知,我们使用 LM 35 温度传感器和一个蜂鸣器。
  5. 一套继电器,用于控制灯光和风扇。

电路原理图

图:完整电路图(点击放大)

您可以此处了解如何搭建 12v 电源。

我们使用了为 Urobi 开发的继电器板

上图是项目的主电路。Arduino 连接到继电器模块。通过继电器控制灯泡和风扇。电气单元通过继电器连接到主电源。当继电器激活时,电气单元开启,消耗更多电流。当继电器关闭时,电气负载不消耗电流。LM 35 连接到 A5,用于感应温度值。感应值通过串行端口发送到 PC,然后传输到 ThingSpeak 频道。

第一个继电器用于控制门锁的方向。其输入为 +-12v。当该继电器关闭时,其输出为 +12v,当激活时,输出为 -12v。该继电器的输出连接到第二个继电器。该继电器的输出连接到门锁。因此,如果继电器 1 和继电器 2 都激活,门将逆时针转动;如果只有继电器 2 激活,门将顺时针转动。

继电器 3 和 4 分别驱动灯光和风扇。这些继电器由 12V 4.5A 电池供电。

由于我们使用的是 12V 继电器,而微控制器 5V 的供电无法驱动它,因此通过 MCTE2E 光电耦合器连接继电器。

硬件模块如下:

图:12 伏电源

图:继电器板

图:LED 灯阵列、LM 35 和蜂鸣器

编码

Arduino 代码

让我们先看看我们的 Arduino Sketch。`bulbPin` 和 `fanPin` 是与灯泡和风扇继电器连接的引脚。`tSensor` 是连接 LM 35 温度传感器的模拟引脚。`lockPin` 是连接门锁的继电器。`dpdt` 是方向控制继电器,根据其状态输出 +12v 或 -12v。当 `dpdt` 和 `lockPin` 都设置为高电平时,带门锁的电机将顺时针转动。当 `dpdt` 为低电平而 `lockPin` 为高电平时,锁电机将逆时针转动,模拟锁关闭状态。

由于 Arduino 将接收来自我们 C# 客户端的串行数据,因此会调用 `Serial.begin()` 来初始化串行端口通信。当 C# 向 Arduino 发送数据时,如果 `Serial.available()` 非零,则会将字符值转换为数字。例如,PC 发送命令 '1',Arduino 通过从接收字符的 ASCII 值中减去 `48` 将其转换为 `1`。

命令 1 和 2 分别对应灯泡开启和关闭。

命令 3 和 4 分别对应风扇开启和关闭。

命令 5 和 6 分别对应锁定和解锁。请注意,当启动锁定或解锁操作时,电机将转动约 300ms 然后停止。这是为了使电机仅进行小范围移动即可完成锁定和解锁。

命令 0 对应于关闭所有家庭设备。当用户外出时,这很有用。他可以用一个命令关闭所有电器。

int bulbPin=9;
int fanPin=11;
int tSensor=5;
int buzzPin=8;
int dpdt=13;
int lockPin=12;
int ref=0;

void setup() {
  analogReference(INTERNAL);
  // initialize digital pin 13 as an output.
  Serial.begin(19200);
  pinMode(bulbPin, OUTPUT);
  pinMode(fanPin,OUTPUT);
  pinMode(buzzPin,OUTPUT);
  pinMode(dpdt,OUTPUT);
  pinMode(lockPin,OUTPUT);
  digitalWrite(lockPin,LOW);
  digitalWrite(dpdt,LOW);
  digitalWrite(bulbPin,LOW);
  digitalWrite(fanPin, LOW);
  digitalWrite(buzzPin,LOW);
  Serial.println(ref);
}

// the loop function runs over and over again forever
bool on=false;
float avEn=0;
int count=0;
void loop() {

  int reading=analogRead(tSensor);
  float tempC = reading / 9.31;
  avEn=avEn+tempC;
  //Serial.println(tempC);
  count++;
  if(tempC>40)
  {
  // Serial.println("High");
   digitalWrite(buzzPin,HIGH);
 }
 else
 {
  digitalWrite(buzzPin,LOW);
 }
  if(Serial.available())
  {
   int r=Serial.read();
   r=r-48;
   //1= light on 2=light off 3 fan on 4 fan off 0 all off
   switch(r)
   {
    case 1: digitalWrite(bulbPin,HIGH);
    break;
    case 2: digitalWrite(bulbPin,LOW);
    break;
    case 3: digitalWrite(fanPin,HIGH);
    break;
    case 4: digitalWrite(fanPin,LOW);
    break;
    case 5: // Lock
    digitalWrite(dpdt,HIGH);
    digitalWrite(lockPin,HIGH);
    delay(3000);
    digitalWrite(lockPin,LOW);
    digitalWrite(dpdt,LOW);
    break;

    case 6: //unlock 
    digitalWrite(lockPin,HIGH);
    delay(5000);
    digitalWrite(lockPin,LOW);
    break;
    case 0: digitalWrite(fanPin, LOW);
            digitalWrite(bulbPin,LOW);
     break;
   }
  }
  
  delay(100);              // wait for 100 ms
}

C# 代码

我们在 Sergio 的实时面部识别文章的基础上开发了我们的代码。

图:窗体设计(点击放大)

当窗体加载时,我们的 Mqtt 客户端将连接到全局 Mqtt Broker iot.eclipse.org(或 test.mosquitto.org)。如果 Mqtt 客户端 `mc` 成功连接到 Broker,它将订阅一个名为 `rupam/MyHome` 的频道(您必须更改名称,因为许多读者可能尝试同时使用同一频道,这会导致您的命令出现意外行为)。因此,每当 `mqtt` 收到命令时,都会调用 `clientmc_MqttMsgPublishReceived` 事件处理程序。

我们为所有操作提供了手动按钮:灯泡开、灯泡关、风扇开、风扇关、全部关闭、锁定和解锁。

同时,我们循环遍历所有可用的串行端口,并将端口名称加载到标有“连接到 Arduino”的 `GroupBox` 中的 `combobox` 里。

MqttClient mc = null;
    Color ac = Color.Blue;
    string topic = "rupam/MyHome"; 
private void FrmPrincipal_Load(object sender, EventArgs e)
        {
            string[] ports = SerialPort.GetPortNames();
            for (int i = 0; i < ports.Length; i++)
            {
                cmbPort.Items.Add(ports[i]);
            }
            try
            {
                mc = new MqttClient("test.mosquitto.org");
                mc.Connect("RUPAM");
                mc.Subscribe(new string[] { topic }, new byte[] { (byte)0 });
                mc.MqttMsgPublishReceived += mc_MqttMsgPublishReceived;
                label5.Text = "Connected to test.mosquitto.org in channel rupam/MyHome";
                label5.BackColor = Color.Green;
            }
            catch
            {
                label5.Text = "No Connection with Server-Check Internet Connection";
                label5.BackColor = Color.Red;
            }
            ac = btnConnect.BackColor;
        }

一旦串行端口连接成功,您就可以在尝试 Mqtt 远程控制之前,通过点击这些按钮来测试操作。当点击一个特定按钮时,它的颜色会变为绿色。当点击对应于该操作的另一个按钮时,按钮的颜色会恢复为原始颜色。因此,当点击“灯泡开”时,“灯泡关”变为灰色,“灯泡开”为绿色;当点击“灯泡关”时,该按钮将变为绿色,而标有“灯泡开”的按钮将恢复其原始颜色。

private void btnBulbOn_Click(object sender, EventArgs e)
        {
            serialPort1.WriteLine("1");
            btnBulbOn.BackColor = Color.Green;
            btnBulbOff.BackColor = ac;
        }

        private void btnBulbOff_Click(object sender, EventArgs e)
        {
            serialPort1.WriteLine("2");
            btnBulbOn.BackColor = ac;
            btnBulbOff.BackColor = Color.Green;
        }

        private void btnFanOn_Click(object sender, EventArgs e)
        {
            serialPort1.WriteLine("3");
            btnFanOn.BackColor = Color.Green;
            btnFanOff.BackColor = ac;
        }

        private void btnFanOff_Click(object sender, EventArgs e)
        {
            serialPort1.WriteLine("4");
            btnFanOn.BackColor = ac;
            btnFanOff.BackColor = Color.Green;
        }

        private void btnAllOff_Click(object sender, EventArgs e)
        {
            serialPort1.WriteLine("0");
            btnFanOff.BackColor = ac;
            btnFanOn.BackColor = ac;
            btnBulbOn.BackColor = ac;
            btnBulbOff.BackColor = ac;
        }

对于“锁定”和“解锁”按钮,都需要进行用户面部验证。一旦用户面部被验证,`Label4` 将会显示已验证用户的名称标签,否则将显示“未知”标签。如果用户面部未知,将捕获照片并发送到输入的电子邮件,同时发送摄像头前的人脸照片。Gmail 消息通过 `EmailSend` 类中的 `SendMail` 方法发送,并附带附件。

您需要将您的邮箱 ID 和密码设置在 `EmailSend` 的 `SendMail` 方法中,才能从您的邮箱发送邮件。

您还需要遵循官方 Gmail 教程,允许不太安全的应用程序访问您的邮箱

 public class EmailSend
    {
        static Stopwatch stTime = new Stopwatch();
        public static void SendMail(string emailTo, string subject,
                                    string body,string attachmentPath)
        {
            if (!stTime.IsRunning || stTime.Elapsed.TotalMilliseconds > 10000 || 
                                     subject.Contains("Alarm"))
            {
                string emailFrom = "YourMailID@gmail.com";
                string password = "yourgmailPassword";
                string smtpAddress = "smtp.gmail.com";
                int portNumber = 587;
                bool enableSSL = true;

                using (MailMessage mail = new MailMessage())
                {
                    mail.From = new MailAddress(emailFrom);
                    mail.To.Add(emailTo);
                    mail.Subject = subject;
                    mail.Body = body;
                    mail.IsBodyHtml = true;
                    // Can set to false, if you are sending pure text.
                    if (attachmentPath.Length > 2)
                    {
                        mail.Attachments.Add(new Attachment(attachmentPath));
                    }

                    using (SmtpClient smtp = new SmtpClient(smtpAddress, portNumber))
                    {
                        smtp.Credentials = new NetworkCredential(emailFrom, password);
                        smtp.EnableSsl = enableSSL;
                        smtp.Send(mail);
                    }
                }
                if (!stTime.IsRunning)
                {
                    stTime.Start();
                }
                else
                {
                    stTime.Reset();
                    stTime.Restart();
                }
            }
        }
    }
 private void btnLock_Click(object sender, EventArgs e)
        {
            if (!label4.Text.Contains("Unknown"))
            {
                serialPort1.WriteLine("5");
                btnUnlock.BackColor = ac;
                btnLock.BackColor = Color.Green;
                grpDoor.Enabled = false;                
            }
            else
            {
                //// Send a Mail
                (new System.Speech.Synthesis.SpeechSynthesizer()).SpeakAsync
                ("Unauthorized Attempt!!!");

                String fileName = ".\\Unknown\\Image_" + DateTime.Now.ToString
                       ("yyyyMMdd_hhmmss") + ".jpg";//Save the filename first on 

                imageBoxFrameGrabber.Image.Save(fileName);
                EmailSend.SendMail("receiversmailID@gmail.com", 
                "Unknown Person", "Unknown person attempting unlock " + 
                 DateTime.Now, fileName);
                ///////
            }            
        }

名为 `serialPort1` 的串行端口对象添加了一个名为 `serialPort1_DataReceived` 的事件处理程序。每当端口中有数据时,就会通过委托捕获(因为它是一个独立的线程,无法使用 UI 元素)。

 private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            string s = serialPort1.ReadLine();
            this.BeginInvoke(MyDlg, s);
        }

请注意,Arduino 只将温度数据发送到 C# 应用程序。因此,该应用程序只接收温度数据。这些数据被发送到 ThingSpeak 频道,并分析其阈值 >40。如果温度超过阈值,则会向用户发送电子邮件。

void Display(string s)
        {            
            try
            {
                double en = double.Parse(s);
                chart1.Series[0].Points.AddY(en);
                if (en > 40)
                {
                    EmailSend.SendMail("receiversMailId@gmail.com", 
                    "Fire at home Alarm","Fire at Home Alarm "+DateTime.Now ,"");
                }
                //string s = UpdateThingSpeakData(new string[] { "40", "40" }, 
                //"C606GJW98AWMN9P9");
                string s1 = UpdateThingSpeakData(new string[] 
                            { en.ToString() }, "MB2RQE6KCT9D670V");
            }
            catch
            {
            }
        }

在 Mqtt 事件处理程序中,我们检查命令,将字符命令转换为数字,然后通过调用按钮点击事件处理程序来相应地点击相应的按钮。

void mc_MqttMsgPublishReceived(object sender, 
     uPLibrary.Networking.M2Mqtt.Messages.MqttMsgPublishEventArgs e)
        {
            //throw new NotImplementedException();
            //MessageBox.Show(GetString(e.Message));
            string s = "";
            try
            {
                byte b = e.Message[0];
                //e.Retain = false;
                s = (b - 48).ToString();
            }
            catch
            {
                return;
            }
            this.Invoke((MethodInvoker)delegate
            {
                if (s.Equals("1"))
                {
                    btnBulbOn_Click(btnBulbOn, new EventArgs());
                }
                if (s.Equals("2"))
                {
                    btnBulbOff_Click(btnConnect, new EventArgs());
                }
                if (s.Equals("3"))
                {
                    btnFanOn_Click(btnFanOn, new EventArgs());
                }
                if (s.Equals("4"))
                {
                    btnFanOff_Click(btnFanOff, new EventArgs());
                }
                if (s.Equals("0"))
                {
                    btnAllOff_Click(btnAllOff, new EventArgs());
                }
                if (s.Equals("5"))
                {
                    if (grpDoor.Enabled)
                    {
                        btnLock_Click(btnAllOff, new EventArgs());
                    }
                    else
                    {
                        (new System.Speech.Synthesis.SpeechSynthesizer()).SpeakAsync
                        ("Unauthorized Attempt!!!");
                    }
                }
                if (s.Equals("6"))
                {
                    if (grpDoor.Enabled)
                    {
                        btnUnlock_Click(btnAllOff, new EventArgs());
                    }
                    else
                    {
                        (new System.Speech.Synthesis.SpeechSynthesizer()).SpeakAsync
                        ("Unauthorized Attempt!!!");

                        String fileName = ".\\Unknown\\Image_" + 
                        DateTime.Now.ToString("yyyyMMdd_hhmmss") + 
                        ".jpg";//Save the filename first on                 
                        
                        imageBoxFrameGrabber.Image.Save(fileName);
                        EmailSend.SendMail("maneeshanmc27@gmail.com", 
                        "Unknown Person", "Unknown person attempting unlock " + 
                         DateTime.Now, fileName);
                    }
                }
            });
        }

您可以从 Google Play 下载MyMqtt App。在该应用的设置中,将 Broker 地址设置为 iot.eclipse.org 或 test.mosquitto.org(您在窗体中设置的任何地址)。然后发布到频道 `rupam/MyHome`。

您需要发布 1 来打开灯泡,2 来关闭灯泡,以此类推。

结果

首先,要成为认证用户,需要通过面部识别在软件上注册姓名。这需要管理员权限。如果授予了权限,则用户被视为授权。

图:用户注册第一步

图:用户面部注册管理员密码

图:授予管理员权限

图:用户已注册

现在,Arduino 已通过虚拟线路连接到系统。选择端口。

图:选择 Arduino 端口

图:启动家居控制温度单元

图:ThingSpeak 中也提供了温度数据

现在,要提高温度传感器值,可以将火柴棒靠近 LM-35(温度传感器)。

图:提高温度传感器值

图:温度升高,显示在图上

当温度升高并超过阈值时,将向 Gmail 发送通知。

图:温度通知的 Gmail 通知

现在,如果一个陌生人想开门,软件将显示“未知”。未知人员是指尚未注册姓名的人。

图:向用户显示未知

不仅软件显示“未知”,而且该未知人员的照片也将发送到 Gmail 地址,以便用户可以采取行动。

图:未知人员照片的电子邮件

结论

家居自动化一直是业余爱好者项目和工业物联网开发领域的研究热门领域。然而,与家庭监控和安全系统相比,家居自动化一直被单独考虑。在本项目中,我们将安全、安防、监控和控制系统与物联网相结合。安全系统确保在发生火灾时,用户能够尽早得到通知。安防系统确保未经授权的面部无法闯入家中。控制系统确保用户可以通过手机控制灯光和风扇。监控系统确保用户可以远程监控家中的温度,这对于家中有对温度敏感的植物很有帮助。结果表明,该系统满足所有先决条件,并生成实时数据和警报。通过结合自动气候控制(基于温度监控)和自动照明控制(基于光值监控)等其他功能,可以进一步改进此系统。

因此,尽管 HASSI 简单且成本低廉,但它仍然非常强大。您可以基于此框架构建更复杂的家居自动化项目。

© . All rights reserved.