Electron: Collect data every minute, publish it every 15 minutes - with sleep. Better way to do this?

Hi all,

As subject states, I’m looking at collecting values from sensors(6) once per minute, and then publish that data every 15 minutes. Since I’m new to this, I constructed some simple code using arrays, and delay statements - see below.
What I want to know is, should this way - although functioning - be replaced by other methods?

  char fullpublish[400];
  char tempd[6];
  int x;
  int y;
  float storage[16][7];
  fullpublish[0] = '\0';  
  //begin loop to collect 15 sample sets, 1 set per minute
  //just using set values for soon to be 3-digit sensor values
  for ( int x = 1; x < 16; x++ ) {
    storage[x][1] = 342;
    storage[x][2] = 543;
    storage[x][3] = 323;
    storage[x][4] = 764;
    storage[x][5] = 245;
    storage[x][6] = 642;
    delay(60000); //about one minute
  }



//Send fullpublish out to publish
//Gather array and place into single string 'fullpublish'
  for ( x = 1; x < 16; x++ ) {
    for ( y = 1; y < 7; y++ ) {
    snprintf(tempd, sizeof(tempd), "%.0f", storage[x][y]);
    strcat ( fullpublish, tempd );    
       if (y != 6) 
          {strcat ( fullpublish, "," );}
        else
          {strcat ( fullpublish, "!" );}
    }
  }

cout<<fullpublish;

Thanks,
Bob

delay() is not really a good tool for that as it just wastes all that time doing noting.

You definetly want to adopt a non-blocking programming paradigm (e.g. FSM).

You could shorten your string building code like this

    snprintf(tempd, sizeof(tempd), "%.0f%s", storage[x][y], (y==6) ? "!" : ",");
    strncat(fullpublish, tempd, sizeof(fullpublish) - strlen(fullpublish) - 1); // better to check the boundaries

or slightly less efficient but in a single line (renaming the variable for brevity)

    snprintf(msg, sizeof(msg), "%s%.0f%s", msg, storage[x][y], (y==6) ? "!" : ",");

BTW, I doubt cout<< would work on the Electron at all as there is no stdout defined IIRC.
And is there any reason you are not using the zero indexed fields in your arrays? Once you start getting used to zero-based indexing you will see its advantages especially when replacing conditional coding with arithmetic :wink:

2 Likes

ScruffR,

Thanks much! I just started reading this after a search to see what FSM is State Machine Design and trying to get my head around it. I do know that using delay as such is not optimal, but with my current skillset, it was a start. I’ll take a dive into FSM this evening and weekend and see what I can learn.

Oops, the ‘cout<<’ was left over from testing the code in an editor and meant to take out - as I try to achieve headway with such stuff I find it easier to try it in a live editor instead of constantly sending firmware over, but not all the commands (for electron) are available. Is there any better way that you test code snippets such as this that may be better or more well rounded?

Also thanks for the pointer on not using zero indexes on the arrays, I didn’t really think that through or pay attention, for some reason my brain started at 1…

Thanks again,
Kolbi

1 Like

ScruffR,

I made the suggested edits, all works well. I’ll read up on FSM.

#include <iostream>
#include <cstring>  //For the string functions
using namespace std;

int main()
{

  char fullpublish[400];
  char tempd[5];
  int x;
  int y;
  float storage[16][7];
  fullpublish[0] = '\0';  
  //begin loop to collect 15 samples, 1 per minute
  for ( int x = 0; x < 15; x++ ) {
    storage[x][0] = 442;
    storage[x][1] = 443;
    storage[x][2] = 423;
    storage[x][3] = 464;
    storage[x][4] = 445;
    storage[x][5] = 442;
    //delay(60000); //about one minute
  }

//Send fullpublish out to publish
//Gather array and place into single string 'fullpublish'
  for ( x = 0; x < 15; x++ ) {
    for ( y = 0; y < 6; y++ ) {
    snprintf(tempd, sizeof(tempd), "%.0f%s", storage[x][y], (y==5) ? "!" : ",");
    strncat(fullpublish, tempd, sizeof(fullpublish) - strlen(fullpublish) - 1); // better to check the boundaries
    tempd == "";
    }
  }

cout<<fullpublish;
cout<<"\n";

std::string str (fullpublish);
std::cout << "The size of str is " << str.length() << " bytes.\n";

}
1 Like

Arg… the //delay(60000) shouldn’t be commented out, just as cout and std: are only for testing on my editor.

You don't want/need to do that for multiple reasons :wink:

  • the double quote is only the boolean equality operator which will just give you false in 100% of cases
  • but it's no use since you don't do anything with the result
  • however, if you wanted to "reset" the string via the assignment operator = is not the tool to use since you cannot change the address of an array but that would be what this operator tries to do.
  • to "clear" a string you'd do other things (e.g. tempd[0] = 0;, strcpy(tempd, "");memset(tempd, 0, sizeof(tempd));)
  • but since you overwrite the string via snprintf() that "clearing" is superfluous

