Compare commits

..

No commits in common. "master" and "v1.2.0-beta2" have entirely different histories.

48 changed files with 113 additions and 1553 deletions

View File

@ -36,7 +36,7 @@ jobs:
git config --global advice.detachedHead false
- name: Run PlatformIO
run: pio run -e gravity-release -e gravity32-release -e gravity32c3-release -e gravity32s2-release -e gravity32c3v1-release -e gravity32lite-release
run: pio run -e gravity-release -e gravity32-release -e gravity32c3-release -e gravity32s2-release
- uses: EndBug/add-and-commit@v9 # You can change this to use a specific version. https://github.com/marketplace/actions/add-commit
with:

View File

@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- uses: actions/setup-python@v4
- name: clang format support
run: |
sudo apt install clang-format cppcheck

2
.gitignore vendored
View File

@ -2,5 +2,3 @@
.vscode/*
*.map
src_docs/_build/*
.env/*
*.pyc

View File

@ -3,14 +3,14 @@
![release](https://img.shields.io/github/v/release/mp-se/gravitymon?label=latest%20release)
![issues](https://img.shields.io/github/issues/mp-se/gravitymon)
![pr](https://img.shields.io/github/issues-pr/mp-se/gravitymon)
![dev_build](https://img.shields.io/github/actions/workflow/status/mp-se/gravitymon/pio-build.yaml?branch=dev)
![doc_build](https://img.shields.io/github/actions/workflow/status/mp-se/gravitymon/doc-build.yaml?branch=master)
![dev_build](https://img.shields.io/github/workflow/status/mp-se/gravitymon/PlatformIO%20CI/dev?label=dev%20build)
![doc_build](https://img.shields.io/github/workflow/status/mp-se/gravitymon/Sphinx%20Build/dev?label=doc%20build)
# Gravity Monitor for Beer Brewing
![GravityMon Logo](src_docs/source/images/gravitymon_logo_s.png)
GravityMon is a replacement firmware for the iSpindle firmware. It's 100% compatible with the iSpindle hardware design so it does not require any hardware changes. From v1.2 you can also use GravityMon for the DIY Floaty Hardware with ESP32 Lite.
GravityMon is a replacement firmware for the iSpindle firmware. It's 100% compatible with the iSpindle hardware design so it does not require any hardware changes.
Now also works with ESP32 d1 mini, ESP32 c3 mini, ESP32 S2 mini which both are pin compatible with ESP8266.
@ -28,5 +28,4 @@ If you want to support my work you can do that through these options
Thanks to the following persons for supporting me and this project:
* David Conde, @davidconde
* Lars H.
* David Conde

31
TEST.md
View File

@ -1,31 +0,0 @@
# Unit testing - Python Script
I have moved my test scripts into this project now. They are mainly based on python scrips and validate the features from the API's.
Create a virtual environment and install the needed dependecies
```
python -m venv .env
pip install -r requirements.txt
```
Before you runt the script you need to update the IP adress to match the device that you have on your network.
Running the ALL tests
```
cd src/test
python3 -m unittest -v apitests.py -v
```
Running the ONE test
```
cd src/test
python3 -m unittest -v apitests.API.test_status -v
```
# Unit testing - Specific build
I've added a specific build that uses the AUnit (https://github.com/bxparks/AUnit) testing framework so that we can test functions or classes on the device itself. I hope this will simplify the release and testing cycle.
1. Select the build target (gravity-unit)
2. Build/upload the code to an iSpindle device.
3. Check the output from the serial console.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1 +1 @@
{ "project":"gravmon", "version":"1.2.1", "html": [ ] }
{ "project":"gravmon", "version":"1.2.0", "html": [ ] }

View File

@ -468,7 +468,7 @@
</div>
</div>
<div class="row mb-3" id="ota-hide">
<div class="row mb-3">
<label for="ota-url" class="col-sm-2 col-form-label">OTA base URL</label>
<div class="col-sm-8">
<input type="url" maxlength="90" class="form-control" name="ota-url" id="ota-url" placeholder="http://www.local.com/path/" data-bs-toggle="tooltip" title="Base URL to where firmware and version.json file can be found. Needs to end with '/', example: http://www.mysite.com/firmware/">
@ -507,9 +507,9 @@
if ( b ) {
$("#ota-url").val(gravitymonUrl);
$('#ota-hide').hide();
$("#ota-url").prop("disabled", true);
} else {
$('#ota-hide').show();
$("#ota-url").prop("disabled", false);
}
});
</script>
@ -966,7 +966,7 @@
if( cfg["ota-url"] == gravitymonUrl) {
$("#gravitymon-com").prop( "checked", true );
$("#ota-hide").hide();
$("#ota-url").prop("disabled", true);
}
$("#token").val(cfg["token"]);

File diff suppressed because one or more lines are too long

View File

@ -39,21 +39,21 @@ build_flags =
-DUSE_LITTLEFS=true
-DUSER_SSID=\""\"" # =\""myssid\""
-DUSER_SSID_PWD=\""\"" # =\""mypwd\""
-DCFG_APPVER="\"1.2.1\""
#-DCFG_GITREV=\""beta-3\""
!python script/git_rev.py
-DCFG_APPVER="\"1.2.0\""
-DCFG_GITREV=\""beta-2\""
#!python script/git_rev.py
lib_deps =
# Using local copy of these libraries
# https://github.com/mp-se/i2cdevlib.git#<document>
# https://github.com/mp-se/i2cdevlib.git#<document>
# https://github.com/mp-se/OneWire
# https://github.com/mp-se/Arduino-Temperature-Control-Library
# https://github.com/khoih-prog/ESP_WiFiManager
# https://github.com/khoih-prog/ESP_DoubleResetDetector
https://github.com/mp-se/tinyexpr # https://github.com/codeplea/tinyexpr
https://github.com/mp-se/tinyexpr # https://github.com/codeplea/tinyexpr
https://github.com/mp-se/Arduino-Log#1.1.1 # https://github.com/thijse/Arduino-Log
https://github.com/mp-se/ArduinoJson#v6.18.5 # https://github.com/bblanchon/ArduinoJson
https://github.com/mp-se/arduinoCurveFitting#v1.0.6 # https://github.com/Rotario/arduinoCurveFitting
https://github.com/mp-se/arduino-mqtt#v2.5.0 # https://github.com/256dpi/arduino-mqtt
https://github.com/mp-se/ArduinoJson#v6.18.5 # https://github.com/bblanchon/ArduinoJson
https://github.com/mp-se/arduinoCurveFitting#v1.0.6 # https://github.com/Rotario/arduinoCurveFitting
https://github.com/mp-se/arduino-mqtt#v2.5.0 # https://github.com/256dpi/arduino-mqtt
lib_deps32 =
https://github.com/mp-se/NimBLE-Arduino#1.3.8 # https://github.com/h2zero/NimBLE-Arduino
extra_scripts =
@ -102,30 +102,7 @@ lib_deps =
${common_env_data.lib_deps}
board = ${common_env_data.board}
build_type = release
#build_type = debug
board_build.filesystem = littlefs
build_src_filter = +<*> -<../test/tests*.cpp>
monitor_filters = esp8266_exception_decoder
[env:gravity-unit]
upload_speed = ${common_env_data.upload_speed}
monitor_speed = ${common_env_data.monitor_speed}
framework = ${common_env_data.framework}
platform = ${common_env_data.platform}
extra_scripts = ${common_env_data.extra_scripts}
build_unflags = ${common_env_data.build_unflags}
build_flags =
${common_env_data.build_flags}
-D LOG_LEVEL=4
lib_deps =
https://github.com/mp-se/incbin # https://github.com/graphitemaster/incbin
https://github.com/bxparks/AUnit#v1.6.1
${common_env_data.lib_deps}
board = ${common_env_data.board}
build_type = debug
board_build.filesystem = littlefs
build_src_filter = +<*> -<main.cpp> +<../test/tests*.cpp>
monitor_filters = esp8266_exception_decoder
[env:gravity32-release]
framework = ${common_env_data.framework}
@ -166,39 +143,6 @@ platform = ${common_env_data.platform32}
upload_speed = ${common_env_data.upload_speed}
monitor_speed = ${common_env_data.monitor_speed}
extra_scripts = ${common_env_data.extra_scripts}
build_unflags =
${common_env_data.build_unflags}
build_flags =
-Wl,-Map,output.map
${common_env_data.build_flags}
-DLOG_LEVEL=5
-DCORE_DEBUG_LEVEL=0
-DESP32C3
-DARDUINO_ESP32C3_DEV
#-DUSE_SERIAL_PINS # Use the TX/RX pins for the serial port
lib_deps =
${common_env_data.lib_deps}
${common_env_data.lib_deps32}
lib_ignore =
board = lolin_c3_mini
build_type = release
board_build.partitions = part32.csv
board_build.filesystem = littlefs
board_build.embed_txtfiles =
html/calibration.min.htm
html/config.min.htm
html/firmware.min.htm
html/format.min.htm
html/about.min.htm
html/index.min.htm
html/test.min.htm
[env:gravity32c3v1-release]
framework = ${common_env_data.framework}
platform = ${common_env_data.platform32}
upload_speed = ${common_env_data.upload_speed}
monitor_speed = ${common_env_data.monitor_speed}
extra_scripts = ${common_env_data.extra_scripts}
build_unflags =
${common_env_data.build_unflags}
build_flags =

View File

@ -1 +0,0 @@
requests

View File

@ -45,12 +45,6 @@ def after_build(source, target, env):
print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target )
if name == "gravity32c3v1-release" :
target = dir + "/bin/firmware32c3v1.bin"
source = dir + "/.pio/build/" + name + "/firmware.bin"
print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target )
if name == "gravity32s2-release" :
target = dir + "/bin/firmware32s2.bin"
source = dir + "/.pio/build/" + name + "/firmware.bin"

View File

@ -27,6 +27,9 @@ SOFTWARE.
#include <calc.hpp>
#include <main.hpp>
//
// Use values to derive a formula
//
int createFormula(RawFormulaData &fd, char *formulaBuffer,
int formulaBufferSize, int order) {
int noAngles = 0;
@ -115,6 +118,10 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer,
return ERR_FORMULA_INTERNAL;
}
//
// Calculates gravity according to supplied formula, compatible with
// iSpindle/Fermentrack formula
//
double calculateGravity(double angle, double temp, const char *tempFormula) {
const char *formula = myConfig.getGravityFormula();
@ -156,10 +163,12 @@ double calculateGravity(double angle, double temp, const char *tempFormula) {
return 0;
}
//
// Do a standard gravity temperature correction. This is a simple way to adjust
// for differnt worth temperatures. This function uses C as temperature.
//
// Source: https://homebrewacademy.com/hydrometer-temperature-correction/
//
double gravityTemperatureCorrectionC(double gravitySG, double tempC,
double calTempC) {
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)

View File

@ -133,6 +133,12 @@ void setup() {
LOG_PERF_START("main-setup");
runtimeMillis = millis();
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
// Add a delay so that serial is started.
// delay(3000);
#endif
delay(2000);
// Main startup
#if defined(ESP8266)
Log.notice(F("Main: Started setup for %s." CR),

View File

@ -80,6 +80,9 @@ const char mqttFormat[] PROGMEM =
"ispindel/${mdns}/interval:${sleep-interval}|"
"ispindel/${mdns}/RSSI:${rssi}|";
//
// Initialize the variables
//
void TemplatingEngine::initialize(float angle, float gravitySG,
float corrGravitySG, float tempC,
float runTime) {
@ -135,9 +138,10 @@ void TemplatingEngine::initialize(float angle, float gravitySG,
#endif
}
// the useDefaultTemplate param is there to support unit tests.
const char* TemplatingEngine::create(TemplatingEngine::Templates idx,
bool useDefaultTemplate) {
//
// Create the data using defined template.
//
const char* TemplatingEngine::create(TemplatingEngine::Templates idx) {
String fname;
_baseTemplate.reserve(600);
@ -165,16 +169,15 @@ const char* TemplatingEngine::create(TemplatingEngine::Templates idx,
break;
}
if (!useDefaultTemplate) {
File file = LittleFS.open(fname, "r");
if (file) {
char buf[file.size() + 1];
memset(&buf[0], 0, file.size() + 1);
file.readBytes(&buf[0], file.size());
_baseTemplate = String(&buf[0]);
file.close();
Log.notice(F("TPL : Template loaded from disk %s." CR), fname.c_str());
}
// TODO: Add code to load templates from disk if they exist.
File file = LittleFS.open(fname, "r");
if (file) {
char buf[file.size() + 1];
memset(&buf[0], 0, file.size() + 1);
file.readBytes(&buf[0], file.size());
_baseTemplate = String(&buf[0]);
file.close();
Log.notice(F("TPL : Template loaded from disk %s." CR), fname.c_str());
}
#if LOG_LEVEL == 6
@ -194,17 +197,4 @@ const char* TemplatingEngine::create(TemplatingEngine::Templates idx,
return "";
}
// added to support more unit test scenarios.
const char* TemplatingEngine::create(const char* formatTemplate) {
_baseTemplate = String(formatTemplate);
// Insert data into template.
transform();
_baseTemplate.clear();
if (_output) return _output;
return "";
}
// EOF

View File

@ -160,8 +160,7 @@ class TemplatingEngine {
}
}
}
// strncat(_output, format + k, size - k);
strncat(_output, format + k, strlen(format + k));
strncat(_output, format + k, size - k);
Log.notice(F("TPL : Transformed template %d chars to %d chars" CR),
strlen(format), strlen(_output));
@ -201,9 +200,7 @@ class TemplatingEngine {
}
void initialize(float angle, float gravitySG, float corrGravitySG,
float tempC, float runTime);
const char *create(TemplatingEngine::Templates idx,
bool useDefaultTemplate = false);
const char *create(const char *formatTemplate);
const char *create(TemplatingEngine::Templates idx);
};
#endif // SRC_TEMPLATING_HPP_

View File

@ -58,6 +58,9 @@ void TempSensor::setup() {
#endif
}
//
// Retrieving value from sensor, value is in Celcius
//
float TempSensor::getValue(bool useGyro) {
if (useGyro) {
// When using the gyro temperature only the first read value will be

View File

@ -430,8 +430,6 @@ bool WifiConnection::checkFirmwareVersion() {
}
http.end();
if (_newFirmware) Log.notice(F("WIFI: Found new version." CR));
#if LOG_LEVEL == 6
Log.verbose(F("WIFI: OTA found new version %s." CR),
_newFirmware ? "true" : "false");

View File

@ -10,7 +10,7 @@ To reduce the need for adding custom endpoints for various services there is an
.. warning::
If format templates are large this feature can be slow on a small device such as the esp8266.
Since the format templates can be big this function can be quite slow on a small device such as the esp8266.
.. image:: images/format.png
:width: 800

View File

@ -30,10 +30,9 @@ In the platformio config there are 3 targets defined
* gravity-debug; Maximum logging for trouble shooting, deep sleep is disabled.
* gravity-release; Standard release
* gravity32-release: Version for ESP32 mini.
* gravity32c3-release: Version for ESP32 C3 mini v2.1+.
* gravity32c3v1-release: Version for ESP32 C3 mini v1.0.
* gravity32s2-release: Version for ESP32 S2 mini.
* gravity32lite-release: Version for ESP32 lite (Floaty hardware).
* gravity32-c3-release: Version for ESP32 C3 mini.
* gravity32-s2-release: Version for ESP32 S2 mini.
* gravity32-lite-release: Version for ESP32 lite (Floaty hardware).
.. warning::
The debug target can be unstable and crash the device under certain circumstanses. Excessive logging to the serial port can cause corruption and crashes.

View File

@ -18,11 +18,11 @@
# -- Project information -----------------------------------------------------
project = 'GravityMon'
copyright = '2021-2023, Magnus Persson'
copyright = '2021-2022, Magnus Persson'
author = 'Magnus Persson'
# The full version, including alpha/beta/rc tags
release = '1.2.1'
release = '1.2.0'
# -- General configuration ---------------------------------------------------

View File

@ -165,7 +165,7 @@ Name of organisation in Influx.
* **Influx DB v2 Bucket:**
Token for bucket. Don't use the bucket name.
Identifier for bucket.
* **Influx DB v2 Token:**

View File

@ -3,11 +3,6 @@
Hardware
########
I'm not a hardware designer so I would recommend the following resources for more in depth information on this topic.
* `Cherry Philip Hardware design <https://github.com/cherryphilip74/iSpindel-PCB>`_
* `OpenSource Distilling <https://www.opensourcedistilling.com/ispindel>`_
iSpindle based on esp8266
=========================
@ -25,20 +20,24 @@ Schema for esp8266 build
:width: 700
:alt: Schema esp8266
iSpindle based on esp32
=======================
Gravitymon supports a number of ESP32 boards that offers bluetooth support.
I've experimented with porting my software version to esp32 and this is a selection of options i have been testing.
.. warning::
Work to support esp32s2 is still ongoing, ESP32-mini and ESP32c3-mini is confirmed to be working. However I would recommend the C3 variant.
.. image:: images/esp32_hardware.jpg
:width: 500
:alt: iSpindle esp32 hardware options
* esp32d1 mini, this was the first board i tried which is a smaller form factor of the first generetion esp32 with 2 cores. Slow on connecting to wifi is the main downside.
* esp32c3 mini, a newer version based on the latest risc v7 architecture, is seen as the replacement for the esp8266 with bluetooth support. Don't buy v1.0 since that has a faulty wifi antenna.
* esp32 mini, this was the first board i tried which is a smaller form factor of the first generetion esp32 with 2 cores. Slow on connecting to wifi is the main downside.
* esp32c3 mini, a newer version based on the latest risc v7 architecture, is seen as the replacement for the esp8266 with bluetooth support.
* esp32s2 mini, similar to the c3 board but without bluetooth support.
It's possible to use this PCB and mount an ESP32 mini on top of that (c3 or s2 are prefered). The esp32 d1 mini is a larger formfactor and can be hard to fit into the tube.
It's possible to use this PCB and mount an ESP32 mini on top of that (c3 and s2 are prefered). The esp32 mini is a larger formfactor and can be hard to fit into the tube.
.. note::
You need to add a resistor between A0 (Analog PIN) and ground of 470k. The reason is that the esp8266 has a build in resistor for a voltage divider
@ -48,7 +47,7 @@ It's possible to use this PCB and mount an ESP32 mini on top of that (c3 or s2 a
ESP32c3 mini
++++++++++++
This is model is fully supported by gravitymon.
This is model is now fully supported by gravitymon.
.. image:: images/ispindel_esp32c3.jpg
:width: 500
@ -63,7 +62,7 @@ Here is an image of where I added the resistor for the voltage divider.
ESP32s2 mini
++++++++++++
This is model is fully supported by gravitymon. Same setup as for ESP32C3 mini.
This is model is now fully supported by gravitymon. Same setup as for ESP32C3 mini.
ESP32 d1 mini
+++++++++++++
@ -78,7 +77,7 @@ Schema for esp32 build
++++++++++++++++++++++
.. note::
This schema assumes that an ESP32 d1 mini (pin compatible with ESP8266). The ESP32 has two rows of pins but
This schema assumes that an ESP32 D1 Mini (pin compatible with ESP8266 D1 Mini is used). The ESP32 has two rows of pins but
only the inner row is used. The main difference is the added resistor R3 so we get a voltage divider for measuring battery.
The ESP8266 has a built in resistor thats not visible on the schema and this acts as a voltage divider.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 78 KiB

View File

@ -7,7 +7,7 @@ Welcome to GravityMon
#####################
.. note::
This documentation reflects **v1.2.1**. Last updated 2023-02-03
This documentation reflects **v1.2 - beta 2**. Last updated 2022-11-19
What is GravityMon?
--------------------
@ -39,8 +39,8 @@ My approach to this software is a little different from that the original iSpind
The github repository can be found here; `GravityMon on Github <https://github.com/mp-se/gravitymon>`_
.. note::
I don't take responsibility for any errors or issues caused by the software or hardware.
The software is provided as-is. I will however try my best to fix issues that might occur.
I don't take responsibility for any errors or issues caused by the software. The software is provided as-is. I will however
try my best to fix issues that might occur.
I have tested this software on 40+ brews with good results.

View File

@ -45,8 +45,8 @@ In the /bin directory you will find 4 different firmware builds;
* **firmware32c3.bin**
This is the release build for an ESP32c3-mini variant v2.1 or newer. When flashing an ESP32 you also need the *partition32c3.bin* file that outlines the flash memory structure. Due to
the size of the firmware we are using a custom partition setup. If you have a v1.0 board use the firmware32c3v1.bin instead.
This is the release build for an ESP32c3-mini variant. When flashing an ESP32 you also need the *partition32c3.bin* file that outlines the flash memory structure. Due to
the size of the firmware we are using a custom partition setup.
* **firmware32s2.bin**
@ -97,7 +97,7 @@ OTA Option
You can use the OTA option by adding this URL to your configuration and when the device starts up in configuration mode it
will check for a new version and if it finds a newer version it will do an update.
``https://gravitymon.com/firmware/``
``https://mp-se.github.io/gravitymon/release/``
Manual update
*************

View File

@ -1,22 +1,6 @@
Q & A
#####
User interface does not render correctly
----------------------------------------
Since the user interface is built using bootstrap v5 the device requires access to the internet
to download required javascript and css files. Due to size it would not be possible to store these
on the device. Make sure the device can access: https://cdn.jsdelivr.net/npm/bootstrap
Data is not populated in the fields
------------------------------------
The user interface uses JQuery to fetch data from the device. This javascript library needs to be downloaded
from the internet. Due to size it would not be possible to store these on the device. Make sure the
device can access: https://code.jquery.com
Also ensure that any security tools does not block the execution of these features.
My device is no going in to sleep after fully charged
-----------------------------------------------------
- Calibrate the device in the web interface
@ -27,7 +11,7 @@ My device is no going in to sleep after fully charged
My device reports a temperature of -273C or -491F
-------------------------------------------------
- The DS18B20 temperature sensor cannot be found and this is the default value reported in this case.
- Check the orientation of the sensor and the soldering.
- Check the orientation of the sensor and soldering.
Calibration error (unable to find a valid formula)
--------------------------------------------------
@ -46,26 +30,18 @@ To fix these this you can;
In the case above this parameter was changed from 1.6 SG to 4 SG and the formula was accepted. The deviation on this point was just above 3 SG.
How can I filter data on influxdb without needing to know the time range
------------------------------------------------------------------------
User interface does not render correctly
----------------------------------------
You can use any of the available fields to enter your custom data. An option is to use the token fields to
add some custom information to identify your brew. This can then be used to filter your data in influxdb.
Since the user interface is built using bootstrap v5 the device requires access to the internet
to download required javascript and css files. Due to size it would not be possible to store these
on the device. Make sure the device can access: https://cdn.jsdelivr.net/npm/bootstrap
When you switch brews you need to go in and change the token to identify the brewing session.
Data is not populated in the fields
------------------------------------
Change the format template for the influx target to include the token field. Now you will have an
field called event that you can filter on in influx.
.. code-block::
gravity-format=${gravity-unit} gravity=${gravity},corr-gravity=${corr-gravity},
angle=${angle},temp=${temp},battery=${battery},rssi=${rssi}
to
.. code-block::
gravity-format=${gravity-unit} gravity=${gravity},corr-gravity=${corr-gravity},
angle=${angle},temp=${temp},battery=${battery},rssi=${rssi},event=${token}
The user interface uses JQuery to fetch data from the device. This javascript library needs to be downloaded
from the internet. Due to size it would not be possible to store these on the device. Make sure the
device can access: https://code.jquery.com
Also ensure that any security tools does not block the execution of these features.

View File

@ -3,47 +3,40 @@
Releases
########
v1.2.1
======
Issues adressed
++++++++++++++++
* BUG: Under some circumstances the last part of the format template was omitted.
Other
+++++
* Update tinyexpr library to latest baseline. For forumula evaluation.
Documentation
+++++++++++++
* Minor updates and corrections to the documetation.
* Updated Q&A section
v1.2.0
======
v1.2.0 - beta2
==============
Features
++++++++
* Added function to calcualate voltage factor based on measured value.
* Updated battery estimation for the various esp32 boards.
* Added support for the ESP32 lite board which is used in the Floaty Hydrometer variant.
* Added support for the ESP32 C3 mini board
* Added support for the ESP32 S2 mini board
* Serial output is written to TX/RX pins instead of the USB connection for the ESP32c3. This way the serial console can be viewed when running on battery power.
* Merged in unit tests and api tests into this project
* Added option to download firmware updates from https://www.gravitymon.com
Documentation
+++++++++++++
* Added section about the Floaty hardware
* Fixed schema errors in hardware section and linked PCB options
* Updated hardware section with options for ESP32 boards
* Updated installation instructions.
* Added section about the Floaty hardware.
v1.2.0 - beta1
==============
* Changes to 1.2.0 only affect the ESP32 hardware, the ESP8266 is the same as for v1.1.1
Features
++++++++
* Added support for the ESP32 C3 mini board
* Added support for the ESP32 S2 mini board
* Serial output is written to TX/RX pins instead of the USB connection for the ESP32c3. This way the serial console can be viewed when running on battery power.
Issues adressed
++++++++++++++++
* BUG: The first portion of a format template was lost when doing conversion.
Documentation
+++++++++++++
* Updated hardware section with options for ESP32 boards
* Updated installation instructions.
v1.1.1
======
* BUG: The text before the first variable was missed in the conversion of a format template.

View File

@ -1,643 +0,0 @@
import unittest, requests, json, time
ver = "1.2.0"
#host = "192.168.1.195"
#id = "6ac6f6"
host = "192.168.1.106"
id = "3045f4"
# python3 -m unittest -v apitests.API.test_bug_79
# python3 -m unittest -v apitests
def call_api_post( path, json ):
url = "http://" + host + path
return requests.post( url, data=json )
def call_api_get( path ):
url = "http://" + host + path
return requests.get( url )
class API(unittest.TestCase):
# Do factory reset for testing
def test_factory(self):
r = call_api_get( "/api/status" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
r = call_api_get( "/api/factory?id=" + j["id"])
time.sleep(4)
# Check that all parameters exist
def test_status(self):
r = call_api_get( "/api/status" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
self.assertEqual(j["wifi-ssid"], "@home")
self.assertNotEqual(j["angle"], 0)
self.assertNotEqual(j["gravity"], 2)
self.assertNotEqual(j["temp-c"], 0)
self.assertNotEqual(j["temp-f"], 0)
self.assertNotEqual(j["battery"], 0)
self.assertNotEqual(j["temp-format"], "")
self.assertNotEqual(j["gravity-format"], "")
self.assertNotEqual(j["sleep-mode"], True)
self.assertNotEqual(j["rssi"], 0)
self.assertNotEqual(j["app-ver"], "0.0.0")
self.assertNotEqual(j["app-build"], "test")
self.assertNotEqual(j["mdns"], "")
self.assertNotEqual(j["platform"], "")
self.assertNotEqual(j["runtime-average"], -1)
# Check that all parameters exist
def test_config_1(self):
j = { "id": id, "http-push": "https://push.me", "token": "mytoken", "token2": "mytoken2", "http-push2": "http://push.me", "http-push3": "http://push.me", "influxdb2-push": "http://influx.db", "influxdb2-org": "my-org",
"influxdb2-bucket": "my-bucket", "influxdb2-auth": "my-secret", "mqtt-push": "mqtt.com", "mqtt-port": 1883, "mqtt-user": "my-user",
"mqtt-pass": "my-pass", "http-push-h1": "header1", "http-push-h2": "header2", "http-push2-h1": "header1(2)", "http-push2-h2": "header2(2)" }
r = call_api_post( "/api/config/push", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["http-push-h1"], "header1")
self.assertEqual(j["http-push-h2"], "header2")
self.assertEqual(j["http-push2-h1"], "header1(2)")
self.assertEqual(j["http-push2-h2"], "header2(2)")
# Check that all parameters exist
def test_config_2(self):
j = { "id": id, "mdns": "gravmon", "temp-format": "C", "sleep-interval": 300 }
r = call_api_post( "/api/config/device", j )
self.assertEqual(r.status_code, 200)
def test_config_3(self):
j = { "id": id, "http-push": "https://push.me", "token": "mytoken", "token2": "mytoken2", "http-push2": "http://push.me", "http-push3": "http://push.me", "influxdb2-push": "http://influx.db", "influxdb2-org": "my-org",
"influxdb2-bucket": "my-bucket", "influxdb2-auth": "my-secret", "mqtt-push": "mqtt.com", "mqtt-port": 1883, "mqtt-user": "my-user",
"mqtt-pass": "my-pass" }
r = call_api_post( "/api/config/push", j )
self.assertEqual(r.status_code, 200)
def test_config_4(self):
j = { "id": id, "ota-url": "http://ota.url/path", "voltage-factor": 1.55, "temp-adjustment-value": -2, "gyro-temp": "on", "ble": "color" }
r = call_api_post( "/api/config/hardware", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
self.assertEqual(j["ble"], "color")
self.assertEqual(j["ota-url"], "http://ota.url/path")
self.assertEqual(j["voltage-factor"], 1.55)
self.assertEqual(j["temp-adjustment-value"], -2)
self.assertEqual(j["gyro-temp"], True)
# These are read only here, just checking them.
self.assertNotEqual(j["gyro-calibration-data"]["ax"], 0.0001)
self.assertNotEqual(j["gyro-calibration-data"]["ay"], 0.0001)
self.assertNotEqual(j["gyro-calibration-data"]["az"], 0.0001)
self.assertNotEqual(j["gyro-calibration-data"]["gx"], 0.0001)
self.assertNotEqual(j["gyro-calibration-data"]["gy"], 0.0001)
self.assertNotEqual(j["gyro-calibration-data"]["gz"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a1"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a2"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a3"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a4"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a5"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a6"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a7"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a8"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a9"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a10"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g1"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g2"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g3"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g4"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g5"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g6"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g7"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g8"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g9"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g10"], 0.0001)
self.assertNotEqual(j["angle"], 0)
self.assertNotEqual(j["gravity"], -10)
self.assertNotEqual(j["battery"], 0)
def test_config_5(self):
j = { "id": id, "gravity-formula": "my-formula", "gravity-temp-adjustment": "on", "gravity-format": "G" }
r = call_api_post( "/api/config/gravity", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
self.assertEqual(j["gravity-format"], "G")
self.assertEqual(j["gravity-formula"], "my-formula")
self.assertEqual(j["gravity-temp-adjustment"], True)
def test_config_6(self):
j = { "id": id, "mdns": "gravmon", "temp-format": "F", "sleep-interval": 300 }
r = call_api_post( "/api/config/device", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
self.assertEqual(j["mdns"], "gravmon")
self.assertEqual(j["wifi-ssid"], "@home")
self.assertEqual(j["wifi-pass"], "") # Should not be displayed in API
self.assertEqual(j["temp-format"], "F")
self.assertEqual(j["sleep-interval"], 300)
def test_config_7(self):
j = { "id": id, "ota-url": "", "voltage-factor": 1.55, "temp-adjustment-value": -2, "gyro-temp": "off", "ble": "blue" }
r = call_api_post( "/api/config/hardware", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
self.assertEqual(j["ble"], "blue")
self.assertEqual(j["ota-url"], "")
self.assertEqual(j["voltage-factor"], 1.55)
self.assertEqual(j["temp-adjustment-value"], -2)
self.assertEqual(j["gyro-temp"], False)
def test_config_8(self):
j = { "id": id, "gravity-formula": "", "gravity-temp-adjustment": "off", "gravity-format": "P" }
r = call_api_post( "/api/config/gravity", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gravity-format"], "P")
self.assertEqual(j["gravity-temp-adjustment"], False)
self.assertEqual(j["gravity-formula"], "")
def test_config_9(self):
j = { "id": id, "gravity-temp-adjustment": "on" }
r = call_api_post( "/api/config/gravity", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gravity-temp-adjustment"], True)
j = { "id": id } # No checkbox tag should set it to false.
r = call_api_post( "/api/config/gravity", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gravity-temp-adjustment"], False)
def test_config_A(self):
j = { "id": id, "gyro-temp": "on" }
r = call_api_post( "/api/config/hardware", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gyro-temp"], True)
j = { "id": id } # No checkbox tag should set it to false.
r = call_api_post( "/api/config/hardware", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gyro-temp"], False)
# Check formula api (sg mode)
def test_formula_sg_1(self):
# Ensure we have SG defined as gravity
j = { "id": id, "gravity-formula": "", "gravity-temp-adjustment": "off", "gravity-format": "G" }
r = call_api_post("/api/config/gravity", j)
self.assertEqual(r.status_code, 200)
def test_formula_sg_2(self):
# Fails due to wrong id
j = { "id": "wrong", "g1": 0, "g2": 1, "g3": 1.02, "g4": 1.0333, "g5": 1.00011, "g6": 1, "g7": 1, "g8": 1, "g9": 1, "g10": 1, "a1": 0, "a2": 25, "a3": 25.5, "a4": 25.55, "a5": 25.555, "a6": 0, "a7": 0, "a8": 0, "a9": 0, "a10": 0, "gravity-formula": "ThisShouldChange" }
r = call_api_post("/api/formula", j)
self.assertNotEqual(r.status_code, 200)
# Fails due to to few values
j = { "id": id, "g1": 0, "g2": 1, "g3": 1.02, "g4": 1.0333, "g5": 1.00011, "g6": 1, "g7": 1, "g8": 1, "g9": 1, "g10": 1, "a1": 0, "a2": 25, "a3": 0, "a4": 0, "a5": 0, "a6": 0, "a7": 0, "a8": 0, "a9": 0, "a10": 0, "gravity-formula": "ThisShouldChange" }
r = call_api_post("/api/formula", j)
self.assertEqual(r.status_code, 200)
# Checks that values from last call was stored
r = call_api_get( "/api/formula" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gravity-formula"], "")
self.assertEqual(j["error"], "Not enough values to create formula, need at least 3 angles.")
def test_formula_sg_3(self):
# Check a simple formula
j = { "id": id, "g1": 1.0, "g2": 1.01, "g3": 1.02, "g4": 1.03, "g5": 1.04, "g6": 1.05, "g7": 1.06, "g8": 1.07, "g9": 1.08, "g10": 1.1, "a1": 25, "a2": 30, "a3": 35, "a4": 40, "a5": 45, "a6": 50, "a7": 55, "a8": 60, "a9": 65, "a10": 70, "gravity-formula": "ThisShouldChange" }
r = call_api_post("/api/formula", j)
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/formula" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gravity-formula"], "0.00000909*tilt^2+0.00124545*tilt+0.96445455")
# Check formula api (plato mode)
def test_formula_plato_1(self):
# Ensure we have Plato defined as gravity
j = { "id": id, "gravity-formula": "", "gravity-temp-adjustment": "off", "gravity-format": "P" }
r = call_api_post("/api/config/gravity", j)
self.assertEqual(r.status_code, 200)
def test_formula_plato_2(self):
# Fails due to to few values
j = { "id": id, "g1": 0, "g2": 3, "g3": 5.3, "g4": 7.44, "g5": 8.555, "g6": 9, "g7": 9.1, "g8": 9.2, "g9": 9.3, "g10": 9.4, "a1": 0, "a2": 25, "a3": 25.5, "a4": 25.55, "a5": 25.555, "a6": 35, "a7": 36, "a8": 37, "a9": 38, "a10": 39, "gravity-formula": "ThisShouldChange" }
r = call_api_post("/api/formula", j)
self.assertEqual(r.status_code, 200)
# Checks that values from last call was stored
r = call_api_get( "/api/formula" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
self.assertEqual(j["g1"], 0)
self.assertEqual(j["g2"], 3)
self.assertEqual(j["g3"], 5.3)
self.assertEqual(j["g4"], 7.4)
self.assertEqual(j["g5"], 8.6)
self.assertEqual(j["g6"], 9)
self.assertEqual(j["g7"], 9.1)
self.assertEqual(j["g8"], 9.2)
self.assertEqual(j["g9"], 9.3)
self.assertEqual(j["g10"], 9.4)
self.assertEqual(j["a1"], 0)
self.assertEqual(j["a2"], 25)
self.assertEqual(j["a3"], 25.5)
self.assertEqual(j["a4"], 25.55)
self.assertEqual(j["a5"], 25.56)
self.assertEqual(j["a6"], 35)
self.assertEqual(j["a7"], 36)
self.assertEqual(j["a8"], 37)
self.assertEqual(j["a9"], 38)
self.assertEqual(j["a10"], 39)
self.assertEqual(j["gravity-format"], "P")
self.assertEqual(j["gravity-formula"], "-0.00012155*tilt^2+0.00874785*tilt+0.88003318")
self.assertEqual(j["error"], "")
def test_formula_plato_3(self):
j = { "id": id, "g1": 0, "g2": 3, "g3": 0, "g4": 0, "g5": 0, "g6": 0, "g7": 0, "g8": 0, "g9": 0, "g10": 0, "a1": 0, "a2": 25, "a3": 0, "a4": 0, "a5": 0, "a6": 0, "a7": 0, "a8": 0, "a9": 0, "a10": 0, "gravity-formula": "ThisShouldChange" }
r = call_api_post("/api/formula", j)
self.assertEqual(r.status_code, 200)
# Checks that values from last call was stored
r = call_api_get( "/api/formula" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["error"], "Not enough values to create formula, need at least 3 angles.")
def test_formula_plato_4(self):
# Ensure we have Plato defined as gravity
j = { "id": id, "gravity-formula": "", "gravity-temp-adjustment": "off", "gravity-format": "P" }
r = call_api_post("/api/config/gravity", j)
self.assertEqual(r.status_code, 200)
# Check a simple formula
j = { "id": id, "g1": 1.0, "g2": 1.1, "g3": 1.2, "g4": 1.3, "g5": 1.4, "g6": 1.5, "g7": 1.6, "g8": 1.7, "g9": 1.8, "g10": 1.9, "a1": 25, "a2": 30, "a3": 35, "a4": 40, "a5": 45, "a6": 50, "a7": 55, "a8": 60, "a9": 65, "a10": 70, "gravity-formula": "ThisShouldChange" }
r = call_api_post("/api/formula", j)
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/formula" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
#self.assertEqual(j["gravity-formula"], "0.00000001*tilt^2+0.00007752*tilt+1.00193428") # 1.6 max deviation
self.assertEqual(j["gravity-formula"], "-0.00000352*tilt^2+0.00045454*tilt+0.99231483") # 3.0 max deviation
# Check format api
def test_pushtest_1(self):
j = { "id": id, "http-push": "http://push.me", "token": "mytoken", "token2": "mytoken2", "http-push2": "http://push.me", "http-push3": "http://push.me", "brewfather-push": "http://push.me", "influxdb2-push": "http://influx.db", "influxdb2-org": "my-org",
"influxdb2-bucket": "my-bucket", "influxdb2-auth": "my-secret", "mqtt-push": "mqtt.com", "mqtt-port": 1883, "mqtt-user": "my-user",
"mqtt-pass": "my-pass", "http-push-h1": "header1", "http-push-h2": "header2", "http-push2-h1": "header1(2)", "http-push2-h2": "header2(2)" }
r = call_api_post( "/api/config/push", j )
self.assertEqual(r.status_code, 200)
def test_pushtest_2(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=http-1" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], False)
self.assertEqual(j["enabled"], True)
self.assertEqual(j["code"], -1)
def test_pushtest_3(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=http-2" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], False)
self.assertEqual(j["enabled"], True)
self.assertEqual(j["code"], -1)
def test_pushtest_4(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=http-3" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], False)
self.assertEqual(j["enabled"], True)
self.assertEqual(j["code"], -1)
def test_pushtest_5(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=influxdb" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], False)
self.assertEqual(j["enabled"], True)
self.assertEqual(j["code"], -1)
def test_pushtest_6(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=mqtt" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], False)
self.assertEqual(j["enabled"], True)
self.assertEqual(j["code"], -3)
# Check format api
def test_push_1(self):
r = call_api_get( "/api/factory?id=" + id)
time.sleep(4)
# Note: The endpoint test.php does not validate the payload, it only accepts the request and return 200.
j = { "id": id, "http-push": "http://www.allerum.net/test.php", "http-push2": "http://www.allerum.net/test.php", "http-push3": "http://www.allerum.net/test.php", "mqtt-push": "192.168.1.16", "mqtt-port": 1883, "mqtt-user": "", "mqtt-pass": "" }
r = call_api_post( "/api/config/push", j )
self.assertEqual(r.status_code, 200)
def test_push_2(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=http-1" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], True)
def test_push_3(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=http-2" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], True)
def test_push_4(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=http-3" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], True)
def test_push_5(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=mqtt" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], True)
def test_push_6(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=influx" )
# TODO: Figure out how to test the influx db setup. Create my own mockup ?
# Check format api
def test_format_1(self):
j = { "id": id, "http-1": "one" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_2(self):
j = { "id": id, "http-2": "two" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_3(self):
j = { "id": id, "http-3": "five" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_4(self):
j = { "id": id, "influxdb": "three" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_5(self):
j = { "id": id, "mqtt": "four" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_6(self):
r = call_api_get( "/api/config/format" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
self.assertEqual(j["http-1"], "one")
self.assertEqual(j["http-2"], "two")
self.assertEqual(j["http-3"], "five")
self.assertEqual(j["influxdb"], "three")
self.assertEqual(j["mqtt"], "four")
def test_format_7(self):
j = { "id": id, "http-1": "" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_8(self):
j = { "id": id, "http-2": "" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_9(self):
j = { "id": id, "http-3": "" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_A(self):
j = { "id": id, "influxdb": "" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_B(self):
j = { "id": id, "mqtt": "" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_C(self):
r = call_api_get( "/api/config/format" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
#print(j["http-1"])
#print(j["http-2"])
#print(j["http-3"])
#print(j["influxdb"])
#print(j["mqtt"])
self.assertEqual(j["http-1"], "%7B%22name%22%20%3A%20%22%24%7Bmdns%7D%22%2C%20%22ID%22%3A%20%22%24%7Bid%7D%22%2C%20%22token%22%20%3A%20%22%24%7Btoken%7D%22%2C%20%22interval%22%3A%20%24%7Bsleep%2Dinterval%7D%2C%20%22temperature%22%3A%20%24%7Btemp%7D%2C%20%22temp%5Funits%22%3A%20%22%24%7Btemp%2Dunit%7D%22%2C%20%22gravity%22%3A%20%24%7Bgravity%7D%2C%20%22angle%22%3A%20%24%7Bangle%7D%2C%20%22battery%22%3A%20%24%7Bbattery%7D%2C%20%22RSSI%22%3A%20%24%7Brssi%7D%2C%20%22corr%2Dgravity%22%3A%20%24%7Bcorr%2Dgravity%7D%2C%20%22gravity%2Dunit%22%3A%20%22%24%7Bgravity%2Dunit%7D%22%2C%20%22run%2Dtime%22%3A%20%24%7Brun%2Dtime%7D%20%7D")
self.assertEqual(j["http-2"], "%7B%22name%22%20%3A%20%22%24%7Bmdns%7D%22%2C%20%22ID%22%3A%20%22%24%7Bid%7D%22%2C%20%22token%22%20%3A%20%22%24%7Btoken%7D%22%2C%20%22interval%22%3A%20%24%7Bsleep%2Dinterval%7D%2C%20%22temperature%22%3A%20%24%7Btemp%7D%2C%20%22temp%5Funits%22%3A%20%22%24%7Btemp%2Dunit%7D%22%2C%20%22gravity%22%3A%20%24%7Bgravity%7D%2C%20%22angle%22%3A%20%24%7Bangle%7D%2C%20%22battery%22%3A%20%24%7Bbattery%7D%2C%20%22RSSI%22%3A%20%24%7Brssi%7D%2C%20%22corr%2Dgravity%22%3A%20%24%7Bcorr%2Dgravity%7D%2C%20%22gravity%2Dunit%22%3A%20%22%24%7Bgravity%2Dunit%7D%22%2C%20%22run%2Dtime%22%3A%20%24%7Brun%2Dtime%7D%20%7D")
self.assertEqual(j["http-3"], "%3Fname%3D%24%7Bmdns%7D%26id%3D%24%7Bid%7D%26token%3D%24%7Btoken2%7D%26interval%3D%24%7Bsleep%2Dinterval%7D%26temperature%3D%24%7Btemp%7D%26temp%2Dunits%3D%24%7Btemp%2Dunit%7D%26gravity%3D%24%7Bgravity%7D%26angle%3D%24%7Bangle%7D%26battery%3D%24%7Bbattery%7D%26rssi%3D%24%7Brssi%7D%26corr%2Dgravity%3D%24%7Bcorr%2Dgravity%7D%26gravity%2Dunit%3D%24%7Bgravity%2Dunit%7D%26run%2Dtime%3D%24%7Brun%2Dtime%7D")
self.assertEqual(j["influxdb"], "measurement%2Chost%3D%24%7Bmdns%7D%2Cdevice%3D%24%7Bid%7D%2Ctemp%2Dformat%3D%24%7Btemp%2Dunit%7D%2Cgravity%2Dformat%3D%24%7Bgravity%2Dunit%7D%20gravity%3D%24%7Bgravity%7D%2Ccorr%2Dgravity%3D%24%7Bcorr%2Dgravity%7D%2Cangle%3D%24%7Bangle%7D%2Ctemp%3D%24%7Btemp%7D%2Cbattery%3D%24%7Bbattery%7D%2Crssi%3D%24%7Brssi%7D%0A")
self.assertEqual(j["mqtt"], "ispindel%2F%24%7Bmdns%7D%2Ftilt%3A%24%7Bangle%7D%7Cispindel%2F%24%7Bmdns%7D%2Ftemperature%3A%24%7Btemp%7D%7Cispindel%2F%24%7Bmdns%7D%2Ftemp%5Funits%3A%24%7Btemp%2Dunit%7D%7Cispindel%2F%24%7Bmdns%7D%2Fbattery%3A%24%7Bbattery%7D%7Cispindel%2F%24%7Bmdns%7D%2Fgravity%3A%24%7Bgravity%7D%7Cispindel%2F%24%7Bmdns%7D%2Finterval%3A%24%7Bsleep%2Dinterval%7D%7Cispindel%2F%24%7Bmdns%7D%2FRSSI%3A%24%7Brssi%7D%7C")
# Toggle sleep mode
def toggle_sleepmode_1(self):
j = { "id": id, "sleep-mode": "on" }
r = call_api_post( "/api/status/sleepmode" )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/status" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["sleep-mode"], True)
def toggle_sleepmode_2(self):
j = { "id": id, "sleep-mode": "off" }
r = call_api_post( "/api/status/sleepmode" )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/status" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["sleep-mode"], False)
# Clear setting
def default_settings_1(self):
j = { "id": id, "mdns": "gravmon", "temp-format": "C", "sleep-interval": 300 }
r = call_api_post( "/api/config/device", j )
self.assertEqual(r.status_code, 200)
def default_settings_2(self):
j = { "id": id, "token": "", "token2": "", "http-push": "", "http-push2": "", "http-push3": "", "influxdb2-push": "", "influxdb2-org": "", "influxdb2-bucket": "",
"influxdb2-auth": "", "mqtt-push": "", "mqtt-port": 1883, "mqtt-user": "", "mqtt-pass": "" }
r = call_api_post( "/api/config/push", j )
self.assertEqual(r.status_code, 200)
def default_settings_3(self):
j = { "id": id, "ota-url": "", "voltage-factor": 1.55, "temp-adjustment-value": -2, "gyro-temp": "off" }
r = call_api_post( "/api/config/hardware", j )
self.assertEqual(r.status_code, 200)
def default_settings_4(self):
j = { "id": id, "gravity-formula": "", "gravity-temp-adjustment": "off", "gravity-format": "G" }
r = call_api_post( "/api/config/gravity", j )
self.assertEqual(r.status_code, 200)
# Check advanced
def test_advanced_config_1(self):
r = call_api_get( "/api/status" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
r = call_api_get( "/api/factory?id=" + j["id"])
time.sleep(4)
r = call_api_get( "/api/config/advanced" )
j = json.loads(r.text)
#print(j)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gyro-read-count"], 50)
self.assertEqual(j["tempsensor-resolution"], 9)
self.assertEqual(j["gyro-moving-threashold"], 500)
self.assertEqual(j["formula-max-deviation"], 3)
self.assertEqual(j["formula-calibration-temp"], 20)
self.assertEqual(j["wifi-portal-timeout"], 120)
self.assertEqual(j["wifi-connect-timeout"], 20)
self.assertEqual(j["ignore-low-angles"], False)
self.assertEqual(j["formula-calibration-temp"], 20)
self.assertEqual(j["int-http1"], 0)
self.assertEqual(j["int-http2"], 0)
self.assertEqual(j["int-http3"], 0)
self.assertEqual(j["int-influx"], 0)
self.assertEqual(j["int-mqtt"], 0)
def test_advanced_config_2(self):
j = { "id": id, "gyro-read-count": 51, "tempsensor-resolution": 10, "gyro-moving-threashold": 501, "formula-max-deviation": 1.7, "ignore-low-angles": "on",
"formula-calibration-temp": 21, "wifi-portal-timeout": 121, "wifi-connect-timeout": 21, "int-http1": 1, "int-http2": 2, "int-http3": 3, "int-influx": 4, "int-mqtt": 5 }
r = call_api_post( "/api/config/advanced", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config/advanced" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gyro-read-count"], 51)
self.assertEqual(j["tempsensor-resolution"], 10)
self.assertEqual(j["gyro-moving-threashold"], 501)
self.assertEqual(j["formula-max-deviation"], 1.7)
self.assertEqual(j["formula-calibration-temp"], 21)
self.assertEqual(j["wifi-portal-timeout"], 121)
self.assertEqual(j["wifi-connect-timeout"], 21)
self.assertEqual(j["ignore-low-angles"], True)
self.assertEqual(j["int-http1"], 1)
self.assertEqual(j["int-http2"], 2)
self.assertEqual(j["int-http3"], 3)
self.assertEqual(j["int-influx"], 4)
self.assertEqual(j["int-mqtt"], 5)
def test_advanced_config_3(self):
j = { "id": id, "ignore-low-angles": "on" }
r = call_api_post( "/api/config/advanced", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config/advanced" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["ignore-low-angles"], True)
j = { "id": id } # Skipping checkbox variable should set it to false
r = call_api_post( "/api/config/advanced", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config/advanced" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["ignore-low-angles"], False)
def test_bug_71(self):
format = "gm%3A%7B%22name%22%3A%22%24%7Bmdns%7D%22%2C%20%22ID%22%3A%22%24%7Bid%7D%22%2C%20%22temperature%22%3A%20%24%7Btemp%7D%2C%20%22gravity%22%3A%24%7Bgravity%7D%2C%22angle%22%3A%20%24%7Bangle%7D%2C%20%22battery%22%3A%24%7Bbattery%7D%2C%20%22rssi%22%3A%20%24%7Brssi%7D%7D"
j = { "id": id, "mqtt": format }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config/format" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["mqtt"], format)
def test_bug_79(self):
j = { "id": id, "formula-max-deviation": 1.7 }
r = call_api_post( "/api/config/advanced", j )
self.assertEqual(r.status_code, 200)
j = { "id": id, "g1": 1.0, "g2": 1.009, "g3": 1.014, "g4": 1.027, "g5": 1.037, "g6": 1.042, "g7": 1.051, "g8": 1.060, "g9": 1.073, "g10": 1.078, "a1": 23.52, "a2": 26.47, "a3": 29.87, "a4": 33.43, "a5": 38.16, "a6": 40.6, "a7": 45.85, "a8": 50.12, "a9": 56.55, "a10": 59.078, "gravity-formula": "" }
r = call_api_post("/api/formula", j)
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/formula" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["error"], 'Unable to find an accurate formula based on input, check error log and graph below.')
j = { "id": id, "formula-max-deviation": 4 }
r = call_api_post( "/api/config/advanced", j )
self.assertEqual(r.status_code, 200)
j = { "id": id, "g1": 1.0, "g2": 1.009, "g3": 1.014, "g4": 1.027, "g5": 1.037, "g6": 1.042, "g7": 1.051, "g8": 1.060, "g9": 1.073, "g10": 1.078, "a1": 23.52, "a2": 26.47, "a3": 29.87, "a4": 33.43, "a5": 38.16, "a6": 40.6, "a7": 45.85, "a8": 50.12, "a9": 56.55, "a10": 59.078, "gravity-formula": "" }
r = call_api_post("/api/formula", j)
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/formula" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["error"], '')
if __name__ == '__main__':
unittest.main()

View File

@ -3,7 +3,7 @@
"wifi-ssid2": "ssid 2",
"mdns": "gravmon3",
"id": "7376ef",
"ota-url": "https://www.gravitymon.com/firmware/",
"ota-url": "http://192.168.1.100:80/firmware/gravmon/",
"temp-format": "C",
"http-push": "http://192.168.1.10:9090/api/v1/ZYfjlUNeiuyu9N/telemetry",
"http-push-h1": "Auth: Basic T7IF9DD9fF3RDddE=",

View File

@ -1,82 +0,0 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <Arduino.h>
#include <main.hpp>
using aunit::Printer;
using aunit::TestRunner;
using aunit::Verbosity;
/*
// Boolean
assertEqual(a, b)
assertNotEqual(a, b)
assertLess(a, b)
assertMore(a, b)
assertLessOrEqual(a, b)
assertMoreOrEqual(a, b)
// String
assertStringCaseEqual(a, b)
assertStringCaseNotEqual(a, b)
assertNear(a, b, error)
assertNotNear(a, b, error)
checkTestDone(name)
checkTestNotDone(name)
checkTestPass(name)
checkTestNotPass(name)
checkTestFail(name)
checkTestNotFail(name)
checkTestSkip(name)
checkTestNotSkip(name)
checkTestExpire(name) [*]
checkTestNotExpire(name) [*]
assertTestDone(name)
assertTestNotDone(name)
assertTestPass(name)
assertTestNotPass(name)
assertTestFail(name)
assertTestNotFail(name)
assertTestSkip(name)
assertTestNotSkip(name)
assertTestExpire(name) [*]
assertTestNotExpire(name) [*]
*/
void setup() {
Serial.begin(115200);
Serial.println("Gravitymon - Unit Test Build");
delay(2000);
Printer::setPrinter(&Serial);
// TestRunner::setVerbosity(Verbosity::kAll);
}
void loop() {
TestRunner::run();
delay(10);
}
// EOF

View File

@ -1,31 +0,0 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <Arduino.h>
#include <main.hpp>
// No unit testing for the BLE module.
// EOF

View File

@ -1,67 +0,0 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <calc.hpp>
#include <helper.hpp>
// TODO: Add more test cases to explore formula creation error conditions when
// values are out of bounds
// TODO: Add more test cases to check order 3 + 4 formula creation as well.
test(calc_createFormula1) {
char buffer[100];
RawFormulaData fd = {
{0.0, 25.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
{0.0, 1.0, 1.02, 1.033, 1.00011, 1.0, 1.0, 1.0, 1.0, 1.0}};
int i = createFormula(fd, &buffer[0], sizeof(buffer), 2);
assertEqual(i, ERR_FORMULA_NOTENOUGHVALUES);
}
test(calc_createFormula2) {
char buffer[100];
RawFormulaData fd = {
{25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 55.0, 60.0, 65.0, 70.0},
{1.0, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.1}};
int i = createFormula(fd, &buffer[0], sizeof(buffer), 2);
assertEqual(i, 0);
assertEqual(&buffer[0], "0.00000909*tilt^2+0.00124545*tilt+0.96445455");
}
test(calc_calculateGravity) {
const char* formula = "0.00000909*tilt^2+0.00124545*tilt+0.96445455";
double g = calculateGravity(30, 20, formula);
float v1 = reduceFloatPrecision(g, 2);
float v2 = 1.01;
assertEqual(v1, v2);
}
test(calc_gravityTemperatureCorrectionC) {
double g = gravityTemperatureCorrectionC(1.02, 45.0, 20.0);
float v1 = reduceFloatPrecision(g, 2);
float v2 = 1.03;
assertEqual(v1, v2);
}
// EOF

View File

@ -1,73 +0,0 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <config.hpp>
test(config_defaultValues) {
assertEqual(myConfig.getGravityFormat(), 'G');
assertEqual(myConfig.getTempFormat(), 'C');
assertEqual(myConfig.getSleepInterval(), 900);
assertEqual(myConfig.getTempSensorAdjC(), 0.0);
float f = 4.15;
assertEqual(myConfig.getVoltageConfig(), f);
}
test(config_advDefaultValues) {
assertEqual(myAdvancedConfig.getDefaultCalibrationTemp(), 20.0);
assertEqual(myAdvancedConfig.getGyroReadCount(), 50);
assertEqual(myAdvancedConfig.getGyroReadDelay(), 3150);
assertEqual(myAdvancedConfig.getGyroSensorMovingThreashold(), 500);
assertEqual(myAdvancedConfig.getMaxFormulaCreationDeviation(), 3.0);
assertEqual(myAdvancedConfig.getPushIntervalHttp1(), 0);
assertEqual(myAdvancedConfig.getPushIntervalHttp2(), 0);
assertEqual(myAdvancedConfig.getPushIntervalHttp3(), 0);
assertEqual(myAdvancedConfig.getPushIntervalMqtt(), 0);
assertEqual(myAdvancedConfig.getPushIntervalInflux(), 0);
assertEqual(myAdvancedConfig.getPushTimeout(), 10);
assertEqual(myAdvancedConfig.getTempSensorResolution(), 9);
assertEqual(myAdvancedConfig.getWifiConnectTimeout(), 20);
assertEqual(myAdvancedConfig.getWifiPortalTimeout(), 120);
assertEqual(myAdvancedConfig.isIgnoreLowAnges(), false);
}
test(config_tempFormat) {
myConfig.setTempFormat('F');
assertEqual(myConfig.getTempFormat(), 'F');
myConfig.setTempFormat('C');
assertEqual(myConfig.getTempFormat(), 'C');
myConfig.setTempFormat('X');
assertEqual(myConfig.getTempFormat(), 'C');
}
test(config_gravityFormat) {
myConfig.setGravityFormat('P');
assertEqual(myConfig.getGravityFormat(), 'P');
myConfig.setGravityFormat('G');
assertEqual(myConfig.getGravityFormat(), 'G');
myConfig.setGravityFormat('X');
assertEqual(myConfig.getGravityFormat(), 'G');
}
// EOF

View File

@ -1,44 +0,0 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <gyro.hpp>
test(gyro_connectGyro) {
myGyro.setup();
assertEqual(myGyro.isConnected(), true);
}
test(gyro_readGyro) {
myGyro.setup();
assertEqual(myGyro.read(), true);
}
test(gyro_readGyroTemp) {
myGyro.setup();
assertNotEqual(myGyro.getInitialSensorTempC(), -273.0);
assertNotEqual(myGyro.getSensorTempC(), -273.0);
}
// EOF

View File

@ -1,93 +0,0 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <helper.hpp>
BatteryVoltage myBatteryVoltage;
test(helper_convertToPlato) {
double p = convertToPlato(1.008);
char buffer[20];
String s = convertFloatToString(p, &buffer[0], 2);
s.trim();
assertEqual(s, "2.06");
}
test(helper_convertToSG) {
double p = convertToSG(2.06);
char buffer[20];
String s = convertFloatToString(p, &buffer[0], 3);
s.trim();
assertEqual(s, "1.008");
}
test(helper_convertCtoF) {
float t = convertCtoF(20.0);
assertEqual(t, 68.0);
}
test(helper_convertFtoC) {
float t = convertFtoC(68.0);
assertEqual(t, 20.0);
}
test(helper_urlEncode) {
String s = urlencode("Hello world");
assertEqual(s, "Hello%20world");
}
test(helper_urlDecode) {
String s = urldecode("Hello%20world");
assertEqual(s, "Hello world");
}
test(helper_convertFloatToString) {
char buffer[20];
String s = convertFloatToString(20.2, &buffer[0], 2);
s.trim();
assertEqual(s, "20.20");
}
test(helper_reduceFloatPrecision1) {
float v = 20.233;
float f = reduceFloatPrecision(v, 2);
v = 20.23;
assertEqual(f, v);
}
test(helper_reduceFloatPrecision2) {
float v = 20.238;
float f = reduceFloatPrecision(v, 2);
v = 20.24;
assertEqual(f, v);
}
test(helper_readBatteryVoltage) {
myBatteryVoltage.read();
float f = myBatteryVoltage.getVoltage();
assertMoreOrEqual(f, 2.0);
}
// EOF

View File

@ -1,32 +0,0 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <Arduino.h>
#include <main.hpp>
// TODO: Build some php scripts that run on gravitymon.com for testing the push
// data.
// EOF

View File

@ -1,150 +0,0 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <config.hpp>
#include <templating.hpp>
test(template_applyTemplate1) {
TemplatingEngine e;
char buffer[20];
myConfig.setMDNS("gravitymon");
e.initialize(45.0, 1.123, 1.223, 21.2, 2.98);
String s = e.create(TemplatingEngine::TEMPLATE_HTTP1);
String id = myConfig.getID();
String batt =
convertFloatToString(myBatteryVoltage.getVoltage(), &buffer[0], 2);
batt.trim();
String v = "{\"name\" : \"gravitymon\", \"ID\": \"" + id +
"\", \"token\" : \"\", \"interval\": 900, \"temperature\": 21.2, "
"\"temp_units\": \"C\", \"gravity\": 1.1230, \"angle\": 45.00, "
"\"battery\": " +
batt +
", \"RSSI\": 31, \"corr-gravity\": 1.2230, \"gravity-unit\": "
"\"G\", \"run-time\": 3.0 }";
assertEqual(s, v);
}
test(template_applyTemplate2) {
TemplatingEngine e;
char buffer[20];
myConfig.setMDNS("gravitymon");
e.initialize(45.0, 1.123, 1.223, 21.2, 2.98);
String s = e.create(TemplatingEngine::TEMPLATE_HTTP2);
String id = myConfig.getID();
String batt =
convertFloatToString(myBatteryVoltage.getVoltage(), &buffer[0], 2);
batt.trim();
String v = "{\"name\" : \"gravitymon\", \"ID\": \"" + id +
"\", \"token\" : \"\", \"interval\": 900, \"temperature\": 21.2, "
"\"temp_units\": \"C\", \"gravity\": 1.1230, \"angle\": 45.00, "
"\"battery\": " +
batt +
", \"RSSI\": 31, \"corr-gravity\": 1.2230, \"gravity-unit\": "
"\"G\", \"run-time\": 3.0 }";
assertEqual(s, v);
}
test(template_applyTemplate3) {
TemplatingEngine e;
char buffer[20];
myConfig.setMDNS("gravitymon");
e.initialize(45.0, 1.123, 1.223, 21.2, 2.98);
String s = e.create(TemplatingEngine::TEMPLATE_HTTP3);
String id = myConfig.getID();
String batt =
convertFloatToString(myBatteryVoltage.getVoltage(), &buffer[0], 2);
batt.trim();
String v = "?name=gravitymon&id=" + id +
"&token=&interval=900&temperature=21.2&temp-units=C&gravity=1."
"1230&angle=45.00&battery=" +
batt + "&rssi=31&corr-gravity=1.2230&gravity-unit=G&run-time=3.0";
assertEqual(s, v);
}
test(template_applyTemplate4) {
TemplatingEngine e;
char buffer[20];
myConfig.setMDNS("gravitymon");
e.initialize(45.0, 1.123, 1.223, 21.2, 2.98);
String s = e.create(TemplatingEngine::TEMPLATE_INFLUX);
String id = myConfig.getID();
String batt =
convertFloatToString(myBatteryVoltage.getVoltage(), &buffer[0], 2);
batt.trim();
String v =
"measurement,host=gravitymon,device=" + id +
",temp-format=C,gravity-format=G "
"gravity=1.1230,corr-gravity=1.2230,angle=45.00,temp=21.2,battery=" +
batt + ",rssi=31\n";
assertEqual(s, v);
}
test(template_applyTemplate5) {
TemplatingEngine e;
char buffer[20];
myConfig.setMDNS("gravitymon");
e.initialize(45.0, 1.123, 1.223, 21.2, 2.98);
String s = e.create(TemplatingEngine::TEMPLATE_MQTT);
String batt =
convertFloatToString(myBatteryVoltage.getVoltage(), &buffer[0], 2);
batt.trim();
String v =
"ispindel/gravitymon/tilt:45.00|ispindel/gravitymon/"
"temperature:21.2|ispindel/gravitymon/temp_units:C|ispindel/gravitymon/"
"battery:" +
batt +
"|ispindel/gravitymon/gravity:1.1230|ispindel/gravitymon/"
"interval:900|ispindel/gravitymon/RSSI:31|";
assertEqual(s, v);
}
test(template_applyTemplate6) {
TemplatingEngine e;
char buffer[20];
myConfig.setMDNS("gravitymon");
const char* tpl =
"<prtg><result><channel>Densite</channel><float>1</float><value>${gravity}</value></result>"
"<result><channel>Batterie</channel><float>1</float><value>${battery}</value></result>"
"<result><channel>Temperature</channel><float>1</float><value>${temp}</value></result></prtg>";
e.initialize(45.0, 1.123, 1.223, 21.2, 2.98);
String s = e.create(tpl);
String batt =
convertFloatToString(myBatteryVoltage.getVoltage(), &buffer[0], 2);
batt.trim();
String v = "<prtg><result><channel>Densite</channel><float>1</float><value>1.1230</value></result>"
"<result><channel>Batterie</channel><float>1</float><value>" + batt + "</value></result>"
"<result><channel>Temperature</channel><float>1</float><value>21.2</value></result></prtg>";
assertEqual(s, v);
}
// EOF

View File

@ -1,34 +0,0 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <tempsensor.hpp>
test(temp_readSensor) {
myTempSensor.setup();
myTempSensor.getTempC();
assertEqual(myTempSensor.isSensorAttached(), true);
}
// EOF

View File

@ -1,32 +0,0 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <Arduino.h>
#include <main.hpp>
// No unit testing for the WEB module. These tests are done using python script
// and the API's
// EOF

View File

@ -1,32 +0,0 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <Arduino.h>
#include <main.hpp>
// No unit testing for the WIFI module. These tests are manual when testing the
// installation steps.
// EOF