Issue with millis() when controlling 2 LEDs with an IR Remote

Hello everyone!

I am a scale-modeller and want to enhance my next model a bit by adding lighting to it. I did that before with physical switches on the model without any Issues but this time around, I wanted the LEDs to be controlled by an IR-Remote.

To be very honest: most of my knowledge about Arduino comes from YouTube videos and copy-pasting stuff off the Internet.

Now to the project: I have managed to have constant-shining-LEDs turn on and off with the remote with no issues(will be utilized for interior lighting etc.). Since my Model is a rescue helicopter, it will need to have a beacon- and a strobe-light. I also know that using delay() does not work for my kind of project so I decided to use millis(). When I simply have the LEDs flashing at different rates and no IR-remote, It also works fine. Its also no problem when I introduce the IR-stuff to the code and only try to turn one of the two LEDs on, but as soon as I try to turn on the second LED, either the first or the second one is lit up constantly and nothing else happens. I´ve already looked through many forum entries here and elsewhere but cant seem to find an exact answer so I turn to this forum in hopes of finding a way to do what I plan to do.

Here is the code so far (note: this code only includes the blinking LEDs as of now since I tested the non-blinking LEDs already and want to approach the topic one at a time)

Thanks in advance!!

#include <IRremote.h>
int receiver = 11;
uint32_t Previous;
IRrecv irrecv(receiver);
decode_results results;

const int led1 = 8;
const long onDuration = 1201;
const long offDuration = 199;
int LEDstate1 = LOW;

const int led2 = 7;
const long onDuration2 = 1219;
const long offDuration2 = 167;
int LEDstate2 = LOW;

long rememberTime=0;
long rememberTime2=0;

void setup() {
  pinMode(led1, OUTPUT);
  digitalWrite(led1, LEDstate1);
  pinMode(led2, OUTPUT);
  digitalWrite(led2, LEDstate2);
  Serial.begin (9600);
  irrecv.enableIRIn();
}

void loop() {


{if (results.value==0xFFA25D){
  if ( LEDstate1 == LOW )
  {
  if( (millis()-rememberTime) >= onDuration){
    LEDstate1 = HIGH;
    rememberTime=millis();
  }
  }
    else 
  {
        if( (millis()- rememberTime) >= offDuration){
      LEDstate1 = LOW;
      rememberTime = millis();
      }
  }
digitalWrite(led1, LEDstate1);
}
}


{if (results.value==0xFF629D){
  if ( LEDstate2 == LOW )
  {
  if( (millis()-rememberTime2) >= onDuration2){
    LEDstate2 = HIGH;
    rememberTime2=millis();
  }
  }
    else 
  {
        if( (millis()- rememberTime2) >= offDuration2){
      LEDstate2 = LOW;
      rememberTime2 = millis();
      }
  }
digitalWrite(led2, LEDstate2);
}
}
if (irrecv.decode(&results)){
  if (results.value==0xFFFFFFFF){
      results.value=Previous;
      }

Serial.println (results.value, HEX);
irrecv.resume();
}
}

Use that to set a flag down where you are receiving, and use that flag in the if statement.

Use the other value you can receive to set a second flag, and use that second flag in the other if statement.

Use a different code or two to clear those flags, or, without using up more remote codes, you could use reception of the code to toggle the associated flag.

What’s happening now is that results.value can only be one or the other, so only the guts of one the if statements controlling whether its LED is blinking or not will be executed.

Nice blinking code at a glance, I assume you honest when you say they work both when you aren’t trying to remote control them.

Fragments, sry I assume everyone knows what a flag is in this context. Flags be useful.


bool blinkOne;    // global variable flag

….

   if (blinkOne) { do the blinking of the one LED…}

…..
// in the reception section at the end of you *loop*() function:
    if (results.value==0xFFA25D) blinkOne = true;

a7

Not sure I understand the problem or your code. Do you want the two IR buttons to toggle the LED blinking on and off? If so, maybe this variant of @alto777 's thought would work (compiles, not tested):

#include <IRremote.h>

#define NUM_LEDS        2

int receiver = 11;
uint32_t irCode, irPrev;
IRrecv irrecv(receiver);
decode_results results;

const uint8_t led1 = 8;
const uint32_t onDuration = 1201;
const uint32_t offDuration = 199;

const uint8_t led2 = 7;
const uint32_t onDuration2 = 1219;
const uint32_t offDuration2 = 167;

typedef struct structLEDs 
{    
    uint8_t     pin;
    bool        enable;
    bool        state;
    uint32_t    tLED;  
    uint32_t    tOn;
    uint32_t    tOff;
}sLEDs_t;

sLEDs_t grLEDs[] = 
{
    { led1, false, false, 0ul, onDuration, offDuration },
    { led2, false, false, 0ul, onDuration2, offDuration2 }
    
};

void setup() 
{
    for( uint8_t i=0; i<NUM_LEDS; i++ )
    {
        pinMode( grLEDs[i].pin, OUTPUT );
        digitalWrite( grLEDs[i].pin, grLEDs[i].state );
        
    }//for
    
    Serial.begin (9600);
    irrecv.enableIRIn();
    
}//setup

void loop() 
{
    doLEDTimers();
    
    if( irrecv.decode(&results) )
    {        
        irCode = results.value;
        irrecv.resume();
        
        if( irCode == 0xFFFFFFFF)
            irCode = irPrev;

        switch( irCode )
        {
            case    0xFFA25D:
                //toggle the enable for blinking LED 1
                grLEDs[0].enable ^= true;
            break;

            case    0xFF629D:
                //toggle the enable for blinking LED 2
                grLEDs[1].enable ^= true;
            break;
            
        }//switch

        irPrev = irCode;        
        Serial.println( irCode, HEX );        
        
    }//if
        
}//loop

void doLEDTimers( void )
{
    static uint8_t
        index = 0;
    uint32_t
        tNow = millis();
    
    switch( grLEDs[index].state )
    {
        case    false:    
            //LED state is off now; time to turn it on?        
            if( (tNow - grLEDs[index].tLED) >= grLEDs[index].tOff )
            {
                //save time we switched on and set flag 'true'
                grLEDs[index].tLED = tNow;                
                grLEDs[index].state = true;
                
            }//if
            
        break;

        case    true:
            //LED state is on now; time to turn it off?        
            if( (tNow - grLEDs[index].tLED) >= grLEDs[index].tOn )
            {
                //save time we switched off and set flag 'false'
                grLEDs[index].tLED = tNow;
                grLEDs[index].state = false;
                
            }//if
            
        break;
        
    }//switch

    //only actually drive the LED if it is enabled
    digitalWrite( grLEDs[index].pin, (grLEDs[index].enable) ? grLEDs[index].state : LOW );

    //on each pass check one LED
    index++;
    if( index == NUM_LEDS )
        index = 0;
    
}//doLEDTimers

@Blackfin nice.

Yes, very more elaborate and legit, I'll post my hack when I get back from the beach.

What I can't know or test is how the ir receive code works, if it would get multiple hits frustrating any kind of toggle logic, so I thought my way around that by just having codes to turn on the blinkers and one or two others to turn them off.

L8R

a7

This is mostly the original code, with pushbuttons used instead of reception of valid IRremote commands, just to show the flag thing in a literal way. A crude hack, but easy to see right there with no additional concepts or ideas and no difference in structure.

Obvsly if there's much further to go, departing from the methods @Blackfin illustrates would be preferred.

I changed the time constants. I ran the code through an autoformat step like the one available in the IDE.

I see a flaw or feature: if you stop the blinking, you might do when one or both LEDs are on. I think @Blackfin's might also.

And to promote the use of wokwi!

// #include <IRremote.h>
// no IRremote, so pushbutton proxies here

// https://forum.arduino.cc/t/issue-with-millis-when-controlling-2-leds-with-an-ir-remote/964143

int receiver = 11;
uint32_t Previous;

const int led1 = 8;
const long onDuration = 600;
const long offDuration = 200;
int LEDstate1 = LOW;

const int led2 = 7;
const long onDuration2 = 700;
const long offDuration2 = 400;
int LEDstate2 = LOW;

long rememberTime = 0;
long rememberTime2 = 0;

void setup()
{
  pinMode(led1, OUTPUT);
  digitalWrite(led1, LEDstate1);
  pinMode(led2, OUTPUT);
  digitalWrite(led2, LEDstate2);

  pinMode(A0, INPUT_PULLUP);
  pinMode(A1, INPUT_PULLUP);
  pinMode(A2, INPUT_PULLUP);

  Serial.begin (9600);
  Serial.println("\nHello World!\n");
}

