Project tutorial
Arduino - Remotely Open Door via Web with Pattern Password

Arduino - Remotely Open Door via Web with Pattern Password © GPL3+

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

  • 1,993 views
  • 0 comments
  • 15 respects

Components and supplies

About this project

Demonstration

How It Works

1. Authentication via pattern password

see https://www.hackster.io/phpoc_man/arduino-web-pattern-unlock-7e1f07

2. Open the door

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

Wiring

  • Stack PHPoC WiFi shield on Arduino
  • Stack Relay shield on PHPoC Shield
  • Connect EM lock to Relay Shield
  • In relay shield, move jumper to set relay works as NC (Normal Closed) mode

How To

  • Compile Arduino code and upload to Arduino Uno
  • Upload door.php to PHPoC WiFi Shield via this instruction
  • Access web page of door: http://192.168.0.234/door.php (you need to replace your PHPoC Shield's IP address)
  • Draw the pattern to get the access permission
  • Rotate the door handle to open the door

Web User Interface

Code

ArduinoDoor.inoArduino
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.phpPHP
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>

Schematics

arduino-schematic_brCbNXdvLA.jpg
Arduino schematic brcbnxdvla

Comments

Similar projects you might like

Arduino - Web Pattern Unlock

Project tutorial by phpoc_man

  • 12,527 views
  • 2 comments
  • 68 respects

Arduino - Monitoring Door Open via Facebook Messenger

Project tutorial by phpoc_man

  • 9,403 views
  • 1 comment
  • 37 respects

Smart BT Door Lock (Simple Arduino)

Project showcase by Arun

  • 11,299 views
  • 1 comment
  • 22 respects

Arduino Dynamic Web Control

Project tutorial by phpoc_man

  • 39,845 views
  • 18 comments
  • 143 respects

Arduino - Control Arm Robot via Web

Project tutorial by phpoc_man

  • 13,992 views
  • 23 comments
  • 147 respects

Arduino and Android Based Password Protected Door Lock

Project tutorial by Md. Khairul Alam

  • 13,675 views
  • 6 comments
  • 50 respects
Add projectSign up / Login