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

  • 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

  • So I can't get that to work, no other coder here, just me. I'm a pretty competent C coder but this SDK is so hard to follow (very common complaint around the internet.) I was going to implement a time out, since that's pretty easy... if the program gets stuck, force a timeout and go back to advertising. However, when I hit this snag it seems to be disabling all other interrupts. My SPIM gets hung up waiting for an event end interrupt, and my RTC interrupt to reset everything doesn't trigger.

    I set my RTC interrupt to 0 to try and over ride whatever the SD is doing. I'm totally lost now... I get that some SD events are priority 0, but I don't know how/why writing to a characteristic that doesn't have notifications set on the other side would trigger a priority 0 interrupt.

    Any thoughts? I'm beating my head against the wall here.

  • Adam Gerken said:
    I set my RTC interrupt to 0 to try and over ride whatever the SD is doing. I'm totally lost now... I get that some SD events are priority 0, but I don't know how/why writing to a characteristic that doesn't have notifications set on the other side would trigger a priority 0 interrupt.

     Are you talking about the interrupt priorities now? If so, don't change them. It shouldn't be needed in this case. Setting the RTC interrupt IRQ priority to 0 will damage the softdevice functionality.

    Set the RTC priority back to 6.

    Are you able to see from your application when the notification is enabled? If yes, I suggest you look into using the app_timer as your timer to check whether notifications are enabled. Check out the ble_app_hrs example, which uses an app_timer to generate simulated battery measurements.

    Best regards,

    Edvin

  • Yes, I'm talking about interrupts. When my application sends data to a client that doesn't have notifications enabled, the program hangs up and no interrupts work any more. I'm trying to use a timer to reset the connection and go back to advertising when this happens, but it doesn't work because interrupts don't work. No interrupts work when this happens.... My SPI hangs up, Timer hangs up, RTC hangs up, everything.

    No, I can not see in my application when notifications are enabled. That's exactly what I said I can't get to work.

    How does a timer help me check for an event when notifications are enabled? I'm not following you at all here, I think we're talking about very different things. Are you suggesting using a timer to poll the notification status? I asked a while ago if I can just check the status of notifications, because then I can just do that before trying to transmit on that characteristic, and just not transmit if notifications aren't enabled. That would be the ideal situation, but I thought you said you can't query the notification status.

    The basic flow of the program:

    Server = Nordic device, Client = PC

    • Server advertises
    • Client connects
    • Client requests x number of data samples
    • Server collects samples
    • Server transmits samples 

    It's the last step that's getting hung up if the client doesn't have notifications enabled. The Server needs to transmit a large amount of data but just locks up.

Related