diff --git a/html/config.htm b/html/config.htm index 732ddad..d8c66bb 100644 --- a/html/config.htm +++ b/html/config.htm @@ -107,14 +107,14 @@
- -
+ +
- Temperature Format: -
+ Temperature Format: +
- -
+ +
- +
-
+
@@ -146,10 +146,10 @@
- - - -
+ + + +
@@ -171,14 +171,14 @@
- -
+ +
- -
+ +
@@ -186,8 +186,8 @@
- -
+ +
@@ -195,31 +195,61 @@
- -
- + +
+
- -
+ +
- -
+ +
- -
+
+
+ +
+ +
+
-
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
@@ -241,14 +271,13 @@
- -
+ +
-
-
+
-
+
@@ -280,34 +309,36 @@
- -
+ +
- +
- -
+ +
- -
- +
+
+ + +
- -
+ +
-
+
@@ -397,6 +428,10 @@ $("#influxdb2-org").val(cfg["influxdb2-org"]); $("#influxdb2-bucket").val(cfg["influxdb2-bucket"]); $("#influxdb2-auth").val(cfg["influxdb2-auth"]); + $("#mqtt-push").val(cfg["mqtt-push"]); + $("#mqtt-topic").val(cfg["mqtt-topic"]); + $("#mqtt-user").val(cfg["mqtt-user"]); + $("#mqtt-pass").val(cfg["mqtt-pass"]); $("#sleep-interval").val(cfg["sleep-interval"]); $("#voltage-factor").val(cfg["voltage-factor"]); $("#gravity-formula").val(cfg["gravity-formula"]); @@ -406,7 +441,7 @@ $("#gyro-calibration-data").text( cfg["gyro-calibration-data"]["ax"] + "," + cfg["gyro-calibration-data"]["ay"] + "," + cfg["gyro-calibration-data"]["az"] + "," + cfg["gyro-calibration-data"]["gx"] + "," + cfg["gyro-calibration-data"]["gy"] + "," + cfg["gyro-calibration-data"]["gz"] ); $("#battery").text(cfg["battery"] + " V"); $("#angle").text(cfg["angle"]); - $("#gravity").text(cfg["gravity"] + " SG"); + //$("#gravity").text(cfg["gravity"] + " SG"); }) .fail(function () { showError('Unable to get data from the device.'); diff --git a/html/config.min.htm b/html/config.min.htm index c595e7f..9344bbc 100644 --- a/html/config.min.htm +++ b/html/config.min.htm @@ -1 +1 @@ -Beer Gravity Monitor

Temperature Format:




