DHTesp-push DHT22-to promethues-pushgateway

esp32-anduino.ino code

#include <WiFi.h>
#include <WiFiMulti.h>
WiFiMulti WiFiMulti;

#include <DHTesp.h>

DHTesp dht;

// change values to your own setup
const char SSID[] = "xxxxxxxx"; // your WiFi name
const char PASSWORD[] = "xxxxxxxxxxxxxx"; // your WiFi password
const char * HOST = "xxxxxxxxxxxxx"; // pushGW: ip or dns
const uint16_t PORT = 9091; // pushGW: TCP port

bool DEBUG = false;

// prometheus metadata
String JOB = String("dh22");
String INSTANCE = String("dc");

enum DATA_TYPE {CSV, JSON, PROMETHEUS};

// enable serial outputter: CSV, JSON, PROMETHEUS
DATA_TYPE SERIAL_OUTPUTTER = CSV;
// enable data push to HTTP endpoint CSV, JSON, PROMETHEUS
DATA_TYPE PUSH_DATA = PROMETHEUS;

//prometheus_metric
#include "prometheus_metric.h"
Metric temperature(MetricType::gauge, "temperature_celsius", "DHT22 exported temperature value in celsius", 1);
Metric humidity(MetricType::gauge, "humidity", "DHT22 exported relative humidity value in percent", 1);
Metric absHumidity(MetricType::gauge, "absolute_humidity", "DHT22 computed absolute humidity value", 1);
Metric heatIndex(MetricType::gauge, "heat_index", "DHT22 computed heat index value", 1);
Metric dewPoint(MetricType::gauge, "dew_point", "DHT22 computed dew point value", 1);
Metric tooHot(MetricType::gauge, "too_hot", "DHT22 comfort value too hot", 1);
Metric tooCold(MetricType::gauge, "too_cold", "DHT22 comfort value too cold", 1);
Metric tooHumid(MetricType::gauge, "too_humid", "DHT22 comfort value too humid", 1);
Metric tooDry(MetricType::gauge, "too_dry", "DHT22 comfort value distance too dry", 1);
Metric distanceTooHot(MetricType::gauge, "distance_too_hot", "DHT22 comfort value distance to tooHot", 1);
Metric distanceTooCold(MetricType::gauge, "distance_too_cold", "DHT22 comfort value distance to tooCold", 1);
Metric distanceTooHumid(MetricType::gauge, "distance_too_humid", "DHT22 comfort value distance to tooHumid", 1);
Metric distanceTooDry(MetricType::gauge, "distance_too_dry", "DHT22 comfort value distance to tooDry", 1, std::unordered_map<std::string, std::string>() );
Metric comfortStatus(MetricType::gauge, "comfort_status", "DHT22 comfort status value", 1, {
      {"state", ""},
});
Metric perceptionStatus(MetricType::gauge, "perception_status", "DHT22 perception status value", 1, {
      {"state", ""},
});


// value object
struct MetricData {
  float temperature;
  float humidity; // relative

  // https://en.wikipedia.org/wiki/Humidity#Absolute_humidity
  float absHumidity;
  // https://en.wikipedia.org/wiki/Heat_index
  float heatIndex;
  // https://en.wikipedia.org/wiki/Dew_point
  float dewPoint;

  // comfort status data
  float distanceTooHot;
  float distanceTooCold;
  float distanceTooHumid;
  float distanceTooDry;
  bool too Hot;
  bool tooCold;
  bool tooHumid;
  bool tooDry;
  String comfortStatus;
  String perceptionStatus;
};

// Metrics implements interaction with dht22 to create MetricData and exports to JSON/CSV/Prometheus
class Metrics {

private:
  MetricData md;

public:

  MetricData getMetricsData() { return md; }

