Skill Level: Intermediate
Table Of Contents
- Introduction
- What Is Needed
- Background Information
- Constructing The Scene And Circuit
- Writing The Sketch
- Running And Testing The Simulation
- Additional Resources
- Summary
Introduction
This tutorial will show you how to use an Arduino to simulate fireflies so you can enjoy them any time of year.
A basic understanding of electronics and programming is expected along with some familiarity with the Arduino platform. If you are new to Arduino, or would just like to refresh your knowledge, please see our Blink: Making An LED Blink On An Arduino Uno tutorial before proceeding with this one.
This tutorial uses a schematic diagram to build a circuit. The All About Circuit’s Understanding Schematics and SparkFun’s How to Read a Schematic are good resources for understanding how to read schematics.
The resources created for this tutorial are available on GitHub for your reference.
What Is Needed
- Linux, macOS, Or Windows Based Computer With A USB Port
- Printer
- Arduino IDE
- Arduino Uno (Rev3 available on Arduino and SparkFun; WiFi Rev2 on Arduino and SparkFun) With Compatible USB Cable
- 9 x Male/Male Jumper Wires (available on Adafruit and Arrow)
- 9 x Alligator Clip Test Leads (available on Adafruit and SparkFun)
- 8 x Standard 5mm or 3mm Yellow LEDs (5mm available on Adafruit and SparkFun, 3mm available on Adafruit and Sparkfun)
- 8 x 330 Ω Resistors (available on SparkFun and Amazon)
- Conductive Copper Tape (available on Adafruit and Amazon)
- Masking Tape And/Or Electrical Tape (preferred for insulating electrical connections)
- Scissors
- Double Sided Tape Or Glue Stick
- Nail Or Tack
- Cardboard
Background Information
One night while watching fireflies in the backyard, my wife and I came up with the idea to create a firefly project that lit up the same way fireflies do in the summertime. She would create the design and I would create the electronics. For this project, we’ll be printing out her scene design and constructing the circuit on the back of the scene. In order to be as realistic as possible, I reviewed several sources on the internet to determine typical firefly flash timings. I did not realize that there were so many species of fireflies or that they were so different in their flash patterns. I started with some initial flash timings based on that research, but ended up fine tuning them in the sketch based on my own observations watching them in the backyard.
Depending on how you want your simulated fireflies scene to look and how permanent you want the project to be, there are a variety of options you can use for your scene and circuit. The scene can be physically propped up on its side with cardboard stabilizers or placed in a frame or shadow box for a more polished look. The electronics (LEDs and resistors) can be connected in the scene using solder, wires, conductive tape, alligator clip test leads, or some combination thereof. I chose a simpler and more temporary approach for my project, and this tutorial, using conductive tape and alligator test leads, but I am placing the scene within an 8″x10″ frame.
In addition, I wrote this tutorial using the standard Arduino Uno form factor to appeal to a wider audience. However, if you are interested in a more permanent display, one of the smaller form factors, e.g. Arduino Nano Every or Arduino Micro , along with a separate power source or battery may be more suitable for your needs. Specifically, I am using the Arduino Uno WiFi Rev2 development board connected to a macOS based computer running the desktop Arduino IDE. If you are using a different Arduino board or computer setup, the vast majority of this tutorial should still apply, however, some minor changes may be necessary.
If you need assistance with your particular setup, post a question in the comments section below and I, or someone else, can try to help you.
Constructing The Scene And Circuit
Download the printable fireflies scene graphic (shown below) from the GitHub repository for this project. There isn’t anything electronically special about this scene, you can design and use your own scene if you prefer.
Print the scene onto a piece of paper and trim the edges to your desired size. I am using a size of 8″x10″ in order to place the scene within a frame of the same size.
Cut out a piece of cardboard (e.g. corrugated cardboard, cereal box, etc.) of the same size as the paper scene and attach the scene with glue or double sided tape to the cardboard. This makes the scene sturdy enough to attach our electronics.
The circuit for the fireflies will be constructed on the back of the scene according to the schematic shown below.
Poke holes in the scene with a nail or a tack where you want your LEDs to shine through. Poke from the front of the scene so that the excess paper and cardboard are pushed to the back. Use larger holes for fireflies in the foreground and smaller holes for fireflies in the background.
Tape the bodies of all the LEDs (on their sides) with masking or electrical tape over the punched holes on the back of the scene so that the light from the LEDs shines through to the front of the scene. All the terminals of the LEDs should be free of tape (available for connections).
Use long strips of conductive copper tape to connect the cathode (shorter lead) terminals of all the LEDs together to form a common ground.
Attach a 330 Ω resistor to the scene next to the anode (unconnected longer lead) side of one of the LEDs using masking tape across the body of the resistor, leaving both of the resistor’s terminals free. Attach the closest terminal of the resistor to the LED’s anode using a short strip of conductive tape. You may need to use additional masking or electrical tape underneath the connection in order to isolate the resistor connection from the ground connection. Do this for all of the other LEDs as well.
Verify all components are securely attached to the back of the scene and that you do not have any unintended circuit connections.
The back of your scene should look similar to the following once it is complete.
Next, we will connect our scene to the Arduino. Before connecting any circuitry to your Arduino board, disconnect it from power and your computer. This avoids accidental damage during wiring.
Attach one side of 9 male-to-male jumper wires to each of the Arduino’s 8 digital GPIO pins (2 through 9) and a ground pin.
Using the alligator clip test leads, connect the open terminal of each resistor in the scene to the Arduino GPIO jumper wires, and then connect the common ground on the scene to the Arduino’s ground (GND) pin.
Once you’re happy with your fireflies scene and circuit, you can add cardboard stabilizers to the back sides of the scene or place the scene in a frame. The completed scene and circuit should look similar to the following once completed.
Once the circuit and scene are built, verify that you do not have any unintended circuit connections. You can use electrical tape around the alligator clips for more protection.
Connect your Arduino to your computer with the USB cable.
Writing The Sketch
Open the Arduino IDE and create a sketch named Fireflies with the code shown below.
#define DEBUG 1 // mode of operation; 0 = normal, 1 = debug struct Firefly { uint8_t pin; // pin controlling the LED bool isLit; // current lit status; true = on, false = off unsigned long triggerTime; // last time the firefly was lit unsigned long triggerDelay; // delay between successive firefly light times }; const uint8_t LEDs[] = {2, 3, 4, 5, 6, 7, 8, 9}; // LED pins representing the fireflies const uint8_t LEDsNum = sizeof(LEDs) / sizeof(LEDs[0]); // number of LEDs (fireflies) const unsigned long LightTime = 500; // time that a firefly is lit in milliseconds const unsigned long MinDarkTime = 5000; // minimum time that a firefly is not lit in milliseconds const unsigned long MaxDarkTime = 10000; // maximum time that a firefly is not lit in milliseconds Firefly fireflies[LEDsNum]; // array of all firefly instances void setup() { if (DEBUG) { // Serial Monitor Serial.begin(9600); // initialize serial bus while (!Serial); // wait for serial connection Serial.println(F("Running in DEBUG mode. Turn off for normal operation.")); } // Configure all fireflies for (uint8_t i = 0; i < LEDsNum; i++) { pinMode(LEDs[i], OUTPUT); digitalWrite(LEDs[i], LOW); fireflies[i].pin = LEDs[i]; fireflies[i].isLit = false; fireflies[i].triggerTime = 0; fireflies[i].triggerDelay = random(MinDarkTime, MaxDarkTime); } } void loop() { // Process all fireflies for (uint8_t i = 0; i < LEDsNum; i++) { processFirefly(fireflies[i]); } } void printFireflyStatus(unsigned long currentTime, Firefly firefly) { Serial.print(F("Firefly: currentTime = ")); Serial.print(currentTime); Serial.print(F(", triggerTime = ")); Serial.print(firefly.triggerTime); Serial.print(F(", triggerDelay = ")); Serial.print(firefly.triggerDelay); Serial.print(F(", pin = ")); Serial.print(firefly.pin); Serial.print(F(", isLit = ")); Serial.println(firefly.isLit); } void processFirefly(Firefly &firefly) { unsigned long currentTime = millis(); // Light firefly at appropriate trigger time if (!firefly.isLit && (currentTime - firefly.triggerTime >= firefly.triggerDelay)) { if (DEBUG) { printFireflyStatus(currentTime, firefly); Serial.println(F(" Turning on firefly.")); } digitalWrite(firefly.pin, HIGH); firefly.isLit = true; firefly.triggerTime = currentTime; // Turn off firefly after appropriate lit time } else if (firefly.isLit && (currentTime - firefly.triggerTime >= LightTime)) { if (DEBUG) { printFireflyStatus(currentTime, firefly); Serial.println(F(" Turning off firefly.")); } digitalWrite(firefly.pin, LOW); firefly.isLit = false; firefly.triggerDelay = LightTime + random(MinDarkTime, MaxDarkTime); } }
The first line defines the debugging mode of the sketch. If enabled (DEBUG
is set to 1
), then a serial connection is established (lines 22-23) and debugging messages are displayed within the Serial Monitor. Set DEBUG
to 0
for normal operation.
The Firefly
structure, defined on lines 3-8, holds the relevant information required for each distinct firefly instance that we want to simulate. The pin
member contains the GPIO pin that will be used to control the LED for that specific firefly instance. isLit
holds the status of whether the firefly is currently lit or not. The triggerTime
member keeps track of the last time (in milliseconds) the firefly was turned on. And finally, triggerDelay
holds the delay (also in milliseconds) between the time the firefly was last lit to the next time that the firefly will be lit.
The LEDs
array, defined on line 10, specifies all of the GPIO pins that will be used to control the connected LEDs representing our fireflies. The sketch was written to take into account a variable number of GPIO pins. So if you want to use fewer or more pins to simulate your fireflies, the sketch can handle that. For instance, the next line automatically determines the number of LEDs used and stores that number in the LEDsNum
constant that is used throughout the sketch. I am using 8 GPIO pins in this sketch, the same number as the background scene, to demonstrate sufficient firefly activity. If you increase the number of LEDs, ensure the board you are using can handle the extra current load.
Lines 13-15 define the firefly flash timings. As mentioned previously these numbers are based on research and observation. LightTime
specifies how long each firefly will remain lit in milliseconds; a fixed half a second in this case. The MinDarkTime
and MaxDarkTime
constants define the minimum and maximum amount of time that each firefly will not be lit; defined here as somewhere between 5 and 10 seconds. These timing constants can be adjusted to suit your own needs or observations.
Line 17 defines the full array of individual Firefly
structures that are then initialized for use within the setup()
routine (lines 28-35). As you can see on line 34, I am randomizing the trigger delay value to be somewhere between MinDarkTime
and MaxDarkTime
.
The loop()
routine simply processes each firefly in turn by calling the processFirefly()
function on each firefly instance.
The processFirefly()
function, defined on lines 58-79, is the heart of the sketch. It compares the current time against the firefly timing constants to determine the appropriate times to turn on or off the firefly instance along with its associated LED. The isLit
, triggerTime
, and triggerDelay
firefly instance members are then updated as needed in preparation for the next time that the firefly will be processed. If the debugging mode is enabled, the printFireflyStatus()
function, defined on lines 45-56, is utilized to print the current time and status of the firefly instance upon each change of status.
If there is something that needs further explanation, please let me know in the comment section and I will try to answer your question.
Verify (Main Menu > Sketch > Verify/Compile) your code and fix any errors that are reported.
Save your program when you are done editing.
Running And Testing The Simulation
Now that our scene and circuit are built and our software is written, it is time to run and test our simulated fireflies.
Open the Serial Monitor (Main Menu > Tools > Serial Monitor) so that we can see the program’s output. Upload (Main Menu > Sketch > Upload) the sketch to the board and you should see all of the fireflies lighting up within the scene along with each firefly’s status change being printed within the Serial Monitor (if the debugging mode is enabled).
The following video shows my simulated scene.
Working Fireflies Simulation
If you are interested, try adjusting some of the firefly timing constants and watching how those changes affect your simulation.
The flash pattern and timings that I used for my simulated fireflies may be different from the firefly species you have in your neck of the woods. If you are feeling particularly ambitious, perhaps you can try to implement some of the more complex firefly flash patterns as described within the Talk Like a Firefly by Science Friday and Firefly Flash Patterns by the United States National Park Service articles.
Before we end, now would be a good time to upload the BareMinimum sketch (Main Menu > File > Examples > 01.Basics > BareMinimum) to reset all GPIO pins back to their default states. This ensures no outputs are being driven when plugging in your board for your next project.
Additional Resources
The following is a list of additional resources you may find helpful.
- Firefly on Wikipedia
- Talk Like a Firefly by Science Friday
- Firefly Flash Patterns by the United States National Park Service
Summary
In this tutorial, we
- built a scene with LEDs to display simulated fireflies,
- connected the scene’s LEDs to an Arduino,
- wrote a sketch to simulate the firefly flash timings of the LEDs, and
- ran and tested the sketch to display our simulated fireflies.
The final source code, schematic, and scene used for this tutorial are available on GitHub. The GitHub version of the code is fully commented to include additional information, such as the program’s description, circuit connections, and other details. The comments are also Doxygen compatible in case you want to generate the code documentation.
Thank you for joining me on this journey and I hope you enjoyed the experience. Please feel free to share your thoughts or questions in the comments section below.
This tutorial is provided as a free service to our valued readers. Please help us continue this endeavor by considering a donation.
Great tutorial thanks. Is there a way to start lights asynchronous and have it change to be synchronous based on proximity (people, objects, etc) ?
Thank you, I’m glad you liked the tutorial.
If you are wanting the fireflies to sync up their timings when a person or object is close by, you could include a distance sensor (Interfacing Ultrasonic Distance Sensors With An Arduino Uno) and process the fireflies manually when an object is in range. Something like the following should work.
I’m a code nooby and really appreciate your code above. It saved me a LOT of time!
Glad to hear it. Thanks for reading.
John, I would like to sweep a servo to provide movement for my fireflies, but cannot seem to get the code to work on pin 9. Could you help me get the syntax right? I keep getting errors. Thanks in advance.
#include
Servo myservo; // create servo object to control a servo
int pos = 0; // variable to store the servo position
void setup() {
myservo.attach(9); // attaches the servo on pin 9 to the servo object
}
void loop() {
for (pos = 0; pos = 0; pos -= 1) { // set length of sweep
myservo.write(pos); // tell servo to go to position in variable ‘pos’
delay(15); // waits for the servo to reach the position
}
}
Sure, I’ll try to help.
I see a few things I would suggest changing.
– The
#include
line does not actually include the servo library, i.e.#include <Servo.h>
.– When calling the
attach()
method, include the optional minimum and maximum positions so as not to accidentally push your servo beyond its capability. I like to start withmyservo.attach(9, 1000, 2000)
and adjust (calibrate) it later if necessary.– Your
for
loop is not quite right. The middle parameter should be an expression, e.g.for (pos = 0; pos < 180; pos += 1)
.In addition, check out my Controlling A Servo Motor With An Arduino tutorial, it might provide you some assistance.
Let me know how that goes.
Thanks! Based on your directions, I came up with the following (see below). It almost worked, but sweepOperations() in the LOOP caused the firefly LEDs to not run in a proper manner. They are lit for too long, and several LEDs light up at the same time. They no longer look like fireflies. If I comment out sweepOperations(), it reverts back to proper operation (but no servo sweep)
Note: In the firefly code, I removed the LED access to pin 9 since that was designated for the servo.
/* Multiple Fireflies
https://www.woolseyworkshop.com/2022/09/09/simulating-fireflies-with-an-arduino/
by John Woolsey
*/
#include // get servo library
uint8_t ServoA = 9;
Servo servoA;
struct Firefly {
uint8_t pin; // pin controlling the LED
bool isLit; // current lit status; true = on, false = off
unsigned long triggerTime; // last time the firefly was lit
unsigned long triggerDelay; // delay between successive firefly light times
};
const uint8_t LEDs[] = {2, 3, 4, 5, 6, 7, 8}; // LED pins representing the fireflies
const uint8_t LEDsNum = sizeof(LEDs) / sizeof(LEDs[0]); // number of LEDs (fireflies)
const unsigned long LightTime = 1000; // time that a firefly is lit in milliseconds
const unsigned long MinDarkTime = 15000; // minimum time that a firefly is not lit in milliseconds
const unsigned long MaxDarkTime = 25000; // maximum time that a firefly is not lit in milliseconds
Firefly fireflies[LEDsNum]; // array of all firefly instances
void setup() {
// configure servo
Serial.begin(9600);
while (!Serial);
servoA.attach(ServoA, 600, 2000); // assign the position of 0 and 180 degrees
// Configure all fireflies
for (uint8_t i = 0; i < LEDsNum; i++) {
pinMode(LEDs[i], OUTPUT);
digitalWrite(LEDs[i], LOW);
fireflies[i].pin = LEDs[i];
fireflies[i].isLit = false;
fireflies[i].triggerTime = 0;
fireflies[i].triggerDelay = random(MinDarkTime, MaxDarkTime);
}
}
void loop() {
// Process all fireflies
for (uint8_t i = 0; i = firefly.triggerDelay)) {
digitalWrite(firefly.pin, HIGH);
firefly.isLit = true;
firefly.triggerTime = currentTime;
// Turn off firefly after appropriate lit time
} else if (firefly.isLit && (currentTime – firefly.triggerTime >= LightTime)) {
digitalWrite(firefly.pin, LOW);
firefly.isLit = false;
firefly.triggerDelay = LightTime + random(MinDarkTime, MaxDarkTime);
}
}
void servoSweep(
uint8_t startAngle,
uint8_t stopAngle,
uint8_t stepAngle = 1,
unsigned long stepTime = 25) {
if (startAngle < stopAngle) {
for (int angle = startAngle; angle = stopAngle; angle -= stepAngle) {
servoA.write(angle);
delay(stepTime);
}
}
}
void sweepOperations() {
servoSweep(0, 180);
delay(500);
servoSweep(180, 0);
delay(500);
}
I didn’t review all of your code, but I do believe I see the issue. The firefly timings are managed by comparing times and processing a firefly if a certain amount of time has elapsed. They will not function properly when using the
delay()
function as that function will stop all other code from running during the delay. I suggest using something like the following for your sweep code.and call that function within your
loop()
function. This code will continuously sweep the servo between 0 and 180 degrees.It also appears that your code is being interpreted as HTML code and not printing all of the characters properly. Try placing a <pre> tag before your code and a </pre> tag after your code for it to show up properly.
Hopefully, this helps you along your way. It is a great idea to have a firefly move as well.
The
sweepOperations()
function I provided previously works forstepAngle
s that evenly divide into the 180 degree range, but may drive the servo beyond its capabilities otherwise. It is better to include the following line just after thestepAngle = -stepAngle;
line to make sure that does not happen.I also edited my previous comment to include it.
Thanks John, I was getting an error, so in my noobieness I tried to fix it. It said that myservo was not declared, and my previous code had “servoA” to ID it. I removed all the servoA lines and added what I thought needed to go in to make it happy. It IS happy with my code below, but the servo is not sweeping. Everything that I put in is marked in the comments with “***”, so you can let me know where I went wrong.
#include // *** load servo library
Servo myservo; // *** create servo object
struct Firefly {
uint8_t pin; // pin controlling the LED
bool isLit; // current lit status; true = on, false = off
unsigned long triggerTime; // last time the firefly was lit
unsigned long triggerDelay; // delay between successive firefly light times
};
const uint8_t LEDs[] = {2, 3, 4, 5, 6, 7, 8}; // LED pins representing the fireflies
const uint8_t LEDsNum = sizeof(LEDs) / sizeof(LEDs[0]); // number of LEDs (fireflies)
const unsigned long LightTime = 1000; // time that a firefly is lit in milliseconds
const unsigned long MinDarkTime = 15000; // minimum time that a firefly is not lit in milliseconds
const unsigned long MaxDarkTime = 25000; // maximum time that a firefly is not lit in milliseconds
Firefly fireflies[LEDsNum]; // array of all firefly instances
void setup() {
myservo.attach(9); // *** attaches servo object to pin 9
// Configure all fireflies
for (uint8_t i = 0; i < LEDsNum; i++) {
pinMode(LEDs[i], OUTPUT);
digitalWrite(LEDs[i], LOW);
fireflies[i].pin = LEDs[i];
fireflies[i].isLit = false;
fireflies[i].triggerTime = 0;
fireflies[i].triggerDelay = random(MinDarkTime, MaxDarkTime);
}
}
void loop() {
// Process all fireflies
for (uint8_t i = 0; i = firefly.triggerDelay)) {
digitalWrite(firefly.pin, HIGH);
firefly.isLit = true;
firefly.triggerTime = currentTime;
// Turn off firefly after appropriate lit time
} else if (firefly.isLit && (currentTime – firefly.triggerTime >= LightTime)) {
digitalWrite(firefly.pin, LOW);
firefly.isLit = false;
firefly.triggerDelay = LightTime + random(MinDarkTime, MaxDarkTime);
}
}
// sweep the servo
void sweepOperations() {
static unsigned long previousTime = 0;
unsigned long currentTime = millis();
unsigned long stepTime = 25;
static int stepAngle = 1;
static int angle = 0;
if (currentTime – previousTime >= stepTime) {
angle += stepAngle;
if (angle = 180) {
stepAngle = -stepAngle;
}
myservo.write(angle);
previousTime = currentTime;
}
}
Got it working. Actually, not sure how. The good news now is that I have 8 fireflies and a sweeping servo to get them to “fly about”. Thank you so much for your direction! Here is the final code:
/* Multiple Fireflies & Servo Sweep
https://www.woolseyworkshop.com/2022/09/09/simulating-fireflies-with-an-arduino/
by John Woolsey
*/
#include // load servo library
Servo myservo; // create servo object
struct Firefly {
uint8_t pin; // pin controlling the LED
bool isLit; // current lit status; true = on, false = off
unsigned long triggerTime; // last time the firefly was lit
unsigned long triggerDelay; // delay between successive firefly light times
};
const uint8_t LEDs[] = {2, 3, 4, 5, 6, 7, 8}; // LED pins representing the fireflies
const uint8_t LEDsNum = sizeof(LEDs) / sizeof(LEDs[0]); // number of LEDs (fireflies)
const unsigned long LightTime = 1000; // time that a firefly is lit in milliseconds
const unsigned long MinDarkTime = 15000; // minimum time that a firefly is not lit in milliseconds
const unsigned long MaxDarkTime = 25000; // maximum time that a firefly is not lit in milliseconds
Firefly fireflies[LEDsNum]; // array of all firefly instances
void setup() {
myservo.attach(9, 1000, 2000); // attaches servo object to pin 9
// Configure all fireflies
for (uint8_t i = 0; i < LEDsNum; i++) {
pinMode(LEDs[i], OUTPUT);
digitalWrite(LEDs[i], LOW);
fireflies[i].pin = LEDs[i];
fireflies[i].isLit = false;
fireflies[i].triggerTime = 0;
fireflies[i].triggerDelay = random(MinDarkTime, MaxDarkTime);
}
}
void loop() {
// Process all fireflies
for (uint8_t i = 0; i = firefly.triggerDelay)) {
digitalWrite(firefly.pin, HIGH);
firefly.isLit = true;
firefly.triggerTime = currentTime;
// Turn off firefly after appropriate lit time
} else if (firefly.isLit && (currentTime – firefly.triggerTime >= LightTime)) {
digitalWrite(firefly.pin, LOW);
firefly.isLit = false;
firefly.triggerDelay = LightTime + random(MinDarkTime, MaxDarkTime);
}
}
// sweep the servo
void sweepOperations() {
static unsigned long previousTime = 0;
unsigned long currentTime = millis();
unsigned long stepTime = 15;
static int stepAngle = 1;
static int angle = 0;
if (currentTime – previousTime >= stepTime) {
angle += stepAngle;
if (angle = 180) {
stepAngle = -stepAngle;
angle += stepAngle;
}
myservo.write(angle);
previousTime = currentTime;
}
}
Glad to hear you got it working. Just a reminder, your code does not come through properly without surrounding it with <pre> and </pre> tags.
FYI, I did put the tags. Not sure how I can fix that in my text-only comment box?
I appreciate you trying. I tried a few different ways myself to display code as a user and I could not get it to work either without specifically replacing the < and > characters within the code with their entity codes of < and >. I am able to display code as an administrator but not as a user. I guess we learn something new every day. If anyone else knows how to submit code in user generated comments, please let us know.