Search code examples
cfunctionraspberry-pidelay

Raspberry Pi, looping delay function


This is my code:

#include <wiringPi.h>
#include <stdio.h>

#define LED 18
#define IN 24

int main(void)
{
    wiringPiSetupGpio ();
    pinMode (LED, OUTPUT);
    pinMode (IN, INPUT);
    for (;;)
    {
        digitalWrite (LED, 1);
        delay (2000);
        digitalWrite (LED, 0);
        delay (2000);

        if (digitalRead (IN) == 1)
        {
            for (;;)
            {
                digitalWrite (LED, 1);
                delay(500);
                digitalWrite (LED, 0);
                delay(500);

            }
        }    
    }    
    return 0;
}

I use a switch to go from the 2000 delay to the 500 delay. The problem is that when I press the switch it waits until the loop is over until it becomes faster. Same applies to vice versa. I need to write a function or a loop somewhere in my code that allows me to press the switch and immediately with no wait go to the slower/faster speed. I am coding this in C, on a raspberry pi using the wiring pi libraries, and gpio pin numbering.


Solution

  • First things first, that code as you've shown it will never deviate from a delay of 2000 simply because there's no way to exit from the initial for(;;) loop. That's probably the first thing you need to check and fix.


    In terms of being able to end a delay early, you can do something like change:

    delay (2000);
    

    into:

    for (i = 0; i < 2000; i += 10) {
        delay (10);
        if (digitalRead (IN) == 1)
            break;
    }
    

    This basically exits the delay early if the switch changes, so that you'll never be waiting more than 10 time units (rather than up to 2000).

    Just be aware that there may be an overhead to repeatedly calling delay. In other words, while delay(2000) may take very close to 2000 time units, two hundred calls to delay(10) may take a little more because of setup time for each call and so on.


    In fact, you can probably greatly simplify your code by not using hard-coded values at all. Instead, you can have a single loop with minimal delay, with some internal controls which work out when and how to change the LED (i.e., not every time through the loop).

    For example, something like this would do it. I haven't tested it since my Pi2 is still sitting in a box awaiting some time for me to play with it, but it should be pretty close:

    #include <wiringPi.h>
    #include <stdio.h>
    
    #define LED 18
    #define IN 24
    #define RES 10
    
    int main (void) {
        int cycleTime, ledState, lastSwitch, currSwitch, timeLeft;
    
        wiringPiSetupGpio ();
        pinMode (LED, OUTPUT);
        pinMode (IN, INPUT);
    
        cycleTime = 2000;  // Initial cycle time
        ledState = 0;      //   and LED state.
        lastSwitch = 0;    // Previous switch state,
    
        // Start infinite loop with first cycle time.
    
        timeLeft = cycleTime;
        for (;;) {
            // Delay for minimal time and adjust time left.
    
            delay (RES);
            timeLeft = timeLeft - RES;
    
            // Detect switch change.
    
            currSwith = digitalRead (IN);
            if (currSwitch != lastSwitch) {
                // If so, store new state, change cycle time
                //   and force IMMEDIATE end of current cycle.
    
                lastSwitch = currSwitch;
                cycleTime = 2500 - cycleTime;  // switch 500 <-> 2000
                timeLeft = 0;
            }
    
            // Detect end of cycle.
    
            if (timeLeft <= 0) {
                // Toggle LED and start new cycle.
    
                ledState = 1 - ledState;
                digitalWrite (LED, ledState);
                timeLeft = cycleTime;
            }
        }
    }
    

    Basically, you have a single loop but one which loops every ten time units rather than every 500 or 2000. You maintain a separate counter which decides when the LED should be flipped and the starting value for this counter depends on the current cycle time.

    When the switch is flipped, that counter is zeroed so that it (almost) immediately changes to the next cycle.