FlickerCandle + IR Remote Assistance

Hello!

Having a great time with my new NeoPixel ring and this lovely code written by timpear.

I added some bits to turn the ring on and off with an IR remote. The remote works perfectly when I set the "case" values to just a solid color. However, when I set the case to point to a function, or a series of functions, I can no longer send the blackout command- like it is no longer "listening" for a new case because it is looping through the flicker function. Is there a way to force it to "listen" while looping through the flicker functions?

I am quite new to programming. Hope this is a simple one. Thanks everyone!

#include <IRLib2.h>
#include <IRLibAll.h>
#include <IRLibDecodeBase.h>
#include <IRLibGlobals.h>
#include <IRLibRecvBase.h>
#include <IRLibRecvLoop.h>
#include <Adafruit_NeoPixel.h>

#define PIN 3
#define blackout 0x508F00FF
#define FlickerCandleMode 0x508FF00F

//IR Stuff

IRrecv myReceiver(7);
IRdecode myDecoder;

// color variables: mix RGB (0-255) for desired yellow
int redPx = 255;
int grnHigh = 100;
int bluePx = 10;

// animation time variables, with recommendations
int burnDepth = 6; //how much green dips below grnHigh for normal burn - 
int flutterDepth = 20; //maximum dip for flutter
int cycleTime = 120; //duration of one dip in milliseconds

// pay no attention to that man behind the curtain
int fDelay;
int fRep;
int flickerDepth;
int burnDelay;
int burnLow;
int flickDelay;
int flickLow;
int flutDelay;
int flutLow;



Adafruit_NeoPixel strip = Adafruit_NeoPixel(12, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  flickerDepth = (burnDepth + flutterDepth) / 2.4;
  burnLow = grnHigh - burnDepth;
  burnDelay = (cycleTime / 2) / burnDepth;
  flickLow = grnHigh - flickerDepth;
  flickDelay = (cycleTime / 2) / flickerDepth;
  flutLow = grnHigh - flutterDepth;
  flutDelay = ((cycleTime / 2) / flutterDepth);
  
  strip.begin();
  strip.show();
  myReceiver.enableIRIn(); //start reciever
}

// In loop, call CANDLE STATES, with duration in seconds
// 1. on() = solid yellow
// 2. burn() = candle is burning normally, flickering slightly
// 3. flicker() = candle flickers noticably
// 4. flutter() = the candle needs air!

void loop() {
  if (myReceiver.getResults()) {
    myDecoder.decode();
    if (myDecoder.protocolNum == NEC) {
      switch(myDecoder.value) {
   
   case 0x508FF00F:  //Button 1
  
           burn(10);
           flicker(5);
           burn(8);
           flutter(6);
           burn(3);
           on(10);
           burn(10);
           flicker(10);
       break;
       
          
   case 0x508F00FF:  //Blackout Button
    strip.setPixelColor(0, 0 ,0, 0);
    strip.setPixelColor(1, 0 ,0, 0);
    strip.setPixelColor(2, 0 ,0, 0);
    strip.setPixelColor(3, 0 ,0, 0);
    strip.setPixelColor(4, 0 ,0, 0);
    strip.setPixelColor(5, 0 ,0, 0);
    strip.setPixelColor(6, 0 ,0, 0);
    strip.setPixelColor(7, 0 ,0, 0);
    strip.setPixelColor(8, 0 ,0, 0);
    strip.setPixelColor(9, 0 ,0, 0);
    strip.setPixelColor(10, 0 ,0, 0);
    strip.setPixelColor(11, 0 ,0, 0);
   break;
       }
    strip.show();
    myReceiver.enableIRIn(); //Restart the receiver
    }
  }
}

// basic fire funciton - not called in main loop
void fire(int grnLow) {
  for (int grnPx = grnHigh; grnPx > grnLow; grnPx--) {
    int halfGrn = grnHigh - ((grnHigh - grnPx) / 2);
    int darkGrn = grnPx - 70;
    darkGrn = constrain(darkGrn, 5, 255);
    strip.setPixelColor(0, redPx-180, darkGrn, 0);
    strip.setPixelColor(1, redPx-180, darkGrn, 0);
    strip.setPixelColor(2, redPx-120, grnPx-50, bluePx-5);
    strip.setPixelColor(3, redPx-60, grnPx-35, bluePx-2);
    strip.setPixelColor(4, redPx, grnPx, bluePx);
    strip.setPixelColor(5, redPx, grnPx, bluePx);
    strip.setPixelColor(6, redPx, halfGrn, bluePx);
    strip.setPixelColor(7, redPx, grnHigh, bluePx);
    strip.show();
    delay(fDelay);
    
  }  
  for (int grnPx = grnLow; grnPx < grnHigh; grnPx++) {
    int halfGrn = grnHigh - ((grnHigh - grnPx) / 2);
    int darkGrn = grnPx-70;
    darkGrn = constrain(darkGrn, 5, 255);
    strip.setPixelColor(0, redPx-180, darkGrn, 0);
    strip.setPixelColor(1, redPx-180, darkGrn, 0);
    strip.setPixelColor(2, redPx-120, grnPx-50, bluePx-5);
    strip.setPixelColor(3, redPx-60, grnPx-35, bluePx-2);
    strip.setPixelColor(4, redPx, grnPx, bluePx);
    strip.setPixelColor(5, redPx, grnPx, bluePx);
    strip.setPixelColor(6, redPx, halfGrn, bluePx);
    strip.setPixelColor(7, redPx, grnHigh, bluePx);
    strip.show();
    delay(fDelay);
    
  }
}

// fire animation
void on(int f) {
  fRep = f * 1000;
  int grnPx = grnHigh - 6;
    strip.setPixelColor(0, redPx-180, grnPx-70, 0);
    strip.setPixelColor(1, redPx-180, grnPx-70, 0);
    strip.setPixelColor(2, redPx-120, grnPx-50, bluePx-5);
    strip.setPixelColor(3, redPx-60, grnPx-35, bluePx-2);
    strip.setPixelColor(4, redPx, grnPx, bluePx);
    strip.setPixelColor(5, redPx, grnPx, bluePx);
    strip.setPixelColor(6, redPx, grnPx, bluePx);
    strip.setPixelColor(7, redPx, grnHigh, bluePx);
  strip.show();
  delay(fRep);
 
}


void burn(int f) {
  fRep = f * 8;
  fDelay = burnDelay;
  for (int var = 0; var < fRep; var++) {
    fire(burnLow);
   
  }  
}

void flicker(int f) {
  fRep = f * 8;
  fDelay = burnDelay;
  fire(burnLow);
  fDelay = flickDelay;
  for (int var = 0; var < fRep; var++) {
    fire(flickLow);
  }
  fDelay = burnDelay;
  fire(burnLow);
  fire(burnLow);
  fire(burnLow);

}

void flutter(int f) {
  fRep = f * 8;  
  fDelay = burnDelay;
  fire(burnLow);
  fDelay = flickDelay;
  fire(flickLow);
  fDelay = flutDelay;
  for (int var = 0; var < fRep; var++) {
    fire(flutLow);
  }
  fDelay = flickDelay;
  fire(flickLow);
  fire(flickLow);
  fDelay = burnDelay;
  fire(burnLow);
  fire(burnLow);
 
}

Is there a way to force it to "listen" while looping through the flicker functions?

It IS listening. You just don't check to see what it heard.

You need to check more often.

PaulS:
You need to check more often.

Thanks for the response. I see what you are saying, but looking at the code I'm not sure how to make it check more often. From my understanding, the if (myReceiver.getResults()) {myDecoder.decode(); bit is doing the actual check. Is that right? Maybe I need to go research the IRlib more.

From my understanding, the if (myReceiver.getResults()) {myDecoder.decode(); bit is doing the actual check. Is that right?

Yes, that is right.

What you need to do is break; out of any for loop, or return; from any function, when the "Hey, enough, I'm trying to sleep" button is pressed on the remote.

Okay, so I've cut this down to make a simpler example. I'm still not getting it to work properly, but maybe you can tell me if I am closer to the path...

void loop() {
  if (myReceiver.getResults()) {
    myDecoder.decode();
    if (myDecoder.protocolNum == NEC) {
      switch(myDecoder.value) {
   
   case 0x508FF00F:  //Button 1
  
       on(10);
           
    break;
       }
      }
     }
    }



// fire animation
void on(int f) {

//check for blackout from remote input
   if (myReceiver.getResults()) {
    myDecoder.decode();
    if (myDecoder.protocolNum == NEC) {
      switch(myDecoder.value) {
   

   case 0x508F08F7:  //Blackout Button
    strip.setPixelColor(0, 0 ,0, 0);
    strip.setPixelColor(1, 0 ,0, 0);
    strip.setPixelColor(2, 0 ,0, 0);
    strip.setPixelColor(3, 0 ,0, 0);
    strip.setPixelColor(4, 0 ,0, 0);
    strip.setPixelColor(5, 0 ,0, 0);
    strip.setPixelColor(6, 0 ,0, 0);
    strip.setPixelColor(7, 0 ,0, 0);
    strip.setPixelColor(8, 0 ,0, 0);
    strip.setPixelColor(9, 0 ,0, 0);
    strip.setPixelColor(10, 0 ,0, 0);
    strip.setPixelColor(11, 0 ,0, 0);
    strip.show();
   return;
       }
    }
  }

//LEDs on
  fRep = f * 1000;
  int grnPx = grnHigh - 6;
    strip.setPixelColor(0, redPx-180, grnPx-70, 0);
    strip.setPixelColor(1, redPx-180, grnPx-70, 0);
    strip.setPixelColor(2, redPx-120, grnPx-50, bluePx-5);
    strip.setPixelColor(3, redPx-60, grnPx-35, bluePx-2);
    strip.setPixelColor(4, redPx, grnPx, bluePx);
    strip.setPixelColor(5, redPx, grnPx, bluePx);
    strip.setPixelColor(6, redPx, grnPx, bluePx);
    strip.setPixelColor(7, redPx, grnHigh, bluePx);
  strip.show();


  delay(fRep);
 
}

I remember developing this for a non-blocking version, and adaptatation of Tim's code, but I cannot test it at the moment to see if it works. you can try it (I did notice a compile error):

#include <Adafruit_NeoPixel.h>

#define PIXEL_COUNT 3
#define PIXEL_PIN 2
#define PIXEL_TYPE NEO_GRB

enum PixelSelect {
  EVERY_PIXEL,
  SINGLE_PIXEL,
};

class Candle : public Adafruit_NeoPixel
{
  public:
    enum CandleStates {
      BURN_CANDLE,
      FLICKER_CANDLE,
      FLUTTER_CANDLE,
      MODES_MAX_CANDLE
    };
    Candle(uint16_t count, uint8_t pin, uint8_t type);
    Candle(uint16_t count, uint8_t pin, uint8_t type, PixelSelect pixel, uint32_t pixNum = 0);
    ~Candle() {};
    void update();

  private:
    void fire(uint8_t greenDropValue, uint32_t cycleTime);
    PixelSelect _pixelMode = EVERY_PIXEL;
    uint32_t _pixNum = 0;
    CandleStates _mode;
    uint32_t _lastModeChange;
    uint32_t _modeDuration;
    uint8_t _redPx = 255;
    uint8_t _bluePx = 10; //10 for 5v, 15 for 3.3v
    uint8_t _grnHigh = 100; //110-120 for 5v, 135 for 3.3v
    uint8_t _grnPx = 100;
    uint32_t _lastBurnUpdate = 0;
    int _direction = 1;
};

Candle::Candle(uint16_t count, uint8_t pin, uint8_t type) : Adafruit_NeoPixel(count, pin, type)
{
  randomSeed(millis() + micros());
  _mode = BURN_CANDLE;
}

Candle::Candle(uint16_t count, uint8_t pin, uint8_t type, PixelSelect pixel, uint32_t pixNum) : Adafruit_NeoPixel(count, pin, type)
{
  _pixelMode = pixel;
  _pixNum = pixNum;
}

void Candle::update()
{
  if (millis() - _lastModeChange > _modeDuration)
  {
    _mode = static_cast<CandleStates>(random(static_cast<int>(MODES_MAX_CANDLE)));
    _modeDuration = random(1000, 8000);
    _lastModeChange = millis();
  }
  switch (_mode)
  {
    case BURN_CANDLE:
      this->fire(10, 120);
      break;
    case FLICKER_CANDLE:
      this->fire(15, 120);
      break;
    case FLUTTER_CANDLE:
      this->fire(30, 120);
      break;
    case MODES_MAX_CANDLE:
      break;
  };
}

void Candle::fire(uint8_t greenDropValue, uint32_t cycleTime)
{
  int currentMillis = millis();
  if (currentMillis - _lastBurnUpdate > (cycleTime / greenDropValue / 2))
  {
    _grnPx = constrain(_grnPx += _direction, _grnHigh - greenDropValue, _grnHigh);
    if (_grnPx == _grnHigh - greenDropValue or _grnPx == _grnHigh)
    {
      _direction *= -1;
    }
    switch (_pixelMode)
    {
      case EVERY_PIXEL:
        for (size_t i = 0; i < this->numPixels(); i++)
        {
          this->setPixelColor(i, _grnPx, _redPx, _bluePx);
        }
        break;
      case SINGLE_PIXEL:
        this->setPixelColor(_pixNum, _grnPx, _redPx, _bluePx);
        break;
    }
    this->show();
    _lastBurnUpdate = currentMillis;
  }
}


Candle candle = Candle(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE, EVERY_PIXEL);

void setup()
{
  candle.begin();
  candle.show();
}

void loop()
{
  candle.update();
}

you just have to develop a state machine to control the LEDs, calling regular neopixel functions on the candle object will do it...

BulldogLowell:
I remember developing this for a non-blocking version, and adaptatation of Tim's code, but I cannot test it at the moment to see if it works. you can try it (I did notice a compile error):

Thanks Bulldog. I'm actually really close to getting the code I already have to work...just having issues with the damn blackout function. Maybe you have some insight?

In my main loop, I tell it to go to blackout(); on a button press or to on();. This works fine and easily switches between blackout(); and on();. However, when I add anything to run with on();, it simply will not switch back to blackout(); on button press. Here is my main loop...

void loop() {

if (irrecv.decode(&results))
  {
    irrecv.resume();
  }

    if (results.value == 0x508F08F7)
    {
   blackout(); 
    }

    if (results.value == 0x508FF00F) {
           
          on(10);
          flicker(10);
                  
    }
  }

...so removing the flicker(10); allows it to work properly. Here are the functions in question:

void on(int f) {

  fRep = f * 1000;
  int grnPx = grnHigh - 6;
    strip.setPixelColor(0, redPx-180, grnPx-70, 0);
    strip.setPixelColor(1, redPx-180, grnPx-70, 0);
    strip.setPixelColor(2, redPx-120, grnPx-50, bluePx-5);
    strip.setPixelColor(3, redPx-60, grnPx-35, bluePx-2);
    strip.setPixelColor(4, redPx, grnPx, bluePx);
    strip.setPixelColor(5, redPx, grnPx, bluePx);
    strip.setPixelColor(6, redPx, grnPx, bluePx);
    strip.setPixelColor(7, redPx, grnHigh, bluePx);
  strip.show(); 
}

void blackout() {

    strip.setPixelColor(0, 0 ,0, 0);
    strip.setPixelColor(1, 0 ,0, 0);
    strip.setPixelColor(2, 0 ,0, 0);
    strip.setPixelColor(3, 0 ,0, 0);
    strip.setPixelColor(4, 0 ,0, 0);
    strip.setPixelColor(5, 0 ,0, 0);
    strip.setPixelColor(6, 0 ,0, 0);
    strip.setPixelColor(7, 0 ,0, 0);
    strip.setPixelColor(8, 0 ,0, 0);
    strip.setPixelColor(9, 0 ,0, 0);
    strip.setPixelColor(10, 0 ,0, 0);
    strip.setPixelColor(11, 0 ,0, 0);
    strip.show();
}

void flicker(int f) {

 
  fRep = f * 8;
  fDelay = burnDelay;
  fire(burnLow);
  fDelay = flickDelay;
  for (int var = 0; var < fRep; var++) {
    fire(flickLow);
  }
  fDelay = burnDelay;
  fire(burnLow);
  fire(burnLow);
  fire(burnLow);
  }

th3w0lf3:
...so removing the flicker(10); allows it to work properly.

'cuz you are working with blocking code....

the **delay()**s called by the candle flicker code are preventing your program flow from detecting a state change event.

exactly why I built that class.

BulldogLowell:
'cuz you are working with blocking code....

the **delay()**s called by the candle flicker code are preventing your program flow from detecting a state change event.

exactly why I built that class.

Ah. I didn't think delays would prevent it from detecting a change. Interesting..
Not gonna lie, I simply don't understand the code you posted. I'm pretty new to this. I'm going to try it out though.
Basically what you're saying is that all of the delays in this original code are keeping it from reading a state change in the main loop?

BulldogLowell:
you can try it (I did notice a compile error):

Compiled fine for me! Just changed NEO_GRB to RGB- oddly that made it green and blue flicker even though it's a GRB neopixel ring.

It looks really nice. Thanks for sharing that, I really appreciate it. I hope I can modify it to work how I want!

th3w0lf3:
Basically what you're saying is that all of the delays in this original code are keeping it from reading a state change in the main loop?

exactly.

something like this (enter a zero or one in the Serial Monitor to turn it on/off)

#include <Adafruit_NeoPixel.h>

enum CandleState {
  CANDLE_OFF,
  CANDLE_ON,
} state = CANDLE_OFF;

#define PIXEL_COUNT 3
#define PIXEL_PIN 2
#define PIXEL_TYPE NEO_RGB

enum PixelSelect {
  EVERY_PIXEL,
  SINGLE_PIXEL,
};

class Candle : public Adafruit_NeoPixel
{
  public:
    enum CandleStates {
      BURN_CANDLE,
      FLICKER_CANDLE,
      FLUTTER_CANDLE,
      MODES_MAX_CANDLE
    };
    Candle(uint16_t count, uint8_t pin, uint8_t type);
    Candle(uint16_t count, uint8_t pin, uint8_t type, PixelSelect pixel, uint32_t pixNum = 0);
    ~Candle() {};
    void update();

  private:
    void fire(uint8_t greenDropValue, uint32_t cycleTime);
    PixelSelect _pixelMode = EVERY_PIXEL;
    uint32_t _pixNum = 0;
    CandleStates _mode;
    uint32_t _lastModeChange;
    uint32_t _modeDuration;
    uint8_t _redPx = 255;
    uint8_t _bluePx = 10; //10 for 5v, 15 for 3.3v
    uint8_t _grnHigh = 100; //110-120 for 5v, 135 for 3.3v
    uint8_t _grnPx = 100;
    uint32_t _lastBurnUpdate = 0;
    int _direction = 1;
};

Candle::Candle(uint16_t count, uint8_t pin, uint8_t type) : Adafruit_NeoPixel(count, pin, type)
{
  randomSeed(millis() + micros());
  _mode = BURN_CANDLE;
}

Candle::Candle(uint16_t count, uint8_t pin, uint8_t type, PixelSelect pixel, uint32_t pixNum) : Adafruit_NeoPixel(count, pin, type)
{
  _pixelMode = pixel;
  _pixNum = pixNum;
}

void Candle::update()
{
  if (millis() - _lastModeChange > _modeDuration)
  {
    _mode = static_cast<CandleStates>(random(static_cast<int>(MODES_MAX_CANDLE)));
    _modeDuration = random(1000, 8000);
    _lastModeChange = millis();
  }
  switch (_mode)
  {
    case BURN_CANDLE:
      this->fire(10, 120);
      break;
    case FLICKER_CANDLE:
      this->fire(15, 120);
      break;
    case FLUTTER_CANDLE:
      this->fire(30, 120);
      break;
    case MODES_MAX_CANDLE:
      break;
  };
}

void Candle::fire(uint8_t greenDropValue, uint32_t cycleTime)
{
  int currentMillis = millis();
  if (currentMillis - _lastBurnUpdate > (cycleTime / greenDropValue / 2))
  {
    _grnPx = constrain(_grnPx += _direction, _grnHigh - greenDropValue, _grnHigh);
    if (_grnPx == _grnHigh - greenDropValue or _grnPx == _grnHigh)
    {
      _direction *= -1;
    }
    switch (_pixelMode)
    {
      case EVERY_PIXEL:
        for (size_t i = 0; i < this->numPixels(); i++)
        {
          this->setPixelColor(i, _grnPx, _redPx, _bluePx);
        }
        break;
      case SINGLE_PIXEL:
        this->setPixelColor(_pixNum, _grnPx, _redPx, _bluePx);
        break;
    }
    this->show();
    _lastBurnUpdate = currentMillis;
  }
}

Candle candle = Candle(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE, EVERY_PIXEL);

void setup()
{
  Serial.begin(9600);
  candle.begin();
  candle.show();
}

void loop()
{
  static CandleState lastCandleState = CANDLE_ON;
  if (Serial.available()) {
    const char command = Serial.read();
    if (command == '0') {
      state = CANDLE_OFF;
    } else if (command == '1') {
      state = CANDLE_ON;
    }
  }
  
  switch (state) {
    case CANDLE_OFF:
      if (lastCandleState != CANDLE_OFF) {
        for (size_t i = 0; i < PIXEL_COUNT; i++) {
          candle.setPixelColor(i, 0, 0, 0);
        }
        candle.show();
      }
      break;
    case CANDLE_ON:
      candle.update();
      break;
  }
  lastCandleState = state;  // EDIT
}

BulldogLowell:
exactly.

something like this (enter a zero or one in the Serial Monitor to turn it on/off)

That is an incredibly helpful example. Thank you.

BulldogLowell:
exactly.

something like this (enter a zero or one in the Serial Monitor to turn it on/off)

I've added in the IR controls. Having the same issue- can turn it on, but it is not turning back off. However, it will turn off by sending "0" via serial monitor- so I think this is just an IR problem now...

void loop()

  
{
  static CandleState lastCandleState = CANDLE_ON;
  if (Serial.available()) {
    const char command = Serial.read();
    if (command == '0') {
      state = CANDLE_OFF;
    } else if (command == '1') {
      state = CANDLE_ON;
    }
  }
  
if (irrecv.decode(&results))
  {
    irrecv.resume();
  }
   if (results.value == 0x508FF00F) {
      state = CANDLE_OFF;
    } 
   if (results.value == 0x508F08F7) {
      state = CANDLE_ON;
    }
  
  switch (state) {
    case CANDLE_OFF:
      if (lastCandleState != CANDLE_OFF) {
        for (size_t i = 0; i < PIXEL_COUNT; i++) {
          candle.setPixelColor(i, 0, 0, 0);
        }
        candle.show();
       
      }
      break;
    case CANDLE_ON:
      candle.update();
     
      break;
  }
  lastCandleState = state;  // EDIT
}

can this be right?

  if (irrecv.decode(&results)) {
    irrecv.resume();
  }
  if (results.value == 0x508FF00F) { 
    state = CANDLE_OFF;
  }
  if (results.value == 0x508F08F7) {
    state = CANDLE_ON;
  }

maybe:

  if (irrecv.decode(&results)) {
    irrecv.resume();
    if (results.value == 0x508FF00F) {
      state = CANDLE_OFF;
    } else if (results.value == 0x508F08F7) {
      state = CANDLE_ON;
    }
  }

watch your formatting!

I see what you did there :slight_smile:
Didn't work though. Same issue.

th3w0lf3:
I see what you did there :slight_smile:
Didn't work though. Same issue.

yeah, I don't know anything about that device/library...

Do you have basic code to turn on/off LED 13?

BulldogLowell:
Do you have basic code to turn on/off LED 13?

I do. I added irrecv.blink13(true); to void setup.
The light blinks every time I press a button, so I know for sure it is seeing what I send.

I added feedback for the candle states as well, so I know when it is receiving the command via IR and when it isn't receiving anything. I can press the "OFF" button over and over again and it will readout properly- "Candle is OFF". However, as soon as I hit the "ON' button and make the CANDLE_ON state active, it no longer accepts or looks for IR input- pressing either button does not bring up "Candle is ON" or "Candle is OFF". The pin13 led blinks- it just does nothing with the signal.

#include <boarddefs.h>
#include <IRremote.h>
#include <IRremoteInt.h>
#include <ir_Lego_PF_BitStreamEncoder.h>
#include <Adafruit_NeoPixel.h>

enum CandleState {
  CANDLE_OFF,
  CANDLE_ON,
} state = CANDLE_OFF;

#define PIXEL_COUNT 12
#define PIXEL_PIN 3
#define PIXEL_TYPE NEO_RGB
int IRpin = 7;
IRrecv irrecv(IRpin);
decode_results results;

enum PixelSelect {
  EVERY_PIXEL,
  SINGLE_PIXEL,
};

class Candle : public Adafruit_NeoPixel
{
  public:
    enum CandleStates {
      BURN_CANDLE,
      FLICKER_CANDLE,
      FLUTTER_CANDLE,
      MODES_MAX_CANDLE
    };
    Candle(uint16_t count, uint8_t pin, uint8_t type);
    Candle(uint16_t count, uint8_t pin, uint8_t type, PixelSelect pixel, uint32_t pixNum = 0);
    ~Candle() {};
    void update();

  private:
    void fire(uint8_t greenDropValue, uint32_t cycleTime);
    PixelSelect _pixelMode = EVERY_PIXEL;
    uint32_t _pixNum = 0;
    CandleStates _mode;
    uint32_t _lastModeChange;
    uint32_t _modeDuration;
    uint8_t _redPx = 255;
    uint8_t _bluePx = 10; //10 for 5v, 15 for 3.3v
    uint8_t _grnHigh = 100; //110-120 for 5v, 135 for 3.3v
    uint8_t _grnPx = 100;
    uint32_t _lastBurnUpdate = 0;
    int _direction = 1;
};

Candle::Candle(uint16_t count, uint8_t pin, uint8_t type) : Adafruit_NeoPixel(count, pin, type)
{
  randomSeed(millis() + micros());
  _mode = BURN_CANDLE;
}

Candle::Candle(uint16_t count, uint8_t pin, uint8_t type, PixelSelect pixel, uint32_t pixNum) : Adafruit_NeoPixel(count, pin, type)
{
  _pixelMode = pixel;
  _pixNum = pixNum;
}

void Candle::update()
{
  if (millis() - _lastModeChange > _modeDuration)
  {
    _mode = static_cast<CandleStates>(random(static_cast<int>(MODES_MAX_CANDLE)));
    _modeDuration = random(1000, 8000);
    _lastModeChange = millis();
  }
  switch (_mode)
  {
    case BURN_CANDLE:
      this->fire(10, 120);
      break;
    case FLICKER_CANDLE:
      this->fire(15, 120);
      break;
    case FLUTTER_CANDLE:
      this->fire(30, 120);
      break;
    case MODES_MAX_CANDLE:
      break;
  };
}

void Candle::fire(uint8_t greenDropValue, uint32_t cycleTime)
{
  int currentMillis = millis();
  if (currentMillis - _lastBurnUpdate > (cycleTime / greenDropValue / 2))
  {
    _grnPx = constrain(_grnPx += _direction, _grnHigh - greenDropValue, _grnHigh);
    if (_grnPx == _grnHigh - greenDropValue or _grnPx == _grnHigh)
    {
      _direction *= -1;
    }
    switch (_pixelMode)
    {
      case EVERY_PIXEL:
        for (size_t i = 0; i < this->numPixels(); i++)
        {
          this->setPixelColor(i, _grnPx, _redPx, _bluePx);
        }
        break;
      case SINGLE_PIXEL:
        this->setPixelColor(_pixNum, _grnPx, _redPx, _bluePx);
        break;
    }
    this->show();
    _lastBurnUpdate = currentMillis;
  }
}

Candle candle = Candle(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE, EVERY_PIXEL);

void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn();
  candle.begin();
  candle.show();
}

