Compare commits
159 Commits
Author | SHA1 | Date | |
---|---|---|---|
4f28ea9975 | |||
c1eade04af | |||
2fd0c881f3 | |||
8113275402 | |||
f4401ea526 | |||
535367d943 | |||
8db58c76dc | |||
9441e4bc54 | |||
b7d2183564 | |||
18f7ba8911 | |||
af0d6f21ca | |||
e3fcd53a36 | |||
5149760d22 | |||
403719073b | |||
dd3a4e5742 | |||
2e27ec78d6 | |||
d5dae6a40d | |||
3b716e5499 | |||
2aebae59b3 | |||
eb4b975407 | |||
1f25e3ac50 | |||
f1a608d060 | |||
c33326e6a4 | |||
1afb5ed604 | |||
823d8ca39e | |||
5a11ab84db | |||
5378d4c700 | |||
c10abe2087 | |||
ec3ef06826 | |||
b9273b7b79 | |||
6b649070e1 | |||
291229255c | |||
2747484775 | |||
ecf0c08f47 | |||
eb82bf526d | |||
b45ef627a0 | |||
059865f271 | |||
a9deb588aa | |||
0de8971255 | |||
08a102159b | |||
8b13b4769c | |||
c3044f9878 | |||
0a4e7f37b2 | |||
69f4b8ec3d | |||
d4c88c1200 | |||
4bbfb77361 | |||
655235fc4e | |||
4b0e5b393a | |||
db590f8f5f | |||
4c805fe235 | |||
34a1d7d3c3 | |||
bcee0e21da | |||
9fc5ab147d | |||
b3a89a1fcc | |||
03b58eb112 | |||
63ddcfbb57 | |||
68d44a5846 | |||
45294f6b07 | |||
09d8226af1 | |||
6c94c6b204 | |||
9e437d1e0d | |||
a486f1be30 | |||
0536b14280 | |||
a6bff7287e | |||
414d7d51c2 | |||
640733f143 | |||
f6196e6dbf | |||
f857dab3c0 | |||
787c5c09bb | |||
afe3ec2412 | |||
980099a5e5 | |||
99d577d4a0 | |||
6db6e96d90 | |||
92df08baa2 | |||
5466550f68 | |||
94f703b087 | |||
57e078986b | |||
e445e7649c | |||
a5f7f0f8a4 | |||
7826e56d8a | |||
353d6d77e1 | |||
437873489e | |||
e34e73fae2 | |||
69a4c5607c | |||
d980fdae1a | |||
272e349375 | |||
34d46ef768 | |||
588ff2d1e0 | |||
5d1811d240 | |||
85e602d29b | |||
8ee882c522 | |||
51e7ee6867 | |||
70858ef841 | |||
83bd80ba28 | |||
d33576b2df | |||
5c9525c0c5 | |||
ad8704fc20 | |||
f1c1958f88 | |||
e5394113e4 | |||
6d4e713da8 | |||
36a858af2d | |||
77d2c15e39 | |||
9df072cc78 | |||
bedbda4662 | |||
76702dfc95 | |||
789eb32aa8 | |||
c0db4dd8da | |||
cb677e10ae | |||
b4c7566a08 | |||
70f391fa1a | |||
542beffe4c | |||
1a42bd332f | |||
c16108352c | |||
4b5df951de | |||
6bbb904427 | |||
66c6c44a38 | |||
e2fee1fb35 | |||
ae595ff50c | |||
50257e2805 | |||
c503ad88a9 | |||
5b7290c991 | |||
f7c43dad55 | |||
95654bac66 | |||
ffa7ac294d | |||
79a3274286 | |||
0c936cfb88 | |||
f366b78cb3 | |||
702eba515d | |||
a992e90bc3 | |||
8b4b89ba20 | |||
361d287a22 | |||
8e36453b13 | |||
ded06d15a1 | |||
67f60b817e | |||
6373923506 | |||
bf3a3de207 | |||
e6fd027d51 | |||
0cba58a0dd | |||
fd71a2d428 | |||
907e33ce2d | |||
18ba0b225f | |||
2a9472b453 | |||
0b23a0f69e | |||
9cba1db4e5 | |||
9d5d6a5a58 | |||
58fe408803 | |||
c2ae70bb1a | |||
7a3b048d80 | |||
ad3fbb7270 | |||
02df656343 | |||
f73f63fec3 | |||
bb09072520 | |||
5875ee83d8 | |||
6185d67d12 | |||
00f82f5c37 | |||
59ad285bb8 | |||
bc09d617fc | |||
a71f54b99f | |||
152ff89bf6 |
17
.github/stale.yaml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Number of days of inactivity before an issue becomes stale
|
||||||
|
daysUntilStale: 60
|
||||||
|
# Number of days of inactivity before a stale issue is closed
|
||||||
|
daysUntilClose: 14
|
||||||
|
# Issues with these labels will never be considered stale
|
||||||
|
exemptLabels:
|
||||||
|
- on-hold
|
||||||
|
- security
|
||||||
|
# Label to use when marking an issue as stale
|
||||||
|
staleLabel: wontfix
|
||||||
|
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||||
|
markComment: >
|
||||||
|
This issue has been automatically marked as stale because it has not had
|
||||||
|
recent activity. It will be closed if no further activity occurs. Thank you
|
||||||
|
for your contributions.
|
||||||
|
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||||
|
closeComment: false
|
8
.github/workflows/doc-build.yaml
vendored
@ -16,8 +16,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
docs-folder: "src_docs/"
|
docs-folder: "src_docs/"
|
||||||
pre-build-command: |
|
pre-build-command: |
|
||||||
pip install sphinx_rtd_theme
|
pip install --upgrade pip
|
||||||
pip install furo
|
pip install sphinx==4.3.2
|
||||||
|
pip install docutils==0.16
|
||||||
|
pip install pygments==2.11.1
|
||||||
|
pip install furo==2022.1.2
|
||||||
|
pip list
|
||||||
build-command: "sphinx-build -b html ./source ../docs"
|
build-command: "sphinx-build -b html ./source ../docs"
|
||||||
|
|
||||||
- name: Commit documentation changes
|
- name: Commit documentation changes
|
||||||
|
4
.github/workflows/pio-build-patch.yaml
vendored
@ -36,9 +36,7 @@ jobs:
|
|||||||
git config --global advice.detachedHead false
|
git config --global advice.detachedHead false
|
||||||
|
|
||||||
- name: Run PlatformIO
|
- name: Run PlatformIO
|
||||||
#run: pio run -e gravity-release -e gravity-perf -e gravity-debug
|
run: pio run -e gravity-release
|
||||||
run: pio run -e gravity-release -e gravity-perf
|
|
||||||
#run: pio run -e gravity-release
|
|
||||||
|
|
||||||
- uses: EndBug/add-and-commit@v7 # You can change this to use a specific version. https://github.com/marketplace/actions/add-commit
|
- uses: EndBug/add-and-commit@v7 # You can change this to use a specific version. https://github.com/marketplace/actions/add-commit
|
||||||
with:
|
with:
|
||||||
|
19
.github/workflows/pre-commit.yaml
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
name: pre-commit
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- dev
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
pre-commit:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-python@v2
|
||||||
|
- name: clang format support
|
||||||
|
run: |
|
||||||
|
sudo apt install clang-format cppcheck
|
||||||
|
- uses: pre-commit/action@v2.0.3
|
@ -4,7 +4,10 @@ repos:
|
|||||||
hooks:
|
hooks:
|
||||||
- id: clang-format
|
- id: clang-format
|
||||||
files: ^src/
|
files: ^src/
|
||||||
|
exclude: 'lib/'
|
||||||
- id: cpplint
|
- id: cpplint
|
||||||
files: ^src/
|
files: ^src/
|
||||||
|
exclude: 'lib/'
|
||||||
- id: cppcheck
|
- id: cppcheck
|
||||||
files: ^src/
|
files: ^src/
|
||||||
|
exclude: 'lib/'
|
||||||
|
34
README.md
@ -21,18 +21,24 @@ Note! If Brewflasher being flagged as malware by your antivirus software, try th
|
|||||||
The main differences:
|
The main differences:
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
* Modern web based user interface for configuration when connected to WIFI
|
* Operates in two modes gravity monitoring and configuration mode
|
||||||
* Efficient software, long lifespan (+45 days with 5min update frequencey)
|
* Gravity mode is comparable to how the iSpindle works when collectintg data
|
||||||
* Send data to multiple endpoints (http-post, http-get, influxdb v2, mqtt)
|
* Configuration mode has a modern HTML5 based web UI. No need to start the access point to change settings
|
||||||
* Instructions for service such as; Brewfather, Fermentrack, Ubidots, Home Assistant, Brewers Friend, Brewspy, Thingspeak, Blynk.
|
* Offloading some of the functionallity to run in the web browser, this allows for more advanced features.
|
||||||
* SSL support for all remote endpoints
|
* REST API to enable scripted configuration
|
||||||
* ESP32 support with Bluetooth push
|
* Send data to multiple endpoints and services at once (2xHTTP POST, HTTP GET, MQTT, INFLUXDB2)
|
||||||
* Customize data format to be pushed
|
* Directly test all endpoints from user interface with feedback to simplify troubleshooting
|
||||||
* Automatic temperature adjustment of gravity when enabled
|
* Complete format customization for all endpoints using templates (dont really need to change the software to support new services)
|
||||||
* Use the temperature sensor in gyro instead of DS18B20
|
* Setup guides for how to send data to many popular services. Currently 10+ are documented
|
||||||
* Built in function to create gravity formulas, no need for additional software, just enter tilt/gravity.
|
* Automatic temperature adjustment of gravity (just tick a checkbox)
|
||||||
* Visual graph showing how gravity formula will be interpreted
|
* OTA support from webserver
|
||||||
* OTA support or firmware upload via web interface
|
* Firmware update via web interface
|
||||||
* REST API for scripting
|
* Built in function to create gravity formulas, no need for additional software, just enter tilt/gravity and let GravityMon creates a formula
|
||||||
|
* Visual graph showing how formula will be interpreted based on entered values
|
||||||
|
* Using the temperature sensor in gyro instead of DS18B20 (faster)
|
||||||
|
* SSL support in all endpoints (no certificate validation due to limitations on esp8266).
|
||||||
|
* Built in performance measurements (used to optimise code)
|
||||||
|
* Storage mode when placed on cap (indefinite sleep)
|
||||||
|
* Customize various hardware parameters to opimize device functionallity.
|
||||||
|
*
|
||||||
No code has been reused from the iSpindle project.
|
No code has been reused from the iSpindle project.
|
||||||
|
@ -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://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"><style>.row-margin-10{margin-top:1em}</style></head><body class="py-4"><!-- START MENU --><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script><nav class="navbar navbar-expand-lg navbar-dark bg-primary"><div class="container"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbarNav"><ul class="navbar-nav"><li class="nav-item"><a class="nav-link" href="/index.htm">Home</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 active"><a class="nav-link" href="#"><b>About</b></a></li></ul></div></div></nav><!-- START BODY --><div class="container row-margin-10"><div class="accordion row-margin-10" id="accordion"><div class="accordion-item"><h2 class="accordion-header" id="headingAbout"><button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseAbout" aria-expanded="true" aria-controls="collapseAbout"><b>About</b></button></h2><div id="collapseAbout" class="accordion-collapse collapse show" aria-labelledby="headingAbout" data-bs-parent="#accordion"><div class="accordion-body"><div class="row h3 col-sm-8">Beer Gravity Monitor</div><div class="row col-sm-8 mb-3">This is a piece of software for the iSpindle hardware and will work in a similar way. No part of this software is copied from the iSpindle project.</div><div class="row h3 col-sm-8 mb-3">MIT License</div><div class="row col-sm-8 mb-3">Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 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.</div><div class="row h3 col-sm-8 mb-3">Credits to</div><div class="row col-sm-8 mb-3">This software uses the following libraries and without these this software would have been much more difficult to acheive:<br><br><ul><li>https://github.com/jrowberg/i2cdevlib</li><li>https://github.com/codeplea/tinyexpr</li><li>https://github.com/graphitemaster/incbin</li><li>https://github.com/khoih-prog/ESP_DoubleResetDetector</li><li>https://github.com/khoih-prog/ESP_WiFiManager</li><li>https://github.com/thijse/Arduino-Log</li><li>https://github.com/bblanchon/ArduinoJson</li><li>https://github.com/PaulStoffregen/OneWire</li><li>https://github.com/milesburton/Arduino-Temperature-Control-Library</li><li>https://github.com/Rotario</li><li>https://github.com/256dpi/arduino-mqtt</li><li>https://graphjs.com</li><li>https://getbootstrap.com</li><li>https://github.com/lorol/LITTLEFS</li><li>https://github.com/h2zero/NimBLE-Arduino</li><li>https://github.com/spouliot/tilt-sim</li></ul></div></div></div></div></div></div><!-- START FOOTER --><div class="container themed-container bg-primary text-light row-margin-10">(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://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"><style>.row-margin-10{margin-top:1em}</style></head><body class="py-4"><!-- START MENU --><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script><nav class="navbar navbar-expand-lg navbar-dark bg-primary"><div class="container"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbarNav"><ul class="navbar-nav"><li class="nav-item"><a class="nav-link" href="/index.htm">Home</a></li><li class="nav-item dropdown"><a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">Configuration</a><ul class="dropdown-menu"><li><a class="dropdown-item" href="/config.htm">Configuration</a></li><li><a class="dropdown-item" href="/format.htm">Format editor</a></li><li><a class="dropdown-item" href="/test.htm">Test push</a></li><li><a class="dropdown-item" href="/firmware.htm">Upload firmware</a></li></ul></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item"><a class="nav-link active" href="#">About</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START BODY --><div class="container row-margin-10"><div class="accordion row-margin-10" id="accordion"><div class="accordion-item"><h2 class="accordion-header" id="headingAbout"><button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseAbout" aria-expanded="true" aria-controls="collapseAbout"><b>About</b></button></h2><div id="collapseAbout" class="accordion-collapse collapse show" aria-labelledby="headingAbout" data-bs-parent="#accordion"><div class="accordion-body"><div class="row h3 col-sm-8">Beer Gravity Monitor</div><div class="row col-sm-8 mb-3">This is a piece of software for the iSpindle hardware and will work in a similar way. No part of this software is copied from the iSpindle project.</div><div class="row h3 col-sm-8 mb-3">MIT License</div><div class="row col-sm-8 mb-3">Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 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.</div><div class="row h3 col-sm-8 mb-3">Credits to</div><div class="row col-sm-8 mb-3">This software uses the following libraries and without these this software would have been much more difficult to acheive:<br><br><ul><li>https://github.com/jrowberg/i2cdevlib</li><li>https://github.com/codeplea/tinyexpr</li><li>https://github.com/graphitemaster/incbin</li><li>https://github.com/khoih-prog/ESP_DoubleResetDetector</li><li>https://github.com/khoih-prog/ESP_WiFiManager</li><li>https://github.com/thijse/Arduino-Log</li><li>https://github.com/bblanchon/ArduinoJson</li><li>https://github.com/PaulStoffregen/OneWire</li><li>https://github.com/milesburton/Arduino-Temperature-Control-Library</li><li>https://github.com/Rotario</li><li>https://github.com/256dpi/arduino-mqtt</li><li>https://graphjs.com</li><li>https://getbootstrap.com</li><li>https://github.com/lorol/LITTLEFS</li><li>https://github.com/h2zero/NimBLE-Arduino</li><li>https://github.com/spouliot/tilt-sim</li></ul></div></div></div></div></div></div><!-- START FOOTER --><div class="container themed-container bg-primary text-light row-margin-10">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
BIN
bin/firmware.bin
@ -1 +1 @@
|
|||||||
{ "project":"gravmon", "version":"1.0.0", "html": [ ] }
|
{ "project":"gravmon", "version":"1.1.0", "html": [ ] }
|
@ -28,20 +28,29 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/index.htm">Home</a>
|
<a class="nav-link" href="/index.htm">Home</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link" href="/config.htm">Configuration</a>
|
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
Configuration
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a class="dropdown-item" href="/config.htm">Configuration</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/format.htm">Format editor</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/test.htm">Test push</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/firmware.htm">Upload firmware</a></li>
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/calibration.htm">Calibration</a>
|
<a class="nav-link" href="/calibration.htm">Calibration</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item active">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="#"><b>About</b></a>
|
<a class="nav-link active" href="#">About</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="spinner-border text-light" id="spinner" role="status"></div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- START BODY -->
|
<!-- START BODY -->
|
||||||
|
|
||||||
<div class="container row-margin-10">
|
<div class="container row-margin-10">
|
||||||
|
@ -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://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"><style>.row-margin-10{margin-top:1em}</style></head><body class="py-4"><!-- START MENU --><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script><nav class="navbar navbar-expand-lg navbar-dark bg-primary"><div class="container"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbarNav"><ul class="navbar-nav"><li class="nav-item"><a class="nav-link" href="/index.htm">Home</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 active"><a class="nav-link" href="#"><b>About</b></a></li></ul></div></div></nav><!-- START BODY --><div class="container row-margin-10"><div class="accordion row-margin-10" id="accordion"><div class="accordion-item"><h2 class="accordion-header" id="headingAbout"><button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseAbout" aria-expanded="true" aria-controls="collapseAbout"><b>About</b></button></h2><div id="collapseAbout" class="accordion-collapse collapse show" aria-labelledby="headingAbout" data-bs-parent="#accordion"><div class="accordion-body"><div class="row h3 col-sm-8">Beer Gravity Monitor</div><div class="row col-sm-8 mb-3">This is a piece of software for the iSpindle hardware and will work in a similar way. No part of this software is copied from the iSpindle project.</div><div class="row h3 col-sm-8 mb-3">MIT License</div><div class="row col-sm-8 mb-3">Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 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.</div><div class="row h3 col-sm-8 mb-3">Credits to</div><div class="row col-sm-8 mb-3">This software uses the following libraries and without these this software would have been much more difficult to acheive:<br><br><ul><li>https://github.com/jrowberg/i2cdevlib</li><li>https://github.com/codeplea/tinyexpr</li><li>https://github.com/graphitemaster/incbin</li><li>https://github.com/khoih-prog/ESP_DoubleResetDetector</li><li>https://github.com/khoih-prog/ESP_WiFiManager</li><li>https://github.com/thijse/Arduino-Log</li><li>https://github.com/bblanchon/ArduinoJson</li><li>https://github.com/PaulStoffregen/OneWire</li><li>https://github.com/milesburton/Arduino-Temperature-Control-Library</li><li>https://github.com/Rotario</li><li>https://github.com/256dpi/arduino-mqtt</li><li>https://graphjs.com</li><li>https://getbootstrap.com</li><li>https://github.com/lorol/LITTLEFS</li><li>https://github.com/h2zero/NimBLE-Arduino</li><li>https://github.com/spouliot/tilt-sim</li></ul></div></div></div></div></div></div><!-- START FOOTER --><div class="container themed-container bg-primary text-light row-margin-10">(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://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"><style>.row-margin-10{margin-top:1em}</style></head><body class="py-4"><!-- START MENU --><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script><nav class="navbar navbar-expand-lg navbar-dark bg-primary"><div class="container"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbarNav"><ul class="navbar-nav"><li class="nav-item"><a class="nav-link" href="/index.htm">Home</a></li><li class="nav-item dropdown"><a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">Configuration</a><ul class="dropdown-menu"><li><a class="dropdown-item" href="/config.htm">Configuration</a></li><li><a class="dropdown-item" href="/format.htm">Format editor</a></li><li><a class="dropdown-item" href="/test.htm">Test push</a></li><li><a class="dropdown-item" href="/firmware.htm">Upload firmware</a></li></ul></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item"><a class="nav-link active" href="#">About</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START BODY --><div class="container row-margin-10"><div class="accordion row-margin-10" id="accordion"><div class="accordion-item"><h2 class="accordion-header" id="headingAbout"><button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseAbout" aria-expanded="true" aria-controls="collapseAbout"><b>About</b></button></h2><div id="collapseAbout" class="accordion-collapse collapse show" aria-labelledby="headingAbout" data-bs-parent="#accordion"><div class="accordion-body"><div class="row h3 col-sm-8">Beer Gravity Monitor</div><div class="row col-sm-8 mb-3">This is a piece of software for the iSpindle hardware and will work in a similar way. No part of this software is copied from the iSpindle project.</div><div class="row h3 col-sm-8 mb-3">MIT License</div><div class="row col-sm-8 mb-3">Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 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.</div><div class="row h3 col-sm-8 mb-3">Credits to</div><div class="row col-sm-8 mb-3">This software uses the following libraries and without these this software would have been much more difficult to acheive:<br><br><ul><li>https://github.com/jrowberg/i2cdevlib</li><li>https://github.com/codeplea/tinyexpr</li><li>https://github.com/graphitemaster/incbin</li><li>https://github.com/khoih-prog/ESP_DoubleResetDetector</li><li>https://github.com/khoih-prog/ESP_WiFiManager</li><li>https://github.com/thijse/Arduino-Log</li><li>https://github.com/bblanchon/ArduinoJson</li><li>https://github.com/PaulStoffregen/OneWire</li><li>https://github.com/milesburton/Arduino-Temperature-Control-Library</li><li>https://github.com/Rotario</li><li>https://github.com/256dpi/arduino-mqtt</li><li>https://graphjs.com</li><li>https://getbootstrap.com</li><li>https://github.com/lorol/LITTLEFS</li><li>https://github.com/h2zero/NimBLE-Arduino</li><li>https://github.com/spouliot/tilt-sim</li></ul></div></div></div></div></div></div><!-- START FOOTER --><div class="container themed-container bg-primary text-light row-margin-10">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
@ -16,8 +16,6 @@
|
|||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
|
||||||
<!-- START MENU -->
|
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
|
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
|
||||||
@ -29,11 +27,19 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/index.htm">Home</a>
|
<a class="nav-link" href="/index.htm">Home</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link" href="/config.htm">Configuration</a>
|
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
Configuration
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a class="dropdown-item" href="/config.htm">Configuration</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/format.htm">Format editor</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/test.htm">Test push</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/firmware.htm">Upload firmware</a></li>
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item active">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="#"><b>Calibration</b></a>
|
<a class="nav-link active" href="#">Calibration</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/about.htm">About</a>
|
<a class="nav-link" href="/about.htm">About</a>
|
||||||
|
@ -28,10 +28,18 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/index.htm">Home</a>
|
<a class="nav-link" href="/index.htm">Home</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link active" href="#"><b>Configuration</b></a>
|
<a class="nav-link dropdown-toggle active" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
Configuration
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a class="dropdown-item" href="#">Configuration</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/format.htm">Format editor</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/test.htm">Test push</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/firmware.htm">Upload firmware</a></li>
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/calibration.htm">Calibration</a>
|
<a class="nav-link" href="/calibration.htm">Calibration</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
@ -107,7 +115,7 @@
|
|||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="mdns" class="col-sm-2 col-form-label">Device name</label>
|
<label for="mdns" class="col-sm-2 col-form-label">Device name</label>
|
||||||
<div class="col-sm-3">
|
<div class="col-sm-3">
|
||||||
<input type="text" maxlength="12" class="form-control" name="mdns" id="mdns" placeholder="gravmon" data-bs-toggle="tooltip" title="Name of the device. Will be used for identifying the device when pushing data and on the local network.">
|
<input type="text" maxlength="63" class="form-control" name="mdns" id="mdns" placeholder="gravmon" data-bs-toggle="tooltip" title="Name of the device. Will be used for identifying the device when pushing data and on the local network.">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -390,11 +398,18 @@
|
|||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="voltage-factor" class="col-sm-2 col-form-label">Voltage factor</label>
|
<label for="voltage-factor" class="col-sm-2 col-form-label">Voltage factor</label>
|
||||||
<div class="col-sm-2">
|
<div class="col-sm-2">
|
||||||
<input type="number" step=".01" class="form-control" name="voltage-factor" id="voltage-factor" placeholder="1.59" data-bs-toggle="tooltip" title="Factor used to calculate the battery voltage. When running on battery, the voltage should be less than 4.15V">
|
<input type="number" step=".01" class="form-control" name="voltage-factor" id="voltage-factor" placeholder="1.59" data-bs-toggle="tooltip" title="Factor used to calculate the battery voltage. Can vary depending on the R2 value">
|
||||||
</div>
|
</div>
|
||||||
<label for="voltage-factor" class="col-sm-3 col-form-label" id="battery">Loading...</label>
|
<label for="voltage-factor" class="col-sm-3 col-form-label" id="battery">Loading...</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="voltage-config" class="col-sm-2 col-form-label">Config voltage</label>
|
||||||
|
<div class="col-sm-2">
|
||||||
|
<input type="number" step=".01" min="3.00" max="6.00" class="form-control" name="voltage-config" id="voltage-config" placeholder="4.16" data-bs-toggle="tooltip" title="Over this level the device will always go into configuration mode, some batteries might have a higher voltage when fully charged">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="temp-adjustment-value" class="col-sm-2 col-form-label">Temp Sensor Adj</label>
|
<label for="temp-adjustment-value" class="col-sm-2 col-form-label">Temp Sensor Adj</label>
|
||||||
<div class="col-sm-2">
|
<div class="col-sm-2">
|
||||||
@ -403,7 +418,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-sm-3 offset-sm-2">
|
<div class="col-sm-4 offset-sm-2">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" name="gyro-temp" id="gyro-temp" data-bs-toggle="tooltip" title="Use the temperature sensor in the gyro instead of DS18B20, require 300s update interval to be accurate">
|
<input class="form-check-input" type="checkbox" name="gyro-temp" id="gyro-temp" data-bs-toggle="tooltip" title="Use the temperature sensor in the gyro instead of DS18B20, require 300s update interval to be accurate">
|
||||||
<label class="form-check-label" for="gyro-temp">Use gyro temperature</label>
|
<label class="form-check-label" for="gyro-temp">Use gyro temperature</label>
|
||||||
@ -411,6 +426,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-sm-4 offset-sm-2">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" name="storage-sleep" id="storage-sleep" data-bs-toggle="tooltip" title="If enabled and the device is placed on its cap (less than 5 degress) it will go into sleep for 2000 minutes">
|
||||||
|
<label class="form-check-label" for="storage-sleep">Enable storage mode when placed on cap</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label class="col-sm-2 col-form-label" for="ble">Bluetooth tilt color:</label>
|
<label class="col-sm-2 col-form-label" for="ble">Bluetooth tilt color:</label>
|
||||||
<div class="col-sm-2">
|
<div class="col-sm-2">
|
||||||
@ -490,9 +514,9 @@
|
|||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="formula-max-deviation" class="col-sm-3 col-form-label">Formula max deviation (SG)</label>
|
<label for="formula-max-deviation" class="col-sm-3 col-form-label">Formula max deviation (SG)</label>
|
||||||
<div class="col-sm-3">
|
<div class="col-sm-3">
|
||||||
<input disabled type="number" step=".1" min="1" max="10" class="form-control" name="formula-max-deviation" id="formula-max-deviation" placeholder="1.6" checked data-bs-toggle="tooltip" title="When validating the derived formula this is the maximum accepted deviation for the supplied values">
|
<input disabled type="number" step=".1" min="1" max="10" class="form-control" name="formula-max-deviation" id="formula-max-deviation" placeholder="3" checked data-bs-toggle="tooltip" title="When validating the derived formula this is the maximum accepted deviation for the supplied values">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-5">(1 - 10) - default 1.6 SG</div>
|
<div class="col-sm-5">(1 - 10) - default 3 SG</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
@ -505,6 +529,14 @@
|
|||||||
<div class="col-sm-5" name="water-angle" id="water-angle"></div>
|
<div class="col-sm-5" name="water-angle" id="water-angle"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="formula-calibration-temp" class="col-sm-3 col-form-label">Gravity calibration temp</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input disabled type="number" step=".01" min="0" max="100" class="form-control" name="formula-calibration-temp" id="formula-calibration-temp" placeholder="20" checked data-bs-toggle="tooltip" title="Calibration temperature, used in temperatur correction formula, default 20C/68F">
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-5">(0 - 100) - default 20C/68F</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
@ -764,6 +796,7 @@
|
|||||||
$("#formula-max-deviation").prop("disabled", b);
|
$("#formula-max-deviation").prop("disabled", b);
|
||||||
$("#wifi-portal-timeout").prop("disabled", b);
|
$("#wifi-portal-timeout").prop("disabled", b);
|
||||||
$("#wifi-connect-timeout").prop("disabled", b);
|
$("#wifi-connect-timeout").prop("disabled", b);
|
||||||
|
$("#formula-calibration-temp").prop("disabled", b);
|
||||||
$("#int-http1").prop("disabled", b);
|
$("#int-http1").prop("disabled", b);
|
||||||
$("#int-http2").prop("disabled", b);
|
$("#int-http2").prop("disabled", b);
|
||||||
$("#int-http3").prop("disabled", b);
|
$("#int-http3").prop("disabled", b);
|
||||||
@ -790,14 +823,15 @@
|
|||||||
$("#wifi-connect-timeout").val(cfg["wifi-connect-timeout"]);
|
$("#wifi-connect-timeout").val(cfg["wifi-connect-timeout"]);
|
||||||
$("#tempsensor-resolution").val(cfg["tempsensor-resolution"]);
|
$("#tempsensor-resolution").val(cfg["tempsensor-resolution"]);
|
||||||
$("#ignore-low-angles").prop( "checked", cfg["ignore-low-angles"] );
|
$("#ignore-low-angles").prop( "checked", cfg["ignore-low-angles"] );
|
||||||
|
$("#formula-calibration-temp").val(cfg["formula-calibration-temp"]);
|
||||||
$("#int-http1").val(cfg["int-http1"]);
|
$("#int-http1").val(cfg["int-http1"]);
|
||||||
$("#int-http2").val(cfg["int-http2"]);
|
$("#int-http2").val(cfg["int-http2"]);
|
||||||
$("#int-http3").val(cfg["int-http3"]);
|
$("#int-http3").val(cfg["int-http3"]);
|
||||||
$("#int-influx").val(cfg["int-influx"]);
|
$("#int-influx").val(cfg["int-influx"]);
|
||||||
$("#int-mqtt").val(cfg["int-mqtt"]);
|
$("#int-mqtt").val(cfg["int-mqtt"]);
|
||||||
|
|
||||||
if ( cfg["gyro-read-count"] != 50 || cfg["gyro-moving-threashold"] != 500 || cfg["formula-max-deviation"] != 1.6 || cfg["wifi-portal-timeout"] != 120 || cfg["wifi-connect-timeout"] != 20 || cfg["tempsensor-resolution"] != 9 ||
|
if ( cfg["gyro-read-count"] != 50 || cfg["gyro-moving-threashold"] != 500 || cfg["formula-max-deviation"] != 3 || cfg["wifi-portal-timeout"] != 120 || cfg["wifi-connect-timeout"] != 20 || cfg["tempsensor-resolution"] != 9 ||
|
||||||
cfg["int-http1"] != 0 || cfg["int-http2"] != 0 || cfg["int-http3"] != 0 || cfg["int-influx"] != 0 || cfg["int-mqtt"] != 0 || cfg["ignore-low-angles"] != false ) {
|
cfg["int-http1"] != 0 || cfg["int-http2"] != 0 || cfg["int-http3"] != 0 || cfg["int-influx"] != 0 || cfg["int-mqtt"] != 0 || cfg["ignore-low-angles"] != false || (cfg["formula-calibration-temp"] != 20 && cfg["formula-calibration-temp"] != 68)) {
|
||||||
$("#adv-config").attr("checked", false );
|
$("#adv-config").attr("checked", false );
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -856,10 +890,12 @@
|
|||||||
$("#mqtt-pass").val(cfg["mqtt-pass"]);
|
$("#mqtt-pass").val(cfg["mqtt-pass"]);
|
||||||
$("#sleep-interval").val(cfg["sleep-interval"]);
|
$("#sleep-interval").val(cfg["sleep-interval"]);
|
||||||
$("#voltage-factor").val(cfg["voltage-factor"]);
|
$("#voltage-factor").val(cfg["voltage-factor"]);
|
||||||
|
$("#voltage-config").val(cfg["voltage-config"]);
|
||||||
$("#gravity-formula").val(cfg["gravity-formula"]);
|
$("#gravity-formula").val(cfg["gravity-formula"]);
|
||||||
$("#temp-adjustment-value").val(cfg["temp-adjustment-value"]);
|
$("#temp-adjustment-value").val(cfg["temp-adjustment-value"]);
|
||||||
$("#gravity-temp-adjustment").prop( "checked", cfg["gravity-temp-adjustment"] );
|
$("#gravity-temp-adjustment").prop( "checked", cfg["gravity-temp-adjustment"] );
|
||||||
$("#gyro-temp").prop( "checked", cfg["gyro-temp"] );
|
$("#gyro-temp").prop( "checked", cfg["gyro-temp"] );
|
||||||
|
$("#storage-sleep").prop( "checked", cfg["storage-sleep"] );
|
||||||
$("#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"] );
|
$("#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");
|
$("#battery").text(cfg["battery"] + " V");
|
||||||
$("#angle").text(cfg["angle"]);
|
$("#angle").text(cfg["angle"]);
|
||||||
|
@ -15,8 +15,6 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
<!-- START MENU -->
|
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
|
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
|
||||||
@ -24,16 +22,34 @@
|
|||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
<ul class="navbar-nav mr-auto">
|
<ul class="navbar-nav">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="javascript:history.back()">Back to configuration</a>
|
<a class="nav-link" href="/index.htm">Home</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle active" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
Configuration
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a class="dropdown-item" href="/config.htm">Configuration</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/format.htm">Format editor</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/test.htm">Test push</a></li>
|
||||||
|
<li><a class="dropdown-item" href="#">Upload firmware</a></li>
|
||||||
|
</ul>
|
||||||
|
</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>
|
||||||
<div class="spinner-border text-light" id="spinner" role="status"></div>
|
<div class="spinner-border text-light" id="spinner" role="status"></div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
<!-- START MAIN INDEX -->
|
<!-- START MAIN INDEX -->
|
||||||
|
|
||||||
<div class="container row-margin-10">
|
<div class="container row-margin-10">
|
||||||
@ -135,7 +151,7 @@
|
|||||||
showError("Upload failed");
|
showError("Upload failed");
|
||||||
},
|
},
|
||||||
success: function(resp) {
|
success: function(resp) {
|
||||||
showSuccess("Upload completed, device is restarting. Waiting 10 seconds to refresh browser.");
|
showSuccess("Upload completed, device is restarting. Waiting 10 seconds to refresh browser. If you don't get any gyro readings after update, please press the reset button!");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location = "/";
|
window.location = "/";
|
||||||
}, 10000);
|
}, 10000);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<!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://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"><style>.row-margin-10{margin-top:1em}</style></head><body class="py-4"><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><!-- START MENU --><nav class="navbar navbar-expand-lg navbar-dark bg-primary"><div class="container"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbarNav"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="javascript:history.back()">Back to configuration</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START MAIN INDEX --><div class="container row-margin-10"><div class="alert alert-success alert-dismissible hide fade d-none" role="alert"><div id="alert"></div><button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("hide").addClass("show").removeClass("d-none"),$("#alert").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("hide").addClass("show").removeClass("d-none"),$("#alert").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("hide").removeClass("show").addClass("d-none")})</script><div class="accordion" id="accordion"><div class="accordion-item"><h2 class="accordion-header" id="headingFirmware"><button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFirmware" aria-expanded="true" aria-controls="collapseFirmware"><b>Upload firmware</b></button></h2><div id="collapseFirmware" class="accordion-collapse collapse show" aria-labelledby="headingFirmware" data-bs-parent="#accordion"><div class="accordion-body"><div class="row mb-3"><div class="col-md-12 themed-grid-col bg-light">Here you can upload a new firmware version, it will not check the version number so you can also downgrade the firmware here.</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">Current version:</div><div class="col-md-10 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">Platform:</div><div class="col-md-10 themed-grid-col bg-light" id="platform">Loading...</div></div><form id="uploadForm" enctype="multipart/form-data"><div class="row mb-3"><div class="col-md-8 custom-file"><input type="file" accept=".bin" class="custom-file-input" name="name" id="name" onchange="checkName()" data-bs-toggle="tooltip" title="Select a firmware file to upload"></div></div><div class="row mb-3"><div class="col-md-4"><button type="submit" class="btn btn-primary" id="upload-btn" value="upload" data-bs-toggle="tooltip" title="Update the device with the selected firmware">Flash firmware</button></div></div></form><div class="progress"><div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div></div></div></div></div></div><script type="text/javascript">window.onload = getStatus;
|
<!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://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"><style>.row-margin-10{margin-top:1em}</style></head><body class="py-4"><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><nav class="navbar navbar-expand-lg navbar-dark bg-primary"><div class="container"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbarNav"><ul class="navbar-nav"><li class="nav-item"><a class="nav-link" href="/index.htm">Home</a></li><li class="nav-item dropdown"><a class="nav-link dropdown-toggle active" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">Configuration</a><ul class="dropdown-menu"><li><a class="dropdown-item" href="/config.htm">Configuration</a></li><li><a class="dropdown-item" href="/format.htm">Format editor</a></li><li><a class="dropdown-item" href="/test.htm">Test push</a></li><li><a class="dropdown-item" href="#">Upload firmware</a></li></ul></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></div></nav><!-- START MAIN INDEX --><div class="container row-margin-10"><div class="alert alert-success alert-dismissible hide fade d-none" role="alert"><div id="alert"></div><button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("hide").addClass("show").removeClass("d-none"),$("#alert").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("hide").addClass("show").removeClass("d-none"),$("#alert").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("hide").removeClass("show").addClass("d-none")})</script><div class="accordion" id="accordion"><div class="accordion-item"><h2 class="accordion-header" id="headingFirmware"><button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFirmware" aria-expanded="true" aria-controls="collapseFirmware"><b>Upload firmware</b></button></h2><div id="collapseFirmware" class="accordion-collapse collapse show" aria-labelledby="headingFirmware" data-bs-parent="#accordion"><div class="accordion-body"><div class="row mb-3"><div class="col-md-12 themed-grid-col bg-light">Here you can upload a new firmware version, it will not check the version number so you can also downgrade the firmware here.</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">Current version:</div><div class="col-md-10 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">Platform:</div><div class="col-md-10 themed-grid-col bg-light" id="platform">Loading...</div></div><form id="uploadForm" enctype="multipart/form-data"><div class="row mb-3"><div class="col-md-8 custom-file"><input type="file" accept=".bin" class="custom-file-input" name="name" id="name" onchange="checkName()" data-bs-toggle="tooltip" title="Select a firmware file to upload"></div></div><div class="row mb-3"><div class="col-md-4"><button type="submit" class="btn btn-primary" id="upload-btn" value="upload" data-bs-toggle="tooltip" title="Update the device with the selected firmware">Flash firmware</button></div></div></form><div class="progress"><div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div></div></div></div></div></div><script type="text/javascript">window.onload = getStatus;
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$("#uploadForm").on('submit', function(e) {
|
$("#uploadForm").on('submit', function(e) {
|
||||||
@ -26,7 +26,7 @@
|
|||||||
showError("Upload failed");
|
showError("Upload failed");
|
||||||
},
|
},
|
||||||
success: function(resp) {
|
success: function(resp) {
|
||||||
showSuccess("Upload completed, device is restarting. Waiting 10 seconds to refresh browser.");
|
showSuccess("Upload completed, device is restarting. Waiting 10 seconds to refresh browser. If you don't get any gyro readings after update, please press the reset button!");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location = "/";
|
window.location = "/";
|
||||||
}, 10000);
|
}, 10000);
|
||||||
|
178
html/format.htm
@ -15,8 +15,6 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
<!-- START MENU -->
|
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
|
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
|
||||||
@ -24,39 +22,81 @@
|
|||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
<ul class="navbar-nav mr-auto">
|
<ul class="navbar-nav">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="javascript:history.back()">Back to configuration</a>
|
<a class="nav-link" href="/index.htm">Home</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle active" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
Configuration
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a class="dropdown-item" href="/config.htm">Configuration</a></li>
|
||||||
|
<li><a class="dropdown-item" href="#">Format editor</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/test.htm">Test push</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/firmware.htm">Upload firmware</a></li>
|
||||||
|
</ul>
|
||||||
|
</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>
|
||||||
<div class="spinner-border text-light" id="spinner" role="status"></div>
|
<div class="spinner-border text-light" id="spinner" role="status"></div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
<!-- START MAIN INDEX -->
|
<!-- START MAIN INDEX -->
|
||||||
|
|
||||||
<div class="container row-margin-10">
|
<div class="container row-margin-10">
|
||||||
|
|
||||||
<div class="alert alert-success alert-dismissible hide fade d-none" role="alert">
|
<div class="alert alert-success alert-dismissible hide fade d-none" role="alert" id="alert">
|
||||||
<div id="alert"></div>
|
<div id="alert-msg"></div>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="alert alert-warning alert-dismissible hide fade d-none" role="alert" id="warning-ha">
|
||||||
|
<div>Home Assistant device configuration detected in MQTT format. These messages will be posted when format is saved and not during gravity measurement.</div>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="alert alert-warning alert-dismissible hide fade d-none" role="alert" id="warning-length">
|
||||||
|
<div>The format template is quite large, its possible the device will not have enough memory to handle it.</div>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function showError( msg ) {
|
function showError( msg ) {
|
||||||
$('.alert').removeClass('alert-success').addClass('alert-danger').removeClass('hide').addClass('show').removeClass('d-none');
|
$('#alert').removeClass('alert-success').addClass('alert-danger').removeClass('hide').addClass('show').removeClass('d-none');
|
||||||
$('#alert').text( msg );
|
$('#alert-msg').text( msg );
|
||||||
}
|
}
|
||||||
|
|
||||||
function showSuccess( msg ) {
|
function showSuccess( msg ) {
|
||||||
$('.alert').addClass('alert-success').removeClass('alert-danger').removeClass('hide').addClass('show').removeClass('d-none');
|
$('#alert').addClass('alert-success').removeClass('alert-danger').removeClass('hide').addClass('show').removeClass('d-none');
|
||||||
$('#alert').text( msg );
|
$('#alert-msg').text( msg );
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#alert-btn").click(function(e){
|
$("#alert-btn").click(function(e){
|
||||||
$('.alert').addClass('hide').removeClass('show').addClass('d-none');
|
$('#alert').addClass('hide').removeClass('show').addClass('d-none');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function showWarningHomeAssistant() {
|
||||||
|
$('#warning-ha').removeClass('d-none').addClass('show').removeClass('hide');
|
||||||
|
}
|
||||||
|
function hideWarningHomeAssistant() {
|
||||||
|
$('#warning-ha').addClass('d-none').removeClass('show').addClass('hide');
|
||||||
|
}
|
||||||
|
|
||||||
|
function showWarningLength() {
|
||||||
|
$('#warning-length').removeClass('d-none').addClass('show').removeClass('hide');
|
||||||
|
}
|
||||||
|
function hideWarningLength() {
|
||||||
|
$('#warning-length').addClass('d-none').removeClass('show').addClass('hide');
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="accordion" id="accordion">
|
<div class="accordion" id="accordion">
|
||||||
@ -89,19 +129,21 @@
|
|||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<textarea rows="10" class="form-control" name="format" id="format">
|
<textarea rows="10" class="form-control" name="format" id="format" onchange="updateStatusField()" onkeydown="updateStatusField()">
|
||||||
</textarea>
|
</textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
let formatTemplates = [
|
let formatTemplates = [
|
||||||
{ "id": "GravityMon-Post", "format": "%7B%0A%20%22name%22%20%3A%20%22%24%7Bmdns%7D%22%2C%0A%20%22ID%22%3A%20%22%24%7Bid%7D%22%2C%0A%20%22token%22%20%3A%20%22gravmon%22%2C%0A%20%22interval%22%3A%20%24%7Bsleep-interval%7D%2C%0A%20%22temperature%22%3A%20%24%7Btemp%7D%2C%0A%20%22temp_units%22%3A%20%22%24%7Btemp-unit%7D%22%2C%0A%20%22gravity%22%3A%20%24%7Bgravity%7D%2C%0A%20%22angle%22%3A%20%24%7Bangle%7D%2C%0A%20%22battery%22%3A%20%24%7Bbattery%7D%2C%0A%20%22RSSI%22%3A%20%24%7Brssi%7D%2C%0A%20%22corr-gravity%22%3A%20%24%7Bcorr-gravity%7D%2C%0A%20%22gravity-unit%22%3A%20%22%24%7Bgravity-unit%7D%22%2C%0A%20%22run-time%22%3A%20%24%7Brun-time%7D%0A%7D" },
|
{ "id": "GravityMon-Post", "format": "%7B%20%22name%22%20%3A%20%22%24%7Bmdns%7D%22%2C%20%22ID%22%3A%20%22%24%7Bid%7D%22%2C%20%22token%22%20%3A%20%22%24%7Btoken%7D%22%2C%20%22interval%22%3A%20%24%7Bsleep-interval%7D%2C%20%22temperature%22%3A%20%24%7Btemp%7D%2C%20%22temp_units%22%3A%20%22%24%7Btemp-unit%7D%22%2C%20%22gravity%22%3A%20%24%7Bgravity%7D%2C%20%22angle%22%3A%20%24%7Bangle%7D%2C%20%22battery%22%3A%20%24%7Bbattery%7D%2C%20%22RSSI%22%3A%20%24%7Brssi%7D%2C%20%22corr-gravity%22%3A%20%24%7Bcorr-gravity%7D%2C%20%22gravity-unit%22%3A%20%22%24%7Bgravity-unit%7D%22%2C%20%22run-time%22%3A%20%24%7Brun-time%7D%7D" },
|
||||||
{ "id": "GravityMon-Get", "format": "%3Fname%3D%24%7Bmdns%7D%26id%3D%24%7Bid%7D%26token%3D%24%7Btoken2%7D%26interval%3D%24%7Bsleep-interval%7D%26temperature%3D%24%7Btemp%7D%26%0Atemp-units%3D%24%7Btemp-unit%7D%26gravity%3D%24%7Bgravity%7D%26angle%3D%24%7Bangle%7D%26battery%3D%24%7Bbattery%7D%26rssi%3D%24%7Brssi%7D%26%0Acorr-gravity%3D%24%7Bcorr-gravity%7D%26gravity-unit%3D%24%7Bgravity-unit%7D%26run-time%3D%24%7Brun-time%7D" },
|
{ "id": "GravityMon-Get", "format": "%3Fname%3D%24%7Bmdns%7D%26id%3D%24%7Bid%7D%26token%3D%24%7Btoken2%7D%26interval%3D%24%7Bsleep-interval%7D%26temperature%3D%24%7Btemp%7D%26%0Atemp-units%3D%24%7Btemp-unit%7D%26gravity%3D%24%7Bgravity%7D%26angle%3D%24%7Bangle%7D%26battery%3D%24%7Bbattery%7D%26rssi%3D%24%7Brssi%7D%26%0Acorr-gravity%3D%24%7Bcorr-gravity%7D%26gravity-unit%3D%24%7Bgravity-unit%7D%26run-time%3D%24%7Brun-time%7D" },
|
||||||
{ "id": "iSpindle-Post", "format": "%7B%0A%20%22name%22%20%3A%20%22%24%7Bmdns%7D%22%2C%0A%20%22ID%22%3A%20%22%24%7Bid%7D%22%2C%0A%20%22token%22%20%3A%20%22gravmon%22%2C%0A%20%22interval%22%3A%20%24%7Bsleep-interval%7D%2C%0A%20%22temperature%22%3A%20%24%7Btemp%7D%2C%0A%20%22temp_units%22%3A%20%22%24%7Btemp-unit%7D%22%2C%0A%20%22gravity%22%3A%20%24%7Bgravity%7D%2C%0A%20%22angle%22%3A%20%24%7Bangle%7D%2C%0A%20%22battery%22%3A%20%24%7Bbattery%7D%2C%0A%20%22RSSI%22%3A%20%24%7Brssi%7D%0A%7D" },
|
{ "id": "iSpindle-Post", "format": "%7B%20%22name%22%20%3A%20%22%24%7Bmdns%7D%22%2C%20%22ID%22%3A%20%22%24%7Bid%7D%22%2C%20%22token%22%20%3A%20%22%24%7Btoken%7D%22%2C%20%22interval%22%3A%20%24%7Bsleep-interval%7D%2C%20%22temperature%22%3A%20%24%7Btemp%7D%2C%20%22temp_units%22%3A%20%22%24%7Btemp-unit%7D%22%2C%20%22gravity%22%3A%20%24%7Bgravity%7D%2C%20%22angle%22%3A%20%24%7Bangle%7D%2C%20%22battery%22%3A%20%24%7Bbattery%7D%2C%20%22RSSI%22%3A%20%24%7Brssi%7D%7D" },
|
||||||
{ "id": "BrewFatherCustom-Post", "format": "%7B%0A%20%20%20%22name%22%3A%20%22%24%7Bmdns%7D%22%2C%0A%20%20%20%22temp%22%3A%20%24%7Btemp%7D%2C%0A%20%20%20%22aux_temp%22%3A%200%2C%0A%20%20%20%22ext_temp%22%3A%200%2C%0A%20%20%20%22temp_unit%22%3A%20%22%24%7Btemp-unit%7D%22%2C%0A%20%20%20%22gravity%22%3A%20%24%7Bgravity%7D%2C%0A%20%20%20%22gravity_unit%22%3A%20%22%24%7Bgravity-unit%7D%22%2C%0A%20%20%20%22pressure%22%3A%200%2C%0A%20%20%20%22pressure_unit%22%3A%20%22PSI%22%2C%0A%20%20%20%22ph%22%3A%200%2C%0A%20%20%20%22bpm%22%3A%200%2C%0A%20%20%20%22comment%22%3A%20%22%22%2C%0A%20%20%20%22beer%22%3A%20%22%22%2C%0A%20%20%20%22battery%22%3A%20%24%7Bbattery%7D%0A%7D" },
|
{ "id": "BrewFatherCustom-Post", "format": "%7B%20%20%20%22name%22%3A%20%22%24%7Bmdns%7D%22%2C%20%20%20%22temp%22%3A%20%24%7Btemp%7D%2C%20%20%20%22aux_temp%22%3A%200%2C%20%20%20%22ext_temp%22%3A%200%2C%20%20%20%22temp_unit%22%3A%20%22%24%7Btemp-unit%7D%22%2C%20%20%20%22gravity%22%3A%20%24%7Bgravity%7D%2C%20%20%20%22gravity_unit%22%3A%20%22%24%7Bgravity-unit%7D%22%2C%20%20%20%22pressure%22%3A%200%2C%20%20%20%22pressure_unit%22%3A%20%22PSI%22%2C%20%20%20%22ph%22%3A%200%2C%20%20%20%22bpm%22%3A%200%2C%20%20%20%22comment%22%3A%20%22%22%2C%20%20%20%22beer%22%3A%20%22%22%2C%20%20%20%22battery%22%3A%20%24%7Bbattery%7D%7D" },
|
||||||
{ "id": "iSpindle-Mqtt", "format": "ispindel%2F%24%7Bmdns%7D%2Ftilt%3A%24%7Bangle%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2Ftemperature%3A%24%7Btemp%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2Ftemp_units%3A%24%7Btemp-unit%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2Fbattery%3A%24%7Bbattery%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2Fgravity%3A%24%7Bgravity%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2Finterval%3A%24%7Bsleep-interval%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2FRSSI%3A%24%7Brssi%7D%7C" },
|
{ "id": "iSpindle-Mqtt", "format": "ispindel%2F%24%7Bmdns%7D%2Ftilt%3A%24%7Bangle%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2Ftemperature%3A%24%7Btemp%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2Ftemp_units%3A%24%7Btemp-unit%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2Fbattery%3A%24%7Bbattery%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2Fgravity%3A%24%7Bgravity%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2Finterval%3A%24%7Bsleep-interval%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2FRSSI%3A%24%7Brssi%7D%7C" },
|
||||||
{ "id": "HomeAssistant-Mqtt", "format": "gravmon%2F%24%7Bmdns%7D%2Ftilt%3A%24%7Bangle%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Ftemperature%3A%24%7Btemp%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Ftemp_units%3A%24%7Btemp-unit%7D%7C" },
|
{ "id": "HomeAssistant-Mqtt", "format": "gravmon%2F%24%7Bmdns%7D%2Ftemperature%3A%24%7Btemp%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Fgravity%3A%24%7Bgravity%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Frssi%3A%24%7Brssi%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Ftilt%3A%24%7Btilt%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Fbattery%3A%24%7Bbattery%7D%7C%0A" },
|
||||||
|
{ "id": "HomeAssistant-Mqtt2", "format": "gravmon%2F%24%7Bmdns%7D%2Ftemperature%3A%24%7Btemp%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Fgravity%3A%24%7Bgravity%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Frssi%3A%24%7Brssi%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Ftilt%3A%24%7Btilt%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Fbattery%3A%24%7Bbattery%7D%7C%0Ahomeassistant%2Fsensor%2Fgravmon_%24%7Bid%7D%2Ftemperature%2Fconfig%3A%7B%22dev%22%3A%7B%22name%22%3A%22%24%7Bmdns%7D%22%2C%22mdl%22%3A%22gravmon%22%2C%22sw%22%3A%22%24%7Bapp-ver%7D%22%2C%22ids%22%3A%22%24%7Bid%7D%22%7D%2C%22uniq_id%22%3A%22%24%7Bid%7D_temp%22%2C%22name%22%3A%22temperature%22%2C%22dev_cla%22%3A%22temperature%22%2C%22unit_of_meas%22%3A%22%24%7Btemp-unit%7D%22%2C%22stat_t%22%3A%22gravmon%2F%24%7Bmdns%7D%2Ftemperature%22%7D%7C%0Ahomeassistant%2Fsensor%2Fgravmon_%24%7Bid%7D%2Fgravity%2Fconfig%3A%7B%22dev%22%3A%7B%22name%22%3A%22%24%7Bmdns%7D%22%2C%22mdl%22%3A%22gravmon%22%2C%22sw%22%3A%22%24%7Bapp-ver%7D%22%2C%22ids%22%3A%22%24%7Bid%7D%22%7D%2C%22uniq_id%22%3A%22%24%7Bid%7D_grav%22%2C%22name%22%3A%22gravity%22%2C%22dev_cla%22%3A%22temperature%22%2C%22unit_of_meas%22%3A%22%20%24%7Bgravity-unit%7D%22%2C%22stat_t%22%3A%22gravmon%2F%24%7Bmdns%7D%2Fgravity%22%7D%7C%0Ahomeassistant%2Fsensor%2Fgravmon_%24%7Bid%7D%2Frssi%2Fconfig%3A%7B%22dev%22%3A%7B%22name%22%3A%22%24%7Bmdns%7D%22%2C%22mdl%22%3A%22gravmon%22%2C%22sw%22%3A%22%24%7Bapp-ver%7D%22%2C%22ids%22%3A%22%24%7Bid%7D%22%7D%2C%22uniq_id%22%3A%22%24%7Bid%7D_rssi%22%2C%22name%22%3A%22rssi%22%2C%22dev_cla%22%3A%22signal_strength%22%2C%22unit_of_meas%22%3A%22dBm%22%2C%22stat_t%22%3A%22gravmon%2F%24%7Bmdns%7D%2Frssi%22%7D%7C%0Ahomeassistant%2Fsensor%2Fgravmon_%24%7Bid%7D%2Ftilt%2Fconfig%3A%7B%22dev%22%3A%7B%22name%22%3A%22%24%7Bmdns%7D%22%2C%22mdl%22%3A%22gravmon%22%2C%22sw%22%3A%22%24%7Bapp-ver%7D%22%2C%22ids%22%3A%22%24%7Bid%7D%22%7D%2C%22uniq_id%22%3A%22%24%7Bid%7D_tilt%22%2C%22name%22%3A%22tilt%22%2C%22dev_cla%22%3A%22temperature%22%2C%22stat_t%22%3A%22gravmon%2F%24%7Bmdns%7D%2Ftilt%22%7D%7C%0Ahomeassistant%2Fsensor%2Fgravmon_%24%7Bid%7D%2Fbattery%2Fconfig%3A%7B%22dev%22%3A%7B%22name%22%3A%22%24%7Bmdns%7D%22%2C%22mdl%22%3A%22gravmon%22%2C%22sw%22%3A%22%24%7Bapp-ver%7D%22%2C%22ids%22%3A%22%24%7Bid%7D%22%7D%2C%22uniq_id%22%3A%22%24%7Bid%7D_batt%22%2C%22name%22%3A%22battery%22%2C%22dev_cla%22%3A%22voltage%22%2C%22unit_of_meas%22%3A%22V%22%2C%22stat_t%22%3A%22gravmon%2F%24%7Bmdns%7D%2Fbattery%22%7D%7C%0A" },
|
||||||
|
{ "id": "Brewblox-Mqtt", "format": "brewcast%2Fhistory%3A%7B%22key%22%3A%22%24%7Bmdns%7D%22%2C%22data%22%3A%7B%22Temperature%5BdegC%5D%22%3A%20%24%7Btemp-c%7D%2C%22Temperature%5BdegF%5D%22%3A%20%24%7Btemp-f%7D%2C%22Battery%5BV%5D%22%3A%24%7Bbattery%7D%2C%22Tilt%5Bdeg%5D%22%3A%24%7Bangle%7D%2C%22Rssi%5BdBm%5D%22%3A%24%7Brssi%7D%2C%22SG%22%3A%24%7Bgravity-sg%7D%2C%22Plato%22%3A%24%7Bgravity-plato%7D%7D%7D%7C" },
|
||||||
{ "id": "UBIDots-Post", "format": "%7B%0A%20%20%20%22temperature%22%3A%20%24%7Btemp%7D%2C%0A%20%20%20%22gravity%22%3A%20%24%7Bgravity%7D%2C%0A%20%20%20%22angle%22%3A%20%24%7Bangle%7D%2C%0A%20%20%20%22battery%22%3A%20%24%7Bbattery%7D%2C%0A%20%20%20%22rssi%22%3A%20%24%7Brssi%7D%0A%7D" } ];
|
{ "id": "UBIDots-Post", "format": "%7B%0A%20%20%20%22temperature%22%3A%20%24%7Btemp%7D%2C%0A%20%20%20%22gravity%22%3A%20%24%7Bgravity%7D%2C%0A%20%20%20%22angle%22%3A%20%24%7Bangle%7D%2C%0A%20%20%20%22battery%22%3A%20%24%7Bbattery%7D%2C%0A%20%20%20%22rssi%22%3A%20%24%7Brssi%7D%0A%7D" } ];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -115,9 +157,12 @@
|
|||||||
<option value="GravityMon-Post">GravityMon (POST)</option>
|
<option value="GravityMon-Post">GravityMon (POST)</option>
|
||||||
<option value="iSpindle-Mqtt">iSpindle (MQTT)</option>
|
<option value="iSpindle-Mqtt">iSpindle (MQTT)</option>
|
||||||
<option value="HomeAssistant-Mqtt">Home Assistant (MQTT)</option>
|
<option value="HomeAssistant-Mqtt">Home Assistant (MQTT)</option>
|
||||||
|
<option value="HomeAssistant-Mqtt2">Home Assistant - Auto register sensor (MQTT)</option>
|
||||||
<option value="UBIDots-Post">UBIdots (POST)</option>
|
<option value="UBIDots-Post">UBIdots (POST)</option>
|
||||||
<option value="BrewFatherCustom-Post">Brewfather Custom Endpoint (POST)</option>
|
<option value="BrewFatherCustom-Post">Brewfather - Custom Endpoint (POST)</option>
|
||||||
|
<option value="iSpindle-Post">Brewfather - iSpindle Endpoint (POST)</option>
|
||||||
<option value="GravityMon-Get">GravityMon (GET)</option>
|
<option value="GravityMon-Get">GravityMon (GET)</option>
|
||||||
|
<option value="Brewblox-Mqtt">BrewBlox (MQTT)</option>
|
||||||
</select>
|
</select>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<button class="btn btn-secondary" id="copy-btn" data-bs-toggle="tooltip" title="Copy the selected format template to the selected push target">Copy format</button>
|
<button class="btn btn-secondary" id="copy-btn" data-bs-toggle="tooltip" title="Copy the selected format template to the selected push target">Copy format</button>
|
||||||
@ -125,6 +170,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-8" id="status">
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr class="my-2">
|
<hr class="my-2">
|
||||||
|
|
||||||
<pre class="card-preview" id="preview" name="preview"></pre>
|
<pre class="card-preview" id="preview" name="preview"></pre>
|
||||||
@ -138,6 +186,8 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
window.onload = getConfig;
|
window.onload = getConfig;
|
||||||
|
|
||||||
|
var maxCharsInFormatTemplate = 1500;
|
||||||
|
|
||||||
setButtonDisabled( true );
|
setButtonDisabled( true );
|
||||||
|
|
||||||
// Opens the targetet according (if URL has #collapseOne to #collapseFour)
|
// Opens the targetet according (if URL has #collapseOne to #collapseFour)
|
||||||
@ -148,6 +198,17 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function updateStatusField() {
|
||||||
|
var t = $("#format").val();
|
||||||
|
$("#status").text( t.length + " characters in template, recommended maximum is " + maxCharsInFormatTemplate );
|
||||||
|
|
||||||
|
if (t.length > maxCharsInFormatTemplate) {
|
||||||
|
showWarningLength();
|
||||||
|
} else {
|
||||||
|
hideWarningLength();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
$("#push-target").change(function(e){
|
$("#push-target").change(function(e){
|
||||||
console.log(e)
|
console.log(e)
|
||||||
selectFormat();
|
selectFormat();
|
||||||
@ -164,29 +225,88 @@
|
|||||||
$("#format").val( decodeURIComponent(item.format) );
|
$("#format").val( decodeURIComponent(item.format) );
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateStatusField();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Clear the selected template
|
// Clear the selected template
|
||||||
$("#clear-btn").click(function(e) {
|
$("#clear-btn").click(function(e) {
|
||||||
$("#format").val( "" );
|
$("#format").val( "" );
|
||||||
|
updateStatusField();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Store the format
|
// Store the format
|
||||||
$("#format-btn").click(function(e) {
|
$("#format-btn").click(function(e) {
|
||||||
var s = $("#format").val();
|
var s = $("#format").val();
|
||||||
s = s.replaceAll("\n", "");
|
|
||||||
var obj = 'id=' + $("#id").val() + '&' + $("#push-target").val() + '=' + encodeURIComponent(s);
|
|
||||||
console.log(obj);
|
|
||||||
|
|
||||||
|
/*if (s.length > maxCharsInFormatTemplate) {
|
||||||
|
showError("Format template is too large for the device to handle, unable to save.")
|
||||||
|
return;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
$('#spinner').show();
|
||||||
|
setButtonDisabled( true );
|
||||||
|
var ha = false;
|
||||||
|
|
||||||
|
hideWarningHomeAssistant();
|
||||||
|
if ($("#push-target").val() == "mqtt") {
|
||||||
|
if (s.search("homeassistant/sensor/") != -1) {
|
||||||
|
console.log("Current format is mqtt, it contains topics for Home Assistant device registration.")
|
||||||
|
showWarningHomeAssistant();
|
||||||
|
ha = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s = s.replaceAll("\n", "");
|
||||||
|
var obj = 'id=' + $("#id").val() + "&" + $("#push-target").val() + '=' + encodeURIComponent(s);
|
||||||
|
console.log(obj);
|
||||||
|
|
||||||
$.ajax( {
|
$.ajax( {
|
||||||
type: "POST",
|
type: "POST",
|
||||||
url: "/api/config/format",
|
url: "/api/config/format",
|
||||||
data: obj,
|
data: obj,
|
||||||
success: function(result) { showSuccess('Format stored successfully.'); getConfig(); },
|
success: function(result) { showSuccess('Format stored successfully.'); postHomeAssistant(ha); },
|
||||||
error: function(result) { showError('Unable to store format.'); }
|
error: function(result) { showError('Unable to store format.'); $('#spinner').hide(); },
|
||||||
|
always: function() { $('#spinner').hide(); setButtonDisabled( false ); }
|
||||||
} );
|
} );
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function postHomeAssistant(active) {
|
||||||
|
if (!active) {
|
||||||
|
getConfig();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Current format is mqtt, running post tests to register device.")
|
||||||
|
$('#spinner').show();
|
||||||
|
setButtonDisabled( true );
|
||||||
|
|
||||||
|
var url = "/api/test/push";
|
||||||
|
url += "?id=" + $("#id").val() + "&format=mqtt";
|
||||||
|
//var url = "/test/push.json";
|
||||||
|
$.getJSON(url, function (cfg) {
|
||||||
|
console.log(cfg);
|
||||||
|
|
||||||
|
var code = cfg["code"];
|
||||||
|
var success = cfg["success"];
|
||||||
|
var enabled = cfg["enabled"];
|
||||||
|
|
||||||
|
if(success) {
|
||||||
|
showSuccess( "Format stored successfully. Home Assistant Device Registration Successful." );
|
||||||
|
} else {
|
||||||
|
showError( "Format stored successfully. Home Assistant Device Registration Failed!" );
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fail(function() {
|
||||||
|
showError( "Format stored successfully. Home Assistant Device Registration Failed!" );
|
||||||
|
})
|
||||||
|
.always(function() {
|
||||||
|
$('#spinner').hide();
|
||||||
|
setButtonDisabled( false );
|
||||||
|
getConfig();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Test the calibration
|
// Test the calibration
|
||||||
$("#test-btn").click(function(e) {
|
$("#test-btn").click(function(e) {
|
||||||
var url = "/api/status";
|
var url = "/api/status";
|
||||||
@ -235,6 +355,8 @@
|
|||||||
doc = doc.replaceAll("${corr-gravity}", cfg["gravity"]);
|
doc = doc.replaceAll("${corr-gravity}", cfg["gravity"]);
|
||||||
doc = doc.replaceAll("${angle}", cfg["angle"]);
|
doc = doc.replaceAll("${angle}", cfg["angle"]);
|
||||||
doc = doc.replaceAll("${tilt}", cfg["angle"]);
|
doc = doc.replaceAll("${tilt}", cfg["angle"]);
|
||||||
|
doc = doc.replaceAll("${app-ver}", cfg["app-ver"]);
|
||||||
|
doc = doc.replaceAll("${app-build}", cfg["app-build"]);
|
||||||
|
|
||||||
// Format in a readable json string.
|
// Format in a readable json string.
|
||||||
try {
|
try {
|
||||||
@ -258,6 +380,9 @@
|
|||||||
function setButtonDisabled( b ) {
|
function setButtonDisabled( b ) {
|
||||||
$("#format-btn").prop("disabled", b);
|
$("#format-btn").prop("disabled", b);
|
||||||
$("#test-btn").prop("disabled", b);
|
$("#test-btn").prop("disabled", b);
|
||||||
|
$("#copy-btn").prop("disabled", b);
|
||||||
|
$("#clear-btn").prop("disabled", b);
|
||||||
|
$("#push-target").prop("disabled", b);
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectFormat() {
|
function selectFormat() {
|
||||||
@ -269,6 +394,7 @@
|
|||||||
console.log(s);
|
console.log(s);
|
||||||
$("#format").val(s);
|
$("#format").val(s);
|
||||||
$("#preview").text("");
|
$("#preview").text("");
|
||||||
|
updateStatusField();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the configuration values from the API
|
// Get the configuration values from the API
|
||||||
@ -276,7 +402,7 @@
|
|||||||
setButtonDisabled( true );
|
setButtonDisabled( true );
|
||||||
|
|
||||||
var url = "/api/config/format";
|
var url = "/api/config/format";
|
||||||
//var url = "/test/format.json";
|
// var url = "/test/format.json";
|
||||||
$('#spinner').show();
|
$('#spinner').show();
|
||||||
$.getJSON(url, function (cfg) {
|
$.getJSON(url, function (cfg) {
|
||||||
console.log( cfg );
|
console.log( cfg );
|
||||||
@ -296,6 +422,8 @@
|
|||||||
$('#spinner').hide();
|
$('#spinner').hide();
|
||||||
setButtonDisabled( false );
|
setButtonDisabled( false );
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateStatusField();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -16,8 +16,6 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
<!-- START MENU -->
|
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
|
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
|
||||||
@ -27,12 +25,20 @@
|
|||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link active" href="#"><b>Home</b></a>
|
<a class="nav-link active" href="#">Home</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link" href="/config.htm">Configuration</a>
|
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
Configuration
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a class="dropdown-item" href="/config.htm">Configuration</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/format.htm">Format editor</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/test.htm">Test push</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/firmware.htm">Upload firmware</a></li>
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/calibration.htm">Calibration</a>
|
<a class="nav-link" href="/calibration.htm">Calibration</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
@ -44,6 +50,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
<!-- START MAIN INDEX -->
|
<!-- START MAIN INDEX -->
|
||||||
|
|
||||||
<div class="container row-margin-10">
|
<div class="container row-margin-10">
|
||||||
@ -153,11 +160,33 @@
|
|||||||
});
|
});
|
||||||
setInterval(function() {
|
setInterval(function() {
|
||||||
loadLog();
|
loadLog();
|
||||||
}, 5000);
|
}, 30000);
|
||||||
|
|
||||||
function loadLog() {
|
function loadLog() {
|
||||||
$("#logContent").load("/log");
|
var url2 = "/log2";
|
||||||
//$("#logContent").load("/test/log");
|
var url1 = "/log";
|
||||||
|
//var url2 = "/test/log2";
|
||||||
|
//var url1 = "/test/log1";
|
||||||
|
var log = "";
|
||||||
|
|
||||||
|
$.get(url2, function(data) {
|
||||||
|
console.log(data);
|
||||||
|
var list = data.split("\n");
|
||||||
|
list.forEach(function (item, index) {
|
||||||
|
log = item + "\n" + log;
|
||||||
|
});
|
||||||
|
}).always( function() {
|
||||||
|
$.get(url1, function(data) {
|
||||||
|
console.log(data);
|
||||||
|
var list = data.split("\n");
|
||||||
|
list.forEach(function (item, index) {
|
||||||
|
log = item + "\n" + log;
|
||||||
|
});
|
||||||
|
}).always( function() {
|
||||||
|
console.log(log);
|
||||||
|
$("#logContent").text(log);
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -175,13 +204,13 @@
|
|||||||
|
|
||||||
<div class="collapse row-margin-10" id="collapseLog">
|
<div class="collapse row-margin-10" id="collapseLog">
|
||||||
<div class="card card-body">
|
<div class="card card-body">
|
||||||
<pre><code class="card-text" id="logContent"></code></pre>
|
<pre><code class="card-text" id="logContent" data-bs-toggle="tooltip" title="Shows the last errors on the device, newest on top.">Loading log data, please wait...</code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="collapse row-margin-10" id="collapseSupport">
|
<div class="collapse row-margin-10" id="collapseSupport">
|
||||||
<div class="card card-body">
|
<div class="card card-body">
|
||||||
<pre><code class="card-text" id="supportContent"></code></pre>
|
<pre><code class="card-text" id="supportContent">Collecting support data, please wait...</code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -293,7 +322,11 @@
|
|||||||
|
|
||||||
var angle = cfg["angle"];
|
var angle = cfg["angle"];
|
||||||
|
|
||||||
if(angle==0) {
|
if(angle==-1) {
|
||||||
|
showError("Unable to connect to gyro, try a reset or power off/on")
|
||||||
|
$("#angle").text("No gyro");
|
||||||
|
$("#gravity").text("No gyro");
|
||||||
|
} else if(angle==0) {
|
||||||
$("#angle").text("Gyro moving");
|
$("#angle").text("Gyro moving");
|
||||||
$("#gravity").text("Gyro moving");
|
$("#gravity").text("Gyro moving");
|
||||||
} else {
|
} else {
|
||||||
@ -322,10 +355,14 @@
|
|||||||
|
|
||||||
$("#battery").text(batt + " V (" + charge + "%)" );
|
$("#battery").text(batt + " V (" + charge + "%)" );
|
||||||
|
|
||||||
if( cfg["temp-format"] == "C")
|
if( cfg["temp-c"] == -273) {
|
||||||
$("#temp").text(cfg["temp-c"] + " C");
|
$("#temp").text("No temp sensor");
|
||||||
else
|
} else {
|
||||||
$("#temp").text(cfg["temp-f"] + " F");
|
if( cfg["temp-format"] == "C")
|
||||||
|
$("#temp").text(cfg["temp-c"] + " C");
|
||||||
|
else
|
||||||
|
$("#temp").text(cfg["temp-f"] + " F");
|
||||||
|
}
|
||||||
|
|
||||||
if( cfg["sleep-mode"] )
|
if( cfg["sleep-mode"] )
|
||||||
$("#sleep-mode").attr("checked", true );
|
$("#sleep-mode").attr("checked", true );
|
||||||
@ -342,7 +379,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function start() {
|
function start() {
|
||||||
setInterval(getStatus, 3000);
|
setInterval(getStatus, 5000);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -15,8 +15,6 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
<!-- START MENU -->
|
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
|
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
|
||||||
@ -24,11 +22,28 @@
|
|||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
<ul class="navbar-nav mr-auto">
|
<ul class="navbar-nav">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="javascript:history.back()">Back to configuration</a>
|
<a class="nav-link" href="/index.htm">Home</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle active" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
Configuration
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a class="dropdown-item" href="/config.htm">Configuration</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/format.htm">Format editor</a></li>
|
||||||
|
<li><a class="dropdown-item" href="#">Test push</a></li>
|
||||||
|
<li><a class="dropdown-item" href="/firmware.htm">Upload firmware</a></li>
|
||||||
|
</ul>
|
||||||
|
</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>
|
||||||
<div class="spinner-border text-light" id="spinner" role="status"></div>
|
<div class="spinner-border text-light" id="spinner" role="status"></div>
|
||||||
</div>
|
</div>
|
||||||
|
399
lib/ESP_DoubleResetDetector/ESP_DoubleResetDetector.h
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
/****************************************************************************************************************************
|
||||||
|
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.3.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-
|
||||||
|
1.3.0 K Hoang 10/02/2022 Add support to new ESP32-S3
|
||||||
|
1.3.1 K Hoang 04/03/2022 Add waitingForDRD() function to signal in DRD wating period
|
||||||
|
*****************************************************************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef ESP_DoubleResetDetector_H
|
||||||
|
#define ESP_DoubleResetDetector_H
|
||||||
|
|
||||||
|
#ifndef DOUBLERESETDETECTOR_DEBUG
|
||||||
|
#define DOUBLERESETDETECTOR_DEBUG false
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(ARDUINO) && (ARDUINO >= 100)
|
||||||
|
#include <Arduino.h>
|
||||||
|
#else
|
||||||
|
#include <WProgram.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ESP_DOUBLE_RESET_DETECTOR_VERSION
|
||||||
|
#define ESP_DOUBLE_RESET_DETECTOR_VERSION "ESP_DoubleResetDetector v1.3.1"
|
||||||
|
|
||||||
|
#define ESP_DOUBLE_RESET_DETECTOR_VERSION_MAJOR 1
|
||||||
|
#define ESP_DOUBLE_RESET_DETECTOR_VERSION_MINOR 3
|
||||||
|
#define ESP_DOUBLE_RESET_DETECTOR_VERSION_PATCH 1
|
||||||
|
|
||||||
|
#define ESP_DOUBLE_RESET_DETECTOR_VERSION_INT 1003001
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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)
|
||||||
|
|
||||||
|
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||||
|
#warning Neither EEPROM, SPIFFS nor LittleFS selected. Default to EEPROM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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)
|
||||||
|
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||||
|
#warning Neither RTC, EEPROM, LITTLEFS nor SPIFFS selected. Default to EEPROM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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) )
|
||||||
|
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||||
|
#warning Using ESP32 Core 1.0.6 or 2.0.0+
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// The library has been merged into esp32 core from release 1.0.6
|
||||||
|
#include <LittleFS.h>
|
||||||
|
|
||||||
|
#define FileFS LittleFS
|
||||||
|
#define FS_Name "LittleFS"
|
||||||
|
#else
|
||||||
|
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||||
|
#warning Using ESP32 Core 1.0.5-. You must install LITTLEFS library
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
bool waitingForDRD()
|
||||||
|
{
|
||||||
|
return waitingForDoubleReset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
if (waitingForDoubleReset && millis() > timeout)
|
||||||
|
{
|
||||||
|
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||||
|
Serial.println("Stop doubleResetDetecting");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void stop()
|
||||||
|
{
|
||||||
|
clearRecentlyResetFlag();
|
||||||
|
waitingForDoubleReset = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool doubleResetDetected;
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t DOUBLERESETDETECTOR_FLAG;
|
||||||
|
unsigned long timeout;
|
||||||
|
int address;
|
||||||
|
bool waitingForDoubleReset;
|
||||||
|
|
||||||
|
bool detectRecentlyResetFlag()
|
||||||
|
{
|
||||||
|
#if (ESP_DRD_USE_EEPROM)
|
||||||
|
EEPROM.get(EEPROM_START, DOUBLERESETDETECTOR_FLAG);
|
||||||
|
doubleResetDetectorFlag = DOUBLERESETDETECTOR_FLAG;
|
||||||
|
|
||||||
|
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||||
|
Serial.printf("EEPROM Flag read = 0x%X\n", DOUBLERESETDETECTOR_FLAG);
|
||||||
|
#endif
|
||||||
|
#elif ( ESP_DRD_USE_LITTLEFS || ESP_DRD_USE_SPIFFS )
|
||||||
|
// LittleFS / SPIFFS code
|
||||||
|
if (FileFS.exists(DRD_FILENAME))
|
||||||
|
{
|
||||||
|
// if config file exists, load
|
||||||
|
File file = FileFS.open(DRD_FILENAME, "r");
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||||
|
Serial.println("Loading config file failed");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
file.readBytes((char *) &DOUBLERESETDETECTOR_FLAG, sizeof(DOUBLERESETDETECTOR_FLAG));
|
||||||
|
doubleResetDetectorFlag = DOUBLERESETDETECTOR_FLAG;
|
||||||
|
|
||||||
|
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||||
|
|
||||||
|
#if ESP_DRD_USE_LITTLEFS
|
||||||
|
Serial.printf("LittleFS Flag read = 0x%X\n", DOUBLERESETDETECTOR_FLAG);
|
||||||
|
#else
|
||||||
|
Serial.printf("SPIFFS Flag read = 0x%X\n", DOUBLERESETDETECTOR_FLAG);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#ifdef ESP8266
|
||||||
|
//RTC only for ESP8266
|
||||||
|
ESP.rtcUserMemoryRead(address, &doubleResetDetectorFlag, sizeof(doubleResetDetectorFlag));
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
doubleResetDetected = (doubleResetDetectorFlag == DOUBLERESETDETECTOR_FLAG_SET);
|
||||||
|
return doubleResetDetected;
|
||||||
|
};
|
||||||
|
|
||||||
|
void setRecentlyResetFlag()
|
||||||
|
{
|
||||||
|
doubleResetDetectorFlag = DOUBLERESETDETECTOR_FLAG_SET;
|
||||||
|
|
||||||
|
DOUBLERESETDETECTOR_FLAG = DOUBLERESETDETECTOR_FLAG_SET;
|
||||||
|
|
||||||
|
#if (ESP_DRD_USE_EEPROM)
|
||||||
|
EEPROM.put(EEPROM_START, DOUBLERESETDETECTOR_FLAG);
|
||||||
|
EEPROM.commit();
|
||||||
|
|
||||||
|
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||||
|
delay(1000);
|
||||||
|
EEPROM.get(EEPROM_START, DOUBLERESETDETECTOR_FLAG);
|
||||||
|
|
||||||
|
Serial.printf("SetFlag write = 0x%X\n", DOUBLERESETDETECTOR_FLAG);
|
||||||
|
#endif
|
||||||
|
#elif ( ESP_DRD_USE_LITTLEFS || ESP_DRD_USE_SPIFFS )
|
||||||
|
// LittleFS / SPIFFS code
|
||||||
|
File file = FileFS.open(DRD_FILENAME, "w");
|
||||||
|
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||||
|
Serial.println("Saving config file...");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (file)
|
||||||
|
{
|
||||||
|
file.write((uint8_t *) &DOUBLERESETDETECTOR_FLAG, sizeof(DOUBLERESETDETECTOR_FLAG));
|
||||||
|
file.close();
|
||||||
|
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||||
|
Serial.println("Saving config file OK");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||||
|
Serial.println("Saving config file failed");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#ifdef ESP8266
|
||||||
|
//RTC only for ESP8266
|
||||||
|
ESP.rtcUserMemoryWrite(address, &doubleResetDetectorFlag, sizeof(doubleResetDetectorFlag));
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void clearRecentlyResetFlag()
|
||||||
|
{
|
||||||
|
doubleResetDetectorFlag = DOUBLERESETDETECTOR_FLAG_CLEAR;
|
||||||
|
DOUBLERESETDETECTOR_FLAG = DOUBLERESETDETECTOR_FLAG_CLEAR;
|
||||||
|
|
||||||
|
#if (ESP_DRD_USE_EEPROM)
|
||||||
|
//DOUBLERESETDETECTOR_FLAG = DOUBLERESETDETECTOR_FLAG_CLEAR;
|
||||||
|
EEPROM.put(EEPROM_START, DOUBLERESETDETECTOR_FLAG);
|
||||||
|
EEPROM.commit();
|
||||||
|
|
||||||
|
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||||
|
delay(1000);
|
||||||
|
EEPROM.get(EEPROM_START, DOUBLERESETDETECTOR_FLAG);
|
||||||
|
|
||||||
|
Serial.printf("ClearFlag write = 0x%X\n", DOUBLERESETDETECTOR_FLAG);
|
||||||
|
#endif
|
||||||
|
#elif ( ESP_DRD_USE_LITTLEFS || ESP_DRD_USE_SPIFFS )
|
||||||
|
// LittleFS / SPIFFS code
|
||||||
|
File file = FileFS.open(DRD_FILENAME, "w");
|
||||||
|
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||||
|
Serial.println("Saving config file...");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (file)
|
||||||
|
{
|
||||||
|
file.write((uint8_t *) &DOUBLERESETDETECTOR_FLAG, sizeof(DOUBLERESETDETECTOR_FLAG));
|
||||||
|
file.close();
|
||||||
|
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||||
|
Serial.println("Saving config file OK");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||||
|
Serial.println("Saving config file failed");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#ifdef ESP8266
|
||||||
|
//RTC only for ESP8266
|
||||||
|
ESP.rtcUserMemoryWrite(address, &doubleResetDetectorFlag, sizeof(doubleResetDetectorFlag));
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t doubleResetDetectorFlag;
|
||||||
|
};
|
||||||
|
#endif // ESP_DoubleResetDetector_H
|
2122
lib/ESP_WifiManager/ESP_WiFiManager-Impl.h
Normal file
72
lib/ESP_WifiManager/ESP_WiFiManager.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/****************************************************************************************************************************
|
||||||
|
ESP_WiFiManager.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.9.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
|
||||||
|
1.9.0 K Hoang 17/01/2022 Enable compatibility with old code to include only ESP_WiFiManager.h
|
||||||
|
*****************************************************************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef ESP_WiFiManager_h
|
||||||
|
#define ESP_WiFiManager_h
|
||||||
|
|
||||||
|
#include <ESP_WiFiManager.hpp> //https://github.com/khoih-prog/ESP_WiFiManager
|
||||||
|
#include <ESP_WiFiManager-Impl.h> //https://github.com/khoih-prog/ESP_WiFiManager
|
||||||
|
|
||||||
|
#endif // ESP_WiFiManager_h
|
||||||
|
|
728
lib/ESP_WifiManager/ESP_WiFiManager.hpp
Normal file
128
lib/ESP_WifiManager/ESP_WiFiManager_Debug.h
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/****************************************************************************************************************************
|
||||||
|
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.9.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
|
||||||
|
1.9.0 K Hoang 17/01/2022 Enable compatibility with old code to include only ESP_WiFiManager.h
|
||||||
|
*****************************************************************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef ESP_WiFiManager_Debug_H
|
||||||
|
#define ESP_WiFiManager_Debug_H
|
||||||
|
|
||||||
|
#ifdef WIFIMGR_DEBUG_PORT
|
||||||
|
#define WM_DBG_PORT WIFIMGR_DEBUG_PORT
|
||||||
|
#else
|
||||||
|
#define WM_DBG_PORT Serial
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Change _WIFIMGR_LOGLEVEL_ to set tracing and logging verbosity
|
||||||
|
// 0: DISABLED: no logging
|
||||||
|
// 1: ERROR: errors
|
||||||
|
// 2: WARN: errors and warnings
|
||||||
|
// 3: INFO: errors, warnings and informational (default)
|
||||||
|
// 4: DEBUG: errors, warnings, informational and debug
|
||||||
|
|
||||||
|
#ifndef _WIFIMGR_LOGLEVEL_
|
||||||
|
#define _WIFIMGR_LOGLEVEL_ 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char WM_MARK[] = "[WM] ";
|
||||||
|
const char WM_SP[] = " ";
|
||||||
|
|
||||||
|
#define WM_PRINT WM_DBG_PORT.print
|
||||||
|
#define WM_PRINTLN WM_DBG_PORT.println
|
||||||
|
|
||||||
|
#define WM_PRINT_MARK WM_PRINT(WM_MARK)
|
||||||
|
#define WM_PRINT_SP WM_PRINT(WM_SP)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#define LOGERROR(x) if(_WIFIMGR_LOGLEVEL_>0) { WM_PRINT_MARK; WM_PRINTLN(x); }
|
||||||
|
#define LOGERROR0(x) if(_WIFIMGR_LOGLEVEL_>0) { WM_PRINT(x); }
|
||||||
|
#define LOGERROR1(x,y) if(_WIFIMGR_LOGLEVEL_>0) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINTLN(y); }
|
||||||
|
#define LOGERROR2(x,y,z) if(_WIFIMGR_LOGLEVEL_>0) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINT(y); WM_PRINT_SP; WM_PRINTLN(z); }
|
||||||
|
#define LOGERROR3(x,y,z,w) if(_WIFIMGR_LOGLEVEL_>0) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINT(y); WM_PRINT_SP; WM_PRINT(z); WM_PRINT_SP; WM_PRINTLN(w); }
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#define LOGWARN(x) if(_WIFIMGR_LOGLEVEL_>1) { WM_PRINT_MARK; WM_PRINTLN(x); }
|
||||||
|
#define LOGWARN0(x) if(_WIFIMGR_LOGLEVEL_>1) { WM_PRINT(x); }
|
||||||
|
#define LOGWARN1(x,y) if(_WIFIMGR_LOGLEVEL_>1) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINTLN(y); }
|
||||||
|
#define LOGWARN2(x,y,z) if(_WIFIMGR_LOGLEVEL_>1) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINT(y); WM_PRINT_SP; WM_PRINTLN(z); }
|
||||||
|
#define LOGWARN3(x,y,z,w) if(_WIFIMGR_LOGLEVEL_>1) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINT(y); WM_PRINT_SP; WM_PRINT(z); WM_PRINT_SP; WM_PRINTLN(w); }
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#define LOGINFO(x) if(_WIFIMGR_LOGLEVEL_>2) { WM_PRINT_MARK; WM_PRINTLN(x); }
|
||||||
|
#define LOGINFO0(x) if(_WIFIMGR_LOGLEVEL_>2) { WM_PRINT(x); }
|
||||||
|
#define LOGINFO1(x,y) if(_WIFIMGR_LOGLEVEL_>2) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINTLN(y); }
|
||||||
|
#define LOGINFO2(x,y,z) if(_WIFIMGR_LOGLEVEL_>2) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINT(y); WM_PRINT_SP; WM_PRINTLN(z); }
|
||||||
|
#define LOGINFO3(x,y,z,w) if(_WIFIMGR_LOGLEVEL_>2) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINT(y); WM_PRINT_SP; WM_PRINT(z); WM_PRINT_SP; WM_PRINTLN(w); }
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#define LOGDEBUG(x) if(_WIFIMGR_LOGLEVEL_>3) { WM_PRINT_MARK; WM_PRINTLN(x); }
|
||||||
|
#define LOGDEBUG0(x) if(_WIFIMGR_LOGLEVEL_>3) { WM_PRINT(x); }
|
||||||
|
#define LOGDEBUG1(x,y) if(_WIFIMGR_LOGLEVEL_>3) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINTLN(y); }
|
||||||
|
#define LOGDEBUG2(x,y,z) if(_WIFIMGR_LOGLEVEL_>3) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINT(y); WM_PRINT_SP; WM_PRINTLN(z); }
|
||||||
|
#define LOGDEBUG3(x,y,z,w) if(_WIFIMGR_LOGLEVEL_>3) { WM_PRINT_MARK; WM_PRINT(x); WM_PRINT_SP; WM_PRINT(y); WM_PRINT_SP; WM_PRINT(z); WM_PRINT_SP; WM_PRINTLN(w); }
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#endif //ESP_WiFiManager_Debug_H
|
2150
lib/ESP_WifiManager/utils/TZ.h
Normal file
@ -195,11 +195,7 @@ uint8_t OneWire::reset(void)
|
|||||||
// Write a bit. Port and bit is used to cut lookup time and provide
|
// Write a bit. Port and bit is used to cut lookup time and provide
|
||||||
// more certain timing.
|
// more certain timing.
|
||||||
//
|
//
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
|
||||||
void IRAM_ATTR OneWire::write_bit(uint8_t v)
|
|
||||||
#else
|
|
||||||
void OneWire::write_bit(uint8_t v)
|
void OneWire::write_bit(uint8_t v)
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask;
|
IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask;
|
||||||
volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg;
|
volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg;
|
||||||
@ -227,11 +223,7 @@ void OneWire::write_bit(uint8_t v)
|
|||||||
// Read a bit. Port and bit is used to cut lookup time and provide
|
// Read a bit. Port and bit is used to cut lookup time and provide
|
||||||
// more certain timing.
|
// more certain timing.
|
||||||
//
|
//
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
|
||||||
uint8_t IRAM_ATTR OneWire::read_bit(void)
|
|
||||||
#else
|
|
||||||
uint8_t OneWire::read_bit(void)
|
uint8_t OneWire::read_bit(void)
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask;
|
IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask;
|
||||||
volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg;
|
volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg;
|
||||||
@ -585,4 +577,4 @@ uint16_t OneWire::crc16(const uint8_t* input, uint16_t len, uint16_t crc)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -99,18 +99,10 @@ class OneWire
|
|||||||
|
|
||||||
// Write a bit. The bus is always left powered at the end, see
|
// Write a bit. The bus is always left powered at the end, see
|
||||||
// note in write() about that.
|
// note in write() about that.
|
||||||
#if defined (ARDUINO_ARCH_ESP32)
|
|
||||||
void IRAM_ATTR write_bit(uint8_t v);
|
|
||||||
#else
|
|
||||||
void write_bit(uint8_t v);
|
void write_bit(uint8_t v);
|
||||||
#endif
|
|
||||||
|
|
||||||
// Read a bit.
|
// Read a bit.
|
||||||
#if defined (ARDUINO_ARCH_ESP32)
|
|
||||||
uint8_t IRAM_ATTR read_bit(void);
|
|
||||||
#else
|
|
||||||
uint8_t read_bit(void);
|
uint8_t read_bit(void);
|
||||||
#endif
|
|
||||||
|
|
||||||
// Stop forcing power onto the bus. You only need to do this if
|
// Stop forcing power onto the bus. You only need to do this if
|
||||||
// you used the 'power' flag to write() or used a write_bit() call
|
// you used the 'power' flag to write() or used a write_bit() call
|
||||||
@ -187,4 +179,4 @@ class OneWire
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
#endif // OneWire_h
|
#endif // OneWire_h
|
@ -127,10 +127,14 @@
|
|||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline))
|
||||||
IO_REG_TYPE directRead(IO_REG_TYPE pin)
|
IO_REG_TYPE directRead(IO_REG_TYPE pin)
|
||||||
{
|
{
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
return (GPIO.in.val >> pin) & 0x1;
|
||||||
|
#else // plain ESP32
|
||||||
if ( pin < 32 )
|
if ( pin < 32 )
|
||||||
return (GPIO.in >> pin) & 0x1;
|
return (GPIO.in >> pin) & 0x1;
|
||||||
else if ( pin < 40 )
|
else if ( pin < 46 )
|
||||||
return (GPIO.in1.val >> (pin - 32)) & 0x1;
|
return (GPIO.in1.val >> (pin - 32)) & 0x1;
|
||||||
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -138,38 +142,38 @@ IO_REG_TYPE directRead(IO_REG_TYPE pin)
|
|||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline))
|
||||||
void directWriteLow(IO_REG_TYPE pin)
|
void directWriteLow(IO_REG_TYPE pin)
|
||||||
{
|
{
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
GPIO.out_w1tc.val = ((uint32_t)1 << pin);
|
||||||
|
#else // plain ESP32
|
||||||
if ( pin < 32 )
|
if ( pin < 32 )
|
||||||
GPIO.out_w1tc = ((uint32_t)1 << pin);
|
GPIO.out_w1tc = ((uint32_t)1 << pin);
|
||||||
else if ( pin < 34 )
|
else if ( pin < 46 )
|
||||||
GPIO.out1_w1tc.val = ((uint32_t)1 << (pin - 32));
|
GPIO.out1_w1tc.val = ((uint32_t)1 << (pin - 32));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline))
|
||||||
void directWriteHigh(IO_REG_TYPE pin)
|
void directWriteHigh(IO_REG_TYPE pin)
|
||||||
{
|
{
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
GPIO.out_w1ts.val = ((uint32_t)1 << pin);
|
||||||
|
#else // plain ESP32
|
||||||
if ( pin < 32 )
|
if ( pin < 32 )
|
||||||
GPIO.out_w1ts = ((uint32_t)1 << pin);
|
GPIO.out_w1ts = ((uint32_t)1 << pin);
|
||||||
else if ( pin < 34 )
|
else if ( pin < 46 )
|
||||||
GPIO.out1_w1ts.val = ((uint32_t)1 << (pin - 32));
|
GPIO.out1_w1ts.val = ((uint32_t)1 << (pin - 32));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline))
|
||||||
void directModeInput(IO_REG_TYPE pin)
|
void directModeInput(IO_REG_TYPE pin)
|
||||||
{
|
{
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
GPIO.enable_w1tc.val = ((uint32_t)1 << (pin));
|
||||||
|
#else
|
||||||
if ( digitalPinIsValid(pin) )
|
if ( digitalPinIsValid(pin) )
|
||||||
{
|
{
|
||||||
#if defined(ESP_ARDUINO_VERSION)
|
#if ESP_IDF_VERSION_MAJOR < 4 // IDF 3.x ESP32/PICO-D4
|
||||||
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0)
|
|
||||||
int pin_io = rtc_io_number_get((gpio_num_t)pin);
|
|
||||||
uint32_t rtc_reg(rtc_io_desc[pin_io].reg);
|
|
||||||
|
|
||||||
if ( rtc_reg ) // RTC pins PULL settings
|
|
||||||
{
|
|
||||||
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_io_desc[pin_io].mux);
|
|
||||||
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_io_desc[pin_io].pullup | rtc_io_desc[pin_io].pulldown);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
uint32_t rtc_reg(rtc_gpio_desc[pin].reg);
|
uint32_t rtc_reg(rtc_gpio_desc[pin].reg);
|
||||||
|
|
||||||
if ( rtc_reg ) // RTC pins PULL settings
|
if ( rtc_reg ) // RTC pins PULL settings
|
||||||
@ -177,40 +181,25 @@ void directModeInput(IO_REG_TYPE pin)
|
|||||||
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].mux);
|
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].mux);
|
||||||
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].pullup | rtc_gpio_desc[pin].pulldown);
|
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].pullup | rtc_gpio_desc[pin].pulldown);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
// Input
|
||||||
if ( pin < 32 )
|
if ( pin < 32 )
|
||||||
GPIO.enable_w1tc = ((uint32_t)1 << pin);
|
GPIO.enable_w1tc = ((uint32_t)1 << pin);
|
||||||
else
|
else
|
||||||
GPIO.enable1_w1tc.val = ((uint32_t)1 << (pin - 32));
|
GPIO.enable1_w1tc.val = ((uint32_t)1 << (pin - 32));
|
||||||
|
|
||||||
uint32_t pinFunction((uint32_t)2 << FUN_DRV_S); // what are the drivers?
|
|
||||||
pinFunction |= FUN_IE; // input enable but required for output as well?
|
|
||||||
pinFunction |= ((uint32_t)2 << MCU_SEL_S);
|
|
||||||
|
|
||||||
ESP_REG(DR_REG_IO_MUX_BASE + esp32_gpioMux[pin].reg) = pinFunction;
|
|
||||||
|
|
||||||
GPIO.pin[pin].val = 0;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline))
|
||||||
void directModeOutput(IO_REG_TYPE pin)
|
void directModeOutput(IO_REG_TYPE pin)
|
||||||
{
|
{
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
GPIO.enable_w1ts.val = ((uint32_t)1 << (pin));
|
||||||
|
#else
|
||||||
if ( digitalPinIsValid(pin) && pin <= 33 ) // pins above 33 can be only inputs
|
if ( digitalPinIsValid(pin) && pin <= 33 ) // pins above 33 can be only inputs
|
||||||
{
|
{
|
||||||
#if defined(ESP_ARDUINO_VERSION)
|
#if ESP_IDF_VERSION_MAJOR < 4 // IDF 3.x ESP32/PICO-D4
|
||||||
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0)
|
|
||||||
int pin_io = rtc_io_number_get((gpio_num_t)pin);
|
|
||||||
uint32_t rtc_reg(rtc_io_desc[pin_io].reg);
|
|
||||||
|
|
||||||
if ( rtc_reg ) // RTC pins PULL settings
|
|
||||||
{
|
|
||||||
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_io_desc[pin_io].mux);
|
|
||||||
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_io_desc[pin_io].pullup | rtc_io_desc[pin_io].pulldown);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
uint32_t rtc_reg(rtc_gpio_desc[pin].reg);
|
uint32_t rtc_reg(rtc_gpio_desc[pin].reg);
|
||||||
|
|
||||||
if ( rtc_reg ) // RTC pins PULL settings
|
if ( rtc_reg ) // RTC pins PULL settings
|
||||||
@ -218,21 +207,14 @@ void directModeOutput(IO_REG_TYPE pin)
|
|||||||
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].mux);
|
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].mux);
|
||||||
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].pullup | rtc_gpio_desc[pin].pulldown);
|
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].pullup | rtc_gpio_desc[pin].pulldown);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
// Output
|
||||||
if ( pin < 32 )
|
if ( pin < 32 )
|
||||||
GPIO.enable_w1ts = ((uint32_t)1 << pin);
|
GPIO.enable_w1ts = ((uint32_t)1 << pin);
|
||||||
else // already validated to pins <= 33
|
else // already validated to pins <= 33
|
||||||
GPIO.enable1_w1ts.val = ((uint32_t)1 << (pin - 32));
|
GPIO.enable1_w1ts.val = ((uint32_t)1 << (pin - 32));
|
||||||
|
|
||||||
uint32_t pinFunction((uint32_t)2 << FUN_DRV_S); // what are the drivers?
|
|
||||||
pinFunction |= FUN_IE; // input enable but required for output as well?
|
|
||||||
pinFunction |= ((uint32_t)2 << MCU_SEL_S);
|
|
||||||
|
|
||||||
ESP_REG(DR_REG_IO_MUX_BASE + esp32_gpioMux[pin].reg) = pinFunction;
|
|
||||||
|
|
||||||
GPIO.pin[pin].val = 0;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DIRECT_READ(base, pin) directRead(pin)
|
#define DIRECT_READ(base, pin) directRead(pin)
|
||||||
@ -445,6 +427,20 @@ void directWriteHigh(IO_REG_TYPE mask)
|
|||||||
#define DIRECT_MODE_INPUT(base, mask) directModeInput(mask)
|
#define DIRECT_MODE_INPUT(base, mask) directModeInput(mask)
|
||||||
#define DIRECT_MODE_OUTPUT(base, mask) directModeOutput(mask)
|
#define DIRECT_MODE_OUTPUT(base, mask) directModeOutput(mask)
|
||||||
|
|
||||||
|
#elif defined(ARDUINO_ARCH_MBED_RP2040)|| defined(ARDUINO_ARCH_RP2040)
|
||||||
|
#define delayMicroseconds(time) busy_wait_us(time)
|
||||||
|
#define PIN_TO_BASEREG(pin) (0)
|
||||||
|
#define PIN_TO_BITMASK(pin) (pin)
|
||||||
|
#define IO_REG_TYPE unsigned int
|
||||||
|
#define IO_REG_BASE_ATTR
|
||||||
|
#define IO_REG_MASK_ATTR
|
||||||
|
#define DIRECT_READ(base, pin) digitalRead(pin)
|
||||||
|
#define DIRECT_WRITE_LOW(base, pin) digitalWrite(pin, LOW)
|
||||||
|
#define DIRECT_WRITE_HIGH(base, pin) digitalWrite(pin, HIGH)
|
||||||
|
#define DIRECT_MODE_INPUT(base, pin) pinMode(pin,INPUT)
|
||||||
|
#define DIRECT_MODE_OUTPUT(base, pin) pinMode(pin,OUTPUT)
|
||||||
|
#warning "OneWire. RP2040 in Fallback mode. Using API calls for pinMode,digitalRead and digitalWrite."
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define PIN_TO_BASEREG(pin) (0)
|
#define PIN_TO_BASEREG(pin) (0)
|
||||||
#define PIN_TO_BITMASK(pin) (pin)
|
#define PIN_TO_BITMASK(pin) (pin)
|
||||||
@ -460,4 +456,4 @@ void directWriteHigh(IO_REG_TYPE mask)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -282,7 +282,8 @@ int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8
|
|||||||
useWire->beginTransmission(devAddr);
|
useWire->beginTransmission(devAddr);
|
||||||
useWire->write(regAddr);
|
useWire->write(regAddr);
|
||||||
useWire->endTransmission();
|
useWire->endTransmission();
|
||||||
useWire->beginTransmission(devAddr);
|
// See: https://github.com/espressif/arduino-esp32/issues/6674
|
||||||
|
// useWire->beginTransmission(devAddr);
|
||||||
useWire->requestFrom((uint8_t)devAddr, (uint8_t)min((int)length - k, I2CDEVLIB_WIRE_BUFFER_LENGTH));
|
useWire->requestFrom((uint8_t)devAddr, (uint8_t)min((int)length - k, I2CDEVLIB_WIRE_BUFFER_LENGTH));
|
||||||
|
|
||||||
for (; useWire->available() && (timeout == 0 || millis() - t1 < timeout); count++) {
|
for (; useWire->available() && (timeout == 0 || millis() - t1 < timeout); count++) {
|
||||||
@ -1485,4 +1486,4 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT;
|
|||||||
return rxBufferLength - rxBufferIndex;
|
return rxBufferLength - rxBufferIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -308,4 +308,4 @@ class I2Cdev {
|
|||||||
|
|
||||||
#endif // I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE
|
#endif // I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE
|
||||||
|
|
||||||
#endif /* _I2CDEV_H_ */
|
#endif /* _I2CDEV_H_ */
|
@ -66,7 +66,7 @@ void MPU6050_Base::initialize() {
|
|||||||
* @return True if connection is valid, false otherwise
|
* @return True if connection is valid, false otherwise
|
||||||
*/
|
*/
|
||||||
bool MPU6050_Base::testConnection() {
|
bool MPU6050_Base::testConnection() {
|
||||||
return getDeviceID() == 0x34 || getDeviceID() == 0x38; // Allow both MPU6050 and MPU6000
|
return getDeviceID() == 0x34;
|
||||||
}
|
}
|
||||||
|
|
||||||
// AUX_VDDIO register (InvenSense demo code calls this RA_*G_OFFS_TC)
|
// AUX_VDDIO register (InvenSense demo code calls this RA_*G_OFFS_TC)
|
||||||
@ -2556,8 +2556,8 @@ void MPU6050_Base::setClockSource(uint8_t source) {
|
|||||||
* -------------+------------------
|
* -------------+------------------
|
||||||
* 0 | 1.25 Hz
|
* 0 | 1.25 Hz
|
||||||
* 1 | 2.5 Hz
|
* 1 | 2.5 Hz
|
||||||
* 2 | 5 Hz
|
* 2 | 20 Hz
|
||||||
* 3 | 10 Hz
|
* 3 | 40 Hz
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* For further information regarding the MPU-60X0's power modes, please refer to
|
* For further information regarding the MPU-60X0's power modes, please refer to
|
||||||
@ -3371,25 +3371,27 @@ void MPU6050_Base::PID(uint8_t ReadAddress, float kP,float kI, uint8_t Loops){
|
|||||||
resetDMP();
|
resetDMP();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPU6050_Base::PrintActiveOffsets() {
|
int16_t * MPU6050_Base::GetActiveOffsets() {
|
||||||
uint8_t AOffsetRegister = (getDeviceID() < 0x38 )? MPU6050_RA_XA_OFFS_H:0x77;
|
uint8_t AOffsetRegister = (getDeviceID() < 0x38 )? MPU6050_RA_XA_OFFS_H:0x77;
|
||||||
int16_t Data[3];
|
if(AOffsetRegister == 0x06) I2Cdev::readWords(devAddr, AOffsetRegister, 3, (uint16_t *)offsets, I2Cdev::readTimeout, wireObj);
|
||||||
//Serial.print(F("Offset Register 0x"));
|
else {
|
||||||
//Serial.print(AOffsetRegister>>4,HEX);Serial.print(AOffsetRegister&0x0F,HEX);
|
I2Cdev::readWords(devAddr, AOffsetRegister, 1, (uint16_t *)offsets, I2Cdev::readTimeout, wireObj);
|
||||||
Serial.print(F("\n// X Accel Y Accel Z Accel X Gyro Y Gyro Z Gyro\n// OFFSETS "));
|
I2Cdev::readWords(devAddr, AOffsetRegister+3, 1, (uint16_t *)(offsets+1), I2Cdev::readTimeout, wireObj);
|
||||||
if(AOffsetRegister == 0x06) I2Cdev::readWords(devAddr, AOffsetRegister, 3, (uint16_t *)Data, I2Cdev::readTimeout, wireObj);
|
I2Cdev::readWords(devAddr, AOffsetRegister+6, 1, (uint16_t *)(offsets+2), I2Cdev::readTimeout, wireObj);
|
||||||
else {
|
}
|
||||||
I2Cdev::readWords(devAddr, AOffsetRegister, 1, (uint16_t *)Data, I2Cdev::readTimeout, wireObj);
|
I2Cdev::readWords(devAddr, 0x13, 3, (uint16_t *)(offsets+3), I2Cdev::readTimeout, wireObj);
|
||||||
I2Cdev::readWords(devAddr, AOffsetRegister+3, 1, (uint16_t *)Data+1, I2Cdev::readTimeout, wireObj);
|
return offsets;
|
||||||
I2Cdev::readWords(devAddr, AOffsetRegister+6, 1, (uint16_t *)Data+2, I2Cdev::readTimeout, wireObj);
|
|
||||||
}
|
|
||||||
// A_OFFSET_H_READ_A_OFFS(Data);
|
|
||||||
Serial.print((float)Data[0], 5); Serial.print(", ");
|
|
||||||
Serial.print((float)Data[1], 5); Serial.print(", ");
|
|
||||||
Serial.print((float)Data[2], 5); Serial.print(", ");
|
|
||||||
I2Cdev::readWords(devAddr, 0x13, 3, (uint16_t *)Data, I2Cdev::readTimeout, wireObj);
|
|
||||||
// XG_OFFSET_H_READ_OFFS_USR(Data);
|
|
||||||
Serial.print((float)Data[0], 5); Serial.print(", ");
|
|
||||||
Serial.print((float)Data[1], 5); Serial.print(", ");
|
|
||||||
Serial.print((float)Data[2], 5); Serial.print("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MPU6050_Base::PrintActiveOffsets() {
|
||||||
|
GetActiveOffsets();
|
||||||
|
// A_OFFSET_H_READ_A_OFFS(Data);
|
||||||
|
Serial.print((float)offsets[0], 5); Serial.print(",\t");
|
||||||
|
Serial.print((float)offsets[1], 5); Serial.print(",\t");
|
||||||
|
Serial.print((float)offsets[2], 5); Serial.print(",\t");
|
||||||
|
|
||||||
|
// XG_OFFSET_H_READ_OFFS_USR(Data);
|
||||||
|
Serial.print((float)offsets[3], 5); Serial.print(",\t");
|
||||||
|
Serial.print((float)offsets[4], 5); Serial.print(",\t");
|
||||||
|
Serial.print((float)offsets[5], 5); Serial.print("\n\n");
|
||||||
|
}
|
@ -832,12 +832,16 @@ class MPU6050_Base {
|
|||||||
void CalibrateAccel(uint8_t Loops = 15);// Fine tune after setting offsets with less Loops.
|
void CalibrateAccel(uint8_t Loops = 15);// Fine tune after setting offsets with less Loops.
|
||||||
void PID(uint8_t ReadAddress, float kP,float kI, uint8_t Loops); // Does the math
|
void PID(uint8_t ReadAddress, float kP,float kI, uint8_t Loops); // Does the math
|
||||||
void PrintActiveOffsets(); // See the results of the Calibration
|
void PrintActiveOffsets(); // See the results of the Calibration
|
||||||
|
int16_t * GetActiveOffsets();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint8_t devAddr;
|
uint8_t devAddr;
|
||||||
void *wireObj;
|
void *wireObj;
|
||||||
uint8_t buffer[14];
|
uint8_t buffer[14];
|
||||||
uint32_t fifoTimeout = MPU6050_FIFO_DEFAULT_TIMEOUT;
|
uint32_t fifoTimeout = MPU6050_FIFO_DEFAULT_TIMEOUT;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int16_t offsets[6];
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef I2CDEVLIB_MPU6050_TYPEDEF
|
#ifndef I2CDEVLIB_MPU6050_TYPEDEF
|
||||||
@ -845,4 +849,4 @@ class MPU6050_Base {
|
|||||||
typedef MPU6050_Base MPU6050;
|
typedef MPU6050_Base MPU6050;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* _MPU6050_H_ */
|
#endif /* _MPU6050_H_ */
|
@ -15,11 +15,12 @@ include_dir = lib
|
|||||||
[common_env_data]
|
[common_env_data]
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
platform = espressif8266 @ 3.2.0
|
platform = espressif8266 @ 4.0.1
|
||||||
framework = arduino
|
framework = arduino
|
||||||
board = d1_mini
|
board = d1_mini
|
||||||
build_unflags =
|
build_unflags =
|
||||||
build_flags =
|
build_flags =
|
||||||
|
-Wl,-Map,output.map
|
||||||
-D BAUD=${common_env_data.monitor_speed}
|
-D BAUD=${common_env_data.monitor_speed}
|
||||||
-D ACTIVATE_OTA
|
-D ACTIVATE_OTA
|
||||||
#-D DEBUG_ESP_HTTP_CLIENT
|
#-D DEBUG_ESP_HTTP_CLIENT
|
||||||
@ -33,16 +34,16 @@ build_flags =
|
|||||||
-D EMBED_HTML # If this is not used the html files needs to be on the file system (can be uploaded)
|
-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=\""\"" # =\""myssid\""
|
||||||
-D USER_SSID_PWD=\""\"" # =\""mypwd\""
|
-D USER_SSID_PWD=\""\"" # =\""mypwd\""
|
||||||
-D CFG_APPVER="\"1.0.0\""
|
-D CFG_APPVER="\"1.1.1\""
|
||||||
#-D CFG_GITREV=\""beta-2\""
|
#-D CFG_GITREV=\""beta-4\""
|
||||||
!python script/git_rev.py
|
!python script/git_rev.py
|
||||||
lib_deps = # Switched to forks for better version control.
|
lib_deps = # Switched to forks for better version control.
|
||||||
# Using local copy of these libraries
|
# Using local copy of these libraries
|
||||||
#https://github.com/jrowberg/i2cdevlib.git#<document>
|
#https://github.com/mp-se/i2cdevlib.git#<document>
|
||||||
#https://github.com/PaulStoffregen/OneWire
|
#https://github.com/mp-se/OneWire # Using this version; https://github.com/arendst/Tasmota/tree/development/lib/lib_basic/OneWire-Stickbreaker
|
||||||
#https://github.com/milesburton/Arduino-Temperature-Control-Library
|
#https://github.com/mp-se/Arduino-Temperature-Control-Library
|
||||||
https://github.com/mp-se/ESP_WiFiManager#v1.9.0 # https://github.com/khoih-prog/ESP_WiFiManager
|
#https://github.com/khoih-prog/ESP_WiFiManager
|
||||||
https://github.com/mp-se/ESP_DoubleResetDetector#v1.2.1 # https://github.com/khoih-prog/ESP_DoubleResetDetector
|
#https://github.com/khoih-prog/ESP_DoubleResetDetector
|
||||||
https://github.com/mp-se/tinyexpr # https://github.com/codeplea/tinyexpr
|
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/incbin # https://github.com/graphitemaster/incbin
|
||||||
https://github.com/mp-se/Arduino-Log#1.1.1 # https://github.com/thijse/Arduino-Log
|
https://github.com/mp-se/Arduino-Log#1.1.1 # https://github.com/thijse/Arduino-Log
|
||||||
@ -62,9 +63,8 @@ extra_scripts =
|
|||||||
build_unflags =
|
build_unflags =
|
||||||
${common_env_data.build_unflags}
|
${common_env_data.build_unflags}
|
||||||
build_flags =
|
build_flags =
|
||||||
-Wl,-Map,output.map
|
|
||||||
${common_env_data.build_flags}
|
${common_env_data.build_flags}
|
||||||
#-D PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS
|
-D PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS
|
||||||
#-D SKIP_SLEEPMODE
|
#-D SKIP_SLEEPMODE
|
||||||
#-D DOUBLERESETDETECTOR_DEBUG=true
|
#-D DOUBLERESETDETECTOR_DEBUG=true
|
||||||
#-D FORCE_GRAVITY_MODE # used to debug gravity mode
|
#-D FORCE_GRAVITY_MODE # used to debug gravity mode
|
||||||
@ -128,7 +128,7 @@ board_build.filesystem = littlefs
|
|||||||
|
|
||||||
[env:gravity32-release]
|
[env:gravity32-release]
|
||||||
framework = arduino
|
framework = arduino
|
||||||
platform = espressif32 @ 3.5.0
|
platform = espressif32 @ 5.0.0
|
||||||
upload_speed = ${common_env_data.upload_speed}
|
upload_speed = ${common_env_data.upload_speed}
|
||||||
monitor_speed = ${common_env_data.monitor_speed}
|
monitor_speed = ${common_env_data.monitor_speed}
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
@ -153,8 +153,8 @@ build_flags =
|
|||||||
-D MAIN_DISABLE_LOGGING
|
-D MAIN_DISABLE_LOGGING
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common_env_data.lib_deps}
|
${common_env_data.lib_deps}
|
||||||
https://github.com/lorol/LITTLEFS#1.0.6
|
|
||||||
https://github.com/mp-se/NimBLE-Arduino#1.3.8 # https://github.com/h2zero/NimBLE-Arduino
|
https://github.com/mp-se/NimBLE-Arduino#1.3.8 # https://github.com/h2zero/NimBLE-Arduino
|
||||||
|
lib_ignore =
|
||||||
board = featheresp32
|
board = featheresp32
|
||||||
build_type = release
|
build_type = release
|
||||||
board_build.partitions = part32.csv
|
board_build.partitions = part32.csv
|
||||||
@ -163,7 +163,7 @@ monitor_filters = esp32_exception_decoder
|
|||||||
|
|
||||||
[env:gravity32-perf]
|
[env:gravity32-perf]
|
||||||
framework = arduino
|
framework = arduino
|
||||||
platform = espressif32 @ 3.5.0
|
platform = espressif32 @ 5.0.0
|
||||||
upload_speed = ${common_env_data.upload_speed}
|
upload_speed = ${common_env_data.upload_speed}
|
||||||
monitor_speed = ${common_env_data.monitor_speed}
|
monitor_speed = ${common_env_data.monitor_speed}
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
@ -179,35 +179,9 @@ build_flags =
|
|||||||
-D LOG_LEVEL=5
|
-D LOG_LEVEL=5
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common_env_data.lib_deps}
|
${common_env_data.lib_deps}
|
||||||
https://github.com/lorol/LITTLEFS#1.0.6
|
|
||||||
https://github.com/mp-se/NimBLE-Arduino#1.3.8 # https://github.com/h2zero/NimBLE-Arduino
|
https://github.com/mp-se/NimBLE-Arduino#1.3.8 # https://github.com/h2zero/NimBLE-Arduino
|
||||||
board = featheresp32
|
board = featheresp32
|
||||||
build_type = release
|
build_type = release
|
||||||
board_build.partitions = part32.csv
|
board_build.partitions = part32.csv
|
||||||
board_build.filesystem = littlefs
|
board_build.filesystem = littlefs
|
||||||
monitor_filters = esp32_exception_decoder
|
monitor_filters = esp32_exception_decoder
|
||||||
|
|
||||||
#[env:gravity32-release2]
|
|
||||||
#framework = arduino
|
|
||||||
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip # build fails
|
|
||||||
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.2/platform-tasmota-espressif32-2.0.2.zip
|
|
||||||
#platform = espressif32 @ 4.1.0
|
|
||||||
#upload_speed = ${common_env_data.upload_speed}
|
|
||||||
#monitor_speed = ${common_env_data.monitor_speed}
|
|
||||||
#extra_scripts =
|
|
||||||
# script/copy_html.py
|
|
||||||
# script/copy_firmware.py
|
|
||||||
# script/create_versionjson.py
|
|
||||||
#build_unflags =
|
|
||||||
# ${common_env_data.build_unflags}
|
|
||||||
#build_flags =
|
|
||||||
# ${common_env_data.build_flags}
|
|
||||||
# -D ESPRESSIF32_20 # v2.0 framework
|
|
||||||
# -D LOG_LEVEL=5
|
|
||||||
#lib_deps =
|
|
||||||
# ${common_env_data.lib_deps}
|
|
||||||
# https://github.com/mp-se/NimBLE-Arduino#1.3.8 # https://github.com/h2zero/NimBLE-Arduino
|
|
||||||
#board = featheresp32
|
|
||||||
#build_type = release
|
|
||||||
#board_build.partitions = part32.csv
|
|
||||||
#board_build.filesystem = littlefs
|
|
||||||
|
47
src/calc.cpp
@ -52,8 +52,7 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (noAngles < 3) {
|
if (noAngles < 3) {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("CALC: Not enough values for deriving formula");
|
||||||
errLog.addEntry(F("CALC: Not enough values for deriving formula"));
|
|
||||||
return ERR_FORMULA_NOTENOUGHVALUES;
|
return ERR_FORMULA_NOTENOUGHVALUES;
|
||||||
} else {
|
} else {
|
||||||
double coeffs[order + 1];
|
double coeffs[order + 1];
|
||||||
@ -98,18 +97,15 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer,
|
|||||||
|
|
||||||
// If the deviation is more than 2 degress we mark it as failed.
|
// If the deviation is more than 2 degress we mark it as failed.
|
||||||
if (dev * 1000 > myAdvancedConfig.getMaxFormulaCreationDeviation()) {
|
if (dev * 1000 > myAdvancedConfig.getMaxFormulaCreationDeviation()) {
|
||||||
char s[20];
|
writeErrorLog(
|
||||||
snprintf(&s[0], sizeof(s), "%.8f", dev);
|
"CALC: Validation failed on angle %.2f, deviation too large %.4f "
|
||||||
Log.error(F("CALC: Deviation to large: %s" CR), &s[0]);
|
"SG, formula order %d",
|
||||||
|
fd.a[i], dev * 1000, order);
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
ErrorFileLog errLog;
|
|
||||||
errLog.addEntry(
|
|
||||||
F("CALC: Error validating created formula. Deviation to large, "
|
|
||||||
"formula rejected."));
|
|
||||||
return ERR_FORMULA_UNABLETOFFIND;
|
return ERR_FORMULA_UNABLETOFFIND;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,8 +114,7 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("CALC: Internal error finding formula.");
|
||||||
errLog.addEntry(F("CALC: Internal error finding formula."));
|
|
||||||
return ERR_FORMULA_INTERNAL;
|
return ERR_FORMULA_INTERNAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,8 +159,7 @@ double calculateGravity(double angle, double temp, const char *tempFormula) {
|
|||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("CALC: Failed to parse gravity expression %d", err);
|
||||||
errLog.addEntry("CALC: Failed to parse gravity expression " + String(err));
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,16 +169,15 @@ double calculateGravity(double angle, double temp, const char *tempFormula) {
|
|||||||
//
|
//
|
||||||
// Source: https://homebrewacademy.com/hydrometer-temperature-correction/
|
// Source: https://homebrewacademy.com/hydrometer-temperature-correction/
|
||||||
//
|
//
|
||||||
double gravityTemperatureCorrectionC(double gravity, double tempC,
|
double gravityTemperatureCorrectionC(double gravitySG, double tempC,
|
||||||
double calTempC) {
|
double calTempC) {
|
||||||
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)
|
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)
|
||||||
Log.verbose(F("CALC: Adjusting gravity based on temperature, gravity %F, "
|
Log.verbose(F("CALC: Adjusting gravity based on temperature, gravity %F, "
|
||||||
"temp %F, calTemp %F." CR),
|
"temp %F, calTemp %F." CR),
|
||||||
gravity, tempC, calTempC);
|
gravitySG, tempC, calTempC);
|
||||||
#endif
|
#endif
|
||||||
// float tempF = convertCtoF(tempC);
|
double tempF = convertCtoF(tempC);
|
||||||
// float calTempF = convertCtoF(calTempC);
|
double calTempF = convertCtoF(calTempC);
|
||||||
|
|
||||||
const char *formula =
|
const char *formula =
|
||||||
"gravity*((1.00130346-0.000134722124*temp+0.00000204052596*temp^2-0."
|
"gravity*((1.00130346-0.000134722124*temp+0.00000204052596*temp^2-0."
|
||||||
"00000000232820948*temp^3)/"
|
"00000000232820948*temp^3)/"
|
||||||
@ -193,7 +186,7 @@ double gravityTemperatureCorrectionC(double gravity, double tempC,
|
|||||||
|
|
||||||
// Store variable names and pointers.
|
// Store variable names and pointers.
|
||||||
te_variable vars[] = {
|
te_variable vars[] = {
|
||||||
{"gravity", &gravity}, {"temp", &tempC}, {"cal", &calTempC}};
|
{"gravity", &gravitySG}, {"temp", &tempF}, {"cal", &calTempF}};
|
||||||
|
|
||||||
int err;
|
int err;
|
||||||
// Compile the expression with variables.
|
// Compile the expression with variables.
|
||||||
@ -204,18 +197,18 @@ double gravityTemperatureCorrectionC(double gravity, double tempC,
|
|||||||
te_free(expr);
|
te_free(expr);
|
||||||
|
|
||||||
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)
|
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)
|
||||||
char s[10];
|
char s[80];
|
||||||
snprintf(&s[0], sizeof(s), "%.8f", g);
|
snprintf(&s[0], sizeof(s), "Corrected gravity=%.8f, input gravity=%.8f", g,
|
||||||
Log.verbose(F("CALC: Corrected gravity is %s." CR), &s[0]);
|
gravitySG);
|
||||||
|
Log.verbose(F("CALC: %s." CR), &s[0]);
|
||||||
#endif
|
#endif
|
||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorFileLog errLog;
|
writeErrorLog(
|
||||||
errLog.addEntry(
|
"CALC: Failed to parse expression for gravity temperature correction %d",
|
||||||
"CALC: Failed to parse expression for gravity temperature correction " +
|
err);
|
||||||
String(err));
|
return gravitySG;
|
||||||
return gravity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EOF
|
// EOF
|
||||||
|
@ -32,9 +32,8 @@ SOFTWARE.
|
|||||||
|
|
||||||
double calculateGravity(double angle, double tempC,
|
double calculateGravity(double angle, double tempC,
|
||||||
const char *tempFormula = 0);
|
const char *tempFormula = 0);
|
||||||
double gravityTemperatureCorrectionC(
|
double gravityTemperatureCorrectionC(double gravity, double tempC,
|
||||||
double gravity, double tempC,
|
double calTempC);
|
||||||
double calTempC = myAdvancedConfig.getDefaultCalibrationTemp());
|
|
||||||
int createFormula(RawFormulaData &fd, char *formulaBuffer,
|
int createFormula(RawFormulaData &fd, char *formulaBuffer,
|
||||||
int formulaBufferSize, int order);
|
int formulaBufferSize, int order);
|
||||||
|
|
||||||
|
@ -86,12 +86,14 @@ void Config::createJson(DynamicJsonDocument& doc) {
|
|||||||
doc[PARAM_PUSH_MQTT_USER] = getMqttUser();
|
doc[PARAM_PUSH_MQTT_USER] = getMqttUser();
|
||||||
doc[PARAM_PUSH_MQTT_PASS] = getMqttPass();
|
doc[PARAM_PUSH_MQTT_PASS] = getMqttPass();
|
||||||
doc[PARAM_SLEEP_INTERVAL] = getSleepInterval();
|
doc[PARAM_SLEEP_INTERVAL] = getSleepInterval();
|
||||||
doc[PARAM_VOLTAGEFACTOR] = getVoltageFactor();
|
doc[PARAM_VOLTAGE_FACTOR] = getVoltageFactor();
|
||||||
|
doc[PARAM_VOLTAGE_CONFIG] = getVoltageConfig();
|
||||||
doc[PARAM_GRAVITY_FORMULA] = getGravityFormula();
|
doc[PARAM_GRAVITY_FORMULA] = getGravityFormula();
|
||||||
doc[PARAM_GRAVITY_FORMAT] = String(getGravityFormat());
|
doc[PARAM_GRAVITY_FORMAT] = String(getGravityFormat());
|
||||||
doc[PARAM_TEMP_ADJ] = getTempSensorAdjC();
|
doc[PARAM_TEMP_ADJ] = getTempSensorAdjC();
|
||||||
doc[PARAM_GRAVITY_TEMP_ADJ] = isGravityTempAdj();
|
doc[PARAM_GRAVITY_TEMP_ADJ] = isGravityTempAdj();
|
||||||
doc[PARAM_GYRO_TEMP] = isGyroTemp();
|
doc[PARAM_GYRO_TEMP] = isGyroTemp();
|
||||||
|
doc[PARAM_STORAGE_SLEEP] = isStorageSleep();
|
||||||
|
|
||||||
JsonObject cal = doc.createNestedObject(PARAM_GYRO_CALIBRATION);
|
JsonObject cal = doc.createNestedObject(PARAM_GYRO_CALIBRATION);
|
||||||
cal["ax"] = _gyroCalibration.ax;
|
cal["ax"] = _gyroCalibration.ax;
|
||||||
@ -143,12 +145,11 @@ bool Config::saveFile() {
|
|||||||
File configFile = LittleFS.open(CFG_FILENAME, "w");
|
File configFile = LittleFS.open(CFG_FILENAME, "w");
|
||||||
|
|
||||||
if (!configFile) {
|
if (!configFile) {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("CFG : Failed to save configuration.");
|
||||||
errLog.addEntry(F("CFG : Failed to save configuration."));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicJsonDocument doc(CFG_JSON_BUFSIZE);
|
DynamicJsonDocument doc(3000);
|
||||||
createJson(doc);
|
createJson(doc);
|
||||||
|
|
||||||
#if LOG_LEVEL == 6 && !defined(DISABLE_LOGGING)
|
#if LOG_LEVEL == 6 && !defined(DISABLE_LOGGING)
|
||||||
@ -174,23 +175,21 @@ bool Config::loadFile() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!LittleFS.exists(CFG_FILENAME)) {
|
if (!LittleFS.exists(CFG_FILENAME)) {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("CFG : Configuration file does not exist.");
|
||||||
errLog.addEntry(F("CFG : Configuration file does not exist."));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
File configFile = LittleFS.open(CFG_FILENAME, "r");
|
File configFile = LittleFS.open(CFG_FILENAME, "r");
|
||||||
|
|
||||||
if (!configFile) {
|
if (!configFile) {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("CFG : Failed to load configuration.");
|
||||||
errLog.addEntry(F("CFG : Failed to load configuration."));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.notice(F("CFG : Size of configuration file=%d bytes." CR),
|
Log.notice(F("CFG : Size of configuration file=%d bytes." CR),
|
||||||
configFile.size());
|
configFile.size());
|
||||||
|
|
||||||
DynamicJsonDocument doc(CFG_JSON_BUFSIZE);
|
DynamicJsonDocument doc(3000);
|
||||||
DeserializationError err = deserializeJson(doc, configFile);
|
DeserializationError err = deserializeJson(doc, configFile);
|
||||||
#if LOG_LEVEL == 6
|
#if LOG_LEVEL == 6
|
||||||
serializeJson(doc, Serial);
|
serializeJson(doc, Serial);
|
||||||
@ -199,8 +198,7 @@ bool Config::loadFile() {
|
|||||||
configFile.close();
|
configFile.close();
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("CFG : Failed to parse configuration (json)");
|
||||||
errLog.addEntry(F("CFG : Failed to parse configuration (json)"));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,14 +251,18 @@ bool Config::loadFile() {
|
|||||||
|
|
||||||
if (!doc[PARAM_SLEEP_INTERVAL].isNull())
|
if (!doc[PARAM_SLEEP_INTERVAL].isNull())
|
||||||
setSleepInterval(doc[PARAM_SLEEP_INTERVAL].as<int>());
|
setSleepInterval(doc[PARAM_SLEEP_INTERVAL].as<int>());
|
||||||
if (!doc[PARAM_VOLTAGEFACTOR].isNull())
|
if (!doc[PARAM_VOLTAGE_FACTOR].isNull())
|
||||||
setVoltageFactor(doc[PARAM_VOLTAGEFACTOR].as<float>());
|
setVoltageFactor(doc[PARAM_VOLTAGE_FACTOR].as<float>());
|
||||||
|
if (!doc[PARAM_VOLTAGE_CONFIG].isNull())
|
||||||
|
setVoltageConfig(doc[PARAM_VOLTAGE_CONFIG].as<float>());
|
||||||
if (!doc[PARAM_GRAVITY_FORMULA].isNull())
|
if (!doc[PARAM_GRAVITY_FORMULA].isNull())
|
||||||
setGravityFormula(doc[PARAM_GRAVITY_FORMULA]);
|
setGravityFormula(doc[PARAM_GRAVITY_FORMULA]);
|
||||||
if (!doc[PARAM_GRAVITY_TEMP_ADJ].isNull())
|
if (!doc[PARAM_GRAVITY_TEMP_ADJ].isNull())
|
||||||
setGravityTempAdj(doc[PARAM_GRAVITY_TEMP_ADJ].as<bool>());
|
setGravityTempAdj(doc[PARAM_GRAVITY_TEMP_ADJ].as<bool>());
|
||||||
if (!doc[PARAM_GYRO_TEMP].isNull())
|
if (!doc[PARAM_GYRO_TEMP].isNull())
|
||||||
setGyroTemp(doc[PARAM_GYRO_TEMP].as<bool>());
|
setGyroTemp(doc[PARAM_GYRO_TEMP].as<bool>());
|
||||||
|
if (!doc[PARAM_STORAGE_SLEEP].isNull())
|
||||||
|
setStorageSleep(doc[PARAM_STORAGE_SLEEP].as<bool>());
|
||||||
if (!doc[PARAM_GRAVITY_FORMAT].isNull()) {
|
if (!doc[PARAM_GRAVITY_FORMAT].isNull()) {
|
||||||
String s = doc[PARAM_GRAVITY_FORMAT];
|
String s = doc[PARAM_GRAVITY_FORMAT];
|
||||||
setGravityFormat(s.charAt(0));
|
setGravityFormat(s.charAt(0));
|
||||||
@ -371,8 +373,7 @@ bool AdvancedConfig::saveFile() {
|
|||||||
File configFile = LittleFS.open(CFG_HW_FILENAME, "w");
|
File configFile = LittleFS.open(CFG_HW_FILENAME, "w");
|
||||||
|
|
||||||
if (!configFile) {
|
if (!configFile) {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("CFG : Failed to write hardware configuration ");
|
||||||
errLog.addEntry(F("CFG : Failed to write hardware configuration "));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,8 +424,7 @@ bool AdvancedConfig::loadFile() {
|
|||||||
File configFile = LittleFS.open(CFG_HW_FILENAME, "r");
|
File configFile = LittleFS.open(CFG_HW_FILENAME, "r");
|
||||||
|
|
||||||
if (!configFile) {
|
if (!configFile) {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("CFG : Failed to read hardware configuration");
|
||||||
errLog.addEntry(F("CFG : Failed to read hardware configuration "));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,8 +440,7 @@ bool AdvancedConfig::loadFile() {
|
|||||||
configFile.close();
|
configFile.close();
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("CFG : Failed to parse hardware configuration (json)");
|
||||||
errLog.addEntry(F("CFG : Failed to parse hardware configuration (json)"));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,8 +27,6 @@ SOFTWARE.
|
|||||||
#include <helper.hpp>
|
#include <helper.hpp>
|
||||||
#include <resources.hpp>
|
#include <resources.hpp>
|
||||||
|
|
||||||
#define CFG_JSON_BUFSIZE 3192
|
|
||||||
|
|
||||||
#define CFG_APPNAME "GravityMon" // Name of firmware
|
#define CFG_APPNAME "GravityMon" // Name of firmware
|
||||||
#define CFG_FILENAME "/gravitymon.json" // Name of config file
|
#define CFG_FILENAME "/gravitymon.json" // Name of config file
|
||||||
#define CFG_HW_FILENAME "/hardware.json" // Name of config file for hw
|
#define CFG_HW_FILENAME "/hardware.json" // Name of config file for hw
|
||||||
@ -56,12 +54,12 @@ struct RawFormulaData {
|
|||||||
|
|
||||||
class AdvancedConfig {
|
class AdvancedConfig {
|
||||||
private:
|
private:
|
||||||
int _wifiPortalTimeout = 120;
|
int _wifiPortalTimeout = 120; // seconds
|
||||||
int _wifiConnectTimeout = 20;
|
int _wifiConnectTimeout = 20; // seconds
|
||||||
float _maxFormulaCreationDeviation = 1.6;
|
float _maxFormulaCreationDeviation = 3; // SG
|
||||||
float _defaultCalibrationTemp = 20.0;
|
float _defaultCalibrationTemp = 20.0; // C
|
||||||
int _gyroSensorMovingThreashold = 500;
|
int _gyroSensorMovingThreashold = 500;
|
||||||
int _tempSensorResolution = 9;
|
int _tempSensorResolution = 9; // bits
|
||||||
int _gyroReadCount = 50;
|
int _gyroReadCount = 50;
|
||||||
int _gyroReadDelay = 3150; // us, empirical, to hold sampling to 200 Hz
|
int _gyroReadDelay = 3150; // us, empirical, to hold sampling to 200 Hz
|
||||||
int _pushTimeout = 10; // seconds
|
int _pushTimeout = 10; // seconds
|
||||||
@ -146,9 +144,11 @@ class Config {
|
|||||||
String _otaURL = "";
|
String _otaURL = "";
|
||||||
char _tempFormat = 'C';
|
char _tempFormat = 'C';
|
||||||
float _voltageFactor = 1.59;
|
float _voltageFactor = 1.59;
|
||||||
|
float _voltageConfig = 4.15;
|
||||||
float _tempSensorAdjC = 0;
|
float _tempSensorAdjC = 0;
|
||||||
int _sleepInterval = 900;
|
int _sleepInterval = 900;
|
||||||
bool _gyroTemp = false;
|
bool _gyroTemp = false;
|
||||||
|
bool _storageSleep = false;
|
||||||
|
|
||||||
// Wifi Config
|
// Wifi Config
|
||||||
String _wifiSSID[2] = {"", ""};
|
String _wifiSSID[2] = {"", ""};
|
||||||
@ -184,7 +184,8 @@ class Config {
|
|||||||
|
|
||||||
// Gyro calibration and formula calculation data
|
// Gyro calibration and formula calculation data
|
||||||
RawGyroData _gyroCalibration = {0, 0, 0, 0, 0, 0};
|
RawGyroData _gyroCalibration = {0, 0, 0, 0, 0, 0};
|
||||||
RawFormulaData _formulaData = {{0, 0, 0, 0, 0}, {1, 1, 1, 1, 1}};
|
RawFormulaData _formulaData = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};
|
||||||
|
|
||||||
void formatFileSystem();
|
void formatFileSystem();
|
||||||
|
|
||||||
@ -206,6 +207,12 @@ class Config {
|
|||||||
_saveNeeded = true;
|
_saveNeeded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool isStorageSleep() { return _storageSleep; }
|
||||||
|
void setStorageSleep(bool b) {
|
||||||
|
_storageSleep = b;
|
||||||
|
_saveNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
const char* getOtaURL() { return _otaURL.c_str(); }
|
const char* getOtaURL() { return _otaURL.c_str(); }
|
||||||
void setOtaURL(String s) {
|
void setOtaURL(String s) {
|
||||||
_otaURL = s;
|
_otaURL = s;
|
||||||
@ -370,6 +377,16 @@ class Config {
|
|||||||
_saveNeeded = true;
|
_saveNeeded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float getVoltageConfig() { return _voltageConfig; }
|
||||||
|
void setVoltageConfig(float f) {
|
||||||
|
_voltageConfig = f;
|
||||||
|
_saveNeeded = true;
|
||||||
|
}
|
||||||
|
void setVoltageConfig(String s) {
|
||||||
|
_voltageConfig = s.toFloat();
|
||||||
|
_saveNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
float getTempSensorAdjC() { return _tempSensorAdjC; }
|
float getTempSensorAdjC() { return _tempSensorAdjC; }
|
||||||
void setTempSensorAdjC(float f) {
|
void setTempSensorAdjC(float f) {
|
||||||
_tempSensorAdjC = f;
|
_tempSensorAdjC = f;
|
||||||
|
12
src/gyro.cpp
@ -43,9 +43,10 @@ bool GyroSensor::setup() {
|
|||||||
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having
|
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having
|
||||||
// compilation difficulties
|
// compilation difficulties
|
||||||
|
|
||||||
if (!accelgyro.testConnection()) {
|
uint8_t id = accelgyro.getDeviceID();
|
||||||
ErrorFileLog errLog;
|
|
||||||
errLog.addEntry(F("GYRO: Failed to connect to gyro, is it connected?"));
|
if (id != 0x34 && id != 0x38) { // Allow both MPU6050 and MPU6000
|
||||||
|
writeErrorLog("GYRO: Failed to connect to gyro, is it connected?");
|
||||||
_sensorConnected = false;
|
_sensorConnected = false;
|
||||||
} else {
|
} else {
|
||||||
#if !defined(GYRO_DISABLE_LOGGING)
|
#if !defined(GYRO_DISABLE_LOGGING)
|
||||||
@ -292,9 +293,8 @@ void GyroSensor::applyCalibration() {
|
|||||||
if ((_calibrationOffset.ax + _calibrationOffset.ay + _calibrationOffset.az +
|
if ((_calibrationOffset.ax + _calibrationOffset.ay + _calibrationOffset.az +
|
||||||
_calibrationOffset.gx + _calibrationOffset.gy + _calibrationOffset.gz) ==
|
_calibrationOffset.gx + _calibrationOffset.gy + _calibrationOffset.gz) ==
|
||||||
0) {
|
0) {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog(
|
||||||
errLog.addEntry(
|
"GYRO: No valid calibration values, please calibrate the device.");
|
||||||
F("GYRO: No valid calibration values, please calibrate the device."));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
167
src/helper.cpp
@ -47,75 +47,68 @@ void tcp_cleanup() {
|
|||||||
while (tcp_tw_pcbs) tcp_abort(tcp_tw_pcbs);
|
while (tcp_tw_pcbs) tcp_abort(tcp_tw_pcbs);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
void checkResetReason() {
|
||||||
// Convert sg to plato
|
#if defined(ESP8266)
|
||||||
//
|
rst_info* _rinfo;
|
||||||
|
_rinfo = ESP.getResetInfoPtr();
|
||||||
|
|
||||||
|
Log.notice(F("HELP: Last reset cause %d" CR), _rinfo->exccause);
|
||||||
|
|
||||||
|
if (_rinfo->exccause > 0) {
|
||||||
|
char s[120];
|
||||||
|
snprintf(&s[0], sizeof(s),
|
||||||
|
"HELP: Exception (%d) reason=%d epc1=0x%08x epc2=0x%08x "
|
||||||
|
"epc3=0x%08x execvaddr=0x%08x depc=0x%08x",
|
||||||
|
_rinfo->exccause, _rinfo->reason, _rinfo->epc1, _rinfo->epc2,
|
||||||
|
_rinfo->epc3, _rinfo->excvaddr, _rinfo->depc);
|
||||||
|
writeErrorLog(&s[0]);
|
||||||
|
}
|
||||||
|
#else // defined (ESP32)
|
||||||
|
#warning "Todo: Handle reset reasons on ESP32"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeErrorLog(const char* format, ...) {
|
||||||
|
File f = LittleFS.open(ERR_FILENAME, "a");
|
||||||
|
|
||||||
|
if (f && f.size() > ERR_FILEMAXSIZE) {
|
||||||
|
f.close();
|
||||||
|
LittleFS.remove(ERR_FILENAME2);
|
||||||
|
LittleFS.rename(ERR_FILENAME, ERR_FILENAME2);
|
||||||
|
f = LittleFS.open(ERR_FILENAME, "a");
|
||||||
|
}
|
||||||
|
|
||||||
|
va_list arg;
|
||||||
|
va_start(arg, format);
|
||||||
|
char buf[120];
|
||||||
|
vsnprintf(&buf[0], sizeof(buf), format, arg);
|
||||||
|
va_end(arg);
|
||||||
|
Log.errorln(&buf[0]);
|
||||||
|
|
||||||
|
if (f) {
|
||||||
|
#if defined(ESP8266)
|
||||||
|
f.write(&buf[0], strlen(&buf[0]));
|
||||||
|
#else // ESP32
|
||||||
|
f.write((unsigned char*)&buf[0], strlen(&buf[0]));
|
||||||
|
#endif
|
||||||
|
f.println();
|
||||||
|
f.close();
|
||||||
|
} else {
|
||||||
|
Log.warning(F("HELP: Failed to open error log." CR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
double convertToPlato(double sg) {
|
double convertToPlato(double sg) {
|
||||||
if (sg) return 259 - (259 / sg);
|
if (sg) return 259 - (259 / sg);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Convert plato to sg
|
|
||||||
//
|
|
||||||
double convertToSG(double plato) { return 259 / (259 - plato); }
|
double convertToSG(double plato) { return 259 / (259 - plato); }
|
||||||
|
|
||||||
//
|
|
||||||
// Conversion to F
|
|
||||||
//
|
|
||||||
float convertCtoF(float c) { return (c * 1.8) + 32.0; }
|
float convertCtoF(float c) { return (c * 1.8) + 32.0; }
|
||||||
|
|
||||||
//
|
|
||||||
// Conversion to C
|
|
||||||
//
|
|
||||||
float convertFtoC(float f) { return (f - 32.0) / 1.8; }
|
float convertFtoC(float f) { return (f - 32.0) / 1.8; }
|
||||||
|
|
||||||
//
|
|
||||||
// Load error log from disk
|
|
||||||
//
|
|
||||||
ErrorFileLog::ErrorFileLog() {
|
|
||||||
File errFile = LittleFS.open(ERR_FILENAME, "r");
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
if (errFile) {
|
|
||||||
do {
|
|
||||||
_errors[i] = errFile.readStringUntil('\n');
|
|
||||||
_errors[i].replace("\r", "");
|
|
||||||
_errors[i].replace("\n", "");
|
|
||||||
} while (_errors[i++].length());
|
|
||||||
errFile.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Add new entry to top of error log
|
|
||||||
//
|
|
||||||
void ErrorFileLog::addEntry(String err) {
|
|
||||||
for (int i = (ERR_COUNT - 1); i > 0; i--) {
|
|
||||||
_errors[i] = _errors[i - 1];
|
|
||||||
}
|
|
||||||
_errors[0] = err;
|
|
||||||
err += String(CR);
|
|
||||||
Log.error(err.c_str());
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Save error log
|
|
||||||
//
|
|
||||||
void ErrorFileLog::save() {
|
|
||||||
File errFile = LittleFS.open(ERR_FILENAME, "w");
|
|
||||||
if (errFile) {
|
|
||||||
for (int i = 0; i < ERR_COUNT; i++) {
|
|
||||||
errFile.println(_errors[i]);
|
|
||||||
}
|
|
||||||
errFile.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Load history log of floats
|
|
||||||
//
|
|
||||||
FloatHistoryLog::FloatHistoryLog(String fName) {
|
FloatHistoryLog::FloatHistoryLog(String fName) {
|
||||||
_fName = fName;
|
_fName = fName;
|
||||||
|
|
||||||
@ -133,9 +126,6 @@ FloatHistoryLog::FloatHistoryLog(String fName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Add entry to top of log
|
|
||||||
//
|
|
||||||
void FloatHistoryLog::addEntry(float time) {
|
void FloatHistoryLog::addEntry(float time) {
|
||||||
for (int i = (10 - 1); i > 0; i--) {
|
for (int i = (10 - 1); i > 0; i--) {
|
||||||
_runTime[i] = _runTime[i - 1];
|
_runTime[i] = _runTime[i - 1];
|
||||||
@ -144,9 +134,6 @@ void FloatHistoryLog::addEntry(float time) {
|
|||||||
save();
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Save log
|
|
||||||
//
|
|
||||||
void FloatHistoryLog::save() {
|
void FloatHistoryLog::save() {
|
||||||
File runFile = LittleFS.open(_fName, "w");
|
File runFile = LittleFS.open(_fName, "w");
|
||||||
if (runFile) {
|
if (runFile) {
|
||||||
@ -157,9 +144,6 @@ void FloatHistoryLog::save() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Print the heap information.
|
|
||||||
//
|
|
||||||
void printHeap(String prefix) {
|
void printHeap(String prefix) {
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
Log.notice(
|
Log.notice(
|
||||||
@ -175,9 +159,6 @@ void printHeap(String prefix) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Enter deep sleep for the defined duration (Argument is seconds)
|
|
||||||
//
|
|
||||||
void deepSleep(int t) {
|
void deepSleep(int t) {
|
||||||
#if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING)
|
#if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING)
|
||||||
Log.verbose(F("HELP: Entering sleep mode for %ds." CR), t);
|
Log.verbose(F("HELP: Entering sleep mode for %ds." CR), t);
|
||||||
@ -186,30 +167,18 @@ void deepSleep(int t) {
|
|||||||
ESP.deepSleep(wake);
|
ESP.deepSleep(wake);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Print the build options used
|
|
||||||
//
|
|
||||||
void printBuildOptions() {
|
void printBuildOptions() {
|
||||||
Log.notice(F("Build options: %s (%s) LOGLEVEL %d "
|
Log.notice(F("Build options: %s (%s) LOGLEVEL %d "
|
||||||
#ifdef SKIP_SLEEPMODE
|
#ifdef SKIP_SLEEPMODE
|
||||||
"SKIP_SLEEP "
|
"SKIP_SLEEP "
|
||||||
#endif
|
#endif
|
||||||
#ifdef EMBED_HTML
|
|
||||||
"EMBED_HTML "
|
|
||||||
#endif
|
|
||||||
#ifdef COLLECT_PERFDATA
|
#ifdef COLLECT_PERFDATA
|
||||||
"PERFDATA "
|
"PERFDATA "
|
||||||
#endif
|
|
||||||
#ifdef ACTIVATE_OTA
|
|
||||||
"OTA "
|
|
||||||
#endif
|
#endif
|
||||||
CR),
|
CR),
|
||||||
CFG_APPVER, CFG_GITREV, LOG_LEVEL);
|
CFG_APPVER, CFG_GITREV, LOG_LEVEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Configure serial debug output
|
|
||||||
//
|
|
||||||
SerialDebug::SerialDebug(const uint32_t serialSpeed) {
|
SerialDebug::SerialDebug(const uint32_t serialSpeed) {
|
||||||
// Start serial with auto-detected rate (default to defined BAUD)
|
// Start serial with auto-detected rate (default to defined BAUD)
|
||||||
Serial.flush();
|
Serial.flush();
|
||||||
@ -220,18 +189,12 @@ SerialDebug::SerialDebug(const uint32_t serialSpeed) {
|
|||||||
getLog()->notice(F("SDBG: Serial logging started at %u." CR), serialSpeed);
|
getLog()->notice(F("SDBG: Serial logging started at %u." CR), serialSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Print the timestamp (ms since start of device)
|
|
||||||
//
|
|
||||||
void printTimestamp(Print* _logOutput, int _logLevel) {
|
void printTimestamp(Print* _logOutput, int _logLevel) {
|
||||||
char c[12];
|
char c[12];
|
||||||
snprintf(c, sizeof(c), "%10lu ", millis());
|
snprintf(c, sizeof(c), "%10lu ", millis());
|
||||||
_logOutput->print(c);
|
_logOutput->print(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Read and calculate the battery voltage
|
|
||||||
//
|
|
||||||
void BatteryVoltage::read() {
|
void BatteryVoltage::read() {
|
||||||
// The analog pin can only handle 3.3V maximum voltage so we need to reduce
|
// The analog pin can only handle 3.3V maximum voltage so we need to reduce
|
||||||
// the voltage (from max 5V)
|
// the voltage (from max 5V)
|
||||||
@ -257,9 +220,6 @@ void BatteryVoltage::read() {
|
|||||||
|
|
||||||
PerfLogging myPerfLogging;
|
PerfLogging myPerfLogging;
|
||||||
|
|
||||||
//
|
|
||||||
// Clear the current cache
|
|
||||||
//
|
|
||||||
void PerfLogging::clear() {
|
void PerfLogging::clear() {
|
||||||
// Clear the measurements
|
// Clear the measurements
|
||||||
if (first == 0) return;
|
if (first == 0) return;
|
||||||
@ -276,17 +236,11 @@ void PerfLogging::clear() {
|
|||||||
} while (pe != 0);
|
} while (pe != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Start measuring this performance point
|
|
||||||
//
|
|
||||||
void PerfLogging::start(const char* key) {
|
void PerfLogging::start(const char* key) {
|
||||||
PerfEntry* pe = add(key);
|
PerfEntry* pe = add(key);
|
||||||
pe->start = millis();
|
pe->start = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Finalize measuring of this performance point
|
|
||||||
//
|
|
||||||
void PerfLogging::stop(const char* key) {
|
void PerfLogging::stop(const char* key) {
|
||||||
PerfEntry* pe = find(key);
|
PerfEntry* pe = find(key);
|
||||||
|
|
||||||
@ -299,9 +253,6 @@ void PerfLogging::stop(const char* key) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Print the collected performance data
|
|
||||||
//
|
|
||||||
void PerfLogging::print() {
|
void PerfLogging::print() {
|
||||||
PerfEntry* pe = first;
|
PerfEntry* pe = first;
|
||||||
|
|
||||||
@ -311,9 +262,6 @@ void PerfLogging::print() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Push collected performance data to influx (use influx configuration)
|
|
||||||
//
|
|
||||||
void PerfLogging::pushInflux() {
|
void PerfLogging::pushInflux() {
|
||||||
if (!myConfig.isInfluxDb2Active()) return;
|
if (!myConfig.isInfluxDb2Active()) return;
|
||||||
|
|
||||||
@ -415,29 +363,19 @@ void PerfLogging::pushInflux() {
|
|||||||
|
|
||||||
#endif // COLLECT_PERFDATA
|
#endif // COLLECT_PERFDATA
|
||||||
|
|
||||||
//
|
|
||||||
// Convert float to formatted string with n decimals. Buffer should be at least
|
|
||||||
// 10 chars.
|
|
||||||
//
|
|
||||||
char* convertFloatToString(float f, char* buffer, int dec) {
|
char* convertFloatToString(float f, char* buffer, int dec) {
|
||||||
dtostrf(f, 6, dec, buffer);
|
dtostrf(f, 6, dec, buffer);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Reduce precision to n decimals
|
|
||||||
//
|
|
||||||
float reduceFloatPrecision(float f, int dec) {
|
float reduceFloatPrecision(float f, int dec) {
|
||||||
char buffer[5];
|
char buffer[5];
|
||||||
dtostrf(f, 6, dec, &buffer[0]);
|
dtostrf(f, 6, dec, &buffer[0]);
|
||||||
return atof(&buffer[0]);
|
return atof(&buffer[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// urlencode
|
// urlencode
|
||||||
//
|
|
||||||
// https://circuits4you.com/2019/03/21/esp8266-url-encode-decode-example/
|
// https://circuits4you.com/2019/03/21/esp8266-url-encode-decode-example/
|
||||||
//
|
|
||||||
String urlencode(String str) {
|
String urlencode(String str) {
|
||||||
String encodedString;
|
String encodedString;
|
||||||
encodedString.reserve(str.length() * 2);
|
encodedString.reserve(str.length() * 2);
|
||||||
@ -481,9 +419,6 @@ unsigned char h2int(char c) {
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// urlencode string
|
|
||||||
//
|
|
||||||
String urldecode(String str) {
|
String urldecode(String str) {
|
||||||
String encodedString;
|
String encodedString;
|
||||||
encodedString.reserve(str.length());
|
encodedString.reserve(str.length());
|
||||||
|
@ -28,13 +28,18 @@ SOFTWARE.
|
|||||||
#include <main.hpp>
|
#include <main.hpp>
|
||||||
|
|
||||||
#define ERR_FILENAME "/error.log"
|
#define ERR_FILENAME "/error.log"
|
||||||
#define ERR_COUNT 15
|
#define ERR_FILENAME2 "/error2.log"
|
||||||
|
#define ERR_FILEMAXSIZE 4000
|
||||||
|
|
||||||
#define RUNTIME_FILENAME "/runtime.log"
|
#define RUNTIME_FILENAME "/runtime.log"
|
||||||
|
|
||||||
// tcp cleanup
|
// tcp cleanup
|
||||||
void tcp_cleanup();
|
void tcp_cleanup();
|
||||||
|
|
||||||
|
// Error logging
|
||||||
|
void writeErrorLog(const char* format, ...);
|
||||||
|
void checkResetReason();
|
||||||
|
|
||||||
// Sleep mode
|
// Sleep mode
|
||||||
void deepSleep(int t);
|
void deepSleep(int t);
|
||||||
|
|
||||||
@ -67,16 +72,6 @@ class SerialDebug {
|
|||||||
static Logging* getLog() { return &Log; }
|
static Logging* getLog() { return &Log; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class ErrorFileLog {
|
|
||||||
private:
|
|
||||||
String _errors[ERR_COUNT];
|
|
||||||
|
|
||||||
public:
|
|
||||||
ErrorFileLog();
|
|
||||||
void addEntry(String error);
|
|
||||||
void save();
|
|
||||||
};
|
|
||||||
|
|
||||||
class FloatHistoryLog {
|
class FloatHistoryLog {
|
||||||
private:
|
private:
|
||||||
String _fName;
|
String _fName;
|
||||||
|
62
src/main.cpp
@ -50,9 +50,6 @@ uint32_t stableGyroMillis; // Used to calculate the total time since last
|
|||||||
|
|
||||||
RunMode runMode = RunMode::gravityMode;
|
RunMode runMode = RunMode::gravityMode;
|
||||||
|
|
||||||
//
|
|
||||||
// Check if we should be in sleep mode
|
|
||||||
//
|
|
||||||
void checkSleepMode(float angle, float volt) {
|
void checkSleepMode(float angle, float volt) {
|
||||||
#if defined(SKIP_SLEEPMODE)
|
#if defined(SKIP_SLEEPMODE)
|
||||||
runMode = RunMode::configurationMode;
|
runMode = RunMode::configurationMode;
|
||||||
@ -83,8 +80,12 @@ void checkSleepMode(float angle, float volt) {
|
|||||||
Log.notice(F("MAIN: Sleep mode disabled from web interface." CR));
|
Log.notice(F("MAIN: Sleep mode disabled from web interface." CR));
|
||||||
#endif
|
#endif
|
||||||
runMode = RunMode::configurationMode;
|
runMode = RunMode::configurationMode;
|
||||||
} else if ((volt < 4.15 && (angle > 85 && angle < 95)) || (volt > 4.15)) {
|
} else if ((volt < myConfig.getVoltageConfig() &&
|
||||||
|
(angle > 85 && angle < 95)) ||
|
||||||
|
(volt > myConfig.getVoltageConfig())) {
|
||||||
runMode = RunMode::configurationMode;
|
runMode = RunMode::configurationMode;
|
||||||
|
} else if (angle < 5 && myConfig.isStorageSleep()) {
|
||||||
|
runMode = RunMode::storageMode;
|
||||||
} else {
|
} else {
|
||||||
runMode = RunMode::gravityMode;
|
runMode = RunMode::gravityMode;
|
||||||
}
|
}
|
||||||
@ -104,12 +105,29 @@ void checkSleepMode(float angle, float volt) {
|
|||||||
volt);
|
volt);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
case RunMode::storageMode:
|
||||||
|
#if !defined(MAIN_DISABLE_LOGGING)
|
||||||
|
Log.notice(F("MAIN: run mode STORAGE (angle=%F)." CR), angle);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are in storage mode, just go back to sleep
|
||||||
|
if (runMode == RunMode::storageMode) {
|
||||||
|
Log.notice(
|
||||||
|
F("Main: Storage mode entered, going to sleep for maximum time." CR));
|
||||||
|
#if defined(ESP8266)
|
||||||
|
// ESP.deepSleep(ESP.deepSleepMax());
|
||||||
|
ESP.deepSleep(0); // indefinite sleep
|
||||||
|
#else
|
||||||
|
#warning "Check and test the max deep sleep for esp32"
|
||||||
|
// deepSleep(70 * 60); // quick search on internet suggest max time is 70
|
||||||
|
// min
|
||||||
|
ESP.deepSleep(0); // indefinite sleep
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Setup
|
|
||||||
//
|
|
||||||
void setup() {
|
void setup() {
|
||||||
LOG_PERF_START("run-time");
|
LOG_PERF_START("run-time");
|
||||||
LOG_PERF_START("main-setup");
|
LOG_PERF_START("main-setup");
|
||||||
@ -118,12 +136,9 @@ void setup() {
|
|||||||
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
||||||
// Add a delay so that serial is started.
|
// Add a delay so that serial is started.
|
||||||
// delay(3000);
|
// delay(3000);
|
||||||
#if defined(ESP8266)
|
|
||||||
Log.verbose(F("Main: Reset reason %s." CR), ESP.getResetInfo().c_str());
|
|
||||||
#else // defined (ESP32)
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
// Main startup
|
// Main startup
|
||||||
|
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
Log.notice(F("Main: Started setup for %s." CR),
|
Log.notice(F("Main: Started setup for %s." CR),
|
||||||
String(ESP.getChipId(), HEX).c_str());
|
String(ESP.getChipId(), HEX).c_str());
|
||||||
@ -142,6 +157,7 @@ void setup() {
|
|||||||
|
|
||||||
LOG_PERF_START("main-config-load");
|
LOG_PERF_START("main-config-load");
|
||||||
myConfig.checkFileSystem();
|
myConfig.checkFileSystem();
|
||||||
|
checkResetReason();
|
||||||
myConfig.loadFile();
|
myConfig.loadFile();
|
||||||
myWifi.init();
|
myWifi.init();
|
||||||
myAdvancedConfig.loadFile();
|
myAdvancedConfig.loadFile();
|
||||||
@ -177,14 +193,14 @@ void setup() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (!myGyro.setup()) {
|
if (myGyro.setup()) {
|
||||||
ErrorFileLog errLog;
|
|
||||||
errLog.addEntry(
|
|
||||||
F("MAIN: Failed to initialize the gyro, is it connected?"));
|
|
||||||
} else {
|
|
||||||
LOG_PERF_START("main-gyro-read");
|
LOG_PERF_START("main-gyro-read");
|
||||||
myGyro.read();
|
myGyro.read();
|
||||||
LOG_PERF_STOP("main-gyro-read");
|
LOG_PERF_STOP("main-gyro-read");
|
||||||
|
} else {
|
||||||
|
Log.notice(
|
||||||
|
F("Main: Failed to connect to the gyro, software will not be able "
|
||||||
|
"to detect angles." CR));
|
||||||
}
|
}
|
||||||
|
|
||||||
myBatteryVoltage.read();
|
myBatteryVoltage.read();
|
||||||
@ -264,7 +280,12 @@ bool loopReadGravity() {
|
|||||||
LOG_PERF_STOP("loop-temp-read");
|
LOG_PERF_STOP("loop-temp-read");
|
||||||
|
|
||||||
float gravitySG = calculateGravity(angle, tempC);
|
float gravitySG = calculateGravity(angle, tempC);
|
||||||
float corrGravitySG = gravityTemperatureCorrectionC(gravitySG, tempC);
|
float corrGravitySG = gravityTemperatureCorrectionC(
|
||||||
|
gravitySG, tempC, myAdvancedConfig.getDefaultCalibrationTemp());
|
||||||
|
|
||||||
|
if (myConfig.isGravityTempAdj()) {
|
||||||
|
gravitySG = corrGravitySG;
|
||||||
|
}
|
||||||
|
|
||||||
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
||||||
Log.verbose(F("Main: Sensor values gyro angle=%F, temp=%FC, gravity=%F, "
|
Log.verbose(F("Main: Sensor values gyro angle=%F, temp=%FC, gravity=%F, "
|
||||||
@ -358,11 +379,12 @@ void goToSleep(int sleepInterval) {
|
|||||||
deepSleep(sleepInterval);
|
deepSleep(sleepInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Main loops
|
|
||||||
//
|
|
||||||
void loop() {
|
void loop() {
|
||||||
switch (runMode) {
|
switch (runMode) {
|
||||||
|
case RunMode::storageMode:
|
||||||
|
// This point is never reached, just here to remove warning.
|
||||||
|
break;
|
||||||
|
|
||||||
case RunMode::configurationMode:
|
case RunMode::configurationMode:
|
||||||
if (myWifi.isConnected()) myWebServerHandler.loop();
|
if (myWifi.isConnected()) myWebServerHandler.loop();
|
||||||
|
|
||||||
|
14
src/main.hpp
@ -29,7 +29,12 @@ SOFTWARE.
|
|||||||
#include <ArduinoLog.h>
|
#include <ArduinoLog.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
enum RunMode { gravityMode = 0, configurationMode = 1, wifiSetupMode = 2 };
|
enum RunMode {
|
||||||
|
gravityMode = 0,
|
||||||
|
configurationMode = 1,
|
||||||
|
wifiSetupMode = 2,
|
||||||
|
storageMode = 3
|
||||||
|
};
|
||||||
extern RunMode runMode;
|
extern RunMode runMode;
|
||||||
|
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
@ -41,13 +46,8 @@ extern RunMode runMode;
|
|||||||
#define PIN_LED 2
|
#define PIN_LED 2
|
||||||
// #define PIN_A0 A0
|
// #define PIN_A0 A0
|
||||||
#else // defined (ESP32)
|
#else // defined (ESP32)
|
||||||
#if defined(ESPRESSIF32_20)
|
|
||||||
#include <LittleFS.h>
|
|
||||||
#else
|
|
||||||
#include <LITTLEFS.h>
|
|
||||||
#define LittleFS LITTLEFS
|
|
||||||
#endif
|
|
||||||
#include <FS.h>
|
#include <FS.h>
|
||||||
|
#include <LittleFS.h>
|
||||||
#define ESPhttpUpdate httpUpdate
|
#define ESPhttpUpdate httpUpdate
|
||||||
#define ESP_RESET ESP.restart
|
#define ESP_RESET ESP.restart
|
||||||
#define ESP8266WebServer WebServer
|
#define ESP8266WebServer WebServer
|
||||||
|
@ -34,9 +34,6 @@ SOFTWARE.
|
|||||||
|
|
||||||
#define PUSHINT_FILENAME "/push.dat"
|
#define PUSHINT_FILENAME "/push.dat"
|
||||||
|
|
||||||
//
|
|
||||||
// Decrease counters
|
|
||||||
//
|
|
||||||
void PushIntervalTracker::update(const int index, const int defaultValue) {
|
void PushIntervalTracker::update(const int index, const int defaultValue) {
|
||||||
if (_counters[index] <= 0)
|
if (_counters[index] <= 0)
|
||||||
_counters[index] = defaultValue;
|
_counters[index] = defaultValue;
|
||||||
@ -44,9 +41,6 @@ void PushIntervalTracker::update(const int index, const int defaultValue) {
|
|||||||
_counters[index]--;
|
_counters[index]--;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Load data from file
|
|
||||||
//
|
|
||||||
void PushIntervalTracker::load() {
|
void PushIntervalTracker::load() {
|
||||||
File intFile = LittleFS.open(PUSHINT_FILENAME, "r");
|
File intFile = LittleFS.open(PUSHINT_FILENAME, "r");
|
||||||
|
|
||||||
@ -143,10 +137,11 @@ void PushTarget::sendAll(float angle, float gravitySG, float corrGravitySG,
|
|||||||
|
|
||||||
if (myConfig.isMqttActive() && intDelay.useMqtt()) {
|
if (myConfig.isMqttActive() && intDelay.useMqtt()) {
|
||||||
LOG_PERF_START("push-mqtt");
|
LOG_PERF_START("push-mqtt");
|
||||||
sendMqtt(engine, myConfig.isMqttSSL());
|
sendMqtt(engine, myConfig.isMqttSSL(), true);
|
||||||
LOG_PERF_STOP("push-mqtt");
|
LOG_PERF_STOP("push-mqtt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
engine.freeMemory();
|
||||||
intDelay.save();
|
intDelay.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,8 +227,7 @@ void PushTarget::sendInfluxDb2(TemplatingEngine& engine, bool isSecure) {
|
|||||||
_lastSuccess = true;
|
_lastSuccess = true;
|
||||||
Log.notice(F("PUSH: InfluxDB2 push successful, response=%d" CR), _lastCode);
|
Log.notice(F("PUSH: InfluxDB2 push successful, response=%d" CR), _lastCode);
|
||||||
} else {
|
} else {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("PUSH: Influxdb push failed response=%d", _lastCode);
|
||||||
errLog.addEntry("PUSH: Influxdb push failed response=" + String(_lastCode));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSecure) {
|
if (isSecure) {
|
||||||
@ -261,8 +255,8 @@ void PushTarget::addHttpHeader(HTTPClient& http, String header) {
|
|||||||
value.c_str());
|
value.c_str());
|
||||||
http.addHeader(name, value);
|
http.addHeader(name, value);
|
||||||
} else {
|
} else {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("PUSH: Unable to set header, invalid value %s",
|
||||||
errLog.addEntry("PUSH: Unable to set header, invalid value " + header);
|
header.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,9 +332,8 @@ void PushTarget::sendHttpPost(TemplatingEngine& engine, bool isSecure,
|
|||||||
_lastSuccess = true;
|
_lastSuccess = true;
|
||||||
Log.notice(F("PUSH: HTTP post successful, response=%d" CR), _lastCode);
|
Log.notice(F("PUSH: HTTP post successful, response=%d" CR), _lastCode);
|
||||||
} else {
|
} else {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("PUSH: HTTP post failed response=%d http%d", _lastCode,
|
||||||
errLog.addEntry("PUSH: HTTP post failed response=" + String(_lastCode) +
|
index + 1);
|
||||||
String(index == 0 ? " (http)" : " (http2)"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSecure) {
|
if (isSecure) {
|
||||||
@ -399,8 +392,7 @@ void PushTarget::sendHttpGet(TemplatingEngine& engine, bool isSecure) {
|
|||||||
_lastSuccess = true;
|
_lastSuccess = true;
|
||||||
Log.notice(F("PUSH: HTTP get successful, response=%d" CR), _lastCode);
|
Log.notice(F("PUSH: HTTP get successful, response=%d" CR), _lastCode);
|
||||||
} else {
|
} else {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("PUSH: HTTP get failed response=%d", _lastCode);
|
||||||
errLog.addEntry("PUSH: HTTP get failed response=" + String(_lastCode));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSecure) {
|
if (isSecure) {
|
||||||
@ -416,9 +408,11 @@ void PushTarget::sendHttpGet(TemplatingEngine& engine, bool isSecure) {
|
|||||||
//
|
//
|
||||||
// Send data to mqtt target
|
// Send data to mqtt target
|
||||||
//
|
//
|
||||||
void PushTarget::sendMqtt(TemplatingEngine& engine, bool isSecure) {
|
void PushTarget::sendMqtt(TemplatingEngine& engine, bool isSecure,
|
||||||
|
bool skipHomeAssistantRegistration) {
|
||||||
#if !defined(PUSH_DISABLE_LOGGING)
|
#if !defined(PUSH_DISABLE_LOGGING)
|
||||||
Log.notice(F("PUSH: Sending values to mqtt." CR));
|
Log.notice(F("PUSH: Sending values to mqtt. Skip HA registration=%s" CR),
|
||||||
|
skipHomeAssistantRegistration ? "yes" : "no");
|
||||||
#endif
|
#endif
|
||||||
_lastCode = 0;
|
_lastCode = 0;
|
||||||
_lastSuccess = false;
|
_lastSuccess = false;
|
||||||
@ -483,15 +477,22 @@ void PushTarget::sendMqtt(TemplatingEngine& engine, bool isSecure) {
|
|||||||
Log.verbose(F("PUSH: topic '%s', value '%s'." CR), topic.c_str(),
|
Log.verbose(F("PUSH: topic '%s', value '%s'." CR), topic.c_str(),
|
||||||
value.c_str());
|
value.c_str());
|
||||||
#endif
|
#endif
|
||||||
if (mqtt.publish(topic, value)) {
|
|
||||||
_lastSuccess = true;
|
if (skipHomeAssistantRegistration &&
|
||||||
Log.notice(F("PUSH: MQTT publish successful on %s" CR), topic.c_str());
|
topic.startsWith("homeassistant/sensor/")) {
|
||||||
_lastCode = 0;
|
Log.notice(F("PUSH: Ignoring Home Assistant registration topic %s" CR),
|
||||||
|
topic.c_str());
|
||||||
} else {
|
} else {
|
||||||
_lastCode = mqtt.lastError();
|
if (mqtt.publish(topic, value)) {
|
||||||
ErrorFileLog errLog;
|
_lastSuccess = true;
|
||||||
errLog.addEntry("PUSH: MQTT push on " + topic +
|
Log.notice(F("PUSH: MQTT publish successful on %s" CR), topic.c_str());
|
||||||
" failed error=" + String(mqtt.lastError()));
|
_lastCode = 0;
|
||||||
|
} else {
|
||||||
|
_lastSuccess = false;
|
||||||
|
_lastCode = mqtt.lastError();
|
||||||
|
writeErrorLog("PUSH: MQTT push on %s failed error=%d", topic.c_str(),
|
||||||
|
_lastCode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
index = next + 1;
|
index = next + 1;
|
||||||
|
@ -61,7 +61,8 @@ class PushTarget {
|
|||||||
sendHttpGet(engine, isSecure);
|
sendHttpGet(engine, isSecure);
|
||||||
}
|
}
|
||||||
void sendInfluxDb2(TemplatingEngine& engine, bool isSecure);
|
void sendInfluxDb2(TemplatingEngine& engine, bool isSecure);
|
||||||
void sendMqtt(TemplatingEngine& engine, bool isSecure);
|
void sendMqtt(TemplatingEngine& engine, bool isSecure,
|
||||||
|
bool skipHomeAssistantRegistration = true);
|
||||||
int getLastCode() { return _lastCode; }
|
int getLastCode() { return _lastCode; }
|
||||||
bool getLastSuccess() { return _lastSuccess; }
|
bool getLastSuccess() { return _lastSuccess; }
|
||||||
};
|
};
|
||||||
|
@ -53,13 +53,15 @@ SOFTWARE.
|
|||||||
#define PARAM_PUSH_MQTT_PORT "mqtt-port"
|
#define PARAM_PUSH_MQTT_PORT "mqtt-port"
|
||||||
#define PARAM_SLEEP_INTERVAL "sleep-interval"
|
#define PARAM_SLEEP_INTERVAL "sleep-interval"
|
||||||
#define PARAM_TEMPFORMAT "temp-format"
|
#define PARAM_TEMPFORMAT "temp-format"
|
||||||
#define PARAM_VOLTAGEFACTOR "voltage-factor"
|
#define PARAM_VOLTAGE_FACTOR "voltage-factor"
|
||||||
|
#define PARAM_VOLTAGE_CONFIG "voltage-config"
|
||||||
#define PARAM_GRAVITY_FORMULA "gravity-formula"
|
#define PARAM_GRAVITY_FORMULA "gravity-formula"
|
||||||
#define PARAM_GRAVITY_FORMAT "gravity-format"
|
#define PARAM_GRAVITY_FORMAT "gravity-format"
|
||||||
#define PARAM_GRAVITY_TEMP_ADJ "gravity-temp-adjustment"
|
#define PARAM_GRAVITY_TEMP_ADJ "gravity-temp-adjustment"
|
||||||
#define PARAM_TEMP_ADJ "temp-adjustment-value"
|
#define PARAM_TEMP_ADJ "temp-adjustment-value"
|
||||||
#define PARAM_GYRO_CALIBRATION "gyro-calibration-data"
|
#define PARAM_GYRO_CALIBRATION "gyro-calibration-data"
|
||||||
#define PARAM_GYRO_TEMP "gyro-temp"
|
#define PARAM_GYRO_TEMP "gyro-temp"
|
||||||
|
#define PARAM_STORAGE_SLEEP "storage-sleep"
|
||||||
#define PARAM_FORMULA_DATA "formula-calculation-data"
|
#define PARAM_FORMULA_DATA "formula-calculation-data"
|
||||||
#define PARAM_FILES "files"
|
#define PARAM_FILES "files"
|
||||||
#define PARAM_FILE_NAME "file-name"
|
#define PARAM_FILE_NAME "file-name"
|
||||||
|
@ -130,6 +130,9 @@ void TemplatingEngine::initialize(float angle, float gravitySG,
|
|||||||
setVal(TPL_GRAVITY_CORR_P, convertToPlato(corrGravitySG), 1);
|
setVal(TPL_GRAVITY_CORR_P, convertToPlato(corrGravitySG), 1);
|
||||||
setVal(TPL_GRAVITY_UNIT, myConfig.getGravityFormat());
|
setVal(TPL_GRAVITY_UNIT, myConfig.getGravityFormat());
|
||||||
|
|
||||||
|
setVal(TPL_APP_VER, CFG_APPVER);
|
||||||
|
setVal(TPL_APP_BUILD, CFG_GITREV);
|
||||||
|
|
||||||
#if LOG_LEVEL == 6
|
#if LOG_LEVEL == 6
|
||||||
// dumpAll();
|
// dumpAll();
|
||||||
#endif
|
#endif
|
||||||
@ -138,30 +141,30 @@ void TemplatingEngine::initialize(float angle, float gravitySG,
|
|||||||
//
|
//
|
||||||
// Create the data using defined template.
|
// Create the data using defined template.
|
||||||
//
|
//
|
||||||
const String& TemplatingEngine::create(TemplatingEngine::Templates idx) {
|
const char* TemplatingEngine::create(TemplatingEngine::Templates idx) {
|
||||||
String fname;
|
String fname;
|
||||||
baseTemplate.reserve(600);
|
_baseTemplate.reserve(600);
|
||||||
|
|
||||||
// Load templates from memory
|
// Load templates from memory
|
||||||
switch (idx) {
|
switch (idx) {
|
||||||
case TEMPLATE_HTTP1:
|
case TEMPLATE_HTTP1:
|
||||||
baseTemplate = String(iSpindleFormat);
|
_baseTemplate = String(iSpindleFormat);
|
||||||
fname = TPL_FNAME_HTTP1;
|
fname = TPL_FNAME_HTTP1;
|
||||||
break;
|
break;
|
||||||
case TEMPLATE_HTTP2:
|
case TEMPLATE_HTTP2:
|
||||||
baseTemplate = String(iSpindleFormat);
|
_baseTemplate = String(iSpindleFormat);
|
||||||
fname = TPL_FNAME_HTTP2;
|
fname = TPL_FNAME_HTTP2;
|
||||||
break;
|
break;
|
||||||
case TEMPLATE_HTTP3:
|
case TEMPLATE_HTTP3:
|
||||||
baseTemplate = String(iHttpGetFormat);
|
_baseTemplate = String(iHttpGetFormat);
|
||||||
fname = TPL_FNAME_HTTP3;
|
fname = TPL_FNAME_HTTP3;
|
||||||
break;
|
break;
|
||||||
case TEMPLATE_INFLUX:
|
case TEMPLATE_INFLUX:
|
||||||
baseTemplate = String(influxDbFormat);
|
_baseTemplate = String(influxDbFormat);
|
||||||
fname = TPL_FNAME_INFLUXDB;
|
fname = TPL_FNAME_INFLUXDB;
|
||||||
break;
|
break;
|
||||||
case TEMPLATE_MQTT:
|
case TEMPLATE_MQTT:
|
||||||
baseTemplate = String(mqttFormat);
|
_baseTemplate = String(mqttFormat);
|
||||||
fname = TPL_FNAME_MQTT;
|
fname = TPL_FNAME_MQTT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -172,7 +175,7 @@ const String& TemplatingEngine::create(TemplatingEngine::Templates idx) {
|
|||||||
char buf[file.size() + 1];
|
char buf[file.size() + 1];
|
||||||
memset(&buf[0], 0, file.size() + 1);
|
memset(&buf[0], 0, file.size() + 1);
|
||||||
file.readBytes(&buf[0], file.size());
|
file.readBytes(&buf[0], file.size());
|
||||||
baseTemplate = String(&buf[0]);
|
_baseTemplate = String(&buf[0]);
|
||||||
file.close();
|
file.close();
|
||||||
Log.notice(F("TPL : Template loaded from disk %s." CR), fname.c_str());
|
Log.notice(F("TPL : Template loaded from disk %s." CR), fname.c_str());
|
||||||
}
|
}
|
||||||
@ -182,13 +185,16 @@ const String& TemplatingEngine::create(TemplatingEngine::Templates idx) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Insert data into template.
|
// Insert data into template.
|
||||||
transform(baseTemplate);
|
transform();
|
||||||
|
_baseTemplate.clear();
|
||||||
|
|
||||||
#if LOG_LEVEL == 6
|
#if LOG_LEVEL == 6
|
||||||
// Log.verbose(F("TPL : Transformed '%s'." CR), baseTemplate.c_str());
|
// Log.verbose(F("TPL : Transformed '%s'." CR), baseTemplate.c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return baseTemplate;
|
if (_output) return _output;
|
||||||
|
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// EOF
|
// EOF
|
||||||
|
@ -53,6 +53,8 @@ SOFTWARE.
|
|||||||
#define TPL_GRAVITY_CORR_G "${corr-gravity-sg}"
|
#define TPL_GRAVITY_CORR_G "${corr-gravity-sg}"
|
||||||
#define TPL_GRAVITY_CORR_P "${corr-gravity-plato}"
|
#define TPL_GRAVITY_CORR_P "${corr-gravity-plato}"
|
||||||
#define TPL_GRAVITY_UNIT "${gravity-unit}" // G or P
|
#define TPL_GRAVITY_UNIT "${gravity-unit}" // G or P
|
||||||
|
#define TPL_APP_VER "${app-ver}"
|
||||||
|
#define TPL_APP_BUILD "${app-build}"
|
||||||
|
|
||||||
#define TPL_FNAME_HTTP1 "/http-1.tpl"
|
#define TPL_FNAME_HTTP1 "/http-1.tpl"
|
||||||
#define TPL_FNAME_HTTP2 "/http-2.tpl"
|
#define TPL_FNAME_HTTP2 "/http-2.tpl"
|
||||||
@ -73,33 +75,35 @@ class TemplatingEngine {
|
|||||||
String val;
|
String val;
|
||||||
};
|
};
|
||||||
|
|
||||||
KeyVal items[21] = {{TPL_MDNS, ""}, {TPL_ID, ""},
|
KeyVal _items[23] = {{TPL_MDNS, ""}, {TPL_ID, ""},
|
||||||
{TPL_SLEEP_INTERVAL, ""}, {TPL_TEMP, ""},
|
{TPL_SLEEP_INTERVAL, ""}, {TPL_TEMP, ""},
|
||||||
{TPL_TEMP_C, ""}, {TPL_TEMP_F, ""},
|
{TPL_TEMP_C, ""}, {TPL_TEMP_F, ""},
|
||||||
{TPL_TEMP_UNITS, ""}, {TPL_BATTERY, ""},
|
{TPL_TEMP_UNITS, ""}, {TPL_BATTERY, ""},
|
||||||
{TPL_RSSI, ""}, {TPL_RUN_TIME, ""},
|
{TPL_RSSI, ""}, {TPL_RUN_TIME, ""},
|
||||||
{TPL_ANGLE, ""}, {TPL_TILT, ""},
|
{TPL_ANGLE, ""}, {TPL_TILT, ""},
|
||||||
{TPL_GRAVITY, ""}, {TPL_GRAVITY_G, ""},
|
{TPL_GRAVITY, ""}, {TPL_GRAVITY_G, ""},
|
||||||
{TPL_GRAVITY_P, ""}, {TPL_GRAVITY_CORR, ""},
|
{TPL_GRAVITY_P, ""}, {TPL_GRAVITY_CORR, ""},
|
||||||
{TPL_GRAVITY_CORR_G, ""}, {TPL_GRAVITY_CORR_P, ""},
|
{TPL_GRAVITY_CORR_G, ""}, {TPL_GRAVITY_CORR_P, ""},
|
||||||
{TPL_GRAVITY_UNIT, ""}, {TPL_TOKEN, ""},
|
{TPL_GRAVITY_UNIT, ""}, {TPL_TOKEN, ""},
|
||||||
{TPL_TOKEN2, ""}};
|
{TPL_TOKEN2, ""}, {TPL_APP_VER, ""},
|
||||||
|
{TPL_APP_BUILD, ""}};
|
||||||
|
|
||||||
char buffer[20];
|
char _buffer[20] = "";
|
||||||
String baseTemplate;
|
String _baseTemplate;
|
||||||
|
char *_output = 0;
|
||||||
|
|
||||||
void setVal(String key, float val, int dec = 2) {
|
void setVal(String key, float val, int dec = 2) {
|
||||||
String s = convertFloatToString(val, &buffer[0], dec);
|
String s = convertFloatToString(val, &_buffer[0], dec);
|
||||||
s.trim();
|
s.trim();
|
||||||
setVal(key, s);
|
setVal(key, s);
|
||||||
}
|
}
|
||||||
void setVal(String key, int val) { setVal(key, String(val)); }
|
void setVal(String key, int val) { setVal(key, String(val)); }
|
||||||
void setVal(String key, char val) { setVal(key, String(val)); }
|
void setVal(String key, char val) { setVal(key, String(val)); }
|
||||||
void setVal(String key, String val) {
|
void setVal(String key, String val) {
|
||||||
int max = sizeof(items) / sizeof(KeyVal);
|
int max = sizeof(_items) / sizeof(KeyVal);
|
||||||
for (int i = 0; i < max; i++) {
|
for (int i = 0; i < max; i++) {
|
||||||
if (items[i].key.equals(key)) {
|
if (_items[i].key.equals(key)) {
|
||||||
items[i].val = val;
|
_items[i].val = val;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,21 +111,73 @@ class TemplatingEngine {
|
|||||||
Log.warning(F("TPL : Key not found %s." CR), key.c_str());
|
Log.warning(F("TPL : Key not found %s." CR), key.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void transform(String& s) {
|
void transform() {
|
||||||
int max = sizeof(items) / sizeof(KeyVal);
|
const char *format = _baseTemplate.c_str();
|
||||||
for (int i = 0; i < max; i++) {
|
int len = _baseTemplate.length();
|
||||||
while (s.indexOf(items[i].key) != -1)
|
int size = len;
|
||||||
s.replace(items[i].key, items[i].val);
|
|
||||||
|
// Lets check how much memory will be needed to transform the template
|
||||||
|
for (int j = 0; j < len - 2; j++) {
|
||||||
|
if (*(format + j) == '$' && *(format + j + 1) == '{') {
|
||||||
|
// Start of format tag found
|
||||||
|
int max = sizeof(_items) / sizeof(KeyVal);
|
||||||
|
for (int i = 0; i < max; i++) {
|
||||||
|
if (strncmp(format + j, _items[i].key.c_str(),
|
||||||
|
_items[i].key.length()) == 0) {
|
||||||
|
// Found key
|
||||||
|
size = size - _items[i].key.length() + _items[i].val.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
freeMemory(); // In case this is reused
|
||||||
|
_output = static_cast<char *>(malloc(size + 20));
|
||||||
|
|
||||||
|
if (!_output) {
|
||||||
|
Log.error(F("TPL : Unable to allocate memory for transforming template, "
|
||||||
|
"needed %d." CR),
|
||||||
|
size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(_output, 0, size + 20);
|
||||||
|
|
||||||
|
// Lets do the transformation
|
||||||
|
int k = 0;
|
||||||
|
for (int j = 0; j < len - 2; j++) {
|
||||||
|
if (*(format + j) == '$' && *(format + j + 1) == '{') {
|
||||||
|
// Start of format tag found
|
||||||
|
int max = sizeof(_items) / sizeof(KeyVal);
|
||||||
|
for (int i = 0; i < max; i++) {
|
||||||
|
if (strncmp(format + j, _items[i].key.c_str(),
|
||||||
|
_items[i].key.length()) == 0) {
|
||||||
|
// Found key
|
||||||
|
strncat(_output, format + k, j - k);
|
||||||
|
strncat(_output, _items[i].val.c_str(), _items[i].val.length());
|
||||||
|
k = j + _items[i].key.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strncat(_output, format + k, size - k);
|
||||||
|
Log.notice(F("TPL : Transformed template %d chars to %d chars" CR),
|
||||||
|
strlen(format), strlen(_output));
|
||||||
|
|
||||||
|
#if LOG_LEVEL == 6
|
||||||
|
printHeap("TPL ");
|
||||||
|
Log.verboseln(format);
|
||||||
|
Log.verboseln(_output);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void dumpAll() {
|
void dumpAll() {
|
||||||
int max = sizeof(items) / sizeof(KeyVal);
|
int max = sizeof(_items) / sizeof(KeyVal);
|
||||||
for (int i = 0; i < max; i++) {
|
for (int i = 0; i < max; i++) {
|
||||||
Serial.print("Key=\'");
|
Serial.print("Key=\'");
|
||||||
Serial.print(items[i].key.c_str());
|
Serial.print(_items[i].key.c_str());
|
||||||
Serial.print("\', Val=\'");
|
Serial.print("\', Val=\'");
|
||||||
Serial.print(items[i].val.c_str());
|
Serial.print(_items[i].val.c_str());
|
||||||
Serial.println("\'");
|
Serial.println("\'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,9 +191,17 @@ class TemplatingEngine {
|
|||||||
TEMPLATE_MQTT = 4
|
TEMPLATE_MQTT = 4
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TemplatingEngine() {}
|
||||||
|
~TemplatingEngine() { freeMemory(); }
|
||||||
|
|
||||||
|
void freeMemory() {
|
||||||
|
if (_output) free(_output);
|
||||||
|
|
||||||
|
_output = 0;
|
||||||
|
}
|
||||||
void initialize(float angle, float gravitySG, float corrGravitySG,
|
void initialize(float angle, float gravitySG, float corrGravitySG,
|
||||||
float tempC, float runTime);
|
float tempC, float runTime);
|
||||||
const String& create(TemplatingEngine::Templates idx);
|
const char *create(TemplatingEngine::Templates idx);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SRC_TEMPLATING_HPP_
|
#endif // SRC_TEMPLATING_HPP_
|
||||||
|
@ -44,12 +44,15 @@ void WebServerHandler::webHandleConfig() {
|
|||||||
LOG_PERF_START("webserver-api-config");
|
LOG_PERF_START("webserver-api-config");
|
||||||
Log.notice(F("WEB : webServer callback for /api/config(get)." CR));
|
Log.notice(F("WEB : webServer callback for /api/config(get)." CR));
|
||||||
|
|
||||||
DynamicJsonDocument doc(CFG_JSON_BUFSIZE);
|
DynamicJsonDocument doc(2000);
|
||||||
myConfig.createJson(doc);
|
myConfig.createJson(doc);
|
||||||
|
|
||||||
doc[PARAM_PASS] = ""; // dont show the wifi password
|
doc[PARAM_PASS] = ""; // dont show the wifi password
|
||||||
doc[PARAM_PASS2] = "";
|
doc[PARAM_PASS2] = "";
|
||||||
|
|
||||||
|
doc[PARAM_APP_VER] = String(CFG_APPVER);
|
||||||
|
doc[PARAM_APP_BUILD] = String(CFG_GITREV);
|
||||||
|
|
||||||
double angle = 0;
|
double angle = 0;
|
||||||
|
|
||||||
if (myGyro.hasValue()) angle = myGyro.getAngle();
|
if (myGyro.hasValue()) angle = myGyro.getAngle();
|
||||||
@ -69,8 +72,8 @@ void WebServerHandler::webHandleConfig() {
|
|||||||
doc[PARAM_TEMP_ADJ] = reduceFloatPrecision(myConfig.getTempSensorAdjC(), 1);
|
doc[PARAM_TEMP_ADJ] = reduceFloatPrecision(myConfig.getTempSensorAdjC(), 1);
|
||||||
|
|
||||||
if (myConfig.isGravityTempAdj()) {
|
if (myConfig.isGravityTempAdj()) {
|
||||||
gravity =
|
gravity = gravityTemperatureCorrectionC(
|
||||||
gravityTemperatureCorrectionC(gravity, tempC, myConfig.getTempFormat());
|
gravity, tempC, myAdvancedConfig.getDefaultCalibrationTemp());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (myConfig.isGravityPlato()) {
|
if (myConfig.isGravityPlato()) {
|
||||||
@ -97,8 +100,9 @@ void WebServerHandler::webHandleConfig() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
String out;
|
String out;
|
||||||
out.reserve(CFG_JSON_BUFSIZE);
|
out.reserve(2000);
|
||||||
serializeJson(doc, out);
|
serializeJson(doc, out);
|
||||||
|
doc.clear();
|
||||||
_server->send(200, "application/json", out.c_str());
|
_server->send(200, "application/json", out.c_str());
|
||||||
LOG_PERF_STOP("webserver-api-config");
|
LOG_PERF_STOP("webserver-api-config");
|
||||||
}
|
}
|
||||||
@ -160,13 +164,11 @@ void WebServerHandler::webHandleUpload() {
|
|||||||
String out;
|
String out;
|
||||||
out.reserve(300);
|
out.reserve(300);
|
||||||
serializeJson(doc, out);
|
serializeJson(doc, out);
|
||||||
|
doc.clear();
|
||||||
_server->send(200, "application/json", out.c_str());
|
_server->send(200, "application/json", out.c_str());
|
||||||
LOG_PERF_STOP("webserver-api-upload");
|
LOG_PERF_STOP("webserver-api-upload");
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Callback from webServer when / has been accessed.
|
|
||||||
//
|
|
||||||
void WebServerHandler::webHandleUploadFile() {
|
void WebServerHandler::webHandleUploadFile() {
|
||||||
LOG_PERF_START("webserver-api-upload-file");
|
LOG_PERF_START("webserver-api-upload-file");
|
||||||
Log.verbose(F("WEB : webServer callback for /api/upload(post)." CR));
|
Log.verbose(F("WEB : webServer callback for /api/upload(post)." CR));
|
||||||
@ -208,9 +210,7 @@ void WebServerHandler::webHandleUploadFile() {
|
|||||||
maxSketchSpace / 1024);
|
maxSketchSpace / 1024);
|
||||||
|
|
||||||
if (!Update.begin(maxSketchSpace, U_FLASH, PIN_LED)) {
|
if (!Update.begin(maxSketchSpace, U_FLASH, PIN_LED)) {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("WEB : Not enough space to store for this firmware.");
|
||||||
errLog.addEntry(
|
|
||||||
F("WEB : Not enough space to store for this firmware."));
|
|
||||||
_uploadReturn = 500;
|
_uploadReturn = 500;
|
||||||
}
|
}
|
||||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||||
@ -232,9 +232,8 @@ void WebServerHandler::webHandleUploadFile() {
|
|||||||
delay(500);
|
delay(500);
|
||||||
ESP_RESET();
|
ESP_RESET();
|
||||||
} else {
|
} else {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("WEB : Failed to finish firmware flashing error=%d",
|
||||||
errLog.addEntry("WEB : Failed to finish firmware flashing error=" +
|
Update.getError());
|
||||||
String(Update.getError()));
|
|
||||||
_uploadReturn = 500;
|
_uploadReturn = 500;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -292,9 +291,6 @@ void WebServerHandler::webHandleCalibrate() {
|
|||||||
LOG_PERF_STOP("webserver-api-calibrate");
|
LOG_PERF_STOP("webserver-api-calibrate");
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Callback from webServer when / has been accessed.
|
|
||||||
//
|
|
||||||
void WebServerHandler::webHandleFactoryDefaults() {
|
void WebServerHandler::webHandleFactoryDefaults() {
|
||||||
String id = _server->arg(PARAM_ID);
|
String id = _server->arg(PARAM_ID);
|
||||||
Log.notice(F("WEB : webServer callback for /api/factory." CR));
|
Log.notice(F("WEB : webServer callback for /api/factory." CR));
|
||||||
@ -318,16 +314,27 @@ void WebServerHandler::webHandleFactoryDefaults() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
void WebServerHandler::webHandleLogClear() {
|
||||||
// Callback from webServer when / has been accessed.
|
String id = _server->arg(PARAM_ID);
|
||||||
//
|
Log.notice(F("WEB : webServer callback for /api/clearlog." CR));
|
||||||
|
|
||||||
|
if (!id.compareTo(myConfig.getID())) {
|
||||||
|
_server->send(200, "text/plain", "Removing logfiles...");
|
||||||
|
LittleFS.remove(ERR_FILENAME);
|
||||||
|
LittleFS.remove(ERR_FILENAME2);
|
||||||
|
_server->send(200, "text/plain", "Logfiles cleared.");
|
||||||
|
} else {
|
||||||
|
_server->send(400, "text/plain", "Unknown ID.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void WebServerHandler::webHandleStatus() {
|
void WebServerHandler::webHandleStatus() {
|
||||||
LOG_PERF_START("webserver-api-status");
|
LOG_PERF_START("webserver-api-status");
|
||||||
Log.notice(F("WEB : webServer callback for /api/status(get)." CR));
|
Log.notice(F("WEB : webServer callback for /api/status(get)." CR));
|
||||||
|
|
||||||
DynamicJsonDocument doc(512);
|
DynamicJsonDocument doc(500);
|
||||||
|
|
||||||
double angle = 0;
|
double angle = 0; // Indicate we have no valid gyro value
|
||||||
|
|
||||||
if (myGyro.hasValue()) angle = myGyro.getAngle();
|
if (myGyro.hasValue()) angle = myGyro.getAngle();
|
||||||
|
|
||||||
@ -335,9 +342,13 @@ void WebServerHandler::webHandleStatus() {
|
|||||||
double gravity = calculateGravity(angle, tempC);
|
double gravity = calculateGravity(angle, tempC);
|
||||||
|
|
||||||
doc[PARAM_ID] = myConfig.getID();
|
doc[PARAM_ID] = myConfig.getID();
|
||||||
doc[PARAM_ANGLE] = reduceFloatPrecision(angle);
|
doc[PARAM_ANGLE] = myGyro.isConnected()
|
||||||
|
? reduceFloatPrecision(angle)
|
||||||
|
: -1; // Indicate that we have no connection to gyro
|
||||||
|
|
||||||
if (myConfig.isGravityTempAdj()) {
|
if (myConfig.isGravityTempAdj()) {
|
||||||
gravity = gravityTemperatureCorrectionC(gravity, tempC);
|
gravity = gravityTemperatureCorrectionC(
|
||||||
|
gravity, tempC, myAdvancedConfig.getDefaultCalibrationTemp());
|
||||||
}
|
}
|
||||||
if (myConfig.isGravityPlato()) {
|
if (myConfig.isGravityPlato()) {
|
||||||
doc[PARAM_GRAVITY] = reduceFloatPrecision(convertToPlato(gravity), 1);
|
doc[PARAM_GRAVITY] = reduceFloatPrecision(convertToPlato(gravity), 1);
|
||||||
@ -376,15 +387,13 @@ void WebServerHandler::webHandleStatus() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
String out;
|
String out;
|
||||||
out.reserve(300);
|
out.reserve(500);
|
||||||
serializeJson(doc, out);
|
serializeJson(doc, out);
|
||||||
|
doc.clear();
|
||||||
_server->send(200, "application/json", out.c_str());
|
_server->send(200, "application/json", out.c_str());
|
||||||
LOG_PERF_STOP("webserver-api-status");
|
LOG_PERF_STOP("webserver-api-status");
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Callback from webServer when / has been accessed.
|
|
||||||
//
|
|
||||||
void WebServerHandler::webHandleClearWIFI() {
|
void WebServerHandler::webHandleClearWIFI() {
|
||||||
String id = _server->arg(PARAM_ID);
|
String id = _server->arg(PARAM_ID);
|
||||||
Log.notice(F("WEB : webServer callback for /api/clearwifi." CR));
|
Log.notice(F("WEB : webServer callback for /api/clearwifi." CR));
|
||||||
@ -405,9 +414,6 @@ void WebServerHandler::webHandleClearWIFI() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Used to force the device to never sleep.
|
|
||||||
//
|
|
||||||
void WebServerHandler::webHandleStatusSleepmode() {
|
void WebServerHandler::webHandleStatusSleepmode() {
|
||||||
LOG_PERF_START("webserver-api-sleepmode");
|
LOG_PERF_START("webserver-api-sleepmode");
|
||||||
String id = _server->arg(PARAM_ID);
|
String id = _server->arg(PARAM_ID);
|
||||||
@ -605,8 +611,10 @@ void WebServerHandler::webHandleConfigHardware() {
|
|||||||
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
|
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (_server->hasArg(PARAM_VOLTAGEFACTOR))
|
if (_server->hasArg(PARAM_VOLTAGE_FACTOR))
|
||||||
myConfig.setVoltageFactor(_server->arg(PARAM_VOLTAGEFACTOR).toFloat());
|
myConfig.setVoltageFactor(_server->arg(PARAM_VOLTAGE_FACTOR).toFloat());
|
||||||
|
if (_server->hasArg(PARAM_VOLTAGE_CONFIG))
|
||||||
|
myConfig.setVoltageConfig(_server->arg(PARAM_VOLTAGE_CONFIG).toFloat());
|
||||||
if (_server->hasArg(PARAM_TEMP_ADJ)) {
|
if (_server->hasArg(PARAM_TEMP_ADJ)) {
|
||||||
if (myConfig.isTempC()) {
|
if (myConfig.isTempC()) {
|
||||||
myConfig.setTempSensorAdjC(_server->arg(PARAM_TEMP_ADJ));
|
myConfig.setTempSensorAdjC(_server->arg(PARAM_TEMP_ADJ));
|
||||||
@ -624,6 +632,12 @@ void WebServerHandler::webHandleConfigHardware() {
|
|||||||
_server->arg(PARAM_GYRO_TEMP).equalsIgnoreCase("on") ? true : false);
|
_server->arg(PARAM_GYRO_TEMP).equalsIgnoreCase("on") ? true : false);
|
||||||
else
|
else
|
||||||
myConfig.setGyroTemp(false);
|
myConfig.setGyroTemp(false);
|
||||||
|
if (_server->hasArg(PARAM_STORAGE_SLEEP))
|
||||||
|
myConfig.setStorageSleep(
|
||||||
|
_server->arg(PARAM_STORAGE_SLEEP).equalsIgnoreCase("on") ? true
|
||||||
|
: false);
|
||||||
|
else
|
||||||
|
myConfig.setStorageSleep(false);
|
||||||
|
|
||||||
myConfig.saveFile();
|
myConfig.saveFile();
|
||||||
_server->sendHeader("Location", "/config.htm#collapseHardware", true);
|
_server->sendHeader("Location", "/config.htm#collapseHardware", true);
|
||||||
@ -663,9 +677,11 @@ void WebServerHandler::webHandleConfigAdvancedWrite() {
|
|||||||
if (_server->hasArg(PARAM_HW_FORMULA_DEVIATION))
|
if (_server->hasArg(PARAM_HW_FORMULA_DEVIATION))
|
||||||
myAdvancedConfig.setMaxFormulaCreationDeviation(
|
myAdvancedConfig.setMaxFormulaCreationDeviation(
|
||||||
_server->arg(PARAM_HW_FORMULA_DEVIATION).toFloat());
|
_server->arg(PARAM_HW_FORMULA_DEVIATION).toFloat());
|
||||||
if (_server->hasArg(PARAM_HW_FORMULA_CALIBRATION_TEMP))
|
if (_server->hasArg(PARAM_HW_FORMULA_CALIBRATION_TEMP)) {
|
||||||
myAdvancedConfig.SetDefaultCalibrationTemp(
|
float t = _server->arg(PARAM_HW_FORMULA_CALIBRATION_TEMP).toFloat();
|
||||||
_server->arg(PARAM_HW_FORMULA_CALIBRATION_TEMP).toFloat());
|
if (myConfig.isTempF()) t = convertFtoC(t);
|
||||||
|
myAdvancedConfig.SetDefaultCalibrationTemp(t);
|
||||||
|
}
|
||||||
if (_server->hasArg(PARAM_HW_WIFI_PORTAL_TIMEOUT))
|
if (_server->hasArg(PARAM_HW_WIFI_PORTAL_TIMEOUT))
|
||||||
myAdvancedConfig.setWifiPortalTimeout(
|
myAdvancedConfig.setWifiPortalTimeout(
|
||||||
_server->arg(PARAM_HW_WIFI_PORTAL_TIMEOUT).toInt());
|
_server->arg(PARAM_HW_WIFI_PORTAL_TIMEOUT).toInt());
|
||||||
@ -714,7 +730,7 @@ void WebServerHandler::webHandleConfigAdvancedRead() {
|
|||||||
LOG_PERF_START("webserver-api-config-advanced");
|
LOG_PERF_START("webserver-api-config-advanced");
|
||||||
Log.notice(F("WEB : webServer callback for /api/config/advanced(get)." CR));
|
Log.notice(F("WEB : webServer callback for /api/config/advanced(get)." CR));
|
||||||
|
|
||||||
DynamicJsonDocument doc(512);
|
DynamicJsonDocument doc(500);
|
||||||
|
|
||||||
doc[PARAM_HW_GYRO_READ_COUNT] = myAdvancedConfig.getGyroReadCount();
|
doc[PARAM_HW_GYRO_READ_COUNT] = myAdvancedConfig.getGyroReadCount();
|
||||||
// doc[PARAM_HW_GYRO_READ_DELAY] = myAdvancedConfig.getGyroReadDelay();
|
// doc[PARAM_HW_GYRO_READ_DELAY] = myAdvancedConfig.getGyroReadDelay();
|
||||||
@ -725,8 +741,9 @@ void WebServerHandler::webHandleConfigAdvancedRead() {
|
|||||||
doc[PARAM_HW_WIFI_PORTAL_TIMEOUT] = myAdvancedConfig.getWifiPortalTimeout();
|
doc[PARAM_HW_WIFI_PORTAL_TIMEOUT] = myAdvancedConfig.getWifiPortalTimeout();
|
||||||
doc[PARAM_HW_WIFI_CONNECT_TIMEOUT] = myAdvancedConfig.getWifiConnectTimeout();
|
doc[PARAM_HW_WIFI_CONNECT_TIMEOUT] = myAdvancedConfig.getWifiConnectTimeout();
|
||||||
doc[PARAM_HW_PUSH_TIMEOUT] = myAdvancedConfig.getPushTimeout();
|
doc[PARAM_HW_PUSH_TIMEOUT] = myAdvancedConfig.getPushTimeout();
|
||||||
|
float t = myAdvancedConfig.getDefaultCalibrationTemp();
|
||||||
doc[PARAM_HW_FORMULA_CALIBRATION_TEMP] =
|
doc[PARAM_HW_FORMULA_CALIBRATION_TEMP] =
|
||||||
myAdvancedConfig.getDefaultCalibrationTemp();
|
myConfig.isTempC() ? t : reduceFloatPrecision(convertCtoF(t), 1);
|
||||||
doc[PARAM_HW_PUSH_INTERVAL_HTTP1] = myAdvancedConfig.getPushIntervalHttp1();
|
doc[PARAM_HW_PUSH_INTERVAL_HTTP1] = myAdvancedConfig.getPushIntervalHttp1();
|
||||||
doc[PARAM_HW_PUSH_INTERVAL_HTTP2] = myAdvancedConfig.getPushIntervalHttp2();
|
doc[PARAM_HW_PUSH_INTERVAL_HTTP2] = myAdvancedConfig.getPushIntervalHttp2();
|
||||||
doc[PARAM_HW_PUSH_INTERVAL_HTTP3] = myAdvancedConfig.getPushIntervalHttp3();
|
doc[PARAM_HW_PUSH_INTERVAL_HTTP3] = myAdvancedConfig.getPushIntervalHttp3();
|
||||||
@ -742,8 +759,9 @@ void WebServerHandler::webHandleConfigAdvancedRead() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
String out;
|
String out;
|
||||||
out.reserve(512);
|
out.reserve(500);
|
||||||
serializeJson(doc, out);
|
serializeJson(doc, out);
|
||||||
|
doc.clear();
|
||||||
_server->send(200, "application/json", out.c_str());
|
_server->send(200, "application/json", out.c_str());
|
||||||
LOG_PERF_STOP("webserver-api-config-advanced");
|
LOG_PERF_STOP("webserver-api-config-advanced");
|
||||||
}
|
}
|
||||||
@ -755,7 +773,7 @@ void WebServerHandler::webHandleFormulaRead() {
|
|||||||
LOG_PERF_START("webserver-api-formula-read");
|
LOG_PERF_START("webserver-api-formula-read");
|
||||||
Log.notice(F("WEB : webServer callback for /api/formula(get)." CR));
|
Log.notice(F("WEB : webServer callback for /api/formula(get)." CR));
|
||||||
|
|
||||||
DynamicJsonDocument doc(512);
|
DynamicJsonDocument doc(500);
|
||||||
const RawFormulaData& fd = myConfig.getFormulaData();
|
const RawFormulaData& fd = myConfig.getFormulaData();
|
||||||
|
|
||||||
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
||||||
@ -773,10 +791,13 @@ void WebServerHandler::webHandleFormulaRead() {
|
|||||||
doc[PARAM_ERROR] = "Internal error creating formula.";
|
doc[PARAM_ERROR] = "Internal error creating formula.";
|
||||||
break;
|
break;
|
||||||
case ERR_FORMULA_NOTENOUGHVALUES:
|
case ERR_FORMULA_NOTENOUGHVALUES:
|
||||||
doc[PARAM_ERROR] = "Not enough values to create formula.";
|
doc[PARAM_ERROR] =
|
||||||
|
"Not enough values to create formula, need at least 3 angles.";
|
||||||
break;
|
break;
|
||||||
case ERR_FORMULA_UNABLETOFFIND:
|
case ERR_FORMULA_UNABLETOFFIND:
|
||||||
doc[PARAM_ERROR] = "Unable to find an accurate formula based on input.";
|
doc[PARAM_ERROR] =
|
||||||
|
"Unable to find an accurate formula based on input, check error log "
|
||||||
|
"and graph below.";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
doc[PARAM_GRAVITY_FORMULA] = myConfig.getGravityFormula();
|
doc[PARAM_GRAVITY_FORMULA] = myConfig.getGravityFormula();
|
||||||
@ -824,8 +845,9 @@ void WebServerHandler::webHandleFormulaRead() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
String out;
|
String out;
|
||||||
out.reserve(256);
|
out.reserve(500);
|
||||||
serializeJson(doc, out);
|
serializeJson(doc, out);
|
||||||
|
doc.clear();
|
||||||
_server->send(200, "application/json", out.c_str());
|
_server->send(200, "application/json", out.c_str());
|
||||||
LOG_PERF_STOP("webserver-api-formula-read");
|
LOG_PERF_STOP("webserver-api-formula-read");
|
||||||
}
|
}
|
||||||
@ -869,8 +891,7 @@ void WebServerHandler::webHandleConfigFormatWrite() {
|
|||||||
_server->sendHeader("Location", "/format.htm", true);
|
_server->sendHeader("Location", "/format.htm", true);
|
||||||
_server->send(302, "text/plain", "Format updated");
|
_server->send(302, "text/plain", "Format updated");
|
||||||
} else {
|
} else {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("WEB : Unable to store format file");
|
||||||
errLog.addEntry(F("WEB : Unable to store format file"));
|
|
||||||
_server->send(400, "text/plain", "Unable to store format in file.");
|
_server->send(400, "text/plain", "Unable to store format in file.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -900,7 +921,8 @@ void WebServerHandler::webHandleTestPush() {
|
|||||||
float angle = myGyro.getAngle();
|
float angle = myGyro.getAngle();
|
||||||
float tempC = myTempSensor.getTempC(myConfig.isGyroTemp());
|
float tempC = myTempSensor.getTempC(myConfig.isGyroTemp());
|
||||||
float gravitySG = calculateGravity(angle, tempC);
|
float gravitySG = calculateGravity(angle, tempC);
|
||||||
float corrGravitySG = gravityTemperatureCorrectionC(gravitySG, tempC);
|
float corrGravitySG = gravityTemperatureCorrectionC(
|
||||||
|
gravitySG, tempC, myAdvancedConfig.getDefaultCalibrationTemp());
|
||||||
|
|
||||||
TemplatingEngine engine;
|
TemplatingEngine engine;
|
||||||
engine.initialize(angle, gravitySG, corrGravitySG, tempC, 2.1);
|
engine.initialize(angle, gravitySG, corrGravitySG, tempC, 2.1);
|
||||||
@ -923,10 +945,11 @@ void WebServerHandler::webHandleTestPush() {
|
|||||||
push.sendInfluxDb2(engine, myConfig.isInfluxSSL());
|
push.sendInfluxDb2(engine, myConfig.isInfluxSSL());
|
||||||
enabled = true;
|
enabled = true;
|
||||||
} else if (!type.compareTo(PARAM_FORMAT_MQTT) && myConfig.isMqttActive()) {
|
} else if (!type.compareTo(PARAM_FORMAT_MQTT) && myConfig.isMqttActive()) {
|
||||||
push.sendMqtt(engine, myConfig.isMqttSSL());
|
push.sendMqtt(engine, myConfig.isMqttSSL(), false);
|
||||||
enabled = true;
|
enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
engine.freeMemory();
|
||||||
DynamicJsonDocument doc(100);
|
DynamicJsonDocument doc(100);
|
||||||
doc[PARAM_PUSH_ENABLED] = enabled;
|
doc[PARAM_PUSH_ENABLED] = enabled;
|
||||||
doc[PARAM_PUSH_SUCCESS] = push.getLastSuccess();
|
doc[PARAM_PUSH_SUCCESS] = push.getLastSuccess();
|
||||||
@ -935,6 +958,7 @@ void WebServerHandler::webHandleTestPush() {
|
|||||||
String out;
|
String out;
|
||||||
out.reserve(100);
|
out.reserve(100);
|
||||||
serializeJson(doc, out);
|
serializeJson(doc, out);
|
||||||
|
doc.clear();
|
||||||
|
|
||||||
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
||||||
serializeJson(doc, Serial);
|
serializeJson(doc, Serial);
|
||||||
@ -997,48 +1021,52 @@ void WebServerHandler::webHandleConfigFormatRead() {
|
|||||||
LOG_PERF_START("webserver-api-config-format-read");
|
LOG_PERF_START("webserver-api-config-format-read");
|
||||||
Log.notice(F("WEB : webServer callback for /api/config/formula(get)." CR));
|
Log.notice(F("WEB : webServer callback for /api/config/formula(get)." CR));
|
||||||
|
|
||||||
DynamicJsonDocument doc(2048);
|
String out;
|
||||||
|
out.reserve(7000);
|
||||||
doc[PARAM_ID] = myConfig.getID();
|
out += "{\"id\":\"" + String(myConfig.getID()) + "\",";
|
||||||
|
|
||||||
String s = readFile(TPL_FNAME_HTTP1);
|
String s = readFile(TPL_FNAME_HTTP1);
|
||||||
|
out += "\"" + String(PARAM_FORMAT_HTTP1) + "\":\"";
|
||||||
if (s.length())
|
if (s.length())
|
||||||
doc[PARAM_FORMAT_HTTP1] = urlencode(s);
|
out += urlencode(s);
|
||||||
else
|
else
|
||||||
doc[PARAM_FORMAT_HTTP1] = urlencode(String(&iSpindleFormat[0]));
|
out += urlencode(String(&iSpindleFormat[0]));
|
||||||
|
|
||||||
s = readFile(TPL_FNAME_HTTP2);
|
s = readFile(TPL_FNAME_HTTP2);
|
||||||
|
out += "\",\"" + String(PARAM_FORMAT_HTTP2) + "\":\"";
|
||||||
if (s.length())
|
if (s.length())
|
||||||
doc[PARAM_FORMAT_HTTP2] = urlencode(s);
|
out += urlencode(s);
|
||||||
else
|
else
|
||||||
doc[PARAM_FORMAT_HTTP2] = urlencode(String(&iSpindleFormat[0]));
|
out += urlencode(String(&iSpindleFormat[0]));
|
||||||
|
|
||||||
s = readFile(TPL_FNAME_HTTP3);
|
s = readFile(TPL_FNAME_HTTP3);
|
||||||
|
out += "\",\"" + String(PARAM_FORMAT_HTTP3) + "\":\"";
|
||||||
if (s.length())
|
if (s.length())
|
||||||
doc[PARAM_FORMAT_HTTP3] = urlencode(s);
|
out += urlencode(s);
|
||||||
else
|
else
|
||||||
doc[PARAM_FORMAT_HTTP3] = urlencode(String(&iHttpGetFormat[0]));
|
out += urlencode(String(&iHttpGetFormat[0]));
|
||||||
|
|
||||||
s = readFile(TPL_FNAME_INFLUXDB);
|
s = readFile(TPL_FNAME_INFLUXDB);
|
||||||
|
out += "\",\"" + String(PARAM_FORMAT_INFLUXDB) + "\":\"";
|
||||||
if (s.length())
|
if (s.length())
|
||||||
doc[PARAM_FORMAT_INFLUXDB] = urlencode(s);
|
out += urlencode(s);
|
||||||
else
|
else
|
||||||
doc[PARAM_FORMAT_INFLUXDB] = urlencode(String(&influxDbFormat[0]));
|
out += urlencode(String(&influxDbFormat[0]));
|
||||||
|
|
||||||
s = readFile(TPL_FNAME_MQTT);
|
s = readFile(TPL_FNAME_MQTT);
|
||||||
|
out += "\",\"" + String(PARAM_FORMAT_MQTT) + "\":\"";
|
||||||
if (s.length())
|
if (s.length())
|
||||||
doc[PARAM_FORMAT_MQTT] = urlencode(s);
|
out += urlencode(s);
|
||||||
else
|
else
|
||||||
doc[PARAM_FORMAT_MQTT] = urlencode(String(&mqttFormat[0]));
|
out += urlencode(String(&mqttFormat[0]));
|
||||||
|
|
||||||
|
out += "\"}";
|
||||||
|
|
||||||
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
||||||
serializeJson(doc, Serial);
|
Serial.print(out.c_str());
|
||||||
Serial.print(CR);
|
Serial.print(CR);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
String out;
|
|
||||||
out.reserve(2048);
|
|
||||||
serializeJson(doc, out);
|
|
||||||
_server->send(200, "application/json", out.c_str());
|
_server->send(200, "application/json", out.c_str());
|
||||||
LOG_PERF_STOP("webserver-api-config-format-read");
|
LOG_PERF_STOP("webserver-api-config-format-read");
|
||||||
}
|
}
|
||||||
@ -1099,6 +1127,7 @@ void WebServerHandler::webHandleFormulaWrite() {
|
|||||||
fd.g[9] = _server->arg("g10").toDouble();
|
fd.g[9] = _server->arg("g10").toDouble();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fd.g[0] = 1; // force first point to SG gravity of water
|
||||||
myConfig.setFormulaData(fd);
|
myConfig.setFormulaData(fd);
|
||||||
|
|
||||||
int e;
|
int e;
|
||||||
@ -1228,6 +1257,7 @@ bool WebServerHandler::setupWebServer() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Static content
|
// Static content
|
||||||
|
Log.notice(F("WEB : Setting up handlers for web server." CR));
|
||||||
#if defined(EMBED_HTML)
|
#if defined(EMBED_HTML)
|
||||||
_server->on("/", std::bind(&WebServerHandler::webReturnIndexHtm, this));
|
_server->on("/", std::bind(&WebServerHandler::webReturnIndexHtm, this));
|
||||||
_server->on("/index.htm",
|
_server->on("/index.htm",
|
||||||
@ -1270,30 +1300,26 @@ bool WebServerHandler::setupWebServer() {
|
|||||||
_server->on("/firmware.htm",
|
_server->on("/firmware.htm",
|
||||||
std::bind(&WebServerHandler::webReturnFirmwareHtm, this));
|
std::bind(&WebServerHandler::webReturnFirmwareHtm, this));
|
||||||
_server->serveStatic("/log", LittleFS, ERR_FILENAME);
|
_server->serveStatic("/log", LittleFS, ERR_FILENAME);
|
||||||
|
_server->serveStatic("/log2", LittleFS, ERR_FILENAME2);
|
||||||
_server->serveStatic("/runtime", LittleFS, RUNTIME_FILENAME);
|
_server->serveStatic("/runtime", LittleFS, RUNTIME_FILENAME);
|
||||||
|
|
||||||
// Dynamic content
|
// Dynamic content
|
||||||
_server->on(
|
_server->on("/api/clearlog", HTTP_GET,
|
||||||
"/api/config", HTTP_GET,
|
std::bind(&WebServerHandler::webHandleLogClear, this));
|
||||||
std::bind(&WebServerHandler::webHandleConfig, this)); // Get config.json
|
_server->on("/api/config", HTTP_GET,
|
||||||
|
std::bind(&WebServerHandler::webHandleConfig, this));
|
||||||
_server->on("/api/formula", HTTP_GET,
|
_server->on("/api/formula", HTTP_GET,
|
||||||
std::bind(&WebServerHandler::webHandleFormulaRead,
|
std::bind(&WebServerHandler::webHandleFormulaRead, this));
|
||||||
this)); // Get formula.json (calibration page)
|
|
||||||
_server->on("/api/formula", HTTP_POST,
|
_server->on("/api/formula", HTTP_POST,
|
||||||
std::bind(&WebServerHandler::webHandleFormulaWrite,
|
std::bind(&WebServerHandler::webHandleFormulaWrite, this));
|
||||||
this)); // Get formula.json (calibration page)
|
|
||||||
_server->on("/api/calibrate", HTTP_POST,
|
_server->on("/api/calibrate", HTTP_POST,
|
||||||
std::bind(&WebServerHandler::webHandleCalibrate,
|
std::bind(&WebServerHandler::webHandleCalibrate, this));
|
||||||
this)); // Run calibration routine (param id)
|
|
||||||
_server->on("/api/factory", HTTP_GET,
|
_server->on("/api/factory", HTTP_GET,
|
||||||
std::bind(&WebServerHandler::webHandleFactoryDefaults,
|
std::bind(&WebServerHandler::webHandleFactoryDefaults, this));
|
||||||
this)); // Reset the device
|
|
||||||
_server->on("/api/status", HTTP_GET,
|
_server->on("/api/status", HTTP_GET,
|
||||||
std::bind(&WebServerHandler::webHandleStatus,
|
std::bind(&WebServerHandler::webHandleStatus, this));
|
||||||
this)); // Get the status.json
|
|
||||||
_server->on("/api/clearwifi", HTTP_GET,
|
_server->on("/api/clearwifi", HTTP_GET,
|
||||||
std::bind(&WebServerHandler::webHandleClearWIFI,
|
std::bind(&WebServerHandler::webHandleClearWIFI, this));
|
||||||
this)); // Clear wifi settings
|
|
||||||
_server->on(
|
_server->on(
|
||||||
"/api/upload", HTTP_GET,
|
"/api/upload", HTTP_GET,
|
||||||
std::bind(&WebServerHandler::webHandleUpload, this)); // Get upload.json
|
std::bind(&WebServerHandler::webHandleUpload, this)); // Get upload.json
|
||||||
@ -1340,9 +1366,6 @@ bool WebServerHandler::setupWebServer() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// called from main loop
|
|
||||||
//
|
|
||||||
void WebServerHandler::loop() {
|
void WebServerHandler::loop() {
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
MDNS.update();
|
MDNS.update();
|
||||||
|
@ -76,6 +76,7 @@ class WebServerHandler {
|
|||||||
void webHandleCalibrate();
|
void webHandleCalibrate();
|
||||||
void webHandleUploadFile();
|
void webHandleUploadFile();
|
||||||
void webHandleUpload();
|
void webHandleUpload();
|
||||||
|
void webHandleLogClear();
|
||||||
void webHandlePageNotFound();
|
void webHandlePageNotFound();
|
||||||
|
|
||||||
String readFile(String fname);
|
String readFile(String fname);
|
||||||
|
15
src/wifi.cpp
@ -203,9 +203,7 @@ bool WifiConnection::waitForConnection(int maxTime) {
|
|||||||
|
|
||||||
if (i++ >
|
if (i++ >
|
||||||
(maxTime * 10)) { // Try for maxTime seconds. Since delay is 100ms.
|
(maxTime * 10)) { // Try for maxTime seconds. Since delay is 100ms.
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("WIFI: Failed to connect to wifi %d", WiFi.status());
|
||||||
errLog.addEntry("WIFI: Failed to connect to wifi " +
|
|
||||||
String(WiFi.status()));
|
|
||||||
WiFi.disconnect();
|
WiFi.disconnect();
|
||||||
Serial.print(CR);
|
Serial.print(CR);
|
||||||
return false; // Return to main that we have failed to connect.
|
return false; // Return to main that we have failed to connect.
|
||||||
@ -346,9 +344,7 @@ bool WifiConnection::updateFirmware() {
|
|||||||
|
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case HTTP_UPDATE_FAILED: {
|
case HTTP_UPDATE_FAILED: {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("WIFI: OTA update failed %d", ESPhttpUpdate.getLastError());
|
||||||
errLog.addEntry("WIFI: OTA update failed " +
|
|
||||||
String(ESPhttpUpdate.getLastError()));
|
|
||||||
} break;
|
} break;
|
||||||
case HTTP_UPDATE_NO_UPDATES:
|
case HTTP_UPDATE_NO_UPDATES:
|
||||||
break;
|
break;
|
||||||
@ -378,9 +374,7 @@ void WifiConnection::downloadFile(HTTPClient &http, String &fname) {
|
|||||||
f.close();
|
f.close();
|
||||||
Log.notice(F("WIFI: Downloaded file %s." CR), fname.c_str());
|
Log.notice(F("WIFI: Downloaded file %s." CR), fname.c_str());
|
||||||
} else {
|
} else {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("WIFI: Failed to download html-file %d", httpResponseCode);
|
||||||
errLog.addEntry("WIFI: Failed to download html-file " +
|
|
||||||
String(httpResponseCode));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,8 +413,7 @@ bool WifiConnection::checkFirmwareVersion() {
|
|||||||
#endif
|
#endif
|
||||||
DeserializationError err = deserializeJson(ver, payload);
|
DeserializationError err = deserializeJson(ver, payload);
|
||||||
if (err) {
|
if (err) {
|
||||||
ErrorFileLog errLog;
|
writeErrorLog("WIFI: Failed to parse version.json");
|
||||||
errLog.addEntry(F("WIFI: Failed to parse version.json"));
|
|
||||||
} else {
|
} else {
|
||||||
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
|
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
|
||||||
Log.verbose(F("WIFI: Project %s version %s." CR),
|
Log.verbose(F("WIFI: Project %s version %s." CR),
|
||||||
|
@ -17,7 +17,7 @@ To reduce the need for adding custom endpoints for various services there is an
|
|||||||
:alt: Format editor
|
:alt: Format editor
|
||||||
|
|
||||||
You enter the format data in the text field and the test button will show an example on what the output would look like. If the data cannot be formatted in json it will just be displayed as a long string.
|
You enter the format data in the text field and the test button will show an example on what the output would look like. If the data cannot be formatted in json it will just be displayed as a long string.
|
||||||
The save button will save the current formla and reload the data from the device.
|
The save button will save the current formula and reload the data from the device.
|
||||||
|
|
||||||
You can also select a template from the list and copy that to the current endpoint.
|
You can also select a template from the list and copy that to the current endpoint.
|
||||||
|
|
||||||
@ -99,4 +99,10 @@ These are the format keys available for use in the format.
|
|||||||
* - ${gravity-unit}
|
* - ${gravity-unit}
|
||||||
- Gravity format, `G` or `P`
|
- Gravity format, `G` or `P`
|
||||||
- G
|
- G
|
||||||
|
* - ${app-ver}
|
||||||
|
- Software version
|
||||||
|
- 1.1.0
|
||||||
|
* - ${app-build}
|
||||||
|
- Software revision (git hash)
|
||||||
|
- ..e456743
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ All the API's use a key called ``ID`` which is the unique device id (chip id). T
|
|||||||
GET: /api/config
|
GET: /api/config
|
||||||
================
|
================
|
||||||
|
|
||||||
Retrive the current configuation of the device via an HTTP GET command. Payload is in JSON format.
|
Retrieve the current configuration of the device via an HTTP GET command. Payload is in JSON format.
|
||||||
|
|
||||||
* ``temp-format`` can be either ``C`` or ``F``
|
* ``temp-format`` can be either ``C`` or ``F``
|
||||||
* ``gravity-format`` is always ``G`` (plato is not yet supported)
|
* ``gravity-format`` is always ``G`` (plato is not yet supported)
|
||||||
@ -65,6 +65,8 @@ Other parameters are the same as in the configuration guide.
|
|||||||
"angle": 90.93,
|
"angle": 90.93,
|
||||||
"gravity": 1.105,
|
"gravity": 1.105,
|
||||||
"battery": 0.04,
|
"battery": 0.04,
|
||||||
|
"app-ver": "0.1.0",
|
||||||
|
"app-build": "build",
|
||||||
"platform": "esp8266",
|
"platform": "esp8266",
|
||||||
"runtime-average": 3.12
|
"runtime-average": 3.12
|
||||||
}
|
}
|
||||||
@ -79,10 +81,12 @@ This API has been removed from 0.9 and merged with /api/status
|
|||||||
GET: /api/status
|
GET: /api/status
|
||||||
================
|
================
|
||||||
|
|
||||||
Retrive the current device status via an HTTP GET command. Payload is in JSON format.
|
Retrieve the current device status via an HTTP GET command. Payload is in JSON format.
|
||||||
|
|
||||||
* ``temp-format`` can be either ``C`` or ``F``
|
* ``temp-format`` can be either ``C`` or ``F``
|
||||||
* ``platform`` can be either ``esp8266`` or ``esp32``
|
* ``platform`` can be either ``esp8266`` or ``esp32``
|
||||||
|
* ``temp-c`` will be set to -273 C if there is no temp sensor
|
||||||
|
* ``angle`` will be set to 0 if no valid angle is found and -1 if there is no gyro
|
||||||
|
|
||||||
Other parameters are the same as in the configuration guide.
|
Other parameters are the same as in the configuration guide.
|
||||||
|
|
||||||
@ -102,6 +106,7 @@ Other parameters are the same as in the configuration guide.
|
|||||||
"token2": "token2",
|
"token2": "token2",
|
||||||
"rssi": -56,
|
"rssi": -56,
|
||||||
"app-ver": "0.0.0",
|
"app-ver": "0.0.0",
|
||||||
|
"app-build": "gitrev",
|
||||||
"mdns": "gravmon",
|
"mdns": "gravmon",
|
||||||
"sleep-interval": 30,
|
"sleep-interval": 30,
|
||||||
"platform": "esp8266",
|
"platform": "esp8266",
|
||||||
@ -112,10 +117,10 @@ Other parameters are the same as in the configuration guide.
|
|||||||
GET: /api/config/formula
|
GET: /api/config/formula
|
||||||
========================
|
========================
|
||||||
|
|
||||||
Retrive the data used for formula calculation data via an HTTP GET command. Payload is in JSON format.
|
Retrieve the data used for formula calculation data via an HTTP GET command. Payload is in JSON format.
|
||||||
|
|
||||||
* ``a1``-``a10`` are the angles/tilt readings (up to 10 are currently supported)
|
* ``a1``-``a10`` are the angles/tilt readings (up to 10 are currently supported)
|
||||||
* ``g1``-``g10`` are the corresponding gravity reaadings in SG or Plato depending on the device-format.
|
* ``g1``-``g10`` are the corresponding gravity readings in SG or Plato depending on the device-format.
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
@ -156,18 +161,19 @@ Used for adjusting some internal constants and other advanced settings. Should b
|
|||||||
|
|
||||||
{
|
{
|
||||||
"gyro-read-count": 50,
|
"gyro-read-count": 50,
|
||||||
"tempsensor-resolution": 9,
|
|
||||||
"gyro-moving-threashold": 500,
|
"gyro-moving-threashold": 500,
|
||||||
"formula-max-deviation": 1.6,
|
"formula-max-deviation": 3.0,
|
||||||
"wifi-portal-timeout": 120,
|
"wifi-portal-timeout": 120,
|
||||||
"wifi-connect-timeout": 20,
|
"wifi-connect-timeout": 20,
|
||||||
|
"push-timeout": 10,
|
||||||
"formula-calibration-temp": 20,
|
"formula-calibration-temp": 20,
|
||||||
"ignore-low-angles": false,
|
|
||||||
"int-http1": 0,
|
"int-http1": 0,
|
||||||
"int-http2": 0,
|
"int-http2": 0,
|
||||||
"int-http3": 0,
|
"int-http3": 0,
|
||||||
"int-influx": 0,
|
"int-influx": 0,
|
||||||
"int-mqtt": 0
|
"int-mqtt": 0,
|
||||||
|
"tempsensor-resolution": 9,
|
||||||
|
"ignore-low-angles": false
|
||||||
}
|
}
|
||||||
|
|
||||||
POST: /api/config/advanced
|
POST: /api/config/advanced
|
||||||
@ -233,7 +239,7 @@ Used to update device settings via an HTTP POST command.
|
|||||||
|
|
||||||
Payload should be in standard format used for posting a form. Such as as: `id=value&mdns=value` etc. Key value pairs are shown below.
|
Payload should be in standard format used for posting a form. Such as as: `id=value&mdns=value` etc. Key value pairs are shown below.
|
||||||
|
|
||||||
* ``temp-format`` can be either ``C`` (Celcius) or ``F`` (Farenheight)
|
* ``temp-format`` can be either ``C`` (Celsius) or ``F`` (Fahrenheit)
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
@ -321,7 +327,7 @@ POST: /api/config/formula
|
|||||||
Used to update formula calculation data via an HTTP POST command. Payload is in JSON format.
|
Used to update formula calculation data via an HTTP POST command. Payload is in JSON format.
|
||||||
|
|
||||||
* ``a1``-``a10`` are the angles/tilt readings (up to 10 are currently supported)
|
* ``a1``-``a10`` are the angles/tilt readings (up to 10 are currently supported)
|
||||||
* ``g1``-``g10`` are the corresponding gravity reaadings (in SG)
|
* ``g1``-``g10`` are the corresponding gravity readings (in SG)
|
||||||
|
|
||||||
Payload should be in standard format used for posting a form. Such as as: `id=value&mdns=value` etc. Key value pairs are shown below.
|
Payload should be in standard format used for posting a form. Such as as: `id=value&mdns=value` etc. Key value pairs are shown below.
|
||||||
|
|
||||||
@ -354,7 +360,7 @@ Calling the API's from Python
|
|||||||
=============================
|
=============================
|
||||||
|
|
||||||
Here is some example code for how to access the API's from a python script. Keys should always be
|
Here is some example code for how to access the API's from a python script. Keys should always be
|
||||||
present or the API call will fail.
|
present or the API call will fail. You only need to include the parameters you want to change.
|
||||||
|
|
||||||
The requests package converts the json to standard form post format.
|
The requests package converts the json to standard form post format.
|
||||||
|
|
||||||
@ -363,7 +369,7 @@ The requests package converts the json to standard form post format.
|
|||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
|
|
||||||
host = "192.168.1.1" # IP adress (or name) of the device to send these settings to
|
host = "192.168.1.1" # IP address (or name) of the device to send these settings to
|
||||||
id = "ee1bfc" # Device ID (shown in serial console during startup or in UI)
|
id = "ee1bfc" # Device ID (shown in serial console during startup or in UI)
|
||||||
|
|
||||||
def set_config( url, json ):
|
def set_config( url, json ):
|
||||||
@ -419,7 +425,7 @@ The requests package converts the json to standard form post format.
|
|||||||
"temp-adjustment": 0, # If temp sensor needs to be corrected
|
"temp-adjustment": 0, # If temp sensor needs to be corrected
|
||||||
"gyro-temp": "on", # Use the temp sensor in the gyro instead (on/off)
|
"gyro-temp": "on", # Use the temp sensor in the gyro instead (on/off)
|
||||||
"ble": "red", # Enable ble on esp32
|
"ble": "red", # Enable ble on esp32
|
||||||
"ota-url": "" # if the device should seach for a new update when active
|
"ota-url": "" # if the device should search for a new update when active
|
||||||
}
|
}
|
||||||
set_config( url, json )
|
set_config( url, json )
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ copyright = '2021-2022, Magnus Persson'
|
|||||||
author = 'Magnus Persson'
|
author = 'Magnus Persson'
|
||||||
|
|
||||||
# The full version, including alpha/beta/rc tags
|
# The full version, including alpha/beta/rc tags
|
||||||
release = '1.0.0'
|
release = '1.1.0'
|
||||||
|
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
# -- General configuration ---------------------------------------------------
|
||||||
|
@ -23,22 +23,23 @@ URL: (http://gravmon.local)
|
|||||||
:alt: Index page
|
:alt: Index page
|
||||||
|
|
||||||
|
|
||||||
Configuration is accessed by entering the URL for the device, this will be the mDNS name *device.local* or the IP adress. The following chapter assumes the device name is *gravmon*.
|
Configuration is accessed by entering the URL for the device, this will be the mDNS name *device.local* or the IP address. The following chapter assumes the device name is *gravmon*.
|
||||||
|
|
||||||
The main page shows the device readings; gravity, angle, temperature and battery charge. If the checkbox is active then the device will never go into sleep mode. This is useful if
|
The main page shows the device readings; gravity, angle, temperature and battery charge. If the checkbox is active then the device will never go into sleep mode. This is useful if
|
||||||
you are collecting angle/tilt for calibration. If this is unchecked the device will change mode as explained before.
|
you are collecting angle/tilt for calibration. If this is unchecked the device will change mode as explained before.
|
||||||
|
|
||||||
You can also view the average time a gravity measurement takes. Under optimal setting this should be around 1.5 - 2.0 seconds. If this is higher than 2 seconds this is most likley connected to slow wifi
|
You can also view the average time a gravity measurement takes. Under optimal setting this should be around 1.5 - 2.0 seconds. If this is higher than 2 seconds this is most likely connected to slow wifi
|
||||||
connection. It will show 0 if data has not been collected yet.
|
connection. It will show 0 if data has not been collected yet.
|
||||||
|
|
||||||
.. tip::
|
.. tip::
|
||||||
|
|
||||||
If you are connected to the device via a serial console (speed: 115200) you can see the connection sequence and get the Unique ID and IP adress from there.
|
If you are connected to the device via a serial console (speed: 115200) you can see the connection sequence and get the Unique ID and IP address from there.
|
||||||
|
|
||||||
.. tip::
|
.. tip::
|
||||||
|
|
||||||
The button `view error log` will show the last 15 errors on the device. This can be useful for checking errors without
|
The button `view error log` will show the last error messages on the device. This can be useful for checking errors without
|
||||||
the need to connect to the serial port or to check what errors has occured while in `gravity mode`.
|
the need to connect to the serial port or to check what errors has occurred while in `gravity mode`. From v1.1 it will also detect
|
||||||
|
any abnormal restarts or crashes and record these in the logfile.
|
||||||
|
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
@ -55,11 +56,12 @@ Device Setting
|
|||||||
|
|
||||||
* **Device name:**
|
* **Device name:**
|
||||||
|
|
||||||
This is unique name for the device. It will be used in pushing data as well as mDNS name on the network (<name>.local)
|
This is unique name for the device. It will be used in pushing data as well as mDNS name on the network (<name>.local).
|
||||||
|
The limitation is 63 chars but using long names might break endpoints that data is sent to if they have other limitations.
|
||||||
|
|
||||||
* **Temperature format:**
|
* **Temperature format:**
|
||||||
|
|
||||||
Choose between Celsius and Farenheight when displaying temperature.
|
Choose between Celsius and Fahrenheit when displaying temperature.
|
||||||
|
|
||||||
* **Interval:**
|
* **Interval:**
|
||||||
|
|
||||||
@ -92,7 +94,7 @@ Push Settings
|
|||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
When enabling SSL this will not validate the root CA of the remote service, this is a design decision based on two aspects. Enabling CA validation will take 3-4s extra on each connection which means way less
|
When enabling SSL this will not validate the root CA of the remote service, this is a design decision based on two aspects. Enabling CA validation will take 3-4s extra on each connection which means way less
|
||||||
battery life, so the decision is to prioritize battery life over security. The data transmitted is not really that sensitive anyway so I belive this is a good balance.
|
battery life, so the decision is to prioritize battery life over security. The data transmitted is not really that sensitive anyway so I believe this is a good balance.
|
||||||
|
|
||||||
* **HTTP 1 (POST):**
|
* **HTTP 1 (POST):**
|
||||||
|
|
||||||
@ -127,7 +129,7 @@ The token is included in the default format for the HTTP GET url but can be used
|
|||||||
:width: 300
|
:width: 300
|
||||||
:alt: HTTP Headers
|
:alt: HTTP Headers
|
||||||
|
|
||||||
You can define 2 http headers per push target. This is available via a pop-up window but dont forget
|
You can define 2 http headers per push target. This is available via a pop-up window but don't forget
|
||||||
to press the save buttons on the post section to save the values. One common header is content type which is the
|
to press the save buttons on the post section to save the values. One common header is content type which is the
|
||||||
default setting for http targets.
|
default setting for http targets.
|
||||||
|
|
||||||
@ -195,14 +197,14 @@ Gravity Settings
|
|||||||
|
|
||||||
* **Gravity format:**
|
* **Gravity format:**
|
||||||
|
|
||||||
Gravity format can be eihter `SG` or `Plato`. The device will use SG Internally and convert to Plato when displaying or sending data.
|
Gravity format can be either `SG` or `Plato`. The device will use SG Internally and convert to Plato when displaying or sending data.
|
||||||
|
|
||||||
* **Gravity formula:**
|
* **Gravity formula:**
|
||||||
|
|
||||||
Gravity formula is compatible with standard iSpindle formulas so any existing calculation option can be used. You can also use
|
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`
|
the feature to create the formula by supplying the raw data. See :ref:`create-formula`
|
||||||
|
|
||||||
The gravity formula accepts to paramaters, **tilt** for the angle or **temp** for temperature (temperature inserted into the formula
|
The gravity formula accepts to parameters, **tilt** for the angle or **temp** for temperature (temperature inserted into the formula
|
||||||
will be in celsius). I would recommend to use the formula calculation feature instead since this is much easier.
|
will be in celsius). I would recommend to use the formula calculation feature instead since this is much easier.
|
||||||
|
|
||||||
* **Temperature correct gravity:**
|
* **Temperature correct gravity:**
|
||||||
@ -214,7 +216,7 @@ build this into the gravity formula.
|
|||||||
|
|
||||||
This formula assumes that the calibration has been done at 20°C / 68°F.
|
This formula assumes that the calibration has been done at 20°C / 68°F.
|
||||||
|
|
||||||
Formula used in temperature correction.
|
Formula used in temperature correction. The calibration temperature can be changed under advanced settings.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@ -231,7 +233,13 @@ Hardware Settings
|
|||||||
|
|
||||||
* **Voltage factor:**
|
* **Voltage factor:**
|
||||||
|
|
||||||
Factor used to calcualate the battery voltage. If you get a too low/high voltage you can adjust this value.
|
Factor used to calculate the battery voltage. If you get a too low/high voltage you can adjust this value.
|
||||||
|
|
||||||
|
* **Config voltage:**
|
||||||
|
|
||||||
|
Defines the level of voltage when the device should enter config mode due to charging. This might vary between different battery manufacturers.
|
||||||
|
If you don't what the device to go into configuration mode when charging, set this to 6V. This was added since different batteries have different
|
||||||
|
voltages when fully charged.
|
||||||
|
|
||||||
* **Temperature correction:**
|
* **Temperature correction:**
|
||||||
|
|
||||||
@ -242,9 +250,16 @@ when the device starts. So changing this will not take affect until the device i
|
|||||||
|
|
||||||
Enable this feature will use the temp sensor i the gyro instead of the DS18B20, the benefit is shorter run time and
|
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
|
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
|
device is activated, since the gyro should be cool this is reflecting the surrounding temperature. After it has
|
||||||
been running the value would be totally off.
|
been running the value would be totally off.
|
||||||
|
|
||||||
|
* **Enable storage mode when placed on cap**
|
||||||
|
|
||||||
|
When place on the cap (<5 degree tilt) the device will go into deep sleep forever (until reset). In order to wake it
|
||||||
|
up you need to do a reset. One option is to attach a magnetic reed switch (default open) to the reset pin and use a
|
||||||
|
magnet to force a reset without opening the tube. The reed switch is typically an electronic component of 14 mm
|
||||||
|
long encapsulated in a small glass tube. See hardware section for more information, :ref:`hardware`.
|
||||||
|
|
||||||
* **Bluetooth: (Only ESP32)**
|
* **Bluetooth: (Only ESP32)**
|
||||||
|
|
||||||
If the build is using an ESP32 then you can send data over BLE, simulating a Tilt device. Choose the color that you want the device to simulate.
|
If the build is using an ESP32 then you can send data over BLE, simulating a Tilt device. Choose the color that you want the device to simulate.
|
||||||
@ -275,7 +290,7 @@ This option gives you the possibility to install an new version of the firmware
|
|||||||
:alt: Update firmware
|
:alt: Update firmware
|
||||||
|
|
||||||
|
|
||||||
Advanded Settings
|
Advanced Settings
|
||||||
+++++++++++++++++
|
+++++++++++++++++
|
||||||
|
|
||||||
.. image:: images/config5.png
|
.. image:: images/config5.png
|
||||||
@ -284,27 +299,31 @@ Advanded Settings
|
|||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
Changeing these parameters with caution. The wrong values might cause the device to become unresponsive.
|
Change these parameters with caution. The wrong values might cause the device to become unresponsive.
|
||||||
|
|
||||||
|
|
||||||
* **Gyro reads:**
|
* **Gyro reads:**
|
||||||
|
|
||||||
This defines how many gyro reads will be done before an angle is calculated. More reads will give better accuracy and also allow detection of
|
This defines how many gyro reads will be done before an angle is calculated. More reads will give better accuracy and also allow detection of
|
||||||
movement. Too many reads will take time and affecte batterylife. 50 takes about 800 ms to execute.
|
movement. Too many reads will take time and affect battery life. 50 takes about 800 ms to execute.
|
||||||
|
|
||||||
* **Gyro moving threashold:**
|
* **Gyro moving threshold:**
|
||||||
|
|
||||||
This is the max amount of deviation allowed for a stable reading.
|
This is the max amount of deviation allowed for a stable reading.
|
||||||
|
|
||||||
* **Formula deviation:**
|
* **Formula deviation:**
|
||||||
|
|
||||||
This is the maximum devation on the formlula allowed for it to be accepted. Once the formula has been derived it will be validated against the supplied
|
This is the maximum deviation on the formula allowed for it to be accepted. Once the formula has been derived it will be validated against the supplied
|
||||||
data and of the deviation on any point is bigger the formula will be rejected.
|
data and of the deviation on any point is bigger the formula will be rejected.
|
||||||
|
|
||||||
* **Ignore angles below water:**
|
* **Ignore angles below water:**
|
||||||
|
|
||||||
If this option is checked any angles below that of SG 1 will be discarded as invalid and never sent to any server. Default = off.
|
If this option is checked any angles below that of SG 1 will be discarded as invalid and never sent to any server. Default = off.
|
||||||
|
|
||||||
|
* **Gravity calibration temp**
|
||||||
|
|
||||||
|
This option allows you to set the correction temperature used in the automatic temperature gravity adjustment formula. Standard is 20C.
|
||||||
|
|
||||||
* **DS18B20 Resolution:**
|
* **DS18B20 Resolution:**
|
||||||
|
|
||||||
Define the resolution used on the temp sensor. 9 bits is default and will give an accuracy of 0.5C, 12 bits will give an accuracy of 0.0625C but will also
|
Define the resolution used on the temp sensor. 9 bits is default and will give an accuracy of 0.5C, 12 bits will give an accuracy of 0.0625C but will also
|
||||||
@ -316,11 +335,11 @@ This is the amount of time allowed for a wifi connect.
|
|||||||
|
|
||||||
* **Wifi portal timeout:**
|
* **Wifi portal timeout:**
|
||||||
|
|
||||||
If the wifi portal is triggered (can be triggerd by reset) then this is the amount of time allowed before it exists again.
|
If the wifi portal is triggered (can be triggered by reset) then this is the amount of time allowed before it exists again.
|
||||||
|
|
||||||
* **Skip Interval (...):**
|
* **Skip Interval (...):**
|
||||||
|
|
||||||
These options allow the user to have variable push intervals for the diffrent endpoints. 0 means that every wakeup will send data to that endpoint. If you enter another number then that defines how many sleep cycles will be skipped.
|
These options allow the user to have variable push intervals for the different endpoints. 0 means that every wakeup will send data to that endpoint. If you enter another number then that defines how many sleep cycles will be skipped.
|
||||||
|
|
||||||
If the sleep interval is 300s and MQTT is set to 0 and HTTP1 is set to 2 then MQTT will be sent every 300s while HTTP1 would be sent 900s. This is great if you want to send data to a local mqtt server often but brewfather will only
|
If the sleep interval is 300s and MQTT is set to 0 and HTTP1 is set to 2 then MQTT will be sent every 300s while HTTP1 would be sent 900s. This is great if you want to send data to a local mqtt server often but brewfather will only
|
||||||
accept data every 15 min.
|
accept data every 15 min.
|
||||||
|
@ -7,11 +7,11 @@ In order to keep the source code in good condition I use `pre-commit <https://pr
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
If you are using Windows as a base platform I would suggest that you install pre-commit under wsl (Windows Subssytem for Windows) and run it from there, I have found
|
If you are using Windows as a base platform I would suggest that you install pre-commit under wsl (Windows Subsystem for Windows) and run it from there, I have found
|
||||||
that this approach works fine.
|
that this approach works fine.
|
||||||
|
|
||||||
|
|
||||||
The following command will run pre-commit on all the source files. Assuming you are inte project directory.
|
The following command will run pre-commit on all the source files. Assuming you are in the project directory.
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ This is the format template used to create the json above.
|
|||||||
{
|
{
|
||||||
"name" : "${mdns}",
|
"name" : "${mdns}",
|
||||||
"ID": "${id}",
|
"ID": "${id}",
|
||||||
"token" : "gravmon",
|
"token" : "${token}",
|
||||||
"interval": ${sleep-interval},
|
"interval": ${sleep-interval},
|
||||||
"temperature": ${temp},
|
"temperature": ${temp},
|
||||||
"temp_units": "${temp-unit}",
|
"temp_units": "${temp-unit}",
|
||||||
@ -129,34 +129,18 @@ This is the format template used to create the json above.
|
|||||||
ispindel/${mdns}/interval:${sleep-interval}|
|
ispindel/${mdns}/interval:${sleep-interval}|
|
||||||
ispindel/${mdns}/RSSI:${rssi}|
|
ispindel/${mdns}/RSSI:${rssi}|
|
||||||
|
|
||||||
This is a format template that is compatible with v0.6. Just replace the `topic` with the topic you want to post data to.
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
topic:{"name":"gravmon", "ID":"${id}", "token":"gravmon", "interval": ${sleep-interval},
|
|
||||||
"temperature": ${temp}, "temp_units": "${temp-unit}", "gravity":${gravity},
|
|
||||||
"angle": ${angle}, "battery":${battery}, "rssi": ${rssi}, "corr-gravity":${corr-gravity},
|
|
||||||
"gravity-unit": "${gravity-unit}", "run-time": ${run-time}}|
|
|
||||||
|
|
||||||
|
|
||||||
version.json
|
version.json
|
||||||
============
|
============
|
||||||
|
|
||||||
Contents version.json. The version is used by the device to check if the this version is newer. The html files will also be downloaded if the are present on the server. This way it's easy to
|
Contents version.json. The version is used by the device to check if the this version is newer. The html files will also be downloaded if the are present on the server. This way it's easy to
|
||||||
upgrade to a version that serve the html files from the file system. If they dont exist nothing will happen, the OTA flashing will still work. If the html files are missing from the file system
|
upgrade to a version that serve the html files from the file system. If they don't exist nothing will happen, the OTA flashing will still work. If the html files are missing from the file system
|
||||||
they can be uploaded manually afterwards.
|
they can be uploaded manually afterwards.
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
{
|
{
|
||||||
"project":"gravmon",
|
"project":"gravmon",
|
||||||
"version":"0.7.0",
|
"version":"1.0.0",
|
||||||
"html": [
|
"html": [ ]
|
||||||
"index.min.htm",
|
|
||||||
"test.min.htm",
|
|
||||||
"config.min.htm",
|
|
||||||
"format.min.htm",
|
|
||||||
"calibration.min.htm",
|
|
||||||
"about.min.htm"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,31 @@ Create formula
|
|||||||
|
|
||||||
Here you can enter up to 10 values (angles + gravity) that is then used to create the formula. Angles equal to zero will be regarded as empty even if there is a gravity reading.
|
Here you can enter up to 10 values (angles + gravity) that is then used to create the formula. Angles equal to zero will be regarded as empty even if there is a gravity reading.
|
||||||
|
|
||||||
|
When you submit the values the device will try create a formula with increasing level of complexity. It will start
|
||||||
|
with a order 2 formula and then try 3 and 4.
|
||||||
|
|
||||||
|
Once the formula has been created it will validate the formula against the supplied angles/gravity and if there is a too
|
||||||
|
high difference, it will fail. You can adjust the sensitivity under advanced settings if you have issues.
|
||||||
|
|
||||||
|
Under the Error Log you will also find hints to what problem the formula creator encountered. Here is an example:
|
||||||
|
|
||||||
|
`CALC: Validation failed on angle 33.430000, deviation too large 5.86, formula order 4`
|
||||||
|
|
||||||
|
`CALC: Validation failed on angle 33.430000, deviation too large 3.14, formula order 2`
|
||||||
|
|
||||||
|
This means that the angle 33.43 had a deviation of 5.8 SG and since the default threshold is 3, it will fail. You
|
||||||
|
can also see that it has failed on that point in both a order 2 and 4 formula.
|
||||||
|
|
||||||
|
.. image:: images/qa_1.png
|
||||||
|
:width: 400
|
||||||
|
:alt: Example of deviating value
|
||||||
|
|
||||||
|
So in this case you can either increase the threshold or remove the angle that has an issue. You can also
|
||||||
|
use the graph on the calibration page to identify angles that is probably not correct.
|
||||||
|
|
||||||
.. image:: images/formula2.png
|
.. image:: images/formula2.png
|
||||||
:width: 800
|
:width: 800
|
||||||
:alt: Formula graph
|
:alt: Formula graph
|
||||||
|
|
||||||
Once the formula is created a graph over the entered values and a simulation of the formula will give you a nice overview on how the formula will work.
|
Once the formula is created a graph over the entered values and a simulation of the formula will give you a nice overview on how the formula will work.
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.. _functionallity:
|
.. _functionality:
|
||||||
|
|
||||||
Functionallity
|
Functionality
|
||||||
==============
|
==============
|
||||||
|
|
||||||
The main features
|
The main features
|
||||||
@ -68,7 +68,7 @@ The main features
|
|||||||
Currently the device can handle 10 data points which should be enough to get a accurate formula. At least 3 data points
|
Currently the device can handle 10 data points which should be enough to get a accurate formula. At least 3 data points
|
||||||
is needed to get an accurate formula.
|
is needed to get an accurate formula.
|
||||||
|
|
||||||
* **Customize the data format beeing sent to push targets**
|
* **Customize the data format being sent to push targets**
|
||||||
|
|
||||||
In order to make it easier to support more targets there is a built in format editor that can be used to
|
In order to make it easier to support more targets there is a built in format editor that can be used to
|
||||||
customize the data that is to be sent. This way you can easily adapt the software to new targets without coding.
|
customize the data that is to be sent. This way you can easily adapt the software to new targets without coding.
|
||||||
@ -79,7 +79,7 @@ The main features
|
|||||||
* **Automatic temperature adjustment of gravity reading**
|
* **Automatic temperature adjustment of gravity reading**
|
||||||
|
|
||||||
If you want to correct gravity based on beer temperature you can do this in the formula but here is a nice
|
If you want to correct gravity based on beer temperature you can do this in the formula but here is a nice
|
||||||
feature that can correct the gravity as a second step making this independant of the formula.
|
feature that can correct the gravity as a second step making this independent of the formula.
|
||||||
|
|
||||||
* **OTA support from webserver**
|
* **OTA support from webserver**
|
||||||
|
|
||||||
@ -99,8 +99,8 @@ The main features
|
|||||||
|
|
||||||
* **WIFI connection issues**
|
* **WIFI connection issues**
|
||||||
|
|
||||||
The software will not wait indefiently for a wifi connection. If it takes longer than 20 seconds to connect then
|
The software will not wait indefinitely for a wifi connection. If it takes longer than 20 seconds to connect then
|
||||||
the device will try the seconday wifi configuration, and that also failes it will go into deep sleep for 60 seconds and then
|
the device will try the secondary wifi configuration, and that also fails it will go into deep sleep for 60 seconds and then
|
||||||
retry later. This to conserve batter as much as possible.
|
retry later. This to conserve batter as much as possible.
|
||||||
|
|
||||||
* **Use gyro temperature sensor**
|
* **Use gyro temperature sensor**
|
||||||
@ -119,7 +119,7 @@ The main features
|
|||||||
:width: 800
|
:width: 800
|
||||||
:alt: Gyro temp vs DS18B20
|
:alt: Gyro temp vs DS18B20
|
||||||
|
|
||||||
* **Celsius or Farenheigt**
|
* **Celsius or Fahrenheit**
|
||||||
|
|
||||||
You can switch between different temperature formats. GravityMon will always use C for it's internal calculations and
|
You can switch between different temperature formats. GravityMon will always use C for it's internal calculations and
|
||||||
convert to F when displayed.
|
convert to F when displayed.
|
||||||
@ -131,15 +131,15 @@ The main features
|
|||||||
|
|
||||||
* **Stable gyro data**
|
* **Stable gyro data**
|
||||||
|
|
||||||
The device will read the gyro 50 times to get an accurate reading. If the standad deviation is to high it will not
|
The device will read the gyro 50 times to get an accurate reading. If the standard deviation is to high it will not
|
||||||
use the data since this is inacurate and the device is probably moving, probably do to active fermentation or movement of
|
use the data since this is inaccurate and the device is probably moving, probably do to active fermentation or movement of
|
||||||
fermentation vessel. This sequence takes 900 ms seconds to execute and besides wifi connection this is what consumes the most
|
fermentation vessel. This sequence takes 900 ms seconds to execute and besides wifi connection this is what consumes the most
|
||||||
battery. With more testing this might be changes to either speed up or provide more stable readings.
|
battery. With more testing this might be changes to either speed up or provide more stable readings.
|
||||||
|
|
||||||
* **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
|
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. This is a lot of help trying to figure out where bottlenecks
|
way I can find a good balance between performance and quality. This is a lot of help trying to figure out where bottlenecks
|
||||||
are in the code and where to put optimization efforts. Examples of real measurements:
|
are in the code and where to put optimization efforts. Examples of real measurements:
|
||||||
|
|
||||||
* Reading the gyro: 885 ms
|
* Reading the gyro: 885 ms
|
||||||
@ -160,7 +160,7 @@ The main features
|
|||||||
Battery life
|
Battery life
|
||||||
------------
|
------------
|
||||||
|
|
||||||
The long term battery test has now been completed. Using a 2200 mA battery and sending data every 5 minutes to a local server on my network. The battery lasted 47 days which is excellet battery life.
|
The long term battery test has now been completed. Using a 2200 mA battery and sending data every 5 minutes to a local server on my network. The battery lasted 47 days which is excellent battery life.
|
||||||
|
|
||||||
In another test I had a device running with an sleep interval of only 30s with ok wifi connection. The device lasted 12 days which i think is excellent considering the short sleep interval.
|
In another test I had a device running with an sleep interval of only 30s with ok wifi connection. The device lasted 12 days which i think is excellent considering the short sleep interval.
|
||||||
|
|
||||||
@ -173,7 +173,7 @@ Performance
|
|||||||
Since I have the possibility to measure the performance of different function in the code this is what I have been able to gather.
|
Since I have the possibility to measure the performance of different function in the code this is what I have been able to gather.
|
||||||
|
|
||||||
The typical runtime in a measurement cycle is approx 2 seconds and in some cases it can take up to 6-8 seconds but this is mainly related to establishing the WIFI connection. So stable wifi is
|
The typical runtime in a measurement cycle is approx 2 seconds and in some cases it can take up to 6-8 seconds but this is mainly related to establishing the WIFI connection. So stable wifi is
|
||||||
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
|
essential for long battery life. 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 measuring
|
||||||
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.
|
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 orange is the total runtime for the awake period.
|
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.
|
@ -3,7 +3,7 @@
|
|||||||
Hardware
|
Hardware
|
||||||
########
|
########
|
||||||
|
|
||||||
There are lots of resouces out there on how to build the hardware for an iSpindle so I will not go into details on that part. Here are two of my builds using the iSpindle PCB v4.
|
There are lots of resources out there on how to build the hardware for an iSpindle so I will not go into details on that part. Here are two of my builds using the iSpindle PCB v4.
|
||||||
|
|
||||||
.. image:: images/ispindel.jpg
|
.. image:: images/ispindel.jpg
|
||||||
:width: 500
|
:width: 500
|
||||||
@ -43,3 +43,24 @@ Schema for esp32 build
|
|||||||
.. image:: images/schema_esp32.png
|
.. image:: images/schema_esp32.png
|
||||||
:width: 700
|
:width: 700
|
||||||
:alt: Schema esp32
|
:alt: Schema esp32
|
||||||
|
|
||||||
|
Modifying with reed switch
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
A reed switch is a switch that reacts to magnetic fields. The ones I have tested are normally open and close in proximity to
|
||||||
|
a magnet.
|
||||||
|
|
||||||
|
.. image:: images/reed.jpg
|
||||||
|
:width: 400
|
||||||
|
:alt: Reed switch
|
||||||
|
|
||||||
|
If this is connected to the reset button a magnet can be used to trigger a reset of the device. The image below shows how
|
||||||
|
I mounted the iSPINDLE PCB v4.0 just under the cap. The lower red circle shows the reset connection point for the reed switch.
|
||||||
|
|
||||||
|
The reed switch is the glass tube visible under the esp8266.
|
||||||
|
|
||||||
|
.. image:: images/reed_build.jpg
|
||||||
|
:width: 400
|
||||||
|
:alt: Reed build
|
||||||
|
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 36 KiB |
BIN
src_docs/source/images/qa_1.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
src_docs/source/images/reed.jpg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
src_docs/source/images/reed_build.jpg
Normal file
After Width: | Height: | Size: 574 KiB |
@ -7,66 +7,90 @@ Welcome to GravityMon's documentation!
|
|||||||
######################################
|
######################################
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
This documentation reflects **v1.0**. Last updated 2022-05-08
|
This documentation reflects **v1.1**. Last updated 2022-08-14
|
||||||
|
|
||||||
* Docs for: `v0.8 <https://mp-se.github.io/gravitymon/v0.8/index.html>`_
|
User interface overview
|
||||||
* Docs for: `v0.9 <https://mp-se.github.io/gravitymon/v0.9/index.html>`_
|
-----------------------
|
||||||
|
|
||||||
|
This animation shows how the user interface is structured, it reflects an older version but the structure is the same.
|
||||||
|
|
||||||
.. image:: images/gravitymon.gif
|
.. image:: images/gravitymon.gif
|
||||||
:width: 800
|
:width: 800
|
||||||
:alt: User Inteface Walkthrough
|
:alt: User Inteface Walkthrough
|
||||||
|
|
||||||
|
.. _main_features:
|
||||||
|
|
||||||
|
Main features
|
||||||
|
-------------
|
||||||
|
|
||||||
|
* Operates in two modes gravity monitoring and configuration mode
|
||||||
|
* Gravity mode is comparable to how the iSpindle works when collecting data
|
||||||
|
* Configuration mode has a modern HTML5 based web UI. No need to start the access point to change settings
|
||||||
|
* Offloading some of the functionality to run in the web browser, this allows for more advanced features.
|
||||||
|
* REST API to enable scripted configuration
|
||||||
|
* Send data to multiple endpoints and services at once (2xHTTP POST, HTTP GET, MQTT, INFLUXDB2)
|
||||||
|
* Directly test all endpoints from user interface with feedback to simplify troubleshooting
|
||||||
|
* Complete format customization for all endpoints using templates (don't really need to change the software to support new services)
|
||||||
|
* Setup guides for how to send data to many popular services. Currently 10+ are documented
|
||||||
|
* Automatic temperature adjustment of gravity (just tick a checkbox)
|
||||||
|
* OTA support from webserver
|
||||||
|
* Firmware update via web interface
|
||||||
|
* Built in function to create gravity formulas, no need for additional software, just enter tilt/gravity and let GravityMon creates a formula
|
||||||
|
* Visual graph showing how formula will be interpreted based on entered values
|
||||||
|
* Using the temperature sensor in gyro instead of DS18B20 (faster)
|
||||||
|
* SSL support in all endpoints (no certificate validation due to limitations on esp8266).
|
||||||
|
* Built in performance measurements (used to optimize code)
|
||||||
|
* Storage mode when placed on cap (indefinite sleep)
|
||||||
|
* Customize various hardware parameters to optimize device functionality.
|
||||||
|
|
||||||
|
For a complete breakdown see the :ref:`functionality`
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
If you are missing some feature, please reach out on `Github <https://github.com/mp-se/gravitymon/discussions>`_ or `homebrewtalk.com <https://www.homebrewtalk.com/threads/replacement-firmware-for-ispindel-gravitymon.698058/>`_
|
||||||
|
|
||||||
|
What is GravityMon?
|
||||||
|
--------------------
|
||||||
|
|
||||||
GravityMon is used to measure gravity and temperature during fermentation of beer and report the progress. The graph below is
|
GravityMon is used to measure gravity and temperature during fermentation of beer and report the progress. The graph below is
|
||||||
an example on how the fermentation process can be tracked. This is from my last brew that was over on a few days. The graph is rendered using
|
an example on how the fermentation process can be tracked. The graph has been rendered using Fermentrack.
|
||||||
Fermentrack.
|
|
||||||
|
|
||||||
.. image:: images/fermentation.png
|
.. image:: images/fermentation.png
|
||||||
:width: 500
|
:width: 500
|
||||||
:alt: Example fermentation
|
:alt: Example fermentation
|
||||||
|
|
||||||
GravityMon is a replacement firmware for the iSpindle and uses the same hardware configuration and is 100% compatible. It
|
GravityMon is a replacement firmware for the iSpindle and uses the same hardware configuration and is 100% compatible. It
|
||||||
implements a lot of the features that has been requested in the orginal iSpindle project but has been rejected for
|
implements a lot of the features that has been requested in the original iSpindle project but never implemented for
|
||||||
various reasons. Here is a list of :ref:`main_features`.
|
various reasons. Here is a list of :ref:`main_features`.
|
||||||
|
|
||||||
From v0.9 the firmware now supports a iSpindle build based on an ESP32 d1 mini (pin compatible with esp8266). See :ref:`hardware`.
|
From v0.9 the firmware also supports a iSpindle built using an ESP32 d1 mini (pin compatible with esp8266). Currently this is an experimental
|
||||||
|
version but since it has more memory and processing capacity it could support more functions. See :ref:`hardware`.
|
||||||
|
|
||||||
I started GravityMon because I like to create software and wanted to do some low level programming. I had done a few
|
I started GravityMon because I like to create software and wanted to do some microcontroller programming. I had done a few
|
||||||
projects based on esp8266 and also started to brew beer so this combination was quite natural.
|
projects based on esp8266 and also started to brew beer so this combination was quite natural.
|
||||||
|
|
||||||
The hardware design comes from the fantastic iSpindle project so that is not covered in this documentation. For more
|
The hardware design comes from the fantastic iSpindle project so that is not covered in this documentation. For more
|
||||||
information on this topic and function please visit `iSpindel Homepage <https://www.ispindel.de>`_ .
|
information on this topic and function please visit `iSpindel Homepage <https://www.ispindel.de>`_ .
|
||||||
|
|
||||||
My approach to this software is a little different from that the original iSpindle firmware. The github repository
|
My approach to this software is a little different from that the original iSpindle firmware as can be seen in the list of features.
|
||||||
can be found here; `GravityMon on Github <https://github.com/mp-se/gravitymon>`_
|
|
||||||
|
The github repository can be found here; `GravityMon on Github <https://github.com/mp-se/gravitymon>`_
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
I dont take responsibility for any errors or issues caused by the software. The software is provided as-is. I will however
|
I don't take responsibility for any errors or issues caused by the software. The software is provided as-is. I will however
|
||||||
try my best to fix issues that might occur.
|
try my best to fix issues that might occur.
|
||||||
|
|
||||||
I have tested this software over the last year on 20+ brews with good results.
|
I have tested this software on 40+ brews with good results.
|
||||||
|
|
||||||
.. _main_features:
|
|
||||||
|
|
||||||
Main features:
|
Documentation for older versions
|
||||||
--------------
|
--------------------------------
|
||||||
|
|
||||||
* Operates in two modes gravity monitoring and configuration mode (simplify calibration). Gravity mode
|
* Docs for: `v1.0 <https://mp-se.github.io/gravitymon/v1.0/index.html>`_
|
||||||
is comparable to how the iSpindle works.
|
* Docs for: `v0.9 <https://mp-se.github.io/gravitymon/v0.9/index.html>`_
|
||||||
* Modern web based UI when in configuration mode. No need to start the access point changing settings.
|
|
||||||
* REST API to enable scripted configuration
|
|
||||||
* Send data to multiple endpoints and services at once
|
|
||||||
* Setup guides for how to send data to many popular services. Currently 8+ are documented.
|
|
||||||
* Automatic temperature adjustment of gravity reading
|
|
||||||
* OTA support from webserver
|
|
||||||
* Built in function to create gravity formulas, no need for additional software, just enter tilt/gravity and
|
|
||||||
let GravityMon create the formula.
|
|
||||||
* Visual graph showing how formula will be interpreted based on entered values
|
|
||||||
* Using the temperature sensor in gyro instead of DS18B20 (faster)
|
|
||||||
* SSL support in standard HTTP and MQTT connections.
|
|
||||||
* Option to customize data posted to endpoints using template from the UI.
|
|
||||||
* Built in performance measurements (used to optimise code)
|
|
||||||
|
|
||||||
For a complete breakdown see the :ref:`functionallity`
|
|
||||||
|
Software architecture
|
||||||
|
---------------------
|
||||||
|
|
||||||
This is a simple overview of the different components that the software contains. The green ones are only active during `configuration mode` in
|
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.
|
order to save battery.
|
||||||
@ -78,8 +102,8 @@ order to save battery.
|
|||||||
|
|
||||||
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 discussions 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 achieve:
|
||||||
|
|
||||||
* https://github.com/jrowberg/i2cdevlib
|
* https://github.com/jrowberg/i2cdevlib
|
||||||
|
|
||||||
@ -87,7 +111,7 @@ the following libraries and without these this would have been much more difficu
|
|||||||
|
|
||||||
* https://github.com/codeplea/tinyexpr
|
* https://github.com/codeplea/tinyexpr
|
||||||
|
|
||||||
Proccess the gravity formula and calculate the gravity and various corrections.
|
Process the gravity formula and calculate the gravity and various corrections.
|
||||||
|
|
||||||
* https://github.com/graphitemaster/incbin
|
* https://github.com/graphitemaster/incbin
|
||||||
|
|
||||||
@ -123,7 +147,7 @@ the following libraries and without these this would have been much more difficu
|
|||||||
|
|
||||||
* https://github.com/256dpi/arduino-mqtt
|
* https://github.com/256dpi/arduino-mqtt
|
||||||
|
|
||||||
Library for sending data to mqtt based on lightweight mqtt implemenentation.
|
Library for sending data to mqtt based on lightweight mqtt implementation.
|
||||||
|
|
||||||
* https://graphjs.com/
|
* https://graphjs.com/
|
||||||
|
|
||||||
@ -151,7 +175,7 @@ the following libraries and without these this would have been much more difficu
|
|||||||
|
|
||||||
intro
|
intro
|
||||||
releases
|
releases
|
||||||
functionallity
|
functionality
|
||||||
installation
|
installation
|
||||||
configuration
|
configuration
|
||||||
troubleshooting
|
troubleshooting
|
||||||
|
@ -12,7 +12,7 @@ You have these 3 options for flashing this firmware.
|
|||||||
Brewflasher
|
Brewflasher
|
||||||
===========
|
===========
|
||||||
|
|
||||||
The prefered option for flashing GravityMon is using BrewFlasher, its a tools that support many brewing related firmwares for ESP8266 and ESP32. This works
|
The preferred 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/>`_ there is also a web based version
|
on both Windows and Mac. You can download the latest version from here: `Brewflasher <https://www.brewflasher.com/>`_ there is also a web based version
|
||||||
available here `Brewflasher WEB <https://web.brewflasher.com/>`_.
|
available here `Brewflasher WEB <https://web.brewflasher.com/>`_.
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ In the /bin directory you will find 3 different firmware builds;
|
|||||||
|
|
||||||
* **firmware.bin**
|
* **firmware.bin**
|
||||||
|
|
||||||
This is the standard release build (prefered version)
|
This is the standard release build (preferred version)
|
||||||
|
|
||||||
* **firmware-perf.bin**
|
* **firmware-perf.bin**
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ iSpindel
|
|||||||
========
|
========
|
||||||
|
|
||||||
If you already have the device flashed with iSpindel firmware you can go into the configuration mode where you will find
|
If you already have the device flashed with iSpindel firmware you can go into the configuration mode where you will find
|
||||||
an option for updating firmware. The option is under the maintence meny.
|
an option for updating firmware. The option is under the maintenance menu.
|
||||||
|
|
||||||
Select the esp8266 version of the firmware called firmware.bin and press upload.
|
Select the esp8266 version of the firmware called firmware.bin and press upload.
|
||||||
|
|
||||||
@ -113,17 +113,22 @@ 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
|
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 already exist.
|
the device its possible that wifi settings already exist.
|
||||||
|
|
||||||
If this is not configured in the device it will create an wirless access point called `GravMon`. The default password is `password`.
|
If this is not configured in the device it will create an wireless 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
|
Connect to this AP and enter the SSID and password you want to use. If the web page don't open automatically you can enter the following address
|
||||||
in the browser: **http://192.168.4.1**
|
in the browser: **http://192.168.4.1**
|
||||||
|
|
||||||
Before pressing save on the network infomration, make a note of the devicename that is shown on the screen, this will be the name that is used
|
Before pressing save on the network information, make a note of the devicename that is shown on the screen, this will be the name that is used
|
||||||
in the next step to access the configuration pages. The link would look like this: **http://gravitymon56EA34.local**
|
in the next step to access the configuration pages. The link would look like this: **http://gravitymon56EA34.local**
|
||||||
|
|
||||||
Under wifi settings you can define a primary and seconday wifi SSID. The seconday will be used in case the primary fails. If the seconday is
|
.. note::
|
||||||
|
When selecting a SSID in the list this will be populated in both wifi fields. This is the behaviour of the wifi manager library that I'm using,
|
||||||
|
in the future this is planned to be moved to the normal UI.
|
||||||
|
|
||||||
|
Under wifi settings you can define a primary and secondary wifi SSID. The secondary will be used in case the primary fails. If the secondary is
|
||||||
successful then it will be used as primary. *The second wifi setting is optional and not needed.*
|
successful then it will be used as primary. *The second wifi setting is optional and not needed.*
|
||||||
|
|
||||||
|
|
||||||
.. image:: images/wifi.png
|
.. image:: images/wifi.png
|
||||||
:width: 200
|
:width: 200
|
||||||
:alt: Wifi page
|
:alt: Wifi page
|
||||||
@ -136,10 +141,10 @@ Finding the device adress
|
|||||||
|
|
||||||
Once the wifi network settings have been added then the device will reboot and connect to your network. If the blue led is flashing then it's up and running and is ready to be configured.
|
Once the wifi network settings have been added then the device will reboot and connect to your network. If the blue led is flashing then it's up and running and is ready to be configured.
|
||||||
|
|
||||||
If your computer supports mDNS the adress you saw before can be used in your web browser to connect to the device. Windows does not have the best support for mDNS so if you are having issues
|
If your computer supports mDNS the address you saw before can be used in your web browser to connect to the device. Windows does not have the best support for mDNS so if you are having issues
|
||||||
with finding the network name you can try the following:
|
with finding the network name you can try the following:
|
||||||
|
|
||||||
* Check your wireless router for the IP adress and use that to connect instead, for example; http://192.168.1.56
|
* Check your wireless router for the IP address and use that to connect instead, for example; http://192.168.1.56
|
||||||
* Download an IP scanner / Port Scanner on your Windows computer or mobile device and use that to find what devices are listening on port 80.
|
* Download an IP scanner / Port Scanner on your Windows computer or mobile device and use that to find what devices are listening on port 80.
|
||||||
|
|
||||||
Once you can access the user interface then proceed to the next step.
|
Once you can access the user interface then proceed to the next step.
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
Getting started
|
Getting started
|
||||||
===============
|
===============
|
||||||
|
|
||||||
First you need a completed iSpindle hardware, there are several resouces around that topic so it
|
First you need a completed iSpindle hardware, there are several resources around that topic so it
|
||||||
will not be covered in this documentation. Please visit `iSpindel Homepage <https://www.ispindel.de>`_ for
|
will not be covered in this documentation. Please visit `iSpindel Homepage <https://www.ispindel.de>`_ for
|
||||||
more information.
|
more information.
|
||||||
|
|
||||||
@ -19,11 +19,17 @@ Step 2 - Setup WIFI
|
|||||||
When the device starts up the first time it will first start an WIFI access point so that the WIFI Settings
|
When the device starts up the first time it will first start an WIFI access point so that the WIFI Settings
|
||||||
can be configured. The instructions for that can be found here :ref:`setup_wifi`
|
can be configured. The instructions for that can be found here :ref:`setup_wifi`
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Since the user interface is built using modern frameworks the device requires access to the internet
|
||||||
|
for the UI to render and data to be populated. The sites that are needed are; https://cdn.jsdelivr.net/npm/bootstrap
|
||||||
|
and https://code.jquery.com
|
||||||
|
|
||||||
|
|
||||||
Step 3 - Configuration
|
Step 3 - Configuration
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
Once the device can connect to WIFI it will go into `configuration mode` and start a web server for
|
Once the device can connect to WIFI it will go into `configuration mode` and start a web server for
|
||||||
doing the initial configuration. In order to access the device you will need to find its name or ip adress.
|
doing the initial configuration. In order to access the device you will need to find its name or ip address.
|
||||||
|
|
||||||
It will broadcast a name like gravitymonXXXXXX.local over mDNS. Where the XXXXXX is the unique device id. You can
|
It will broadcast a name like gravitymonXXXXXX.local over mDNS. Where the XXXXXX is the unique device id. You can
|
||||||
find the name via an mDNS browser, check your router or connect the device to a serial monitor. On windows mDNS
|
find the name via an mDNS browser, check your router or connect the device to a serial monitor. On windows mDNS
|
||||||
@ -44,7 +50,7 @@ Configuration - Device Settings - Gyro Calibration
|
|||||||
++++++++++++++++++++++++++++++++++++++++++++++++++
|
++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
You need to place the device on a flat surface and then press the
|
You need to place the device on a flat surface and then press the
|
||||||
calibrate button. It will take a few seconds for this to complete and the angle should be close to 90 degress. Without
|
calibrate button. It will take a few seconds for this to complete and the angle should be close to 90 degrees. Without
|
||||||
calibration the device will not go into gravity mode.
|
calibration the device will not go into gravity mode.
|
||||||
|
|
||||||
Configuration - Push Settings
|
Configuration - Push Settings
|
||||||
@ -59,7 +65,7 @@ previous calibration then you can add them here, if not follow the calibration g
|
|||||||
|
|
||||||
There are several guides for how to calibrate the device (`iSpindle Calibration <https://www.ispindel.de/docs/Calibration_en.html>`_)
|
There are several guides for how to calibrate the device (`iSpindle Calibration <https://www.ispindel.de/docs/Calibration_en.html>`_)
|
||||||
|
|
||||||
This will get the data points needed to create the formula, and the datapoints will be stored on the device so you can
|
This will get the data points needed to create the formula, and the data points will be stored on the device so you can
|
||||||
adjust them when needed.
|
adjust them when needed.
|
||||||
|
|
||||||
Step 4 - Completed
|
Step 4 - Completed
|
||||||
|
@ -4,11 +4,44 @@ Q & A
|
|||||||
My device is no going in to sleep after fully charged
|
My device is no going in to sleep after fully charged
|
||||||
-----------------------------------------------------
|
-----------------------------------------------------
|
||||||
- Calibrate the device in the web interface
|
- 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 the angle/tilt. If the device is reporting 90 degrees 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.
|
- Check in reported voltage of the battery in the web interface. If this is higher than 4.15V the device believes its being charged. In that case adjust the voltage factor under hardware so the voltage drops below 4.15V.
|
||||||
- Check if the `always on` option is activated in the web interface.
|
- Check if the `always on` option is activated in the web interface.
|
||||||
|
|
||||||
My device reports a temperature of -273C or -491F
|
My device reports a temperature of -273C or -491F
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
- The DS18B20 temperature sensor cannot be found and this is the default value reported in this case.
|
- The DS18B20 temperature sensor cannot be found and this is the default value reported in this case.
|
||||||
- Check the orienation of the sensor and soldering.
|
- Check the orientation of the sensor and soldering.
|
||||||
|
|
||||||
|
Calibration error (unable to find a valid formula)
|
||||||
|
--------------------------------------------------
|
||||||
|
If you have issues to get a calibration formula. When a formula has been created the device always tries to validate the formula against the supplied values and if there is a to high deviation on any of the values then the formula will be rejected.
|
||||||
|
|
||||||
|
The image here shows such a case, one of the values is out of bounds.
|
||||||
|
|
||||||
|
.. image:: images/qa_1.png
|
||||||
|
:width: 500
|
||||||
|
:alt: Calibration Error
|
||||||
|
|
||||||
|
To fix these this you can;
|
||||||
|
|
||||||
|
- remove the value from the list (setting the angle to zero will do that)
|
||||||
|
- change the advanced setting (Formula max deviation) to a higher value and save the values again.
|
||||||
|
|
||||||
|
In the case above this parameter was changed from 1.6 SG to 4 SG and the formula was accepted. The deviation on this point was just above 3 SG.
|
||||||
|
|
||||||
|
User interface does not render correctly
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
Since the user interface is built using bootstrap v5 the device requires access to the internet
|
||||||
|
to download required javascript and css files. Due to size it would not be possible to store these
|
||||||
|
on the device. Make sure the device can access: https://cdn.jsdelivr.net/npm/bootstrap
|
||||||
|
|
||||||
|
Data is not populated in the fields
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
The user interface uses JQuery to fetch data from the device. This javascript library needs to be downloaded
|
||||||
|
from the internet. Due to size it would not be possible to store these on the device. Make sure the
|
||||||
|
device can access: https://code.jquery.com
|
||||||
|
|
||||||
|
Also ensure that any security tools does not block the execution of these features.
|
||||||
|
@ -3,6 +3,75 @@
|
|||||||
Releases
|
Releases
|
||||||
########
|
########
|
||||||
|
|
||||||
|
v1.1.1
|
||||||
|
======
|
||||||
|
* BUG: The text before the first variable was missed in the conversion of a format template.
|
||||||
|
|
||||||
|
v1.1.0
|
||||||
|
======
|
||||||
|
|
||||||
|
Features
|
||||||
|
++++++++
|
||||||
|
* Added information to error log about abnormal resets (for instance crashes) to detect and fix those
|
||||||
|
* Changed storage mode so that the device will go into deep sleep until reset (sleep forever)
|
||||||
|
* Updated sensor types in home assistant for auto registration of device
|
||||||
|
* Added ${app-ver} and ${app-build} to format template as new variables
|
||||||
|
* Improved error messages when creating formula so the troublesome measurement points can be identified
|
||||||
|
* Changed default validation threshold from 1.6 SG to 3.0 SG, this should allow for some more variance when creating formula
|
||||||
|
* Updated format template for Home Assistant, aligned with new mqtt configuration format
|
||||||
|
* Added format template for Home Assistant with automatic device registration
|
||||||
|
* Added storage mode which is activated under hardware setting. When place on the cap (<5 degree tilt) the device will go into storage mode and deep sleep.
|
||||||
|
|
||||||
|
Known issues, not yet fixed
|
||||||
|
+++++++++++++++++++++++++++
|
||||||
|
* When updating firmware and the feature `deep sleep` is active the device will activate deep sleep if the gyro is not responding. FIX: Reboot device
|
||||||
|
|
||||||
|
Issues addressed
|
||||||
|
++++++++++++++++
|
||||||
|
* Refactored error logging function to reduce memory usage and crashes. Max size of error log is 2 x 4 kb
|
||||||
|
* Refactored format template engine to reduce memory usage and crashes, can how handle slightly larger payloads than before. Increase from around 1100 chars to 1600 chars
|
||||||
|
* BUG: Refactored format api to handle larger payloads
|
||||||
|
* BUG: After manual firmware upload the device would crash and go into wifi setup mode.
|
||||||
|
* BUG: After manual firmware upload the device will in some cases not be able to connect with the gyro, the symptom is that it will say, "Gyro moving" in the web UI. In this case the device needs to be reset (or powered on/off). I havent found a way to fix this from the code. The message after firmware update has been updated with this information
|
||||||
|
* BUG: Temp corrected gravity was not used when pushing data to removed
|
||||||
|
* BUG: Low memory in format api which resulted in mqtt template to be set to null
|
||||||
|
* BUG: Large format templates could be saved but when loading it's only blank
|
||||||
|
* BUG: Copy format templates used an old format for iSpindle and Gravmon where the token was not used
|
||||||
|
* BUG: Gravity correction formula not calculating correctly
|
||||||
|
|
||||||
|
User interface
|
||||||
|
++++++++++++++
|
||||||
|
* Updated format template with information on size and warning message if the template is too large
|
||||||
|
* Added error message if gyro connection/initialization fails (before the message was Gyro Moving only)
|
||||||
|
* Added error message if no temp sensor can be found
|
||||||
|
* Added drop down menus in user interface to simplify navigation to sub pages (format, test and upload)
|
||||||
|
* Added Assistant Device registration, this is only done when format template is saved, during normal operation only data values are posted on MQTT. If HA is restarted then the device will disappear
|
||||||
|
* Calibration temperature (for temp adjustment) can now be set under advanced settings, default is 20C
|
||||||
|
* Changed length of device name from 12 to 63 chars. 63 is the max limit according to mdns.
|
||||||
|
* Under format options its now possible to select brewfather iSpindle format to avoid errors connected to using the wrong format template with the various brewfather endpoints
|
||||||
|
* Added brewblox as format under format options
|
||||||
|
* Added home assistant (with device registration) as format under format options
|
||||||
|
* User can now edit the voltage level that forces the device into config mode (device detects charging)
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
+++++++++++++
|
||||||
|
* Added documentation for Brewpiless as target
|
||||||
|
* Added documentation for BVrewblox as target
|
||||||
|
* Updated documentation for HA integration since described method was deprecated
|
||||||
|
* Updated documentation for ubidots service integration
|
||||||
|
* Updated documentation in data format section
|
||||||
|
* Updated hardware section with documentation on installing reed switch
|
||||||
|
* Updated configuration section with documentation on new settings
|
||||||
|
* Added q&a on formula creation and value deviation
|
||||||
|
|
||||||
|
Other
|
||||||
|
+++++
|
||||||
|
* Upgraded framework for ESP8266 to v5.0.0
|
||||||
|
* Upgraded framework for ESP32 to v2.0.2
|
||||||
|
* Updated OneWire library to be complaint with new ESP32 SDK
|
||||||
|
* Fixed issue in i2cdev connected to wrong usage of TwoWire on ESP32 (Gyro initialization hang).
|
||||||
|
|
||||||
|
|
||||||
v1.0.0
|
v1.0.0
|
||||||
======
|
======
|
||||||
|
|
||||||
@ -10,14 +79,14 @@ Documentation
|
|||||||
+++++++++++++
|
+++++++++++++
|
||||||
* Update documentation to match v1.0
|
* Update documentation to match v1.0
|
||||||
* Installation instructions updated on how to find the device after wifi has been configured.
|
* Installation instructions updated on how to find the device after wifi has been configured.
|
||||||
* Documentation on brewfather has been updated to adress SG/Plato conversion
|
* Documentation on brewfather has been updated to address SG/Plato conversion
|
||||||
* Added circuit diagram for esp8266 and esp32
|
* Added circuit diagram for esp8266 and esp32
|
||||||
* Added additional http error codes to troubleshooting documentation
|
* Added additional http error codes to troubleshooting documentation
|
||||||
|
|
||||||
User interface
|
User interface
|
||||||
++++++++++++++
|
++++++++++++++
|
||||||
* Upgraded to bootstrap v5.1 for web pages.
|
* Upgraded to bootstrap v5.1 for web pages.
|
||||||
* Added button on indexpage to direct to github issues.
|
* Added button on index page to direct to github issues.
|
||||||
* Added button to extract important information for support requests.
|
* Added button to extract important information for support requests.
|
||||||
* First point in gravity formula is now reserved for water gravity, this to allow detection of angles below water that can be filtered out.
|
* First point in gravity formula is now reserved for water gravity, this to allow detection of angles below water that can be filtered out.
|
||||||
* Changed layout on index page with measured data on top.
|
* Changed layout on index page with measured data on top.
|
||||||
@ -28,19 +97,19 @@ Features
|
|||||||
++++++++
|
++++++++
|
||||||
* Added advanced setting to ignore angles that are lower than water. This is disabled by default.
|
* Added advanced setting to ignore angles that are lower than water. This is disabled by default.
|
||||||
* Added support for MPU6500 (standard is MPU6050).
|
* Added support for MPU6500 (standard is MPU6050).
|
||||||
* Removed brewfather option (can use standard HTTP options), the old apporach can still be used via changing format template.
|
* Removed brewfather option (can use standard HTTP options), the old approach can still be used via changing format template.
|
||||||
* Added 5 more points for formula creation, so a total of 10 angles/gravity values can be stored.
|
* Added 5 more points for formula creation, so a total of 10 angles/gravity values can be stored.
|
||||||
* Added https support for Influxdb v2
|
* Added https support for Influxdb v2
|
||||||
* Added possibility to set 2 wifi ssid where the second acts as a fallback in case it fails to connect. If succesful the seconday becomes the new primary.
|
* Added possibility to set 2 wifi ssid where the second acts as a fallback in case it fails to connect. If successful the secondary becomes the new primary.
|
||||||
* SSL connections are skipped on ESP8266 when in config mode since there is a high probability it will crash due to low memory.
|
* SSL connections are skipped on ESP8266 when in config mode since there is a high probability it will crash due to low memory.
|
||||||
* Advanced settings: Added possibility to have variable push intervals for different endpoints so that different frequency can be used, for example; 5min mqtt, 15min brewfather.
|
* Advanced settings: Added possibility to have variable push intervals for different endpoints so that different frequency can be used, for example; 5min mqtt, 15min brewfather.
|
||||||
* Advanced settings: Changes how many times the gyro is read (less reads, quicker but less accurate)
|
* Advanced settings: Changes how many times the gyro is read (less reads, quicker but less accurate)
|
||||||
* Advanced settings: Set amount of gyro movement is allowed for a accurate read.
|
* Advanced settings: Set amount of gyro movement is allowed for a accurate read.
|
||||||
* Advanced settings: What deviation is acceptable for creating formula deviation
|
* Advanced settings: What deviation is acceptable for creating formula deviation
|
||||||
* Advanced settings: Various timeouts, wifi connect, wifi portal, http connects.
|
* Advanced settings: Various timeouts, wifi connect, wifi portal, http connects.
|
||||||
* Advanced settings: Adjust resolution of temp sensor (9 bits to 12 bits), higher resolution takes longer thus reducing batterylife
|
* Advanced settings: Adjust resolution of temp sensor (9 bits to 12 bits), higher resolution takes longer thus reducing battery life
|
||||||
|
|
||||||
Issues adressed
|
Issues addressed
|
||||||
++++++++++++++++
|
++++++++++++++++
|
||||||
* BUG: Fixed issue in formula calculation in case there were a gap in the data series
|
* BUG: Fixed issue in formula calculation in case there were a gap in the data series
|
||||||
* BUG: Field name for wifi strength changed from "rssi" to "RSSI"
|
* BUG: Field name for wifi strength changed from "rssi" to "RSSI"
|
||||||
@ -51,7 +120,7 @@ v0.9.0
|
|||||||
======
|
======
|
||||||
* Added one http push target that uses HTTP GET. This can be used with ubidots or blynk api's.
|
* Added one http push target that uses HTTP GET. This can be used with ubidots or blynk api's.
|
||||||
* Added function to test push targets from configuration page. It will send data and show the return code as a first step.
|
* Added function to test push targets from configuration page. It will send data and show the return code as a first step.
|
||||||
* Added documetation on how to integrate with Blynk.io using http get.
|
* Added documentation on how to integrate with Blynk.io using http get.
|
||||||
* Config page now shows the estimated runtime for the device (based on a full battery and previous average runtime)
|
* Config page now shows the estimated runtime for the device (based on a full battery and previous average runtime)
|
||||||
* Experimental release of firmware using an esp32 instead of esp8266
|
* Experimental release of firmware using an esp32 instead of esp8266
|
||||||
* Merged index and device pages into one so that all the needed information is available on the index page.
|
* Merged index and device pages into one so that all the needed information is available on the index page.
|
||||||
@ -68,9 +137,9 @@ v0.9.0
|
|||||||
* BUG: Corrected PIN for voltage read on ESP32
|
* BUG: Corrected PIN for voltage read on ESP32
|
||||||
* BUG: If using plato and not gravity formula was defined the value was set to null.
|
* BUG: If using plato and not gravity formula was defined the value was set to null.
|
||||||
* BUG: Temp format name was incorrect in iSpindle format causing receiver to incorrectly read temperature.
|
* BUG: Temp format name was incorrect in iSpindle format causing receiver to incorrectly read temperature.
|
||||||
* BUG: Temperature sensor adjusmemnt value was not handled properly when using Farenheight.
|
* BUG: Temperature sensor adjustment value was not handled properly when using Fahrenheit.
|
||||||
* BUG: If the ID was to low the device id could end up with a leading space causing errors in data post. Added leading zero to ID.
|
* BUG: If the ID was to low the device id could end up with a leading space causing errors in data post. Added leading zero to ID.
|
||||||
* BUG: Entering wifi setup and a timeout occured the wifi settings could be deleted.
|
* BUG: Entering wifi setup and a timeout occurred the wifi settings could be deleted.
|
||||||
|
|
||||||
v0.8.0
|
v0.8.0
|
||||||
======
|
======
|
||||||
@ -90,7 +159,7 @@ v0.8.0
|
|||||||
from 16k to 2k. This can make a huge difference on a device with only 40k RAM. Not all
|
from 16k to 2k. This can make a huge difference on a device with only 40k RAM. Not all
|
||||||
servers might support this feature.
|
servers might support this feature.
|
||||||
* Updated documentation pages.
|
* Updated documentation pages.
|
||||||
* Tested batterylife, 47 days using an update frequency of 5 min
|
* Tested battery life, 47 days using an update frequency of 5 min
|
||||||
|
|
||||||
v0.7.1
|
v0.7.1
|
||||||
======
|
======
|
||||||
@ -112,7 +181,7 @@ Latest stable version. `Release v0.7 on Github <https://github.com/mp-se/gravity
|
|||||||
* Added support for Plato
|
* Added support for Plato
|
||||||
* Added error handling for calibration page.
|
* Added error handling for calibration page.
|
||||||
* Added experimental target ESP32 (using an ESP32 D1 Mini which is pin compatible with ESP8266). Not
|
* Added experimental target ESP32 (using an ESP32 D1 Mini which is pin compatible with ESP8266). Not
|
||||||
really usable since wifi connection is extreamly slow with current Arduino releases (3-8 seconds).
|
really usable since wifi connection is extremely slow with current Arduino releases (3-8 seconds).
|
||||||
* Added experimental format editor so users can customize their data format used for pushing data.
|
* Added experimental format editor so users can customize their data format used for pushing data.
|
||||||
This will reduce the need for custom push targets. As long as the service is supporting http
|
This will reduce the need for custom push targets. As long as the service is supporting http
|
||||||
or https then the data format can be customized.
|
or https then the data format can be customized.
|
||||||
@ -143,7 +212,7 @@ v0.6.0
|
|||||||
|
|
||||||
v0.5.0
|
v0.5.0
|
||||||
======
|
======
|
||||||
* Added feature to calcuate formula on device
|
* Added feature to calculate 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
|
||||||
* Defined version numbers for all dependant libraries to avoid updates breaking build.
|
* Defined version numbers for all dependant libraries to avoid updates breaking build.
|
||||||
|
@ -8,11 +8,11 @@ This chapter contains a list of targets and what configuration is needed to inte
|
|||||||
Brewfather
|
Brewfather
|
||||||
++++++++++
|
++++++++++
|
||||||
|
|
||||||
Brewfather is an all in one service that allows you to manage you recepies and brews.
|
Brewfather is an all in one service that allows you to manage you recipes and brews.
|
||||||
|
|
||||||
**Option 1** - iSpindle Endpoint
|
**Option 1** - iSpindle Endpoint
|
||||||
|
|
||||||
This opion makes use of the standard http (1 or 2) endpoints in the push section. If you are using SG then the device name needs to end with [SG] or brewfather will assume
|
This option makes use of the standard http (1 or 2) endpoints in the push section. If you are using SG then the device name needs to end with [SG] or brewfather will assume
|
||||||
that the data is in plato. You can also modify the format template using the following options:
|
that the data is in plato. You can also modify the format template using the following options:
|
||||||
|
|
||||||
Update the following part `"gravity": ${gravity-plato},` or `"name" : "${mdns}[SG]",``
|
Update the following part `"gravity": ${gravity-plato},` or `"name" : "${mdns}[SG]",``
|
||||||
@ -29,7 +29,7 @@ Documentation on this can be found under `Brewfather iSpindle Endpoint <https://
|
|||||||
|
|
||||||
**Option 2** - Custom Stream
|
**Option 2** - Custom Stream
|
||||||
|
|
||||||
This option makes use of the http push endpoint with a custom format template. Just enter the http stream adress found
|
This option makes use of the http push endpoint with a custom format template. Just enter the http stream address found
|
||||||
on brewfather, not other settings are needed. The stream endpoint URL has the following format:
|
on brewfather, not other settings are needed. The stream endpoint URL has the following format:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
@ -75,9 +75,11 @@ GravityMon can be installed and used as an iSpindle. Just register the device as
|
|||||||
UBIdots
|
UBIdots
|
||||||
+++++++
|
+++++++
|
||||||
|
|
||||||
`UBIdots <https://www.ubidots.com>`_ is a IoT service that display data collected various sources.
|
`UBIdots <https://www.ubidots.com>`_ is a IoT service that display data collected various sources. There is a limitation on this service where it can only handle 10 variables per device so
|
||||||
|
you might need to reduce the number of values sent to the service. It will also treat every parameter as a number unless you create a custom device type and explicit define the string values
|
||||||
|
as text. This will require a paid subscription (as I interpret the documentation). The example format below will only send numbers so that should work fine with the paid subscription.
|
||||||
|
|
||||||
For this service there are two options to configure the integration. First you will need your default token which is found under `API Credentials` (<api-tokem> in the example below).
|
For this service there are two options to configure the integration. First you will need your default token which is found under `API Credentials` (<api-token> in the example below).
|
||||||
Swap the text <devicename> with the name you want to show in ubidots.
|
Swap the text <devicename> with the name you want to show in ubidots.
|
||||||
|
|
||||||
**Option 1** - token as an URL parameter
|
**Option 1** - token as an URL parameter
|
||||||
@ -110,7 +112,7 @@ Under `Headers` (button after the http url in the UI) enter the following string
|
|||||||
|
|
||||||
This is the more secure option.
|
This is the more secure option.
|
||||||
|
|
||||||
Even though ubidots can handle the default ispindle format it probably better to just post the data you want. This is an example of a
|
Even though ubidots can handle the default iSpindle format it probably better to just post the data you want. This is an example of a
|
||||||
format template that can be used. For information on customizing the format see :ref:`format-editor`.
|
format template that can be used. For information on customizing the format see :ref:`format-editor`.
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
@ -127,7 +129,7 @@ format template that can be used. For information on customizing the format see
|
|||||||
Home Assistant
|
Home Assistant
|
||||||
+++++++++++++++
|
+++++++++++++++
|
||||||
|
|
||||||
`HomeAssistant <https://www.homeassistant.com>`_ is a platform for home automation and can collect sensor data
|
`HomeAssistant <https://www.homeassistant.io>`_ is a platform for home automation and can collect sensor data
|
||||||
from multiple devices.
|
from multiple devices.
|
||||||
|
|
||||||
This setup uses the MQTT integration with home assistant to collect values from the device.
|
This setup uses the MQTT integration with home assistant to collect values from the device.
|
||||||
@ -137,40 +139,60 @@ device is named `gravmon2`
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
mqtt:
|
||||||
sensor:
|
sensor:
|
||||||
- platform: mqtt
|
- name: "Gravmon2-Gravity"
|
||||||
name: "gravmon2_gravity"
|
state_topic: "gravmon/gravmon2/gravity"
|
||||||
state_topic: "gravmon/gravmon2/gravity"
|
unique_id: gravmon2_grav
|
||||||
- platform: mqtt
|
unit_of_measurement: "SG"
|
||||||
name: "gravmon2_battery"
|
- name: "Gravmon2-RSSI"
|
||||||
state_topic: "gravmon/gravmon2/battery"
|
state_topic: "gravmon/gravmon2/rssi"
|
||||||
- platform: mqtt
|
unique_id: gravmon2_rssi
|
||||||
name: "gravmon2_rssi"
|
unit_of_measurement: "dBm"
|
||||||
state_topic: "gravmon/gravmon2/RSSI"
|
|
||||||
- platform: mqtt
|
|
||||||
name: "gravmon2_temp"
|
|
||||||
state_topic: "gravmon/gravmon2/temp"
|
|
||||||
|
|
||||||
|
|
||||||
Enter the name of the MQTT server in Home Assistant in the URL. You might need to install that option
|
Enter the name of the MQTT server in Home Assistant in the URL. You might need to install that option
|
||||||
first. This is the format needed to submit the data to the correct topics as needed above. You can add as
|
first. This is the format needed to submit the data to the correct topics as needed above. You can add as
|
||||||
many sensors / topics as you want.
|
many sensors / topics as you want. It's also possible that you will need to create a user and supply the
|
||||||
|
username / password to be able to publish messages on a topic.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
gravmon/${mdns}/gravity:${gravity}|
|
gravmon/${mdns}/gravity:${gravity}|
|
||||||
|
gravmon/${mdns}/rssi:${rssi}|
|
||||||
gravmon/${mdns}/battery:${battery}|
|
gravmon/${mdns}/battery:${battery}|
|
||||||
gravmon/${mdns}/RSSI:${rssi}|
|
|
||||||
gravmon/${mdns}/temp:${temp}|
|
|
||||||
|
It's also possible to allow home assistant to do auto discovery and automatically create the sensor. This format
|
||||||
|
template will create two sensors and update the values for them. The registration will occur when you save the format template. If Home Assistant
|
||||||
|
is restarted then the device will disappear. The first method is the most persistent one.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
This will only work on 1.1+ since the the memory allocation on previous versions are not enough to handle this large payload.
|
||||||
|
Earlier version can handle 2 of the values.
|
||||||
|
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
gravmon/${mdns}/temperature:${temp}|
|
||||||
|
gravmon/${mdns}/gravity:${gravity}|
|
||||||
|
gravmon/${mdns}/rssi:${rssi}|
|
||||||
|
gravmon/${mdns}/tilt:${tilt}|
|
||||||
|
gravmon/${mdns}/battery:${battery}|
|
||||||
|
homeassistant/sensor/gravmon_${id}/temperature/config:{"dev":{"name":"${mdns}","mdl":"gravmon","sw":"${app-ver}","ids":"${id}"},"uniq_id":"${id}_temp","name":"temperature","dev_cla":"temperature","unit_of_meas":"${temp-unit}","stat_t":"gravmon/${mdns}/temperature"}|
|
||||||
|
homeassistant/sensor/gravmon_${id}/gravity/config:{"dev":{"name":"${mdns}","mdl":"gravmon","sw":"${app-ver}","ids":"${id}"},"uniq_id":"${id}_grav","name":"gravity","dev_cla":"temperature","unit_of_meas":" ${gravity-unit}","stat_t":"gravmon/${mdns}/gravity"}|
|
||||||
|
homeassistant/sensor/gravmon_${id}/rssi/config:{"dev":{"name":"${mdns}","mdl":"gravmon","sw":"${app-ver}","ids":"${id}"},"uniq_id":"${id}_rssi","name":"rssi","dev_cla":"temperature","unit_of_meas":"dBm","stat_t":"gravmon/${mdns}/rssi"}|
|
||||||
|
homeassistant/sensor/gravmon_${id}/tilt/config:{"dev":{"name":"${mdns}","mdl":"gravmon","sw":"${app-ver}","ids":"${id}"},"uniq_id":"${id}_tilt","name":"tilt","dev_cla":"temperature","stat_t":"gravmon/${mdns}/tilt"}|
|
||||||
|
homeassistant/sensor/gravmon_${id}/battery/config:{"dev":{"name":"${mdns}","mdl":"gravmon","sw":"${app-ver}","ids":"${id}"},"uniq_id":"${id}_batt","name":"battery","dev_cla":"voltage","unit_of_meas":"V","stat_t":"gravmon/${mdns}/battery"}|
|
||||||
|
|
||||||
|
|
||||||
Brewer's Friend
|
Brewer's Friend
|
||||||
+++++++++++++++
|
+++++++++++++++
|
||||||
|
|
||||||
Brewer's friend is an all in one service that allows you to manage you recepies and brews.
|
Brewer's friend is an all in one service that allows you to manage you recipes and brews.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
I dont have an account for brewers friend so I have not been able to verfy this completely. Its based on
|
I don't have an account for brewers friend so I have not been able to verify this completely. Its based on
|
||||||
the available documentation. If this works please let
|
the available documentation. If this works please let
|
||||||
|
|
||||||
You can find you API key when logged in to the service. Follow these `instructions <https://docs.brewersfriend.com/devices/ispindel>`_
|
You can find you API key when logged in to the service. Follow these `instructions <https://docs.brewersfriend.com/devices/ispindel>`_
|
||||||
@ -264,3 +286,44 @@ starting with a ``?``. This string will be added to the URL above when doing the
|
|||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
?token=${token2}&v1=${temp}&v2=${gravity}&v3=${angle}
|
?token=${token2}&v1=${temp}&v2=${gravity}&v3=${angle}
|
||||||
|
|
||||||
|
|
||||||
|
Brewpiless
|
||||||
|
++++++++++
|
||||||
|
|
||||||
|
If you connect the device to the brewpiless access point there is not way to access the user interface for configuration so it's recommended to connect the device to your normal network.
|
||||||
|
|
||||||
|
The device need to have a name starting with iSpindle, for example `iSpindel000`. Set the URL for one of the http POST targets to `http://ip/gravity` where ip is the ip address of Brewpiless.
|
||||||
|
|
||||||
|
|
||||||
|
BrewBlox
|
||||||
|
++++++++++
|
||||||
|
|
||||||
|
To send iSpindel data to brewblox over mqtt you need to modify the format template to match the expected format. Once you have configured the mqtt information you also need to update the format template
|
||||||
|
for this target.
|
||||||
|
|
||||||
|
This format template will post the expected json document on the topic, don't forget the `|` character at the end of the line which is needed to parse the payload. The first to words are the topic
|
||||||
|
name and after the first `:` this is the json payload. Text within the brackets will be used as the unit for the value and degC is displayed as °C. You can add other parameters under the data section
|
||||||
|
in the json document if you need other values as well.
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
brewcast/history:{"key":"${mdns}","data":{"Temperature[degC]": ${temp-c},"Temperature[degF]": ${temp-f},"Battery[V]":${battery},"Tilt[deg]":${angle},"Rssi[dBm]":${rssi},"SG":${gravity-sg},"Plato":${gravity-plato}}}|
|
||||||
|
|
||||||
|
|
||||||
|
The json message on the mqtt topic would look like this:
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"key": "gravitymon",
|
||||||
|
"data": {
|
||||||
|
"Temperature[degC]": 27,
|
||||||
|
"Temperature[degF]": 80,
|
||||||
|
"Battery[V]": 4.1,
|
||||||
|
"Tilt[deg]": 25,
|
||||||
|
"Rssi[dBm]": -78,
|
||||||
|
"SG": 1,
|
||||||
|
"Plato": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -13,12 +13,12 @@ Log errors
|
|||||||
* Error validating created formula. Deviation to large, formula rejected
|
* Error validating created formula. Deviation to large, formula rejected
|
||||||
|
|
||||||
The device will try to create formulas with different complexities. It will try to
|
The device will try to create formulas with different complexities. It will try to
|
||||||
validate the formula using the supplied values. If the differnce is more than 1.6 SG on any point
|
validate the formula using the supplied values. If the difference is more than 1.6 SG on any point
|
||||||
the formula will be rejected. Check the entered values if they seams to be resonable.
|
the formula will be rejected. Check the entered values if they seams to be reasonable.
|
||||||
|
|
||||||
* No valid calibration values, please calibrate the device.
|
* No valid calibration values, please calibrate the device.
|
||||||
|
|
||||||
The gyro needs to be calibrated at 90 degress (flat). This is done on the configration page.
|
The gyro needs to be calibrated at 90 degrees (flat). This is done on the configuration page.
|
||||||
|
|
||||||
* Low on memory, skipping push
|
* Low on memory, skipping push
|
||||||
|
|
||||||
@ -33,10 +33,10 @@ Log errors
|
|||||||
* Influxdb push failed response
|
* Influxdb push failed response
|
||||||
* HTTP push failed response
|
* HTTP push failed response
|
||||||
|
|
||||||
All these errors are standard http error codes. This are the commone ones;
|
All these errors are standard http error codes. This are the common ones;
|
||||||
|
|
||||||
* 400 - Bad request. Probably an issue with the post format. Check format in the format editor.
|
* 400 - Bad request. Probably an issue with the post format. Check format in the format editor.
|
||||||
* 401 - Unathorized. The service needs an token or other means to authenticate the device.
|
* 401 - Unauthorized. The service needs an token or other means to authenticate the device.
|
||||||
* 403 - Forbidden. Could be an issue with token or URL.
|
* 403 - Forbidden. Could be an issue with token or URL.
|
||||||
* 404 - Not found. Probably a wrong URL.
|
* 404 - Not found. Probably a wrong URL.
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"gyro-read-count": 50,
|
"gyro-read-count": 50,
|
||||||
"gyro-moving-threashold": 500,
|
"gyro-moving-threashold": 500,
|
||||||
"formula-max-deviation": 1.6,
|
"formula-max-deviation": 3,
|
||||||
"wifi-portal-timeout": 120,
|
"wifi-portal-timeout": 120,
|
||||||
"wifi-connect-timeout": 20,
|
"wifi-connect-timeout": 20,
|
||||||
"formula-calibration-temp": 20,
|
|
||||||
"tempsensor-resolution": 9,
|
|
||||||
"ignore-low-angles": false,
|
|
||||||
"push-timeout": 10,
|
"push-timeout": 10,
|
||||||
|
"formula-calibration-temp": 20,
|
||||||
"int-http1": 0,
|
"int-http1": 0,
|
||||||
"int-http2": 0,
|
"int-http2": 0,
|
||||||
"int-http3": 0,
|
"int-http3": 0,
|
||||||
"int-influx": 0,
|
"int-influx": 0,
|
||||||
"int-mqtt": 0
|
"int-mqtt": 0,
|
||||||
|
"tempsensor-resolution": 9,
|
||||||
|
"ignore-low-angles": false
|
||||||
}
|
}
|
@ -20,8 +20,10 @@
|
|||||||
"mqtt-port": 1883,
|
"mqtt-port": 1883,
|
||||||
"mqtt-user": "user",
|
"mqtt-user": "user",
|
||||||
"mqtt-pass": "pass",
|
"mqtt-pass": "pass",
|
||||||
|
"storage-sleep": true,
|
||||||
"sleep-interval": 30,
|
"sleep-interval": 30,
|
||||||
"voltage-factor": 1.59,
|
"voltage-factor": 1.59,
|
||||||
|
"voltage-config": 4.15,
|
||||||
"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",
|
||||||
"gravity-format": "G",
|
"gravity-format": "G",
|
||||||
"temp-adjustment-value": 0,
|
"temp-adjustment-value": 0,
|
||||||
@ -62,5 +64,7 @@
|
|||||||
"battery": 0.04,
|
"battery": 0.04,
|
||||||
"runtime-average": 3.0,
|
"runtime-average": 3.0,
|
||||||
"ble": "pink",
|
"ble": "pink",
|
||||||
|
"app-ver": "0.1.0",
|
||||||
|
"app-build": "build",
|
||||||
"platform": "esp32"
|
"platform": "esp32"
|
||||||
}
|
}
|
7
test/log1
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Log Entry 11
|
||||||
|
Log Entry 12
|
||||||
|
Log Entry 13
|
||||||
|
Log Entry 14
|
||||||
|
Log Entry 15
|
||||||
|
Log Entry 16
|
||||||
|
Log Entry 17
|