Skip to main content

Turning a cheap 'police light' into an IoT device

If you've read my blog in the past you'll know I like to make ambient devices: mixtures of electronics and physical objects that blend into a home and provide a useful service. I have, for example, a model bus that shows the live times of buses near my home, and a Totoro that shows the weather forecast, and an old candle mug turned into a breathing nightlight using sea glass gathered on a beach.

The Totoro uses an ESP8266 in the form of a NodeMCU for a useful combination of WiFi connectivity, HTTP and GPIO for controlling physical devices like the LEDs in its eyes.

One of the challenges with working with these devices is updating the software on the NodeMCU when new functionality is implemented. Every code change has to be uploaded via a USB cable. For a new project I decided to make use of Cloudflare's Workers product to provide a simple API that tells an ambient device what to do.

By creating an API that just controls the physical aspects of the device (in this case a motor and an LED) I've offloaded the logic of when the motor or LED should be on or off to the Cloudflare Worker. It can be modified at will and the device has minimal code just to poll the API for updates.


Better living through microcontrollers

This project came about because I happened upon a cheap, rotating 'police light' in Flying Tiger which I bought on a whim thinking I'd find a use for it. It's very simple: there's a single switch that turns it on and off, a motor to rotate the internal reflector and a single LED. There's also a battery compartment for 3 AA batteries; that compartment turns out to be a good size to hide the NodeMCU in.

And 3 AA batteries is 4.5V which made me think I could get away with powering the entire device on 5V from an old wall wart. Thus was born a project to microcontroller-ize the light. In doing so I separated the LED and motor so they can be controlled separetly.

Although this project is small and a little silly it demonstrates something powerful: simple, maintainable code in the IoT device talking to a much more complex API implemented on Cloudflare's Workers which can be updated quickly. Also, the code in the Worker is totally secure, unlike the code running in the device which is at the mercy of its owner.

The demonstration project here uses a simple API that returns a state for the bluelight's motor ("on" or "off") and for the LED ("off", "steady", "flashing"). Because the LED is hooked up to the microcontroller it's possible to make it do things that are more interesting than being just on or off, hence I added a flashing mode.

The API response looks like this:

{ "motor": "on", "led": "flashing" }

The code on the NodeMCU polls the API once every 10 seconds and checks the result and sets the appropriate motor and LED states. All the logic concerning whether the motor should run or the LED be illuminated is in the Cloudflare Worker making it easy to update.

If you want to build something like this yourself... here's a description of what I did. It's not a total tutorial on how to transform the Flying Tiger lamp, so if you aren't familiar with this type of hardware hacking you'll do well to read this and some Adafruit tutorials. This might make a good first real project to get your hands dirty with some microcontroller fun.


Hacking the hardware

