looping a function while checking for change.

Hey, I have a Led cube and have made 3 functions for it, then I got Ken Shirriff's IRlibary and got each function (pattern or sequence w/e) to be turned on by a certain button on my remote the problem is, this only works once. So the function is run finished then all leds go off until I push another button. I want it to loop the function when i push the button until another button is pressed, but the problem is while the function is looping the code cant check for another button press as the function involves delays. How can i make it work, i cant use interrupts since i'm using delays and using the millis() method would get too complicated rather not go there.. any idea's?

The code in void loop so far..

void loop () {
  //ir receiver bit
  if (irrecv.decode(&results)) {
    if (results.value == Button_1) {
      Serial.write("Button 1 pressed\n"); 
       pressed1 = 1;
       }

    else if (results.value == Button_2) {
      Serial.write("Button 2 pressed\n");    
       pressed1 = 2;
     
     }
    
    else if (results.value == Button_3) {
      Serial.write("Button 3 pressed\n");    
       pressed1 = 3;
        }
      
    
    else if (results.value == OFF) {
      Serial.write("ON/OFF pressed\n");    
       pressed1 = 0;
        allOff();
      }
    
    else {
      Serial.println(results.value, HEX); 
    } 

    irrecv.resume(); // Receive the next value
  }
  
  
  // led cube bit
 if (pressed1 == 1) {scrollthroughled ();}
 else if (pressed1 == 2) {scrollupanddown ();}
 else if (pressed1 == 3) {spinthroughlayers ();}
 
}

The whole code can be seen here IR_LED_Cube code - Pastebin.com
All help appreciated. :wink:

Perhaps write a "pollingDelay()" function that loops polling the buttons whilst also keeping its eye on the result of millis() ? The idea is that if a button is pressed the delay is cancelled immediately.

MarkT, that is brilliant and although this is not my thread I will do as you suggested post haste.

Seriously though, how many questions on this forum could be answered with a reference to such a function?

Yes, look at blink without delay and learn about state machines would still be a standard response but I think it would be helpful to people to have such a function available.

I suppose the question becomes "polling what?" for a hypothetical pollingDelay() but still I think there is merit in this idea.

zer044,
The only way you can do what you want without using an interrupt is to break your tasks up into smaller bits and check the IR sensor in between. So instead of grouping your tasks into a large function you need to break it up into smaller pieces you can run. For example:

Instead of:

void loop () {
  //ir receiver bit
  if (irrecv.decode(&results)) {
    ...
  }
   ...
   if (pressed1 == 3) {spinthroughlayers ();}
}

void spinthroughlayers () {
  //middle layer
  while(x<30)...
  //second layer
  while(x<30)...
  // third layer
  while(x<30)...
  // forth layer
  while(x<30)...
}

Try something like this:

int spinphase = 0;

void loop () {
  //ir receiver bit
  if (irrecv.decode(&results)) {
    ...
  }
	...
	if (pressed1 == 3) {
	  if spinphase = 0{
	    //middle layer
		while(x<30)...
	  }
	  if spinphase = 1{
	    //second layer
		while(x<30)...
	  }
	  if spinphase = 2{
	    // third layer
		while(x<30)...
	  }
	  if spinphase = 3{
	    // forth layer
		while(x<30)...
	  }
	  if spinphase > 3{
	    spinphase = 0;
	  }
	  spinphase++;
    }
}

In the code above you will see that you can now check the IR sensor 4 times while you are executing the same thing as your previous single function. As you start to think in this perspective and get more experience you will realize that you can probably store the elements driving your ledOn function in something like an array and call it in a loop that increments through your array - allowing you to now check the IR in between every LED update...

I wanted to keep the examples above using the same convention you have been using with the IF..THEN control, but what you are doing is really suited towards using SWITCH/CASE statements: http://www.arduino.cc/en/Reference/SwitchCase By using switch/case your code will be cleaner and it will operate faster than multiple if..then statements.

Example for your code:

int spinphase = 0;

void loop () {
  //ir receiver bit
  if (irrecv.decode(&results)) {
    ...
  }
  switch (pressed1) {
    case 1:
	scrollthroughled ();
	break;
    case 2:
	scrollupanddown ();
	break;
    case 3:
	spinthroughlayers ();
	break;	  
    default: 
	// if nothing else matches, do the default
	// default is optional
  }
}
  
void spinthroughlayers () {
  switch (spinphase) {
    case 0:
	//middle layer
	while(x<30)...
	break;
    case 1:
	//second layer
	while(x<30)...
	break;
    case 2:
	// third layer
	while(x<30)...
	break;
    case 3:
	// forth layer
	while(x<30)...
	break;	  
    default: 
	spinphase = 0;
  }
  spinphase++;
}

Hope it helps,

willnue

PS. If you get to a point where the IR function is taking too long to execute (slowing down your LEDs) let me know and you/I can create a new post on that with a workaround.

Seriously though, how many questions on this forum could be answered with a reference to such a function?

A few, but isn't it better to learn how to do it properly and not waste cycles in a "delay"?

Think about such a function - how do you decide how to break the wait time down into delays and checks?
Every millisecond?
Every 100 microseconds?

after each if does the arduino just go onto the next if or does it start from the top again..

