SerialLink.cc 17.1 KB
Newer Older
pixhawk's avatar
pixhawk committed
1
2
3
4
5
6
7
8
9
10
11
12
/*=====================================================================
======================================================================*/
/**
 * @file
 *   @brief Cross-platform support for serial ports
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

#include <QTimer>
#include <QDebug>
pixhawk's avatar
pixhawk committed
13
#include <QSettings>
pixhawk's avatar
pixhawk committed
14
#include <QMutexLocker>
dogmaphobic's avatar
dogmaphobic committed
15
16
17
18

#ifdef __android__
#include "qserialport.h"
#else
19
#include <QSerialPort>
dogmaphobic's avatar
dogmaphobic committed
20
#endif
21

pixhawk's avatar
pixhawk committed
22
#include "SerialLink.h"
pixhawk's avatar
pixhawk committed
23
#include "QGC.h"
24
#include "MG.h"
25
#include "QGCLoggingCategory.h"
Don Gagne's avatar
Don Gagne committed
26
#include "QGCApplication.h"
27
#include "QGCSerialPortInfo.h"
28

29
QGC_LOGGING_CATEGORY(SerialLinkLog, "SerialLinkLog")
pixhawk's avatar
pixhawk committed
30

31
32
static QStringList kSupportedBaudRates;

DonLakeFlyer's avatar
DonLakeFlyer committed
33
34
SerialLink::SerialLink(SharedLinkConfigurationPointer& config, bool isPX4Flow)
    : LinkInterface(config, isPX4Flow)
murata's avatar
murata committed
35
    , _port(nullptr)
36
37
38
39
    , _bytesRead(0)
    , _stopp(false)
    , _reqReset(false)
    , _serialConfig(qobject_cast<SerialConfiguration*>(config.data()))
40
{
DonLakeFlyer's avatar
DonLakeFlyer committed
41
42
43
44
    if (!_serialConfig) {
        qWarning() << "Internal error";
        return;
    }
45
46

    qCDebug(SerialLinkLog) << "Create SerialLink " << _serialConfig->portName() << _serialConfig->baud() << _serialConfig->flowControl()
DonLakeFlyer's avatar
DonLakeFlyer committed
47
                           << _serialConfig->parity() << _serialConfig->dataBits() << _serialConfig->stopBits();
48
    qCDebug(SerialLinkLog) << "portName: " << _serialConfig->portName();
pixhawk's avatar
pixhawk committed
49
}
50

51
52
void SerialLink::requestReset()
{
53
54
    QMutexLocker locker(&this->_stoppMutex);
    _reqReset = true;
55
}
pixhawk's avatar
pixhawk committed
56
57
58

SerialLink::~SerialLink()
{
59
    _disconnect();
60
61
}

62
bool SerialLink::_isBootloader()
63
{
64
    QList<QSerialPortInfo> portList = QSerialPortInfo::availablePorts();
65
66
67
    if( portList.count() == 0){
        return false;
    }
68
    for (const QSerialPortInfo &info: portList)
69
    {
70
71
        qCDebug(SerialLinkLog) << "PortName    : " << info.portName() << "Description : " << info.description();
        qCDebug(SerialLinkLog) << "Manufacturer: " << info.manufacturer();
72
        if (info.portName().trimmed() == _serialConfig->portName() &&
73
74
75
76
77
                (info.description().toLower().contains("bootloader") ||
                 info.description().toLower().contains("px4 bl") ||
                 info.description().toLower().contains("px4 fmu v1.6"))) {
            qCDebug(SerialLinkLog) << "BOOTLOADER FOUND";
            return true;
DonLakeFlyer's avatar
DonLakeFlyer committed
78
        }
79
80
81
82
83
    }
    // Not found
    return false;
}

84
void SerialLink::_writeBytes(const QByteArray data)
85
{
86
    if(_port && _port->isOpen()) {
87
        emit bytesSent(this, data);
88
89
        _logOutputDataRate(data.size(), QDateTime::currentMSecsSinceEpoch());
        _port->write(data);
90
    } else {
Ricardo de Almeida Gonzaga's avatar
Ricardo de Almeida Gonzaga committed
91
        // Error occurred
DonLakeFlyer's avatar
DonLakeFlyer committed
92
        qWarning() << "Serial port not writeable";
93
        _emitLinkError(tr("Could not send data - link %1 is disconnected!").arg(getName()));
pixhawk's avatar
pixhawk committed
94
95
96
97
98
99
100
101
    }
}

/**
 * @brief Disconnect the connection.
 *
 * @return True if connection has been disconnected, false if connection couldn't be disconnected.
 **/
