pebble/devsite/source/_guides/smartstraps/talking-to-pebble.md
2025-02-24 18:58:29 -08:00

8.2 KiB

title description guide_group order related_docs related_examples
Talking To Pebble Information on how to implement the smartstrap protocol to talk to the Pebble accessory port. smartstraps 2
Smartstrap
title url
Smartstrap Button Counter https://github.com/pebble-examples/smartstrap-button-counter
title url
Smartstrap Library Test https://github.com/pebble-examples/smartstrap-library-test

In order to communicate successfully with Pebble, the smartstrap hardware must correctly implement the smartstrap protocol as defined in {% guide_link smartstraps/smartstrap-protocol %}.

Arduino Library

For developers prototyping with some of the most common Arduino boards (based on the AVR ATmega 32U4, 2560, 328, or 328P chips), the simplest way of doing this is to use the ArduinoPebbleSerial library. This open-source reference implementation takes care of the smartstrap protocol and allows easy communication with the Pebble accessory port.

Download the library as a .zip file. In the Arduino IDE, go to 'Sketch' -> 'Include Library' -> 'Add .ZIP LIbrary...'. Choose the library .zip file. This will import the library into Arduino and add the appropriate include statement at the top of the sketch:

#include <ArduinoPebbleSerial.h>

After including the ArduinoPebbleSerial library, begin the sketch with the standard template functions (these may already exist):

void setup() {

}

void loop() {

}

Connecting to Pebble

Declare the buffer to be used for transferring data (of type uint8_t), and its maximum length. This should be large enough for managing the largest possible request from the watch, but not so large that there is no memory left for the rest of the program:

Note: The buffer must be at least 6 bytes in length to handle internal protocol messages.

// The buffer for transferring data
static uint8_t s_data_buffer[256];

Define which service IDs the strap will support. See {% guide_link smartstraps/smartstrap-protocol#generic-service-profile "Generic Service Profile" %} for details on which values may be used here. An example service ID and attribute ID both of value 0x1001 are shown below:

static const uint16_t s_service_ids[] = {(uint16_t)0x1001};
static const uint16_t s_attr_ids[] = {(uint16_t)0x1001};

The last decision to be made before connection is which baud rate will be used. This will be the speed of the connection, chosen as one of the available baud rates from the Baud enum:

typedef enum {
  Baud9600,
  Baud14400,
  Baud19200,
  Baud28800,
  Baud38400,
  Baud57600,
  Baud62500,
  Baud115200,
  Baud125000,
  Baud230400,
  Baud250000,
  Baud460800,
} Baud;

This should be chosen as the highest rate supported by the board used, to allow the watch to save power by sleeping as much as possible. The recommended value is Baud57600 for most Arduino-like boards.

Hardware Serial

If using the hardware UART for the chosen board (the Serial library), initialize the ArduinoPebbleSerial library in the setup() function to prepare for connection:

// Setup the Pebble smartstrap connection
ArduinoPebbleSerial::begin_hardware(s_data_buffer, sizeof(s_data_buffer),
                                                  Baud57600, s_service_ids, 1);

Software Serial

Alternatively, software serial emulation can be used for any pin on the chosen board that supports interrupts. In this case, initialize the library in the following manner, where pin is the compatible pin number. For example, using Arduino Uno pin D8, specify a value of 8. As with begin_hardware(), the baud rate and supported service IDs must also be provided here:

int pin = 8;

// Setup the Pebble smartstrap connection using one wire software serial
ArduinoPebbleSerial::begin_software(pin, s_data_buffer, sizeof(s_data_buffer),
                                                   Baud57600, s_service_ids, 1);

Checking Connection Status

Once the smartstrap has been physically connected to the watch and the connection has been established, calling ArduinoPebbleSerial::is_connected() will allow the program to check the status of the connection, and detect disconnection on the smartstrap side. This can be indicated to the wearer using an LED, for example:

if(ArduinoPebbleSerial::is_connected()) {
  // Connection is valid, turn LED on
  digitalWrite(7, HIGH);
} else {
  // Connection is not valid, turn LED off
  digitalWrite(7, LOW);
}

Processing Commands

In each iteration of the loop() function, the program must allow the library to process any bytes which have been received over the serial connection using ArduinoPebbleSerial::feed(). This function will return true if a complete frame has been received, and set the values of the parameters to inform the program of which type of frame was received:

size_t length;
RequestType type;
uint16_t service_id;
uint16_t attribute_id;

// Check to see if a frame was received, and for which service and attribute
if(ArduinoPebbleSerial::feed(&service_id, &attribute_id, &length, &type)) {
  // We got a frame!
  if((service_id == 0) && (attribute_id == 0)) {
    // This is a raw data service frame
    // Null-terminate and display what was received in the Arduino terminal
    s_data_buffer[min(length_read, sizeof(s_data_buffer))] = `\0`;
    Serial.println(s_data_buffer);
  } else {
    // This may be one of our service IDs, check it.
    if(service_id == s_service_ids[0] && attribute_id == s_attr_ids[0]) {
      // This frame is for our supported service!
      s_data_buffer[min(length_read, sizeof(s_data_buffer))] = `\0`;
      Serial.print("Write to service ID: ");
      Serial.print(service_id);
      Serial.print(" Attribute ID: ");
      Serial.print(attribute_id);
      Serial.print(": ");
      Serial.println(s_data_buffer);
    }
  }
}

If the watch is requesting data, the library also allows the Arduino to respond back using ArduinoPebbleSerial::write(). This function accepts parameters to tell the connected watch which service and attribute is responding to the read request, as well is whether or not the read was successful:

Note: A write to the watch must occur during processing for a RequestType of RequestTypeRead or RequestTypeWriteRead.

if(type == RequestTypeRead || type == RequestTypeWriteRead) {
  // The watch is requesting data, send a friendly response
  char *msg = "Hello, Pebble";

  // Clear the buffer
  memset(s_data_buffer, 0, sizeof(s_data_buffer));

  // Write the response into the buffer
  snprintf((char*)s_data_buffer, sizeof(s_data_buffer), "%s", msg);

  // Send the data to the watch for this service and attribute
  ArduinoPebbleSerial::write(true, s_data_buffer, strlen((char*)s_data_buffer)+1);
}

Notifying the Watch

To save power, it is strongly encouraged to design the communication scheme in such a way that avoids needing the watch to constantly query the status of the smartstrap, allowing it to sleep. To aid in this effort, the ArduinoPebbleSerial library includes the ArduinoPebbleSerial::notify() function to cause the watchapp to receive a SmartstrapNotifyHandler.

For example, to notify the watch once a second:

// The last time the watch was notified
static unsigned long s_last_notif_time = 0;

void loop() {

  /* other code */

  // Notify the watch every second
  if (millis() - s_last_notif_time  > 1000) {
    // Send notification with our implemented serviceID  and attribute ID
    ArduinoPebbleSerial::notify(s_service_ids[0], s_attr_ids[0]);

    // Record the time of this notification
    s_last_notif_time = millis();
  }
}