Toggling a counter to run through elements in array (Arduino)

Thread Starter

abu1985

Joined Oct 18, 2015
74
I would like to shift the data in the array as I push a button on an IR remote. My current code shown below, the analogWrite outputs the 5 elements in the array every 2 seconds after I press the button on the remote. I want it to wait to increment until I press the button again on the remote.

Any takers on trying to provide some direction?
Thanks, stay safe.

C++:
#include <IRremote.h>


const int analogOutPin = 9; //PWM (Motor) Output
const int RECV_PIN = 8; //RF Reciever input


int outputValue = 0;
int SpeedArray[5] = {254, 220, 190, 160, 145};
int count = 0;

IRrecv irrecv(RECV_PIN);
decode_results results;


void setup()
{
  // Enable the IR Receiver
  irrecv.enableIRIn();
}


void loop()
{
  if (irrecv.decode(&results))
  {
    if (results.value = 0x150C)
    {
      for (count = 0; count < 5; count++)
      {
        analogWrite(analogOutPin, SpeedArray[count]);
        delay(2000);
        analogWrite(analogOutPin, 0);
      }
    }
    irrecv.resume();
  }
}
 

ZCochran98

Joined Jul 24, 2018
88
If I understand you correctly, you want your counter to increment only after you press the button, yes? If that's the case, there are two things you can possibly do.
  1. You can, in your loop() function, check to see if your button is down. If not, continue on (this is called "polling"). This solution is not very efficient
  2. You can create a subroutine that's called asynchronously whenever your button is pressed. This is called an "interrupt," and can be done by "attaching" an interrupt to a button that refers to a specific function (in this case, all that function would do is increment your counter). To save you the time of trying to find the syntax, this can be set up in the following way:

Code:
  attachInterrupt(digitalPinToInterrupt(PIN_NAME), function_name, RISING);
This attaches an interrupt to PIN_NAME that calls some function named "function_name" when the Arduino detects a rising-edge signal from whatever pin your remote is attached to. You could also use FALLING instead of RISING.

Interrupts are a valuable tool for things like remote controls or any "asynchronous" (e.g.: human interaction) signals. If you want to know more about interrupts, there are a ton of resources online that can help. They're pretty cool!

Hope this helps and points you in the right direction!
 

Thread Starter

abu1985

Joined Oct 18, 2015
74
Hope this helps and points you in the right direction!
Nicely done, I appreciate the direction. I did get it to work using the polling system, but I didn't know the Arduino could have interrupts.

