DIY WiFi Garage door opener and status checker

I borrowed my brother’s iPhone in order to capture the video.

Summary

My dad and I made a wifi garage door opener and status checker.

We did this project crazy-fast and without any hang-ups, which was interesting to me because it showed how far along the tools have come.

Hardware

  • Original Raspberry Pi
  • 1x Opto-isolator
  • 1x Reed switch

Embedded Software:

  • OS: Nerves (Linux boot to Erlang)
  • App: Custom Elixir app source

Mobile Software:

  • App: Custom Ionic app source

Problem

Our garage door opener has a physical panel just outside where one can enter a 4 digit pin and open the garage.

After some fairly recent rain storms, the panel has been malfunctioning and fails to work 95% of the time. It’s not a battery issue.

It was expensive to replace that from the manufacturer, and we didn’t want the same faulty product!

We want to know if the garage door is open or closed and be able to open or close it remotely.

Solution

This would take two GPIO pins.

  1. read logic level 1 or 0 based on the garage door being open or closed. a reed switch affixed to the door’s frame, adjecent to a strong magnet on the door, will do the trick.

  2. write logic level 1 to a circuit that simulates a button press on the garage door manual switch, located inside the garage. We used an opto-isolator for this.

Build-out

Embedded

I used Nerves on an old Raspberry Pi.

Nerves makes it possible to create minimal ARM firmware that boots directly into the Erlang virtual machine (BEAM).

Essentially it helps you compile a barebones linux kernel with the init system replaced.

I chose Nerves because I like Elixir and the emphasis placed on making fault-tolerant systems right from the start. I think it’s quite apt for the embedded space.

After making Nerves development easier on Mac, I started the project.

Controlling GPIO pins was easy to do thanks to Elixir ALE.

The core concept is to listen on HTTP and allow read/write on any GPIO pin:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
defmodule Codelock.Router do
use Plug.Router
plug Plug.Logger
plug Corsica, origins: "*"
plug :match
plug Plug.Parsers, parsers: [:urlencoded, :json], json_decoder: Poison
plug :dispatch
def start_link do
{:ok, _} = Plug.Adapters.Cowboy.http Codelock.Router, []
end
post "/digital_write/:gpio_out/:value" do
gpio_out |> String.to_integer |> digital_write(String.to_integer(value))
send_resp(conn, 200, "{}")
end
post "/digital_read/:gpio_in" do
value = gpio_in |> String.to_integer |> digital_read
send_resp(conn, 200, Poison.encode!(%{ value: value }))
end
match _ do
send_resp(conn, 404, "Not found")
end
defp digital_write(pin, value) do
{:ok, pid} = Gpio.start_link(pin, :output)
Gpio.write(pid, value)
IO.puts "Wrote #{value} to pin #{pin}"
Process.exit(pid, :normal)
value
end
defp digital_read(pin) do
{:ok, pid} = Gpio.start_link(pin, :input)
value = Gpio.read(pid)
IO.puts "Read #{value} from pin #{pin}"
Process.exit(pid, :normal)
value
end
end

Circuit

Credit goes 100% to my dad for all the circuit design and prep downstream of the GPIO pins. If you’d like to replicate this look at the reference schematics for an appropriate voltage reed switch and opto-isolator.

A Normally Open (NO) Reed Switch and a magnet are used as the Garage Door status sensor. The Reed switch is installed on the frame, and the magnet on moving door. When the door is in the closed position, the switch short the PI’s input to ground indicating logic “0,” otherwise that input is in logic “1.” We chose a passive Reed magnetic sensor over hall effect transistor because the latter would have required access to a supply voltage and therefore we would have to run 3 wires from PI to the sensor.

All I had to do was put the board together with the Raspberry Pi:

naked boards pi closeup

Mobile App

Prior experience indicated that Ionic would make the mobile app development portion trivial.

The core concept for the mobile UI here was to list one or more “things” that have state and can be toggled:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<ion-view view-title="Dashboard">
<ion-content class="padding">
<div class="list card" ng-repeat="thing in things">
<div class="item item-divider" ng-init="thing.init()">{{ thing.label }}</div>
<div class="item item-body">
<div>
State: {{ thing.state }}
</div>
<button class="button button-full button-positive" ng-click="thing.toggle()">
Toggle
</button>
</div>
</div>
</ion-content>
</ion-view>

