Commit f752bf09 authored by Gus Grubba's avatar Gus Grubba

Merge pull request #3299 from dogmaphobic/supportForNewESPFirmware

Support for new esp firmware
parents 9c023cdf 5a07b8ee
......@@ -145,6 +145,8 @@
<file alias="TelemRSSI.svg">src/ui/toolbar/Images/TelemRSSI.svg</file>
<file alias="Yield.svg">src/ui/toolbar/Images/Yield.svg</file>
<file alias="CogWheel.svg">src/MissionManager/CogWheel.svg</file>
<file alias="StationMode.svg">src/AutoPilotPlugins/Common/Images/StationMode.svg</file>
<file alias="APMode.svg">src/AutoPilotPlugins/Common/Images/APMode.svg</file>
</qresource>
<qresource prefix="/res">
<file alias="AntennaRC">resources/Antenna_RC.svg</file>
......
......@@ -40,13 +40,16 @@ QGCView {
QGCPalette { id: palette; colorGroupEnabled: panel.enabled }
property int _firstColumn: ScreenTools.defaultFontPixelWidth * 20
property int _secondColumn: ScreenTools.defaultFontPixelWidth * 12
property real _margins: ScreenTools.defaultFontPixelHeight
property real _middleRowWidth: ScreenTools.defaultFontPixelWidth * 18
property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 18
property real _labelWidth: ScreenTools.defaultFontPixelWidth * 10
property real _statusWidth: ScreenTools.defaultFontPixelWidth * 5
readonly property string dialogTitle: qsTr("controller WiFi Bridge")
property int stStatus: XMLHttpRequest.UNSENT
property int stErrorCount: 0
property bool stEnabled: false
property bool stResetCounters: false
property bool stResetCounters:false
ESP8266ComponentController {
id: controller
......@@ -64,7 +67,9 @@ QGCView {
function updateStatus() {
timer.stop()
var req = new XMLHttpRequest;
var url = "http://192.168.4.1/status.json"
var url = "http://"
url += controller.wifiIPAddress
url += "/status.json"
if(stResetCounters) {
url = url + "?r=1"
stResetCounters = false
......@@ -77,24 +82,21 @@ QGCView {
if (objectArray.errors !== undefined) {
console.log(qsTr("Error fetching WiFi Bridge Status: %1").arg(objectArray.errors[0].message))
stErrorCount = stErrorCount + 1
if(stErrorCount < 2 && stEnabled)
if(stErrorCount < 2)
timer.start()
} else {
if(stEnabled) {
//-- This should work but it doesn't
// var n = 34523453.345
// n.toLocaleString()
// "34,523,453.345"
vpackets.text = thisThingHasNoNumberLocaleSupport(objectArray["vpackets"])
vsent.text = thisThingHasNoNumberLocaleSupport(objectArray["vsent"])
vlost.text = thisThingHasNoNumberLocaleSupport(objectArray["vlost"])
gpackets.text = thisThingHasNoNumberLocaleSupport(objectArray["gpackets"])
gsent.text = thisThingHasNoNumberLocaleSupport(objectArray["gsent"])
glost.text = thisThingHasNoNumberLocaleSupport(objectArray["glost"])
stErrorCount = 0
wifiStatus.visible = true
timer.start()
}
//-- This should work but it doesn't
// var n = 34523453.345
// n.toLocaleString()
// "34,523,453.345"
vpackets.text = thisThingHasNoNumberLocaleSupport(objectArray["vpackets"])
vsent.text = thisThingHasNoNumberLocaleSupport(objectArray["vsent"])
vlost.text = thisThingHasNoNumberLocaleSupport(objectArray["vlost"])
gpackets.text = thisThingHasNoNumberLocaleSupport(objectArray["gpackets"])
gsent.text = thisThingHasNoNumberLocaleSupport(objectArray["gsent"])
glost.text = thisThingHasNoNumberLocaleSupport(objectArray["glost"])
stErrorCount = 0
timer.start()
}
}
}
......@@ -105,8 +107,10 @@ QGCView {
timer.interval = 1000
timer.repeat = true
timer.triggered.connect(updateStatus)
timer.start()
}
property Fact wifiMode: controller.getParameterFact(controller.componentID, "WIFI_MODE", false) //-- Don't bitch about missing as this is new
property Fact wifiChannel: controller.getParameterFact(controller.componentID, "WIFI_CHANNEL")
property Fact hostPort: controller.getParameterFact(controller.componentID, "WIFI_UDP_HPORT")
property Fact clientPort: controller.getParameterFact(controller.componentID, "WIFI_UDP_CPORT")
......@@ -116,265 +120,353 @@ QGCView {
anchors.fill: parent
Flickable {
anchors.fill: parent
clip: true
contentHeight: innerColumn.height
contentWidth: panel.width
boundsBehavior: Flickable.StopAtBounds
flickableDirection: Flickable.VerticalFlick
clip: true
anchors.fill: parent
contentHeight: mainCol.height
flickableDirection: Flickable.VerticalFlick
Column {
id: innerColumn
width: panel.width
spacing: ScreenTools.defaultFontPixelHeight * 0.5
id: mainCol
spacing: _margins
anchors.horizontalCenter: parent.horizontalCenter
Item { width: 1; height: _margins * 0.5; }
QGCLabel {
text: qsTr("WiFi Bridge Settings")
font.weight: Font.DemiBold
text: qsTr("ESP WiFi Bridge Settings")
font.weight: Font.DemiBold
}
Rectangle {
width: parent.width
height: wifiStatus.visible ? Math.max(wifiCol.height, wifiStatus.height) + (ScreenTools.defaultFontPixelHeight * 2) : wifiCol.height + (ScreenTools.defaultFontPixelHeight * 2)
color: palette.windowShade
color: palette.windowShade
width: statusLayout.width + _margins * 4
height: settingsRow.height + _margins * 2
Row {
anchors.verticalCenter: parent.verticalCenter
spacing: ScreenTools.defaultFontPixelWidth
Rectangle {
height: parent.height
width: 1
color: palette.window
id: settingsRow
spacing: _margins
anchors.centerIn: parent
Item { width: _margins * 0.5; height: 1; }
QGCColoredImage {
color: palette.text
height: ScreenTools.defaultFontPixelWidth * 10
width: ScreenTools.defaultFontPixelWidth * 12
mipmap: true
fillMode: Image.PreserveAspectFit
source: wifiMode ? (wifiMode.value === 0 ? "/qmlimages/APMode.svg" : "/qmlimages/StationMode.svg") : "/qmlimages/APMode.svg"
anchors.verticalCenter: parent.verticalCenter
}
Item { width: _margins * 0.5; height: 1; }
Column {
id: wifiCol
anchors.verticalCenter: parent.verticalCenter
spacing: ScreenTools.defaultFontPixelHeight / 2
spacing: _margins * 0.5
anchors.verticalCenter: parent.verticalCenter
Row {
spacing: ScreenTools.defaultFontPixelWidth
visible: wifiMode
QGCLabel {
text: qsTr("WiFi Channel")
width: _firstColumn
anchors.baseline: channelField.baseline
text: qsTr("WiFi Mode")
width: _middleRowWidth
anchors.baseline: modeField.baseline
}
QGCComboBox {
id: channelField
width: _secondColumn
model: controller.wifiChannels
currentIndex: wifiChannel ? wifiChannel.value - 1 : 0
id: modeField
width: _editFieldWidth
model: ["Access Point Mode", "Station Mode"]
currentIndex: wifiMode ? wifiMode.value : 0
onActivated: {
wifiChannel.value = index + 1
}
}
}
Row {
spacing: ScreenTools.defaultFontPixelWidth
QGCLabel {
text: qsTr("WiFi SSID")
width: _firstColumn
anchors.baseline: ssidField.baseline
}
QGCTextField {
id: ssidField
width: _secondColumn
text: controller.wifiSSID
maximumLength: 16
onEditingFinished: {
controller.wifiSSID = text
wifiMode.value = index
}
}
}
Row {
spacing: ScreenTools.defaultFontPixelWidth
QGCLabel {
text: qsTr("WiFi Password")
width: _firstColumn
anchors.baseline: passwordField.baseline
}
QGCTextField {
id: passwordField
width: _secondColumn
text: controller.wifiPassword
maximumLength: 16
onEditingFinished: {
controller.wifiPassword = text
}
}
}
Row {
spacing: ScreenTools.defaultFontPixelWidth
QGCLabel {
text: qsTr("UART Baud Rate")
width: _firstColumn
anchors.baseline: baudField.baseline
text: qsTr("WiFi Channel")
width: _middleRowWidth
anchors.baseline: channelField.baseline
}
QGCComboBox {
id: baudField
width: _secondColumn
model: controller.baudRates
currentIndex: controller.baudIndex
id: channelField
width: _editFieldWidth
enabled: wifiMode && wifiMode.value === 0
model: controller.wifiChannels
currentIndex: wifiChannel ? wifiChannel.value - 1 : 0
onActivated: {
controller.baudIndex = index
wifiChannel.value = index + 1
}
}
}
Row {
spacing: ScreenTools.defaultFontPixelWidth
QGCLabel {
text: qsTr("QGC UDP Port")
width: _firstColumn
anchors.baseline: qgcportField.baseline
text: qsTr("WiFi AP SSID")
width: _middleRowWidth
anchors.baseline: ssidField.baseline
}
QGCTextField {
id: qgcportField
width: _secondColumn
text: hostPort ? hostPort.valueString : ""
validator: IntValidator {bottom: 1024; top: 65535;}
inputMethodHints: Qt.ImhDigitsOnly
id: ssidField
width: _editFieldWidth
text: controller.wifiSSID
maximumLength: 16
onEditingFinished: {
hostPort.value = text
controller.wifiSSID = text
}
}
}
}
Rectangle {
height: parent.height
width: 1
color: palette.text
visible: wifiStatus.visible
}
Column {
id: wifiStatus
anchors.margins: ScreenTools.defaultFontPixelHeight / 2
spacing: ScreenTools.defaultFontPixelHeight / 2
visible: false
QGCLabel {
text: qsTr("Bridge/Vehicle Link")
font.weight: Font.DemiBold
}
Row {
spacing: ScreenTools.defaultFontPixelWidth
QGCLabel {
text: qsTr("Messages Received")
width: _firstColumn
}
QGCLabel {
id: vpackets
}
}
Row {
spacing: ScreenTools.defaultFontPixelWidth
QGCLabel {
text: qsTr("Messages Lost")
width: _firstColumn
}
QGCLabel {
id: vlost
}
}
Row {
spacing: ScreenTools.defaultFontPixelWidth
QGCLabel {
text: qsTr("Messages Sent")
width: _firstColumn
}
QGCLabel {
id: vsent
}
}
Rectangle {
height: 1
width: parent.width
color: palette.text
}
QGCLabel {
text: qsTr("Bridge/QGC Link")
font.weight: Font.DemiBold
}
Row {
spacing: ScreenTools.defaultFontPixelWidth
QGCLabel {
text: qsTr("Messages Received")
width: _firstColumn
}
QGCLabel {
id: gpackets
}
}
Row {
spacing: ScreenTools.defaultFontPixelWidth
QGCLabel {
text: qsTr("Messages Lost")
width: _firstColumn
text: qsTr("WiFi AP Password")
width: _middleRowWidth
anchors.baseline: passwordField.baseline
}
QGCLabel {
id: glost
QGCTextField {
id: passwordField
width: _editFieldWidth
text: controller.wifiPassword
maximumLength: 16
onEditingFinished: {
controller.wifiPassword = text
}
}
}
Row {
spacing: ScreenTools.defaultFontPixelWidth
QGCLabel {
text: qsTr("Messages Sent")
width: _firstColumn
text: qsTr("WiFi STA SSID")
width: _middleRowWidth
anchors.baseline: stassidField.baseline
}
QGCLabel {
id: gsent
QGCTextField {
id: stassidField
width: _editFieldWidth
text: controller.wifiSSIDSta
maximumLength: 16
enabled: wifiMode && wifiMode.value === 1
onEditingFinished: {
controller.wifiSSIDSta = text
}
}
}
Rectangle {
height: 1
width: parent.width
color: palette.text
}
QGCLabel {
text: qsTr("QGC/Bridge Link")
font.weight: Font.DemiBold
}
Row {
spacing: ScreenTools.defaultFontPixelWidth
QGCLabel {
text: qsTr("Messages Received")
width: _firstColumn
text: qsTr("WiFi STA Password")
width: _middleRowWidth
anchors.baseline: passwordStaField.baseline
}
QGCLabel {
text: controller.vehicle ? thisThingHasNoNumberLocaleSupport(controller.vehicle.messagesReceived) : 0
QGCTextField {
id: passwordStaField
width: _editFieldWidth
text: controller.wifiPasswordSta
maximumLength: 16
enabled: wifiMode && wifiMode.value === 1
onEditingFinished: {
controller.wifiPasswordSta = text
}
}
}
Row {
spacing: ScreenTools.defaultFontPixelWidth
QGCLabel {
text: qsTr("Messages Lost")
width: _firstColumn
text: qsTr("UART Baud Rate")
width: _middleRowWidth
anchors.baseline: baudField.baseline
}
QGCLabel {
text: controller.vehicle ? thisThingHasNoNumberLocaleSupport(controller.vehicle.messagesLost) : 0
QGCComboBox {
id: baudField
width: _editFieldWidth
model: controller.baudRates
currentIndex: controller.baudIndex
onActivated: {
controller.baudIndex = index
}
}
}
Row {
spacing: ScreenTools.defaultFontPixelWidth
QGCLabel {
text: qsTr("Messages Sent")
width: _firstColumn
text: qsTr("QGC UDP Port")
width: _middleRowWidth
anchors.baseline: qgcportField.baseline
}
QGCLabel {
text: controller.vehicle ? thisThingHasNoNumberLocaleSupport(controller.vehicle.messagesSent) : 0
QGCTextField {
id: qgcportField
width: _editFieldWidth
text: hostPort ? hostPort.valueString : ""
validator: IntValidator {bottom: 1024; top: 65535;}
inputMethodHints: Qt.ImhDigitsOnly
onEditingFinished: {
hostPort.value = text
}
}
}
}
}
}
QGCLabel {
text: qsTr("ESP WiFi Bridge Status")
font.weight: Font.DemiBold
}
Rectangle {
color: palette.windowShade
width: statusLayout.width + _margins * 4
height: statusLayout.height + _margins * 2
GridLayout {
id: statusLayout
columns: 3
columnSpacing: _margins * 2
anchors.centerIn: parent
QGCLabel {
text: qsTr("Bridge/Vehicle Link")
Layout.alignment: Qt.AlignHCenter
}
QGCLabel {
text: qsTr("Bridge/QGC Link")
Layout.alignment: Qt.AlignHCenter
}
QGCLabel {
text: qsTr("QGC/Bridge Link")
Layout.alignment: Qt.AlignHCenter
}
Row {
spacing: _margins
QGCLabel {
text: qsTr("Messages Received")
font.pixelSize: ScreenTools.smallFontPixelSize
width: _labelWidth
}
QGCLabel {
id: vpackets
font.pixelSize: ScreenTools.smallFontPixelSize
width: _statusWidth
horizontalAlignment: Text.AlignRight
font.family: "Monospace"
}
}
Row {
spacing: _margins
QGCLabel {
font.pixelSize: ScreenTools.smallFontPixelSize
text: qsTr("Messages Received")
width: _labelWidth
}
QGCLabel {
id: gpackets
font.pixelSize: ScreenTools.smallFontPixelSize
width: _statusWidth
horizontalAlignment: Text.AlignRight
font.family: "Monospace"
}
}
Row {
spacing: _margins
QGCLabel {
font.pixelSize: ScreenTools.smallFontPixelSize
text: qsTr("Messages Received")
width: _labelWidth
}
QGCLabel {
font.pixelSize: ScreenTools.smallFontPixelSize
text: controller.vehicle ? thisThingHasNoNumberLocaleSupport(controller.vehicle.messagesReceived) : 0
width: _statusWidth
horizontalAlignment: Text.AlignRight
font.family: "Monospace"
}
}
Row {
spacing: _margins
QGCLabel {
text: qsTr("Messages Lost")
font.pixelSize: ScreenTools.smallFontPixelSize
width: _labelWidth
}
QGCLabel {
id: vlost
width: _statusWidth
horizontalAlignment: Text.AlignRight
font.pixelSize: ScreenTools.smallFontPixelSize
font.family: "Monospace"
}
}
Row {
spacing: _margins
QGCLabel {
text: qsTr("Messages Lost")
font.pixelSize: ScreenTools.smallFontPixelSize
width: _labelWidth
}
QGCLabel {
id: glost
width: _statusWidth
horizontalAlignment: Text.AlignRight
font.pixelSize: ScreenTools.smallFontPixelSize
font.family: "Monospace"
}
}
Row {
spacing: _margins
QGCLabel {
text: qsTr("Messages Lost")
font.pixelSize: ScreenTools.smallFontPixelSize
width: _labelWidth
}
QGCLabel {
text: controller.vehicle ? thisThingHasNoNumberLocaleSupport(controller.vehicle.messagesLost) : 0
width: _statusWidth
horizontalAlignment: Text.AlignRight
font.pixelSize: ScreenTools.smallFontPixelSize
font.family: "Monospace"
}
}
Row {
spacing: _margins
QGCLabel {
text: qsTr("Messages Sent")
font.pixelSize: ScreenTools.smallFontPixelSize
width: _labelWidth
}
QGCLabel {
id: vsent
width: _statusWidth
horizontalAlignment: Text.AlignRight
font.pixelSize: ScreenTools.smallFontPixelSize
font.family: "Monospace"
}
}
Row {
spacing: _margins
QGCLabel {
text: qsTr("Messages Sent")
font.pixelSize: ScreenTools.smallFontPixelSize
width: _labelWidth
}
QGCLabel {
id: gsent
width: _statusWidth
horizontalAlignment: Text.AlignRight
font.pixelSize: ScreenTools.smallFontPixelSize
font.family: "Monospace"
}
}
Row {
spacing: _margins
QGCLabel {
text: qsTr("Messages Sent")
font.pixelSize: ScreenTools.smallFontPixelSize
width: _labelWidth
}
QGCLabel {
text: controller.vehicle ? thisThingHasNoNumberLocaleSupport(controller.vehicle.messagesSent) : 0
width: _statusWidth
horizontalAlignment: Text.AlignRight
font.pixelSize: ScreenTools.smallFontPixelSize
font.family: "Monospace"
}
}
}
}
Row {
spacing: ScreenTools.defaultFontPixelWidth * 1.5
spacing: _margins
anchors.horizontalCenter: parent.horizontalCenter
QGCButton {
text: qsTr("Restore Defaults")
width: ScreenTools.defaultFontPixelWidth * 16
text: qsTr("Restore Defaults")
width: ScreenTools.defaultFontPixelWidth * 16
onClicked: {
controller.restoreDefaults()
}
}
QGCButton {
text: qsTr("Restart WiFi Bridge")
enabled: !controller.busy
width: ScreenTools.defaultFontPixelWidth * 16
text: qsTr("Restart WiFi Bridge")
enabled: !controller.busy
width: ScreenTools.defaultFontPixelWidth * 16
onClicked: {
rebootDialog.visible = true
}
......@@ -395,23 +487,8 @@ QGCView {
}
}
QGCButton {
text: stEnabled ? qsTr("Hide Status") : qsTr("Show Status")
width: ScreenTools.defaultFontPixelWidth * 16
onClicked: {
stEnabled = !stEnabled
if(stEnabled)
updateStatus()
else {
wifiStatus.visible = false
timer.stop()
}
}
}
QGCButton {
text: qsTr("Reset Counters")
visible: stEnabled
enabled: stEnabled
width: ScreenTools.defaultFontPixelWidth * 16
text: qsTr("Reset Counters")
width: ScreenTools.defaultFontPixelWidth * 16
onClicked: {
stResetCounters = true;
updateStatus()
......
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2016 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
......@@ -30,6 +30,9 @@
#include "QGCApplication.h"
#include "UAS.h"
#include <QHostAddress>
#include <QtEndian>
QGC_LOGGING_CATEGORY(ESP8266ComponentControllerLog, "ESP8266ComponentControllerLog")
#define MAX_RETRIES 5
......@@ -74,6 +77,21 @@ ESP8266ComponentController::version()
return versionString;
}
//-----------------------------------------------------------------------------
QString
ESP8266ComponentController::wifiIPAddress()
{
if(_ipAddress.isEmpty()) {
if(parameterExists(MAV_COMP_ID_UDP_BRIDGE, "WIFI_IPADDRESS")) {
QHostAddress address(qFromBigEndian(getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_IPADDRESS")->rawValue().toUInt()));
_ipAddress = address.toString();
} else {
_ipAddress = "192.168.4.1";
}
}
return _ipAddress;
}
//-----------------------------------------------------------------------------
QString
ESP8266ComponentController::wifiSSID()
......@@ -152,6 +170,94 @@ ESP8266ComponentController::setWifiPassword(QString password)
f4->setRawValue(QVariant(u));
}
//-----------------------------------------------------------------------------
QString
ESP8266ComponentController::wifiSSIDSta()
{
if(!parameterExists(MAV_COMP_ID_UDP_BRIDGE, "WIFI_SSIDSTA1")) {
return QString();
}
uint32_t s1 = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_SSIDSTA1")->rawValue().toUInt();
uint32_t s2 = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_SSIDSTA2")->rawValue().toUInt();
uint32_t s3 = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_SSIDSTA3")->rawValue().toUInt();
uint32_t s4 = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_SSIDSTA4")->rawValue().toUInt();
char tmp[20];
memcpy(&tmp[0], &s1, sizeof(uint32_t));
memcpy(&tmp[4], &s2, sizeof(uint32_t));
memcpy(&tmp[8], &s3, sizeof(uint32_t));
memcpy(&tmp[12], &s4, sizeof(uint32_t));
return QString(tmp);
}
//-----------------------------------------------------------------------------
void
ESP8266ComponentController::setWifiSSIDSta(QString ssid)
{
if(parameterExists(MAV_COMP_ID_UDP_BRIDGE, "WIFI_SSIDSTA1")) {
char tmp[20];
memset(tmp, 0, sizeof(tmp));
std::string sid = ssid.toStdString();
strncpy(tmp, sid.c_str(), sizeof(tmp));
Fact* f1 = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_SSIDSTA1");
Fact* f2 = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_SSIDSTA2");
Fact* f3 = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_SSIDSTA3");
Fact* f4 = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_SSIDSTA4");
uint32_t u;
memcpy(&u, &tmp[0], sizeof(uint32_t));
f1->setRawValue(QVariant(u));
memcpy(&u, &tmp[4], sizeof(uint32_t));
f2->setRawValue(QVariant(u));
memcpy(&u, &tmp[8], sizeof(uint32_t));
f3->setRawValue(QVariant(u));
memcpy(&u, &tmp[12], sizeof(uint32_t));
f4->setRawValue(QVariant(u));
}
}
//-----------------------------------------------------------------------------
QString
ESP8266ComponentController::wifiPasswordSta()
{
if(!parameterExists(MAV_COMP_ID_UDP_BRIDGE, "WIFI_PWDSTA1")) {
return QString();
}
uint32_t s1 = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_PWDSTA1")->rawValue().toUInt();
uint32_t s2 = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_PWDSTA2")->rawValue().toUInt();
uint32_t s3 = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_PWDSTA3")->rawValue().toUInt();
uint32_t s4 = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_PWDSTA4")->rawValue().toUInt();
char tmp[20];
memcpy(&tmp[0], &s1, sizeof(uint32_t));
memcpy(&tmp[4], &s2, sizeof(uint32_t));
memcpy(&tmp[8], &s3, sizeof(uint32_t));
memcpy(&tmp[12], &s4, sizeof(uint32_t));
return QString(tmp);
}
//-----------------------------------------------------------------------------
void
ESP8266ComponentController::setWifiPasswordSta(QString password)
{
if(parameterExists(MAV_COMP_ID_UDP_BRIDGE, "WIFI_PWDSTA1")) {
char tmp[20];
memset(tmp, 0, sizeof(tmp));
std::string pwd = password.toStdString();
strncpy(tmp, pwd.c_str(), sizeof(tmp));
Fact* f1 = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_PWDSTA1");
Fact* f2 = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_PWDSTA2");
Fact* f3 = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_PWDSTA3");
Fact* f4 = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_PWDSTA4");
uint32_t u;
memcpy(&u, &tmp[0], sizeof(uint32_t));
f1->setRawValue(QVariant(u));
memcpy(&u, &tmp[4], sizeof(uint32_t));
f2->setRawValue(QVariant(u));
memcpy(&u, &tmp[8], sizeof(uint32_t));
f3->setRawValue(QVariant(u));
memcpy(&u, &tmp[12], sizeof(uint32_t));
f4->setRawValue(QVariant(u));
}
}
//-----------------------------------------------------------------------------
int
ESP8266ComponentController::baudIndex()
......
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2016 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/>.
======================================================================*/
......@@ -45,28 +45,34 @@ namespace Ui {
class ESP8266ComponentController : public FactPanelController
{
Q_OBJECT
public:
ESP8266ComponentController ();
~ESP8266ComponentController ();
Q_PROPERTY(int componentID READ componentID CONSTANT)
Q_PROPERTY(QString version READ version NOTIFY versionChanged)
Q_PROPERTY(QString wifiSSID READ wifiSSID WRITE setWifiSSID NOTIFY wifiSSIDChanged)
Q_PROPERTY(QString wifiPassword READ wifiPassword WRITE setWifiPassword NOTIFY wifiPasswordChanged)
Q_PROPERTY(QStringList wifiChannels READ wifiChannels CONSTANT)
Q_PROPERTY(QStringList baudRates READ baudRates CONSTANT)
Q_PROPERTY(int baudIndex READ baudIndex WRITE setBaudIndex NOTIFY baudIndexChanged)
Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
Q_PROPERTY(Vehicle* vehicle READ vehicle CONSTANT)
Q_PROPERTY(int componentID READ componentID CONSTANT)
Q_PROPERTY(QString version READ version NOTIFY versionChanged)
Q_PROPERTY(QString wifiIPAddress READ wifiIPAddress CONSTANT)
Q_PROPERTY(QString wifiSSID READ wifiSSID WRITE setWifiSSID NOTIFY wifiSSIDChanged)
Q_PROPERTY(QString wifiPassword READ wifiPassword WRITE setWifiPassword NOTIFY wifiPasswordChanged)
Q_PROPERTY(QString wifiSSIDSta READ wifiSSIDSta WRITE setWifiSSIDSta NOTIFY wifiSSIDStaChanged)
Q_PROPERTY(QString wifiPasswordSta READ wifiPasswordSta WRITE setWifiPasswordSta NOTIFY wifiPasswordStaChanged)
Q_PROPERTY(QStringList wifiChannels READ wifiChannels CONSTANT)
Q_PROPERTY(QStringList baudRates READ baudRates CONSTANT)
Q_PROPERTY(int baudIndex READ baudIndex WRITE setBaudIndex NOTIFY baudIndexChanged)
Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
Q_PROPERTY(Vehicle* vehicle READ vehicle CONSTANT)
Q_INVOKABLE void restoreDefaults();
Q_INVOKABLE void reboot ();
int componentID () { return MAV_COMP_ID_UDP_BRIDGE; }
QString version ();
QString wifiIPAddress ();
QString wifiSSID ();
QString wifiPassword ();
QString wifiSSIDSta ();
QString wifiPasswordSta ();
QStringList wifiChannels () { return _channels; }
QStringList baudRates () { return _baudRates; }
int baudIndex ();
......@@ -75,14 +81,18 @@ public:
void setWifiSSID (QString id);
void setWifiPassword (QString pwd);
void setWifiSSIDSta (QString id);
void setWifiPasswordSta (QString pwd);
void setBaudIndex (int idx);
signals:
void versionChanged ();
void wifiSSIDChanged ();
void wifiPasswordChanged ();
void baudIndexChanged ();
void busyChanged ();
void versionChanged ();
void wifiSSIDChanged ();
void wifiPasswordChanged ();
void wifiSSIDStaChanged ();
void wifiPasswordStaChanged ();
void baudIndexChanged ();
void busyChanged ();
private slots:
void _processTimeout ();
......@@ -100,6 +110,7 @@ private:
QTimer _timer;
QStringList _channels;
QStringList _baudRates;
QString _ipAddress;
enum {
WAIT_FOR_NOTHING,
......
......@@ -25,6 +25,7 @@ FactPanel {
property Fact wifiHostPort: controller.getParameterFact(esp8266.componentID, "WIFI_UDP_HPORT")
property Fact wifiClientPort: controller.getParameterFact(esp8266.componentID, "WIFI_UDP_CPORT")
property Fact uartBaud: controller.getParameterFact(esp8266.componentID, "UART_BAUDRATE")
property Fact wifiMode: controller.getParameterFact(esp8266.componentID, "WIFI_MODE", false) //-- Don't bitch if missing
Column {
anchors.fill: parent
......@@ -33,18 +34,30 @@ FactPanel {
labelText: qsTr("Firmware Version:")
valueText: esp8266.version
}
VehicleSummaryRow {
labelText: qsTr("WiFi Mode:")
valueText: wifiMode ? (wifiMode.value === 0 ? "AP Mode" : "Station Mode") : "AP Mode"
}
VehicleSummaryRow {
labelText: qsTr("WiFi Channel:")
valueText: wifiChannel ? wifiChannel.valueString : ""
}
VehicleSummaryRow {
labelText: qsTr("WiFi SSID:")
labelText: qsTr("WiFi AP SSID:")
valueText: esp8266.wifiSSID
}
VehicleSummaryRow {
labelText: qsTr("WiFi Password:")
labelText: qsTr("WiFi AP Password:")
valueText: esp8266.wifiPassword
}
VehicleSummaryRow {
labelText: qsTr("WiFi STA SSID:")
valueText: esp8266.wifiSSIDSta
}
VehicleSummaryRow {
labelText: qsTr("WiFi STA Password:")
valueText: esp8266.wifiPasswordSta
}
VehicleSummaryRow {
labelText: qsTr("UART Baud Rate:")
valueText: uartBaud ? uartBaud.valueString : ""
......
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 59.671 87.808" style="enable-background:new 0 0 59.671 87.808;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;stroke:#FFFFFF;}
.st1{fill:none;stroke:#FFFFFF;stroke-width:3;stroke-linecap:round;}
.st2{fill:none;stroke:#FFFFFF;stroke-width:3;}
.st3{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:3;}
.st4{fill:#FFFFFF;}
</style>
<sodipodi:namedview bordercolor="#666666" borderopacity="1.0" id="base" inkscape:current-layer="layer1" inkscape:cx="282.69484" inkscape:cy="221.0125" inkscape:document-units="px" inkscape:guide-bbox="true" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-height="1017" inkscape:window-maximized="1" inkscape:window-width="1920" inkscape:window-x="-8" inkscape:window-y="-8" inkscape:zoom="2.8" pagecolor="#ffffff" showgrid="true" showguides="true">
<sodipodi:guide id="guide2985" orientation="1,0" position="250,420"></sodipodi:guide>
<inkscape:grid empspacing="10" enabled="true" id="grid2987" snapvisiblegridlinesonly="true" type="xygrid" visible="true">
</inkscape:grid>
<sodipodi:guide id="guide2989" orientation="0,1" position="270,250"></sodipodi:guide>
</sodipodi:namedview>
<g>
<defs>
<inkscape:path-effect copytype="repeated" effect="skeletal" fuse_tolerance="0" id="path-effect4881" is_visible="true" normal_offset="0" pattern="M 0,0 1,0" prop_scale="1" prop_units="false" scale_y_rel="false" spacing="0" tang_offset="0" vertical_pattern="false">
</inkscape:path-effect>
<inkscape:path-effect bendpath="m 110,180 140,0" effect="bend_path" id="path-effect3765" is_visible="true" prop_scale="1" scale_y_rel="false" vertical="false">
</inkscape:path-effect>
</defs>
<g>
<g>
<path id="path2991_3_" sodipodi:cx="110" sodipodi:cy="110" sodipodi:rx="20" sodipodi:ry="20" sodipodi:type="arc" class="st0" d="
M18.734,7.918c-0.169,0.83-1.147,1.355-2.178,1.136c-1.031-0.218-1.679-1.087-1.455-1.906c0.225-0.819,1.193-1.288,2.168-1.081
C18.245,6.273,18.903,7.087,18.734,7.918z"/>
<path id="path3761_3_" sodipodi:cx="110" sodipodi:cy="110" sodipodi:end="6.4866523" sodipodi:open="true" sodipodi:rx="50" sodipodi:ry="50" sodipodi:start="1.374765" sodipodi:type="arc" class="st1" d="
M17.012,12.188c-3.308-0.136-5.924-2.602-5.69-5.147c0.234-2.545,2.84-4.234,5.668-4.091s5.181,2.08,5.427,4.618
c0.059,0.614-0.023,1.229-0.256,1.807"/>
<path id="path2991-6_3_" inkscape:transform-center-x="-140.5" inkscape:transform-center-y="-140.5" sodipodi:cx="110" sodipodi:cy="110" sodipodi:rx="20" sodipodi:ry="20" sodipodi:type="arc" class="st0" d="
M43.115,9.054c-1.031,0.218-2.009-0.306-2.178-1.136c-0.169-0.83,0.489-1.644,1.464-1.851c0.976-0.207,1.944,0.263,2.168,1.081
C44.794,7.967,44.146,8.835,43.115,9.054z"/>
<path id="path3761-1_3_" inkscape:transform-center-x="-140.5" inkscape:transform-center-y="-140.50478" sodipodi:cx="110" sodipodi:cy="110" sodipodi:end="6.4866523" sodipodi:open="true" sodipodi:rx="50" sodipodi:ry="50" sodipodi:start="1.374765" sodipodi:type="arc" class="st1" d="
M37.498,9.343c-0.942-2.394,0.618-4.969,3.233-5.979c2.615-1.011,5.647-0.216,7.03,1.977c1.384,2.192,0.25,5.149-2.807,6.362
c-0.74,0.293-1.54,0.46-2.34,0.488"/>
<path id="path2991-9_3_" sodipodi:cx="110" sodipodi:cy="110" sodipodi:rx="20" sodipodi:ry="20" sodipodi:type="arc" class="st0" d="
M45.245,29.041c-0.169-0.83,0.806-1.701,2.171-1.99s2.647,0.103,2.872,0.921s-0.737,1.765-2.158,2.066
C46.71,30.34,45.415,29.872,45.245,29.041z"/>
<path id="path3761-2_3_" sodipodi:cx="110" sodipodi:cy="110" sodipodi:end="6.4866523" sodipodi:open="true" sodipodi:rx="50" sodipodi:ry="50" sodipodi:start="1.374765" sodipodi:type="arc" class="st1" d="
M45.334,24.432c3.764-1.233,7.892-0.762,9.499,1.362c1.607,2.124-0.252,5.327-4.451,6.802c-4.199,1.475-8.784,0.445-9.957-1.921
c-0.284-0.572-0.359-1.188-0.237-1.809"/>
<path id="path2991-6-9_3_" inkscape:transform-center-x="140.5" inkscape:transform-center-y="140.50002" sodipodi:cx="110" sodipodi:cy="110" sodipodi:rx="20" sodipodi:ry="20" sodipodi:type="arc" class="st0" d="
M12.254,27.051c1.365,0.289,2.341,1.159,2.171,1.99c-0.169,0.83-1.465,1.298-2.885,0.997c-1.42-0.301-2.382-1.247-2.158-2.066
C9.608,27.154,10.889,26.762,12.254,27.051z"/>
<path id="path3761-1-5_3_" inkscape:transform-center-x="140.5" inkscape:transform-center-y="140.50476" sodipodi:cx="110" sodipodi:cy="110" sodipodi:end="6.4866523" sodipodi:open="true" sodipodi:rx="50" sodipodi:ry="50" sodipodi:start="1.374765" sodipodi:type="arc" class="st1" d="
M19.49,28.9c0.476,2.566-2.624,4.715-7.081,4.356c-4.457-0.359-8.169-3.149-8.159-5.762c0.01-2.613,3.343-4.056,7.313-3.651
c0.96,0.098,1.916,0.303,2.825,0.604"/>
</g>
<path class="st2" d="M11.897,28.545C23.883,24.596,34.911,17.522,42.758,7.56"/>
<path class="st2" d="M47.777,28.545C35.791,24.596,24.763,17.522,16.915,7.56"/>
<path class="st3" d="M38.681,19.141c0.453,3.364-3.496,6.665-8.845,6.665s-9.299-3.302-8.845-6.665
c0.453-3.364,4.404-5.613,8.845-5.613C34.276,13.527,38.228,15.777,38.681,19.141z"/>
</g>
</g>
<path class="st4" d="M27.003,55.688c0.762-0.793,1.69-1.182,2.785-1.166c1.095,0.016,2.039,0.405,2.833,1.166
c0.793,0.762,1.19,1.69,1.19,2.785s-0.397,2.039-1.19,2.833c-0.762,0.793-1.69,1.19-2.785,1.19s-2.039-0.397-2.833-1.19
c-0.793-0.762-1.19-1.698-1.19-2.809S26.209,56.45,27.003,55.688z M13.006,71.352c1.047,0,1.968,0.397,2.761,1.19
c3.872,3.84,8.554,5.761,14.045,5.761s10.172-1.92,14.045-5.761c0.793-0.793,1.73-1.182,2.809-1.166s2.015,0.405,2.809,1.166
c0.793,0.793,1.19,1.722,1.19,2.785s-0.397,1.992-1.19,2.785c-3.555,3.555-7.713,5.959-12.473,7.213s-9.53,1.254-14.306,0
s-8.942-3.658-12.497-7.213c-0.793-0.793-1.19-1.722-1.19-2.785s0.397-1.992,1.19-2.785C10.99,71.749,11.927,71.352,13.006,71.352z
M38.238,62.925c1.079,0,2.015,0.397,2.809,1.19c0.793,0.793,1.19,1.73,1.19,2.809s-0.397,2.015-1.19,2.809
c-2.031,2.031-4.412,3.404-7.141,4.118s-5.459,0.714-8.189,0s-5.11-2.087-7.141-4.118c-0.793-0.793-1.19-1.73-1.19-2.809
s0.397-2.015,1.19-2.809c0.793-0.793,1.73-1.19,2.809-1.19s2.015,0.397,2.809,1.19c1.555,1.555,3.428,2.333,5.618,2.333
s4.063-0.778,5.618-2.333C36.223,63.322,37.159,62.925,38.238,62.925z"/>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 59.671 87.808" style="enable-background:new 0 0 59.671 87.808;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;stroke:#FFFFFF;}
.st1{fill:none;stroke:#FFFFFF;stroke-width:3;stroke-linecap:round;}
.st2{fill:none;stroke:#FFFFFF;stroke-width:3;}
.st3{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:3;}
.st4{fill:#FFFFFF;}
</style>
<sodipodi:namedview bordercolor="#666666" borderopacity="1.0" id="base" inkscape:current-layer="layer1" inkscape:cx="282.69484" inkscape:cy="221.0125" inkscape:document-units="px" inkscape:guide-bbox="true" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-height="1017" inkscape:window-maximized="1" inkscape:window-width="1920" inkscape:window-x="-8" inkscape:window-y="-8" inkscape:zoom="2.8" pagecolor="#ffffff" showgrid="true" showguides="true">
<sodipodi:guide id="guide2985" orientation="1,0" position="250,420"></sodipodi:guide>
<inkscape:grid empspacing="10" enabled="true" id="grid2987" snapvisiblegridlinesonly="true" type="xygrid" visible="true">
</inkscape:grid>
<sodipodi:guide id="guide2989" orientation="0,1" position="270,250"></sodipodi:guide>
</sodipodi:namedview>
<g>
<defs>
<inkscape:path-effect copytype="repeated" effect="skeletal" fuse_tolerance="0" id="path-effect4881" is_visible="true" normal_offset="0" pattern="M 0,0 1,0" prop_scale="1" prop_units="false" scale_y_rel="false" spacing="0" tang_offset="0" vertical_pattern="false">
</inkscape:path-effect>
<inkscape:path-effect bendpath="m 110,180 140,0" effect="bend_path" id="path-effect3765" is_visible="true" prop_scale="1" scale_y_rel="false" vertical="false">
</inkscape:path-effect>
</defs>
<g>
<g>
<path id="path2991_3_" sodipodi:cx="110" sodipodi:cy="110" sodipodi:rx="20" sodipodi:ry="20" sodipodi:type="arc" class="st0" d="
M18.734,7.918c-0.169,0.83-1.147,1.355-2.178,1.136c-1.031-0.218-1.679-1.087-1.455-1.906c0.225-0.819,1.193-1.288,2.168-1.081
C18.245,6.273,18.903,7.087,18.734,7.918z"/>
<path id="path3761_3_" sodipodi:cx="110" sodipodi:cy="110" sodipodi:end="6.4866523" sodipodi:open="true" sodipodi:rx="50" sodipodi:ry="50" sodipodi:start="1.374765" sodipodi:type="arc" class="st1" d="
M17.012,12.188c-3.308-0.136-5.924-2.602-5.69-5.147c0.234-2.545,2.84-4.234,5.668-4.091s5.181,2.08,5.427,4.618
c0.059,0.614-0.023,1.229-0.256,1.807"/>
<path id="path2991-6_3_" inkscape:transform-center-x="-140.5" inkscape:transform-center-y="-140.5" sodipodi:cx="110" sodipodi:cy="110" sodipodi:rx="20" sodipodi:ry="20" sodipodi:type="arc" class="st0" d="
M43.115,9.054c-1.031,0.218-2.009-0.306-2.178-1.136c-0.169-0.83,0.489-1.644,1.464-1.851c0.976-0.207,1.944,0.263,2.168,1.081
C44.794,7.967,44.146,8.835,43.115,9.054z"/>
<path id="path3761-1_3_" inkscape:transform-center-x="-140.5" inkscape:transform-center-y="-140.50478" sodipodi:cx="110" sodipodi:cy="110" sodipodi:end="6.4866523" sodipodi:open="true" sodipodi:rx="50" sodipodi:ry="50" sodipodi:start="1.374765" sodipodi:type="arc" class="st1" d="
M37.498,9.343c-0.942-2.394,0.618-4.969,3.233-5.979c2.615-1.011,5.647-0.216,7.03,1.977c1.384,2.192,0.25,5.149-2.807,6.362
c-0.74,0.293-1.54,0.46-2.34,0.488"/>
<path id="path2991-9_3_" sodipodi:cx="110" sodipodi:cy="110" sodipodi:rx="20" sodipodi:ry="20" sodipodi:type="arc" class="st0" d="
M45.245,29.041c-0.169-0.83,0.806-1.701,2.171-1.99s2.647,0.103,2.872,0.921s-0.737,1.765-2.158,2.066
C46.71,30.34,45.415,29.872,45.245,29.041z"/>
<path id="path3761-2_3_" sodipodi:cx="110" sodipodi:cy="110" sodipodi:end="6.4866523" sodipodi:open="true" sodipodi:rx="50" sodipodi:ry="50" sodipodi:start="1.374765" sodipodi:type="arc" class="st1" d="
M45.334,24.432c3.764-1.233,7.892-0.762,9.499,1.362c1.607,2.124-0.252,5.327-4.451,6.802c-4.199,1.475-8.784,0.445-9.957-1.921
c-0.284-0.572-0.359-1.188-0.237-1.809"/>
<path id="path2991-6-9_3_" inkscape:transform-center-x="140.5" inkscape:transform-center-y="140.50002" sodipodi:cx="110" sodipodi:cy="110" sodipodi:rx="20" sodipodi:ry="20" sodipodi:type="arc" class="st0" d="
M12.254,27.051c1.365,0.289,2.341,1.159,2.171,1.99c-0.169,0.83-1.465,1.298-2.885,0.997c-1.42-0.301-2.382-1.247-2.158-2.066
C9.608,27.154,10.889,26.762,12.254,27.051z"/>
<path id="path3761-1-5_3_" inkscape:transform-center-x="140.5" inkscape:transform-center-y="140.50476" sodipodi:cx="110" sodipodi:cy="110" sodipodi:end="6.4866523" sodipodi:open="true" sodipodi:rx="50" sodipodi:ry="50" sodipodi:start="1.374765" sodipodi:type="arc" class="st1" d="
M19.49,28.9c0.476,2.566-2.624,4.715-7.081,4.356c-4.457-0.359-8.169-3.149-8.159-5.762c0.01-2.613,3.343-4.056,7.313-3.651
c0.96,0.098,1.916,0.303,2.825,0.604"/>
</g>
<path class="st2" d="M11.897,28.545C23.883,24.596,34.911,17.522,42.758,7.56"/>
<path class="st2" d="M47.777,28.545C35.791,24.596,24.763,17.522,16.915,7.56"/>
<path class="st3" d="M38.681,19.141c0.453,3.364-3.496,6.665-8.845,6.665s-9.299-3.302-8.845-6.665
c0.453-3.364,4.404-5.613,8.845-5.613C34.276,13.527,38.228,15.777,38.681,19.141z"/>
</g>
</g>
<path class="st4" d="M32.668,85.099c-0.762,0.793-1.69,1.182-2.785,1.166s-2.039-0.405-2.833-1.166
c-0.793-0.762-1.19-1.69-1.19-2.785s0.397-2.039,1.19-2.833c0.762-0.793,1.69-1.19,2.785-1.19s2.039,0.397,2.833,1.19
c0.793,0.762,1.19,1.698,1.19,2.809S33.462,84.337,32.668,85.099z M46.665,69.435c-1.047,0-1.968-0.397-2.761-1.19
c-3.872-3.84-8.554-5.761-14.045-5.761s-10.172,1.92-14.045,5.761c-0.793,0.793-1.73,1.182-2.809,1.166s-2.015-0.405-2.809-1.166
c-0.793-0.793-1.19-1.722-1.19-2.785s0.397-1.992,1.19-2.785c3.555-3.555,7.713-5.959,12.473-7.213s9.53-1.254,14.306,0
s8.942,3.658,12.497,7.213c0.793,0.793,1.19,1.722,1.19,2.785s-0.397,1.992-1.19,2.785C48.68,69.038,47.744,69.435,46.665,69.435z
M21.432,77.862c-1.079,0-2.015-0.397-2.809-1.19c-0.793-0.793-1.19-1.73-1.19-2.809s0.397-2.015,1.19-2.809
c2.031-2.031,4.412-3.404,7.141-4.118s5.459-0.714,8.189,0s5.11,2.087,7.141,4.118c0.793,0.793,1.19,1.73,1.19,2.809
s-0.397,2.015-1.19,2.809c-0.793,0.793-1.73,1.19-2.809,1.19s-2.015-0.397-2.809-1.19c-1.555-1.555-3.428-2.333-5.618-2.333
s-4.063,0.778-5.618,2.333C23.448,77.465,22.512,77.862,21.432,77.862z"/>
</svg>
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