ScruffR, thanks!

Yeah, sorry about that - I knew that, just read over that subject about two hours ago, and then brain farted and typed it right in there.

BTW, do you have any suggestions like codebender sort of ide, that can run code for particle and like devices? I’m using coderunner on mac but it doesn’t recognize millis and some other commands.

Thanks,
Kolbi

1 Like

I think I read that @rickkas7 had some tool with some function stubs to accept Particle code, maybe he can chime in once he's back.

ScruffR,

I think I made some learning progress, I got rid of the delay statements for a timed event trigger. I believe this model below is kinda what you were talking about in regards to non-blocking and FSM?

#include <iostream>
using namespace std;
int main()
{

char fullpublish[400];
char tempd[5];
int x; x = 0;
int y; y = 0;
int z;
int t2;
float storage[16][7];
fullpublish[0] = ‘\0’;
int samplei = 3; // SAMPLE INTERVAL, HOW MANY SECONDS TO WAIT BETWEEN EACH
int publishf = 5; // WAIT UNTIL x NUMBER OF SAMPLES ARE TAKEN TO BURST PUBLISH

// THIS Z LOOP IS TO SIMULATE ‘VOID LOOP()’ ON ELECTRON
for( z = 0; z <900000000; z++) {
// THIS Z LOOP IS TO SIMULATE LOOP ON ELECTRON

// CHECK IF IT HAS BEEN LONG ENOUGH ELASPED TIME TO TAKE A SAMPLE
if (time(0) > t2) {
storage[x][0] = 442;
storage[x][1] = 443;
storage[x][2] = 423;
storage[x][3] = 464;
storage[x][4] = 445;
storage[x][5] = 442;
x++;
t2 = time(0) + samplei;
cout<< "Saved readings in Storage " << x << “.\n”;
} // DONE SAVING SAMPLE

// CHECK TO SEE IF ENOUGH SAMPLES WERE TAKEN TO PUBLISH
if (x == publishf) {
for ( x = 0; x < publishf; x++ ) {
for ( y = 0; y < 6; y++ ) {
snprintf(tempd, sizeof(tempd), “%.0f%s”, storage[x][y], (y==5) ? “!” : “,”);
strncat(fullpublish, tempd, sizeof(fullpublish) - strlen(fullpublish) - 1); // better to check the boundaries
}} // BUILT FULLPUBLISH STRING

// DO SOMETHING WITH FULLPUBLISH SAMPLE
// ONCE ON ELECTRON, THIS WILL BE PARTICLE.PUBLISH EVENT
x = 0;
cout<< “Published readings.\n”;
cout<<fullpublish;
cout<<"\n";
std::string str (fullpublish);
std::cout << “The size of str is " << str.length() << " bytes.\n”;
fullpublish[0] = 0; // CLEAR THE FULLPUBLISH STRING

}
// DONE WITH PUBLISHING SAMPLES

} // THIS Z LOOP IS TO SIMULATE LOOP ON ELECTRON
}

Thanks much for the direction,
Kolbi

I added event checking, to change report times based on if sensor values are above a set change percentage. Feels like it got big and a bit convoluted but all functions work on C++ ide.
If anyone has any suggestions or critiques on this, I will gladly take on board.

