Archive

Spark Core

Not a lot has been happening on this blog for the last few weeks, and the reason for that is that I have been busy beginning my internship at Pebble! It’s been a great experience so far (We released SDK 2.5 today!) and I can’t wait to work on more awesome things to help make Pebble even more useful smartwatch.

That being said, I’ve been able to find time to maintain and update my public Pebble apps at weekends, and hope to continue this pattern whenever I can, because I have some ideas I want to implement even just for myself.

A result of this is a new application of my Spark Core driven LCD project in collaboration with a new colleague to display social media trends from WhatTheTrend on animated cards on Pebble, and as an added bonus show the same information on the LCD display.

After some teething issues, it was eventually presentable and works pretty well – but for some reason only on Wi-Fi. Here’s a photo of the whole thing in action. (Eduardo Sasha is imprinted on my brain now from all the testing…)

image

Stay tuned for coming updates to Dashboard and Wristponder, as well as bringing Watch Trigger up to date as well!

Advertisements

Note: The JS code may only work on Android devices.

Part 1: Linking Pebble and Spark Core

Introduction

In the last post (linked above) I detailed the basics of connecting a Pebble watchapp’s button clicks to a Spark.function() call on a Spark Core. In this post I will go over the reverse process: sending data back to the Pebble asynchronously. Once again this process uses a combination of Spark Cloud, PebbleKit JS and AppMessage to convey the message, which this time will be alerting a Pebble watch wearer that a button connected to the Core has been pressed via a short vibration pulse.

Preparing Pebble

The initial Pebble C program code is similar in structure to the last post’s starting point, but without any of the Click functionality, as this will be a receive-only app. Thus the start of your project’s main .c file will look like this:

#include <pebble.h>

#define KEY_BUTTON_STATE 0

static Window *window;
static TextLayer *text_layer;

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 button on Core 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_window_handlers(window, (WindowHandlers) {
		.load = window_load,
		.unload = window_unload,
	});

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

	window_stack_push(window, true);
}

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

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

Note that the name of the main AppMessage key has changed to a more appropriate KEY_BUTTON_STATE, but this is arbitrary – the value is still 0.

Instead of receiving button clicks, the app will be receiving messages sent from the phone on receiving a message from the Spark Cloud. To do this, we register an AppMessageInboxReceived handler before opening the service:

app_message_register_inbox_received((AppMessageInboxReceived) in_recv_handler);

and also declare the function above init():

static void in_recv_handler(DictionaryIterator *iterator, void *context)
{

}

This handler provides a DictionaryIterator structure that contains the received dictionary. To access the data, we use the dict_read_first() function to extract the tuple. This contains the key and value pair. We will then compare the value cstring and act accordingly (“HIGH” for button pressed and pulling pin D0 HIGH on the Core):

static void in_recv_handler(DictionaryIterator *iterator, void *context)
{
	//Get first tuple (should be KEY_BUTTON_STATE)
	Tuple *t = dict_read_first(iterator);

	//If it's there
	if(t)
	{
		if(strcmp("HIGH", t->value->cstring) == 0)
		{
			vibes_short_pulse();
		}
	}
}

Compile this and upload to your Pebble to make sure it is ready to work with PebbleKit JS, which we will set up next.

Preparing PebbleKit JS

Also similar to last time, we must setup the JS code to listen for events from the Spark Cloud and send AppMessages on to the watch. However, this time we do not require jQuery but instead use an object called EventSource that will provide the messages in a callback. This is done in the “ready” event handler:

Pebble.addEventListener("ready",
	function(e) {
		//Register EventSource listener
		var core = new EventSource("https://api.spark.io/v1/events/?access_token=" + accessToken);
		core.addEventListener("button_state", 
			function(response) {
				
			}, 
			false
		);

		console.log("Pebble JS Ready!");
	}
);

Note: This requires only your Access Token, not the Device ID.

Once this callback has been created, it will be executed whenever a Core firmware uses Spark.publish() with the topic “button_state”. When this event occurs, we will send the accompanying payload, either “HIGH” or “LOW” (details later) to the Pebble for it to decide whether to vibrate or not. This process looks like this:

core.addEventListener("button_state", 
	function(response) {
		//Interpret response as JSON
		var json = JSON.parse(response.data);

		console.log("Payload is '" + json.data + "'");

		//Send the payload
		Pebble.sendAppMessage(
			{"KEY_BUTTON_STATE":json.data},
			function(e) {
				console.log("Sent '" + json.data + "' to Pebble.");
			},
			function(e) {
				console.log("Failed to send data to Pebble!");
			}
		);
	}, 
	false
);

The AppMessage dictionary takes the form of a JSON dictionary with the key-value pair consisting of the declared key (remember to alter appinfo.json or the App Keys section in Settings on CloudPebble) and the word “HIGH” or “LOW” as received from the Core. We also get to register two callbacks for if the message was successful, and if it is not. The above code segment uses this to provide some meaningful log output.