The core concept for the controller was to provide the list of “things” that know how to fetch state and be toggled:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$scope.things = [{
label: "Garage Door",
state: "unknown",
init: function() {
var self = this;
var fetchState = function() {
$http.post('http://garage:4000/digital_read/4', {}, httpConfig)
.success(function(res) {
if (res.value === 1) self.state = "Open";
else if (res.value === 0) self.state = "Closed";
})
fetchState();
setInterval(fetchState, 5000);
},
toggle: function() {
$http.post('http://garage:4000/digital_write/18/1', {}, httpConfig).success(function() {
setTimeout(function() {
$http.post('http://garage:4000/digital_write/18/0', {}, httpConfig)
}, 800)
})
}
}];

This code could definitely use improvement (and reveals other problems), but this resulted in a decent app:

ionic app

Installation

After soldering the wires from our opto-isolator into the manual switch, we’ve got this:

manual switch

The last step was hooking up the reed switch to the door. You can see the reed switch in the left of the pic below:

reed switch

You can see the whole thing in action in the video at the top of this post.

Setup go compiler on arm and compile buildkite agent

Notes getting buildkite agent running on BeagleBone/ARM7

Beaglebone Black running Debian

Linux arm-worker 3.8.13 #1 SMP Mon Sep 22 10:22:05 CST 2014 armv7l GNU/Linux

After some failed attempts with default sources and unmaintained PPA’s, I found Dave Cheney’s website where he distributes ARM tarballs of Go: http://dave.cheney.net/unofficial-arm-tarballs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Get mercurial, need it later for some packages
apt-get update
apt-get install mercurial
# Get Go
wget http://dave.cheney.net/paste/go1.4.2.linux-arm~multiarch-armv7-1.tar.gz
sha1sum go1.4.2.linux-arm~multiarch-armv7-1.tar.gz
# should be 607573c55dc89d135c3c9c84bba6ba6095a37a1e
tar -zxvf go1.4.2.linux-arm~multiarch-armv7-1.tar.gz
# Setup your Go installation
export GOROOT=$HOME/go
export PATH=$PATH:$GOROOT/bin
# Setup your GOPATH
export GOPATH="$HOME/Code/go"
export PATH="$HOME/Code/go/bin:$PATH"
# Get godep
go get github.com/tools/godep
# Checkout the code
mkdir -p $GOPATH/src/github.com/buildkite/agent
git clone git@github.com:buildkite/agent.git $GOPATH/src/github.com/buildkite/agent
cd $GOPATH/src/github.com/buildkite/agent
godep get
# Test it
go run *.go start --debug --token "abc123" --bootstrap-script templates/bootstrap.sh --build-path ~

Find listening process on mac os x

Let’s say you have something listening on port 4000, when you hit that port nothing happens, and you can’t start any services on that port because something is using it.

On Mac you can find out what process is using it by executing lsof -i :4000

This command will show you the program, pid, and many other pieces of information you can use to track down and kill the process.

Raspberry pi iphone tether

iPhone Tethering on Raspberry Pi

The instructions here are useful although the current packages in Arch and Debian repositories do not work with iOS 7 (Trust Loop Bug) but it is still a good starting point to understand how this works.

https://wiki.archlinux.org/index.php/IPhone_Tethering

iOS 7 Support

Install libimobiledevice from latest source

In order to get iOS 7 support, we need to compile everything from source. Find the scripts for ArchLinux and Raspbian here: https://gist.github.com/kfatehi/8922430

Usage

Mounting your iPhone

Start usbmuxd: usbmuxd

Create a mount point: mkdir /media/iphone

Mount the device: ifuse /media/iphone

(You can unmount using umount /media/iphone)

You should now be able to view the contents of your iPhone.

Networking

At this point you should reboot so that modules and rules get loaded. After that, I gave up on ArchLinux due to issues getting actual network traffic to go across, so I can’t speak for ArchLinux from herein. However I did have success on Raspbian. You should be able to simply plug in your iPhone and see a new interface come up and be able to ping the outside world. Enjoy!

Npm mirror

UPDATE Feb 2, 2014

Don’t bother with any of this. Use sinopia instead! I’ve prepared a docker image for it as well.


This tutorial is my version of this other tutorial.

First off make sure you have a large enough disk drive. You can find out the remote disk size easy like this:

1
2
[root@alarmpi ~]# curl http://isaacs.iriscouch.com/registry/
{"db_name":"registry","doc_count":52298,"doc_del_count":4836,"update_seq":861940,"purge_seq":0,"compact_running":false,"disk_size":214735753351,"data_size":174529391409,"instance_start_time":"1387441403828175","disk_format_version":6,"committed_update_seq":861940}

data_size lets us know that at the time of this writing you’ll be safe
with a 250GB SSD, but it’s anyone’s guess when that will be
insufficient. Currently that’s what I’m running.

Install & Configure CouchDB

On Mac OS X

  1. Use Homebrew to install couchdb: brew install couchdb read the
    caveats to make it autostart on reboots
  2. Point your browser to the CouchDB configuration page which should now be available at http://127.0.0.1:5984/_utils/index.html

On a Raspberry Pi w/ Arch Linux

  1. Grab the Arch image from http://www.raspberrypi.org/downloads
  2. Determine the device path for your SD card with df at the terminal.
  3. Unzip and then dd the image to path you found: sudo dd bs=4m if=/path/to/img of=/dev/mysd make sure you point to the card and not
    the partition (e.g. /dev/disk1, not /dev/disk1s1)
  4. Insert the SD card, start the Pi, and ssh into it (root/root)
  5. Format your external drive, ensure it is mountable,
    and that your /etc/fstab is set to
    automount it. Use lsblk -f to discover it. If you need more help check
    this ArchWiki Page
  6. Download some packages: pacman -Sy couchdb for more info see this ArchWiki Page
  7. Edit /etc/couchdb/local.ini and set ;bind_address = 127.0.0.1 to bind_address = 0.0.0.0 if you want to access it from another system
  8. Edit /etc/couchdb/local.ini and under [couchdb] add these 2 lines, per your storage location:
    database_dir = /media/storage/couchdb and view_index_dir = /media/storage/couchdb
  9. Give the couchdb daemon permission to write to your external storage:
    chown couchdb:daemon /media/storage
  10. Setup couchdb to autostart after reboot systemctl enable couchdb
  11. Start couchdb with systemctl start couchdb
  12. Connect to Futon using your-ip:5984/_utils

CouchDB configuration continued

Now you have CouchDB installed and can access Futon and hopefully the
internet.

From Futon click “Configuration” and find secure_rewrites and set it to false

Tell CouchDB to replicate continuously from NPM

Open terminal and enter the following to setup continuous replication
from the official npm registry:

1
curl -X POST http://127.0.0.1:5984/_replicate -d '{"source":"http://isaacs.iriscouch.com/registry/", "target":"registry", "continuous":true, "create_target":true}' -H "Content-Type: application/json"

Tell CouchDB to stop replicating

In case you ever need it:

1
curl -X POST http://127.0.0.1:5984/_replicate -d '{"source":"http://isaacs.iriscouch.com/registry/", "target":"registry", "continuous":true, "create_target":true, "cancel":true}' -H "Content-Type: application/json"

Making sure that it keeps replicating

There’s an unfortunate issue that I’m experiencing with couchdb, but it may
just stop replicating often only after transferring 5-7 GB of
data – a trivial retrigger of the replication with the above command
would cause it to pick up where it left off, so I’ve developed a script.

  1. Install nodejs
  2. npm install npm-replication-watcher
  3. npm install forever
  4. forever npm-replication-watcher/bin/npm-replication-watcher

For more information check out
npm-replication-watcher

Finalizing the installation

This information can also be found here https://github.com/isaacs/npmjs.org. That link will also explain how to tell npm to use your new registry.

  1. git clone git://github.com/isaacs/npmjs.org.git
  2. cd npmjs.org
  3. sudo npm install -g couchapp
  4. npm install couchapp
  5. npm install semver
  6. couchapp push registry/app.js http://localhost:5984/registry
  7. couchapp push www/app.js http://localhost:5984/registry

Testing the installation

  1. npm --registry http://localhost:5984/registry/_design/scratch/_rewrite login
  2. npm --registry http://localhost:5984/registry/_design/scratch/_rewrite search

Cucumber and html5 and microsoft

At work we have built a product that is comprised of 3 parts: a backend
written in Ruby on Rails, a web interface that is served by rails and
uses the rails controllers as usual, and an iOS app that interacts with
the Grape API mounted to the Rails app.

For the purpose of this article, a “feature file” describes a feature,
written in Gherkin, the start point for behavior driven development
using cucumber.

The rails application, or rather the interactions from the served HTML,
have feature files. This makes me feel good because if I would
transition to an HTML5 app, separated from the rails controllers, that
uses the Grape API just as the iOS app does, the feature file can be
taken and placed into a new project (say, one generated with Brunch),
and cucumber.js can be used to behavior-drive the remaning functionality
with javascript or coffeescript.

What does this have to do with microsoft? Well it seems like folks are
doubtful that microsoft will be able to contend on the mobile
marketplace against the Apple and Google platforms. The most common
argument I have heard is that “windows phone doesn’t have apps” but if
you think about it, neither did iOS or Android at one time.

Building iOS and Android apps is harder than building an
HTML5/Javascript app for many reasons. Developers are impeded on two
major, fundamental fronts:

  • Learning curve (brand new language, low level)
  • Platform/SDK/Tools (must be on Mac for iOS, must learn Xcode, must
    learn android tools)

As technologies like Apache Cordova improve alongside HTML5/CSS3, and
the javascript ecosystem becomes richer, prototyping on the web is the
obvious choice.

So back to the project at my work…

The iOS application had no feature files at all – it was terrifying for
a test-driven developer like me to have to maintain that and manage its
developers… they were like cowboys and you never knew if they would
ship with regression. Thanks to our iOS tester we catch most bugs quickly,
but the risks are unnecessary.

Luckily we found Calabash which is based on cucumber and uses the same
format for feature files. Some overlap with the web application and work
differently, and thus have different steps, but the same feature.
However the point is that now these feature files can be extracted and
placed into a new application.

This new application can be purely HTML5/CSS/Javascript and ship to
Windows, Mac, iOS, Android, the platform is not relevant as long as the
app is written in a responsive manner and has the appropriate bindings
to the OS, provided by PhoneGap, Microsoft, Qt, or whatever hackery
you’re doing to deploy your HTML5 app to the target platform.

In the future this hackery will reduce, and hardware will be exposed
directly (e.g. getUserMedia()) in a device-compatible way, and Microsoft
is just as relevant as any other. Microsoft will have all the same apps
when it costs very little to get onto the platform in the first place
– and for us, it does not cost much to get on Windows and Windows
Phone. I imagine other development shops see this too and will also
target the Microsoft platform offerings.

Conflict of interest

taken from source:

When is a startup a conflict of interest with existing job?

At least in Switzerland, you have to be very cautious about what of the things you create during your employment belongs to your employer. If you do not especially ask for the contract to include only results of your work for the company, all your private inventions and achievements not only in your field of employment can be claimed by the employer. It happened to a friend of mine that even the rights on a published book of poems were taken by his employer, another one lost the rights to a new bicycle break he invented. Both were programmers.

Some employers insist on 100% of your work time, including private work. They then claim that all achievements have been made during their time.

Rare cases I agree. And maybe its different where you live. Just read your contract very carefully.

“things you create during your employment belongs to your employer” - is this the default (i.e. with no other agreements in place) rule? In the states, that’s not the default, but it can be written into contracts and it has held up in court. – Alex Papadimoulis Oct 12 ‘09 at 16:46

It’s a little more wordy,but the standard clause boils down to this in front of the court. It is one of the things that usually are missed by people - which does not matter in most cases, but when you are successful with your startup suddenly might. – malach Oct 13 ‘09 at 4:34