  void createMetricsData() {
    TempAndHumidity tah = dht.getTempAndHumidity();
    md.temperature = tah.temperature;
    md.humidity = tah.humidity;
    md.absHumidity = dht.computeAbsoluteHumidity(tah.temperature, tah.humidity, false); // in "g/m3"
    md.heatIndex = dht.computeHeatIndex(tah.temperature, tah.humidity, false);
    md.dewPoint = dht.computeDewPoint(tah.temperature, tah.humidity, false);

    ComfortState cf;
    // if we want to support setting custom comfort ratio
    //float comfortRatio = dht.getComfortRatio( & amp;cf, tah.temperature, tah.humidity, false);
    md.comfortStatus = ComfortStatus(cf);

    ComfortProfile cp = dht.getComfortProfile();
    md.tooHot = cp.isTooHot(tah.temperature, tah.humidity);
    md.tooCold = cp.isTooCold(tah.temperature, tah.humidity);
    md.tooHumid = cp.isTooHumid(tah.temperature, tah.humidity);
    md.tooDry = cp.isTooDry(tah.temperature, tah.humidity);
    md.distanceTooHot = cp.distanceTooHot(tah.temperature, tah.humidity);
    md.distanceTooCold = cp.distanceTooCold(tah.temperature, tah.humidity);
    md.distanceTooHumid = cp.distanceTooHumid(tah.temperature, tah.humidity);
    md.distanceTooDry = cp.distanceTooDry(tah.temperature, tah.humidity);

    PerceptionState ps = (PerceptionState) dht.computePerception(tah.temperature, tah.humidity, false);
    md.perceptionStatus = PerceptionStatusString(ps);
  }

  String ComfortStatus(ComfortState cf) {
    String comfortStatus;
    switch(cf) {
    case Comfort_OK:
      comfortStatus = "Comfort_OK";
      break;
    case Comfort_TooHot:
      comfortStatus = "Comfort_TooHot";
      break;
    case Comfort_TooCold:
      comfortStatus = "Comfort_TooCold";
      break;
    case Comfort_TooDry:
      comfortStatus = "Comfort_TooDry";
      break;
    case Comfort_TooHumid:
      comfortStatus = "Comfort_TooHumid";
      break;
    case Comfort_HotAndHumid:
      comfortStatus = "Comfort_HotAndHumid";
      break;
    case Comfort_HotAndDry:
      comfortStatus = "Comfort_HotAndDry";
      break;
    case Comfort_ColdAndHumid:
      comfortStatus = "Comfort_ColdAndHumid";
      break;
    case Comfort_ColdAndDry:
      comfortStatus = "Comfort_ColdAndDry";
      break;
    default:
      comfortStatus = "Unknown:";
      break;
    };

    return comfortStatus;
  }

  String PerceptionStatusString(PerceptionState ps) {
    switch(ps) {
    case Perception_Dry: return "Perception_DRY";
    case Perception_VeryComfy: return "Perception_VeryComfy";
    case Perception_Comfy: return "Perception_Comfy";
    case Perception_Ok: return "Perception_Ok";
    case Perception_UnComfy: return "Perception_UnComfy";
    case Perception_QuiteUnComfy: return "Perception_QuiteUnComfy";
    case Perception_VeryUnComfy: return "Perception_VeryUnComfy";
    case Perception_SevereUncomfy: return "Perception_SevereUncomfy";
    default: return "Perception_Unknown";
    }
  }

  String CSV() {
    return "temperature;humidity;absHumidity;heatIndex;dewPoint;ComfortStatus;tooHot;tooCold;tooHumid;tooDry;distTooHot;distTooCold;distTooHumid;distTooDry;perceptionState\\
"
       + String(md.temperature) + ";"
       + String(md.humidity) + ";"
       + String(md.absHumidity) + ";"
       + String(md. heatIndex) + ";"
       + String(md.dewPoint) + ";"
       + md.comfortStatus + ";"
       + String(md.tooHot) + ";"
       + String(md.tooCold) + ";"
       + String(md.tooHumid) + ";"
       + String(md.tooDry) + ";"
       + String(md.distanceTooHot) + ";"
       + String(md.distanceTooCold) + ";"
       + String(md.distanceTooHumid) + ";"
       + String(md.distanceTooDry) + ";"
       + md.perceptionStatus;
  }

  String JSON() {
    return "{\\
 "temperature": " + String(md.temperature) +
      ",\\
 "humidity": " + String(md.humidity) +
      ",\\
 "absHumidity": " + String(md.absHumidity) +
      ",\\
 "heatIndex": " + String(md.heatIndex) +
      ",\\
 "dewPoint": " + String(md.dewPoint) +
      ",\\
 "ComfortStatus": "" + md.comfortStatus + """ +
      ",\\
 "tooHot": " + String(md.tooHot) +
      ",\\
 "tooCold": " + String(md.tooCold) +
      ",\\
 "tooHumid": " + String(md.tooHumid) +
      ",\\
 "tooDry": " + String(md.tooDry) +
      ",\\
 "distTooHot": " + String(md.distanceTooHot) +
      ",\\
 "distTooCold": " + String(md.distanceTooCold) +
      ",\\
 "distTooHumid": " + String(md.distanceTooHumid) +
      ",\\
 "distTooDry": " + String(md.distanceTooDry) +
      ",\\
 "perceptionState": "" + md.perceptionStatus +
      ""\\
}";
  }


  String Prometheus() {
    temperature.setValue(md.temperature);
    humidity. setValue(md. humidity);
    absHumidity.setValue(md.absHumidity);
    heatIndex.setValue(md.heatIndex);
    dewPoint.setValue(md.dewPoint);
    tooHot. setValue(md. tooHot);
    tooCold.setValue(md.tooCold);
    tooDry. setValue(md. tooDry);
    tooHumid. setValue(md. tooHumid);
    distanceTooHot.setValue(md.distanceTooHot);
    distanceTooCold.setValue(md.distanceTooCold);
    distanceTooHumid.setValue(md.distanceTooHumid);
    distanceTooDry.setValue(md.distanceTooDry);

    comfortStatus. setValue(1, {
        {"state", md.comfortStatus.c_str()},
    });
    perceptionStatus. setValue(1, {
        {"state", md.perceptionStatus.c_str()},
    });

    return temperature. getString()
       + humidity. getString()
       + absHumidity. getString()
       + heatIndex. getString()
       + dewPoint. getString()
       + tooHot. getString()
       + tooCold. getString()
       + tooHumid. getString()
       + tooDry. getString()
       + distanceTooHot. getString()
       + distanceTooCold. getString()
       + distanceTooHumid. getString()
       + distanceTooDry. getString()
       + comfortStatus. getString()
       + perceptionStatus. getString();
  }
};

void setup() {
  Serial.begin(115200);

  WiFiMulti.addAP(SSID, PASSWORD);

  if (DEBUG) {
    Serial. println();
    Serial. println();
    Serial.print("Waiting for WiFi... ");
  }

  while(WiFiMulti. run() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }

  if (DEBUG) {
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
  }

  // DH22
  // wake up DH22 sensor
  dht.setup(17, DHTesp::AM2302);
  if (DEBUG) {
    Serial.print("dht status: ");
    Serial.println(dht.getStatusString());
  }
  delay(500);
}

void loop()
{
  if (DEBUG) {
    Serial.print("Connecting to ");
    Serial. println(HOST);
  }

  // Use WiFiClient class to create TCP connections
  WiFiClient client;

  if (!client. connect(HOST, PORT)) {
    Serial.println("Connection failed.");
    Serial.println("Waiting 5 seconds before retrying...");
    delay(5000);
    return;
  }

  Metrics m = Metrics();
  m. createMetricsData();
  MetricData md = m.getMetricsData();

  String jsonData = m. JSON();
  String csvData = m. CSV();
  String prometheusData = m. Prometheus();

  switch (SERIAL_OUTPUTTER) {
  case JSON:
    Serial. println(jsonData);
    break;
  case CSV:
    Serial. println(csvData);
    break;
  case PROMETHEUS:
    Serial.println(prometheusData);
    break;
  }

  switch (PUSH_DATA) {
  case JSON:
    client.print("POST /temperature HTTP/1.1\r\\
");
    client.print("Content-Type: application/json\r\\
");
    client.print("Content-Length: ");
    client.print(jsonData.length());
    client.print("Host: ");
    client.print(HOST);
    client.print("\r\\
\r\\
");
    client.print(jsonData);
    break;
  case CSV:
    client.print("POST /temperature HTTP/1.1\r\\
");
    client.print("Content-Type: text/plain\r\\
");
    client.print("Content-Length: ");
    client.print(csvData.length());
    client.print("Host: ");
    client.print(HOST);
    client.print("\r\\
\r\\
");
    client.print(csvData);
    break;
  case PROMETHEUS:
    client.print("POST /metrics/job/");
    client. print(JOB);
    client.print("/instance/");
    client. print(INSTANCE);
    client.print("HTTP/1.1\r\\
");
    client.print("Content-Type: application/x-www-form-urlencoded\r\\
");
    client.print("Content-Length: ");
    client.print(prometheusData.length());
    client.print("\r\\
");
    client.print("Host: ");
    client.print(HOST);
    client.print("\r\\
\r\\
");
    client.print(prometheusData);
    break;
  }

  int maxloops = 0;
  // wait for the server's reply to become available
  while (!client.available() & amp; & amp; maxloops < 1000) {
    maxloops++;
    delay(1); //delay 1 msec
  }
  if (client. available() > 0) {
    while (client. available() > 0) {
      //read back one line from the server
      String line = client. readStringUntil('\r\\
');
      if (DEBUG) {
        Serial.print("> ");
        Serial. println(line);
      }
    }
  } else {
    Serial.println("HTTP Response timeout from server");
  }

  if (DEBUG) { Serial. println("Closing connection."); }
  client. stop();

  if (DEBUG) { Serial.println("Waiting 5 seconds before restarting..."); }
  delay(5000);
}

DHTesp.h

/********************************************** *********************
  DHT Temperature & amp; Humidity Sensor library for Arduino & amp; ESP32.

  Features:
  - Support for DHT11 and DHT22/AM2302/RHT03
  -Auto detect sensor model
  - Very low memory footprint
  -Very small code

  https://github.com/beegee-tokyo/arduino-DHTesp

  Written by Mark Ruys, [email protected].
  Updated to work with ESP32 by Bernd Giesecke, [email protected]

  GNU General Public License, check LICENSE for more information.
  All text above must be included in any redistribution.

  Datasheets:
  - http://www.micro4you.com/files/sensor/DHT11.pdf
  - http://www.adafruit.com/datasheets/DHT22.pdf
  - http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Sensors/Weather/RHT03.pdf
  - http://meteobox.tk/files/AM2302.pdf

  Changelog:
    See README.md
 ***************************************************** *****************/

#ifndef dhtesp_h
#define dhtesp_h

#if ARDUINO < 100
  #include <WProgram.h>
#else
  #include <Arduino.h>
#endif

// Reference: http://epb.apogee.net/res/refcomf.asp (References invalid)
enum ComfortState {
  Comfort_OK = 0,
  Comfort_TooHot = 1,
  Comfort_TooCold = 2,
  Comfort_TooDry = 4,
  Comfort_TooHumid = 8,
  Comfort_HotAndHumid = 9,
  Comfort_HotAndDry = 5,
  Comfort_ColdAndHumid = 10,
  Comfort_ColdAndDry = 6
};

// References https://en.wikipedia.org/wiki/Dew_point ==> Relationship to human comfort
enum PerceptionState {
  Perception_Dry = 0,
  Perception_VeryComfy = 1,
  Perception_Comfy = 2,
  Perception_Ok = 3,
  Perception_Uncomfy = 4,
  Perception_QuiteUncomfy = 5,
  Perception_VeryUncomfy = 6,
  Perception_SevereUncomfy = 7
};

struct TempAndHumidity {
  float temperature;
  float humidity;
};

struct ComfortProfile
{
  //Represent the 4 line equations:
  //dry, humid, hot, cold, using the y = mx + b formula
  float m_tooHot_m, m_tooHot_b;
  float m_tooCold_m, m_tooHCold_b;
  float m_tooDry_m, m_tooDry_b;
  float m_tooHumid_m, m_tooHumid_b;

  inline bool isTooHot(float temp, float humidity) {return (temp > (humidity * m_tooHot_m + m_tooHot_b));}
  inline bool isTooHumid(float temp, float humidity) {return (temp > (humidity * m_tooHumid_m + m_tooHumid_b));}
  inline bool isTooCold(float temp, float humidity) {return (temp < (humidity * m_tooCold_m + m_tooHCold_b));}
  inline bool isTooDry(float temp, float humidity) {return (temp < (humidity * m_tooDry_m + m_tooDry_b));}

  inline float distanceTooHot(float temp, float humidity) {return temp - (humidity * m_tooHot_m + m_tooHot_b);}
  inline float distanceTooHumid(float temp, float humidity) {return temp - (humidity * m_tooHumid_m + m_tooHumid_b);}
  inline float distanceTooCold(float temp, float humidity) {return (humidity * m_tooCold_m + m_tooHCold_b) - temp;}
  inline float distanceTooDry(float temp, float humidity) {return (humidity * m_tooDry_m + m_tooDry_b) - temp;}
};

class DHTesp
{
public:

  typedef enum {
    AUTO_DETECT,
    DHT11,
    DHT22,
    AM2302, // Packaged DHT22
    RHT03 // Equivalent to DHT22
  }
  DHT_MODEL_t;

  typedef enum {
    ERROR_NONE = 0,
    ERROR_TIMEOUT,
    ERROR_CHECKSUM
  }
  DHT_ERROR_t;

  TempAndHumidity values;

  // setup(dhtPin) is deprecated, auto detection is not working well on ESP32. Use setup(dhtPin, DHTesp::DHT11) instead!
  void setup(uint8_t dhtPin) __attribute__((deprecated));
  void setup(uint8_t pin, DHT_MODEL_t model=AUTO_DETECT);
  void resetTimer();

  float getTemperature();
  float getHumidity();
  TempAndHumidity getTempAndHumidity();

  DHT_ERROR_t getStatus() { return error; };
  const char* getStatusString();

  DHT_MODEL_t getModel() { return model; }

  int getMinimumSamplingPeriod() { return model == DHT11 ? 1000 : 2000; }

  int8_t getNumberOfDecimalsTemperature() { return model == DHT11 ? 0 : 1; };
  int8_t getLowerBoundTemperature() { return model == DHT11 ? 0 : -40; };
  int8_t getUpperBoundTemperature() { return model == DHT11 ? 50 : 125; };

  int8_t getNumberOfDecimalsHumidity() { return 0; };
  int8_t getLowerBoundHumidity() { return model == DHT11 ? 20 : 0; };
  int8_t getUpperBoundHumidity() { return model == DHT11 ? 90 : 100; };

  static float toFahrenheit(float from Celcius) { return 1.8 * from Celcius + 32.0; };
  static float to Celsius(float from Fahrenheit) { return (from Fahrenheit - 32.0) / 1.8; };

  float computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit=false);
  float computeDewPoint(float temperature, float percentHumidity, bool isFahrenheit=false);
  float getComfortRatio(ComfortState & destComfStatus, float temperature, float percentHumidity, bool isFahrenheit=false);
  ComfortProfile getComfortProfile() {return m_comfort;}
  void setComfortProfile(ComfortProfile & c) {m_comfort = c;}
  inline bool isTooHot(float temp, float humidity) {return m_comfort.isTooHot(temp, humidity);}
  inline bool isTooHumid(float temp, float humidity) {return m_comfort.isTooHumid(temp, humidity);}
  inline bool isTooCold(float temp, float humidity) {return m_comfort.isTooCold(temp, humidity);}
  inline bool isTooDry(float temp, float humidity) {return m_comfort.isTooDry(temp, humidity);}
  byte computePerception(float temperature, float percentHumidity, bool isFahrenheit=false);
  float computeAbsoluteHumidity(float temperature, float percentHumidity, bool isFahrenheit=false);
  uint8_t getPin() { return pin; }
protected:
  void readSensor();

  float temperature;
  float humidity;

  uint8_t pin;

private:
  DHT_MODEL_t model;
  DHT_ERROR_t error;
  unsigned long lastReadTime;
  ComfortProfile m_comfort;
};

#endif /*dhtesp_h*/