Linking Pebble and Spark Core

Note: This post assumes basic knowledge of Pebble AppMessage, PebbleKit JS, jQuery $.ajax(), Spark.function() and similar API calls.

Also, the JS code may only work on Android devices.

Introduction

A major appeal of the Pebble smartwatch is its potential both as a data display and a data input device. The addition of PebbleKit JS in SDK 2.0 allows a watchapp to connect to the internet and download data. Through the use of jQuery data can be requested, and with the EventSource object data can be listened for asynchronously.

This enables the watch to display data sent from the Core as well as make requests to the Spark Cloud to instruct the Core to execute functions or request the status of exposed variables. This means that the Pebble can use the Core as an interface for its I/O pins, which is an exciting prospect when considered with all the libraries available for Arduino (and by extension, the Core).

The purpose of this post is to instruct in what is required to get these two devices to interact. To do so, you must setup:

  • AppMessage and keys for the Pebble C program.
  • PebbleKit JS listeners (including jQuery and/or EventSource).
  • Use Spark.function(), Spark.variable() or Spark.publish() to expose the data you want to request/functions you want to execute remotely.

Visually, the process for triggering a Spark.function() call from Pebble looks like this (Spark.variable() works in the same way):

pebble-core-fuction

Prepare Pebble

To prepare the Pebble end, declare the keys you will be using for AppMessage communication. For this example, we will use a key called KEY_TOGGLE with a value of 0. This will be used to instruct PebbleKit JS to call a function registered on the Core with Spark.function() to toggle a pin HIGH or LOW. This is shown below in the starting template for the watchapp:

#include <pebble.h>

#define KEY_TOGGLE 0

static Window *window;
static TextLayer *text_layer;

static void select_click_handler(ClickRecognizerRef recognizer, void *context) 
{
  text_layer_set_text(text_layer, "Select");
}

static void click_config_provider(void *context) 
{
  window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler);
}

static void window_load(Window *window) 
{
  //Create TextLayer
  text_layer = text_layer_create(GRect(0, 0, 144, 168));
  text_layer_set_text(text_layer, "Press SELECT to toggle Spark pin D0");
  text_layer_set_text_alignment(text_layer, GTextAlignmentCenter);
  layer_add_child(window_get_root_layer(window), text_layer_get_layer(text_layer));
}

static void window_unload(Window *window) 
{
  //Destroy TextLayer
  text_layer_destroy(text_layer);
}

static void init(void) 
{
  //Create Window
  window = window_create();
  window_set_click_config_provider(window, click_config_provider);
  window_set_window_handlers(window, (WindowHandlers) {
    .load = window_load,
    .unload = window_unload,
  });
  window_stack_push(window, true);
}

static void deinit(void) 
{
  //Destroy Window
  window_destroy(window);
}

int main(void) 
{
  init();
  app_event_loop();
  deinit();
}

The next step is to declare this key in the Pebble app package when it is compiled. This is in appinfo.json (or Settings on CloudPebble):

"appKeys": {
  "KEY_TOGGLE": 0
}

Next, we open AppMessage in init():

//Prepare AppMessage
app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum());

create a function to send a key-value pair through AppMessage:

static void send_int(int key, int cmd)
{
  DictionaryIterator *iter;
  app_message_outbox_begin(&iter);
  
  Tuplet value = TupletInteger(key, cmd);
  dict_write_tuplet(iter, &value);
  
  app_message_outbox_send();
}

and add a call to send KEY_TOGGLE when the select button is pressed:

static void select_click_handler(ClickRecognizerRef recognizer, void *context) 
{
  send_int(KEY_TOGGLE, 0);  //Value can be any int for now
}

Prepare PebbleKit JS
After preparing the Pebble app to send an AppMessage, we must prepare PebbleKit JS to receive it and make a call to the Spark Cloud. The first stage in this is to initialise the pebble-js-app.js file like so:

var deviceId = "";
var accessToken = "";

Pebble.addEventListener("ready",
    function(e) {
        console.log("Pebble JS Ready!");
    }
);

Pebble.addEventListener("appmessage",
	function(dict) {
		console.log("AppMessage received!");
	}
);

The “appmessage” event callback is where we will make the Spark Cloud request, as this is triggered when an AppMessage is received. This will be run by any message received, but for the sake of precision and to accomodate multiple messages in an eventual application, we will single out messages with our KEY_TOGGLE key:

