How to Deep sleep with SiPy + Pysense + 3.7V LiPo



  • Hi from Colombia,

    By these days I was in a battle with the deep sleep mode of the Pysense board. Finally I made It works and I want to share my discoveries with you.

    My problem was that in Deep sleep mode my SiPy + Pysense setup current consumption was around 2.5 mA (by using the Pysense library as It's adviced in the forums), which is not quite low power. I also was not powering the boards by USB but using a 3.7V LiPo connected to JST connector on Pysense. And I was using the last stable versions of SiPy and Pysense firmware (1.18.0 and 0.0.8 respectively). The following was my code in main.py:

    from pysense import Pysense
    import pycom
    import time
    
    py = Pysense()
    
    #Turn-off heartbeat
    if pycom.heartbeat() == True:
        pycom.heartbeat(False)
    
    #Blink the LED
    for i in range(5):
        pycom.rgbled(0x1F)
        time.sleep(0.05)
        pycom.rgbled(0x00)
        time.sleep(1)
    
    #Sleep 20 seconds
    py.setup_sleep(20)
    py.go_to_sleep()
    

    And in order to reduce power consumption in active mode (down from ~120 mA to ~40 mA), I placed the following code in boot.py:

    from network import WLAN
    
    wlan = WLAN()
    wlan.deinit()
    

    As you can see, everything looks right because I did not want to use neither the accelerometer and INT pin interruptions, I just wanted to blink an LED a few times and then put the module N seconds in Deep sleep mode (nothing out of this world).

    So testing and failing until success I found the following:

    • It's mandatory to import the LIS2HH12 library and create an object in order to turn-on the accelerometer
    • It's mandatory to enable at least one accelerometer's activity/inactivity interruption by using the setup_int_wake_up() method from Pysense library. In my case I enabled only the activity interruption:
    py.setup_int_wake_up(True, False)
    

    When I did that the Deep sleep current was reduced to ~380 µA, so much better but still too high.

    • I realized that if I removed boot.py from my project, the Deep sleep current lowers to 124 µA (not bad). The drawback: Active current rises to ~120 mA again.

    As the accelerometer was in active mode (although I did not want to use It) its current consumption should be around 110 µA according to datasheet. So I went deeper into the datasheet and the LIS2HH12 class and found a way to put the accelerometer in Power-down mode: Write a 0 to the ODR bits of the CTRL1 register. Fortunately the LIS2HH12 class has a method named set_odr() to do that. So:

    • Use set_odr(0) to put the accelerometer in Power-down mode. That lowered the Deep sleep current of my SiPy + Pysense to just 19.5 µA

    Finally, to get a low active current again, disabling WiFi at the beginning of the program made my boards drain ~120 mA during the first two seconds after power on and then It was reduced to ~40 mA (as It's supposed), the surprise was that (for some reason I can not explain) enabling WiFi again just before entering Deep sleep mode made me win 1 µA more in Deep sleep. So at the end of my experiments I got 18,3 µA!!! in Deep sleep mode with a SiPy + Pysense + 3.7V LiPo setup. Yay!!!

    Here is my main.py code (I let the comments in spanish). I hope you guys find It useful. Espero les sea de utilidad ;)

    from pysense import Pysense         #Tarjeta Pysense
    from LIS2HH12 import LIS2HH12       #Acelerómetro
    from network import WLAN            #WiFi
    import pycom                        #Usado para manejo del led RGB
    import time                         #Usado para retardos de tiempo
    
    py = Pysense()                      #Objeto para manejo de librería pysense
    acc = LIS2HH12()                    #Objeto para manejo del acelerómetro
    wlan = WLAN(mode=WLAN.STA)          #Objeto para manejo de WiFi
    
    #Desactivar wlan para reducir consumo en modo activo
    wlan.deinit()
    
    #Apagar el modo heartbeat si se encuentra activo
    if pycom.heartbeat() == True:
        pycom.heartbeat(False)
    
    print("Parpadear led tres veces...")
    for i in range(3):
        print(str(i + 1))
        pycom.rgbled(0x1F)
        time.sleep(0.05)
        pycom.rgbled(0x00)
        time.sleep(1)
    
    #Activar interrupción por acelerómetro (Actividad, Inactividad)
    py.setup_int_wake_up(True, False)
    
    #Poner acelerómetro en modo Power-down
    #  Escribir 0 en los bits ODR del registro CTRL1
    acc.set_odr(0)
    
    #Activar WLAN antes de pasar a Deep sleep
    #  Por algún motivo si dejo el WiFi desactivado el consumo sube un poco 
    #  al entrar en modo Deep sleep
    wlan.init(mode=WLAN.STA)
    
    print("A dormir por 30 segundos...")
    #Configurar tiempo y entrar en modo Deep Sleep
    py.setup_sleep(30)
    py.go_to_sleep()
    


  • @dmayorquin Interesting observation. It would be so much easier to understand these things if Pycom would make the schematic available. Thanks for posting!



  • Hello everyone, I've discovered something new. The function go_to_sleep() has one parameter which can be True or False (default is True). This function is located in pycoproc.py and these are its first lines of code:

    def go_to_sleep(self, gps=True):
            # enable or disable back-up power to the GPS receiver
            if gps:
                self.set_bits_in_memory(PORTC_ADDR, 1 << 7)
            else:
                self.mask_bits_in_memory(PORTC_ADDR, ~(1 << 7))
    

    Pysense board doesn't have GPS, so I think in this case the RC7 pin of the PIC is doing something with the power supply for sensors. Moreover, if I follow the traces in the PCB, I can see that RC7 signal is related with the pin #3 (3V3_Sensors) of the External IO Header.

    As you can see in the code, the function puts a '1' in RC7 if the parameter is True and puts a '0' if False, so I called py.go_to_sleep(False) and the Deep sleep current lowered to less than 10 µA!!!. What's more:

    • Not necessary to import LIS2HH12 nor to create an object
    • Not necessary to call py.setup_int_wake_up(True, False)
    • Not necessary to power-down the accelerometer with acc.set_odr(0)

    The new (shorter) code is:

    from pysense import Pysense         #Tarjeta Pysense
    import pycom                        #Usado para manejo del led RGB
    import time                         #Usado para retardos de tiempo
    
    py = Pysense()                      #Objeto para manejo de librería pysense
    
    #Apagar el modo heartbeat si se encuentra activo
    if pycom.heartbeat() == True:
        pycom.heartbeat(False)
    
    #Desactivar el WiFi al iniciar, si está activo
    if pycom.wifi_on_boot() == True:
        pycom.wifi_on_boot(False)
    
    print("Parpadear led tres veces...")
    for i in range(3):
        print(str(i + 1))
        pycom.rgbled(0x1F)
        time.sleep(0.05)
        pycom.rgbled(0x00)
        time.sleep(1)
    
    print("A dormir por 30 segundos...")
    #Configurar tiempo y entrar en modo Deep Sleep
    py.setup_sleep(30)
    py.go_to_sleep(False)
    


  • Thanks @jcaron!
    Instead the init/deinit WLAN calls, I'm using pycom.wifi_on_boot(False) as you suggested. Deep sleep current lowered to 17,8 µA and I also got a better mean active current, because (according to docs) WiFi on boot flag is stored in non-volatile memory. So, after using pycom.wifi_on_boot(False) WiFi will turn-off on the next boot before executing main.py.

    Updated code is:

    from pysense import Pysense         #Tarjeta Pysense
    from LIS2HH12 import LIS2HH12       #Acelerómetro
    import pycom                        #Usado para manejo del led RGB
    import time                         #Usado para retardos de tiempo
    
    py = Pysense()                      #Objeto para manejo de librería pysense
    acc = LIS2HH12()                    #Objeto para manejo del acelerómetro
    
    #Apagar el modo heartbeat si se encuentra activo
    if pycom.heartbeat() == True:
        pycom.heartbeat(False)
    
    #Desactivar el WiFi al iniciar, si está activo
    if pycom.wifi_on_boot() == True:
        pycom.wifi_on_boot(False)
    
    print("Parpadear led tres veces...")
    for i in range(3):
        print(str(i + 1))
        pycom.rgbled(0x1F)
        time.sleep(0.05)
        pycom.rgbled(0x00)
        time.sleep(1)
    
    #Activar interrupción por acelerómetro (Actividad, Inactividad)
    py.setup_int_wake_up(True, False)
    
    #Poner acelerómetro en modo Power-down
    #  Escribir 0 en los bits ODR del registro CTRL1
    acc.set_odr(0)
    
    print("A dormir por 30 segundos...")
    #Configurar tiempo y entrar en modo Deep Sleep
    py.setup_sleep(30)
    py.go_to_sleep()
    


  • @dmayorquin that seems quite weird, and I’m pretty sure you can get even better, as in your case the sensors are powered by the PIC when you actually don’t need them.

    For WiFi checkout pycom.wifi_on_boot. This should only affect active mode and not deep sleep though.


 

Pycom on Twitter