Breaking out of a while loop using IR remote

So I have this code to control my three leds using IR remote. With this code the while loop runs only once, does anybody knows how to make it run until I press any other button on the remote?

#include <IRremote.h>
#include <Adafruit_NeoPixel.h>

int LEDP = 6;
int pocet = 3;
Adafruit_NeoPixel pixely(pocet, LEDP, NEO_GRB + NEO_KHZ800);
const int RECV_PIN = 3;
IRrecv irrecv(RECV_PIN);
decode_results results;
unsigned long key_value = 0;

void setup(){
  Serial.begin(9600);
  irrecv.enableIRIn();
  irrecv.blink13(true);
  pixely.begin();
}

void loop() {
    if (irrecv.decode(&results)) {
        if (results.value == 0xFFFFFFFF) {
            results.value = key_value;
        } // if
        
        switch (results.value) {
          case 0xFF6897:
          pixely.setPixelColor(0, pixely.Color(0,0,0));
          pixely.setPixelColor(1, pixely.Color(0,0,0));
          pixely.setPixelColor(2, pixely.Color(0,0,0));
          pixely.show();
          break;
                
          case 0xFF30CF:
          pixely.setPixelColor(0, pixely.Color(0,255,0));
          pixely.setPixelColor(1, pixely.Color(0,255,0));
          pixely.setPixelColor(2, pixely.Color(0,255,0));
          pixely.show();
          break;
                
          case 0xFF18E7:
          pixely.setPixelColor(0, pixely.Color(150,150,150));
          pixely.setPixelColor(1, pixely.Color(150,150,150));
          pixely.setPixelColor(2, pixely.Color(150,150,150));
          pixely.show();
          break;
                
          case 0xFF7A85:
          pixely.setPixelColor(0, pixely.Color(150,150,150));
          pixely.setPixelColor(1, pixely.Color(0,0,0));
          pixely.setPixelColor(2, pixely.Color(0,0,0));
          pixely.show();
          break;
                
          case 0xFF10EF:
          pixely.setPixelColor(0, pixely.Color(0,0,0));
          pixely.setPixelColor(1, pixely.Color(0,0,0));
          pixely.setPixelColor(2, pixely.Color(150,150,150));
          pixely.show();
          break;
                
          case 0xFF38C7:
          pixely.setPixelColor(0, pixely.Color(0,0,0));
          pixely.setPixelColor(1, pixely.Color(150,150,150));
          pixely.setPixelColor(2, pixely.Color(0,0,0));
          pixely.show();
          break;
                
          case 0xFF5AA5:
          
          irrecv.resume();
                
          while (0xFF5AA5 == results.value) {
          pixely.setPixelColor(0, pixely.Color(150,150,150));
          pixely.setPixelColor(1, pixely.Color(0,0,0));
          pixely.setPixelColor(2, pixely.Color(0,0,0));
          pixely.show();
          delay(1000);
          pixely.setPixelColor(0, pixely.Color(0,0,0));
          pixely.setPixelColor(1, pixely.Color(0,0,0));
          pixely.setPixelColor(2, pixely.Color(150,150,150));
          pixely.show();
          delay(1000);
          pixely.setPixelColor(0, pixely.Color(0,0,0));
          pixely.setPixelColor(1, pixely.Color(150,150,150));
          pixely.setPixelColor(2, pixely.Color(0,0,0));
          pixely.show();
          delay(1000);
          pixely.setPixelColor(0, pixely.Color(150,150,150));
          pixely.setPixelColor(1, pixely.Color(150,150,150));
          pixely.setPixelColor(2, pixely.Color(150,150,150));
          pixely.show();
                    delay(1000);
           
        if (irrecv.decode(&results)) {
                        break;
                    } // if
                } // while
                break;
        } // switch        
        key_value = results.value;
        irrecv.resume();
        
    } // if
} // loop()

I like to put "halting" code in void setup(). When the looping code exits, the program ends (assuming void loop() is left empty). This is because I am not a fan of break();

Well, I am quite new to arduino and I do not know what that actually is, would it solve that my while loop inside void loop runs only once?

All Arduino code have two functions:

void setup()

and

void loop()

Most Arduino programs use void setup() to "set things up" because it runs only once.
Most Arduino programs use void loop() to "repeat these steps" because often, repetition is needed, and seldom do programs want to run once and end.

However; if you want a code that halts without having to make an infinite loop, move the code you have written inside the "void loop()" and paste it inside the "void setup()" to run your code once only. This means, if you want at least an initial loop, you will have to wrap your code in a loop that allows your code to repeat.

I am sure I do not know what @xfpd is talking about or has in mind.

You could make the while condition just be 1,

   while (1) {

making an infinite loop.

Within that loop, read the IR remote signal and if you get any, or an appropriate one, break out of the while loop.

But… the use of dealy() in your while loop may mean non-responsiveness to the signals. I've never used that library, but it looks in any case like you'd usually be waiting some good time for a reaction to new IR signals. Or worse maybe have to stab the remote button during a brief and unknowable window of opportunity during the time it is running.

This kind of functionality really needs a different approach, expecially if you ever want to immediately suspender a running animation or long sequence of activites.

a7

1 Like

but what i want to do is that i want to press a button a start a loop that would go until i press another button

well, first of all, thank you, however right now when i start the loop it goes forever until i press another button, so that works, but when i start the loop for the second time it runs only once, i have no clue why is that happening, do you have any idea?

When you "start a second time"... how are you starting? Arduino reset button? IDE Upload? IRremote press? Also... maybe your switch/case: needs a "default:" if all other cases fail.

I start it for the second time by IR remote press, everything else works

what you could do is do a "if ... else" statement inside "loop()".
You would be checking the input (from the IR receiver) and keep the current state as a variable in memory. If the input is equal to 0x3A run the first if loop and if it is not run the second loop (or just stuff under "else" statements)

I would assume it's because this statement

if (irrecv.decode(&results)) {
                        break;
                    } // if

evaluate to true.

Weird. Did you retry changing it along the lines I suggested?

This is the entire case you working with, with all that stuff changed to just print.

        while (0xFF5AA5 == results.value) {
             
                Serial.println("going to sleep for 4 seconds)";
          	delay(4000);
          	
	        if (irrecv.decode(&results)) 
                        break;
                
         } // while

        break;

You are saying that the fits time you hit "0xFF5AA5", you get a long string of activity, like in my reduction just printing the message over and over…

and when you hit another button, that stops…

and when you hit "0xFF5AA5" again, you get that message once only?

Have you tried... making the break in the if act on one specific other code?

        while (0xFF5AA5 == results.value) {
             
                Serial.println("going to sleep for 4 seconds)";
          	delay(4000);
          	
	        if (irrecv.decode(&results)) {
                       if (results.value == 0xFFFFFFFF) && key_value == SOME OTHER BUTTON CODE)
                             break;
             }
         } // while

        break;

The behaviour you describe is impossible, but if you are observing it, the only way to "see" what is going on is to add some print statments,some more print statEments.

I suggest you to use something like my modified case above, you could even turn the four second delay down massively, to see the effect of all that sleeping you doing. Try delay(50), yes it will spam your serial monitor but might shed some light.

@xfpd points out you don't have a default case, that will not change anything as far as I can tell.

I also recommend, since at the time of triggering the while results.value IS 0xFF55AA55, you do what I said:

        while (1) {  // until we get signal (or a specifici signal in this version
             
                Serial.println("going to sleep for 4 seconds)";
          	delay(4000);
          	
	        if (irrecv.decode(&results)) {
                       if (results.value == 0xFFFFFFFF) && key_value == SOME OTHER BUTTON CODE)
                             break;
             }
         } // while

        break;

Are you 100 percent sure you've shared the code you running?

Obvsy replace "SOME OTHER BUTTON CODE" with another valid known IR code that you know you can reliably receive.

I'll need to go to considerably more trouble to actually run this thing, so it's on you for a bit Skippy!

HTH

a7

1 Like

It depend on how the sensor/input is implemented. Especially since there is such a large delay in between commands.
Perhaps it's because he have one too many irrcev.resume()

Yes, one should not use (especially values that are so large) delays inside, well, any function. Try the example "blink without delay". Although it should not be a massive problem, but again, it depend on the input you are using. Some are quite nice while others are quite ugly.

You know more than I do if you aren't just guessing!

Do you think experiments as I coded examples of which in #11 will be useful?

a7

2 Likes

Well, I would just assume it since sensors (especially cheap ones) that I have came across don't like delays. Others like delays.
So yes, I am guessing, but it's based on stuff I have encountered (and knew). Which isn't that different.

For example. To take a measure with the cheap ultrasonic sensor in the robot car kit we have, you first send a "measure" command. Which is straightforward. However the sensor then turns off its I2C interface once it receives the measure command (and is measuring) so any commands from the host during this time will hang the host.
In this case there is not much you can do but use a delay of at least 100ms.

Whereas the compass module have a built-in microcontroller and initializes everything so it produce results every 33.3ms (and overwrites this with new data instantaneously) no matter if you need it or not.

The SHT30 temperature sensor from Sensirion have a "clock-stretching" mode that holds the clock line low until measurement is complete before sending back the data, which will also hang the processor. It also have a non-clock-stretching mode where the sensor immediately respond NACK (not acknowledge) and so there will be no hangs/pause.

I have not encountered any hang caused by buffer overflow (usually incoming buffers), but it does not mean there is any, especially with UART (serial).

In his sketch for example if the IR receiver is .. The time between his irrecv.resume() and irrecv.decode() is 4 seconds. What happens between the 4 seconds, I don't know. Maybe the IR receiver is constantly clocking in new data and when he checks, the new data might not be immediately available (causing it to return gibberish, which is not 0), or that the receiver have waited for too long and decided to do other things (low power sleep?) that might require another command and thus decode will not work.

It's best to avoid delay, (of any length), and read the datasheet.

My implementation of my SHT30 sketch involves a menu (on a screen) that allow the user to input various different commands (e.g. continuous, single-shot, soft reset, heater, repeatability, sleep) to the temperature sensor, while the "unofficial library" support just a few, with great limitations (e.g. clock stretching).

One of the obvious downsides (of writing every code you need yourself) is massive headache and time spent. Which is understandable. But at least read the datasheet.


(let's get back)

Yes. Your code is a very simple structure that can test if the library/sensor can "deal with" the program structure.

You can see that in his code he is trying to pause the sketch into displaying perhaps some colored LED sequence until it receives any other data, which exits the while loop and go back to the main loop.

Yes.

So the second time you tried to pause it it will only run once.

Have you tested with other cases (e.g., do some non-loop cases like 0xFF30CF)?

Also. Your switch loop is lacking a "default". This might cause problems.

Just remember the last key received and repeat that. If you don't want to repeat an action, set 'key_value' back to 0.

void loop()
{
  if (irrecv.decode(&results))
  {
    if (results.value != 0xFFFFFFFF)
    {
      // Not q repeat code so remember the code:
      key_value = results.value;
    } // if
  } // if

  // Repeat key_value until it is changed
  switch (key_value)
  {
    case 0xFF6897:
      pixely.setPixelColor(0, pixely.Color(0, 0, 0));
      pixely.setPixelColor(1, pixely.Color(0, 0, 0));
      pixely.setPixelColor(2, pixely.Color(0, 0, 0));
      pixely.show();
      break;

    case 0xFF30CF:
      pixely.setPixelColor(0, pixely.Color(0, 255, 0));
      pixely.setPixelColor(1, pixely.Color(0, 255, 0));
      pixely.setPixelColor(2, pixely.Color(0, 255, 0));
      pixely.show();
      break;

    case 0xFF18E7:
      pixely.setPixelColor(0, pixely.Color(150, 150, 150));
      pixely.setPixelColor(1, pixely.Color(150, 150, 150));
      pixely.setPixelColor(2, pixely.Color(150, 150, 150));
      pixely.show();
      break;

    case 0xFF7A85:
      pixely.setPixelColor(0, pixely.Color(150, 150, 150));
      pixely.setPixelColor(1, pixely.Color(0, 0, 0));
      pixely.setPixelColor(2, pixely.Color(0, 0, 0));
      pixely.show();
      break;

    case 0xFF10EF:
      pixely.setPixelColor(0, pixely.Color(0, 0, 0));
      pixely.setPixelColor(1, pixely.Color(0, 0, 0));
      pixely.setPixelColor(2, pixely.Color(150, 150, 150));
      pixely.show();
      break;

    case 0xFF38C7:
      pixely.setPixelColor(0, pixely.Color(0, 0, 0));
      pixely.setPixelColor(1, pixely.Color(150, 150, 150));
      pixely.setPixelColor(2, pixely.Color(0, 0, 0));
      pixely.show();
      break;

    case 0xFF5AA5:
      pixely.setPixelColor(0, pixely.Color(150, 150, 150));
      pixely.setPixelColor(1, pixely.Color(0, 0, 0));
      pixely.setPixelColor(2, pixely.Color(0, 0, 0));
      pixely.show();
      delay(1000);
      pixely.setPixelColor(0, pixely.Color(0, 0, 0));
      pixely.setPixelColor(1, pixely.Color(0, 0, 0));
      pixely.setPixelColor(2, pixely.Color(150, 150, 150));
      pixely.show();
      delay(1000);
      pixely.setPixelColor(0, pixely.Color(0, 0, 0));
      pixely.setPixelColor(1, pixely.Color(150, 150, 150));
      pixely.setPixelColor(2, pixely.Color(0, 0, 0));
      pixely.show();
      delay(1000);
      pixely.setPixelColor(0, pixely.Color(150, 150, 150));
      pixely.setPixelColor(1, pixely.Color(150, 150, 150));
      pixely.setPixelColor(2, pixely.Color(150, 150, 150));
      pixely.show();
      delay(1000);
      break;
  } // switch

  irrecv.resume();
  
} // loop()
1 Like

[quote="johnwasser, post:15, topic:987232"]
'key_value' back to 0.
[/quote]

Sorta like switch() {default: key_value = 0}; except altoid777 says that is useless code.

If it does, what code would you put in the default case that you think might avoid those problems?

A default case with no code is as good as not having one…

a7

put anything. Like, say, a dummy irrecv.decode() to flush the library buffers, if they did implement one. Before or after a irrecv.resome()

Now that we mentioned the library for the 100th time, maybe I should look into what is the library (what does it do, etc). Sounds interesting since I have some useless remotes laying around.

And yes, if default is not written then it just won't do anything.

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