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

Check if currently advertising

In my application, a host co-processor controls when to start/stop advertising, connect etc.
I've noticed that soft-device command are state-dependent, e.g. calling ble_advertising_start() while advertising, or calling sd_ble_gap_adv_stop() while not, causes an SD assert which by default soft-resets the device (related question here).

How should I mitigate this? Do I have to keep track of the stack's state (Advertising, Connected, Scanning, etc.), or, better yet, are there API methods to check the state of the stack before issuing a command?

Version: nRF5 SDK 11.0.0, s132, with PCA10040 EVB

  • Hi,

    First I want to mention that it is not the SD that asserts. It simply returns an error code. The APP_ERROR_CHECK macro is the one that will reset the device, unless you choose to handle it in a different way. You can call any API functions with any parameter, at any time, and as long as you do not lie about the length of any buffers you provide, the SD shall never assert or crash. The functions will return an error code stating what happened.

    And for your question, you are right in your assumption that you have to remember the state in your application. If sd_ble_gap_adv_start() returns NRF_SUCCESS, the device will advertise until

    1. BLE_GAP_EVT_CONNECTED event is received, i.e. a connection is made (in the case of connectable advertisement). If you are trying to use the central to connect at the same time, you need to check the role flag of the connection. If BLE_GAP_ROLE_PERIPH, you are no longer advertising.

    2. BLE_GAP_EVT_TIMEOUT event is received (in the case where you set a timeout, or use high duty-cycle directed advertisements) and the src field is BLE_GAP_TIMEOUT_SRC_ADVERTISING.

    3. sd_ble_gap_adv_stop() has been called.

    There are no commands to check the current state, and it would be prone to race conditions if we had one. Instead, our stack relies on application events to tell you when the internal state changes.

  • Thanks Ulrich. Some thoughts:

    • Can you please suggest a safe way to stop advertising in case: 1) Advertising timeout is finite 2) Advertising is forever?
    • You mentioned race conditions. Assuming I only call SD methods from the main thread, will using the application scheduler and SOFTDEVICE_HANDLER_APPSH_INIT mitigate those?
  • The safe way is to create a (possibly volatile) flag like is_advertising, which is set to true whenever adv_start() returns NRF_SUCCESS, and set to false when any of the three conditions above are hit. Never call adv_stop() unless the flag is true, and adv_start() unless it is false. The easier way is to simply handle or ignore the NRF_ERROR_INVALID_STATE return code, accepting that you do not really keep track of the state yourself.

    What I meant about race conditions is in the hypothetical case where you had a function to retrieve state. Then you could look at the state, see that it is advertising, then call adv_stop(). If advertising stops by itself between the two last steps, you would have the same problem as now. There are no such problems in the current implementation, unless you are changing your own application states in higher interrupt levels.

  • Thanks. Am I right to say that first option (using a volatile the flag) does not solve the race condition? Come to think of it, you will always have race conditions in a communication protocol, e.g. you call disconnect() at the exact same moment as your peer disconnects. The device must not reset in these situations.

    I'm opting for the second option, and be tolerant to 'Invalid State' errors, hence:

    • Do you see any problems with this approach I might have missed?
    • What is the correct way to ignore the NRF_ERROR_INVALID_STATE code? Should I override app_error_fault_handler()?
  • If the advertising stops, and you have not yet pulled the event stating this, then I think you should be safe. I'm not 100% sure on the internals, but as far as I know, all states are "pending" until you pull them with sd_ble_evt_get(). From then on, the stack assumes that you have dealt with the consequences of the event.

    I'm not well-wersed in the current SDK, so someone should correct me - but I think the correct way is to simply make an

    if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)
    {
        APP_ERROR_CHECK(err_code);
    }
    
Related