Control Legacy Electronic Devices Via the Internet

Saurabh Chaturvedi
The Startup
Published in
12 min readOct 25, 2020
Many legacy electronic devices can be IoT-ified inexpensively. Image by Gerd Altmann from Pixabay

I’m fascinated by the idea of smart consumer electronics. When it comes to tools I use on a daily basis, I’m sort of a control freak and like to utilize them as much as I can, from wherever I can. Unfortunately though, some of these tools are legacy electronics, like my air conditioner, and I simply don’t want to spend money buying a new smart AC because this one just works fine. So for quite some time, I had been wondering if there was a way to smart up my AC in a cost-effective manner. I then realised last week that I had a Raspberry Pi, as well as Adafruit’s CircuitPlayground Express, which I had got as part of conference swag lying around in my desk drawer. These little beasts were essentially begging to be utilized for some good as I had not played much with them.

I noticed that CircuitPlayground Express had an embedded IR transceiver. While it doesn’t have its own OS and Internet connectivity, I thought I could use it as a substitute for my AC remote in some way. Soon I also realised that the Internet connectivity void could be filled by the Raspberry Pi! Next thing I knew, I was scouring through the specs of these boards, trying to devise a way to use them in conjunction to smarten up my AC. After spending days on this problem (some of which were very, very frustrating), I was finally able to accomplish what I wanted!

As a starting step, the first thing I did was build a way to remotely turn on my AC — and by remotely I mean from anywhere in the world! In this article, I’ll be demonstrating how I used few open source tools along with the aforementioned boards to bring the power of Internet to my legacy air conditioner, which has no other means of I/O except infrared.

Prerequisites

I employed the following devices:

  • A legacy air conditioner (in my case it is an old Fujitsu General model)
  • The remote control of that AC
  • Raspberry Pi (any version will work)
  • Adafruit CircuitPlayground Express (to be used as an infrared interface)
  • A provisioned box on any cloud provider of your choice (I used AWS)
Raspberry Pi. Image by Kevin Partner from Pixabay

Now, if you don’t have CircuitPlayground Express (CPX), you can substitute it with any IR module, like this. I used CPX because I already had it. Also, what we’ll be doing isn’t resource intensive, so any cloud box with basic specs will work, including free tier ones (in fact, I used a free-tier AWS EC2 box for this).

Adafruit CircuitPlayground Express. Image Source: adafruit.com

On the software end, we will need the following tools (I’ll explain their usage later in the article):

  • tmux
  • Python3.x with Flask web framework
  • frp
  • mu-editor for dumping code to CPX
  • Shortcuts app on iOS / IFTTT on Android (I only describe the steps for iOS but they’ll be almost identical for Android)

Note that all but the last one are open source tools!

Dealing with Infrared Signals

In order for us to be able to switch on the AC from CPX, we first need to see how the remote control does it. Sure, it sends an infrared signal to the receiver in AC, but we need to know how this signal looks like so that we can imitate it from CPX.

Figuring this out was arguably the hardest part for me, because the official Adafruit tutorial and many other tutorials on the Web assume that devices we we’re working with follow the NEC infrared protocol. Unfortunately that wasn’t the case with me and I realised that after painful 4 hours of scratching my head. Different companies use different protocols, and Fujitsu General doesn’t use NEC. I then tried to get the IR codes for my AC model directly from the docs of Arduino libraries like these, but deciphering them was even harder.

Much later, I thought — what if I don’t try to obtain the decoded sequence from the IR pulses, but rather just imitate the pulses directly? For this, I experimented with the pulseio CPX library (preinstalled on the board)and discovered that imitating my AC remote this way was much easier — plus this approach also generalizes to all infrared protocols, not just NEC!

Capturing the Signal Sent from Remote Control

After connecting CPX to my laptop via USB, I first imported the necessary libraries and set up the IR receiver in mu-editor’s REPL. Then I pointed my AC remote control towards the CPX board and hit the power button on it. CPX received the IR pulses sent from the remote control, which I then printed on screen:

REPL session on Mu-editor for capturing IR signals from AC Remote Control

I ignored the stray values (generally short in length), and copied the large blob that shows up just after remote sends the power-on signal to CPX.

I repeated the process to check if values are same all the time. Notice that individual pulses have slightly different numbers for each transmission, but as we’ll see later, they work just fine as long as they’re together. The only significant pulse array you can see above is when I’d accidentally sent power-off signal from the remote control instead of power-on.

