Commit ec67f11b authored by Don Gagne's avatar Don Gagne
Browse files

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
......
......@@ -25,7 +25,8 @@
/// @brief PX4 Bootloader Utility routines
/// @author Don Gagne <don@thegagnes.com>
#include "PX4Bootloader.h"
#include "Bootloader.h"
#include "QGCLoggingCategory.h"
#include <QFile>
#include <QSerialPortInfo>
......@@ -78,13 +79,13 @@ static quint32 crc32(const uint8_t *src, unsigned len, unsigned state)
return state;
}
PX4Bootloader::PX4Bootloader(QObject *parent) :
Bootloader::Bootloader(QObject *parent) :
QObject(parent)
{
}
bool PX4Bootloader::write(QextSerialPort* port, const uint8_t* data, qint64 maxSize)
bool Bootloader::_write(QextSerialPort* port, const uint8_t* data, qint64 maxSize)
{
qint64 bytesWritten = port->write((const char*)data, maxSize);
if (bytesWritten == -1) {
......@@ -101,13 +102,13 @@ bool PX4Bootloader::write(QextSerialPort* port, const uint8_t* data, qint64 maxS
return true;
}
bool PX4Bootloader::write(QextSerialPort* port, const uint8_t byte)
bool Bootloader::_write(QextSerialPort* port, const uint8_t byte)
{
uint8_t buf[1] = { byte };
return write(port, buf, 1);
return _write(port, buf, 1);
}
bool PX4Bootloader::read(QextSerialPort* port, uint8_t* data, qint64 maxSize, int readTimeout)
bool Bootloader::_read(QextSerialPort* port, uint8_t* data, qint64 maxSize, int readTimeout)
{
qint64 bytesAlreadyRead = 0;
......@@ -137,11 +138,13 @@ bool PX4Bootloader::read(QextSerialPort* port, uint8_t* data, qint64 maxSize, in
return true;
}
bool PX4Bootloader::getCommandResponse(QextSerialPort* port, int responseTimeout)
/// Read a PROTO_SYNC command response from the bootloader
/// @param responseTimeout Msecs to wait for response bytes to become available on port
bool Bootloader::_getCommandResponse(QextSerialPort* port, int responseTimeout)
{
uint8_t response[2];
if (!read(port, response, 2, responseTimeout)) {
if (!_read(port, response, 2, responseTimeout)) {
_errorString.prepend("Get Command Response: ");
return false;
}
......@@ -164,18 +167,21 @@ bool PX4Bootloader::getCommandResponse(QextSerialPort* port, int responseTimeout
return true;
}
bool PX4Bootloader::getBoardInfo(QextSerialPort* port, uint8_t param, uint32_t& value)
/// Send a PROTO_GET_DEVICE command to retrieve a value from the PX4 bootloader
/// @param param Value to retrieve using INFO_BOARD_* enums
/// @param value Returned value
bool Bootloader::_getPX4BoardInfo(QextSerialPort* port, uint8_t param, uint32_t& value)
{
uint8_t buf[3] = { PROTO_GET_DEVICE, param, PROTO_EOC };
if (!write(port, buf, sizeof(buf))) {
if (!_write(port, buf, sizeof(buf))) {
goto Error;
}
port->flush();
if (!read(port, (uint8_t*)&value, sizeof(value))) {
if (!_read(port, (uint8_t*)&value, sizeof(value))) {
goto Error;
}
if (!getCommandResponse(port)) {
if (!_getCommandResponse(port)) {
goto Error;
}
......@@ -186,15 +192,18 @@ Error:
return false;
}
bool PX4Bootloader::sendCommand(QextSerialPort* port, const uint8_t cmd, int responseTimeout)
/// Send a command to the bootloader
/// @param cmd Command to send using PROTO_* enums
/// @return true: Command sent and valid sync response returned
bool Bootloader::_sendCommand(QextSerialPort* port, const uint8_t cmd, int responseTimeout)
{
uint8_t buf[2] = { cmd, PROTO_EOC };
if (!write(port, buf, 2)) {
if (!_write(port, buf, 2)) {
goto Error;
}
port->flush();
if (!getCommandResponse(port, responseTimeout)) {
if (!_getCommandResponse(port, responseTimeout)) {
goto Error;
}
......@@ -205,10 +214,10 @@ Error:
return false;
}
bool PX4Bootloader::erase(QextSerialPort* port)
bool Bootloader::erase(QextSerialPort* port)
{
// Erase is slow, need larger timeout
if (!sendCommand(port, PROTO_CHIP_ERASE, _eraseTimeout)) {
if (!_sendCommand(port, PROTO_CHIP_ERASE, _eraseTimeout)) {
_errorString = tr("Board erase failed: %1").arg(_errorString);
return false;
}
......@@ -216,11 +225,20 @@ bool PX4Bootloader::erase(QextSerialPort* port)
return true;
}
bool PX4Bootloader::program(QextSerialPort* port, const QString& firmwareFilename)
bool Bootloader::program(QextSerialPort* port, const FirmwareImage* image)
{
QFile firmwareFile(firmwareFilename);
if (image->imageIsBinFormat()) {
return _binProgram(port, image);
} else {
return _ihxProgram(port, image);
}
}
bool Bootloader::_binProgram(QextSerialPort* port, const FirmwareImage* image)
{
QFile firmwareFile(image->binFilename());
if (!firmwareFile.open(QIODevice::ReadOnly)) {
_errorString = tr("Unable to open firmware file %1: %2").arg(firmwareFilename).arg(firmwareFile.errorString());
_errorString = tr("Unable to open firmware file %1: %2").arg(image->binFilename()).arg(firmwareFile.errorString());
return false;
}
uint32_t imageSize = (uint32_t)firmwareFile.size();
......@@ -248,12 +266,12 @@ bool PX4Bootloader::program(QextSerialPort* port, const QString& firmwareFilenam
Q_ASSERT(bytesToSend <= 0x8F);
bool failed = true;
if (write(port, PROTO_PROG_MULTI)) {
if (write(port, (uint8_t)bytesToSend)) {
if (write(port, imageBuf, bytesToSend)) {
if (write(port, PROTO_EOC)) {
if (_write(port, PROTO_PROG_MULTI)) {
if (_write(port, (uint8_t)bytesToSend)) {
if (_write(port, imageBuf, bytesToSend)) {
if (_write(port, PROTO_EOC)) {
port->flush();
if (getCommandResponse(port)) {
if (_getCommandResponse(port)) {
failed = false;
}
}
......@@ -270,7 +288,7 @@ bool PX4Bootloader::program(QextSerialPort* port, const QString& firmwareFilenam
// Calculate the CRC now so we can test it after the board is flashed.
_imageCRC = crc32((uint8_t *)imageBuf, bytesToSend, _imageCRC);
emit updateProgramProgress(bytesSent, imageSize);
emit updateProgress(bytesSent, imageSize);
}
firmwareFile.close();
......@@ -284,46 +302,131 @@ bool PX4Bootloader::program(QextSerialPort* port, const QString& firmwareFilenam
return true;
}
bool PX4Bootloader::verify(QextSerialPort* port, const QString firmwareFilename)
bool Bootloader::_ihxProgram(QextSerialPort* port, const FirmwareImage* image)
{
uint32_t imageSize = image->imageSize();
uint32_t bytesSent = 0;
for (uint16_t index=0; index<image->ihxBlockCount(); index++) {
bool failed;
uint16_t flashAddress;
QByteArray bytes;
if (!image->ihxGetBlock(index, flashAddress, bytes)) {
_errorString = QString("Unable to retrieve block from ihx: index %1").arg(index);
return false;
}
qCDebug(FirmwareUpgradeLog) << QString("Bootloader::_ihxProgram - address:%1 size:%2 block:%3").arg(flashAddress).arg(bytes.count()).arg(index);
// Set flash address
failed = true;
if (_write(port, PROTO_LOAD_ADDRESS) &&
_write(port, flashAddress & 0xFF) &&
_write(port, (flashAddress >> 8) & 0xFF) &&
_write(port, PROTO_EOC)) {
port->flush();
if (_getCommandResponse(port)) {
failed = false;
}
}
if (failed) {
_errorString = QString("Unable to set flash start address: 0x%2").arg(flashAddress, 8, 16, QLatin1Char('0'));
return false;
}
// Flash
int bytesIndex = 0;
uint16_t bytesLeftToWrite = bytes.count();
while (bytesLeftToWrite > 0) {
uint8_t bytesToWrite;
if (bytesLeftToWrite > PROG_MULTI_MAX) {
bytesToWrite = PROG_MULTI_MAX;
} else {
bytesToWrite = bytesLeftToWrite;
}
failed = true;
if (_write(port, PROTO_PROG_MULTI) &&
_write(port, bytesToWrite) &&
_write(port, &((uint8_t *)bytes.data())[bytesIndex], bytesToWrite) &&
_write(port, PROTO_EOC)) {
port->flush();
if (_getCommandResponse(port)) {
failed = false;
}
}
if (failed) {
_errorString = QString("Flash failed: %1 at address 0x%2").arg(_errorString).arg(flashAddress, 8, 16, QLatin1Char('0'));
return false;
}
bytesIndex += bytesToWrite;
bytesLeftToWrite -= bytesToWrite;
bytesSent += bytesToWrite;
emit updateProgress(bytesSent, imageSize);
}
}
return true;
}
bool Bootloader::verify(QextSerialPort* port, const FirmwareImage* image)
{
bool ret;
if (_bootloaderVersion <= 2) {
ret = _bootloaderVerifyRev2(port, firmwareFilename);
if (!image->imageIsBinFormat() || _bootloaderVersion <= 2) {
ret = _verifyBytes(port, image);
} else {
ret = _bootloaderVerifyRev3(port);
ret = _verifyCRC(port);
}
sendBootloaderReboot(port);
reboot(port);
return ret;
}
/// @brief Verify the flash on bootloader version 2 by reading it back and comparing it against
/// the original firmware file.
bool PX4Bootloader::_bootloaderVerifyRev2(QextSerialPort* port, const QString firmwareFilename)
/// @brief Verify the flash on bootloader eading it back and comparing it against the original image->
bool Bootloader::_verifyBytes(QextSerialPort* port, const FirmwareImage* image)
{
QFile firmwareFile(firmwareFilename);
if (image->imageIsBinFormat()) {
return _binVerifyBytes(port, image);
} else {
return _ihxVerifyBytes(port, image);
}
}
bool Bootloader::_binVerifyBytes(QextSerialPort* port, const FirmwareImage* image)
{
Q_ASSERT(image->imageIsBinFormat());
QFile firmwareFile(image->binFilename());
if (!firmwareFile.open(QIODevice::ReadOnly)) {
_errorString = tr("Unable to open firmware file %1: %2").arg(firmwareFilename).arg(firmwareFile.errorString());
_errorString = tr("Unable to open firmware file %1: %2").arg(image->binFilename()).arg(firmwareFile.errorString());
return false;
}
uint32_t imageSize = (uint32_t)firmwareFile.size();
if (!sendCommand(port, PROTO_CHIP_VERIFY)) {
if (!_sendCommand(port, PROTO_CHIP_VERIFY)) {
return false;
}
uint8_t fileBuf[READ_MULTI_MAX];
uint8_t flashBuf[READ_MULTI_MAX];
uint8_t readBuf[READ_MULTI_MAX];
uint32_t bytesVerified = 0;
Q_ASSERT(PROG_MULTI_MAX <= 0x8F);
while (bytesVerified < imageSize) {
int bytesToRead = imageSize - bytesVerified;
if (bytesToRead > (int)sizeof(fileBuf)) {
bytesToRead = (int)sizeof(fileBuf);
if (bytesToRead > (int)sizeof(readBuf)) {
bytesToRead = (int)sizeof(readBuf);
}
Q_ASSERT((bytesToRead % 4) == 0);
......@@ -337,48 +440,137 @@ bool PX4Bootloader::_bootloaderVerifyRev2(QextSerialPort* port, const QString fi
Q_ASSERT(bytesToRead <= 0x8F);
bool failed = true;
if (write(port, PROTO_READ_MULTI)) {
if (write(port, (uint8_t)bytesToRead)) {
if (write(port, PROTO_EOC)) {
port->flush();
if (read(port, flashBuf, sizeof(flashBuf))) {
if (getCommandResponse(port)) {
failed = false;
}
}
if (_write(port, PROTO_READ_MULTI) &&
_write(port, (uint8_t)bytesToRead) &&
_write(port, PROTO_EOC)) {
port->flush();
if (_read(port, readBuf, sizeof(readBuf))) {
if (_getCommandResponse(port)) {
failed = false;
}
}
}
if (failed) {
_errorString = tr("Verify failed: %1 at address: 0x%2").arg(_errorString).arg(bytesVerified, 8, 16, QLatin1Char('0'));
_errorString = tr("Read failed: %1 at address: 0x%2").arg(_errorString).arg(bytesVerified, 8, 16, QLatin1Char('0'));
return false;
}
for (int i=0; i<bytesToRead; i++) {
if (fileBuf[i] != flashBuf[i]) {
_errorString = tr("Compare failed at %1: file(0x%2) flash(0x%3) at address: 0x%4").arg(bytesVerified + i).arg(fileBuf[i], 2, 16, QLatin1Char('0')).arg(flashBuf[i], 2, 16, QLatin1Char('0')).arg(bytesVerified, 8, 16, QLatin1Char('0'));
if (fileBuf[i] != readBuf[i]) {
_errorString = tr("Compare failed: expected(0x%1) actual(0x%2) at address: 0x%3").arg(fileBuf[i], 2, 16, QLatin1Char('0')).arg(readBuf[i], 2, 16, QLatin1Char('0')).arg(bytesVerified + i, 8, 16, QLatin1Char('0'));
return false;
}
}
bytesVerified += bytesToRead;
emit updateProgress(bytesVerified, imageSize);
}
firmwareFile.close();
return true;
}
/// @Brief Verify the flash on a version 3 or higher bootloader board by comparing CRCs.
bool PX4Bootloader::_bootloaderVerifyRev3(QextSerialPort* port)
bool Bootloader::_ihxVerifyBytes(QextSerialPort* port, const FirmwareImage* image)
{
Q_ASSERT(!image->imageIsBinFormat());
uint32_t imageSize = image->imageSize();
uint32_t bytesVerified = 0;
for (uint16_t index=0; index<image->ihxBlockCount(); index++) {
bool failed;
uint16_t readAddress;
QByteArray imageBytes;
if (!image->ihxGetBlock(index, readAddress, imageBytes)) {
_errorString = QString("Unable to retrieve block from ihx: index %1").arg(index);
return false;
}
qCDebug(FirmwareUpgradeLog) << QString("Bootloader::_ihxVerifyBytes - address:%1 size:%2 block:%3").arg(readAddress).arg(imageBytes.count()).arg(index);
// Set read address
failed = true;
if (_write(port, PROTO_LOAD_ADDRESS) &&
_write(port, readAddress & 0xFF) &&
_write(port, (readAddress >> 8) & 0xFF) &&
_write(port, PROTO_EOC)) {
port->flush();
if (_getCommandResponse(port)) {
failed = false;
}
}
if (failed) {
_errorString = QString("Unable to set read start address: 0x%2").arg(readAddress, 8, 16, QLatin1Char('0'));
return false;
}
// Read back
int bytesIndex = 0;
uint16_t bytesLeftToRead = imageBytes.count();
while (bytesLeftToRead > 0) {
uint8_t bytesToRead;
uint8_t readBuf[READ_MULTI_MAX];
if (bytesLeftToRead > READ_MULTI_MAX) {
bytesToRead = READ_MULTI_MAX;
} else {
bytesToRead = bytesLeftToRead;
}
failed = true;
if (_write(port, PROTO_READ_MULTI) &&
_write(port, bytesToRead) &&
_write(port, PROTO_EOC)) {
port->flush();
if (_read(port, readBuf, bytesToRead)) {
if (_getCommandResponse(port)) {
failed = false;
}
}
}
if (failed) {
_errorString = tr("Read failed: %1 at address: 0x%2").arg(_errorString).arg(readAddress, 8, 16, QLatin1Char('0'));
return false;
}
// Compare
for (int i=0; i<bytesToRead; i++) {
if ((uint8_t)imageBytes[bytesIndex + i] != readBuf[i]) {
_errorString = QString("Compare failed: expected(0x%1) actual(0x%2) at address: 0x%3").arg(imageBytes[bytesIndex + i], 2, 16, QLatin1Char('0')).arg(readBuf[i], 2, 16, QLatin1Char('0')).arg(readAddress + i, 8, 16, QLatin1Char('0'));
return false;
}
}
bytesVerified += bytesToRead;
bytesIndex += bytesToRead;
bytesLeftToRead -= bytesToRead;
emit updateProgress(bytesVerified, imageSize);
}
}
return true;
}
/// @Brief Verify the flash by comparing CRCs.
bool Bootloader::_verifyCRC(QextSerialPort* port)
{
uint8_t buf[2] = { PROTO_GET_CRC, PROTO_EOC };
quint32 flashCRC;
bool failed = true;
if (write(port, buf, 2)) {
if (_write(port, buf, 2)) {
port->flush();
if (read(port, (uint8_t*)&flashCRC, sizeof(flashCRC), _verifyTimeout)) {
if (getCommandResponse(port)) {
if (_read(port, (uint8_t*)&flashCRC, sizeof(flashCRC), _verifyTimeout)) {
if (_getCommandResponse(port)) {
failed = false;
}
}
......@@ -395,7 +587,7 @@ bool PX4Bootloader::_bootloaderVerifyRev3(QextSerialPort* port)
return true;
}
bool PX4Bootloader::open(QextSerialPort* port, const QString portName)
bool Bootloader::open(QextSerialPort* port, const QString portName)
{
Q_ASSERT(!port->isOpen());
......@@ -414,10 +606,10 @@ bool PX4Bootloader::open(QextSerialPort* port, const QString portName)
return true;
}
bool PX4Bootloader::sync(QextSerialPort* port)
bool Bootloader::sync(QextSerialPort* port)
{
// Send sync command
if (sendCommand(port, PROTO_GET_SYNC)) {
if (_sendCommand(port, PROTO_GET_SYNC)) {
return true;
} else {
_errorString.prepend("Sync: ");
......@@ -425,10 +617,10 @@ bool PX4Bootloader::sync(QextSerialPort* port)
}
}
bool PX4Bootloader::getBoardInfo(QextSerialPort* port, uint32_t& bootloaderVersion, uint32_t& boardID, uint32_t& flashSize)
bool Bootloader::getPX4BoardInfo(QextSerialPort* port, uint32_t& bootloaderVersion, uint32_t& boardID, uint32_t& flashSize)
{
if (!getBoardInfo(port, INFO_BL_REV, _bootloaderVersion)) {
if (!_getPX4BoardInfo(port, INFO_BL_REV, _bootloaderVersion)) {
goto Error;
}
if (_bootloaderVersion < BL_REV_MIN || _bootloaderVersion > BL_REV_MAX) {
......@@ -436,11 +628,11 @@ bool PX4Bootloader::getBoardInfo(QextSerialPort* port, uint32_t& bootloaderVersi
goto Error;
}
if (!getBoardInfo(port, INFO_BOARD_ID, _boardID)) {
if (!_getPX4BoardInfo(port, INFO_BOARD_ID, _boardID)) {
goto Error;
}
if (!getBoardInfo(port, INFO_FLASH_SIZE, _boardFlashSize)) {
if (!_getPX4BoardInfo(port, INFO_FLASH_SIZE, _boardFlashSize)) {
qWarning() << _errorString;
goto Error;
}
......@@ -456,7 +648,35 @@ Error:
return false;
}
bool PX4Bootloader::sendBootloaderReboot(QextSerialPort* port)
bool Bootloader::get3DRRadioBoardId(QextSerialPort* port, uint32_t& boardID)
{
uint8_t buf[2] = { PROTO_GET_DEVICE, PROTO_EOC };
if (!_write(port, buf, sizeof(buf))) {
goto Error;
}
port->flush();
if (!_read(port, (uint8_t*)buf, 2)) {
goto Error;
}
if (!_getCommandResponse(port)) {
goto Error;
}
boardID = buf[0];
_bootloaderVersion = 0;
_boardFlashSize = 0;
return true;
Error:
_errorString.prepend("Get Board Id: ");
return false;
}
bool Bootloader::reboot(QextSerialPort* port)
{
return write(port, PROTO_BOOT) && write(port, PROTO_EOC);
return _write(port, PROTO_BOOT) && _write(port, PROTO_EOC);
}
......@@ -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
......
/*=====================================================================
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
/// @brief Support for Intel Hex firmware file
/// @author Don Gagne <don@thegagnes.com>
#include "FirmwareImage.h"
#include "QGCLoggingCategory.h"
#include <QDebug>
#include <QFile>
#include <QTextStream>
#include <QJsonDocument>
#include <QJsonObject>
#include <QSettings>
#include <QFileInfo>
#include <QDir>
FirmwareImage::FirmwareImage(QObject* parent) :
QObject(parent),
_imageSize(0)
{
}
bool FirmwareImage::load(const QString& imageFilename, uint32_t boardId)
{
_imageSize = 0;
_boardId = boardId;
if (imageFilename.endsWith(".bin")) {
return _binLoad(imageFilename);
_binFormat = true;
return true;
} else if (imageFilename.endsWith(".px4")) {
_binFormat = true;
return _px4Load(imageFilename);
} else if (imageFilename.endsWith(".ihx")) {
_binFormat = false;
return _ihxLoad(imageFilename);
} else {
emit errorMessage("Unsupported file format");
return false;
}
}
bool FirmwareImage::_readByteFromStream(QTextStream& stream, uint8_t& byte)
{
QString hex = stream.read(2);
if (hex.count() != 2) {
return false;
}
bool success;
byte = (uint8_t)hex.toInt(&success, 16);
return success;
}
bool FirmwareImage::_readWordFromStream(QTextStream& stream, uint16_t& word)
{
QString hex = stream.read(4);
if (hex.count() != 4) {
return false;
}
bool success;
word = (uint16_t)hex.toInt(&success, 16);
return success;
}
bool FirmwareImage::_readBytesFromStream(QTextStream& stream, uint8_t byteCount, QByteArray& bytes)
{
bytes.clear();
while (byteCount) {
uint8_t byte;
if (!_readByteFromStream(stream, byte)) {
return false;
}
bytes += byte;
byteCount--;
}
return true;
}
bool FirmwareImage::_ihxLoad(const QString& ihxFilename)
{
_imageSize = 0;
_ihxBlocks.clear();
QFile ihxFile(ihxFilename);
if (!ihxFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
emit errorMessage(QString("Unable to open firmware file %1, error: %2").arg(ihxFilename).arg(ihxFile.errorString()));
return false;
}
QTextStream stream(&ihxFile);
while (true) {
if (stream.read(1) != ":") {
emit errorMessage("Incorrectly formatted .ihx file, line does not begin with :");
return false;
}
uint8_t blockByteCount;
uint16_t address;
uint8_t recordType;
QByteArray bytes;
uint8_t crc;
if (!_readByteFromStream(stream, blockByteCount) ||
!_readWordFromStream(stream, address) ||
!_readByteFromStream(stream, recordType) ||
!_readBytesFromStream(stream, blockByteCount, bytes) ||
!_readByteFromStream(stream, crc)) {
emit errorMessage("Incorrectly formatted line in .ihx file, line too short");
return false;
}
if (!(recordType == 0 || recordType == 1)) {
emit errorMessage(QString("Unsupported record type in file: %1").arg(recordType));
return false;
}
if (recordType == 0) {
bool appendToLastBlock = false;
// Can we append this block to the last one?
if (_ihxBlocks.count()) {
int lastBlockIndex = _ihxBlocks.count() - 1;
if (_ihxBlocks[lastBlockIndex].address + _ihxBlocks[lastBlockIndex].bytes.count() == address) {
appendToLastBlock = true;
}
}
if (appendToLastBlock) {
_ihxBlocks[_ihxBlocks.count() - 1].bytes += bytes;
qCDebug(FirmwareUpgradeLog) << QString("_ihxLoad - append - address:%1 size:%2 block:%3").arg(address).arg(blockByteCount).arg(ihxBlockCount());
} else {
IntelHexBlock_t block;
block.address = address;
block.bytes = bytes;
_ihxBlocks += block;
qCDebug(FirmwareUpgradeLog) << QString("_ihxLoad - new block - address:%1 size:%2 block:%3").arg(address).arg(blockByteCount).arg(ihxBlockCount());
}
_imageSize += blockByteCount;
} else if (recordType == 1) {
// EOF
qCDebug(FirmwareUpgradeLog) << QString("_ihxLoad - EOF");
break;
}
// Move to next line
stream.readLine();
}
ihxFile.close();
return true;
}
bool FirmwareImage::_px4Load(const QString& imageFilename)
{
_imageSize = 0;
// We need to collect information from the .px4 file as well as pull the binary image out to a seperate file.
QFile px4File(imageFilename);
if (!px4File.open(QIODevice::ReadOnly | QIODevice::Text)) {
emit errorMessage(QString("Unable to open firmware file %1, error: %2").arg(imageFilename).arg(px4File.errorString()));
return false;
}
QByteArray bytes = px4File.readAll();
px4File.close();
QJsonDocument doc = QJsonDocument::fromJson(bytes);
if (doc.isNull()) {
emit errorMessage("Supplied file is not a valid JSON document");
return false;
}
QJsonObject px4Json = doc.object();
// Make sure the keys we need are available
static const char* rgJsonKeys[] = { "board_id", "image_size", "description", "git_identity" };
for (size_t i=0; i<sizeof(rgJsonKeys)/sizeof(rgJsonKeys[0]); i++) {
if (!px4Json.contains(rgJsonKeys[i])) {
emit errorMessage(QString("Incorrectly formatted firmware file. No %1 key.").arg(rgJsonKeys[i]));
return false;
}
}
uint32_t firmwareBoardId = (uint32_t)px4Json.value(QString("board_id")).toInt();
if (firmwareBoardId != _boardId) {
emit errorMessage(QString("Downloaded firmware board id does not match hardware board id: %1 != %2").arg(firmwareBoardId).arg(_boardId));
return false;
}
// Decompress the parameter xml and save to file
QByteArray decompressedBytes;
bool success = _decompressJsonValue(px4Json, // JSON object
bytes, // Raw bytes of JSON document
"parameter_xml_size", // key which holds byte size
"parameter_xml", // key which holds compress bytes
decompressedBytes); // Returned decompressed bytes
if (success) {
// We cache the parameter xml in the same location as settings
QSettings settings;
QDir parameterDir = QFileInfo(settings.fileName()).dir();
QString parameterFilename = parameterDir.filePath("PX4ParameterFactMetaData.xml");
qDebug() << parameterFilename;
QFile parameterFile(parameterFilename);
if (parameterFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
qint64 bytesWritten = parameterFile.write(decompressedBytes);
if (bytesWritten != decompressedBytes.count()) {
// FIXME: What about these warnings?
emit statusMessage(QString("Write failed for parameter meta data file, error: %1").arg(parameterFile.errorString()));
parameterFile.close();
QFile::remove(parameterFilename);
} else {
parameterFile.close();
}
} else {
emit statusMessage(QString("Unable to open parameter meta data file %1 for writing, error: %2").arg(parameterFilename).arg(parameterFile.errorString()));
}
}
// Decompress the image and save to file
_imageSize = px4Json.value(QString("image_size")).toInt();
success = _decompressJsonValue(px4Json, // JSON object
bytes, // Raw bytes of JSON document
"image_size", // key which holds byte size
"image", // key which holds compress bytes
decompressedBytes); // Returned decompressed bytes
if (!success) {
return false;
}
// Pad image to 4-byte boundary
while ((decompressedBytes.count() % 4) != 0) {
decompressedBytes.append(static_cast<char>(static_cast<unsigned char>(0xFF)));
}
// Store decompressed image file in same location as original download file
QDir imageDir = QFileInfo(imageFilename).dir();
QString decompressFilename = imageDir.filePath("PX4FlashUpgrade.bin");
QFile decompressFile(decompressFilename);
if (!decompressFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
emit errorMessage(QString("Unable to open decompressed file %1 for writing, error: %2").arg(decompressFilename).arg(decompressFile.errorString()));
return false;
}
qint64 bytesWritten = decompressFile.write(decompressedBytes);
if (bytesWritten != decompressedBytes.count()) {
emit errorMessage(QString("Write failed for decompressed image file, error: %1").arg(decompressFile.errorString()));
return false;
}
decompressFile.close();
_binFilename = decompressFilename;
return true;
}
/// Decompress a set of bytes stored in a Json document.
bool FirmwareImage::_decompressJsonValue(const QJsonObject& jsonObject, ///< JSON object
const QByteArray& jsonDocBytes, ///< Raw bytes of JSON document
const QString& sizeKey, ///< key which holds byte size
const QString& bytesKey, ///< key which holds compress bytes
QByteArray& decompressedBytes) ///< Returned decompressed bytes
{
// Validate decompressed size key
if (!jsonObject.contains(sizeKey)) {
emit statusMessage(QString("Firmware file missing %1 key").arg(sizeKey));
return false;
}
int decompressedSize = jsonObject.value(QString(sizeKey)).toInt();
if (decompressedSize == 0) {
emit errorMessage(QString("Firmware file has invalid decompressed size for %1").arg(sizeKey));
return false;
}
// XXX Qt's JSON string handling is terribly broken, strings
// with some length (18K / 25K) are just weirdly cut.
// The code below works around this by manually 'parsing'
// for the image string. Since its compressed / checksummed
// this should be fine.
QStringList parts = QString(jsonDocBytes).split(QString("\"%1\": \"").arg(bytesKey));
if (parts.count() == 1) {
emit errorMessage(QString("Could not find compressed bytes for %1 in Firmware file").arg(bytesKey));
return false;
}
parts = parts.last().split("\"");
if (parts.count() == 1) {
emit errorMessage(QString("Incorrectly formed compressed bytes section for %1 in Firmware file").arg(bytesKey));
return false;
}
// Store decompressed size as first four bytes. This is required by qUncompress routine.
QByteArray raw;
raw.append((unsigned char)((decompressedSize >> 24) & 0xFF));
raw.append((unsigned char)((decompressedSize >> 16) & 0xFF));
raw.append((unsigned char)((decompressedSize >> 8) & 0xFF));
raw.append((unsigned char)((decompressedSize >> 0) & 0xFF));
QByteArray raw64 = parts.first().toUtf8();
raw.append(QByteArray::fromBase64(raw64));
decompressedBytes = qUncompress(raw);
if (decompressedBytes.count() == 0) {
emit errorMessage(QString("Firmware file has 0 length %1").arg(bytesKey));
return false;
}
if (decompressedBytes.count() != decompressedSize) {
emit errorMessage(QString("Size for decompressed %1 does not match stored size: Expected(%1) Actual(%2)").arg(decompressedSize).arg(decompressedBytes.count()));
return false;
}
emit statusMessage(QString("Succesfully decompressed %1").arg(bytesKey));
return true;
}
uint16_t FirmwareImage::ihxBlockCount(void) const
{
return _ihxBlocks.count();
}
bool FirmwareImage::ihxGetBlock(uint16_t index, uint16_t& address, QByteArray& bytes) const
{
address = 0;
bytes.clear();
if (index < ihxBlockCount()) {
address = _ihxBlocks[index].address;
bytes = _ihxBlocks[index].bytes;
return true;
} else {
return false;
}
}
bool FirmwareImage::_binLoad(const QString& imageFilename)
{
QFile binFile(imageFilename);
if (!binFile.open(QIODevice::ReadOnly)) {
emit errorMessage(QString("Unabled to open firmware file %1, %2").arg(imageFilename).arg(binFile.errorString()));
return false;
}
_imageSize = (uint32_t)binFile.size();
binFile.close();
return true;
}
/*=====================================================================
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
......@@ -34,158 +34,337 @@ import QGroundControl.Controllers 1.0
import QGroundControl.ScreenTools 1.0
QGCView {
viewPanel: panel
id: qgcView
viewPanel: panel
// User visible strings
readonly property string title: "FIRMWARE UPDATE"
readonly property string highlightPrefix: "<font color=\"yellow\">"
readonly property string highlightSuffix: "</font>"
readonly property string welcomeText: "QGroundControl can upgrade the firmware on Pixhawk devices, 3DR Radios and PX4 Flow Smart Cameras."
readonly property string plugInText: highlightPrefix + "Plug in your device" + highlightSuffix + " via USB to " + highlightPrefix + "start" + highlightSuffix + " firmware upgrade"
readonly property string qgcDisconnectText: "All QGroundControl connections to vehicles must be disconnected prior to firmware upgrade. " +
"Click " + highlightPrefix + "Disconnect" + highlightSuffix + " in the toolbar above."
property string usbUnplugText: "Device must be disconnected from USB to start firmware upgrade. " +
highlightPrefix + "Disconnect {0}" + highlightSuffix + " from usb."
property string firmwareWarningMessage
property bool controllerCompleted: false
property bool initialBoardSearch: true
property string firmwareName
function cancelFlash() {
statusTextArea.append(highlightPrefix + "Upgrade cancelled" + highlightSuffix)
statusTextArea.append("------------------------------------------")
controller.cancel()
flashCompleteWaitTimer.running = true
}
QGCPalette { id: qgcPal; colorGroupEnabled: panel.enabled }
FirmwareUpgradeController {
id: controller
upgradeButton: upgradeButton
progressBar: progressBar
statusLog: statusTextArea
firmwareType: FirmwareUpgradeController.StableFirmware
onShowMessage: {
showMessage(title, message, StandardButton.Ok)
Component.onCompleted: {
controllerCompleted = true
if (qgcView.completedSignalled) {
// We can only start the board search when the Qml and Controller are completely done loading
controller.startBoardSearch()
}
}
onNoBoardFound: {
initialBoardSearch = false
statusTextArea.append(plugInText)
}
onBoardGone: {
initialBoardSearch = false
statusTextArea.append(plugInText)
}
onBoardFound: {
if (initialBoardSearch) {
// Board was found right away, so something is already plugged in before we've started upgrade
if (controller.qgcConnections) {
statusTextArea.append(qgcDisconnectText)
} else {
statusTextArea.append(usbUnplugText.replace('{0}', controller.boardType))
}
} else {
// We end up here when we detect a board plugged in after we've started upgrade
statusTextArea.append(highlightPrefix + "Found device" + highlightSuffix + ": " + controller.boardType)
if (controller.boardType == "Pixhawk") {
showDialog(pixhawkFirmwareSelectDialog, title, 50, StandardButton.Ok | StandardButton.Cancel)
}
}
}
onError: {
hideDialog()
flashCompleteWaitTimer.running = true
}
onFlashComplete: flashCompleteWaitTimer.running = true
}
QGCViewPanel {
id: panel
anchors.fill: parent
onCompleted: {
if (controllerCompleted) {
// We can only start the board search when the Qml and Controller are completely done loading
controller.startBoardSearch()
}
}
Component {
id: firmwareWarningComponent
// After a flash completes we start this timer to trigger resetting the ui back to it's initial state of being ready to
// flash another board. We do this only after the timer triggers to leave the results of the previous flash on the screen
// for a small amount amount of time.
QGCViewMessage {
message: firmwareWarningMessage
Timer {
id: flashCompleteWaitTimer
interval: 15000
function accept() {
hideDialog()
controller.doFirmwareUpgrade();
}
}
onTriggered: {
initialBoardSearch = true
progressBar.value = 0
statusTextArea.append(welcomeText)
controller.startBoardSearch()
}
}
Component {
id: pixhawkFirmwareSelectDialog
Column {
QGCViewDialog {
anchors.fill: parent
property bool showVersionSelection: apmFlightStack.checked || advancedMode.checked
QGCLabel {
text: "FIRMWARE UPDATE"
font.pixelSize: ScreenTools.largeFontPixelSize
function accept() {
hideDialog()
controller.flash(firmwareVersionCombo.model.get(firmwareVersionCombo.currentIndex).firmwareType)
}
function reject() {
cancelFlash()
hideDialog()
}
ExclusiveGroup {
id: firmwareGroup
}
ListModel {
id: px4FirmwareTypeList
ListElement {
text: qsTr("Standard Version (stable)");
firmwareType: FirmwareUpgradeController.PX4StableFirmware
}
ListElement {
text: qsTr("Beta Testing (beta)");
firmwareType: FirmwareUpgradeController.PX4BetaFirmware
}
ListElement {
text: qsTr("Developer Build (master)");
firmwareType: FirmwareUpgradeController.PX4DeveloperFirmware
}
ListElement {
text: qsTr("Custom firmware file...");
firmwareType: FirmwareUpgradeController.PX4CustomFirmware
}
}
ListModel {
id: apmFirmwareTypeList
Item {
// Just used as a spacer
height: 20
width: 10
ListElement {
text: "ArduCopter Quad"
firmwareType: FirmwareUpgradeController.ApmArduCopterQuadFirmware
}
ListElement {
text: "ArduCopter X8"
firmwareType: FirmwareUpgradeController.ApmArduCopterX8Firmware
}
ListElement {
text: "ArduCopter Hexa"
firmwareType: FirmwareUpgradeController.ApmArduCopterHexaFirmware
}
ListElement {
text: "ArduCopter Octo"
firmwareType: FirmwareUpgradeController.ApmArduCopterOctoFirmware
}
ListElement {
text: "ArduCopter Y"
firmwareType: FirmwareUpgradeController.ApmArduCopterYFirmware
}
ListElement {
text: "ArduCopter Y6"
firmwareType: FirmwareUpgradeController.ApmArduCopterY6Firmware
}
ListElement {
text: "ArduCopter Heli"
firmwareType: FirmwareUpgradeController.ApmArduCopterHeliFirmware
}
ListElement {
text: "ArduPlane"
firmwareType: FirmwareUpgradeController.ApmArduPlaneFirmware
}
ListElement {
text: "Rover"
firmwareType: FirmwareUpgradeController.ApmRoverFirmware
}
}
Row {
spacing: 10
ListModel {
id: firmwareItems
ListElement {
text: qsTr("Standard Version (stable)");
firmwareType: FirmwareUpgradeController.StableFirmware
}
ListElement {
text: qsTr("Beta Testing (beta)");
firmwareType: FirmwareUpgradeController.BetaFirmware
}
ListElement {
text: qsTr("Developer Build (master)");
firmwareType: FirmwareUpgradeController.DeveloperFirmware
}
ListElement {
text: qsTr("Custom firmware file...");
firmwareType: FirmwareUpgradeController.CustomFirmware
}
Column {
anchors.fill: parent
spacing: defaultTextHeight
QGCLabel {
width: parent.width
wrapMode: Text.WordWrap
text: "Detected Pixhawk board. You can select from the following flight stacks:"
}
QGCComboBox {
id: firmwareCombo
width: 200
height: upgradeButton.height
model: firmwareItems
}
QGCButton {
id: upgradeButton
text: "UPGRADE"
primary: true
onClicked: {
if (controller.activeQGCConnections()) {
showMessage("Firmware Upgrade",
"There are still vehicles connected to QGroundControl. " +
"You must disconnect all vehicles from QGroundControl prior to Firmware Upgrade.",
StandardButton.Ok)
return
}
function firmwareVersionChanged(model) {
firmwareVersionWarningLabel.visible = false
// All of this bizarre, setting model to null and index to 1 and then to 0 is to work around
// strangeness in the combo box implementation. This sequence of steps correctly changes the combo model
// without generating any warnings and correctly updates the combo text with the new selection.
firmwareVersionCombo.model = null
firmwareVersionCombo.model = model
firmwareVersionCombo.currentIndex = 1
firmwareVersionCombo.currentIndex = 0
}
if (controller.pluggedInBoard()) {
showMessage("Firmware Upgrade",
"You vehicle is currently connected via USB. " +
"You must unplug your vehicle from USB prior to Firmware Upgrade.",
StandardButton.Ok)
return
}
QGCRadioButton {
id: px4FlightStack
checked: true
exclusiveGroup: firmwareGroup
text: "PX4 Flight Stack (full QGC support)"
onClicked: parent.firmwareVersionChanged(px4FirmwareTypeList)
}
QGCRadioButton {
id: apmFlightStack
exclusiveGroup: firmwareGroup
text: "APM Flight Stack (partial QGC support)"
controller.firmwareType = firmwareItems.get(firmwareCombo.currentIndex).firmwareType
if (controller.firmwareType == 1) {
firmwareWarningMessage = "WARNING: BETA FIRMWARE\n" +
"This firmware version is ONLY intended for beta testers. " +
"Although it has received FLIGHT TESTING, it represents actively changed code. " +
"Do NOT use for normal operation.\n\n" +
"Click Cancel to abort upgrade, Click Ok to Upgrade anwyay"
showDialog(firmwareWarningComponent, "Firmware Upgrade", 50, StandardButton.Cancel | StandardButton.Ok)
} else if (controller.firmwareType == 2) {
firmwareWarningMessage = "WARNING: CONTINUOUS BUILD FIRMWARE\n" +
"This firmware has NOT BEEN FLIGHT TESTED. " +
"It is only intended for DEVELOPERS. " +
"Run bench tests without props first. " +
"Do NOT fly this without addional safety precautions. " +
"Follow the mailing list actively when using it.\n\n" +
"Click Cancel to abort upgrade, Click Ok to Upgrade anwyay"
showDialog(firmwareWarningComponent, "Firmware Upgrade", 50, StandardButton.Cancel | StandardButton.Ok)
} else {
controller.doFirmwareUpgrade();
onClicked: parent.firmwareVersionChanged(apmFirmwareTypeList)
}
QGCLabel {
width: parent.width
wrapMode: Text.WordWrap
visible: showVersionSelection
text: "Select which version of the above flight stack you would like to install:"
}
QGCComboBox {
id: firmwareVersionCombo
width: 200
visible: showVersionSelection
model: px4FirmwareTypeList
onActivated: {
if (model.get(index).firmwareType == FirmwareUpgradeController.PX4BetaFirmware) {
firmwareVersionWarningLabel.visible = true
firmwareVersionWarningLabel.text = "WARNING: BETA FIRMWARE. " +
"This firmware version is ONLY intended for beta testers. " +
"Although it has received FLIGHT TESTING, it represents actively changed code. " +
"Do NOT use for normal operation."
} else if (model.get(index).firmwareType == FirmwareUpgradeController.PX4DeveloperFirmware) {
firmwareVersionWarningLabel.visible = true
firmwareVersionWarningLabel.text = "WARNING: CONTINUOUS BUILD FIRMWARE. " +
"This firmware has NOT BEEN FLIGHT TESTED. " +
"It is only intended for DEVELOPERS. " +
"Run bench tests without props first. " +
"Do NOT fly this without addional safety precautions. " +
"Follow the mailing list actively when using it."
} else {
firmwareVersionWarningLabel.visible = false
}
}
}
}
QGCLabel {
id: firmwareVersionWarningLabel
width: parent.width
wrapMode: Text.WordWrap
visible: false
}
}
QGCCheckBox {
id: advancedMode
anchors.bottom: parent.bottom
text: "Advanced mode"
onClicked: {
firmwareVersionCombo.currentIndex = 0
firmwareVersionWarningLabel.visible = false
}
}
Item {
// Just used as a spacer
height: 20
width: 10
QGCButton {
anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 2
anchors.left: advancedMode.right
anchors.bottom: parent.bottom
text: "Help me pick a flight stack"
onClicked: Qt.openUrlExternally("http://pixhawk.org/choice")
}
} // QGCViewDialog
} // Component - pixhawkFirmwareSelectDialog
Component {
id: firmwareWarningDialog
QGCViewMessage {
message: firmwareWarningMessage
ProgressBar {
id: progressBar
width: parent.width
function accept() {
hideDialog()
controller.doFirmwareUpgrade();
}
}
}
QGCViewPanel {
id: panel
anchors.fill: parent
TextArea {
id: statusTextArea
QGCLabel {
id: titleLabel
text: title
font.pixelSize: ScreenTools.largeFontPixelSize
}
width: parent.width
height: 300
readOnly: true
frameVisible: false
font.pixelSize: ScreenTools.defaultFontPixelSize
text: qsTr("Please disconnect all vehicles from QGroundControl before selecting Upgrade.")
ProgressBar {
id: progressBar
anchors.topMargin: ScreenTools.defaultFontPixelHeight
anchors.top: titleLabel.bottom
width: parent.width
}
style: TextAreaStyle {
textColor: qgcPal.text
backgroundColor: qgcPal.windowShade
}
TextArea {
id: statusTextArea
anchors.topMargin: ScreenTools.defaultFontPixelHeight
anchors.top: progressBar.bottom
anchors.bottom: parent.bottom
width: parent.width
readOnly: true
frameVisible: false
font.pixelSize: ScreenTools.defaultFontPixelSize
textFormat: TextEdit.RichText
text: welcomeText
style: TextAreaStyle {
textColor: qgcPal.text
backgroundColor: qgcPal.windowShade
}
} // Column
} // QGCViewPanel
} // QGCView
}
} // QGCViewPabel
} // QGCView
\ No newline at end of file
......@@ -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
......@@ -28,16 +28,27 @@
#ifndef PX4FirmwareUpgradeThread_H
#define PX4FirmwareUpgradeThread_H
#include "Bootloader.h"
#include "FirmwareImage.h"
#include <QObject>
#include <QThread>
#include <QTimer>
#include <QTime>
#include <QSerialPortInfo>
#include "qextserialport.h"
#include <stdint.h>
#include "PX4Bootloader.h"
typedef enum {
FoundBoardPX4FMUV1,
FoundBoardPX4FMUV2,
FoundBoardPX4Flow,
FoundBoard3drRadio
} PX4FirmwareUpgradeFoundBoardType_t;
class PX4FirmwareUpgradeThreadController;
/// @brief Used to run bootloader commands on a seperate thread. These routines are mainly meant to to be called
/// internally by the PX4FirmwareUpgradeThreadController. Clients should call the various public methods
......@@ -47,52 +58,59 @@ class PX4FirmwareUpgradeThreadWorker : public QObject
Q_OBJECT
public:
PX4FirmwareUpgradeThreadWorker(QObject* parent = NULL);
PX4FirmwareUpgradeThreadWorker(PX4FirmwareUpgradeThreadController* controller);
~PX4FirmwareUpgradeThreadWorker();
enum {
commandBootloader,
commandProgram,
commandVerify,
commandErase,
commandCancel
};
public slots:
void init(void);
void findBoard(int msecTimeout);
void findBootloader(const QString portName, int msecTimeout);
void timeout(void);
void cancelFind(void);
void sendBootloaderReboot(void);
void program(const QString firmwareFilename);
void verify(const QString firmwareFilename);
void erase(void);
signals:
void foundBoard(bool firstTry, const QString portname, QString portDescription);
void updateProgress(int curr, int total);
void foundBoard(bool firstAttempt, const QSerialPortInfo& portInfo, int type);
void noBoardFound(void);
void boardGone(void);
void foundBootloader(int bootloaderVersion, int boardID, int flashSize);
void bootloaderSyncFailed(void);
void error(const int command, const QString errorString);
void complete(const int command);
void findTimeout(void);
void updateProgress(int curr, int total);
void error(const QString& errorString);
void status(const QString& statusText);
void eraseStarted(void);
void eraseComplete(void);
void flashComplete(void);
private slots:
void _init(void);
void _startFindBoardLoop(void);
void _reboot(void);
void _flash(void);
void _findBoardOnce(void);
void _findBootloaderOnce(void);
void _updateProgramProgress(int curr, int total) { emit updateProgress(curr, total); }
void _closeFind(void);
void _updateProgress(int curr, int total) { emit updateProgress(curr, total); }
void _cancel(void);
private:
PX4Bootloader* _bootloader;
bool _findBoardFromPorts(QSerialPortInfo& portInfo, PX4FirmwareUpgradeFoundBoardType_t& type);
bool _findBootloader(const QSerialPortInfo& portInfo, bool radioMode, bool errorOnNotFound);
void _3drRadioForceBootloader(const QSerialPortInfo& portInfo);
bool _erase(void);
PX4FirmwareUpgradeThreadController* _controller;
Bootloader* _bootloader;
QextSerialPort* _bootloaderPort;
QTimer* _timerTimeout;
QTimer* _timerRetry;
QTime _elapsed;
QString _portName;
static const int _retryTimeout = 1000;
bool _findBoardFirstAttempt;
bool _foundBoard; ///< true: board is currently connected
bool _findBoardFirstAttempt; ///< true: this is our first try looking for a board
QSerialPortInfo _foundBoardPortInfo; ///< port info for found board
// Serial port info for supported devices
static const int _px4VendorId = 9900;
static const int _pixhawkFMUV2ProductId = 17;
static const int _pixhawkFMUV1ProductId = 16;
static const int _flowProductId = 21;
static const int _3drRadioVendorId = 1027;
static const int _3drRadioProductId = 24597;
};
/// @brief Provides methods to interact with the bootloader. The commands themselves are signalled
......@@ -105,82 +123,74 @@ public:
PX4FirmwareUpgradeThreadController(QObject* parent = NULL);
~PX4FirmwareUpgradeThreadController(void);
/// Returns true is a board is currently connected via USB
bool pluggedInBoard(void);
/// @brief Begins the process of searching for a PX4 board connected to any serial port.
/// @param msecTimeout Numbers of msecs to continue looking for a board to become available.
void findBoard(int msecTimeout);
/// @brief Begins the process of attempting to communicate with the bootloader on the specified port.
/// @param portName Name of port to attempt a bootloader connection on.
/// @param msecTimeout Number of msecs to continue to wait for a bootloader to appear on the port.
void findBootloader(const QString& portName, int msecTimeout);
/// @brief Begins the process of searching for a supported board connected to any serial port. This will
/// continue until cancelFind is called. Signals foundBoard and boardGone as boards come and go.
void startFindBoardLoop(void);
/// @brief Cancel an in progress findBoard or FindBootloader
void cancelFind(void) { emit _cancelFindOnThread(); }
void cancel(void);
/// @brief Sends a reboot command to the bootloader
void sendBootloaderReboot(void) { emit _sendBootloaderRebootOnThread(); }
void reboot(void) { emit _rebootOnThread(); }
/// @brief Flash the specified firmware onto the board
void program(const QString firmwareFilename) { emit _programOnThread(firmwareFilename); }
void flash(const FirmwareImage* image);
/// @brief Verify the board flash with respect to the specified firmware image
void verify(const QString firmwareFilename) { emit _verifyOnThread(firmwareFilename); }
const FirmwareImage* image(void) { return _image; }
/// @brief Send and erase command to the bootloader
void erase(void) { emit _eraseOnThread(); }
signals:
/// @brief Emitted by the findBoard process when it finds the board.
/// @param firstTry true: board found on first attempt
/// @param portName Port that board is on
/// @param portDescription User friendly port description
void foundBoard(bool firstTry, const QString portname, QString portDescription);
/// @brief Emitted by the find board process when it finds a board.
void foundBoard(bool firstAttempt, const QSerialPortInfo &portInfo, int type);
void noBoardFound(void);
/// @brief Emitted by the find board process when a board it previously reported as found disappears.
void boardGone(void);
/// @brief Emitted by the findBootloader process when has a connection to the bootloader
void foundBootloader(int bootloaderVersion, int boardID, int flashSize);
/// @brief Emitted by the bootloader commands when an error occurs.
/// @param errorCommand Command which caused the error, using PX4FirmwareUpgradeThreadWorker command* enum values
void error(const int errorCommand, const QString errorString);
void error(const QString& errorString);
void status(const QString& status);
/// @brief Signalled when the findBootloader process connects to the port, but cannot sync to the
/// bootloader.
void bootloaderSyncFailed(void);
/// @brief Signalled when the findBoard or findBootloader process times out before success
void findTimeout(void);
void eraseStarted(void);
void eraseComplete(void);
/// @brief Signalled by the bootloader commands other than find* that they have complete successfully.
/// @param command Command which completed, using PX4FirmwareUpgradeThreadWorker command* enum values
void complete(const int command);
void flashComplete(void);
/// @brief Signalled to update progress for long running bootloader commands
void updateProgress(int curr, int total);
// Internal signals to communicate with thread worker
void _initThreadWorker(void);
void _findBoardOnThread(int msecTimeout);
void _findBootloaderOnThread(const QString& portName, int msecTimeout);
void _sendBootloaderRebootOnThread(void);
void _programOnThread(const QString firmwareFilename);
void _verifyOnThread(const QString firmwareFilename);
void _eraseOnThread(void);
void _cancelFindOnThread(void);
void _startFindBoardLoopOnThread(void);
void _rebootOnThread(void);
void _flashOnThread(void);
void _cancel(void);
private slots:
void _foundBoard(bool firstTry, const QString portname, QString portDescription);
void _foundBootloader(int bootloaderVersion, int boardID, int flashSize);
void _bootloaderSyncFailed(void);
void _error(const int errorCommand, const QString errorString) { emit error(errorCommand, errorString); }
void _complete(const int command) { emit complete(command); }
void _findTimeout(void);
void _updateProgress(int curr, int total) { emit updateProgress(curr, total); }
void _foundBoard(bool firstAttempt, const QSerialPortInfo& portInfo, int type) { emit foundBoard(firstAttempt, portInfo, type); }
void _noBoardFound(void) { emit noBoardFound(); }
void _boardGone(void) { emit boardGone(); }
void _foundBootloader(int bootloaderVersion, int boardID, int flashSize) { emit foundBootloader(bootloaderVersion, boardID, flashSize); }
void _bootloaderSyncFailed(void) { emit bootloaderSyncFailed(); }
void _error(const QString& errorString) { emit error(errorString); }
void _status(const QString& statusText) { emit status(statusText); }
void _eraseStarted(void) { emit eraseStarted(); }
void _eraseComplete(void) { emit eraseComplete(); }
void _flashComplete(void) { emit flashComplete(); }
private:
void _updateProgress(int curr, int total) { emit updateProgress(curr, total); }
PX4FirmwareUpgradeThreadWorker* _worker;
QThread* _workerThread; ///< Thread which PX4FirmwareUpgradeThreadWorker runs on
const FirmwareImage* _image;
};
#endif
......@@ -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
......
Supports Markdown
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