BLE Object Transfer Service OACP advice?

jcwren
Posts: 3
Joined: Mon May 29, 2017 2:22 am

BLE Object Transfer Service OACP advice?

Postby jcwren » Tue Jun 19, 2018 1:47 am

I've got a BLE profile working nicely, and I'm trying to add Object Transfer Service support for firmware updates. There's one piece (so far...) that I'm having a really difficult time with, and that's how to specify the OACP characteristic table. The BT specs are really opaque, at least to me. Below is the table I'm using. When I use the Android nrfConnect application, the service is found, and the characteristics are read, but I know the OACP entries aren't correct. They're the two entries at the very end.

Am I supposed to create multiple OTS_IDX_OBJECT_ACTION_CONTROL_POINT_CHARACTERISTIC/VALUE entries for each OACP function supported (create, delete, read, write, checksum, etc)?

Any advice would be greatly appreciated. I've looked at code in the nrf51 SDK, and the TI code base, but they're so different in architecture that I haven't been able to draw a parallel for how the characteristic tables are created.

Code: Select all

static otsFeature_t   ots_feature_value =
{
  .oacp = OTS_OACP_CREATE | OTS_OACP_DELETE | OTS_OACP_CHECKSUM | OTS_OACP_READ | OTS_OACP_WRITE | OTS_OACP_TRUNCATE,
  .olcp = 0,
};
static uint8_t        ots_name_value [8]                = {'F', 'i', 'r', 'm', 'w', 'a', 'r', 'e'};
static uint8_t        ots_type_value [ESP_UUID_LEN_128] = {0x14, 0xdc, 0x86, 0x4f, 0xba, 0x52, 0xea, 0x83, 0x12, 0x44, 0xf9, 0x49, 0xce, 0x33, 0xdd, 0xbf};
static otsSize_t      ots_size_value                    = {1024, 1024};
static uint32_t       ots_properties_value              = OTS_PROPERTIES_DELETE | OTS_PROPERTIES_READ | OTS_PROPERTIES_WRITE | OTS_PROPERTIES_TRUNCATE;
static uint8_t        ots_acp_value []                  = {OTS_OPCODE_CREATE, OTS_OPCODE_DELETE, OTS_OPCODE_CHECKSUM, OTS_OPCODE_READ, OTS_OPCODE_WRITE, OTS_OPCODE_ABORT, OTS_OPCODE_RESPONSE};

static const uint8_t  ots_service_uuid [ESP_UUID_LEN_128] = GATT_UUID_OBJECT_TRANSFER_SERVICE;

static const uint16_t ots_feature_uuid    = GATT_UUID_OBJECT_FEATURE;               // 0x2abd
static const uint16_t ots_name_uuid       = GATT_UUID_OBJECT_NAME;                  // 0x2abe
static const uint16_t ots_type_uuid       = GATT_UUID_OBJECT_TYPE;                  // 0x2abf
static const uint16_t ots_size_uuid       = GATT_UUID_OBJECT_SIZE;                  // 0x2ac0
static const uint16_t ots_properties_uuid = GATT_UUID_OBJECT_PROPERTIES;            // 0x2ac4
static const uint16_t ots_acp_uuid        = GATT_UUID_OBJECT_ACTION_CONTROL_POINT;  // 0x2ac5

static const esp_gatts_attr_db_t ots_gatt_db [OTS_IDX_NB] =
{
  [OTS_IDX_SERVICE] =
  {
    .attr_control.auto_rsp = ESP_GATT_AUTO_RSP,
    .att_desc.uuid_length  = sizeof (primary_service_uuid),
    .att_desc.uuid_p       = (uint8_t *) &primary_service_uuid,
    .att_desc.perm         = ESP_GATT_PERM_READ,
    .att_desc.max_length   = sizeof (ots_service_uuid),
    .att_desc.length       = sizeof (ots_service_uuid),
    .att_desc.value        = (uint8_t *) &ots_service_uuid,
  },

  [OTS_IDX_OBJECT_FEATURE_CHARACTERISTIC] =
  {
    .attr_control.auto_rsp = ESP_GATT_AUTO_RSP,
    .att_desc.uuid_length  = sizeof (characteristic_declaration_uuid),
    .att_desc.uuid_p       = (uint8_t *) &characteristic_declaration_uuid,
    .att_desc.perm         = ESP_GATT_PERM_READ,
    .att_desc.max_length   = CHARACTERISTIC_DECLARATION_SIZE,
    .att_desc.length       = CHARACTERISTIC_DECLARATION_SIZE,
    .att_desc.value        = (uint8_t *) &characteristic_property_read,
  },

  [OTS_IDX_OBJECT_FEATURE_VALUE] =
  {
    .attr_control.auto_rsp = ESP_GATT_AUTO_RSP,
    .att_desc.uuid_length  = sizeof (ots_feature_uuid),
    .att_desc.uuid_p       = (uint8_t *) &ots_feature_uuid,
    .att_desc.perm         = ESP_GATT_PERM_READ,
    .att_desc.max_length   = sizeof (ots_feature_value),
    .att_desc.length       = sizeof (ots_feature_value),
    .att_desc.value        = (uint8_t *) &ots_feature_value,
  },

  [OTS_IDX_OBJECT_NAME_CHARACTERISTIC] =
  {
    .attr_control.auto_rsp = ESP_GATT_AUTO_RSP,
    .att_desc.uuid_length  = sizeof (characteristic_declaration_uuid),
    .att_desc.uuid_p       = (uint8_t *) &characteristic_declaration_uuid,
    .att_desc.perm         = ESP_GATT_PERM_READ,
    .att_desc.max_length   = CHARACTERISTIC_DECLARATION_SIZE,
    .att_desc.length       = CHARACTERISTIC_DECLARATION_SIZE,
    .att_desc.value        = (uint8_t *) &characteristic_property_read,
  },

  [OTS_IDX_OBJECT_NAME_VALUE] =
  {
    .attr_control.auto_rsp = ESP_GATT_AUTO_RSP,
    .att_desc.uuid_length  = sizeof (ots_name_uuid),
    .att_desc.uuid_p       = (uint8_t *) &ots_name_uuid,
    .att_desc.perm         = ESP_GATT_PERM_READ,
    .att_desc.max_length   = sizeof (ots_name_value),
    .att_desc.length       = sizeof (ots_name_value),
    .att_desc.value        = (uint8_t *) &ots_name_value,
  },

  [OTS_IDX_OBJECT_TYPE_CHARACTERISTIC] =
  {
    .attr_control.auto_rsp = ESP_GATT_AUTO_RSP,
    .att_desc.uuid_length  = sizeof (characteristic_declaration_uuid),
    .att_desc.uuid_p       = (uint8_t *) &characteristic_declaration_uuid,
    .att_desc.perm         = ESP_GATT_PERM_READ,
    .att_desc.max_length   = CHARACTERISTIC_DECLARATION_SIZE,
    .att_desc.length       = CHARACTERISTIC_DECLARATION_SIZE,
    .att_desc.value        = (uint8_t *) &characteristic_property_read,
  },

  [OTS_IDX_OBJECT_TYPE_VALUE] =
  {
    .attr_control.auto_rsp = ESP_GATT_AUTO_RSP,
    .att_desc.uuid_length  = sizeof (ots_type_uuid),
    .att_desc.uuid_p       = (uint8_t *) &ots_type_uuid,
    .att_desc.perm         = ESP_GATT_PERM_READ,
    .att_desc.max_length   = sizeof (ots_type_value),
    .att_desc.length       = sizeof (ots_type_value),
    .att_desc.value        = (uint8_t *) &ots_type_value,
  },

  [OTS_IDX_OBJECT_SIZE_CHARACTERISTIC] =
  {
    .attr_control.auto_rsp = ESP_GATT_AUTO_RSP,
    .att_desc.uuid_length  = sizeof (characteristic_declaration_uuid),
    .att_desc.uuid_p       = (uint8_t *) &characteristic_declaration_uuid,
    .att_desc.perm         = ESP_GATT_PERM_READ,
    .att_desc.max_length   = CHARACTERISTIC_DECLARATION_SIZE,
    .att_desc.length       = CHARACTERISTIC_DECLARATION_SIZE,
    .att_desc.value        = (uint8_t *) &characteristic_property_read,
  },

  [OTS_IDX_OBJECT_SIZE_VALUE] =
  {
    .attr_control.auto_rsp = ESP_GATT_AUTO_RSP,
    .att_desc.uuid_length  = sizeof (ots_size_uuid),
    .att_desc.uuid_p       = (uint8_t *) &ots_size_uuid,
    .att_desc.perm         = ESP_GATT_PERM_READ,
    .att_desc.max_length   = sizeof (ots_size_value),
    .att_desc.length       = sizeof (ots_size_value),
    .att_desc.value        = (uint8_t *) &ots_size_value,
  },

  [OTS_IDX_OBJECT_PROPERTIES_CHARACTERISTIC] =
  {
    .attr_control.auto_rsp = ESP_GATT_AUTO_RSP,
    .att_desc.uuid_length  = sizeof (characteristic_declaration_uuid),
    .att_desc.uuid_p       = (uint8_t *) &characteristic_declaration_uuid,
    .att_desc.perm         = ESP_GATT_PERM_READ,
    .att_desc.max_length   = CHARACTERISTIC_DECLARATION_SIZE,
    .att_desc.length       = CHARACTERISTIC_DECLARATION_SIZE,
    .att_desc.value        = (uint8_t *) &characteristic_property_read,
  },

  [OTS_IDX_OBJECT_PROPERTIES_VALUE] =
  {
    .attr_control.auto_rsp = ESP_GATT_AUTO_RSP,
    .att_desc.uuid_length  = sizeof (ots_properties_uuid),
    .att_desc.uuid_p       = (uint8_t *) &ots_properties_uuid,
    .att_desc.perm         = ESP_GATT_PERM_READ,
    .att_desc.max_length   = sizeof (ots_properties_value),
    .att_desc.length       = sizeof (ots_properties_value),
    .att_desc.value        = (uint8_t *) &ots_properties_value,
  },

  [OTS_IDX_OBJECT_ACTION_CONTROL_POINT_CHARACTERISTIC] =
  {
    .attr_control.auto_rsp = ESP_GATT_AUTO_RSP,
    .att_desc.uuid_length  = sizeof (characteristic_declaration_uuid),
    .att_desc.uuid_p       = (uint8_t *) &characteristic_declaration_uuid,
    .att_desc.perm         = ESP_GATT_PERM_READ,
    .att_desc.max_length   = CHARACTERISTIC_DECLARATION_SIZE,
    .att_desc.length       = CHARACTERISTIC_DECLARATION_SIZE,
    .att_desc.value        = (uint8_t *) &characteristic_property_read,
  },

  [OTS_IDX_OBJECT_ACTION_CONTROL_POINT_VALUE] =
  {
    .attr_control.auto_rsp = ESP_GATT_AUTO_RSP,
    .att_desc.uuid_length  = sizeof (ots_acp_uuid),
    .att_desc.uuid_p       = (uint8_t *) &ots_acp_uuid,
    .att_desc.perm         = ESP_GATT_PERM_READ,
    .att_desc.max_length   = sizeof (ots_acp_value),
    .att_desc.length       = sizeof (ots_acp_value),
    .att_desc.value        = (uint8_t *) &ots_acp_value,
  },

};

