diff --git a/.gitignore b/.gitignore
index 51fbad32bf30b3c45a40f8900f8619fcf8952b4e..fbb8c0eedf313e85c0e3903dadf3391c09928306 100644
--- a/.gitignore
+++ b/.gitignore
@@ -142,4 +142,5 @@ cython_debug/
 # custom
 weights/
 creds.json
-src/creds.py
+creds.py
+.log
diff --git a/farmbot_yolo/client.py b/farmbot_yolo/client.py
index 4aa4344f18ed1381e7a08e7331f46078db7cd20f..45d5f9b8cdcad40e231044a67c5318ffe1069cc4 100755
--- a/farmbot_yolo/client.py
+++ b/farmbot_yolo/client.py
@@ -1,3 +1,4 @@
+<<<<<<<< HEAD:farmbot_yolo/client.py
 '''
 Communicate with the server at the manufacturer
 Not useful for the local drive script
@@ -139,3 +140,345 @@ class FarmbotClient(object):
       logging.debug("> _on_message [%s] [%s]", msg.topic, resp)
     if msg.topic.endswith("/from_device") and resp['args']['label'] == self.pending_uuid:
       self.rpc_status = resp['kind']
+|||||||| 90c14bb:src/client.py
+'''
+Communicate with the server at the manufacturer
+Not useful for the local drive script
+'''
+import paho.mqtt.client as mqtt
+import json
+import time
+from uuid import uuid4 # 通用唯一标识符 ( Universally Unique Identifier )
+import logging #日志模块
+
+# values over max (and under min) will be clipped
+MAX_X = 2400
+MAX_Y = 1200
+MAX_Z = 469  # TODO test this one!
+
+def coord(x, y, z):
+  return {"kind": "coordinate", "args": {"x": x, "y": y, "z": z}} # 返回json 嵌套对象
+
+def move_request(x, y, z):
+  return {"kind": "rpc_request",  # 返回 json对象,对象内含数组
+          "args": {"label": ""},
+          "body": [{"kind": "move_absolute",
+                    "args": {"location": coord(x, y, z),
+                             "offset": coord(0, 0, 0),
+                             "speed": 100}}]}
+
+def take_photo_request():
+  return {"kind": "rpc_request",
+          "args": {"label": ""}, #label空着是为了在blocking_request中填上uuid,唯一识别码
+          "body": [{"kind": "take_photo", "args": {}}]}
+
+def clip(v, min_v, max_v):
+  if v < min_v: return min_v
+  if v > max_v: return max_v
+  return v
+
+class FarmbotClient(object):
+
+  def __init__(self, device_id, token):
+
+    self.device_id = device_id
+    self.client = mqtt.Client() # 类元素继承了另一个对象
+    self.client.username_pw_set(self.device_id, token) #传入 用户名和密码
+    self.client.on_connect = self._on_connect  #???
+    self.client.on_message = self._on_message
+
+    logging.basicConfig(level=logging.DEBUG,
+                        format="%(asctime)s\t%(name)s\t%(levelname)s\t%(message)s",
+                        filename='farmbot_client.log',
+                        filemode='a')
+    console = logging.StreamHandler()
+    console.setLevel(logging.INFO)
+    console.setFormatter(logging.Formatter("%(asctime)s\t%(message)s"))
+    logging.getLogger('').addHandler(console)
+
+    self.connected = False
+    self.client.connect("clever-octopus.rmq.cloudamqp.com", 1883, 60)  #前面的url要运行按README.md中request_token.py 后面俩是TCP Port, Websocket Port
+    self.client.loop_start()
+    # 初始化函数里就会连接到服务器上,所以每次实例化一个新的client时,就已经连上了
+
+
+  def shutdown(self):
+    self.client.disconnect()
+    self.client.loop_stop()
+
+  def move(self, x, y, z):
+    x = clip(x, 0, MAX_X)
+    y = clip(y, 0, MAX_Y)
+    z = clip(z, 0, MAX_Z)
+    status_ok = self._blocking_request(move_request(x, y, z)) # 发请求
+    logging.info("MOVE (%s,%s,%s) [%s]", x, y, z, status_ok) #存日志,包括执行了什么“move x y z +返回值 ”
+
+  def take_photo(self):
+    # TODO: is this enough? it's issue a request for the photo, but is the actual capture async?
+    status_ok = self._blocking_request(take_photo_request())
+    logging.info("TAKE_PHOTO [%s]", status_ok)
+
+  def _blocking_request(self, request, retries_remaining=3):
+    if retries_remaining==0:
+      logging.error("< blocking request [%s] OUT OF RETRIES", request) #尝试3次,然后在日志中记录错误
+      return False
+
+    self._wait_for_connection() #在哪定义的?
+
+    # assign a new uuid for this attempt
+    self.pending_uuid = str(uuid4())
+    request['args']['label'] = self.pending_uuid #接收move_request函数的json对象
+    logging.debug("> blocking request [%s] retries=%d", request, retries_remaining)
+
+    # send request off 发送请求
+    self.rpc_status = None
+    self.client.publish("bot/" + self.device_id + "/from_clients", json.dumps(request))
+
+    # wait for response
+    timeout_counter = 600  # ~1min 等待1s
+    while self.rpc_status is None:          #这个self.rpc_status 是应答的flag
+      time.sleep(0.1)
+      timeout_counter -= 1
+      if timeout_counter == 0:
+        logging.warn("< blocking request TIMEOUT [%s]", request) #时间到了,无应答
+        return self._blocking_request(request, retries_remaining-1)
+    self.pending_uuid = None
+
+    # if it's ok, we're done!
+    if self.rpc_status == 'rpc_ok':
+      logging.debug("< blocking request OK [%s]", request)
+      return True
+
+    # if it's not ok, wait a bit and retry
+    if self.rpc_status == 'rpc_error':
+      logging.warn("< blocking request ERROR [%s]", request)
+      time.sleep(1)
+      return self._blocking_request(request, retries_remaining-1)
+
+    # unexpected state (???)
+    msg = "unexpected rpc_status [%s]" % self.rpc_status
+    logging.error(msg)
+    raise Exception(msg)
+
+
+  def _wait_for_connection(self):
+    # TODO: better way to do all this async event driven rather than with polling :/
+    timeout_counter = 600  # ~1min
+    while not self.connected: #用一个self.connected判断连上了没有,若没连上,等待
+      time.sleep(0.1)
+      timeout_counter -= 1
+      if timeout_counter == 0:
+        raise Exception("unable to connect")
+
+  def _on_connect(self, client, userdata, flags, rc):
+    logging.debug("> _on_connect")
+    self.client.subscribe("bot/" + self.device_id + "/from_device")
+    self.connected = True
+    logging.debug("< _on_connect")
+
+  def _on_message(self, client, userdata, msg):
+    resp = json.loads(msg.payload.decode())
+    if resp['args']['label'] != 'ping':
+      logging.debug("> _on_message [%s] [%s]", msg.topic, resp)
+    if msg.topic.endswith("/from_device") and resp['args']['label'] == self.pending_uuid:
+      self.rpc_status = resp['kind']
+========
+'''
+Communicate with the server at the manufacturer
+Not useful for the local drive script
+'''
+import paho.mqtt.client as mqtt
+import json
+import time
+from uuid import uuid4 # 通用唯一标识符 ( Universally Unique Identifier )
+import logging #日志模块
+
+# values over max (and under min) will be clipped
+MAX_X = 2400
+MAX_Y = 1200
+MAX_Z = 469  # TODO test this one!
+
+
+def coord(x, y, z):
+  return {"kind": "coordinate", "args": {"x": x, "y": y, "z": z}} # 返回json 嵌套对象
+
+def move_request(x, y, z):
+  return {"kind": "rpc_request",  # 返回 json对象,对象内含数组
+          "args": {"label": ""},
+          "body": [{"kind": "move_absolute",
+                    "args": {"location": coord(x, y, z),
+                             "offset": coord(0, 0, 0),
+                             "speed": 100}}]}
+
+def read_pin_request(pin_number, pin_mode="digital"):
+  modes = {"digital": 0, "analog": 1}
+
+  if pin_mode != "digital":
+    raise NotImplementedError()
+
+  return {"kind": "rpc_request",
+          "args": {"label": ""},
+          "body": [{"kind": "read_pin",
+                    "args": {
+                      "label": "pin" + str(pin_number),
+                      "pin_mode": modes[pin_mode] or (modes["digital"]),
+                      "pin_number": pin_number
+                    }}]}
+
+def write_pin_request(pin_number, pin_value, pin_mode="digital"):
+  modes = {"digital": 0, "analog": 1}
+
+  if pin_mode != "digital":
+    raise NotImplementedError()
+
+  return {"kind": "rpc_request",
+          "args": {"label": ""},
+          "body": [{"kind": "write_pin",
+                    "args": {
+            "pin_mode": modes[pin_mode] or (modes["digital"]),
+            "pin_number": pin_number,
+            "pin_value": pin_value
+                            }
+                    }
+                  ]
+          }
+
+def toggle_pin_request(pin_number):
+  return {"kind": "rpc_request",
+          "args": {"label": ""},
+          "body": [{"kind": "toggle_pin",
+                    "args": {
+            "pin_number": pin_number,
+                            }
+                    }
+                  ]
+          }
+
+def take_photo_request():
+  return {"kind": "rpc_request",
+          "args": {"label": ""}, #label空着是为了在blocking_request中填上uuid,唯一识别码
+          "body": [{"kind": "take_photo", "args": {}}]}
+
+def clip(v, min_v, max_v):
+  if v < min_v: return min_v
+  if v > max_v: return max_v
+  return v
+
+class FarmbotClient(object):
+
+  def __init__(self, device_id, token):
+
+    self.device_id = device_id
+    self.client = mqtt.Client() # 类元素继承了另一个对象
+    self.client.username_pw_set(self.device_id, token) #传入 用户名和密码
+    self.client.on_connect = self._on_connect  #???
+    self.client.on_message = self._on_message
+
+    logging.basicConfig(level=logging.DEBUG,
+                        format="%(asctime)s\t%(name)s\t%(levelname)s\t%(message)s",
+                        filename='farmbot_client.log',
+                        filemode='a')
+    console = logging.StreamHandler()
+    console.setLevel(logging.INFO)
+    console.setFormatter(logging.Formatter("%(asctime)s\t%(message)s"))
+    logging.getLogger('').addHandler(console)
+
+    self.connected = False
+    self.client.connect("clever-octopus.rmq.cloudamqp.com", 1883, 60)  #前面的url要运行按README.md中request_token.py 后面俩是TCP Port, Websocket Port
+    self.client.loop_start()
+    # 初始化函数里就会连接到服务器上,所以每次实例化一个新的client时,就已经连上了
+
+
+  def shutdown(self):
+    self.client.disconnect()
+    self.client.loop_stop()
+
+  def move(self, x, y, z):
+    x = clip(x, 0, MAX_X)
+    y = clip(y, 0, MAX_Y)
+    z = clip(z, 0, MAX_Z)
+    status_ok = self._blocking_request(move_request(x, y, z)) # 发请求
+    logging.info("MOVE (%s,%s,%s) [%s]", x, y, z, status_ok) #存日志,包括执行了什么“move x y z +返回值 ”
+
+  def take_photo(self):
+    # TODO: is this enough? it's issue a request for the photo, but is the actual capture async?
+    status_ok = self._blocking_request(take_photo_request())
+    logging.info("TAKE_PHOTO [%s]", status_ok)
+
+  def read_pin(self, pin_number, pin_mode="digital"):
+    status_ok = self._blocking_request(read_pin_request(pin_number, pin_mode=pin_mode))
+    logging.info(f"READ PIN (pin_number: {pin_number}) [{status_ok}]")
+
+  def write_pin(self, pin_number, pin_value, pin_mode="digital"):
+    status_ok = self._blocking_request(write_pin_request(pin_number, pin_value, pin_mode=pin_mode))
+    logging.info(f"WRITE PIN (pin_number: {pin_number}, pin_value: {pin_value}) [{status_ok}]")
+
+  def toggle_pin(self, pin_number):
+    status_ok = self._blocking_request(toggle_pin_request(pin_number))
+    logging.info(f"TOGGLE PIN (pin_number: {pin_number}) [{status_ok}]")
+
+  def _blocking_request(self, request, retries_remaining=3):
+    if retries_remaining==0:
+      logging.error("< blocking request [%s] OUT OF RETRIES", request) #尝试3次,然后在日志中记录错误
+      return False
+
+    self._wait_for_connection() #在哪定义的?
+
+    # assign a new uuid for this attempt
+    self.pending_uuid = str(uuid4())
+    request['args']['label'] = self.pending_uuid #接收move_request函数的json对象
+    logging.debug("> blocking request [%s] retries=%d", request, retries_remaining)
+
+    # send request off 发送请求
+    self.rpc_status = None
+    self.client.publish("bot/" + self.device_id + "/from_clients", json.dumps(request))
+
+    # wait for response
+    timeout_counter = 600  # ~1min 等待1s
+    while self.rpc_status is None:          #这个self.rpc_status 是应答的flag
+      time.sleep(0.1)
+      timeout_counter -= 1
+      if timeout_counter == 0:
+        logging.warn("< blocking request TIMEOUT [%s]", request) #时间到了,无应答
+        return self._blocking_request(request, retries_remaining-1)
+    self.pending_uuid = None
+
+    # if it's ok, we're done!
+    if self.rpc_status == 'rpc_ok':
+      logging.debug("< blocking request OK [%s]", request)
+      return True
+
+    # if it's not ok, wait a bit and retry
+    if self.rpc_status == 'rpc_error':
+      logging.warn("< blocking request ERROR [%s]", request)
+      time.sleep(1)
+      return self._blocking_request(request, retries_remaining-1)
+
+    # unexpected state (???)
+    msg = "unexpected rpc_status [%s]" % self.rpc_status
+    logging.error(msg)
+    raise Exception(msg)
+
+
+  def _wait_for_connection(self):
+    # TODO: better way to do all this async event driven rather than with polling :/
+    timeout_counter = 600  # ~1min
+    while not self.connected: #用一个self.connected判断连上了没有,若没连上,等待
+      time.sleep(0.1)
+      timeout_counter -= 1
+      if timeout_counter == 0:
+        raise Exception("unable to connect")
+
+  def _on_connect(self, client, userdata, flags, rc):
+    logging.debug("> _on_connect")
+    self.client.subscribe("bot/" + self.device_id + "/from_device")
+    self.connected = True
+    logging.debug("< _on_connect")
+
+  def _on_message(self, client, userdata, msg):
+    resp = json.loads(msg.payload.decode())
+    if resp['args']['label'] != 'ping':
+      logging.debug("> _on_message [%s] [%s]", msg.topic, resp)
+    if msg.topic.endswith("/from_device") and resp['args']['label'] == self.pending_uuid:
+      self.rpc_status = resp['kind']
+>>>>>>>> master:src/utils/client.py
diff --git a/farmbot_yolo/download.py b/farmbot_yolo/download.py
new file mode 100644
index 0000000000000000000000000000000000000000..67caace59ae348c9a5181e23e9d0faebee39516b
--- /dev/null
+++ b/farmbot_yolo/download.py
@@ -0,0 +1,102 @@
+"""Download images from Farmbot instance."""
+import argparse
+import os
+from pathlib import Path
+from typing import List
+
+import requests
+
+import utils.creds as creds
+
+
+# note: download only returns 100 at a time!
+# note: we are currently ignoreing placeholders
+
+DEBUG_SKIP_DELETE_FILES = True
+
+IMG_FILE_SUFFIX = ".jpg"
+
+REQUEST_HEADERS = {
+    "Authorization": "Bearer " + creds.token,
+    "content-type": "application/json",
+}
+
+
+def download_images(directory: os.PathLike, delete_after=False) -> List[str]:
+    """Download all images on server, optionally deleting after download.
+
+    Parameters
+    ----------
+    directory
+        directory to store images in
+
+    Raises
+    ------
+    RuntimeError
+        If the server gives a bad response (not 200).
+
+    Returns
+    -------
+    list of str
+        List of filepaths with downloaded images
+    """
+    response = requests.get("https://my.farmbot.io/api/images", headers=REQUEST_HEADERS)
+    json_response = response.json()
+
+    if response.status_code != 200:
+        raise RuntimeError(f"Got status code {response.status_code}.")
+
+    print(json_response)
+    print(f"Got a response containing {len(json_response)}")
+
+    if len(json_response) < 1:
+        return []
+
+    img_paths = []
+
+    for img_dict in json_response:
+        if "placehold.it" in img_dict["attachment_url"]:
+            print("IGNORE! placeholder", img_dict["id"])
+            continue
+
+        server_path: str = img_dict["meta"]["name"]
+
+        if not server_path.startswith("/tmp/images"):
+            print("meta name does not start with /tmp/images")
+            continue
+
+        filename = Path(server_path).stem + IMG_FILE_SUFFIX
+        filepath = Path(directory) / filename
+
+        print(">", filepath)
+
+        # download image from google storage and save locally
+        if filepath.exists():
+            print("File exists, skipping")
+            continue
+
+        img_req = requests.get(img_dict["attachment_url"], allow_redirects=True)
+
+        with filepath.open(mode="wb") as fp:
+            fp.write(img_req.content)
+
+        img_paths.append(filepath)
+
+        # post delete from cloud storage
+        if delete_after:
+            requests.delete(
+                f"https://my.farmbot.io/api/images/{img_dict['id']}",
+                headers=REQUEST_HEADERS,
+            )
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="Farmbot YOLO image downloader")
+    parser.add_argument(
+        "download_dir", type=Path, help="Directory to download images too."
+    )
+    parser.add_argument("-d", "--delete", action="store_true")
+
+    args = parser.parse_args()
+
+    download_images(args.download_dir, delete_after=args.delete)
diff --git a/farmbot_yolo/gripper.py b/farmbot_yolo/gripper.py
index d53b714fc353c24ca9f0435d3f0dc8262a09aa92..27b72215533b7f2414874aca6fb8068055dd86e7 100755
--- a/farmbot_yolo/gripper.py
+++ b/farmbot_yolo/gripper.py
@@ -1,11 +1,24 @@
-#!/usr/bin/env python3
-import serial
+"""Functions to manipulate gripper.
+
+TODO: Integrate with FarmbotClient or FarmbotYoloClient
+"""
+from utils.client import FarmbotClient
+from utils.creds import device_id
+from utils.creds import token
+
+GRIPPER_PIN = 12
+GRIPPER_OPEN_STATE = 0
+GRIPPER_CLOSED_STATE = 1
+
 def gripper_open():
-    ser = serial.Serial('/dev/ttyUSB0')
-    ser.write(str.encode("o"))
-    ser.close
+    """Manipulate gripper by setting pin to 1."""
+    client = FarmbotClient(device_id, token)
+    client.write_pin(GRIPPER_PIN, GRIPPER_OPEN_STATE, pin_mode="digital")
+    client.shutdown()
+
 
 def gripper_close():
-    ser = serial.Serial('/dev/ttyUSB0')
-    ser.write(str.encode("c"))
-    ser.close
\ No newline at end of file
+    """Manipulate gripper by setting pin to 0."""
+    client = FarmbotClient(device_id, token)
+    client.write_pin(GRIPPER_PIN, GRIPPER_CLOSED_STATE, pin_mode="digital")
+    client.shutdown()
diff --git a/farmbot_yolo/main.py b/farmbot_yolo/main.py
index 3ae2d0fb5f624a9d5d75b607ea641982f252c25a..5295b4e6bd94d337197f2f567cd3d2e31ed4a3dd 100644
--- a/farmbot_yolo/main.py
+++ b/farmbot_yolo/main.py
@@ -48,7 +48,6 @@ def remove_overlap(table_coordinate:DataFrame, tolerance=50.00)->DataFrame:
     return table_coordinate              
 
 
-
 def remove_temp(path: Path)-> None:
     '''
     Clean temporary files, i.e., photos, location.txt, annotations
