Commit ec67f11b authored by Don Gagne's avatar Don Gagne

Major re-write of Firmware Upgrade

Usability plus, new features:
- 3DR Radio support
- APM Stack support
parent 8e9dd2f7
......@@ -591,8 +591,10 @@ HEADERS+= \
!MobileBuild {
HEADERS += \
src/VehicleSetup/FirmwareUpgradeController.h \
src/VehicleSetup/PX4Bootloader.h \
src/VehicleSetup/PX4FirmwareUpgradeThread.h
src/VehicleSetup/Bootloader.h \
src/VehicleSetup/PX4FirmwareUpgradeThread.h \
src/VehicleSetup/FirmwareImage.h \
}
SOURCES += \
......@@ -621,8 +623,10 @@ SOURCES += \
!MobileBuild {
SOURCES += \
src/VehicleSetup/FirmwareUpgradeController.cc \
src/VehicleSetup/PX4Bootloader.cc \
src/VehicleSetup/PX4FirmwareUpgradeThread.cc
src/VehicleSetup/Bootloader.cc \
src/VehicleSetup/PX4FirmwareUpgradeThread.cc \
src/VehicleSetup/FirmwareImage.cc \
}
# Fact System code
......
......@@ -201,6 +201,12 @@
<file alias="QGroundControlConnect">resources/QGroundControlConnect.svg</file>
</qresource>
<qresource prefix="/res/firmware">
<file alias="px4.png">resources/firmware/px4.png</file>
<file alias="apm.png">resources/firmware/apm.png</file>
<file alias="3drradio.png">resources/firmware/3drradio.png</file>
</qresource>
<qresource prefix="/res/mavs">
<file alias="Helicopter">resources/mavs/helicopter.svg</file>
<file alias="Unknown">resources/mavs/unknown.svg</file>
......
......@@ -27,7 +27,7 @@
#include "QGCLoggingCategory.h"
// Add Global logging categories (not class specific) here using QGC_LOGGING_CATEGORY
// There currently are no global categories
QGC_LOGGING_CATEGORY(FirmwareUpgradeLog, "FirmwareUpgradeLog")
QGCLoggingCategoryRegister* _instance = NULL;
......
......@@ -31,7 +31,7 @@
#include <QStringList>
// Add Global logging categories (not class specific) here using Q_DECLARE_LOGGING_CATEGORY
// There currently are no global categories
Q_DECLARE_LOGGING_CATEGORY(FirmwareUpgradeLog)
/// @def QGC_LOGGING_CATEGORY
/// This is a QGC specific replacement for Q_LOGGING_CATEGORY. It will register the category name into a
......
......@@ -22,126 +22,121 @@
======================================================================*/
/// @file
/// @brief PX4 Bootloader Utility routines
/// @author Don Gagne <don@thegagnes.com>
#ifndef PX4Bootloader_H
#define PX4Bootloader_H
#ifndef Bootloader_H
#define Bootloader_H
#include "FirmwareImage.h"
#include "qextserialport.h"
#include <stdint.h>
/// @brief This class is used to communicate with the Bootloader.
class PX4Bootloader : public QObject
/// Bootloader Utility routines. Works with PX4 bootloader and 3DR Radio bootloader.
class Bootloader : public QObject
{
Q_OBJECT
public:
explicit PX4Bootloader(QObject *parent = 0);
explicit Bootloader(QObject *parent = 0);
/// @brief Returns the error message associated with the last failed call to one of the bootloader
/// utility routine below.
QString errorString(void) { return _errorString; }
/// @brief Write a byte to the port
/// @param port Port to write to
/// @param data Bytes to write
/// @param maxSize Number of bytes to write
/// @return true: success
bool write(QextSerialPort* port, const uint8_t* data, qint64 maxSize);
bool write(QextSerialPort* port, const uint8_t byte);
/// @brief Read a set of bytes from the port
/// @param data Read bytes into this buffer
/// @param maxSize Number of bytes to read
/// @param readTimeout Msecs to wait for bytes to become available on port
bool read(QextSerialPort* port, uint8_t* data, qint64 maxSize, int readTimeout = _readTimout);
/// @brief Read a PROTO_SYNC command response from the bootloader
/// @param responseTimeout Msecs to wait for response bytes to become available on port
bool getCommandResponse(QextSerialPort* port, const int responseTimeout = _responseTimeout);
/// @brief Send a PROTO_GET_DEVICE command to retrieve a value from the bootloader
/// @param param Value to retrieve using INFO_BOARD_* enums
/// @param value Returned value
bool getBoardInfo(QextSerialPort* port, uint8_t param, uint32_t& value);
/// @brief Send a command to the bootloader
/// @param cmd Command to send using PROTO_* enums
/// @return true: Command sent and valid sync response returned
bool sendCommand(QextSerialPort* port, uint8_t cmd, int responseTimeout = _responseTimeout);
/// @brief Program the board with the specified firmware
bool program(QextSerialPort* port, const QString& firmwareFilename);
/// @brief Verify the board flash. How it works depend on bootloader rev
/// Rev 2: Read the flash back and compare it against the firmware file
/// Rev 3: Compare CRCs for flash and file
bool verify(QextSerialPort* port, const QString firmwareFilename);
/// @brief Opens a port to the bootloader
bool open(QextSerialPort* port, const QString portName);
/// @brief Read a PROTO_SYNC response from the bootloader
/// @return true: Valid sync response was received
bool sync(QextSerialPort* port);
/// @brief Retrieve a set of board info from the bootloader
/// @brief Erases the current program
bool erase(QextSerialPort* port);
/// @brief Program the board with the specified image
bool program(QextSerialPort* port, const FirmwareImage* image);
/// @brief Verify the board flash.
bool verify(QextSerialPort* port, const FirmwareImage* image);
/// @brief Retrieve a set of board info from the bootloader of PX4 FMU and PX4 Flow boards
/// @param bootloaderVersion Returned INFO_BL_REV
/// @param boardID Returned INFO_BOARD_ID
/// @param flashSize Returned INFO_FLASH_SIZE
bool getBoardInfo(QextSerialPort* port, uint32_t& bootloaderVersion, uint32_t& boardID, uint32_t& flashSize);
bool getPX4BoardInfo(QextSerialPort* port, uint32_t& bootloaderVersion, uint32_t& boardID, uint32_t& flashSize);
/// @brief Opens a port to the bootloader
bool open(QextSerialPort* port, const QString portName);
/// @brief Retrieve the board id from a 3DR Radio
bool get3DRRadioBoardId(QextSerialPort* port, uint32_t& boardID);
/// @brief Sends a PROTO_REBOOT command to the bootloader
bool sendBootloaderReboot(QextSerialPort* port);
bool reboot(QextSerialPort* port);
/// @brief Sends a PROTO_ERASE command to the bootlader
bool erase(QextSerialPort* port);
// Supported bootloader board ids
static const int boardIDPX4FMUV1 = 5; ///< PX4 V1 board
static const int boardIDPX4FMUV2 = 9; ///< PX4 V2 board
static const int boardIDPX4Flow = 6; ///< PX4 Flow board
static const int boardIDAeroCore = 98; ///< Gumstix AeroCore board
static const int boardID3DRRadio = 78; ///< 3DR Radio
signals:
/// @brief Signals progress indicator for long running bootloader utility routines
void updateProgramProgress(int curr, int total);
void updateProgress(int curr, int total);
private:
bool _binProgram(QextSerialPort* port, const FirmwareImage* image);
bool _ihxProgram(QextSerialPort* port, const FirmwareImage* image);
bool _write(QextSerialPort* port, const uint8_t* data, qint64 maxSize);
bool _write(QextSerialPort* port, const uint8_t byte);
bool _read(QextSerialPort* port, uint8_t* data, qint64 maxSize, int readTimeout = _readTimout);
bool _sendCommand(QextSerialPort* port, uint8_t cmd, int responseTimeout = _responseTimeout);
bool _getCommandResponse(QextSerialPort* port, const int responseTimeout = _responseTimeout);
bool _getPX4BoardInfo(QextSerialPort* port, uint8_t param, uint32_t& value);
bool _verifyBytes(QextSerialPort* port, const FirmwareImage* image);
bool _binVerifyBytes(QextSerialPort* port, const FirmwareImage* image);
bool _ihxVerifyBytes(QextSerialPort* port, const FirmwareImage* image);
bool _verifyCRC(QextSerialPort* port);
enum {
// protocol bytes
PROTO_INSYNC = 0x12, ///< 'in sync' byte sent before status
PROTO_EOC = 0x20, ///< end of command
PROTO_INSYNC = 0x12, ///< 'in sync' byte sent before status
PROTO_EOC = 0x20, ///< end of command
// Reply bytes
PROTO_OK = 0x10, ///< INSYNC/OK - 'ok' response
PROTO_FAILED = 0x11, ///< INSYNC/FAILED - 'fail' response
PROTO_INVALID = 0x13, ///< INSYNC/INVALID - 'invalid' response for bad commands
PROTO_OK = 0x10, ///< INSYNC/OK - 'ok' response
PROTO_FAILED = 0x11, ///< INSYNC/FAILED - 'fail' response
PROTO_INVALID = 0x13, ///< INSYNC/INVALID - 'invalid' response for bad commands
// Command bytes
PROTO_GET_SYNC = 0x21, ///< NOP for re-establishing sync
PROTO_GET_DEVICE = 0x22, ///< get device ID bytes
PROTO_CHIP_ERASE = 0x23, ///< erase program area and reset program address
PROTO_PROG_MULTI = 0x27, ///< write bytes at program address and increment
PROTO_GET_CRC = 0x29, ///< compute & return a CRC
PROTO_BOOT = 0x30, ///< boot the application
PROTO_GET_SYNC = 0x21, ///< NOP for re-establishing sync
PROTO_GET_DEVICE = 0x22, ///< get device ID bytes
PROTO_CHIP_ERASE = 0x23, ///< erase program area and reset program address
PROTO_LOAD_ADDRESS = 0x24, ///< set next programming address
PROTO_PROG_MULTI = 0x27, ///< write bytes at program address and increment
PROTO_GET_CRC = 0x29, ///< compute & return a CRC
PROTO_BOOT = 0x30, ///< boot the application
// Command bytes - Rev 2 boootloader only
PROTO_CHIP_VERIFY = 0x24, ///< begin verify mode
PROTO_READ_MULTI = 0x28, ///< read bytes at programm address and increment
PROTO_CHIP_VERIFY = 0x24, ///< begin verify mode
PROTO_READ_MULTI = 0x28, ///< read bytes at programm address and increment
INFO_BL_REV = 1, ///< bootloader protocol revision
BL_REV_MIN = 2, ///< Minimum supported bootlader protocol
BL_REV_MAX = 4, ///< Maximum supported bootloader protocol
INFO_BOARD_ID = 2, ///< board type
INFO_BOARD_REV = 3, ///< board revision
INFO_FLASH_SIZE = 4, ///< max firmware size in bytes
INFO_BL_REV = 1, ///< bootloader protocol revision
BL_REV_MIN = 2, ///< Minimum supported bootlader protocol
BL_REV_MAX = 4, ///< Maximum supported bootloader protocol
INFO_BOARD_ID = 2, ///< board type
INFO_BOARD_REV = 3, ///< board revision
INFO_FLASH_SIZE = 4, ///< max firmware size in bytes
PROG_MULTI_MAX = 32, ///< write size for PROTO_PROG_MULTI, must be multiple of 4
READ_MULTI_MAX = 32 ///< read size for PROTO_READ_MULTI, must be multiple of 4
PROG_MULTI_MAX = 64, ///< write size for PROTO_PROG_MULTI, must be multiple of 4
READ_MULTI_MAX = 255 ///< read size for PROTO_READ_MULTI, must be multiple of 4
};
bool _findBootloader(void);
bool _downloadFirmware(void);
bool _bootloaderVerifyRev2(QextSerialPort* port, const QString firmwareFilename);
bool _bootloaderVerifyRev3(QextSerialPort* port);
uint32_t _boardID; ///< board id for currently connected board
uint32_t _boardFlashSize; ///< flash size for currently connected board
uint32_t _imageCRC; ///< CRC for image in currently selected firmware file
......
This diff is collapsed.
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL 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.
QGROUNDCONTROL 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 QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
/// @file
/// @author Don Gagne <don@thegagnes.com>
#ifndef FirmwareImage_H
#define FirmwareImage_H
#include <QObject>
#include <QString>
#include <QByteArray>
#include <QList>
#include <QTextStream>
#include <stdint.h>
/// Support for Intel Hex firmware file
class FirmwareImage : public QObject
{
Q_OBJECT
public:
FirmwareImage(QObject *parent = 0);
/// Loads the specified image file. Supported formats: .px4, .bin, .ihx.
/// Emits errorMesssage and statusMessage signals while loading.
/// @param imageFilename Image file to load
/// @param boardId Board id that we are going to load this image onto
/// @return true: success, false: failure
bool load(const QString& imageFilename, uint32_t boardId);
/// Returns the number of bytes in the image.
uint32_t imageSize(void) const { return _imageSize; }
/// @return true: image format is .bin
bool imageIsBinFormat(void) const { return _binFormat; }
/// @return Filename for .bin file
QString binFilename(void) const { return _binFilename; }
/// @return Block count from .ihx image
uint16_t ihxBlockCount(void) const;
/// Retrieves the specified block from the .ihx image
/// @param index Index of block to return
/// @param address Address of returned block
/// @param byets Bytes of returned block
/// @return true: block retrieved
bool ihxGetBlock(uint16_t index, uint16_t& address, QByteArray& bytes) const;
signals:
void errorMessage(const QString& errorString);
void statusMessage(const QString& warningtring);
private:
bool _binLoad(const QString& px4Filename);
bool _px4Load(const QString& px4Filename);
bool _ihxLoad(const QString& ihxFilename);
bool _readByteFromStream(QTextStream& stream, uint8_t& byte);
bool _readWordFromStream(QTextStream& stream, uint16_t& word);
bool _readBytesFromStream(QTextStream& stream, uint8_t byteCount, QByteArray& bytes);
bool _decompressJsonValue(const QJsonObject& jsonObject,
const QByteArray& jsonDocBytes,
const QString& sizeKey,
const QString& bytesKey,
QByteArray& decompressedBytes);
typedef struct {
uint16_t address;
QByteArray bytes;
} IntelHexBlock_t;
bool _binFormat;
uint32_t _boardId;
QString _binFilename;
QList<IntelHexBlock_t> _ihxBlocks;
uint32_t _imageSize;
};
#endif
This diff is collapsed.
......@@ -28,6 +28,8 @@
#define FirmwareUpgradeController_H
#include "PX4FirmwareUpgradeThread.h"
#include "LinkManager.h"
#include "FirmwareImage.h"
#include <QObject>
#include <QUrl>
......@@ -51,37 +53,46 @@ public:
/// Supported firmware types. If you modify these you will need to update the qml file as well.
typedef enum {
StableFirmware,
BetaFirmware,
DeveloperFirmware,
CustomFirmware
PX4StableFirmware,
PX4BetaFirmware,
PX4DeveloperFirmware,
PX4CustomFirmware,
ApmArduCopterQuadFirmware,
ApmArduCopterX8Firmware,
ApmArduCopterHexaFirmware,
ApmArduCopterOctoFirmware,
ApmArduCopterYFirmware,
ApmArduCopterY6Firmware,
ApmArduCopterHeliFirmware,
ApmArduPlaneFirmware,
ApmRoverFirmware,
} FirmwareType_t;
Q_ENUMS(FirmwareType_t)
/// Firmare type to load
Q_PROPERTY(FirmwareType_t firmwareType READ firmwareType WRITE setFirmwareType)
Q_PROPERTY(QString boardPort READ boardPort NOTIFY boardFound)
Q_PROPERTY(QString boardDescription READ boardDescription NOTIFY boardFound)
Q_PROPERTY(QString boardType MEMBER _foundBoardType NOTIFY boardFound)
/// Upgrade push button in UI
Q_PROPERTY(QQuickItem* upgradeButton READ upgradeButton WRITE setUpgradeButton)
/// TextArea for log output
Q_PROPERTY(QQuickItem* statusLog READ statusLog WRITE setStatusLog)
/// Progress bar for you know what
Q_PROPERTY(QQuickItem* progressBar READ progressBar WRITE setProgressBar)
/// Returns true if there are active QGC connections
Q_PROPERTY(bool qgcConnections READ qgcConnections NOTIFY qgcConnectionsChanged)
Q_INVOKABLE bool activeQGCConnections(void);
Q_INVOKABLE bool pluggedInBoard(void);
/// Starts searching for boards on the background thread
Q_INVOKABLE void startBoardSearch(void);
/// Begins the firware upgrade process
Q_INVOKABLE void doFirmwareUpgrade(void);
FirmwareType_t firmwareType(void) { return _firmwareType; }
void setFirmwareType(FirmwareType_t firmwareType) { _firmwareType = firmwareType; }
/// Cancels whatever state the upgrade worker thread is in
Q_INVOKABLE void cancel(void);
QQuickItem* upgradeButton(void) { return _upgradeButton; }
void setUpgradeButton(QQuickItem* upgradeButton) { _upgradeButton = upgradeButton; }
/// Called when the firmware type has been selected by the user to continue the flash process.
Q_INVOKABLE void flash(FirmwareType_t firmwareType);
// Property accessors
QQuickItem* progressBar(void) { return _progressBar; }
void setProgressBar(QQuickItem* progressBar) { _progressBar = progressBar; }
......@@ -89,48 +100,61 @@ public:
QQuickItem* statusLog(void) { return _statusLog; }
void setStatusLog(QQuickItem* statusLog) { _statusLog = statusLog; }
bool qgcConnections(void);
QString boardPort(void) { return _foundBoardInfo.portName(); }
QString boardDescription(void) { return _foundBoardInfo.description(); }
signals:
void showMessage(const QString& title, const QString& message);
void boardFound(void);
void noBoardFound(void);
void boardGone(void);
void flashComplete(void);
void flashCancelled(void);
void qgcConnectionsChanged(bool connections);
void error(void);
private slots:
void _downloadProgress(qint64 curr, qint64 total);
void _downloadFinished(void);
void _downloadError(QNetworkReply::NetworkError code);
void _foundBoard(bool firstTry, const QString portname, QString portDescription);
void _foundBoard(bool firstAttempt, const QSerialPortInfo& portInfo, int type);
void _noBoardFound(void);
void _boardGone();
void _foundBootloader(int bootloaderVersion, int boardID, int flashSize);
void _error(const int command, const QString errorString);
void _error(const QString& errorString);
void _status(const QString& statusString);
void _bootloaderSyncFailed(void);
void _findTimeout(void);
void _complete(const int command);
void _flashComplete(void);
void _updateProgress(int curr, int total);
void _restart(void);
void _eraseStarted(void);
void _eraseComplete(void);
void _eraseProgressTick(void);
void _linkDisconnected(LinkInterface* link);
private:
void _findBoard(void);
void _findBootloader(void);
void _cancel(void);
void _getFirmwareFile(void);
void _getFirmwareFile(FirmwareType_t firmwareType);
void _downloadFirmware(void);
void _erase(void);
void _appendStatusLog(const QString& text);
bool _decompressJsonValue(const QJsonObject& jsonObject,
const QByteArray& jsonDocBytes,
const QString& sizeKey,
const QString& bytesKey,
QByteArray& decompressedBytes);
void _appendStatusLog(const QString& text, bool critical = false);
void _errorCancel(const QString& msg);
typedef struct {
FirmwareType_t firmwareType;
const char* downloadLocation;
} DownloadLocationByFirmwareType_t;
QString _portName;
QString _portDescription;
uint32_t _bootloaderVersion;
static const int _boardIDPX4FMUV1 = 5; ///< Board ID for PX4 V1 board
static const int _boardIDPX4FMUV2 = 9; ///< Board ID for PX4 V2 board
static const int _boardIDPX4Flow = 6; ///< Board ID for PX4 Flow board
static const int _boardIDAeroCore = 98; ///< Board ID for Gumstix AeroCore board
uint32_t _boardID; ///< Board ID
uint32_t _boardFlashSize; ///< Flash size in bytes of board
/// Information which comes back from the bootloader
bool _bootloaderFound; ///< true: we have received the foundBootloader signals
uint32_t _bootloaderVersion; ///< Bootloader version
uint32_t _bootloaderBoardID; ///< Board ID
uint32_t _bootloaderBoardFlashSize; ///< Flash size in bytes of board
bool _startFlashWhenBootloaderFound;
FirmwareType_t _startFlashWhenBootloaderFoundFirmwareType;
uint32_t _imageSize; ///< Image size of firmware being flashed
QPixmap _boardIcon; ///< Icon used to display image of board
......@@ -151,12 +175,15 @@ private:
static const int _findBoardTimeoutMsec = 30000; ///< Amount of time for user to plug in USB
static const int _findBootloaderTimeoutMsec = 5000; ///< Amount time to look for bootloader
FirmwareType_t _firmwareType; ///< Firmware type to load
QQuickItem* _upgradeButton; ///< Upgrade button in ui
QQuickItem* _statusLog; ///< Status log TextArea Qml control
QQuickItem* _progressBar;
bool _searchingForBoard; ///< true: searching for board, false: search for bootloader
QSerialPortInfo _foundBoardInfo;
QString _foundBoardType;
FirmwareImage* _image;
};
#endif
This diff is collapsed.
......@@ -30,6 +30,7 @@ This file is part of the QGROUNDCONTROL project
#include <QApplication>
#include <QSslSocket>
#include <QSerialPortInfo>
#include "QGCApplication.h"
#include "MainWindow.h"
......@@ -49,6 +50,7 @@ This file is part of the QGROUNDCONTROL project
#undef main
#endif
Q_DECLARE_METATYPE(QSerialPortInfo)
#ifdef Q_OS_WIN
......@@ -107,6 +109,8 @@ int main(int argc, char *argv[])
qRegisterMetaType<QSerialPort::SerialPortError>();
#endif
qRegisterMetaType<QAbstractSocket::SocketError>();
qRegisterMetaType<QSerialPortInfo>();
// We statically link to the google QtLocation plugin
#ifdef Q_OS_WIN
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment