Getting an Array of data from spark :)

Although documenting it obviously wouldn’t hurt, it’s more of a general rule that files should not contain ‘strange’ characters. Many operating systems and/or programs will not know what to do with them, and thus give you an error. The same is true for spaces in names, which should also be avoided.

2 Likes

There really isn’t a variable, if you want to get the string that you can pass to publish, for instance, you need to pass a char array to the printTo function (instead of Serial, like in the example code),

    char jsonString[255];
    root.printTo(jsonString, sizeof(jsonString));
    Particle.publish("someName", jsonString);
1 Like

Yes, of course you’re right @Moors7 .
I am too used since 30 years to working with a Mac.
When working with Windows PC I also used to avoid this…


@Ric : Thanks for your clarification of the JSON formatting!
I’ll see if I can apply it in the sketch posted yesterday…

:ok_hand:

1 Like

@Ric : That’s really great, it works right away!
This will be very useful for me…
It’s great to have this choice between JSON and SPRINT formatting. I learnt a lot today, but I still have lots more to learn…
But that’s OK, I’m only 61 now :wink:

1 Like

@Ric
There we are again :disappointed_relieved:

I tried to use the JSON method to send variables, concatenated into one string from one Particle to another one, using Publish & Subscribe.

My Publishing sketch works fine:

/* JSON Generator & Publish test
*/

#include "SparkJson/SparkJson.h"

// Initiate all variables we want to package in a JSON string:
char Controller[ ] = "ECO system";
int T1=0;
int T2=0;
int T3=0;
int T4=0;
int T5=0;
int T6=0;
int T7=0;
int Energy=0;

char jsonString[255];
StaticJsonBuffer<300> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();


void setup()
{
 Serial.begin(9600);
}

void loop()
{
// Modify the variables in each cycle, to simulate updates:
 T1=T1+1;
 T2=T2+2;
 T3=T3+3;
 T4=T4+4;
 T5=T5+5;
 T6=T6+6;
 T7=T7+111;
 Energy=Energy+1;
 
 // Put variables in JSON format:
 root["Controller"] = Controller;
 root["T1"] = T1;
 root["T2"] = T2;
 root["T3"] = T3;
 root["T4"] = T4;
 root["T5"] = T5;
 root["T6"] = T6;
 root["T7"] = T7;
 root["Energy"] = Energy;
 
 root.printTo(jsonString, sizeof(jsonString));

// Output variables as a JSON string:
 Serial.println(jsonString);
 Particle.publish("Status", jsonString,60,PRIVATE);
 delay(5000);
}

But with the Subscribing sketch, I get this error message:

In file included from SparkJson/./ArduinoJson.h:7:0,
                 from SparkJson/SparkJson.h:13,
                 from json_parsing_and_subscribe.cpp:4:
SparkJson/././DynamicJsonBuffer.h: In destructor 'ArduinoJson::DynamicJsonBuffer::~DynamicJsonBuffer()':
SparkJson/././DynamicJsonBuffer.h:20:33: warning: deleting object of polymorphic class type 'ArduinoJson::DynamicJsonBuffer' which has non-virtual destructor might cause undefined behaviour [-Wdelete-non-virtual-dtor]
   ~DynamicJsonBuffer() { delete _next; }
                                 ^
json_parsing_and_subscribe.cpp: In function 'void myHandler(const char*, const char*)':
json_parsing_and_subscribe.cpp:54:7: error: expected primary-expression before ']' token
   
       ^

make[1]: *** [../build/target/user/platform-6json_parsing_and_subscribe.o] Error 1
make: *** [user] Error 2
Error: Could not compile. Please review your code.

Here is my troublesome Subscribing sketch:

/* JSON Parsing & Subscribe test
*/

#include "SparkJson/SparkJson.h"

char json[255];

void setup()
{
  Serial.begin(9600);
  Particle.subscribe("Status", myHandler, MY_DEVICES);
}


void loop()
{
  StaticJsonBuffer<300> jsonBuffer;

  JsonObject& root = jsonBuffer.parseObject(json);

  if (!root.success())
  {
    Serial.println("parseObject() failed");
    return;
  }

  const char* Controller = root["Controller"];
  int T1 = root["T1"];
  int T2 = root["T2"];
  int T3 = root["T3"];
  int T4 = root["T4"];
  int T5 = root["T5"];
  int T6 = root["T6"];
  int T7 = root["T7"];
  int Energy = root["Energy"];

// Print all variables separately:
  Serial.println(Controller);
  Serial.println(T1);
  Serial.println(T2);
  Serial.println(T3);
  Serial.println(T4);
  Serial.println(T5);
  Serial.println(T6);
  Serial.println(T7);
  Serial.println(Energy);
  Serial.println();
  
  delay(3000);
}

void myHandler(const char *event, const char *data)
{
 json[] = data;
}

Obviously, I’m doing something wrong in the “myHandler” function where the published string “data” should be registered in the “json[]” array.

But I have not enough experience to see how to correct it…
Any idea?

I’m not sure what you’re trying to do with that code. The code you have in loop, shouldn’t be in loop, since you don’t want it running over and over, and you don’t have “json” defined there (that you’re passing to parseObject). I think you want to put the code in the subscribe handler. The passed in data already is a json string, but you need to convert it to a char* (from const char*) which you can do with strdup. I think this should work,

void myHandler(const char *event, const char *data) {
    StaticJsonBuffer<255> jsonBuffer;
    char *jsonString = strdup(data);
    JsonObject& root = jsonBuffer.parseObject(jsonString);

    if (!root.success()) {
            Serial.println("parseObject() failed");
            return;
        }
    
       const char* Controller = root["Controller"];
   int T1 = root["T1"];
   int T2 = root["T2"];
   int T3 = root["T3"];
   int T4 = root["T4"];
   int T5 = root["T5"];
   int T6 = root["T6"];
   int T7 = root["T7"];
   int Energy = root["Energy"];
}
1 Like

Oh I see @Ric , thanks for putting me back on track! :+1:

Your modified sketch now gives no errors anymore, great!
But the serial monitor keeps showing “parseObject() failed

Here’s what I’m trying to achieve:

Particle 1 runs the “JSON Generator & Publish” sketch, (see previous posting) sending a message called “Status” with the JSON text string
I can see the message is sent correctly.
For example: {“Controller”:“ECO system”,“T1”:354,“T2”:708,“T3”:1062,“T4”:1416,“T5”:1770,“T6”:2124,“T7”:39294,“Energy”:354}

Particle 2 runs the below “JSON Parsing & Subscribe” sketch. It should ‘catch’ the JSON text string in the “Status” message and split it up into the variables again. These should then be output separately on the serial monitor.

// JSON Parsing & Subscribe test

 #include "SparkJson/SparkJson.h"

char json[255];

void setup()
{
  Serial.begin(9600);
  Particle.subscribe("Status", myHandler, MY_DEVICES);
}


void loop()
{
}

void myHandler(const char *event, const char *data)
{
    StaticJsonBuffer<255> jsonBuffer;

    //char *jsonString = strdup(data);                       // Original line
    //JsonObject& root = jsonBuffer.parseObject(jsonString); // Original line
    JsonObject& root = jsonBuffer.parseObject(data);         // NEW line

    if (!root.success())
    {
        Serial.println("parseObject() failed");
        return;
    }

   const char* Controller = root["Controller"];
   int T1 = root["T1"];
   int T2 = root["T2"];
   int T3 = root["T3"];
   int T4 = root["T4"];
   int T5 = root["T5"];
   int T6 = root["T6"];
   int T7 = root["T7"];
   int Energy = root["Energy"];
   
   // Print all variables separately:
  Serial.println(Controller);
  Serial.println(T1);
  Serial.println(T2);
  Serial.println(T3);
  Serial.println(T4);
  Serial.println(T5);
  Serial.println(T6);
  Serial.println(T7);
  Serial.println(Energy);
  Serial.println();
}

Did we forget something?

Try to Serial.println(data) to see what you actually receive.

I’d also think you can drop the char *jsonString = strdup(data); and directly do JsonObject& root = jsonBuffer.parseObject(data);

1 Like

Thanx again @ScruffR !
I checked what I receive, and that looks like expected:
{“Controller”:“ECO system”,“T1”:103,“T2”:206,“T3”:309,“T4”:412,“T5”:515,“T6”:618,“T7”:11433,“Energy”:103}

When I comment out both (old) lines and I add your (new) line, I get this error message, which is not understandable to me:

You can get rid of that error message by just type casting data

Serial.println((char*)data);

Although I’m not sure why Serial.println() would demand a non-const string - it won’t modify it anyhow :confused:


Update:
Argh :flushed:!
I fell for a mistake I’ve been telling others about no end of time :blush:
The error message shows Serial.println() as the source, but it’s actually the line #34.
I’d have to have a look at the implementation of parseObject() if it actually manipulates the string or if would be safe to cast. Otherwise you could always use an automatic char array and use strcpy() to pull a copy (as I dislike strdup() for its risk of heap fragmentation).

2 Likes

Hi @ScruffR , thanks for your tips, but I am not really following...

The sketch in my previous post works with the "old lines" (before I replaced them by your "new line")
So, the Serial.println(data); command works.

The error message comes only if I replace the 2 "old lines" by your "new line".
Here's the full error message:

In file included from SparkJson/./ArduinoJson.h:7:0,
                 from SparkJson/SparkJson.h:13,
                 from json_parsing_and_subscribe.cpp:11:
SparkJson/././DynamicJsonBuffer.h: In destructor 'ArduinoJson::DynamicJsonBuffer::~DynamicJsonBuffer()':
SparkJson/././DynamicJsonBuffer.h:20:33: warning: deleting object of polymorphic class type 'ArduinoJson::DynamicJsonBuffer' which has non-virtual destructor might cause undefined behaviour [-Wdelete-non-virtual-dtor]
   ~DynamicJsonBuffer() { delete _next; }
                                 ^
json_parsing_and_subscribe.cpp: In function 'void myHandler(const char*, const char*)':
json_parsing_and_subscribe.cpp:34:51: error: invalid conversion from 'const char*' to 'char*' [-fpermissive]
     Serial.println(data); // Check what I receive!
                                                   ^

In file included from SparkJson/././DynamicJsonBuffer.h:9:0,
                 from SparkJson/./ArduinoJson.h:7,
                 from SparkJson/SparkJson.h:13,
                 from json_parsing_and_subscribe.cpp:11:
SparkJson/././JsonBuffer.h:68:15: error:   initializing argument 1 of 'ArduinoJson::JsonObject& ArduinoJson::JsonBuffer::parseObject(char*, uint8_t)' [-fpermissive]
   JsonObject &parseObject(char *json, uint8_t nestingLimit = DEFAULT_LIMIT);
               ^

make[1]: *** [../build/target/user/platform-6json_parsing_and_subscribe.o] Error 1
make: *** [user] Error 2
Error: Could not compile. Please review your code.

I think you still need to copy data to get the proper type to pass to parseObject. You can use strcpy or strncpy instead of strdup as @ScruffR suggested, but this doesn’t fix the problem of the parse failing. I changed the code to the following, just to check that data didn’t have any non-printable characters that were screwing up the parsing, but there weren’t any (the data starts with 123 and ends with 125 which are the ascii codes for “{” and “}”),

