Exporting KiCad PCB w/ silkscreen to Fusion 360

Here’s the process I’ve been using to add a silkscreen image to the exported STEP model from KiCad to Fusion 360. The steps are:

In KiCad PCB:
  • File -> Export -> STEP to export 3D model of PCB w/ parts
  • Open View -> 3D Viewer
  • Press Z or Shift-Z to bright top or bottom silk face on
  • Go to the 3D Viewer “Appearance Manager” and hide all parts
  • Click File -> Save Current View as PNG
  • Open up screenshot and crop to board edges
  • Close 3D viewer – Open Inspect -> Board Statistics and note Board Size Width & Height in mm
In Fusion 360:
  • Upload STEP file
  • Open uploaded file
  • Click on the face of the PCB you want the silkscreen on
  • Press A key to open up Appearance pane
  • Click Apply To: Face
  • In the “In This Design”, pick any square, right-click on it, choose “Copy to My Appearances”
  • Go down to Library / My Appearances and right-click on newly added square
  • Click Edit, this brings up another pane window.
  • Click small down triangle to right of Color parameter
  • Select “Image” and a file dialog will appear. Select your cropped silk image and “Open”
  • Click down arrow again and “Edit Image…”, a new pane appears
  • Under “Scale”, enter the board dimensions from above, make sure the aspect ratio lock is unchecked
  • Under “Position”, make sure the “Offset” is -1/2 the board width and height
  • Under “Repeat”, select “None” to turn off tiling
  • Close that pane
  • Back in the Material Editor, select “Apply To: Faces”
  • Drag the new material you’ve created to the face of the PCB object in Fusion
  • Done!

Speed up CircuitPython LED animations 10x!

In many LED animations, you need to apply an operation to all LEDs as fast as possible. Often this is something like “fadeToBlackBy”, i.e. “dim all LEDs by a given amount”. There are some common effects you get by using “fadeToBlackBy” and one other action. For example: the Cylon effect is “Turn an LED on, Fade all LEDs toward black, Go to next LED, Repeat”. A simple firework simulation is “Turn on random LED, Fade all LEDs toward black, Repeat”.

Since I don’t come from a normal Python background, I’ve not used much Numpy. But I knew it was great for vector math. And I knew CircuitPython had a minimal version of Numpy called ulab it inherited from Micropython. Even though I’d seen the wonderful ulab learn guide that jepler did, the usefulness of numpy/ulab didn’t sink in. It was only when I was hacking on LED animation speedups did I re-stumble upon ulab. And am I glad I did.

As a test with a simple fire animation (as optimized as I could make it in normal Python), the results with ulab are striking:

The general technique is to create a “working copy” of the LED data in a ulab array, and use ulab functions as much as possible to modify that working copy. Then at the last possible moment, copy the working copy data to the real LEDs object. It only adds a few lines of code to existing solutions and you get access to all these cool ulab array functions for LED effects! For instance, to constrain all RGB values of all LEDs to 0-255, this is a single line in ulab: leds_np = np.clip(leds_np,0,255). And it further cements my belief that any time you’re doing a for-loop on a large list in Python, you’re probably doing it wrong. :-)

Screenshots of functionally identical code, before / after:

The video demo below shows the difference (also available on youtube). Each setup is identical, calculating for 256 LEDs even though only 64 are displayed. The “no ulab” case is perfectly usable if a bit choppy, but you lose ~36 milliseconds where you could be doing something else. (Like, for instance, these LED matrices are wired up in a serpentine pattern, so you need some math to unravel that if you want to draw shapes). The ulab version seems much smoother and more organic, at least to my eyes.

If you’d like to try this yourself, I’ve collected this code and a rudimentary “fire_leds” library that uses this technique in the repository: https://github.com/todbot/circuitpython_led_effects

Multiple Displays in CircuitPython & Compiling Custom CircuitPython

Did you know you can run multiple displays in CircuitPython? One way: wire up two displays in parallel.

