Creating an utterly useless "smart" power plug

2017-04-02 19:07:15

Ever wanted to get a notification when you plug in your favorite power plug?
Let your friends know about it, and tell the whole world along the way?

Ever wished your favorite power plug would inform you about getting unplugged?
Ever imagined someone could even have a favorite power plug?

If you actually answered a "yes" somewhere along the way, well you're in for a treat, you magnificent visionary. All this - and so much more - is possible with the utterly useless "smart" plug.

What it is and does

Okay, so, this is actually a real thing. I built a power plug around an ESP8266 that will notice whenever you plug it in and out the power socket. The ESP8266 firmware can be compiled to either use a third party service (ThingSpeak), or inform a homebrew backend about the plug state change. More on that later. First, a picture:

Assembled power plug

And to proof this is not just some power plug enclosure, here is what it looks like once you open it and take it a bit apart:

Power plug opened

What you see here is almost all there is to see:

  • on the top, a small LiPo battery and a partially covered LiPo charging circuit
  • in the center, an ESP-01 module and a 3.3V DC-DC regulator (mostly hidden under the ESP)
  • on the bottom, all the rest (MOSFET as power supply switch, BJTs as logic switches and some resistors to accompany all that)

Only thing not shown is the 230V to 5V switching power supply, connecting the LiPo battery charging circuit to the mains power. That one is hot glued to the back of the board, along with a wild mess of wires.

Power plug opened from the backside

Before moving on, it's probably a good idea to refer to the Smartplug's GitHub repository where you'll also find its schematic.

The Hardware

So the plan is to have both states - plugged and unplugged - detected. The first one is easy, once it powers up, it must be plugged in. The latter part is a different story entirely. I could have built a system that pings the plug periodically to see if it's still answering - or have the plug send a heartbeat message. But that didn't seem right.

I've been looking for a reason to play around with LiPo batteries and their charging circuits for a while already, and this seemed like the perfect opportunity now. Once the main power is removed, the battery would (hopefully seamlessly) take over to keep the system up and running. A first check verified: yes, it does.

Detecting the power supply removal was the next step, which means pretty much just connecting the 5V side of the switching power supply through a voltage divider to an ESP8266 GPIO and enable an interrupt on the pin. I went for a 2k2 and 3k3 resistor, resulting in theoretical 3.0V at the GPIO. That's high enough to be detected as logic one, and low enough to do no harm.

Speaking of proper voltages, the ESP supply voltage should be between 3.0 and 3.6V, with 3.3V being a common value. However, the LiPo charging circuit and battery provide either the charging voltage (4.2V) or the battery's own output voltage. Regulating it with a LD1117 wouldn't work due to the regulator's 1V drop-out voltage. So I looked into DC-DC regulators and found one that regulates anything from 3.0-5.5V to 3.3V.

But wait! How to power the whole system down now? Once the power supply removal is detected, the battery jumps in and even if the ESP8266 would power itself down somehow / go to sleep, the regulator wouldn't. This is not a satisfying solution, I'd want it to completely shut down eventually.

Hello darkness, my old friend...

My mind got occupied with thoughts of suicide. Regarding the smartplug, of course.

I needed some sort of kill switch that would cut the power between the battery and the DC-DC regulator. It quickly became clear, this is a different story than switching a LED on and off with a transistor, and I ended up in p-channel MOSFET territory and the concept of "high side switching". But before the MOSFET could kill anything, it first needs to make it alive.

So, whenever the power supply is available (i.e. the plug is plugged in), it should switch the MOSFET (Q1 in the schematic) to connect the ESP to its supply voltage. Once powered on, the ESP should then use a GPIO pin to keep the gate voltage as needed to keep the MOSFET switched once the power supply is removed. After that, the ESP holds its own fate in its hand pin. This is practically a simple OR gate, which can be achieved using two simple NPN transistors.

Building an actual power plug

Up until now, everything was put together on a breadboard using a nice ESP8266 development board with lots of pins broken out. This was very convenient to have enough GPIOs to detect the supply voltage, switch the MOSFET, and keep the ESP's boot mode state detection pins unrelated to all of it. (ESP8266 can have different boot modes like loading the firmware from the flash memory or a UART connection)

Smartplug on a breadboard

