Another ECM Mode question

In the document describing how to set up ECM mode, there is this little statement:

Note 1: When the modem reboots or the internet connection is lost for any reason, you have to run AT#ECM=1,0 command again to establish the internet connection.

Okay. Now what exactly does that mean? That implies that one should periodically check to see if the internet connection is active and then issue that command.

What would be the best way to test for the presence of an internet connection in this case? In particular what would be the best way to test the state of the internet connection without incurring data charges?

It seems you could use the AT#ECMC? command, which produces output like this when the internet is up:

AT#ECMC?
#ECMC: 0,1,"192.168.225.44","255.255.255.0","192.168.225.1","127.0.0.1","","",""

OK

But that seems to be checking whether the connection is enabled or disabled, not whether the connection has been lost. So how exactly do you figure out that the connection has been lost?

1 Like

Perusal of the sixfab core sources indicates to me that sixfab core solves this by periodically pinging a on the internet. Is this a correct assessment of how sixfab core determines whether the internet connection is up?

1 Like

any new information on this? The point of this hat is so that I have an uninterrupted internet service, so would be weird if it goes down in some arbitrary amount of time

The problem here is not the hat, it is local cell service sometimes drops out the internet connection.

Right now I am solving it approximately like sixfab core does, by periodically (every 15 minutes) checking the connection and bringing it back up when it is down.

A better solution for down the road will be to also run those checks when any of my internet operations (mostly a restful web api through python requests) fail and retry them if the internet is down.

The ideal solution for further down the road will be to batch those internet operations and only bring the network up when I need to do them.

Sample code that checks for internet:

import subprocess
import re

verbose = False

def check_connection(host):
    """ typically called with check_connection('sixfab.com') """
    cmd = ["ping", "-i", "0.4", "-c", "5", host]

    if verbose:
        print(f"[ecm] Checking ECM connection to {host}")

    output = subprocess.run(cmd, capture_output=True, text=True).stdout

    m = re.search(r' ([0-9]+) received, ', output)

    if m is not None:
        if verbose:
            print(f"[ecm] {host} returned {int(m[1])} packets")

        return int(m[1]) != 0

    return False

Cool, thank you for the code!! Are you using something like pynicom or maybe another method to re-establish the connection if it’s lost? Instead of having to manually do it in the terminal…

I’m using cu and pexpect to do basically the same thing. The nice thing about pexpect is it gives you a good framework to parse out the results of AT commands and also to deal with errors in a reasonable way.

Right now it is all contained in a huge wrapper class that abstracts various AT commands and returns their results.

This thread gives some example code I used using this technique.

1 Like

You’re a saint! I put your code together to get the following and it seems to work quite well :grin: Thanks!!

#!/home/pi/venv/bin/python3

import os
import re
import subprocess
import argparse
import time
import pexpect

verbose = False

class ECMError(Exception):
    pass


class ECMDataError(ECMError):
    pass


class ECMBusted(ECMError):
    pass


class ECMTimeout(ECMError):
    pass

def check_connection(host):
    """ typically called with check_connection('sixfab.com') """
    cmd = ["ping", "-i", "0.4", "-c", "5", host]

    if verbose:
        print(f"[ecm] Checking ECM connection to {host}")

    output = subprocess.run(cmd, capture_output=True, text=True).stdout

    m = re.search(r' ([0-9]+) received, ', output)

    if m is not None:
        if verbose:
            print(f"[ecm] {host} returned {int(m[1])} packets")

        return int(m[1]) != 0

    return False

def wait_for_result():
    """wait for OK, ERROR, or timeout return "OK" or raise an exception"""
    i = child.expect([pexpect.TIMEOUT, "OK\r\n", "ERROR\r\n"], timeout=10)

    if i == 0:
        if verbose:
            print(f"[ecm] Timed out!")

        raise ECMTimeout
    elif i == 1:
        if verbose:
            print(f"[ecm] Saw OK")

        return "OK"
    elif i == 2:
        if verbose:
            print(f"[ecm] Saw ERROR")

        raise ECMError

    raise ECMBusted

def send_at_ok():
    """simple test to send an at command and make sure things are cool"""

    child.send("\r")
    time.sleep(0.1)

    for k in range(10):
        if verbose:
            print(f"[ecm] Trying to send 'AT', try #{k+1}")

        child.send("AT\r")

        try:
            _ = wait_for_result()

        except ECMTimeout:
            time.sleep(0.5)

        else:
            return

    raise ECMTimeout

def send_ecm_on():
    """turn the ecm on"""

    child.send(f"AT#ECM=1,0\r")

    _ = wait_for_result()
    return True

def check_device(d):
    return os.path.exists(d)


def main():
    global child, verbose 

    ap = argparse.ArgumentParser()
    ap.add_argument("--verbose", action='store_true', default=False)
    ap.add_argument("--device", type=str, default='/dev/ttyUSB2')
    ap.add_argument("--host", type=str, default='google.com')

    args = ap.parse_args()
    verbose = args.verbose
    host = args.host

    if not check_device(args.device):
        print(f"[ecm.error]  {args.device} does not exist")
        exit(1)

    child = pexpect.spawn(f"cu --nostop --parity none --baud 115200 --line {args.device} dir")

    time.sleep(0.5)

    try:
        send_at_ok()

        if not check_connection(host):
            _ = send_ecm_on()


    except ECMError as e:
        print(f"[ecm] ***ERROR***: {e}:")
        print(child)

    child.sendline("~.")
    time.sleep(0.5)


if __name__ == "__main__":
    main()
1 Like