Report by Exception with JsonObject

Hey folks, I have a project where I read a qwiic sensor from an ESP32, build and serialize a JSON object and publish to an MQTT broker.

I have it 99% working, but with one remaining change that I can't quite wrap my head around. A best practice when working with MQTT is to 'report by exception', or only publish changes that are different from the last transmitted value. I've read the StateChangeDetection sketch and guide, however I can't quite figure out how to implement that with a JSON object and I find the ArduinoJSON documentation quite obtuse and jargon-y.

#include <Wire.h>
#include "person_sensor.h"
#include <ArduinoJson.h>
#include "EspMQTTClient.h"

EspMQTTClient client(
  "WIFI_SSID",
  "WIFI_KEY",
  "BROKER_IP",  // MQTT Broker server ip
  "CLIENT_ID",           // Client name that uniquely identify your device
  1883               // The MQTT port, default to 1883. this line can be omitted
);

const int32_t SAMPLE_DELAY_MS = 1000;
StaticJsonDocument<256> doc;


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

  client.enableLastWillMessage("esp32/state", "offline", true);
}

void onConnectionEstablished(){
client.publish("esp32/state", "online", true);
}

void loop() {
  client.loop();
  person_sensor_results_t results = {};
  JsonObject object = doc.to<JsonObject>();
  char buffer[256];

  if (!person_sensor_read(&results)) {
    Serial.println("No person sensor results found on the i2c bus");
    delay(SAMPLE_DELAY_MS);
    return;
  }
 
  Serial.println("********************");

  object["faces"] = results.num_faces;

  for (int i = 0; i < results.num_faces; ++i) {
    const person_sensor_face_t* face = &results.faces[i];
    JsonObject FACE = object.createNestedObject("Face" + String(i));
    FACE["confidence"] = face->box_confidence;
    FACE["left"] = face->box_left;
    FACE["top"] = face->box_top;
    FACE["right"] = face->box_right;
    FACE["bottom"] = face->box_bottom;
    FACE["facing"] = face->is_facing;
  }
  serializeJsonPretty(doc, buffer);
  client.publish("esp32/faces", buffer, true);
  serializeJsonPretty(object, Serial);
  Serial.println("");
  Serial.println("********************");
  delay(SAMPLE_DELAY_MS);
}

I know I need to store a new variable at the end of my loop containing my JSON object, call it previousPayload and then compare previousPayload with the outgoing payload, but I can't figure out how to do that.

Many examples seem to declare the currentState and previousState variables outside of the loop, but when I did that, I ended up with stickiness, where old nested objects weren't correctly being cleared out.

I'd love some help from you brilliant people.

rather than comparing the JSON, compare the data in the person_sensor_results_t structure

add a global variable person_sensor_results_t oldResult ={};

and when you acquire the new data you compare the old and new structures

  if (!person_sensor_read(&results)) {
    Serial.println("No person sensor results found on the i2c bus");
    delay(SAMPLE_DELAY_MS);
    return;
  }

  if (! personDataAreTheSame(oldResult, results)) {
    // they are different, memorise the new one and send the new data
   oldResult = results;

    Serial.println("********************");

    object["faces"] = results.num_faces;

    for (int i = 0; i < results.num_faces; ++i) {
      const person_sensor_face_t* face = &results.faces[i];
      JsonObject FACE = object.createNestedObject("Face" + String(i));
      FACE["confidence"] = face->box_confidence;
      FACE["left"] = face->box_left;
      FACE["top"] = face->box_top;
      FACE["right"] = face->box_right;
      FACE["bottom"] = face->box_bottom;
      FACE["facing"] = face->is_facing;
    }
    serializeJsonPretty(doc, buffer);
    client.publish("esp32/faces", buffer, true);
    serializeJsonPretty(object, Serial);
    Serial.println("");
    Serial.println("********************");
  } 

you'll have to write the personDataAreTheSame() function that compares whatever is inside a person_sensor_results_t and return true if they are the same and false if they are not

Also oldResult = results; assumes that the person_sensor_results_t type knows how to copy itself

I'll give that a go and see where it gets me.

Thanks for the insight, I'm not sure where I got stuck on comparing the JSON, when I have a results structure way before I build the JSON. This is also presumably more efficient, as I am not building and then throwing away the JSON, if they end up being the same.

yes in terms of computation and you don't need lots of extra memory to store the JSON representation of the previous message

Thanks JML, this got me the rest of the way.

I'll look at optimizing it, but I created a function(not sure if that's what they are called in arduino/c++) to evaluate equivalence for the payload, and then used your suggestion to compare current vs. past results.

Thank you for the help.

Glad if that helped

Yes they are called functions in C++

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.