Remotely control door via web with pattern password protected. Your door is securely controlled from anywhere through Internet.
Click image for larger version  Name:	phpoc-cover.png Views:	0 Size:	273.5 KB ID:	1596


Hardware components
× 1
× 1
Electromagnetic Lock
× 1



Demonstration







How It Works


1. Authentication via pattern password

see https://www.hackster.io/iot_lover/we...-device-aeaf44

2. Open the door
  • The password is checked by PHPoC.
  • If password is correct, user can rotate the door handle on web.
  • Web send command to unlock the door to PHPoC.
  • PHPoC control Relay to deactivate EM lock



How To
  • Upload task0.php and index.phpto PHPoC board via this instruction
  • Access web page of door: http://192.168.0.234(you need to replace your PHPoC's IP address)
  • Draw the pattern to get the access permission
  • Rotate the door handle to open the door



Web User Interface






Schematics

Click image for larger version  Name:	phpoc- schematic.jpg Views:	0 Size:	56.3 KB ID:	1597


Code
task0.php
PHP Code:
<?php
include_once "/lib/sd_340.php";
include_once 
"/lib/sd_spc.php";
include_once 
"/lib/sn_tcp_ws.php";

define("CMD_AUTH",    0);
define("CMD_CTRL",    1);
define("ACCESS_ACCEPTED",        0);
define("ACCESS_UNAUTHORIZED",    1);
define("DOOR_STATE_OPEN",        2);
define("DOOR_STATE_CLOSE",        3);
define("DOOR_TIMEOUT_MS",        10000);

$rbuf "";
$authenticated false;
$pattern "1,4,8,6,3";
$last_active_time 0;

ws_setup(0"web_pattern""text.phpoc");

st_free_setup(0"ms");
spc_reset();
spc_sync_baud(115200);
spc_request_dev(1"set 0 output high");

while(
1)
{
    if(
ws_state(0) == TCP_CONNECTED)
    {
        
$rlen ws_read_line(0$rbuf);

        if(
$rlen)
        {
            
$array explode(":",$rbuf);
            
$cmd = (int) $array[0];
            
$data rtrim($array[1], "\r\n");

            if(
$cmd == CMD_AUTH)
            {
                if(
$pattern == $data)
                {
                    
$authenticated true;
                    
$last_active_time st_free_get_count(0);
                    
ws_write(0, (string)ACCESS_ACCEPTED "\r\n");
                }
                else
                {
                    
$authenticated false;
                    
ws_write(0, (string)ACCESS_UNAUTHORIZED "\r\n");
                }
            }
            else
            if(
$cmd == CMD_CTRL) {
                if(
$authenticated) {
                    
$control = (int)$data;

                    if(!
$control)
                    {
                        
spc_request_dev(1"set 0 output high");
                        
ws_write(0, (string)DOOR_STATE_CLOSE "\r\n");
                    }
                    else
                    {
                        
spc_request_dev(1"set 0 output low");
                        
ws_write(0, (string)DOOR_STATE_OPEN "\r\n");
                    }

                    
$last_active_time st_free_get_count(0);
                }
                else
                {
                    
ws_write(0, (string)ACCESS_UNAUTHORIZED "\r\n");
                }
            }
        }

        if (
$authenticated && ((st_free_get_count(0) - $last_active_time) > DOOR_TIMEOUT_MS))
        {
            
$authenticated false;
            
spc_request_dev(1"set 0 output high");
            
ws_write(0, (string)ACCESS_UNAUTHORIZED "\r\n");
            
usleep(500000);
            
ws_write(0, (string)DOOR_STATE_CLOSE "\r\n");
        }
    }
    else
    {
        
$authenticated false;
    }
}




index.php
PHP Code:
<!DOCTYPE html>
<html>
<head>
<title>PHPoC</title>
<meta name="viewport" content="width=device-width, initial-scale=0.7, maximum-scale=0.7">
<meta charset="utf-8">
<style>
body { text-align: center; font-size: width/2pt; }
h1 { font-weight: bold; font-size: width/2pt; }
h2 { font-weight: bold; font-size: width/2pt; }
button {font-weight: bold; font-size: width/2pt;}
</style>
<script>

