From 38d1587091ba7b7258a194f6ad950e80c1490225 Mon Sep 17 00:00:00 2001
From: Scott Lahteine <thinkyhead@users.noreply.github.com>
Date: Mon, 4 May 2020 14:37:43 -0500
Subject: [PATCH] Malyan M200 V2 (#17840)

---
 Marlin/src/core/boards.h                      | 51 ++++++++++---------
 .../lib/ftdi_eve_touch_ui/marlin_events.cpp   | 10 ++--
 Marlin/src/lcd/extui/ui_api.cpp               |  4 +-
 Marlin/src/lcd/extui_dgus_lcd.cpp             | 13 +++--
 Marlin/src/lcd/extui_malyan_lcd.cpp           | 48 +++++++++++++----
 Marlin/src/lcd/language/language_en.h         |  4 ++
 Marlin/src/pins/pins.h                        |  2 +
 Marlin/src/pins/stm32f0/pins_MALYAN_M200_V2.h | 31 +++++++++++
 Marlin/src/pins/stm32f1/pins_MALYAN_M200.h    |  4 +-
 .../share/PlatformIO/boards/malyanM200.json   |  1 -
 .../share/PlatformIO/boards/malyanM200v2.json |  2 +-
 platformio.ini                                | 18 +++++--
 12 files changed, 132 insertions(+), 56 deletions(-)
 create mode 100644 Marlin/src/pins/stm32f0/pins_MALYAN_M200_V2.h

diff --git a/Marlin/src/core/boards.h b/Marlin/src/core/boards.h
index 82a969b71a..22e64f7ccf 100644
--- a/Marlin/src/core/boards.h
+++ b/Marlin/src/core/boards.h
@@ -277,31 +277,32 @@
 
 #define BOARD_STM32F103RE             4000  // STM32F103RE Libmaple-based STM32F1 controller
 #define BOARD_MALYAN_M200             4001  // STM32C8T6  Libmaple-based STM32F1 controller
-#define BOARD_STM3R_MINI              4002  // STM32F103RE Libmaple-based STM32F1 controller
-#define BOARD_GTM32_PRO_VB            4003  // STM32F103VET6 controller
-#define BOARD_MORPHEUS                4004  // STM32F103C8 / STM32F103CB  Libmaple-based STM32F1 controller
-#define BOARD_CHITU3D                 4005  // Chitu3D (STM32F103RET6)
-#define BOARD_MKS_ROBIN               4006  // MKS Robin (STM32F103ZET6)
-#define BOARD_MKS_ROBIN_MINI          4007  // MKS Robin Mini (STM32F103VET6)
-#define BOARD_MKS_ROBIN_NANO          4008  // MKS Robin Nano (STM32F103VET6)
-#define BOARD_MKS_ROBIN_LITE          4009  // MKS Robin Lite/Lite2 (STM32F103RCT6)
-#define BOARD_MKS_ROBIN_LITE3         4010  // MKS Robin Lite3 (STM32F103RCT6)
-#define BOARD_MKS_ROBIN_PRO           4011  // MKS Robin Pro (STM32F103ZET6)
-#define BOARD_BTT_SKR_MINI_V1_1       4012  // BigTreeTech SKR Mini v1.1 (STM32F103RC)
-#define BOARD_BTT_SKR_MINI_E3_V1_0    4013  // BigTreeTech SKR Mini E3 (STM32F103RC)
-#define BOARD_BTT_SKR_MINI_E3_V1_2    4014  // BigTreeTech SKR Mini E3 V1.2 (STM32F103RC)
-#define BOARD_BTT_SKR_E3_DIP          4015  // BigTreeTech SKR E3 DIP V1.0 (STM32F103RC / STM32F103RE)
-#define BOARD_JGAURORA_A5S_A1         4016  // JGAurora A5S A1 (STM32F103ZET6)
-#define BOARD_FYSETC_AIO_II           4017  // FYSETC AIO_II
-#define BOARD_FYSETC_CHEETAH          4018  // FYSETC Cheetah
-#define BOARD_FYSETC_CHEETAH_V12      4019  // FYSETC Cheetah V1.2
-#define BOARD_LONGER3D_LK             4020  // Alfawise U20/U20+/U30 (Longer3D LK1/2) / STM32F103VET6
-#define BOARD_GTM32_MINI              4021  // STM32F103VET6 controller
-#define BOARD_GTM32_MINI_A30          4022  // STM32F103VET6 controller
-#define BOARD_GTM32_REV_B             4023  // STM32F103VET6 controller
-#define BOARD_MKS_ROBIN_E3D           4024  // MKS Robin E3D(STM32F103RCT6)
-#define BOARD_MKS_ROBIN_E3            4025  // MKS Robin E3(STM32F103RCT6)
-#define BOARD_MALYAN_M300             4026  // STM32F070-based delta
+#define BOARD_MALYAN_M200_V2          4002  // STM32F070RB  Libmaple-based STM32F0 controller
+#define BOARD_STM3R_MINI              4003  // STM32F103RE  Libmaple-based STM32F1 controller
+#define BOARD_GTM32_PRO_VB            4004  // STM32F103VET6 controller
+#define BOARD_MORPHEUS                4005  // STM32F103C8 / STM32F103CB  Libmaple-based STM32F1 controller
+#define BOARD_CHITU3D                 4006  // Chitu3D (STM32F103RET6)
+#define BOARD_MKS_ROBIN               4007  // MKS Robin (STM32F103ZET6)
+#define BOARD_MKS_ROBIN_MINI          4008  // MKS Robin Mini (STM32F103VET6)
+#define BOARD_MKS_ROBIN_NANO          4009  // MKS Robin Nano (STM32F103VET6)
+#define BOARD_MKS_ROBIN_LITE          4010  // MKS Robin Lite/Lite2 (STM32F103RCT6)
+#define BOARD_MKS_ROBIN_LITE3         4011  // MKS Robin Lite3 (STM32F103RCT6)
+#define BOARD_MKS_ROBIN_PRO           4012  // MKS Robin Pro (STM32F103ZET6)
+#define BOARD_BTT_SKR_MINI_V1_1       4013  // BigTreeTech SKR Mini v1.1 (STM32F103RC)
+#define BOARD_BTT_SKR_MINI_E3_V1_0    4014  // BigTreeTech SKR Mini E3 (STM32F103RC)
+#define BOARD_BTT_SKR_MINI_E3_V1_2    4015  // BigTreeTech SKR Mini E3 V1.2 (STM32F103RC)
+#define BOARD_BTT_SKR_E3_DIP          4016  // BigTreeTech SKR E3 DIP V1.0 (STM32F103RC / STM32F103RE)
+#define BOARD_JGAURORA_A5S_A1         4017  // JGAurora A5S A1 (STM32F103ZET6)
+#define BOARD_FYSETC_AIO_II           4018  // FYSETC AIO_II
+#define BOARD_FYSETC_CHEETAH          4019  // FYSETC Cheetah
+#define BOARD_FYSETC_CHEETAH_V12      4020  // FYSETC Cheetah V1.2
+#define BOARD_LONGER3D_LK             4021  // Alfawise U20/U20+/U30 (Longer3D LK1/2) / STM32F103VET6
+#define BOARD_GTM32_MINI              4022  // STM32F103VET6 controller
+#define BOARD_GTM32_MINI_A30          4023  // STM32F103VET6 controller
+#define BOARD_GTM32_REV_B             4024  // STM32F103VET6 controller
+#define BOARD_MKS_ROBIN_E3D           4025  // MKS Robin E3D(STM32F103RCT6)
+#define BOARD_MKS_ROBIN_E3            4026  // MKS Robin E3(STM32F103RCT6)
+#define BOARD_MALYAN_M300             4027  // STM32F070-based delta
 
 //
 // ARM Cortex-M4F
diff --git a/Marlin/src/lcd/extui/lib/ftdi_eve_touch_ui/marlin_events.cpp b/Marlin/src/lcd/extui/lib/ftdi_eve_touch_ui/marlin_events.cpp
index e4dbf7b360..6e9595e099 100644
--- a/Marlin/src/lcd/extui/lib/ftdi_eve_touch_ui/marlin_events.cpp
+++ b/Marlin/src/lcd/extui/lib/ftdi_eve_touch_ui/marlin_events.cpp
@@ -150,19 +150,19 @@ namespace ExtUI {
   #if HAS_PID_HEATING
     void onPidTuning(const result_t rst) {
       // Called for temperature PID tuning result
-      SERIAL_ECHOLNPAIR("OnPidTuning:", rst);
+      //SERIAL_ECHOLNPAIR("OnPidTuning:", rst);
       switch (rst) {
         case PID_BAD_EXTRUDER_NUM:
-          StatusScreen::setStatusMessage(STR_PID_BAD_EXTRUDER_NUM);
+          StatusScreen::setstatusmessagePGM(GET_TEXT(MSG_PID_BAD_EXTRUDER_NUM));
           break;
         case PID_TEMP_TOO_HIGH:
-          StatusScreen::setStatusMessage(STR_PID_TEMP_TOO_HIGH);
+          StatusScreen::setstatusmessagePGM(GET_TEXT(MSG_PID_TEMP_TOO_HIGH));
           break;
         case PID_TUNING_TIMEOUT:
-          StatusScreen::setStatusMessage(STR_PID_TIMEOUT);
+          StatusScreen::setstatusmessagePGM(GET_TEXT(MSG_PID_TIMEOUT));
           break;
         case PID_DONE:
-          StatusScreen::setStatusMessage(STR_PID_AUTOTUNE_FINISHED);
+          StatusScreen::setstatusmessagePGM(GET_TEXT(MSG_PID_AUTOTUNE_DONE));
           break;
       }
       GOTO_SCREEN(StatusScreen);
diff --git a/Marlin/src/lcd/extui/ui_api.cpp b/Marlin/src/lcd/extui/ui_api.cpp
index 937a435d7d..95f630f7ec 100644
--- a/Marlin/src/lcd/extui/ui_api.cpp
+++ b/Marlin/src/lcd/extui/ui_api.cpp
@@ -330,8 +330,8 @@ namespace ExtUI {
     // Delta limits XY based on the current offset from center
     // This assumes the center is 0,0
     #if ENABLED(DELTA)
-      if (axis != Z_AXIS) {
-        max = SQRT(sq((float)(DELTA_PRINTABLE_RADIUS)) - sq(current_position[Y_AXIS - axis])); // (Y_AXIS - axis) == the other axis
+      if (axis != Z) {
+        max = SQRT(sq(float(DELTA_PRINTABLE_RADIUS)) - sq(current_position[Y - axis])); // (Y - axis) == the other axis
         min = -max;
       }
     #endif
diff --git a/Marlin/src/lcd/extui_dgus_lcd.cpp b/Marlin/src/lcd/extui_dgus_lcd.cpp
index c29bc5a114..56308f08d6 100644
--- a/Marlin/src/lcd/extui_dgus_lcd.cpp
+++ b/Marlin/src/lcd/extui_dgus_lcd.cpp
@@ -45,7 +45,7 @@ namespace ExtUI {
 
   void onIdle() { ScreenHandler.loop(); }
 
-  void onPrinterKilled(PGM_P error, PGM_P component) {
+  void onPrinterKilled(PGM_P const error, PGM_P const component) {
     ScreenHandler.sendinfoscreen(GET_TEXT(MSG_HALTED), error, NUL_STR, GET_TEXT(MSG_PLEASE_RESET), true, true, true, true);
     ScreenHandler.GotoScreen(DGUSLCD_SCREEN_KILL);
     while (!ScreenHandler.loop());  // Wait while anything is left to be sent
@@ -127,19 +127,18 @@ namespace ExtUI {
   #if HAS_PID_HEATING
     void onPidTuning(const result_t rst) {
       // Called for temperature PID tuning result
-      SERIAL_ECHOLNPAIR("onPidTuning:",rst);
-      switch(rst) {
+      switch (rst) {
         case PID_BAD_EXTRUDER_NUM:
-          ScreenHandler.setstatusmessagePGM(PSTR(STR_PID_BAD_EXTRUDER_NUM));
+          ScreenHandler.setstatusmessagePGM(GET_TEXT(MSG_PID_BAD_EXTRUDER_NUM));
           break;
         case PID_TEMP_TOO_HIGH:
-          ScreenHandler.setstatusmessagePGM(PSTR(STR_PID_TEMP_TOO_HIGH));
+          ScreenHandler.setstatusmessagePGM(GET_TEXT(MSG_PID_TEMP_TOO_HIGH));
           break;
         case PID_TUNING_TIMEOUT:
-          ScreenHandler.setstatusmessagePGM(PSTR(STR_PID_TIMEOUT));
+          ScreenHandler.setstatusmessagePGM(GET_TEXT(MSG_PID_TIMEOUT));
           break;
         case PID_DONE:
-          ScreenHandler.setstatusmessagePGM(PSTR(STR_PID_AUTOTUNE_FINISHED));
+          ScreenHandler.setstatusmessagePGM(GET_TEXT(MSG_PID_AUTOTUNE_DONE));
           break;
       }
       ScreenHandler.GotoScreen(DGUSLCD_SCREEN_MAIN);
diff --git a/Marlin/src/lcd/extui_malyan_lcd.cpp b/Marlin/src/lcd/extui_malyan_lcd.cpp
index 63b52e2332..85101ae8d9 100644
--- a/Marlin/src/lcd/extui_malyan_lcd.cpp
+++ b/Marlin/src/lcd/extui_malyan_lcd.cpp
@@ -97,6 +97,18 @@ void write_to_lcd(const char * const message) {
   LCD_SERIAL.Print::write(encoded_message, message_length);
 }
 
+// {E:<msg>} is for error states.
+void set_lcd_error_P(PGM_P const error, PGM_P const component=nullptr) {
+  write_to_lcd_P(PSTR("{E:"));
+  write_to_lcd_P(error);
+  if (component) {
+    write_to_lcd_P(PSTR(" "));
+    write_to_lcd_P(component);
+  }
+  write_to_lcd_P(PSTR("}"));
+}
+
+
 /**
  * Process an LCD 'C' command.
  * These are currently all temperature commands
@@ -465,15 +477,33 @@ namespace ExtUI {
     #endif
   }
 
-  // {E:<msg>} is for error states.
-  void onPrinterKilled(PGM_P error, PGM_P component) {
-    write_to_lcd_P(PSTR("{E:"));
-    write_to_lcd_P(error);
-    write_to_lcd_P(PSTR(" "));
-    write_to_lcd_P(component);
-    write_to_lcd_P("}");
+  void onPrinterKilled(PGM_P const error, PGM_P const component) {
+    set_lcd_error_P(error, component);
   }
 
+  #if HAS_PID_HEATING
+
+    void onPidTuning(const result_t rst) {
+      // Called for temperature PID tuning result
+      //SERIAL_ECHOLNPAIR("OnPidTuning:", rst);
+      switch (rst) {
+        case PID_BAD_EXTRUDER_NUM:
+          set_lcd_error_P(GET_TEXT(MSG_PID_BAD_EXTRUDER_NUM));
+          break;
+        case PID_TEMP_TOO_HIGH:
+          set_lcd_error_P(GET_TEXT(MSG_PID_TEMP_TOO_HIGH));
+          break;
+        case PID_TUNING_TIMEOUT:
+          set_lcd_error_P(GET_TEXT(MSG_PID_TIMEOUT));
+          break;
+        case PID_DONE:
+          set_lcd_error_P(GET_TEXT(MSG_PID_AUTOTUNE_DONE));
+          break;
+      }
+    }
+
+  #endif
+
   void onPrintTimerStarted() { write_to_lcd_P(PSTR("{SYS:BUILD}")); }
   void onPrintTimerPaused() {}
   void onPrintTimerStopped() { write_to_lcd_P(PSTR("{TQ:100}")); }
@@ -500,10 +530,6 @@ namespace ExtUI {
   #if ENABLED(POWER_LOSS_RECOVERY)
     void onPowerLossResume() {}
   #endif
-
-  #if HAS_PID_HEATING
-    void onPidTuning(const result_t rst) {}
-  #endif
 }
 
 #endif // MALYAN_LCD
diff --git a/Marlin/src/lcd/language/language_en.h b/Marlin/src/lcd/language/language_en.h
index f9fb84cc80..8249dba743 100644
--- a/Marlin/src/lcd/language/language_en.h
+++ b/Marlin/src/lcd/language/language_en.h
@@ -266,6 +266,10 @@ namespace Language_en {
   PROGMEM Language_Str MSG_LCD_OFF                         = _UxGT("Off");
   PROGMEM Language_Str MSG_PID_AUTOTUNE                    = _UxGT("PID Autotune");
   PROGMEM Language_Str MSG_PID_AUTOTUNE_E                  = _UxGT("PID Autotune *");
+  PROGMEM Language_Str MSG_PID_AUTOTUNE_DONE               = _UxGT("PID tuning done");
+  PROGMEM Language_Str MSG_PID_BAD_EXTRUDER_NUM            = _UxGT("Autotune failed. Bad extruder.");
+  PROGMEM Language_Str MSG_PID_TEMP_TOO_HIGH               = _UxGT("Autotune failed. Temperature too high.");
+  PROGMEM Language_Str MSG_PID_TIMEOUT                     = _UxGT("Autotune failed! Timeout.");
   PROGMEM Language_Str MSG_PID_P                           = _UxGT("PID-P");
   PROGMEM Language_Str MSG_PID_P_E                         = _UxGT("PID-P *");
   PROGMEM Language_Str MSG_PID_I                           = _UxGT("PID-I");
diff --git a/Marlin/src/pins/pins.h b/Marlin/src/pins/pins.h
index dc42d80706..3ce761c6cf 100644
--- a/Marlin/src/pins/pins.h
+++ b/Marlin/src/pins/pins.h
@@ -469,6 +469,8 @@
 //
 // STM32 ARM Cortex-M0
 //
+#elif MB(MALYAN_M200_V2)
+  #include "stm32f0/pins_MALYAN_M200_V2.h"      // STM32F0                                env:STM32F070RB_malyan
 #elif MB(MALYAN_M300)
   #include "stm32f0/pins_MALYAN_M300.h"         // STM32F070                              env:malyan_M300
 
diff --git a/Marlin/src/pins/stm32f0/pins_MALYAN_M200_V2.h b/Marlin/src/pins/stm32f0/pins_MALYAN_M200_V2.h
new file mode 100644
index 0000000000..e8b4eed2fd
--- /dev/null
+++ b/Marlin/src/pins/stm32f0/pins_MALYAN_M200_V2.h
@@ -0,0 +1,31 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#ifndef STM32F0xx
+  #error "Oops! Select an STM32F0 board in your IDE."
+#endif
+
+#define BOARD_INFO_NAME "Malyan M200 V2"
+
+#include "../stm32f1/pins_MALYAN_M200.h"
diff --git a/Marlin/src/pins/stm32f1/pins_MALYAN_M200.h b/Marlin/src/pins/stm32f1/pins_MALYAN_M200.h
index 87f323dfa5..6e96df45c1 100644
--- a/Marlin/src/pins/stm32f1/pins_MALYAN_M200.h
+++ b/Marlin/src/pins/stm32f1/pins_MALYAN_M200.h
@@ -29,7 +29,9 @@
   #error "Oops! Select an STM32 board in your IDE."
 #endif
 
-#define BOARD_INFO_NAME "Malyan M200"
+#ifndef BOARD_INFO_NAME
+  #define BOARD_INFO_NAME "Malyan M200"
+#endif
 
 // Assume Flash EEPROM
 #if NO_EEPROM_SELECTED
diff --git a/buildroot/share/PlatformIO/boards/malyanM200.json b/buildroot/share/PlatformIO/boards/malyanM200.json
index dc0cf51f9e..bd783fe899 100644
--- a/buildroot/share/PlatformIO/boards/malyanM200.json
+++ b/buildroot/share/PlatformIO/boards/malyanM200.json
@@ -11,7 +11,6 @@
     "ldscript": "jtagOffset.ld",
     "mcu": "stm32f103cb",
     "variant": "malyanM200",
-    "genericvariant" : "MALYAN_M200_V1",
     "vec_tab_addr": "0x8002000"
   },
   "debug": {
diff --git a/buildroot/share/PlatformIO/boards/malyanM200v2.json b/buildroot/share/PlatformIO/boards/malyanM200v2.json
index 0d2090a93b..9e301ee79f 100644
--- a/buildroot/share/PlatformIO/boards/malyanM200v2.json
+++ b/buildroot/share/PlatformIO/boards/malyanM200v2.json
@@ -4,7 +4,7 @@
     "extra_flags": "-DSTM32F070xB",
     "f_cpu": "48000000L",
     "mcu": "stm32f070rbt6",
-    "genericvariant" : "MALYAN_M200_V2",
+    "variant": "MALYANM200_F070CB",
     "vec_tab_addr": "0x8002000"
   },
   "debug": {
diff --git a/platformio.ini b/platformio.ini
index 36d943d670..163c6b7419 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -605,10 +605,22 @@ lib_ignore    = Adafruit NeoPixel, SPI
 [env:STM32F103CB_malyan]
 platform    = ststm32
 board       = malyanM200
-build_flags = !python Marlin/src/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
+build_flags = !python Marlin/src/HAL/STM32F1/build_flags.py -DMCU_STM32F103CB -D __STM32F1__=1 -std=c++1y -DSERIAL_USB -ffunction-sections -fdata-sections -Wl,--gc-sections
   -DDEBUG_LEVEL=0 -D__MARLIN_FIRMWARE__
 src_filter  = ${common.default_src_filter} +<src/HAL/STM32F1>
-lib_ignore  = Adafruit NeoPixel, LiquidCrystal, LiquidTWI2, TMCStepper, U8glib-HAL, SPI
+lib_ignore  = LiquidCrystal, LiquidTWI2, Adafruit NeoPixel, TMCStepper, U8glib-HAL, SPI
+
+#
+# Malyan M200 v2 (STM32F070RB)
+#
+[env:STM32F070RB_malyan]
+platform    = ststm32
+board       = malyanM200v2
+build_flags = -DSTM32F0xx -DUSBCON -DUSBD_VID=0x0483 '-DUSB_MANUFACTURER="Unknown"' '-DUSB_PRODUCT="ARMED_V1"' -DUSBD_USE_CDC -DHAL_PCD_MODULE_ENABLED
+  -O2 -ffreestanding -fsigned-char -fno-move-loop-invariants -fno-strict-aliasing -std=gnu11 -std=gnu++11
+  -IMarlin/src/HAL/STM32
+src_filter  = ${common.default_src_filter} +<src/HAL/STM32>
+lib_ignore  = LiquidCrystal, LiquidTWI2, Adafruit NeoPixel, TMCStepper, U8glib-HAL
 
 #
 # Malyan M300 (STM32F070CB)
@@ -620,7 +632,7 @@ build_flags = ${common.build_flags}
     -DUSBCON -DUSBD_VID=0x0483 "-DUSB_MANUFACTURER=\"Unknown\"" "-DUSB_PRODUCT=\"MALYAN_M300\""
     -DHAL_PCD_MODULE_ENABLED -DUSBD_USE_CDC -DDISABLE_GENERIC_SERIALUSB -DHAL_UART_MODULE_ENABLED
 src_filter  = ${common.default_src_filter} +<src/HAL/STM32>
-lib_ignore  = U8glib, LiquidCrystal_I2C, LiquidCrystal, NewliquidCrystal, LiquidTWI2, Adafruit NeoPixel, TMCStepper, Servo(STM32F1), TMC26XStepper, U8glib-HAL
+lib_ignore  = LiquidCrystal, LiquidTWI2, Adafruit NeoPixel, TMCStepper, U8glib-HAL
 
 #
 # Chitu boards like Tronxy X5s (STM32F103ZET6)
-- 
GitLab