While that board is great for breadboarding, it gets a but inconvenient for the power plug case I got - both for space and pin assignment reasons. I also preferred to keep those boards available for future breadboard prototyping, and use an ESP-01 module instead (which doesn't really work too well on a breadboard and additionally has a much lower price tag).

Hmm, well, the ESP-01 has only two GPIO pins available on its breakout headers, which are of course GPIO0 and GPIO2, the pins the boot mode state is determined from.

The good news is, these pins are only used when powering up the ESP, once the boot mode is determined and execution starts, the pins can be used as regular GPIO pins. And as mentioned before, two pins is all we need: one to detect the supply voltage, one to switch the MOSFET. But this means the hardware must be designed to ensure the correct default levels of those two GPIO pins during power up. I decided to program the ESP only externally and never inside this hardware arrangement, so I didn't have to worry about changing the boot modes. So it will always have the "boot from flash" mode, meaning both GPIO0 and GPIO2 must be high level on power up.

So, GPIO0 is used for main power supply detection, that means it's anyway high level when power is applied, so that's taken care of. For GPIO2 I took one more NPN transistor (Q4 in the schematic) with

  • Base connected to the main power supply
  • Collector connected to the ESP supply voltage (i.e. the DC-DC regulator output)
  • Emitter connected to GPIO2

Meaning, when powered up, the transistor will switch the ESP's supply voltage to its GPIO2 pin, and both GPIO pins will have high level. All good this way.

And that concludes the smartplug's hardware. For another summary, have a look at the hardware section on GitHub. Now let's have the hardware actually do something.

ESP8266 firmware

There's not too much happening in the smartplug firmware, it looks more complex than it actually is. Its main operation is

  • Set GPIO0 as input and set up its interrupt handler
  • Set GPIO2 as output
  • Set up debounce timer for GPIO0 (more on that in a moment)
  • Set up WiFi
  • Send "plug plugged in" state

After that, it just waits for the interrupt from GPIO0 when the plug gets unplugged and the power supply detection pin goes low. The signal will most likely bounce a bit though, causing more than one interrupt. So instead of handling the signal change right away, so just mentioned debounce timer is used to wait for the signal to settle.

Every time the GPIO interrupt happens, the timer is reset (without executing its own handler) and restarted for 200ms. To reset the timer on any signal transition, the interrupt itself is set up for both rising and falling edge.

Once the timer actually does expire and triggers its own interrupt handler, it can be assumed the signal is not bouncing anymore (since there was no GPIO change that would have reset the timer), and the level on the pin is reliable.

Simplified, it works somewhat like this:

uint8_t debounce_timer_armed;

void gpio_interrupt(void *args) {
    if (debounce_timer_armed) {
        debounce_timer_armed = 0;
    }
    setup_timer_for_200ms();
    debounce_timer_armed = 1;
}

void timer_interrupt(void *args) {
    debounce_timer_armed = 0;
    handle_gpio_state();
}

The full implementation can be found in the firmware's main.c file.

Once the GPIO state is successfully sent, GPIO2 is set low, thus cutting its power supply by switching the MOSFET off. And to be on the safe side, a fallback timer is started to forcefully cut the power after 15 seconds in case the communication fails. That is mainly all there is.

Of course, once the GPIO state is determined, the ESP8266 has to inform someone somewhere about it. Initially I just wanted the thing to tweet when it's plugged in and out, nothing else. Communicating directly with the Twitter API in the ESP firmware seemed a bit much though at that point, so I looked into third party alternatives. I discovered ThingSpeak and figured that'll do. Not like I'm actually going to use that smartplug for anything serious.

But yeah, eventually I changed my mind and didn't want to settle with that. But I already implemented the communication with ThingSpeak in the firmware, and I didn't want to discard it either. So I added a configuration option use either ThingSpeak as backend system, or use a self hosted backend system. Details on the configuration itself is described in the firmware GitHub section.

A Python backend

So.. yes. This comes also with a backend system. Once again, there's additional information on GitHub.

Well, "backend system" is maybe a bit exaggerated, it's just a little python script built around the Bottle web framework, Autobahn WebSockets and Mike Taylor's Python Twitter API wrapper. So far I could refrain from adding persistent storage - and probably more importantly, from deploying it to the wild.

Now, what it does, is running a web server through Bottle, doing basically nothing but accepting one type of POST request for JSON data containing a smartplug's identifier and its power plug state. Since there's only one, single compatible smartplug in this world, adding the identifier feels a bit pointless. But without it, I might as well send nothing but a single 0 or 1, and that just looked a bit sad. Plus, it's 2017, you can't just send small, efficient values.

Anyway, other clients can connect to the system via WebSockets, and the backend will pass the received state on to those connected clients. Using the Twitter API wrapper is optional, but if enabled, it will additionally tweet the latest smartplug state.

Time for a little testing. Either with the actual hardware, or for starters, with curl:

curl -XPOST -v -H "Content-type: application/json" -d '{"id": "0xb00bf0cc", "value": 0}' http://localhost:9000/data

To which the backend will print out something like this:

Running server backend screenshot

To enable tweeting, some Twitter account needs to be linked to it and access tokens need to be generated for it and added to the Python script. Follow the backend GitHub section for further information.

So that's that. Now, the smartplug is ready to know when it's plugged in to and out of the power socket, notify the backend system, and tweet about it, if wanted.

But wait, there is more!

Presenting PlugBuddy, the Android companion app.

Yeah, I know, this is getting ridiculous. Tell me about taking a joke too far. But hey, the backend has WebSockets for a reason, and you gotta have some app with that IoTness.

Nothing too wild happening here though. Again, Autobahn WebSockets are used to listen for status changes sent by the Python backend. Other than that, an icon is showing the current state, and a system notification is displayed once the status changes.

The active app will look like this while having the plug unplugged.. PlugBuddy app with unplugged smartplug

..and like this after plugging it into a socket. PlugBuddy app with plugged in smartplug

As for the notification, this is after plugging it in.. PlugBuddy notification with plugged in smartplug

..and once it got unplugged again. PlugBuddy notification with unplugged smartplug

To make it easier to use, there's also a simple configuration dialog to set up the WebSocket server address and port, plus a specific smartplug identifier to listen to. Again, this is in a way pointless since this is a one of a kind thing at this point.

Conclusion

Yeah, this probably went too far to be a simple joke project, but I learned quite a few new things while refreshing some old knowledge. And while the system on the whole might have no real use, its single components (or parts of them) might.

Some side notes

The status change / notification is more or less real-time, but there's always going to be a delay. This is caused by the ESP8266 firmware, for example when plugging in the plug, it takes a moment to scan and connect the WiFi and receive an IP.

Also, obviously, all components, i.e. the ESP8266, the backend and the Android device, need to be in the same network to make this work. In theory this should work over the internet, but I wouldn't recommend that. Not like I believe anyone is actually going to rebuild this. If you do, oh please let me know, I'd love to be your plug buddy.

Categories

Tags