RAK3172 RUI awake sleep(ms) from Pin?

Question: With RAK3172 RUI4 should I be able to use PIN 32 to awaken from

api.system.sleep.all(sleepDurationMs);

when the RAK3172 configuration includes LPM=1 and LPMLVL=2

Setup includes:

api.system.sleep.setup(RUI_WAKEUP_RISING_EDGE, PIR_GPIO_PIN_NUMBER);
api.system.sleep.registerWakeupCallback(pirWakeupCallback)

The purpose is to abort a long sleep duration when a PIR detection occurs.

Yes, that should be possible. Did you try it?

@Bernd Thanks for the prompt feedback! Yes,I am trying to have both the time duration and pin rising signal awake the sleep.all timer.

Since you comment that it should work, I will continue to investigate why the rising level on the GPIO pin is not waking the RUI sleep timer.

Thanks!

How do you initialize pin 32. It is PB3 but it might be pre-configured as an analog input (ADC1).

Do you set it up as an input with pinMode(PB3,INPUT); ?

@beegee I wrote a test program which seems to indicate at least PIN32 does not wake the RAK3172 from sleep.all(). I tested with a PIR (monitoring the Pin value with a meter as well). Here is the program and section of serial monitor listing of my testing. It seems like only the sleep timer calls the registerWakeCallback routine. I have not attempted yet with AT commands instead of RUI API.

// -------------------------------------------------------------
// RAK3172 (RUI3 v4.2.1+) PIR Wake Test with GPIO-Verified Wake Reason
// - Logs PIR state before sleep and after wake
// - 5s awake, 10s sleep
// - PB3 (Pin 32) = PIR digital input, rising-edge wake
// - PA9 (Pin 20) = LED output, LOW = ON, HIGH = OFF
// -------------------------------------------------------------

#include “Arduino.h”

// -------------------------------------------------------------
// Pin definitions
// -------------------------------------------------------------
#define PIR_PIN PB3 // Arduino-style pin for pinMode(), digitalRead()
#define PIR_PIN_GPIO 32 // RUI3 GPIO number for sleep.setup()

#define LED_PIN PA9 // Pin 20 (LED output)

// -------------------------------------------------------------
// Wake reason flag (set ONLY by callback, for visibility)
// -------------------------------------------------------------
volatile bool pirWakeFlag = false;

// Forward declaration
void pirWakeupCallback(void);

void setup() {
Serial.begin(115200);
delay(200);

Serial.println("\n--- RAK3172 PIR Wake Test (PIR State Before/After Sleep) ---");

// LED output
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);   // LED ON when awake

// PIR input (hardware debounce)
pinMode(PIR_PIN, INPUT);

// Configure wakeup source (requires numeric GPIO ID)
api.system.sleep.setup(RUI_WAKEUP_RISING_EDGE, PIR_PIN_GPIO);

// Register callback
api.system.sleep.registerWakeupCallback(pirWakeupCallback);

Serial.println("Setup complete. Waiting for PIR or sleep cycle...");

}

void loop() {
// Indicate awake
digitalWrite(LED_PIN, LOW); // LED ON
Serial.println(“Awake. Running for 5 seconds…”);
delay(5000);

// Clear wake flag before sleeping (for logging clarity)
pirWakeFlag = false;

// ---------------------------------------------------------
// Log PIR state BEFORE sleep
// ---------------------------------------------------------
bool pirBefore = digitalRead(PIR_PIN);
Serial.print("PIR state BEFORE sleep: ");
Serial.println(pirBefore ? "HIGH" : "LOW");

// Prepare to sleep
Serial.println("Going to sleep for 10000 ms unless PIR triggers...");
digitalWrite(LED_PIN, HIGH);  // LED OFF just before sleep

// Enter low-power sleep (LPMLVL=2 supported)
api.system.sleep.all(10000);

// Execution resumes here after timeout OR PIR wake
digitalWrite(LED_PIN, LOW);   // LED ON immediately after wake

// ---------------------------------------------------------
// Log PIR state AFTER wake
// ---------------------------------------------------------
bool pirAfter = digitalRead(PIR_PIN);
Serial.print("PIR state AFTER wake: ");
Serial.println(pirAfter ? "HIGH" : "LOW");

// ---------------------------------------------------------
// GPIO-verified wake reason
// ---------------------------------------------------------
if (pirAfter == HIGH) {
    Serial.println("WAKE REASON: PIR (PB3 is HIGH after wake)");
} else {
    Serial.println("WAKE REASON: TIMEOUT (PB3 is LOW after wake)");
}

}

// -------------------------------------------------------------
// PIR wake callback
// -------------------------------------------------------------
void pirWakeupCallback(void) {
pirWakeFlag = true; // Mark that RUI3 thinks PIR caused the wake
Serial.println(“>>> PIR WAKEUP CALLBACK TRIGGERED <<<”);
}

Serial Monitor: (see time stamp starting at 11:29:34.762), PIR active during sleep failed to awaken sleep timer.

11:29:14.685 →

11:29:14.685 → — RAK3172 PIR Wake Test (PIR State Before/After Sleep) —

11:29:14.722 → Setup complete. Waiting for PIR or sleep cycle…

11:29:14.722 → Current Work Mode: LoRaWAN.

11:29:14.722 → Awake. Running for 5 seconds…

11:29:19.738 → PIR state BEFORE sleep: LOW

11:29:19.738 → Going to sleep for 10000 ms unless PIR triggers…

11:29:29.705 → >>> PIR WAKEUP CALLBACK TRIGGERED <<<

11:29:29.748 → PIR state AFTER wake: LOW

11:29:29.748 → WAKE REASON: TIMEOUT (PB3 is LOW after wake)

11:29:29.748 → Awake. Running for 5 seconds…

11:29:34.762 → PIR state BEFORE sleep: LOW

11:29:34.762 → Going to sleep for 10000 ms unless PIR triggers…

11:29:44.730 → >>> PIR WAKEUP CALLBACK TRIGGERED <<<

11:29:44.730 → PIR state AFTER wake: HIGH

11:29:44.730 → WAKE REASON: PIR (PB3 is HIGH after wake)

11:29:44.776 → Awake. Running for 5 seconds…

11:29:49.771 → PIR state BEFORE sleep: HIGH

11:29:49.771 → Going to sleep for 10000 ms unless PIR triggers…

11:29:59.742 → >>> PIR WAKEUP CALLBACK TRIGGERED <<<

11:29:59.778 → PIR state AFTER wake: LOW

11:29:59.778 → WAKE REASON: TIMEOUT (PB3 is LOW after wake)

11:29:59.778 → Awake. Running for 5 seconds…

(1) It looks like the PIR trigger is detected, because of >>> PIR WAKEUP CALLBACK TRIGGERED <<<, but it seems loop is not called immediately. Not sure why, could be a bug in RUI3.

(2) Are you sure that your PIR output goes to HIGH when triggered?
The PIR that I have at hand (RAK12006) has pulls its output to low when triggered pulls.

(3) My approach to your functionality
I am usually not using the api.system.sleep.setup and api.system.sleep.register functions when handling interrupt driven events.

With LPM and LPMLVL set, the device goes automatically into low power mode unless something happens that wakes it up, e.g. timers, interrupts, LoRa or LoRaWAN events.

My loop() just calls api.system.sleep.all(); and the interrupt event is handled in a separate function. As the interrupt callback should be short, I use a timer to call a handler from the interrupt callback and handle the event there.

Log from my code:

--- RAK3172 PIR Wake Test (PIR State Before/After Sleep) ---
Setup complete. Waiting for PIR or sleep cycle...
Current Work Mode: LoRa P2P.
>>> PIR WAKEUP CALLBACK TRIGGERED < < <
PIR state AFTER wake: LOW
PIR state AFTER wait: HIGH
>>> PIR WAKEUP CALLBACK TRIGGERED < < <
PIR state AFTER wake: LOW
PIR state AFTER wait: LOW
>>> PIR WAKEUP CALLBACK TRIGGERED < < <
PIR state AFTER wake: LOW
PIR state AFTER wait: HIGH
>>> PIR WAKEUP CALLBACK TRIGGERED < < <
PIR state AFTER wake: LOW
PIR state AFTER wait: HIGH
>>> PIR WAKEUP CALLBACK TRIGGERED < < <
PIR state AFTER wake: LOW
PIR state AFTER wait: HIGH
>>> PIR WAKEUP CALLBACK TRIGGERED < < <
PIR state AFTER wake: LOW
PIR state AFTER wait: HIGH

My code:
WakeFromPIR.ino (4.7 KB)

@beegee Excellent guidance!
The code now works as expected using your alternative approach with

api.system.sleep.all();

for indefinite duration, and a separate one-shot timer like

api.system.timer.create(RAK_TIMER_0, pirHandler,RAK_TIMER_ONESHOT);

to wake up along with an interrupt from a GPIO pin (to abort the timer delay) like

attachInterrupt(PIR_PIN, pirWakeupCallback, RISING);

I also disable and re-enable the GPIO interrupt as per your example, and stop the timer upon interrupt from GPIO.
I won’t necessarily call it a bug in RUI3

api.system.sleep.all(int ms_time = POWERSAVE_NO_TIMEOUT),

but it would be a nice feature for interrupts on GPIO to also work in this case as well as the other system timer case.

Eventually, I may redo the entire application as per your best practices of fully event driven with no loop() active. For now, it is good to have the current active loop() working!

Thank-you for your responsiveness and support.