diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro
index 98e0bd5f31c18fc86534079c2388c2cd26e5d850..228643ab3e20e7a3d141bc3c6b131df3aa539b93 100644
--- a/qgroundcontrol.pro
+++ b/qgroundcontrol.pro
@@ -69,11 +69,16 @@ QT += \
xml \
!MobileBuild {
- QT += \
+QT += \
printsupport \
serialport \
}
+MobileBuild {
+QT += \
+ bluetooth \
+}
+
contains(DEFINES, QGC_NOTIFY_TUNES_ENABLED) {
QT += multimedia
}
@@ -306,6 +311,11 @@ WindowsBuild {
HEADERS += src/stable_headers.h
}
+MobileBuild {
+ HEADERS += \
+ src/comm/BluetoothLink.h \
+}
+
!iOSBuild {
HEADERS += \
src/comm/QGCSerialPortInfo.h \
@@ -425,6 +435,11 @@ SOURCES += \
src/ui/SerialConfigurationWindow.cc \
}
+MobileBuild {
+ SOURCES += \
+ src/comm/BluetoothLink.cc \
+}
+
!MobileBuild {
SOURCES += \
src/comm/LogReplayLink.cc \
diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc
index 230f97571c99818a2f3ee45bb12a5a2419882ee1..9a447a30b7e59ebede2b4e98f98ca59e22807d51 100644
--- a/qgroundcontrol.qrc
+++ b/qgroundcontrol.qrc
@@ -16,6 +16,7 @@
src/AutoPilotPlugins/PX4/FlightModesComponent.qml
src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml
+ src/ui/preferences/BluetoothSettings.qml
src/ui/preferences/DebugWindow.qml
src/ui/preferences/GeneralSettings.qml
src/ui/preferences/LinkSettings.qml
diff --git a/src/comm/BluetoothLink.cc b/src/comm/BluetoothLink.cc
new file mode 100644
index 0000000000000000000000000000000000000000..69895e54f0acfc1aa22f09dd5e08a54f037ecae9
--- /dev/null
+++ b/src/comm/BluetoothLink.cc
@@ -0,0 +1,377 @@
+/*=====================================================================
+
+QGroundControl Open Source Ground Control Station
+
+(c) 2009 - 2015 QGROUNDCONTROL PROJECT
+
+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 .
+
+======================================================================*/
+
+/**
+ * @file
+ * @brief Definition of Bluetooth connection for unmanned vehicles
+ * @author Gus Grubba
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "BluetoothLink.h"
+#include "QGC.h"
+
+/*
+static void print_device_info(QBluetoothDeviceInfo info)
+{
+ qDebug() << "Bluetooth: " << info.name();
+ qDebug() << " Services:" << info.serviceClasses();
+ qDebug() << " Major Classs:" << info.majorDeviceClass();
+ qDebug() << " Minor Classs:" << info.minorDeviceClass();
+ qDebug() << " RSSI:" << info.rssi();
+ qDebug() << " UUID:" << info.deviceUuid();
+ qDebug() << " Service UUID:" << info.serviceUuids();
+ qDebug() << " Core Config:" << info.coreConfigurations();
+ qDebug() << " Valid:" << info.isValid();
+ qDebug() << " Address:" << info.address().toString();
+}
+*/
+
+BluetoothLink::BluetoothLink(BluetoothConfiguration* config)
+ : _connectState(false)
+ , _targetSocket(NULL)
+ , _targetDevice(NULL)
+ , _running(false)
+{
+ Q_ASSERT(config != NULL);
+ _config = config;
+ _config->setLink(this);
+ // We're doing it wrong - because the Qt folks got the API wrong:
+ // http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/
+ moveToThread(this);
+}
+
+BluetoothLink::~BluetoothLink()
+{
+ // Disconnect link from configuration
+ _config->setLink(NULL);
+ _disconnect();
+ // Tell the thread to exit
+ _running = false;
+ quit();
+ // Wait for it to exit
+ wait();
+ this->deleteLater();
+}
+
+void BluetoothLink::run()
+{
+ if(_running && _hardwareConnect()) {
+ exec();
+ }
+}
+
+void BluetoothLink::_restartConnection()
+{
+ if(this->isConnected())
+ {
+ _disconnect();
+ _connect();
+ }
+}
+
+QString BluetoothLink::getName() const
+{
+ return _config->name();
+}
+
+void BluetoothLink::writeBytes(const char* data, qint64 size)
+{
+ _sendBytes(data, size);
+}
+
+void BluetoothLink::_sendBytes(const char* data, qint64 size)
+{
+ if(_targetSocket)
+ {
+ if(_targetSocket->isWritable())
+ {
+ if(_targetSocket->write(data, size) > 0) {
+ _logOutputDataRate(size, QDateTime::currentMSecsSinceEpoch());
+ }
+ else
+ qWarning() << "Bluetooth write error";
+ }
+ else
+ qWarning() << "Bluetooth not writable error";
+ }
+}
+
+void BluetoothLink::readBytes()
+{
+ while (_targetSocket->bytesAvailable() > 0)
+ {
+ QByteArray datagram;
+ datagram.resize(_targetSocket->bytesAvailable());
+ _targetSocket->read(datagram.data(), datagram.size());
+ emit bytesReceived(this, datagram);
+ _logInputDataRate(datagram.length(), QDateTime::currentMSecsSinceEpoch());
+ }
+}
+
+void BluetoothLink::_disconnect(void)
+{
+ _running = false;
+ quit();
+ wait();
+ if(_targetDevice)
+ {
+ delete _targetDevice;
+ _targetDevice = NULL;
+ }
+ if(_targetSocket)
+ {
+ _targetSocket->deleteLater();
+ _targetSocket = NULL;
+ emit disconnected();
+ }
+ _connectState = false;
+}
+
+bool BluetoothLink::_connect(void)
+{
+ if(this->isRunning() || _running)
+ {
+ _running = false;
+ quit();
+ wait();
+ }
+ if(_targetDevice)
+ {
+ delete _targetDevice;
+ _targetDevice = NULL;
+ }
+ //-- Start Thread
+ _running = true;
+ start(NormalPriority);
+ return true;
+}
+
+bool BluetoothLink::_hardwareConnect()
+{
+ if(_targetSocket)
+ {
+ delete _targetSocket;
+ _targetSocket = NULL;
+ }
+ _targetDevice = new QBluetoothDeviceInfo(QBluetoothAddress(_config->device().address), _config->device().name, _config->device().bits);
+ _targetDevice->setCoreConfigurations(QBluetoothDeviceInfo::BaseRateCoreConfiguration);
+ _targetSocket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, this);
+ _targetSocket->moveToThread(this);
+ //print_device_info(*_targetDevice);
+ QObject::connect(_targetSocket, SIGNAL(connected()), this, SLOT(deviceConnected()));
+ QObject::connect(_targetSocket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(deviceError(QBluetoothSocket::SocketError)));
+ QObject::connect(_targetSocket, SIGNAL(readyRead()), this, SLOT(readBytes()));
+ QObject::connect(_targetSocket, SIGNAL(disconnected()), this, SLOT(deviceDisconnected()));
+ _targetSocket->connectToService(_targetDevice->address(), QBluetoothUuid(QBluetoothUuid::Rfcomm));
+ return true;
+}
+
+void BluetoothLink::deviceConnected()
+{
+ _connectState = true;
+ emit connected();
+}
+
+void BluetoothLink::deviceDisconnected()
+{
+ _connectState = false;
+ qWarning() << "Bluetooth disconnected";
+}
+
+void BluetoothLink::deviceError(QBluetoothSocket::SocketError error)
+{
+ _connectState = false;
+ qWarning() << "Bluetooth error" << error;
+ emit communicationError("Bluetooth Link Error", _targetSocket->errorString());
+}
+
+bool BluetoothLink::isConnected() const
+{
+ return _connectState;
+}
+
+qint64 BluetoothLink::getConnectionSpeed() const
+{
+ return 1000000; // 1 Mbit
+}
+
+qint64 BluetoothLink::getCurrentInDataRate() const
+{
+ return 0;
+}
+
+qint64 BluetoothLink::getCurrentOutDataRate() const
+{
+ return 0;
+}
+
+//--------------------------------------------------------------------------
+//-- BluetoothConfiguration
+
+BluetoothConfiguration::BluetoothConfiguration(const QString& name)
+ : LinkConfiguration(name)
+ , _deviceDiscover(NULL)
+{
+
+}
+
+BluetoothConfiguration::BluetoothConfiguration(BluetoothConfiguration* source)
+ : LinkConfiguration(source)
+ , _deviceDiscover(NULL)
+{
+ _device = source->device();
+}
+
+BluetoothConfiguration::~BluetoothConfiguration()
+{
+ if(_deviceDiscover)
+ {
+ _deviceDiscover->stop();
+ delete _deviceDiscover;
+ }
+}
+
+void BluetoothConfiguration::copyFrom(LinkConfiguration *source)
+{
+ LinkConfiguration::copyFrom(source);
+ BluetoothConfiguration* usource = dynamic_cast(source);
+ Q_ASSERT(usource != NULL);
+ _device = usource->device();
+}
+
+void BluetoothConfiguration::saveSettings(QSettings& settings, const QString& root)
+{
+ settings.beginGroup(root);
+ settings.setValue("name", _device.name);
+ settings.setValue("address", _device.address);
+ settings.setValue("bits", _device.bits);
+ settings.endGroup();
+}
+
+void BluetoothConfiguration::loadSettings(QSettings& settings, const QString& root)
+{
+ settings.beginGroup(root);
+ _device.name = settings.value("name", _device.name).toString();
+ _device.address = settings.value("address", _device.address).toString();
+ _device.bits = settings.value("bits", _device.bits).toUInt();
+ settings.endGroup();
+}
+
+void BluetoothConfiguration::updateSettings()
+{
+ if(_link) {
+ BluetoothLink* ulink = dynamic_cast(_link);
+ if(ulink) {
+ ulink->_restartConnection();
+ }
+ }
+}
+
+void BluetoothConfiguration::stopScan()
+{
+ if(_deviceDiscover)
+ {
+ _deviceDiscover->stop();
+ _deviceDiscover->deleteLater();
+ _deviceDiscover = NULL;
+ emit scanningChanged();
+ }
+}
+
+void BluetoothConfiguration::startScan()
+{
+ if(!_deviceDiscover)
+ {
+ _deviceDiscover = new QBluetoothDeviceDiscoveryAgent(this);
+ connect(_deviceDiscover, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &BluetoothConfiguration::deviceDiscovered);
+ connect(_deviceDiscover, &QBluetoothDeviceDiscoveryAgent::finished, this, &BluetoothConfiguration::doneScanning);
+ emit scanningChanged();
+ }
+ else
+ {
+ _deviceDiscover->stop();
+ }
+ _nameList.clear();
+ _deviceList.clear();
+ emit nameListChanged();
+ _deviceDiscover->setInquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry);
+ _deviceDiscover->start();
+}
+
+void BluetoothConfiguration::deviceDiscovered(QBluetoothDeviceInfo info)
+{
+ //print_device_info(info);
+ if(!info.name().isEmpty() && info.isValid())
+ {
+ BluetoothData data;
+ data.name = info.name();
+ data.address = info.address().toString();
+ data.bits |= ((qint32)info.serviceClasses() << 13); // Service Class
+ data.bits |= ((qint32)info.majorDeviceClass() << 8); // CLASS MAJOR
+ data.bits |= ((qint32)info.minorDeviceClass() << 2); // CLASS MINOR
+ if(!_deviceList.contains(data))
+ {
+ _deviceList += data;
+ _nameList += data.name;
+ emit nameListChanged();
+ return;
+ }
+ }
+}
+
+void BluetoothConfiguration::doneScanning()
+{
+ if(_deviceDiscover)
+ {
+ _deviceDiscover->deleteLater();
+ _deviceDiscover = NULL;
+ emit scanningChanged();
+ }
+}
+
+void BluetoothConfiguration::setDevName(const QString &name)
+{
+ foreach(const BluetoothData& data, _deviceList)
+ {
+ if(data.name == name)
+ {
+ _device = data;
+ emit devNameChanged();
+ emit addressChanged();
+ return;
+ }
+ }
+}
+
diff --git a/src/comm/BluetoothLink.h b/src/comm/BluetoothLink.h
new file mode 100644
index 0000000000000000000000000000000000000000..8311c630c44c32e40b04884d5611396b6f851c47
--- /dev/null
+++ b/src/comm/BluetoothLink.h
@@ -0,0 +1,188 @@
+/*=====================================================================
+
+QGroundControl Open Source Ground Control Station
+
+(c) 2009 - 2015 QGROUNDCONTROL PROJECT
+
+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 .
+
+======================================================================*/
+
+/*!
+ * @file
+ * @brief Bluetooth connection for unmanned vehicles
+ * @author Gus Grubba
+ *
+ */
+
+#ifndef BTLINK_H
+#define BTLINK_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "QGCConfig.h"
+#include "LinkManager.h"
+
+class QBluetoothDeviceDiscoveryAgent;
+
+class BluetoothData
+{
+public:
+ BluetoothData()
+ {
+ bits = 0;
+ }
+ BluetoothData(const BluetoothData& other)
+ {
+ *this = other;
+ }
+ bool operator==(const BluetoothData& other)
+ {
+ return bits == other.bits && name == other.name && address == other.address;
+ }
+ BluetoothData& operator=(const BluetoothData& other)
+ {
+ bits = other.bits;
+ name = other.name;
+ address = other.address;
+ return *this;
+ }
+ quint32 bits;
+ QString name;
+ QString address;
+};
+
+class BluetoothConfiguration : public LinkConfiguration
+{
+ Q_OBJECT
+
+public:
+
+ BluetoothConfiguration(const QString& name);
+ BluetoothConfiguration(BluetoothConfiguration* source);
+ ~BluetoothConfiguration();
+
+ Q_PROPERTY(QString devName READ devName WRITE setDevName NOTIFY devNameChanged)
+ Q_PROPERTY(QString address READ address NOTIFY addressChanged)
+ Q_PROPERTY(QStringList nameList READ nameList NOTIFY nameListChanged)
+ Q_PROPERTY(bool scanning READ scanning NOTIFY scanningChanged)
+
+ Q_INVOKABLE void startScan ();
+ Q_INVOKABLE void stopScan ();
+
+ QString devName () { return _device.name; }
+ QString address () { return _device.address; }
+ QStringList nameList () { return _nameList; }
+ bool scanning () { return _deviceDiscover != NULL; }
+
+ BluetoothData device () { return _device; }
+
+ void setDevName (const QString& name);
+
+ /// From LinkConfiguration
+ LinkType type () { return LinkConfiguration::TypeBluetooth; }
+ void copyFrom (LinkConfiguration* source);
+ void loadSettings (QSettings& settings, const QString& root);
+ void saveSettings (QSettings& settings, const QString& root);
+ void updateSettings ();
+ bool isAutoConnectAllowed () { return false; }
+ QString settingsURL () { return "BluetoothSettings.qml"; }
+
+public slots:
+ void deviceDiscovered (QBluetoothDeviceInfo info);
+ void doneScanning ();
+
+signals:
+ void newDevice (QBluetoothDeviceInfo info);
+ void devNameChanged ();
+ void addressChanged ();
+ void nameListChanged ();
+ void scanningChanged ();
+
+private:
+ QBluetoothDeviceDiscoveryAgent* _deviceDiscover;
+ BluetoothData _device;
+ QStringList _nameList;
+ QList _deviceList;
+};
+
+class BluetoothLink : public LinkInterface
+{
+ Q_OBJECT
+
+ friend class BluetoothConfiguration;
+ friend class LinkManager;
+
+public:
+ void requestReset () { }
+ bool isConnected () const;
+ QString getName () const;
+
+ // Extensive statistics for scientific purposes
+ qint64 getConnectionSpeed () const;
+ qint64 getCurrentInDataRate () const;
+ qint64 getCurrentOutDataRate () const;
+
+ void run();
+
+ // These are left unimplemented in order to cause linker errors which indicate incorrect usage of
+ // connect/disconnect on link directly. All connect/disconnect calls should be made through LinkManager.
+ bool connect (void);
+ bool disconnect (void);
+
+ LinkConfiguration* getLinkConfiguration() { return _config; }
+
+public slots:
+
+ void readBytes ();
+ void writeBytes (const char* data, qint64 length);
+ void deviceConnected ();
+ void deviceDisconnected ();
+ void deviceError (QBluetoothSocket::SocketError error);
+
+protected:
+
+ BluetoothConfiguration* _config;
+ bool _connectState;
+
+private:
+ // Links are only created/destroyed by LinkManager so constructor/destructor is not public
+ BluetoothLink(BluetoothConfiguration* config);
+ ~BluetoothLink();
+
+ // From LinkInterface
+ bool _connect (void);
+ void _disconnect (void);
+
+ bool _hardwareConnect ();
+ void _restartConnection ();
+ void _sendBytes (const char* data, qint64 size);
+
+private:
+
+ QBluetoothSocket* _targetSocket;
+ QBluetoothDeviceInfo* _targetDevice;
+ bool _running;
+};
+
+#endif // BTLINK_H
diff --git a/src/comm/LinkConfiguration.cc b/src/comm/LinkConfiguration.cc
index 1656ff5f622f9ee1c6844bb8a95e45764859db9d..0779af0606b50ab5c7b4cd38a82116214035adc0 100644
--- a/src/comm/LinkConfiguration.cc
+++ b/src/comm/LinkConfiguration.cc
@@ -36,7 +36,9 @@ This file is part of the QGROUNDCONTROL project
#ifndef __mobile__
#include "LogReplayLink.h"
#endif
-
+#ifdef __mobile__
+#include "BluetoothLink.h"
+#endif
#ifdef QT_DEBUG
#include "MockLink.h"
#endif
@@ -101,6 +103,11 @@ LinkConfiguration* LinkConfiguration::createSettings(int type, const QString& na
case LinkConfiguration::TypeTcp:
config = new TCPConfiguration(name);
break;
+#ifdef __mobile__
+ case LinkConfiguration::TypeBluetooth:
+ config = new BluetoothConfiguration(name);
+ break;
+#endif
#ifndef __mobile__
case LinkConfiguration::TypeLogReplay:
config = new LogReplayLinkConfiguration(name);
@@ -134,6 +141,11 @@ LinkConfiguration* LinkConfiguration::duplicateSettings(LinkConfiguration* sourc
case TypeTcp:
dupe = new TCPConfiguration(dynamic_cast(source));
break;
+#ifdef __mobile__
+ case TypeBluetooth:
+ dupe = new BluetoothConfiguration(dynamic_cast(source));
+ break;
+#endif
#ifndef __mobile__
case TypeLogReplay:
dupe = new LogReplayLinkConfiguration(dynamic_cast(source));
diff --git a/src/comm/LinkConfiguration.h b/src/comm/LinkConfiguration.h
index d569d4f9e495bddee12f4453238fac942c896f27..a874329a8cec8caf1130ec30ce5789ec7a877b42 100644
--- a/src/comm/LinkConfiguration.h
+++ b/src/comm/LinkConfiguration.h
@@ -57,12 +57,16 @@ public:
void setLink(LinkInterface* link);
/// The link types supported by QGC
+ /// Any changes here MUST be reflected in LinkManager::linkTypeStrings()
enum LinkType {
#ifndef __ios__
TypeSerial, ///< Serial Link
#endif
TypeUdp, ///< UDP Link
TypeTcp, ///< TCP Link
+#ifdef __mobile__
+ TypeBluetooth, ///< Bluetooth Link
+#endif
#if 0
// TODO Below is not yet implemented
TypeForwarding, ///< Forwarding Link
diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc
index 55d0a36b99abe46c912a9347eec1c3a670be66a4..3a01b6bab530e6e16b77b8a991dcb0c8e291cd9a 100644
--- a/src/comm/LinkManager.cc
+++ b/src/comm/LinkManager.cc
@@ -44,6 +44,9 @@ This file is part of the QGROUNDCONTROL project
#include "QGCApplication.h"
#include "UDPLink.h"
#include "TCPLink.h"
+#ifdef __mobile__
+#include "BluetoothLink.h"
+#endif
QGC_LOGGING_CATEGORY(LinkManagerLog, "LinkManagerLog")
QGC_LOGGING_CATEGORY(LinkManagerVerboseLog, "LinkManagerVerboseLog")
@@ -124,6 +127,11 @@ LinkInterface* LinkManager::createConnectedLink(LinkConfiguration* config)
case LinkConfiguration::TypeTcp:
pLink = new TCPLink(dynamic_cast(config));
break;
+#ifdef __mobile__
+ case LinkConfiguration::TypeBluetooth:
+ pLink = new BluetoothLink(dynamic_cast(config));
+ break;
+#endif
#ifndef __mobile__
case LinkConfiguration::TypeLogReplay:
pLink = new LogReplayLink(dynamic_cast(config));
@@ -359,6 +367,11 @@ void LinkManager::loadLinkConfigurationList()
case LinkConfiguration::TypeTcp:
pLink = (LinkConfiguration*)new TCPConfiguration(name);
break;
+#ifdef __mobile__
+ case LinkConfiguration::TypeBluetooth:
+ pLink = (LinkConfiguration*)new BluetoothConfiguration(name);
+ break;
+#endif
#ifndef __mobile__
case LinkConfiguration::TypeLogReplay:
pLink = (LinkConfiguration*)new LogReplayLinkConfiguration(name);
@@ -701,12 +714,16 @@ QStringList LinkManager::linkTypeStrings(void) const
#endif
list += "UDP";
list += "TCP";
+#ifdef __mobile__
+ list += "Bluetooth";
+#endif
#ifdef QT_DEBUG
list += "Mock Link";
#endif
#ifndef __mobile__
list += "Log Replay";
#endif
+ Q_ASSERT(list.size() == (int)LinkConfiguration::TypeLast);
}
return list;
}
@@ -806,7 +823,7 @@ void LinkManager::_fixUnnamed(LinkConfiguration* config)
#ifndef __ios__
case LinkConfiguration::TypeSerial: {
QString tname = dynamic_cast(config)->portName();
-#ifdef Q_OS_WIN32
+#ifdef Q_OS_WIN
tname.replace("\\\\.\\", "");
#else
tname.replace("/dev/cu.", "");
@@ -828,6 +845,15 @@ void LinkManager::_fixUnnamed(LinkConfiguration* config)
}
}
break;
+#ifdef __mobile__
+ case LinkConfiguration::TypeBluetooth: {
+ BluetoothConfiguration* tconfig = dynamic_cast(config);
+ if(tconfig) {
+ config->setName(QString("%1 (Bluetooth Device)").arg(tconfig->device().name));
+ }
+ }
+ break;
+#endif
#ifndef __mobile__
case LinkConfiguration::TypeLogReplay: {
LogReplayLinkConfiguration* tconfig = dynamic_cast(config);
diff --git a/src/comm/SerialLink.cc b/src/comm/SerialLink.cc
index 308537e700f8abae81af54c6975a3aa7138c3a09..4f1b250874f3b9bee310137aa4d467c5d58e6cb4 100644
--- a/src/comm/SerialLink.cc
+++ b/src/comm/SerialLink.cc
@@ -471,7 +471,7 @@ void SerialConfiguration::setPortName(const QString& portName)
QString SerialConfiguration::cleanPortDisplayname(const QString name)
{
QString pname = name.trimmed();
-#ifdef Q_OS_WIN32
+#ifdef Q_OS_WIN
pname.replace("\\\\.\\", "");
#else
pname.replace("/dev/cu.", "");
diff --git a/src/main.cc b/src/main.cc
index 2de8233a8047c6f6b9a1785c0ed718c4b6a5ad32..12ab5816add68a0c265447f73ee6b259e95bf9e9 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -50,6 +50,10 @@ This file is part of the QGROUNDCONTROL project
#endif
#endif
+#ifdef __mobile__
+#include
+#endif
+
#include
/* SDL does ugly things to main() */
@@ -135,6 +139,10 @@ int main(int argc, char *argv[])
// anyway to silence the debug output.
#ifndef __ios__
qRegisterMetaType();
+#endif
+#ifdef __mobile__
+ qRegisterMetaType();
+ qRegisterMetaType();
#endif
qRegisterMetaType();
#ifndef __mobile__
diff --git a/src/ui/QGCLinkConfiguration.cc b/src/ui/QGCLinkConfiguration.cc
index 8db70b76aec47cc597a92c6f752387c96c15dd69..279a66a48be925baa2fa417a4ef551d245d227f4 100644
--- a/src/ui/QGCLinkConfiguration.cc
+++ b/src/ui/QGCLinkConfiguration.cc
@@ -143,7 +143,7 @@ void QGCLinkConfiguration::_fixUnnamed(LinkConfiguration* config)
#ifndef __ios__
case LinkConfiguration::TypeSerial: {
QString tname = dynamic_cast(config)->portName();
-#ifdef Q_OS_WIN32
+#ifdef Q_OS_WIN
tname.replace("\\\\.\\", "");
#else
tname.replace("/dev/cu.", "");
diff --git a/src/ui/preferences/BluetoothSettings.qml b/src/ui/preferences/BluetoothSettings.qml
new file mode 100644
index 0000000000000000000000000000000000000000..c821ad4a44b1b283a42780085f0b051cdb090dc0
--- /dev/null
+++ b/src/ui/preferences/BluetoothSettings.qml
@@ -0,0 +1,169 @@
+/*=====================================================================
+
+ QGroundControl Open Source Ground Control Station
+
+ (c) 2009 - 2015 QGROUNDCONTROL PROJECT
+
+ 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 .
+
+ ======================================================================*/
+
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.Dialogs 1.1
+
+import QGroundControl 1.0
+import QGroundControl.Controls 1.0
+import QGroundControl.ScreenTools 1.0
+import QGroundControl.Palette 1.0
+
+Item {
+ id: _btSettings
+ width: parent ? parent.width : 0
+ height: btColumn.height
+
+ function saveSettings() {
+ // No need
+ }
+
+ Column {
+ id: btColumn
+ spacing: ScreenTools.defaultFontPixelHeight / 2
+
+ ExclusiveGroup { id: linkGroup }
+
+ QGCPalette {
+ id: qgcPal
+ colorGroupEnabled: enabled
+ }
+
+ QGCLabel {
+ id: btLabel
+ text: "Bluetooth Link Settings"
+ }
+ Rectangle {
+ height: 1
+ width: btLabel.width
+ color: qgcPal.button
+ }
+ Item {
+ height: ScreenTools.defaultFontPixelHeight / 2
+ width: parent.width
+ }
+ Row {
+ spacing: ScreenTools.defaultFontPixelWidth
+ QGCLabel {
+ text: "Device:"
+ width: _firstColumn
+ }
+ QGCLabel {
+ id: deviceField
+ text: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeBluetooth ? subEditConfig.devName : ""
+ }
+ }
+ Row {
+ spacing: ScreenTools.defaultFontPixelWidth
+ QGCLabel {
+ text: "Address:"
+ width: _firstColumn
+ }
+ QGCLabel {
+ id: addressField
+ text: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeBluetooth ? subEditConfig.address : ""
+ }
+ }
+ Item {
+ height: ScreenTools.defaultFontPixelHeight / 2
+ width: parent.width
+ }
+ QGCLabel {
+ text: "Bluetooth Devices:"
+ }
+ Item {
+ width: hostRow.width
+ height: hostRow.height
+ Row {
+ id: hostRow
+ spacing: ScreenTools.defaultFontPixelWidth
+ Item {
+ height: 1
+ width: _firstColumn
+ }
+ Column {
+ id: hostColumn
+ spacing: ScreenTools.defaultFontPixelHeight / 2
+ Rectangle {
+ height: 1
+ width: _secondColumn
+ color: qgcPal.button
+ visible: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeBluetooth && subEditConfig.nameList.length > 0
+ }
+ Repeater {
+ model: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeBluetooth ? subEditConfig.nameList : ""
+ delegate:
+ QGCButton {
+ text: modelData
+ width: _secondColumn
+ anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 2
+ exclusiveGroup: linkGroup
+ onClicked: {
+ checked = true
+ if(subEditConfig && modelData !== "")
+ subEditConfig.devName = modelData
+ }
+ }
+ }
+ Rectangle {
+ height: 1
+ width: _secondColumn
+ color: qgcPal.button
+ }
+ Item {
+ height: ScreenTools.defaultFontPixelHeight / 2
+ width: parent.width
+ }
+ Item {
+ width: _secondColumn
+ height: udpButtonRow.height
+ Row {
+ id: udpButtonRow
+ spacing: ScreenTools.defaultFontPixelWidth
+ anchors.horizontalCenter: parent.horizontalCenter
+ QGCButton {
+ width: ScreenTools.defaultFontPixelWidth * 10
+ text: "Scan"
+ enabled: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeBluetooth && !subEditConfig.scanning
+ onClicked: {
+ if(subEditConfig)
+ subEditConfig.startScan()
+ }
+ }
+ QGCButton {
+ width: ScreenTools.defaultFontPixelWidth * 10
+ text: "Stop"
+ enabled: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeBluetooth && subEditConfig.scanning
+ onClicked: {
+ if(subEditConfig)
+ subEditConfig.stopScan()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/ui/preferences/LinkSettings.qml b/src/ui/preferences/LinkSettings.qml
index 4088ca33a17cfb2125a4553e26f3777920ed6e89..9afbbecbcf0d57ee6a1475af4d9a43495af312c0 100644
--- a/src/ui/preferences/LinkSettings.qml
+++ b/src/ui/preferences/LinkSettings.qml
@@ -202,6 +202,7 @@ Rectangle {
}
}
Flickable {
+ id: settingsFlick
clip: true
anchors.top: parent.top
width: parent.width
diff --git a/src/ui/preferences/SerialSettings.qml b/src/ui/preferences/SerialSettings.qml
index b15101c78b43448478077469f76e031fcca432c8..06f4465ef730bb264a8cecc82e9b7ee01e8a396a 100644
--- a/src/ui/preferences/SerialSettings.qml
+++ b/src/ui/preferences/SerialSettings.qml
@@ -75,7 +75,7 @@ Item {
}
Component.onCompleted: {
if(subEditConfig != null) {
- if(subEditConfig.portDisplayName === "")
+ if(subEditConfig.portDisplayName === "" && QGroundControl.linkManager.serialPorts.length > 0)
subEditConfig.portName = QGroundControl.linkManager.serialPorts[0]
var index = commPortCombo.find(subEditConfig.portDisplayName)
if (index === -1) {