if (pressed1 == 3) {
	  if spinphase = 0{
	    //middle layer
		while(x<30)...
	  }
	  if spinphase = 1{
	    //second layer
		while(x<30)...
}

I ask because here there is no check for the if (ir) in between my function if's so would it still check or do i have to make the if (ir) part a function and call it between each if?

Code is executed in the order defined. One if statement ends, the next line of code is executed.

Yes it goes through each of the 4 IF statements on each pass when pressed1=3.

So first you are checking to see if your button state is set to 3 to run your spinthrough effect and if so you are then checking to see what "stage" of your spinthrough effect should be run. It goes through the 4 stages in order then the last IF resets the counter to 0 to start back at the first stage.

Also, I just realized after looking at your code again, what your main issue is. You are doing IF..THEN..ELSE statements in the main loop, the result of which leave pressed1 set to 0 at the end of a pass if no button was pressed. Really I think what you want to do is just check to see if a button was pressed otherwise just keep doing the last effect selected. To do this you really just need to remove the ELSE statements.

Try this first before making any other changes I suggested:

void loop () {
  //ir receiver bit
  if (irrecv.decode(&results)) {
    if (results.value == Button_1) {
      Serial.write("Button 1 pressed\n");
       pressed1 = 1;
    }
 
    if (results.value == Button_2) {
      Serial.write("Button 2 pressed\n");    
       pressed1 = 2;
    }
   
    if (results.value == Button_3) {
      Serial.write("Button 3 pressed\n");    
       pressed1 = 3;
    }
    
    if (results.value == OFF) {
      Serial.write("ON/OFF pressed\n");    
       pressed1 = 0;
        allOff();
    }
   
    Serial.println(results.value, HEX);
 
    irrecv.resume(); // Receive the next value
}

If you follow the code through you will now see that that unless the button is pressed "pressed1" remains the same each time. FYI - Your code still won't be able to sense a button press until after each effect has run through. In order to switch the effects "in the middle" you will need to breakup the code as I suggested yesterday.

Also I might suggest changing

int pressed1=0;

To:

int pressed1=1;

That way when your project starts the first effect will be running without having to press a button.

After you get it working, save a copy then when you get a chance try to re-write it with the CASE statements. After that try breaking it up into the smaller bits...

willnue

Thx the method suggested works brilliantly, now when i press another button it waits for current sequence to finish then goes to the sequence of the next button i pressed..

the current full code is now..

Edit: I have added the switch case works exactly same way as above code but i save valuable memory, but i have a mega so got plenty of that
here is the switch case full code.. switch case_CUBE update - Pastebin.com

okay now i will see how it works as case, then i will work on figuring out how to save and read my sequence functions as byte arrays..

Edit: does anybody know any example's or tutorials i can get started on turning my functions into byte arrays?

AWOL:

Seriously though, how many questions on this forum could be answered with a reference to such a function?

A few, but isn't it better to learn how to do it properly and not waste cycles in a "delay"?

Think about such a function - how do you decide how to break the wait time down into delays and checks?
Every millisecond?
Every 100 microseconds?

Well lets say like the OP I want my leds to blink while accepting input and thats it. Wow, a pollingDelay() at any kind of sane granularity works wonders and solves that problem.

I can make a state machine, or I could use such a function. No it doesn't solve every problem, but for those that it does solve there is no reason not to consider it a "proper" solution.

zer044:
Thx the method suggested works brilliantly, now when i press another button it waits for current sequence to finish then goes to the sequence of the next button i pressed..

Great to see you got it working! I took a look at your new code with the CASE statements and it looks good, but I think you can eliminate "pressed1" and the IF statements in the //Led Cube Bit section:

Try this:

void loop () {
  //ir receiver bit
  if (irrecv.decode(&results)) {
    switch (results.value) {
   
    case Button_1:
      Serial.write("Button 1 pressed\n");
      scrollthroughled();
      break;
 
    case Button_2:
      Serial.write("Button 2 pressed\n");    
      scrollupanddown();
      break;
   
    case Button_3:
      Serial.write("Button 3 pressed\n");    
      spinthroughlayers ();
      break;
    
    case OFF:
      Serial.write("ON/OFF pressed\n");    
      allOff();
      break;
    
    default :
      Serial.println(results.value, HEX);
    }
    irrecv.resume(); // Receive the next value
  }
}

As far as the array goes you can use arrays of type Int since the values passed to your LedOn function are Ints. You could use 3 arrays -> one for X, one for Y and one for T to store the values for your effects. If you really get into it you could use a multidimensional array to store it all... Anyway, here are a few links for using arrays with Arduino:

Arduino reference page for Arrays:
http://arduino.cc/en/Reference/Array

Array Tutorial:
http://arduino.cc/en/Tutorial/Array

willnue

I have 1 problem, it only works when connected to computer not when powered in other ways.. anyone know why?

it only works when connected to computer not when powered in other ways..

What is "it" that only works when connected to the computer?

What other ways have you tried powering "it"?

It is the arduino mega connected to a 3d led Cube.. and i have tried powering it with a 6v and 9v power adapters, a mains to usb adapter via usb port, with each case power light shows but the cube doesn't respond to remote, that suggests the code needs the computer but i dont know why?

Regarding the making sequence function as byte arrays.. i couldn't use byte because it only accepts 8 values and i have 9 leds.. I wrote the arrays as ints instead but when i upload it nothing happens to the cube..(power shows on mega just the cube doesnt work).

heres the code..

int levelPins[] = {22,23,24};
int colPins[] = {30,31,32,33,34,35,36,37,38};

int first[3][9] = {
111000000,
011100000,
001110000,
};

void setup () {
  //set all output
  for (int i=0; i<3; i++) {
    pinMode(levelPins[i], OUTPUT);
  }
  for (int j=0; j<9; j++) {
    pinMode(colPins[j], OUTPUT);

  }
}

void show (int image[3][9]) {
 for (int level=0; level<3; level++) {
  digitalWrite(levelPins[level], LOW);
   
   for (int col=0; col < 9; col++) {
    int pixel = image[level][col];
    
  if (pixel == 1) {digitalWrite(colPins[col], HIGH);}
 
 delay(30);
 digitalWrite(colPins[col], LOW);
       } // col for
   digitalWrite(levelPins[level], HIGH);
   } // level for
 } // end of show


void loop() {

show(first);
  }

This is what its like when connected to computer..

FYI - The cube looks great!

On the issue of powering the Arduino, I would suggest you take a look at (or measure) the current rating for your adapters. Typically a USB port will provide 500ma of current to any device (arduino), but your 9V adapter may not provide that much current. FYI - The 6V adapter is not really a good option since the 5V regulator on the Arduino needs an input of over 7V to get 5V out. 9V seems to be the typical choice for the Arduino, so I would stick with that.

For the array issue, your thinking is close, but you are combining the 9 Ints into 1 value ie. 111000000 should really be more along the lines of {1,1,1,0,0,0,0,0,0}.

Take a look at the following post in the forums from a user working on a 4x4 LED cube. The article and answer should give a good example on how to declare and initialize the arrays. Also not they are using Boolean data types to represent the on/off status of the LEDs. I think this is a better way than using Ints as I originally suggested.

multi-dimensional arrays
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1220238747

willnue

Thx i got the the array bit working.
Regarding powering the led cube, I can power the led cube the without the ir receiver code.. another piece of code that just makes the cube loop through the sequences and also my other project where I connected a piezoelectric sensor to the analog input and made it create patterns for the cube depending on how its hit, > the point is these work with the 6v (500 mA << the adapter is rated 6v but for some reason outputs 8v on multimeter) and 9v (2.5A) adapters.. but the ir code doesn't.

May it have something to do with the #include i used for the ir libary?

Code for reading from array...

/** 
* by zer044 arduino account holder
* 25/08/2011
* 3by3 Led cube read from array
**/


int levelPins[] = {22,23,24};
int colPins[] = {30,31,32,33,34,35,36,37,38};
int x=0;
int x=y;


int first[3][9] = {
{1, 1, 1, 0, 0, 0, 0, 0, 0},
{1, 1, 1, 0, 0, 0, 0, 0, 0},
{1, 1, 1, 0, 0, 0, 0, 0, 0},
};
int second[3][9] = {
 {0, 0, 0, 1, 1, 1, 0, 0, 0},
 {0, 0, 0, 1, 1, 1, 0, 0, 0},
 {0, 0, 0, 1, 1, 1, 0, 0, 0},
};
int third[3][9] = {
{0, 0, 0, 0, 0, 0, 1, 1, 1},
{0, 0, 0, 0, 0, 0, 1, 1, 1},
{0, 0, 0, 0, 0, 0, 1, 1, 1},
};

int all[3][9] = {
{1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1},
};

void setup () {
 
  //set all output
  for (int i=0; i<3; i++) {
    pinMode(levelPins[i], OUTPUT);
  }
  for (int j=0; j<9; j++) {
    pinMode(colPins[j], OUTPUT);

  }
}

void show (int image[3][9]) {
 
  
   for (int level=0; level<3; level++) {
  digitalWrite(levelPins[level], LOW);
   
   for (int col=0; col < 9; col++) {
    int pixel = image[level][col];
    
  if (pixel == 1) {digitalWrite(colPins[col], HIGH);}
   
   delayMicroseconds(30);
 digitalWrite(colPins[col], LOW);
       } // col for
   digitalWrite(levelPins[level], HIGH);
   } // level for
 
 
 } // end of show
 
 

void loop() 
{
show(first);
show(second);
show(third);
}

If it works without the IR code & library, but stops when you add it then the problem is either a conflict with the library or your code working with it. I'm wondering if your IR code is maybe just going into an endless loop and therefore it appears to be broken? I would try troubleshooting by first adding back the include statement for the IR library along with the "irrecv.enableIRIn(); statement in your setup and testing that.

If running with the library & setup code works then you really need to focus on your code that uses the library. Add some Serial.print statements after each line or block of your code to see what is happening while running...

On your 6V power supply measuring 8V - that is very common. The rating printed on it is really a "minimum", the actual output can vary greatly. See Example 2 in this article for more info Multimeter Tutorial - Voltage.

willnue