Wio Lite MG126 - wake-up interrupt conflicts with BLE?

I want to put the MCU into sleep/standby mode using ArduinoLowPower library, using an interrupt on a digital pin to wake up. I cannot get this to work when using the MG126 BLE system. If I do not initialize the MG126 BLE system, the wake-up interrupt works fine (as on other SAMD21 based systems, like the XIAO).

If I use LowPower.attachInterrupWakeup prior to initializing the ble system ( MG126_Ble_Class::ble_init), the BLE system will not work. If I do this after initializing ble, the mcu will not sleep. I believe the interrupt used in the ble system is waking it up. Note that I am checking ble_run_interrupt_McuCanSleep(), and it is fine (returns non-zero).

I implemented new routines to detach the ble interrupt prior to attempting to sleep, and reattach after wake. Between these, I attach the wakeup interrupt, attempt to sleep, and detach the wakeup interrupt after waking. In this case, the MCU will sleep, but the BLE system does not work properly after wake.

I can detach and reattach the BLE interrupts, and everything works fine. I can also sleep the MCU for a set period, using LowPower.sleep(100000), for instance, and reattach the BLE interrupt after wake, and this works fine. The problem therefore seems to be with the interrupts, not with waking from sleep.

Whenever I call LowPower.attachInterruptWakeup() (and my own LowPower.detachInterruptWakeup() after wake), the BLE can no longer connect. Note that the failure to reconnect is independent of whether or not the MCU sleeps between – just attaching the wakeup interrupt is enough to ensure the BLE will not reconnect properly.

I wrote the following to illustrate the problem. Two #define statements near the top control the behavior, WAKEINTERRUPT and SLEEP. If the first is defined, the code will include calls to LowPower.attachInterruptWakeup() /LowPower.detachInterruptWakeup(). If the second is defined, it will include calls to either LowPower.sleep() (if WAKEINTERRUPT is defined), or LowPower.sleep(100000) (if the wakeup interrupt is not included).

There is a 10s delay after initializing BLE, allowing time to connect a bluetooth terminal (I use Serial Bluetooth Terminal on an Android phone). After 10s elapses, it detaches the ble interrupt, attempts to attach the wake-up interrupt (if applicable), sleep (if applicable), wake, detach wake-up, calls radio_fixSPI, and reattach the ble interrupt. The user should then try to reconnect the bluetooth terminal. If and only if WAKEINTERRUPT is defined, this will fail. It works in all other cases.

What am I missing?

Is there another solution? Is it possible to shut the BLE system down after initializing it, and later re-initialize it, when needed?

Thanks in advance. Here’s the code.

Note that I am using Arduino 1.8.12 on Windows 10.


#include <Arduino.h>
#include “MG126_Ble.h”
#include “SPI.h”
#include “ArduinoLowPower.h”

#define WAKEINTERRUPT
//#define SLEEP

/*
Notes

  1. “Serial Bluetooth Terminal” was used (on Android phone) to connect to/monitor BLE. Connect
    to BLE during initial 10s delay. During sleep/delay, BLE will disconnect. Try to reconnect after wake.
  2. If SLEEP is defined, the MCU will attempt to either sleep until a digital pin interrupt is triggered
    (when WAKEINTERRUPT is defined) or for 10s (LowPower.sleep(10000) ifndef WAKEINTERRUPT
  3. If SLEEP is not defined, a 5s delay is used in its place, to give BLE terminal time to disconnect (a
    result of detaching the BLE interrupt).
  4. In all cases, the BLE interrupt is detached, and then reattached after sleep/delay
  5. Defining WAKEINTERRUPT attaches a wake-up interrupt to a digital pin, to bring MCU out of sleep.
    After waking, the wake-up interrupt is detached.

Results:
Whenever WAKEINTERRUPT is defined, independent of SLEEP, BLE will not reconnect after detaching/
reattaching LowPower wake interrupts. “Serial Bluetooth Terminal” fails to connect with gatt status 133

*/

#define CS_PIN 47
#define IRQ_PIN 7

extern ble_trans_t recv_struct;

MG126_Ble_Class MG126_Ble(CS_PIN, IRQ_PIN);

void setup() {
SERIAL.begin(115200);
while (!SERIAL);

// initialize BLE system

// 10s delay to give user time to connect phone to BLE prior to sleep attempt
SERIAL.print(“Start 10s delay … Connect to BLE now\n”);
delay(10000);
SERIAL.write(“10s delay finished\n\n”);

MG126_Ble.ble_detachIRQ(); // detach BLE interrupt

#ifdef WAKEINTERRUPT

pinMode(9, INPUT_PULLUP);
LowPower.attachInterruptWakeup(9, wakeRoutine, CHANGE);

#ifdef SLEEP
if(MG126_Ble.ble_cansleep()) LowPower.sleep();
else SERIAL.write(“ble_cansleep() was false\n”);
#else
SERIAL.write(“5s delay instead of sleep …\n”);
delay(5000);
#endif

LowPower.detachInterruptWakeup(digitalPinToInterrupt(9));

#else

#ifdef SLEEP
if(MG126_Ble.ble_cansleep()) LowPower.sleep(10000);
else SERIAL.write(“ble_cansleep() was false\n”);
#else
SERIAL.write(“5s delay instead of sleep …\n”);
delay(5000);
#endif

#endif

MG126_Ble.ble_fixSPI(); // fix states of cs, sck and mosi post-sleep
MG126_Ble.ble_attachIRQ(); // re-attach BLE interrupt

SERIAL.write(“Try to reconnect to BLE now\n”); // usb terminal probably won’t work after sleep…
// reconnect fails with “gatt status 133” ifdef WAKEINTERRUPT
}

void loop() {
// put your main code here, to run repeatedly:

}

void wakeRoutine(){

}

/////////////////////// added to ArduinoLowPower.cpp /////////////
// void ArduinoLowPowerClass::detachInterruptWakeup(uint32_t pin){
// detachInterrupt(digitalPinToInterrupt(pin));
// }
//////////////////////////////////////////////////////////////////

/////////////////////// added to bsp.c //////////////////////////
// void BSP_attachIRQ(int irq_pin){
// attachInterrupt(digitalPinToInterrupt(irq_pin), External_Handler, FALLING);
// }
// void BSP_detachIRQ(int irq_pin){
// detachInterrupt(digitalPinToInterrupt(irq_pin));
// }
//////////////////////////////////////////////////////////////////

/////////////////////// routines added to MG126_Ble_Class ////////
/*
void MG126_Ble_Class::ble_detachIRQ(){
BSP_detachIRQ(irq);
}

void MG126_Ble_Class::ble_fixSPI(){
radio_fixSPI(cs,PIN_SPI_SCK,PIN_SPI_MOSI);
}

void MG126_Ble_Class::ble_attachIRQ(){
BSP_attachIRQ(irq);
}

unsigned char MG126_Ble_Class::ble_cansleep(){
return ble_run_interrupt_McuCanSleep();
}
*/

Note that in the above, after the comment “// initialize BLE system”, the following line should have been included:

MG126_Ble.ble_init();

Please add this to your code if you are trying to reproduce the problem.