void loop()

  
{
  static CandleState lastCandleState = CANDLE_ON;
  if (Serial.available()) {
    const char command = Serial.read();
    if (command == '0') {
      state = CANDLE_OFF;
      Serial.println("Candle is OFF");
    } else if (command == '1') {
      state = CANDLE_ON;
      Serial.println("Candle is OFF");
    }
  }
  
if (irrecv.decode(&results)) {
irrecv.resume();
    if (results.value == 0x508F08F7) {
      state = CANDLE_OFF;
      Serial.println("Candle is OFF");
      
    } else if (results.value == 0x508FF00F) {
      state = CANDLE_ON;
      Serial.println("Candle is ON");
      
    }
  }
  
  switch (state) {
    case CANDLE_OFF:

      if (lastCandleState != CANDLE_OFF) {
        for (size_t i = 0; i < PIXEL_COUNT; i++) {
          candle.setPixelColor(i, 0, 0, 0);
        }
        candle.show();
       
      }
      break;
    case CANDLE_ON:
  
      candle.update();
     
      break;
  }
  lastCandleState = state;  // EDIT
}

Well, it doesn't make any sense to me...but by making the CANDLE_OFF state controlled by a simple else clause (allowing any input that ISN'T 0x508FF00F to turn it off)...it works fine. Good enough for me.

