Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

A Problem on Buttonless DFU + Bonding valid.

Currently, we are testing that 「buttonless DFU + Bonding valid」 in our App.

But After performing F / W Update with DFU, other BLE Service became invisible.(※2)

So, as a result of comparing the log of the Normal Project(No problem) with the log of the current problem project,

I found that there are the following differences.(In the normal case, the following events will occur.)

· PM_EVT_SERVICE_CHANGED_IND_CONFIRMED
· PM_EVT_PEER_DATA_UPDATE_SUCCEEDED

And Compared & checked "sdk_config.h", "makefile", "linkscript file", "source file", etc. But did not find a place likely to be a cause.

How can I solve this problem?

Why does the above log difference occur?

Please answer me about my question.

※ SDK Version

     nRF5_SDK_15.0.0_a53641a

※ IC

     nRF52832

※ Problem Project'SDK Config File

sdk_config.h

(※2)

<Before DFU Update>

      

<After DFU Update>

Parents
  • Hi Cerevo, 

     

    I suspect that the service changed characteristic was not included in either the application or the bootloader.

    I would suggest to turn off and on the iPhone and test again. 

     

    You mentioned  a "normal case" what was that ? Which firmware did you use when you have a normal case ? 

     

    Have you test and verify your new firmware actually has other services ? (test without bootloader, flash softdevice + application directly)

  • Hi Hung Bui,

    >I suspect that the service changed characteristic was not included in either the application or the bootloader.

    No, both Bootloader (DFU) and Application are defined and activated as follows.

    #define NRF_SDH_BLE_SERVICE_CHANGED 1

    > I would suggest to turn off and on the iPhone and test again.

    I tested it again, but it was the same result.

    > You mention a "normal case" what was that? Which firmware did you use when you have a normal case?

    I added Device Information Service based on "ble_app_buttonless_dfu" in example and tested it.

    This is OK, DIS (Device Information Service) is displayed even after F / W Update.

    > Have you test and verify your new firmware actually has other services ? (test without bootloader, flash softdevice + application directly)

    Yes, My Application includes "DIS", "Battery Service", and self-defined Service like the attached photo ("Before DFU Update").

    Of course, it is tested and there is no problem.

    So, you do not know why this problem happens after all?

  • Hi Chan, 

    I was suggesting turn off and on the phone to avoid attribute table caching, but maybe it's not the case.

    Please clarify, that you can actually do DFU update with our buttonless DFU. Meaning a new image has been sent to the device and run to 100% , device updated to the new firmware ? 

    If it's the case which firmware you use first and which you used later ? 

    Please aware that when you trigger a DFU update via buttonless application, the application first switch to the DFU bootloader. And in DFU bootloader you only have the DFU service as you see in the screenshot.

    You need to verify that the new application is running. Could you use another central device and check that after the update you actually have the new application running with all the services ? 

  • Hi Hung Bui,
    >「Please clarify, that you can actually do DFU update with our buttonless DFU. Meaning a new image has been sent to the device and run to 100%, device updated to the new firmware?」
    Yes, I could transfer 100% without problems.
    I also attach evidence log(※1).
    >「 If it's the case which firmware you use first and which you used later?」
    No, the newly burned F / W and Update F / W are exactly the same.

    >「Please aware that when you trigger a DFU update via buttonless application, the application first switch to the DFU bootloader. And in DFU bootloader you only have the DFU service as you see in the screenshot. 」
    Yes, I know.
    >「You need to verify that the new application is running. Could you use another central device and check that after the update you actually have the new application running with all the services ?」
    Yes, i confirmed that it works.
    Attach the evidence.(※2)  <----- Do you know the cause of this part?  Give me advice.
    (※1)
    <buttonless dfu update process log>
    <application_dfu_update_process_log>
    (※2)
    After deleting the attachment file LINE 443(「if (*peer_data.p_service_changed_pending)」), it was displayed successfully.
    /**
     * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA
     * 
     * All rights reserved.
     * 
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     * 
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     * 
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     * 
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     * 
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     * 
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     * 
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     * 
     */
    #include "sdk_common.h"
    #if NRF_MODULE_ENABLED(PEER_MANAGER)
    #include "gatt_cache_manager.h"
    
    #include "ble_gap.h"
    #include "ble_err.h"
    #include "ble_conn_state.h"
    #include "peer_manager_types.h"
    #include "peer_manager_internal.h"
    #include "id_manager.h"
    #include "gatts_cache_manager.h"
    #include "peer_database.h"
    #include "pm_mutex.h"
    
    
    // The number of registered event handlers.
    #define GCM_EVENT_HANDLERS_CNT      (sizeof(m_evt_handlers) / sizeof(m_evt_handlers[0]))
    
    // GATT Cache Manager event handler in Peer Manager.
    extern void pm_gcm_evt_handler(pm_evt_t * p_gcm_evt);
    
    // GATT Cache Manager events' handlers.
    // The number of elements in this array is GCM_EVENT_HANDLERS_CNT.
    static pm_evt_handler_internal_t m_evt_handlers[] =
    {
        pm_gcm_evt_handler
    };
    
    static bool                           m_module_initialized;
    static uint8_t                        m_db_update_in_progress_mutex;  /**< Mutex indicating whether a local DB write operation is ongoing. */
    static ble_conn_state_user_flag_id_t  m_flag_local_db_update_pending; /**< Flag ID for flag collection to keep track of which connections need a local DB update procedure. */
    static ble_conn_state_user_flag_id_t  m_flag_local_db_apply_pending;  /**< Flag ID for flag collection to keep track of which connections need a local DB apply procedure. */
    static ble_conn_state_user_flag_id_t  m_flag_service_changed_pending; /**< Flag ID for flag collection to keep track of which connections need to be sent a service changed indication. */
    static ble_conn_state_user_flag_id_t  m_flag_service_changed_sent;    /**< Flag ID for flag collection to keep track of which connections have been sent a service changed indication and are waiting for a handle value confirmation. */
    
    #ifdef PM_SERVICE_CHANGED_ENABLED
        STATIC_ASSERT(PM_SERVICE_CHANGED_ENABLED || !NRF_SDH_BLE_SERVICE_CHANGED,
                     "PM_SERVICE_CHANGED_ENABLED should be enabled if NRF_SDH_BLE_SERVICE_CHANGED is enabled.");
    #else
        #define PM_SERVICE_CHANGED_ENABLED 1
    #endif
    
    /**@brief Function for resetting the module variable(s) of the GSCM module.
     *
     * @param[out]  The instance to reset.
     */
    static void internal_state_reset()
    {
        m_module_initialized = false;
    }
    
    
    static void evt_send(pm_evt_t * p_gcm_evt)
    {
        p_gcm_evt->peer_id = im_peer_id_get_by_conn_handle(p_gcm_evt->conn_handle);
    
        for (uint32_t i = 0; i < GCM_EVENT_HANDLERS_CNT; i++)
        {
            m_evt_handlers[i](p_gcm_evt);
        }
    }
    
    
    /**@brief Function for checking a write event for whether a CCCD was written during the write
     *        operation.
     *
     * @param[in]  p_write_evt  The parameters of the write event.
     *
     * @return  Whether the write was on a CCCD.
     */
    static bool cccd_written(ble_gatts_evt_write_t const * p_write_evt)
    {
        return (    (p_write_evt->op        == BLE_GATTS_OP_WRITE_REQ)
                 && (p_write_evt->uuid.type == BLE_UUID_TYPE_BLE)
                 && (p_write_evt->uuid.uuid == BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG)
               );
    }
    
    
    /**@brief Function for sending an PM_EVT_ERROR_UNEXPECTED event.
     *
     * @param[in]  conn_handle  The connection handle the event pertains to.
     * @param[in]  err_code     The unexpected error that occurred.
     */
    static void send_unexpected_error(uint16_t conn_handle, ret_code_t err_code)
    {
        pm_evt_t error_evt =
        {
            .evt_id = PM_EVT_ERROR_UNEXPECTED,
            .conn_handle = conn_handle,
            .params =
            {
                .error_unexpected =
                {
                    .error = err_code,
                }
            }
        };
        evt_send(&error_evt);
    }
    
    
    /**@brief Function for performing the local DB update procedure in an event context, where no return
     *        code can be given.
     *
     * @details This function will do the procedure, and check the result, set a flag if needed, and
     *          send an event if needed.
     *
     * @param[in]  conn_handle  The connection to perform the procedure on.
     */
    static void local_db_apply_in_evt(uint16_t conn_handle)
    {
        bool set_procedure_as_pending = false;
        ret_code_t err_code;
        pm_evt_t event =
        {
            .conn_handle = conn_handle,
        };
    
        if (conn_handle == BLE_CONN_HANDLE_INVALID)
        {
            return;
        }
    
        err_code = gscm_local_db_cache_apply(conn_handle);
    
        switch (err_code)
        {
            case NRF_SUCCESS:
                event.evt_id = PM_EVT_LOCAL_DB_CACHE_APPLIED;
    
                evt_send(&event);
                break;
    
            case NRF_ERROR_BUSY:
                set_procedure_as_pending = true;
                break;
    
            case NRF_ERROR_INVALID_DATA:
                event.evt_id = PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED;
    
                evt_send(&event);
                break;
    
            case BLE_ERROR_INVALID_CONN_HANDLE:
                /* Do nothing */
                break;
    
            default:
                send_unexpected_error(conn_handle, err_code);
                break;
        }
    
        ble_conn_state_user_flag_set(conn_handle, m_flag_local_db_apply_pending, set_procedure_as_pending);
    }
    
    
    /**@brief Function for asynchronously starting a DB update procedure.
     *
     * @note This procedure can only be started asynchronously.
     *
     * @param[in]  conn_handle  The connection to perform the procedure on.
     * @param[in]  update       Whether to perform the procedure.
     */
    static __INLINE void local_db_update(uint16_t conn_handle, bool update)
    {
        ble_conn_state_user_flag_set(conn_handle, m_flag_local_db_update_pending, update);
    }
    
    
    /**@brief Function for performing the local DB update procedure in an event context, where no return
     *        code can be given.
     *
     * @details This function will do the procedure, and check the result, set a flag if needed, and
     *          send an event if needed.
     *
     * @param[in]  conn_handle  The connection to perform the procedure on.
     */
    static bool local_db_update_in_evt(uint16_t conn_handle)
    {
        bool set_procedure_as_pending = false;
        bool success = false;
        ret_code_t err_code = gscm_local_db_cache_update(conn_handle);
    
        switch (err_code)
        {
            case NRF_SUCCESS:
                success = true;
                break;
    
            case BLE_ERROR_INVALID_CONN_HANDLE:
                /* Do nothing */
                break;
    
            case NRF_ERROR_BUSY:
                set_procedure_as_pending = true;
                break;
    
            case NRF_ERROR_STORAGE_FULL:
            {
                pm_evt_t event =
                {
                    .evt_id      = PM_EVT_STORAGE_FULL,
                    .conn_handle = conn_handle,
                };
    
                evt_send(&event);
                break;
            }
    
            default:
                send_unexpected_error(conn_handle, err_code);
                break;
        }
    
        local_db_update(conn_handle, set_procedure_as_pending);
    
        return success;
    }
    
    #if PM_SERVICE_CHANGED_ENABLED
    /**@brief Function for sending a service changed indication in an event context, where no return
     *        code can be given.
     *
     * @details This function will do the procedure, and check the result, set a flag if needed, and
     *          send an event if needed.
     *
     * @param[in]  conn_handle  The connection to perform the procedure on.
     */
    static void service_changed_send_in_evt(uint16_t conn_handle)
    {
        bool sc_pending_state = true;
        bool sc_sent_state = false;
        ret_code_t err_code = gscm_service_changed_ind_send(conn_handle);
    
        switch (err_code)
        {
            case NRF_SUCCESS:
            {
                pm_evt_t event =
                {
                    .evt_id      = PM_EVT_SERVICE_CHANGED_IND_SENT,
                    .conn_handle = conn_handle,
                };
    
                sc_sent_state = true;
    
                evt_send(&event);
                break;
            }
    
            case NRF_ERROR_BUSY:
                // Do nothing.
                break;
    
            case NRF_ERROR_INVALID_STATE:
                // CCCDs not enabled. Drop indication.
                // Fallthrough.
    
            case NRF_ERROR_NOT_SUPPORTED:
                // Service changed not supported. Drop indication.
                sc_pending_state = false;
                gscm_db_change_notification_done(im_peer_id_get_by_conn_handle(conn_handle));
                break;
    
            case BLE_ERROR_GATTS_SYS_ATTR_MISSING:
                local_db_apply_in_evt(conn_handle);
                break;
    
            case BLE_ERROR_INVALID_CONN_HANDLE:
                // Do nothing.
                break;
    
            default:
                send_unexpected_error(conn_handle, err_code);
                break;
        }
    
        ble_conn_state_user_flag_set(conn_handle, m_flag_service_changed_pending, sc_pending_state);
        ble_conn_state_user_flag_set(conn_handle, m_flag_service_changed_sent, sc_sent_state);
    }
    #endif
    
    static void apply_pending_handle(uint16_t conn_handle, void * p_context)
    {
        UNUSED_PARAMETER(p_context);
        local_db_apply_in_evt(conn_handle);
    }
    
    
    static __INLINE void apply_pending_flags_check(void)
    {
        UNUSED_RETURN_VALUE(ble_conn_state_for_each_set_user_flag(m_flag_local_db_apply_pending,
                                                                  apply_pending_handle,
                                                                  NULL));
    }
    
    
    static void db_update_pending_handle(uint16_t conn_handle, void * p_context)
    {
        UNUSED_PARAMETER(p_context);
        if (pm_mutex_lock(&m_db_update_in_progress_mutex, 0))
        {
            if (local_db_update_in_evt(conn_handle))
            {
                // Successfully started writing to flash.
                return;
            }
            else
            {
                pm_mutex_unlock(&m_db_update_in_progress_mutex, 0);
            }
        }
    }
    
    
    static __INLINE void update_pending_flags_check(void)
    {
        UNUSED_RETURN_VALUE(ble_conn_state_for_each_set_user_flag(m_flag_local_db_update_pending,
                                                                  db_update_pending_handle,
                                                                  NULL));
    }
    
    
    #if PM_SERVICE_CHANGED_ENABLED
    static void sc_send_pending_handle(uint16_t conn_handle, void * p_context)
    {
        UNUSED_PARAMETER(p_context);
        if (!ble_conn_state_user_flag_get(conn_handle, m_flag_service_changed_sent))
        {
            service_changed_send_in_evt(conn_handle);
        }
    }
    
    
    static __INLINE void service_changed_pending_flags_check(void)
    {
        UNUSED_RETURN_VALUE(ble_conn_state_for_each_set_user_flag(m_flag_service_changed_pending,
                                                                  sc_send_pending_handle,
                                                                  NULL));
    }
    #endif
    
    
    /**@brief Callback function for events from the ID Manager module.
     *        This function is registered in the ID Manager module.
     *
     * @param[in]  p_event  The event from the ID Manager module.
     */
    void gcm_im_evt_handler(pm_evt_t * p_event)
    {
        switch (p_event->evt_id)
        {
            case PM_EVT_BONDED_PEER_CONNECTED:
                local_db_apply_in_evt(p_event->conn_handle);
    #if (PM_SERVICE_CHANGED_ENABLED == 1)
                if (gscm_service_changed_ind_needed(p_event->conn_handle))
                {
                    ble_conn_state_user_flag_set(p_event->conn_handle, m_flag_service_changed_pending, true);
                }
    #endif
                break;
            default:
                break;
        }
    }
    
    
    /**@brief Callback function for events from the Peer Database module.
     *        This handler is extern in Peer Database.
     *
     * @param[in]  p_event  The event from the Security Dispatcher module.
     */
    void gcm_pdb_evt_handler(pm_evt_t * p_event)
    {
        if (   p_event->evt_id == PM_EVT_PEER_DATA_UPDATE_SUCCEEDED
            && p_event->params.peer_data_update_succeeded.action == PM_PEER_DATA_OP_UPDATE)
        {
            switch (p_event->params.peer_data_update_succeeded.data_id)
            {
                case PM_PEER_DATA_ID_BONDING:
                {
                    uint16_t conn_handle = im_conn_handle_get(p_event->peer_id);
    
                    if (conn_handle != BLE_CONN_HANDLE_INVALID)
                    {
                        local_db_update(conn_handle, true);
                    }
                    break;
                }
    
    #if PM_SERVICE_CHANGED_ENABLED
                case PM_PEER_DATA_ID_SERVICE_CHANGED_PENDING:
                {
                    ret_code_t           err_code;
                    pm_peer_data_flash_t peer_data;
    
                    err_code = pdb_peer_data_ptr_get(p_event->peer_id,
                                                     PM_PEER_DATA_ID_SERVICE_CHANGED_PENDING,
                                                     &peer_data);
    
                    if (err_code == NRF_SUCCESS)
                    {
                        if (*peer_data.p_service_changed_pending)
                        {
                            uint16_t conn_handle = im_conn_handle_get(p_event->peer_id);
                            if (conn_handle != BLE_CONN_HANDLE_INVALID)
                            {
                                ble_conn_state_user_flag_set(conn_handle, m_flag_service_changed_pending, true);
                                service_changed_pending_flags_check();
                            }
                        }
                    }
                    break;
                }
    #endif
    
                case PM_PEER_DATA_ID_GATT_LOCAL:
                    pm_mutex_unlock(&m_db_update_in_progress_mutex, 0);
                    // Expecting a call to update_pending_flags_check() immediately.
                    break;
    
                default:
                    /* No action */
                    break;
            }
        }
    
        update_pending_flags_check();
    }
    
    
    ret_code_t gcm_init()
    {
        NRF_PM_DEBUG_CHECK(!m_module_initialized);
    
        internal_state_reset();
    
        m_flag_local_db_update_pending = ble_conn_state_user_flag_acquire();
        m_flag_local_db_apply_pending  = ble_conn_state_user_flag_acquire();
        m_flag_service_changed_pending = ble_conn_state_user_flag_acquire();
        m_flag_service_changed_sent    = ble_conn_state_user_flag_acquire();
    
        if  ((m_flag_local_db_update_pending  == BLE_CONN_STATE_USER_FLAG_INVALID)
          || (m_flag_local_db_apply_pending   == BLE_CONN_STATE_USER_FLAG_INVALID)
          || (m_flag_service_changed_pending  == BLE_CONN_STATE_USER_FLAG_INVALID)
          || (m_flag_service_changed_sent     == BLE_CONN_STATE_USER_FLAG_INVALID)
          )
        {
            return NRF_ERROR_INTERNAL;
        }
    
        pm_mutex_init(&m_db_update_in_progress_mutex, 1);
    
        m_module_initialized = true;
    
        return NRF_SUCCESS;
    }
    
    
    /**@brief Callback function for BLE events from the SoftDevice.
     *
     * @param[in]  p_ble_evt  The BLE event from the SoftDevice.
     */
    void gcm_ble_evt_handler(ble_evt_t const * p_ble_evt)
    {
        uint16_t conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GATTS_EVT_SYS_ATTR_MISSING:
                local_db_apply_in_evt(conn_handle);
                break;
    
    #if PM_SERVICE_CHANGED_ENABLED
            case BLE_GATTS_EVT_SC_CONFIRM:
            {
                pm_evt_t event =
                {
                    .evt_id      = PM_EVT_SERVICE_CHANGED_IND_CONFIRMED,
                    .peer_id     = im_peer_id_get_by_conn_handle(conn_handle),
                    .conn_handle = conn_handle,
                };
    
                gscm_db_change_notification_done(event.peer_id);
    
                ble_conn_state_user_flag_set(conn_handle, m_flag_service_changed_sent, false);
                ble_conn_state_user_flag_set(conn_handle, m_flag_service_changed_pending, false);
                evt_send(&event);
                break;
            }
    #endif
    
            case BLE_GATTS_EVT_WRITE:
                if (cccd_written(&p_ble_evt->evt.gatts_evt.params.write))
                {
                    local_db_update(conn_handle, true);
                    update_pending_flags_check();
                }
                break;
        }
    
        apply_pending_flags_check();
    #if PM_SERVICE_CHANGED_ENABLED
        service_changed_pending_flags_check();
    #endif
    }
    
    
    ret_code_t gcm_local_db_cache_update(uint16_t conn_handle)
    {
        NRF_PM_DEBUG_CHECK(m_module_initialized);
    
        local_db_update(conn_handle, true);
        update_pending_flags_check();
    
        return NRF_SUCCESS;
    }
    
    
    #if PM_SERVICE_CHANGED_ENABLED
    void gcm_local_database_has_changed(void)
    {
        gscm_local_database_has_changed();
    
        ble_conn_state_conn_handle_list_t conn_handles = ble_conn_state_conn_handles();
    
        for (uint16_t i = 0; i < conn_handles.len; i++)
        {
            if (im_peer_id_get_by_conn_handle(conn_handles.conn_handles[i]) == PM_PEER_ID_INVALID)
            {
                ble_conn_state_user_flag_set(conn_handles.conn_handles[i], m_flag_service_changed_pending, true);
            }
        }
    
        service_changed_pending_flags_check();
    }
    #endif
    #endif // NRF_MODULE_ENABLED(PEER_MANAGER)
    
Reply
  • Hi Hung Bui,
    >「Please clarify, that you can actually do DFU update with our buttonless DFU. Meaning a new image has been sent to the device and run to 100%, device updated to the new firmware?」
    Yes, I could transfer 100% without problems.
    I also attach evidence log(※1).
    >「 If it's the case which firmware you use first and which you used later?」
    No, the newly burned F / W and Update F / W are exactly the same.

    >「Please aware that when you trigger a DFU update via buttonless application, the application first switch to the DFU bootloader. And in DFU bootloader you only have the DFU service as you see in the screenshot. 」
    Yes, I know.
    >「You need to verify that the new application is running. Could you use another central device and check that after the update you actually have the new application running with all the services ?」
    Yes, i confirmed that it works.
    Attach the evidence.(※2)  <----- Do you know the cause of this part?  Give me advice.
    (※1)
    <buttonless dfu update process log>
    <application_dfu_update_process_log>
    (※2)
    After deleting the attachment file LINE 443(「if (*peer_data.p_service_changed_pending)」), it was displayed successfully.
    /**
     * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA
     * 
     * All rights reserved.
     * 
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     * 
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     * 
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     * 
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     * 
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     * 
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     * 
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     * 
     */
    #include "sdk_common.h"
    #if NRF_MODULE_ENABLED(PEER_MANAGER)
    #include "gatt_cache_manager.h"
    
    #include "ble_gap.h"
    #include "ble_err.h"
    #include "ble_conn_state.h"
    #include "peer_manager_types.h"
    #include "peer_manager_internal.h"
    #include "id_manager.h"
    #include "gatts_cache_manager.h"
    #include "peer_database.h"
    #include "pm_mutex.h"
    
    
    // The number of registered event handlers.
    #define GCM_EVENT_HANDLERS_CNT      (sizeof(m_evt_handlers) / sizeof(m_evt_handlers[0]))
    
    // GATT Cache Manager event handler in Peer Manager.
    extern void pm_gcm_evt_handler(pm_evt_t * p_gcm_evt);
    
    // GATT Cache Manager events' handlers.
    // The number of elements in this array is GCM_EVENT_HANDLERS_CNT.
    static pm_evt_handler_internal_t m_evt_handlers[] =
    {
        pm_gcm_evt_handler
    };
    
    static bool                           m_module_initialized;
    static uint8_t                        m_db_update_in_progress_mutex;  /**< Mutex indicating whether a local DB write operation is ongoing. */
    static ble_conn_state_user_flag_id_t  m_flag_local_db_update_pending; /**< Flag ID for flag collection to keep track of which connections need a local DB update procedure. */
    static ble_conn_state_user_flag_id_t  m_flag_local_db_apply_pending;  /**< Flag ID for flag collection to keep track of which connections need a local DB apply procedure. */
    static ble_conn_state_user_flag_id_t  m_flag_service_changed_pending; /**< Flag ID for flag collection to keep track of which connections need to be sent a service changed indication. */
    static ble_conn_state_user_flag_id_t  m_flag_service_changed_sent;    /**< Flag ID for flag collection to keep track of which connections have been sent a service changed indication and are waiting for a handle value confirmation. */
    
    #ifdef PM_SERVICE_CHANGED_ENABLED
        STATIC_ASSERT(PM_SERVICE_CHANGED_ENABLED || !NRF_SDH_BLE_SERVICE_CHANGED,
                     "PM_SERVICE_CHANGED_ENABLED should be enabled if NRF_SDH_BLE_SERVICE_CHANGED is enabled.");
    #else
        #define PM_SERVICE_CHANGED_ENABLED 1
    #endif
    
    /**@brief Function for resetting the module variable(s) of the GSCM module.
     *
     * @param[out]  The instance to reset.
     */
    static void internal_state_reset()
    {
        m_module_initialized = false;
    }
    
    
    static void evt_send(pm_evt_t * p_gcm_evt)
    {
        p_gcm_evt->peer_id = im_peer_id_get_by_conn_handle(p_gcm_evt->conn_handle);
    
        for (uint32_t i = 0; i < GCM_EVENT_HANDLERS_CNT; i++)
        {
            m_evt_handlers[i](p_gcm_evt);
        }
    }
    
    
    /**@brief Function for checking a write event for whether a CCCD was written during the write
     *        operation.
     *
     * @param[in]  p_write_evt  The parameters of the write event.
     *
     * @return  Whether the write was on a CCCD.
     */
    static bool cccd_written(ble_gatts_evt_write_t const * p_write_evt)
    {
        return (    (p_write_evt->op        == BLE_GATTS_OP_WRITE_REQ)
                 && (p_write_evt->uuid.type == BLE_UUID_TYPE_BLE)
                 && (p_write_evt->uuid.uuid == BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG)
               );
    }
    
    
    /**@brief Function for sending an PM_EVT_ERROR_UNEXPECTED event.
     *
     * @param[in]  conn_handle  The connection handle the event pertains to.
     * @param[in]  err_code     The unexpected error that occurred.
     */
    static void send_unexpected_error(uint16_t conn_handle, ret_code_t err_code)
    {
        pm_evt_t error_evt =
        {
            .evt_id = PM_EVT_ERROR_UNEXPECTED,
            .conn_handle = conn_handle,
            .params =
            {
                .error_unexpected =
                {
                    .error = err_code,
                }
            }
        };
        evt_send(&error_evt);
    }
    
    
    /**@brief Function for performing the local DB update procedure in an event context, where no return
     *        code can be given.
     *
     * @details This function will do the procedure, and check the result, set a flag if needed, and
     *          send an event if needed.
     *
     * @param[in]  conn_handle  The connection to perform the procedure on.
     */
    static void local_db_apply_in_evt(uint16_t conn_handle)
    {
        bool set_procedure_as_pending = false;
        ret_code_t err_code;
        pm_evt_t event =
        {
            .conn_handle = conn_handle,
        };
    
        if (conn_handle == BLE_CONN_HANDLE_INVALID)
        {
            return;
        }
    
        err_code = gscm_local_db_cache_apply(conn_handle);
    
        switch (err_code)
        {
            case NRF_SUCCESS:
                event.evt_id = PM_EVT_LOCAL_DB_CACHE_APPLIED;
    
                evt_send(&event);
                break;
    
            case NRF_ERROR_BUSY:
                set_procedure_as_pending = true;
                break;
    
            case NRF_ERROR_INVALID_DATA:
                event.evt_id = PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED;
    
                evt_send(&event);
                break;
    
            case BLE_ERROR_INVALID_CONN_HANDLE:
                /* Do nothing */
                break;
    
            default:
                send_unexpected_error(conn_handle, err_code);
                break;
        }
    
        ble_conn_state_user_flag_set(conn_handle, m_flag_local_db_apply_pending, set_procedure_as_pending);
    }
    
    
    /**@brief Function for asynchronously starting a DB update procedure.
     *
     * @note This procedure can only be started asynchronously.
     *
     * @param[in]  conn_handle  The connection to perform the procedure on.
     * @param[in]  update       Whether to perform the procedure.
     */
    static __INLINE void local_db_update(uint16_t conn_handle, bool update)
    {
        ble_conn_state_user_flag_set(conn_handle, m_flag_local_db_update_pending, update);
    }
    
    
    /**@brief Function for performing the local DB update procedure in an event context, where no return
     *        code can be given.
     *
     * @details This function will do the procedure, and check the result, set a flag if needed, and
     *          send an event if needed.
     *
     * @param[in]  conn_handle  The connection to perform the procedure on.
     */
    static bool local_db_update_in_evt(uint16_t conn_handle)
    {
        bool set_procedure_as_pending = false;
        bool success = false;
        ret_code_t err_code = gscm_local_db_cache_update(conn_handle);
    
        switch (err_code)
        {
            case NRF_SUCCESS:
                success = true;
                break;
    
            case BLE_ERROR_INVALID_CONN_HANDLE:
                /* Do nothing */
                break;
    
            case NRF_ERROR_BUSY:
                set_procedure_as_pending = true;
                break;
    
            case NRF_ERROR_STORAGE_FULL:
            {
                pm_evt_t event =
                {
                    .evt_id      = PM_EVT_STORAGE_FULL,
                    .conn_handle = conn_handle,
                };
    
                evt_send(&event);
                break;
            }
    
            default:
                send_unexpected_error(conn_handle, err_code);
                break;
        }
    
        local_db_update(conn_handle, set_procedure_as_pending);
    
        return success;
    }
    
    #if PM_SERVICE_CHANGED_ENABLED
    /**@brief Function for sending a service changed indication in an event context, where no return
     *        code can be given.
     *
     * @details This function will do the procedure, and check the result, set a flag if needed, and
     *          send an event if needed.
     *
     * @param[in]  conn_handle  The connection to perform the procedure on.
     */
    static void service_changed_send_in_evt(uint16_t conn_handle)
    {
        bool sc_pending_state = true;
        bool sc_sent_state = false;
        ret_code_t err_code = gscm_service_changed_ind_send(conn_handle);
    
        switch (err_code)
        {
            case NRF_SUCCESS:
            {
                pm_evt_t event =
                {
                    .evt_id      = PM_EVT_SERVICE_CHANGED_IND_SENT,
                    .conn_handle = conn_handle,
                };
    
                sc_sent_state = true;
    
                evt_send(&event);
                break;
            }
    
            case NRF_ERROR_BUSY:
                // Do nothing.
                break;
    
            case NRF_ERROR_INVALID_STATE:
                // CCCDs not enabled. Drop indication.
                // Fallthrough.
    
            case NRF_ERROR_NOT_SUPPORTED:
                // Service changed not supported. Drop indication.
                sc_pending_state = false;
                gscm_db_change_notification_done(im_peer_id_get_by_conn_handle(conn_handle));
                break;
    
            case BLE_ERROR_GATTS_SYS_ATTR_MISSING:
                local_db_apply_in_evt(conn_handle);
                break;
    
            case BLE_ERROR_INVALID_CONN_HANDLE:
                // Do nothing.
                break;
    
            default:
                send_unexpected_error(conn_handle, err_code);
                break;
        }
    
        ble_conn_state_user_flag_set(conn_handle, m_flag_service_changed_pending, sc_pending_state);
        ble_conn_state_user_flag_set(conn_handle, m_flag_service_changed_sent, sc_sent_state);
    }
    #endif
    
    static void apply_pending_handle(uint16_t conn_handle, void * p_context)
    {
        UNUSED_PARAMETER(p_context);
        local_db_apply_in_evt(conn_handle);
    }
    
    
    static __INLINE void apply_pending_flags_check(void)
    {
        UNUSED_RETURN_VALUE(ble_conn_state_for_each_set_user_flag(m_flag_local_db_apply_pending,
                                                                  apply_pending_handle,
                                                                  NULL));
    }
    
    
    static void db_update_pending_handle(uint16_t conn_handle, void * p_context)
    {
        UNUSED_PARAMETER(p_context);
        if (pm_mutex_lock(&m_db_update_in_progress_mutex, 0))
        {
            if (local_db_update_in_evt(conn_handle))
            {
                // Successfully started writing to flash.
                return;
            }
            else
            {
                pm_mutex_unlock(&m_db_update_in_progress_mutex, 0);
            }
        }
    }
    
    
    static __INLINE void update_pending_flags_check(void)
    {
        UNUSED_RETURN_VALUE(ble_conn_state_for_each_set_user_flag(m_flag_local_db_update_pending,
                                                                  db_update_pending_handle,
                                                                  NULL));
    }
    
    
    #if PM_SERVICE_CHANGED_ENABLED
    static void sc_send_pending_handle(uint16_t conn_handle, void * p_context)
    {
        UNUSED_PARAMETER(p_context);
        if (!ble_conn_state_user_flag_get(conn_handle, m_flag_service_changed_sent))
        {
            service_changed_send_in_evt(conn_handle);
        }
    }
    
    
    static __INLINE void service_changed_pending_flags_check(void)
    {
        UNUSED_RETURN_VALUE(ble_conn_state_for_each_set_user_flag(m_flag_service_changed_pending,
                                                                  sc_send_pending_handle,
                                                                  NULL));
    }
    #endif
    
    
    /**@brief Callback function for events from the ID Manager module.
     *        This function is registered in the ID Manager module.
     *
     * @param[in]  p_event  The event from the ID Manager module.
     */
    void gcm_im_evt_handler(pm_evt_t * p_event)
    {
        switch (p_event->evt_id)
        {
            case PM_EVT_BONDED_PEER_CONNECTED:
                local_db_apply_in_evt(p_event->conn_handle);
    #if (PM_SERVICE_CHANGED_ENABLED == 1)
                if (gscm_service_changed_ind_needed(p_event->conn_handle))
                {
                    ble_conn_state_user_flag_set(p_event->conn_handle, m_flag_service_changed_pending, true);
                }
    #endif
                break;
            default:
                break;
        }
    }
    
    
    /**@brief Callback function for events from the Peer Database module.
     *        This handler is extern in Peer Database.
     *
     * @param[in]  p_event  The event from the Security Dispatcher module.
     */
    void gcm_pdb_evt_handler(pm_evt_t * p_event)
    {
        if (   p_event->evt_id == PM_EVT_PEER_DATA_UPDATE_SUCCEEDED
            && p_event->params.peer_data_update_succeeded.action == PM_PEER_DATA_OP_UPDATE)
        {
            switch (p_event->params.peer_data_update_succeeded.data_id)
            {
                case PM_PEER_DATA_ID_BONDING:
                {
                    uint16_t conn_handle = im_conn_handle_get(p_event->peer_id);
    
                    if (conn_handle != BLE_CONN_HANDLE_INVALID)
                    {
                        local_db_update(conn_handle, true);
                    }
                    break;
                }
    
    #if PM_SERVICE_CHANGED_ENABLED
                case PM_PEER_DATA_ID_SERVICE_CHANGED_PENDING:
                {
                    ret_code_t           err_code;
                    pm_peer_data_flash_t peer_data;
    
                    err_code = pdb_peer_data_ptr_get(p_event->peer_id,
                                                     PM_PEER_DATA_ID_SERVICE_CHANGED_PENDING,
                                                     &peer_data);
    
                    if (err_code == NRF_SUCCESS)
                    {
                        if (*peer_data.p_service_changed_pending)
                        {
                            uint16_t conn_handle = im_conn_handle_get(p_event->peer_id);
                            if (conn_handle != BLE_CONN_HANDLE_INVALID)
                            {
                                ble_conn_state_user_flag_set(conn_handle, m_flag_service_changed_pending, true);
                                service_changed_pending_flags_check();
                            }
                        }
                    }
                    break;
                }
    #endif
    
                case PM_PEER_DATA_ID_GATT_LOCAL:
                    pm_mutex_unlock(&m_db_update_in_progress_mutex, 0);
                    // Expecting a call to update_pending_flags_check() immediately.
                    break;
    
                default:
                    /* No action */
                    break;
            }
        }
    
        update_pending_flags_check();
    }
    
    
    ret_code_t gcm_init()
    {
        NRF_PM_DEBUG_CHECK(!m_module_initialized);
    
        internal_state_reset();
    
        m_flag_local_db_update_pending = ble_conn_state_user_flag_acquire();
        m_flag_local_db_apply_pending  = ble_conn_state_user_flag_acquire();
        m_flag_service_changed_pending = ble_conn_state_user_flag_acquire();
        m_flag_service_changed_sent    = ble_conn_state_user_flag_acquire();
    
        if  ((m_flag_local_db_update_pending  == BLE_CONN_STATE_USER_FLAG_INVALID)
          || (m_flag_local_db_apply_pending   == BLE_CONN_STATE_USER_FLAG_INVALID)
          || (m_flag_service_changed_pending  == BLE_CONN_STATE_USER_FLAG_INVALID)
          || (m_flag_service_changed_sent     == BLE_CONN_STATE_USER_FLAG_INVALID)
          )
        {
            return NRF_ERROR_INTERNAL;
        }
    
        pm_mutex_init(&m_db_update_in_progress_mutex, 1);
    
        m_module_initialized = true;
    
        return NRF_SUCCESS;
    }
    
    
    /**@brief Callback function for BLE events from the SoftDevice.
     *
     * @param[in]  p_ble_evt  The BLE event from the SoftDevice.
     */
    void gcm_ble_evt_handler(ble_evt_t const * p_ble_evt)
    {
        uint16_t conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GATTS_EVT_SYS_ATTR_MISSING:
                local_db_apply_in_evt(conn_handle);
                break;
    
    #if PM_SERVICE_CHANGED_ENABLED
            case BLE_GATTS_EVT_SC_CONFIRM:
            {
                pm_evt_t event =
                {
                    .evt_id      = PM_EVT_SERVICE_CHANGED_IND_CONFIRMED,
                    .peer_id     = im_peer_id_get_by_conn_handle(conn_handle),
                    .conn_handle = conn_handle,
                };
    
                gscm_db_change_notification_done(event.peer_id);
    
                ble_conn_state_user_flag_set(conn_handle, m_flag_service_changed_sent, false);
                ble_conn_state_user_flag_set(conn_handle, m_flag_service_changed_pending, false);
                evt_send(&event);
                break;
            }
    #endif
    
            case BLE_GATTS_EVT_WRITE:
                if (cccd_written(&p_ble_evt->evt.gatts_evt.params.write))
                {
                    local_db_update(conn_handle, true);
                    update_pending_flags_check();
                }
                break;
        }
    
        apply_pending_flags_check();
    #if PM_SERVICE_CHANGED_ENABLED
        service_changed_pending_flags_check();
    #endif
    }
    
    
    ret_code_t gcm_local_db_cache_update(uint16_t conn_handle)
    {
        NRF_PM_DEBUG_CHECK(m_module_initialized);
    
        local_db_update(conn_handle, true);
        update_pending_flags_check();
    
        return NRF_SUCCESS;
    }
    
    
    #if PM_SERVICE_CHANGED_ENABLED
    void gcm_local_database_has_changed(void)
    {
        gscm_local_database_has_changed();
    
        ble_conn_state_conn_handle_list_t conn_handles = ble_conn_state_conn_handles();
    
        for (uint16_t i = 0; i < conn_handles.len; i++)
        {
            if (im_peer_id_get_by_conn_handle(conn_handles.conn_handles[i]) == PM_PEER_ID_INVALID)
            {
                ble_conn_state_user_flag_set(conn_handles.conn_handles[i], m_flag_service_changed_pending, true);
            }
        }
    
        service_changed_pending_flags_check();
    }
    #endif
    #endif // NRF_MODULE_ENABLED(PEER_MANAGER)
    
Children
  • Did you do bonding  in your application and in your DFU bootloader ? If you do bonding, have you set NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS = 1  ? and use ble_dfu_bonded.c in your application code ?

    The line you commented out checking if the service changed flag has been set or not. If it has been set, gatt_cache_manager.c will send a service changed indication to the iPhone and the iPhone will do a service discovery. By removing that check, you always request the phone to do service discovery, resulting in the full att table read by iPhone. 

    To get the flag set, you would need to call gscm_local_database_has_changed(). This is done inside ble_dfu_buttonless_bootloader_start_prepare(), if you set the NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS  flag correctly, it should be called before you switch to the bootloader. 

    Note that you need to make sure the flash is being updated properly before you switch to bootloader. 

  • ・「Did you do bonding  in your application and in your DFU bootloader ? If you do bonding, have you set NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS = 1  ? and use ble_dfu_bonded.c in your application code ?」

    Yes, of cource.No Problem.

    For Application,

    #define BLE_DFU_ENABLED 1

    #define NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS 1

    #define NRF_SDH_BLE_SERVICE_CHANGED 1

    I am defining it.

    In the case of DFU,

    #define NRF_BL_DFU_ENTER_METHOD_BUTTONLESS 1

    #define NRF_DFU_BLE_REQUIRES_BONDS 1

    #define NRF_SDH_BLE_SERVICE_CHANGED 1

    I am defining it.

    ・「The line you commented out checking if the service changed flag has been set or not. If it has been set, gatt_cache_manager.c will send a service changed indication to the iPhone and the iPhone will do a service discovery. By removing that check, you always request the phone to do service discovery, resulting in the full att table read by iPhone. 

    To get the flag set, you would need to call gscm_local_database_has_changed(). This is done inside ble_dfu_buttonless_bootloader_start_prepare(), if you set the NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS  flag correctly, it should be called before you switch to the bootloader. 」

    Yes, I already understand the above content.

    I also confirmed the contents of "ble_dfu_buttonless_bootloader_start_prepare ()".

    ・「Note that you need to make sure the flash is being updated properly before you switch to bootloader. 

    Yes, I do not think that writing to Flash eventually went well.
    However, as in the log of (★) below, Write to Flash was also Success.

    In the end, despite successfully writing to Flash correctly,
    Why does this problem occur?

    ========================LOG==================================
    0> <info> app: Writing peer data to the bootloader...
    0> <info> app: system attribute table len: 8
    0> <info> app: In ble_dfu_buttonless_bootloader_start_prepare
    0> <info> app: Device is preparing to enter bootloader mode.
    0> <info> app: Disconnected peer
    0> <info> app: peers_count 1
    0> <info> app: peer id 0
    0> <info> app: PM_EVT_PEER_DATA_UPDATE_SUCCEEDED
    0> <info> app: Updating Service Changed indication for peers, 0 left
    0> <info> app: Finished updating Service Changed indication for peers     <------★
    0> <info> app: Device will enter bootloader mode.
    0> <info> app: Start....
    0> <info> app: gpiote_init
    0> <info> app: button_init
    0> <info> app: initADPD153GGRI

  • You may need to dig deeper, and check on the first time booting up,after a DFU, would the new firmware be able to read the peer information data and get p_service_changed_pending = 1  ? or not. Maybe you put a break point in right after this printed out: Finished updating Service Changed indication for peers  , and check in flash if the data is written correctly. (you can simply dump the flash content to a hex and compare 2 hexes, before and after you trigger Start DFU) 

    You mentioned that you didn't have trouble when testing with the buttonless example ? 

    I assume you don't change attribute table or anything, simply DFU update the same firmware ?

     

  • Hi Hung Bui.

    ・「You may need to dig deeper, and check on the first time booting up,after a DFU, would the new firmware be able to read the peer information data and get p_service_changed_pending = 1  ? or not. 

    -->I Checked. it was p_service_changed_pending  = 0. 

    ・「Maybe you put a break point in right after this printed out: Finished updating Service Changed indication for peers  , and check in flash if the data is written correctly. (you can simply dump the flash content to a hex and compare 2 hexes, before and after you trigger Start DFU) 

    --> I did. But When comparing HEX Dump,Because i do not know the storage address (because it saved the data with pdb_raw_store), I could not identify the location of p_service_changed_pending.

    ・「I assume you don't change attribute table

    --> Thank you. I looked at this part. 

    After all, the cause was the definition of "NRF_SDH_BLE_GATT_MAX_MTU_SIZE".

    To increase the transfer speed, set the MTU size to I changed it from 23 to 247.

    When I restored it(247 -> 23),
    · PM_EVT_SERVICE_CHANGED_IND_CONFIRMED
    · PM_EVT_PEER_DATA_UPDATE_SUCCEEDED
    An event occurred. it's OK.

    By the way, I have the following questions so please check.
    1) First of all, why does this part cause this problem phenomenon?

    2) Next, in order to increase the transfer speed, I would like to maintain the MTU size as 247,
    How can I deal with it?

  • Hi Chan, 

     

    Thanks for updating us. 

    Could you confirm if you modify the buttonless dfu example in the SDK to have MTU size to 247, you see the same problem ? 

    If you can check that, we can reproduce the issue here and find out what could be the wrong. Which phone did you use ? 

Related