You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@mynewt.apache.org by "David G. Simmons" <sa...@mac.com> on 2016/11/28 20:42:01 UTC

BLE Questions

Hi All,

Hope all my American cohorts had a great Thanksgiving! 

I'm working on a BLE app for MyNewt, and have been going through the code and docs trying to figure out how a subscribed characteristic gets updated. I'm looking through the bleprph app code for hints. 

It's my understanding from that code that the callback function for all the characteristics is registered in the gatt_srv table, and that in e example they all have the same callback function, namely gatt_svr_chr_access_alert. So I looked there for further help. And indeed, in gatt_svr_chr_access_alert(), I see what is supposed to happen when the various characteristics are accessed.

case GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID:
        assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
        rc = os_mbuf_append(ctxt->om, &gatt_svr_new_alert_cat,
                            sizeof gatt_svr_new_alert_cat);
        return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;

is called when I read the GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID characteristic -- and I've verified this via a connection through LightBlue.  GATT_SVR_CHR_NEW_ALERT is a 'subscribable' UUID, and I would expect that 

case GATT_SVR_CHR_NEW_ALERT:
        if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
            rc = gatt_svr_chr_write(ctxt->om, 0,
                                    sizeof gatt_svr_new_alert_val,
                                    gatt_svr_new_alert_val,
                                    &gatt_svr_new_alert_val_len);
            return rc;
        } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
            rc = os_mbuf_append(ctxt->om, &gatt_svr_new_alert_val,
                                sizeof gatt_svr_new_alert_val);
            return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
        }

would get called when I subscribe to this characteristic. But it does not. 

So, if I want to have a subscribable service characteristic, How do I implement that, and where does the characteristic value get updated so that it is propagated to the connected device (if any)? 

Thanks.
dg
--
David G. Simmons
(919) 534-5099
Web <https://davidgs.com/> • Blog <https://davidgs.com/davidgs_blog> • Linkedin <http://linkedin.com/in/davidgsimmons> • Twitter <http://twitter.com/TechEvangelist1> • GitHub <http://github.com/davidgs>
/** Message digitally signed for security and authenticity.  
* If you cannot read the PGP.sig attachment, please go to 
 * http://www.gnupg.com/ <http://www.gnupg.com/> Secure your email!!!
 * Public key available at keyserver.pgp.com <http://keyserver.pgp.com/>
**/
♺ This email uses 100% recycled electrons. Don't blow it by printing!

There are only 2 hard things in computer science: Cache invalidation, naming things, and off-by-one errors.



Re: BLE Questions

Posted by Christopher Collins <cc...@apache.org>.
Argh... I messed up the call to ble_gatts_chr_updated().  I forgot to
pass the characteristic handle to this function.  The corrected code is
below:

    /* Update characteristic value. */
    gatt_svr_new_alert_val[0] = 10;
    gatt_svr_new_alert_val[1] = 20;
    gatt_svr_new_alert_val[2] = 30;
    gatt_svr_new_alert_val[3] = 40;
    gatt_svr_new_alert_val_len = 4

    /* Look up characteristic handle. */
    uint16_t chr_val_handle;
    rc = ble_gatts_find_chr(BLE_UUID16(GATT_SVR_SVC_ALERT_UUID),
                            BLE_UUID16(GATT_SVR_CHR_NEW_ALERT),
                            NULL, &chr_val_handle);
    assert(rc == 0);


    ble_gatts_chr_updated(chr_val_handle);

Sorry about that,
Chris

On Mon, Nov 28, 2016 at 03:01:52PM -0800, Christopher Collins wrote:
> On Mon, Nov 28, 2016 at 04:33:23PM -0500, David G. Simmons wrote:
> > Thanks to both of you for hugely helpful answers! 
> > 
> > Now, the last bit ... 
> > 
> > Where is the value -- or the variable, more accurately -- associated
> > with a characteristic defined? So that I know which one to update. :-) 
> 
> The source of the characteristic data is determined by the GATT callback
> function.  In English, your app defines a variable to hold the
> characteristic value, and the callback retrieves this variable when the
> characteristic is read.  To change the value of the characteristic,
> write to the source variable that the callback reads from.
> 
> Looking at bleprph's alert notification service callback:
> 
>     case GATT_SVR_CHR_NEW_ALERT:
>         if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
>             rc = gatt_svr_chr_write(ctxt->om, 0,
>                                     sizeof gatt_svr_new_alert_val,
>                                     gatt_svr_new_alert_val,
>                                     &gatt_svr_new_alert_val_len);
>             return rc;
>         } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
>             rc = os_mbuf_append(ctxt->om, &gatt_svr_new_alert_val,
>                                 sizeof gatt_svr_new_alert_val);
>             return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
>         }
> 
> This callback uses the "gatt_svr_new_alert_val" variable to hold the
> "new alert" characteristic value.  So, if you wanted send a notification
> with the four-byte value {10,20,30,40} to to all subscribed peers, you
> might do something like this (not compiled!):
> 
>     /* Update characteristic value. */
>     gatt_svr_new_alert_val[0] = 10;
>     gatt_svr_new_alert_val[1] = 20;
>     gatt_svr_new_alert_val[2] = 30;
>     gatt_svr_new_alert_val[3] = 40;
>     gatt_svr_new_alert_val_len = 4
> 
>     /* Look up characteristic handle. */
>     uint16_t chr_val_handle;
>     rc = ble_gatts_find_chr(BLE_UUID16(GATT_SVR_SVC_ALERT_UUID),
>                             BLE_UUID16(GATT_SVR_CHR_NEW_ALERT),
>                             NULL, &chr_val_handle);
>     assert(rc == 0);
> 
> 
>     ble_gatts_chr_updated();
> 
> I hope that clears things up.  If not, I probably am not explaining it
> well, so please don't hesitate to ask again :).
> 
> There are a few secondary issues this email raises that I should mention
> for completeness:
> 
> 1. Using ble_gatts_find_chr() is not the preferred way to determine a
> characteristic's attribute handle.  Rather, it is better to discover
> this information at registration time via the "val_handle" pointer in
> the characteristic definition.  For an example of how this is done, see
> the New Alert characteristic definition in
> net/nimble/host/services/ans/src/ble_svc_ans.c.
> 
> 2. The above point leads to this one: why does bleprph re-implement the
> Alert Notification Service when there is already an ANS Mynewt package?!
> This is my screw-up; I merged the ANS pull request without removing the
> duplicate implementation from the sample apps.
> 
> Thanks,
> Chris
> 
> > 
> > dg
> > 
> > > On Nov 28, 2016, at 4:30 PM, Christopher Collins <cc...@apache.org> wrote:
> > > 
> > > As long as you specify one of the BLE_GATT_CHR_F_NOTIFY or
> > > BLE_GATT_CHR_F_INDICATE flags in the characteristic definition, the
> > > characteristic will be subscribable.
> > > 
> > > When a peer subscribes to a characteristic, the stack signals this event
> > > to the application via the GAP event callback associated with the peer.
> > > When a peer unsubscribes, this is signalled via the same mechanism.  For
> > > rationale on why the GAP event callback is used here, see this email:
> > > https://lists.apache.org/thread.html/8706f7914d2b716a02c25bdfc57fe942f7c7fca82446dd3523014d43@%3Cdev.mynewt.apache.org%3E <https://lists.apache.org/thread.html/8706f7914d2b716a02c25bdfc57fe942f7c7fca82446dd3523014d43@%3Cdev.mynewt.apache.org%3E>
> > > 
> > > As Mike indicated, your application tells the stack that a
> > > characteristic's value has changed via the ble_gatts_chr_updated()
> > > function.  It is up to the application to actually change the value
> > > prior to calling this function.
> > > 
> > > If you are not sure which handle is associated with a particular
> > > characteristic, you can perform a lookup by
> > > service-characteristic-UUID-pair using the ble_gatts_find_chr()
> > > function.
> > 
> > --
> > David G. Simmons
> > (919) 534-5099
> > Web <https://davidgs.com/> \u2022 Blog <https://davidgs.com/davidgs_blog> \u2022 Linkedin <http://linkedin.com/in/davidgsimmons> \u2022 Twitter <http://twitter.com/TechEvangelist1> \u2022 GitHub <http://github.com/davidgs>
> > /** Message digitally signed for security and authenticity.  
> > * If you cannot read the PGP.sig attachment, please go to 
> >  * http://www.gnupg.com/ <http://www.gnupg.com/> Secure your email!!!
> >  * Public key available at keyserver.pgp.com <http://keyserver.pgp.com/>
> > **/
> > \u267a This email uses 100% recycled electrons. Don't blow it by printing!
> > 
> > There are only 2 hard things in computer science: Cache invalidation, naming things, and off-by-one errors.
> > 
> > 

Re: BLE Questions

Posted by Christopher Collins <cc...@apache.org>.
On Mon, Nov 28, 2016 at 04:33:23PM -0500, David G. Simmons wrote:
> Thanks to both of you for hugely helpful answers! 
> 
> Now, the last bit ... 
> 
> Where is the value -- or the variable, more accurately -- associated
> with a characteristic defined? So that I know which one to update. :-) 

The source of the characteristic data is determined by the GATT callback
function.  In English, your app defines a variable to hold the
characteristic value, and the callback retrieves this variable when the
characteristic is read.  To change the value of the characteristic,
write to the source variable that the callback reads from.

Looking at bleprph's alert notification service callback:

    case GATT_SVR_CHR_NEW_ALERT:
        if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
            rc = gatt_svr_chr_write(ctxt->om, 0,
                                    sizeof gatt_svr_new_alert_val,
                                    gatt_svr_new_alert_val,
                                    &gatt_svr_new_alert_val_len);
            return rc;
        } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
            rc = os_mbuf_append(ctxt->om, &gatt_svr_new_alert_val,
                                sizeof gatt_svr_new_alert_val);
            return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
        }

This callback uses the "gatt_svr_new_alert_val" variable to hold the
"new alert" characteristic value.  So, if you wanted send a notification
with the four-byte value {10,20,30,40} to to all subscribed peers, you
might do something like this (not compiled!):

    /* Update characteristic value. */
    gatt_svr_new_alert_val[0] = 10;
    gatt_svr_new_alert_val[1] = 20;
    gatt_svr_new_alert_val[2] = 30;
    gatt_svr_new_alert_val[3] = 40;
    gatt_svr_new_alert_val_len = 4

    /* Look up characteristic handle. */
    uint16_t chr_val_handle;
    rc = ble_gatts_find_chr(BLE_UUID16(GATT_SVR_SVC_ALERT_UUID),
                            BLE_UUID16(GATT_SVR_CHR_NEW_ALERT),
                            NULL, &chr_val_handle);
    assert(rc == 0);


    ble_gatts_chr_updated();

I hope that clears things up.  If not, I probably am not explaining it
well, so please don't hesitate to ask again :).

There are a few secondary issues this email raises that I should mention
for completeness:

1. Using ble_gatts_find_chr() is not the preferred way to determine a
characteristic's attribute handle.  Rather, it is better to discover
this information at registration time via the "val_handle" pointer in
the characteristic definition.  For an example of how this is done, see
the New Alert characteristic definition in
net/nimble/host/services/ans/src/ble_svc_ans.c.

2. The above point leads to this one: why does bleprph re-implement the
Alert Notification Service when there is already an ANS Mynewt package?!
This is my screw-up; I merged the ANS pull request without removing the
duplicate implementation from the sample apps.

Thanks,
Chris

> 
> dg
> 
> > On Nov 28, 2016, at 4:30 PM, Christopher Collins <cc...@apache.org> wrote:
> > 
> > As long as you specify one of the BLE_GATT_CHR_F_NOTIFY or
> > BLE_GATT_CHR_F_INDICATE flags in the characteristic definition, the
> > characteristic will be subscribable.
> > 
> > When a peer subscribes to a characteristic, the stack signals this event
> > to the application via the GAP event callback associated with the peer.
> > When a peer unsubscribes, this is signalled via the same mechanism.  For
> > rationale on why the GAP event callback is used here, see this email:
> > https://lists.apache.org/thread.html/8706f7914d2b716a02c25bdfc57fe942f7c7fca82446dd3523014d43@%3Cdev.mynewt.apache.org%3E <https://lists.apache.org/thread.html/8706f7914d2b716a02c25bdfc57fe942f7c7fca82446dd3523014d43@%3Cdev.mynewt.apache.org%3E>
> > 
> > As Mike indicated, your application tells the stack that a
> > characteristic's value has changed via the ble_gatts_chr_updated()
> > function.  It is up to the application to actually change the value
> > prior to calling this function.
> > 
> > If you are not sure which handle is associated with a particular
> > characteristic, you can perform a lookup by
> > service-characteristic-UUID-pair using the ble_gatts_find_chr()
> > function.
> 
> --
> David G. Simmons
> (919) 534-5099
> Web <https://davidgs.com/> \u2022 Blog <https://davidgs.com/davidgs_blog> \u2022 Linkedin <http://linkedin.com/in/davidgsimmons> \u2022 Twitter <http://twitter.com/TechEvangelist1> \u2022 GitHub <http://github.com/davidgs>
> /** Message digitally signed for security and authenticity.  
> * If you cannot read the PGP.sig attachment, please go to 
>  * http://www.gnupg.com/ <http://www.gnupg.com/> Secure your email!!!
>  * Public key available at keyserver.pgp.com <http://keyserver.pgp.com/>
> **/
> \u267a This email uses 100% recycled electrons. Don't blow it by printing!
> 
> There are only 2 hard things in computer science: Cache invalidation, naming things, and off-by-one errors.
> 
> 

Re: BLE Questions

Posted by "David G. Simmons" <sa...@mac.com>.
Thanks to both of you for hugely helpful answers! 

Now, the last bit ... 

Where is the value -- or the variable, more accurately -- associated with a characteristic defined? So that I know which one to update. :-) 

dg

> On Nov 28, 2016, at 4:30 PM, Christopher Collins <cc...@apache.org> wrote:
> 
> As long as you specify one of the BLE_GATT_CHR_F_NOTIFY or
> BLE_GATT_CHR_F_INDICATE flags in the characteristic definition, the
> characteristic will be subscribable.
> 
> When a peer subscribes to a characteristic, the stack signals this event
> to the application via the GAP event callback associated with the peer.
> When a peer unsubscribes, this is signalled via the same mechanism.  For
> rationale on why the GAP event callback is used here, see this email:
> https://lists.apache.org/thread.html/8706f7914d2b716a02c25bdfc57fe942f7c7fca82446dd3523014d43@%3Cdev.mynewt.apache.org%3E <https://lists.apache.org/thread.html/8706f7914d2b716a02c25bdfc57fe942f7c7fca82446dd3523014d43@%3Cdev.mynewt.apache.org%3E>
> 
> As Mike indicated, your application tells the stack that a
> characteristic's value has changed via the ble_gatts_chr_updated()
> function.  It is up to the application to actually change the value
> prior to calling this function.
> 
> If you are not sure which handle is associated with a particular
> characteristic, you can perform a lookup by
> service-characteristic-UUID-pair using the ble_gatts_find_chr()
> function.

--
David G. Simmons
(919) 534-5099
Web <https://davidgs.com/> • Blog <https://davidgs.com/davidgs_blog> • Linkedin <http://linkedin.com/in/davidgsimmons> • Twitter <http://twitter.com/TechEvangelist1> • GitHub <http://github.com/davidgs>
/** Message digitally signed for security and authenticity.  
* If you cannot read the PGP.sig attachment, please go to 
 * http://www.gnupg.com/ <http://www.gnupg.com/> Secure your email!!!
 * Public key available at keyserver.pgp.com <http://keyserver.pgp.com/>
**/
♺ This email uses 100% recycled electrons. Don't blow it by printing!

There are only 2 hard things in computer science: Cache invalidation, naming things, and off-by-one errors.



Re: BLE Questions

Posted by Christopher Collins <cc...@apache.org>.
Hi David,

Mike wrote a good reply, but a few things that were said are based on an
older version of the nimble stack.  Thankfully, some parts have improved
since 0.9.0.  My response is below.

