Using the Tinker API

Hello,

I’m trying to run the analogread POST sample from the docs, replacing the device ID and access_token with my Spark Core’s, but I keep getting:

 "code": 400,
    "error": "invalid_request",
    "error_description": "The access token was not found"

I’ve tried both CURL and using the RestClient chrome extension to test the call and I get the same response.
Also, I’ve tried resetting the access tokens a few times and that didn’t change anything.

Any hints/tips on how I could move on ?

Thanks!

Hi @georgeprofenza

Have you claimed your core, either in the iOS or Android app or using Spark CLI or USB? Does your core show up on the target tab of the web IDE (the BUILD button)? If not, then you need to claim it, that associate you account with that core.

If it is claimed, I would cut-and-paste the device ID and access token from the web IDE. You can find the the device ID by click on the little target icon and then on the arrow to the right of the core name. The access token as I am sure you know is available after you click the gear icon.

The only real thing that matches your symptom is that one of the numbers is wrong.

@bko Thanks Brian, I’m using the Android app, I’ve connected it and in the Spark Build IDE Cores option I can see the id (which is copy into the curl/post request, replacing the test id) and from settings I can fetch the Access Token. I’ve tried resetting the token but no joy.

Is it possible by any chance you could test my device at your end ?

What else should I check/test ?

Thank you,
George

Try to reflash the Tinker firmware and use the Android App to control D7.

It should be working fine since your core is listed in the Web IDE :slight_smile:

just did that now and I see no change :frowning:

I’m not trying to log out-log in, reset the device name and access token and hope for the best.

I feels I’m missing something and I can’t figure out what.

What else should I try ?

Maybe you should cut-and-paste your curl command here (editing out the access token at least and the device id if you want).

Sometimes curl can be finicky about how you call it–for instance, the Tinker application function names are all lower case, not camel case–that messed me up for a while:


	Spark.function("digitalread", tinkerDigitalRead);
	Spark.function("digitalwrite", tinkerDigitalWrite);

	Spark.function("analogread", tinkerAnalogRead);
	Spark.function("analogwrite", tinkerAnalogWrite);

Can you show us an example with the private codes removed?

A bit more stuck. After removing the spark from Web IDE, I can’t seem to reclaim it on the android app. The Core itself seems connected(cyan LED breathes), but the app lists No Cores found.

What should I do ?

I would try a factory reset which is described here:

http://docs.spark.io/#/troubleshooting/can-t-get-connected-step-3-reboot-and-clear-memory

You will need to setup the WiFi again but it should remained “claimed”.

One other thing that has screwed some people up: the web IDE makes it easy to create a new account if it doesn’t find cookies from your last visit, so you can end up with multiple accounts if you are not careful. The core only shows up on the email account that claimed it.

[Edit] You also need to be on the same subnet as the core in order to claim it on the Android app.

I can’t seem to reclaim the Spark Core with the Android app. I’ve read the instructions you linked to and managed to get the device ID via USB and use the Spark Build web app to claim it.

I still can’t access the device on the Android phone and the post does not work:

curl https://api.spark.io/v1/devices/MyDeviceIDHERE/analogread -d access_token=MyAccessToken -d params=A0

Thanks again @bko!
At this point I’m less concerned with using the Android App and more interested in using the Tinker REST API.
Any hints on how I can do that ?

I’ve got an update: using my computer the POST didn’t work, but using another computer on the same network worked like a charm. Can’t work out what’s wrong though.

If there’s an easy way to figure out why my computer doesn’t play well, that would be great,but for now I’ll stick to using the other one.

Also, I plan to poll the api for a read once every few seconds, but I think that’s not very nice for your servers. Is there an easy way using the Tinker API to listen for an event when a read value goes past a threshold ? I’m guessing I should write some code in Spark Build, but am a bit worried about loosing control over the Spark again.

HI @georgeprofenza

