class sqnsupgrade:

    global sysname

    def __init__(self):

        self.__sysname = sysname
        self.__pins = None
        self.__connected = False
        self.__sdpath = None
        self.__resp_921600 = False
        self.__serial = None
        self.__kill_ppp_ok = False

        if 'GPy' in self.__sysname:
            self.__pins = ('P5', 'P98', 'P7', 'P99')
        else:
            self.__pins = ('P20', 'P18', 'P19', 'P17')

    def special_print(self, msg, flush=None, end='\n'):
        if 'FiPy' in self.__sysname or 'GPy' in self.__sysname:
            print(msg, end=end)
        else:
            print(msg, flush=flush, end=end)

    def read_rsp(self, size=None, timeout=-1):
        if timeout < 0:
            timeout = 20000
        elif timeout is None:
            timeout = 0
        if 'FiPy' in self.__sysname or 'GPy' in self.__sysname:
            while not self.__serial.any() and timeout > 0:
                time.sleep_ms(1)
                timeout -= 1
        else:
            while self.__serial.in_waiting <= 0 and timeout > 0:
                time.sleep(0.001)
                timeout -= 1

        if size is not None:
            rsp = self.__serial.read(size)
        else:
            rsp = self.__serial.read()
        if rsp is not None:
            return rsp
        else:
            return b''

    def print_pretty_response(self, rsp, flush=False):
        lines = rsp.decode('ascii').split('\r\n')
        for line in lines:
            if 'OK' not in line and line != '':
                self.special_print(line, flush=flush)

    def return_pretty_response(self, rsp):
        ret_str = ''
        lines = rsp.decode('ascii').split('\r\n')
        for line in lines:
            if 'OK' not in line:
                ret_str += line
        return ret_str

    def return_upgrade_response(self, rsp):
        pretty = self.return_pretty_response(rsp)
        if "+SMUPGRADE:" in pretty:
            try:
                return pretty.split(':')[1].strip()
            except:
                pass
        return None

    def return_code(self, rsp, debug=False):
        ret_str = b''
        lines = rsp.decode('ascii').split('\r\n')
        for line in lines:
            if 'OK' not in line and len(line) > 0:
                try:
                    if debug:
                        print('Converting response: {} to int...'.format(line))
                    return int(line)
                except:
                    pass
        raise OSError('Could not decode modem state')

    def wait_for_modem(self, send=True, expected=b'OK', echo_char=None):
        rsp = b''
        start = time.time()
        while True:
            if send:
                self.__serial.write(b"AT\r\n")
            r = self.read_rsp(size=(len(expected) + 4), timeout=50)
            if r:
                rsp += r
            if expected in rsp:
                if echo_char is not None:
                    print()
                break
            else:
                if echo_char is not None:
                    self.special_print(echo_char, end='', flush=True)
                time.sleep(0.5)
            if time.time() - start >= 300:
                raise OSError('Timeout waiting for modem to respond!')

    def __check_file(self, file_path, debug=False):
        if 'FiPy' in self.__sysname or 'GPy' in self.__sysname:
            if file_path[
                    0] == '/' and not 'flash' in file_path and not file_path.split(
                        '/')[1] in os.listdir('/'):
                if self.__sdpath is None:
                    self.__sdpath = file_path.split('/')[1]
                    try:
                        sd = SD()
                        time.sleep(0.5)
                        os.mount(sd, '/{}'.format(self.__sdpath))
                    except Exception as ex:
                        print('Unable to mount SD card!')
                        return False
                else:
                    print('SD card already mounted on {}!'.format(
                        self.__sdpath))
                    return False
        try:
            size = os.stat(file_path)[6]
            if debug: print('File {} has size {}'.format(file_path, size))
            return True
        except Exception as ex:
            print('Exception when checking file {}... wrong file name?'.format(
                file_path))
            print('{}'.format(ex))
            return False
        return False

    def check_files(self, ffile, mfile=None, debug=False):
        if mfile is not None:
            if self.__check_file(mfile, debug):
                return self.__check_file(ffile, debug)
            else:
                return False
        else:
            return self.__check_file(ffile, debug)

    def __check_resp(self, resp, kill_ppp=False):
        if resp is not None:
            self.__resp_921600 = b'OK' in resp or b'ERROR' in resp
            self.__kill_ppp_ok = self.__kill_ppp_ok or (kill_ppp
                                                        and b'OK' in resp)

    def __hangup_modem(self, delay, debug):
        self.__serial.read()
        if not self.__kill_ppp_ok:
            self.__serial.write(b"+++")
            time.sleep_ms(1150)
            resp = self.__serial.read()
            if debug: print('Response (+++ #1): {}'.format(resp))
            self.__check_resp(resp, True)
        self.__serial.write(b"AT\r\n")
        time.sleep_ms(250)
        resp = self.__serial.read()
        if debug: print('Response (AT #1) {}'.format(resp))
        self.__check_resp(resp)
        if resp is not None:
            if b'OK' not in resp and not self.__kill_ppp_ok:
                self.__serial.write(b"AT\r\n")
                time.sleep_ms(250)
                resp = self.__serial.read()
                if debug: print('Response (AT #2) {}'.format(resp))
                self.__check_resp(resp)
                if resp is not None and b'OK' in resp:
                    return True
                self.__serial.write(b"+++")
                time.sleep_ms(1150)
                resp = self.__serial.read()
                if debug: print('Response (+++ #2): {}'.format(resp))
                self.__check_resp(resp, True)
            if resp is not None and b'OK' in resp:
                self.__serial.write(b"AT\r\n")
                time.sleep_ms(250)
                resp = self.__serial.read()
                if debug: print('Response (AT #2) {}'.format(resp))
                self.__check_resp(resp)
                if resp is not None and b'OK' in resp:
                    return True
        return False

    def detect_modem_state(self, retry=3, initial_delay=1000, debug=False):
        count = 0
        self.__serial = UART(1,
                             baudrate=921600,
                             pins=self.__pins,
                             timeout_chars=1)
        self.__serial.read()
        while count < retry:
            count += 1
            delay = initial_delay * count
            if debug: print("The current delay is {}".format(delay))
            self.__serial = UART(1,
                                 baudrate=921600,
                                 pins=self.__pins,
                                 timeout_chars=10)
            #if True:
            if self.__hangup_modem(initial_delay, debug):
                self.__serial.write(b"AT+SMOD?\r\n")
                time.sleep_ms(delay)
                resp = self.__serial.read()
                if debug: print('Response (AT+SMOD?) {}'.format(resp))
                try:
                    return self.return_code(resp, debug)
                except:
                    pass
            else:
                self.__serial = UART(1,
                                     baudrate=921600,
                                     pins=self.__pins,
                                     timeout_chars=1)
                self.__serial.read()
                self.__serial.write(b"AT\r\n")
                time.sleep_ms(delay)
                resp = self.__serial.read()
                self.__check_resp(resp)
                if debug: print('Response (AT #3) {}'.format(resp))
                if resp is not None and b'OK' in resp:
                    self.__serial.write(b"AT+SMOD?\r\n")
                    time.sleep_ms(delay)
                    resp = self.__serial.read()
                    try:
                        if debug: print('Response (AT+SMOD?) {}'.format(resp))
                        return self.return_code(resp, debug)
                    except:
                        pass
                self.__serial.write(b"AT\r\n")
                time.sleep_ms(delay)
                resp = self.__serial.read()
                self.__check_resp(resp)
                if debug: print('Response (AT #4) {}'.format(resp))
                if resp is not None and b'OK' in resp:
                    self.__serial.write(b"AT+SMOD?\r\n")
                    time.sleep_ms(delay)
                    resp = self.__serial.read()
                    try:
                        return self.return_code(resp, debug)
                        if debug: print('Response (AT+SMOD?) {}'.format(resp))
                    except:
                        pass
                else:
                    if not self.__resp_921600:
                        self.__serial = UART(1,
                                             baudrate=115200,
                                             pins=self.__pins,
                                             timeout_chars=10)
                        self.__serial.write(b"AT\r\n")
                        time.sleep_ms(delay)
                        resp = self.__serial.read()
                        if debug:
                            print('Response (AT #1 @ 115200) {}'.format(resp))
                        if resp is not None and b'OK' in resp:
                            self.__serial.write(b"AT+SMOD?\r\n")
                            time.sleep_ms(delay)
                            resp = self.__serial.read()
                            try:
                                if debug:
                                    print(
                                        'Response (AT+SMOD?) {}'.format(resp))
                                return self.return_code(resp, debug)
                            except:
                                pass
                        self.__serial.write(b"AT\r\n")
                        time.sleep_ms(delay)
                        resp = self.__serial.read()
                        if debug:
                            print('Response (AT #2 @ 115200) {}'.format(resp))
                        if resp is not None and b'OK' in resp:
                            self.__serial.write(b"AT+SMOD?\r\n")
                            time.sleep_ms(delay)
                            resp = self.__serial.read()
                            try:
                                if debug:
                                    print(
                                        'Response (AT+SMOD?) {}'.format(resp))
                                return self.return_code(resp, debug)
                            except:
                                pass
        return None

    def __get_power_warning(self):
        return "<<<=== DO NOT DISCONNECT POWER ===>>>"

    def __get_wait_msg(self, load_fff=True):
        if not self.__wait_msg:
            self.__wait_msg = True
            if load_fff:
                return "Waiting for modem to finish the update...\nThis might take several minutes!\n" + self.__get_power_warning(
                )
            else:
                return "Waiting for modem to finish the update...\n" + self.__get_power_warning(
                )
        return None

    def __run(self,
              file_path=None,
              baudrate=921600,
              port=None,
              resume=False,
              load_ffh=False,
              mirror=False,
              switch_ffh=False,
              bootrom=False,
              rgbled=0x050505,
              debug=False,
              pkgdebug=False,
              atneg=True,
              max_try=10,
              direct=True,
              atneg_only=False,
              version_only=False,
              expected_smod=None,
              verbose=False,
              load_fff=False):
        self.__wait_msg = False
        mirror = True if atneg_only else mirror
        recover = True if atneg_only else load_ffh
        resume = True if mirror or recover or atneg_only or version_only else resume
        verbose = True if debug else verbose
        load_fff = False if bootrom and switch_ffh else load_fff
        if debug:
            print(
                'mirror? {}  recover? {}  resume? {}  direct? {}  atneg_only? {} bootrom? {} load_fff? {}'
                .format(mirror, recover, resume, direct, atneg_only, bootrom,
                        load_fff))
        abort = True
        external = False
        self.__serial = None

        if 'FiPy' in self.__sysname or 'GPy' in self.__sysname:

            self.__serial = UART(1,
                                 baudrate=115200 if recover else baudrate,
                                 pins=self.__pins,
                                 timeout_chars=100)
            self.__serial.read()
        else:
            if port is None:
                raise ValueError('serial port not specified')
            if debug: print('Setting port {}'.format(port))
            external = True
            br = 115200 if recover and not direct else baudrate
            if debug: print('Setting baudrate to {}'.format(br))
            self.__serial = serial.Serial(port,
                                          br,
                                          bytesize=serial.EIGHTBITS,
                                          timeout=1 if version_only else 0.1)
            self.__serial.reset_input_buffer()
            self.__serial.reset_output_buffer()

        if version_only:
            self.__serial.read()
            self.__serial.write(b'AT\r\n')
            self.__serial.write(b'AT\r\n')
            self.__serial.read()
            if verbose:
                self.__serial.write(b"AT!=\"showver\"\r\n")
            else:
                self.__serial.write(b"ATI1\r\n")
            time.sleep(.5)
            shver = self.read_rsp(2000)
            if shver is not None:
                self.print_pretty_response(shver)
            return True

        if debug: print('Initial prepartion complete...')

        if not mirror:
            if bootrom:
                if debug: print('Loading built-in recovery bootrom...')
                try:
                    # try compressed bootrom first
                    from sqnsbrz import bootrom
                except:
                    # fallback to uncompressed
                    from sqnsbr import bootrom
                blob = bootrom()
                blobsize = blob.get_size()
            else:
                if debug: print('Loading {}'.format(file_path))
                blobsize = os.stat(file_path)[6]
                if blobsize < 128:
                    print('Firmware file is too small!')
                    reconnect_uart()
                    sys.exit(1)
                if blobsize > 4194304:
                    if load_fff:
                        print(
                            "Firmware file is too big to load via FFF method. Using ON_THE_FLY"
                        )
                    load_fff = False
                blob = open(file_path, "rb")

        if not load_ffh:
            if not self.wakeup_modem(baudrate, port, 10, 1, debug):
                return False

        if not resume:

            # disable echo
            self.__serial.write(b"ATE0\r\n")
            response = self.read_rsp(size=6)

            self.__serial.read(100)
            if debug: print('Entering upgrade mode...')

            self.__serial.write(b"AT+SMOD?\r\n")
            response = self.return_pretty_response(self.read_rsp(size=7))
            self.__serial.read(100)
            if debug: print("AT+SMOD? returned {}".format(response))

            self.__serial.write(b"AT+SQNSUPGRADENTF=\"started\"\r\n")
            self.wait_for_modem()
            if not load_fff:
                self.__serial.write(b"AT+SMSWBOOT=3,1\r\n")
                resp = self.read_rsp(100)
                if debug: print('AT+SMSWBOOT=3,1 returned: {}'.format(resp))
                if b'ERROR' in resp:
                    time.sleep(5)
                    self.__serial.write(b"AT+SMSWBOOT=3,0\r\n")
                    resp = self.read_rsp(100)
                    if debug:
                        print('AT+SMSWBOOT=3,0 returned: {}'.format(resp))
                    if b'OK' in resp:
                        self.__serial.write(b"AT^RESET\r\n")
                        resp = self.read_rsp(100)
                        if debug: print('AT^RESET returned: {}'.format(resp))
                    else:
                        print('Received ERROR from AT+SMSWBOOT=3,1! Aborting!')
                        reconnect_uart()
                        sys.exit(1)
                time.sleep(3)
                resp = self.__serial.read()
                if debug: print("Response after reset: {}".format(resp))
                self.wait_for_modem()
                self.__serial.write(b"AT\r\n")

        else:
            self.__serial.read(100)
            if debug: print('Entering recovery mode')

            self.__serial.write(b"AT+SMOD?\r\n")
            response = self.return_pretty_response(self.read_rsp(size=7))
            self.__serial.read(100)
            if debug: print("AT+SMOD? returned {}".format(response))

        time.sleep(1)
        self.__serial.read()

        if (not recover) and (not direct):
            if mirror:
                time.sleep(.5)
                self.__serial.read(100)
                print(
                    'Going into MIRROR mode... please close this terminal to resume the upgrade via UART'
                )
                self.uart_mirror(rgbled)

            elif bootrom:
                if verbose: print('Starting STP')
            else:
                if verbose:
                    if load_fff:
                        print('Starting STP [FFF]')
                    else:
                        print('Starting STP ON_THE_FLY')

            self.__serial.read(100)
            if verbose: print("Sending AT+CFUN=4")
            resonse = self.__serial.write(b'AT+CFUN=4\r\n')
            if verbose: print("AT+CFUN=4 returned {}".format(response))
            self.__serial.read(100)

            if load_fff:
                if debug: print("Sending AT+SMSTPU")
                self.__serial.write(b'AT+SMSTPU\r\n')
            else:
                if debug: print("Sending AT+SMSTPU=\"ON_THE_FLY\"")
                self.__serial.write(b'AT+SMSTPU=\"ON_THE_FLY\"\r\n')

            response = self.read_rsp(size=4)
            if response != b'OK\r\n' and response != b'\r\nOK' and response != b'\nOK':
                raise OSError("Invalid answer '%s' from the device" % response)
                blob.close()

            self.__serial.read()
        elif recover and (not direct):
            if atneg:
                result = self.at_negotiation(baudrate, port, max_try, mirror,
                                             atneg_only, debug)
                if result:
                    if atneg_only:
                        return True
                    if mirror:
                        time.sleep(.5)
                        self.__serial.read(100)
                        print(
                            'Going into MIRROR mode... please close this terminal to resume the upgrade via UART'
                        )
                        self.uart_mirror(rgbled)
                    else:
                        self.__serial.write(b"AT+STP\n")
                        response = self.read_rsp(size=6)
                        if not b'OK' in response:
                            print('Failed to start STP mode!')
                            reconnect_uart()
                            sys.exit(1)
                else:
                    print('AT auto-negotiation failed! Exiting.')
                    return False
        else:
            if debug: print('Starting STP mode...')
            self.__serial.write(b"AT+STP\n")
            response = self.read_rsp(size=6)
            if not b'OK' in response:
                print('Failed to start STP mode!')
                reconnect_uart()
                sys.exit(1)

        try:
            if debug:
                if verbose: print('Starting STP code upload')
            if stp.start(blob,
                         blobsize,
                         self.__serial,
                         baudrate,
                         AT=False,
                         debug=debug,
                         pkgdebug=pkgdebug):
                blob.close()
                self.__serial.read()
                if switch_ffh:
                    if verbose:
                        print(
                            'Bootrom updated successfully, switching to recovery mode'
                        )
                    abort = False
                elif load_ffh:
                    if not self.wakeup_modem(baudrate, port, 100, 1, debug,
                                             'Waiting for updater to load...'):
                        return False
                    if verbose:
                        print(
                            'Upgrader loaded successfully, modem is in update mode'
                        )
                    return True
                else:
                    if verbose:
                        print('Code download done, returning to user mode')
                    abort = recover
            else:
                blob.close()
                print('Code download failed, aborting!')
                return False
        except:
            blob.close()
            print('Code download failed, aborting!')
            abort = True

        time.sleep(1.5)

        if not abort:
            self.__serial.read()
            if switch_ffh:
                self.__serial.write(b"AT+SMSWBOOT=0,1\r\n")
                resp = self.read_rsp(100)
                if debug: print("AT+SMSWBOOT=0,1 returned {}".format(resp))
                if b"ERROR" in resp:
                    time.sleep(5)
                    self.__serial.write(b"AT+SMSWBOOT=0,0\r\n")
                    resp = self.read_rsp(100)
                    if debug:
                        print('AT+SMSWBOOT=0,0 returned: {}'.format(resp))
                    if b'OK' in resp:
                        self.__serial.write(b"AT^RESET\r\n")
                        resp = self.read_rsp(100)
                        if debug: print('AT^RESET returned: {}'.format(resp))
                        return True
                    else:
                        print('Received ERROR from AT+SMSWBOOT=0,0! Aborting!')
                        return False
                return True
            else:
                if load_fff:
                    self.__serial.write(b"AT+SMUPGRADE\r\n")
                if not self.wakeup_modem(
                        baudrate, port, 100, 1, debug,
                        self.__get_wait_msg(load_fff=load_fff)):
                    print(
                        "Timeout while waiting for modem to finish updating!")
                    reconnect_uart()
                    sys.exit(1)

                start = time.time()
                while True:
                    self.__serial.read()
                    self.__serial.write(b"AT+SMUPGRADE?\r\n")
                    resp = self.read_rsp(1024)
                    if debug:
                        print("AT+SMUPGRADE? returned {} [timeout: {}]".format(
                            resp,
                            time.time() - start))

                    if resp == b'\x00' or resp == b'':
                        time.sleep(2)

                    if b'No report' in resp or b'on-going' in resp:
                        time.sleep(1)

                    if b'success' in resp or b'fail' in resp:
                        break

                    if time.time() - start >= 300:
                        raise OSError('Timeout waiting for modem to respond!')

                self.__serial.write(b"AT+SMSWBOOT?\r\n")
                resp = self.read_rsp(100)
                if debug: print("AT+SMSWBOOT? returned {}".format(resp))
                start = time.time()
                while (b"RECOVERY"
                       not in resp) and (b"FFH" not in resp) and (b"FFF"
                                                                  not in resp):
                    if debug: print("Timeout: {}".format(time.time() - start))
                    if time.time() - start >= 300:
                        reconnect_uart()
                        raise OSError('Timeout waiting for modem to respond!')
                    time.sleep(2)
                    if not self.wakeup_modem(
                            baudrate, port, 100, 1, debug,
                            self.__get_wait_msg(load_fff=load_fff)):
                        reconnect_uart()
                        raise OSError(
                            'Timeout while waiting for modem to finish updating!'
                        )
                    self.__serial.read()
                    self.__serial.write(b"AT+SMSWBOOT?\r\n")
                    resp = self.read_rsp(100)
                    if debug: print("AT+SMSWBOOT? returned {}".format(resp))
                self.__serial.read()
                self.__serial.write(b"AT+SMUPGRADE?\r\n")
                resp = self.read_rsp(1024)
                if debug: print("AT+SMUPGRADE? returned {}".format(resp))
                sqnup_result = self.return_upgrade_response(resp)
                if debug: print('This is my result: {}'.format(sqnup_result))
                if 'success' in sqnup_result:
                    if not load_fff:
                        self.special_print('Resetting.', end='', flush=True)
                        self.__serial.write(b"AT+SMSWBOOT=1,1\r\n")
                        if debug:
                            print("AT+SMSWBOOT=1,1 returned {}".format(resp))
                        if b"ERROR" in resp:
                            time.sleep(5)
                            self.__serial.write(b"AT+SMSWBOOT=1,0\r\n")
                            resp = self.read_rsp(100)
                            if debug:
                                print('AT+SMSWBOOT=1,0 returned: {}'.format(
                                    resp))
                            if b'OK' in resp:
                                self.__serial.write(b"AT^RESET\r\n")
                                resp = self.read_rsp(100)
                                if debug:
                                    print('AT^RESET returned: {}'.format(resp))
                                return True
                            else:
                                print(
                                    'Received ERROR from AT+SMSWBOOT=1,0! Aborting!'
                                )
                                return False
                        self.wait_for_modem(send=False,
                                            echo_char='.',
                                            expected=b'+SYSSTART')

                elif sqnup_result is not None:
                    print(
                        'Upgrade failed with result {}!'.format(sqnup_result))
                    print('Please check your firmware file(s)')
                else:
                    print("Invalid response after upgrade... aborting.")
                    reconnect_uart()
                    sys.exit(1)

                self.__serial.write(b"AT\r\n")
                self.__serial.write(b"AT\r\n")
                time.sleep(0.5)

                if 'success' in sqnup_result:
                    self.__serial.write(b"AT+SQNSUPGRADENTF=\"success\"\r\n")
                    self.__serial.read()
                    return True
                elif sqnup_result is None:
                    print(
                        'Modem upgrade was unsucessfull. Please check your firmware file(s)'
                    )
        return False

    def __check_br(self, br_only=False, verbose=False, debug=False):
        old_br = None
        old_sw = None
        if debug: print("Checking bootrom & application")
        self.__serial.write(b"AT!=\"showver\"\r\n")
        time.sleep(.5)
        shver = self.read_rsp(2000)
        if shver is not None:
            for line in shver.decode('ascii').split('\n'):
                if debug: print('Checking line {}'.format(line))
                if "Bootloader0" in line:
                    old_br = "[33080]" in line
                    if debug: print("old_br: {}".format(old_br))

                if "Software" in line:
                    old_sw = "[33080]" in line
                    if debug: print("old_sw: {}".format(old_sw))
        if old_br is None or old_sw is None:
            if debug: print("Returning: None")
            return None
        if old_br and (br_only or not old_sw):
            if debug: print("Returning: True")
            return True
        if debug: print("Returning: False")
        return False

    def wakeup_modem(self,
                     baudrate,
                     port,
                     max_try,
                     delay,
                     debug,
                     msg='Attempting AT wakeup...'):
        if 'FiPy' in self.__sysname or 'GPy' in self.__sysname:
            self.__serial = UART(1,
                                 baudrate=baudrate,
                                 pins=self.__pins,
                                 timeout_chars=1)
        MAX_TRY = max_try
        count = 0
        if msg is not None:
            print(msg)
        self.__serial.read()
        self.__serial.write(b"AT\r\n")
        response = self.read_rsp(size=6)
        if debug: print('{}'.format(response))
        while (not b'OK' in response) and (count < MAX_TRY):
            count = count + 1
            if debug: print('count={}'.format(count))
            time.sleep(delay)
            self.__serial.read()
            self.__serial.write(b"AT\r\n")
            response = self.read_rsp(size=6)
            if debug: print('{}'.format(response))
        if 'FiPy' in sysname or 'GPy' in sysname:
            self.__serial = UART(1,
                                 baudrate=baudrate,
                                 pins=self.__pins,
                                 timeout_chars=100)
        return count < MAX_TRY

    def at_negotiation(self, baudrate, port, max_try, mirror, atneg_only,
                       debug):
        MAX_TRY = max_try
        count = 0
        print('Attempting AT auto-negotiation...')
        self.__serial.write(b"AT\r\n")
        response = self.read_rsp(size=6)
        if debug: print('{}'.format(response))
        while (not b'OK' in response) and (count < MAX_TRY):
            count = count + 1
            if debug: print('count={}'.format(count))
            time.sleep(1)
            self.__serial.read()
            self.__serial.write(b"AT\r\n")
            response = self.read_rsp(size=6)
            if debug: print('{}'.format(response))
        if b'OK' in response:
            self.__serial.read()
            cmd = "AT+IPR=%d\n" % baudrate
            if debug: print('Setting baudrate to {}'.format(baudrate))
            self.__serial.write(cmd.encode())
            response = self.read_rsp(size=6)
            if debug: print('{}'.format(response))
            if b'OK' in response:
                if atneg_only:
                    return True
                if 'FiPy' in self.__sysname or 'GPy' in self.__sysname:
                    self.__serial = UART(1,
                                         baudrate=baudrate,
                                         pins=self.__pins,
                                         timeout_chars=100)
                else:
                    self.__serial = None
                    self.__serial = serial.Serial(port,
                                                  baudrate,
                                                  bytesize=serial.EIGHTBITS,
                                                  timeout=0.1)
                    self.__serial.reset_input_buffer()
                    self.__serial.reset_output_buffer()
                    self.__serial.flush()
                self.__serial.read()
                if debug: print('Checking SMOD')
                self.__serial.write(b"AT+SMOD?\r\n")
                response = self.read_rsp(size=1)
                if b'0' in response:
                    if debug: print("AT+SMOD? returned {}".format(response))
                    self.__serial.read()
                    return True
                else:
                    print('ERROR in AT+SMOD returned {}'.format(response))
                    return False
            else:
                print('ERROR in AT+IPR={} returned {}'.format(
                    baudrate, response))
                return False
        else:
            print(
                'ERROR sending AT command... no response? {}'.format(response))
            return False
        time.sleep(1)
        return True

    def uart_mirror(self, color):
        import pycom
        pycom.heartbeat(False)
        time.sleep(.5)
        pycom.rgbled(color)
        LTE.modem_upgrade_mode()

    def success_message(self, port=None, verbose=False, debug=False):
        print("Your modem has been successfully updated.")
        print("Here is the current firmware version:\n")
        self.show_version(port=port, verbose=verbose, debug=debug)

    def upgrade(self,
                ffile,
                mfile=None,
                baudrate=921600,
                retry=False,
                resume=False,
                debug=False,
                pkgdebug=False,
                verbose=False,
                load_fff=True):
        success = True
        if not retry and mfile is not None:
            if resume or self.__check_br(
                    br_only=True, verbose=verbose, debug=debug):
                success = False
                success = self.__run(bootrom=True,
                                     resume=resume,
                                     switch_ffh=True,
                                     direct=False,
                                     debug=debug,
                                     pkgdebug=pkgdebug,
                                     verbose=verbose)
                time.sleep(1)
            else:
                print('{} is not required. Resumining normal upgrade.'.format(
                    mfile))
                mfile = None
                success = True
        if debug: print('Success1? {}'.format(success))
        if success:
            if mfile is not None:
                success = False
                success = self.__run(file_path=mfile,
                                     load_ffh=True,
                                     direct=False,
                                     baudrate=baudrate,
                                     debug=debug,
                                     pkgdebug=pkgdebug,
                                     verbose=verbose)
                time.sleep(1)
            else:
                success = True
        else:
            print('Unable to upgrade bootrom.')
        if debug: print('Success2? {}'.format(success))
        if success:
            if self.__run(file_path=ffile,
                          resume=True if mfile is not None else resume,
                          baudrate=baudrate,
                          direct=False,
                          debug=debug,
                          pkgdebug=pkgdebug,
                          verbose=verbose,
                          load_fff=False if mfile else load_fff):
                if self.__check_br(verbose=verbose, debug=debug):
                    self.__run(bootrom=True,
                               debug=debug,
                               direct=False,
                               pkgdebug=pkgdebug,
                               verbose=verbose,
                               load_fff=True)
                self.success_message(verbose=verbose, debug=debug)
        else:
            print('Unable to load updater from {}'.format(mfile))

    def upgrade_uart(self,
                     ffh_mode=False,
                     mfile=None,
                     retry=False,
                     resume=False,
                     color=0x050505,
                     debug=False,
                     pkgdebug=False,
                     verbose=False,
                     load_fff=True):
        success = False
        try:
            success = hasattr(LTE, 'modem_upgrade_mode')
        except:
            success = False
        if not success:
            print('Firmware does not support LTE.modem_upgrade_mode()!')
            reconnect_uart()
            sys.exit(1)
        print('Preparing modem for upgrade...')
        if not retry and ffh_mode:
            success = False
            success = self.__run(bootrom=True,
                                 resume=resume,
                                 switch_ffh=True,
                                 direct=False,
                                 debug=debug,
                                 pkgdebug=pkgdebug,
                                 verbose=verbose)
            time.sleep(1)
        if success:
            if mfile is not None:
                success = False
                success = self.__run(file_path=mfile,
                                     load_ffh=True,
                                     direct=False,
                                     debug=debug,
                                     pkgdebug=pkgdebug,
                                     verbose=verbose)
                if debug: print('Success2? {}'.format(success))
                if success:
                    self.__run(mirror=True,
                               load_ffh=False,
                               direct=False,
                               rgbled=color,
                               debug=debug,
                               verbose=verbose)
                else:
                    print('Unable to load updater from {}'.format(mfile))
            else:
                self.__run(mirror=True,
                           load_ffh=ffh_mode,
                           direct=False,
                           rgbled=color,
                           debug=debug,
                           verbose=verbose)
        else:
            print('Unable to upgrade bootrom.')

    def show_version(self, port=None, debug=False, verbose=False):
        self.__run(port=port, debug=debug, version_only=True, verbose=verbose)

    def upgrade_ext(self,
                    port,
                    ffile,
                    mfile,
                    resume=False,
                    debug=False,
                    pkgdebug=False,
                    verbose=False,
                    load_fff=True):
        success = True
        if mfile is not None:
            success = False
            success = self.__run(file_path=mfile,
                                 load_ffh=True,
                                 port=port,
                                 debug=debug,
                                 pkgdebug=pkgdebug,
                                 verbose=verbose)
        if success:
            if self.__run(file_path=ffile,
                          resume=True if mfile is not None else resume,
                          direct=False,
                          port=port,
                          debug=debug,
                          pkgdebug=pkgdebug,
                          verbose=verbose,
                          load_fff=load_fff):
                self.success_message(port=port, verbose=verbose, debug=debug)
        else:
            print('Unable to load updater from {}'.format(mfile))
