diff --git a/config.yml b/config.yml
index 4d98384..3a9f9ec 100644
--- a/config.yml
+++ b/config.yml
@@ -6,7 +6,7 @@ core:
   loopspeed: 60 # fps
 
 mqtt:
-  enabled: True
+  enabled: False
   server: 172.31.108.4
 
 arm:
@@ -28,8 +28,8 @@ cables:
 
 cameras:
   banner:
-    ip: 192.168.1.125
-    port: 32200
+    ip: 192.168.1.199
+    port: 80
 
 animation_time: 60
 
@@ -268,7 +268,7 @@ led:
       size: 24
       diameter: 63.5
       angle: 180
-      pos: [197.973, 38.1]
+      pos: [201.973, 34.1]
     - type: circle
       start: 624
       size: 24
@@ -474,7 +474,7 @@ position_map:
   - index: 7
     pos: [-38.1, 197.973]
   - index: 8
-    pos: [38.1, 197.973]
+    pos: [34.1, 201.973]
   - index: 9
     pos: [114.3, 197.973]
   - index: 10
@@ -564,4 +564,4 @@ position_map:
   - index: 52
     pos: [76.2, -263.965]
   - index: 53
-    pos: [152.4, -263.965]
\ No newline at end of file
+    pos: [152.4, -263.965]
diff --git a/map.png b/map.png
index f0f08f2..6fa53ff 100644
Binary files a/map.png and b/map.png differ
diff --git a/pick_count.txt b/pick_count.txt
index 7c6ba0f..2ebc651 100644
--- a/pick_count.txt
+++ b/pick_count.txt
@@ -1 +1 @@
-55
\ No newline at end of file
+56
\ No newline at end of file
diff --git a/process_video.py b/process_video.py
index 303ab59..13143d6 100755
--- a/process_video.py
+++ b/process_video.py
@@ -43,7 +43,7 @@ class qr_reader():
             response = requests.get(self.url, timeout=tries * 15)
             response.raise_for_status()  # Raise an error for bad status codes
             print(response.text)  # Or handle the response as needed
-            if len(response.text < 8):
+            if len(response.text) < 8:
                 return False
             return response.text
         except requests.Timeout:
@@ -69,4 +69,4 @@ if __name__ == "__main__":
     import time
     while True:
         fprint(test.read_qr(300))
-        time.sleep(1)
\ No newline at end of file
+        time.sleep(1)
diff --git a/requirements.txt b/requirements.txt
index e5aefa5..5aa5e39 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,6 @@
 # Runtime
 camelot-py[base]==0.9.0
-opencv-python
+#opencv-python
 pypdf2==2.12.1
 alive-progress
 requests
diff --git a/run.py b/run.py
index 2311388..99887b9 100755
--- a/run.py
+++ b/run.py
@@ -11,6 +11,7 @@ import multiprocessing
 from time import sleep
 from util import fprint
 from util import run_cmd
+from util import win32
 import sys
 import ur5_control
 from ur5_control import Rob
@@ -33,9 +34,10 @@ import fileserver
 import paho.mqtt.client as mqtt
 import pickle
 import time
+import subprocess
 
 # set to false to run without real hardware for development
-real = False
+real = True
 skip_scanning = True
 
 mbconn = None
@@ -82,8 +84,10 @@ unacked_publish = set()
 mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
 counter_file = 'pick_count.txt'
 cycle_start_time = time.time()
-
-
+arm_distance = 0
+arm_distance_old = 0
+arm_distance_total = 0
+kill_ssh = False
 mqttc.user_data_set(unacked_publish)
 
 def arm_start_callback(res):
@@ -375,7 +379,8 @@ def check_server():
                                 mainloop_get.put(("pickup", data["position"]))
                             elif "tray" in data:
                                 fprint("Adding tray return to dispense queue")
-                                mainloop_get.put(("return", data["tray"]))
+                                mainloop_get.put(("returnCheck", 0))
+                                #mainloop_get.put(("return", data["tray"]))
                             else:
                                 fprint("Invalid data.")
                         elif call == 'request':