There’s also true dual displays, more on that in a bit. For the above, here’s the wiring diagram and the code “gc9a01_hellocircles_compact.py”. Note how the second display is hooked up in parallel and the code just sees one display.

But what if you want each display to be independent?

Almost all CircuitPython boards support a display. CircuitPython makes it really easy to configure a display’s driver and all displays act the same for drawing images and text: monochrome OLEDs, RGB TFT LCDs, even ePaper!

But only a single display is supported for the pre-built versions of CircuitPython (The one exception is the MonsterMask) Multiple displays are supported in the design of CircuitPython, but it’s a compile-time setting. This is for RAM savings reasons. That decision is a few years old and I think for modern chips like ESP32-S2/3 and RP2040, there’s more than enough RAM to allow that default to go up to two.

Compiling CircuitPython to Add Second Display

In the meantime, we can re-compile CircuitPython ourselves to increase that limit. It’s pretty easy if you’re comfortable with the command line. If you’re already know how to build CircuitPython, the short answer is:

Add this line to your board’s mpconfigboard.h:
#define CIRCUITPY_DISPLAY_LIMIT (2)
recompile and install the resulting UF2 file.

If you’ve never compiled CircuitPython before, there is a very useful Adafruit Learn Guide building CircuitPython. I recommend start there and go through each step. This post is not that. This post is a reminder for myself, with an abbreviated version those steps that I can just copy-n-paste when I want an entirely fresh checkout. Not all of these steps are needed if you already have a checkout.

First: initial git checkout, getting the git submodules, and installing python requirements.

git clone https://github.com/adafruit/circuitpython circuitpython-todbot
cd circuitpython-todbot
make fetch-submodules
git checkout main
pip3 install --upgrade -r requirements-dev.txt
pip3 install --upgrade -r requirements-doc.txt
make -C mpy-cross

Next: do an easy build for QTPy M0 to make sure everything works:

cd ports/atmel-samd
make BOARD=qtpy_m0   
# or can do: make -j10 BOARD=qtpy_m0   # since you have lots of cores 
ls -l build-qtpy_m0/firmware.uf2

Also, let’s try building CircuitPython for Raspberry Pi Pico:

cd ports/raspberrypi
make -j10 BOARD=raspberry_pi_pico
ls -l build-raspberry_pi_pico/firmware.uf2

Now build for QTPy ESP32-S2. We need to install the ESP-IDF for this (see circuitpython/ports/espressif/README for details). Fortunately, the CircuitPython devs make this easy:

cd ports/espressif
./esp-idf/install.sh
. ./esp-idf/export.sh 
make -j10 BOARD=adafruit_qtpy_esp32s2
ls -l build-adafruit_qtpy_esp32s2/firmware.uf2

For any of these, when you get a .UF2 file, you can copy to your board in UF2 bootloader mode, like a normal CircuitPython upgrade. This is a great way to try the latest changes without waiting for the automated builds on Adafruit’s S3 cache.

Now we can make the change to support two displays. Let’s add two displays to CircuitPython for QTPy ESP32-S2:

cd ports/espressif
echo "#define CIRCUITPY_DISPLAY_LIMIT (2)" >> boards/adafruit_qtpy_esp32s2/mpconfigboard.h
make -j10 BOARD=adafruit_qtpy_esp32s2

Copy the resulting “firmware.uf2” file to install your new CircuitPython and start trying out dual displays!

Using Multiple Displays

You can now wire up multiple displays as needed for your application, CircuitPython doesn’t care: two SPI busses with independent CS/control lines, a single SPI bus with independent CS/control lines, a single I2C bus, or multiple I2C busses.

For example, if using two I2C SSD1306 monochrome OLED displays at addresses 0x3C and 0x3D on the same I2C bus, creating two display objects would look like:

import busio, displayio
from adafruit_displayio_ssd1306 import SSD1306

dw,dh = 128,64 # display width,height
i2c = busio.I2C(scl=board.GPIO0, sda=board.GPIO1) # e.g. on a Pico

display_busA = displayio.I2CDisplay(i2c, device_address=0x3C)
display_busB = displayio.I2CDisplay(i2c, device_address=0x3D)

displayA = SSD1306(display_busA, width=dw, height=dh)
displayB = SSD1306(display_busB, width=dw, height=dh)

Notice you need a display_bus object for each display. For two GC9A01 round LCDs using SPI, you can still use a single SPI bus, but you do need separate CS, CD, RST lines, like:

import busio, displayio
import gc9A01

dw,dh = 240,240 # display width,height
tft_clk  = board.SCK  # e.g. QTPy ESP32S2 pinout
tft_mosi = board.MOSI
tftA_rst  = board.TX
tftA_dc   = board.RX
tftA_cs   = board.A3
tftB_rst  = board.A2
tftB_dc   = board.SDA
tftB_cs   = board.SCL
spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi)

display_busA = displayio.FourWire(spi, command=tftA_dc, chip_select=tftA_cs, reset=tftA_rst)
display_busB = displayio.FourWire(spi, command=tftB_dc, chip_select=tftB_cs, reset=tftB_rst)

displayA = gc9a01.GC9A01(display_busA, width=dw, height=dh)
displayB = gc9a01.GC9A01(display_busB, width=dw, height=dh)

And the result can be something like this!

The code is at the gist ‘eyeballs_dual_gc9a01.py‘ and the wiring for the above is using a single SPI bus and separate lines for CS,DC,RST and looks like:

Give it a try! I’ve used this technique on a variety of displays. If you need help creating a UF2 or have other questions, let me know!

Get HID Report Descriptors with “win-hid-dump” & “mac-hid-dump”

I help support the USB HID projects hidapi and node-hid. One of the aspects of HID that isn’t clear is that HID devices transmit and receive data with “reports”, fixed-size data packets. What reports a device responds to is described by the HID Report Descriptor it hands to the OS. The OS then only permits those reports to be sent and received. This descriptor is different from the USB Config & Device Descriptors Those two are easily discoverable using standard system tools, but for whatever reason, getting HID Report Descriptors is much harder.

To address this, I’ve created two tools: win-hid-dump and mac-hid-dump from code snippets around the Net. They are akin to simplified versions of usbhid-dump for Linux. They print the HID Report Descriptors of the HID devices currently connected and the output can be parsed by things like https://eleccelerator.com/usbdescreqparser/

The tool repos with pre-compiled releases:

I’ve found many of the issues I have in supporting node-hid is getting people to understand that you need to know the type and sizes of the reports you want. Hopefully they’ll be useful to others as well. Let me know if you have any issues with them.

The output looks something like:

tod@demo ~/Downloads/mac-hid-dump$ ./mac-hid-dump
mac-hid-dump:
16C0 0486:  - Teensyduino RawHID Device
DESCRIPTOR:
  06  c9  ff  09  04  a1  5c  75  08  15  00  26  ff  00  95  40
  09  75  81  02  95  20  09  76  91  02  95  04  09  76  b1  02
  c0
  (33 bytes)
16C0 0486:  - Teensyduino RawHID Device
DESCRIPTOR:
  06  ab  ff  0a  00  02  a1  01  75  08  15  00  26  ff  00  95
  40  09  01  81  02  95  40  09  02  91  02  c0
  (28 bytes)
27B8 01ED: ThingM - blink(1) mk3
DESCRIPTOR:
  06  ab  ff  0a  00  20  a1  01  15  00  26  ff  00  75  08  85
  01  95  08  09  00  b2  02  01  75  08  85  02  95  3c  09  00
  b2  02  01  c0
  (36 bytes)
tod@demo ~/Downloads/mac-hid-dump$

DIY Neopixel / WS2812 Development Kit

