Internet of Bling
Thu, 03 Dec 2020 22:56:27 +0000
Advent of Blog, day 3: MQTT wifi Christmas lights
I had a Neopixel strip in my electronics bits box, and wanted to make some needlessly complicated Christmas lights.
- neopixel strip
- high current 5v power supply
- a Wemos D1 ESP8266 board
- a big capacitor
- a small resistor
- a 74HCT04
- a breadboard and some jumper wires to connect it all together
Wiring a Neopixel strip to an Arduino is well-trodden territory . The capacitor smooths out the power, the resistor is in series with the first LED in the strip, and some digital output pin on the Arduino connects to the resistor.
However: this works only for 5v devices. The Wemos D1, although behaving for many purposes like an Arduino, is a 3.3v device. So, we need something to switch it up. The Adafruit page says "you must use a logic level shifter such as a 74AHCT125 or 74HCT245." but Bitsbox didn't have any, so improvisation was needed.
After some web searching to actually understand what I was sticking together, this is what I found: the 74 series of ICs are logic gates (the AND/OR/NAND/NOT etc that we learned about in secondary school electronics lessons). 74LS are TTL chips (so, power-hungry old technology), 74HC are CMOS, and 74HCT is a special version of 74HC with 74LS TTL-compatible inputs. Why is this interesting? TTL says that logic 0 is 0-0.8V, and logic 1 is 2-5.5V, so the 3.3V emitted by the D1 is nicely inside that range. CMOS says that the output voltage for a logic 1 is the supply voltage. So, all we have to do is implement the identity function using some 74HCT-family chip, and we will get a 3.3v to 5v convertor "for free". The simplest possible way to do this with components that were in stock seemed to be to get a 74HCT04 - also known as "six NOT gates in one IC" and wire two of them in series. So, the MCU's digital output pin connects to a NOT gate, which connects to another NOT gate, which connects to the resistor, which connects to the strip.
That's the hardware, pretty much.
For the software I wanted to use MQTT, mostly because I have used it before and it's low-effort in the Arduino environment. One thing to be aware of here is that the Arduino MQTT library may have a payload size limit of 128 bytes (some sources say 256), which is clearly not enough to send individual 8 bit RGB values for each of 150 LEDs in the strip, so some kind of encoding/trade-off is needed. I settled on a format that allows a small number of pixels to be defined precisely in HSV format, and has the MCU interpolate the values of all the intervening pixels. A cheap and cheesy Ruby script to send the packets looks like this:
def send_strip(speed, positions) IO.popen("mosquitto_pub -h hostname.lan -d --stdin-file --username username --pw password -t sensors/neoxmas", "w") do |io| array = [speed] + positions io.print array.pack("C*") end end
send_strip(40, # animation speed [ # pixel offset, hue (0-255), saturation (0-255), value (0-255) 0,9,120,160, 74, 49, 120, 60, 145, 49, 120, 60, ])
and the Arduino sketch to receive them and make the pixels light up is in this gist here
I know I said there would probably be no Nix in this entry, but I can't help myself. Nixpkgs support for Arduino includes only the stuff for actual Arduinos, not for other architectures that also use the IDE. To get something that will also work for ESP8266 and the like, you can do
nix-shell https://github.com/nix-community/nix-environments/archive/master.tar.gz -A arduino
It's not very declarative, but nothing I build on a breadboard is reproducible anyway so ...
In unrelated news, I am deeply indebted to leo homestuck who kindly pointed out after reading yesterday's entry that I should have been more careful writing my regex in the sniproxy config. I should have been and now I have been and I will update the blog entry to reflect this, when it's not two minutes until tomorrow.