ESP8266: part III
And we're back with the ESP8266! Now it's time to finally present the project and my results with it, both in its original form and the upgrades.
And we're back with the ESP8266! Now it's time to finally present the project and my results with it, both in its original form and the upgrades.
The victim
(Source: http://www.ikea.com/de/de/catalog/products/40192361/)
While I was Berlin, I got my hands on an Ikea Dioder, which is nothing more than some LED bars attached to a controller and a power supply. This controller allows you to select some color modes or a fixed color for the LEDs. Turning it on and off is also done directly on it. One thing I missed was being able to set the brightness on it, but that was ok.
Anyway, as someone that always wants to break stuff apart, I decided to open it and take a look at how it work: it's just a microcontroller (as expected), some big transistors and random electronics to connect everything together. You can find more information about how it works and even how to reprogram it (it's a PIC micro, btw) online. My idea, however, was to hack that and allow me to control it remotely. Also, one big requirement on my project: I want to keep the original microcontroller and circuit, so that, if one day I decide to throw the hack away, I can still use the device. That essentially means no part replacements and no reprogramming.
The story of a hack
While I was still in Berlin I decided to modify this so I could remotely control it. Since the idea was to keep the device mostly intact, I decided to look at the circuit and trace everything that was relevant to me: the +12v and +5v rails, the ground and all colors (red, green and blue) lines. By carefully (sure) studying examples and other hacks online, I soon realized that my idea wasn't hard at all, and that others have managed to control the colors by applying 5v to the appropriate traces and the brightness by using PWM. I quickly tested it on my board, and, of course, it worked as expected.
I was amazed by this, but I couldn't really control it remotely: there was no code for that yet. I didn't have any other mean of doing that at the time except by attaching it to my computer, but since they weren't really close to each other, I've decided to connect the Arduino to a Raspberry Pi. Then that one would be later connected to my network through a very long network cable, since somehow wireless was crappy inside my room and I also used to stream TV. By adding some scripts on the Pi and coding some small applications on C# for the desktop, I was able to have both a desktop-based interface and a mobile one. I could control the color and brightness by using the Raspberry Pi for processing the data before sending stuff to the serial line, where it would then be read by the Arduino and processed (aka analog/PWM values written to the correct pins).
Soon I began to realize this wasn't great: I was using some power from my Raspberry Pi (not much, but still power) only for changing LEDs. This felt like using my i7 machine to run DOS. This is wrong. So I bought an Ethernet Shield for my Arduino and decided to code everything straight on it. After some try-and-error I managed to make it work, and it was pretty good. The performance was even better than compared to the one I had with the Pi: it was responding way faster to the extreme color changing scripts I had. But that introduced another issue: power. You see, the Ikea Dioder has a 5V regulator inside of it to provide power to its own microcontroller. I could tap on that line for my Arduino, and it work, but with the Ethernet shield I couldn't make it boot: the current draw was way too high and it would drop the voltage below the minimum value for the ATmega328. So essentially the board was kinda stuck on a reboot-loop, since it voltage would always drop too much when the AVR was booting. At the time I tried adding a capacitor, but I didn't have a big one to see if I could hold the voltage high enough. Actually, I remember adding something like 6 caps to the circuit, but it would still be not enough. Solution? I had a crappy phone charger a friend gave me, so I used it to provide 5V to the Arduino. With all grounds connected to each other, the circuit worked like a charm. Downsides? Having two power sockets in use for LEDs. Meh :-/
Here's how it looked like without the Arduino attached to it:
Those extra wires have probably killed the warranty!
That defintely killed the warranty!
Something new
While still in Berlin I was always thinking about moving this project to something wireless. The network cable was heavier than my setup, so you can imagine the issues with trying to make this stick to a wall. Also, having a gray bulky wire coming out of that was kinda distracting and ugly. Then the ESP8266 came, but I didn't buy it until I was in Brazil (that was a mistake, I know).
A few days I decided to continue this project, so I got the Dioder out of a box and went straight to the power socket to power it up. And then I saw one kinda big issue:
Right on the third line of the power supply specifications, you can see this: Prim.: 220-240 Vac, 50 Hz. First of all, here in Porto Alegre we have 127V and not 220V. Although I can get a socket with 220V more or less easily, in theory it wouldn't work, or at least not properly. You see, this thing was designed to run in Europe (or at least in Germany), which means it was designed to work at 50 Hz. But unfortunately we have 60 Hz. Well, that sucks. But fear not, my friend! Look at the fourth line: Sec.: 12 Vdc, Max 0,42A, 0,5-5W. So this is essentially what, a 12V 500mA power supply? Please, I have tons of those. So all I need to do is strip this out of the thing and add my own custom power supply to it, right? Right. Let's take a look at the power distribution hub:
See those beefy cables in the middle? Those are 12V wires, even though they're kinda huge for that. So all I need to do is replace those wires with something I can hook up to a custom power supply later. Well, that was easy, nothing a soldering iron couldn't do in 5 minutes. And it obviously work as expected, no issues whatsover. Finally, one problem solved!
So now we want to hack this to work with the ESP8266. Remember when I said it has 3.3v I/O lines? Well, consider now that the original Dioder was designed to work with 5V lines. You see the issue? Yes, it's not enough voltage. The PWM will be all messed up! Shit!
For the last whole I was thinking on how to fix this. I asked friends, I designed circuits, I looked tons of datasheets online, checked up some prices, everything. I did the whole thing. I even have the circuit on my mind already. Remember, this is not a 3.3 to 5v logic converter, this is a PWM converter, so it really needs to be 5v and not 4.5v as most people do. Man...
Turns out nothing like this is necessary. I did a quick test: I fired up the ESP8266, set all colors to the max and connected the red wire from the Dioder to the circuit. Boom, it worked, as expected. But then I also connected it to the 5v rail, so I could see a difference in brightness (after all, 3.3 is less than 5). Nothing. No difference at all. It seems the circuit is designed to work with lower voltages too! Well that's awesome! That means I don't have to design a circuit to shift from 3.3 to 5v - I can provide the I/O straight from the ESP without problems! Nice!
I connected everything and did this:
$ curl "http://172.16.1.154/set?red=1023&blue=1023&green=1023"
Ok.
$
Oh yeah! Full brightness, everything worked like a charm! This worked way better than I expected! Here, have a picture of my whole setup:
Is it ugly? YES! Does it work? YES!
To err is human
A while ago I said the ESP8266 was perfectly able to handle HTTP requests. And yes, it is, but not fast enough. I tried flooding it to make some kind of color loop and it went crazy. The parsing was taking so much CPU it would either crash or drop from the network. That sucks.
So I decided to go back and change to the UDP method again. Why? Because I know that UDP packets can be handled extremely fast, even over wireless. I also have old applications and scripts that can now work again since they were designed to handle the LEDs over UDP. So good-bye HTTP, hello UDP! Here's the code:
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <WiFiUdp.h>
// RGB output pins
#define PIN_R 0
#define PIN_G 2
#define PIN_B 3
// UDP configuration
#define UDP_PORT 5000
// WiFi configuration
const char* ssid = "Not really my WiFi";
const char* pass = "iabadabadu";
// WiFi UDP object and packet buffer.
WiFiUDP udp;
byte packetBuffer[UDP_TX_PACKET_MAX_SIZE];
// Initial LED state.
unsigned int ledStatus[] = { 0, 0, 0 };
void setup() {
pinMode(BUILTIN_LED, OUTPUT);
digitalWrite(BUILTIN_LED, LOW);
pinMode(PIN_R, OUTPUT);
pinMode(PIN_G, OUTPUT);
pinMode(PIN_B, OUTPUT);
// Let's connect.
WiFi.begin(ssid, pass);
// Blink while not connected :-)
while (WiFi.status() != WL_CONNECTED) {
delay(250);
digitalWrite(BUILTIN_LED, !digitalRead(BUILTIN_LED));
}
// We are online (low on built-in LED means ON).
digitalWrite(BUILTIN_LED, LOW);
// Reset all colors.
analogWrite(PIN_R, 0);
analogWrite(PIN_G, 0);
analogWrite(PIN_B, 0);
// Start the UDP server.
udp.begin(UDP_PORT);
}
void loop() {
int packetSize = udp.parsePacket();
// We got something.
if (packetSize) {
// Read the packet.
udp.read(packetBuffer,UDP_TX_PACKET_MAX_SIZE);
// Is this a read or write?
if ((packetSize == 1) && (packetBuffer[0] == '?')) {
// Packet: read.
// Reads the status.
udp.beginPacket(udp.remoteIP(), UDP_PORT);
udp.write((const unsigned char*)ledStatus, 3);
udp.endPacket();
} else if (packetSize == 3) {
// Packet: write.
// Convert the colors from the 0-255 range to the 0-PWM_RANGE.
unsigned int red = (PWMRANGE * packetBuffer[0])/255;
unsigned int green = (PWMRANGE * packetBuffer[1])/255;
unsigned int blue = (PWMRANGE * packetBuffer[2])/255;
analogWrite(PIN_R, red);
analogWrite(PIN_G, green);
analogWrite(PIN_B, blue);
// Saves the status.
ledStatus[0] = red;
ledStatus[1] = green;
ledStatus[2] = blue;
}
}
}
There are a few differences in this code, besides not having as many comments as the last one. The biggest one is that I now save the status of the LEDs in case I get a package querying for the data. I could, however, read the analog values, but for some weird reason at that time I decided to not do it like this. I'll eventually try it with the ESP8266, and, if it works, stick with it.
The second biggest change is the math for setting the values on the pins. You see, the legacy code was designed to send RGB values: one byte for each color. That means each color is going from 0 to 255. However, as I previously noted, the ESP8266 has, by default, a different PWM range (0 to 1023). Since I didn't want to deal with changing the range right now and I'm way to lazy to change all my applications, I just added some math to convert from one range to another. I also used the PWMRANGE
to make it easier to remember what the hell is that code doing in the future. Trust me, it works. At least with me it works :-)
Not good enough
Remember when I said that I was using a second power supply only to power the Arduino because of the Ethernet Shield? Well, the ESP8266 also takes some juice when it's booting, so I'm also using an external supply here. But do I really need?
I started playing with the 5v rail I had from the Dioder. No, it's not possible to get power from there. The rail doesn't handle that much current well and drops the voltage too. But here in Brazil I have some old bigger caps, so I added one to the circuit: 6.3v 1500uF. Oh yeah, that's some juice. I can add that cap both the 5v and 3.3v rail: one is before and one is after the regulator. In both cases, it works: the capacitor is able to handle the voltage drop and keep it booting until the current draw drops a bit. That means I don't need the external power supply anymore. Nice!
I have, however, one quite big issue: the 5v regulator is pretty small and probably wasn't designed to handle that much current. It gets hot. Quite hot. Not something like burning your finger, but it gets hot enough so that you can't really touch it forever. Well, that can become an issue, since, as you may have imagined, that's dangerous (hopefully for the regulator only and not my house).
There's, however, an alternative: connecting my 3.3v regulator to the 12v rail. I would still need the cap though for some reason, but it would be only on the 3.3v side (since the cap is up to 6.3v only). I tried and it works, but my regulator gets way too hot. It really doesn't like the 12v, even though it was designed to work up to 35v (and the graph on the datasheet shows up to 10v - ha, funny). I looked all over the place but unfortunately I couldn't find a small heatsink to add to it, so it would dissipate all that heat easier. Meh, screw it, let's burn the 5v regulator and not my 3.3v one. After all, if the 5v rail dies, I can still regulate from the 12v one!
Making it pretty
I have no idea how I'll close this thing. Seriously. How can I fit the ESP8266, required components, regulator and wiring all inside that little case?! I need a bigger one. I, however, don't have it, so it's gonna take a while until I can actually install it somewhere in my place without my cat destroying the wiring and probably killing something in the middle (hopefully not himself).
Next time I'll see if I can discuss the interfaces (desktop and web).
Oh, for the money shot, I took a picture of the RGB LED showing its tiny colored ones inside. That's pretty:
(Original resolution here)
Cya!