Connecting Particle with Thingsboard demo

Hello everyone

I am a new to the Particle community. I am working on a project to monitor environmental conditions in a greenhouse. I started with an Air-quality Argon kit. I managed to assumable the kit together following the example given in here: Air Quality Monitoring Kit | Quickstart | Particle . I am using Particle web IDE. I can see the collected data using the Console tool. I would like some guidance on the following point:

-. I need to attach this system with the Thingsboard demo for data visualisation in the dashboard. I have seen some people already ask similar questions, but I can not modify the output of my sensor value to send them to the Thingsboard. In total, the system I working with now measures 7 different parameters (temp, humidity, pressure, airQuality, lowpulseoccupancy, ratio, concentration) as I can see them in the Console tool.

Here is my code:

// This #include statement was automatically added by the Particle IDE.
#include <JsonParserGeneratorRK.h>

// This #include statement was automatically added by the Particle IDE.
#include <Adafruit_BME280.h>

// This #include statement was automatically added by the Particle IDE.
#include <Grove_Air_quality_Sensor.h>

// This #include statement was automatically added by the Particle IDE.
#include <OLED_Display_128X64.h>

/*
 * Project env-sensor-kit
 * Description: Basic Tutorial project for the Particle Envrionmental Sensor Kit
 * Author: Brandon Satrom <brandon@particle.io>
 * Date: 09/16/2019
 *
 * Required libraries:
 * - OLED_Display_128X64=1.0.0
 * - Grove_Air_quality_Sensor=1.0.1
 * - Adafruit_BME280=1.1.5
 * - JsonParserGeneratorRK=0.0.7
 */
#include "Particle.h"

SYSTEM_THREAD(ENABLED);

#include "Air_Quality_Sensor.h"
#include "Adafruit_BME280.h"
#include "SeeedOLED.h"
#include "JsonParserGeneratorRK.h"

#define AQS_PIN A2
#define DUST_SENSOR_PIN D4
#define SENSOR_READING_INTERVAL 30000
#define jsonOut

AirQualitySensor aqSensor(AQS_PIN);
Adafruit_BME280 bme;

unsigned long lastInterval;
unsigned long lowpulseoccupancy = 0;
unsigned long last_lpo = 0;
unsigned long duration;

float ratio = 0;
float concentration = 0;

int getBMEValues(int &temp, int &humidity, int &pressure);
void getDustSensorReadings();
String getAirQuality();
void createEventPayload(int temp, int humidity, int pressure, String airQuality);
void updateDisplay(int temp, int humidity, int pressure, String airQuality);

void setup()
{
  Serial.begin(9600);
  delay(50);

  // Configure the dust sensor pin as an input
  pinMode(DUST_SENSOR_PIN, INPUT);

  if (aqSensor.init())
  {
    Serial.println("Air Quality Sensor ready.");
  }
  else
  {
    Serial.println("Air Quality Sensor ERROR!");
  }

  Wire.begin();
  SeeedOled.init();

  SeeedOled.clearDisplay();
  SeeedOled.setNormalDisplay();
  SeeedOled.setPageMode();

  SeeedOled.setTextXY(2, 0);
  SeeedOled.putString("Particle");
  SeeedOled.setTextXY(3, 0);
  SeeedOled.putString("Air Quality");
  SeeedOled.setTextXY(4, 0);
  SeeedOled.putString("Monitor");

  if (bme.begin())
  {
    Serial.println("BME280 Sensor ready.");
  }
  else
  {
    Serial.println("BME280 Sensor ERROR!");
  }

  lastInterval = millis();
}

void loop()
{
  int temp, pressure, humidity;

  duration = pulseIn(DUST_SENSOR_PIN, LOW);
  lowpulseoccupancy = lowpulseoccupancy + duration;

  if ((millis() - lastInterval) > SENSOR_READING_INTERVAL)
  {
    String quality = getAirQuality();
    Serial.printlnf("Air Quality: %s", quality.c_str());

    getBMEValues(temp, pressure, humidity);
    Serial.printlnf("Temp: %d", temp);
    Serial.printlnf("Pressure: %d", pressure);
    Serial.printlnf("Humidity: %d", humidity);

    getDustSensorReadings();

    updateDisplay(temp, humidity, pressure, quality);

    createEventPayload(temp, humidity, pressure, quality);

    lowpulseoccupancy = 0;
    lastInterval = millis();
  }
}

String getAirQuality()
{
  int quality = aqSensor.slope();
  String qual = "None";

  if (quality == AirQualitySensor::FORCE_SIGNAL)
  {
    qual = "Danger";
  }
  else if (quality == AirQualitySensor::HIGH_POLLUTION)
  {
    qual = "High Pollution";
  }
  else if (quality == AirQualitySensor::LOW_POLLUTION)
  {
    qual = "Low Pollution";
  }
  else if (quality == AirQualitySensor::FRESH_AIR)
  {
    qual = "Fresh Air";
  }

  return qual;
}

int getBMEValues(int &temp, int &pressure, int &humidity)
{
  temp = (int)bme.readTemperature();
  pressure = (int)(bme.readPressure() / 100.0F);
  humidity = (int)bme.readHumidity();

  return 1;

  

}

void getDustSensorReadings()
{
  // This particular dust sensor returns 0s often, so let's filter them out by making sure we only
  // capture and use non-zero LPO values for our calculations once we get a good reading.
  if (lowpulseoccupancy == 0)
  {
    lowpulseoccupancy = last_lpo;
  }
  else
  {
    last_lpo = lowpulseoccupancy;
  }

  ratio = lowpulseoccupancy / (SENSOR_READING_INTERVAL * 10.0);                   // Integer percentage 0=>100
  concentration = 1.1 * pow(ratio, 3) - 3.8 * pow(ratio, 2) + 520 * ratio + 0.62; // using spec sheet curve

  Serial.printlnf("LPO: %d", lowpulseoccupancy);
  Serial.printlnf("Ratio: %f%%", ratio);
  Serial.printlnf("Concentration: %f pcs/L", concentration);
}

