Platform Independent Serial Port Programming with Qt

Since Qt 5.1 there is a QSerialPort Class included. Maybe this artikle is still of some use for older Qt versions.


The Bones Speech and Sound Module is an Text to Speech hardware module communicating via a HDLC like protocol over FTDI USB serial port. Included with the Development Board of the module is a platform independent demo application showing the capabilities of the module. This Demo application is build upon Qt and the excellent QextSerialPort library to access the serial port.

Using the serial port library is pretty easy. First step is to look for available ports. We will filter all non FTDI Ports because our Adapter is based on FTDI:

  m_ports = QextSerialEnumerator::getPorts();
  qDebug() << List of ports:;
  for(int i = 0; i < m_ports.size(); i++) {

#ifdef FTDI_ONLY
    if(m_ports.at(i).vendorID == 0x403 && m_ports.at(i).productID == 0x6001) {
      qDebug() << Found FTDI: << m_ports.at(i).portName;
      ui->cbSerial->addItem(m_ports.at(i).friendName);
    }
#else
    qDebug() << Found: << m_ports.at(i).portName;
    ui->cbSerial->addItem(m_ports.at(i).friendName);
#endif

    qDebug() << port name: << m_ports.at(i).portName;
    qDebug() << friendly name: << m_ports.at(i).friendName;
    qDebug() << physical name: << m_ports.at(i).physName;
    qDebug() << enumerator name: << m_ports.at(i).enumName;
    qDebug() << vendor ID: << QString::number(m_ports.at(i).vendorID, 16);
    qDebug() << product ID: << QString::number(m_ports.at(i).productID, 16);
    qDebug() << ===================================;
  }

Then we have to create a port and connect its signals to to corresponding slots:

  m_port = new QextSerialPort(portName, QextSerialPort::EventDriven);
  m_port->setBaudRate(BAUD38400);
  m_port->setFlowControl(FLOW_OFF);
  m_port->setDataBits(DATA_8);
  m_port->setParity(PAR_NONE);
  m_port->setStopBits(STOP_1);

  if(m_port->open(QIODevice::ReadWrite) == true) {
    connect(m_port, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
    connect(m_port, SIGNAL(dsrChanged(bool)), this, SLOT(onDsrChanged(bool)));
    m_textOut->append(tr(Listening for data on %1).arg(m_port->portName()));
  } else {
    m_textOut->append(tr(Device failed to open: %1).arg(m_port->errorString()));
  }

Sending messages is a no-brainer, too:

  QByteArray message = QByteArray::fromHex(i_text.toLocal8Bit());
  m_port->write(message);
  m_port->flush();

Receiving bytes is done via the readyRead signal. Because BSM Messages are send in an HDLC like protocol we have to seperate protocoll messages from debug output. This is done by looking for the start/end flags:

void PortListener::onReadyRead()
{
  int oldBytes = m_receivedBytes.size();
  int newBytes = m_port->bytesAvailable();
  m_receivedBytes.resize(oldBytes + newBytes);
  m_port->read(m_receivedBytes.data() + oldBytes, newBytes);
  qDebug() << bytes read ( << newBytes << ):  << m_receivedBytes.toHex();

  do {
    // Each BSM message must beginn with 0x7e
    QByteArray plainText;
    while(m_receivedBytes.count() && 0x7e != m_receivedBytes[0]) {
      plainText.append(m_receivedBytes[0]);
      m_receivedBytes.remove(0,1);
    }

    if(plainText.count()) {
      qDebug() << Plain Text Message:  << plainText;
      QColor col = m_plainTextOut->textColor();
      m_plainTextOut->setTextColor(Qt::blue);
      m_plainTextOut->moveCursor(QTextCursor::End);
      m_plainTextOut->insertPlainText(plainText);
      m_plainTextOut->setTextColor(col);
      m_plainTextOut->ensureCursorVisible();
    }

    if(m_receivedBytes.count()<=1)
      return;

    // Subsequent 0x7e can be ignored
    while(0x7e == m_receivedBytes[1])
      m_receivedBytes.remove(1,1);

    for(int i=2; i<m_receivedBytes.size(); ++i) {
      if(0x7e == m_receivedBytes[i]) {
        parseMessage(m_receivedBytes.left(i+1));
        m_receivedBytes = m_receivedBytes.right(m_receivedBytes.size()-(i+1));
        break;
      }
    }
  } while(m_receivedBytes.size()>1 && 0x7e == m_receivedBytes[m_receivedBytes.size()-1]);
}

The full source of the application is available on Github: https://github.com/MikeBergmann/BSMDemo

I hope it is of some use to you.