Added errlog, custom http headers

This commit is contained in:
Magnus Persson 2022-01-27 14:00:12 +01:00
parent b106ebfa20
commit 63fd80e750
23 changed files with 411 additions and 187 deletions

View File

@ -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

View File

@ -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"]);
})

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"><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">&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="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">&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="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>

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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(); }

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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_

View File

@ -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;
}

View File

@ -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;

View File

@ -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(); }

View File

@ -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"

View File

@ -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);
};

View File

@ -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(

View File

@ -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),

View File

@ -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": "",

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

View File

@ -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
------

View File

@ -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",