Pages: [1] 2   Go Down
Author Topic: looping a function while checking for change.  (Read 1461 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 1
Posts: 49
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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..
Code:
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 http://pastebin.com/5U2t7CgK
All help appreciated.  smiley-wink

Logged

0
Offline Offline
Shannon Member
****
Karma: 161
Posts: 10445
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

[ I won't respond to messages, use the forum please ]

Portland, OR
Offline Offline
Full Member
***
Karma: 0
Posts: 101
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

USA
Offline Offline
Jr. Member
**
Karma: 0
Posts: 90
I’m no expert, but I play one on the Internet…
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
Code:
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:
Code:
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:
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.

« Last Edit: August 11, 2011, 10:29:39 pm by willnue » Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 239
Posts: 24371
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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?
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Offline Offline
Newbie
*
Karma: 1
Posts: 49
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Code:
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?
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 549
Posts: 46113
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

USA
Offline Offline
Jr. Member
**
Karma: 0
Posts: 90
I’m no expert, but I play one on the Internet…
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
Code:
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
Code:
int pressed1=0;
To:
Code:
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
Logged

Offline Offline
Newbie
*
Karma: 1
Posts: 49
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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..

http://pastebin.com/vgb1q6N2

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.. http://pastebin.com/RpJvu5gd

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?
« Last Edit: August 12, 2011, 07:25:31 pm by zer044 » Logged

Portland, OR
Offline Offline
Full Member
***
Karma: 0
Posts: 101
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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.
« Last Edit: August 12, 2011, 08:23:46 pm by growler » Logged

USA
Offline Offline
Jr. Member
**
Karma: 0
Posts: 90
I’m no expert, but I play one on the Internet…
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
Code:
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
Logged

Offline Offline
Newbie
*
Karma: 1
Posts: 49
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 549
Posts: 46113
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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"?
Logged

Offline Offline
Newbie
*
Karma: 1
Posts: 49
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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..
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..
« Last Edit: August 19, 2011, 11:16:02 am by zer044 » Logged

USA
Offline Offline
Jr. Member
**
Karma: 0
Posts: 90
I’m no expert, but I play one on the Internet…
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Pages: [1] 2   Go Up
Jump to: