Spark.variable() STRINGS larger than 9 chars issue [SOLVED]

I have determined that when using the Spark.variable() for string lengths larger than 9 characters results in a timeout error.

The following code example will set the variable output to a 9 character string for 10 seconds and turn off the onboard D7 LED, then set output to a 10 character string for 10 seconds and turn on the D7 LED.

#define UPDATE_INTERVAL 10000
bool state = false;
char output[20];
uint32_t lastTime = 0;

void setup() {
  Serial1.begin(9600);
  Spark.variable("read", &output, STRING);
  pinMode(D7,OUTPUT);
}

void loop() {
  if(millis() - lastTime > UPDATE_INTERVAL) {
    lastTime = millis();
    
    if(state) {
      strcpy(output, "1234567890");
      Serial1.println(output);
    }
    else {
      strcpy(output, "123456789");
      Serial1.println(output);
    }
    digitalWrite(D7,state); // Low when 9 chars, High when 10 chars
    state = !state;
  }
}

If you send a GET request:
https://api.spark.io/v1/devices/DEVICEID/read?access_token=ACCESSTOKENID

…for the output while the string is 9 chars, it returns fine.

{
cmd: "VarReturn"
name: "read"
TEMPORARY_allTypes: {
string: "123456789"
uint32: 825373492
number: 825373492
double: 1.0300843656201408e-71
float: 2.593151471330657e-9
raw: "123456789"
}-
result: "123456789"
coreInfo: {
last_app: ""
last_heard: "2014-01-06T02:16:03.710Z"
connected: true
deviceID: "xxxxx"
}-
}

But if you request for the output when it is 10 chars or larger, after 10 seconds it returns a timeout error:

{
error: "Timed out."
}

15 seconds later it drops off the cloud momentarily (fast blinking cyan), and reconnects. Only after this happens can you try to read the 9 character output successfully. If you manage to try to read it after the 10 char timeout, it will also time out.

Obviously it would be very nice to be able to read strings larger than 9 characters :wink: One application would be generating JSON string with multiple key:value pairs, then parsing it in whatever is doing your requesting with something like var obj = JSON.parse(sparkString);

5 Likes

Awesome!!

Related:

That is a great catch!

1 Like

Thanks. Added the code example that I referred to… somehow I forgot to add it.

1 Like

Interesting… I’ve run into this, but it’s just a simple request to see the value of D7 using the following URL: https://api.spark.io/v1/devices/xxxxx/digitalread?access_token=xxxxx&params=D7

Here is the output I get: (replacing id with xxxxx)

{
  "cmd": "VarReturn",
  "name": "digitalread",
  "TEMPORARY_allTypes": {
    "string": " \u0000P\u0000",
    "uint32": 536891392,
    "number": 536891392,
    "double": null,
    "float": 1.0868491504456741e-19,
    "raw": " \u0000P\u0000"
  },
  "result": " \u0000P\u0000",
  "coreInfo": {
    "last_app": "",
    "last_heard": "2014-01-14T07:14:22.952Z",
    "connected": true,
    "deviceID": "xxxxxx"
  }
}

You will get this cryptic response when you have done something in error…

Do you have the Tinker app flashed to your Core, and are using the correct core ID and access_token?

You’ll need a little bit more param for toggling D7:
https://api.spark.io/v1/devices/xxxxx/digitalread?access_token=xxxxx&params=D7,HIGH

Also, it needs to be a POST request, meaning you can’t just put this one in your browser’s address bar. Those only generate GET requests. This is likely your real issue.

Here’s one way to do it with a nice Chrome extension:

Dang I wasn’t clear enough, my bad.

“digitalwrite” is working just fine, it’s the “digitalread” that is returning what I pasted above.

I am using the Tinker App on the core and since I’ve made a successful digitalwrite I at least know I’ve got the right token and core ID being sent.

