phpoc_man
Published © GPL3+

Arduino - Remotely Open Door via Web with Pattern Password

Remotely control door via web with pattern password protected. Your door is securely controlled from anywhere through Internet.

BeginnerFull instructions provided1 hour7,557
Arduino - Remotely Open Door via Web with Pattern Password

Things used in this project

Story

Read more

Schematics

arduino-schematic_brCbNXdvLA.jpg

Code

ArduinoDoor.ino

Arduino
This is Arduino code
/* arduino web server - pattern unlock */

#include <Phpoc.h>
#include <PhpocExpansion.h>

#define CMD_AUTH	0
#define CMD_CTRL	1
#define ACCESS_ACCEPTED		"0\r\n"
#define ACCESS_UNAUTHORIZED	"1\r\n"
#define DOOR_STATE_OPEN		"2\r\n"
#define DOOR_STATE_CLOSE	"3\r\n"
#define DOOR_TIMEOUT_MS		10000

PhpocServer server(80);
ExpansionRelayOutput relay(1, 0);
String pattern;
bool authenticated;

unsigned long lastActiveTime;

void setup() {
	Serial.begin(9600);
	while(!Serial)
		;

	Phpoc.begin(PF_LOG_SPI | PF_LOG_NET);
	//Phpoc.begin();

	server.beginWebSocket("web_pattern");

	Serial.print("WebSocket server address: ");
	Serial.println(Phpoc.localIP());

	// initialize expansion board
	Expansion.begin();

	pattern = String("1,4,8,6,3");
	authenticated = false;
	lastActiveTime = 0;
}

void loop() {
	// wait for a new client:
	PhpocClient client = server.available();

	if (client) {
		String data = client.readLine();

		if(data) {
			int pos = data.indexOf(':');
			int cmd = data.substring(0, pos).toInt();

			Serial.print("<<");
			Serial.print(data);
			if(cmd == CMD_AUTH) {
				String reqPattern = data.substring(pos+1);

				reqPattern.remove(reqPattern.indexOf(13));
				reqPattern.remove(reqPattern.indexOf(10));

				if(pattern.equals(reqPattern)) {
					authenticated = true;
					sendResponse(ACCESS_ACCEPTED, 3);
					lastActiveTime = millis();
				}
				else {
					authenticated = false;
					sendResponse(ACCESS_UNAUTHORIZED, 3);
				}
			}
			else
			if(cmd == CMD_CTRL) {
				if(authenticated) {
					int control = data.substring(pos+1).toInt();
	
					if(!control){
						relay.off();
						sendResponse(DOOR_STATE_CLOSE, 3);
					} else {
						relay.on();
						sendResponse(DOOR_STATE_OPEN, 3);
					}

					lastActiveTime = millis();
				}
				else {
					sendResponse(ACCESS_UNAUTHORIZED, 3);
				}
			}
		}
	}

	if (authenticated && ((millis() - lastActiveTime) > DOOR_TIMEOUT_MS)){
		authenticated = false;
		sendResponse(ACCESS_UNAUTHORIZED, 3);
		relay.off();
		delay(500);
		sendResponse(DOOR_STATE_CLOSE, 3);
	}
}

void sendResponse(char *data, int len) {
	server.write(data, len); 
	Serial.print(">>");
	Serial.print(data);
}

door.php

PHP
This is Web User Interface. It need to be uploaded to PHPoC Shield via PHPoC Debugger
<!DOCTYPE html>
<html>
<head>
<title>Arduino - PHPoC Shield</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 Arduino!");
	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>

Credits

phpoc_man

phpoc_man

62 projects • 407 followers

Comments