示例#2
0
class sqnsupgrade:

    global sysname

    def __init__(self):

        self.__sysname = sysname
        self.__pins = None
        self.__connected = False
        self.__sdpath = None

    def special_print(self, msg, flush=None, end='\n'):
        if 'FiPy' in self.__sysname or 'GPy' in self.__sysname:
            print(msg, end=end)
        else:
            print(msg, flush=flush, end=end)

    def read_rsp(self, size=None, timeout=-1):
        if timeout < 0:
            timeout = 20000
        elif timeout is None:
            timeout = 0
        if 'FiPy' in self.__sysname or 'GPy' in self.__sysname:
            while not self.__serial.any() and timeout > 0:
                time.sleep_ms(1)
                timeout -= 1
        else:
            while self.__serial.in_waiting <= 0 and timeout > 0:
                time.sleep(0.001)
                timeout -= 1

        if size is not None:
            rsp = self.__serial.read(size)
        else:
            rsp = self.__serial.read()
        if rsp is not None:
            return rsp
        else:
            return b''

    def print_pretty_response(self, rsp, flush=False):
        lines = rsp.decode('ascii').split('\r\n')
        for line in lines:
            if 'OK' not in line:
                self.special_print(line, flush=flush)

    def return_pretty_response(self, rsp):
        ret_str = ''
        lines = rsp.decode('ascii').split('\r\n')
        for line in lines:
            if 'OK' not in line:
                ret_str += line
        return ret_str

    def return_code(self, rsp):
        ret_str = b''
        lines = rsp.decode('ascii').split('\r\n')
        for line in lines:
            if 'OK' not in line:
                ret_str += line
        try:
            return int(ret_str)
        except:
            return -1

    def wait_for_modem(self, send=True, expected=b'OK'):
        rsp = b''
        while True:
            if send:
                self.__serial.write(b"AT\r\n")
            r = self.read_rsp(size=(len(expected) + 4), timeout=50)
            if r:
                rsp += r
            if expected in rsp:
                print()
                break
            else:
                self.special_print('.', end='', flush=True)
                time.sleep(0.5)

    def __check_file(self, file_path, debug=False):
        if 'FiPy' in self.__sysname or 'GPy' in self.__sysname:
            if file_path[
                    0] == '/' and not 'flash' in file_path and not file_path.split(
                        '/')[1] in os.listdir('/'):
                if self.__sdpath is None:
                    self.__sdpath = file_path.split('/')[1]
                    sd = SD()
                    time.sleep(0.5)
                    os.mount(sd, '/{}'.format(self.__sdpath))
                else:
                    print('SD card already mounted on {}!'.format(
                        self.__sdpath))
                    return False
        try:
            size = os.stat(file_path)[6]
            if debug: print('File {} has size {}'.format(file_path, size))
            return True
        except Exception as ex:
            print('Exception when checking file... wrong file name?')
            print('{}'.format(ex))
            return False
        return False

    def check_files(self, ffile, mfile=None, debug=False):
        if mfile is not None:
            if self.__check_file(mfile, debug):
                return self.__check_file(ffile, debug)
            else:
                return False
        else:
            return self.__check_file(ffile, debug)

    def detect_modem_state(self, retry=10, initial_delay=5):
        if 'FiPy' or 'GPy' in self.__sysname:

            if 'GPy' in self.__sysname:
                pins = ('P5', 'P98', 'P7', 'P99')
            else:
                pins = ('P20', 'P18', 'P19', 'P17')
        count = 0
        while count < retry:
            count += 1
            delay = initial_delay * count
            s = UART(1, baudrate=921600, pins=pins, timeout_chars=10)
            s.read()
            s.write(b"AT\r\n")
            time.sleep_ms(delay)
            resp = s.read()
            s.write(b"AT\r\n")
            time.sleep_ms(delay)
            resp = s.read()
            if resp is not None and b'OK' in resp:
                s.write(b"AT+SMOD?\r\n")
                time.sleep_ms(delay)
                resp = s.read()
                try:
                    return self.return_code(resp)
                except:
                    continue
            else:
                s = UART(1, baudrate=115200, pins=pins, timeout_chars=10)
                s.write(b"AT\r\n")
                time.sleep_ms(delay)
                resp = s.read()
                s.write(b"AT\r\n")
                time.sleep_ms(delay)
                resp = s.read()
                if resp is not None and b'OK' in resp:
                    s.write(b"AT+SMOD?\r\n")
                    time.sleep_ms(delay)
                    resp = s.read()
                    try:
                        return self.return_code(resp)
                    except:
                        continue

    def __run(self,
              file_path=None,
              baudrate=921600,
              port=None,
              resume=False,
              load_ffh=False,
              mirror=False,
              switch_ffh=False,
              bootrom=False,
              rgbled=0x050505,
              debug=False,
              pkgdebug=False,
              atneg=True,
              max_try=10,
              direct=True,
              atneg_only=False,
              version_only=False):
        mirror = True if atneg_only else mirror
        recover = True if atneg_only else load_ffh
        resume = True if mirror or recover or atneg_only or version_only else resume
        if debug:
            print(
                'mirror? {}  recover? {}  resume? {}  direct? {}  atneg_only? {} bootrom? {} '
                .format(mirror, recover, resume, direct, atneg_only, bootrom))
        abort = True
        external = False
        self.__serial = None

        if 'FiPy' in self.__sysname or 'GPy' in self.__sysname:

            if 'GPy' in self.__sysname:
                self.__pins = ('P5', 'P98', 'P7', 'P99')
            else:
                self.__pins = ('P20', 'P18', 'P19', 'P17')

            self.__serial = UART(1,
                                 baudrate=115200 if recover else baudrate,
                                 pins=self.__pins,
                                 timeout_chars=100)
            self.__serial.read()
        else:
            if port is None:
                raise ValueError('serial port not specified')
            if debug: print('Setting port {}'.format(port))
            external = True
            br = 115200 if recover and not direct else baudrate
            if debug: print('Setting baudrate to {}'.format(br))
            self.__serial = serial.Serial(port,
                                          br,
                                          bytesize=serial.EIGHTBITS,
                                          timeout=1 if version_only else 0.1)
            self.__serial.reset_input_buffer()
            self.__serial.reset_output_buffer()

        if debug: print('Initial prepartion complete...')

        if version_only:
            self.__serial.read()
            self.__serial.write(b"AT!=\"showver\"\r\n")
            time.sleep(.5)
            shver = self.read_rsp(2000)
            if shver is not None:
                self.print_pretty_response(shver)
            return True

        if not mirror:
            if bootrom:
                if debug: print('Loading built-in recovery bootrom')
                from sqnsbr import bootrom
                blob = bootrom()
                blobsize = blob.get_size()
            else:
                if debug: print('Loading {}'.format(file_path))
                blobsize = os.stat(file_path)[6]
                blob = open(file_path, "rb")

        if not load_ffh:
            if not self.wakeup_modem(baudrate, port, 10, 1, debug):
                return False

        if not resume:

            # disable echo
            self.__serial.write(b"ATE0\r\n")
            response = self.read_rsp(size=6)

            self.__serial.read(100)
            if debug: print('Entering recovery mode')

            self.__serial.write(b"AT+SMOD?\r\n")
            response = self.return_pretty_response(self.read_rsp(size=7))
            self.__serial.read(100)
            if debug: print("AT+SMOD? returned {}".format(response))

            if not bootrom:
                self.__serial.write(b"AT+SMSWBOOT=3,1\r\n")
                time.sleep(2)
                self.wait_for_modem()
                self.__serial.write(b"AT\r\n")
                self.__serial.write(b"AT\r\n")

        else:
            self.__serial.read(100)
            if debug: print('Entering recovery mode')

            self.__serial.write(b"AT+SMOD?\r\n")
            response = self.return_pretty_response(self.read_rsp(size=7))
            self.__serial.read(100)
            if debug: print("AT+SMOD? returned {}".format(response))

        time.sleep(1)
        self.__serial.read()

        if (not recover) and (not direct):
            if mirror:
                time.sleep(.5)
                self.__serial.read(100)
                print(
                    'Going into MIRROR mode... please close this terminal to resume the upgrade via UART'
                )
                self.uart_mirror(rgbled)

            elif bootrom:
                print('Starting STP (DO NOT DISCONNECT POWER!!!)')

            else:
                print('Starting STP ON_THE_FLY')
            self.__serial.read(100)

            self.__serial.write(b'AT+SMSTPU=\"ON_THE_FLY\"\r\n')
            response = self.read_rsp(size=4)
            if response != b'OK\r\n' and response != b'\r\nOK' and response != b'\nOK':
                raise OSError("Invalid answer '%s' from the device" % response)
                blob.close()

            self.__serial.read()
        elif recover and (not direct):
            if atneg:
                result = self.at_negotiation(baudrate, port, max_try, mirror,
                                             atneg_only, debug)
                if result:
                    if atneg_only:
                        return True
                    if mirror:
                        time.sleep(.5)
                        self.__serial.read(100)
                        print(
                            'Going into MIRROR mode... please close this terminal to resume the upgrade via UART'
                        )
                        self.uart_mirror(rgbled)
                    else:
                        self.__serial.write(b"AT+STP\n")
                        response = self.read_rsp(size=6)
                        if not b'OK' in response:
                            print('Failed to start STP mode!')
                            sys.exit(1)
                else:
                    print('AT auto-negotiation failed! Exiting.')
                    return False
        else:
            if debug: print('Starting STP mode...')
            self.__serial.write(b"AT+STP\n")
            response = self.read_rsp(size=6)
            if not b'OK' in response:
                print('Failed to start STP mode!')
                sys.exit(1)

        try:
            if debug:
                print('Starting STP code upload')
            if stp.start(blob,
                         blobsize,
                         self.__serial,
                         baudrate,
                         AT=False,
                         debug=debug,
                         pkgdebug=pkgdebug):
                blob.close()
                if switch_ffh:
                    print(
                        'Bootrom updated successfully, switching to upgrade mode'
                    )
                    abort = False
                elif load_ffh:
                    if not self.wakeup_modem(baudrate, port, 100, 1, debug):
                        return False
                    print(
                        'Upgrader loaded successfully, modem is in upgrade mode'
                    )
                    return True
                else:
                    print('Code download done, returning to user mode')
                    abort = recover
            else:
                blob.close()
                print('Code download failed, aborting!')
                return False
        except:
            blob.close()
            print('Code download failed, aborting!')
            abort = True

        time.sleep(1.5)

        if not abort:
            self.__serial.read()
            if switch_ffh:
                self.__serial.write(b"AT+SMSWBOOT=0,1\r\n")
                return True
            else:
                self.special_print('Resetting (DO NOT DISCONNECT POWER!!!).',
                                   end='',
                                   flush=True)
                self.__serial.write(b"AT+SMSWBOOT=1,1\r\n")
                self.wait_for_modem(send=False, expected=b'+SYSSTART')

                self.__serial.write(b"AT\r\n")
                self.__serial.write(b"AT\r\n")
                time.sleep(0.5)
                self.__serial.read()
                print('Upgrade completed!')
                print("Here's the current firmware version:")
                time.sleep(0.5)
                self.__serial.read()
                self.__serial.write(b"AT!=\"showver\"\r\n")
                time.sleep(.5)
                shver = self.read_rsp(2000)
                if shver is not None:
                    self.print_pretty_response(shver)
                return True
        return False

    def wakeup_modem(self, baudrate, port, max_try, delay, debug):
        if 'FiPy' in self.__sysname or 'GPy' in self.__sysname:
            self.__serial = UART(1,
                                 baudrate=baudrate,
                                 pins=self.__pins,
                                 timeout_chars=1)
        MAX_TRY = max_try
        count = 0
        print('Attempting AT wakeup...')
        self.__serial.read()
        self.__serial.write(b"AT\r\n")
        response = self.read_rsp(size=6)
        if debug: print('{}'.format(response))
        while (not b'OK' in response) and (count < MAX_TRY):
            count = count + 1
            if debug: print('count={}'.format(count))
            time.sleep(delay)
            self.__serial.read()
            self.__serial.write(b"AT\r\n")
            response = self.read_rsp(size=6)
            if debug: print('{}'.format(response))
        if 'FiPy' in sysname or 'GPy' in sysname:
            self.__serial = UART(1,
                                 baudrate=baudrate,
                                 pins=self.__pins,
                                 timeout_chars=100)
        return count < MAX_TRY

    def at_negotiation(self, baudrate, port, max_try, mirror, atneg_only,
                       debug):
        MAX_TRY = max_try
        count = 0
        print('Attempting AT auto-negotiation...')
        self.__serial.write(b"AT\r\n")
        response = self.read_rsp(size=6)
        if debug: print('{}'.format(response))
        while (not b'OK' in response) and (count < MAX_TRY):
            count = count + 1
            if debug: print('count={}'.format(count))
            time.sleep(1)
            self.__serial.read()
            self.__serial.write(b"AT\r\n")
            response = self.read_rsp(size=6)
            if debug: print('{}'.format(response))
        if b'OK' in response:
            self.__serial.read()
            cmd = "AT+IPR=%d\n" % baudrate
            if debug: print('Setting baudrate to {}'.format(baudrate))
            self.__serial.write(cmd.encode())
            response = self.read_rsp(size=6)
            if debug: print('{}'.format(response))
            if b'OK' in response:
                if atneg_only:
                    return True
                if 'FiPy' in self.__sysname or 'GPy' in self.__sysname:
                    self.__serial = UART(1,
                                         baudrate=baudrate,
                                         pins=self.__pins,
                                         timeout_chars=100)
                else:
                    self.__serial = None
                    self.__serial = serial.Serial(port,
                                                  baudrate,
                                                  bytesize=serial.EIGHTBITS,
                                                  timeout=0.1)
                    self.__serial.reset_input_buffer()
                    self.__serial.reset_output_buffer()
                    self.__serial.flush()
                self.__serial.read()
                if debug: print('Checking SMOD')
                self.__serial.write(b"AT+SMOD?\r\n")
                response = self.read_rsp(size=1)
                if b'0' in response:
                    if debug: print("AT+SMOD? returned {}".format(response))
                    self.__serial.read()
                    return True
                else:
                    print('ERROR in AT+SMOD returned {}'.format(response))
                    return False
            else:
                print('ERROR in AT+IPR={} returned {}'.format(
                    baudrate, response))
                return False
        else:
            print(
                'ERROR sending AT command... no response? {}'.format(response))
            return False
        time.sleep(1)
        return True

    def uart_mirror(self, color):
        import pycom
        pycom.heartbeat(False)
        time.sleep(.5)
        pycom.rgbled(color)
        from network import LTE
        LTE.modem_upgrade_mode()

    def upgrade_sd(self,
                   ffile,
                   mfile=None,
                   baudrate=921600,
                   retry=False,
                   resume=False,
                   debug=False,
                   pkgdebug=False):
        print('<<< Welcome to the SQN3330 firmware updater >>>')
        success = True
        if not retry and mfile is not None:
            success = False
            success = self.__run(bootrom=True,
                                 resume=resume,
                                 switch_ffh=True,
                                 direct=False,
                                 debug=debug,
                                 pkgdebug=pkgdebug)
            time.sleep(1)
        if debug: print('Success1? {}'.format(success))
        if success:
            if mfile is not None:
                success = False
                success = self.__run(file_path=mfile,
                                     load_ffh=True,
                                     direct=False,
                                     baudrate=baudrate,
                                     debug=debug,
                                     pkgdebug=pkgdebug)
                time.sleep(1)
            else:
                success = True
        else:
            print('Unable to upgrade bootrom.')
        if debug: print('Success2? {}'.format(success))
        if success:
            self.__run(file_path=ffile,
                       resume=True if mfile is not None else resume,
                       baudrate=baudrate,
                       direct=False,
                       debug=debug,
                       pkgdebug=pkgdebug)
        else:
            print('Unable to load updater from {}'.format(mfile))

    def upgrade_uart(self,
                     ffh_mode=False,
                     mfile=None,
                     retry=False,
                     resume=False,
                     color=0x050505,
                     debug=False,
                     pkgdebug=False):
        success = True
        print('Preparing modem for upgrade...')
        if not retry and ffh_mode:
            success = False
            success = self.__run(bootrom=True,
                                 resume=resume,
                                 switch_ffh=True,
                                 direct=False,
                                 debug=debug,
                                 pkgdebug=pkgdebug)
            time.sleep(1)
        if success:
            if mfile is not None:
                success = False
                success = self.__run(file_path=mfile,
                                     load_ffh=True,
                                     direct=False,
                                     debug=debug,
                                     pkgdebug=pkgdebug)
                if debug: print('Success2? {}'.format(success))
                if success:
                    self.__run(mirror=True,
                               load_ffh=False,
                               direct=False,
                               rgbled=color,
                               debug=debug)
                else:
                    print('Unable to load updater from {}'.format(mfile))
            else:
                self.__run(mirror=True,
                           load_ffh=ffh_mode,
                           direct=False,
                           rgbled=color,
                           debug=debug)
        else:
            print('Unable to upgrade bootrom.')

    def show_version(self, port=None, debug=False):
        self.__run(port=port, debug=debug, version_only=True)

    def upgrade_ext(self,
                    port,
                    ffile,
                    mfile,
                    resume=False,
                    debug=False,
                    pkgdebug=False):
        success = True
        print('<<< Welcome to the SQN3330 firmware updater >>>')
        if mfile is not None:
            success = False
            success = self.__run(file_path=mfile,
                                 load_ffh=True,
                                 port=port,
                                 debug=debug,
                                 pkgdebug=pkgdebug)
        if success:
            self.__run(file_path=ffile,
                       resume=True if mfile is not None else resume,
                       direct=False,
                       port=port,
                       debug=debug,
                       pkgdebug=pkgdebug)
        else:
            print('Unable to load updater from {}'.format(mfile))
示例#3
0
def run(file_path, baudrate, port=None, resume=False):
    global sysname

    abort = True
    s = None

    print('<<< Welcome to the SQN3330 firmware updater >>>')

    if 'FiPy' in sysname or 'GPy' in sysname:
        if '/sd' in file_path and not 'sd' in os.listdir('/'):
            sd = SD()
            time.sleep(0.5)
            os.mount(sd, '/sd')
            time.sleep(0.5)

        if 'GPy' in sysname:
            pins = ('P5', 'P98', 'P7', 'P99')
        else:
            pins = ('P20', 'P18', 'P19', 'P17')

        s = UART(1, baudrate=baudrate, pins=pins, timeout_chars=100)
        s.read()
    else:
        if port is None:
            raise ValueError('serial port not specified')
        s = serial.Serial(port,
                          baudrate=921600,
                          bytesize=serial.EIGHTBITS,
                          timeout=0.1)
        s.reset_input_buffer()
        s.reset_output_buffer()

    blobsize = os.stat(file_path)[6]
    blob = open(file_path, "rb")

    if not resume:
        # disable echo
        s.write(b"ATE0\r\n")
        response = read_rsp(s, size=6)

        s.read(100)
        print('Entering recovery mode')
        s.write(b"AT+SMSWBOOT=3,0\r\n")
        response = read_rsp(s, size=6)
        if b'OK' in response:
            print('Resetting.', end='', flush=True)
            s.write(b'AT^RESET\r\n')
            wait_for_modem(s, send=False, expected=b'+SHUTDOWN')
            time.sleep(2)
            wait_for_modem(s)
            s.write(b"AT\r\n")
            s.write(b"AT\r\n")
        else:
            raise OSError('AT+SMSWBOOT=3,0 failed!')

    time.sleep(1)
    s.read()

    print('Starting STP (DO NOT DISCONNECT POWER!!!)')
    s.read(100)
    s.write(b'AT+SMSTPU=\"ON_THE_FLY\"\r\n')
    response = read_rsp(s, size=4)
    if response != b'OK\r\n' and response != b'\r\nOK' and response != b'\nOK':
        raise OSError("Invalid answer '%s' from the device" % response)
        blob.close()

    s.read()
    try:
        stp.start(blob, blobsize, s, baudrate, AT=False)
        print('Code download done, returning to user mode')
        abort = False
    except:
        blob.close()
        print('Code download failed, aborting!')
        abort = True

    time.sleep(1.5)
    s.read()
    s.write(b"AT+SMSWBOOT=1,0\r\n")
    response = read_rsp(s, size=6)

    print('Resetting (DO NOT DISCONNECT POWER!!!).', end='', flush=True)
    time.sleep(1.5)
    s.write(b"AT^RESET\r\n")
    wait_for_modem(s, send=False, expected=b'+SHUTDOWN')
    time.sleep(2)
    wait_for_modem(s, send=False, expected=b'+SYSSTART')

    if not abort:
        time.sleep(0.5)
        print('Deploying the upgrade (DO NOT DISCONNECT POWER!!!)...')
        s.write(b"AT+SMUPGRADE\r\n")
        response = read_rsp(s, size=6, timeout=120000)

        print('Resetting (DO NOT DISCONNECT POWER!!!).', end='', flush=True)
        time.sleep(1.5)
        s.write(b"AT^RESET\r\n")
        wait_for_modem(s, send=False, expected=b'+SHUTDOWN')
        time.sleep(2)
        wait_for_modem(s, send=False, expected=b'+SYSSTART')
        s.write(b"AT\r\n")
        s.write(b"AT\r\n")
        time.sleep(0.5)
        s.read()
        print('Upgrade completed!')
        print("Here's the current firmware version:")
        time.sleep(0.5)
        s.read()
        s.write(b"ATI1\r\n")
        response = read_rsp(s, size=100)
        print_pretty_response(response)