I then copied the pulse array for power-on signal and stored it in a separate place (Notes app on my laptop).

Imitating the Transmission of Power-on Signal from CPX

This bit was much easier. In the mu-editor REPL, I stored the power-on pulse array as a variable (the same pulse array I’d copied earlier). Then I initialized and configured the IR transmitter on CPX board. A standard frequency of transmission is 38kHz, with a signal duty cycle (“on-time”) of 32768, or 2¹⁵. Before sending the pulses, this library requires them to be converted to half-words, which I can be done via the pre-installed array library itself.

REPL session on Mu-editor for imitating transmission of IR signals from CPX to the AC

The moment of truth for me was when I finally pressed enter after the pulseout.send(power_on_sig) line. Immediately after pressing enter, I heard the clicking beep from my AC and it did turn on! 🥳

A nice, neat little trick to try out is seeing CPX sending the IR pulses via a super-slow-mo camera. Since IR pulses are sent super quickly, and therefore not visible to the naked eye, you can capture them on video and play it back in slow-mo to see it in action — notice the blinking whitish dot in the middle of the board:

IR Transmission in Action. Footage Courtesy: my friend Manan Nahata

Telling CPX When to Send the Infrared Pulses

Now that the core functionality of my solution had been figured out, rest of the steps were relatively smoother. Next thing to find out how to signal CPX itself to fire these IR pulses towards the AC. Interestingly, the USB cable that powers up CPX can also be used as a serial input-output periphery. So I decided that a simple way to signal CPX about when to transmit IR pulses would be to send bytes representing the string "on" through the USB. CPX will fire the IR pulses every time it receives this sequence of bytes from its mini-USB port. I also decided to add a flash of green to the LED’s on CPX as a cool way of acknowledging received command to turn on the AC. The overall code for this is fairly simple:

So that was how CPX handles serial input and infrared output. Now I needed a way to send commands to CPX via the Internet, so that I can indirectly control my AC remotely. This is where Raspberry Pi came in. I SSH-ed to my Pi, installed Flask on it via Python’s package manager pip, and then wrote a simple server that sends bytes over USB representing the commands. What I envisioned is, whenever I make a GET /switch/on request to this server, it should send on bytes via USB to CPX, which in turn sends corresponding IR signals to my AC to switch itself on. Here’s the mostly-self-explanatory code for the server:

After connecting CPX to my Raspberry Pi via USB, I fired up this server on Pi via python3 command_sender.py. Next, from my laptop, I sent a cURL GET request to my Pi (via home LAN), as follows:

$ curl -XGET http://192.168.0.107:5000/switch/on

And yet again, my AC turned on gracefully as soon as I pressed enter! This was one of those rare occasions when my code works as intended in the very first run 😄. But this happiness didn’t last long as I was about to hit a kind of dead end. One side-note, though: a GET request isn’t the most suitable HTTP request for changing the state of the underlying system (in this case the AC) — a better request would’ve been a PATCH request. However, I was still experimenting and GET requests are way easier to test than PATCH ones (I can simply put the URLs in a browser to test them), so I went with the GET requests for experimentation.

How to Access this Raspberry Pi via Public Internet?

The system I was trying to build wouldn’t be of much utility if I couldn’t access it via Internet. It worked till now because I was sending commands to Raspberry Pi while being on the same home Wifi network.

While the straightforward way to make the Pi accessible via the public Internet was to enable port-forwarding on my home router, I didn’t want to do that mostly because of two reasons:

  1. I didn’t want to expose my home network’s public IP (I’m not a security whiz but I just didn’t feel safe doing this).
  2. My ISP can change the public IP address of my home without informing me, which can lead to headaches and unnecessary reconfiguration.

Anticipating the unreliability of this approach, I decided to look for alternatives. I was aware that reverse proxies are a common way to solve these kinds of problems, since they don’t require messing with router configurations and are relatively safer since connections are always initiated from within the network. A cool reverse proxy solution I was familiar with is ngrok , but the problem with ngrok is that the free version only allows connectivity for 8 hours. Then there is another solution built specifically for Raspberry Pi’s is dataplicity, but it’s terminal interface has some bugs and the free version is quite limited, just like ngrok. Moreover, I wanted to avoid as much external dependencies as possible.

