diff --git a/src/VehicleSetup/FirmwareUpgrade.qml b/src/VehicleSetup/FirmwareUpgrade.qml
index d20ee293134aa3861f6c1d1ef30ce2d551d356dd..adf0b7ba9167b7d171206836041d4e0b6364eab6 100644
--- a/src/VehicleSetup/FirmwareUpgrade.qml
+++ b/src/VehicleSetup/FirmwareUpgrade.qml
@@ -45,6 +45,8 @@ QGCView {
     property bool   initialBoardSearch:       true
     property string firmwareName
 
+    property bool _singleFirmwareMode: QGroundControl.corePlugin.options.firmwareUpgradeSingleURL.length != 0   ///< true: running in special single firmware download mode
+
     function cancelFlash() {
         statusTextArea.append(highlightPrefix + qsTr("Upgrade cancelled") + highlightSuffix)
         statusTextArea.append("------------------------------------------")
@@ -147,17 +149,21 @@ QGCView {
 
             function accept() {
                 hideDialog()
-                var stack = apmFlightStack.checked ? FirmwareUpgradeController.AutoPilotStackAPM : FirmwareUpgradeController.AutoPilotStackPX4
-                if (px4Flow) {
-                    stack = FirmwareUpgradeController.PX4Flow
-                }
+                if (_singleFirmwareMode) {
+                    controller.flashSingleFirmwareMode()
+                } else {
+                    var stack = apmFlightStack.checked ? FirmwareUpgradeController.AutoPilotStackAPM : FirmwareUpgradeController.AutoPilotStackPX4
+                    if (px4Flow) {
+                        stack = FirmwareUpgradeController.PX4Flow
+                    }
 
-                var firmwareType = firmwareVersionCombo.model.get(firmwareVersionCombo.currentIndex).firmwareType
-                var vehicleType = FirmwareUpgradeController.DefaultVehicleFirmware
-                if (apmFlightStack.checked) {
-                    vehicleType = controller.vehicleTypeFromVersionIndex(vehicleTypeSelectionCombo.currentIndex)
+                    var firmwareType = firmwareVersionCombo.model.get(firmwareVersionCombo.currentIndex).firmwareType
+                    var vehicleType = FirmwareUpgradeController.DefaultVehicleFirmware
+                    if (apmFlightStack.checked) {
+                        vehicleType = controller.vehicleTypeFromVersionIndex(vehicleTypeSelectionCombo.currentIndex)
+                    }
+                    controller.flash(stack, firmwareType, vehicleType)
                 }
-                controller.flash(stack, firmwareType, vehicleType)
             }
 
             function reject() {
@@ -203,6 +209,19 @@ QGCView {
                 }
             }
 
+            ListModel {
+                id: singleFirmwareModeTypeList
+
+                ListElement {
+                    text:           qsTr("Standard Version")
+                    firmwareType:   FirmwareUpgradeController.StableFirmware
+                }
+                ListElement {
+                    text:           qsTr("Custom firmware file...")
+                    firmwareType:   FirmwareUpgradeController.CustomFirmware
+                }
+            }
+
             Column {
                 anchors.fill:   parent
                 spacing:        defaultTextHeight
@@ -210,7 +229,11 @@ QGCView {
                 QGCLabel {
                     width:      parent.width
                     wrapMode:   Text.WordWrap
-                    text:       px4Flow ? "Detected PX4 Flow board. You can select from the following firmware:" : "Detected Pixhawk board. You can select from the following flight stacks:"
+                    text:       _singleFirmwareMode ? _singleFirmwareLabel : (px4Flow ? _px4FlowLabel : _pixhawkLabel)
+
+                    readonly property string _px4FlowLabel:          qsTr("Detected PX4 Flow board. You can select from the following firmware:")
+                    readonly property string _pixhawkLabel:          qsTr("Detected Pixhawk board. You can select from the following flight stacks:")
+                    readonly property string _singleFirmwareLabel:   qsTr("Press Ok to upgrade your vehicle.")
                 }
 
                 function firmwareVersionChanged(model) {
@@ -229,7 +252,7 @@ QGCView {
                     checked:        true
                     exclusiveGroup: firmwareGroup
                     text:           qsTr("PX4 Flight Stack ")
-                    visible:        !px4Flow
+                    visible:        !_singleFirmwareMode && !px4Flow
 
                     onClicked: parent.firmwareVersionChanged(firmwareTypeList)
                 }
@@ -238,7 +261,7 @@ QGCView {
                     id:             apmFlightStack
                     exclusiveGroup: firmwareGroup
                     text:           qsTr("ArduPilot Flight Stack")
-                    visible:        !px4Flow
+                    visible:        !_singleFirmwareMode && !px4Flow
 
                     onClicked: parent.firmwareVersionChanged(firmwareTypeList)
                 }
@@ -295,7 +318,7 @@ QGCView {
                     anchors.left:   parent.left
                     anchors.right:  parent.right
                     visible:        showFirmwareTypeSelection
-                    model:          px4Flow ? px4FlowTypeList : firmwareTypeList
+                    model:          _singleFirmwareMode ? singleFirmwareModeTypeList: (px4Flow ? px4FlowTypeList : firmwareTypeList)
                     currentIndex:   controller.selectedFirmwareType
 
                     onActivated: {
diff --git a/src/VehicleSetup/FirmwareUpgradeController.cc b/src/VehicleSetup/FirmwareUpgradeController.cc
index bf83ef8cc5ee9e36ee052464eb3268279211b202..0378805d88ef9825f037d7ac05e46494597031f2 100644
--- a/src/VehicleSetup/FirmwareUpgradeController.cc
+++ b/src/VehicleSetup/FirmwareUpgradeController.cc
@@ -17,6 +17,8 @@
 #include "QGCFileDialog.h"
 #include "QGCApplication.h"
 #include "QGCFileDownload.h"
+#include "QGCOptions.h"
+#include "QGCCorePlugin.h"
 
 #include <QStandardPaths>
 #include <QRegularExpression>
@@ -41,7 +43,9 @@ uint qHash(const FirmwareUpgradeController::FirmwareIdentifier& firmwareId)
 
 /// @Brief Constructs a new FirmwareUpgradeController Widget. This widget is used within the PX4VehicleConfig set of screens.
 FirmwareUpgradeController::FirmwareUpgradeController(void)
-    : _downloadManager(NULL)
+    : _singleFirmwareURL(qgcApp()->toolbox()->corePlugin()->options()->firmwareUpgradeSingleURL())
+    , _singleFirmwareMode(!_singleFirmwareURL.isEmpty())
+    , _downloadManager(NULL)
     , _downloadNetworkReply(NULL)
     , _statusLog(NULL)
     , _selectedFirmwareType(StableFirmware)
@@ -110,6 +114,11 @@ void FirmwareUpgradeController::flash(const FirmwareIdentifier& firmwareId)
     flash(firmwareId.autopilotStackType, firmwareId.firmwareType, firmwareId.firmwareVehicleType);
 }
 
+void FirmwareUpgradeController::flashSingleFirmwareMode(void)
+{
+    flash(SingleFirmwareMode, StableFirmware, DefaultVehicleFirmware);
+}
+
 void FirmwareUpgradeController::cancel(void)
 {
     _eraseTimer.stop();
@@ -202,7 +211,8 @@ void FirmwareUpgradeController::_initFirmwareHash()
         { AutoPilotStackAPM, DeveloperFirmware, CopterFirmware,         "http://firmware.ardupilot.org/Copter/latest/PX4/ArduCopter-v4.px4"},
         { AutoPilotStackAPM, DeveloperFirmware, HeliFirmware,           "http://firmware.ardupilot.org/Copter/latest/PX4-heli/ArduCopter-v4.px4"},
         { AutoPilotStackAPM, DeveloperFirmware, PlaneFirmware,          "http://firmware.ardupilot.org/Plane/latest/PX4/ArduPlane-v4.px4"},
-        { AutoPilotStackAPM, DeveloperFirmware, RoverFirmware,          "http://firmware.ardupilot.org/Rover/latest/PX4/APMrover2-v4.px4"}
+        { AutoPilotStackAPM, DeveloperFirmware, RoverFirmware,          "http://firmware.ardupilot.org/Rover/latest/PX4/APMrover2-v4.px4"},
+        { SingleFirmwareMode,StableFirmware,    DefaultVehicleFirmware, _singleFirmwareURL},
     };
 
     //////////////////////////////////// PX4FMUV2 firmwares //////////////////////////////////////////////////
@@ -228,6 +238,7 @@ void FirmwareUpgradeController::_initFirmwareHash()
         { AutoPilotStackAPM, DeveloperFirmware, PlaneFirmware,          "http://firmware.ardupilot.org/Plane/latest/PX4/ArduPlane-v2.px4"},
         { AutoPilotStackAPM, DeveloperFirmware, RoverFirmware,          "http://firmware.ardupilot.org/Rover/latest/PX4/APMrover2-v2.px4"},
         { AutoPilotStackAPM, DeveloperFirmware, SubFirmware,            "http://firmware.ardupilot.org/Sub/latest/PX4/ArduSub-v2.px4"},
+        { SingleFirmwareMode,StableFirmware,    DefaultVehicleFirmware, _singleFirmwareURL},
     };
 
     //////////////////////////////////// PX4FMU aerocore firmwares //////////////////////////////////////////////////
@@ -299,6 +310,7 @@ void FirmwareUpgradeController::_initFirmwareHash()
         { AutoPilotStackPX4, StableFirmware,    DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/tap-v1_default.px4"},
         { AutoPilotStackPX4, BetaFirmware,      DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/tap-v1_default.px4"},
         { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/tap-v1_default.px4"},
+        { SingleFirmwareMode,StableFirmware,    DefaultVehicleFirmware, _singleFirmwareURL},
     };
     //////////////////////////////////// ASCV1 firmwares //////////////////////////////////////////////////
     FirmwareToUrlElement_t rgASCV1FirmwareArray[] = {
@@ -376,6 +388,12 @@ void FirmwareUpgradeController::_initFirmwareHash()
         const FirmwareToUrlElement_t& element = rg3DRRadioFirmwareArray[i];
         _rg3DRRadioFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
     }
+
+    size = sizeof(rg3DRRadioFirmwareArray)/sizeof(rg3DRRadioFirmwareArray[0]);
+    for (int i = 0; i < size; i++) {
+        const FirmwareToUrlElement_t& element = rg3DRRadioFirmwareArray[i];
+        _rg3DRRadioFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url);
+    }
 }
 
 /// @brief Called when the findBootloader process is unable to sync to the bootloader. Moves the state
@@ -429,7 +447,6 @@ void FirmwareUpgradeController::_getFirmwareFile(FirmwareIdentifier firmwareId)
                                                            QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), // Initial directory
                                                            "Firmware Files (*.px4 *.bin *.ihx)");                               // File filter
     } else {
-
         if (prgFirmware->contains(firmwareId)) {
             _firmwareFilename = prgFirmware->value(firmwareId);
         } else {
diff --git a/src/VehicleSetup/FirmwareUpgradeController.h b/src/VehicleSetup/FirmwareUpgradeController.h
index 1209135dfd51ec4d374dd42d00eaba2588e4f361..6ce335d6caa2a0eb561e34b161a111bf05322dd8 100644
--- a/src/VehicleSetup/FirmwareUpgradeController.h
+++ b/src/VehicleSetup/FirmwareUpgradeController.h
@@ -42,7 +42,8 @@ public:
             AutoPilotStackPX4,
             AutoPilotStackAPM,
             PX4Flow,
-            ThreeDRRadio
+            ThreeDRRadio,
+            SingleFirmwareMode
         } AutoPilotStackType_t;
 
         typedef enum {
@@ -122,6 +123,9 @@ public:
                            FirmwareType_t firmwareType = StableFirmware,
                            FirmwareVehicleType_t vehicleType = DefaultVehicleFirmware );
 
+    /// Called to flash when upgrade is running in singleFirmwareMode
+    Q_INVOKABLE void flashSingleFirmwareMode(void);
+
     Q_INVOKABLE FirmwareVehicleType_t vehicleTypeFromVersionIndex(int index);
     
     // overload, not exposed to qml side
@@ -191,6 +195,8 @@ private:
     QHash<FirmwareIdentifier, QString>* _firmwareHashForBoardId(int boardId);
     void _determinePX4StableVersion(void);
 
+    QString _singleFirmwareURL;
+    bool    _singleFirmwareMode;
     QString _portName;
     QString _portDescription;
 
diff --git a/src/api/QGCOptions.h b/src/api/QGCOptions.h
index baf6cb678c8126adacadda4a5ba05ae0b6c20952..d21b5920e7b2f2929aa404983d8bf1662abba620 100644
--- a/src/api/QGCOptions.h
+++ b/src/api/QGCOptions.h
@@ -35,6 +35,7 @@ public:
     Q_PROPERTY(bool                     showSensorCalibrationAirspeed   READ showSensorCalibrationAirspeed  NOTIFY showSensorCalibrationAirspeedChanged)
     Q_PROPERTY(bool                     showSensorCalibrationOrient     READ showSensorCalibrationOrient    NOTIFY showSensorCalibrationOrientChanged)
     Q_PROPERTY(bool                     showFirmwareUpgrade             READ showFirmwareUpgrade            NOTIFY showFirmwareUpgradeChanged)
+    Q_PROPERTY(QString                  firmwareUpgradeSingleURL        READ firmwareUpgradeSingleURL       CONSTANT)
 
     /// Should QGC hide its settings menu and colapse it into one single menu (Settings and Vehicle Setup)?
     /// @return true if QGC should consolidate both menus into one.
@@ -53,14 +54,19 @@ public:
     virtual CustomInstrumentWidget* instrumentWidget();
 
     /// By returning false you can hide the following sensor calibration pages
-    virtual bool showSensorCalibrationCompass   () const { return true; }
-    virtual bool showSensorCalibrationGyro      () const { return true; }
-    virtual bool showSensorCalibrationAccel     () const { return true; }
-    virtual bool showSensorCalibrationLevel     () const { return true; }
-    virtual bool showSensorCalibrationAirspeed  () const { return true; }
-    virtual bool showSensorCalibrationOrient    () const { return true; }
+    virtual bool    showSensorCalibrationCompass    () const { return true; }
+    virtual bool    showSensorCalibrationGyro       () const { return true; }
+    virtual bool    showSensorCalibrationAccel      () const { return true; }
+    virtual bool    showSensorCalibrationLevel      () const { return true; }
+    virtual bool    showSensorCalibrationAirspeed   () const { return true; }
+    virtual bool    showSensorCalibrationOrient     () const { return true; }
 
-    virtual bool showFirmwareUpgrade            () const { return true; }
+    virtual bool    showFirmwareUpgrade             () const { return true; }
+
+    /// If returned QString in non-empty it means that firmware upgrade will run in a mode which only
+    /// supports downloading a single firmware file from the URL. It also supports custom install through
+    /// the Advanced options.
+    virtual QString firmwareUpgradeSingleURL        () const { return QString(); }
 
 signals:
     void showSensorCalibrationCompassChanged    (bool show);