Does WiFi.off() work from within an interrupt?

Does WiFi.off() work when called within an interrupt routine? I would like to have the WiFi module off most of the time to conserve battery so I set up a switch to turn the WiFi module on and off when desired. Any time I try using WiFi.off() from my interrupt the Photon hangs and the RGB LED shows solid cyan. If I move the WiFi.off() anywhere else, it works fine. I would like to avoid checking a flag on every loop, if possible, which is why I created an interrupt.

In the test code below, everything works as expected except the “OFF” in the dipSwitch2 ISR. (I did try including Particle.disconnect() ahead of the WiFi.off() call but that didn’t change the behavior.)

// Set up the input pins for the dip switches
volatile bool wifiModulePower = false;
int dip1 = D6;  // dip 1 is connected to D3
int dip2 = D5;  // dip 2 is connected to D4
int LED1 = D7;  // Use the built-in LED on D7

// Put system into SEMI_AUTOMATIC to control access to the cloud
SYSTEM_MODE(SEMI_AUTOMATIC);

// Turn off WiFi as soon as possible to conserve battery
STARTUP( WiFi.off() );                                          // Works Fine

void setup(void) {
    
    // Start the serial port
    Serial1.begin(115200);

    // Set up pin modes
    pinMode(LED1, OUTPUT); 
    pinMode(dip1, INPUT_PULLDOWN);
    pinMode(dip2, INPUT_PULLDOWN);
    
    // attach interrupts to the 2 dip switches
    attachInterrupt(dip1, dipSwitch1, CHANGE);
    attachInterrupt(dip2, dipSwitch2, CHANGE);
}

// Interrupt Service Routine for DIP 1
void dipSwitch1()
{
    int val = digitalRead(dip1);  

    // Connect to the cloud (turns on wifi module by default)
    if (val == HIGH && Particle.connected() == false) {         
        wifiModulePower = true;
        Particle.connect(); // This Works Fine                                                 
        Serial1.println("DIP 1 CONNECTED TO CLOUD & TURNED ON WIFI");
        // ISR doesn't return to main loop until after connection is made... cool
    }
 
    // Set wifiModulePower false so the main loop will turn off the wifi module
    if (val == LOW && Particle.connected() == true) {           
        wifiModulePower = false;
        Serial1.println("DIP 1 DISCONNECTED FROM CLOUD");
    }
}

// Interrupt Service Routine for DIP 2
void dipSwitch2()
{
    int val = digitalRead(dip2);  

    // Connect to the cloud (turns on wifi module by default) 
    if (val == HIGH && Particle.connected() == false) {         
        wifiModulePower = true;
        Particle.connect(); // This Works Fine 
        Serial1.println("DIP 2 CONNECTED TO CLOUD & TURNED ON WIFI");
        // ISR doesn't return to main loop until after connection is made... cool
    }
 
    // turn WiFi module off (disconnect from cloud by default)
    if (val == LOW && Particle.connected() == true) {           
        wifiModulePower = false;
        Serial1.println("DIP 2 ATTEMPTING TO TURN OFF WIFI");

//************************** THIS SECTION DOESN'T WORK**********************************
        WiFi.off();                                             // *** Photon hangs and shows solid cyan ***
        Serial1.println("DIP 2 TURNED OFF WIFI MODULE POWER");  // *** This line never prints ***
//***************************************************************************************
    }
}

void loop(void) {
    Serial1.println("Hello World!");
    
    // No need for the wifi module sucking power if you are not connected to the cloud
    if ( wifiModulePower == false && WiFi.ready() ) {
        WiFi.off(); // This Works Fine
        Serial1.println("MAIN LOOP TURNED OFF WIFI MODULE POWER");
    }
    
    delay(1000);
}

Why don't you want to check a flag? That is the correct way to handle this situation. Your ISRs should be as short as possible, and you certainly should not put any blocking code in one. The only thing you need to do in your case, is set a flag in the ISR that the switch was flipped.

For your use case I don’t see the need for interrupts at all.
I’d rather go with software timers, which saves you the flag checking in loop() and allows you to do almost anything in the handler functions.

1 Like

I am trying to read in data as close to 1KHz as I can and then record it to an SD card and was trying to save as much time in the loop as I could. I guess in my mind, I was thinking that checking a flag and polling the input pins would take the same amount of time but probably just checking if a flag is set would be much faster. I can move the actual “connect” and “off” calls out of the ISR so the ISR is just setting a flag.

I hadn’t considered a software timer… would you just set up a timer to check the state of the input pins once a second or so?

If your switches stay in position for longer than the sampling periode (as I'd assume, judging by the variable dip) then yes, otherwise you can use an interrupt that retrieves the state of the pin and flags it.
But the frequency you want to check the state depends on your desired response time. If you want near immeditate feedback, you can sample more frequently.
But if you want to sample 1kHz signals you should maybe put the sampling in an ISR (e.g. via the SparkIntervalTimer library) since the cloud servicing between iterations of loop() takes a bit over 1ms each time, when you are connected and without SYSTEM_THREAD(ENABLED).

Another way to speed things up is to use pinReadFast() instead of digitalRead(). This would not be much slower than just checking a flag.

You are correct about the dips… not very creative with my variable names :smile:

When I plan to sample and process data at a high rate I won’t be connected to the cloud so I think I will explore either just using a timer to check the pin state every once in a while (since, at the moment, I don’t think the button needs to be very responsive) or I will just set a flag in an ISR and check each loop. Given that they are dip switches I may just set it up so that the user needs to determine how they plan to use the device before they turn it on and then just do everything in setup()… but I need to think about that a little more.

Both suggestions are very helpful!

1 Like

at some high sample rate frequency, you’ll need to use DMA, not programmed I/O.