Question about interrupts: if you have 50 lines of code, the code enter the subroutine at line 25 and begins executing, when it exits the subroutine, does the "scan" (Sorry, I'm a PLC guy) continue where it left off at line 26?

Thanks!
 

KeithWalker

Joined Jul 10, 2017
1,154
A simpler solution would be to use a flag which can have a value of 1 or 0.
Each time you receive a valid IR code, check it's value.
If it is 1, change it's value and continue with your array routine.
If it is 0, change it's value and skip to the end of the loop.
Regards,
Keith
 

Thread Starter

abu1985

Joined Oct 18, 2015
74
A simpler solution would be to use a flag which can have a value of 1 or 0.
Each time you receive a valid IR code, check it's value.
If it is 1, change it's value and continue with your array routine.
If it is 0, change it's value and skip to the end of the loop.
Regards,
Keith
This is what I ended up doing a couple days ago. It works just fine. I think it's about what you're saying.

Code:
void loop()
{
    if (irrecv.decode(&results))
  {
    switch (results.value)
    {
     case 0x150C: //case 1
      if (count < 5)
      {
      count++;
      analogWrite(analogOutPin, SpeedArray[count]);
      Serial.print(count);
      }
      else
      {
        Serial.print("Max");
      }
      break;
      
      

     case 0x1587: //case 2
      if (count > 0)
      {
      count--;
      analogWrite(analogOutPin, SpeedArray[count]);
      Serial.print(count);
      }
      else
      {
        Serial.print("Min");
      }
      
      break;
     }
       irrecv.resume();
  }
}
 

ZCochran98

Joined Jul 24, 2018
88
Nicely done, I appreciate the direction. I did get it to work using the polling system, but I didn't know the Arduino could have interrupts.

Question about interrupts: if you have 50 lines of code, the code enter the subroutine at line 25 and begins executing, when it exits the subroutine, does the "scan" (Sorry, I'm a PLC guy) continue where it left off at line 26?

Thanks!
You are correct: when an interrupt is is triggered, the processor completes whatever instruction it's on, jumps into the interrupt routine, and then, on completion, resumes where it left off. It's like a subroutine that can be called at any given moment, which makes them wonderful tools. So if it is triggered when the processor is at line 25, it goes to the interrupt, finishes, and resumes at line 26.
 

djsfantasi

Joined Apr 11, 2010
6,723
You are correct: when an interrupt is is triggered, the processor completes whatever instruction it's on, jumps into the interrupt routine, and then, on completion, resumes where it left off. It's like a subroutine that can be called at any given moment, which makes them wonderful tools. So if it is triggered when the processor is at line 25, it goes to the interrupt, finishes, and resumes at line 26.
When coding an interrupt routine, it’s often a good idea to start it by disabling interrupts. Then, when done, re-enable interrupts before returning.

If you don’t do this, you could have a situation whereby the interrupt is interrupted. This could result in a loop. Every interrupt, some memory is used to save the previous state. An interrupted loop could quickly cause all memory to be consumed.
 

ZCochran98

Joined Jul 24, 2018
88
When coding an interrupt routine, it’s often a good idea to start it by disabling interrupts. Then, when done, re-enable interrupts before returning.

If you don’t do this, you could have a situation whereby the interrupt is interrupted. This could result in a loop. Every interrupt, some memory is used to save the previous state. An interrupted loop could quickly cause all memory to be consumed.
This is a very good point. I had forgotten to mention that. When you have complicated programs with long interrupt service routines, this can happen frequently (speaking from experience....) In Arduino, in your service routine, you probably should have the first line be noInterrupts(); and the last line of the routine be interrupts();. The first will temporarily disable interrupts, and the second will re-enable them.
 

Thread Starter

abu1985

Joined Oct 18, 2015
74
When coding an interrupt routine, it’s often a good idea to start it by disabling interrupts. Then, when done, re-enable interrupts before returning.

If you don’t do this, you could have a situation whereby the interrupt is interrupted. This could result in a loop. Every interrupt, some memory is used to save the previous state. An interrupted loop could quickly cause all memory to be consumed.
Are you saying the main routine is still executing while the interrupt is also running?
 

ZCochran98

Joined Jul 24, 2018
88
Are you saying the main routine is still executing while the interrupt is also running?
Not quite - that'd be more like multithreading (which I don't think the Arduino can do?); other interrupts could potentially "interrupt the interrupt." For instance, if you don't have button debouncing, you could end up triggering the interrupt several times in a few milliseconds (which would give unexpected results - you'd see your counter suddenly jump a few values instead of just incrementing once - like going from 3 to 7 instead of 3 to 4). Or, if you have something that is based on the clock's interrupt (called a real-time interrupt - loop() may or may not be one; that I don't know), it could interrupt the service routine for your remote if you pressed the button at just the right time. Or, if your service routine is really long, it is conceivable that it would last long enough for you to push the button again while it's still handling the previous button press. In this case, it's a really short service routine, I imagine, so it probably wouldn't be too high of a risk, but it doesn't hurt to temporarily disable and then re-enable interrupts.
 

djsfantasi

Joined Apr 11, 2010
6,723
Not quite - that'd be more like multithreading (which I don't think the Arduino can do?); other interrupts could potentially "interrupt the interrupt." For instance, if you don't have button debouncing, you could end up triggering the interrupt several times in a few milliseconds (which would give unexpected results - you'd see your counter suddenly jump a few values instead of just incrementing once - like going from 3 to 7 instead of 3 to 4). Or, if you have something that is based on the clock's interrupt (called a real-time interrupt - loop() may or may not be one; that I don't know), it could interrupt the service routine for your remote if you pressed the button at just the right time. Or, if your service routine is really long, it is conceivable that it would last long enough for you to push the button again while it's still handling the previous button press. In this case, it's a really short service routine, I imagine, so it probably wouldn't be too high of a risk, but it doesn't hurt to temporarily disable and then re-enable interrupts.
The interrupt routine is a shutdown routine. At the end of the routine it’s unnecessary to enable interrupts. The PIC will be shutdown.

On the other hand, disabling interrupts upon entering the routine is recommended.
 

ZCochran98

Joined Jul 24, 2018
88
The interrupt routine is a shutdown routine. At the end of the routine it’s unnecessary to enable interrupts. The PIC will be shutdown.

On the other hand, disabling interrupts upon entering the routine is recommended.
Oh yeah, that's true. Forgot about that, to be honest.
Doing a little digging, I found that, while in general when working with interrupts it is good practice to manually disable interrupts upon entering an ISR (as we discussed), but Arduino actually automatically does that, apparently (link - it's about 1/4 down the page, starting with the phrase "The vast majority of interrupt service routines written for the Arduino are not reentrant," for reference if you're curious).

So, in the long run: good practice to disable interrupts in the ISR, but not necessary to re-enable (it's done automatically), though in Arduino, however, neither is necessary, as Arduino does it automatically, apparently (but you should probably still disable them anyway, just for good practice).
 

Thread Starter

abu1985

Joined Oct 18, 2015
74
No. An interrupt routine “interrupts” the main routine. The main routine stops while the interrupt routine does its thing.
Some good explanations here. But if the interrupt stops the main routine, I'm struggling to see how the interrupt can be interrupted? The interrupt must finish and move back to the main to be called again, or call any other interrupt, no?
 

ZCochran98

Joined Jul 24, 2018
88
Some good explanations here. But if the interrupt stops the main routine, I'm struggling to see how the interrupt can be interrupted? The interrupt must finish and move back to the main to be called again, or call any other interrupt, no?
In some code, the interrupt itself can be interrupted if another interrupt occurs that has a higher priority. For instance, if you pressed the "reset" button on the Arduino it triggers a built-in interrupt that has nearly-highest priority. In theory, if you timed it just right, you could trigger this interrupt while the other is running.

Basically, interrupts interrupting interrupts (say that five times fast....) act similarly to subroutines calling subroutines. An interrupt temporarily halts another batch of code and demands the CPU's attention, if it has a higher execution priority than what was previously being executed. If it does, then the CPU finishes what it's doing, goes to the interrupt requesting attention, handles it, and then resumes what it was doing before it was interrupted, be that the main function, another subroutine, or even another interrupt. Now, certain ones are special, like reset, that will basically "clear" the CPU's memory (as a simple explanation) of where it had previously been (that information is held in a stack somewhere, and reset forces the stack pointer to go back to the beginning).

So, in the long run, interrupts, if they have higher execution priority than whatever's currently running, will stop the CPU, let it finish the particular instruction it's on, move focus to its ISR, finish that (unless interrupted by a higher-priority interrupt itself), then release focus back to the original piece of code, picking up where it last left off.
 

Thread Starter

abu1985

Joined Oct 18, 2015
74
So, in the long run, interrupts, if they have higher execution priority than whatever's currently running, will stop the CPU, let it finish the particular instruction it's on, move focus to its ISR, finish that (unless interrupted by a higher-priority interrupt itself), then release focus back to the original piece of code, picking up where it last left off.
Yup, okay. It's not necessarily that my main code will interrupt my interrupt, there are other systems at play here. Disabling interrupts at the beginning of the interrupt code is good practice...

I will write up something with interrupts and study the behavior.

Thanks
 
We need an option in schematic tool itself to define major components and connector interfaces placement as an add on feature which can be used directly into PCB design from the net list package.

I found the time of flight sensor documentation from SSLA. I highly recommend their service as well.
 

ZCochran98

Joined Jul 24, 2018
88
We need an option in schematic tool itself to define major components and connector interfaces placement as an add on feature which can be used directly into PCB design from the net list package.

I found the time of flight sensor documentation from SSLA. I highly recommend their service as well.
I think you might want to check that you replied to the correct forum post.
 
Top