chegewara
Posts: 530
Joined: Wed Jun 14, 2017 9:00 pm

Re: BLE Object Transfer Service OACP advice?

Postby chegewara » Wed Jun 20, 2018 7:31 am

Sorry for maybe stupid question, but have you study OTS_v10.pdf? OTS document:
https://www.bluetooth.com/specifications/gatt

From quick look at this document, there is chapter 3.3.2 and sub-chapters. For example to create object you have to send op-code 0x01 with additional parameters and then await indication with op-code 0x60 and success/error code:
The Object Action Control Point is used by a Client to control certain behaviors of the Server. With the
exception of the Create procedure which creates a new object, the OACP procedures affect the Current
Object only.
The procedures are triggered by writing a value that includes an Op Code specifying the operation and this
may be followed by a Parameter that is valid within the context of that Op Code (see Table 3.9). For each
of the procedures described in the next sections, the Server shall indicate the OACP characteristic with the
Response Code Op Code (0x60) along with the Request Op Code and “Success” or other appropriate
Result Code contained in the response as listed in Table 3.11.

jcwren
Posts: 3
Joined: Mon May 29, 2017 2:22 am

Re: BLE Object Transfer Service OACP advice?

Postby jcwren » Wed Jun 20, 2018 2:10 pm

Yes, I did see that, but it wasn't clear to me if a service need only one OACP descriptor, or one for each operation. Part of that lack of understanding was exacerbated by there being no OTS support in the esp-idf libraries. I found a couple of BT devices that do support OTS, and they all only had a single OACP descriptor.

Now I'm stuck on how the L2CAP channel gets established for OACP read and write operations. The Nordic nrf51 SDK supports OTS with a nice system of callbacks and such, but the esp-idf SDK hasn't had that equivalent implemented yet.

Who is online

Users browsing this forum: michprev and 13 guests