Storing very infrequent data while Photon offline (not cloud connected)


#8

You can use that string to send data via a Particle Publish to services like Losant and Ubidots.

Here is an example dashboard I set up in Losant that I feed with a simple Particle Publish in a 220 byte strings.

I like Losant because I can send Particle Publish data separated by simple colons : : and then break it down on the backend of Losant.


#9

It would be safer to use a c-string (char array) instead of a String object. You can use sprintf() to convert your data to a char array, and then strcat() to concatenate that new string to the end of the larger string that you’re building.


#10

“Safer” but significantly steeper learning curve and difficulty for me to figure out how to actually implement it. I go cross-eyed reading about c-strings and it seems a very clunky way to do things. It sounds much easier how you explain it, but the devil’s in the code…


#11

@charrold303, it’s not as steep as you think and if you give it a shot, the community can help you get there.


#12

@charrold303, you cannot use a String object as retained variable.
And using an array is neither more likely to break (actually the opposite) nor is preventing you from having the array retained.

Parsing a C string is definetly way more complicated than wrapping the numeric values stored in an array into a string.


#13

@charrold303 The C-String used to confuse me also but after playing around a bit I now see how simple they are to work with to format data before sending it in a Particle Publish.

Here is how I setup a C-String (Char array) in my code to send GPS Data & Battery Voltage to Losant.

First define your Char Array and change publishStateString to what ever you want it to be:

The 256 is the max byte size of the array which is the max size of a Particle Publish payload +1

Now I use the snprintf function to format the data that will be put into the char array before sending it out as the Particle Publish data payload.

snprintf(publishStateString, sizeof(publishStateString), "%.6f, %.6f:%.2f:%u:%.2f:%.2f:%.u:%.2f:",gps.location.lat(), gps.location.lng(), gps.altitude.feet(), gps.satellites.value(), (gps.hdop.value()/100.0), fuel.getSoC(), fuel.getVCell()  );
Particle.publish("GPS", publishStateString);

Pay attention to this first line of code below because it contains the commands that actually format the data:

"%.6f, %.6f:%.2f:%u:%.2f:%.2f:%.u:%.2f:"
,gps.location.lat(), gps.location.lng(), gps.altitude.feet(), gps.satellites.value(), (gps.hdop.value()/100.0), fuel.getSoC(), fuel.getVCell()  );

Now the %.6f is a snprintf formatting function for turning the data returned from gps.location.lat() into a float data point with 6 numbers after the decimial point.

So you can see we use the same %.6f for the gps.location.lng() data point also because it’s also a float data point with 6 digits after the decimal point.

For the next data point we use %.2f because gps.altitude.feet() the accuracy of the altitude only goes to 2 points past the decimal point and it’s a floating point number.

Now for the next data point we use %u because it’s not a float but a nubmer from 0-99 showing how many satellites are in view: gps.satellites.value()

And we just keep doing this as we go down the list of data variables we want to send.

Here is a reference for the different types of data formatting you can do using the snprintf function.

http://www.cplusplus.com/reference/cstdio/printf/

When I send a Particle Publish the snprintf function formats the data payload like this:

 {"data"39.123456 , -85.123456:856.45:14:1.12:85:3.88:":","ttl":60,"published_at":"2017-07-10T20:28:13.934Z","coreid":"460042001051353338363333","name":"GPS"}

Then on the back end of Losant I can just pull data out between the colons and define what that data is so it can be databased and then made available to view in custom dashboards.

That may or may not make sense and I’m a newbie but after playing around with this and looking at what actually get’s Published will help you figure out this formatting function rather quickly.

The help is here if you need it.


Official Losant + Particle Setup Tutorial
Official Losant + Particle Setup Tutorial
Photon with intermittent connection
Why can I only publish 5 events from one device
#14

from one newbie to another, thank you. This was easy to read and follow and gave me exactly what I needed to know. I will be trying it out this week and will let you know what I come up with.


#15

Glad I can help out after so many others have helped me out on here :slight_smile:


#16

Thanks for the help!


#17

First issue struck immediately - was just testing storing a string into a very short char array.

Here is the “greatly simplified” code:

retained char dev_name[12];    <- Pretty sure this is a character array of 12 characters (12 was random - could be more or less as needed)
sprintf(dev_name, sizeof(dev_name), "%.s", "Tomato_1"); <- if I read your post right, this should write "Tomato_1" as a string into the aforementioned character array

instead i get a compiler error of: “invalid conversion from ‘unsigned int’ to ‘const char*’ [-fpermissive]”

Is there nothing approximating:

retained char dev_name[12] = “Tomato_1” ?

I have seen some really (what I hope at least is) archaic string array things where I would have to literally walk down each char spot in the array and store the letter in that spot?


#18

@charrold303, you were SO close! The command you wanted is snprintf() not sprintf() - note the missing “n”. :wink:


#19

ALWAYS THE TYPOS! :stuck_out_tongue:

Thanks @peekay123


#20

If you want to copy a string literal into a char[] you’d use either

  strcpy(dev_name, "Tomato_1"); 
  // or
  strncpy(dev_name, "Tomato_1", sizeof(dev_name));
  // or less commonly since you are not actually formatting the string but just setting it
  snprintf(dev_name, sizeof(dev_name), "Tomato_1");

I should keep it shorter, then I wouldn’t be beaten by @peekay123 :wink:


#21

@charrold303, I believe you CAN do retained char dev_name[12] = "Tomato_1". However, that only initializes the c-string. I believe @ScruffR was guiding you to use snprintf() to produce your publish string and to avoid using (bad and evil) Arduino Strings.


#22

Also not sure if the %.s should be %s or not. I think the decimal is just used on Floats where the number after the decimal on the float %f. function just tells it how many zeros you want to display after the decimal.

For an Unsigned Integer, I just use %u


#23

replaced the snprintf with the strcopy for the device name (dev_name) (thanks @ScruffR and @peekay123

removed the . in “%.s” (thanks @RWB (interesting side note - compiled without errors with the “.” so maybe it ignored it either way? Just an interesting note))


#24

Subthread!

I am trying to combine the ideas of the snprintf formatting and the incrementing array to the char array, and the compiler clearly doesn’t like what I am doing. I suspect adding a string to my char array is not as simple as I think it is (or you illustrated here). Here is what I tried:

retained char offlinevals[1024];
retained int16_t globalindex = 0;
for(a = 0; a < 6; a = a + 1 ) {
int SMVolts = analogRead(as[a]);
  if (SMVolts < 4000) {
    String voltage = String(SMVolts);
    String publishdata = publishdata + "S" + a + "::" + voltage + "::";
  }
  else {
    String publishdata = publishdata + "S" + a + "::DC::";
  }
}
// store the reading in the array 
snprintf(offlinevals, sizeof(offlinevals), "%s", publishdata);  <- this threw all sorts of nasty errors about copying things that shouldn't be copied... Also I know it is silly to create a formatted string and then create a string with formatting, but I am trying... *sigh*
}

#25

It would be better to get rid of the String objects altogether. You can’t use %s for String objects, it’s only for c-strings. Instead of this,

String voltage = String(SMVolts);
String publishdata = publishdata + "S" + a + "::" + voltage + "::";

Do something like this,

char publishdata[12];
snprintf(publishdata, sizeof(publishdata), "S%d::%d::", a, SMVolts);

It’s not clear to me why you have publishdata on both sides of the equal sign, so what I wrote might not be what you want. If you’re using publishdata as the data argument in a Partilce.publish(), that’s fine; you can pass a char array or a String object to that function.


#26

the publishdata = publishdata + “other stuff” was my attempt to concatenate the existing string with more data into a new string with the sam name. Is that not allowed?


#27

If you want to use a String object as %s parameter in snprtintf() you need to provide its contents as (const char*) and my prefered way to do that is via type cast

snprintf(offlinevals, sizeof(offlinevals), "%s", (const char*)publishdata);
// but this would also work - less elegant IMHO
snprintf(offlinevals, sizeof(offlinevals), "%s", publishdata.c_str());

But this would defeat the whole purpose to use snprintf() to get rid of the heap fragmentation effect String objects have.
@Ric has shown how you’d do the string building the better way.

BTW, since you create a new instance of publishdata with each iteration of your loop, your publishdata = publishdata + ... will always ever add the new data to the newly created and hence empty string.

If you wanted to code what you verbally described, you’d do it this way

  String publishdata = "Data:"; // create a new string only once 
  for (int i=0; i<10;i++) {
    publishdata += String(i);
    publishdata += ", ";
  }

But as said, forget String and try to understand the power of snprintf(), strcpy() and strcat().
I view String objects as a crutch for non-technical coders but embedded programming calls for more skill and hence less “comfortable” but more powerful tools.