diff --git a/buildroot/share/PlatformIO/ldscripts/chitu_f103.ld b/buildroot/share/PlatformIO/ldscripts/chitu_f103.ld
new file mode 100644
index 0000000000000000000000000000000000000000..cc581236369e5e7d3f32b6d5b55e4dea8c49b813
--- /dev/null
+++ b/buildroot/share/PlatformIO/ldscripts/chitu_f103.ld
@@ -0,0 +1,14 @@
+MEMORY
+{
+  ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
+  rom (rx)  : ORIGIN = 0x08008800, LENGTH = 512K - 32K
+}
+
+/* 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/chitu_crypt.py b/buildroot/share/PlatformIO/scripts/chitu_crypt.py
new file mode 100644
index 0000000000000000000000000000000000000000..7634422d38a78c79e8caaf67fe90b74d5ff75110
--- /dev/null
+++ b/buildroot/share/PlatformIO/scripts/chitu_crypt.py
@@ -0,0 +1,119 @@
+Import("env")
+import struct
+
+# Relocate firmware from 0x08000000 to 0x08008800
+for define in env['CPPDEFINES']:
+    if define[0] == "VECT_TAB_ADDR":
+        env['CPPDEFINES'].remove(define)
+env['CPPDEFINES'].append(("VECT_TAB_ADDR", "0x8008800"))
+env.Replace(LDSCRIPT_PATH="buildroot/share/PlatformIO/ldscripts/chitu_f103.ld")
+
+def calculate_crc(contents, seed):
+    accumulating_xor_value = seed;
+
+    for i in range(0, len(contents), 4):
+        value = struct.unpack('<I', contents[ i : i + 4])[0]
+        accumulating_xor_value = accumulating_xor_value ^ value
+    return accumulating_xor_value
+
+def xor_block(r0, r1, block_number, block_size, file_key):
+    # This is the loop counter
+    loop_counter = 0x0
+
+    # This is the key length
+    key_length = 0x18
+
+    # This is an initial seed
+    xor_seed = 0x4bad
+
+    # This is the block counter
+    block_number = xor_seed * block_number
+
+    #load the xor key from the file
+    r7 =  file_key
+
+    for loop_counter in range(0, block_size):
+        # meant to make sure different bits of the key are used.
+        xor_seed = int(loop_counter/key_length)
+
+        # IP is a scratch register / R12
+        ip = loop_counter - (key_length * xor_seed)
+
+        # xor_seed = (loop_counter * loop_counter) + block_number
+        xor_seed = (loop_counter * loop_counter) + block_number
+
+        # shift the xor_seed left by the bits in IP.
+        xor_seed = xor_seed >> ip
+
+        # load a byte into IP
+        ip = r0[loop_counter]
+
+        # XOR the seed with r7
+        xor_seed = xor_seed ^ r7
+
+        # and then with IP
+        xor_seed = xor_seed ^ ip
+
+        #Now store the byte back
+        r1[loop_counter] = xor_seed & 0xFF
+
+        #increment the loop_counter
+        loop_counter = loop_counter + 1
+
+
+def encrypt_file(input, output_file, file_length):
+    input_file = bytearray(input.read())
+    block_size = 0x800
+    key_length = 0x18
+    file_key = 0xDAB27F94
+
+    xor_crc = 0xef3d4323;
+
+    # the input file is exepcted to be in chunks of 0x800
+    # so round the size
+    while len(input_file) % block_size != 0:
+        input_file.extend(b'0x0')
+
+    # write the file header
+    output_file.write(struct.pack(">I", 0x443D2D3F))
+    # encrypt the contents using a known file header key
+
+    # write the file_key
+    output_file.write(struct.pack(">I", 0x947FB2DA))
+
+    #TODO - how to enforce that the firmware aligns to block boundaries?
+    block_count = int(len(input_file) / block_size)
+    print "Block Count is ", block_count
+    for block_number in range(0, block_count):
+        block_offset = (block_number * block_size)
+        block_end = block_offset + block_size
+        block_array = bytearray(input_file[block_offset: block_end])
+        xor_block(block_array, block_array, block_number, block_size, file_key)
+        for n in range (0, block_size):
+            input_file[block_offset + n] = block_array[n]
+
+        # update the expected CRC value.
+        xor_crc = calculate_crc(block_array, xor_crc)
+
+    # write CRC
+    output_file.write(struct.pack("<I", xor_crc))
+
+    # finally, append the encrypted results.
+    output_file.write(input_file)
+    return
+
+
+# Encrypt ${PROGNAME}.bin and save it as 'update.cbd'
+def encrypt(source, target, env):
+    import os
+
+    firmware = open(target[0].path, "rb")
+    update = open(target[0].dir.path +'/update.cbd', "wb")
+    length = os.path.getsize(target[0].path)
+
+    encrypt_file(firmware, update, length)
+
+    firmware.close()
+    update.close()
+
+env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", encrypt);
diff --git a/platformio.ini b/platformio.ini
index 5b829b812e1b1a561ee2e3d2f8c01325d3228e41..ddc2642a64dc1dfa5f464c4149d3b0870ce357ef 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -526,6 +526,33 @@ lib_ignore    = Adafruit NeoPixel
 src_filter    = ${common.default_src_filter} +<src/HAL/HAL_TEENSY31_32>
 monitor_speed = 250000
 
+#
+# Malyan M200 (STM32F103CB)
+#
+[env:STM32F103CB_malyan]
+platform    = ststm32
+framework   = arduino
+board       = malyanM200
+build_flags = !python Marlin/src/HAL/HAL_STM32F1/build_flags.py -DMCU_STM32F103CB -D __STM32F1__=1 -std=c++1y -D MOTHERBOARD="BOARD_MALYAN_M200" -DSERIAL_USB -ffunction-sections -fdata-sections -Wl,--gc-sections
+  -DDEBUG_LEVEL=0 -D__MARLIN_FIRMWARE__
+src_filter  = ${common.default_src_filter} +<src/HAL/HAL_STM32F1>
+lib_ignore  = Adafruit NeoPixel, LiquidCrystal, LiquidTWI2, TMCStepper, U8glib-HAL, SPI
+
+#
+# Chitu boards like Tronxy X5s (STM32F103ZET6)
+#
+[env:chitu_f103]
+platform      = ststm32
+framework     = arduino
+board         = genericSTM32F103ZE
+extra_scripts = buildroot/share/PlatformIO/scripts/chitu_crypt.py
+build_flags   = !python Marlin/src/HAL/HAL_STM32F1/build_flags.py
+  ${common.build_flags} -DSTM32F1xx -std=gnu++14
+build_unflags = -std=gnu++11 -DCONFIG_MAPLE_MINI_NO_DISABLE_DEBUG= -DERROR_LED_PORT=GPIOE -DERROR_LED_PIN=6
+src_filter    = ${common.default_src_filter} +<src/HAL/HAL_STM32F1>
+lib_deps      = ${common.lib_deps}
+lib_ignore    = Adafruit NeoPixel
+
 #
 # Teensy 3.5 / 3.6 (ARM Cortex-M4)
 #
@@ -540,19 +567,6 @@ lib_ignore    = Adafruit NeoPixel
 src_filter    = ${common.default_src_filter} +<src/HAL/HAL_TEENSY35_36>
 monitor_speed = 250000
 
-#
-# Malyan M200 (STM32F103CB)
-#
-[env:STM32F103CB_malyan]
-platform    = ststm32
-framework   = arduino
-board       = malyanM200
-build_flags = !python Marlin/src/HAL/HAL_STM32F1/build_flags.py -DMCU_STM32F103CB -D __STM32F1__=1 -std=c++1y -D MOTHERBOARD="BOARD_MALYAN_M200" -DSERIAL_USB -ffunction-sections -fdata-sections -Wl,--gc-sections
-  -DDEBUG_LEVEL=0 -D__MARLIN_FIRMWARE__
-src_filter  = ${common.default_src_filter} +<src/HAL/HAL_STM32F1>
-#-<frameworks>
-lib_ignore  = Adafruit NeoPixel, LiquidCrystal, LiquidTWI2, TMCStepper, U8glib-HAL, SPI
-
 #
 # Espressif ESP32
 #