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

Client without Notifications bricking device

Hey everyone, I have a device that's in production and everything is working great, however we have a customer that is creating their own software to interface with our sensor and they bricked a couple units because they did not turn notifications on. This causes the unit to seemingly  wait indefinitely waiting to get the thumbs up from the client that it received the packet, which never comes. We don't expect the unit to work without notifications on, but I don't want it to lock up like this... we have no hardware reset capability so the units are junk now.

I figure there has to be an event for a time out or the like that I could use to force a disconnect and start advertising again, I just can't seem to find it. I've tied BLE_GATTC_EVT_TIMEOUT, BLE_GATTS_EVT_TIMEOUT, and BLE_GAP_EVT_TIMEOUT. None of these seem to be triggered by the sensor not being able to send a packet.

Any thoughts?

Thanks,

Adam

Parents
  • Hello,

    What do you mean by "bricking device"? 

     

    This causes the unit to seemingly  wait indefinitely waiting to get the thumbs up from the client that it received the packet, which never comes.

     What device is waiting? Central or peripheral? And is this "thumbs up" supposed to be delivered via a notification, perhaps?

    Have you/they tried to debug the "bricked device" while this is happening? Does the log say anything?

    My suspicion is that the device is trying to send a notification, but this isn't possible since it isn't enabled by the central(client). But you/they would need to debug to see why the thumbs up is not working. Remember that we don't know anything about the logic in your application.

    Best regards,

    Edvin

  • This is a production device that a customer is having this issue with when trying to integrate into their software ecosystem, no debug available. Like I said, there's no hardware reset, and the device disappears.

    "My suspicion is that the device is trying to send a notification, but this isn't possible since it isn't enabled by the central(client)."

    Yes, exactly what's happening. I need to detect when this happens, and instead of sitting there waiting forever, I need to disconnect and advertise. I just can't find the notification that says the client hasn't responded or whatever to trigger the disconnect and advertise.

Reply
  • This is a production device that a customer is having this issue with when trying to integrate into their software ecosystem, no debug available. Like I said, there's no hardware reset, and the device disappears.

    "My suspicion is that the device is trying to send a notification, but this isn't possible since it isn't enabled by the central(client)."

    Yes, exactly what's happening. I need to detect when this happens, and instead of sitting there waiting forever, I need to disconnect and advertise. I just can't find the notification that says the client hasn't responded or whatever to trigger the disconnect and advertise.

Children
  • Oh, I see. Well. There is no event for something that never occurs in this case, unfortunately. Since it isn't mandatory to enable notifications, there will be no timeout from the SoftDevice.

    I suggest that you try to add an app_timer to the device that you start in the connected event. If the connected device enables notifications on this characteristic, stop the timer, but if the timer times out, you would disconnect from the connected device, and it should start advertising again.

    Does that seem like a viable solution?

    BR,

    Edvin

  • Seems like a reasonable idea. How would I see if the connected device enables notifications? I could also just check when the client requests that I send data (part of our application) and just deny the request if they don't have notifications enabled.

  • Looking at the ble_app_uart example, the BLE_GATTS_EVT_WRITE event in ble_nus.c is triggered whenever someone write something to a characteristic, including the CCCD (for enabling notifications). In SDK16, it looks like this:

    static void on_write(ble_nus_t * p_nus, ble_evt_t const * p_ble_evt)
    {
        ret_code_t                    err_code;
        ble_nus_evt_t                 evt;
        ble_nus_client_context_t    * p_client;
        ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
    
        err_code = blcm_link_ctx_get(p_nus->p_link_ctx_storage,
                                     p_ble_evt->evt.gatts_evt.conn_handle,
                                     (void *) &p_client);
        if (err_code != NRF_SUCCESS)
        {
            NRF_LOG_ERROR("Link context for 0x%02X connection handle could not be fetched.",
                          p_ble_evt->evt.gatts_evt.conn_handle);
        }
    
        memset(&evt, 0, sizeof(ble_nus_evt_t));
        evt.p_nus       = p_nus;
        evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
        evt.p_link_ctx  = p_client;
    
        if ((p_evt_write->handle == p_nus->tx_handles.cccd_handle) &&
            (p_evt_write->len == 2))
        {
        ...

    The last if-check in this snippet checks whether this event is caused by a notification change. If the value is 0x0001 then notifications are enabled.

    You can do this with a central that enables notifications to see what it looks like.

  • Alright, I'm totally lost... this sdk is so hard to follow for hardware guy like me.

    Right now my "on_write" function is:

    static void on_write(ble_ctcws_t * p_ctcws, ble_evt_t const * p_ble_evt)
    {
        ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
    
        //take care of a write to config
        if (p_evt_write->handle == p_ctcws->config_char_handles.value_handle)
        {
            p_ctcws->config_write_handler(p_ctcws, p_evt_write->data, p_evt_write->len);
        }
    
        //take care of a write to samples
        else if (p_evt_write->handle == p_ctcws->samples_char_handles.value_handle)
        {
            p_ctcws->samples_write_handler(p_ctcws, p_evt_write->data, p_evt_write->len);
        }
    
        //take care of a write to status
        else if (p_evt_write->handle == p_ctcws->status_char_handles.value_handle)
        {
            p_ctcws->status_write_handler(p_ctcws, p_evt_write->data, p_evt_write->len);
        }
    }

    One of those (status) is one that I need to check notifications on, would I just add the cccd stuff like so (line 22+):

    static void on_write(ble_ctcws_t * p_ctcws, ble_evt_t const * p_ble_evt)
    {
        ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
    
        //take care of a write to config
        if (p_evt_write->handle == p_ctcws->config_char_handles.value_handle)
        {
            p_ctcws->config_write_handler(p_ctcws, p_evt_write->data, p_evt_write->len);
        }
    
        //take care of a write to samples
        else if (p_evt_write->handle == p_ctcws->samples_char_handles.value_handle)
        {
            p_ctcws->samples_write_handler(p_ctcws, p_evt_write->data, p_evt_write->len);
        }
    
        //take care of a write to status
        else if (p_evt_write->handle == p_ctcws->status_char_handles.value_handle)
        {
            p_ctcws->status_write_handler(p_ctcws, p_evt_write->data, p_evt_write->len);
    
            if ((p_evt_write->handle == p_ctcws->status_char_handles.cccd_handle) && (p_evt_write->len == 2)){
                if (ble_srv_is_notification_enabled(p_evt_write->data)){
                //notifications are on
                }
                else{
                //notifications are off
                }
            }
    }

    The other characteristic doesn't have a write event because it's data is transmit only, I would just create a new write handle and all that for that one then implement similar to the status one above?

    Also, how does this status get back to the main program?.. this service thing is the one part of the code I never really got my head around... it all just looks like an enormous web of data structures and things I can't follow.

    Also, just thinking out loud, what happens if the notifications are on by default, will this ever get called? Is there a way I can just check notification status in my main.c? That would be way easier, if I could just check before sending a packet.

  • Do you have anyone with more software experience that can have a look?

    If you look at the ble_app_uart example, in the same event handler:

        if ((p_evt_write->handle == p_nus->tx_handles.cccd_handle) &&
            (p_evt_write->len == 2))
        {
            if (p_client != NULL)
            {
                if (ble_srv_is_notification_enabled(p_evt_write->data))
                {
                    p_client->is_notification_enabled = true;
                    evt.type                          = BLE_NUS_EVT_COMM_STARTED;
                }
                else
                {
                    p_client->is_notification_enabled = false;
                    evt.type                          = BLE_NUS_EVT_COMM_STOPPED;
                }
    
                if (p_nus->data_handler != NULL)
                {
                    p_nus->data_handler(&evt);
                }
    
            }

    The line:

    evt.type                          = BLE_NUS_EVT_COMM_STARTED;

    and 

    p_nus->data_handler(&evt); You don't see this event in the event handler in main.c, but that is only because it is not checked.

    If you add this event in the nus_data_handler() event handler in main.c you can see it:

    static void nus_data_handler(ble_nus_evt_t * p_evt)
    {
    
        if (p_evt->type == BLE_NUS_EVT_RX_DATA)
        {
            uint32_t err_code;
    
            NRF_LOG_DEBUG("Received data from BLE NUS. Writing data on UART.");
            NRF_LOG_HEXDUMP_DEBUG(p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
    
            for (uint32_t i = 0; i < p_evt->params.rx_data.length; i++)
            {
                do
                {
                    err_code = app_uart_put(p_evt->params.rx_data.p_data[i]);
                    if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_BUSY))
                    {
                        NRF_LOG_ERROR("Failed receiving NUS message. Error 0x%x. ", err_code);
                        APP_ERROR_CHECK(err_code);
                    }
                } while (err_code == NRF_ERROR_BUSY);
            }
            if (p_evt->params.rx_data.p_data[p_evt->params.rx_data.length - 1] == '\r')
            {
                while (app_uart_put('\n') == NRF_ERROR_BUSY);
            }
        }
        else if (p_evt->type == BLE_NUS_EVT_COMM_STARTED)
        {
            NRF_LOG_INFO("notifications started");
        }
    
    }

    will forward the event to the event handler that is passed on in ble_nus_init(). But in your custom project, I am not sure whether you have implemented this kind of event handler. But it is possible.

    This characteristic (the TX characteristic) is not a write characteristic either, but since it is possible to enable notifications on it, it is possible to write to the CCCD of the characteristic. This will appear as an BLE_GATTS_EVT_WRITE event. 

    I don't know anything about how your characteristic is set up. Are notifications supported?

    If you want a getting started guide, you can check out this github guide:

    https://github.com/edvinand/custom_ble_service_example

    However, I believe that you have already made a working application, so this guide may not suit very well. 

    This is at least how you can check whether the notifications are enabled, but how to forward this to main.c, is not Nordic SDK specific, but this is one way to do it in C.

    Best regards,

    Edvin

Related