BLE scanResult.getServiceUuids() returns an empty iterator

Help please with suggestions how to troubleshoot a connection problem with a BLE app I'm developing.

I've examined the several examples given here in the forum (the Raspberry Pi app and the Threadmill machine app) and I don't see anything different from what I'm doing.

Basically the BLE scan is able to discover the device of interest, but the problem after that is the call to getServiceUuids() that returns an empty iterator.

In the same time using a BLE scanner application on my android phone discovers properly the exposed services and their characteristics.

CIQ however doesn't discover any services for some reason.

Here's something like a log from the println() statements along the code:

Starting BLE scan (use device = [2, 1, 6, 12, 255, 13, 0, 88, 67, 84, 82, 65, 67, 69, 82, 0, 10, 9, 88, 67, 45, 84, 114, 97, 99, 101, 114])
XCT_BleDelegate.onScanStateChange, scanstate=1 ,status=0
XCT_BleDelegate.onScanResults
_use_device_to_connect=[2, 1, 6, 12, 255, 13, 0, 88, 67, 84, 82, 65, 67, 69, 82, 0, 10, 9, 88, 67, 45, 84, 114, 97, 99, 101, 114]                                -> this is where onScanResults is entered with a specific device to search for

current iterator device:XC-Tracer, rawdata=[2, 1, 6, 12, 255, 13, 0, 88, 67, 84, 82, 65, 67, 69, 82, 0, 10, 9, 88, 67, 45, 84, 114, 97, 99, 101, 114]     -> this is the scan results iterator
match=true
device found in scan results

search for service = 0000FFE0-0000-1000-8000-00805F9B34FB                   ->after the wanted device is found, we continue to searching for the service we're interested in

                                                                                                                          ->on this place there have to be entries like "current iterator service: [UUID]", but the iterator is empty
flight data service not found on device

  • Is everything set up properly as far as the profile and registering that profile (including all services and their characteristics?)

    Things must be set up in the profile, as it won't discover things that aren't defined.  You'll want a profile for

    0000FFE0-0000-1000-8000-00805F9B34FB

    Here's a case where I use two services on the thiny52.  The profiles and the registration.

        private var _envProfileDef = {
            :uuid => THINGY_ENVIRONMENTAL_SERVICE,
            :characteristics => [{
                :uuid => TEMPERATURE_CHARACTERISTIC,
                :descriptors => [Ble.cccdUuid()]
            }, {
                :uuid => PRESSURE_CHARACTERISTIC,
                :descriptors => [Ble.cccdUuid()]
            }, {
                :uuid => HUMIDITY_CHARACTERISTIC,
                :descriptors => [Ble.cccdUuid()]
            }, {
                :uuid => AIR_QUALITY_CHARACTERISTIC,
                :descriptors => [Ble.cccdUuid()]
            }, {
                :uuid => COLOR_CHARACTERISTIC,
                :descriptors => [Ble.cccdUuid()]
            }, {
                :uuid => ENVCFG_CHARACTERISTIC,
                :descriptors => [Ble.cccdUuid()]
            }]
        };
        
        private var _uiProfileDef = {
            :uuid => THINGY_UI_SERVICE,
            :characteristics => [{
                :uuid => LED_CHARACTERISTIC,
                :descriptors => [Ble.cccdUuid()]
            }, {
                :uuid => BUTTON_CHARACTERISTIC,
                :descriptors => [Ble.cccdUuid()]
            }, {
                :uuid => EXTPIN_CHARACTERISTIC,
                :descriptors => [Ble.cccdUuid()]
            }]
        };  
        
        function registerProfiles() {
            Ble.registerProfile( _envProfileDef );
            Ble.registerProfile( _uiProfileDef );     
        }

  • The profile definition is correct. It's very simple containing only one service and one characteristic.

    I've spent a whole day changing things and trying only to get the same results - no services reported by getServiceUuids() function of the scan results. (empty iterator). However it turned out that if I just go ahead and pair with the device and try to subscribe for the characteristic change notifications  for that "invisible" service, then it actually works - the connection to the device is successfull and incoming data starts to enter through the onCharacteristicChanged() callback.

    Now I have another  problem that I've been trying to solve without much success so far. The refresh rate of the data coming from the connected BLE device is so high that during the time needed to parse, transform and process it there seem to be messages that are missed. This is easily spotted in my case because the device is sending a series of different NMEA sentences, but since the sentences are long, each of them is sent in portions that have to be combined together. If I do no processing and just print the incoming data to the log, the data is OK. However if I start parsing and processing the received NMEA sentences, the output in the log shows that the messages that I'm working with become incomplete - my explanation being that most likely some of the incoming BLE communication is skipped during the processing time and hence that result.

    Is there a way the BleDelegate to run in a separate thread or something like this independent from the UI?

  • If you're doing things based on the nordic thingy52 sample, are you sure you're consistently not getting an  iterator, or are you just not getting a scan result that matches what you are looking for?

    In "contains" in the BLE delegate, if I display the UUIDs found, and always return true, I see 5-10 things advertising around me  (watches, headphones, etc) so I'm thinking the scan results are there, but maybe there's something wrong with your compare.  In onScanResults, you don't see everything that was found up to that point, buy just what was found since the last time, and based on the speed a device advertises, so in the sample, you'll see it build it's own array of scan results that it's looking for.  I have 2 thingys, and what I'll see is it will find the first, then the next time onScanResults is called the second, etc.

    As far as "keeping up", what are you using for BLE?  The nRF52-dk or the dongle?  I was doing some testing a few months back where I'd enabled a bunch of notifies, and it seemed the dongle just couldn't keep up and things got messy.  The nrf52-dk could keep up though.  What happens if you try a real device?   If your're using the nrf52-dk, can you set the frequency that notifies occur on the thingy there's a configuration characteristic when you can specify the interval.  For example, for temperature and pressure there's:

    uint16_t - Temperature interval in ms (100 ms - 60 s).

    uint16_t - Pressure interval in ms (50 ms - 60 s).

    in the configuration characteristic for the environmental service.  Maybe you can slow things down in a similar manner?

    CIQ apps are single threaded.

  • Also, if your app is written such that it updates the screen based on a timer, you may want to change that, as you may only need to update the display when something changes.  So instead of a timer, call Ui.requestUpdate() only when there's something new based on onCharacteristicChanged().  That way the delegate and the display of the data don't compete.

  • In the sim, I have seen cases where device.getService(uuid) return null when it shouldn't, and while I don't use device.getServices() it wouldn't surprise me if it can happens there too.

    And maybe itt's related to scanResult.getServiceUuids().

    It's not consistent, and restarting the simulation usually clears it up.  I don't see it on real devices though, but for testing in the sim, I just do a println so I know to restart the simulation. 

  • Excuse me for the delayed reply, but  wanted to check and double check everything to be completely sure about what I'm answering.

    1)  scanResult.getServiceUuids()

    It indeed returns an empty iterator (that's on the real device) - consistently, every time the same. The execution never enters the loop that iterates the available services, because the first call to next() already returns null. So the code never reaches to the comparison of available service UUIDs to the UUID that is registered in the profile and I'm interested in.

    In the same time, if I go ahead and pair directly to the BLE sensor (cause I know it sends this data) and then call device.getService(uuid) if returns a valid reference that can be used to obtain a reference to the charactreristic, subscribe for notifications etc.

    2) About  your question what I'm using for the BLE development purposes

    All described above happens on the real device (edge1030).

    Since I've examined the BLE sensor through my phone (with a suitable app) and was able to obtain all the necessary info for connecting to it (UUIDs of services and characteristics) from one side, and having some of your example code how to manage the connection in CIQ from another, I've decided to go ahead and test and debug the things on the real device - code with a lot of println statements, sideloads and examining of the generated log file after that. It's kind of slow, but works.

    After the BLE layer is verified to work, I can continue testing the rest of the application in the simulator as before, by replacing the class that commands the BLE stuff with a dummy one that will simulate some data resembling the real one. I'll have to anyway go this way, since I cannot run the simulator with edge1030 if I have anything from CIQ3.1 in the code, because in SDK3.1.6 this device is still 3.0.

    3) Updating the UI

    I've initially gone into direction of processing/parsing the data as it arrives through the characteristic change notification and updating the UI accordingly, as this is the most logical thing to do. This is where I stumble upon the unexpected problem with high refresh rate ot the incoming data. It actually the garmin device (edge1030) that is not able to keep up and crashes.

    If I comment out the parsing, processing and visualization if the data, the log files reveal consistent flow of "good" data coming though onCharacteristicChanged(). However as soon as I switch the data processing on, things get messy. A lot of "broken" data messages start to be logged arriving in onCharacteristicChanged().

    For that reason I've switched to the method that you actually suggest not to use - updating teh UI through a timer. The BLE layer only collects the data, and I run a timer every one sec. in the UI that gets from the BLE layer the last stored messages, parses them and refreshes the display. This seems to work kind of OK in my particular case.

    -----------

    In a hindsight the approach of developing with sideloads on the real device has not been an optimal choice at all - maybe a hundred sideloads with different println statements here and there were needed until I manage to clean all BLE communication stuff.

    That was indeed slow  - upload, run, examine logs file, make some minor changes then upload on device and repeat (the slowest part of the process being actually the time needed by the device to switch to USB mass storage mode when connected to the computer and then restart for normal operation when disconnected from the computer)

  • $10US for the dongle is well worth the price when developing an app and you can use a target that supports BLE.

    But I'm confused here..  If you don't get any scan results, you can't pair, as the scan result is used for that, and pairing returns the device

  • But I'm confused here..  If you don't get any scan results, you can't pair, as the scan result is used for that, and pairing returns the device

    There are scan results that you can use to iterate all BLE devices in reach.

    However all the examples - yours and the one for the Treadmill machine - then use the getServiceUuids() method of the ScanResult class to discover the supported services by each device and only attempt to pair with a device that provides the specific service we're interested in.

    It is that particular method that returns no results in my case - no services seem to be advertised.

    However if I pair anyway and then call device.getService() with the service UUID that I already know, there is a valid result returned.

  • Ok, so it sounds like you're getting scanResults but no service UUIDs are being advertised by the device you're using (that's something on the device end).

    Maybe you can use something like getRawData() to figure out if you have the scanResult for the device you're interested in.