Having trouble using Sleep() with wakeUpPin

Thank you for your code. I do have to start using debounce, but I didn’t bother because I didn’t think that was the cause in this instance. If CHANGE does not work, why is it listed as an option in the documentation? I also found that it did not work. I updated my response above to say that I was able to get it working, but now have a new problem… It seems that when the Core wakes up (no matter if it is by interrupt or by timer) it runs the interrupt function associated with that pin… Here is my current code:

const int buttonpin = D0;
const int sleepbuttonpin = D1;
const int servopin = A0;

const int closepos = 1;
const int openpos = 180;

int servoflag = 0;
int sleepflag = 0;
int currentpos = 0;

Servo myservo;

void setup() {
    pinMode(buttonpin, INPUT_PULLDOWN);
    pinMode(D1, INPUT_PULLDOWN);

    currentpos = closepos;
    
    attachInterrupt(buttonpin, moveservo, FALLING);
    attachInterrupt(sleepbuttonpin, sleepmode, FALLING);
}

void loop() {
    
    if (servoflag == 1) {
        myservo.attach(servopin);
        
        if (currentpos == closepos) {
            myservo.write(1);
            delay(250);
            myservo.write(openpos);
            delay(1000);
            currentpos = openpos;
        } else if (currentpos == openpos) {
            myservo.write(1);
            delay(250);
            myservo.write(closepos);
            delay(1000);
            currentpos = closepos;
        }
        
        myservo.detach();
        servoflag = 0;
    }
    
    if (sleepflag == 1) {
        Spark.sleep(buttonpin,RISING,20);
        //Spark.sleep(D2,RISING,20);
    }
}

void moveservo() {
    servoflag = 1;
}

void sleepmode() {
    sleepflag = 1;
}

So in the code above the D0 pin is being used for the moveservo() function AND the wake interrupt. If I use the other button to put the Core to sleep and wait 20 seconds (or press the D0 button), the Core wakes up and the servo runs once as if I pressed the D0 button. Why would a wakeup trigger that interrupt?

Sorry for dwelling on this, but I think I've suggested the use of RISING or FALLING - knowing the possible issues - before (even in my first reply), but it is a bit frustrating if suggestions are not even given a chance.
And it is not that CHANGE doesn't work, but you'd need to use it in the correct context - which an open switch isn't, but any circuitry that never floats the pin would be (as my post above gave an example for)

The reason why your INPUT_PULLDOWN didn't make a difference is, that designating one of the three trigger options does implicitly require a pin mode that fits that desigantion (which for CHANGE is more like INPUT without pull-up/-down) - you'd not even need to set the pin mode at all, since designating it as interrupt does implicitly do that anyhow, but it's still good practice to do it (but correctly).
Edit: It does go for Spark.sleep(pin, trigger [, timeout]), but NOT for attachInterrupt() - I was wrong :blush:
At least this is what happens when doing attachInterrupt() and I guess the same goes for a wake up interrupt, but here @peekay123 might have a better founded insight.

Especially since my own experience does not fit to this

Paul ( @peekay123 ) could you please set me right on this?

@ScruffR, @mnetwork, the STM32 does support falling, rising and falling&rising trigger configurations. Furthermore, it is correct to first set the pinMode() for the interrupt pin prior to calling attachInterrupt(). The latter does not change the pinMode() on the pin.

So, I agree with @ScruffR that even though the interrupt pin is set to INPUT_PULLDOWN, it may be too weak to ensure noiseless operation, ie. without false triggering, in CHANGE mode. Instead, the pin should be set to INPUT_FLOATING with a strong (10K ohms) external pull-up or pull-down resistor. I will be testing this tonight in both pull-up and pull-down configurations. :smile:

Thank you. That theory does make sense. Let me know what you find with that.

What do you think about the other issue? I am trying to use a pin for BOTH an interrupt and a wake interrupt. When the Core wakes (via timeout or interrupt) it triggers the function associated with the interrupt. Why would this be? I now I can get around this by checking mills() before processing the code in the function, but I’m curious as to why this is happening.

Sorry Paul, but I'm somehow inclined to contradict, not because I have written prove, but out of experience.
Edit: Paul was right for attachInterrupt() after all, but for sleep() it does happen as mentioned
Given this little test sketch does work although pinMode() was set to OUTPUT prior Spark.sleep(), this does strongly suggest to me that some mode switching must take place (similar behaviour I've seen with attachInterrupt()) and I think to recall one of our Spark hardware geeks having said something similar too, but I can't find the respective post anymore :weary:

const int MODEPIN = A0; 
const int WAKEPIN = D6;

void setup()
{
  pinMode(D7, OUTPUT);
  pinMode(WAKEPIN, OUTPUT);
  
  digitalWrite(D7, HIGH);
  delay(5000);
  digitalWrite(D7, LOW);
  delay(5000);
}

void loop()
{
  unsigned long ms = millis();
  int mode = analogRead(MODEPIN);
  int trig;
  
  if (mode < 10)
    trig = RISING;
  else if (mode > 4085)
    trig = FALLING;
  else 
    trig = CHANGE;

  while (millis() - ms < 2000)
    if (CHANGE == trig) digitalWrite(D7, !digitalRead(D7));

  Spark.sleep(WAKEPIN, trig, 10);
}

This test sketch also shows that the pull-downs/-ups do suffice for RISING and FALLING respectively.
If trying the sketch with D7 as WAKEPIN (while disabling the D7-code lines) you'll see how the pull-up gets attached automatically when sleep(WAKEPIN, FALLING, 10) gets called and when then pulling the pin LOW the Core wakes.
Assuming the invers behaviour for RISING and the pull-down (without visual prove on D7) would then lead to:
"If RISING causes a pull-down and FALLING a pull-up to be attached independent of the previously selected pinMode setting, the logical consequence would be that CHANGE would either attach both or none of the pull resistors, of which the latter would be the more logical way to go."

@ScruffR, no problem! The statement I made was in regards to attachInterrupt() not having implicit code that changes the pinMode(). This much is true. However, Spark.sleep() sets up the STM32 backup registers for a processor STOP mode just prior to calling NVIC_SystemReset(). On the reboot, the firmware resets the pinMode for the wakepin then goes into STOP mode waiting for the pin event. When the processor exits STOP mode, it continues and runs the user code from the start.

So in your code, no matter what pinMode you set for WAKEPIN, it will be overridden by Spark.sleep() and set to INPUT for CHANGE mode, INPUT_PULLUP for FALLING mode and INPUT_PULLDOWN for RISING mode. It is the CHANGE mode that is the issue because it is set to INPUT with neither pull-up or pull-down making it susceptible to noise. This is the issue we are seeing here. :smile:

1 Like

Again I haven't tested my theory and I have no prove, but just to rule some things out.
Would you try to set your interrupt flags to zero in setup, rather than at the declaration of the variables?
Another - but less likely - reason to consider might be the D-pin glitch on startup.

@ScruffR, good point on the startup glitching! It would be interesting to move the WAKEPIN to an analog pin which does not suffer from that glitch. Also, it would be worth testing with an external pull-up or pull-down resistor using the CHANGE mode in Spark.sleep(). This would put to rest the pinMode issue with Spark.sleep() being susceptible to noise. I’ll have to give that a shot tonight. :smile:

This was one of the first things I tested and it did not change the outcome. The function is still run.

1 Like

Which of the two suggestions I made?

One other thing came to mind. You are using the servo pin to wake the Core and also start the servo.
Maybe after waking you should keep looping till the button is released and only after that attach the servo interrupt.

Another thing is that you’re using INPUT_PULLDOWN first, but then attach an interrupt on the FALLING edge.

Edit: Since attachInterrupt() does no resistor switching this can go
Switching pull resistors might take a while and thus trigger the interrupt.

Again - theories that might be completely ridiculous, but unless falsified still thinkable :stuck_out_tongue_winking_eye:

1 Like

I tried setting the flag variable to 0 in setup().

If it was an issue with waking wouldn’t it happen when it is powered on too? I also tried using a different pin for the wake interrupt and that stopped it from happening, so the pin being used in sleep() is directly related. I will try putting that delay/check in between pinMode and attachInterrupt. Worse case I do a mills() check on the interrupt function to not let it set the flag during the first few seconds. It just seemed a little hacky to me to have to do so.

No since it is ENTER_STOP_Mode() via Spark.sleep() that sets the pinMode() used for waking which is ONLY set when the sleep mode is entered. :smile:

I'm not involved enough to answer this and I'm not going to read into the bootloader/powerup firmware to provide hard facts, but in theory (again :wink: - and I have been proven wrong before :blush: ):
While on power-up - I think @mdma said - all variables declared global and/or static will get initialzed by the Spark "framework", this was not said explicitly for wake-up too, so there could still be some difference in code paths.
The only thing that I remember being said for wake-up is that your user code will definetly go through setup().
So I'm not saying it is that way, but concluding from the fact that something does work at power-up, it would have to be the same on wake-up, might not actually have to be true.

I don't know! @peekay123 can say things better and in less words, so I'm always second :smile: I should maybe stop reading philosophy.

But back to work: If you want a less hacky approach for this, you could think up a way to have both - the wake and the servo - interrupts trigger on the same edge , circumventing my suspected pull-resistor-switching problem. Edit: Ridiculous suspicions on my side :blush:

@ScruffR and @mnetwork, let’s review please:

  1. Setting a pinMode for the pin used by Spark.sleep(pin, mode, time) PRIOR to calling Spark.sleep() does nothing since it will get set to pinMode(pin, INPUT) for mode==CHANGE after Spark.sleep() causes the core to reset and go into STOP mode. This pinMode MAY cause false triggering due to noise.

  2. When the Core is awakened, it will act as if the RESET button was pressed

  3. Digital inputs MAY glitch on power up causing a false trigger when the Core resets to go into STOP mode.

  4. Using an analog pin for the wakeup pin may alleviate 3 above.

I will test different scenarios tonight and report my findings.

2 Likes

Thank you. i will also do some testing.

1 Like

OK, I’ve done some testing too, and proven myself partly wrong :blush: and partly (sort of) right :wink:

As for all the references to attachInterrupt() causing a change in pinMode, I got it wrong. As Paul said this doesn’t happen (I’ll add according comments to my previous posts - to punish myself, before others will :wink: )
But for Spark.sleep() with wake on interrupt my additional tests still suggest, that pinMode including pull resistors are set to match the trigger option (as mentioned above). But I still could have chosen unfit test scenarios, so I’m anxiously awaiting Paul’s results.

So, I’m fully agreeing with Paul’s statement 1. I even got the impression, that the false triggering is more likely to happen than not - with floating pin this is.

As for 2. this is what I see too, but my “philosophical” discourse above was rather targeted at the risk to get bitten by erronous conclusions based on wonky data.

I agree that 4. would be a good test for 3.

Wow, @ScruffR, that must have hurt just to write it! :stuck_out_tongue: So here is what we have learned from this so far:

  • Using Spark.sleep(pin, CHANGE, time) will put the wake pin in INPUT mode, leaving it susceptible to noise (IMO). So to use this mode, an external pull-up or pull-down resistor is recommended.

:smile:

2 Likes

You bet! :stuck_out_tongue_closed_eyes:

And yes, it was my first suspicion that CHANGE was the culprit and should be avoided (for wake-up) by any means, if you can't be sure that you have a clean signal at any time (either dedicated pull resistors or a reliable signal source).
If this had been taken up immediately, I would not have humiliated myself - again :wink:

1 Like

So, I did more testing today and found this…

If I set button pin to use a PULLUP (and wired it to ground) and set the sleep interrupt for FALLING the server flag would not get set on wake up. It seems there is some kind of cycle happening during wakeup.

This was the only time using FALLING for the sleep interrupt worked. It seems that sleep interrupt can’t be used when it is cycled? You can’t set the switch to be wired to a 3.3v and set the sleep interrupt to be FALLING. It seems it can only detect the initial change (LOW -> HIGH) and not a cycle (LOW -> HIGH -> LOW). I was only trying to use a pulldown because I’ve always just assumed that a PULLDOWN would use less power when sitting (not pressing the button) than a PULLUP. After some reading I learned that neither uses more power when sitting.

This code is working just fine. I removed the sleep button now. I only put that in for testing.

const int buttonpin = D0;
const int servopin = A0;

const int closepos = 1;
const int openpos = 180;

int servoflag = 0;
int currentpos = 0;

Servo myservo;

void setup() {
    pinMode(buttonpin, INPUT_PULLUP);

    currentpos = closepos;
    
    attachInterrupt(buttonpin, moveservo, RISING);
}

void loop() {
    
    if (servoflag == 1) {
        myservo.attach(servopin);
        
        if (currentpos == closepos) {
            myservo.write(1);
            delay(250);
            myservo.write(openpos);
            delay(1000);
            currentpos = openpos;
        } else if (currentpos == openpos) {
            myservo.write(1);
            delay(250);
            myservo.write(closepos);
            delay(1000);
            currentpos = closepos;
        }
        
        myservo.detach();
        servoflag = 0;
        
        delay(500);
        Spark.sleep(buttonpin,FALLING,20);
    }
}

void moveservo() {
    servoflag = 1;
}
2 Likes

Good to hear that you've sorted your problem :+1:

Just out of interest, what do you mean by this?

I especially don't get the meaning of "when it is cycled"?
Is it power cycle, sleep-wake cycle, reset, ...
And what do you mean with "sleep-interrupt"?
Your previous interrupt sleepmode() or the wake-on-interrupt?

Since when I tested wake-on-interrupt, I could use any pin and edge trigger combination just fine.

The only thing that would prevent a wake would be that wake-on FALLING does attach a pull-up implicitly, thus you can't produce a falling edge with your button wired to HIGH.
When pressing the button, the already HIGH pin just remains HIGH and when you release the button the pull-up takes over again and keeps the pin HIGH still, so there never occures a falling edge, which would wake the Core.

Just to stress this again: When setting wake-on triggers, whatever pinMode() you set your pin before going to sleep will be lost. The selected trigger type does set its own pull-resistors ("overwriting" your previous choice).

1 Like