Method To Take Control of Cell Modem



  • Is there a way to directly take control of the cell modem and talk to it directly with a raw serial interrupt driver (machine.UART)? In other words I do not want to use the LTE module other than possibly initializing the cell modem. I want to write all the modem interaction including the LTE.send_at_cmd() in 100% MicroPython using AT commands. I'll handle all the modem responses, timeouts, error checking, etc. No calls to the LTE module desired.

    Why would I want to do such a thing (call me crazy)?

    I want to try using the built-in Sequans "Specific HTTP Commands".

    Maybe this would improve the integrity of the GPy.

    I currently have a program working in production using Pycom MicroPython V1.20.0.rc13 on many units. When I try to run the same program on a newly purchased GPy running Pycom MicroPython 1.20.2.r3, the program crashes (20 sec watchdog timeout) when I do an AT command ('AT+CEREG?') after disconnecting from a successful transmission. How do I troubleshoot such a crash?

    Modem commands have always interfered with multi-threading and interrupt servicing. Maybe using just the MicroPython serial interrupt drivers would allow more smooth multi-threading and interrupt servicing. I could also have complete control over modem interaction, timeouts, retries, etc. without affecting the other tasks running on the system. Sending commands to a cell modem should not interfere with the servicing of an interrupt or the switching of threads in MicroPython.

    Looking at the C code for the LTE module shows a significant amount of complexity. Let's open up the cell communication software in Python so everyone can see and make improvements and changes. Let's create high level commands in Python that make cell communication reliable and transparent to the rest of the system.

    I've built embedded cell modem based systems using just AT commands that worked great. It shouldn't be that complicated.

    Another possibility is to run the ESP32 MicroPython on the GPy and bypass Pycom's implementation. Has anyone tried this before?



  • Ok, I made a little more progress today.

    One of the problematic aspects of coding modem communication routines is knowing what to do when errors occur and how to reliably handle unsolicited modem responses. Currently I've decided against throwing exceptions in the functions. The calling program will need to decide what to do if errors occur.

    I've upgraded the at() function to look for unsolicited responses before transmitting the requested command. If anything is in the RX buffer, it is assumed to be an unsolicited response. There is always a remote chance that an unsolicited response will arrive during the transmission of the command. This will mess up the expected response received.

    I will later try to identify specific unsolicited responses the modem may emit. Currently I've noticed it emits a "+CEREG" on occassion. This can be turned off if we decide to later.

    from machine import UART
    from utime import sleep_ms, ticks_ms, ticks_diff
    
    uart = None
    
    #####################################################################
    #               Initialize Uart Connected To LTE Modem
    #####################################################################
    def init_uart():
        global uart
    
        # FiPy: pins=('P20', 'P18', 'P19', 'P17')
        # GPy:  pins=('P5', 'P98', 'P7', 'P99')
        uart = UART(1, baudrate=921600, pins=('P5', 'P98', 'P7', 'P99'), timeout_chars=2)
    
    
    #####################################################################
    #                     Send AT Command To Modem
    #####################################################################
    def at(cmd):
        try:
            if uart.any() > 0:
                # get unsolicited modem response 
                a = get_modem_response()
    
                if len(a) > 0:
                    print("unsolicited modem response: {}".format(a))
                
                # process unsolicited results here
    
            print("modem command: {}".format(cmd))
    
            # send command to modem
            n = uart.write(cmd + '\r\n')
            if n != len(cmd) + 2:
                print("error sending command to modem: n = {}".format(n))
                return []
    
            a = get_modem_response()
    
        except Exception as ex:
            sys.print_exception(ex)
            a = []
    
        return a    # return modem response
    
    
    #####################################################################
    #                     Get Modem Response
    #####################################################################
    def get_modem_response():
        try:
            t0 = ticks_ms()
            
            while True:
                if uart.any() > 0:
                    # insure entire response has been received
                    sleep_ms(1)
                    break
    
                t1 = ticks_ms()
                tx = ticks_diff(t1, t0)
    
                if tx > 10000:
                    print("timeout error - no modem response after 10 secs")
                    return []
    
                sleep_ms(100)
    
            r = uart.read()
            s = str(r, 'utf-8')
            a = s.split('\r\n')
            a = list(filter(None, a))
    
            print("modem response: {}".format(a))
    
        except Exception as ex:
            sys.print_exception(ex)
            a = []
    
        return a    # return modem response
    
    
    #####################################################################
    #                       Test Modem Module
    #####################################################################
    def test():
        init_uart()
        r = at("ATZ")
        if (r[0] != 'OK'):
            print("error")
    
        r = at("AT+CEREG?")
        if (r[1] != 'OK'):
            print("error")
    
        s = iccid()
        print("ICCID: " + s)
    
        s = imei()
        print("IMEI: " + s)
    
        n = signal_strength()
        print("Signal Strength: " + str(n))
    
        print("End of test")
    
    #####################################################################
    #                     Get SIM Card ICCID Number
    #####################################################################
    def iccid():
        try:
            a = at("AT+SQNCCID?")
            
            # example response: ['+SQNCCID: "89014103271203065543",""', 'OK']
            if a[1] != 'OK':
                return ''
    
            s = a[0]
            if s[0:10] != '+SQNCCID: ':
                return ''
    
            a = s.split(",")
            n = len(a[0])
            r = a[0][11:n-1]
    
        except Exception as ex:
            sys.print_exception(ex)
            r = ''
    
        return r
    
    
    
    #####################################################################
    #                       Get Modem IMEI Number
    #####################################################################
    def imei():
        try:
            a = at("AT+CGSN=1")
            
            # example response: ['+CGSN: "354347094028575"', 'OK']
            if a[1] != 'OK':
                return ''
    
            s = a[0]
            if s[0:7] != '+CGSN: ':
                return ''
    
            n = len(s)
            r = s[8:n-1]
    
        except Exception as ex:
            sys.print_exception(ex)
            r = ''
    
        return r
    
    
    #####################################################################
    #                       Get Signal Strength
    #####################################################################
    def signal_strength():
        # 0-31, 99-unknown
        try:
            while True:
                a = at('AT+CSQ')
                # example response: ['+CSQ: 18,99', 'OK'] where 18 is signal strength
                if a[1] != 'OK':
                    break
    
                s = a[0]
                if (s[0:6] != "+CSQ: "):
                    break
                
                n = len(s)
                s = s[6:n-1]
                a = s.split(",")
                cell_signal = int(a[0])
    
                return cell_signal
    
        except Exception as ex:
            sys.print_exception(ex)
    
        # unknown signal strength
        return 99
    


  • Ok, here's my first bit of code to send AT commands. Hopefully I'll get more done tomorrow. Comments and suggestions are welcome. I'm thinking about making the at() function check the last result line for "OK" and throwing an exception if it is different.

    from machine import UART
    from utime import sleep_ms, ticks_ms, ticks_diff
    
    uart = None
    
    #####################################################################
    #               Initialize Uart Connected To LTE Modem
    #####################################################################
    def init_uart():
        global uart
    
        # FiPy: pins=('P20', 'P18', 'P19', 'P17')
        # GPy:  pins=('P5', 'P98', 'P7', 'P99')
        uart = UART(1, baudrate=921600, pins=('P5', 'P98', 'P7', 'P99'), timeout_chars=2)
    
    
    #####################################################################
    #                     Send AT Command To Modem
    #####################################################################
    def at(cmd):
        try:
            print("modem command: {}".format(cmd))
    
            n = uart.write(cmd + '\r\n')
            if n != len(cmd) + 2:
                print("error sending command to modem: n = {}".format(n))
                return []
    
            t0 = ticks_ms()
            
            while True:
                if uart.any() > 0:
                    sleep_ms(1) # insure entire response has been received from modem
                    break
    
                t1 = ticks_ms()
                tx = ticks_diff(t1, t0)
    
                if tx > 10000:
                    print("timeout error waiting for response from modem")
                    return []
    
                sleep_ms(100)
    
            r = uart.read()
            s = str(r, 'utf-8')
            a = s.split('\r\n')
            a = list(filter(None, a))
    
            print("response: {}".format(a))
    
        except Exception as ex:
            sys.print_exception(ex)
            a = []
    
        return a
    
    #####################################################################
    #                       Test Modem Module
    #####################################################################
    def test():
        init_uart()
        r = at("ATZ")
        if (r[0] != 'OK'):
            print("error")
    
        r = at("AT+CEREG?")
        if (r[1] != 'OK'):
            print("error")
     
    
    


  • Thanks for the help; it works! Now let me see if I can build a complete communication module that can do HTTP gets and puts with nothing but AT commands.

    from machine import UART
    # Open serial connection to LTE modem
    # FiPy: pins=('P20', 'P18', 'P19', 'P17')
    # GPy:  pins=('P5', 'P98', 'P7', 'P99')
    uart = UART(1, baudrate=921600, pins=('P5', 'P98', 'P7', 'P99'), timeout_chars=1000)
    uart.write("AT" + "\r\n")
    r = uart.read()
    print(r)    # b'\r\nOK\r\n'
    uart.deinit()```


  • I communicate with the LTE modem through UART, it seems faster and more reliable than the LTE() class... I don't know if the pins on a GPy are different from the ones on my FiPy...

    from machine import UART
    # Open serial connection to LTE modem
    serial = UART(1, baudrate=921600, pins=('P20', 'P18', 'P19', 'P17'), timeout_chars=1)
    # For example write AT to modem
    serial.write("AT" + '\r\n')
    # Read back the response from the modem
    serial.read()
     # Deinit uart to lte modem
    serial.deinit()
    


  • @tlanier I believe this has been discussed around here before, though I only found this post which indirectly adresses the topic (as it does indeed communicate with the modem directly via the UART class).


Log in to reply
 

Pycom on Twitter