From 0f57818f2decca8340fd5fc86f9ea7b792d9d802 Mon Sep 17 00:00:00 2001
From: thisiskeithb <13375512+thisiskeithb@users.noreply.github.com>
Date: Fri, 15 Nov 2019 00:51:26 -0800
Subject: [PATCH] Support 512K "RCT6" in BTT SKR Mini (#15890)

---
 .travis.yml                                   |    2 +-
 Marlin/src/HAL/HAL_STM32F1/HAL.h              |    8 +
 Marlin/src/pins/pins.h                        |    8 +-
 .../src/pins/stm32/pins_BIGTREE_SKR_E3_DIP.h  |    8 +-
 .../src/pins/stm32/pins_BIGTREE_SKR_MINI_E3.h |    8 +-
 .../pins/stm32/pins_BIGTREE_SKR_MINI_V1_1.h   |    8 +-
 .../pins/stm32/pins_BTT_SKR_MINI_E3_V1_2.h    |    2 +-
 ...R_MINI.ld => STM32F103RC_SKR_MINI_256K.ld} |    0
 .../ldscripts/STM32F103RC_SKR_MINI_512K.ld    |   14 +
 .../scripts/STM32F103RC_SKR_MINI.py           |    9 +-
 buildroot/share/atom/auto_build.py            | 2021 +++++++++--------
 ...-tests => STM32F103RC_bigtree_NOUSB-tests} |    0
 platformio.ini                                |   42 +-
 13 files changed, 1089 insertions(+), 1041 deletions(-)
 rename buildroot/share/PlatformIO/ldscripts/{STM32F103RC_SKR_MINI.ld => STM32F103RC_SKR_MINI_256K.ld} (100%)
 create mode 100644 buildroot/share/PlatformIO/ldscripts/STM32F103RC_SKR_MINI_512K.ld
 rename buildroot/share/tests/{STM32F103RC_bigtree_USB-tests => STM32F103RC_bigtree_NOUSB-tests} (100%)

diff --git a/.travis.yml b/.travis.yml
index 745023fc5b..68282c727b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -27,7 +27,7 @@ env:
 
   # Extended STM32 Environments
   - TEST_PLATFORM="STM32F103RC_bigtree"
-  - TEST_PLATFORM="STM32F103RC_bigtree_USB"
+  - TEST_PLATFORM="STM32F103RC_bigtree_NOUSB"
   - TEST_PLATFORM="STM32F103RC_fysetc"
   - TEST_PLATFORM="jgaurora_a5s_a1"
   - TEST_PLATFORM="STM32F103VE_longer"
diff --git a/Marlin/src/HAL/HAL_STM32F1/HAL.h b/Marlin/src/HAL/HAL_STM32F1/HAL.h
index 044914d0a2..a88b72107e 100644
--- a/Marlin/src/HAL/HAL_STM32F1/HAL.h
+++ b/Marlin/src/HAL/HAL_STM32F1/HAL.h
@@ -51,6 +51,14 @@
 // Defines
 // ------------------------
 
+#ifndef STM32_FLASH_SIZE
+  #ifdef MCU_STM32F103RE
+    #define STM32_FLASH_SIZE 512
+  #else
+    #define STM32_FLASH_SIZE 256
+  #endif
+#endif
+
 #ifdef SERIAL_USB
   #ifndef USE_USB_COMPOSITE
     #define UsbSerial Serial
diff --git a/Marlin/src/pins/pins.h b/Marlin/src/pins/pins.h
index 2b84e38171..bec999aa28 100644
--- a/Marlin/src/pins/pins.h
+++ b/Marlin/src/pins/pins.h
@@ -470,13 +470,13 @@
 #elif MB(MKS_ROBIN_LITE)
   #include "stm32/pins_MKS_ROBIN_LITE.h"        // STM32F1                                env:mks_robin_lite
 #elif MB(BIGTREE_SKR_MINI_V1_1)
-  #include "stm32/pins_BIGTREE_SKR_MINI_V1_1.h" // STM32F1                                env:STM32F103RC_bigtree
+  #include "stm32/pins_BIGTREE_SKR_MINI_V1_1.h" // STM32F1                                env:STM32F103RC_bigtree env:STM32F103RC_bigtree_512K env:STM32F103RC_bigtree_NOUSB env:STM32F103RC_bigtree_512K_NOUSB
 #elif MB(BTT_SKR_MINI_E3_V1_2)
-  #include "stm32/pins_BTT_SKR_MINI_E3_V1_2.h"  // STM32F1                                env:STM32F103RC_bigtree
+  #include "stm32/pins_BTT_SKR_MINI_E3_V1_2.h"  // STM32F1                                env:STM32F103RC_bigtree env:STM32F103RC_bigtree_512K env:STM32F103RC_bigtree_NOUSB env:STM32F103RC_bigtree_512K_NOUSB
 #elif MB(BIGTREE_SKR_MINI_E3)
-  #include "stm32/pins_BIGTREE_SKR_MINI_E3.h"   // STM32F1                                env:STM32F103RC_bigtree
+  #include "stm32/pins_BIGTREE_SKR_MINI_E3.h"   // STM32F1                                env:STM32F103RC_bigtree env:STM32F103RC_bigtree_512K env:STM32F103RC_bigtree_NOUSB env:STM32F103RC_bigtree_512K_NOUSB
 #elif MB(BIGTREE_SKR_E3_DIP)
-  #include "stm32/pins_BIGTREE_SKR_E3_DIP.h"    // STM32F1                                env:STM32F103RC_bigtree
+  #include "stm32/pins_BIGTREE_SKR_E3_DIP.h"    // STM32F1                                env:STM32F103RC_bigtree env:STM32F103RC_bigtree_512K env:STM32F103RC_bigtree_NOUSB env:STM32F103RC_bigtree_512K_NOUSB
 #elif MB(JGAURORA_A5S_A1)
   #include "stm32/pins_JGAURORA_A5S_A1.h"       // STM32F1                                env:jgaurora_a5s_a1
 #elif MB(FYSETC_AIO_II)
diff --git a/Marlin/src/pins/stm32/pins_BIGTREE_SKR_E3_DIP.h b/Marlin/src/pins/stm32/pins_BIGTREE_SKR_E3_DIP.h
index 5f099f9897..aa177765ca 100644
--- a/Marlin/src/pins/stm32/pins_BIGTREE_SKR_E3_DIP.h
+++ b/Marlin/src/pins/stm32/pins_BIGTREE_SKR_E3_DIP.h
@@ -33,15 +33,9 @@
 // Ignore temp readings during development.
 //#define BOGUS_TEMPERATURE_GRACE_PERIOD 2000
 
-#ifdef MCU_STM32F103RE
-  #define STM32_FLASH_SIZE (512 * 1024)
-#else
-  #define STM32_FLASH_SIZE (256 * 1024)
-#endif
-
 #define FLASH_EEPROM_EMULATION
 #define EEPROM_PAGE_SIZE     uint16(0x800) // 2KB
-#define EEPROM_START_ADDRESS uint32(0x8000000 + STM32_FLASH_SIZE - 2 * EEPROM_PAGE_SIZE)
+#define EEPROM_START_ADDRESS uint32(0x8000000 + (STM32_FLASH_SIZE) * 1024 - 2 * EEPROM_PAGE_SIZE)
 #undef E2END
 #define E2END                (EEPROM_PAGE_SIZE - 1) // 2KB
 
diff --git a/Marlin/src/pins/stm32/pins_BIGTREE_SKR_MINI_E3.h b/Marlin/src/pins/stm32/pins_BIGTREE_SKR_MINI_E3.h
index 7f0698483e..e39a8ed68e 100644
--- a/Marlin/src/pins/stm32/pins_BIGTREE_SKR_MINI_E3.h
+++ b/Marlin/src/pins/stm32/pins_BIGTREE_SKR_MINI_E3.h
@@ -33,15 +33,9 @@
 // Ignore temp readings during development.
 //#define BOGUS_TEMPERATURE_GRACE_PERIOD 2000
 
-#ifdef MCU_STM32F103RE
-  #define STM32_FLASH_SIZE (512 * 1024)
-#else
-  #define STM32_FLASH_SIZE (256 * 1024)
-#endif
-
 #define FLASH_EEPROM_EMULATION
 #define EEPROM_PAGE_SIZE     uint16(0x800) // 2KB
-#define EEPROM_START_ADDRESS uint32(0x8000000 + STM32_FLASH_SIZE - 2 * EEPROM_PAGE_SIZE)
+#define EEPROM_START_ADDRESS uint32(0x8000000 + (STM32_FLASH_SIZE) * 1024 - 2 * EEPROM_PAGE_SIZE)
 #undef E2END
 #define E2END                (EEPROM_PAGE_SIZE - 1) // 2KB
 
diff --git a/Marlin/src/pins/stm32/pins_BIGTREE_SKR_MINI_V1_1.h b/Marlin/src/pins/stm32/pins_BIGTREE_SKR_MINI_V1_1.h
index 0041d60e20..4525fb1a86 100644
--- a/Marlin/src/pins/stm32/pins_BIGTREE_SKR_MINI_V1_1.h
+++ b/Marlin/src/pins/stm32/pins_BIGTREE_SKR_MINI_V1_1.h
@@ -33,15 +33,9 @@
 // Ignore temp readings during development.
 //#define BOGUS_TEMPERATURE_GRACE_PERIOD 2000
 
-#ifdef MCU_STM32F103RE
-  #define STM32_FLASH_SIZE (512 * 1024)
-#else
-  #define STM32_FLASH_SIZE (256 * 1024)
-#endif
-
 #define FLASH_EEPROM_EMULATION
 #define EEPROM_PAGE_SIZE     (0x800) // 2KB
-#define EEPROM_START_ADDRESS (0x8000000 + STM32_FLASH_SIZE - 2 * EEPROM_PAGE_SIZE)
+#define EEPROM_START_ADDRESS uint32(0x8000000 + (STM32_FLASH_SIZE) * 1024 - 2 * EEPROM_PAGE_SIZE)
 #define E2END                (EEPROM_PAGE_SIZE - 1)
 
 //
diff --git a/Marlin/src/pins/stm32/pins_BTT_SKR_MINI_E3_V1_2.h b/Marlin/src/pins/stm32/pins_BTT_SKR_MINI_E3_V1_2.h
index 55bed85644..94b4152a69 100644
--- a/Marlin/src/pins/stm32/pins_BTT_SKR_MINI_E3_V1_2.h
+++ b/Marlin/src/pins/stm32/pins_BTT_SKR_MINI_E3_V1_2.h
@@ -35,7 +35,7 @@
 
 #define FLASH_EEPROM_EMULATION
 #define EEPROM_PAGE_SIZE     uint16(0x800) // 2KB
-#define EEPROM_START_ADDRESS uint32(0x8000000 + 256 * 1024 - 2 * EEPROM_PAGE_SIZE)
+#define EEPROM_START_ADDRESS uint32(0x8000000 + (STM32_FLASH_SIZE) * 1024 - 2 * EEPROM_PAGE_SIZE)
 #undef E2END
 #define E2END                (EEPROM_PAGE_SIZE - 1) // 2KB
 
diff --git a/buildroot/share/PlatformIO/ldscripts/STM32F103RC_SKR_MINI.ld b/buildroot/share/PlatformIO/ldscripts/STM32F103RC_SKR_MINI_256K.ld
similarity index 100%
rename from buildroot/share/PlatformIO/ldscripts/STM32F103RC_SKR_MINI.ld
rename to buildroot/share/PlatformIO/ldscripts/STM32F103RC_SKR_MINI_256K.ld
diff --git a/buildroot/share/PlatformIO/ldscripts/STM32F103RC_SKR_MINI_512K.ld b/buildroot/share/PlatformIO/ldscripts/STM32F103RC_SKR_MINI_512K.ld
new file mode 100644
index 0000000000..feb6c0d3ba
--- /dev/null
+++ b/buildroot/share/PlatformIO/ldscripts/STM32F103RC_SKR_MINI_512K.ld
@@ -0,0 +1,14 @@
+MEMORY
+{
+  ram (rwx) : ORIGIN = 0x20000000, LENGTH = 48K - 40
+  rom (rx)  : ORIGIN = 0x08007000, LENGTH = 512K - 28K
+}
+
+/* Provide memory region aliases for common.inc */
+REGION_ALIAS("REGION_TEXT", rom);
+REGION_ALIAS("REGION_DATA", ram);
+REGION_ALIAS("REGION_BSS", ram);
+REGION_ALIAS("REGION_RODATA", rom);
+
+/* Let common.inc handle the real work. */
+INCLUDE common.inc
diff --git a/buildroot/share/PlatformIO/scripts/STM32F103RC_SKR_MINI.py b/buildroot/share/PlatformIO/scripts/STM32F103RC_SKR_MINI.py
index b167b85d8d..497a035fdf 100644
--- a/buildroot/share/PlatformIO/scripts/STM32F103RC_SKR_MINI.py
+++ b/buildroot/share/PlatformIO/scripts/STM32F103RC_SKR_MINI.py
@@ -1,13 +1,18 @@
 import os
 Import("env")
 
-# Relocate firmware from 0x08000000 to 0x08007000
+STM32_FLASH_SIZE = 256
+
 for define in env['CPPDEFINES']:
     if define[0] == "VECT_TAB_ADDR":
         env['CPPDEFINES'].remove(define)
+    if define[0] == "STM32_FLASH_SIZE":
+        STM32_FLASH_SIZE = define[1]
+
+# Relocate firmware from 0x08000000 to 0x08007000
 env['CPPDEFINES'].append(("VECT_TAB_ADDR", "0x08007000"))
 
-custom_ld_script = os.path.abspath("buildroot/share/PlatformIO/ldscripts/STM32F103RC_SKR_MINI.ld")
+custom_ld_script = os.path.abspath("buildroot/share/PlatformIO/ldscripts/STM32F103RC_SKR_MINI_" + str(STM32_FLASH_SIZE) + "K.ld")
 for i, flag in enumerate(env["LINKFLAGS"]):
     if "-Wl,-T" in flag:
         env["LINKFLAGS"][i] = "-Wl,-T" + custom_ld_script
diff --git a/buildroot/share/atom/auto_build.py b/buildroot/share/atom/auto_build.py
index 3586676619..001317103f 100644
--- a/buildroot/share/atom/auto_build.py
+++ b/buildroot/share/atom/auto_build.py
@@ -75,10 +75,10 @@ from __future__ import division
 import sys
 import os
 
-pwd = os.getcwd()    # make sure we're executing from the correct directory level
+pwd = os.getcwd()  # make sure we're executing from the correct directory level
 pwd = pwd.replace('\\', '/')
 if 0 <= pwd.find('buildroot/share/atom'):
-  pwd = pwd[ : pwd.find('buildroot/share/atom')]
+  pwd = pwd[:pwd.find('buildroot/share/atom')]
   os.chdir(pwd)
 print('pwd: ', pwd)
 
@@ -93,7 +93,7 @@ print('build_type:  ', build_type)
 
 print('\nWorking\n')
 
-python_ver = sys.version_info[0] # major version - 2 or 3
+python_ver = sys.version_info[0]  # major version - 2 or 3
 
 print("python version " + str(sys.version_info[0]) + "." + str(sys.version_info[1]) + "." + str(sys.version_info[2]))
 
@@ -118,8 +118,6 @@ from datetime import datetime, date, time
 #    reboot
 #########
 
-
-
 ##########################################################################################
 #
 # popup to get input from user
@@ -128,79 +126,83 @@ from datetime import datetime, date, time
 
 def get_answer(board_name, cpu_label_txt, cpu_a_txt, cpu_b_txt):
 
+  if python_ver == 2:
+    import Tkinter as tk
+  else:
+    import tkinter as tk
 
-        if python_ver == 2:
-            import Tkinter as tk
-        else:
-            import tkinter as tk
-
-        def CPU_exit_3():   # forward declare functions
-
-          CPU_exit_3_()
-        def CPU_exit_4():
-
-          CPU_exit_4_()
-        def kill_session():
-          kill_session_()
+  def CPU_exit_3():  # forward declare functions
+    CPU_exit_3_()
 
-        root_get_answer = tk.Tk()
-        root_get_answer.attributes("-topmost", True)
+  def got_answer():
+    got_answer_()
 
-        root_get_answer.chk_state_1 = 1   # declare variables used by TK and enable
+  def kill_session():
+    kill_session_()
 
-        chk_state_1 = 0   # set initial state of check boxes
+  root_get_answer = tk.Tk()
+  root_get_answer.title('')
+  root_get_answer.withdraw()
+  root_get_answer.deiconify()
 
+  def disable_event():
+    pass
 
-        global get_answer_val
-        get_answer_val = 2       # return get_answer_val, set default to match chk_state_1 default
+  root_get_answer.protocol("WM_DELETE_WINDOW", disable_event)
+  root_get_answer.resizable(False, False)
 
+  root_get_answer.radio_state = 1  # declare variables used by TK and enable
 
-        l1 = tk.Label(text=board_name,
-              fg = "light green",
-              bg = "dark green",
-              font = "Helvetica 12 bold").grid(row=1)
+  global get_answer_val
+  get_answer_val = 2  # return get_answer_val, set default to match radio_state default
 
-        l2 = tk.Label(text=cpu_label_txt,
-              fg = "light green",
-              bg = "dark green",
-              font = "Helvetica 16 bold italic").grid(row=2)
+  radio_state = tk.IntVar()
+  radio_state.set(get_answer_val)
 
-        b4 = tk.Checkbutton(text=cpu_a_txt,
-              fg = "black",
-              font = "Times 20 bold ",
-                variable=chk_state_1, onvalue=1, offvalue=0,
+  l1 = tk.Label(text=board_name, fg="light green", bg="dark green",
+                font="default 14 bold").grid(row=0, columnspan=2, sticky='EW', ipadx=2, ipady=2)
 
-                command = CPU_exit_3).grid(row=3)
+  l2 = tk.Label(text=cpu_label_txt).grid(row=1, pady=4, columnspan=2, sticky='EW')
 
-        b5 = tk.Checkbutton(text=cpu_b_txt,
-              fg = "black",
-              font = "Times 20 bold ",
-                variable=chk_state_1, onvalue=0, offvalue=1,
+  b4 = tk.Radiobutton(
+    text=cpu_a_txt,
+    fg="black",
+    bg="lightgray",
+    relief=tk.SUNKEN,
+    selectcolor="green",
+    variable=radio_state,
+    value=1,
+    indicatoron=0,
+    command=CPU_exit_3
+  ).grid(row=2, pady=1, ipady=2, ipadx=10, columnspan=2)
 
-                command = CPU_exit_4).grid(row=4)  # use same variable but inverted so they will track
-        b6 = tk.Button(text="CONFIRM",
-              fg = "blue",
-              font = "Times 20 bold ",
-                command = root_get_answer.destroy).grid(row=5, pady=4)
+  b5 = tk.Radiobutton(
+    text=cpu_b_txt,
+    fg="black",
+    bg="lightgray",
+    relief=tk.SUNKEN,
+    selectcolor="green",
+    variable=radio_state,
+    value=2,
+    indicatoron=0,
+    command=CPU_exit_3
+  ).grid(row=3, pady=1, ipady=2, ipadx=10, columnspan=2)  # use same variable but inverted so they will track
 
-        b7 = tk.Button(text="CANCEL",
-              fg = "red",
-              font = "Times 12 bold ",
-                command = kill_session).grid(row=6, pady=4)
+  b6 = tk.Button(text="Cancel", fg="red", command=kill_session).grid(row=4, column=0, padx=4, pady=4, ipadx=2, ipady=2)
 
+  b7 = tk.Button(text="Continue", fg="green", command=got_answer).grid(row=4, column=1, padx=4, pady=4, ipadx=2, ipady=2)
 
-        def CPU_exit_3_():
-                global get_answer_val
-                get_answer_val = 1
+  def got_answer_():
+    root_get_answer.destroy()
 
-        def CPU_exit_4_():
-                global get_answer_val
-                get_answer_val = 2
+  def CPU_exit_3_():
+    global get_answer_val
+    get_answer_val = radio_state.get()
 
-        def kill_session_():
-                raise SystemExit(0)     # kill everything
+  def kill_session_():
+    raise SystemExit(0)  # kill everything
 
-        root_get_answer.mainloop()
+  root_get_answer.mainloop()
 
 # end - get answer
 
@@ -209,102 +211,101 @@ def get_answer(board_name, cpu_label_txt, cpu_a_txt, cpu_b_txt):
 # move custom board definitions from project folder to PlatformIO
 #
 def resolve_path(path):
-        import os
-
-    # turn the selection into a partial path
-
-        if 0 <= path.find('"'):
-          path = path[ path.find('"') : ]
-          if 0 <= path.find(', line '):
-            path = path.replace(', line ', ':')
-          path = path.replace('"', '')
-
-       #get line and column numbers
-        line_num = 1
-        column_num = 1
-        line_start = path.find(':', 2)                  # use 2 here so don't eat Windows full path
-        column_start = path.find(':', line_start + 1)
-        if column_start == -1:
-          column_start = len(path)
-        column_end = path.find(':', column_start + 1)
-        if column_end == -1:
-          column_end = len(path)
-        if 0 <= line_start:
-          line_num = path[ line_start + 1 : column_start]
-          if line_num == '':
-            line_num = 1
-        if column_start != column_end:
-          column_num = path[ column_start + 1 : column_end]
-          if column_num == '':
-            column_num = 0
-
-        index_end = path.find(',')
-        if 0 <= index_end:
-            path = path[ : index_end]  # delete comma and anything after
-        index_end = path.find(':', 2)
-        if 0 <= index_end:
-            path = path[ : path.find(':', 2)]  # delete the line number and anything after
-
-        path = path.replace('\\','/')
-
-        if 1 == path.find(':') and current_OS == 'Windows':
-          return path, line_num, column_num                    # found a full path - no need for further processing
-        elif 0 == path.find('/') and (current_OS == 'Linux' or current_OS == 'Darwin'):
-          return path, line_num, column_num                    # found a full path - no need for further processing
+  import os
+
+  # turn the selection into a partial path
+
+  if 0 <= path.find('"'):
+    path = path[path.find('"'):]
+    if 0 <= path.find(', line '):
+      path = path.replace(', line ', ':')
+    path = path.replace('"', '')
+
+  # get line and column numbers
+  line_num = 1
+  column_num = 1
+  line_start = path.find(':', 2)  # use 2 here so don't eat Windows full path
+  column_start = path.find(':', line_start + 1)
+  if column_start == -1:
+    column_start = len(path)
+  column_end = path.find(':', column_start + 1)
+  if column_end == -1:
+    column_end = len(path)
+  if 0 <= line_start:
+    line_num = path[line_start + 1:column_start]
+    if line_num == '':
+      line_num = 1
+  if column_start != column_end:
+    column_num = path[column_start + 1:column_end]
+    if column_num == '':
+      column_num = 0
+
+  index_end = path.find(',')
+  if 0 <= index_end:
+    path = path[:index_end]  # delete comma and anything after
+  index_end = path.find(':', 2)
+  if 0 <= index_end:
+    path = path[:path.find(':', 2)]  # delete the line number and anything after
+
+  path = path.replace('\\', '/')
+
+  if 1 == path.find(':') and current_OS == 'Windows':
+    return path, line_num, column_num  # found a full path - no need for further processing
+  elif 0 == path.find('/') and (current_OS == 'Linux' or current_OS == 'Darwin'):
+    return path, line_num, column_num  # found a full path - no need for further processing
+
+  else:
+
+    # resolve as many '../' as we can
+    while 0 <= path.find('../'):
+      end = path.find('../') - 1
+      start = path.find('/')
+      while 0 <= path.find('/', start) and end > path.find('/', start):
+        start = path.find('/', start) + 1
+      path = path[0:start] + path[end + 4:]
+
+  # this is an alternative to the above - it just deletes the '../' section
+  # start_temp = path.find('../')
+  # while 0 <= path.find('../',start_temp):
+  #   start = path.find('../',start_temp)
+  #   start_temp = start  + 1
+  # if 0 <= start:
+  #   path = path[start + 2 : ]
+
+    start = path.find('/')
+    if start != 0:  # make sure path starts with '/'
+      while 0 == path.find(' '):  # eat any spaces at the beginning
+        path = path[1:]
+      path = '/' + path
+
+    if current_OS == 'Windows':
+      search_path = path.replace('/', '\\')  # os.walk uses '\' in Windows
+    else:
+      search_path = path
 
-        else:
+    start_path = os.path.abspath('')
+
+    # search project directory for the selection
+    found = False
+    full_path = ''
+    for root, directories, filenames in os.walk(start_path):
+      for filename in filenames:
+        if 0 <= root.find('.git'):  # don't bother looking in this directory
+          break
+        full_path = os.path.join(root, filename)
+        if 0 <= full_path.find(search_path):
+          found = True
+          break
+      if found:
+        break
 
-          # resolve as many '../' as we can
-            while 0 <= path.find('../'):
-              end =  path.find('../') - 1
-              start = path.find('/')
-              while 0 <= path.find('/',start) and end > path.find('/',start):
-                start = path.find('/',start) + 1
-              path = path[0:start] + path[end + 4: ]
-
-            # this is an alternative to the above - it just deletes the '../' section
-            # start_temp = path.find('../')
-            # while 0 <= path.find('../',start_temp):
-            #   start = path.find('../',start_temp)
-            #   start_temp = start  + 1
-            # if 0 <= start:
-            #   path = path[start + 2 : ]
-
-
-            start = path.find('/')
-            if start != 0:                  # make sure path starts with '/'
-              while 0 == path.find(' '):    # eat any spaces at the beginning
-                path = path[ 1 : ]
-              path = '/' + path
-
-            if current_OS == 'Windows':
-              search_path = path.replace('/', '\\')  # os.walk uses '\' in Windows
-            else:
-              search_path = path
-
-            start_path = os.path.abspath('')
-
-        # search project directory for the selection
-            found = False
-            full_path = ''
-            for root, directories, filenames in os.walk(start_path):
-              for filename in filenames:
-                      if  0 <= root.find('.git'):              # don't bother looking in this directory
-                        break
-                      full_path = os.path.join(root,filename)
-                      if 0 <= full_path.find(search_path):
-                        found = True
-                        break
-              if found:
-                break
-
-            return full_path, line_num, column_num
+    return full_path, line_num, column_num
 
 # end - resolve_path
 
 
 #
-# Opens the file in the preferred editor at the line & column number
+# Open the file in the preferred editor at the line & column number
 #   If the preferred editor isn't already running then it tries the next.
 #   If none are open then the system default is used.
 #
@@ -315,317 +316,331 @@ def resolve_path(path):
 #   4. System default (opens at line 1, column 1 only)
 #
 def open_file(path):
-        import subprocess
-        file_path, line_num, column_num = resolve_path(path)
-
-        if file_path == '' :
-          return
-
-        if current_OS == 'Windows':
-
-            editor_note = subprocess.check_output('wmic process where "name=' + "'notepad++.exe'" + '" get ExecutablePath')
-            editor_sublime = subprocess.check_output('wmic process where "name=' + "'sublime_text.exe'" + '" get ExecutablePath')
-            editor_atom = subprocess.check_output('wmic process where "name=' + "'atom.exe'" + '" get ExecutablePath')
-
-            if 0 <= editor_note.find('notepad++.exe'):
-                start = editor_note.find('\n') + 1
-                end = editor_note.find('\n',start + 5) -4
-                editor_note = editor_note[ start : end]
-                command = file_path ,  ' -n' + str(line_num) ,   ' -c' + str(column_num)
-                subprocess.Popen([editor_note, command])
-
-            elif 0 <= editor_sublime.find('sublime_text.exe'):
-                start = editor_sublime.find('\n') + 1
-                end = editor_sublime.find('\n',start + 5) -4
-                editor_sublime = editor_sublime[ start : end]
-                command = file_path + ':' + line_num + ':' + column_num
-                subprocess.Popen([editor_sublime, command])
-
-            elif 0 <= editor_atom.find('atom.exe'):
-                start = editor_atom.find('\n') + 1
-                end = editor_atom.find('\n',start + 5) -4
-                editor_atom = editor_atom[ start : end]
-                command = file_path  + ':' + str(line_num) + ':' + str(column_num)
-                subprocess.Popen([editor_atom, command])
-
-            else:
-              os.startfile(resolve_path(path))  # open file with default app
-
-        elif current_OS == 'Linux':
-
-              command = file_path  + ':' + str(line_num) + ':' + str(column_num)
-              index_end = command.find(',')
-              if 0 <= index_end:
-                  command = command[ : index_end]  # sometimes a comma magically appears, don't want it
-              running_apps = subprocess.Popen('ps ax -o cmd', stdout=subprocess.PIPE, shell=True)
-              (output, err) = running_apps.communicate()
-              temp = output.split('\n')
-
-              def find_editor_linux(name, search_obj):
-                  for line in search_obj:
-                      if 0 <= line.find(name):
-                          path = line
-                          return True, path
-                  return False , ''
-
-              (success_sublime, editor_path_sublime) = find_editor_linux('sublime_text',temp)
-              (success_atom, editor_path_atom) = find_editor_linux('atom',temp)
-
-              if success_sublime:
-                  subprocess.Popen([editor_path_sublime, command])
-
-              elif success_atom:
-                  subprocess.Popen([editor_path_atom, command])
-
-              else:
-                  os.system('xdg-open ' + file_path )
-
-        elif current_OS == 'Darwin':  # MAC
-
-              command = file_path  + ':' + str(line_num) + ':' + str(column_num)
-              index_end = command.find(',')
-              if 0 <= index_end:
-                  command = command[ : index_end]  # sometimes a comma magically appears, don't want it
-              running_apps = subprocess.Popen('ps axwww -o command', stdout=subprocess.PIPE, shell=True)
-              (output, err) = running_apps.communicate()
-              temp = output.split('\n')
-
-              def find_editor_mac(name, search_obj):
-                  for line in search_obj:
-                      if 0 <= line.find(name):
-                          path = line
-                          if 0 <= path.find('-psn'):
-                              path = path[ : path.find('-psn') - 1 ]
-                          return True, path
-                  return False , ''
-
-              (success_sublime, editor_path_sublime) = find_editor_mac('Sublime',temp)
-              (success_atom, editor_path_atom) = find_editor_mac('Atom',temp)
-
-              if success_sublime:
-                  subprocess.Popen([editor_path_sublime, command])
-
-              elif success_atom:
-                  subprocess.Popen([editor_path_atom, command])
-
-              else:
-                  os.system('open ' + file_path )
+  import subprocess
+  file_path, line_num, column_num = resolve_path(path)
+
+  if file_path == '':
+    return
+
+  if current_OS == 'Windows':
+
+    editor_note = subprocess.check_output('wmic process where "name=' + "'notepad++.exe'" + '" get ExecutablePath')
+    editor_sublime = subprocess.check_output('wmic process where "name=' + "'sublime_text.exe'" + '" get ExecutablePath')
+    editor_atom = subprocess.check_output('wmic process where "name=' + "'atom.exe'" + '" get ExecutablePath')
+
+    if 0 <= editor_note.find('notepad++.exe'):
+      start = editor_note.find('\n') + 1
+      end = editor_note.find('\n', start + 5) - 4
+      editor_note = editor_note[start:end]
+      command = file_path, ' -n' + str(line_num), ' -c' + str(column_num)
+      subprocess.Popen([editor_note, command])
+
+    elif 0 <= editor_sublime.find('sublime_text.exe'):
+      start = editor_sublime.find('\n') + 1
+      end = editor_sublime.find('\n', start + 5) - 4
+      editor_sublime = editor_sublime[start:end]
+      command = file_path + ':' + line_num + ':' + column_num
+      subprocess.Popen([editor_sublime, command])
+
+    elif 0 <= editor_atom.find('atom.exe'):
+      start = editor_atom.find('\n') + 1
+      end = editor_atom.find('\n', start + 5) - 4
+      editor_atom = editor_atom[start:end]
+      command = file_path + ':' + str(line_num) + ':' + str(column_num)
+      subprocess.Popen([editor_atom, command])
+
+    else:
+      os.startfile(resolve_path(path))  # open file with default app
+
+  elif current_OS == 'Linux':
+
+    command = file_path + ':' + str(line_num) + ':' + str(column_num)
+    index_end = command.find(',')
+    if 0 <= index_end:
+      command = command[:index_end]  # sometimes a comma magically appears, don't want it
+    running_apps = subprocess.Popen('ps ax -o cmd', stdout=subprocess.PIPE, shell=True)
+    (output, err) = running_apps.communicate()
+    temp = output.split('\n')
+
+    def find_editor_linux(name, search_obj):
+      for line in search_obj:
+        if 0 <= line.find(name):
+          path = line
+          return True, path
+      return False, ''
+
+    (success_sublime, editor_path_sublime) = find_editor_linux('sublime_text', temp)
+    (success_atom, editor_path_atom) = find_editor_linux('atom', temp)
+
+    if success_sublime:
+      subprocess.Popen([editor_path_sublime, command])
+
+    elif success_atom:
+      subprocess.Popen([editor_path_atom, command])
+
+    else:
+      os.system('xdg-open ' + file_path)
+
+  elif current_OS == 'Darwin':  # MAC
+
+    command = file_path + ':' + str(line_num) + ':' + str(column_num)
+    index_end = command.find(',')
+    if 0 <= index_end:
+      command = command[:index_end]  # sometimes a comma magically appears, don't want it
+    running_apps = subprocess.Popen('ps axwww -o command', stdout=subprocess.PIPE, shell=True)
+    (output, err) = running_apps.communicate()
+    temp = output.split('\n')
+
+    def find_editor_mac(name, search_obj):
+      for line in search_obj:
+        if 0 <= line.find(name):
+          path = line
+          if 0 <= path.find('-psn'):
+            path = path[:path.find('-psn') - 1]
+          return True, path
+      return False, ''
+
+    (success_sublime, editor_path_sublime) = find_editor_mac('Sublime', temp)
+    (success_atom, editor_path_atom) = find_editor_mac('Atom', temp)
+
+    if success_sublime:
+      subprocess.Popen([editor_path_sublime, command])
+
+    elif success_atom:
+      subprocess.Popen([editor_path_atom, command])
+
+    else:
+      os.system('open ' + file_path)
+
 # end - open_file
 
 
-# gets the last build environment
+# Get the last build environment
 def get_build_last():
-      env_last = ''
-      DIR_PWD = os.listdir('.')
-      if '.pio' in DIR_PWD:
-        date_last = 0.0
-        DIR__pioenvs = os.listdir('.pio')
-        for name in DIR__pioenvs:
-          if 0 <= name.find('.') or 0 <= name.find('-'):   # skip files in listing
-            continue
-          DIR_temp = os.listdir('.pio/build/' + name)
-          for names_temp in DIR_temp:
-
-            if 0 == names_temp.find('firmware.'):
-              date_temp = os.path.getmtime('.pio/build/' + name + '/' + names_temp)
-              if date_temp > date_last:
-                date_last = date_temp
-                env_last = name
-      return env_last
-
-
-# gets the board being built from the Configuration.h file
-#   returns: board name, major version of Marlin being used (1 or 2)
+  env_last = ''
+  DIR_PWD = os.listdir('.')
+  if '.pio' in DIR_PWD:
+    date_last = 0.0
+    DIR__pioenvs = os.listdir('.pio')
+    for name in DIR__pioenvs:
+      if 0 <= name.find('.') or 0 <= name.find('-'):  # skip files in listing
+        continue
+      DIR_temp = os.listdir('.pio/build/' + name)
+      for names_temp in DIR_temp:
+
+        if 0 == names_temp.find('firmware.'):
+          date_temp = os.path.getmtime('.pio/build/' + name + '/' + names_temp)
+          if date_temp > date_last:
+            date_last = date_temp
+            env_last = name
+  return env_last
+
+
+# Get the board being built from the Configuration.h file
+#   return: board name, major version of Marlin being used (1 or 2)
 def get_board_name():
-      board_name = ''
-      # get board name
-
-      with open('Marlin/Configuration.h', 'r') as myfile:
-        Configuration_h = myfile.read()
-
-      Configuration_h = Configuration_h.split('\n')
-      Marlin_ver = 0  # set version to invalid number
-      for lines in Configuration_h:
-        if 0 == lines.find('#define CONFIGURATION_H_VERSION 01'):
-          Marlin_ver = 1
-        if 0 == lines.find('#define CONFIGURATION_H_VERSION 02'):
-          Marlin_ver = 2
-        board = lines.find(' BOARD_') + 1
-        motherboard = lines.find(' MOTHERBOARD ') + 1
-        define = lines.find('#define ')
-        comment = lines.find('//')
-        if (comment == -1 or comment > board) and \
-          board > motherboard and \
-          motherboard > define and \
-          define >= 0 :
-          spaces = lines.find(' ', board)  # find the end of the board substring
-          if spaces == -1:
-            board_name = lines[board : ]
-          else:
-            board_name = lines[board : spaces]
-          break
-
+  board_name = ''
+  # get board name
+
+  with open('Marlin/Configuration.h', 'r') as myfile:
+    Configuration_h = myfile.read()
+
+  Configuration_h = Configuration_h.split('\n')
+  Marlin_ver = 0  # set version to invalid number
+  for lines in Configuration_h:
+    if 0 == lines.find('#define CONFIGURATION_H_VERSION 01'):
+      Marlin_ver = 1
+    if 0 == lines.find('#define CONFIGURATION_H_VERSION 02'):
+      Marlin_ver = 2
+    board = lines.find(' BOARD_') + 1
+    motherboard = lines.find(' MOTHERBOARD ') + 1
+    define = lines.find('#define ')
+    comment = lines.find('//')
+    if (comment == -1 or comment > board) and \
+      board > motherboard and \
+      motherboard > define and \
+      define >= 0 :
+      spaces = lines.find(' ', board)  # find the end of the board substring
+      if spaces == -1:
+        board_name = lines[board:]
+      else:
+        board_name = lines[board:spaces]
+      break
 
-      return board_name, Marlin_ver
+  return board_name, Marlin_ver
 
 
-# extract first environment name it finds after the start position
-#   returns: environment name and position to start the next search from
+# extract first environment name found after the start position
+#   return: environment name and position to start the next search from
 def get_env_from_line(line, start_position):
-      env = ''
-      next_position = -1
-      env_position = line.find('env:', start_position)
-      if 0 < env_position:
-        next_position = line.find(' ', env_position + 4)
-        if 0 < next_position:
-          env = line[env_position + 4 : next_position]
-        else:
-          env = line[env_position + 4 :              ]    # at the end of the line
-      return env, next_position
-
+  env = ''
+  next_position = -1
+  env_position = line.find('env:', start_position)
+  if 0 < env_position:
+    next_position = line.find(' ', env_position + 4)
+    if 0 < next_position:
+      env = line[env_position + 4:next_position]
+    else:
+      env = line[env_position + 4:]  # at the end of the line
+  return env, next_position
 
 
-#scans pins.h for board name and returns the environment(s) it finds
+# scan pins.h for board name and return the environment(s) found
 def get_starting_env(board_name_full, version):
-      # get environment starting point
-
-      if version == 1:
-        path = 'Marlin/pins.h'
-      if version == 2:
-        path = 'Marlin/src/pins/pins.h'
-      with open(path, 'r') as myfile:
-        pins_h = myfile.read()
-
-      env_A = ''
-      env_B = ''
-      env_C = ''
-
-      board_name = board_name_full[ 6 : ]  # only use the part after "BOARD_" since we're searching the pins.h file
-      pins_h = pins_h.split('\n')
-      environment = ''
-      board_line = ''
-      cpu_A = ''
-      cpu_B = ''
-      i = 0
-      list_start_found = False
-      for lines in pins_h:
-        i = i + 1   # i is always one ahead of the index into pins_h
-        if 0 < lines.find("Unknown MOTHERBOARD value set in Configuration.h"):
-          break   #  no more
-        if 0 < lines.find('1280'):
-          list_start_found = True
-        if list_start_found == False:  # skip lines until find start of CPU list
-          continue
-        board = lines.find(board_name)
-        comment_start = lines.find('// ')
-        cpu_A_loc = comment_start
-        cpu_B_loc = 0
-        if board > 0:  # need to look at the next line for environment info
-          cpu_line = pins_h[i]
-          comment_start = cpu_line.find('// ')
-          env_A, next_position = get_env_from_line(cpu_line, comment_start)  # get name of environment & start of search for next
-          env_B, next_position = get_env_from_line(cpu_line, next_position)  # get next environment, if it exists
-          env_C, next_position = get_env_from_line(cpu_line, next_position)  # get next environment, if it exists
-          break
-      return env_A, env_B, env_C
-
-
-# scans input string for CPUs that the users may need to select from
-#   returns: CPU name
+  # get environment starting point
+
+  if version == 1:
+    path = 'Marlin/pins.h'
+  if version == 2:
+    path = 'Marlin/src/pins/pins.h'
+  with open(path, 'r') as myfile:
+    pins_h = myfile.read()
+
+  env_A = ''
+  env_B = ''
+  env_C = ''
+
+  board_name = board_name_full[6:]  # only use the part after "BOARD_" since we're searching the pins.h file
+  pins_h = pins_h.split('\n')
+  environment = ''
+  board_line = ''
+  cpu_A = ''
+  cpu_B = ''
+  i = 0
+  list_start_found = False
+  for lines in pins_h:
+    i = i + 1  # i is always one ahead of the index into pins_h
+    if 0 < lines.find("Unknown MOTHERBOARD value set in Configuration.h"):
+      break  #  no more
+    if 0 < lines.find('1280'):
+      list_start_found = True
+    if list_start_found == False:  # skip lines until find start of CPU list
+      continue
+    board = lines.find(board_name)
+    comment_start = lines.find('// ')
+    cpu_A_loc = comment_start
+    cpu_B_loc = 0
+    if board > 0:  # need to look at the next line for environment info
+      cpu_line = pins_h[i]
+      comment_start = cpu_line.find('// ')
+      env_A, next_position = get_env_from_line(cpu_line, comment_start)  # get name of environment & start of search for next
+      env_B, next_position = get_env_from_line(cpu_line, next_position)  # get next environment, if it exists
+      env_C, next_position = get_env_from_line(cpu_line, next_position)  # get next environment, if it exists
+      break
+  return env_A, env_B, env_C
+
+
+# Scan input string for CPUs that users may need to select from
+#   return: CPU name
 def get_CPU_name(environment):
-          CPU_list = ('1280', '2560','644', '1284', 'LPC1768', 'DUE')
-          CPU_name = ''
-          for CPU in CPU_list:
-            if 0 < environment.find(CPU):
-              return CPU
+  CPU_list = ('1280', '2560', '644', '1284', 'LPC1768', 'DUE')
+  CPU_name = ''
+  for CPU in CPU_list:
+    if 0 < environment.find(CPU):
+      return CPU
 
 
 # get environment to be used for the build
-#  returns: environment
+#  return: environment
 def get_env(board_name, ver_Marlin):
-      def no_environment():
-            print('ERROR - no environment for this board')
-            print(board_name)
-            raise SystemExit(0)                          # no environment so quit
 
-      def invalid_board():
-            print('ERROR - invalid board')
-            print(board_name)
-            raise SystemExit(0)                          # quit if unable to find board
+  def no_environment():
+    print('ERROR - no environment for this board')
+    print(board_name)
+    raise SystemExit(0)  # no environment so quit
+
+  def invalid_board():
+    print('ERROR - invalid board')
+    print(board_name)
+    raise SystemExit(0)  # quit if unable to find board
 
+  CPU_question = (('1280', '2560', '1280 or 2560 CPU?'), ('644', '1284', '644 or 1284 CPU?'))
 
-      CPU_question = ( ('1280', '2560', " 1280 or 2560 CPU? "), ('644', '1284', " 644 or 1284 CPU? ") )
+  if 0 < board_name.find('MELZI'):
+    get_answer(
+      board_name, " Which flavor of Melzi? ", "Melzi (Optiboot bootloader)", "Melzi                                      "
+    )
+    if 1 == get_answer_val:
+      target_env = 'melzi_optiboot'
+    else:
+      target_env = 'melzi'
+  else:
+    env_A, env_B, env_C = get_starting_env(board_name, ver_Marlin)
+
+    if env_A == '':
+      no_environment()
+    if env_B == '':
+      return env_A  # only one environment so finished
+
+    CPU_A = get_CPU_name(env_A)
+    CPU_B = get_CPU_name(env_B)
+
+    for item in CPU_question:
+      if CPU_A == item[0]:
+        get_answer(board_name, item[2], item[0], item[1])
+        if 2 == get_answer_val:
+          target_env = env_B
+        else:
+          target_env = env_A
+        return target_env
 
-      if 0 < board_name.find('MELZI') :
-          get_answer(' ' + board_name + ' ', " Which flavor of Melzi? ", "Melzi (Optiboot bootloader)", "Melzi                                      ")
-          if 1 == get_answer_val:
-            target_env = 'melzi_optiboot'
-          else:
-            target_env = 'melzi'
+    if env_A == 'LPC1768':
+      if build_type == 'traceback' or (build_type == 'clean' and get_build_last() == 'LPC1768_debug_and_upload'):
+        target_env = 'LPC1768_debug_and_upload'
       else:
-          env_A, env_B, env_C = get_starting_env(board_name, ver_Marlin)
-
-          if env_A == '':
-            no_environment()
-          if env_B == '':
-            return env_A      # only one environment so finished
-
-          CPU_A = get_CPU_name(env_A)
-          CPU_B = get_CPU_name(env_B)
-
-          for item in CPU_question:
-            if CPU_A == item[0]:
-              get_answer(' ' + board_name + ' ', item[2], item[0], item[1])
-              if 2 == get_answer_val:
-                target_env = env_B
-              else:
-                target_env = env_A
-              return target_env
-
-          if env_A == 'LPC1768':
-              if build_type == 'traceback' or (build_type == 'clean' and get_build_last() == 'LPC1768_debug_and_upload'):
-                target_env = 'LPC1768_debug_and_upload'
-              else:
-                target_env = 'LPC1768'
-          elif env_A == 'DUE':
-              target_env = 'DUE'
-              if build_type == 'traceback' or (build_type == 'clean' and get_build_last() == 'DUE_debug'):
-                  target_env = 'DUE_debug'
-              elif env_B == 'DUE_USB':
-                get_answer(' ' + board_name + ' ', " DUE: need download port ", "USB (native USB) port", "Programming port       ")
-                if 1 == get_answer_val:
-                  target_env = 'DUE_USB'
-                else:
-                  target_env = 'DUE'
-          else:
-              invalid_board()
-
-      if build_type == 'traceback' and target_env != 'LPC1768_debug_and_upload' and target_env != 'DUE_debug' and Marlin_ver == 2:
-          print("ERROR - this board isn't setup for traceback")
-          print('board_name: ', board_name)
-          print('target_env: ', target_env)
-          raise SystemExit(0)
-
-      return target_env
+        target_env = 'LPC1768'
+    elif env_A == 'DUE':
+      target_env = 'DUE'
+      if build_type == 'traceback' or (build_type == 'clean' and get_build_last() == 'DUE_debug'):
+        target_env = 'DUE_debug'
+      elif env_B == 'DUE_USB':
+        get_answer(board_name, 'DUE Download Port?', '(Native) USB port', 'Programming port')
+        if 1 == get_answer_val:
+          target_env = 'DUE_USB'
+        else:
+          target_env = 'DUE'
+    elif env_A == 'STM32F103RC_bigtree':
+      get_answer(board_name, 'RCT6 Flash Size?', '512K', '256K')
+      if 1 == get_answer_val:
+        target_env = 'STM32F103RC_bigtree_512K'
+      else:
+        target_env = 'STM32F103RC_bigtree'
+      get_answer(board_name, 'USB Support?', 'No USB', 'USB')
+      if 1 == get_answer_val:
+        target_env += '_NOUSB'
+    else:
+      invalid_board()
+
+  if build_type == 'traceback' and target_env != 'LPC1768_debug_and_upload' and target_env != 'DUE_debug' and Marlin_ver == 2:
+    print("ERROR - this board isn't setup for traceback")
+    print('board_name: ', board_name)
+    print('target_env: ', target_env)
+    raise SystemExit(0)
+
+  return target_env
+
 # end - get_env
 
+
 # puts screen text into queue so that the parent thread can fetch the data from this thread
 if python_ver == 2:
-    import Queue as queue
+  import Queue as queue
 else:
-    import queue as queue
+  import queue as queue
 IO_queue = queue.Queue()
+
+
 #PIO_queue = queue.Queue()    not used!
-def write_to_screen_queue(text, format_tag = 'normal'):
-      double_in = [text, format_tag]
-      IO_queue.put(double_in, block = False)
+def write_to_screen_queue(text, format_tag='normal'):
+  double_in = [text, format_tag]
+  IO_queue.put(double_in, block=False)
 
 
 #
 #  send one line to the terminal screen with syntax highlighting
 #
 # input: unformatted text, flags from previous run
-# returns: formatted text ready to go to the terminal, flags from this run
+# return: formatted text ready to go to the terminal, flags from this run
 #
 # This routine remembers the status from call to call because previous
 # lines can affect how the current line is highlighted
@@ -641,166 +656,164 @@ next_line_warning = False
 warning_continue = False
 line_counter = 0
 
+
 def line_print(line_input):
 
-      global warning
-      global warning_FROM
-      global error
-      global standard
-      global prev_line_COM
-      global next_line_warning
-      global warning_continue
-      global line_counter
-
-      # all '0' elements must precede all '1' elements or they'll be skipped
-      platformio_highlights = [
-              ['Environment', 0, 'highlight_blue'],
-              ['[SKIP]', 1, 'warning'],
-              ['[IGNORED]', 1, 'warning'],
-              ['[ERROR]', 1, 'error'],
-              ['[FAILED]', 1, 'error'],
-              ['[SUCCESS]', 1, 'highlight_green']
-      ]
-
-      def write_to_screen_with_replace(text, highlights):  # search for highlights & split line accordingly
-        did_something = False
-        for highlight in highlights:
-          found = text.find(highlight[0])
-          if did_something == True:
-            break
-          if found >= 0 :
-            did_something = True
-            if 0 == highlight[1]:
-              found_1 = text.find(' ')
-              found_tab = text.find('\t')
-              if found_1 < 0 or found_1 > found_tab:
-                found_1 = found_tab
-              write_to_screen_queue(text[            : found_1 + 1      ])
-              for highlight_2 in highlights:
-                if  highlight[0] == highlight_2[0] :
-                  continue
-                found = text.find(highlight_2[0])
-                if found >= 0 :
-                  found_space = text.find(' ', found_1 + 1)
-                  found_tab = text.find('\t', found_1 + 1)
-                  if found_space < 0 or found_space > found_tab:
-                    found_space = found_tab
-                  found_right = text.find(']', found + 1)
-                  write_to_screen_queue(text[found_1 + 1 : found_space + 1     ], highlight[2])
-                  write_to_screen_queue(text[found_space + 1 : found + 1     ])
-                  write_to_screen_queue(text[found + 1   : found_right], highlight_2[2])
-                  write_to_screen_queue(text[found_right :                ] + '\n')
-                  break
-              break
-            if 1 == highlight[1]:
+  global warning
+  global warning_FROM
+  global error
+  global standard
+  global prev_line_COM
+  global next_line_warning
+  global warning_continue
+  global line_counter
+
+  # all '0' elements must precede all '1' elements or they'll be skipped
+  platformio_highlights = [
+    ['Environment', 0, 'highlight_blue'], ['[SKIP]', 1, 'warning'], ['[IGNORED]', 1, 'warning'], ['[ERROR]', 1, 'error'],
+    ['[FAILED]', 1, 'error'], ['[SUCCESS]', 1, 'highlight_green']
+  ]
+
+  def write_to_screen_with_replace(text, highlights):  # search for highlights & split line accordingly
+    did_something = False
+    for highlight in highlights:
+      found = text.find(highlight[0])
+      if did_something == True:
+        break
+      if found >= 0:
+        did_something = True
+        if 0 == highlight[1]:
+          found_1 = text.find(' ')
+          found_tab = text.find('\t')
+          if found_1 < 0 or found_1 > found_tab:
+            found_1 = found_tab
+          write_to_screen_queue(text[:found_1 + 1])
+          for highlight_2 in highlights:
+            if highlight[0] == highlight_2[0]:
+              continue
+            found = text.find(highlight_2[0])
+            if found >= 0:
+              found_space = text.find(' ', found_1 + 1)
+              found_tab = text.find('\t', found_1 + 1)
+              if found_space < 0 or found_space > found_tab:
+                found_space = found_tab
               found_right = text.find(']', found + 1)
-              write_to_screen_queue(text[               : found + 1   ])
-              write_to_screen_queue(text[found + 1      : found_right ], highlight[2])
-              write_to_screen_queue(text[found_right :                ] + '\n' + '\n')
-            break
-        if did_something == False:
-          r_loc = text.find('\r') + 1
-          if r_loc > 0 and r_loc < len(text):  # need to split this line
-            text = text.split('\r')
-            for line in text:
-              if line != '':
-                write_to_screen_queue(line + '\n')
-          else:
-            write_to_screen_queue(text + '\n')
-      # end - write_to_screen_with_replace
-
-
-    # scan the line
-      line_counter = line_counter + 1
-      max_search = len(line_input)
-      if max_search > 3 :
-        max_search = 3
-      beginning = line_input[:max_search]
-
-      # set flags
-      if 0 < line_input.find(': warning: '): # start of warning block
-        warning = True
-        warning_FROM = False
-        error = False
-        standard = False
-        prev_line_COM = False
-        prev_line_COM = False
-        warning_continue = True
-      if 0 < line_input.find('Thank you') or 0 < line_input.find('SUMMARY') :
-        warning = False               #standard line found
-        warning_FROM = False
-        error = False
-        standard = True
-        prev_line_COM = False
-        warning_continue = False
-      elif beginning == 'War' or \
-        beginning == '#er' or \
-        beginning == 'In ' or \
-        (beginning != 'Com' and prev_line_COM == True and not(beginning == 'Arc' or beginning == 'Lin'  or beginning == 'Ind') or \
-        next_line_warning == True):
-        warning = True                #warning found
-        warning_FROM = False
-        error = False
-        standard = False
-        prev_line_COM = False
-      elif beginning == 'Com' or \
-        beginning == 'Ver' or \
-        beginning == ' [E' or \
-        beginning == 'Rem' or \
-        beginning == 'Bui' or \
-        beginning == 'Ind' or \
-        beginning == 'PLA':
-        warning = False               #standard line found
-        warning_FROM = False
-        error = False
-        standard = True
-        prev_line_COM = False
-        warning_continue = False
-      elif beginning == '***':
-        warning = False               # error found
-        warning_FROM = False
-        error = True
-        standard = False
-        prev_line_COM = False
-      elif 0 < line_input.find(': error:') or \
-        0 < line_input.find(': fatal error:'):       # start of warning /error block
-        warning = False                                 # error found
-        warning_FROM = False
-        error = True
-        standard = False
-        prev_line_COM = False
-        warning_continue = True
-      elif beginning == 'fro' and warning == True or \
-        beginning == '.pi' :                             # start of warning /error block
-        warning_FROM = True
-        prev_line_COM = False
-        warning_continue = True
-      elif warning_continue == True:
-        warning = True
-        warning_FROM = False          # keep the warning status going until find a standard line or an error
-        error = False
-        standard = False
-        prev_line_COM = False
-        warning_continue = True
-
+              write_to_screen_queue(text[found_1 + 1:found_space + 1], highlight[2])
+              write_to_screen_queue(text[found_space + 1:found + 1])
+              write_to_screen_queue(text[found + 1:found_right], highlight_2[2])
+              write_to_screen_queue(text[found_right:] + '\n')
+              break
+          break
+        if 1 == highlight[1]:
+          found_right = text.find(']', found + 1)
+          write_to_screen_queue(text[:found + 1])
+          write_to_screen_queue(text[found + 1:found_right], highlight[2])
+          write_to_screen_queue(text[found_right:] + '\n' + '\n')
+        break
+    if did_something == False:
+      r_loc = text.find('\r') + 1
+      if r_loc > 0 and r_loc < len(text):  # need to split this line
+        text = text.split('\r')
+        for line in text:
+          if line != '':
+            write_to_screen_queue(line + '\n')
       else:
-        warning = False               # unknown so assume standard line
-        warning_FROM = False
-        error = False
-        standard = True
-        prev_line_COM = False
-        warning_continue = False
-
-      if beginning == 'Com':
-        prev_line_COM = True
-
-    # print based on flags
-      if standard == True:
-        write_to_screen_with_replace(line_input, platformio_highlights)   #print white on black with substitutions
-      if warning == True:
-        write_to_screen_queue(line_input + '\n', 'warning')
-      if error == True:
-        write_to_screen_queue(line_input + '\n', 'error')
+        write_to_screen_queue(text + '\n')
+
+  # end - write_to_screen_with_replace
+
+# scan the line
+  line_counter = line_counter + 1
+  max_search = len(line_input)
+  if max_search > 3:
+    max_search = 3
+  beginning = line_input[:max_search]
+
+  # set flags
+  if 0 < line_input.find(': warning: '):  # start of warning block
+    warning = True
+    warning_FROM = False
+    error = False
+    standard = False
+    prev_line_COM = False
+    prev_line_COM = False
+    warning_continue = True
+  if 0 < line_input.find('Thank you') or 0 < line_input.find('SUMMARY'):
+    warning = False  #standard line found
+    warning_FROM = False
+    error = False
+    standard = True
+    prev_line_COM = False
+    warning_continue = False
+  elif beginning == 'War' or \
+    beginning == '#er' or \
+    beginning == 'In ' or \
+    (beginning != 'Com' and prev_line_COM == True and not(beginning == 'Arc' or beginning == 'Lin'  or beginning == 'Ind') or \
+    next_line_warning == True):
+    warning = True  #warning found
+    warning_FROM = False
+    error = False
+    standard = False
+    prev_line_COM = False
+  elif beginning == 'Com' or \
+    beginning == 'Ver' or \
+    beginning == ' [E' or \
+    beginning == 'Rem' or \
+    beginning == 'Bui' or \
+    beginning == 'Ind' or \
+    beginning == 'PLA':
+    warning = False  #standard line found
+    warning_FROM = False
+    error = False
+    standard = True
+    prev_line_COM = False
+    warning_continue = False
+  elif beginning == '***':
+    warning = False  # error found
+    warning_FROM = False
+    error = True
+    standard = False
+    prev_line_COM = False
+  elif 0 < line_input.find(': error:') or \
+    0 < line_input.find(': fatal error:'):       # start of warning /error block
+    warning = False  # error found
+    warning_FROM = False
+    error = True
+    standard = False
+    prev_line_COM = False
+    warning_continue = True
+  elif beginning == 'fro' and warning == True or \
+    beginning == '.pi' :                             # start of warning /error block
+    warning_FROM = True
+    prev_line_COM = False
+    warning_continue = True
+  elif warning_continue == True:
+    warning = True
+    warning_FROM = False  # keep the warning status going until find a standard line or an error
+    error = False
+    standard = False
+    prev_line_COM = False
+    warning_continue = True
+
+  else:
+    warning = False  # unknown so assume standard line
+    warning_FROM = False
+    error = False
+    standard = True
+    prev_line_COM = False
+    warning_continue = False
+
+  if beginning == 'Com':
+    prev_line_COM = True
+
+# print based on flags
+  if standard == True:
+    write_to_screen_with_replace(line_input, platformio_highlights)  #print white on black with substitutions
+  if warning == True:
+    write_to_screen_queue(line_input + '\n', 'warning')
+  if error == True:
+    write_to_screen_queue(line_input + '\n', 'error')
+
 # end - line_print
 
 
@@ -810,7 +823,6 @@ def line_print(line_input):
 #                                                                        #
 ##########################################################################
 
-
 #  build      platformio run -e  target_env
 #  clean      platformio run --target clean -e  target_env
 #  upload     platformio run --target upload -e  target_env
@@ -823,478 +835,467 @@ def line_print(line_input):
 
 def sys_PIO():
 
-    ##########################################################################
-    #                                                                        #
-    # run Platformio inside the same shell as this Python script             #
-    #                                                                        #
-    ##########################################################################
-
-    global build_type
-    global target_env
-
-    import os
-
-    print('build_type: ', build_type)
-    print('starting platformio')
-
-    if   build_type == 'build':
-          # pio_result = os.system("echo -en '\033c'")
-          pio_result = os.system('platformio run -e ' + target_env)
-    elif build_type == 'clean':
-          pio_result = os.system('platformio run --target clean -e ' + target_env)
-    elif build_type == 'upload':
-          pio_result = os.system('platformio run --target upload -e ' + target_env)
-    elif build_type == 'traceback':
-          pio_result = os.system('platformio run --target upload -e ' + target_env)
-    elif build_type == 'program':
-          pio_result = os.system('platformio run --target program -e ' + target_env)
-    elif build_type == 'test':
-          pio_result = os.system('platformio test upload -e ' + target_env)
-    elif build_type == 'remote':
-          pio_result = os.system('platformio remote run --target program -e ' + target_env)
-    elif build_type == 'debug':
-          pio_result = os.system('platformio debug -e ' + target_env)
-    else:
-          print('ERROR - unknown build type:  ', build_type)
-          raise SystemExit(0)     # kill everything
-
-    # stream output from subprocess and split it into lines
-      #for line in iter(pio_subprocess.stdout.readline, ''):
-      #      line_print(line.replace('\n', ''))
+  ##########################################################################
+  #                                                                        #
+  # run Platformio inside the same shell as this Python script             #
+  #                                                                        #
+  ##########################################################################
+
+  global build_type
+  global target_env
+
+  import os
+
+  print('build_type: ', build_type)
+  print('starting platformio')
+
+  if build_type == 'build':
+    # pio_result = os.system("echo -en '\033c'")
+    pio_result = os.system('platformio run -e ' + target_env)
+  elif build_type == 'clean':
+    pio_result = os.system('platformio run --target clean -e ' + target_env)
+  elif build_type == 'upload':
+    pio_result = os.system('platformio run --target upload -e ' + target_env)
+  elif build_type == 'traceback':
+    pio_result = os.system('platformio run --target upload -e ' + target_env)
+  elif build_type == 'program':
+    pio_result = os.system('platformio run --target program -e ' + target_env)
+  elif build_type == 'test':
+    pio_result = os.system('platformio test upload -e ' + target_env)
+  elif build_type == 'remote':
+    pio_result = os.system('platformio remote run --target program -e ' + target_env)
+  elif build_type == 'debug':
+    pio_result = os.system('platformio debug -e ' + target_env)
+  else:
+    print('ERROR - unknown build type:  ', build_type)
+    raise SystemExit(0)  # kill everything
 
+  # stream output from subprocess and split it into lines
+  #for line in iter(pio_subprocess.stdout.readline, ''):
+  #      line_print(line.replace('\n', ''))
 
-    # append info used to run PlatformIO
-    #  write_to_screen_queue('\nBoard name: ' + board_name  + '\n')  # put build info at the bottom of the screen
-    #  write_to_screen_queue('Build type: ' + build_type  + '\n')
-    #  write_to_screen_queue('Environment used: ' + target_env  + '\n')
-    #  write_to_screen_queue(str(datetime.now()) + '\n')
+  # append info used to run PlatformIO
+  #  write_to_screen_queue('\nBoard name: ' + board_name  + '\n')  # put build info at the bottom of the screen
+  #  write_to_screen_queue('Build type: ' + build_type  + '\n')
+  #  write_to_screen_queue('Environment used: ' + target_env  + '\n')
+  #  write_to_screen_queue(str(datetime.now()) + '\n')
 
 # end - sys_PIO
 
 
-
 def run_PIO(dummy):
 
-    global build_type
-    global target_env
-    global board_name
-    print('build_type:  ', build_type)
-
-    import subprocess
-    import sys
-
-    print('starting platformio')
-
-    if   build_type == 'build':
-          # platformio run -e  target_env
-          # combine stdout & stderr so all compile messages are included
-          pio_subprocess = subprocess.Popen(['platformio', 'run', '-e', target_env], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
-
-    elif build_type == 'clean':
-          # platformio run --target clean -e  target_env
-          # combine stdout & stderr so all compile messages are included
-          pio_subprocess = subprocess.Popen(['platformio', 'run', '--target', 'clean', '-e', target_env], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
-
-    elif build_type == 'upload':
-          # platformio run --target upload -e  target_env
-          # combine stdout & stderr so all compile messages are included
-          pio_subprocess = subprocess.Popen(['platformio', 'run', '--target', 'upload', '-e',  target_env], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
-
-    elif build_type == 'traceback':
-          # platformio run --target upload -e  target_env  - select the debug environment if there is one
-          # combine stdout & stderr so all compile messages are included
-          pio_subprocess = subprocess.Popen(['platformio', 'run', '--target', 'upload', '-e', target_env], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
-
-    elif build_type == 'program':
-          # platformio run --target program -e  target_env
-          # combine stdout & stderr so all compile messages are included
-          pio_subprocess = subprocess.Popen(['platformio', 'run', '--target', 'program', '-e',  target_env], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
-
-    elif build_type == 'test':
-          #platformio test upload -e  target_env
-          # combine stdout & stderr so all compile messages are included
-          pio_subprocess = subprocess.Popen(['platformio', 'test', 'upload', '-e', target_env], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
-
-    elif build_type == 'remote':
-          # platformio remote run --target upload -e  target_env
-          # combine stdout & stderr so all compile messages are included
-          pio_subprocess = subprocess.Popen(['platformio', 'remote', 'run', '--target', 'program', '-e',  target_env], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
-
-    elif build_type == 'debug':
-          # platformio debug -e  target_env
-          # combine stdout & stderr so all compile messages are included
-          pio_subprocess = subprocess.Popen(['platformio', 'debug', '-e', target_env], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
-
-    else:
-          print('ERROR - unknown build type:  ', build_type)
-          raise SystemExit(0)     # kill everything
-
-  # stream output from subprocess and split it into lines
-    if python_ver == 2:
-        for line in iter(pio_subprocess.stdout.readline, ''):
-            line_print(line.replace('\n', ''))
-    else:
-        for line in iter(pio_subprocess.stdout.readline, b''):
-            line = line.decode('utf-8')
-            line_print(line.replace('\n', ''))
-
-  # append info used to run PlatformIO
-    write_to_screen_queue('\nBoard name: ' + board_name  + '\n')  # put build info at the bottom of the screen
-    write_to_screen_queue('Build type: ' + build_type  + '\n')
-    write_to_screen_queue('Environment used: ' + target_env  + '\n')
-    write_to_screen_queue(str(datetime.now()) + '\n')
+  global build_type
+  global target_env
+  global board_name
+  print('build_type:  ', build_type)
+
+  import subprocess
+  import sys
+
+  print('starting platformio')
+
+  if build_type == 'build':
+    # platformio run -e  target_env
+    # combine stdout & stderr so all compile messages are included
+    pio_subprocess = subprocess.Popen(
+      ['platformio', 'run', '-e', target_env], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
+    )
+
+  elif build_type == 'clean':
+    # platformio run --target clean -e  target_env
+    # combine stdout & stderr so all compile messages are included
+    pio_subprocess = subprocess.Popen(
+      ['platformio', 'run', '--target', 'clean', '-e', target_env], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
+    )
+
+  elif build_type == 'upload':
+    # platformio run --target upload -e  target_env
+    # combine stdout & stderr so all compile messages are included
+    pio_subprocess = subprocess.Popen(
+      ['platformio', 'run', '--target', 'upload', '-e', target_env], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
+    )
+
+  elif build_type == 'traceback':
+    # platformio run --target upload -e  target_env  - select the debug environment if there is one
+    # combine stdout & stderr so all compile messages are included
+    pio_subprocess = subprocess.Popen(
+      ['platformio', 'run', '--target', 'upload', '-e', target_env], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
+    )
+
+  elif build_type == 'program':
+    # platformio run --target program -e  target_env
+    # combine stdout & stderr so all compile messages are included
+    pio_subprocess = subprocess.Popen(
+      ['platformio', 'run', '--target', 'program', '-e', target_env], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
+    )
+
+  elif build_type == 'test':
+    #platformio test upload -e  target_env
+    # combine stdout & stderr so all compile messages are included
+    pio_subprocess = subprocess.Popen(
+      ['platformio', 'test', 'upload', '-e', target_env], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
+    )
+
+  elif build_type == 'remote':
+    # platformio remote run --target upload -e  target_env
+    # combine stdout & stderr so all compile messages are included
+    pio_subprocess = subprocess.Popen(
+      ['platformio', 'remote', 'run', '--target', 'program', '-e', target_env],
+      stdout=subprocess.PIPE,
+      stderr=subprocess.STDOUT
+    )
+
+  elif build_type == 'debug':
+    # platformio debug -e  target_env
+    # combine stdout & stderr so all compile messages are included
+    pio_subprocess = subprocess.Popen(
+      ['platformio', 'debug', '-e', target_env], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
+    )
+
+  else:
+    print('ERROR - unknown build type:  ', build_type)
+    raise SystemExit(0)  # kill everything
+
+# stream output from subprocess and split it into lines
+  if python_ver == 2:
+    for line in iter(pio_subprocess.stdout.readline, ''):
+      line_print(line.replace('\n', ''))
+  else:
+    for line in iter(pio_subprocess.stdout.readline, b''):
+      line = line.decode('utf-8')
+      line_print(line.replace('\n', ''))
+
+# append info used to run PlatformIO
+  write_to_screen_queue('\nBoard name: ' + board_name + '\n')  # put build info at the bottom of the screen
+  write_to_screen_queue('Build type: ' + build_type + '\n')
+  write_to_screen_queue('Environment used: ' + target_env + '\n')
+  write_to_screen_queue(str(datetime.now()) + '\n')
 
 # end - run_PIO
 
-
-
 ########################################################################
 
 import time
 import threading
 if python_ver == 2:
-    import Tkinter as tk
-    import Queue as queue
-    import ttk
-    from Tkinter import Tk, Frame, Text, Scrollbar, Menu
-    #from tkMessageBox import askokcancel      this is not used: removed
-    import tkFileDialog as fileDialog
+  import Tkinter as tk
+  import Queue as queue
+  import ttk
+  from Tkinter import Tk, Frame, Text, Scrollbar, Menu
+  #from tkMessageBox import askokcancel      this is not used: removed
+  import tkFileDialog as fileDialog
 else:
-    import tkinter as tk
-    import queue as queue
-    from tkinter import ttk, Tk, Frame, Text, Scrollbar, Menu
-    from tkinter import filedialog
+  import tkinter as tk
+  import queue as queue
+  from tkinter import ttk, Tk, Frame, Text, Menu
 import subprocess
 import sys
 que = queue.Queue()
 #IO_queue = queue.Queue()
 
-class output_window(Text):
- # based on Super Text
-    global continue_updates
-    continue_updates = True
-
-    global search_position
-    search_position = ''       # start with invalid search position
-
-    global error_found
-    error_found = False        # are there any errors?
-
-
-    def  __init__(self):
-
-
-        self.root = tk.Tk()
-        self.root.attributes("-topmost", True)
-        self.frame = tk.Frame(self.root)
-        self.frame.pack(fill='both', expand=True)
-
-        # text widget
-        #self.text = tk.Text(self.frame, borderwidth=3, relief="sunken")
-        Text.__init__(self, self.frame, borderwidth=3, relief="sunken")
-        self.config(tabs=(400,))  # configure Text widget tab stops
-        self.config(background = 'black', foreground = 'white', font= ("consolas", 12), wrap = 'word', undo = 'True')
-        #self.config(background = 'black', foreground = 'white', font= ("consolas", 12), wrap = 'none', undo = 'True')
-        self.config(height  = 24, width = 100)
-        self.config(insertbackground = 'pale green')  # keyboard insertion point
-        self.pack(side='left', fill='both', expand=True)
-
-        self.tag_config('normal', foreground = 'white')
-        self.tag_config('warning', foreground = 'yellow' )
-        self.tag_config('error', foreground = 'red')
-        self.tag_config('highlight_green', foreground = 'green')
-        self.tag_config('highlight_blue', foreground = 'cyan')
-        self.tag_config('error_highlight_inactive', background = 'dim gray')
-        self.tag_config('error_highlight_active', background = 'light grey')
-
-        self.bind_class("Text","<Control-a>", self.select_all)  # required in windows, works in others
-        self.bind_all("<Control-Shift-E>", self.scroll_errors)
-        self.bind_class("<Control-Shift-R>", self.rebuild)
-
-        # scrollbar
-
-        scrb = tk.Scrollbar(self.frame, orient='vertical', command=self.yview)
-        self.config(yscrollcommand=scrb.set)
-        scrb.pack(side='right', fill='y')
-
-        #self.scrb_Y = tk.Scrollbar(self.frame, orient='vertical', command=self.yview)
-        #self.scrb_Y.config(yscrollcommand=self.scrb_Y.set)
-        #self.scrb_Y.pack(side='right', fill='y')
-
-        #self.scrb_X = tk.Scrollbar(self.frame, orient='horizontal', command=self.xview)
-        #self.scrb_X.config(xscrollcommand=self.scrb_X.set)
-        #self.scrb_X.pack(side='bottom', fill='x')
-
-        #scrb_X = tk.Scrollbar(self, orient=tk.HORIZONTAL, command=self.xview)  # tk.HORIZONTAL now have a horizsontal scroll bar BUT... shrinks it to a postage stamp and hides far right behind the vertical scroll bar
-        #self.config(xscrollcommand=scrb_X.set)
-        #scrb_X.pack(side='bottom', fill='x')
-
-        #scrb= tk.Scrollbar(self, orient='vertical', command=self.yview)
-        #self.config(yscrollcommand=scrb.set)
-        #scrb.pack(side='right', fill='y')
-
-        #self.config(height  = 240, width = 1000)            # didn't get the size baCK TO NORMAL
-        #self.pack(side='left', fill='both', expand=True)    # didn't get the size baCK TO NORMAL
-
-
-        # pop-up menu
-        self.popup = tk.Menu(self, tearoff=0)
-
-        self.popup.add_command(label='Copy', command=self._copy)
-        self.popup.add_command(label='Paste', command=self._paste)
-        self.popup.add_separator()
-        self.popup.add_command(label='Cut', command=self._cut)
-        self.popup.add_separator()
-        self.popup.add_command(label='Select All', command=self._select_all)
-        self.popup.add_command(label='Clear All', command=self._clear_all)
-        self.popup.add_separator()
-        self.popup.add_command(label='Save As', command=self._file_save_as)
-        self.popup.add_separator()
-        #self.popup.add_command(label='Repeat Build(CTL-shift-r)', command=self._rebuild)
-        self.popup.add_command(label='Repeat Build', command=self._rebuild)
-        self.popup.add_separator()
-        self.popup.add_command(label='Scroll Errors (CTL-shift-e)', command=self._scroll_errors)
-        self.popup.add_separator()
-        self.popup.add_command(label='Open File at Cursor', command=self._open_selected_file)
-
-        if current_OS == 'Darwin':  # MAC
-          self.bind('<Button-2>', self._show_popup)  # macOS only
-        else:
-          self.bind('<Button-3>', self._show_popup)  # Windows & Linux
 
+class output_window(Text):
+  # based on Super Text
+  global continue_updates
+  continue_updates = True
+
+  global search_position
+  search_position = ''  # start with invalid search position
+
+  global error_found
+  error_found = False  # are there any errors?
+
+  def __init__(self):
+
+    self.root = tk.Tk()
+    self.root.attributes("-topmost", True)
+    self.frame = tk.Frame(self.root)
+    self.frame.pack(fill='both', expand=True)
+
+    # text widget
+    #self.text = tk.Text(self.frame, borderwidth=3, relief="sunken")
+    Text.__init__(self, self.frame, borderwidth=3, relief="sunken")
+    self.config(tabs=(400, ))  # configure Text widget tab stops
+    self.config(background='black', foreground='white', font=("consolas", 12), wrap='word', undo='True')
+    #self.config(background = 'black', foreground = 'white', font= ("consolas", 12), wrap = 'none', undo = 'True')
+    self.config(height=24, width=100)
+    self.config(insertbackground='pale green')  # keyboard insertion point
+    self.pack(side='left', fill='both', expand=True)
+
+    self.tag_config('normal', foreground='white')
+    self.tag_config('warning', foreground='yellow')
+    self.tag_config('error', foreground='red')
+    self.tag_config('highlight_green', foreground='green')
+    self.tag_config('highlight_blue', foreground='cyan')
+    self.tag_config('error_highlight_inactive', background='dim gray')
+    self.tag_config('error_highlight_active', background='light grey')
+
+    self.bind_class("Text", "<Control-a>", self.select_all)  # required in windows, works in others
+    self.bind_all("<Control-Shift-E>", self.scroll_errors)
+    self.bind_class("<Control-Shift-R>", self.rebuild)
+
+    # scrollbar
+
+    scrb = tk.Scrollbar(self.frame, orient='vertical', command=self.yview)
+    self.config(yscrollcommand=scrb.set)
+    scrb.pack(side='right', fill='y')
+
+    #self.scrb_Y = tk.Scrollbar(self.frame, orient='vertical', command=self.yview)
+    #self.scrb_Y.config(yscrollcommand=self.scrb_Y.set)
+    #self.scrb_Y.pack(side='right', fill='y')
+
+    #self.scrb_X = tk.Scrollbar(self.frame, orient='horizontal', command=self.xview)
+    #self.scrb_X.config(xscrollcommand=self.scrb_X.set)
+    #self.scrb_X.pack(side='bottom', fill='x')
+
+    #scrb_X = tk.Scrollbar(self, orient=tk.HORIZONTAL, command=self.xview)  # tk.HORIZONTAL now have a horizsontal scroll bar BUT... shrinks it to a postage stamp and hides far right behind the vertical scroll bar
+    #self.config(xscrollcommand=scrb_X.set)
+    #scrb_X.pack(side='bottom', fill='x')
+
+    #scrb= tk.Scrollbar(self, orient='vertical', command=self.yview)
+    #self.config(yscrollcommand=scrb.set)
+    #scrb.pack(side='right', fill='y')
+
+    #self.config(height  = 240, width = 1000)            # didn't get the size baCK TO NORMAL
+    #self.pack(side='left', fill='both', expand=True)    # didn't get the size baCK TO NORMAL
+
+    # pop-up menu
+    self.popup = tk.Menu(self, tearoff=0)
+
+    self.popup.add_command(label='Copy', command=self._copy)
+    self.popup.add_command(label='Paste', command=self._paste)
+    self.popup.add_separator()
+    self.popup.add_command(label='Cut', command=self._cut)
+    self.popup.add_separator()
+    self.popup.add_command(label='Select All', command=self._select_all)
+    self.popup.add_command(label='Clear All', command=self._clear_all)
+    self.popup.add_separator()
+    self.popup.add_command(label='Save As', command=self._file_save_as)
+    self.popup.add_separator()
+    #self.popup.add_command(label='Repeat Build(CTL-shift-r)', command=self._rebuild)
+    self.popup.add_command(label='Repeat Build', command=self._rebuild)
+    self.popup.add_separator()
+    self.popup.add_command(label='Scroll Errors (CTL-shift-e)', command=self._scroll_errors)
+    self.popup.add_separator()
+    self.popup.add_command(label='Open File at Cursor', command=self._open_selected_file)
+
+    if current_OS == 'Darwin':  # MAC
+      self.bind('<Button-2>', self._show_popup)  # macOS only
+    else:
+      self.bind('<Button-3>', self._show_popup)  # Windows & Linux
 
-  # threading & subprocess section
+# threading & subprocess section
 
-    def start_thread(self, ):
-        global continue_updates
-        # create then start a secondary thread to run an arbitrary function
-        #  must have at least one argument
-        self.secondary_thread = threading.Thread(target = lambda q, arg1: q.put(run_PIO(arg1)), args=(que, ''))
-        self.secondary_thread.start()
-        continue_updates = True
-        # check the Queue in 50ms
-        self.root.after(50, self.check_thread)
-        self.root.after(50, self.update)
+  def start_thread(self, ):
+    global continue_updates
+    # create then start a secondary thread to run an arbitrary function
+    #  must have at least one argument
+    self.secondary_thread = threading.Thread(target=lambda q, arg1: q.put(run_PIO(arg1)), args=(que, ''))
+    self.secondary_thread.start()
+    continue_updates = True
+    # check the Queue in 50ms
+    self.root.after(50, self.check_thread)
+    self.root.after(50, self.update)
 
+  def check_thread(self):  # wait for user to kill the window
+    global continue_updates
+    if continue_updates == True:
+      self.root.after(10, self.check_thread)
 
-    def check_thread(self):  # wait for user to kill the window
-        global continue_updates
-        if continue_updates == True:
-          self.root.after(10, self.check_thread)
+  def update(self):
+    global continue_updates
+    if continue_updates == True:
+      self.root.after(10, self.update)  #method is called every 50ms
+    temp_text = ['0', '0']
+    if IO_queue.empty():
+      if not (self.secondary_thread.is_alive()):
+        continue_updates = False  # queue is exhausted and thread is dead so no need for further updates
+    else:
+      try:
+        temp_text = IO_queue.get(block=False)
+      except Queue.Empty:
+        continue_updates = False  # queue is exhausted so no need for further updates
+      else:
+        self.insert('end', temp_text[0], temp_text[1])
+        self.see("end")  # make the last line visible (scroll text off the top)
 
+# text editing section
 
-    def update(self):
-        global continue_updates
-        if continue_updates == True:
-           self.root.after(10, self.update)#method is called every 50ms
-        temp_text = ['0','0']
-        if IO_queue.empty():
-          if not(self.secondary_thread.is_alive()):
-            continue_updates = False  # queue is exhausted and thread is dead so no need for further updates
+  def _scroll_errors(self):
+    global search_position
+    global error_found
+    if search_position == '':  # first time so highlight all errors
+      countVar = tk.IntVar()
+      search_position = '1.0'
+      search_count = 0
+      while search_position != '' and search_count < 100:
+        search_position = self.search("error", search_position, stopindex="end", count=countVar, nocase=1)
+        search_count = search_count + 1
+        if search_position != '':
+          error_found = True
+          end_pos = '{}+{}c'.format(search_position, 5)
+          self.tag_add("error_highlight_inactive", search_position, end_pos)
+          search_position = '{}+{}c'.format(search_position, 1)  # point to the next character for new search
         else:
-          try:
-              temp_text = IO_queue.get(block = False)
-          except Queue.Empty:
-              continue_updates = False  # queue is exhausted so no need for further updates
-          else:
-              self.insert('end', temp_text[0], temp_text[1])
-              self.see("end")  # make the last line visible (scroll text off the top)
-
-
-  # text editing section
-
-
-    def _scroll_errors(self):
-        global search_position
-        global error_found
-        if search_position == '':  # first time so highlight all errors
-            countVar = tk.IntVar()
-            search_position = '1.0'
-            search_count = 0
-            while search_position != '' and search_count < 100:
-                search_position = self.search("error", search_position, stopindex="end", count=countVar, nocase=1)
-                search_count = search_count + 1
-                if search_position != '':
-                    error_found = True
-                    end_pos = '{}+{}c'.format(search_position, 5)
-                    self.tag_add("error_highlight_inactive", search_position, end_pos)
-                    search_position = '{}+{}c'.format(search_position, 1)  # point to the next character for new search
-                else:
-                    break
-
-        if error_found:
-            if search_position == '':
-                search_position = self.search("error", '1.0', stopindex="end",  nocase=1)  # new search
-            else:                           # remove active highlight
-                end_pos = '{}+{}c'.format(search_position, 5)
-                start_pos = '{}+{}c'.format(search_position, -1)
-                self.tag_remove("error_highlight_active", start_pos, end_pos)
-            search_position = self.search("error", search_position, stopindex="end",  nocase=1)  # finds first occurrence AGAIN on the first time through
-            if search_position == "":  # wrap around
-                search_position = self.search("error", '1.0', stopindex="end", nocase=1)
-            end_pos = '{}+{}c'.format(search_position, 5)
-            self.tag_add("error_highlight_active", search_position, end_pos)      # add active highlight
-            self.see(search_position)
-            search_position = '{}+{}c'.format(search_position, 1)  # point to the next character for new search
-
-    def scroll_errors(self, event):
-        self._scroll_errors()
-
-
-    def _rebuild(self):
-        #global board_name
-        #global Marlin_ver
-        #global target_env
-        #board_name, Marlin_ver = get_board_name()
-        #target_env = get_env(board_name, Marlin_ver)
-        self.start_thread()
-
-    def rebuild(self, event):
-        print("event happened")
-        self._rebuild()
-
-
-    def _open_selected_file(self):
-        current_line = self.index('insert')
-        line_start = current_line[ : current_line.find('.')] + '.0'
-        line_end = current_line[ : current_line.find('.')] + '.200'
-        self.mark_set("path_start", line_start)
-        self.mark_set("path_end", line_end)
-        path = self.get("path_start", "path_end")
-        from_loc = path.find('from ')
-        colon_loc = path.find(': ')
-        if 0 <= from_loc and ((colon_loc == -1) or (from_loc < colon_loc)) :
-          path = path [ from_loc + 5 : ]
-        if 0 <= colon_loc:
-          path = path [ :  colon_loc ]
-        if 0 <= path.find('\\') or  0 <= path.find('/'):  # make sure it really contains a path
-          open_file(path)
-
-
-    def _file_save_as(self):
-        self.filename = fileDialog.asksaveasfilename(defaultextension = '.txt')
-        f = open(self.filename, 'w')
-        f.write(self.get('1.0', 'end'))
-        f.close()
-
-
-
-    def copy(self, event):
-        try:
-            selection = self.get(*self.tag_ranges('sel'))
-            self.clipboard_clear()
-            self.clipboard_append(selection)
-        except TypeError:
-            pass
-
-    def cut(self, event):
-
-        try:
-            selection = self.get(*self.tag_ranges('sel'))
-            self.clipboard_clear()
-            self.clipboard_append(selection)
-            self.delete(*self.tag_ranges('sel'))
-        except TypeError:
-            pass
-
-    def _show_popup(self, event):
-        '''right-click popup menu'''
-
-        if self.root.focus_get() != self:
-            self.root.focus_set()
-
-        try:
-            self.popup.tk_popup(event.x_root, event.y_root, 0)
-        finally:
-            self.popup.grab_release()
-
-    def _cut(self):
-
-        try:
-            selection = self.get(*self.tag_ranges('sel'))
-            self.clipboard_clear()
-            self.clipboard_append(selection)
-            self.delete(*self.tag_ranges('sel'))
-        except TypeError:
-            pass
-
-    def cut(self, event):
-        self._cut()
-
-    def _copy(self):
-
-        try:
-            selection = self.get(*self.tag_ranges('sel'))
-            self.clipboard_clear()
-            self.clipboard_append(selection)
-        except TypeError:
-            pass
-
-    def copy(self, event):
-        self._copy()
-
-    def _paste(self):
-
-        self.insert('insert', self.selection_get(selection='CLIPBOARD'))
-
-    def _select_all(self):
-        self.tag_add('sel', '1.0', 'end')
-
-
-    def select_all(self, event):
-        self.tag_add('sel', '1.0', 'end')
-
-
-    def _clear_all(self):
-        #'''erases all text'''
-        #
-        #isok = askokcancel('Clear All', 'Erase all text?', frame=self,
-        #                   default='ok')
-        #if isok:
-        #    self.delete('1.0', 'end')
-        self.delete('1.0', 'end')
+          break
 
+    if error_found:
+      if search_position == '':
+        search_position = self.search("error", '1.0', stopindex="end", nocase=1)  # new search
+      else:  # remove active highlight
+        end_pos = '{}+{}c'.format(search_position, 5)
+        start_pos = '{}+{}c'.format(search_position, -1)
+        self.tag_remove("error_highlight_active", start_pos, end_pos)
+      search_position = self.search(
+        "error", search_position, stopindex="end", nocase=1
+      )  # finds first occurrence AGAIN on the first time through
+      if search_position == "":  # wrap around
+        search_position = self.search("error", '1.0', stopindex="end", nocase=1)
+      end_pos = '{}+{}c'.format(search_position, 5)
+      self.tag_add("error_highlight_active", search_position, end_pos)  # add active highlight
+      self.see(search_position)
+      search_position = '{}+{}c'.format(search_position, 1)  # point to the next character for new search
+
+  def scroll_errors(self, event):
+    self._scroll_errors()
+
+  def _rebuild(self):
+    #global board_name
+    #global Marlin_ver
+    #global target_env
+    #board_name, Marlin_ver = get_board_name()
+    #target_env = get_env(board_name, Marlin_ver)
+    self.start_thread()
+
+  def rebuild(self, event):
+    print("event happened")
+    self._rebuild()
+
+  def _open_selected_file(self):
+    current_line = self.index('insert')
+    line_start = current_line[:current_line.find('.')] + '.0'
+    line_end = current_line[:current_line.find('.')] + '.200'
+    self.mark_set("path_start", line_start)
+    self.mark_set("path_end", line_end)
+    path = self.get("path_start", "path_end")
+    from_loc = path.find('from ')
+    colon_loc = path.find(': ')
+    if 0 <= from_loc and ((colon_loc == -1) or (from_loc < colon_loc)):
+      path = path[from_loc + 5:]
+    if 0 <= colon_loc:
+      path = path[:colon_loc]
+    if 0 <= path.find('\\') or 0 <= path.find('/'):  # make sure it really contains a path
+      open_file(path)
+
+  def _file_save_as(self):
+    self.filename = fileDialog.asksaveasfilename(defaultextension='.txt')
+    f = open(self.filename, 'w')
+    f.write(self.get('1.0', 'end'))
+    f.close()
+
+  def copy(self, event):
+    try:
+      selection = self.get(*self.tag_ranges('sel'))
+      self.clipboard_clear()
+      self.clipboard_append(selection)
+    except TypeError:
+      pass
+
+  def cut(self, event):
+
+    try:
+      selection = self.get(*self.tag_ranges('sel'))
+      self.clipboard_clear()
+      self.clipboard_append(selection)
+      self.delete(*self.tag_ranges('sel'))
+    except TypeError:
+      pass
+
+  def _show_popup(self, event):
+    '''right-click popup menu'''
+
+    if self.root.focus_get() != self:
+      self.root.focus_set()
+
+    try:
+      self.popup.tk_popup(event.x_root, event.y_root, 0)
+    finally:
+      self.popup.grab_release()
+
+  def _cut(self):
+
+    try:
+      selection = self.get(*self.tag_ranges('sel'))
+      self.clipboard_clear()
+      self.clipboard_append(selection)
+      self.delete(*self.tag_ranges('sel'))
+    except TypeError:
+      pass
+
+  def cut(self, event):
+    self._cut()
+
+  def _copy(self):
+
+    try:
+      selection = self.get(*self.tag_ranges('sel'))
+      self.clipboard_clear()
+      self.clipboard_append(selection)
+    except TypeError:
+      pass
+
+  def copy(self, event):
+    self._copy()
+
+  def _paste(self):
+
+    self.insert('insert', self.selection_get(selection='CLIPBOARD'))
+
+  def _select_all(self):
+    self.tag_add('sel', '1.0', 'end')
+
+  def select_all(self, event):
+    self.tag_add('sel', '1.0', 'end')
+
+  def _clear_all(self):
+    #'''erases all text'''
+    #
+    #isok = askokcancel('Clear All', 'Erase all text?', frame=self,
+    #                   default='ok')
+    #if isok:
+    #    self.delete('1.0', 'end')
+    self.delete('1.0', 'end')
 
 # end - output_window
 
 
-
 def main():
 
+  ##########################################################################
+  #                                                                        #
+  # main program                                                           #
+  #                                                                        #
+  ##########################################################################
 
-    ##########################################################################
-    #                                                                        #
-    # main program                                                           #
-    #                                                                        #
-    ##########################################################################
+  global build_type
+  global target_env
+  global board_name
 
-    global build_type
-    global target_env
-    global board_name
+  board_name, Marlin_ver = get_board_name()
 
-    board_name, Marlin_ver = get_board_name()
-
-    target_env = get_env(board_name, Marlin_ver)
-
-    # Re-use the VSCode terminal, if possible
-    if os.environ.get('PLATFORMIO_CALLER', '') == 'vscode':
-        sys_PIO()
-    else:
-        auto_build = output_window()
-        auto_build.start_thread()  # executes the "run_PIO" function
+  target_env = get_env(board_name, Marlin_ver)
 
-        auto_build.root.mainloop()
+  # Re-use the VSCode terminal, if possible
+  if os.environ.get('PLATFORMIO_CALLER', '') == 'vscode':
+    sys_PIO()
+  else:
+    auto_build = output_window()
+    auto_build.start_thread()  # executes the "run_PIO" function
 
+    auto_build.root.mainloop()
 
 
 if __name__ == '__main__':
 
-    main()
+  main()
diff --git a/buildroot/share/tests/STM32F103RC_bigtree_USB-tests b/buildroot/share/tests/STM32F103RC_bigtree_NOUSB-tests
similarity index 100%
rename from buildroot/share/tests/STM32F103RC_bigtree_USB-tests
rename to buildroot/share/tests/STM32F103RC_bigtree_NOUSB-tests
diff --git a/platformio.ini b/platformio.ini
index 7801fe9023..f02e97f812 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -294,12 +294,33 @@ upload_protocol   = serial
 #
 # BigTree SKR Mini V1.1 / SKR mini E3 / SKR E3 DIP (STM32F103RCT6 ARM Cortex-M3)
 #
+#   STM32F103RC_bigtree .............. RCT6 with 256K
+#   STM32F103RC_bigtree_NOUSB ........ RCT6 with 256K (no USB)
+#   STM32F103RC_bigtree_512K.......... RCT6 with 512K
+#   STM32F103RC_bigtree_512K_NOUSB ... RCT6 with 512K (no USB)
+#
+
 [env:STM32F103RC_bigtree]
 platform          = ststm32
 framework         = arduino
 board             = genericSTM32F103RC
 platform_packages = tool-stm32duino
 extra_scripts     = buildroot/share/PlatformIO/scripts/STM32F103RC_SKR_MINI.py
+build_flags       = !python Marlin/src/HAL/HAL_STM32F1/build_flags.py
+  ${common.build_flags} -DDEBUG_LEVEL=0 -std=gnu++14 -DUSE_USB_COMPOSITE -DHAVE_SW_SERIAL -DSS_TIMER=4 -DSTM32_FLASH_SIZE=256
+build_unflags     = -std=gnu++11
+lib_deps          = ${common.lib_deps}
+  SoftwareSerialM=https://github.com/FYSETC/SoftwareSerialM/archive/master.zip
+lib_ignore        = Adafruit NeoPixel, SPI
+src_filter        = ${common.default_src_filter} +<src/HAL/HAL_STM32F1>
+monitor_speed     = 115200
+
+[env:STM32F103RC_bigtree_NOUSB]
+platform          = ststm32
+framework         = arduino
+board             = genericSTM32F103RC
+platform_packages = tool-stm32duino
+extra_scripts     = buildroot/share/PlatformIO/scripts/STM32F103RC_SKR_MINI.py
 build_flags       = !python Marlin/src/HAL/HAL_STM32F1/build_flags.py
   ${common.build_flags} -DDEBUG_LEVEL=0 -std=gnu++14 -DHAVE_SW_SERIAL -DSS_TIMER=4
 build_unflags     = -std=gnu++11
@@ -309,14 +330,31 @@ lib_ignore        = Adafruit NeoPixel, SPI
 src_filter        = ${common.default_src_filter} +<src/HAL/HAL_STM32F1>
 monitor_speed     = 115200
 
-[env:STM32F103RC_bigtree_USB]
+[env:STM32F103RC_bigtree_512K]
+platform          = ststm32
+framework         = arduino
+board             = genericSTM32F103RC
+board_upload.maximum_size=524288
+platform_packages = tool-stm32duino
+extra_scripts     = buildroot/share/PlatformIO/scripts/STM32F103RC_SKR_MINI.py
+build_flags       = !python Marlin/src/HAL/HAL_STM32F1/build_flags.py
+  ${common.build_flags} -DDEBUG_LEVEL=0 -std=gnu++14 -DUSE_USB_COMPOSITE -DHAVE_SW_SERIAL -DSS_TIMER=4 -DSTM32_FLASH_SIZE=512
+build_unflags     = -std=gnu++11
+lib_deps          = ${common.lib_deps}
+  SoftwareSerialM=https://github.com/FYSETC/SoftwareSerialM/archive/master.zip
+lib_ignore        = Adafruit NeoPixel, SPI
+src_filter        = ${common.default_src_filter} +<src/HAL/HAL_STM32F1>
+monitor_speed     = 115200
+
+[env:STM32F103RC_bigtree_512K_NOUSB]
 platform          = ststm32
 framework         = arduino
 board             = genericSTM32F103RC
+board_upload.maximum_size=524288
 platform_packages = tool-stm32duino
 extra_scripts     = buildroot/share/PlatformIO/scripts/STM32F103RC_SKR_MINI.py
 build_flags       = !python Marlin/src/HAL/HAL_STM32F1/build_flags.py
-  ${common.build_flags} -DDEBUG_LEVEL=0 -std=gnu++14 -DUSE_USB_COMPOSITE -DHAVE_SW_SERIAL -DSS_TIMER=4
+  ${common.build_flags} -DDEBUG_LEVEL=0 -std=gnu++14 -DHAVE_SW_SERIAL -DSS_TIMER=4 -DSTM32_FLASH_SIZE=512
 build_unflags     = -std=gnu++11
 lib_deps          = ${common.lib_deps}
   SoftwareSerialM=https://github.com/FYSETC/SoftwareSerialM/archive/master.zip
-- 
GitLab