Tutorial: Spark Variable and Function on One Web Page

Let’s say you have a Spark core with both a Spark variable and a Spark function. In my case, I have the world’s simplest servo control sketch, using the nice miniature servo in the Spark Maker Kit. Using these parts, we will build a servo that you can control from a web page anywhere and monitor the position of the servo too!

Here’s a preview of the finished tutorial:

There is a coarse adjust slider, two fine adjust buttons, and the current position of the servo is displayed. That thing on the right, is the servo with a white control arm; it is being held in a small vise in front of screen.

The Wiring

Servos are easy to interface to and the Spark core has the servo library built-in, so there is no need to include a library. The servos that we use mostly come from the model airplane, helicopter, and boat world and are designed to work on a 4.8V battery pack. So you need to use a +5V supply for the servo, which is close enough. If you only have one small servo to drive, you don’t need a separate supply, but big servos can draw a lot of current and the motors can be electrically noisy, so use a separate power supply in that case and just tie the grounds together.

Servos are controlled using a PWM signal that always has the same frequency but varies in term of duty-cycle. The more time the control signal is high (greater dut-cycle), the more the servo will move in one direction. When the duty-cycle is lower and the control spends less time high, the more the servo will move in the other direction.

Servos generally run from 0 to 180 degrees with 90 degrees being the neutral, or mid-point. As we shall see, the servo stops are not always perfectly at 0 and 180 and you might some testing per servo. The servo library has a way to deal with that, if you need it.

Here is how we wire it up:

And a wider shot showing the actual servo with a white control arm stuck on it:

A +5V supply voltage comes in from the top of the picture and is connected to the power rails on the bread board. The core VIN pin is wired to +5V (Warning: don’t connect that to any other pin or you will damage your core!). A core ground pin is connected to the ground or negative supply rail.

Now for the servo, we can use that standard Maker Kit jumper wires to connect to it and just push the pins into the servo connector. Use a red jumper wire to connect the +5V rail to the middle red wire on the servo connector. Use a black jumper wire to connect the bread board ground rail and the brown outside wire on the servo connector. Finally use a white jumper wire to connect core pin A0 the remaining yellow wire on the servo connector. Note that only certain pins on the Spark core can run servos since making the PWM waveform the control input to the servo requires a timer on the core; see the documentation for the details. The servo will work fine with a 3.3V control signal, so no need for a level shifter.

That completes the wiring. Two power wires for the core, two for servo, and one control wire–pretty simple.

(moors7: A0 won’t work for the Photon, use any of the following and change the code accordingly: D0, D1, D2, D3, A4, A5, WKP, RX, and TX. See these docs.)

The Code

This may be the shortest sketch I have ever written. By using a Spark function to set the servo position and a Spark variable to report the servo position, there is not a lot of core to write:

Servo myservo;  // create servo object to control a servo

int pos = 0;    // variable to store the servo position

void setup()
{
  myservo.attach(A0);  // attaches the servo on the A0 pin to the servo object
  Spark.function("setpos", setPosition);
  Spark.variable("getpos", &pos, INT);
}

void loop()
{
}

int setPosition(String posValue) {
    pos = posValue.toInt();
    myservo.write(pos);
    return 0;
}

A few things to note here. The loop() function does nothing in this sketch, all the work is done by the Spark function and variable. We are using a int for the datatype of the position, just because that is easier to declare as a Spark variable. Note that we could have read the servo position from the myservo object, but it would be the same value we just wrote anyway. The servo position in these small servos is not read back in any way, you just tell the servo where you want it to be. There are some more expensive servos where you can read the actual position back in the processor, and we would need an analog input to read that as voltage if we had one of those.

Also the myservo.write(pos) function limits the possible values to 0 to 180 by default, so we don’t have to do any bounds checking or limiting on the position variable.

Since a Spark function always takes a String argument, we use the String toInt() method to convert it to a number. The return value is set to zero here and not really used, but generally the return value of a Spark function should be used to indicate success or error of the command.

Notice that I have named by function and variable set and get, just like an object method.

The Web Side

So far, things have been really easy, but now things are going to pick up a bit. First a word of friendly advice: don’t expect video game type interactivity from the Spark cloud. You can get good performance, but you can only go so far. As I have said before, my house is over an 1/8 of second away from the Spark cloud in my testing, so you should expect near real-time, not hard real-time control to be possible here. Ok, that’s out of the way!

Remember, we are going to put our access token in the HTML file, so keep it private. I like to put mine on a service like Dropbox so I can access them from many computers and even my phone, but I never put the file on the public internet.

Here’s the code:

<!DOCTYPE HTML>
<html>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<body>
    <P>Set Servo Position:<br><br>
    <input type="range" name="degBox" id="degBoxId" min="0" max="180" step="1" value="90" list="myData" onchange="setValue(this)">
    <!-- This adds the tick marks to the range but does not in Safari -->
    <datalist id="myData">
       <option value="0">
       <option value="30">
       <option value="60">
       <option value="90">
       <option value="120">
       <option value="150">
       <option value="180">
    </datalist>
    <br><br>
    <button id="minusbutton" onclick="fineAdjust(-5)">&lArr; -5 &deg;</button>
    <button id="plusbutton"  onclick="fineAdjust(+5)">+5 &deg; &rArr;</button>
    <br><br>
    <P>Current Position: <span id="curPos"></span><br>

    <script type="text/javascript">
      var deviceID    = "<< device id >>";
      var accessToken = "<< access token >>";
      var setFunc = "setpos";
      var getFunc = "getpos";

      window.setInterval(function() {
        requestURL = "https://api.spark.io/v1/devices/" + deviceID + "/" + getFunc + "/?access_token=" + accessToken;
        $.getJSON(requestURL, function(json) {
                 document.getElementById("curPos").innerHTML = json.result + "&deg;";
                 document.getElementById("curPos").style.fontSize = "28px";
                 document.getElementById("degBoxId").value = parseInt(json.result);
                 });
      }, 1000);

      function setValue(obj) {
        var newValue = document.getElementById('degBoxId').value;
        sparkSetPos(newValue);
      }
      
      function fineAdjust(value) {
        var currentValue = parseInt(document.getElementById('curPos').innerHTML);
        var setValue = value + currentValue;
        sparkSetPos(setValue);
        document.getElementById("degBoxId").value = setValue;
      }

      function sparkSetPos(newValue) {
	var requestURL = "https://api.spark.io/v1/devices/" +deviceID + "/" + setFunc + "/";
        $.post( requestURL, { params: newValue, access_token: accessToken });
      }

    </script>
</body>
</html>

OK, so we are using AJAX and jquery again to do HTTP GET and POST request to the Spark cloud, so we load up the jquery library. Then we define an input range selector. This can look different in different browsers but it is generally rendered as a slider control. The datalist markup command is new in HTML5 and lets you have an enumerated value or in our case adds little tick marks to the slider scale and makes the slider want to “snap” to those values. You can hit values between the tick marks, but if you are close, it will try snap to the grid we have setup. Note that the datalist markup does not work in Safari or mobile Safari–you just don’t see the tick marks at all.

Then there are two buttons for fine adjustments that add or subract 5 degrees from the current angle. Finally we have a span that represent the current value of the servo position, based on the last setting we sent down.

The next section is the Javascript code that does all the real work. First we assign some variables with our device ID and access token (don’t forget to edit these in) and then names of our Spark function and variable, in case your want to change them later.

Now we have window.setInterval() javascript function with 1 second interval that gets the value of the Spark variable from the core and does two things with it. First it displays the value in the span we set up for that, but then it finds the slider input control and forces the current vale to be value got back from the core.

This has good and bad effects! First off, we can now be sure that whenever we reload the web page, the slider starts in the right position relative to the current servo position. Any time we click to update the slider, the slider will move to the actual set position of the servo in no more than one second. But the down side is that if we drag the slider around quickly, this forces a jumpy and sometime awkward motion of the slider.

Next up we have the setValue function which just gets called when the value of the slide control changes. It calls a helper sparkSetPos that does the actual command to POST the Spark function on the core.

We also have the fine adjust function that is called for both of the fine adjust buttons with either a +5 or -5 argument. I tried setting that to +/- 1 degree but it was hard to see how much it moved!

So on the web side, we have $.getJSON to read the Spark variable and $.post to call the Spark function.

So, How Does it Work?

It works great! You can click on the slider and move quickly to any position. You can click and hold the slider and it might jump around a bit, but it will eventually land where you let go. You can use the fine adjust buttons to move +/- 5 degrees and the servo and the slider are both updated.

I have not had any luck making movies that are small enough to upload here and yet have enough detail to read, so I have a series of photos showing both the screen and the servo held up in a small vise in front of the screen. The servo is held sideways, so 90 degrees is straight up and down. I probably should have flipped it over in the vise since greater angles move the top of the arm to left in the photos.

