Commit 1cc4c7d8 authored by Gus Grubba's avatar Gus Grubba

Merge pull request #2354 from dogmaphobic/bluetooth

Bluetooth
parents dfa702a4 4211dca4
...@@ -69,11 +69,16 @@ QT += \ ...@@ -69,11 +69,16 @@ QT += \
xml \ xml \
!MobileBuild { !MobileBuild {
QT += \ QT += \
printsupport \ printsupport \
serialport \ serialport \
} }
MobileBuild {
QT += \
bluetooth \
}
contains(DEFINES, QGC_NOTIFY_TUNES_ENABLED) { contains(DEFINES, QGC_NOTIFY_TUNES_ENABLED) {
QT += multimedia QT += multimedia
} }
...@@ -306,6 +311,11 @@ WindowsBuild { ...@@ -306,6 +311,11 @@ WindowsBuild {
HEADERS += src/stable_headers.h HEADERS += src/stable_headers.h
} }
MobileBuild {
HEADERS += \
src/comm/BluetoothLink.h \
}
!iOSBuild { !iOSBuild {
HEADERS += \ HEADERS += \
src/comm/QGCSerialPortInfo.h \ src/comm/QGCSerialPortInfo.h \
...@@ -425,6 +435,11 @@ SOURCES += \ ...@@ -425,6 +435,11 @@ SOURCES += \
src/ui/SerialConfigurationWindow.cc \ src/ui/SerialConfigurationWindow.cc \
} }
MobileBuild {
SOURCES += \
src/comm/BluetoothLink.cc \
}
!MobileBuild { !MobileBuild {
SOURCES += \ SOURCES += \
src/comm/LogReplayLink.cc \ src/comm/LogReplayLink.cc \
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
<file alias="FlightModesComponent.qml">src/AutoPilotPlugins/PX4/FlightModesComponent.qml</file> <file alias="FlightModesComponent.qml">src/AutoPilotPlugins/PX4/FlightModesComponent.qml</file>
<file alias="FlightModesComponentSummary.qml">src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml</file> <file alias="FlightModesComponentSummary.qml">src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml</file>
<file alias="BluetoothSettings.qml">src/ui/preferences/BluetoothSettings.qml</file>
<file alias="DebugWindow.qml">src/ui/preferences/DebugWindow.qml</file> <file alias="DebugWindow.qml">src/ui/preferences/DebugWindow.qml</file>
<file alias="GeneralSettings.qml">src/ui/preferences/GeneralSettings.qml</file> <file alias="GeneralSettings.qml">src/ui/preferences/GeneralSettings.qml</file>
<file alias="LinkSettings.qml">src/ui/preferences/LinkSettings.qml</file> <file alias="LinkSettings.qml">src/ui/preferences/LinkSettings.qml</file>
......
This diff is collapsed.
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
/*!
* @file
* @brief Bluetooth connection for unmanned vehicles
* @author Gus Grubba <mavlink@grubba.com>
*
*/
#ifndef BTLINK_H
#define BTLINK_H
#include <QString>
#include <QList>
#include <QMutex>
#include <QMutexLocker>
#include <QQueue>
#include <QByteArray>
#include <QBluetoothDeviceInfo>
#include <QtBluetooth/QBluetoothSocket>
#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<BluetoothData> _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
...@@ -36,7 +36,9 @@ This file is part of the QGROUNDCONTROL project ...@@ -36,7 +36,9 @@ This file is part of the QGROUNDCONTROL project
#ifndef __mobile__ #ifndef __mobile__
#include "LogReplayLink.h" #include "LogReplayLink.h"
#endif #endif
#ifdef __mobile__
#include "BluetoothLink.h"
#endif
#ifdef QT_DEBUG #ifdef QT_DEBUG
#include "MockLink.h" #include "MockLink.h"
#endif #endif
...@@ -101,6 +103,11 @@ LinkConfiguration* LinkConfiguration::createSettings(int type, const QString& na ...@@ -101,6 +103,11 @@ LinkConfiguration* LinkConfiguration::createSettings(int type, const QString& na
case LinkConfiguration::TypeTcp: case LinkConfiguration::TypeTcp:
config = new TCPConfiguration(name); config = new TCPConfiguration(name);
break; break;
#ifdef __mobile__
case LinkConfiguration::TypeBluetooth:
config = new BluetoothConfiguration(name);
break;
#endif
#ifndef __mobile__ #ifndef __mobile__
case LinkConfiguration::TypeLogReplay: case LinkConfiguration::TypeLogReplay:
config = new LogReplayLinkConfiguration(name); config = new LogReplayLinkConfiguration(name);
...@@ -134,6 +141,11 @@ LinkConfiguration* LinkConfiguration::duplicateSettings(LinkConfiguration* sourc ...@@ -134,6 +141,11 @@ LinkConfiguration* LinkConfiguration::duplicateSettings(LinkConfiguration* sourc
case TypeTcp: case TypeTcp:
dupe = new TCPConfiguration(dynamic_cast<TCPConfiguration*>(source)); dupe = new TCPConfiguration(dynamic_cast<TCPConfiguration*>(source));
break; break;
#ifdef __mobile__
case TypeBluetooth:
dupe = new BluetoothConfiguration(dynamic_cast<BluetoothConfiguration*>(source));
break;
#endif
#ifndef __mobile__ #ifndef __mobile__
case TypeLogReplay: case TypeLogReplay:
dupe = new LogReplayLinkConfiguration(dynamic_cast<LogReplayLinkConfiguration*>(source)); dupe = new LogReplayLinkConfiguration(dynamic_cast<LogReplayLinkConfiguration*>(source));
......
...@@ -57,12 +57,16 @@ public: ...@@ -57,12 +57,16 @@ public:
void setLink(LinkInterface* link); void setLink(LinkInterface* link);
/// The link types supported by QGC /// The link types supported by QGC
/// Any changes here MUST be reflected in LinkManager::linkTypeStrings()
enum LinkType { enum LinkType {
#ifndef __ios__ #ifndef __ios__
TypeSerial, ///< Serial Link TypeSerial, ///< Serial Link
#endif #endif
TypeUdp, ///< UDP Link TypeUdp, ///< UDP Link
TypeTcp, ///< TCP Link TypeTcp, ///< TCP Link
#ifdef __mobile__
TypeBluetooth, ///< Bluetooth Link
#endif
#if 0 #if 0
// TODO Below is not yet implemented // TODO Below is not yet implemented
TypeForwarding, ///< Forwarding Link TypeForwarding, ///< Forwarding Link
......
...@@ -44,6 +44,9 @@ This file is part of the QGROUNDCONTROL project ...@@ -44,6 +44,9 @@ This file is part of the QGROUNDCONTROL project
#include "QGCApplication.h" #include "QGCApplication.h"
#include "UDPLink.h" #include "UDPLink.h"
#include "TCPLink.h" #include "TCPLink.h"
#ifdef __mobile__
#include "BluetoothLink.h"
#endif
QGC_LOGGING_CATEGORY(LinkManagerLog, "LinkManagerLog") QGC_LOGGING_CATEGORY(LinkManagerLog, "LinkManagerLog")
QGC_LOGGING_CATEGORY(LinkManagerVerboseLog, "LinkManagerVerboseLog") QGC_LOGGING_CATEGORY(LinkManagerVerboseLog, "LinkManagerVerboseLog")
...@@ -124,6 +127,11 @@ LinkInterface* LinkManager::createConnectedLink(LinkConfiguration* config) ...@@ -124,6 +127,11 @@ LinkInterface* LinkManager::createConnectedLink(LinkConfiguration* config)
case LinkConfiguration::TypeTcp: case LinkConfiguration::TypeTcp:
pLink = new TCPLink(dynamic_cast<TCPConfiguration*>(config)); pLink = new TCPLink(dynamic_cast<TCPConfiguration*>(config));
break; break;
#ifdef __mobile__
case LinkConfiguration::TypeBluetooth:
pLink = new BluetoothLink(dynamic_cast<BluetoothConfiguration*>(config));
break;
#endif
#ifndef __mobile__ #ifndef __mobile__
case LinkConfiguration::TypeLogReplay: case LinkConfiguration::TypeLogReplay:
pLink = new LogReplayLink(dynamic_cast<LogReplayLinkConfiguration*>(config)); pLink = new LogReplayLink(dynamic_cast<LogReplayLinkConfiguration*>(config));
...@@ -359,6 +367,11 @@ void LinkManager::loadLinkConfigurationList() ...@@ -359,6 +367,11 @@ void LinkManager::loadLinkConfigurationList()
case LinkConfiguration::TypeTcp: case LinkConfiguration::TypeTcp:
pLink = (LinkConfiguration*)new TCPConfiguration(name); pLink = (LinkConfiguration*)new TCPConfiguration(name);
break; break;
#ifdef __mobile__
case LinkConfiguration::TypeBluetooth:
pLink = (LinkConfiguration*)new BluetoothConfiguration(name);
break;
#endif
#ifndef __mobile__ #ifndef __mobile__
case LinkConfiguration::TypeLogReplay: case LinkConfiguration::TypeLogReplay:
pLink = (LinkConfiguration*)new LogReplayLinkConfiguration(name); pLink = (LinkConfiguration*)new LogReplayLinkConfiguration(name);
...@@ -701,12 +714,16 @@ QStringList LinkManager::linkTypeStrings(void) const ...@@ -701,12 +714,16 @@ QStringList LinkManager::linkTypeStrings(void) const
#endif #endif
list += "UDP"; list += "UDP";
list += "TCP"; list += "TCP";
#ifdef __mobile__
list += "Bluetooth";
#endif
#ifdef QT_DEBUG #ifdef QT_DEBUG
list += "Mock Link"; list += "Mock Link";
#endif #endif
#ifndef __mobile__ #ifndef __mobile__
list += "Log Replay"; list += "Log Replay";
#endif #endif
Q_ASSERT(list.size() == (int)LinkConfiguration::TypeLast);
} }
return list; return list;
} }
...@@ -806,7 +823,7 @@ void LinkManager::_fixUnnamed(LinkConfiguration* config) ...@@ -806,7 +823,7 @@ void LinkManager::_fixUnnamed(LinkConfiguration* config)
#ifndef __ios__ #ifndef __ios__
case LinkConfiguration::TypeSerial: { case LinkConfiguration::TypeSerial: {
QString tname = dynamic_cast<SerialConfiguration*>(config)->portName(); QString tname = dynamic_cast<SerialConfiguration*>(config)->portName();
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN
tname.replace("\\\\.\\", ""); tname.replace("\\\\.\\", "");
#else #else
tname.replace("/dev/cu.", ""); tname.replace("/dev/cu.", "");
...@@ -828,6 +845,15 @@ void LinkManager::_fixUnnamed(LinkConfiguration* config) ...@@ -828,6 +845,15 @@ void LinkManager::_fixUnnamed(LinkConfiguration* config)
} }
} }
break; break;
#ifdef __mobile__
case LinkConfiguration::TypeBluetooth: {
BluetoothConfiguration* tconfig = dynamic_cast<BluetoothConfiguration*>(config);
if(tconfig) {
config->setName(QString("%1 (Bluetooth Device)").arg(tconfig->device().name));
}
}
break;
#endif
#ifndef __mobile__ #ifndef __mobile__
case LinkConfiguration::TypeLogReplay: { case LinkConfiguration::TypeLogReplay: {
LogReplayLinkConfiguration* tconfig = dynamic_cast<LogReplayLinkConfiguration*>(config); LogReplayLinkConfiguration* tconfig = dynamic_cast<LogReplayLinkConfiguration*>(config);
......
...@@ -471,7 +471,7 @@ void SerialConfiguration::setPortName(const QString& portName) ...@@ -471,7 +471,7 @@ void SerialConfiguration::setPortName(const QString& portName)
QString SerialConfiguration::cleanPortDisplayname(const QString name) QString SerialConfiguration::cleanPortDisplayname(const QString name)
{ {
QString pname = name.trimmed(); QString pname = name.trimmed();
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN
pname.replace("\\\\.\\", ""); pname.replace("\\\\.\\", "");
#else #else
pname.replace("/dev/cu.", ""); pname.replace("/dev/cu.", "");
......
...@@ -50,6 +50,10 @@ This file is part of the QGROUNDCONTROL project ...@@ -50,6 +50,10 @@ This file is part of the QGROUNDCONTROL project
#endif #endif
#endif #endif
#ifdef __mobile__
#include <QtBluetooth/QBluetoothSocket>
#endif
#include <iostream> #include <iostream>
/* SDL does ugly things to main() */ /* SDL does ugly things to main() */
...@@ -135,6 +139,10 @@ int main(int argc, char *argv[]) ...@@ -135,6 +139,10 @@ int main(int argc, char *argv[])
// anyway to silence the debug output. // anyway to silence the debug output.
#ifndef __ios__ #ifndef __ios__
qRegisterMetaType<QSerialPort::SerialPortError>(); qRegisterMetaType<QSerialPort::SerialPortError>();
#endif
#ifdef __mobile__
qRegisterMetaType<QBluetoothSocket::SocketError>();
qRegisterMetaType<QBluetoothServiceInfo>();
#endif #endif
qRegisterMetaType<QAbstractSocket::SocketError>(); qRegisterMetaType<QAbstractSocket::SocketError>();
#ifndef __mobile__ #ifndef __mobile__
......
...@@ -143,7 +143,7 @@ void QGCLinkConfiguration::_fixUnnamed(LinkConfiguration* config) ...@@ -143,7 +143,7 @@ void QGCLinkConfiguration::_fixUnnamed(LinkConfiguration* config)
#ifndef __ios__ #ifndef __ios__
case LinkConfiguration::TypeSerial: { case LinkConfiguration::TypeSerial: {
QString tname = dynamic_cast<SerialConfiguration*>(config)->portName(); QString tname = dynamic_cast<SerialConfiguration*>(config)->portName();
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN
tname.replace("\\\\.\\", ""); tname.replace("\\\\.\\", "");
#else #else
tname.replace("/dev/cu.", ""); tname.replace("/dev/cu.", "");
......
/*=====================================================================
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/>.
======================================================================*/
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()
}
}
}
}
}
}
}
}
}
...@@ -202,6 +202,7 @@ Rectangle { ...@@ -202,6 +202,7 @@ Rectangle {
} }
} }
Flickable { Flickable {
id: settingsFlick
clip: true clip: true
anchors.top: parent.top anchors.top: parent.top
width: parent.width width: parent.width
......
...@@ -75,7 +75,7 @@ Item { ...@@ -75,7 +75,7 @@ Item {
} }
Component.onCompleted: { Component.onCompleted: {
if(subEditConfig != null) { if(subEditConfig != null) {
if(subEditConfig.portDisplayName === "") if(subEditConfig.portDisplayName === "" && QGroundControl.linkManager.serialPorts.length > 0)
subEditConfig.portName = QGroundControl.linkManager.serialPorts[0] subEditConfig.portName = QGroundControl.linkManager.serialPorts[0]
var index = commPortCombo.find(subEditConfig.portDisplayName) var index = commPortCombo.find(subEditConfig.portDisplayName)
if (index === -1) { if (index === -1) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment