Re-initialisation of input after calling System.sleep()

Hi, all. I have an odd problem that I cannot currently identify a cause for. I have the following (abridged) set up:

SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);
setup(){
    pinMode(WKP,INPUT_PULLUP); //WKP pin (external open-drain regulator output requires pull-up)
    .... 
    }

    loop(){
        ... calls processCommand below when required
        ... re-reads 
            pwrGood.raw = (HIGH == digitalRead(WKP));

    }

    void processCommand(const char* textMessage)
    {
        String message=String(textMessage);
        String requestCommand="";

        // parse message string into passcode, command and optional option.
        ...
       
        if (requestCommand == "reset") {
              System.sleep(SLEEP_MODE_SOFTPOWEROFF, 5);
        }
    }

My issue is that, on initial boot/button reset, the pullup is applied to the WKP pin. I can verify this externally, using a DMM (3.3V on the pin, when not pulled down by regulator output).

After I call the System.sleep(…) command above, programmatically, the device does a soft power-off and comes up after 5 secs, as expected, but the pullup is no longer applied to the WKP pin. This leaves the pin floating, typically <1.0V. The external regulator is producing normal output, and is not otherwise able to be affected by the electron’s actions, so power is ‘good’.

I have confirmed, using Log.trace(), that the setup() code is re-executed after the software reset, but it doesn’t seem to restore the input pinMode(WKP, INPUT_PULLUP); setup requested?

The result for my code, is that the WKP pin is read as LOW, and a ‘power supply fault’ state is recorded for my application’s purposes.

Any clues as to why the input pullup doesn’t get re-applied after a software instantiated reset?

What is the reason for using the WKP pin for that?
Typically the WKP pin is meant to wake the device from deep sleep with a rising edge on that pin - which implicitly means there will/should be a pull-down resistor applied while sleeping.

Are you using SYSTEM_THREAD(ENABLED)?
How does your code behave when you apply pinMode() somewhat delayed after wake?

1 Like

Hi @ScruffR, The WKP/A7 pin is being externally driven by an open-drain (to ground) output from the power regulator I’m using. The output goes high-impedance when the regulator has ‘good’ regulated voltage at its output, so the pull-up resistor provides bias for a rising edge (if used for that purpose). I designed the board that way in case I wanted restoration of external power to the main board to wake the Electron from a battery-backed, low-power mode but, for now, I’m only using the static level on that pin for a check of power supply health when the application is actively running. Indeed, I may have made a poor assumption about the pull-up being active when the STM32 is quiesced by System.sleep(), so an external pull-up resistor would have been wiser! Could be a board hack is required :frowning:

However, as I’m not edge-triggering to wake the Electron, but merely checking the level after the application is restarted, I would expect the GPIO pull-up to be restored in setup(), after software sleep/reset, as it is on hard reset or post dfu reset. This is what doesn’t appear to be happening.

I will re-apply the pinMode() again, in the main loop initialisation state, and see what happens.

I’m using SYSTEM_THREAD(ENABLED); and SYSTEM_MODE(MANUAL); (should’ve added that to my abridged code, sorry - post edited, now).

Since nothing beats a test case - I’ve cut back my app to the basic code that sets up and processes the input. This can be run stand-alone, as long as you have a meter to test the WKP/A7 pin, confirming the pull-up is not re-applied after System.sleep() initiated reset:

#include "Particle.h"
SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);

// Note A7 is also WKP 
#define PWRGOOD A7

// Set base timing intervals for various functional actions (mS)
const unsigned short DEBOUNCE_TIMEBASE=20;

typedef struct InputStruct{
    bool state;
    bool raw;
    unsigned short hist;
} InputStruct;

// digital Input variables (set aynchronously by interrupt or via timer event)
// pwrGood - from SMPS module (high impedance open drain if regulated output between 4 and 6V).
volatile InputStruct pwrGood;

Timer inputTimer(DEBOUNCE_TIMEBASE, _inputHandler);

//SerialLogHandler logHandler(LOG_LEVEL_ALL);
SerialLogHandler logHandler(LOG_LEVEL_TRACE);

// consume any typed char
void serialEvent()
{
    char c;
    while (Serial.available())
        c = Serial.read();
    switch (c) {
        case 'f' :
            Log.info("serEvt: Firmware DFU requested");
            delay(100);
            System.dfu();
            break;
        case 's' :
            Log.info("serEvt: set pinMode requested");
            pinMode(PWRGOOD,INPUT_PULLUP);
            break;
        case 'r' :
            Log.info("serEvt: reset requested");
            //System.sleep(SLEEP_MODE_DEEP,15);
            //System.sleep(SLEEP_MODE_SOFTPOWEROFF,15);
            delay(100);
            System.reset();
            break;
        case 'z' :
            Log.info("serEvt: sleep mode/reset requested");
            //System.sleep(SLEEP_MODE_DEEP,15);
            //System.sleep(SLEEP_MODE_SOFTPOWEROFF,15);
            delay(100);
            System.sleep(SLEEP_MODE_DEEP,10);
            break;
    }
}

void setup() {
    //Configure Sensing
    pinMode(PWRGOOD,INPUT); //WKP pin (PGOOD, requires pull-up)
    pinMode(PWRGOOD,INPUT_PULLUP); //WKP pin (PGOOD, requires pull-up)
    
    // disabled for now, to reduce variable factors // 
    // attachInterrupt(PWRGOOD, _pwrInterrupt, CHANGE);
    
    // initialise
    pwrGood.raw = (HIGH == digitalRead(PWRGOOD));
    inputTimer.start();
    Serial.begin();
    // seems to need several seconds to set up Serial if you want it to wait for terminal reliably
    delay(2000);
    while(!Serial.isConnected()) {
        delay(500);
    }
    Log.info("setup(); pwrGood.raw: %d", pwrGood.raw);
}

void loop() {
    Log.info("loop(); Pwr Good.raw/.state: %d/%d", pwrGood.raw, pwrGood.state);
    delay(2500);
}

// timer handler for physical inputs (Switches, Sensing)
// Called once per INPUT_TIMEBASE interval (mS) to process current values of input globals
// Effectively, used to debounce inputs.
// The variables processed by this handler could change in real-time due to interrupts,
// however, the debounce function won't care - it just tests the raw value once per call. 

    void inputDebouncer(volatile InputStruct &input) 
    {
        if (input.raw) input.hist++;
        else input.hist--;
    
        if (input.hist > 8) input.hist = 8;
        if (input.hist < 1) input.hist = 1;
    
        //hysteresis
        if (input.state) {
            if (3 > input.hist) input.state = false;
        } else {
            if (6 < input.hist) input.state = true;
        }
    }

void _inputHandler()
{
    // polling power good IO for now, in case interrupt handler issue after sleep()
    pwrGood.raw = (HIGH == digitalRead(PWRGOOD));
    // debounce inputs
    inputDebouncer(pwrGood);
}

// if power regulator running, pwr good input is low
void _pwrInterrupt(){
    pwrGood.raw = (HIGH == digitalRead(PWRGOOD));
}
// End

While it’s probably not pertinent, I left in my timer based debounce code, to reflect the actual use case.

Sample output:

Response after post-DFU reset:

0000002054 [app] INFO: setup(); pwrGood.raw: 1
0000002054 [app] INFO: loop(); Pwr Good: 1
0000007057 [app] INFO: loop(); Pwr Good: 1
0000012059 [app] INFO: loop(); Pwr Good: 1
0000013072 [app] INFO: serEvt: sleep mode/reset requested

<-- usb serial disconnect
in new terminal session -->

Response after System.sleep()

0000002054 [app] INFO: setup(); pwrGood.raw: 0
0000002054 [app] INFO: loop(); Pwr Good: 0
0000007057 [app] INFO: loop(); Pwr Good: 0
0000012057 [app] INFO: serEvt: set pinMode requested
0000012059 [app] INFO: loop(); Pwr Good: 0
0000017062 [app] INFO: loop(); Pwr Good: 0

[Edited to include reset() and sleep() test.]

