Prusa Lack Stack, LED Lighting, CircuitPython Tweaks

Much like those recipes on the internet where the author tells you their life story or inspiration, I’ve got a lot to share before I get to the punchline of this blog post (a bunch of CircuitPython tweaks).  Edit:  On second thought:

  • Keep the lines of code <250
  • Try using mpy-cross.exe to compress the *.py to a *.mpy file

This is a bit of a winding road, so buckle up.

Admission time – I bought a Prusa1 about three years ago, but never powered it on until about a month ago.  It was just classic analysis paralysis / procrastineering.  I wanted to set up the Prusa Lack enclosure – but most of the parts couldn’t be printed on my MonoPrice Mini Delta, which meant I had to set up the Prusa first and find a place to set it up.  But, I also wanted to install the Pi Zero W upgrade so I could connect to it wirelessly – but there was a Pi shortage and it was hard to find the little headers too.  Plus, that also meant printing a new plate to go over where the Pi Zero was installed, a plate that I could only print on the Prusa, but I didn’t have a place to set it up…

ANYHOW, we’ve since moved, I set up the Prusa (without the Pi Zero installed yet), printed a Prusa Lack stack connector to house/organize my printers.  Unlike the official version, I didn’t have to drill any pilot holes or screw anything into the legs of the Lack tables.

Once the Lack tables were put together, I set about putting in some addressable LEDs off Amazon. I found a strip that had the voltage (5V for USB power), density (60 LED’s per meter), and the length (5 meters) I wanted at a pretty good price <$14, shipped.  I did find one LED with a badly soldered SMD component which caused a problem, but I cut the strip to either side of the it, then soldered it back together.  Faster and less wasteful than a return at the cost of a single pixel and bit of solder.

The Lack stack is three tables tall, keeps extra filament under the bottom of the first table, my trusty Brother laser printer on top of the first table, my trusty Monoprice Mini Delta (Roberto) on top of the second table, and the Prusa (as yet unnamed Futurama robot reference… Crushinator?) on top.  Since I don’t need to illuminate the laser printer, I didn’t run any LED’s above it.  I did run a bunch of LED’s around the bottom of the third printer…  this is difficult to explain, so I should just show a picture.

When Adafruit launched their QtPy board about four years ago, I picked up several of them.  I found CircuitPython was a million times easier for me to code than Adafruit, not least of which because it meant I didn’t have to compile, upload, then run – I could just hit “save” in Mu and see whether the code worked.  I also started buying their 2MB flash chips solder onto the backs of the QtPy’s to a ton of extra space.  Whenever I put a QtPy into a project, I would just buy another one (or two) to replace them.  There’s one in my Cloud-E robot and my wife’s octopus robot.  Now, there’s one powering the LED’s in my Lack Stack too.

I soldered headers and the 2MB chip into one of the QtPy’s, which now basically lives in a breadboard so I can experiment with it before I commit those changes to a final project.  After I got some decent code to animate the 300 or so pixels, I soldered an LED connector directly into a brand new QtPy and uploaded the code – and it worked!

Or, so I thought.  The code ran – which is good.  But, it ran slowly, really slowly – which was bad.  The extra flash memory shouldn’t have impacted the little MCU’s processor or the onboard RAM – just given it more space to store files.  The only other difference I could think of was that the QtPy + SOIC chip required a different bootloader from the stock QtPy bootloader to recognize the chip.  I tried flashing the alternate “Haxpress” bootloader to the new QtPy, but that didn’t help either.  Having exhausted my limited abilities, I turned to the Adafruit discord.

I’ll save you from my blind thrashing about and cut to the chase:

  • Two very kind people, Neradoc and anecdata, figured out the reason the unmodified QtPy was running slower was because the QtPy + 2MB chip running Haxpress “puts the CIRCUITPY drive onto the flash chip, freeing a lot of space in the internal flash to put more things.”
    • This bit of code shows how to test how quickly the QtPy was able to update the LED strip.
      • from supervisor import ticks_ms
      • t0 = ticks_ms()
      • pixels.fill(0xFF0000)
      • t1 = ticks_ms()
      • print(t1 – t0, “ms”)
    • It turns out the stock QtPy needed 192ms to update 300 LED’s.  This doesn’t seem like a lot, until you realize that’s 1/5th of a second, or 5 frames a second.  For animation to appear fluid, you need at least 24 frames per second.  If you watched a cartoon at 5 frames per second, it would look incredibly choppy.
    • The Haxpress QtPy with the 2MB chip could update 300 LED’s at just 2ms or 500 frames per second.  This was more than enough for an incredibly fluid looking animation.
    • Solution 1:  Just solder in my last 2MB chip.  Adafruit has been out of these chips for several months now.  My guess is they’re going to come out with a new version of the QtPy which has a lot more space on board.
      • Even so, I’ve got several QtPy’s and they could all use the speed/space boost.  I’m not great at reading/interpreting a component’s data sheet, but using the one on Adafruit, it looks like these on Digikey would be a good match.
  • The second item was a kept running into a “memory allocation” error while writing animations for these LED’s.  This seemed pretty strange since just adding a single very innocuous line of code could send the QtPy into “memory allocation” errors.
    • Then I remembered that there’s a limit of about 250 lines of code.  Just removing vestigial code and removing some comments helped tremendously.
    • The next thing that I could do would be to compress some of the animations from python (*.py) code into *.mpy files which use less memory.  I found a copy of the necessary compression/compiler program on my computer (mpy-cross.exe), but it appeared to be out of date.  I didn’t save the location where I found the file, so I had to search for it all over again.  Only after giving up and moving on to search for “how many lines of code for circuitpython on a microcontroller” did I find the location again by accident..  Adafruit, of course.  :)
    • I’m pretty confident I will need to find the link to the latest mpy-cross.exe again in the future.  On that day, when I google for a solution I’ve already solved, I hope this post is the first result.  :)

The animations for the Lack table are coming along.  I’ve got a nice “pulse” going, a rainbow pattern, color chases, color wipes, and a “matrix rain” / sparkle effect that mostly works.

Animated GIF

I started this blog post roughly 7 months ago2 by the time I finally hit publish.  After all that fuss, ended up switching from CircuitPython (which I find easy to read, write, maintain, update) to Arduino because it was able to hold more code and run more animations.  Besides the pulse animations, rainbow patterns, color chases, color wipes, and a matrix rain, it’s also got this halo animation, some Nyan cat inspired chases, and plays the animations at a lower brightness for 12 hours a day (which is intended to be less harsh at night).  I could probably add a light sensor, but I don’t really want to take everything apart to add one component.

  1. The i3 MK3S+! []
  2. January 7, 2025 []

Companion Robots and Maker Faire Season!

I’m super excited for Maker Faire Bay Area / Mare Island and Mini Maker Faire Rocklin.1  I’m not just excited to see everything, but to show all the things I’ve been working on for a while now.  It’s also time to pick up all the little dev boards I’ve somehow accumulated and see if I can make anything with them to show off.

  1. Project Boards
    1. Wemos D1 Mini.  A small insanely cheap (~$3?!) WiFi enabled dev board2 , which has 4MB onboard and can run Arduino.  I think it can also run MicroPython, but I haven’t tested this yet.
    2. Wemos 600 Pico.  An even smaller, even cheaper (~$2 when ordered from China) WiFi enabled dev board that runs… MicroPython?  I think??  I’m saying “I think” because I haven’t been able to get it to do anything yet.
      1. Since starting this blog post, I found a guide on installing MicroPython on Wemos boards that seems promising.
        1. Flashing MicroPython on an ESP8266
        2. https://github.com/espressif/esptool/tree/master
        3. Arguing with Python to let me use “esptool.py”
          1. esptool -p COM13 -c esp8266 flash_id
      2. As promising as that series of blog posts looked, I eventually scrapped the Wemos because it was just too much of a pain to get going with MicroPython.  I think I could have made it work, but for $7 I could also just use the Adafruit QtPy I already have.  The advantages of simply uploading code over a USB cable into a virtual drive just can’t be overstated.
    3. Other Boards
      1. I have a bad habit of picking up dev boards.  I’ve got several Adafruit QtPy’s, several Adafruit Trinkets, an Adafruit FX Sound Board, Raspberry Pi Pico (non-WiFi), various Digispark boards, a small handful of ATTiny85’s, and an even weirder assortment of VERY small programmable circuit boards (ISD1806B-COB) designed to go in greeting cards (just 6 seconds), etc.
  2. Companion Robot
    1. Background.
      1. I started this post at least a month ago when I only had a vague idea of what I wanted to make and even fewer skills.  After seeing my kid’s companion robot take shape, I wanted to get in on the action and make my own.  I decided to make a really small companion robot with just some LED’s, piezo, and small microcontroller unit.  I’d taken a stab at making a companion robot a few years ago, but set it aside for a variety of reasons and never went back.
      2. The idea for this new robot would be something a little less ambitious, make more use of NeoPixels than in prior projects3, with a little more interactivity, trying out some CircuitPython, and… let’s be real… more pizzazz!
    2. Idea:  Friendly Cloud/Vapor/Flame
      1. I still really like the copper-toned PLA I’ve been using since it has something of a steampunk flair to it.  I settled on repurposing a small plastic enclosure with a clear dome as the “body” for the robot.  I wanted it to look something like a small entrapped / captive / domesticated4 sentient cloud of vapor or perhaps flame held within a steampunk enclosure.
      2. As a very small, inexpensive board that could run either Arduino or CircuitPython, I decided on the Adafruit QtPy M0.  It could run NeoPixels, there were lots of cool guides on it, plenty of pinouts, and could definitely fit within the confines of my enclosure.
    3. Enclosure:
      1. I started the enclosure by trying to design and 3D print a part to mate with the clear plastic dome.  It took a few tries.

        This slideshow requires JavaScript.

      2. Once I had that, I extended the base so it could hold more electronics.  I could definitely have shoehorned everything into the dome, especially if I took up some of the space inside the dome, but even with an “elevated base” it was still plenty small and could use a battery pack rather than a rechargeable lipo.
      3. Once I had a good design for the enclosure, I tried to make it work with an existing 3xAAA battery pack.  In the process I yanked off the connector and ended up soldering the battery pack leads directly into the circuits.
    4. Internal Electronics
      1. I’m just not a great electrical engineer and am still copy/pasting from various guides, tinkering, changing bits of code, swapping out parts, and using “close enough” resistors.  Wiring up some LEDs or a piezo to a project isn’t very difficult – it’s some of the more fiddly bits.
      2. Piezo Element Speaker
        1. I wanted to use a piezo buzzer/speaker because they’re large and incredibly thin.  They’re not without their downsides.  The crystal wafer is also thin and a little fragile.  The piezo buzzer without additional electronics has the potential to act as a knock sensor and can generate a high voltage spike which can fry a board.  And, without additional electronics, the piezo just isn’t very loud.  There are some libraries for the Arduino that basically double the volume of a piezo by connecting it to two pins and then running each opposite of the other, doubling the voltage difference, but they only work for Arduino chips.5
        2. After searching for various ways to increase the sound of the piezo elements, I settled on trying to use the Adafruit piezo amp.  I bought two – and tried desoldering the terminal blocks.  This completely ruined one.  The other one worked great, but for the modest volume gain it was just too big in an already cramped enclosure.
        3. After searching around, I found some amplifier circuits using a small number of common parts.6
        4. Then I tried building an amplifier circuit using an NPN transistor.  After reviewing the datasheets for my NPN transistors (and PNP transistors), and breadboarding the circuit with resistors, I sketched it a few times, laid it down with copper tape, soldered it in place with SMD resistors, then pulled it off and placed it onto a piece of Kapton tape and put another piece on top – “laminating” it in place.
      3. Capacitive Touch
        1. Buttons are great and all, but with a capacitive touch pad, I could add metallic elements to my robot rather than a much bulkier button.  I bought a few brass upholstery tacks because they looked great – but they just would not accept molten solder.  I ended up cutting the prongs short with wire cutters, wrapping the stub with copper tape, then soldering the wires to the tape.  I’d also added a little piece of heat shrink tubing over the connection to help keep it together.  It’s been working well so far.
      4. LED Animations
        1. As we know from Phillip Burgess‘ incredible “power sipping NeoPixel” guide, we can conserve power and increase the impact of the LED’s by reducing the number of LED’s, keeping max brightness ~20% for a disproportionately large impact, running fewer LED’s at a time, and even running fewer colors at a time.  Between Phillip’s work, Todbot’s guide, and the specialized QtPy NeoPixel guide by Kattni Rembor, I was able to put together a few neat animations.
      5. Piezo Sounds
        1. I had a heck of time getting the piezo buzzer to do anything interesting.  Fortunately, with my kid helped convert the piano music for “Paint It Black” into tones for me.  I haven’t gotten all the note timings right, but I’m working on it!
  3. Future Modifications
    1. More Accessible Enclosure.  Right now the “lid” with a hole for the LED ring just sits on the enclosure with a light friction fit.  One idea is a hinged lid, either with a conventional hinge or perhaps a hidden swivel hinge.  The problem with that, of course, is it requires even more internal space.  Other ideas include a ring on top that screws down, holding the top down and in place.  I’m crap at designing screw threads, so I’ve avoided this.

      Hinged lid for enclosure
      Hinged lid for enclosure
    2. Piezo Knocks.  Perhaps the next version will include some kind of tap / double tap / knock sensors using one or more piezo elements.
    3. Knobs.  There’s not a ton of room inside the enclosure, but by including a gear within a gear, I might be able to rotate part of the case and have it manipulate a potentiometer.

      Offset gear within gear, manipulating an off-center internal potentiometer
      Offset gear within gear, manipulating an off-center internal potentiometer
    4. Motors.  A robot that just flashes lights and makes a few beeps can still be pretty interesting.  However, I have some neat potential features that could be added with just one or two motors.  There are some interesting limitations with the current incarnation of this robot and using a QtPy.  I’ve only got 10 pinouts7 , 1 for NeoPixels, 1 for the piezo, 6 in use for the capacitive touch sensors, leaving 2 for other potential tasks.8  However, space is already tight so one or two micro servos would be a big space commitment.  I’ve seen some really tiny micro servos that might work, but I have no idea where to source them.  One silly idea is a “weapons system” using a spring loaded projectile activated by a very small servo.

      A small spring loaded projectile launcher, actuated by a small servo
      A small spring loaded projectile launcher, actuated by a small servo
    5. Creating Tone Library.  The basic piezo tones are easy enough to play, but including the entire list of tones and the frequencies associated with them seems eat up the poor little QtPy’s memory.  I think compressing them into a library might be the way around this issue.
    6. Playing WAV files.  WAV files are bulky, but that’s the only sound file format a QtPy M0 can play.  However, with the extra 2MB from the SPI chip installed, this shouldn’t be a huge problem.  I used Audacity to mix the sound clip down to mono then to 22 KHz sample rate.  My preliminary tests worked – but it was incredibly quiet.  I haven’t run it through the audio amplifier yet, but I’m planning to.
    7. Sleep / Deep Sleep.  Ever since I swapped out the tiny LiPo for a 3xAAA battery pack, I’ve had a lot more battery life, so adding sleep / deep sleep functions haven’t been a priority.  However, this inclusion just couldn’t hurt.
  4. Other QtPy and CircuitPython Resources
    1. Adafruit’s QtPy CircuitPython PWM resource
    2. TodBot’s CircuitPython tricks
Companion Robots: Building Robot Friends
  1. Cephalopod Robot Friend, the story so far
  2. Cephalopod Robot Friend Progress
  3. CuttleBot Body and OpenSCAD Design Tips
  4. An Assembled CuttleBot Body
  5. Building the Monocle Top Hat Cat for #MicrobitVirtualConcert
  6. Companion Robots and Maker Faire Season!
  1. I just got a notice they’re no longer a “Mini”! []
  2. pinouts for my future reference []
  3. LED goggles and a Marvel Universe inspired set of “Infinity Knuckles” []
  4. OMG dome-sticated?! []
  5. This is just my very basic understanding of how it works.  I’m entirely positive this is far too simplified. []
  6. And one very long article about using a lot of parts []
  7. 12 if you want to count the onboard NeoPixel []
  8. Or 4… []