“In computer networks, a reverse proxy is a type of proxy server that retrieves resources on behalf of a client from one or more servers. These resources are then returned to the client, appearing as if they originated from the server itself.” — Wikipedia

After searching the Web for a while, I discovered a free and open-source tool called frp, short for “fast reverse proxy”, that can be set up as a reverse proxy server, and has an extensive set of configurations available. I tinkered with it for a while and found it to be a great fit for my needs. Leveraging its power, I decided to go with the following approach:

I would set up a public-Internet facing box on AWS, that will act as a reverse proxy server. This means I’ll be sending my AC control requests to the IP of this box, and it will in turn forward that request to the Pi in my home. This is better than doing port forwarding, as I don’t have to mess with my router settings and change configuration every time my ISP changes the public IP for my home network. It is also safer as I can fine tune access to devices in my home network, way beyond the basic security configuration most home routers provide. More benefits of using a reverse proxy can be found in this Stackoverflow answer.

Setting up a Reverse Proxy Server on Cloud

As I said earlier, I used AWS for provisioning a box, but any other cloud provider will work just as good. After provisioning, I SSH-ed to the box and downloaded frp on it. Server side configuration is simple, I just had to add a couple of lines to frps.ini file:

[common]
bind_port = 7000

frp will be using this port (7000) for internal communication with clients (Pi in our case).

Next, I installed tmux and then triggered a tmux session. Then I started the frp server from within it. After that I detached from the session so that reverse proxy session keeps running even after I disconnect from the box:

Then I SSHed to my Raspberry Pi again, downloaded frp on it, and configured the frpc.ini file as such:

[common]
server_addr = <public ip of my AWS EC2 instance>
server_port = 7000
[web]
type = tcp
local_ip = 127.0.0.1
local_port = 5000
remote_port = 6000

Here, server_port is same as bind_port that I’d set on the server — 7000. Now, remote_port and local_port values mean that the reverse proxy server will accept requests on port 6000 and forward them to port 5000 on the Pi. I also modified the inbound settings of my EC2 instance’s security group so that ports 7000 and 8000 can be accessed.

Next, I installed tmux on the Pi too, and after opening a tmux session I started the command sender Flask server (note that I did not set up a production server but a development server, since I was still experimenting) as well as the frp client, and then finally detached from this session and disconnected from the Pi:

Most of my setup was done at this point. I connected my laptop to my mobile data, then made the following cURL call:

$ curl -XGET my.ec2.public.ip:6000/switch/on

…and the AC beeped to life yet again, this time via a signal sent over the public Internet!

Setting up a Shortcut on My Phone

The only thing remaining was to set up a way to conveniently make this API call from my phone to turn the AC on — I didn’t want to memorise the IP address of my EC2 machine, so I finally decided to put the Shortcuts app on my phone to some use, as that was another thing I rarely used until now, and I really wanted to try it out.

Here’s how I built a shortcut for my use case:

Open Shortcuts app > Create Shortcut > Add Action > Web > URL > add my API call > Tapped on + to add another action > Get Contents of URL (this makes the actual GET request for the URL configured in previous action)

Steps on Android phones / IFTTT app must be similar.

Here’s how the overall system looks like:

Images Courtesy: Adafruit, Pixabay, Unsplash

Everything was done now, so it was time for a demo!

While still on my mobile data, I opened the Shortcuts app on my phone and tapped on the shortcut I just created. Here’s what happened:

It works like magic!

So that’s how I made my AC remotely accessible. It can only power-on right now, but other functionalities can be added easily, now that the barebones have been set up. The AWS box I created can act as a single hub for all communications to and from legacy electronics. Not only can I send commands to the AC, it’s also possible to get its state and create controlled feedback loops for a smart temperature system — a lot of things can be implemented!

Bonus

frp also provides a dashboard to monitor traffic, you can enable it via adding these lines to frps.ini file:

dashboard_port = 9000
dashboard_user = admin
dashboard_pwd = foobar

Don’t forget to create an inbound rule for this port (9000 in this example) so that the dashboard can be accessed. It looks something like this:

I hope this article inspires you to work on your own DIY projects with Raspberry Pi, CircuitPlayground Express and legacy devices! Let me know your thoughts and feedback in the comments!

--

--

Saurabh Chaturvedi
The Startup

Software Engineer @Google. Passionate. Pythonist. Perfectionist.