Making things open with a Raspberry PI and AWS Lambda

One part of gaining out-of-hours access to my office is via a remote-controlled gate. I wanted everyone to be able to open the gate without the need to purchase a £10 fob and remember to have it with them. I embarked on making a quick and dirty web/phone accessible form that would initiate the opening of the gate provided someone submitted the correct password.

Building the circuit

The first step towards this was to buy a single fob and get it coded to open the gate. Next I needed to operate the fob via another circuit i.e. I needed to use a relay. Now I had to start googling for a circuit design that my tiny mind could understand and I eventually found this: https://openhomeautomation.net/control-a-relay-from-anywhere-using-the-raspberry-pi and started looking into ordering the bits. After a couple of weeks only a few of the bits had arrived from Amazon and I was bored with waiting. After contacting my brother who works in a school, I ended up making the following substitutions:

  • 5v SPST > 6v SPDT relay
  • 2N2222 > BC548 transistor

Apart from that everything remained the same as the link and I built the circuit.

Instead of the LED I opened the pre-programmed fob and found the push button. I proceeded to connect one side of the button to the COM pin of the relay and the other side to the NO (normally open) pin. I then dropped into Raspbian and activated the PIN in Python and made sure the fob’s light came on. You’ll easily be able to find how to do this by googling or refer to my Java client later on and avoid Python altogether.

Building the “server”

I would be able to give the RPI internet access from the office but it would only be able to reach out and poll the server periodically rather than have anything pushed to it. Aside from hosting a little HTML form somewhere, I simply needed 2 endpoints, one telling the server that someone wants to open the gate and one for the RPI to check if it needs to open the gate. In addition I needed a way (in-memory/file/database) to hold the state or the switch.

Using AWS Lambda combined with API Gateway is a quick way to publish a bit of code without provisioning any compute resources and have it easily accessible via a URL. DynamoDB with a single table would suffice to store state. I won’t discuss the database any further than saying it had a table called “fob” with a column called “open” which, when someone wanted to open the gate, would be given a value called “requested”. You might see these name as constants in the code later. If you create something like this, make sure that you give your Lambdas a role that provide access to the resource.

I used Java 8 to create the Lambdas, the first one shown below expects a single password param posted, checks it and when correct, adds the “requested” value to the “open” column of the “fob” table.

public class RequestOpenLambdaFunctionHandler implements RequestHandler {

	final AmazonDynamoDB ddb = AmazonDynamoDBClientBuilder.defaultClient();
	
	public static final String TABLE_NAME = "fob";
	public static final String COLUMN_NAME = "open";
	public static final String PRESSED_NAME = "requested";
	
	public String handleRequest(Request input, Context context) {
		String body = input.bodyjson;
		if(body.split("=")[1].equals("password")) {
			HashMap itemValues = new HashMap();
			itemValues.put(RequestOpenLambdaFunctionHandler.COLUMN_NAME, new AttributeValue(RequestOpenLambdaFunctionHandler.PRESSED_NAME));
			ddb.putItem(RequestOpenLambdaFunctionHandler.TABLE_NAME, itemValues);
		}
		return "";
    }
}

The second Lambda is used to check if a request has been made for the gate to open, it returns the string of “open” or “closed” depending on the state. We’ll need in API Gateway.

public class CheckOpenLambdaFunctionHandler implements RequestHandler {

	final AmazonDynamoDB ddb = AmazonDynamoDBClientBuilder.defaultClient();

	@Override
	public String handleRequest(Object input, Context context) {
		HashMap itemValues = new HashMap();
		itemValues.put(RequestOpenLambdaFunctionHandler.COLUMN_NAME, new AttributeValue(RequestOpenLambdaFunctionHandler.PRESSED_NAME));
		
			if(ddb.getItem(RequestOpenLambdaFunctionHandler.TABLE_NAME, itemValues).getItem()!=null) {
				ddb.deleteItem(RequestOpenLambdaFunctionHandler.TABLE_NAME, itemValues);
				throw new RuntimeException("opening");
			}
			
		
		return "closed";
	}
}

In API Gateway you’ll need two resources; “check” with a GET method and “open” with a POST method. In the POST method you’ll need a mapping of type “application/x-www-form-urlencoded” in the Integration Request section. You can then select the provided default “Method Request passthrough” template. You’ll notice from the Lambda, I decant the the request into a POJO (I won’t bother including here) of type “Request” that has a public parameter called “bodyjson” (can’t use a hyphen for a variable name). I therefore removed the hyphen in body-json from default template.  As we know there will only be one param, we can get at it quickly and easily as per the first Lambda example.

The quickest way and with the least overhead I could think of  to signal to the RPI client when the gate should open was to toggle the response code. 200 (OK) would signify opening the gate and 418 (I am a teapot) would signify that the gate should remain closed. In order to achieve this, just as we mapped the request, we’re going to need to interrogate the response and change the status code accordingly. This is done through the Integration Response section on the outbound path of API Gateway. We need two rules: 200 based on regex’ing the string of opening coming from the Lambda Error Regex and 418 for everything else (default mapping set to yes). Yes, it’s horrible but you’ll notice I’m throwing a runtime exception from the code when the gate should open as I’m trying to make the code as small as possible.

Once you publish your API and get its address you can head back to the RPI and write your client.  Pick your lanuage of choice (Java again for me although I did one in Python as well), create a loop, pick a polling interval and work out what pin you need to activate to bring the fob circuit to life. A simple Java example is shown below.

 

public class FobServer {
	public static void main(String[] args) {
		final GpioController gpio = GpioFactory.getInstance();
		final GpioPinDigitalOutput pin = gpio.provisionDigitalOutputPin(RaspiPin.getPinByAddress(4), "relay", PinState.LOW);
		pin.setShutdownOptions(true, PinState.LOW);
		String address = "https://yoururl/check";
		while (true) {
			try {
				Thread.sleep(8000);
			} catch (InterruptedException e) {};

			try {
				URL url = new URL(address);
				HttpsURLConnection con = (HttpsURLConnection) url.openConnection();

				if(con.getResponseCode() == 418) {
					continue;
				}
				//Open!
				System.out.println("Opening");
				pin.pulse(2000, true);

			} catch (MalformedURLException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

No we can all use our phones to open the gate 🙂

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Advertisements
%d bloggers like this: