Compare commits

...

57 Commits

Author SHA1 Message Date
Magnus
433502e7a0 Minor updates to docs 2023-02-03 11:05:44 +01:00
Magnus
cd1ada6744
Merge pull request #125 from mp-se/dev
Fix bug in template engine
2023-01-24 21:12:39 +01:00
GitHub Action
22a4f40f41 GitHub Action Build 2023-01-24 20:00:27 +00:00
Magnus
59b95cd68b Revert arduino baseline 2023-01-24 15:21:00 +01:00
Magnus
88c396398d Update release notes 2023-01-23 12:15:28 +01:00
GitHub Action
5477ab4683 GitHub Action Build 2023-01-19 12:02:29 +00:00
Magnus
a8773a7ba1 Fixed templating bug and update test cases 2023-01-19 12:55:46 +01:00
Magnus
71e67ca3f1 Fix shield.io links 2022-12-25 10:36:58 +01:00
Magnus
b76c7a55b4 FIxed ota link under install 2022-12-08 20:26:12 +01:00
Magnus
4f806f4b02
Update README.md 2022-12-06 21:50:14 +01:00
Magnus
a515a164fc
Merge pull request #122 from mp-se/dev
Merge 1.2 rc1 into master
2022-12-06 21:49:00 +01:00
GitHub Action
b9e645b20d GitHub Action Build 2022-12-06 20:42:27 +00:00
Magnus
87caf6225b Updated docs 2022-12-06 21:35:42 +01:00
GitHub Action
474bcdc5e9 GitHub Action Build 2022-12-05 21:41:05 +00:00
Magnus
19ab8f5271 Added message when new ota is found 2022-12-05 22:35:19 +01:00
Magnus
6706a7f9d6 Updated ota html 2022-12-05 22:09:03 +01:00
magnus
7c35bb59d0 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-12-05 21:49:49 +01:00
Magnus
47527b8a73 Updated docs 2022-12-05 21:49:08 +01:00
GitHub Action
49921bf8dc GitHub Action Build 2022-12-05 20:26:40 +00:00
Magnus
35540243c6 Added more targets in workflow 2022-12-05 21:21:07 +01:00
magnus
963966d201 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-12-05 21:18:44 +01:00
Magnus
6d5d859283 Fixed corrupted test script 2022-12-05 21:18:17 +01:00
GitHub Action
fe7be5d8d6 GitHub Action Build 2022-12-05 19:41:01 +00:00
Magnus
71f87ff143 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-12-05 20:37:13 +01:00
Magnus
e29090a2a2 Refactor location for unit tests 2022-12-05 20:35:17 +01:00
GitHub Action
924b0356f5 GitHub Action Build 2022-12-05 19:11:39 +00:00
magnus
41cb3113b9 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-12-05 20:06:40 +01:00
Magnus
dcebbf2e83 Update precommit action 2022-12-05 20:03:50 +01:00
GitHub Action
66d2e69927 GitHub Action Build 2022-12-05 19:01:52 +00:00
Magnus
89aa631b75 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-12-05 19:56:44 +01:00
Magnus
e75a31ebb7 Update docs and pre-commit formatting 2022-12-05 19:56:16 +01:00
GitHub Action
e622580493 GitHub Action Build 2022-12-05 16:22:34 +00:00
Magnus
b3a4266da1 Build 1.2 for final testing 2022-12-05 17:16:23 +01:00
Magnus
fe67ff63f1 Merged unit tests into dev 2022-12-05 17:15:41 +01:00
Magnus
ce1061dfb3 Merge branch 'dev' into unit 2022-12-05 14:04:39 +01:00
Magnus
c4d070ee89 Merge branch 'master' into dev 2022-12-05 14:04:27 +01:00
Magnus
032f656d51 Fix spelling errors in doc 2022-12-05 14:04:09 +01:00
Magnus
65e243e391 Update excludes 2022-12-05 14:00:18 +01:00
Magnus
8a4d0ac035 Merge branch 'master' into dev 2022-12-05 13:58:32 +01:00
Magnus
3be6847238 Merge branch 'master' of into patch2 2022-12-05 13:51:42 +01:00
Magnus
683ff4e6d5 Fix hardware schematic issues 2022-12-05 13:47:04 +01:00
Magnus
8e12eac627 remove verbose output 2022-12-02 14:56:09 +01:00
Magnus
83c0bb5283 Added temp tests 2022-12-02 14:10:53 +01:00
Magnus
8257e9ab51 Added templating tests 2022-12-02 14:07:19 +01:00
Magnus
67ebd559d7 Added helper tests 2022-12-02 13:32:49 +01:00
Magnus
6f09afcf96 Added gyro tests 2022-12-02 11:40:40 +01:00
Magnus
a7362a42fa Added tests for calc 2022-12-02 10:59:39 +01:00
Magnus
a191f6bb35 Added config unit tests 2022-12-02 10:07:06 +01:00
Magnus
88cdd986e6 Updated skeleton 2022-12-02 08:44:38 +01:00
Magnus
1607503103 Created skeletons for unit tests 2022-12-02 08:40:40 +01:00
Magnus
31c06bcce4 Update readme 2022-12-01 08:54:16 +01:00
Magnus
b2449db6f9 Update test structure 2022-12-01 08:44:56 +01:00
Magnus
b79e5b5d73 Merge branch 'dev' into unit 2022-11-30 23:20:28 +01:00
Magnus
d28d545109 Cleanup setup 2022-11-30 23:20:00 +01:00
Magnus
577d7382f4 Adding unit test target 2022-11-30 23:18:35 +01:00
Magnus
d4dfccdddd Updating docs for c3v1 2022-11-24 14:49:34 +01:00
Magnus
88fc94ed8c Added esp32c3 v1.0 board target 2022-11-24 14:39:47 +01:00
48 changed files with 1553 additions and 113 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
run: pio run -e gravity-release -e gravity32-release -e gravity32c3-release -e gravity32s2-release -e gravity32c3v1-release -e gravity32lite-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@v4
- uses: actions/setup-python@v3
- name: clang format support
run: |
sudo apt install clang-format cppcheck

