Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QBluetoothSocket never connects



  • Background: I have a Raspberry Pi running a simple Bluetooth server using an L2CAP socket: it advertises nothing, just listens on L2CAP port 0x1001, accepts any connection, reads from the socket and prints the received data. It works just fine from a client on a second Raspberry Pi, and tests show it can achieve 100 kBytes/sec over the L2CAP socket (I need 15 kBytes/sec, well beyond RFcomm). Note the server RPI has already paired with the client RPI and with my laptop (used separately). Both RPi client and server use the Bluez library, no Qt -- this is just describing the environment, not where the trouble lies.

    The problem is on Mac OS X, for which Bluetooth development in Python is not currently possible and C++ is outrageously complex, except using Qt. My code mimics the Linux client, and tries to connect to the server. The server prints that it accepted the connection from my laptop, but the client's QBluetoothSocket never transitions from from ConnectingState to ConnectedState. I have used Qt extensively, but this is my first use of its Bluetooth module. Here's the (simplified) client code

            // QCoreApplication stuff omitted, exec() is called right after printing CC
            printf("AA\n");
            sock = new QBluetoothSocket(QBluetoothServiceInfo::L2capProtocol,0);
            // ... connect all signals to callbacks that print their names + arguments
            printf("BB\n");
            QBluetoothAddress addr("B8:27:EB:71:F4:02"); // RPi Bluetooth addr
            sock->connectToService(addr, 0x1001);
            printf("CC\n");
            // QCoreApplication::exec() called here
            sleep(5);  // actually QTimer::singleShot()
            printf("DD\n");
            int n = sock->write("Test");
            printf("test: %d %s\n",n,qPrintable(sock->errorString()));
            // ... goes on to close the socket and closeup
    

    I thought that is all I need. It directly parallels the Linux client. Here's the result of running it on my Mac. The RPi server printed "accepted connection" right after CC (very early in the sleep).

    main begun
    AA
    BB
    stateChanged 2
    CC
    Enter event loop
    <RPi server prints "accepted connection [laptop address]">
    <5 second delay>
    DD
    error 19 Cannot write while not connected
    test: -1 Cannot write while not connected
    <10 second delay, after which CoreApplication::quit() is called>
    main ends
    stateChanged 6
    stateChanged 0
    disconnected
    <program exits>
    

    What am I missing?
    Why doesn't the QBluetoothSocket ever go to ConnectedState?

    Is it that QBluetoothSocket doesn't work in this mode and needs a QBluetoothServiceInfo, so I have to go to the hassle of advertising a service, and all the complexity that entails (hard-coding the RPi address is acceptable here, and much simpler).

    I can provide all the code, but thought this simplified code would be better to look at (no AttachFile on this forum).


  • Qt Champions 2019

    @tjrob said in QBluetoothSocket never connects:

    sock->connectToService(addr, 0x1001);
    printf("CC\n");
    // QCoreApplication::exec() called here

    This is not how it should be done: QCoreApplication::exec() is a blocking call! The code after exec() will be executed when your app is terminating.
    Connect https://doc.qt.io/qt-5/qbluetoothsocket.html#connected signal to a slot and check whether it is called.



  • I used QCoreApplication correctly, but as I said, I collapsed code into a single sequence so the structure and problem are more obvious. So here are T.h, T.cc, and T.pro: build via "qmake && make". Note that T::connected() is never called, but the RPi server printed "accepted connection [laptop address]". The problem is clearly on the Mac where this Qt client program is run.

    BTW I am running the RPi server in another Terminal window via a Bluetooth serial connection from the Mac via "screen /dev/cu.rpi7-SerialPort 115200" -- so I know they are paired and connectable.

    The RPi code follows.

    //      T.h
      
    #include <QObject>
    #include <QBluetoothSocket>
    
    class T : public QObject {
            Q_OBJECT
            QBluetoothSocket *sock;
    public:
            T();
            ~T() { if(sock != 0) sock->close(); sock = 0; }
    public slots:
            void connected();
            void disconnected();
            void error(QBluetoothSocket::SocketError error);
            void stateChanged(QBluetoothSocket::SocketState state);
            void test();
    };
    
    //      T.cc - Qt Bluetooth ?
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    #include <QCoreApplication>
    #include <QTimer>
    
    #include "T.h"
    
    T::T() : sock(0)
    {
            printf("AA\n");
            sock = new QBluetoothSocket(QBluetoothServiceInfo::L2capProtocol,0);
            connect(sock,SIGNAL(connected()),this,SLOT(connected()));
            connect(sock,SIGNAL(disconnected()),this,SLOT(disconnected()));
            connect(sock,SIGNAL(error(QBluetoothSocket::SocketError)),
                    this,SLOT(error(QBluetoothSocket::SocketError)));
            connect(sock,SIGNAL(stateChanged(QBluetoothSocket::SocketState)),
                    this,SLOT(stateChanged(QBluetoothSocket::SocketState)));
            printf("BB\n");
            QBluetoothAddress addr("B8:27:EB:71:F4:02");
            sock->connectToService(addr,0x1001);
            printf("CC\n");
    }
    
    void T::connected()
    {
            printf("connected\n");
    }
    
    void T::disconnected()
    {
            printf("disconnected\n");
    }
    
    void T::error(QBluetoothSocket::SocketError error)
    {
            printf("error %d %s\n",error,qPrintable(sock->errorString()));
    }
    
    void T::stateChanged(QBluetoothSocket::SocketState state)
    {
            printf("stateChanged %d\n",state);
    }
    
    void T::test()
    {
            printf("DD\n");
            int n = sock->write("Test");
            printf("test: %d %s\n",n,qPrintable(sock->errorString()));
    }
    
    int main(int argc, char *argv[])
    {
            QCoreApplication app(argc,argv);
    
            printf("main begun\n");
            T t;
    
            QTimer::singleShot(5000, &t, SLOT(test()));
            QTimer::singleShot(12000, &app, SLOT(quit()));
    
            printf("Enter event loop\n");
            app.exec();
    
            printf("main ends\n");
            return 0;
    }
    
    #    T.pro
    TARGET = t
    INCLUDEPATH += .
    QT += bluetooth
    CONFIG += console
    
    HEADERS += *.h
    SOURCES += *.cc
    

    For completeness, here is the server code running on the Raspberry Pi (no Qt). The server needs to be re-run after each connection; it never gets past the call to read(). The Mac and the RPi should already be paired.

    // server.cc
    // For Raspberry Pi: sudo apt-get bluez libbluetooth-dev
    // g++ -o server server.cc -lbluetooth
    
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <bluetooth/bluetooth.h>
    #include <bluetooth/l2cap.h>
    
    bdaddr_t Any = { 0,0,0,0,0,0 };
    
    const int MTU=672;
    
    int main(int argc, char **argv)
    {
        struct sockaddr_l2 loc_addr = { 0 }, rem_addr = { 0 };
        char buf[1024] = { 0 };
        int s, client, bytes_read;
        socklen_t opt = sizeof(rem_addr);
    
        // allocate socket
        s = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
    
        // bind socket to port 0x1001 of the first available
        // bluetooth adapter
        loc_addr.l2_family = AF_BLUETOOTH;
        loc_addr.l2_bdaddr = Any;
        //loc_addr.l2_bdaddr = *BDADDR_ANY;
        loc_addr.l2_psm = htobs(0x1001);
    
        bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));
    
        // put socket into listening mode
        listen(s, 1);
    
        // accept one connection
        client = accept(s, (struct sockaddr *)&rem_addr, &opt);
    
        ba2str( &rem_addr.l2_bdaddr, buf );
        fprintf(stderr, "socket=%d accepted connection from %s\n", client, buf);
    
        // read data from the client
        bytes_read = read(client, buf, sizeof(buf));
        if( bytes_read > 0 ) {
            buf[bytes_read] = '\0';
            printf("received [%s]\n", buf);
        } else {
            printf("read error\n");
        }
    
        // write lots of data to meausre data transfer rate
        char packet[MTU];
        for(int i=0; i<MTU; ++i)
            packet[i] = 'A';
        for(int i=0; i<10000; ++i) {
            write(client, packet, MTU);
        }
    
        // close connection
        close(client);
        close(s);
    }
    

    Here is Python3 client code for a second Raspberry Pi that successfully connects to the server and measures the data transfer rate over the L2CAP socket.

    import time
    import socket
    
    serverMACAddress = 'B8:27:EB:71:F4:02'
    port = 0x1001
    s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_L2CAP)
    s.connect((serverMACAddress,port))
    
    time.sleep(5)
    print("Send Config line")
    s.send(b'Config line')
    
    # measure data transfer rate
    start = 0.0
    now = start
    update = start
    try:
        data = ""
        while 1:
            text = s.recv(1024).decode('ascii')
            if start == 0.0:
                    start = time.time()
                    update = start
            data += text
            now = time.time()
            if now-update > 10.0:
                    print(len(data)/(now-update))
                    update = now
                    data = ""
    except:
        pass
    
    s.close()
    

    Bluetooth development on Linux is much easier, because the libraries are complete. The Mac is MUCH more difficult.


  • Qt Champions 2019

    @tjrob said in QBluetoothSocket never connects:

    I collapsed code into a single sequence so the structure and problem are more obvious

    But they weren't as the code you posted originally was simply wrong and how can anybody guess how your real code looks like? It is important to post the code you're really using.

    Is any other slot called? Especially error(...)?

    Just a note: why do you use printf() in C++?



  • All slots do a printf; no other slot was called, including error. I use printf because it is often easier to format data than using cout. I guess I show my age, as I learned C in the 80s before there was C++.

    I built this same code on a Raspberry Pi 3B+, and it connects properly to the server (on the other RPi).

    main begun
    AA
    BB
    stateChanged 2
    CC
    Enter event loop
    stateChanged 3
    connected
    <... few seconds delay>
    DD
    test: 4 
    <... few seconds delay>
    main ends
    stateChanged 6
    stateChanged 0
    disconnected
    

    This is clearly a Mac issue in QBluetoothSocket. Very frustrating, as Apple uses Objective C for their Bluetooth library, making C++ usage obscure at best.


Log in to reply