Don Gagne's avatar
Don Gagne committed
102
void SerialLink::_disconnect(void)
103
{
Don Gagne's avatar
Don Gagne committed
104
105
    if (_port) {
        _port->close();
DonLakeFlyer's avatar
DonLakeFlyer committed
106
        _port->deleteLater();
murata's avatar
murata committed
107
        _port = nullptr;
108
    }
109

dogmaphobic's avatar
dogmaphobic committed
110
#ifdef __android__
111
    qgcApp()->toolbox()->linkManager()->suspendConfigurationUpdates(false);
dogmaphobic's avatar
dogmaphobic committed
112
#endif
pixhawk's avatar
pixhawk committed
113
114
115
116
117
118
119
}

/**
 * @brief Connect the connection.
 *
 * @return True if connection has been established, false if connection couldn't be established.
 **/
120
bool SerialLink::_connect(void)
121
{
122
    qCDebug(SerialLinkLog) << "CONNECT CALLED";
Don Gagne's avatar
Don Gagne committed
123
124

    _disconnect();
125

dogmaphobic's avatar
dogmaphobic committed
126
#ifdef __android__
127
    qgcApp()->toolbox()->linkManager()->suspendConfigurationUpdates(true);
Don Gagne's avatar
Don Gagne committed
128
#endif
129

130
131
132
    QSerialPort::SerialPortError    error;
    QString                         errorString;

dogmaphobic's avatar
dogmaphobic committed
133
    // Initialize the connection
134
135
136
137
138
139
140
    if (!_hardwareConnect(error, errorString)) {
        if (qgcApp()->toolbox()->linkManager()->isAutoconnectLink(this)) {
            // Be careful with spitting out open error related to trying to open a busy port using autoconnect
            if (error == QSerialPort::PermissionError) {
                // Device already open, ignore and fail connect
                return false;
            }
dogmaphobic's avatar
dogmaphobic committed
141
        }
142

DonLakeFlyer's avatar
DonLakeFlyer committed
143
        _emitLinkError(tr("Error connecting: Could not create port. %1").arg(errorString));
dogmaphobic's avatar
dogmaphobic committed
144
145
        return false;
    }
146
    return true;
pixhawk's avatar
pixhawk committed
147
148
}

149
150
151
152
153
/// Performs the actual hardware port connection.
///     @param[out] error if failed
///     @param[out] error string if failed
/// @return success/fail
bool SerialLink::_hardwareConnect(QSerialPort::SerialPortError& error, QString& errorString)
154
{
155
    if (_port) {
DonLakeFlyer's avatar
   
DonLakeFlyer committed
156
        qCDebug(SerialLinkLog) << "SerialLink:" << QString::number((qulonglong)this, 16) << "closing port";
157
        _port->close();
158
159
160
161
162
163

        // Wait 50 ms while continuing to run the event queue
        for (unsigned i = 0; i < 10; i++) {
            QGC::SLEEP::usleep(5000);
            qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents);
        }
164
        delete _port;
murata's avatar
murata committed
165
        _port = nullptr;
166
    }
pixhawk's avatar
pixhawk committed
167

168
    qCDebug(SerialLinkLog) << "SerialLink: hardwareConnect to " << _serialConfig->portName();
169

170
    // If we are in the Pixhawk bootloader code wait for it to timeout
