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

How to configure GATT server in NRF app to test CTS example

Hello, I read the instructions here for using the Nordic NRF Connect app for testing the CTS example: https://devzone.nordicsemi.com/f/nordic-q-a/53382/implementing-current-time-service-example

However, I do not see an option in the app for configuring the GATT server as described. I have an iPhone running iOS 13.6. I am using a Rigado BMD-340 Dev Kit with NRF52840 for running the CTS example. I have the most updated version of the NRF app.

This is a screenshot of what I see when I connect via the NRF Connect app:

As you can see from this screenshot, CTS discovery is not successful - no event seems to be triggered:

Question 1: How do I configure the GATT server on the NRF app to make my phone a Current Time server?

Question 2: How does the current time service get called? In the second screenshot (see references in left sidebar), it is evident that the event handler is never explicitly called. So how is an event generated? Let's say that a current time server is properly set up on the app. What does the client (NRF device) have to do to discover the Current Time Server, or to request a Current Time reading from the server?

Thanks!

  • Hi,

    Question 1: How do I configure the GATT server on the NRF app to make my phone a Current Time server?

    The instructions in the example documentation are for nRF Connect for Desktop. Using that you simply import the cts_central.ncs by selecting Server Setup and loading the file:

    You can set this up for iOS also, but then you need to configure it manually by adding the CTS service (UUID 0x1805) and the current time characteristic (UUID 0x2A2B).

    Question 2: How does the current time service get called?

    You can see this from the example in the SDK (examples\ble_peripheral\ble_app_cts_c\main.c), and this follows the same approach as all other services in the SDK.

    In the second screenshot (see references in left sidebar), it is evident that the event handler is never explicitly called. So how is an event generated?

    The on_cts_c_evt() function is a callback function / event handler that is registered when you initialize the CTS service (see the services_init() function in the example). The CTS implementation gets all BLE events as it is registered as an observer by the NRF_SDH_BLE_OBSERVERS macro which is part of the BLE_CTS_C_DEF macro. So when there is a BLE event that is handled by the service implementation (components\ble\ble_services\ble_cts_c\ble_cts_c.c), which calls the registered event handler when appropriate.

    What does the client (NRF device) have to do to discover the Current Time Server

    That is part of the service discovery procedure which is handled by the DB discovery module. Looking at main.c again, you can see in pm_evt_handler() that service discovery is started once the link is encrypted, by the call to ble_db_discovery_start(). Events from the discovery module is passed to the CTS implementation by the call to ble_cts_c_on_db_disc_evt() within db_disc_handler(). And here the CTS module does as described before, handles the event, and if relevant, passes an event back to the registered event handler (BLE_CTS_C_EVT_DISCOVERY_COMPLETE or BLE_CTS_C_EVT_DISCOVERY_FAILED in this case).

    or to request a Current Time reading from the server?

    Once the service is discovered, you can get the current time by calling ble_cts_c_current_time_read(). Then wait for the BLE_CTS_C_EVT_CURRENT_TIME event and do something with it (the example prints it using current_time_print()).

  • Thank you so much for this thorough reply!

    You can set this up for iOS also, but then you need to configure it manually by adding the CTS service (UUID 0x1805) and the current time characteristic (UUID 0x2A2B)

    Do you mean that these have to be added on the phone side in the NRF app, or on the firmware side? If the former, how do you add a service and characteristic in the app -- I did not see an option to do so...

    The CTS implementation gets all BLE events as it is registered as an observer by the NRF_SDH_BLE_OBSERVERS macro which is part of the BLE_CTS_C_DEF macro.

    Do you mean that the CTS instance and anything else registered as an observer each gets ALL the BLE events that occur and has to filter internally which events are relevant / can be handled by itself? Or if not, do you mean that the db_discovery handles the filtering of events to the right modules? Does it handle all BLE events, or only events associated with services?

    Once the service is discovered, you can get the current time by calling ble_cts_c_current_time_read().

    If my previous statement is true, does that mean ble_cts_c_current_time_read() passes an event to the db discovery module?

  • Hi,

    nordev said:
    Do you mean that these have to be added on the phone side in the NRF app, or on the firmware side? If the former, how do you add a service and characteristic in the app -- I did not see an option to do so...

    You are right, I was writing too fast. That is only available on the Android version. So you have to use nRF Connect for desktop or nRF Connect for Android to do this test.

    nordev said:
    Do you mean that the CTS instance and anything else registered as an observer each gets ALL the BLE events that occur and has to filter internally which events are relevant / can be handled by itself?

    Yes, that is how it is done.

    nordev said:
    Does it handle all BLE events, or only events associated with services?

    The service implementation only handles what it cares about and ignore other events.

    nordev said:
    If my previous statement is true, does that mean ble_cts_c_current_time_read() passes an event to the db discovery module?

    No. The discovery module is used for service discovery, and no longer used when that is done. ble_cts_c_current_time_read() is implemented in the CTS client implementation (components\ble\ble_services\ble_cts_c\ble_cts_c.c), and that just makes a GATTC read operation. That is queued and handled by the SoftDevice, which will generate an event when the read is completed. If you look more in ble_cts_c.c, you will see the ble_cts_c_on_ble_evt() handler which handled events, and it calls current_time_read() do process this before it calls the applications event handler in the end. That is where you see this in your application. In the example, the event handler is on_cts_c_evt() in the main., and that is where you read it.

  • Events from the discovery module is passed to the CTS implementation by the call to ble_cts_c_on_db_disc_evt() within db_disc_handler()

    Hypothetically, if I develop a custom service for the central, should I put something equivalent to custom_service_on_db_disc_evt() into the db_disc_handler() as well, just like how it is for CTS?

    you will see the ble_cts_c_on_ble_evt() handler which handled events, and it calls current_time_read() do process this before it calls the applications event handler in the end. That is where you see this in your application. In the example, the event handler is on_cts_c_evt() in the main., and that is where you read it.

    ble_cts_c_on_ble_evt() seems like a callback function too, but it doesn't seem to be registered anywhere. What passes it the events? 

    To clarify, the applications event handler is on_cts_c_evt(), and ble_cts_c_on_ble_evt() is the SoftDevice event handler?

    Thank you again for the helpful and detailed answers!

  • Hi,

    nordev said:
    Hypothetically, if I develop a custom service for the central, should I put something equivalent to custom_service_on_db_disc_evt() into the db_disc_handler() as well, just like how it is for CTS?

    Yes, if you make a custom service client, then you need to be able to discover it. And it makes sense to use the same approach as in the SDK examples.

    nordev said:
    ble_cts_c_on_ble_evt() seems like a callback function too, but it doesn't seem to be registered anywhere.

    It is a bit difficult to see, but you should look at the definition of BLE_CTS_C_DEF in ble_cts_c.h. This includes NRF_SDH_BLE_OBSERVER which adds ble_cts_c_on_ble_evt to the list of BLE observers. This is a section variable holding function pointers that should get all BLE events, so this makes the SoftDevice handler call ble_cts_c_on_ble_evt. Section variables are a bit like black magic, but it works Slight smile .

    nordev said:
    What passes it the events? 

    If you want to look into the details you can check nrf_sdh.c, which is where the handler is called from. If you have configured to use the scheduler for BLE events, this is done by nrf_sdh_evts_poll().

    nordev said:
    To clarify, the applications event handler is on_cts_c_evt(), and ble_cts_c_on_ble_evt() is the SoftDevice event handler?

    Yes, sort off. on_cts_c_evt() is the applications event handler that handles events from the CTS client (ble_cts_c), and ble_cts_c_on_ble_evt() is the CTS client module's event handler for handling events form the SoftDevice.

Related