WS2812 LED Pixels with ola using SPI

Controlling WS2812 LED-Pixels from an Raspberry-Pi can be done. Other people have done this before:
My approach is different, but not that new either. I already implemented similar code for the Lightpainting-Stick-Clone.

To get the required timing with the SPI we can output some patterns to make it look like the WS2812-Protocol.

The yellow line is MOSI (DIN). The 50us Reset is visible on channel 2.
Closeup of the actual timing. This image shows the first 7 bits (1000010)


However, there are some issues as metioned in the inline-comments. At first it seems like the code does not work when sending dmx-data with ola_dmxconsole. Once there are more changes in a short period of time, the pixels start to react. My guess here that this is related to the scheduler of the kernel. When as the kernel switches tasks the SPI output-stream gets interrupted, thus making the timing invalid. I will investigate this further.

void SPIOutput::IndividualWS2812Control(const DmxBuffer &buffer) {
   * This is an hack!
   * Basically the RaspberryPI cant drive WS2812 Pixels because they have
   * some strict timing requirements.
   * Using the SPI-Module we can emulate those pulses.
   * This will only work if the SPI-Module outputs all data in one consecutive
   * stream without any pauses. More recent versions of the Raspberry Kernel
   * seem to implement DMA for SPI which makes this possible.
   * WS2811-Chips have similar timings but not exactly the same as WS2812.
   * Some sources say the most important part is the total period-time
   * of one bit. There are lots of descriptions of WS2812-timing requirements
   * out there, one of them is: (with +-150ns tolerance)
   * 0-Bit 0.25us high, 1us low
   * 1-Bit 0.6us high, 0.65us low
   * Running the SPI at 4Mhz gives us a pulse-width of 250ns
   * Using a 5-bit pattern we can emulate those timings
   *   0-Bit: 10000 (0.25us high, 1us low)
   *   1-Bit: 11110 (1us high, 0.25us low)
   * Those values are not an exact match, but they seem to work for both
   * WS2812 and WS2811 in high-speed-mode.
   * We need to send 8 of those 5-packs for each byte. Those 40 bits are
   * precalculated and stored in a lookup table.
   * To issue an reset, the data-line needs to be low for 50us, thats
   * 200 bits, or 25 bytes. For safety, we will send 27 bytes.
   * Connect the Data-In-Pin of the LEDs to the MOSI Pin of the SPI-Module,
   * ignore the clock Pin.
  const uint8_t reset_bytes = 27;
  const unsigned int first_slot = m_start_address - 1;  // 0 offset
  // We always check out the entire string length, even if we only have data
  // for part of it
  const unsigned int output_len = reset_bytes + m_pixel_count *
                                  WS2812_SLOTS_PER_PIXEL * WS2812_SPI_LUT_LEN;
  uint8_t *output = m_backend->Checkout(m_output_number, output_len, 0);

  if (!output)

  // set the reset-bytes to zero
  memset(output, 0, reset_bytes);

  // loop over all pixels
  unsigned int output_pos = reset_bytes;
  for (uint16_t px = 0; px < m_pixel_count * WS2812_SLOTS_PER_PIXEL; px++) {
    uint8_t c = buffer.Get(px + first_slot);
    // now output 5 bytes from the LUT
    for (unsigned int lut = 0; lut < WS2812_SPI_LUT_LEN; lut++) {       output[output_pos++] = WS2812_SPI_LUT[c * 5 + lut];     }   }   m_backend->Commit(m_output_number);

I pushed that code here:

This is the config I used:

base_uid = 7a70:00000100
device_prefix = spidev
enabled = true
spidev0.0-0-device-label = SPI Device - spidev0.0
spidev0.0-0-dmx-address = 1
spidev0.0-0-personality = 9
spidev0.0-0-pixel-count = 10
spidev0.0-backend = software
spidev0.0-ports = 1
spidev0.0-spi-ce-high = false
spidev0.0-spi-speed = 8000000
spidev0.0-sync-port = 0

Demo time:

Replace the battery of a Digital caliper with a supercap

I got the calipers off ebay (<10$) quite a while ago, but they kept draining the battery really fast. After i discovered this blogpost I decided to modify my calipers in a similar way.

Modified caliper with charging cable

The basic idea is the same:

  • replace the battery with a supercap
  • build a charging device

The only real difference compared to trevors project is the voltage used. I didnt have a 1.8V LDO around, so i decided to go with 3.3V.  An additional SI-Diode in the charging-path drops 0.7Volt, so the actual voltage for a charged cap is 2.6V which seems to be fine for the caliper (i did no long-time test yet). Also, with that diode one cant blow the cap by charging with reverse polarity.

I only used parts you can get from the german distributor Reichelt. The cap is a “SPK 1.0F”, the switch is called ‘T215″.

I did not much testing yet. But the cap kept enough power to work with the caliper even after 80 hours ‘standby’ 🙂

Adding alignment lasers to my laser-cutter

I bought my el-cheapo chinese lasercutter without a pilot laser. They did offer a visible laser for alingment, but that was just a simple laser-pointer mounted next to the main-laser-nozzle. It was kind of expensive (despite the fact that it was a really simple non-coaxial-design) so i decided to build my own.

From ebay i got two laser-modules in a cylindrical housing for about 21€ including shipping which already produce a line (they have a prism build in).

I  order to mount them i designed and build a bracket out of 12mm acrylic. Basically its a ring with a slot, i can slide it up the nozzle and fix its positin by tightening a screw. You can see how it looks mounted to the machine in the first picture.

Unfortunately i wasnt able to mount it further down on the nozzle. Now the laser angle is to steep and part of the laser is blocked by the nozzle itself (you can see the laser on the nozzle in the second picture). I managed to adjust it in a way that at least the pilot lasers cross in one point. Alignment in parallel to the working area required a little patience, but somehow i managed:

As a last job i wired everything up and slid the cables through the drag-chain. Since i was already soldering and installing additional wires, i also added two LED-Stripes to the portal to have better illumination while working.