/**************************************************************************** * * (c) 2009-2020 QGROUNDCONTROL PROJECT * * QGroundControl is licensed according to the terms in the file * COPYING.md in the root of the source code directory. * ****************************************************************************/ #include "FirmwareUpgradeController.h" #include "Bootloader.h" #include "QGCApplication.h" #include "QGCFileDownload.h" #include "QGCOptions.h" #include "QGCCorePlugin.h" #include "FirmwareUpgradeSettings.h" #include "SettingsManager.h" #include "QGCZlib.h" #include "JsonHelper.h" #include #include #include #include #include #include #include "zlib.h" const char* FirmwareUpgradeController::_manifestFirmwareJsonKey = "firmware"; const char* FirmwareUpgradeController::_manifestBoardIdJsonKey = "board_id"; const char* FirmwareUpgradeController::_manifestMavTypeJsonKey = "mav-type"; const char* FirmwareUpgradeController::_manifestFormatJsonKey = "format"; const char* FirmwareUpgradeController::_manifestUrlJsonKey = "url"; const char* FirmwareUpgradeController::_manifestMavFirmwareVersionTypeJsonKey = "mav-firmware-version-type"; const char* FirmwareUpgradeController::_manifestUSBIDJsonKey = "USBID"; const char* FirmwareUpgradeController::_manifestMavFirmwareVersionJsonKey = "mav-firmware-version"; const char* FirmwareUpgradeController::_manifestBootloaderStrJsonKey = "bootloader_str"; const char* FirmwareUpgradeController::_manifestLatestKey = "latest"; const char* FirmwareUpgradeController::_manifestPlatformKey = "platform"; const char* FirmwareUpgradeController::_manifestBrandNameKey = "brand_name"; struct FirmwareToUrlElement_t { FirmwareUpgradeController::AutoPilotStackType_t stackType; FirmwareUpgradeController::FirmwareBuildType_t firmwareType; FirmwareUpgradeController::FirmwareVehicleType_t vehicleType; QString url; }; uint qHash(const FirmwareUpgradeController::FirmwareIdentifier& firmwareId) { return static_cast(( firmwareId.autopilotStackType | (firmwareId.firmwareType << 8) | (firmwareId.firmwareVehicleType << 16) )); } /// @Brief Constructs a new FirmwareUpgradeController Widget. This widget is used within the PX4VehicleConfig set of screens. FirmwareUpgradeController::FirmwareUpgradeController(void) : _singleFirmwareURL (qgcApp()->toolbox()->corePlugin()->options()->firmwareUpgradeSingleURL()) , _singleFirmwareMode (!_singleFirmwareURL.isEmpty()) , _downloadingFirmwareList (false) , _downloadManager (nullptr) , _downloadNetworkReply (nullptr) , _statusLog (nullptr) , _selectedFirmwareBuildType (StableFirmware) , _image (nullptr) , _apmBoardDescriptionReplaceText ("") , _apmChibiOSSetting (qgcApp()->toolbox()->settingsManager()->firmwareUpgradeSettings()->apmChibiOS()) , _apmVehicleTypeSetting (qgcApp()->toolbox()->settingsManager()->firmwareUpgradeSettings()->apmVehicleType()) { _manifestMavFirmwareVersionTypeToFirmwareBuildTypeMap["OFFICIAL"] = StableFirmware; _manifestMavFirmwareVersionTypeToFirmwareBuildTypeMap["BETA"] = BetaFirmware; _manifestMavFirmwareVersionTypeToFirmwareBuildTypeMap["DEV"] = DeveloperFirmware; _manifestMavTypeToFirmwareVehicleTypeMap["Copter"] = CopterFirmware; _manifestMavTypeToFirmwareVehicleTypeMap["HELICOPTER"] = HeliFirmware; _manifestMavTypeToFirmwareVehicleTypeMap["FIXED_WING"] = PlaneFirmware; _manifestMavTypeToFirmwareVehicleTypeMap["GROUND_ROVER"] = RoverFirmware; _manifestMavTypeToFirmwareVehicleTypeMap["SUBMARINE"] = SubFirmware; _threadController = new PX4FirmwareUpgradeThreadController(this); Q_CHECK_PTR(_threadController); connect(_threadController, &PX4FirmwareUpgradeThreadController::foundBoard, this, &FirmwareUpgradeController::_foundBoard); connect(_threadController, &PX4FirmwareUpgradeThreadController::noBoardFound, this, &FirmwareUpgradeController::_noBoardFound); connect(_threadController, &PX4FirmwareUpgradeThreadController::boardGone, this, &FirmwareUpgradeController::_boardGone); connect(_threadController, &PX4FirmwareUpgradeThreadController::foundBoardInfo, this, &FirmwareUpgradeController::_foundBoardInfo); connect(_threadController, &PX4FirmwareUpgradeThreadController::error, this, &FirmwareUpgradeController::_error); connect(_threadController, &PX4FirmwareUpgradeThreadController::updateProgress, this, &FirmwareUpgradeController::_updateProgress); connect(_threadController, &PX4FirmwareUpgradeThreadController::status, this, &FirmwareUpgradeController::_status); connect(_threadController, &PX4FirmwareUpgradeThreadController::eraseStarted, this, &FirmwareUpgradeController::_eraseStarted); connect(_threadController, &PX4FirmwareUpgradeThreadController::eraseComplete, this, &FirmwareUpgradeController::_eraseComplete); connect(_threadController, &PX4FirmwareUpgradeThreadController::flashComplete, this, &FirmwareUpgradeController::_flashComplete); connect(_threadController, &PX4FirmwareUpgradeThreadController::updateProgress, this, &FirmwareUpgradeController::_updateProgress); connect(&_eraseTimer, &QTimer::timeout, this, &FirmwareUpgradeController::_eraseProgressTick); #if !defined(NO_ARDUPILOT_DIALECT) connect(_apmChibiOSSetting, &Fact::rawValueChanged, this, &FirmwareUpgradeController::_buildAPMFirmwareNames); connect(_apmVehicleTypeSetting, &Fact::rawValueChanged, this, &FirmwareUpgradeController::_buildAPMFirmwareNames); #endif _initFirmwareHash(); _determinePX4StableVersion(); #if !defined(NO_ARDUPILOT_DIALECT) _downloadArduPilotManifest(); #endif } FirmwareUpgradeController::~FirmwareUpgradeController() { qgcApp()->toolbox()->linkManager()->setConnectionsAllowed(); } void FirmwareUpgradeController::startBoardSearch(void) { LinkManager* linkMgr = qgcApp()->toolbox()->linkManager(); linkMgr->setConnectionsSuspended(tr("Connect not allowed during Firmware Upgrade.")); // FIXME: Why did we get here with active vehicle? if (!qgcApp()->toolbox()->multiVehicleManager()->activeVehicle()) { // We have to disconnect any inactive links linkMgr->disconnectAll(); } _bootloaderFound = false; _startFlashWhenBootloaderFound = false; _threadController->startFindBoardLoop(); } void FirmwareUpgradeController::flash(AutoPilotStackType_t stackType, FirmwareBuildType_t firmwareType, FirmwareVehicleType_t vehicleType) { qCDebug(FirmwareUpgradeLog) << "_flash stackType:firmwareType:vehicleType" << stackType << firmwareType << vehicleType; FirmwareIdentifier firmwareId = FirmwareIdentifier(stackType, firmwareType, vehicleType); if (_bootloaderFound) { _getFirmwareFile(firmwareId); } else { // We haven't found the bootloader yet. Need to wait until then to flash _startFlashWhenBootloaderFound = true; _startFlashWhenBootloaderFoundFirmwareIdentity = firmwareId; _firmwareFilename.clear(); } } void FirmwareUpgradeController::flashFirmwareUrl(QString firmwareFlashUrl) { _firmwareFilename = firmwareFlashUrl; if (_bootloaderFound) { _downloadFirmware(); } else { // We haven't found the bootloader yet. Need to wait until then to flash _startFlashWhenBootloaderFound = true; } } void FirmwareUpgradeController::flash(const FirmwareIdentifier& firmwareId) { flash(firmwareId.autopilotStackType, firmwareId.firmwareType, firmwareId.firmwareVehicleType); } void FirmwareUpgradeController::flashSingleFirmwareMode(FirmwareBuildType_t firmwareType) { flash(SingleFirmwareMode, firmwareType, DefaultVehicleFirmware); } void FirmwareUpgradeController::cancel(void) { _eraseTimer.stop(); _threadController->cancel(); } QStringList FirmwareUpgradeController::availableBoardsName(void) { QGCSerialPortInfo::BoardType_t boardType; QString boardName; QStringList names; auto ports = QGCSerialPortInfo::availablePorts(); for(const auto info : ports) { if(info.canFlash()) { info.getBoardInfo(boardType, boardName); names.append(boardName); } } return names; } void FirmwareUpgradeController::_foundBoard(bool firstAttempt, const QSerialPortInfo& info, int boardType, QString boardName) { _boardInfo = info; _boardType = static_cast(boardType); _boardTypeName = boardName; qDebug() << info.manufacturer() << info.description(); _startFlashWhenBootloaderFound = false; if (_boardType == QGCSerialPortInfo::BoardTypeSiKRadio) { if (!firstAttempt) { // Radio always flashes latest firmware, so we can start right away without // any further user input. _startFlashWhenBootloaderFound = true; _startFlashWhenBootloaderFoundFirmwareIdentity = FirmwareIdentifier(ThreeDRRadio, StableFirmware, DefaultVehicleFirmware); } } qCDebug(FirmwareUpgradeLog) << _boardType << _boardTypeName; emit boardFound(); } void FirmwareUpgradeController::_noBoardFound(void) { emit noBoardFound(); } void FirmwareUpgradeController::_boardGone(void) { emit boardGone(); } /// @brief Called when the bootloader is connected to by the findBootloader process. Moves the state machine /// to the next step. void FirmwareUpgradeController::_foundBoardInfo(int bootloaderVersion, int boardID, int flashSize) { _bootloaderFound = true; _bootloaderVersion = static_cast(bootloaderVersion); _bootloaderBoardID = static_cast(boardID); _bootloaderBoardFlashSize = static_cast(flashSize); _appendStatusLog(tr("Connected to bootloader:")); _appendStatusLog(tr(" Version: %1").arg(_bootloaderVersion)); _appendStatusLog(tr(" Board ID: %1").arg(_bootloaderBoardID)); _appendStatusLog(tr(" Flash size: %1").arg(_bootloaderBoardFlashSize)); if (_startFlashWhenBootloaderFound) { flash(_startFlashWhenBootloaderFoundFirmwareIdentity); } if (_rgManifestFirmwareInfo.count()) { _buildAPMFirmwareNames(); } emit bootloaderFound(); } /// @brief intializes the firmware hashes with proper urls. /// This happens only once for a class instance first time when it is needed. void FirmwareUpgradeController::_initFirmwareHash() { // indirect check whether this function has been called before or not // may have to be modified if _rgPX4FMUV2Firmware disappears if (!_rgPX4FMUV2Firmware.isEmpty()) { return; } //////////////////////////////////// PX4FMU aerocore firmwares ////////////////////////////////////////////////// FirmwareToUrlElement_t rgAeroCoreFirmwareArray[] = { { AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware, "http://gumstix-aerocore.s3.amazonaws.com/PX4/stable/aerocore_default.px4"}, { AutoPilotStackPX4, BetaFirmware, DefaultVehicleFirmware, "http://gumstix-aerocore.s3.amazonaws.com/PX4/beta/aerocore_default.px4"}, { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://gumstix-aerocore.s3.amazonaws.com/PX4/master/aerocore_default.px4"}, }; //////////////////////////////////// AUAVX2_1 firmwares ////////////////////////////////////////////////// FirmwareToUrlElement_t rgAUAVX2_1FirmwareArray[] = { { AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/auav-x21_default.px4"}, { AutoPilotStackPX4, BetaFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/auav-x21_default.px4"}, { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/auav-x21_default.px4"}, }; //////////////////////////////////// MindPXFMUV2 firmwares ////////////////////////////////////////////////// FirmwareToUrlElement_t rgMindPXFMUV2FirmwareArray[] = { { AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/mindpx-v2_default.px4"}, { AutoPilotStackPX4, BetaFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/mindpx-v2_default.px4"}, { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/mindpx-v2_default.px4"}, }; //////////////////////////////////// TAPV1 firmwares ////////////////////////////////////////////////// FirmwareToUrlElement_t rgTAPV1FirmwareArray[] = { { 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[] = { { AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/asc-v1_default.px4"}, { AutoPilotStackPX4, BetaFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/asc-v1_default.px4"}, { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/asc-v1_default.px4"}, }; //////////////////////////////////// Crazyflie 2.0 firmwares ////////////////////////////////////////////////// FirmwareToUrlElement_t rgCrazyflie2FirmwareArray[] = { { AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/crazyflie_default.px4"}, { AutoPilotStackPX4, BetaFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/crazyflie_default.px4"}, { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/crazyflie_default.px4"}, }; //////////////////////////////////// Omnibus F4 SD firmwares ////////////////////////////////////////////////// FirmwareToUrlElement_t rgOmnibusF4SDFirmwareArray[] = { { AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/omnibus_f4sd_default.px4"}, { AutoPilotStackPX4, BetaFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/omnibus_f4sd_default.px4"}, { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/omnibus_f4sd_default.px4"}, }; //////////////////////////////////// FMUK66V3 firmwares ////////////////////////////////////////////////// FirmwareToUrlElement_t rgFMUK66V3FirmwareArray[] = { { AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/nxp_fmuk66-v3_default.px4"}, { AutoPilotStackPX4, BetaFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/nxp_fmuk66-v3_default.px4"}, { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/nxp_fmuk66-v3_default.px4"}, }; //////////////////////////////////// Kakute F7 firmwares ////////////////////////////////////////////////// FirmwareToUrlElement_t rgKakuteF7FirmwareArray[] = { { AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/holybro_kakutef7_default.px4"}, { AutoPilotStackPX4, BetaFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/holybro_kakutef7_default.px4"}, { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/holybro_kakutef7_default.px4"}, }; //////////////////////////////////// Durandal firmwares ////////////////////////////////////////////////// FirmwareToUrlElement_t rgDurandalV1FirmwareArray[] = { { AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/holybro_durandal-v1_default.px4"}, { AutoPilotStackPX4, BetaFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/holybro_durandal-v1_default.px4"}, { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/holybro_durandal-v1_default.px4"}, }; //////////////////////////////////// ModalAI FC v1 firmwares ////////////////////////////////////////////////// FirmwareToUrlElement_t rgModalFCV1FirmwareArray[] = { { AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/modalai_fc-v1_default.px4"}, { AutoPilotStackPX4, BetaFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/modalai_fc-v1_default.px4"}, { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/modalai_fc-v1_default.px4"}, }; //////////////////////////////////// mRo Control Zero firmwares ////////////////////////////////////////////////// FirmwareToUrlElement_t rgmRoCtrlZero[] = { { AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/mro_ctrl-zero-f7_default.px4"}, { AutoPilotStackPX4, BetaFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/mro_ctrl-zero-f7_default.px4"}, { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/mro_ctrl-zero-f7_default.px4"}, }; //////////////////////////////////// UVify FC firmwares ////////////////////////////////////////////////// FirmwareToUrlElement_t rgUVifyCoreFirmwareArray[] = { { AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/uvify_core_default.px4"}, { AutoPilotStackPX4, BetaFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/uvify_core_default.px4"}, { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/uvify_core_default.px4"}, }; /////////////////////////////// px4flow firmwares /////////////////////////////////////// FirmwareToUrlElement_t rgPX4FLowFirmwareArray[] = { { PX4FlowPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Flow/master/px4flow.px4" }, #if !defined(NO_ARDUPILOT_DIALECT) { PX4FlowAPM, StableFirmware, DefaultVehicleFirmware, "http://firmware.ardupilot.org/Tools/PX4Flow/px4flow-klt-latest.px4" }, #endif }; /////////////////////////////// 3dr radio firmwares /////////////////////////////////////// FirmwareToUrlElement_t rg3DRRadioFirmwareArray[] = { { ThreeDRRadio, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/SiK/stable/radio~hm_trp.ihx"} }; // We build the maps for PX4 firmwares dynamically using the data below #if 0 Example URLs for PX4 and ArduPilot { AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/px4fmu-v4_default.px4"}, { AutoPilotStackAPM, StableFirmware, CopterFirmware, "http://firmware.ardupilot.org/Copter/stable/PX4/ArduCopter-v4.px4"}, { AutoPilotStackAPM, DeveloperFirmware, CopterChibiosFirmware, "http://firmware.ardupilot.org/Copter/latest/fmuv4/arducopter.apj"}, #endif QString px4Url ("http://px4-travis.s3.amazonaws.com/Firmware/%1/px4fmu-%2_default.px4"); QMap px4MapFirmwareTypeToDir; px4MapFirmwareTypeToDir[StableFirmware] = QStringLiteral("stable"); px4MapFirmwareTypeToDir[BetaFirmware] = QStringLiteral("beta"); px4MapFirmwareTypeToDir[DeveloperFirmware] = QStringLiteral("master"); // PX4 Firmwares for (const FirmwareBuildType_t& firmwareType: px4MapFirmwareTypeToDir.keys()) { QString dir = px4MapFirmwareTypeToDir[firmwareType]; _rgFMUV5Firmware.insert (FirmwareIdentifier(AutoPilotStackPX4, firmwareType, DefaultVehicleFirmware), px4Url.arg(dir).arg("v5")); _rgFMUV4PROFirmware.insert (FirmwareIdentifier(AutoPilotStackPX4, firmwareType, DefaultVehicleFirmware), px4Url.arg(dir).arg("v4pro")); _rgFMUV4Firmware.insert (FirmwareIdentifier(AutoPilotStackPX4, firmwareType, DefaultVehicleFirmware), px4Url.arg(dir).arg("v4")); _rgFMUV3Firmware.insert (FirmwareIdentifier(AutoPilotStackPX4, firmwareType, DefaultVehicleFirmware), px4Url.arg(dir).arg("v3")); _rgPX4FMUV2Firmware.insert (FirmwareIdentifier(AutoPilotStackPX4, firmwareType, DefaultVehicleFirmware), px4Url.arg(dir).arg("v2")); } QString px4CUAVX7Url ("http://px4-travis.s3.amazonaws.com/Firmware/%1/cuav_%2_default.px4"); for (const FirmwareBuildType_t& firmwareType: px4MapFirmwareTypeToDir.keys()) { QString dir = px4MapFirmwareTypeToDir[firmwareType]; _rgPX4CUAVX7Fireware.insert (FirmwareIdentifier(AutoPilotStackPX4, firmwareType, DefaultVehicleFirmware), px4CUAVX7Url.arg(dir).arg("x7pro")); } int size = sizeof(rgAeroCoreFirmwareArray)/sizeof(rgAeroCoreFirmwareArray[0]); for (int i = 0; i < size; i++) { const FirmwareToUrlElement_t& element = rgAeroCoreFirmwareArray[i]; _rgAeroCoreFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); } size = sizeof(rgAUAVX2_1FirmwareArray)/sizeof(rgAUAVX2_1FirmwareArray[0]); for (int i = 0; i < size; i++) { const FirmwareToUrlElement_t& element = rgAUAVX2_1FirmwareArray[i]; _rgAUAVX2_1Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); } size = sizeof(rgMindPXFMUV2FirmwareArray)/sizeof(rgMindPXFMUV2FirmwareArray[0]); for (int i = 0; i < size; i++) { const FirmwareToUrlElement_t& element = rgMindPXFMUV2FirmwareArray[i]; _rgMindPXFMUV2Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); } size = sizeof(rgTAPV1FirmwareArray)/sizeof(rgTAPV1FirmwareArray[0]); for (int i = 0; i < size; i++) { const FirmwareToUrlElement_t& element = rgTAPV1FirmwareArray[i]; _rgTAPV1Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); } size = sizeof(rgASCV1FirmwareArray)/sizeof(rgASCV1FirmwareArray[0]); for (int i = 0; i < size; i++) { const FirmwareToUrlElement_t& element = rgASCV1FirmwareArray[i]; _rgASCV1Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); } size = sizeof(rgCrazyflie2FirmwareArray)/sizeof(rgCrazyflie2FirmwareArray[0]); for (int i = 0; i < size; i++) { const FirmwareToUrlElement_t& element = rgCrazyflie2FirmwareArray[i]; _rgCrazyflie2Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); } size = sizeof(rgOmnibusF4SDFirmwareArray)/sizeof(rgOmnibusF4SDFirmwareArray[0]); for (int i = 0; i < size; i++) { const FirmwareToUrlElement_t& element = rgOmnibusF4SDFirmwareArray[i]; _rgOmnibusF4SDFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); } size = sizeof(rgKakuteF7FirmwareArray)/sizeof(rgKakuteF7FirmwareArray[0]); for (int i = 0; i < size; i++) { const FirmwareToUrlElement_t& element = rgKakuteF7FirmwareArray[i]; _rgKakuteF7Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); } size = sizeof(rgDurandalV1FirmwareArray)/sizeof(rgDurandalV1FirmwareArray[0]); for (int i = 0; i < size; i++) { const FirmwareToUrlElement_t& element = rgDurandalV1FirmwareArray[i]; _rgDurandalV1Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); } size = sizeof(rgFMUK66V3FirmwareArray)/sizeof(rgFMUK66V3FirmwareArray[0]); for (int i = 0; i < size; i++) { const FirmwareToUrlElement_t& element = rgFMUK66V3FirmwareArray[i]; _rgFMUK66V3Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); } size = sizeof(rgModalFCV1FirmwareArray)/sizeof(rgModalFCV1FirmwareArray[0]); for (int i = 0; i < size; i++) { const FirmwareToUrlElement_t& element = rgModalFCV1FirmwareArray[i]; _rgModalFCV1Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); } size = sizeof(rgmRoCtrlZero)/sizeof(rgmRoCtrlZero[0]); for (int i = 0; i < size; i++) { const FirmwareToUrlElement_t& element = rgmRoCtrlZero[i]; _rgmRoCtrlZeroF7Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); } size = sizeof(rgUVifyCoreFirmwareArray)/sizeof(rgUVifyCoreFirmwareArray[0]); for (int i = 0; i < size; i++) { const FirmwareToUrlElement_t& element = rgUVifyCoreFirmwareArray[i]; _rgUVifyCoreFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); } size = sizeof(rgPX4FLowFirmwareArray)/sizeof(rgPX4FLowFirmwareArray[0]); for (int i = 0; i < size; i++) { const FirmwareToUrlElement_t& element = rgPX4FLowFirmwareArray[i]; _rgPX4FLowFirmware.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); } 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 /// machine to the appropriate error state. void FirmwareUpgradeController::_bootloaderSyncFailed(void) { _errorCancel("Unable to sync with bootloader."); } QHash* FirmwareUpgradeController::_firmwareHashForBoardId(int boardId) { _rgFirmwareDynamic.clear(); switch (boardId) { case Bootloader::boardIDPX4Flow: _rgFirmwareDynamic = _rgPX4FLowFirmware; break; case Bootloader::boardIDPX4FMUV2: _rgFirmwareDynamic = _rgPX4FMUV2Firmware; break; case Bootloader::boardIDPX4FMUV3: _rgFirmwareDynamic = _rgFMUV3Firmware; break; case Bootloader::boardIDPX4FMUV4: _rgFirmwareDynamic = _rgFMUV4Firmware; break; case Bootloader::boardIDPX4FMUV4PRO: _rgFirmwareDynamic = _rgFMUV4PROFirmware; break; case Bootloader::boardIDPX4FMUV5: _rgFirmwareDynamic = _rgFMUV5Firmware; break; case Bootloader::boardIDAeroCore: _rgFirmwareDynamic = _rgAeroCoreFirmware; break; case Bootloader::boardIDAUAVX2_1: _rgFirmwareDynamic = _rgAUAVX2_1Firmware; break; case Bootloader::boardIDMINDPXFMUV2: _rgFirmwareDynamic = _rgMindPXFMUV2Firmware; break; case Bootloader::boardIDTAPV1: _rgFirmwareDynamic = _rgTAPV1Firmware; break; case Bootloader::boardIDASCV1: _rgFirmwareDynamic = _rgASCV1Firmware; break; case Bootloader::boardIDCrazyflie2: _rgFirmwareDynamic = _rgCrazyflie2Firmware; break; case Bootloader::boardIDOmnibusF4SD: _rgFirmwareDynamic = _rgOmnibusF4SDFirmware; break; case Bootloader::boardIDKakuteF7: _rgFirmwareDynamic = _rgKakuteF7Firmware; break; case Bootloader::boardIDDurandalV1: _rgFirmwareDynamic = _rgDurandalV1Firmware; break; case Bootloader::boardIDFMUK66V3: _rgFirmwareDynamic = _rgFMUK66V3Firmware; break; case Bootloader::boardIDModalFCV1: _rgFirmwareDynamic = _rgModalFCV1Firmware; break; case Bootloader::boardIDmRoCtrlZeroF7: _rgFirmwareDynamic = _rgmRoCtrlZeroF7Firmware; break; case Bootloader::boardIDUVifyCore: _rgFirmwareDynamic = _rgUVifyCoreFirmware; break; case Bootloader::boardID3DRRadio: _rgFirmwareDynamic = _rg3DRRadioFirmware; break; case Bootloader::boardIDCUAVX7: _rgFirmwareDynamic = _rgPX4CUAVX7Fireware; break; default: // Unknown board id break; } return &_rgFirmwareDynamic; } void FirmwareUpgradeController::_getFirmwareFile(FirmwareIdentifier firmwareId) { QHash* prgFirmware = _firmwareHashForBoardId(static_cast(_bootloaderBoardID)); if (firmwareId.firmwareType == CustomFirmware) { _firmwareFilename = QString(); _errorCancel(tr("Custom firmware selected but no filename given.")); } else { if (prgFirmware->contains(firmwareId)) { _firmwareFilename = prgFirmware->value(firmwareId); } else { _errorCancel(tr("Unable to find specified firmware for board type")); return; } } if (_firmwareFilename.isEmpty()) { _errorCancel(tr("No firmware file selected")); } else { _downloadFirmware(); } } /// @brief Begins the process of downloading the selected firmware file. void FirmwareUpgradeController::_downloadFirmware(void) { Q_ASSERT(!_firmwareFilename.isEmpty()); _appendStatusLog(tr("Downloading firmware...")); _appendStatusLog(tr(" From: %1").arg(_firmwareFilename)); QGCFileDownload* downloader = new QGCFileDownload(this); connect(downloader, &QGCFileDownload::downloadComplete, this, &FirmwareUpgradeController::_firmwareDownloadComplete); connect(downloader, &QGCFileDownload::downloadProgress, this, &FirmwareUpgradeController::_firmwareDownloadProgress); downloader->download(_firmwareFilename); } /// @brief Updates the progress indicator while downloading void FirmwareUpgradeController::_firmwareDownloadProgress(qint64 curr, qint64 total) { // Take care of cases where 0 / 0 is emitted as error return value if (total > 0) { _progressBar->setProperty("value", static_cast(curr) / static_cast(total)); } } /// @brief Called when the firmware download completes. void FirmwareUpgradeController::_firmwareDownloadComplete(QString /*remoteFile*/, QString localFile, QString errorMsg) { if (errorMsg.isEmpty()) { _appendStatusLog(tr("Download complete")); FirmwareImage* image = new FirmwareImage(this); connect(image, &FirmwareImage::statusMessage, this, &FirmwareUpgradeController::_status); connect(image, &FirmwareImage::errorMessage, this, &FirmwareUpgradeController::_error); if (!image->load(localFile, _bootloaderBoardID)) { _errorCancel(tr("Image load failed")); return; } // We can't proceed unless we have the bootloader if (!_bootloaderFound) { _errorCancel(tr("Bootloader not found")); return; } if (_bootloaderBoardFlashSize != 0 && image->imageSize() > _bootloaderBoardFlashSize) { _errorCancel(tr("Image size of %1 is too large for board flash size %2").arg(image->imageSize()).arg(_bootloaderBoardFlashSize)); return; } _threadController->flash(image); } else { _errorCancel(errorMsg); } } /// @brief returns firmware type as a string QString FirmwareUpgradeController::firmwareTypeAsString(FirmwareBuildType_t type) const { switch (type) { case StableFirmware: return "stable"; case DeveloperFirmware: return "developer"; case BetaFirmware: return "beta"; default: return "custom"; } } /// @brief Signals completion of one of the specified bootloader commands. Moves the state machine to the /// appropriate next step. void FirmwareUpgradeController::_flashComplete(void) { delete _image; _image = nullptr; _appendStatusLog(tr("Upgrade complete"), true); _appendStatusLog("------------------------------------------", false); emit flashComplete(); qgcApp()->toolbox()->linkManager()->setConnectionsAllowed(); } void FirmwareUpgradeController::_error(const QString& errorString) { delete _image; _image = nullptr; _errorCancel(QString("Error: %1").arg(errorString)); } void FirmwareUpgradeController::_status(const QString& statusString) { _appendStatusLog(statusString); } /// @brief Updates the progress bar from long running bootloader commands void FirmwareUpgradeController::_updateProgress(int curr, int total) { // Take care of cases where 0 / 0 is emitted as error return value if (total > 0) { _progressBar->setProperty("value", static_cast(curr) / static_cast(total)); } } /// @brief Moves the progress bar ahead on tick while erasing the board void FirmwareUpgradeController::_eraseProgressTick(void) { _eraseTickCount++; _progressBar->setProperty("value", static_cast(_eraseTickCount*_eraseTickMsec) / static_cast(_eraseTotalMsec)); } /// Appends the specified text to the status log area in the ui void FirmwareUpgradeController::_appendStatusLog(const QString& text, bool critical) { Q_ASSERT(_statusLog); QVariant returnedValue; QVariant varText; if (critical) { varText = QString("%1").arg(text); } else { varText = text; } QMetaObject::invokeMethod(_statusLog, "append", Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, varText)); } void FirmwareUpgradeController::_errorCancel(const QString& msg) { _appendStatusLog(msg, false); _appendStatusLog(tr("Upgrade cancelled"), true); _appendStatusLog("------------------------------------------", false); emit error(); cancel(); qgcApp()->toolbox()->linkManager()->setConnectionsAllowed(); } void FirmwareUpgradeController::_eraseStarted(void) { // We set up our own progress bar for erase since the erase command does not provide one _eraseTickCount = 0; _eraseTimer.start(_eraseTickMsec); } void FirmwareUpgradeController::_eraseComplete(void) { _eraseTimer.stop(); } void FirmwareUpgradeController::setSelectedFirmwareBuildType(FirmwareBuildType_t firmwareType) { _selectedFirmwareBuildType = firmwareType; emit selectedFirmwareBuildTypeChanged(_selectedFirmwareBuildType); _buildAPMFirmwareNames(); } void FirmwareUpgradeController::_buildAPMFirmwareNames(void) { #if !defined(NO_ARDUPILOT_DIALECT) bool chibios = _apmChibiOSSetting->rawValue().toInt() == 0; FirmwareVehicleType_t vehicleType = static_cast(_apmVehicleTypeSetting->rawValue().toInt()); QString boardDescription = _boardInfo.description(); quint16 boardVID = _boardInfo.vendorIdentifier(); quint16 boardPID = _boardInfo.productIdentifier(); uint32_t rawBoardId = _bootloaderBoardID == Bootloader::boardIDPX4FMUV3 ? Bootloader::boardIDPX4FMUV2 : _bootloaderBoardID; if (_boardType == QGCSerialPortInfo::BoardTypePX4Flow) { return; } qCDebug(FirmwareUpgradeLog) << QStringLiteral("_buildAPMFirmwareNames description(%1) vid(%2/0x%3) pid(%4/0x%5)").arg(boardDescription).arg(boardVID).arg(boardVID, 1, 16).arg(boardPID).arg(boardPID, 1, 16); _apmFirmwareNames.clear(); _apmFirmwareNamesBestIndex = -1; _apmFirmwareUrls.clear(); QString apmDescriptionSuffix("-BL"); QString boardDescriptionPrefix; bool bootloaderMatch = boardDescription.endsWith(apmDescriptionSuffix); int currentIndex = 0; for (const ManifestFirmwareInfo_t& firmwareInfo: _rgManifestFirmwareInfo) { bool match = false; if (firmwareInfo.firmwareBuildType == _selectedFirmwareBuildType && firmwareInfo.chibios == chibios && firmwareInfo.vehicleType == vehicleType && firmwareInfo.boardId == rawBoardId) { if (firmwareInfo.fmuv2 && _bootloaderBoardID == Bootloader::boardIDPX4FMUV3) { qCDebug(FirmwareUpgradeLog) << "Skipping fmuv2 manifest entry for fmuv3 board:" << firmwareInfo.friendlyName << boardDescription << firmwareInfo.rgBootloaderPortString << firmwareInfo.url << firmwareInfo.vehicleType; } else { qCDebug(FirmwareUpgradeLog) << "Board id match:" << firmwareInfo.friendlyName << boardDescription << firmwareInfo.rgBootloaderPortString << firmwareInfo.url << firmwareInfo.vehicleType; match = true; if (bootloaderMatch && _apmFirmwareNamesBestIndex == -1 && firmwareInfo.rgBootloaderPortString.contains(boardDescription)) { _apmFirmwareNamesBestIndex = currentIndex; qCDebug(FirmwareUpgradeLog) << "Bootloader best match:" << firmwareInfo.friendlyName << boardDescription << firmwareInfo.rgBootloaderPortString << firmwareInfo.url << firmwareInfo.vehicleType; } } } if (match) { _apmFirmwareNames.append(firmwareInfo.friendlyName); _apmFirmwareUrls.append(firmwareInfo.url); currentIndex++; } } if (_apmFirmwareNamesBestIndex == -1) { _apmFirmwareNamesBestIndex++; if (_apmFirmwareNames.count() > 1) { _apmFirmwareNames.prepend(tr("Choose board type")); _apmFirmwareUrls.prepend(QString()); } } emit apmFirmwareNamesChanged(); #endif } FirmwareUpgradeController::FirmwareVehicleType_t FirmwareUpgradeController::vehicleTypeFromFirmwareSelectionIndex(int index) { if (index < 0 || index >= _apmVehicleTypeFromCurrentVersionList.count()) { qWarning() << "Invalid index, index:count" << index << _apmVehicleTypeFromCurrentVersionList.count(); return CopterFirmware; } return _apmVehicleTypeFromCurrentVersionList[index]; } void FirmwareUpgradeController::_determinePX4StableVersion(void) { QGCFileDownload* downloader = new QGCFileDownload(this); connect(downloader, &QGCFileDownload::downloadComplete, this, &FirmwareUpgradeController::_px4ReleasesGithubDownloadComplete); downloader->download(QStringLiteral("https://api.github.com/repos/PX4/Firmware/releases")); } void FirmwareUpgradeController::_px4ReleasesGithubDownloadComplete(QString /*remoteFile*/, QString localFile, QString errorMsg) { if (errorMsg.isEmpty()) { QFile jsonFile(localFile); if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) { qCWarning(FirmwareUpgradeLog) << "Unable to open github px4 releases json file" << localFile << jsonFile.errorString(); return; } QByteArray bytes = jsonFile.readAll(); jsonFile.close(); QJsonParseError jsonParseError; QJsonDocument doc = QJsonDocument::fromJson(bytes, &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qCWarning(FirmwareUpgradeLog) << "Unable to open px4 releases json document" << localFile << jsonParseError.errorString(); return; } // Json should be an array of release objects if (!doc.isArray()) { qCWarning(FirmwareUpgradeLog) << "px4 releases json document is not an array" << localFile; return; } QJsonArray releases = doc.array(); // The first release marked prerelease=false is stable // The first release marked prerelease=true is beta bool foundStable = false; bool foundBeta = false; for (int i=0; idownload(QStringLiteral("http://firmware.ardupilot.org/manifest.json.gz")); } void FirmwareUpgradeController::_ardupilotManifestDownloadComplete(QString remoteFile, QString localFile, QString errorMsg) { if (errorMsg.isEmpty()) { // Delete the QGCFileDownload object sender()->deleteLater(); qCDebug(FirmwareUpgradeLog) << "_ardupilotManifestDownloadFinished" << remoteFile << localFile; QString jsonFileName(QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation)).absoluteFilePath("ArduPilot.Manifest.json")); if (!QGCZlib::inflateGzipFile(localFile, jsonFileName)) { qCWarning(FirmwareUpgradeLog) << "Inflate of compressed manifest failed" << localFile; return; } QString errorString; QJsonDocument doc; if (!JsonHelper::isJsonFile(jsonFileName, doc, errorString)) { qCWarning(FirmwareUpgradeLog) << "Json file read failed" << errorString; return; } QJsonObject json = doc.object(); QJsonArray rgFirmware = json[_manifestFirmwareJsonKey].toArray(); for (int i=0; i(firmwareJson[_manifestBoardIdJsonKey].toInt()); firmwareInfo.firmwareBuildType = firmwareBuildType; firmwareInfo.vehicleType = firmwareVehicleType; firmwareInfo.url = firmwareJson[_manifestUrlJsonKey].toString(); firmwareInfo.version = firmwareJson[_manifestMavFirmwareVersionJsonKey].toString(); firmwareInfo.chibios = format == QStringLiteral("apj"); firmwareInfo.fmuv2 = platform.contains(QStringLiteral("fmuv2")); QJsonArray bootloaderArray = firmwareJson[_manifestBootloaderStrJsonKey].toArray(); for (int j=0; j