var width = window.innerWidth - 10;
var ratio = width / 800;
if(ratio > 1)
ratio = 1;

var CMD_AUTH = 0;
var CMD_CTRL = 1;
var ACCESS_ACCEPTED = 0;
var ACCESS_UNAUTHORIZED = 1;
var DOOR_STATE_OPEN = 2;
var DOOR_STATE_CLOSE = 3;
var ws;
var authorized = false;

/* lock variable */
var canvas_width = 800 * ratio;
var canvas_height = 1300 * ratio;
var lock_edge = 40 * ratio;
var pad = 50 * ratio;
var handle_width = 500 * ratio;
var handle_height = 120 * ratio;
var lock_width = (canvas_width - 2 * lock_edge - handle_width) * 2;
var lock_height = canvas_height - 2 * lock_edge;

var pattern_width = lock_width;
var pattern_height = lock_width;
var pattern_inner_radius = 14 * ratio;
var pattern_middle_radius = 22 * ratio;
var pattern_outer_radius = 34 * ratio;
var pattern_gap = lock_width / 3;

var body_width = lock_width;
var body_height = canvas_height - lock_edge * 2 - pattern_height - pad - (handle_width - lock_width / 2);
var knob_center_x = lock_edge + body_width / 2;
var knob_center_y = lock_edge + pattern_height + pad + body_height - body_width / 2;

var touch_x = 0, touch_y = 0;
var pattern_touch_state = 0;
var pattern_touch_list = new Array();
var handle_angle = 0;
var handle_last_angle = 0;
var handle_touch_state = 0;
var mouse_state = "MOUSE_UP";
var is_door_open = false;

function init()
{
var lock = document.getElementById("lock");
lock.width = canvas_width;
lock.height = canvas_height;

lock.addEventListener("touchstart", mouse_down);
lock.addEventListener("touchend", mouse_up);
lock.addEventListener("touchmove", mouse_move);
lock.addEventListener("mousedown", mouse_down);
lock.addEventListener("mouseup", mouse_up);
lock.addEventListener("mousemove", mouse_move);

var ctx = lock.getContext("2d");
ctx.lineCap="round";
ctx.lineJoin="round";

update_view();

var ws_host_addr = "<?echo _SERVER("HTTP_HOST")?>";
if((navigator.platform.indexOf("Win") != -1) && (ws_host_addr.charAt(0) == "["))
{
// network resource identifier to UNC path name conversion
ws_host_addr = ws_host_addr.replace(/[\[\]]/g, '');
ws_host_addr = ws_host_addr.replace(/:/g, "-");
ws_host_addr += ".ipv6-literal.net";
}

ws = new WebSocket("ws://" + ws_host_addr + "/web_pattern", "text.phpoc");
ws.onopen = ws_onopen;
ws.onclose = ws_onclose;
ws.onmessage = ws_onmessage;
}

function ws_onopen()
{
update_view();
}
function ws_onclose()
{
alert("CANNOT connect to PHPoC!");
ws.onopen = null;
ws.onclose = null;
ws.onmessage = null;
ws = null;
authorized = false;
update_view();
}
function ws_onmessage(e_msg)
{
e_msg = e_msg || window.event; // MessageEvent

var resp = parseInt(e_msg.data);

if(resp == ACCESS_ACCEPTED)
authorized = true;
else if(resp == ACCESS_UNAUTHORIZED)
authorized = false;
else if(resp == DOOR_STATE_OPEN)
is_door_open = true;
else if(resp == DOOR_STATE_CLOSE)
{
is_door_open = false;
handle_last_angle = 0;
handle_angle = 0;
}
else
console.log("unknown:" + resp);

update_view();
}
function update_view()
{
var pattern_area = document.getElementById('pattern_area');
var control_area = document.getElementById('control_area');

var lock = document.getElementById("lock");
var ctx = lock.getContext("2d");

ctx.clearRect(0, 0, canvas_width, canvas_height);

// draw boder
ctx.shadowBlur = 10;
ctx.shadowColor = "LightGray";
ctx.fillStyle = "#6A4439";
ctx.beginPath();
ctx.lineTo(0, 0);
ctx.lineTo(body_width + 2 * lock_edge , 0);
ctx.arc(knob_center_x, knob_center_y, body_width / 2 + lock_edge, 0, Math.PI);
ctx.closePath();
ctx.fill();

// draw pattern password background
ctx.fillStyle = "black";
ctx.fillRect(lock_edge, lock_edge, pattern_width, pattern_height);

// draw status indicator
if(authorized)
ctx.fillStyle = "Cyan";
else
ctx.fillStyle = "white";
ctx.fillRect(lock_edge, lock_edge + pattern_height, pattern_width, pad);

ctx.font = "22px Arial"
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.fillStyle = "black";

var text = "";
if(is_door_open)
text += "Openned, ";
else
text += "Closed, ";

if(authorized)
text += "Access permitted!";
else
text += "Access denied!";

ctx.fillText(text, lock_edge + pattern_width / 2, lock_edge + pattern_height + pad / 2);

// draw body background
ctx.fillStyle = "#A07B72";
ctx.beginPath();
ctx.lineTo(lock_edge, lock_edge + pattern_height + pad);
ctx.lineTo(lock_edge + body_width , lock_edge + pattern_height + pad);
ctx.arc(knob_center_x, knob_center_y, body_width / 2, 0, Math.PI);
ctx.closePath();
ctx.fill();

// draw knob
ctx.save();
ctx.translate(knob_center_x, knob_center_y);
if(authorized)
ctx.fillStyle = "Cyan";
else
ctx.fillStyle = "white";
ctx.shadowBlur = 1;
ctx.shadowColor = "black";
ctx.beginPath();
ctx.arc(0, 0, body_width / 2 * 0.8, 0, 2 * Math.PI);
ctx.fill();

ctx.fillStyle = "black";
ctx.shadowBlur = 1;
ctx.shadowColor = "black";
ctx.beginPath();
ctx.arc(0, 0, body_width / 2 * 0.6, 0, 2 * Math.PI);
ctx.fill();

// draw handle
ctx.rotate(handle_angle * Math.PI / 180);
var grd = ctx.createLinearGradient(0, 0, handle_width, 0);
grd.addColorStop(0, "#6A4439");
grd.addColorStop(0.2, "#A07B72");
grd.addColorStop(0.8, "#A07B72");
grd.addColorStop(1, "#6A4439");

ctx.fillStyle = grd;
ctx.shadowBlur = 10;
ctx.shadowColor = "black";
ctx.beginPath();
ctx.arc(0, 0, handle_height / 2, 0.5 * Math.PI, 1.5 * Math.PI);
ctx.arc(handle_width - handle_height / 2 * 0.7, 0, handle_height / 2 * 0.78, 1.5 * Math.PI, 0.5 * Math.PI);
ctx.closePath();
ctx.fill();
ctx.restore();

// draw touched point and line
ctx.save();
ctx.shadowBlur = 20;
ctx.shadowColor = "LightGray";
ctx.lineWidth = 10;
ctx.strokeStyle="white";
ctx.globalAlpha=1;
ctx.beginPath();

ctx.translate(pattern_width/2 + lock_edge, pattern_height/2 + lock_edge);

for (var i = 0; i < pattern_touch_list.length; i++)
{
var temp = pattern_touch_list[i] - 1;
var x = temp % 3 - 1;
var y = Math.floor(temp / 3) - 1;

ctx.lineTo(x*pattern_gap, y*pattern_gap);
}

if(pattern_touch_state)
ctx.lineTo(touch_x, touch_y);

ctx.stroke();

for (var i = 0; i < pattern_touch_list.length; i++)
{
var temp = pattern_touch_list[i] - 1;
var x = temp % 3 - 1;
var y = Math.floor(temp / 3) - 1;

ctx.globalAlpha=0.2;
ctx.fillStyle = "white";
ctx.beginPath();
ctx.arc(x*pattern_gap, y*pattern_gap, pattern_outer_radius, 0, 2 * Math.PI);
ctx.fill();
}

// draw base
for(var y = -1; y <= 1; y++)
{
for(var x = -1; x <= 1; x++)
{
ctx.globalAlpha=0.5;
ctx.fillStyle = "white";
ctx.beginPath();
ctx.arc(x*pattern_gap, y*pattern_gap, pattern_middle_radius, 0, 2 * Math.PI);
ctx.fill();

ctx.globalAlpha=1;
ctx.fillStyle = "Cyan";
ctx.beginPath();
ctx.arc(x*pattern_gap, y*pattern_gap, pattern_inner_radius, 0, 2 * Math.PI);
ctx.fill();
}
}

ctx.restore();
}

function process_event(event)
{
if(event.offsetX)
{
touch_x = event.offsetX;
touch_y = event.offsetY;
}
else if(event.layerX)
{
touch_x = event.layerX;
touch_y = event.layerY;
}
else
{
touch_x = (Math.round(event.touches[0].pageX - event.touches[0].target.offsetLeft));
touch_y = (Math.round(event.touches[0].pageY - event.touches[0].target.offsetTop));
}

if(touch_x > lock_edge && touch_x < (lock_edge + pattern_width) && touch_y > lock_edge && touch_y < (lock_edge + pattern_height))
{ // pattern password area
touch_x -= (lock_edge + pattern_width / 2);
touch_y -= (lock_edge + pattern_height / 2);

for(var i = 1; i <= 9; i++)
{
if(i == pattern_touch_list[pattern_touch_list.length - 1])
continue;

var idx_x = (i-1)%3 - 1;
var idx_y = Math.floor((i-1)/3) - 1;

var knob_center_x = idx_x * pattern_gap;
var knob_center_y = idx_y * pattern_gap;

var dist = Math.sqrt( (touch_x - knob_center_x)*(touch_x - knob_center_x) + (touch_y - knob_center_y)*(touch_y - knob_center_y) );

if(dist < pattern_outer_radius)
{
pattern_touch_list.push(i);
break;
}
}

pattern_touch_state = 1;
}
else
{
pattern_touch_state = 0;

if(!authorized)
return;

var knob_center_x = lock_edge + body_width / 2;
var knob_center_y = lock_edge + pattern_height + pad + body_height - body_width / 2;
touch_x -= knob_center_x;
touch_y -= knob_center_y;
var current_angle = Math.atan2(touch_y, touch_x) * 180 / Math.PI;

/* rotate coordinate */
var radian = -handle_angle / 180 * Math.PI;
var rc_x = touch_x * Math.cos(radian) - touch_y * Math.sin(radian);
var rc_y = touch_x * Math.sin(radian) + touch_y * Math.cos(radian);

if(mouse_state == "MOUSE_DOWN")
{
if(rc_x > (handle_width * 0.3) && rc_x < handle_width && rc_y > (-handle_height / 2) && rc_y < (handle_height / 2))
{
handle_last_angle = current_angle;
handle_touch_state = 1;
}
}
else
if(mouse_state == "MOUSE_MOVE" && handle_touch_state == 1)
{
var angle = current_angle - handle_last_angle;
if((handle_angle + angle) > 0 && (handle_angle + angle) <= 45)
{
handle_angle += angle;
handle_last_angle = current_angle;
if(handle_angle < 30)
send_to_Arduino(CMD_CTRL, 1);
}
}
}

update_view();
}
function mouse_down()
{
if(ws == null)
return;

event.preventDefault();
mouse_state = "MOUSE_DOWN";
process_event(event);
}
function mouse_up()
{
if(ws == null)
return;

event.preventDefault();

if(ws != null && pattern_touch_state)
send_to_Arduino(CMD_AUTH, pattern_touch_list.toString());

pattern_touch_state = 0;
mouse_state = "MOUSE_UP";
pattern_touch_list.splice(0, pattern_touch_list.length);
update_view();
}
function mouse_move()
{
if(ws == null)
return;

event.preventDefault();
mouse_state = "MOUSE_MOVE";
process_event(event);
}

function send_to_Arduino(cmd, data)
{
if(ws.readyState == 1)
{
ws.send(cmd + ":" + data + "\r\n");
}
}

window.onload = init;
</script>
</head>

<body>
<div id="pattern_area" style="display:block;">
<canvas id="lock"></canvas>
</div>
</body>
</html>