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.

  • 5,121 views
  • 0 comments
  • 22 respects

Components and supplies

About this project

If you are a beginner, you can learn about wifi and electromagnetic lock in Arduino - Wifi and Arduino - EM Lock tutorials.

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

The Best Arduino Starter Kit for Beginner

If you are looking for an Arduino kit, see The Best Arduino Kit for Beginners


Function References

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

  • 14,306 views
  • 2 comments
  • 77 respects

Arduino - Control Light Bulb via Web

Project tutorial by Homer

  • 13,305 views
  • 15 comments
  • 68 respects

Arduino - Monitoring Door Open via Facebook Messenger

Project tutorial by phpoc_man

  • 13,245 views
  • 6 comments
  • 45 respects

Arduino - Send Temperature to Web via Serial

Project tutorial by IoT_hobbyist

  • 7,891 views
  • 7 comments
  • 32 respects

Arduino - Control Arm Robot via Web

Project tutorial by phpoc_man

  • 19,981 views
  • 25 comments
  • 171 respects

Arduino - Control Step Motor Precisely via Web

Project tutorial by IoT_lover

  • 13,519 views
  • 3 comments
  • 76 respects
Add projectSign up / Login