(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file +Beer Gravity Monitor

Temperature Format:





(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 25c7f9c..604d26c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -52,6 +52,7 @@ lib_deps = # Switched to forks for better version control. https://github.com/mp-se/OneWire#v2.3.6 # https://github.com/PaulStoffregen/OneWire https://github.com/mp-se/Arduino-Temperature-Control-Library#3.9.1 # https://github.com/milesburton/Arduino-Temperature-Control-Library https://github.com/mp-se/arduinoCurveFitting#v1.0.6 # https://github.com/Rotario/arduinoCurveFitting + https://github.com/256dpi/arduino-mqtt [env:gravity-debug] upload_speed = ${common_env_data.upload_speed} @@ -64,8 +65,9 @@ extra_scripts = script/create_versionjson.py build_unflags = ${common_env_data.build_unflags} - -D MAIN_DISABLE_LOGGING +# -D MAIN_DISABLE_LOGGING -D WEB_DISABLE_LOGGING + -D PUSH_DISABLE_LOGGING build_flags = ${common_env_data.build_flags} -D PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS diff --git a/src/config.cpp b/src/config.cpp index 3abecc2..323efd9 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -74,6 +74,10 @@ void Config::createJson(DynamicJsonDocument& doc) { doc[CFG_PARAM_PUSH_INFLUXDB2_ORG] = getInfluxDb2PushOrg(); doc[CFG_PARAM_PUSH_INFLUXDB2_BUCKET] = getInfluxDb2PushBucket(); doc[CFG_PARAM_PUSH_INFLUXDB2_AUTH] = getInfluxDb2PushToken(); + doc[CFG_PARAM_PUSH_MQTT] = getMqttUrl(); + doc[CFG_PARAM_PUSH_MQTT_TOPIC] = getMqttTopic(); + doc[CFG_PARAM_PUSH_MQTT_USER] = getMqttUser(); + doc[CFG_PARAM_PUSH_MQTT_PASS] = getMqttPass(); doc[CFG_PARAM_SLEEP_INTERVAL] = getSleepInterval(); doc[CFG_PARAM_VOLTAGEFACTOR] = getVoltageFactor(); doc[CFG_PARAM_GRAVITY_FORMULA] = getGravityFormula(); @@ -189,16 +193,20 @@ bool Config::loadFile() { if (!doc[CFG_PARAM_MDNS].isNull()) setMDNS(doc[CFG_PARAM_MDNS]); if (!doc[CFG_PARAM_SSID].isNull()) setWifiSSID(doc[CFG_PARAM_SSID]); if (!doc[CFG_PARAM_PASS].isNull()) setWifiPass(doc[CFG_PARAM_PASS]); + if (!doc[CFG_PARAM_TEMPFORMAT].isNull()) { String s = doc[CFG_PARAM_TEMPFORMAT]; setTempFormat(s.charAt(0)); } + if (!doc[CFG_PARAM_PUSH_BREWFATHER].isNull()) setBrewfatherPushUrl(doc[CFG_PARAM_PUSH_BREWFATHER]); + if (!doc[CFG_PARAM_PUSH_HTTP].isNull()) setHttpPushUrl(doc[CFG_PARAM_PUSH_HTTP]); if (!doc[CFG_PARAM_PUSH_HTTP2].isNull()) setHttpPushUrl2(doc[CFG_PARAM_PUSH_HTTP2]); + if (!doc[CFG_PARAM_PUSH_INFLUXDB2].isNull()) setInfluxDb2PushUrl(doc[CFG_PARAM_PUSH_INFLUXDB2]); if (!doc[CFG_PARAM_PUSH_INFLUXDB2_ORG].isNull()) @@ -207,6 +215,15 @@ bool Config::loadFile() { setInfluxDb2PushBucket(doc[CFG_PARAM_PUSH_INFLUXDB2_BUCKET]); if (!doc[CFG_PARAM_PUSH_INFLUXDB2_AUTH].isNull()) setInfluxDb2PushToken(doc[CFG_PARAM_PUSH_INFLUXDB2_AUTH]); + + if (!doc[CFG_PARAM_PUSH_MQTT].isNull()) setMqttUrl(doc[CFG_PARAM_PUSH_MQTT]); + if (!doc[CFG_PARAM_PUSH_MQTT_TOPIC].isNull()) + setMqttTopic(doc[CFG_PARAM_PUSH_MQTT_TOPIC]); + if (!doc[CFG_PARAM_PUSH_MQTT_USER].isNull()) + setMqttUser(doc[CFG_PARAM_PUSH_MQTT_USER]); + if (!doc[CFG_PARAM_PUSH_MQTT_PASS].isNull()) + setMqttPass(doc[CFG_PARAM_PUSH_MQTT_PASS]); + if (!doc[CFG_PARAM_SLEEP_INTERVAL].isNull()) setSleepInterval(doc[CFG_PARAM_SLEEP_INTERVAL].as()); if (!doc[CFG_PARAM_VOLTAGEFACTOR].isNull()) diff --git a/src/config.hpp b/src/config.hpp index 7c9fb27..9b85505 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -45,20 +45,24 @@ SOFTWARE. // These are used in API + Savefile #define CFG_PARAM_ID "id" -#define CFG_PARAM_MDNS "mdns" // Device name -#define CFG_PARAM_OTA "ota-url" // Base URL for OTA -#define CFG_PARAM_SSID "wifi-ssid" // WIFI -#define CFG_PARAM_PASS "wifi-pass" // WIFI +#define CFG_PARAM_MDNS "mdns" // Device name +#define CFG_PARAM_OTA "ota-url" +#define CFG_PARAM_SSID "wifi-ssid" +#define CFG_PARAM_PASS "wifi-pass" -#define CFG_PARAM_PUSH_BREWFATHER "brewfather-push" // URL (brewfather format) -#define CFG_PARAM_PUSH_HTTP "http-push" // URL (iSpindle format) -#define CFG_PARAM_PUSH_HTTP2 "http-push2" // URL (iSpindle format) -#define CFG_PARAM_PUSH_INFLUXDB2 "influxdb2-push" // URL -#define CFG_PARAM_PUSH_INFLUXDB2_ORG "influxdb2-org" // URL -#define CFG_PARAM_PUSH_INFLUXDB2_BUCKET "influxdb2-bucket" // URL -#define CFG_PARAM_PUSH_INFLUXDB2_AUTH "influxdb2-auth" // URL -#define CFG_PARAM_SLEEP_INTERVAL "sleep-interval" // Sleep interval -#define CFG_PARAM_TEMPFORMAT "temp-format" // C or F +#define CFG_PARAM_PUSH_BREWFATHER "brewfather-push" +#define CFG_PARAM_PUSH_HTTP "http-push" +#define CFG_PARAM_PUSH_HTTP2 "http-push2" +#define CFG_PARAM_PUSH_INFLUXDB2 "influxdb2-push" +#define CFG_PARAM_PUSH_INFLUXDB2_ORG "influxdb2-org" +#define CFG_PARAM_PUSH_INFLUXDB2_BUCKET "influxdb2-bucket" +#define CFG_PARAM_PUSH_INFLUXDB2_AUTH "influxdb2-auth" +#define CFG_PARAM_PUSH_MQTT "mqtt-push" +#define CFG_PARAM_PUSH_MQTT_USER "mqtt-user" +#define CFG_PARAM_PUSH_MQTT_PASS "mqtt-pass" +#define CFG_PARAM_PUSH_MQTT_TOPIC "mqtt-topic" +#define CFG_PARAM_SLEEP_INTERVAL "sleep-interval" // Sleep interval +#define CFG_PARAM_TEMPFORMAT "temp-format" // C or F #define CFG_PARAM_VOLTAGEFACTOR \ "voltage-factor" // Factor to calculate the battery voltage #define CFG_PARAM_GRAVITY_FORMULA \ @@ -135,6 +139,11 @@ class Config { String influxDb2Bucket; // Bucket for InfluxDB v2 String influxDb2Token; // Auth Token for InfluxDB v2 + String mqttUrl; // Server name + String mqttTopic; + String mqttUser; + String mqttPass; + // Gravity and temperature calculations String gravityFormula; bool gravityTempAdj; // true, false @@ -229,6 +238,29 @@ class Config { saveNeeded = true; } + // MQTT + bool isMqttActive() { return mqttUrl.length() ? true : false; } + const char* getMqttUrl() { return mqttUrl.c_str(); } + void setMqttUrl(String s) { + mqttUrl = s; + saveNeeded = true; + } + const char* getMqttTopic() { return mqttTopic.c_str(); } + void setMqttTopic(String s) { + mqttTopic = s; + saveNeeded = true; + } + const char* getMqttUser() { return mqttUser.c_str(); } + void setMqttUser(String s) { + mqttUser = s; + saveNeeded = true; + } + const char* getMqttPass() { return mqttPass.c_str(); } + void setMqttPass(String s) { + mqttPass = s; + saveNeeded = true; + } + int getSleepInterval() { return sleepInterval; } void setSleepInterval(int v) { sleepInterval = v; diff --git a/src/pushtarget.cpp b/src/pushtarget.cpp index f4668ca..c296b86 100644 --- a/src/pushtarget.cpp +++ b/src/pushtarget.cpp @@ -21,6 +21,8 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include + #include #include #include @@ -74,6 +76,12 @@ void PushTarget::send(float angle, float gravity, float corrGravity, float temp, sendInfluxDb2(angle, gravity, corrGravity, temp, runTime); LOG_PERF_STOP("push-influxdb2"); } + + if (myConfig.isMqttActive()) { + LOG_PERF_START("push-mqtt"); + sendMqtt(angle, gravity, corrGravity, temp, runTime); + LOG_PERF_STOP("push-mqtt"); + } } // @@ -98,14 +106,14 @@ void PushTarget::sendInfluxDb2(float angle, float gravity, float corrGravity, // Create body for influxdb2 char buf[1024]; - snprintf( - &buf[0], sizeof(buf), - "measurement,host=%s,device=%s,temp-format=%c,gravity-format=%s " - "gravity=%.4f,corr-gravity=%.4f,angle=%.2f,temp=%.2f,battery=%.2f,rssi=%d\n", - // TODO: Add support for plato format - myConfig.getMDNS(), myConfig.getID(), myConfig.getTempFormat(), "SG", - myConfig.isGravityTempAdj() ? corrGravity : gravity, corrGravity, angle, - temp, myBatteryVoltage.getVoltage(), WiFi.RSSI()); + snprintf(&buf[0], sizeof(buf), + "measurement,host=%s,device=%s,temp-format=%c,gravity-format=%s " + "gravity=%.4f,corr-gravity=%.4f,angle=%.2f,temp=%.2f,battery=%.2f," + "rssi=%d\n", + // TODO: Add support for plato format + myConfig.getMDNS(), myConfig.getID(), myConfig.getTempFormat(), "SG", + myConfig.isGravityTempAdj() ? corrGravity : gravity, corrGravity, + angle, temp, myBatteryVoltage.getVoltage(), WiFi.RSSI()); #if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING) Log.verbose(F("PUSH: url %s." CR), serverPath.c_str()); @@ -197,16 +205,9 @@ void PushTarget::sendBrewfather(float angle, float gravity, float corrGravity, // // Send data to http target // -void PushTarget::sendHttp(String serverPath, float angle, float gravity, - float corrGravity, float temp, float runTime) { -#if !defined(PUSH_DISABLE_LOGGING) - Log.notice( - F("PUSH: Sending values to http angle=%F, gravity=%F, temp=%F." CR), - angle, gravity, temp); -#endif - - DynamicJsonDocument doc(256); - +void PushTarget::createIspindleFormat(DynamicJsonDocument &doc, float angle, + float gravity, float corrGravity, + float temp, float runTime) { // Use iSpindle format for compatibility doc["name"] = myConfig.getMDNS(); doc["ID"] = myConfig.getID(); @@ -225,6 +226,21 @@ void PushTarget::sendHttp(String serverPath, float angle, float gravity, // Some additional information doc["gravity-units"] = "SG"; doc["run-time"] = reduceFloatPrecision(runTime, 2); +} + +// +// Send data to http target +// +void PushTarget::sendHttp(String serverPath, float angle, float gravity, + float corrGravity, float temp, float runTime) { +#if !defined(PUSH_DISABLE_LOGGING) + Log.notice( + F("PUSH: Sending values to http angle=%F, gravity=%F, temp=%F." CR), + angle, gravity, temp); +#endif + + DynamicJsonDocument doc(256); + createIspindleFormat(doc, angle, gravity, corrGravity, temp, runTime); WiFiClient client; HTTPClient http; @@ -252,4 +268,43 @@ void PushTarget::sendHttp(String serverPath, float angle, float gravity, http.end(); } +// +// Send data to http target +// +void PushTarget::sendMqtt(float angle, float gravity, float corrGravity, + float temp, float runTime) { +#if !defined(PUSH_DISABLE_LOGGING) + Log.notice( + F("PUSH: Sending values to mqtt angle=%F, gravity=%F, temp=%F." CR), + angle, gravity, temp); +#endif + + DynamicJsonDocument doc(256); + createIspindleFormat(doc, angle, gravity, corrGravity, temp, runTime); + + WiFiClient client; + MQTTClient mqtt; + + mqtt.begin(myConfig.getMqttUrl(), client); + mqtt.connect(myConfig.getMDNS(), myConfig.getMqttUser(), + myConfig.getMqttPass()); + + String json; + serializeJson(doc, json); +#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING) + Log.verbose(F("PUSH: url %s." CR), serverPath.c_str()); + Log.verbose(F("PUSH: json %s." CR), json.c_str()); +#endif + + // Send MQQT message + mqtt.setTimeout(10); // 10 seconds timeout + if (mqtt.publish(myConfig.getMqttTopic(), json)) { + Log.notice(F("PUSH: MQTT publish successful" CR)); + } else { + Log.error(F("PUSH: MQTT publish failed" CR)); + } + + mqtt.disconnect(); +} + // EOF diff --git a/src/pushtarget.hpp b/src/pushtarget.hpp index 221e0eb..330fc8c 100644 --- a/src/pushtarget.hpp +++ b/src/pushtarget.hpp @@ -43,6 +43,11 @@ class PushTarget { float corrGravity, float temp, float runTime); void sendInfluxDb2(float angle, float gravity, float corrGravity, float temp, float runTime); + void sendMqtt(float angle, float gravity, float corrGravity, float temp, + float runTime); + void createIspindleFormat(DynamicJsonDocument &doc, float angle, + float gravity, float corrGravity, float temp, + float runTime); public: PushTarget() { ms = millis(); } diff --git a/src/webserver.cpp b/src/webserver.cpp index 923621a..dd60bb6 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -359,6 +359,10 @@ void WebServer::webHandleConfigPush() { server->arg(CFG_PARAM_PUSH_INFLUXDB2_BUCKET).c_str()); myConfig.setInfluxDb2PushToken( server->arg(CFG_PARAM_PUSH_INFLUXDB2_AUTH).c_str()); + myConfig.setMqttUrl(server->arg(CFG_PARAM_PUSH_MQTT).c_str()); + myConfig.setMqttTopic(server->arg(CFG_PARAM_PUSH_MQTT_TOPIC).c_str()); + myConfig.setMqttUser(server->arg(CFG_PARAM_PUSH_MQTT_USER).c_str()); + myConfig.setMqttPass(server->arg(CFG_PARAM_PUSH_MQTT_PASS).c_str()); myConfig.saveFile(); server->sendHeader("Location", "/config.htm#collapseTwo", true); server->send(302, "text/plain", "Push config updated"); diff --git a/test/config.json b/test/config.json index 38f0a6b..73b4278 100644 --- a/test/config.json +++ b/test/config.json @@ -10,6 +10,10 @@ "influxdb2-org": "hello", "influxdb2-bucket": "spann", "influxdb2-auth": "OijkU((jhfkh", + "mqtt-push": "192.168.1.10", + "mqtt-topic": "mytopic", + "mqtt-user": "user", + "mqtt-pass": "pass", "sleep-interval": 30, "voltage-factor": 1.59, "gravity-formula": "0.0*tilt^3+0.0*tilt^2+0.0017978*tilt+0.9436",