Now click on the 150 mark, moving the top of the arm to the left:

Now click on the 180 mark, moving the arm again to left horizontally.

Now back the other way to 60 degrees:

And then all the way to zero, but notice that 0 degrees is past horizontal–we can correct for this in the attach() call on the core:

Now back to 90 and we try to fine adjust buttons:



Wrapping it up

So now I just have to think of something as clever as the Facebook Like’s Push-up Monitor to use this for! You can think of your applications as well, and not just for servos. Anytime you need to set and get values using the Spark cloud.

Now we have a complete set of tutorials: you can read Spark published values in plain and JSON format, you can monitor Spark variables either on command or continuously, and now you can both control things via Spark functions and monitor them via Spark variables, all from your own Javascript/AJAX web pages. Go have fun!

31 Likes

Hi!
Can I use a function to set a value so I can adjust from Tinker or another app I will create? To test, I was thinking I could write from Tinker a value for A0 then use A0 as a value for an equation in my loop.
The objective is to change a setting without the need to re-program every time.
any advice will help!
Thanks!

hi @Dup,

Sure! A Spark.function is a good way to change a parameter you have in your core firmware. This Javascript could be adapted to control the Tinker app or a pin on the that app easily.

bko!

I tried changing A0 but I get value of 10, -10 or 0. Must something be attached to the actual pin so my program can read the value of the pin?

Must it be a pin value or can I have any pre-set value be changed?
thanks!

1 Like

Hi @Dup

If you are doing an analogRead of a pin that is not driven by anything, you will just get random values. Tinker is just am example–you can do your own thing very easily.

If you want to use a Spark.function to set a parameter, that is, something you want to change to “tune” you code to behave better on the fly, that is a perfect use. You would just do:

int myParam = 0;  // or other initial value

void setup()
{
  Spark.function("setparam", setParam);
  Spark.variable("getparam", &myParam, INT);
  // whatever other setup you need
}

void loop() {
//you code that uses myParam
}

int setParam(String paramValue) {
    myParam = paramValue.toInt();
    return 0;
}

Now from the web page you can read and set the parameter value and in your program, you just use myParam however you want.

1 Like

Thanks!
I added int setParam(String paramValue); above setup

and I get {
“id”: “xxxxxxxxxxxxxxxxxxxxxx”,
“name”: “xxxdevice_namexxxxx”,
“last_app”: null,
“connected”: true,
“return_value”: -1
}

when using this
curl https://api.spark.io/v1/devices/xxxxxxxxxxxxxxx/setParam -d access_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -d “myParam=188”

Hi @Dup

I just tested the exact code I posted above it worked great for me. OK I think I see what is wrong–you are calling setParam in curl and you should be calling setparam, all lower case! On the web, the name is the thing in double quotes when you setup the Spark.function.

curl  https://api.spark.io/v1/devices/<<dev id>>/setparam?access_token=<<access token>> -d "myparam=17"
{
  "id": "<<dev id>>",
  "name": "<<name>>",
  "last_app": null,
  "connected": true,
  "return_value": 0
}

curl  https://api.spark.io/v1/devices/<<dev id>>/getparam?access_token=<<access token>>
{
  "cmd": "VarReturn",
  "name": "getparam",
  "result": 17,
  "coreInfo": {
    "last_app": "",
    "last_heard": "2014-05-17T14:47:57.347Z",
    "connected": true,
    "deviceID": "<<dev id>>"
  }
}

bko!
awesome!! it worked !!

thanks again!

1 Like

I dont yet have a servo, so can you explain how I can read a analogue input and or a temperature ic

Hi again @kiwibird1

First try looking at this basic example for how to use a Spark variable and a temperature sensor

http://docs.spark.io/#/examples/measuring-the-temperature

Sorry–this tutorial here may be a bit advanced. I should have pointed you to this tutorial which handles the exact thing you want to do:

Sorry I cut-and-pasted the wrong tutorial pointer for you!

Hi bko. I get the temp sensor bit, but not sure how to get that via the html file. Thanks for your patience as my knowledge of some of these specs its very low.

this code gives red led flashing since the servo section is added.
Needs factory reset to fix
Can anyone help?

#include "spark_disable_wlan.h"
#include "spark_disable_cloud.h"

// Read temperature
// -----------------

// Create a variable that will store the temperature value
int temperatureraw = 0;
double voltage=0;
double temperature=0;

// -----------------------------------
// Controlling LEDs over the Internet
// -----------------------------------

// name the pins
int led1 = D0;
int led2 = D1;
int led8 = D7;
int wififailurecounter = 0;
int BeepCounter = 0;

int ledControl(String command);

// controlling servo
Servo myservo;  // create servo object to control a servo

int pos = 0;    // variable to store the servo position


void setup()
{
    Spark.variable("temperature", &temperature, DOUBLE);

    Serial.begin(9600);
    Serial.println("Booting up");
    //register the Spark function
    Spark.function("brew", brewCoffee);

    // Connect the temperature sensor to A7 and configure it
    // to be an input
    pinMode(A7, INPUT);

    //Register our Spark function here
    Spark.function("led", ledControl);

    // Configure the pins to be outputs
    pinMode(led1, OUTPUT);
    pinMode(led2, OUTPUT);
    pinMode(led8, OUTPUT);

    // Initialize both the LEDs to be OFF
    digitalWrite(led1,LOW);
    digitalWrite(led2,LOW);
    digitalWrite(led8,LOW);
    

    myservo.attach(A0);  // attaches the servo on the A0 pin to the servo object
    Spark.function("setpos", setPosition);
    Spark.variable("getpos", &pos, INT);
}

void loop()
{
    // Keep reading the temperature so when we make an API
    // call to read its value, we have the latest one
    temperatureraw = analogRead(A7);
    voltage = ( (double)temperatureraw * 3.3)/4095;

    temperature  =(voltage-0.5)*100;


    //digitalWrite(led1,LOW);
    
    
    // delay(1000);

    // Prints out the network parameters over Serial
    Serial.println(Network.SSID());
    Serial.println("test");
    Serial.println("test:");
    Serial.println("wififailurecounter:");
    Serial.println(wififailurecounter);
    Serial.println(Network.gatewayIP());
    Serial.println(Network.subnetMask());
    Serial.println(Network.localIP());
    Serial.println(Network.RSSI()); // strength -127 = no signal.
    Serial.println("wififailurecounter:");
    Serial.println(wififailurecounter);

    
    delay(2000);
    
    digitalWrite(led8,HIGH);
    digitalWrite(led1,HIGH);
    delay(5);
    
    BeepCounter=wififailurecounter;
    while (BeepCounter >0) //IF IT FAILES, BEEP ONCE FOR EACH FAILURE
    
    {
        BeepCounter--;
        digitalWrite(led1,HIGH);
        delay(500);
        digitalWrite(led1,LOW);
        delay(500);
        
    }
    
    
    digitalWrite(led8,LOW);
    digitalWrite(led1,LOW);
    
    //delay(1000);
    brewCoffee("coffee");
}

// This function gets called whenever there is a matching API request
// the command string format is l<led number>,<state>
// for example: l1,HIGH or l1,LOW
//              l2,HIGH or l2,LOW




//this function automagically gets called upon a matching POST request
int brewCoffee(String command)
{
    //look for the matching argument "coffee" <-- max of 64 characters long
    if(command == "coffee")
    {
        //  digitalWrite(led8,HIGH);
        //do something here
        //activateWaterHeater();
        //activateWaterPump();
        return 1;
    }
    else return -1;
}


int ledControl(String command)
{
    int state = 0;
    //find out the pin number and convert the ascii to integer
    int pinNumber = (command.charAt(1) - '0') - 1;
    //if (command=="8") pinNumber=7;
    //if (command=="2") pinNumber=1;
    //if (command=="1") pinNumber=0;
    //Sanity check to see if the pin numbers are within limits
    if (pinNumber < 0 || pinNumber > 7) return -1;

    // find out the state of the led
    if(command.substring(3,7) == "HIGH") state = 1;
    else if(command.substring(3,6) == "LOW") state = 0;
    else return -1;

    // write to the appropriate pin
    digitalWrite(pinNumber, state);
    // digitalWrite(7, state);

    return 1;
}

int setPosition(String posValue) {
    pos = posValue.toInt();
    myservo.write(pos);
    return 0;
}

So at the beginning of your code, you turn off both the wireless LAN and the Spark cloud. Do you ever turn them back on?

I am not sure I see what you are trying to do–without both the cloud and the WLAN, the Spark variables and functions will not work.

