Compare commits
75 Commits
Author | SHA1 | Date | |
---|---|---|---|
e234d6f908 | |||
2662f38bf7 | |||
3b3ee5bbf9 | |||
4731569b75 | |||
a255239c02 | |||
c72ba40884 | |||
7dde662aa8 | |||
c08ac7c4e4 | |||
4e32bfbec6 | |||
e4fd33fe87 | |||
33748aa1c2 | |||
c72a249bd4 | |||
b1ccdfad6a | |||
b277274eab | |||
e321eadf0f | |||
604761dbe0 | |||
1d5e0ffb63 | |||
dfe6bbf61d | |||
1fe49d5fad | |||
3f74228a12 | |||
788e0e783d | |||
825d2ba6f3 | |||
4d65e0d3e0 | |||
95985eab0e | |||
5119ffcbe5 | |||
0a53a5efab | |||
2a61658088 | |||
33081ee290 | |||
d21d5f7965 | |||
7e264b29ff | |||
503de20e42 | |||
9ea4d302d6 | |||
d7b30c484d | |||
3c1bf123b4 | |||
42205a94c9 | |||
1894bc4959 | |||
1928dbb749 | |||
6c26bbeacb | |||
c67585c7e4 | |||
11d106da4f | |||
fdd04bc19a | |||
5f2c7eb2d8 | |||
864f04c707 | |||
8f95cfaea5 | |||
19f3ba1aae | |||
3e947346b4 | |||
31a8500405 | |||
5573e06155 | |||
298ea1de61 | |||
1478430f03 | |||
5612c0ce64 | |||
e3ac920fc4 | |||
ed53182c29 | |||
88bd971b73 | |||
5d9115137f | |||
299d2dd0fe | |||
2fb86c594a | |||
51a2d30824 | |||
4de40366bb | |||
27b7b70998 | |||
4f0b6d11e7 | |||
20d5f50c19 | |||
93df4b60e0 | |||
0c0e9067f9 | |||
c1db431490 | |||
791f5fedd2 | |||
f8e5957045 | |||
088a37eaf3 | |||
5a7858ecaf | |||
0783a206b2 | |||
d796794602 | |||
d075fe2117 | |||
48f71bd59a | |||
bdd4b7117b | |||
29174bf1f9 |
43
.github/workflows/doc-build.yaml
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
name: Sphinx Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: ammaraskar/sphinx-action@master
|
||||||
|
with:
|
||||||
|
docs-folder: "src_docs/"
|
||||||
|
pre-build-command: "pip install sphinx_rtd_theme"
|
||||||
|
build-command: "sphinx-build -b html ./source ../docs"
|
||||||
|
|
||||||
|
- name: Commit documentation changes
|
||||||
|
run: |
|
||||||
|
pwd
|
||||||
|
ls -al
|
||||||
|
ls -al docs
|
||||||
|
git clone https://github.com/mp-se/gravitymon.git --branch ghpages --single-branch ghpages
|
||||||
|
mkdir -p ghpages/docs
|
||||||
|
cp -r docs/* ghpages/docs
|
||||||
|
cd ghpages
|
||||||
|
touch docs/.nojekyll
|
||||||
|
git config --local user.email "action@noreply.github.com"
|
||||||
|
git config --local user.name "GitHub Action"
|
||||||
|
git add .
|
||||||
|
git commit -m "Update documentation" -a || true
|
||||||
|
# git push https://${{secrets.token}}@github.com/mp-se/gravitymon.git
|
||||||
|
# The above command will fail if no changes were present, so we ignore
|
||||||
|
# the return code.
|
||||||
|
- name: Push changes
|
||||||
|
uses: ad-m/github-push-action@master
|
||||||
|
with:
|
||||||
|
branch: ghpages
|
||||||
|
directory: ghpages
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
52
.github/workflows/pio-build.yaml
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
name: PlatformIO CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Cache pip
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.cache/pip
|
||||||
|
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pip-
|
||||||
|
|
||||||
|
- name: Cache PlatformIO
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.platformio
|
||||||
|
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
|
||||||
|
- name: Install PlatformIO
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install --upgrade platformio
|
||||||
|
git config --global advice.detachedHead false
|
||||||
|
|
||||||
|
- name: Run PlatformIO
|
||||||
|
#run: pio run -e gravity-release -e gravity-perf -e gravity-debug
|
||||||
|
run: pio run -e gravity-release -e gravity-perf
|
||||||
|
|
||||||
|
- uses: EndBug/add-and-commit@v7 # You can change this to use a specific version. https://github.com/marketplace/actions/add-commit
|
||||||
|
with:
|
||||||
|
add: 'bin'
|
||||||
|
author_name: GitHub Action
|
||||||
|
author_email: magnus@users.noreply.github.com
|
||||||
|
|
||||||
|
branch: dev
|
||||||
|
|
||||||
|
default_author: github_actor
|
||||||
|
message: 'GitHub Action Build'
|
||||||
|
pathspec_error_handling: ignore
|
5
.gitignore
vendored
@ -1,8 +1,9 @@
|
|||||||
.pio/*
|
.pio/*
|
||||||
.vscode/*
|
.vscode/*
|
||||||
*.map
|
*.map
|
||||||
docs/*
|
|
||||||
test/*.md
|
test/*.md
|
||||||
test/env/*
|
test/env/*
|
||||||
test/*.py
|
test/configure_*.py
|
||||||
TODO.md
|
TODO.md
|
||||||
|
src_docs/_build/*
|
||||||
|
data/*.min.htm
|
||||||
|
8
.pre-commit-config.yaml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
repos:
|
||||||
|
- repo: https://github.com/bmorcos/pre-commit-hooks-cpp
|
||||||
|
rev: 9a5aa38207bf557961110d6a4f7e3a9d352911f9
|
||||||
|
hooks:
|
||||||
|
- id: clang-format
|
||||||
|
- id: cpplint
|
||||||
|
- id: cppcheck
|
||||||
|
|
@ -1,7 +1,6 @@
|
|||||||
/*
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 Magnus
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -20,17 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
|
||||||
#ifndef _CALC_H
|
|
||||||
#define _CALC_H
|
|
||||||
|
|
||||||
// Includes
|
|
||||||
#include "helper.h"
|
|
||||||
|
|
||||||
// Functions
|
|
||||||
double calculateGravity( double angle, double temp );
|
|
||||||
double gravityTemperatureCorrection( double gravity, double temp, char tempFormat, double calTemp = 20 );
|
|
||||||
|
|
||||||
#endif // _CALC_H
|
|
||||||
|
|
||||||
// EOF
|
|
153
README.md
@ -2,155 +2,4 @@
|
|||||||
|
|
||||||
This software can be used with iSpindle hardware and utilizes the same hardware configuration. No code has been reused from the iSpindle project.
|
This software can be used with iSpindle hardware and utilizes the same hardware configuration. No code has been reused from the iSpindle project.
|
||||||
|
|
||||||
I started this project out of curiosity for how a motion sensor is working and since I like to brew beer this was the result.
|
The documenation is now moved to https://mp-se.github.io/gravitymon/index.html
|
||||||
|
|
||||||
After 6 months of testing I believe this is working as planned. It give accurate readings same as the orginal iSpindel software.
|
|
||||||
|
|
||||||
Version history
|
|
||||||
* v0.4 First official version with 5+ brews on record.
|
|
||||||
|
|
||||||
Lower priority
|
|
||||||
* Add support for Plato in device (today it assumes that formula is in SG).
|
|
||||||
* Add support for converting between SG/Plato in device.
|
|
||||||
* Add support for Blynk as endpoint
|
|
||||||
* Add support for https connections (push) - [need to reduce memory usage for this to work, gets out of memory error]
|
|
||||||
* Add support for https web server (will require certificates to be created as part of build process)
|
|
||||||
* Add support for WifiManager Secure access, depends on support in library.
|
|
||||||
|
|
||||||
# Functionallity
|
|
||||||
|
|
||||||
I have made a few different design decision compared to the standard iSpindle software.
|
|
||||||
|
|
||||||
* The device operate in two modes, __always-on__ or __deep-sleep__. Always on can be triggered in two ways: Connected to charger and the power is over 4.1V or the device is lying flat (angle is approx 90 degrees).
|
|
||||||
|
|
||||||
* Configuration options have been moved to a web gui and is accesible when the device is in __always-on__ mode. The Wifi portal only need to be used for changing WIFI network.
|
|
||||||
|
|
||||||
* The software also has built in OTA support so new versions can be downloaded from a local webserver and checks are done during startup and the device is in __always-on__ mode.
|
|
||||||
|
|
||||||
* Temperature calibration has been added for the DS18B20 sensor so you can adjust the temperature sensor readings if there is a need. When the device is in __always-on__ mode the temperature will rise in the container so the value will increase and not reflect the temperature for the surronding the container.
|
|
||||||
|
|
||||||
* There is an option to automatically correct the gravity calculation based on the temperature. Useful if you are fermenting at lower temperatures. It's possible to build this into the normal gravity formula but this is an easier option. Just make sure that the calibration is done at 20°C.
|
|
||||||
|
|
||||||
* The software will read the motion sensor 50 times and use the average to calculate the angle. If the readings show that the device is moving it will wait a few seconds and retry the operation again to make sure that no invalid angles should be reported. If the device is unsuccesful to get a valid angle within 10s it will go to sleep for 60s and then retry again (TODO: This will be adjusted after more testing).
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
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__.
|
|
||||||
|
|
||||||
### Index page
|
|
||||||
|
|
||||||
http://gravmon.local/
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Device page
|
|
||||||
|
|
||||||
http://gravmon.local/device.htm
|
|
||||||
|
|
||||||
The device page shows the device settings and software versions.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Config page
|
|
||||||
|
|
||||||
http://gravmon.local/config.htm
|
|
||||||
|
|
||||||
* This page is divided into several categories of settings. The first one contains device settings, mDNS name, temperature format, sleep interval and gyro calibration data. The interval setting is the amount of time the device will be in sleep mode between readings (interval is in seconds). To simplify this you can also see the conversion to minutes / seconds next to the input field.
|
|
||||||
|
|
||||||
* The sleep interval can be set between 10 - 3600 seconds (60 minutes).
|
|
||||||
|
|
||||||
* Calibration needs to be done or the device will not work correctly. Place the device flat on a surface with gyro up and press the calibrate button when it's stable. If no calibration data exist the device will not enter sleep-mode.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
* The second section contains the push settings, two URL's for http posts, Brewfather and settings for InfluxDB v2.
|
|
||||||
|
|
||||||
### This is the format used for standard http posts.
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"name" : "gravmon", // mDNS name
|
|
||||||
"ID": "2E6753", // esp device id
|
|
||||||
"token" : "gravmon",
|
|
||||||
"interval": 900,
|
|
||||||
"temperature": 20.5, // C or F based on setting, adjusted value.
|
|
||||||
"temp-units": "C", // C or F based on setting
|
|
||||||
"gravity": 1.0050, // Raw or temperature corrected gravity (based on setting)
|
|
||||||
"corr-gravity": 1.0050, // Temperature corrected gravity
|
|
||||||
"angle": 45.34,
|
|
||||||
"battery": 3.67,
|
|
||||||
"rssi": -12,
|
|
||||||
"run-time": 230, // ms, Runtime for this reading, this is an additional field not part of the standard format
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### This is the format for Brewfather
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"name" : "gravmon", // mDNS name
|
|
||||||
"temp": 20.5,
|
|
||||||
"temp-unit": "C",
|
|
||||||
"battery": 3.67,
|
|
||||||
"gravity": 1.0050,
|
|
||||||
"gravity_unit": "G", // G = SG, Plato is not yet supported
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### This is the format for InfluxDB v2
|
|
||||||
|
|
||||||
```
|
|
||||||
measurement,host=<mdns>,device=<id>,temp-format=<C|F>,gravity-format=SG,gravity=1.0004,corr-gravity=1.0004,angle=45.45,temp=20.1,battery=3.96,rssi=-18
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
* The third section contains the gravity formula and also option for doing temperature compensation. The calibration formula uses two keywords, temp and tilt. Temperature is in the selected device format.
|
|
||||||
|
|
||||||
* Gravity formula is compatible with standard iSpindle formulas so any existing calculation option can be used. I use the tool fermentrack for controlling my fermentation and I use this tool for calculating gravity. The formula can handle two keywords, __tilt__ and __temp__. This is an example of a formula; __0.00145*tilt^3+0.1445*tilt^2+0.00179*tilt+0.9436__
|
|
||||||
|
|
||||||
* This is the formula used for temperature calibration (temp is in F). Cal = 20C.
|
|
||||||
```
|
|
||||||
gravity*((1.00130346-0.000134722124*temp+0.00000204052596*temp^2-0.00000000232820948*temp^3)/(1.00130346-0.000134722124*cal+0.00000204052596*cal^2-0.00000000232820948*cal^3))
|
|
||||||
```
|
|
||||||

|
|
||||||
|
|
||||||
* Hardware settings contain settings for temperature sensor adjustment, voltage factor (to calulate the battery level) and OTA URL.
|
|
||||||
|
|
||||||
* For the OTA to work, place the following files (version.json + firmware.bin) at the location that you pointed out in OTA URL. If the version number in the json file is newer than in the code the update will be done during startup.
|
|
||||||
|
|
||||||
Example; OTA URL (don't forget trailing dash), the name of the file should be firmware.bin
|
|
||||||
```
|
|
||||||
http://192.168.1.1/firmware/gravmon/
|
|
||||||
```
|
|
||||||
|
|
||||||
Contents version.json
|
|
||||||
```
|
|
||||||
{ "project":"gravmon", "version":"0.3.0" }
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
# Building a device
|
|
||||||
|
|
||||||
See the iSpindle documentation for building a device.
|
|
||||||
|
|
||||||
I've included my 3d sled that I use for my builds that allows for easy adjustment of the default angle. The stl files can be found under the stl directory.
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
# Compiling the software
|
|
||||||
|
|
||||||
I recommend that VSCODE with PlatformIO and Minfy extensions are used. Minify is used to reduce the size of the HTML files which are embedded into the firmware or uploaded to the file system. When using minify on a file, for example index.htm the output will be called index.min.htm. This is the file that will be used when buildning the image.
|
|
||||||
|
|
||||||
By default the html files are embedded but there are options to upload them to the file system to reduce the size of the image if the size becomes to large for OTA.
|
|
||||||
|
|
||||||
You can set the SSID and PWD as presets through platformio.ini by adding the settings to the following definitions:
|
|
||||||
```
|
|
||||||
-D USER_SSID=\""\"" // =\""myssid\""
|
|
||||||
-D USER_SSID_PWD=\""\"" // =\""mypwd\""
|
|
||||||
```
|
|
||||||
|
|
||||||
There are more options in teh platform.ini file that enable/disable additional functions for logging level, pushing performance data to InfluxDB and more. If i get the time I will add some documentation around these.
|
|
||||||
|
@ -1 +1 @@
|
|||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item active"><a class="nav-link" href="/about.htm">About</a></li></ul></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="row mb-3"><h3>Beer Gravity Monitor</h3>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 mb-3"><h3>MIT License</h3>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><hr class="my-4"></div><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021 Magnus Persson</div></body></html>
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item active"><a class="nav-link" href="/about.htm">About</a></li></ul></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="row mb-3"><h3>Beer Gravity Monitor</h3>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 mb-3"><h3>MIT License</h3>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><hr class="my-4"></div><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
52
bin/calibration.min.htm
Normal file
@ -1 +1 @@
|
|||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item active"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Current version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3" id="h-app-ver-new" hidden><div class="col-md-8 themed-grid-col bg-light">New version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver-new">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Host name:</div><div class="col-md-4 themed-grid-col bg-light" id="mdns">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Device ID:</div><div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div></div><hr class="my-4"></div><script type="text/javascript">function getConfig(){var n="/api/device";$("#spinner").show(),$.getJSON(n,function(n){console.log(n),$("#app-ver").text(n["app-ver"]+" (html 0.4.0)"),$("#mdns").text(n.mdns),$("#id").text(n.id)}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}window.onload=getConfig</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021 Magnus Persson</div></body></html>
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item active"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Current version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3" id="h-app-ver-new" hidden><div class="col-md-8 themed-grid-col bg-light">New version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver-new">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Host name:</div><div class="col-md-4 themed-grid-col bg-light" id="mdns">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Device ID:</div><div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div></div><hr class="my-4"></div><script type="text/javascript">function getConfig(){var n="/api/device";$("#spinner").show(),$.getJSON(n,function(n){console.log(n),$("#app-ver").text(n["app-ver"]+" (html 0.5.0)"),$("#mdns").text(n.mdns),$("#id").text(n.id)}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}window.onload=getConfig</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
BIN
bin/firmware.bin
@ -1 +1 @@
|
|||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item active"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="" id="id" hidden></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Gravity:</div><div class="col-md-4 themed-grid-col bg-light" id="gravity">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Temperature:</div><div class="col-md-4 themed-grid-col bg-light" id="temp">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Angle/Tilt:</div><div class="col-md-4 themed-grid-col bg-light" id="angle">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Battery:</div><div class="col-md-4 themed-grid-col bg-light" id="battery">Loading...</div></div><div class="row mb-3"><div class="col-md-12 px-md-5 themed-grid-col bg-light custom-control custom-checkbox"><input type="checkbox" class="custom-control-input" name="sleep-mode" id="sleep-mode" disabled> <label class="custom-control-label" for="sleep-mode">Do not enter sleep mode when floating (check this if you are collecting angles/tilt for calibration).</label></div></div><hr class="my-4"></div><script type="text/javascript">function getStatus(){var e="/api/status";$("#spinner").show(),$.getJSON(e,function(e){console.log(e),$("#id").text(e.id),$("#angle").text(e.angle),$("#gravity").text(e.gravity+" SG"),$("#battery").text(e.battery+" V"),"C"==e["temp-format"]?$("#temp").text(e["temp-c"]+" C"):$("#temp").text(e["temp-f"]+" F"),e["sleep-mode"]?$("#sleep-mode").attr("checked",!0):$("#sleep-mode").attr("checked",!1),$("#sleep-mode").removeAttr("disabled")}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}function start(){setInterval(getStatus,3e3)}window.onload=start,$("#sleep-mode").click(function(e){console.log("Blocking sleep mode = "+$("#sleep-mode").is(":checked")),$.ajax({type:"POST",url:"/api/status/sleepmode",data:{id:$("#id").text(),"sleep-mode":$("#sleep-mode").is(":checked")},success:function(e){},error:function(e){showError("Could not update sleep mode for device.")}})})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021 Magnus Persson</div></body></html>
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item active"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="" id="id" hidden></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Gravity:</div><div class="col-md-4 themed-grid-col bg-light" id="gravity">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Temperature:</div><div class="col-md-4 themed-grid-col bg-light" id="temp">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Angle/Tilt:</div><div class="col-md-4 themed-grid-col bg-light" id="angle">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Battery:</div><div class="col-md-4 themed-grid-col bg-light" id="battery">Loading...</div></div><div class="row mb-3"><div class="col-md-12 px-md-5 themed-grid-col bg-light custom-control custom-checkbox"><input type="checkbox" class="custom-control-input" name="sleep-mode" id="sleep-mode" disabled> <label class="custom-control-label" for="sleep-mode">Do not enter sleep mode when floating (check this if you are collecting angles/tilt for calibration).</label></div></div><hr class="my-4"></div><script type="text/javascript">function getStatus(){var e="/api/status";$("#spinner").show(),$.getJSON(e,function(e){console.log(e),$("#id").text(e.id),$("#angle").text(e.angle),$("#gravity").text(e.gravity+" SG"),$("#battery").text(e.battery+" V"),"C"==e["temp-format"]?$("#temp").text(e["temp-c"]+" C"):$("#temp").text(e["temp-f"]+" F"),e["sleep-mode"]?$("#sleep-mode").attr("checked",!0):$("#sleep-mode").attr("checked",!1),$("#sleep-mode").removeAttr("disabled")}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}function start(){setInterval(getStatus,3e3)}window.onload=start,$("#sleep-mode").click(function(e){console.log("Blocking sleep mode = "+$("#sleep-mode").is(":checked")),$.ajax({type:"POST",url:"/api/status/sleepmode",data:{id:$("#id").text(),"sleep-mode":$("#sleep-mode").is(":checked")},success:function(e){},error:function(e){showError("Could not update sleep mode for device.")}})})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
@ -1 +1 @@
|
|||||||
{ "project":"gravmon", "version":"0.4.0", "html": [ "index.min.htm", "device.min.htm", "config.min.htm", "about.min.htm" ] }
|
{ "project":"gravmon", "version":"0.5.0", "html": [ "index.min.htm", "device.min.htm", "config.min.htm", "calibration.min.htm", "about.min.htm" ] }
|
@ -1 +0,0 @@
|
|||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item active"><a class="nav-link" href="/about.htm">About</a></li></ul></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="row mb-3"><h3>Beer Gravity Monitor</h3>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 mb-3"><h3>MIT License</h3>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><hr class="my-4"></div><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021 Magnus Persson</div></body></html>
|
|
@ -1 +0,0 @@
|
|||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item active"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Current version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3" id="h-app-ver-new" hidden><div class="col-md-8 themed-grid-col bg-light">New version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver-new">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Host name:</div><div class="col-md-4 themed-grid-col bg-light" id="mdns">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Device ID:</div><div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div></div><hr class="my-4"></div><script type="text/javascript">function getConfig(){var n="/api/device";$("#spinner").show(),$.getJSON(n,function(n){console.log(n),$("#app-ver").text(n["app-ver"]+" (html 0.4.0)"),$("#mdns").text(n.mdns),$("#id").text(n.id)}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}window.onload=getConfig</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021 Magnus Persson</div></body></html>
|
|
@ -1 +0,0 @@
|
|||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item active"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="" id="id" hidden></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Gravity:</div><div class="col-md-4 themed-grid-col bg-light" id="gravity">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Temperature:</div><div class="col-md-4 themed-grid-col bg-light" id="temp">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Angle/Tilt:</div><div class="col-md-4 themed-grid-col bg-light" id="angle">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Battery:</div><div class="col-md-4 themed-grid-col bg-light" id="battery">Loading...</div></div><div class="row mb-3"><div class="col-md-12 px-md-5 themed-grid-col bg-light custom-control custom-checkbox"><input type="checkbox" class="custom-control-input" name="sleep-mode" id="sleep-mode" disabled> <label class="custom-control-label" for="sleep-mode">Do not enter sleep mode when floating (check this if you are collecting angles/tilt for calibration).</label></div></div><hr class="my-4"></div><script type="text/javascript">function getStatus(){var e="/api/status";$("#spinner").show(),$.getJSON(e,function(e){console.log(e),$("#id").text(e.id),$("#angle").text(e.angle),$("#gravity").text(e.gravity+" SG"),$("#battery").text(e.battery+" V"),"C"==e["temp-format"]?$("#temp").text(e["temp-c"]+" C"):$("#temp").text(e["temp-f"]+" F"),e["sleep-mode"]?$("#sleep-mode").attr("checked",!0):$("#sleep-mode").attr("checked",!1),$("#sleep-mode").removeAttr("disabled")}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}function start(){setInterval(getStatus,3e3)}window.onload=start,$("#sleep-mode").click(function(e){console.log("Blocking sleep mode = "+$("#sleep-mode").is(":checked")),$.ajax({type:"POST",url:"/api/status/sleepmode",data:{id:$("#id").text(),"sleep-mode":$("#sleep-mode").is(":checked")},success:function(e){},error:function(e){showError("Could not update sleep mode for device.")}})})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021 Magnus Persson</div></body></html>
|
|
@ -31,6 +31,9 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/config.htm">Configuration</a>
|
<a class="nav-link" href="/config.htm">Configuration</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/calibration.htm">Calibration</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item active">
|
<li class="nav-item active">
|
||||||
<a class="nav-link" href="/about.htm">About</a>
|
<a class="nav-link" href="/about.htm">About</a>
|
||||||
</li>
|
</li>
|
||||||
@ -73,6 +76,6 @@
|
|||||||
|
|
||||||
<!-- START FOOTER -->
|
<!-- START FOOTER -->
|
||||||
|
|
||||||
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021 Magnus Persson</div>
|
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
1
html/about.min.htm
Normal file
@ -0,0 +1 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item active"><a class="nav-link" href="/about.htm">About</a></li></ul></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="row mb-3"><h3>Beer Gravity Monitor</h3>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 mb-3"><h3>MIT License</h3>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><hr class="my-4"></div><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
346
html/calibration.htm
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<title>Beer Gravity Monitor</title>
|
||||||
|
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
</head>
|
||||||
|
<body class="py-4">
|
||||||
|
|
||||||
|
<!-- START MENU -->
|
||||||
|
|
||||||
|
<nav class="navbar navbar-expand-sm navbar-dark bg-primary">
|
||||||
|
|
||||||
|
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="collapse navbar-collapse" id="navbar">
|
||||||
|
<ul class="navbar-nav mr-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/device.htm">Device</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/config.htm">Configuration</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item active">
|
||||||
|
<a class="nav-link" href="/calibration.htm">Calibration</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/about.htm">About</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="spinner-border text-light" id="spinner" role="status"></div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- START MAIN INDEX -->
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<hr class="my-2">
|
||||||
|
|
||||||
|
<div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert">
|
||||||
|
<div id="alert-msg">...</div>
|
||||||
|
<button type="button" id="alert-btn" class="close" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
function showError( msg ) {
|
||||||
|
$('.alert').removeClass('alert-success').addClass('alert-danger').removeClass('d-none').addClass('show')
|
||||||
|
$('#alert-msg').text( msg );
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSuccess( msg ) {
|
||||||
|
$('.alert').addClass('alert-success').removeClass('alert-danger').removeClass('d-none').addClass('show')
|
||||||
|
$('#alert-msg').text( msg );
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#alert-btn").click(function(e){
|
||||||
|
$('.alert').addClass('d-none').removeClass('show')
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="accordion" id="accordion">
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header" id="headingOne">
|
||||||
|
<h2 class="mb-0">
|
||||||
|
<button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
|
||||||
|
Formula calculation
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
|
||||||
|
<div class="card-body">
|
||||||
|
<form action="/api/formula" method="post">
|
||||||
|
<input type="text" name="id" id="id" hidden>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
Here you can create your gravity formula by entering angles/tilt and the corresponding gravity (SG). These values
|
||||||
|
will be saved for future use. Angles with 0 (zero) will be skipped. The values below will be used to check the
|
||||||
|
formula and if the deviation is more than 1.5 SG then the forumla will be rejected. On the bottom of the page you can
|
||||||
|
see a graph over the entered values + values calcualated by the formula.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-sm-2 col-form-label">#:</label>
|
||||||
|
<label class="col-sm-4 col-form-label">Angle/Tilt:</label>
|
||||||
|
<label class="col-sm-4 col-form-label">Gravity (SG):</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="angle1" class="col-sm-2 col-form-label">1.</label>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input type="number" min="0" max="90" step="0.001" class="form-control" name="a1" id="a1">
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input type="number" min="1" max="2" step="0.0001" class="form-control" name="g1" id="g1">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="angle2" class="col-sm-2 col-form-label">2.</label>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input type="number" min="0" max="90" step="0.001" class="form-control" name="a2" id="a2">
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input type="number" min="1" max="2" step="0.0001" class="form-control" name="g2" id="g2">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="angle3" class="col-sm-2 col-form-label">3.</label>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input type="number" min="0" max="90" step="0.001" class="form-control" name="a3" id="a3">
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input type="number" min="1" max="2" step="0.0001" class="form-control" name="g3" id="g3">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="angle4" class="col-sm-2 col-form-label">4.</label>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input type="number" min="0" max="90" step="0.001" class="form-control" name="a4" id="a4">
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input type="number" min="1" max="2" step="0.0001" class="form-control" name="g4" id="g4">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="angle5" class="col-sm-2 col-form-label">5.</label>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input type="number" min="0" max="90" step="0.001" class="form-control" name="a5" id="a5">
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input type="number" min="1" max="2" step="0.0001" class="form-control" name="g5" id="g5">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-sm-8 offset-sm-0">
|
||||||
|
<button type="submit" class="btn btn-primary" id="calculate-btn">Save & Calculate</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="calculate-btn" class="col-sm-2 col-form-label">Current angle: </label>
|
||||||
|
<label for="calculate-btn" class="col-sm-2 col-form-label" id="angle"></label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="calculate-btn" class="col-sm-2 col-form-label">Formula: </label>
|
||||||
|
<label for="calculate-btn" class="col-sm-8 col-form-label" id="formula">Loading...</label>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr class="my-4">
|
||||||
|
<div>
|
||||||
|
<canvas id="gravityChart"></canvas>
|
||||||
|
</div>
|
||||||
|
<hr class="my-4">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var chartDataForm = [];
|
||||||
|
var chartDataCalc = [];
|
||||||
|
|
||||||
|
const dataSetChart = {
|
||||||
|
datasets: [{
|
||||||
|
label: 'Raw data',
|
||||||
|
borderColor: 'blue',
|
||||||
|
backgroundColor: 'blue',
|
||||||
|
data: chartDataForm
|
||||||
|
}, {
|
||||||
|
label: 'Calculated',
|
||||||
|
borderColor: 'green',
|
||||||
|
backgroundColor: 'green',
|
||||||
|
data: chartDataCalc
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
const configChart = {
|
||||||
|
type: 'line',
|
||||||
|
data: dataSetChart,
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
interaction: {
|
||||||
|
intersect: false,
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
display: true,
|
||||||
|
type: 'linear',
|
||||||
|
grace: '5%',
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Angle/Tilt'
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
crossAlign: 'far'
|
||||||
|
},
|
||||||
|
suggestedMin: 25
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
display: true,
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Gravity'
|
||||||
|
},
|
||||||
|
suggestedMin: 1.000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var myChart = 0;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
g1.onchange = setFourNumberDecimal
|
||||||
|
g2.onchange = setFourNumberDecimal
|
||||||
|
g3.onchange = setFourNumberDecimal
|
||||||
|
g4.onchange = setFourNumberDecimal
|
||||||
|
g5.onchange = setFourNumberDecimal
|
||||||
|
|
||||||
|
a1.onchange = setTwoNumberDecimal
|
||||||
|
a2.onchange = setTwoNumberDecimal
|
||||||
|
a3.onchange = setTwoNumberDecimal
|
||||||
|
a4.onchange = setTwoNumberDecimal
|
||||||
|
a5.onchange = setTwoNumberDecimal
|
||||||
|
|
||||||
|
window.onload = getConfig;
|
||||||
|
setButtonDisabled( true );
|
||||||
|
|
||||||
|
function setTwoNumberDecimal(event) {
|
||||||
|
this.value = parseFloat(this.value).toFixed(2);
|
||||||
|
populateChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFourNumberDecimal(event) {
|
||||||
|
this.value = parseFloat(this.value).toFixed(4);
|
||||||
|
populateChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateChartForm(a, g) {
|
||||||
|
if( a != 0)
|
||||||
|
chartDataForm.push( { x: parseFloat(a), y: parseFloat(g) });
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateChartCalc(a, g) {
|
||||||
|
chartDataCalc.push( { x: parseFloat(a), y: parseFloat(g) });
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateChart() {
|
||||||
|
|
||||||
|
chartDataCalc.length = 0
|
||||||
|
|
||||||
|
for( i = 25.0; i<80.0; i+=5.0) {
|
||||||
|
var formula = $("#formula").text();
|
||||||
|
var angle = i.toString();
|
||||||
|
formula=formula.replaceAll( "tilt^3", angle+"*"+angle+"*"+angle );
|
||||||
|
formula=formula.replaceAll( "tilt^2", angle+"*"+angle );
|
||||||
|
formula=formula.replaceAll( "tilt", angle );
|
||||||
|
var g = eval( formula );
|
||||||
|
populateChartCalc( i, g );
|
||||||
|
}
|
||||||
|
|
||||||
|
chartDataForm.length = 0
|
||||||
|
populateChartForm( $("#a1").val(), $("#g1").val() );
|
||||||
|
populateChartForm( $("#a2").val(), $("#g2").val() );
|
||||||
|
populateChartForm( $("#a3").val(), $("#g3").val() );
|
||||||
|
populateChartForm( $("#a4").val(), $("#g4").val() );
|
||||||
|
populateChartForm( $("#a5").val(), $("#g5").val() );
|
||||||
|
|
||||||
|
if( myChart )
|
||||||
|
myChart.destroy();
|
||||||
|
|
||||||
|
myChart = new Chart(
|
||||||
|
document.getElementById('gravityChart'),
|
||||||
|
configChart
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setButtonDisabled( b ) {
|
||||||
|
$("#calculate-btn").prop("disabled", b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the configuration values from the API
|
||||||
|
function getConfig() {
|
||||||
|
setButtonDisabled( true );
|
||||||
|
|
||||||
|
var url = "/api/formula";
|
||||||
|
//var url = "/test/formula.json";
|
||||||
|
$('#spinner').show();
|
||||||
|
$.getJSON(url, function (cfg) {
|
||||||
|
console.log( cfg );
|
||||||
|
|
||||||
|
$("#id").val(cfg["id"]);
|
||||||
|
$("#angle").text(cfg["angle"]);
|
||||||
|
$("#formula").text(cfg["gravity-formula"]);
|
||||||
|
|
||||||
|
$("#a1").val( parseFloat(cfg["a1"]).toFixed(2) );
|
||||||
|
$("#a2").val( parseFloat(cfg["a2"]).toFixed(2) );
|
||||||
|
$("#a3").val( parseFloat(cfg["a3"]).toFixed(2) );
|
||||||
|
$("#a4").val( parseFloat(cfg["a4"]).toFixed(2) );
|
||||||
|
$("#a5").val( parseFloat(cfg["a5"]).toFixed(2) );
|
||||||
|
|
||||||
|
$("#g1").val( parseFloat(cfg["g1"]).toFixed(4) );
|
||||||
|
$("#g2").val( parseFloat(cfg["g2"]).toFixed(4) );
|
||||||
|
$("#g3").val( parseFloat(cfg["g3"]).toFixed(4) );
|
||||||
|
$("#g4").val( parseFloat(cfg["g4"]).toFixed(4) );
|
||||||
|
$("#g5").val( parseFloat(cfg["g5"]).toFixed(4) );
|
||||||
|
|
||||||
|
populateChart();
|
||||||
|
})
|
||||||
|
.fail(function () {
|
||||||
|
showError('Unable to get data from the device.');
|
||||||
|
})
|
||||||
|
.always(function() {
|
||||||
|
$('#spinner').hide();
|
||||||
|
setButtonDisabled( false );
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- START FOOTER -->
|
||||||
|
|
||||||
|
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
52
html/calibration.min.htm
Normal file
@ -31,6 +31,9 @@
|
|||||||
<li class="nav-item active">
|
<li class="nav-item active">
|
||||||
<a class="nav-link" href="/config.htm">Configuration</a>
|
<a class="nav-link" href="/config.htm">Configuration</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/calibration.htm">Calibration</a>
|
||||||
|
</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>
|
||||||
</li>
|
</li>
|
||||||
@ -378,6 +381,6 @@
|
|||||||
|
|
||||||
<!-- START FOOTER -->
|
<!-- START FOOTER -->
|
||||||
|
|
||||||
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021 Magnus Persson</div>
|
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
1
html/config.min.htm
Normal file
@ -32,6 +32,9 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/config.htm">Configuration</a>
|
<a class="nav-link" href="/config.htm">Configuration</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/calibration.htm">Calibration</a>
|
||||||
|
</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>
|
||||||
</li>
|
</li>
|
||||||
@ -97,7 +100,7 @@
|
|||||||
$('#spinner').show();
|
$('#spinner').show();
|
||||||
$.getJSON(url, function (cfg) {
|
$.getJSON(url, function (cfg) {
|
||||||
console.log( cfg );
|
console.log( cfg );
|
||||||
$("#app-ver").text(cfg["app-ver"] + " (html 0.4.0)");
|
$("#app-ver").text(cfg["app-ver"] + " (html 0.5.0)");
|
||||||
$("#mdns").text(cfg["mdns"]);
|
$("#mdns").text(cfg["mdns"]);
|
||||||
$("#id").text(cfg["id"]);
|
$("#id").text(cfg["id"]);
|
||||||
})
|
})
|
||||||
@ -112,6 +115,6 @@
|
|||||||
|
|
||||||
<!-- START FOOTER -->
|
<!-- START FOOTER -->
|
||||||
|
|
||||||
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021 Magnus Persson</div>
|
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
1
html/device.min.htm
Normal file
@ -0,0 +1 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item active"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Current version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3" id="h-app-ver-new" hidden><div class="col-md-8 themed-grid-col bg-light">New version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver-new">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Host name:</div><div class="col-md-4 themed-grid-col bg-light" id="mdns">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Device ID:</div><div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div></div><hr class="my-4"></div><script type="text/javascript">function getConfig(){var n="/api/device";$("#spinner").show(),$.getJSON(n,function(n){console.log(n),$("#app-ver").text(n["app-ver"]+" (html 0.5.0)"),$("#mdns").text(n.mdns),$("#id").text(n.id)}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}window.onload=getConfig</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
@ -31,6 +31,9 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/config.htm">Configuration</a>
|
<a class="nav-link" href="/config.htm">Configuration</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/calibration.htm">Calibration</a>
|
||||||
|
</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>
|
||||||
</li>
|
</li>
|
||||||
@ -148,6 +151,6 @@
|
|||||||
|
|
||||||
<!-- START FOOTER -->
|
<!-- START FOOTER -->
|
||||||
|
|
||||||
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021 Magnus Persson</div>
|
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
1
html/index.min.htm
Normal file
@ -0,0 +1 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item active"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="" id="id" hidden></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Gravity:</div><div class="col-md-4 themed-grid-col bg-light" id="gravity">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Temperature:</div><div class="col-md-4 themed-grid-col bg-light" id="temp">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Angle/Tilt:</div><div class="col-md-4 themed-grid-col bg-light" id="angle">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Battery:</div><div class="col-md-4 themed-grid-col bg-light" id="battery">Loading...</div></div><div class="row mb-3"><div class="col-md-12 px-md-5 themed-grid-col bg-light custom-control custom-checkbox"><input type="checkbox" class="custom-control-input" name="sleep-mode" id="sleep-mode" disabled> <label class="custom-control-label" for="sleep-mode">Do not enter sleep mode when floating (check this if you are collecting angles/tilt for calibration).</label></div></div><hr class="my-4"></div><script type="text/javascript">function getStatus(){var e="/api/status";$("#spinner").show(),$.getJSON(e,function(e){console.log(e),$("#id").text(e.id),$("#angle").text(e.angle),$("#gravity").text(e.gravity+" SG"),$("#battery").text(e.battery+" V"),"C"==e["temp-format"]?$("#temp").text(e["temp-c"]+" C"):$("#temp").text(e["temp-f"]+" F"),e["sleep-mode"]?$("#sleep-mode").attr("checked",!0):$("#sleep-mode").attr("checked",!1),$("#sleep-mode").removeAttr("disabled")}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}function start(){setInterval(getStatus,3e3)}window.onload=start,$("#sleep-mode").click(function(e){console.log("Blocking sleep mode = "+$("#sleep-mode").is(":checked")),$.ajax({type:"POST",url:"/api/status/sleepmode",data:{id:$("#id").text(),"sleep-mode":$("#sleep-mode").is(":checked")},success:function(e){},error:function(e){showError("Could not update sleep mode for device.")}})})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
@ -74,6 +74,10 @@
|
|||||||
<div class="col-md-2 themed-grid-col bg-light">config.min.htm</div>
|
<div class="col-md-2 themed-grid-col bg-light">config.min.htm</div>
|
||||||
<div class="col-md-6 themed-grid-col bg-light" id="config">Checking...</div>
|
<div class="col-md-6 themed-grid-col bg-light" id="config">Checking...</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-2 themed-grid-col bg-light">calibration.min.htm</div>
|
||||||
|
<div class="col-md-6 themed-grid-col bg-light" id="calibration">Checking...</div>
|
||||||
|
</div>
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-md-2 themed-grid-col bg-light">about.min.htm</div>
|
<div class="col-md-2 themed-grid-col bg-light">about.min.htm</div>
|
||||||
<div class="col-md-6 themed-grid-col bg-light" id="about">Checking...</div>
|
<div class="col-md-6 themed-grid-col bg-light" id="about">Checking...</div>
|
||||||
@ -118,11 +122,16 @@ function getUpload() {
|
|||||||
else
|
else
|
||||||
$("#device").text("File is missing.");
|
$("#device").text("File is missing.");
|
||||||
|
|
||||||
if( cfg["config"] )
|
if( cfg["config"] )
|
||||||
$("#config").text("Completed.");
|
$("#config").text("Completed.");
|
||||||
else
|
else
|
||||||
$("#config").text("File is missing.");
|
$("#config").text("File is missing.");
|
||||||
|
|
||||||
|
if( cfg["calibration"] )
|
||||||
|
$("#calibration").text("Completed.");
|
||||||
|
else
|
||||||
|
$("#calibration").text("File is missing.");
|
||||||
|
|
||||||
if( cfg["about"] )
|
if( cfg["about"] )
|
||||||
$("#about").text("Completed.");
|
$("#about").text("Completed.");
|
||||||
else
|
else
|
||||||
@ -141,6 +150,6 @@ function getUpload() {
|
|||||||
|
|
||||||
<!-- START FOOTER -->
|
<!-- START FOOTER -->
|
||||||
|
|
||||||
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021 Magnus Persson</div>
|
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -1 +1 @@
|
|||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/upload.htm">Beer Gravity Monitor - Missing html files</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">The listed files below needs to be uploaded to the FileSystem in order for the GUI to work. You can also flash the LittleFS filesystem but in that case you will loose your device settings. An OTA upgrade will automatically download the files if they are found in the same location as the firmware.bin. This page is a fallback option.</div><div class="col-md-8 themed-grid-col bg-light"><br><strong>Once all the files are confirmed, please reboot the device for normal operation.</strong></div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">index.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="index">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">device.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="device">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">config.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="config">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">about.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="about">Checking...</div></div><div class="row mb-3"><form action="/api/upload" method="post" enctype="multipart/form-data"><div class="col-md-8 custom-file"><input type="file" class="custom-file-input" name="name" id="name"> <label class="custom-file-label" for="name">Choose file</label></div><button type="submit" class="btn btn-primary" id="upload-btn" value="upload">Upload</button></form></div><hr class="my-4"></div><script type="text/javascript">function getUpload(){var e="/api/upload";$("#spinner").show(),$.getJSON(e,function(e){console.log(e),e.index?$("#index").text("Completed."):$("#index").text("File is missing."),e.device?$("#device").text("Completed."):$("#device").text("File is missing."),e.config?$("#config").text("Completed."):$("#config").text("File is missing."),e.about?$("#about").text("Completed."):$("#about").text("File is missing.")}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}window.onload=getUpload,$(".custom-file-input").on("change",function(){var e=$(this).val().split("\\").pop();$(this).siblings(".custom-file-label").addClass("selected").html(e)})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021 Magnus Persson</div></body></html>
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/upload.htm">Beer Gravity Monitor - Missing html files</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">The listed files below needs to be uploaded to the FileSystem in order for the GUI to work. You can also flash the LittleFS filesystem but in that case you will loose your device settings. An OTA upgrade will automatically download the files if they are found in the same location as the firmware.bin. This page is a fallback option.</div><div class="col-md-8 themed-grid-col bg-light"><br><strong>Once all the files are confirmed, please reboot the device for normal operation.</strong></div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">index.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="index">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">device.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="device">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">config.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="config">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">calibration.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="calibration">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">about.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="about">Checking...</div></div><div class="row mb-3"><form action="/api/upload" method="post" enctype="multipart/form-data"><div class="col-md-8 custom-file"><input type="file" class="custom-file-input" name="name" id="name"> <label class="custom-file-label" for="name">Choose file</label></div><button type="submit" class="btn btn-primary" id="upload-btn" value="upload">Upload</button></form></div><hr class="my-4"></div><script type="text/javascript">function getUpload(){var i="/api/upload";$("#spinner").show(),$.getJSON(i,function(i){console.log(i),i.index?$("#index").text("Completed."):$("#index").text("File is missing."),i.device?$("#device").text("Completed."):$("#device").text("File is missing."),i.config?$("#config").text("Completed."):$("#config").text("File is missing."),i.calibration?$("#calibration").text("Completed."):$("#calibration").text("File is missing."),i.about?$("#about").text("Completed."):$("#about").text("File is missing.")}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}window.onload=getUpload,$(".custom-file-input").on("change",function(){var i=$(this).val().split("\\").pop();$(this).siblings(".custom-file-label").addClass("selected").html(i)})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
BIN
img/config1.png
Before Width: | Height: | Size: 13 KiB |
BIN
img/config2.png
Before Width: | Height: | Size: 19 KiB |
BIN
img/config3.png
Before Width: | Height: | Size: 7.7 KiB |
BIN
img/config4.png
Before Width: | Height: | Size: 11 KiB |
BIN
img/device.png
Before Width: | Height: | Size: 16 KiB |
BIN
img/index.png
Before Width: | Height: | Size: 20 KiB |
@ -3,6 +3,9 @@
|
|||||||
// 2013-06-05 by Jeff Rowberg <jeff@rowberg.net>
|
// 2013-06-05 by Jeff Rowberg <jeff@rowberg.net>
|
||||||
//
|
//
|
||||||
// Changelog:
|
// Changelog:
|
||||||
|
// 2021-09-28 - allow custom Wire object as transaction function argument
|
||||||
|
// 2020-01-20 - hardija : complete support for Teensy 3.x
|
||||||
|
// 2015-10-30 - simondlevy : support i2c_t3 for Teensy3.1
|
||||||
// 2013-05-06 - add Francesco Ferrara's Fastwire v0.24 implementation with small modifications
|
// 2013-05-06 - add Francesco Ferrara's Fastwire v0.24 implementation with small modifications
|
||||||
// 2013-05-05 - fix issue with writing bit values to words (Sasquatch/Farzanegan)
|
// 2013-05-05 - fix issue with writing bit values to words (Sasquatch/Farzanegan)
|
||||||
// 2012-06-09 - fix major issue with reading > 32 bytes at a time with Arduino Wire
|
// 2012-06-09 - fix major issue with reading > 32 bytes at a time with Arduino Wire
|
||||||
@ -87,11 +90,6 @@ THE SOFTWARE.
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef BUFFER_LENGTH
|
|
||||||
// band-aid fix for platforms without Wire-defined BUFFER_LENGTH (removed from some official implementations)
|
|
||||||
#define BUFFER_LENGTH 32
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/** Default constructor.
|
/** Default constructor.
|
||||||
*/
|
*/
|
||||||
I2Cdev::I2Cdev() {
|
I2Cdev::I2Cdev() {
|
||||||
@ -105,9 +103,9 @@ I2Cdev::I2Cdev() {
|
|||||||
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
|
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
|
||||||
* @return Status of read operation (true = success)
|
* @return Status of read operation (true = success)
|
||||||
*/
|
*/
|
||||||
int8_t I2Cdev::readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data, uint16_t timeout) {
|
int8_t I2Cdev::readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data, uint16_t timeout, void *wireObj) {
|
||||||
uint8_t b;
|
uint8_t b;
|
||||||
uint8_t count = readByte(devAddr, regAddr, &b, timeout);
|
uint8_t count = readByte(devAddr, regAddr, &b, timeout, wireObj);
|
||||||
*data = b & (1 << bitNum);
|
*data = b & (1 << bitNum);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
@ -120,9 +118,9 @@ int8_t I2Cdev::readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t
|
|||||||
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
|
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
|
||||||
* @return Status of read operation (true = success)
|
* @return Status of read operation (true = success)
|
||||||
*/
|
*/
|
||||||
int8_t I2Cdev::readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t *data, uint16_t timeout) {
|
int8_t I2Cdev::readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t *data, uint16_t timeout, void *wireObj) {
|
||||||
uint16_t b;
|
uint16_t b;
|
||||||
uint8_t count = readWord(devAddr, regAddr, &b, timeout);
|
uint8_t count = readWord(devAddr, regAddr, &b, timeout, wireObj);
|
||||||
*data = b & (1 << bitNum);
|
*data = b & (1 << bitNum);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
@ -136,14 +134,14 @@ int8_t I2Cdev::readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16
|
|||||||
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
|
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
|
||||||
* @return Status of read operation (true = success)
|
* @return Status of read operation (true = success)
|
||||||
*/
|
*/
|
||||||
int8_t I2Cdev::readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data, uint16_t timeout) {
|
int8_t I2Cdev::readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data, uint16_t timeout, void *wireObj) {
|
||||||
// 01101001 read byte
|
// 01101001 read byte
|
||||||
// 76543210 bit numbers
|
// 76543210 bit numbers
|
||||||
// xxx args: bitStart=4, length=3
|
// xxx args: bitStart=4, length=3
|
||||||
// 010 masked
|
// 010 masked
|
||||||
// -> 010 shifted
|
// -> 010 shifted
|
||||||
uint8_t count, b;
|
uint8_t count, b;
|
||||||
if ((count = readByte(devAddr, regAddr, &b, timeout)) != 0) {
|
if ((count = readByte(devAddr, regAddr, &b, timeout, wireObj)) != 0) {
|
||||||
uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1);
|
uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1);
|
||||||
b &= mask;
|
b &= mask;
|
||||||
b >>= (bitStart - length + 1);
|
b >>= (bitStart - length + 1);
|
||||||
@ -161,7 +159,7 @@ int8_t I2Cdev::readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint
|
|||||||
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
|
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
|
||||||
* @return Status of read operation (1 = success, 0 = failure, -1 = timeout)
|
* @return Status of read operation (1 = success, 0 = failure, -1 = timeout)
|
||||||
*/
|
*/
|
||||||
int8_t I2Cdev::readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t *data, uint16_t timeout) {
|
int8_t I2Cdev::readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t *data, uint16_t timeout, void *wireObj) {
|
||||||
// 1101011001101001 read byte
|
// 1101011001101001 read byte
|
||||||
// fedcba9876543210 bit numbers
|
// fedcba9876543210 bit numbers
|
||||||
// xxx args: bitStart=12, length=3
|
// xxx args: bitStart=12, length=3
|
||||||
@ -169,7 +167,7 @@ int8_t I2Cdev::readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uin
|
|||||||
// -> 010 shifted
|
// -> 010 shifted
|
||||||
uint8_t count;
|
uint8_t count;
|
||||||
uint16_t w;
|
uint16_t w;
|
||||||
if ((count = readWord(devAddr, regAddr, &w, timeout)) != 0) {
|
if ((count = readWord(devAddr, regAddr, &w, timeout, wireObj)) != 0) {
|
||||||
uint16_t mask = ((1 << length) - 1) << (bitStart - length + 1);
|
uint16_t mask = ((1 << length) - 1) << (bitStart - length + 1);
|
||||||
w &= mask;
|
w &= mask;
|
||||||
w >>= (bitStart - length + 1);
|
w >>= (bitStart - length + 1);
|
||||||
@ -185,8 +183,8 @@ int8_t I2Cdev::readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uin
|
|||||||
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
|
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
|
||||||
* @return Status of read operation (true = success)
|
* @return Status of read operation (true = success)
|
||||||
*/
|
*/
|
||||||
int8_t I2Cdev::readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_t timeout) {
|
int8_t I2Cdev::readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_t timeout, void *wireObj) {
|
||||||
return readBytes(devAddr, regAddr, 1, data, timeout);
|
return readBytes(devAddr, regAddr, 1, data, timeout, wireObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Read single word from a 16-bit device register.
|
/** Read single word from a 16-bit device register.
|
||||||
@ -196,8 +194,8 @@ int8_t I2Cdev::readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_
|
|||||||
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
|
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
|
||||||
* @return Status of read operation (true = success)
|
* @return Status of read operation (true = success)
|
||||||
*/
|
*/
|
||||||
int8_t I2Cdev::readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16_t timeout) {
|
int8_t I2Cdev::readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16_t timeout, void *wireObj) {
|
||||||
return readWords(devAddr, regAddr, 1, data, timeout);
|
return readWords(devAddr, regAddr, 1, data, timeout, wireObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Read multiple bytes from an 8-bit device register.
|
/** Read multiple bytes from an 8-bit device register.
|
||||||
@ -208,7 +206,7 @@ int8_t I2Cdev::readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16
|
|||||||
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
|
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
|
||||||
* @return Number of bytes read (-1 indicates failure)
|
* @return Number of bytes read (-1 indicates failure)
|
||||||
*/
|
*/
|
||||||
int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout) {
|
int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout, void *wireObj) {
|
||||||
#ifdef I2CDEV_SERIAL_DEBUG
|
#ifdef I2CDEV_SERIAL_DEBUG
|
||||||
Serial.print("I2C (0x");
|
Serial.print("I2C (0x");
|
||||||
Serial.print(devAddr, HEX);
|
Serial.print(devAddr, HEX);
|
||||||
@ -223,70 +221,72 @@ int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8
|
|||||||
uint32_t t1 = millis();
|
uint32_t t1 = millis();
|
||||||
|
|
||||||
#if (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE)
|
#if (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE)
|
||||||
|
TwoWire *useWire = &Wire;
|
||||||
|
if (wireObj) useWire = (TwoWire *)wireObj;
|
||||||
|
|
||||||
#if (ARDUINO < 100)
|
#if (ARDUINO < 100)
|
||||||
// Arduino v00xx (before v1.0), Wire library
|
// Arduino v00xx (before v1.0), Wire library
|
||||||
|
|
||||||
// I2C/TWI subsystem uses internal buffer that breaks with large data requests
|
// I2C/TWI subsystem uses internal buffer that breaks with large data requests
|
||||||
// so if user requests more than BUFFER_LENGTH bytes, we have to do it in
|
// so if user requests more than I2CDEVLIB_WIRE_BUFFER_LENGTH bytes, we have to do it in
|
||||||
// smaller chunks instead of all at once
|
// smaller chunks instead of all at once
|
||||||
for (uint8_t k = 0; k < length; k += min((int)length, BUFFER_LENGTH)) {
|
for (int k = 0; k < length; k += min((int)length, I2CDEVLIB_WIRE_BUFFER_LENGTH)) {
|
||||||
Wire.beginTransmission(devAddr);
|
useWire->beginTransmission(devAddr);
|
||||||
Wire.send(regAddr);
|
useWire->send(regAddr);
|
||||||
Wire.endTransmission();
|
useWire->endTransmission();
|
||||||
Wire.beginTransmission(devAddr);
|
useWire->beginTransmission(devAddr);
|
||||||
Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH));
|
useWire->requestFrom((uint8_t)devAddr, (uint8_t)min((int)length - k, I2CDEVLIB_WIRE_BUFFER_LENGTH));
|
||||||
|
|
||||||
for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) {
|
for (; useWire->available() && (timeout == 0 || millis() - t1 < timeout); count++) {
|
||||||
data[count] = Wire.receive();
|
data[count] = useWire->receive();
|
||||||
#ifdef I2CDEV_SERIAL_DEBUG
|
#ifdef I2CDEV_SERIAL_DEBUG
|
||||||
Serial.print(data[count], HEX);
|
Serial.print(data[count], HEX);
|
||||||
if (count + 1 < length) Serial.print(" ");
|
if (count + 1 < length) Serial.print(" ");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Wire.endTransmission();
|
useWire->endTransmission();
|
||||||
}
|
}
|
||||||
#elif (ARDUINO == 100)
|
#elif (ARDUINO == 100)
|
||||||
// Arduino v1.0.0, Wire library
|
// Arduino v1.0.0, Wire library
|
||||||
// Adds standardized write() and read() stream methods instead of send() and receive()
|
// Adds standardized write() and read() stream methods instead of send() and receive()
|
||||||
|
|
||||||
// I2C/TWI subsystem uses internal buffer that breaks with large data requests
|
// I2C/TWI subsystem uses internal buffer that breaks with large data requests
|
||||||
// so if user requests more than BUFFER_LENGTH bytes, we have to do it in
|
// so if user requests more than I2CDEVLIB_WIRE_BUFFER_LENGTH bytes, we have to do it in
|
||||||
// smaller chunks instead of all at once
|
// smaller chunks instead of all at once
|
||||||
for (uint8_t k = 0; k < length; k += min((int)length, BUFFER_LENGTH)) {
|
for (int k = 0; k < length; k += min((int)length, I2CDEVLIB_WIRE_BUFFER_LENGTH)) {
|
||||||
Wire.beginTransmission(devAddr);
|
useWire->beginTransmission(devAddr);
|
||||||
Wire.write(regAddr);
|
useWire->write(regAddr);
|
||||||
Wire.endTransmission();
|
useWire->endTransmission();
|
||||||
Wire.beginTransmission(devAddr);
|
useWire->beginTransmission(devAddr);
|
||||||
Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH));
|
useWire->requestFrom((uint8_t)devAddr, (uint8_t)min((int)length - k, I2CDEVLIB_WIRE_BUFFER_LENGTH));
|
||||||
|
|
||||||
for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) {
|
for (; useWire->available() && (timeout == 0 || millis() - t1 < timeout); count++) {
|
||||||
data[count] = Wire.read();
|
data[count] = useWire->read();
|
||||||
#ifdef I2CDEV_SERIAL_DEBUG
|
#ifdef I2CDEV_SERIAL_DEBUG
|
||||||
Serial.print(data[count], HEX);
|
Serial.print(data[count], HEX);
|
||||||
if (count + 1 < length) Serial.print(" ");
|
if (count + 1 < length) Serial.print(" ");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Wire.endTransmission();
|
useWire->endTransmission();
|
||||||
}
|
}
|
||||||
#elif (ARDUINO > 100)
|
#elif (ARDUINO > 100)
|
||||||
// Arduino v1.0.1+, Wire library
|
// Arduino v1.0.1+, Wire library
|
||||||
// Adds official support for repeated start condition, yay!
|
// Adds official support for repeated start condition, yay!
|
||||||
|
|
||||||
// I2C/TWI subsystem uses internal buffer that breaks with large data requests
|
// I2C/TWI subsystem uses internal buffer that breaks with large data requests
|
||||||
// so if user requests more than BUFFER_LENGTH bytes, we have to do it in
|
// so if user requests more than I2CDEVLIB_WIRE_BUFFER_LENGTH bytes, we have to do it in
|
||||||
// smaller chunks instead of all at once
|
// smaller chunks instead of all at once
|
||||||
for (uint8_t k = 0; k < length; k += min((int)length, BUFFER_LENGTH)) {
|
for (int k = 0; k < length; k += min((int)length, I2CDEVLIB_WIRE_BUFFER_LENGTH)) {
|
||||||
Wire.beginTransmission(devAddr);
|
useWire->beginTransmission(devAddr);
|
||||||
Wire.write(regAddr);
|
useWire->write(regAddr);
|
||||||
Wire.endTransmission();
|
useWire->endTransmission();
|
||||||
Wire.beginTransmission(devAddr);
|
useWire->beginTransmission(devAddr);
|
||||||
Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH));
|
useWire->requestFrom((uint8_t)devAddr, (uint8_t)min((int)length - k, I2CDEVLIB_WIRE_BUFFER_LENGTH));
|
||||||
|
|
||||||
for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) {
|
for (; useWire->available() && (timeout == 0 || millis() - t1 < timeout); count++) {
|
||||||
data[count] = Wire.read();
|
data[count] = useWire->read();
|
||||||
#ifdef I2CDEV_SERIAL_DEBUG
|
#ifdef I2CDEV_SERIAL_DEBUG
|
||||||
Serial.print(data[count], HEX);
|
Serial.print(data[count], HEX);
|
||||||
if (count + 1 < length) Serial.print(" ");
|
if (count + 1 < length) Serial.print(" ");
|
||||||
@ -328,7 +328,7 @@ int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8
|
|||||||
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
|
* @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
|
||||||
* @return Number of words read (-1 indicates failure)
|
* @return Number of words read (-1 indicates failure)
|
||||||
*/
|
*/
|
||||||
int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, uint16_t timeout) {
|
int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, uint16_t timeout, void *wireObj) {
|
||||||
#ifdef I2CDEV_SERIAL_DEBUG
|
#ifdef I2CDEV_SERIAL_DEBUG
|
||||||
Serial.print("I2C (0x");
|
Serial.print("I2C (0x");
|
||||||
Serial.print(devAddr, HEX);
|
Serial.print(devAddr, HEX);
|
||||||
@ -343,28 +343,30 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1
|
|||||||
uint32_t t1 = millis();
|
uint32_t t1 = millis();
|
||||||
|
|
||||||
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE
|
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE
|
||||||
|
TwoWire *useWire = &Wire;
|
||||||
|
if (wireObj) useWire = (TwoWire *)wireObj;
|
||||||
|
|
||||||
#if (ARDUINO < 100)
|
#if (ARDUINO < 100)
|
||||||
// Arduino v00xx (before v1.0), Wire library
|
// Arduino v00xx (before v1.0), Wire library
|
||||||
|
|
||||||
// I2C/TWI subsystem uses internal buffer that breaks with large data requests
|
// I2C/TWI subsystem uses internal buffer that breaks with large data requests
|
||||||
// so if user requests more than BUFFER_LENGTH bytes, we have to do it in
|
// so if user requests more than I2CDEVLIB_WIRE_BUFFER_LENGTH bytes, we have to do it in
|
||||||
// smaller chunks instead of all at once
|
// smaller chunks instead of all at once
|
||||||
for (uint8_t k = 0; k < length * 2; k += min(length * 2, BUFFER_LENGTH)) {
|
for (uint8_t k = 0; k < length * 2; k += min(length * 2, I2CDEVLIB_WIRE_BUFFER_LENGTH)) {
|
||||||
Wire.beginTransmission(devAddr);
|
useWire->beginTransmission(devAddr);
|
||||||
Wire.send(regAddr);
|
useWire->send(regAddr);
|
||||||
Wire.endTransmission();
|
useWire->endTransmission();
|
||||||
Wire.beginTransmission(devAddr);
|
useWire->beginTransmission(devAddr);
|
||||||
Wire.requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes
|
useWire->requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes
|
||||||
|
|
||||||
bool msb = true; // starts with MSB, then LSB
|
bool msb = true; // starts with MSB, then LSB
|
||||||
for (; Wire.available() && count < length && (timeout == 0 || millis() - t1 < timeout);) {
|
for (; useWire->available() && count < length && (timeout == 0 || millis() - t1 < timeout);) {
|
||||||
if (msb) {
|
if (msb) {
|
||||||
// first byte is bits 15-8 (MSb=15)
|
// first byte is bits 15-8 (MSb=15)
|
||||||
data[count] = Wire.receive() << 8;
|
data[count] = useWire->receive() << 8;
|
||||||
} else {
|
} else {
|
||||||
// second byte is bits 7-0 (LSb=0)
|
// second byte is bits 7-0 (LSb=0)
|
||||||
data[count] |= Wire.receive();
|
data[count] |= useWire->receive();
|
||||||
#ifdef I2CDEV_SERIAL_DEBUG
|
#ifdef I2CDEV_SERIAL_DEBUG
|
||||||
Serial.print(data[count], HEX);
|
Serial.print(data[count], HEX);
|
||||||
if (count + 1 < length) Serial.print(" ");
|
if (count + 1 < length) Serial.print(" ");
|
||||||
@ -374,30 +376,30 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1
|
|||||||
msb = !msb;
|
msb = !msb;
|
||||||
}
|
}
|
||||||
|
|
||||||
Wire.endTransmission();
|
useWire->endTransmission();
|
||||||
}
|
}
|
||||||
#elif (ARDUINO == 100)
|
#elif (ARDUINO == 100)
|
||||||
// Arduino v1.0.0, Wire library
|
// Arduino v1.0.0, Wire library
|
||||||
// Adds standardized write() and read() stream methods instead of send() and receive()
|
// Adds standardized write() and read() stream methods instead of send() and receive()
|
||||||
|
|
||||||
// I2C/TWI subsystem uses internal buffer that breaks with large data requests
|
// I2C/TWI subsystem uses internal buffer that breaks with large data requests
|
||||||
// so if user requests more than BUFFER_LENGTH bytes, we have to do it in
|
// so if user requests more than I2CDEVLIB_WIRE_BUFFER_LENGTH bytes, we have to do it in
|
||||||
// smaller chunks instead of all at once
|
// smaller chunks instead of all at once
|
||||||
for (uint8_t k = 0; k < length * 2; k += min(length * 2, BUFFER_LENGTH)) {
|
for (uint8_t k = 0; k < length * 2; k += min(length * 2, I2CDEVLIB_WIRE_BUFFER_LENGTH)) {
|
||||||
Wire.beginTransmission(devAddr);
|
useWire->beginTransmission(devAddr);
|
||||||
Wire.write(regAddr);
|
useWire->write(regAddr);
|
||||||
Wire.endTransmission();
|
useWire->endTransmission();
|
||||||
Wire.beginTransmission(devAddr);
|
useWire->beginTransmission(devAddr);
|
||||||
Wire.requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes
|
useWire->requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes
|
||||||
|
|
||||||
bool msb = true; // starts with MSB, then LSB
|
bool msb = true; // starts with MSB, then LSB
|
||||||
for (; Wire.available() && count < length && (timeout == 0 || millis() - t1 < timeout);) {
|
for (; useWire->available() && count < length && (timeout == 0 || millis() - t1 < timeout);) {
|
||||||
if (msb) {
|
if (msb) {
|
||||||
// first byte is bits 15-8 (MSb=15)
|
// first byte is bits 15-8 (MSb=15)
|
||||||
data[count] = Wire.read() << 8;
|
data[count] = useWire->read() << 8;
|
||||||
} else {
|
} else {
|
||||||
// second byte is bits 7-0 (LSb=0)
|
// second byte is bits 7-0 (LSb=0)
|
||||||
data[count] |= Wire.read();
|
data[count] |= useWire->read();
|
||||||
#ifdef I2CDEV_SERIAL_DEBUG
|
#ifdef I2CDEV_SERIAL_DEBUG
|
||||||
Serial.print(data[count], HEX);
|
Serial.print(data[count], HEX);
|
||||||
if (count + 1 < length) Serial.print(" ");
|
if (count + 1 < length) Serial.print(" ");
|
||||||
@ -407,30 +409,30 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1
|
|||||||
msb = !msb;
|
msb = !msb;
|
||||||
}
|
}
|
||||||
|
|
||||||
Wire.endTransmission();
|
useWire->endTransmission();
|
||||||
}
|
}
|
||||||
#elif (ARDUINO > 100)
|
#elif (ARDUINO > 100)
|
||||||
// Arduino v1.0.1+, Wire library
|
// Arduino v1.0.1+, Wire library
|
||||||
// Adds official support for repeated start condition, yay!
|
// Adds official support for repeated start condition, yay!
|
||||||
|
|
||||||
// I2C/TWI subsystem uses internal buffer that breaks with large data requests
|
// I2C/TWI subsystem uses internal buffer that breaks with large data requests
|
||||||
// so if user requests more than BUFFER_LENGTH bytes, we have to do it in
|
// so if user requests more than I2CDEVLIB_WIRE_BUFFER_LENGTH bytes, we have to do it in
|
||||||
// smaller chunks instead of all at once
|
// smaller chunks instead of all at once
|
||||||
for (uint8_t k = 0; k < length * 2; k += min(length * 2, BUFFER_LENGTH)) {
|
for (uint8_t k = 0; k < length * 2; k += min(length * 2, I2CDEVLIB_WIRE_BUFFER_LENGTH)) {
|
||||||
Wire.beginTransmission(devAddr);
|
useWire->beginTransmission(devAddr);
|
||||||
Wire.write(regAddr);
|
useWire->write(regAddr);
|
||||||
Wire.endTransmission();
|
useWire->endTransmission();
|
||||||
Wire.beginTransmission(devAddr);
|
useWire->beginTransmission(devAddr);
|
||||||
Wire.requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes
|
useWire->requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes
|
||||||
|
|
||||||
bool msb = true; // starts with MSB, then LSB
|
bool msb = true; // starts with MSB, then LSB
|
||||||
for (; Wire.available() && count < length && (timeout == 0 || millis() - t1 < timeout);) {
|
for (; useWire->available() && count < length && (timeout == 0 || millis() - t1 < timeout);) {
|
||||||
if (msb) {
|
if (msb) {
|
||||||
// first byte is bits 15-8 (MSb=15)
|
// first byte is bits 15-8 (MSb=15)
|
||||||
data[count] = Wire.read() << 8;
|
data[count] = useWire->read() << 8;
|
||||||
} else {
|
} else {
|
||||||
// second byte is bits 7-0 (LSb=0)
|
// second byte is bits 7-0 (LSb=0)
|
||||||
data[count] |= Wire.read();
|
data[count] |= useWire->read();
|
||||||
#ifdef I2CDEV_SERIAL_DEBUG
|
#ifdef I2CDEV_SERIAL_DEBUG
|
||||||
Serial.print(data[count], HEX);
|
Serial.print(data[count], HEX);
|
||||||
if (count + 1 < length) Serial.print(" ");
|
if (count + 1 < length) Serial.print(" ");
|
||||||
@ -440,7 +442,7 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1
|
|||||||
msb = !msb;
|
msb = !msb;
|
||||||
}
|
}
|
||||||
|
|
||||||
Wire.endTransmission();
|
useWire->endTransmission();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -479,11 +481,11 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1
|
|||||||
* @param value New bit value to write
|
* @param value New bit value to write
|
||||||
* @return Status of operation (true = success)
|
* @return Status of operation (true = success)
|
||||||
*/
|
*/
|
||||||
bool I2Cdev::writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data) {
|
bool I2Cdev::writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data, void *wireObj) {
|
||||||
uint8_t b;
|
uint8_t b;
|
||||||
readByte(devAddr, regAddr, &b);
|
readByte(devAddr, regAddr, &b, I2Cdev::readTimeout, wireObj);
|
||||||
b = (data != 0) ? (b | (1 << bitNum)) : (b & ~(1 << bitNum));
|
b = (data != 0) ? (b | (1 << bitNum)) : (b & ~(1 << bitNum));
|
||||||
return writeByte(devAddr, regAddr, b);
|
return writeByte(devAddr, regAddr, b, wireObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** write a single bit in a 16-bit device register.
|
/** write a single bit in a 16-bit device register.
|
||||||
@ -493,11 +495,11 @@ bool I2Cdev::writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t
|
|||||||
* @param value New bit value to write
|
* @param value New bit value to write
|
||||||
* @return Status of operation (true = success)
|
* @return Status of operation (true = success)
|
||||||
*/
|
*/
|
||||||
bool I2Cdev::writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data) {
|
bool I2Cdev::writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data, void *wireObj) {
|
||||||
uint16_t w;
|
uint16_t w;
|
||||||
readWord(devAddr, regAddr, &w);
|
readWord(devAddr, regAddr, &w, I2Cdev::readTimeout, wireObj);
|
||||||
w = (data != 0) ? (w | (1 << bitNum)) : (w & ~(1 << bitNum));
|
w = (data != 0) ? (w | (1 << bitNum)) : (w & ~(1 << bitNum));
|
||||||
return writeWord(devAddr, regAddr, w);
|
return writeWord(devAddr, regAddr, w, wireObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Write multiple bits in an 8-bit device register.
|
/** Write multiple bits in an 8-bit device register.
|
||||||
@ -508,7 +510,7 @@ bool I2Cdev::writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_
|
|||||||
* @param data Right-aligned value to write
|
* @param data Right-aligned value to write
|
||||||
* @return Status of operation (true = success)
|
* @return Status of operation (true = success)
|
||||||
*/
|
*/
|
||||||
bool I2Cdev::writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data) {
|
bool I2Cdev::writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data, void *wireObj) {
|
||||||
// 010 value to write
|
// 010 value to write
|
||||||
// 76543210 bit numbers
|
// 76543210 bit numbers
|
||||||
// xxx args: bitStart=4, length=3
|
// xxx args: bitStart=4, length=3
|
||||||
@ -517,13 +519,13 @@ bool I2Cdev::writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8
|
|||||||
// 10100011 original & ~mask
|
// 10100011 original & ~mask
|
||||||
// 10101011 masked | value
|
// 10101011 masked | value
|
||||||
uint8_t b;
|
uint8_t b;
|
||||||
if (readByte(devAddr, regAddr, &b) != 0) {
|
if (readByte(devAddr, regAddr, &b, I2Cdev::readTimeout, wireObj) != 0) {
|
||||||
uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1);
|
uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1);
|
||||||
data <<= (bitStart - length + 1); // shift data into correct position
|
data <<= (bitStart - length + 1); // shift data into correct position
|
||||||
data &= mask; // zero all non-important bits in data
|
data &= mask; // zero all non-important bits in data
|
||||||
b &= ~(mask); // zero all important bits in existing byte
|
b &= ~(mask); // zero all important bits in existing byte
|
||||||
b |= data; // combine data with existing byte
|
b |= data; // combine data with existing byte
|
||||||
return writeByte(devAddr, regAddr, b);
|
return writeByte(devAddr, regAddr, b, wireObj);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -537,7 +539,7 @@ bool I2Cdev::writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8
|
|||||||
* @param data Right-aligned value to write
|
* @param data Right-aligned value to write
|
||||||
* @return Status of operation (true = success)
|
* @return Status of operation (true = success)
|
||||||
*/
|
*/
|
||||||
bool I2Cdev::writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data) {
|
bool I2Cdev::writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data, void *wireObj) {
|
||||||
// 010 value to write
|
// 010 value to write
|
||||||
// fedcba9876543210 bit numbers
|
// fedcba9876543210 bit numbers
|
||||||
// xxx args: bitStart=12, length=3
|
// xxx args: bitStart=12, length=3
|
||||||
@ -546,13 +548,13 @@ bool I2Cdev::writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint
|
|||||||
// 1010001110010110 original & ~mask
|
// 1010001110010110 original & ~mask
|
||||||
// 1010101110010110 masked | value
|
// 1010101110010110 masked | value
|
||||||
uint16_t w;
|
uint16_t w;
|
||||||
if (readWord(devAddr, regAddr, &w) != 0) {
|
if (readWord(devAddr, regAddr, &w, I2Cdev::readTimeout, wireObj) != 0) {
|
||||||
uint16_t mask = ((1 << length) - 1) << (bitStart - length + 1);
|
uint16_t mask = ((1 << length) - 1) << (bitStart - length + 1);
|
||||||
data <<= (bitStart - length + 1); // shift data into correct position
|
data <<= (bitStart - length + 1); // shift data into correct position
|
||||||
data &= mask; // zero all non-important bits in data
|
data &= mask; // zero all non-important bits in data
|
||||||
w &= ~(mask); // zero all important bits in existing word
|
w &= ~(mask); // zero all important bits in existing word
|
||||||
w |= data; // combine data with existing word
|
w |= data; // combine data with existing word
|
||||||
return writeWord(devAddr, regAddr, w);
|
return writeWord(devAddr, regAddr, w, wireObj);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -564,8 +566,8 @@ bool I2Cdev::writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint
|
|||||||
* @param data New byte value to write
|
* @param data New byte value to write
|
||||||
* @return Status of operation (true = success)
|
* @return Status of operation (true = success)
|
||||||
*/
|
*/
|
||||||
bool I2Cdev::writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data) {
|
bool I2Cdev::writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data, void *wireObj) {
|
||||||
return writeBytes(devAddr, regAddr, 1, &data);
|
return writeBytes(devAddr, regAddr, 1, &data, wireObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Write single word to a 16-bit device register.
|
/** Write single word to a 16-bit device register.
|
||||||
@ -574,8 +576,8 @@ bool I2Cdev::writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data) {
|
|||||||
* @param data New word value to write
|
* @param data New word value to write
|
||||||
* @return Status of operation (true = success)
|
* @return Status of operation (true = success)
|
||||||
*/
|
*/
|
||||||
bool I2Cdev::writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data) {
|
bool I2Cdev::writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data, void *wireObj) {
|
||||||
return writeWords(devAddr, regAddr, 1, &data);
|
return writeWords(devAddr, regAddr, 1, &data, wireObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Write multiple bytes to an 8-bit device register.
|
/** Write multiple bytes to an 8-bit device register.
|
||||||
@ -585,7 +587,7 @@ bool I2Cdev::writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data) {
|
|||||||
* @param data Buffer to copy new data from
|
* @param data Buffer to copy new data from
|
||||||
* @return Status of operation (true = success)
|
* @return Status of operation (true = success)
|
||||||
*/
|
*/
|
||||||
bool I2Cdev::writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t* data) {
|
bool I2Cdev::writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t* data, void *wireObj) {
|
||||||
#ifdef I2CDEV_SERIAL_DEBUG
|
#ifdef I2CDEV_SERIAL_DEBUG
|
||||||
Serial.print("I2C (0x");
|
Serial.print("I2C (0x");
|
||||||
Serial.print(devAddr, HEX);
|
Serial.print(devAddr, HEX);
|
||||||
@ -596,14 +598,20 @@ bool I2Cdev::writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_
|
|||||||
Serial.print("...");
|
Serial.print("...");
|
||||||
#endif
|
#endif
|
||||||
uint8_t status = 0;
|
uint8_t status = 0;
|
||||||
|
|
||||||
|
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE
|
||||||
|
TwoWire *useWire = &Wire;
|
||||||
|
if (wireObj) useWire = (TwoWire *)wireObj;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
|
#if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
|
||||||
Wire.beginTransmission(devAddr);
|
useWire->beginTransmission(devAddr);
|
||||||
Wire.send((uint8_t) regAddr); // send address
|
useWire->send((uint8_t) regAddr); // send address
|
||||||
#elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \
|
#elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \
|
||||||
|| (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \
|
|| (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \
|
||||||
|| I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE)
|
|| I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE)
|
||||||
Wire.beginTransmission(devAddr);
|
useWire->beginTransmission(devAddr);
|
||||||
Wire.write((uint8_t) regAddr); // send address
|
useWire->write((uint8_t) regAddr); // send address
|
||||||
#elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
|
#elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
|
||||||
Fastwire::beginTransmission(devAddr);
|
Fastwire::beginTransmission(devAddr);
|
||||||
Fastwire::write(regAddr);
|
Fastwire::write(regAddr);
|
||||||
@ -614,21 +622,21 @@ bool I2Cdev::writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_
|
|||||||
if (i + 1 < length) Serial.print(" ");
|
if (i + 1 < length) Serial.print(" ");
|
||||||
#endif
|
#endif
|
||||||
#if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
|
#if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
|
||||||
Wire.send((uint8_t) data[i]);
|
useWire->send((uint8_t) data[i]);
|
||||||
#elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \
|
#elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \
|
||||||
|| (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \
|
|| (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \
|
||||||
|| I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE)
|
|| I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE)
|
||||||
Wire.write((uint8_t) data[i]);
|
useWire->write((uint8_t) data[i]);
|
||||||
#elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
|
#elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
|
||||||
Fastwire::write((uint8_t) data[i]);
|
Fastwire::write((uint8_t) data[i]);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
|
#if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
|
||||||
Wire.endTransmission();
|
useWire->endTransmission();
|
||||||
#elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \
|
#elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \
|
||||||
|| (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \
|
|| (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \
|
||||||
|| I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE)
|
|| I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE)
|
||||||
status = Wire.endTransmission();
|
status = useWire->endTransmission();
|
||||||
#elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
|
#elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
|
||||||
Fastwire::stop();
|
Fastwire::stop();
|
||||||
//status = Fastwire::endTransmission();
|
//status = Fastwire::endTransmission();
|
||||||
@ -646,7 +654,7 @@ bool I2Cdev::writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_
|
|||||||
* @param data Buffer to copy new data from
|
* @param data Buffer to copy new data from
|
||||||
* @return Status of operation (true = success)
|
* @return Status of operation (true = success)
|
||||||
*/
|
*/
|
||||||
bool I2Cdev::writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t* data) {
|
bool I2Cdev::writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t* data, void *wireObj) {
|
||||||
#ifdef I2CDEV_SERIAL_DEBUG
|
#ifdef I2CDEV_SERIAL_DEBUG
|
||||||
Serial.print("I2C (0x");
|
Serial.print("I2C (0x");
|
||||||
Serial.print(devAddr, HEX);
|
Serial.print(devAddr, HEX);
|
||||||
@ -657,14 +665,20 @@ bool I2Cdev::writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16
|
|||||||
Serial.print("...");
|
Serial.print("...");
|
||||||
#endif
|
#endif
|
||||||
uint8_t status = 0;
|
uint8_t status = 0;
|
||||||
|
|
||||||
|
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE
|
||||||
|
TwoWire *useWire = &Wire;
|
||||||
|
if (wireObj) useWire = (TwoWire *)wireObj;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
|
#if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
|
||||||
Wire.beginTransmission(devAddr);
|
useWire->beginTransmission(devAddr);
|
||||||
Wire.send(regAddr); // send address
|
useWire->send(regAddr); // send address
|
||||||
#elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \
|
#elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \
|
||||||
|| (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \
|
|| (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \
|
||||||
|| I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE)
|
|| I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE)
|
||||||
Wire.beginTransmission(devAddr);
|
useWire->beginTransmission(devAddr);
|
||||||
Wire.write(regAddr); // send address
|
useWire->write(regAddr); // send address
|
||||||
#elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
|
#elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
|
||||||
Fastwire::beginTransmission(devAddr);
|
Fastwire::beginTransmission(devAddr);
|
||||||
Fastwire::write(regAddr);
|
Fastwire::write(regAddr);
|
||||||
@ -675,13 +689,13 @@ bool I2Cdev::writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16
|
|||||||
if (i + 1 < length) Serial.print(" ");
|
if (i + 1 < length) Serial.print(" ");
|
||||||
#endif
|
#endif
|
||||||
#if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
|
#if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
|
||||||
Wire.send((uint8_t)(data[i] >> 8)); // send MSB
|
useWire->send((uint8_t)(data[i] >> 8)); // send MSB
|
||||||
Wire.send((uint8_t)data[i]); // send LSB
|
useWire->send((uint8_t)data[i]); // send LSB
|
||||||
#elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \
|
#elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \
|
||||||
|| (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \
|
|| (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \
|
||||||
|| I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE)
|
|| I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE)
|
||||||
Wire.write((uint8_t)(data[i] >> 8)); // send MSB
|
useWire->write((uint8_t)(data[i] >> 8)); // send MSB
|
||||||
Wire.write((uint8_t)data[i]); // send LSB
|
useWire->write((uint8_t)data[i]); // send LSB
|
||||||
#elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
|
#elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
|
||||||
Fastwire::write((uint8_t)(data[i] >> 8)); // send MSB
|
Fastwire::write((uint8_t)(data[i] >> 8)); // send MSB
|
||||||
status = Fastwire::write((uint8_t)data[i]); // send LSB
|
status = Fastwire::write((uint8_t)data[i]); // send LSB
|
||||||
@ -689,11 +703,11 @@ bool I2Cdev::writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
|
#if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
|
||||||
Wire.endTransmission();
|
useWire->endTransmission();
|
||||||
#elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \
|
#elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \
|
||||||
|| (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \
|
|| (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \
|
||||||
|| I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE)
|
|| I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE)
|
||||||
status = Wire.endTransmission();
|
status = useWire->endTransmission();
|
||||||
#elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
|
#elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
|
||||||
Fastwire::stop();
|
Fastwire::stop();
|
||||||
//status = Fastwire::endTransmission();
|
//status = Fastwire::endTransmission();
|
||||||
@ -753,7 +767,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
TWSR = 0; // no prescaler => prescaler = 1
|
TWSR = 0; // no prescaler => prescaler = 1
|
||||||
TWBR = ((16000L / khz) - 16) / 2; // change the I2C clock rate
|
TWBR = F_CPU / 2000 / khz - 8; // change the I2C clock rate
|
||||||
TWCR = 1 << TWEN; // enable twi module, no interrupt
|
TWCR = 1 << TWEN; // enable twi module, no interrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -993,7 +1007,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT;
|
|||||||
TWBR = ((CPU_FREQ / TWI_FREQ) - 16) / 2; // bitrate register
|
TWBR = ((CPU_FREQ / TWI_FREQ) - 16) / 2; // bitrate register
|
||||||
// enable twi module, acks, and twi interrupt
|
// enable twi module, acks, and twi interrupt
|
||||||
|
|
||||||
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
|
TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWEA);
|
||||||
|
|
||||||
/* TWEN - TWI Enable Bit
|
/* TWEN - TWI Enable Bit
|
||||||
TWIE - TWI Interrupt Enable
|
TWIE - TWI Interrupt Enable
|
||||||
@ -1062,7 +1076,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT;
|
|||||||
}
|
}
|
||||||
|
|
||||||
void twii_SetStart() {
|
void twii_SetStart() {
|
||||||
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);
|
TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWEA) | (1 << TWINT) | (1 << TWSTA);
|
||||||
}
|
}
|
||||||
|
|
||||||
void twi_write01() {
|
void twi_write01() {
|
||||||
@ -1145,19 +1159,19 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT;
|
|||||||
void twi_reply(uint8_t ack) {
|
void twi_reply(uint8_t ack) {
|
||||||
// transmit master read ready signal, with or without ack
|
// transmit master read ready signal, with or without ack
|
||||||
if (ack){
|
if (ack){
|
||||||
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);
|
TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWINT) | (1 << TWEA);
|
||||||
} else {
|
} else {
|
||||||
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);
|
TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWINT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void twi_stop(void) {
|
void twi_stop(void) {
|
||||||
// send stop condition
|
// send stop condition
|
||||||
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO);
|
TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWEA) | (1 << TWINT) | (1 << TWSTO);
|
||||||
|
|
||||||
// wait for stop condition to be exectued on bus
|
// wait for stop condition to be exectued on bus
|
||||||
// TWINT is not set after a stop condition!
|
// TWINT is not set after a stop condition!
|
||||||
while (TWCR & _BV(TWSTO)) {
|
while (TWCR & (1 << TWSTO)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1167,7 +1181,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT;
|
|||||||
|
|
||||||
void twi_releaseBus(void) {
|
void twi_releaseBus(void) {
|
||||||
// release bus
|
// release bus
|
||||||
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);
|
TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWEA) | (1 << TWINT);
|
||||||
|
|
||||||
// update twi state
|
// update twi state
|
||||||
twi_state = TWI_READY;
|
twi_state = TWI_READY;
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
// 2013-06-05 by Jeff Rowberg <jeff@rowberg.net>
|
// 2013-06-05 by Jeff Rowberg <jeff@rowberg.net>
|
||||||
//
|
//
|
||||||
// Changelog:
|
// Changelog:
|
||||||
|
// 2021-09-28 - allow custom Wire object as transaction function argument
|
||||||
// 2020-01-20 - hardija : complete support for Teensy 3.x
|
// 2020-01-20 - hardija : complete support for Teensy 3.x
|
||||||
// 2015-10-30 - simondlevy : support i2c_t3 for Teensy3.1
|
// 2015-10-30 - simondlevy : support i2c_t3 for Teensy3.1
|
||||||
// 2013-05-06 - add Francesco Ferrara's Fastwire v0.24 implementation with small modifications
|
// 2013-05-06 - add Francesco Ferrara's Fastwire v0.24 implementation with small modifications
|
||||||
@ -48,6 +49,11 @@ THE SOFTWARE.
|
|||||||
#ifndef _I2CDEV_H_
|
#ifndef _I2CDEV_H_
|
||||||
#define _I2CDEV_H_
|
#define _I2CDEV_H_
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Enable deprecated pgmspace typedefs in avr-libc
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
#define __PROG_TYPES_COMPAT__
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// I2C interface implementation setting
|
// I2C interface implementation setting
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@ -99,10 +105,26 @@ THE SOFTWARE.
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SPARK
|
#ifdef SPARK
|
||||||
#include <spark_wiring_i2c.h>
|
#include "application.h"
|
||||||
#define ARDUINO 101
|
#define ARDUINO 101
|
||||||
|
#define BUFFER_LENGTH 32
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef I2CDEVLIB_WIRE_BUFFER_LENGTH
|
||||||
|
#if defined(I2C_BUFFER_LENGTH)
|
||||||
|
// Arduino ESP32 core Wire uses this
|
||||||
|
#define I2CDEVLIB_WIRE_BUFFER_LENGTH I2C_BUFFER_LENGTH
|
||||||
|
#elif defined(BUFFER_LENGTH)
|
||||||
|
// Arduino AVR core Wire and many others use this
|
||||||
|
#define I2CDEVLIB_WIRE_BUFFER_LENGTH BUFFER_LENGTH
|
||||||
|
#elif defined(SERIAL_BUFFER_SIZE)
|
||||||
|
// Arduino SAMD core Wire uses this
|
||||||
|
#define I2CDEVLIB_WIRE_BUFFER_LENGTH SERIAL_BUFFER_SIZE
|
||||||
|
#else
|
||||||
|
// should be a safe fallback, though possibly inefficient
|
||||||
|
#define I2CDEVLIB_WIRE_BUFFER_LENGTH 32
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
// 1000ms default read timeout (modify with "I2Cdev::readTimeout = [ms];")
|
// 1000ms default read timeout (modify with "I2Cdev::readTimeout = [ms];")
|
||||||
#define I2CDEV_DEFAULT_READ_TIMEOUT 1000
|
#define I2CDEV_DEFAULT_READ_TIMEOUT 1000
|
||||||
@ -111,23 +133,23 @@ class I2Cdev {
|
|||||||
public:
|
public:
|
||||||
I2Cdev();
|
I2Cdev();
|
||||||
|
|
||||||
static int8_t readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
static int8_t readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout, void *wireObj=0);
|
||||||
static int8_t readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
static int8_t readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout, void *wireObj=0);
|
||||||
static int8_t readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
static int8_t readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout, void *wireObj=0);
|
||||||
static int8_t readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
static int8_t readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout, void *wireObj=0);
|
||||||
static int8_t readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
static int8_t readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout, void *wireObj=0);
|
||||||
static int8_t readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
static int8_t readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout, void *wireObj=0);
|
||||||
static int8_t readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
static int8_t readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout, void *wireObj=0);
|
||||||
static int8_t readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
static int8_t readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout, void *wireObj=0);
|
||||||
|
|
||||||
static bool writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data);
|
static bool writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data, void *wireObj=0);
|
||||||
static bool writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data);
|
static bool writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data, void *wireObj=0);
|
||||||
static bool writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data);
|
static bool writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data, void *wireObj=0);
|
||||||
static bool writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data);
|
static bool writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data, void *wireObj=0);
|
||||||
static bool writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data);
|
static bool writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data, void *wireObj=0);
|
||||||
static bool writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data);
|
static bool writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data, void *wireObj=0);
|
||||||
static bool writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data);
|
static bool writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, void *wireObj=0);
|
||||||
static bool writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data);
|
static bool writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, void *wireObj=0);
|
||||||
|
|
||||||
static uint16_t readTimeout;
|
static uint16_t readTimeout;
|
||||||
};
|
};
|
||||||
@ -240,7 +262,7 @@ class I2Cdev {
|
|||||||
|
|
||||||
/* TWI Status is in TWSR, in the top 5 bits: TWS7 - TWS3 */
|
/* TWI Status is in TWSR, in the top 5 bits: TWS7 - TWS3 */
|
||||||
|
|
||||||
#define TW_STATUS_MASK (_BV(TWS7)|_BV(TWS6)|_BV(TWS5)|_BV(TWS4)|_BV(TWS3))
|
#define TW_STATUS_MASK ((1 << TWS7)|(1 << TWS6)|(1 << TWS5)|(1 << TWS4)|(1 << TWS3))
|
||||||
#define TW_STATUS (TWSR & TW_STATUS_MASK)
|
#define TW_STATUS (TWSR & TW_STATUS_MASK)
|
||||||
#define TW_START 0x08
|
#define TW_START 0x08
|
||||||
#define TW_REP_START 0x10
|
#define TW_REP_START 0x10
|
||||||
@ -275,11 +297,11 @@ class I2Cdev {
|
|||||||
//#define _SFR_BYTE(sfr) _MMIO_BYTE(_SFR_ADDR(sfr))
|
//#define _SFR_BYTE(sfr) _MMIO_BYTE(_SFR_ADDR(sfr))
|
||||||
|
|
||||||
#ifndef sbi // set bit
|
#ifndef sbi // set bit
|
||||||
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
|
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= (1 << bit))
|
||||||
#endif // sbi
|
#endif // sbi
|
||||||
|
|
||||||
#ifndef cbi // clear bit
|
#ifndef cbi // clear bit
|
||||||
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
|
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~(1 << bit))
|
||||||
#endif // cbi
|
#endif // cbi
|
||||||
|
|
||||||
extern TwoWire Wire;
|
extern TwoWire Wire;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
|
// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
|
||||||
//
|
//
|
||||||
// Changelog:
|
// Changelog:
|
||||||
|
// 2021/09/27 - split implementations out of header files, finally
|
||||||
// ... - ongoing debug release
|
// ... - ongoing debug release
|
||||||
|
|
||||||
// NOTE: THIS IS ONLY A PARIAL RELEASE. THIS DEVICE CLASS IS CURRENTLY UNDERGOING ACTIVE
|
// NOTE: THIS IS ONLY A PARIAL RELEASE. THIS DEVICE CLASS IS CURRENTLY UNDERGOING ACTIVE
|
||||||
@ -38,12 +39,15 @@ THE SOFTWARE.
|
|||||||
#define _MPU6050_H_
|
#define _MPU6050_H_
|
||||||
|
|
||||||
#include "I2Cdev.h"
|
#include "I2Cdev.h"
|
||||||
|
#include "helper_3dmath.h"
|
||||||
|
|
||||||
// supporting link: http://forum.arduino.cc/index.php?&topic=143444.msg1079517#msg1079517
|
// supporting link: http://forum.arduino.cc/index.php?&topic=143444.msg1079517#msg1079517
|
||||||
// also: http://forum.arduino.cc/index.php?&topic=141571.msg1062899#msg1062899s
|
// also: http://forum.arduino.cc/index.php?&topic=141571.msg1062899#msg1062899s
|
||||||
|
|
||||||
#ifdef __AVR__
|
#ifdef __AVR__
|
||||||
#include <avr/pgmspace.h>
|
#include <avr/pgmspace.h>
|
||||||
|
#elif defined(ESP32)
|
||||||
|
#include <pgmspace.h>
|
||||||
#else
|
#else
|
||||||
//#define PROGMEM /* empty */
|
//#define PROGMEM /* empty */
|
||||||
//#define pgm_read_byte(x) (*(x))
|
//#define pgm_read_byte(x) (*(x))
|
||||||
@ -431,11 +435,11 @@ THE SOFTWARE.
|
|||||||
#define MPU6050_DMP_MEMORY_BANK_SIZE 256
|
#define MPU6050_DMP_MEMORY_BANK_SIZE 256
|
||||||
#define MPU6050_DMP_MEMORY_CHUNK_SIZE 16
|
#define MPU6050_DMP_MEMORY_CHUNK_SIZE 16
|
||||||
|
|
||||||
// note: DMP code memory blocks defined at end of header file
|
#define MPU6050_FIFO_DEFAULT_TIMEOUT 11000
|
||||||
|
|
||||||
class MPU6050 {
|
class MPU6050_Base {
|
||||||
public:
|
public:
|
||||||
MPU6050(uint8_t address=MPU6050_DEFAULT_ADDRESS);
|
MPU6050_Base(uint8_t address=MPU6050_DEFAULT_ADDRESS, void *wireObj=0);
|
||||||
|
|
||||||
void initialize();
|
void initialize();
|
||||||
bool testConnection();
|
bool testConnection();
|
||||||
@ -717,6 +721,8 @@ class MPU6050 {
|
|||||||
int8_t GetCurrentFIFOPacket(uint8_t *data, uint8_t length);
|
int8_t GetCurrentFIFOPacket(uint8_t *data, uint8_t length);
|
||||||
void setFIFOByte(uint8_t data);
|
void setFIFOByte(uint8_t data);
|
||||||
void getFIFOBytes(uint8_t *data, uint8_t length);
|
void getFIFOBytes(uint8_t *data, uint8_t length);
|
||||||
|
void setFIFOTimeout(uint32_t fifoTimeout);
|
||||||
|
uint32_t getFIFOTimeout();
|
||||||
|
|
||||||
// WHO_AM_I register
|
// WHO_AM_I register
|
||||||
uint8_t getDeviceID();
|
uint8_t getDeviceID();
|
||||||
@ -827,215 +833,16 @@ class MPU6050 {
|
|||||||
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
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
// special methods for MotionApps 2.0 implementation
|
|
||||||
#ifdef MPU6050_INCLUDE_DMP_MOTIONAPPS20
|
|
||||||
|
|
||||||
uint8_t dmpInitialize();
|
|
||||||
bool dmpPacketAvailable();
|
|
||||||
|
|
||||||
uint8_t dmpSetFIFORate(uint8_t fifoRate);
|
|
||||||
uint8_t dmpGetFIFORate();
|
|
||||||
uint8_t dmpGetSampleStepSizeMS();
|
|
||||||
uint8_t dmpGetSampleFrequency();
|
|
||||||
int32_t dmpDecodeTemperature(int8_t tempReg);
|
|
||||||
|
|
||||||
// Register callbacks after a packet of FIFO data is processed
|
|
||||||
//uint8_t dmpRegisterFIFORateProcess(inv_obj_func func, int16_t priority);
|
|
||||||
//uint8_t dmpUnregisterFIFORateProcess(inv_obj_func func);
|
|
||||||
uint8_t dmpRunFIFORateProcesses();
|
|
||||||
|
|
||||||
// Setup FIFO for various output
|
|
||||||
uint8_t dmpSendQuaternion(uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendGyro(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendAccel(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendLinearAccel(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendLinearAccelInWorld(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendControlData(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendSensorData(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendExternalSensorData(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendGravity(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendPacketNumber(uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendQuantizedAccel(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendEIS(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
|
|
||||||
// Get Fixed Point data from FIFO
|
|
||||||
uint8_t dmpGetAccel(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetAccel(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetAccel(VectorInt16 *v, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetQuaternion(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetQuaternion(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetQuaternion(Quaternion *q, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGet6AxisQuaternion(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGet6AxisQuaternion(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGet6AxisQuaternion(Quaternion *q, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetRelativeQuaternion(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetRelativeQuaternion(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetRelativeQuaternion(Quaternion *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGyro(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGyro(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGyro(VectorInt16 *v, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpSetLinearAccelFilterCoefficient(float coef);
|
|
||||||
uint8_t dmpGetLinearAccel(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetLinearAccel(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetLinearAccel(VectorInt16 *v, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetLinearAccel(VectorInt16 *v, VectorInt16 *vRaw, VectorFloat *gravity);
|
|
||||||
uint8_t dmpGetLinearAccelInWorld(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetLinearAccelInWorld(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, VectorInt16 *vReal, Quaternion *q);
|
|
||||||
uint8_t dmpGetGyroAndAccelSensor(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGyroAndAccelSensor(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGyroAndAccelSensor(VectorInt16 *g, VectorInt16 *a, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGyroSensor(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGyroSensor(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGyroSensor(VectorInt16 *v, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetControlData(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetTemperature(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGravity(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGravity(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGravity(VectorInt16 *v, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGravity(VectorFloat *v, Quaternion *q);
|
|
||||||
uint8_t dmpGetUnquantizedAccel(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetUnquantizedAccel(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetUnquantizedAccel(VectorInt16 *v, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetQuantizedAccel(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetQuantizedAccel(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetQuantizedAccel(VectorInt16 *v, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetExternalSensorData(int32_t *data, uint16_t size, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetEIS(int32_t *data, const uint8_t* packet=0);
|
|
||||||
|
|
||||||
uint8_t dmpGetEuler(float *data, Quaternion *q);
|
|
||||||
uint8_t dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity);
|
|
||||||
|
|
||||||
// Get Floating Point data from FIFO
|
|
||||||
uint8_t dmpGetAccelFloat(float *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetQuaternionFloat(float *data, const uint8_t* packet=0);
|
|
||||||
|
|
||||||
uint8_t dmpProcessFIFOPacket(const unsigned char *dmpData);
|
|
||||||
uint8_t dmpReadAndProcessFIFOPacket(uint8_t numPackets, uint8_t *processed=NULL);
|
|
||||||
|
|
||||||
uint8_t dmpSetFIFOProcessedCallback(void (*func) (void));
|
|
||||||
|
|
||||||
uint8_t dmpInitFIFOParam();
|
|
||||||
uint8_t dmpCloseFIFO();
|
|
||||||
uint8_t dmpSetGyroDataSource(uint8_t source);
|
|
||||||
uint8_t dmpDecodeQuantizedAccel();
|
|
||||||
uint32_t dmpGetGyroSumOfSquare();
|
|
||||||
uint32_t dmpGetAccelSumOfSquare();
|
|
||||||
void dmpOverrideQuaternion(long *q);
|
|
||||||
uint16_t dmpGetFIFOPacketSize();
|
|
||||||
uint8_t dmpGetCurrentFIFOPacket(uint8_t *data); // overflow proof
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// special methods for MotionApps 4.1 implementation
|
|
||||||
#ifdef MPU6050_INCLUDE_DMP_MOTIONAPPS41
|
|
||||||
|
|
||||||
uint8_t dmpInitialize();
|
|
||||||
bool dmpPacketAvailable();
|
|
||||||
|
|
||||||
uint8_t dmpSetFIFORate(uint8_t fifoRate);
|
|
||||||
uint8_t dmpGetFIFORate();
|
|
||||||
uint8_t dmpGetSampleStepSizeMS();
|
|
||||||
uint8_t dmpGetSampleFrequency();
|
|
||||||
int32_t dmpDecodeTemperature(int8_t tempReg);
|
|
||||||
|
|
||||||
// Register callbacks after a packet of FIFO data is processed
|
|
||||||
//uint8_t dmpRegisterFIFORateProcess(inv_obj_func func, int16_t priority);
|
|
||||||
//uint8_t dmpUnregisterFIFORateProcess(inv_obj_func func);
|
|
||||||
uint8_t dmpRunFIFORateProcesses();
|
|
||||||
|
|
||||||
// Setup FIFO for various output
|
|
||||||
uint8_t dmpSendQuaternion(uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendGyro(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendAccel(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendLinearAccel(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendLinearAccelInWorld(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendControlData(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendSensorData(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendExternalSensorData(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendGravity(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendPacketNumber(uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendQuantizedAccel(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
uint8_t dmpSendEIS(uint_fast16_t elements, uint_fast16_t accuracy);
|
|
||||||
|
|
||||||
// Get Fixed Point data from FIFO
|
|
||||||
uint8_t dmpGetAccel(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetAccel(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetAccel(VectorInt16 *v, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetQuaternion(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetQuaternion(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetQuaternion(Quaternion *q, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGet6AxisQuaternion(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGet6AxisQuaternion(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGet6AxisQuaternion(Quaternion *q, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetRelativeQuaternion(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetRelativeQuaternion(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetRelativeQuaternion(Quaternion *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGyro(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGyro(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGyro(VectorInt16 *v, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetMag(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpSetLinearAccelFilterCoefficient(float coef);
|
|
||||||
uint8_t dmpGetLinearAccel(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetLinearAccel(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetLinearAccel(VectorInt16 *v, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetLinearAccel(VectorInt16 *v, VectorInt16 *vRaw, VectorFloat *gravity);
|
|
||||||
uint8_t dmpGetLinearAccelInWorld(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetLinearAccelInWorld(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, VectorInt16 *vReal, Quaternion *q);
|
|
||||||
uint8_t dmpGetGyroAndAccelSensor(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGyroAndAccelSensor(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGyroAndAccelSensor(VectorInt16 *g, VectorInt16 *a, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGyroSensor(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGyroSensor(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGyroSensor(VectorInt16 *v, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetControlData(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetTemperature(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGravity(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGravity(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGravity(VectorInt16 *v, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetGravity(VectorFloat *v, Quaternion *q);
|
|
||||||
uint8_t dmpGetUnquantizedAccel(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetUnquantizedAccel(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetUnquantizedAccel(VectorInt16 *v, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetQuantizedAccel(int32_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetQuantizedAccel(int16_t *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetQuantizedAccel(VectorInt16 *v, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetExternalSensorData(int32_t *data, uint16_t size, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetEIS(int32_t *data, const uint8_t* packet=0);
|
|
||||||
|
|
||||||
uint8_t dmpGetEuler(float *data, Quaternion *q);
|
|
||||||
uint8_t dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity);
|
|
||||||
|
|
||||||
// Get Floating Point data from FIFO
|
|
||||||
uint8_t dmpGetAccelFloat(float *data, const uint8_t* packet=0);
|
|
||||||
uint8_t dmpGetQuaternionFloat(float *data, const uint8_t* packet=0);
|
|
||||||
|
|
||||||
uint8_t dmpProcessFIFOPacket(const unsigned char *dmpData);
|
|
||||||
uint8_t dmpReadAndProcessFIFOPacket(uint8_t numPackets, uint8_t *processed=NULL);
|
|
||||||
|
|
||||||
uint8_t dmpSetFIFOProcessedCallback(void (*func) (void));
|
|
||||||
|
|
||||||
uint8_t dmpInitFIFOParam();
|
|
||||||
uint8_t dmpCloseFIFO();
|
|
||||||
uint8_t dmpSetGyroDataSource(uint8_t source);
|
|
||||||
uint8_t dmpDecodeQuantizedAccel();
|
|
||||||
uint32_t dmpGetGyroSumOfSquare();
|
|
||||||
uint32_t dmpGetAccelSumOfSquare();
|
|
||||||
void dmpOverrideQuaternion(long *q);
|
|
||||||
uint16_t dmpGetFIFOPacketSize();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint8_t devAddr;
|
uint8_t devAddr;
|
||||||
|
void *wireObj;
|
||||||
uint8_t buffer[14];
|
uint8_t buffer[14];
|
||||||
#if defined(MPU6050_INCLUDE_DMP_MOTIONAPPS20) or defined(MPU6050_INCLUDE_DMP_MOTIONAPPS41)
|
uint32_t fifoTimeout = MPU6050_FIFO_DEFAULT_TIMEOUT;
|
||||||
uint8_t *dmpPacketBuffer;
|
|
||||||
uint16_t dmpPacketSize;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef I2CDEVLIB_MPU6050_TYPEDEF
|
||||||
|
#define I2CDEVLIB_MPU6050_TYPEDEF
|
||||||
|
typedef MPU6050_Base MPU6050;
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* _MPU6050_H_ */
|
#endif /* _MPU6050_H_ */
|
||||||
|
216
lib/mpu6050/helper_3dmath.h
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class, 3D math helper
|
||||||
|
// 6/5/2012 by Jeff Rowberg <jeff@rowberg.net>
|
||||||
|
// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
|
||||||
|
//
|
||||||
|
// Changelog:
|
||||||
|
// 2012-06-05 - add 3D math helper file to DMP6 example sketch
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
I2Cdev device library code is placed under the MIT license
|
||||||
|
Copyright (c) 2012 Jeff Rowberg
|
||||||
|
|
||||||
|
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 above 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.
|
||||||
|
===============================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HELPER_3DMATH_H_
|
||||||
|
#define _HELPER_3DMATH_H_
|
||||||
|
|
||||||
|
class Quaternion {
|
||||||
|
public:
|
||||||
|
float w;
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
|
||||||
|
Quaternion() {
|
||||||
|
w = 1.0f;
|
||||||
|
x = 0.0f;
|
||||||
|
y = 0.0f;
|
||||||
|
z = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
Quaternion(float nw, float nx, float ny, float nz) {
|
||||||
|
w = nw;
|
||||||
|
x = nx;
|
||||||
|
y = ny;
|
||||||
|
z = nz;
|
||||||
|
}
|
||||||
|
|
||||||
|
Quaternion getProduct(Quaternion q) {
|
||||||
|
// Quaternion multiplication is defined by:
|
||||||
|
// (Q1 * Q2).w = (w1w2 - x1x2 - y1y2 - z1z2)
|
||||||
|
// (Q1 * Q2).x = (w1x2 + x1w2 + y1z2 - z1y2)
|
||||||
|
// (Q1 * Q2).y = (w1y2 - x1z2 + y1w2 + z1x2)
|
||||||
|
// (Q1 * Q2).z = (w1z2 + x1y2 - y1x2 + z1w2
|
||||||
|
return Quaternion(
|
||||||
|
w*q.w - x*q.x - y*q.y - z*q.z, // new w
|
||||||
|
w*q.x + x*q.w + y*q.z - z*q.y, // new x
|
||||||
|
w*q.y - x*q.z + y*q.w + z*q.x, // new y
|
||||||
|
w*q.z + x*q.y - y*q.x + z*q.w); // new z
|
||||||
|
}
|
||||||
|
|
||||||
|
Quaternion getConjugate() {
|
||||||
|
return Quaternion(w, -x, -y, -z);
|
||||||
|
}
|
||||||
|
|
||||||
|
float getMagnitude() {
|
||||||
|
return sqrt(w*w + x*x + y*y + z*z);
|
||||||
|
}
|
||||||
|
|
||||||
|
void normalize() {
|
||||||
|
float m = getMagnitude();
|
||||||
|
w /= m;
|
||||||
|
x /= m;
|
||||||
|
y /= m;
|
||||||
|
z /= m;
|
||||||
|
}
|
||||||
|
|
||||||
|
Quaternion getNormalized() {
|
||||||
|
Quaternion r(w, x, y, z);
|
||||||
|
r.normalize();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class VectorInt16 {
|
||||||
|
public:
|
||||||
|
int16_t x;
|
||||||
|
int16_t y;
|
||||||
|
int16_t z;
|
||||||
|
|
||||||
|
VectorInt16() {
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
z = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorInt16(int16_t nx, int16_t ny, int16_t nz) {
|
||||||
|
x = nx;
|
||||||
|
y = ny;
|
||||||
|
z = nz;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getMagnitude() {
|
||||||
|
return sqrt(x*x + y*y + z*z);
|
||||||
|
}
|
||||||
|
|
||||||
|
void normalize() {
|
||||||
|
float m = getMagnitude();
|
||||||
|
x /= m;
|
||||||
|
y /= m;
|
||||||
|
z /= m;
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorInt16 getNormalized() {
|
||||||
|
VectorInt16 r(x, y, z);
|
||||||
|
r.normalize();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rotate(Quaternion *q) {
|
||||||
|
// http://www.cprogramming.com/tutorial/3d/quaternions.html
|
||||||
|
// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/transforms/index.htm
|
||||||
|
// http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation
|
||||||
|
// ^ or: http://webcache.googleusercontent.com/search?q=cache:xgJAp3bDNhQJ:content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation&hl=en&gl=us&strip=1
|
||||||
|
|
||||||
|
// P_out = q * P_in * conj(q)
|
||||||
|
// - P_out is the output vector
|
||||||
|
// - q is the orientation quaternion
|
||||||
|
// - P_in is the input vector (a*aReal)
|
||||||
|
// - conj(q) is the conjugate of the orientation quaternion (q=[w,x,y,z], q*=[w,-x,-y,-z])
|
||||||
|
Quaternion p(0, x, y, z);
|
||||||
|
|
||||||
|
// quaternion multiplication: q * p, stored back in p
|
||||||
|
p = q -> getProduct(p);
|
||||||
|
|
||||||
|
// quaternion multiplication: p * conj(q), stored back in p
|
||||||
|
p = p.getProduct(q -> getConjugate());
|
||||||
|
|
||||||
|
// p quaternion is now [0, x', y', z']
|
||||||
|
x = p.x;
|
||||||
|
y = p.y;
|
||||||
|
z = p.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorInt16 getRotated(Quaternion *q) {
|
||||||
|
VectorInt16 r(x, y, z);
|
||||||
|
r.rotate(q);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class VectorFloat {
|
||||||
|
public:
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
|
||||||
|
VectorFloat() {
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
z = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorFloat(float nx, float ny, float nz) {
|
||||||
|
x = nx;
|
||||||
|
y = ny;
|
||||||
|
z = nz;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getMagnitude() {
|
||||||
|
return sqrt(x*x + y*y + z*z);
|
||||||
|
}
|
||||||
|
|
||||||
|
void normalize() {
|
||||||
|
float m = getMagnitude();
|
||||||
|
x /= m;
|
||||||
|
y /= m;
|
||||||
|
z /= m;
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorFloat getNormalized() {
|
||||||
|
VectorFloat r(x, y, z);
|
||||||
|
r.normalize();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rotate(Quaternion *q) {
|
||||||
|
Quaternion p(0, x, y, z);
|
||||||
|
|
||||||
|
// quaternion multiplication: q * p, stored back in p
|
||||||
|
p = q -> getProduct(p);
|
||||||
|
|
||||||
|
// quaternion multiplication: p * conj(q), stored back in p
|
||||||
|
p = p.getProduct(q -> getConjugate());
|
||||||
|
|
||||||
|
// p quaternion is now [0, x', y', z']
|
||||||
|
x = p.x;
|
||||||
|
y = p.y;
|
||||||
|
z = p.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorFloat getRotated(Quaternion *q) {
|
||||||
|
VectorFloat r(x, y, z);
|
||||||
|
r.rotate(q);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _HELPER_3DMATH_H_ */
|
@ -15,34 +15,46 @@ include_dir = lib
|
|||||||
[common_env_data]
|
[common_env_data]
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
platform = espressif8266
|
#platform = espressif8266 @ 2.6.3
|
||||||
|
platform = espressif8266 @ 3.2.0
|
||||||
framework = arduino
|
framework = arduino
|
||||||
board = d1_mini
|
board = d1_mini
|
||||||
build_unflags =
|
build_unflags =
|
||||||
build_flags = #-O0 -Wl,-Map,output.map
|
#src_build_flags = -Wunused-variable -Wregister -Wchar-subscripts
|
||||||
|
build_flags =
|
||||||
-D BAUD=${common_env_data.monitor_speed}
|
-D BAUD=${common_env_data.monitor_speed}
|
||||||
-D ACTIVATE_OTA
|
-D ACTIVATE_OTA
|
||||||
#-D USE_GYRO_TEMP # If this is enabled the DS18 will not be used, temp is read from the gyro.
|
#-D USE_GYRO_TEMP # If this is enabled the DS18 will not be used, temp is read from the gyro.
|
||||||
#-D DEBUG_ESP_HTTP_CLIENT
|
#-D DEBUG_ESP_HTTP_CLIENT
|
||||||
#-D DEBUG_ESP_HTTP_SERVER
|
#-D DEBUG_ESP_HTTP_SERVER
|
||||||
#-D DEBUG_ESP_PORT=Serial
|
#-D DEBUG_ESP_PORT=Serial
|
||||||
|
#-D DEBUG_ESP_WIFI
|
||||||
|
#-D DEBUG_ESP_CORE
|
||||||
#-D SKIP_SLEEPMODE
|
#-D SKIP_SLEEPMODE
|
||||||
#-D DOUBLERESETDETECTOR_DEBUG true
|
#-D DOUBLERESETDETECTOR_DEBUG true
|
||||||
|
-D CFG_DISABLE_LOGGING # Turn off verbose logging for some of the parts (too much will cause a crash) but also add space
|
||||||
|
-D GYRO_DISABLE_LOGGING
|
||||||
|
-D PUSH_DISABLE_LOGGING
|
||||||
|
-D TSEN_DISABLE_LOGGING
|
||||||
|
-D WEB_DISABLE_LOGGING
|
||||||
|
#-D MAIN_DISABLE_LOGGING
|
||||||
-D USE_LITTLEFS=true
|
-D USE_LITTLEFS=true
|
||||||
-D EMBED_HTML # If this is not used the html files needs to be on the file system (can be uploaded)
|
-D 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="\"0.4.0\""
|
-D CFG_APPVER="\"0.5.0\""
|
||||||
lib_deps =
|
lib_deps = # Switched to forks for better version control.
|
||||||
# https://github.com/jrowberg/i2cdevlib.git # Using local copy of this library
|
# Using local copy of this library
|
||||||
https://github.com/codeplea/tinyexpr
|
#https://github.com/mp-se/i2cdevlib # https://github.com/jrowberg/i2cdevlib.git
|
||||||
https://github.com/graphitemaster/incbin
|
https://github.com/mp-se/tinyexpr # https://github.com/codeplea/tinyexpr
|
||||||
https://github.com/khoih-prog/ESP_DoubleResetDetector
|
https://github.com/mp-se/incbin # https://github.com/graphitemaster/incbin
|
||||||
https://github.com/tzapu/WiFiManager
|
https://github.com/mp-se/ESP_DoubleResetDetector#v1.2.1 # https://github.com/khoih-prog/ESP_DoubleResetDetector
|
||||||
https://github.com/thijse/Arduino-Log
|
https://github.com/mp-se/WiFiManager#2.0.5-beta # https://github.com/tzapu/WiFiManager
|
||||||
https://github.com/bblanchon/ArduinoJson
|
https://github.com/mp-se/Arduino-Log#1.1.1 # https://github.com/thijse/Arduino-Log
|
||||||
https://github.com/PaulStoffregen/OneWire
|
https://github.com/mp-se/ArduinoJson#v6.18.5 # https://github.com/bblanchon/ArduinoJson
|
||||||
https://github.com/milesburton/Arduino-Temperature-Control-Library
|
https://github.com/mp-se/OneWire#v2.3.6 # https://github.com/PaulStoffregen/OneWire
|
||||||
|
https://github.com/mp-se/Arduino-Temperature-Control-Library#3.9.1 # https://github.com/milesburton/Arduino-Temperature-Control-Library
|
||||||
|
https://github.com/mp-se/arduinoCurveFitting#v1.0.6 # https://github.com/Rotario/arduinoCurveFitting
|
||||||
|
|
||||||
[env:gravity-debug]
|
[env:gravity-debug]
|
||||||
upload_speed = ${common_env_data.upload_speed}
|
upload_speed = ${common_env_data.upload_speed}
|
||||||
@ -50,18 +62,24 @@ monitor_speed = ${common_env_data.monitor_speed}
|
|||||||
framework = ${common_env_data.framework}
|
framework = ${common_env_data.framework}
|
||||||
platform = ${common_env_data.platform}
|
platform = ${common_env_data.platform}
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
|
script/copy_html.py
|
||||||
script/copy_firmware.py
|
script/copy_firmware.py
|
||||||
script/create_versionjson.py
|
script/create_versionjson.py
|
||||||
build_unflags = ${common_env_data.build_unflags}
|
build_unflags =
|
||||||
|
${common_env_data.build_unflags}
|
||||||
build_flags =
|
build_flags =
|
||||||
${common_env_data.build_flags}
|
#-D PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS
|
||||||
-D COLLECT_PERFDATA # This option will collect runtime data for a few defined methods to measure time, dumped to serial and/or influxdb
|
#-D SKIP_SLEEPMODE
|
||||||
-D LOG_LEVEL=6 # Maximum log level for the debug build.
|
${common_env_data.build_flags}
|
||||||
|
-D COLLECT_PERFDATA # This option will collect runtime data for a few defined methods to measure time, dumped to serial and/or influxdb
|
||||||
|
-D LOG_LEVEL=6 # Maximum log level for the debug build.
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common_env_data.lib_deps}
|
${common_env_data.lib_deps}
|
||||||
board = ${common_env_data.board}
|
#board = ${common_env_data.board}
|
||||||
|
board = nodemcuv2
|
||||||
build_type = debug
|
build_type = debug
|
||||||
board_build.filesystem = littlefs
|
board_build.filesystem = littlefs
|
||||||
|
monitor_filters = esp8266_exception_decoder
|
||||||
|
|
||||||
[env:gravity-release]
|
[env:gravity-release]
|
||||||
upload_speed = ${common_env_data.upload_speed}
|
upload_speed = ${common_env_data.upload_speed}
|
||||||
@ -69,6 +87,7 @@ monitor_speed = ${common_env_data.monitor_speed}
|
|||||||
framework = ${common_env_data.framework}
|
framework = ${common_env_data.framework}
|
||||||
platform = ${common_env_data.platform}
|
platform = ${common_env_data.platform}
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
|
script/copy_html.py
|
||||||
script/copy_firmware.py
|
script/copy_firmware.py
|
||||||
script/create_versionjson.py
|
script/create_versionjson.py
|
||||||
build_unflags = ${common_env_data.build_unflags}
|
build_unflags = ${common_env_data.build_unflags}
|
||||||
@ -87,6 +106,7 @@ monitor_speed = ${common_env_data.monitor_speed}
|
|||||||
framework = ${common_env_data.framework}
|
framework = ${common_env_data.framework}
|
||||||
platform = ${common_env_data.platform}
|
platform = ${common_env_data.platform}
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
|
script/copy_html.py
|
||||||
script/copy_firmware.py
|
script/copy_firmware.py
|
||||||
script/create_versionjson.py
|
script/create_versionjson.py
|
||||||
build_unflags = ${common_env_data.build_unflags}
|
build_unflags = ${common_env_data.build_unflags}
|
||||||
|
@ -11,13 +11,13 @@ def after_build(source, target, env):
|
|||||||
print( "Executing custom step " )
|
print( "Executing custom step " )
|
||||||
dir = env.GetLaunchDir()
|
dir = env.GetLaunchDir()
|
||||||
name = env.get( "PIOENV" )
|
name = env.get( "PIOENV" )
|
||||||
source = dir + "\\.pio\\build\\" + name + "\\firmware.bin"
|
source = dir + "/.pio/build/" + name + "/firmware.bin"
|
||||||
if name == "gravity-debug" :
|
if name == "gravity-debug" :
|
||||||
target = dir + "\\bin\\firmware-debug.bin"
|
target = dir + "/bin/firmware-debug.bin"
|
||||||
if name == "gravity-release" :
|
if name == "gravity-release" :
|
||||||
target = dir + "\\bin\\firmware.bin"
|
target = dir + "/bin/firmware.bin"
|
||||||
if name == "gravity-perf" :
|
if name == "gravity-perf" :
|
||||||
target = dir + "\\bin\\firmware-perf.bin"
|
target = dir + "/bin/firmware-perf.bin"
|
||||||
print( "Copy file : " + source + " -> " + target )
|
print( "Copy file : " + source + " -> " + target )
|
||||||
shutil.copyfile( source, target )
|
shutil.copyfile( source, target )
|
||||||
|
|
||||||
|
28
script/copy_html.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
Import("env")
|
||||||
|
import shutil, os
|
||||||
|
|
||||||
|
print( "Executing custom step " )
|
||||||
|
dir = env.GetLaunchDir()
|
||||||
|
source = dir + "/html/"
|
||||||
|
target = dir + "/data/"
|
||||||
|
print( "Copy html-files from " + source + " -> " + target )
|
||||||
|
|
||||||
|
os.makedirs(os.path.dirname( target ), exist_ok=True)
|
||||||
|
file = "about.min.htm"
|
||||||
|
#print( "Copy file: " + source + file + "->" + target + file)
|
||||||
|
shutil.copyfile( source + file, target + file )
|
||||||
|
file = "calibration.min.htm"
|
||||||
|
#print( "Copy file: " + source + file + "->" + target + file)
|
||||||
|
shutil.copyfile( source + file, target + file )
|
||||||
|
file = "config.min.htm"
|
||||||
|
#print( "Copy file: " + source + file + "->" + target + file)
|
||||||
|
shutil.copyfile( source + file, target + file )
|
||||||
|
file = "device.min.htm"
|
||||||
|
#print( "Copy file: " + source + file + "->" + target + file)
|
||||||
|
shutil.copyfile( source + file, target + file )
|
||||||
|
file = "index.min.htm"
|
||||||
|
#print( "Copy file: " + source + file + "->" + target + file)
|
||||||
|
shutil.copyfile( source + file, target + file )
|
||||||
|
file = "upload.min.htm"
|
||||||
|
#print( "Copy file: " + source + file + "->" + target + file)
|
||||||
|
shutil.copyfile( source + file, target + file )
|
@ -13,38 +13,44 @@ def after_build(source, target, env):
|
|||||||
#name = env.get( "PIOENV" )
|
#name = env.get( "PIOENV" )
|
||||||
|
|
||||||
# Copy file 1
|
# Copy file 1
|
||||||
source = dir + "\\data\\index.min.htm"
|
source = dir + "/data/index.min.htm"
|
||||||
target = dir + "\\bin\\index.min.htm"
|
target = dir + "/bin/index.min.htm"
|
||||||
print( "Copy file : " + source + " -> " + target )
|
print( "Copy file : " + source + " -> " + target )
|
||||||
shutil.copyfile( source, target )
|
shutil.copyfile( source, target )
|
||||||
|
|
||||||
# Copy file 2
|
# Copy file 2
|
||||||
source = dir + "\\data\\device.min.htm"
|
source = dir + "/data/device.min.htm"
|
||||||
target = dir + "\\bin\\device.min.htm"
|
target = dir + "/bin/device.min.htm"
|
||||||
print( "Copy file : " + source + " -> " + target )
|
print( "Copy file : " + source + " -> " + target )
|
||||||
shutil.copyfile( source, target )
|
shutil.copyfile( source, target )
|
||||||
|
|
||||||
# Copy file 3
|
# Copy file 3
|
||||||
source = dir + "\\data\\config.min.htm"
|
source = dir + "/data/config.min.htm"
|
||||||
target = dir + "\\bin\\config.min.htm"
|
target = dir + "/bin/config.min.htm"
|
||||||
print( "Copy file : " + source + " -> " + target )
|
print( "Copy file : " + source + " -> " + target )
|
||||||
shutil.copyfile( source, target )
|
shutil.copyfile( source, target )
|
||||||
|
|
||||||
# Copy file 4
|
# Copy file 4
|
||||||
source = dir + "\\data\\about.min.htm"
|
source = dir + "/data/about.min.htm"
|
||||||
target = dir + "\\bin\\about.min.htm"
|
target = dir + "/bin/about.min.htm"
|
||||||
print( "Copy file : " + source + " -> " + target )
|
print( "Copy file : " + source + " -> " + target )
|
||||||
shutil.copyfile( source, target )
|
shutil.copyfile( source, target )
|
||||||
|
|
||||||
target = dir + "\\bin\\version.json"
|
# Copy file 5
|
||||||
|
source = dir + "/data/calibration.min.htm"
|
||||||
|
target = dir + "/bin/calibration.min.htm"
|
||||||
|
print( "Copy file : " + source + " -> " + target )
|
||||||
|
shutil.copyfile( source, target )
|
||||||
|
|
||||||
|
target = dir + "/bin/version.json"
|
||||||
ver = get_build_flag_value("CFG_APPVER")
|
ver = get_build_flag_value("CFG_APPVER")
|
||||||
|
|
||||||
print( "Creating version.json" )
|
print( "Creating version.json" )
|
||||||
f = open( target, "w" )
|
f = open( target, "w" )
|
||||||
f.write( "{ \"project\":\"gravmon\", \"version\":" + ver + ", " )
|
f.write( "{ \"project\":\"gravmon\", \"version\":" + ver + ", " )
|
||||||
f.write( " \"html\": [ \"index.min.htm\", \"device.min.htm\", \"config.min.htm\", \"about.min.htm\" ] }" )
|
f.write( " \"html\": [ \"index.min.htm\", \"device.min.htm\", \"config.min.htm\", \"calibration.min.htm\", \"about.min.htm\" ] }" )
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
print( "Adding custom build step (create json):")
|
print( "Adding custom build step (create json):")
|
||||||
env.AddPreAction("buildprog", after_build)
|
env.AddPostAction("buildprog", after_build)
|
||||||
|
209
src/calc.cpp
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 Magnus
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -21,78 +21,183 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "calc.h"
|
#include <curveFitting.h>
|
||||||
#include "helper.h"
|
#include <tinyexpr.h>
|
||||||
#include "config.h"
|
|
||||||
#include "tinyexpr.h"
|
#include <calc.hpp>
|
||||||
#include "tempsensor.h"
|
#include <config.hpp>
|
||||||
|
#include <helper.hpp>
|
||||||
|
#include <tempsensor.hpp>
|
||||||
|
|
||||||
|
#define FORMULA_MAX_DEVIATION 1.5
|
||||||
|
|
||||||
//
|
//
|
||||||
// Calculates gravity according to supplied formula, compatible with iSpindle/Fermentrack formula
|
// Use values to derive a formula
|
||||||
//
|
//
|
||||||
double calculateGravity( double angle, double temp ) {
|
int createFormula(RawFormulaData &fd, char *formulaBuffer,
|
||||||
const char* formula = myConfig.getGravityFormula();
|
int formulaBufferSize, int order) {
|
||||||
#if LOG_LEVEL==6
|
int noAngles = 0;
|
||||||
Log.verbose(F("CALC: Calculating gravity for angle %F, temp %F." CR), angle, temp);
|
|
||||||
Log.verbose(F("CALC: Formula %s." CR), formula);
|
// Check how many valid values we have got
|
||||||
|
if (fd.a[0] > 0 && fd.a[1] > 0 && fd.a[2] > 0 && fd.a[3] > 0 && fd.a[4] > 0)
|
||||||
|
noAngles = 5;
|
||||||
|
else if (fd.a[0] > 0 && fd.a[1] > 0 && fd.a[2] > 0 && fd.a[3] > 0)
|
||||||
|
noAngles = 4;
|
||||||
|
else if (fd.a[0] > 0 && fd.a[1] > 0 && fd.a[2] > 0)
|
||||||
|
noAngles = 3;
|
||||||
|
|
||||||
|
#if LOG_LEVEL == 6
|
||||||
|
Log.verbose(
|
||||||
|
F("CALC: Trying to create formula using order = %d, found %d angles" CR),
|
||||||
|
order, noAngles);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if( strlen(formula) == 0 )
|
if (!noAngles) {
|
||||||
return 0.0;
|
Log.error(F("CALC: Not enough values for deriving formula" CR));
|
||||||
|
return ERR_FORMULA_NOTENOUGHVALUES;
|
||||||
|
} else {
|
||||||
|
double coeffs[order + 1];
|
||||||
|
int ret = fitCurve(order, noAngles, fd.a, fd.g,
|
||||||
|
sizeof(coeffs) / sizeof(double), coeffs);
|
||||||
|
|
||||||
// Store variable names and pointers.
|
// Returned value is 0 if no error
|
||||||
te_variable vars[] = {{"tilt", &angle}, {"temp", &temp}};
|
if (ret == 0) {
|
||||||
|
#if LOG_LEVEL == 6
|
||||||
int err;
|
Log.verbose(F("CALC: Finshied processing data points." CR));
|
||||||
// Compile the expression with variables.
|
|
||||||
te_expr *expr = te_compile(formula, vars, 2, &err);
|
|
||||||
|
|
||||||
if (expr) {
|
|
||||||
double g = te_eval(expr);
|
|
||||||
te_free(expr);
|
|
||||||
|
|
||||||
#if LOG_LEVEL==6
|
|
||||||
Log.verbose(F("CALC: Calculated gravity is %F." CR), g);
|
|
||||||
#endif
|
#endif
|
||||||
return g;
|
|
||||||
|
// Print the formula based on 'order'
|
||||||
|
if (order == 4) {
|
||||||
|
snprintf(formulaBuffer, formulaBufferSize,
|
||||||
|
"%.8f*tilt^4+%.8f*tilt^3+%.8f*tilt^2+%.8f*tilt+%.8f",
|
||||||
|
coeffs[0], coeffs[1], coeffs[2], coeffs[3], coeffs[4]);
|
||||||
|
} else if (order == 3) {
|
||||||
|
snprintf(formulaBuffer, formulaBufferSize,
|
||||||
|
"%.8f*tilt^3+%.8f*tilt^2+%.8f*tilt+%.8f", coeffs[0], coeffs[1],
|
||||||
|
coeffs[2], coeffs[3]);
|
||||||
|
} else if (order == 2) {
|
||||||
|
snprintf(formulaBuffer, formulaBufferSize, "%.8f*tilt^2+%.8f*tilt+%.8f",
|
||||||
|
coeffs[0], coeffs[1], coeffs[2]);
|
||||||
|
} else { // order == 1
|
||||||
|
snprintf(formulaBuffer, formulaBufferSize, "%.8f*tilt+%.8f", coeffs[0],
|
||||||
|
coeffs[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LOG_LEVEL == 6
|
||||||
|
Log.verbose(F("CALC: Formula: %s" CR), formulaBuffer);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool valid = true;
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
if (fd.a[i] == 0 && valid) break;
|
||||||
|
|
||||||
|
double g = calculateGravity(fd.a[i], 0, formulaBuffer);
|
||||||
|
double dev = (g - fd.g[i]) < 0 ? (fd.g[i] - g) : (g - fd.g[i]);
|
||||||
|
|
||||||
|
// If the deviation is more than 2 degress we mark it as failed.
|
||||||
|
if (dev * 1000 > FORMULA_MAX_DEVIATION) valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
|
Log.error(F("CALC: Deviation to large, formula rejected." CR));
|
||||||
|
return ERR_FORMULA_UNABLETOFFIND;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.info(F("CALC: Found formula '%s'." CR), formulaBuffer);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Log.error(F("CALC: Failed to parse expression %d." CR), err);
|
Log.error(F("CALC: Internal error finding formula." CR));
|
||||||
return 0;
|
return ERR_FORMULA_INTERNAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Do a standard gravity temperature correction. This is a simple way to adjust for differnt worth temperatures
|
// Calculates gravity according to supplied formula, compatible with
|
||||||
|
// iSpindle/Fermentrack formula
|
||||||
//
|
//
|
||||||
double gravityTemperatureCorrection( double gravity, double temp, char tempFormat, double calTemp) {
|
double calculateGravity(double angle, double temp, const char *tempFormula) {
|
||||||
#if LOG_LEVEL==6
|
const char *formula = myConfig.getGravityFormula();
|
||||||
Log.verbose(F("CALC: Adjusting gravity based on temperature, gravity %F, temp %F, calTemp %F." CR), gravity, temp, calTemp);
|
|
||||||
|
if (tempFormula != 0) {
|
||||||
|
#if LOG_LEVEL == 6
|
||||||
|
Log.verbose(F("CALC: Using temporary formula." CR));
|
||||||
|
#endif
|
||||||
|
formula = tempFormula;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LOG_LEVEL == 6
|
||||||
|
Log.verbose(F("CALC: Calculating gravity for angle %F, temp %F." CR), angle,
|
||||||
|
temp);
|
||||||
|
Log.verbose(F("CALC: Formula %s." CR), formula);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if( tempFormat == 'C')
|
if (strlen(formula) == 0) return 0.0;
|
||||||
temp = convertCtoF( temp );
|
|
||||||
double calTempF = convertCtoF(calTemp); // calTemp is in C
|
|
||||||
const char* formula = "gravity*((1.00130346-0.000134722124*temp+0.00000204052596*temp^2-0.00000000232820948*temp^3)/(1.00130346-0.000134722124*cal+0.00000204052596*cal^2-0.00000000232820948*cal^3))";
|
|
||||||
|
|
||||||
// Store variable names and pointers.
|
// Store variable names and pointers.
|
||||||
te_variable vars[] = {{"gravity", &gravity}, {"temp", &temp}, {"cal", &calTempF}};
|
te_variable vars[] = {{"tilt", &angle}, {"temp", &temp}};
|
||||||
|
|
||||||
int err;
|
int err;
|
||||||
// Compile the expression with variables.
|
// Compile the expression with variables.
|
||||||
te_expr *expr = te_compile(formula, vars, 3, &err);
|
te_expr *expr = te_compile(formula, vars, 2, &err);
|
||||||
|
|
||||||
if (expr) {
|
if (expr) {
|
||||||
double g = te_eval(expr);
|
double g = te_eval(expr);
|
||||||
te_free(expr);
|
te_free(expr);
|
||||||
|
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6
|
||||||
Log.verbose(F("CALC: Corrected gravity is %F." CR), g);
|
Log.verbose(F("CALC: Calculated gravity is %F." CR), g);
|
||||||
#endif
|
#endif
|
||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.error(F("CALC: Failed to parse expression %d, no correction has been made." CR), err);
|
Log.error(F("CALC: Failed to parse expression %d." CR), err);
|
||||||
return gravity;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Do a standard gravity temperature correction. This is a simple way to adjust
|
||||||
|
// for differnt worth temperatures
|
||||||
|
//
|
||||||
|
double gravityTemperatureCorrection(double gravity, double temp,
|
||||||
|
char tempFormat, double calTemp) {
|
||||||
|
#if LOG_LEVEL == 6
|
||||||
|
Log.verbose(F("CALC: Adjusting gravity based on temperature, gravity %F, "
|
||||||
|
"temp %F, calTemp %F." CR),
|
||||||
|
gravity, temp, calTemp);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (tempFormat == 'C') temp = convertCtoF(temp);
|
||||||
|
double calTempF = convertCtoF(calTemp); // calTemp is in C
|
||||||
|
const char *formula =
|
||||||
|
"gravity*((1.00130346-0.000134722124*temp+0.00000204052596*temp^2-0."
|
||||||
|
"00000000232820948*temp^3)/"
|
||||||
|
"(1.00130346-0.000134722124*cal+0.00000204052596*cal^2-0."
|
||||||
|
"00000000232820948*cal^3))";
|
||||||
|
|
||||||
|
// Store variable names and pointers.
|
||||||
|
te_variable vars[] = {
|
||||||
|
{"gravity", &gravity}, {"temp", &temp}, {"cal", &calTempF}};
|
||||||
|
|
||||||
|
int err;
|
||||||
|
// Compile the expression with variables.
|
||||||
|
te_expr *expr = te_compile(formula, vars, 3, &err);
|
||||||
|
|
||||||
|
if (expr) {
|
||||||
|
double g = te_eval(expr);
|
||||||
|
te_free(expr);
|
||||||
|
|
||||||
|
#if LOG_LEVEL == 6
|
||||||
|
Log.verbose(F("CALC: Corrected gravity is %F." CR), g);
|
||||||
|
#endif
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.error(
|
||||||
|
F("CALC: Failed to parse expression %d, no correction has been made." CR),
|
||||||
|
err);
|
||||||
|
return gravity;
|
||||||
}
|
}
|
||||||
|
|
||||||
// EOF
|
// EOF
|
44
src/calc.hpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
|
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 above 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.
|
||||||
|
*/
|
||||||
|
#ifndef SRC_CALC_HPP_
|
||||||
|
#define SRC_CALC_HPP_
|
||||||
|
|
||||||
|
// Includes
|
||||||
|
#include <config.hpp>
|
||||||
|
#include <helper.hpp>
|
||||||
|
|
||||||
|
#define ERR_FORMULA_NOTENOUGHVALUES -1
|
||||||
|
#define ERR_FORMULA_INTERNAL -2
|
||||||
|
#define ERR_FORMULA_UNABLETOFFIND -3
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
double calculateGravity(double angle, double temp, const char *tempFormula = 0);
|
||||||
|
double gravityTemperatureCorrection(double gravity, double temp,
|
||||||
|
char tempFormat, double calTemp = 20);
|
||||||
|
int createFormula(RawFormulaData &fd, char *formulaBuffer,
|
||||||
|
int formulaBufferSize, int order);
|
||||||
|
|
||||||
|
#endif // SRC_CALC_HPP_
|
||||||
|
|
||||||
|
// EOF
|
411
src/config.cpp
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 Magnus
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -21,247 +21,294 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "config.h"
|
|
||||||
#include "helper.h"
|
|
||||||
#include <LittleFS.h>
|
#include <LittleFS.h>
|
||||||
|
|
||||||
|
#include <config.hpp>
|
||||||
|
#include <helper.hpp>
|
||||||
|
|
||||||
Config myConfig;
|
Config myConfig;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Create the config class with default settings.
|
// Create the config class with default settings.
|
||||||
//
|
//
|
||||||
Config::Config() {
|
Config::Config() {
|
||||||
// Assiging default values
|
// Assiging default values
|
||||||
char buf[20];
|
char buf[30];
|
||||||
sprintf(&buf[0], "%6x", (unsigned int) ESP.getChipId() );
|
snprintf(&buf[0], sizeof(buf), "%6x", (unsigned int)ESP.getChipId());
|
||||||
id = &buf[0];
|
id = String(&buf[0]);
|
||||||
sprintf(&buf[0], "" WIFI_MDNS "%s", getID() );
|
snprintf(&buf[0], sizeof(buf), "" WIFI_MDNS "%s", getID());
|
||||||
mDNS = &buf[0];
|
mDNS = String(&buf[0]);
|
||||||
setTempFormat('C');
|
|
||||||
setGravityFormat('G');
|
#if LOG_LEVEL == 6 && !defined(CFG_DISABLE_LOGGING)
|
||||||
setSleepInterval(900); // 15 minutes
|
Log.verbose(F("CFG : Created config for %s (%s)." CR), id.c_str(),
|
||||||
setVoltageFactor(1.59); // Conversion factor for battery
|
mDNS.c_str());
|
||||||
setTempSensorAdj(0.0);
|
#endif
|
||||||
setGravityTempAdj(false);
|
|
||||||
gyroCalibration = { 0, 0, 0, 0, 0 ,0 };
|
setTempFormat('C');
|
||||||
saveNeeded = false;
|
setGravityFormat('G');
|
||||||
|
setSleepInterval(900); // 15 minutes
|
||||||
|
setVoltageFactor(1.59); // Conversion factor for battery
|
||||||
|
setTempSensorAdj(0.0);
|
||||||
|
setGravityTempAdj(false);
|
||||||
|
gyroCalibration = {0, 0, 0, 0, 0, 0};
|
||||||
|
formulaData = {{0, 0, 0, 0, 0}, {1, 1, 1, 1, 1}};
|
||||||
|
saveNeeded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Populate the json document with all configuration parameters (used in both web and saving to file)
|
// Populate the json document with all configuration parameters (used in both
|
||||||
|
// web and saving to file)
|
||||||
//
|
//
|
||||||
void Config::createJson(DynamicJsonDocument& doc) {
|
void Config::createJson(DynamicJsonDocument& doc) {
|
||||||
doc[ CFG_PARAM_MDNS ] = getMDNS();
|
doc[CFG_PARAM_MDNS] = getMDNS();
|
||||||
doc[ CFG_PARAM_ID ] = getID();
|
doc[CFG_PARAM_ID] = getID();
|
||||||
doc[ CFG_PARAM_OTA ] = getOtaURL();
|
doc[CFG_PARAM_OTA] = getOtaURL();
|
||||||
doc[ CFG_PARAM_TEMPFORMAT ] = String( getTempFormat() );
|
doc[CFG_PARAM_SSID] = getWifiSSID();
|
||||||
doc[ CFG_PARAM_PUSH_BREWFATHER ] = getBrewfatherPushUrl();
|
doc[CFG_PARAM_PASS] = getWifiPass();
|
||||||
doc[ CFG_PARAM_PUSH_HTTP ] = getHttpPushUrl();
|
doc[CFG_PARAM_TEMPFORMAT] = String(getTempFormat());
|
||||||
doc[ CFG_PARAM_PUSH_HTTP2 ] = getHttpPushUrl2();
|
doc[CFG_PARAM_PUSH_BREWFATHER] = getBrewfatherPushUrl();
|
||||||
doc[ CFG_PARAM_PUSH_INFLUXDB2 ] = getInfluxDb2PushUrl();
|
doc[CFG_PARAM_PUSH_HTTP] = getHttpPushUrl();
|
||||||
doc[ CFG_PARAM_PUSH_INFLUXDB2_ORG ] = getInfluxDb2PushOrg();
|
doc[CFG_PARAM_PUSH_HTTP2] = getHttpPushUrl2();
|
||||||
doc[ CFG_PARAM_PUSH_INFLUXDB2_BUCKET ] = getInfluxDb2PushBucket();
|
doc[CFG_PARAM_PUSH_INFLUXDB2] = getInfluxDb2PushUrl();
|
||||||
doc[ CFG_PARAM_PUSH_INFLUXDB2_AUTH ] = getInfluxDb2PushToken();
|
doc[CFG_PARAM_PUSH_INFLUXDB2_ORG] = getInfluxDb2PushOrg();
|
||||||
doc[ CFG_PARAM_SLEEP_INTERVAL ] = getSleepInterval();
|
doc[CFG_PARAM_PUSH_INFLUXDB2_BUCKET] = getInfluxDb2PushBucket();
|
||||||
// doc[ CFG_PARAM_PUSH_INTERVAL ] = getSleepInterval(); // TODO: @deprecated
|
doc[CFG_PARAM_PUSH_INFLUXDB2_AUTH] = getInfluxDb2PushToken();
|
||||||
doc[ CFG_PARAM_VOLTAGEFACTOR ] = getVoltageFactor();
|
doc[CFG_PARAM_SLEEP_INTERVAL] = getSleepInterval();
|
||||||
doc[ CFG_PARAM_GRAVITY_FORMULA ] = getGravityFormula();
|
doc[CFG_PARAM_VOLTAGEFACTOR] = getVoltageFactor();
|
||||||
doc[ CFG_PARAM_GRAVITY_FORMAT ] = String(getGravityFormat());
|
doc[CFG_PARAM_GRAVITY_FORMULA] = getGravityFormula();
|
||||||
doc[ CFG_PARAM_TEMP_ADJ ] = getTempSensorAdj();
|
doc[CFG_PARAM_GRAVITY_FORMAT] = String(getGravityFormat());
|
||||||
doc[ CFG_PARAM_GRAVITY_TEMP_ADJ ] = isGravityTempAdj();
|
doc[CFG_PARAM_TEMP_ADJ] = getTempSensorAdj();
|
||||||
|
doc[CFG_PARAM_GRAVITY_TEMP_ADJ] = isGravityTempAdj();
|
||||||
|
|
||||||
JsonObject cal = doc.createNestedObject( CFG_PARAM_GYRO_CALIBRATION );
|
JsonObject cal = doc.createNestedObject(CFG_PARAM_GYRO_CALIBRATION);
|
||||||
cal["ax"] = gyroCalibration.ax;
|
cal["ax"] = gyroCalibration.ax;
|
||||||
cal["ay"] = gyroCalibration.ay;
|
cal["ay"] = gyroCalibration.ay;
|
||||||
cal["az"] = gyroCalibration.az;
|
cal["az"] = gyroCalibration.az;
|
||||||
cal["gx"] = gyroCalibration.gx;
|
cal["gx"] = gyroCalibration.gx;
|
||||||
cal["gy"] = gyroCalibration.gy;
|
cal["gy"] = gyroCalibration.gy;
|
||||||
cal["gz"] = gyroCalibration.gz;
|
cal["gz"] = gyroCalibration.gz;
|
||||||
|
|
||||||
|
JsonObject cal2 = doc.createNestedObject(CFG_PARAM_FORMULA_DATA);
|
||||||
|
cal2["a1"] = reduceFloatPrecision(formulaData.a[0], 2);
|
||||||
|
cal2["a2"] = reduceFloatPrecision(formulaData.a[1], 2);
|
||||||
|
cal2["a3"] = reduceFloatPrecision(formulaData.a[2], 2);
|
||||||
|
cal2["a4"] = reduceFloatPrecision(formulaData.a[3], 2);
|
||||||
|
cal2["a5"] = reduceFloatPrecision(formulaData.a[4], 2);
|
||||||
|
|
||||||
|
cal2["g1"] = reduceFloatPrecision(formulaData.g[0], 4);
|
||||||
|
cal2["g2"] = reduceFloatPrecision(formulaData.g[1], 4);
|
||||||
|
cal2["g3"] = reduceFloatPrecision(formulaData.g[2], 4);
|
||||||
|
cal2["g4"] = reduceFloatPrecision(formulaData.g[3], 4);
|
||||||
|
cal2["g5"] = reduceFloatPrecision(formulaData.g[4], 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Save json document to file
|
// Save json document to file
|
||||||
//
|
//
|
||||||
bool Config::saveFile() {
|
bool Config::saveFile() {
|
||||||
if( !saveNeeded ) {
|
if (!saveNeeded) {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(CFG_DISABLE_LOGGING)
|
||||||
Log.verbose(F("CFG : Skipping save, not needed." CR));
|
Log.verbose(F("CFG : Skipping save, not needed." CR));
|
||||||
#endif
|
#endif
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LOG_LEVEL==6
|
|
||||||
Log.verbose(F("CFG : Saving configuration to file." CR));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
File configFile = LittleFS.open(CFG_FILENAME, "w");
|
|
||||||
|
|
||||||
if (!configFile) {
|
|
||||||
Log.error(F("CFG : Failed to open file " CFG_FILENAME " for save." CR));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DynamicJsonDocument doc(CFG_JSON_BUFSIZE);
|
|
||||||
createJson( doc );
|
|
||||||
#if LOG_LEVEL==6
|
|
||||||
serializeJson(doc, Serial);
|
|
||||||
Serial.print( CR );
|
|
||||||
#endif
|
|
||||||
serializeJson(doc, configFile);
|
|
||||||
configFile.flush();
|
|
||||||
configFile.close();
|
|
||||||
|
|
||||||
saveNeeded = false;
|
|
||||||
myConfig.debug();
|
|
||||||
Log.notice(F("CFG : Configuration saved to " CFG_FILENAME "." CR));
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LOG_LEVEL == 6 && !defined(CFG_DISABLE_LOGGING)
|
||||||
|
Log.verbose(F("CFG : Saving configuration to file." CR));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
File configFile = LittleFS.open(CFG_FILENAME, "w");
|
||||||
|
|
||||||
|
if (!configFile) {
|
||||||
|
Log.error(F("CFG : Failed to open file " CFG_FILENAME " for save." CR));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicJsonDocument doc(CFG_JSON_BUFSIZE);
|
||||||
|
createJson(doc);
|
||||||
|
|
||||||
|
#if LOG_LEVEL == 6 && !defined(CFG_DISABLE_LOGGING)
|
||||||
|
serializeJson(doc, Serial);
|
||||||
|
Serial.print(CR);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
serializeJson(doc, configFile);
|
||||||
|
configFile.flush();
|
||||||
|
configFile.close();
|
||||||
|
|
||||||
|
saveNeeded = false;
|
||||||
|
myConfig.debug();
|
||||||
|
Log.notice(F("CFG : Configuration saved to " CFG_FILENAME "." CR));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Load config file from disk
|
// Load config file from disk
|
||||||
//
|
//
|
||||||
bool Config::loadFile() {
|
bool Config::loadFile() {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(CFG_DISABLE_LOGGING)
|
||||||
Log.verbose(F("CFG : Loading configuration from file." CR));
|
Log.verbose(F("CFG : Loading configuration from file." CR));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!LittleFS.exists(CFG_FILENAME)) {
|
if (!LittleFS.exists(CFG_FILENAME)) {
|
||||||
Log.error(F("CFG : Configuration file does not exist " CFG_FILENAME "." CR));
|
Log.error(
|
||||||
return false;
|
F("CFG : Configuration file does not exist " CFG_FILENAME "." CR));
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
File configFile = LittleFS.open(CFG_FILENAME, "r");
|
File configFile = LittleFS.open(CFG_FILENAME, "r");
|
||||||
|
|
||||||
if (!configFile) {
|
if (!configFile) {
|
||||||
Log.error(F("CFG : Failed to open " CFG_FILENAME "." CR));
|
Log.error(F("CFG : Failed to open " CFG_FILENAME "." CR));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.notice(F("CFG : Size of configuration file=%d bytes." CR), configFile.size() );
|
Log.notice(F("CFG : Size of configuration file=%d bytes." CR),
|
||||||
|
configFile.size());
|
||||||
|
|
||||||
DynamicJsonDocument doc(CFG_JSON_BUFSIZE);
|
DynamicJsonDocument doc(CFG_JSON_BUFSIZE);
|
||||||
DeserializationError err = deserializeJson(doc, configFile);
|
DeserializationError err = deserializeJson(doc, configFile);
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6
|
||||||
serializeJson(doc, Serial);
|
serializeJson(doc, Serial);
|
||||||
Serial.print( CR );
|
Serial.print(CR);
|
||||||
#endif
|
#endif
|
||||||
configFile.close();
|
configFile.close();
|
||||||
|
|
||||||
if( err ) {
|
if (err) {
|
||||||
Log.error(F("CFG : Failed to parse " CFG_FILENAME " file, Err: %s, %d." CR), err.c_str(), doc.capacity());
|
Log.error(F("CFG : Failed to parse " CFG_FILENAME " file, Err: %s, %d." CR),
|
||||||
return false;
|
err.c_str(), doc.capacity());
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6
|
||||||
Log.verbose(F("CFG : Parsed configuration file." CR));
|
Log.verbose(F("CFG : Parsed configuration file." CR));
|
||||||
#endif
|
#endif
|
||||||
if( !doc[ CFG_PARAM_OTA ].isNull() )
|
if (!doc[CFG_PARAM_OTA].isNull()) setOtaURL(doc[CFG_PARAM_OTA]);
|
||||||
setOtaURL( doc[ CFG_PARAM_OTA ] );
|
if (!doc[CFG_PARAM_MDNS].isNull()) setMDNS(doc[CFG_PARAM_MDNS]);
|
||||||
if( !doc[ CFG_PARAM_MDNS ].isNull() )
|
if (!doc[CFG_PARAM_SSID].isNull()) setWifiSSID(doc[CFG_PARAM_SSID]);
|
||||||
setMDNS( doc[ CFG_PARAM_MDNS ] );
|
if (!doc[CFG_PARAM_PASS].isNull()) setWifiPass(doc[CFG_PARAM_PASS]);
|
||||||
if( !doc[ CFG_PARAM_TEMPFORMAT ].isNull() ) {
|
if (!doc[CFG_PARAM_TEMPFORMAT].isNull()) {
|
||||||
String s = doc[ CFG_PARAM_TEMPFORMAT ];
|
String s = doc[CFG_PARAM_TEMPFORMAT];
|
||||||
setTempFormat( s.charAt(0) );
|
setTempFormat(s.charAt(0));
|
||||||
}
|
}
|
||||||
if( !doc[ CFG_PARAM_PUSH_BREWFATHER ].isNull() )
|
if (!doc[CFG_PARAM_PUSH_BREWFATHER].isNull())
|
||||||
setBrewfatherPushUrl( doc[ CFG_PARAM_PUSH_BREWFATHER ] );
|
setBrewfatherPushUrl(doc[CFG_PARAM_PUSH_BREWFATHER]);
|
||||||
if( !doc[ CFG_PARAM_PUSH_HTTP ].isNull() )
|
if (!doc[CFG_PARAM_PUSH_HTTP].isNull())
|
||||||
setHttpPushUrl( doc[ CFG_PARAM_PUSH_HTTP ] );
|
setHttpPushUrl(doc[CFG_PARAM_PUSH_HTTP]);
|
||||||
if( !doc[ CFG_PARAM_PUSH_HTTP2 ].isNull() )
|
if (!doc[CFG_PARAM_PUSH_HTTP2].isNull())
|
||||||
setHttpPushUrl2( doc[ CFG_PARAM_PUSH_HTTP2 ] );
|
setHttpPushUrl2(doc[CFG_PARAM_PUSH_HTTP2]);
|
||||||
if( !doc[ CFG_PARAM_PUSH_INFLUXDB2 ].isNull() )
|
if (!doc[CFG_PARAM_PUSH_INFLUXDB2].isNull())
|
||||||
setInfluxDb2PushUrl( doc[ CFG_PARAM_PUSH_INFLUXDB2 ] );
|
setInfluxDb2PushUrl(doc[CFG_PARAM_PUSH_INFLUXDB2]);
|
||||||
if( !doc[ CFG_PARAM_PUSH_INFLUXDB2_ORG ].isNull() )
|
if (!doc[CFG_PARAM_PUSH_INFLUXDB2_ORG].isNull())
|
||||||
setInfluxDb2PushOrg( doc[ CFG_PARAM_PUSH_INFLUXDB2_ORG ] );
|
setInfluxDb2PushOrg(doc[CFG_PARAM_PUSH_INFLUXDB2_ORG]);
|
||||||
if( !doc[ CFG_PARAM_PUSH_INFLUXDB2_BUCKET ].isNull() )
|
if (!doc[CFG_PARAM_PUSH_INFLUXDB2_BUCKET].isNull())
|
||||||
setInfluxDb2PushBucket( doc[ CFG_PARAM_PUSH_INFLUXDB2_BUCKET ] );
|
setInfluxDb2PushBucket(doc[CFG_PARAM_PUSH_INFLUXDB2_BUCKET]);
|
||||||
if( !doc[ CFG_PARAM_PUSH_INFLUXDB2_AUTH ].isNull() )
|
if (!doc[CFG_PARAM_PUSH_INFLUXDB2_AUTH].isNull())
|
||||||
setInfluxDb2PushToken( doc[ CFG_PARAM_PUSH_INFLUXDB2_AUTH ] );
|
setInfluxDb2PushToken(doc[CFG_PARAM_PUSH_INFLUXDB2_AUTH]);
|
||||||
if( !doc[ CFG_PARAM_SLEEP_INTERVAL ].isNull() )
|
if (!doc[CFG_PARAM_SLEEP_INTERVAL].isNull())
|
||||||
setSleepInterval( doc[ CFG_PARAM_SLEEP_INTERVAL ].as<int>() );
|
setSleepInterval(doc[CFG_PARAM_SLEEP_INTERVAL].as<int>());
|
||||||
if( !doc[ CFG_PARAM_PUSH_INTERVAL ].isNull() ) // TODO: @deprecated
|
if (!doc[CFG_PARAM_VOLTAGEFACTOR].isNull())
|
||||||
setSleepInterval( doc[ CFG_PARAM_PUSH_INTERVAL ].as<int>() ); // TODO: @deprecated
|
setVoltageFactor(doc[CFG_PARAM_VOLTAGEFACTOR].as<float>());
|
||||||
if( !doc[ CFG_PARAM_VOLTAGEFACTOR ].isNull() )
|
if (!doc[CFG_PARAM_GRAVITY_FORMULA].isNull())
|
||||||
setVoltageFactor( doc[ CFG_PARAM_VOLTAGEFACTOR ].as<float>() );
|
setGravityFormula(doc[CFG_PARAM_GRAVITY_FORMULA]);
|
||||||
if( !doc[ CFG_PARAM_GRAVITY_FORMULA ].isNull() )
|
if (!doc[CFG_PARAM_GRAVITY_TEMP_ADJ].isNull())
|
||||||
setGravityFormula( doc[ CFG_PARAM_GRAVITY_FORMULA ] );
|
setGravityTempAdj(doc[CFG_PARAM_GRAVITY_TEMP_ADJ].as<bool>());
|
||||||
if( !doc[ CFG_PARAM_GRAVITY_TEMP_ADJ ].isNull() )
|
if (!doc[CFG_PARAM_GRAVITY_FORMAT].isNull()) {
|
||||||
setGravityTempAdj( doc[ CFG_PARAM_GRAVITY_TEMP_ADJ ].as<bool>() );
|
String s = doc[CFG_PARAM_GRAVITY_FORMAT];
|
||||||
if( !doc[ CFG_PARAM_GRAVITY_FORMAT ].isNull() ) {
|
setGravityFormat(s.charAt(0));
|
||||||
String s = doc[ CFG_PARAM_GRAVITY_FORMAT ];
|
}
|
||||||
setGravityFormat( s.charAt(0) );
|
if (!doc[CFG_PARAM_TEMP_ADJ].isNull())
|
||||||
}
|
setTempSensorAdj(doc[CFG_PARAM_TEMP_ADJ].as<float>());
|
||||||
if( !doc[ CFG_PARAM_TEMP_ADJ ].isNull() )
|
|
||||||
setTempSensorAdj( doc[ CFG_PARAM_TEMP_ADJ ].as<float>() );
|
|
||||||
|
|
||||||
if( !doc[ CFG_PARAM_GYRO_CALIBRATION ]["ax"].isNull() )
|
if (!doc[CFG_PARAM_GYRO_CALIBRATION]["ax"].isNull())
|
||||||
gyroCalibration.ax = doc[ CFG_PARAM_GYRO_CALIBRATION ]["ax"];
|
gyroCalibration.ax = doc[CFG_PARAM_GYRO_CALIBRATION]["ax"];
|
||||||
if( !doc[ CFG_PARAM_GYRO_CALIBRATION ]["ay"].isNull() )
|
if (!doc[CFG_PARAM_GYRO_CALIBRATION]["ay"].isNull())
|
||||||
gyroCalibration.ay = doc[ CFG_PARAM_GYRO_CALIBRATION ]["ay"];
|
gyroCalibration.ay = doc[CFG_PARAM_GYRO_CALIBRATION]["ay"];
|
||||||
if( !doc[ CFG_PARAM_GYRO_CALIBRATION ]["az"].isNull() )
|
if (!doc[CFG_PARAM_GYRO_CALIBRATION]["az"].isNull())
|
||||||
gyroCalibration.az = doc[ CFG_PARAM_GYRO_CALIBRATION ]["az"];
|
gyroCalibration.az = doc[CFG_PARAM_GYRO_CALIBRATION]["az"];
|
||||||
if( !doc[ CFG_PARAM_GYRO_CALIBRATION ]["gx"].isNull() )
|
if (!doc[CFG_PARAM_GYRO_CALIBRATION]["gx"].isNull())
|
||||||
gyroCalibration.gx = doc[ CFG_PARAM_GYRO_CALIBRATION ]["gx"];
|
gyroCalibration.gx = doc[CFG_PARAM_GYRO_CALIBRATION]["gx"];
|
||||||
if( !doc[ CFG_PARAM_GYRO_CALIBRATION ]["gy"].isNull() )
|
if (!doc[CFG_PARAM_GYRO_CALIBRATION]["gy"].isNull())
|
||||||
gyroCalibration.gy = doc[ CFG_PARAM_GYRO_CALIBRATION ]["gy"];
|
gyroCalibration.gy = doc[CFG_PARAM_GYRO_CALIBRATION]["gy"];
|
||||||
if( !doc[ CFG_PARAM_GYRO_CALIBRATION ]["gz"].isNull() )
|
if (!doc[CFG_PARAM_GYRO_CALIBRATION]["gz"].isNull())
|
||||||
gyroCalibration.gz = doc[ CFG_PARAM_GYRO_CALIBRATION ]["gz"];
|
gyroCalibration.gz = doc[CFG_PARAM_GYRO_CALIBRATION]["gz"];
|
||||||
|
|
||||||
myConfig.debug();
|
if (!doc[CFG_PARAM_FORMULA_DATA]["a1"].isNull())
|
||||||
saveNeeded = false; // Reset save flag
|
formulaData.a[0] = doc[CFG_PARAM_FORMULA_DATA]["a1"].as<double>();
|
||||||
Log.notice(F("CFG : Configuration file " CFG_FILENAME " loaded." CR));
|
if (!doc[CFG_PARAM_FORMULA_DATA]["a2"].isNull())
|
||||||
return true;
|
formulaData.a[1] = doc[CFG_PARAM_FORMULA_DATA]["a2"].as<double>();
|
||||||
|
if (!doc[CFG_PARAM_FORMULA_DATA]["a3"].isNull())
|
||||||
|
formulaData.a[2] = doc[CFG_PARAM_FORMULA_DATA]["a3"].as<double>();
|
||||||
|
if (!doc[CFG_PARAM_FORMULA_DATA]["a4"].isNull())
|
||||||
|
formulaData.a[3] = doc[CFG_PARAM_FORMULA_DATA]["a4"].as<double>();
|
||||||
|
if (!doc[CFG_PARAM_FORMULA_DATA]["a5"].isNull())
|
||||||
|
formulaData.a[4] = doc[CFG_PARAM_FORMULA_DATA]["a5"].as<double>();
|
||||||
|
|
||||||
|
if (!doc[CFG_PARAM_FORMULA_DATA]["g1"].isNull())
|
||||||
|
formulaData.g[0] = doc[CFG_PARAM_FORMULA_DATA]["g1"].as<double>();
|
||||||
|
if (!doc[CFG_PARAM_FORMULA_DATA]["g2"].isNull())
|
||||||
|
formulaData.g[1] = doc[CFG_PARAM_FORMULA_DATA]["g2"].as<double>();
|
||||||
|
if (!doc[CFG_PARAM_FORMULA_DATA]["g3"].isNull())
|
||||||
|
formulaData.g[2] = doc[CFG_PARAM_FORMULA_DATA]["g3"].as<double>();
|
||||||
|
if (!doc[CFG_PARAM_FORMULA_DATA]["g4"].isNull())
|
||||||
|
formulaData.g[3] = doc[CFG_PARAM_FORMULA_DATA]["g4"].as<double>();
|
||||||
|
if (!doc[CFG_PARAM_FORMULA_DATA]["g5"].isNull())
|
||||||
|
formulaData.g[4] = doc[CFG_PARAM_FORMULA_DATA]["g5"];
|
||||||
|
|
||||||
|
myConfig.debug();
|
||||||
|
saveNeeded = false; // Reset save flag
|
||||||
|
Log.notice(F("CFG : Configuration file " CFG_FILENAME " loaded." CR));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Check if file system can be mounted, if not we format it.
|
// Check if file system can be mounted, if not we format it.
|
||||||
//
|
//
|
||||||
void Config::formatFileSystem() {
|
void Config::formatFileSystem() {
|
||||||
#if LOG_LEVEL==6
|
Log.notice(F("CFG : Formating filesystem." CR));
|
||||||
Log.verbose(F("CFG : Formating filesystem." CR));
|
LittleFS.format();
|
||||||
#endif
|
|
||||||
LittleFS.format();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Check if file system can be mounted, if not we format it.
|
// Check if file system can be mounted, if not we format it.
|
||||||
//
|
//
|
||||||
void Config::checkFileSystem() {
|
void Config::checkFileSystem() {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(CFG_DISABLE_LOGGING)
|
||||||
Log.verbose(F("CFG : Checking if filesystem is valid." CR));
|
Log.verbose(F("CFG : Checking if filesystem is valid." CR));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (LittleFS.begin()) {
|
if (LittleFS.begin()) {
|
||||||
Log.notice(F("CFG : Filesystem mounted." CR));
|
Log.notice(F("CFG : Filesystem mounted." CR));
|
||||||
} else {
|
} else {
|
||||||
Log.error(F("CFG : Unable to mount file system, formatting..." CR));
|
Log.error(F("CFG : Unable to mount file system, formatting..." CR));
|
||||||
LittleFS.format();
|
LittleFS.format();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Dump the configuration to the serial port
|
// Dump the configuration to the serial port
|
||||||
//
|
//
|
||||||
void Config::debug() {
|
void Config::debug() {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(CFG_DISABLE_LOGGING)
|
||||||
Log.verbose(F("CFG : Dumping configration " CFG_FILENAME "." CR));
|
Log.verbose(F("CFG : Dumping configration " CFG_FILENAME "." CR));
|
||||||
Log.verbose(F("CFG : ID; '%s'." CR), getID());
|
Log.verbose(F("CFG : ID; '%s'." CR), getID());
|
||||||
Log.verbose(F("CFG : mDNS; '%s'." CR), getMDNS() );
|
Log.verbose(F("CFG : WIFI; '%s', '%s'." CR), getWifiSSID(), getWifiPass());
|
||||||
Log.verbose(F("CFG : Sleep interval; %d." CR), getSleepInterval() );
|
Log.verbose(F("CFG : mDNS; '%s'." CR), getMDNS());
|
||||||
Log.verbose(F("CFG : OTA; '%s'." CR), getOtaURL() );
|
Log.verbose(F("CFG : Sleep interval; %d." CR), getSleepInterval());
|
||||||
Log.verbose(F("CFG : Temp Format; %c." CR), getTempFormat() );
|
Log.verbose(F("CFG : OTA; '%s'." CR), getOtaURL());
|
||||||
Log.verbose(F("CFG : Temp Adj; %F." CR), getTempSensorAdj() );
|
Log.verbose(F("CFG : Temp Format; %c." CR), getTempFormat());
|
||||||
Log.verbose(F("CFG : VoltageFactor; %F." CR), getVoltageFactor() );
|
Log.verbose(F("CFG : Temp Adj; %F." CR), getTempSensorAdj());
|
||||||
Log.verbose(F("CFG : Gravity formula; '%s'." CR), getGravityFormula() );
|
Log.verbose(F("CFG : VoltageFactor; %F." CR), getVoltageFactor());
|
||||||
Log.verbose(F("CFG : Gravity format; '%c'." CR), getGravityFormat() );
|
Log.verbose(F("CFG : Gravity formula; '%s'." CR), getGravityFormula());
|
||||||
Log.verbose(F("CFG : Gravity temp adj; %s." CR), isGravityTempAdj()?"true":"false" );
|
Log.verbose(F("CFG : Gravity format; '%c'." CR), getGravityFormat());
|
||||||
Log.verbose(F("CFG : Push brewfather; '%s'." CR), getBrewfatherPushUrl() );
|
Log.verbose(F("CFG : Gravity temp adj; %s." CR),
|
||||||
Log.verbose(F("CFG : Push http; '%s'." CR), getHttpPushUrl() );
|
isGravityTempAdj() ? "true" : "false");
|
||||||
Log.verbose(F("CFG : Push http2; '%s'." CR), getHttpPushUrl2() );
|
Log.verbose(F("CFG : Push brewfather; '%s'." CR), getBrewfatherPushUrl());
|
||||||
Log.verbose(F("CFG : InfluxDb2; '%s', '%s', '%s', '%s'." CR), getInfluxDb2PushUrl(), getInfluxDb2PushOrg(),
|
Log.verbose(F("CFG : Push http; '%s'." CR), getHttpPushUrl());
|
||||||
getInfluxDb2PushBucket(), getInfluxDb2PushToken() );
|
Log.verbose(F("CFG : Push http2; '%s'." CR), getHttpPushUrl2());
|
||||||
// Log.verbose(F("CFG : Accel offset\t%d\t%d\t%d" CR), gyroCalibration.ax, gyroCalibration.ay, gyroCalibration.az );
|
Log.verbose(F("CFG : InfluxDb2; '%s', '%s', '%s', '%s'." CR),
|
||||||
// Log.verbose(F("CFG : Gyro offset \t%d\t%d\t%d" CR), gyroCalibration.gx, gyroCalibration.gy, gyroCalibration.gz );
|
getInfluxDb2PushUrl(), getInfluxDb2PushOrg(),
|
||||||
|
getInfluxDb2PushBucket(), getInfluxDb2PushToken());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
206
src/config.h
@ -1,206 +0,0 @@
|
|||||||
/*
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2021 Magnus
|
|
||||||
|
|
||||||
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 above 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.
|
|
||||||
*/
|
|
||||||
#ifndef _CONFIG_H
|
|
||||||
#define _CONFIG_H
|
|
||||||
|
|
||||||
// Includes
|
|
||||||
#include "helper.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <ArduinoJson.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
// defintions
|
|
||||||
#define CFG_JSON_BUFSIZE 2000
|
|
||||||
|
|
||||||
#define CFG_APPNAME "GravityMon " // Name of firmware
|
|
||||||
#define CFG_FILENAME "/gravitymon.json" // Name of config file
|
|
||||||
|
|
||||||
#define WIFI_DEFAULT_SSID "GravityMon" // Name of created SSID
|
|
||||||
#define WIFI_DEFAULT_PWD "password" // Password for created SSID
|
|
||||||
#define WIFI_MDNS "gravitymon" // Prefix for MDNS name
|
|
||||||
#define WIFI_PORTAL_TIMEOUT 120 // Number of seconds until the config portal is closed
|
|
||||||
|
|
||||||
// These are used in API + Savefile
|
|
||||||
#define CFG_PARAM_ID "id"
|
|
||||||
#define CFG_PARAM_MDNS "mdns" // Device name
|
|
||||||
#define CFG_PARAM_OTA "ota-url" // Base URL for OTA
|
|
||||||
#define CFG_PARAM_PUSH_BREWFATHER "brewfather-push" // URL (brewfather format)
|
|
||||||
#define CFG_PARAM_PUSH_HTTP "http-push" // URL (iSpindle format)
|
|
||||||
#define CFG_PARAM_PUSH_HTTP2 "http-push2" // URL (iSpindle format)
|
|
||||||
#define CFG_PARAM_PUSH_INFLUXDB2 "influxdb2-push" // URL
|
|
||||||
#define CFG_PARAM_PUSH_INFLUXDB2_ORG "influxdb2-org" // URL
|
|
||||||
#define CFG_PARAM_PUSH_INFLUXDB2_BUCKET "influxdb2-bucket" // URL
|
|
||||||
#define CFG_PARAM_PUSH_INFLUXDB2_AUTH "influxdb2-auth" // URL
|
|
||||||
#define CFG_PARAM_SLEEP_INTERVAL "sleep-interval" // Sleep interval
|
|
||||||
// TODO: @deprecated setting
|
|
||||||
#define CFG_PARAM_PUSH_INTERVAL "push-interval" // Time between push
|
|
||||||
#define CFG_PARAM_TEMPFORMAT "temp-format" // C or F
|
|
||||||
#define CFG_PARAM_VOLTAGEFACTOR "voltage-factor" // Factor to calculate the battery voltage
|
|
||||||
#define CFG_PARAM_GRAVITY_FORMULA "gravity-formula" // Formula for calculating gravity
|
|
||||||
#define CFG_PARAM_GRAVITY_FORMAT "gravity-format" // Gravity format G or P
|
|
||||||
#define CFG_PARAM_GRAVITY_TEMP_ADJ "gravity-temp-adjustment" // True/False. Adjust gravity for temperature
|
|
||||||
#define CFG_PARAM_TEMP_ADJ "temp-adjustment-value" // Correction value for temp sensor
|
|
||||||
#define CFG_PARAM_GYRO_CALIBRATION "gyro-calibration-data" // READ ONLY
|
|
||||||
|
|
||||||
// These are used in API's
|
|
||||||
#define CFG_PARAM_APP_NAME "app-name"
|
|
||||||
#define CFG_PARAM_APP_VER "app-ver"
|
|
||||||
#define CFG_PARAM_ANGLE "angle"
|
|
||||||
#define CFG_PARAM_GRAVITY "gravity"
|
|
||||||
#define CFG_PARAM_TEMP_C "temp-c"
|
|
||||||
#define CFG_PARAM_TEMP_F "temp-f"
|
|
||||||
#define CFG_PARAM_BATTERY "battery"
|
|
||||||
#define CFG_PARAM_SLEEP_MODE "sleep-mode"
|
|
||||||
#define CFG_PARAM_RSSI "rssi"
|
|
||||||
|
|
||||||
// Used for holding sensordata or sensoroffsets
|
|
||||||
struct RawGyroData {
|
|
||||||
int16_t ax; // Raw Acceleration
|
|
||||||
int16_t ay;
|
|
||||||
int16_t az;
|
|
||||||
|
|
||||||
int16_t gx; // Raw Position
|
|
||||||
int16_t gy;
|
|
||||||
int16_t gz;
|
|
||||||
|
|
||||||
int16_t temp; // Only for information (temperature of chip)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Main configuration class
|
|
||||||
class Config {
|
|
||||||
private:
|
|
||||||
bool saveNeeded;
|
|
||||||
|
|
||||||
// Device configuration
|
|
||||||
String id;
|
|
||||||
String mDNS;
|
|
||||||
String otaURL;
|
|
||||||
char tempFormat; // C, F
|
|
||||||
float voltageFactor;
|
|
||||||
float tempSensorAdj; // This value will be added to the read sensor value
|
|
||||||
int sleepInterval;
|
|
||||||
|
|
||||||
// Push target settings
|
|
||||||
String brewfatherPushUrl; // URL For brewfather
|
|
||||||
|
|
||||||
String httpPushUrl; // URL 1 for standard http
|
|
||||||
String httpPushUrl2; // URL 2 for standard http
|
|
||||||
|
|
||||||
String influxDb2Url; // URL for InfluxDB v2
|
|
||||||
String influxDb2Org; // Organisation for InfluxDB v2
|
|
||||||
String influxDb2Bucket; // Bucket for InfluxDB v2
|
|
||||||
String influxDb2Token; // Auth Token for InfluxDB v2
|
|
||||||
|
|
||||||
// Gravity and temperature calculations
|
|
||||||
String gravityFormula;
|
|
||||||
bool gravityTempAdj; // true, false
|
|
||||||
char gravityFormat; // G, P
|
|
||||||
|
|
||||||
// Gyro calibration data
|
|
||||||
RawGyroData gyroCalibration; // Holds the gyro calibration constants (6 * int16_t)
|
|
||||||
|
|
||||||
void debug();
|
|
||||||
void formatFileSystem();
|
|
||||||
|
|
||||||
public:
|
|
||||||
Config();
|
|
||||||
const char* getID() { return id.c_str(); };
|
|
||||||
|
|
||||||
const char* getMDNS() { return mDNS.c_str(); }
|
|
||||||
void setMDNS( String s ) { mDNS = s; saveNeeded = true; }
|
|
||||||
|
|
||||||
const char* getOtaURL() { return otaURL.c_str(); }
|
|
||||||
void setOtaURL( String s ) { otaURL = s; saveNeeded = true; }
|
|
||||||
bool isOtaActive() { return otaURL.length()?true:false; }
|
|
||||||
|
|
||||||
// Brewfather
|
|
||||||
const char* getBrewfatherPushUrl() { return brewfatherPushUrl.c_str(); }
|
|
||||||
void setBrewfatherPushUrl( String s ) { brewfatherPushUrl = s; saveNeeded = true; }
|
|
||||||
bool isBrewfatherActive() { return brewfatherPushUrl.length()?true:false; }
|
|
||||||
|
|
||||||
// Standard HTTP
|
|
||||||
const char* getHttpPushUrl() { return httpPushUrl.c_str(); }
|
|
||||||
void setHttpPushUrl( String s ) { httpPushUrl = s; saveNeeded = true; }
|
|
||||||
bool isHttpActive() { return httpPushUrl.length()?true:false; }
|
|
||||||
const char* getHttpPushUrl2() { return httpPushUrl2.c_str(); }
|
|
||||||
void setHttpPushUrl2( String s ) { httpPushUrl2 = s; saveNeeded = true; }
|
|
||||||
bool isHttpActive2() { return httpPushUrl2.length()?true:false; }
|
|
||||||
|
|
||||||
// InfluxDB2
|
|
||||||
const char* getInfluxDb2PushUrl() { return influxDb2Url.c_str(); }
|
|
||||||
void setInfluxDb2PushUrl( String s ) { influxDb2Url = s; saveNeeded = true; }
|
|
||||||
bool isInfluxDb2Active() { return influxDb2Url.length()?true:false; }
|
|
||||||
const char* getInfluxDb2PushOrg() { return influxDb2Org.c_str(); }
|
|
||||||
void setInfluxDb2PushOrg( String s ) { influxDb2Org = s; saveNeeded = true; }
|
|
||||||
const char* getInfluxDb2PushBucket() { return influxDb2Bucket.c_str(); }
|
|
||||||
void setInfluxDb2PushBucket( String s ) { influxDb2Bucket = s; saveNeeded = true; }
|
|
||||||
const char* getInfluxDb2PushToken() { return influxDb2Token.c_str(); }
|
|
||||||
void setInfluxDb2PushToken( String s ) { influxDb2Token = s; saveNeeded = true; }
|
|
||||||
|
|
||||||
int getSleepInterval() { return sleepInterval; }
|
|
||||||
void setSleepInterval( int v ) { sleepInterval = v; saveNeeded = true; }
|
|
||||||
void setSleepInterval( String s ) { sleepInterval = s.toInt(); saveNeeded = true; }
|
|
||||||
|
|
||||||
char getTempFormat() { return tempFormat; }
|
|
||||||
void setTempFormat( char c ) { tempFormat = c; saveNeeded = true; }
|
|
||||||
bool isTempC() { return tempFormat=='C'?false:true; };
|
|
||||||
bool isTempF() { return tempFormat=='F'?false:true; };
|
|
||||||
|
|
||||||
float getVoltageFactor() { return voltageFactor; }
|
|
||||||
void setVoltageFactor( float f ) { voltageFactor = f; saveNeeded = true; }
|
|
||||||
void setVoltageFactor( String s ) { voltageFactor = s.toFloat(); saveNeeded = true; }
|
|
||||||
|
|
||||||
float getTempSensorAdj() { return tempSensorAdj; }
|
|
||||||
void setTempSensorAdj( float f ) { tempSensorAdj = f; saveNeeded = true; }
|
|
||||||
void setTempSensorAdj( String s ) { tempSensorAdj = s.toFloat(); saveNeeded = true; }
|
|
||||||
|
|
||||||
const char* getGravityFormula() { return gravityFormula.c_str(); }
|
|
||||||
void setGravityFormula( String s ) { gravityFormula = s; saveNeeded = true; }
|
|
||||||
|
|
||||||
bool isGravityTempAdj() { return gravityTempAdj; }
|
|
||||||
void setGravityTempAdj( bool b ) { gravityTempAdj = b; saveNeeded = true; }
|
|
||||||
|
|
||||||
char getGravityFormat() { return gravityFormat; }
|
|
||||||
void setGravityFormat( char c ) { gravityFormat = c; saveNeeded = true; }
|
|
||||||
bool isGravitySG() { return gravityFormat=='G'?false:true; };
|
|
||||||
bool isGravityPlato() { return gravityFormat=='P'?false:true; };
|
|
||||||
|
|
||||||
const RawGyroData& getGyroCalibration() { return gyroCalibration; }
|
|
||||||
void setGyroCalibration( const RawGyroData &r ) { gyroCalibration = r; saveNeeded = true; }
|
|
||||||
|
|
||||||
// IO functions
|
|
||||||
void createJson(DynamicJsonDocument& doc);
|
|
||||||
bool saveFile();
|
|
||||||
bool loadFile();
|
|
||||||
void checkFileSystem();
|
|
||||||
bool isSaveNeeded() { return saveNeeded; };
|
|
||||||
void setSaveNeeded() { saveNeeded = true; };
|
|
||||||
};
|
|
||||||
|
|
||||||
// Global instance created
|
|
||||||
extern Config myConfig;
|
|
||||||
|
|
||||||
#endif // _CONFIG_H
|
|
||||||
|
|
||||||
// EOF
|
|
307
src/config.hpp
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
/*
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
|
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 above 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.
|
||||||
|
*/
|
||||||
|
#ifndef SRC_CONFIG_HPP_
|
||||||
|
#define SRC_CONFIG_HPP_
|
||||||
|
|
||||||
|
// Includes
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <helper.hpp>
|
||||||
|
|
||||||
|
// defintions
|
||||||
|
#define CFG_JSON_BUFSIZE 3192
|
||||||
|
|
||||||
|
#define CFG_APPNAME "GravityMon " // Name of firmware
|
||||||
|
#define CFG_FILENAME "/gravitymon.json" // Name of config file
|
||||||
|
|
||||||
|
#define WIFI_DEFAULT_SSID "GravityMon" // Name of created SSID
|
||||||
|
#define WIFI_DEFAULT_PWD "password" // Password for created SSID
|
||||||
|
#define WIFI_MDNS "gravitymon" // Prefix for MDNS name
|
||||||
|
#define WIFI_PORTAL_TIMEOUT \
|
||||||
|
120 // Number of seconds until the config portal is closed
|
||||||
|
|
||||||
|
// These are used in API + Savefile
|
||||||
|
#define CFG_PARAM_ID "id"
|
||||||
|
#define CFG_PARAM_MDNS "mdns" // Device name
|
||||||
|
#define CFG_PARAM_OTA "ota-url" // Base URL for OTA
|
||||||
|
#define CFG_PARAM_SSID "wifi-ssid" // WIFI
|
||||||
|
#define CFG_PARAM_PASS "wifi-pass" // WIFI
|
||||||
|
|
||||||
|
#define CFG_PARAM_PUSH_BREWFATHER "brewfather-push" // URL (brewfather format)
|
||||||
|
#define CFG_PARAM_PUSH_HTTP "http-push" // URL (iSpindle format)
|
||||||
|
#define CFG_PARAM_PUSH_HTTP2 "http-push2" // URL (iSpindle format)
|
||||||
|
#define CFG_PARAM_PUSH_INFLUXDB2 "influxdb2-push" // URL
|
||||||
|
#define CFG_PARAM_PUSH_INFLUXDB2_ORG "influxdb2-org" // URL
|
||||||
|
#define CFG_PARAM_PUSH_INFLUXDB2_BUCKET "influxdb2-bucket" // URL
|
||||||
|
#define CFG_PARAM_PUSH_INFLUXDB2_AUTH "influxdb2-auth" // URL
|
||||||
|
#define CFG_PARAM_SLEEP_INTERVAL "sleep-interval" // Sleep interval
|
||||||
|
#define CFG_PARAM_TEMPFORMAT "temp-format" // C or F
|
||||||
|
#define CFG_PARAM_VOLTAGEFACTOR \
|
||||||
|
"voltage-factor" // Factor to calculate the battery voltage
|
||||||
|
#define CFG_PARAM_GRAVITY_FORMULA \
|
||||||
|
"gravity-formula" // Formula for calculating gravity
|
||||||
|
#define CFG_PARAM_GRAVITY_FORMAT "gravity-format" // Gravity format G or P
|
||||||
|
#define CFG_PARAM_GRAVITY_TEMP_ADJ \
|
||||||
|
"gravity-temp-adjustment" // True/False. Adjust gravity for temperature
|
||||||
|
#define CFG_PARAM_TEMP_ADJ \
|
||||||
|
"temp-adjustment-value" // Correction value for temp sensor
|
||||||
|
#define CFG_PARAM_GYRO_CALIBRATION "gyro-calibration-data" // READ ONLY
|
||||||
|
|
||||||
|
#define CFG_PARAM_FORMULA_DATA \
|
||||||
|
"formula-calculation-data" // Raw data for the formula calculation
|
||||||
|
|
||||||
|
// These are used in API's
|
||||||
|
#define CFG_PARAM_APP_NAME "app-name"
|
||||||
|
#define CFG_PARAM_APP_VER "app-ver"
|
||||||
|
#define CFG_PARAM_ANGLE "angle"
|
||||||
|
#define CFG_PARAM_GRAVITY "gravity"
|
||||||
|
#define CFG_PARAM_TEMP_C "temp-c"
|
||||||
|
#define CFG_PARAM_TEMP_F "temp-f"
|
||||||
|
#define CFG_PARAM_BATTERY "battery"
|
||||||
|
#define CFG_PARAM_SLEEP_MODE "sleep-mode"
|
||||||
|
#define CFG_PARAM_RSSI "rssi"
|
||||||
|
|
||||||
|
// Used for holding sensordata or sensoroffsets
|
||||||
|
struct RawGyroData {
|
||||||
|
int16_t ax; // Raw Acceleration
|
||||||
|
int16_t ay;
|
||||||
|
int16_t az;
|
||||||
|
|
||||||
|
int16_t gx; // Raw Position
|
||||||
|
int16_t gy;
|
||||||
|
int16_t gz;
|
||||||
|
|
||||||
|
int16_t temp; // Only for information (temperature of chip)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Used for holding formulaData (used for calculating formula on device)
|
||||||
|
struct RawFormulaData {
|
||||||
|
double a[5];
|
||||||
|
double g[5];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Main configuration class
|
||||||
|
class Config {
|
||||||
|
private:
|
||||||
|
bool saveNeeded;
|
||||||
|
|
||||||
|
// Device configuration
|
||||||
|
String id;
|
||||||
|
String mDNS;
|
||||||
|
String otaURL;
|
||||||
|
char tempFormat; // C, F
|
||||||
|
float voltageFactor;
|
||||||
|
float tempSensorAdj; // This value will be added to the read sensor value
|
||||||
|
int sleepInterval;
|
||||||
|
|
||||||
|
// Wifi Config
|
||||||
|
String wifiSSID;
|
||||||
|
String wifiPASS;
|
||||||
|
|
||||||
|
// Push target settings
|
||||||
|
String brewfatherPushUrl; // URL For brewfather
|
||||||
|
|
||||||
|
String httpPushUrl; // URL 1 for standard http
|
||||||
|
String httpPushUrl2; // URL 2 for standard http
|
||||||
|
|
||||||
|
String influxDb2Url; // URL for InfluxDB v2
|
||||||
|
String influxDb2Org; // Organisation for InfluxDB v2
|
||||||
|
String influxDb2Bucket; // Bucket for InfluxDB v2
|
||||||
|
String influxDb2Token; // Auth Token for InfluxDB v2
|
||||||
|
|
||||||
|
// Gravity and temperature calculations
|
||||||
|
String gravityFormula;
|
||||||
|
bool gravityTempAdj; // true, false
|
||||||
|
char gravityFormat; // G, P
|
||||||
|
|
||||||
|
// Gyro calibration data
|
||||||
|
RawGyroData
|
||||||
|
gyroCalibration; // Holds the gyro calibration constants (6 * int16_t)
|
||||||
|
RawFormulaData formulaData; // Used for creating formula
|
||||||
|
|
||||||
|
void debug();
|
||||||
|
void formatFileSystem();
|
||||||
|
|
||||||
|
public:
|
||||||
|
Config();
|
||||||
|
const char* getID() { return id.c_str(); }
|
||||||
|
|
||||||
|
const char* getMDNS() { return mDNS.c_str(); }
|
||||||
|
void setMDNS(String s) {
|
||||||
|
mDNS = s;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* getOtaURL() { return otaURL.c_str(); }
|
||||||
|
void setOtaURL(String s) {
|
||||||
|
otaURL = s;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
bool isOtaActive() { return otaURL.length() ? true : false; }
|
||||||
|
|
||||||
|
const char* getWifiSSID() { return wifiSSID.c_str(); }
|
||||||
|
void setWifiSSID(String s) {
|
||||||
|
wifiSSID = s;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
const char* getWifiPass() { return wifiPASS.c_str(); }
|
||||||
|
void setWifiPass(String s) {
|
||||||
|
wifiPASS = s;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Brewfather
|
||||||
|
const char* getBrewfatherPushUrl() { return brewfatherPushUrl.c_str(); }
|
||||||
|
void setBrewfatherPushUrl(String s) {
|
||||||
|
brewfatherPushUrl = s;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
bool isBrewfatherActive() {
|
||||||
|
return brewfatherPushUrl.length() ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard HTTP
|
||||||
|
const char* getHttpPushUrl() { return httpPushUrl.c_str(); }
|
||||||
|
void setHttpPushUrl(String s) {
|
||||||
|
httpPushUrl = s;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
bool isHttpActive() { return httpPushUrl.length() ? true : false; }
|
||||||
|
const char* getHttpPushUrl2() { return httpPushUrl2.c_str(); }
|
||||||
|
void setHttpPushUrl2(String s) {
|
||||||
|
httpPushUrl2 = s;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
bool isHttpActive2() { return httpPushUrl2.length() ? true : false; }
|
||||||
|
|
||||||
|
// InfluxDB2
|
||||||
|
const char* getInfluxDb2PushUrl() { return influxDb2Url.c_str(); }
|
||||||
|
void setInfluxDb2PushUrl(String s) {
|
||||||
|
influxDb2Url = s;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
bool isInfluxDb2Active() { return influxDb2Url.length() ? true : false; }
|
||||||
|
const char* getInfluxDb2PushOrg() { return influxDb2Org.c_str(); }
|
||||||
|
void setInfluxDb2PushOrg(String s) {
|
||||||
|
influxDb2Org = s;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
const char* getInfluxDb2PushBucket() { return influxDb2Bucket.c_str(); }
|
||||||
|
void setInfluxDb2PushBucket(String s) {
|
||||||
|
influxDb2Bucket = s;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
const char* getInfluxDb2PushToken() { return influxDb2Token.c_str(); }
|
||||||
|
void setInfluxDb2PushToken(String s) {
|
||||||
|
influxDb2Token = s;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getSleepInterval() { return sleepInterval; }
|
||||||
|
void setSleepInterval(int v) {
|
||||||
|
sleepInterval = v;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
void setSleepInterval(String s) {
|
||||||
|
sleepInterval = s.toInt();
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
char getTempFormat() { return tempFormat; }
|
||||||
|
void setTempFormat(char c) {
|
||||||
|
tempFormat = c;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
bool isTempC() { return tempFormat == 'C' ? false : true; }
|
||||||
|
bool isTempF() { return tempFormat == 'F' ? false : true; }
|
||||||
|
|
||||||
|
float getVoltageFactor() { return voltageFactor; }
|
||||||
|
void setVoltageFactor(float f) {
|
||||||
|
voltageFactor = f;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
void setVoltageFactor(String s) {
|
||||||
|
voltageFactor = s.toFloat();
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getTempSensorAdj() { return tempSensorAdj; }
|
||||||
|
void setTempSensorAdj(float f) {
|
||||||
|
tempSensorAdj = f;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
void setTempSensorAdj(String s) {
|
||||||
|
tempSensorAdj = s.toFloat();
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* getGravityFormula() { return gravityFormula.c_str(); }
|
||||||
|
void setGravityFormula(String s) {
|
||||||
|
gravityFormula = s;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isGravityTempAdj() { return gravityTempAdj; }
|
||||||
|
void setGravityTempAdj(bool b) {
|
||||||
|
gravityTempAdj = b;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
char getGravityFormat() { return gravityFormat; }
|
||||||
|
void setGravityFormat(char c) {
|
||||||
|
gravityFormat = c;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
bool isGravitySG() { return gravityFormat == 'G' ? false : true; }
|
||||||
|
bool isGravityPlato() { return gravityFormat == 'P' ? false : true; }
|
||||||
|
|
||||||
|
const RawGyroData& getGyroCalibration() { return gyroCalibration; }
|
||||||
|
void setGyroCalibration(const RawGyroData& r) {
|
||||||
|
gyroCalibration = r;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RawFormulaData& getFormulaData() { return formulaData; }
|
||||||
|
void setFormulaData(const RawFormulaData& r) {
|
||||||
|
formulaData = r;
|
||||||
|
saveNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IO functions
|
||||||
|
void createJson(DynamicJsonDocument& doc);
|
||||||
|
bool saveFile();
|
||||||
|
bool loadFile();
|
||||||
|
void checkFileSystem();
|
||||||
|
bool isSaveNeeded() { return saveNeeded; }
|
||||||
|
void setSaveNeeded() { saveNeeded = true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Global instance created
|
||||||
|
extern Config myConfig;
|
||||||
|
|
||||||
|
#endif // SRC_CONFIG_HPP_
|
||||||
|
|
||||||
|
// EOF
|
497
src/gyro.cpp
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 Magnus
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -21,155 +21,162 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "gyro.h"
|
#include <gyro.hpp>
|
||||||
#include "helper.h"
|
#include <helper.hpp>
|
||||||
|
|
||||||
GyroSensor myGyro;
|
GyroSensor myGyro;
|
||||||
|
|
||||||
#define GYRO_USE_INTERRUPT // Use interrupt to detect when new sample is ready
|
#define GYRO_USE_INTERRUPT // Use interrupt to detect when new sample is ready
|
||||||
#define SENSOR_MOVING_THREASHOLD 500
|
#define SENSOR_MOVING_THREASHOLD 500
|
||||||
#define SENSOR_READ_COUNT 50
|
#define SENSOR_READ_COUNT 50
|
||||||
#define SENSOR_READ_DELAY 3150 // us, empirical, to hold sampling to 200 Hz
|
#define SENSOR_READ_DELAY 3150 // us, empirical, to hold sampling to 200 Hz
|
||||||
|
|
||||||
#define GYRO_SHOW_MINMAX // Will calculate the min/max values when doing calibration
|
#define GYRO_SHOW_MINMAX // Will calculate the min/max values when doing
|
||||||
//#define GYRO_CALIBRATE_STARTUP // Will calibrate sensor at startup
|
// calibration
|
||||||
|
// #define GYRO_CALIBRATE_STARTUP // Will calibrate sensor at startup
|
||||||
|
|
||||||
//
|
//
|
||||||
// Initialize the sensor chip.
|
// Initialize the sensor chip.
|
||||||
//
|
//
|
||||||
bool GyroSensor::setup() {
|
bool GyroSensor::setup() {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||||
Log.verbose(F("GYRO: Setting up hardware." CR));
|
Log.verbose(F("GYRO: Setting up hardware." CR));
|
||||||
#endif
|
#endif
|
||||||
Wire.begin(D3, D4);
|
Wire.begin(D3, D4);
|
||||||
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
|
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having
|
||||||
accelgyro.initialize();
|
// compilation difficulties
|
||||||
|
accelgyro.initialize();
|
||||||
|
|
||||||
if( !accelgyro.testConnection() ) {
|
if (!accelgyro.testConnection()) {
|
||||||
Log.error(F("GYRO: Failed to connect to MPU6050 (gyro)." CR));
|
Log.error(F("GYRO: Failed to connect to MPU6050 (gyro)." CR));
|
||||||
sensorConnected = false;
|
sensorConnected = false;
|
||||||
} else {
|
} else {
|
||||||
|
#if !defined(GYRO_DISABLE_LOGGING)
|
||||||
|
Log.notice(F("GYRO: Connected to MPU6050 (gyro)." CR));
|
||||||
|
#endif
|
||||||
|
sensorConnected = true;
|
||||||
|
|
||||||
Log.notice(F("GYRO: Connected to MPU6050 (gyro)." CR));
|
// Configure the sensor
|
||||||
sensorConnected = true;
|
accelgyro.setTempSensorEnabled(true);
|
||||||
|
accelgyro.setDLPFMode(MPU6050_DLPF_BW_5);
|
||||||
// Configure the sensor
|
#if defined(GYRO_USE_INTERRUPT)
|
||||||
accelgyro.setTempSensorEnabled(true);
|
// Alternative method to read data, let the MPU signal when sampling is
|
||||||
//accelgyro.setFullScaleAccelRange(MPU6050_ACCEL_FS_2); // Set in .initalize()
|
// done.
|
||||||
//accelgyro.setFullScaleGyroRange(MPU6050_GYRO_FS_250); // Set in .initalize()
|
accelgyro.setRate(17);
|
||||||
accelgyro.setDLPFMode(MPU6050_DLPF_BW_5);
|
accelgyro.setInterruptDrive(1);
|
||||||
#if defined( GYRO_USE_INTERRUPT )
|
accelgyro.setInterruptMode(1);
|
||||||
// Alternative method to read data, let the MPU signal when sampling is done.
|
accelgyro.setInterruptLatch(0);
|
||||||
accelgyro.setRate(17);
|
accelgyro.setIntDataReadyEnabled(true);
|
||||||
accelgyro.setInterruptDrive(1);
|
|
||||||
accelgyro.setInterruptMode(1);
|
|
||||||
accelgyro.setInterruptLatch(0);
|
|
||||||
accelgyro.setIntDataReadyEnabled(true);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined ( GYRO_CALIBRATE_STARTUP )
|
#if defined(GYRO_CALIBRATE_STARTUP)
|
||||||
// Run the calibration at start, useful for testing.
|
// Run the calibration at start, useful for testing.
|
||||||
calibrateSensor();
|
calibrateSensor();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Once we have calibration values stored we just apply them from the config.
|
// Once we have calibration values stored we just apply them from the
|
||||||
calibrationOffset = myConfig.getGyroCalibration();
|
// config.
|
||||||
applyCalibration();
|
calibrationOffset = myConfig.getGyroCalibration();
|
||||||
}
|
applyCalibration();
|
||||||
|
}
|
||||||
return sensorConnected;
|
return sensorConnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Set sensor in sleep mode to conserve battery
|
// Set sensor in sleep mode to conserve battery
|
||||||
//
|
//
|
||||||
void GyroSensor::enterSleep() {
|
void GyroSensor::enterSleep() {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||||
Log.verbose(F("GYRO: Setting up hardware." CR));
|
Log.verbose(F("GYRO: Setting up hardware." CR));
|
||||||
#endif
|
#endif
|
||||||
accelgyro.setSleepEnabled( true );
|
accelgyro.setSleepEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Do a number of reads to get a more stable value.
|
// Do a number of reads to get a more stable value.
|
||||||
//
|
//
|
||||||
void GyroSensor::readSensor(RawGyroData &raw, const int noIterations, const int delayTime) {
|
void GyroSensor::readSensor(RawGyroData &raw, const int noIterations,
|
||||||
RawGyroDataL average = { 0, 0, 0, 0, 0, 0 };
|
const int delayTime) {
|
||||||
|
RawGyroDataL average = {0, 0, 0, 0, 0, 0};
|
||||||
|
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||||
Log.verbose(F("GYRO: Reading sensor with %d iterations %d us delay." CR), noIterations, delayTime );
|
Log.verbose(F("GYRO: Reading sensor with %d iterations %d us delay." CR),
|
||||||
|
noIterations, delayTime);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Set some initial values
|
// Set some initial values
|
||||||
#if defined( GYRO_SHOW_MINMAX )
|
#if defined(GYRO_SHOW_MINMAX)
|
||||||
RawGyroData min, max;
|
RawGyroData min, max;
|
||||||
//accelgyro.getRotation( &min.gx, &min.gy, &min.gz );
|
accelgyro.getAcceleration(&min.ax, &min.ay, &min.az);
|
||||||
accelgyro.getAcceleration( &min.ax, &min.ay, &min.az );
|
min.temp = accelgyro.getTemperature();
|
||||||
min.temp = accelgyro.getTemperature();
|
max = min;
|
||||||
max = min;
|
|
||||||
#endif
|
|
||||||
for(int cnt = 0; cnt < noIterations ; cnt ++) {
|
|
||||||
|
|
||||||
#if defined( GYRO_USE_INTERRUPT )
|
|
||||||
while( accelgyro.getIntDataReadyStatus() == 0) {
|
|
||||||
delayMicroseconds( 1 );
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//accelgyro.getRotation( &raw.gx, &raw.gy, &raw.gz );
|
|
||||||
//accelgyro.getAcceleration( &raw.ax, &raw.ay, &raw.az );
|
|
||||||
accelgyro.getMotion6(&raw.ax, &raw.ay, &raw.az, &raw.gx, &raw.gy, &raw.gz);
|
|
||||||
raw.temp = accelgyro.getTemperature();
|
|
||||||
|
|
||||||
average.ax += raw.ax;
|
|
||||||
average.ay += raw.ay;
|
|
||||||
average.az += raw.az;
|
|
||||||
average.gx += raw.gx;
|
|
||||||
average.gy += raw.gy;
|
|
||||||
average.gz += raw.gz;
|
|
||||||
average.temp += raw.temp;
|
|
||||||
|
|
||||||
// Log what the minium value is
|
|
||||||
#if defined( GYRO_SHOW_MINMAX )
|
|
||||||
if( raw.ax < min.ax ) min.ax = raw.ax;
|
|
||||||
if( raw.ay < min.ay ) min.ay = raw.ay;
|
|
||||||
if( raw.az < min.az ) min.az = raw.az;
|
|
||||||
if( raw.gx < min.gx ) min.gx = raw.gx;
|
|
||||||
if( raw.gy < min.gy ) min.gy = raw.gy;
|
|
||||||
if( raw.gz < min.gz ) min.gz = raw.gz;
|
|
||||||
if( raw.temp < min.temp ) min.temp = raw.temp;
|
|
||||||
|
|
||||||
// Log what the maximum value is
|
|
||||||
if( raw.ax > max.ax ) max.ax = raw.ax;
|
|
||||||
if( raw.ay > max.ay ) max.ay = raw.ay;
|
|
||||||
if( raw.az > max.az ) max.az = raw.az;
|
|
||||||
if( raw.gx > max.gx ) max.gx = raw.gx;
|
|
||||||
if( raw.gy > max.gy ) max.gy = raw.gy;
|
|
||||||
if( raw.gz > max.gz ) max.gz = raw.gz;
|
|
||||||
if( raw.temp > max.temp ) max.temp = raw.temp;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined( GYRO_USE_INTERRUPT )
|
|
||||||
delayMicroseconds( delayTime );
|
|
||||||
#endif
|
#endif
|
||||||
|
for (int cnt = 0; cnt < noIterations; cnt++) {
|
||||||
|
#if defined(GYRO_USE_INTERRUPT)
|
||||||
|
while (accelgyro.getIntDataReadyStatus() == 0) {
|
||||||
|
delayMicroseconds(1);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
raw.ax = average.ax/noIterations;
|
accelgyro.getMotion6(&raw.ax, &raw.ay, &raw.az, &raw.gx, &raw.gy, &raw.gz);
|
||||||
raw.ay = average.ay/noIterations;
|
raw.temp = accelgyro.getTemperature();
|
||||||
raw.az = average.az/noIterations;
|
|
||||||
raw.gx = average.gx/noIterations;
|
|
||||||
raw.gy = average.gy/noIterations;
|
|
||||||
raw.gz = average.gz/noIterations;
|
|
||||||
raw.temp = average.temp/noIterations;
|
|
||||||
|
|
||||||
#if LOG_LEVEL==6
|
average.ax += raw.ax;
|
||||||
#if defined( GYRO_SHOW_MINMAX )
|
average.ay += raw.ay;
|
||||||
Log.verbose(F("GYRO: Min \t%d\t%d\t%d\t%d\t%d\t%d\t%d." CR), min.ax, min.ay, min.az, min.gx, min.gy, min.gz, min.temp );
|
average.az += raw.az;
|
||||||
Log.verbose(F("GYRO: Max \t%d\t%d\t%d\t%d\t%d\t%d\t%d." CR), max.ax, max.ay, max.az, max.gx, max.gy, max.gz, max.temp );
|
average.gx += raw.gx;
|
||||||
#endif
|
average.gy += raw.gy;
|
||||||
Log.verbose(F("GYRO: Average\t%d\t%d\t%d\t%d\t%d\t%d\t%d." CR), raw.ax, raw.ay, raw.az, raw.gx, raw.gy, raw.gz, raw.temp );
|
average.gz += raw.gz;
|
||||||
//Log.verbose(F("GYRO: Result \t%d\t%d\t%d\t%d\t%d\t%d." CR), average.ax/noIterations, average.ay/noIterations, average.az/noIterations,
|
average.temp += raw.temp;
|
||||||
// average.gx/noIterations, average.gy/noIterations, average.gz/noIterations );
|
|
||||||
|
// Log what the minium value is
|
||||||
|
#if defined(GYRO_SHOW_MINMAX)
|
||||||
|
if (raw.ax < min.ax) min.ax = raw.ax;
|
||||||
|
if (raw.ay < min.ay) min.ay = raw.ay;
|
||||||
|
if (raw.az < min.az) min.az = raw.az;
|
||||||
|
if (raw.gx < min.gx) min.gx = raw.gx;
|
||||||
|
if (raw.gy < min.gy) min.gy = raw.gy;
|
||||||
|
if (raw.gz < min.gz) min.gz = raw.gz;
|
||||||
|
if (raw.temp < min.temp) min.temp = raw.temp;
|
||||||
|
|
||||||
|
// Log what the maximum value is
|
||||||
|
if (raw.ax > max.ax) max.ax = raw.ax;
|
||||||
|
if (raw.ay > max.ay) max.ay = raw.ay;
|
||||||
|
if (raw.az > max.az) max.az = raw.az;
|
||||||
|
if (raw.gx > max.gx) max.gx = raw.gx;
|
||||||
|
if (raw.gy > max.gy) max.gy = raw.gy;
|
||||||
|
if (raw.gz > max.gz) max.gz = raw.gz;
|
||||||
|
if (raw.temp > max.temp) max.temp = raw.temp;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(GYRO_USE_INTERRUPT)
|
||||||
|
delayMicroseconds(delayTime);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
raw.ax = average.ax / noIterations;
|
||||||
|
raw.ay = average.ay / noIterations;
|
||||||
|
raw.az = average.az / noIterations;
|
||||||
|
raw.gx = average.gx / noIterations;
|
||||||
|
raw.gy = average.gy / noIterations;
|
||||||
|
raw.gz = average.gz / noIterations;
|
||||||
|
raw.temp = average.temp / noIterations;
|
||||||
|
|
||||||
|
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||||
|
#if defined(GYRO_SHOW_MINMAX)
|
||||||
|
Log.verbose(F("GYRO: Min \t%d\t%d\t%d\t%d\t%d\t%d\t%d." CR), min.ax,
|
||||||
|
min.ay, min.az, min.gx, min.gy, min.gz, min.temp);
|
||||||
|
Log.verbose(F("GYRO: Max \t%d\t%d\t%d\t%d\t%d\t%d\t%d." CR), max.ax,
|
||||||
|
max.ay, max.az, max.gx, max.gy, max.gz, max.temp);
|
||||||
|
#endif
|
||||||
|
Log.verbose(F("GYRO: Average\t%d\t%d\t%d\t%d\t%d\t%d\t%d." CR), raw.ax,
|
||||||
|
raw.ay, raw.az, raw.gx, raw.gy, raw.gz, raw.temp);
|
||||||
|
// Log.verbose(F("GYRO: Result \t%d\t%d\t%d\t%d\t%d\t%d." CR),
|
||||||
|
// average.ax/noIterations, average.ay/noIterations, average.az/noIterations,
|
||||||
|
// average.gx/noIterations,
|
||||||
|
// average.gy/noIterations,
|
||||||
|
// average.gz/noIterations
|
||||||
|
// );
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,85 +184,95 @@ void GyroSensor::readSensor(RawGyroData &raw, const int noIterations, const int
|
|||||||
// Calcuate the angles (tilt)
|
// Calcuate the angles (tilt)
|
||||||
//
|
//
|
||||||
float GyroSensor::calculateAngle(RawGyroData &raw) {
|
float GyroSensor::calculateAngle(RawGyroData &raw) {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||||
Log.verbose(F("GYRO: Calculating the angle." CR) );
|
Log.verbose(F("GYRO: Calculating the angle." CR));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Smooth out the readings to we can have a more stable angle/tilt.
|
// Smooth out the readings to we can have a more stable angle/tilt.
|
||||||
// ------------------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------------------
|
||||||
// Accelerometer full scale range of +/- 2g with Sensitivity Scale Factor of 16,384 LSB(Count)/g.
|
// Accelerometer full scale range of +/- 2g with Sensitivity Scale Factor of
|
||||||
// Gyroscope full scale range of +/- 250 °/s with Sensitivity Scale Factor of 131 LSB (Count)/°/s.
|
// 16,384 LSB(Count)/g. Gyroscope full scale range of +/- 250 °/s with
|
||||||
float ax = ((float) raw.ax)/16384,
|
// Sensitivity Scale Factor of 131 LSB (Count)/°/s.
|
||||||
ay = ((float) raw.ay)/16384,
|
float ax = (static_cast<float>(raw.ax)) / 16384,
|
||||||
az = ((float) raw.az)/16384;
|
ay = (static_cast<float>(raw.ay)) / 16384,
|
||||||
|
az = (static_cast<float>(raw.az)) / 16384;
|
||||||
|
|
||||||
// Source: https://www.nxp.com/docs/en/application-note/AN3461.pdf
|
// Source: https://www.nxp.com/docs/en/application-note/AN3461.pdf
|
||||||
float v = (acos( ay / sqrt( ax*ax + ay*ay + az*az ) ) *180.0 / PI);
|
float v = (acos(ay / sqrt(ax * ax + ay * ay + az * az)) * 180.0 / PI);
|
||||||
//Log.notice(F("GYRO: angle = %F." CR), v );
|
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||||
//double v = (acos( raw.az / sqrt( raw.ax*raw.ax + raw.ay*raw.ay + raw.az*raw.az ) ) *180.0 / PI);
|
Log.verbose(F("GYRO: angle = %F." CR), v);
|
||||||
//Log.notice(F("GYRO: angle = %F." CR), v );
|
|
||||||
|
|
||||||
#if LOG_LEVEL==6
|
|
||||||
Log.verbose(F("GYRO: angle = %F." CR), v );
|
|
||||||
#endif
|
#endif
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Check if the values are high that indicate that the sensor is moving.
|
// Check if the values are high that indicate that the sensor is moving.
|
||||||
//
|
//
|
||||||
bool GyroSensor::isSensorMoving(RawGyroData &raw) {
|
bool GyroSensor::isSensorMoving(RawGyroData &raw) {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||||
Log.verbose(F("GYRO: Checking for sensor movement." CR) );
|
Log.verbose(F("GYRO: Checking for sensor movement." CR));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int x = abs(raw.gx), y = abs(raw.gy), z = abs(raw.gz);
|
int x = abs(raw.gx), y = abs(raw.gy), z = abs(raw.gz);
|
||||||
|
|
||||||
if( x>SENSOR_MOVING_THREASHOLD || y>SENSOR_MOVING_THREASHOLD || z>SENSOR_MOVING_THREASHOLD ) {
|
if (x > SENSOR_MOVING_THREASHOLD || y > SENSOR_MOVING_THREASHOLD ||
|
||||||
Log.notice(F("GYRO: Movement detected (%d)\t%d\t%d\t%d." CR), SENSOR_MOVING_THREASHOLD, x, y, z);
|
z > SENSOR_MOVING_THREASHOLD) {
|
||||||
return true;
|
Log.notice(F("GYRO: Movement detected (%d)\t%d\t%d\t%d." CR),
|
||||||
}
|
SENSOR_MOVING_THREASHOLD, x, y, z);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Read the tilt angle from the gyro.
|
// Read the tilt angle from the gyro.
|
||||||
//
|
//
|
||||||
bool GyroSensor::read() {
|
bool GyroSensor::read() {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||||
Log.verbose(F("GYRO: Getting new gyro position." CR) );
|
Log.verbose(F("GYRO: Getting new gyro position." CR));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
readSensor( lastGyroData, SENSOR_READ_COUNT, SENSOR_READ_DELAY ); // Last param is unused if GYRO_USE_INTERRUPT is defined.
|
if (!sensorConnected) return false;
|
||||||
|
|
||||||
// If the sensor is unstable we return false to signal we dont have valid value
|
readSensor(lastGyroData, SENSOR_READ_COUNT,
|
||||||
if( isSensorMoving(lastGyroData) ) {
|
SENSOR_READ_DELAY); // Last param is unused if GYRO_USE_INTERRUPT
|
||||||
Log.notice(F("GYRO: Sensor is moving." CR) );
|
// is defined.
|
||||||
validValue = false;
|
|
||||||
} else {
|
|
||||||
validValue = true;
|
|
||||||
angle = calculateAngle( lastGyroData );
|
|
||||||
//Log.notice(F("GYRO: Sensor values %d,%d,%d\t%F" CR), raw.ax, raw.ay, raw.az, angle );
|
|
||||||
}
|
|
||||||
|
|
||||||
sensorTemp = ((float) lastGyroData.temp) / 340 + 36.53;
|
// If the sensor is unstable we return false to signal we dont have valid
|
||||||
|
// value
|
||||||
|
if (isSensorMoving(lastGyroData)) {
|
||||||
|
#if !defined(GYRO_DISABLE_LOGGING)
|
||||||
|
Log.notice(F("GYRO: Sensor is moving." CR));
|
||||||
|
#endif
|
||||||
|
validValue = false;
|
||||||
|
} else {
|
||||||
|
validValue = true;
|
||||||
|
angle = calculateAngle(lastGyroData);
|
||||||
|
#if !defined(GYRO_DISABLE_LOGGING)
|
||||||
|
Log.notice(F("GYRO: Sensor values %d,%d,%d\t%F" CR), lastGyroData.ax,
|
||||||
|
lastGyroData.ay, lastGyroData.az, angle);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// The first read value is close to the DS18 value according to my tests, if more reads are
|
sensorTemp = (static_cast<float>(lastGyroData.temp)) / 340 + 36.53;
|
||||||
// done then the gyro temp will increase to much
|
|
||||||
if( initialSensorTemp == INVALID_TEMPERATURE )
|
|
||||||
initialSensorTemp = sensorTemp;
|
|
||||||
|
|
||||||
return validValue;
|
// The first read value is close to the DS18 value according to my tests, if
|
||||||
|
// more reads are done then the gyro temp will increase to much
|
||||||
|
if (initialSensorTemp == INVALID_TEMPERATURE) initialSensorTemp = sensorTemp;
|
||||||
|
|
||||||
|
return validValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Dump the stored calibration values.
|
// Dump the stored calibration values.
|
||||||
//
|
//
|
||||||
void GyroSensor::dumpCalibration() {
|
void GyroSensor::dumpCalibration() {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||||
Log.verbose(F("GYRO: Accel offset\t%d\t%d\t%d" CR), calibrationOffset.ax, calibrationOffset.ay, calibrationOffset.az );
|
Log.verbose(F("GYRO: Accel offset\t%d\t%d\t%d" CR), calibrationOffset.ax,
|
||||||
Log.verbose(F("GYRO: Gyro offset \t%d\t%d\t%d" CR), calibrationOffset.gx, calibrationOffset.gy, calibrationOffset.gz );
|
calibrationOffset.ay, calibrationOffset.az);
|
||||||
|
Log.verbose(F("GYRO: Gyro offset \t%d\t%d\t%d" CR), calibrationOffset.gx,
|
||||||
|
calibrationOffset.gy, calibrationOffset.gz);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,96 +280,116 @@ void GyroSensor::dumpCalibration() {
|
|||||||
// Update the sensor with out calculated offsets.
|
// Update the sensor with out calculated offsets.
|
||||||
//
|
//
|
||||||
void GyroSensor::applyCalibration() {
|
void GyroSensor::applyCalibration() {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||||
Log.verbose(F("GYRO: Applying calibration offsets to sensor." CR) );
|
Log.verbose(F("GYRO: Applying calibration offsets to sensor." CR));
|
||||||
#endif
|
#endif
|
||||||
if( ( calibrationOffset.ax + calibrationOffset.ay + calibrationOffset.az + calibrationOffset.gx + calibrationOffset.gy + calibrationOffset.gz ) == 0 ) {
|
|
||||||
Log.error(F("GYRO: No valid calibraion values exist, aborting." CR) );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
accelgyro.setXAccelOffset( calibrationOffset.ax );
|
if ((calibrationOffset.ax + calibrationOffset.ay + calibrationOffset.az +
|
||||||
accelgyro.setYAccelOffset( calibrationOffset.ay );
|
calibrationOffset.gx + calibrationOffset.gy + calibrationOffset.gz) ==
|
||||||
accelgyro.setZAccelOffset( calibrationOffset.az );
|
0) {
|
||||||
accelgyro.setXGyroOffset( calibrationOffset.gx );
|
Log.error(F("GYRO: No valid calibraion values exist, aborting." CR));
|
||||||
accelgyro.setYGyroOffset( calibrationOffset.gy );
|
return;
|
||||||
accelgyro.setZGyroOffset( calibrationOffset.gz );
|
}
|
||||||
|
|
||||||
|
accelgyro.setXAccelOffset(calibrationOffset.ax);
|
||||||
|
accelgyro.setYAccelOffset(calibrationOffset.ay);
|
||||||
|
accelgyro.setZAccelOffset(calibrationOffset.az);
|
||||||
|
accelgyro.setXGyroOffset(calibrationOffset.gx);
|
||||||
|
accelgyro.setYGyroOffset(calibrationOffset.gy);
|
||||||
|
accelgyro.setZGyroOffset(calibrationOffset.gz);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Calculate the offsets for calibration.
|
// Calculate the offsets for calibration.
|
||||||
//
|
//
|
||||||
void GyroSensor::calibrateSensor() {
|
void GyroSensor::calibrateSensor() {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||||
Log.verbose(F("GYRO: Calibrating sensor" CR) );
|
Log.verbose(F("GYRO: Calibrating sensor" CR));
|
||||||
#endif
|
#endif
|
||||||
//accelgyro.PrintActiveOffsets();
|
// accelgyro.PrintActiveOffsets();
|
||||||
//Serial.print( CR );
|
// Serial.print( CR );
|
||||||
|
|
||||||
accelgyro.setDLPFMode(MPU6050_DLPF_BW_5);
|
accelgyro.setDLPFMode(MPU6050_DLPF_BW_5);
|
||||||
accelgyro.CalibrateAccel(6); // 6 = 600 readings
|
accelgyro.CalibrateAccel(6); // 6 = 600 readings
|
||||||
accelgyro.CalibrateGyro(6);
|
accelgyro.CalibrateGyro(6);
|
||||||
|
|
||||||
accelgyro.PrintActiveOffsets();
|
accelgyro.PrintActiveOffsets();
|
||||||
Serial.print( CR );
|
Serial.print(CR);
|
||||||
|
|
||||||
calibrationOffset.ax = accelgyro.getXAccelOffset();
|
calibrationOffset.ax = accelgyro.getXAccelOffset();
|
||||||
calibrationOffset.ay = accelgyro.getYAccelOffset();
|
calibrationOffset.ay = accelgyro.getYAccelOffset();
|
||||||
calibrationOffset.az = accelgyro.getZAccelOffset();
|
calibrationOffset.az = accelgyro.getZAccelOffset();
|
||||||
calibrationOffset.gx = accelgyro.getXGyroOffset();
|
calibrationOffset.gx = accelgyro.getXGyroOffset();
|
||||||
calibrationOffset.gy = accelgyro.getYGyroOffset();
|
calibrationOffset.gy = accelgyro.getYGyroOffset();
|
||||||
calibrationOffset.gz = accelgyro.getZGyroOffset();
|
calibrationOffset.gz = accelgyro.getZGyroOffset();
|
||||||
|
|
||||||
// Save the calibrated values
|
// Save the calibrated values
|
||||||
myConfig.setGyroCalibration( calibrationOffset );
|
myConfig.setGyroCalibration(calibrationOffset);
|
||||||
myConfig.saveFile();
|
myConfig.saveFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Calibrate the device.
|
// Calibrate the device.
|
||||||
//
|
//
|
||||||
void GyroSensor::debug() {
|
void GyroSensor::debug() {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||||
Log.verbose(F("GYRO: Debug - Clock src %d." CR), accelgyro.getClockSource() );
|
Log.verbose(F("GYRO: Debug - Clock src %d." CR),
|
||||||
Log.verbose(F("GYRO: Debug - Device ID %d." CR), accelgyro.getDeviceID() );
|
accelgyro.getClockSource());
|
||||||
Log.verbose(F("GYRO: Debug - DHPF Mode %d." CR), accelgyro.getDHPFMode() );
|
Log.verbose(F("GYRO: Debug - Device ID %d." CR), accelgyro.getDeviceID());
|
||||||
Log.verbose(F("GYRO: Debug - DMP on %s." CR), accelgyro.getDMPEnabled()?"on":"off" );
|
Log.verbose(F("GYRO: Debug - DHPF Mode %d." CR), accelgyro.getDHPFMode());
|
||||||
Log.verbose(F("GYRO: Debug - Acc range %d." CR), accelgyro.getFullScaleAccelRange() );
|
Log.verbose(F("GYRO: Debug - DMP on %s." CR),
|
||||||
Log.verbose(F("GYRO: Debug - Gyr range %d." CR), accelgyro.getFullScaleGyroRange() );
|
accelgyro.getDMPEnabled() ? "on" : "off");
|
||||||
Log.verbose(F("GYRO: Debug - Int %s." CR), accelgyro.getIntEnabled()?"on":"off" );
|
Log.verbose(F("GYRO: Debug - Acc range %d." CR),
|
||||||
Log.verbose(F("GYRO: Debug - Clock %d." CR), accelgyro.getMasterClockSpeed() );
|
accelgyro.getFullScaleAccelRange());
|
||||||
Log.verbose(F("GYRO: Debug - Rate %d." CR), accelgyro.getRate() );
|
Log.verbose(F("GYRO: Debug - Gyr range %d." CR),
|
||||||
Log.verbose(F("GYRO: Debug - Gyro range %d." CR), accelgyro.getFullScaleGyroRange() );
|
accelgyro.getFullScaleGyroRange());
|
||||||
// Log.verbose(F("GYRO: Debug - I2C bypass %s." CR), accelgyro.getI2CBypassEnabled()?"on":"off" );
|
Log.verbose(F("GYRO: Debug - Int %s." CR),
|
||||||
// Log.verbose(F("GYRO: Debug - I2C master %s." CR), accelgyro.getI2CMasterModeEnabled()?"on":"off" );
|
accelgyro.getIntEnabled() ? "on" : "off");
|
||||||
Log.verbose(F("GYRO: Debug - Acc FactX %d." CR), accelgyro.getAccelXSelfTestFactoryTrim() );
|
Log.verbose(F("GYRO: Debug - Clock %d." CR),
|
||||||
Log.verbose(F("GYRO: Debug - Acc FactY %d." CR), accelgyro.getAccelYSelfTestFactoryTrim() );
|
accelgyro.getMasterClockSpeed());
|
||||||
Log.verbose(F("GYRO: Debug - Acc FactZ %d." CR), accelgyro.getAccelZSelfTestFactoryTrim() );
|
Log.verbose(F("GYRO: Debug - Rate %d." CR), accelgyro.getRate());
|
||||||
Log.verbose(F("GYRO: Debug - Gyr FactX %d." CR), accelgyro.getGyroXSelfTestFactoryTrim() );
|
Log.verbose(F("GYRO: Debug - Gyro range %d." CR),
|
||||||
Log.verbose(F("GYRO: Debug - Gyr FactY %d." CR), accelgyro.getGyroYSelfTestFactoryTrim() );
|
accelgyro.getFullScaleGyroRange());
|
||||||
Log.verbose(F("GYRO: Debug - Gyr FactZ %d." CR), accelgyro.getGyroZSelfTestFactoryTrim() );
|
Log.verbose(F("GYRO: Debug - Acc FactX %d." CR),
|
||||||
|
accelgyro.getAccelXSelfTestFactoryTrim());
|
||||||
|
Log.verbose(F("GYRO: Debug - Acc FactY %d." CR),
|
||||||
|
accelgyro.getAccelYSelfTestFactoryTrim());
|
||||||
|
Log.verbose(F("GYRO: Debug - Acc FactZ %d." CR),
|
||||||
|
accelgyro.getAccelZSelfTestFactoryTrim());
|
||||||
|
Log.verbose(F("GYRO: Debug - Gyr FactX %d." CR),
|
||||||
|
accelgyro.getGyroXSelfTestFactoryTrim());
|
||||||
|
Log.verbose(F("GYRO: Debug - Gyr FactY %d." CR),
|
||||||
|
accelgyro.getGyroYSelfTestFactoryTrim());
|
||||||
|
Log.verbose(F("GYRO: Debug - Gyr FactZ %d." CR),
|
||||||
|
accelgyro.getGyroZSelfTestFactoryTrim());
|
||||||
|
|
||||||
switch( accelgyro.getFullScaleAccelRange() ) {
|
switch (accelgyro.getFullScaleAccelRange()) {
|
||||||
case 0:
|
case 0:
|
||||||
Log.verbose(F("GYRO: Debug - Accel range +/- 2g." CR));
|
Log.verbose(F("GYRO: Debug - Accel range +/- 2g." CR));
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
Log.verbose(F("GYRO: Debug - Accel range +/- 4g." CR));
|
Log.verbose(F("GYRO: Debug - Accel range +/- 4g." CR));
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
Log.verbose(F("GYRO: Debug - Accel range +/- 8g." CR));
|
Log.verbose(F("GYRO: Debug - Accel range +/- 8g." CR));
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
Log.verbose(F("GYRO: Debug - Accel range +/- 16g." CR));
|
Log.verbose(F("GYRO: Debug - Accel range +/- 16g." CR));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.verbose(F("GYRO: Debug - Acc OffX %d\t%d." CR), accelgyro.getXAccelOffset(), calibrationOffset.az );
|
Log.verbose(F("GYRO: Debug - Acc OffX %d\t%d." CR),
|
||||||
Log.verbose(F("GYRO: Debug - Acc OffY %d\t%d." CR), accelgyro.getYAccelOffset(), calibrationOffset.ay );
|
accelgyro.getXAccelOffset(), calibrationOffset.az);
|
||||||
Log.verbose(F("GYRO: Debug - Acc OffZ %d\t%d." CR), accelgyro.getZAccelOffset(), calibrationOffset.az );
|
Log.verbose(F("GYRO: Debug - Acc OffY %d\t%d." CR),
|
||||||
Log.verbose(F("GYRO: Debug - Gyr OffX %d\t%d." CR), accelgyro.getXGyroOffset(), calibrationOffset.gx );
|
accelgyro.getYAccelOffset(), calibrationOffset.ay);
|
||||||
Log.verbose(F("GYRO: Debug - Gyr OffY %d\t%d." CR), accelgyro.getYGyroOffset(), calibrationOffset.gy );
|
Log.verbose(F("GYRO: Debug - Acc OffZ %d\t%d." CR),
|
||||||
Log.verbose(F("GYRO: Debug - Gyr OffZ %d\t%d." CR), accelgyro.getZGyroOffset(), calibrationOffset.gz );
|
accelgyro.getZAccelOffset(), calibrationOffset.az);
|
||||||
|
Log.verbose(F("GYRO: Debug - Gyr OffX %d\t%d." CR),
|
||||||
|
accelgyro.getXGyroOffset(), calibrationOffset.gx);
|
||||||
|
Log.verbose(F("GYRO: Debug - Gyr OffY %d\t%d." CR),
|
||||||
|
accelgyro.getYGyroOffset(), calibrationOffset.gy);
|
||||||
|
Log.verbose(F("GYRO: Debug - Gyr OffZ %d\t%d." CR),
|
||||||
|
accelgyro.getZGyroOffset(), calibrationOffset.gz);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
87
src/gyro.h
@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2021 Magnus
|
|
||||||
|
|
||||||
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 above 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.
|
|
||||||
*/
|
|
||||||
#ifndef _GYRO_H
|
|
||||||
#define _GYRO_H
|
|
||||||
|
|
||||||
#define I2CDEV_IMPLEMENTATION I2CDEV_ARDUINO_WIRE
|
|
||||||
//#define I2CDEV_IMPLEMENTATION I2CDEV_BUILTIN_SBWIRE
|
|
||||||
|
|
||||||
// Includes
|
|
||||||
#include <arduino.h>
|
|
||||||
#include "MPU6050.h"
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
// Classes
|
|
||||||
struct RawGyroDataL { // Used for average multiple readings
|
|
||||||
long ax; // Raw Acceleration
|
|
||||||
long ay;
|
|
||||||
long az;
|
|
||||||
|
|
||||||
long gx; // Raw Position
|
|
||||||
long gy;
|
|
||||||
long gz;
|
|
||||||
|
|
||||||
long temp; // Only for information (temperature of chip)
|
|
||||||
};
|
|
||||||
|
|
||||||
#define INVALID_TEMPERATURE -273
|
|
||||||
|
|
||||||
class GyroSensor {
|
|
||||||
private:
|
|
||||||
MPU6050 accelgyro;
|
|
||||||
bool sensorConnected = false;
|
|
||||||
bool validValue = false;
|
|
||||||
float angle = 0;
|
|
||||||
float sensorTemp = 0;
|
|
||||||
float initialSensorTemp = INVALID_TEMPERATURE;
|
|
||||||
RawGyroData calibrationOffset;
|
|
||||||
RawGyroData lastGyroData;
|
|
||||||
|
|
||||||
void debug();
|
|
||||||
void applyCalibration();
|
|
||||||
void dumpCalibration();
|
|
||||||
void readSensor(RawGyroData &raw, const int noIterations = 100, const int delayTime = 1);
|
|
||||||
bool isSensorMoving(RawGyroData &raw);
|
|
||||||
float calculateAngle(RawGyroData &raw);
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool setup();
|
|
||||||
bool read();
|
|
||||||
void calibrateSensor();
|
|
||||||
|
|
||||||
const RawGyroData& getLastGyroData() { return lastGyroData; }
|
|
||||||
float getAngle() { return angle; };
|
|
||||||
float getSensorTempC() { return sensorTemp; };
|
|
||||||
float getInitialSensorTempC() { return initialSensorTemp; };
|
|
||||||
bool isConnected() { return sensorConnected; };
|
|
||||||
bool hasValue() { return validValue; };
|
|
||||||
void enterSleep();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Global instance created
|
|
||||||
extern GyroSensor myGyro;
|
|
||||||
|
|
||||||
#endif // _GYRO_H
|
|
||||||
|
|
||||||
// EOF
|
|
89
src/gyro.hpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
|
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 above 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.
|
||||||
|
*/
|
||||||
|
#ifndef SRC_GYRO_HPP_
|
||||||
|
#define SRC_GYRO_HPP_
|
||||||
|
|
||||||
|
#define I2CDEV_IMPLEMENTATION I2CDEV_ARDUINO_WIRE
|
||||||
|
// #define I2CDEV_IMPLEMENTATION I2CDEV_BUILTIN_SBWIRE
|
||||||
|
|
||||||
|
// Includes
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <MPU6050.h>
|
||||||
|
|
||||||
|
#include <config.hpp>
|
||||||
|
|
||||||
|
// Classes
|
||||||
|
struct RawGyroDataL { // Used for average multiple readings
|
||||||
|
int32_t ax; // Raw Acceleration
|
||||||
|
int32_t ay;
|
||||||
|
int32_t az;
|
||||||
|
|
||||||
|
int32_t gx; // Raw Position
|
||||||
|
int32_t gy;
|
||||||
|
int32_t gz;
|
||||||
|
|
||||||
|
int32_t temp; // Only for information (temperature of chip)
|
||||||
|
};
|
||||||
|
|
||||||
|
#define INVALID_TEMPERATURE -273
|
||||||
|
|
||||||
|
class GyroSensor {
|
||||||
|
private:
|
||||||
|
MPU6050 accelgyro;
|
||||||
|
bool sensorConnected = false;
|
||||||
|
bool validValue = false;
|
||||||
|
float angle = 0;
|
||||||
|
float sensorTemp = 0;
|
||||||
|
float initialSensorTemp = INVALID_TEMPERATURE;
|
||||||
|
RawGyroData calibrationOffset;
|
||||||
|
RawGyroData lastGyroData;
|
||||||
|
|
||||||
|
void debug();
|
||||||
|
void applyCalibration();
|
||||||
|
void dumpCalibration();
|
||||||
|
void readSensor(RawGyroData &raw, const int noIterations = 100,
|
||||||
|
const int delayTime = 1);
|
||||||
|
bool isSensorMoving(RawGyroData &raw);
|
||||||
|
float calculateAngle(RawGyroData &raw);
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool setup();
|
||||||
|
bool read();
|
||||||
|
void calibrateSensor();
|
||||||
|
|
||||||
|
const RawGyroData &getLastGyroData() { return lastGyroData; }
|
||||||
|
float getAngle() { return angle; }
|
||||||
|
float getSensorTempC() { return sensorTemp; }
|
||||||
|
float getInitialSensorTempC() { return initialSensorTemp; }
|
||||||
|
bool isConnected() { return sensorConnected; }
|
||||||
|
bool hasValue() { return validValue; }
|
||||||
|
void enterSleep();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Global instance created
|
||||||
|
extern GyroSensor myGyro;
|
||||||
|
|
||||||
|
#endif // SRC_GYRO_HPP_
|
||||||
|
|
||||||
|
// EOF
|
250
src/helper.cpp
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 Magnus
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -21,12 +21,13 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "helper.h"
|
|
||||||
#include "config.h"
|
|
||||||
#include "gyro.h"
|
|
||||||
#include "tempsensor.h"
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#include <ESP8266HTTPClient.h>
|
#include <ESP8266HTTPClient.h>
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
|
||||||
|
#include <config.hpp>
|
||||||
|
#include <gyro.hpp>
|
||||||
|
#include <helper.hpp>
|
||||||
|
#include <tempsensor.hpp>
|
||||||
|
|
||||||
SerialDebug mySerial;
|
SerialDebug mySerial;
|
||||||
BatteryVoltage myBatteryVoltage;
|
BatteryVoltage myBatteryVoltage;
|
||||||
@ -35,8 +36,10 @@ BatteryVoltage myBatteryVoltage;
|
|||||||
// Print the heap information.
|
// Print the heap information.
|
||||||
//
|
//
|
||||||
void printHeap() {
|
void printHeap() {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6
|
||||||
Log.verbose(F("HELP: Heap %d kb, HeapFrag %d %%, FreeSketch %d kb." CR), ESP.getFreeHeap()/1024, ESP.getHeapFragmentation(), ESP.getFreeSketchSpace()/1024 );
|
Log.verbose(F("HELP: Heap %d kb, HeapFrag %d %%, FreeSketch %d kb." CR),
|
||||||
|
ESP.getFreeHeap() / 1024, ESP.getHeapFragmentation(),
|
||||||
|
ESP.getFreeSketchSpace() / 1024);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,44 +47,45 @@ void printHeap() {
|
|||||||
// Enter deep sleep for the defined duration (Argument is seconds)
|
// Enter deep sleep for the defined duration (Argument is seconds)
|
||||||
//
|
//
|
||||||
void deepSleep(int t) {
|
void deepSleep(int t) {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6
|
||||||
Log.verbose(F("HELP: Entering sleep mode for %ds." CR), t );
|
Log.verbose(F("HELP: Entering sleep mode for %ds." CR), t);
|
||||||
#endif
|
#endif
|
||||||
uint64_t wake = t * 1000000;
|
uint32_t wake = t * 1000000;
|
||||||
ESP.deepSleep( wake );
|
ESP.deepSleep(wake);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Print the build options used
|
// Print the build options used
|
||||||
//
|
//
|
||||||
void printBuildOptions() {
|
void printBuildOptions() {
|
||||||
Log.notice( F("Build options: %s LOGLEVEL %d "
|
Log.notice(F("Build options: %s LOGLEVEL %d "
|
||||||
#ifdef SKIP_SLEEPMODE
|
#ifdef SKIP_SLEEPMODE
|
||||||
"SKIP_SLEEP "
|
"SKIP_SLEEP "
|
||||||
#endif
|
#endif
|
||||||
#ifdef EMBED_HTML
|
#ifdef EMBED_HTML
|
||||||
"EMBED_HTML "
|
"EMBED_HTML "
|
||||||
#endif
|
#endif
|
||||||
#ifdef COLLECT_PERFDATA
|
#ifdef COLLECT_PERFDATA
|
||||||
"PERFDATA "
|
"PERFDATA "
|
||||||
#endif
|
#endif
|
||||||
#ifdef ACTIVATE_OTA
|
#ifdef ACTIVATE_OTA
|
||||||
"OTA "
|
"OTA "
|
||||||
#endif
|
#endif
|
||||||
CR), CFG_APPVER, LOG_LEVEL );
|
CR),
|
||||||
|
CFG_APPVER, LOG_LEVEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Configure serial debug output
|
// Configure serial debug output
|
||||||
//
|
//
|
||||||
SerialDebug::SerialDebug(const long 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();
|
||||||
Serial.begin(serialSpeed);
|
Serial.begin(serialSpeed);
|
||||||
|
|
||||||
getLog()->begin(LOG_LEVEL, &Serial, true);
|
getLog()->begin(LOG_LEVEL, &Serial, true);
|
||||||
getLog()->setPrefix(printTimestamp);
|
getLog()->setPrefix(printTimestamp);
|
||||||
getLog()->notice(F("SDBG: Serial logging started at %l." CR), serialSpeed);
|
getLog()->notice(F("SDBG: Serial logging started at %u." CR), serialSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -89,7 +93,7 @@ SerialDebug::SerialDebug(const long serialSpeed) {
|
|||||||
//
|
//
|
||||||
void printTimestamp(Print* _logOutput, int _logLevel) {
|
void printTimestamp(Print* _logOutput, int _logLevel) {
|
||||||
char c[12];
|
char c[12];
|
||||||
sprintf(c, "%10lu ", millis());
|
snprintf(c, sizeof(c), "%10lu ", millis());
|
||||||
_logOutput->print(c);
|
_logOutput->print(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,16 +101,19 @@ void printTimestamp(Print* _logOutput, int _logLevel) {
|
|||||||
// Read and calculate the battery voltage
|
// 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 voltage (from max 5V)
|
// The analog pin can only handle 3.3V maximum voltage so we need to reduce
|
||||||
float factor = myConfig.getVoltageFactor(); // Default value is 1.63
|
// the voltage (from max 5V)
|
||||||
int v = analogRead( A0 );
|
float factor = myConfig.getVoltageFactor(); // Default value is 1.63
|
||||||
batteryLevel = ((3.3/1023)*v)*factor;
|
int v = analogRead(A0);
|
||||||
#if LOG_LEVEL==6
|
batteryLevel = ((3.3 / 1023) * v) * factor;
|
||||||
Log.verbose(F("BATT: Reading voltage level. Factor=%F Value=%d, Voltage=%F." CR), factor, v, batteryLevel );
|
#if LOG_LEVEL == 6
|
||||||
|
Log.verbose(
|
||||||
|
F("BATT: Reading voltage level. Factor=%F Value=%d, Voltage=%F." CR),
|
||||||
|
factor, v, batteryLevel);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined( COLLECT_PERFDATA )
|
#if defined(COLLECT_PERFDATA)
|
||||||
|
|
||||||
PerfLogging myPerfLogging;
|
PerfLogging myPerfLogging;
|
||||||
|
|
||||||
@ -114,143 +121,150 @@ PerfLogging myPerfLogging;
|
|||||||
// Clear the current cache
|
// Clear the current cache
|
||||||
//
|
//
|
||||||
void PerfLogging::clear() {
|
void PerfLogging::clear() {
|
||||||
// Clear the measurements
|
// Clear the measurements
|
||||||
if( first == 0 )
|
if (first == 0) return;
|
||||||
return;
|
|
||||||
|
|
||||||
PerfEntry* pe = first;
|
PerfEntry* pe = first;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
pe->max = 0;
|
pe->max = 0;
|
||||||
pe->start = 0;
|
pe->start = 0;
|
||||||
pe->end = 0;
|
pe->end = 0;
|
||||||
pe->mA = 0;
|
pe->mA = 0;
|
||||||
pe->V = 0;
|
pe->V = 0;
|
||||||
pe = pe->next;
|
pe = pe->next;
|
||||||
} while( pe != 0 );
|
} while (pe != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Start measuring this performance point
|
// 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
|
// 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);
|
||||||
|
|
||||||
if( pe != 0 ) {
|
if (pe != 0) {
|
||||||
pe->end = millis();
|
pe->end = millis();
|
||||||
|
|
||||||
unsigned long t = pe->end - pe->start;
|
uint32_t t = pe->end - pe->start;
|
||||||
|
|
||||||
if( t > pe->max )
|
if (t > pe->max) pe->max = t;
|
||||||
pe->max = t;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Print the collected performance data
|
// Print the collected performance data
|
||||||
//
|
//
|
||||||
void PerfLogging::print() {
|
void PerfLogging::print() {
|
||||||
PerfEntry* pe = first;
|
PerfEntry* pe = first;
|
||||||
|
|
||||||
while( pe != 0 ) {
|
while (pe != 0) {
|
||||||
//Log.notice( F("PERF: %s=%l ms (%l, %l)" CR), pe->key, (pe->end - pe->start), pe->start, pe->end );
|
Log.notice(F("PERF: %s %ums" CR), pe->key, pe->max);
|
||||||
Log.notice( F("PERF: %s %lms" CR), pe->key, pe->max );
|
pe = pe->next;
|
||||||
pe = pe->next;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Push collected performance data to influx (use influx configuration)
|
// Push collected performance data to influx (use influx configuration)
|
||||||
//
|
//
|
||||||
void PerfLogging::pushInflux() {
|
void PerfLogging::pushInflux() {
|
||||||
if( !myConfig.isInfluxDb2Active() )
|
if (!myConfig.isInfluxDb2Active()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
WiFiClient client;
|
WiFiClient client;
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
String serverPath = String(myConfig.getInfluxDb2PushUrl()) + "/api/v2/write?org=" +
|
String serverPath =
|
||||||
String(myConfig.getInfluxDb2PushOrg()) + "&bucket=" +
|
String(myConfig.getInfluxDb2PushUrl()) +
|
||||||
String(myConfig.getInfluxDb2PushBucket());
|
"/api/v2/write?org=" + String(myConfig.getInfluxDb2PushOrg()) +
|
||||||
|
"&bucket=" + String(myConfig.getInfluxDb2PushBucket());
|
||||||
|
|
||||||
http.begin( client, serverPath);
|
http.begin(client, serverPath);
|
||||||
|
|
||||||
// Create body for influxdb2, format used
|
// Create body for influxdb2, format used
|
||||||
// key,host=mdns value=0.0
|
// key,host=mdns value=0.0
|
||||||
String body;
|
String body;
|
||||||
|
|
||||||
// Create the payload with performance data.
|
// Create the payload with performance data.
|
||||||
// ------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------
|
||||||
PerfEntry* pe = first;
|
PerfEntry* pe = first;
|
||||||
char buf[100];
|
char buf[100];
|
||||||
sprintf( &buf[0], "perf,host=%s,device=%s ", myConfig.getMDNS(), myConfig.getID() );
|
snprintf(&buf[0], sizeof(buf), "perf,host=%s,device=%s ", myConfig.getMDNS(),
|
||||||
body += &buf[0];
|
myConfig.getID());
|
||||||
|
body += &buf[0];
|
||||||
|
|
||||||
while( pe != 0 ) {
|
while (pe != 0) {
|
||||||
if( pe->max ) {
|
if (pe->max) {
|
||||||
if( pe->next )
|
if (pe->next)
|
||||||
sprintf( &buf[0], "%s=%ld,", pe->key, pe->max);
|
snprintf(&buf[0], sizeof(buf), "%s=%u,", pe->key, pe->max);
|
||||||
else
|
else
|
||||||
sprintf( &buf[0], "%s=%ld", pe->key, pe->max);
|
snprintf(&buf[0], sizeof(buf), "%s=%u", pe->key, pe->max);
|
||||||
|
|
||||||
body += &buf[0];
|
body += &buf[0];
|
||||||
}
|
|
||||||
pe = pe->next;
|
|
||||||
}
|
}
|
||||||
|
pe = pe->next;
|
||||||
|
}
|
||||||
|
|
||||||
// Create the payload with debug data for validating sensor stability
|
// Create the payload with debug data for validating sensor stability
|
||||||
// ------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------
|
||||||
sprintf( &buf[0], "\ndebug,host=%s,device=%s ", myConfig.getMDNS(), myConfig.getID() );
|
snprintf(&buf[0], sizeof(buf), "\ndebug,host=%s,device=%s ",
|
||||||
body += &buf[0];
|
myConfig.getMDNS(), myConfig.getID());
|
||||||
sprintf( &buf[0], "angle=%.4f,gyro-ax=%d,gyro-ay=%d,gyro-az=%d,gyro-temp=%.2f,ds-temp=%.2f", myGyro.getAngle(), myGyro.getLastGyroData().ax,
|
body += &buf[0];
|
||||||
myGyro.getLastGyroData().ay, myGyro.getLastGyroData().az, myGyro.getSensorTempC(), myTempSensor.getTempC() );
|
snprintf(
|
||||||
body += &buf[0];
|
&buf[0], sizeof(buf),
|
||||||
|
"angle=%.4f,gyro-ax=%d,gyro-ay=%d,gyro-az=%d,gyro-temp=%.2f,ds-temp=%.2f",
|
||||||
|
myGyro.getAngle(), myGyro.getLastGyroData().ax,
|
||||||
|
myGyro.getLastGyroData().ay, myGyro.getLastGyroData().az,
|
||||||
|
myGyro.getSensorTempC(), myTempSensor.getTempC());
|
||||||
|
body += &buf[0];
|
||||||
|
|
||||||
// Log.notice(F("PERF: data %s." CR), body.c_str() );
|
// Log.notice(F("PERF: data %s." CR), body.c_str() );
|
||||||
|
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6
|
||||||
Log.verbose(F("PERF: url %s." CR), serverPath.c_str());
|
Log.verbose(F("PERF: url %s." CR), serverPath.c_str());
|
||||||
Log.verbose(F("PERF: data %s." CR), body.c_str() );
|
Log.verbose(F("PERF: data %s." CR), body.c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Send HTTP POST request
|
// Send HTTP POST request
|
||||||
String auth = "Token " + String( myConfig.getInfluxDb2PushToken() );
|
String auth = "Token " + String(myConfig.getInfluxDb2PushToken());
|
||||||
http.addHeader(F("Authorization"), auth.c_str() );
|
http.addHeader(F("Authorization"), auth.c_str());
|
||||||
int httpResponseCode = http.POST(body);
|
int httpResponseCode = http.POST(body);
|
||||||
|
|
||||||
if (httpResponseCode==204) {
|
if (httpResponseCode == 204) {
|
||||||
Log.notice(F("PERF: InfluxDB2 push performance data successful, response=%d" CR), httpResponseCode);
|
Log.notice(
|
||||||
} else {
|
F("PERF: InfluxDB2 push performance data successful, response=%d" CR),
|
||||||
Log.error(F("PERF: InfluxDB2 push performance data failed, response=%d" CR), httpResponseCode);
|
httpResponseCode);
|
||||||
}
|
} else {
|
||||||
|
Log.error(F("PERF: InfluxDB2 push performance data failed, response=%d" CR),
|
||||||
|
httpResponseCode);
|
||||||
|
}
|
||||||
|
|
||||||
http.end();
|
http.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // COLLECT_PERFDATA
|
#endif // COLLECT_PERFDATA
|
||||||
|
|
||||||
//
|
//
|
||||||
// Convert float to formatted string with n decimals. Buffer should be at least 10 chars.
|
// 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
|
// 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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// EOF
|
// EOF
|
156
src/helper.h
@ -1,156 +0,0 @@
|
|||||||
/*
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2021 Magnus
|
|
||||||
|
|
||||||
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 above 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.
|
|
||||||
*/
|
|
||||||
#ifndef _HELPER_H
|
|
||||||
#define _HELPER_H
|
|
||||||
|
|
||||||
// Includes
|
|
||||||
#include <ArduinoLog.h>
|
|
||||||
|
|
||||||
// Sleep mode
|
|
||||||
void deepSleep(int t);
|
|
||||||
|
|
||||||
// Show build options
|
|
||||||
void printBuildOptions();
|
|
||||||
|
|
||||||
// Float to String
|
|
||||||
char* convertFloatToString( float f, char* buf, int dec = 2);
|
|
||||||
float reduceFloatPrecision( float f, int dec = 2 );
|
|
||||||
|
|
||||||
// Logging via serial
|
|
||||||
void printTimestamp(Print* _logOutput, int _logLevel);
|
|
||||||
void printNewline(Print* _logOutput);
|
|
||||||
void printHeap();
|
|
||||||
|
|
||||||
// Classes
|
|
||||||
class SerialDebug {
|
|
||||||
public:
|
|
||||||
SerialDebug(const long serialSpeed = 115200L);
|
|
||||||
static Logging* getLog() { return &Log; };
|
|
||||||
};
|
|
||||||
|
|
||||||
class BatteryVoltage {
|
|
||||||
private:
|
|
||||||
float batteryLevel;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void read();
|
|
||||||
float getVoltage() { return batteryLevel; };
|
|
||||||
};
|
|
||||||
|
|
||||||
#if defined( COLLECT_PERFDATA )
|
|
||||||
|
|
||||||
class PerfLogging {
|
|
||||||
private:
|
|
||||||
struct PerfEntry {
|
|
||||||
unsigned long start; // millis()
|
|
||||||
unsigned long end; // millis()
|
|
||||||
unsigned long max; // max time in ms
|
|
||||||
const char* key; // measurement
|
|
||||||
|
|
||||||
PerfEntry* next; // Next in the linked list
|
|
||||||
|
|
||||||
float mA; // Power consumption
|
|
||||||
float V; // Power consumption
|
|
||||||
};
|
|
||||||
|
|
||||||
PerfEntry* first = 0;
|
|
||||||
bool measurePower = false;
|
|
||||||
|
|
||||||
PerfEntry* find( const char* k ) {
|
|
||||||
if( first == 0 )
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
PerfEntry* pe = first;
|
|
||||||
|
|
||||||
while( strcmp( k, pe->key ) != 0 ) {
|
|
||||||
if( pe->next == 0 )
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
pe = pe->next;
|
|
||||||
}
|
|
||||||
return pe;
|
|
||||||
};
|
|
||||||
|
|
||||||
PerfEntry* add( const char* k ) {
|
|
||||||
if( first == 0 ) {
|
|
||||||
first = new PerfEntry();
|
|
||||||
first->key = k;
|
|
||||||
first->next = 0;
|
|
||||||
first->max = 0;
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
|
|
||||||
PerfEntry* pe = first;
|
|
||||||
|
|
||||||
while( strcmp( k, pe->key ) != 0 ) {
|
|
||||||
if( pe->next == 0 ) {
|
|
||||||
pe->next = new PerfEntry();
|
|
||||||
pe->next->key = k;
|
|
||||||
pe->next->max = 0;
|
|
||||||
pe->next->next = 0;
|
|
||||||
return pe->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
pe = pe->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pe;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
void clear();
|
|
||||||
void start( const char* key );
|
|
||||||
void stop( const char* key );
|
|
||||||
void print();
|
|
||||||
void pushInflux();
|
|
||||||
};
|
|
||||||
|
|
||||||
extern PerfLogging myPerfLogging;
|
|
||||||
|
|
||||||
// Use these to collect performance data from various parts of the code
|
|
||||||
#define LOG_PERF_START(s) myPerfLogging.start(s)
|
|
||||||
#define LOG_PERF_STOP(s) myPerfLogging.stop(s)
|
|
||||||
//#define LOG_PERF_PRINT() myPerfLogging.print()
|
|
||||||
#define LOG_PERF_PRINT()
|
|
||||||
#define LOG_PERF_CLEAR() myPerfLogging.clear()
|
|
||||||
#define LOG_PERF_PUSH() myPerfLogging.pushInflux()
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
// These will disable the performance collection function
|
|
||||||
#define LOG_PERF_START(s)
|
|
||||||
#define LOG_PERF_STOP(s)
|
|
||||||
#define LOG_PERF_PRINT()
|
|
||||||
#define LOG_PERF_CLEAR()
|
|
||||||
#define LOG_PERF_PUSH()
|
|
||||||
|
|
||||||
#endif // COLLECT_PERFDATA
|
|
||||||
|
|
||||||
// Global instance created
|
|
||||||
extern SerialDebug mySerial;
|
|
||||||
extern BatteryVoltage myBatteryVoltage;
|
|
||||||
|
|
||||||
#endif // _HELPER_H
|
|
||||||
|
|
||||||
// EOF
|
|
153
src/helper.hpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
|
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 above 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.
|
||||||
|
*/
|
||||||
|
#ifndef SRC_HELPER_HPP_
|
||||||
|
#define SRC_HELPER_HPP_
|
||||||
|
|
||||||
|
// Includes
|
||||||
|
#include <ArduinoLog.h>
|
||||||
|
|
||||||
|
// Sleep mode
|
||||||
|
void deepSleep(int t);
|
||||||
|
|
||||||
|
// Show build options
|
||||||
|
void printBuildOptions();
|
||||||
|
|
||||||
|
// Float to String
|
||||||
|
char* convertFloatToString(float f, char* buf, int dec = 2);
|
||||||
|
float reduceFloatPrecision(float f, int dec = 2);
|
||||||
|
|
||||||
|
// Logging via serial
|
||||||
|
void printTimestamp(Print* _logOutput, int _logLevel);
|
||||||
|
void printNewline(Print* _logOutput);
|
||||||
|
void printHeap();
|
||||||
|
|
||||||
|
// Classes
|
||||||
|
class SerialDebug {
|
||||||
|
public:
|
||||||
|
explicit SerialDebug(const uint32_t serialSpeed = 115200L);
|
||||||
|
static Logging* getLog() { return &Log; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class BatteryVoltage {
|
||||||
|
private:
|
||||||
|
float batteryLevel;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void read();
|
||||||
|
float getVoltage() { return batteryLevel; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(COLLECT_PERFDATA)
|
||||||
|
|
||||||
|
class PerfLogging {
|
||||||
|
private:
|
||||||
|
struct PerfEntry {
|
||||||
|
uint32_t start; // millis()
|
||||||
|
uint32_t end; // millis()
|
||||||
|
uint32_t max; // max time in ms
|
||||||
|
const char* key; // measurement
|
||||||
|
|
||||||
|
PerfEntry* next; // Next in the linked list
|
||||||
|
|
||||||
|
float mA; // Power consumption
|
||||||
|
float V; // Power consumption
|
||||||
|
};
|
||||||
|
|
||||||
|
PerfEntry* first = 0;
|
||||||
|
bool measurePower = false;
|
||||||
|
|
||||||
|
PerfEntry* find(const char* k) {
|
||||||
|
if (first == 0) return 0;
|
||||||
|
|
||||||
|
PerfEntry* pe = first;
|
||||||
|
|
||||||
|
while (strcmp(k, pe->key) != 0) {
|
||||||
|
if (pe->next == 0) return 0;
|
||||||
|
pe = pe->next;
|
||||||
|
}
|
||||||
|
return pe;
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfEntry* add(const char* k) {
|
||||||
|
if (first == 0) {
|
||||||
|
first = new PerfEntry();
|
||||||
|
first->key = k;
|
||||||
|
first->next = 0;
|
||||||
|
first->max = 0;
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfEntry* pe = first;
|
||||||
|
|
||||||
|
while (strcmp(k, pe->key) != 0) {
|
||||||
|
if (pe->next == 0) {
|
||||||
|
pe->next = new PerfEntry();
|
||||||
|
pe->next->key = k;
|
||||||
|
pe->next->max = 0;
|
||||||
|
pe->next->next = 0;
|
||||||
|
return pe->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
pe = pe->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pe;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void clear();
|
||||||
|
void start(const char* key);
|
||||||
|
void stop(const char* key);
|
||||||
|
void print();
|
||||||
|
void pushInflux();
|
||||||
|
};
|
||||||
|
|
||||||
|
extern PerfLogging myPerfLogging;
|
||||||
|
|
||||||
|
// Use these to collect performance data from various parts of the code
|
||||||
|
#define LOG_PERF_START(s) myPerfLogging.start(s)
|
||||||
|
#define LOG_PERF_STOP(s) myPerfLogging.stop(s)
|
||||||
|
// #define LOG_PERF_PRINT() myPerfLogging.print()
|
||||||
|
#define LOG_PERF_PRINT()
|
||||||
|
#define LOG_PERF_CLEAR() myPerfLogging.clear()
|
||||||
|
#define LOG_PERF_PUSH() myPerfLogging.pushInflux()
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// These will disable the performance collection function
|
||||||
|
#define LOG_PERF_START(s)
|
||||||
|
#define LOG_PERF_STOP(s)
|
||||||
|
#define LOG_PERF_PRINT()
|
||||||
|
#define LOG_PERF_CLEAR()
|
||||||
|
#define LOG_PERF_PUSH()
|
||||||
|
|
||||||
|
#endif // COLLECT_PERFDATA
|
||||||
|
|
||||||
|
// Global instance created
|
||||||
|
extern SerialDebug mySerial;
|
||||||
|
extern BatteryVoltage myBatteryVoltage;
|
||||||
|
|
||||||
|
#endif // SRC_HELPER_HPP_
|
||||||
|
|
||||||
|
// EOF
|
431
src/main.cpp
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 Magnus
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -21,259 +21,286 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "helper.h"
|
|
||||||
#include "gyro.h"
|
|
||||||
#include "config.h"
|
|
||||||
#include "wifi.h"
|
|
||||||
#include "webserver.h"
|
|
||||||
#include "calc.h"
|
|
||||||
#include "tempsensor.h"
|
|
||||||
#include "pushtarget.h"
|
|
||||||
#include <LittleFS.h>
|
#include <LittleFS.h>
|
||||||
|
|
||||||
|
#include <calc.hpp>
|
||||||
|
#include <config.hpp>
|
||||||
|
#include <gyro.hpp>
|
||||||
|
#include <helper.hpp>
|
||||||
|
#include <pushtarget.hpp>
|
||||||
|
#include <tempsensor.hpp>
|
||||||
|
#include <webserver.hpp>
|
||||||
|
#include <wifi.hpp>
|
||||||
|
|
||||||
// Settings for double reset detector.
|
// Settings for double reset detector.
|
||||||
#define ESP8266_DRD_USE_RTC true
|
#define ESP8266_DRD_USE_RTC true
|
||||||
#define DRD_TIMEOUT 2
|
#define DRD_TIMEOUT 2
|
||||||
#define DRD_ADDRESS 0
|
#define DRD_ADDRESS 0
|
||||||
#include <ESP_DoubleResetDetector.h>
|
#include <ESP_DoubleResetDetector.h>
|
||||||
DoubleResetDetector *drd;
|
DoubleResetDetector *drd;
|
||||||
|
|
||||||
// Define constats for this program
|
// Define constats for this program
|
||||||
#if LOG_LEVEL==6
|
#ifdef DEACTIVATE_SLEEPMODE
|
||||||
const int interval = 1000; // ms, time to wait between changes to output
|
const int interval = 1000; // ms, time to wait between changes to output
|
||||||
bool sleepModeAlwaysSkip = true; // Web interface can override normal behaviour
|
bool sleepModeAlwaysSkip = true; // Web interface can override normal behaviour
|
||||||
#else
|
#else
|
||||||
const int interval = 200; // ms, time to wait between changes to output
|
const int interval = 200; // ms, time to wait between changes to output
|
||||||
bool sleepModeAlwaysSkip = false; // Web interface can override normal behaviour
|
bool sleepModeAlwaysSkip =
|
||||||
|
false; // Web interface can override normal behaviour
|
||||||
#endif
|
#endif
|
||||||
unsigned long loopMillis = 0; // Used for main loop to run the code every _interval_
|
uint32_t loopMillis = 0; // Used for main loop to run the code every _interval_
|
||||||
unsigned long runtimeMillis; // Used to calculate the total time since start/wakeup
|
uint32_t runtimeMillis; // Used to calculate the total time since start/wakeup
|
||||||
unsigned long stableGyroMillis; // Used to calculate the total time since last stable gyro reading
|
uint32_t stableGyroMillis; // Used to calculate the total time since last
|
||||||
bool sleepModeActive = false;
|
// stable gyro reading
|
||||||
bool goToSleep = false;
|
bool sleepModeActive = false;
|
||||||
int loopCounter = 0;
|
bool goToSleep = false;
|
||||||
|
int loopCounter = 0;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Check if we should be in sleep mode
|
// Check if we should be in sleep mode
|
||||||
//
|
//
|
||||||
void checkSleepMode( float angle, float volt ) {
|
void checkSleepMode(float angle, float volt) {
|
||||||
|
#if defined(SKIP_SLEEPMODE)
|
||||||
|
sleepModeActive = false;
|
||||||
|
Log.verbose(F("MAIN: Skipping sleep mode (SKIP_SLEEPMODE is defined)." CR));
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined( SKIP_SLEEPMODE )
|
const RawGyroData &g = myConfig.getGyroCalibration();
|
||||||
|
|
||||||
|
// Will not enter sleep mode if: no calibration data
|
||||||
|
if (g.ax == 0 && g.ay == 0 && g.az == 0 && g.gx == 0 && g.gy == 0 &&
|
||||||
|
g.gz == 0) {
|
||||||
|
Log.notice(
|
||||||
|
F("MAIN: Missing calibration data, so forcing webserver to be "
|
||||||
|
"active." CR));
|
||||||
|
sleepModeAlwaysSkip = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sleepModeAlwaysSkip) {
|
||||||
|
Log.notice(F("MAIN: Sleep mode disabled from web interface." CR));
|
||||||
sleepModeActive = false;
|
sleepModeActive = false;
|
||||||
Log.verbose(F("MAIN: Skipping sleep mode (SKIP_SLEEPMODE is defined)." CR) );
|
|
||||||
return;
|
return;
|
||||||
#endif
|
}
|
||||||
|
|
||||||
const RawGyroData &g = myConfig.getGyroCalibration();
|
// Will not enter sleep mode if: charger is connected
|
||||||
|
sleepModeActive = (volt < 4.15 && (angle > 85 && angle < 95)) || (volt > 4.15)
|
||||||
|
? false
|
||||||
|
: true;
|
||||||
|
|
||||||
// Will not enter sleep mode if: no calibration data
|
// sleep mode active when flat
|
||||||
if( g.ax==0 && g.ay==0 && g.az==0 && g.gx==0 && g.gy==0 && g.gz==0 ) {
|
Log.notice(F("MAIN: Deep sleep mode %s (angle=%F volt=%F)." CR),
|
||||||
Log.notice(F("MAIN: Missing calibration data, so forcing webserver to be active." CR) );
|
sleepModeActive ? "true" : "false", angle, volt);
|
||||||
sleepModeAlwaysSkip = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( sleepModeAlwaysSkip ) {
|
|
||||||
Log.notice(F("MAIN: Sleep mode disabled from web interface." CR) );
|
|
||||||
sleepModeActive = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Will not enter sleep mode if: charger is connected
|
|
||||||
sleepModeActive = (volt<4.15 && (angle>85 && angle<95)) || (volt>4.15) ? false : true;
|
|
||||||
|
|
||||||
// sleep mode active when flat
|
|
||||||
//sleepModeActive = ( angle<85 && angle>5 ) ? true : false;
|
|
||||||
#if LOG_LEVEL==6
|
|
||||||
Log.verbose(F("MAIN: Deep sleep mode %s (angle=%F volt=%F)." CR), sleepModeActive ? "true":"false", angle, volt );
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Setup
|
// 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");
|
||||||
runtimeMillis = millis();
|
runtimeMillis = millis();
|
||||||
|
|
||||||
drd = new DoubleResetDetector(DRD_TIMEOUT, DRD_ADDRESS);
|
drd = new DoubleResetDetector(DRD_TIMEOUT, DRD_ADDRESS);
|
||||||
bool dt = drd->detectDoubleReset();
|
bool dt = drd->detectDoubleReset();
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
||||||
Log.verbose(F("Main: Reset reason %s." CR), ESP.getResetInfo().c_str() );
|
delay(3000); // Wait a few seconds when using debug version so that serial is
|
||||||
|
// started.
|
||||||
|
Log.verbose(F("Main: Reset reason %s." CR), ESP.getResetInfo().c_str());
|
||||||
#endif
|
#endif
|
||||||
// Main startup
|
// Main startup
|
||||||
Log.notice(F("Main: Started setup for %s." CR), String( ESP.getChipId(), HEX).c_str() );
|
Log.notice(F("Main: Started setup for %s." CR),
|
||||||
printBuildOptions();
|
String(ESP.getChipId(), HEX).c_str());
|
||||||
|
printBuildOptions();
|
||||||
|
|
||||||
LOG_PERF_START("main-config-load");
|
LOG_PERF_START("main-config-load");
|
||||||
myConfig.checkFileSystem();
|
myConfig.checkFileSystem();
|
||||||
myConfig.loadFile();
|
myConfig.loadFile();
|
||||||
LOG_PERF_STOP("main-config-load");
|
LOG_PERF_STOP("main-config-load");
|
||||||
|
|
||||||
// Setup watchdog
|
// Setup watchdog
|
||||||
ESP.wdtDisable();
|
ESP.wdtDisable();
|
||||||
ESP.wdtEnable( interval*2 );
|
ESP.wdtEnable(interval * 2);
|
||||||
|
|
||||||
LOG_PERF_START("main-temp-setup");
|
if (dt) {
|
||||||
myTempSensor.setup();
|
Log.notice(F("Main: Detected doubletap on reset. Reset reason=%s" CR),
|
||||||
LOG_PERF_STOP("main-temp-setup");
|
ESP.getResetReason().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
// Setup Gyro
|
#ifdef SKIP_SLEEPMODE
|
||||||
//LOG_PERF_START("main-gyro-setup"); // Takes less than 5ms, so skip this measurment
|
// If we are running in debug more we skip this part. makes is hard to debug
|
||||||
if( !myGyro.setup() )
|
// in case of crash/watchdog reset
|
||||||
Log.error(F("Main: Failed to initialize the gyro." CR));
|
dt = false;
|
||||||
//LOG_PERF_STOP("main-gyro-setup");
|
|
||||||
|
|
||||||
if( dt )
|
|
||||||
Log.notice(F("Main: Detected doubletap on reset." CR));
|
|
||||||
|
|
||||||
LOG_PERF_START("main-wifi-connect");
|
|
||||||
myWifi.connect( dt ); // This will return false if unable to connect to wifi, will be handled in loop()
|
|
||||||
LOG_PERF_STOP("main-wifi-connect");
|
|
||||||
|
|
||||||
LOG_PERF_START("main-gyro-read");
|
|
||||||
myGyro.read();
|
|
||||||
LOG_PERF_STOP("main-gyro-read");
|
|
||||||
|
|
||||||
LOG_PERF_START("main-batt-read");
|
|
||||||
myBatteryVoltage.read();
|
|
||||||
LOG_PERF_STOP("main-batt-read");
|
|
||||||
checkSleepMode( myGyro.getAngle(), myBatteryVoltage.getVoltage() );
|
|
||||||
|
|
||||||
if( myWifi.isConnected() ) {
|
|
||||||
#if defined( ACTIVATE_OTA )
|
|
||||||
LOG_PERF_START("main-wifi-ota");
|
|
||||||
if( !sleepModeActive && myWifi.checkFirmwareVersion() ) {
|
|
||||||
myWifi.updateFirmware();
|
|
||||||
}
|
|
||||||
LOG_PERF_STOP("main-wifi-ota");
|
|
||||||
#endif
|
#endif
|
||||||
if( !sleepModeActive ) {
|
|
||||||
//LOG_PERF_START("main-webserver-setup"); // Takes less than 4ms , so skip this measurment
|
LOG_PERF_START("main-wifi-connect");
|
||||||
myWebServer.setupWebServer();
|
myWifi.connect(dt); // This will return false if unable to connect to wifi,
|
||||||
//LOG_PERF_STOP("main-webserver-setup");
|
// will be handled in loop()
|
||||||
}
|
LOG_PERF_STOP("main-wifi-connect");
|
||||||
|
|
||||||
|
LOG_PERF_START("main-temp-setup");
|
||||||
|
myTempSensor.setup();
|
||||||
|
LOG_PERF_STOP("main-temp-setup");
|
||||||
|
|
||||||
|
if (!myGyro.setup()) // Takes less than 5ms, so skip this
|
||||||
|
Log.error(F("Main: Failed to initialize the gyro." CR));
|
||||||
|
|
||||||
|
LOG_PERF_START("main-gyro-read");
|
||||||
|
myGyro.read();
|
||||||
|
LOG_PERF_STOP("main-gyro-read");
|
||||||
|
|
||||||
|
myBatteryVoltage
|
||||||
|
.read(); // Takes less than 1ms, so skip this measuring time on this
|
||||||
|
checkSleepMode(myGyro.getAngle(), myBatteryVoltage.getVoltage());
|
||||||
|
|
||||||
|
if (myWifi.isConnected()) {
|
||||||
|
#if defined(ACTIVATE_OTA)
|
||||||
|
LOG_PERF_START("main-wifi-ota");
|
||||||
|
if (!sleepModeActive && myWifi.checkFirmwareVersion()) {
|
||||||
|
myWifi.updateFirmware();
|
||||||
}
|
}
|
||||||
|
LOG_PERF_STOP("main-wifi-ota");
|
||||||
|
#endif
|
||||||
|
if (!sleepModeActive) {
|
||||||
|
myWebServer
|
||||||
|
.setupWebServer(); // Takes less than 4ms, so skip this measurement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LOG_PERF_STOP("main-setup");
|
LOG_PERF_STOP("main-setup");
|
||||||
Log.notice(F("Main: Setup completed." CR));
|
Log.notice(F("Main: Setup completed." CR));
|
||||||
stableGyroMillis = millis(); // Put it here so we dont include time for wifi connection
|
stableGyroMillis =
|
||||||
|
millis(); // Put it here so we dont include time for wifi connection
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Main loops
|
// Main loops
|
||||||
//
|
//
|
||||||
void loop() {
|
void loop() {
|
||||||
drd->loop();
|
drd->loop();
|
||||||
|
|
||||||
if( sleepModeActive || abs( (long) (millis() - loopMillis)) > interval ) {
|
if (sleepModeActive || abs((int32_t)(millis() - loopMillis)) > interval) {
|
||||||
float angle = 0;
|
float angle = 0;
|
||||||
float volt = myBatteryVoltage.getVoltage();
|
float volt = myBatteryVoltage.getVoltage();
|
||||||
//float sensorTemp = 0;
|
loopCounter++;
|
||||||
loopCounter++;
|
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
||||||
Log.verbose(F("Main: Entering main loop." CR) );
|
Log.verbose(F("Main: Entering main loop." CR));
|
||||||
#endif
|
#endif
|
||||||
// Process the sensor values and push data to targets.
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
// If we dont get any readings we just skip this and try again the next interval.
|
|
||||||
//
|
|
||||||
if( myGyro.hasValue() ) {
|
|
||||||
angle = myGyro.getAngle(); // Gyro angle
|
|
||||||
|
|
||||||
stableGyroMillis = millis(); // Reset timer
|
// Process the sensor values and push data to targets.
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// If we dont get any readings we just skip this and try again the next
|
||||||
|
// interval.
|
||||||
|
//
|
||||||
|
if (myGyro.hasValue()) {
|
||||||
|
angle = myGyro.getAngle(); // Gyro angle
|
||||||
|
|
||||||
LOG_PERF_START("loop-temp-read");
|
stableGyroMillis = millis(); // Reset timer
|
||||||
float temp = myTempSensor.getTempC();
|
|
||||||
LOG_PERF_STOP("loop-temp-read");
|
|
||||||
|
|
||||||
//LOG_PERF_START("loop-gravity-calc"); // Takes less than 2ms , so skip this measurment
|
LOG_PERF_START("loop-temp-read");
|
||||||
float gravity = calculateGravity( angle, temp );
|
float temp = myTempSensor.getTempC();
|
||||||
//LOG_PERF_STOP("loop-gravity-calc");
|
LOG_PERF_STOP("loop-temp-read");
|
||||||
|
|
||||||
//LOG_PERF_START("loop-gravity-corr"); // Takes less than 2ms , so skip this measurment
|
float gravity = calculateGravity(
|
||||||
// Use default correction temperature of 20C
|
angle, temp); // Takes less than 2ms , so skip this measurment
|
||||||
float corrGravity = gravityTemperatureCorrection( gravity, temp, myConfig.getTempFormat() );
|
float corrGravity = gravityTemperatureCorrection(
|
||||||
//LOG_PERF_STOP("loop-gravity-corr");
|
gravity, temp, myConfig.getTempFormat()); // Takes less than 2ms , so
|
||||||
|
// skip this measurment
|
||||||
|
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
||||||
Log.verbose(F("Main: Sensor values gyro angle=%F, temp=%F, gravity=%F, corr=%F." CR), angle, temp, gravity, corrGravity );
|
Log.verbose(F("Main: Sensor values gyro angle=%F, temp=%F, gravity=%F, "
|
||||||
|
"corr=%F." CR),
|
||||||
|
angle, temp, gravity, corrGravity);
|
||||||
#endif
|
#endif
|
||||||
// Limit the printout when sleep mode is not active.
|
|
||||||
if( loopCounter%10 == 0 || sleepModeActive ) {
|
|
||||||
Log.notice(F("Main: angle=%F, temp=%F, gravity=%F, corrGravity=%F, batt=%F." CR), angle, temp, gravity, corrGravity ,volt );
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_PERF_START("loop-push");
|
// Limit the printout when sleep mode is not active.
|
||||||
myPushTarget.send( angle, gravity, corrGravity, temp, (millis()-runtimeMillis)/1000, sleepModeActive ); // Force the transmission if we are going to sleep
|
if (loopCounter % 10 == 0 || sleepModeActive) {
|
||||||
LOG_PERF_STOP("loop-push");
|
Log.notice(F("Main: angle=%F, temp=%F, gravity=%F, corrGravity=%F, "
|
||||||
|
"batt=%F." CR),
|
||||||
|
angle, temp, gravity, corrGravity, volt);
|
||||||
|
}
|
||||||
|
|
||||||
// If we have completed the update lets go to sleep
|
LOG_PERF_START("loop-push");
|
||||||
if( sleepModeActive )
|
myPushTarget.send(
|
||||||
goToSleep = true;
|
angle, gravity, corrGravity, temp, (millis() - runtimeMillis) / 1000,
|
||||||
} else {
|
sleepModeActive); // Force the transmission if we are going to sleep
|
||||||
Log.error(F("Main: No gyro value." CR) );
|
LOG_PERF_STOP("loop-push");
|
||||||
}
|
|
||||||
|
|
||||||
#if LOG_LEVEL==6
|
// If we have completed the update lets go to sleep
|
||||||
Log.verbose(F("Main: Sleep mode not active." CR) );
|
if (sleepModeActive) goToSleep = true;
|
||||||
#endif
|
} else {
|
||||||
int sleepInterval = myConfig.getSleepInterval();
|
Log.error(F("Main: No gyro value." CR));
|
||||||
|
|
||||||
// If we didnt get a wifi connection, we enter sleep for a short time to conserve battery.
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
if( !myWifi.isConnected() ) { // no connection to wifi
|
|
||||||
Log.notice(F("MAIN: No connection to wifi established, sleeping for 60s." CR) );
|
|
||||||
sleepInterval = 60; // 60s
|
|
||||||
goToSleep = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the sensor is moving and we are not getting a clear reading, we enter sleep for a short time to conserve battery.
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
if( sleepModeActive && ((millis()-stableGyroMillis)>10000L) ) { // 10s since last stable gyro reading
|
|
||||||
Log.notice(F("MAIN: Unable to get a stable reading for 10s, sleeping for 60s." CR) );
|
|
||||||
sleepInterval = 60; // 60s
|
|
||||||
goToSleep = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enter sleep mode if the conditions are right
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
if( goToSleep ) {
|
|
||||||
Log.notice(F("MAIN: Entering deep sleep for %d s, run time %l s, battery=%F V." CR), sleepInterval, (millis()-runtimeMillis)/1000, volt );
|
|
||||||
LittleFS.end();
|
|
||||||
myGyro.enterSleep();
|
|
||||||
drd->stop();
|
|
||||||
LOG_PERF_STOP("run-time");
|
|
||||||
LOG_PERF_PUSH();
|
|
||||||
delay(100);
|
|
||||||
deepSleep( sleepInterval );
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are running in normal mode we just continue
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
// Do these checks if we are running in normal mode (not sleep mode)
|
|
||||||
//
|
|
||||||
checkSleepMode( angle, volt );
|
|
||||||
|
|
||||||
LOG_PERF_START("loop-gyro-read");
|
|
||||||
myGyro.read();
|
|
||||||
LOG_PERF_STOP("loop-gyro-read");
|
|
||||||
|
|
||||||
//LOG_PERF_START("loop-batt-read"); // Takes less than 2ms , so skip this measurment
|
|
||||||
myBatteryVoltage.read();
|
|
||||||
//LOG_PERF_STOP("loop-batt-read");
|
|
||||||
|
|
||||||
loopMillis = millis();
|
|
||||||
#if LOG_LEVEL==6
|
|
||||||
Log.verbose(F("Main: Heap %d kb FreeSketch %d kb." CR), ESP.getFreeHeap()/1024, ESP.getFreeSketchSpace()/1024 );
|
|
||||||
Log.verbose(F("Main: HeapFrag %d %%." CR), ESP.getHeapFragmentation() );
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
myWebServer.loop();
|
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
||||||
|
Log.verbose(F("Main: Sleep mode not active." CR));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int sleepInterval = myConfig.getSleepInterval();
|
||||||
|
|
||||||
|
// If we didnt get a wifi connection, we enter sleep for a short time to
|
||||||
|
// conserve battery.
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
if (!myWifi.isConnected()) { // no connection to wifi
|
||||||
|
Log.notice(
|
||||||
|
F("MAIN: No connection to wifi established, sleeping for 60s." CR));
|
||||||
|
sleepInterval = 60; // 60s
|
||||||
|
goToSleep = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the sensor is moving and we are not getting a clear reading, we enter
|
||||||
|
// sleep for a short time to conserve battery.
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
if (sleepModeActive && ((millis() - stableGyroMillis) >
|
||||||
|
10000L)) { // 10s since last stable gyro reading
|
||||||
|
Log.notice(
|
||||||
|
F("MAIN: Unable to get a stable reading for 10s, sleeping for "
|
||||||
|
"60s." CR));
|
||||||
|
sleepInterval = 60; // 60s
|
||||||
|
goToSleep = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enter sleep mode if the conditions are right
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
if (goToSleep && !sleepModeAlwaysSkip) {
|
||||||
|
Log.notice(F("MAIN: Entering deep sleep for %d s, run time %l s, "
|
||||||
|
"battery=%F V." CR),
|
||||||
|
sleepInterval, (millis() - runtimeMillis) / 1000, volt);
|
||||||
|
LittleFS.end();
|
||||||
|
myGyro.enterSleep();
|
||||||
|
drd->stop();
|
||||||
|
LOG_PERF_STOP("run-time");
|
||||||
|
LOG_PERF_PUSH();
|
||||||
|
delay(100);
|
||||||
|
deepSleep(sleepInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are running in normal mode we just continue
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Do these checks if we are running in normal mode (not sleep mode)
|
||||||
|
//
|
||||||
|
checkSleepMode(angle, volt);
|
||||||
|
|
||||||
|
LOG_PERF_START("loop-gyro-read");
|
||||||
|
myGyro.read();
|
||||||
|
LOG_PERF_STOP("loop-gyro-read");
|
||||||
|
|
||||||
|
myBatteryVoltage.read(); // Takes less than 2ms , so skip this measurment
|
||||||
|
|
||||||
|
loopMillis = millis();
|
||||||
|
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
||||||
|
Log.verbose(F("Main: Heap %d kb FreeSketch %d kb HeapFrag %d %%." CR),
|
||||||
|
ESP.getFreeHeap() / 1024, ESP.getFreeSketchSpace() / 1024,
|
||||||
|
ESP.getHeapFragmentation());
|
||||||
|
#endif
|
||||||
|
LOG_PERF_PUSH();
|
||||||
|
}
|
||||||
|
|
||||||
|
myWebServer.loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// EOF
|
// EOF
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 Magnus
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -21,206 +21,237 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "pushtarget.h"
|
#include <config.hpp>
|
||||||
#include "config.h"
|
#include <gyro.hpp>
|
||||||
#include "gyro.h" // For testing the tempsensor in the gyro
|
#include <pushtarget.hpp>
|
||||||
|
|
||||||
PushTarget myPushTarget;
|
PushTarget myPushTarget;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Send the pressure value
|
// Send the pressure value
|
||||||
//
|
//
|
||||||
void PushTarget::send(float angle, float gravity, float corrGravity, float temp, float runTime, bool force ) {
|
void PushTarget::send(float angle, float gravity, float corrGravity, float temp,
|
||||||
unsigned long timePassed = abs( (long) (millis() - ms) );
|
float runTime, bool force) {
|
||||||
unsigned long interval = myConfig.getSleepInterval()*1000;
|
uint32_t timePassed = abs((int32_t)(millis() - ms));
|
||||||
|
uint32_t interval = myConfig.getSleepInterval() * 1000;
|
||||||
|
|
||||||
if( ( timePassed < interval ) && !force) {
|
if ((timePassed < interval) && !force) {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||||
Log.verbose(F("PUSH: Timer has not expired %l vs %l." CR), timePassed, interval );
|
Log.verbose(F("PUSH: Timer has not expired %l vs %l." CR), timePassed,
|
||||||
|
interval);
|
||||||
#endif
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||||
Log.verbose(F("PUSH: Sending data." CR) );
|
Log.verbose(F("PUSH: Sending data." CR));
|
||||||
#endif
|
#endif
|
||||||
ms = millis();
|
|
||||||
|
|
||||||
if( myConfig.isBrewfatherActive() ) {
|
ms = millis();
|
||||||
LOG_PERF_START("push-brewfather");
|
|
||||||
sendBrewfather( angle, gravity, corrGravity, temp );
|
|
||||||
LOG_PERF_STOP("push-brewfather");
|
|
||||||
}
|
|
||||||
|
|
||||||
if( myConfig.isHttpActive() ) {
|
if (myConfig.isBrewfatherActive()) {
|
||||||
LOG_PERF_START("push-http");
|
LOG_PERF_START("push-brewfather");
|
||||||
sendHttp( myConfig.getHttpPushUrl(), angle, gravity, corrGravity, temp, runTime );
|
sendBrewfather(angle, gravity, corrGravity, temp);
|
||||||
LOG_PERF_STOP("push-http");
|
LOG_PERF_STOP("push-brewfather");
|
||||||
}
|
}
|
||||||
|
|
||||||
if( myConfig.isHttpActive2() ) {
|
if (myConfig.isHttpActive()) {
|
||||||
LOG_PERF_START("push-http2");
|
LOG_PERF_START("push-http");
|
||||||
sendHttp( myConfig.getHttpPushUrl2(), angle, gravity, corrGravity, temp, runTime );
|
sendHttp(myConfig.getHttpPushUrl(), angle, gravity, corrGravity, temp,
|
||||||
LOG_PERF_STOP("push-http2");
|
runTime);
|
||||||
}
|
LOG_PERF_STOP("push-http");
|
||||||
|
}
|
||||||
|
|
||||||
if( myConfig.isInfluxDb2Active() ) {
|
if (myConfig.isHttpActive2()) {
|
||||||
LOG_PERF_START("push-influxdb2");
|
LOG_PERF_START("push-http2");
|
||||||
sendInfluxDb2( angle, gravity, corrGravity, temp, runTime );
|
sendHttp(myConfig.getHttpPushUrl2(), angle, gravity, corrGravity, temp,
|
||||||
LOG_PERF_STOP("push-influxdb2");
|
runTime);
|
||||||
}
|
LOG_PERF_STOP("push-http2");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myConfig.isInfluxDb2Active()) {
|
||||||
|
LOG_PERF_START("push-influxdb2");
|
||||||
|
sendInfluxDb2(angle, gravity, corrGravity, temp, runTime);
|
||||||
|
LOG_PERF_STOP("push-influxdb2");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Send to influx db v2
|
// Send to influx db v2
|
||||||
//
|
//
|
||||||
void PushTarget::sendInfluxDb2(float angle, float gravity, float corrGravity, float temp, float runTime) {
|
void PushTarget::sendInfluxDb2(float angle, float gravity, float corrGravity,
|
||||||
Log.notice(F("PUSH: Sending values to influxdb2 angle=%F, gravity=%F, temp=%F." CR), angle, gravity, temp );
|
float temp, float runTime) {
|
||||||
|
#if !defined(PUSH_DISABLE_LOGGING)
|
||||||
WiFiClient client;
|
Log.notice(
|
||||||
HTTPClient http;
|
F("PUSH: Sending values to influxdb2 angle=%F, gravity=%F, temp=%F." CR),
|
||||||
String serverPath = String(myConfig.getInfluxDb2PushUrl()) + "/api/v2/write?org=" +
|
angle, gravity, temp);
|
||||||
String(myConfig.getInfluxDb2PushOrg()) + "&bucket=" +
|
|
||||||
String(myConfig.getInfluxDb2PushBucket());
|
|
||||||
|
|
||||||
http.begin( client, serverPath);
|
|
||||||
|
|
||||||
// Create body for influxdb2
|
|
||||||
char buf[1024];
|
|
||||||
sprintf( &buf[0], "measurement,host=%s,device=%s,temp-format=%c,gravity-format=%s "
|
|
||||||
"gravity=%.4f,corr-gravity=%.4f,angle=%.2f,temp=%.2f,battery=%.2f,rssi=%d,temp2=%.2f\n",
|
|
||||||
// TODO: Add support for plato format
|
|
||||||
myConfig.getMDNS(), myConfig.getID(), myConfig.getTempFormat(), "SG",
|
|
||||||
myConfig.isGravityTempAdj() ? corrGravity : gravity,
|
|
||||||
corrGravity, angle, temp, myBatteryVoltage.getVoltage(), WiFi.RSSI(), myGyro.getSensorTempC() ); // For comparing gyro tempsensor vs DSB1820
|
|
||||||
|
|
||||||
#if LOG_LEVEL==6
|
|
||||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
|
||||||
Log.verbose(F("PUSH: data %s." CR), &buf[0] );
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Send HTTP POST request
|
WiFiClient client;
|
||||||
String auth = "Token " + String( myConfig.getInfluxDb2PushToken() );
|
HTTPClient http;
|
||||||
http.addHeader(F("Authorization"), auth.c_str() );
|
String serverPath =
|
||||||
int httpResponseCode = http.POST(&buf[0]);
|
String(myConfig.getInfluxDb2PushUrl()) +
|
||||||
|
"/api/v2/write?org=" + String(myConfig.getInfluxDb2PushOrg()) +
|
||||||
|
"&bucket=" + String(myConfig.getInfluxDb2PushBucket());
|
||||||
|
|
||||||
if (httpResponseCode==204) {
|
http.begin(client, serverPath);
|
||||||
Log.notice(F("PUSH: InfluxDB2 push successful, response=%d" CR), httpResponseCode);
|
|
||||||
} else {
|
|
||||||
Log.error(F("PUSH: InfluxDB2 push failed, response=%d" CR), httpResponseCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
http.end();
|
// Create body for influxdb2
|
||||||
|
char buf[1024];
|
||||||
|
snprintf(
|
||||||
|
&buf[0], sizeof(buf),
|
||||||
|
"measurement,host=%s,device=%s,temp-format=%c,gravity-format=%s "
|
||||||
|
"gravity=%.4f,corr-gravity=%.4f,angle=%.2f,temp=%.2f,battery=%.2f,rssi=%"
|
||||||
|
"d,temp2=%.2f\n",
|
||||||
|
// TODO: Add support for plato format
|
||||||
|
myConfig.getMDNS(), myConfig.getID(), myConfig.getTempFormat(), "SG",
|
||||||
|
myConfig.isGravityTempAdj() ? corrGravity : gravity, corrGravity, angle,
|
||||||
|
temp, myBatteryVoltage.getVoltage(), WiFi.RSSI(),
|
||||||
|
myGyro.getSensorTempC()); // For comparing gyro tempsensor vs DSB1820
|
||||||
|
|
||||||
|
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||||
|
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||||
|
Log.verbose(F("PUSH: data %s." CR), &buf[0]);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Send HTTP POST request
|
||||||
|
String auth = "Token " + String(myConfig.getInfluxDb2PushToken());
|
||||||
|
http.addHeader(F("Authorization"), auth.c_str());
|
||||||
|
int httpResponseCode = http.POST(&buf[0]);
|
||||||
|
|
||||||
|
if (httpResponseCode == 204) {
|
||||||
|
Log.notice(F("PUSH: InfluxDB2 push successful, response=%d" CR),
|
||||||
|
httpResponseCode);
|
||||||
|
} else {
|
||||||
|
Log.error(F("PUSH: InfluxDB2 push failed, response=%d" CR),
|
||||||
|
httpResponseCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
http.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Send data to brewfather
|
// Send data to brewfather
|
||||||
//
|
//
|
||||||
void PushTarget::sendBrewfather(float angle, float gravity, float corrGravity, float temp ) {
|
void PushTarget::sendBrewfather(float angle, float gravity, float corrGravity,
|
||||||
Log.notice(F("PUSH: Sending values to brewfather angle=%F, gravity=%F, temp=%F." CR), angle, gravity, temp );
|
float temp) {
|
||||||
|
#if !defined(PUSH_DISABLE_LOGGING)
|
||||||
DynamicJsonDocument doc(300);
|
Log.notice(
|
||||||
//
|
F("PUSH: Sending values to brewfather angle=%F, gravity=%F, temp=%F." CR),
|
||||||
// {
|
angle, gravity, temp);
|
||||||
// "name": "YourDeviceName", // Required field, this will be the ID in Brewfather
|
|
||||||
// "temp": 20.32,
|
|
||||||
// "aux_temp": 15.61, // Fridge Temp
|
|
||||||
// "ext_temp": 6.51, // Room Temp
|
|
||||||
// "temp_unit": "C", // C, F, K
|
|
||||||
// "gravity": 1.042,
|
|
||||||
// "gravity_unit": "G", // G, P
|
|
||||||
// "pressure": 10,
|
|
||||||
// "pressure_unit": "PSI", // PSI, BAR, KPA
|
|
||||||
// "ph": 4.12,
|
|
||||||
// "bpm": 123, // Bubbles Per Minute
|
|
||||||
// "comment": "Hello World",
|
|
||||||
// "beer": "Pale Ale"
|
|
||||||
// "battery": 4.98
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
doc["name"] = myConfig.getMDNS();
|
|
||||||
doc["temp"] = reduceFloatPrecision( temp, 1);
|
|
||||||
doc["temp_unit"] = String( myConfig.getTempFormat() );
|
|
||||||
doc["battery"] = reduceFloatPrecision( myBatteryVoltage.getVoltage(), 2 );
|
|
||||||
// TODO: Add support for plato format
|
|
||||||
doc["gravity"] = reduceFloatPrecision( myConfig.isGravityTempAdj() ? corrGravity : gravity, 4 );
|
|
||||||
doc["gravity_unit"] = myConfig.isGravitySG()?"G":"P";
|
|
||||||
|
|
||||||
WiFiClient client;
|
|
||||||
HTTPClient http;
|
|
||||||
String serverPath = myConfig.getBrewfatherPushUrl();
|
|
||||||
|
|
||||||
// Your Domain name with URL path or IP address with path
|
|
||||||
http.begin( client, serverPath);
|
|
||||||
String json;
|
|
||||||
serializeJson(doc, json);
|
|
||||||
#if LOG_LEVEL==6
|
|
||||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
|
||||||
Log.verbose(F("PUSH: json %s." CR), json.c_str());
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Send HTTP POST request
|
DynamicJsonDocument doc(300);
|
||||||
http.addHeader(F("Content-Type"), F("application/json") );
|
//
|
||||||
int httpResponseCode = http.POST(json);
|
// {
|
||||||
|
// "name": "YourDeviceName", // Required field, this will be the ID in
|
||||||
|
// Brewfather "temp": 20.32, "aux_temp": 15.61, // Fridge Temp
|
||||||
|
// "ext_temp": 6.51, // Room Temp
|
||||||
|
// "temp_unit": "C", // C, F, K
|
||||||
|
// "gravity": 1.042,
|
||||||
|
// "gravity_unit": "G", // G, P
|
||||||
|
// "pressure": 10,
|
||||||
|
// "pressure_unit": "PSI", // PSI, BAR, KPA
|
||||||
|
// "ph": 4.12,
|
||||||
|
// "bpm": 123, // Bubbles Per Minute
|
||||||
|
// "comment": "Hello World",
|
||||||
|
// "beer": "Pale Ale"
|
||||||
|
// "battery": 4.98
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
doc["name"] = myConfig.getMDNS();
|
||||||
|
doc["temp"] = reduceFloatPrecision(temp, 1);
|
||||||
|
doc["temp_unit"] = String(myConfig.getTempFormat());
|
||||||
|
doc["battery"] = reduceFloatPrecision(myBatteryVoltage.getVoltage(), 2);
|
||||||
|
// TODO: Add support for plato format
|
||||||
|
doc["gravity"] = reduceFloatPrecision(
|
||||||
|
myConfig.isGravityTempAdj() ? corrGravity : gravity, 4);
|
||||||
|
doc["gravity_unit"] = myConfig.isGravitySG() ? "G" : "P";
|
||||||
|
|
||||||
if (httpResponseCode==200) {
|
WiFiClient client;
|
||||||
Log.notice(F("PUSH: Brewfather push successful, response=%d" CR), httpResponseCode);
|
HTTPClient http;
|
||||||
} else {
|
String serverPath = myConfig.getBrewfatherPushUrl();
|
||||||
Log.error(F("PUSH: Brewfather push failed, response=%d" CR), httpResponseCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
http.end();
|
// Your Domain name with URL path or IP address with path
|
||||||
|
http.begin(client, serverPath);
|
||||||
|
String json;
|
||||||
|
serializeJson(doc, json);
|
||||||
|
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||||
|
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||||
|
Log.verbose(F("PUSH: json %s." CR), json.c_str());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Send HTTP POST request
|
||||||
|
http.addHeader(F("Content-Type"), F("application/json"));
|
||||||
|
int httpResponseCode = http.POST(json);
|
||||||
|
|
||||||
|
if (httpResponseCode == 200) {
|
||||||
|
Log.notice(F("PUSH: Brewfather push successful, response=%d" CR),
|
||||||
|
httpResponseCode);
|
||||||
|
} else {
|
||||||
|
Log.error(F("PUSH: Brewfather push failed, response=%d" CR),
|
||||||
|
httpResponseCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
http.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Send data to http target
|
// Send data to http target
|
||||||
//
|
//
|
||||||
void PushTarget::sendHttp( String serverPath, float angle, float gravity, float corrGravity, float temp, float runTime ) {
|
void PushTarget::sendHttp(String serverPath, float angle, float gravity,
|
||||||
Log.notice(F("PUSH: Sending values to http angle=%F, gravity=%F, temp=%F." CR), angle, gravity, temp );
|
float corrGravity, float temp, float runTime) {
|
||||||
|
#if !defined(PUSH_DISABLE_LOGGING)
|
||||||
DynamicJsonDocument doc(256);
|
Log.notice(
|
||||||
|
F("PUSH: Sending values to http angle=%F, gravity=%F, temp=%F." CR),
|
||||||
// Use iSpindle format for compatibility
|
angle, gravity, temp);
|
||||||
doc["name"] = myConfig.getMDNS();
|
|
||||||
doc["ID"] = myConfig.getID();
|
|
||||||
doc["token"] = "gravmon";
|
|
||||||
doc["interval"] = myConfig.getSleepInterval();
|
|
||||||
doc["temperature"] = reduceFloatPrecision( temp, 1 );
|
|
||||||
doc["temp-units"] = String( myConfig.getTempFormat() );
|
|
||||||
// TODO: Add support for plato format
|
|
||||||
doc["gravity"] = reduceFloatPrecision( myConfig.isGravityTempAdj() ? corrGravity : gravity, 4 );
|
|
||||||
doc["corr-gravity"] = reduceFloatPrecision( corrGravity, 4 );
|
|
||||||
doc["angle"] = reduceFloatPrecision( angle, 2);
|
|
||||||
doc["battery"] = reduceFloatPrecision( myBatteryVoltage.getVoltage(), 2 );
|
|
||||||
doc["rssi"] = WiFi.RSSI();
|
|
||||||
|
|
||||||
// Some additional information
|
|
||||||
doc["gravity-units"] = "SG";
|
|
||||||
doc["run-time"] = reduceFloatPrecision( runTime, 2 );
|
|
||||||
|
|
||||||
WiFiClient client;
|
|
||||||
HTTPClient http;
|
|
||||||
|
|
||||||
// Your Domain name with URL path or IP address with path
|
|
||||||
http.begin( client, serverPath);
|
|
||||||
String json;
|
|
||||||
serializeJson(doc, json);
|
|
||||||
#if LOG_LEVEL==6
|
|
||||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
|
||||||
Log.verbose(F("PUSH: json %s." CR), json.c_str());
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Send HTTP POST request
|
DynamicJsonDocument doc(256);
|
||||||
http.addHeader(F("Content-Type"), F("application/json") );
|
|
||||||
int httpResponseCode = http.POST(json);
|
|
||||||
|
|
||||||
if (httpResponseCode==200) {
|
// Use iSpindle format for compatibility
|
||||||
Log.notice(F("PUSH: HTTP push successful, response=%d" CR), httpResponseCode);
|
doc["name"] = myConfig.getMDNS();
|
||||||
} else {
|
doc["ID"] = myConfig.getID();
|
||||||
Log.error(F("PUSH: HTTP push failed, response=%d" CR), httpResponseCode);
|
doc["token"] = "gravmon";
|
||||||
}
|
doc["interval"] = myConfig.getSleepInterval();
|
||||||
|
doc["temperature"] = reduceFloatPrecision(temp, 1);
|
||||||
|
doc["temp-units"] = String(myConfig.getTempFormat());
|
||||||
|
// TODO: Add support for plato format
|
||||||
|
doc["gravity"] = reduceFloatPrecision(
|
||||||
|
myConfig.isGravityTempAdj() ? corrGravity : gravity, 4);
|
||||||
|
doc["corr-gravity"] = reduceFloatPrecision(corrGravity, 4);
|
||||||
|
doc["angle"] = reduceFloatPrecision(angle, 2);
|
||||||
|
doc["battery"] = reduceFloatPrecision(myBatteryVoltage.getVoltage(), 2);
|
||||||
|
doc["rssi"] = WiFi.RSSI();
|
||||||
|
|
||||||
http.end();
|
// Some additional information
|
||||||
|
doc["gravity-units"] = "SG";
|
||||||
|
doc["run-time"] = reduceFloatPrecision(runTime, 2);
|
||||||
|
|
||||||
|
WiFiClient client;
|
||||||
|
HTTPClient http;
|
||||||
|
|
||||||
|
// Your Domain name with URL path or IP address with path
|
||||||
|
http.begin(client, serverPath);
|
||||||
|
String json;
|
||||||
|
serializeJson(doc, json);
|
||||||
|
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||||
|
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||||
|
Log.verbose(F("PUSH: json %s." CR), json.c_str());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Send HTTP POST request
|
||||||
|
http.addHeader(F("Content-Type"), F("application/json"));
|
||||||
|
int httpResponseCode = http.POST(json);
|
||||||
|
|
||||||
|
if (httpResponseCode == 200) {
|
||||||
|
Log.notice(F("PUSH: HTTP push successful, response=%d" CR),
|
||||||
|
httpResponseCode);
|
||||||
|
} else {
|
||||||
|
Log.error(F("PUSH: HTTP push failed, response=%d" CR), httpResponseCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
http.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
// EOF
|
// EOF
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 Magnus
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -21,33 +21,37 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#ifndef _PUSHTARGET_H
|
#ifndef SRC_PUSHTARGET_HPP_
|
||||||
#define _PUSHTARGET_H
|
#define SRC_PUSHTARGET_HPP_
|
||||||
|
|
||||||
// Includes
|
// Includes
|
||||||
#include "helper.h"
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#include <ESP8266HTTPClient.h>
|
#include <ESP8266HTTPClient.h>
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
|
||||||
|
#include <helper.hpp>
|
||||||
|
|
||||||
// Classes
|
// Classes
|
||||||
class PushTarget {
|
class PushTarget {
|
||||||
private:
|
private:
|
||||||
unsigned long ms; // Used to check that we do not post to often
|
uint32_t ms; // Used to check that we do not post to often
|
||||||
|
|
||||||
void sendBrewfather(float angle, float gravity, float corrGravity, float temp );
|
void sendBrewfather(float angle, float gravity, float corrGravity,
|
||||||
void sendHttp(String serverPath, float angle, float gravity, float corrGravity, float temp, float runTime);
|
float temp);
|
||||||
void sendInfluxDb2(float angle, float gravity, float corrGravity, float temp, float runTime);
|
void sendHttp(String serverPath, float angle, float gravity,
|
||||||
|
float corrGravity, float temp, float runTime);
|
||||||
|
void sendInfluxDb2(float angle, float gravity, float corrGravity, float temp,
|
||||||
|
float runTime);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PushTarget() { ms = millis(); }
|
PushTarget() { ms = millis(); }
|
||||||
void send(float angle, float gravity, float corrGravity, float temp, float runTime, bool force = false );
|
void send(float angle, float gravity, float corrGravity, float temp,
|
||||||
|
float runTime, bool force = false);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern PushTarget myPushTarget;
|
extern PushTarget myPushTarget;
|
||||||
|
|
||||||
#endif // _PUSHTARGET_H
|
#endif // SRC_PUSHTARGET_HPP_
|
||||||
|
|
||||||
// EOF
|
// EOF
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 Magnus
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -21,20 +21,22 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
#define INCBIN_OUTPUT_SECTION ".irom.text"
|
||||||
#include <incbin.h>
|
#include <incbin.h>
|
||||||
|
|
||||||
#if defined( EMBED_HTML )
|
#if defined(EMBED_HTML)
|
||||||
|
|
||||||
// Using minify to reduce memory usage. Reducing RAM memory usage with about 7%
|
// Using minify to reduce memory usage. Reducing RAM memory usage with about 7%
|
||||||
INCBIN(IndexHtm, "data/index.min.htm" );
|
INCBIN(IndexHtm, "data/index.min.htm");
|
||||||
INCBIN(DeviceHtm, "data/device.min.htm" );
|
INCBIN(DeviceHtm, "data/device.min.htm");
|
||||||
INCBIN(ConfigHtm, "data/config.min.htm" );
|
INCBIN(ConfigHtm, "data/config.min.htm");
|
||||||
INCBIN(AboutHtm, "data/about.min.htm" );
|
INCBIN(CalibrationHtm, "data/calibration.min.htm");
|
||||||
|
INCBIN(AboutHtm, "data/about.min.htm");
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
// Minium web interface for uploading htm files
|
// Minium web interface for uploading htm files
|
||||||
INCBIN(UploadHtm, "data/upload.min.htm" );
|
INCBIN(UploadHtm, "data/upload.min.htm");
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 Magnus
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -21,22 +21,21 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "tempsensor.h"
|
|
||||||
#include "helper.h"
|
|
||||||
#include "config.h"
|
|
||||||
#include "gyro.h"
|
|
||||||
#include <onewire.h>
|
|
||||||
#include <DallasTemperature.h>
|
#include <DallasTemperature.h>
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
|
#include <OneWire.h>
|
||||||
|
|
||||||
|
#include <config.hpp>
|
||||||
|
#include <gyro.hpp>
|
||||||
|
#include <helper.hpp>
|
||||||
|
#include <tempsensor.hpp>
|
||||||
|
|
||||||
//
|
//
|
||||||
// Conversion between C and F
|
// Conversion between C and F
|
||||||
//
|
//
|
||||||
float convertCtoF( float t ) {
|
float convertCtoF(float t) { return (t * 1.8) + 32.0; }
|
||||||
return (t * 1.8 ) + 32.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined( USE_GYRO_TEMP )
|
#if !defined(USE_GYRO_TEMP)
|
||||||
OneWire myOneWire(D6);
|
OneWire myOneWire(D6);
|
||||||
DallasTemperature mySensors(&myOneWire);
|
DallasTemperature mySensors(&myOneWire);
|
||||||
#define TEMPERATURE_PRECISION 9
|
#define TEMPERATURE_PRECISION 9
|
||||||
@ -48,43 +47,42 @@ TempSensor myTempSensor;
|
|||||||
// Setup temp sensors
|
// Setup temp sensors
|
||||||
//
|
//
|
||||||
void TempSensor::setup() {
|
void TempSensor::setup() {
|
||||||
|
#if defined(SIMULATE_TEMP)
|
||||||
#if defined( SIMULATE_TEMP )
|
hasSensors = true;
|
||||||
hasSensors = true;
|
return;
|
||||||
return;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined( USE_GYRO_TEMP )
|
#if defined(USE_GYRO_TEMP)
|
||||||
Log.notice(F("TSEN: Using temperature from gyro." CR));
|
Log.notice(F("TSEN: Using temperature from gyro." CR));
|
||||||
#else
|
#else
|
||||||
// This code is used to read the DS18 temp sensor
|
#if LOG_LEVEL == 6 && !defined(TSEN_DISABLE_LOGGING)
|
||||||
if( mySensors.getDS18Count() )
|
Log.verbose(F("TSEN: Looking for temp sensors." CR));
|
||||||
return;
|
|
||||||
|
|
||||||
#if LOG_LEVEL==6
|
|
||||||
Log.verbose(F("TSEN: Looking for temp sensors." CR));
|
|
||||||
#endif
|
#endif
|
||||||
mySensors.begin();
|
mySensors.begin();
|
||||||
|
|
||||||
if( mySensors.getDS18Count() ) {
|
if (mySensors.getDS18Count()) {
|
||||||
Log.notice(F("TSEN: Found %d temperature sensor(s)." CR), mySensors.getDS18Count());
|
#if !defined(TSEN_DISABLE_LOGGING)
|
||||||
mySensors.setResolution(TEMPERATURE_PRECISION);
|
Log.notice(F("TSEN: Found %d temperature sensor(s)." CR),
|
||||||
}
|
mySensors.getDS18Count());
|
||||||
|
#endif
|
||||||
|
mySensors.setResolution(TEMPERATURE_PRECISION);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
float t = myConfig.getTempSensorAdj();
|
float t = myConfig.getTempSensorAdj();
|
||||||
|
|
||||||
// Set the temp sensor adjustment values
|
// Set the temp sensor adjustment values
|
||||||
if( myConfig.isTempC() ) {
|
if (myConfig.isTempC()) {
|
||||||
tempSensorAdjF = t * 1.8; // Convert the adjustment value to C
|
tempSensorAdjF = t * 1.8; // Convert the adjustment value to C
|
||||||
tempSensorAdjC = t;
|
tempSensorAdjC = t;
|
||||||
} else {
|
} else {
|
||||||
tempSensorAdjF = t;
|
tempSensorAdjF = t;
|
||||||
tempSensorAdjC = t * 0.556; // Convert the adjustent value to F
|
tempSensorAdjC = t * 0.556; // Convert the adjustent value to F
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6 && !defined(TSEN_DISABLE_LOGGING)
|
||||||
Log.verbose(F("TSEN: Adjustment values for temp sensor %F C, %F F." CR), tempSensorAdjC, tempSensorAdjF );
|
Log.verbose(F("TSEN: Adjustment values for temp sensor %F C, %F F." CR),
|
||||||
|
tempSensorAdjC, tempSensorAdjF);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,40 +90,40 @@ void TempSensor::setup() {
|
|||||||
// Retrieving value from sensor, value is in Celcius
|
// Retrieving value from sensor, value is in Celcius
|
||||||
//
|
//
|
||||||
float TempSensor::getValue() {
|
float TempSensor::getValue() {
|
||||||
#if defined( SIMULATE_TEMP )
|
#if defined(SIMULATE_TEMP)
|
||||||
return 21;
|
return 21;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined( USE_GYRO_TEMP )
|
#if defined(USE_GYRO_TEMP)
|
||||||
// When using the gyro temperature only the first read value will be accurate so we will use this for processing.
|
// When using the gyro temperature only the first read value will be accurate
|
||||||
//LOG_PERF_START("temp-get");
|
// so we will use this for processing.
|
||||||
float c = myGyro.getInitialSensorTempC();
|
float c = myGyro.getInitialSensorTempC();
|
||||||
//LOG_PERF_STOP("temp-get");
|
hasSensor = true;
|
||||||
hasSensor = true;
|
return c;
|
||||||
return c;
|
#if LOG_LEVEL == 6 && !defined(TSEN_DISABLE_LOGGING)
|
||||||
#if LOG_LEVEL==6
|
Log.verbose(F("TSEN: Reciving temp value for gyro sensor %F C." CR), c);
|
||||||
Log.verbose(F("TSEN: Reciving temp value for gyro sensor %F C." CR), c);
|
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
// Read the sensors
|
// If we dont have sensors just return 0
|
||||||
//LOG_PERF_START("temp-request");
|
if (!mySensors.getDS18Count()) {
|
||||||
mySensors.requestTemperatures();
|
Log.error(F("TSEN: No temperature sensors found. Skipping read." CR));
|
||||||
//LOG_PERF_STOP("temp-request");
|
return -273;
|
||||||
|
}
|
||||||
|
|
||||||
float c = 0;
|
// Read the sensors
|
||||||
|
mySensors.requestTemperatures();
|
||||||
|
|
||||||
if( mySensors.getDS18Count() >= 1) {
|
float c = 0;
|
||||||
//LOG_PERF_START("temp-get");
|
|
||||||
c = mySensors.getTempCByIndex(0);
|
|
||||||
//LOG_PERF_STOP("temp-get");
|
|
||||||
|
|
||||||
#if LOG_LEVEL==6
|
if (mySensors.getDS18Count() >= 1) {
|
||||||
Log.verbose(F("TSEN: Reciving temp value for sensor %F C." CR), c);
|
c = mySensors.getTempCByIndex(0);
|
||||||
|
|
||||||
|
#if LOG_LEVEL == 6 && !defined(TSEN_DISABLE_LOGGING)
|
||||||
|
Log.verbose(F("TSEN: Reciving temp value for sensor %F C." CR), c);
|
||||||
#endif
|
#endif
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
|
return c;
|
||||||
return c;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 Magnus
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -21,30 +21,30 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#ifndef _TEMPSENSOR_H
|
#ifndef SRC_TEMPSENSOR_HPP_
|
||||||
#define _TEMPSENSOR_H
|
#define SRC_TEMPSENSOR_HPP_
|
||||||
|
|
||||||
// definitions
|
// definitions
|
||||||
float convertCtoF( float t );
|
float convertCtoF(float t);
|
||||||
|
|
||||||
// classes
|
// classes
|
||||||
class TempSensor {
|
class TempSensor {
|
||||||
private:
|
private:
|
||||||
bool hasSensor = false;
|
bool hasSensor = false;
|
||||||
float tempSensorAdjF = 0;
|
float tempSensorAdjF = 0;
|
||||||
float tempSensorAdjC = 0;
|
float tempSensorAdjC = 0;
|
||||||
float getValue();
|
float getValue();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void setup();
|
void setup();
|
||||||
bool isSensorAttached() { return hasSensor; };
|
bool isSensorAttached() { return hasSensor; }
|
||||||
float getTempC() { return getValue() + tempSensorAdjC; }
|
float getTempC() { return getValue() + tempSensorAdjC; }
|
||||||
float getTempF() { return convertCtoF(getValue()) + tempSensorAdjF; };
|
float getTempF() { return convertCtoF(getValue()) + tempSensorAdjF; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Global instance created
|
// Global instance created
|
||||||
extern TempSensor myTempSensor;
|
extern TempSensor myTempSensor;
|
||||||
|
|
||||||
#endif // _TEMPSENSOR_H
|
#endif // SRC_TEMPSENSOR_HPP_
|
||||||
|
|
||||||
// EOF
|
// EOF
|
116
src/webserver.hpp
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
|
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 above 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.
|
||||||
|
*/
|
||||||
|
#ifndef SRC_WEBSERVER_HPP_
|
||||||
|
#define SRC_WEBSERVER_HPP_
|
||||||
|
|
||||||
|
// Include
|
||||||
|
#include <ESP8266WebServer.h>
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <ESP8266mDNS.h>
|
||||||
|
#include <incbin.h>
|
||||||
|
|
||||||
|
// Binary resouces
|
||||||
|
#if defined(EMBED_HTML)
|
||||||
|
INCBIN_EXTERN(IndexHtm);
|
||||||
|
INCBIN_EXTERN(DeviceHtm);
|
||||||
|
INCBIN_EXTERN(ConfigHtm);
|
||||||
|
INCBIN_EXTERN(CalibrationHtm);
|
||||||
|
INCBIN_EXTERN(AboutHtm);
|
||||||
|
#else
|
||||||
|
INCBIN_EXTERN(UploadHtm);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// classes
|
||||||
|
class WebServer {
|
||||||
|
private:
|
||||||
|
ESP8266WebServer* server = 0;
|
||||||
|
File uploadFile;
|
||||||
|
int lastFormulaCreateError = 0;
|
||||||
|
|
||||||
|
void webHandleConfig();
|
||||||
|
void webHandleFormulaWrite();
|
||||||
|
void webHandleFormulaRead();
|
||||||
|
void webHandleConfigHardware();
|
||||||
|
void webHandleConfigGravity();
|
||||||
|
void webHandleConfigPush();
|
||||||
|
void webHandleConfigDevice();
|
||||||
|
void webHandleStatusSleepmode();
|
||||||
|
void webHandleClearWIFI();
|
||||||
|
void webHandleStatus();
|
||||||
|
void webHandleFactoryReset();
|
||||||
|
void webHandleCalibrate();
|
||||||
|
void webHandleUploadFile();
|
||||||
|
void webHandleUpload();
|
||||||
|
void webHandleDevice();
|
||||||
|
void webHandlePageNotFound();
|
||||||
|
|
||||||
|
// Inline functions.
|
||||||
|
void webReturnOK() { server->send(200); }
|
||||||
|
#if defined(EMBED_HTML)
|
||||||
|
void webReturnIndexHtm() {
|
||||||
|
server->send_P(200, "text/html", (const char*)gIndexHtmData, gIndexHtmSize);
|
||||||
|
}
|
||||||
|
void webReturnDeviceHtm() {
|
||||||
|
server->send_P(200, "text/html", (const char*)gDeviceHtmData,
|
||||||
|
gDeviceHtmSize);
|
||||||
|
}
|
||||||
|
void webReturnConfigHtm() {
|
||||||
|
server->send_P(200, "text/html", (const char*)gConfigHtmData,
|
||||||
|
gConfigHtmSize);
|
||||||
|
}
|
||||||
|
void webReturnCalibrationHtm() {
|
||||||
|
server->send_P(200, "text/html", (const char*)gCalibrationHtmData,
|
||||||
|
gCalibrationHtmSize);
|
||||||
|
}
|
||||||
|
void webReturnAboutHtm() {
|
||||||
|
server->send_P(200, "text/html", (const char*)gAboutHtmData, gAboutHtmSize);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void webReturnUploadHtm() {
|
||||||
|
server->send_P(200, "text/html", (const char*)gUploadHtmData,
|
||||||
|
gUploadHtmSize);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum HtmlFile {
|
||||||
|
HTML_INDEX = 0,
|
||||||
|
HTML_DEVICE = 1,
|
||||||
|
HTML_CONFIG = 2,
|
||||||
|
HTML_ABOUT = 3,
|
||||||
|
HTML_CALIBRATION = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
bool setupWebServer();
|
||||||
|
void loop();
|
||||||
|
bool checkHtmlFile(HtmlFile item);
|
||||||
|
const char* getHtmlFileName(HtmlFile item);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Global instance created
|
||||||
|
extern WebServer myWebServer;
|
||||||
|
|
||||||
|
#endif // SRC_WEBSERVER_HPP_
|
||||||
|
|
||||||
|
// EOF
|
403
src/wifi.cpp
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 Magnus
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -21,261 +21,284 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "wifi.h"
|
|
||||||
#include "config.h"
|
|
||||||
#include "helper.h"
|
|
||||||
#include "gyro.h"
|
|
||||||
#include "calc.h"
|
|
||||||
#include "tempsensor.h"
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <ESP8266mDNS.h>
|
|
||||||
#include <ESP8266HTTPClient.h>
|
#include <ESP8266HTTPClient.h>
|
||||||
#include <ESP8266httpUpdate.h>
|
#include <ESP8266httpUpdate.h>
|
||||||
#include <WiFiManager.h>
|
#include <ESP8266mDNS.h>
|
||||||
#include <LittleFS.h>
|
#include <LittleFS.h>
|
||||||
|
#include <WiFiManager.h>
|
||||||
#include <incbin.h>
|
#include <incbin.h>
|
||||||
|
|
||||||
|
#include <calc.hpp>
|
||||||
|
#include <config.hpp>
|
||||||
|
#include <gyro.hpp>
|
||||||
|
#include <helper.hpp>
|
||||||
|
#include <tempsensor.hpp>
|
||||||
|
#include <wifi.hpp>
|
||||||
|
|
||||||
Wifi myWifi;
|
Wifi myWifi;
|
||||||
WiFiManager myWifiManager;
|
|
||||||
bool shouldSaveConfig = false;
|
|
||||||
|
|
||||||
const char* userSSID= USER_SSID;
|
const char *userSSID = USER_SSID;
|
||||||
const char* userPWD = USER_SSID_PWD;
|
const char *userPWD = USER_SSID_PWD;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Callback notifying us of the need to save config
|
// Connect to last known access point or create one if connection is not
|
||||||
|
// working.
|
||||||
//
|
//
|
||||||
void saveConfigCallback () {
|
bool Wifi::connect(bool showPortal) {
|
||||||
shouldSaveConfig = true;
|
WiFi.persistent(true);
|
||||||
}
|
WiFi.mode(WIFI_STA);
|
||||||
|
|
||||||
//
|
if (!strlen(myConfig.getWifiSSID())) {
|
||||||
// Connect to last known access point or create one if connection is not working.
|
Log.info(
|
||||||
//
|
F("WIFI: No SSID seams to be stored, forcing portal to start." CR));
|
||||||
bool Wifi::connect( bool showPortal ) {
|
showPortal = true;
|
||||||
#if LOG_LEVEL==6
|
} else {
|
||||||
Log.verbose(F("WIFI: Connecting to WIFI via connection manager (portal=%s)." CR), showPortal?"true":"false");
|
// Log.info(F("WIFI: Using SSID=%s and %s." CR), myConfig.getWifiSSID(),
|
||||||
|
// myConfig.getWifiPass()); Log.info(F("WIFI: Using SSID=%s and %s." CR),
|
||||||
|
// myConfig.getWifiSSID(), "*****");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(userSSID) == 0 && showPortal) {
|
||||||
|
#if LOG_LEVEL == 6
|
||||||
|
Log.verbose(
|
||||||
|
F("WIFI: Connecting to WIFI via connection manager (portal=%s)." CR),
|
||||||
|
showPortal ? "true" : "false");
|
||||||
|
#endif
|
||||||
|
WiFiManager myWifiManager;
|
||||||
|
Log.notice(F("WIFI: Starting wifi portal." CR));
|
||||||
myWifiManager.setDebugOutput(true);
|
myWifiManager.setDebugOutput(true);
|
||||||
#endif
|
myWifiManager.setClass("invert");
|
||||||
if( strlen(userSSID)==0 && showPortal ) {
|
myWifiManager.setConfigPortalTimeout(120); // Keep it open for 120 seconds
|
||||||
Log.notice(F("WIFI: Starting wifi portal." CR));
|
bool f =
|
||||||
|
myWifiManager.startConfigPortal(WIFI_DEFAULT_SSID, WIFI_DEFAULT_PWD);
|
||||||
myWifiManager.setBreakAfterConfig( true );
|
if (f) {
|
||||||
myWifiManager.setSaveConfigCallback(saveConfigCallback);
|
// Log.notice(F("WIFI: Success got values from WIFI portal=%s,%s." CR),
|
||||||
myWifiManager.setMinimumSignalQuality(10);
|
// myWifiManager.getWiFiSSID(), myWifiManager.getWiFiPass() );
|
||||||
myWifiManager.setClass("invert");
|
Log.notice(F("WIFI: Success got values from WIFI portal=%s,%s." CR),
|
||||||
myWifiManager.setHostname( myConfig.getMDNS() );
|
myWifiManager.getWiFiSSID(), "*****");
|
||||||
myWifiManager.setConfigPortalTimeout( 120 ); // Keep it open for 120 seconds
|
myConfig.setWifiSSID(myWifiManager.getWiFiSSID());
|
||||||
|
myConfig.setWifiPass(myWifiManager.getWiFiPass());
|
||||||
WiFiManagerParameter mdnsParam("mDNS", "hostname", myConfig.getMDNS(), 20);
|
myConfig.saveFile();
|
||||||
myWifiManager.addParameter( &mdnsParam );
|
|
||||||
|
|
||||||
myWifiManager.startConfigPortal( WIFI_DEFAULT_SSID, WIFI_DEFAULT_PWD );
|
|
||||||
|
|
||||||
if( shouldSaveConfig ) {
|
|
||||||
myConfig.setMDNS( mdnsParam.getValue() );
|
|
||||||
myConfig.saveFile();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect to wifi
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
Log.notice(F("WIFI: Connecting to WIFI." CR));
|
|
||||||
WiFi.mode(WIFI_STA);
|
|
||||||
if( strlen(userSSID) ) {
|
|
||||||
Log.notice(F("WIFI: Connecting to wifi using predefined settings %s." CR), userSSID);
|
|
||||||
WiFi.begin( userSSID, userPWD );
|
|
||||||
} else {
|
} else {
|
||||||
WiFi.begin();
|
Log.notice(F("WIFI: Failure from WIFI portal, rebooting." CR));
|
||||||
#if LOG_LEVEL==6
|
delay(200);
|
||||||
Log.verbose(F("WIFI: Using SSID=%s, KEY=%s." CR), WiFi.SSID().c_str(), WiFi.psk().c_str() );
|
ESP.reset();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while( WiFi.status() != WL_CONNECTED ) {
|
// Connect to wifi
|
||||||
delay(100);
|
int i = 0;
|
||||||
Serial.print( "." );
|
|
||||||
|
|
||||||
// if( i++ > 60 ) { // Try for 6 seconds.
|
// Log.notice(F("WIFI: Connecting to WIFI, mode=%d,persistent=%d,fhy=%d ."
|
||||||
if( i++ > 200 ) { // Try for 20 seconds.
|
// CR), WiFi.getMode(), WiFi.getPersistent(), WiFi.getPhyMode() );
|
||||||
Log.error(F("WIFI: Failed to connect to wifi, aborting." CR));
|
WiFi.mode(WIFI_STA);
|
||||||
return connectedFlag; // Return to main that we have failed to connect.
|
if (strlen(userSSID)) {
|
||||||
}
|
Log.notice(F("WIFI: Connecting to wifi using hardcoded settings %s." CR),
|
||||||
|
userSSID);
|
||||||
|
WiFi.begin(userSSID, userPWD);
|
||||||
|
} else {
|
||||||
|
Log.notice(F("WIFI: Connecting to wifi using stored settings %s." CR),
|
||||||
|
myConfig.getWifiSSID());
|
||||||
|
WiFi.begin(myConfig.getWifiSSID(), myConfig.getWifiPass());
|
||||||
|
}
|
||||||
|
|
||||||
|
// WiFi.printDiag(Serial);
|
||||||
|
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
delay(200);
|
||||||
|
Serial.print(".");
|
||||||
|
|
||||||
|
if (i++ > 100) { // Try for 20 seconds.
|
||||||
|
Log.error(F("WIFI: Failed to connect to wifi %d, aborting %s." CR),
|
||||||
|
WiFi.status(), getIPAddress().c_str());
|
||||||
|
WiFi.disconnect();
|
||||||
|
return connectedFlag; // Return to main that we have failed to connect.
|
||||||
}
|
}
|
||||||
Serial.print( CR );
|
}
|
||||||
connectedFlag = true;
|
Serial.print(CR);
|
||||||
Log.notice(F("WIFI: Connected to wifi ip=%s." CR), getIPAddress().c_str() );
|
connectedFlag = true;
|
||||||
Log.notice(F("WIFI: Using mDNS name %s%s." CR), myConfig.getMDNS() );
|
Log.notice(F("WIFI: Connected to wifi ip=%s." CR), getIPAddress().c_str());
|
||||||
return connectedFlag;
|
Log.notice(F("WIFI: Using mDNS name %s." CR), myConfig.getMDNS());
|
||||||
|
return connectedFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// This will erase the stored credentials and forcing the WIFI manager to AP mode.
|
// This will erase the stored credentials and forcing the WIFI manager to AP
|
||||||
|
// mode.
|
||||||
//
|
//
|
||||||
bool Wifi::disconnect() {
|
bool Wifi::disconnect() {
|
||||||
Log.notice(F("WIFI: Erasing stored WIFI credentials." CR));
|
Log.notice(F("WIFI: Erasing stored WIFI credentials." CR));
|
||||||
// Erase WIFI credentials
|
// Erase WIFI credentials
|
||||||
return WiFi.disconnect(true);
|
return WiFi.disconnect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined( ACTIVATE_OTA )
|
#if defined(ACTIVATE_OTA)
|
||||||
|
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
bool Wifi::updateFirmware() {
|
bool Wifi::updateFirmware() {
|
||||||
if( !newFirmware ) {
|
if (!newFirmware) {
|
||||||
Log.notice(F("WIFI: No newer version exist, skipping update." CR));
|
Log.notice(F("WIFI: No newer version exist, skipping update." CR));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6
|
||||||
Log.verbose(F("WIFI: Updating firmware." CR));
|
Log.verbose(F("WIFI: Updating firmware." CR));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
WiFiClient client;
|
WiFiClient client;
|
||||||
String serverPath = myConfig.getOtaURL();
|
String serverPath = myConfig.getOtaURL();
|
||||||
serverPath += "firmware.bin";
|
serverPath += "firmware.bin";
|
||||||
|
|
||||||
HTTPUpdateResult ret = ESPhttpUpdate.update(client, serverPath);
|
HTTPUpdateResult ret = ESPhttpUpdate.update(client, serverPath);
|
||||||
|
|
||||||
switch(ret) {
|
switch (ret) {
|
||||||
case HTTP_UPDATE_FAILED:
|
case HTTP_UPDATE_FAILED:
|
||||||
Log.error(F("WIFI: OTA update failed %d, %s." CR), ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
|
Log.error(F("WIFI: OTA update failed %d, %s." CR),
|
||||||
break;
|
ESPhttpUpdate.getLastError(),
|
||||||
case HTTP_UPDATE_NO_UPDATES:
|
ESPhttpUpdate.getLastErrorString().c_str());
|
||||||
break;
|
break;
|
||||||
case HTTP_UPDATE_OK:
|
case HTTP_UPDATE_NO_UPDATES:
|
||||||
Log.notice("WIFI: OTA Update sucesfull, rebooting." );
|
break;
|
||||||
delay(100);
|
case HTTP_UPDATE_OK:
|
||||||
ESP.reset();
|
Log.notice("WIFI: OTA Update sucesfull, rebooting.");
|
||||||
break;
|
delay(100);
|
||||||
}
|
ESP.reset();
|
||||||
return false;
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Download and save file
|
// Download and save file
|
||||||
//
|
//
|
||||||
void Wifi::downloadFile(const char *fname) {
|
void Wifi::downloadFile(const char *fname) {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6
|
||||||
Log.verbose(F("WIFI: Download file %s." CR), fname);
|
Log.verbose(F("WIFI: Download file %s." CR), fname);
|
||||||
#endif
|
#endif
|
||||||
WiFiClient client;
|
WiFiClient client;
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
String serverPath = myConfig.getOtaURL();
|
String serverPath = myConfig.getOtaURL();
|
||||||
serverPath += fname;
|
serverPath += fname;
|
||||||
|
|
||||||
// Your Domain name with URL path or IP address with path
|
// Your Domain name with URL path or IP address with path
|
||||||
http.begin( client, serverPath);
|
http.begin(client, serverPath);
|
||||||
int httpResponseCode = http.GET();
|
int httpResponseCode = http.GET();
|
||||||
|
|
||||||
if (httpResponseCode==200) {
|
if (httpResponseCode == 200) {
|
||||||
File f = LittleFS.open( fname, "w" );
|
File f = LittleFS.open(fname, "w");
|
||||||
http.writeToStream( &f );
|
http.writeToStream(&f);
|
||||||
f.close();
|
f.close();
|
||||||
Log.notice(F("WIFI: Downloaded file %s." CR), fname);
|
Log.notice(F("WIFI: Downloaded file %s." CR), fname);
|
||||||
} else {
|
} else {
|
||||||
Log.error(F("WIFI: Failed to download file, respone=%d" CR), httpResponseCode);
|
Log.error(F("WIFI: Failed to download file, respone=%d" CR),
|
||||||
}
|
httpResponseCode);
|
||||||
http.end();
|
}
|
||||||
|
http.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Check what firmware version is available over OTA
|
// Check what firmware version is available over OTA
|
||||||
//
|
//
|
||||||
bool Wifi::checkFirmwareVersion() {
|
bool Wifi::checkFirmwareVersion() {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6
|
||||||
Log.verbose(F("WIFI: Checking if new version exist." CR));
|
Log.verbose(F("WIFI: Checking if new version exist." CR));
|
||||||
#endif
|
#endif
|
||||||
WiFiClient client;
|
WiFiClient client;
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
String serverPath = myConfig.getOtaURL();
|
String serverPath = myConfig.getOtaURL();
|
||||||
serverPath += "version.json";
|
serverPath += "version.json";
|
||||||
|
|
||||||
// Your Domain name with URL path or IP address with path
|
// Your Domain name with URL path or IP address with path
|
||||||
http.begin( client, serverPath);
|
http.begin(client, serverPath);
|
||||||
|
|
||||||
// Send HTTP GET request
|
// Send HTTP GET request
|
||||||
int httpResponseCode = http.GET();
|
int httpResponseCode = http.GET();
|
||||||
|
|
||||||
if (httpResponseCode==200) {
|
if (httpResponseCode == 200) {
|
||||||
Log.notice(F("WIFI: Found version.json, response=%d" CR), httpResponseCode);
|
Log.notice(F("WIFI: Found version.json, response=%d" CR), httpResponseCode);
|
||||||
|
|
||||||
String payload = http.getString();
|
String payload = http.getString();
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6
|
||||||
Log.verbose(F("WIFI: Payload %s." CR), payload.c_str());
|
Log.verbose(F("WIFI: Payload %s." CR), payload.c_str());
|
||||||
#endif
|
#endif
|
||||||
DynamicJsonDocument ver(300);
|
DynamicJsonDocument ver(300);
|
||||||
DeserializationError err = deserializeJson(ver, payload);
|
DeserializationError err = deserializeJson(ver, payload);
|
||||||
if( err ) {
|
if (err) {
|
||||||
Log.error(F("WIFI: Failed to parse version.json, %s" CR), err);
|
Log.error(F("WIFI: Failed to parse version.json, %s" CR), err);
|
||||||
} else {
|
|
||||||
#if LOG_LEVEL==6
|
|
||||||
Log.verbose(F("WIFI: Project %s version %s." CR), (const char*) ver["project"], (const char*) ver["version"]);
|
|
||||||
#endif
|
|
||||||
int newVer[3];
|
|
||||||
int curVer[3];
|
|
||||||
|
|
||||||
if( parseFirmwareVersionString( newVer, (const char*) ver["version"] ) ) {
|
|
||||||
if( parseFirmwareVersionString( curVer, CFG_APPVER) ) {
|
|
||||||
#if LOG_LEVEL==6
|
|
||||||
Log.verbose(F("WIFI: OTA checking new=%d.%d.%d cur=%d.%d.%d" CR), newVer[0], newVer[1], newVer[2], curVer[0], curVer[1], curVer[2] );
|
|
||||||
#endif
|
|
||||||
// Compare major version
|
|
||||||
if( newVer[0] > curVer[0] )
|
|
||||||
newFirmware = true;
|
|
||||||
// Compare minor version
|
|
||||||
if( newVer[0] == curVer[0] && newVer[1] > curVer[1] )
|
|
||||||
newFirmware = true;
|
|
||||||
// Compare patch version
|
|
||||||
if( newVer[0] == curVer[0] && newVer[1] == curVer[1] && newVer[2] > curVer[2] )
|
|
||||||
newFirmware = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Download new html files to filesystem if they are present.
|
|
||||||
if( !ver["html"].isNull() && newFirmware ) {
|
|
||||||
Log.notice(F("WIFI: OTA downloading new html files." CR));
|
|
||||||
JsonArray htmlFiles = ver["html"].as<JsonArray>();
|
|
||||||
for(JsonVariant v : htmlFiles) {
|
|
||||||
String s = v;
|
|
||||||
#if LOG_LEVEL==6
|
|
||||||
Log.verbose(F("WIFI: OTA listed html file %s" CR), s.c_str() );
|
|
||||||
#endif
|
|
||||||
downloadFile( s.c_str() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Log.error(F("WIFI: OTA error checking version.json, response=%d" CR), httpResponseCode);
|
#if LOG_LEVEL == 6
|
||||||
}
|
Log.verbose(F("WIFI: Project %s version %s." CR),
|
||||||
http.end();
|
(const char *)ver["project"], (const char *)ver["version"]);
|
||||||
#if LOG_LEVEL==6
|
|
||||||
Log.verbose(F("WIFI: OTA found new version %s." CR), newFirmware?"true":"false");
|
|
||||||
#endif
|
#endif
|
||||||
return newFirmware;
|
int newVer[3];
|
||||||
|
int curVer[3];
|
||||||
|
|
||||||
|
if (parseFirmwareVersionString(newVer, (const char *)ver["version"])) {
|
||||||
|
if (parseFirmwareVersionString(curVer, CFG_APPVER)) {
|
||||||
|
#if LOG_LEVEL == 6
|
||||||
|
Log.verbose(F("WIFI: OTA checking new=%d.%d.%d cur=%d.%d.%d" CR),
|
||||||
|
newVer[0], newVer[1], newVer[2], curVer[0], curVer[1],
|
||||||
|
curVer[2]);
|
||||||
|
#endif
|
||||||
|
// Compare major version
|
||||||
|
if (newVer[0] > curVer[0]) newFirmware = true;
|
||||||
|
// Compare minor version
|
||||||
|
if (newVer[0] == curVer[0] && newVer[1] > curVer[1])
|
||||||
|
newFirmware = true;
|
||||||
|
// Compare patch version
|
||||||
|
if (newVer[0] == curVer[0] && newVer[1] == curVer[1] &&
|
||||||
|
newVer[2] > curVer[2])
|
||||||
|
newFirmware = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download new html files to filesystem if they are present.
|
||||||
|
if (!ver["html"].isNull() && newFirmware) {
|
||||||
|
Log.notice(F("WIFI: OTA downloading new html files." CR));
|
||||||
|
JsonArray htmlFiles = ver["html"].as<JsonArray>();
|
||||||
|
for (JsonVariant v : htmlFiles) {
|
||||||
|
String s = v;
|
||||||
|
#if LOG_LEVEL == 6
|
||||||
|
Log.verbose(F("WIFI: OTA listed html file %s" CR), s.c_str());
|
||||||
|
#endif
|
||||||
|
downloadFile(s.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.error(F("WIFI: OTA error checking version.json, response=%d" CR),
|
||||||
|
httpResponseCode);
|
||||||
|
}
|
||||||
|
http.end();
|
||||||
|
#if LOG_LEVEL == 6
|
||||||
|
Log.verbose(F("WIFI: OTA found new version %s." CR),
|
||||||
|
newFirmware ? "true" : "false");
|
||||||
|
#endif
|
||||||
|
return newFirmware;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Parse a version string in the format M.m.p (eg. 1.2.10)
|
// Parse a version string in the format M.m.p (eg. 1.2.10)
|
||||||
//
|
//
|
||||||
bool Wifi::parseFirmwareVersionString( int (&num)[3], const char *version ) {
|
bool Wifi::parseFirmwareVersionString(int (&num)[3], const char *version) {
|
||||||
#if LOG_LEVEL==6
|
#if LOG_LEVEL == 6
|
||||||
Log.verbose(F("WIFI: Parsing version number string %s." CR), version);
|
Log.verbose(F("WIFI: Parsing version number string %s." CR), version);
|
||||||
#endif
|
#endif
|
||||||
char temp[80];
|
char temp[80];
|
||||||
char *s;
|
char *s;
|
||||||
char *p = &temp[0];
|
char *p = &temp[0];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
strcpy( &temp[0], version );
|
// strcpy(&temp[0], version);
|
||||||
|
snprintf(&temp[0], sizeof(temp), "%s", version);
|
||||||
|
|
||||||
while ((s = strtok_r(p, ".", &p)) != NULL) {
|
while ((s = strtok_r(p, ".", &p)) != NULL) {
|
||||||
num[i++] = atoi( s );
|
num[i++] = atoi(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ACTIVATE_OTA
|
#endif // ACTIVATE_OTA
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 Magnus
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -21,38 +21,38 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#ifndef _WIFI_H
|
#ifndef SRC_WIFI_HPP_
|
||||||
#define _WIFI_H
|
#define SRC_WIFI_HPP_
|
||||||
|
|
||||||
// Include
|
// Include
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
|
|
||||||
// classes
|
// classes
|
||||||
class Wifi {
|
class Wifi {
|
||||||
private:
|
private:
|
||||||
// WIFI
|
// WIFI
|
||||||
bool connectedFlag = false;
|
bool connectedFlag = false;
|
||||||
|
|
||||||
// OTA
|
// OTA
|
||||||
bool newFirmware = false;
|
bool newFirmware = false;
|
||||||
bool parseFirmwareVersionString( int (&num)[3], const char *version );
|
bool parseFirmwareVersionString(int (&num)[3], const char *version);
|
||||||
void downloadFile(const char *fname);
|
void downloadFile(const char *fname);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// WIFI
|
// WIFI
|
||||||
bool connect( bool showPortal = false );
|
bool connect(bool showPortal = false);
|
||||||
bool disconnect();
|
bool disconnect();
|
||||||
bool isConnected() { return connectedFlag; };
|
bool isConnected() { return connectedFlag; }
|
||||||
String getIPAddress() { return WiFi.localIP().toString(); };
|
String getIPAddress() { return WiFi.localIP().toString(); }
|
||||||
|
|
||||||
// OTA
|
// OTA
|
||||||
bool updateFirmware();
|
bool updateFirmware();
|
||||||
bool checkFirmwareVersion();
|
bool checkFirmwareVersion();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Global instance created
|
// Global instance created
|
||||||
extern Wifi myWifi;
|
extern Wifi myWifi;
|
||||||
|
|
||||||
#endif // _WIFI_H
|
#endif // SRC_WIFI_HPP_
|
||||||
|
|
||||||
// EOF
|
// EOF
|
21
src_docs/Makefile
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Minimal makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line, and also
|
||||||
|
# from the environment for the first two.
|
||||||
|
SPHINXOPTS ?=
|
||||||
|
SPHINXBUILD ?= sphinx-build
|
||||||
|
SOURCEDIR = source
|
||||||
|
BUILDDIR = _build
|
||||||
|
|
||||||
|
# Put it first so that "make" without argument is like "make help".
|
||||||
|
help:
|
||||||
|
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|
||||||
|
.PHONY: help Makefile
|
||||||
|
|
||||||
|
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||||
|
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||||
|
%: Makefile
|
||||||
|
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|
35
src_docs/make.bat
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
pushd %~dp0
|
||||||
|
|
||||||
|
REM Command file for Sphinx documentation
|
||||||
|
|
||||||
|
if "%SPHINXBUILD%" == "" (
|
||||||
|
set SPHINXBUILD=sphinx-build
|
||||||
|
)
|
||||||
|
set SOURCEDIR=source
|
||||||
|
set BUILDDIR=build
|
||||||
|
|
||||||
|
if "%1" == "" goto help
|
||||||
|
|
||||||
|
%SPHINXBUILD% >NUL 2>NUL
|
||||||
|
if errorlevel 9009 (
|
||||||
|
echo.
|
||||||
|
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||||
|
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||||
|
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||||
|
echo.may add the Sphinx directory to PATH.
|
||||||
|
echo.
|
||||||
|
echo.If you don't have Sphinx installed, grab it from
|
||||||
|
echo.https://www.sphinx-doc.org/
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:help
|
||||||
|
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||||
|
|
||||||
|
:end
|
||||||
|
popd
|
15
src_docs/source/backlog.rst
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
Backlog of changes
|
||||||
|
##################
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
- Write contribution instructions
|
||||||
|
|
||||||
|
Code
|
||||||
|
-------------
|
||||||
|
|
||||||
|
- Support for MQTT
|
||||||
|
- Support for plato
|
||||||
|
- Use pre-commit for validating check-in
|
||||||
|
- Automatic builds via github actions
|
111
src_docs/source/compiling.rst
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
.. _compiling-the-software:
|
||||||
|
|
||||||
|
Compiling the software
|
||||||
|
######################
|
||||||
|
|
||||||
|
Tools
|
||||||
|
=====
|
||||||
|
I use the following tools in order to build and manage the software:
|
||||||
|
|
||||||
|
* Visual Studio Code
|
||||||
|
* PlatformIO
|
||||||
|
* Git for Windows
|
||||||
|
* VSCode plugin: Minify (used to minimise the html files)
|
||||||
|
|
||||||
|
Code Formatting
|
||||||
|
===============
|
||||||
|
I use pre-commit and their cpp style checks to validate the code. Plugin defintions are found in **.pre-commit-config.yaml**
|
||||||
|
|
||||||
|
`Pre-Commit <https://www.pre-commit.com>`_
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
There is not yet any automatic checks since this does not work on Windows. It works if running under WSL2 with Ubuntu.
|
||||||
|
|
||||||
|
|
||||||
|
Targets
|
||||||
|
=======
|
||||||
|
In the platformio config there are 3 targets defined
|
||||||
|
|
||||||
|
* gravity-debug; Maximum logging for trouble shooting, deep sleep is disabled.
|
||||||
|
* gravity-release; Standard release
|
||||||
|
* gravity-perf; Standard release but contains code for measuring performance
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
The debug target can be unsable and crash the device under certain circumstanses.
|
||||||
|
Excessive logging to the serial port can cause corruption and crashes. I'm still
|
||||||
|
trying to figure out what causes these issues in the debug target. Other targets are
|
||||||
|
stable and works fine.
|
||||||
|
|
||||||
|
|
||||||
|
Source structure
|
||||||
|
================
|
||||||
|
.. list-table:: Directory structure
|
||||||
|
:widths: 40 60
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - path
|
||||||
|
- content
|
||||||
|
* - /bin
|
||||||
|
- Contains compiled binaries
|
||||||
|
* - /data
|
||||||
|
- Directory for flashing device filesystem
|
||||||
|
* - /doc
|
||||||
|
- Various external documents used as input
|
||||||
|
* - /html
|
||||||
|
- Source for html files
|
||||||
|
* - /img
|
||||||
|
- Images uses in README.md
|
||||||
|
* - /lib
|
||||||
|
- External libraries used when compiling
|
||||||
|
* - /script
|
||||||
|
- Scripts used in build process
|
||||||
|
* - /src
|
||||||
|
- Source code for software
|
||||||
|
* - /src_docs
|
||||||
|
- Source code for documentation
|
||||||
|
* - /stl
|
||||||
|
- 3d models
|
||||||
|
* - /test
|
||||||
|
- Test data for developing html files
|
||||||
|
|
||||||
|
|
||||||
|
Options
|
||||||
|
=======
|
||||||
|
This is a list of C++ defines that is used to enable/disable functions in the code.
|
||||||
|
|
||||||
|
.. list-table:: Defines
|
||||||
|
:widths: 40 60
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - define
|
||||||
|
- description
|
||||||
|
* - ACTIVATE_OTA
|
||||||
|
- Enables the OTA functionallity in the code
|
||||||
|
* - USE_GYRO_TEMP
|
||||||
|
- Uses temperature from gyro instead of DS18B20 (experimental)
|
||||||
|
* - SKIP_SLEEPMODE
|
||||||
|
- THe device never goes into sleep mode, useful when developing.
|
||||||
|
* - CFG_DISABLE_LOGGING
|
||||||
|
- Done include verbose logging in Config class. Excessive logging may crash device.
|
||||||
|
* - GYRO_DISABLE_LOGGING
|
||||||
|
- Done include verbose logging in Gyro class. Excessive logging may crash device.
|
||||||
|
* - PUSH_DISABLE_LOGGING
|
||||||
|
- Done include verbose logging in PushTarget class. Excessive logging may crash device.
|
||||||
|
* - TSEN_DISABLE_LOGGING
|
||||||
|
- Done include verbose logging in TempSensor class. Excessive logging may crash device.
|
||||||
|
* - WEB_DISABLE_LOGGING
|
||||||
|
- Done include verbose logging in WebServer class. Excessive logging may crash device.
|
||||||
|
* - MAIN_DISABLE_LOGGING
|
||||||
|
- Done include verbose logging in Main class. Excessive logging may crash device.
|
||||||
|
* - USE_LITTLEFS
|
||||||
|
- Use the new filesystem in Ardurino
|
||||||
|
* - EMBED_HTML
|
||||||
|
- Html files are included in code, if not defined they are served from the file system.
|
||||||
|
* - USER_SSID
|
||||||
|
- If defined the device will always use this SSID
|
||||||
|
* - USER_SSID_PWD
|
||||||
|
- Password to the SSID
|
||||||
|
* - CFG_APPVER
|
||||||
|
- Defines the version of the compiled software
|
||||||
|
|
55
src_docs/source/conf.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# Configuration file for the Sphinx documentation builder.
|
||||||
|
#
|
||||||
|
# This file only contains a selection of the most common options. For a full
|
||||||
|
# list see the documentation:
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||||
|
|
||||||
|
# -- Path setup --------------------------------------------------------------
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
#
|
||||||
|
# import os
|
||||||
|
# import sys
|
||||||
|
# sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
|
|
||||||
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
|
project = 'GravityMon'
|
||||||
|
copyright = '2021-2022, Magnus Persson'
|
||||||
|
author = 'Magnus Persson'
|
||||||
|
|
||||||
|
# The full version, including alpha/beta/rc tags
|
||||||
|
release = '0.5.0'
|
||||||
|
|
||||||
|
|
||||||
|
# -- General configuration ---------------------------------------------------
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
|
# ones.
|
||||||
|
extensions = [
|
||||||
|
]
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and
|
||||||
|
# directories to ignore when looking for source files.
|
||||||
|
# This pattern also affects html_static_path and html_extra_path.
|
||||||
|
exclude_patterns = []
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output -------------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
|
# a list of builtin themes.
|
||||||
|
#
|
||||||
|
html_theme = 'sphinx_rtd_theme'
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
html_static_path = ['_static']
|
561
src_docs/source/configuration.rst
Normal file
@ -0,0 +1,561 @@
|
|||||||
|
.. _setting-up-device:
|
||||||
|
|
||||||
|
Setting up device
|
||||||
|
#################
|
||||||
|
|
||||||
|
The device can operate in two modes and must be in ``configuration mode`` in order for the web server to be active.
|
||||||
|
|
||||||
|
One of the following conditions will place the device in ``configuration mode``:
|
||||||
|
|
||||||
|
- Gyro has not been calibrated
|
||||||
|
- Sleep mode has been disabled in the web interface
|
||||||
|
- Placed in horizontal mode 85-90 degrees
|
||||||
|
- Charger connected >4.15V
|
||||||
|
|
||||||
|
Status
|
||||||
|
======
|
||||||
|
|
||||||
|
URL: (http://gravmon.local)
|
||||||
|
|
||||||
|
.. image:: images/index.png
|
||||||
|
:width: 800
|
||||||
|
: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*.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
.. 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.
|
||||||
|
|
||||||
|
|
||||||
|
Device
|
||||||
|
======
|
||||||
|
|
||||||
|
URL: (http://gravmon.local/device)
|
||||||
|
|
||||||
|
.. image:: images/device.png
|
||||||
|
:width: 800
|
||||||
|
:alt: Device Settings
|
||||||
|
|
||||||
|
|
||||||
|
* **Version:**
|
||||||
|
|
||||||
|
Installed version of the code and html files.
|
||||||
|
|
||||||
|
* **Device name:**
|
||||||
|
|
||||||
|
This is unique name of the device.
|
||||||
|
|
||||||
|
* **Device ID:**
|
||||||
|
|
||||||
|
This is unique identifier for the device (ESP8266 id), this is required when using the API as an API Key to safeguard against faulty requests.
|
||||||
|
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
|
URL: (http://gravmon.local/config)
|
||||||
|
|
||||||
|
Device Setting
|
||||||
|
++++++++++++++
|
||||||
|
|
||||||
|
.. image:: images/config1.png
|
||||||
|
:width: 800
|
||||||
|
:alt: Device Settings
|
||||||
|
|
||||||
|
* **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)
|
||||||
|
|
||||||
|
* **Temperature format:**
|
||||||
|
|
||||||
|
Choose between Celsius and Farenheight
|
||||||
|
|
||||||
|
* **Interval:**
|
||||||
|
|
||||||
|
This defines how long the device should be sleeping between the readings when in `gravity monitoring` mode. You will also see the values in minutes/seconds to easier set the interval. 900s is a recommended interval.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The sleep interval can be set between 10 - 3600 seconds (60 minutes).
|
||||||
|
|
||||||
|
* **Calibration values:**
|
||||||
|
|
||||||
|
These are calibration data for the gyro. Place the device flat on a table and press the button to save the default orientation values. Without this calibration we cannot calculate the correct angle/tilt.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
The device will **not** go into `gravity monitoring` mode unless calibrated
|
||||||
|
|
||||||
|
Push Settings
|
||||||
|
+++++++++++++
|
||||||
|
|
||||||
|
.. image:: images/config2.png
|
||||||
|
:width: 800
|
||||||
|
:alt: Push Settings
|
||||||
|
|
||||||
|
* **HTTP URL 1:**
|
||||||
|
|
||||||
|
Endpoint to send data via http. Format used is standard iSpindle format (see format section).
|
||||||
|
|
||||||
|
* **HTTP URL 2:**
|
||||||
|
|
||||||
|
Endpoint to send data via http. Format used is standard iSpindle format (see format section).
|
||||||
|
|
||||||
|
* **Brewfather URL:**
|
||||||
|
|
||||||
|
Endpoint to send data via http to brewfather. Format used is defined by brewfather (see format section).
|
||||||
|
|
||||||
|
* **Influx DB v2 URL:**
|
||||||
|
|
||||||
|
Endpoint to send data via http to InfluxDB. For format (see format section).
|
||||||
|
|
||||||
|
* **Influx DB v2 Organisation:**
|
||||||
|
|
||||||
|
Name of organisation in Influx.
|
||||||
|
|
||||||
|
* **Influx DB v2 Bucket:**
|
||||||
|
|
||||||
|
Identifier for bucket.
|
||||||
|
|
||||||
|
* **Influx DB v2 Token:**
|
||||||
|
|
||||||
|
Token with write access to bucket.
|
||||||
|
|
||||||
|
|
||||||
|
Gravity Settings
|
||||||
|
++++++++++++++++
|
||||||
|
|
||||||
|
.. image:: images/config3.png
|
||||||
|
:width: 800
|
||||||
|
:alt: Gravity Settings
|
||||||
|
|
||||||
|
* **Gravity formula:**
|
||||||
|
|
||||||
|
Gravity formula is compatible with standard iSpindle formulas so any existing calculation option can be used. Is updated if the calibration function is used.
|
||||||
|
|
||||||
|
* **Temperature correct gravity:**
|
||||||
|
|
||||||
|
Will apply a temperature calibration formula to the gravity as a second step.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
This formula assumes that the calibration has been done at 20C.
|
||||||
|
|
||||||
|
Formula used in temperature correction:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
gravity*((1.00130346-0.000134722124*temp+0.00000204052596*temp^2-0.00000000232820948*temp^3)/
|
||||||
|
(1.00130346-0.000134722124*cal+0.00000204052596*cal^2-0.00000000232820948*cal^3))
|
||||||
|
|
||||||
|
|
||||||
|
Hardware Settings
|
||||||
|
+++++++++++++++++
|
||||||
|
|
||||||
|
.. image:: images/config4.png
|
||||||
|
:width: 800
|
||||||
|
:alt: Hardware Settings
|
||||||
|
|
||||||
|
* **Voltage factor:**
|
||||||
|
|
||||||
|
Factor used to calcualate the battery voltage. If you get a too low/high voltage you can adjust this value.
|
||||||
|
|
||||||
|
* **Temperature correction:**
|
||||||
|
|
||||||
|
This value will be added to the temperature reading (negative value will reduce temperature reading).
|
||||||
|
|
||||||
|
* **OTA URL:**
|
||||||
|
|
||||||
|
Should point to a URL where the .bin file + version.json file is located.
|
||||||
|
|
||||||
|
For the OTA to work, place the following files (version.json + firmware.bin) at the location that you pointed out in OTA URL. If the version number in the json file is newer than in the
|
||||||
|
code the update will be done during startup.
|
||||||
|
|
||||||
|
Example; OTA URL (don't forget trailing dash), the name of the file should be firmware.bin
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
http://192.168.1.1/firmware/gravmon/
|
||||||
|
|
||||||
|
|
||||||
|
.. _create-formula:
|
||||||
|
|
||||||
|
Create formula
|
||||||
|
##############
|
||||||
|
|
||||||
|
.. image:: images/formula1.png
|
||||||
|
:width: 800
|
||||||
|
:alt: Formula data
|
||||||
|
|
||||||
|
Here you can enter up to 5 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.
|
||||||
|
|
||||||
|
.. image:: images/formula2.png
|
||||||
|
:width: 800
|
||||||
|
: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.
|
||||||
|
|
||||||
|
.. _rest-api:
|
||||||
|
|
||||||
|
REST API
|
||||||
|
########
|
||||||
|
|
||||||
|
All the API's use a key called ``ID`` which is the unique device id (chip id). This is used as an API key when sending requests to the device.
|
||||||
|
|
||||||
|
GET: /api/config
|
||||||
|
================
|
||||||
|
|
||||||
|
Retrive the current configuation of the device via an HTTP GET command. Payload is in JSON format.
|
||||||
|
|
||||||
|
* ``temp-format`` can be either ``C`` or ``F``
|
||||||
|
* ``gravity-format`` is always ``G`` (plato is not yet supported)
|
||||||
|
|
||||||
|
Other parameters are the same as in the configuration guide.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"mdns": "gravmon",
|
||||||
|
"id": "ee1bfc",
|
||||||
|
"ota-url": "http://192.168.1.50:80/firmware/gravmon/",
|
||||||
|
"temp-format": "C",
|
||||||
|
"brewfather-push": "http://log.brewfather.net/stream?id=Qwerty",
|
||||||
|
"http-push": "http://192.168.1.50:9090/api/v1/Qwerty/telemetry",
|
||||||
|
"http-push2": "http://192.168.1.50/ispindel",
|
||||||
|
"influxdb2-push": "http://192.168.1.50:8086",
|
||||||
|
"influxdb2-org": "Qwerty",
|
||||||
|
"influxdb2-bucket": "Qwerty",
|
||||||
|
"influxdb2-auth": "Qwerty",
|
||||||
|
"sleep-interval": 30,
|
||||||
|
"voltage-factor": 1.59,
|
||||||
|
"gravity-formula": "0.0*tilt^3+0.0*tilt^2+0.0017978*tilt+0.9436",
|
||||||
|
"gravity-format": "G",
|
||||||
|
"temp-adjustment-value": 0,
|
||||||
|
"gravity-temp-adjustment": false,
|
||||||
|
"gyro-calibration-data": {
|
||||||
|
"ax": -330,
|
||||||
|
"ay": -2249,
|
||||||
|
"az": 1170,
|
||||||
|
"gx": 99,
|
||||||
|
"gy": -6,
|
||||||
|
"gz": 4
|
||||||
|
},
|
||||||
|
"angle": 90.93,
|
||||||
|
"gravity": 1.105,
|
||||||
|
"battery": 0.04
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GET: /api/device
|
||||||
|
================
|
||||||
|
|
||||||
|
Retrive the current device settings via an HTTP GET command. Payload is in JSON format.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"app-name": "GravityMon ",
|
||||||
|
"app-ver": "0.0.0",
|
||||||
|
"id": "ee1bfc",
|
||||||
|
"mdns": "gravmon"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GET: /api/status
|
||||||
|
================
|
||||||
|
|
||||||
|
Retrive the current device status via an HTTP GET command. Payload is in JSON format.
|
||||||
|
|
||||||
|
* ``temp-format`` can be either ``C`` or ``F``
|
||||||
|
|
||||||
|
Other parameters are the same as in the configuration guide.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": "ee1bfc",
|
||||||
|
"angle": 89.86,
|
||||||
|
"gravity": 1.1052,
|
||||||
|
"gravity-tempcorr": 1.1031,
|
||||||
|
"temp-c": 0,
|
||||||
|
"temp-f": 32,
|
||||||
|
"battery": 0,
|
||||||
|
"temp-format": "C",
|
||||||
|
"sleep-mode": false,
|
||||||
|
"rssi": -56
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GET: /api/config/formula
|
||||||
|
========================
|
||||||
|
|
||||||
|
Retrive the data used for formula calculation data via an HTTP GET command. Payload is in JSON format.
|
||||||
|
|
||||||
|
* ``a1``-``a4`` are the angles/tilt readings (up to 5 are currently supported)
|
||||||
|
* ``g1``-``g4`` are the corresponding gravity reaadings (in SG)
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": "ee1bfc",
|
||||||
|
"a1": 22.4,
|
||||||
|
"a2": 54.4,
|
||||||
|
"a3": 58,
|
||||||
|
"a4": 0,
|
||||||
|
"a5": 0,
|
||||||
|
"g1": 1.000,
|
||||||
|
"g2": 1.053,
|
||||||
|
"g3": 1.062,
|
||||||
|
"g4": 1,
|
||||||
|
"g5": 1
|
||||||
|
"gravity-formula": "0.0*tilt^3+0.0*tilt^2+0.0017978*tilt+0.9436",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
POST: /api/config/device
|
||||||
|
========================
|
||||||
|
|
||||||
|
Used to update device settings via an HTTP POST command. Payload is in JSON format.
|
||||||
|
|
||||||
|
* ``temp-format`` can be either ``C`` or ``F``
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": "ee1bfc",
|
||||||
|
"mdns": "gravmon",
|
||||||
|
"temp-format": "C",
|
||||||
|
"sleep-interval": 30
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
POST: /api/config/push
|
||||||
|
======================
|
||||||
|
|
||||||
|
Used to update push settings via an HTTP POST command. Payload is in JSON format.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": "ee1bfc",
|
||||||
|
"http-push": "http://192.168.1.50/ispindel",
|
||||||
|
"http-push2": "",
|
||||||
|
"brewfather-push": "",
|
||||||
|
"influxdb2-push": "http://192.168.1.50:8086",
|
||||||
|
"influxdb2-org": "Qwerty",
|
||||||
|
"influxdb2-bucket": "Qwerty",
|
||||||
|
"influxdb2-auth": "Qwerty"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
POST: /api/config/gravity
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Used to update gravity settings via an HTTP POST command. Payload is in JSON format.
|
||||||
|
|
||||||
|
* ``gravity-formula`` keywords ``temp`` and ``tilt`` are supported.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": "ee1bfc",
|
||||||
|
"gravity-formula": "0.0*tilt^3+0.0*tilt^2+0.0017978*tilt+0.9436",
|
||||||
|
"gravity-temp-adjustment": "off"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
POST: /api/config/gravity
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Used to update hardware settings via an HTTP POST command. Payload is in JSON format.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": "ee1bfc",
|
||||||
|
"voltage-factor": 1.59,
|
||||||
|
"temp-adjustment": 0,
|
||||||
|
"ota-url": "http://192.168.1.50/firmware/gravmon/"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
POST: /api/config/formula
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Used to update formula calculation data via an HTTP POST command. Payload is in JSON format.
|
||||||
|
|
||||||
|
* ``a1``-``a4`` are the angles/tilt readings (up to 5 are currently supported)
|
||||||
|
* ``g1``-``g4`` are the corresponding gravity reaadings (in SG)
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": "ee1bfc",
|
||||||
|
"a1": 22.4,
|
||||||
|
"a2": 54.4,
|
||||||
|
"a3": 58,
|
||||||
|
"a4": 0,
|
||||||
|
"a5": 0,
|
||||||
|
"g1": 1.000,
|
||||||
|
"g2": 1.053,
|
||||||
|
"g3": 1.062,
|
||||||
|
"g4": 1,
|
||||||
|
"g5": 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
present or the API call will fail.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
host = "192.168.1.1" # IP adress (or name) of the device to send these settings to
|
||||||
|
id = "ee1bfc" # Device ID (shown in serial console during startup or in UI)
|
||||||
|
|
||||||
|
def set_config( url, json ):
|
||||||
|
headers = { "ContentType": "application/json" }
|
||||||
|
print( url )
|
||||||
|
resp = requests.post( url, headers=headers, data=json )
|
||||||
|
if resp.status_code != 200 :
|
||||||
|
print ( "Failed " )
|
||||||
|
else :
|
||||||
|
print ( "Success " )
|
||||||
|
|
||||||
|
url = "http://" + host + "/api/config/device"
|
||||||
|
json = { "id": id,
|
||||||
|
"mdns": "gravmon", # Name of the device
|
||||||
|
"temp-format": "C", # Temperature format C or F
|
||||||
|
"sleep-interval": 30 # Sleep interval in seconds
|
||||||
|
}
|
||||||
|
set_config( url, json )
|
||||||
|
|
||||||
|
url = "http://" + host + "/api/config/push"
|
||||||
|
json = { "id": id,
|
||||||
|
"http-push": "http://192.168.1.1/ispindel", # HTTP endpoint
|
||||||
|
"http-push2": "", # HTTP endpoint2
|
||||||
|
"brewfather-push": "", # Brewfather URL
|
||||||
|
"influxdb2-push": "", # InfluxDB2 settings
|
||||||
|
"influxdb2-org": "",
|
||||||
|
"influxdb2-bucket": "",
|
||||||
|
"influxdb2-auth": ""
|
||||||
|
}
|
||||||
|
set_config( url, json )
|
||||||
|
|
||||||
|
url = "http://" + host + "/api/config/gravity"
|
||||||
|
json = { "id": id,
|
||||||
|
"gravity-formula": "", # If you want to set the gravity formula
|
||||||
|
"gravity-temp-adjustment": "off" # on or off
|
||||||
|
}
|
||||||
|
set_config( url, json )
|
||||||
|
|
||||||
|
url = "http://" + host + "/api/config/hardware"
|
||||||
|
json = { "id": id,
|
||||||
|
"voltage-factor": 1.59, # Default value for voltage calculation
|
||||||
|
"temp-adjustment": 0, # If temp sensor needs to be corrected
|
||||||
|
"ota-url": "" # if the device should seach for a new update when active
|
||||||
|
}
|
||||||
|
set_config( url, json )
|
||||||
|
|
||||||
|
url = "http://" + host + "/api/formula"
|
||||||
|
json = { "id": id,
|
||||||
|
"a1": 22.4,
|
||||||
|
"a2": 54.4,
|
||||||
|
"a3": 58,
|
||||||
|
"a4": 0,
|
||||||
|
"a5": 0,
|
||||||
|
"g1": 1.000,
|
||||||
|
"g2": 1.053,
|
||||||
|
"g3": 1.062,
|
||||||
|
"g4": 1,
|
||||||
|
"g5": 1
|
||||||
|
}
|
||||||
|
set_config( url, json )
|
||||||
|
|
||||||
|
|
||||||
|
.. _data-formats:
|
||||||
|
|
||||||
|
Data Formats
|
||||||
|
############
|
||||||
|
|
||||||
|
iSpindle format
|
||||||
|
===============
|
||||||
|
|
||||||
|
This is the format used for standard http posts.
|
||||||
|
|
||||||
|
* ``corr-gravity`` is an extended parameter containing a temperature corrected gravity reading.
|
||||||
|
* ``run-time`` is an extended parameter containing the number of seconds the execution took.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"name" : "gravmon",
|
||||||
|
"ID": "2E6753",
|
||||||
|
"token" : "gravmon",
|
||||||
|
"interval": 900,
|
||||||
|
"temperature": 20.5,
|
||||||
|
"temp-units": "C",
|
||||||
|
"gravity": 1.0050,
|
||||||
|
"corr-gravity": 1.0050,
|
||||||
|
"angle": 45.34,
|
||||||
|
"battery": 3.67,
|
||||||
|
"rssi": -12,
|
||||||
|
"run-time": 6
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Brewfather format
|
||||||
|
=================
|
||||||
|
|
||||||
|
This is the format for Brewfather
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"name" : "gravmon",
|
||||||
|
"temp": 20.5,
|
||||||
|
"temp-unit": "C",
|
||||||
|
"battery": 3.67,
|
||||||
|
"gravity": 1.0050,
|
||||||
|
"gravity_unit": "G",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Influx DB v2
|
||||||
|
============
|
||||||
|
|
||||||
|
This is the format for InfluxDB v2
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
measurement,host=<mdns>,device=<id>,temp-format=<C|F>,gravity-format=SG,gravity=1.0004,corr-gravity=1.0004,angle=45.45,temp=20.1,battery=3.96,rssi=-18
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
they can be uploaded manually afterwards.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"project":"gravmon",
|
||||||
|
"version":"0.4.10",
|
||||||
|
"html": [
|
||||||
|
"index.min.htm",
|
||||||
|
"device.min.htm",
|
||||||
|
"config.min.htm",
|
||||||
|
"calibration.min.htm",
|
||||||
|
"about.min.htm"
|
||||||
|
]
|
||||||
|
}
|
5
src_docs/source/contributing.rst
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Contributing
|
||||||
|
------------
|
||||||
|
|
||||||
|
This section is under construction.
|
||||||
|
|
102
src_docs/source/functionallity.rst
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
.. _functionallity:
|
||||||
|
|
||||||
|
Functionallity
|
||||||
|
==============
|
||||||
|
|
||||||
|
The main differences
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
* **Operates in two modes gravity monitoring and configuration mode**
|
||||||
|
|
||||||
|
In ``gravity monitoring`` mode it behaves just like the iSpindle, it wakes up at regular intervals, measures angle/tile, temperature, calculates gravity and pushes the data to defined endpoints.
|
||||||
|
|
||||||
|
In ``configuration mode`` the device is always active and the webserver is active. Here you can view the angle/tilt values, change configuration options and more. When in this mode you can also interact with the device
|
||||||
|
via an REST API so data can be pushed to the device via scripts (see API section for more information).
|
||||||
|
|
||||||
|
You can force the device into ``configuration mode`` while measuring gravity. This is useful when calibrating the device so you don't needs to wait for the device to wake up and push the data. The entire calibration
|
||||||
|
sequence can be handled via the web interface without need for additional software tools.
|
||||||
|
|
||||||
|
See the :ref:`setting-up-device` section for more information on how to trigger the configuration mode.
|
||||||
|
|
||||||
|
* **Can send data to multiple endpoints at once**
|
||||||
|
|
||||||
|
The original iSpindle can only have one destination, this software will push data to all defined endpoints so in theory you can use them all. However this will consume a lot of battery power so use only as many as needed.
|
||||||
|
|
||||||
|
Currently the device supports the following endpoints: http (2 different), influxdb2 and Brewfather
|
||||||
|
|
||||||
|
If you want additional targets please raise a feature request in the github repo.
|
||||||
|
|
||||||
|
* **Build in function to create gravity formulas, so no need for using other tools for this part**
|
||||||
|
|
||||||
|
Another big difference is that this software can create the gravity formula in the device, just enter the angle/gravity data that you have collected. You will also see a graph simulating how the formula would work.
|
||||||
|
|
||||||
|
* **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 feature that can correct the gravity as a second step making this independant of the formula.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This feature needs more testing to be validated.
|
||||||
|
|
||||||
|
* **OTA support from local webserver**
|
||||||
|
|
||||||
|
When starting up in configuration mode the device will check for a software update from a local webserver.
|
||||||
|
|
||||||
|
* **DS18B20 temperature adjustments**
|
||||||
|
|
||||||
|
You can adjust the temperature reading of the temperature sensor.
|
||||||
|
|
||||||
|
* **Gyro Movement**
|
||||||
|
|
||||||
|
The software will detect if the gyro is moving and if this is the case it will go back to sleep for 60seconds. This way we should avoid faulty measurements.
|
||||||
|
|
||||||
|
Other features
|
||||||
|
--------------
|
||||||
|
|
||||||
|
* Support for Celcius and Farenheigt as temperature formats.
|
||||||
|
|
||||||
|
* Support SG (Plato is not yet supported)
|
||||||
|
|
||||||
|
* Gyro data is read 50 times to ensure good accuracy
|
||||||
|
|
||||||
|
Experimental features
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. tip::
|
||||||
|
These are not enabled by default. To enable them you need to recompile the code and enable the correct defines.
|
||||||
|
|
||||||
|
* Use the temperature sensor in the gyro instead of DS18B20
|
||||||
|
|
||||||
|
This works fine when the device has time to cool down between measurements and it saves a few milliseconds (reduced battery consumption). My testing shows that this is quite accurate.
|
||||||
|
There is lots of battery power to save, reading the temp sensor takes almost as long as the gyro. This could reduce the run time by 40-50% and probly extend battery life with the same.
|
||||||
|
However more testing is required. Might add this as an option in the UI.
|
||||||
|
|
||||||
|
* Performance measurements
|
||||||
|
|
||||||
|
I've also create a small library to measure execution code in some areas of the code that i know is time consuming. This way I can find a good balance between performace and quality.
|
||||||
|
|
||||||
|
See the :ref:`compiling-the-software` for more information.
|
||||||
|
|
||||||
|
|
||||||
|
Battery life
|
||||||
|
------------
|
||||||
|
|
||||||
|
I'm currently measuring battery life of v0.5 but previous versions have been able to measure gravity for a 2-3 weeks without issues. Using 900 seconds as interval.
|
||||||
|
|
||||||
|
*More on this topics once my tests are done*
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
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.
|
||||||
|
|
||||||
|
.. image:: images/perf1.png
|
||||||
|
:width: 800
|
||||||
|
:alt: Performance view
|
BIN
src_docs/source/images/config1.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
src_docs/source/images/config2.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
src_docs/source/images/config3.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
src_docs/source/images/config4.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
src_docs/source/images/device.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
src_docs/source/images/formula1.png
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
src_docs/source/images/formula2.png
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
src_docs/source/images/index.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
src_docs/source/images/perf1.png
Normal file
After Width: | Height: | Size: 85 KiB |
BIN
src_docs/source/images/putty.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
src_docs/source/images/serial.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
src_docs/source/images/wifi.png
Normal file
After Width: | Height: | Size: 16 KiB |
121
src_docs/source/index.rst
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
.. GravityMon documentation master file, created by
|
||||||
|
sphinx-quickstart on Wed Jan 5 22:46:42 2022.
|
||||||
|
You can adapt this file completely to your liking, but it should at least
|
||||||
|
contain the root `toctree` directive.
|
||||||
|
|
||||||
|
Welcome to GravityMon's documentation!
|
||||||
|
######################################
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
This documentation reflects **v0.5**. Last updated 2022-01-09
|
||||||
|
|
||||||
|
|
||||||
|
GravityMon is a replacement firmare for the iSpindle firmware, it uses the same hardware configuration so
|
||||||
|
you can easily switch between them. It's used to measure gravity in beer and show the progress of fermentation.
|
||||||
|
|
||||||
|
For more information on this topic and function please visit `iSpindel Homepage <https://www.ispindel.de>`_ .
|
||||||
|
|
||||||
|
I started GravityMon because i like to create software and wanted to do some low level programming. I had done a few
|
||||||
|
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.
|
||||||
|
|
||||||
|
My approach to this software is a little different from that the original ispindle firmware. The github repository can
|
||||||
|
be found here; `GravityMon on Github <https://github.com/mp-se/gravitymon>`_
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
This software is in the early stages even though its more than one year old so if you find issues, please
|
||||||
|
open a ticket on github.
|
||||||
|
|
||||||
|
I dont take responsibility for any errors that can cause problems with the use. I have tested v0.4 on 5+ brews
|
||||||
|
over the last 6 months without any issues.
|
||||||
|
|
||||||
|
The main differences:
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
* Operates in two modes ``gravity monitoring`` and ``configuration mode``
|
||||||
|
* Send data to multiple endpoints when pushing data.
|
||||||
|
* Automatic temperature adjustment of gravity reading
|
||||||
|
* OTA support from local webserver
|
||||||
|
* Build in function to create gravity formulas
|
||||||
|
|
||||||
|
There are also a experimental features such as:
|
||||||
|
|
||||||
|
* Using the temperature sensor in gyro instead of DS18B20 (faster)
|
||||||
|
* Performance measurements (used to optimise code)
|
||||||
|
|
||||||
|
For a complete breakdown see the :ref:`functionallity`
|
||||||
|
|
||||||
|
Credits to
|
||||||
|
----------
|
||||||
|
Ideas to some of these functions have been picked up from disucssions in the iSpindle forums. This software uses
|
||||||
|
the following libraries and without these this would have been much more difficult to acheive:
|
||||||
|
|
||||||
|
* https://github.com/jrowberg/i2cdevlib.git
|
||||||
|
|
||||||
|
This library contains the basic code to interact with the gyro + many more chips.
|
||||||
|
|
||||||
|
* https://github.com/codeplea/tinyexpr
|
||||||
|
|
||||||
|
Proccess the gravity formula and calculate the gravity and various corrections.
|
||||||
|
|
||||||
|
* https://github.com/graphitemaster/incbin
|
||||||
|
|
||||||
|
Include binary files into the code, used to service html files.
|
||||||
|
|
||||||
|
* https://github.com/khoih-prog/ESP_DoubleResetDetector
|
||||||
|
|
||||||
|
Can detect if the reset button is pressed twice, is used to enter WIFI config mode.
|
||||||
|
|
||||||
|
* https://github.com/tzapu/WiFiManager
|
||||||
|
|
||||||
|
Configure wifi settings.
|
||||||
|
|
||||||
|
* https://github.com/thijse/Arduino-Log
|
||||||
|
|
||||||
|
Logging library for handling different loglevels and configure what sent over the serial.
|
||||||
|
|
||||||
|
* https://github.com/bblanchon/ArduinoJson
|
||||||
|
|
||||||
|
Json parser/creator used in configuration files and API's
|
||||||
|
|
||||||
|
* https://github.com/PaulStoffregen/OneWire
|
||||||
|
|
||||||
|
Communication library used for interacting with temperature sensor.
|
||||||
|
|
||||||
|
* https://github.com/milesburton/Arduino-Temperature-Control-Library
|
||||||
|
|
||||||
|
Interaction with the DS18B20 sensor
|
||||||
|
|
||||||
|
* https://github.com/Rotario/arduinoCurveFitting
|
||||||
|
|
||||||
|
Create the gravity formula.
|
||||||
|
|
||||||
|
* https://graphjs.com/
|
||||||
|
|
||||||
|
Render the graphs in the UI.
|
||||||
|
|
||||||
|
* https://getbootstrap.com/
|
||||||
|
|
||||||
|
CSS templates for the web page.
|
||||||
|
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:caption: Contents:
|
||||||
|
|
||||||
|
license
|
||||||
|
releases
|
||||||
|
functionallity
|
||||||
|
installation
|
||||||
|
configuration
|
||||||
|
compiling
|
||||||
|
contributing
|
||||||
|
backlog
|
||||||
|
|
||||||
|
Indices and tables
|
||||||
|
==================
|
||||||
|
|
||||||
|
* :ref:`genindex`
|
||||||
|
* :ref:`modindex`
|
||||||
|
* :ref:`search`
|
69
src_docs/source/installation.rst
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
Official esptool
|
||||||
|
================
|
||||||
|
|
||||||
|
The prefered option for flashing esp8266 device is via the official esptool. Documentation can be found
|
||||||
|
here; `esptool home page <https://docs.espressif.com/projects/esptool/en/latest/esp32/>`_
|
||||||
|
|
||||||
|
Windows 10 should install a driver for the USB -> Serial automatically when you connect a esp8266.
|
||||||
|
|
||||||
|
Flashing on windows
|
||||||
|
*******************
|
||||||
|
|
||||||
|
The basic command for flashing on Windows is;
|
||||||
|
|
||||||
|
``esptool.py --port COM4 write_flash 0x0 firmware.bin``
|
||||||
|
|
||||||
|
If there are issues you can try do erase the flash first using this command;
|
||||||
|
|
||||||
|
``esptool.py --port COM4 erase_flash``
|
||||||
|
|
||||||
|
Serial Monitoring
|
||||||
|
*******************
|
||||||
|
|
||||||
|
To check output from the device (logs) there are several tools out there. I found this simple tool in the Windows Store called ``Serial Port Monitoring``.
|
||||||
|
Just select a baud rate of 115200, 8N1.
|
||||||
|
|
||||||
|
.. image:: images/serial.png
|
||||||
|
:width: 800
|
||||||
|
:alt: Serial output
|
||||||
|
|
||||||
|
|
||||||
|
Binaries
|
||||||
|
********
|
||||||
|
|
||||||
|
In the /bin directory you will find 2 different firmware builds;
|
||||||
|
|
||||||
|
* **firmware.bin**
|
||||||
|
|
||||||
|
This is the standard release build (prefered version)
|
||||||
|
|
||||||
|
* **firmware-perf.bin**
|
||||||
|
|
||||||
|
This version also submits performance data to an influx database with detailed execution times.
|
||||||
|
|
||||||
|
|
||||||
|
In these versions all the html files are embedded in the binaries. The file system is currently only used for storing
|
||||||
|
the configuration file.
|
||||||
|
|
||||||
|
If the software becomes so large the html files can be moved to the file system, but this is not enabled by
|
||||||
|
default (see compiling for details). This approach makes installation much easier and ensure that html files
|
||||||
|
and code is in sync.
|
||||||
|
|
||||||
|
|
||||||
|
Configuring WIFI
|
||||||
|
================
|
||||||
|
|
||||||
|
When the device is flashed it will need to have WIFI configuration in order to work. If you have used other software on
|
||||||
|
the device its possible that wifi settings exist.
|
||||||
|
|
||||||
|
If this is not configured in the device it will create an wirless access point called `GravMon`. Connect to this AP and
|
||||||
|
enter the SSID and password you want to use. If the web page dont open automatically you can enter the following adress
|
||||||
|
in the browser: **http://192.168.4.1**
|
||||||
|
|
||||||
|
.. image:: images/wifi.png
|
||||||
|
:width: 200
|
||||||
|
:alt: Wifi page
|
||||||
|
|
@ -1,7 +1,11 @@
|
|||||||
/*
|
.. _licence:
|
||||||
|
|
||||||
|
Licence
|
||||||
|
#######
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 Magnus
|
Copyright (c) 2021-22 Magnus
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -20,31 +24,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
|
||||||
#ifndef _WEBSERVER_H
|
|
||||||
#define _WEBSERVER_H
|
|
||||||
|
|
||||||
// Include
|
|
||||||
|
|
||||||
// classes
|
|
||||||
class WebServer {
|
|
||||||
public:
|
|
||||||
enum HtmlFile {
|
|
||||||
HTML_INDEX = 0,
|
|
||||||
HTML_DEVICE = 1,
|
|
||||||
HTML_CONFIG = 2,
|
|
||||||
HTML_ABOUT = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
bool setupWebServer();
|
|
||||||
void loop();
|
|
||||||
bool checkHtmlFile( HtmlFile item );
|
|
||||||
const char* getHtmlFileName( HtmlFile item );
|
|
||||||
};
|
|
||||||
|
|
||||||
// Global instance created
|
|
||||||
extern WebServer myWebServer;
|
|
||||||
|
|
||||||
#endif // _WEBSERVER_H
|
|
||||||
|
|
||||||
// EOF
|
|
24
src_docs/source/releases.rst
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
.. _releases:
|
||||||
|
|
||||||
|
Releases
|
||||||
|
########
|
||||||
|
|
||||||
|
v0.5.0 (beta)
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Latest next target version is: **v0.5.0**. This is hosted in the **dev branch**.
|
||||||
|
|
||||||
|
* Added feature to calcuate formula on device
|
||||||
|
* Total rewrite of documentation
|
||||||
|
* WIFI settings are now stored in config file
|
||||||
|
* Defined version numbers for all dependant libraries to avoid updates breaking build.
|
||||||
|
* Cleanup of code
|
||||||
|
* Refactor code from C to C++
|
||||||
|
|
||||||
|
v0.4.0
|
||||||
|
------
|
||||||
|
|
||||||
|
Latest stable development version is: **v0.4.0**
|
||||||
|
|
||||||
|
`Release v0.4 on Github <https://github.com/mp-se/gravitymon/releases/tag/v0.4.0>`_
|
||||||
|
|
58
src_docs/source/styling.rst
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
Testing formatting
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Header 1
|
||||||
|
########
|
||||||
|
|
||||||
|
Header 2
|
||||||
|
--------
|
||||||
|
|
||||||
|
Header 3
|
||||||
|
********
|
||||||
|
|
||||||
|
**BOLD**
|
||||||
|
|
||||||
|
*ITALIC*
|
||||||
|
|
||||||
|
``CODE SAMPLE``
|
||||||
|
|
||||||
|
1. List
|
||||||
|
2. List
|
||||||
|
|
||||||
|
* List
|
||||||
|
* List
|
||||||
|
|
||||||
|
.. list-table:: Table
|
||||||
|
:widths: 40 60
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - table
|
||||||
|
- table
|
||||||
|
* - content
|
||||||
|
- content
|
||||||
|
|
||||||
|
.. image:: images/formula1.png
|
||||||
|
:width: 400
|
||||||
|
:alt: image
|
||||||
|
|
||||||
|
.. code-block:: objdump
|
||||||
|
|
||||||
|
measurement,host=<mdns>,device=<id>,temp-format=<C|F>,gravity-format=SG,gravity=1.0004,corr-gravity=1.0004,angle=45.45,temp=20.1,battery=3.96,rssi=-18
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"project":"gravmon",
|
||||||
|
"version":"0.4.10",
|
||||||
|
"html": [
|
||||||
|
"index.min.htm",
|
||||||
|
"device.min.htm",
|
||||||
|
"config.min.htm",
|
||||||
|
"calibration.min.htm",
|
||||||
|
"about.min.htm"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Note...
|
81
test/configure.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
host = "192.168.1.1" # IP adress (or name) of the device to send these settings to
|
||||||
|
id = "ee1bfc" # Device ID (shown in serial console during startup or in UI)
|
||||||
|
|
||||||
|
def set_config( url, json ):
|
||||||
|
headers = { "ContentType": "application/json" }
|
||||||
|
print( url )
|
||||||
|
#print( json )
|
||||||
|
resp = requests.post( url, headers=headers, data=json )
|
||||||
|
if resp.status_code != 200 :
|
||||||
|
print ( "Failed " )
|
||||||
|
else :
|
||||||
|
print ( "Success " )
|
||||||
|
|
||||||
|
#
|
||||||
|
# Configure the device settings.
|
||||||
|
#
|
||||||
|
url = "http://" + host + "/api/config/device"
|
||||||
|
json = { "id": id,
|
||||||
|
"mdns": "gravmon", # Name of the device
|
||||||
|
"temp-format": "C", # Temperature format C or F
|
||||||
|
"sleep-interval": 30 # Sleep interval in seconds
|
||||||
|
}
|
||||||
|
set_config( url, json )
|
||||||
|
|
||||||
|
#
|
||||||
|
# Configure the push settings. Blank means that its no used.
|
||||||
|
#
|
||||||
|
url = "http://" + host + "/api/config/push"
|
||||||
|
json = { "id": id,
|
||||||
|
"http-push": "http://192.168.1.1/ispindel", # HTTP endpoint
|
||||||
|
"http-push2": "", # HTTP endpoint2
|
||||||
|
"brewfather-push": "", # Brewfather URL
|
||||||
|
"influxdb2-push": "", # InfluxDB2 settings
|
||||||
|
"influxdb2-org": "",
|
||||||
|
"influxdb2-bucket": "",
|
||||||
|
"influxdb2-auth": ""
|
||||||
|
}
|
||||||
|
set_config( url, json )
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Configure the gravity settings.
|
||||||
|
#
|
||||||
|
url = "http://" + host + "/api/config/gravity"
|
||||||
|
json = { "id": id,
|
||||||
|
"gravity-formula": "", # If you want to set the gravity formula
|
||||||
|
"gravity-temp-adjustment": "off" # on or off
|
||||||
|
}
|
||||||
|
set_config( url, json )
|
||||||
|
|
||||||
|
#
|
||||||
|
# Configure the hardware settings.
|
||||||
|
#
|
||||||
|
url = "http://" + host + "/api/config/hardware"
|
||||||
|
json = { "id": id,
|
||||||
|
"voltage-factor": 1.59, # Default value for voltage calculation
|
||||||
|
"temp-adjustment": 0, # If temp sensor needs to be corrected
|
||||||
|
"ota-url": "" # if the device should seach for a new update when active
|
||||||
|
}
|
||||||
|
set_config( url, json )
|
||||||
|
|
||||||
|
#
|
||||||
|
# Configure the gravity formula settings. If this is called the formula will be updated based on these measurements (zero angle values will be ignored)
|
||||||
|
#
|
||||||
|
url = "http://" + host + "/api/formula"
|
||||||
|
json = { "id": id,
|
||||||
|
"a1": 22.4,
|
||||||
|
"a2": 54.4,
|
||||||
|
"a3": 58,
|
||||||
|
"a4": 0,
|
||||||
|
"a5": 0,
|
||||||
|
"g1": 1.000,
|
||||||
|
"g2": 1.053,
|
||||||
|
"g3": 1.062,
|
||||||
|
"g4": 1,
|
||||||
|
"g5": 1
|
||||||
|
}
|
||||||
|
set_config( url, json )
|
14
test/formula.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"gravity-formula": "0.00000166*tilt^3+-0.00024799*tilt^2+0.01344400*tilt+0.79358248",
|
||||||
|
"angle": 45,
|
||||||
|
"a1": 25,
|
||||||
|
"a2": 35,
|
||||||
|
"a3": 45,
|
||||||
|
"a4": 55,
|
||||||
|
"a5": 65,
|
||||||
|
"g1": 1.000,
|
||||||
|
"g2": 1.010,
|
||||||
|
"g3": 1.025,
|
||||||
|
"g4": 1.040,
|
||||||
|
"g5": 1.060
|
||||||
|
}
|
@ -2,5 +2,6 @@
|
|||||||
"index": false,
|
"index": false,
|
||||||
"device": false,
|
"device": false,
|
||||||
"config": false,
|
"config": false,
|
||||||
|
"calibration": false,
|
||||||
"about": true
|
"about": true
|
||||||
}
|
}
|