Power Saving Mode (PSM) in NB-IoT: problems with reconnecting after deepsleep



  • Who can tell me how to implement Power Saving Mode (PSM) during deepsleep?

    I am testing the following script that emulates a measurement cycle of a device that needs to wake up, send data via NB-IoT and go into deepsleep without detaching from the network (to ensure the next cycle is short). I left out the commands for sending the data with the mqtt, and replaced it with a ping command, as that illustrates the problem well: if the deepsleep time is too long, the script cannot connect to a website altough lte.isattached and lte.isconnected are both True.

    from network import LTE
    from uping import ping
    import time, machine
    import config
    
    def send_at_cmd_pretty(cmd):
        response = lte.send_at_cmd(cmd).split('\r\n')
        for line in response:
            print(line)
    
    lte=LTE()
    
    lte.init()
    
    # attach to LTE network of Orange-Belgium
    lte.attach(band=config.BAND, apn=config.APN) 
    while not lte.isattached():
        time.sleep(0.25)
    
    lte.connect()  # start a data session and obtain an IP address
    while not lte.isconnected():
        time.sleep(0.25)
    
    # test if an internet site can be reached
    (packetsSent,packetsReceived)=ping('www.google.com')
    # if site cannot be reached, this will give OSError: [Errno 202] EAI_FAIL
    
    # at this point payload should be sent with mqtt
    
    lte.disconnect() # End the data session with the network
    
    # Swith off LTE modem to reduce power consumption to the minimum, but
    # don't detach from network and don't reset the modem to keep next cycle short
    lte.deinit(detach=False,reset=False) 
    # lte.detach(reset=False) # alternative that will detach modem from network
    
    sleepSeconds=10
    machine.deepsleep(sleepSeconds*1000) # time in ms
    

    This runs fine as long as the deepsleep time is only 30s or less. But when I make the deepsleep time 70s or longer, I cannot connect to the internet in the next cycle (with the ping command). In that case I get give OSError: [Errno 202] EAI_FAIL. That is what also happens with the mqtt commands if I use them.

    That problem can be avoided by doing a modem reset at the start of the script (lte.reset()). But attaching to the network then takes a lot of time. NB-IoT can only work for battery powered applications if the connection with the network is maintained.

    The time needed for a cycle is as follows:
    if no lte.reset() is used: 8.3s (0.0s for lte.init, + 0.3s for lte.attach + 1.3s for lte.connect + 6.6s for lte.disconnect + 0.1s for lte.deinit)
    if lte.reset() is used: 31.6s (6.0s for lte.reset + 0.0s for lte.init, + 17.6s for lte.attach + 1.3s for lte.connect + 6.6s for lte.disconnect + 0.1s for lte.deinit)

    What worries me also is that changing the options of lte.deinit(detach=False,reset=False) has no effect at all. My understanding from the documentation is that the choice 'detach=False' and 'reset=False' ensures that no time is lost with reconnecting to the network and with resetting the modem. And there seems to be no way to specify the TAU value, i.e., the time that the network needs to keep alive the connection with the device when the latter is in deepsleep with the modem switched off.

    By using the AT command +CPSM, I figured out that the TAU was set at 3 minutes (default?), and PSM was disabled. Enabling PSM and setting the TAU at several hours did not solve my problem and seems not to have any effect. That is strange, because I assumed PSM is implemented with the option lte.deinit(detach=False,reset=False). Power consumption during deepsleep drops to 22µA, which means the Sequans modem must be off. So the modem is correctly switched of, but the connection with the network cannot be maintained during deepsleep.

    I am using a gpy 1.0 with firmware 1.20.1.r2 and I updated the Sequans modem with NB1-41019.dup. I have a sim card from Orange-Belgium for NB-IoT and LTE-M, and sending/recieving data works as long as I don't use the deepsleep ...


  • Global Moderator

    @kjm said in Power Saving Mode (PSM) in NB-IoT: problems with reconnecting after deepsleep:

    @peterp said in Power Saving Mode (PSM) in NB-IoT: problems with reconnecting after deepsleep:

    Also not that I also skip the lte.disconnect() - it is quite slow and seemingly not needed, deinit(detach=False,reset=False) works just fine.

    I have a non PSM related problem with a continuously attached/connected GPY with 43818 modem firmware. I get the socket problem OSError: [Errno 118] EHOSTUNREACH if I haven't contacted to the server for more than about half a minute. I find the only way to fix this is to disconnect then reconnect the modem. Wondering why deinit(detach=False,reset=False) does not work in this situation?

    Not sure. Please make a new forum post, otherwise it will get hard to stay on topic with any topic. If you can please include a minimal example that shows your reproduction.



  • @peterp said in Power Saving Mode (PSM) in NB-IoT: problems with reconnecting after deepsleep:

    Also not that I also skip the lte.disconnect() - it is quite slow and seemingly not needed, deinit(detach=False,reset=False) works just fine.

    I have a non PSM related problem with a continuously attached/connected GPY with 43818 modem firmware. I get the socket problem OSError: [Errno 118] EHOSTUNREACH if I haven't contacted to the server for more than about half a minute. I find the only way to fix this is to disconnect then reconnect the modem. Wondering why deinit(detach=False,reset=False) does not work in this situation?



  • @peterp said in Power Saving Mode (PSM) in NB-IoT: problems with reconnecting after deepsleep:

    @kjm From all I read, it should apply equally to NB-IoT and CAT-M1. I've only tested with NB-IoT.

    tnx. Do you have any comment on the difference in 41065 Vs 43818 firmware without PSM on continuously powered GPYs @ https://forum.pycom.io/topic/5802/lte-firmware-41065-vs-43818


  • Global Moderator

    @kjm said in Power Saving Mode (PSM) in NB-IoT: problems with reconnecting after deepsleep:

    I've been struggling with gpy lte issues surrounding attachment/connection for ages! I'm using CAT-M1 with 41065 or 43818 modem firmware.

    1. Peter, are the tau/act timers NB-IoT specific or are they relevant to CAT-M1 too?

    @kjm From all I read, it should apply equally to NB-IoT and CAT-M1. I've only tested with NB-IoT.

    1. Jan, I can't find the AT+CPSM cmd in the monarch LR5.1.1.0 manual? Is there a link that explains what this cmd does? It returns an error with my modem

    It's AT+CPSMS. It's in section 2.12 when you follow the link from here https://docs.pycom.io/datasheets/development/fipy/#at-commands



  • I've been struggling with gpy lte issues surrounding attachment/connection for ages! I'm using CAT-M1 with 41065 or 43818 modem firmware.

    1. Peter, are the tau/act timers NB-IoT specific or are they relevant to CAT-M1 too?
    2. Jan, I can't find the AT+CPSM cmd in the monarch LR5.1.1.0 manual? Is there a link that explains what this cmd does? It returns an error with my modem

  • Global Moderator

    @jand For PSM, you need to to issue an AT command, to specify two timers to the LTE network:

    • at what intervals will the device connect to the network
    • how long will the LTE modem be active

    See below the code I used.

    I measure 130 uA during deepsleep with a FiPy in Expansion board 3.1 and after wakeup it is still attached, so no time for re-attaching.

    Can you try something similar and report back?

    PS: Note that this is a negotiation. You propose timers, but the network decides. However, I've played a bit with it now and in all my tests, I did always get the values I proposed (Vodafone NL), even nonsensical ones, your mileage may vary.

    Also not that I also skip the lte.disconnect() - it is quite slow and seemingly not needed, deinit(detach=False,reset=False) works just fine.

    from network import LTE
    import time
    import socket
    import machine
    import pycom
    
    def at(cmd):
        response = lte.send_at_cmd(cmd).split('\r\n')
        for line in response:
            if ( len(line) == 0 ):
                continue
            else:
                print(line)
    
    def atv(cmd):
        response = lte.send_at_cmd(cmd).split('\r\n')
        for line in response:
            if ( len(line) == 0 ):
                continue
            elif line == "OK":
                continue
            elif line == "ERROR":
                continue
            else:
                print(line)
                return line
    
    def http_get(url):
        print("get(", url, ")")
        _, _, host, path = url.split('/', 3)
        print("host", host)
        print("path", path)
        addr = socket.getaddrinfo(host, 80)[0][-1]
        print("addr", addr)
        s = socket.socket()
        print("connect")
        s.connect(addr)
        print("send")
        s.send(bytes('GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n' % (path, host), 'utf8'))
        # s.settimeout(5)
        # while True:
        #     data = s.recv(100)
        #     if data:
        #         print(str(data, 'utf8'), end='')
        #     else:
        #         break
        print("close")
        s.close()
        print("done")
    
    def send():
        url = 'http://detectportal.firefox.com/'
        http_get(url)
    
    def encode_psm_tau(value, unit):
        u = psm_tau_units[unit]
        if value > 31:
            raise Exception("31")
        return '"{:03b}{:05b}"'.format(u, value)
    
    def encode_psm_act(value, unit):
        u = psm_act_units[unit]
        if value > 31:
            raise Exception("31")
        return '"{:03b}{:05b}"'.format(u, value)
    
    def decode_psm_tau(tau):
        if len(tau) != 8:
            raise Exception("8")
        us = tau[0:3]
        vs = tau[3:8]
        ui = int(us,2)
        vi = int(vs,2)
        u = psm_tau_units_rev[ui]
        return (vi, u)
    
    def decode_psm_act(act):
        if len(act) != 8:
            raise Exception("8")
        us = act[0:3]
        vs = act[3:8]
        ui = int(us,2)
        vi = int(vs,2)
        u = psm_act_units_rev[ui]
        return (vi, u)
    
    
    def psm_set(tau, act):
        (tauv, tauu) = decode_psm_tau(tau.strip('"'))
        (actv, actu) = decode_psm_act(act.strip('"'))
        print("psm_set: ( 1 ,", tau, ",", act, ") .. tau=", tauv, tauu, "act=", actv, actu)
        cmd='AT+CPSMS=1,,,' + tau + "," + act
        at(cmd)
    
    def psm_get():
        retv = atv('AT+CPSMS?').split(" ")[1].split(",")
        en = retv[0]
        tau = retv[3]
        act = retv[4]
        (tauv, tauu) = decode_psm_tau(tau.strip('"'))
        (actv, actu) = decode_psm_act(act.strip('"'))
        print("psm_get: (", en, ",", tau, ",", act, ") .. tau=", tauv, tauu, "act=", actv, actu)
        return (tau, act)
    
    
    def doit():
        if do_psm:
            (tau_actual, act_actual) =  psm_get()
            tau = encode_psm_tau(psm_tau[0], psm_tau[1])
            act = encode_psm_act(psm_act[0], psm_act[1])
            # print("PSM tau=", psm_tau, tau)
            # print("PSM act=", psm_act, act)
            psm_set(tau, act)
    
    
        if lte.isattached():
            print("already attached")
        else:
            print("attach")
            start = time.time()
            lte.attach(band=20, apn="spe.inetd.vodafone.nbiot")
            print("attached after", time.time() - start, "seconds")
    
            if do_psm:
                (tau_actual, act_actual) =  psm_get()
                if tau_actual != tau or act_actual != act:
                    print("Network changed PSM from(", tau, ",", act, ") to(", tau_actual, ",", act_actual, ")")
            while not lte.isattached():
                time.sleep(0.5)
    
        if do_psm:
            (tau_actual, act_actual) =  psm_get()
            if tau_actual != tau or act_actual != act:
                print("Network changed PSM from(", tau, ",", act, ") to(", tau_actual, ",", act_actual, ")")
    
        print("connect")
        lte.connect()
        while not lte.isconnected():
            time.sleep(0.5)
        send()
    
    psm_tau_units = {
     "2s": 0b011,
    "30s": 0b100,
     "1m": 0b101,
    "10m": 0b000,
     "1h": 0b001,
    "10h": 0b010,
    "disabled": 0b111,
    }
    # reverse lookup
    psm_tau_units_rev = {psm_tau_units[x] : x for x in psm_tau_units}
    
    
    psm_act_units = {
     "2s": 0b000,
     "1m": 0b001,
     "6m": 0b010,
    "disabled": 0b111,
    }
    psm_act_units_rev = {psm_act_units[x] : x for x in psm_act_units}
    
    ############################################
    do_psm = True
    # psm_tau            = (2, "1h")
    # psm_act            = (2, "1m")
    # deepsleep_duration = 3600000 # 1h
    psm_tau            = ( 1, "1h")
    psm_act            = ( 3, "2s")
    deepsleep_duration = 60000 # 1m
    
    print("init")
    lte = LTE()
    doit()
    if do_psm:
        lte.deinit(detach=False, reset=False)
    else:
        lte.deinit()
    
    print("deepsleep")
    machine.deepsleep(deepsleep_duration)


  • A correction to what I wrote previously: I found that lte.deinit(detach=False, reset=False) followed by a deepsleep does not do what it is supposed to do: power consumption during deepsleep still varies between 27-90mA instead of the typical deepsleep power consumption of 22µA. This is not is line with the statement "lte.deinit(dettach=False) allows the modem to go into power saving mode (PSM, eDRX) without dettaching from the network." as posted here: New LTE Firmware release v1.18.1.r3 & CAT-M1 firmware. So the only thing that works for me is ending with lte.deinit(). If that is done, no reset is needed at the start of the next cycle, and re-connection is established reliably. But it takes time (with the lte.attach(... step taking typically 10s, and the total cycle (lte.init, lte.attach, lte.connect, lte.disconnect, lte.deinit, deepsleep) taking about 20-24s.

    Am I correct that this means that it is not yet possible to switch off the modem completely during deepsleep without detaching from the network (PSM)? Or who managed to do this? How?


Log in to reply
 

Pycom on Twitter