#include <iostream>
using namespace std;
int main()
{
// BEGIN USER CONFIGURABLE PARAMETERS
	int samplei = 1;		// SAMPLE INTERVAL, HOW MANY SECONDS TO WAIT BETWEEN EACH
	int publishf = 15;		// WAIT UNTIL x NUMBER OF SAMPLES ARE GATHERED TO BURST PUBLISH
	int sca = 5;			// SENSOR CHANGE ALERT as PERCENTAGE DIFFENCE FROM CURRENT TO LAST THREE VALUES AVERAGED
	int scasamplei = 1;     // ALERT SAMPLE INTERVAL, HOW MANY SECONDS TO WAIT BETWEEN EACH
	int scac = 2;			// WHILE IN SENSOR ALERT - HOW MANY COMPLETE PUBLISH CYCLES (AS DEFINED ABOVE) TO LOOP THROUGH
// END OF USER CONFIGURABLE PARAMETERS

// DECLARE ITEMS USED AND SET INITIAL PARAMETERS
	int csamplei = samplei; // SET CURRENT SAMPLE INTERVAL TO NORMAL SAMPLE INTERVAL AS ABOVE
	char fullpublish[500]; fullpublish[0] = '\0'; // CONTAINS THE STRING TO BE PUBLISHED
	char tempd[5];			// TEMPORY HOLDING STRING USED FOR FULLPUBLISH EVENT
	int v = 6;				// THE NUMBER IF VALUES TO COLLECT PER SAMPLE
	int w = 0;				// THE NUMBER OF SAMPLES TO GATHER
	int x = 0;				// THE NUMBER OF SAMPLES COUNTER
	int xx = 0;				// THE NUMBER OF SAMPLES COUNTER in ALERT STATE
	int y = 0;				// THE SAMPLE PER COUNT GATHERED
	int z = 0;				// FOR TO LOOP TO SIMULATE 'VOID LOOP()'
	int pc = 0;				// COUNTER FOF ALL PUBLISHES PERFORMED
	int sc = 0;				// COUNTER FOF ALL SAMPLES PERFORMED
	int ec = 0;				// SENSOR SAMPLE EVENT CHECK
	int ecd = 0;			// SENSOR SAMPLE EVENT CHECK DEPTH
	int ecdiff = 0;			// SENSOR EVENT CHECK DIFFERENCE
	int scact;				// CYCLE MARK BASED ON 'SC' TO CONTINUE ALERT REPORTING
	int t2 = 0;				// EQUALS TIME(0) + SAMPLEI, USED TO DETERMINE WHEN TO TAKE SAMPLE
	int cias = 0;			// CHANGE IN ALERT STATE FLAG
	float sv[6][6]; 		// DIMENSION THE SENSOR VALUE ARRAY
	float storage[(publishf+1)][7];	// DIMENSION THE STOARAGE CONTAINER ARRAY	
	int a = 362; int b = 400; float c = 3; float d = 5; float e = 5; int f = 5;
	scac = ((scac * publishf) + 1);
// END OF DECLARATIONS AND INITIAL PARAMETERS
	for( z = 0; z <900000000; z++) { // THIS Z LOOP IS TO SIMULATE 'VOID LOOP()' ON ELECTRON !!




// BEGIN COLLECT SENSOR VALUES AND PLACE SV[ARRAY]
sv[0][0] = 440;
sv[1][0] = 441;
sv[2][0] = 422;
sv[3][0] = 463;
sv[4][0] = 444;
sv[5][0] = 445;
// END OF COLLECT SENSOR VALUES AND PLACE SV[ARRAY]



///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\///
//     GATHER, EVENT CHECK, AND PUBLISH COLLECTED SENSOR VALUES BASED ON csamplei AND publishf   //
///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\///

// BEGIN SENSOR VALUE EVENT CHECK 
	if (sc > ec) { 
		ec = sc;
		if ( ecd == 3 ) { ecd = 1; } else { ecd++; }
		for ( w = 0; w < v; w++ ) { sv[w][ecd] = sv[w][0]; sv[w][4] = ((sv[w][1] + sv[w][2] + sv[w][3]) / 3);
		// if (sc == 20) sv[w][4] = sv[w][4] + 100; // ADD 100 TO SENSOR VALUE TO SIMULATE EVENT
		if (sc > 3){
		a = sv[w][0]; b = sv[w][4]; c = 0; d = 0; e = 0; f = 0;	c = std::abs(a-b); d = (a + b) /2;
		if ( c > d) { e = d / c; } else { e = c / d; }
		f = e * 100; sv[w][5] = f; cout << "Percent diff: " << sv[w][5] << ". \n";
		if (sv[w][5] > sca) 
		{ cout << "\n Out of range change in sensor value detected on sensor " << w << ".\n"; 
		// DO SOMETHING HERE LIKE ALTER REPORTING TIMES OR SEND ALERT
		cias = 1; // SIGNIFIES FRESH CHANGE INTO ALERT STATE SO PUBLISHER WILL PUBLISH STORAGE AND THEN START ALERT REPORTING
		csamplei = scasamplei; scact = sc + scac; }
		} 		}		}
		// CHECK IF WE ARE IN AN ALERT REPORT CONDITION AND SEE IF WE SHOULD COME OUT OF IT
		if ( sc > scact) csamplei = samplei;
// END SENSOR VALUR EVENT CHECK

// START OF SAMPLING TIME CHECK
	if (time(0) > t2) {
		for ( w = 0; w < v; w++ ) { storage[x][w] = sv[w][0]; }
		x++;
		t2 = time(0) + csamplei;
		cout<< "Saved readings in Storage " << x << ".\n";
		sc++;
		}		
// END OF SAMPLING TIME CHECK

// ALERT STATE PUBLISH FLUSH
	if (cias == 1 && scact > sc) {    
		for ( xx = 0; xx < x; xx++ ) {
		for ( y = 0; y < v; y++ ) {
		snprintf(tempd, sizeof(tempd), "%.0f%s", storage[x][y], (y==5) ? "^" : ",");
		strncat(fullpublish, tempd, sizeof(fullpublish) - strlen(fullpublish) - 1); // better to check the boundaries
		}} // BUILT FULLPUBLISH STRING
		// DO EVENT WITH FULLPUBLISH SAMPLE - ONCE ON ELECTRON, THIS WILL BE PARTICLE.PUBLISH EVENT							
		x = 0; xx = 0; cias = 0;
		cout<< "Published readings.\n";
		cout<<fullpublish;
		cout<<"\n";
		string str (fullpublish);
		cout << "The size of str is " << str.length() << " bytes.\n";
		fullpublish[0] = 0; // CLEAR THE FULLPUBLISH STRING
		pc++;
		}
// END OF ALERT STATE PUBLISH FLUSH
		
// START OF SAMPLES TAKEN CHECK, IF MET THEN PUBLISH
	if (x == publishf) {    
		for ( x = 0; x < publishf; x++ ) {
		for ( y = 0; y < v; y++ ) {
		if ( scact > sc ) {
		snprintf(tempd, sizeof(tempd), "%.0f%s", storage[x][y], (y==5) ? "!" : ",");
		} else {
		snprintf(tempd, sizeof(tempd), "%.0f%s", storage[x][y], (y==5) ? "^" : ",");
		}
		strncat(fullpublish, tempd, sizeof(fullpublish) - strlen(fullpublish) - 1); // better to check the boundaries
		}} // BUILT FULLPUBLISH STRING
		// DO EVENT WITH FULLPUBLISH SAMPLE - ONCE ON ELECTRON, THIS WILL BE PARTICLE.PUBLISH EVENT							
		x = 0;
		cout<< "Published readings.\n";
		cout<<fullpublish;
		cout<<"\n";
		string str (fullpublish);
		cout << "The size of str is " << str.length() << " bytes.\n";
		fullpublish[0] = 0; // CLEAR THE FULLPUBLISH STRING
		pc++;
		}
// END OF SAMPLES TAKEN CHECK, IF MET THEN PUBLISH

///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\///
//        END OF GATHER AND PUBLISH COLLECTED SENSOR VALUES BASED ON csamplei & publishf         //
///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\///

} // THIS COMPLETES THE Z LOOP THAT SIMULATES 'VOID LOOP()' ON ELECTRON !!
cout << "\n Number of publilshes performed: " << pc;
cout << "\n Number of samples taken: " << sc;
; 
}

