This project involves developing an Android application, a web client and a web service to open and close an electrical garage door. The Raspberry Pi version B and a USB relay board will be used to make the connection between the client devices and the garage door motor.
For now I won't include any code, but if you are interested I can update the article to make it available.
That's not that I don't want to, but for me it's a very different thing to write code for a personal fun project and publish a polished source code example people will try to understand, use and extend.
Re-writing the code in this intent, documenting it properly (with Doxygen or Sphinx), making it work in several environments and explaining it takes a lot of time.
But if you are interested in the code of a particular component, feel free to ask!
I'll provide pictures and a video demonstration at the end of the article.
I'll provide pictures and a video demonstration at the end of the article.
Context
My garage door was installed in 1997.
It can be opened and closed with three devices :
- a push button inside,
- a keypad outside,
- a 435 MHz remote control.
The first two are fine, but the third option had many issues :
- Often times, you would press the button and nothing happened, or only on the 20th attempt.
- It used a battery such as those you would find for wrist watches, except it was not one you could easily find.
- I had only two remotes and I needed more.
I trust them but I would rather not give them the code. This solution is still better than hiding the key under the mat, but there had to be a better option.
Requirements
The features I wanted for the new remote control:
- Work on first attempt
- Fast, like pressing the physical button inside the garage.
- Be able to open the door for friends when I am away
- Secure
- Access Control
- No additional device. Everyone that will be using it has a smartphone. let's use them.
(Of course it would work over Wi-Fi only, but with a data plan they could open the door outside of the Wi-Fi range.)
Solution
There are many solutions to solve these problems, and the one I chose is totally overkill. I know that, but I had fun doing it and it has many features you wouldn't find with a simpler system.
So here is my solution.
For the clients:
- Android application
- Mini web application. Works everywhere, including PCs, iPhones, ...
For the server:
- Raspberry Pi
- RESTful Web Service over HTTPS
- HTTP Basic Authentication
- Identification using Google account and/or phone number
- Relay(s) allowing the Rasperry Pi to open and close the garage door
Overview
So, this system will use the Internet over a secure HTTP link to connect the Raspberry Pi with the clients.
The Raspberry Pi will run a web application that will verify the client is allowed to make a request, and if so will transmit a command to some middleware.
The middleware is made of several little software components that converts the command to bytes sent to the USB Relay Board over USB.
Between this "command" and asking the relay board to close the circuit on the right side, there is a software component that I call "GarageDoorController" (not shown in this schematic).
It is responsible to manage access to the relay board. As I know it takes 20 seconds for the garage door to open or close, that software component will prevent concurrent commands. Even a client might press the button on the Android app twice, and that would cause the garage door to stop at the second press, leaving it half-opened. (Although you might actually want that behavior.)
The USB Relay Board consists of several relays able to handle high voltage (230 V) and high current (up to 10 A).
One pin of the relay is used to make or break the contact between the two other pins. These two other pins are connected to pins on the garage door motor board.
The particular device motor board I have can distinguish between opening and closing the garage door, but your device might not. It also has pins to lock the door and prevent anyone closing or opening it, and pins to switch on and off the halogen light mounted on it, plus a few other things.
At the very end, the garage door motor is powered on, and the garage door opens or closes.
NOTE : I already said it, but this is MY solution. There are many others that may be more efficient or more suitable for your needs.
Depending on your skills you might want to wire the GPIO pins of the Rasperry Pi to relays on a custom-made board.
You may also be paranoïd and think SSL is not safe, or you might not have an Internet connection in your garage and use Bluetooth (or make your Raspberry Pi act as a WiFi router or use one in between) instead.
Android application
The app looks like this:
Very simple, right?
As I can distinguish opening and closing, there are two distinct buttons.
Did you see the icon at the top right?
That's some serious cool stuff!
Usually, with a remote control, you would drive to your house, stop the car, then open the door. But because this works over the Internet and the door takes 20 seconds to open, wouldn't it be nice if you could actually start opening it while you are still driving? And not even wait? Sure it would, but it would be dangerous.
So, before leaving work / the store / your friend's place, press this button and the phone will start watching your location using the GPS. Then when you are close to your house, it will automatically open the door. Neat, isn't it?
To avoid opening/closing the door by mistake and to avoid safety hazards, a confirmation dialog appears before sending the request (except in GPS mode):
And after that a progress dialog is shown, with a countdown:
Security
There are several factors that make the system secure:
- Secure HTTP link, preventing sniffing
- Server SSL certificate is installed in the application, preventing man-in-the middle attacks
- Authentication with user and password
- Identification of device using Google account and phone number
Smart watch
Could a garage door opener get more geeky than that? Yes, thanks to Android Wear! Now I can open my garage using just my watch! The interface is incredibly simple:
The App can be opened from the Wear apps menu on the watch or by saying "Open NameOfApplication".
The watch communicates with the phone via Bluetooth, and the phone connects to the Internet and send an HTTP request, as you already know.
This works using an Intent that eventually triggers an action on an Android service which I had to implement on the phone application.
I'll put a tutorial about that in a few days.
Web client
The main goal of the web application is to act as a web service for the Android app. But I figured: I will have a web server listening to requests from the phone, why not serve regular web clients as well?
So I developed this little web interface:
It behaves like the smartphone application, by showing a little countdown and disabling the buttons while the door is moving:
And it features a "History" page:
This is saved in a table in a SQLite database.
So it's easy to plot statistics from the data.
That's why there is a little statistics page:
This little diagram here shows who uses the apps the most. You could compute other statistics such as the time of the day, what kind of client is used (the web version knows about Browser / OS as well), the connection that was used (over the Internet or in the same LAN), and so on...
You can see there is a "Camera" page. I had the "Pi Camera" display a live image so that you could check the surroundings or do some computer vision and figure out the garage is not free before wasting energy and time opening and closing the door only to figure out you can't park there.
(If that's your only use of the camera, you better use a light switch or a metal detector instead.)
(If that's your only use of the camera, you better use a light switch or a metal detector instead.)
Web service / web application
Technologies
- Raspbian: Debian/Linux for the Raspberry Pi
- Python
- Flask framework for web application and REST/JSON web service
- Jinja2 templates (provided by Flask)
- SQLite3 (to keep a history)
- Tornado : a Python HTTPs-capable web server
- Supervisord : a tool to make a daemon with two lines of configuration
Middleware
All the web app does is call an "open()" or "close()" method on some object.
That object is an instance of the yellow class below. It's a garage door controller that checks if the door is moving, and if not triggers the appropriate relay and blocks for 20 seconds.
The YoctoRelayGarageDoor class triggers the relay(s).
GarageDoor and NonBlockingMonitor are general purpose classes working for any kind of garage door and any kind of resource that need to be reserved for a certain amount of time or until the resource is free again.
The following sections describe each of these classes in more details.
GarageDoorMonitor is an adapter, providing a level above the functionality of the WaitingYoctoRelayGarageDoor.
I didn't do it, but that last class could have been an adapter itself.
Garage Door Interface
The web application doesn't know how the door is actually opened. All it knows it that there is some GarageDoor that you can open() and close().
In Java you would write something like this:
In Java you would write something like this:
Preventing concurrent access (NonBlockingMonitor / GarageDoorMonitor)
Two clients shouldn't be trying to open or close the door at the same time as it will cause the door to stop and stay half-opened or cause other problems.
To solve this problem, I simply used a semaphore that locks the mechanism for 20 seconds (the time the door needs to open or close).
This code snippet shows how you can have a method check if someone is executing it right now. If there indeed is, it does nothing and returns False. Otherwise it does what you asked an returns True.
An implementation of this class for the Door interface above would be this.
In Java you would make the GarageDoorMonitor extend the NonBlockingMonitor and implement the Door interface. Does that make sense for you?
With Python's duck typing, we would simply use the GarageDoorMonitor as if it were a normal GarageDoor as they provide the same public methods.
Note this design has one flaw. You can't distinguish if the wrapped method returned False or if nothing happened. It's not a problem for me as all I want to know is "had my method calling had any effect or not", but you might want to know more.
An implementation of this class for the Door interface above would be this.
In Java you would make the GarageDoorMonitor extend the NonBlockingMonitor and implement the Door interface. Does that make sense for you?
With Python's duck typing, we would simply use the GarageDoorMonitor as if it were a normal GarageDoor as they provide the same public methods.
Note this design has one flaw. You can't distinguish if the wrapped method returned False or if nothing happened. It's not a problem for me as all I want to know is "had my method calling had any effect or not", but you might want to know more.
20 seconds (WaitingYoctoRelayGarageDoor)
Remember when I said it takes 20 seconds to open or close the door?
Now we have a monitor that prevents two people to trigger the door at the same time, but where does that 20 delay appear?
Basically, I'll make that delay appear in the open() and close() method passed as parameters to the monitor, so that the monitor is generic and the delay is tied to a particular GarageDoor implementation which is a better design that putting it in the monitor.
This way, in that implementation, when you call the open() method, it will actually take 20 seconds before the method returns.
But as I still want to keep the option to have non-blocking open and close methods, I simply wrote a "BlockingGarageDoor" class, that subclasses the GarageDoor implementation.
Look at this to understand what I am trying to say.
This object will be passed to the web application. Except the web application will create a new thread that invokes the methods, as it would be unacceptable to take 20 seconds to answer a request from a client which would happen if the 20 second long open/close methods were invoked on the thread processing the request.
Garage Door Implementation (YoctoRelayGarageDoor)
At some point we need to trigger a relay and have a concrete implementation of our GarageDoor interface.
This is the actual code I use but it will be useless if you don't happen to use the same USB relay board as I use, where the manufacturer is nice to provide a Python library to use their products (which is a big selling point if you want my opinion).
GPIO
If you use GPIO, try the RPi.GPIO Python library, whose API is extremely simple. It does however, to my understanding needs to be run with high privileges (root), which is a very bad thing. In that case I would recommend running the garage door implementation behind the NonBlockingMonitor in a separate process.
Make sure to check DoctorMonk's blog where this image comes from.
By the way, if you didn't know yet, here is a simple explanation on what happens between a Python method to switch a pin on and off, and the electrical current going through the wire (actually we will go the other way around):
1. To reference a byte of memory in RAM, you use an address, probably 64 bit long on your system.
Manufacturers use the same mechanism so that some addresses in the range defined by these 64 bits are actually mapped to devices.
Manufacturers use the same mechanism so that some addresses in the range defined by these 64 bits are actually mapped to devices.
2. You can write and read values to/from these devices the same way you would read/write from memory.
3. Let's say the byte at address 0x12345678 belongs to the GPIO device and is made of 8 bits, where each one is the state of a GPIO output. So a value of "0b01000001" (binary) (or 0x41 in hexadecimal or 65 in decimal) would enable the second and the last pin. Using electronics, these bits are connected to the actual pins, so when the bit is 1, electrical current runs through the pin.
4. To write a "driver" to control the GPIO, people would usually write a program in assembler or C. You could use other languages as long as they allow you to control how much bytes and at which memory address you want to write.
5. We can now write bindings in Python, Java, Ruby... that call the C functions from step 4.
This is simple enough, except the operating system knows the address you are using doesn't belong to your program and will not let you read or write from it. That's why you need to be root.
USB Relay
I use this thing. It's a tiny USB relay with two outputs rated for 220 V / 2 A (max 60 W). (Check that in your garage door motor manual or better yet, measure it.)
It is developed by a company in Switzerland, and I like it because they provide libraries for almost every programming language known to man in a very tiny package and a nice enclosure.
They have other products too, such as one with 8 relays with 1500 W each, and even a module that plugs into it and acts as an Ethernet web server, you might want to check them out.
If price is an issue or you have other components, you might consider wiring the relay directly to the GPIO output in the first case or building your own board in the second.
Another cool stuff is that you can label your devices and the individual relays, as well as controlling the devices directly and lighting up a beacon to physically locate the devices using a web interface:
For instance the relay closing the "open" circuit on the garage door motor board can be accessed with the name "garage_relays.open" instead of "RELAYL01-0EE8E.relay1".
Note you just can't have exposed naked boards in your garage, or in any production environment for that matters!
Not only can the currents and voltages be dangerous (often times they are when you use relays), but dust, water, ice, or animals can completely destroy your equipment.
Funny thing, for some reason spiders love the Raspberry Pi and the YoctoRelay. Maybe that's because the company (YoctoPuce) uses a spider in their logo?
Wiring
This comes from the installation manual of my garage door:
If you wonder about the exclamation mark, here goes the text version:
Here they talk about a button. And what do we humans do with a button? We press it for about half a second and release it. You will simply have to replicate that in software by closing (current flows) and opening (no current) the circuit with the relay.
Now you may want to make sure the voltage and current are not higher than those your relay might support.
If you garage door motor works like mine, you better wire so that the circuit is closed when the relay is in the "ON" position.
A relay usually has four legs / pins / IO :
- Coil load.
In my USB relay, this one is hidden. So if you took a wire and connected it on a GPIO pin on one end, the other end would go to this coil load leg. It controls if the relay is "ON" or "OFF".
(By "ON" I mean "closed", current is flowing between C and NO.) - Common (C)
- Normally Opened (NO)
- Normally Closed (NC)
When you load the coil (gpio pin high), electricity flows between C and NO.
When you don't load the coil (gpio pin low), electricity flows between C and NC.
If you mix them all the time like I do, you could use a mnemonic I came up with: "I can relax when the door is closed." or "Relay ON = current flowing through NO"
The "closed" contact is used when the spring is resting, and the "opened" contact is used when the spring is forced into the other position.
If you are still not sure, wire a LED like this guy did in his YouTube video and see for yourself.
Now tell me. As most of the time the circuit will be opened, where should the wires coming from pins 14 and 17 on the garage motor board go? (Now I want you to take the time to figure this yourself.)
Hint: when the wires are resting (no current flowing though them), the coil is resting as well, so you connect it to C and ... ?
We want the circuit to be closed when we activate the relay, so the answer is "C and NO".
Note: if you wire the relay directly to GPIO, you MUST add a pull-down resistor so that the relay reads 0V when the GPIO pin is on its LOW setting.
What it actually looks like
Let's take a look at the installation in my garage:
The image at the top shows the system from the side. You can see a PLC adapter, the garage door motor, the Rasperry Pi, the USB relay board, and spider webs.
I don't have an Ethernet (802.3) plug in the garage (who does?) and the Wi-Fi signal is too weak there. So I used a PLC adapter instead. I have a few of them in my house as the wireless signal has trouble crossing floors. Such an adapter transmits Ethernet packets over electrical conductors.
The electrical motor doesn't seem to be a disturbance at all.
The Raspberry Pi is simply mounted on a screw.
The image at the bottom shows the rear panel of the garage door motor.
Remember in my system it is possible to distinguish opening and closing the door. So my USB relay board features two tiny relays. They are connected using four red wires to the board at the rear of the door motor enclosure. This matches the schematic you saw earlier.
The other wires are for the push button next to the garage door and the keypad outside.
how did you implement history Feature
ReplyDeleteThe open and close requests sent to the web app contain a username and email addr. When the web app treats the request, it then adds this info, along with the current time and the type of request (open or close) to a SQLite database.
DeleteHistory page template: https://pastebin.com/4FHL0uYM
Create SQLite3 table: https://pastebin.com/ZRi5vHMN
Flask web app: https://pastebin.com/0ZcExrMU
Very nice article, I enjoyed reading your post, very nice share, I want to twit this to my followers. Thanks!. Garage Door Opener Replacement Anthem
ReplyDelete