Introduction: Arduino & Neopixel Totally Derivative Fake TV

A maker named Jonathan Bush (JonBush) created an AT Tiny 85

powered Fake TV - Burglar Deterrent. This used Neopixel RGB LEDs and programming on the AT Tiny 85 chip to create a light show that, when seen through drapes or blinds at night, creates a nice illusion of someone being home watching TV. Please see his Instructable here.

Since my 7-yo son is interested in both electronics and programming, this seemed like a good simple project we could build together. So we quickly breadboarded one out using an Arduino Trinket and a strip of 8 Neopixels, all sourced from AdaFruit.com. JonBush's code compiled and ran perfectly on the Trinket.

Then we actually used the Fake TV when we went camping, leaving the thing standing guard in a bedroom window. Although the flickering light of the Fake TV was very realistic, I thought a few things could be improved, or if not improved, at least made ridiculously over complicated. So I set some goals...

Step 1: Goals

These were my goals for this project.
  1. Brighten it up. The original Fake TV (hereafter known as the oFTV) had a potentiometer (pot) to adjust the brightness. I had omitted that and hardcoded the brightness to the maximum. Still, with only 6 Neopixels in my version, it seemed a little dim.
  2. Add a timer function. When I ran the oFTV while on vacation, I put it on plugin timer. Seemed ridiculous given that the Arduino has a clock (as it turns out, it doesn't actually have a real-time clock, just a timer - learned something there).
  3. Change the "cut length" (time between lighting changes) to something more realistic than a linear distribution. The random() function returns a linear distribution, which seemed "unnatural". As it turn out there is significant interest in various directors' cut lengths and what distributions they best fit - scholarly papers on the subject even. I had assumed a normal Gaussian distribution (the famous bell curve), but most film scholars seem to think it's more of a lognormal distribution (although that remains contentious). http://www.cinemetrics.lv/cutting_on_salt.php
  4. Add an occasional fade-to-black, again to increase the realism.
  5. Stick some more controls on it. Mostly just because, you know, complications are fun.

This Instructable is my resulting dFTV (derivative Fake TV).

Step 2: Parts

The oFTV used an AT Tiny 85 controller, which is literally just a bare 8 pin chip. No little board, no LEDs, no buttons, no headers, just that itty bitty chip and a sparse few components – a beautifully minimalistic design. In my dFTV redesign I went through an Arduino Uno, then a Trinket, and finally a Pro Trinket 5 volt. Everything about my dFTV is both more expensive and more complex.

  1. Trinket Pro 5v, $9.95 from adafruit.com
  2. Perma-Proto Quarter-sized Breadboard, $8.50 for a 3-pack from adafruit.com.
  3. 16 RGB LED Neopixel compatible ring ($4.00). I did all my testing with a 12 LED Neopixel ring from adafruit.com, but my final version used this a 16 LED ring from China. Ordering from China takes weeks, but you cannot beat the price. You can order it from here.
  4. 5.5 x 2.1 mm female panel mount power jack. I had this on-hand, but should cost about a buck.
  5. 100-200 µF electrolytic capacitor. The exact value doesn’t matter, you just want a fat little capacitor there to provide some filtering on the power rails. Does need to be a higher voltage rating than your power supply. Had on-hand, <$1.
  6. 10kΩ resistor. This could actually be 20k, 30k, or higher. 10kΩ is traditional for a pull-down resistor. Had on-hand, <$1.
  7. 470Ω resistor. You could go as low as 100Ω here and be fine I think. Had on-hand, <$1.
  8. Panel mount red LED. Had on-hand, ~$1.
  9. Three 100K pots. 10K would work fine as well. I got 10 for $4.12 from China here.
  10. Small panel mount push button. Had on-hand, ~$1.
  11. 5 – 16v power supply w. 5.5mm barrel connector. I cut the B side end off of a junker printer USB cable and soldered a barrel connector onto it. That way I could just use any available 5 volt USB phone/tablet charger as my power supply.
  12. Project box to hold it. I found a lovely plastic project box with a clear top, again from China, $2.69 here.
  13. Assorted hookup wire and some solder. Because I’m doing this with a 7-yo, I used lead-free solder. Got to protect those brain cells!

Step 3: About the Circuit

The heart and brains of the dFTV is the Pro Trinket 5v ($9.95) from adafruit.com. I feel that this board is cool enough that it deserves a few words. Initially I started out with the original Trinket ($6.95) but I quickly got into trouble with it and the project became unstable, but when I upgraded to the Pro Trinket all that went away.

The Pro Trinket is almost an Arduino Uno (same chip), but on a tiny little finger-tip sized PCB. It has a micro-USB connector that can be used to both, power the board, and to program it. About the only things you lose with the Pro Trinket over the Uno is: serial output through the USB port, and pins #2 and #7 support.

One feature that the Pro Trinket has that I used in this project is a built-in 5v power regulator (150mA), so this project can run on anything from 5 – 16v. As it happens, I’m running it from a 5v USB wall-wart, but I could run it off of something else, including batteries, if I wanted to.

You do have to make some minor changes to the Arduino IDE to program the Pro Trinket. Excellent documentation and tutorials on the Pro Trinket are here on the AdaFruit website, those folks do an amazing job.

Above is the fritzing drawing of my final project. I did all my testing and debugging on an Arduino Uno with a standard breadboard. Once I had everything working like I wanted, I replaced the Uno with the Pro Trinket, moving the wires one-for-one, pin-for-pin.

Then I squished all the parts together until it fit on just ¼ of the breadboard. That let me use the AdaFruit Perma-Proto Quarter-sized PCB for the final version. I love these little PCBs (also available in ½ and full size) because they exactly replicate a standard breadboard. All you have to do is move your components over and solder them down just as they were on the breadboard.

The fritzing drawing shows the AdaFruit 12 LED Neopixel ring, and I did actually use that during prototyping and testing, but the final version uses a 16 LED Neopixel compatible ring from China. For $4 you can’t beat the price, and since I was ordering a bunch of stuff from there anyway, why not?

The 100kΩ pots are wired together point to point off of the PCB. Basically I’m using wires to extend the power rails to the three pots. This lets me reduce the number of wires snaking back and forth. The pushbutton also catches the 5v rail this way. As I mentioned, 10kΩ pots would also work fine here. I used the 100k ones because they have less current leakage across the rails.

The center connections on the pots are the wipers, and are connected to analog input pins A1, A2 and A3 respectively. The pots are acting as variable voltage dividers. The analog input pins will see a varying voltage, going from 0 – 5v, as each pot is turned.

R1 on the diagram is a 10kΩ pull-down resister connected to digital input pin 4 on the Pro Trinket, with its other side connect to the ground rail. The pushbutton is also connected to pin 4, with its other side connected to the 5v rail. If the button is not pushed, pin 4 “sees” the ground rail through the pull-down resister and reads that as logic 0. If the button is pushed, pin 4 see the full 5v from the power rail (minus a tiny bit that leaks through the pull-down resister) and reads that as 1. If the pull-down resister were not there, pin 4 would be “floating” when the button wasn’t pushed and randomness would result.

Interestingly, Arduinos have built-in pull-up resistors that can be activated by the INPUT_PULLUP mode parameter to the pinMode() function. Using that would have eliminated the external pull-down resistor. In that case, the button would be wired to the ground rail instead of the 5v rail, and its state would be reversed (1 when not pushed, 0 when pushed).

Either way of handling the button would be correct. I chose to use a pull-down resister just because I was trying to learn and understand the whole pull-up/down thing.

R3 is a current limiting resistor for the red LED activity light. The LED will light whenever pin 13 goes high. The Pro Trinket and the Uno already have a built-in LED connected to pin 13, which will light too - making my external LED both redundant and optional. The reason I have it there it to bring the LED outside of the project case so it’s easily visible to the user.

The electrolytic capacitor is there to provide some filtration on the power rails. Presumably as the Neopixel LEDs are flashing away, their current draw is fluctuating the voltage on the rails. The capacitor is a reservoir of charge that can smooth those fluctuations out. I’ve actually run this project without that capacitor, and most of the Neopixel examples on the Adafruit website also omit it. Still, having it there is a good idea. Watch the polarity! Electrolytic capacitors tend to explode when you plug them in backwards.

The 5.5mm power jack is wired to the BAT (battery) and the G (ground) pins on the Pro Trinket. This means that power goes through the built-in voltage regulator. Input voltage can be anything from 5 – 16v. The power rails on the Perma-Proto board get their regulated 5volts from the G and 5v pins. The output of the Pro Trinket is rated at 150mA. I don’t know what the Neopixel ring is drawing, but nothing seems to be getting hot on the Pro Trinket, no magic blue smoke, so I guess everything’s okay.

Step 4: The Code

This code is pretty heavily modified from what JonBush initially published. Major changes are:

  • Eliminated the use of the delay() function. In the original code the time between each cut (lighting change) was spent in a delay() state. This causes a problem reading the sensors (pots and button) since the Arduino won’t detect any events or changes until the delay() expires. It’s not such a problem for the pots, but for the button you might have to hold it down for 4 seconds before it got picked up. This code spends all of its time looping as fast as possible looking for changes in sensors, or time counters.
  • Used the multiMAP() function (from rob.tillaart@removethisgmail.com) to convert the linear distribution generated by the random() function into something else. I tried several distributions: Gaussian, lognormal, and some I just made up. I left all of them there in the comments. In the end, I’m not sure any of them made much difference, linear was probably just fine.
  • Added a timer to end the light show after a certain amount of time. Controlled by a pot.
  • Added a cut speed multiplier, controlled by a pot.
  • Added a lightshow abort, controlled by the pushbutton.
  • Added a soft reboot function after 24 hours (86,400,000 ms). This causes all the counters and the light show to restart at the same time every day.
  • Added an Activity LED on pin 13.
  • Initialized all of the various program parameters to reasonable values so that any or all of the pots, and the switch, can be eliminated.
  • The routine softReset() forces a program jump to address 0. This has the effect of resetting all the counters and restarting the code from the beginning. It’s a trick to simulate pushing the reset button. It wouldn’t compile for the Trinket, but works on the Pro Trinket and Uno. I don’t know what other Arduinos it might or might not work on.
  • Fade-to-black. This I never got around to implementing. Fail.

I also commented the code pretty completely, so read through it if you have questions.

Step 5: Code Source Listing

//DIY Fake TV
//Keep burglars at bay while you are away
//Created by Jonathan Bush
//Last Updated 3/24/2015
//Runnig on ATTiny 85
//Modified 8/4/2015 by Mark Werley for Trinket Pro
//Rewritten to avoid the use of the delay() function
//Initialized MAXBRIGHT to 255, the maximum
//Added the use of the multiMAP function to convert a uniform distribution to something nonuniform
//Added an auto-off feature so the LEDs go dark after a certain amount of time
//Added a soft reboot feature after 24 hours, so the show starts up again every day at the same time
//Added a blink on the builtin LED, so user can tell program is still running when neopixels are off

#include <Adafruit_NeoPixel.h>

#define PIN 3              //PIN 3 "runs" the NeoPixels. Works on Uno or Trinket Pro
#define ledPin 13          //PIN 13 has built-in LED for Uno or Trinket Pro
#define buttonPin 4        //PIN 4 has a button attached through a 10k pullup resister
int ledState = LOW;        //Keep track of the state of the built-in LED
int buttonState = 0;       //Keep tract of the state of the button
int endShow = false;       //True when the show is over
int POTPIN = A1;  //1st analog pot pin, used for adjusting brightness
int POTPIN2 = A2; //2nd analog pot pin, used for adjusting light show cut speed
int POTPIN3 = A3; //3rd analog pot pin, used for adjust the runtime of the show
//Neopixel library provided by Adafruit, change 1st parameter to number of LEDs in your neopixels
Adafruit_NeoPixel strip = Adafruit_NeoPixel(16, PIN, NEO_GRB + NEO_KHZ800);
int BRIGHTNESS = 0;
int RED = 0;
int BLUE = 0;
int GREEN = 0;
int TIMEDEL = 0;
int mapTIMEDEL = 0;
int MAXBRIGHT = 255;  //set MAXBRIGHT to 255, the max, if no brightness pot
int speedDivider = 1; //set speedDivider to 1, if no cut speed pot
int potval = 0;
int potval2 = 0;
int potval3 = 0;
unsigned long runTimeMillis = 0;      //How long the Fake TV light show will run in milliseconds
int           runTime = 120;          //How long the Fake TV light show will run in minutes, 2 hours if no runTime pot
unsigned long startMillis = 0;        //The sampled starting time of the program, ususally just 0 or 1 milliseconds
unsigned long previousMillis = 0;     //Remember the number of milliseconds from the previous cut trigger
unsigned long rebootTimeMillis = 0;   //How long the program will run before a soft reset/reboot happens (24 hrs = 86,400,000 ms)
unsigned long currentMillis = 0;      //How long the program has run so far
int in[] = {200, 520, 840, 1160, 1480, 1800, 2120, 2440, 2760, 3080, 3400, 3720, 4040}; //This is just linear
// int out[] = {200,392,968,2120,3272,3848,4040,3848,3272,2120,968,392,200}; //normal distribution
// int out[] = {200,392,2120,3848,4040,3848,3560,3272,2696,2120,968,392,200}; //LnNormal-ish
// int out[] = {200,250,300,400,600,1200,4040,1200,600,400,300,250,200}; //made up
int out[] = {200, 250, 300, 350, 400, 500, 600, 700, 800, 1200, 2000, 3000, 4040}; //made up #2
void setup() //Initialize everything
{
  //Initialize the NeoPix
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  pinMode(PIN, OUTPUT); //set the neopixel control pin to output
  pinMode(ledPin, OUTPUT); //set the onboard LED pin to output
  pinMode(buttonPin, INPUT); //set the button pin to input
  //Initialize the serial com, used for debugging on the Uno or Trinket Pro (w FTDI cable), comment out for production
  Serial.begin(9600);
  Serial.println("--- Start Serial Monitor SEND_RCVE ---");
  Serial.println("Serial is active");
  Serial.println();
    
  rebootTimeMillis = 24ul * 60ul * 60ul * 1000ul; //hardcode reboot in 24 hours
  startMillis = millis(); //sample the startup time of the program
}
void loop() //Start the main loop
{
  currentMillis = millis(); //sample milliseconds since startup
  if (currentMillis > rebootTimeMillis) softReset(); //When rebootTimeMillis is reached, reboot
  
// Let's read our sensors/controls
  potval = analogRead(POTPIN); //Reads analog value from brightness Potentiometer/voltage divider, comment out if not using
  MAXBRIGHT = map(potval, 0, 1023, 11, 255); //Maps voltage divider reading to set max brightness btwn 11 and 255, comment out if not using
  potval2 = analogRead(POTPIN2); //Reads analog value from cut speed Potentiometer, comment out if not using
  speedDivider = map(potval2, 0, 1020, 1, 8); //Maps the second pot reading to between 1 and 8, comment out if not using
  
  potval3 = analogRead(POTPIN3); //Reads analog valuse from show lenth Potentiometer, comment out if not using
  runTime = map(potval3, 0, 1020, 15, 480); //Maps the third pot to between 15 and 480 minutes (1/4 to 6 hours), comment out if not using
  runTimeMillis= long(runTime) * 60ul * 1000ul; 
  
  Serial.print("potval3=");
  Serial.print(potval3);
  Serial.print(" runTime=");
  Serial.print(runTime);
  Serial.print(" runTimeMillis=");
  Serial.println(runTimeMillis);
    
  buttonState = digitalRead(buttonPin);    //Sample the state of the button
  if (buttonState == HIGH) endShow = true; //Button was pressed, time to end tonight's show
  if ((currentMillis - previousMillis) > long(mapTIMEDEL)) //Test to see if we're due for a cut (lighting change)
  {
    BRIGHTNESS = random (10, MAXBRIGHT); //Change display brightness from 20% to 100% randomly each cycle
    RED = random (150 , 256); //Set the red component value from 150 to 255
    BLUE = random (150, 256); //Set the blue component value from 150 to 255
    GREEN = random (150, 256); //Set the green component value from 150 to 255
    TIMEDEL = random (200, 4040); //Change the time interval randomly between 0.2 of a second to 4.04 seconds
    mapTIMEDEL = multiMap(TIMEDEL, in, out, 13); //use the multiMap function to remap the delay to something non-uniform
    mapTIMEDEL = mapTIMEDEL / speedDivider; //Divide by speedDivider to set rapidity of cuts
    if ((currentMillis - startMillis) > runTimeMillis) endShow = true; //runTimeMillis has expired, time to end tonight's show
    if (endShow)               //Show's over for the night, aw...
      strip.setBrightness(0);
    else                       //The show is on!
      strip.setBrightness(BRIGHTNESS);
    colorWipe(strip.Color(RED, GREEN, BLUE), 0); //Instantly change entire strip to new randomly generated color
    if (ledState == HIGH)  //toggle the ledState variable
      ledState = LOW;
    else
      ledState = HIGH;
    digitalWrite(ledPin, ledState); //Flip the state of (blink) the built in LED
    previousMillis = currentMillis; //update previousMillis and loop back around
  }
}
// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait)
{
  for (uint16_t i = 0; i < strip.numPixels(); i++)
  {
    strip.setPixelColor(i, c);
    strip.show();
  }
}
// Force a jump to address 0 to restart sketch. Does not reset hardware or registers
void softReset()
{
  asm volatile("  jmp 0");
}
//multiMap is used to map one distribution onto another using interpolation
// note: the _in array should have increasing values
//Code by rob.tillaart@removethisgmail.com
int multiMap(int val, int* _in, int* _out, uint8_t size)
{
  // take care the value is within range
  // val = constrain(val, _in[0], _in[size-1]);
  if (val <= _in[0]) return _out[0];
  if (val >= _in[size - 1]) return _out[size - 1];
  // search right interval
  uint8_t pos = 1;  // _in[0] allready tested
  while (val > _in[pos]) pos++;
  // this will handle all exact "points" in the _in array
  if (val == _in[pos]) return _out[pos];
  // interpolate in the right segment for the rest
  return (val - _in[pos - 1]) * (_out[pos] - _out[pos - 1]) / (_in[pos] - _in[pos - 1]) + _out[pos - 1];
}

Step 6: Assembly

I used the Adafruit Perma-Proto for this project because it makes it so easy to move from breadboard to final version. Of course, you could also use any regular perf board, and that would be cheaper.

My project box came from China and has a transparent top, which is great for letting the Neopixel light out into the room. With an opaque box you would have to mount the Neopixels on the outside of the box.

Holes are drilled through the sides of the box for the: pots, pushbutton, activity LED, and power jack. These are connected back to the Perma-Proto board with hookup wire.

I just taped the Neopixel ring to the inside of the lid. I should think of something better than that.

Step 7: Controls and Usage

Yeah, that’s a lot of knobs. My Grandpa’s shortwave receiver had fewer dials on it. But they all do something useful(ish), and, as I said, I like complications.

  • Pot1: Adjusts brightness. Varies between 11 and 255. Is initialized to the max 255 if pot is omitted.
  • Pot2: Adjusts cut speed. Varies between 1x (RomCom) and 8x (ActionAdventure). Is initialized to 1x if no pot.
  • Pot3: Adjusts the length of the light show. Varies between 15 minutes and 6 hours. Is initialized to 2 hours if no pot.
  • Pushbutton: Aborts the current light show, but doesn’t end the program or change any settings. Useful for when the wife says, “Honey, can you turn that thing off now?”, but you don’t want to unplug it.
  • Activity LED, continues to blink after the light show ends, to let you know the program hasn’t crashed.

Once the code is loaded on your Arduino, usage is easy. Just plug it in to power and the light show will start. This establishes the start time. Turn the Brightness knob to adjust the average brightness of the show.

Turn the Cut Rate pot to change how often the Neopixels change. Set all the way up (8x), they get quite frantic; very Michael Bay.

As the dFTV is running, it’s counting down to the end of the light show. When the time for the end of the light show is reached, the Neopixels go dark, but the program continues to run and the activity LED continues to blink. Turn the Show Length pot to adjust when the show will end.

Let’s say you want the light show to run from 8PM to 10PM, just plug it in at 8 and that sets the start time. Then at 10PM you can turn back the Show Length pot until the Neopixels just go out, and that sets the end time. 24 hours after the dFTV was initially plugged in, the software resets and the light show starts up again.

Step 8: Future Upgrades

One of the things I like about this project is that the pushbutton and pots are all defined by the software, as is the Neopixel display. Want to have a tint knob? Just change the code. Want to turn the whole thing into a disco light show? Change the code again.

Adding a backup battery to keep the program running if there’s a power flicker would be a good addition.

Normally an Arduino doesn’t have a real-time clock, which is why all of my code is based on counting down from the moment power is applied. Adding a RT clock module would make programming and setting the thing much more intuitive.

Each Neopixel is individually addressable. Here they all show the same thing at the same time, but it doesn’t have to be that way. You could, for example, implement a clock face on a Neopixel ring that counts down till when the light show starts again.

Implementing a fade-to-black, which I failed to do, would also be a nice thing. Please share your code if you do.