Thanks much,
Kolbi

While we could look at your code or run it in a similar environment, I'd think it more useful to actually write the code as closely as you could to directly transfer it to the target device.
For one it helps you to avoid re-writing effort and us so we don't have to mentally map your code onto the coding paradigm you'll eventually have to use anyway.

As mentioned above, you can do that yourself too

e.g.

int main() {
  setup();
  while (1) {
    loop();
  }
}

void setup() {
  // setup code here
}

void loop() {
  // loop code here
}

class SerialClass {
  SerialClass() { };
  // add any default print functions you'll need for your code
  ...
} Serial;

enum CloudScope {
  PRIVATE,
  PUBLIC
};

class ParticleClass {
  bool publish(const char* name, const char* data, CloudScope scope) {
    // implement as needed for testing
  }
  // other methods as needed
}
1 Like

ScruffR,

Yes, that is very true and thanks much for the assistance. While I was away and not able to get to my electron, it was the only way I could write and test code in a live fashion. Now that I was able to get it to my electron, a few headaches were had but it is working as expected. Particle.publish is not yet used, ‘cout’ was replaced with Serial.print, so that I am able to see what’s going on without killing my cell data.

#include "application.h"

using namespace std;

// BEGIN USER CONFIGURABLE PARAMETERS
	int samplei = 2;		// SAMPLE INTERVAL, HOW MANY SECONDS TO WAIT BETWEEN EACH
	int publishf = 15;		// WAIT UNTIL x NUMBER OF SAMPLES ARE GATHERED TO BURST PUBLISH
	int sca = 5;			// SENSOR CHANGE ALERT as PERCENTAGE DIFFENCE FROM CURRENT TO LAST THREE VALUES AVERAGED
	int scasamplei = 1;     // ALERT SAMPLE INTERVAL, HOW MANY SECONDS TO WAIT BETWEEN EACH
	int scacr = 2;			// WHILE IN SENSOR ALERT - HOW MANY COMPLETE PUBLISH CYCLES (AS DEFINED ABOVE) TO LOOP THROUGH
    int scac = 31;
    //	       ^ = ( publishf * 2 ) + 1
	float storage[16][7];	// DIMENSION THE STOARAGE CONTAINER ARRAY
	//            ^    publishf + 1
// END OF USER CONFIGURABLE PARAMETERS

