Hi Nordic,
Please provide the way to debug not able to write 14 Bytes data into Queued Writes module, my application not get any nrf_ble_qwr_on_ble_evt.
[Project Informaiton]
nrf52832 (S132 SoftDevice v6.1.1) + MCU (CXD5602), UART interface to connect between each other.
[Design description]
My application designs both ble_peripheral, and ble_central in one.
I integrate "write-queued-characteristic-with-less-than-20-bytes" solutions into my application.
[sdk_config.h]
#define NRF_BLE_QWR_ENABLED 1
#define NRF_BLE_QWR_MAX_ATTR 1
[Issue description]
Not able write more than 13 Bytes data (while write 14 Bytes, My application stuck) into My applcation with nrf_connect android application
In nrf_connect android application shows log in "Log 2020-01-17 11_37_48.txt"
[Central][Log in nrf_connect android application]
1). write 13 Bytes, result: PASS
V 11:37:05.688 Writing request to characteristic 6e400002-b5a3-f393-e0a9-e50e24dcca9e
D 11:37:05.688 gatt.writeCharacteristic(6e400002-b5a3-f393-e0a9-e50e24dcca9e, value=0x31323334353637383930313233)
I 11:37:06.088 Data written to 6e400002-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 31-32-33-34-35-36-37-38-39-30-31-32-33, "1234567890123"
A 11:37:06.088 "1234567890123" sent
2). write 14 Bytes, result: NG
V 11:37:28.334 Writing request to characteristic 6e400002-b5a3-f393-e0a9-e50e24dcca9e
D 11:37:28.334 gatt.writeCharacteristic(6e400002-b5a3-f393-e0a9-e50e24dcca9e, value=0x3132333435363738393031323334)
D 11:37:32.522 [Server callback] Connection state changed with status: 0 and new state: DISCONNECTED (0)
I 11:37:32.522 [Server] Device disconnected
E 11:37:32.538 Error 133 (0x85): GATT ERROR
[Peripheral][Log in my application with nrf52832]
1). write 13 Bytes, result: PASS
[2020-01-17 11:36:49] ble_spresense_handler_on_le_connect_status_change, ble_state_s.ble_connect_handle = 0x0001
[2020-01-17 11:36:49] [BLE_GATT] Connect status ADDR:6D:E1:DE:BF:A7:C3, status:Connected, ble_state->ble_connect_handle = 0x0001
[2020-01-17 11:36:49] Debug : RESP_OK => nrf_ble_qwr_on_ble_evt, p_ble_evt->header.evt_id = 16
[2020-01-17 11:36:49] Debug : RESP_OK => nrf_ble_qwr_on_ble_evt, p_ble_evt->header.evt_id = 35
[2020-01-17 11:36:49] Debug : RESP_OK => nrf_ble_qwr_on_ble_evt, p_ble_evt->header.evt_id = 36
[2020-01-17 11:36:49] Debug : RESP_OK => PHY update request.
[2020-01-17 11:36:49] Debug : RESP_OK => nrf_ble_qwr_on_ble_evt, p_ble_evt->header.evt_id = 33
[2020-01-17 11:36:50] Debug : RESP_OK => nrf_ble_qwr_on_ble_evt, p_ble_evt->header.evt_id = 34
[2020-01-17 11:36:50] Debug : RESP_OK => nrf_ble_qwr_on_ble_evt, p_ble_evt->header.evt_id = 18
[2020-01-17 11:36:51] Debug : RESP_OK => nrf_ble_qwr_on_ble_evt, p_ble_evt->header.evt_id = 18
[2020-01-17 11:36:55] Debug : RESP_OK => nrf_ble_qwr_on_ble_evt, p_ble_evt->header.evt_id = 18
[2020-01-17 11:37:07] Debug : RESP_OK => nrf_ble_qwr_on_ble_evt, p_ble_evt->header.evt_id = 81
[2020-01-17 11:37:07] Debug : RESP_OK => BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST
[2020-01-17 11:37:07] Debug : RESP_OK => on_rw_authorize_request, p_auth_req->request.write.op = 0x1
[2020-01-17 11:37:07] Debug : RESP_OK => nrf_ble_qwrs_on_qwr_evt, p_evt->evt_type = 2
[2020-01-17 11:37:07] Debug : RESP_OK => BLE_QWRS_NEW_DATA_RCVD
[2020-01-17 11:37:07] Debug : RESP_OK => 31323334353637383930313233
2). write 14 Bytes, result: NG
My application not show any evt in nrf_ble_qwr_on_ble_evt fuction, I guess while handle 14 Bytes data write request in softdevice has exception.
Please Nordic help to provide me the way to do next debug
[Attached files]
sdk_config.h (#define NRF_BLE_QWR_ENABLED 1 #define NRF_BLE_QWR_MAX_ATTR 1)
nrf_ble_qwr.c
nrf_ble_qwr.h
nrf_ble_qwrs.c
nrf_ble_qwrs.h
ble_wrap_15.c (my application)
Many thanks.
Caspar
nRF Connect, 2020-01-17 SONY_BLE (E6:E7:6E:CF:B4:C1) I 11:36:46.645 [Server] Server started V 11:36:46.669 Heart Rate (0x180D) - Heart Rate Measurement [N] (0x2A37) Client Characteristic Configuration (0x2902) - Body Sensor Location [R] (0x2A38) - Heart Rate Control Point [W] (0x2A39) Unknown Service (00003802-0000-1000-8000-00805f9b34fb) - Unknown Characteristic [I N R W] (00004a02-0000-1000-8000-00805f9b34fb) Client Characteristic Configuration (0x2902) Unknown Descriptor (0000aab0-0000-1000-8000-aabbccddeeff) Characteristic Presentation Format (0x2904) - Unknown Characteristic [I R W WNR] (0000aaa2-0000-1000-8000-aabbccddeeff) Client Characteristic Configuration (0x2902) User Data (0x181C) - First Name [R W] (0x2A8A) - Last Name [R W] (0x2A90) - Gender [R W] (0x2A8C) Current Time Service (0x1805) - Current Time [N R] (0x2A2B) Client Characteristic Configuration (0x2902) - Local Time Information [R] (0x2A0F) V 11:36:47.020 Connecting to E6:E7:6E:CF:B4:C1... D 11:36:47.021 gatt = device.connectGatt(autoConnect = false, TRANSPORT_LE, preferred PHY = LE 1M) D 11:36:48.538 [Server callback] Connection state changed with status: 0 and new state: CONNECTED (2) I 11:36:48.538 [Server] Device with address E6:E7:6E:CF:B4:C1 connected D 11:36:48.544 [Callback] Connection state changed with status: 0 and new state: CONNECTED (2) I 11:36:48.544 Connected to E6:E7:6E:CF:B4:C1 D 11:36:48.578 [Broadcast] Action received: android.bluetooth.device.action.ACL_CONNECTED V 11:36:48.578 Discovering services... D 11:36:48.578 gatt.discoverServices() I 11:36:48.988 Connection parameters updated (interval: 7.5ms, latency: 0, timeout: 5000ms) D 11:36:49.124 [Callback] Services discovered with status: 0 I 11:36:49.124 Services discovered V 11:36:49.130 Generic Access (0x1800) - Device Name [R W] (0x2A00) - Appearance [R] (0x2A01) - Peripheral Preferred Connection Parameters [R] (0x2A04) - Central Address Resolution [R] (0x2AA6) Generic Attribute (0x1801) Nordic UART Service (6e400001-b5a3-f393-e0a9-e50e24dcca9e) - RX Characteristic [N R W] (6e400002-b5a3-f393-e0a9-e50e24dcca9e) Client Characteristic Configuration (0x2902) D 11:36:49.130 gatt.setCharacteristicNotification(6e400002-b5a3-f393-e0a9-e50e24dcca9e, true) I 11:36:49.211 Connection parameters updated (interval: 45.0ms, latency: 0, timeout: 5000ms) I 11:36:53.218 Connection parameters updated (interval: 195.0ms, latency: 0, timeout: 4000ms) V 11:37:05.688 Writing request to characteristic 6e400002-b5a3-f393-e0a9-e50e24dcca9e D 11:37:05.688 gatt.writeCharacteristic(6e400002-b5a3-f393-e0a9-e50e24dcca9e, value=0x31323334353637383930313233) I 11:37:06.088 Data written to 6e400002-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 31-32-33-34-35-36-37-38-39-30-31-32-33, "1234567890123" A 11:37:06.088 "1234567890123" sent V 11:37:28.334 Writing request to characteristic 6e400002-b5a3-f393-e0a9-e50e24dcca9e D 11:37:28.334 gatt.writeCharacteristic(6e400002-b5a3-f393-e0a9-e50e24dcca9e, value=0x3132333435363738393031323334) D 11:37:32.522 [Server callback] Connection state changed with status: 0 and new state: DISCONNECTED (0) I 11:37:32.522 [Server] Device disconnected E 11:37:32.538 Error 133 (0x85): GATT ERROR D 11:37:32.539 [Callback] Connection state changed with status: 8 and new state: DISCONNECTED (0) E 11:37:32.552 Error 8 (0x8): GATT CONN TIMEOUT I 11:37:32.553 Disconnected D 11:37:32.568 [Broadcast] Action received: android.bluetooth.device.action.ACL_DISCONNECTED
/**
* Copyright (c) 2016 - 2019, 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(NRF_BLE_QWR)
#include <stdlib.h>
#include "nrf_ble_qwr.h"
#include "ble.h"
#include "ble_srv_common.h"
#define NRF_BLE_QWR_INITIALIZED 0xDE // Non-zero value used to make sure the given structure has been initialized by the module.
#define MODULE_INITIALIZED (p_qwr->initialized == NRF_BLE_QWR_INITIALIZED)
#include "sdk_macros.h"
#include <stdio.h>
// Response output macros
#define RESP(_form, ...) do { printf(_form "\n", ##__VA_ARGS__); } while(0)
#define RESP_ASYNC(form, ...) RESP("#" form, ##__VA_ARGS__)
#define RESP_ERROR(form, ...) RESP("*" form, ##__VA_ARGS__)
#define RESP_OK(form, ...) RESP("=" form, ##__VA_ARGS__)
ret_code_t nrf_ble_qwr_init(nrf_ble_qwr_t * p_qwr,
nrf_ble_qwr_init_t const * p_qwr_init)
{
VERIFY_PARAM_NOT_NULL(p_qwr);
VERIFY_PARAM_NOT_NULL(p_qwr_init);
if (MODULE_INITIALIZED)
{
return NRF_ERROR_INVALID_STATE;
}
p_qwr->error_handler = p_qwr_init->error_handler;
p_qwr->initialized = NRF_BLE_QWR_INITIALIZED;
p_qwr->conn_handle = BLE_CONN_HANDLE_INVALID;
#if (NRF_BLE_QWR_MAX_ATTR > 0)
memset(p_qwr->attr_handles, 0, sizeof(p_qwr->attr_handles));
p_qwr->nb_registered_attr = 0;
p_qwr->is_user_mem_reply_pending = false;
p_qwr->mem_buffer = p_qwr_init->mem_buffer;
p_qwr->callback = p_qwr_init->callback;
p_qwr->nb_written_handles = 0;
#endif
return NRF_SUCCESS;
}
#if (NRF_BLE_QWR_MAX_ATTR > 0)
ret_code_t nrf_ble_qwr_attr_register(nrf_ble_qwr_t * p_qwr, uint16_t attr_handle)
{
VERIFY_PARAM_NOT_NULL(p_qwr);
VERIFY_MODULE_INITIALIZED();
if ((p_qwr->nb_registered_attr == NRF_BLE_QWR_MAX_ATTR)
|| (p_qwr->mem_buffer.p_mem == NULL)
|| (p_qwr->mem_buffer.len == 0))
{
return (NRF_ERROR_NO_MEM);
}
if (attr_handle == BLE_GATT_HANDLE_INVALID)
{
return NRF_ERROR_INVALID_PARAM;
}
p_qwr->attr_handles[p_qwr->nb_registered_attr] = attr_handle;
p_qwr->nb_registered_attr++;
return NRF_SUCCESS;
}
ret_code_t nrf_ble_qwr_value_get(nrf_ble_qwr_t * p_qwr,
uint16_t attr_handle,
uint8_t * p_mem,
uint16_t * p_len)
{
VERIFY_PARAM_NOT_NULL(p_qwr);
VERIFY_PARAM_NOT_NULL(p_mem);
VERIFY_PARAM_NOT_NULL(p_len);
VERIFY_MODULE_INITIALIZED();
uint16_t i = 0;
uint16_t handle = BLE_GATT_HANDLE_INVALID;
uint16_t val_len = 0;
uint16_t val_offset = 0;
uint16_t cur_len = 0;
do
{
handle = uint16_decode(&(p_qwr->mem_buffer.p_mem[i]));
if (handle == BLE_GATT_HANDLE_INVALID)
{
break;
}
i += sizeof(uint16_t);
val_offset = uint16_decode(&(p_qwr->mem_buffer.p_mem[i]));
i += sizeof(uint16_t);
val_len = uint16_decode(&(p_qwr->mem_buffer.p_mem[i]));
i += sizeof(uint16_t);
if (handle == attr_handle)
{
cur_len = val_offset + val_len;
if (cur_len <= *p_len)
{
memcpy((p_mem + val_offset), &(p_qwr->mem_buffer.p_mem[i]), val_len);
}
else
{
return NRF_ERROR_NO_MEM;
}
}
i += val_len;
}
while (i < p_qwr->mem_buffer.len);
*p_len = cur_len;
return NRF_SUCCESS;
}
#endif
ret_code_t nrf_ble_qwr_conn_handle_assign(nrf_ble_qwr_t * p_qwr,
uint16_t conn_handle)
{
VERIFY_PARAM_NOT_NULL(p_qwr);
VERIFY_MODULE_INITIALIZED();
p_qwr->conn_handle = conn_handle;
return NRF_SUCCESS;
}
/**@brief checks if a user_mem_reply is pending, if so attempts to send it.
*
* @param[in] p_qwr QWR structure.
*/
static void user_mem_reply(nrf_ble_qwr_t * p_qwr)
{
if (p_qwr->is_user_mem_reply_pending)
{
ret_code_t err_code;
#if (NRF_BLE_QWR_MAX_ATTR == 0)
err_code = sd_ble_user_mem_reply(p_qwr->conn_handle, NULL);
#else
err_code = sd_ble_user_mem_reply(p_qwr->conn_handle, &p_qwr->mem_buffer);
#endif
if (err_code == NRF_SUCCESS)
{
p_qwr->is_user_mem_reply_pending = false;
}
else if (err_code == NRF_ERROR_BUSY)
{
p_qwr->is_user_mem_reply_pending = true;
}
else
{
p_qwr->error_handler(err_code);
}
}
}
/**@brief Handle a user memory request event.
*
* @param[in] p_qwr QWR structure.
* @param[in] p_common_evt User_mem_request event to be handled.
*/
static void on_user_mem_request(nrf_ble_qwr_t * p_qwr,
ble_common_evt_t const * p_common_evt)
{
if ((p_common_evt->params.user_mem_request.type == BLE_USER_MEM_TYPE_GATTS_QUEUED_WRITES) &&
(p_common_evt->conn_handle == p_qwr->conn_handle))
{
p_qwr->is_user_mem_reply_pending = true;
user_mem_reply(p_qwr);
}
}
/**@brief Handle a user memory release event.
*
* @param[in] p_qwr QWR structure.
* @param[in] p_common_evt User_mem_release event to be handled.
*/
static void on_user_mem_release(nrf_ble_qwr_t * p_qwr,
ble_common_evt_t const * p_common_evt)
{
#if (NRF_BLE_QWR_MAX_ATTR > 0)
if ((p_common_evt->params.user_mem_release.type == BLE_USER_MEM_TYPE_GATTS_QUEUED_WRITES) &&
(p_common_evt->conn_handle == p_qwr->conn_handle))
{
// Cancel the current operation.
p_qwr->nb_written_handles = 0;
}
#endif
}
#if (NRF_BLE_QWR_MAX_ATTR > 0)
/**@brief Handle a prepare write event.
*
* @param[in] p_qwr QWR structure.
* @param[in] p_evt_write WRITE event to be handled.
*/
static void on_prepare_write(nrf_ble_qwr_t * p_qwr,
ble_gatts_evt_write_t const * p_evt_write)
{
uint32_t err_code;
ble_gatts_rw_authorize_reply_params_t auth_reply;
memset(&auth_reply, 0, sizeof(auth_reply));
auth_reply.params.write.gatt_status = NRF_BLE_QWR_REJ_REQUEST_ERR_CODE;
auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
uint32_t i;
for (i = 0; i < p_qwr->nb_written_handles; i++)
{
if (p_qwr->written_attr_handles[i] == p_evt_write->handle)
{
auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
break;
}
}
if (auth_reply.params.write.gatt_status != BLE_GATT_STATUS_SUCCESS)
{
for (i = 0; i < p_qwr->nb_registered_attr; i++)
{
if (p_qwr->attr_handles[i] == p_evt_write->handle)
{
auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
p_qwr->written_attr_handles[p_qwr->nb_written_handles++] = p_evt_write->handle;
break;
}
}
}
err_code = sd_ble_gatts_rw_authorize_reply(p_qwr->conn_handle, &auth_reply);
if (err_code != NRF_SUCCESS)
{
// Cancel the current operation.
p_qwr->nb_written_handles = 0;
// Report error to application.
p_qwr->error_handler(err_code);
}
}
/**@brief Handle an execute write event.
*
* @param[in] p_qwr QWR structure.
* @param[in] p_evt_write EXEC WRITE event to be handled.
*/
static void on_execute_write(nrf_ble_qwr_t * p_qwr,
ble_gatts_evt_write_t const * p_evt_write)
{
uint32_t err_code;
ble_gatts_rw_authorize_reply_params_t auth_reply;
memset(&auth_reply, 0, sizeof(auth_reply));
auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
if (p_qwr->nb_written_handles == 0)
{
auth_reply.params.write.gatt_status = NRF_BLE_QWR_REJ_REQUEST_ERR_CODE;
err_code = sd_ble_gatts_rw_authorize_reply(p_qwr->conn_handle, &auth_reply);
if (err_code != NRF_SUCCESS)
{
// Report error to application.
p_qwr->error_handler(err_code);
}
return;
}
for (uint16_t i = 0; i < p_qwr->nb_written_handles; i++)
{
nrf_ble_qwr_evt_t evt;
uint16_t ret_val;
evt.evt_type = NRF_BLE_QWR_EVT_AUTH_REQUEST;
evt.attr_handle = p_qwr->written_attr_handles[i];
ret_val = p_qwr->callback(p_qwr, &evt);
if (ret_val != BLE_GATT_STATUS_SUCCESS)
{
auth_reply.params.write.gatt_status = ret_val;
}
}
err_code = sd_ble_gatts_rw_authorize_reply(p_qwr->conn_handle, &auth_reply);
if (err_code != NRF_SUCCESS)
{
// Report error to application.
p_qwr->error_handler(err_code);
}
// If the execute has not been rejected by any of the registered applications, propagate execute write event to all written handles. */
if (auth_reply.params.write.gatt_status == BLE_GATT_STATUS_SUCCESS)
{
for (uint16_t i = 0; i < p_qwr->nb_written_handles; i++)
{
nrf_ble_qwr_evt_t evt;
evt.evt_type = NRF_BLE_QWR_EVT_EXECUTE_WRITE;
evt.attr_handle = p_qwr->written_attr_handles[i];
/*lint -e534 -save "Ignoring return value of function" */
p_qwr->callback(p_qwr, &evt);
/*lint -restore*/
auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
}
}
p_qwr->nb_written_handles = 0;
}
/**@brief Handle a cancel write event.
*
* @param[in] p_qwr QWR structure.
* @param[in] p_evt_write EXEC WRITE event to be handled.
*/
static void on_cancel_write(nrf_ble_qwr_t * p_qwr,
ble_gatts_evt_write_t const * p_evt_write)
{
uint32_t err_code;
ble_gatts_rw_authorize_reply_params_t auth_reply;
memset(&auth_reply, 0, sizeof(auth_reply));
auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
err_code = sd_ble_gatts_rw_authorize_reply(p_qwr->conn_handle, &auth_reply);
if (err_code != NRF_SUCCESS)
{
// Report error to application.
p_qwr->error_handler(err_code);
}
p_qwr->nb_written_handles = 0;
}
#endif
/**@brief Handle a rw_authorize_request event.
*
* @param[in] p_qwr QWR structure.
* @param[in] p_gatts_evt RW_authorize_request event to be handled.
*/
static void on_rw_authorize_request(nrf_ble_qwr_t * p_qwr,
ble_gatts_evt_t const * p_gatts_evt)
{
if (p_gatts_evt->conn_handle != p_qwr->conn_handle)
{
return;
}
ble_gatts_evt_rw_authorize_request_t const * p_auth_req = &p_gatts_evt->params.authorize_request;
if (p_auth_req->type != BLE_GATTS_AUTHORIZE_TYPE_WRITE)
{
return;
}
#if (NRF_BLE_QWR_MAX_ATTR == 0)
// Handle only queued write related operations.
if ((p_auth_req->request.write.op != BLE_GATTS_OP_PREP_WRITE_REQ) &&
(p_auth_req->request.write.op != BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) &&
(p_auth_req->request.write.op != BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL))
{
return;
}
// Prepare the response.
ble_gatts_rw_authorize_reply_params_t auth_reply = {0};
auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
auth_reply.params.write.gatt_status = NRF_BLE_QWR_REJ_REQUEST_ERR_CODE;
if (p_auth_req->request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL)
{
auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
}
ret_code_t err_code = sd_ble_gatts_rw_authorize_reply(p_gatts_evt->conn_handle, &auth_reply);
if (err_code != NRF_SUCCESS)
{
// Report error to application.
p_qwr->error_handler(err_code);
}
#else
ble_gatts_rw_authorize_reply_params_t auth_reply = {0};
nrf_ble_qwr_evt_t evt;
RESP_OK("%s, p_auth_req->request.write.op = 0x%x", __func__, p_auth_req->request.write.op);
switch (p_auth_req->request.write.op)
{
case BLE_GATTS_OP_WRITE_REQ:
auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
auth_reply.params.write.update = 1;
auth_reply.params.write.p_data = p_auth_req->request.write.data;
auth_reply.params.write.len = p_auth_req->request.write.len;
ret_code_t err_code = sd_ble_gatts_rw_authorize_reply(p_qwr->conn_handle,&auth_reply);
if (err_code != NRF_SUCCESS)
{
// Report error to application.
p_qwr->error_handler(err_code);
}
evt.evt_type = NRF_BLE_QWR_EVT_WRITE;
evt.attr_handle = p_gatts_evt->params.authorize_request.request.write.handle;
evt.write_len = p_auth_req->request.write.len;
p_qwr->callback(p_qwr, &evt);
break;
case BLE_GATTS_OP_PREP_WRITE_REQ:
on_prepare_write(p_qwr, &p_auth_req->request.write);
break; // BLE_GATTS_OP_PREP_WRITE_REQ
case BLE_GATTS_OP_EXEC_WRITE_REQ_NOW:
on_execute_write(p_qwr, &p_auth_req->request.write);
break; // BLE_GATTS_OP_EXEC_WRITE_REQ_NOW
case BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL:
on_cancel_write(p_qwr, &p_auth_req->request.write);
break; // BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL
default:
// No implementation needed.
break;
}
#endif
}
void nrf_ble_qwr_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
RESP_OK("%s", __func__);
VERIFY_PARAM_NOT_NULL_VOID(p_context);
VERIFY_PARAM_NOT_NULL_VOID(p_ble_evt);
RESP_OK("%s, p_ble_evt->header.evt_id = %d", __func__, p_ble_evt->header.evt_id);
nrf_ble_qwr_t * p_qwr = (nrf_ble_qwr_t *)p_context;
VERIFY_MODULE_INITIALIZED_VOID();
if (p_ble_evt->evt.common_evt.conn_handle == p_qwr->conn_handle)
{
user_mem_reply(p_qwr);
}
switch (p_ble_evt->header.evt_id)
{
case BLE_EVT_USER_MEM_REQUEST:
RESP_OK("BLE_EVT_USER_MEM_REQUEST");
on_user_mem_request(p_qwr, &p_ble_evt->evt.common_evt);
break; // BLE_EVT_USER_MEM_REQUEST
case BLE_EVT_USER_MEM_RELEASE:
RESP_OK("BLE_EVT_USER_MEM_RELEASE");
on_user_mem_release(p_qwr, &p_ble_evt->evt.common_evt);
break; // BLE_EVT_USER_MEM_REQUEST
case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
RESP_OK("BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST");
on_rw_authorize_request(p_qwr, &p_ble_evt->evt.gatts_evt);
break; // BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST
case BLE_GAP_EVT_DISCONNECTED:
if (p_ble_evt->evt.gap_evt.conn_handle == p_qwr->conn_handle)
{
p_qwr->conn_handle = BLE_CONN_HANDLE_INVALID;
#if (NRF_BLE_QWR_MAX_ATTR > 0)
p_qwr->nb_written_handles = 0;
#endif
}
break; // BLE_GAP_EVT_DISCONNECTED
default:
break;
}
}
#endif // NRF_MODULE_ENABLED(NRF_BLE_QWR)
/**
* Copyright (c) 2016 - 2019, 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 <stdio.h>
#include "nrf_ble_qwrs.h"
#include <string.h>
#include "ble_srv_common.h"
#include "nrf_ble_qwr.h"
#define NRF_LOG_MODULE_NAME BLE_QWRS
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
#define BLE_UUID_QWRS_SERVICE 0x0001 /**< The UUID of the QWRS Service. */
#define BLE_UUID_QWRS_LONG_CHARACTERISTIC 0x0002 /**< The UUID of the QWRS Long Characteristic. */
#define BLE_QWRS_MAX_LONG_CHAR_LEN NRF_BLE_QWRS_MAX_RCV_SIZE /**< Maximum length of the QWRS Long Characteristic (in bytes). */
#define BLE_QWRS_MAX_CHAR_LEN 20 /**< Maximum length of the QWRS Characteristic (in bytes). */
#define QWRS_BASE_UUID {{0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x00, 0x00, 0x40, 0x6E}} /**< Used vendor specific UUID. */
// Response output macros
#define RESP(_form, ...) do { printf(_form "\n", ##__VA_ARGS__); } while(0)
#define RESP_ASYNC(form, ...) RESP("#" form, ##__VA_ARGS__)
#define RESP_ERROR(form, ...) RESP("*" form, ##__VA_ARGS__)
#define RESP_OK(form, ...) RESP("=" form, ##__VA_ARGS__)
uint16_t nrf_ble_qwrs_on_qwr_evt(nrf_ble_qwrs_t *p_qwrs,
nrf_ble_qwr_t * p_qwr,
nrf_ble_qwr_evt_t * p_evt)
{
RESP_OK("%s, p_evt->evt_type = %d", __func__, p_evt->evt_type);
if (p_qwrs->evt_handler != NULL)
{
nrf_ble_qwrs_evt_t cur_evt;
ret_code_t err_code;
if(p_evt->evt_type == NRF_BLE_QWR_EVT_WRITE)
{
uint8_t recv_data[BLE_QWRS_MAX_CHAR_LEN];
memset(recv_data,0,BLE_QWRS_MAX_CHAR_LEN);
ble_gatts_value_t gatt_value = {0};
gatt_value.p_value = recv_data;
gatt_value.len = p_evt->write_len;
err_code = sd_ble_gatts_value_get(p_qwr->conn_handle,p_evt->attr_handle,&gatt_value);
if (err_code == NRF_SUCCESS)
{
cur_evt.evt_type = BLE_QWRS_NEW_DATA_RCVD;
memcpy(&cur_evt.rcvd_data, recv_data, p_evt->write_len);
cur_evt.rcv_length = p_evt->write_len;
return p_qwrs->evt_handler(p_qwrs, &(cur_evt));
}
else
{
RESP_ERROR("%s, sd_ble_gatts_value_get failed.", __func__);
}
}
else
{
cur_evt.rcv_length = NRF_BLE_QWRS_MAX_RCV_SIZE;
err_code = nrf_ble_qwr_value_get(p_qwr, p_evt->attr_handle, cur_evt.rcvd_data, &cur_evt.rcv_length);
if (err_code != NRF_SUCCESS)
{
RESP_ERROR("%s, nrf_ble_qwr_value_get failed.", __func__);
return BLE_GATT_STATUS_ATTERR_INSUF_AUTHORIZATION;
}
if (p_evt->evt_type == NRF_BLE_QWR_EVT_AUTH_REQUEST)
{
cur_evt.evt_type = BLE_QWRS_CHECK_RCVD_DATA;
return p_qwrs->evt_handler(p_qwrs, &(cur_evt));
}
else if (p_evt->evt_type == NRF_BLE_QWR_EVT_EXECUTE_WRITE)
{
if (p_qwrs->evt_handler != NULL)
{
cur_evt.evt_type = BLE_QWRS_NEW_DATA_RCVD;
return p_qwrs->evt_handler(p_qwrs, &(cur_evt));
}
}
}
}
return BLE_GATT_STATUS_SUCCESS;
}
uint32_t nrf_ble_qwrs_init(nrf_ble_qwrs_init_t *p_qwrs_init, nrf_ble_qwrs_t *p_qwrs)
{
ret_code_t err_code;
ble_uuid_t ble_uuid;
// Initialize service structure.
p_qwrs->evt_handler = p_qwrs_init->evt_handler;
p_qwrs->error_handler = p_qwrs_init->error_handler;
p_qwrs->conn_handle = BLE_CONN_HANDLE_INVALID;
// Add service.
ble_uuid128_t base_uuid = QWRS_BASE_UUID;
err_code = sd_ble_uuid_vs_add(&base_uuid, &p_qwrs->uuid_type);
RESP_OK("%s, sd_ble_uuid_vs_add, err_code = %lu", __func__, err_code);
VERIFY_SUCCESS(err_code);
ble_uuid.type = p_qwrs->uuid_type;
ble_uuid.uuid = BLE_UUID_QWRS_SERVICE;
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
&ble_uuid,
&p_qwrs->service_handle);
RESP_OK("%s, sd_ble_gatts_service_add, err_code = %lu", __func__, err_code);
VERIFY_SUCCESS(err_code);
//Add Long characteristic
ble_add_char_params_t add_char_params;
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_UUID_QWRS_LONG_CHARACTERISTIC;
add_char_params.uuid_type = p_qwrs->uuid_type;
add_char_params.max_len = BLE_QWRS_MAX_LONG_CHAR_LEN;
add_char_params.init_len = 0;
add_char_params.char_props.write = true;
add_char_params.char_props.read = true;
add_char_params.char_props.notify= true;
add_char_params.cccd_write_access= true;
add_char_params.read_access = SEC_OPEN;
add_char_params.write_access = SEC_OPEN;
add_char_params.is_defered_read = true;
add_char_params.is_defered_write = true;
err_code = characteristic_add(p_qwrs->service_handle,
&add_char_params,
&p_qwrs->long_charact_handles);
RESP_OK("%s, characteristic_add, err_code = %lu", __func__, err_code);
VERIFY_SUCCESS(err_code);
if (p_qwrs_init->p_qwr_ctx != NULL)
{
err_code = nrf_ble_qwr_attr_register(p_qwrs_init->p_qwr_ctx,
p_qwrs->long_charact_handles.value_handle);
RESP_OK("%s, nrf_ble_qwr_attr_register, err_code = %lu", __func__, err_code);
VERIFY_SUCCESS(err_code);
}
return err_code;
}
/**
* Copyright (c) 2014 - 2017, 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, O
* 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 <utils.h>
// ============================= ble_app_queued_writes =============================
#include "nrf_ble_qwr.h"
#include "nrf_ble_qwrs.h"
// ============================= ble_app_queued_writes =============================
#define APP_FEATURE_NOT_SUPPORTED BLE_GATT_STATUS_ATTERR_APP_BEGIN + 2 /**< Reply when unsupported features are requested. */
#define APP_BLE_OBSERVER_PRIO 1 /**< Application's BLE observer priority. You shouldn't need to modify this value. */
#define APP_BLE_CONN_CFG_TAG 1 /**< A tag identifying the SoftDevice BLE configuration. */
#define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000) /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
#define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000) /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
#define MAX_CONN_PARAMS_UPDATE_COUNT 3 /**< Number of attempts before giving up the connection parameter negotiation. */
#define CONFIG_BLUETOOTH_LE_NAME "BLE-CLASSIC"
#define SEND_DISCOVERY_RESULT_DELAY APP_TIMER_TICKS(10000) /**< Discovery notification delay time. */
#define APP_ADV_INTERVAL 300 /**< The advertising interval (in units of 0.625 ms. This value corresponds to 187.5 ms). */
#define APP_ADV_DURATION 18000 /**< The advertising duration (180 seconds) in units of 10 milliseconds. */
// ============================= ble_app_queued_writes =============================
#define MEM_BUFF_SIZE 512
// ============================= ble_app_queued_writes =============================
// Global variables
//#define BLE_WRAP_NO_NUS 1 //CEI
// ============================= ble_app_queued_writes =============================
NRF_BLE_QWR_DEF(m_qwr); /**< Queued Writes structure.*/
// ============================= ble_app_queued_writes =============================
NRF_BLE_GATT_DEF(m_gatt); /** GATT module instance*/
BLE_ADVERTISING_DEF(m_advertising); /** Advertising module instance*/
#ifndef BLE_WRAP_NO_NUS
BLE_NUS_DEF(m_nus, NRF_SDH_BLE_TOTAL_LINK_COUNT); /** Structure to identify the Nordic UART Service*/
#endif
#define IS_CENTRAL (m_ble_role == eBLE_Central)
// Response output macros
#define RESP(_form, ...) do { printf(_form "\n", ##__VA_ARGS__); } while(0)
#define RESP_ASYNC(form, ...) RESP("#" form, ##__VA_ARGS__)
#define RESP_ERROR(form, ...) RESP("*" form, ##__VA_ARGS__)
#define RESP_OK(form, ...) RESP("=" form, ##__VA_ARGS__)
// ============================= Code copied from Nordic template =============================
extern uint8_t m_enc_advdata[];
extern uint8_t m_adv_handle;
extern ble_gap_adv_data_t m_adv_data;
extern ble_gap_adv_params_t m_adv_params;
extern ble_gap_scan_params_t m_scan_params;
extern ble_gap_conn_params_t m_connection_param;
// ============================= ble_app_queued_writes, Start =============================
static uint8_t m_buffer[MEM_BUFF_SIZE];
static nrf_ble_qwrs_t m_qwrs;
// ============================= ble_app_queued_writes, End =============================
static char const m_target_periph_name[] = "GATTTool"; /**< Name of the device we try to connect to. This name is searched in the scan report data*/
//Todo
static void scan_evt_handler(scan_evt_t const * p_scan_evt)
{
ret_code_t err_code;
switch(p_scan_evt->scan_evt_id)
{
case NRF_BLE_SCAN_EVT_CONNECTING_ERROR:
err_code = p_scan_evt->params.connecting_err.err_code;
APP_ERROR_CHECK(err_code);
break;
default:
break;
}
}
//From v15 nrf_ble_scan
static void scan_init(void)
{
ret_code_t err_code;
nrf_ble_scan_init_t init_scan;
memset(&init_scan, 0, sizeof(init_scan));
init_scan.connect_if_match = true;
init_scan.conn_cfg_tag = BLE_CONN_CFG_TAG_DEFAULT;//APP_BLE_CONN_CFG_TAG;
init_scan.p_scan_param = &m_scan_params;
init_scan.p_conn_param = &m_connection_param;
err_code = nrf_ble_scan_init(&m_scan, &init_scan, scan_evt_handler);
APP_ERROR_CHECK(err_code);
// Setting filters for scanning.
err_code = nrf_ble_scan_filters_enable(&m_scan, NRF_BLE_SCAN_NAME_FILTER, false);
APP_ERROR_CHECK(err_code);
err_code = nrf_ble_scan_filter_set(&m_scan, SCAN_NAME_FILTER, m_target_periph_name);
APP_ERROR_CHECK(err_code);
}
// =================================================================================
// ============================= Code copied from Nordic template =============================
/**@brief Function for handling Peer Manager events.
*
* @param[in] p_evt Peer Manager event.
*/
__WEAK void pm_evt_handler(pm_evt_t const * p_evt)
{
ret_code_t err_code;
switch (p_evt->evt_id)
{
case PM_EVT_BONDED_PEER_CONNECTED:
{
NRF_LOG_INFO("Connected to a previously bonded device.");
} break;
case PM_EVT_CONN_SEC_SUCCEEDED:
{
NRF_LOG_INFO("Connection secured: role: %d, conn_handle: 0x%x, procedure: %d.",
ble_conn_state_role(p_evt->conn_handle),
p_evt->conn_handle,
p_evt->params.conn_sec_succeeded.procedure);
} break;
case PM_EVT_CONN_SEC_FAILED:
{
/* Often, when securing fails, it shouldn't be restarted, for security reasons.
* Other times, it can be restarted directly.
* Sometimes it can be restarted, but only after changing some Security Parameters.
* Sometimes, it cannot be restarted until the link is disconnected and reconnected.
* Sometimes it is impossible, to secure the link, or the peer device does not support it.
* How to handle this error is highly application dependent. */
} break;
case PM_EVT_CONN_SEC_CONFIG_REQ:
{
// Reject pairing request from an already bonded peer.
pm_conn_sec_config_t conn_sec_config = {.allow_repairing = false};
pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
} break;
case PM_EVT_STORAGE_FULL:
{
// Run garbage collection on the flash.
err_code = fds_gc();
if (err_code == FDS_ERR_BUSY || err_code == FDS_ERR_NO_SPACE_IN_QUEUES)
{
// Retry.
}
else
{
APP_ERROR_CHECK(err_code);
}
} break;
case PM_EVT_PEERS_DELETE_SUCCEEDED:
{
//advertising_start(false);
} break;
case PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED:
{
// The local database has likely changed, send service changed indications.
pm_local_database_has_changed();
} break;
case PM_EVT_PEER_DATA_UPDATE_FAILED:
{
// Assert.
APP_ERROR_CHECK(p_evt->params.peer_data_update_failed.error);
} break;
case PM_EVT_PEER_DELETE_FAILED:
{
// Assert.
APP_ERROR_CHECK(p_evt->params.peer_delete_failed.error);
} break;
case PM_EVT_PEERS_DELETE_FAILED:
{
// Assert.
APP_ERROR_CHECK(p_evt->params.peers_delete_failed_evt.error);
} break;
case PM_EVT_ERROR_UNEXPECTED:
{
// Assert.
APP_ERROR_CHECK(p_evt->params.error_unexpected.error);
} break;
case PM_EVT_CONN_SEC_START:
case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
case PM_EVT_PEER_DELETE_SUCCEEDED:
case PM_EVT_LOCAL_DB_CACHE_APPLIED:
case PM_EVT_SERVICE_CHANGED_IND_SENT:
case PM_EVT_SERVICE_CHANGED_IND_CONFIRMED:
default:
break;
}
}
/**@brief Function for the Timer initialization.
*
* @details Initializes the timer module. This creates and starts application timers.
*/
__WEAK void timers_init(void)
{
// Initialize timer module.
ret_code_t err_code = app_timer_init();
APP_ERROR_CHECK(err_code);
}
/**@brief Function for initializing the GATT module.
*/
static void gatt_init(void)
{
ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL);
//RESP_OK("%s, error_code = %ld", __func__, err_code);
APP_ERROR_CHECK(err_code);
}
#ifndef BLE_WRAP_NO_NUS
__WEAK void nus_string_received(const char *str) {
}
/**@brief Function for handling the data from the Nordic UART Service.
*
* @details This function will process the data received from the Nordic UART BLE Service and send
* it to the UART module.
*
* @param[in] p_nus Nordic UART Service structure.
* @param[in] p_data Data to be send to UART module.
* @param[in] length Length of the data.
*/
/**@snippet [Handling the data received over BLE] */
__WEAK void nus_data_handler(ble_nus_evt_t * p_evt)
{
if (p_evt->type == BLE_NUS_EVT_RX_DATA) {
uint16_t length = MIN(sizeof(m_nus_input)-1, p_evt->params.rx_data.length);
strncpy(m_nus_input, (char*)p_evt->params.rx_data.p_data, length);
m_nus_input[length] = 0;
if (strstr(m_nus_input, "DFUStart")) {
// Catch this command here in case application doesn't call ble_wrap_handle_pending
ble_wrap_enter_ota_mode();
}
}
}
#endif
// ============================= ble_app_queued_writes, Start =============================
/**@brief Function for handling Service errors.
*
* @details A pointer to this function will be passed to each service which may need to inform the
* application about an error.
*
* @param[in] nrf_error Error code containing information about what went wrong.
*/
static void service_error_handler(uint32_t nrf_error)
{
APP_ERROR_HANDLER(nrf_error);
}
/**@brief Function for handling the Queued Write Example Service events.
*
* @details This function will be called for all Queued Write Example Service events which are passed to
* the application.
*
* @param[in] p_qwrs Queued Write Example Service structure.
* @param[in] p_evt Event received from the Queued Write Example Service.
*
*/
static uint16_t queued_write_example_service_evt_handler(nrf_ble_qwrs_t * p_qwrs,
nrf_ble_qwrs_evt_t * p_evt)
{
uint16_t ret_val = BLE_GATT_STATUS_SUCCESS;
switch (p_evt->evt_type)
{
case BLE_QWRS_CHECK_RCVD_DATA:
// This is where your application can check the received value before accepting the write.
if (p_evt->rcvd_data[0] == 0)
{
RESP_ERROR("First byte is 0, reject the operation");
ret_val = BLE_GATT_STATUS_ATTERR_INSUF_AUTHORIZATION;
}
else
{
RESP_OK("First byte is not 0, accept the operation");
ret_val = BLE_GATT_STATUS_SUCCESS;
}
break;//BLE_QWRS_CHECK_RCVD_DATA
case BLE_QWRS_NEW_DATA_RCVD:
RESP_OK("BLE_QWRS_NEW_DATA_RCVD");
/**
* Write Queued Characteristic with Less than 20 bytes
* https://devzone.nordicsemi.com/f/nordic-q-a/37970/write-queued-characteristic-with-less-than-20-bytes/151680#151680
**/
if (p_evt->rcv_length > 0)
{
uint8_t buff[p_evt->rcv_length];
memset(buff, 0x00, p_evt->rcv_length);
bytes_to_hex(p_evt->rcvd_data, p_evt->rcv_length, (char*)buff);
RESP_OK("%s", buff);
}
// This is where the data is actually received and has been accepted.
break;//BLE_QWRS_NEW_DATA_RCVD
default:
// No implementation needed.
break;
}
return ret_val;
}
/**@brief Function for handling events from the Queued Write module.
*/
uint16_t queued_write_handler(nrf_ble_qwr_t * p_qwr, nrf_ble_qwr_evt_t * p_evt)
{
return nrf_ble_qwrs_on_qwr_evt(&m_qwrs, p_qwr, p_evt);
}
// ============================= ble_app_queued_writes, End =============================
/**@brief Function for initializing services that will be used by the application.
*/
__WEAK void services_init(void)
{
#ifndef BLE_WRAP_NO_NUS
uint32_t err_code;
ble_nus_init_t nus_init;
memset(&nus_init, 0, sizeof(nus_init));
nus_init.data_handler = nus_data_handler;
err_code = ble_nus_init(&m_nus, &nus_init);
APP_ERROR_CHECK(err_code);
#endif
}
// ============================= ble_app_queued_writes, Start =============================
/**@brief Function for initializing gatt services that will be used by the application.
*/
__WEAK ret_code_t gatt_services_init(void)
{
ret_code_t err_code;
nrf_ble_qwr_init_t qwr_init;
nrf_ble_qwrs_init_t qwrs_init;
memset(&qwr_init,0x00,sizeof(qwr_init));
// Initialize Queued Write Module
qwr_init.mem_buffer.len = MEM_BUFF_SIZE;
qwr_init.mem_buffer.p_mem = m_buffer;
qwr_init.error_handler = service_error_handler;
qwr_init.callback = queued_write_handler;
err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);
RESP_OK("%s, nrf_ble_qwr_init, err_code = %lu\n", __func__, err_code);
//APP_ERROR_CHECK(err_code);
//initialize the Queued Writes Example Service
memset(&qwrs_init, 0, sizeof(qwrs_init));
qwrs_init.evt_handler = queued_write_example_service_evt_handler;
qwrs_init.error_handler = service_error_handler;
qwrs_init.p_qwr_ctx = &m_qwr;
err_code = nrf_ble_qwrs_init(&qwrs_init, &m_qwrs);
RESP_OK("%s, nrf_ble_qwrs_init, err_code = %lu\n", __func__, err_code);
//APP_ERROR_CHECK(err_code);
return err_code;
}
// ============================= ble_app_queued_writes, End =============================
/**@brief Function for initializing the Connection Parameters module.
*/
__WEAK void conn_params_init(void)
{
uint32_t err_code;
ble_conn_params_init_t cp_init;
memset(&cp_init, 0, sizeof(cp_init));
cp_init.p_conn_params = NULL;
cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
cp_init.next_conn_params_update_delay = NEXT_CONN_PARAMS_UPDATE_DELAY;
cp_init.max_conn_params_update_count = MAX_CONN_PARAMS_UPDATE_COUNT;
cp_init.start_on_notify_cccd_handle = BLE_GATT_HANDLE_INVALID;
cp_init.disconnect_on_fail = false;
cp_init.evt_handler = on_conn_params_evt;
cp_init.error_handler = conn_params_error_handler;
err_code = ble_conn_params_init(&cp_init);
APP_ERROR_CHECK(err_code);
}
/**@brief Function for handling BLE events.
*
* @param[in] p_ble_evt Bluetooth stack event.
* @param[in] p_context Unused.
*/
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
ret_code_t err_code = NRF_SUCCESS;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_DISCONNECTED:
RESP_OK("Disconnected.");
m_is_connected = false;
m_conn_handle = BLE_CONN_HANDLE_INVALID;
break;
case BLE_GAP_EVT_CONNECTED:
RESP_OK("Connected.");
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
m_is_connected = true;
RESP_OK("%s, p_ble_evt->header.evt_id = %d, IS_CENTRAL = %d, m_conn_handle = %04X, p_ble_evt->evt.gap_evt.conn_handle = %04X",
__func__, p_ble_evt->header.evt_id, IS_CENTRAL, m_conn_handle, p_ble_evt->evt.gap_evt.conn_handle);
// ============================= ble_app_queued_writes, Start =============================
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
RESP_OK("%s, err_code = %lu", __func__, err_code);
// ============================= ble_app_queued_writes, End =============================
#ifndef PERIPHERAL_ONLY
#if 0 //CEI
//not Start service discovery at this time, it should trigger by spresense/examples/bluetooth_le_central/bluetooth_le_central_main.c
if (IS_CENTRAL) {
// Start service discovery
//APP_ERROR_CHECK(ble_db_discovery_start(&m_ble_db_discovery, p_ble_evt->evt.gap_evt.conn_handle));
memset(&m_ble_db_discovery, 0x00, sizeof(m_ble_db_discovery));
err_code = ble_db_discovery_start(&m_ble_db_discovery, p_ble_evt->evt.gap_evt.conn_handle);
}
#endif
#endif
break;
#if defined(S132)
case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
{
RESP_OK("PHY update request.");
ble_gap_phys_t const phys =
{
.rx_phys = BLE_GAP_PHY_AUTO,
.tx_phys = BLE_GAP_PHY_AUTO,
};
err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
APP_ERROR_CHECK(err_code);
} break;
#endif
case BLE_GATTC_EVT_TIMEOUT:
// Disconnect on GATT Client timeout event.
RESP_OK("GATT Client Timeout.");
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
break;
case BLE_GATTS_EVT_TIMEOUT:
// Disconnect on GATT Server timeout event.
RESP_OK("GATT Server Timeout.");
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
break;
case BLE_EVT_USER_MEM_REQUEST:
err_code = sd_ble_user_mem_reply(p_ble_evt->evt.gattc_evt.conn_handle, NULL);
APP_ERROR_CHECK(err_code);
break;
case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
{
ble_gatts_evt_rw_authorize_request_t req;
ble_gatts_rw_authorize_reply_params_t auth_reply;
req = p_ble_evt->evt.gatts_evt.params.authorize_request;
if (req.type != BLE_GATTS_AUTHORIZE_TYPE_INVALID)
{
if ((req.request.write.op == BLE_GATTS_OP_PREP_WRITE_REQ) ||
(req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) ||
(req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL))
{
if (req.type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
{
auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
}
else
{
auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_READ;
}
auth_reply.params.write.gatt_status = APP_FEATURE_NOT_SUPPORTED;
err_code = sd_ble_gatts_rw_authorize_reply(p_ble_evt->evt.gatts_evt.conn_handle,
&auth_reply);
APP_ERROR_CHECK(err_code);
}
}
} break; // BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST
case BLE_GATTS_EVT_HVN_TX_COMPLETE:
m_buffer_available = true;
break;
#ifndef PERIPHERAL_ONLY
case BLE_GAP_EVT_ADV_REPORT: {
ble_evt_t *p = (ble_evt_t*)p_ble_evt;
ble_gap_evt_t *p_gap_evt = &p->evt.gap_evt;
if (m_adv_report_cb) m_adv_report_cb(&p_gap_evt->params.adv_report);
break;
}
#endif
case BLE_GAP_EVT_TIMEOUT: {
const ble_gap_evt_t *p_gap_evt = &p_ble_evt->evt.gap_evt;
if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_SCAN) {
RESP_ERROR("Scan timed out");
} else if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN) {
RESP_ERROR("Connection Request timed out");
}
break;
}
default:
// No implementation needed.
break;
}
#ifndef PERIPHERAL_ONLY
if (IS_CENTRAL) {
ble_db_discovery_on_ble_evt(p_ble_evt, &m_ble_db_discovery);
#ifndef BLE_WRAP_NO_NUS
ble_nus_c_on_ble_evt(p_ble_evt, &m_ble_nus_c);
#endif
}
#endif
on_ble_evt((ble_evt_t*)p_ble_evt);
}
/**@brief Function for initializing the BLE stack.
*
* @details Initializes the SoftDevice and the BLE event interrupt.
*/
__WEAK void ble_stack_init()
{
ret_code_t err_code;
err_code = nrf_sdh_enable_request();
APP_ERROR_CHECK(err_code);
// Configure the BLE stack using the default settings.
// Fetch the start address of the application RAM.
uint32_t ram_start = 0;
err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
APP_ERROR_CHECK(err_code);
//RESP_OK("%s, nrf_sdh_ble_default_cfg_set() = %lX", __func__, err_code);
// Enable BLE stack.
err_code = nrf_sdh_ble_enable(&ram_start);
APP_ERROR_CHECK(err_code);
//RESP_OK("%s, nrf_sdh_ble_enable() = %lX", __func__, err_code);
// Enabling and populating the DC/DC converter,
err_code = sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE);
//RESP_OK("%s, sd_power_dcdc_mode_set() = %lX", __func__, err_code);
// Register a handler for BLE events.
NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}
/**@brief Function for the Peer Manager initialization.
*/
__WEAK void peer_manager_init(void)
{
ble_gap_sec_params_t sec_param;
ret_code_t err_code;
err_code = pm_init();
APP_ERROR_CHECK(err_code);
memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));
// Security parameters to be used for all security procedures.
sec_param.bond = SEC_PARAM_BOND;
sec_param.mitm = SEC_PARAM_MITM;
sec_param.lesc = SEC_PARAM_LESC;
sec_param.keypress = SEC_PARAM_KEYPRESS;
sec_param.io_caps = SEC_PARAM_IO_CAPABILITIES;
sec_param.oob = SEC_PARAM_OOB;
sec_param.min_key_size = SEC_PARAM_MIN_KEY_SIZE;
sec_param.max_key_size = SEC_PARAM_MAX_KEY_SIZE;
sec_param.kdist_own.enc = 1;
sec_param.kdist_own.id = 1;
sec_param.kdist_peer.enc = 1;
sec_param.kdist_peer.id = 1;
err_code = pm_sec_params_set(&sec_param);
APP_ERROR_CHECK(err_code);
err_code = pm_register(pm_evt_handler);
APP_ERROR_CHECK(err_code);
}
/**@brief Clear bond information from persistent storage.
*/
static void delete_bonds(void)
{
ret_code_t err_code;
NRF_LOG_INFO("Erase bonds!");
err_code = pm_peers_delete();
APP_ERROR_CHECK(err_code);
}
// ============================= End of Nordic template code =============================
void ble_wrap_erase_bonds() {
delete_bonds();
}
uint32_t _millis() {
return (uint32_t)((uint64_t)NRF_RTC0->COUNTER*1000/APP_TIMER_CLOCK_FREQ);
}
__WEAK void ble_log_init()
{
static bool done = false;
if (done) return;
ret_code_t err_code = NRF_LOG_INIT(_millis);
APP_ERROR_CHECK(err_code);
NRF_LOG_DEFAULT_BACKENDS_INIT();
done = true;
}
#if !defined(PERIPHERAL_ONLY) && !defined(BLE_WRAP_NO_NUS)
static void ble_nus_c_evt_handler(ble_nus_c_t * p_ble_nus_c, const ble_nus_c_evt_t * p_ble_nus_evt) {
uint32_t err_code;
switch (p_ble_nus_evt->evt_type) {
case BLE_NUS_C_EVT_DISCOVERY_COMPLETE:
// UART service detected
err_code = ble_nus_c_handles_assign(p_ble_nus_c, p_ble_nus_evt->conn_handle, &p_ble_nus_evt->handles);
APP_ERROR_CHECK(err_code);
err_code = ble_nus_c_tx_notif_enable(p_ble_nus_c);
APP_ERROR_CHECK(err_code);
LOG_DEBUG("Service detected");
if (m_nus_c_rx_cb)
m_nus_c_rx_cb(NULL, 1);
break;
case BLE_NUS_C_EVT_NUS_TX_EVT:
// UART response received
if (m_nus_c_rx_cb)
m_nus_c_rx_cb(p_ble_nus_evt->p_data, p_ble_nus_evt->data_len);
break;
case BLE_NUS_C_EVT_DISCONNECTED:
if (m_nus_c_rx_cb)
m_nus_c_rx_cb(NULL, 0);
break;
}
}
static void nus_c_init(void) {
ble_nus_c_init_t nus_c_init_t;
nus_c_init_t.evt_handler = ble_nus_c_evt_handler;
APP_ERROR_CHECK(ble_nus_c_init(&m_ble_nus_c, &nus_c_init_t));
}
#endif
#ifndef PERIPHERAL_ONLY
__WEAK void discovery_handler(ble_db_discovery_evt_t *p_evt) {}
static void db_disc_handler(ble_db_discovery_evt_t * p_evt) {
#ifndef BLE_WRAP_NO_NUS
ble_nus_c_on_db_disc_evt(&m_ble_nus_c, p_evt);
#endif
discovery_handler(p_evt);
}
#endif
//--------------------------------------------------------------------------
void ble_wrap_init(const char *device_name) {
if (strlen(device_name) == 0)
m_ble_role = eBLE_Central;
//printf("%s, %d\n", __func__, __LINE__);
#ifndef PERIPHERAL_ONLY
if (IS_CENTRAL) {
APP_ERROR_CHECK(ble_db_discovery_init(db_disc_handler));
}
#endif
ble_log_init();
timers_init();
ble_stack_init();
gap_params_init(CONFIG_BLUETOOTH_LE_NAME);
gatt_init();
services_init();
conn_params_init();
peer_manager_init();
scan_init();
#ifdef INCLUDE_BATT_MEAS
init_battery_measurement();
#endif
#if !defined(PERIPHERAL_ONLY) && !defined(BLE_WRAP_NO_NUS)
if (IS_CENTRAL) {
nus_c_init();
}
#endif
check_set_pub_mac_addr();
ble_gap_addr_t bt_addr;
sd_ble_gap_addr_get(&bt_addr);
LOG_INFO("Mac address: %s", ble_wrap_addr2str(&bt_addr));
RESP_OK("%s, bt_addr = %02X:%02X:%02X:%02X:%02X:%02X, m_ble_role = %02X",
__func__,
bt_addr.addr[5], bt_addr.addr[4], bt_addr.addr[3],
bt_addr.addr[2], bt_addr.addr[1], bt_addr.addr[0],
m_ble_role);
//nrf_delay_ms(1000);
}
void ble_wrap_teardown() {
m_is_connected = false;
nrf_sdh_disable_request();
}
static bool m_is_connectable = true;
void ble_wrap_set_connectable(bool on) {
m_is_connectable = on;
}
bool ble_wrap_advertise(uint16_t company_id, ble_advdata_name_type_t type,
uint8_t *p_data, uint8_t size,
uint32_t interval_ms, uint32_t timeout_s) {
RESP_OK("%s, m_is_connected = %d, m_is_advertising = %d, interval_ms = %lu, m_is_connectable = %d",
__func__, m_is_connected, m_is_advertising, interval_ms, m_is_connectable);
if (m_is_connected) return false;
if (m_is_advertising && sd_ble_gap_adv_stop(m_advertising.adv_handle) != NRF_SUCCESS) return false;
if (!interval_ms) {
m_is_advertising = false;
return true;
}
if (m_is_connectable)
{
ble_advertising_init_t init;
memset(&init, 0, sizeof(init));
init.advdata.name_type = type;
init.advdata.include_appearance = true; // extra add, 2019/12/16
init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
ble_advdata_manuf_data_t manuf_specific_data;
if (p_data != NULL)
{
manuf_specific_data.company_identifier = company_id;
manuf_specific_data.data.p_data = p_data;
manuf_specific_data.data.size = size;
init.advdata.p_manuf_specific_data = &manuf_specific_data;
}
#ifdef ADVERTISE_SERVICE_UUIDS
init.advdata.uuids_complete.uuid_cnt = SERVICE_UUID_CNT;
init.advdata.uuids_complete.p_uuids = m_adv_uuids;
#endif
init.config.ble_adv_fast_enabled = true;
init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;// MSEC_TO_UNITS(interval_ms, UNIT_0_625_MS);
init.config.ble_adv_fast_timeout = APP_ADV_DURATION;// timeout_s;
init.evt_handler = ble_wrap_on_adv_evt;
APP_ERROR_CHECK(ble_advertising_init(&m_advertising, &init));
ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
}
else
{
static ble_gap_adv_params_t m_adv_params;
ble_advdata_t advdata;
memset(&advdata, 0, sizeof(advdata));
ble_advdata_manuf_data_t manuf_specific_data;
if (p_data != NULL)
{
manuf_specific_data.company_identifier = company_id;
manuf_specific_data.data.p_data = p_data;
manuf_specific_data.data.size = size;
advdata.p_manuf_specific_data = &manuf_specific_data;
}
advdata.name_type = type;
advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
#if NRF_SDK_VERSION == 15
ble_advdata_encode(&advdata, m_adv_data.adv_data.p_data, &m_adv_data.adv_data.len);
//APP_ERROR_CHECK(err_code);
memset(&m_adv_params, 0, sizeof(m_adv_params));
m_adv_params.p_peer_addr = NULL;
m_adv_params.filter_policy = BLE_GAP_ADV_FP_ANY;
m_adv_params.interval = MSEC_TO_UNITS(interval_ms, UNIT_0_625_MS);
m_adv_params.properties.type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED;
m_adv_params.filter_policy = BLE_GAP_ADV_FP_ANY;
m_adv_params.duration = timeout_s;
sd_ble_gap_adv_set_configure(&m_adv_handle, &m_adv_data, &m_adv_params);
sd_ble_gap_adv_start(m_adv_handle, APP_BLE_CONN_CFG_TAG);
#else
ble_advdata_set(&advdata, NULL);
memset(&m_adv_params, 0, sizeof(m_adv_params));
m_adv_params.p_peer_addr = NULL;
m_adv_params.filter_policy = BLE_GAP_ADV_FP_ANY;
m_adv_params.interval = MSEC_TO_UNITS(interval_ms, UNIT_0_625_MS);
m_adv_params.type = BLE_GAP_ADV_TYPE_ADV_NONCONN_IND;
m_adv_params.fp = BLE_GAP_ADV_FP_ANY;
m_adv_params.timeout = timeout_s;
sd_ble_gap_adv_start(&m_adv_params, APP_BLE_CONN_CFG_TAG);
#endif
m_is_advertising = true;
}
// For now, api above return invalid state after first call...
return true;
}
uint32_t ble_wrap_stop_advertise() {
RESP_OK("%s, m_is_connected = %d, m_is_advertising = %d, m_is_connectable = %d, m_advertising.adv_handle = %X",
__func__, m_is_connected, m_is_advertising, m_is_connectable, m_advertising.adv_handle);
(void) sd_ble_gap_adv_stop(m_advertising.adv_handle);
m_advertising.adv_modes_config.ble_adv_fast_timeout = 1;// timeout_s;
ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
return NRF_SUCCESS;
}