6 Commits
v0.6.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
27 changed files with 1133 additions and 75 deletions

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

BIN
data/certs.ar Normal file

Binary file not shown.

View File

@ -88,6 +88,10 @@
<div class="col-md-8 themed-grid-col bg-light">Device ID:</div> <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 class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div>
</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"> <hr class="my-4">
</div> </div>
@ -103,6 +107,11 @@
$("#app-ver").text(cfg["app-ver"] + " (html 0.6.0)"); $("#app-ver").text(cfg["app-ver"] + " (html 0.6.0)");
$("#mdns").text(cfg["mdns"]); $("#mdns").text(cfg["mdns"]);
$("#id").text(cfg["id"]); $("#id").text(cfg["id"]);
if( cfg["certs"] )
$("#certs").text("CA Store installed.");
else
$("#certs").text("CA Store NOT installed.");
}) })
.fail(function () { .fail(function () {
showError('Unable to get data from the device.'); 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.6.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-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 class="col-md-6 themed-grid-col bg-light" id="about">Checking...</div>
</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"> <div class="row mb-3">
<form action="/api/upload" method="post" enctype="multipart/form-data"> <form action="/api/upload" method="post" enctype="multipart/form-data">
@ -137,6 +141,10 @@ function getUpload() {
else else
$("#about").text("File is missing."); $("#about").text("File is missing.");
if( cfg["certs"] )
$("#certs").text("Completed.");
else
$("#certs").text("File is missing (optional).");
}) })
.fail(function () { .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>

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

@ -89,6 +89,7 @@ SOFTWARE.
#define CFG_PARAM_BATTERY "battery" #define CFG_PARAM_BATTERY "battery"
#define CFG_PARAM_SLEEP_MODE "sleep-mode" #define CFG_PARAM_SLEEP_MODE "sleep-mode"
#define CFG_PARAM_RSSI "rssi" #define CFG_PARAM_RSSI "rssi"
#define CFG_PARAM_CERTS "certs"
// Used for holding sensordata or sensoroffsets // Used for holding sensordata or sensoroffsets
struct RawGyroData { struct RawGyroData {
@ -179,6 +180,7 @@ class Config {
saveNeeded = true; saveNeeded = true;
} }
bool isOtaActive() { return otaURL.length() ? true : false; } bool isOtaActive() { return otaURL.length() ? true : false; }
bool isOtaSecure() { return otaURL.startsWith("https://"); }
const char* getWifiSSID() { return wifiSSID.c_str(); } const char* getWifiSSID() { return wifiSSID.c_str(); }
void setWifiSSID(String s) { void setWifiSSID(String s) {
@ -208,12 +210,14 @@ class Config {
saveNeeded = true; saveNeeded = true;
} }
bool isHttpActive() { return httpPushUrl.length() ? true : false; } bool isHttpActive() { return httpPushUrl.length() ? true : false; }
bool isHttpSecure() { return httpPushUrl.startsWith("https://"); }
const char* getHttpPushUrl2() { return httpPushUrl2.c_str(); } const char* getHttpPushUrl2() { return httpPushUrl2.c_str(); }
void setHttpPushUrl2(String s) { void setHttpPushUrl2(String s) {
httpPushUrl2 = s; httpPushUrl2 = s;
saveNeeded = true; saveNeeded = true;
} }
bool isHttpActive2() { return httpPushUrl2.length() ? true : false; } bool isHttpActive2() { return httpPushUrl2.length() ? true : false; }
bool isHttpSecure2() { return httpPushUrl2.startsWith("https://"); }
// InfluxDB2 // InfluxDB2
const char* getInfluxDb2PushUrl() { return influxDb2Url.c_str(); } const char* getInfluxDb2PushUrl() { return influxDb2Url.c_str(); }
@ -240,6 +244,7 @@ class Config {
// MQTT // MQTT
bool isMqttActive() { return mqttUrl.length() ? true : false; } bool isMqttActive() { return mqttUrl.length() ? true : false; }
bool isMqttSecure() { return mqttUrl.endsWith(":8883"); }
const char* getMqttUrl() { return mqttUrl.c_str(); } const char* getMqttUrl() { return mqttUrl.c_str(); }
void setMqttUrl(String s) { void setMqttUrl(String s) {
mqttUrl = s; mqttUrl = s;

View File

@ -180,6 +180,16 @@ void setup() {
break; 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_PERF_STOP("main-setup");
Log.notice(F("Main: Setup completed." CR)); Log.notice(F("Main: Setup completed." CR));
stableGyroMillis = millis(); // Dont include time for wifi connection stableGyroMillis = millis(); // Dont include time for wifi connection

View File

@ -21,11 +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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include <pushtarget.hpp>
#include <MQTT.h> #include <MQTT.h>
#include <config.hpp> #include <config.hpp>
#include <gyro.hpp> #include <gyro.hpp>
#include <pushtarget.hpp> #include <wifi.hpp>
PushTarget myPushTarget; PushTarget myPushTarget;
@ -243,10 +243,24 @@ void PushTarget::sendHttp(String serverPath, float angle, float gravity,
createIspindleFormat(doc, angle, gravity, corrGravity, temp, runTime); createIspindleFormat(doc, angle, gravity, corrGravity, temp, runTime);
WiFiClient client; WiFiClient client;
WiFiClientSecure clientSecure;
HTTPClient http; HTTPClient http;
// Your Domain name with URL path or IP address with path if (serverPath.startsWith("https://")) {
http.begin(client, serverPath); /*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; String json;
serializeJson(doc, json); serializeJson(doc, json);
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
@ -283,9 +297,26 @@ void PushTarget::sendMqtt(float angle, float gravity, float corrGravity,
createIspindleFormat(doc, angle, gravity, corrGravity, temp, runTime); createIspindleFormat(doc, angle, gravity, corrGravity, temp, runTime);
WiFiClient client; WiFiClient client;
MQTTClient mqtt(512); // Maximum message size 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.begin(myConfig.getMqttUrl(), client);
mqtt.connect(myConfig.getMDNS(), myConfig.getMqttUser(), mqtt.connect(myConfig.getMDNS(), myConfig.getMqttUser(),
myConfig.getMqttPass()); myConfig.getMqttPass());

View File

@ -32,12 +32,9 @@ INCBIN(DeviceHtm, "data/device.min.htm");
INCBIN(ConfigHtm, "data/config.min.htm"); INCBIN(ConfigHtm, "data/config.min.htm");
INCBIN(CalibrationHtm, "data/calibration.min.htm"); INCBIN(CalibrationHtm, "data/calibration.min.htm");
INCBIN(AboutHtm, "data/about.min.htm"); INCBIN(AboutHtm, "data/about.min.htm");
#else
// Minium web interface for uploading htm files
INCBIN(UploadHtm, "data/upload.min.htm");
#endif #endif
// Minium web interface for uploading htm files, also used to upload certificate store.
INCBIN(UploadHtm, "data/upload.min.htm");
// EOF // EOF

View File

@ -49,6 +49,7 @@ void WebServer::webHandleDevice() {
doc[CFG_PARAM_APP_NAME] = CFG_APPNAME; doc[CFG_PARAM_APP_NAME] = CFG_APPNAME;
doc[CFG_PARAM_APP_VER] = CFG_APPVER; doc[CFG_PARAM_APP_VER] = CFG_APPVER;
doc[CFG_PARAM_MDNS] = myConfig.getMDNS(); doc[CFG_PARAM_MDNS] = myConfig.getMDNS();
doc[CFG_PARAM_CERTS] = checkHtmlFile(CA_CERTS);
#if LOG_LEVEL == 6 #if LOG_LEVEL == 6
serializeJson(doc, Serial); serializeJson(doc, Serial);
Serial.print(CR); Serial.print(CR);
@ -108,6 +109,7 @@ void WebServer::webHandleUpload() {
doc["config"] = myWebServer.checkHtmlFile(WebServer::HTML_CONFIG); doc["config"] = myWebServer.checkHtmlFile(WebServer::HTML_CONFIG);
doc["calibration"] = myWebServer.checkHtmlFile(WebServer::HTML_CALIBRATION); doc["calibration"] = myWebServer.checkHtmlFile(WebServer::HTML_CALIBRATION);
doc["about"] = myWebServer.checkHtmlFile(WebServer::HTML_ABOUT); doc["about"] = myWebServer.checkHtmlFile(WebServer::HTML_ABOUT);
doc["certs"] = myWebServer.checkHtmlFile(WebServer::CA_CERTS);
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
serializeJson(doc, Serial); serializeJson(doc, Serial);
@ -134,7 +136,8 @@ void WebServer::webHandleUploadFile() {
f.equalsIgnoreCase("device.min.htm") || f.equalsIgnoreCase("device.min.htm") ||
f.equalsIgnoreCase("calibration.min.htm") || f.equalsIgnoreCase("calibration.min.htm") ||
f.equalsIgnoreCase("config.min.htm") || f.equalsIgnoreCase("config.min.htm") ||
f.equalsIgnoreCase("about.min.htm")) { f.equalsIgnoreCase("about.min.htm") ||
f.equalsIgnoreCase("certs.ar")) {
validFilename = true; validFilename = true;
} }
@ -587,6 +590,8 @@ const char* WebServer::getHtmlFileName(HtmlFile item) {
return "calibration.min.htm"; return "calibration.min.htm";
case HTML_ABOUT: case HTML_ABOUT:
return "about.min.htm"; return "about.min.htm";
case CA_CERTS:
return "certs.ar";
} }
return ""; return "";
@ -636,6 +641,7 @@ bool WebServer::setupWebServer() {
server->on("/calibration.htm", server->on("/calibration.htm",
std::bind(&WebServer::webReturnCalibrationHtm, this)); std::bind(&WebServer::webReturnCalibrationHtm, this));
server->on("/about.htm", std::bind(&WebServer::webReturnAboutHtm, this)); server->on("/about.htm", std::bind(&WebServer::webReturnAboutHtm, this));
server->on("/upload.htm", std::bind(&WebServer::webReturnUploadHtm, this));
#else #else
// Show files in the filessytem at startup // Show files in the filessytem at startup

View File

@ -37,9 +37,8 @@ INCBIN_EXTERN(DeviceHtm);
INCBIN_EXTERN(ConfigHtm); INCBIN_EXTERN(ConfigHtm);
INCBIN_EXTERN(CalibrationHtm); INCBIN_EXTERN(CalibrationHtm);
INCBIN_EXTERN(AboutHtm); INCBIN_EXTERN(AboutHtm);
#else
INCBIN_EXTERN(UploadHtm);
#endif #endif
INCBIN_EXTERN(UploadHtm);
// classes // classes
class WebServer { class WebServer {
@ -86,12 +85,11 @@ class WebServer {
void webReturnAboutHtm() { void webReturnAboutHtm() {
server->send_P(200, "text/html", (const char*)gAboutHtmData, gAboutHtmSize); server->send_P(200, "text/html", (const char*)gAboutHtmData, gAboutHtmSize);
} }
#else #endif
void webReturnUploadHtm() { void webReturnUploadHtm() {
server->send_P(200, "text/html", (const char*)gUploadHtmData, server->send_P(200, "text/html", (const char*)gUploadHtmData,
gUploadHtmSize); gUploadHtmSize);
} }
#endif
public: public:
enum HtmlFile { enum HtmlFile {
@ -99,7 +97,8 @@ class WebServer {
HTML_DEVICE = 1, HTML_DEVICE = 1,
HTML_CONFIG = 2, HTML_CONFIG = 2,
HTML_ABOUT = 3, HTML_ABOUT = 3,
HTML_CALIBRATION = 4 HTML_CALIBRATION = 4,
CA_CERTS = 5
}; };
bool setupWebServer(); bool setupWebServer();

View File

@ -33,6 +33,7 @@ SOFTWARE.
#include <helper.hpp> #include <helper.hpp>
#include <tempsensor.hpp> #include <tempsensor.hpp>
#include <wifi.hpp> #include <wifi.hpp>
#warning "Implement SSL for OTA"
// Settings for DRD // Settings for DRD
#define ESP_DRD_USE_LITTLEFS true #define ESP_DRD_USE_LITTLEFS true
@ -70,6 +71,31 @@ const char *userPWD = USER_SSID_PWD;
const int PIN_LED = 2; const int PIN_LED = 2;
//
// 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 // Constructor
// //

View File

@ -26,18 +26,23 @@ SOFTWARE.
// Include // Include
#include <Arduino.h> #include <Arduino.h>
#include <CertStoreBearSSL.h>
// classes // classes
class WifiConnection { class WifiConnection {
private: private:
// SSL
BearSSL::CertStore _certStore;
int _certCount = 0;
// WIFI // WIFI
void connectAsync();
bool waitForConnection(int maxTime = 20);
// OTA // OTA
bool newFirmware = false; bool newFirmware = false;
bool parseFirmwareVersionString(int (&num)[3], const char *version); bool parseFirmwareVersionString(int (&num)[3], const char *version);
void downloadFile(const char *fname); void downloadFile(const char *fname);
void connectAsync();
bool waitForConnection(int maxTime = 20);
public: public:
// WIFI // WIFI
@ -53,6 +58,12 @@ class WifiConnection {
void startPortal(); void startPortal();
void loop(); void loop();
// SSL
void initCertstore();
BearSSL::CertStore* getCertStore() { return &_certStore; }
int getCertCount() { return _certCount; }
void initNTP();
// OTA // OTA
bool updateFirmware(); bool updateFirmware();
bool checkFirmwareVersion(); bool checkFirmwareVersion();

View File

@ -1,13 +1,18 @@
Backlog of changes Backlog of changes
################## ##################
This is a list of potential ideas to implemnt in the software.
Documentation Documentation
------------- -------------
- Write contribution instructions - Write contribution instructions
- Example project for creating integrations and instructions
Code Code
------------- ----
- Support for plato - Support for plato
- Use pre-commit for validating check-in - Use pre-commit for validating check-in
- Show indicated battery life based on interval (check if its feasable)
- Use brewflasher for flashing

View File

@ -99,19 +99,19 @@ Push Settings
* **HTTP URL 1:** * **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:** * **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:** * **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:** * **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:** * **Influx DB v2 Organisation:**
@ -127,7 +127,7 @@ Push Settings
* **MQTT server:** * **MQTT server:**
IP or name of server to send data to. IP or name of server to send data to. Format used :ref:`data-formats-ispindle`
* **MQTT Topic:** * **MQTT Topic:**
@ -151,7 +151,8 @@ Gravity Settings
* **Gravity formula:** * **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:** * **Temperature correct gravity:**
@ -253,6 +254,10 @@ Other parameters are the same as in the configuration guide.
"influxdb2-org": "Qwerty", "influxdb2-org": "Qwerty",
"influxdb2-bucket": "Qwerty", "influxdb2-bucket": "Qwerty",
"influxdb2-auth": "Qwerty", "influxdb2-auth": "Qwerty",
"mqtt-push": "192.168.1.50",
"mqtt-topic": "Qwerty",
"mqtt-user": "Qwerty",
"mqtt-pass": "Qwerty",
"sleep-interval": 30, "sleep-interval": 30,
"voltage-factor": 1.59, "voltage-factor": 1.59,
"gravity-formula": "0.0*tilt^3+0.0*tilt^2+0.0017978*tilt+0.9436", "gravity-formula": "0.0*tilt^3+0.0*tilt^2+0.0017978*tilt+0.9436",
@ -373,6 +378,10 @@ Used to update push settings via an HTTP POST command. Payload is in JSON format
"influxdb2-org": "Qwerty", "influxdb2-org": "Qwerty",
"influxdb2-bucket": "Qwerty", "influxdb2-bucket": "Qwerty",
"influxdb2-auth": "Qwerty" "influxdb2-auth": "Qwerty"
"mqtt-push": "192.168.1.50",
"mqtt-topic": "Qwerty",
"mqtt-user": "Qwerty",
"mqtt-pass": "Qwerty",
} }
@ -480,7 +489,11 @@ present or the API call will fail.
"influxdb2-push": "", "influxdb2-push": "",
"influxdb2-org": "", "influxdb2-org": "",
"influxdb2-bucket": "", "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 ) set_config( url, json )
@ -521,6 +534,8 @@ present or the API call will fail.
Data Formats Data Formats
############ ############
.. _data-formats-ispindle:
iSpindle format iSpindle format
=============== ===============
@ -547,6 +562,8 @@ This is the format used for standard http posts.
} }
.. _data-formats-brewfather:
Brewfather format Brewfather format
================= =================
@ -564,6 +581,8 @@ This is the format for Brewfather
} }
.. _data-formats-influxdb2:
Influx DB v2 Influx DB v2
============ ============

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

View File

@ -7,7 +7,7 @@ Welcome to GravityMon's documentation!
###################################### ######################################
.. note:: .. note::
This documentation reflects **v0.6**. Last updated 2022-01-13 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 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 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>`_ be found here; `GravityMon on Github <https://github.com/mp-se/gravitymon>`_
.. note:: .. note::
This software is in the early stages even though its more than one year old so if you find issues, please 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. open a ticket on github.
@ -30,29 +33,36 @@ be found here; `GravityMon on Github <https://github.com/mp-se/gravitymon>`_
I dont take responsibility for any errors that can cause problems with the use. I have tested v0.4 on 5+ brews I dont take responsibility for any errors that can cause problems with the use. I have tested v0.4 on 5+ brews
over the last 6 months without any issues. over the last 6 months without any issues.
The main differences: The main differences:
--------------------- ---------------------
* Operates in two modes ``gravity monitoring`` and ``configuration mode`` * Operates in two modes gravity monitoring and configuration mode (simplify calibration)
* Send data to multiple endpoints when pushing data. * Modern web based UI for configuration (in config mode)
* Automatic temperature adjustment of gravity reading * REST API
* OTA support from local webserver * Send data to multiple endpoints when pushing data (2xhttp, brewfather, influxdb v2, mqtt supported)
* Build in function to create gravity formulas * Automatic temperature adjustment of gravity reading
* OTA support from local webserver
There are also a experimental features such as: * 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) * 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` 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 Credits to
---------- ----------
Ideas to some of these functions have been picked up from disucssions in the iSpindle forums. This software uses 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: 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. This library contains the basic code to interact with the gyro + many more chips.

View File

@ -1,36 +1,16 @@
Installation Installation
------------ ------------
Official esptool Brewflasher
================ ===========
The prefered option for flashing esp8266 device is via the official esptool. Documentation can be found The prefered option for flashing GravityMon is using BrewFlasher, its a tools that support many brewing related firmwares for ESP8266 and ESP32. This works
here; `esptool home page <https://docs.espressif.com/projects/esptool/en/latest/esp32/>`_ 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. .. image:: images/brewflasher.png
:width: 600
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
:alt: Serial output :alt: Serial output
Binaries 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. 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 In these versions all the html files are embedded in the binaries. The file system is currently only used for storing
the configuration file. 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 default (see compiling for details). This approach makes installation much easier and ensure that html files
and code is in sync. 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 Configuring WIFI
================ ================

View File

@ -3,10 +3,10 @@
Releases Releases
######## ########
v0.6.0 (work in progress) v0.6.0
------------------------- ------
This is features for the next release. Latest stable version.
* Changed the wifi manager and refactored wifi.cpp * Changed the wifi manager and refactored wifi.cpp
* LED is now turned on when Wifi Portal is open * LED is now turned on when Wifi Portal is open
@ -19,11 +19,11 @@ This is features for the next release.
* Added support for MQTT * Added support for MQTT
* Bug: MPU init sometimes caused crash during startup. * 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 v0.5.0
------ ------
Latest stable version.
* Added feature to calcuate formula on device * Added feature to calcuate formula on device
* Total rewrite of documentation * Total rewrite of documentation
* WIFI settings are now stored in config file * WIFI settings are now stored in config file

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>

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

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

View File

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