SerialLink.cc 17.2 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;

33
34
35
36
37
38
39
SerialLink::SerialLink(SharedLinkConfigurationPointer& config)
    : LinkInterface(config)
    , _port(NULL)
    , _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
    if (_port) {
        delete _port;
    }
63
    _port = NULL;
64
65
}

66
bool SerialLink::_isBootloader()
67
{
68
    QList<QSerialPortInfo> portList = QSerialPortInfo::availablePorts();
69
70
71
72
73
    if( portList.count() == 0){
        return false;
    }
    foreach (const QSerialPortInfo &info, portList)
    {
74
75
        qCDebug(SerialLinkLog) << "PortName    : " << info.portName() << "Description : " << info.description();
        qCDebug(SerialLinkLog) << "Manufacturer: " << info.manufacturer();
76
        if (info.portName().trimmed() == _serialConfig->portName() &&
77
78
79
80
81
                (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
82
        }
83
84
85
86
87
    }
    // Not found
    return false;
}

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

/**
 * @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
104
void SerialLink::_disconnect(void)
105
{
Don Gagne's avatar
Don Gagne committed
106
107
108
109
    if (_port) {
        _port->close();
        delete _port;
        _port = NULL;
110
    }
111

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

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

    _disconnect();
127

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

132
133
134
    QSerialPort::SerialPortError    error;
    QString                         errorString;

dogmaphobic's avatar
dogmaphobic committed
135
    // Initialize the connection
136
137
138
139
140
141
142
    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
143
        }
144

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

151
152
153
154
155
/// 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)
156
{
157
    if (_port) {
158
        qCDebug(SerialLinkLog) << "SerialLink:" << QString::number((long)this, 16) << "closing port";
159
        _port->close();
160
161
162
163
164
165

        // 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);
        }
166
167
        delete _port;
        _port = NULL;
168
    }
pixhawk's avatar
pixhawk committed
169

170
    qCDebug(SerialLinkLog) << "SerialLink: hardwareConnect to " << _serialConfig->portName();
171

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

178
        for (retries = 0; retries < retry_limit; retries++) {
179
            if (!_isBootloader()) {
180
181
182
183
184
                // 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);
                }
185
186
                break;
            }
187
188
189
190
191
192

            // 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);
            }
193
194
195
196
        }
        // Check limit
        if (retries == retry_limit) {
            // bail out
197
            qWarning() << "Timeout waiting for something other than booloader";
198
199
200
201
            return false;
        }
    }

202
    _port = new QSerialPort(_serialConfig->portName());
Bill Bonney's avatar
Bill Bonney committed
203

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

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

210
    // TODO This needs a bit of TLC still...
211

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

    // Try to open the port three times
    for (int openRetries = 0; openRetries < 3; openRetries++) {
220
        if (!_port->open(QIODevice::ReadWrite)) {
221
            qCDebug(SerialLinkLog) << "Port open failed, retrying";
222
223
224
225
226
227
            // 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);
228
229
230
        } else {
            break;
        }
231
    }
dogmaphobic's avatar
dogmaphobic committed
232
#endif
233
    if (!_port->isOpen() ) {
234
235
236
        qDebug() << "open failed" << _port->errorString() << _port->error() << getName() << qgcApp()->toolbox()->linkManager()->isAutoconnectLink(this);
        error = _port->error();
        errorString = _port->errorString();
DonLakeFlyer's avatar
DonLakeFlyer committed
237
        emit communicationUpdate(getName(), tr("Error opening port: %1").arg(_port->errorString()));
238
        _port->close();
Don Gagne's avatar
Don Gagne committed
239
240
        delete _port;
        _port = NULL;
241
        return false; // couldn't open serial port
242
243
    }

244
245
    _port->setDataTerminalReady(true);

246
    qCDebug(SerialLinkLog) << "Configuring port";
247
248
249
250
251
    _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()));
252

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

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

Bill Bonney's avatar
Bill Bonney committed
259
    return true; // successful connection
pixhawk's avatar
pixhawk committed
260
}
261

Don Gagne's avatar
Don Gagne committed
262
263
264
void SerialLink::_readBytes(void)
{
    qint64 byteCount = _port->bytesAvailable();
265
    if (byteCount) {
Don Gagne's avatar
Don Gagne committed
266
267
268
269
270
271
272
        QByteArray buffer;
        buffer.resize(byteCount);
        _port->read(buffer.data(), buffer.size());
        emit bytesReceived(this, buffer);
    }
}

273
void SerialLink::linkError(QSerialPort::SerialPortError error)
274
{
Don Gagne's avatar
Don Gagne committed
275
276
277
278
279
280
281
    switch (error) {
    case QSerialPort::NoError:
        break;
    case QSerialPort::ResourceError:
        emit connectionRemoved(this);
        break;
    default:
282
283
284
285
        // 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.
286
        //qCDebug(SerialLinkLog) << "SerialLink::linkError" << error;
Don Gagne's avatar
Don Gagne committed
287
        break;
288
    }
289
290
}

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

300
    if (_port) {
301
        isConnected = _port->isOpen();
lm's avatar
lm committed
302
    }
303

304
    return isConnected;
pixhawk's avatar
pixhawk committed
305
306
}

307
QString SerialLink::getName() const
pixhawk's avatar
pixhawk committed
308
{
309
    return _serialConfig->portName();
pixhawk's avatar
pixhawk committed
310
311
}

312
313
314
315
/**
  * This function maps baud rate constants to numerical equivalents.
  * It relies on the mapping given in qportsettings.h from the QSerialPort library.
  */
316
qint64 SerialLink::getConnectionSpeed() const
317
{
Bill Bonney's avatar
Bill Bonney committed
318
    int baudRate;
319
320
    if (_port) {
        baudRate = _port->baudRate();
Bill Bonney's avatar
Bill Bonney committed
321
    } else {
322
        baudRate = _serialConfig->baud();
Bill Bonney's avatar
Bill Bonney committed
323
324
325
326
    }
    qint64 dataRate;
    switch (baudRate)
    {
DonLakeFlyer's avatar
DonLakeFlyer committed
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
    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
355
356
357
358
    }
    return dataRate;
}

359
void SerialLink::_resetConfiguration()
360
{
361
    if (_port) {
362
363
364
365
366
        _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()));
367
    }
pixhawk's avatar
pixhawk committed
368
369
}

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

377
378
//--------------------------------------------------------------------------
//-- SerialConfiguration
pixhawk's avatar
pixhawk committed
379

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

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

402
void SerialConfiguration::copyFrom(LinkConfiguration *source)
403
{
404
405
    LinkConfiguration::copyFrom(source);
    SerialConfiguration* ssource = dynamic_cast<SerialConfiguration*>(source);
DonLakeFlyer's avatar
DonLakeFlyer committed
406
407
408
409
410
411
412
413
414
415
416
417
    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";
    }
418
419
}

420
void SerialConfiguration::updateSettings()
421
{
422
423
424
425
426
    if(_link) {
        SerialLink* serialLink = dynamic_cast<SerialLink*>(_link);
        if(serialLink) {
            serialLink->_resetConfiguration();
        }
427
    }
428
429
}

430
void SerialConfiguration::setBaud(int baud)
pixhawk's avatar
pixhawk committed
431
{
432
    _baud = baud;
pixhawk's avatar
pixhawk committed
433
434
}

435
void SerialConfiguration::setDataBits(int databits)
pixhawk's avatar
pixhawk committed
436
{
437
    _dataBits = databits;
pixhawk's avatar
pixhawk committed
438
439
}

440
void SerialConfiguration::setFlowControl(int flowControl)
pixhawk's avatar
pixhawk committed
441
{
442
    _flowControl = flowControl;
pixhawk's avatar
pixhawk committed
443
444
}

445
void SerialConfiguration::setStopBits(int stopBits)
446
{
447
    _stopBits = stopBits;
pixhawk's avatar
pixhawk committed
448
449
}

450
void SerialConfiguration::setParity(int parity)
451
{
452
    _parity = parity;
pixhawk's avatar
pixhawk committed
453
454
}

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

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

477
void SerialConfiguration::saveSettings(QSettings& settings, const QString& root)
478
{
479
    settings.beginGroup(root);
480
481
482
483
484
485
486
    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);
487
    settings.endGroup();
488
}
489

490
void SerialConfiguration::loadSettings(QSettings& settings, const QString& root)
491
{
492
    settings.beginGroup(root);
493
494
495
496
497
498
499
    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();
500
    settings.endGroup();
501
}
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552

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

void SerialConfiguration::_initBaudRates()
{
    kSupportedBaudRates.clear();
#if USE_ANCIENT_RATES
#if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_DARWIN)
    kSupportedBaudRates << "50";
    kSupportedBaudRates << "75";
#endif
    kSupportedBaudRates << "110";
#if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_DARWIN)
    kSupportedBaudRates << "134";
    kSupportedBaudRates << "150";
    kSupportedBaudRates << "200";
#endif
    kSupportedBaudRates << "300";
    kSupportedBaudRates << "600";
    kSupportedBaudRates << "1200";
#if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_DARWIN)
    kSupportedBaudRates << "1800";
#endif
#endif
    kSupportedBaudRates << "2400";
    kSupportedBaudRates << "4800";
    kSupportedBaudRates << "9600";
#if defined(Q_OS_WIN)
    kSupportedBaudRates << "14400";
#endif
    kSupportedBaudRates << "19200";
    kSupportedBaudRates << "38400";
#if defined(Q_OS_WIN)
    kSupportedBaudRates << "56000";
#endif
    kSupportedBaudRates << "57600";
    kSupportedBaudRates << "115200";
#if defined(Q_OS_WIN)
    kSupportedBaudRates << "128000";
#endif
    kSupportedBaudRates << "230400";
#if defined(Q_OS_WIN)
    kSupportedBaudRates << "256000";
#endif
    kSupportedBaudRates << "460800";
    kSupportedBaudRates << "500000";
DonLakeFlyer's avatar
DonLakeFlyer committed
553
#if defined(Q_OS_LINUX)
554
555
556
557
558
    kSupportedBaudRates << "576000";
#endif
    kSupportedBaudRates << "921600";
}

559
560
561
562
563
564
565
void SerialConfiguration::setUsbDirect(bool usbDirect)
{
    if (_usbDirect != usbDirect) {
        _usbDirect = usbDirect;
        emit usbDirectChanged(_usbDirect);
    }
}