diff --git a/html/about.htm b/html/about.htm index 6b5137c..88218fa 100644 --- a/html/about.htm +++ b/html/about.htm @@ -5,74 +5,117 @@ Beer Gravity Monitor - - - + + - + - + -
-
-
-

Beer Gravity Monitor

- This is a piece of software for the iSpindle hardware and will work in a similar way. No part of this software is copied from the iSpindle project. -
-
-

MIT License

+
- Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - 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. +
+
+

+ +

+
+
+
+ Beer Gravity Monitor +
+
+ This is a piece of software for the iSpindle hardware and will work in a similar way. No part of this software is copied from the iSpindle project. +
+
+ MIT License +
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + 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. +
+ +
+ Credits to +
+
+ This software uses + the following libraries and without these this software would have been much more difficult to acheive:

+
    +
  • https://github.com/jrowberg/i2cdevlib
  • +
  • https://github.com/codeplea/tinyexpr
  • +
  • https://github.com/graphitemaster/incbin
  • +
  • https://github.com/khoih-prog/ESP_DoubleResetDetector
  • +
  • https://github.com/khoih-prog/ESP_WiFiManager
  • +
  • https://github.com/thijse/Arduino-Log
  • +
  • https://github.com/bblanchon/ArduinoJson
  • +
  • https://github.com/PaulStoffregen/OneWire
  • +
  • https://github.com/milesburton/Arduino-Temperature-Control-Library
  • +
  • https://github.com/Rotario
  • +
  • https://github.com/256dpi/arduino-mqtt
  • +
  • https://graphjs.com
  • +
  • https://getbootstrap.com
  • +
  • https://github.com/lorol/LITTLEFS
  • +
  • https://github.com/h2zero/NimBLE-Arduino
  • +
  • https://github.com/spouliot/tilt-sim
  • +
+
+
+
+
-
-
+ - - -
(C) Copyright 2021-22 Magnus Persson
+
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/html/about.min.htm b/html/about.min.htm index 36cdb1a..d31775b 100644 --- a/html/about.min.htm +++ b/html/about.min.htm @@ -1 +1 @@ -Beer Gravity Monitor

Beer Gravity Monitor

This is a piece of software for the iSpindle hardware and will work in a similar way. No part of this software is copied from the iSpindle project.

MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 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.

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

Beer Gravity Monitor
This is a piece of software for the iSpindle hardware and will work in a similar way. No part of this software is copied from the iSpindle project.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 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.
Credits to
This software uses the following libraries and without these this software would have been much more difficult to acheive:

  • https://github.com/jrowberg/i2cdevlib
  • https://github.com/codeplea/tinyexpr
  • https://github.com/graphitemaster/incbin
  • https://github.com/khoih-prog/ESP_DoubleResetDetector
  • https://github.com/khoih-prog/ESP_WiFiManager
  • https://github.com/thijse/Arduino-Log
  • https://github.com/bblanchon/ArduinoJson
  • https://github.com/PaulStoffregen/OneWire
  • https://github.com/milesburton/Arduino-Temperature-Control-Library
  • https://github.com/Rotario
  • https://github.com/256dpi/arduino-mqtt
  • https://graphjs.com
  • https://getbootstrap.com
  • https://github.com/lorol/LITTLEFS
  • https://github.com/h2zero/NimBLE-Arduino
  • https://github.com/spouliot/tilt-sim
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/html/calibration.htm b/html/calibration.htm index fc59cf8..a82e3e9 100644 --- a/html/calibration.htm +++ b/html/calibration.htm @@ -5,379 +5,422 @@ Beer Gravity Monitor - - - - + + - + + + - + - + -
+
+ -
+ - function showSuccess( msg ) { - $('.alert').addClass('alert-success').removeClass('alert-danger').removeClass('d-none').addClass('show') - $('#alert-msg').text( msg ); - } - - $("#alert-btn").click(function(e){ - $('.alert').addClass('d-none').removeClass('show') - }); - - -
- -
-
-

-

-
+
+
+
+ + -
-
- - - +
+ Here you can create your gravity formula by entering angles/tilt and the corresponding gravity. These values + will be saved for future use. Angles with 0 (zero) will be skipped. The values below will be used to check the + formula and if the deviation is more than 1.5SG / 0.38P on any of the provided points then the forumla will be + rejected. On the bottom of the page you can see a graph over the entered values + values calcualated by the formula. +
-
- Here you can create your gravity formula by entering angles/tilt and the corresponding gravity. These values - will be saved for future use. Angles with 0 (zero) will be skipped. The values below will be used to check the - formula and if the deviation is more than 1.5SG / 0.38P on any of the provided points then the forumla will be - rejected. On the bottom of the page you can see a graph over the entered values + values calcualated by the formula. -
+
+ + + +
-
- - - -
+
+ +
+
+
-
- -
- +
+ +
+
-
- -
-
-
- -
- +
+ +
+
-
- -
-
-
- -
- +
+ +
+
-
- -
-
-
- -
- +
+ +
+
-
- -
-
-
- -
- +
+ +
+
-
- -
-
-
-
- +
+ +
+
-
-
- - -
-
+
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+
+ +
+ +
+ + +
+
- -
- + +
+ +
+
+
+ +
+

+ +

+
+
+ +
-
-
- -
-
-
- + var myChart = 0; + - + } - + function populateChartForm(a, g) { + if( a != 0) + chartDataForm.push( { x: parseFloat(a), y: parseFloat(g) }); -
(C) Copyright 2021-22 Magnus Persson
+ chartDataForm.sort(function (a, b) { + return a.x - b.x; + }); + } + + function populateChartCalc(a, g) { + chartDataCalc.push( { x: parseFloat(a), y: parseFloat(g) }); + } + + function isPlato() { + return $("#gravity-format").text() == "P"; + } + + function populateChart() { + + chartDataCalc.length = 0 + + for( i = 25.0; i<80.0; i+=5.0) { + var formula = $("#formula").text(); + var angle = i.toString(); + formula=formula.replaceAll( "tilt^3", angle+"*"+angle+"*"+angle ); + formula=formula.replaceAll( "tilt^2", angle+"*"+angle ); + formula=formula.replaceAll( "tilt", angle ); + var g = eval( formula ); + + if(isPlato()) + g = convertToPlato(g); + + populateChartCalc( i, g ); + } + + chartDataForm.length = 0 + populateChartForm( $("#a1").val(), $("#g1").val() ); + populateChartForm( $("#a2").val(), $("#g2").val() ); + populateChartForm( $("#a3").val(), $("#g3").val() ); + populateChartForm( $("#a4").val(), $("#g4").val() ); + populateChartForm( $("#a5").val(), $("#g5").val() ); + populateChartForm( $("#a6").val(), $("#g6").val() ); + populateChartForm( $("#a7").val(), $("#g7").val() ); + populateChartForm( $("#a8").val(), $("#g8").val() ); + populateChartForm( $("#a9").val(), $("#g9").val() ); + populateChartForm( $("#a10").val(), $("#g10").val() ); + + if( myChart ) + myChart.destroy(); + + myChart = new Chart( + document.getElementById('gravityChart'), + configChart + ); + } + + function setButtonDisabled( b ) { + $("#calculate-btn").prop("disabled", b); + } + + // Get the configuration values from the API + function getConfig() { + setButtonDisabled( true ); + + var url = "/api/formula"; + //var url = "/test/formula.json"; + $('#spinner').show(); + $.getJSON(url, function (cfg) { + console.log( cfg ); + + $("#id").val(cfg["id"]); + $("#angle").text(cfg["angle"]); + $("#formula").text(cfg["gravity-formula"]); + $("#gravity-format").text(cfg["gravity-format"]); // Sets the variable used by isPlato() + + if(isPlato()) { + $("#gravity-header").text("Gravity (Plato):"); + $("#g1").val( parseFloat(cfg["g1"]).toFixed(1) ); + $("#g2").val( parseFloat(cfg["g2"]).toFixed(1) ); + $("#g3").val( parseFloat(cfg["g3"]).toFixed(1) ); + $("#g4").val( parseFloat(cfg["g4"]).toFixed(1) ); + $("#g5").val( parseFloat(cfg["g5"]).toFixed(1) ); + $("#g6").val( parseFloat(cfg["g6"]).toFixed(1) ); + $("#g7").val( parseFloat(cfg["g7"]).toFixed(1) ); + $("#g8").val( parseFloat(cfg["g8"]).toFixed(1) ); + $("#g9").val( parseFloat(cfg["g9"]).toFixed(1) ); + $("#g10").val( parseFloat(cfg["g10"]).toFixed(1) ); + } else { + $("#gravity-header").text("Gravity (SG):"); + $("#g1").val( parseFloat(cfg["g1"]).toFixed(4) ); + $("#g2").val( parseFloat(cfg["g2"]).toFixed(4) ); + $("#g3").val( parseFloat(cfg["g3"]).toFixed(4) ); + $("#g4").val( parseFloat(cfg["g4"]).toFixed(4) ); + $("#g5").val( parseFloat(cfg["g5"]).toFixed(4) ); + $("#g6").val( parseFloat(cfg["g6"]).toFixed(4) ); + $("#g7").val( parseFloat(cfg["g7"]).toFixed(4) ); + $("#g8").val( parseFloat(cfg["g8"]).toFixed(4) ); + $("#g9").val( parseFloat(cfg["g9"]).toFixed(4) ); + $("#g10").val( parseFloat(cfg["g10"]).toFixed(4) ); + } + + $("#a1").val( parseFloat(cfg["a1"]).toFixed(2) ); + $("#a2").val( parseFloat(cfg["a2"]).toFixed(2) ); + $("#a3").val( parseFloat(cfg["a3"]).toFixed(2) ); + $("#a4").val( parseFloat(cfg["a4"]).toFixed(2) ); + $("#a5").val( parseFloat(cfg["a5"]).toFixed(2) ); + $("#a6").val( parseFloat(cfg["a6"]).toFixed(2) ); + $("#a7").val( parseFloat(cfg["a7"]).toFixed(2) ); + $("#a8").val( parseFloat(cfg["a8"]).toFixed(2) ); + $("#a9").val( parseFloat(cfg["a9"]).toFixed(2) ); + $("#a10").val( parseFloat(cfg["a10"]).toFixed(2) ); + + if( cfg["error"]!="" ) { + showError(cfg["error"]); + } + + populateChart(); + }) + .fail(function () { + showError('Unable to get data from the device.'); + }) + .always(function() { + $('#spinner').hide(); + setButtonDisabled( false ); + }); + } + + + + +
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/html/calibration.min.htm b/html/calibration.min.htm index dfe3c24..3e3454b 100644 --- a/html/calibration.min.htm +++ b/html/calibration.min.htm @@ -1,52 +1,52 @@ -Beer Gravity Monitor

Here you can create your gravity formula by entering angles/tilt and the corresponding gravity. These values will be saved for future use. Angles with 0 (zero) will be skipped. The values below will be used to check the formula and if the deviation is more than 1.5SG / 0.38P on any of the provided points then the forumla will be rejected. On the bottom of the page you can see a graph over the entered values + values calcualated by the formula.


Here you can create your gravity formula by entering angles/tilt and the corresponding gravity. These values will be saved for future use. Angles with 0 (zero) will be skipped. The values below will be used to check the formula and if the deviation is more than 1.5SG / 0.38P on any of the provided points then the forumla will be rejected. On the bottom of the page you can see a graph over the entered values + values calcualated by the formula.

(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file + var myChart = 0;
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/html/config.htm b/html/config.htm index c4ef28e..e81a462 100644 --- a/html/config.htm +++ b/html/config.htm @@ -5,150 +5,151 @@ Beer Gravity Monitor - - - + + - + + - + - + -
+
-
+ - + - + - + + function showWarningSleep() { + $('#warning-sleep').removeClass('d-none').addClass('show').removeClass('hide'); + } + function hideWarningSleep() { + $('#warning-sleep').addClass('d-none').removeClass('show').addClass('hide'); + } + function showWarningGyro() { + $('#warning-gyro').removeClass('d-none').addClass('show').removeClass('hide'); + } + function hideWarningGyro() { + $('#warning-gyro').addClass('d-none').removeClass('show').addClass('hide'); + } +
- +
+

+ +

+
+
-
-
-

- -

-
-
-
-
- -
- + +
+ +
+
-
- Temperature Format: -
-
- - + +
+
+ Temperature Format: +
+
+ + +
+
+ + +
-
- - -
-
-
-
- + +
+ +
+
- +
-
-
- + +
+
+

-
- - - -
- +
+ + + +
+
@@ -156,16 +157,14 @@
-
-
-

- -

-
-
-
+
+

+ +

+
+
@@ -174,68 +173,58 @@ -
+
- +
- +
-
+ +
- +
- +
-
+
- +
-
- -
+
- +
-
+
- +
-
- -
- -
- -
-
- -
+
- +
-
+
- - + +
@@ -243,83 +232,88 @@
-
-
-

- -

-
-
-
+
+

+ +

+
+
+
-
+
-
- +
+
-
+ +
- +
-
+ +
- +
-
+ +
- +

-
+
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- +
-
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
- +
-
+
- - + +
@@ -327,54 +321,53 @@
-
-
-

- -

-
-
-
+
+

+ +

+
+
+
-
- Gravity Format: -
-
- - + +
+
+ Gravity Format: +
+
+ + +
+
+ + +
-
- - -
-
-
-
- + +
+ +
+
- +
-
+ +
- - + +
-
+ +
- +
@@ -382,45 +375,46 @@
-
-
-

- -

-
-
-
+
+

+ +

+
+
+
-
+ +
- +
-
+ +
- +
-
+ +
- - + +
-
+ +
- @@ -433,258 +427,399 @@
-
+ +
-
- +
+
-
+ +
- +
-
+ +
- +
+
+
+

+ +

+
+
+ +
+ + +
+ +
+ +
+
(10-100) - default 50
+
+ +
+ +
+ +
+
(50-1000) - default 500
+
+ +
+ +
+ +
+
(1 - 10) - default 1.6 SG
+
+ +
+ +
+ +
+ +
+
(1 - 60) - default 20 s
+
+ +
+ +
+ +
+
(10 - 240) - default 120 s
+
+ +
+ +
+ +
+ +
+
(0 - 5) - default 0
+
+ +
+ +
+ +
+
(0 - 5) - default 0
+
+ +
+ +
+ +
+
(0 - 5) - default 0
+
+ +
+ +
+ +
+
(0 - 5) - default 0
+
+ +
+ +
+ +
+
(0 - 5) - default 0
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+ + -
-
- - - - - - - // Trigger the calibration and show warnings if needed - $("#sleep-interval").keyup(updateSleepInfo); - $("#gyro-temp").change(updateSleepInfo); + - + // Trigger the calibration + $("#calibrate-btn").click(function(e){ + console.log( "Calibrating..." ); + $.ajax( { + type: "POST", + url: "/api/calibrate", + data: { id: $("#id1").val() }, + success: function(result) { showSuccess('Calibration of device was completed successfully.'); getConfig(); }, + error: function(result) { showError('Unable to calibrate device.'); } + } ); + }); -
(C) Copyright 2021-22 Magnus Persson
+ $("#firmware-btn").click(function(e){ + window.location.href = "/firmware.htm"; + }); + + // Open the format editor + $("#format-btn").click(function(e){ + window.location.href = "/format.htm"; + }); + + // Open the format editor + $("#format-btn2").click(function(e){ + window.location.href = "/format.htm"; + }); + + $("#test-btn").click(function(e){ + window.location.href = "/test.htm"; + }); + + $("#test-btn2").click(function(e){ + window.location.href = "/test.htm"; + }); + + function estimateBatteryLife(interval, rt) { + // ESP8266 consumes between 140-170mA when WIFI is on. Deep sleep is 20uA. + // MPU-6050 consumes 4mA + // DS18B20 consumes 1mA + // For this estimation we use an average of 160mA + + var pwrActive = 170; // mA per hour + var pwrSleep = 15; // mA per day + var batt = 2000; // mA + + if(rt<1) rt = 2; + + // The deep sleep will consume approx 1mA per day. + var powerPerDay = (24*3600)/(interval+rt)*(rt/3600)*pwrActive + pwrSleep; + return batt/powerPerDay; + } + + function updateSleepInfo() { + var i = parseInt($("#sleep-interval").val()); + var rt = parseInt($("#runtime-average").val()); + var j = 0; + + if( rt>0 ) + j = estimateBatteryLife(i, rt); + + var t1 = Math.floor(i/60) + " m " + (i%60) + " s"; + var t2 = Math.floor(j/7) + " weeks " + Math.floor(j%7) + " days"; + + if( j ) + $("#sleep-interval-info").text( t1 + " - Estimated runtime: " + t2); + else + $("#sleep-interval-info").text( t1 ); + + hideWarningGyro(); + if(i>0 && i<300) { + if( $("#gyro-temp").is(":checked") ) + showWarningGyro(); + showWarningSleep(); + } else { + hideWarningSleep(); + } + } + + // Trigger the calibration and show warnings if needed + $("#sleep-interval").keyup(updateSleepInfo); + $("#gyro-temp").change(updateSleepInfo); + + function setButtonDisabled( b ) { + $("#device-btn").prop("disabled", b); + $("#calibrate-btn").prop("disabled", b); + $("#push-btn").prop("disabled", b); + $("#format-btn").prop("disabled", b); + $("#test-btn").prop("disabled", b); + $("#gravity-btn").prop("disabled", b); + $("#hardware-btn").prop("disabled", b); + $("#push-btn2").prop("disabled", b); + $("#format-btn2").prop("disabled", b); + $("#test-btn2").prop("disabled", b); + $("#advanced-btn").prop("disabled", b); + } + + // Get the advanced values from the API + function getAdvancedConfig() { + setButtonDisabled( true ); + + var url = "/api/config/advanced"; + //var url = "/test/adv.json"; + $('#spinner').show(); + $.getJSON(url, function (cfg) { + console.log( cfg ); + + $("#gyro-read-count").val(cfg["gyro-read-count"]); + $("#gyro-moving-threashold").val(cfg["gyro-moving-threashold"]); + $("#formula-max-deviation").val(cfg["formula-max-deviation"]); + $("#wifi-portal-timeout").val(cfg["wifi-portal-timeout"]); + $("#wifi-connect-timeout").val(cfg["wifi-connect-timeout"]); + $("#int-http1").val(cfg["int-http1"]); + $("#int-http2").val(cfg["int-http2"]); + $("#int-http3").val(cfg["int-http3"]); + $("#int-influx").val(cfg["int-influx"]); + $("#int-mqtt").val(cfg["int-mqtt"]); + }) + .fail(function () { + showError('Unable to get data from the device.'); + }) + .always(function() { + $('#spinner').hide(); + setButtonDisabled( false ); + }); + } + + // Get the configuration values from the API + function getConfig() { + setButtonDisabled( true ); + + var url = "/api/config"; + //var url = "/test/config.json"; + $('#spinner').show(); + $.getJSON(url, function (cfg) { + console.log( cfg ); + + if(cfg["platform"]=="esp32") { + $('#ble').prop('disabled', false); + $("#ble").val(cfg["ble"]); + } + + $("#id1").val(cfg["id"]); + $("#id2").val(cfg["id"]); + $("#id3").val(cfg["id"]); + $("#id4").val(cfg["id"]); + $("#id5").val(cfg["id"]); + $("#id6").val(cfg["id"]); + $("#mdns").val(cfg["mdns"]); + if( cfg["temp-format"] == "C" ) $("#temp-format-c").click(); + else $("#temp-format-f").click(); + if( cfg["gravity-format"] == "G" ) $("#gravity-format-g").click(); + else $("#gravity-format-p").click(); + $("#ota-url").val(cfg["ota-url"]); + $("#token").val(cfg["token"]); + $("#token2").val(cfg["token2"]); + $("#http-push").val(cfg["http-push"]); + $("#http-push-h1").val(cfg["http-push-h1"]); + $("#http-push-h2").val(cfg["http-push-h2"]); + $("#http-push2").val(cfg["http-push2"]); + $("#http-push2-h1").val(cfg["http-push2-h1"]); + $("#http-push2-h2").val(cfg["http-push2-h2"]); + $("#http-push3").val(cfg["http-push3"]); + $("#influxdb2-push").val(cfg["influxdb2-push"]); + $("#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-port").val(cfg["mqtt-port"]); + $("#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"]); + $("#temp-adjustment-value").val(cfg["temp-adjustment-value"]); + $("#gravity-temp-adjustment").prop( "checked", cfg["gravity-temp-adjustment"] ); + $("#gyro-temp").prop( "checked", cfg["gyro-temp"] ); + $("#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"]); + $("#runtime-average").val(cfg["runtime-average"]); + //$("#gravity").text(cfg["gravity"] + " SG"); + }) + .fail(function () { + showError('Unable to get data from the device.'); + }) + .always(function() { + //$('#spinner').hide(); + //setButtonDisabled( false ); + updateSleepInfo(); + getAdvancedConfig(); + }); + } + + + + +
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/html/config.min.htm b/html/config.min.htm index 799f564..82a3e60 100644 --- a/html/config.min.htm +++ b/html/config.min.htm @@ -1 +1 @@ -Beer Gravity Monitor

Temperature Format:




Gravity Format:


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

Temperature Format:


Gravity Format:

(10-100) - default 50
(50-1000) - default 500
(1 - 10) - default 1.6 SG

(1 - 60) - default 20 s
(10 - 240) - default 120 s

(0 - 5) - default 0
(0 - 5) - default 0
(0 - 5) - default 0
(0 - 5) - default 0
(0 - 5) - default 0
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/html/firmware.htm b/html/firmware.htm index 5d68c1c..48eff24 100644 --- a/html/firmware.htm +++ b/html/firmware.htm @@ -5,184 +5,191 @@ Beer Gravity Monitor - - - + + - - + + - + - + -
+
-
+ - + - $("#alert-btn").click(function(e){ - $('.alert').addClass('d-none').removeClass('show') - }); - +
+
+

+ +

+
+
-
-
Here you can upload a new firmware version, it will not check the version number so you can also downgrade the firmware here. +
+
Here you can upload a new firmware version, it will not check the version number so you can also downgrade the firmware here. +
+
+ +
+
Current version:
+
Loading...
+
+ +
+
Platform:
+
Loading...
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+ +
+
-
-
Current version:
-
Loading...
-
-
-
Platform:
-
Loading...
-
- -
- -
-
- - -
- -
-
- -
-
-
- -
-
- - + function start() { + setInterval(getStatus, 3000); + } + - + -
(C) Copyright 2021-22 Magnus Persson
+
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/html/firmware.min.htm b/html/firmware.min.htm index 5bd8ab8..f6f9b05 100644 --- a/html/firmware.min.htm +++ b/html/firmware.min.htm @@ -1,87 +1,79 @@ -Beer Gravity Monitor

Here you can upload a new firmware version, it will not check the version number so you can also downgrade the firmware here.
Current version:
Loading...
Platform:
Loading...

Here you can upload a new firmware version, it will not check the version number so you can also downgrade the firmware here.
Current version:
Loading...
Platform:
Loading...
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file + function start() { + setInterval(getStatus, 3000); + }
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/html/format.htm b/html/format.htm index 4561a76..86e4055 100644 --- a/html/format.htm +++ b/html/format.htm @@ -5,264 +5,302 @@ Beer Gravity Monitor - - - + + - + + - + - + -
+
-
+ - + - $("#alert-btn").click(function(e){ - $('#alert').addClass('d-none').removeClass('show'); - }); - - -
- -
-
-

-

-
-
-
+
+
+ - -
+
- -
- -
+ +
-
-
-
- - + + +
+
+ + +
+ +
+ +
+
+
 
+
+          
+
-
-
+ - + $("#push-target").change(function(e){ + console.log(e) + selectFormat(); + }); -
(C) Copyright 2021-22 Magnus Persson
+ // Copy the selected template + $("#copy-btn").click(function(e) { + var id = $("#predefined").val(); + + //console.log( encodeURIComponent( $("#format").val() ) ); + + formatTemplates.forEach(function (item, index) { + if( item.id == id ) { + $("#format").val( decodeURIComponent(item.format) ); + } + }); + }); + + // Clear the selected template + $("#clear-btn").click(function(e) { + $("#format").val( "" ); + }); + + // Store the format + $("#format-btn").click(function(e) { + var s = $("#format").val(); + s = s.replaceAll("\n", ""); + var obj = 'id=' + $("#id").val() + '&' + $("#push-target").val() + '=' + encodeURIComponent(s); + console.log(obj); + + $.ajax( { + type: "POST", + url: "/api/config/format", + data: obj, + success: function(result) { showSuccess('Format stored successfully.'); getConfig(); }, + error: function(result) { showError('Unable to store format.'); } + } ); + }); + + // Test the calibration + $("#test-btn").click(function(e) { + var url = "/api/status"; + //var url = "/test/status.json"; + $('#spinner').show(); + $.getJSON(url, function (cfg) { + console.log( cfg ); + + var doc = $("#format").val(); + + if (cfg["temp-format"]=="C") + doc = doc.replaceAll("${temp}", cfg["temp-c"]); + else + doc = doc.replaceAll("${temp}", cfg["temp-f"]); + + if (cfg["gravity-format"]=="G") { + var sg = cfg["gravity"]; + doc = doc.replaceAll("${gravity-sg}", sg); + doc = doc.replaceAll("${corr-gravity-sg}", sg); + var plato = 259 - (259 - sg); + doc = doc.replaceAll("${gravity-plato}", plato); + doc = doc.replaceAll("${corr-gravity-plato}", plato); + } + else { + var plato = cfg["gravity"]; + doc = doc.replaceAll("${gravity-plato}", plato); + doc = doc.replaceAll("${corr-gravity-plato}", plato); + var sg = 259 / (259 - plato); + doc = doc.replaceAll("${gravity-sg}", sg); + doc = doc.replaceAll("${corr-gravity-sg}", sg); + } + + doc = doc.replaceAll("${mdns}", cfg["mdns"]); + doc = doc.replaceAll("${id}", cfg["id"]); + doc = doc.replaceAll("${sleep-interval}", cfg["sleep-interval"]); + doc = doc.replaceAll("${token}", cfg["token"]); + doc = doc.replaceAll("${token2}", cfg["token2"]); + doc = doc.replaceAll("${temp-c}", cfg["temp-c"]); + doc = doc.replaceAll("${temp-f}", cfg["temp-f"]); + doc = doc.replaceAll("${temp-unit}", cfg["temp-format"]); + doc = doc.replaceAll("${battery}", cfg["battery"]); + doc = doc.replaceAll("${rssi}", cfg["rssi"]); + doc = doc.replaceAll("${run-time}", cfg["runtime-average"]); + doc = doc.replaceAll("${gravity}", cfg["gravity"]); + doc = doc.replaceAll("${gravity-unit}", cfg["gravity-format"]); + doc = doc.replaceAll("${corr-gravity}", cfg["gravity"]); + doc = doc.replaceAll("${angle}", cfg["angle"]); + doc = doc.replaceAll("${tilt}", cfg["angle"]); + + // Format in a readable json string. + try { + var json = JSON.parse(doc); + doc = JSON.stringify(json, null, 2); + } catch(e) { + console.log("Not a javascript object!") + } + + $("#preview").text(doc); + + }) + .fail(function () { + showError('Unable to get data from the device.'); + }) + .always(function() { + $('#spinner').hide(); + }); + }); + + function setButtonDisabled( b ) { + $("#format-btn").prop("disabled", b); + $("#test-btn").prop("disabled", b); + } + + function selectFormat() { + var s = "#" + $("#push-target").val() + console.log(s); + s = decodeURIComponent($(s).val()); + console.log(s); + s = s.replaceAll("|", "|\n"); + console.log(s); + $("#format").val(s); + $("#preview").text(""); + } + + // Get the configuration values from the API + function getConfig() { + setButtonDisabled( true ); + + var url = "/api/config/format"; + //var url = "/test/format.json"; + $('#spinner').show(); + $.getJSON(url, function (cfg) { + console.log( cfg ); + $("#id").val(cfg["id"]); + + $("#http-1").val(cfg["http-1"]); + $("#http-2").val(cfg["http-2"]); + $("#http-3").val(cfg["http-3"]); + $("#influxdb").val(cfg["influxdb"]); + $("#mqtt").val(cfg["mqtt"]); + selectFormat(); + }) + .fail(function () { + showError('Unable to get data from the device.'); + }) + .always(function() { + $('#spinner').hide(); + setButtonDisabled( false ); + }); + } + + + + +
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/html/format.min.htm b/html/format.min.htm index 6c06b7e..5c0697f 100644 --- a/html/format.min.htm +++ b/html/format.min.htm @@ -1,2 +1,9 @@ -Beer Gravity Monitor


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


(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/html/index.htm b/html/index.htm index fd92efc..e348d72 100644 --- a/html/index.htm +++ b/html/index.htm @@ -5,237 +5,258 @@ Beer Gravity Monitor - - - + + - + + - + - +
-
+ -
+ + +
+
+

+ +

+
+
+
+
Current version:
+
Loading...
+
+ +
+
Host name:
+
Loading...
+
+
+
Device ID:
+
Loading...
+
+
+
Platform:
+
Loading...
+
+ + + + + +
+
+
+
+
+ +
+
+
+ +

+ +

+
+
+ +
+
Gravity:
+
Loading...
+
+
+
Temperature:
+
Loading...
+
+
+
Angle/Tilt:
+
Loading...
+
+
+
Battery:
+
Loading...
+
+ +
+
Average runtime:
+
Loading...
+
+ +
+
+ + +
+
+ +
+
+
-
-
Current version:
-
Loading...
-
- -
-
Host name:
-
Loading...
-
-
-
Device ID:
-
Loading...
-
-
-
Platform:
-
Loading...
-
+ - - - - -
-
-
-
-
- -
- -
-
Gravity:
-
Loading...
-
-
-
Temperature:
-
Loading...
-
-
-
Angle/Tilt:
-
Loading...
-
-
-
Battery:
-
Loading...
-
- -
-
Average runtime:
-
Loading...
-
- -
-
- - -
-
- -
- -
- - - - - -
(C) Copyright 2021-22 Magnus Persson
+
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/html/index.min.htm b/html/index.min.htm index b80fa87..139ad7e 100644 --- a/html/index.min.htm +++ b/html/index.min.htm @@ -1 +1 @@ -Beer Gravity Monitor

Current version:
Loading...
Host name:
Loading...
Device ID:
Loading...
Platform:
Loading...

Gravity:
Loading...
Temperature:
Loading...
Angle/Tilt:
Loading...
Battery:
Loading...
Average runtime:
Loading...

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

Current version:
Loading...
Host name:
Loading...
Device ID:
Loading...
Platform:
Loading...

Gravity:
Loading...
Temperature:
Loading...
Angle/Tilt:
Loading...
Battery:
Loading...
Average runtime:
Loading...
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/html/test.htm b/html/test.htm index e05e6cd..4d10371 100644 --- a/html/test.htm +++ b/html/test.htm @@ -5,213 +5,219 @@ Beer Gravity Monitor - - - + + - + + - + - + -
+
-
+ - -
-
-
-
 
-    
-
+ -
-
- -
-
- -
-
- -
-
- - - - - -
(C) Copyright 2021-22 Magnus Persson
+
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/html/test.min.htm b/html/test.min.htm index 3d6ee27..a5a89a5 100644 --- a/html/test.min.htm +++ b/html/test.min.htm @@ -1 +1 @@ -Beer Gravity Monitor


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

Press test button to start testing all defined push targets.
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/html/upload.htm b/html/upload.htm index d06eb25..db63b5a 100644 --- a/html/upload.htm +++ b/html/upload.htm @@ -5,160 +5,175 @@ Beer Gravity Monitor - - - + + - + + - + - + -
+
-
+ - -
-
The listed files below needs to be uploaded to the FileSystem in order for the GUI to work. - You can also flash the LittleFS filesystem but in that case you will loose your device settings. An OTA upgrade will automatically download - the files if they are found in the same location as the firmware.bin. This page is a fallback option. -
-

Once all the files are confirmed, please reboot the device for normal operation.
-
+ -
-
index.min.htm
-
Checking...
-
-
-
config.min.htm
-
Checking...
-
-
-
calibration.min.htm
-
Checking...
-
-
-
format.min.htm
-
Checking...
-
-
-
test.min.htm
-
Checking...
-
-
-
about.min.htm
-
Checking...
-
- -
-
-
- - -
- -
-
- -
-
- - - - - -
(C) Copyright 2021-22 Magnus Persson
+
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/html/upload.min.htm b/html/upload.min.htm index 08f7198..d197c8a 100644 --- a/html/upload.min.htm +++ b/html/upload.min.htm @@ -1 +1 @@ -Beer Gravity Monitor

The listed files below needs to be uploaded to the FileSystem in order for the GUI to work. You can also flash the LittleFS filesystem but in that case you will loose your device settings. An OTA upgrade will automatically download the files if they are found in the same location as the firmware.bin. This page is a fallback option.

Once all the files are confirmed, please reboot the device for normal operation.
index.min.htm
Checking...
config.min.htm
Checking...
calibration.min.htm
Checking...
format.min.htm
Checking...
test.min.htm
Checking...
about.min.htm
Checking...

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

The listed files below needs to be uploaded to the FileSystem in order for the GUI to work. You can also flash the LittleFS filesystem but in that case you will loose your device settings. An OTA upgrade will automatically download the files if they are found in the same location as the firmware.bin. This page is a fallback option.

Once all the files are confirmed, please reboot the device for normal operation.
index.min.htm
Checking...
config.min.htm
Checking...
calibration.min.htm
Checking...
format.min.htm
Checking...
test.min.htm
Checking...
about.min.htm
Checking...
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 4fb7d33..a4bb2fd 100644 --- a/platformio.ini +++ b/platformio.ini @@ -33,7 +33,7 @@ build_flags = -D EMBED_HTML # If this is not used the html files needs to be on the file system (can be uploaded) -D USER_SSID=\""\"" # =\""myssid\"" -D USER_SSID_PWD=\""\"" # =\""mypwd\"" - -D CFG_APPVER="\"0.9.0\"" + -D CFG_APPVER="\"1.0.0\"" !python script/git_rev.py lib_deps = # Switched to forks for better version control. # Using local copy of these libraries @@ -66,6 +66,7 @@ build_flags = #-D PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS #-D SKIP_SLEEPMODE #-D DOUBLERESETDETECTOR_DEBUG=true + #-D FORCE_GRAVITY_MODE # used to debug gravity mode -D COLLECT_PERFDATA # This option will collect runtime data for a few defined methods to measure time, dumped to serial and/or influxdb -D LOG_LEVEL=6 # Maximum log level for the debug build. -D CFG_DISABLE_LOGGING # Turn off verbose/notice logging to reduce size and dont overload uart. @@ -126,7 +127,7 @@ board_build.filesystem = littlefs [env:gravity32-release] framework = arduino -platform = espressif32 +platform = espressif32 @ 3.5.0 upload_speed = ${common_env_data.upload_speed} monitor_speed = ${common_env_data.monitor_speed} extra_scripts = @@ -161,7 +162,7 @@ monitor_filters = esp32_exception_decoder [env:gravity32-perf] framework = arduino -platform = espressif32 +platform = espressif32 @ 3.5.0 upload_speed = ${common_env_data.upload_speed} monitor_speed = ${common_env_data.monitor_speed} extra_scripts = diff --git a/src/calc.cpp b/src/calc.cpp index 4624cf8..eac7985 100644 --- a/src/calc.cpp +++ b/src/calc.cpp @@ -33,14 +33,16 @@ SOFTWARE. int createFormula(RawFormulaData &fd, char *formulaBuffer, int formulaBufferSize, int order) { int noAngles = 0; + RawFormulaData fd2; - // Check how many valid values we have got - if (fd.a[0] > 0 && fd.a[1] > 0 && fd.a[2] > 0 && fd.a[3] > 0 && fd.a[4] > 0) - noAngles = 5; - else if (fd.a[0] > 0 && fd.a[1] > 0 && fd.a[2] > 0 && fd.a[3] > 0) - noAngles = 4; - else if (fd.a[0] > 0 && fd.a[1] > 0 && fd.a[2] > 0) - noAngles = 3; + // Check how many valid values we have got and make sure we have a full series. + for (int i = 0; i < FORMULA_DATA_SIZE; i++) { + if (fd.a[i]) { + fd2.a[noAngles] = fd.a[i]; + fd2.g[noAngles] = fd.g[i]; + noAngles++; + } + } #if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) Log.verbose( @@ -48,19 +50,19 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer, order, noAngles); #endif - if (!noAngles) { + if (noAngles <3) { ErrorFileLog errLog; errLog.addEntry(F("CALC: Not enough values for deriving formula")); return ERR_FORMULA_NOTENOUGHVALUES; } else { double coeffs[order + 1]; - int ret = fitCurve(order, noAngles, fd.a, fd.g, + int ret = fitCurve(order, noAngles, fd2.a, fd2.g, sizeof(coeffs) / sizeof(double), coeffs); // Returned value is 0 if no error if (ret == 0) { #if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) - Log.verbose(F("CALC: Finshied processing data points." CR)); + Log.verbose(F("CALC: Finshied processing data points, order = %d." CR), order); #endif // Print the formula based on 'order' @@ -93,12 +95,10 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer, double dev = (g - fd.g[i]) < 0 ? (fd.g[i] - g) : (g - fd.g[i]); // If the deviation is more than 2 degress we mark it as failed. - if (dev * 1000 > myHardwareConfig.getMaxFormulaCreationDeviation()) { -#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) + if (dev * 1000 > myAdvancedConfig.getMaxFormulaCreationDeviation()) { char s[20]; snprintf(&s[0], sizeof(s), "%.8f", dev); - Log.verbose(F("CALC: Deviation is: %s" CR), &s[0]); -#endif + Log.error(F("CALC: Deviation to large: %s" CR), &s[0]); valid = false; } } diff --git a/src/calc.hpp b/src/calc.hpp index fa19c3b..19bd786 100644 --- a/src/calc.hpp +++ b/src/calc.hpp @@ -34,7 +34,7 @@ double calculateGravity(double angle, double tempC, const char *tempFormula = 0); double gravityTemperatureCorrectionC( double gravity, double tempC, - double calTempC = myHardwareConfig.getDefaultCalibrationTemp()); + double calTempC = myAdvancedConfig.getDefaultCalibrationTemp()); int createFormula(RawFormulaData &fd, char *formulaBuffer, int formulaBufferSize, int order); diff --git a/src/config.cpp b/src/config.cpp index 1f66db3..b21e768 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -26,7 +26,7 @@ SOFTWARE. #include Config myConfig; -HardwareConfig myHardwareConfig; +AdvancedConfig myAdvancedConfig; // // Create the config class with default settings. @@ -66,7 +66,6 @@ void Config::createJson(DynamicJsonDocument& doc) { doc[PARAM_PASS] = getWifiPass(); doc[PARAM_BLE] = getColorBLE(); doc[PARAM_TEMPFORMAT] = String(getTempFormat()); - doc[PARAM_PUSH_BREWFATHER] = getBrewfatherPushUrl(); doc[PARAM_TOKEN] = getToken(); doc[PARAM_TOKEN2] = getToken2(); doc[PARAM_PUSH_HTTP] = getHttpUrl(); @@ -106,12 +105,22 @@ void Config::createJson(DynamicJsonDocument& doc) { cal2["a3"] = reduceFloatPrecision(_formulaData.a[2], 2); cal2["a4"] = reduceFloatPrecision(_formulaData.a[3], 2); cal2["a5"] = reduceFloatPrecision(_formulaData.a[4], 2); + cal2["a6"] = reduceFloatPrecision(_formulaData.a[5], 2); + cal2["a7"] = reduceFloatPrecision(_formulaData.a[6], 2); + cal2["a8"] = reduceFloatPrecision(_formulaData.a[7], 2); + cal2["a9"] = reduceFloatPrecision(_formulaData.a[8], 2); + cal2["a10"] = reduceFloatPrecision(_formulaData.a[9], 2); cal2["g1"] = reduceFloatPrecision(_formulaData.g[0], 4); cal2["g2"] = reduceFloatPrecision(_formulaData.g[1], 4); cal2["g3"] = reduceFloatPrecision(_formulaData.g[2], 4); cal2["g4"] = reduceFloatPrecision(_formulaData.g[3], 4); cal2["g5"] = reduceFloatPrecision(_formulaData.g[4], 4); + cal2["g6"] = reduceFloatPrecision(_formulaData.g[5], 4); + cal2["g7"] = reduceFloatPrecision(_formulaData.g[6], 4); + cal2["g8"] = reduceFloatPrecision(_formulaData.g[7], 4); + cal2["g9"] = reduceFloatPrecision(_formulaData.g[8], 4); + cal2["g10"] = reduceFloatPrecision(_formulaData.g[9], 4); } // @@ -207,9 +216,6 @@ bool Config::loadFile() { setTempFormat(s.charAt(0)); } - if (!doc[PARAM_PUSH_BREWFATHER].isNull()) - setBrewfatherPushUrl(doc[PARAM_PUSH_BREWFATHER]); - if (!doc[PARAM_TOKEN].isNull()) setToken(doc[PARAM_TOKEN]); if (!doc[PARAM_TOKEN2].isNull()) setToken2(doc[PARAM_TOKEN2]); if (!doc[PARAM_PUSH_HTTP].isNull()) setHttpUrl(doc[PARAM_PUSH_HTTP]); @@ -281,6 +287,16 @@ bool Config::loadFile() { _formulaData.a[3] = doc[PARAM_FORMULA_DATA]["a4"].as(); if (!doc[PARAM_FORMULA_DATA]["a5"].isNull()) _formulaData.a[4] = doc[PARAM_FORMULA_DATA]["a5"].as(); + if (!doc[PARAM_FORMULA_DATA]["a6"].isNull()) + _formulaData.a[5] = doc[PARAM_FORMULA_DATA]["a6"].as(); + if (!doc[PARAM_FORMULA_DATA]["a7"].isNull()) + _formulaData.a[6] = doc[PARAM_FORMULA_DATA]["a7"].as(); + if (!doc[PARAM_FORMULA_DATA]["a8"].isNull()) + _formulaData.a[7] = doc[PARAM_FORMULA_DATA]["a8"].as(); + if (!doc[PARAM_FORMULA_DATA]["a9"].isNull()) + _formulaData.a[8] = doc[PARAM_FORMULA_DATA]["a9"].as(); + if (!doc[PARAM_FORMULA_DATA]["a10"].isNull()) + _formulaData.a[9] = doc[PARAM_FORMULA_DATA]["a10"].as(); if (!doc[PARAM_FORMULA_DATA]["g1"].isNull()) _formulaData.g[0] = doc[PARAM_FORMULA_DATA]["g1"].as(); @@ -292,6 +308,16 @@ bool Config::loadFile() { _formulaData.g[3] = doc[PARAM_FORMULA_DATA]["g4"].as(); if (!doc[PARAM_FORMULA_DATA]["g5"].isNull()) _formulaData.g[4] = doc[PARAM_FORMULA_DATA]["g5"].as(); + if (!doc[PARAM_FORMULA_DATA]["g6"].isNull()) + _formulaData.g[5] = doc[PARAM_FORMULA_DATA]["g6"].as(); + if (!doc[PARAM_FORMULA_DATA]["g7"].isNull()) + _formulaData.g[6] = doc[PARAM_FORMULA_DATA]["g7"].as(); + if (!doc[PARAM_FORMULA_DATA]["g8"].isNull()) + _formulaData.g[7] = doc[PARAM_FORMULA_DATA]["g8"].as(); + if (!doc[PARAM_FORMULA_DATA]["g9"].isNull()) + _formulaData.g[8] = doc[PARAM_FORMULA_DATA]["g9"].as(); + if (!doc[PARAM_FORMULA_DATA]["g10"].isNull()) + _formulaData.g[9] = doc[PARAM_FORMULA_DATA]["g10"].as(); /*if( doc[PARAM_CONFIG_VER].isNull() ) { // If this parameter is missing we need to reset the gyrocalibaration due to @@ -333,7 +359,7 @@ void Config::checkFileSystem() { // // Save json document to file // -bool HardwareConfig::saveFile() { +bool AdvancedConfig::saveFile() { #if LOG_LEVEL == 6 && !defined(DISABLE_LOGGING) Log.verbose(F("CFG : Saving hardware configuration to file." CR)); #endif @@ -352,8 +378,14 @@ bool HardwareConfig::saveFile() { doc[PARAM_HW_GYRO_READ_DELAY] = this->getGyroReadDelay(); doc[PARAM_HW_GYRO_MOVING_THREASHOLD] = this->getGyroSensorMovingThreashold(); doc[PARAM_HW_FORMULA_DEVIATION] = this->getMaxFormulaCreationDeviation(); - doc[PARAM_HW_WIFI_PORTALTIMEOUT] = this->getWifiPortalTimeout(); + doc[PARAM_HW_WIFI_PORTAL_TIMEOUT] = this->getWifiPortalTimeout(); + doc[PARAM_HW_WIFI_CONNECT_TIMEOUT] = this->getWifiConnectTimeout(); doc[PARAM_HW_FORMULA_CALIBRATION_TEMP] = this->getDefaultCalibrationTemp(); + doc[PARAM_HW_PUSH_INTERVAL_HTTP1] = this->getPushIntervalHttp1(); + doc[PARAM_HW_PUSH_INTERVAL_HTTP2] = this->getPushIntervalHttp2(); + doc[PARAM_HW_PUSH_INTERVAL_HTTP3] = this->getPushIntervalHttp3(); + doc[PARAM_HW_PUSH_INTERVAL_INFLUX] = this->getPushIntervalInflux(); + doc[PARAM_HW_PUSH_INTERVAL_MQTT] = this->getPushIntervalMqtt(); #if LOG_LEVEL == 6 && !defined(DISABLE_LOGGING) serializeJson(doc, Serial); @@ -371,7 +403,7 @@ bool HardwareConfig::saveFile() { // // Load config file from disk // -bool HardwareConfig::loadFile() { +bool AdvancedConfig::loadFile() { #if LOG_LEVEL == 6 && !defined(DISABLE_LOGGING) Log.verbose(F("CFG : Loading hardware configuration from file." CR)); #endif @@ -424,10 +456,22 @@ bool HardwareConfig::loadFile() { if (!doc[PARAM_HW_FORMULA_CALIBRATION_TEMP].isNull()) this->SetDefaultCalibrationTemp( doc[PARAM_HW_FORMULA_CALIBRATION_TEMP].as()); - if (!doc[PARAM_HW_WIFI_PORTALTIMEOUT].isNull()) - this->setWifiPortalTimeout(doc[PARAM_HW_WIFI_PORTALTIMEOUT].as()); + if (!doc[PARAM_HW_WIFI_PORTAL_TIMEOUT].isNull()) + this->setWifiPortalTimeout(doc[PARAM_HW_WIFI_PORTAL_TIMEOUT].as()); + if (!doc[PARAM_HW_WIFI_CONNECT_TIMEOUT].isNull()) + this->setWifiConnectTimeout(doc[PARAM_HW_WIFI_CONNECT_TIMEOUT].as()); if (!doc[PARAM_HW_PUSH_TIMEOUT].isNull()) this->setPushTimeout(doc[PARAM_HW_PUSH_TIMEOUT].as()); + if (!doc[PARAM_HW_PUSH_INTERVAL_HTTP1].isNull()) + this->setPushIntervalHttp1(doc[PARAM_HW_PUSH_INTERVAL_HTTP1].as()); + if (!doc[PARAM_HW_PUSH_INTERVAL_HTTP2].isNull()) + this->setPushIntervalHttp2(doc[PARAM_HW_PUSH_INTERVAL_HTTP2].as()); + if (!doc[PARAM_HW_PUSH_INTERVAL_HTTP3].isNull()) + this->setPushIntervalHttp3(doc[PARAM_HW_PUSH_INTERVAL_HTTP3].as()); + if (!doc[PARAM_HW_PUSH_INTERVAL_INFLUX].isNull()) + this->setPushIntervalInflux(doc[PARAM_HW_PUSH_INTERVAL_INFLUX].as()); + if (!doc[PARAM_HW_PUSH_INTERVAL_MQTT].isNull()) + this->setPushIntervalMqtt(doc[PARAM_HW_PUSH_INTERVAL_MQTT].as()); Log.notice(F("CFG : Configuration file " CFG_HW_FILENAME " loaded." CR)); return true; diff --git a/src/config.hpp b/src/config.hpp index fec2e75..d9aacda 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -47,41 +47,75 @@ struct RawGyroData { }; // Used for holding formulaData (used for calculating formula on device) +#define FORMULA_DATA_SIZE 10 + struct RawFormulaData { - double a[5]; - double g[5]; + double a[FORMULA_DATA_SIZE]; + double g[FORMULA_DATA_SIZE]; }; -class HardwareConfig { +class AdvancedConfig { private: int _wifiPortalTimeout = 120; + int _wifiConnectTimeout = 20; float _maxFormulaCreationDeviation = 1.6; float _defaultCalibrationTemp = 20.0; int _gyroSensorMovingThreashold = 500; int _gyroReadCount = 50; int _gyroReadDelay = 3150; // us, empirical, to hold sampling to 200 Hz int _pushTimeout = 10; // seconds + int _pushIntervalHttp1 = 0; + int _pushIntervalHttp2 = 0; + int _pushIntervalHttp3 = 0; + int _pushIntervalInflux = 0; + int _pushIntervalMqtt = 0; public: int getWifiPortalTimeout() { return _wifiPortalTimeout; } void setWifiPortalTimeout(int t) { _wifiPortalTimeout = t; } + + int getWifiConnectTimeout() { return _wifiConnectTimeout; } + void setWifiConnectTimeout(int t) { _wifiConnectTimeout = t; } + float getMaxFormulaCreationDeviation() { return _maxFormulaCreationDeviation; } void setMaxFormulaCreationDeviation(float f) { _maxFormulaCreationDeviation = f; } + float getDefaultCalibrationTemp() { return _defaultCalibrationTemp; } void SetDefaultCalibrationTemp(float t) { _defaultCalibrationTemp = t; } + int getGyroSensorMovingThreashold() { return _gyroSensorMovingThreashold; } void setGyroSensorMovingThreashold(int t) { _gyroSensorMovingThreashold = t; } + int getGyroReadCount() { return _gyroReadCount; } void setGyroReadCount(int c) { _gyroReadCount = c; } + int getGyroReadDelay() { return _gyroReadDelay; } void setGyroReadDelay(int d) { _gyroReadDelay = d; } + int getPushTimeout() { return _pushTimeout; } void setPushTimeout(int t) { _pushTimeout = t; } + int getPushIntervalHttp1() { return _pushIntervalHttp1; } + void setPushIntervalHttp1(int t) { _pushIntervalHttp1 = t; } + + int getPushIntervalHttp2() { return _pushIntervalHttp2; } + void setPushIntervalHttp2(int t) { _pushIntervalHttp2 = t; } + + int getPushIntervalHttp3() { return _pushIntervalHttp3; } + void setPushIntervalHttp3(int t) { _pushIntervalHttp3 = t; } + + int getPushIntervalInflux() { return _pushIntervalInflux; } + void setPushIntervalInflux(int t) { _pushIntervalInflux = t; } + + int getPushIntervalMqtt() { return _pushIntervalMqtt; } + void setPushIntervalMqtt(int t) { _pushIntervalMqtt = t; } + + bool isPushIntervalActive() { return (_pushIntervalHttp1+_pushIntervalHttp2+_pushIntervalHttp3+_pushIntervalInflux+_pushIntervalMqtt) == 0 ? false : true; } + bool saveFile(); bool loadFile(); }; @@ -106,8 +140,6 @@ class Config { String _wifiPASS = ""; // Push target settings - String _brewfatherPushUrl = ""; - String _token = ""; String _token2 = ""; @@ -178,16 +210,6 @@ class Config { _saveNeeded = true; } - // Brewfather - const char* getBrewfatherPushUrl() { return _brewfatherPushUrl.c_str(); } - void setBrewfatherPushUrl(String s) { - _brewfatherPushUrl = s; - _saveNeeded = true; - } - bool isBrewfatherActive() { - return _brewfatherPushUrl.length() ? true : false; - } - // Token parameter const char* getToken() { return _token.c_str(); } void setToken(String s) { @@ -242,6 +264,7 @@ class Config { _saveNeeded = true; } bool isInfluxDb2Active() { return _influxDb2Url.length() ? true : false; } + bool isInfluxSSL() { return _influxDb2Url.startsWith("https://"); } const char* getInfluxDb2PushOrg() { return _influxDb2Org.c_str(); } void setInfluxDb2PushOrg(String s) { _influxDb2Org = s; @@ -361,7 +384,7 @@ class Config { bool isBLEActive() { return _colorBLE.length() ? true : false; } bool isWifiPushActive() { return (isHttpActive() || isHttp2Active() || isHttp3Active() || - isBrewfatherActive() || isInfluxDb2Active() || isMqttActive()) + isInfluxDb2Active() || isMqttActive()) ? true : false; } @@ -388,7 +411,7 @@ class Config { }; extern Config myConfig; -extern HardwareConfig myHardwareConfig; +extern AdvancedConfig myAdvancedConfig; #endif // SRC_CONFIG_HPP_ diff --git a/src/gyro.cpp b/src/gyro.cpp index b49ade3..86354bd 100644 --- a/src/gyro.cpp +++ b/src/gyro.cpp @@ -217,7 +217,7 @@ bool GyroSensor::isSensorMoving(RawGyroData &raw) { #endif int x = abs(raw.gx), y = abs(raw.gy), z = abs(raw.gz); - int threashold = myHardwareConfig.getGyroSensorMovingThreashold(); + int threashold = myAdvancedConfig.getGyroSensorMovingThreashold(); if (x > threashold || y > threashold || z > threashold) { Log.notice(F("GYRO: Movement detected (%d)\t%d\t%d\t%d." CR), threashold, x, @@ -239,8 +239,8 @@ bool GyroSensor::read() { if (!_sensorConnected) return false; readSensor( - _lastGyroData, myHardwareConfig.getGyroReadCount(), - myHardwareConfig.getGyroReadDelay()); // Last param is unused if + _lastGyroData, myAdvancedConfig.getGyroReadCount(), + myAdvancedConfig.getGyroReadDelay()); // Last param is unused if // GYRO_USE_INTERRUPT is defined. // If the sensor is unstable we return false to signal we dont have valid diff --git a/src/helper.cpp b/src/helper.cpp index 7fba565..8023174 100644 --- a/src/helper.cpp +++ b/src/helper.cpp @@ -387,7 +387,7 @@ void PerfLogging::pushInflux() { // Send HTTP POST request String auth = "Token " + String(myConfig.getInfluxDb2PushToken()); http.addHeader(F("Authorization"), auth.c_str()); - http.setTimeout(myHardwareConfig.getPushTimeout()); + http.setTimeout(myAdvancedConfig.getPushTimeout()); int httpResponseCode = http.POST(body); if (httpResponseCode == 204) { diff --git a/src/main.cpp b/src/main.cpp index af69136..882a76c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,6 +60,12 @@ void checkSleepMode(float angle, float volt) { return; #endif +#if defined( FORCE_GRAVITY_MODE ) + Log.notice( + F("MAIN: Forcing device into gravity mode for debugging" CR)); + runMode = RunMode::gravityMode; +#endif + const RawGyroData &g = myConfig.getGyroCalibration(); if (!g.ax && !g.ay && !g.az && !g.gx && !g.gy && !g.gz) { @@ -138,7 +144,7 @@ void setup() { myConfig.checkFileSystem(); myConfig.loadFile(); myWifi.init(); - myHardwareConfig.loadFile(); + myAdvancedConfig.loadFile(); LOG_PERF_STOP("main-config-load"); // Setup watchdog diff --git a/src/pushtarget.cpp b/src/pushtarget.cpp index 9cf045c..68123c5 100644 --- a/src/pushtarget.cpp +++ b/src/pushtarget.cpp @@ -32,6 +32,74 @@ SOFTWARE. #include #include +#define PUSHINT_FILENAME "/push.dat" + +// +// Decrease counters +// +void PushIntervalTracker::update(const int index, const int defaultValue) { + if (_counters[index] <= 0) + _counters[index] = defaultValue; + else + _counters[index]--; +} + +// +// Load data from file +// +void PushIntervalTracker::load() { + File intFile = LittleFS.open(PUSHINT_FILENAME, "r"); + int i = 0; + + if (intFile) { + String line = intFile.readStringUntil('\n'); + Log.notice(F("PUSH: Read interval tracker %s." CR), line.c_str()); + + char temp[80]; + char *s, *p = &temp[0]; + int i = 0; + + snprintf(&temp[0], sizeof(temp), "%s", line.c_str()); + while ((s = strtok_r(p, ":", &p)) != NULL) { + _counters[i++] = atoi(s); + } + + intFile.close(); + } + +#if !defined(PUSH_DISABLE_LOGGING) + Log.verbose(F("PUSH: Parsed trackers: %d:%d:%d:%d:%d." CR), _counters[0], _counters[1], _counters[2], _counters[3], _counters[4] ); +#endif +} + +// +// Update and save counters +// +void PushIntervalTracker::save() { + update(0, myAdvancedConfig.getPushIntervalHttp1()); + update(1, myAdvancedConfig.getPushIntervalHttp2()); + update(2, myAdvancedConfig.getPushIntervalHttp3()); + update(3, myAdvancedConfig.getPushIntervalInflux()); + update(4, myAdvancedConfig.getPushIntervalMqtt()); + + // If this feature is disabled we skip saving the file + if (!myAdvancedConfig.isPushIntervalActive()) { +#if !defined(PUSH_DISABLE_LOGGING) + Log.notice(F("PUSH: Variabled push interval disabled." CR)); +#endif + LittleFS.remove(PUSHINT_FILENAME); + } else { + Log.notice(F("PUSH: Variabled push interval enabled, updating counters." CR)); + File intFile = LittleFS.open(PUSHINT_FILENAME, "w"); + + if (intFile) { + // Format=http1:http2:http3:influx:mqtt + intFile.printf("%d:%d:%d:%d:%d\n", _counters[0], _counters[1], _counters[2], _counters[3], _counters[4] ); + intFile.close(); + } + } +} + // // Send the data to targets // @@ -44,47 +112,76 @@ void PushTarget::sendAll(float angle, float gravitySG, float corrGravitySG, TemplatingEngine engine; engine.initialize(angle, gravitySG, corrGravitySG, tempC, runTime); - if (myConfig.isBrewfatherActive()) { - LOG_PERF_START("push-brewfather"); - sendBrewfather(engine); - LOG_PERF_STOP("push-brewfather"); - } + PushIntervalTracker intDelay; + intDelay.load(); - if (myConfig.isHttpActive()) { + if (myConfig.isHttpActive() && intDelay.useHttp1()) { LOG_PERF_START("push-http"); sendHttpPost(engine, myConfig.isHttpSSL(), 0); LOG_PERF_STOP("push-http"); } - if (myConfig.isHttp2Active()) { + if (myConfig.isHttp2Active() && intDelay.useHttp2()) { LOG_PERF_START("push-http2"); sendHttpPost(engine, myConfig.isHttp2SSL(), 1); LOG_PERF_STOP("push-http2"); } - if (myConfig.isHttp3Active()) { + if (myConfig.isHttp3Active() && intDelay.useHttp3()) { LOG_PERF_START("push-http3"); sendHttpGet(engine, myConfig.isHttp3SSL()); LOG_PERF_STOP("push-http3"); } - if (myConfig.isInfluxDb2Active()) { + if (myConfig.isInfluxDb2Active() && intDelay.useInflux()) { LOG_PERF_START("push-influxdb2"); - sendInfluxDb2(engine); + sendInfluxDb2(engine, myConfig.isInfluxSSL()); LOG_PERF_STOP("push-influxdb2"); } - if (myConfig.isMqttActive()) { + if (myConfig.isMqttActive() && intDelay.useMqtt()) { LOG_PERF_START("push-mqtt"); sendMqtt(engine, myConfig.isMqttSSL()); LOG_PERF_STOP("push-mqtt"); } + + intDelay.save(); +} + +// +// Check if the server can reduce the buffer size to save memory (ESP8266 only) +// +void PushTarget::probeMaxFragement( String& serverPath ) { +#if defined(ESP8266) // Looks like this is feature is not supported by influxdb + // Format: http:://servername:port/path + int port = 443; + String host = + serverPath.substring(8); // remove the prefix or the probe will fail, + // it needs a pure host name. + // Remove the path if it exist + int idx = host.indexOf("/"); + if (idx != -1) host = host.substring(0, idx); + + // If a server port is defined, lets extract that part + idx = host.indexOf(":"); + if (idx != -1) { + String p = host.substring(idx+1); + port = p.toInt(); + host = host.substring(0, idx); + } + + Log.notice(F("PUSH: Probing server to max fragment %s:%d" CR), host.c_str(), port); + if (_wifiSecure.probeMaxFragmentLength(host, port, 512)) { + Log.notice(F("PUSH: Server supports smaller SSL buffer." CR)); + _wifiSecure.setBufferSizes(512, 512); + } +#endif } // // Send to influx db v2 // -void PushTarget::sendInfluxDb2(TemplatingEngine& engine) { +void PushTarget::sendInfluxDb2(TemplatingEngine& engine, bool isSecure) { #if !defined(PUSH_DISABLE_LOGGING) Log.notice(F("PUSH: Sending values to influxdb2." CR)); #endif @@ -97,17 +194,27 @@ void PushTarget::sendInfluxDb2(TemplatingEngine& engine) { "&bucket=" + String(myConfig.getInfluxDb2PushBucket()); String doc = engine.create(TemplatingEngine::TEMPLATE_INFLUX); - _http.begin(_wifi, serverPath); - _http.setTimeout(myHardwareConfig.getPushTimeout() * 1000); - #if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING) Log.verbose(F("PUSH: url %s." CR), serverPath.c_str()); Log.verbose(F("PUSH: data %s." CR), doc.c_str()); #endif String auth = "Token " + String(myConfig.getInfluxDb2PushToken()); - _http.addHeader(F("Authorization"), auth.c_str()); - _lastCode = _http.POST(doc); + + if (isSecure) { + Log.notice(F("PUSH: InfluxDB, SSL enabled without validation." CR)); + _wifiSecure.setInsecure(); + probeMaxFragement( serverPath ); + _httpSecure.setTimeout(myAdvancedConfig.getPushTimeout() * 1000); + _httpSecure.begin(_wifiSecure, serverPath); + _httpSecure.addHeader(F("Authorization"), auth.c_str()); + _lastCode = _httpSecure.POST(doc); + } else { + _http.setTimeout(myAdvancedConfig.getPushTimeout() * 1000); + _http.begin(_wifi, serverPath); + _http.addHeader(F("Authorization"), auth.c_str()); + _lastCode = _http.POST(doc); + } if (_lastCode == 204) { _lastSuccess = true; @@ -117,52 +224,18 @@ void PushTarget::sendInfluxDb2(TemplatingEngine& engine) { errLog.addEntry("PUSH: Influxdb push failed response=" + String(_lastCode)); } - _http.end(); - _wifi.stop(); - tcp_cleanup(); -} - -// -// Send data to brewfather -// -void PushTarget::sendBrewfather(TemplatingEngine& engine) { -#if !defined(PUSH_DISABLE_LOGGING) - Log.notice(F("PUSH: Sending values to brewfather" CR)); -#endif - _lastCode = 0; - _lastSuccess = false; - - String serverPath = myConfig.getBrewfatherPushUrl(); - String doc = engine.create(TemplatingEngine::TEMPLATE_BREWFATHER); - - _http.begin(_wifi, serverPath); - _http.setTimeout(myHardwareConfig.getPushTimeout() * 1000); - -#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), doc.c_str()); -#endif - - _http.addHeader(F("Content-Type"), F("application/json")); - _lastCode = _http.POST(doc); - - if (_lastCode == 200) { - _lastSuccess = true; - Log.notice(F("PUSH: Brewfather push successful, response=%d" CR), - _lastCode); + if (isSecure) { + _httpSecure.end(); + _wifiSecure.stop(); } else { - ErrorFileLog errLog; - errLog.addEntry("PUSH: Brewfather push failed response=" + - String(_lastCode)); + _http.end(); + _wifi.stop(); } - - _http.end(); - _wifi.stop(); tcp_cleanup(); } // -// +// Add HTTP header to request // void PushTarget::addHttpHeader(HTTPClient& http, String header) { if (!header.length()) return; @@ -211,22 +284,9 @@ void PushTarget::sendHttpPost(TemplatingEngine& engine, bool isSecure, if (isSecure) { Log.notice(F("PUSH: HTTP, SSL enabled without validation." CR)); _wifiSecure.setInsecure(); - -#if defined(ESP8266) - String host = - serverPath.substring(8); // remove the prefix or the probe will fail, - // it needs a pure host name. - int idx = host.indexOf("/"); - if (idx != -1) host = host.substring(0, idx); - - if (_wifiSecure.probeMaxFragmentLength(host, 443, 512)) { - Log.notice(F("PUSH: HTTP server supports smaller SSL buffer." CR)); - _wifiSecure.setBufferSizes(512, 512); - } -#endif - + probeMaxFragement( serverPath ); + _httpSecure.setTimeout(myAdvancedConfig.getPushTimeout() * 1000); _httpSecure.begin(_wifiSecure, serverPath); - _httpSecure.setTimeout(myHardwareConfig.getPushTimeout() * 1000); if (index == 0) { addHttpHeader(_httpSecure, myConfig.getHttpHeader(0)); @@ -238,8 +298,8 @@ void PushTarget::sendHttpPost(TemplatingEngine& engine, bool isSecure, _lastCode = _httpSecure.POST(doc); } else { + _http.setTimeout(myAdvancedConfig.getPushTimeout() * 1000); _http.begin(_wifi, serverPath); - _http.setTimeout(myHardwareConfig.getPushTimeout() * 1000); if (index == 0) { addHttpHeader(_http, myConfig.getHttpHeader(0)); @@ -293,26 +353,13 @@ void PushTarget::sendHttpGet(TemplatingEngine& engine, bool isSecure) { if (isSecure) { Log.notice(F("PUSH: HTTP, SSL enabled without validation." CR)); _wifiSecure.setInsecure(); - -#if defined(ESP8266) - String host = - serverPath.substring(8); // remove the prefix or the probe will fail, - // it needs a pure host name. - int idx = host.indexOf("/"); - if (idx != -1) host = host.substring(0, idx); - - if (_wifiSecure.probeMaxFragmentLength(host, 443, 512)) { - Log.notice(F("PUSH: HTTP server supports smaller SSL buffer." CR)); - _wifiSecure.setBufferSizes(512, 512); - } -#endif - + probeMaxFragement( serverPath ); + _httpSecure.setTimeout(myAdvancedConfig.getPushTimeout() * 1000); _httpSecure.begin(_wifiSecure, serverPath); - _httpSecure.setTimeout(myHardwareConfig.getPushTimeout() * 1000); _lastCode = _httpSecure.GET(); } else { + _http.setTimeout(myAdvancedConfig.getPushTimeout() * 1000); _http.begin(_wifi, serverPath); - _http.setTimeout(myHardwareConfig.getPushTimeout() * 1000); _lastCode = _http.GET(); } @@ -360,8 +407,10 @@ void PushTarget::sendMqtt(TemplatingEngine& engine, bool isSecure) { } #endif + mqtt.setTimeout(myAdvancedConfig.getPushTimeout() * 1000); mqtt.begin(host.c_str(), port, _wifiSecure); } else { + mqtt.setTimeout(myAdvancedConfig.getPushTimeout() * 1000); mqtt.begin(host.c_str(), port, _wifi); } @@ -373,9 +422,6 @@ void PushTarget::sendMqtt(TemplatingEngine& engine, bool isSecure) { Log.verbose(F("PUSH: data %s." CR), doc.c_str()); #endif - // Send MQQT message(s) - mqtt.setTimeout(myHardwareConfig.getPushTimeout()); // 10 seconds timeout - int lines = 1; // Find out how many lines are in the document. Each line is one // topic/message. | is used as new line. diff --git a/src/pushtarget.hpp b/src/pushtarget.hpp index 8bc8337..e657565 100644 --- a/src/pushtarget.hpp +++ b/src/pushtarget.hpp @@ -45,12 +45,12 @@ class PushTarget { void sendHttpPost(TemplatingEngine& engine, bool isSecure, int index); void sendHttpGet(TemplatingEngine& engine, bool isSecure); void addHttpHeader(HTTPClient& http, String header); + void probeMaxFragement( String& serverPath ); public: void sendAll(float angle, float gravitySG, float corrGravitySG, float tempC, float runTime); - void sendBrewfather(TemplatingEngine& engine); void sendHttp1(TemplatingEngine& engine, bool isSecure) { sendHttpPost(engine, isSecure, 0); } @@ -60,12 +60,27 @@ class PushTarget { void sendHttp3(TemplatingEngine& engine, bool isSecure) { sendHttpGet(engine, isSecure); } - void sendInfluxDb2(TemplatingEngine& engine); + void sendInfluxDb2(TemplatingEngine& engine, bool isSecure); void sendMqtt(TemplatingEngine& engine, bool isSecure); int getLastCode() { return _lastCode; } bool getLastSuccess() { return _lastSuccess; } }; +class PushIntervalTracker { + private: + int _counters[5] = { 0, 0, 0, 0, 0 }; + void update(const int index, const int defaultValue); + + public: + bool useHttp1() { return _counters[0] == 0 ? true : false; } + bool useHttp2() { return _counters[1] == 0 ? true : false; } + bool useHttp3() { return _counters[2] == 0 ? true : false; } + bool useInflux() { return _counters[3] == 0 ? true : false; } + bool useMqtt() { return _counters[4] == 0 ? true : false; } + void load(); + void save(); +}; + #endif // SRC_PUSHTARGET_HPP_ // EOF diff --git a/src/resources.hpp b/src/resources.hpp index cbcc47b..f787e28 100644 --- a/src/resources.hpp +++ b/src/resources.hpp @@ -32,7 +32,6 @@ SOFTWARE. #define PARAM_SSID "wifi-ssid" #define PARAM_PASS "wifi-pass" #define PARAM_RUNTIME_AVERAGE "runtime-average" -#define PARAM_PUSH_BREWFATHER "brewfather-push" #define PARAM_TOKEN "token" #define PARAM_TOKEN2 "token2" #define PARAM_PUSH_HTTP "http-push" @@ -80,12 +79,17 @@ SOFTWARE. #define PARAM_HW_GYRO_MOVING_THREASHOLD "gyro-moving-threashold" #define PARAM_HW_FORMULA_DEVIATION "formula-max-deviation" #define PARAM_HW_FORMULA_CALIBRATION_TEMP "formula-calibration-temp" -#define PARAM_HW_WIFI_PORTALTIMEOUT "wifi-portaltimeout" +#define PARAM_HW_WIFI_PORTAL_TIMEOUT "wifi-portal-timeout" +#define PARAM_HW_WIFI_CONNECT_TIMEOUT "wifi-connect-timeout" #define PARAM_HW_PUSH_TIMEOUT "push-timeout" +#define PARAM_HW_PUSH_INTERVAL_HTTP1 "int-http1" +#define PARAM_HW_PUSH_INTERVAL_HTTP2 "int-http2" +#define PARAM_HW_PUSH_INTERVAL_HTTP3 "int-http3" +#define PARAM_HW_PUSH_INTERVAL_INFLUX "int-influx" +#define PARAM_HW_PUSH_INTERVAL_MQTT "int-mqtt" #define PARAM_FORMAT_HTTP1 "http-1" #define PARAM_FORMAT_HTTP2 "http-2" #define PARAM_FORMAT_HTTP3 "http-3" -#define PARAM_FORMAT_BREWFATHER "brewfather" #define PARAM_FORMAT_INFLUXDB "influxdb" #define PARAM_FORMAT_MQTT "mqtt" #define PARAM_PUSH_FORMAT "format" diff --git a/src/templating.cpp b/src/templating.cpp index 3cb47dd..23b076f 100644 --- a/src/templating.cpp +++ b/src/templating.cpp @@ -42,7 +42,7 @@ const char iSpindleFormat[] PROGMEM = "\"gravity\": ${gravity}, " "\"angle\": ${angle}, " "\"battery\": ${battery}, " - "\"rssi\": ${rssi}, " + "\"RSSI\": ${rssi}, " "\"corr-gravity\": ${corr-gravity}, " "\"gravity-unit\": \"${gravity-unit}\", " "\"run-time\": ${run-time} " @@ -64,24 +64,6 @@ const char iHttpGetFormat[] PROGMEM = "&gravity-unit=${gravity-unit}" "&run-time=${run-time}"; -const char brewfatherFormat[] PROGMEM = - "{" - "\"name\": \"${mdns}\"," - "\"temp\": ${temp}, " - "\"aux_temp\": 0, " - "\"ext_temp\": 0, " - "\"temp_unit\": \"${temp-unit}\", " - "\"gravity\": ${gravity}, " - "\"gravity_unit\": \"${gravity-unit}\", " - "\"pressure\": 0, " - "\"pressure_unit\": \"PSI\", " - "\"ph\": 0, " - "\"bpm\": 0, " - "\"comment\": \"\", " - "\"beer\": \"\", " - "\"battery\": ${battery}" - "}"; - const char influxDbFormat[] PROGMEM = "measurement,host=${mdns},device=${id},temp-format=${temp-unit},gravity-" "format=${gravity-unit} " @@ -174,10 +156,6 @@ const String& TemplatingEngine::create(TemplatingEngine::Templates idx) { baseTemplate = String(iHttpGetFormat); fname = TPL_FNAME_HTTP3; break; - case TEMPLATE_BREWFATHER: - baseTemplate = String(brewfatherFormat); - // fname = TPL_FNAME_BREWFATHER; - break; case TEMPLATE_INFLUX: baseTemplate = String(influxDbFormat); fname = TPL_FNAME_INFLUXDB; diff --git a/src/templating.hpp b/src/templating.hpp index 4800fa8..579c71e 100644 --- a/src/templating.hpp +++ b/src/templating.hpp @@ -57,13 +57,11 @@ SOFTWARE. #define TPL_FNAME_HTTP1 "/http-1.tpl" #define TPL_FNAME_HTTP2 "/http-2.tpl" #define TPL_FNAME_HTTP3 "/http-3.tpl" -// #define TPL_FNAME_BREWFATHER "/brewfather.tpl" #define TPL_FNAME_INFLUXDB "/influxdb.tpl" #define TPL_FNAME_MQTT "/mqtt.tpl" extern const char iSpindleFormat[] PROGMEM; extern const char iHttpGetFormat[] PROGMEM; -extern const char brewfatherFormat[] PROGMEM; extern const char influxDbFormat[] PROGMEM; extern const char mqttFormat[] PROGMEM; @@ -133,9 +131,8 @@ class TemplatingEngine { TEMPLATE_HTTP1 = 0, TEMPLATE_HTTP2 = 1, TEMPLATE_HTTP3 = 2, - TEMPLATE_BREWFATHER = 3, - TEMPLATE_INFLUX = 4, - TEMPLATE_MQTT = 5 + TEMPLATE_INFLUX = 3, + TEMPLATE_MQTT = 4 }; void initialize(float angle, float gravitySG, float corrGravitySG, diff --git a/src/webserver.cpp b/src/webserver.cpp index 7be5ad8..d16bf54 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -498,8 +498,6 @@ void WebServerHandler::webHandleConfigPush() { myConfig.setHttp2Header(_server->arg(PARAM_PUSH_HTTP2_H2).c_str(), 1); if (_server->hasArg(PARAM_PUSH_HTTP3)) myConfig.setHttp3Url(_server->arg(PARAM_PUSH_HTTP3).c_str()); - if (_server->hasArg(PARAM_PUSH_BREWFATHER)) - myConfig.setBrewfatherPushUrl(_server->arg(PARAM_PUSH_BREWFATHER).c_str()); if (_server->hasArg(PARAM_PUSH_INFLUXDB2)) myConfig.setInfluxDb2PushUrl(_server->arg(PARAM_PUSH_INFLUXDB2).c_str()); if (_server->hasArg(PARAM_PUSH_INFLUXDB2_ORG)) @@ -624,18 +622,18 @@ void WebServerHandler::webHandleConfigHardware() { } // -// Update device parameters. +// Update advanced settings. // -void WebServerHandler::webHandleDeviceParam() { - LOG_PERF_START("webserver-api-device-param"); +void WebServerHandler::webHandleConfigAdvancedWrite() { + LOG_PERF_START("webserver-api-config-advanced"); String id = _server->arg(PARAM_ID); - Log.notice(F("WEB : webServer callback for /api/device/param(post)." CR)); + Log.notice(F("WEB : webServer callback for /api/config/advaced(post)." CR)); if (!id.equalsIgnoreCase(myConfig.getID())) { Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(), myConfig.getID()); _server->send(400, "text/plain", "Invalid ID."); - LOG_PERF_STOP("webserver-api-device-param"); + LOG_PERF_STOP("webserver-api-config-advanced"); return; } @@ -643,41 +641,65 @@ void WebServerHandler::webHandleDeviceParam() { Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str()); #endif - for (int i = 0; i < _server->args(); i++) { - String s = _server->arg(i); + if (_server->hasArg(PARAM_HW_GYRO_READ_COUNT)) + myAdvancedConfig.setGyroReadCount(_server->arg(PARAM_HW_GYRO_READ_COUNT).toInt()); + if (_server->hasArg(PARAM_HW_GYRO_READ_DELAY)) + myAdvancedConfig.setGyroReadDelay(_server->arg(PARAM_HW_GYRO_READ_DELAY).toInt()); + if (_server->hasArg(PARAM_HW_GYRO_MOVING_THREASHOLD)) + myAdvancedConfig.setGyroSensorMovingThreashold(_server->arg(PARAM_HW_GYRO_MOVING_THREASHOLD).toInt()); + if (_server->hasArg(PARAM_HW_FORMULA_DEVIATION)) + myAdvancedConfig.setMaxFormulaCreationDeviation(_server->arg(PARAM_HW_FORMULA_DEVIATION).toFloat()); + if (_server->hasArg(PARAM_HW_FORMULA_CALIBRATION_TEMP)) + myAdvancedConfig.SetDefaultCalibrationTemp(_server->arg(PARAM_HW_FORMULA_CALIBRATION_TEMP).toFloat()); + if (_server->hasArg(PARAM_HW_WIFI_PORTAL_TIMEOUT)) + myAdvancedConfig.setWifiPortalTimeout(_server->arg(PARAM_HW_WIFI_PORTAL_TIMEOUT).toInt()); + if (_server->hasArg(PARAM_HW_WIFI_CONNECT_TIMEOUT)) + myAdvancedConfig.setWifiConnectTimeout(_server->arg(PARAM_HW_WIFI_CONNECT_TIMEOUT).toInt()); + if (_server->hasArg(PARAM_HW_PUSH_TIMEOUT)) + myAdvancedConfig.setPushTimeout(_server->arg(PARAM_HW_PUSH_TIMEOUT).toInt()); + if (_server->hasArg(PARAM_HW_PUSH_INTERVAL_HTTP1)) + myAdvancedConfig.setPushIntervalHttp1(_server->arg(PARAM_HW_PUSH_INTERVAL_HTTP1).toInt()); + if (_server->hasArg(PARAM_HW_PUSH_INTERVAL_HTTP2)) + myAdvancedConfig.setPushIntervalHttp2(_server->arg(PARAM_HW_PUSH_INTERVAL_HTTP2).toInt()); + if (_server->hasArg(PARAM_HW_PUSH_INTERVAL_HTTP3)) + myAdvancedConfig.setPushIntervalHttp3(_server->arg(PARAM_HW_PUSH_INTERVAL_HTTP3).toInt()); + if (_server->hasArg(PARAM_HW_PUSH_INTERVAL_INFLUX)) + myAdvancedConfig.setPushIntervalInflux(_server->arg(PARAM_HW_PUSH_INTERVAL_INFLUX).toInt()); + if (_server->hasArg(PARAM_HW_PUSH_INTERVAL_MQTT)) + myAdvancedConfig.setPushIntervalMqtt(_server->arg(PARAM_HW_PUSH_INTERVAL_MQTT).toInt()); - if (_server->argName(i).equalsIgnoreCase(PARAM_HW_GYRO_READ_COUNT)) - myHardwareConfig.setGyroReadCount(s.toInt()); - else if (_server->argName(i).equalsIgnoreCase(PARAM_HW_GYRO_READ_DELAY)) - myHardwareConfig.setGyroReadDelay(s.toInt()); - else if (_server->argName(i).equalsIgnoreCase( - PARAM_HW_GYRO_MOVING_THREASHOLD)) - myHardwareConfig.setGyroSensorMovingThreashold(s.toInt()); - else if (_server->argName(i).equalsIgnoreCase(PARAM_HW_FORMULA_DEVIATION)) - myHardwareConfig.setMaxFormulaCreationDeviation(s.toFloat()); - else if (_server->argName(i).equalsIgnoreCase( - PARAM_HW_FORMULA_CALIBRATION_TEMP)) - myHardwareConfig.SetDefaultCalibrationTemp(s.toFloat()); - else if (_server->argName(i).equalsIgnoreCase(PARAM_HW_WIFI_PORTALTIMEOUT)) - myHardwareConfig.setWifiPortalTimeout(s.toInt()); - else if (_server->argName(i).equalsIgnoreCase(PARAM_HW_PUSH_TIMEOUT)) - myHardwareConfig.setPushTimeout(s.toInt()); - } + myAdvancedConfig.saveFile(); + _server->sendHeader("Location", "/config.htm#collapseAdvanced", true); + _server->send(302, "text/plain", "Advanced config updated"); + LOG_PERF_STOP("webserver-api-config-advanced"); +} - myHardwareConfig.saveFile(); - // Return the current configuration. +// +// Read advanced settings +// +void WebServerHandler::webHandleConfigAdvancedRead() { + LOG_PERF_START("webserver-api-config-advanced"); + Log.notice(F("WEB : webServer callback for /api/config/advanced(get)." CR)); + DynamicJsonDocument doc(512); - doc[PARAM_HW_GYRO_READ_COUNT] = myHardwareConfig.getGyroReadCount(); - doc[PARAM_HW_GYRO_READ_DELAY] = myHardwareConfig.getGyroReadDelay(); + doc[PARAM_HW_GYRO_READ_COUNT] = myAdvancedConfig.getGyroReadCount(); + doc[PARAM_HW_GYRO_READ_DELAY] = myAdvancedConfig.getGyroReadDelay(); doc[PARAM_HW_GYRO_MOVING_THREASHOLD] = - myHardwareConfig.getGyroSensorMovingThreashold(); + myAdvancedConfig.getGyroSensorMovingThreashold(); doc[PARAM_HW_FORMULA_DEVIATION] = - myHardwareConfig.getMaxFormulaCreationDeviation(); - doc[PARAM_HW_WIFI_PORTALTIMEOUT] = myHardwareConfig.getWifiPortalTimeout(); + myAdvancedConfig.getMaxFormulaCreationDeviation(); + doc[PARAM_HW_WIFI_PORTAL_TIMEOUT] = myAdvancedConfig.getWifiPortalTimeout(); + doc[PARAM_HW_WIFI_CONNECT_TIMEOUT] = myAdvancedConfig.getWifiConnectTimeout(); + doc[PARAM_HW_PUSH_TIMEOUT] = myAdvancedConfig.getPushTimeout(); doc[PARAM_HW_FORMULA_CALIBRATION_TEMP] = - myHardwareConfig.getDefaultCalibrationTemp(); + myAdvancedConfig.getDefaultCalibrationTemp(); + doc[PARAM_HW_PUSH_INTERVAL_HTTP1] = myAdvancedConfig.getPushIntervalHttp1(); + doc[PARAM_HW_PUSH_INTERVAL_HTTP2] = myAdvancedConfig.getPushIntervalHttp2(); + doc[PARAM_HW_PUSH_INTERVAL_HTTP3] = myAdvancedConfig.getPushIntervalHttp3(); + doc[PARAM_HW_PUSH_INTERVAL_INFLUX] = myAdvancedConfig.getPushIntervalInflux(); + doc[PARAM_HW_PUSH_INTERVAL_MQTT] = myAdvancedConfig.getPushIntervalMqtt(); #if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING) serializeJson(doc, Serial); @@ -688,7 +710,7 @@ void WebServerHandler::webHandleDeviceParam() { out.reserve(512); serializeJson(doc, out); _server->send(200, "application/json", out.c_str()); - LOG_PERF_STOP("webserver-api-device-param"); + LOG_PERF_STOP("webserver-api-config-advanced"); } // @@ -698,7 +720,7 @@ void WebServerHandler::webHandleFormulaRead() { LOG_PERF_START("webserver-api-formula-read"); Log.notice(F("WEB : webServer callback for /api/formula(get)." CR)); - DynamicJsonDocument doc(250); + DynamicJsonDocument doc(512); const RawFormulaData& fd = myConfig.getFormulaData(); #if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING) @@ -731,6 +753,11 @@ void WebServerHandler::webHandleFormulaRead() { doc["a3"] = reduceFloatPrecision(fd.a[2], 2); doc["a4"] = reduceFloatPrecision(fd.a[3], 2); doc["a5"] = reduceFloatPrecision(fd.a[4], 2); + doc["a6"] = reduceFloatPrecision(fd.a[5], 2); + doc["a7"] = reduceFloatPrecision(fd.a[6], 2); + doc["a8"] = reduceFloatPrecision(fd.a[7], 2); + doc["a9"] = reduceFloatPrecision(fd.a[8], 2); + doc["a10"] = reduceFloatPrecision(fd.a[9], 2); if (myConfig.isGravityPlato()) { doc["g1"] = reduceFloatPrecision(convertToPlato(fd.g[0]), 1); @@ -738,12 +765,22 @@ void WebServerHandler::webHandleFormulaRead() { doc["g3"] = reduceFloatPrecision(convertToPlato(fd.g[2]), 1); doc["g4"] = reduceFloatPrecision(convertToPlato(fd.g[3]), 1); doc["g5"] = reduceFloatPrecision(convertToPlato(fd.g[4]), 1); + doc["g6"] = reduceFloatPrecision(convertToPlato(fd.g[5]), 1); + doc["g7"] = reduceFloatPrecision(convertToPlato(fd.g[6]), 1); + doc["g8"] = reduceFloatPrecision(convertToPlato(fd.g[7]), 1); + doc["g9"] = reduceFloatPrecision(convertToPlato(fd.g[8]), 1); + doc["g10"] = reduceFloatPrecision(convertToPlato(fd.g[9]), 1); } else { doc["g1"] = reduceFloatPrecision(fd.g[0], 4); doc["g2"] = reduceFloatPrecision(fd.g[1], 4); doc["g3"] = reduceFloatPrecision(fd.g[2], 4); doc["g4"] = reduceFloatPrecision(fd.g[3], 4); doc["g5"] = reduceFloatPrecision(fd.g[4], 4); + doc["g6"] = reduceFloatPrecision(fd.g[5], 4); + doc["g7"] = reduceFloatPrecision(fd.g[6], 4); + doc["g8"] = reduceFloatPrecision(fd.g[7], 4); + doc["g9"] = reduceFloatPrecision(fd.g[8], 4); + doc["g10"] = reduceFloatPrecision(fd.g[9], 4); } #if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING) @@ -792,10 +829,6 @@ void WebServerHandler::webHandleConfigFormatWrite() { } else if (_server->hasArg(PARAM_FORMAT_MQTT)) { success = writeFile(TPL_FNAME_MQTT, _server->arg(PARAM_FORMAT_MQTT)); } - /*else if (_server->hasArg(PARAM_FORMAT_BREWFATHER)) { - success = writeFile(TPL_FNAME_BREWFATHER, - _server->arg(PARAM_FORMAT_BREWFATHER)); - }*/ if (success) { _server->sendHeader("Location", "/format.htm", true); @@ -841,11 +874,7 @@ void WebServerHandler::webHandleTestPush() { PushTarget push; bool enabled = false; - if (!type.compareTo(PARAM_FORMAT_BREWFATHER) && - myConfig.isBrewfatherActive()) { - push.sendBrewfather(engine); - enabled = true; - } else if (!type.compareTo(PARAM_FORMAT_HTTP1) && myConfig.isHttpActive()) { + if (!type.compareTo(PARAM_FORMAT_HTTP1) && myConfig.isHttpActive()) { push.sendHttp1(engine, myConfig.isHttpSSL()); enabled = true; } else if (!type.compareTo(PARAM_FORMAT_HTTP2) && myConfig.isHttp2Active()) { @@ -856,7 +885,7 @@ void WebServerHandler::webHandleTestPush() { enabled = true; } else if (!type.compareTo(PARAM_FORMAT_INFLUXDB) && myConfig.isInfluxDb2Active()) { - push.sendInfluxDb2(engine); + push.sendInfluxDb2(engine, myConfig.isInfluxSSL()); enabled = true; } else if (!type.compareTo(PARAM_FORMAT_MQTT) && myConfig.isMqttActive()) { push.sendMqtt(engine, myConfig.isMqttSSL()); @@ -955,12 +984,6 @@ void WebServerHandler::webHandleConfigFormatRead() { else doc[PARAM_FORMAT_HTTP3] = urlencode(String(&iHttpGetFormat[0])); - /*s = readFile(TPL_FNAME_BREWFATHER); - if (s.length()) - doc[PARAM_FORMAT_BREWFATHER] = urlencode(s); - else - doc[PARAM_FORMAT_BREWFATHER] = urlencode(&brewfatherFormat[0]);*/ - s = readFile(TPL_FNAME_INFLUXDB); if (s.length()) doc[PARAM_FORMAT_INFLUXDB] = urlencode(s); @@ -1011,6 +1034,11 @@ void WebServerHandler::webHandleFormulaWrite() { fd.a[2] = _server->arg("a3").toDouble(); fd.a[3] = _server->arg("a4").toDouble(); fd.a[4] = _server->arg("a5").toDouble(); + fd.a[5] = _server->arg("a6").toDouble(); + fd.a[6] = _server->arg("a7").toDouble(); + fd.a[7] = _server->arg("a8").toDouble(); + fd.a[8] = _server->arg("a9").toDouble(); + fd.a[9] = _server->arg("a10").toDouble(); if (myConfig.isGravityPlato()) { fd.g[0] = convertToSG(_server->arg("g1").toDouble()); @@ -1018,12 +1046,22 @@ void WebServerHandler::webHandleFormulaWrite() { fd.g[2] = convertToSG(_server->arg("g3").toDouble()); fd.g[3] = convertToSG(_server->arg("g4").toDouble()); fd.g[4] = convertToSG(_server->arg("g5").toDouble()); + fd.g[5] = convertToSG(_server->arg("g6").toDouble()); + fd.g[6] = convertToSG(_server->arg("g7").toDouble()); + fd.g[7] = convertToSG(_server->arg("g8").toDouble()); + fd.g[8] = convertToSG(_server->arg("g9").toDouble()); + fd.g[9] = convertToSG(_server->arg("g10").toDouble()); } else { fd.g[0] = _server->arg("g1").toDouble(); fd.g[1] = _server->arg("g2").toDouble(); fd.g[2] = _server->arg("g3").toDouble(); fd.g[3] = _server->arg("g4").toDouble(); fd.g[4] = _server->arg("g5").toDouble(); + fd.g[5] = _server->arg("g6").toDouble(); + fd.g[6] = _server->arg("g7").toDouble(); + fd.g[7] = _server->arg("g8").toDouble(); + fd.g[8] = _server->arg("g9").toDouble(); + fd.g[9] = _server->arg("g10").toDouble(); } myConfig.setFormulaData(fd); @@ -1231,9 +1269,12 @@ bool WebServerHandler::setupWebServer() { _server->on("/api/config/format", HTTP_POST, std::bind(&WebServerHandler::webHandleConfigFormatWrite, this)); // Change template formats - _server->on("/api/device/param", HTTP_GET, - std::bind(&WebServerHandler::webHandleDeviceParam, - this)); // Change device params + _server->on("/api/config/advanced", HTTP_GET, + std::bind(&WebServerHandler::webHandleConfigAdvancedRead, + this)); // Read advanced settings + _server->on("/api/config/advanced", HTTP_POST, + std::bind(&WebServerHandler::webHandleConfigAdvancedWrite, + this)); // Change advanced params _server->on("/api/test/push", HTTP_GET, std::bind(&WebServerHandler::webHandleTestPush, this)); // diff --git a/src/webserver.hpp b/src/webserver.hpp index 2594f68..6a68ac8 100644 --- a/src/webserver.hpp +++ b/src/webserver.hpp @@ -60,6 +60,8 @@ class WebServerHandler { void webHandleConfig(); void webHandleFormulaWrite(); void webHandleFormulaRead(); + void webHandleConfigAdvancedRead(); + void webHandleConfigAdvancedWrite(); void webHandleConfigHardware(); void webHandleConfigGravity(); void webHandleConfigPush(); @@ -74,7 +76,6 @@ class WebServerHandler { void webHandleCalibrate(); void webHandleUploadFile(); void webHandleUpload(); - void webHandleDeviceParam(); void webHandlePageNotFound(); String readFile(String fname); diff --git a/src/wifi.cpp b/src/wifi.cpp index 7fedb92..aafa962 100644 --- a/src/wifi.cpp +++ b/src/wifi.cpp @@ -137,7 +137,7 @@ void WifiConnection::startPortal() { myWifiManager->setMinimumSignalQuality(-1); myWifiManager->setConfigPortalChannel(0); myWifiManager->setConfigPortalTimeout( - myHardwareConfig.getWifiPortalTimeout()); + myAdvancedConfig.getWifiPortalTimeout()); String mdns("

Default mDNS name is: http://"); mdns += myConfig.getMDNS(); @@ -221,7 +221,7 @@ bool WifiConnection::waitForConnection(int maxTime) { // bool WifiConnection::connect() { connectAsync(); - return waitForConnection(20); // 20 seconds. + return waitForConnection(myAdvancedConfig.getWifiConnectTimeout()); } // diff --git a/src_docs/source/api.rst b/src_docs/source/api.rst index 6cd1478..49149cb 100644 --- a/src_docs/source/api.rst +++ b/src_docs/source/api.rst @@ -24,7 +24,6 @@ Other parameters are the same as in the configuration guide. "ota-url": "http://192.168.1.50:80/firmware/gravmon/", "temp-format": "C", "ble": "color", - "brewfather-push": "http://log.brewfather.net/stream?id=Qwerty", "token": "token", "token2": "token2", "http-push": "http://192.168.1.50:9090/api/v1/Qwerty/telemetry", @@ -63,11 +62,19 @@ Other parameters are the same as in the configuration guide. "a3":35, "a4":40, "a5":45, + "a5":0, + "a6":0, + "a7":0, + "a8":0, "g1":1, "g2":1.01, "g3":1.02, "g4":1.03, - "g5":1.04 + "g4":1.04, + "g5":1, + "g6":1, + "g7":1, + "g8":1 }, "angle": 90.93, "gravity": 1.105, @@ -120,8 +127,8 @@ GET: /api/config/formula Retrive the data used for formula calculation data via an HTTP GET command. Payload is in JSON format. -* ``a1``-``a4`` are the angles/tilt readings (up to 5 are currently supported) -* ``g1``-``g4`` are the corresponding gravity reaadings in SG or Plato depending on the device-format. +* ``a1``-``a8`` are the angles/tilt readings (up to 8 are currently supported) +* ``g1``-``g8`` are the corresponding gravity reaadings in SG or Plato depending on the device-format. .. code-block:: json @@ -132,16 +139,52 @@ Retrive the data used for formula calculation data via an HTTP GET command. Payl "a3": 58, "a4": 0, "a5": 0, + "a6": 0, + "a7": 0, + "a8": 0, "g1": 1.000, "g2": 1.053, "g3": 1.062, "g4": 1, "g5": 1, + "g6": 1, + "g7": 1, + "g8": 1, + "error": "Potential error message", "gravity-format": "G", "gravity-formula": "0.0*tilt^3+0.0*tilt^2+0.0017978*tilt+0.9436" } +GET: /api/config/advanced +========================= + +Used for adjusting some internal constants and other advanced settings. Should be used with caution. + +.. code-block:: json + + { + "gyro-read-count": 50, + "gyro-read-delay": 3150, + "gyro-moving-threashold": 500, + "formula-max-deviation": 1.6, + "wifi-portaltimeout": 120, + "formula-calibration-temp": 20, + "int-http1": 0, + "int-http2": 0, + "int-http3": 0, + "int-influx": 0, + "int-mqtt": 0 + } + +POST: /api/config/advanced +========================== + +Same parameters as above. + +Payload should be in standard format used for posting a form + + GET: /api/clearwifi =================== @@ -173,7 +216,7 @@ Trigger a push on one of the targets, used to validate the configuration from th Requires to parameters to function /api/test/push?id=&format= -* ``format`` defines which endpoint to test, valid values are; http-1, http-2, brewfather, influxdb, mqtt +* ``format`` defines which endpoint to test, valid values are; http-1, http-2, http-3, influxdb, mqtt The response is an json message with the following values. @@ -223,7 +266,6 @@ Payload should be in standard format used for posting a form. Such as as: `id=va http-push-h2= http-push2-h1= http-push2-h2= - brewfather-push= influxdb2-push=http://192.168.1.50:8086 influxdb2-org= influxdb2-bucket= @@ -282,8 +324,8 @@ POST: /api/config/formula Used to update formula calculation data via an HTTP POST command. Payload is in JSON format. -* ``a1``-``a4`` are the angles/tilt readings (up to 5 are currently supported) -* ``g1``-``g4`` are the corresponding gravity reaadings (in SG) +* ``a1``-``a8`` are the angles/tilt readings (up to 5 are currently supported) +* ``g1``-``g8`` are the corresponding gravity reaadings (in SG) Payload should be in standard format used for posting a form. Such as as: `id=value&mdns=value` etc. Key value pairs are shown below. @@ -295,11 +337,17 @@ Payload should be in standard format used for posting a form. Such as as: `id=va a3=58 a4=0 a5=0 + a6=0 + a7=0 + a8=0 g1=1.000 g2=1.053 g3=1.062 g4=1 g5=1 + g6=1 + g7=1 + g8=1 Calling the API's from Python @@ -346,7 +394,6 @@ The requests package converts the json to standard form post format. "http-push-h2": "", "http-push2-h1": "" "http-push2-h2": "", - "brewfather-push": "", "influxdb2-push": "", "influxdb2-org": "", "influxdb2-bucket": "", @@ -383,10 +430,16 @@ The requests package converts the json to standard form post format. "a3": 58, "a4": 0, "a5": 0, + "a6": 0, + "a7": 0, + "a8": 0, "g1": 1.000, "g2": 1.053, "g3": 1.062, "g4": 1, - "g5": 1 + "g5": 1, + "g6": 1, + "g7": 1, + "g8": 1 } set_config( url, json ) diff --git a/src_docs/source/conf.py b/src_docs/source/conf.py index 9d48bb8..e0d837b 100644 --- a/src_docs/source/conf.py +++ b/src_docs/source/conf.py @@ -22,7 +22,7 @@ copyright = '2021-2022, Magnus Persson' author = 'Magnus Persson' # The full version, including alpha/beta/rc tags -release = '0.9.0' +release = '1.0.0' # -- General configuration --------------------------------------------------- diff --git a/src_docs/source/configuration.rst b/src_docs/source/configuration.rst index cb52218..456d8ec 100644 --- a/src_docs/source/configuration.rst +++ b/src_docs/source/configuration.rst @@ -121,12 +121,6 @@ If you add the prefix `https://` then the device will use SSL when sending data. The token is included in the default format for the HTTP GET url but can be used for any of the formats. For HTTP GET use can use this for an authorization token with for instance ubidots or blynk http api. -* **Brewfather URL:** - -Endpoint to send data via http to brewfather. Format used :ref:`data-formats-brewfather` - -SSL is not supported for this target. - * **HTTP Headers** .. image:: images/config-popup1.png @@ -281,3 +275,14 @@ This option gives you the possibility to install an new version of the firmware :alt: Update firmware +Advanded Settings ++++++++++++++++++ + +.. image:: images/config5.png + :width: 800 + :alt: Advanced Settings + +* **Header:** + +To be described + diff --git a/src_docs/source/data.rst b/src_docs/source/data.rst index 9036805..11aef37 100644 --- a/src_docs/source/data.rst +++ b/src_docs/source/data.rst @@ -26,7 +26,7 @@ This is the format used for standard http posts. "gravity": 1.0050, "angle": 45.34, "battery": 3.67, - "rssi": -12, + "RSSI": -12, "corr-gravity": 1.0050, "gravity-unit": "G", @@ -47,32 +47,13 @@ This is the format template used to create the json above. "gravity": ${gravity}, "angle": ${angle}, "battery": ${battery}, - "rssi": ${rssi}, + "RSSI": ${rssi}, "corr-gravity": ${corr-gravity}, "gravity-unit": "${gravity-unit}", "run-time": ${run-time} } -.. _data-formats-brewfather: - -Brewfather format -================= - -This is the format for Brewfather. See: `Brewfather API docs `_ - -.. code-block:: json - - { - "name" : "gravmon", - "temp": 20.5, - "temp_unit": "C", - "battery": 3.67, - "gravity": 1.0050, - "gravity_unit": "G", - } - - .. _data-formats-influxdb2: HTTP Get diff --git a/src_docs/source/index.rst b/src_docs/source/index.rst index bb39ac9..d826f83 100644 --- a/src_docs/source/index.rst +++ b/src_docs/source/index.rst @@ -7,7 +7,7 @@ Welcome to GravityMon's documentation! ###################################### .. note:: - This documentation reflects **v0.9**. Last updated 2022-04-23 + This documentation reflects **v1.0**. Last updated 2022-04-26 .. image:: images/gravitymon.gif :width: 800 diff --git a/src_docs/source/releases.rst b/src_docs/source/releases.rst index 435838a..14fbe0e 100644 --- a/src_docs/source/releases.rst +++ b/src_docs/source/releases.rst @@ -3,7 +3,23 @@ Releases ######## +v1.0.0 +------ +* Upgraded to bootstrap v5.1 for web pages. +* Added tooltips to all fields in user interface +* Removed brewfather option (can use standard HTTP options), the old apporach can still be used via changing format template. +* Added 5 more points for formula creation, so a total of 10 angles/gravity values can be stored. +* Added function on format page so that it's easy to copy a format template from the docs (simplify service integration). +* Added https support for Influxdb +* Added possibility to have variable push intervals for different endpoints so that different frequency can be used, for example; 5min mqtt, 15min brewfather. +* Added advanced settings to configuration for adjusting some internal values (gyro reads, accepted formula deviation, timeouts, moving detection etc). +* Added additional http error codes to troubleshooting documentation * Installation instructions updated on how to find the device after wifi has been configured. +* Documentation on brewfather has been updated to adress SG/Plato conversion +* BUG: Fixed issue in formula calculation in case there were a gap in the data series +* BUG: Field name for wifi strenght changed from "rssi" to "RSSI" + +* TODO: Fix documentation for advanced settings. v0.9.0 ------ diff --git a/src_docs/source/services.rst b/src_docs/source/services.rst index f3c2a1a..f5e9ca9 100644 --- a/src_docs/source/services.rst +++ b/src_docs/source/services.rst @@ -10,14 +10,26 @@ Brewfather Brewfather is an all in one service that allows you to manage you recepies and brews. -.. tip:: +**Option 1** - iSpindle Endpoint - The integration named Brewfather is uses the custom stream endpoint in brewfather not the standard iSpindle - endpoint. You can use the iSpindle endpoint as well. In that case just use the http-1 or http-2 fields. +This opion makes use of the standard http (1 or 2) endpoints in the push section. If you are using SG then the device name needs to end with [SG] or brewfather will assume +that the data is in plato. You can also modify the format template using the following options: -**Option 1** - Custom Stream +Update the following part `"gravity": ${gravity-plato},` or `"name" : "${mdns}[SG]",`` -This option makes use of the push endpoint called Brewfather in the UI. Just enter the http stream adress found +This makes use of the standard format template, no changes needed. + +.. code-block:: + + http://log.brewfather.net/ispindel?id= + + +Documentation on this can be found under `Brewfather iSpindle Endpoint `_ + + +**Option 2** - Custom Stream + +This option makes use of the http push endpoint with a custom format template. Just enter the http stream adress found on brewfather, not other settings are needed. The stream endpoint URL has the following format: .. code-block:: @@ -48,18 +60,6 @@ The implementation is basically a http request with the following format templat } -**Option 2** - iSpindle Endpoint - -This opion makes use of the standard http (1 or 2) endpoints in the push section. If you are using SG then the device name needs to end with [SG] or brewfather will assume -that the data is in plato. The brewfather iSpindle endpoint has the following format: - -.. code-block:: - - http://log.brewfather.net/ispindel?id= - - -Documentation on this can be found under `Brewfather iSpindle Endpoint `_ - Fermentrack +++++++++++ diff --git a/src_docs/source/troubleshooting.rst b/src_docs/source/troubleshooting.rst index b441e8c..2fcbbde 100644 --- a/src_docs/source/troubleshooting.rst +++ b/src_docs/source/troubleshooting.rst @@ -31,7 +31,6 @@ Log errors Check the format for your custom header. This means it has not a correct format. * Influxdb push failed response -* Brewfather push failed response * HTTP push failed response All these errors are standard http error codes. This are the commone ones; @@ -41,6 +40,20 @@ Log errors * 403 - Forbidden. Could be an issue with token or URL. * 404 - Not found. Probably a wrong URL. + In some cases there can be negative error codes which have the following meaning: + + * -1 - Connection refused + * -2 - Send header failed + * -3 - Send payload failed + * -4 - Not connected + * -5 - Connection lost + * -6 - No stream + * -7 - No HTTP server + * -8 - Too little RAM available + * -9 - Error encoding + * -10 - Error writing to stream + * -11 - Read timeout + * MQTT push on failed error * -3 - Network failed connected diff --git a/test/adv.json b/test/adv.json new file mode 100644 index 0000000..dfda261 --- /dev/null +++ b/test/adv.json @@ -0,0 +1,15 @@ +{ + "gyro-read-count": 51, + "gyro-read-delay": 3151, + "gyro-moving-threashold": 501, + "formula-max-deviation": 1.7, + "wifi-portal-timeout": 121, + "wifi-connect-timeout": 21, + "formula-calibration-temp": 21, + "push-timeout": 10, + "int-http1": 1, + "int-http2": 2, + "int-http3": 3, + "int-influx": 4, + "int-mqtt": 5 +} \ No newline at end of file diff --git a/test/config.json b/test/config.json index 3383978..d56ecd1 100644 --- a/test/config.json +++ b/test/config.json @@ -3,7 +3,6 @@ "id": "7376ef", "ota-url": "http://192.168.1.100:80/firmware/gravmon/", "temp-format": "C", - "brewfather-push": "http://log.brewfather.net/stream?id=KfkJU43jUFfj", "http-push": "http://192.168.1.10:9090/api/v1/ZYfjlUNeiuyu9N/telemetry", "http-push-h1": "Auth: Basic T7IF9DD9fF3RDddE=", "http-push-h2": "Auth: Advanced T7IF9DD9fF3RDddE=", diff --git a/test/configure.py b/test/configure.py index 0d9bd41..9906e87 100644 --- a/test/configure.py +++ b/test/configure.py @@ -37,7 +37,6 @@ json = { "id": id, "http-push-h2": "", "http-push2-h1": "Content-Type: application/json", "http-push2-h2": "", - "brewfather-push": "", # Brewfather URL "influxdb2-push": "", # InfluxDB2 settings "influxdb2-org": "", "influxdb2-bucket": "", diff --git a/test/formula.json b/test/formula.json index abc1fe1..5bceaf1 100644 --- a/test/formula.json +++ b/test/formula.json @@ -6,9 +6,20 @@ "a2": 45, "a4": 55, "a5": 30, + "a6": 30, + "a7": 30, + "a8": 30, + "a9": 30, + "a10": 30, "g1": 1.000, "g3": 1.010, "g2": 1.025, "g4": 1.040, - "g5": 1.005 + "g5": 1.005, + "g6": 1.005, + "g7": 1.005, + "g8": 1.005, + "g9": 1.005, + "g10": 1.005, + "error": "" } \ No newline at end of file