void createEventPayload(int temp, int humidity, int pressure, String airQuality)
{
  JsonWriterStatic<256> jw;
  {
    JsonWriterAutoObject obj(&jw);

    jw.insertKeyValue("temp", temp);
    jw.insertKeyValue("humidity", humidity);
    jw.insertKeyValue("pressure", pressure);
    jw.insertKeyValue("air-quality", airQuality);

    if (lowpulseoccupancy > 0)
    {
      jw.insertKeyValue("dust-lpo", lowpulseoccupancy);
      jw.insertKeyValue("dust-ratio", ratio);
      jw.insertKeyValue("dust-concentration", concentration);
    }
  }


  Particle.publish("env-vals", jw.getBuffer(), PRIVATE);
    
}

void updateDisplay(int temp, int humidity, int pressure, String airQuality)
{
  SeeedOled.clearDisplay();

  SeeedOled.setTextXY(0, 3);
  SeeedOled.putString(airQuality);

  SeeedOled.setTextXY(2, 0);
  SeeedOled.putString("Temp: ");
  SeeedOled.putNumber(temp);
  SeeedOled.putString("C");

  SeeedOled.setTextXY(3, 0);
  SeeedOled.putString("Humidity: ");
  SeeedOled.putNumber(humidity);
  SeeedOled.putString("%");

  SeeedOled.setTextXY(4, 0);
  SeeedOled.putString("Press: ");
  SeeedOled.putNumber(pressure);
  SeeedOled.putString(" hPa");

  if (concentration > 1)
  {
    SeeedOled.setTextXY(5, 0);
    SeeedOled.putString("Dust: ");
    SeeedOled.putNumber(concentration); // Will cast our float to an int to make it more compact
    SeeedOled.putString(" pcs/L");
  }
}

After I create a new device in the Thingsboard demo, I copied the Access Token and I used the Integration tool on the IDE Console to connect the Jason file. Before that, I tried to use Particle.publish() to Configure my Particle device to publish an event with a numerical floating point value to the Particle Cloud using this code:

// Publish as events

    sprintf(jsonOut,  "{'temp': %0.2f; 'humidity': %0.2f; 'pressure': %0.2f; 'airQuality': 
    %0.2f; 'lowpulseoccupancy': %0.2f; 'ratio': %0.2f; 'concentration': %0.2f}",
      temp, humidity, pressure, airQuality, lowpulseoccupancy, ratio, concentration);
    Particle.publish("env-vals", jsonOut, PRIVATE);

But, it giving me an error that I can not figure out.

I appreciate any help as I am stuck as my background does not allow me to do too many things on that, however, I try my best to learn more in this interesting area.

Could you show us the respective error message? Maybe we can help figure it out :wink:

One thing I notice is that you are using single quotes (') in your JSON string but they need to be double quotes ("). In order to embed those in a string you need to escape them like snprintf(jsonOut, sizeof(jsonOut), "{ \"temp\": %.2f, ... }", temp);

You are also using semicolons (;) to separate the JSON fields, but you'd rather need to use comma (,).

You could always print out your resulting JSON string and run that through an online JSON validator like this one
http://rickkas7.github.io/jsonparser/

Thank you for your reply, here is the code after I modified based on your comment:

  // Publish as events
    sprintf(jsonOut, sizeof(jsonOut),  "{\"temp\": %0.2f, \"humidity\": %0.2f, \"pressure\": %0.2f, \"airQuality\": %0.2f, \"lowpulseoccupancy\": %0.2f}", 
      temp, humidity, pressure, airQuality, lowpulseoccupancy, ratio, concentration);
    Particle.publish("env-vals", jsonOut, PRIVATE);
    
  Particle.publish("env-vals", jw.getBuffer(), PRIVATE);

and here is the error I have:

 expected primary-expression before ',' token

expected primary-expression before ')' token

 expected primary-expression before ',' token

The idea is just to get the numerical values of the measured parameters such as Temp, humidity, pressure and etc to be send to Thingsboard demo dashboard.

Best regards

If you look closely I used snprintf() not sprintf() - reason being that the latter runs the risk of overshooting the provided buffer and hence corrupting unrelated data.

Also when posting error messages it would be good to know to which exact code line they belong - the error message always also provides the row and column numbers where the error was found and most the time also an excerpt of the “offending” statement.

I’m also not sure why you have two publishes for the same event name.

BTW, I find this way of writing such long string construction more readable and maintainable

    snprintf(jsonOut, sizeof(jsonOut)
            , "{ \"temp\": %.2f"
              ", \"humidity\": %.2f"
              ", \"pressure\": %.2f"
              ", \"airQuality\": %.2f"
              ", \"lowpulseoccupancy\": %.2f"
              "}"
            , temp
            , humidity
            , pressure
            , airQuality
            , lowpulseoccupancy
            , ratio                          // <-- not used in format string
            , concentration                  // <-- not used in format string
            );
    Particle.publish("env-vals", jsonOut);   // since the PUBLIC option has gone PRIVATE is not needed anymore 

This way it’s easy to spot that you are providing more variables than you’re actually using and adding/removing fields won’t need any alteration to the existing fields - just add or remove the respective code lines (no need to check trailing commas or such).

2 Likes