This completes the setup of the JS segment of the message’s journey. With the JS code in place, re-compile and re-upload your Pebble .pbw file to your watch.

Preparing the Core

The last thing to do is to configure the Core to call Spark.publish() to notify the JS and C code we have already set up. This is done in the loop() function and takes the form of a simple if, else statement, depending on whether digitalRead(D0) determines whether the button is pressed. If you don’t have a button to hand, you can simulate one by simply touching the 3.3V pin of your core to D0 briefly once the following code is in place and uploaded:

static bool pressed = false;

void setup() {
    pinMode(D0, INPUT);
}

void loop() {
    //Publish button state
    if(digitalRead(D0) == HIGH && pressed == false)
    {
        Spark.publish("button_state", "HIGH");
        pressed = true;
        
        //Rate limit to prevent spamming the cloud
        delay(500);
    }
    else if(digitalRead(D0) == LOW && pressed == true)
    {
        Spark.publish("button_state", "LOW");
        pressed = false;
    }
}

If you do have a push button to hand, here is how to connect it up, as depicted on the Arduino site, except instead of pin 2, we are using Core pin D0. Once this is done, ensure both watchapp and Core firmware are uploaded and running before pressing the button. The watch should vibrate within a couple of seconds!

Conclusion

There we have an expansion on the original post, showing how to send asynchronous events and data from the Spare Core to the Pebble watch. A slight reduction in latency between the two can be theoretically achieved by calling app_comm_set_sniff_interval(SNIFF_INTERVAL_REDUCED), although this will consume more power over a long term period.

As always, the source code to this project can be found here on GitHub.

Enjoy!

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!

After blowing up my last LCD module used for a project, I decided to buy a new one, and decided on this RGB back-light module from Sparkfun. It shares the standard pin-out as any Arduino compatible module (the 4/8-bit parallel Hitachi HD44780 interface), and includes a back-light that can be illuminated using an RGB LED.

I made the requisite connections for use on my Spark Core, and after discovering that the LED K connection is to ground and that the contrast adjustment pin requires a voltage of more than 4.5V (So grounding on a 3.3V system such as the Core is not enough) I had the module running nicely. As an additional feature, I connected the blue LED terminal via a BC457b transistor to allow control via software. Future expansion could include RGB PWM control for some funky effects, perhaps.

After playing around with setting text in the Spark IDE, I expanded the firmware to allow reception of text from a POST request, and then created such a request in Android to allow control of the back-light and text shown in app form. After a bit of layout attention, this was the result. The font size and margins are chosen to wrap the same as the LCD (Word wrapping is a novel feature of the Core firmware I wrote).

Screenshot_2014-05-04-21-31-28The small size of the LCD module image is due to the fact I couldn’t find any on the internet larger (later I may create my own), and scaling it up proved unsightly. Still quite legible on my Nexus 5 display, however. Entering the above text and hitting ‘SET’ gives the following real-world result:

IMG_20140504_213246The font isn’t exactly the same, but close enough for my own use. I think that on any other sized display some more advanced layout management would be required, but that is for a future time. Also featured in the above picture, but unused, is the ADXL362 accelerometer I also used with the Core after tweaking the Arduino library to run, but haven’t written about yet.

Two useful code snippets I created for re-use in future LCD projects are sending the Spark Cloud request in Android:

public static String cloudRequest(String deviceId, String accessToken, String functionOrVariableName, String argString) {
	try {
		//Setup connection
		URL url = new URL("https://api.spark.io/v1/devices/" + deviceId + "/" + functionOrVariableName);
		HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
		con.setRequestMethod("POST");
		String urlParameters = "access_token=" + accessToken + "&args=" + argString;
		con.setDoOutput(true);

		//Send request
		DataOutputStream wr = new DataOutputStream(con.getOutputStream());
		wr.writeBytes(urlParameters);
		wr.flush();
		wr.close();

		//Receive response
		con.getResponseCode();
		BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
		String inputLine;
		StringBuffer response = new StringBuffer();
		while ((inputLine = in.readLine()) != null) {
			response.append(inputLine);
		}
		in.close();

		return response.toString();
	} catch (Exception e) {
		System.err.println(SparkUtils.class.getName() + ": Exception:");
		e.printStackTrace();
		return "FAILED";
	}
}

And also performing simple word wrapping between lines 0 and 1 of the LCD display:

//Check overrun
if(msg.charAt(15) != ' ' && msg.charAt(16) != ' ')
{
    //Find start of offending word
    int index = 15;
    while(msg.charAt(index) != ' ' && index >= 0)
    {
        index--;
    }

    String one = msg.substring(0, index);
    String two = msg.substring(index + 1);

    lcd.print(one);
    lcd.setCursor(0, 1);
    lcd.print(two);
}

Building on the small amount of JavaScript code developed by controlling the Spark Core pins from my Pebble, I decided to write a basic webpage to allow me to perform the same control functions but from a browser. Sure, it’s been done before, but as I have no real expertise or experience of JavaScript beyond that used for PebbleKit JS apps, and virtually none at all for HTML, it seemed like a good learning opportunity.

And it turned out to be easier than expected! After a couple of hours, the basic code was in place. The webpage looks like this:

jQUery CoreNothing too pretty to look at, but does the job well.

During the aforementioned Pebble project, I sought help with a problem in getting the same pin number back as I sent to the Spark Cloud. The solution to this turned out to be specifying the Spark.function() parameter string as a key-value dictionary, making the core (aha) code segment in this webpage as so:

//Send the request to the Core
var sendRequest = function(pin, on, device_id, access_token) {
	var url;
	if(on) {
		url = "https://api.spark.io/v1/devices/" + device_id + "/on?access_token=" + access_token;
	} else {
		url = "https://api.spark.io/v1/devices/" + device_id + "/off?access_token=" + access_token;
	}

	console.log("jQuery AJAX: Requesting pin " + pin + " " + (on == true ? "on" : "off") + "...");

	//Send request using jQuert AJAX
	$.ajax({
	  type: "POST",
	  url: url,
	  data: {"args":pin},
	  success: success,
	  dataType: "json"
	});
};

The entire project (three files!) can be found on GitHub here. I may expand it to include more functions in the future, but at the moment it provides a good platform to play around with, and I’ve learned a small amount about HTML forms and using jQuery. Time well spent!

 

Updates:

Appears the return_code from the Core is not always that which is requested. Works well for Pin D0 though.

– Source code repo updated. All Dx pins now work. Uses jQuery. Still READY-TIMEOUTs though…

I’ve revisited my control of the Spark Core Dx pins, with some polishing up and JS driven UI updating so I know the response has been received from the Spark Cloud.

pebble-screenshot_2014-04-05_17-55-09

I’m loving working with it again, and can’t wait to build some real remote controlled device, but that will have to wait for another day. I plan to release this app eventually, possibly under the name of PebbleTinker, derived from the stock Tinker app for Spark Core, and all looks to be working fine, but I’m being haunted by the READY-TIMEOUT, where PebbleKit JS is killed after about ten seconds after install. Once this is fixed, I look forward to releasing!

If you want to give it a go early, the source code is partly available on GitHub.

As with the Pebble smartwatch, I also backed another project called Spark Core, which promised the idea of a small, compact and easy to use Wi-Fi-enabled Arduino like device. And I must say, they certainly delivered!

IMG_20131223_140517

After a few initial problems programming the device from the Spark Cloud (a browser based IDE) which turned out to be mainly my fault (The device appears to rely on a rapid loop() completion to stay in contact with the Spark Cloud and I was inhibiting it with a habitual while(true) loop within that loop, preventing Cloud access) I have succeeded in my first very minor project – interfacing with an Arduino Uno.

The idea is simple: The Adruino sends the characters ‘1’, ‘2’ and ‘3’ in sequence and the Core flashes an attached LED the corresponding number of times.

The Arduino sketch:

void flash(int pin)
{
  digitalWrite(pin, HIGH);
  delay(50);
  digitalWrite(pin, LOW);
  delay(50);
}

void setup()
{
  Serial.begin(9600);
  pinMode(13, OUTPUT);
}

void loop()
{
  Serial.print('1');
  flash(13);
  delay(1000);
  Serial.print('2');
  flash(13);
  delay(1000);
  Serial.print('3');
  flash(13);
  delay(1000);
}

And the Core code:

int output_led = D0;
int onboard_led = D7;

void flash(int pin);

void setup()
{
    Serial1.begin(9600);
    pinMode(output_led, OUTPUT);
    pinMode(onboard_led, OUTPUT);
}

void loop()
{
    if(Serial1.available() > 0)
    {
        flash(onboard_led);

        char c = (char)Serial1.read();

        switch(c) {
            case '1':
            {
                flash(output_led);
                break;
            }
            case '2':
            {
                flash(output_led);
                flash(output_led);
                break;
            }
            case '3':
            {
                flash(output_led);
                flash(output_led);
                flash(output_led);
            }
        }
    }
}

void flash(int pin)
{
    digitalWrite(pin, HIGH);
    delay(50);
    digitalWrite(pin, LOW);
    delay(50);
}

And finally a video!

Aside from taking a while to discover that the TX RX pins are actually Serial1 and not Serial, the project was simple enough to implement. The Spark Cloud IDE is easy to use and satisfyingly hands-free!