Question about setDeepSleep

Posted on
  • Hi,

    I have a problem with the setDeepSleep. My understanding is that it should work on the Espruino Pico. Is this correct?

    It does lead to weird effects for me. Basically I am using the following snippet from the power tutorial (with different constants):

    setInterval(function() {
        digitalWrite(LED1, 1);
        setTimeout(function () {
            digitalWrite(LED1, 0);
        }, 5000);
    }, 10000);

    The code works fine when connected to USB (my understanding is that deep sleep is disabled in that case anyway). It does not work when connected to a battery. Instead of blinking for 5 seconds every 10 seconds, it blinks every 10 seconds for like 3 ms (measured by monitoring the current consumption with an oscilloscope).

    If I remove the setDeepSleep, then it works fine even with a battery - albeit with a lot higher power consumption, of course. I monitor the battery voltage and stays constant at about 4 V (Li-Ion).

    Am I doing something wrong?


  • Hi Stevie,

    Your code looks fine to me. I just checked it out here and I have exactly the same problem, on both boards.

    Sorry about that - I have absolutely no idea how this got missed (it seems to have been like it for a few versions). I think it's because I'd been testing with things similar to:

    setInterval(function() {
    }, 10000);

    Which don't set a timeout from within an interval.

    I'll try and get this fixed today and will push out a release.

  • It's proving a bit difficult to find the problem, so this could take a while.

    In the mean time you can probably get what you want by making sure you wake up at smaller time intervals (add a setInterval("",1000);). It won't be quite as efficient, but you'll still get some power savings.

  • Thanks for looking into this!

    Actually I started with smaller intervals (2s and 1s) and thought that maybe the small interval is the problem and switched to a bigger one. I can retry that later today and play with even smaller ones. But it's not too urgent on my side.

  • I think it works as long as you have another interval waking it up relatively frequently (don't know how accurate that is though).

    When it's sorted you won't have to worry though. Also, as part of the USB HID support I should be able to get the current consumption in deep sleep down a lot more (currently the USB hardware is still partially on).

  • Okay, sounds great! Especially the second part...

  • Errata: ...but keep reading... bottom line: output GPIO pins are still driven, even in deep-sleep, and issue was cause by - now fixed - firmware.

    I wonder anyway why to go to a deepsleep and expecting the the pins still to power something... To me, a deep sleep is switching everything possible off but the mechanism to be woken up by some timer...

    Looking at the code it is clear why the LED is only on for that much time:

    • The interval wakes Espruino up and the 'outer' anonymous function executes:
      • LED is switched on
      • Timeout is set for LED to go out
    • Espruino is done with what has to be done in the interval and goes back to deep sleep... which of course switches the LED off.

    What this code and observations say: it takes 3ms for Espruino to setup the timout and to be back in deep sleep ;)

    @Gordon may fix the issue with the timeout waking the thing up again (separate time event replacing the first one and every other event would have to be recalced... time event list could fix that... but such a list may not be there and there is only on time event handled for now). Coming back, I would expected - at best - that the pin state is to be restored(?), even though it makes not much sense - it would light the LED for an other few ms to the point where it is turned off for good.

    Since the LED is drawing power anyway, you might need to go for a different approach: have an external (CMOS) flip flop (built with 4011 quad NAND gates) that is never gona sleep, but is triggered by Espruino with two different, catch-22 cycling timeouts pulsing on two different pins the flip-flop's R and R-, respective. The remaining 2 NANDs you may use in parallel to get the current for the LED... or even parallel one of the flip-flop's NAND - to tripple it to get enought current. The CMOS 4011 is living on a very frugal budget - and with Espruino in deep sleep practically all the time, it is just the LEDs 50% duty-cycle that runs the battery down.

    Looking at a datasheet, you get about 1..1.5mA from that: Not a bright LED. Adding a (Darlington) transistor will give you the current you need...

    Said so - Darlington - you may get away just with that: every 10 seconds you give a pulse for some time to charge a capacitor that then feeds the Darlington for 5 seconds LED-on time... This saves you a pint AND a 4011. The LED is though not that steady... it fades (I have a cheap digital multi-meter that works like this. A button press charges the capacitor, then the backlight is on steady for few seconds, before it begins to fade and go out completely).

    Nice 'problem' solving challenge!

  • @allObjects: I don't think it is so natural, that putting the CPU in deep sleep mode switches off the outputs. With other MCUs I used before, this does not happen.

    In any case, the reproduction case I mentioned is just that: A reproduction case. I was using a much longer sleep and then sent out a radio packet using the NRF24 module. That did not work, that's why I tried it with the LED and found out that it does not work either. What I also tried was to use "setDeepSleep(0)" inside the interval timer and switch it on again when the LED was switched off. That did not work either.

  • Errata: ...but keep reading... bottom line: output GPIO pins are still driven, even in deep-sleep.

    I don't think it is so natural... deep sleep mode switches off the outputs.

    The part 'deep' makes it natural...:

    Looking at the datasheet, deepsleep vs just sleep does what it says: deep sleep (called stop mode vs sleep mode) switches everything off but (one single?) internal timer (and connection to input) for the wake up. If a pin gate is not driven, it floats... otherwise one would not get down to the desired power savings with consumption of microAmps only.

    Even when the deep sleep is not enabled, Espruino - when done with an execution phase - already goes - by its intended architectural nature - into sleep mode - a sleep mode with all the peripherals still powered.

    Every code execution phase is actually just an interrupt handling - and that makes Espruino event driven. The processor is busy only after a power on event, timer event, input pin event, or I/O-subsystem event. That is what makes Espruino conceptionally totally different from Arduino and a-like for programming. There is no infinite execution loop...

    The setDeepSleep() sets a flag, which is checked when the Espruino JS interpreter is done with handling the interrupt. If deep sleep is enabled, Espruno JS interpreter commands the processor into a stop mode (there are various run modes, sleep modes, stop modes, and even a - Espruino not used - standby mode). Check out pages 26 and 67 and previous ones of Pico's datasheet and page 48 and around of Espruino original board's datasheet.

    @Gordon can tell us off the bat which one he has chosen for the sleep and deep sleep modes for Espruino. The Power Consumption page is not that specific... and your observations contradict the example: I'm not sure if it is correct (any more), because you measured 3ms flashes only, where the example uses 20 ms and the page says that you should be able to have flashes up to 1.5 sec (pending callback), because that's the deep sleep entry/timer cycle/interval. May be the sample was tested and it flashed... worked! ...sort of... because who can say by the naked eye if it is 3ms or 20ms... or was the flash time measured with an oszi? ;-)

    With some extra (peeking and) pooking you may tweek the processor in a '@Stevie sleep mode' - the way serves your needs and affordable by the chosen battery.

    @Gordon - 2 things:

    • I did not see a link to Pico's datasheet on Pico's board reference page (yet). Standard board reference page has that convenience.
    • On power consumption page - under stop mode - the last item just says 'before sleeping' and is confusing, because it means 'before deep sleping:

    All external peripherals (including timers for PWM) will stop. Espruino does not currently detect if any of these timers are in use before sleeping.

  • @Gordon - I checked the pulse example you gave above and it does work for me.

    @allObjects - Thanks for checking the data sheet. I did not check it myself, because the example suggested, that this could work. And for example the AVR Attiny allows a deep sleep but will maintain the pin values. Also for example the NXP LPC 810 which I often use as a barebones chip has a power mode which has very low consumption but still drives the IO pins.

    In any case, as written above, I tried the following as well:

    function onInit()
      setInterval(function() {
        digitalWrite(LED2, true);
        setTimeout(function() {
          digitalWrite(LED2, false);
        }, 100);
      }, 1000);

    If that would work, then I could achieve what I need. But it does not work either. Internally I would assume it does work because the pulse thing does work and it would have to disable the deep sleep according to your finding in the data sheet to be able to drive the pin.

    BTW: Although you can actually see the visual difference between a 3ms LED pulse and a 20ms LED pulse (the 20ms is much brighter), I did use the oszi as written in the original post to find out the time.

    For the interrupt driven operation: I think that's what @Gordon wants to make you believe :-). I don't think it is actually the case. In fact I believe it cannot be done. The way it usually works is that after initialization of the MCU, the processor then goes into a sort of main function which starts an endless loop. It will sleep in that loop until an interrupt occurs. In the interrupt handler it will have a very short routine which schedules some event. Then the loop in main wakes up and will handle the event. Then it goes to sleep again. I guess that's also why the documentation says that the deep sleep will come into effect when the current JavaScript execution is finished. That's when it returns to the main loop. Then it can sleep.

    If you would do everything in the interrupt then all other interrupts would be blocked and that would be bad for the IO timing if you do something which is longer than a few microseconds... And I wrote I believe it cannot be done in that pure way, because the loop is actually needed. Otherwise the CPU would crash (I believe). The least you need to have is something like "while(1) __WFI;".

  • Espruino is done with what has to be done in the interval and goes back to deep sleep... which of course switches the LED off.

    That's not the case at all. Espruino goes into deep sleep (STOP) modes while still keeping power on all GPIO outputs.

    Pretty much all peripherals are powered down, except GPIO which still works (or setWatch wouldn't do anything).

    The original code should definitely work, and did work in the past - it's a bug that it doesn't.


    @Stevie you're spot on. When in deep sleep, you get woken by an interrupt. If that's a watch, it pokes an event into queue and returns (if it's a timer it does nothing except wake up). Espruino then runs around checking timers and also the event queue and executes any JS that needs running.

    There's one exception in that you can use setWatch(...., { irq:true }) which will execute a native code function in the IRQ itself. However that function can't even be compiled JS at the moment, as you can get into all kinds of trouble by trying to allocate a Variable while the main loop is also trying to do the same.

  • Just to add, the words I use for sleep are a bit misleading...

    • setDeepSleep(0) the STM32 goes into SLEEP mode (via __WFI) when it's not doing anything - all peripherals are still powered on.
    • setDeepSleep(1) the STM32 goes into STOP mode when it's not doing anything - the high speed oscillator is off, as are most peripherals. It would be the default, but having it on means USARTs/etc won't work.
  • Ok, fixed! It turned out to be completely unrelated to deep sleep - the bug was just way more obvious when running with deep sleep :)

  • Espruino is done with what has to be done in the interval and goes back to deep sleep... which of course switches the LED off.
    That's not the case at all. Espruino goes into deep sleep (STOP) modes while still keeping power on all GPIO outputs.
    Pretty much all peripherals are powered down, except GPIO which still works (or setWatch wouldn't do anything).

    That's quite confusing... But to verify from a different point of view: In deep sleep, if a GPIO pin is on output and high, and I place a drain of 10mA - LED to GND with current limiting resistor - does that mean that the 'processor' draws something between 10.5..11mA - 10 for the LED and some 0.5+mA for the rest of the chip? Or, in other words: driving an output stage is included in the 0.5+mA, but of course the sourcing not, and there is no need and no gain what so ever to put something external to it?

    Another clarification: peripherals means more than just a GPIO pin is sensing (input) or is driven (output), such as any serial I/O, because handling any serial I/O needs the high speed oszillator to run and drive the cpu crunching code. Is that correct?

    @Stevie, that's correct that there are two layers of execution code:

    1. the firmware that put's hardware events into the JS software event queue (with time and other information in order to not loose anything in case the (single threaded) JS interpreter is busy doing something at the moment of the hardware interrupt)
    2. the JS interpreter that runs until all interrupts in the JS software event queue are served.

    I would though still not call it 'an endless loop', because the concept of a '... loop' is very misleading, as noticed troughout the forum posts... Yes, as soon as a hardware interrupt happened, the JS interpreter is kicked off and loops emptying the interrupt queue.

  • @Stevie, quite interesting this NXP LPC800 family of processors. A very intriguing feature is the pattern matching engine. Do you have some experience with it? Does it - A) work synchronously on a number of pins or - B) asynchronously with a synchronous clocking pattern on a single pin? It is known that especially in RF it is a challenge to detect patterns in incomming stream of signals that also includes noise, and therefore a start/stop bit detection is a challenge. If it works asynchronously that would be really cool - of course, it requires some sort of shift register that is filled weighted by the speed cycle and then parallel accessed by this pattern matching algorithm to create a trigger for the application software to start doing something what is received next...

    In espruino the double buffered wave on input could include kind of a similar route, but a processing in the Javascript interpreter is just too slow for pattern detection and there is also no shifting involved. It is a cool thing though, because it keeps recording to one buffer in the firmware layser while the other is processed in the javascript interpreter. Using assembled javascript can/could speed up the processing, but it the shifting would still be missed. @Gordon, could you think of an additional buffer mode, such as a circular buffer with automatic modulo-addressing mechanism? With a circular buffer though, the callback invocation event needs a redefiniton...

  • if a GPIO pin is on output and high, and I place a drain of 10mA - LED to GND with current limiting resistor - does that mean that the 'processor' draws something between 10.5..11mA - 10 for the LED and some 0.5+mA for the rest of the chip?

    Yes, that's right.

    I'm not sure what you mean about the circular buffer? I had hoped to expose the 'event queue' (also a circular buffer) at some point. It'd mean that native functions could then do processing of things like radio signals and could poke data straight into that queue for handing by JS in the main event loop.

    If you're willing to recompile Espruino with your own changes then you can do that kind of thing pretty easily at the moment, but the trick would be exposing it to code that was uploaded by the Web IDE.

  • @Gordon - I reflashed the board with the newest version and it now works like a charm. Thanks for the fast fix! And glad, that I was not totally mistaken about the internal works of Espruino :-).

    @allObjects - Yes, the LPC 810 is an awesome little board. I did not have a reason to play around with the pattern matching engine, sorry.

  • @Gordon, Waveform gives one buffer at a time on full event (single or double buffered). Having a circular buffer with javascript optimized access (doing address modulo calculation inside) - and may be pattern matching on bit level, that cold become helpful in decoding RF input and catch package destination address matches.

    @Stevie, I 'like' - ;) - to be mistaken when it is for the better. It makes actually sense, becuase adding some external xMOS is obviously not different than the output stage of the ST processor. It also tells me that the term 'peripherals' does not include GPIO pins, even though they are involded. Peripherals means the processing entities that can do any serial, adc, pwm(?),... and need the high-speed osci and cpu running.

  • @allObjects yes, I'd considered some kind of pattern matching for RF stuff - but then I thought I'd just be implementing yet another language interpreter - albeit for a very basic language :)

    It's why you can now execute assembler code directly from a setWatch interrupt - so you can actually write whatever decoder you want in assembler. Until I manage to get compiled JS working in interrupts that's still quite painful to do though.

  • ...déformation professionelle starts to hit home: for someone with a hammer everythng looks like a nail... and for you with such a powerful language interpreter mind, every problem looks like a language will solve it... ;-) - no offense... it is a compliment. Luckily, software is in the virtual/modelling world and processor hardware transports the problem into that world. We solve it with what software is made of and the result is transported back. Like simulation, operations research, ....

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview

Question about setDeepSleep

Posted by Avatar for Stevie @Stevie