I often follow a lot of techy instructables and am always amazed with the things people come up with. A while back, I found an instructable on a wifi garage door opener which I thought was really cool and added it to my unending to-do list of fun projects. Fast forward to the present, and I still hadn't gotten around to the project. But I did have a baby (well, my wife did, not me). My in-laws were gracious enough to offer 5-day care at my house (yes, I'm spoiled) but they had pleaded with me to give them a garage door clicker to keep in their car, versus having to use the temperamental keypad outside the garage. So, two options laid before me. Take the five minutes to reprogram their clicker for my garage. Or, take a few weeks to finally work on my garage door opener project. And so, bingo-bango, my instructable was born.
I went back to the original posting and liked many hardware features that the author used, but the software isn't exactly what I wanted. So the solution I desired needed to have the follow characteristics:
- Didn't require a password
- Should only work when connected to my house LAN
- Custom app on my iPhone to control it
- Use an Arduino with either an ethernet or wifi shield
- Make it in-law proof
Teachers! Did you use this instructable in your classroom?
Add a Teacher Note to share how you incorporated it into your lesson.
Step 1: What You Need
The materials I used for this project were:
- Arduino UNO with ethernet shield: Mine was the Vilros Ethernet R3 for Arduino, but an Etherten shield or equivalent should be fine
- A spare Linksys router: I reflashed the firmware with DD-WRT (I won't cover this step since there are many tutorials online of how to do this) to be used as a WiFi repeater bridge. Find one on Craigslist. They're dirt cheap
- iPhone: mine is a 7 plus
- LEDs, 330 ohm resistors, breadboard, jumper cables: used for debugging
- 5V Relays: I ordered mine from here. It took about 2 weeks or so to arrive from China. But they are cheap enough, so order a bunch
- 3D printed case (optional): to house electronics
- Copy of Xcode (the Apple app developing software)
Step 2: Setting Up Hardware for Debugging
First, let's set up the hardware that we will use for debugging purposes. I temporarily mounted the Arduino and ethernet shield along with my breadboard in a small platform I had. I inserted the LEDs and resistors as shown in the pictures. If you need more specifics on how to connect LEDs to the Arduino, this tutorial may help. But there are as many of these tutorials as colors of the rainbow, so choose your favorite. Lastly, I connected the ethernet port of the Arduino to the router, and plugged the Arduino into my laptop via a USB cable. That's it.
Step 3: Writing the Arduino Code
The next step is to determine how the Arduino is going to execute what I want it to do. Initially, when a user's iPhone first connects to the LAN, the Arduino should communicate the current state of the doors. Afterwards, the Arduino would receive commands from the user's iPhone to open/close the doors along with updating the variables in the Arduino code keeping track of the door's status.
I decided the best way to do this was to pass JSON (Java Script Object Notation) messages. I liked these because they have a key-value structure along with header information. And it turns out, the Arduino can support this if you download the ArduinoJSON library. They have many examples on how to interact with this library. A few examples you'll want to pay close attention to use the classes:
EthernetServer EthernetClient DynamicsJsonBuffer
Or you can have a look at my code to see how the functions in these classes are used.
I also scoured the interwebz for example code wherever I could find it. One semi-related application was this temperature sensor application for the Arduino and iPhone. It's a very complete example with a lot of good explanations and downloadable code.
Ok, so the next thing which made me scratch my head was do I make the Arduino the server or the client. It seemed like the Arduino should be the server when a user initially connects since it is serving the initial door states to the iPhone. Alternatively, when the Arduino is receiving commands from the iPhone, I thought it should be the client since it's passively receiving data.
It turns out what worked was to make the Arduino the server in both cases. The way the code essentially breaks down is within the main Arduino
call, I defined a function called
This function makes the Arduino the server and it listens for any clients. If it finds one, it disregards the client's JSON's header information (unimportant) and attempts to parse the data. If it finds data it can parse, then we know the iPhone has sent us information about what the state of the doors should be and the Arduino updates those variables accordingly. If, on the other hand, we do not find any data to parse, then we know this is the first time the user has connected (so he cannot have possibly sent us any information about the doors) and we send him a JSON message informing him what the current state of the doors are.
If anything I said above seems confusing, let me know, but you can also view the code at my repo.
Step 4: Disclaimer Before Writing the IPhone Code
Apple...oh Apple. [Cue heavy sigh]. I used to love Apple. But this little diatribe is not relevant for this discussion.
Swift is the latest language for designing apps on the iPhone. Folks, I really don't care for it. It seems to change with the tides and honestly I don't even know which version I have installed on Xcode. Additionally, when looking for examples/tutorials, not all were relevant due to how quickly the language changes.
Now if this weren't bad enough, (I promise this is going somewhere), I discovered after compiling/building my XCode project, that it wouldn't load on my iPhone. The reason is because my machine is so old (MacBook Pro from 2010) that the highest OS that I have is 10.11.6 (El Capitan). Essentially, this means I cannot install the newest Xcode on my machine which has support for the latest iOS in my iPhone (which is 11.1 or 11.2). The error manifests itself by saying something like "Could not locate device support files".
It turns out that if you suffer from the same problem, there is a fix which is more of a hack. Entering the error into Google, leads to a wealth of similar solutions. Here is one. The upshot is you need to download the latest beta version of Xcode (this is important so it doesn't automatically install and then subsequently fail) and then navigate to:
and copy all those directories that you do not have into the SAME path for your CURRENT version of Xcode. Afterwards, restart Xcode, and try to compile etc. and it should work fine. It did for me.
Step 5: Writing the IPhone Code
With version problems under control, I had to cobble together some resources to write the app, and I didn't want to spend a month learning Swift. With that said, I did find Code with Chris a really good resource and investment of time. His tutorials are especially good because it shows you how to implement touch-screen functions and the interaction between the Main.Storyboard with the actual code. Especially in the last few tutorials. So I really encourage you to watch them before you embark on Xcoding. Alternatively, like anyone else, I looked for similar Swift examples and tried to learn from them. I'm no Swift expert, but I found what I needed.
The idea behind the app was to design custom Open and Close touch buttons in whatever paint application you have that would function as you may expect. When the app first powers up, it utilizes a function called
which subsequently calls:
that actually does the heavy-lifting and parses the initial JSON data from the Arduino describing the current state of the doors.
The other functions I utilized were:
leftOpenImageTapped() leftClosedImageTapped() rightOpenImageTapped() rightClosedImageTapped()
which, when called, update the current state of the doors and then call the function:
to actually create the necessary JSON to be sent to the Arduino.
For more details, you can see the code for the project here which should explain a bit more since I tried to comment it reasonably well. Honestly, I kind of hacked it together to get it to do what I wanted.
After the code was written, I uploaded it to the iPhone and began my debugging.
Step 6: Debugging
With my code written and uploaded to both the Arduino and the iPhone, it was time to debug. Since I didn't have everything connected up to my garage yet, I wanted to mimic it's behavior somehow. Cue the LEDs.
I took two red and two green LEDs hooked up as shown in an earlier pic. One set of green and red would represent the left door and the remaining set for the right door. If either door was closed, then the green light would blink non-stop. Otherwise if any door were open, then the red light would blink. I chose the initial state of the doors to be closed (green blinking lights) because when I first hook up the Arduino to the opener, I am going to make sure the doors are shut.
To see how it works, you can watch the short (crappy res -- sorry!) video of it in action. Voila! Works so far!
Step 7: Hooking It Up
Since my 3D printer is being serviced I didn't have a chance to print a case yet. The prior instructable that I based this one on, has .stl files available for download. (Note: If you plan to use the .stl files, the units are in cm, after corresponding with the author. I had to scale my .stl files by a factor of 10 since my printer works in units of mm). I will have to modify the design since I have two doors and hence need two relays. But since I'm eager to get everything connected, I began mounting the electronics on a scrap piece of wood I had lying around. So, I won't be showcasing the absolute finished product in this instructable.
Once the electronics were mounted to the piece of wood I had, it was a simple matter to find a temporary home for it on the wall. In the pictures, you can see a bit of a mess of wires running from the router to the Arduino, the Arduino to the relay, and finally the relay to the garage door buttons. When I popped off the actual button and unscrewed the casing from the wall, there were only two wire terminals. So, just like the instructable I used to base mine on, you can hook the wires from the relay in any order to the terminals on the door button.
After this, the entire setup magically worked first time! Sorry for not posting a video. I had to temporarily take it down before I could capture a video of it working, but I swear it did!
Step 8: Final Thoughts
After playing with this a little bit, I did notice some issues which I'll mention below. These are kind of important, so please consider them before deciding to duplicate this project.
- There was some inconsistent latency between hitting the button on the iPhone and having the garage doors respond. This could be a software or implementation bug, but I am still exploring it.
- A big issue: After loading the app onto the iPhone app, I noticed that after a few days when I would try to open the app, it would instead just return to the home screen. This was a consistent behavior. After wracking my brain for a bit, I finally broke down and posted the question on stackoverflow. One fellow's answer seemed to be correct: The evil empire that Apple has become decided that if you are not a developer, then the trust certificate you obtain from Xcode for you newly developed app is only good for 1 week. After that, it will not work on your iPhone unless you reinstall it (and the 1 week countdown resets) or you pay them a $99 fee for a longer certificate. I found this completely disheartening. Almost like I wasted my time. But if you are a developer, then this won't be an issue for you.
- One thought which occurred to me was if someone manually presses the garage door button, this event is not captured in the Arduino code. So as far as the Arduino is concerned, the door's state remains unchanged prior to the button being pressed. Additionally, if someone were standing underneath the door when another person were using the app to close it. The door sensors will force the door to go back up again and this event, as well, is not captured in the Arduino code. Needless to say this is another point to be figured out.
So thanks for bearing with me and again I apologies for some of the buggy behavior. Please let me know if you have questions!