Skip to content

Commit 14dbd99

Browse files
committed
Add InfluxDB Support (both push and pull)
1 parent c8603b2 commit 14dbd99

13 files changed

Lines changed: 312 additions & 3 deletions

platformio.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ lib_deps =
3333
robtillaart/INA219@^0.3.1
3434
sensirion/Sensirion I2C SHT4x@^1.1.0
3535
mathertel/OneButton@^2.6.1
36+
tobiasschuerg/ESP8266 Influxdb @ ^3.13.2
3637
build_flags =
3738
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
3839
-std=c++2a

src/ambient/ambient-sht30.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ void APB::Ambient::readSensor() {
4343
// Log.traceln("reading values: %d degrees, %d humidity", _reading->temperature, _reading->humidity);
4444
} else {
4545
logSHT30Error("reading values");
46+
_reading.reset();
4647
}
4748
}
4849

src/ambient/ambient-sht4x.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,12 @@ bool APB::Ambient::initialiseSensor() {
3232
}
3333

3434
void APB::Ambient::readSensor() {
35-
shtCheckForError(sht.measureHighPrecision(_reading->temperature, _reading->humidity), "Error reading temperature/humidity");
35+
Reading reading;
36+
if(!shtCheckForError(sht.measureHighPrecision(reading.temperature, reading.humidity), "Error reading temperature/humidity")) {
37+
_reading = reading;
38+
} else {
39+
_reading.reset();
40+
}
3641
}
3742

3843
#endif

src/configuration.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#define APB_STATUS_LED_INVERT_LOGIC false
1414
#define APB_MAX_STATIONS 5
15+
#define APB_NETWORK_LOGGER_BACKLOG 0
1516
#define APB_AMBIENT_UPDATE_INTERVAL_SECONDS 5
1617
#define APB_HEATER_UPDATE_INTERVAL_SECONDS 5
1718
#define APB_AMBIENT_TEMPERATURE_SENSOR_I2C_ADDRESS 0x44
@@ -25,6 +26,7 @@
2526
#define APB_POWER_INA219_GAIN 8
2627
#define APB_POWER_INA219_VOLTAGE_RANGE 16
2728
#define APB_HISTORY_TASK_SECONDS 10'000
29+
#define APB_INFLUXDB_TASK_SECONDS 2'000
2830

2931
#define APB_AMBIENT_TEMPERATURE_SENSOR_SHT4x
3032
#define APB_HEATER_TEMPERATURE_SENSOR_THERMISTOR

src/history.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
#include <ArduinoLog.h>
33
#include "utils.h"
44
#include <iterator>
5+
#include "configuration.h"
56
#include <memory>
67

78
APB::History &APB::History::Instance = *new APB::History{};
89

10+
911
APB::History::History() {
1012
}
1113

@@ -85,7 +87,7 @@ void APB::History::add() {
8587
entry.heaters[i].set(APB::Heaters::Instance[i]);
8688
}
8789
#endif
88-
entry.setPower(APB::PowerMonitor::Instance.status());
90+
entry.setPower(PowerMonitor::Instance.status());
8991

9092
_entries.push_back(entry);
9193
while(_entries.size() > maxSize) {

src/influxdb.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#include "influxdb.h"
2+
#include "configuration.h"
3+
4+
APB::InfluxDb &APB::InfluxDb::Instance = *new APB::InfluxDb{};
5+
6+
#ifdef USE_INFLUXDB
7+
#include "settings.h"
8+
#include "wifimanager.h"
9+
#include "powermonitor.h"
10+
#include "heater.h"
11+
#include "ambient/ambient.h"
12+
#include <StreamString.h>
13+
#include <ArduinoLog.h>
14+
15+
16+
#include <InfluxDbClient.h>
17+
#include <InfluxDbCloud.h>
18+
#define TZ_INFO "UTC1"
19+
20+
void setPointSource(Point &point) {
21+
point.addTag("source", APB::Settings::Instance.apConfiguration().essid);
22+
}
23+
24+
InfluxDBClient influxDbClient(INFLUXDB_URL, INFLUXDB_ORG, INFLUXDB_BUCKET, INFLUXDB_TOKEN, InfluxDbCloud2CACert);
25+
Point powerSensor("power");
26+
Point ambientSensor("ambient");
27+
Point heatersSensor("heaters");
28+
29+
void APB::InfluxDb::setup(Scheduler & scheduler) {
30+
setPointSource(powerSensor);
31+
setPointSource(ambientSensor);
32+
setPointSource(heatersSensor);
33+
WiFiManager::Instance.addOnConnectedListener(std::bind(&InfluxDb::onWiFiConnected, this));
34+
35+
new Task(APB_INFLUXDB_TASK_SECONDS, TASK_FOREVER, std::bind(&InfluxDb::sendData, this), &scheduler, true);
36+
}
37+
38+
void APB::InfluxDb::onWiFiConnected() {
39+
Log.infoln("InfluxDB: onWiFiConnected");
40+
timeSync(TZ_INFO, "pool.ntp.org", "time.nis.gov");
41+
connected = influxDbClient.validateConnection();
42+
if(connected) {
43+
Log.infoln("InfluxDB Connected");
44+
} else {
45+
Log.errorln("InfluxDB connection error: %s", influxDbClient.getLastErrorMessage().c_str());
46+
}
47+
}
48+
49+
50+
void sendPoint(Point &point) {
51+
bool written = influxDbClient.writePoint(point);
52+
Log.traceln("InfluxDB: Sending point <%s>: %d", powerSensor.toLineProtocol().c_str(), written);
53+
if(!written) {
54+
Log.errorln("InfluxDB last error: %s", influxDbClient.getLastErrorMessage().c_str());
55+
}
56+
}
57+
58+
void APB::InfluxDb::sendData() {
59+
if(!connected) {
60+
return;
61+
}
62+
powerSensor.clearFields();
63+
auto powerReading = PowerMonitor::Instance.status();
64+
powerSensor.addField("voltage", powerReading.busVoltage);
65+
powerSensor.addField("current", powerReading.current);
66+
powerSensor.addField("power", powerReading.power);
67+
sendPoint(powerSensor);
68+
69+
#ifndef APB_AMBIENT_TEMPERATURE_SENSOR_NONE
70+
auto ambientReading = Ambient::Instance.reading();
71+
if(ambientReading.has_value()) {
72+
ambientSensor.addField("temperature", ambientReading->temperature);
73+
ambientSensor.addField("humidity", ambientReading->humidity);
74+
ambientSensor.addField("dewpoint", ambientReading->dewpoint());
75+
sendPoint(ambientSensor);
76+
}
77+
#endif
78+
#if APB_HEATERS_SIZE > 0
79+
for(Heater &heater : Heaters::Instance) {
80+
heatersSensor.clearFields();
81+
heatersSensor.addField("index", heater.index());
82+
if(heater.temperature().has_value()) {
83+
heatersSensor.addField("temperature", heater.temperature().value());
84+
}
85+
if(heater.targetTemperature().has_value()) {
86+
heatersSensor.addField("target_temperature", heater.targetTemperature().value());
87+
}
88+
heatersSensor.addField("duty", heater.duty());
89+
heatersSensor.addField("mode", heater.mode());
90+
heatersSensor.addField("active", heater.active());
91+
sendPoint(heatersSensor);
92+
}
93+
#endif
94+
}
95+
#else
96+
void APB::InfluxDb::setup(Scheduler & scheduler) {}
97+
void APB::InfluxDb::onWiFiConnected() {}
98+
void APB::InfluxDb::sendData() {}
99+
#endif

src/influxdb.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#pragma once
2+
#include <TaskSchedulerDeclarations.h>
3+
4+
namespace APB {
5+
class InfluxDb {
6+
public:
7+
static InfluxDb &Instance;
8+
void setup(Scheduler &scheduler);
9+
void onWiFiConnected();
10+
void sendData();
11+
bool available() const { return connected; }
12+
private:
13+
bool connected = false;
14+
};
15+
}

src/main.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <ArduinoOTA.h>
1919
#include <AsyncTCP.h>
2020
#include "asyncbufferedtcplogger.h"
21+
#include "influxdb.h"
2122

2223
Scheduler scheduler;
2324
AsyncServer loggerServer{9911};
@@ -55,6 +56,8 @@ void setup() {
5556

5657
LittleFS.begin();
5758
APB::Settings::Instance.setup();
59+
APB::InfluxDb::Instance.setup(scheduler);
60+
5861
APB::StatusLed::Instance.setup();
5962

6063
APB::WiFiManager::Instance.setup(scheduler);

src/metricsresponse.h

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#pragma once
2+
3+
#include <ESPAsyncWebServer.h>
4+
#include <list>
5+
#include <ArduinoLog.h>
6+
#include <StreamString.h>
7+
#include "utils.h"
8+
#include <algorithm>
9+
#include <numeric>
10+
11+
#define METRICS_CONTENT_TYPE "text/plain; version=0.0.4"
12+
13+
namespace APB {
14+
15+
class MetricsResponse {
16+
public:
17+
struct Labels {
18+
StreamString buffer;
19+
Labels concat(const Labels &other) const {
20+
Labels newLabels;
21+
newLabels.add(*this);
22+
newLabels.add(other);
23+
return newLabels;
24+
}
25+
26+
27+
Labels &add(const Labels &other) {
28+
if(!other.buffer.isEmpty()) {
29+
addSeparator();
30+
}
31+
buffer.print(other.c_str());
32+
return *this;
33+
}
34+
35+
Labels &add(const char *label, const char *value) {
36+
addSeparator();
37+
buffer.printf(R"(%s="%s")", label, value);
38+
return *this;
39+
}
40+
Labels &field(const char *value) {
41+
return add("field", value);
42+
}
43+
Labels &unit(const char *value) {
44+
return add("unit", value);
45+
}
46+
47+
const char *c_str() const {
48+
return buffer.c_str();
49+
}
50+
51+
void addSeparator() {
52+
if(!buffer.isEmpty()) {
53+
buffer.write(',');
54+
}
55+
}
56+
};
57+
MetricsResponse(AsyncWebServerRequest *request, const Labels &fixedLabels, size_t bufferSize=1048 * 10, int statusCode=200)
58+
: request{request}, response{request->beginResponseStream(METRICS_CONTENT_TYPE)}, fixedLabels{fixedLabels} {
59+
}
60+
61+
MetricsResponse &counter(const char *name, float value, const Labels &labels = {}, const char *help = nullptr, bool addHeaders=true) {
62+
if(addHeaders) {
63+
addHelp(name, help);
64+
addType(name, "counter");
65+
}
66+
response->printf("%s {%s} %f\n", name, fixedLabels.concat(labels).c_str(), value);
67+
return *this;
68+
}
69+
70+
MetricsResponse &gauge(const char *name, float value, const Labels &labels = {}, const char *help = nullptr, bool addHeaders=true) {
71+
if(addHeaders) {
72+
addHelp(name, help);
73+
addType(name, "gauge");
74+
}
75+
76+
77+
response->printf("%s {%s} %f\n", name, fixedLabels.concat(labels).c_str(), value);
78+
return *this;
79+
}
80+
81+
~MetricsResponse() {
82+
request->send(response);
83+
}
84+
85+
private:
86+
void addType(const char *name, const char *type) {
87+
response->printf("# TYPE %s %s\n", name, type);
88+
}
89+
void addHelp(const char *name, const char *help) {
90+
if(help) {
91+
response->printf("# HELP %s %s\n", name, help);
92+
}
93+
}
94+
95+
AsyncResponseStream *response;
96+
AsyncWebServerRequest *request;
97+
const Labels fixedLabels;
98+
};
99+
100+
}

0 commit comments

Comments
 (0)