diff --git a/farmbot_yolo/move.py b/farmbot_yolo/move.py
index 85eeba094f630110b2bee97ae25dcb72061daa83..064cf5518240f03df8a79a0d1239cab629c89afb 100644
--- a/farmbot_yolo/move.py
+++ b/farmbot_yolo/move.py
@@ -1,15 +1,17 @@
-'''
+"""
 Author: Ziliang Xiong
 This script is for all the functions that drive Farmbot to Move, including:
 1. Taking Photos 2. Move to an assigned point (x, y, z)
 3. Sweep the planting bed 4. Grip a target
 Note: it is for remote server, can ben replaced by a local script
-'''
+"""
 from argparse import ArgumentParser
 from logging import getLogger
 from os import path, makedirs, system
-from time  import sleep, strftime, time
-#from serial import Serial, PARITY_NONE, STOPBITS_ONE, EIGHTBITS 
+import sys
+from time import sleep, strftime, time
+
+# from serial import Serial, PARITY_NONE, STOPBITS_ONE, EIGHTBITS
 from requests.api import delete
 from typing import List
 from pathlib import Path
@@ -20,14 +22,14 @@ from datetime import timezone, datetime
 from dateutil.parser import parse
 from requests import get, delete
 
-from creds import read_credentials
+import creds
 from client import FarmbotClient
 
-
 _SWEEEP_HEIGHT = 0
 
 Logger = getLogger(__name__)
 
+
 class Opts:
     def __init__(self, min_x, max_x, min_y, max_y, delta, offset, flag):
         self.min_x = min_x
@@ -37,11 +39,20 @@ class Opts:
         self.delta = delta
         self.offset = offset
         self.flag = flag
-    
 
-def scan(img_path: Path, location_path: Path, # smaller delta
-         min_x=0, max_x=1300, min_y=0, max_y=1000, delta=1000, offset=0, flag=True) -> List: #里面的数字需要重新测量
-    '''
+
+def scan(
+    img_path: Path,
+    location_path: Path,  # smaller delta
+    min_x=0,
+    max_x=1175,
+    min_y=0,
+    max_y=974,
+    delta=300,
+    offset=0,
+    flag=True,
+) -> List:  # 里面的数字需要重新测量
+    """
     scan the bed at a certain height, first move along x axis, then y, like a zig zag;
     Taking pictures and record the location of the camera that corresponds to the picture
     The default value of x, y should be from the measurement of Farmbot
@@ -53,7 +64,7 @@ def scan(img_path: Path, location_path: Path, # smaller delta
            offset:
            flag: for degging, if true, don't actually drive FarmBot
     Output: none
-    '''
+    """
     opts = Opts(min_x, max_x, min_y, max_y, delta, offset, flag)
     creds = read_credentials()
 
@@ -65,95 +76,98 @@ def scan(img_path: Path, location_path: Path, # smaller delta
             y_range = reversed(y_range)
         sweep_y_negative = not sweep_y_negative
         for y in y_range:
-            pts.append((x+opts.offset, y+opts.offset))
+            pts.append((x + opts.offset, y + opts.offset))
 
-    Logger.info('Moving pattern generated')
+    Logger.info("Moving pattern generated")
 
     if opts.flag:
-        Logger.info('Run without sweep')
+        Logger.info("Run without sweep")
         exit()
 
     client = FarmbotClient(creds["device_id"], creds["token"])
-    client.move(0, 0, _SWEEEP_HEIGHT) # ensure moving from original 
+    client.move(0, 0, _SWEEEP_HEIGHT)  # ensure moving from original
     for x, y in pts:
-        client.move(x, y, _SWEEEP_HEIGHT) # move camera
-        take_photo(img_path)
+        client.move(x, y, _SWEEEP_HEIGHT)  # move camera
+        # take_photo(img_path)
+        client.take_photo()
     client.shutdown()
     # write to img/location
-    with open(path.join(location_path, "location.txt"), 'w') as f:
+    with open(path.join(location_path, "location.txt"), "w") as f:
         for postion in pts:
-            f.write('{} {} {}\n'.format(postion[0], postion[1], _SWEEEP_HEIGHT))
-    return None 
+            f.write("{} {} {}\n".format(postion[0], postion[1], _SWEEEP_HEIGHT))
+    return None
 
 
-def take_photo(img_path: Path):
-    HERE = path.dirname(__file__)
-    IMG_DIR = path.join(HERE, img_path)
+def take_photo():
+    client = FarmbotClient(creds.device_id, creds.token)
+    client.take_photo()
+    # download image
+    system("python ./utils/download.py")
 
-    with request.urlopen('http://localhost:8080/?action=snapshot') as photo:
-        filename = datetime.now().strftime("%Y-%m-%dT%H:%M:%S") + ".jpg"
-        with open(path.join(IMG_DIR, filename), mode="wb") as save_file:
-            save_file.write(photo.read())
 
+# def take_photo(img_path: Path):
+#     HERE = path.dirname(__file__)
+#     IMG_DIR = path.join(HERE, img_path)
 
-def simple_move(x: int, y: int, z: int) -> None: 
-    '''
+#     with request.urlopen('http://localhost:8080/?action=snapshot') as photo:
+#         filename = datetime.now().strftime("%Y-%m-%dT%H:%M:%S") + ".jpg"
+#         with open(path.join(IMG_DIR, filename), mode="wb") as save_file:
+#             save_file.write(photo.read())
+
+
+def simple_move(x: int, y: int, z: int) -> None:
+    """
     Move to a place, if flag is true, take a picture
     Input: x, y,z: destination point
            photo: take a pic or not
-    '''
+    """
     creds = read_credentials()
     client = FarmbotClient(creds["device_id"], creds["token"])
-    client.move(x, y, z)  
+    client.move(x, y, z)
     client.shutdown()
     return None
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     parser = ArgumentParser()
     parser.add_argument(
-        '-m',
-        '--mode',
+        "-m",
+        "--mode",
         type=int,
-        help='Mode for FarmBot, 1 for simple move with an assigned detination, 2 for scaning' 
+        help="Mode for FarmBot, 1 for simple move with an assigned detination, 2 for scaning",
     )
     parser.add_argument(
-        '-l',
-        '--log',
-        type=Path,
-        default='../log/move.log',
-        help='Path to the log file'
+        "-l", "--log", type=Path, default="../log/move.log", help="Path to the log file"
     )
     parser.add_argument(
-        '-p',
-        '--photo',
+        "-p",
+        "--photo",
         type=Path,
         default="../img",
-        help='Mode for FarmBot, 1 for simple move with an assigned detination, 2 for scaning' 
+        help="Mode for FarmBot, 1 for simple move with an assigned detination, 2 for scaning",
     )
     parser.add_argument(
-        '-loc',
-        '--locations',
+        "-loc",
+        "--locations",
         type=Path,
-        default='../img/locations/',
-        help='the path to txt files contains locations from encoders corresponds to each photo'
+        default="../img/locations/",
+        help="the path to txt files contains locations from encoders corresponds to each photo",
     )
-    parser.add_argument('-v', '--verbose', action='store_true', help='Verbose mode')
+    parser.add_argument("-v", "--verbose", action="store_true", help="Verbose mode")
     arguments = parser.parse_args()
-    
 
     if arguments.mode == 1:
-        Logger.info('Input the destination:')
-        destination_x = int(input('X:'))
-        destination_y = int(input('Y:'))
-        destination_z = int(input('Z:'))
-        photo = True if input('Take a photo or not?[Y/N]:') == 'Y' else False 
+        Logger.info("Input the destination:")
+        destination_x = int(input("X:"))
+        destination_y = int(input("Y:"))
+        destination_z = int(input("Z:"))
         simple_move_start = time()
-        simple_move(destination_x, destination_y, destination_z, photo)
-        Logger.info(f'time cost {time()-simple_move_start}')
+        simple_move(destination_x, destination_y, destination_z)
+        Logger.info(f"time cost {time()-simple_move_start}")
     elif arguments.mode == 2:
         scan(arguments.photo, arguments.locations, flag=False)
+        # take_photo(arguments.photo)
+    elif arguments.mode == 3:
+        take_photo()
     else:
-        Logger.error('Wrong mode number {arguments.mode}')
-
-
+        Logger.error("Wrong mode number {arguments.mode}")
diff --git a/img/WIN_20211103_21_16_18_Pro.jpg b/img/WIN_20211103_21_16_18_Pro.jpg
deleted file mode 100644
index 27b585013a55f2853a29424ccfeb124cc719edb1..0000000000000000000000000000000000000000
Binary files a/img/WIN_20211103_21_16_18_Pro.jpg and /dev/null differ
diff --git a/img/annotations/WIN_20211103_21_16_18_Pro.txt b/img/annotations/WIN_20211103_21_16_18_Pro.txt
deleted file mode 100644
index 7fd6b5a6fdeeb230cbf138bde46f802ea1928095..0000000000000000000000000000000000000000
--- a/img/annotations/WIN_20211103_21_16_18_Pro.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-3 1055.2865 148.5889 443.2753 73.4710 99.5100
-1 188.5453 22.3466 85.2053 49.0357 99.9200
-3 1048.2261 134.2752 271.3041 76.3464 99.9600
diff --git a/img/locations/WIN_20211101_17_19_37_Pro.txt b/img/locations/WIN_20211101_17_19_37_Pro.txt
deleted file mode 100644
index fdd0b107edc822f4b6d0a84b83d0b5d910757b21..0000000000000000000000000000000000000000
--- a/img/locations/WIN_20211101_17_19_37_Pro.txt
+++ /dev/null
@@ -1 +0,0 @@
-350 700 -100
\ No newline at end of file
diff --git a/img/readme.md b/img/readme.md
deleted file mode 100644
index d6af6012b91c95a8c1a29b38ffad1f453ca7a0e9..0000000000000000000000000000000000000000
--- a/img/readme.md
+++ /dev/null
@@ -1,2 +0,0 @@
-This folder is to store all the photos of the planting bed, 
-and is supposed to be cleaned each time after detection.
\ No newline at end of file