@@ -549,7 +554,7 @@ def get_sensors():
     
     """
     if real:
-        sens = [352, 288, 304, 368]
+        sens = [320, 336, 352, 368]
         for idx in range(len(sens)):
             reg = sens[idx]
             val = mbconn.read_holding_registers(reg)
@@ -636,10 +641,13 @@ def mainloop_server(pool, manager):
     global timecount
     global secondsclock
     global cycle_start_time
-
+    global arm_distance
+    global arm_distance_old
+    global arm_distance_total
 
     if mode != oldmode:
         print(" ***** Running mode:", mode, "***** ")
+        send_data("mode", "send", "{\"mode\": \"" + mode + "\" }")
         oldmode = mode
         mqtt_send(mode, "mode")
         if mode == "Startup": # very first loop
@@ -661,6 +669,12 @@ def mainloop_server(pool, manager):
         
         if isinstance(val, tuple):
             arm_position = val
+            if arm_distance_old == 0:
+                arm_distance_old = val[0] + val[1] + val[2]
+                arm_distance = 0
+            else:
+                arm_distance += val[0] + val[1] + val[2] - arm_distance_old
+                arm_distance_old = val[0] + val[1] + val[2] 
         else:
             print("Arm queue message " + str(val))
             checkpoint = val
@@ -706,7 +720,13 @@ def mainloop_server(pool, manager):
     # every 1 second
     if secondsclock >= config["core"]["loopspeed"]:
         secondsclock = 1
-        
+        arm_distance_total += arm_distance
+        if abs(arm_distance) < 0.001:
+            arm_distance = 0.0
+            
+        mqtt_send("{\"value\": " + str(abs(arm_distance)) + " }", "arm_speed")
+        mqtt_send("{\"value\": " + str(abs(arm_distance_total)) + " }", "arm_distance") 
+        arm_distance_old = 0 # reset counter
 
 
 
@@ -767,7 +787,7 @@ def mainloop_server(pool, manager):
                 global scan_value
                 if scan_value is False:
                     cable_list.append(scan_value)
-                elif scan_value.upper.find("BLDN.APP/") > -1:
+                elif scan_value.upper().find("BLDN.APP/") > -1:
                     scan_value = scan_value[scan_value.find("bldn.app/")+9:]
                 else:
                     cable_list.append(scan_value)
@@ -880,14 +900,7 @@ def mainloop_server(pool, manager):
 
         else:
             global mainloop_get
-            #print("Checking sensors..")
-            if real:
-                newtube = get_sensors()
-            else:
-                newtube = -1
-            if newtube >= 0:
-                # need to return a cable
-                mainloop_get.put(("return", newtube))
+            
             
             if not mainloop_get.empty():
                 action, get_cable = mainloop_get.get()
@@ -902,8 +915,19 @@ def mainloop_server(pool, manager):
                     pool.apply_async(ur5_control.move_to_packup, (arm,), callback=arm_start_callback, error_callback=handle_error)
                     sleep(30)
                     killme.set(1)
+                elif action == "returnCheck":
+                     print("Checking sensors..")
+                     if real:
+                         newtube = get_sensors()
+                     else:
+                         newtube = -1
+                     if newtube >= 0:
+                         # need to return a cable
+                         mainloop_get.put(("return", newtube))
+                         mainloop_get.put(("returnCheck", 0))
                 else:
                     fprint("Movement requested. Keep clear of the machine!")
+                    arm_distance_total = 0
                     #mqtt_send("{\"value\": " + str(time.time() * 1000) + " }", "cycle_start")
                     cycle_start_time = int(time.time() * 1000)
                     increment_counter()
@@ -995,8 +1019,8 @@ def mainloop_server(pool, manager):
                     timecount = 0
                     pool.apply_async(camera.read_qr, (10,), callback=camera_start_callback, error_callback=handle_error)
 
-            elif scan_value.find("bldn.app/") > -1:
-                scan_value = scan_value[scan_value.find("bldn.app/")+9:]
+            elif scan_value.upper().find("BLDN.APP/") > -1:
+                scan_value = scan_value[scan_value.upper().find("BLDN.APP/")+9:]
             
                 fprint("Got cable: " + str(scan_value))
                 if scan_value[0:2] == "BL" or scan_value[0:2] == "AW":
@@ -1054,7 +1078,23 @@ def mainloop_server(pool, manager):
             
     
         
+def ping(host):
+        #Returns True if host (str) responds to a ping request.
 
+        # Option for the number of packets as a function of
+        if win32:
+            param1 = '-n'
+            param2 = '-w'
+            param3 = '250'
+        else:
+            param1 = '-c'
+            param2 = '-W'
+            param3 = '0.25'
+
+        # Building the command. Ex: "ping -c 1 google.com"
+        command = ['ping', param1, '1', param2, param3, host]
+
+        return subprocess.call(command, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) == 0
 
 def run_loading_app():
     
@@ -1071,25 +1111,44 @@ def setup_client(pool):
     global serverproc
     if config["core"]["server"] == "Hyper-V":
         run_cmd("Start-VM -Name Jukebox*") # any and all VMs starting with "Jukebox"
+
+    fprint("Waiting for VM to start...")
+    while not ping("192.168.1.25"):
+        sleep(0.25)
+
+    fprint("VM online.")
     # Windows client setup
+    from subprocess import PIPE
+    fprint("Running full jukebox control system...")
+    jb = subprocess.Popen("ssh root@192.168.1.25 -- /root/jukebox-software/run.sh".split(' '), stdout=PIPE, stderr=PIPE)
+    for line in jb.stdout:
+        print(line, end='')
+        if line.find("Running mode: Idle") > 0:
+            # Jukebox started
+            break # continue with program
+
     fprint("Opening browser...")
     
     firefox = webdriver.Firefox()
     firefox.fullscreen_window()
-    
-    # Open loading wepage
-    p = Process(target=run_loading_app)
-    p.start()
 
     
-    firefox.get('http://localhost:7000')
-    
-    # start Linux server VM
+        # firefox.get('http://localhost:7000')
     
 
-    sleep(35)
-    p.terminate()
     firefox.get('http://192.168.1.25:3000')
+    global kill_ssh
+    while kill_ssh is False:
+        sleep(0.1)
+        
+    firefox.close()
+    jb.terminate()
+    run_cmd("Stop-VM -Name Jukebox*")
+    killall()
+        
+    #firefox.execute_script('document.body.style.MozTransform = "scale(0.80)";')
+    #firefox.execute_script('document.body.style.MozTransformOrigin = "0 0";')
+    #firefox.execute_script("document.body.style.zoom='80%'")
     # import time
     # while True:
     #     time.sleep(2)  # Wait for a given interval
@@ -1101,12 +1160,12 @@ def setup_client(pool):
     #         # else:
     #         #     break  # Exit the loop or continue depending on your logic
 
-    return True
+        #return True
     
 
 def mainloop_client(pool):
     sleep(0.1)
-
+    killall()
     # listen for & act on commands from VM, if needed
     # mainly just shut down, possibly connect to wifi or something
 
@@ -1139,7 +1198,11 @@ def killall_signal(a, b):
     global config
     if config["core"]["server"] == "Hyper-V":
         run_cmd("Stop-VM -Name Jukebox*") # any and all VMs starting with "Jukebox"
-    killall()
+    if config["core"]["mode"] == "winclient":
+        global kill_ssh
+        kill_ssh = True
+    else:
+        killall()
 
 def error(msg, *args):
     return multiprocessing.get_logger().error(msg, *args)
diff --git a/run.sh b/run.sh
new file mode 100755
index 0000000..90cd12d
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+pkill -9 python # kill any old processes
+cd /root/jukebox-software
+python run.py
\ No newline at end of file
diff --git a/ur5_control.py b/ur5_control.py
index 21c3c97..0269453 100755
--- a/ur5_control.py
+++ b/ur5_control.py
@@ -581,7 +581,7 @@ def tray_routine(robot, slot=0, pick_up=True):
 
    # Positions for each slot
     slot_distance = .052
-    slot_height = -.015-.0095
+    slot_height = -.015-.0095+0.007 # add 7mm for shim
     first_slot = -0.3084+0.01
     slot_position = [
         [first_slot, -0.3426, slot_height, 1.5899, 1.5526, -0.9411],
@@ -745,27 +745,28 @@ def close_gripper():
 
 
 def get_position_thread(robot, pos_updates):
-    robot = connect(robot)
-    rob = robot.robot
-    oldvals = rob.getl()
-    deltavals = [0,0,0]
-    import uptime
-    t = 0.01
-    count = 0
-    while True:
-        start = uptime.uptime()
-        if pos_updates.qsize() < 2:
-            vals = rob.getl()
-            if vals != oldvals:
-                if pos_updates is not None:
-                    pos_updates.put(tuple(oldvals))
+    try:
+        robot = connect(robot)
+        rob = robot.robot
+        oldvals = rob.getl()
+        deltavals = [0,0,0]
+        import uptime
+        t = 0.01
+        count = 0
+        while True:
+            start = uptime.uptime()
+            if pos_updates.qsize() < 2:
+                vals = rob.getl()
+                if vals != oldvals:
+                    if pos_updates is not None:
+                        pos_updates.put(tuple(oldvals))
                 #time.sleep(0.01)
                 # deltavals = list()
                 # deltavals.append(vals[0]-oldvals[0])
                 # deltavals.append(vals[1]-oldvals[1])
                 # deltavals.append(vals[2]-oldvals[2])
                 # count = 0
-                oldvals = vals
+                    oldvals = vals
                 
             # else:
             #     count += 0.2
@@ -775,9 +776,10 @@ def get_position_thread(robot, pos_updates):
             #         tmpvals[1] = oldvals[1] + deltavals[1]*count
             #         tmpvals[2] = oldvals[2] + deltavals[2]*count
             #         pos_updates.put(tuple(tmpvals))
-        while start + t > uptime.uptime():
-            time.sleep(0.0001)
-                
+            while start + t > uptime.uptime():
+                time.sleep(0.0001)
+    except:
+        pass
 
 if __name__ == "__main__":
     
@@ -795,9 +797,9 @@ if __name__ == "__main__":
     rob = robot.robot # rob is robot.robot is the urx robot class, what we've been using previously
 
 
-    move_to_packup(robot)
-    
-    # pick_up_holder(robot, 2)
+    #move_to_packup(robot)
+    move_to_home(robot)
+    pick_up_holder(robot, None, 8)
     #drop_off_tray(robot, 0)
     # drop_off_tray(robot, 1)
     # drop_off_tray(robot, 2)