diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro
index 98e0bd5f31c18fc86534079c2388c2cd26e5d850..83ee73aa55ce9e596d6932232a85bf4a6ca9130b 100644
--- a/qgroundcontrol.pro
+++ b/qgroundcontrol.pro
@@ -69,11 +69,16 @@ QT += \
xml \
!MobileBuild {
- QT += \
+QT += \
printsupport \
serialport \
}
+!WindowsBuild {
+QT += \
+ bluetooth \
+}
+
contains(DEFINES, QGC_NOTIFY_TUNES_ENABLED) {
QT += multimedia
}
@@ -304,6 +309,9 @@ HEADERS += \
WindowsBuild {
PRECOMPILED_HEADER += src/stable_headers.h
HEADERS += src/stable_headers.h
+} else {
+ HEADERS += \
+ src/comm/BluetoothLink.h \
}
!iOSBuild {
@@ -425,6 +433,11 @@ SOURCES += \
src/ui/SerialConfigurationWindow.cc \
}
+!WindowsBuild {
+ 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..6fb2a0eb8671f1b449c762337373cde1d119e3e7
--- /dev/null
+++ b/src/comm/BluetoothLink.cc
@@ -0,0 +1,420 @@
+/*=====================================================================
+
+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();
+}
+
+BluetoothLink::BluetoothLink(BluetoothConfiguration* config)
+ : _connectState(false)
+ , _targetSocket(NULL)
+ , _targetDevice(NULL)
+ , _deviceDiscover(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()
+{
+ while (!_targetDevice && _running)
+ this->msleep(50);
+ if(_running && _hardwareConnect()) {
+ exec();
+ }
+}
+
+void BluetoothLink::deviceDiscovered(QBluetoothDeviceInfo info)
+{
+ if(info.name() == _config->device())
+ {
+ if(!_targetDevice)
+ {
+ print_device_info(info);
+ _targetDevice = new QBluetoothDeviceInfo(info);
+ //-- Start Thread
+ _running = true;
+ start(NormalPriority);
+ }
+ }
+}
+
+void BluetoothLink::doneScanning()
+{
+ if(!_targetDevice)
+ {
+ _connectState = false;
+ qWarning() << "Bluetooth scanning did not find device" << _config->device();
+ emit communicationError("Bluetooth Link Error", "Device Not Found");
+ _running = false;
+ }
+}
+
+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();
+ }
+ if(_deviceDiscover)
+ {
+ _deviceDiscover->stop();
+ delete _deviceDiscover;
+ _deviceDiscover = NULL;
+ }
+ _connectState = false;
+}
+
+bool BluetoothLink::_connect(void)
+{
+ if(this->isRunning() || _running)
+ {
+ _running = false;
+ quit();
+ wait();
+ }
+ if(_deviceDiscover)
+ {
+ _deviceDiscover->stop();
+ delete _deviceDiscover;
+ _deviceDiscover = NULL;
+ }
+ if(_targetDevice)
+ {
+ delete _targetDevice;
+ _targetDevice = NULL;
+ }
+ //-- Scan devices
+ _deviceDiscover = new QBluetoothDeviceDiscoveryAgent();
+ QObject::connect(_deviceDiscover, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)), this, SLOT(deviceDiscovered(QBluetoothDeviceInfo)));
+ QObject::connect(_deviceDiscover, SIGNAL(finished()), this, SLOT(doneScanning()));
+ _deviceDiscover->setInquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry);
+ _deviceDiscover->start();
+ return true;
+}
+
+bool BluetoothLink::_hardwareConnect()
+{
+ if(_targetSocket)
+ {
+ delete _targetSocket;
+ _targetSocket = NULL;
+ }
+ // Build Device Class Descriptor
+ /*
+ quint32 device_class = 0;
+ device_class |= ((0x20 | 0x100) << 13); // Service Class
+ device_class |= (0x04 << 8); // Audio Device (CLASS MAJOR) The Crossfire presents itself as an "Audio Service"
+ device_class |= (0x01 << 2); // WearableHeadsetDevice (CLASS MINOR)
+ qDebug() << _config->address() << _config->device() << device_class;
+ _targetDevice = new QBluetoothDeviceInfo(QBluetoothAddress(_config->address()), _config->device(), device_class);
+ _targetDevice->setCoreConfigurations(QBluetoothDeviceInfo::BaseRateCoreConfiguration);
+ */
+
+ _targetSocket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, this);
+ _targetSocket->moveToThread(this);
+
+ qDebug() << "Connecting...";
+ 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()));
+ QObject::connect(_targetSocket, SIGNAL(stateChanged(QBluetoothSocket::SocketState)), this, SLOT(stateChanged(QBluetoothSocket::SocketState)));
+ _targetSocket->connectToService(_targetDevice->address(), QBluetoothUuid(QBluetoothUuid::Rfcomm));
+ return true;
+}
+
+void BluetoothLink::stateChanged(QBluetoothSocket::SocketState state)
+{
+ qDebug() << "Bluetooth state:" << state;
+}
+
+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)
+{
+ _address = source->address();
+ _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);
+ _address = usource->address();
+ _device = usource->device();
+}
+
+void BluetoothConfiguration::saveSettings(QSettings& settings, const QString& root)
+{
+ settings.beginGroup(root);
+ settings.setValue("device", _device);
+ settings.setValue("address", _address);
+ settings.endGroup();
+}
+
+void BluetoothConfiguration::loadSettings(QSettings& settings, const QString& root)
+{
+ settings.beginGroup(root);
+ QString device = settings.value("device", _device).toString();
+ QString address = settings.value("address", _address).toString();
+ _device = device;
+ _address = address;
+ settings.endGroup();
+}
+
+void BluetoothConfiguration::updateSettings()
+{
+ if(_link) {
+ BluetoothLink* ulink = dynamic_cast(_link);
+ if(ulink) {
+ ulink->_restartConnection();
+ }
+ }
+}
+
+void BluetoothConfiguration::stopScan()
+{
+ if(_deviceDiscover)
+ {
+
+ }
+}
+
+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();
+ }
+ _deviceList.clear();
+ _addressList.clear();
+ emit deviceListChanged();
+ _deviceDiscover->setInquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry);
+ _deviceDiscover->start();
+}
+
+void BluetoothConfiguration::deviceDiscovered(QBluetoothDeviceInfo info)
+{
+ _deviceList += info.name();
+ _addressList += info.address().toString();
+ print_device_info(info);
+ emit deviceListChanged();
+}
+
+void BluetoothConfiguration::doneScanning()
+{
+ if(_deviceDiscover)
+ {
+ _deviceDiscover->deleteLater();
+ _deviceDiscover = NULL;
+ emit scanningChanged();
+ }
+}
+
+void BluetoothConfiguration::setDevice(QString device)
+{
+ int idx = _deviceList.indexOf(device);
+ if(idx >= 0)
+ {
+ _address = _addressList.at(idx);
+ _device = device;
+ emit deviceChanged();
+ emit addressChanged();
+ }
+}
+
diff --git a/src/comm/BluetoothLink.h b/src/comm/BluetoothLink.h
new file mode 100644
index 0000000000000000000000000000000000000000..ee4993c41e0e782391e0896f2263b3aff8b3b18f
--- /dev/null
+++ b/src/comm/BluetoothLink.h
@@ -0,0 +1,166 @@
+/*=====================================================================
+
+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 BluetoothConfiguration : public LinkConfiguration
+{
+ Q_OBJECT
+
+public:
+
+ BluetoothConfiguration(const QString& name);
+ BluetoothConfiguration(BluetoothConfiguration* source);
+ ~BluetoothConfiguration();
+
+ Q_PROPERTY(QString device READ device WRITE setDevice NOTIFY deviceChanged)
+ Q_PROPERTY(QString address READ address WRITE setAddress NOTIFY addressChanged)
+ Q_PROPERTY(QStringList deviceList READ deviceList NOTIFY deviceListChanged)
+ Q_PROPERTY(bool scanning READ scanning NOTIFY scanningChanged)
+
+ Q_INVOKABLE void startScan ();
+ Q_INVOKABLE void stopScan ();
+
+ QString device () { return _device; }
+ QString address () { return _address; }
+ QStringList deviceList () { return _deviceList; }
+ bool scanning () { return _deviceDiscover != NULL; }
+
+ void setDevice (QString device);
+ void setAddress (QString address) { _address = address; emit addressChanged(); }
+
+ /// 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 ();
+ QString settingsURL () { return "BluetoothSettings.qml"; }
+
+public slots:
+ void deviceDiscovered (QBluetoothDeviceInfo info);
+ void doneScanning ();
+
+signals:
+ void newDevice (QBluetoothDeviceInfo info);
+ void deviceChanged ();
+ void addressChanged ();
+ void deviceListChanged ();
+ void scanningChanged ();
+
+private:
+
+private:
+ QBluetoothDeviceDiscoveryAgent* _deviceDiscover;
+ QString _device;
+ QString _address;
+ QStringList _deviceList;
+ QStringList _addressList;
+};
+
+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);
+ void stateChanged (QBluetoothSocket::SocketState state);
+ void deviceDiscovered (QBluetoothDeviceInfo info);
+ void doneScanning ();
+
+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;
+ QBluetoothDeviceDiscoveryAgent* _deviceDiscover;
+ bool _running;
+};
+
+#endif // BTLINK_H
diff --git a/src/comm/LinkConfiguration.cc b/src/comm/LinkConfiguration.cc
index 1656ff5f622f9ee1c6844bb8a95e45764859db9d..9af87788f2658a56c6c3a9f6d1f2e95da610c56e 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
-
+#ifndef Q_OS_WIN
+#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;
+#ifndef Q_OS_WIN
+ 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;
+#ifndef Q_OS_WIN
+ 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..082f0961842cb4ef5f87f86a6a03162eae70b9d2 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
+#ifndef Q_OS_WIN
+ 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..93b5e5c2a1c46fe10a5d99237b6e8f28bbec2e57 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"
+#ifndef Q_OS_WIN
+#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;
+#ifndef Q_OS_WIN
+ 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;
+#ifndef Q_OS_WIN
+ 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";
+#ifndef Q_OS_WIN
+ 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;
}
@@ -828,6 +845,15 @@ void LinkManager::_fixUnnamed(LinkConfiguration* config)
}
}
break;
+#ifndef Q_OS_WIN
+ case LinkConfiguration::TypeBluetooth: {
+ BluetoothConfiguration* tconfig = dynamic_cast(config);
+ if(tconfig) {
+ config->setName(QString("Bluetooth Device on %1").arg(tconfig->device()));
+ }
+ }
+ break;
+#endif
#ifndef __mobile__
case LinkConfiguration::TypeLogReplay: {
LogReplayLinkConfiguration* tconfig = dynamic_cast(config);
diff --git a/src/main.cc b/src/main.cc
index 2de8233a8047c6f6b9a1785c0ed718c4b6a5ad32..03a524fce84ac470a20777f08fb4029a2fa6bbf5 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -50,6 +50,10 @@ This file is part of the QGROUNDCONTROL project
#endif
#endif
+#ifndef Q_OS_WIN
+#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
+#ifndef Q_OS_WIN
+ qRegisterMetaType();
+ qRegisterMetaType();
#endif
qRegisterMetaType();
#ifndef __mobile__
diff --git a/src/ui/preferences/BluetoothSettings.qml b/src/ui/preferences/BluetoothSettings.qml
new file mode 100644
index 0000000000000000000000000000000000000000..b6a10e4ec81be622c4311009675018e5eeb5a347
--- /dev/null
+++ b/src/ui/preferences/BluetoothSettings.qml
@@ -0,0 +1,179 @@
+/*=====================================================================
+
+ 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
+ }
+
+ property var _currentDevice: ""
+
+ 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.device : ""
+ }
+ }
+ 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.deviceList.length > 0
+ }
+ Repeater {
+ model: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeBluetooth ? subEditConfig.deviceList : ""
+ delegate:
+ QGCButton {
+ text: modelData
+ width: _secondColumn
+ anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 2
+ exclusiveGroup: linkGroup
+ onClicked: {
+ checked = true
+ _btSettings._currentDevice = 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()
+ }
+ }
+ QGCButton {
+ width: ScreenTools.defaultFontPixelWidth * 10
+ enabled: _btSettings._currentDevice && _btSettings._currentDevice !== ""
+ text: "Select"
+ onClicked: {
+ if(subEditConfig)
+ subEditConfig.device = _btSettings._currentDevice
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}