Added errlog, custom http headers
This commit is contained in:
parent
b106ebfa20
commit
63fd80e750
@ -170,17 +170,28 @@
|
||||
<div class="card-body">
|
||||
<form action="/api/config/push" method="post">
|
||||
<input type="text" name="id" id="id2" hidden>
|
||||
<input type="text" name="http-push-h1" id="http-push-h1" hidden>
|
||||
<input type="text" name="http-push-h2" id="http-push-h2" hidden>
|
||||
<input type="text" name="http-push2-h1" id="http-push2-h1" hidden>
|
||||
<input type="text" name="http-push2-h2" id="http-push2-h2" hidden>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="http-push" class="col-sm-2 col-form-label">Http URL 1:</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="col-sm-8">
|
||||
<input type="url" maxlength="120" class="form-control" name="http-push" id="http-push">
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<button type="button" class="btn btn-info" data-field1="#http-push-h1" data-field2="#http-push-h2" data-toggle="modal" data-target="#modal-http">Headers</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="http-push2" class="col-sm-2 col-form-label">Http URL 2:</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="col-sm-8">
|
||||
<input type="url" maxlength="120" class="form-control" name="http-push2" id="http-push2">
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<button type="button" class="btn btn-info" data-field1="#http-push2-h1" data-field2="#http-push2-h2" data-toggle="modal" data-target="#modal-http">Headers</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
@ -257,7 +268,7 @@
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-8 offset-sm-2">
|
||||
<button class="btn btn-secondary" id="format-btn">Format editor</button>
|
||||
<button class="btn btn-info" id="format-btn">Format editor</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -375,6 +386,47 @@
|
||||
<hr class="my-4">
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="modal-http" tabindex="-1" role="dialog" aria-labelledby="modal-header" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="modal-header">Define HTTP headers</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<label for="http-header" class="col-form-label">Header 1 (Header: value)</label>
|
||||
<input type="text" maxlength="100" class="form-control" id="header1">
|
||||
<label for="http-header" class="col-form-label">Header 2 (Header: value)</label>
|
||||
<input type="text" maxlength="100" class="form-control" id="header2">
|
||||
<input type="text" id="field1" hidden>
|
||||
<input type="text" id="field2" hidden>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$('#modal-http').on('show.bs.modal', function (event) {
|
||||
var button = $(event.relatedTarget)
|
||||
var field1 = button.data('field1')
|
||||
var field2 = button.data('field2')
|
||||
var modal = $(this)
|
||||
modal.find('.modal-body #header1').val($(field1).val())
|
||||
modal.find('.modal-body #header2').val($(field2).val())
|
||||
modal.find('.modal-body #field1').val(field1)
|
||||
modal.find('.modal-body #field2').val(field2)
|
||||
})
|
||||
$('#modal-http').on('hide.bs.modal', function (event) {
|
||||
var modal = $(this)
|
||||
field1 = modal.find('.modal-body #field1').val()
|
||||
field2 = modal.find('.modal-body #field2').val()
|
||||
$(field1).val(modal.find('.modal-body #header1').val())
|
||||
$(field2).val(modal.find('.modal-body #header2').val())
|
||||
})
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.onload = getConfig;
|
||||
|
||||
@ -452,7 +504,11 @@
|
||||
else $("#gravity-format-p").click();
|
||||
$("#ota-url").val(cfg["ota-url"]);
|
||||
$("#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"]);
|
||||
$("#brewfather-push").val(cfg["brewfather-push"]);
|
||||
$("#influxdb2-push").val(cfg["influxdb2-push"]);
|
||||
$("#influxdb2-org").val(cfg["influxdb2-org"]);
|
||||
|
File diff suppressed because one or more lines are too long
@ -88,7 +88,35 @@
|
||||
<div class="col-md-8 themed-grid-col bg-light">Device ID:</div>
|
||||
<div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div>
|
||||
</div>
|
||||
<hr class="my-4">
|
||||
|
||||
<div class="row mb-3">
|
||||
<a class="badge badge-primary" data-toggle="collapse" href="#collapseLog" role="button" aria-expanded="false" aria-controls="collapseLog" id="log-btn">
|
||||
View error log
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$("#log-btn").click(function(e){
|
||||
loadLog();
|
||||
});
|
||||
setInterval(function() {
|
||||
loadLog();
|
||||
}, 3000); //5 seconds
|
||||
|
||||
function loadLog() {
|
||||
$("#logContent").load("/log");
|
||||
//$("#logContent").load("/test/log");
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="collapse" id="collapseLog">
|
||||
<div class="card card-body">
|
||||
<pre><code id="logContent"></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
@ -100,7 +128,7 @@
|
||||
$('#spinner').show();
|
||||
$.getJSON(url, function (cfg) {
|
||||
console.log( cfg );
|
||||
$("#app-ver").text(cfg["app-ver"] + " (html 0.7.0)");
|
||||
$("#app-ver").text(cfg["app-ver"] + " (html 0.7.9)");
|
||||
$("#mdns").text(cfg["mdns"]);
|
||||
$("#id").text(cfg["id"]);
|
||||
})
|
||||
|
@ -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"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item active"><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><div class="spinner-border text-light" id="spinner" role="status"></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">×</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="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Current version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3" id="h-app-ver-new" hidden><div class="col-md-8 themed-grid-col bg-light">New version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver-new">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Host name:</div><div class="col-md-4 themed-grid-col bg-light" id="mdns">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Device ID:</div><div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div></div><hr class="my-4"></div><script type="text/javascript">function getConfig(){var n="/api/device";$("#spinner").show(),$.getJSON(n,function(n){console.log(n),$("#app-ver").text(n["app-ver"]+" (html 0.7.0)"),$("#mdns").text(n.mdns),$("#id").text(n.id)}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}window.onload=getConfig</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"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item active"><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><div class="spinner-border text-light" id="spinner" role="status"></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">×</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="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Current version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3" id="h-app-ver-new" hidden><div class="col-md-8 themed-grid-col bg-light">New version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver-new">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Host name:</div><div class="col-md-4 themed-grid-col bg-light" id="mdns">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Device ID:</div><div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div></div><div class="row mb-3"><a class="badge badge-primary" data-toggle="collapse" href="#collapseLog" role="button" aria-expanded="false" aria-controls="collapseLog" id="log-btn">View error log</a></div><script>function loadLog(){$("#logContent").load("/log")}$("#log-btn").click(function(o){loadLog()}),setInterval(function(){loadLog()},3e3)</script><div class="collapse" id="collapseLog"><div class="card card-body"><pre><code id="logContent"></code></pre></div></div><hr class="my-4"></div><script type="text/javascript">function getConfig(){var n="/api/device";$("#spinner").show(),$.getJSON(n,function(n){console.log(n),$("#app-ver").text(n["app-ver"]+" (html 0.7.0)"),$("#mdns").text(n.mdns),$("#id").text(n.id)}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}window.onload=getConfig</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
13
src/calc.cpp
13
src/calc.cpp
@ -49,7 +49,7 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer,
|
||||
#endif
|
||||
|
||||
if (!noAngles) {
|
||||
Log.error(F("CALC: Not enough values for deriving formula" CR));
|
||||
myLastErrors.addEntry(F("CALC: Not enough values for deriving formula"));
|
||||
return ERR_FORMULA_NOTENOUGHVALUES;
|
||||
} else {
|
||||
double coeffs[order + 1];
|
||||
@ -103,7 +103,7 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer,
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
Log.error(F("CALC: Deviation to large, formula rejected." CR));
|
||||
myLastErrors.addEntry(F("CALC: Deviation to large, formula rejected."));
|
||||
return ERR_FORMULA_UNABLETOFFIND;
|
||||
}
|
||||
|
||||
@ -112,7 +112,7 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer,
|
||||
}
|
||||
}
|
||||
|
||||
Log.error(F("CALC: Internal error finding formula." CR));
|
||||
myLastErrors.addEntry(F("CALC: Internal error finding formula."));
|
||||
return ERR_FORMULA_INTERNAL;
|
||||
}
|
||||
|
||||
@ -157,7 +157,7 @@ double calculateGravity(double angle, double temp, const char *tempFormula) {
|
||||
return g;
|
||||
}
|
||||
|
||||
Log.error(F("CALC: Failed to parse expression %d." CR), err);
|
||||
myLastErrors.addEntry("CALC: Failed to parse expression " + String(err));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -203,9 +203,8 @@ double gravityTemperatureCorrectionC(double gravity, double tempC,
|
||||
return g;
|
||||
}
|
||||
|
||||
Log.error(
|
||||
F("CALC: Failed to parse expression %d, no correction has been made." CR),
|
||||
err);
|
||||
myLastErrors.addEntry(
|
||||
"CALC: Failed to parse expression for gravity correction " + String(err));
|
||||
return gravity;
|
||||
}
|
||||
|
||||
|
@ -34,11 +34,11 @@ HardwareConfig myHardwareConfig;
|
||||
Config::Config() {
|
||||
// Assiging default values
|
||||
char buf[30];
|
||||
#if defined (ESP8266)
|
||||
#if defined(ESP8266)
|
||||
snprintf(&buf[0], sizeof(buf), "%6x", (unsigned int)ESP.getChipId());
|
||||
#else // defined (ESP32)
|
||||
#else // defined (ESP32)
|
||||
uint32_t chipId = 0;
|
||||
for (int i = 0; i < 17; i = i+8) {
|
||||
for (int i = 0; i < 17; i = i + 8) {
|
||||
chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
|
||||
}
|
||||
snprintf(&buf[0], sizeof(buf), "%6x", chipId);
|
||||
@ -54,10 +54,10 @@ Config::Config() {
|
||||
|
||||
setTempFormat('C');
|
||||
setGravityFormat('G');
|
||||
setSleepInterval(900); // 15 minutes
|
||||
#if defined (ESP8266)
|
||||
setSleepInterval(900); // 15 minutes
|
||||
#if defined(ESP8266)
|
||||
setVoltageFactor(1.59); // Conversion factor for battery on ESP8266
|
||||
#else // defined (ESP32)
|
||||
#else // defined (ESP32)
|
||||
setVoltageFactor(1.43); // Conversion factor for battery on ESP32
|
||||
#endif
|
||||
setTempSensorAdjC(0.0);
|
||||
@ -67,6 +67,8 @@ Config::Config() {
|
||||
_gyroTemp = false;
|
||||
_saveNeeded = false;
|
||||
_mqttPort = 1883;
|
||||
_httpHeader[0] = F("Content-Type: application/json");
|
||||
_http2Header[0] = F("Content-Type: application/json");
|
||||
}
|
||||
|
||||
//
|
||||
@ -81,8 +83,12 @@ void Config::createJson(DynamicJsonDocument& doc) {
|
||||
doc[PARAM_PASS] = getWifiPass();
|
||||
doc[PARAM_TEMPFORMAT] = String(getTempFormat());
|
||||
doc[PARAM_PUSH_BREWFATHER] = getBrewfatherPushUrl();
|
||||
doc[PARAM_PUSH_HTTP] = getHttpPushUrl();
|
||||
doc[PARAM_PUSH_HTTP2] = getHttpPushUrl2();
|
||||
doc[PARAM_PUSH_HTTP] = getHttpUrl();
|
||||
doc[PARAM_PUSH_HTTP_H1] = getHttpHeader(0);
|
||||
doc[PARAM_PUSH_HTTP_H2] = getHttpHeader(1);
|
||||
doc[PARAM_PUSH_HTTP2] = getHttp2Url();
|
||||
doc[PARAM_PUSH_HTTP2_H1] = getHttp2Header(0);
|
||||
doc[PARAM_PUSH_HTTP2_H2] = getHttp2Header(1);
|
||||
doc[PARAM_PUSH_INFLUXDB2] = getInfluxDb2PushUrl();
|
||||
doc[PARAM_PUSH_INFLUXDB2_ORG] = getInfluxDb2PushOrg();
|
||||
doc[PARAM_PUSH_INFLUXDB2_BUCKET] = getInfluxDb2PushBucket();
|
||||
@ -139,7 +145,7 @@ bool Config::saveFile() {
|
||||
File configFile = LittleFS.open(CFG_FILENAME, "w");
|
||||
|
||||
if (!configFile) {
|
||||
Log.error(F("CFG : Failed to open file " CFG_FILENAME " for save." CR));
|
||||
myLastErrors.addEntry(F("CFG : Failed to save configuration."));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -156,7 +162,6 @@ bool Config::saveFile() {
|
||||
configFile.close();
|
||||
|
||||
_saveNeeded = false;
|
||||
myConfig.debug();
|
||||
Log.notice(F("CFG : Configuration saved to " CFG_FILENAME "." CR));
|
||||
return true;
|
||||
}
|
||||
@ -170,15 +175,14 @@ bool Config::loadFile() {
|
||||
#endif
|
||||
|
||||
if (!LittleFS.exists(CFG_FILENAME)) {
|
||||
Log.error(
|
||||
F("CFG : Configuration file does not exist " CFG_FILENAME "." CR));
|
||||
myLastErrors.addEntry(F("CFG : Configuration file does not exist."));
|
||||
return false;
|
||||
}
|
||||
|
||||
File configFile = LittleFS.open(CFG_FILENAME, "r");
|
||||
|
||||
if (!configFile) {
|
||||
Log.error(F("CFG : Failed to open " CFG_FILENAME "." CR));
|
||||
myLastErrors.addEntry(F("CFG : Failed to load configuration."));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -194,8 +198,7 @@ bool Config::loadFile() {
|
||||
configFile.close();
|
||||
|
||||
if (err) {
|
||||
Log.error(F("CFG : Failed to parse " CFG_FILENAME " file, Err: %s, %d." CR),
|
||||
err.c_str(), doc.capacity());
|
||||
myLastErrors.addEntry(F("CFG : Failed to parse configuration (json)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -215,8 +218,16 @@ bool Config::loadFile() {
|
||||
if (!doc[PARAM_PUSH_BREWFATHER].isNull())
|
||||
setBrewfatherPushUrl(doc[PARAM_PUSH_BREWFATHER]);
|
||||
|
||||
if (!doc[PARAM_PUSH_HTTP].isNull()) setHttpPushUrl(doc[PARAM_PUSH_HTTP]);
|
||||
if (!doc[PARAM_PUSH_HTTP2].isNull()) setHttpPushUrl2(doc[PARAM_PUSH_HTTP2]);
|
||||
if (!doc[PARAM_PUSH_HTTP].isNull()) setHttpUrl(doc[PARAM_PUSH_HTTP]);
|
||||
if (!doc[PARAM_PUSH_HTTP_H1].isNull())
|
||||
setHttpHeader(doc[PARAM_PUSH_HTTP_H1], 0);
|
||||
if (!doc[PARAM_PUSH_HTTP_H2].isNull())
|
||||
setHttpHeader(doc[PARAM_PUSH_HTTP_H2], 1);
|
||||
if (!doc[PARAM_PUSH_HTTP2].isNull()) setHttp2Url(doc[PARAM_PUSH_HTTP2]);
|
||||
if (!doc[PARAM_PUSH_HTTP2_H1].isNull())
|
||||
setHttp2Header(doc[PARAM_PUSH_HTTP2_H1], 0);
|
||||
if (!doc[PARAM_PUSH_HTTP2_H2].isNull())
|
||||
setHttp2Header(doc[PARAM_PUSH_HTTP2_H2], 1);
|
||||
|
||||
if (!doc[PARAM_PUSH_INFLUXDB2].isNull())
|
||||
setInfluxDb2PushUrl(doc[PARAM_PUSH_INFLUXDB2]);
|
||||
@ -287,7 +298,6 @@ bool Config::loadFile() {
|
||||
if (!doc[PARAM_FORMULA_DATA]["g5"].isNull())
|
||||
_formulaData.g[4] = doc[PARAM_FORMULA_DATA]["g5"].as<double>();
|
||||
|
||||
myConfig.debug();
|
||||
_saveNeeded = false; // Reset save flag
|
||||
Log.notice(F("CFG : Configuration file " CFG_FILENAME " loaded." CR));
|
||||
return true;
|
||||
@ -317,34 +327,6 @@ void Config::checkFileSystem() {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Dump the configuration to the serial port
|
||||
//
|
||||
void Config::debug() {
|
||||
#if LOG_LEVEL == 6 && !defined(DISABLE_LOGGING)
|
||||
Log.verbose(F("CFG : Dumping configration " CFG_FILENAME "." CR));
|
||||
Log.verbose(F("CFG : ID; '%s'." CR), getID());
|
||||
Log.verbose(F("CFG : WIFI; '%s', '%s'." CR), getWifiSSID(), getWifiPass());
|
||||
Log.verbose(F("CFG : mDNS; '%s'." CR), getMDNS());
|
||||
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), 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());
|
||||
Log.verbose(F("CFG : Gravity temp adj; %s." CR),
|
||||
isGravityTempAdj() ? "true" : "false");
|
||||
Log.verbose(F("CFG : Gyro temp; %s." CR), isGyroTemp() ? "true" : "false");
|
||||
Log.verbose(F("CFG : Push brewfather; '%s'." CR), getBrewfatherPushUrl());
|
||||
Log.verbose(F("CFG : Push http; '%s'." CR), getHttpPushUrl());
|
||||
Log.verbose(F("CFG : Push http2; '%s'." CR), getHttpPushUrl2());
|
||||
Log.verbose(F("CFG : InfluxDb2; '%s', '%s', '%s', '%s'." CR),
|
||||
getInfluxDb2PushUrl(), getInfluxDb2PushOrg(),
|
||||
getInfluxDb2PushBucket(), getInfluxDb2PushToken());
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Save json document to file
|
||||
//
|
||||
@ -356,7 +338,7 @@ bool HardwareConfig::saveFile() {
|
||||
File configFile = LittleFS.open(CFG_HW_FILENAME, "w");
|
||||
|
||||
if (!configFile) {
|
||||
Log.error(F("CFG : Failed to open file " CFG_HW_FILENAME " for save." CR));
|
||||
myLastErrors.addEntry(F("CFG : Failed to write hardware configuration "));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -399,7 +381,7 @@ bool HardwareConfig::loadFile() {
|
||||
File configFile = LittleFS.open(CFG_HW_FILENAME, "r");
|
||||
|
||||
if (!configFile) {
|
||||
Log.error(F("CFG : Failed to open " CFG_HW_FILENAME "." CR));
|
||||
myLastErrors.addEntry(F("CFG : Failed to read hardware configuration "));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -415,9 +397,8 @@ bool HardwareConfig::loadFile() {
|
||||
configFile.close();
|
||||
|
||||
if (err) {
|
||||
Log.error(
|
||||
F("CFG : Failed to parse " CFG_HW_FILENAME " file, Err: %s, %d." CR),
|
||||
err.c_str(), doc.capacity());
|
||||
myLastErrors.addEntry(
|
||||
F("CFG : Failed to parse hardware configuration (json)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -104,8 +104,10 @@ class Config {
|
||||
// Push target settings
|
||||
String _brewfatherPushUrl;
|
||||
|
||||
String _httpPushUrl;
|
||||
String _httpPushUrl2;
|
||||
String _httpUrl;
|
||||
String _httpHeader[2];
|
||||
String _http2Url;
|
||||
String _http2Header[2];
|
||||
|
||||
String _influxDb2Url;
|
||||
String _influxDb2Org;
|
||||
@ -126,7 +128,6 @@ class Config {
|
||||
RawGyroData _gyroCalibration;
|
||||
RawFormulaData _formulaData;
|
||||
|
||||
void debug();
|
||||
void formatFileSystem();
|
||||
|
||||
public:
|
||||
@ -174,18 +175,29 @@ class Config {
|
||||
}
|
||||
|
||||
// Standard HTTP
|
||||
const char* getHttpPushUrl() { return _httpPushUrl.c_str(); }
|
||||
void setHttpPushUrl(String s) {
|
||||
_httpPushUrl = s;
|
||||
const char* getHttpUrl() { return _httpUrl.c_str(); }
|
||||
void setHttpUrl(String s) {
|
||||
_httpUrl = s;
|
||||
_saveNeeded = true;
|
||||
}
|
||||
bool isHttpActive() { return _httpPushUrl.length() ? true : false; }
|
||||
const char* getHttpPushUrl2() { return _httpPushUrl2.c_str(); }
|
||||
void setHttpPushUrl2(String s) {
|
||||
_httpPushUrl2 = s;
|
||||
const char* getHttpHeader(int idx) { return _httpHeader[idx].c_str(); }
|
||||
void setHttpHeader(String s, int idx) {
|
||||
_httpHeader[idx] = s;
|
||||
_saveNeeded = true;
|
||||
}
|
||||
bool isHttpActive2() { return _httpPushUrl2.length() ? true : false; }
|
||||
bool isHttpActive() { return _httpUrl.length() ? true : false; }
|
||||
|
||||
const char* getHttp2Url() { return _http2Url.c_str(); }
|
||||
void setHttp2Url(String s) {
|
||||
_http2Url = s;
|
||||
_saveNeeded = true;
|
||||
}
|
||||
const char* getHttp2Header(int idx) { return _http2Header[idx].c_str(); }
|
||||
void setHttp2Header(String s, int idx) {
|
||||
_http2Header[idx] = s;
|
||||
_saveNeeded = true;
|
||||
}
|
||||
bool isHttpActive2() { return _http2Url.length() ? true : false; }
|
||||
|
||||
// InfluxDB2
|
||||
const char* getInfluxDb2PushUrl() { return _influxDb2Url.c_str(); }
|
||||
|
@ -44,7 +44,7 @@ bool GyroSensor::setup() {
|
||||
// compilation difficulties
|
||||
|
||||
if (!accelgyro.testConnection()) {
|
||||
Log.error(F("GYRO: Failed to connect to MPU6050 (gyro)." CR));
|
||||
myLastErrors.addEntry(F("GYRO: Failed to connect to gyro"));
|
||||
_sensorConnected = false;
|
||||
} else {
|
||||
#if !defined(GYRO_DISABLE_LOGGING)
|
||||
@ -286,7 +286,7 @@ void GyroSensor::applyCalibration() {
|
||||
if ((_calibrationOffset.ax + _calibrationOffset.ay + _calibrationOffset.az +
|
||||
_calibrationOffset.gx + _calibrationOffset.gy + _calibrationOffset.gz) ==
|
||||
0) {
|
||||
Log.error(F("GYRO: No valid calibraion values exist, aborting." CR));
|
||||
myLastErrors.addEntry(F("GYRO: No valid calibration values, aborting"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -21,12 +21,12 @@ 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.
|
||||
*/
|
||||
#if defined (ESP8266)
|
||||
#if defined(ESP8266)
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#else // defined (ESP32)
|
||||
#include <WiFi.h>
|
||||
#else // defined (ESP32)
|
||||
#include <HTTPClient.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <config.hpp>
|
||||
@ -37,6 +37,7 @@ SOFTWARE.
|
||||
#include <wifi.hpp>
|
||||
|
||||
SerialDebug mySerial;
|
||||
ErrorFileLog myLastErrors;
|
||||
BatteryVoltage myBatteryVoltage;
|
||||
|
||||
//
|
||||
@ -59,16 +60,61 @@ float convertCtoF(float c) { return (c * 1.8) + 32.0; }
|
||||
//
|
||||
float convertFtoC(float f) { return (f - 32.0) / 1.8; }
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
ErrorFileLog::ErrorFileLog() {
|
||||
File errFile = LittleFS.open(ERR_FILENAME, "r");
|
||||
int i = 0;
|
||||
|
||||
if (errFile) {
|
||||
do {
|
||||
errors[i] = errFile.readStringUntil('\n');
|
||||
} while (errors[i++].length());
|
||||
errFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
const char* ErrorFileLog::getEntry(int idx) { return errors[idx].c_str(); }
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
void ErrorFileLog::addEntry(String err) {
|
||||
for (int i = (ERR_COUNT - 1); i > 0; i--) {
|
||||
errors[i] = errors[i - 1];
|
||||
}
|
||||
errors[0] = err;
|
||||
Log.errorln(err.c_str());
|
||||
save();
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
void ErrorFileLog::save() {
|
||||
File errFile = LittleFS.open(ERR_FILENAME, "w");
|
||||
if (errFile) {
|
||||
for (int i = 0; i < ERR_COUNT; i++) {
|
||||
errFile.println(errors[i]);
|
||||
}
|
||||
errFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Print the heap information.
|
||||
//
|
||||
void printHeap() {
|
||||
#if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING)
|
||||
#if defined (ESP8266)
|
||||
#if defined(ESP8266)
|
||||
Log.verbose(F("HELP: Heap %d kb, HeapFrag %d %%, FreeSketch %d kb." CR),
|
||||
ESP.getFreeHeap() / 1024, ESP.getHeapFragmentation(),
|
||||
ESP.getFreeSketchSpace() / 1024);
|
||||
#else // defined (ESP32)
|
||||
#else // defined (ESP32)
|
||||
Log.verbose(F("HELP: Heap %d kb, FreeSketch %d kb." CR),
|
||||
ESP.getFreeHeap() / 1024, ESP.getFreeSketchSpace() / 1024);
|
||||
#endif
|
||||
@ -141,11 +187,11 @@ void BatteryVoltage::read() {
|
||||
// An ESP8266 has a ADC range of 0-1023 and a maximum voltage of 3.3V
|
||||
// An ESP32 has an ADC range of 0-4095 and a maximum voltage of 3.3V
|
||||
|
||||
#if defined (ESP8266)
|
||||
#if defined(ESP8266)
|
||||
_batteryLevel = ((3.3 / 1023) * v) * factor;
|
||||
#else // defined (ESP32)
|
||||
#else // defined (ESP32)
|
||||
_batteryLevel = ((3.3 / 4095) * v) * factor;
|
||||
#endif
|
||||
#endif
|
||||
#if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING)
|
||||
Log.verbose(
|
||||
F("BATT: Reading voltage level. Factor=%F Value=%d, Voltage=%F." CR),
|
||||
@ -319,40 +365,40 @@ String urlencode(String str) {
|
||||
char c;
|
||||
char code0;
|
||||
char code1;
|
||||
for (int i =0; i < static_cast<int>(str.length()); i++) {
|
||||
for (int i = 0; i < static_cast<int>(str.length()); i++) {
|
||||
c = str.charAt(i);
|
||||
if (isalnum(c)){
|
||||
if (isalnum(c)) {
|
||||
encodedString += c;
|
||||
} else {
|
||||
code1 = (c & 0xf) + '0';
|
||||
if ((c & 0xf) >9) {
|
||||
code1 = (c & 0xf) - 10 + 'A';
|
||||
if ((c & 0xf) > 9) {
|
||||
code1 = (c & 0xf) - 10 + 'A';
|
||||
}
|
||||
c = (c>>4) & 0xf;
|
||||
c = (c >> 4) & 0xf;
|
||||
code0 = c + '0';
|
||||
if (c > 9) {
|
||||
code0 = c - 10 + 'A';
|
||||
code0 = c - 10 + 'A';
|
||||
}
|
||||
encodedString += '%';
|
||||
encodedString += code0;
|
||||
encodedString += code1;
|
||||
}
|
||||
}
|
||||
//Log.verbose(F("HELP: encode=%s" CR), encodedString.c_str());
|
||||
return encodedString;
|
||||
// Log.verbose(F("HELP: encode=%s" CR), encodedString.c_str());
|
||||
return encodedString;
|
||||
}
|
||||
|
||||
unsigned char h2int(char c) {
|
||||
if (c >= '0' && c <='9') {
|
||||
return((unsigned char)c - '0');
|
||||
if (c >= '0' && c <= '9') {
|
||||
return ((unsigned char)c - '0');
|
||||
}
|
||||
if (c >= 'a' && c <='f') {
|
||||
return((unsigned char)c - 'a' + 10);
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
return ((unsigned char)c - 'a' + 10);
|
||||
}
|
||||
if (c >= 'A' && c <='F') {
|
||||
return((unsigned char)c - 'A' + 10);
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
return ((unsigned char)c - 'A' + 10);
|
||||
}
|
||||
return(0);
|
||||
return (0);
|
||||
}
|
||||
|
||||
String urldecode(String str) {
|
||||
@ -360,7 +406,7 @@ String urldecode(String str) {
|
||||
char c;
|
||||
char code0;
|
||||
char code1;
|
||||
for (int i = 0; i < static_cast<int>(str.length()); i++){
|
||||
for (int i = 0; i < static_cast<int>(str.length()); i++) {
|
||||
c = str.charAt(i);
|
||||
if (c == '%') {
|
||||
i++;
|
||||
@ -372,9 +418,9 @@ String urldecode(String str) {
|
||||
} else {
|
||||
encodedString += c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Log.verbose(F("HELP: decode=%s" CR), encodedString.c_str());
|
||||
// Log.verbose(F("HELP: decode=%s" CR), encodedString.c_str());
|
||||
return encodedString;
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,9 @@ SOFTWARE.
|
||||
// Includes
|
||||
#include <main.hpp>
|
||||
|
||||
#define ERR_FILENAME "/error.log"
|
||||
#define ERR_COUNT 15
|
||||
|
||||
// Sleep mode
|
||||
void deepSleep(int t);
|
||||
|
||||
@ -59,6 +62,17 @@ class SerialDebug {
|
||||
static Logging* getLog() { return &Log; }
|
||||
};
|
||||
|
||||
class ErrorFileLog {
|
||||
private:
|
||||
String errors[ERR_COUNT];
|
||||
|
||||
public:
|
||||
ErrorFileLog();
|
||||
const char* getEntry(int idx);
|
||||
void addEntry(String error);
|
||||
void save();
|
||||
};
|
||||
|
||||
class BatteryVoltage {
|
||||
private:
|
||||
float _batteryLevel;
|
||||
@ -156,6 +170,7 @@ extern PerfLogging myPerfLogging;
|
||||
|
||||
// Global instance created
|
||||
extern SerialDebug mySerial;
|
||||
extern ErrorFileLog myLastErrors;
|
||||
extern BatteryVoltage myBatteryVoltage;
|
||||
|
||||
#endif // SRC_HELPER_HPP_
|
||||
|
18
src/main.cpp
18
src/main.cpp
@ -110,19 +110,19 @@ void setup() {
|
||||
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
||||
// Add a delay so that serial is started.
|
||||
// delay(3000);
|
||||
#if defined (ESP8266)
|
||||
#if defined(ESP8266)
|
||||
Log.verbose(F("Main: Reset reason %s." CR), ESP.getResetInfo().c_str());
|
||||
#else // defined (ESP32)
|
||||
#else // defined (ESP32)
|
||||
#endif
|
||||
#endif
|
||||
// Main startup
|
||||
#if defined (ESP8266)
|
||||
#if defined(ESP8266)
|
||||
Log.notice(F("Main: Started setup for %s." CR),
|
||||
String(ESP.getChipId(), HEX).c_str());
|
||||
#else // defined (ESP32)
|
||||
#else // defined (ESP32)
|
||||
char buf[20];
|
||||
uint32_t chipId = 0;
|
||||
for (int i = 0; i < 17; i = i+8) {
|
||||
for (int i = 0; i < 17; i = i + 8) {
|
||||
chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
|
||||
}
|
||||
snprintf(&buf[0], sizeof(buf), "%6x", chipId);
|
||||
@ -138,10 +138,10 @@ void setup() {
|
||||
LOG_PERF_STOP("main-config-load");
|
||||
|
||||
// Setup watchdog
|
||||
#if defined (ESP8266)
|
||||
#if defined(ESP8266)
|
||||
ESP.wdtDisable();
|
||||
ESP.wdtEnable(5000); // 5 seconds
|
||||
#else // defined (ESP32)
|
||||
#else // defined (ESP32)
|
||||
#endif
|
||||
|
||||
// No stored config, move to portal
|
||||
@ -173,7 +173,7 @@ void setup() {
|
||||
LOG_PERF_STOP("main-temp-setup");
|
||||
|
||||
if (!myGyro.setup()) {
|
||||
Log.error(F("Main: Failed to initialize the gyro." CR));
|
||||
myLastErrors.addEntry(F("MAIN: Failed to initialize the gyro"));
|
||||
} else {
|
||||
LOG_PERF_START("main-gyro-read");
|
||||
myGyro.read();
|
||||
@ -252,7 +252,7 @@ bool loopReadGravity() {
|
||||
LOG_PERF_STOP("loop-push");
|
||||
return true;
|
||||
} else {
|
||||
Log.error(F("Main: No gyro value." CR));
|
||||
myLastErrors.addEntry(F("MAIN: No gyro value"));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -22,10 +22,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(ESP8266)
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#else // defined (ESP32)
|
||||
#include <HTTPClient.h>
|
||||
#endif
|
||||
#include <MQTT.h>
|
||||
|
||||
@ -56,9 +54,14 @@ void PushTarget::send(float angle, float gravitySG, float corrGravitySG,
|
||||
|
||||
#if defined(ESP8266)
|
||||
if (ESP.getFreeContStack() < 1500) {
|
||||
Log.error(F("PUSH: Low on memory, skipping push since it will crasch. "
|
||||
"(stack=%d, heap=%d)." CR),
|
||||
ESP.getFreeContStack(), ESP.getFreeHeap());
|
||||
if (!_memErrorReported) {
|
||||
myLastErrors.addEntry(F("PUSH: Low on memory, skipping push ") +
|
||||
String(ESP.getFreeContStack()));
|
||||
} else {
|
||||
Log.error(F("PUSH: Low on memory, skipping push %d" CR),
|
||||
ESP.getFreeContStack());
|
||||
}
|
||||
_memErrorReported = true; // Dont report this again unti restarted.
|
||||
myWifi.closeWifiClient();
|
||||
return;
|
||||
}
|
||||
@ -131,8 +134,8 @@ void PushTarget::sendInfluxDb2(TemplatingEngine& engine) {
|
||||
Log.notice(F("PUSH: InfluxDB2 push successful, response=%d" CR),
|
||||
httpResponseCode);
|
||||
} else {
|
||||
Log.error(F("PUSH: InfluxDB2 push failed, response=%d" CR),
|
||||
httpResponseCode);
|
||||
myLastErrors.addEntry("PUSH: Influxdb push failed response=" +
|
||||
String(httpResponseCode));
|
||||
}
|
||||
|
||||
http.end();
|
||||
@ -167,34 +170,53 @@ void PushTarget::sendBrewfather(TemplatingEngine& engine) {
|
||||
Log.notice(F("PUSH: Brewfather push successful, response=%d" CR),
|
||||
httpResponseCode);
|
||||
} else {
|
||||
Log.error(F("PUSH: Brewfather push failed, response=%d" CR),
|
||||
httpResponseCode);
|
||||
myLastErrors.addEntry("PUSH: Brewfather push failed response=" +
|
||||
String(httpResponseCode));
|
||||
}
|
||||
|
||||
http.end();
|
||||
myWifi.closeWifiClient();
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
void PushTarget::addHttpHeader(HTTPClient& http, String header) {
|
||||
if (!header.length()) return;
|
||||
|
||||
int i = header.indexOf(":");
|
||||
if (i) {
|
||||
String name = header.substring(0, i);
|
||||
String value = header.substring(i + 1);
|
||||
value.trim();
|
||||
Log.notice(F("PUSH: Adding header '%s': '%s'" CR), name.c_str(),
|
||||
value.c_str());
|
||||
http.addHeader(name, value);
|
||||
} else {
|
||||
myLastErrors.addEntry("PUSH: Invalid http header " + header);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Send data to http target
|
||||
//
|
||||
void PushTarget::sendHttp(TemplatingEngine& engine, int index) {
|
||||
#if !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.notice(F("PUSH: Sending values to http (%s)" CR),
|
||||
index ? "http2" : "http1");
|
||||
index ? "http2" : "http");
|
||||
#endif
|
||||
|
||||
String serverPath, doc;
|
||||
HTTPClient http;
|
||||
|
||||
if (index == 0) {
|
||||
serverPath = myConfig.getHttpPushUrl();
|
||||
serverPath = myConfig.getHttpUrl();
|
||||
doc = engine.create(TemplatingEngine::TEMPLATE_HTTP1);
|
||||
} else {
|
||||
serverPath = myConfig.getHttpPushUrl2();
|
||||
serverPath = myConfig.getHttp2Url();
|
||||
doc = engine.create(TemplatingEngine::TEMPLATE_HTTP2);
|
||||
}
|
||||
|
||||
HTTPClient http;
|
||||
if (serverPath.startsWith("https://")) {
|
||||
myWifi.getWifiClientSecure().setInsecure();
|
||||
Log.notice(F("PUSH: HTTP, SSL enabled without validation." CR));
|
||||
@ -203,20 +225,30 @@ void PushTarget::sendHttp(TemplatingEngine& engine, int index) {
|
||||
http.begin(myWifi.getWifiClient(), serverPath);
|
||||
}
|
||||
|
||||
if (index == 0) {
|
||||
addHttpHeader(http, myConfig.getHttpHeader(0));
|
||||
addHttpHeader(http, myConfig.getHttpHeader(1));
|
||||
} else {
|
||||
addHttpHeader(http, myConfig.getHttp2Header(0));
|
||||
addHttpHeader(http, myConfig.getHttp2Header(1));
|
||||
}
|
||||
|
||||
#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
|
||||
|
||||
// Send HTTP POST request
|
||||
http.addHeader(F("Content-Type"), F("application/json"));
|
||||
// http.addHeader(F("Content-Type"), F("application/json"));
|
||||
int httpResponseCode = http.POST(doc);
|
||||
|
||||
if (httpResponseCode == 200) {
|
||||
Log.notice(F("PUSH: HTTP push successful, response=%d" CR),
|
||||
httpResponseCode);
|
||||
} else {
|
||||
Log.error(F("PUSH: HTTP push failed, response=%d" CR), httpResponseCode);
|
||||
myLastErrors.addEntry(
|
||||
"PUSH: HTTP push failed response=" + String(httpResponseCode) +
|
||||
String(index == 0 ? " (http)" : " (http2)"));
|
||||
}
|
||||
|
||||
http.end();
|
||||
@ -236,15 +268,13 @@ void PushTarget::sendMqtt(TemplatingEngine& engine) {
|
||||
String doc = engine.create(TemplatingEngine::TEMPLATE_MQTT);
|
||||
int port = myConfig.getMqttPort();
|
||||
|
||||
// if (url.endsWith(":8883")) {
|
||||
if (port > 8000) {
|
||||
// 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());
|
||||
mqtt.begin(url.c_str(), port, myWifi.getWifiClientSecure());
|
||||
} else {
|
||||
mqtt.begin(myConfig.getMqttUrl(), myWifi.getWifiClient());
|
||||
mqtt.begin(myConfig.getMqttUrl(), port, myWifi.getWifiClient());
|
||||
}
|
||||
|
||||
mqtt.connect(myConfig.getMDNS(), myConfig.getMqttUser(),
|
||||
@ -280,8 +310,8 @@ void PushTarget::sendMqtt(TemplatingEngine& engine) {
|
||||
if (mqtt.publish(topic, value)) {
|
||||
Log.notice(F("PUSH: MQTT publish successful on %s" CR), topic.c_str());
|
||||
} else {
|
||||
Log.error(F("PUSH: MQTT publish failed err=%d, ret=%d" CR),
|
||||
mqtt.lastError(), mqtt.returnCode());
|
||||
myLastErrors.addEntry("PUSH: MQTT push on " + topic +
|
||||
" failed error=" + String(mqtt.lastError()));
|
||||
}
|
||||
|
||||
index = next + 1;
|
||||
|
@ -26,14 +26,23 @@ SOFTWARE.
|
||||
|
||||
#include <templating.hpp>
|
||||
|
||||
#if defined(ESP8266)
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#else // defined (ESP32)
|
||||
#include <HTTPClient.h>
|
||||
#endif
|
||||
|
||||
class PushTarget {
|
||||
private:
|
||||
uint32_t _ms; // Used to check that we do not post to often
|
||||
bool _memErrorReported =
|
||||
false; // Avoid filling the error log with memory errors.
|
||||
|
||||
void sendBrewfather(TemplatingEngine& engine);
|
||||
void sendHttp(TemplatingEngine& engine, int index);
|
||||
void sendInfluxDb2(TemplatingEngine& engine);
|
||||
void sendMqtt(TemplatingEngine& engine);
|
||||
void addHttpHeader(HTTPClient& http, String header);
|
||||
|
||||
public:
|
||||
PushTarget() { _ms = millis(); }
|
||||
|
@ -32,7 +32,11 @@ SOFTWARE.
|
||||
#define PARAM_PASS "wifi-pass"
|
||||
#define PARAM_PUSH_BREWFATHER "brewfather-push"
|
||||
#define PARAM_PUSH_HTTP "http-push"
|
||||
#define PARAM_PUSH_HTTP_H1 "http-push-h1"
|
||||
#define PARAM_PUSH_HTTP_H2 "http-push-h2"
|
||||
#define PARAM_PUSH_HTTP2 "http-push2"
|
||||
#define PARAM_PUSH_HTTP2_H1 "http-push2-h1"
|
||||
#define PARAM_PUSH_HTTP2_H2 "http-push2-h2"
|
||||
#define PARAM_PUSH_INFLUXDB2 "influxdb2-push"
|
||||
#define PARAM_PUSH_INFLUXDB2_ORG "influxdb2-org"
|
||||
#define PARAM_PUSH_INFLUXDB2_BUCKET "influxdb2-bucket"
|
||||
|
@ -26,9 +26,10 @@ SOFTWARE.
|
||||
|
||||
// Includes
|
||||
#include <Arduino.h>
|
||||
#include <main.hpp>
|
||||
#include <helper.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <helper.hpp>
|
||||
#include <main.hpp>
|
||||
|
||||
// Templating variables
|
||||
#define TPL_MDNS "${mdns}"
|
||||
@ -37,19 +38,19 @@ SOFTWARE.
|
||||
#define TPL_TEMP "${temp}"
|
||||
#define TPL_TEMP_C "${temp-c}"
|
||||
#define TPL_TEMP_F "${temp-f}"
|
||||
#define TPL_TEMP_UNITS "${temp-unit}" // C or F
|
||||
#define TPL_TEMP_UNITS "${temp-unit}" // C or F
|
||||
#define TPL_BATTERY "${battery}"
|
||||
#define TPL_RSSI "${rssi}"
|
||||
#define TPL_RUN_TIME "${run-time}"
|
||||
#define TPL_ANGLE "${angle}"
|
||||
#define TPL_TILT "${tilt}" // same as angle
|
||||
#define TPL_TILT "${tilt}" // same as angle
|
||||
#define TPL_GRAVITY "${gravity}"
|
||||
#define TPL_GRAVITY_G "${gravity-sg}"
|
||||
#define TPL_GRAVITY_P "${gravity-plato}"
|
||||
#define TPL_GRAVITY_CORR "${corr-gravity}"
|
||||
#define TPL_GRAVITY_CORR_G "${corr-gravity-sg}"
|
||||
#define TPL_GRAVITY_CORR_P "${corr-gravity-plato}"
|
||||
#define TPL_GRAVITY_UNIT "${gravity-unit}" // G or P
|
||||
#define TPL_GRAVITY_UNIT "${gravity-unit}" // G or P
|
||||
|
||||
#define TPL_FNAME_HTTP1 "/http-1.tpl"
|
||||
#define TPL_FNAME_HTTP2 "/http-2.tpl"
|
||||
@ -70,36 +71,29 @@ class TemplatingEngine {
|
||||
String val;
|
||||
};
|
||||
|
||||
KeyVal items[19] = {
|
||||
{ TPL_MDNS, "" },
|
||||
{ TPL_ID, "" },
|
||||
{ TPL_SLEEP_INTERVAL, "" },
|
||||
{ TPL_TEMP, "" },
|
||||
{ TPL_TEMP_C, "" },
|
||||
{ TPL_TEMP_F, "" },
|
||||
{ TPL_TEMP_UNITS, "" },
|
||||
{ TPL_BATTERY, "" },
|
||||
{ TPL_RSSI, "" },
|
||||
{ TPL_RUN_TIME, "" },
|
||||
{ TPL_ANGLE, "" },
|
||||
{ TPL_TILT, "" },
|
||||
{ TPL_GRAVITY, "" },
|
||||
{ TPL_GRAVITY_G, "" },
|
||||
{ TPL_GRAVITY_P, "" },
|
||||
{ TPL_GRAVITY_CORR, "" },
|
||||
{ TPL_GRAVITY_CORR_G, "" },
|
||||
{ TPL_GRAVITY_CORR_P, "" },
|
||||
{ TPL_GRAVITY_UNIT, "" }
|
||||
};
|
||||
KeyVal items[19] = {{TPL_MDNS, ""}, {TPL_ID, ""},
|
||||
{TPL_SLEEP_INTERVAL, ""}, {TPL_TEMP, ""},
|
||||
{TPL_TEMP_C, ""}, {TPL_TEMP_F, ""},
|
||||
{TPL_TEMP_UNITS, ""}, {TPL_BATTERY, ""},
|
||||
{TPL_RSSI, ""}, {TPL_RUN_TIME, ""},
|
||||
{TPL_ANGLE, ""}, {TPL_TILT, ""},
|
||||
{TPL_GRAVITY, ""}, {TPL_GRAVITY_G, ""},
|
||||
{TPL_GRAVITY_P, ""}, {TPL_GRAVITY_CORR, ""},
|
||||
{TPL_GRAVITY_CORR_G, ""}, {TPL_GRAVITY_CORR_P, ""},
|
||||
{TPL_GRAVITY_UNIT, ""}};
|
||||
|
||||
char buffer[20];
|
||||
String baseTemplate;
|
||||
|
||||
void setVal(String key, float val, int dec = 2) { String s = convertFloatToString(val, &buffer[0], dec); s.trim(); setVal(key, s); }
|
||||
void setVal(String key, float val, int dec = 2) {
|
||||
String s = convertFloatToString(val, &buffer[0], dec);
|
||||
s.trim();
|
||||
setVal(key, s);
|
||||
}
|
||||
void setVal(String key, int val) { setVal(key, String(val)); }
|
||||
void setVal(String key, char val) { setVal(key, String(val)); }
|
||||
void setVal(String key, String val) {
|
||||
int max = sizeof(items)/sizeof(KeyVal);
|
||||
int max = sizeof(items) / sizeof(KeyVal);
|
||||
for (int i = 0; i < max; i++) {
|
||||
if (items[i].key.equals(key)) {
|
||||
items[i].val = val;
|
||||
@ -107,11 +101,11 @@ class TemplatingEngine {
|
||||
}
|
||||
}
|
||||
|
||||
Log.error(F("TPL : Key not found %s." CR), key.c_str());
|
||||
Log.warning(F("TPL : Key not found %s." CR), key.c_str());
|
||||
}
|
||||
|
||||
void transform(String& s) {
|
||||
int max = sizeof(items)/sizeof(KeyVal);
|
||||
int max = sizeof(items) / sizeof(KeyVal);
|
||||
for (int i = 0; i < max; i++) {
|
||||
while (s.indexOf(items[i].key) != -1)
|
||||
s.replace(items[i].key, items[i].val);
|
||||
@ -119,13 +113,13 @@ class TemplatingEngine {
|
||||
}
|
||||
|
||||
void dumpAll() {
|
||||
int max = sizeof(items)/sizeof(KeyVal);
|
||||
int max = sizeof(items) / sizeof(KeyVal);
|
||||
for (int i = 0; i < max; i++) {
|
||||
Serial.print( "Key=\'" );
|
||||
Serial.print( items[i].key.c_str() );
|
||||
Serial.print( "\', Val=\'" );
|
||||
Serial.print( items[i].val.c_str() );
|
||||
Serial.println( "\'" );
|
||||
Serial.print("Key=\'");
|
||||
Serial.print(items[i].key.c_str());
|
||||
Serial.print("\', Val=\'");
|
||||
Serial.print(items[i].val.c_str());
|
||||
Serial.println("\'");
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,7 +132,8 @@ class TemplatingEngine {
|
||||
TEMPLATE_MQTT = 4
|
||||
};
|
||||
|
||||
void initialize(float angle, float gravitySG, float corrGravitySG, float tempC, float runTime);
|
||||
void initialize(float angle, float gravitySG, float corrGravitySG,
|
||||
float tempC, float runTime);
|
||||
const String& create(TemplatingEngine::Templates idx);
|
||||
};
|
||||
|
||||
|
@ -350,8 +350,12 @@ void WebServerHandler::webHandleConfigPush() {
|
||||
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
|
||||
#endif
|
||||
|
||||
myConfig.setHttpPushUrl(_server->arg(PARAM_PUSH_HTTP).c_str());
|
||||
myConfig.setHttpPushUrl2(_server->arg(PARAM_PUSH_HTTP2).c_str());
|
||||
myConfig.setHttpUrl(_server->arg(PARAM_PUSH_HTTP).c_str());
|
||||
myConfig.setHttpHeader(_server->arg(PARAM_PUSH_HTTP_H1).c_str(), 0);
|
||||
myConfig.setHttpHeader(_server->arg(PARAM_PUSH_HTTP_H2).c_str(), 1);
|
||||
myConfig.setHttp2Url(_server->arg(PARAM_PUSH_HTTP2).c_str());
|
||||
myConfig.setHttp2Header(_server->arg(PARAM_PUSH_HTTP2_H1).c_str(), 0);
|
||||
myConfig.setHttp2Header(_server->arg(PARAM_PUSH_HTTP2_H2).c_str(), 1);
|
||||
myConfig.setBrewfatherPushUrl(_server->arg(PARAM_PUSH_BREWFATHER).c_str());
|
||||
myConfig.setInfluxDb2PushUrl(_server->arg(PARAM_PUSH_INFLUXDB2).c_str());
|
||||
myConfig.setInfluxDb2PushOrg(_server->arg(PARAM_PUSH_INFLUXDB2_ORG).c_str());
|
||||
@ -626,7 +630,7 @@ void WebServerHandler::webHandleConfigFormatWrite() {
|
||||
_server->sendHeader("Location", "/format.htm", true);
|
||||
_server->send(302, "text/plain", "Format updated");
|
||||
} else {
|
||||
Log.error(F("WEB : Unable to store format file" CR));
|
||||
myLastErrors.addEntry(F("WEB : Unable to store format file"));
|
||||
_server->send(400, "text/plain", "Unable to store format in file.");
|
||||
}
|
||||
|
||||
@ -920,6 +924,7 @@ bool WebServerHandler::setupWebServer() {
|
||||
_server->on("/", std::bind(&WebServerHandler::webReturnUploadHtm, this));
|
||||
}
|
||||
#endif
|
||||
_server->serveStatic("/log", LittleFS, ERR_FILENAME);
|
||||
|
||||
// Dynamic content
|
||||
_server->on(
|
||||
|
26
src/wifi.cpp
26
src/wifi.cpp
@ -21,24 +21,25 @@ 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.
|
||||
*/
|
||||
#if defined (ESP8266)
|
||||
#if defined(ESP8266)
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <ESP8266httpUpdate.h>
|
||||
#else // defined (ESP32)
|
||||
#include <WiFi.h>
|
||||
#else // defined (ESP32)
|
||||
#include <HTTPClient.h>
|
||||
#include <HTTPUpdate.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
#include <incbin.h>
|
||||
|
||||
#include <config.hpp>
|
||||
#include <main.hpp>
|
||||
#include <wifi.hpp>
|
||||
|
||||
// Settings for DRD
|
||||
#if defined (ESP8266)
|
||||
#if defined(ESP8266)
|
||||
#define ESP_DRD_USE_LITTLEFS true
|
||||
#define ESP_DRD_USE_SPIFFS false
|
||||
#else // defined (ESP32)
|
||||
#else // defined (ESP32)
|
||||
#define ESP_DRD_USE_LITTLEFS false
|
||||
#define ESP_DRD_USE_SPIFFS true
|
||||
#endif
|
||||
@ -191,8 +192,8 @@ bool WifiConnection::waitForConnection(int maxTime) {
|
||||
|
||||
if (i++ >
|
||||
(maxTime * 10)) { // Try for maxTime seconds. Since delay is 100ms.
|
||||
Log.error(F("WIFI: Failed to connect to wifi %d, aborting %s." CR),
|
||||
WiFi.status(), getIPAddress().c_str());
|
||||
myLastErrors.addEntry("WIFI: Failed to connect to wifi " +
|
||||
String(WiFi.status()));
|
||||
WiFi.disconnect();
|
||||
Serial.print(CR);
|
||||
return false; // Return to main that we have failed to connect.
|
||||
@ -250,9 +251,8 @@ bool WifiConnection::updateFirmware() {
|
||||
|
||||
switch (ret) {
|
||||
case HTTP_UPDATE_FAILED:
|
||||
Log.error(F("WIFI: OTA update failed %d, %s." CR),
|
||||
ESPhttpUpdate.getLastError(),
|
||||
ESPhttpUpdate.getLastErrorString().c_str());
|
||||
myLastErrors.addEntry("WIFI: OTA update failed " +
|
||||
String(ESPhttpUpdate.getLastError()));
|
||||
break;
|
||||
case HTTP_UPDATE_NO_UPDATES:
|
||||
break;
|
||||
@ -292,8 +292,8 @@ void WifiConnection::downloadFile(const char *fname) {
|
||||
f.close();
|
||||
Log.notice(F("WIFI: Downloaded file %s." CR), fname);
|
||||
} else {
|
||||
Log.error(F("WIFI: Failed to download file, respone=%d" CR),
|
||||
httpResponseCode);
|
||||
myLastErrors.addEntry("WIFI: Failed to download html-file " +
|
||||
String(httpResponseCode));
|
||||
}
|
||||
http.end();
|
||||
myWifi.closeWifiClient();
|
||||
@ -332,7 +332,7 @@ bool WifiConnection::checkFirmwareVersion() {
|
||||
DynamicJsonDocument ver(300);
|
||||
DeserializationError err = deserializeJson(ver, payload);
|
||||
if (err) {
|
||||
Log.error(F("WIFI: Failed to parse version.json, %s" CR), err);
|
||||
myLastErrors.addEntry(F("WIFI: Failed to parse version.json"));
|
||||
} else {
|
||||
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
|
||||
Log.verbose(F("WIFI: Project %s version %s." CR),
|
||||
|
@ -41,6 +41,8 @@ URL: (http://gravmon.local/device)
|
||||
:width: 800
|
||||
:alt: Device Settings
|
||||
|
||||
The button `view error log` will show the last 15 errors on the device. This can be useful for checking errors without
|
||||
the need to connect to the serial port.
|
||||
|
||||
* **Version:**
|
||||
|
||||
@ -156,6 +158,20 @@ Push Settings
|
||||
|
||||
Password or blank if anonymous is accepted
|
||||
|
||||
* **HTTP Headers**
|
||||
|
||||
.. image:: images/config-popup1.png
|
||||
:width: 300
|
||||
:alt: HTTP Headers
|
||||
|
||||
Its now possible to define 2 http headers per push target. This is avaialble via a pop-up window but dont forget
|
||||
to press the save buttons on the post section to save the values.
|
||||
|
||||
Mozilla has a good guide on what options exist; `HTTP Headers <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers>`_
|
||||
Refer to the documentation on what headers would be needed for a specific service target.
|
||||
|
||||
|
||||
|
||||
|
||||
Gravity Settings
|
||||
++++++++++++++++
|
||||
@ -360,7 +376,11 @@ Other parameters are the same as in the configuration guide.
|
||||
"temp-format": "C",
|
||||
"brewfather-push": "http://log.brewfather.net/stream?id=Qwerty",
|
||||
"http-push": "http://192.168.1.50:9090/api/v1/Qwerty/telemetry",
|
||||
"http-push-h1": "",
|
||||
"http-push-h2": "",
|
||||
"http-push2": "http://192.168.1.50/ispindel",
|
||||
"http-push2-h1": "",
|
||||
"http-push2-h2": "",
|
||||
"influxdb2-push": "http://192.168.1.50:8086",
|
||||
"influxdb2-org": "Qwerty",
|
||||
"influxdb2-bucket": "Qwerty",
|
||||
@ -485,6 +505,10 @@ Used to update push settings via an HTTP POST command. Payload is in JSON format
|
||||
"id": "ee1bfc",
|
||||
"http-push": "http://192.168.1.50/ispindel",
|
||||
"http-push2": "",
|
||||
"http-push-h1": "",
|
||||
"http-push-h2": "",
|
||||
"http-push2-h1": "",
|
||||
"http-push2-h2": "",
|
||||
"brewfather-push": "",
|
||||
"influxdb2-push": "http://192.168.1.50:8086",
|
||||
"influxdb2-org": "Qwerty",
|
||||
@ -599,6 +623,10 @@ present or the API call will fail.
|
||||
json = { "id": id,
|
||||
"http-push": "http://192.168.1.1/ispindel",
|
||||
"http-push2": "",
|
||||
"http-push-h1": "",
|
||||
"http-push-h2": "",
|
||||
"http-push2-h1": "",
|
||||
"http-push2-h2": "",
|
||||
"brewfather-push": "",
|
||||
"influxdb2-push": "",
|
||||
"influxdb2-org": "",
|
||||
|
BIN
src_docs/source/images/config-popup1.png
Normal file
BIN
src_docs/source/images/config-popup1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 22 KiB |
Binary file not shown.
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 20 KiB |
@ -3,6 +3,18 @@
|
||||
Releases
|
||||
########
|
||||
|
||||
v0.8.0
|
||||
------
|
||||
|
||||
* Added option to set http headers (2 per endpoint)
|
||||
* Added possibility to view last 10 errors on device page.
|
||||
|
||||
v0.7.1
|
||||
------
|
||||
|
||||
* Defined mqtt port was ignored, used default values.
|
||||
* Extended length of HTTP url fields from 100 to 120 chars.
|
||||
|
||||
v0.7.0
|
||||
------
|
||||
|
||||
|
@ -5,7 +5,11 @@
|
||||
"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=",
|
||||
"http-push2": "http://192.168.1.10/ispindel",
|
||||
"http-push2-h1": "Second",
|
||||
"http-push2-h2": "First",
|
||||
"influxdb2-push": "http://192.168.1.10:8086",
|
||||
"influxdb2-org": "hello",
|
||||
"influxdb2-bucket": "spann",
|
||||
|
Loading…
Reference in New Issue
Block a user