The first step is to dismantle the bluelight and cut a few wires. First, you remove the top blue portion by removing three screws and popping it off. Then hold the central black spindle in one hand and pull off the reflector (if you just pull on the reflector you'll find the entire internal mechanism falls apart... if you do this don't worry, it's fixable, just a nuisance).

Then remove the bottom four screw and take the cover off to reveal the internals. Here you are going to find a motor (with two wires), two wires disappearing up to the LED (one of those two wires has a resistor on it: that's because you can't just connect an LED to power otherwise the current will cause it to burn out).

First step is to snip the wires where they connect to the switch and the battery pack so you end up with two wires to the motor and two wires (plus resistor) to the LED. You can pop the switch out since you won't use it again (this is where I put my power socket).

In that picture you can see a capacitor soldered across the terminals of the motor. That's there to cut down on any noise generated by the motor and should be left in place. We'll be adding a diode across the motor on the circuit board as well for a circuit that eventually looks like this:

There are only three components added to the existing parts of the lamp: one resistor (R2, 330 ohms), one NPN transistor (Q1, 2N3904) and one diode (D1, IN4001). R1 and L1 are the existing LED and its current limiting resistor, M1 and C1 are the existing motor and its noise-suppressing capacitor. There are two connections to the NodeMCU labelled PORT which I connected to D8 and D7 to control them from code.

Turning the port connected to R2/Q1 sends power into the transistor Q1's base. Q1 turns on and power flows through M1/C1 to turn the motor and reflector. When the port goes low the motor stops. The diode D1 protects the transistor from a spike in current in the reverse direction that can occur when the motor comes to a halt.

One question that occurs with transistors is "How much current needs to pass across the transistor?". This is critical when choosing a suitable transistor. I happened to have some 2N3904's lying around and so I measured the current through the motor when running (roughly 70mA) and when stalled by holding the spindle with my finger (roughly 140mA). The 2n3904 data sheet says the continuous collector current is rated at 200mA for continuous operation. Should be OK. But if the motor was more power hungry a different transistor would be needed.

I built also this onto a piece of strip board with a power connector I'd scavenged from somewhere else.

Only four pins from the NodeMCU at actually soldered to the board: Vin, GND, D8 and D7. The whole contraption fits inside the battery compartment of the lamp (I removed the metal battery clips and snipped out some plastic).

The power wires and cables to the LED and motor fit through an existing hole in the plastic.

Then it's just a simple matter of putting the cover back on and uploading code. 


The code

As I mentioned above I've implemented fairly simple code on the NodeMCU that polls an API every ten seconds. The API is implemented as a Cloudflare Worker and any logic about when the LED should be lit, or when the motor should turn can be implemented and updated quickly in the Worker. For test purposes there's a very simple Worker in the source code repository that turns the LED on at 0743, makes it start flashing at 0744 and turns the motor on at 0745, at 0746 everything turns off again. The idea is that this warns me to leave the house to get to the bus.

Of course, any amount of much more complex logic could be implemented in the Worker. And all without ever having to touch the code embedded in the NodeMCU.

To get started you'll need to build the NodeMCU's firmware. I always use this site because it makes it easy to get a custom firmware build. For this project I built with the following modules: file, gpio, http, net, node, pwm, sjson, tmr, uart, wifi, tls.

Once you have the firmware I use esptool to flash the hardware like this: --port /dev/cu.SLAB_USBtoUART write_flash -fm qio 0x00000 nodemcu-master-11-modules-2018-11-30-12-31-22-integer.bin

Note that I used the integer build since I don't need floating point arithmetic. On the firmware is flashed it's then possible to connect to the NodeMCU using a terminal program (I use and upload Lua code. The Makefile in the repo has commands for uploading and connecting to the device.

If you reuse my code you'll need to edit bluelight-config.lua for your WiFi network and for the API endpoint you've set up. Note that I'm using a really simple authentication scheme for the API (a shared secret in a parameter), but you could implement something much more robust for a real device.

Once the code is uploaded and the device connects to WiFi it starts hitting the API end point every ten seconds. Just modified worker.js to implement whatever logic you want and start controlling a cheap blue 'police light' from the Internet.

The beauty of this is that once the code is running on the lamp any logic changes can be done in the Worker without having to go through the somewhat laborious code uploading process for the NodeMCU. It also means that once installed somewhere there's no need to do maintenance on the device itself, just change the code running the API to make the lamp do different things.

Since the Worker can itself make calls to other APIs it's just a SMOP (Simple Matter of Programming) to make the lamp turn on when it's about to rain, your sports team is winning, the bus is about to arrive, ...


Unknown said…
>>One of the challenges with working with these devices is updating the software on the NodeMCU when new functionality is implemented. Every code change has to be uploaded via a USB cable.

Have a look at ESPHome. You can do over the air updates.

Popular posts from this blog

Your last name contains invalid characters

My last name is "Graham-Cumming". But here's a typical form response when I enter it: Does the web site have any idea how rude it is to claim that my last name contains invalid characters? Clearly not. What they actually meant is: our web site will not accept that hyphen in your last name. But do they say that? No, of course not. They decide to shove in my face the claim that there's something wrong with my name. There's nothing wrong with my name, just as there's nothing wrong with someone whose first name is Jean-Marie, or someone whose last name is O'Reilly. What is wrong is that way this is being handled. If the system can't cope with non-letters and spaces it needs to say that. How about the following error message: Our system is unable to process last names that contain non-letters, please replace them with spaces. Don't blame me for having a last name that your system doesn't like, whose fault is that? Saying "Your

How to write a successful blog post

First, a quick clarification of 'successful'. In this instance, I mean a blog post that receives a large number of page views. For my, little blog the most successful post ever got almost 57,000 page views. Not a lot by some other standards, but I was pretty happy about it. Looking at the top 10 blog posts (by page views) on my site, I've tried to distill some wisdom about what made them successful. Your blog posting mileage may vary. 1. Avoid using the passive voice The Microsoft Word grammar checker has probably been telling you this for years, but the passive voice excludes the people involved in your blog post. And that includes you, the author, and the reader. By using personal pronouns like I, you and we, you will include the reader in your blog post. When I first started this blog I avoid using "I" because I thought I was being narcissistic. But we all like to read about other people, people help anchor a story in reality. Without people your bl

The Elevator Button Problem

User interface design is hard. It's hard because people perceive apparently simple things very differently. For example, take a look at this interface to an elevator: From flickr Now imagine the following situation. You are on the third floor of this building and you wish to go to the tenth. The elevator is on the fifth floor and there's an indicator that tells you where it is. Which button do you press? Most people probably say: "press up" since they want to go up. Not long ago I watched someone do the opposite and questioned them about their behavior. They said: "well the elevator is on the fifth floor and I am on the third, so I want it to come down to me". Much can be learnt about the design of user interfaces by considering this, apparently, simple interface. If you think about the elevator button problem you'll find that something so simple has hidden depths. How do people learn about elevator calling? What's the right amount of