Detect which button caused wakeup

Hi, I am trying to build a very low power remote control device with multiple buttons: My goal is to put the MG24 in deep sleep most of the time. It should only wake up when a button is pressed, send a BLE advertisement and go back to sleep. How could I do this with two buttons? The part I am stuck on is waking up from sleep and detecting which button was pressed reliably.

I tried these two approaches:

  1. Attaching two pins as external Wakeup. This doesn’t seem to work, it seems only one pin can be set as wakeup pin at a time. E.g. with this code:

      LowPower.attachInterruptWakeup(PA5, nullptr, RISING);
      LowPower.attachInterruptWakeup(PD2, nullptr, RISING);
    

    Only Pin PD2 triggers a wakeup, the wakeup for pin PA5 seems to be overwritten.

  2. Check which button is pressed directly after wakeup (triggered by the same pin for all buttons). This works, but very short button presses are not detected.

Does anyone have other ideas how I could achieve this? Does the first approach really not work with the MG24 or did I do something wrong? Is there another board this would work with?

Thanks.

1 Like

Hi there,

So, you’re not doing anything “wrong” in principle – you’ve just run into two real constraints:

  1. MG24 hardware: only specific GPIOs can wake from the deepest low-power mode (EM4), not every pin.

  2. Arduino LowPower implementation: on the current XIAO MG24 Arduino core, LowPower.attachInterruptWakeup() basically sets up one EM4 wake source, and the second call overwrites the first.

That’s why:

LowPower.attachInterruptWakeup(PA5, nullptr, RISING);
LowPower.attachInterruptWakeup(PD2, nullptr, RISING);

only lets PD2 wake it – the second call wins, and the first setup is effectively lost.

On the Silicon Labs side, the underlying API does support multiple EM4 wake pins using a bitmask (e.g. GPIO_EM4EnablePinWakeup(pinmask, polaritymask) and GPIO_EM4GetPinWakeupCause()), so at the silicon level you can absolutely wake from several pins and even ask “which one fired?”. Silicon Labs+1

But the Arduino LowPower wrapper on MG24 doesn’t expose that; it’s simplified to “one wakeup pin”.

Why your second approach “almost works”

Check which button is pressed directly after wakeup (triggered by the same pin for all buttons). This works, but very short button presses are not detected.

That’s expected:

  • EM4 wake is edge/level triggered.

  • By the time the chip has fully woken and your code runs, a very short tap can already be gone, so your digitalRead() sees “no button pressed”.

You wake up correctly, but you miss the identity of a very short press.

Practical ways to solve this

:white_check_mark: Option 1 – Use one wake pin, but wire both buttons smartly

Use a single EM4 wake-capable pin (whatever attachInterruptWakeup is happy with), and then:

  • Wire each button to its own GPIO (for identification),

  • And also OR them together into the EM4 wake pin.

Simple version:

  • Buttons are active-low to GND.

  • Each button goes to:

    • Its own GPIO input (just like you have now), and

    • A shared “WAKE” GPIO (the one used with attachInterruptWakeup).

Then on wake:

  1. EM4 wake happened because some button pulled WAKE low.

  2. Immediately in setup() / start of loop(), you read both button GPIOs:

    • If Button 1 pin is LOW → Button 1.

    • If Button 2 pin is LOW → Button 2.

To make short presses reliable:

  • Add a small RC (e.g. 10–100 nF to ground with a suitable pull-up) or require ~50 ms press in UI (in practice people hold a remote button longer than that).

  • Or after waking, stay awake for 50–100 ms before going straight back to deep sleep; that gives you time to see the button still held.

This keeps EM4 power savings and is fully doable from Arduino.


:white_check_mark: Option 2 – Use EM2 “sleep” instead of EM4 and use normal GPIO interrupts

MG24’s EM2 current is already extremely low (≈1–2 µA range on the modules). xonstorage.z8.web.core.windows.net+1 For a BLE remote, that’s often more than good enough.

If you use sleep (EM2) instead of deepSleep (EM4):

  • You can usually use standard GPIO interrupts on multiple pins.

  • Both buttons can have their own interrupt.

  • When any fires, the chip wakes and you know exactly which one it was, without tricky analog hacks.

So:

attachInterrupt(digitalPinToInterrupt(BUTTON1_PIN), isrButton1, FALLING);
attachInterrupt(digitalPinToInterrupt(BUTTON2_PIN), isrButton2, FALLING);
LowPower.sleep();   // EM2-style sleep, not EM4

Trade-off: slightly higher sleep current than EM4, but much simpler and you get reliable multi-button wake.

:white_check_mark: Option 3 – Drop Arduino and use the Silabs SDK directly

If you’re willing to go lower-level (Simplicity Studio / Gecko SDK instead of Arduino), you can:

  • Call GPIO_EM4EnablePinWakeup(pinmask, polaritymask) with multiple pins in the bitmask.

  • After wake, call GPIO_EM4GetPinWakeupCause() to know which pin caused it. Silicon Labs+1

That’s the “cleanest” solution architecturally, but it’s not exposed in the current Arduino core.


“Is there another board this would work with?”

If you want this to just work from Arduino without going into vendor APIs:

  • ESP32 is very flexible: it supports deep sleep with a bitmask of wake GPIOs and even lets you query which one woke it. Garry’s blog

  • Some other boards/cores also allow multiple wakeup sources, but it’s very platform-dependent.

On MG24 specifically, the limitation is not the silicon, it’s how the current Arduino LowPower.attachInterruptWakeup is implemented.

HTH

GL :slight_smile: PJ :v:

1 Like