Introduction: ATtiny85 POV Display

About: An aspiring artist. Visit me at https://www.instagram.com/erilyth_art/

The first time I saw a POV (Persistence Of Vision) display was on a show called FAQ on TV. The POV display consisted of an oscillating shaft with 6 LED's mounted on the end of the shaft.

Since then I have always wanted to make one myself, I tried making one about 2 months ago with an oscillating shaft myself but I was not successful as the speed of the shaft was too low for the POV display to work. Now I decided to make the POV display with just a DC Motor instead of an oscillating shaft as they are much cheaper and easily available compared to the shafts.

In this instructable I will show you how to make the POV Display yourself!

This is a very simple project both on hardware and software (coding) areas. It costed me only about 5$ to make it from start to finish!

So lets get started!

Here is a video of it in action!

Note: The brightening and dimming of the LEDs in the video is due to my crappy camera, in reality its consistent and quite bright.

Step 1: Persistence of Vision and How It Works

POV stands for Persistence Of VIsion.

Persistence Of Vision is the phenomenon of the eye by which an image seen by our eye persists for about 0.04s during which any other images that we see are merged together with this image.

This phenomenon is used in the POV Display as we turn the LEDs on and off in such a way that the different images overlap each other forming letters.

For example:

The formation of the letter E with 5 LEDs;

1 2 3 <- Time

1 1 1 <- Bulb 1

1 0 0 <- Bulb 2

1 1 1 <- Bulb 3

1 0 0 <- Bulb 4

1 1 1 <- Bulb 5

Each column represents the 5 LEDs we used to make the display. Each element in the row represents the state of the LED at that given time.

So at t = 1 Bulb 1,2,3,4,5 are all on

at t=2,3 Bulb 1,3,5 are on

This way we can visually see the letter E formed by the LEDs but the time interval would be very small in milliseconds and not as given in the example.

Due to the short time intervals and the ability of the LEDs to turn on and off very quickly we can see the letter E as all the 3 images merge. As the motor is spinning, as time passes the LEDs move from one position to the next so all these images are merged together.

For more information on how this works have a look at these links:

Note: You can see in the above images how the 3 different pictures merge to form the letter E

Here is an animated gif that shows the formation of the letter E :https://www.instructables.com/files/orig/FIZ/ZDS5/HW4OQXU8/FIZZDS5HW4OQXU8.gif

Step 2: Materials and Tools Required

Materials :

1) 5 LEDs (Radioshack) (Ebay)

2) 3V Coin Cell (Radioshack)(Ebay)

3) Coin Cell Holder (Radioshack)(Ebay)

4) USB Header (a normal USB cord would do)

5) ATtiny85 (Ebay)(Atmel Sample)

6) DC Motor (Radioshack)(Ebay)

7) A Case/ Enclosure to keep the motor in

8) A piece of wood or cardboard for the LED bar

9) IC holder (Ebay)

10) Protoboard (Radioshack)(Ebay)

11) Jumper Cables (Radioshack)(Ebay)

Materials for Programming the ATtiny85 chip :

1) Arduino Duemilanove/Uno/Mega

2) 10 Micro farad Capacitor (Radioshack)

3) USB cable for the Arduino

4) Breadboard (Radioshack)(Ebay)

Tools :

1) Glue Gun

2) Soldering Iron

3) Leatherman tool (Optional)

4) Scissors

Note: Buying the materials from ebay would be cheaper but the quality would be just as bad, whereas radioshack is a bit more expensive but its worth it all for the great quality.

Step 3: Making the LED Bar

Cut out a piece of cardboard/wood in which you can mount 5 LEDs and attach it to the motor, this would be the rotating bar of the POV.

Make 5 holes in the cardboard one next to the other in which the LEDs will fit in snug. Insert the LEDs and connect all the cathodes (-) together forming a common cathode leaving the anodes separately.

Note : Diffused LEDs give the best results so its a good idea to diffuse the LEDs. You can do this by sanding the LEDs with sandpaper.

Add a coin or some sort of counter weight on the other end of the LED bar for perfect rotation when the motor is added.

Step 4: Connecting the ATtiny85

Using the given pinout, wire the IC holder to the LED bar. Using a protoboard attach 2 wires to the 3V (Pin 8) and Gnd (Pin 4) on the IC holder which will be attached to the 3V battery holder later.

Connect the common cathode to the Gnd (Pin 4) of the IC holder and the 5 anodes of the LEDs to pins 2,3,5,6,7 of the IC holder.

Glue this to the LED bar made before.

Once we program the ATtiny85 we will insert it into the IC holder. The holder also makes it easy to reprogram the ATtiny85 whenever we need to.

Once all the connections are made, you can use some hot glue to secure everything in place as the LED bar is going to be rotated everything should be firmly held together.

Step 5: Connecting the Motor and the Power Suply

Once the LED bar is ready you can attach it to the motor with some hot glue. Attach a small gear on the motor and then attach the LED bar onto the gear as shown in the images.

In order to power the motor you can use a 9V battery with a voltage regulator or you can use a USB cable to power it.

I decided to use a USB cable as I wouldn't have to run around for batteries every time one runs out of juice.

In a USB cable there are 4 wires, red, black, green, white/yellow. The green and white/yellow wires correspond to the TX and RX used for data communication with the computer. All we need are the red and black wires as they are the +5V and Gnd wires. Connect these 2 wires to the motor along with a switch and a resistor if required.

(I needed 25 Ohms for my motor but I didn't have any so I used 4 100 Ohm resistors in parallel which worked perfectly)

Step 6: Casing and Enclosure

Once you have attached the LED bar to the motor you can place the whole setup in some sort of box/can etc.

I placed the motor in a LEGO wheel's outer rubber, and then hot glued it on top of a perfume can that I had lying around. The can has a top which can be placed back on. I made a small hole on the side of the can through which the USB wire could come out and then added a few stones in for weight. Once this was done I put the top back on and made sure everything was held firmly.

Step 7: Installing Support for the ATtiny Series

In order to program the ATtiny chips with the arduino, we need to install a few things first before we get programming.

Go to https://github.com/damellis/attiny/archive/master.zip and download the "attiny-master" folder. In the folder you should see "attiny" folder. Open the Arduino sketchbook folder, you can find its location by checking in the preferences menu.

In the arduino sketchbook folder, create a folder named hardware and copy the "attiny" folder into the hardware folder.

Documents>Arduino>hardware>attiny

The attiny folder should have a boards.txt and another folder "variants".

Once you do this you can just open your Arduino IDE and in the boards menu you should see options such as ATtiny45/85/44/84 etc.

For more information visit : http://highlowtech.org/?p=1695

Step 8: Programming the ATtiny85

You will have to connect 6 pins on the ATtiny85 to the arduino and a capacitor in order to program it.

Pin connections:

  • ATtiny Pin 2 to Arduino Pin 13 (SCK)
  • ATtiny Pin 1 to Arduino Pin 12 (MISO)
  • ATtiny Pin 0 to Arduino Pin 11 (MOSI)
  • ATtiny Reset Pin to Arduino Pin 10 (RESET)

You will also have to connect the +5V and Gnd from the ATtiny to the Arduino.

Attach a 10 Micro Farad capacitor between Reset and Gnd of the Arduino.

(Refer to the diagram given above)

Once that is done, open up the Arduino IDE, and go to Tools, Board and select the appropriate board (Usually its going to be ATtiny85 (1 MHz internal clock)).

You should also select the right serial port in tools, serial port.

Once that is done go to Tools, Programmer and change it to Arduino as ISP.

When you have done all these things, copy and paste the code from below and hit Upload, then plug the ATtiny into the IC holder and watch your POV Display spring into action.

Code:

int delayTime = 1;
int charBreak = 2.1;

int LED1 = 0; int LED2 = 1; int LED3 = 2; int LED4 = 3; int LED5 = 4;

void setup() { pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); pinMode(LED3, OUTPUT); pinMode(LED4, OUTPUT); pinMode(LED5, OUTPUT); }

int a[] = {1, 6, 26, 6, 1}; int b[] = {31, 21, 21, 10, 0}; int c2[] = {14, 17, 17, 10, 0}; int d[] = {31, 17, 17, 14, 0}; int e[] = {31, 21, 21, 17, 0}; int f[] = {31, 20, 20, 16, 0}; int g[] = {14, 17, 19, 10, 0}; int h[] = {31, 4, 4, 4, 31}; int i[] = {0, 17, 31, 17, 0}; int j[] = {0, 17, 30, 16, 0}; int k[] = {31, 4, 10, 17, 0}; int l[] = {31, 1, 1, 1, 0}; int m[] = {31, 12, 3, 12, 31}; int n[] = {31, 12, 3, 31, 0}; int o[] = {14, 17, 17, 14, 0}; int p[] = {31, 20, 20, 8, 0}; int q[] = {14, 17, 19, 14, 2}; int r[] = {31, 20, 22, 9, 0}; int s[] = {8, 21, 21, 2, 0}; int t[] = {16, 16, 31, 16, 16}; int u[] = {30, 1, 1, 30, 0}; int v[] = {24, 6, 1, 6, 24}; int w[] = {28, 3, 12, 3, 28}; int x[] = {17, 10, 4, 10, 17}; int y[] = {17, 10, 4, 8, 16}; int z[] = {19, 21, 21, 25, 0};

int eos[] = {0, 1, 0, 0, 0}; int excl[] = {0, 29, 0, 0, 0}; int ques[] = {8, 19, 20, 8, 0};

void displayLine(int line) { int myline; myline = line; if (myline>=16) {digitalWrite(LED1, HIGH); myline-=16;} else {digitalWrite(LED1, LOW);} if (myline>=8) {digitalWrite(LED2, HIGH); myline-=8;} else {digitalWrite(LED2, LOW);} if (myline>=4) {digitalWrite(LED3, HIGH); myline-=4;} else {digitalWrite(LED3, LOW);} if (myline>=2) {digitalWrite(LED4, HIGH); myline-=2;} else {digitalWrite(LED4, LOW);} if (myline>=1) {digitalWrite(LED5, HIGH); myline-=1;} else {digitalWrite(LED5, LOW);} }

void displayChar(char c) { if (c == 'a'){for (int i = 0; i <5; i++){displayLine(a[i]);delay(delayTime);}displayLine(0);} if (c == 'b'){for (int i = 0; i <5; i++){displayLine(b[i]);delay(delayTime);}displayLine(0);} if (c == 'c2'){for (int i = 0; i <5; i++){displayLine(c2[i]);delay(delayTime);}displayLine(0);} if (c == 'd'){for (int i = 0; i <5; i++){displayLine(d[i]);delay(delayTime);}displayLine(0);} if (c == 'e'){for (int i = 0; i <5; i++){displayLine(e[i]);delay(delayTime);}displayLine(0);} if (c == 'f'){for (int i = 0; i <5; i++){displayLine(f[i]);delay(delayTime);}displayLine(0);} if (c == 'g'){for (int i = 0; i <5; i++){displayLine(g[i]);delay(delayTime);}displayLine(0);} if (c == 'h'){for (int i = 0; i <5; i++){displayLine(h[i]);delay(delayTime);}displayLine(0);} if (c == 'i'){for (int it = 0; it <5; it++){displayLine(i[it]);delay(delayTime);}displayLine(0);} if (c == 'j'){for (int i = 0; i <5; i++){displayLine(j[i]);delay(delayTime);}displayLine(0);} if (c == 'k'){for (int i = 0; i <5; i++){displayLine(k[i]);delay(delayTime);}displayLine(0);} if (c == 'l'){for (int i = 0; i <5; i++){displayLine(l[i]);delay(delayTime);}displayLine(0);} if (c == 'm'){for (int i = 0; i <5; i++){displayLine(m[i]);delay(delayTime);}displayLine(0);} if (c == 'n'){for (int i = 0; i <5; i++){displayLine(n[i]);delay(delayTime);}displayLine(0);} if (c == 'o'){for (int i = 0; i <5; i++){displayLine(o[i]);delay(delayTime);}displayLine(0);} if (c == 'p'){for (int i = 0; i <5; i++){displayLine(p[i]);delay(delayTime);}displayLine(0);} if (c == 'q'){for (int i = 0; i <5; i++){displayLine(q[i]);delay(delayTime);}displayLine(0);} if (c == 'r'){for (int i = 0; i <5; i++){displayLine(r[i]);delay(delayTime);}displayLine(0);} if (c == 's'){for (int i = 0; i <5; i++){displayLine(s[i]);delay(delayTime);}displayLine(0);} if (c == 't'){for (int i = 0; i <5; i++){displayLine(t[i]);delay(delayTime);}displayLine(0);} if (c == 'u'){for (int i = 0; i <5; i++){displayLine(u[i]);delay(delayTime);}displayLine(0);} if (c == 'v'){for (int i = 0; i <5; i++){displayLine(v[i]);delay(delayTime);}displayLine(0);} if (c == 'w'){for (int i = 0; i <5; i++){displayLine(w[i]);delay(delayTime);}displayLine(0);} if (c == 'x'){for (int i = 0; i <5; i++){displayLine(x[i]);delay(delayTime);}displayLine(0);} if (c == 'y'){for (int i = 0; i <5; i++){displayLine(y[i]);delay(delayTime);}displayLine(0);} if (c == 'z'){for (int i = 0; i <5; i++){displayLine(z[i]);delay(delayTime);}displayLine(0);} if (c == '!'){for (int i = 0; i <5; i++){displayLine(excl[i]);delay(delayTime);}displayLine(0);} if (c == '?'){for (int i = 0; i <5; i++){displayLine(ques[i]);delay(delayTime);}displayLine(0);} if (c == '.'){for (int i = 0; i <5; i++){displayLine(eos[i]);delay(delayTime);}displayLine(0);} delay(charBreak); }

void displayString(char* s) { for (int i = 0; i<=strlen(s); i++) { displayChar(s[i]); } }

void loop() { displayString("hello world"); }

Note : I have not written the entire code myself, I just made adjustments to an already existing code to fit my POV display and I have also adjusted different letters to make them appear better.

Step 9: Explanation of the Code

int delayTime = 1; //The delay time between sub characters 
int charBreak = 2.1; //The delay between different characters

The above 2 delay times should be adjusted as required. Try changing them until you get the desired display you want, increasing the delayTime will increase the width of each part of the letter whereas increasing the charBreak will increase the space between 2 letters.

int LED1 = 0; //Defining which pins the LEDs are attached to
int LED2 = 1;
int LED3 = 2;
int LED4 = 3;
int LED5 = 4;
void setup()
{
  pinMode(LED1, OUTPUT); //Setting LED1 as output
  pinMode(LED2, OUTPUT); //Setting LED2 as output	
  pinMode(LED3, OUTPUT); //Setting LED3 as output
  pinMode(LED4, OUTPUT); //Setting LED4 as output	
  pinMode(LED5, OUTPUT); //Setting LED5 as output
}
int a[] = {1, 6, 26, 6, 1}; // These are the integers that help us display the letter A
int b[] = {31, 21, 21, 10, 0};
int c2[] = {14, 17, 17, 10, 0};
int d[] = {31, 17, 17, 14, 0};
int e[] = {31, 21, 21, 17, 0};
int f[] = {31, 20, 20, 16, 0};
int g[] = {14, 17, 19, 10, 0};
int h[] = {31, 4, 4, 4, 31};
int i[] = {0, 17, 31, 17, 0};
int j[] = {0, 17, 30, 16, 0};
int k[] = {31, 4, 10, 17, 0};
int l[] = {31, 1, 1, 1, 0};
int m[] = {31, 12, 3, 12, 31};
int n[] = {31, 12, 3, 31, 0};
int o[] = {14, 17, 17, 14, 0};
int p[] = {31, 20, 20, 8, 0};
int q[] = {14, 17, 19, 14, 2};
int r[] = {31, 20, 22, 9, 0};
int s[] = {8, 21, 21, 2, 0};
int t[] = {16, 16, 31, 16, 16};
int u[] = {30, 1, 1, 30, 0};
int v[] = {24, 6, 1, 6, 24};
int w[] = {28, 3, 12, 3, 28};
int x[] = {17, 10, 4, 10, 17};
int y[] = {17, 10, 4, 8, 16};
int z[] = {19, 21, 21, 25, 0}; 

