diff --git a/src/config.default.h b/src/config.default.h index a170c2f..6b0c058 100644 --- a/src/config.default.h +++ b/src/config.default.h @@ -22,21 +22,23 @@ #define WIFI_SSID "" // Wi-Fi password (required) #define WIFI_PASSWORD "" -// Wi-Fi hostname (for DHCP, uncomment if non-default desired) +// Hostname for DHCP DDNS, overrides the default (uncomment to enable) // #define WIFI_HOSTNAME "my_host_name" -// Wi-Fi static IPv4 address enabled or disabled (disable for DHCPv4) -#define WIFI_IPV4_STATIC false -// Wi-Fi static IPv4 address +// Use static IPv4 addressing, disable for DHCPv4 +#define WIFI_IPV4_STATIC true +// Static IPv4 address #define WIFI_IPV4_ADDRESS 192, 168, 1, 15 -// Wi-Fi static IPv4 gateway address +// Static IPv4 gateway address #define WIFI_IPV4_GATEWAY 192, 168, 1, 1 -// Wi-Fi static IPv4 subnet mask +// Static IPv4 subnet mask #define WIFI_IPV4_SUBNET_MASK 255, 255, 255, 0 -// Wi-Fi static IPv4 primary DNS server +// Static IPv4 primary DNS server #define WIFI_IPV4_DNS_1 1, 1, 1, 1 -// Wi-Fi static IPv4 secondary DNS server +// Static IPv4 secondary DNS server #define WIFI_IPV4_DNS_2 1, 0, 0, 1 // HTTP server port #define HTTP_SERVER_PORT 80 // HTTP metrics endpoint #define HTTP_METRICS_ENDPOINT "/metrics" +// Prometheus namespace, aka metric prefix +#define PROM_NAMESPACE "iot" diff --git a/src/src.ino b/src/src.ino index e7c5046..5c71159 100644 --- a/src/src.ino +++ b/src/src.ino @@ -1,11 +1,12 @@ #include #include -#include #include #include "config.h" #include "version.h" +#define EXPLODE4(arr) (arr[0], arr[1], arr[2], arr[3]) + enum LogLevel { DEBUG, INFO, @@ -28,78 +29,90 @@ float humidity, temperature, heat_index; uint32_t previous_read_time = 0; void setup(void) { + char message[128]; Serial.begin(9600); setup_dht_sensor(); setup_wifi(); setup_http_server(); + snprintf(message, 128, "Namespace: %s", PROM_NAMESPACE); + log(message); + log("Setup done"); } void setup_dht_sensor() { - log("Setting up DHT sensor ..."); + log("Setting up DHT sensor"); dht_sensor.setup(DHT_PIN, DHTesp::DHT_TYPE); delay(dht_sensor.getMinimumSamplingPeriod()); // Test read read_sensors(true); - log("DHT sensor ready."); + log("DHT sensor ready"); } void setup_wifi() { - WiFi.mode(WIFI_STA); char message[128]; + log("Setting up Wi-Fi"); snprintf(message, 128, "Wi-Fi SSID: %s", WIFI_SSID); log(message); + snprintf(message, 128, "MAC address: %s", WiFi.macAddress().c_str()); + log(message); + snprintf(message, 128, "Initial hostname: %s", WiFi.hostname().c_str()); + log(message); - if (WIFI_IPV4_STATIC) { - IPAddress address(WIFI_IPV4_ADDRESS); - IPAddress subnet(WIFI_IPV4_SUBNET_MASK); - IPAddress gateway(WIFI_IPV4_GATEWAY); - IPAddress dns1(WIFI_IPV4_DNS_1); - IPAddress dns2(WIFI_IPV4_DNS_2); - snprintf(message, 128, "Static IPv4 address: %d.%d.%d.%d", address[0], address[1], address[2], address[3]); - log(message); - snprintf(message, 128, "Static IPv4 subnet mask: %d.%d.%d.%d", subnet[0], subnet[1], subnet[2], subnet[3]); - log(message); - snprintf(message, 128, "Static IPv4 gateway: %d.%d.%d.%d", gateway[0], gateway[1], gateway[2], gateway[3]); - log(message); - snprintf(message, 128, "Static IPv4 primary DNS server: %d.%d.%d.%d", dns1[0], dns1[1], dns1[2], dns1[3]); - log(message); - snprintf(message, 128, "Static IPv4 secondary DNS server: %d.%d.%d.%d", dns2[0], dns2[1], dns2[2], dns2[3]); - log(message); - if (!WiFi.config(address, gateway, subnet, dns1, dns2)) { - log("Failed to configure Wi-Fi.", LogLevel::ERROR); + WiFi.mode(WIFI_STA); + + #if WIFI_IPV4_STATIC == true + log("Using static IPv4 adressing"); + IPAddress static_address(WIFI_IPV4_ADDRESS); + IPAddress static_subnet(WIFI_IPV4_SUBNET_MASK); + IPAddress static_gateway(WIFI_IPV4_GATEWAY); + IPAddress static_dns1(WIFI_IPV4_DNS_1); + IPAddress static_dns2(WIFI_IPV4_DNS_2); + if (!WiFi.config(static_address, static_gateway, static_subnet, static_dns1, static_dns2)) { + log("Failed to configure static addressing", LogLevel::ERROR); } - } - - log("Wi-Fi connecting ..."); - #ifdef WIFI_HOSTNAME - snprintf(message, 128, "Initial hostname: %s", WiFi.hostname().c_str()); - log(message); - log("Requesting hostname " WIFI_HOSTNAME); - if (!WiFi.hostname(WIFI_HOSTNAME)) { - log("Hostname request failed"); - } else { - log("Hostname request succeeded"); - } - snprintf(message, 128, "New hostname: %s", WiFi.hostname().c_str()); - log(message); #endif + + #ifdef WIFI_HOSTNAME + log("Requesting hostname: " WIFI_HOSTNAME); + if (!WiFi.hostname(WIFI_HOSTNAME)) { + log("Hostname changed"); + } else { + log("Failed to change hostname (too long?)"); + } + #endif + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); while (WiFi.status() != WL_CONNECTED) { - log("Wi-Fi waiting ...", LogLevel::DEBUG); + log("Wi-Fi connection not ready, waiting", LogLevel::DEBUG); delay(500); } - const IPAddress &address = WiFi.localIP(); + log("Wi-Fi connected."); - snprintf(message, 128, "IPv4 address: %d.%d.%d.%d", address[0], address[1], address[2], address[3]); + snprintf(message, 128, "SSID: %s", WiFi.SSID().c_str()); + log(message); + snprintf(message, 128, "BSSID: %s", WiFi.BSSIDstr().c_str()); + log(message); + snprintf(message, 128, "Hostname: %s", WiFi.hostname().c_str()); + log(message); + snprintf(message, 128, "IPv4 address: %s", WiFi.localIP().toString().c_str()); + log(message); + snprintf(message, 128, "IPv4 subnet mask: %s", WiFi.subnetMask().toString().c_str()); + log(message); + snprintf(message, 128, "IPv4 gateway: %s", WiFi.gatewayIP().toString().c_str()); + log(message); + snprintf(message, 128, "Primary DNS server: %s", WiFi.dnsIP(0).toString().c_str()); + log(message); + snprintf(message, 128, "Secondary DNS server: %s", WiFi.dnsIP(1).toString().c_str()); log(message); } void setup_http_server() { - http_server.on("/", HTTPMethod::HTTP_GET, handle_http_home_client); - http_server.on(HTTP_METRICS_ENDPOINT, HTTPMethod::HTTP_GET, handle_http_metrics_client); + char message[128]; + http_server.on("/", HTTPMethod::HTTP_GET, handle_http_root); + http_server.on(HTTP_METRICS_ENDPOINT, HTTPMethod::HTTP_GET, handle_http_metrics); + http_server.onNotFound(handle_http_not_found); http_server.begin(); log("HTTP server started."); - char message[128]; snprintf(message, 128, "Metrics endpoint: %s", HTTP_METRICS_ENDPOINT); log(message); } @@ -108,7 +121,8 @@ void loop(void) { http_server.handleClient(); } -void handle_http_home_client() { +void handle_http_root() { + log_request(); static size_t const BUFSIZE = 256; static char const *response_template = "Prometheus ESP8266 DHT Exporter by HON95.\n" @@ -121,25 +135,26 @@ void handle_http_home_client() { http_server.send(200, "text/plain; charset=utf-8", response); } -void handle_http_metrics_client() { +void handle_http_metrics() { + log_request(); static size_t const BUFSIZE = 1024; static char const *response_template = - "# HELP iot_info Metadata about the device.\n" - "# TYPE iot_info gauge\n" - "# UNIT iot_info \n" - "iot_info{version=\"%s\",board=\"%s\",sensor=\"%s\"} 1\n" - "# HELP iot_air_humidity_percent Air humidity.\n" - "# TYPE iot_air_humidity_percent gauge\n" - "# UNIT iot_air_humidity_percent %%\n" - "iot_air_humidity_percent %f\n" - "# HELP iot_air_temperature_celsius Air temperature.\n" - "# TYPE iot_air_temperature_celsius gauge\n" - "# UNIT iot_air_temperature_celsius \u00B0C\n" - "iot_air_temperature_celsius %f\n" - "# HELP iot_air_heat_index_celsius Apparent air temperature, based on temperature and humidity.\n" - "# TYPE iot_air_heat_index_celsius gauge\n" - "# UNIT iot_air_heat_index_celsius \u00B0C\n" - "iot_air_heat_index_celsius %f\n"; + "# HELP " PROM_NAMESPACE "_info Metadata about the device.\n" + "# TYPE " PROM_NAMESPACE "_info gauge\n" + "# UNIT " PROM_NAMESPACE "_info \n" + PROM_NAMESPACE "_info{version=\"%s\",board=\"%s\",sensor=\"%s\"} 1\n" + "# HELP " PROM_NAMESPACE "_air_humidity_percent Air humidity.\n" + "# TYPE " PROM_NAMESPACE "_air_humidity_percent gauge\n" + "# UNIT " PROM_NAMESPACE "_air_humidity_percent %%\n" + PROM_NAMESPACE "_air_humidity_percent %f\n" + "# HELP " PROM_NAMESPACE "_air_temperature_celsius Air temperature.\n" + "# TYPE " PROM_NAMESPACE "_air_temperature_celsius gauge\n" + "# UNIT " PROM_NAMESPACE "_air_temperature_celsius \u00B0C\n" + PROM_NAMESPACE "_air_temperature_celsius %f\n" + "# HELP " PROM_NAMESPACE "_air_heat_index_celsius Apparent air temperature, based on temperature and humidity.\n" + "# TYPE " PROM_NAMESPACE "_air_heat_index_celsius gauge\n" + "# UNIT " PROM_NAMESPACE "_air_heat_index_celsius \u00B0C\n" + PROM_NAMESPACE "_air_heat_index_celsius %f\n"; read_sensors(); if (isnan(humidity) || isnan(temperature) || isnan(heat_index)) { @@ -152,6 +167,11 @@ void handle_http_metrics_client() { http_server.send(200, "text/plain; charset=utf-8", response); } +void handle_http_not_found() { + log_request(); + http_server.send(404, "text/plain; charset=utf-8", "Not found."); +} + void read_sensors(boolean force) { uint32_t min_interval = max(dht_sensor.getMinimumSamplingPeriod(), READ_INTERVAL); uint32_t current_time = millis(); @@ -211,6 +231,27 @@ bool read_sensor(float (*function)(), float *value) { return success; } +void log_request() { + char message[128]; + char method_name[16]; + get_http_method_name(method_name, 16, http_server.method()); + snprintf(message, 128, "Request: client=%s:%u method=%s path=%s", + http_server.client().remoteIP().toString().c_str(), http_server.client().remotePort(), method_name, http_server.uri().c_str()); + log(message, LogLevel::INFO); +} + +void get_http_method_name(char *name, size_t name_length, HTTPMethod method) { + switch (method) { + case HTTP_ANY: + snprintf(name, name_length, "ANY"); + break; + default: + snprintf(name, name_length, "UNKNOWN"); + break; + //, HTTP_GET, HTTP_HEAD, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS } + } +} + void log(char const *message, LogLevel level) { if (DEBUG_MODE == 0 && level == LogLevel::DEBUG) { return;