DHT22 data + Webhook data issue parsing

I have spent many hours now trying to come to a solution that I feel should be simple however I am not familiar with C++ programming. The goal I have is the following:

  • Publish event that triggers webhook to get weather data from api.
  • Receive weather data from api and merge it with current DHT22 temperature/humidity reading.
  • Send a formatted JSON string with the combined weather data via webhook to MongoDB.

I have the webhook call to the weather api recieving data. I have the webhook call to MongoDB sending data in the correct manner. The issue I currently have is getting the values from the weather api webhook response into the combined result needed to send to MongoDB.

The "outside_humidity" value doesn't get set in the final bit:
{"outside_temperature": 45.54, "outside_humidity": , "inside_temperature": -4.00, "inside_humidity": -4.00}

Also, both "inside_" values return -4.00 for a while after I update the firmware but then eventually register what I would expect from the DHT22 sensor. Not sure why.

Here is my code. DISCLAIMER: I used ChatGPT to get some of the key:value parsing since I am unfamiliar with C++.

#include <PietteTech_DHT.h>

#define DHTTYPE  DHT22
#define DHTPIN   D4
// sensor sending interval (seconds)
#define SEND_INTERVAL 600

PietteTech_DHT DHT(DHTPIN, DHTTYPE);

float humidity;
float temperature;

char humidityString[10];
char temperatureString[10];
char temperatureBuffer[10];
char humidityBuffer[10];

int failed = 0;

// last time since we sent sensor readings
int lastUpdate = 0;

void parseKeyValue(const char *inputString, char delimiter, std::function<void(const char *, const char *)> callback) {
    char token[40];  // Adjust the size based on your needs
    char key[20];    // Adjust the size based on your needs
    char value[20];  // Adjust the size based on your needs

    int tokenIndex = 0;

    for (int i = 0; i < strlen(inputString); i++) {
        char currentChar = inputString[i];

        if (currentChar == delimiter || currentChar == '\0') {
            // Null-terminate the token
            token[tokenIndex] = '\0';

            // Extract key and value from the token
            int colonIndex = -1;
            for (int j = 0; j < tokenIndex; j++) {
                if (token[j] == ':') {
                    colonIndex = j;
                    break;
                }
            }

            if (colonIndex != -1) {
                // Null-terminate the key and value
                strncpy(key, token, colonIndex);
                key[colonIndex] = '\0';

                strncpy(value, token + colonIndex + 1, tokenIndex - colonIndex - 1);
                value[tokenIndex - colonIndex - 1] = '\0';

                // Invoke the callback function with key and value
                callback(key, value);
            }

            // Reset the token index for the next token
            tokenIndex = 0;
        } else {
            // Build the token
            token[tokenIndex++] = currentChar;
        }
    }
}

void keyValueCallback(const char *key, const char *value) {
    // Store the parsed values in variables
    if (strcmp(key, "temp") == 0) {
        strncpy(temperatureBuffer, value, sizeof(temperatureBuffer));
    } else if (strcmp(key, "hum") == 0) {
        strncpy(humidityBuffer, value, sizeof(humidityBuffer));
    }
}

void setup()
{
    Serial.begin(9600);
  
    DHT.begin();
    
    // configure Particle variables - float isn't accepted, so we have to use string versions
    Particle.variable("temperature", &temperatureString[0], STRING);
    Particle.variable("humidity", &humidityString[0], STRING);
    
    // Subscribe to the integration response event
    Particle.subscribe("hook-response/currentWeather", myHandler, MY_DEVICES);
    
    // run the first measurement
    loop();
}

void myHandler(const char *event, const char *data) 
{
  
    const char *inputString = data;
    char delimiter = ',';

    // Parse the input string
    parseKeyValue(data, delimiter, keyValueCallback);

    // Create a JSON-formatted string
    char jsonString[150];  // Adjust the size based on your needs
    snprintf(jsonString, sizeof(jsonString), "{\"outside_temperature\": %s, \"outside_humidity\": %s, \"inside_temperature\": %s, \"inside_humidity\": %s}", temperatureBuffer, humidityBuffer, temperatureString, humidityString);

    // Publish the JSON-formatted string
    Particle.publish("weatherMongoDB", jsonString, PRIVATE);
}

void loop()
{
    int now = Time.now();
    
    // only run every SEND_INTERVAL seconds
    if (now - lastUpdate < SEND_INTERVAL) {
        return;
    }

    lastUpdate = now;
    
    int result = DHT.acquireAndWait(2000);
  
    temperature = DHT.getFahrenheit();
    humidity = DHT.getHumidity();
  
    // // convert floats to strings for Particle variables
    sprintf(temperatureString, "%.2f", temperature);
    sprintf(humidityString, "%.2f", humidity);
  
    // Particle.publish("temperature", temperatureString, PRIVATE);
    // Particle.publish("humidity", humidityString, PRIVATE);
    
    // weather API webhook
    // Get some data
    String data = String(10);
    Particle.publish("currentWeather", data, PRIVATE);
  

    // Wait 60 seconds
    delay(60000);
}

Hi @jasonhodges

I am not able to assist with your API calls, but I might be able to shed some light on the DHT subject. To be honest, these sensors (DHT family) are not the fastest bunch of sensors out there. If you poll them to fast, the readings will be all over the place or just return error (-4.00) you are receiving.

Many humidity sensors have this delay as they have small heating elements that needs to get up to temperature in order to measure the humidity. You should allow for this in you code. As you mentioned, after a short while the reasings are what you would expect.

Regards
Friedl.

Instead of sending the data from the weather API back to the device for processing then triggering the MongoDB storage from the device again, I would take a look at the new Logicfeature. It's cloud-based Javascript that runs in the Particle cloud that can be triggered from an event (or on a schedule) and can both trigger a webhook or parse the response from a webhook. This might be an easier way to handle your situation.

2 Likes

Thanks @rickkas7
I have just researched all the info on the new Logic feature you mentioned but don't understand how I could utilize it as a solution. I see that you can make a Logic function that responds to an event or a webhook. My end goal is to take the data I receive from the DHT22 sensor and combine it with the data I receive from the webhook api call and send it as a single entry to MongoDB. The purpose of having the two data sets combined, ideally at similar time of day, and sent to storage is so I can write a frontend application visually graphing the comparison of inside temperature/humidity in a specific room relative to the outside weather. Unless I am missing something in the documentation for Logic functions, I don't understand how it would allow me to combine to the two sets of data as one to then send via my MongoDB webhook.

Hello @friedl_1977
Thanks for your reply. I have the API calls working, that is good. I just don't know enough about the firmware code to know why I am not getting the value for "outside_humidity" when I do in fact see that I am supplying it from the weather API webhook to the subscribe.

This is the response I generate from the weather API webhook. I have used a response template to get only these two values:


Inside my code I have Particle.subscribe to process the response inside the myHandler() function. The goal is to take the values on the device (from DHT22) and the parsed values from the subscribe and publish them via the 'weatherMongoDB' event but the "outside_humidity" value doesn't show up:

I was just looking at the code again and analyzing what could be different for the "outside_humidity" value. Seeing that the code has the comma "," set as the delimiter used in the parsing functionality and the humidity value is the second value picked up in the subscribe temp:**, hum:**, I tried adding a comma after in my response template and now I have the humidity value!


1 Like

UPDATE There is still something going wrong with how the final JSON string is being formed. I tried to adjust the interval for the initial publish event and now I am getting combined values on outside_temperature

{"outside_temperature": 43.74-4.00, "outside_humidity": 85, "inside_temperature": -4.00, "inside_humidity": -4.00}