text: #!/usr/bin/env python
  
  """
  The WiPy firmware update script. Transmits the specified firmware file
  over FTP, and then resets the WiPy and optionally verifies that software
  was correctly updated.
  
  Usage:
  
      ./update-wipy.py --file "path_to_mcuimg.bin" --verify
  
  Or:
  
      python update-wipy.py --file "path_to_mcuimg.bin"
  """
  
  import sys
  import argparse
  import time
  import socket
  from ftplib import FTP
  from telnetlib import Telnet
  
  
  def print_exception(e):
      print("Exception: {}, on line {}".format(e, sys.exc_info()[-1].tb_lineno))
  
  
  def ftp_directory_exists(ftpobj, directory_name):
      filelist = []
      ftpobj.retrlines("LIST", filelist.append)
      for f in filelist:
          if f.split()[-1] == directory_name:
              return True
      return False
  
  
  def transfer_file(args):
      with FTP(args.ip, timeout=20) as ftp:
          print("FTP connection established")
  
          if "230" in ftp.login(args.user, args.password):
              print("Login successful")
  
              if "250" in ftp.cwd("/flash"):
                  if not ftp_directory_exists(ftp, "sys"):
                      print("/flash/sys directory does not exist")
                      if not "550" in ftp.mkd("sys"):
                          print("/flash/sys directory created")
                      else:
                          print("Error: cannot create /flash/sys directory")
                          return False
                  if "250" in ftp.cwd("sys"):
                      print("Entered '/flash/sys' directory")
                      with open(args.file, "rb") as fwfile:
                          print("Firmware image found, initiating transfer...")
                          if "226" in ftp.storbinary("STOR " + "mcuimg.bin", fwfile, 512):
                              print("File transfer complete")
                              return True
                          else:
                              print("Error: file transfer failed")
                  else:
                      print("Error: cannot enter /flash/sys directory")
              else:
                  print("Error: cannot enter /flash directory")
          else:
              print("Error: ftp login failed")
  
      return False
  
  
  def reset_board(args):
      success = False
  
      try:
          tn = Telnet(args.ip, timeout=5)
          print("Connected via Telnet, trying to login now")
  
          if b"Login as:" in tn.read_until(b"Login as:", timeout=5):
              tn.write(bytes(args.user, "ascii") + b"\r\n")
  
              if b"Password:" in tn.read_until(b"Password:", timeout=5):
                  # needed because of internal implementation details of the WiPy's telnet server
                  time.sleep(0.2)
                  tn.write(bytes(args.password, "ascii") + b"\r\n")
  
                  if b'Type "help()" for more information.' in tn.read_until(
                      b'Type "help()" for more information.', timeout=5
                  ):
                      print("Telnet login succeeded")
                      tn.write(b"\r\x03\x03")  # ctrl-C twice: interrupt any running program
                      time.sleep(1)
                      tn.write(b"\r\x02")  # ctrl-B: enter friendly REPL
                      if b'Type "help()" for more information.' in tn.read_until(
                          b'Type "help()" for more information.', timeout=5
                      ):
                          tn.write(b"import machine\r\n")
                          tn.write(b"machine.reset()\r\n")
                          time.sleep(2)
                          print("Reset performed")
                          success = True
                      else:
                          print("Error: cannot enter friendly REPL")
                  else:
                      print("Error: telnet login failed")
  
      except Exception as e:
          print_exception(e)
      finally:
          try:
              tn.close()
          except Exception as e:
              pass
          return success
  
  
  def verify_update(args):
      success = False
      firmware_tag = ""
  
      def find_tag(tag):
          if tag in firmware_tag:
              print("Verification passed")
              return True
          else:
              print("Error: verification failed, the git tag doesn't match")
              return False
  
      retries = 0
      while True:
          try:
              # Specify a longer time out value here because the board has just been
              # reset and the wireless connection might not be fully established yet
              tn = Telnet(args.ip, timeout=10)
              print("Connected via telnet again, lets check the git tag")
              break
          except socket.timeout:
              if retries < 5:
                  print("Timeout while connecting via telnet, retrying...")
                  retries += 1
              else:
                  print("Error: Telnet connection timed out!")
                  return False
  
      try:
          firmware_tag = tn.read_until(b"with CC3200")
          tag_file_path = args.file.rstrip("mcuimg.bin") + "genhdr/mpversion.h"
  
          if args.tag is not None:
              success = find_tag(bytes(args.tag, "ascii"))
          else:
              with open(tag_file_path) as tag_file:
                  for line in tag_file:
                      bline = bytes(line, "ascii")
                      if b"MICROPY_GIT_HASH" in bline:
                          bline = (
                              bline.lstrip(b"#define MICROPY_GIT_HASH ")
                              .replace(b'"', b"")
                              .replace(b"\r", b"")
                              .replace(b"\n", b"")
                          )
                          success = find_tag(bline)
                          break
  
      except Exception as e:
          print_exception(e)
      finally:
          try:
              tn.close()
          except Exception as e:
              pass
          return success
  
  
  def main():
      cmd_parser = argparse.ArgumentParser(
          description="Update the WiPy firmware with the specified image file"
      )
      cmd_parser.add_argument("-f", "--file", default=None, help="the path of the firmware file")
      cmd_parser.add_argument("-u", "--user", default="micro", help="the username")
      cmd_parser.add_argument("-p", "--password", default="python", help="the login password")
      cmd_parser.add_argument("--ip", default="192.168.1.1", help="the ip address of the WiPy")
      cmd_parser.add_argument(
          "--verify", action="store_true", help="verify that the update succeeded"
      )
      cmd_parser.add_argument("-t", "--tag", default=None, help="git tag of the firmware image")
      args = cmd_parser.parse_args()
  
      result = 1
  
      try:
          if args.file is None:
              raise ValueError("the image file path must be specified")
          if transfer_file(args):
              if reset_board(args):
                  if args.verify:
                      print("Waiting for the WiFi connection to come up again...")
                      # this time is to allow the system's wireless network card to
                      # connect to the WiPy again.
                      time.sleep(5)
                      if verify_update(args):
                          result = 0
                  else:
                      result = 0
  
      except Exception as e:
          print_exception(e)
      finally:
          sys.exit(result)
  
  
  if __name__ == "__main__":
      main()
  