I tried making a POST request to “digitalread” (after setting the D7 pin to HIGH assuming that’s what I would get back), but the return_value is 0 and it also sets my D7 pin to low automatically (or that’s what I assume since it’s shutting off the LED).

That’s actually the correct behavior. You can’t set a pin HIGH using digitalWrite and then read that value, because when you switch to digitalRead, you are setting the pin as an input, and its previous state (determined when it was an output) disappears.

If you’re trying to test out basic Input/Outputs, set a pin (for example, D0) to HIGH, and then wire up D0 to D1, and then read the value from D1. That should give you a true reading.

1 Like

Alright thanks for the reply.

Maybe the name “digitalRead” is what’s throwing me off from an API perspective. Was thinking that if I needed to read the value of a pin (maybe digital is a dumb example, but getting the value of analogRead for the temp sensor would be something I want to query semi-quick) that I would “read” the value of the pin itself.

Say if I did want to poll for a pin’s value over and over is that a custom method that I need to write? (ie. losing the Tinker App)

edit: (since I can’t reply more than 3 times to the same topic… Not really cool for a new user or anyone really, anyways…)

Ahhh! Yes, the drift has been caught!

That makes much more sense and on par with what I would expect. My last question for the thread though is if you guys are throttling API requests (I assume so), and if so, by how much? I’d like to code it in my script so that it only polls once every 500ms or when the response comes back (whichever is larger).

No you’re thinking about the role of digitalread and analogread correctly; they’re used to read the values of something attached to that pin externally, like a sensor. So if you had, for instance, a potentiometer wired up to A0, you could turn the potentiometer and see the value change as you call analogread on A0.

However the disconnect comes into play for the LED attached to D7. When you see the LED at D7 is on, that is because the D7 pin is driven HIGH, and D7 is acting as an output. However if you then try to digitalread D7, it is now an input, so it is no longer driving the pin HIGH, and therefore the LED turns off. Something external must drive the pin HIGH, whether it’s an attached button or sensor, or another pin that is wired up to the pin that you’re reading from.

Catch my drift?

Just a heads up that we’re removing TEMPORARY_allTypes today!

1 Like

@BDub I hope this helps:

2 Likes

Thanks for this pull request, I tested this on my core, and merged it into master / compile-server2, so if you build / flash on the build site, it’ll be included. :smile: – I think there is probably more to be done here with regards to very long responses, but this seemed like a safe improvement in the meantime.

My only regret is that I couldn’t merge all the things yet.

Thanks!
David

2 Likes

Until the very end of debugging I was 50% sure it was problem in server code, so I’m glad that it actually paid off.

If I read the code correctly, the maximum length should be now somewhere near 600? In reality I think prints and String objects use 256 which should be enough for a remotely read variable. I’m not trying to push limit myself, I only need max 32 or so.

I would have to look this up, but I think long-ish payloads might require an extra CoAP packet, not 100% sure – I’m testing now to see how long the strings can be – it looks like the limit is between 230 and 240

2 Likes

My guess was purely based on 640 queue I saw somewhere, but even 160 is longer than a tweet and with neatly formatted data its quite long.

1 Like

Awexome guys! I looked at the code… not understanding how the queue[] works just by the DIFF, but great job @weakset :slight_smile:

2 Likes

The ~240 limit does make sense, because I realized the queue is an array of unsigned chars. So even if the queue could contain a longer string, the size indicator in can’t be more than 255 (and queue has some headers, like the size indicator itself.)

I made calculations and limit should be 239.
(239& ~15) + 16 = 240 (still room for headers).
(240& ~15) + 16 = 256 (already over 255 without headers).

I missed a spot. There was that 6 char header, so limit is 233. This should truncate it so API won’t time out anymore.

2 Likes

Did the support for “Strings as variables greater than 9 chars” ever get resolved… I’m still seeing weird String values for variables returned from core…

I’d love to be able to return JSON strings even if they are restricted to just over 200chars.

1 Like