Iron Man 3 Chest Arc Reactor

I made this prop to wear to the Iron Man 3 release (and to test out some new technology for a future project).

Arc Reactor

The whole thing was designed in OpenSCAD and 3D printed in ABS (both the housing and the diffusor puck).  A Teensy 2 module drives the LED strip (WS2812 clone, 60 pixels/m) and is is wired up to a BLE112 module for communications.

You can control it via Bluetooth (no app yet, you hand craft a 1, 3, 6, or 7 byte message in a BLE explorer app to set a greyscale color, or one/two RGB colors and a ping-pong speed).  By default it boots up into ‘Arc reactor blue’ with a soft breathing animation.

Prototype case design
Diffusor Test
Innards
Fully assembled
Glowy Victory
Thru T-Shirt
Iron Man Selfie

Speeding up LPD8806 show() without hardware SPI

If you’re using LPD8806 LED strips and you can’t use the hardware SPI port (e.g., when using an Ethernet board), there are two other options in the Adafruit library: the default mode and ‘slowmo’ mode. The default mode is decent, but the flexibility of being able to choose the pins at runtime comes with a cost.

However, you can still get a decent speedup by defining your pin usage at compile time in a replacement show() function. I measured the time required to update an 86 LED strip using each method on an EtherTen board (Atmega328 @ 16 MHz, same as the Uno):

30.23 ms - Adafruit 'slowmo' method (digitalWrite)
7.76 ms - Adafruit default method (port pointers)
1.54 ms - Compile-time method
1.43 ms - Adafruit hardware SPI method

The timing was done using micros() around the show() call with strip.pause = 0.

I’ve tried to make this method as minimally hardcoded as possible. To use, throw the code from CompileTimeLEDs.h into the LPD8806 class in LPD8806.h and replace:

int ClockPin = 3;
int DataPin = 2;
...
strip.show();

with:

const int ClockPin = 3;
const int DataPin = 2;
...
strip.showCompileTime<ClockPin, DataPin>();

This overload is only available on ATmega168 or ATmega328 boards; on the Arduino Mega or other random boards, you need to specify the port register and use pin offsets within the port instead of the Arduino board pin number (e.g., showCompileTime<0..7, 0..7>(PORTD, PORTD))

CompileTimeLEDs.h (Download)

template<unsigned int ClockPin>
void PulseClockLine(volatile uint8_t& ClockRegister)
{
  const byte LED_CLOCK_MASK = 1 << ClockPin;
  ClockRegister |= LED_CLOCK_MASK;
  ClockRegister &= ~LED_CLOCK_MASK;
}
 
template<unsigned int ClockPin, unsigned int DataPin>
void TransmitBit(byte& CurrentByte, volatile uint8_t& ClockRegister, volatile uint8_t& DataRegister)
{
  // Set the data bit
  const byte LED_DATA_MASK = 1 << DataPin;
  if (CurrentByte & 0x80)
  {
    DataRegister |= LED_DATA_MASK;
  }
  else
  {
    DataRegister &= ~LED_DATA_MASK;
  }
 
  // Pulse the clock line
  PulseClockLine<ClockPin>(ClockRegister);
 
  // Advance to the next bit to transmit
  CurrentByte = CurrentByte << 1;
}
 
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
  #define MAP_ARDUINO_PIN_TO_PORT_PIN(ArduinoPin) \
    ( ArduinoPin & 7 )
 
  #define MAP_ARDUINO_PIN_TO_PORT_REG(ArduinoPin) \
    ( (ArduinoPin >= 16) ? PORTC : (((ArduinoPin) >= 8) ? PORTB : PORTD) )
 
  // Specify Arduino pin numbers
  template<unsigned int ClockPin, unsigned int DataPin>
  void showCompileTime()
  {
    showCompileTime<MAP_ARDUINO_PIN_TO_PORT_PIN(ClockPin), MAP_ARDUINO_PIN_TO_PORT_PIN(DataPin)>(
      MAP_ARDUINO_PIN_TO_PORT_REG(ClockPin),
      MAP_ARDUINO_PIN_TO_PORT_REG(DataPin));
  }
 
  #undef MAP_ARDUINO_PIN_TO_PORT_PIN
  #undef MAP_ARDUINO_PIN_TO_PORT_REG
#else
  // Sorry: Didn't write an equivalent for other boards; use the other
  // overload and explicitly specify ports and offsets within those ports
#endif
 
// Note: Pin template params need to be relative to their port (0..7), not Arduino pinout numbers
template<unsigned int ClockPin, unsigned int DataPin>
void showCompileTime(volatile uint8_t& ClockRegister, volatile uint8_t& DataRegister)
{
  // Clock out the color for each LED
  byte* DataPtr = pixels;
  byte* EndDataPtr = pixels + (numLEDs * 3);
 
  do
  {
    byte CurrentByte = *DataPtr++;
 
    TransmitBit<ClockPin, DataPin>(CurrentByte, ClockRegister, DataRegister);
    TransmitBit<ClockPin, DataPin>(CurrentByte, ClockRegister, DataRegister);
    TransmitBit<ClockPin, DataPin>(CurrentByte, ClockRegister, DataRegister);
    TransmitBit<ClockPin, DataPin>(CurrentByte, ClockRegister, DataRegister);
 
    TransmitBit<ClockPin, DataPin>(CurrentByte, ClockRegister, DataRegister);
    TransmitBit<ClockPin, DataPin>(CurrentByte, ClockRegister, DataRegister);
    TransmitBit<ClockPin, DataPin>(CurrentByte, ClockRegister, DataRegister);
    TransmitBit<ClockPin, DataPin>(CurrentByte, ClockRegister, DataRegister);
  }
  while (DataPtr != EndDataPtr);
 
  // Clear the data line while we clock out the latching pattern
  const byte LED_DATA_MASK = 1 << DataPin;
  DataRegister &= ~LED_DATA_MASK;
 
  // All of the original data had the high bit set in each byte.  To latch
  // the color in, we need to clock out another LED worth of 0's for every
  // 64 LEDs in the strip apparently.
  byte RemainingLatchBytes = ((numLEDs + 63) / 64) * 3;
  do 
  {
    PulseClockLine<ClockPin>(ClockRegister);
    PulseClockLine<ClockPin>(ClockRegister);
    PulseClockLine<ClockPin>(ClockRegister);
    PulseClockLine<ClockPin>(ClockRegister);
 
    PulseClockLine<ClockPin>(ClockRegister);
    PulseClockLine<ClockPin>(ClockRegister);
    PulseClockLine<ClockPin>(ClockRegister);
    PulseClockLine<ClockPin>(ClockRegister);
  } while (--RemainingLatchBytes);
 
  // Need a bit of a delay before clocking again, but ideally this
  // is set to 0 and meaningful work is done instead
  if (pause)
  {
    delay(pause);
  }
}

License: CC0 (Placed into the public domain).

The avr-gcc compiler seems to do fine at recognizing the compile time constants and doing the right thing in codegen, as this method is just as fast as my original macrotastic implementation. You can probably eke out some more performance (would be nice to beat the hardware SPI implementation ^_^), but this was enough to get the headroom I needed.

Infected Omen Light Box

I pulled an all-nighter and designed/fabricated a LED edge lighting box plus etched plate in advance of the Gears 3 launch. The original plan was to slide an unused junk android tablet in and show some numbers from the stats dashboard in that central window, but I wasn’t able to get the tablet on the secure network at work, so now it’s just a pretty light box sitting on my desk. I almost slid my iPad in, but the slot I machined for the tablet is about 0.5 in too short for an iPad, though it had 0.75 in of slack for the target tablet.

The light gently pulsates / undulates around the border (driven by an Arduino), aiming to look a bit like the corrupted omen on the title screen.

The LED strip I used are LPD8806 driven strips from Adafruit, and they are a dream to use compared to the older Christmas light strand style strips I’ve used in the past. Each LED is individually addressable via a SPI-like interface to set a 21 bpp RGB color, and they have their own internal PWM clock, so you can fire and forget, no need to keep clocking them.

Also, Gears of War 3 is out, you should play it!

Aether converter cell

I was thinking of doing something Steampunk themed for Halloween, and thought I might spend a few hours prototyping a ‘raygun’ prop.  This aether cell as far as I got, it would serve as the ‘ammo cartridge’ for the raygun, sticking out of the back at a slight angle.

It’s got a red LED, a cyan ultrabright, and a UV LED, and the fluid is a mixture of diet tonic water, vodka, and highlighter fluid, so you get a very nice eerie glow with the UV LED on.

Aether converter cell in action

One reason I didn’t go further is because Mighty Putty sucks.  I had purchased some on sale at Target because it’s supposed to be waterproof, but it certainly didn’t form a watertight seal against my brass coupling and I’m glad I tried it here and not in an emergency plumbing situation!  It also smells incredibly foul, far worse than other epoxies I’ve used.

Another thing I’d try differently next time is to omit the highlighter fluid.  It does glow brightly, but it’s fairly opaque as well, making the mix look cloudy with the LEDs off and I think it limits how far the UV can travel too much.

Aether converter cell in action
Mounting LED + crystal
Testing crystal positioning
Red LED on