If the System.reset() call is used, the Electron behaves as expected, and re-asserts input pull-up in setup(). Only System.sleep(…) calls seem not to restore the pull-up, and actively prevent the pull-up being subsequently restored by repeated pinMode() requests (‘s’ key action above).

I think I have now confirmed this with this simple sketch here

void setup() {
    pinMode(A7, OUTPUT);
    pinMode(A6, OUTPUT);

    digitalWrite(A7, HIGH);
    digitalWrite(A6, HIGH);

    delay(5000); 

    pinMode(A7, INPUT_PULLUP);
    pinMode(A6, INPUT_PULLUP);
    
    delay(10000);
    
    System.sleep(SLEEP_MODE_DEEP, 30);
}

When bridging A6 or A7 (WKP) over to the D7 LED one can see that A6 works as expected but A7 does ignore any pinMode(). Not even OUTPUT is accepted after the first wake.
I’ve filed a bug report here

1 Like

Cheers, @ScruffR. I was hoping I’d done something wrong that wasn’t clear from the doco, but it looked like a bug to me, too. I can work around it using System.reset(), for now.

My original plan around sleeping the Electron has already been thwarted, anyway. I had hoped to use:
System.sleep(wakeUpPin, edgeTriggerMode, seconds, SLEEP_NETWORK_STANDBY);
with the RI_UC line from the modem (set to rise for incoming call/text).

Unfortunately, RI_UC is not able to be used in the sleep() call as a wake-up signal line.

No biggie, the project will run with solar power in the day and off a big enough battery at night not to actually need the sleep mode.

Cheers,
AT

It is. I have done it before - there is just no way to wire it externally and you need to set the trigger to FALLING.

1 Like

Thanks! I previously tried it with RISING, as that’s the pattern I expected from other behaviour. It didn’t work, so then I read the doco (!) which suggested only a certain set of GPIOs could be used, anyway. Hence, my comment. I’ll give it a go with FALLING.

Hi, I never got back to updating this. I tried RISING, FALLING and CHANGE, all to no avail, unfortunately. I’m still of the opinion that the RI_UC input is not one that can be used to trigger the wake-from-sleep event/interrupt. It would be good to see the sample you got working, @ScruffR?

In the meantime, it’s not significant for the project, which is now installed and working reliably. I’m just leaving it powered up full-time at the moment. It’s attached to a lead acid starter battery (with a solar trickle charger), so it’s in no particular danger of exhausting the supply!

One I may revisit in autumn/winter, when the imminent threat of bushfires is lessened and I can decommission the beast for a month to further enhance it. It’s gold plating, really!

Cheers, AT

@apt, this is some test code that works for me

SYSTEM_MODE(MANUAL)
STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY))

retained int sleepSince;

void setup() {
  Cellular.on();
}

void loop() {
  delay(10000);
  Serial.println("Going to sleep in");
  for (int i = 10; i; i--) {
	Serial.printf("\r %2d \t", i);
    delay(1000);
  }
  Serial.println();
  sleepSince = Time.now();
  System.sleep(RI_UC, FALLING, 60, SLEEP_NETWORK_STANDBY);
  delay(10000);
  Serial.printlnf("Slept for %d seconds", Time.now() - sleepSince); 
}

and this is the output I get from it

PS C:\Particle\Test\RIUCWake> particle serial monitor --follow
Polling for available serial device...
Opening serial monitor for com port: "COM3"
Serial monitor opened successfully:
Going to sleep in
  1    Serial connection closed.  Attempting to reconnect...
Serial monitor opened successfully:
Slept for 40 seconds
Going to sleep in
  1     Serial connection closed.  Attempting to reconnect...
Serial monitor opened successfully:
Slept for 70 seconds
Going to sleep in
  1     Serial connection closed.  Attempting to reconnect...
Serial monitor opened successfully:
Slept for 70 seconds
Going to sleep in
  1     Serial connection closed.  Attempting to reconnect...

The first wake (40 seconds) relates to the wake via SMS or ring the 70 sec (60 sleep + 10 delay) are the timed wake.

SYSTEM_MODE(MANUAL) is only to save data with my 3rd party SIM, but is not required to get RI_UC to work.

2 Likes