when the first two lines are removed, it works. :slight_smile: The servo home is set wrong to it jams at under 5 degrees.
How thing works erratically intermittently. Will replace servo when I get a chance. lateny is about 2 seconds. A bit slower than I would like, but I can live with that.

Glad it is working! You can set the pos variable to 90 to center the servo at power up, if you like.

Thank you for an excellent tutorial! Unforunately I am having difficulty, but it’s with the javascript.

Why does this concatenation not work? Say I have 255 entered into a text input field with id=‘rVal1’, if I try to put this value in a string like this:

var pr = "pr=";
var prval = document.getElementById('rVal1').value.toString(256);
var pg = ",pg="
var term = pr + prval + pg;
document.getElementById('rVal1').innerHTML = term; renders as
pr=,pg=

But, document.getElementById(‘rVal1’).innerHTML = prval; renders as


255

Hi @Elijah

The toString() method works on a numeric object like an integer or floating point number. If given an argument like you do with 256 above, it treats that as the base, 2 for binary, 10 for decimal, 16 for hex. But 256 is not a legal base.

var pr = "pr=";
var prval = document.getElementById('rVal1').value.toString();
var pg = ",pg="
var term = pr + prval + pg;

Then term should be the string “pr=255,pg=”

What string are your trying to get?

[Issue solved: The problem was defining variables outside of the function scope where document.getElementById(‘rVal1’).innerHTML = term;was declared (see HTML below), so by moving the var pr, var prval, var pg,andvar term lines to within setColor() { } it works (without 256 argument to toString, thank you @bko)!

I think I was a little too liberal with my interpretation of ‘radix’, but this does not appear to be root cause: Here is basic HTML to reproduce the issue (js/jquery.js) refers to latest stable .min from jquery.org and downloaded to a local directory /js/ and renamed jquery.js:


The following code contains an error in the javascript, see top of post for fix

<code=“lang-auto”><html>
<script src=“js/jquery.js” type=“text/javascript” charset=“utf-8”></script>
<body>
<div class=“container”><div class=“control”><div><label>R</label><input type=“text” id=“rVal1” /></div></div></div>
<button type=“button” id=“colorsel” onclick=“setColors()”>Change colors!</button>
<p>Debugging Info: </p>
<i id=“returnVal”>Output…</i>
<button type=“button” onclick=“changeText()”>Grab rVal1</button>
<script type=“text/javascript”>
function changeText() {
var userInput = document.getElementById(‘rVal1’).value.toString();
document.getElementById(‘returnVal’).innerHTML = userInput;
}
</script>
<script type=“text/javascript”>
var pr = “pr=”;
var prval = document.getElementById(‘rVal1’).value.toString();
var pg = “,pg=”;
var term = pr + prval + pg;
function setColors() {
document.getElementById(‘returnVal’).innerHTML = term; //renders pr=,pg=
}
</script>
</body>
</html>
1 Like

Nice example Brian <@bko>,

Great job, this should open up a world of possibilities for everyone!

My idea light bulb is burning brightly :smile:

Performance Issue:
I noticed that adding a couple of JSON variable requests to the “window.setInterval” 1 second tasks, the updates do not occur at 1 second intervals. In fact, this results in my Spark seemly to lose connection with the cloud intermittently (The blue LED flickers for a while and then returns to the normal pulsing).

This might be an issue with jQuery/javascript.But it is definitely annoying.

I was wondering if there is a method available from the Spark Code side to pack multiple variables in a single JSON structure returned from the POST?

Let’s say a variable “loopcount” returned:
{
“cmd”: “VarReturn”,
“name”: “loopcount”,
“result”: 1209,
“coreInfo”: {
“last_app”: “”,
“last_heard”: “2014-06-27T15:55:03.840Z”,
“connected”: true,
“deviceID”: “”
}
}

Can we have multiple variable returned?

For example:
{
“cmd”: “VarReturn”,
“name”: “loopcounts”: {
“loop1_result”: ,
“loop2_result”: ,
“loop3_result”: ,
}
“coreInfo”: {
“last_app”: “”,
“last_heard”: “2014-06-27T15:55:03.840Z”,
“connected”: true,
“deviceID”: “”
}
}

It would seem to be an insignificant time difference whether 1 or 10 variables are returned from a single Query,
which would be much more efficient.

Is this possible?

HI @surfnturf57

You should see how I do this in my publishing a JSON tutorial. You can return a C-style char array string as a Spark variable and then parse the string as a JSON.