The 5 different integers correspond to the 5 time frames that make up each letter.

(In the 2nd step of this instructable I used 3 time frames to make up the letter E, here we use 5 time frames)

int eos[] = {0, 1, 0, 0, 0};
int excl[] = {0, 29, 0, 0, 0};
int ques[] = {8, 19, 20, 8, 0};
void displayLine(int line)
{
  int myline;
  myline = line;
  if (myline>=16) {digitalWrite(LED1, HIGH); myline-=16;} else {digitalWrite(LED1, LOW);}
  if (myline>=8)  {digitalWrite(LED2, HIGH); myline-=8;}  else {digitalWrite(LED2, LOW);}
  if (myline>=4)  {digitalWrite(LED3, HIGH); myline-=4;}  else {digitalWrite(LED3, LOW);}
  if (myline>=2)  {digitalWrite(LED4, HIGH); myline-=2;}  else {digitalWrite(LED4, LOW);}
  if (myline>=1)  {digitalWrite(LED5, HIGH); myline-=1;}  else {digitalWrite(LED5, LOW);} 
} 

This is the most important part of the code which explains what all the number we mentioned above mean,

For example let x be an integer. If x is greater than or equal to 16, then LED1 turns on, and we subtract 16 from x, now if (x-16) is greater than or equal to 8, LED2 turns on as well along with LED1, and we subtract 8, now if (x-24) is greater than or equal to 4, LED3 turns on along with LED1 and LED2 and so on.....

If at any instance it is less than the specified number, then that LED is off and we move on to the next step without subtracting any number.

Ex : Lets take 18, now it is greater than 16, therefore LED1 is on, now on subtracting 16 we have 2, 2 is less than 8 so LED2 is off, now 2 is less than 4 so LED3 is off, now 2 is greater than or equal to 2 , therefore LED4 is on, and on subtracting 2 we have 0 which is less than 1, therefore LED5 is off.

In such manner we can assign different integers to turn on and off different LEDs as we require.

Lets take the letter H for example, the integers are 31,4,4,4,31;

So first 31, it turns on all the LEDs 1 to 5, next 4 turns on only LED3, then 4 again turns on LED3 and 4 again turns on LED3 then 31 turns on all LEDs 1 to 5. In this manner when we put these next to each other as shown in step 2 of this instructable, we see the letter H.

void displayChar(char c)
{
  if (c == 'a'){for (int i = 0; i <5; i++){displayLine(a[i]);delay(delayTime);}displayLine(0);}
  if (c == 'b'){for (int i = 0; i <5; i++){displayLine(b[i]);delay(delayTime);}displayLine(0);}
  if (c == 'c2'){for (int i = 0; i <5; i++){displayLine(c2[i]);delay(delayTime);}displayLine(0);}
  if (c == 'd'){for (int i = 0; i <5; i++){displayLine(d[i]);delay(delayTime);}displayLine(0);}
  if (c == 'e'){for (int i = 0; i <5; i++){displayLine(e[i]);delay(delayTime);}displayLine(0);}
  if (c == 'f'){for (int i = 0; i <5; i++){displayLine(f[i]);delay(delayTime);}displayLine(0);}
  if (c == 'g'){for (int i = 0; i <5; i++){displayLine(g[i]);delay(delayTime);}displayLine(0);}
  if (c == 'h'){for (int i = 0; i <5; i++){displayLine(h[i]);delay(delayTime);}displayLine(0);} 
  if (c == 'i'){for (int it = 0; it <5; it++){displayLine(i[it]);delay(delayTime);}displayLine(0);}
  if (c == 'j'){for (int i = 0; i <5; i++){displayLine(j[i]);delay(delayTime);}displayLine(0);}
  if (c == 'k'){for (int i = 0; i <5; i++){displayLine(k[i]);delay(delayTime);}displayLine(0);}
  if (c == 'l'){for (int i = 0; i <5; i++){displayLine(l[i]);delay(delayTime);}displayLine(0);}
  if (c == 'm'){for (int i = 0; i <5; i++){displayLine(m[i]);delay(delayTime);}displayLine(0);}
  if (c == 'n'){for (int i = 0; i <5; i++){displayLine(n[i]);delay(delayTime);}displayLine(0);}
  if (c == 'o'){for (int i = 0; i <5; i++){displayLine(o[i]);delay(delayTime);}displayLine(0);}
  if (c == 'p'){for (int i = 0; i <5; i++){displayLine(p[i]);delay(delayTime);}displayLine(0);}
  if (c == 'q'){for (int i = 0; i <5; i++){displayLine(q[i]);delay(delayTime);}displayLine(0);}
  if (c == 'r'){for (int i = 0; i <5; i++){displayLine(r[i]);delay(delayTime);}displayLine(0);}
  if (c == 's'){for (int i = 0; i <5; i++){displayLine(s[i]);delay(delayTime);}displayLine(0);}
  if (c == 't'){for (int i = 0; i <5; i++){displayLine(t[i]);delay(delayTime);}displayLine(0);}
  if (c == 'u'){for (int i = 0; i <5; i++){displayLine(u[i]);delay(delayTime);}displayLine(0);}
  if (c == 'v'){for (int i = 0; i <5; i++){displayLine(v[i]);delay(delayTime);}displayLine(0);}
  if (c == 'w'){for (int i = 0; i <5; i++){displayLine(w[i]);delay(delayTime);}displayLine(0);}
  if (c == 'x'){for (int i = 0; i <5; i++){displayLine(x[i]);delay(delayTime);}displayLine(0);}
  if (c == 'y'){for (int i = 0; i <5; i++){displayLine(y[i]);delay(delayTime);}displayLine(0);}
  if (c == 'z'){for (int i = 0; i <5; i++){displayLine(z[i]);delay(delayTime);}displayLine(0);}
  if (c == '!'){for (int i = 0; i <5; i++){displayLine(excl[i]);delay(delayTime);}displayLine(0);}
  if (c == '?'){for (int i = 0; i <5; i++){displayLine(ques[i]);delay(delayTime);}displayLine(0);}
  if (c == '.'){for (int i = 0; i <5; i++){displayLine(eos[i]);delay(delayTime);}displayLine(0);}
  delay(charBreak);
} 

This part of the code tells the arduino to move through one time frame after another with the delay of delayTime and it cycles from i=0 (Time frame 1) to i=4 (Time frame 5) displaying each integer as it moves on.

void displayString(char* s)
{
  for (int i = 0; i<=strlen(s); i++)
  {
    displayChar(s[i]);
  }
} 

This tells the arduino to display all the characters we want one after another starting from i=0 till i=string length (number of characters).

void loop()
{  
  displayString("hello world");
} 

"hello world" is what is currently displayed, you can change it to whatever you want. Just make sure that there aren't too many characters that the POV is not able to display with a given motor speed. If you increase the motor speed and reduce the delay timings you can display more number of characters. With my motor and with the delay timings I used, I was able to display 12 characters in total including the spaces, so hello world was perfect.

Step 10: What Next?

So now that you have built a 5 LED POV Display, why not try something bigger? How about a 10 LED POV Display with an Arduino instead of an ATTiny85 (Supports only 5 outputs).

We can also make this wall mountable by adding a small hole through which you can hang it on a nail firmly.

Using some tri colour LEDs and more pins, we can give out a multicolored display!

The possibilities are endless.....

If you have made this POV Display, be sure to post a picture of it below in the comments as I would be more than happy to see them!

Update: I have attached a 3V battery holder next to the ATtiny so now the battery can be connected and disconnected easily whenever required..

Battery Powered Contest

First Prize in the
Battery Powered Contest

Green Design Contest

Participated in the
Green Design Contest

Epilog Challenge VI

Participated in the
Epilog Challenge VI

Glue Contest

Participated in the
Glue Contest