14 KiB
title | description | guide_group | order | platforms | related_docs | |||||
---|---|---|---|---|---|---|---|---|---|---|
Talking To Smartstraps | Information on how to use the Pebble C API to talk to a connected smartstrap. | smartstraps | 3 |
|
|
To talk to a connected smartstrap, the Smartstrap
API is used to establish a
connection and exchange arbitrary data. The exchange protocol is specified in
{% guide_link smartstraps/smartstrap-protocol %} and most of
it is abstracted by the SDK. This also includes handling of the
{% guide_link smartstraps/smartstrap-protocol#link-control-profile "Link Control Profile" %}.
Read {% guide_link smartstraps/talking-to-pebble %} to learn how to use an example library for popular Arduino microcontrollers to implement the smartstrap side of the protocol.
Note: Apps running on multiple hardware platforms that may or may not include a smartstrap connector should use the
PBL_SMARTSTRAP
compile-time define (as well as checking API return values) to gracefully handle when it is not available.
Services and Attributes
Generic Service Profile
The Pebble smartstrap protocol uses the concept of 'services' and 'attributes' to organize the exchange of data between the watch and the smartstrap. Services are identified by a 16-bit number. Some of these service identifiers have a specific meaning; developers should read {% guide_link smartstraps/smartstrap-protocol#supported-services-and-attributes "Supported Services and Attributes" %} for a complete list of reserved service IDs and ranges of service IDs that can be used for experimentation.
Attributes are also identified by a 16-bit number. The meaning of attribute values is specific to the service of that attribute. The smartstrap protocol defines the list of attributes for some services, but developers are free to define their own list of attributes in their own services.
This abstraction supports read and write operations on any attribute as well as sending notifications from the strap when an attribute value changes. This is called the Generic Service Profile and is the recommended way to exchange data with smartstraps.
Raw Data Service
Developers can also choose to use the Raw Data Service to minimize the overhead
associated with transmitting data. To use this profile a Pebble developer will
use the same APIs described in this guide with the service ID and attribute ID
set to SMARTSTRAP_RAW_DATA_SERVICE_ID
and
SMARTSTRAP_RAW_DATA_ATTRIBUTE_ID
SDK constants respectively.
Manipulating Attributes
The Smartstrap
API uses the SmartstrapAttribute
type as a proxy for an
attribute on the smartstrap. It includes the service ID of the attribute, the ID
of the attribute itself, as well as a data buffer that is used to store the
latest read or written value of the attribute.
Before you can read or write an attribute, you need to initialize a
SmartstrapAttribute
that will be used as a proxy for the attribute on the
smartstrap. The first step developers should take is to decide upon and define
their services and attributes:
// Define constants for your service ID, attribute ID
// and buffer size of your attribute.
static const SmartstrapServiceId s_service_id = 0x1001;
static const SmartstrapAttributeId s_attribute_id = 0x0001;
static const int s_buffer_length = 64;
Then, define the attribute globally:
// Declare an attribute pointer
static SmartstrapAttribute *s_attribute;
Lastly create the attribute during app initialization, allocating its buffer:
// Create the attribute, and allocate a buffer for its data
s_attribute = smartstrap_attribute_create(s_service_id, s_attribute_id,
s_buffer_length);
Later on, APIs such as smartstrap_attribute_get_service_id()
and
smartstrap_attribute_get_attribute_id()
can be used to confirm these values
for any SmartstrapAttribute
created previously. This is useful if an app
deals with more than one service or attribute.
Attributes can also be destroyed when an app is exiting or no longer requires
them by using smartstrap_attribute_destroy()
:
// Destroy this attribute
smartstrap_attribute_destroy(s_attribute);
Connecting to a Smartstrap
The first thing a smartstrap-enabled app should do is call
smartstrap_subscribe()
to register the handler functions (described below)
that will be called when smartstrap-related events occur. Such events can be one
of four types.
The SmartstrapServiceAvailabilityHandler
handler, used when a smartstrap
reports that a service is available, or has become unavailable.
static void strap_availability_handler(SmartstrapServiceId service_id,
bool is_available) {
// A service's availability has changed
APP_LOG(APP_LOG_LEVEL_INFO, "Service %d is %s available",
(int)service_id, is_available ? "now" : "NOT");
}
See below under Writing Data and Reading Data for explanations of the other callback types.
With all four of these handlers in place, the subscription to the associated events can be registered.
// Subscribe to the smartstrap events
smartstrap_subscribe((SmartstrapHandlers) {
.availability_did_change = strap_availability_handler,
.did_read = strap_read_handler,
.did_write = strap_write_handler,
.notified = strap_notify_handler
});
As with the other [Event Services
](Event Service
), the subscription can be
removed at any time:
// Stop getting callbacks
smartstrap_unsubscribe();
The availability of a service can be queried at any time:
if(smartstrap_service_is_available(s_service_id)) {
// Our service is available!
} else {
// Our service is not currently available, handle gracefully
APP_LOG(APP_LOG_LEVEL_ERROR, "Service %d is not available.", (int)s_service_id);
}
Writing Data
The smartstrap communication model (detailed under {% guide_link smartstraps/smartstrap-protocol#communication-model "Communication Model" %}) uses the master-slave principle. This one-way relationship means that Pebble can request data from the smartstrap at any time, but the smartstrap cannot. However, the smartstrap may notify the watch that data is waiting to be read so that the watch can read that data at the next opportunity.
To send data to a smartstrap an app must call
smartstrap_attribute_begin_write()
which will return a buffer to write into.
When the app is done preparing the data to be sent in the buffer, it calls
smartstrap_attribute_end_write()
to actually send the data.
// Pointer to the attribute buffer
size_t buff_size;
uint8_t *buffer;
// Begin the write request, getting the buffer and its length
smartstrap_attribute_begin_write(attribute, &buffer, &buff_size);
// Store the data to be written to this attribute
snprintf((char*)buffer, buff_size, "Hello, smartstrap!");
// End the write request, and send the data, not expecting a response
smartstrap_attribute_end_write(attribute, buff_size, false);
Another message cannot be sent until the strap responds (a
did_write
callback for Write requests, ordid_read
for Read/Write+Read requests) or the timeout expires. Doing so will cause the API to returnSmartstrapResultBusy
. Read {% guide_link smartstraps/talking-to-smartstraps#timeouts "Timeouts" %} for more information on smartstrap timeouts.
The SmartstrapWriteHandler
will be called when the smartstrap has
acknowledged the write operation (if using the Raw Data Service, there
is no acknowledgement and the callback will be called when Pebble is done
sending the frame to the smartstrap). If a read is requested (with the
request_read
parameter of smartstrap_attribute_end_write()
) then the read
callback will also be called when the smartstrap sends the attribute value.
static void strap_write_handler(SmartstrapAttribute *attribute,
SmartstrapResult result) {
// A write operation has been attempted
if(result != SmartstrapResultOk) {
// Handle the failure
APP_LOG(APP_LOG_LEVEL_ERROR, "Smartstrap error occured: %s",
smartstrap_result_to_string(result));
}
}
If a timeout occurs on a non-raw-data write request (with the request_read
parameter set to false
), SmartstrapResultTimeOut
will be passed to the
did_write
handler on the watch side.
Reading Data
The simplest way to trigger a read request is to call
smartstrap_attribute_read()
. Another way to trigger a read is to set the
request_read
parameter of smartstrap_attribute_end_write()
to true
. In
both cases, the response will be received asynchronously and the
SmartstrapReadHandler
will be called when it is received.
static void strap_read_handler(SmartstrapAttribute *attribute,
SmartstrapResult result, const uint8_t *data,
size_t length) {
if(result == SmartstrapResultOk) {
// Data has been read into the data buffer provided
APP_LOG(APP_LOG_LEVEL_INFO, "Smartstrap sent: %s", (char*)data);
} else {
// Some error has occured
APP_LOG(APP_LOG_LEVEL_ERROR, "Error in read handler: %d", (int)result);
}
}
static void read_attribute() {
SmartstrapResult result = smartstrap_attribute_read(attribute);
if(result != SmartstrapResultOk) {
APP_LOG(APP_LOG_LEVEL_ERROR, "Error reading attribute: %s",
smartstrap_result_to_string(result));
}
}
Note:
smartstrap_attribute_begin_write()
may not be called within adid_read
handler (SmartstrapResultBusy
will be returned).
Similar to write requests, if a timeout occurs when making a read request the
did_read
handler will be called with SmartstrapResultTimeOut
passed in the
result
parameter.
Receiving Notifications
To save as much power as possible, the notification mechanism can be used by the
smartstrap to alert the watch when there is data that requires processing. When
this happens, the SmartstrapNotifyHandler
handler is called with the
appropriate attribute provided. Developers can use this mechanism to allow the
watch to sleep until it is time to read data from the smartstrap, or simply as a
messsaging mechanism.
static void strap_notify_handler(SmartstrapAttribute *attribute) {
// The smartstrap has emitted a notification for this attribute
APP_LOG(APP_LOG_LEVEL_INFO, "Attribute with ID %d sent notification",
(int)smartstrap_attribute_get_attribute_id(attribute));
// Some data is ready, let's read it
smartstrap_attribute_read(attribute);
}
Callbacks For Each Type of Request
There are a few different scenarios that involve the SmartstrapReadHandler
and SmartstrapWriteHandler
, where the callbacks to these
SmartstrapHandlers
will change depending on the type of request made by the
watch.
Request Type | Callback Sequence |
---|---|
Read only | did_write when the request is sent. did_read when the response arrives or an error (e.g.: a timeout) occurs. |
Write+Read request | did_write when the request is sent. did_read when the response arrives or an error (e.g.: a timeout) occurs. |
Write (Raw Data Service) | did_write when the request is sent. |
Write (any other service) | did_write when the write request is acknowledged by the smartstrap. |
For Write requests only, did_write
will be called when the attribute is ready
for another request, and for Reads/Write+Read requests did_read
will be called
when the attribute is ready for another request.
Timeouts
Read requests and write requests to an attribute expect a response from the smartstrap and will generate a timeout error if the strap does not respond before the expiry of the timeout.
The maximum timeout value supported is 1000ms, with the default value
SMARTSTRAP_TIMEOUT_DEFAULT
of 250ms. A smaller or larger value can be
specified by the developer:
// Set a timeout of 500ms
smartstrap_set_timeout(500);
Smartstrap Results
When data is sent to the smartstrap, one of several results is possible. These
are returned by various API functions (such as smartstrap_attribute_read()
),
and are enumerated as follows:
Result | Value | Description |
---|---|---|
SmartstrapResultOk |
0 |
No error occured. |
SmartstrapResultInvalidArgs |
1 |
The arguments provided were invalid. |
SmartstrapResultNotPresent |
2 |
The Smartstrap port is not present on this watch. |
SmartstrapResultBusy |
3 |
The connection is currently busy. For example, this can happen if the watch is waiting for a response from the smartstrap. |
SmartstrapResultServiceUnavailable |
4 |
Either a smartstrap is not connected or the connected smartstrap does not support the specified service. |
SmartstrapResultAttributeUnsupported |
5 |
The smartstrap reported that it does not support the requested attribute. |
SmartstrapResultTimeOut |
6 |
A timeout occured during the request. |
The function shown below returns a human-readable string for each value, useful for debugging.
static char* smartstrap_result_to_string(SmartstrapResult result) {
switch(result) {
case SmartstrapResultOk:
return "SmartstrapResultOk";
case SmartstrapResultInvalidArgs:
return "SmartstrapResultInvalidArgs";
case SmartstrapResultNotPresent:
return "SmartstrapResultNotPresent";
case SmartstrapResultBusy:
return "SmartstrapResultBusy";
case SmartstrapResultServiceUnavailable:
return "SmartstrapResultServiceUnavailable";
case SmartstrapResultAttributeUnsupported:
return "SmartstrapResultAttributeUnsupported";
case SmartstrapResultTimeOut:
return "SmartstrapResultTimeOut";
default:
return "Not a SmartstrapResult value!";
}
}