171
    if (_isBootloader()) {
172
        qCDebug(SerialLinkLog) << "Not connecting to a bootloader, waiting for 2nd chance";
173
174
        const unsigned retry_limit = 12;
        unsigned retries;
175

176
        for (retries = 0; retries < retry_limit; retries++) {
177
            if (!_isBootloader()) {
178
179
180
181
182
                // Wait 500 ms while continuing to run the event loop
                for (unsigned i = 0; i < 100; i++) {
                    QGC::SLEEP::msleep(5);
                    qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents);
                }
183
184
                break;
            }
185
186
187
188
189
190

            // Wait 500 ms while continuing to run the event loop
            for (unsigned i = 0; i < 100; i++) {
                QGC::SLEEP::msleep(5);
                qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents);
            }
191
192
193
194
        }
        // Check limit
        if (retries == retry_limit) {
            // bail out
195
            qWarning() << "Timeout waiting for something other than booloader";
196
197
198
199
            return false;
        }
    }

DonLakeFlyer's avatar
DonLakeFlyer committed
200
    _port = new QSerialPort(_serialConfig->portName(), this);
Bill Bonney's avatar
Bill Bonney committed
201

202
203
    QObject::connect(_port, static_cast<void (QSerialPort::*)(QSerialPort::SerialPortError)>(&QSerialPort::error),
                     this, &SerialLink::linkError);
Don Gagne's avatar
Don Gagne committed
204
    QObject::connect(_port, &QIODevice::readyRead, this, &SerialLink::_readBytes);
Bill Bonney's avatar
Bill Bonney committed
205

206
    //  port->setCommTimeouts(QSerialPort::CtScheme_NonBlockingRead);
pixhawk's avatar
pixhawk committed
207

208
    // TODO This needs a bit of TLC still...
209

210
    // After the bootloader times out, it still can take a second or so for the Pixhawk USB driver to come up and make
211
    // the port available for open. So we retry a few times to wait for it.
dogmaphobic's avatar
dogmaphobic committed
212
213
214
#ifdef __android__
    _port->open(QIODevice::ReadWrite);
#else
215
216
217

    // Try to open the port three times
    for (int openRetries = 0; openRetries < 3; openRetries++) {
218
        if (!_port->open(QIODevice::ReadWrite)) {
219
            qCDebug(SerialLinkLog) << "Port open failed, retrying";
220
221
222
223
224
225
            // Wait 250 ms while continuing to run the event loop
            for (unsigned i = 0; i < 50; i++) {
                QGC::SLEEP::msleep(5);
                qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents);
            }
            qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents);
226
227
228
        } else {
            break;
        }
229
    }
dogmaphobic's avatar
dogmaphobic committed
230
#endif
231
    if (!_port->isOpen() ) {
232
233
234
        qDebug() << "open failed" << _port->errorString() << _port->error() << getName() << qgcApp()->toolbox()->linkManager()->isAutoconnectLink(this);
        error = _port->error();
        errorString = _port->errorString();
DonLakeFlyer's avatar
DonLakeFlyer committed
235
        emit communicationUpdate(getName(), tr("Error opening port: %1").arg(_port->errorString()));
236
        _port->close();
Don Gagne's avatar
Don Gagne committed
237
        delete _port;
murata's avatar
murata committed
238
        _port = nullptr;
239
        return false; // couldn't open serial port
240
241
    }

242
243
    _port->setDataTerminalReady(true);

244
    qCDebug(SerialLinkLog) << "Configuring port";
245
246
247
248
249
    _port->setBaudRate     (_serialConfig->baud());
    _port->setDataBits     (static_cast<QSerialPort::DataBits>     (_serialConfig->dataBits()));
    _port->setFlowControl  (static_cast<QSerialPort::FlowControl>  (_serialConfig->flowControl()));
    _port->setStopBits     (static_cast<QSerialPort::StopBits>     (_serialConfig->stopBits()));
    _port->setParity       (static_cast<QSerialPort::Parity>       (_serialConfig->parity()));
250

251
    emit communicationUpdate(getName(), "Opened port!");
Bill Bonney's avatar
Bill Bonney committed
252
    emit connected();
253

254
    qCDebug(SerialLinkLog) << "Connection SeriaLink: " << "with settings" << _serialConfig->portName()
DonLakeFlyer's avatar
DonLakeFlyer committed
255
                           << _serialConfig->baud() << _serialConfig->dataBits() << _serialConfig->parity() << _serialConfig->stopBits();
256

Bill Bonney's avatar
Bill Bonney committed
257
    return true; // successful connection
pixhawk's avatar
pixhawk committed
258
}
259

Don Gagne's avatar
Don Gagne committed
260
261
void SerialLink::_readBytes(void)
{
DonLakeFlyer's avatar
DonLakeFlyer committed
262
263
264
265
266
267
268
269
270
271
272
273
    if (_port && _port->isOpen()) {
        qint64 byteCount = _port->bytesAvailable();
        if (byteCount) {
            QByteArray buffer;
            buffer.resize(byteCount);
            _port->read(buffer.data(), buffer.size());
            emit bytesReceived(this, buffer);
        }
    } else {
        // Error occurred
        qWarning() << "Serial port not readable";
        _emitLinkError(tr("Could not read data - link %1 is disconnected!").arg(getName()));
Don Gagne's avatar
Don Gagne committed
274
275
276
    }
}

277
void SerialLink::linkError(QSerialPort::SerialPortError error)
278
{
Don Gagne's avatar
Don Gagne committed
279
280
281
282
283
284
285
    switch (error) {
    case QSerialPort::NoError:
        break;
    case QSerialPort::ResourceError:
        emit connectionRemoved(this);
        break;
    default:
286
287
288
289
        // You can use the following qDebug output as needed during development. Make sure to comment it back out
        // when you are done. The reason for this is that this signal is very noisy. For example if you try to
        // connect to a PixHawk before it is ready to accept the connection it will output a continuous stream
        // of errors until the Pixhawk responds.
290
        //qCDebug(SerialLinkLog) << "SerialLink::linkError" << error;
Don Gagne's avatar
Don Gagne committed
291
        break;
292
    }
293
294
}

pixhawk's avatar
pixhawk committed
295
296
297
298
299
/**
 * @brief Check if connection is active.
 *
 * @return True if link is connected, false otherwise.
 **/
300
bool SerialLink::isConnected() const
301
{
302
    bool isConnected = false;
Bill Bonney's avatar
Bill Bonney committed
303

304
    if (_port) {
305
        isConnected = _port->isOpen();
lm's avatar
lm committed
306
    }
307

308
    return isConnected;
pixhawk's avatar
pixhawk committed
309
310
}

311
QString SerialLink::getName() const
pixhawk's avatar
pixhawk committed
312
{
313
    return _serialConfig->name();
pixhawk's avatar
pixhawk committed
314
315
}

316
317
318
319
/**
  * This function maps baud rate constants to numerical equivalents.
  * It relies on the mapping given in qportsettings.h from the QSerialPort library.
  */
320
qint64 SerialLink::getConnectionSpeed() const
321
{
Bill Bonney's avatar
Bill Bonney committed
322
    int baudRate;
323
324
    if (_port) {
        baudRate = _port->baudRate();
Bill Bonney's avatar
Bill Bonney committed
325
    } else {
326
        baudRate = _serialConfig->baud();
Bill Bonney's avatar
Bill Bonney committed
327
328
329
330
    }
    qint64 dataRate;
    switch (baudRate)
    {
DonLakeFlyer's avatar
DonLakeFlyer committed
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
    case QSerialPort::Baud1200:
        dataRate = 1200;
        break;
    case QSerialPort::Baud2400:
        dataRate = 2400;
        break;
    case QSerialPort::Baud4800:
        dataRate = 4800;
        break;
    case QSerialPort::Baud9600:
        dataRate = 9600;
        break;
    case QSerialPort::Baud19200:
        dataRate = 19200;
        break;
    case QSerialPort::Baud38400:
        dataRate = 38400;
        break;
    case QSerialPort::Baud57600:
        dataRate = 57600;
        break;
    case QSerialPort::Baud115200:
        dataRate = 115200;
        break;
        // Otherwise do nothing.
    default:
        dataRate = -1;
        break;
pixhawk's avatar
pixhawk committed
359
360
361
362
    }
    return dataRate;
}

363
void SerialLink::_resetConfiguration()
364
{
365
    if (_port) {
366
367
368
369
370
        _port->setBaudRate      (_serialConfig->baud());
        _port->setDataBits      (static_cast<QSerialPort::DataBits>    (_serialConfig->dataBits()));
        _port->setFlowControl   (static_cast<QSerialPort::FlowControl> (_serialConfig->flowControl()));
        _port->setStopBits      (static_cast<QSerialPort::StopBits>    (_serialConfig->stopBits()));
        _port->setParity        (static_cast<QSerialPort::Parity>      (_serialConfig->parity()));
371
    }
pixhawk's avatar
pixhawk committed
372
373
}

374
void SerialLink::_emitLinkError(const QString& errorMsg)
375
{
376
    QString msg("Error on link %1. %2");
Don Gagne's avatar
Don Gagne committed
377
    qDebug() << errorMsg;
378
    emit communicationError(tr("Link Error"), msg.arg(getName()).arg(errorMsg));
pixhawk's avatar
pixhawk committed
379
380
}

381
382
//--------------------------------------------------------------------------
//-- SerialConfiguration
pixhawk's avatar
pixhawk committed
383

384
SerialConfiguration::SerialConfiguration(const QString& name) : LinkConfiguration(name)
385
{
386
387
388
389
390
    _baud       = 57600;
    _flowControl= QSerialPort::NoFlowControl;
    _parity     = QSerialPort::NoParity;
    _dataBits   = 8;
    _stopBits   = 1;
391
    _usbDirect  = false;
pixhawk's avatar
pixhawk committed
392
393
}

394
SerialConfiguration::SerialConfiguration(SerialConfiguration* copy) : LinkConfiguration(copy)
395
{
396
397
398
399
400
401
402
    _baud               = copy->baud();
    _flowControl        = copy->flowControl();
    _parity             = copy->parity();
    _dataBits           = copy->dataBits();
    _stopBits           = copy->stopBits();
    _portName           = copy->portName();
    _portDisplayName    = copy->portDisplayName();
403
    _usbDirect          = copy->_usbDirect;
pixhawk's avatar
pixhawk committed
404
405
}

406
void SerialConfiguration::copyFrom(LinkConfiguration *source)
407
{
408
    LinkConfiguration::copyFrom(source);
409
    auto* ssource = qobject_cast<SerialConfiguration*>(source);
DonLakeFlyer's avatar
DonLakeFlyer committed
410
411
412
413
414
415
416
417
418
419
420
421
    if (ssource) {
        _baud               = ssource->baud();
        _flowControl        = ssource->flowControl();
        _parity             = ssource->parity();
        _dataBits           = ssource->dataBits();
        _stopBits           = ssource->stopBits();
        _portName           = ssource->portName();
        _portDisplayName    = ssource->portDisplayName();
        _usbDirect          = ssource->_usbDirect;
    } else {
        qWarning() << "Internal error";
    }
422
423
}

424
void SerialConfiguration::updateSettings()
425
{
426
    if(_link) {
427
        auto* serialLink = qobject_cast<SerialLink*>(_link);
428
429
430
        if(serialLink) {
            serialLink->_resetConfiguration();
        }
431
    }
432
433
}

434
void SerialConfiguration::setBaud(int baud)
pixhawk's avatar
pixhawk committed
435
{
436
    _baud = baud;
pixhawk's avatar
pixhawk committed
437
438
}

439
void SerialConfiguration::setDataBits(int databits)
pixhawk's avatar
pixhawk committed
440
{
441
    _dataBits = databits;
pixhawk's avatar
pixhawk committed
442
443
}

444
void SerialConfiguration::setFlowControl(int flowControl)
pixhawk's avatar
pixhawk committed
445
{
446
    _flowControl = flowControl;
pixhawk's avatar
pixhawk committed
447
448
}

449
void SerialConfiguration::setStopBits(int stopBits)
450
{
451
    _stopBits = stopBits;
pixhawk's avatar
pixhawk committed
452
453
}

454
void SerialConfiguration::setParity(int parity)
455
{
456
    _parity = parity;
pixhawk's avatar
pixhawk committed
457
458
}

459
void SerialConfiguration::setPortName(const QString& portName)
460
{
461
462
463
464
    // No effect on a running connection
    QString pname = portName.trimmed();
    if (!pname.isEmpty() && pname != _portName) {
        _portName = pname;
465
        _portDisplayName = cleanPortDisplayname(pname);
466
467
468
    }
}

469
470
471
QString SerialConfiguration::cleanPortDisplayname(const QString name)
{
    QString pname = name.trimmed();
dogmaphobic's avatar
dogmaphobic committed
472
#ifdef Q_OS_WIN
473
474
475
476
477
478
479
480
    pname.replace("\\\\.\\", "");
#else
    pname.replace("/dev/cu.", "");
    pname.replace("/dev/", "");
#endif
    return pname;
}

481
void SerialConfiguration::saveSettings(QSettings& settings, const QString& root)
482
{
483
    settings.beginGroup(root);
484
485
486
487
488
489
490
    settings.setValue("baud",           _baud);
    settings.setValue("dataBits",       _dataBits);
    settings.setValue("flowControl",    _flowControl);
    settings.setValue("stopBits",       _stopBits);
    settings.setValue("parity",         _parity);
    settings.setValue("portName",       _portName);
    settings.setValue("portDisplayName",_portDisplayName);
491
    settings.endGroup();
492
}
493

494
void SerialConfiguration::loadSettings(QSettings& settings, const QString& root)
495
{
496
    settings.beginGroup(root);
497
498
499
500
501
502
503
    if(settings.contains("baud"))           _baud           = settings.value("baud").toInt();
    if(settings.contains("dataBits"))       _dataBits       = settings.value("dataBits").toInt();
    if(settings.contains("flowControl"))    _flowControl    = settings.value("flowControl").toInt();
    if(settings.contains("stopBits"))       _stopBits       = settings.value("stopBits").toInt();
    if(settings.contains("parity"))         _parity         = settings.value("parity").toInt();
    if(settings.contains("portName"))       _portName       = settings.value("portName").toString();
    if(settings.contains("portDisplayName"))_portDisplayName= settings.value("portDisplayName").toString();
504
    settings.endGroup();
505
}
506
507
508
509
510
511
512
513
514
515
516

QStringList SerialConfiguration::supportedBaudRates()
{
    if(!kSupportedBaudRates.size())
        _initBaudRates();
    return kSupportedBaudRates;
}

void SerialConfiguration::_initBaudRates()
{
    kSupportedBaudRates.clear();
517
    kSupportedBaudRates = QStringList({
518
519
#if USE_ANCIENT_RATES
#if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_DARWIN)
520
521
        "50",
        "75",
522
#endif
523
        "110",
524
#if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_DARWIN)
525
526
527
        "150",
        "200" ,
        "134"  ,
528
#endif
529
530
531
        "300",
        "600",
        "1200",
532
#if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_DARWIN)
533
        "1800",
534
535
#endif
#endif
536
537
538
        "2400",
        "4800",
        "9600",
539
#if defined(Q_OS_WIN)
540
        "14400",
541
#endif
542
543
        "19200",
        "38400",
544
#if defined(Q_OS_WIN)
545
        "56000",
546
#endif
547
548
        "57600",
        "115200",
549
#if defined(Q_OS_WIN)
550
        "128000",
551
#endif
552
        "230400",
553
#if defined(Q_OS_WIN)
554
        "256000",
555
#endif
556
557
        "460800",
        "500000",
DonLakeFlyer's avatar
DonLakeFlyer committed
558
#if defined(Q_OS_LINUX)
559
        "576000",
560
#endif
561
562
        "921600",
    });
563
564
}

565
566
567
568
569
570
571
void SerialConfiguration::setUsbDirect(bool usbDirect)
{
    if (_usbDirect != usbDirect) {
        _usbDirect = usbDirect;
        emit usbDirectChanged(_usbDirect);
    }
}