If you are like me, you play around with Neopixel / WS2812-style LEDs a lot. Normally I solder my own custom-connectors and don’t use the ones that come with the reels I buy. But since we’ve been playing around with WS2812-compatible Xmas lights, I decided to make a “Neopixel Dev Kit” for easier experimenting with different LEDs and different ways of powering them. Most LED reels come with a 3-pin JST SM socket (female) connector installed with also maybe a 2.1mm DC barrel connector to inject power (see reel picture below). They’ll also throw in a pigtail JST SM plug (male) connector to attach to your controller. So I standardized on this. The kit as shown consists of (from bottom to top):

  • 3-pin JST SM socket (female) added to weird new WS2812-compatible Xmas lights
  • Known-good USB-powered WS2812 controller from those lights, rewired with JST SM plug
  • Standard 3-pin JST SM plug to JST SM socket, with 2.1mm DC barrel connector power injector
  • 3-pin JST SM plug (male) to header pins, for breadboarding
  • Known-good normal WS2812 strip with JST SM socket
  • 5V power supply with 2.1mm DC barrel connector
  • USB power meter for seeing voltage & amps drawn by any particular setup (see example below)
  • Arduino Pro Micro clone on tiny breadboard. This is a 5V microcontroller, so no need to worry about 3.3V -> 5V data issues. And it’s cheap in case we blow it up

Overall it’s working out okay. I need to add a few “sacrificial neopixels” to the mix to make using 3.3V-based microcontrollers and maybe some breadboard screw-terminals.

WS2812-compatible “fairy light” LEDs that know their address

I’ve not done much on our plotter Xmas tree this week because, well… if you follow me on Twitter, it’s likely you’ve seen me ramble on about these crazy new Christmas Tree lights we got for our tree (first tree ever!). I wasn’t able to find appropriate “Christmas Tree” light forms of the WS2812 lights, but Carlyn did! And they were even better than normal Xmas light styles. These were “fairy lights” with small beads of light connected by solid enamel-coated wire that almost disappear visually. The LED string appeared to maybe be WS2812 / Neopixel compatible and yup, they were! But the weird thing was, as I learned was discovered back in November, these LEDs remember their address when cut out of the strand. If you’re familiar with WS2812-style LEDs, this is bonkers. Normally these LEDs have four pins: +5V, Gnd, DataIn, and DataOut. To make an LED strip, the LEDs are wired in a chain, one LED’s DataOut going to next LED’s DataIn. This is great! It means you just have one wire to control hundreds of LEDs!

What’s going on with these LEDs? You cut them up, wire them in parallel (there’s no data “in” and “out”, just “data”) and they still know where they should be in the data stream, only picking the data for them. I’m still researching this with the help of some friends who can navigate Shenzhen better than me, but it’s looking like these LEDs have their address set OTP (one-time programmable) at the factory. No LED part number yet. If you’d like to get some to try yourself, here’s the 100-LED exact Amazon ASIN I bought as well as the 200-LED ASIN Carlyn originally found (both affil links).

Here’s some pics showing the box and how I took it apart to test on an Arduino Micro clone.

Precise flood fill with AxiDraw

We recently got some inexpensive multicolored 0.3mm technical markers (AmzAffil) for use on the AxiDraw and they are pretty good! For this week’s batch of days for our 31-box AxiDraw calendar, I wanted to play with doing “flood fill” / solid color fills. Normally the “hatch fill” extension part of the AxiDraw Inkscape extensions creates slanted lines to imply a filled color. I knew a true full fill would be touchy because the paper can tear when fully inked, but I wanted to try. It turns out to work great!

There’s not much of a trick to it, just adjust the “hatch fill” algorithm to have closely-spaced lines (“2” for a 0.3mm pen worked well) and “connect nearby ends” to go back-n-forth to minimize pen up/down messiness. Also orienting your hatch direction, optimizing for your object (0º in my case) can make the fill succeed better. Plotting does take a while (especially for multi-colors like the 12 ornaments) and does cause some bleed-through. But I think it looks awesome.

And the video of it is mesmerizing: