Hello everybody,
i currently develop two devices, one master and one slave (sensor). Both devices have the dfu service completly working.
My problem is that the master should trigger the slave to enter bootloader so that the app could load the new firmware.
So i wrote the ble_dfu_c component to enable indication and enter bootloader.
The slave is responding with 0x20 0x01 0x01, which is the same response doing it over the nrf connect app (over the nrf connect app everything is working).
Is there something i forgot?
NRF52832
SDK: 15.2.0 - S132
/** * Device Firmware Update Service Client */ #include "sdk_common.h" #if NRF_MODULE_ENABLED(BLE_DFU_C) #include "ble_dfu_c.h" #include <string.h> #define NRF_LOG_MODULE_NAME ble_dfu_c #include "nrf_log.h" NRF_LOG_MODULE_REGISTER(); #define MAX_CTRL_POINT_RESP_PARAM_LEN 3 /**< Max length of the responses. */ #define BLE_DFU_SERVICE_UUID 0xFE59 /**< The 16-bit UUID of the Secure DFU Service. */ /**@brief Function that is called if no event handler is provided. */ static void dummy_evt_handler(ble_dfu_c_t * p_ble_dfu_c, ble_dfu_c_evt_t const * p_evt) { NRF_LOG_DEBUG("Dummy event handler received event 0x%x", p_evt->evt_type); } #define TX_BUFFER_MASK 0x07 /**< TX Buffer mask, must be a mask of continuous zeroes, followed by continuous sequence of ones: 000...111. */ #define TX_BUFFER_SIZE (TX_BUFFER_MASK + 1) /**< Size of send buffer, which is 1 higher than the mask. */ #define WRITE_MESSAGE_LENGTH 8 /**< Length of the write message for CCCD. */ typedef enum { READ_REQ, /**< Type identifying that this tx_message is a read request. */ WRITE_REQ /**< Type identifying that this tx_message is a write request. */ } tx_request_t; /**@brief Structure for writing a message to the peer, i.e. CCCD. */ typedef struct { uint8_t gattc_value[WRITE_MESSAGE_LENGTH]; /**< The message to write. */ ble_gattc_write_params_t gattc_params; /**< GATTC parameters for this message. */ } write_params_t; /**@brief Structure for holding data to be transmitted to the connected central. */ typedef struct { uint16_t conn_handle; /**< Connection handle to be used when transmitting this message. */ tx_request_t type; /**< Type of this message, i.e. read or write message. */ union { uint16_t read_handle; /**< Read request message. */ write_params_t write_req; /**< Write request message. */ } req; } tx_message_t; static tx_message_t m_tx_buffer[TX_BUFFER_SIZE]; /**< Transmit buffer for messages to be transmitted to the central. */ static uint32_t m_tx_insert_index = 0; /**< Current index in the transmit buffer where the next message should be inserted. */ static uint32_t m_tx_index = 0; /**< Current index in the transmit buffer from where the next message to be transmitted resides. */ /**@brief Function for passing any pending request from the buffer to the stack. */ static uint32_t tx_buffer_process(void) { if (m_tx_index != m_tx_insert_index) { uint32_t err_code; if (m_tx_buffer[m_tx_index].type == READ_REQ) { err_code = sd_ble_gattc_read(m_tx_buffer[m_tx_index].conn_handle, m_tx_buffer[m_tx_index].req.read_handle, 0); } else { err_code = sd_ble_gattc_write(m_tx_buffer[m_tx_index].conn_handle, &m_tx_buffer[m_tx_index].req.write_req.gattc_params); } if (err_code != NRF_SUCCESS) { NRF_LOG_DEBUG("Error: Msg[%d] will be repeated..", m_tx_index); } return err_code; } return NRF_SUCCESS; } /**@brief Function for handling write response events. * * @param[in] p_ble_hrs_c Pointer to the Heart Rate Client structure. * @param[in] p_ble_evt Pointer to the BLE event received. */ static void on_write_rsp(ble_dfu_c_t * p_ble_dfu_c, const ble_evt_t * p_ble_evt) { // Check if the event if on the link for this instance if (p_ble_dfu_c->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle) { return; } if ((p_ble_evt->evt.gattc_evt.gatt_status == BLE_GATT_STATUS_ATTERR_INSUF_AUTHENTICATION) || (p_ble_evt->evt.gattc_evt.gatt_status == BLE_GATT_STATUS_ATTERR_INSUF_ENCRYPTION)) { // Do nothing to reattempt write. } else { if (p_ble_evt->evt.gattc_evt.params.write_rsp.handle == p_ble_dfu_c->handles.chr || p_ble_evt->evt.gattc_evt.params.write_rsp.handle == p_ble_dfu_c->handles.cccd) { m_tx_index++; m_tx_index &= TX_BUFFER_MASK; } } // Check if there is any message to be sent across to the peer and send it. tx_buffer_process(); } /**@brief Disconnect event handler. * * @param[in] p_ble_evt Event received from the BLE stack. */ static void on_disconnect(ble_dfu_c_t * p_ble_dfu_c, ble_evt_t const * p_ble_evt) { if (p_ble_dfu_c->conn_handle != p_ble_evt->evt.gap_evt.conn_handle) { return; } if (p_ble_dfu_c->evt_handler != NULL) { ble_dfu_c_evt_t dfu_c_evt; dfu_c_evt.conn_handle = p_ble_dfu_c->conn_handle; dfu_c_evt.evt_type = BLE_DFU_C_EVT_DISCONNECTED; p_ble_dfu_c->evt_handler(p_ble_dfu_c, &dfu_c_evt); } p_ble_dfu_c->conn_handle = BLE_CONN_HANDLE_INVALID; p_ble_dfu_c->indication_enabled = false; } /**@brief Function for handling Handle Value Notification received from the SoftDevice. * * @details This function will uses the Handle Value Notification received from the SoftDevice * and checks if it is a notification of the NUS TX characteristic from the peer. If * it is, this function will decode the data and send it to the * application. * * @param[in] p_ble_nus_c Pointer to the NUS Client structure. * @param[in] p_ble_evt Pointer to the BLE event received. */ static void on_hvx(ble_dfu_c_t * p_ble_dfu_c, ble_evt_t const * p_ble_evt) { // HVX can only occur from client sending. if ((p_ble_dfu_c->handles.chr != BLE_GATT_HANDLE_INVALID) && (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_dfu_c->handles.chr) && (p_ble_dfu_c->evt_handler != NULL)) { NRF_LOG_ERROR("on HVX: %d", p_ble_evt->evt.gattc_evt.params.hvx.handle); NRF_LOG_HEXDUMP_DEBUG(p_ble_evt->evt.gattc_evt.params.hvx.data, p_ble_evt->evt.gattc_evt.params.hvx.len); ble_dfu_c_evt_t dfu_c_evt; dfu_c_evt.conn_handle = p_ble_dfu_c->conn_handle; dfu_c_evt.evt_type = BLE_DFU_C_EVT_BOOTLOADER_STARTING; p_ble_dfu_c->evt_handler(p_ble_dfu_c, &dfu_c_evt); NRF_LOG_DEBUG("DFU C bootloader starting?"); } } void ble_dfu_c_on_db_disc_evt(ble_dfu_c_t * p_ble_dfu_c, ble_db_discovery_evt_t * p_evt) { ble_dfu_c_evt_t dfu_c_evt; memset(&dfu_c_evt, 0, sizeof(ble_dfu_c_evt_t)); ble_gatt_db_char_t * p_chars = p_evt->params.discovered_db.charateristics; // Check if the DFU was discovered. if ((p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE) && (p_evt->params.discovered_db.srv_uuid.uuid == BLE_DFU_SERVICE_UUID) && (p_evt->params.discovered_db.srv_uuid.type == p_ble_dfu_c->uuid_type)) { for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++) { switch (p_chars[i].characteristic.uuid.uuid) { case BLE_DFU_C_CHAR_UUID: dfu_c_evt.handles.chr = p_chars[i].characteristic.handle_value; dfu_c_evt.handles.cccd = p_chars[i].cccd_handle; break; default: break; } } if (p_ble_dfu_c->evt_handler != NULL) { dfu_c_evt.conn_handle = p_evt->conn_handle; dfu_c_evt.evt_type = BLE_DFU_C_EVT_DISCOVERY_COMPLETE; p_ble_dfu_c->evt_handler(p_ble_dfu_c, &dfu_c_evt); } } } void ble_dfu_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context) { ble_dfu_c_t * p_ble_dfu_c = (ble_dfu_c_t *)p_context; if ((p_ble_dfu_c == NULL) || (p_ble_evt == NULL)) { return; } if ((p_ble_dfu_c->conn_handle != BLE_CONN_HANDLE_INVALID) && (p_ble_dfu_c->conn_handle != p_ble_evt->evt.gap_evt.conn_handle)) { return; } switch (p_ble_evt->header.evt_id) { case BLE_GATTC_EVT_HVX: on_hvx(p_ble_dfu_c, p_ble_evt); break; case BLE_GATTC_EVT_WRITE_RSP: on_write_rsp(p_ble_dfu_c, p_ble_evt); break; case BLE_GAP_EVT_DISCONNECTED: on_disconnect(p_ble_dfu_c, p_ble_evt); break; default: // no implementation break; } } uint32_t ble_dfu_c_init(ble_dfu_c_t * p_ble_dfu_c, const ble_dfu_c_init_t * p_ble_dfu_c_init) { uint32_t err_code; ble_uuid_t service_uuid; ble_uuid128_t base_uuid = BLE_NORDIC_VENDOR_BASE_UUID; VERIFY_PARAM_NOT_NULL(p_ble_dfu_c); VERIFY_PARAM_NOT_NULL(p_ble_dfu_c_init); // Initialize the service structure. p_ble_dfu_c->conn_handle = BLE_CONN_HANDLE_INVALID; p_ble_dfu_c->evt_handler = p_ble_dfu_c_init->evt_handler; p_ble_dfu_c->handles.chr = BLE_GATT_HANDLE_INVALID; p_ble_dfu_c->handles.cccd = BLE_GATT_HANDLE_INVALID; BLE_UUID_BLE_ASSIGN(service_uuid, BLE_DFU_SERVICE_UUID); if (p_ble_dfu_c->evt_handler == NULL) { p_ble_dfu_c->evt_handler = dummy_evt_handler; } p_ble_dfu_c->uuid_type = BLE_UUID_TYPE_BLE; return ble_db_discovery_evt_register(&service_uuid); } uint32_t ble_dfu_c_indication_enable(ble_dfu_c_t * p_ble_dfu_c, bool enable) { VERIFY_PARAM_NOT_NULL(p_ble_dfu_c); if ((p_ble_dfu_c->conn_handle == BLE_CONN_HANDLE_INVALID) || (p_ble_dfu_c->handles.cccd == BLE_GATT_HANDLE_INVALID)) { return NRF_ERROR_INVALID_STATE; } ret_code_t err_code = NRF_SUCCESS; if (p_ble_dfu_c->indication_enabled == false) { tx_message_t * p_msg; uint16_t cccd_val = enable ? BLE_GATT_HVX_INDICATION : 0; p_msg = &m_tx_buffer[m_tx_insert_index++]; m_tx_insert_index &= TX_BUFFER_MASK; p_msg->req.write_req.gattc_params.handle = p_ble_dfu_c->handles.cccd; p_msg->req.write_req.gattc_params.len = BLE_CCCD_VALUE_LEN; p_msg->req.write_req.gattc_params.p_value = p_msg->req.write_req.gattc_value; p_msg->req.write_req.gattc_params.offset = 0; p_msg->req.write_req.gattc_params.write_op = BLE_GATT_OP_WRITE_REQ; p_msg->req.write_req.gattc_value[0] = LSB_16(cccd_val); p_msg->req.write_req.gattc_value[1] = MSB_16(cccd_val); p_msg->conn_handle = p_ble_dfu_c->conn_handle; p_msg->type = WRITE_REQ; err_code = tx_buffer_process(); p_ble_dfu_c->indication_enabled = true; } return err_code; } uint32_t ble_dfu_c_advname_set(ble_dfu_c_t * p_ble_dfu_c, uint8_t * p_adv_name, uint16_t length) { VERIFY_PARAM_NOT_NULL(p_ble_dfu_c); if (length > BLE_GAP_DEVNAME_DEFAULT_LEN - 2) { NRF_LOG_WARNING("Content too long."); return NRF_ERROR_INVALID_PARAM; } if (p_ble_dfu_c->conn_handle == BLE_CONN_HANDLE_INVALID) { NRF_LOG_WARNING("Connection handle invalid."); return NRF_ERROR_INVALID_STATE; } tx_message_t * p_msg; p_msg = &m_tx_buffer[m_tx_insert_index++]; m_tx_insert_index &= TX_BUFFER_MASK; p_msg->req.write_req.gattc_params.handle = p_ble_dfu_c->handles.chr; p_msg->req.write_req.gattc_params.len = (length + 1); p_msg->req.write_req.gattc_params.p_value = p_msg->req.write_req.gattc_value; p_msg->req.write_req.gattc_params.offset = 0; p_msg->req.write_req.gattc_params.write_op = BLE_GATT_OP_WRITE_REQ; p_msg->conn_handle = p_ble_dfu_c->conn_handle; p_msg->type = WRITE_REQ; p_msg->req.write_req.gattc_value[0] = DFU_OP_SET_ADV_NAME; p_msg->req.write_req.gattc_value[1] = length; memcpy(&p_msg->req.write_req.gattc_value[2], p_adv_name, length); return tx_buffer_process(); } uint32_t ble_dfu_c_enter_bootloader(ble_dfu_c_t * p_ble_dfu_c) { VERIFY_PARAM_NOT_NULL(p_ble_dfu_c); if (p_ble_dfu_c->conn_handle == BLE_CONN_HANDLE_INVALID) { NRF_LOG_WARNING("Connection handle invalid."); return NRF_ERROR_INVALID_STATE; } tx_message_t * p_msg; p_msg = &m_tx_buffer[m_tx_insert_index++]; m_tx_insert_index &= TX_BUFFER_MASK; p_msg->req.write_req.gattc_params.handle = p_ble_dfu_c->handles.chr; p_msg->req.write_req.gattc_params.len = 1; p_msg->req.write_req.gattc_params.p_value = p_msg->req.write_req.gattc_value; p_msg->req.write_req.gattc_params.offset = 0; p_msg->req.write_req.gattc_params.write_op = BLE_GATT_OP_WRITE_REQ; p_msg->conn_handle = p_ble_dfu_c->conn_handle; p_msg->type = WRITE_REQ; p_msg->req.write_req.gattc_value[0] = DFU_OP_ENTER_BOOTLOADER; return tx_buffer_process(); } uint32_t ble_dfu_c_handles_assign(ble_dfu_c_t * p_ble_dfu_c, uint16_t conn_handle, ble_dfu_c_handles_t const * p_peer_handles) { VERIFY_PARAM_NOT_NULL(p_ble_dfu_c); p_ble_dfu_c->conn_handle = conn_handle; if (p_peer_handles != NULL) { p_ble_dfu_c->handles.chr = p_peer_handles->chr; p_ble_dfu_c->handles.cccd = p_peer_handles->cccd; } return NRF_SUCCESS; } #endif