Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
03e6fb6b22 | |||
2532f50215 | |||
10163f3aa7 | |||
1adef20edd | |||
617e77a9d8 | |||
c8d48a3236 | |||
f3d2e88b31 | |||
a9dc3be329 | |||
043963df91 | |||
f318dea9b7 | |||
06b5b949d0 | |||
5c400e4e47 | |||
e1484ca2fd | |||
4d300908a6 | |||
5b6ce7d672 | |||
adc21b5527 | |||
8919c842fb | |||
a270faa480 | |||
aad35e20bd | |||
a83a74b5a4 | |||
a703add227 | |||
534a739891 | |||
01fe6bbf19 | |||
19d1f7b1ca | |||
2a2aecdee5 | |||
325c7ee2ca | |||
57f5816f63 | |||
46b17177f2 | |||
df1981e3dd | |||
2f391c95c7 | |||
ea62d9e752 | |||
6364e251b7 | |||
93d9effcef | |||
4ff114642e | |||
7f775d78eb | |||
17bc23bef8 |
6
.github/workflows/doc-build.yaml
vendored
@ -3,7 +3,7 @@ name: Sphinx Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@ -15,7 +15,9 @@ jobs:
|
||||
- uses: ammaraskar/sphinx-action@master
|
||||
with:
|
||||
docs-folder: "src_docs/"
|
||||
pre-build-command: "pip install sphinx_rtd_theme"
|
||||
pre-build-command: |
|
||||
pip install sphinx_rtd_theme
|
||||
pip install furo
|
||||
build-command: "sphinx-build -b html ./source ../docs"
|
||||
|
||||
- name: Commit documentation changes
|
||||
|
2
.github/workflows/pio-build.yaml
vendored
@ -43,7 +43,7 @@ jobs:
|
||||
with:
|
||||
add: 'bin'
|
||||
author_name: GitHub Action
|
||||
author_email: magnus@users.noreply.github.com
|
||||
author_email: mp-se@noreply.github.com
|
||||
|
||||
branch: dev
|
||||
|
||||
|
@ -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.5.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"><div class="col-md-8 themed-grid-col bg-light">Certificates:</div><div class="col-md-4 themed-grid-col bg-light" id="certs">Loading...</div></div><hr class="my-4"></div><script type="text/javascript">function getConfig(){var t="/api/device";$("#spinner").show(),$.getJSON(t,function(t){console.log(t),$("#app-ver").text(t["app-ver"]+" (html 0.6.0)"),$("#mdns").text(t.mdns),$("#id").text(t.id),t.certs?$("#certs").text("CA Store installed."):$("#certs").text("CA Store NOT installed.")}).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>
|
BIN
bin/firmware.bin
@ -1 +1 @@
|
||||
{ "project":"gravmon", "version":"0.5.0", "html": [ "index.min.htm", "device.min.htm", "config.min.htm", "calibration.min.htm", "about.min.htm" ] }
|
||||
{ "project":"gravmon", "version":"0.6.0", "html": [ "index.min.htm", "device.min.htm", "config.min.htm", "calibration.min.htm", "about.min.htm" ] }
|
BIN
data/certs.ar
Normal file
167
html/config.htm
@ -55,20 +55,41 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning fade hide show d-none" role="alert" id="warning-sleep">
|
||||
<div>A sleep-interval of less than 300s will reduce battery life, consider using 900s</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning fade hide show d-none" role="alert" id="warning-gyro">
|
||||
<div>When using the gyro temperature use a sleep-interval that is greater than 300s for accurate readings</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function showError( msg ) {
|
||||
$('.alert').removeClass('alert-success').addClass('alert-danger').removeClass('d-none').addClass('show')
|
||||
$('#alert').removeClass('alert-success').addClass('alert-danger').removeClass('d-none').addClass('show');
|
||||
$('#alert-msg').text( msg );
|
||||
}
|
||||
|
||||
function showSuccess( msg ) {
|
||||
$('.alert').addClass('alert-success').removeClass('alert-danger').removeClass('d-none').addClass('show')
|
||||
$('#alert').addClass('alert-success').removeClass('alert-danger').removeClass('d-none').addClass('show');
|
||||
$('#alert-msg').text( msg );
|
||||
}
|
||||
|
||||
$("#alert-btn").click(function(e){
|
||||
$('.alert').addClass('d-none').removeClass('show')
|
||||
$('#alert').addClass('d-none').removeClass('show');
|
||||
});
|
||||
|
||||
function showWarningSleep() {
|
||||
$('#warning-sleep').removeClass('d-none').addClass('show');
|
||||
}
|
||||
function hideWarningSleep() {
|
||||
$('#warning-sleep').addClass('d-none').removeClass('show');
|
||||
}
|
||||
function showWarningGyro() {
|
||||
$('#warning-gyro').removeClass('d-none').addClass('show');
|
||||
}
|
||||
function hideWarningGyro() {
|
||||
$('#warning-gyro').addClass('d-none').removeClass('show');
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="accordion" id="accordion">
|
||||
@ -86,14 +107,14 @@
|
||||
<form action="/api/config/device" method="post">
|
||||
<input type="text" name="id" id="id1" hidden>
|
||||
<div class="form-group row">
|
||||
<label for="mdns" class="col-sm-4 col-form-label">Device name:</label>
|
||||
<div class="col-sm-8">
|
||||
<label for="mdns" class="col-sm-3 col-form-label">Device name:</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" maxlength="12" class="form-control" name="mdns" id="mdns">
|
||||
</div>
|
||||
</div>
|
||||
<fieldset class="form-group row">
|
||||
<legend class="col-form-label col-sm-4 float-sm-left pt-0">Temperature Format:</legend>
|
||||
<div class="col-sm-8">
|
||||
<legend class="col-form-label col-sm-3 float-sm-left pt-0">Temperature Format:</legend>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="temp-format" id="temp-format-c" value="C" checked>
|
||||
<label class="form-check-label" for="temp-format-c">
|
||||
@ -109,14 +130,14 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="form-group row">
|
||||
<label for="sleep-interval" class="col-sm-4 col-form-label">Interval (seconds):</label>
|
||||
<div class="col-sm-4">
|
||||
<label for="sleep-interval" class="col-sm-3 col-form-label">Interval (seconds):</label>
|
||||
<div class="col-sm-2">
|
||||
<input type="number" min="10" max="3600" class="form-control" name="sleep-interval" id="sleep-interval">
|
||||
</div>
|
||||
<label for="sleep-interval" class="col-sm-4 col-form-label" id="sleep-interval-info"></label>
|
||||
<label for="sleep-interval" class="col-sm-3 col-form-label" id="sleep-interval-info"></label>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-8 offset-sm-4">
|
||||
<div class="col-sm-8 offset-sm-3">
|
||||
<button type="submit" class="btn btn-primary" id="device-btn">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -125,10 +146,10 @@
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="calibrate-btn" class="col-sm-4 col-form-label">Current calibration values:</label>
|
||||
<label for="calibrate-btn" class="col-sm-4 col-form-label" id="gyro-calibration-data">Loading...</label>
|
||||
<label for="gyro-calibration-data" class="col-sm-4 col-form-label" id="angle">Loading...</label>
|
||||
<div class="col-sm-8 offset-sm-4">
|
||||
<label for="calibrate-btn" class="col-sm-3 col-form-label">Current calibration values:</label>
|
||||
<label for="calibrate-btn" class="col-sm-3 col-form-label" id="gyro-calibration-data">Loading...</label>
|
||||
<label for="gyro-calibration-data" class="col-sm-3 col-form-label" id="angle">Loading...</label>
|
||||
<div class="col-sm-8 offset-sm-3">
|
||||
<button type="button" class="btn btn-warning" id="calibrate-btn">Calibrate device</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -150,14 +171,14 @@
|
||||
<form action="/api/config/push" method="post">
|
||||
<input type="text" name="id" id="id2" hidden>
|
||||
<div class="form-group row">
|
||||
<label for="http-push" class="col-sm-4 col-form-label">Http URL 1:</label>
|
||||
<div class="col-sm-8">
|
||||
<label for="http-push" class="col-sm-2 col-form-label">Http URL 1:</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="url" maxlength="100" class="form-control" name="http-push" id="http-push">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="http-push2" class="col-sm-4 col-form-label">Http URL 2:</label>
|
||||
<div class="col-sm-8">
|
||||
<label for="http-push2" class="col-sm-2 col-form-label">Http URL 2:</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="url" maxlength="100" class="form-control" name="http-push2" id="http-push2">
|
||||
</div>
|
||||
</div>
|
||||
@ -165,8 +186,8 @@
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="inputBrewfatherPush" class="col-sm-4 col-form-label">Brewfather URL:</label>
|
||||
<div class="col-sm-8">
|
||||
<label for="inputBrewfatherPush" class="col-sm-2 col-form-label">Brewfather URL:</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="url" maxlength="100" class="form-control" name="brewfather-push" id="brewfather-push">
|
||||
</div>
|
||||
</div>
|
||||
@ -174,31 +195,61 @@
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="influxdb2-push" class="col-sm-4 col-form-label">InfluxDB v2 URL:</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="url" maxlength="100" class="form-control" name="influxdb2-push" id="influxdb2-push">
|
||||
<label for="influxdb2-push" class="col-sm-2 col-form-label">InfluxDB v2 URL:</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="url" maxlength="40" class="form-control" name="influxdb2-push" id="influxdb2-push">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="influxdb2-org" class="col-sm-4 col-form-label">InfluxDB v2 Organisation:</label>
|
||||
<div class="col-sm-8">
|
||||
<label for="influxdb2-org" class="col-sm-2 col-form-label">InfluxDB v2 Org:</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" maxlength="50" class="form-control" name="influxdb2-org" id="influxdb2-org">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="influxdb2-bucket" class="col-sm-4 col-form-label">InfluxDB v2 Bucket:</label>
|
||||
<div class="col-sm-8">
|
||||
<label for="influxdb2-bucket" class="col-sm-2 col-form-label">InfluxDB v2 Bucket:</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" maxlength="50" class="form-control" name="influxdb2-bucket" id="influxdb2-bucket">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="influxdb2-auth" class="col-sm-4 col-form-label">InfluxDB v2 Auth. Token:</label>
|
||||
<div class="col-sm-8">
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="influxdb2-auth" class="col-sm-2 col-form-label">InfluxDB v2 Auth:</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" maxlength="100" class="form-control" name="influxdb2-auth" id="influxdb2-auth">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-8 offset-sm-4">
|
||||
<label for="mqtt-push" class="col-sm-2 col-form-label">MQTT Server:</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" maxlength="40" class="form-control" name="mqtt-push" id="mqtt-push">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="mqtt-topic" class="col-sm-2 col-form-label">MQTT Topic:</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" maxlength="30" class="form-control" name="mqtt-topic" id="mqtt-topic">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="mqtt-user" class="col-sm-2 col-form-label">MQTT User:</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" maxlength="20" class="form-control" name="mqtt-user" id="mqtt-user">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="mqtt-pass" class="col-sm-2 col-form-label">MQTT Password:</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" maxlength="20" class="form-control" name="mqtt-pass" id="mqtt-pass">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-8 offset-sm-2">
|
||||
<button type="submit" class="btn btn-primary" id="push-btn">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -220,14 +271,13 @@
|
||||
<form action="/api/config/gravity" method="post">
|
||||
<input type="text" name="id" id="id3" hidden>
|
||||
<div class="form-group row">
|
||||
<label for="gravity-formula" class="col-sm-4 col-form-label">Formula</label>
|
||||
<div class="col-sm-8">
|
||||
<label for="gravity-formula" class="col-sm-2 col-form-label">Formula</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" maxlength="200" class="form-control" name="gravity-formula" id="gravity-formula">
|
||||
</div>
|
||||
</div>
|
||||
<label for="gravity-formula"" class="col-sm-8 offset-sm-4 col-form-label" id="gravity">Loading...</label>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-8 offset-sm-4">
|
||||
<div class="col-sm-4 offset-sm-2">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="gravity-temp-adjustment" id="gravity-temp-adjustment">
|
||||
<label class="form-check-label" for="gravity-temp-adjustment">
|
||||
@ -237,7 +287,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-8 offset-sm-4">
|
||||
<div class="col-sm-4 offset-sm-2">
|
||||
<button type="submit" class="btn btn-primary" id="gravity-btn">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -259,26 +309,36 @@
|
||||
<form action="/api/config/hardware" method="post">
|
||||
<input type="text" name="id" id="id4" hidden>
|
||||
<div class="form-group row">
|
||||
<label for="voltage-factor" class="col-sm-4 col-form-label">Voltage factor:</label>
|
||||
<div class="col-sm-4">
|
||||
<label for="voltage-factor" class="col-sm-2 col-form-label">Voltage factor:</label>
|
||||
<div class="col-sm-2">
|
||||
<input type="number" step=".01" class="form-control" name="voltage-factor" id="voltage-factor">
|
||||
</div>
|
||||
<label for="voltage-factor" class="col-sm-4 col-form-label" id="battery">Loading...</label>
|
||||
<label for="voltage-factor" class="col-sm-3 col-form-label" id="battery">Loading...</label>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="temp-adjustment-value" class="col-sm-4 col-form-label">Temp Sensor Adj:</label>
|
||||
<div class="col-sm-8">
|
||||
<label for="temp-adjustment-value" class="col-sm-2 col-form-label">Temp Sensor Adj:</label>
|
||||
<div class="col-sm-2">
|
||||
<input type="number" step=".1" class="form-control" name="temp-adjustment-value" id="temp-adjustment-value">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="ota-url" class="col-sm-4 col-form-label">OTA base URL:</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="col-sm-3 offset-sm-2">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="gyro-temp" id="gyro-temp">
|
||||
<label class="form-check-label" for="gyro-temp">
|
||||
Use gyro temperature
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="ota-url" class="col-sm-2 col-form-label">OTA base URL:</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="url" maxlength="90" class="form-control" name="ota-url" id="ota-url">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-8 offset-sm-4">
|
||||
<div class="col-sm-8 offset-sm-2">
|
||||
<button type="submit" class="btn btn-primary" id="hardware-btn">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -319,10 +379,20 @@
|
||||
function updateSleepInfo() {
|
||||
var i = $("#sleep-interval").val()
|
||||
$("#sleep-interval-info").text( Math.floor(i/60) + " m " + (i%60) + " s" )
|
||||
|
||||
hideWarningGyro();
|
||||
if(i>0 && i<300) {
|
||||
if( $("#gyro-temp").is(":checked") )
|
||||
showWarningGyro();
|
||||
showWarningSleep();
|
||||
} else {
|
||||
hideWarningSleep();
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger the calibration
|
||||
// Trigger the calibration and show warnings if needed
|
||||
$("#sleep-interval").keyup(updateSleepInfo);
|
||||
$("#gyro-temp").change(updateSleepInfo);
|
||||
|
||||
function setButtonDisabled( b ) {
|
||||
$("#device-btn").prop("disabled", b);
|
||||
@ -358,15 +428,20 @@
|
||||
$("#influxdb2-org").val(cfg["influxdb2-org"]);
|
||||
$("#influxdb2-bucket").val(cfg["influxdb2-bucket"]);
|
||||
$("#influxdb2-auth").val(cfg["influxdb2-auth"]);
|
||||
$("#mqtt-push").val(cfg["mqtt-push"]);
|
||||
$("#mqtt-topic").val(cfg["mqtt-topic"]);
|
||||
$("#mqtt-user").val(cfg["mqtt-user"]);
|
||||
$("#mqtt-pass").val(cfg["mqtt-pass"]);
|
||||
$("#sleep-interval").val(cfg["sleep-interval"]);
|
||||
$("#voltage-factor").val(cfg["voltage-factor"]);
|
||||
$("#gravity-formula").val(cfg["gravity-formula"]);
|
||||
$("#temp-adjustment-value").val(cfg["temp-adjustment-value"]);
|
||||
$("#gravity-temp-adjustment").prop( "checked", cfg["gravity-temp-adjustment"] );
|
||||
$("#gyro-temp").prop( "checked", cfg["gyro-temp"] );
|
||||
$("#gyro-calibration-data").text( cfg["gyro-calibration-data"]["ax"] + "," + cfg["gyro-calibration-data"]["ay"] + "," + cfg["gyro-calibration-data"]["az"] + "," + cfg["gyro-calibration-data"]["gx"] + "," + cfg["gyro-calibration-data"]["gy"] + "," + cfg["gyro-calibration-data"]["gz"] );
|
||||
$("#battery").text(cfg["battery"] + " V");
|
||||
$("#angle").text(cfg["angle"]);
|
||||
$("#gravity").text(cfg["gravity"] + " SG");
|
||||
//$("#gravity").text(cfg["gravity"] + " SG");
|
||||
})
|
||||
.fail(function () {
|
||||
showError('Unable to get data from the device.');
|
||||
|
@ -88,6 +88,10 @@
|
||||
<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">
|
||||
<div class="col-md-8 themed-grid-col bg-light">Certificates:</div>
|
||||
<div class="col-md-4 themed-grid-col bg-light" id="certs">Loading...</div>
|
||||
</div>
|
||||
<hr class="my-4">
|
||||
</div>
|
||||
|
||||
@ -100,9 +104,14 @@
|
||||
$('#spinner').show();
|
||||
$.getJSON(url, function (cfg) {
|
||||
console.log( cfg );
|
||||
$("#app-ver").text(cfg["app-ver"] + " (html 0.5.0)");
|
||||
$("#app-ver").text(cfg["app-ver"] + " (html 0.6.0)");
|
||||
$("#mdns").text(cfg["mdns"]);
|
||||
$("#id").text(cfg["id"]);
|
||||
|
||||
if( cfg["certs"] )
|
||||
$("#certs").text("CA Store installed.");
|
||||
else
|
||||
$("#certs").text("CA Store NOT installed.");
|
||||
})
|
||||
.fail(function () {
|
||||
showError('Unable to get data from the device.');
|
||||
|
@ -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.5.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"><div class="col-md-8 themed-grid-col bg-light">Certificates:</div><div class="col-md-4 themed-grid-col bg-light" id="certs">Loading...</div></div><hr class="my-4"></div><script type="text/javascript">function getConfig(){var t="/api/device";$("#spinner").show(),$.getJSON(t,function(t){console.log(t),$("#app-ver").text(t["app-ver"]+" (html 0.6.0)"),$("#mdns").text(t.mdns),$("#id").text(t.id),t.certs?$("#certs").text("CA Store installed."):$("#certs").text("CA Store NOT installed.")}).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>
|
@ -82,6 +82,10 @@
|
||||
<div class="col-md-2 themed-grid-col bg-light">about.min.htm</div>
|
||||
<div class="col-md-6 themed-grid-col bg-light" id="about">Checking...</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-2 themed-grid-col bg-light">certs.ar</div>
|
||||
<div class="col-md-6 themed-grid-col bg-light" id="certs">Checking...</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<form action="/api/upload" method="post" enctype="multipart/form-data">
|
||||
@ -137,6 +141,10 @@ function getUpload() {
|
||||
else
|
||||
$("#about").text("File is missing.");
|
||||
|
||||
if( cfg["certs"] )
|
||||
$("#certs").text("Completed.");
|
||||
else
|
||||
$("#certs").text("File is missing (optional).");
|
||||
|
||||
})
|
||||
.fail(function () {
|
||||
|
@ -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="/upload.htm">Beer Gravity Monitor - Missing html files</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"><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</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">The listed files below needs to be uploaded to the FileSystem in order for the GUI to work. You can also flash the LittleFS filesystem but in that case you will loose your device settings. An OTA upgrade will automatically download the files if they are found in the same location as the firmware.bin. This page is a fallback option.</div><div class="col-md-8 themed-grid-col bg-light"><br><strong>Once all the files are confirmed, please reboot the device for normal operation.</strong></div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">index.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="index">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">device.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="device">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">config.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="config">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">calibration.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="calibration">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">about.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="about">Checking...</div></div><div class="row mb-3"><form action="/api/upload" method="post" enctype="multipart/form-data"><div class="col-md-8 custom-file"><input type="file" class="custom-file-input" name="name" id="name"> <label class="custom-file-label" for="name">Choose file</label></div><button type="submit" class="btn btn-primary" id="upload-btn" value="upload">Upload</button></form></div><hr class="my-4"></div><script type="text/javascript">function getUpload(){var i="/api/upload";$("#spinner").show(),$.getJSON(i,function(i){console.log(i),i.index?$("#index").text("Completed."):$("#index").text("File is missing."),i.device?$("#device").text("Completed."):$("#device").text("File is missing."),i.config?$("#config").text("Completed."):$("#config").text("File is missing."),i.calibration?$("#calibration").text("Completed."):$("#calibration").text("File is missing."),i.about?$("#about").text("Completed."):$("#about").text("File is missing.")}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}window.onload=getUpload,$(".custom-file-input").on("change",function(){var i=$(this).val().split("\\").pop();$(this).siblings(".custom-file-label").addClass("selected").html(i)})</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="/upload.htm">Beer Gravity Monitor - Missing html files</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"><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</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">The listed files below needs to be uploaded to the FileSystem in order for the GUI to work. You can also flash the LittleFS filesystem but in that case you will loose your device settings. An OTA upgrade will automatically download the files if they are found in the same location as the firmware.bin. This page is a fallback option.</div><div class="col-md-8 themed-grid-col bg-light"><br><strong>Once all the files are confirmed, please reboot the device for normal operation.</strong></div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">index.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="index">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">device.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="device">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">config.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="config">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">calibration.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="calibration">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">about.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="about">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">certs.ar</div><div class="col-md-6 themed-grid-col bg-light" id="certs">Checking...</div></div><div class="row mb-3"><form action="/api/upload" method="post" enctype="multipart/form-data"><div class="col-md-8 custom-file"><input type="file" class="custom-file-input" name="name" id="name"> <label class="custom-file-label" for="name">Choose file</label></div><button type="submit" class="btn btn-primary" id="upload-btn" value="upload">Upload</button></form></div><hr class="my-4"></div><script type="text/javascript">function getUpload(){var e="/api/upload";$("#spinner").show(),$.getJSON(e,function(e){console.log(e),e.index?$("#index").text("Completed."):$("#index").text("File is missing."),e.device?$("#device").text("Completed."):$("#device").text("File is missing."),e.config?$("#config").text("Completed."):$("#config").text("File is missing."),e.calibration?$("#calibration").text("Completed."):$("#calibration").text("File is missing."),e.about?$("#about").text("Completed."):$("#about").text("File is missing."),e.certs?$("#certs").text("Completed."):$("#certs").text("File is missing (optional).")}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}window.onload=getUpload,$(".custom-file-input").on("change",function(){var e=$(this).val().split("\\").pop();$(this).siblings(".custom-file-label").addClass("selected").html(e)})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
368
lib/ESP_DoubleResetDetector/ESP_DoubleResetDetector.h
Normal file
@ -0,0 +1,368 @@
|
||||
/****************************************************************************************************************************
|
||||
ESP_DoubleResetDetector.h
|
||||
For ESP8266 / ESP32 boards
|
||||
|
||||
ESP_DoubleResetDetector is a library for the ESP8266/Arduino platform
|
||||
to enable trigger configure mode by resetting ESP32 / ESP8266 twice.
|
||||
|
||||
Forked from DataCute https://github.com/datacute/DoubleResetDetector
|
||||
|
||||
Built by Khoi Hoang https://github.com/khoih-prog/ESP_DoubleResetDetector
|
||||
Licensed under MIT license
|
||||
Version: 1.2.1
|
||||
|
||||
Version Modified By Date Comments
|
||||
------- ----------- ---------- -----------
|
||||
1.0.0 K Hoang 15/12/2019 Initial coding
|
||||
1.0.1 K Hoang 30/12/2019 Now can use EEPROM or SPIFFS for both ESP8266 and ESP32. RTC still OK for ESP8266
|
||||
1.0.2 K Hoang 10/04/2020 Fix bug by left-over cpp file and in example.
|
||||
1.0.3 K Hoang 13/05/2020 Update to use LittleFS for ESP8266 core 2.7.1+
|
||||
1.1.0 K Hoang 04/12/2020 Add support to LittleFS for ESP32 using LITTLEFS Library
|
||||
1.1.1 K Hoang 28/12/2020 Suppress all possible compiler warnings
|
||||
1.1.2 K Hoang 10/10/2021 Update `platform.ini` and `library.json`
|
||||
1.2.0 K Hoang 26/11/2021 Auto detect ESP32 core and use either built-in LittleFS or LITTLEFS library
|
||||
1.2.1 K Hoang 26/11/2021 Fix compile error for ESP32 core v1.0.5-
|
||||
*****************************************************************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef ESP_DoubleResetDetector_H
|
||||
#define ESP_DoubleResetDetector_H
|
||||
|
||||
#if defined(ARDUINO) && (ARDUINO >= 100)
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <WProgram.h>
|
||||
#endif
|
||||
|
||||
#define ESP_DOUBLE_RESET_DETECTOR_VERSION "ESP_DoubleResetDetector v1.2.1"
|
||||
#define ESP_DOUBLERESETDETECTOR_VERSION ESP_DOUBLE_RESET_DETECTOR_VERSION
|
||||
|
||||
//#define ESP_DRD_USE_EEPROM false
|
||||
//#define ESP_DRD_USE_LITTLEFS false
|
||||
//#define ESP_DRD_USE_SPIFFS false
|
||||
//#define ESP8266_DRD_USE_RTC false //true
|
||||
|
||||
#ifdef ESP32
|
||||
#if (!ESP_DRD_USE_EEPROM && !ESP_DRD_USE_SPIFFS && !ESP_DRD_USE_LITTLEFS)
|
||||
#warning Neither EEPROM, SPIFFS nor LittleFS selected. Default to EEPROM
|
||||
#ifdef ESP_DRD_USE_EEPROM
|
||||
#undef ESP_DRD_USE_EEPROM
|
||||
#define ESP_DRD_USE_EEPROM true
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ESP8266
|
||||
#if (!ESP8266_DRD_USE_RTC && !ESP_DRD_USE_EEPROM && !ESP_DRD_USE_SPIFFS && !ESP_DRD_USE_LITTLEFS)
|
||||
#warning Neither RTC, EEPROM, LITTLEFS nor SPIFFS selected. Default to EEPROM
|
||||
#ifdef ESP_DRD_USE_EEPROM
|
||||
#undef ESP_DRD_USE_EEPROM
|
||||
#define ESP_DRD_USE_EEPROM true
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//default to use EEPROM, otherwise, use LITTLEFS (higher priority), then SPIFFS
|
||||
#if ESP_DRD_USE_EEPROM
|
||||
#include <EEPROM.h>
|
||||
|
||||
#define FLAG_DATA_SIZE 4
|
||||
|
||||
#ifndef EEPROM_SIZE
|
||||
#define EEPROM_SIZE 512
|
||||
#endif
|
||||
|
||||
#ifndef EEPROM_START
|
||||
#define EEPROM_START 256
|
||||
#endif
|
||||
|
||||
#elif ( ESP_DRD_USE_LITTLEFS || ESP_DRD_USE_SPIFFS )
|
||||
|
||||
#include <FS.h>
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
#if ESP_DRD_USE_LITTLEFS
|
||||
// Check cores/esp32/esp_arduino_version.h and cores/esp32/core_version.h
|
||||
//#if ( ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0) ) //(ESP_ARDUINO_VERSION_MAJOR >= 2)
|
||||
#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) )
|
||||
#warning Using ESP32 Core 1.0.6 or 2.0.0+
|
||||
// The library has been merged into esp32 core from release 1.0.6
|
||||
#include <LittleFS.h>
|
||||
|
||||
#define FileFS LittleFS
|
||||
#define FS_Name "LittleFS"
|
||||
#else
|
||||
#warning Using ESP32 Core 1.0.5-. You must install LITTLEFS library
|
||||
// The library has been merged into esp32 core from release 1.0.6
|
||||
#include <LITTLEFS.h> // https://github.com/lorol/LITTLEFS
|
||||
|
||||
#define FileFS LITTLEFS
|
||||
#define FS_Name "LittleFS"
|
||||
#endif
|
||||
#else
|
||||
#include "SPIFFS.h"
|
||||
// ESP32 core 1.0.4 still uses SPIFFS
|
||||
#define FileFS SPIFFS
|
||||
#endif
|
||||
|
||||
#else
|
||||
// From ESP8266 core 2.7.1
|
||||
#include <LittleFS.h>
|
||||
|
||||
#if ESP_DRD_USE_LITTLEFS
|
||||
#define FileFS LittleFS
|
||||
#else
|
||||
#define FileFS SPIFFS
|
||||
#endif
|
||||
|
||||
#endif // #if ESP_DRD_USE_EEPROM
|
||||
|
||||
|
||||
|
||||
#define DRD_FILENAME "/drd.dat"
|
||||
|
||||
#endif //#if ESP_DRD_USE_EEPROM
|
||||
|
||||
#ifndef DOUBLERESETDETECTOR_DEBUG
|
||||
#define DOUBLERESETDETECTOR_DEBUG false
|
||||
#endif
|
||||
|
||||
#define DOUBLERESETDETECTOR_FLAG_SET 0xD0D01234
|
||||
#define DOUBLERESETDETECTOR_FLAG_CLEAR 0xD0D04321
|
||||
|
||||
class DoubleResetDetector
|
||||
{
|
||||
public:
|
||||
DoubleResetDetector(int timeout, int address)
|
||||
{
|
||||
#if ESP_DRD_USE_EEPROM
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
Serial.printf("EEPROM size = %d, start = %d\n", EEPROM_SIZE, EEPROM_START);
|
||||
#endif
|
||||
|
||||
EEPROM.begin(EEPROM_SIZE);
|
||||
#elif ( ESP_DRD_USE_LITTLEFS || ESP_DRD_USE_SPIFFS )
|
||||
// LittleFS / SPIFFS code
|
||||
if (!FileFS.begin())
|
||||
{
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
|
||||
#if ESP_DRD_USE_LITTLEFS
|
||||
Serial.println("LittleFS failed!. Please use SPIFFS or EEPROM.");
|
||||
#else
|
||||
Serial.println("SPIFFS failed!. Please use LittleFS or EEPROM.");
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
#ifdef ESP8266
|
||||
//RTC only for ESP8266
|
||||
#endif
|
||||
#endif
|
||||
|
||||
this->timeout = timeout * 1000;
|
||||
this->address = address;
|
||||
doubleResetDetected = false;
|
||||
waitingForDoubleReset = false;
|
||||
};
|
||||
|
||||
bool detectDoubleReset()
|
||||
{
|
||||
doubleResetDetected = detectRecentlyResetFlag();
|
||||
|
||||
if (doubleResetDetected)
|
||||
{
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
Serial.println("doubleResetDetected");
|
||||
#endif
|
||||
|
||||
clearRecentlyResetFlag();
|
||||
}
|
||||
else
|
||||
{
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
Serial.println("No doubleResetDetected");
|
||||
#endif
|
||||
|
||||
setRecentlyResetFlag();
|
||||
waitingForDoubleReset = true;
|
||||
}
|
||||
|
||||
return doubleResetDetected;
|
||||
|
||||
};
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (waitingForDoubleReset && millis() > timeout)
|
||||
{
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
Serial.println("Stop doubleResetDetecting");
|
||||
#endif
|
||||
|
||||
stop();
|
||||
}
|
||||
};
|
||||
|
||||
void stop()
|
||||
{
|
||||
clearRecentlyResetFlag();
|
||||
waitingForDoubleReset = false;
|
||||
};
|
||||
|
||||
bool doubleResetDetected;
|
||||
|
||||
|
||||
private:
|
||||
uint32_t DOUBLERESETDETECTOR_FLAG;
|
||||
unsigned long timeout;
|
||||
int address;
|
||||
bool waitingForDoubleReset;
|
||||
|
||||
bool detectRecentlyResetFlag()
|
||||
{
|
||||
#if (ESP_DRD_USE_EEPROM)
|
||||
EEPROM.get(EEPROM_START, DOUBLERESETDETECTOR_FLAG);
|
||||
doubleResetDetectorFlag = DOUBLERESETDETECTOR_FLAG;
|
||||
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
Serial.printf("EEPROM Flag read = 0x%X\n", DOUBLERESETDETECTOR_FLAG);
|
||||
#endif
|
||||
#elif ( ESP_DRD_USE_LITTLEFS || ESP_DRD_USE_SPIFFS )
|
||||
// LittleFS / SPIFFS code
|
||||
if (FileFS.exists(DRD_FILENAME))
|
||||
{
|
||||
// if config file exists, load
|
||||
File file = FileFS.open(DRD_FILENAME, "r");
|
||||
|
||||
if (!file)
|
||||
{
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
Serial.println("Loading config file failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
file.readBytes((char *) &DOUBLERESETDETECTOR_FLAG, sizeof(DOUBLERESETDETECTOR_FLAG));
|
||||
doubleResetDetectorFlag = DOUBLERESETDETECTOR_FLAG;
|
||||
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
|
||||
#if ESP_DRD_USE_LITTLEFS
|
||||
Serial.printf("LittleFS Flag read = 0x%X\n", DOUBLERESETDETECTOR_FLAG);
|
||||
#else
|
||||
Serial.printf("SPIFFS Flag read = 0x%X\n", DOUBLERESETDETECTOR_FLAG);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
file.close();
|
||||
}
|
||||
#else
|
||||
#ifdef ESP8266
|
||||
//RTC only for ESP8266
|
||||
ESP.rtcUserMemoryRead(address, &doubleResetDetectorFlag, sizeof(doubleResetDetectorFlag));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
doubleResetDetected = (doubleResetDetectorFlag == DOUBLERESETDETECTOR_FLAG_SET);
|
||||
return doubleResetDetected;
|
||||
};
|
||||
|
||||
void setRecentlyResetFlag()
|
||||
{
|
||||
doubleResetDetectorFlag = DOUBLERESETDETECTOR_FLAG_SET;
|
||||
|
||||
DOUBLERESETDETECTOR_FLAG = DOUBLERESETDETECTOR_FLAG_SET;
|
||||
|
||||
#if (ESP_DRD_USE_EEPROM)
|
||||
EEPROM.put(EEPROM_START, DOUBLERESETDETECTOR_FLAG);
|
||||
EEPROM.commit();
|
||||
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
delay(1000);
|
||||
EEPROM.get(EEPROM_START, DOUBLERESETDETECTOR_FLAG);
|
||||
|
||||
Serial.printf("SetFlag write = 0x%X\n", DOUBLERESETDETECTOR_FLAG);
|
||||
#endif
|
||||
#elif ( ESP_DRD_USE_LITTLEFS || ESP_DRD_USE_SPIFFS )
|
||||
// LittleFS / SPIFFS code
|
||||
File file = FileFS.open(DRD_FILENAME, "w");
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
Serial.println("Saving config file...");
|
||||
#endif
|
||||
|
||||
if (file)
|
||||
{
|
||||
file.write((uint8_t *) &DOUBLERESETDETECTOR_FLAG, sizeof(DOUBLERESETDETECTOR_FLAG));
|
||||
file.close();
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
Serial.println("Saving config file OK");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
Serial.println("Saving config file failed");
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
#ifdef ESP8266
|
||||
//RTC only for ESP8266
|
||||
ESP.rtcUserMemoryWrite(address, &doubleResetDetectorFlag, sizeof(doubleResetDetectorFlag));
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
void clearRecentlyResetFlag()
|
||||
{
|
||||
doubleResetDetectorFlag = DOUBLERESETDETECTOR_FLAG_CLEAR;
|
||||
DOUBLERESETDETECTOR_FLAG = DOUBLERESETDETECTOR_FLAG_CLEAR;
|
||||
|
||||
#if (ESP_DRD_USE_EEPROM)
|
||||
//DOUBLERESETDETECTOR_FLAG = DOUBLERESETDETECTOR_FLAG_CLEAR;
|
||||
EEPROM.put(EEPROM_START, DOUBLERESETDETECTOR_FLAG);
|
||||
EEPROM.commit();
|
||||
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
delay(1000);
|
||||
EEPROM.get(EEPROM_START, DOUBLERESETDETECTOR_FLAG);
|
||||
|
||||
Serial.printf("ClearFlag write = 0x%X\n", DOUBLERESETDETECTOR_FLAG);
|
||||
#endif
|
||||
#elif ( ESP_DRD_USE_LITTLEFS || ESP_DRD_USE_SPIFFS )
|
||||
// LittleFS / SPIFFS code
|
||||
File file = FileFS.open(DRD_FILENAME, "w");
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
Serial.println("Saving config file...");
|
||||
#endif
|
||||
|
||||
if (file)
|
||||
{
|
||||
file.write((uint8_t *) &DOUBLERESETDETECTOR_FLAG, sizeof(DOUBLERESETDETECTOR_FLAG));
|
||||
file.close();
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
Serial.println("Saving config file OK");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
Serial.println("Saving config file failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
#ifdef ESP8266
|
||||
//RTC only for ESP8266
|
||||
ESP.rtcUserMemoryWrite(address, &doubleResetDetectorFlag, sizeof(doubleResetDetectorFlag));
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
uint32_t doubleResetDetectorFlag;
|
||||
};
|
||||
#endif // ESP_DoubleResetDetector_H
|
2122
lib/ESP_WiFiManager/ESP_WiFiManager-Impl.h
Normal file
728
lib/ESP_WiFiManager/ESP_WiFiManager.h
Normal file
127
lib/ESP_WiFiManager/ESP_WiFiManager_Debug.h
Normal file
@ -0,0 +1,127 @@
|
||||
/****************************************************************************************************************************
|
||||
ESP_WiFiManager_Debug.h
|
||||
For ESP8266 / ESP32 boards
|
||||
|
||||
ESP_WiFiManager is a library for the ESP8266/Arduino platform
|
||||
(https://github.com/esp8266/Arduino) to enable easy
|
||||
configuration and reconfiguration of WiFi credentials using a Captive Portal
|
||||
inspired by:
|
||||
http://www.esp8266.com/viewtopic.php?f=29&t=2520
|
||||
https://github.com/chriscook8/esp-arduino-apboot
|
||||
https://github.com/esp8266/Arduino/blob/master/libraries/DNSServer/examples/CaptivePortalAdvanced/
|
||||
|
||||
Modified from Tzapu https://github.com/tzapu/WiFiManager
|
||||
and from Ken Taylor https://github.com/kentaylor
|
||||
|
||||
Built by Khoi Hoang https://github.com/khoih-prog/ESP_WiFiManager
|
||||
Licensed under MIT license
|
||||
|
||||
Version: 1.8.0
|
||||
|
||||
Version Modified By Date Comments
|
||||
------- ----------- ---------- -----------
|
||||
1.0.0 K Hoang 07/10/2019 Initial coding
|
||||
1.0.1 K Hoang 13/12/2019 Fix bug. Add features. Add support for ESP32
|
||||
1.0.2 K Hoang 19/12/2019 Fix bug thatkeeps ConfigPortal in endless loop if Portal/Router SSID or Password is NULL.
|
||||
1.0.3 K Hoang 05/01/2020 Option not displaying AvailablePages in Info page. Enhance README.md. Modify examples
|
||||
1.0.4 K Hoang 07/01/2020 Add RFC952 setHostname feature.
|
||||
1.0.5 K Hoang 15/01/2020 Add configurable DNS feature. Thanks to @Amorphous of https://community.blynk.cc
|
||||
1.0.6 K Hoang 03/02/2020 Add support for ArduinoJson version 6.0.0+ ( tested with v6.14.1 )
|
||||
1.0.7 K Hoang 13/04/2020 Reduce start time, fix SPIFFS bug in examples, update README.md
|
||||
1.0.8 K Hoang 10/06/2020 Fix STAstaticIP issue. Restructure code. Add LittleFS support for ESP8266 core 2.7.1+
|
||||
1.0.9 K Hoang 29/07/2020 Fix ESP32 STAstaticIP bug. Permit changing from DHCP <-> static IP using Config Portal.
|
||||
Add, enhance examples (fix MDNS for ESP32)
|
||||
1.0.10 K Hoang 08/08/2020 Add more features to Config Portal. Use random WiFi AP channel to avoid conflict.
|
||||
1.0.11 K Hoang 17/08/2020 Add CORS feature. Fix bug in softAP, autoConnect, resetSettings.
|
||||
1.1.0 K Hoang 28/08/2020 Add MultiWiFi feature to autoconnect to best WiFi at runtime
|
||||
1.1.1 K Hoang 30/08/2020 Add setCORSHeader function to allow flexible CORS. Fix typo and minor improvement.
|
||||
1.1.2 K Hoang 17/08/2020 Fix bug. Add example.
|
||||
1.2.0 K Hoang 09/10/2020 Restore cpp code besides Impl.h code to use if linker error. Fix bug.
|
||||
1.3.0 K Hoang 04/12/2020 Add LittleFS support to ESP32 using LITTLEFS Library
|
||||
1.4.1 K Hoang 22/12/2020 Fix staticIP not saved. Add functions. Add complex examples. Sync with ESPAsync_WiFiManager
|
||||
1.4.2 K Hoang 14/01/2021 Fix examples' bug not using saved WiFi Credentials after losing all WiFi connections.
|
||||
1.4.3 K Hoang 23/01/2021 Fix examples' bug not saving Static IP in certain cases.
|
||||
1.5.0 K Hoang 12/02/2021 Add support to new ESP32-S2
|
||||
1.5.1 K Hoang 26/03/2021 Fix compiler error if setting Compiler Warnings to All. Retest with esp32 core v1.0.6
|
||||
1.5.2 K Hoang 08/04/2021 Fix example misleading messages.
|
||||
1.5.3 K Hoang 13/04/2021 Add dnsServer error message.
|
||||
1.6.0 K Hoang 20/04/2021 Add support to new ESP32-C3 using SPIFFS or EEPROM
|
||||
1.6.1 K Hoang 25/04/2021 Fix MultiWiFi bug. Fix captive-portal bug if CP AP address is not default 192.168.4.1
|
||||
1.7.0 K Hoang 06/05/2021 Set _timezoneName. Add support to new ESP32-S2 (METRO_ESP32S2, FUNHOUSE_ESP32S2, etc.)
|
||||
1.7.1 K Hoang 08/05/2021 Fix Json bug. Fix timezoneName not displayed in Info page.
|
||||
1.7.2 K Hoang 08/05/2021 Fix warnings with ESP8266 core v3.0.0
|
||||
1.7.3 K Hoang 29/07/2021 Fix MultiWiFi connection issue with ESP32 core v2.0.0-rc1+
|
||||
1.7.4 K Hoang 13/08/2021 Add WiFi scanning of hidden SSIDs
|
||||
1.7.5 K Hoang 10/10/2021 Update `platform.ini` and `library.json`
|
||||
1.7.6 K Hoang 26/11/2021 Auto detect ESP32 core and use either built-in LittleFS or LITTLEFS library
|
||||
1.7.7 K Hoang 26/11/2021 Fix compile error for ESP32 core v1.0.5-
|
||||
1.7.8 K Hoang 30/11/2021 Fix bug to permit using HTTP port different from 80. Fix bug
|
||||
1.8.0 K Hoang 29/12/2021 Fix `multiple-definitions` linker error and weird bug related to src_cpp
|
||||
*****************************************************************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef ESP_WiFiManager_Debug_H
|
||||
#define ESP_WiFiManager_Debug_H
|
||||
|
||||
#ifdef WIFIMGR_DEBUG_PORT
|
||||
#define WM_DBG_PORT WIFIMGR_DEBUG_PORT
|
||||
#else
|
||||
#define WM_DBG_PORT Serial
|
||||
#endif
|
||||
|
||||
// Change _WIFIMGR_LOGLEVEL_ to set tracing and logging verbosity
|
||||
// 0: DISABLED: no logging
|
||||
// 1: ERROR: errors
|
||||
// 2: WARN: errors and warnings
|
||||
// 3: INFO: errors, warnings and informational (default)
|
||||
// 4: DEBUG: errors, warnings, informational and debug
|
||||
|
||||
#ifndef _WIFIMGR_LOGLEVEL_
|
||||
#define _WIFIMGR_LOGLEVEL_ 0
|
||||
#endif
|
||||
|
||||
const char WM_MARK[] = "[WM] ";
|
||||
const char WM_SP[] = " ";
|
||||
|
||||
#define WM_PRINT WM_DBG_PORT.print
|
||||
#define WM_PRINTLN WM_DBG_PORT.println
|
||||
|
||||
#define WM_PRINT_MARK WM_PRINT(WM_MARK)
|
||||
#define WM_PRINT_SP WM_PRINT(WM_SP)
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
#define LOGERROR(x) if(_WIFIMGR_LOGLEVEL_>0) { WM_PRINT_MARK; WM_PRINTLN(x); }
|
||||
#define LOGERROR0(x) if(_WIFIMGR_LOGLEVEL_>0) { WM_PRINT(x); }
|
||||
#define LOGERROR1(x,y) if(_WIFIMGR_LOGLEVEL_>0) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINTLN(y); }
|
||||
#define LOGERROR2(x,y,z) if(_WIFIMGR_LOGLEVEL_>0) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINT(y); WM_PRINT_SP; WM_PRINTLN(z); }
|
||||
#define LOGERROR3(x,y,z,w) if(_WIFIMGR_LOGLEVEL_>0) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINT(y); WM_PRINT_SP; WM_PRINT(z); WM_PRINT_SP; WM_PRINTLN(w); }
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
#define LOGWARN(x) if(_WIFIMGR_LOGLEVEL_>1) { WM_PRINT_MARK; WM_PRINTLN(x); }
|
||||
#define LOGWARN0(x) if(_WIFIMGR_LOGLEVEL_>1) { WM_PRINT(x); }
|
||||
#define LOGWARN1(x,y) if(_WIFIMGR_LOGLEVEL_>1) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINTLN(y); }
|
||||
#define LOGWARN2(x,y,z) if(_WIFIMGR_LOGLEVEL_>1) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINT(y); WM_PRINT_SP; WM_PRINTLN(z); }
|
||||
#define LOGWARN3(x,y,z,w) if(_WIFIMGR_LOGLEVEL_>1) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINT(y); WM_PRINT_SP; WM_PRINT(z); WM_PRINT_SP; WM_PRINTLN(w); }
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
#define LOGINFO(x) if(_WIFIMGR_LOGLEVEL_>2) { WM_PRINT_MARK; WM_PRINTLN(x); }
|
||||
#define LOGINFO0(x) if(_WIFIMGR_LOGLEVEL_>2) { WM_PRINT(x); }
|
||||
#define LOGINFO1(x,y) if(_WIFIMGR_LOGLEVEL_>2) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINTLN(y); }
|
||||
#define LOGINFO2(x,y,z) if(_WIFIMGR_LOGLEVEL_>2) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINT(y); WM_PRINT_SP; WM_PRINTLN(z); }
|
||||
#define LOGINFO3(x,y,z,w) if(_WIFIMGR_LOGLEVEL_>2) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINT(y); WM_PRINT_SP; WM_PRINT(z); WM_PRINT_SP; WM_PRINTLN(w); }
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
#define LOGDEBUG(x) if(_WIFIMGR_LOGLEVEL_>3) { WM_PRINT_MARK; WM_PRINTLN(x); }
|
||||
#define LOGDEBUG0(x) if(_WIFIMGR_LOGLEVEL_>3) { WM_PRINT(x); }
|
||||
#define LOGDEBUG1(x,y) if(_WIFIMGR_LOGLEVEL_>3) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINTLN(y); }
|
||||
#define LOGDEBUG2(x,y,z) if(_WIFIMGR_LOGLEVEL_>3) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINT(y); WM_PRINT_SP; WM_PRINTLN(z); }
|
||||
#define LOGDEBUG3(x,y,z,w) if(_WIFIMGR_LOGLEVEL_>3) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINT(y); WM_PRINT_SP; WM_PRINT(z); WM_PRINT_SP; WM_PRINTLN(w); }
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
#endif //ESP_WiFiManager_Debug_H
|
2150
lib/ESP_WiFiManager/utils/TZ.h
Normal file
@ -15,46 +15,44 @@ include_dir = lib
|
||||
[common_env_data]
|
||||
upload_speed = 921600
|
||||
monitor_speed = 115200
|
||||
#platform = espressif8266 @ 2.6.3
|
||||
platform = espressif8266 @ 3.2.0
|
||||
framework = arduino
|
||||
board = d1_mini
|
||||
build_unflags =
|
||||
#src_build_flags = -Wunused-variable -Wregister -Wchar-subscripts
|
||||
build_flags =
|
||||
-Wl,-Map,output.map
|
||||
-D BAUD=${common_env_data.monitor_speed}
|
||||
-D ACTIVATE_OTA
|
||||
#-D USE_GYRO_TEMP # If this is enabled the DS18 will not be used, temp is read from the gyro.
|
||||
#-D DEBUG_ESP_HTTP_CLIENT
|
||||
#-D DEBUG_ESP_HTTP_SERVER
|
||||
#-D DEBUG_ESP_PORT=Serial
|
||||
#-D DEBUG_ESP_WIFI
|
||||
#-D DEBUG_ESP_CORE
|
||||
#-D SKIP_SLEEPMODE
|
||||
#-D DOUBLERESETDETECTOR_DEBUG true
|
||||
-D CFG_DISABLE_LOGGING # Turn off verbose logging for some of the parts (too much will cause a crash) but also add space
|
||||
-D GYRO_DISABLE_LOGGING
|
||||
-D PUSH_DISABLE_LOGGING
|
||||
-D TSEN_DISABLE_LOGGING
|
||||
-D WEB_DISABLE_LOGGING
|
||||
#-D MAIN_DISABLE_LOGGING
|
||||
-D MAIN_DISABLE_LOGGING
|
||||
-D USE_LITTLEFS=true
|
||||
-D EMBED_HTML # If this is not used the html files needs to be on the file system (can be uploaded)
|
||||
-D USER_SSID=\""\"" # =\""myssid\""
|
||||
-D USER_SSID_PWD=\""\"" # =\""mypwd\""
|
||||
-D CFG_APPVER="\"0.5.0\""
|
||||
-D CFG_APPVER="\"0.6.0\""
|
||||
lib_deps = # Switched to forks for better version control.
|
||||
# Using local copy of this library
|
||||
#https://github.com/mp-se/i2cdevlib # https://github.com/jrowberg/i2cdevlib.git
|
||||
#https://github.com/jrowberg/i2cdevlib.git#<document>
|
||||
#https://github.com/khoih-prog/ESP_WiFiManager#<document>
|
||||
#https://github.com/khoih-prog/ESP_DoubleResetDetector#<document>
|
||||
https://github.com/mp-se/tinyexpr # https://github.com/codeplea/tinyexpr
|
||||
https://github.com/mp-se/incbin # https://github.com/graphitemaster/incbin
|
||||
https://github.com/mp-se/ESP_DoubleResetDetector#v1.2.1 # https://github.com/khoih-prog/ESP_DoubleResetDetector
|
||||
https://github.com/mp-se/WiFiManager#2.0.5-beta # https://github.com/tzapu/WiFiManager
|
||||
https://github.com/mp-se/Arduino-Log#1.1.1 # https://github.com/thijse/Arduino-Log
|
||||
https://github.com/mp-se/ArduinoJson#v6.18.5 # https://github.com/bblanchon/ArduinoJson
|
||||
https://github.com/mp-se/OneWire#v2.3.6 # https://github.com/PaulStoffregen/OneWire
|
||||
https://github.com/mp-se/Arduino-Temperature-Control-Library#3.9.1 # https://github.com/milesburton/Arduino-Temperature-Control-Library
|
||||
https://github.com/mp-se/arduinoCurveFitting#v1.0.6 # https://github.com/Rotario/arduinoCurveFitting
|
||||
https://github.com/mp-se/arduino-mqtt#v2.5.0 # https://github.com/256dpi/arduino-mqtt
|
||||
|
||||
[env:gravity-debug]
|
||||
upload_speed = ${common_env_data.upload_speed}
|
||||
@ -67,17 +65,21 @@ extra_scripts =
|
||||
script/create_versionjson.py
|
||||
build_unflags =
|
||||
${common_env_data.build_unflags}
|
||||
# -D MAIN_DISABLE_LOGGING
|
||||
-D WEB_DISABLE_LOGGING
|
||||
-D PUSH_DISABLE_LOGGING
|
||||
build_flags =
|
||||
#-D PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS
|
||||
#-D SKIP_SLEEPMODE
|
||||
${common_env_data.build_flags}
|
||||
-D PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS
|
||||
#-D SKIP_SLEEPMODE
|
||||
-D DOUBLERESETDETECTOR_DEBUG=true
|
||||
-D COLLECT_PERFDATA # This option will collect runtime data for a few defined methods to measure time, dumped to serial and/or influxdb
|
||||
-D LOG_LEVEL=6 # Maximum log level for the debug build.
|
||||
lib_deps =
|
||||
${common_env_data.lib_deps}
|
||||
#board = ${common_env_data.board}
|
||||
board = nodemcuv2
|
||||
build_type = debug
|
||||
board = ${common_env_data.board}
|
||||
#build_type = debug # Using debug type crashes my devkit...
|
||||
build_type = release
|
||||
board_build.filesystem = littlefs
|
||||
monitor_filters = esp8266_exception_decoder
|
||||
|
||||
|
85
script/create_cert.py
Normal file
@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# This script pulls the list of Mozilla trusted certificate authorities
|
||||
# from the web at the "mozurl" below, parses the file to grab the PEM
|
||||
# for each cert, and then generates DER files in a new ./data directory
|
||||
# Upload these to an on-chip filesystem and use the CertManager to parse
|
||||
# and use them for your outgoing SSL connections.
|
||||
#
|
||||
# Script by Earle F. Philhower, III. Released to the public domain.
|
||||
from __future__ import print_function
|
||||
import csv
|
||||
import os
|
||||
import sys
|
||||
from shutil import which
|
||||
|
||||
# Change the path to the installed files.
|
||||
arCmd = "C:\\Users\\magnu\\.platformio\\packages\\toolchain-xtensa\\bin\\xtensa-lx106-elf-ar.exe"
|
||||
opensslCmd = "C:\\Program Files\\Git\\usr\\bin\\openssl.exe"
|
||||
|
||||
from subprocess import Popen, PIPE, call
|
||||
try:
|
||||
from urllib.request import urlopen
|
||||
except Exception:
|
||||
from urllib2 import urlopen
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except Exception:
|
||||
from io import StringIO
|
||||
|
||||
# check if ar and openssl are available
|
||||
#if which('ar') is None and not os.path.isfile('./ar') and not os.path.isfile('./ar.exe'):
|
||||
# raise Exception("You need the program 'ar' from xtensa-lx106-elf found here: (esp8266-arduino-core)/hardware/esp8266com/esp8266/tools/xtensa-lx106-elf/xtensa-lx106-elf/bin/ar")
|
||||
#if which('openssl') is None and not os.path.isfile('./openssl') and not os.path.isfile('./openssl.exe'):
|
||||
# raise Exception("You need to have openssl in PATH, installable from https://www.openssl.org/")
|
||||
|
||||
# Mozilla's URL for the CSV file with included PEM certs
|
||||
mozurl = "https://ccadb-public.secure.force.com/mozilla/IncludedCACertificateReportPEMCSV"
|
||||
|
||||
# Load the names[] and pems[] array from the URL
|
||||
names = []
|
||||
pems = []
|
||||
response = urlopen(mozurl)
|
||||
csvData = response.read()
|
||||
if sys.version_info[0] > 2:
|
||||
csvData = csvData.decode('utf-8')
|
||||
csvFile = StringIO(csvData)
|
||||
csvReader = csv.reader(csvFile)
|
||||
for row in csvReader:
|
||||
names.append(row[0]+":"+row[1]+":"+row[2])
|
||||
for item in row:
|
||||
if item.startswith("'-----BEGIN CERTIFICATE-----"):
|
||||
pems.append(item)
|
||||
del names[0] # Remove headers
|
||||
del pems[0] # Remove headers
|
||||
|
||||
# Try and make ./data, skip if present
|
||||
try:
|
||||
os.mkdir("../data")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
derFiles = []
|
||||
idx = 0
|
||||
# Process the text PEM using openssl into DER files
|
||||
for i in range(0, len(pems)):
|
||||
certName = "../data/ca_%03d.der" % (idx);
|
||||
thisPem = pems[i].replace("'", "")
|
||||
print(names[i] + " -> " + certName)
|
||||
ssl = Popen([opensslCmd,'x509','-inform','PEM','-outform','DER','-out', certName], shell = False, stdin = PIPE)
|
||||
pipe = ssl.stdin
|
||||
pipe.write(thisPem.encode('utf-8'))
|
||||
pipe.close()
|
||||
ssl.wait()
|
||||
if os.path.exists(certName):
|
||||
derFiles.append(certName)
|
||||
idx = idx + 1
|
||||
|
||||
if os.path.exists("../data/certs.ar"):
|
||||
os.unlink("../data/certs.ar");
|
||||
|
||||
arCmd = [arCmd, 'q', '../data/certs.ar'] + derFiles;
|
||||
call( arCmd )
|
||||
|
||||
for der in derFiles:
|
||||
os.unlink(der)
|
635
src/certificates.h
Normal file
@ -52,6 +52,7 @@ Config::Config() {
|
||||
setGravityTempAdj(false);
|
||||
gyroCalibration = {0, 0, 0, 0, 0, 0};
|
||||
formulaData = {{0, 0, 0, 0, 0}, {1, 1, 1, 1, 1}};
|
||||
gyroTemp = false;
|
||||
saveNeeded = false;
|
||||
}
|
||||
|
||||
@ -73,12 +74,17 @@ void Config::createJson(DynamicJsonDocument& doc) {
|
||||
doc[CFG_PARAM_PUSH_INFLUXDB2_ORG] = getInfluxDb2PushOrg();
|
||||
doc[CFG_PARAM_PUSH_INFLUXDB2_BUCKET] = getInfluxDb2PushBucket();
|
||||
doc[CFG_PARAM_PUSH_INFLUXDB2_AUTH] = getInfluxDb2PushToken();
|
||||
doc[CFG_PARAM_PUSH_MQTT] = getMqttUrl();
|
||||
doc[CFG_PARAM_PUSH_MQTT_TOPIC] = getMqttTopic();
|
||||
doc[CFG_PARAM_PUSH_MQTT_USER] = getMqttUser();
|
||||
doc[CFG_PARAM_PUSH_MQTT_PASS] = getMqttPass();
|
||||
doc[CFG_PARAM_SLEEP_INTERVAL] = getSleepInterval();
|
||||
doc[CFG_PARAM_VOLTAGEFACTOR] = getVoltageFactor();
|
||||
doc[CFG_PARAM_GRAVITY_FORMULA] = getGravityFormula();
|
||||
doc[CFG_PARAM_GRAVITY_FORMAT] = String(getGravityFormat());
|
||||
doc[CFG_PARAM_TEMP_ADJ] = getTempSensorAdj();
|
||||
doc[CFG_PARAM_GRAVITY_TEMP_ADJ] = isGravityTempAdj();
|
||||
doc[CFG_PARAM_GYRO_TEMP] = isGyroTemp();
|
||||
|
||||
JsonObject cal = doc.createNestedObject(CFG_PARAM_GYRO_CALIBRATION);
|
||||
cal["ax"] = gyroCalibration.ax;
|
||||
@ -187,16 +193,20 @@ bool Config::loadFile() {
|
||||
if (!doc[CFG_PARAM_MDNS].isNull()) setMDNS(doc[CFG_PARAM_MDNS]);
|
||||
if (!doc[CFG_PARAM_SSID].isNull()) setWifiSSID(doc[CFG_PARAM_SSID]);
|
||||
if (!doc[CFG_PARAM_PASS].isNull()) setWifiPass(doc[CFG_PARAM_PASS]);
|
||||
|
||||
if (!doc[CFG_PARAM_TEMPFORMAT].isNull()) {
|
||||
String s = doc[CFG_PARAM_TEMPFORMAT];
|
||||
setTempFormat(s.charAt(0));
|
||||
}
|
||||
|
||||
if (!doc[CFG_PARAM_PUSH_BREWFATHER].isNull())
|
||||
setBrewfatherPushUrl(doc[CFG_PARAM_PUSH_BREWFATHER]);
|
||||
|
||||
if (!doc[CFG_PARAM_PUSH_HTTP].isNull())
|
||||
setHttpPushUrl(doc[CFG_PARAM_PUSH_HTTP]);
|
||||
if (!doc[CFG_PARAM_PUSH_HTTP2].isNull())
|
||||
setHttpPushUrl2(doc[CFG_PARAM_PUSH_HTTP2]);
|
||||
|
||||
if (!doc[CFG_PARAM_PUSH_INFLUXDB2].isNull())
|
||||
setInfluxDb2PushUrl(doc[CFG_PARAM_PUSH_INFLUXDB2]);
|
||||
if (!doc[CFG_PARAM_PUSH_INFLUXDB2_ORG].isNull())
|
||||
@ -205,6 +215,15 @@ bool Config::loadFile() {
|
||||
setInfluxDb2PushBucket(doc[CFG_PARAM_PUSH_INFLUXDB2_BUCKET]);
|
||||
if (!doc[CFG_PARAM_PUSH_INFLUXDB2_AUTH].isNull())
|
||||
setInfluxDb2PushToken(doc[CFG_PARAM_PUSH_INFLUXDB2_AUTH]);
|
||||
|
||||
if (!doc[CFG_PARAM_PUSH_MQTT].isNull()) setMqttUrl(doc[CFG_PARAM_PUSH_MQTT]);
|
||||
if (!doc[CFG_PARAM_PUSH_MQTT_TOPIC].isNull())
|
||||
setMqttTopic(doc[CFG_PARAM_PUSH_MQTT_TOPIC]);
|
||||
if (!doc[CFG_PARAM_PUSH_MQTT_USER].isNull())
|
||||
setMqttUser(doc[CFG_PARAM_PUSH_MQTT_USER]);
|
||||
if (!doc[CFG_PARAM_PUSH_MQTT_PASS].isNull())
|
||||
setMqttPass(doc[CFG_PARAM_PUSH_MQTT_PASS]);
|
||||
|
||||
if (!doc[CFG_PARAM_SLEEP_INTERVAL].isNull())
|
||||
setSleepInterval(doc[CFG_PARAM_SLEEP_INTERVAL].as<int>());
|
||||
if (!doc[CFG_PARAM_VOLTAGEFACTOR].isNull())
|
||||
@ -213,6 +232,8 @@ bool Config::loadFile() {
|
||||
setGravityFormula(doc[CFG_PARAM_GRAVITY_FORMULA]);
|
||||
if (!doc[CFG_PARAM_GRAVITY_TEMP_ADJ].isNull())
|
||||
setGravityTempAdj(doc[CFG_PARAM_GRAVITY_TEMP_ADJ].as<bool>());
|
||||
if (!doc[CFG_PARAM_GYRO_TEMP].isNull())
|
||||
setGyroTemp(doc[CFG_PARAM_GYRO_TEMP].as<bool>());
|
||||
if (!doc[CFG_PARAM_GRAVITY_FORMAT].isNull()) {
|
||||
String s = doc[CFG_PARAM_GRAVITY_FORMAT];
|
||||
setGravityFormat(s.charAt(0));
|
||||
@ -253,7 +274,7 @@ bool Config::loadFile() {
|
||||
if (!doc[CFG_PARAM_FORMULA_DATA]["g4"].isNull())
|
||||
formulaData.g[3] = doc[CFG_PARAM_FORMULA_DATA]["g4"].as<double>();
|
||||
if (!doc[CFG_PARAM_FORMULA_DATA]["g5"].isNull())
|
||||
formulaData.g[4] = doc[CFG_PARAM_FORMULA_DATA]["g5"];
|
||||
formulaData.g[4] = doc[CFG_PARAM_FORMULA_DATA]["g5"].as<double>();
|
||||
|
||||
myConfig.debug();
|
||||
saveNeeded = false; // Reset save flag
|
||||
@ -303,6 +324,7 @@ void Config::debug() {
|
||||
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());
|
||||
|
@ -45,20 +45,24 @@ SOFTWARE.
|
||||
|
||||
// These are used in API + Savefile
|
||||
#define CFG_PARAM_ID "id"
|
||||
#define CFG_PARAM_MDNS "mdns" // Device name
|
||||
#define CFG_PARAM_OTA "ota-url" // Base URL for OTA
|
||||
#define CFG_PARAM_SSID "wifi-ssid" // WIFI
|
||||
#define CFG_PARAM_PASS "wifi-pass" // WIFI
|
||||
#define CFG_PARAM_MDNS "mdns" // Device name
|
||||
#define CFG_PARAM_OTA "ota-url"
|
||||
#define CFG_PARAM_SSID "wifi-ssid"
|
||||
#define CFG_PARAM_PASS "wifi-pass"
|
||||
|
||||
#define CFG_PARAM_PUSH_BREWFATHER "brewfather-push" // URL (brewfather format)
|
||||
#define CFG_PARAM_PUSH_HTTP "http-push" // URL (iSpindle format)
|
||||
#define CFG_PARAM_PUSH_HTTP2 "http-push2" // URL (iSpindle format)
|
||||
#define CFG_PARAM_PUSH_INFLUXDB2 "influxdb2-push" // URL
|
||||
#define CFG_PARAM_PUSH_INFLUXDB2_ORG "influxdb2-org" // URL
|
||||
#define CFG_PARAM_PUSH_INFLUXDB2_BUCKET "influxdb2-bucket" // URL
|
||||
#define CFG_PARAM_PUSH_INFLUXDB2_AUTH "influxdb2-auth" // URL
|
||||
#define CFG_PARAM_SLEEP_INTERVAL "sleep-interval" // Sleep interval
|
||||
#define CFG_PARAM_TEMPFORMAT "temp-format" // C or F
|
||||
#define CFG_PARAM_PUSH_BREWFATHER "brewfather-push"
|
||||
#define CFG_PARAM_PUSH_HTTP "http-push"
|
||||
#define CFG_PARAM_PUSH_HTTP2 "http-push2"
|
||||
#define CFG_PARAM_PUSH_INFLUXDB2 "influxdb2-push"
|
||||
#define CFG_PARAM_PUSH_INFLUXDB2_ORG "influxdb2-org"
|
||||
#define CFG_PARAM_PUSH_INFLUXDB2_BUCKET "influxdb2-bucket"
|
||||
#define CFG_PARAM_PUSH_INFLUXDB2_AUTH "influxdb2-auth"
|
||||
#define CFG_PARAM_PUSH_MQTT "mqtt-push"
|
||||
#define CFG_PARAM_PUSH_MQTT_USER "mqtt-user"
|
||||
#define CFG_PARAM_PUSH_MQTT_PASS "mqtt-pass"
|
||||
#define CFG_PARAM_PUSH_MQTT_TOPIC "mqtt-topic"
|
||||
#define CFG_PARAM_SLEEP_INTERVAL "sleep-interval" // Sleep interval
|
||||
#define CFG_PARAM_TEMPFORMAT "temp-format" // C or F
|
||||
#define CFG_PARAM_VOLTAGEFACTOR \
|
||||
"voltage-factor" // Factor to calculate the battery voltage
|
||||
#define CFG_PARAM_GRAVITY_FORMULA \
|
||||
@ -69,6 +73,8 @@ SOFTWARE.
|
||||
#define CFG_PARAM_TEMP_ADJ \
|
||||
"temp-adjustment-value" // Correction value for temp sensor
|
||||
#define CFG_PARAM_GYRO_CALIBRATION "gyro-calibration-data" // READ ONLY
|
||||
#define CFG_PARAM_GYRO_TEMP \
|
||||
"gyro-temp" // True/False. Use temp sensor in gyro (only in gravity mode)
|
||||
|
||||
#define CFG_PARAM_FORMULA_DATA \
|
||||
"formula-calculation-data" // Raw data for the formula calculation
|
||||
@ -83,6 +89,7 @@ SOFTWARE.
|
||||
#define CFG_PARAM_BATTERY "battery"
|
||||
#define CFG_PARAM_SLEEP_MODE "sleep-mode"
|
||||
#define CFG_PARAM_RSSI "rssi"
|
||||
#define CFG_PARAM_CERTS "certs"
|
||||
|
||||
// Used for holding sensordata or sensoroffsets
|
||||
struct RawGyroData {
|
||||
@ -116,6 +123,7 @@ class Config {
|
||||
float voltageFactor;
|
||||
float tempSensorAdj; // This value will be added to the read sensor value
|
||||
int sleepInterval;
|
||||
bool gyroTemp; // Experimental feature
|
||||
|
||||
// Wifi Config
|
||||
String wifiSSID;
|
||||
@ -132,6 +140,11 @@ class Config {
|
||||
String influxDb2Bucket; // Bucket for InfluxDB v2
|
||||
String influxDb2Token; // Auth Token for InfluxDB v2
|
||||
|
||||
String mqttUrl; // Server name
|
||||
String mqttTopic;
|
||||
String mqttUser;
|
||||
String mqttPass;
|
||||
|
||||
// Gravity and temperature calculations
|
||||
String gravityFormula;
|
||||
bool gravityTempAdj; // true, false
|
||||
@ -155,12 +168,19 @@ class Config {
|
||||
saveNeeded = true;
|
||||
}
|
||||
|
||||
const bool isGyroTemp() { return gyroTemp; }
|
||||
void setGyroTemp(bool b) {
|
||||
gyroTemp = b;
|
||||
saveNeeded = true;
|
||||
}
|
||||
|
||||
const char* getOtaURL() { return otaURL.c_str(); }
|
||||
void setOtaURL(String s) {
|
||||
otaURL = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
bool isOtaActive() { return otaURL.length() ? true : false; }
|
||||
bool isOtaSecure() { return otaURL.startsWith("https://"); }
|
||||
|
||||
const char* getWifiSSID() { return wifiSSID.c_str(); }
|
||||
void setWifiSSID(String s) {
|
||||
@ -190,12 +210,14 @@ class Config {
|
||||
saveNeeded = true;
|
||||
}
|
||||
bool isHttpActive() { return httpPushUrl.length() ? true : false; }
|
||||
bool isHttpSecure() { return httpPushUrl.startsWith("https://"); }
|
||||
const char* getHttpPushUrl2() { return httpPushUrl2.c_str(); }
|
||||
void setHttpPushUrl2(String s) {
|
||||
httpPushUrl2 = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
bool isHttpActive2() { return httpPushUrl2.length() ? true : false; }
|
||||
bool isHttpSecure2() { return httpPushUrl2.startsWith("https://"); }
|
||||
|
||||
// InfluxDB2
|
||||
const char* getInfluxDb2PushUrl() { return influxDb2Url.c_str(); }
|
||||
@ -220,6 +242,30 @@ class Config {
|
||||
saveNeeded = true;
|
||||
}
|
||||
|
||||
// MQTT
|
||||
bool isMqttActive() { return mqttUrl.length() ? true : false; }
|
||||
bool isMqttSecure() { return mqttUrl.endsWith(":8883"); }
|
||||
const char* getMqttUrl() { return mqttUrl.c_str(); }
|
||||
void setMqttUrl(String s) {
|
||||
mqttUrl = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
const char* getMqttTopic() { return mqttTopic.c_str(); }
|
||||
void setMqttTopic(String s) {
|
||||
mqttTopic = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
const char* getMqttUser() { return mqttUser.c_str(); }
|
||||
void setMqttUser(String s) {
|
||||
mqttUser = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
const char* getMqttPass() { return mqttPass.c_str(); }
|
||||
void setMqttPass(String s) {
|
||||
mqttPass = s;
|
||||
saveNeeded = true;
|
||||
}
|
||||
|
||||
int getSleepInterval() { return sleepInterval; }
|
||||
void setSleepInterval(int v) {
|
||||
sleepInterval = v;
|
||||
|
@ -25,6 +25,7 @@ SOFTWARE.
|
||||
#include <helper.hpp>
|
||||
|
||||
GyroSensor myGyro;
|
||||
MPU6050 accelgyro;
|
||||
|
||||
#define GYRO_USE_INTERRUPT // Use interrupt to detect when new sample is ready
|
||||
#define SENSOR_MOVING_THREASHOLD 500
|
||||
@ -45,8 +46,7 @@ bool GyroSensor::setup() {
|
||||
Wire.begin(D3, D4);
|
||||
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having
|
||||
// compilation difficulties
|
||||
accelgyro.initialize();
|
||||
|
||||
|
||||
if (!accelgyro.testConnection()) {
|
||||
Log.error(F("GYRO: Failed to connect to MPU6050 (gyro)." CR));
|
||||
sensorConnected = false;
|
||||
@ -54,6 +54,7 @@ bool GyroSensor::setup() {
|
||||
#if !defined(GYRO_DISABLE_LOGGING)
|
||||
Log.notice(F("GYRO: Connected to MPU6050 (gyro)." CR));
|
||||
#endif
|
||||
accelgyro.initialize();
|
||||
sensorConnected = true;
|
||||
|
||||
// Configure the sensor
|
||||
|
@ -50,7 +50,6 @@ struct RawGyroDataL { // Used for average multiple readings
|
||||
|
||||
class GyroSensor {
|
||||
private:
|
||||
MPU6050 accelgyro;
|
||||
bool sensorConnected = false;
|
||||
bool validValue = false;
|
||||
float angle = 0;
|
||||
|
@ -220,7 +220,7 @@ void PerfLogging::pushInflux() {
|
||||
"angle=%.4f,gyro-ax=%d,gyro-ay=%d,gyro-az=%d,gyro-temp=%.2f,ds-temp=%.2f",
|
||||
myGyro.getAngle(), myGyro.getLastGyroData().ax,
|
||||
myGyro.getLastGyroData().ay, myGyro.getLastGyroData().az,
|
||||
myGyro.getSensorTempC(), myTempSensor.getTempC());
|
||||
myGyro.getSensorTempC(), myTempSensor.getTempC(myConfig.isGyroTemp()));
|
||||
body += &buf[0];
|
||||
|
||||
// Log.notice(F("PERF: data %s." CR), body.c_str() );
|
||||
|
387
src/main.cpp
@ -32,65 +32,64 @@ SOFTWARE.
|
||||
#include <webserver.hpp>
|
||||
#include <wifi.hpp>
|
||||
|
||||
// Settings for double reset detector.
|
||||
#define ESP8266_DRD_USE_RTC true
|
||||
#define DRD_TIMEOUT 2
|
||||
#define DRD_ADDRESS 0
|
||||
#include <ESP_DoubleResetDetector.h>
|
||||
DoubleResetDetector *drd;
|
||||
|
||||
// Define constats for this program
|
||||
#ifdef DEACTIVATE_SLEEPMODE
|
||||
const int interval = 1000; // ms, time to wait between changes to output
|
||||
bool sleepModeAlwaysSkip = true; // Web interface can override normal behaviour
|
||||
const int interval = 1000; // ms, time to wait between changes to output
|
||||
#else
|
||||
const int interval = 200; // ms, time to wait between changes to output
|
||||
bool sleepModeAlwaysSkip =
|
||||
false; // Web interface can override normal behaviour
|
||||
int interval = 200; // ms, time to wait between changes to output
|
||||
#endif
|
||||
bool sleepModeAlwaysSkip =
|
||||
false; // Flag set in web interface to override normal behaviour
|
||||
uint32_t loopMillis = 0; // Used for main loop to run the code every _interval_
|
||||
uint32_t runtimeMillis; // Used to calculate the total time since start/wakeup
|
||||
uint32_t stableGyroMillis; // Used to calculate the total time since last
|
||||
// stable gyro reading
|
||||
bool sleepModeActive = false;
|
||||
bool goToSleep = false;
|
||||
int loopCounter = 0;
|
||||
|
||||
enum RunMode { gravityMode = 0, configurationMode = 1, wifiSetupMode = 2 };
|
||||
|
||||
RunMode runMode = RunMode::gravityMode;
|
||||
|
||||
//
|
||||
// Check if we should be in sleep mode
|
||||
//
|
||||
void checkSleepMode(float angle, float volt) {
|
||||
#if defined(SKIP_SLEEPMODE)
|
||||
sleepModeActive = false;
|
||||
runMode = RunMode::configurationMode;
|
||||
Log.verbose(F("MAIN: Skipping sleep mode (SKIP_SLEEPMODE is defined)." CR));
|
||||
return;
|
||||
#endif
|
||||
|
||||
const RawGyroData &g = myConfig.getGyroCalibration();
|
||||
|
||||
// Will not enter sleep mode if: no calibration data
|
||||
if (g.ax == 0 && g.ay == 0 && g.az == 0 && g.gx == 0 && g.gy == 0 &&
|
||||
g.gz == 0) {
|
||||
if (!g.ax && !g.ay && !g.az && !g.gx && !g.gy && !g.gz) {
|
||||
// Will not enter sleep mode if: no calibration data
|
||||
Log.notice(
|
||||
F("MAIN: Missing calibration data, so forcing webserver to be "
|
||||
"active." CR));
|
||||
sleepModeAlwaysSkip = true;
|
||||
}
|
||||
|
||||
if (sleepModeAlwaysSkip) {
|
||||
runMode = RunMode::configurationMode;
|
||||
} else if (sleepModeAlwaysSkip) {
|
||||
// Check if the flag from the UI has been set, the we force configuration
|
||||
// mode.
|
||||
Log.notice(F("MAIN: Sleep mode disabled from web interface." CR));
|
||||
sleepModeActive = false;
|
||||
return;
|
||||
runMode = RunMode::configurationMode;
|
||||
} else if ((volt < 4.15 && (angle > 85 && angle < 95)) || (volt > 4.15)) {
|
||||
runMode = RunMode::configurationMode;
|
||||
} else {
|
||||
runMode = RunMode::gravityMode;
|
||||
}
|
||||
|
||||
// Will not enter sleep mode if: charger is connected
|
||||
sleepModeActive = (volt < 4.15 && (angle > 85 && angle < 95)) || (volt > 4.15)
|
||||
? false
|
||||
: true;
|
||||
|
||||
// sleep mode active when flat
|
||||
Log.notice(F("MAIN: Deep sleep mode %s (angle=%F volt=%F)." CR),
|
||||
sleepModeActive ? "true" : "false", angle, volt);
|
||||
switch (runMode) {
|
||||
case RunMode::configurationMode:
|
||||
Log.notice(F("MAIN: run mode CONFIG (angle=%F volt=%F)." CR), angle,
|
||||
volt);
|
||||
break;
|
||||
case RunMode::wifiSetupMode:
|
||||
break;
|
||||
case RunMode::gravityMode:
|
||||
Log.notice(F("MAIN: run mode GRAVITY (angle=%F volt=%F)." CR), angle,
|
||||
volt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@ -101,11 +100,9 @@ void setup() {
|
||||
LOG_PERF_START("main-setup");
|
||||
runtimeMillis = millis();
|
||||
|
||||
drd = new DoubleResetDetector(DRD_TIMEOUT, DRD_ADDRESS);
|
||||
bool dt = drd->detectDoubleReset();
|
||||
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
||||
delay(3000); // Wait a few seconds when using debug version so that serial is
|
||||
// started.
|
||||
// Add a delay so that serial is started.
|
||||
// delay(3000);
|
||||
Log.verbose(F("Main: Reset reason %s." CR), ESP.getResetInfo().c_str());
|
||||
#endif
|
||||
// Main startup
|
||||
@ -120,187 +117,219 @@ void setup() {
|
||||
|
||||
// Setup watchdog
|
||||
ESP.wdtDisable();
|
||||
ESP.wdtEnable(interval * 2);
|
||||
ESP.wdtEnable(5000); // 5 seconds
|
||||
|
||||
if (dt) {
|
||||
Log.notice(F("Main: Detected doubletap on reset. Reset reason=%s" CR),
|
||||
ESP.getResetReason().c_str());
|
||||
// No stored config, move to portal
|
||||
if (!myWifi.hasConfig()) {
|
||||
Log.notice(
|
||||
F("Main: No wifi configuration detected, entering wifi setup." CR));
|
||||
runMode = RunMode::wifiSetupMode;
|
||||
}
|
||||
|
||||
#ifdef SKIP_SLEEPMODE
|
||||
// If we are running in debug more we skip this part. makes is hard to debug
|
||||
// in case of crash/watchdog reset
|
||||
dt = false;
|
||||
#endif
|
||||
// Double reset, go to portal.
|
||||
if (myWifi.isDoubleResetDetected()) {
|
||||
Log.notice(F("Main: Double reset detected, entering wifi setup." CR));
|
||||
runMode = RunMode::wifiSetupMode;
|
||||
}
|
||||
|
||||
LOG_PERF_START("main-wifi-connect");
|
||||
myWifi.connect(dt); // This will return false if unable to connect to wifi,
|
||||
// will be handled in loop()
|
||||
LOG_PERF_STOP("main-wifi-connect");
|
||||
// Do this setup for all modes exect wifi setup
|
||||
switch (runMode) {
|
||||
case RunMode::wifiSetupMode:
|
||||
myWifi.startPortal();
|
||||
break;
|
||||
|
||||
LOG_PERF_START("main-temp-setup");
|
||||
myTempSensor.setup();
|
||||
LOG_PERF_STOP("main-temp-setup");
|
||||
default:
|
||||
LOG_PERF_START("main-wifi-connect");
|
||||
myWifi.connect();
|
||||
LOG_PERF_STOP("main-wifi-connect");
|
||||
|
||||
if (!myGyro.setup()) // Takes less than 5ms, so skip this
|
||||
Log.error(F("Main: Failed to initialize the gyro." CR));
|
||||
LOG_PERF_START("main-temp-setup");
|
||||
myTempSensor.setup();
|
||||
LOG_PERF_STOP("main-temp-setup");
|
||||
|
||||
LOG_PERF_START("main-gyro-read");
|
||||
myGyro.read();
|
||||
LOG_PERF_STOP("main-gyro-read");
|
||||
if (!myGyro.setup()) {
|
||||
Log.error(F("Main: Failed to initialize the gyro." CR));
|
||||
} else {
|
||||
LOG_PERF_START("main-gyro-read");
|
||||
myGyro.read();
|
||||
LOG_PERF_STOP("main-gyro-read");
|
||||
}
|
||||
|
||||
myBatteryVoltage
|
||||
.read(); // Takes less than 1ms, so skip this measuring time on this
|
||||
checkSleepMode(myGyro.getAngle(), myBatteryVoltage.getVoltage());
|
||||
myBatteryVoltage.read();
|
||||
checkSleepMode(myGyro.getAngle(), myBatteryVoltage.getVoltage());
|
||||
break;
|
||||
}
|
||||
|
||||
if (myWifi.isConnected()) {
|
||||
// Do this setup for configuration mode
|
||||
switch (runMode) {
|
||||
case RunMode::configurationMode:
|
||||
if (myWifi.isConnected()) {
|
||||
#if defined(ACTIVATE_OTA)
|
||||
LOG_PERF_START("main-wifi-ota");
|
||||
if (!sleepModeActive && myWifi.checkFirmwareVersion()) {
|
||||
myWifi.updateFirmware();
|
||||
}
|
||||
LOG_PERF_STOP("main-wifi-ota");
|
||||
LOG_PERF_START("main-wifi-ota");
|
||||
if (myWifi.checkFirmwareVersion()) myWifi.updateFirmware();
|
||||
LOG_PERF_STOP("main-wifi-ota");
|
||||
#endif
|
||||
if (!sleepModeActive) {
|
||||
myWebServer
|
||||
.setupWebServer(); // Takes less than 4ms, so skip this measurement
|
||||
}
|
||||
myWebServer
|
||||
.setupWebServer(); // Takes less than 4ms, so skip this measurement
|
||||
}
|
||||
|
||||
interval = 1000; // Change interval from 200ms to 1s
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if we need SSL for any of the push targets
|
||||
if (myConfig.isHttpSecure() || myConfig.isHttpSecure2() || myConfig.isMqttSecure()) {
|
||||
LOG_PERF_START("main-cert-store");
|
||||
myWifi.initCertstore();
|
||||
LOG_PERF_STOP("main-cert-store");
|
||||
LOG_PERF_START("main-cert-ntp");
|
||||
myWifi.initNTP();
|
||||
LOG_PERF_STOP("main-cert-ntp");
|
||||
}
|
||||
|
||||
LOG_PERF_STOP("main-setup");
|
||||
Log.notice(F("Main: Setup completed." CR));
|
||||
stableGyroMillis =
|
||||
millis(); // Put it here so we dont include time for wifi connection
|
||||
stableGyroMillis = millis(); // Dont include time for wifi connection
|
||||
}
|
||||
|
||||
//
|
||||
// Main loops
|
||||
// Main loop that does gravity readings and push data to targets
|
||||
//
|
||||
void loop() {
|
||||
drd->loop();
|
||||
|
||||
if (sleepModeActive || abs((int32_t)(millis() - loopMillis)) > interval) {
|
||||
float angle = 0;
|
||||
float volt = myBatteryVoltage.getVoltage();
|
||||
loopCounter++;
|
||||
// Return true if gravity reading was successful
|
||||
//
|
||||
bool loopReadGravity() {
|
||||
float angle = 0;
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
||||
Log.verbose(F("Main: Entering main loop." CR));
|
||||
Log.verbose(F("Main: Entering main loopGravity." CR));
|
||||
#endif
|
||||
|
||||
// Process the sensor values and push data to targets.
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// If we dont get any readings we just skip this and try again the next
|
||||
// interval.
|
||||
//
|
||||
if (myGyro.hasValue()) {
|
||||
angle = myGyro.getAngle(); // Gyro angle
|
||||
// Process the sensor values and push data to targets.
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// If we dont get any readings we just skip this and try again the next
|
||||
// interval.
|
||||
//
|
||||
if (myGyro.hasValue()) {
|
||||
angle = myGyro.getAngle(); // Gyro angle
|
||||
stableGyroMillis = millis(); // Reset timer
|
||||
|
||||
stableGyroMillis = millis(); // Reset timer
|
||||
LOG_PERF_START("loop-temp-read");
|
||||
float temp = myTempSensor.getTempC(myConfig.isGyroTemp());
|
||||
LOG_PERF_STOP("loop-temp-read");
|
||||
|
||||
LOG_PERF_START("loop-temp-read");
|
||||
float temp = myTempSensor.getTempC();
|
||||
LOG_PERF_STOP("loop-temp-read");
|
||||
|
||||
float gravity = calculateGravity(
|
||||
angle, temp); // Takes less than 2ms , so skip this measurment
|
||||
float corrGravity = gravityTemperatureCorrection(
|
||||
gravity, temp, myConfig.getTempFormat()); // Takes less than 2ms , so
|
||||
// skip this measurment
|
||||
float gravity = calculateGravity(angle, temp);
|
||||
float corrGravity =
|
||||
gravityTemperatureCorrection(gravity, temp, myConfig.getTempFormat());
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
||||
Log.verbose(F("Main: Sensor values gyro angle=%F, temp=%F, gravity=%F, "
|
||||
"corr=%F." CR),
|
||||
angle, temp, gravity, corrGravity);
|
||||
Log.verbose(F("Main: Sensor values gyro angle=%F, temp=%F, gravity=%F, "
|
||||
"corr=%F." CR),
|
||||
angle, temp, gravity, corrGravity);
|
||||
#endif
|
||||
|
||||
// Limit the printout when sleep mode is not active.
|
||||
if (loopCounter % 10 == 0 || sleepModeActive) {
|
||||
Log.notice(F("Main: angle=%F, temp=%F, gravity=%F, corrGravity=%F, "
|
||||
"batt=%F." CR),
|
||||
angle, temp, gravity, corrGravity, volt);
|
||||
}
|
||||
|
||||
LOG_PERF_START("loop-push");
|
||||
myPushTarget.send(
|
||||
angle, gravity, corrGravity, temp, (millis() - runtimeMillis) / 1000,
|
||||
sleepModeActive); // Force the transmission if we are going to sleep
|
||||
LOG_PERF_STOP("loop-push");
|
||||
|
||||
// If we have completed the update lets go to sleep
|
||||
if (sleepModeActive) goToSleep = true;
|
||||
} else {
|
||||
Log.error(F("Main: No gyro value." CR));
|
||||
}
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
||||
Log.verbose(F("Main: Sleep mode not active." CR));
|
||||
#endif
|
||||
|
||||
int sleepInterval = myConfig.getSleepInterval();
|
||||
|
||||
// If we didnt get a wifi connection, we enter sleep for a short time to
|
||||
// conserve battery.
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
//
|
||||
if (!myWifi.isConnected()) { // no connection to wifi
|
||||
Log.notice(
|
||||
F("MAIN: No connection to wifi established, sleeping for 60s." CR));
|
||||
sleepInterval = 60; // 60s
|
||||
goToSleep = true;
|
||||
}
|
||||
|
||||
// If the sensor is moving and we are not getting a clear reading, we enter
|
||||
// sleep for a short time to conserve battery.
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
//
|
||||
if (sleepModeActive && ((millis() - stableGyroMillis) >
|
||||
10000L)) { // 10s since last stable gyro reading
|
||||
Log.notice(
|
||||
F("MAIN: Unable to get a stable reading for 10s, sleeping for "
|
||||
"60s." CR));
|
||||
sleepInterval = 60; // 60s
|
||||
goToSleep = true;
|
||||
}
|
||||
|
||||
// Enter sleep mode if the conditions are right
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
//
|
||||
if (goToSleep && !sleepModeAlwaysSkip) {
|
||||
Log.notice(F("MAIN: Entering deep sleep for %d s, run time %l s, "
|
||||
"battery=%F V." CR),
|
||||
sleepInterval, (millis() - runtimeMillis) / 1000, volt);
|
||||
LittleFS.end();
|
||||
myGyro.enterSleep();
|
||||
drd->stop();
|
||||
LOG_PERF_STOP("run-time");
|
||||
LOG_PERF_PUSH();
|
||||
delay(100);
|
||||
deepSleep(sleepInterval);
|
||||
}
|
||||
|
||||
// If we are running in normal mode we just continue
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Do these checks if we are running in normal mode (not sleep mode)
|
||||
//
|
||||
checkSleepMode(angle, volt);
|
||||
|
||||
LOG_PERF_START("loop-gyro-read");
|
||||
myGyro.read();
|
||||
LOG_PERF_STOP("loop-gyro-read");
|
||||
|
||||
myBatteryVoltage.read(); // Takes less than 2ms , so skip this measurment
|
||||
LOG_PERF_START("loop-push");
|
||||
// Force the transmission if we are going to sleep
|
||||
myPushTarget.send(angle, gravity, corrGravity, temp,
|
||||
(millis() - runtimeMillis) / 1000,
|
||||
runMode == RunMode::gravityMode ? true : false);
|
||||
LOG_PERF_STOP("loop-push");
|
||||
return true;
|
||||
} else {
|
||||
Log.error(F("Main: No gyro value." CR));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Wrapper for loopGravity that only calls every 200ms so that we dont overload
|
||||
// this.
|
||||
//
|
||||
void loopGravityOnInterval() {
|
||||
if (abs((int32_t)(millis() - loopMillis)) > interval) {
|
||||
loopReadGravity();
|
||||
loopMillis = millis();
|
||||
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
||||
Log.verbose(F("Main: Heap %d kb FreeSketch %d kb HeapFrag %d %%." CR),
|
||||
ESP.getFreeHeap() / 1024, ESP.getFreeSketchSpace() / 1024,
|
||||
ESP.getHeapFragmentation());
|
||||
#endif
|
||||
LOG_PERF_START("loop-gyro-read");
|
||||
myGyro.read();
|
||||
LOG_PERF_STOP("loop-gyro-read");
|
||||
myBatteryVoltage.read();
|
||||
checkSleepMode(myGyro.getAngle(), myBatteryVoltage.getVoltage());
|
||||
LOG_PERF_PUSH();
|
||||
}
|
||||
}
|
||||
|
||||
myWebServer.loop();
|
||||
//
|
||||
// Main loop that determines if device should go to sleep
|
||||
//
|
||||
void goToSleep(int sleepInterval) {
|
||||
float volt = myBatteryVoltage.getVoltage();
|
||||
float runtime = (millis() - runtimeMillis);
|
||||
|
||||
Log.notice(F("MAIN: Entering deep sleep for %ds, run time %Fs, "
|
||||
"battery=%FV." CR),
|
||||
sleepInterval, reduceFloatPrecision(runtime / 1000, 2), volt);
|
||||
LittleFS.end();
|
||||
myGyro.enterSleep();
|
||||
LOG_PERF_STOP("run-time");
|
||||
LOG_PERF_PUSH();
|
||||
delay(100);
|
||||
deepSleep(sleepInterval);
|
||||
}
|
||||
|
||||
//
|
||||
// Main loops
|
||||
//
|
||||
void loop() {
|
||||
switch (runMode) {
|
||||
case RunMode::configurationMode:
|
||||
myWebServer.loop();
|
||||
myWifi.loop();
|
||||
loopGravityOnInterval();
|
||||
break;
|
||||
|
||||
case RunMode::gravityMode:
|
||||
// If we didnt get a wifi connection, we enter sleep for a short time to
|
||||
// conserve battery.
|
||||
if (!myWifi.isConnected()) { // no connection to wifi
|
||||
Log.notice(
|
||||
F("MAIN: No connection to wifi established, sleeping for 60s." CR));
|
||||
myWifi.stopDoubleReset();
|
||||
goToSleep(60);
|
||||
}
|
||||
|
||||
if (loopReadGravity()) {
|
||||
myWifi.stopDoubleReset();
|
||||
goToSleep(myConfig.getSleepInterval());
|
||||
}
|
||||
|
||||
// If the sensor is moving and we are not getting a clear reading, we
|
||||
// enter sleep for a short time to conserve battery.
|
||||
if (((millis() - stableGyroMillis) >
|
||||
10000L)) { // 10s since last stable gyro reading
|
||||
Log.notice(
|
||||
F("MAIN: Unable to get a stable reading for 10s, sleeping for "
|
||||
"60s." CR));
|
||||
myWifi.stopDoubleReset();
|
||||
goToSleep(60);
|
||||
}
|
||||
|
||||
LOG_PERF_START("loop-gyro-read");
|
||||
myGyro.read();
|
||||
LOG_PERF_STOP("loop-gyro-read");
|
||||
myWifi.loop();
|
||||
break;
|
||||
|
||||
case RunMode::wifiSetupMode:
|
||||
myWifi.loop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
@ -21,9 +21,11 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include <pushtarget.hpp>
|
||||
#include <MQTT.h>
|
||||
#include <config.hpp>
|
||||
#include <gyro.hpp>
|
||||
#include <pushtarget.hpp>
|
||||
#include <wifi.hpp>
|
||||
|
||||
PushTarget myPushTarget;
|
||||
|
||||
@ -74,6 +76,12 @@ void PushTarget::send(float angle, float gravity, float corrGravity, float temp,
|
||||
sendInfluxDb2(angle, gravity, corrGravity, temp, runTime);
|
||||
LOG_PERF_STOP("push-influxdb2");
|
||||
}
|
||||
|
||||
if (myConfig.isMqttActive()) {
|
||||
LOG_PERF_START("push-mqtt");
|
||||
sendMqtt(angle, gravity, corrGravity, temp, runTime);
|
||||
LOG_PERF_STOP("push-mqtt");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@ -98,16 +106,14 @@ void PushTarget::sendInfluxDb2(float angle, float gravity, float corrGravity,
|
||||
|
||||
// Create body for influxdb2
|
||||
char buf[1024];
|
||||
snprintf(
|
||||
&buf[0], sizeof(buf),
|
||||
"measurement,host=%s,device=%s,temp-format=%c,gravity-format=%s "
|
||||
"gravity=%.4f,corr-gravity=%.4f,angle=%.2f,temp=%.2f,battery=%.2f,rssi=%"
|
||||
"d,temp2=%.2f\n",
|
||||
// TODO: Add support for plato format
|
||||
myConfig.getMDNS(), myConfig.getID(), myConfig.getTempFormat(), "SG",
|
||||
myConfig.isGravityTempAdj() ? corrGravity : gravity, corrGravity, angle,
|
||||
temp, myBatteryVoltage.getVoltage(), WiFi.RSSI(),
|
||||
myGyro.getSensorTempC()); // For comparing gyro tempsensor vs DSB1820
|
||||
snprintf(&buf[0], sizeof(buf),
|
||||
"measurement,host=%s,device=%s,temp-format=%c,gravity-format=%s "
|
||||
"gravity=%.4f,corr-gravity=%.4f,angle=%.2f,temp=%.2f,battery=%.2f,"
|
||||
"rssi=%d\n",
|
||||
// TODO: Add support for plato format
|
||||
myConfig.getMDNS(), myConfig.getID(), myConfig.getTempFormat(), "SG",
|
||||
myConfig.isGravityTempAdj() ? corrGravity : gravity, corrGravity,
|
||||
angle, temp, myBatteryVoltage.getVoltage(), WiFi.RSSI());
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||
@ -199,16 +205,9 @@ void PushTarget::sendBrewfather(float angle, float gravity, float corrGravity,
|
||||
//
|
||||
// Send data to http target
|
||||
//
|
||||
void PushTarget::sendHttp(String serverPath, float angle, float gravity,
|
||||
float corrGravity, float temp, float runTime) {
|
||||
#if !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.notice(
|
||||
F("PUSH: Sending values to http angle=%F, gravity=%F, temp=%F." CR),
|
||||
angle, gravity, temp);
|
||||
#endif
|
||||
|
||||
DynamicJsonDocument doc(256);
|
||||
|
||||
void PushTarget::createIspindleFormat(DynamicJsonDocument &doc, float angle,
|
||||
float gravity, float corrGravity,
|
||||
float temp, float runTime) {
|
||||
// Use iSpindle format for compatibility
|
||||
doc["name"] = myConfig.getMDNS();
|
||||
doc["ID"] = myConfig.getID();
|
||||
@ -227,12 +226,41 @@ void PushTarget::sendHttp(String serverPath, float angle, float gravity,
|
||||
// Some additional information
|
||||
doc["gravity-units"] = "SG";
|
||||
doc["run-time"] = reduceFloatPrecision(runTime, 2);
|
||||
}
|
||||
|
||||
//
|
||||
// Send data to http target
|
||||
//
|
||||
void PushTarget::sendHttp(String serverPath, float angle, float gravity,
|
||||
float corrGravity, float temp, float runTime) {
|
||||
#if !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.notice(
|
||||
F("PUSH: Sending values to http angle=%F, gravity=%F, temp=%F." CR),
|
||||
angle, gravity, temp);
|
||||
#endif
|
||||
|
||||
DynamicJsonDocument doc(256);
|
||||
createIspindleFormat(doc, angle, gravity, corrGravity, temp, runTime);
|
||||
|
||||
WiFiClient client;
|
||||
WiFiClientSecure clientSecure;
|
||||
HTTPClient http;
|
||||
|
||||
// Your Domain name with URL path or IP address with path
|
||||
http.begin(client, serverPath);
|
||||
if (serverPath.startsWith("https://")) {
|
||||
/*if (myWifi.getCertCount() > 0) {
|
||||
// Allow secure channel, with CA validation
|
||||
clientSecure.setCertStore(myWifi.getCertStore());
|
||||
Log.notice(F("PUSH: SSL enabled using certificate store." CR));
|
||||
} else*/ {
|
||||
// Allow secure channel, but without certificate validation
|
||||
clientSecure.setInsecure();
|
||||
Log.notice(F("PUSH: SSL enabled without validation." CR));
|
||||
}
|
||||
http.begin(clientSecure, serverPath);
|
||||
} else {
|
||||
http.begin(client, serverPath);
|
||||
}
|
||||
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||
@ -254,4 +282,61 @@ void PushTarget::sendHttp(String serverPath, float angle, float gravity,
|
||||
http.end();
|
||||
}
|
||||
|
||||
//
|
||||
// Send data to http target
|
||||
//
|
||||
void PushTarget::sendMqtt(float angle, float gravity, float corrGravity,
|
||||
float temp, float runTime) {
|
||||
#if !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.notice(
|
||||
F("PUSH: Sending values to mqtt angle=%F, gravity=%F, temp=%F." CR),
|
||||
angle, gravity, temp);
|
||||
#endif
|
||||
|
||||
DynamicJsonDocument doc(256);
|
||||
createIspindleFormat(doc, angle, gravity, corrGravity, temp, runTime);
|
||||
|
||||
WiFiClient client;
|
||||
WiFiClientSecure clientSecure;
|
||||
MQTTClient mqtt(512); // Maximum message size
|
||||
|
||||
if (myConfig.isMqttSecure()) {
|
||||
if (myWifi.getCertCount() > 0) {
|
||||
// Allow secure channel, with CA validation
|
||||
clientSecure.setCertStore(myWifi.getCertStore());
|
||||
Log.notice(F("PUSH: SSL enabled using certificate store." CR));
|
||||
} else {
|
||||
// Allow secure channel, but without certificate validation
|
||||
clientSecure.setInsecure();
|
||||
Log.notice(F("PUSH: SSL enabled without validation." CR));
|
||||
}
|
||||
String url = myConfig.getMqttUrl();
|
||||
url.replace(":8883", "");
|
||||
mqtt.begin(url.c_str(), 8883, clientSecure);
|
||||
} else {
|
||||
mqtt.begin(myConfig.getMqttUrl(), client);
|
||||
}
|
||||
|
||||
mqtt.connect(myConfig.getMDNS(), myConfig.getMqttUser(),
|
||||
myConfig.getMqttPass());
|
||||
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.verbose(F("PUSH: url %s." CR), myConfig.getMqttUrl());
|
||||
Log.verbose(F("PUSH: json %s." CR), json.c_str());
|
||||
#endif
|
||||
|
||||
// Send MQQT message
|
||||
mqtt.setTimeout(10); // 10 seconds timeout
|
||||
if (mqtt.publish(myConfig.getMqttTopic(), json)) {
|
||||
Log.notice(F("PUSH: MQTT publish successful" CR));
|
||||
} else {
|
||||
Log.error(F("PUSH: MQTT publish failed err=%d, ret=%d" CR),
|
||||
mqtt.lastError(), mqtt.returnCode());
|
||||
}
|
||||
|
||||
mqtt.disconnect();
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
@ -43,6 +43,11 @@ class PushTarget {
|
||||
float corrGravity, float temp, float runTime);
|
||||
void sendInfluxDb2(float angle, float gravity, float corrGravity, float temp,
|
||||
float runTime);
|
||||
void sendMqtt(float angle, float gravity, float corrGravity, float temp,
|
||||
float runTime);
|
||||
void createIspindleFormat(DynamicJsonDocument &doc, float angle,
|
||||
float gravity, float corrGravity, float temp,
|
||||
float runTime);
|
||||
|
||||
public:
|
||||
PushTarget() { ms = millis(); }
|
||||
|
@ -32,12 +32,9 @@ INCBIN(DeviceHtm, "data/device.min.htm");
|
||||
INCBIN(ConfigHtm, "data/config.min.htm");
|
||||
INCBIN(CalibrationHtm, "data/calibration.min.htm");
|
||||
INCBIN(AboutHtm, "data/about.min.htm");
|
||||
|
||||
#else
|
||||
|
||||
// Minium web interface for uploading htm files
|
||||
INCBIN(UploadHtm, "data/upload.min.htm");
|
||||
|
||||
#endif
|
||||
|
||||
// Minium web interface for uploading htm files, also used to upload certificate store.
|
||||
INCBIN(UploadHtm, "data/upload.min.htm");
|
||||
|
||||
// EOF
|
||||
|
@ -22,8 +22,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include <DallasTemperature.h>
|
||||
#include <Wire.h>
|
||||
#include <OneWire.h>
|
||||
#include <Wire.h>
|
||||
|
||||
#include <config.hpp>
|
||||
#include <gyro.hpp>
|
||||
@ -35,16 +35,14 @@ SOFTWARE.
|
||||
//
|
||||
float convertCtoF(float t) { return (t * 1.8) + 32.0; }
|
||||
|
||||
#if !defined(USE_GYRO_TEMP)
|
||||
OneWire myOneWire(D6);
|
||||
DallasTemperature mySensors(&myOneWire);
|
||||
#define TEMPERATURE_PRECISION 9
|
||||
#endif
|
||||
|
||||
TempSensor myTempSensor;
|
||||
|
||||
//
|
||||
// Setup temp sensors
|
||||
// Setup DS18B20 temp sensor. Doing setup is not that time consuming.
|
||||
//
|
||||
void TempSensor::setup() {
|
||||
#if defined(SIMULATE_TEMP)
|
||||
@ -52,9 +50,6 @@ void TempSensor::setup() {
|
||||
return;
|
||||
#endif
|
||||
|
||||
#if defined(USE_GYRO_TEMP)
|
||||
Log.notice(F("TSEN: Using temperature from gyro." CR));
|
||||
#else
|
||||
#if LOG_LEVEL == 6 && !defined(TSEN_DISABLE_LOGGING)
|
||||
Log.verbose(F("TSEN: Looking for temp sensors." CR));
|
||||
#endif
|
||||
@ -67,7 +62,6 @@ void TempSensor::setup() {
|
||||
#endif
|
||||
mySensors.setResolution(TEMPERATURE_PRECISION);
|
||||
}
|
||||
#endif
|
||||
|
||||
float t = myConfig.getTempSensorAdj();
|
||||
|
||||
@ -89,21 +83,22 @@ void TempSensor::setup() {
|
||||
//
|
||||
// Retrieving value from sensor, value is in Celcius
|
||||
//
|
||||
float TempSensor::getValue() {
|
||||
float TempSensor::getValue(bool useGyro) {
|
||||
#if defined(SIMULATE_TEMP)
|
||||
return 21;
|
||||
#endif
|
||||
|
||||
#if defined(USE_GYRO_TEMP)
|
||||
// When using the gyro temperature only the first read value will be accurate
|
||||
// so we will use this for processing.
|
||||
float c = myGyro.getInitialSensorTempC();
|
||||
hasSensor = true;
|
||||
return c;
|
||||
if (useGyro) {
|
||||
// When using the gyro temperature only the first read value will be
|
||||
// accurate so we will use this for processing.
|
||||
float c = myGyro.getInitialSensorTempC();
|
||||
#if LOG_LEVEL == 6 && !defined(TSEN_DISABLE_LOGGING)
|
||||
Log.verbose(F("TSEN: Reciving temp value for gyro sensor %F C." CR), c);
|
||||
Log.verbose(F("TSEN: Reciving temp value for gyro sensor %F C." CR), c);
|
||||
#endif
|
||||
#else
|
||||
hasSensor = true;
|
||||
return c;
|
||||
}
|
||||
|
||||
// If we dont have sensors just return 0
|
||||
if (!mySensors.getDS18Count()) {
|
||||
Log.error(F("TSEN: No temperature sensors found. Skipping read." CR));
|
||||
@ -119,12 +114,11 @@ float TempSensor::getValue() {
|
||||
c = mySensors.getTempCByIndex(0);
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(TSEN_DISABLE_LOGGING)
|
||||
Log.verbose(F("TSEN: Reciving temp value for sensor %F C." CR), c);
|
||||
Log.verbose(F("TSEN: Reciving temp value for DS18B20 sensor %F C." CR), c);
|
||||
#endif
|
||||
hasSensor = true;
|
||||
}
|
||||
return c;
|
||||
#endif
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
@ -33,13 +33,17 @@ class TempSensor {
|
||||
bool hasSensor = false;
|
||||
float tempSensorAdjF = 0;
|
||||
float tempSensorAdjC = 0;
|
||||
float getValue();
|
||||
float getValue(bool useGyro);
|
||||
|
||||
public:
|
||||
void setup();
|
||||
bool isSensorAttached() { return hasSensor; }
|
||||
float getTempC() { return getValue() + tempSensorAdjC; }
|
||||
float getTempF() { return convertCtoF(getValue()) + tempSensorAdjF; }
|
||||
float getTempC(bool useGyro = false) {
|
||||
return getValue(useGyro) + tempSensorAdjC;
|
||||
}
|
||||
float getTempF(bool useGyro = false) {
|
||||
return convertCtoF(getValue(useGyro)) + tempSensorAdjF;
|
||||
}
|
||||
};
|
||||
|
||||
// Global instance created
|
||||
|
@ -41,7 +41,7 @@ extern bool sleepModeAlwaysSkip;
|
||||
void WebServer::webHandleDevice() {
|
||||
LOG_PERF_START("webserver-api-device");
|
||||
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
||||
Log.verbose(F("WEB : webServer callback for /api/config." CR));
|
||||
Log.verbose(F("WEB : webServer callback for /api/device." CR));
|
||||
#endif
|
||||
|
||||
DynamicJsonDocument doc(100);
|
||||
@ -49,6 +49,7 @@ void WebServer::webHandleDevice() {
|
||||
doc[CFG_PARAM_APP_NAME] = CFG_APPNAME;
|
||||
doc[CFG_PARAM_APP_VER] = CFG_APPVER;
|
||||
doc[CFG_PARAM_MDNS] = myConfig.getMDNS();
|
||||
doc[CFG_PARAM_CERTS] = checkHtmlFile(CA_CERTS);
|
||||
#if LOG_LEVEL == 6
|
||||
serializeJson(doc, Serial);
|
||||
Serial.print(CR);
|
||||
@ -69,8 +70,10 @@ void WebServer::webHandleConfig() {
|
||||
DynamicJsonDocument doc(CFG_JSON_BUFSIZE);
|
||||
myConfig.createJson(doc);
|
||||
|
||||
doc[CFG_PARAM_PASS] = ""; // dont show the wifi password
|
||||
|
||||
double angle = myGyro.getAngle();
|
||||
double temp = myTempSensor.getTempC();
|
||||
double temp = myTempSensor.getTempC(myConfig.isGyroTemp());
|
||||
double gravity = calculateGravity(angle, temp);
|
||||
|
||||
doc[CFG_PARAM_ANGLE] = reduceFloatPrecision(angle);
|
||||
@ -106,6 +109,7 @@ void WebServer::webHandleUpload() {
|
||||
doc["config"] = myWebServer.checkHtmlFile(WebServer::HTML_CONFIG);
|
||||
doc["calibration"] = myWebServer.checkHtmlFile(WebServer::HTML_CALIBRATION);
|
||||
doc["about"] = myWebServer.checkHtmlFile(WebServer::HTML_ABOUT);
|
||||
doc["certs"] = myWebServer.checkHtmlFile(WebServer::CA_CERTS);
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
||||
serializeJson(doc, Serial);
|
||||
@ -132,14 +136,15 @@ void WebServer::webHandleUploadFile() {
|
||||
f.equalsIgnoreCase("device.min.htm") ||
|
||||
f.equalsIgnoreCase("calibration.min.htm") ||
|
||||
f.equalsIgnoreCase("config.min.htm") ||
|
||||
f.equalsIgnoreCase("about.min.htm")) {
|
||||
f.equalsIgnoreCase("about.min.htm") ||
|
||||
f.equalsIgnoreCase("certs.ar")) {
|
||||
validFilename = true;
|
||||
}
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
||||
Log.debug(F("WEB : webServer callback for /api/upload, receiving file %s, "
|
||||
"valid=%s." CR),
|
||||
f.c_str(), validFilename ? "yes" : "no");
|
||||
Log.verbose(F("WEB : webServer callback for /api/upload, receiving file %s, "
|
||||
"valid=%s." CR),
|
||||
f.c_str(), validFilename ? "yes" : "no");
|
||||
#endif
|
||||
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
@ -213,7 +218,7 @@ void WebServer::webHandleStatus() {
|
||||
DynamicJsonDocument doc(256);
|
||||
|
||||
double angle = myGyro.getAngle();
|
||||
double temp = myTempSensor.getTempC();
|
||||
double temp = myTempSensor.getTempC(myConfig.isGyroTemp());
|
||||
double gravity = calculateGravity(angle, temp);
|
||||
|
||||
doc[CFG_PARAM_ID] = myConfig.getID();
|
||||
@ -225,7 +230,8 @@ void WebServer::webHandleStatus() {
|
||||
else
|
||||
doc[CFG_PARAM_GRAVITY] = reduceFloatPrecision(gravity, 4);
|
||||
doc[CFG_PARAM_TEMP_C] = reduceFloatPrecision(temp, 1);
|
||||
doc[CFG_PARAM_TEMP_F] = reduceFloatPrecision(myTempSensor.getTempF(), 1);
|
||||
doc[CFG_PARAM_TEMP_F] =
|
||||
reduceFloatPrecision(myTempSensor.getTempF(myConfig.isGyroTemp()), 1);
|
||||
doc[CFG_PARAM_BATTERY] = reduceFloatPrecision(myBatteryVoltage.getVoltage());
|
||||
doc[CFG_PARAM_TEMPFORMAT] = String(myConfig.getTempFormat());
|
||||
doc[CFG_PARAM_SLEEP_MODE] = sleepModeAlwaysSkip;
|
||||
@ -356,6 +362,10 @@ void WebServer::webHandleConfigPush() {
|
||||
server->arg(CFG_PARAM_PUSH_INFLUXDB2_BUCKET).c_str());
|
||||
myConfig.setInfluxDb2PushToken(
|
||||
server->arg(CFG_PARAM_PUSH_INFLUXDB2_AUTH).c_str());
|
||||
myConfig.setMqttUrl(server->arg(CFG_PARAM_PUSH_MQTT).c_str());
|
||||
myConfig.setMqttTopic(server->arg(CFG_PARAM_PUSH_MQTT_TOPIC).c_str());
|
||||
myConfig.setMqttUser(server->arg(CFG_PARAM_PUSH_MQTT_USER).c_str());
|
||||
myConfig.setMqttPass(server->arg(CFG_PARAM_PUSH_MQTT_PASS).c_str());
|
||||
myConfig.saveFile();
|
||||
server->sendHeader("Location", "/config.htm#collapseTwo", true);
|
||||
server->send(302, "text/plain", "Push config updated");
|
||||
@ -411,15 +421,18 @@ void WebServer::webHandleConfigHardware() {
|
||||
}
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
||||
Log.verbose(F("WEB : vf=%s, tempadj=%s, ota=%s." CR),
|
||||
Log.verbose(F("WEB : vf=%s, tempadj=%s, ota=%s gyrotemp=%s." CR),
|
||||
server->arg(CFG_PARAM_VOLTAGEFACTOR).c_str(),
|
||||
server->arg(CFG_PARAM_TEMP_ADJ).c_str(),
|
||||
server->arg(CFG_PARAM_OTA).c_str());
|
||||
server->arg(CFG_PARAM_OTA).c_str(),
|
||||
server->arg(CFG_PARAM_GYRO_TEMP).c_str());
|
||||
#endif
|
||||
|
||||
myConfig.setVoltageFactor(server->arg(CFG_PARAM_VOLTAGEFACTOR).toFloat());
|
||||
myConfig.setTempSensorAdj(server->arg(CFG_PARAM_TEMP_ADJ).toFloat());
|
||||
myConfig.setOtaURL(server->arg(CFG_PARAM_OTA).c_str());
|
||||
myConfig.setGyroTemp(
|
||||
server->arg(CFG_PARAM_GYRO_TEMP).equalsIgnoreCase("on") ? true : false);
|
||||
myConfig.saveFile();
|
||||
server->sendHeader("Location", "/config.htm#collapseFour", true);
|
||||
server->send(302, "text/plain", "Hardware config updated");
|
||||
@ -559,6 +572,7 @@ void WebServer::webHandleFormulaWrite() {
|
||||
server->send(302, "text/plain", "Formula updated");
|
||||
LOG_PERF_STOP("webserver-api-formula-write");
|
||||
}
|
||||
|
||||
//
|
||||
// Helper function to check if files exist on file system.
|
||||
//
|
||||
@ -576,6 +590,8 @@ const char* WebServer::getHtmlFileName(HtmlFile item) {
|
||||
return "calibration.min.htm";
|
||||
case HTML_ABOUT:
|
||||
return "about.min.htm";
|
||||
case CA_CERTS:
|
||||
return "certs.ar";
|
||||
}
|
||||
|
||||
return "";
|
||||
@ -625,6 +641,7 @@ bool WebServer::setupWebServer() {
|
||||
server->on("/calibration.htm",
|
||||
std::bind(&WebServer::webReturnCalibrationHtm, this));
|
||||
server->on("/about.htm", std::bind(&WebServer::webReturnAboutHtm, this));
|
||||
server->on("/upload.htm", std::bind(&WebServer::webReturnUploadHtm, this));
|
||||
#else
|
||||
// Show files in the filessytem at startup
|
||||
|
||||
|
@ -37,9 +37,8 @@ INCBIN_EXTERN(DeviceHtm);
|
||||
INCBIN_EXTERN(ConfigHtm);
|
||||
INCBIN_EXTERN(CalibrationHtm);
|
||||
INCBIN_EXTERN(AboutHtm);
|
||||
#else
|
||||
INCBIN_EXTERN(UploadHtm);
|
||||
#endif
|
||||
INCBIN_EXTERN(UploadHtm);
|
||||
|
||||
// classes
|
||||
class WebServer {
|
||||
@ -86,12 +85,11 @@ class WebServer {
|
||||
void webReturnAboutHtm() {
|
||||
server->send_P(200, "text/html", (const char*)gAboutHtmData, gAboutHtmSize);
|
||||
}
|
||||
#else
|
||||
#endif
|
||||
void webReturnUploadHtm() {
|
||||
server->send_P(200, "text/html", (const char*)gUploadHtmData,
|
||||
gUploadHtmSize);
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
enum HtmlFile {
|
||||
@ -99,7 +97,8 @@ class WebServer {
|
||||
HTML_DEVICE = 1,
|
||||
HTML_CONFIG = 2,
|
||||
HTML_ABOUT = 3,
|
||||
HTML_CALIBRATION = 4
|
||||
HTML_CALIBRATION = 4,
|
||||
CA_CERTS = 5
|
||||
};
|
||||
|
||||
bool setupWebServer();
|
||||
|
234
src/wifi.cpp
@ -21,77 +21,168 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <ESP8266httpUpdate.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <LittleFS.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <incbin.h>
|
||||
|
||||
#include <ArduinoJson.hpp>
|
||||
#include <calc.hpp>
|
||||
#include <config.hpp>
|
||||
#include <gyro.hpp>
|
||||
#include <helper.hpp>
|
||||
#include <tempsensor.hpp>
|
||||
#include <wifi.hpp>
|
||||
#warning "Implement SSL for OTA"
|
||||
|
||||
Wifi myWifi;
|
||||
// Settings for DRD
|
||||
#define ESP_DRD_USE_LITTLEFS true
|
||||
#define ESP_DRD_USE_SPIFFS false
|
||||
#define ESP_DRD_USE_EEPROM false
|
||||
#include <ESP_DoubleResetDetector.h>
|
||||
#define DRD_TIMEOUT 3
|
||||
#define DRD_ADDRESS 0
|
||||
|
||||
// Settings for WIFI Manager
|
||||
#define USE_ESP_WIFIMANAGER_NTP false
|
||||
#define USE_CLOUDFLARE_NTP false
|
||||
#define USING_CORS_FEATURE false
|
||||
#define NUM_WIFI_CREDENTIALS 1
|
||||
#define USE_STATIC_IP_CONFIG_IN_CP false
|
||||
#include <ESP_WiFiManager.h>
|
||||
// Override the look and feel of the standard ui (hide secondary forms)
|
||||
const char WM_HTTP_FORM_START[] PROGMEM =
|
||||
"<form method='get' "
|
||||
"action='wifisave'><fieldset><div><label>SSID</label><input id='s' "
|
||||
"name='s' length=32 "
|
||||
"placeholder='SSID'><div></div></div><div><label>Password</label><input "
|
||||
"id='p' name='p' length=64 placeholder='password'><div></div></div><div "
|
||||
"hidden><label>SSID1</label><input id='s1' name='s1' length=32 "
|
||||
"placeholder='SSID1'><div></div></div><div "
|
||||
"hidden><label>Password</label><input id='p1' name='p1' length=64 "
|
||||
"placeholder='password1'><div></div></div></fieldset>";
|
||||
#include <ESP_WiFiManager-Impl.h>
|
||||
ESP_WiFiManager *myWifiManager;
|
||||
DoubleResetDetector *myDRD;
|
||||
|
||||
WifiConnection myWifi;
|
||||
const char *userSSID = USER_SSID;
|
||||
const char *userPWD = USER_SSID_PWD;
|
||||
|
||||
//
|
||||
// Connect to last known access point or create one if connection is not
|
||||
// working.
|
||||
//
|
||||
bool Wifi::connect(bool showPortal) {
|
||||
WiFi.persistent(true);
|
||||
WiFi.mode(WIFI_STA);
|
||||
const int PIN_LED = 2;
|
||||
|
||||
if (!strlen(myConfig.getWifiSSID())) {
|
||||
Log.info(
|
||||
F("WIFI: No SSID seams to be stored, forcing portal to start." CR));
|
||||
showPortal = true;
|
||||
//
|
||||
// Initialize the certificate store
|
||||
//
|
||||
void WifiConnection::initCertstore() {
|
||||
_certCount = _certStore.initCertStore(LittleFS, "/certs.idx", "/certs.ar");
|
||||
Log.notice(F("WIFI: Number of CA certs read: %d." CR), _certCount);
|
||||
}
|
||||
|
||||
// Set time via NTP, as required for x.509 validation
|
||||
void WifiConnection::initNTP() {
|
||||
configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov");
|
||||
|
||||
Log.notice(F("WIFI: Waiting for NTP time sync." CR));
|
||||
time_t now = time(nullptr);
|
||||
while (now < 8 * 3600 * 2) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
now = time(nullptr);
|
||||
}
|
||||
Serial.println();
|
||||
struct tm timeinfo;
|
||||
gmtime_r(&now, &timeinfo);
|
||||
Log.notice(F("WIFI: Current time %s." CR), asctime(&timeinfo));
|
||||
}
|
||||
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
WifiConnection::WifiConnection() {
|
||||
myDRD = new DoubleResetDetector(DRD_TIMEOUT, DRD_ADDRESS);
|
||||
}
|
||||
|
||||
//
|
||||
// Check if we have a valid wifi configuration
|
||||
//
|
||||
bool WifiConnection::hasConfig() {
|
||||
if (strlen(myConfig.getWifiSSID())) return true;
|
||||
if (strlen(userSSID)) return true;
|
||||
|
||||
// Check if there are stored WIFI Settings we can use.
|
||||
String ssid = WiFi.SSID();
|
||||
if (ssid.length()) {
|
||||
Log.notice(F("WIFI: Found credentials in EEPORM." CR));
|
||||
myConfig.setWifiSSID(WiFi.SSID());
|
||||
myConfig.setWifiPass(WiFi.psk());
|
||||
myConfig.saveFile();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Check if the wifi is connected
|
||||
//
|
||||
bool WifiConnection::isConnected() { return WiFi.status() == WL_CONNECTED; }
|
||||
|
||||
//
|
||||
// Get the IP adress
|
||||
//
|
||||
String WifiConnection::getIPAddress() { return WiFi.localIP().toString(); }
|
||||
|
||||
//
|
||||
// Additional method to detect double reset.
|
||||
//
|
||||
bool WifiConnection::isDoubleResetDetected() {
|
||||
return myDRD->detectDoubleReset();
|
||||
}
|
||||
|
||||
//
|
||||
// Stop double reset detection
|
||||
//
|
||||
void WifiConnection::stopDoubleReset() { myDRD->stop(); }
|
||||
|
||||
//
|
||||
// Start the wifi manager
|
||||
//
|
||||
void WifiConnection::startPortal() {
|
||||
Log.notice(F("WIFI: Starting Wifi config portal." CR));
|
||||
|
||||
pinMode(PIN_LED, OUTPUT);
|
||||
digitalWrite(PIN_LED, LOW);
|
||||
|
||||
myWifiManager = new ESP_WiFiManager(WIFI_MDNS);
|
||||
myWifiManager->setMinimumSignalQuality(-1);
|
||||
myWifiManager->setConfigPortalChannel(0);
|
||||
myWifiManager->setConfigPortalTimeout(120);
|
||||
|
||||
if (myWifiManager->startConfigPortal(WIFI_DEFAULT_SSID, WIFI_DEFAULT_PWD)) {
|
||||
Log.notice(F("WIFI: Exited portal, connected to wifi. Rebooting..." CR));
|
||||
myConfig.setWifiSSID(myWifiManager->getSSID());
|
||||
myConfig.setWifiPass(myWifiManager->getPW());
|
||||
myConfig.saveFile();
|
||||
} else {
|
||||
// Log.info(F("WIFI: Using SSID=%s and %s." CR), myConfig.getWifiSSID(),
|
||||
// myConfig.getWifiPass()); Log.info(F("WIFI: Using SSID=%s and %s." CR),
|
||||
// myConfig.getWifiSSID(), "*****");
|
||||
Log.notice(
|
||||
F("WIFI: Exited portal, no connection to wifi. Rebooting..." CR));
|
||||
}
|
||||
|
||||
if (strlen(userSSID) == 0 && showPortal) {
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(
|
||||
F("WIFI: Connecting to WIFI via connection manager (portal=%s)." CR),
|
||||
showPortal ? "true" : "false");
|
||||
#endif
|
||||
WiFiManager myWifiManager;
|
||||
Log.notice(F("WIFI: Starting wifi portal." CR));
|
||||
myWifiManager.setDebugOutput(true);
|
||||
myWifiManager.setClass("invert");
|
||||
myWifiManager.setConfigPortalTimeout(120); // Keep it open for 120 seconds
|
||||
bool f =
|
||||
myWifiManager.startConfigPortal(WIFI_DEFAULT_SSID, WIFI_DEFAULT_PWD);
|
||||
if (f) {
|
||||
// Log.notice(F("WIFI: Success got values from WIFI portal=%s,%s." CR),
|
||||
// myWifiManager.getWiFiSSID(), myWifiManager.getWiFiPass() );
|
||||
Log.notice(F("WIFI: Success got values from WIFI portal=%s,%s." CR),
|
||||
myWifiManager.getWiFiSSID(), "*****");
|
||||
myConfig.setWifiSSID(myWifiManager.getWiFiSSID());
|
||||
myConfig.setWifiPass(myWifiManager.getWiFiPass());
|
||||
myConfig.saveFile();
|
||||
} else {
|
||||
Log.notice(F("WIFI: Failure from WIFI portal, rebooting." CR));
|
||||
delay(200);
|
||||
ESP.reset();
|
||||
}
|
||||
}
|
||||
stopDoubleReset();
|
||||
delay(500);
|
||||
ESP.reset();
|
||||
}
|
||||
|
||||
// Connect to wifi
|
||||
int i = 0;
|
||||
//
|
||||
// Call the wifi manager in loop
|
||||
//
|
||||
void WifiConnection::loop() { myDRD->loop(); }
|
||||
|
||||
// Log.notice(F("WIFI: Connecting to WIFI, mode=%d,persistent=%d,fhy=%d ."
|
||||
// CR), WiFi.getMode(), WiFi.getPersistent(), WiFi.getPhyMode() );
|
||||
//
|
||||
// Connect to last known access point, non blocking mode.
|
||||
//
|
||||
void WifiConnection::connectAsync() {
|
||||
WiFi.persistent(true);
|
||||
WiFi.mode(WIFI_STA);
|
||||
if (strlen(userSSID)) {
|
||||
Log.notice(F("WIFI: Connecting to wifi using hardcoded settings %s." CR),
|
||||
@ -102,32 +193,50 @@ bool Wifi::connect(bool showPortal) {
|
||||
myConfig.getWifiSSID());
|
||||
WiFi.begin(myConfig.getWifiSSID(), myConfig.getWifiPass());
|
||||
}
|
||||
}
|
||||
|
||||
// WiFi.printDiag(Serial);
|
||||
|
||||
//
|
||||
// Blocks until wifi connection has been found
|
||||
//
|
||||
bool WifiConnection::waitForConnection(int maxTime) {
|
||||
#if DEBUG_LEVEL == 6
|
||||
WiFi.printDiag(Serial);
|
||||
#endif
|
||||
int i = 0;
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(200);
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
|
||||
if (i++ > 100) { // Try for 20 seconds.
|
||||
if (i % 10) Serial.print(".");
|
||||
|
||||
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());
|
||||
WiFi.disconnect();
|
||||
return connectedFlag; // Return to main that we have failed to connect.
|
||||
Serial.print(CR);
|
||||
return false; // Return to main that we have failed to connect.
|
||||
}
|
||||
}
|
||||
|
||||
Serial.print(CR);
|
||||
connectedFlag = true;
|
||||
Log.notice(F("WIFI: Connected to wifi ip=%s." CR), getIPAddress().c_str());
|
||||
Log.notice(F("WIFI: Using mDNS name %s." CR), myConfig.getMDNS());
|
||||
return connectedFlag;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Connect to last known access point, blocking mode.
|
||||
//
|
||||
bool WifiConnection::connect() {
|
||||
connectAsync();
|
||||
return waitForConnection(20); // 20 seconds.
|
||||
}
|
||||
|
||||
//
|
||||
// This will erase the stored credentials and forcing the WIFI manager to AP
|
||||
// mode.
|
||||
//
|
||||
bool Wifi::disconnect() {
|
||||
bool WifiConnection::disconnect() {
|
||||
Log.notice(F("WIFI: Erasing stored WIFI credentials." CR));
|
||||
// Erase WIFI credentials
|
||||
return WiFi.disconnect(true);
|
||||
@ -138,7 +247,7 @@ bool Wifi::disconnect() {
|
||||
//
|
||||
//
|
||||
//
|
||||
bool Wifi::updateFirmware() {
|
||||
bool WifiConnection::updateFirmware() {
|
||||
if (!newFirmware) {
|
||||
Log.notice(F("WIFI: No newer version exist, skipping update." CR));
|
||||
return false;
|
||||
@ -173,7 +282,7 @@ bool Wifi::updateFirmware() {
|
||||
//
|
||||
// Download and save file
|
||||
//
|
||||
void Wifi::downloadFile(const char *fname) {
|
||||
void WifiConnection::downloadFile(const char *fname) {
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("WIFI: Download file %s." CR), fname);
|
||||
#endif
|
||||
@ -201,7 +310,7 @@ void Wifi::downloadFile(const char *fname) {
|
||||
//
|
||||
// Check what firmware version is available over OTA
|
||||
//
|
||||
bool Wifi::checkFirmwareVersion() {
|
||||
bool WifiConnection::checkFirmwareVersion() {
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("WIFI: Checking if new version exist." CR));
|
||||
#endif
|
||||
@ -282,7 +391,8 @@ bool Wifi::checkFirmwareVersion() {
|
||||
//
|
||||
// Parse a version string in the format M.m.p (eg. 1.2.10)
|
||||
//
|
||||
bool Wifi::parseFirmwareVersionString(int (&num)[3], const char *version) {
|
||||
bool WifiConnection::parseFirmwareVersionString(int (&num)[3],
|
||||
const char *version) {
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("WIFI: Parsing version number string %s." CR), version);
|
||||
#endif
|
||||
|
33
src/wifi.hpp
@ -25,13 +25,19 @@ SOFTWARE.
|
||||
#define SRC_WIFI_HPP_
|
||||
|
||||
// Include
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <Arduino.h>
|
||||
#include <CertStoreBearSSL.h>
|
||||
|
||||
// classes
|
||||
class Wifi {
|
||||
class WifiConnection {
|
||||
private:
|
||||
// SSL
|
||||
BearSSL::CertStore _certStore;
|
||||
int _certCount = 0;
|
||||
|
||||
// WIFI
|
||||
bool connectedFlag = false;
|
||||
void connectAsync();
|
||||
bool waitForConnection(int maxTime = 20);
|
||||
|
||||
// OTA
|
||||
bool newFirmware = false;
|
||||
@ -40,10 +46,23 @@ class Wifi {
|
||||
|
||||
public:
|
||||
// WIFI
|
||||
bool connect(bool showPortal = false);
|
||||
WifiConnection();
|
||||
|
||||
bool connect();
|
||||
bool disconnect();
|
||||
bool isConnected() { return connectedFlag; }
|
||||
String getIPAddress() { return WiFi.localIP().toString(); }
|
||||
bool isConnected();
|
||||
bool isDoubleResetDetected();
|
||||
void stopDoubleReset();
|
||||
bool hasConfig();
|
||||
String getIPAddress();
|
||||
void startPortal();
|
||||
void loop();
|
||||
|
||||
// SSL
|
||||
void initCertstore();
|
||||
BearSSL::CertStore* getCertStore() { return &_certStore; }
|
||||
int getCertCount() { return _certCount; }
|
||||
void initNTP();
|
||||
|
||||
// OTA
|
||||
bool updateFirmware();
|
||||
@ -51,7 +70,7 @@ class Wifi {
|
||||
};
|
||||
|
||||
// Global instance created
|
||||
extern Wifi myWifi;
|
||||
extern WifiConnection myWifi;
|
||||
|
||||
#endif // SRC_WIFI_HPP_
|
||||
|
||||
|
@ -1,15 +1,18 @@
|
||||
Backlog of changes
|
||||
##################
|
||||
|
||||
This is a list of potential ideas to implemnt in the software.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
- Write contribution instructions
|
||||
- Example project for creating integrations and instructions
|
||||
|
||||
Code
|
||||
-------------
|
||||
----
|
||||
|
||||
- Support for MQTT
|
||||
- Support for plato
|
||||
- Use pre-commit for validating check-in
|
||||
- Automatic builds via github actions
|
||||
- Show indicated battery life based on interval (check if its feasable)
|
||||
- Use brewflasher for flashing
|
||||
|
@ -82,8 +82,6 @@ This is a list of C++ defines that is used to enable/disable functions in the co
|
||||
- description
|
||||
* - ACTIVATE_OTA
|
||||
- Enables the OTA functionallity in the code
|
||||
* - USE_GYRO_TEMP
|
||||
- Uses temperature from gyro instead of DS18B20 (experimental)
|
||||
* - SKIP_SLEEPMODE
|
||||
- THe device never goes into sleep mode, useful when developing.
|
||||
* - CFG_DISABLE_LOGGING
|
||||
|
@ -22,7 +22,7 @@ copyright = '2021-2022, Magnus Persson'
|
||||
author = 'Magnus Persson'
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = '0.5.0'
|
||||
release = '0.6.0'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
@ -41,15 +41,17 @@ templates_path = ['_templates']
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = []
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_theme = 'furo'
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
html_static_path = ['_static']
|
||||
|
||||
html_show_sourcelink = False
|
||||
html_show_sphinx = True
|
||||
|
@ -99,19 +99,19 @@ Push Settings
|
||||
|
||||
* **HTTP URL 1:**
|
||||
|
||||
Endpoint to send data via http. Format used is standard iSpindle format (see format section).
|
||||
Endpoint to send data via http. Format used Format used :ref:`data-formats-ispindle`
|
||||
|
||||
* **HTTP URL 2:**
|
||||
|
||||
Endpoint to send data via http. Format used is standard iSpindle format (see format section).
|
||||
Endpoint to send data via http. Format used :ref:`data-formats-ispindle`
|
||||
|
||||
* **Brewfather URL:**
|
||||
|
||||
Endpoint to send data via http to brewfather. Format used is defined by brewfather (see format section).
|
||||
Endpoint to send data via http to brewfather. Format used :ref:`data-formats-brewfather`
|
||||
|
||||
* **Influx DB v2 URL:**
|
||||
|
||||
Endpoint to send data via http to InfluxDB. For format (see format section).
|
||||
Endpoint to send data via http to InfluxDB. Format used :ref:`data-formats-influxdb2`
|
||||
|
||||
* **Influx DB v2 Organisation:**
|
||||
|
||||
@ -125,6 +125,22 @@ Push Settings
|
||||
|
||||
Token with write access to bucket.
|
||||
|
||||
* **MQTT server:**
|
||||
|
||||
IP or name of server to send data to. Format used :ref:`data-formats-ispindle`
|
||||
|
||||
* **MQTT Topic:**
|
||||
|
||||
Name of topic to publish sensor readings to, iSpindle format is used.
|
||||
|
||||
* **MQTT user:**
|
||||
|
||||
Username or blank if anonymous is accepted
|
||||
|
||||
* **MQTT password:**
|
||||
|
||||
Password or blank if anonymous is accepted
|
||||
|
||||
|
||||
Gravity Settings
|
||||
++++++++++++++++
|
||||
@ -135,7 +151,8 @@ Gravity Settings
|
||||
|
||||
* **Gravity formula:**
|
||||
|
||||
Gravity formula is compatible with standard iSpindle formulas so any existing calculation option can be used. Is updated if the calibration function is used.
|
||||
Gravity formula is compatible with standard iSpindle formulas so any existing calculation option can be used. You can also use
|
||||
the feature to create the formula by supplying the raw data. See :ref:`create-formula`
|
||||
|
||||
* **Temperature correct gravity:**
|
||||
|
||||
@ -165,7 +182,15 @@ Hardware Settings
|
||||
|
||||
* **Temperature correction:**
|
||||
|
||||
This value will be added to the temperature reading (negative value will reduce temperature reading).
|
||||
This value will be added to the temperature reading (negative value will reduce temperature reading). This is applied
|
||||
when the device starts. So changing this will not take affect until the device is restarted.
|
||||
|
||||
* **Gyro Temperature:**
|
||||
|
||||
Enable this feature will use the temp sensor i the gyro instead of the DS18B20, the benefit is shorter run time and
|
||||
longer battery life (this is an experimental feature). The value used is the first temperature reading from when the
|
||||
device is activated, since the gyro should be cool this is reflecting the surronding temperature. After it has
|
||||
been running the value would be totally off.
|
||||
|
||||
* **OTA URL:**
|
||||
|
||||
@ -229,12 +254,17 @@ Other parameters are the same as in the configuration guide.
|
||||
"influxdb2-org": "Qwerty",
|
||||
"influxdb2-bucket": "Qwerty",
|
||||
"influxdb2-auth": "Qwerty",
|
||||
"mqtt-push": "192.168.1.50",
|
||||
"mqtt-topic": "Qwerty",
|
||||
"mqtt-user": "Qwerty",
|
||||
"mqtt-pass": "Qwerty",
|
||||
"sleep-interval": 30,
|
||||
"voltage-factor": 1.59,
|
||||
"gravity-formula": "0.0*tilt^3+0.0*tilt^2+0.0017978*tilt+0.9436",
|
||||
"gravity-format": "G",
|
||||
"temp-adjustment-value": 0,
|
||||
"gravity-temp-adjustment": false,
|
||||
"gyro-temp": true,
|
||||
"gyro-calibration-data": {
|
||||
"ax": -330,
|
||||
"ay": -2249,
|
||||
@ -348,6 +378,10 @@ Used to update push settings via an HTTP POST command. Payload is in JSON format
|
||||
"influxdb2-org": "Qwerty",
|
||||
"influxdb2-bucket": "Qwerty",
|
||||
"influxdb2-auth": "Qwerty"
|
||||
"mqtt-push": "192.168.1.50",
|
||||
"mqtt-topic": "Qwerty",
|
||||
"mqtt-user": "Qwerty",
|
||||
"mqtt-pass": "Qwerty",
|
||||
}
|
||||
|
||||
|
||||
@ -358,6 +392,10 @@ Used to update gravity settings via an HTTP POST command. Payload is in JSON for
|
||||
|
||||
* ``gravity-formula`` keywords ``temp`` and ``tilt`` are supported.
|
||||
|
||||
.. note::
|
||||
``gravity-temp-adjustment`` is defined as "on" or "off" when posting since this is the output values
|
||||
from a checkbox, when reading data it's sent as boolean (true,false).
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
@ -367,17 +405,22 @@ Used to update gravity settings via an HTTP POST command. Payload is in JSON for
|
||||
}
|
||||
|
||||
|
||||
POST: /api/config/gravity
|
||||
=========================
|
||||
POST: /api/config/hardware
|
||||
==========================
|
||||
|
||||
Used to update hardware settings via an HTTP POST command. Payload is in JSON format.
|
||||
|
||||
.. note::
|
||||
``gyro-temp`` is defined as "on" or "off" when posting since this is the output values from a checkbox, when
|
||||
reading data it's sent as boolean (true,false).
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"id": "ee1bfc",
|
||||
"voltage-factor": 1.59,
|
||||
"temp-adjustment": 0,
|
||||
"gyro-temp": "off",
|
||||
"ota-url": "http://192.168.1.50/firmware/gravmon/"
|
||||
}
|
||||
|
||||
@ -440,28 +483,33 @@ present or the API call will fail.
|
||||
|
||||
url = "http://" + host + "/api/config/push"
|
||||
json = { "id": id,
|
||||
"http-push": "http://192.168.1.1/ispindel", # HTTP endpoint
|
||||
"http-push2": "", # HTTP endpoint2
|
||||
"brewfather-push": "", # Brewfather URL
|
||||
"influxdb2-push": "", # InfluxDB2 settings
|
||||
"http-push": "http://192.168.1.1/ispindel",
|
||||
"http-push2": "",
|
||||
"brewfather-push": "",
|
||||
"influxdb2-push": "",
|
||||
"influxdb2-org": "",
|
||||
"influxdb2-bucket": "",
|
||||
"influxdb2-auth": ""
|
||||
"influxdb2-auth": "",
|
||||
"mqtt-push": "192.168.1.50",
|
||||
"mqtt-topic": "Qwerty",
|
||||
"mqtt-user": "Qwerty",
|
||||
"mqtt-pass": "Qwerty"
|
||||
}
|
||||
set_config( url, json )
|
||||
|
||||
url = "http://" + host + "/api/config/gravity"
|
||||
json = { "id": id,
|
||||
"gravity-formula": "", # If you want to set the gravity formula
|
||||
"gravity-temp-adjustment": "off" # on or off
|
||||
"gravity-formula": "",
|
||||
"gravity-temp-adjustment": "off" # Adjust gravity (on/off)
|
||||
}
|
||||
set_config( url, json )
|
||||
|
||||
url = "http://" + host + "/api/config/hardware"
|
||||
json = { "id": id,
|
||||
"voltage-factor": 1.59, # Default value for voltage calculation
|
||||
"temp-adjustment": 0, # If temp sensor needs to be corrected
|
||||
"ota-url": "" # if the device should seach for a new update when active
|
||||
"voltage-factor": 1.59, # Default value for voltage calculation
|
||||
"temp-adjustment": 0, # If temp sensor needs to be corrected
|
||||
"gyro-temp": "on", # Use the temp sensor in the gyro instead (on/off)
|
||||
"ota-url": "" # if the device should seach for a new update when active
|
||||
}
|
||||
set_config( url, json )
|
||||
|
||||
@ -486,6 +534,8 @@ present or the API call will fail.
|
||||
Data Formats
|
||||
############
|
||||
|
||||
.. _data-formats-ispindle:
|
||||
|
||||
iSpindle format
|
||||
===============
|
||||
|
||||
@ -512,6 +562,8 @@ This is the format used for standard http posts.
|
||||
}
|
||||
|
||||
|
||||
.. _data-formats-brewfather:
|
||||
|
||||
Brewfather format
|
||||
=================
|
||||
|
||||
@ -529,6 +581,8 @@ This is the format for Brewfather
|
||||
}
|
||||
|
||||
|
||||
.. _data-formats-influxdb2:
|
||||
|
||||
Influx DB v2
|
||||
============
|
||||
|
||||
|
@ -48,7 +48,29 @@ The main differences
|
||||
|
||||
* **Gyro Movement**
|
||||
|
||||
The software will detect if the gyro is moving and if this is the case it will go back to sleep for 60seconds. This way we should avoid faulty measurements.
|
||||
The software will detect if the gyro is moving and if this is the case it will go back to sleep for 60seconds.
|
||||
This way we should avoid faulty measurements.
|
||||
|
||||
* **WIFI connection issues**
|
||||
|
||||
The software will not wait indefiently for a wifi connection. If it takes longer than 20 seconds to connect then
|
||||
the device will go into deep sleep for 60 seoncds and then retry.
|
||||
|
||||
* **Use gyro temperature sensor**
|
||||
|
||||
This works fine when the device has time to cool down between measurements and it saves up to 400 ms.
|
||||
My testing shows that this is quite accurate with a deviation of less than 0.3C. This
|
||||
reduces the run time by 20% (with optimal wifi connection).
|
||||
|
||||
The graph below compares from the temp from two different devices in the same bucket of water. One with
|
||||
gyro temp enabled and one with the DS18B20 sensor. The blue line is the gyro temperature and this clear
|
||||
that the temperature will be higher after it has been running but cools down when in sleep mode. The interval
|
||||
has been set to 300s. A low delay of 30s will not allow the gyro to cool down and the temperature will
|
||||
be 0.5-1.0C higher.
|
||||
|
||||
.. image:: images/temp1.png
|
||||
:width: 800
|
||||
:alt: Gyro temp vs DS18B20
|
||||
|
||||
Other features
|
||||
--------------
|
||||
@ -62,26 +84,21 @@ Other features
|
||||
Experimental features
|
||||
---------------------
|
||||
|
||||
.. tip::
|
||||
These are not enabled by default. To enable them you need to recompile the code and enable the correct defines.
|
||||
|
||||
* Use the temperature sensor in the gyro instead of DS18B20
|
||||
|
||||
This works fine when the device has time to cool down between measurements and it saves a few milliseconds (reduced battery consumption). My testing shows that this is quite accurate.
|
||||
There is lots of battery power to save, reading the temp sensor takes almost as long as the gyro. This could reduce the run time by 40-50% and probly extend battery life with the same.
|
||||
However more testing is required. Might add this as an option in the UI.
|
||||
|
||||
* Performance measurements
|
||||
* **Performance measurements**
|
||||
|
||||
I've also create a small library to measure execution code in some areas of the code that i know is time consuming. This way I can find a good balance between performace and quality.
|
||||
|
||||
See the :ref:`compiling-the-software` for more information.
|
||||
|
||||
* **Power measurements**
|
||||
|
||||
I've also create a project to measure the power consumption of the device, but more on this later.
|
||||
|
||||
|
||||
Battery life
|
||||
------------
|
||||
|
||||
I'm currently measuring battery life of v0.5 but previous versions have been able to measure gravity for a 2-3 weeks without issues. Using 900 seconds as interval.
|
||||
I'm currently measuring battery life of v0.5 but previous versions have been able to measure gravity for a 2-3 weeks without issues (Using 900 seconds as interval).
|
||||
|
||||
*More on this topics once my tests are done*
|
||||
|
||||
@ -95,7 +112,7 @@ The typical runtime in a measurement cycle is approx 2 seconds and in some cases
|
||||
essential for long batterylife. Out of the 2 seconds of run-time the major time is spent on gyro readings (1.3s) and temperature measurements of (0.6s) so using the gyro sensor for measureing
|
||||
temperature would reduce the total runtime with 25%. Sending data over http takes less than 100ms (on my local network) so this is not drawing much power.
|
||||
|
||||
The image below shows how the run-time varies over time. The pink line is the wifi connection time and this is why the time varies.
|
||||
The image below shows how the run-time varies over time. The pink line is the wifi connection time and this is why the time varies. The orange is the total runtime for the awake period.
|
||||
|
||||
.. image:: images/perf1.png
|
||||
:width: 800
|
||||
|
BIN
src_docs/source/images/brewflasher.png
Normal file
After Width: | Height: | Size: 156 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 36 KiB |
BIN
src_docs/source/images/software_design.png
Normal file
After Width: | Height: | Size: 196 KiB |
BIN
src_docs/source/images/temp1.png
Normal file
After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 14 KiB |
@ -7,7 +7,7 @@ Welcome to GravityMon's documentation!
|
||||
######################################
|
||||
|
||||
.. note::
|
||||
This documentation reflects **v0.5**. Last updated 2022-01-09
|
||||
This documentation reflects **v0.6**. Last updated 2022-01-15
|
||||
|
||||
|
||||
GravityMon is a replacement firmare for the iSpindle firmware, it uses the same hardware configuration so
|
||||
@ -23,6 +23,9 @@ The hardware design comes from the fantastic iSpindle project so that is not cov
|
||||
My approach to this software is a little different from that the original ispindle firmware. The github repository can
|
||||
be found here; `GravityMon on Github <https://github.com/mp-se/gravitymon>`_
|
||||
|
||||
|
||||
|
||||
|
||||
.. note::
|
||||
This software is in the early stages even though its more than one year old so if you find issues, please
|
||||
open a ticket on github.
|
||||
@ -33,25 +36,33 @@ be found here; `GravityMon on Github <https://github.com/mp-se/gravitymon>`_
|
||||
The main differences:
|
||||
---------------------
|
||||
|
||||
* Operates in two modes ``gravity monitoring`` and ``configuration mode``
|
||||
* Send data to multiple endpoints when pushing data.
|
||||
* Automatic temperature adjustment of gravity reading
|
||||
* OTA support from local webserver
|
||||
* Build in function to create gravity formulas
|
||||
|
||||
There are also a experimental features such as:
|
||||
|
||||
* Operates in two modes gravity monitoring and configuration mode (simplify calibration)
|
||||
* Modern web based UI for configuration (in config mode)
|
||||
* REST API
|
||||
* Send data to multiple endpoints when pushing data (2xhttp, brewfather, influxdb v2, mqtt supported)
|
||||
* Automatic temperature adjustment of gravity reading
|
||||
* OTA support from local webserver
|
||||
* Built in function to create gravity formulas, no need for additional software, just enter tilt/gravity.
|
||||
* Visual graph showing how formula will be interpreted
|
||||
* Using the temperature sensor in gyro instead of DS18B20 (faster)
|
||||
* Performance measurements (used to optimise code)
|
||||
* Built in performance measurements (used to optimise code)
|
||||
|
||||
For a complete breakdown see the :ref:`functionallity`
|
||||
|
||||
This is a simple overview of the different components that the software contains. The green ones are only active during `configuration mode` in
|
||||
order to save battery.
|
||||
|
||||
.. image:: images/software_design.png
|
||||
:width: 600
|
||||
:alt: Software design
|
||||
|
||||
|
||||
Credits to
|
||||
----------
|
||||
Ideas to some of these functions have been picked up from disucssions in the iSpindle forums. This software uses
|
||||
the following libraries and without these this would have been much more difficult to acheive:
|
||||
|
||||
* https://github.com/jrowberg/i2cdevlib.git
|
||||
* https://github.com/jrowberg/i2cdevlib
|
||||
|
||||
This library contains the basic code to interact with the gyro + many more chips.
|
||||
|
||||
@ -67,7 +78,7 @@ the following libraries and without these this would have been much more difficu
|
||||
|
||||
Can detect if the reset button is pressed twice, is used to enter WIFI config mode.
|
||||
|
||||
* https://github.com/tzapu/WiFiManager
|
||||
* https://github.com/khoih-prog/ESP_WiFiManager
|
||||
|
||||
Configure wifi settings.
|
||||
|
||||
@ -91,6 +102,10 @@ the following libraries and without these this would have been much more difficu
|
||||
|
||||
Create the gravity formula.
|
||||
|
||||
* https://github.com/256dpi/arduino-mqtt
|
||||
|
||||
Library for sending data to mqtt based on lightweight mqtt implemenentation.
|
||||
|
||||
* https://graphjs.com/
|
||||
|
||||
Render the graphs in the UI.
|
||||
@ -111,6 +126,7 @@ the following libraries and without these this would have been much more difficu
|
||||
configuration
|
||||
compiling
|
||||
contributing
|
||||
q_and_a
|
||||
backlog
|
||||
|
||||
Indices and tables
|
||||
|
@ -1,36 +1,16 @@
|
||||
Installation
|
||||
------------
|
||||
|
||||
Official esptool
|
||||
================
|
||||
Brewflasher
|
||||
===========
|
||||
|
||||
The prefered option for flashing esp8266 device is via the official esptool. Documentation can be found
|
||||
here; `esptool home page <https://docs.espressif.com/projects/esptool/en/latest/esp32/>`_
|
||||
The prefered option for flashing GravityMon is using BrewFlasher, its a tools that support many brewing related firmwares for ESP8266 and ESP32. This works
|
||||
on both Windows and Mac. You can download the latest version from here: `Brewflasher <https://www.brewflasher.com/>`_
|
||||
|
||||
Windows 10 should install a driver for the USB -> Serial automatically when you connect a esp8266.
|
||||
|
||||
Flashing on windows
|
||||
*******************
|
||||
|
||||
The basic command for flashing on Windows is;
|
||||
|
||||
``esptool.py --port COM4 write_flash 0x0 firmware.bin``
|
||||
|
||||
If there are issues you can try do erase the flash first using this command;
|
||||
|
||||
``esptool.py --port COM4 erase_flash``
|
||||
|
||||
Serial Monitoring
|
||||
*******************
|
||||
|
||||
To check output from the device (logs) there are several tools out there. I found this simple tool in the Windows Store called ``Serial Port Monitoring``.
|
||||
Just select a baud rate of 115200, 8N1.
|
||||
|
||||
.. image:: images/serial.png
|
||||
:width: 800
|
||||
.. image:: images/brewflasher.png
|
||||
:width: 600
|
||||
:alt: Serial output
|
||||
|
||||
|
||||
Binaries
|
||||
********
|
||||
|
||||
@ -44,7 +24,6 @@ In the /bin directory you will find 2 different firmware builds;
|
||||
|
||||
This version also submits performance data to an influx database with detailed execution times.
|
||||
|
||||
|
||||
In these versions all the html files are embedded in the binaries. The file system is currently only used for storing
|
||||
the configuration file.
|
||||
|
||||
@ -52,6 +31,31 @@ If the software becomes so large the html files can be moved to the file system,
|
||||
default (see compiling for details). This approach makes installation much easier and ensure that html files
|
||||
and code is in sync.
|
||||
|
||||
Esptool
|
||||
=======
|
||||
|
||||
The other option for flashing esp8266 device is via the official esptool. Documentation can be found
|
||||
here; `esptool home page <https://docs.espressif.com/projects/esptool/en/latest/esp32/>`_
|
||||
|
||||
Windows 10 should install a driver for the USB -> Serial automatically when you connect a esp8266.
|
||||
|
||||
The basic command for flashing on Windows is;
|
||||
|
||||
``esptool.py --port COM4 write_flash 0x0 firmware.bin``
|
||||
|
||||
If there are issues you can try do erase the flash first using this command;
|
||||
|
||||
``esptool.py --port COM4 erase_flash``
|
||||
|
||||
Serial Monitoring
|
||||
=================
|
||||
|
||||
To check output from the device (logs) there are several tools out there. I found this simple tool in the Windows Store called ``Serial Port Monitoring``.
|
||||
Just select a baud rate of 115200, 8N1.
|
||||
|
||||
.. image:: images/serial.png
|
||||
:width: 800
|
||||
:alt: Serial output
|
||||
|
||||
Configuring WIFI
|
||||
================
|
||||
@ -59,8 +63,9 @@ Configuring WIFI
|
||||
When the device is flashed it will need to have WIFI configuration in order to work. If you have used other software on
|
||||
the device its possible that wifi settings exist.
|
||||
|
||||
If this is not configured in the device it will create an wirless access point called `GravMon`. Connect to this AP and
|
||||
enter the SSID and password you want to use. If the web page dont open automatically you can enter the following adress
|
||||
If this is not configured in the device it will create an wirless access point called `GravMon`. The default password is `password`.
|
||||
|
||||
Connect to this AP and enter the SSID and password you want to use. If the web page dont open automatically you can enter the following adress
|
||||
in the browser: **http://192.168.4.1**
|
||||
|
||||
.. image:: images/wifi.png
|
||||
|
8
src_docs/source/q_and_a.rst
Normal file
@ -0,0 +1,8 @@
|
||||
Q & A
|
||||
#####
|
||||
|
||||
My device is no going in to sleep after fully charged
|
||||
-----------------------------------------------------
|
||||
- Calibrate the device in the web interface
|
||||
- Check the angle/tilt. If the device is reporting 90 degress then its not going into sleep. Tilt the device and check if sleep works.
|
||||
- Check in reported voltage of the battery in the web interface. If this is higher than 4.15V the device belives its beeing charged. In that case adjust the voltage factor under hardware so the voltage drops below 4.15V.
|
@ -3,10 +3,26 @@
|
||||
Releases
|
||||
########
|
||||
|
||||
v0.5.0 (beta)
|
||||
-------------
|
||||
v0.6.0
|
||||
------
|
||||
|
||||
Latest next target version is: **v0.5.0**. This is hosted in the **dev branch**.
|
||||
Latest stable version.
|
||||
|
||||
* Changed the wifi manager and refactored wifi.cpp
|
||||
* LED is now turned on when Wifi Portal is open
|
||||
* Refactored main.cpp to make it easier to read
|
||||
* Tested runtime performance
|
||||
* Improved documentation
|
||||
* Added warning on config page when sleep is <300
|
||||
* Enabled selection of gyro temperature sensor under Hardware settings
|
||||
* Added warning when short sleep interval and gyro temp is enabled
|
||||
* Added support for MQTT
|
||||
* Bug: MPU init sometimes caused crash during startup.
|
||||
|
||||
`Release v0.6 on Github <https://github.com/mp-se/gravitymon/releases/tag/v0.6.0>`_
|
||||
|
||||
v0.5.0
|
||||
------
|
||||
|
||||
* Added feature to calcuate formula on device
|
||||
* Total rewrite of documentation
|
||||
@ -15,10 +31,9 @@ Latest next target version is: **v0.5.0**. This is hosted in the **dev branch**.
|
||||
* Cleanup of code
|
||||
* Refactor code from C to C++
|
||||
|
||||
`Release v0.5 on Github <https://github.com/mp-se/gravitymon/releases/tag/v0.5.0>`_
|
||||
|
||||
v0.4.0
|
||||
------
|
||||
|
||||
Latest stable development version is: **v0.4.0**
|
||||
|
||||
`Release v0.4 on Github <https://github.com/mp-se/gravitymon/releases/tag/v0.4.0>`_
|
||||
|
||||
|
1
src_docs/source/sofware_overview.drawio
Normal file
@ -0,0 +1 @@
|
||||
<mxfile host="app.diagrams.net" modified="2022-01-14T15:32:37.218Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/97.0.1072.55" etag="muU0EPGkWBAsdBhYYONY" version="14.7.3" type="device"><diagram id="H8u3kietkkmoQsI5FEME" name="Page-1">7VpRb6M4EP41kfYetgoYCHls0na7uq3U22TV7dPJgSHxLcGRMU3SX38GDAFMknZL6vR0LxEeG2O+b+absUMPjZebLwyvFnfUh7Bn9v1ND131THOILPGbGra5wUDWILfMGfGlbWeYkGeQxr60JsSHuDaQUxpysqobPRpF4PGaDTNG1/VhAQ3rT13hOSiGiYdD1fpAfL7Ira452NlvgcwXxZMNZ5j3LHExWL5JvMA+XVdM6LqHxoxSnl8tN2MIU/AKXPL7bvb0lgtjEPGX3BDebQa3z8nj3xMy/OFux7M/J78+IznNEw4T+cZytXxbQMBoEvmQztLvodF6QThMVthLe9eCdGFb8GUoWoa4xCGZR+I6hECsaiRnB8Zhs3fdRomGcCOgS+BsK4bIG2yJX+FBRXu9o2PgSNuiQoVduBCWLjAvp96hJC4kUK8AzXROC5qP40V2q9ENgoZRh7AMrgqEJVpVCK2TQai63X0SL7LgFj9TzObA4wOgGsdBDUgYjmlIWXYvCoLA9Dxhjzmjv6DS4zszx3a6gdoaNKBu8VbDbIHaOZmzHvdViPzLVCpFywtxHBOvjiRsCP+ZXl/0naL9mHr2hY2QbF9tpKtnjW2lcQ+MiFcBJm3548FXdLeBsVgiTZgHx7xI5aLq1m3CIG0MQszJU30ZbfjLJ9xTIhZYUo3cOtVOM1jy5cu7qsLcmMhxGj6DGhPxLByUiTJ3KF/79z0EKR5yO53eZ+sz+6LHCVNRmjFxNU+vRgzWgchuwNS+r1EQJpurUTpfy613f02nivuJaON1h6uHaEQjaMSzNDV0M41cIlL3pTQvie+nD2lVirpAdxD6TiP0jWFL6LeprHuq0LcUYr9sGX2bqhaQewKVNKT1yKxhn5vMDl4gs0LyJrJJGV/QOY1weL2zNnxyN+YbpSuJ/z/A+VaWyTgR2bLGTofaaufjcu05MM5pJ+rF4vom1F0dIAso2fZntSGTYdHc5cKsVSRDDeRYOskZaiWnwsdjte98yNkjce9DjqFFn/ax0z/CTlmB1qvPg7WnDkb7WilFeil9nRoWlO5ofKwQ3E5pLBjg6lYlM9+QUGMWNLQGc/k6/+fBPQBpDUxTb2B+AHpMnfTYSum+xCT69IdC2gfcKCGrvlFCSPdGST07ncJyBQzzhIHomEAUCyS6xb4DJJtbTv1IGq4C5QTYUwqievKCV6uQeCKaaKRDjDoUleII5XhO3rP7kYR+7l8YhmHUSDXfeDRYDKFBEMNJDuuKt6+Q/gCzjPeW47hPHo0CQubihiX14cSS5tvg+labpLnmDDknOmK3tJ/9FE5U4eT79WQqLJf3X/eR8p/mxLa0c+J8pJrrdzZDXUqq9VJJ1XtmMVTjDLCf5bsb8fvARKi00v4NzyA8Ejh7/z1gEJNnPMvmS4FfpWKfvZs96tlXhyJIfpUgb+6V+aVK0gH33Rtv/QsRv3a9Hnlb5iqSYn3SssZ4h8Sm/lshEluqodXKRdFSlkSxGJT29Wfp5x7ATq2oGNygtXB3PBdmQTeK6rjnluXMlsqDBKRjsDuArlmpWy2fO7wzdKYC3TirARLWXpF/wJ2mgvrQ1o26+p/cO2w1zwJ8/ZtTU92cnkAtzgJs/fqCVH35XsmLPjwRgaiSPCFeuaajFspv/yDjeFF1/JOMgEZcFtqiCumEObORVM22MHFbmDNfz5xo7r7tzIuk3Rey6Ppf</diagram></mxfile>
|
@ -1,58 +0,0 @@
|
||||
Testing formatting
|
||||
------------------
|
||||
|
||||
Header 1
|
||||
########
|
||||
|
||||
Header 2
|
||||
--------
|
||||
|
||||
Header 3
|
||||
********
|
||||
|
||||
**BOLD**
|
||||
|
||||
*ITALIC*
|
||||
|
||||
``CODE SAMPLE``
|
||||
|
||||
1. List
|
||||
2. List
|
||||
|
||||
* List
|
||||
* List
|
||||
|
||||
.. list-table:: Table
|
||||
:widths: 40 60
|
||||
:header-rows: 1
|
||||
|
||||
* - table
|
||||
- table
|
||||
* - content
|
||||
- content
|
||||
|
||||
.. image:: images/formula1.png
|
||||
:width: 400
|
||||
:alt: image
|
||||
|
||||
.. code-block:: objdump
|
||||
|
||||
measurement,host=<mdns>,device=<id>,temp-format=<C|F>,gravity-format=SG,gravity=1.0004,corr-gravity=1.0004,angle=45.45,temp=20.1,battery=3.96,rssi=-18
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"project":"gravmon",
|
||||
"version":"0.4.10",
|
||||
"html": [
|
||||
"index.min.htm",
|
||||
"device.min.htm",
|
||||
"config.min.htm",
|
||||
"calibration.min.htm",
|
||||
"about.min.htm"
|
||||
]
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
Note...
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 115 KiB |
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 114 KiB |
195
test/certs-from-mozilla.py
Normal file
@ -0,0 +1,195 @@
|
||||
# Script to extract root CA.
|
||||
#
|
||||
# Credit to: https://maakbaas.com/esp8266-iot-framework/logs/https-requests/
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
import csv
|
||||
import os
|
||||
import re
|
||||
import string
|
||||
import sys
|
||||
from asn1crypto.x509 import Certificate
|
||||
import hashlib
|
||||
|
||||
from subprocess import Popen, PIPE, call, check_output
|
||||
try:
|
||||
from urllib.request import urlopen
|
||||
except:
|
||||
from urllib2 import urlopen
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except:
|
||||
from io import StringIO
|
||||
|
||||
import inspect, os.path
|
||||
|
||||
import socket
|
||||
from ssl import wrap_socket, CERT_NONE, PROTOCOL_SSLv23
|
||||
from ssl import SSLContext # Modern SSL?
|
||||
from ssl import HAS_SNI # Has SNI?
|
||||
|
||||
def preBuildCertificatesFun(domains, openssl):
|
||||
|
||||
print('Start building certificate store', flush=True)
|
||||
|
||||
allDomains = True
|
||||
|
||||
if len(domains) > 0:
|
||||
print('Only for domains: ' + domains, flush=True)
|
||||
domains = domains.split(',')
|
||||
allDomains = False
|
||||
|
||||
if allDomains:
|
||||
domains = []
|
||||
print('For all domains', flush=True)
|
||||
|
||||
filename = inspect.getframeinfo(inspect.currentframe()).filename
|
||||
dir_path = os.path.dirname(os.path.abspath(filename))
|
||||
|
||||
#path to openssl
|
||||
if openssl is None:
|
||||
openssl = "openssl.exe"
|
||||
|
||||
# below script content is adapted from:
|
||||
# https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py
|
||||
|
||||
f = open(dir_path + "/../src/certificates.h", "w", encoding="utf8")
|
||||
|
||||
f.write("#ifndef CERT_H" + "\n")
|
||||
f.write("#define CERT_H" + "\n\n")
|
||||
f.write("#include <Arduino.h>" + "\n\n")
|
||||
|
||||
# Mozilla's URL for the CSV file with included PEM certs
|
||||
mozurl = "https://ccadb-public.secure.force.com/mozilla/IncludedCACertificateReportPEMCSV"
|
||||
|
||||
# Load the manes[] and pems[] array from the URL
|
||||
names = []
|
||||
pems = []
|
||||
dates = []
|
||||
response = urlopen(mozurl)
|
||||
csvData = response.read()
|
||||
if sys.version_info[0] > 2:
|
||||
csvData = csvData.decode('utf-8')
|
||||
csvFile = StringIO(csvData)
|
||||
csvReader = csv.reader(csvFile)
|
||||
for row in csvReader:
|
||||
names.append(row[0]+":"+row[1]+":"+row[2])
|
||||
pems.append(row[32])
|
||||
dates.append(row[8])
|
||||
del names[0] # Remove headers
|
||||
del pems[0] # Remove headers
|
||||
del dates[0] # Remove headers
|
||||
|
||||
certFiles = []
|
||||
totalbytes = 0
|
||||
idx = 0
|
||||
addflag = False
|
||||
|
||||
# Process the text PEM using openssl into DER files
|
||||
sizes=[]
|
||||
for i in range(0, len(pems)):
|
||||
certName = "ca_%03d" % (idx);
|
||||
thisPem = pems[i].replace("'", "")
|
||||
|
||||
pemfile = open(certName + '.pem', "w", encoding="utf8")
|
||||
pemfile.write(thisPem)
|
||||
pemfile.close()
|
||||
|
||||
if allDomains:
|
||||
print('Added: ' + re.sub(f'[^{re.escape(string.printable)}]', '', names[i]), flush=True)
|
||||
else:
|
||||
for j in range(0, len(domains)):
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect((domains[j], 443))
|
||||
|
||||
context = SSLContext(2)
|
||||
context.verify_mode = 2
|
||||
context.load_verify_locations(certName + '.pem')
|
||||
try:
|
||||
if HAS_SNI: # Platform-specific: OpenSSL with enabled SNI
|
||||
context.wrap_socket(s, server_hostname=domains[j])
|
||||
else:
|
||||
context.wrap_socket(s)
|
||||
print('Added: ' + re.sub(f'[^{re.escape(string.printable)}]', '', names[i]), flush=True)
|
||||
addFlag = True
|
||||
break
|
||||
except:
|
||||
print('Skipped: ' + re.sub(f'[^{re.escape(string.printable)}]', '', names[i]), flush=True)
|
||||
addFlag = False
|
||||
|
||||
s.close()
|
||||
|
||||
if allDomains or addFlag:
|
||||
|
||||
f.write(("//" + dates[i] + " " + re.sub(f'[^{re.escape(string.printable)}]', '', names[i]) + "\n"))
|
||||
|
||||
ssl = Popen([openssl,'x509','-inform','PEM','-outform','DER','-out', certName + '.der'], shell = False, stdin = PIPE)
|
||||
pipe = ssl.stdin
|
||||
pipe.write(thisPem.encode('utf-8'))
|
||||
pipe.close()
|
||||
ssl.wait()
|
||||
|
||||
if os.path.exists(certName + '.der'):
|
||||
certFiles.append(certName)
|
||||
|
||||
der = open(certName + '.der','rb')
|
||||
|
||||
bytestr = der.read();
|
||||
sizes.append(len(bytestr))
|
||||
cert = Certificate.load(bytestr)
|
||||
idxHash = hashlib.sha256(cert.issuer.dump()).digest()
|
||||
|
||||
f.write("const uint8_t cert_" + str(idx) + "[] PROGMEM = {")
|
||||
for j in range(0, len(bytestr)):
|
||||
totalbytes+=1
|
||||
f.write(hex(bytestr[j]))
|
||||
if j<len(bytestr)-1:
|
||||
f.write(", ")
|
||||
f.write("};\n")
|
||||
|
||||
f.write("const uint8_t idx_" + str(idx) + "[] PROGMEM = {")
|
||||
for j in range(0, len(idxHash)):
|
||||
totalbytes+=1
|
||||
f.write(hex(idxHash[j]))
|
||||
if j<len(idxHash)-1:
|
||||
f.write(", ")
|
||||
f.write("};\n\n")
|
||||
|
||||
der.close()
|
||||
idx = idx + 1
|
||||
|
||||
os.unlink(certName + '.der')
|
||||
|
||||
os.unlink(certName + '.pem')
|
||||
|
||||
f.write("//global variables for certificates using " + str(totalbytes) + " bytes\n")
|
||||
f.write("const uint16_t numberOfCertificates PROGMEM = " + str(idx) + ";\n\n")
|
||||
|
||||
f.write("const uint16_t certSizes[] PROGMEM = {")
|
||||
for i in range(0, idx):
|
||||
f.write(str(sizes[i]))
|
||||
if i<idx-1:
|
||||
f.write(", ")
|
||||
f.write("};\n\n")
|
||||
|
||||
f.write("const uint8_t* const certificates[] PROGMEM = {")
|
||||
for i in range(0, idx):
|
||||
f.write("cert_" + str(i))
|
||||
if i<idx-1:
|
||||
f.write(", ")
|
||||
f.write("};\n\n")
|
||||
|
||||
f.write("const uint8_t* const indices[] PROGMEM = {")
|
||||
for i in range(0, idx):
|
||||
f.write("idx_" + str(i))
|
||||
if i<idx-1:
|
||||
f.write(", ")
|
||||
f.write("};\n\n#endif" + "\n")
|
||||
|
||||
f.close()
|
||||
|
||||
domains = ''
|
||||
openssl = 'openssl'
|
||||
preBuildCertificatesFun(domains, openssl)
|
@ -10,12 +10,17 @@
|
||||
"influxdb2-org": "hello",
|
||||
"influxdb2-bucket": "spann",
|
||||
"influxdb2-auth": "OijkU((jhfkh",
|
||||
"mqtt-push": "192.168.1.10",
|
||||
"mqtt-topic": "mytopic",
|
||||
"mqtt-user": "user",
|
||||
"mqtt-pass": "pass",
|
||||
"sleep-interval": 30,
|
||||
"voltage-factor": 1.59,
|
||||
"gravity-formula": "0.0*tilt^3+0.0*tilt^2+0.0017978*tilt+0.9436",
|
||||
"gravity-format": "G",
|
||||
"temp-adjustment-value": 0,
|
||||
"gravity-temp-adjustment": false,
|
||||
"gyro-temp": false,
|
||||
"gyro-calibration-data": {
|
||||
"ax": -330,
|
||||
"ay": -2249,
|
||||
|
@ -58,6 +58,7 @@ url = "http://" + host + "/api/config/hardware"
|
||||
json = { "id": id,
|
||||
"voltage-factor": 1.59, # Default value for voltage calculation
|
||||
"temp-adjustment": 0, # If temp sensor needs to be corrected
|
||||
"gyro-temp": "off", # Use the temp sensor in the gyro
|
||||
"ota-url": "" # if the device should seach for a new update when active
|
||||
}
|
||||
set_config( url, json )
|
||||
|
@ -2,5 +2,6 @@
|
||||
"app-name": "GravityMon ",
|
||||
"app-ver": "0.0.0",
|
||||
"id": "7376ef",
|
||||
"certs": true,
|
||||
"mdns": "gravmon"
|
||||
}
|
@ -3,5 +3,6 @@
|
||||
"device": false,
|
||||
"config": false,
|
||||
"calibration": false,
|
||||
"certs": false,
|
||||
"about": true
|
||||
}
|