LED Candle - Lights, Flickers, Blows Out, Smokes, and Smells

by keith204 in Circuits > Arduino

47778 Views, 313 Favorites, 0 Comments

LED Candle - Lights, Flickers, Blows Out, Smokes, and Smells

Candle-Part-1-Thumb2.jpg
My Post.jpg
LED-Candle-Cover.jpg
TIMELINE.00_03_01_20.Still030-2.jpg
TIMELINE.00_00_07_08.Still022.bmp
TIMELINE.00_09_40_10.Still026.jpg
TIMELINE.00_06_29_28.Still016.jpg
TIMELINE.00_09_30_00.Still025.jpg
TIMELINE.00_12_20_19.Still003.jpg

About This Candle & How to Read This Instructable

Here's my take on a LED candle that offers the full experience of a real candle. Start to finish.

Be sure to watch the YouTube video below!

Background:

I bought my first Arduino about 3 years ago with a LED candle project in mind. I wanted to make a candle that you could light with a match, then blow it out. I've been fine-tuning various aspects of it ever since. About a month ago, I realized that adding smoke was a very real possibility. Since then, I've been finalizing the candle, recording videos, and preparing content for this Instructable.

How To Read This Instructable

This candle is a very technical candle, so you and I must proceed carefully for the sake of clarity.

[ Brackets ] will surround secondary information like tips, tricks, and details that are totally OK to skip your first time through this. Then, if you end up actually doing this Instructable, I think you'll find the content in those brackets worth reading.

I Won't Micromanage Your Soldering Tip

I built this candle around various things I had available. Notably, the vase. Chances are, my vase is a different size than your vase. Therefore, I'm not going to tell you exactly how to lay out the components and where to solder what. But I will give you clear Fritzing diagrams so you have a clear idea of what connections you need to make.

[Hopefully this helps keep this content insightful while reducing the inapplicable.]

See the brackets? You would have been totally OK skipping over that.

You Like Videos? Me too.

I uploaded this 12-minute video to YouTube as a supplement to this Instructable: Be sure to check it out.

Additional Resources

GitHub Repo

KeithsTestGarage.com - additional details will be posted here over time.

Three Milestones

Milestones.jpg
TIMELINE.00_09_30_00.Still025.jpg
TIMELINE.00_05_35_21.Still021.jpg

The whole project is pretty big. I'm splitting it into 3 bite-sized milestones as is very natural, as well as providing a Fritzing diagram and full code for each one. This should be less daunting for both of us.

Milestone 1: Built-In LED

  • Lighting with a match
  • Blowing out with your breath

Milestone 2: + NeoPixels

  • Lighting with a match
  • Blowing out with your breath
  • Full NeoPixel implementation

Milestone 3: + Smoke

  • Lighting with a match
  • Blowing out with your breath
  • Full NeoPixel implementation
  • Smoke

Gather Parts

IMG_9106.jpg
IMG_9103.jpg
IMG_9119.jpg
TIMELINE.00_05_46_13.Still019.jpg
IMG_9107.jpg

Core Parts

I figure you have a lot of parts on hand, and I'm having trouble trying to figure out how granular to be with my parts list. I think rather than trying to psychoanalyze you from afar, I'll just post links to the items I bought. Then, you can decide what you need, what you don't, and where to deviate from my list.

Milestone 1

Milestone 2

Milestone 3

Project Etc.

  • Fiberglass PCB boards. I like this variety kit. Good range of sizes and cheap enough to use them without worrying too much about the cost.
  • Connectors - I used a mixture of connectors, but, in the future, I'd definitely only use spring terminals for everything. They pull apart and combine into longer strips. (that would have saved me a lot of desoldering.)