void loop()

  
{
  static CandleState lastCandleState = CANDLE_ON;
  if (Serial.available()) {
    const char command = Serial.read();
    if (command == '0') {
      state = CANDLE_OFF;
      Serial.println("Candle is OFF");
    } else if (command == '1') {
      state = CANDLE_ON;
      Serial.println("Candle is ON");
    }
  }
  
if (irrecv.decode(&results)) {
    irrecv.resume();
  
   if (results.value == 0x508FF00F) {
      state = CANDLE_ON;
      Serial.println("Candle is ON");
       Serial.println(results.value, HEX);
      
    }else{
      Serial.println("Candle is OFF");
      Serial.println(results.value, HEX);
      state = CANDLE_OFF;
    }
  }
  
  switch (state) {
    case CANDLE_OFF:

      if (lastCandleState != CANDLE_OFF) {
        for (size_t i = 0; i < PIXEL_COUNT; i++) {
          candle.setPixelColor(i, 0, 0, 0);
        }
        candle.show();
       
      }
      break;
    case CANDLE_ON:
  
      candle.update();
     
      break;

      default:
        Serial.println("Dead end, break at switch");
        break;
  }
  lastCandleState = state;  // EDIT
}

You really think you need this

      default:
        Serial.println("Dead end, break at switch");
        break;

If your state can only be ON or OFF

Also, properly formatting your code will help you, now and forever.

:wink:

Nah, I don't need that. Some dude was helping me with the code earlier and added that in.

What formatting errors do you mean? Just overall organization and good habits? I agree...I am bad at this. :slight_smile: