2

I need a very straightforward piece of code that connects a bluetooth device (home made) as a virtual COM (Serial Port Profile), writes some data to it and then reads the reply. The written data turns ON a LED on the device (which makes it easy to see if data sent are actually received by the device)

I have it working with QSerialPort, using Qt 5.3.1, compiled with Visual Studio 2010 on Windows7. It works on my laptop, I just need to specify the port name COMi. Once my device is paired, the Bluetooth properties dialog of Windows tells me which virtual port should be used to connect the device.

I want to port this Qt code to Android. Apparently, Android system won't let me open the COM port directly as a QSerialPort (/dev/tty* access is denied). So I need to port the code to use a QBluetoothSocket rather than a QSerialPort.

Unfortunately, I can't get this QBluetoothSocket based code to work...

Here is the QSerialPort code working:

#include <QApplication>
#include <QThread>
#include <QMessageBox>
#include <QtSerialPort/QSerialPort>

#include <sstream>
#include <vector>

static std::vector<char> sReceived;
class readThread : public QThread
{
public:
    readThread( QSerialPort* port ) : m_port( port ), m_bContinue( true )
    {

    }

    void run()
    {
        while ( m_bContinue )
        {
            m_port->waitForReadyRead( 100 );

            QByteArray received = m_port->readAll();
            for ( size_t i = 0; i != received.size(); ++i )
            {
                sReceived.push_back( received.at(i) );
            }
        }
    }

    volatile bool m_bContinue;

private:
    QSerialPort* m_port;
};

int main( int argc, char* argv[] )
{
    QApplication app(argc, argv);

    int res = 1;
    if ( argc != 2 )
    {
        QMessageBox::critical( NULL, "Invalid argument", "Specify COM port name as first and unic parameter" );
    }
    else
    {
        QString portName = argv[1];

        QSerialPort port( portName );

        if ( port.open( QSerialPort::ReadWrite ) )
        {
            if ( port.setBaudRate( QSerialPort::Baud115200 ) &&
                 port.setFlowControl( QSerialPort::HardwareControl ) &&
                 port.setStopBits( QSerialPort::OneStop ) &&
                 port.setParity( QSerialPort::NoParity ) &&
                 port.setDataBits( QSerialPort::Data8 ) )
            {
                readThread thrd( &port );
                thrd.start();

                static const size_t requestMessageSize = 10;
                char request[requestMessageSize] = { 0x01, 0x00, 0x07, 0x01, 0x01, 0x00, 0x00, 0xBE, 0x0B, 0x00 };
                if ( port.write( request, requestMessageSize ) == requestMessageSize )
                {
                    port.waitForBytesWritten( 1000 );

                    QThread::sleep( 5 );

                    if ( !sReceived.empty() )
                    {
                        std::stringstream str;
                        str << "Received:" << std::hex;
                        for ( size_t i = 0; i != sReceived.size(); ++i )
                        {
                            str << " 0x" << static_cast<int>( sReceived[i] );
                        }
                        str << std::endl;
                        str << "Could open port, send and receive data" << std::endl;
                        QMessageBox::information( NULL, "OK", str.str().c_str() );
                        res = 0;
                    }
                    else
                    {
                        QMessageBox::critical( NULL, "Error", "Could open port, send data, but did not received any reply" );
                    }
                }
                else
                {
                    QMessageBox::critical( NULL, "Error", "Unable to send request to port" );
                }

                thrd.m_bContinue = false;
                thrd.wait();
            }
            else
            {
                QMessageBox::critical( NULL, "Error", "Unable to configure port" );
            }
            port.close();
        }
        else
        {
            QMessageBox::critical( NULL, "Unable to connect", QString("Could not open connection with %1").arg( portName ) );
        }
    }

    return res;
}

And here is the QBluetooth based code not working:

bthandler.h:

#pragma once

#include <QObject>
#include <QtBluetooth/QBluetoothDeviceDiscoveryAgent>
#include <QtBluetooth/QBluetoothSocket>

class BTHandler : public QObject
{
    Q_OBJECT

public:
    BTHandler( QBluetoothDeviceDiscoveryAgent& agent );

public slots:
    void deviceDiscovered( const QBluetoothDeviceInfo& );
    void detectError( QBluetoothDeviceDiscoveryAgent::Error );
    void detectError( QBluetoothSocket::SocketError );

    QBluetoothDeviceInfo* getDevice() { return (m_foundDeviceFlag) ? &m_foundDevice : NULL; }

    QBluetoothSocket* createSocket();

private:
    volatile bool m_foundDeviceFlag;
    QBluetoothDeviceInfo m_foundDevice;
};

bthandler.cpp:

#include "bthandler.h"

#include <sstream>

#include <QtBluetooth/QBluetoothDeviceInfo>
#include <QtBluetooth/QBluetoothSocket>
#include <QtBluetooth/QBluetoothUuid>

#include <QMessageBox>
#include <QThread>

BTHandler::BTHandler( QBluetoothDeviceDiscoveryAgent& agent ) : 
                         m_foundDeviceFlag( false )
{
    // Create a discovery agent and connect to its signals
    connect(&agent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),
            this, SLOT(deviceDiscovered(QBluetoothDeviceInfo)));
    connect(&agent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)),
            this, SLOT(detectError(QBluetoothDeviceDiscoveryAgent::Error)));
}

void BTHandler::deviceDiscovered( const QBluetoothDeviceInfo& device )
{
    QBluetoothDeviceInfo::DataCompleteness complete = QBluetoothDeviceInfo::DataComplete;
    QBluetoothDeviceInfo::DataCompleteness incomplete = QBluetoothDeviceInfo::DataIncomplete;
    QList<QBluetoothUuid> services1 = device.serviceUuids();
    QList<QBluetoothUuid> services2 = device.serviceUuids(&complete);
    QList<QBluetoothUuid> services3 = device.serviceUuids(&incomplete);

    QMessageBox::information( NULL, "Found device", device.name() );

    if ( device.name() == "BLUESWEAT11" )
    {
        QMessageBox::information( NULL, "Info", "Found correct device" );
        m_foundDevice = QBluetoothDeviceInfo( device );
        m_foundDeviceFlag = true;
    }
}

void BTHandler::detectError( QBluetoothDeviceDiscoveryAgent::Error error )
{
    std::stringstream str;
    str << "An error occured during bluetooth lookup: " << (int) error;
    QMessageBox::critical( NULL, "Error", str.str().c_str() );
}

void BTHandler::detectError( QBluetoothSocket::SocketError error )
{
    std::stringstream str;
    str << "An error occured during socket use: " << (int) error;
    QMessageBox::critical( NULL, "Error", str.str().c_str() );
}

QBluetoothSocket* BTHandler::createSocket()
{
    QBluetoothSocket* socket = NULL;

    if ( m_foundDeviceFlag )
    {
        QBluetoothUuid uuid( QBluetoothUuid::Rfcomm );
        socket = new QBluetoothSocket( QBluetoothServiceInfo::RfcommProtocol );

        connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)),
                this, SLOT(detectError(QBluetoothSocket::SocketError)));

        socket->connectToService( m_foundDevice.address(), uuid );

        if ( !socket->isOpen() )
        {
            QMessageBox::critical( NULL, "Error", "Unabel to open socket" );
            delete socket;
        }

        while ( socket->state() != QBluetoothSocket::ConnectedState )
        {
            QThread::sleep( 1 );
        }
    }

    return socket;
}

main.cpp:

#include "bthandler.h"

#include <vector>
#include <sstream>

#include <QMessageBox>
#include <QApplication>

#include <QtBluetooth/QBluetoothDeviceDiscoveryAgent>
#include <QtBluetooth/QBluetoothDeviceInfo>
#include <QtBluetooth/QBluetoothSocket>
#include <QtBluetooth/QBluetoothUuid>

#include <QThread>

static std::vector<char> sReceived;
class readThread : public QThread
{
public:
    readThread( QBluetoothSocket* port ) : m_port( port ), m_bContinue( true )
    {

    }

    void run()
    {
        while ( m_bContinue )
        {
            m_port->waitForReadyRead( 100 );

            QByteArray received = m_port->readAll();
            for ( size_t i = 0; i != received.size(); ++i )
            {
                sReceived.push_back( received.at(i) );
            }
        }
    }

    volatile bool m_bContinue;

private:
    QBluetoothSocket* m_port;
};

int main( int argc, char* argv[] )
{
    QApplication app(argc, argv);

    QBluetoothDeviceDiscoveryAgent discoveryAgent;
    BTHandler handler( discoveryAgent );

    discoveryAgent.start();
    while ( handler.getDevice() == NULL )
    {
        QThread::sleep( 1 );
        app.processEvents();
    }
    discoveryAgent.stop();

    int res = 1;

    QBluetoothSocket* socket = handler.createSocket();

    if ( socket )
    {
        readThread thrd( socket );
        thrd.start();

        static const size_t requestMessageSize = 10;
        char request[requestMessageSize] = { 0x01, 0x00, 0x07, 0x01, 0x01, 0x00, 0x00, 0xBE, 0x0B, 0x00 };
        if ( socket->write( request, requestMessageSize ) == requestMessageSize )
        {
            socket->waitForBytesWritten( 1000 );

            QThread::sleep( 5 );

            if ( !sReceived.empty() )
            {
                std::stringstream str;
                str << "Received:" << std::hex;
                for ( size_t i = 0; i != sReceived.size(); ++i )
                {
                    str << " 0x" << static_cast<int>( sReceived[i] );
                }
                str << std::endl;
                str << "Could open port, send and receive data" << std::endl;
                QMessageBox::information( NULL, "OK", str.str().c_str() );
                res = 0;
            }
            else
            {
                QMessageBox::critical( NULL, "Error", "Could open port, send data, but did not received any reply" );
            }
        }
        else
        {
            QMessageBox::critical( NULL, "Error", "Unable to send request to port" );
        }

        thrd.m_bContinue = false;
        thrd.wait();

        socket->close();
        delete socket;
    }
    else
    {
        QMessageBox::critical( NULL, "Unable to connect", "Could not open bluetooth connection with device" );
    }

    return res;
}

In this version of the code, while waiting for connection to be established (while ( socket->state() != QBluetoothSocket::ConnectedState )), QtCreator log reports the following error:

W/BluetoothAdapter( 5866): getBluetoothService() called with no BluetoothManagerCallback
D/BluetoothSocket( 5866): connect(), SocketState: INIT, mPfd: {ParcelFileDescriptor: FileDescriptor[65]}
W/System.err( 5866): java.io.IOException: read failed, socket might closed or timeout, read ret: -1
W/System.err( 5866):    at android.bluetooth.BluetoothSocket.readAll(BluetoothSocket.java:505)
W/System.err( 5866):    at android.bluetooth.BluetoothSocket.readInt(BluetoothSocket.java:516)
W/System.err( 5866):    at android.bluetooth.BluetoothSocket.connect(BluetoothSocket.java:320)
W/System.err( 5866):    at dalvik.system.NativeStart.run(Native Method)
jpo38
  • 20,821
  • 10
  • 70
  • 151
  • Are you absolutely sure you got a connection and all handshaking is done by the time you call the `waitForReadyRead`? – Surt Nov 20 '14 at 17:32
  • Good point. I was actually testing `socket->isOpen()` but this is apparently not enough. I updated the post, now I wait for socket's state to be `QBluetoothSocket::ConnectedState` and QtCReator log now reports an IO error. – jpo38 Nov 21 '14 at 08:12
  • Are you saying that you have successfully connected and exchanged data through Bluetooth SPP mode from a Windows machine ? If this is the case, it is strange, since Qt documentation - http://doc.qt.io/qt-5/qtbluetooth-index.html - explicitly specifies the supported OSes, Windows not being among them. Also, there is nothing related to QSerialPort in the Qt documentation about it being able to connect and exchange data with a bluetooth device. So this begs the question - how specially set up was that home made device of yours ? Would the QSerialPort work with any SPP bluetooth device ? Thanks! – kellogs Aug 11 '15 at 20:02
  • 1
    Once paired to a Windows laptop with BT support, my home made device (like any other SPP based device) has a COM port assigned and can then be used through 'QSerialPort' (not using QBluetooth). Under Android you can't do that because the OS won't let you connect the port. – jpo38 Aug 12 '15 at 11:13

2 Answers2

0

Are you absolutely sure you got a connection and all handshaking is done by the time you call the waitForReadyRead?

USB / Bluetooth usually demand a bit bit more waiting than serial.

Surt
  • 15,501
  • 3
  • 23
  • 39
  • Yes, after your comment above, the original post was updated. Please see the error reported by the System. Waiting is not the solution: I now wait for connection to be ready, but am getting a System error. – jpo38 Nov 21 '14 at 11:03
  • My memory of this is rather foggy about this so I can only advice to look at [read failed](http://stackoverflow.com/questions/18657427/ioexception-read-failed-socket-might-closed-bluetooth-on-android-4-3?lq=1) or [no BluetoothManagerCallback](http://stackoverflow.com/questions/17304479/android-getbluetoothservice-called-with-no-bluetoothmanagercallback). If that doesn't help I'm falling back to googeling. – Surt Nov 21 '14 at 12:07
0

After searching again on the web, I found this Qtbug, reporting the same issue: https://bugreports.qt-project.org/browse/QTBUG-40172

I upgraded to Qt 5.3.2, and the code simply worked!!!

Thanks Surt for the help, dunno if it would have worked without the while ( socket->state() != QBluetoothSocket::ConnectedState ) loop. But for sure, it cannot work with Qt 5.3.1!

jpo38
  • 20,821
  • 10
  • 70
  • 151