Make Realistic Fake Fireflies/Lightning Bugs for your Garden
Fireflies or lightning bugs, one of natures miracles, but not so common in the UK especially in my garden. Lets make a realistic alternative
Components and supplies
IRFZ44N Mosfet
WS2812B Copper String LED Lights (200)
TP4056 Based Charge/Boost Converter
Arduino Pro Mini 328 - 5V/16MHz
Solar Panel 2W 6V
FTDI Breakout Board
1M Variable Resistor
Resistor 10k ohm
18650 Battery Compartment
Apps and platforms
Arduino IDE
Project description
Code
Source Code for "Dusk Mode"
c_cpp
All versions downloadable from: https://robsmithdev.co.uk/youtube/fireflies.zip
1//////////////////////////////////////////////////////////////////////////////////////////////////// 2// Realistic FireFlies / Lightning Bugs // 3// Created by Robert Smith // 4// https://www.youtube.com/c/robsmithdev // 5// https://robsmithdev.co.uk // 6//////////////////////////////////////////////////////////////////////////////////////////////////// 7// Designed for ATMega328 microcontrollers. The 'sleep' code may need modifying for others 8 9#include <FastLED.h> 10#include <avr/sleep.h> 11#include <avr/wdt.h> 12 13#define MOSFET_POWER_CONTROL 4 14 15// The data pin for the LEDs 16#define LED_PIN 6 17 18// Number of LEDs on the cable 19#define NUM_LEDS 200 20 21// Fade in/out time in milliseconds 22#define FADETIME 50 23 24// Maximum number of visible flies at a time 25#define NUM_FLIES 10 26 27// An array of LED values 28CRGB leds[NUM_LEDS]; 29 30// A class to manage each fly 31class Fly { 32 private: 33 bool isYellowType; // A yellow one flashes and moves, the white ones just twinkle 34 unsigned long when; // When it should appear 35 uint16_t duration; // How long for 36 uint8_t targetLED; // Which LED to control 37 38 // For 'yellow' types 39 uint8_t counter; // How many LEDs it can move along 40 bool movementDirection; // And in which direction (true is forward) 41 42 // Returns the current brightness at this time, or -1 if the fly is 'dead' 43 uint8_t getBrightness(const unsigned long now) { 44 // Calculate elapsed time 45 long elapsed = now - when; 46 47 // Not on yet? brightness is 0 48 if (elapsed<0) return 0; 49 50 // Fade in? 51 if (elapsed < FADETIME) return map(elapsed,0,FADETIME,0,255); 52 elapsed -= FADETIME; 53 54 // Stay on during the duation 55 if (elapsed < duration) return 255; 56 elapsed -= duration; 57 58 // Fade out 59 if (elapsed >= duration) return 0; 60 return map(elapsed,0,FADETIME,255,0); 61 } 62 63 // Get the colour we should show 64 CRGB getColor(uint8_t brightness) { 65 if (isYellowType) { 66 return CRGB(brightness, brightness, 0); 67 } else { 68 return CRGB(brightness, brightness, brightness); 69 } 70 } 71 72 // Handle the dead fly, depending on which type it is 73 void handleDeadFly(const unsigned long now) { 74 // If ran out of count, or its not a yellow one 75 if ((counter==0) || (!isYellowType)) { 76 reset(now); 77 } else { 78 // Move to the next one 79 if (movementDirection) targetLED++; else targetLED--; 80 counter--; 81 when = now + 1000; // see again 1 second from now 82 } 83 } 84 85 public: 86 87 // set which type this is 88 void setYellow(bool isYellowType) { 89 this->isYellowType = isYellowType; 90 } 91 92 // Reset the fly 93 reset(const unsigned long now, const bool longerInitialDelay = false) { 94 when = now + (random(1,5) * 1000); // upto 5 seconds before used again, lower for more at once 95 if (longerInitialDelay) when += random(1,10)*1000; // this allows them to gracefully appear rather than all at once 96 97 if (isYellowType) { 98 movementDirection = random(0,2)==1; // Movement direction 99 counter = random(2,10); // Number of movements 100 101 if (movementDirection) // forward? 102 targetLED = random(NUM_LEDS/2,NUM_LEDS-counter); // To start anywhere remove the NUM_LEDS/2 103 else // backwards 104 targetLED = random((NUM_LEDS/2) + counter,NUM_LEDS); // To start anywhere remove the (NUM_LEDS/2) 105 106 duration = random(50,100); 107 108 } else { 109 // Random flash 110 duration = random(50,100); 111 targetLED = random(0,NUM_LEDS/2); // If you want these to appear anywhere, remove the /2 112 } 113 114 duration += FADETIME * 2; 115 } 116 117 118 // Run the fly 119 void run(const unsigned long now) { 120 long elapsed = now - when; 121 122 // Has animation elapsed? 123 if (elapsed > duration + FADETIME + FADETIME) { 124 // Ensure its off 125 leds[targetLED] = CRGB(0,0,0); 126 // Reset 127 handleDeadFly(now); 128 } 129 130 // Animate the LED 131 leds[targetLED] = getColor(getBrightness(now)); 132 } 133}; 134 135 136Fly flies[NUM_FLIES]; 137bool inDayMode = true; 138 139 140void prepareAllFlies(const unsigned long now) { 141 randomSeed(analogRead(A1)); // seed based off random noise as the pin isn't connected 142 143 // Reset all flies 144 for (uint8_t fly=0; fly<NUM_FLIES; fly++) { 145 // This gives us a small number of yellow ones 146 flies[fly].setYellow( fly < (NUM_FLIES/4) ); 147 flies[fly].reset(now, true); 148 } 149} 150 151void setup() { 152 delay( 300 ); 153 FastLED.addLeds<WS2811, LED_PIN, RGB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip ); 154 FastLED.setBrightness( 10 ); 155 pinMode(MOSFET_POWER_CONTROL, OUTPUT); // MOSFET control 156 digitalWrite(MOSFET_POWER_CONTROL, LOW); // Turn ON to setup 157 158 pinMode(A2, INPUT); 159 pinMode(A1, INPUT); 160 pinMode(A0, INPUT); 161 pinMode(LED_PIN, OUTPUT); 162 163 prepareAllFlies(millis()); 164} 165 166// watchdog interrupt, just used to disable the watchdog 167ISR (WDT_vect) 168{ 169 wdt_disable(); // disable watchdog 170} 171 172void loop() 173{ 174 // See if theres a small voltage coming from the solar panel 175 uint16_t voltage = analogRead(A0); 176 177 // The values here are different to prevent flicking as we move between the two 178 bool shouldBeInDayMode = voltage > 150; 179 bool shouldBeInNightMode = voltage < 100; 180 181 // Has the state changed? 182 if ((shouldBeInDayMode || shouldBeInNightMode) && (shouldBeInDayMode != inDayMode)) { 183 inDayMode = shouldBeInDayMode; 184 185 // If we went to day mode then turn the LEDs off 186 if (inDayMode) { 187 FastLED.clear(true); 188 } else { 189 prepareAllFlies( millis() ); 190 } 191 } 192 193 if (inDayMode) { 194 digitalWrite(MOSFET_POWER_CONTROL, LOW); // Turn OFF the Mosfet and thus the power to the LEDs 195 FastLED.clear(true); 196 // We want to put the chip to sleep. Interrupts are off, millis won't work, but we dont need them. 197 // We'll be using the hardware watchdog to 'wake' from sleep, enable it for an 8 second sleep cycle 198 // But we dont want the watchdog to reset the system, just wake up. So we make it call an interrupt, which we dont bother implementing 199 WDTCSR |= bit(WDCE) | bit(WDE); // This line allows changes to be made to the watchdog 200 WDTCSR = bit(WDIE) | bit(WDP0) | bit(WDP3); // enable watchdog timer interrupt (no reset), for 8 seconds delay 201 wdt_reset(); // Reset the timeout 202 // We'll put the chip into Power Down state, which can only be woken by INT0, INT1, Pin Change Interrupts, and the Watchdog Timer (we disabled BOD - brown-out detection) 203 set_sleep_mode(SLEEP_MODE_PWR_DOWN); 204 noInterrupts(); 205 sleep_enable(); 206 //sleep_bod_disable(); 207 interrupts(); 208 sleep_cpu(); // Zzzzzzzzzzzzzzzz 209 sleep_disable(); 210 } else { 211 digitalWrite(MOSFET_POWER_CONTROL, HIGH); // Turn ON the Mosfet and thus the power to the LEDs 212 // Run the firefly simulation 213 unsigned long now = millis(); 214 215 // Update each fly 216 for (uint8_t a=0; a<NUM_FLIES; a++) 217 flies[a].run(now); 218 219 // Set brightness based on the value from A2 (variable resistor between 0 and 5v) 220 FastLED.setBrightness(map(analogRead(A2),0,1023,10,255)); 221 222 // Update output 223 FastLED.show(); 224 225 // And pause 226 FastLED.delay(10); 227 } 228} 229
Source Code for "Dusk Mode"
c_cpp
All versions downloadable from: https://robsmithdev.co.uk/youtube/fireflies.zip
1//////////////////////////////////////////////////////////////////////////////////////////////////// 2// 3 Realistic FireFlies / Lightning Bugs // 4// 5 Created by Robert Smith // 6// 7 https://www.youtube.com/c/robsmithdev // 8// 9 https://robsmithdev.co.uk // 10//////////////////////////////////////////////////////////////////////////////////////////////////// 11// 12 Designed for ATMega328 microcontrollers. The 'sleep' code may need modifying for 13 others 14 15#include <FastLED.h> 16#include <avr/sleep.h> 17#include <avr/wdt.h> 18 19#define 20 MOSFET_POWER_CONTROL 4 21 22// The data pin for the LEDs 23#define LED_PIN 6 24 25// 26 Number of LEDs on the cable 27#define NUM_LEDS 200 28 29// Fade in/out time 30 in milliseconds 31#define FADETIME 50 32 33// Maximum number of visible flies 34 at a time 35#define NUM_FLIES 10 36 37// An array of LED values 38CRGB leds[NUM_LEDS]; 39 40// 41 A class to manage each fly 42class Fly { 43 private: 44 bool isYellowType; 45 // A yellow one flashes and moves, the white ones just twinkle 46 47 unsigned long when; // When it should appear 48 uint16_t duration; 49 // How long for 50 uint8_t targetLED; // Which 51 LED to control 52 53 // For 'yellow' types 54 uint8_t counter; // 55 How many LEDs it can move along 56 bool movementDirection; // And 57 in which direction (true is forward) 58 59 // Returns the current brightness 60 at this time, or -1 if the fly is 'dead' 61 uint8_t getBrightness(const unsigned 62 long now) { 63 // Calculate elapsed time 64 long elapsed = now - when; 65 66 67 // Not on yet? brightness is 0 68 if (elapsed<0) return 0; 69 70 71 // Fade in? 72 if (elapsed < FADETIME) return map(elapsed,0,FADETIME,0,255); 73 74 elapsed -= FADETIME; 75 76 // Stay on during the duation 77 78 if (elapsed < duration) return 255; 79 elapsed -= duration; 80 81 82 // Fade out 83 if (elapsed >= duration) return 0; 84 return map(elapsed,0,FADETIME,255,0); 85 86 } 87 88 // Get the colour we should show 89 CRGB getColor(uint8_t 90 brightness) { 91 if (isYellowType) { 92 return CRGB(brightness, brightness, 93 0); 94 } else { 95 return CRGB(brightness, brightness, brightness); 96 97 } 98 } 99 100 // Handle the dead fly, depending on which type it 101 is 102 void handleDeadFly(const unsigned long now) { 103 // If ran out of 104 count, or its not a yellow one 105 if ((counter==0) || (!isYellowType)) { 106 107 reset(now); 108 } else { 109 // Move to the next one 110 if 111 (movementDirection) targetLED++; else targetLED--; 112 counter--; 113 when 114 = now + 1000; // see again 1 second from now 115 } 116 } 117 118 public: 119 120 121 // set which type this is 122 void setYellow(bool isYellowType) { 123 this->isYellowType 124 = isYellowType; 125 } 126 127 // Reset the fly 128 reset(const unsigned 129 long now, const bool longerInitialDelay = false) { 130 when = now + (random(1,5) 131 * 1000); // upto 5 seconds before used again, lower for more at once 132 if 133 (longerInitialDelay) when += random(1,10)*1000; // this allows them to gracefully 134 appear rather than all at once 135 136 if (isYellowType) { 137 138 movementDirection = random(0,2)==1; // Movement direction 139 counter 140 = random(2,10); // Number of movements 141 142 if (movementDirection) 143 // forward? 144 targetLED = random(NUM_LEDS/2,NUM_LEDS-counter); // 145 To start anywhere remove the NUM_LEDS/2 146 else // backwards 147 targetLED 148 = random((NUM_LEDS/2) + counter,NUM_LEDS); // To start anywhere remove the (NUM_LEDS/2) 149 150 151 duration = random(50,100); 152 153 } else { 154 // Random flash 155 156 duration = random(50,100); 157 targetLED = random(0,NUM_LEDS/2); 158 // If you want these to appear anywhere, remove the /2 159 } 160 161 162 duration += FADETIME * 2; 163 } 164 165 166 // Run the fly 167 void run(const 168 unsigned long now) { 169 long elapsed = now - when; 170 171 // Has animation 172 elapsed? 173 if (elapsed > duration + FADETIME + FADETIME) { 174 // Ensure 175 its off 176 leds[targetLED] = CRGB(0,0,0); 177 // Reset 178 handleDeadFly(now); 179 180 } 181 182 // Animate the LED 183 leds[targetLED] = getColor(getBrightness(now)); 184 185 } 186}; 187 188 189Fly flies[NUM_FLIES]; 190bool inDayMode = true; 191 192 193void 194 prepareAllFlies(const unsigned long now) { 195 randomSeed(analogRead(A1)); // 196 seed based off random noise as the pin isn't connected 197 198 // Reset all flies 199 200 for (uint8_t fly=0; fly<NUM_FLIES; fly++) { 201 // This gives us a small 202 number of yellow ones 203 flies[fly].setYellow( fly < (NUM_FLIES/4) ); 204 205 flies[fly].reset(now, true); 206 } 207} 208 209void setup() { 210 delay( 211 300 ); 212 FastLED.addLeds<WS2811, LED_PIN, RGB>(leds, NUM_LEDS).setCorrection( 213 TypicalLEDStrip ); 214 FastLED.setBrightness( 10 ); 215 pinMode(MOSFET_POWER_CONTROL, 216 OUTPUT); // MOSFET control 217 digitalWrite(MOSFET_POWER_CONTROL, LOW); // 218 Turn ON to setup 219 220 pinMode(A2, INPUT); 221 pinMode(A1, INPUT); 222 pinMode(A0, 223 INPUT); 224 pinMode(LED_PIN, OUTPUT); 225 226 prepareAllFlies(millis()); 227} 228 229// 230 watchdog interrupt, just used to disable the watchdog 231ISR (WDT_vect) 232{ 233 234 wdt_disable(); // disable watchdog 235} 236 237void loop() 238{ 239 // 240 See if theres a small voltage coming from the solar panel 241 uint16_t voltage 242 = analogRead(A0); 243 244 // The values here are different to prevent flicking 245 as we move between the two 246 bool shouldBeInDayMode = voltage > 150; 247 bool 248 shouldBeInNightMode = voltage < 100; 249 250 // Has the state changed? 251 252 if ((shouldBeInDayMode || shouldBeInNightMode) && (shouldBeInDayMode != inDayMode)) 253 { 254 inDayMode = shouldBeInDayMode; 255 256 // If we went to day mode 257 then turn the LEDs off 258 if (inDayMode) { 259 FastLED.clear(true); 260 261 } else { 262 prepareAllFlies( millis() ); 263 } 264 } 265 266 267 if (inDayMode) { 268 digitalWrite(MOSFET_POWER_CONTROL, LOW); // Turn 269 OFF the Mosfet and thus the power to the LEDs 270 FastLED.clear(true); 271 272 // We want to put the chip to sleep. Interrupts are off, millis won't work, 273 but we dont need them. 274 // We'll be using the hardware watchdog to 'wake' 275 from sleep, enable it for an 8 second sleep cycle 276 // But we dont want the 277 watchdog to reset the system, just wake up. So we make it call an interrupt, which 278 we dont bother implementing 279 WDTCSR |= bit(WDCE) | bit(WDE); // 280 This line allows changes to be made to the watchdog 281 WDTCSR = bit(WDIE) 282 | bit(WDP0) | bit(WDP3); // enable watchdog timer interrupt (no reset), for 8 283 seconds delay 284 wdt_reset(); // Reset the 285 timeout 286 // We'll put the chip into Power Down state, which can only 287 be woken by INT0, INT1, Pin Change Interrupts, and the Watchdog Timer (we disabled 288 BOD - brown-out detection) 289 set_sleep_mode(SLEEP_MODE_PWR_DOWN); 290 291 noInterrupts(); 292 sleep_enable(); 293 //sleep_bod_disable(); 294 295 interrupts(); 296 sleep_cpu(); // Zzzzzzzzzzzzzzzz 297 sleep_disable(); 298 299 } else { 300 digitalWrite(MOSFET_POWER_CONTROL, HIGH); 301 // Turn ON the Mosfet and thus the power to the LEDs 302 // Run the firefly 303 simulation 304 unsigned long now = millis(); 305 306 // Update each 307 fly 308 for (uint8_t a=0; a<NUM_FLIES; a++) 309 flies[a].run(now); 310 311 312 // Set brightness based on the value from A2 (variable resistor between 0 313 and 5v) 314 FastLED.setBrightness(map(analogRead(A2),0,1023,10,255)); 315 316 317 // Update output 318 FastLED.show(); 319 320 // And pause 321 322 FastLED.delay(10); 323 } 324} 325
Downloadable files
Schematics
Schematics

Documentation
3D Printed Box
Optional box for the parts
https://www.thingiverse.com/thing:5438104
3D Printed Box
Optional box for the parts
https://www.thingiverse.com/thing:5438104
Comments
Only logged in users can leave comments