How to set up a JSON for multiple variables in a webhook integration

I have 3 variables I want to send to thingspeak to graph via a webhook integration and I am struggling with getting the JSON configured correctly. Can anyone guide me in the right direction?

If you show what you’ve done we might be able to spot the error :wink:


This is what I have so far and looking at other pieces of code I’ve tried this: Particle.publish(“thingSpeakWrite_batterytest”, “{ “1”: “” + String(battvolts) + “”, “k”: “” + key +”" }", 60, PRIVATE); and a handful of other variations of it. I’m mostly struggling with getting the code to play nice.

I’ve not yet looked at all of this, but this would be the first thing I’d change

char msg[256];
// assuming batvolts is double or float
snprintf(msg, sizeof(msg), "{\"1\":\"%.2f\",\"k\":\"%s\"}", batvolts, key);
Particle.publish("thingSpeakWrite_batterytest", msg, PRIVATE); 

I’m not sure whether webhook event names are prefix filters like Particle.subscribe() filters.
And I’m also not sure whether ThingSpeak doesn’t expect numeric values to be sent without the double quotes.
I’ll have to check.

Try this :

const char * eventName = "thingSpeakWrite_";        // This must match the name of the event you use for the WebHook
//ThingSpeak Channel Info                        
unsigned long myChannelNumber =xxxxxx;              //  From your ThingSpeak Account Info
const char * myWriteAPIKey = "xxxxxxxxxxxxxx";      //  From your ThingSpeak Account Info (API KEYS tab)

float CH1, CH2, CH3;  

And the Publish Looks like this:

Particle.publish(eventName, "{ \"1\": \"" + String(CH1,1) + "\", \"2\": \"" + String(CH2,1) + "\", \"3\": \"" + String(CH3,1) + "\", \"k\": \"" + myWriteAPIKey + "\" }", PRIVATE, NO_ACK);  

The snprintf per @ScruffR is a much better method though. I plan on changing my code once I get a better handle on C++ String Objects (if that’s even the correct term)

1 Like

yes, my battery test is a float. so i am going to insert one of these snprintf’s in for each of my events?

You can send ALL the ThinkSpeak Fields in 1 snprintf if you want.
I looked at several examples on this forum and this is what I found:

snprintf(msg, sizeof(msg), 
// ThingSpeak Field #1  , ThingSpeak Field #2   ,  ThingSpeak WriteKey  
   "{\"1\":\"%.1f\"     ,   \"2\":\"%.1f\"      ,   \"k\":\"%s\"}"      ,
// Float for Field #1   , Float for Field #2    ,  ThingSpeak WriteKey     
        field1          ,       field2          ,   myWriteAPIKey)      ;

Particle.publish(eventName, msg, PRIVATE, NO_ACK);

Flash this code and play around with it:

// Snprintf example
const char * myWriteAPIKey = "xxxxxxxxxxxxxx";      //  From your ThingSpeak Account Info (API KEYS tab)
const char * eventName = "thingSpeakWrite_";        // This must match the name of the event you use for the WebHook

float field1 = 1.1;
float field2 = 2.1;

char msg[256];

void setup() {
}

void loop() {

snprintf(msg, sizeof(msg), 
// ThingSpeak Field #1  , ThingSpeak Field #2   ,  ThingSpeak WriteKey  
   "{\"1\":\"%.1f\"     ,   \"2\":\"%.1f\"      ,   \"k\":\"%s\"}"      ,
// Float for Field #1   , Float for Field #2    ,  ThingSpeak WriteKey     
        field1          ,       field2          ,   myWriteAPIKey)      ;

Particle.publish(eventName, msg, PRIVATE, NO_ACK);

field1 = field1 + 0.1;
field2 = field2 + 0.1; 

delay(20000);  // Wait 20 seconds before sending ThingSpeak.com more data.
}

/*
Sending 4 Fields to ThingSpeak (Floats, w/ 1 decimal precision) would look like this:
snprintf(msg, sizeof(msg), 
"{\"1\":\"%.1f\"    ,   \"2\":\"%.1f\"     ,  \"3\":\"%.1f\"   ,   \"4\":\"%.1f\"  ,   \"k\":\"%s\"}"      ,
     field1         ,       field2         ,       field3      ,       field4      ,   myWriteAPIKey)      ;
*/


I see this for floats; however one of my variables is an analog read. how different is the code for this?

#define alarm_delay        2000   
unsigned long previousMillis;
unsigned int lastAlarm = 0;  
int Water = D5;   // 2 wire "Switch" will be connected to Ground and Digital Pin 6
int Switch = 0;   // Set to HIGH for startup (High Signal means the Switch is hanging in air- "no water".  We dont want a false alarm at system startup.
int soilMoistureSensor = A0; 
int pump = D3;
int moisture; 
int battvolts;
int testpin = A4;

const char * eventName = "thingSpeakWrite_";

const char * myWriteAPIKey = "xxxxxxxxxxxx";

char msg[256];

void setup() {
    
  pinMode(pump,OUTPUT);
    pinMode(soilMoistureSensor,INPUT); 
    Particle.variable("moisture", &moisture, INT);
    pinMode(Water, INPUT_PULLUP);   // Bring the pin HIGH. The circuit will be grounded when the Switch CLOSES (when water reaches the 2-Wires) and pull D6 LOW.
    Serial.begin(9600);
}

void loop() {
  
  if (Particle.connected() == false) {
    Particle.connect();}
  
  snprintf(msg, sizeof(msg), 
// ThingSpeak Field #1  , ThingSpeak Field #2   ,  ThingSpeak WriteKey  
   "{\"1\":\"%.1f\"     ,   \"2\":\"%.1f\"      ,   \"k\":\"%s\"}"      ,
// Float for Field #1   , Float for Field #2    ,  ThingSpeak WriteKey     
        battvolts           ,       moisture         ,   myWriteAPIKey)      ;

Particle.publish(eventName, msg, PRIVATE, NO_ACK);

 

  
float battvolts;
    battvolts = analogRead(testpin);
    battvolts = float (battvolts)*3.3/4096*(5.634/.994);
    Particle.publish("batterytest",String(battvolts), 60);
    delay(5000);
    
     Switch = digitalRead(Water);
    if (Switch == 1)  { // 0 is LOW, so the circuit was grounded by the 2-Wire float switch (HIGH WATER Condition)
        unsigned long now = millis();
        if ((now - lastAlarm) > alarm_delay)  {
            Particle.publish("ALARM", "LOWWATER"); // Water has reached the 2 wires.
            lastAlarm = now;
            System.sleep(SLEEP_MODE_DEEP,60);
        }
    }
    delay(100);
    
    moisture = analogRead(soilMoistureSensor);
   char str[10];
    sprintf(str, "%d", moisture);
    Particle.publish("moisture", String(moisture), 60, PRIVATE);
    
    unsigned long now = millis();
    delay(10000); 

  if (moisture < 500){
    digitalWrite(pump, HIGH);
    delay(5000);
   System.sleep(SLEEP_MODE_DEEP,60);
    }
    else if (moisture >= 500){
            System.sleep(SLEEP_MODE_DEEP,60);
       
    }
   
    }

This is what I have. I’m sorry for bugging you guys so much about this stuff.

1 Like

Since moisture is an int you'd use %d instead of %.1f (find more info about printf() patterns)
Just as you've got in your code already here

And to be consistent there is little point in creating str via sprintf() and then not use it.
Just change that publish to this :wink:

    Particle.publish("moisture", str, PRIVATE);

BTW, this is the old (deprecated) syntax

    Particle.variable("moisture", &moisture, INT);

we now use

    Particle.variable("moisture", moisture);

Also if you want to safe data (e.g. for Electrons with limited quota) just remove the extra blanks from your string pattern.

@ScruffR or anyone else really:
For Electron Data usage, is it wasteful to declare char msg[] as the max size of 256 bytes ?
In other words, would it be beneficial to add all the ASCII characters + API key and declare msg[] a few more bytes than that total, for the Particle Publish ?

I’m sorry for the silly question. I cant find the answer. Thanks in advance!

// Snprintf example
    const char * myWriteAPIKey = "xxxxxxxxxxxxxx";     
    const char * eventName = "thingSpeakWrite_";        

    float field1 = 1.111;
    float field2 = 2.222;

    char msg[256];

    void setup() {
    }

    void loop() {

    snprintf(msg, sizeof(msg), "{\"1\":\"%.1f\",\"2\":\"%.1f\",\"k\":\"%s\"}", field1, field2, myWriteAPIKey);

    Particle.publish(eventName, msg, PRIVATE, NO_ACK);

    field1 = field1 + 0.1;
    field2 = field2 + 0.1; 

    delay(20000);  // Wait 20 seconds before sending ThingSpeak.com more data.
    }

The size of your buffer is irrelevant as only the bytes preceeding (and including) the zero-terminator are actually sent.

With this code, the payload will still be only two byte long.

char buf[1024];
...
void loop() {
  ...
  strcpy(buf, "x");
  Particle.publish("test", buf, PRIVATE);
  ...
}

The only benefit of declaring the buffer smaller is less local RAM usage.

2 Likes