36 Commits
v0.5.0 ... ssl

Author SHA1 Message Date
03e6fb6b22 Full ssl implementation (buggy) 2022-01-17 20:13:48 +01:00
2532f50215 Added script to extract CA 2022-01-16 22:08:25 +01:00
10163f3aa7 New install method via brewflasher 2022-01-15 16:49:36 +01:00
1adef20edd Updated docs 2022-01-14 16:39:59 +01:00
617e77a9d8 Updated formats with mqtt 2022-01-13 21:15:48 +01:00
c8d48a3236 Updated link to v0.6 2022-01-13 18:21:57 +01:00
f3d2e88b31 GitHub Action Build 2022-01-13 17:12:15 +00:00
a9dc3be329 Bump html to 0.6 2022-01-13 18:10:06 +01:00
043963df91 Added reference to mqtt 2022-01-13 12:38:29 +01:00
f318dea9b7 Moved some images 2022-01-13 09:34:01 +01:00
06b5b949d0 GitHub Action Build 2022-01-13 08:21:35 +00:00
5c400e4e47 Bump version 2022-01-13 09:18:51 +01:00
e1484ca2fd GitHub Action Build 2022-01-13 08:12:04 +00:00
4d300908a6 Updated release notes / doc 2022-01-13 09:09:09 +01:00
5b6ce7d672 Fixed crash bug in gyro.cpp 2022-01-13 09:08:53 +01:00
adc21b5527 Added new theames to doc build 2022-01-13 09:08:24 +01:00
8919c842fb GitHub Action Build 2022-01-12 17:57:58 +00:00
a270faa480 Merged mqtt 2022-01-12 18:55:54 +01:00
aad35e20bd Fixed mqtt issues 2022-01-12 17:39:54 +01:00
a83a74b5a4 Initial mqtt version 2022-01-11 23:23:43 +01:00
a703add227 GitHub Action Build 2022-01-11 16:22:54 +00:00
534a739891 Updated docs 2022-01-11 17:20:38 +01:00
01fe6bbf19 Updated influx format (corr gyrotemp) 2022-01-11 11:32:04 +01:00
19d1f7b1ca GitHub Action Build 2022-01-11 08:08:00 +00:00
2a2aecdee5 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-01-11 09:06:03 +01:00
325c7ee2ca Merge 2022-01-11 09:05:27 +01:00
57f5816f63 Enabled gyro temp 2022-01-11 09:04:09 +01:00
46b17177f2 GitHub Action Build 2022-01-10 19:42:49 +00:00
df1981e3dd Updated workflow 2022-01-10 20:40:44 +01:00
2f391c95c7 Updated code to 0.6 test 2022-01-10 20:14:58 +01:00
ea62d9e752 Added warning on short sleep interval 2022-01-10 20:14:23 +01:00
6364e251b7 Moved doc publich to master branch 2022-01-10 20:14:03 +01:00
93d9effcef Integrated changed for new wifimgr 2022-01-10 10:27:24 +01:00
4ff114642e Added wifmanager 2022-01-09 23:12:40 +01:00
7f775d78eb Refactor main to prepare for wifimgr lite 2022-01-09 14:49:20 +01:00
17bc23bef8 Updated release notes for 0.5 2022-01-09 09:50:28 +01:00
63 changed files with 7449 additions and 547 deletions

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item active"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Current version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3" id="h-app-ver-new" hidden><div class="col-md-8 themed-grid-col bg-light">New version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver-new">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Host name:</div><div class="col-md-4 themed-grid-col bg-light" id="mdns">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Device ID:</div><div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div></div><hr class="my-4"></div><script type="text/javascript">function getConfig(){var n="/api/device";$("#spinner").show(),$.getJSON(n,function(n){console.log(n),$("#app-ver").text(n["app-ver"]+" (html 0.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">&times;</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Current version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3" id="h-app-ver-new" hidden><div class="col-md-8 themed-grid-col bg-light">New version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver-new">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Host name:</div><div class="col-md-4 themed-grid-col bg-light" id="mdns">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Device ID:</div><div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div></div><div class="row mb-3"><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>

Binary file not shown.

Binary file not shown.

View File

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

Binary file not shown.

View File

@ -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.');

File diff suppressed because one or more lines are too long

View File

@ -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.');

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item active"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Current version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3" id="h-app-ver-new" hidden><div class="col-md-8 themed-grid-col bg-light">New version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver-new">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Host name:</div><div class="col-md-4 themed-grid-col bg-light" id="mdns">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Device ID:</div><div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div></div><hr class="my-4"></div><script type="text/javascript">function getConfig(){var n="/api/device";$("#spinner").show(),$.getJSON(n,function(n){console.log(n),$("#app-ver").text(n["app-ver"]+" (html 0.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">&times;</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Current version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3" id="h-app-ver-new" hidden><div class="col-md-8 themed-grid-col bg-light">New version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver-new">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Host name:</div><div class="col-md-4 themed-grid-col bg-light" id="mdns">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Device ID:</div><div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div></div><div class="row mb-3"><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>

View File

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

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/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">&times;</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">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">&times;</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">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>

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View 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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because one or more lines are too long

View 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());

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

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

View File

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

View 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.

View File

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

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

View File

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

View File

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 112 KiB

View File

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

View File

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 115 KiB

View File

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 114 KiB

195
test/certs-from-mozilla.py Normal file
View 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)

View File

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

View File

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

View File

@ -2,5 +2,6 @@
"app-name": "GravityMon ",
"app-ver": "0.0.0",
"id": "7376ef",
"certs": true,
"mdns": "gravmon"
}

View File

@ -3,5 +3,6 @@
"device": false,
"config": false,
"calibration": false,
"certs": false,
"about": true
}