Hello.
I'm trying to control a GoPro camera via ble. Gopro has documentation that describes the available services and chars, as well as the exchange protocol (https://gopro.github.io/OpenGoPro/ble/).
At the moment I am trying to send a command to the camera and get a response from it. I need to write the command in the characteristic 0x0072 of the service 0xFEA6, and get the response in the notification of the characteristic 0x0073.
I used the ble-nus-c example (for central device) and modified it a little. I posted the full code on GitHub https://github.com/Sergey1560/nrf52_gopro/tree/master
If I understand correctly, in my case, service 0xFEA6 is type 1 (BLE_UUID_TYPE_BLE), and the characteristics of this are type 2 (BLE_UUID_TYPE_VENDOR_BEGIN).
Init function:
#define GOPRO_BASE_UUID {{0x1b, 0xc5, 0xd5, 0xa5, 0x02, 0x00, 0x46, 0x90, 0xe3, 0x11, 0x8d, 0xaa, 0x00, 0x00, 0xf9, 0xb5}} /**< Used vendor-specific UUID. */
#define BLE_UUID16_GOPRO_SERVICE 0xFEA6
uint32_t ble_gopro_init(ble_nus_c_t * p_ble_nus_c, ble_nus_c_init_t * p_ble_nus_c_init)
{
uint32_t err_code;
ble_uuid_t uart_uuid;
ble_uuid128_t nus_base_uuid = GOPRO_BASE_UUID;
VERIFY_PARAM_NOT_NULL(p_ble_nus_c);
VERIFY_PARAM_NOT_NULL(p_ble_nus_c_init);
VERIFY_PARAM_NOT_NULL(p_ble_nus_c_init->p_gatt_queue);
err_code = sd_ble_uuid_vs_add(&nus_base_uuid, &p_ble_nus_c->uuid_type);
VERIFY_SUCCESS(err_code);
uart_uuid.type = BLE_UUID_TYPE_BLE;
uart_uuid.uuid = BLE_UUID16_GOPRO_SERVICE;
NRF_LOG_DEBUG("Register uuid 0x%0X type %d",uart_uuid.uuid,uart_uuid.type);
p_ble_nus_c->conn_handle = BLE_CONN_HANDLE_INVALID;
p_ble_nus_c->evt_handler = p_ble_nus_c_init->evt_handler;
p_ble_nus_c->error_handler = p_ble_nus_c_init->error_handler;
p_ble_nus_c->handles.command_tx_handle = BLE_GATT_HANDLE_INVALID;
p_ble_nus_c->handles.command_rx_handle = BLE_GATT_HANDLE_INVALID;
p_ble_nus_c->p_gatt_queue = p_ble_nus_c_init->p_gatt_queue;
return ble_db_discovery_evt_register(&uart_uuid);
}
.
Discovery event function:
#define BLE_UUID_GOPRO_COMMAND_TX_CHARACTERISTIC 0x0072
#define BLE_UUID_GOPRO_COMMAND_RX_CHARACTERISTIC 0x0073
void ble_nus_c_on_db_disc_evt(ble_nus_c_t * p_ble_nus_c, ble_db_discovery_evt_t * p_evt)
{
ble_nus_c_evt_t nus_c_evt;
memset(&nus_c_evt,0,sizeof(ble_nus_c_evt_t));
ble_gatt_db_char_t * p_chars = p_evt->params.discovered_db.charateristics;
NRF_LOG_DEBUG("DB discovery evt Type: %d uuid 0x%0X uuid type: %d",p_evt->evt_type,p_evt->params.discovered_db.srv_uuid.uuid,p_evt->params.discovered_db.srv_uuid.type);
if ( (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE)
&& (p_evt->params.discovered_db.srv_uuid.uuid == BLE_UUID16_GOPRO_SERVICE))
// && (p_evt->params.discovered_db.srv_uuid.type == p_ble_nus_c->uuid_type))
{
for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
{
nrf_delay_ms(100); //to prevent log overflow
NRF_LOG_INFO("Discovery %d / %d char 0x%0X handle 0x%0X",i, p_evt->params.discovered_db.char_count,p_chars[i].characteristic.uuid.uuid,p_chars[i].characteristic.handle_value);
switch (p_chars[i].characteristic.uuid.uuid)
{
case BLE_UUID_GOPRO_COMMAND_TX_CHARACTERISTIC:
NRF_LOG_DEBUG("TX handle: 0x%0X",p_chars[i].characteristic.handle_value);
nus_c_evt.handles.command_tx_handle = p_chars[i].characteristic.handle_value;
break;
case BLE_UUID_GOPRO_COMMAND_RX_CHARACTERISTIC:
NRF_LOG_DEBUG("RX handle: 0x%0X cccd: 0x%0X",p_chars[i].characteristic.handle_value,p_chars[i].cccd_handle);
nus_c_evt.handles.command_rx_handle = p_chars[i].characteristic.handle_value;
nus_c_evt.handles.command_rx_cccd_handle = p_chars[i].cccd_handle;
break;
default:
break;
}
}
if (p_ble_nus_c->evt_handler != NULL)
{
nus_c_evt.conn_handle = p_evt->conn_handle;
nus_c_evt.evt_type = BLE_NUS_C_EVT_DISCOVERY_COMPLETE;
p_ble_nus_c->evt_handler(p_ble_nus_c, &nus_c_evt);
}
}
}
Now, I can scan for camera (scan for device with service 0xFEA6), found it and connect. Connection log:
<info> app_timer: RTC: initialized. <debug> app: Register uuid 0xFEA6 type 1 <info> app: INIT strcut type: 2 <debug> ble_scan: Added filter on UUID FEA6 <info> app: BLE UART central example started. <debug> ble_scan: Scanning <debug> app: Scan not found <debug> ble_scan: Scanning <debug> app: Scan not found <debug> ble_scan: Scanning <debug> app: Scan not found <debug> ble_scan: Scanning <debug> app: Scan not found <debug> ble_scan: Scanning <debug> app: Scan not found <debug> ble_scan: Scanning <debug> ble_scan: Connecting <debug> ble_scan: Connection status: 0 <debug> app: Scan filter match <debug> ble_scan: Scanning <info> app: Connecting to target C0:3D:0B:EF:2F:77 <debug> nrf_ble_gatt: Requesting to update ATT MTU to 247 bytes on connection 0x0. <debug> nrf_ble_gatt: Updating data length to 251 on connection 0x0. <info> app: BLE EVT Connected <debug> app: Assign handles RX: 0x400 CCCD 0x2000 <debug> nrf_ble_gq: Registering connection handle: 0x0000 <info> app: Start discovery <debug> ble_db_disc: Starting discovery of service with UUID 0xFEA6 on connection handle 0x0. <debug> nrf_ble_gatt: Data length updated to 251 on connection 0x0. <debug> nrf_ble_gatt: max_rx_octets: 251 <debug> nrf_ble_gatt: max_tx_octets: 251 <debug> nrf_ble_gatt: max_rx_time: 2120 <debug> nrf_ble_gatt: max_tx_time: 2120 <debug> nrf_ble_gq: Processing the request queue... <debug> nrf_ble_gq: GATTC Primary Service Discovery Request <debug> nrf_ble_gq: SD GATT procedure (2) succeeded on connection handle: 0. <debug> nrf_ble_gatt: ATT MTU updated to 247 bytes on connection 0x0 (response). <info> app: ATT MTU exchange completed. <info> app: Ble NUS max data length set to 0xF4(244) <debug> nrf_ble_gq: Processing the request queue... <debug> ble_db_disc: Found service UUID 0xFEA6. <debug> nrf_ble_gq: Adding item to the request queue <debug> nrf_ble_gq: GATTC Characteristic Discovery Request <debug> nrf_ble_gq: SD GATT procedure (3) succeeded on connection handle: 0. <debug> nrf_ble_gq: Processing the request queue... <debug> nrf_ble_gq: Adding item to the request queue <debug> nrf_ble_gq: GATTC Characteristic Discovery Request <debug> nrf_ble_gq: SD GATT procedure (3) succeeded on connection handle: 0. <debug> nrf_ble_gq: Processing the request queue... <debug> nrf_ble_gq: Adding item to the request queue <debug> nrf_ble_gq: GATTC Characteristic Descriptor Request <debug> nrf_ble_gq: SD GATT procedure (4) succeeded on connection handle: 0. <debug> nrf_ble_gq: Processing the request queue... <debug> nrf_ble_gq: Adding item to the request queue <debug> nrf_ble_gq: GATTC Characteristic Descriptor Request <debug> nrf_ble_gq: SD GATT procedure (4) succeeded on connection handle: 0. <debug> nrf_ble_gq: Processing the request queue... <debug> nrf_ble_gq: Adding item to the request queue <debug> nrf_ble_gq: GATTC Characteristic Descriptor Request <debug> nrf_ble_gq: SD GATT procedure (4) succeeded on connection handle: 0. <debug> nrf_ble_gq: Processing the request queue... <debug> nrf_ble_gq: Adding item to the request queue <debug> nrf_ble_gq: GATTC Characteristic Descriptor Request <debug> nrf_ble_gq: SD GATT procedure (4) succeeded on connection handle: 0. <debug> nrf_ble_gq: Processing the request queue... <debug> ble_db_disc: Discovery of service with UUID 0xFEA6 completed with success on connection handle 0x0. <debug> app: DB discovery evt Type: 0 uuid 0xFEA6 uuid type: 1 <info> app: Discovery 0 / 8 char 0x72 handle 0x38 <debug> app: TX handle: 0x38 <info> app: Discovery 1 / 8 char 0x73 handle 0x3A <debug> app: RX handle: 0x3A cccd: 0x3B <info> app: Discovery 2 / 8 char 0x74 handle 0x3D <info> app: Discovery 3 / 8 char 0x75 handle 0x3F <info> app: Discovery 4 / 8 char 0x76 handle 0x42 <info> app: Discovery 5 / 8 char 0x77 handle 0x44 <info> app: Discovery 6 / 8 char 0x78 handle 0x47 <info> app: Discovery 7 / 8 char 0x79 handle 0x49 <info> app: NUS EVT Discovery complete. <debug> app: Assign handles RX: 0x3A CCCD 0x3B <debug> app: Enable notify RX handles: 0x0 0x3B <info> app: CCCD Config
All chars are discovered. I try to write command to char 0x0072 with handle 0x38:
uint32_t ble_gopro_cmd_send(ble_nus_c_t * p_ble_nus_c, uint8_t * p_cmd, uint16_t length)
{
VERIFY_PARAM_NOT_NULL(p_ble_nus_c);
nrf_ble_gq_req_t write_req;
memset(&write_req, 0, sizeof(nrf_ble_gq_req_t));
if (length > BLE_NUS_MAX_DATA_LEN)
{
NRF_LOG_WARNING("Content too long.");
return NRF_ERROR_INVALID_PARAM;
}
if (p_ble_nus_c->conn_handle == BLE_CONN_HANDLE_INVALID)
{
NRF_LOG_WARNING("Connection handle invalid.");
return NRF_ERROR_INVALID_STATE;
}
NRF_LOG_DEBUG("Send %d bytes data on handle 0x%0X",length,p_ble_nus_c->handles.command_tx_handle);
NRF_LOG_HEXDUMP_DEBUG(p_cmd, length);
write_req.type = NRF_BLE_GQ_REQ_GATTC_WRITE;
write_req.error_handler.cb = gatt_error_handler;
write_req.error_handler.p_ctx = p_ble_nus_c;
write_req.params.gattc_write.handle = p_ble_nus_c->handles.command_tx_handle;
write_req.params.gattc_write.len = length;
write_req.params.gattc_write.offset = 0;
write_req.params.gattc_write.p_value = p_cmd;
write_req.params.gattc_write.write_op = BLE_GATT_OP_WRITE_REQ;
write_req.params.gattc_write.flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE;
return nrf_ble_gq_item_add(p_ble_nus_c->p_gatt_queue, &write_req, p_ble_nus_c->conn_handle);
}
Log:
<info> app: Button 0 <debug> app: Send 4 bytes data on handle 0x38 <debug> app: 03 01 01 01 |.... <debug> nrf_ble_gq: Adding item to the request queue <debug> nrf_ble_gq: GATTC Write Request <debug> nrf_ble_gq: SD GATT procedure (1) succeeded on connection handle: 0. <info> app: Send ret: 0 <debug> nrf_ble_gq: Processing the request queue...
But the camera does not respond to the command.
I used a sniffer to see what is happening and compare the command that I send from the phone via nRF Connect and from my code.
From nRF Connect:

From my code:

The difference is that when writing from my code, service 0xFEA6 is not specified in Handle 0x38, only UUID . That's why I get an error reply from camera.
What am I doing wrong, why is the service not specified when writing?
I use old SDK v17.1.0, but I think the problem is not in SDK, but in my code. New SDK uses Zephyr OS and switching to it takes time.

