You can do anything with blinking LEDs, and in this simple project, we’ll create a small device that lights up in the air when you wave it back and forth. It’s easier than you think, and the total parts cost is less than $5.

This project uses an optical illusion called persistence of vision to make text appear in the air. You’ve probably come across this phenomenon when someone quickly moved a lit sparkler at night. We will be using an Arduino and some LEDs to simulate this effect. This project is easy to build, with a few components and a little bit of soldering (although if you’d like tips on getting started with a soldering iron, check out this guide.).

You will need

  • Arduino (I used a $2 Nano because it’s small and cheap, but any model should work).
  • 5x red LEDs .
  • 5x 220 ohm resistors.
  • 1x 9V battery holder.
  • 1x rocker switch.
  • 1x prototype part.
  • Case for the finished product (I used a small cardboard box painted black).

You will also need a soldering iron and some solder to put it all together.

Although not required, some solder wicks can be very helpful in case of soldering errors. A breadboard and some connecting wires are also useful for checking your circuit.

Components required for the project


Wiring couldn’t be easier. First connect the Arduino, LEDs and resistors as shown in this schematic.

POV Fritzing

Pins D2-D6 go to each resistor, respectively, which are attached to the positive anode (long leg) of each LED. The cathode is connected to ground, which is connected to the Arduino. When it’s done, it should look something like this:

POV layout

That’s all: our scheme is ready! We’ll add the battery case and switch to it later, but that’s all we need to see if it works.

The code

Now connect your Arduino to your computer and open the Arduino IDE. If you are new to using Arduino and want a good tutorial on how to go to this link will be helpful.

Open a new sketch and copy this code in. The code has been modified from this project and you can download it in its entirety in this Gist.

// how long each led stays on for int delayTime = 1; //how long between each int charBreak = 3; //how long to wait after the message is finished before it repeats int resetTime = 20; int LED1 = 2; int LED2 = 3; int LED3 = 4; int LED4 = 5; int LED5 = 6; 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}; int space[] = {0, 0, 0, 0, 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);} if (c == ' '){for (int i = 0; i <5; i++){displayLine(space[i]);delay(delayTime);}displayLine(0);} delay(charBreak); } void displayString(char* s){ for (int i = 0; i<=strlen(s); i++){ displayChar(s[i]); } } void loop(){ //Change the text inside here to change the text displayed. //Make sure to keep the brackets and the quotation marks. displayString("..hello!.."); delay(40); } 

Save the sketch and upload it to your board. If you get any errors, make sure you select the correct board and com port. Your LEDs should begin to flicker. If they don't, check all your connections on the breadboard. The integers declared at the beginning of the code determine how quickly the text is displayed. The higher the number, the faster you will have to swing the device. I found that the numbers used here worked well.

Now to do a quick test. Disconnect the Arduino from the USB cable and attach the battery holder to the VIN and GND pins of your Arduino.

Pv Fritz with Batt

Now that you can move it around, carefully try turning the LEDs in front of you. A few letters should appear in the air if it's dark enough around you. I found that it helped set up my phone to capture this.

bread test

It is working! Now to make it a little more permanent.

Irons ready!

Put the Arduino board, resistors and LEDs on your board like this:

laid out on board

Of course, you can make the layout as compact as you want, as long as the layout stays the same. Make sure you leave at least one protocard line on each side of your Arduino for the components and power supply. If you are new to electronics, here are some helpful tips for you to

We will solder the LED cathodes in one line to create a common language, just like we did when we made our LED matrix.

row cathodes

Fold each cathode so that it overlaps the next and solder them to create one continuous line. Now we need to solder our anodes to the legs of the resistors next to them. I found the easiest way to do this is to solder each leg to its respective shoe and then place an iron between them to heat up both joints before finally adding more solder to bridge the gap between them.

Check each connection to make sure it's secure, then use wire cutters to cut off any extra legs from the resistors and LEDs.

resistors to anodes

Now let's repeat the same task, but this time by connecting the other end of our resistors to the Arduino legs. This is the hardest part of soldering. Each resistor is strongly attached to one pin of the Arduino, otherwise the circuit will not work as intended. I made a few mistakes here and had to use a solder wick to remove the unwanted solder a few times.

Once you have established all the correct connections, double check that none of them intersect with their neighbors. If you have access to a multimeter, you can use continuity mode to check that each pin only has a continuous line to its resistor. If you only have a multimeter, this article has a great getting started guide.

continuity pattern

After you have checked each connection, cut off any excess length from the legs of the resistors.

Adding strength

Now run a piece of wire from the GND pin of your Arduino to the hole near where your solder cathode line ends and place the black wire of your battery holder next to it.

top of gnd wire

Now solder a wire to the GND pin, similar to how we did the resistors, and solder both wires to the end of the cathode line to create a common ground for the circuit.

Finally, attach a wire to the VIN pin of your arduino and place a switch between this wire and the battery. Apologies for the hot glue switch in this photo, I had to disassemble the finished car to photograph this step!

wine switch

Once it's all soldered together, insert the battery and hit the switch. Congratulations! The scheme is done. If you don't see anything light up, carefully check each solder joint to see where there might be an open or short.


Now that we have this working, let's put it in a box. A painted cardboard box was used here, though you could make something more permanent using a box bought from a hobby store or even an old dish box. For more information, you can even print your 3D on one of these cheap 3D printers.

Cut your prototype down to the size you want by nailing it down and carefully removing unnecessary details.

proto gif

Place your circuit in the case and mark where the LEDs and switch should go. Optionally, you can also mark where the USB connector on the Arduino board meets the edge so you can plug it in to change the text without opening the box. Drill or cut the desired holes in the housing.

make holes

Now add some hot glue to the edges of the LEDs and glue them in place. I glued a piece of sponge under the outline so that it fits snugly in the box. Attach your switch to the case. I used hot glue, although screws or bolts would have made a much stronger connection. You can also glue the battery holder to the back of the PCB so it doesn't move around inside while using the cell, but make sure any metal contacts on the case don't touch your solder joints!

measurement switch

Work is done!

Close your case and you're done! To change the text, connect the USB cable to the Arduino board and change the text in brackets after displayString in method loop . Find a dark place and have fun!


Have you created POV machines before and know how to improve the design? Are you planning to build one yourself? Tell us about your project in the comments below, we'd love to hear it!

Похожие записи