This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Understanding Zephyr example samples/bluetooth/peripheral

I need to build a simple BLE dc motor control with a potentiometer to provide the rotational position of the motor. 

Anyways, my first step is understanding the Nordic libraries.  I opened up the Zephyr peripheral sample and have a few questions.

Here is a code excerpt.

/* Vendor Primary Service Declaration */
BT_GATT_SERVICE_DEFINE(vnd_svc,
 BT_GATT_PRIMARY_SERVICE(&vnd_uuid),
 BT_GATT_CHARACTERISTIC(&vnd_enc_uuid.uuid,
          BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE |
          BT_GATT_CHRC_INDICATE,
          BT_GATT_PERM_READ_ENCRYPT |
          BT_GATT_PERM_WRITE_ENCRYPT,
          read_vnd, write_vnd, vnd_value),
 BT_GATT_CCC(vnd_ccc_cfg_changed,
      BT_GATT_PERM_READ | BT_GATT_PERM_WRITE_ENCRYPT),
 BT_GATT_CHARACTERISTIC(&vnd_auth_uuid.uuid,
          BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
          BT_GATT_PERM_READ_AUTHEN |
          BT_GATT_PERM_WRITE_AUTHEN,
          read_vnd, write_vnd, vnd_value),
 BT_GATT_CHARACTERISTIC(&vnd_long_uuid.uuid, BT_GATT_CHRC_READ |
          BT_GATT_CHRC_WRITE | BT_GATT_CHRC_EXT_PROP,
          BT_GATT_PERM_READ | BT_GATT_PERM_WRITE |
          BT_GATT_PERM_PREPARE_WRITE,
          read_long_vnd, write_long_vnd, &vnd_long_value),
 BT_GATT_CEP(&vnd_long_cep),
 BT_GATT_CHARACTERISTIC(&vnd_signed_uuid.uuid, BT_GATT_CHRC_READ |
          BT_GATT_CHRC_WRITE | BT_GATT_CHRC_AUTH,
          BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
          read_signed, write_signed, &signed_value),
 BT_GATT_CHARACTERISTIC(&vnd_write_cmd_uuid.uuid,
          BT_GATT_CHRC_WRITE_WITHOUT_RESP,
          BT_GATT_PERM_WRITE, NULL,
          write_without_rsp_vnd, &vnd_value),
);
static ssize_t read_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr,
   void *buf, u16_t len, u16_t offset)
{
 const char *value = attr->user_data;


 return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
     strlen(value));
}


static ssize_t write_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr,
    const void *buf, u16_t len, u16_t offset,
    u8_t flags)
{
 u8_t *value = attr->user_data;


 if (offset + len > sizeof(vnd_value)) {
  return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
 }


 memcpy(value + offset, buf, len);

 return len;
}

Question #1

The code defines various custom characteristics for a custom service.  I assume this is just giving an example of what is possible and that those UUID's can be set to any value. Or do vnd_enc and vnd_auth have some specific meaning?

Question #2

Right now reading and writing to those custom attributes are set to callback functions such as read_vnd above.  I see the value to read and return to the client is set from the attr->user_data. However, no value is being read when I try to read that characteristic via the LightBlue app.

What is it's value? When you read a characteristic you do not pass in any arguments so I am confused about this.

Question #3

In the write_vnd function I assume the attr->user_data is the value the client wants to write to this characteristic, but I could be wrong. However, I don't understand why the code

is writing to buf. What is the purpose of it?

  • Question #4

    The function below is the callback for the characteristic ending in F3.  When I connect to the device and read this characteristic, I get a really long value.  What is this value? Where is it coming from?  Also, I changed the value of *value = "foo";, but this foo was not returned when I did another read.

    static ssize_t read_signed(struct bt_conn *conn, const struct bt_gatt_attr *attr,
          void *buf, u16_t len, u16_t offset)
    {
     const char *value = attr->user_data; //"foo";
     return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
         sizeof(signed_value));
    }
  • Hi,

    Q1. The UUID can be any value. But you need to generate it from an UUID generator. For example this: https://www.uuidgenerator.net/

    I don't think vnd_enc and vnd_auth have any specific meaning, they are only example UUIDs used in the example. 

    Q2. In the read call back (e.g read_vnd) you can choose if you want to change the value attribute of the attribute before you answer to the read request from the peer. In the example there isn't any change and we simply assign the value to the actual value of the attribute (user_data) and response to the read command with this value. 
    Could you please try to test again with nRF Connect if you see any return ? 

    Q3. When you receive a write callback, it's only the buffer contain the data for the write, the user_data doesn't get written yet. In the write_vnd() you can find that we use memcpy to write to the user_data (which is used as the value of the characteristic). If you don't do anything in the callback, the data from the client will not be written to anywhere. 

    Q4. Please double check again with NRF Connect. What I'm seeing here is 4 bytes when I'm trying to read the characteristic which match with the size of int. Please make sure you bonded with the device when you test this. 

Related