Hey guys,
I didnât manage to upload it yesterday, so Iâm doing it now. Hopefully some of it is at least partially useful to someone. Keep in mind that I donât know how to code, and this is my first project. Having said that, the code seems to work, which Iâm glad for.
HTML code. Insert your device ID and accesstoken, and you should be good to go. Since these are âout in the openâ itâs wise to keep this file safe. Donât share your credentials, since they could mess with your Core.
Iâd suggest copy/pasting the code, and then try to go through the comments (I used notepad++). I wasnât really sure what the best comment syntax would be, but on a large enough screen it seems decent 
Either way, here you go:
<!DOCTYPE HTML>
<html>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, minimal-ui">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.js"></script>
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.css">
<script>
var deviceID = "<<insert device id here>>"; //Makes your deviceID a variable for easy reference.
var accessToken = "<<insert accesstoken here>>"; //Makes your accestoken a variable for easy reference.
var baseURL = "https://api.spark.io/v1/devices/"; //Creates a variable for the static URL, for easy reference (always the same).
var redValue; //Creates a global variable for redValue.
var greenValue; //Creates a global variable for greenValue.
var blueValue; //Creates a global variable for blueValue.
var Colors; //Creates a global variable for Colors.
var eventSource = new EventSource(baseURL + deviceID + "/events/?access_token=" + accessToken); //Subscribes to your eventstream.
var requestURL = baseURL + deviceID + "/" + "GetColor" + "/?access_token=" + accessToken; //Easy reference for making the requests
alert = function() {}; /////////////// comment this function by adding two '//' if you would like to debug. you will see alerts in your browser at almost every step. This makes it easier to see what your code is doing.
$(document).ready(function(){ //Waits for the page to finish loading before executing the following code.
alert('DOM loaded, I\'m gonna do stuff...'); //Display a message
$("#Red ").on( "slidestop", function( event, ui ) { //Create a 'slidestop' event bound to the element with the id "Red". The function fires a soon as the slider is released. Better mobile compatibility than mouseup.
redValue = document.getElementById('Red').value; //Read the value from the element with id="Red", and assigns it to redValue
Colors = document.getElementById('Red').name + (redValue).toString(); //Creates the required syntax for the "params" function by combining name and value
doMethod("params", Colors); //Initiates the doMethod with "params" and Colors as variables
} );
$("#Green").on( "slidestop", function( event, ui ) {
greenValue = document.getElementById('Green').value;
Colors = document.getElementById('Green').name + (greenValue).toString();
doMethod("params", Colors);
} );
$("#Blue").on( "slidestop", function( event, ui ) {
blueValue = document.getElementById('Blue').value;
Colors = document.getElementById('Blue').name + blueValue.toString();
doMethod("params", Colors);
} );
//SSE listener, updates colors and sliders if/when changed
eventSource.addEventListener('color_values', function(e) { //Checks your eventstream for events named 'color_values', after which it will execute the following function.
alert('Looks like I just received a SSE...'); //Display a message
var rawData = JSON.parse(e.data); //Create new variable rawData in which the 'data' section of the SSE will be parsed. This contains a nested JSON.
var parsedData = JSON.parse(rawData.data); //Create new variable parsedData in which the nested JSON data from rawData will be parsed, giving you the desired output.
color_display.innerHTML = "(" + parsedData.red + "," + parsedData.green + "," + parsedData.blue + ")"; //Display the RGB value in the HTML element with id="color_display".
alert('Going to check if the values changed since last time'); //Display a message.
if (redValue != parsedData.red || greenValue != parsedData.green || blueValue != parsedData.blue){ //Check to see it the parsed values are different from the values last set by this page. If they're different, execute the following code:
alert('Turns out the values are indeed different. Let\'s update them and change the sliders accordingly.'); //Display a message
redValue = parsedData.red; //Parse the JSON data, and assign it to variable.
greenValue = parsedData.green; //Parse the JSON data, and assign it to variable.
blueValue = parsedData.blue; //Parse the JSON data, and assign it to variable.
alert('Going to update slider because I liked what the SSE showed me.'); //Display a message
updateSliders(); //Execute the updateSliders function.
}
else {
alert('Well, I guess the values didn\'t change. No use for unnecesarily updating the sliders then, now is there?'); //Display a message
}
}, false);
});
$(window).load(function() { //Execute the following code, only if the entire page is completely loaded.
alert('Window loaded, going to execute doMethod for initial request'); //Display a message
doMethod("params","GetColor=0"); //Initiates the doMethod with "params" and Colors as variables
});
function updateSliders(){
alert('Going to update sliders RIGHT NOW.'); //Display a message
$('#Red').val(redValue); //Update the value of the element with id="Red" to be redValue.
$('#Green').val(greenValue);
$('#Blue').val(blueValue);
$('#Red, #Green, #Blue').slider('refresh'); //Refresh the elements with id="Red", id="Green", id="Blue", for the changes in values to take effect.
alert('Sliders updated!'); //Display a message
}
//This is where the magic happens. It receives the variables from the doMethod("params", Colors) from above and inserts them like this: "params" becomes 'method', and 'Colors' becomes 'data'. This is then combined with the baseURL and deviceID to create a new variable 'url'. Next, an ajax request is made. It's a POST request with the newly created 'url' variable as URL and the data is your accessToken and the 'data' from the doMethod Function (which is actually 'Colors', which is actually [element]'name' combined with [element]'value'). The whole is send as JSON, which is easy to interpret. A complete walkthrough/example can be found at the bottom to make things easier to understand.
function doMethod(method, data) {
var url = baseURL + deviceID + "/" + method;
$.ajax({
type: "POST",
url: url,
data: {
access_token: accessToken,
args: data
},
dataType: "json"
})
}
//following block was added to disable scrolling on mobile devices. you can safely remove it.
$(document).on("touchmove", function(event) { //might break some other functions. take caution!
event.preventDefault();
event.stopPropagation();
});
</script>
<body>
<div data-role="page" id="pageone"> <!-- Create a Jquery Mobile page -->
<div data-role="content"> <!-- Assign this section the Jquery Mobile 'content' parameter -->
<!-- Create a HTML5 slider by making type="range", the name is made "colorRed=" because it was easier to make it work with the Params function on the Core code.
id="Red" enables you to find the element by its ID. 'min' and 'max' determine the range of the slider, and 'steps' determines the intervals.
'data-highlight' colors in the part of the slider up to your value. 'data-popup-enabled="true"' creates a pop-up above your handle which shows the current value of the slider.
-->
Red: <input type="range" name="colorRed=" id="Red" min="0" max="255" step="1" data-highlight="true" value="0" >
Green: <input type="range" name="colorGreen=" id="Green" min="0" max="255" step="1" data-highlight="true" value="0" >
Blue: <input type="range" name="colorBlue=" id="Blue" min="0" max="255" step="1" data-highlight="true" value="0" >
Current RGB value: <span id="color_display"></span>
</div>
</div>
</body>
</html>
<!--
Example walkthrough
As soon as the page is completely loaded it will execute this:
$(window).load(function() {
alert('Window loaded, going to execute doMethod for initial request');
doMethod("params","GetColor=0");
});
This shows you a message to indicate that it's activated, and it will also call ==> doMethod("params","GetColor=0");
function doMethod(method, data) {
var url = baseURL + deviceID + "/" + method;
$.ajax({
type: "POST",
url: url,
data: {
access_token: accessToken,
args: data
},
dataType: "json"
})
}
This will send a POST request to the Cloud and subsequently your Core. It activates ==> Spark.function("params", handleParams);
(Code on Core:)
int handleParams(String command)
{
int p = 0;
while (p<(int)command.length()) {
int i = command.indexOf(',',p);
if (i<0) i = command.length();
int j = command.indexOf('=',p);
if (j<0) break;
String key = command.substring(p,j);
String value = command.substring(j+1,i);
int val = value.toInt();
if (key=="colorRed"){
red = val;
Serial.println('Set red value');
}
...
...
...
else if (key == "GetColor"){
Serial.println('Got a request for my colors. \n Guess I exit this function \n and publish my values.');
//Doesn't really do anything, just needed to initiate one of these Params once in order to publish the current RGB values using the code below.
}
sprintf(pubstring,"{\"red\": %u, \"green\": %u, \"blue\": %u}",red,green,blue); //Convert RGB values to JSON format for easy processing.
Spark.publish("color_values",pubstring); //Publish SSE with current RGB values in JSON format.
Serial.println(String(pubstring)); //Print current RGB values to Serial.
p = i+1;
}
}
This function will use and parse the argument from your POST request, which in this case was "GetColor=0". It tries to match the characters before the '=' sign to the key inside the 'if' operator. If matched, the relevant code will the executed, and the value behind the '=' sign can be used as a value. We however merely wanted to execute the handleParams once, in order to publish the current RGB values. Like the comments indicate, the RGB values will be parsed to a JSON format, for easy handling, after which they'll be published.
We could've avoided the whole handleParams function by using a separate Spark.function(). But since we're limited to four functions, and I already had this in place, I thought I might as well use it.
Now we've got a JSON published, we move back to the page, which receives it through this function:
eventSource.addEventListener('color_values', function(e) {
alert('Looks like I just received a SSE...');
var rawData = JSON.parse(e.data);
var parsedData = JSON.parse(rawData.data);
color_display.innerHTML = "(" + parsedData.red + "," + parsedData.green + "," + parsedData.blue + ")";
alert('Going to check if the values changed since last time');
if (redValue != parsedData.red || greenValue != parsedData.green || blueValue != parsedData.blue){
alert('Turns out the values are indeed different. Let\'s update them and change the sliders accordingly.');
redValue = parsedData.red;
greenValue = parsedData.green;
blueValue = parsedData.blue;
alert('Going to update slider because I liked what the SSE showed me.');
updateSliders();
}
else {
alert('Well, I guess the values didn\'t change. No use for unnecessarily updating the sliders then, now is there?');
}
}, false);
The comments are pretty self-explanatory, but for documentations sake; As as the publish from the Core is noticed, this code will parse the include data (twice), checks to see if any values are different from what the page currently has, and will update the sliders if they're indeed different. If they're the same, there's no use in updating them, so the code simply does nothing. Updating the sliders happens through this function:
function updateSliders(){
alert('Going to update sliders RIGHT NOW.');
$('#Red').val(redValue);
$('#Green').val(greenValue);
$('#Blue').val(blueValue);
$('#Red, #Green, #Blue').slider('refresh');
alert('Sliders updated!'); //Display a message
}
It assigns the values, from either the slider, or the SSE parser to the relevant sliders. You select the slider by its ID '#Red', after which you tell it to change the value '.val' to '(redValue)'. It will then refresh the slider to make sure the change is also noticeable.
So if a core has the RGB value of (255,0,0) upon starting, the following will happen:
The page loads completely, and sends a POST request to the Core, thereby activating the handleParams function. This will then broadcast the current RGB value (255,0,0) in JSON format. This gets picked up by the webpage, which parses it, and compares it to its own values. Since the initial values of the page are (0,0,0), they're indeed different. The page will then update its sliders accordingly.
Let's say we change this value from (255,0,0) to (127,0,0) by sliding the red slider to halfway:
$("#Red ").on( "slidestop", function( event, ui ) {
redValue = document.getElementById('Red').value;
Colors = document.getElementById('Red').name + (redValue).toString();
doMethod("params", Colors);
} );
This code is bound to the slidestop event, and thus will fire as soon as the slider is released. The redValue will be updated according to the value of the slider. It will then be combined to a string along with the element name, in order to get the right format for the parse function on the Core. In this case, that'd be "colorRed=127" (NO SPACES!). This will then be send to the doMethod function, with the respective variables.
The exact same will happen as described above, with the addition of the following:
On the Core the "colorRed=127" will be matched to:
(code on Core)
if (key=="colorRed"){ //Compares incoming Post-request to set key, execute if matched.
red = val; //Sets red to the value passed by the Post-request.
Serial.println('Set red value');
}
The red value will then be set to 127. This function ends, and a new JSON RGB value is published, which can be picked up by the page. Since this time the slider value is already 127 (since you just put it there), there is no need to update the sliders, thus nothing happens.
On the Core, the continuous loop sets the RGB led to the values specified, in this case (127,0,0), reducing the value by half.
-->
The complementary Core code:
int red = 0; //Create integer for red value, initally 0
int green = 0; //Create integer for green value, initally 0
int blue = 0; //Create integer for blue value, initally 0
char pubstring[64]; //Create Char array with 64 spaces
void setup() {
Serial.begin(9600); //Open Serial connection, 9600 Baud rate
RGB.control(true); //Enable onboard RGB LED control
RGB.color(255,0,0); //Set onboard RGB to (255,0,0)
delay(200); //Wait 200 microsecond
RGB.color(0,0,0); //Set onboard RGB to (0,255,0)
delay(200); //Wait 200 microseconds
RGB.color(0,0,255); //Set onboard RGB to (0,0,255)
delay(200); //Wait 200 microseconds
Spark.function("params", handleParams); //Expose handleParams function under "params" request.
}
void loop() {
RGB.color(red, green, blue);
}
void color (unsigned char red, unsigned char green, unsigned char blue)
{
analogWrite(redPin, red);
analogWrite(greenPin, green);
analogWrite(bluePin, blue);
}
int handleParams(String command)
{ //This code makes it easy to change parameters (credits to @Luz at bottom)
int p = 0; //The code is activated by the "params" function, and used the data included
while (p<(int)command.length()) { //in order to change given parameters. The code expects to find following
int i = command.indexOf(',',p); //syntax: key=val without spaces. Check out the HTML code to see what kind
if (i<0) i = command.length(); //of request is required to make this work.
int j = command.indexOf('=',p);
if (j<0) break;
String key = command.substring(p,j);
String value = command.substring(j+1,i);
int val = value.toInt();
if (key=="colorRed"){ //Compares incoming Post-request to set key, execute if matched.
red = val; //Sets red to the value passed by the Post-request.
Serial.println('Set red value');
}
else if (key=="colorGreen"){ //Compares incoming Post-request to set key, execute if matched.
green = val; //Sets green to the value passed by the Post-request.
Serial.println('Set green value');
}
else if (key=="colorBlue"){ //Compares incoming Post-request to set key, execute if matched.
blue = val; //Sets red to the value passed by the Post-request.
Serial.println('Set blue value');
}
else if (key == "GetColor"){ //Compares incoming Post-request to set key, execute if matched.
Serial.println('Got a request for my colors. \n Guess I exit this function \n and publish my values.');
//Doesn't really do anything.
//Just needed to initiate one of these Params once
//in order to publish the current RGB values
//Using the code below.
}
sprintf(pubstring,"{\"red\": %u, \"green\": %u, \"blue\": %u}",red,green,blue); //Convert RGB values to JSON format for easy processing.
Spark.publish("color_values",pubstring); //Publish SSE with current RGB values in JSON format.
Serial.println(String(pubstring)); //Print current RGB values to Serial.
p = i+1;
}
}
/* ---- Credits to Lukas (@Luz on Spark) for this implementation of the parameter function, it's awesome! ----
The MIT License (MIT)
Copyright (c) 2014 Lukas Zeller
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
You can download both files in a .rar over [here.][1]
Lastly, Iâd like to thank @bko again for his great tutorials as well as his commitment here on the forum. Youâre a great help to a lot of people around here, so please keep up the amazing work!
And then Iâd like to thank @luz again for his code on the parameters. Itâs a really neat way to effectively gain an unlimited number of Spark.function()s, and to easily change stuff in your code, without having to flash stuff all over again.
Of course Iâd like to thank all the other people on here who make it such a great community. Itâs a joy watching the forum, seeing people helping each other out, wherever they can. If only the world was a bit more
like, then itâd be a whole lot more enjoyable out there 
Enjoy!
[1]: http://jordymoors.nl/spark/spark.rar