2
.gitignore vendored
View File

@ -2,3 +2,5 @@
.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/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)
![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)
# 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.
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.
Now also works with ESP32 d1 mini, ESP32 c3 mini, ESP32 S2 mini which both are pin compatible with ESP8266.
@ -28,4 +28,5 @@ 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
* David Conde, @davidconde
* Lars H.

31
TEST.md Normal file
View File

@ -0,0 +1,31 @@
# 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.

BIN
battery-test-1.2.xlsx Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
bin/firmware32c3v1.bin Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

@ -468,7 +468,7 @@
</div>
</div>
<div class="row mb-3">
<div class="row mb-3" id="ota-hide">
<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-url").prop("disabled", true);
$('#ota-hide').hide();
} else {
$("#ota-url").prop("disabled", false);
$('#ota-hide').show();
}
});
</script>
@ -966,7 +966,7 @@
if( cfg["ota-url"] == gravitymonUrl) {
$("#gravitymon-com").prop( "checked", true );
$("#ota-url").prop("disabled", true);
$("#ota-hide").hide();
}
$("#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.0\""
-DCFG_GITREV=\""beta-2\""
#!python script/git_rev.py
-DCFG_APPVER="\"1.2.1\""
#-DCFG_GITREV=\""beta-3\""
!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,7 +102,30 @@ 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}
@ -143,6 +166,39 @@ 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 =

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
requests

View File

@ -45,6 +45,12 @@ 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,9 +27,6 @@ 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;
@ -118,10 +115,6 @@ 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();
@ -163,12 +156,10 @@ 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,12 +133,6 @@ 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,9 +80,6 @@ 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) {
@ -138,10 +135,9 @@ void TemplatingEngine::initialize(float angle, float gravitySG,
#endif
}
//
// Create the data using defined template.
//
const char* TemplatingEngine::create(TemplatingEngine::Templates idx) {
// the useDefaultTemplate param is there to support unit tests.
const char* TemplatingEngine::create(TemplatingEngine::Templates idx,
bool useDefaultTemplate) {
String fname;
_baseTemplate.reserve(600);
@ -169,15 +165,16 @@ const char* TemplatingEngine::create(TemplatingEngine::Templates idx) {
break;
}
// 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 (!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());
}
}
#if LOG_LEVEL == 6
@ -197,4 +194,17 @@ 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,7 +160,8 @@ class TemplatingEngine {
}
}
}
strncat(_output, format + k, size - k);
// strncat(_output, format + k, size - k);
strncat(_output, format + k, strlen(format + k));
Log.notice(F("TPL : Transformed template %d chars to %d chars" CR),
strlen(format), strlen(_output));
@ -200,7 +201,9 @@ class TemplatingEngine {
}
void initialize(float angle, float gravitySG, float corrGravitySG,
float tempC, float runTime);
const char *create(TemplatingEngine::Templates idx);
const char *create(TemplatingEngine::Templates idx,
bool useDefaultTemplate = false);
const char *create(const char *formatTemplate);
};
#endif // SRC_TEMPLATING_HPP_

View File

@ -58,9 +58,6 @@ 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,6 +430,8 @@ 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::
Since the format templates can be big this function can be quite slow on a small device such as the esp8266.
If format templates are large this feature can be slow on a small device such as the esp8266.
.. image:: images/format.png
:width: 800

View File

@ -30,9 +30,10 @@ 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.
* 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).
* 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).
.. 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-2022, Magnus Persson'
copyright = '2021-2023, Magnus Persson'
author = 'Magnus Persson'
# The full version, including alpha/beta/rc tags
release = '1.2.0'
release = '1.2.1'
# -- General configuration ---------------------------------------------------

View File

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

View File

@ -3,6 +3,11 @@
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
=========================
@ -20,24 +25,20 @@ Schema for esp8266 build
:width: 700
:alt: Schema esp8266
iSpindle based on esp32
=======================
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.
Gravitymon supports a number of ESP32 boards that offers bluetooth support.
.. image:: images/esp32_hardware.jpg
:width: 500
:alt: iSpindle esp32 hardware options
* 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.
* 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.
* 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 and s2 are prefered). The esp32 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 or s2 are prefered). The esp32 d1 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
@ -47,7 +48,7 @@ It's possible to use this PCB and mount an ESP32 mini on top of that (c3 and s2
ESP32c3 mini
++++++++++++
This is model is now fully supported by gravitymon.
This is model is fully supported by gravitymon.
.. image:: images/ispindel_esp32c3.jpg
:width: 500
@ -62,7 +63,7 @@ Here is an image of where I added the resistor for the voltage divider.
ESP32s2 mini
++++++++++++
This is model is now fully supported by gravitymon. Same setup as for ESP32C3 mini.
This is model is fully supported by gravitymon. Same setup as for ESP32C3 mini.
ESP32 d1 mini
+++++++++++++
@ -77,7 +78,7 @@ Schema for esp32 build
++++++++++++++++++++++
.. note::
This schema assumes that an ESP32 D1 Mini (pin compatible with ESP8266 D1 Mini is used). The ESP32 has two rows of pins but
This schema assumes that an ESP32 d1 mini (pin compatible with ESP8266). 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: 68 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 127 KiB

View File

@ -7,7 +7,7 @@ Welcome to GravityMon
#####################
.. note::
This documentation reflects **v1.2 - beta 2**. Last updated 2022-11-19
This documentation reflects **v1.2.1**. Last updated 2023-02-03
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. 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 or hardware.
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. 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.
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.
* **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://mp-se.github.io/gravitymon/release/``
``https://gravitymon.com/firmware/``
Manual update
*************

View File

@ -1,6 +1,22 @@
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
@ -11,7 +27,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 soldering.
- Check the orientation of the sensor and the soldering.
Calibration error (unable to find a valid formula)
--------------------------------------------------
@ -30,18 +46,26 @@ 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.
User interface does not render correctly
----------------------------------------
How can I filter data on influxdb without needing to know the time range
------------------------------------------------------------------------
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
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.
Data is not populated in the fields
------------------------------------
When you switch brews you need to go in and change the token to identify the brewing session.
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
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}
Also ensure that any security tools does not block the execution of these features.

View File

@ -3,40 +3,47 @@
Releases
########
v1.2.0 - beta2
==============
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
======
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.
Documentation
+++++++++++++
* 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.
* 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.
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.

643
test/apitests.py Normal file
View File

@ -0,0 +1,643 @@
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": "http://192.168.1.100:80/firmware/gravmon/",
"ota-url": "https://www.gravitymon.com/firmware/",
"temp-format": "C",
"http-push": "http://192.168.1.10:9090/api/v1/ZYfjlUNeiuyu9N/telemetry",
"http-push-h1": "Auth: Basic T7IF9DD9fF3RDddE=",

82
test/tests.cpp Normal file
View File

@ -0,0 +1,82 @@
/*
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

31
test/tests_ble.cpp Normal file
View File

@ -0,0 +1,31 @@
/*
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

67
test/tests_calc.cpp Normal file
View File

@ -0,0 +1,67 @@
/*
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

73
test/tests_config.cpp Normal file
View File

@ -0,0 +1,73 @@
/*
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

44
test/tests_gyro.cpp Normal file
View File

@ -0,0 +1,44 @@
/*
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

93
test/tests_helper.cpp Normal file
View File

@ -0,0 +1,93 @@
/*
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

32
test/tests_pushtarget.cpp Normal file
View File

@ -0,0 +1,32 @@
/*
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

150
test/tests_templating.cpp Normal file
View File

@ -0,0 +1,150 @@
/*
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

34
test/tests_tempsensor.cpp Normal file
View File

@ -0,0 +1,34 @@
/*
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

32
test/tests_webserver.cpp Normal file
View File

@ -0,0 +1,32 @@
/*
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

32
test/tests_wifi.cpp Normal file
View File

@ -0,0 +1,32 @@
/*
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