diff --git a/README.md b/README.md index e54ae99..63df549 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,22 @@ # prometheus-arduino-dht-exporter -A Prometheus exporter for an Arduino using a DHT sensor. +A Prometheus exporter for IoT temperature and humidity measurements, using an Arduino with a Wi-Fi module and a DHT (temperature + humidity) sensor. + +## Hardware + +Arduino-compatible board: [WEMOS D1 Mini](https://wiki.wemos.cc/products:d1:d1_mini) (ESP8266) + +DHT sensor: [Wemos DHT Shield](https://wiki.wemos.cc/products:retired:dht_shield_v1.0.0) (DHT11) + +## Requirements + +- [Arduino IDE](https://www.arduino.cc/en/Main/Software) (download) +- [esp8266 library for Arduino](https://github.com/esp8266/Arduino#installing-with-boards-manager) (see instructions) +- [Adafruit DHT sensor library](https://github.com/adafruit/DHT-sensor-library) (install using the library manager, including all dependencies) + +## Building + +1. Copy `config.default.h` to `config.h` and fill inn the details. +1. Set the correct board settings in Arduino IDE: + - WEMOS D1 Mini uses board "WeMoS D1 R2 & mini", CPU frequency 160MHz and upload speed 821 600. +1. Build and upload in Arduino IDE. diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..18e58a7 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +/config.h diff --git a/src/config.default.h b/src/config.default.h new file mode 100644 index 0000000..4222ddf --- /dev/null +++ b/src/config.default.h @@ -0,0 +1,21 @@ +#pragma once + +// Debug mode is enabled if not zero +#define DEBUG 0 +// DHT sensor type: DHT11, DHT21 or DHT22 +#define DHT_TYPE DHT11 +// DHT pin +#define DHT_PIN D4 +// Temperature offset in degrees Celsius +#define TEMPERATURE_CORRECTION_OFFSET 0 +// Humidity offset in percent +#define HUMIDITY_CORRECTION_OFFSET 0 +// How long to cache the sensor results +#define READ_INTERVAL 2000 +// Retry up to this many times to read the snsor correctly before returning before returning error +#define READ_RETRY_COUNT 5 + +// WiFi SSID (required) +const char *WIFI_SSID = ""; +// WiFi password (required) +const char *WIFI_PASSWORD = ""; diff --git a/src/src.ino b/src/src.ino new file mode 100644 index 0000000..b9b6e7c --- /dev/null +++ b/src/src.ino @@ -0,0 +1,117 @@ +#include +#include +#include +#include + +#include "config.h" + +DHT dht_sensor(DHT_PIN, DHT_TYPE); +ESP8266WebServer web_server(80); + +float humidity, temperature; +char str_humidity[10], str_temperature[10]; +unsigned long previousReadTime = 0; + +void setup_wifi(); +void setup_dht(); +void setup_web(); +void read_sensors(boolean force=false); +void read_sensor(const char *name, float (*function)(), const char *str_format, char *str_result, size_t len_result, float &result); +void log(const char *message, boolean error=false); + +void setup(void) { + Serial.begin(9600); + log("Starting setup ..."); + setup_wifi(); + setup_dht(); + setup_web(); + log("Started setup"); +} + +void setup_wifi() { + char message[100]; + snprintf(message, 100, "SSID: %s", WIFI_SSID); + log(message); + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + log("WiFi connecting ..."); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + } + const IPAddress &ipaddr = WiFi.localIP(); + snprintf(message, 100, "IP address: %d.%d.%d.%d", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); + log(message); + log("Started WiFi"); +} + +void setup_dht() { + dht_sensor.begin(); + // Initial read + read_sensors(true); + log("Started DHT sensor"); +} + +void setup_web() { + web_server.on("/", [] { + read_sensors(); + char response[100]; + snprintf(response, 100, "Temperature: %s\nHumidity: %s", str_temperature, str_humidity); + web_server.send(200, "text/plain", response); + }); + web_server.begin(); + log("Started web server"); +} + +void loop(void) { + web_server.handleClient(); +} + +void read_sensors(boolean force) { + unsigned long currentTime = millis(); + if (!force && currentTime - previousReadTime < READ_INTERVAL) { + // Use cached values + return; + } + previousReadTime = currentTime; + + // Read temperature as degrees Celsius and force read + read_sensor("temperature", []() { + return dht_sensor.readTemperature(false, true) + TEMPERATURE_CORRECTION_OFFSET; + }, "%.0f\u00B0C", str_temperature, 10, temperature); + + // Read humidity and force read + read_sensor("humidity", []() { + return dht_sensor.readHumidity(true) + HUMIDITY_CORRECTION_OFFSET; + }, "%.0f%%", str_humidity, 10, humidity); +} + +void read_sensor(const char *name, float (*function)(), const char *str_format, char *str_result, size_t len_result, float &result) { + char message[100]; + result = function(); + for (int i = 0; isnan(result) && i < READ_RETRY_COUNT; i++) { +#if DEBUG != 0 + snprintf(message, 100, "Re-reading failed %s ...", name); + log(message, true); +#endif + result = function(); + } + if (!isnan(humidity)) { + snprintf(str_result, len_result, str_format, result); + } else { + strcpy(str_result, "ERROR"); + snprintf(message, 100, "Failed to read %s!", name); + log(message, true); + } +} + +void log(const char *message, boolean error) { + float seconds = millis() / 1000.0; + char level[10]; + if (!error) { + strcpy(level, "INFO"); + } else { + strcpy(level, "ERROR"); + } + char record[150]; + snprintf(record, 150, "[%10.3f] [%-5s] %s", seconds, level, message); + Serial.println(record); +}