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

Bluetooth Low Energy Scanner: Discover Service Details



  • Hello,

    I am trying to understand how BLE works by playing with the BLE Scanner example from Qt. So after discovering BLE devices, connecting to a device and selecting a service, the next step is to print the characteristics. For this purpose, there is the following code in the example:

    void Device::connectToService(const QString &uuid)
    {
        QLowEnergyService *service = nullptr;
        for (auto s: qAsConst(m_services)) {
            auto serviceInfo = qobject_cast<ServiceInfo *>(s);
            if (!serviceInfo)
                continue;
    
            if (serviceInfo->getUuid() == uuid) {
                service = serviceInfo->service();
                break;
            }
        }
    
        if (!service)
            return;
    
        qDeleteAll(m_characteristics);
        m_characteristics.clear();
        emit characteristicsUpdated();
    
        if (service->state() == QLowEnergyService::DiscoveryRequired) {
            //! [les-service-3]
            connect(service, &QLowEnergyService::stateChanged,
                    this, &Device::serviceDetailsDiscovered);
            service->discoverDetails();
            setUpdate("Back\n(Discovering details...)");
            //! [les-service-3]
            return;
        }
    
        //discovery already done
        const QList<QLowEnergyCharacteristic> chars = service->characteristics();
        for (const QLowEnergyCharacteristic &ch : chars) {
            auto cInfo = new CharacteristicInfo(ch);
            m_characteristics.append(cInfo);
        }
    
        QTimer::singleShot(0, this, &Device::characteristicsUpdated);
    }
    
    
    void Device::serviceDetailsDiscovered(QLowEnergyService::ServiceState newState)
    {
        qDebug() << "service details discovered" << newState; //this line is from me for debugging
    
        if (newState != QLowEnergyService::ServiceDiscovered) {
            // do not hang in "Scanning for characteristics" mode forever
            // in case the service discovery failed
            // We have to queue the signal up to give UI time to even enter
            // the above mode
            if (newState != QLowEnergyService::DiscoveringServices) {
                QMetaObject::invokeMethod(this, "characteristicsUpdated",
                                          Qt::QueuedConnection);
            }
            return;
        }
    
        auto service = qobject_cast<QLowEnergyService *>(sender());
        if (!service)
            return;
    
    
    
        //! [les-chars]
        const QList<QLowEnergyCharacteristic> chars = service->characteristics();
        for (const QLowEnergyCharacteristic &ch : chars) {
            auto cInfo = new CharacteristicInfo(ch);
            m_characteristics.append(cInfo);
        }
        //! [les-chars]
    
        emit characteristicsUpdated();
    }
    

    So when I run the program with the code line I inserted for debugging in the serviceDetailsDiscovered - slot

        qDebug() << "service details discovered" << newState;
    

    This code is called twice and the output is:
    "service details discovered QLowEnergyService::DiscoveringServices
    service details discovered QLowEnergyService::ServiceDiscovered"

    But as I can see, the slot is only called once in the code, when the state changes. So how is it possible, that the slot is executed twice although it is only called once? if I debug with a breakpoint, it is not working because only when main is executed to end the UI is shown up..


  • Moderators

    @SpaceToon said in Bluetooth Low Energy Scanner: Discover Service Details:

    But as I can see, the slot is only called once in the code, when the state changes

    That method is a slot. It is called every time QLowEnergyService::stateChanged() signal is emitted. In this case, visibly, it is emitted twice: first when discovery is started, then when a service was discovered.


  • Moderators

    @SpaceToon said in Bluetooth Low Energy Scanner: Discover Service Details:

    But as I can see, the slot is only called once in the code, when the state changes

    That method is a slot. It is called every time QLowEnergyService::stateChanged() signal is emitted. In this case, visibly, it is emitted twice: first when discovery is started, then when a service was discovered.



  • @sierdzio said in Bluetooth Low Energy Scanner: Discover Service Details:

    @SpaceToon said in Bluetooth Low Energy Scanner: Discover Service Details:

    But as I can see, the slot is only called once in the code, when the state changes

    That method is a slot. It is called every time QLowEnergyService::stateChanged() signal is emitted. In this case, visibly, it is emitted twice: first when discovery is started, then when a service was discovered.

    Hey, thank you for your response. That makes sense. The problem is, that in my code, the method is only called once so that means, that the state "ServiceDiscovered" is not reached at all. Do you have any clue why? This is my code:

    void Scanner::discoveryFinished(){
        qDebug() << "\nService Discovery finished. Following Services found:\n";
        QList<QBluetoothUuid> serviceList = controller->services();
    
        QLowEnergyService *uartService = controller->createServiceObject(adafruitServiceUuid);
        connect(uartService, &QLowEnergyService::stateChanged, this, &Scanner::printChars);
        uartService->discoverDetails();
    }
    
    
    
    void Scanner::printChars(QLowEnergyService::ServiceState newState){
    
        auto service = qobject_cast<QLowEnergyService *>(sender());
    
        const QList<QLowEnergyCharacteristic> chars = service->characteristics();
        qDebug()<< chars.size();
        for (const QLowEnergyCharacteristic &ch : chars) {
            qDebug() << &ch;
        }
    }
    

    My Slot printChars is only called once so the chars.size() gives me 0. But Why the state is not changing to ServiceDiscovered so that I can print the characteristics?


  • Moderators

    It's possible that your service has already discovered all the details, and it is not searching anymore. https://doc.qt.io/qt-5/qlowenergyservice.html#ServiceState-enum

    What is the returned state at this point?



  • @sierdzio said in Bluetooth Low Energy Scanner: Discover Service Details:

    It's possible that your service has already discovered all the details, and it is not searching anymore. https://doc.qt.io/qt-5/qlowenergyservice.html#ServiceState-enum

    What is the returned state at this point?

    It is "DiscoveringServices" - so it is not done discovering the details but I do not know why.
    When I run the Scanner example and chose my BLE device, the characteristics are shown normally. But when I execute my own written code, the state "QLowEnergyService::ServiceDiscovered" is not reached, so I think the problem is not my BLE device but something in my code.


  • Moderators

    No idea.

    But notice that the example code specifically does not call characteristics() method when state is DiscoveringServices. Perhaps try doing the same?



  • @sierdzio said in Bluetooth Low Energy Scanner: Discover Service Details:

    No idea.

    But notice that the example code specifically does not call characteristics() method when state is DiscoveringServices. Perhaps try doing the same?

    I made a switch statement in my slot so if state is Discovering i should only print the state and if the state is "ServiceDiscovered" then it shoul call characteristics(). But as I mentioned, the state "ServiceDiscovered" is never reached.