[IoT] OPEN THE DOOR スマホでドアを開ける [ESP8266]Wi-Fiモジュール [arduino IDE]

背景

会社の事務所の玄関ドアがめっちゃ古くてICカードも使えず鍵しか使えない、あるいはインタホンで室内の人呼び出し、ドアを開けてあげる。今Iotの時代がやって来て、こんなに面倒な入室システムは時代に遅れている、改造していきたいと思います。(物理的な破壊ぜず、今の入室システム影響無いように改造する)。

設計

ユースケース:入室しようとする人が玄関でルーム番号入力し「呼び出す」を押すと室内のインタホンが鳴り始めて、受話器を取って解除ボタンを押してドアが開く。
受話器の取り上げと解除ボタンの押下を自動的にすれば、実現できるはずです。
システム設計図:

機械調達

社内サーバーを用意します。

ebayにて購入したESP8266マイコンボードも無事に届きました。

コントローラーの設計

部品:

  • ESP8266ボード X 1
  • 基盤X1
  • トランジスター(NPN) X 2
  • 5Vリレー X 2
  • ジャンパーワイヤー 若干

回路図:

半田付け後実物:

ESP8266ボードの開発:
usbシリアル変換ドライバのインストール、ARDUINO IDEのインストール、ESP8266ボードの追加など略にします。(GOOGLEで検索すれば、出るはずです。)
ソースコード:

  
  #include 
#include 

const char* ssid = "xxxxxxxxxxx";
const char* password = "xxxxxxxxx";
WiFiUDP Udp;
unsigned int localUdpPort = 4242;  // local port to listen on
char incomingPacket[255];  // buffer for incoming packets
char  replyPacket[] = "Hi there! Got the message :-)";  // a reply string to send back

void setup() {
  Serial.begin(115200);
  delay(10);

  // prepare GPIO2
  pinMode(2, OUTPUT);
  digitalWrite(2, 0);
  
  // Connect to WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  
  WiFi.mode(WIFI_AP_STA);
  WiFi.softAP(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN,HIGH);
  // not working DC Current per I/O Pin: 40 mA
  digitalWrite(LED_BUILTIN,LOW);
  
  // Print the IP address
  Serial.println(WiFi.localIP());
  
  Udp.begin(localUdpPort);
}

void loop() {

  int packetSize = Udp.parsePacket();
  if (packetSize)
  {
    // receive incoming UDP packets
    Serial.printf("Received %d bytes from %s, port %d\n", packetSize, Udp.remoteIP().toString().c_str(), Udp.remotePort());
    int len = Udp.read(incomingPacket, 255);
    if (len > 0)
    {
      incomingPacket[len] = 0;
    }
    Serial.printf("UDP packet contents: %s\n", incomingPacket);

    if (String(incomingPacket) == "open the door")
    {
      Serial.printf("begin");
      // Set GPIO2 according to the request
      digitalWrite(2, 1);
      Serial.printf("sleep 1 s");
      delay(1000);
      digitalWrite(2, 0);
      Serial.printf("end");
    }

    // send back a reply, to the IP address and port we got the packet from
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
    Udp.write(replyPacket);
    Udp.endPacket();
  }
}

  
  
  

スマホ画面の設計

ボタン一個だけでとてもシンプルなデザインです。(シンプルイズザベスト)

AJAX HTML:

  
  

  <html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.2/css/bootstrap.min.css" integrity="sha384-Smlep5jCw/wG7hdkwQ/Z5nLIefveQRIY9nfy6xoR1uRYBtpZgI6339F5dgvm/e9B" crossorigin="anonymous">
	<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.1/css/all.css" integrity="sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ" crossorigin="anonymous">

    <title>UENO</title>
<style>
@charset "UTF-8";
html,
body {
  height: 100%;
}

body {
  display: -ms-flexbox;
  display: flex;
  -ms-flex-align: center;
  align-items: center;
  vertical-align: middle;
}

div {
  width: 100%;
  margin: auto;
}

