code: async def _update_firmware(filename, loop):
      
      try:
          from opentrons import robot
      except ModuleNotFoundError:
          res = "Unable to find module `opentrons`--not updating firmware"
          rc = 1
          log.error(res)
      else:
          # ensure there is a reference to the port
          if not robot.is_connected():
              robot.connect()
  
          # get port name
          port = str(robot._driver.port)
          # set smoothieware into programming mode
          robot._driver._smoothie_programming_mode()
          # close the port so other application can access it
          robot._driver._connection.close()
  
          # run lpc21isp, THIS WILL TAKE AROUND 1 MINUTE TO COMPLETE
          update_cmd = 'lpc21isp -wipe -donotstart {0} {1} {2} 12000'.format(
              filename, port, robot.config.serial_speed)
          proc = await asyncio.create_subprocess_shell(
              update_cmd,
              stdout=asyncio.subprocess.PIPE,
              loop=loop)
          rd = await proc.stdout.read()
          res = rd.decode().strip()
          await proc.communicate()
          rc = proc.returncode
  
          if rc == 0:
              # re-open the port
              robot._driver._connection.open()
              # reset smoothieware
              robot._driver._smoothie_reset()
              # run setup gcodes
              robot._driver._setup()
  
      return res, rc
docs: Currently uses the robot singleton from the API server to connect to
      Smoothie. Those calls should be separated out from the singleton so it can
      be used directly without requiring a full initialization of the API robot.