diff --git a/src/main/io/serial_cli.c b/src/main/io/serial_cli.c
new file mode 100644
index 0000000000..d73b2d45f7
--- /dev/null
+++ b/src/main/io/serial_cli.c
@@ -0,0 +1,2522 @@
+/*
+ * This file is part of Cleanflight.
+ *
+ * Cleanflight 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.
+ *
+ * Cleanflight 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 Cleanflight. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "platform.h"
+#include "scheduler.h"
+#include "version.h"
+
+#include "build_config.h"
+
+#include "common/axis.h"
+#include "common/maths.h"
+#include "common/color.h"
+#include "common/typeconversion.h"
+
+#include "drivers/system.h"
+
+#include "drivers/sensor.h"
+#include "drivers/accgyro.h"
+#include "drivers/compass.h"
+
+#include "drivers/serial.h"
+#include "drivers/bus_i2c.h"
+#include "drivers/gpio.h"
+#include "drivers/timer.h"
+#include "drivers/pwm_rx.h"
+
+#include "drivers/buf_writer.h"
+
+#include "io/escservo.h"
+#include "io/gps.h"
+#include "io/gimbal.h"
+#include "io/rc_controls.h"
+#include "io/serial.h"
+#include "io/ledstrip.h"
+#include "io/flashfs.h"
+#include "io/beeper.h"
+
+#include "rx/rx.h"
+#include "rx/spektrum.h"
+
+#include "sensors/battery.h"
+#include "sensors/boardalignment.h"
+#include "sensors/sensors.h"
+#include "sensors/acceleration.h"
+#include "sensors/gyro.h"
+#include "sensors/compass.h"
+#include "sensors/barometer.h"
+
+#include "flight/pid.h"
+#include "flight/imu.h"
+#include "flight/mixer.h"
+#include "flight/navigation.h"
+#include "flight/failsafe.h"
+
+#include "telemetry/telemetry.h"
+#include "telemetry/frsky.h"
+
+#include "config/runtime_config.h"
+#include "config/config.h"
+#include "config/config_profile.h"
+#include "config/config_master.h"
+
+#include "common/printf.h"
+
+#include "serial_cli.h"
+
+// FIXME remove this for targets that don't need a CLI. Perhaps use a no-op macro when USE_CLI is not enabled
+// signal that we're in cli mode
+uint8_t cliMode = 0;
+
+#ifdef USE_CLI
+
+extern uint16_t cycleTime; // FIXME dependency on mw.c
+
+void gpsEnablePassthrough(serialPort_t *gpsPassthroughPort);
+
+static serialPort_t *cliPort;
+static bufWriter_t *cliWriter;
+static uint8_t cliWriteBuffer[sizeof(*cliWriter) + 16];
+
+static void cliAux(char *cmdline);
+static void cliRxFail(char *cmdline);
+static void cliAdjustmentRange(char *cmdline);
+static void cliMotorMix(char *cmdline);
+static void cliDefaults(char *cmdline);
+static void cliDump(char *cmdLine);
+static void cliExit(char *cmdline);
+static void cliFeature(char *cmdline);
+static void cliMotor(char *cmdline);
+static void cliPlaySound(char *cmdline);
+static void cliProfile(char *cmdline);
+static void cliRateProfile(char *cmdline);
+static void cliReboot(void);
+static void cliSave(char *cmdline);
+static void cliSerial(char *cmdline);
+
+#ifdef USE_SERVOS
+static void cliServo(char *cmdline);
+static void cliServoMix(char *cmdline);
+#endif
+
+static void cliSet(char *cmdline);
+static void cliGet(char *cmdline);
+static void cliStatus(char *cmdline);
+#ifndef SKIP_TASK_STATISTICS
+static void cliTasks(char *cmdline);
+#endif
+static void cliVersion(char *cmdline);
+static void cliRxRange(char *cmdline);
+
+#ifdef GPS
+static void cliGpsPassthrough(char *cmdline);
+#endif
+
+static void cliHelp(char *cmdline);
+static void cliMap(char *cmdline);
+
+#ifdef LED_STRIP
+static void cliLed(char *cmdline);
+static void cliColor(char *cmdline);
+#endif
+
+#ifndef USE_QUAD_MIXER_ONLY
+static void cliMixer(char *cmdline);
+#endif
+
+#ifdef USE_FLASHFS
+static void cliFlashInfo(char *cmdline);
+static void cliFlashErase(char *cmdline);
+#ifdef USE_FLASH_TOOLS
+static void cliFlashWrite(char *cmdline);
+static void cliFlashRead(char *cmdline);
+#endif
+#endif
+
+// buffer
+static char cliBuffer[48];
+static uint32_t bufferIndex = 0;
+
+#ifndef USE_QUAD_MIXER_ONLY
+// sync this with mixerMode_e
+static const char * const mixerNames[] = {
+ "TRI", "QUADP", "QUADX", "BI",
+ "GIMBAL", "Y6", "HEX6",
+ "FLYING_WING", "Y4", "HEX6X", "OCTOX8", "OCTOFLATP", "OCTOFLATX",
+ "AIRPLANE", "HELI_120_CCPM", "HELI_90_DEG", "VTAIL4",
+ "HEX6H", "PPM_TO_SERVO", "DUALCOPTER", "SINGLECOPTER",
+ "ATAIL4", "CUSTOM", "CUSTOMAIRPLANE", "CUSTOMTRI", "QUADX1234", NULL
+};
+#endif
+
+// sync this with features_e
+static const char * const featureNames[] = {
+ "RX_PPM", "VBAT", "INFLIGHT_ACC_CAL", "RX_SERIAL", "MOTOR_STOP",
+ "SERVO_TILT", "SOFTSERIAL", "GPS", "FAILSAFE",
+ "SONAR", "TELEMETRY", "CURRENT_METER", "3D", "RX_PARALLEL_PWM",
+ "RX_MSP", "RSSI_ADC", "LED_STRIP", "DISPLAY", "ONESHOT125",
+ "BLACKBOX", "CHANNEL_FORWARDING", NULL
+};
+
+// sync this with rxFailsafeChannelMode_e
+static const char rxFailsafeModeCharacters[] = "ahs";
+
+static const rxFailsafeChannelMode_e rxFailsafeModesTable[RX_FAILSAFE_TYPE_COUNT][RX_FAILSAFE_MODE_COUNT] = {
+ { RX_FAILSAFE_MODE_AUTO, RX_FAILSAFE_MODE_HOLD, RX_FAILSAFE_MODE_INVALID },
+ { RX_FAILSAFE_MODE_INVALID, RX_FAILSAFE_MODE_HOLD, RX_FAILSAFE_MODE_SET }
+};
+
+#ifndef CJMCU
+// sync this with sensors_e
+static const char * const sensorTypeNames[] = {
+ "GYRO", "ACC", "BARO", "MAG", "SONAR", "GPS", "GPS+MAG", NULL
+};
+
+#define SENSOR_NAMES_MASK (SENSOR_GYRO | SENSOR_ACC | SENSOR_BARO | SENSOR_MAG)
+
+static const char * const sensorHardwareNames[4][11] = {
+ { "", "None", "MPU6050", "L3G4200D", "MPU3050", "L3GD20", "MPU6000", "MPU6500", "FAKE", NULL },
+ { "", "None", "ADXL345", "MPU6050", "MMA845x", "BMA280", "LSM303DLHC", "MPU6000", "MPU6500", "FAKE", NULL },
+ { "", "None", "BMP085", "MS5611", "BMP280", NULL },
+ { "", "None", "HMC5883", "AK8975", "AK8963", NULL }
+};
+#endif
+
+typedef struct {
+ const char *name;
+#ifndef SKIP_CLI_COMMAND_HELP
+ const char *description;
+ const char *args;
+#endif
+ void (*func)(char *cmdline);
+} clicmd_t;
+
+#ifndef SKIP_CLI_COMMAND_HELP
+#define CLI_COMMAND_DEF(name, description, args, method) \
+{ \
+ name , \
+ description , \
+ args , \
+ method \
+}
+#else
+#define CLI_COMMAND_DEF(name, description, args, method) \
+{ \
+ name, \
+ method \
+}
+#endif
+
+// should be sorted a..z for bsearch()
+const clicmd_t cmdTable[] = {
+ CLI_COMMAND_DEF("adjrange", "configure adjustment ranges", NULL, cliAdjustmentRange),
+ CLI_COMMAND_DEF("aux", "configure modes", NULL, cliAux),
+#ifdef LED_STRIP
+ CLI_COMMAND_DEF("color", "configure colors", NULL, cliColor),
+#endif
+ CLI_COMMAND_DEF("defaults", "reset to defaults and reboot", NULL, cliDefaults),
+ CLI_COMMAND_DEF("dump", "dump configuration",
+ "[master|profile]", cliDump),
+ CLI_COMMAND_DEF("exit", NULL, NULL, cliExit),
+ CLI_COMMAND_DEF("feature", "configure features",
+ "list\r\n"
+ "\t<+|->[name]", cliFeature),
+#ifdef USE_FLASHFS
+ CLI_COMMAND_DEF("flash_erase", "erase flash chip", NULL, cliFlashErase),
+ CLI_COMMAND_DEF("flash_info", "show flash chip info", NULL, cliFlashInfo),
+#ifdef USE_FLASH_TOOLS
+ CLI_COMMAND_DEF("flash_read", NULL, " ", cliFlashRead),
+ CLI_COMMAND_DEF("flash_write", NULL, " ", cliFlashWrite),
+#endif
+#endif
+ CLI_COMMAND_DEF("get", "get variable value",
+ "[name]", cliGet),
+#ifdef GPS
+ CLI_COMMAND_DEF("gpspassthrough", "passthrough gps to serial", NULL, cliGpsPassthrough),
+#endif
+ CLI_COMMAND_DEF("help", NULL, NULL, cliHelp),
+#ifdef LED_STRIP
+ CLI_COMMAND_DEF("led", "configure leds", NULL, cliLed),
+#endif
+ CLI_COMMAND_DEF("map", "configure rc channel order",
+ "[