void myHandler(const char *event, const char *data) {
    char jsonString[255];
    strncpy(jsonString, data, 255);
    //Serial.println(jsonString);
    int counter = 0;
    while (jsonString[counter] != 0) {
        Serial.printf("%d ", jsonString[counter]);
        counter ++;
    }
    
    Serial.println();
    JsonObject& root = jsonBuffer.parseObject(jsonString);

    if (!root.success()) {
        Serial.println("parseObject() failed");
   // return;
    }

So, I’m not sure why the error is occurring. Data, and jsonString are valid json.

2 Likes

Ok, I found the problem. jsonBuffer was too small. This code worked,

void myHandler(const char *event, const char *data) {
    StaticJsonBuffer<512> jsonBuffer;
    char jsonString[255];
    strncpy(jsonString, data, 255);
    Serial.println(jsonString);
    Serial.println();
    JsonObject& root = jsonBuffer.parseObject(jsonString);

    if (!root.success()) {
        Serial.println("parseObject() failed");
         return;
    }
    
    const char* Controller = root["Controller"];
   int T1 = root["T1"];
   int T2 = root["T2"];
   int T3 = root["T3"];
   int T4 = root["T4"];
   int T5 = root["T5"];
   int T6 = root["T6"];
   int T7 = root["T7"];
   int Energy = root["Energy"];
   
    Serial.println(Controller);
    Serial.println(T1);
    Serial.println(T2);
    Serial.println(T3);
    Serial.println(T4);
    Serial.println(T5);
    Serial.println(T6);
    Serial.println(T7);
    Serial.println(Energy);
    Serial.println();
}

I checked several sizes. 260 did not work, 300 did.

2 Likes

Fantastic work @Ric !!!
It’s so great to see this work…
Now I can finally realize this project without problems!

It’s incredible to see you guys juggle with these things.
This is a level of understanding that I will never reach…
C++ is a new world for me!

Cheers!
And thanks also to @ScruffR and @Moors7

2 Likes

@Ric
Sorry for coming back with yet another question in this area… :pray:
But for my project, I see specific uses for both the JSON and SPRINTF techniques you introduced here.
In my opinion, they are complementary and both have advantages in particular cases.

To achieve my goal of exchanging variables between Particles, for example the ‘compactness’ of the SPRINTF string can be useful, or the ‘readability’ of the JSON strings.

In my above example with JSON, the resulting string is rather long, but it does contain the name labels:

{"Controller":"ECO system","T1":8,"T2":16,"T3":24,"T4":32,"T5":40,"T6":48,"T7":888,"Energy":12.7}

Using SPRINTF, we receive a very compact string:

"ECO system,8,16,24,32,40,48,888,12.7"

Now, for JSON, I found the documentation how to handle all kind of variables.
For SPRINTF, you showed me how to parse INTEGERS and FLOATING POINT variables:

  int T1 = atoi(strtok(NULL, ","));
  int T2 = atoi(strtok(NULL, ","));
  int T3 = atoi(strtok(NULL, ","));
  int T4 = atoi(strtok(NULL, ","));
  int T5 = atoi(strtok(NULL, ","));
  int T6 = atoi(strtok(NULL, ","));
  int T7 = atoi(strtok(NULL, ","));
  float Energy = atof(strtok(NULL, ","));

But I can’t find how to get a STRING with variable length or a BOOLEAN variable.
For strings, I tried a few commands, but got errors.

For example:

  const char* Controller = strtok((char*)data, ",");
  char Controller[5] = strtok((char*)data, ",");

Can you give me a few more tips?

A char* should work, and you don’t need to cast anything in the strtok argument,

void setup() {
    Serial.begin(9600);
    delay(5000);
    String args = "hello,15,goodbye,3.14"; // creating a String here to mimic what you get passed into a subscribe handler
    
    const char* stringArgs = args.c_str();
    char* s = strtok(strdup(stringArgs), ",");
    int second = atoi (strtok(NULL, ","));
    char* third = (strtok(NULL, ","));
    float fourth = atof (strtok(NULL, ","));
    Serial.printlnf("first= %s, second=%d, third=%s, fourth=%.2f", s, second, third, fourth);
}

this prints: first= hello, second=15, third=goodbye, fourth=3.14

1 Like

Wow @Ric , that’s magic! Thanks for your quick reply!
I would never have found it myself…


I guess this is not possible with BOOLEAN variables?
We can of course use INTEGERS “1” and “0” for these…

Greetz!

Yeah, I’m not sure about using Bools. I also found that you don’t need to use strdup like I did in the above code, you can just cast the result of c_str() to char*. I’m far from an expert on C – my programming experience is mostly Objective-C and Swift, with just enough C to be dangerous.

void setup() {
    Serial.begin(9600);
    delay(5000);
    String args = "hello,15,goodbye,3.14"; // creating a String here to mimic what you get passed into a subscribe handler
    
    char *cString = (char*)args.c_str();
    char* first = strtok(cString, ",");
    int second = atoi (strtok(NULL, ","));
    char* third = (strtok(NULL, ","));
    float fourth = atof (strtok(NULL, ","));
    Serial.printlnf("first= %s, second=%d, third=%s, fourth=%.2f", first, second, third, fourth);
}

OK @Ric that should be simpler, but there seems to be a problem with that last tip:

This one works in my example:

char* Controller = strtok(strdup(data), ",");

When I use this one, I get an error:

char* Controller = strtok(data, ",");

Error message:

sprintf_parsing_and_subscribe.cpp: In function 'void stringParser(const char*, const char*)':
sprintf_parsing_and_subscribe.cpp:19:38: error: invalid conversion from 'const char*' to 'char*' [-fpermissive]
 {
                                      ^

In file included from /usr/local/gcc-arm-embedded-gcc-arm-none-eabi-4_8-2014q2-20140609-linux-tar-bz2/arm-none-eabi/include/string.h:10:0,
                 from ../wiring/inc/spark_wiring_print.h:31,
                 from ../wiring/inc/spark_wiring_string.h:34,
                 from ../wiring/inc/spark_wiring_stream.h:30,
                 from ../wiring/inc/spark_wiring.h:39,
                 from ./inc/application.h:36,
                 from sprintf_parsing_and_subscribe.cpp:5:
/usr/local/gcc-arm-embedded-gcc-arm-none-eabi-4_8-2014q2-20140609-linux-tar-bz2/arm-none-eabi/include/string.h:43:8: error:   initializing argument 1 of 'char* strtok(char*, const char*)' [-fpermissive]
 char  *_EXFUN(strtok,(char *__restrict, const char *__restrict));
        ^

make[1]: *** [../build/target/user/platform-6sprintf_parsing_and_subscribe.o] Error 1
make: *** [user] Error 2
Error: Could not compile. Please review your code.

Sorry, I was mixing up the Particle.function callback which has a String as the argument with the subscribe “data” argument which is a const char*. I think I tried casting strdup(data) to a char* but that gave errors. So I think, you either need to use strdup, or strcpy to get from the const char* to a char*.