Travel, Code, and Engineering
on September 18, 2016 by Kurt Tomlinson
It's been my dream for a long time to be able to control the lights in my house from anywhere in the world. Now I can do that without breaking the bank. Using a Raspberry Pi, some Etekcity Remote Control Outlets, and a 433 MHz transmitter, I can control the outlets in my home with Siri and any other HomeKit-enabled app on iOS. In this tutorial, I'll show you how to do it too.
Unlike the other tutorials in this series, you'll need a few things for this one:
Additionally, you'll need some tools and consumables:
The price of this project is quite cheap if you already have a Raspberry Pi and some basic electronic tools and components: just $32 for the RF outlets and the RF modules! That's downright miserly compared to the cost of buying a commercial, ready-made solution.
This project takes some time to complete, so I recommend you plan to complete this project over the course of several days. I've broken it up into steps with completion milestones to give you good stopping points.
The RF Transmitter and Receiver need antennae to function really well. Without antennae, their range will be limited to just a few meters. You have a couple choices for your antenna design. The simplest choice is to build a quarter-wave monopole. A quarter wave monopole antenna is just a straight piece of wire that is 25% as long as the wavelength RF signals you're working with.
In this case, the Etekcity outlets operate at 433 MHz. We can find the wavelength of 433 MHz waves with the wave equation
c = f * λ
c = f * λ
where c is the speed of light (299,792,458 m/s), f is the frequency of the wave (433,000,000 Hz), and λ is the wavelength of the wave (in meters). Using this equation, we find that our antenna should be 173 mm (or 6-13/16 inches):
299,792,458 = 433,000,000 * λ
λ = 299,792,458 / 433,000,000
λ = 0.69236 m = 692 mm
λ/4 = 173.09 mm
299,792,458 = 433,000,000 * λ
λ = 299,792,458 / 433,000,000
λ = 0.69236 m = 692 mm
λ/4 = 173.09 mm
Calculating that was the hard part. To make the antenna, just cut a portion of your 18 AWG wire to the correct length and then strip off about 3 mm (or 1/8 inch) of the insulation. You'll need to do this two times: once for the transmitter and once for the receiver. (I chose 18 AWG wire because it was what I had on hand, it didn't bend easily, and it fit nicely into the ANT holes of the RF modules. The diameter of the wire doesn't really matter, but stiffer is better because the antenna should be straight.)
Once you've made your antennae, solder them into the holes marked ANT on your RF transmitter and receivers.
Once you've soldered the antennae to your RF modules, you're done with this step. In the image, I used a different design for my transmitter's antenna called a "coil loaded" antenna. I can't comment on which antenna has better performance, but the coil loaded antenna is slightly more compact.
This step is relatively straightforward. Peel off six jumper wires from your Female to Female Jumper Wires and make the connections in the diagram below.
Here's the same information in a couple tables (the pin locations are described when viewed from the top with the pins on the side closest to you):
You'll need to find out what codes are sent to turn on and off each of your outlets.
First, update and upgrade your packages and package repositories:
sudo apt-get update
sudo apt-get upgrade
Then install Git:
sudo apt-get install git
Clone and build the WiringPi utility. It's a library that is used by some other programs we'll be using shortly.
git clone git://git.drogon.net/wiringPi
Now you can compile a couple utilities, codesend and RFSniffer, that let you send and receive data with your 433 MHz RF modules.
git clone --recursive git://github.com/ninjablocks/433Utils.git
Next, you can run RFSniffer to find out what codes are assigned to each of your outlets. RFSniffer won't show any output in your console right away. If you press and hold a button on your Etekcity remote, you should see lines like Recieved 5518604 printed in your console.
Make a table showing which codes correspond to which buttons on your remote. The codes for my remote are below. Your codes will be different.
Once you've created your own table, take a break! You're almost done, and you've completed a good portion of this tutorial.
Now we can use codesend to confirm our RF codes are correct and our transmitter is functioning correctly.
Plug a lamp into your first outlet, and make sure you can turn it on and off with your remote. Then open up two terminal windows. In the first terminal window, open RFSniffer so you can see the codes that are sent by your remote and the codes that are sent by your transmitter module:
In the second terminal window, you can send some codes to try to turn your outlets on and off. For example, the last line below turns off my first outlet:
sudo ./codesend 5510460 -l 200
5510460 is the code that I got from RFSniffer. the -l argument tells the codesend utility to send the code with a pulse length of 200 us. I was able to control my outlets with pulse lengths between 105 and 1445 us.
You will want to try using different pulse lengths to see what works for your setup. Longer pulse lengths will make it take longer to send a code, but shorter pulse lengths will make it more likely that your code will not be properly received.
I found 200 us to be a good compromise between speed and reliability for me. I'd recommend you use pulse lengths around 200 as well since long pulse lengths can also be unreliable if you get too close to the upper limit for your devices.
After you can successfully turn your outlets on and off with your Raspberry Pi, celebrate! You've completed another milestone in getting your home more automated.
Homebridge is a Node.js package that lets you use HomeKit to control devices that weren't designed for HomeKit. We'll use it as he missing link between Siri on iOS and our RF outlets. First, we need to install Node.js:
sudo dpkg -i node_latest_armhf.deb
Confirm that Node and npm are installed correctly by running node -v and npm -v. The output I got is shown below:
pi@raspberrypi:~ $ node -v
pi@raspberrypi:~ $ npm -v
Install make and Avahi. Make is used to build binaries for your system, and Avahi gives Homebridge access to Apple's Bonjour protocol.
sudo apt-get install make
sudo apt-get install libavahi-compat-libdnssd-dev
Finally, Install Homebridge:
sudo npm install -g homebridge
Now we'll add some plugins to Homebridge, configure them, and check to make sure that we can talk to Homebridge from iOS.
First, install the dummy-switch plugin and the rcswitch platform:
npm install -g homebridge-dummy
npm install -g homebridge-platform-rcswitch
Dummy switches are switches that turn off immediately after you turn them on. They're useful just for checking if your iPhone can talk to your Homebridge.
When you install plugins and platforms, you tell Homebridge what type of devices you want it to be able to talk to. The next step is to tell Homebridge how many of those devices you have, what their names are, and how to talk to them. In other words, we need to configure Homebridge. We do that by modifying Homebridge's config.json file.
Overwrite your Homebridge config.json with Nate Henrie's config.json file:
sudo wget https://gist.github.com/n8henrie/639c7f5d72b4202cce7e/raw -O /etc/homebridge/config.json
Here's the content of his config.json file, if you're curious:
"description": "Example config file to test with a homebridge-dummy switch.",
"name": "A dummy switch"
Update the config file and run Homebridge with the command below:
sudo DEBUG=* /usr/local/bin/homebridge -D -U /etc/homebridge
I believe DEBUG=* and -D put Homebridge into debug mode so it prints more helpful information into the console. I think -U /etc/homebridge tells home bridge to look in that directory for its config.json file and to store any persistence data it might have.
After you run Homebridge you should get an output that looks something like the image above. Once that happens, open a HomeKit-enabled app on your phone such as Elgato Eve, Insteon+, or on iOS 10 you can use the pre-installed Home app. In whichever app you choose, tap the button for "adding an accessory" or something similar. You will be prompted for a 8-digit PIN. Enter the PIN printed into your console by Homebridge. Scanning the pin is usually unreliable, so I suggest typing it in.
At this point you should be able to see a single dummy switch that switches itself off immediately after you switch it on. Congratulations! You've reached another milestone in this project. You've linked your Raspberry Pi to your iPhone through HomeKit and Homebridge.
We're almost done. Now we just have to update our config.json file to tell Homebridge that we have RC switches we want it to control, what their names are, and what codes to send to turn them on or off. To get Homebridge to read the updates you're going to make to the config.json file, we're have to restart Homebridge. I ran into issues with Homebridge not releasing the port it was running on properly, so I found that a reboot was the surest way to cleanly shut down Homebridge.
First, update your config.json file:
sudo nano /etc/homebridge/config.json
I've included my config.json file for your reference. You're want to change the "pin" value to a new PIN so no one else can control your devices. Also, update the "code" lines with the on and off codes you wrote down earlier. If your Etekcity outlets worked well with a pulse length significantly different than 198, update the "pulse" lines as we11 with yoru pulse length
"description":"A Homebridge with four RC switches.",
Finally, reboot and start Homebridge again. Rebooting might take longer than usual because Homebridge likes to take a long time to shut down:
sudo DEBUG=* /usr/local/bin/homebridge -D -U /etc/homebridge
At this point you should be able to control your outlets remotely in your HomeKit-enabled app. If not, go back and make sure you can still turn your outlets on and off with codesend and that the codes in your Homebridge config file are correct. Also, make sure that you use the -U /etc/homebridge parameter when running codebridge, otherwise it won't use the config.json file you made.
There are many options for how to get Homebridge to run at boot. You can use systemd, rc.init, cron, and probably 10 other things. My preferred method is with cron. First, open sudo's crontab (and press 2 to use the nano editor if you're given the option):
sudo crontab -e
Then add these lines to the end of your crontab file:
@reboot DEBUG=* /usr/local/bin/homebridge -D -U /etc/homebridge > /home/pi/homebridge.log 2>&1
The SHELL=/bin/bash line tells cron to execute your cron jobs with bash, the default login shell on Raspbian. (Normaly cron uses /bin/sh which can make cron behave differently than your normal shell.) The PATH=... line tells cron what folders to look in for executables like Node. Cron uses a minimal PATH variable by default, so it won't find your Node executable without this little bit of help. Finally, the @reboot ... line tells cron to run Homebridge every time your Raspberry Pi boots up. (It doesn't matter if you reboot or halt and then power on; your command will run at every boot. The @reboot syntax is a bit of a misnomer.)
And that's it! You should now be able to control your Etekcity outlets from an iOS device. Congratulations on completing this tutorial.
If you found that I skipped a step or need some help, leave a message in the comments below. If this tutorial worked for you, let me know that as well!
on May 29, 2016 by Kurt Tomlinson
I wrote the BarStack Windows App way back in 2014 when Windows 8 was brand new. Windows 8.1 hadn't been released yet, and "Metro" style apps had just been rebranded as "Windows Store" apps. Developing Windows Store apps was major pain. The development tools didn't work very well, and the GUI building blocks were far from refined.
Since then, a lot of time has passed. I moved on from BarStack and created my blogging platform, Bloopist. The last update I made to the BarStack Windows app was probably in late 2014, and the majority of updates I made to the BarStack website were security-related. Any new features planned for BarStack were put on hold.
Recently, I was notified that the BarStack Windows app wasn't working. I fired it up to check it out, and it definitely wasn't. It crashed right away when it tried to log in. The first couple bugs were pretty easy to find. The server was responding with text instead of JSON data, so that caused the JSON parser in the Windows app to throw an unhandled exception. I corrected this on the server side, so the Windows app now longer crashed on startup.
The next bug was much harder to root-cause. When the app tried to log in to the server, the server would respond with a JSON structure with an empty "email" field and a null "password" field. According the BarStack API documentation I had written a few years ago, the response was supposed to include a "success" boolean, a "auth_token" string, and an "email" string.
After several hours of poking around, I finally noticed that my Ruby on Rails server process was processing GET requests from the Windows app when in production mode and POST requests when it was in development mode. There was something wrong in production that wasn't wrong in development. The Rails code was properly interpreting a GET to the sign-in path as an "index" action, but the Windows app should have been making POST requests that would be interpreted as "create" actions for the Rails server's sessions controller.
For some reason, POST requests were being turned into GET requests in my production environment before they were handed off to my Rails app's controllers. I checked my webserver's NGINX access logs to see what was going on. I saw that whenever the BarStack Windows app was making a POST requested, NGINX was responding with a 301 Moved Permanently redirect followed by the BarStack Windows app making a GET request for the same resource. I did some digging and found that NGINX could be redirecting the BarStack Windows app because of a rewrite rule. After a redirect, only GET requests can be made. NGINX wasn't the culprit, but this information did help me find the real problem.
After a couple hours of trying to get NGINX to log its redirects and confess to a crime that it didn't really commit, I moved down the stack a little bit. In the BarStack web app, NGINX hands off requests to the Rails app, and then the Rails app processes those requests with Rack before handing it off to the Rails app's controllers. In my Rails app's logs, the first POST request was never recorded, and NGINX wasn't responsible for sending the redirect response. That meant that somewhere in the Rack Middleware portion of the BarStack web app, a redirect response was being generated.
I opened up the gemfile for the BarStack web app and took a quick look around. In the middle of the file, I saw
gem 'rack-ssl', :require => 'rack/ssl' #enable https connections
Oh yeah! I had forgotten that I had started forcing people onto secure https connections. (The purpose of the rack-ssl gem is obvious when you consider its description on rubygems.org is "Rack middleware to force SSL/TLS.") Any connection that was made over http would get redirected to the same resource but with https, and my Rails app's controllers would never know about the original request! That expains why I saw a POST and a GET in my NGINX access logs but only a single GET in my Rails production logs.
I checked the BarStack Windows app's API endpoint, and sure enough, it started with http://.... I slapped an "s" into the endpoint constant, recompiled the app, and it ran smooth as silk.
And that's the story of how I fixed a bug with a single keystroke after 8 hours of debugging.
Photo by Forsaken Fotos
on May 8, 2016 by Kurt Tomlinson
If you have any reason to want to connect to your Raspbery Pi when you're away from home, you're going to want a dynamic DNS address. A dynamic DNS address lets you use a constant address like raspberry.johnsmith.com instead of an ever-changing IP address. This is useful if you've set up a VPN server on your Raspberry Pi or just like to tinker over SSH while you're away.
This tutorial hinges on the assumption that you have a domain name registered with Google Domains. If you have a domain with another registrar, then you should either move your domains to Google, or find another tutorial for dynamic DNS on a Raspberry Pi.
This tutorial lets you use a subdomain of one of your domains to point to your raspberry pi. After forwarding the correct ports from your router you your Raspberry Pi, you'd then be able to access your Raspberry Pi from anywhere in the world.
The first step is to go to the DNS records for the domain you want to use for your dynamic DNS address. There, scroll down to the Synthetic records section and add a Dynamic DNS entry. For me, my dynamic DNS entry is "raspi.kurttomlinson.com".
Next, expand the Dynamic DNS section of that web page and click "View credentials". This will show you a randomly generated username and password that you'll need later.
Next, ssh into your raspberry pi and install ddclient: sudo apt-get install ddclient. Once it's installed, a few prompts will pop up. It doesn't really matter what you enter into the prompts. The prompts are used to created a configuration file for you, but we're going to replace that configuration file anyway.
sudo apt-get install ddclient
To edit ddclient's configuration file, enter sudo nano /etc/ddclient.conf. Make the configuration file look like the image, but replace the username and password with the username and password you got from Google Domains earlier. Replace the last line with the dynamic domain you created earlier on Google Domains. Here's the content of the config file for your convenience:
sudo nano /etc/ddclient.conf
# Configuration file for ddclient generated by debconf
Finally, make sure that ddclient gets run at boot. You can add ddclient to your rc.local file to do this.
Photo by The Preiser Project
on March 27, 2016 by Kurt Tomlinson
Everyone uses the table of elements, but not everyone calls the elements the same thing. For example, in the UK, the element with atomic number 13 is called "aluminium", whereas in the USA, it's called "aluminum".
I decided to look at the differences in names for some of the elements of the periodic table in American English and Korean. I found that, generally, the longer an element has been known, the more likely it is that the Korean name for that element differs from the American name.
In the table below, you can see the names of elements 1-56 and 72-86 in American English, Korean, and Romanized Korean (if the Korean name is substantially different from the American name) as well as the date of discovery. The date of discovery is given as * if the element was known to ancient civilizations. Generally, if an element was discovered before 1774, then the Korean and American names differ. Otherwise, they're the same outside of a few exceptions. This is interesting because one of the first efforts to systematically classify elements was published in 1789 by Antoine Lavoisier.
A couple notes about the Korean names:
Table 1. Selected Elements of the Periodic Table Sorted by Date of Discovery
Photo by Hans Splinter
on March 25, 2016 by Kurt Tomlinson
Apple recently made headlines for its newly unveiled "Apple Renew" program. What is it? It's a buyback program for old Apple products. Why are they doing it? According to Apple, it's good for the environment, and it rewards their customers for recycling their phones with cash. Really, it's just another way to increase revenue.
Is it really a coincidence that Apple unveils plans to buy back old iPhones on the same day that it launches the lowest-priced iPhone ever, the iPhone SE? Nope.
Let's start with a little bit of history. The iPhone SE replaced the iPhone 5S as only new 4-inch iPhone being sold directly from Apple in March, 2016. Before the iPhone SE was revealed, a new, unlocked 16 GB iPhone 5S sold for $450. On eBay, a used 16 GB iPhone 5S went for around $180-300 depending on its condition. In March, 2016, the iPhone 5S was replaced with the $400 iPhone SE.
Before the iPhone SE was released, people looking to buy an entry-level iPhone (or just any 4-inch iPhone) were probably trying to choose between buying a used iPhone 5S for $250 or a new iPhone 5S for $450. That's a pretty big premium to pay for a new phone over a used phone, but the Apple ecosystem is able to support it because of its luxury branding.
However, things change dramatically when the iPhone SE enters the picture. Now, consumers have to choose between a used iPhone 5S with internals that are 2.5 years out of date or a phone with an identical exterior, a much better camera, and an up-to-date processor. On top of all that, the new model only costs $400, the lowest a new, off-contract iPhone has ever cost. Now imagine what that will do to the resale value of the iPhone 5S. The iPhone 5S resale value would absolutely plummet. The price premium between a used iPhone 5S and the equivalent new iPhone drops by $50, and the opportunity cost of going with the used phone has increased because the new iPhone is faster and has a better camera. Additionally, the supply of used iPhone 5S phones would increase as the Apple faithful upgrade to the newest 4-inch iPhone, and increased supply causes additional downward pressure on the resale value. The resale value of the iPhone 5S would instantly drop by $50 to $150.
This pushes the cost of a used iPhone 5S down to $130 to $250, conservatively, or $100 to $220 in a more realistic scenario.
Each iteration of the iPhone has been successively less revolutionary, and this makes consumers desire the latest and greatest iPhone less and less. It's more and more common for people to stick with their iPhones for longer because there are fewer compelling reasons to upgrade now than in the past.
Consumers, faced with choosing to buy a $100 iPhone 5S or a $400 iPhone SE would shun the new phone in droves. Who wouldn't want a relatively recent, off-contract, iPhone for $100? Apple's iPhone sales have been growing more slower as of lately, and the huge supply of cheap, used iPhones has been a huge drag on Apple's main source of revenue: new iPhone sales.
So, if you're Apple, how do you stop used iPhones from eating into your new iPhone sales numbers? Simple, you put a cap on the minimum value resale value of your used phones. Apple did this in a brilliant marketing maneuver where their true intentions are hidden, and the public applauds them for being an environmentally green company: the Apple Renew program pays $150 for a used iPhone 5S. This program does two things for Apple. It increases the lower limit for the price of a used iPhone to a hard $150, and it reduces the number of used iPhones in circulation in the used iPhone market.
The lower limit supports the price of used iPhones, and the reduced number of used iPhones in circulation reduces used iPhone supply. Both put upward pressure on the price of used iPhones.
Now, the consumer looking to get an entry-level iPhone or just a 4-inch iPhone has the much easier choice of buying a used iPhone with a 2.5 year old design in sometimes unknown condition (when buying online) for about $250, or a brand new iPhone SE. Given the much smaller gap in price between the used iPhone 5S and the new iPhone SE as well as the spec differences in the two models, many consumers will choose to buy a new iPhone SE.
The end result? Huge numbers of iPhones sold. Who knows if Apple's profit will increase because of this move or not? Maybe the cost of running the Apple Renew program will eat most of the revenue gained by selling more new iPhones. Maybe it won't.
What's certain is that Apple will sell record numbers of iPhones and achieve record levels of revenue for another year. And that's all that matters to shareholders. In the end, Apple is just a company that's trying to make as much money as possible for its shareholders. If it can earn a positive image as a green company while doing so, then that's just icing on the cake.
Note: I'm sure the good people working at Apple do care about the environment. The main point I'm trying to make is that Apple is just a company, and anthropomorphizing it and believing that it "cares" about the environment is naive. Companies are nothing more than legal entities that don't have the capacity to care about anything. In this situation, as in most situations in life, there is a level of nuance that isn't always readily apparent. My goal in writing this was solely to share my observations on a piece of marketing that I found to be particularly well executed.
Photo by Yagan Kiely