// DECLARE ITEMS USED AND SET INITIAL PARAMETERS
	int csamplei = samplei; // SET CURRENT SAMPLE INTERVAL TO NORMAL SAMPLE INTERVAL AS ABOVE
	char fullpublish[500]; //fullpublish[0] = '\0'; // CONTAINS THE STRING TO BE PUBLISHED
	char tempd[5];			// TEMPORY HOLDING STRING USED FOR FULLPUBLISH EVENT
	int v = 6;				// THE NUMBER IF VALUES TO COLLECT PER SAMPLE
	int w = 0;				// THE NUMBER OF SAMPLES TO GATHER
	int x = 0;				// THE NUMBER OF SAMPLES COUNTER
	int xx = 0;				// THE NUMBER OF SAMPLES COUNTER in ALERT STATE
	int y = 0;				// THE SAMPLE PER COUNT GATHERED
	int z = 0;				// FOR TO LOOP TO SIMULATE 'VOID LOOP()'
	int pc = 0;				// COUNTER FOF ALL PUBLISHES PERFORMED
	int sc = 0;				// COUNTER FOF ALL SAMPLES PERFORMED
	int ec = 0;				// SENSOR SAMPLE EVENT CHECK
	int ecd = 0;			// SENSOR SAMPLE EVENT CHECK DEPTH
	int ecdiff = 0;			// SENSOR EVENT CHECK DIFFERENCE
	int scact;				// CYCLE MARK BASED ON 'SC' TO CONTINUE ALERT REPORTING
	int t2 = 0;				// EQUALS TIME(0) + SAMPLEI, USED TO DETERMINE WHEN TO TAKE SAMPLE
	int cias = 0;			// CHANGE IN ALERT STATE FLAG
	float sv[6][6]; 		// DIMENSION THE SENSOR VALUE ARRAY
	int a = 362; int b = 400; float c = 3; float d = 5; float e = 5; int f = 5;

// END OF DECLARATIONS AND INITIAL PARAMETERS

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

void loop() {

// BEGIN COLLECT SENSOR VALUES AND PLACE SV[ARRAY]
sv[0][0] = 440;
sv[1][0] = 441;
sv[2][0] = 422;
sv[3][0] = 463;
sv[4][0] = 444;
sv[5][0] = 445;
// END OF COLLECT SENSOR VALUES AND PLACE SV[ARRAY]



///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\///
//     GATHER, EVENT CHECK, AND PUBLISH COLLECTED SENSOR VALUES BASED ON csamplei AND publishf   //
///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\///

// BEGIN SENSOR VALUE EVENT CHECK 
	if (sc > ec) { 
		ec = sc;
		if ( ecd == 3 ) { ecd = 1; } else { ecd++; }
		for ( w = 0; w < v; w++ ) { sv[w][ecd] = sv[w][0]; sv[w][4] = ((sv[w][1] + sv[w][2] + sv[w][3]) / 3);
		if (sc == 20) sv[w][4] = sv[w][4] + 100; // ADD 100 TO SENSOR VALUE TO SIMULATE EVENT
		if (sc > 3){
		a = sv[w][0]; b = sv[w][4]; c = 0; d = 0; e = 0; f = 0;	c = std::abs(a-b); d = (a + b) /2;
		if ( c > d) { e = d / c; } else { e = c / d; }
		f = e * 100; sv[w][5] = f; Serial.print("Sensor "); Serial.print(w); Serial.print(" percent diff from last three readings averaged: "); Serial.println(sv[w][5]);
		if (sv[w][5] > sca) 
		{   
	    Serial.print("Out of range change in sensor value detected on sensor: "); Serial.println(w);
		// DO SOMETHING HERE LIKE ALTER REPORTING TIMES OR SEND ALERT
		cias = 1; // SIGNIFIES FRESH CHANGE INTO ALERT STATE SO PUBLISHER WILL PUBLISH STORAGE AND THEN START ALERT REPORTING
		csamplei = scasamplei; scact = sc + scac; }
		} 		}		}
		// CHECK IF WE ARE IN AN ALERT REPORT CONDITION AND SEE IF WE SHOULD COME OUT OF IT
		if ( sc > scact) csamplei = samplei;
// END SENSOR VALUR EVENT CHECK

// START OF SAMPLING TIME CHECK
	if (Time.now() > t2) {
		for ( w = 0; w < v; w++ ) { storage[x][w] = sv[w][0]; }
		x++;
		t2 = Time.now() + csamplei;
		if (scact > sc) { Serial.print("EVENT STATE: Saved readings in Storage: "); Serial.println(x); 
		Serial.print("Current sampling count (sc) = "); Serial.print(sc); Serial.print(" / Alert sample count (scac) = "); Serial.print(scac); Serial.print(" / Current alert sample count (scact) = "); Serial.println(scact);
		} else { Serial.print("Saved readings in Storage: "); Serial.println(x); }
		sc++;
		}	
// END OF SAMPLING TIME CHECK

// ALERT STATE PUBLISH FLUSH
	if (cias == 1 && scact > sc) {    
		for ( xx = 0; xx < x; xx++ ) {
		for ( y = 0; y < v; y++ ) {
		snprintf(tempd, sizeof(tempd), "%.0f%s", storage[x][y], (y==5) ? "^" : ",");
		strncat(fullpublish, tempd, sizeof(fullpublish) - strlen(fullpublish) - 1); // better to check the boundaries
		}} // BUILT FULLPUBLISH STRING
		// DO EVENT WITH FULLPUBLISH SAMPLE - ONCE ON ELECTRON, THIS WILL BE PARTICLE.PUBLISH EVENT							
		x = 0; xx = 0; cias = 0;
		Serial.println("Published readings. ");
		Serial.println(fullpublish);
		string str (fullpublish);
		Serial.print("The size of str is "); Serial.print(str.length()); Serial.println(" bytes.");
		fullpublish[0] = 0; // CLEAR THE FULLPUBLISH STRING
		pc++;
		}
// END OF ALERT STATE PUBLISH FLUSH
		
// START OF SAMPLES TAKEN CHECK, IF MET THEN PUBLISH
	if (x == publishf) {    
		for ( x = 0; x < publishf; x++ ) {
		for ( y = 0; y < v; y++ ) {
		if ( scact > sc ) {
		snprintf(tempd, sizeof(tempd), "%.0f%s", storage[x][y], (y==5) ? "!" : ",");
		} else {
		snprintf(tempd, sizeof(tempd), "%.0f%s", storage[x][y], (y==5) ? "^" : ",");
		}
		strncat(fullpublish, tempd, sizeof(fullpublish) - strlen(fullpublish) - 1); // better to check the boundaries
		}} // BUILT FULLPUBLISH STRING
		// DO EVENT WITH FULLPUBLISH SAMPLE - ONCE ON ELECTRON, THIS WILL BE PARTICLE.PUBLISH EVENT							
		x = 0;
		Serial.println("Published readings. ");
		Serial.println(fullpublish);
		string str (fullpublish);
		Serial.print("The size of str is "); Serial.print(str.length()); Serial.println(" bytes.");
		fullpublish[0] = 0; // CLEAR THE FULLPUBLISH STRING
		pc++;
		}
// END OF SAMPLES TAKEN CHECK, IF MET THEN PUBLISH

///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\///
//        END OF GATHER AND PUBLISH COLLECTED SENSOR VALUES BASED ON csamplei & publishf         //
///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\///

}



1 Like

If anyone would rather see in the Web IDE, here is link:
https://go.particle.io/shared_apps/5ca92482417ead000acd35af

Just a tip: Readability of spaghetti lines like this

  Serial.print("Current sampling count (sc) = "); Serial.print(sc); Serial.print(" / Alert sample count (scac) = "); Serial.print(scac); Serial.print(" / Current alert sample count (scact) = "); Serial.println(scact);

is very poor and I usually avoid reading code like this - let alone trying to interpret it. Even more so with “exotic” indentation :wink: I’ve been mocked by colleagues for often spending considerable time reformatting their code before addressing their issues they asked for help with :blush:
But I’m very much a visual type and usually get the feel for the code and its structure by looking at it and “subconciously” scanning for meaningful blocks - this is impossible with code like this.

BTW, you can use Serial.printlnf() just like snprintf() to format a multi-part string. That also helps making your spaghetti lines shorter :wink:

Haha, yeah. :grin:

Thanks much for your feedback, I do very much appreciate it. I tried to keep all Serial.print statements that comprise a full sentence report on one line, I didn’t think about how it would be difficult for someone else to decrypt my odd style. This has been my first full week with c++ coding, doing lots of reading and research - I’ll eventually get it pretty.

BTW, now I want to eat spaghetti! :smile:

Thanks much,
Kolbi

1 Like

Thanks! This made it a bit more readable.
Serial.printf("Current sampling count (sc) = %d / Alert sample count (scac) = %d / Current alert sample count (scact) = %d \n", sc, scac, scact);

Haha, I tried to clean it up some and use indents… All works as is, at the 20th sample of sensor-3 a value of 100 is added to sensor[4] value (this holds the past three sensor values averaged) to trip an alert event. This part is located in the ‘BEGIN SENSOR VALUE EVENT CHECK’ with the following code:

if (sc == 20)
      sv[3][4] = sv[3][4] + 100; // ADD 100 TO SENSOR-3 VALUE TO SIMULATE EVENT

Sensor alert values are set per sensor, 1 through 6 since each sensor could and most probably will have a different trip alert then the others. See this on line 6:

int sa[7] = {10, 11, 12, 13, 14, 15}; // SET ARRAY FOR SENSOR ALERT

Full code:

#include "application.h"

using namespace std;

// BEGIN USER CONFIGURABLE PARAMETERS
    int sa[7] = {10, 11, 12, 13, 14, 15}; // SET ARRAY FOR SENSOR ALERT
    int samplei = 2;        // SAMPLE INTERVAL, HOW MANY SECONDS TO WAIT BETWEEN EACH
    int publishf = 15;      // WAIT UNTIL x NUMBER OF SAMPLES ARE GATHERED TO BURST PUBLISH
    int scasamplei = 1;     // ALERT SAMPLE INTERVAL, HOW MANY SECONDS TO WAIT BETWEEN EACH
    int scacr = 2;          // WHILE IN SENSOR ALERT - HOW MANY COMPLETE PUBLISH CYCLES (AS DEFINED ABOVE) TO LOOP THROUGH
    int scac = 31;          //*HOW MANY SENSOR SAMPLES TO TAKE WHILE IN EVENT/ALERT MODE
    //	       ^ = ( publishf * 2 ) + 1
    float storage[16][7];   //*DIMENSION THE STORAGE CONTAINER ARRAY
    //            ^    publishf + 1