.btn{
    display: inline-block;
    text-decoration: none;
    color: rgba(255, 0, 0, 0.8);/*アイコン色*/
    width: 80px;
    height: 80px;
    line-height: 80px;
    font-size: 40px;
    border-radius: 50%;
    text-align: center;
    vertical-align: middle;
    overflow: hidden;
    font-weight: bold;
    background-image: -webkit-linear-gradient(#e8e8e8 0%, #d6d6d6 100%);
    background-image: linear-gradient(#e8e8e8 0%, #d6d6d6 100%);
    text-shadow: 1px 1px 1px rgba(255, 255, 255, 0.66);
    box-shadow: inset 0 2px 0 rgba(255,255,255,0.5), 0 2px 2px rgba(0, 0, 0, 0.19);
    border-bottom: solid 0px #b5b5b5;
}
.btn .fa {
    line-height: 80px;
}
.btn:active {/*押したとき*/
    background-image: -webkit-linear-gradient(#efefef 0%, #d6d6d6 100%);
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.5), 0 2px 2px rgba(0, 0, 0, 0.19);
    border-bottom: none;
}

</style>
  </head>
  <body class="text-center">
    <div>
      <a href="#" class="btn"><i class="fa fa-power-off"></i></a>
	</div>
    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.2/js/bootstrap.min.js" integrity="sha384-o+RDsa0aLu++PJvFqy8fFScvbHFLtbvScb8AjopnFD+iEQ7wo/CG0xlczd+2O/em" crossorigin="anonymous"></script>
  </body>
<script type="text/javascript">
<!--
$(function(){
	$( ".btn" ).click(function() {
		$.post("open",
		    { t: new Date().getTime() },
		    function(data, status){ console.log("Data: " + data + "\nStatus: " + status); }
		    );
	});
});
//-->
</script>
</html>
  
  
  

バックエンド設計

java servletのソースコード:

  
  
  package web;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import java.util.logging.FileHandler;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class OpenTheDoor
 */
public class OpenTheDoor extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private Logger logger = Logger.getLogger(OpenTheDoor.class.getName());  
    
    /**
     * Default constructor. 
     */
    public OpenTheDoor() {
        
        try {
            logger.setUseParentHandlers(false);
            FileHandler fh = new FileHandler("/data/log/ueno.log");
            logger.addHandler(fh);
            SimpleFormatter formatter = new SimpleFormatter();  
            fh.setFormatter(formatter);
            logger.info("The server are started.");
        } catch (Exception e) {}
        
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.getRequestDispatcher("WEB-INF/index.jsp").forward(request, response);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        service();
        logger.info("Some one opened the door.");  
    }
    
    private void service() {
        short port = 4242;
        DatagramSocket socket = null;
        try {
            byte[] buf = "open the door".getBytes();
            socket = new DatagramSocket();
            socket.setBroadcast(true);
            // Maybe the Router does not work.
            // Change to local broadcasting ip ex. 192.168.1.255
            //InetAddress address = InetAddress.getByName("255.255.255.255");
            //InetAddress address = InetAddress.getByName("192.168.1.255");
            //DatagramPacket packet = new DatagramPacket(buf, buf.length, address, port);
            //socket.send(packet);

            List broadcastAddress = this.listAllBroadcastAddresses();
            for (InetAddress inetAddress : broadcastAddress) {
                socket.send(new DatagramPacket(buf, buf.length, inetAddress, port));
            }
        } catch (Exception e) {
        } finally {
            if (socket != null)
                socket.close();
        }
    }

    List listAllBroadcastAddresses() throws SocketException {
        List broadcastList = new ArrayList();
        Enumeration interfaces = NetworkInterface.getNetworkInterfaces();
        while (interfaces.hasMoreElements()) {
            NetworkInterface networkInterface = interfaces.nextElement();
            if (networkInterface.isLoopback() || !networkInterface.isUp()) {
                continue;
            }
            networkInterface.getInterfaceAddresses().stream().map(a -> a.getBroadcast()).filter(Objects::nonNull)
                    .forEach(broadcastList::add);
        }
        return broadcastList;
    }
}

  
  

オートロックの分解、分析

既存のオートロック分解してハイキングします。

取り付け

コントローラー取り付けした様子です。

完成

検証してみると上手くできました。

Comments

Popular posts from this blog

Python3 + Django + uWSGI + Nginx On FreeBSD

arduino最小構成 (atmega328/8MHz/3.3V/内部クロック)FT232RLにてブートローダーの書き込み

FreeBSD: Configuring Apache to permit CGI