SerialLink.cc 16.3 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>
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"
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
{
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";
125 126

    _disconnect();
127

dogmaphobic's avatar
dogmaphobic committed
128
#ifdef __android__
129
    qgcApp()->toolbox()->linkManager()->suspendConfigurationUpdates(true);
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

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
        QGC::SLEEP::usleep(50000);
161 162
        delete _port;
        _port = NULL;
163
    }
pixhawk's avatar
pixhawk committed
164

165
    qCDebug(SerialLinkLog) << "SerialLink: hardwareConnect to " << _serialConfig->portName();
166

167
    // If we are in the Pixhawk bootloader code wait for it to timeout
168
    if (_isBootloader()) {
169
        qCDebug(SerialLinkLog) << "Not connecting to a bootloader, waiting for 2nd chance";
170 171 172
        const unsigned retry_limit = 12;
        unsigned retries;
        for (retries = 0; retries < retry_limit; retries++) {
173 174
            if (!_isBootloader()) {
                QGC::SLEEP::msleep(500);
175 176 177 178 179 180 181
                break;
            }
            QGC::SLEEP::msleep(500);
        }
        // Check limit
        if (retries == retry_limit) {
            // bail out
182
            qWarning() << "Timeout waiting for something other than booloader";
183 184 185 186
            return false;
        }
    }

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

189 190
    QObject::connect(_port, static_cast<void (QSerialPort::*)(QSerialPort::SerialPortError)>(&QSerialPort::error),
                     this, &SerialLink::linkError);
191
    QObject::connect(_port, &QIODevice::readyRead, this, &SerialLink::_readBytes);
Bill Bonney's avatar
Bill Bonney committed
192

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

195
    // TODO This needs a bit of TLC still...
196

197
    // After the bootloader times out, it still can take a second or so for the Pixhawk USB driver to come up and make
198
    // the port available for open. So we retry a few times to wait for it.
dogmaphobic's avatar
dogmaphobic committed
199 200 201
#ifdef __android__
    _port->open(QIODevice::ReadWrite);
#else
202 203
    for (int openRetries = 0; openRetries < 4; openRetries++) {
        if (!_port->open(QIODevice::ReadWrite)) {
204
            qCDebug(SerialLinkLog) << "Port open failed, retrying";
205 206 207 208
            QGC::SLEEP::msleep(500);
        } else {
            break;
        }
209
    }
dogmaphobic's avatar
dogmaphobic committed
210
#endif
211
    if (!_port->isOpen() ) {
212 213 214
        qDebug() << "open failed" << _port->errorString() << _port->error() << getName() << qgcApp()->toolbox()->linkManager()->isAutoconnectLink(this);
        error = _port->error();
        errorString = _port->errorString();
215
        emit communicationUpdate(getName(), tr("Error opening port: %1").arg(_port->errorString()));
216
        _port->close();
217 218
        delete _port;
        _port = NULL;
219
        return false; // couldn't open serial port
220 221
    }

222 223
    _port->setDataTerminalReady(true);

224
    qCDebug(SerialLinkLog) << "Configuring port";
225 226 227 228 229
    _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()));
230

231
    emit communicationUpdate(getName(), "Opened port!");
Bill Bonney's avatar
Bill Bonney committed
232
    emit connected();
233

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

Bill Bonney's avatar
Bill Bonney committed
237
    return true; // successful connection
pixhawk's avatar
pixhawk committed
238
}
239

240 241 242
void SerialLink::_readBytes(void)
{
    qint64 byteCount = _port->bytesAvailable();
243
    if (byteCount) {
244 245 246 247 248 249 250
        QByteArray buffer;
        buffer.resize(byteCount);
        _port->read(buffer.data(), buffer.size());
        emit bytesReceived(this, buffer);
    }
}

251
void SerialLink::linkError(QSerialPort::SerialPortError error)
252
{
253 254 255 256 257 258 259
    switch (error) {
    case QSerialPort::NoError:
        break;
    case QSerialPort::ResourceError:
        emit connectionRemoved(this);
        break;
    default:
260 261 262 263
        // 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.
264
        //qCDebug(SerialLinkLog) << "SerialLink::linkError" << error;
265
        break;
266
    }
267 268
}

pixhawk's avatar
pixhawk committed
269 270 271 272 273
/**
 * @brief Check if connection is active.
 *
 * @return True if link is connected, false otherwise.
 **/
274
bool SerialLink::isConnected() const
275
{
276
    bool isConnected = false;
Bill Bonney's avatar
Bill Bonney committed
277

278
    if (_port) {
279
        isConnected = _port->isOpen();
lm's avatar
lm committed
280
    }
281

282
    return isConnected;
pixhawk's avatar
pixhawk committed
283 284
}

285
QString SerialLink::getName() const
pixhawk's avatar
pixhawk committed
286
{
287
    return _serialConfig->portName();
pixhawk's avatar
pixhawk committed
288 289
}

290 291 292 293
/**
  * This function maps baud rate constants to numerical equivalents.
  * It relies on the mapping given in qportsettings.h from the QSerialPort library.
  */
294
qint64 SerialLink::getConnectionSpeed() const
295
{
Bill Bonney's avatar
Bill Bonney committed
296
    int baudRate;
297 298
    if (_port) {
        baudRate = _port->baudRate();
Bill Bonney's avatar
Bill Bonney committed
299
    } else {
300
        baudRate = _serialConfig->baud();
Bill Bonney's avatar
Bill Bonney committed
301 302 303 304
    }
    qint64 dataRate;
    switch (baudRate)
    {
DonLakeFlyer's avatar
DonLakeFlyer committed
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
    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
333 334 335 336
    }
    return dataRate;
}

337
void SerialLink::_resetConfiguration()
338
{
339
    if (_port) {
340 341 342 343 344
        _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()));
345
    }
pixhawk's avatar
pixhawk committed
346 347
}

348
void SerialLink::_emitLinkError(const QString& errorMsg)
349
{
350
    QString msg("Error on link %1. %2");
351
    qDebug() << errorMsg;
352
    emit communicationError(tr("Link Error"), msg.arg(getName()).arg(errorMsg));
pixhawk's avatar
pixhawk committed
353 354
}

355 356
//--------------------------------------------------------------------------
//-- SerialConfiguration
pixhawk's avatar
pixhawk committed
357

358
SerialConfiguration::SerialConfiguration(const QString& name) : LinkConfiguration(name)
359
{
360 361 362 363 364
    _baud       = 57600;
    _flowControl= QSerialPort::NoFlowControl;
    _parity     = QSerialPort::NoParity;
    _dataBits   = 8;
    _stopBits   = 1;
365
    _usbDirect  = false;
pixhawk's avatar
pixhawk committed
366 367
}

368
SerialConfiguration::SerialConfiguration(SerialConfiguration* copy) : LinkConfiguration(copy)
369
{
370 371 372 373 374 375 376
    _baud               = copy->baud();
    _flowControl        = copy->flowControl();
    _parity             = copy->parity();
    _dataBits           = copy->dataBits();
    _stopBits           = copy->stopBits();
    _portName           = copy->portName();
    _portDisplayName    = copy->portDisplayName();
377
    _usbDirect          = copy->_usbDirect;
pixhawk's avatar
pixhawk committed
378 379
}

380
void SerialConfiguration::copyFrom(LinkConfiguration *source)
381
{
382 383
    LinkConfiguration::copyFrom(source);
    SerialConfiguration* ssource = dynamic_cast<SerialConfiguration*>(source);
DonLakeFlyer's avatar
DonLakeFlyer committed
384 385 386 387 388 389 390 391 392 393 394 395
    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";
    }
396 397
}

398
void SerialConfiguration::updateSettings()
399
{
400 401 402 403 404
    if(_link) {
        SerialLink* serialLink = dynamic_cast<SerialLink*>(_link);
        if(serialLink) {
            serialLink->_resetConfiguration();
        }
405
    }
406 407
}

408
void SerialConfiguration::setBaud(int baud)
pixhawk's avatar
pixhawk committed
409
{
410
    _baud = baud;
pixhawk's avatar
pixhawk committed
411 412
}

413
void SerialConfiguration::setDataBits(int databits)
pixhawk's avatar
pixhawk committed
414
{
415
    _dataBits = databits;
pixhawk's avatar
pixhawk committed
416 417
}

418
void SerialConfiguration::setFlowControl(int flowControl)
pixhawk's avatar
pixhawk committed
419
{
420
    _flowControl = flowControl;
pixhawk's avatar
pixhawk committed
421 422
}

423
void SerialConfiguration::setStopBits(int stopBits)
424
{
425
    _stopBits = stopBits;
pixhawk's avatar
pixhawk committed
426 427
}

428
void SerialConfiguration::setParity(int parity)
429
{
430
    _parity = parity;
pixhawk's avatar
pixhawk committed
431 432
}

433
void SerialConfiguration::setPortName(const QString& portName)
434
{
435 436 437 438
    // No effect on a running connection
    QString pname = portName.trimmed();
    if (!pname.isEmpty() && pname != _portName) {
        _portName = pname;
439
        _portDisplayName = cleanPortDisplayname(pname);
440 441 442
    }
}

443 444 445
QString SerialConfiguration::cleanPortDisplayname(const QString name)
{
    QString pname = name.trimmed();
446
#ifdef Q_OS_WIN
447 448 449 450 451 452 453 454
    pname.replace("\\\\.\\", "");
#else
    pname.replace("/dev/cu.", "");
    pname.replace("/dev/", "");
#endif
    return pname;
}

455
void SerialConfiguration::saveSettings(QSettings& settings, const QString& root)
456
{
457
    settings.beginGroup(root);
458 459 460 461 462 463 464
    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);
465
    settings.endGroup();
466
}
467

468
void SerialConfiguration::loadSettings(QSettings& settings, const QString& root)
469
{
470
    settings.beginGroup(root);
471 472 473 474 475 476 477
    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();
478
    settings.endGroup();
479
}
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 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

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";
531
#if defined(Q_OS_LINUX)
532 533 534 535 536
    kSupportedBaudRates << "576000";
#endif
    kSupportedBaudRates << "921600";
}

537 538 539 540 541 542 543
void SerialConfiguration::setUsbDirect(bool usbDirect)
{
    if (_usbDirect != usbDirect) {
        _usbDirect = usbDirect;
        emit usbDirectChanged(_usbDirect);
    }
}