This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nRF5 SDK 15.3.0 initial guidance

Hello,

I’ve developed a BLE peripheral product using Simblee (nRF51822 revision 3), now discontinued. We’ve decided to switch to Laird’s BL651 module and implement the app using nRF5 SDK 15.3.0. I’m new to nRF5 SDK and low-level access to BLE. I would appreciate some initial guidance.

Simblee allowed BLE peripheral development using the Arduino IDE and insulated developers from a lot of the complexities of BLE. I wrote code like this:

Setup …

SimbleeBLE.customUUID = "2220";
SimbleeBLE.txPowerLevel = +4;
SimbleeBLE.deviceName = peripheralName;
SimbleeBLE.advertisementData = advData;
SimbleeBLE.advertisementInterval = 500;
SimbleeBLE.begin();

To send data to the connected iOS app (BLE central):

SimbleeBLE.send(message, sizeof(message));

Interrupt routine for receiving data from the iOS app:

void SimbleeBLE_onReceive(char *data, int len)
{
  ...
}

The peripheral monitors a single GPIO pin and uses GPIOTE, PPI and timers for timing-critical code, which the Simblee library exposed. And we use OTA DFU to push out BLE peripheral app updates via our iOS app.

Simblee has a single service (0x2220) with a send (write) characteristic (0x2222), a receive (read, notify) characteristic (0x2221) and a disconnect (write) characteristic (0x2223). Seems pretty simple.

I have the nRF52 dev kit (which has an nRF52832, but can emulate the nRF52810, which is on Laird's BL651) and I have some of the Nordic examples working (using Segger Embedded Studio on macOS).

I would appreciate some initial guidance. Is the Nordic UART Service appropriate? Would the ble_peripheral/ble_app_uart SDK example be a reasonable starting point? What to focus my learning on? Security is not important, nor is power efficiency (we run on AA batteries and the product is used, on average, 20 hours per month). I would like the app to use the same service and characteristic UUIDs so that it will function with our current BLE central iOS app.

I appreciate any guidance. Many thanks,

Tim

Parents
  • Hi Tim

    I agree the ble_app_uart example should be a good place to start. It allows you to send variable length strings from one side to the other using one characteristic for send and one for receive, just like your original example. 

    The SDK examples as you've seen are significantly more complex to set up than the Arduino examples, giving you more control but also making the learning curve steeper.

    Many of the basic Bluetooth settings, such as the advertising name and advertising interval, are configured through defines at the top of the main.c file. 

    These parameters are then forwarded to the stack in the various init functions (the Bluetooth stack, aka SoftDevice, is configured through the ble_stack_init(), gap_params_init(), gatt_init(), services_init() and advertising_init() calls). 

    The UUID of the service, as well as the other service and characteristic configuration, is handled by the ble_nus.c and ble_nus.h files. 

    If you want to change these files I recommend making a local copy, so that you can make changes freely without modifying the original implementation. 

    The base UUID for the service is set by the NUS_BASE_UUID define on line 64 of ble_nus.c, and the UUID aliases (using the same base) for the two characteristics are defined just above. 

    I am not sure what the base UUID is in your existing example. When implementing a proprietary (non standard) service or characteristic it is not allowed to use the Bluetooth SIG base UUID, but it appears this is what the Arduino example is doing. 
    To verify this you can inspect the UUID in the advertise packet using the nRF Connect app for Android, iOS or desktop. 

    Sending data through the UART service is handled by the ble_nus_data_send function,and on line 545 of main.c you can see how this function is used. 

    Incoming data will be forwarded to the nus_data_handler(..) function, which is implemented on line 197 of main.c.

    This doesn't explain all there is to know about the ble_app_uart example, so I will do my best to help out if you have more questions Slight smile

    Best regards
    Torbjørn

  • Hello Torbjørn,

    Thank you! You affirm my sense of things, which is comforting. I'm enjoying the learning curve, but very much appreciate your willingness to help out when I'm stuck.

    About the base UUID, our product was originally based on RFduino (nRF51822 revision 1), which was a precursor to Simblee. Base UUID 0x2220 was that of RFduino. When we switched to Simblee (whose base UUID is 0xFE84), we used Simblee's ability to specify a custom UUID ('SimbleeBLE.customUUID = "2220";') to ensure compatibility with our iOS app. Both RFduino and Simblee were created by RF Digital (since acquired). Oddly, the Bluetooth SIG shows registration of only 0xFE84 to RF Digital. (... also 0xFE85 for a particular mode of Simblee.) Generally that's the story behind 0x2220.

    I will dive into ble_app_uart! I've succeeded in building and flashing the app to my nRF52 dev kit (using pca10040e and S112). I'm able to connect from iOS device using nRF Toolbox and send data to the nRF52832 (emulating nRF52810).

    My initial issues are:

    1 - Logging does not write to the debug terminal. See separate post.

    2 - If I flash and start debugging but I don't connect to the peripheral (peripheral remains advertising), after about 3 minutes, will fault on NRF_BREAKPOINT_COND. I've attached screen shots of launched app, then fault. Maybe related to an advertising timeout or something of the sort?

    Thanks! I hope this is helpful also for others.

    -Tim

Reply
  • Hello Torbjørn,

    Thank you! You affirm my sense of things, which is comforting. I'm enjoying the learning curve, but very much appreciate your willingness to help out when I'm stuck.

    About the base UUID, our product was originally based on RFduino (nRF51822 revision 1), which was a precursor to Simblee. Base UUID 0x2220 was that of RFduino. When we switched to Simblee (whose base UUID is 0xFE84), we used Simblee's ability to specify a custom UUID ('SimbleeBLE.customUUID = "2220";') to ensure compatibility with our iOS app. Both RFduino and Simblee were created by RF Digital (since acquired). Oddly, the Bluetooth SIG shows registration of only 0xFE84 to RF Digital. (... also 0xFE85 for a particular mode of Simblee.) Generally that's the story behind 0x2220.

    I will dive into ble_app_uart! I've succeeded in building and flashing the app to my nRF52 dev kit (using pca10040e and S112). I'm able to connect from iOS device using nRF Toolbox and send data to the nRF52832 (emulating nRF52810).

    My initial issues are:

    1 - Logging does not write to the debug terminal. See separate post.

    2 - If I flash and start debugging but I don't connect to the peripheral (peripheral remains advertising), after about 3 minutes, will fault on NRF_BREAKPOINT_COND. I've attached screen shots of launched app, then fault. Maybe related to an advertising timeout or something of the sort?

    Thanks! I hope this is helpful also for others.

    -Tim

Children
  • Hi Tim

    The 128 bit UUID consists of a 112-bit 'base UUID' and a 16-bit alias, and when doing something proprietary you either have to define your own unique base and combine it with any alias of your liking, or register a 16-bit alias with the Bluetooth SIG that can be combined with the official SIG base UUID. 

    It is possible that early version of the RFduino didn't follow these rules, and combined the unregistered 0x2220 alias with the Bluetooth SIG base, and that they later saw the error of their ways and registered for an alias through the SIG (0xFE84 and 0xFE85 according to this). 
    Then again, without seeing exactly what is advertised I don't know what setting 'SimbleeBLE.customUUID = "2220" actually means on air. Custom UUID to me would imply that both the base and alias are set to custom values. 

    This blog explains this in more detail. 

    1. I will let Didrik help you out with that one Slight smile

    2. This issue is caused by the fact that the example enters the system OFF sleep mode when advertising times out, and this mode doesn't work properly when running the debugger. 

    If you simply comment out the call to sleep_mode_enter() inside the on_adv_evt(ble_adv_evt_t ble_adv_evt) callback, you should see that this problem goes away. 

    static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
    {
        uint32_t err_code;
    
        switch (ble_adv_evt)
        {
            case BLE_ADV_EVT_FAST:
                err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
                APP_ERROR_CHECK(err_code);
                break;
            case BLE_ADV_EVT_IDLE:
                sleep_mode_enter();  // Try commenting out this line
                break;
            default:
                break;
        }
    }

    You might want to consider what you want to do in the application once the advertising times out. If you want advertising to go on forever you can set the APP_ADV_DURATION define at the top of main to 0. 
    For this to work you first need to change the following line inside the advertising_init(void) function from this (line 618 of main.c):

    init.advdata.flags              = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;

    to this:

    init.advdata.flags              = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;

    The reason for this is that the Bluetooth spec doesn't allow you to be in limited discovery mode for an infinite amount of time. 

    Best regards
    Torbjørn

  • Thank you Torbjørn. First, about UUIDs, I've attached iPad screen captures of nRF Connect and LightBlue connected to our Simblee-based product. Perhaps enough detail shown to be clearer on what 'SimbleeBLE.customUUID = "2220"' is doing. If you're correct that RFduino did not register with Bluetooth SIG, what to do? Should we register it ourselves for our product? (Both RFduino and Simblee have been discontinued for around a year.) Or should we switch to our own custom UUID? Or register our own ID with Bluetooth SIG (no idea what's involved in doing this)? Doing the latter two would break compatibility with our current iOS app and require an update. We can certainly consider this if it's the best way forward. Risk of continuing to use "2220"? Possibility that someone will register it for a new product in the future?

    Thank you, about the advertising timeout issue. For our product, infinite advertising timeout is desired. I've done as you suggest above and it appears to work.

    Next I will look at sending data back and forth between iOS app (central) and ble_app_uart example. I see the example described as able to send strings back and forth. I need to send bytes (sometimes strings, sometimes binary data) and hope this example can do that, or require only simple modifications.

    Grateful for your support, Torbjørn. Thank you.

  • Hi 

    Thanks for the LightBlue captures, they make it pretty clear that they are using an invalid 16-bit UUID, which we obviously must recommend against. 

    The best would be to register your own, but you can't just pick any number unfortunately. The Bluetooth SIG assigns these numbers from the top of the 16-bit address space, to reserve the bottom part for standard UUID's, which is why all the currently registered company UUID's are on the form 0xFXXX.

    For more information about this program please look here:
    https://www.bluetooth.com/specifications/assigned-numbers/16-bit-uuids-for-members/

    I would suggest changing your device to use either a 128-bit custom UUID, or a new 16-bit UUID that you register with the SIG as described above. 

    If you want backwards compatibility it should be possible to update the app to look for both the old 0x2220 devices, and the new ones, just be aware that other devices might also advertise 0x2220 and get mixed up with your devices in the app. 

    Another thing I noticed is that the manufacturer ID in the manufacturer specific data field is set to 0x0000, which is assigned to Ericsson. I assume this is also an oversight?

    The ble_app_uart example essentially allows you to send whatever you like. There is no need to format the data as null terminated strings. That said the idea is to use this service as a starting point, and extend and modify it as you see fit if you need to add more characteristics, change their configuration and so forth. I would also suggest changing the service UUID to a different random 128-bit UUID, again to identify this as your proprietary service, and not one of the dozens of different devices and examples running the Nordic UART service.

    Creating your own unique 128-bit UUID is as simple as generating a random one here (use version 4 rather than version 1):
    https://www.uuidgenerator.net/

    Best regards
    Torbjørn

  • Thanks Torbjørn. You make it clear that we are to use a vendor-specific UUID, or register on with the Bluetooth SIG. I took a quick look and see that we would need to become SIG members, then apply for a UUID ($2,500 each). Our product is a very niche product, likely to sell only into the 1,000s (currently just under 700 in the field). My sense is we'd be okay to generate our own vendor-specific UUID, but would appreciate your thoughts on this. Any pros/cons of registering a UUID with the SIG versus generating/using our own vendor-specific ID?

    About manufacture ID, our Simblee code sets the advertisement data to 0x00007274696D6572.

    SimbleeBLE.advertisementData = advData;

    I noticed that LightBlue shows this value as the Manufacture Data. Maybe for Simblee. the above statement sets both?

    We do this so that when our central (iOS app) scans for peripherals and a peripheral with service UUID 0x2220 is discovered, we check the advertisement data and if it matches, we assume it's one of our devices that has been discovered, and not another RFduino-based device. Objective C code snippet below:

    - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
    {
    
        // Respond only to RFduinos with manufacture data self.manufactureData.
        
        NSData *manufactureDataOfRFduino = [advertisementData valueForKey:CBAdvertisementDataManufacturerDataKey];
        
        if ([manufactureDataOfRFduino isEqual:self.manufactureData]) {
            // It's our device, carry on ...
            .
            .
            .

    I will need to learn about manufacture ID versus advertising data and set them appropriately. Is there a process for choosing a manufacture ID or will the Laird module we use (BL651) have its manufacture ID set appropriately?

    Many thanks ...

  • Hi Tim

    The only drawback of using a 128-bit vendor specific UUID over a registered 16-bit UUID is that you have to reserve a much larger part of the advertise packet to include the longer 128-bit UUID. 

    You will use 18 bytes total for advertising the full UUID (16 byte for the UUID and 2 byte for the advertising field header), leaving you only 13 bytes left of the total 31 byte advertising payload. A 16-bit UUID can be advertised in only 4 bytes. 

    A way to alleviate this issue is to put the 128-bit UUID in the scan response packet. Essentially this allows you to split all your advertise data into two 31 byte packets, giving you twice the amount of data available.
    That said I don't know if the SimbleeBLE API allows you to configure the scan response packet, or if you can only use advertise packet?

    Tim said:

    About manufacture ID, our Simblee code sets the advertisement data to 0x00007274696D6572.

    SimbleeBLE.advertisementData = advData;

    I noticed that LightBlue shows this value as the Manufacture Data. Maybe for Simblee. the above statement sets both?

    Again it seems the SimbleeBLE API is not properly following spec conventions and terminology. 

    The term 'advertisement data' implies that you are talking about the entire advertising payload, consisting of multiple fields such as service UUID, TX power level and local name. 

    In their case this refers to the manufacturer specific data field specifically, which is just a subfield within the entire advertisement packet. 

    The manufacturer specific data field is a nice way to add custom data to the advertise packet, since there is no limit on the size or type of data you can include here, but the first two bytes should reflect the company used. Laird is using company ID 0x0077, so you should probably be able to use that, but I would check with them first to be sure ;)

    The full list of company ID's can be found here:
    https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers/

    I doubt the BL651 will enforce any specific company ID in the manufacturer field. While Laird provide their own API for programming the modules I believe they also support flashing any code into the Nordic device, to avoid restricting the capabilities of the modules. 

    Best regards
    Torbjørn

Related