Pi Lamp Software

Dillon Nichols
My Life as a Tinkerer
5 min readMay 5, 2017

I thought the hardware for my Pi Lamp project was pretty simple. The software for this project was the most challenging aspect. I learned a lot about the Linux operating system and hit a lot of dead ends along the way — but eventually came out with a fully functional project. I’m going to explain all the steps along the way, but use my troubles as a learning experience and don’t follow exactly in my path (repeat after me: “Just use threads”).

I started with r10r’s implementation of the rc-switch library for the Pi. This required having to install WiringPi so I could access the GPIOs in a similar method as Arduino. Then I merged my Spark Lamp code into the project and made the necessary changes so that it could run on a Raspberry Pi, including some small changes to the clickButton library. I decided to write the code using C++ to gain some more experience with it.

The next step was bringing in the Python code to control the Switchmate. This required lots of tricks to get the main C++ program to interface with the Python scripts. The easiest method was to create a bash script with the location of the Python script including its parameters and use the system() call in the C++ code to run it. This worked fine for switching the lights, but it didn’t allow me to run the Python script in the background, so I couldn’t use it to monitor the Switchmate’s state. This started me down a long rabbit hole of mimicking a Linux daemon in Python and calling that from my C++ code. This also worked, but then I didn’t have a way of communicating between the daemon and my C++ code. I was aware of WebSockets, so I tried using Unix domain sockets to send data back and forth. I got this mostly working but was unhappy with how complicated my code had become.

It was then that I discovered threading. Of course, I knew threading existed, but I used to only think of it as a way to run a single calculation on multiple cores. I didn’t think of it as a way to run two separate processes from one program. I used the C++11 standard library header <thread>. I started by using a thread to call my daemon. I learned that I could easily pass data around and start or stop the thread. I knew I finally found the solution I’ve been looking for. With this in mind, I was able to simplify the code so that my main process handles button presses and switching lights, and the thread’s only function is to scan for the Switchmate’s state.

Then I cleaned up my Python code so each file only has one function. I’m not thrilled that my project is a combination of two languages, but the Python was simple and it worked, so I didn’t feel the need to waste time to convert its logic into C++. The individual scripts are located in the switchmate folder. The files are:

  • scan.py: finds the MAC addresses of any Switchmates within BLE range
  • auth.py: generates an authentication code for the supplied MAC address
  • on.sh & off.sh: called from the main process to switch the Switchmate using…
  • switch.py: sends the on or off command to the Switchmate
  • status.py: reads the status bit from the Switchmates to determine if they are on or off (my code doesn’t use this script but it’s helpful for debugging)

One of the newest changes to the software is moving away from using RCSwitch to switch 433Mhz relays and instead using Philips Hue lightbulbs. I used the libcurl library to make the HTTP commands to the Philips Hue API. It works, but C++ is generally not a nice language to use when making API calls due to string concatenations and dealing with JSON (I still haven’t found an easy-to-use JSON library so I just look at a sub-string to check status). I’ve cloned the behavior of the relays to the Hue lights in my code but would like to extend the functionality in the future now that I have the ability to adjust brightness.

My main process generally follows the flow of Spark Lamp, but here’s a brief description of its functionality: It starts by creating a global thread (called scan_service) where the Switchmate Bluetooth LE scanning takes place. I made it global so that I can kill it anywhere and bring it back later. Then it jumps into main() and sets up pins and library for the buttons. It receives the default status of the lamps from the Hue API and receives the default status of the Switchmate from inside the thread. Next, we go into a loop using the clickButton code to determine which button was clicked how many times. The loop is also used to switch the lamps so they match the state of the Switchmate. There are many small functions which relate to switching some light based on some input pattern. One function of note is toggleLight(). We can’t call the Python code that toggles the Switchmate while the thread is running, so it first kills the scanner thread and waits for it to die, then toggles the light, and then starts the scanner thread again.

The switchmate.cpp file is the code that’s run in the thread. It is heavily based on this code example and I couldn’t have finished this project without that example. Bluetooth LE is complex and good documentation is sorely lacking. It’s one of the topics where you need to learn all about it before you can start to code — even if you have found good examples. My code starts at the bottom in scan_service(), where it brings up the BLE hardware. After starting, it receives broadcast packets in a loop and uses process_data() to set the status bit into switchState for the main process to read. Everything else in this file is to connect, communicate, and disconnect to Bluetooth devices.

Looking at the finished project, the functionality is simple, but it took a lot of pain to get to this point. There are some small quirks that I’d like to improve in the software like better logging and brightness control. I’d also like to add another physical remote to have better control over individual lights. As it stands, I’m happy with how the project turned out and find it magical when I walk into the bedroom and click on the lights and everything turns on simultaneously. It’s a great feeling knowing that I made this interaction happen.

--

--

Dillon Nichols
My Life as a Tinkerer

Electrical engineer: hardware/firmware; tinkerer; hobbyist; amateur fabricator;