[* About the Arduino: I'm listing an Arduino Uno, but my photos show an Arduino Pro MIni. They have the same pin mappings. I figure you'll probably build it on an Uno as I did, and then move to something smaller when you're ready.]

[** I used a IRFZ44N in the pics/video, which seems to work great, but it's not ideal. Get the P30N06LE instead.]

[Notice: I use affiliate links. Here is my stance on how I will and won't use them. In a nutshell, I'm not going to let affiliate revenue dictate what content I create. Any revenue from your clicks will go towards an oscilloscope, because I can't seem to rationalize pulling $500 out of our regular envelopes for one.]

Milestone 1: Simple Test

2019-03-04 18_21_06-Untitled Sketch.fzz_ - Fritzing - [Breadboard View].jpg

This is a simple step to help you configure the IR and microphone sensors. Once done, you'll be toggling on and off that built-in LED in style.

Note for Newbies

I expect this project to be for more advanced users, so I don't plan on giving entry level advice about how to use an Arduino. But, this step is pretty straightforward, so if you're here for the fun, I'd recommend going through this guide first: https://www.arduino.cc/en/Guide/ArduinoUno.

Then, come on back!

The Code

[ View prettier code at: https://github.com/keith204/KeithsLEDCandle ]

I've attached this file to this Instructable.

<p>// FIRST TEST OF MIC AND FLAME SENSOR<br>// Keith Kelly 2019-03-04 <a href="http://www.KeithsTestGarage.com"> www.KeithsTestGarage.com
</a>
// Milestone 1 of 3</p><p>#define MIC_PIN A1   // Microphone Pin
#define FLAME_PIN A3 // IR Sensor Pin</p><p>#define FLAME_DURATION 2000 // millis- duration of flame before lighting candle
#define FLAME_THRESHOLD 800 // 0...1023 - analog reading of FLAME value to detect
#define MIC_THRESHOLD 950 // 0...1023 - analog reading of MIC value to detect</p><p>// Vars to keep track of things
bool smoking = false;
bool flaming = false;</p><p>bool lit = false;
int flame_val = 0;
int mic_val = 0;</p><p>// Vars for tracking pseudo-async times
unsigned long previousMillis = 0;        // will store last time LED was updated
unsigned long flameStartedMillis = 0;    // will store last time LED was updated</p><p>void setup(){</p><p>    Serial.begin(9600); // uncomment this to figure sensor readings.</p><p>    // initialize pins
    pinMode(FLAME_PIN, INPUT);
    pinMode(MIC_PIN, INPUT);
    pinMode(LED_BUILTIN, OUTPUT); </p><p>    digitalWrite(LED_BUILTIN, LOW); // Turn OFF built-in LED</p><p>    blowOutCandle();  // Make sure candle is off (but don't smoke)</p><p>}</p><p>void loop(){
    checkSensors(); // Check for fire and strong winds
}</p><p>// Checks for fire and strong winds every 50 milliseconds
void checkSensors(){
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= 50) { // only check every 50ms
    flame_val = analogRead( FLAME_PIN );
    mic_val = analogRead( MIC_PIN );
    previousMillis = currentMillis;
    
    //  Uncomment below to check sensor readings.  
    Serial.print(mic_val);
    Serial.print(" : ");
    Serial.println(flame_val);
  }</p><p>  if (lit && mic_val > MIC_THRESHOLD){ // MIC REGISTERED SOUND.  BLOW OUT.
    blowOutCandle(); // Turn off light (and start SMOKING!)
    lit = false;
  }</p><p>  if (!lit){ // if candle isn't lit
    if (flame_val > FLAME_THRESHOLD)  { // if flame is detected
        
        // record the starting time (if flame not yet detected)
        if (!flaming){  // if not flaming
          flaming = true; 
          flameStartedMillis = millis();  // record time that flaming begins
        }
        else if (checkFlame()){ // has flame been flaming long enough?          
          lightCandle(); // ok finally light it up. 
          lit = true; 
        }
    }
    else{ // no flame detected
      flaming = false; 
    }    
  }  
}</p><p>// Check if flame has been detected long enough.
bool checkFlame(){    
  unsigned long currentMillis = millis(); // get current time
  if (currentMillis - flameStartedMillis >= FLAME_DURATION) {
    return true;  
  }
  else
    return false;
}</p><p>// (pseudo)ASYNC light the candle by starting the animations
void lightCandle(){
    digitalWrite(LED_BUILTIN, HIGH);
}</p><p>void blowOutCandle(){    
  digitalWrite(LED_BUILTIN, LOW);
}</p>

Open Serial Monitor and Adjust Thresholds

Once you have everything hooked up according to the Fritzing diagram and the code uploaded, open up Serial Monitor and play with some wind & fire.

Your sensors may read different values than mine. Adjust thresholds at the top of the code file where necessary.

That's it. Wire everything up using the Fritzing diagram above. Nothing special here.

Milestone 2: Full Candle W/o Smoke

2019-03-04 23_16_01-Medium.fzz - Fritzing - [Breadboard View].jpg
TIMELINE.00_08_50_26.Still024.jpg
TIMELINE.00_08_31_23.Still009.jpg

For The Non-Smokers...

The candle is still really cool without the smoke. That may be all you want. Power with USB or whatever you'd like. Here's the Fritzing diagram and code for that.

Changes since Milestone 1:

  • No longer using the built-in LED
  • Full NeoPixel functionality included
  • NeoPatterns.h file now included

Note: Now that you're using NeoPixels, you need to install a Library from within your Arduino IDE's library manager. The library is: Adafruit_NeoPixel.

Wiring

As you can see in the Fritzing diagram, the right side is new. I don't think there's anything notable to say about the wiring at this stage. Follow the diagram and you'll be good to go.

Code

View code at: https://github.com/keith204/KeithsLEDCandle. I've also attached the code to this Instructable.

KeithsCandle_M2_NoSmoke.ino

To keep things simple, I've included a bunch of comments in the code to avoid having to explain much here.

// Using technique from here https://learn.adafruit.com/multi-tasking-the-ardu...

// see link above for a good tutorial on this multi-task-ish way of doing things.

// Flicker Realistic Candle // Keith Kelly 2019-03-04 www.KeithsTestGarage.com // Milestone 2 of 3

#ifdef __AVR__ #include #endif

// NeoPatterns will require the Adafruit NeoPixel library. Be sure to install that. #include "NeoPatterns.h"

#define NEO_PIN 5 // First RGBW Strip Pin #define NEO2_PIN 9 // Second RGBW Strip Pin #define NEO_COUNT 3 // First RGBW Strip Count #define NEO2_COUNT 3 // Second RGBW Strip Count #define MIC_PIN A1 // Microphone Pin #define FLAME_PIN A3 // IR Sensor Pin

#define FLAME_DURATION 2000 // millis- duration of flame before lighting candle #define FLAME_THRESHOLD 800 // 0...1023 - analog reading of FLAME value to detect #define MIC_THRESHOLD 950 // 0...1023 - analog reading of MIC value to detect

void Strip1Complete(); void Strip2Complete();

NeoPatterns Strip1(NEO_COUNT, NEO_PIN, NEO_RGBW + NEO_KHZ800, &Strip1Complete); NeoPatterns Strip2(NEO_COUNT, NEO2_PIN, NEO_RGBW + NEO_KHZ800, &Strip2Complete);

uint32_t baseColor; // What is the ON resting color? uint32_t offColor; // What is the OFF resting color?

// Vars to keep track of things bool flaming = false;

bool lit = false; int flame_val = 0; int mic_val = 0; byte rnd = 0;

// Vars for tracking pseudo-async times unsigned long previousMillis = 0; // will store last time LED was updated unsigned long flameStartedMillis = 0; // will store last time LED was updated

void setup(){

//Serial.begin(9600); // uncomment this to figure sensor readings.

// initialize pins pinMode(FLAME_PIN, INPUT); pinMode(MIC_PIN, INPUT); pinMode(LED_BUILTIN, OUTPUT);

// Make sure the following pins are OFF digitalWrite(LED_BUILTIN, LOW);

// Initialize NeoPixel Strip 1 Strip1.begin(); Strip1.show(); baseColor = Strip1.Color(2, 110, 0, 100);// GRBW Strip1.Color1 = baseColor;

// Initialize NeoPixel Strip 2 Strip2.begin(); Strip2.show(); baseColor = Strip2.Color(2, 110, 0, 100);// GRBW Strip2.Color1 = baseColor; blowOutCandle(); // Make sure candle is off }

void loop(){ checkSensors(); // Check for fire and strong winds Strip1.Update(); Strip2.Update(); }

// Checks for fire and strong winds every 50 milliseconds void checkSensors(){ unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= 50) { // only check every 50ms flame_val = analogRead( FLAME_PIN ); mic_val = analogRead( MIC_PIN ); previousMillis = currentMillis; // Uncomment below to check sensor readings. // Serial.print(mic_val); // Serial.print(" : "); // Serial.println(flame_val); }

if (lit && mic_val > MIC_THRESHOLD){ // MIC REGISTERED SOUND. BLOW OUT. blowOutCandle(); // Turn off light lit = false; }

if (!lit){ // if candle isn't lit if (flame_val > FLAME_THRESHOLD) { // if flame is detected // record the starting time (if flame not yet detected) if (!flaming){ // if not flaming flaming = true; flameStartedMillis = millis(); // record time that flaming begins } else if (checkFlame()){ // has flame been flaming long enough? lightCandle(); // ok finally light it up. lit = true; } } else{ // no flame detected flaming = false; } } }

// Check if flame has been detected long enough. bool checkFlame(){ unsigned long currentMillis = millis(); // get current time if (currentMillis - flameStartedMillis >= FLAME_DURATION) { return true; } else return false; }

//------------------------------------------------------------ //Completion Routines - get called on completion of a pattern //------------------------------------------------------------

// The below configuration routines are the same except for the strip // being used. I kept them separate so the strips can be configured // with different ranges if desired.

void Strip1Complete(){ Strip1.Reverse();

if (Strip1.Direction == REVERSE){ // go back down Strip1.Interval = random(5,22); // choose random speed in range } else{ // pattern ended. Stop, then trigger re-flicker /* This needs an explanation: rnd = random(random(45,55),random(80,255));

You'd think that we could just do random(45,255), right?

Selecting the low and high randoms first helps make sure that the ending random number is *more likely* to fall within a limited range.

When observing the flickering of a real candle, the flame generally bounces around in a certain range. But, every once in a while, it spikes high or low.

This random(random, random) solves that problem! #proudnerddadmoment

*/ rnd = random(random(45,55),random(80,255));

Strip1.Pixel = random(0,Strip1.numPixels()); // pick a random Pixel Strip1.Interval = 1; Strip1.Color2 = Strip1.Color(2,rnd,0,rnd-10); //GRBW random red and random white-10 } }

void Strip2Complete(){ Strip2.Reverse();

if (Strip2.Direction == REVERSE){ // go back down Strip2.Interval = random(5,22); // choose random speed in range } else{ // pattern ended. Stop, then trigger re-flicker rnd = random(random(45,55),random(80,255)); // see explanation in Strip1Complete(). Strip2.Pixel = random(0,Strip2.numPixels()); // pick a random Pixel Strip2.Interval = 1; Strip2.Color2 = Strip2.Color(2,rnd,0,rnd-10); //GRBW random red and random white-10 } }

// (pseudo)ASYNC light the candle by starting the animations void lightCandle(){ Strip1.Flicker(Strip1.Color1, Strip1.Color2, 20, 5); Strip2.Flicker(Strip2.Color1, Strip2.Color2, 20, 5); }

void blowOutCandle(){ cWipe(offColor, 10); // synchronously turn the candle off.

// We don't want no stinking patterns. Strip1.ActivePattern = NONE; Strip2.ActivePattern = NONE; }

// synchronous color wipe void cWipe(uint32_t c, uint8_t wait) { for (uint16_t i = 0; i < Strip1.numPixels(); i++) { Strip1.setPixelColor(i, c); Strip1.show(); delay(wait); } for (uint16_t i = 0; i < Strip2.numPixels(); i++) { Strip2.setPixelColor(i, c); Strip2.show(); delay(wait); } }

NeoPatterns.h

This has a few patterns in it from Adafruit, so I've omitted most of that content. Grab the file that's attached or in the GitHub repo for the full version.

[Code segment omitted for clarity. See attached file.]

// ***************************************** // START Custom animation for LED candle // ***************************************** // Initialize for a Flicker void Flicker(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval, direction dir = FORWARD) { ActivePattern = FLICKER; Interval = interval; TotalSteps = steps; Color1 = color1; Color2 = color2; Index = 0; Direction = dir; Pixel = random(0,numPixels()); // random(min,max) --max is EXCLUSIVE } // Initialize for a Flicker void FlickerUpdate() { // Calculate linear interpolation between Color1 and Color2 // Optimize order of operations to minimize truncation error uint8_t red = ((Red(Color1) * (TotalSteps - Index)) + (Red(Color2) * Index)) / TotalSteps; uint8_t green = ((Green(Color1) * (TotalSteps - Index)) + (Green(Color2) * Index)) / TotalSteps; uint8_t blue = ((Blue(Color1) * (TotalSteps - Index)) + (Blue(Color2) * Index)) / TotalSteps; uint8_t white = ((White(Color1) * (TotalSteps - Index)) + (White(Color2) * Index)) / TotalSteps; setPixelColor(Pixel,Color(red, green, blue, white)); show(); Increment(); }

// ***************************************** // END Custom animation for LED candle // *****************************************

[Code segment omitted for clarity. See attached file.]

Milestone 3: Full With Smoke [Code]

2019-03-04 21_41_56-Complex.fzz_ - Fritzing - [Breadboard View].jpg
TIMELINE.00_08_39_10.Still023.jpg

This is for those who want to crank it to eleven. I'm assuming you've already done Milestone 2.

View code at: https://github.com/keith204/KeithsLEDCandle. I've also attached the code to this Instructable.

About Milestone 3

This is a pretty considerable milestone, so I'll move the smoke mechanism information to the next page. For now, here are the code updates.

Code

KeithsCandle_M3_FullWithSmoke.ino

[To keep things simple, I've included a bunch of comments in the code to avoid having to explain much here.]

Key changes:

  • Added smoke time tracking
  • Added pin mappings and control for controlling the MOSFET

// Using technique from here https://learn.adafruit.com/multi-tasking-the-ardu...
// see link above for a good tutorial on this multi-task-ish way of doing things.

// Flicker/Smoke Realistic Candle // Keith Kelly 2019-03-04 www.KeithsTestGarage.com // Milestone 3 of 3

#ifdef __AVR__ #include #endif

// NeoPatterns will require the Adafruit NeoPixel library. Be sure to install that. #include "NeoPatterns.h"

#define NEO_PIN 5 // First RGBW Strip Pin #define NEO2_PIN 9 // Second RGBW Strip Pin #define NEO_COUNT 3 // First RGBW Strip Count #define NEO2_COUNT 3 // Second RGBW Strip Count #define MIC_PIN A1 // Microphone Pin #define FLAME_PIN A3 // IR Sensor Pin #define SMOKE_PIN 3 // Smoke Pin #define SMOKE_TIME_MILLIS 1200 // How long should smoke wire be heated?

#define FLAME_DURATION 2000 // millis- duration of flame before lighting candle #define FLAME_THRESHOLD 800 // 0...1023 - analog reading of FLAME value to detect #define MIC_THRESHOLD 950 // 0...1023 - analog reading of MIC value to detect

void Strip1Complete(); void Strip2Complete();

NeoPatterns Strip1(NEO_COUNT, NEO_PIN, NEO_RGBW + NEO_KHZ800, &Strip1Complete); NeoPatterns Strip2(NEO_COUNT, NEO2_PIN, NEO_RGBW + NEO_KHZ800, &Strip2Complete);

uint32_t baseColor; // What is the ON resting color? uint32_t offColor; // What is the OFF resting color?

// Vars to keep track of things bool smoking = false; bool flaming = false;

bool lit = false; int flame_val = 0; int mic_val = 0; byte rnd = 0;

// Vars for tracking pseudo-async times unsigned long previousMillis = 0; // will store last time LED was updated unsigned long smokeStartedMillis = 0; // time smoking started unsigned long flameStartedMillis = 0; // will store last time LED was updated

void setup(){

//Serial.begin(9600); // uncomment this to figure sensor readings.

// initialize pins pinMode(FLAME_PIN, INPUT); pinMode(MIC_PIN, INPUT); pinMode(SMOKE_PIN, OUTPUT); pinMode(LED_BUILTIN, OUTPUT);

// Make sure the following pins are OFF digitalWrite(SMOKE_PIN, LOW); digitalWrite(LED_BUILTIN, LOW);

// Initialize NeoPixel Strip 1 Strip1.begin(); Strip1.show(); baseColor = Strip1.Color(2, 110, 0, 100);// GRBW Strip1.Color1 = baseColor;

// Initialize NeoPixel Strip 2 Strip2.begin(); Strip2.show(); baseColor = Strip2.Color(2, 110, 0, 100);// GRBW Strip2.Color1 = baseColor; blowOutCandle(false); // Make sure candle is off (but don't smoke)

}

void loop(){ smokeCheck(); // Check if we should stop smoking. checkSensors(); // Check for fire and strong winds Strip1.Update(); Strip2.Update(); }

// Checks for fire and strong winds every 50 milliseconds void checkSensors(){ unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= 50) { // only check every 50ms flame_val = analogRead( FLAME_PIN ); mic_val = analogRead( MIC_PIN ); previousMillis = currentMillis; // Uncomment below to check sensor readings. // Serial.print(mic_val); // Serial.print(" : "); // Serial.println(flame_val); }

if (lit && mic_val > MIC_THRESHOLD){ // MIC REGISTERED SOUND. BLOW OUT. blowOutCandle(true); // Turn off light (and start SMOKING!) lit = false; }

if (!lit){ // if candle isn't lit if (flame_val > FLAME_THRESHOLD) { // if flame is detected // record the starting time (if flame not yet detected) if (!flaming){ // if not flaming flaming = true; flameStartedMillis = millis(); // record time that flaming begins } else if (checkFlame()){ // has flame been flaming long enough? lightCandle(); // ok finally light it up. lit = true; } } else{ // no flame detected flaming = false; } } }

// Check if flame has been detected long enough. bool checkFlame(){ unsigned long currentMillis = millis(); // get current time if (currentMillis - flameStartedMillis >= FLAME_DURATION) { return true; } else return false; }

//------------------------------------------------------------ //Completion Routines - get called on completion of a pattern //------------------------------------------------------------

// The below configuration routines are the same except for the strip // being used. I kept them separate so the strips can be configured // with different ranges if desired.

void Strip1Complete(){ Strip1.Reverse();

if (Strip1.Direction == REVERSE){ // go back down Strip1.Interval = random(5,22); // choose random speed in range } else{ // pattern ended. Stop, then trigger re-flicker /* This needs an explanation: rnd = random(random(45,55),random(80,255)); You'd think that we could just do random(45,255), right? Selecting the low and high randoms first helps make sure that the ending random number is *more likely* to fall within a limited range. When observing the flickering of a real candle, the flame generally bounces around in a certain range. But, every once in a while, it spikes high or low. This random(random, random) solves that problem! #proudnerddadmoment */ rnd = random(random(45,55),random(80,255));

Strip1.Pixel = random(0,Strip1.numPixels()); // pick a random Pixel Strip1.Interval = 1; Strip1.Color2 = Strip1.Color(2,rnd,0,rnd-10); //GRBW random red and random white-10 } }

void Strip2Complete(){ Strip2.Reverse();

if (Strip2.Direction == REVERSE){ // go back down Strip2.Interval = random(5,22); // choose random speed in range } else{ // pattern ended. Stop, then trigger re-flicker rnd = random(random(45,55),random(80,255)); // see explanation in Strip1Complete(). Strip2.Pixel = random(0,Strip2.numPixels()); // pick a random Pixel Strip2.Interval = 1; Strip2.Color2 = Strip2.Color(2,rnd,0,rnd-10); //GRBW random red and random white-10 } }

// (pseudo)ASYNC light the candle by starting the animations void lightCandle(){ Strip1.Flicker(Strip1.Color1, Strip1.Color2, 20, 5); Strip2.Flicker(Strip2.Color1, Strip2.Color2, 20, 5); }

void blowOutCandle(bool shouldSmoke){ if(shouldSmoke) smokeStart(); cWipe(offColor, 10); // synchronously turn the candle off.

// We don't want no stinking patterns. Strip1.ActivePattern = NONE; Strip2.ActivePattern = NONE; }

// SMOKING METHODS void smokeStart(){ digitalWrite(SMOKE_PIN, HIGH); // HEAT SMOKE WIRE smoking = true; smokeStartedMillis = millis(); // record time smoking started }

// Stop smoking if the time has come. void smokeCheck(){ if (!smoking) return; // if not smoking, get out of here. unsigned long currentMillis = millis(); if (currentMillis - smokeStartedMillis >= SMOKE_TIME_MILLIS) { smokeStop(); // smoke break is over } }

// Ok seriously now stop it void smokeStop(){ digitalWrite(SMOKE_PIN, LOW); // TURN OFF SMOKE WIRE smoking = false; }

// synchronous color wipe // To avoid smoke becoming fire, be sure to only // use synchronous methods like this SPARINGLY and when you // are sure it won't interfere with the smoking. // // In other words, here's something that would be very bad: // // smokeStart(); // cWipe(offColor, 1000); // CPU would get caught up in // here for a while and would // not stop the smoking process // // At this point, I'm only using it directly after smokeStop();

void cWipe(uint32_t c, uint8_t wait) { for (uint16_t i = 0; i < Strip1.numPixels(); i++) { Strip1.setPixelColor(i, c); Strip1.show(); delay(wait); } for (uint16_t i = 0; i < Strip2.numPixels(); i++) { Strip2.setPixelColor(i, c); Strip2.show(); delay(wait); } }

NeoPatterns.h

This didn't change from last time, but I attached it to this step to save you some clicks.

Next

We make the smoking contraption.

Milestone 3: Full With Smoke [The Contraption]

TIMELINE.00_09_56_19.Still007.jpg
GOPR1323.MP4.23_02_21_15.Still002.jpg
TIMELINE.00_11_37_06.Still005.jpg
TIMELINE.00_05_36_02.Still020.jpg
TIMELINE.00_12_01_10.Still004.jpg
TIMELINE.00_10_17_09.Still027.jpg
TIMELINE.00_10_51_04.Still006.jpg
TIMELINE.00_05_50_24.Still018.jpg

Watch the video for some tips. Here are some important ones that should be stated here though.

The Solution

Is primarily vegetable glycerin, with a couple drops of smell good oil.

  • Mix it up
  • Fill the jar halfway
  • Inject some glycerin solution into the top of the wick to help it get started quicker.

The Wire & Wick

  • I used 28 gauge Kanthal wire.
  • Be sure to remove the webbing from the wick so you only have fiberglass left over.
  • Wrap the wire 3 or so times.
  • Be sure the wire doesn't touch itself.
  • Then proceed to measuring the resistance as shown below and on the video.

4 Ohms

The calculations I'm giving you here are all based on 4 ohms of resistance at the wire. Be sure to measure the resistance of the wire where you're clamping it *while the wire is wrapped around the wick full of solution.* Measuring dry may give different resistance measurements.

  • If you clamp and measure 5 ohms, move the lead clamps closer inward to reduce the resistance.
  • If you need more resistance, move the lead clamps farther away.

Play With The Numbers

This is simply Ohm's law we're working with. Try using fewer volts and less resistance. See what happens. That's part of the awesome joy of doing a project like this!

Safety

  • Be sure to use a fuse in the path of the current that will travel through the wire. That way, if something strange happens, it doesn't turn into something dangerous.

Put It in a Container

TIMELINE.00_07_49_13.Still014.jpg
TIMELINE.00_07_53_13.Still013.jpg
TIMELINE.00_07_44_26.Still015.jpg
IMG_9115.jpg
TIMELINE.00_08_10_15.Still011.jpg
TIMELINE.00_08_02_17.Still012.jpg
IMG_9117.jpg
TIMELINE.00_12_05_02.Still028.jpg
IMG_9116.jpg
TIMELINE.00_08_22_01.Still010.jpg

I figure you'll deviate from my design quite a bit, mainly based on what you have handy. Therefore, details will be pretty slim on this. Your best bet is probably to watch the video where I show you how I did it. Then, find whatever container you have around and do something similar or different based on your needs.

The Container/Diffuser

I'd recommend a glass jar with wax melted along the inner walls. Use a diamond hole saw if you're drilling a hole for a power cord. Use low pressure and take it slow.

Fitting the Contents

This is the fun challenge. Fitting things into a small space. I 3D printed a tray, but you can use hot melt glue and a chunk of wood.

Stack components however possible. I stacked the LEDs on top of the Arduino. I'd recommend putting the LEDs in the center of the candle area.

For the sake of safety when playing with fire, consider using a container that doesn't catch on fire...

Summary

TIMELINE.00_03_01_20.Still030.jpg
TIMELINE.00_12_20_16.Still029.jpg
TIMELINE.00_12_28_02.Still002.jpg

If this took you less than 3 years, I'd say that's a win.

Links:

If you haven't watched the YouTube video, definitely do so.

Questions/Comments

If you have any questions or comments, let me know!