Are the two computers (the one that works and the one that doesn't) on the same wireless network? The same OS? I guess I would run curl -v ... to print more information. You could also try reinstalling curl on the bad one.

You should look at the Spark.publish API and this tutorial:

or this one

Hi,

Thank again for all the resources. I’ve spent some time yesterday to go through the tutorials and Cloud API reference.
I did get the publish() , variable() and function() examples to work. Managed to use the TCP client working as well in one direction.

The problem is today when I tried to reflash the apps/sketches wrote yesterday, they didn’t seem to work today. I keep getting a 404:

{
   "ok":false,
   "error":"Function not found"
}

I’ve tried reflashing a few times, resetting the tokes and updating the html code but no joy.
For reference here is some of the code I used:

bool isBlinking;
int ledState;
unsigned long wait,lastTime;
void setup()
{
   //Register our Spark function here
   Spark.function("led", ledControl);
   Spark.function("ledBlink", ledBlink);
   // Configure the pins to be outputs
   pinMode(D7, OUTPUT);
   digitalWrite(D7, LOW);
   wait = 1000;//default led delay
}


void loop()
{
    if(isBlinking){
        unsigned long now = millis();
        if (now-lastTime>wait) {
            lastTime = now;
            ledState = 1-ledState;
            digitalWrite(D7, ledState);
        }
    }
}


int ledControl(String command)
{
   int state = 0;
   
   if(command == "HIGH") state = 1;
   else if(command == "LOW") state = 0;
   else return -1;
   isBlinking = false;    
   digitalWrite(D7, state);
   return 1;
}
int ledBlink(String delayString){
    int state = 0;
    int value = atoi(delayString.c_str());
    if(value < 0) value = 10;
    if(value > 10000) value = 10000;
    wait = value;
    isBlinking = true;
    return 1;
}

And client code:

<!DOCTYPE html>
<html>
<head>
    <title>Spark POST test</title>
	<meta name="viewport" content="width=device-width,initial-scale=2">
	<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
	<script>
		var deviceID = "###";
        var accessToken = "###";
        
		function sparkToggle(value){
			$.post( "https://api.spark.io/v1/devices/"+deviceID+"/led", { access_token: accessToken, params: value }).done(sparkPostCallback);
		}
		function sparkBlink(value){
			$.post( "https://api.spark.io/v1/devices/"+deviceID+"/ledBlink", { access_token: accessToken, params: value }).done(sparkPostCallback);
		}
		function sparkPostCallback(data){
			console.log(data);
		}
	</script>
</head>
<body>
	<form>
		<input type="button" id="high" value="HIGH" onclick="sparkToggle('HIGH');"><br />
		<input type="button" id="low" value="LOW" onclick="sparkToggle('LOW');"><br />
		<input type="range" name="blink" min="10" max="1000" value="500" onchange="sparkBlink(this.value);blinkLabel.value = 'delay:'+this.value;"/><br />
		<input type="text" name="blinkLabel" disabled/>
	</form>
</body>
</html>

and here’s the modified TCP test:

TCPClient client;
String ip = "192.168.0.5";
bool wasConnected;
void ipArrayFromString(byte ipArray[], String ipString) {
  int dot1 = ipString.indexOf('.');
  ipArray[0] = ipString.substring(0, dot1).toInt();
  int dot2 = ipString.indexOf('.', dot1 + 1);
  ipArray[1] = ipString.substring(dot1 + 1, dot2).toInt();
  dot1 = ipString.indexOf('.', dot2 + 1);
  ipArray[2] = ipString.substring(dot2 + 1, dot1).toInt();
  ipArray[3] = ipString.substring(dot1 + 1).toInt();
}

int connectToMyServer(String ip) {
  byte serverAddress[4];
  ipArrayFromString(serverAddress, ip);

  if (client.connect(serverAddress, 9000)) {
    return 1; // successfully connected
  } else {
    return -1; // failed to connect
  }
}

void setup() {
  Spark.function("connect", connectToMyServer);
    
  for (int pin = D0; pin <= D7; ++pin) {
    pinMode(pin, OUTPUT);
  }
  pinMode(A0,INPUT);
  Serial.begin(9600);
  Serial.println("OK Computer");
}

void loop() { 
  int A0Value = analogRead(A0);
  if (client.connected()) {
    if (client.available()) {
      char pin = client.read() - '0' + D0;
      char level = client.read();
      if ('h' == level) {
        digitalWrite(pin, HIGH);
      } else {
        digitalWrite(pin, LOW);
      }
      //client.println(level,DEC);
    }
    // if(!wasConnected){
    //     wasConnected = true;
    //     client.println(Spark.deviceID());//this crashes the server and I don't know why
    // }
    if(A0Value > 0) {
        client.stop();
        // wasConnected = false;
    }
  }else{
    if(A0Value == 0) connectToMyServer(ip);
  }
}

The only change from the documentation sample is I’m also hard coding the server ip and connecting/disconnecting A0 from ground to connect/disconnect from the TCP server.
The server is a minimal Processing application:

import oscP5.*;
import netP5.*;

OscP5 server;
TcpClient client;
int numClients;
void setup(){
  server = new OscP5(this, 9000, OscP5.TCP);
}
void draw(){
  numClients = server.tcpServer().getClients().length;
  if(client == null && numClients > 0) client = server.tcpServer().getClient(0);
  if(numClients == 0) client = null;
  background(numClients == 0 ? color(192,0,0) : color(0,192,0));
}
void keyPressed(){
  if(numClients > 0){
    if(key == '=') client.send("7h");
    if(key == '-') client.send("7l");
  }
}

Going through the tutorials I have more questions that will go outside of the scope of the original question and I am more than happy to post different threads if that makes it easier for the community.

Related to this thread my questions would be:

  • how can I test/debug/check that the the firmware I expect (from Spark Build) is on the board and can communicate with the Cloud API. So far the LED statuses are my only hints
  • is there a limit for the Cloud API ? Will my board be temporarily banned if I make to many posts ? What are the best practices on this side ?

Thank you again,
George

Hi @georgeprofenza George!

Your firmware and web Javascript worked great for me with a non-local jquery.min.js (I borrowed version 1.3.2 from Google). Are you sure your jquery install is good and permissions are OK?

There are some errors on the Javascript console that might want to look at, but it worked. My little blue LED is blinking away.

For the TCP test, you would really benefit from the IPAddress class that is built-in but I see we need to write up some doc for it.

It was not clear to me what is working and what you still might need help with, so just ask if you need more help. The flashing LED code works; does the TCP code work?

There is a limit for the Spark.publish() API of an average of 1 per second but a burst of up to 4 is allowed. There are not limits for Spark.function and Spark.variable but please try to be a good cloud neighbor.

I have a serial LCD display that I use a lot for debugging. You can also use the USB serial connection to debug, just like Arduino. Depending on your platform, you may need to read the instruction for getting it to work on Windows.

I also use Spark.publish() and Spark.variable() for debugging sometimes, usually with curl as the way to get them.

Hi Brian @bko ,

Thank you for the the helpful comments.

The code seems to be behaving normally now (both the POST test and the TCP Test).
Thank you for the IPAddress hint. I think I spotted that in a UDP example on a forum post somewhere
related to OSC. That should make it simpler/neater than what I have so far.

With the TCP sample, last night eventually I got the code to work again, but for some reason when I grounded pin A0 it kept connecting/disconnecting all the time, but today it works just fine. I’m not sure why that happens. Perhaps the Spark needed a rest too. I’m not sure what to do when the board is not behaving.

Thank you for giving an idea of how to use the Cloud API efficiently and considerately.

The LCD display sounds like a good tip. I’ve tried using the USB serial connection, but using the Cloud API stuff at the same time and it didn’t work. Is it possible to use the Serial at the same time with Spark.function() or TcpClient ?

Thanks,
George

1 Like

Just curious, is A0 set as an Output or Input pin?

Not sure if a pull-down resistor of 10Kohm would help make it stable. So the normal state will be grounded through the resistor.

Or do the Pull-up instead depending on the normal condition you require :slight_smile:

Yes, this is possible, but you do have to watch out for some things. The best way is to set a global variable in the Spark.function() and respond to in your main loop(). That way you can avoid bad interactions.

Awesome!
I modified the TCP code a bit so now I can additionally connect to a TCP server by passing the ip using Serial.
Not sure it’s very practical in the real world, but it was handy to test Serial :smile:

Now to figure out what why the TCP server crashes when I use client.write()/client.print()/client.println() from the Spark :frowning:
Tried with a TCP server in Processing and Openframeworks and both crash.
Trying a minimal python server next.