On Mon, Nov 28, 2016 at 03:42:01PM -0500, David G. Simmons wrote:
[...]
> GATT_SVR_CHR_NEW_ALERT is a 'subscribable' UUID, and I would expect that 
> 
> case GATT_SVR_CHR_NEW_ALERT:
>         if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
>             rc = gatt_svr_chr_write(ctxt->om, 0,
>                                     sizeof gatt_svr_new_alert_val,
>                                     gatt_svr_new_alert_val,
>                                     &gatt_svr_new_alert_val_len);
>             return rc;
>         } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
>             rc = os_mbuf_append(ctxt->om, &gatt_svr_new_alert_val,
>                                 sizeof gatt_svr_new_alert_val);
>             return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
>         }
> 
> would get called when I subscribe to this characteristic. But it does not. 
> 
> So, if I want to have a subscribable service characteristic, How do I
> implement that, and where does the characteristic value get updated so
> that it is propagated to the connected device (if any)? 

As long as you specify one of the BLE_GATT_CHR_F_NOTIFY or
BLE_GATT_CHR_F_INDICATE flags in the characteristic definition, the
characteristic will be subscribable.

When a peer subscribes to a characteristic, the stack signals this event
to the application via the GAP event callback associated with the peer.
When a peer unsubscribes, this is signalled via the same mechanism.  For
rationale on why the GAP event callback is used here, see this email:
https://lists.apache.org/thread.html/8706f7914d2b716a02c25bdfc57fe942f7c7fca82446dd3523014d43@%3Cdev.mynewt.apache.org%3E

As Mike indicated, your application tells the stack that a
characteristic's value has changed via the ble_gatts_chr_updated()
function.  It is up to the application to actually change the value
prior to calling this function.

If you are not sure which handle is associated with a particular
characteristic, you can perform a lookup by
service-characteristic-UUID-pair using the ble_gatts_find_chr()
function.

Thanks,
Chris

Re: BLE Questions

Posted by Mike Ryan <mi...@lacklustre.net>.
On Mon, Nov 28, 2016 at 03:42:01PM -0500, David G. Simmons wrote:
> It's my understanding from that code that the callback function for all the characteristics is registered in the gatt_srv table, and that in e example they all have the same callback function, namely gatt_svr_chr_access_alert. So I looked there for further help. And indeed, in gatt_svr_chr_access_alert(), I see what is supposed to happen when the various characteristics are accessed.
> 
> case GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID:
>         assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
>         rc = os_mbuf_append(ctxt->om, &gatt_svr_new_alert_cat,
>                             sizeof gatt_svr_new_alert_cat);
>         return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
> 
> is called when I read the GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID characteristic -- and I've verified this via a connection through LightBlue.  GATT_SVR_CHR_NEW_ALERT is a 'subscribable' UUID, and I would expect that 
> 
> case GATT_SVR_CHR_NEW_ALERT:
>         if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
>             rc = gatt_svr_chr_write(ctxt->om, 0,
>                                     sizeof gatt_svr_new_alert_val,
>                                     gatt_svr_new_alert_val,
>                                     &gatt_svr_new_alert_val_len);
>             return rc;
>         } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
>             rc = os_mbuf_append(ctxt->om, &gatt_svr_new_alert_val,
>                                 sizeof gatt_svr_new_alert_val);
>             return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
>         }
> 
> would get called when I subscribe to this characteristic. But it does not. 
> 
> So, if I want to have a subscribable service characteristic, How do I implement that, and where does the characteristic value get updated so that it is propagated to the connected device (if any)? 

I implemented several Notify characteristics for HID over GATT here:

https://github.com/mikeryan/SlideQuacker/blob/master/apps/quacker/src/gatt_svr.c#L146-L196

The READ and WRITE CHR callbacks aren't called when you "subscribed"
because the writes are against the CCC Descriptor. You roughly don't
have to worry about when that happens, and at least at the time I wrote
this there was no way to register a callback for when they're written
to.

To send updates to the client, you first update the characteristic value
in memory and then call ble_gatts_chr_updated() with the characteristic
handle number. Example:

https://github.com/mikeryan/SlideQuacker/blob/master/apps/quacker/src/main.c#L376-L377

I don't think there's a good way to turn a UUID into a handle number
from firmware. I looked it up using gatttool and hard coded it into my
app.