bool blinkOne = false;
bool blinkTheOther = false;

void loop()
{
  if (blinkOne) {
    if ( LEDstate1 == LOW ) {
      if ( (millis() - rememberTime) >= onDuration) {
        LEDstate1 = HIGH;
        rememberTime = millis();
      }
    }
    else {
      if ( (millis() - rememberTime) >= offDuration) {
        LEDstate1 = LOW;
        rememberTime = millis();
      }
    }
    digitalWrite(led1, LEDstate1);
  }


  if (blinkTheOther) {
    if ( LEDstate2 == LOW ) {
      if ( (millis() - rememberTime2) >= onDuration2) {
        LEDstate2 = HIGH;
        rememberTime2 = millis();
      }
    }
    else {
      if ( (millis() - rememberTime2) >= offDuration2) {
        LEDstate2 = LOW;
        rememberTime2 = millis();
      }
    }
    digitalWrite(led2, LEDstate2);
  }

// simulate IR reception

  if (!digitalRead(A0)) blinkOne = true;         // 0xFFA25D
  if (!digitalRead(A1)) blinkTheOther = true;    // 0xFF629D

  if (!digitalRead(A2)) {  // some third code TBD
    blinkOne = false;
    blinkTheOther = false;
  }
}

I was hoping to prevent that with:

//only actually drive the LED if it is enabled
    digitalWrite( grLEDs[index].pin, (grLEDs[index].enable) ? grLEDs[index].state : LOW );

If the LED is "enabled" the current value of state is written (i.e. blinking), otherwise a LOW is written, hopefully turning the LED off.

tl;dr: @Blackfin's version catches no LED on.



Fixed that. I hadn't read all the code close, so I should have just "wondered", indicating a higher degree of wonderment. :expressionless:

And I will spare relating the curious challenge that seeing @Blackfin correctly presented my brain, nor offer excuses or explanations. It's right there in the code.

So a point is made, perhaps. Presenting the flag idea in situ so to speak by hacking the OP's code was probably going to make more immediate sense, at the expense of a bigger learning opportunity, then the better version might do.

    //only actually drive the LED if it is enabled
    digitalWrite( grLEDs[index].pin, (grLEDs[index].enable) ? grLEDs[index].state : LOW );

The first thing I missed was where the LED was ever written HIGH.

Even with the comment it took me some time, never mind how long precisely, to see.

That line ends up writing true to turn on the LED, and LOW to turn it off. We have been adviced, advice I don't follow, that digitalWrite wants LOW and HIGH, nothing different. I look forward to this being a problem while also waiting for pigs to fly.

It was the fact that "HIGH" never appeared in the better version that made me look closer.

I stuck the code in the wokwi where another series of unfortunate mistakes (mine, TBC) made it take a while to get working, but yes, the code functions very well. And no catching the LED on.

I wonder that is to say I am supposed to be working on my taxes so won't investigate, whether both solutions, hack and professional, have some aesthetic issues around catching timers in process resulting in truncated on or off periods at the transitions from blinking to not blinking.

Small things like this, and whether they matter and to whom and how easy it is to "fix" the code if the boss lady sez she doesn't like the tiny click when it should be a beep &c. can be part of the fun. And demonstrate the difference between hacking a way to the solution, like I did, or planning in advance of an issue, as @Blackfin did.

I leave it here. The structure for the LED could have also included the IRremote code to which the LED should respond, and the switch/case in the loop would then be a loop over the LEDs &c., so soon enough it would be a very concise expression, tots flexible. Toss in a few more C++ tricks I can't seem to learn to enjoy using, or at all really, and you'd have something that woukd give good returns on any time spent studying.

a7

1 Like

A great point. I admit I thought about it but just got lazy :slight_smile:

Hey there, I just got home from Work and looked through the replies I got in this short time period.

I´ve tried your version of the code and it works like a charm. Since my requirements for this project are very low, you out-did them by making it being able to toggle. I will definitly build upon this code.

Of course I also thank @alto777 for contributing ideas!!

Wishing you two a great evening and greetings from germany :smiley:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.