if(typeof dict.payload["KEY_TOGGLE"] !== "undefined") {
	console.log("KEY_TOGGLE received!");
}

It is in this clause that we will use jQuery to make the Spark Cloud request. First, we must include jQuery as it is not supported by default by PebbleKit JS (to the best of my knowledge!). We can do this by calling the following method in the “ready” event callback:

var importjQuery = function() {
	var script = document.createElement('script');
	script.src = 'http://code.jquery.com/jquery-latest.min.js';
	script.type = 'text/javascript';
	document.getElementsByTagName('head')[0].appendChild(script);
};

Pebble.addEventListener("ready",
    function(e) {
        importjQuery();
        console.log("Pebble JS Ready!");
    }
);

Next, we assemble the URL for the POST request and make the $.ajax() call. The URL contains the following elements (more details can be found on the Spark Docs site):

  • The base URL: https://api.spark.io/v1/devices/
  • The Core Device ID
  • The name of the function declared in Spark.function() (more on this later)
  • The Access Token for secure access for token holders
  • Any arguments (One string at this time)

Our function-to-be will be called int toggle(String args) as this is the accepted signature for Spark.function(). Storing our sensitive Device ID and Access Token as private variables in the JS file, the result looks like this:

var url = "https://api.spark.io/v1/devices/" + deviceId + "/toggle?access_token=" + accessToken;

//Send with jQuery
$.ajax({
  type: "POST",
  url: url,
  data: {"args":"none"},	//No args for the moment
  success: function() {
  	console.log("POST successful!");
  },
  dataType: "json"
});

Make sure you change the deviceId and accessToken variables at the top of the JS file to be those of you own Core!

This completes the PebbleKit JS preparation!

Prepare the Core
The final step in setting up a Spark.function() triggered by Pebble is to write the actual Core firmware. This is a very simple program. A function with the signature mentioned previously is created to do the toggling, with a bool variable to maintain state. This is then exposed to the Cloud API using Spark.function() in setup(). The end result looks like this:

bool is_high = false;

int toggle(String args)
{
    if(is_high == true)
    {
        digitalWrite(D0, LOW);
        is_high = false;
    }
    else
    {
        digitalWrite(D0, HIGH);
        is_high = true;
    }
    
    return 0;
}

void setup() {
    pinMode(D0, OUTPUT);
    Spark.function("toggle", toggle);
}

void loop() {
    //Nothing here
}

Finally, connect an LED to pin D0 of the Core. A recommended circuit is shown below (Using SchemeIT):

pebble-core-cir

Putting It All Together
Compile and upload the watchapp to your Pebble, compile and upload the Core firmware to your Core and launch the watchapp when the Core upload is done. You should see something similar to this:

pebble-spark-screen1

When the Core is breathing cyan and the Pebble watchapp is open, press the SELECT button. The LED should toggle on and off!

Conclusion
That’s a basic overview of the setup to enable the control of Spark Core pins (functions in general) from a Pebble. In the near future I’ll write more to cover sending data asynchronously back the other way using Spark.publish() and EventSource JS objects to receive them.

You can get the sample project code for all stages here on GitHub.

Any queries or feedback if I’ve made a JS faux-pas (I’m relatively new!), let me know!

Advertisements
4 comments
  1. Spencer said:

    ../src/app_message.c:76:12: error: ‘KEY_TOGGLE’ undeclared (first use in this function
    I keep getting this error when I compile the Pebble App on CloudPebble. Can you give me more detailed instructions on how to fix this issue or where you think I messed up. I am looking at the message keys sections on CloudPebble, but I don’t think I am doing this right. I have very little programming experience, so forgive me for not knowing what I am doing. I am learning as I go. Thank you.

    • Spencer said:

      I was able to get my previous issue fixed, but I am still seeing this in the Pebble Cloud App log, and the watch app is not turning D0 from high to low.
      PHONE] pebble-app.js:?: Error: Spark Pebble: ReferenceError: Can’t find variable: $ at line 30 in pebble-js-app.js
      It is referring to line 30 which is $.ajax({

      • bonsitm said:

        This could be due to not importing jQuery correctly. I’m not an expert user, but I believe the way it is done in the source file is adequate.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: