Archive for the ‘Art’ Category

Individually Addressable Incandescent Lamps

Everyone playing with electronics these days knows about LED strips made of dozens or hundreds of individually addressable RGB LEDs. I love them and make great use of them in the nootropic design Lumazoid music visualizer. But what about incandescent lamps? Old-school light bulbs with a filament have a soft warm glow that you just can’t get from LEDs. Wouldn’t it be great to be able to control a bunch of lamps individually by setting their brightness in code on a microcontroller? This is just what I set out to do recently, and the results are great.

Individually addressable lamp module

Individually addressable lamp module




How it works

I simply used the same technology as LED strips to allow communication between lamp modules. LED strips have RGB LEDs with an embedded driver chip which uses PWM (pulse width modulation) to control the duty cycle on the red, green, and blue LEDs. This combined LED/chip is called WS2812 or WS2812B. On older LED strips, the driver chip was not embedded into the LED itself, but was a separate chip called WS2811. These standalone driver chips are somewhat obsolete now which means they are cheap! I got 50 of them on eBay for $5.00. Since these modules use the same technology as LED strips, the same code can be used. Adafruit’s NeoPixel library is a very simple way to control LEDs, so we can control each lamp easily. The lamp is controlled by the “blue” pin on the WS2811 so that is the value to set.

byte brightness = 128;
lamps.setPixelColor(lampNum, pixels.Color(0, 0, brightness));

Circuit

The driver chip cannot sink enough current to control a lamp directly, so I connected one of the LED output pins (the pin designated for blue) to a P-channel MOSFET. The PWM output from the WS2811 pin controls the MOSFET gate and the MOSFET then controls the current flowing to the lamp. I tried several MOSFETs to find one that worked best for a 5V source and allowed good control at very low PWM duty cycle. The FQP27P06 MOSFET allows the lamp filament to glow at an almost imperceptible level at a PWM duty cycle of 1/255. Since a lamp filament takes a bit of time to turn on/off, there is no flicker at low duty cycles. Nice!

lampModuleSchematic


Notice that the module has a big 1500uF capacitor. This is because the PWM creates a lot of ripple in the power line that needs to be smoothed out. At first I did not experience this when using only one WS2811/MOSFET/lamp in the circuit, but as soon as I added more there was lots of flickering caused by power line noise. A big fat cap solved that.

Lamp bulbs and sockets

The board is designed to allow the lamp socket to be mounted on the top or the bottom. That’s why there are 2 lamps in the schematic. There are also solder pads with holes to allow wires to be attached if I don’t want the lamp directly on the board. I used E10 miniature sockets from eBay and #27 bulbs with an E10 miniature base. These are 5V bulbs that draw up to 300mA. These can be bought at Mouser (part number 560-27). Or you can get them and lots of other bulbs from Bulb Town. Who knows more about bulbs than Bulb Town? Nobody, that’s who.

Power

If you are using lots of them at full brightness, you’ll need a beefy power supply. I have 20 lamp modules strung together and power it with a 10A supply. If I try to set all 20 lamps to full brightness at the same time, the lamps at the end of the string are dim because of the resistance in the power wires, so my Arduino driver programs need to account for this. Also, a lamp filament doesn’t turn on/off immediately like an LED, so I also had to take this in consideration in my programs. This slowness in the on/off actually smooths out the illumination so there is no flicker, even at very low duty cycles.

Conclusion

I’m really happy with how this all turned out. I’m not planning on offering these modules as a product because I think the minimum price would need to be about $7 each, which I’m not sure people would want to pay. If you really think I should offer these or are interested, please let me know.


Published by Michael, on May 21st, 2016 at 1:25 pm. Filed under: Arduino,Art. | 4 Comments |

I’m Featured in the New Book “Maker Pro”!

I’m so happy to be one of the authors of the new book Maker Pro edited by John Baichtal. Maker Pro is a collection of essays by makers who have gone pro by starting their own businesses. Last year John asked me to write an essay about my experiences, and I happily penned a piece called “The Power of Constraints”. In the essay I talk about how I got started making and how the technical limitations and constraints of microcontroller programming were the fuel that fed my fire. It’s a great book, so consider buying it on Amazon. I’m truly honored to be among some great hardware hackers and makers.

MakerPro_front_sm


MakerPro_back


Published by Michael, on December 31st, 2014 at 8:02 am. Filed under: Art. | No Comments |

Defusable Clock Used as Prop in ‘The Tomorrow People’ Episode

I am so excited that the nootropic design Defusable Clock was used as a prop in an episode of the new TV show The Tomorrow People on the CW Network! The episode “Kill or Be Killed” aired on October 30th, 2013. If you watch the episode online the Defusable Clock scene is at about 29:30. Here is a short clip showing the prop in action:



A special effects company providing services to this Warner Bros. production contacted me in August about using the Defusable Clock as a prop in the TV show. I worked with the propmaster to deliver several assembled devices quickly to the show’s shooting location in Vancouver, BC, Canada.

I then collaborated with a member of the special effects team to provide modifications to the Arduino code to better suit their needs. I doubled the display refresh rate to 976Hz to avoid flicker on camera. I removed the clock functionality and gave them better control over the timer display. I also modified the input functions so that wires connected to the terminal blocks could be used to control the timer dispaly. This way the crew could change timer from off-camera without having to touch the device.

They made a few other cosmetic changes, including removing my logo (!), but I think they did a great job using this device in the dramatic scene. It was a real thrill for me to watch!

Still frame from "The Tomorrow People"

Still frame from “The Tomorrow People”


Published by Michael, on October 31st, 2013 at 8:30 pm. Filed under: Arduino,Art. | 2 Comments |

Tabasco Bottle LED Mood Lamp

I love Tabasco and always though that their cute little miniature bottles could be the basis for a great specialty lamp. I was very happy to find that a 3mm LED fits perfectly in the bottle opening of the miniature bottles. By designing a custom circuit board I was able to make a lamp that looks like a bundle of dried peppers.




Each bottle is individually controlled with a TLC5940 16-channel PWM driver. The software running on the ATmega328 is Arduino code that implements different lighting effects. Here it is in action:

Hardware

I designed a circuit board for the ATmega328 and the TLC5940. This is basically a custom Arduino and the design files are available below in case you want to make one. The LEDs connect from the bottom of the board. I am using a 5V regulated power supply, so there’s no need for an on-board voltage regulator. I included a 6-pin serial header for easy Arduino programming and mounting holes on the corners so I can hang it from wires. I also included a tactile button in case I wanted to allow some input to control the lamp (currently unused). Also note the Tabasco logo right on the board silkscreen!




I photographed the assembly process so you could see how I constructed the lamp.


1. Make two holes in each cap using a small nail

2. Insert a 3mm LED into the cap

3. Ensure the LED is in the center

4. Connect wires by bending the leads. Make sure they don’t short!

5. Carefully solder and clip the leads.

6. Enclose in heat shrink tubing.

7. Twist wires and weave together.

8. Keep going!

9. Insert wires into underside of board.

10. Now just solder and clip the wires.


Software

Here is the Arduino code running on the lamp. I used the TLC5940 Arduino library to control the PWM driver. You can download the code from here.

#include "Tlc5940.h"

#define NPINS 16
#define NVISUALIZATIONS 6
#define NFUNCTIONS 3
#define BUTTON 2

// pointers to the current and last visualizations                                    
void (*visualization)() = rise;
void (*lastVisualization)() = rise;

// array of visualizations                                                            
void (*visualizations[NVISUALIZATIONS])() = {throb, blinkRandom, onOff, upDown, rise, fall};                        

// An array of all the functions.                                                     
int (*allFunctions[NFUNCTIONS])(float x) = {linear, sine, exponential};


// Each pin has a function associated with it.  The function determines how the brightness                                                                                 
// of the LED changes over time.                                                      
int (*function[NPINS])(float x);

// The x values for the function associated with each LED.  The x value is the current position along                                                                      
// the x axis for the function.  There are are 256 x values on the x axis.  That is, the domain of the                                                                     
// function is [0-255].                                                               
float x[NPINS];

// dx describes (for each LED) how the x value changes over time.  If dx=1, then x is increased by 1                                                                       
// for each duty cycle.  If dx = -1, it is decreased by 1.  If dx=5, then the brightness of the LED will                                                                   
// change faster according to the function because we are moving along the function curve faster.                                                                          
float dx[NPINS];

// y is the current value of the function at x.  If the function is 'linear' and x=128, then y=128.                                                                        
// If the function is 'sine' and x=10, then y=0x03.  See the sineValues array below that defines                                                                           
// the sine function.                                                                 
int y[NPINS];

// For each LED we can specify whether the x value should "wrap" around when reaching the end.                                                                             
// There are 256 possible values for x [0-255].  If we reach 255 and dx=1, then we can wrap around                                                                         
// back to x=0 if wrapFunction=true.  If false, then adding 1 to 255 keeps x=255.     
boolean wrapFunction[NPINS];

// Definition of sine function.  Array lookup executes faster than actually computing the sine.                                                                            
unsigned char sineValues[] = {
  0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
  0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
  0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
  0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c,
  0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
  0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
  0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
  0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
  0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
  0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
  0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
  0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
  0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
  0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
  0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00
};


void setup() {
  randomSeed(analogRead(5));
  pinMode(BUTTON, INPUT);
  digitalWrite(BUTTON, HIGH);
  Tlc.init(0);
}

void loop(){

  lastVisualization = visualization;
  visualization = visualizations[random(NVISUALIZATIONS)];

  // call the current visualization function                                                                                   
  (*visualization)();

}

// Execute n duty cycles.                                                                                                      
// First we call stepXAll() which adds dx to each x value.  This moves the LED along the x axis                                
// of its function.                                                                                                            
// Then, for each LED,  we compute the new y value for the current x value according to which                                  
// function (linear, sine, exponential) that is associated with the LED.                                                       
// For any LED that is supposed to be on at all (any pin with a y value greater than 0), we turn it on.                        
// A duty cycle is a loop of 256 steps executed quickly in succession.  At the beginning of the duty cycle                     
// each LED that has a value of y>0 will be on.  At each step we see if there are any LEDs that need                           
// to be turned off for the remainder of the duty cycle.  So if an LED has y=100, we will turn it off at                       
// step 100.  That LED will have been on for the first 100 steps of the duty cycle and off for the                             
// remaining 156 steps.  So it is dimmer.  If an LED has y=255, then it will remain on during the whole                        
// duty cycle and be at full brightness.  An LED with a low value y=10 will be very dim because we turn                        
// it off very early in the duty cycle.                                                                                        
// This 256 step cycle is executed n times as specified by the input parameter n.                                              

void dutyCycle(int n) {
  for(int i=0;i<n;i++) {
    stepXAll();
    computePinValueAll();
    for(int p=0;p<NPINS;p++) {
      Tlc.set(p, y[p]*16);
    }
    Tlc.update();
    delay(10);
  }
}

void stepXAll() {
  for(int p=0;p<NPINS;p++) {
    stepX(p);
  }
}

// Increment the x value for pin p by the value dx.                                                                            
// Wrap the function around back to 0 (or back to 255)                                                                         
// if wrapFunction is true for the pin p.                                                                                      
void stepX(int p) {
  x[p] = x[p] + dx[p];
  if (wrapFunction[p]) {
    if (x[p] > 255) {
      x[p] = x[p] - 256;
    } else if (x[p] < 0) {
      x[p] = x[p] + 256;
    }
  } else {
    if (x[p] > 255) {
      x[p] = 255;
    } else if (x[p] < 0) {
      x[p] = 0;
    }
  }
}


void computePinValueAll() {
  for(int p=0;p<NPINS;p++) {
    computePinValue(p);
  }
}

// Compute value y for pin p.                                                                                                  
// Apply the function by invoking it with the x value.                                                                         
void computePinValue(int p) {
  y[p] = (*function[p])(x[p]);
}


//////////////////////////////////////////////////////////////////////////////////                                             
// Mathematical Functions                                                                                                      
//////////////////////////////////////////////////////////////////////////////////                                             

int linear(float x) {
  return (int)(x + 0.5);
}

int sine(float x) {
  return sineValues[(int)x];
}

int exponential(float x) {
 return (int) (255.0 * pow(50, ((x/127.5)-2)));
}


//////////////////////////////////////////////////////////////////////////////////                                             
// Visualizations                                                                                                              
//////////////////////////////////////////////////////////////////////////////////                                             

// Do 30 blinks.  Each LED that is blinked will fade                                                                           
// quickly down to 0 according to the exponential function.                                                                    
void blinkRandom() {
  for(int p=0;p<NPINS;p++) {
    function[p] = exponential;
    wrapFunction[p] = false;
    x[p] = 0;
    dx[p] = -4;
  }

  for(int i=0;i<30;i++) {
    int p = random(0, NPINS);
    x[p] = 255;
    dutyCycle(10);
  }
}

void onOff() {
  allOff();

  for(int p=0; p<NPINS; p++) {
    x[p] = 0;
    dx[p] = 0;
    function[p] = linear;
    wrapFunction[p] = false;
  }

  int on = 0;
  int p;
  int d;
  while (on < NPINS) {
    p = random(0, NPINS);
    if (x[p] == 0) {
      on++;
      x[p] = 255;
      dutyCycle(1);
      d = 200 - (on * 10);
      delay(d);
    }
  }

  delay(1000);
  while (on > 0) {
    p = random(0, NPINS);
    if (x[p] == 255) {
      on--;
      x[p] = 0;
      dutyCycle(1);
      d = 40 + (on * 10);
      delay(d);
    }
  }

  delay(100);
}


void throb() {
  eachDown();

  float minSpeed = 0.4;
  float maxSpeed = 1.0;

  // Set random speeds for LEDs                                                                                                
  for(int i=0;i<NPINS;i++) {
    dx[i] = minSpeed + (random(0, 17) * ((maxSpeed - minSpeed) / NPINS));
  }

  for(int p=0; p<NPINS; p++) {
    function[p] = sine;
    wrapFunction[p] = true;
  }

  // Run for a while.  Each pin will throb according to sine wave.                                                             
  dutyCycle(random(500, 2000));

  eachDown();
  delay(500);
}


void fall() {
  int down[NPINS] = {5, 0, 15, 9, 11, 4, 2, 3, 7, 12, 14, 1, 8, 6, 10, 13};

  allOff();
  for(int p=0; p<NPINS; p++) {
    function[down[p]] = linear;
    wrapFunction[down[p]] = false;
    x[down[p]] = 0;
    dx[down[p]] = 8;
    dutyCycle(8);

  }
  delay(200);
  allOff();


}

void rise() {
  int up[NPINS] = {13, 10, 6, 8, 1, 14, 12, 7, 3, 2, 4, 11, 9, 15, 0, 5};

  allOff();
  for(int p=0; p<NPINS; p++) {
    function[up[p]] = linear;
    wrapFunction[up[p]] = false;
    x[up[p]] = 0;
    dx[up[p]] = 8;
    dutyCycle(8);

  }

  delay(200);
  allOff();


}
void upDown() {
 allUp();
 allDown();
}


void allUp() {
  if (lastVisualization == allUp) {
    return;
  }

  for(int p=0; p<NPINS; p++) {
    x[p] = y[p];
    dx[p] = 1.0;
    function[p] = linear;
  }
  dutyCycle(256);
}

void allDown() {
  if (lastVisualization == allDown) {
    return;
  }

  for(int p=0; p<NPINS; p++) {
    x[p] = y[p];
    dx[p] = -1.0;
    function[p] = linear;
  }
  dutyCycle(256);
}


void eachDown() {
  boolean done = true;

  if (lastVisualization == eachDown) {
    return;
  }

  for(int p=0; p<NPINS; p++) {
    x[p] = y[p];
    dx[p] = -1;
    function[p] = linear;
    wrapFunction[p] = false;
  }

  done = false;
  while (!done) {
    dutyCycle(1);
    done = true;
    for(int p=0; p<NPINS; p++) {
      if (y[p] > 0) {
        done = false;
        break;
      }
    }
  }
}

void allOn() {
  for(int p=0; p<NPINS; p++) {
    function[p] = linear;
    x[p] = 255;
    dx[p] = 0;
  }
  dutyCycle(1);
}

void allOff() {
  for(int p=0; p<NPINS; p++) {
    function[p] = linear;
    x[p] = 0;
    dx[p] = 0;
  }
  dutyCycle(1);
}

Hardware Design Files

The schematic and board design are open source hardware, so you are welcome to have a board fabricated yourself! Download the design files here.





Published by Michael, on June 8th, 2012 at 6:48 am. Filed under: Arduino,Art. | 2 Comments |