// END OF USER CONFIGURABLE PARAMETERS


// DECLARE ITEMS USED AND SET INITIAL PARAMETERS
    int csamplei = samplei; // SET CURRENT SAMPLE INTERVAL TO NORMAL SAMPLE INTERVAL AS ABOVE
    char fullpublish[500];  //fullpublish[0] = '\0'; // CONTAINS THE STRING TO BE PUBLISHED
    char tempd[5];          // TEMPORY HOLDING STRING USED FOR FULLPUBLISH EVENT
    int satemp = 0;         // USED AS A TEMP CONTAINER IN SENSOR ALERT CHECKING
    int v = 6;              // THE NUMBER IF VALUES TO COLLECT PER SAMPLE
    int w = 0;              // THE NUMBER OF SAMPLES TO GATHER
    int x = 0;              // THE NUMBER OF SAMPLES COUNTER
    int xx = 0;             // THE NUMBER OF SAMPLES COUNTER in ALERT STATE
    int y = 0;              // THE SAMPLE PER COUNT GATHERED
    int z = 0;              // FOR TO LOOP TO SIMULATE 'VOID LOOP()'
    int pc = 0;             // COUNTER FOF ALL PUBLISHES PERFORMED
    int sc = 0;             // COUNTER FOF ALL SAMPLES PERFORMED
    int ec = 0;             // SENSOR SAMPLE EVENT CHECK
    int ecd = 0;            // SENSOR SAMPLE EVENT CHECK DEPTH
    int ecdiff = 0;         // SENSOR EVENT CHECK DIFFERENCE
    int scact;              // CYCLE MARK BASED ON 'SC' TO CONTINUE ALERT REPORTING
    int t2 = 0;             // EQUALS TIME(0) + SAMPLEI, USED TO DETERMINE WHEN TO TAKE SAMPLE
    int cias = 0;           // CHANGE IN ALERT STATE FLAG
    float sv[6][6];         // DIMENSION THE SENSOR VALUE ARRAY
// END OF DECLARATIONS AND INITIAL PARAMETERS



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


void loop()
{





// BEGIN TO COLLECT SENSOR VALUES AND PLACE IN SV[ARRAY]
    sv[0][0] = 440;
    sv[1][0] = 441;
    sv[2][0] = 422;
    sv[3][0] = 463;
    sv[4][0] = 444;
    sv[5][0] = 445;
// END OF COLLECTING SENSOR VALUES THAT WERE PLACED IN SV[ARRAY]





///\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\///
//     GATHER, EVENT CHECK, AND PUBLISH COLLECTED SENSOR VALUES BASED ON csamplei AND publishf    //
///\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\///



///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//
// BEGIN SENSOR VALUE EVENT CHECK //
///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//
    if (sc > ec) {
        ec = sc;
        if (ecd == 3) {
            ecd = 1;
        }
        else {
            ecd++;
        }
        for (w = 0; w < v; w++) {
            sv[w][ecd] = sv[w][0];
            sv[w][4] = ((sv[w][1] + sv[w][2] + sv[w][3]) / 3);
            if (sc == 20)
                sv[3][4] = sv[3][4] + 100; // ADD 100 TO SENSOR-3 VALUE TO SIMULATE EVENT
            if (sc > 3) {
                satemp = sv[w][0] > sv[w][4] ? sv[w][0] - sv[w][4] : sv[w][4] - sv[w][0];
                if (satemp > sa[w]) {
                    Serial.printf("Out of SA change on sensor %d, ", w);
                    Serial.printlnf("difference of %d, threshold set at %d.", satemp, sa[w]);
                    // DO SOMETHING HERE LIKE ALTER REPORTING TIMES OR SEND ALERT
                    cias = 1; // SIGNIFIES FRESH CHANGE INTO ALERT STATE SO PUBLISHER WILL PUBLISH STORAGE AND THEN START ALERT REPORTING
                    csamplei = scasamplei;
                    scact = sc + scac;
                }
            }
        }
    }
    // CHECK IF WE ARE IN AN ALERT REPORT CONDITION AND SEE IF WE SHOULD COME OUT OF IT
    if (sc > scact)
        csamplei = samplei;
///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//
// END SENSOR VALUR EVENT CHECK //
///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//



///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//
// START OF SAMPLING TIME CHECK //
///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//
    if (Time.now() > t2) {
        for (w = 0; w < v; w++) {
            storage[x][w] = sv[w][0];
        }
        x++;
        t2 = Time.now() + csamplei;
        if (scact > sc) {
            Serial.printf("EVENT STATE: Saved readings in Storage: %d \n", x); // sc, scac, scact
            Serial.printf("Current sampling count (sc) = %d / Alert sample count (scac) = %d / Current alert sample count (scact) = %d \n", sc, scac, scact);
        }
        else {
            Serial.printf("Saved readings in Storage: %d \n", x);
        }
        sc++;
    }
///\/\/\/\/\/\/\/\/\/\/\/\/\/\//    
// END OF SAMPLING TIME CHECK //
///\/\/\/\/\/\/\/\/\/\/\/\/\/\//



//\/\/\/\/\/\/\/\/\/\/\/\/\/\//
// ALERT STATE PUBLISH FLUSH //
//\/\/\/\/\/\/\/\/\/\/\/\/\/\//
    if (cias == 1 && scact > sc) {
        for (xx = 0; xx < x; xx++) {
            for (y = 0; y < v; y++) {
                snprintf(tempd, sizeof(tempd), "%.0f%s", storage[x][y], (y == 5) ? "^" : ",");
                strncat(fullpublish, tempd, sizeof(fullpublish) - strlen(fullpublish) - 1); // better to check the boundaries
            }
        } // BUILT FULLPUBLISH STRING
        // DO EVENT WITH FULLPUBLISH SAMPLE - PARTICLE.PUBLISH EVENT
        x = 0;
        xx = 0;
        cias = 0;
        Serial.println("Published readings. ");
        Serial.println(fullpublish);
        string str(fullpublish);
        Serial.printf("The size of published string is %d bytes. \n", str.length());
        fullpublish[0] = 0; // CLEAR THE FULLPUBLISH STRING
        pc++;
    }
///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//  
// END OF ALERT STATE PUBLISH FLUSH //
///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//



//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//
// START OF SAMPLES TAKEN CHECK, IF MET THEN PUBLISH //
//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//
    if (x == publishf) {
        for (x = 0; x < publishf; x++) {
            for (y = 0; y < v; y++) {
                if (scact > sc) {
                    snprintf(tempd, sizeof(tempd), "%.0f%s", storage[x][y], (y == 5) ? "!" : ",");
                }
                else {
                    snprintf(tempd, sizeof(tempd), "%.0f%s", storage[x][y], (y == 5) ? "^" : ",");
                }
                strncat(fullpublish, tempd, sizeof(fullpublish) - strlen(fullpublish) - 1); // better to check the boundaries
            }
        } // BUILT FULLPUBLISH STRING
        // DO EVENT WITH FULLPUBLISH SAMPLE - ONCE ON ELECTRON, THIS WILL BE PARTICLE.PUBLISH EVENT
        x = 0;
        Serial.println("Published readings. ");
        Serial.println(fullpublish);
        string str(fullpublish);
        Serial.printf("The size of published string is %d bytes. \n", str.length());
        fullpublish[0] = 0; // CLEAR THE FULLPUBLISH STRING
        pc++;
    }
//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//
// END OF SAMPLES TAKEN CHECK, IF MET THEN PUBLISH //
//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//



///\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\///
//        END OF GATHER AND PUBLISH COLLECTED SENSOR VALUES BASED ON csamplei & publishf          //
///\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\///

}

Code is also shared via Particle Web IDE HERE.

Thanks much,
Kolbi

1 Like

This can also be written like this

  sv[3][4] += 100;

Only initialises 6 of 7 values.

You have several arrays with an extent related to the number of sensors, for code maintenance having a constant (e.g. const int numSENSORS = 6;) and using that in the array definitions would allow for easier addition of an extra sensor, especially when then also using loops with that dynamic bound for accessing these arrays.

You could use satemp = abs(sv[w][0] - sv[w][4]); here.

What is that extra variable str for? strlen(fullpublish) would give you the string length too.

One for the geeks :wink:

Could also be written as

        snprintf(tempd, sizeof(tempd), "%.0f%s", storage[x][y], (y == 5) ? (scact > sc ? "!" : "^") : ",");

Or for the hyper-geeks

const char delimiters[2][numSENSORS+1] = 
{ ",,,,,^"
, ",,,,,!"
};
...
  snprintf(tempd, sizeof(tempd), "%.0f%c", storage[x][y], delimiters[scact > sc][y]);

This would also allow for an individual delimiter between each of the values.

1 Like

Wow! THANKS!!!

I didn’t know about ‘const int’ - I tried to do without ‘const’ but when I used that to define an array I got errors. That freed up a couple of lines!

All the rest of the suggestions were great too! Really, thanks much for your time!

The only problem I ran into was with the abs command - that line yielded the following:
satemp = abs(sv[w][0] - sv[w][4]); // GIVES: call of overloaded ‘abs(float)’ is ambiguous

Can see changes HERE.

Again, thanks much ScruffR!
Kolbi

Sorry, I missed that sv[][] is of type float. So you'll need to use fabs() instead - and to use that you also need #include <math.h>

1 Like