Added ssl and plato

This commit is contained in:
Magnus Persson 2022-01-18 23:01:10 +01:00
parent 10163f3aa7
commit ddb34e129d
27 changed files with 556 additions and 315 deletions

View File

@ -86,19 +86,20 @@
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
<div class="card-body">
<form action="/api/formula" method="post">
<input type="text" name="gravity-format" id="gravity-format" hidden>
<input type="text" name="id" id="id" hidden>
<div class="row mb-3">
Here you can create your gravity formula by entering angles/tilt and the corresponding gravity (SG). These values
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.5 SG 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.
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.
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">#:</label>
<label class="col-sm-4 col-form-label">Angle/Tilt:</label>
<label class="col-sm-4 col-form-label">Gravity (SG):</label>
<label class="col-sm-4 col-form-label" id="gravity-header">Gravity (SG):</label>
</div>
<div class="form-group row">
@ -107,7 +108,7 @@
<input type="number" min="0" max="90" step="0.001" class="form-control" name="a1" id="a1">
</div>
<div class="col-sm-4">
<input type="number" min="1" max="2" step="0.0001" class="form-control" name="g1" id="g1">
<input type="number" min="0" max="26" step="0.0001" class="form-control" name="g1" id="g1">
</div>
</div>
@ -117,7 +118,7 @@
<input type="number" min="0" max="90" step="0.001" class="form-control" name="a2" id="a2">
</div>
<div class="col-sm-4">
<input type="number" min="1" max="2" step="0.0001" class="form-control" name="g2" id="g2">
<input type="number" min="0" max="26" step="0.0001" class="form-control" name="g2" id="g2">
</div>
</div>
@ -127,7 +128,7 @@
<input type="number" min="0" max="90" step="0.001" class="form-control" name="a3" id="a3">
</div>
<div class="col-sm-4">
<input type="number" min="1" max="2" step="0.0001" class="form-control" name="g3" id="g3">
<input type="number" min="0" max="26" step="0.0001" class="form-control" name="g3" id="g3">
</div>
</div>
@ -137,7 +138,7 @@
<input type="number" min="0" max="90" step="0.001" class="form-control" name="a4" id="a4">
</div>
<div class="col-sm-4">
<input type="number" min="1" max="2" step="0.0001" class="form-control" name="g4" id="g4">
<input type="number" min="0" max="26" step="0.0001" class="form-control" name="g4" id="g4">
</div>
</div>
@ -147,7 +148,7 @@
<input type="number" min="0" max="90" step="0.001" class="form-control" name="a5" id="a5">
</div>
<div class="col-sm-4">
<input type="number" min="1" max="2" step="0.0001" class="form-control" name="g5" id="g5">
<input type="number" min="0" max="26" step="0.0001" class="form-control" name="g5" id="g5">
</div>
</div>
@ -233,28 +234,40 @@
</script>
<script type="text/javascript">
g1.onchange = setFourNumberDecimal
g2.onchange = setFourNumberDecimal
g3.onchange = setFourNumberDecimal
g4.onchange = setFourNumberDecimal
g5.onchange = setFourNumberDecimal
g1.onchange = setGravityDecimal
g2.onchange = setGravityDecimal
g3.onchange = setGravityDecimal
g4.onchange = setGravityDecimal
g5.onchange = setGravityDecimal
a1.onchange = setTwoNumberDecimal
a2.onchange = setTwoNumberDecimal
a3.onchange = setTwoNumberDecimal
a4.onchange = setTwoNumberDecimal
a5.onchange = setTwoNumberDecimal
a1.onchange = setAngleDecimal
a2.onchange = setAngleDecimal
a3.onchange = setAngleDecimal
a4.onchange = setAngleDecimal
a5.onchange = setAngleDecimal
window.onload = getConfig;
setButtonDisabled( true );
function setTwoNumberDecimal(event) {
function convertToPlato(sg) {
return 259-(259/sg);
}
function convertToSG(plato) {
return 259/(259-plato);
}
function setAngleDecimal(event) {
this.value = parseFloat(this.value).toFixed(2);
populateChart();
}
function setFourNumberDecimal(event) {
function setGravityDecimal(event) {
if(isPlato())
this.value = parseFloat(this.value).toFixed(1);
else
this.value = parseFloat(this.value).toFixed(4);
populateChart();
}
@ -267,6 +280,10 @@
chartDataCalc.push( { x: parseFloat(a), y: parseFloat(g) });
}
function isPlato() {
return $("#gravity-format").text() == "P";
}
function populateChart() {
chartDataCalc.length = 0
@ -278,6 +295,10 @@
formula=formula.replaceAll( "tilt^2", angle+"*"+angle );
formula=formula.replaceAll( "tilt", angle );
var g = eval( formula );
if(isPlato())
g = convertToPlato(g);
populateChartCalc( i, g );
}
@ -314,6 +335,23 @@
$("#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) );
} 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) );
}
$("#a1").val( parseFloat(cfg["a1"]).toFixed(2) );
$("#a2").val( parseFloat(cfg["a2"]).toFixed(2) );
@ -321,11 +359,9 @@
$("#a4").val( parseFloat(cfg["a4"]).toFixed(2) );
$("#a5").val( parseFloat(cfg["a5"]).toFixed(2) );
$("#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) );
if( cfg["error"]!="" ) {
showError(cfg["error"]);
}
populateChart();
})

File diff suppressed because one or more lines are too long

View File

@ -270,8 +270,25 @@
<div class="card-body">
<form action="/api/config/gravity" method="post">
<input type="text" name="id" id="id3" hidden>
<fieldset class="form-group row">
<legend class="col-form-label col-sm-2 float-sm-left pt-0">Gravity Format:</legend>
<div class="col-sm-4">
<div class="form-check">
<input class="form-check-input" type="radio" name="gravity-format" id="gravity-format-g" value="G" checked>
<label class="form-check-label" for="gravity-format-g">
SG
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="gravity-format" id="gravity-format-p" value="P">
<label class="form-check-label" for="gravity-format-p">
Plato
</label>
</div>
</div>
</fieldset>
<div class="form-group row">
<label for="gravity-formula" class="col-sm-2 col-form-label">Formula</label>
<label for="gravity-formula" class="col-sm-2 col-form-label">Formula (SG)</label>
<div class="col-sm-10">
<input type="text" maxlength="200" class="form-control" name="gravity-formula" id="gravity-formula">
</div>
@ -416,10 +433,10 @@
$("#id3").val(cfg["id"]);
$("#id4").val(cfg["id"]);
$("#mdns").val(cfg["mdns"]);
if( cfg["temp-format"] == "C" )
$("#temp-format-c").click();
else
$("#temp-format-f").click();
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"]);
$("#http-push").val(cfg["http-push"]);
$("#http-push2").val(cfg["http-push2"]);

File diff suppressed because one or more lines are too long

View File

@ -123,13 +123,19 @@
console.log( cfg );
$("#id").text(cfg["id"]);
$("#angle").text(cfg["angle"]);
if( cfg["gravity-format"] == "G")
$("#gravity").text(cfg["gravity"] + " SG");
else
$("#gravity").text(cfg["gravity"] + " °P");
$("#battery").text(cfg["battery"] + " V");
if( cfg["temp-format"] == "C")
$("#temp").text(cfg["temp-c"] + " C");
else
$("#temp").text(cfg["temp-f"] + " F");
//console.log(cfg["sleep-mode"] );
if( cfg["sleep-mode"] )
$("#sleep-mode").attr("checked", true );
else

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item active"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="" id="id" hidden></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Gravity:</div><div class="col-md-4 themed-grid-col bg-light" id="gravity">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Temperature:</div><div class="col-md-4 themed-grid-col bg-light" id="temp">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Angle/Tilt:</div><div class="col-md-4 themed-grid-col bg-light" id="angle">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Battery:</div><div class="col-md-4 themed-grid-col bg-light" id="battery">Loading...</div></div><div class="row mb-3"><div class="col-md-12 px-md-5 themed-grid-col bg-light custom-control custom-checkbox"><input type="checkbox" class="custom-control-input" name="sleep-mode" id="sleep-mode" disabled> <label class="custom-control-label" for="sleep-mode">Do not enter sleep mode when floating (check this if you are collecting angles/tilt for calibration).</label></div></div><hr class="my-4"></div><script type="text/javascript">function getStatus(){var e="/api/status";$("#spinner").show(),$.getJSON(e,function(e){console.log(e),$("#id").text(e.id),$("#angle").text(e.angle),$("#gravity").text(e.gravity+" SG"),$("#battery").text(e.battery+" V"),"C"==e["temp-format"]?$("#temp").text(e["temp-c"]+" C"):$("#temp").text(e["temp-f"]+" F"),e["sleep-mode"]?$("#sleep-mode").attr("checked",!0):$("#sleep-mode").attr("checked",!1),$("#sleep-mode").removeAttr("disabled")}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}function start(){setInterval(getStatus,3e3)}window.onload=start,$("#sleep-mode").click(function(e){console.log("Blocking sleep mode = "+$("#sleep-mode").is(":checked")),$.ajax({type:"POST",url:"/api/status/sleepmode",data:{id:$("#id").text(),"sleep-mode":$("#sleep-mode").is(":checked")},success:function(e){},error:function(e){showError("Could not update sleep mode for device.")}})})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item active"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="" id="id" hidden></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Gravity:</div><div class="col-md-4 themed-grid-col bg-light" id="gravity">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Temperature:</div><div class="col-md-4 themed-grid-col bg-light" id="temp">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Angle/Tilt:</div><div class="col-md-4 themed-grid-col bg-light" id="angle">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Battery:</div><div class="col-md-4 themed-grid-col bg-light" id="battery">Loading...</div></div><div class="row mb-3"><div class="col-md-12 px-md-5 themed-grid-col bg-light custom-control custom-checkbox"><input type="checkbox" class="custom-control-input" name="sleep-mode" id="sleep-mode" disabled> <label class="custom-control-label" for="sleep-mode">Do not enter sleep mode when floating (check this if you are collecting angles/tilt for calibration).</label></div></div><hr class="my-4"></div><script type="text/javascript">function getStatus(){var e="/api/status";$("#spinner").show(),$.getJSON(e,function(e){console.log(e),$("#id").text(e.id),$("#angle").text(e.angle),"G"==e["gravity-format"]?$("#gravity").text(e.gravity+" SG"):$("#gravity").text(e.gravity+" °P"),$("#battery").text(e.battery+" V"),"C"==e["temp-format"]?$("#temp").text(e["temp-c"]+" C"):$("#temp").text(e["temp-f"]+" F"),e["sleep-mode"]?$("#sleep-mode").attr("checked",!0):$("#sleep-mode").attr("checked",!1),$("#sleep-mode").removeAttr("disabled")}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}function start(){setInterval(getStatus,3e3)}window.onload=start,$("#sleep-mode").click(function(e){console.log("Blocking sleep mode = "+$("#sleep-mode").is(":checked")),$.ajax({type:"POST",url:"/api/status/sleepmode",data:{id:$("#id").text(),"sleep-mode":$("#sleep-mode").is(":checked")},success:function(e){},error:function(e){showError("Could not update sleep mode for device.")}})})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>

View File

@ -27,19 +27,14 @@ build_flags =
#-D DEBUG_ESP_HTTP_SERVER
#-D DEBUG_ESP_PORT=Serial
#-D DEBUG_ESP_WIFI
#-D DEBUG_ESP_SSL
#-D DEBUG_ESP_CORE
#-D SKIP_SLEEPMODE
-D CFG_DISABLE_LOGGING # Turn off verbose logging for some of the parts (too much will cause a crash) but also add space
-D GYRO_DISABLE_LOGGING
-D PUSH_DISABLE_LOGGING
-D TSEN_DISABLE_LOGGING
-D WEB_DISABLE_LOGGING
-D MAIN_DISABLE_LOGGING
-D USE_LITTLEFS=true
-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.6.0\""
-D CFG_APPVER="\"0.6.1\""
lib_deps = # Switched to forks for better version control.
# Using local copy of this library
#https://github.com/jrowberg/i2cdevlib.git#<document>
@ -65,20 +60,26 @@ extra_scripts =
script/create_versionjson.py
build_unflags =
${common_env_data.build_unflags}
# -D MAIN_DISABLE_LOGGING
-D WEB_DISABLE_LOGGING
-D PUSH_DISABLE_LOGGING
build_flags =
${common_env_data.build_flags}
-D PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS
#-D PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS
#-D SKIP_SLEEPMODE
-D DOUBLERESETDETECTOR_DEBUG=true
#-D DOUBLERESETDETECTOR_DEBUG=true
-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.
-D GYRO_DISABLE_LOGGING
-D CALC_DISABLE_LOGGING
-D HELPER_DISABLE_LOGGING
-D PUSH_DISABLE_LOGGING
-D TSEN_DISABLE_LOGGING
-D WIFI_DISABLE_LOGGING
-D WEB_DISABLE_LOGGING
-D MAIN_DISABLE_LOGGING
lib_deps =
${common_env_data.lib_deps}
board = ${common_env_data.board}
#build_type = debug # Using debug type crashes my devkit...
#build_type = debug
build_type = release
board_build.filesystem = littlefs
monitor_filters = esp8266_exception_decoder

View File

@ -29,7 +29,7 @@ SOFTWARE.
#include <helper.hpp>
#include <tempsensor.hpp>
#define FORMULA_MAX_DEVIATION 1.5
#define FORMULA_MAX_DEVIATION 1.6
//
// Use values to derive a formula
@ -46,7 +46,7 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer,
else if (fd.a[0] > 0 && fd.a[1] > 0 && fd.a[2] > 0)
noAngles = 3;
#if LOG_LEVEL == 6
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)
Log.verbose(
F("CALC: Trying to create formula using order = %d, found %d angles" CR),
order, noAngles);
@ -62,7 +62,7 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer,
// Returned value is 0 if no error
if (ret == 0) {
#if LOG_LEVEL == 6
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)
Log.verbose(F("CALC: Finshied processing data points." CR));
#endif
@ -83,7 +83,7 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer,
coeffs[1]);
}
#if LOG_LEVEL == 6
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)
Log.verbose(F("CALC: Formula: %s" CR), formulaBuffer);
#endif
@ -96,7 +96,14 @@ 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 > FORMULA_MAX_DEVIATION) valid = false;
if (dev * 1000 > FORMULA_MAX_DEVIATION) {
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)
char s[10];
snprintf(&s[0], sizeof(s), "%.8f", dev);
Log.verbose(F("CALC: Deviation is: %s" CR), &s[0]);
#endif
valid = false;
}
}
if (!valid) {
@ -121,13 +128,13 @@ double calculateGravity(double angle, double temp, const char *tempFormula) {
const char *formula = myConfig.getGravityFormula();
if (tempFormula != 0) {
#if LOG_LEVEL == 6
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)
Log.verbose(F("CALC: Using temporary formula." CR));
#endif
formula = tempFormula;
}
#if LOG_LEVEL == 6
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)
Log.verbose(F("CALC: Calculating gravity for angle %F, temp %F." CR), angle,
temp);
Log.verbose(F("CALC: Formula %s." CR), formula);
@ -146,8 +153,10 @@ double calculateGravity(double angle, double temp, const char *tempFormula) {
double g = te_eval(expr);
te_free(expr);
#if LOG_LEVEL == 6
Log.verbose(F("CALC: Calculated gravity is %F." CR), g);
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)
char s[10];
snprintf(&s[0], sizeof(s), "%.8f", g);
Log.verbose(F("CALC: Calculated gravity is %s." CR), &s[0]);
#endif
return g;
}
@ -158,18 +167,20 @@ double calculateGravity(double angle, double temp, const char *tempFormula) {
//
// Do a standard gravity temperature correction. This is a simple way to adjust
// for differnt worth temperatures
// for differnt worth temperatures. This function uses C as temperature.
//
double gravityTemperatureCorrection(double gravity, double temp,
char tempFormat, double calTemp) {
#if LOG_LEVEL == 6
// Source: https://homebrewacademy.com/hydrometer-temperature-correction/
//
double gravityTemperatureCorrectionC(double gravity, double tempC,
double calTempC) {
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)
Log.verbose(F("CALC: Adjusting gravity based on temperature, gravity %F, "
"temp %F, calTemp %F." CR),
gravity, temp, calTemp);
gravity, tempC, calTempC);
#endif
// float tempF = convertCtoF(tempC);
// float calTempF = convertCtoF(calTempC);
if (tempFormat == 'C') temp = convertCtoF(temp);
double calTempF = convertCtoF(calTemp); // calTemp is in C
const char *formula =
"gravity*((1.00130346-0.000134722124*temp+0.00000204052596*temp^2-0."
"00000000232820948*temp^3)/"
@ -178,7 +189,7 @@ double gravityTemperatureCorrection(double gravity, double temp,
// Store variable names and pointers.
te_variable vars[] = {
{"gravity", &gravity}, {"temp", &temp}, {"cal", &calTempF}};
{"gravity", &gravity}, {"temp", &tempC}, {"cal", &calTempC}};
int err;
// Compile the expression with variables.
@ -188,8 +199,10 @@ double gravityTemperatureCorrection(double gravity, double temp,
double g = te_eval(expr);
te_free(expr);
#if LOG_LEVEL == 6
Log.verbose(F("CALC: Corrected gravity is %F." CR), g);
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)
char s[10];
snprintf(&s[0], sizeof(s), "%.8f", g);
Log.verbose(F("CALC: Corrected gravity is %s." CR), &s[0]);
#endif
return g;
}

View File

@ -26,16 +26,16 @@ SOFTWARE.
// Includes
#include <config.hpp>
#include <helper.hpp>
#define ERR_FORMULA_NOTENOUGHVALUES -1
#define ERR_FORMULA_INTERNAL -2
#define ERR_FORMULA_UNABLETOFFIND -3
// Functions
double calculateGravity(double angle, double temp, const char *tempFormula = 0);
double gravityTemperatureCorrection(double gravity, double temp,
char tempFormat, double calTemp = 20);
double calculateGravity(double angle, double tempC,
const char *tempFormula = 0);
double gravityTemperatureCorrectionC(double gravity, double tempC,
double calTempC = 20);
int createFormula(RawFormulaData &fd, char *formulaBuffer,
int formulaBufferSize, int order);

View File

@ -48,7 +48,7 @@ Config::Config() {
setGravityFormat('G');
setSleepInterval(900); // 15 minutes
setVoltageFactor(1.59); // Conversion factor for battery
setTempSensorAdj(0.0);
setTempSensorAdjC(0.0);
setGravityTempAdj(false);
gyroCalibration = {0, 0, 0, 0, 0, 0};
formulaData = {{0, 0, 0, 0, 0}, {1, 1, 1, 1, 1}};
@ -82,7 +82,7 @@ void Config::createJson(DynamicJsonDocument& doc) {
doc[CFG_PARAM_VOLTAGEFACTOR] = getVoltageFactor();
doc[CFG_PARAM_GRAVITY_FORMULA] = getGravityFormula();
doc[CFG_PARAM_GRAVITY_FORMAT] = String(getGravityFormat());
doc[CFG_PARAM_TEMP_ADJ] = getTempSensorAdj();
doc[CFG_PARAM_TEMP_ADJ] = getTempSensorAdjC();
doc[CFG_PARAM_GRAVITY_TEMP_ADJ] = isGravityTempAdj();
doc[CFG_PARAM_GYRO_TEMP] = isGyroTemp();
@ -239,7 +239,7 @@ bool Config::loadFile() {
setGravityFormat(s.charAt(0));
}
if (!doc[CFG_PARAM_TEMP_ADJ].isNull())
setTempSensorAdj(doc[CFG_PARAM_TEMP_ADJ].as<float>());
setTempSensorAdjC(doc[CFG_PARAM_TEMP_ADJ].as<float>());
if (!doc[CFG_PARAM_GYRO_CALIBRATION]["ax"].isNull())
gyroCalibration.ax = doc[CFG_PARAM_GYRO_CALIBRATION]["ax"];
@ -318,7 +318,7 @@ void Config::debug() {
Log.verbose(F("CFG : Sleep interval; %d." CR), getSleepInterval());
Log.verbose(F("CFG : OTA; '%s'." CR), getOtaURL());
Log.verbose(F("CFG : Temp Format; %c." CR), getTempFormat());
Log.verbose(F("CFG : Temp Adj; %F." CR), getTempSensorAdj());
Log.verbose(F("CFG : Temp Adj; %F." CR), getTempSensorAdjC());
Log.verbose(F("CFG : VoltageFactor; %F." CR), getVoltageFactor());
Log.verbose(F("CFG : Gravity formula; '%s'." CR), getGravityFormula());
Log.verbose(F("CFG : Gravity format; '%c'." CR), getGravityFormat());

View File

@ -89,6 +89,7 @@ SOFTWARE.
#define CFG_PARAM_BATTERY "battery"
#define CFG_PARAM_SLEEP_MODE "sleep-mode"
#define CFG_PARAM_RSSI "rssi"
#define CFG_PARAM_ERROR "error"
// Used for holding sensordata or sensoroffsets
struct RawGyroData {
@ -118,41 +119,40 @@ class Config {
String id;
String mDNS;
String otaURL;
char tempFormat; // C, F
char tempFormat;
float voltageFactor;
float tempSensorAdj; // This value will be added to the read sensor value
float tempSensorAdjC;
int sleepInterval;
bool gyroTemp; // Experimental feature
bool gyroTemp;
// Wifi Config
String wifiSSID;
String wifiPASS;
// Push target settings
String brewfatherPushUrl; // URL For brewfather
String brewfatherPushUrl;
String httpPushUrl; // URL 1 for standard http
String httpPushUrl2; // URL 2 for standard http
String httpPushUrl;
String httpPushUrl2;
String influxDb2Url; // URL for InfluxDB v2
String influxDb2Org; // Organisation for InfluxDB v2
String influxDb2Bucket; // Bucket for InfluxDB v2
String influxDb2Token; // Auth Token for InfluxDB v2
String influxDb2Url;
String influxDb2Org;
String influxDb2Bucket;
String influxDb2Token;
String mqttUrl; // Server name
String mqttUrl;
String mqttTopic;
String mqttUser;
String mqttPass;
// Gravity and temperature calculations
String gravityFormula;
bool gravityTempAdj; // true, false
char gravityFormat; // G, P
bool gravityTempAdj;
char gravityFormat;
// Gyro calibration data
RawGyroData
gyroCalibration; // Holds the gyro calibration constants (6 * int16_t)
RawFormulaData formulaData; // Used for creating formula
// Gyro calibration and formula calculation data
RawGyroData gyroCalibration;
RawFormulaData formulaData;
void debug();
void formatFileSystem();
@ -273,11 +273,13 @@ class Config {
char getTempFormat() { return tempFormat; }
void setTempFormat(char c) {
if (c == 'C' || c == 'F') {
tempFormat = c;
saveNeeded = true;
}
bool isTempC() { return tempFormat == 'C' ? false : true; }
bool isTempF() { return tempFormat == 'F' ? false : true; }
}
bool isTempC() { return tempFormat == 'C'; }
bool isTempF() { return tempFormat == 'F'; }
float getVoltageFactor() { return voltageFactor; }
void setVoltageFactor(float f) {
@ -289,13 +291,17 @@ class Config {
saveNeeded = true;
}
float getTempSensorAdj() { return tempSensorAdj; }
void setTempSensorAdj(float f) {
tempSensorAdj = f;
float getTempSensorAdjC() { return tempSensorAdjC; }
void setTempSensorAdjC(float f) {
tempSensorAdjC = f;
saveNeeded = true;
}
void setTempSensorAdj(String s) {
tempSensorAdj = s.toFloat();
void setTempSensorAdjC(String s) {
tempSensorAdjC = s.toFloat();
saveNeeded = true;
}
void setTempSensorAdjF(String s) {
tempSensorAdjC = convertFtoC(s.toFloat());
saveNeeded = true;
}
@ -313,11 +319,13 @@ class Config {
char getGravityFormat() { return gravityFormat; }
void setGravityFormat(char c) {
if (c == 'G' || c == 'P') {
gravityFormat = c;
saveNeeded = true;
}
bool isGravitySG() { return gravityFormat == 'G' ? false : true; }
bool isGravityPlato() { return gravityFormat == 'P' ? false : true; }
}
bool isGravitySG() { return gravityFormat == 'G'; }
bool isGravityPlato() { return gravityFormat == 'P'; }
const RawGyroData& getGyroCalibration() { return gyroCalibration; }
void setGyroCalibration(const RawGyroData& r) {

View File

@ -24,19 +24,39 @@ SOFTWARE.
#include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h>
#include <config.hpp>
#include <gyro.hpp>
#include <helper.hpp>
#include <tempsensor.hpp>
#include <wifi.hpp>
SerialDebug mySerial;
BatteryVoltage myBatteryVoltage;
//
// Convert sg to plato
//
double convertToPlato(double sg) { return 259 - (259 / sg); }
//
// Convert plato to sg
//
double convertToSG(double plato) { return 259 / (259 - plato); }
//
// Conversion to F
//
float convertCtoF(float c) { return (c * 1.8) + 32.0; }
//
// Conversion to C
//
float convertFtoC(float f) { return (f - 32.0) / 1.8; }
//
// Print the heap information.
//
void printHeap() {
#if LOG_LEVEL == 6
#if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING)
Log.verbose(F("HELP: Heap %d kb, HeapFrag %d %%, FreeSketch %d kb." CR),
ESP.getFreeHeap() / 1024, ESP.getHeapFragmentation(),
ESP.getFreeSketchSpace() / 1024);
@ -47,7 +67,7 @@ void printHeap() {
// Enter deep sleep for the defined duration (Argument is seconds)
//
void deepSleep(int t) {
#if LOG_LEVEL == 6
#if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING)
Log.verbose(F("HELP: Entering sleep mode for %ds." CR), t);
#endif
uint32_t wake = t * 1000000;
@ -106,7 +126,7 @@ void BatteryVoltage::read() {
float factor = myConfig.getVoltageFactor(); // Default value is 1.63
int v = analogRead(A0);
batteryLevel = ((3.3 / 1023) * v) * factor;
#if LOG_LEVEL == 6
#if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING)
Log.verbose(
F("BATT: Reading voltage level. Factor=%F Value=%d, Voltage=%F." CR),
factor, v, batteryLevel);
@ -177,14 +197,13 @@ void PerfLogging::print() {
void PerfLogging::pushInflux() {
if (!myConfig.isInfluxDb2Active()) return;
WiFiClient client;
HTTPClient http;
String serverPath =
String(myConfig.getInfluxDb2PushUrl()) +
"/api/v2/write?org=" + String(myConfig.getInfluxDb2PushOrg()) +
"&bucket=" + String(myConfig.getInfluxDb2PushBucket());
http.begin(client, serverPath);
http.begin(myWifi.getWifiClient(), serverPath);
// Create body for influxdb2, format used
// key,host=mdns value=0.0
@ -225,7 +244,7 @@ void PerfLogging::pushInflux() {
// Log.notice(F("PERF: data %s." CR), body.c_str() );
#if LOG_LEVEL == 6
#if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING)
Log.verbose(F("PERF: url %s." CR), serverPath.c_str());
Log.verbose(F("PERF: data %s." CR), body.c_str());
#endif
@ -236,15 +255,18 @@ void PerfLogging::pushInflux() {
int httpResponseCode = http.POST(body);
if (httpResponseCode == 204) {
#if !defined(HELPER_DISABLE_LOGGING)
Log.notice(
F("PERF: InfluxDB2 push performance data successful, response=%d" CR),
httpResponseCode);
#endif
} else {
Log.error(F("PERF: InfluxDB2 push performance data failed, response=%d" CR),
httpResponseCode);
}
http.end();
myWifi.closeWifiClient();
}
#endif // COLLECT_PERFDATA

View File

@ -33,6 +33,12 @@ void deepSleep(int t);
// Show build options
void printBuildOptions();
// Data conversion
double convertToPlato(double sg);
double convertToSG(double plato);
float convertCtoF(float c);
float convertFtoC(float f);
// Float to String
char* convertFloatToString(float f, char* buf, int dec = 2);
float reduceFloatPrecision(float f, int dec = 2);

View File

@ -63,14 +63,18 @@ void checkSleepMode(float angle, float volt) {
if (!g.ax && !g.ay && !g.az && !g.gx && !g.gy && !g.gz) {
// Will not enter sleep mode if: no calibration data
#if !defined(MAIN_DISABLE_LOGGING)
Log.notice(
F("MAIN: Missing calibration data, so forcing webserver to be "
"active." CR));
#endif
runMode = RunMode::configurationMode;
} else if (sleepModeAlwaysSkip) {
// Check if the flag from the UI has been set, the we force configuration
// mode.
#if !defined(MAIN_DISABLE_LOGGING)
Log.notice(F("MAIN: Sleep mode disabled from web interface." CR));
#endif
runMode = RunMode::configurationMode;
} else if ((volt < 4.15 && (angle > 85 && angle < 95)) || (volt > 4.15)) {
runMode = RunMode::configurationMode;
@ -80,14 +84,18 @@ void checkSleepMode(float angle, float volt) {
switch (runMode) {
case RunMode::configurationMode:
#if !defined(MAIN_DISABLE_LOGGING)
Log.notice(F("MAIN: run mode CONFIG (angle=%F volt=%F)." CR), angle,
volt);
#endif
break;
case RunMode::wifiSetupMode:
break;
case RunMode::gravityMode:
#if !defined(MAIN_DISABLE_LOGGING)
Log.notice(F("MAIN: run mode GRAVITY (angle=%F volt=%F)." CR), angle,
volt);
#endif
break;
}
}
@ -207,22 +215,21 @@ bool loopReadGravity() {
stableGyroMillis = millis(); // Reset timer
LOG_PERF_START("loop-temp-read");
float temp = myTempSensor.getTempC(myConfig.isGyroTemp());
float tempC = myTempSensor.getTempC(myConfig.isGyroTemp());
LOG_PERF_STOP("loop-temp-read");
float gravity = calculateGravity(angle, temp);
float corrGravity =
gravityTemperatureCorrection(gravity, temp, myConfig.getTempFormat());
float gravity = calculateGravity(angle, tempC);
float corrGravity = gravityTemperatureCorrectionC(gravity, tempC);
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
Log.verbose(F("Main: Sensor values gyro angle=%F, temp=%F, gravity=%F, "
"corr=%F." CR),
angle, temp, gravity, corrGravity);
Log.verbose(F("Main: Sensor values gyro angle=%F, temp=%FC, gravity=%F, "
"corr_gravity=%F." CR),
angle, tempC, gravity, corrGravity);
#endif
LOG_PERF_START("loop-push");
// Force the transmission if we are going to sleep
myPushTarget.send(angle, gravity, corrGravity, temp,
myPushTarget.send(angle, gravity, corrGravity, tempC,
(millis() - runtimeMillis) / 1000,
runMode == RunMode::gravityMode ? true : false);
LOG_PERF_STOP("loop-push");

View File

@ -21,19 +21,21 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <ESP8266HTTPClient.h>
#include <MQTT.h>
#include <config.hpp>
#include <gyro.hpp>
#include <pushtarget.hpp>
#include <wifi.hpp>
PushTarget myPushTarget;
//
// Send the pressure value
// Send the data to targets
//
void PushTarget::send(float angle, float gravity, float corrGravity, float temp,
float runTime, bool force) {
void PushTarget::send(float angle, float gravity, float corrGravity,
float tempC, float runTime, bool force) {
uint32_t timePassed = abs((int32_t)(millis() - ms));
uint32_t interval = myConfig.getSleepInterval() * 1000;
@ -45,41 +47,37 @@ void PushTarget::send(float angle, float gravity, float corrGravity, float temp,
return;
}
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
Log.verbose(F("PUSH: Sending data." CR));
#endif
ms = millis();
if (myConfig.isBrewfatherActive()) {
LOG_PERF_START("push-brewfather");
sendBrewfather(angle, gravity, corrGravity, temp);
sendBrewfather(angle, gravity, corrGravity, tempC);
LOG_PERF_STOP("push-brewfather");
}
if (myConfig.isHttpActive()) {
LOG_PERF_START("push-http");
sendHttp(myConfig.getHttpPushUrl(), angle, gravity, corrGravity, temp,
sendHttp(myConfig.getHttpPushUrl(), angle, gravity, corrGravity, tempC,
runTime);
LOG_PERF_STOP("push-http");
}
if (myConfig.isHttpActive2()) {
LOG_PERF_START("push-http2");
sendHttp(myConfig.getHttpPushUrl2(), angle, gravity, corrGravity, temp,
sendHttp(myConfig.getHttpPushUrl2(), angle, gravity, corrGravity, tempC,
runTime);
LOG_PERF_STOP("push-http2");
}
if (myConfig.isInfluxDb2Active()) {
LOG_PERF_START("push-influxdb2");
sendInfluxDb2(angle, gravity, corrGravity, temp, runTime);
sendInfluxDb2(angle, gravity, corrGravity, tempC, runTime);
LOG_PERF_STOP("push-influxdb2");
}
if (myConfig.isMqttActive()) {
LOG_PERF_START("push-mqtt");
sendMqtt(angle, gravity, corrGravity, temp, runTime);
sendMqtt(angle, gravity, corrGravity, tempC, runTime);
LOG_PERF_STOP("push-mqtt");
}
}
@ -88,32 +86,43 @@ void PushTarget::send(float angle, float gravity, float corrGravity, float temp,
// Send to influx db v2
//
void PushTarget::sendInfluxDb2(float angle, float gravity, float corrGravity,
float temp, float runTime) {
float tempC, float runTime) {
#if !defined(PUSH_DISABLE_LOGGING)
Log.notice(
F("PUSH: Sending values to influxdb2 angle=%F, gravity=%F, temp=%F." CR),
angle, gravity, temp);
angle, gravity, tempC);
#endif
WiFiClient client;
HTTPClient http;
String serverPath =
String(myConfig.getInfluxDb2PushUrl()) +
"/api/v2/write?org=" + String(myConfig.getInfluxDb2PushOrg()) +
"&bucket=" + String(myConfig.getInfluxDb2PushBucket());
http.begin(client, serverPath);
http.begin(myWifi.getWifiClient(), serverPath);
float temp = myConfig.isTempC() ? tempC : convertCtoF(tempC);
gravity = myConfig.isGravityTempAdj() ? corrGravity : gravity;
// Create body for influxdb2
char buf[1024];
if (myConfig.isGravitySG()) {
snprintf(&buf[0], sizeof(buf),
"measurement,host=%s,device=%s,temp-format=%c,gravity-format=%s "
"gravity=%.4f,corr-gravity=%.4f,angle=%.2f,temp=%.2f,battery=%.2f,"
"rssi=%d\n",
// TODO: Add support for plato format
myConfig.getMDNS(), myConfig.getID(), myConfig.getTempFormat(), "SG",
myConfig.isGravityTempAdj() ? corrGravity : gravity, corrGravity,
angle, temp, myBatteryVoltage.getVoltage(), WiFi.RSSI());
myConfig.getMDNS(), myConfig.getID(), myConfig.getTempFormat(),
"G", gravity, corrGravity, angle, temp,
myBatteryVoltage.getVoltage(), WiFi.RSSI());
} else {
snprintf(&buf[0], sizeof(buf),
"measurement,host=%s,device=%s,temp-format=%c,gravity-format=%s "
"gravity=%.1f,corr-gravity=%.1f,angle=%.2f,temp=%.2f,battery=%.2f,"
"rssi=%d\n",
myConfig.getMDNS(), myConfig.getID(), myConfig.getTempFormat(),
"G", convertToPlato(gravity), convertToPlato(corrGravity), angle,
convertCtoF(temp), myBatteryVoltage.getVoltage(), WiFi.RSSI());
}
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
@ -134,17 +143,18 @@ void PushTarget::sendInfluxDb2(float angle, float gravity, float corrGravity,
}
http.end();
myWifi.closeWifiClient();
}
//
// Send data to brewfather
//
void PushTarget::sendBrewfather(float angle, float gravity, float corrGravity,
float temp) {
float tempC) {
#if !defined(PUSH_DISABLE_LOGGING)
Log.notice(
F("PUSH: Sending values to brewfather angle=%F, gravity=%F, temp=%F." CR),
angle, gravity, temp);
Log.notice(F("PUSH: Sending values to brewfather angle=%F, gravity=%F, "
"corr-gravity=%F, temp=%F." CR),
angle, gravity, corrGravity, tempC);
#endif
DynamicJsonDocument doc(300);
@ -165,21 +175,26 @@ void PushTarget::sendBrewfather(float angle, float gravity, float corrGravity,
// "battery": 4.98
// }
//
float temp = myConfig.isTempC() ? tempC : convertCtoF(tempC);
doc["name"] = myConfig.getMDNS();
doc["temp"] = reduceFloatPrecision(temp, 1);
doc["temp_unit"] = String(myConfig.getTempFormat());
doc["battery"] = reduceFloatPrecision(myBatteryVoltage.getVoltage(), 2);
// TODO: Add support for plato format
if (myConfig.isGravitySG()) {
doc["gravity"] = reduceFloatPrecision(
myConfig.isGravityTempAdj() ? corrGravity : gravity, 4);
} else {
doc["gravity"] = reduceFloatPrecision(
convertToPlato(myConfig.isGravityTempAdj() ? corrGravity : gravity), 1);
}
doc["gravity_unit"] = myConfig.isGravitySG() ? "G" : "P";
WiFiClient client;
HTTPClient http;
String serverPath = myConfig.getBrewfatherPushUrl();
// Your Domain name with URL path or IP address with path
http.begin(client, serverPath);
http.begin(myWifi.getWifiClient(), serverPath);
String json;
serializeJson(doc, json);
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
@ -200,6 +215,7 @@ void PushTarget::sendBrewfather(float angle, float gravity, float corrGravity,
}
http.end();
myWifi.closeWifiClient();
}
//
@ -207,24 +223,31 @@ void PushTarget::sendBrewfather(float angle, float gravity, float corrGravity,
//
void PushTarget::createIspindleFormat(DynamicJsonDocument &doc, float angle,
float gravity, float corrGravity,
float temp, float runTime) {
float tempC, float runTime) {
float temp = myConfig.isTempC() ? tempC : convertCtoF(tempC);
// Use iSpindle format for compatibility
doc["name"] = myConfig.getMDNS();
doc["ID"] = myConfig.getID();
doc["token"] = "gravmon";
doc["token"] = "gravitmon";
doc["interval"] = myConfig.getSleepInterval();
doc["temperature"] = reduceFloatPrecision(temp, 1);
doc["temp-units"] = String(myConfig.getTempFormat());
// TODO: Add support for plato format
if (myConfig.isGravitySG()) {
doc["gravity"] = reduceFloatPrecision(
myConfig.isGravityTempAdj() ? corrGravity : gravity, 4);
doc["corr-gravity"] = reduceFloatPrecision(corrGravity, 4);
} else {
doc["gravity"] = reduceFloatPrecision(
convertToPlato(myConfig.isGravityTempAdj() ? corrGravity : gravity), 1);
doc["corr-gravity"] = reduceFloatPrecision(convertToPlato(corrGravity), 1);
}
doc["angle"] = reduceFloatPrecision(angle, 2);
doc["battery"] = reduceFloatPrecision(myBatteryVoltage.getVoltage(), 2);
doc["rssi"] = WiFi.RSSI();
// Some additional information
doc["gravity-units"] = "SG";
doc["gravity-unit"] = myConfig.isGravitySG() ? "G" : "P";
doc["run-time"] = reduceFloatPrecision(runTime, 2);
}
@ -232,21 +255,26 @@ void PushTarget::createIspindleFormat(DynamicJsonDocument &doc, float angle,
// Send data to http target
//
void PushTarget::sendHttp(String serverPath, float angle, float gravity,
float corrGravity, float temp, float runTime) {
float corrGravity, float tempC, float runTime) {
#if !defined(PUSH_DISABLE_LOGGING)
Log.notice(
F("PUSH: Sending values to http angle=%F, gravity=%F, temp=%F." CR),
angle, gravity, temp);
Log.notice(F("PUSH: Sending values to http angle=%F, gravity=%F, "
"corr-gravity=%F, temp=%F." CR),
angle, gravity, corrGravity, tempC);
#endif
DynamicJsonDocument doc(256);
createIspindleFormat(doc, angle, gravity, corrGravity, temp, runTime);
createIspindleFormat(doc, angle, gravity, corrGravity, tempC, runTime);
WiFiClient client;
HTTPClient http;
// Your Domain name with URL path or IP address with path
http.begin(client, serverPath);
if (serverPath.startsWith("https://")) {
myWifi.getWifiClientSecure().setInsecure();
Log.notice(F("PUSH: HTTP, SSL enabled without validation." CR));
http.begin(myWifi.getWifiClientSecure(), serverPath);
} else {
http.begin(myWifi.getWifiClient(), serverPath);
}
String json;
serializeJson(doc, json);
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
@ -266,26 +294,36 @@ void PushTarget::sendHttp(String serverPath, float angle, float gravity,
}
http.end();
myWifi.closeWifiClient();
}
//
// Send data to http target
//
void PushTarget::sendMqtt(float angle, float gravity, float corrGravity,
float temp, float runTime) {
float tempC, float runTime) {
#if !defined(PUSH_DISABLE_LOGGING)
Log.notice(
F("PUSH: Sending values to mqtt angle=%F, gravity=%F, temp=%F." CR),
angle, gravity, temp);
Log.notice(F("PUSH: Sending values to mqtt angle=%F, gravity=%F, "
"corr-gravity=%F, temp=%F." CR),
angle, gravity, corrGravity, tempC);
#endif
DynamicJsonDocument doc(256);
createIspindleFormat(doc, angle, gravity, corrGravity, temp, runTime);
createIspindleFormat(doc, angle, gravity, corrGravity, tempC, runTime);
WiFiClient client;
MQTTClient mqtt(512); // Maximum message size
String url = myConfig.getMqttUrl();
if (url.endsWith(":8883")) {
// Allow secure channel, but without certificate validation
myWifi.getWifiClientSecure().setInsecure();
Log.notice(F("PUSH: MQTT, SSL enabled without validation." CR));
url.replace(":8883", "");
mqtt.begin(url.c_str(), 8883, myWifi.getWifiClientSecure());
} else {
mqtt.begin(myConfig.getMqttUrl(), myWifi.getWifiClient());
}
mqtt.begin(myConfig.getMqttUrl(), client);
mqtt.connect(myConfig.getMDNS(), myConfig.getMqttUser(),
myConfig.getMqttPass());
@ -306,6 +344,7 @@ void PushTarget::sendMqtt(float angle, float gravity, float corrGravity,
}
mqtt.disconnect();
myWifi.closeWifiClient();
}
// EOF

View File

@ -27,8 +27,6 @@ SOFTWARE.
// Includes
#include <Arduino.h>
#include <ArduinoJson.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h>
#include <helper.hpp>
@ -38,15 +36,15 @@ class PushTarget {
uint32_t ms; // Used to check that we do not post to often
void sendBrewfather(float angle, float gravity, float corrGravity,
float temp);
float tempC);
void sendHttp(String serverPath, float angle, float gravity,
float corrGravity, float temp, float runTime);
void sendInfluxDb2(float angle, float gravity, float corrGravity, float temp,
float corrGravity, float tempC, float runTime);
void sendInfluxDb2(float angle, float gravity, float corrGravity, float tempC,
float runTime);
void sendMqtt(float angle, float gravity, float corrGravity, float temp,
void sendMqtt(float angle, float gravity, float corrGravity, float tempC,
float runTime);
void createIspindleFormat(DynamicJsonDocument &doc, float angle,
float gravity, float corrGravity, float temp,
float gravity, float corrGravity, float tempC,
float runTime);
public:

View File

@ -30,11 +30,6 @@ SOFTWARE.
#include <helper.hpp>
#include <tempsensor.hpp>
//
// Conversion between C and F
//
float convertCtoF(float t) { return (t * 1.8) + 32.0; }
OneWire myOneWire(D6);
DallasTemperature mySensors(&myOneWire);
#define TEMPERATURE_PRECISION 9
@ -63,20 +58,12 @@ void TempSensor::setup() {
mySensors.setResolution(TEMPERATURE_PRECISION);
}
float t = myConfig.getTempSensorAdj();
// Set the temp sensor adjustment values
if (myConfig.isTempC()) {
tempSensorAdjF = t * 1.8; // Convert the adjustment value to C
tempSensorAdjC = t;
} else {
tempSensorAdjF = t;
tempSensorAdjC = t * 0.556; // Convert the adjustent value to F
}
tempSensorAdjC = myConfig.getTempSensorAdjC();
#if LOG_LEVEL == 6 && !defined(TSEN_DISABLE_LOGGING)
Log.verbose(F("TSEN: Adjustment values for temp sensor %F C, %F F." CR),
tempSensorAdjC, tempSensorAdjF);
Log.verbose(F("TSEN: Adjustment values for temp sensor %F C." CR),
tempSensorAdjC);
#endif
}
@ -101,7 +88,9 @@ float TempSensor::getValue(bool useGyro) {
// If we dont have sensors just return 0
if (!mySensors.getDS18Count()) {
Log.error(F("TSEN: No temperature sensors found. Skipping read." CR));
#if !defined(TSEN_DISABLE_LOGGING)
Log.notice(F("TSEN: No temperature sensors found. Skipping read." CR));
#endif
return -273;
}

View File

@ -25,13 +25,11 @@ SOFTWARE.
#define SRC_TEMPSENSOR_HPP_
// definitions
float convertCtoF(float t);
// classes
class TempSensor {
private:
bool hasSensor = false;
float tempSensorAdjF = 0;
float tempSensorAdjC = 0;
float getValue(bool useGyro);
@ -41,9 +39,6 @@ class TempSensor {
float getTempC(bool useGyro = false) {
return getValue(useGyro) + tempSensorAdjC;
}
float getTempF(bool useGyro = false) {
return convertCtoF(getValue(useGyro)) + tempSensorAdjF;
}
};
// Global instance created

View File

@ -72,16 +72,23 @@ void WebServer::webHandleConfig() {
doc[CFG_PARAM_PASS] = ""; // dont show the wifi password
double angle = myGyro.getAngle();
double temp = myTempSensor.getTempC(myConfig.isGyroTemp());
double gravity = calculateGravity(angle, temp);
double tempC = myTempSensor.getTempC(myConfig.isGyroTemp());
double gravity = calculateGravity(angle, tempC);
doc[CFG_PARAM_ANGLE] = reduceFloatPrecision(angle);
if (myConfig.isGravityTempAdj())
doc[CFG_PARAM_GRAVITY] = reduceFloatPrecision(
gravityTemperatureCorrection(gravity, temp, myConfig.getTempFormat()),
4);
else
doc[CFG_PARAM_GRAVITY_FORMAT] = String(myConfig.getGravityFormat());
if (myConfig.isGravityTempAdj()) {
gravity =
gravityTemperatureCorrectionC(gravity, tempC, myConfig.getTempFormat());
}
if (myConfig.isGravityPlato()) {
doc[CFG_PARAM_GRAVITY] = reduceFloatPrecision(convertToPlato(gravity), 1);
} else {
doc[CFG_PARAM_GRAVITY] = reduceFloatPrecision(gravity, 4);
}
doc[CFG_PARAM_BATTERY] = reduceFloatPrecision(myBatteryVoltage.getVoltage());
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
@ -215,22 +222,24 @@ void WebServer::webHandleStatus() {
DynamicJsonDocument doc(256);
double angle = myGyro.getAngle();
double temp = myTempSensor.getTempC(myConfig.isGyroTemp());
double gravity = calculateGravity(angle, temp);
double tempC = myTempSensor.getTempC(myConfig.isGyroTemp());
double gravity = calculateGravity(angle, tempC);
doc[CFG_PARAM_ID] = myConfig.getID();
doc[CFG_PARAM_ANGLE] = reduceFloatPrecision(angle);
if (myConfig.isGravityTempAdj())
doc[CFG_PARAM_GRAVITY] = reduceFloatPrecision(
gravityTemperatureCorrection(gravity, temp, myConfig.getTempFormat()),
4);
else
if (myConfig.isGravityTempAdj()) {
gravity = gravityTemperatureCorrectionC(gravity, tempC); //
}
if (myConfig.isGravityPlato()) {
doc[CFG_PARAM_GRAVITY] = reduceFloatPrecision(convertToPlato(gravity), 1);
} else {
doc[CFG_PARAM_GRAVITY] = reduceFloatPrecision(gravity, 4);
doc[CFG_PARAM_TEMP_C] = reduceFloatPrecision(temp, 1);
doc[CFG_PARAM_TEMP_F] =
reduceFloatPrecision(myTempSensor.getTempF(myConfig.isGyroTemp()), 1);
}
doc[CFG_PARAM_TEMP_C] = reduceFloatPrecision(tempC, 1);
doc[CFG_PARAM_TEMP_F] = reduceFloatPrecision(convertCtoF(tempC), 1);
doc[CFG_PARAM_BATTERY] = reduceFloatPrecision(myBatteryVoltage.getVoltage());
doc[CFG_PARAM_TEMPFORMAT] = String(myConfig.getTempFormat());
doc[CFG_PARAM_GRAVITY_FORMAT] = String(myConfig.getGravityFormat());
doc[CFG_PARAM_SLEEP_MODE] = sleepModeAlwaysSkip;
doc[CFG_PARAM_RSSI] = WiFi.RSSI();
@ -280,8 +289,7 @@ void WebServer::webHandleStatusSleepmode() {
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : sleep-mode=%s." CR),
server->arg(CFG_PARAM_SLEEP_MODE).c_str());
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
#endif
if (server->arg(CFG_PARAM_SLEEP_MODE).equalsIgnoreCase("true"))
@ -309,9 +317,7 @@ void WebServer::webHandleConfigDevice() {
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : mdns=%s, temp-format=%s." CR),
server->arg(CFG_PARAM_MDNS).c_str(),
server->arg(CFG_PARAM_TEMPFORMAT).c_str());
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
#endif
myConfig.setMDNS(server->arg(CFG_PARAM_MDNS).c_str());
@ -339,14 +345,7 @@ void WebServer::webHandleConfigPush() {
return;
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : http=%s,%s, bf=%s influx2=%s, %s, %s, %s." CR),
server->arg(CFG_PARAM_PUSH_HTTP).c_str(),
server->arg(CFG_PARAM_PUSH_HTTP2).c_str(),
server->arg(CFG_PARAM_PUSH_BREWFATHER).c_str(),
server->arg(CFG_PARAM_PUSH_INFLUXDB2).c_str(),
server->arg(CFG_PARAM_PUSH_INFLUXDB2_ORG).c_str(),
server->arg(CFG_PARAM_PUSH_INFLUXDB2_BUCKET).c_str(),
server->arg(CFG_PARAM_PUSH_INFLUXDB2_AUTH).c_str());
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
#endif
myConfig.setHttpPushUrl(server->arg(CFG_PARAM_PUSH_HTTP).c_str());
@ -369,6 +368,25 @@ void WebServer::webHandleConfigPush() {
LOG_PERF_STOP("webserver-api-config-push");
}
//
// Get string with all received arguments. Used for debugging only.
//
String WebServer::getRequestArguments() {
String debug;
for (int i = 0; i < server->args(); i++) {
if (!server->argName(i).equals(
"plain")) { // this contains all the arguments, we dont need that.
if (debug.length()) debug += ", ";
debug += server->argName(i);
debug += "=";
debug += server->arg(i);
}
}
return debug;
}
//
// Update gravity settings.
//
@ -386,11 +404,10 @@ void WebServer::webHandleConfigGravity() {
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : formula=%s, temp-corr=%s." CR),
server->arg(CFG_PARAM_GRAVITY_FORMULA).c_str(),
server->arg(CFG_PARAM_GRAVITY_TEMP_ADJ).c_str());
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
#endif
myConfig.setGravityFormat(server->arg(CFG_PARAM_GRAVITY_FORMAT).charAt(0));
myConfig.setGravityFormula(server->arg(CFG_PARAM_GRAVITY_FORMULA).c_str());
myConfig.setGravityTempAdj(
server->arg(CFG_PARAM_GRAVITY_TEMP_ADJ).equalsIgnoreCase("on") ? true
@ -418,15 +435,15 @@ void WebServer::webHandleConfigHardware() {
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : vf=%s, tempadj=%s, ota=%s gyrotemp=%s." CR),
server->arg(CFG_PARAM_VOLTAGEFACTOR).c_str(),
server->arg(CFG_PARAM_TEMP_ADJ).c_str(),
server->arg(CFG_PARAM_OTA).c_str(),
server->arg(CFG_PARAM_GYRO_TEMP).c_str());
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
#endif
myConfig.setVoltageFactor(server->arg(CFG_PARAM_VOLTAGEFACTOR).toFloat());
myConfig.setTempSensorAdj(server->arg(CFG_PARAM_TEMP_ADJ).toFloat());
if (myConfig.isTempC()) {
myConfig.setTempSensorAdjC(server->arg(CFG_PARAM_TEMP_ADJ));
} else {
myConfig.setTempSensorAdjF(server->arg(CFG_PARAM_TEMP_ADJ));
}
myConfig.setOtaURL(server->arg(CFG_PARAM_OTA).c_str());
myConfig.setGyroTemp(
server->arg(CFG_PARAM_GYRO_TEMP).equalsIgnoreCase("on") ? true : false);
@ -447,24 +464,24 @@ void WebServer::webHandleFormulaRead() {
const RawFormulaData& fd = myConfig.getFormulaData();
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : %F %F %F %F %F." CR), fd.a[0], fd.a[1], fd.a[2], fd.a[3],
fd.a[4]);
Log.verbose(F("WEB : %F %F %F %F %F." CR), fd.g[0], fd.g[1], fd.g[2], fd.g[3],
fd.g[4]);
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
#endif
doc[CFG_PARAM_ID] = myConfig.getID();
doc[CFG_PARAM_ANGLE] = reduceFloatPrecision(myGyro.getAngle());
doc[CFG_PARAM_GRAVITY_FORMAT] = String(myConfig.getGravityFormat());
doc[CFG_PARAM_GRAVITY_FORMULA] = "";
doc[CFG_PARAM_ERROR] = "";
switch (lastFormulaCreateError) {
case ERR_FORMULA_INTERNAL:
doc[CFG_PARAM_GRAVITY_FORMULA] = "Internal error creating formula.";
doc[CFG_PARAM_ERROR] = "Internal error creating formula.";
break;
case ERR_FORMULA_NOTENOUGHVALUES:
doc[CFG_PARAM_GRAVITY_FORMULA] = "Not enough values to create formula.";
doc[CFG_PARAM_ERROR] = "Not enough values to create formula.";
break;
case ERR_FORMULA_UNABLETOFFIND:
doc[CFG_PARAM_GRAVITY_FORMULA] =
doc[CFG_PARAM_ERROR] =
"Unable to find an accurate formula based on input.";
break;
default:
@ -478,11 +495,19 @@ void WebServer::webHandleFormulaRead() {
doc["a4"] = reduceFloatPrecision(fd.a[3], 2);
doc["a5"] = reduceFloatPrecision(fd.a[4], 2);
if (myConfig.isGravityPlato()) {
doc["g1"] = reduceFloatPrecision(convertToPlato(fd.g[0]), 1);
doc["g2"] = reduceFloatPrecision(convertToPlato(fd.g[1]), 1);
doc["g3"] = reduceFloatPrecision(convertToPlato(fd.g[2]), 1);
doc["g4"] = reduceFloatPrecision(convertToPlato(fd.g[3]), 1);
doc["g5"] = reduceFloatPrecision(convertToPlato(fd.g[4]), 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);
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
serializeJson(doc, Serial);
@ -512,13 +537,7 @@ void WebServer::webHandleFormulaWrite() {
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : angles=%F,%F,%F,%F,%F." CR), server->arg("a1").toFloat(),
server->arg("a2").toFloat(), server->arg("a3").toFloat(),
server->arg("a4").toFloat(), server->arg("a5").toFloat());
Log.verbose(F("WEB : gravity=%F,%F,%F,%F,%F." CR),
server->arg("g1").toFloat(), server->arg("g2").toFloat(),
server->arg("g3").toFloat(), server->arg("g4").toFloat(),
server->arg("g5").toFloat());
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
#endif
RawFormulaData fd;
@ -527,11 +546,21 @@ void WebServer::webHandleFormulaWrite() {
fd.a[2] = server->arg("a3").toDouble();
fd.a[3] = server->arg("a4").toDouble();
fd.a[4] = server->arg("a5").toDouble();
if (myConfig.isGravityPlato()) {
fd.g[0] = convertToSG(server->arg("g1").toDouble());
fd.g[1] = convertToSG(server->arg("g2").toDouble());
fd.g[2] = convertToSG(server->arg("g3").toDouble());
fd.g[3] = convertToSG(server->arg("g4").toDouble());
fd.g[4] = convertToSG(server->arg("g5").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();
}
myConfig.setFormulaData(fd);
int e;
@ -727,9 +756,8 @@ bool WebServer::setupWebServer() {
// called from main loop
//
void WebServer::loop() {
// Dont put serial debug output in this call
server->handleClient();
MDNS.update();
server->handleClient();
}
// EOF

View File

@ -65,6 +65,8 @@ class WebServer {
void webHandleDevice();
void webHandlePageNotFound();
String getRequestArguments();
// Inline functions.
void webReturnOK() { server->send(200); }
#if defined(EMBED_HTML)

View File

@ -110,6 +110,8 @@ String WifiConnection::getIPAddress() { return WiFi.localIP().toString(); }
// Additional method to detect double reset.
//
bool WifiConnection::isDoubleResetDetected() {
if (strlen(userSSID))
return false; // Ignore this if we have hardcoded settings.
return myDRD->detectDoubleReset();
}
@ -230,11 +232,17 @@ bool WifiConnection::updateFirmware() {
Log.verbose(F("WIFI: Updating firmware." CR));
#endif
WiFiClient client;
String serverPath = myConfig.getOtaURL();
serverPath += "firmware.bin";
HTTPUpdateResult ret;
HTTPUpdateResult ret = ESPhttpUpdate.update(client, serverPath);
if (serverPath.startsWith("https://")) {
myWifi.getWifiClientSecure().setInsecure();
Log.notice(F("WIFI: OTA, SSL enabled without validation." CR));
ret = ESPhttpUpdate.update(myWifi.getWifiClientSecure(), serverPath);
} else {
ret = ESPhttpUpdate.update(myWifi.getWifiClient(), serverPath);
}
switch (ret) {
case HTTP_UPDATE_FAILED:
@ -257,16 +265,21 @@ bool WifiConnection::updateFirmware() {
// Download and save file
//
void WifiConnection::downloadFile(const char *fname) {
#if LOG_LEVEL == 6
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
Log.verbose(F("WIFI: Download file %s." CR), fname);
#endif
WiFiClient client;
HTTPClient http;
String serverPath = myConfig.getOtaURL();
serverPath += fname;
// Your Domain name with URL path or IP address with path
http.begin(client, serverPath);
if (serverPath.startsWith("https://")) {
myWifi.getWifiClientSecure().setInsecure();
Log.notice(F("WIFI: OTA, SSL enabled without validation." CR));
http.begin(myWifi.getWifiClientSecure(), serverPath);
} else {
http.begin(myWifi.getWifiClient(), serverPath);
}
int httpResponseCode = http.GET();
if (httpResponseCode == 200) {
@ -279,22 +292,28 @@ void WifiConnection::downloadFile(const char *fname) {
httpResponseCode);
}
http.end();
myWifi.closeWifiClient();
}
//
// Check what firmware version is available over OTA
//
bool WifiConnection::checkFirmwareVersion() {
#if LOG_LEVEL == 6
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
Log.verbose(F("WIFI: Checking if new version exist." CR));
#endif
WiFiClient client;
HTTPClient http;
String serverPath = myConfig.getOtaURL();
serverPath += "version.json";
// Your Domain name with URL path or IP address with path
http.begin(client, serverPath);
if (serverPath.startsWith("https://")) {
myWifi.getWifiClientSecure().setInsecure();
Log.notice(F("WIFI: OTA, SSL enabled without validation." CR));
http.begin(myWifi.getWifiClientSecure(), serverPath);
} else {
http.begin(myWifi.getWifiClient(), serverPath);
}
// Send HTTP GET request
int httpResponseCode = http.GET();
@ -303,7 +322,7 @@ bool WifiConnection::checkFirmwareVersion() {
Log.notice(F("WIFI: Found version.json, response=%d" CR), httpResponseCode);
String payload = http.getString();
#if LOG_LEVEL == 6
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
Log.verbose(F("WIFI: Payload %s." CR), payload.c_str());
#endif
DynamicJsonDocument ver(300);
@ -311,7 +330,7 @@ bool WifiConnection::checkFirmwareVersion() {
if (err) {
Log.error(F("WIFI: Failed to parse version.json, %s" CR), err);
} else {
#if LOG_LEVEL == 6
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
Log.verbose(F("WIFI: Project %s version %s." CR),
(const char *)ver["project"], (const char *)ver["version"]);
#endif
@ -320,7 +339,7 @@ bool WifiConnection::checkFirmwareVersion() {
if (parseFirmwareVersionString(newVer, (const char *)ver["version"])) {
if (parseFirmwareVersionString(curVer, CFG_APPVER)) {
#if LOG_LEVEL == 6
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
Log.verbose(F("WIFI: OTA checking new=%d.%d.%d cur=%d.%d.%d" CR),
newVer[0], newVer[1], newVer[2], curVer[0], curVer[1],
curVer[2]);
@ -355,6 +374,7 @@ bool WifiConnection::checkFirmwareVersion() {
httpResponseCode);
}
http.end();
myWifi.closeWifiClient();
#if LOG_LEVEL == 6
Log.verbose(F("WIFI: OTA found new version %s." CR),
newFirmware ? "true" : "false");
@ -367,7 +387,7 @@ bool WifiConnection::checkFirmwareVersion() {
//
bool WifiConnection::parseFirmwareVersionString(int (&num)[3],
const char *version) {
#if LOG_LEVEL == 6
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
Log.verbose(F("WIFI: Parsing version number string %s." CR), version);
#endif
char temp[80];

View File

@ -26,11 +26,19 @@ SOFTWARE.
// Include
#include <Arduino.h>
#include <ESP8266WiFi.h>
// tcp cleanup, to avoid memory crash.
struct tcp_pcb;
extern struct tcp_pcb* tcp_tw_pcbs;
extern "C" void tcp_abort(struct tcp_pcb* pcb);
// classes
class WifiConnection {
private:
// WIFI
WiFiClient _client;
WiFiClientSecure _secureClient;
// OTA
bool newFirmware = false;
@ -53,6 +61,16 @@ class WifiConnection {
void startPortal();
void loop();
WiFiClient& getWifiClient() { return _client; }
WiFiClientSecure& getWifiClientSecure() { return _secureClient; }
void closeWifiClient() {
_client.stopAll();
_secureClient.stopAll();
// Cleanup memory allocated by open tcp connetions.
while (tcp_tw_pcbs) tcp_abort(tcp_tw_pcbs);
}
// OTA
bool updateFirmware();
bool checkFirmwareVersion();

View File

@ -83,19 +83,9 @@ This is a list of C++ defines that is used to enable/disable functions in the co
* - ACTIVATE_OTA
- Enables the OTA functionallity in the code
* - SKIP_SLEEPMODE
- THe device never goes into sleep mode, useful when developing.
* - CFG_DISABLE_LOGGING
- Done include verbose logging in Config class. Excessive logging may crash device.
* - GYRO_DISABLE_LOGGING
- Done include verbose logging in Gyro class. Excessive logging may crash device.
* - PUSH_DISABLE_LOGGING
- Done include verbose logging in PushTarget class. Excessive logging may crash device.
* - TSEN_DISABLE_LOGGING
- Done include verbose logging in TempSensor class. Excessive logging may crash device.
* - WEB_DISABLE_LOGGING
- Done include verbose logging in WebServer class. Excessive logging may crash device.
* - MAIN_DISABLE_LOGGING
- Done include verbose logging in Main class. Excessive logging may crash device.
- The device never goes into sleep mode, useful when developing.
* - xxx_DISABLE_LOGGING
- Done include verbose logging in the corresponding class. Excessive logging may crash device.
* - USE_LITTLEFS
- Use the new filesystem in Ardurino
* - EMBED_HTML

View File

@ -12,6 +12,7 @@ One of the following conditions will place the device in ``configuration mode``:
- Placed in horizontal mode 85-90 degrees
- Charger connected >4.15V
Status
======
@ -97,22 +98,36 @@ Push Settings
:width: 800
:alt: Push Settings
.. note::
When enabling SSL this will not validate the root CA of the remote service, this is a design decision based on two aspects. Enabling CA validation will take 3-4s extra on each connection which means way less
battery life, so the decision is to prioritize battery life over security. The data transmitted is not really that sensitive anyway so I belive this is a good balance.
* **HTTP URL 1:**
Endpoint to send data via http. Format used Format used :ref:`data-formats-ispindle`
If you add the prefix `https://` then the device will use SSL when sending data.
* **HTTP URL 2:**
Endpoint to send data via http. Format used :ref:`data-formats-ispindle`
If you add the prefix `https://` then the device will use SSL when sending data.
* **Brewfather URL:**
Endpoint to send data via http to brewfather. Format used :ref:`data-formats-brewfather`
SSL is not supported for this target.
* **Influx DB v2 URL:**
Endpoint to send data via http to InfluxDB. Format used :ref:`data-formats-influxdb2`
SSL is not supported for this target. Raise a issue on github if this is wanted.
* **Influx DB v2 Organisation:**
Name of organisation in Influx.
@ -129,6 +144,8 @@ Push Settings
IP or name of server to send data to. Format used :ref:`data-formats-ispindle`
If you add the suffix `:8883` to the server name, then the device will use SSL when sending data.
* **MQTT Topic:**
Name of topic to publish sensor readings to, iSpindle format is used.
@ -149,6 +166,10 @@ Gravity Settings
:width: 800
:alt: Gravity Settings
* **Gravity format:**
Gravity format can be eihter `SG` or `Plato`. The device will use SG Internally and convert to Plato when displaying data.
* **Gravity formula:**
Gravity formula is compatible with standard iSpindle formulas so any existing calculation option can be used. You can also use
@ -159,9 +180,9 @@ Gravity Settings
Will apply a temperature calibration formula to the gravity as a second step.
.. warning::
This formula assumes that the calibration has been done at 20C.
This formula assumes that the calibration has been done at 20°C / 68°F.
Formula used in temperature correction:
Formula used in temperature correction.
::
@ -199,6 +220,8 @@ Hardware Settings
For the OTA to work, place the following files (version.json + firmware.bin) at the location that you pointed out in OTA URL. If the version number in the json file is newer than in the
code the update will be done during startup.
If you have the previx `https://` then the device will use secure transfer without CA validation.
Example; OTA URL (don't forget trailing dash), the name of the file should be firmware.bin
.. code-block::
@ -325,7 +348,7 @@ 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)
* ``g1``-``g4`` are the corresponding gravity reaadings in SG or Plato depending on the device-format.
.. code-block:: json
@ -340,8 +363,9 @@ Retrive the data used for formula calculation data via an HTTP GET command. Payl
"g2": 1.053,
"g3": 1.062,
"g4": 1,
"g5": 1
"gravity-formula": "0.0*tilt^3+0.0*tilt^2+0.0017978*tilt+0.9436",
"g5": 1,
"gravity-format": "G",
"gravity-formula": "0.0*tilt^3+0.0*tilt^2+0.0017978*tilt+0.9436"
}
@ -350,7 +374,7 @@ POST: /api/config/device
Used to update device settings via an HTTP POST command. Payload is in JSON format.
* ``temp-format`` can be either ``C`` or ``F``
* ``temp-format`` can be either ``C`` (Celcius) or ``F`` (Farenheight)
.. code-block:: json
@ -391,6 +415,7 @@ POST: /api/config/gravity
Used to update gravity settings via an HTTP POST command. Payload is in JSON format.
* ``gravity-formula`` keywords ``temp`` and ``tilt`` are supported.
* ``gravity-format`` can be either ``G`` (SG) or ``P`` (PLATO)
.. note::
``gravity-temp-adjustment`` is defined as "on" or "off" when posting since this is the output values
@ -401,6 +426,7 @@ Used to update gravity settings via an HTTP POST command. Payload is in JSON for
{
"id": "ee1bfc",
"gravity-formula": "0.0*tilt^3+0.0*tilt^2+0.0017978*tilt+0.9436",
"gravity-format": "P",
"gravity-temp-adjustment": "off"
}
@ -500,6 +526,7 @@ present or the API call will fail.
url = "http://" + host + "/api/config/gravity"
json = { "id": id,
"gravity-formula": "",
"gravity-format": "P",
"gravity-temp-adjustment": "off" # Adjust gravity (on/off)
}
set_config( url, json )
@ -542,6 +569,7 @@ iSpindle format
This is the format used for standard http posts.
* ``corr-gravity`` is an extended parameter containing a temperature corrected gravity reading.
* ``gravity-format`` is an extended parameter containing the gravity format (G or P).
* ``run-time`` is an extended parameter containing the number of seconds the execution took.
.. code-block:: json
@ -554,10 +582,12 @@ This is the format used for standard http posts.
"temperature": 20.5,
"temp-units": "C",
"gravity": 1.0050,
"corr-gravity": 1.0050,
"angle": 45.34,
"battery": 3.67,
"rssi": -12,
"corr-gravity": 1.0050,
"gravity-unit": "G",
"run-time": 6
}
@ -567,14 +597,14 @@ This is the format used for standard http posts.
Brewfather format
=================
This is the format for Brewfather
This is the format for Brewfather. See: `Brewfather API docs <https://docs.brewfather.app/integrations/custom-stream>`_
.. code-block:: json
{
"name" : "gravmon",
"temp": 20.5,
"temp-unit": "C",
"temp_unit": "C",
"battery": 3.67,
"gravity": 1.0050,
"gravity_unit": "G",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -7,7 +7,7 @@ Welcome to GravityMon's documentation!
######################################
.. note::
This documentation reflects **v0.6**. Last updated 2022-01-15
This documentation reflects **v0.7**. Last updated 2022-01-18
GravityMon is a replacement firmare for the iSpindle firmware, it uses the same hardware configuration so
@ -23,9 +23,6 @@ The hardware design comes from the fantastic iSpindle project so that is not cov
My approach to this software is a little different from that the original ispindle firmware. The github repository can
be found here; `GravityMon on Github <https://github.com/mp-se/gravitymon>`_
.. note::
This software is in the early stages even though its more than one year old so if you find issues, please
open a ticket on github.
@ -46,6 +43,7 @@ The main differences:
* Visual graph showing how formula will be interpreted
* Using the temperature sensor in gyro instead of DS18B20 (faster)
* Built in performance measurements (used to optimise code)
* SSL support in standard HTTP and MQTT connections.
For a complete breakdown see the :ref:`functionallity`

View File

@ -3,6 +3,24 @@
Releases
########
v0.7.0
------
Development version (dev branch)
* SSL support for HTTP targets (no validation of CA)
* SSL support for MQTT targets (no validation of CA)
* SSL support for OTA (no validation of CA)
* Breaking change: To simplify the internal structure the
temp sensor adjustment is now stored in C. So if you have
enabled this function using F you will need to go into
the configuration and update the factor again.
* Added error handling for calibration page.
TODO:
Update docs, MQTT ssl is enabled using :8883 at end, http targets enables using prefix https://
Note! Brewfather don't support SSL.
v0.6.0
------