Compare commits

...

578 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
Magnus
0be50389cc
Merge pull request #120 from mp-se/dev
Merge 1.2.0beta2
2022-11-20 20:06:34 +01:00
GitHub Action
9351696732 GitHub Action Build 2022-11-20 19:02:45 +00:00
Magnus
bc21127479 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-11-20 19:57:56 +01:00
Magnus
1dbd03a0f0 Update pre-commit 2022-11-20 19:56:50 +01:00
GitHub Action
61e78beb4a GitHub Action Build 2022-11-20 18:50:43 +00:00
Magnus
d905aeafe9 Adding d8/d7 + serial pins on all versions 2022-11-20 19:46:50 +01:00
Magnus
60675ecf0f Adding new target binaries 2022-11-19 19:01:06 +01:00
Magnus
61715cda08 Bump more gh action version 2022-11-19 18:52:22 +01:00
Magnus
3d26564b8f Update github action 2022-11-19 18:48:38 +01:00
Magnus
74b1c40ffd Bump github action 2022-11-19 18:40:10 +01:00
GitHub Action
186ae4e4ee GitHub Action Build 2022-11-19 16:58:37 +00:00
Magnus
f2afd653ae Update readme 2022-11-19 17:54:09 +01:00
Magnus
c5e5c7727b Update release notes 2022-11-19 17:51:54 +01:00
Magnus
8bb720becd Added volt factor calculator 2022-11-19 17:51:06 +01:00
Magnus
756a49af50 Adjusted power setting for ble 2022-11-19 13:57:27 +01:00
Magnus
e2f972bbc0 Document updates 2022-11-19 13:57:16 +01:00
Magnus
9009b41f57 Updated battery estimation 2022-11-19 12:32:04 +01:00
Magnus
1c3784a223 Added serial pins for esp8266 2022-11-19 10:00:36 +01:00
GitHub Action
489b268f73 GitHub Action Build 2022-11-11 19:50:45 +00:00
Magnus Persson
01c709f9b7 Adding ota for esp32lite 2022-11-11 07:41:58 +01:00
Magnus Persson
ef3dd420f6 Updated docs 2022-11-10 20:04:16 +01:00
Magnus Persson
3505673652 Added OTA from gravmon.com 2022-11-10 20:04:03 +01:00
Magnus Persson
8ca51a2888 Fix volt pin 2022-11-10 19:44:24 +01:00
Magnus Persson
d4ea1c9f98 Updated volt pin name 2022-11-10 19:42:44 +01:00
GitHub Action
6af0af57b8 GitHub Action Build 2022-11-09 21:39:03 +00:00
Magnus Persson
8ce8deb400 Bump version to beta2 2022-11-09 22:33:55 +01:00
Magnus Persson
5e24726e7b Merge branch 'master' into dev 2022-11-09 22:29:42 +01:00
Magnus Persson
0b1c87323a Cleanup 2022-11-09 21:50:49 +01:00
Magnus Persson
787e39eff1 Cleanup failed merge 2022-11-09 21:38:55 +01:00
GitHub Action
683df36164 GitHub Action Build 2022-11-08 20:06:45 +00:00
Magnus Persson
9e1701c2da Precommit changes 2022-11-08 21:02:40 +01:00
Magnus Persson
f220df08ac Updates for floaty build 2022-11-08 21:01:07 +01:00
Magnus Persson
29d6486743 Added code for floaty hardware option 2022-11-06 11:14:40 +01:00
Magnus Persson
09ffcba84e Added esp32lite and prepared power cons. calc 2022-11-06 11:12:27 +01:00
Magnus Persson
87eab2a550 Added define to reduce wifi power on C3 2022-11-06 10:50:23 +01:00
Magnus Persson
5bc3fdb153 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-11-04 18:55:37 +01:00
Magnus Persson
de421b5000 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-11-04 18:55:30 +01:00
GitHub Action
7941b04ce8 GitHub Action Build 2022-11-04 17:46:45 +00:00
Magnus Persson
022e4b804b Update docs path 2022-11-04 18:46:33 +01:00
Magnus Persson
4c7182a6d6 Updates to docs 2022-11-04 18:42:04 +01:00
Magnus Persson
5ac1ead5b9 Update readme 2022-11-04 10:17:31 +01:00
Magnus Persson
4e25eb4b81 Update path 2022-11-04 10:02:38 +01:00
Magnus Persson
6440ba50e2 Change doc publish 2022-11-04 09:55:34 +01:00
Magnus Persson
ea83632285 Reduced ble power for c3 chip 2022-11-01 08:41:44 +01:00
Magnus Persson
f319ec4569 Update docs and added logo 2022-10-22 10:22:49 +02:00
Magnus Persson
21298b09f4 Added logo 2022-10-22 10:09:21 +02:00
GitHub Action
bf17b9b864 GitHub Action Build 2022-10-20 06:07:25 +00:00
Magnus Persson
acaa5d9f1d Fix 2022-10-20 08:03:12 +02:00
Magnus Persson
5dd4fb3f1e Add binaries for s2 2022-10-20 07:58:13 +02:00
Magnus Persson
4aae1b912c Precommit fix 2022-10-20 07:58:01 +02:00
Magnus Persson
50c534cbe5 Added new target to action 2022-10-20 07:52:32 +02:00
Magnus Persson
635be88c11 Fix log level for serial 2022-10-20 07:52:18 +02:00
Magnus Persson
53c953de01 Append fwk debugging to serial 2022-10-20 07:52:05 +02:00
Magnus Persson
d967d8efd9 Update serial console 2022-10-19 19:52:55 +02:00
Magnus Persson
bbdbf88a76 Added support for s2 mini 2022-10-19 11:48:50 +02:00
Magnus Persson
47bae4d28e Update install method for ESP32c3 2022-10-18 19:51:58 +02:00
Magnus
ce8e8bbe7b Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-10-18 19:30:55 +02:00
GitHub Action
0ca60ec71f GitHub Action Build 2022-10-18 17:21:00 +00:00
Magnus Persson
d7981851bd Update readme 2022-10-18 19:19:25 +02:00
Magnus Persson
2a419c10d1 Update 2022-10-18 19:04:05 +02:00
Magnus Persson
b265dba422 Merge 2022-10-18 19:01:41 +02:00
Magnus Persson
74a0598905 Merge 2022-10-18 19:00:53 +02:00
Magnus Persson
3b7be30363 Merge branch 'master' into dev 2022-10-18 19:00:28 +02:00
Magnus Persson
5889ebda1e Bump doc version 2022-10-18 18:57:06 +02:00
Magnus Persson
ec77b873a8 Update build targets 2022-10-18 18:55:03 +02:00
Magnus Persson
04120a0490 Removed obsolete files from bin 2022-10-18 18:08:47 +02:00
Magnus
4f28ea9975 Added release notes 2022-10-18 09:39:04 +02:00
Magnus
c1eade04af Update build 2022-10-18 09:28:54 +02:00
Magnus
2fd0c881f3 Bump version 2022-10-18 09:28:03 +02:00
Magnus
8113275402 Fix bug in templating 2022-10-18 09:27:51 +02:00
GitHub Action
b5928cb201 GitHub Action Build 2022-10-17 19:07:44 +00:00
Magnus Persson
22f9a2d228 Fix precommit 2022-10-17 21:03:12 +02:00
Magnus Persson
343aea7ebe Fix sluggish webserver 2022-10-17 21:02:19 +02:00
Magnus Persson
19e67cb678 Refactor html serving 2022-10-17 21:02:08 +02:00
Magnus Persson
379dff1d22 Fix updated path 2022-10-17 20:34:59 +02:00
Magnus Persson
e82cd2aeae Fix build errors 2022-10-17 20:27:22 +02:00
Magnus Persson
6262cec27d Update esp32c3 support 2022-10-17 20:19:20 +02:00
Magnus Persson
a9347c7e03 Removed warning from wifi lib 2022-10-16 20:40:41 +02:00
Magnus Persson
60adcabd81 Fixed conflict with logging library 2022-10-16 20:40:08 +02:00
Magnus Persson
411cec08c7 Added macro for controling serial port 2022-10-16 20:39:27 +02:00
Magnus Persson
b6fcf94191 Updated install docs 2022-10-16 20:34:57 +02:00
Magnus Persson
e9ef632cce Added docs on esp32 2022-10-16 10:46:09 +02:00
Magnus Persson
7d4c42a47c Added new targets to UI 2022-10-16 10:16:16 +02:00
Magnus Persson
9ce6d54b80 Updated i2c lib for esp32 2022-10-16 09:23:51 +02:00
Magnus Persson
2e96e4aea8 Updated esp32 debug target 2022-10-15 17:42:50 +02:00
Magnus Persson
775ba53260 Update release notes 2022-10-15 17:41:41 +02:00
Magnus Persson
6e0af97574 Disable BLE for ESP32S2 2022-10-15 17:41:17 +02:00
Magnus Persson
531baa917e Added c3 targets to bin folder 2022-10-14 19:39:38 +02:00
Magnus Persson
7f73f22759 Docs update 2022-10-14 19:39:01 +02:00
Magnus Persson
0fad08ccc5 Added log entry when doing factory reset 2022-10-14 19:38:41 +02:00
Magnus Persson
fcfc8df625 Reading wifi creds from eeprom, fixed ota filenames 2022-10-14 19:38:04 +02:00
Magnus Persson
e54c1d2518 Fixed bug in template engine, not including the start of tempalte 2022-10-14 19:37:14 +02:00
Magnus Persson
027e98d2de Update board pins 2022-10-14 19:36:36 +02:00
Magnus Persson
91227b0728 Added esp32 reset reason 2022-10-14 19:36:01 +02:00
Magnus Persson
ddeca29f8e Update targets 2022-10-14 19:35:39 +02:00
Magnus Persson
15d3371437 Updated pins for new boards 2022-10-12 23:26:17 +02:00
Magnus Persson
ac51c52c74 Moved global classes to main 2022-10-12 19:52:44 +02:00
Magnus Persson
1148a23bb4 Added c3 target 2022-10-12 19:52:15 +02:00
Magnus Persson
1ddd8bc669 Removed upload functions (not used) 2022-10-12 19:51:38 +02:00
Magnus Persson
7664215d23 Limit wifi power on esp32c3 2022-10-12 19:30:49 +02:00
Magnus Persson
9eb356bd43 Upgrade wifi libs 2022-10-12 19:26:24 +02:00
Magnus
f4401ea526
Merge pull request #105 from jinjorge/doc_fix_functionality
Typo fixes in functionality doc
2022-09-17 09:39:33 +02:00
Magnus
535367d943
Merge pull request #107 from jinjorge/doc_fix_index
Typo fixes in index doc
2022-09-17 09:39:05 +02:00
Jeremiah Njoroge
8db58c76dc Renamed Functionality doc 2022-09-15 12:58:21 -07:00
Jeremiah Njoroge
9441e4bc54 Integrate PR feedback 2022-09-15 11:41:42 -07:00
Magnus
b7d2183564
Merge pull request #99 from jinjorge/doc_fix_api_rst
Typo fixes for api doc
2022-09-15 20:26:15 +02:00
Magnus
18f7ba8911
Merge pull request #100 from jinjorge/doc_fix_advanced_rst
Typo fixes
2022-09-15 20:25:36 +02:00
Magnus
af0d6f21ca
Merge pull request #101 from jinjorge/doc_fix_configuration
Typo fixes for configuration documentation
2022-09-15 20:25:05 +02:00
Magnus
e3fcd53a36
Merge pull request #102 from jinjorge/doc_fix_contributing
Fix typos in contributing doc
2022-09-15 20:23:36 +02:00
Magnus
5149760d22
Merge pull request #103 from jinjorge/doc_fix_data_doc
Typo fixes in data doc
2022-09-15 20:23:09 +02:00
Magnus
403719073b
Merge pull request #104 from jinjorge/doc_fix_formula
Typo fixes in formula doc
2022-09-15 20:22:32 +02:00
Magnus
dd3a4e5742
Merge pull request #106 from jinjorge/doc_fix_hardware
Typo fix for hardware doc
2022-09-15 20:18:52 +02:00
Magnus
2e27ec78d6
Merge pull request #108 from jinjorge/doc_fix_installation
Typo fixes in installation doc
2022-09-15 20:15:43 +02:00
Magnus
d5dae6a40d
Merge pull request #109 from jinjorge/doc_fx_intro
Typo fixes in intro doc
2022-09-15 20:15:06 +02:00
Magnus
3b716e5499
Merge pull request #110 from jinjorge/doc_fix_q_and_a
Typo fixes in question and answer doc
2022-09-15 20:14:32 +02:00
Magnus
2aebae59b3
Merge pull request #111 from jinjorge/doc_fix_releases
Typo fixes to release notes doc
2022-09-15 20:13:38 +02:00
Magnus
eb4b975407
Merge pull request #112 from jinjorge/doc_fix_services
Typo fixes in services doc
2022-09-15 20:12:49 +02:00
Magnus
1f25e3ac50
Merge pull request #113 from jinjorge/doc_fix_troubleshooting
Typo fixes in troubleshooting doc
2022-09-15 20:11:43 +02:00
Jeremiah Njoroge
f1a608d060 Typo fixes in troubleshooting doc 2022-09-15 11:00:27 -07:00
Jeremiah Njoroge
c33326e6a4 Typo fixes in services doc 2022-09-15 10:57:47 -07:00
Jeremiah Njoroge
1afb5ed604 Typo fixes to release notes doc 2022-09-15 10:52:00 -07:00
Jeremiah Njoroge
823d8ca39e Typo fixes in question and answer doc 2022-09-15 10:42:46 -07:00
Jeremiah Njoroge
5a11ab84db Typo fixes in intro doc 2022-09-15 10:39:25 -07:00
Jeremiah Njoroge
5378d4c700 Typo fixes in installation doc 2022-09-15 10:36:51 -07:00
Jeremiah Njoroge
c10abe2087 Typo fixes in index doc 2022-09-15 10:32:39 -07:00
Jeremiah Njoroge
ec3ef06826 Typo fix for hardware doc 2022-09-15 10:26:55 -07:00
Jeremiah Njoroge
b9273b7b79 Typo fixes in functionality doc 2022-09-15 10:22:29 -07:00
Jeremiah Njoroge
6b649070e1 Typo fixes in formula doc 2022-09-15 10:16:11 -07:00
Jeremiah Njoroge
291229255c Typo fixes in data doc 2022-09-15 10:13:40 -07:00
Jeremiah Njoroge
2747484775 Fix typos in contributing doc 2022-09-15 10:11:04 -07:00
Jeremiah Njoroge
ecf0c08f47 Typo fixes for configuration documentation 2022-09-15 10:06:48 -07:00
Jeremiah Njoroge
eb82bf526d Typo fixes 2022-09-14 23:35:48 -07:00
Jeremiah Njoroge
b45ef627a0 Typo fixes for api doc 2022-09-14 23:30:03 -07:00
GitHub Action
d1b94ce2b0 GitHub Action Build 2022-08-21 17:59:36 +00:00
Magnus Persson
d4b78a0e1b Showing wifi ssid in config 2022-08-21 18:48:08 +02:00
Magnus Persson
646d8f8d12 Added copy button to docs 2022-08-21 18:39:46 +02:00
GitHub Action
059865f271 GitHub Action Build 2022-08-14 12:27:26 +00:00
Magnus Persson
a9deb588aa Fixed error printout for calibration errors 2022-08-14 14:23:23 +02:00
GitHub Action
0de8971255 GitHub Action Build 2022-08-14 11:59:10 +00:00
Magnus Persson
08a102159b Increased file eror log to 4k 2022-08-14 13:54:29 +02:00
GitHub Action
8b13b4769c GitHub Action Build 2022-08-14 08:59:57 +00:00
Magnus Persson
c3044f9878 Updated 1.1 docs 2022-08-14 10:54:43 +02:00
Magnus Persson
0a4e7f37b2 Gen .map always 2022-08-09 20:28:52 +02:00
Magnus Persson
69f4b8ec3d Doc updates 2022-08-09 20:25:17 +02:00
Magnus Persson
d4c88c1200 Updated docs 2022-08-06 09:17:10 +02:00
Magnus Persson
4bbfb77361 Fix pre-commit 2022-08-06 08:55:52 +02:00
Magnus Persson
655235fc4e Added log message to webserver 2022-08-06 08:52:54 +02:00
Magnus Persson
4b0e5b393a Storage mode deepSleep forever 2022-08-05 22:50:03 +02:00
Magnus Persson
db590f8f5f Updated docs 2022-08-05 22:49:44 +02:00
Magnus Persson
4c805fe235 Added crash detector 2022-08-05 18:17:10 +02:00
GitHub Action
34a1d7d3c3 GitHub Action Build 2022-08-05 09:21:15 +00:00
Magnus Persson
bcee0e21da Corrected error code from mqtt push 2022-08-05 11:16:58 +02:00
GitHub Action
9fc5ab147d GitHub Action Build 2022-08-05 08:17:37 +00:00
Magnus Persson
b3a89a1fcc Fix javascript error in format 2022-08-05 10:13:20 +02:00
GitHub Action
03b58eb112 GitHub Action Build 2022-08-05 08:00:11 +00:00
Magnus Persson
63ddcfbb57 Fixed compiler error on esp32 + release notes 2022-08-05 09:55:47 +02:00
Magnus Persson
68d44a5846 Fixes for new tpl engine, optimize memory. 2022-08-05 09:38:10 +02:00
Magnus Persson
45294f6b07 Using strncat instead 2022-08-04 16:17:24 +02:00
Magnus Persson
09d8226af1 Bump beta4 2022-08-04 16:12:28 +02:00
Magnus Persson
6c94c6b204 Precommit fixes 2022-08-04 16:12:14 +02:00
Magnus Persson
9e437d1e0d Refactored error logger to reduce memory footprint 2022-08-04 08:31:10 +02:00
Magnus Persson
a486f1be30 Updated code for gyro error to UI 2022-08-03 17:22:26 +02:00
Magnus Persson
0536b14280 Updated docs for beta3 2022-08-02 13:42:39 +02:00
Magnus Persson
a6bff7287e Added "not found" messages for gyro/temp sensor. 2022-08-02 13:02:01 +02:00
GitHub Action
414d7d51c2 GitHub Action Build 2022-08-02 06:28:23 +00:00
Magnus Persson
640733f143 Added drop down menu, fixed bus in ha workflow 2022-08-02 08:24:08 +02:00
GitHub Action
f6196e6dbf GitHub Action Build 2022-08-01 20:33:24 +00:00
Magnus Persson
f857dab3c0 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-08-01 22:28:48 +02:00
Magnus Persson
787c5c09bb Updated workflow for HA registration 2022-08-01 22:28:17 +02:00
GitHub Action
afe3ec2412 GitHub Action Build 2022-08-01 19:30:38 +00:00
Magnus Persson
980099a5e5 Updated clang format 2022-08-01 21:26:20 +02:00
Magnus Persson
99d577d4a0 Updated release notes and error message 2022-08-01 21:25:21 +02:00
Magnus Persson
6db6e96d90 Fixed crash after firmware update 2022-08-01 16:16:24 +02:00
Magnus Persson
92df08baa2 Updated format template 2022-08-01 11:26:10 +02:00
Magnus Persson
5466550f68 Updated format templates 2022-08-01 11:25:55 +02:00
Magnus Persson
94f703b087 Bump to beta3 2022-08-01 11:06:14 +02:00
Magnus Persson
57e078986b Upd release notes 2022-08-01 11:05:53 +02:00
Magnus Persson
e445e7649c Refactor format read to handle large payload 2022-08-01 11:03:51 +02:00
Magnus Persson
a5f7f0f8a4 Updated release notes for beta 2 2022-07-29 08:37:34 +02:00
GitHub Action
7826e56d8a GitHub Action Build 2022-07-27 22:03:51 +00:00
Magnus Persson
353d6d77e1 Minor fixes for new features 2022-07-27 23:58:25 +02:00
Magnus Persson
437873489e Added ver and build into templating 2022-07-27 13:11:17 +02:00
GitHub Action
e34e73fae2 GitHub Action Build 2022-07-27 06:49:27 +00:00
Magnus
69a4c5607c Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-07-27 08:44:48 +02:00
Magnus Persson
d980fdae1a Adding sw ver to template engine 2022-07-27 08:44:32 +02:00
GitHub Action
272e349375 GitHub Action Build 2022-07-27 06:18:09 +00:00
Magnus Persson
34d46ef768 Updated error message 2022-07-27 08:12:44 +02:00
GitHub Action
588ff2d1e0 GitHub Action Build 2022-07-27 06:07:51 +00:00
Magnus Persson
5d1811d240 Updated config view 2022-07-27 08:02:19 +02:00
GitHub Action
85e602d29b GitHub Action Build 2022-07-27 05:56:41 +00:00
Magnus Persson
8ee882c522 Adjusted error handling and formula deviation 2022-07-27 07:52:03 +02:00
GitHub Action
51e7ee6867 GitHub Action Build 2022-07-27 05:48:54 +00:00
Magnus Persson
70858ef841 Updated error msg 2022-07-27 07:45:02 +02:00
GitHub Action
83bd80ba28 GitHub Action Build 2022-07-27 05:43:50 +00:00
Magnus Persson
d33576b2df Increase size of buffer 2022-07-27 07:39:31 +02:00
GitHub Action
5c9525c0c5 GitHub Action Build 2022-07-26 19:28:17 +00:00
Magnus Persson
ad8704fc20 Changed error message in formula validation 2022-07-26 21:23:50 +02:00
Magnus Persson
f1c1958f88 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-07-26 21:11:24 +02:00
Magnus Persson
e5394113e4 Updated HA integration 2022-07-26 21:11:16 +02:00
GitHub Action
6d4e713da8 GitHub Action Build 2022-07-23 07:08:20 +00:00
Magnus
36a858af2d Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-07-23 09:00:46 +02:00
Magnus Persson
77d2c15e39 Updated docs for v1.1 beta 2 2022-07-23 08:58:48 +02:00
GitHub Action
9df072cc78 GitHub Action Build 2022-07-07 10:25:06 +00:00
Magnus Persson
bedbda4662 Made cal temp adjustable in UI 2022-07-07 11:11:45 +02:00
Magnus Persson
76702dfc95 Fix #90 2022-07-07 10:13:17 +02:00
Magnus
789eb32aa8 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-07-06 09:44:11 +02:00
Magnus Persson
c0db4dd8da Precommit format 2022-07-06 09:43:48 +02:00
GitHub Action
cb677e10ae GitHub Action Build 2022-07-06 07:14:25 +00:00
Magnus Persson
b4c7566a08 Bump docs version 2022-07-06 09:10:23 +02:00
Magnus Persson
70f391fa1a Moved code for storage mode to apply in loop 2022-07-06 09:10:10 +02:00
Magnus Persson
542beffe4c Added brewblox format template 2022-07-06 09:09:18 +02:00
Magnus
1a42bd332f Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-07-05 18:11:22 +02:00
GitHub Action
c16108352c GitHub Action Build 2022-07-05 16:07:32 +00:00
Magnus Persson
4b5df951de Precommit fix 2022-07-05 18:02:47 +02:00
Magnus Persson
6bbb904427 Fixed header case 2022-07-05 18:00:45 +02:00
Magnus Persson
66c6c44a38 precommit fixes 2022-07-05 17:58:14 +02:00
Magnus Persson
e2fee1fb35 Updated esp32 fwk 2022-07-05 17:55:05 +02:00
Magnus Persson
ae595ff50c Updated one wire 2022-07-05 17:50:51 +02:00
Magnus Persson
50257e2805 Updated mpu libraries 2022-07-05 17:50:33 +02:00
Magnus Persson
c503ad88a9 Removed serial debug 2022-07-05 17:43:59 +02:00
Magnus Persson
5b7290c991 Remove warning 2022-07-04 09:51:49 +02:00
Magnus Persson
f7c43dad55 Checked in wifi libs to remove unwanted deps. 2022-07-04 09:47:49 +02:00
Magnus Persson
95654bac66 Updated docs 2022-07-04 09:46:31 +02:00
Magnus Persson
ffa7ac294d Set limits on voltage config 2022-07-04 09:14:10 +02:00
Magnus Persson
79a3274286 Made config voltage configurable 2022-07-03 23:46:44 +02:00
Magnus Persson
0c936cfb88 merge master 2022-07-03 19:50:22 +02:00
Magnus Persson
f366b78cb3 merge master 2022-07-03 19:49:28 +02:00
Magnus Persson
702eba515d Updated history 2022-07-03 19:44:56 +02:00
Magnus Persson
a992e90bc3 Fix #84 2022-07-03 13:20:24 +02:00
Magnus Persson
8b4b89ba20 Fixed storage deep sleep for esp32 2022-07-02 10:45:10 +02:00
Magnus Persson
361d287a22 Updated brewblox integration 2022-07-02 09:52:14 +02:00
Magnus Persson
8e36453b13 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-07-01 21:11:42 +02:00
Magnus Persson
ded06d15a1 Added storage mode 2022-07-01 21:11:10 +02:00
Magnus Persson
67f60b817e Attempting to fix broken docs build 2022-07-01 09:42:25 +02:00
Magnus Persson
6373923506 Doc build pip upgrade 2022-07-01 09:30:16 +02:00
Magnus Persson
bf3a3de207 Added brewblox 2022-07-01 09:22:16 +02:00
GitHub Action
e6fd027d51 GitHub Action Build 2022-06-11 14:03:00 +00:00
Magnus Persson
0cba58a0dd Bump library versions 2022-06-11 15:56:38 +02:00
Magnus
fd71a2d428
Create stale.yaml 2022-06-04 14:40:20 +02:00
Magnus Persson
907e33ce2d Updated ubidots docs 2022-05-26 08:55:25 +02:00
Magnus Persson
18ba0b225f Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-05-26 08:38:37 +02:00
GitHub Action
2a9472b453 GitHub Action Build 2022-05-25 20:42:49 +00:00
Magnus Persson
0b23a0f69e Updated rel notes 2022-05-25 22:39:46 +02:00
Magnus Persson
9cba1db4e5 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-05-25 22:38:23 +02:00
Magnus Persson
9d5d6a5a58 Added brewfather ispindle copy format 2022-05-25 22:38:11 +02:00
GitHub Action
58fe408803 GitHub Action Build 2022-05-25 18:54:46 +00:00
Magnus Persson
c2ae70bb1a #64 fix format, bump ver 2022-05-25 20:50:22 +02:00
Magnus Persson
7a3b048d80 Fixed errors in docs 2022-05-18 07:12:37 +02:00
Magnus
ad3fbb7270 Merge branch 'master' into dev 2022-05-16 19:20:14 +02:00
Magnus Persson
02df656343 Added qa on formula creation 2022-05-16 19:19:00 +02:00
GitHub Action
f73f63fec3 GitHub Action Build 2022-05-16 06:09:36 +00:00
Magnus
bb09072520 Upd pre-commit 2022-05-16 08:04:26 +02:00
GitHub Action
5875ee83d8 GitHub Action Build 2022-05-15 08:31:09 +00:00
Magnus Persson
6185d67d12 Fix pre-commit fail 2022-05-15 10:26:21 +02:00
Magnus Persson
00f82f5c37 Added pre-commit action 2022-05-15 10:24:02 +02:00
Magnus Persson
59ad285bb8 Updated pre-commit hooks 2022-05-15 10:23:01 +02:00
Magnus Persson
bc09d617fc Force g0 to water 2022-05-15 10:22:49 +02:00
Magnus Persson
a71f54b99f Brewpiless docs 2022-05-15 10:05:06 +02:00
Magnus Persson
152ff89bf6 Set rfd grav to 1 2022-05-15 09:55:01 +02:00
Magnus Persson
d9fb8291f4 Merge branch 'master' of https://github.com/mp-se/gravitymon 2022-05-10 18:43:22 +02:00
Magnus Persson
030746e982 Updated readme 2022-05-10 18:42:19 +02:00
GitHub Action
d4260c6380 GitHub Action Build 2022-05-10 10:11:48 +00:00
Magnus
50725407ea Bump v1.0 2022-05-10 12:06:05 +02:00
Magnus
b4bac17114
Update issue templates 2022-05-09 20:44:42 +02:00
GitHub Action
cb433a4a91 GitHub Action Build 2022-05-09 17:23:12 +00:00
Magnus Persson
095c1dc6a7 Adding http to keep-alive 2022-05-09 19:18:44 +02:00
Magnus Persson
43e2d165f5 Fix link error 2022-05-08 09:59:39 +02:00
Magnus Persson
fca7294b61 Updated docs 2022-05-08 09:58:26 +02:00
Magnus Persson
390c0882d1 Applied pre-commit on v1.0 source 2022-05-08 09:54:25 +02:00
Magnus Persson
5e9e705b96 #6 contribution 2022-05-08 09:50:45 +02:00
Magnus Persson
d67f72f123 #6 added contribution 2022-05-08 09:50:27 +02:00
Magnus Persson
f09aadaf66 #69 added schema for esp32 2022-05-08 09:22:52 +02:00
GitHub Action
c68f67a558 GitHub Action Build 2022-05-06 16:11:31 +00:00
Magnus
299e915bd2 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-05-06 18:06:25 +02:00
Magnus Persson
06a1541090 Updated docs for 1.0 2022-05-06 18:05:52 +02:00
Magnus Persson
a8b87140b2 #76 formula creation 2022-05-06 17:39:54 +02:00
GitHub Action
1d738a14dd GitHub Action Build 2022-05-06 14:56:02 +00:00
Magnus Persson
4e37b9329c Updated esp32 code 2022-05-06 16:48:42 +02:00
Magnus Persson
9712e13c78 Updated release notes 2022-05-05 19:12:56 +02:00
GitHub Action
1b11e49883 GitHub Action Build 2022-05-05 16:49:10 +00:00
Magnus Persson
cc6fecbdf8 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-05-05 18:44:35 +02:00
Magnus Persson
971b210438 Fixed error message 2022-05-05 18:44:16 +02:00
Magnus Persson
7b2a99c8a3 #73 add support for MPU6500 2022-05-05 17:52:26 +02:00
GitHub Action
d6f8ff67a3 GitHub Action Build 2022-05-04 14:42:28 +00:00
Magnus Persson
f173b205ae Fix compiler error for esp32 2022-05-04 16:38:39 +02:00
Magnus Persson
e0312ab3c5 Update web pages 2022-05-04 16:31:49 +02:00
Magnus Persson
7678bb1f43 Remove zero length files at startup 2022-05-04 16:31:20 +02:00
Magnus Persson
403ed1d350 #72 lost wifi config 2022-05-04 16:31:02 +02:00
GitHub Action
ced4c9f8fc GitHub Action Build 2022-05-01 16:11:44 +00:00
Magnus Persson
7e1862390e Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-05-01 18:07:22 +02:00
Magnus Persson
a3cd3217ab #63 tilt validation 2022-05-01 18:07:16 +02:00
Magnus Persson
7ede9a6d19 Reserved point for water 2022-04-30 19:37:30 +02:00
Magnus Persson
f27e8ac79f Added function to collect support cfg 2022-04-30 19:30:09 +02:00
Magnus Persson
7874f1bcf2 Bump to beta2 2022-04-30 17:46:55 +02:00
Magnus Persson
9f87bf5432 Adjust advanced settings 2022-04-30 17:46:32 +02:00
Magnus Persson
5bbb7ebfaa Hide wifi pass2 2022-04-30 17:46:09 +02:00
Magnus Persson
328e7e71ae Added mqtt errors to docs 2022-04-30 07:09:08 +02:00
GitHub Action
2912749f19 GitHub Action Build 2022-04-29 18:14:13 +00:00
Magnus Persson
43b6881477 Upd release notes 2022-04-29 20:08:58 +02:00
Magnus Persson
3a4cfb1ca5 Pre update of docs for v1.0 2022-04-29 20:08:02 +02:00
Magnus Persson
a40c3528d4 Update release notes 2022-04-29 19:10:49 +02:00
Magnus Persson
87036d56e5 Moved sensor bits to loop 2022-04-29 19:07:48 +02:00
GitHub Action
e6508b639d GitHub Action Build 2022-04-29 14:46:43 +00:00
Magnus Persson
1fb8541be6 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-04-29 16:42:32 +02:00
Magnus Persson
911b08cd73 Tag build as beta1 2022-04-29 16:42:04 +02:00
Magnus Persson
7d9228f9d8 #67 2022-04-29 16:41:44 +02:00
Magnus Persson
891794af7c Added error code -100 2022-04-29 08:59:37 +02:00
Magnus Persson
56cd730ad4 Removed gyro delay from api 2022-04-29 06:56:38 +02:00
GitHub Action
5a9169be64 GitHub Action Build 2022-04-29 04:52:45 +00:00
Magnus Persson
c45a5174ae Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-04-29 06:48:40 +02:00
Magnus Persson
396608bd7e #65 #58 2022-04-29 06:48:17 +02:00
Magnus Persson
31dc2bae5f Pushing perf-data to influx ssl not supported 2022-04-28 21:28:26 +02:00
Magnus Persson
dfd5cea53d Disable adv config by default 2022-04-28 20:14:16 +02:00
GitHub Action
41507f2bd4 GitHub Action Build 2022-04-28 18:05:57 +00:00
Magnus Persson
304903564d #62 2022-04-28 20:01:45 +02:00
GitHub Action
8dcadf4240 GitHub Action Build 2022-04-27 20:25:31 +00:00
Magnus Persson
b1474d19c4 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-04-27 22:21:04 +02:00
Magnus Persson
d8cb4fe622 Merge branch 'bs5' into dev 2022-04-27 22:19:24 +02:00
Magnus Persson
e30e3b2cb5 Merge branch 'master' into dev 2022-04-27 22:19:14 +02:00
Magnus Persson
e91c8af1a5 moved maxfrag to own function 2022-04-27 22:15:35 +02:00
Magnus Persson
8342341cb9 Specified esp32 fwk ver 2022-04-27 22:14:07 +02:00
Magnus Persson
149124fc34 bump doc ver 2022-04-26 20:52:25 +02:00
Magnus Persson
1761ed47ba Updated release notes 2022-04-26 20:13:35 +02:00
Magnus Persson
1dd4c541b0 Merge branch 'master' into bs5 2022-04-26 20:05:57 +02:00
Magnus Persson
446cb61e1c Updated BF docs to clarify SG 2022-04-26 20:00:26 +02:00
Magnus Persson
2f01222582 Merge branch 'dev' into bs5 2022-04-26 19:55:16 +02:00
Magnus Persson
fa3d68c321 Added wifi advanced config 2022-04-26 19:54:48 +02:00
Magnus Persson
0fe9bb146d Merge branch 'master' into bs5 2022-04-26 19:52:44 +02:00
Magnus Persson
d4df0dd272 Fixed bug with rssi key in json 2022-04-26 16:42:13 +02:00
Magnus
c319afbe9f Added tooltips 2022-04-26 16:24:33 +02:00
Magnus Persson
cf143c0e73 Added advanced settings 2022-04-25 21:41:00 +02:00
Magnus Persson
902d123a68 Added SSL support for influxdb 2022-04-24 22:35:10 +02:00
Magnus Persson
b359f3aba8 Bump ver 2022-04-24 22:06:47 +02:00
Magnus Persson
fb856dde75 Added used repos to about 2022-04-24 22:05:55 +02:00
Magnus Persson
c0f7cf2823 Formula fixes 2022-04-24 22:05:40 +02:00
Magnus Persson
d208b11384 Updated release notes 2022-04-24 18:55:50 +02:00
Magnus Persson
d83ef79165 Added copy function for standard formats 2022-04-24 18:54:16 +02:00
Magnus Persson
c685c18b57 Increasing calibration points to 10 2022-04-24 18:15:37 +02:00
Magnus Persson
7a84042781 Added gif animation of ui 2022-04-23 16:28:33 +02:00
Magnus Persson
14a73b011e Updated install instructions 2022-04-23 16:13:18 +02:00
Magnus Persson
121eeea392 Saving partitions.bin for esp32 2022-04-21 07:35:32 +02:00
GitHub Action
ed8dc68fc7 GitHub Action Build 2022-04-18 15:14:29 +00:00
Magnus Persson
3d4c04333e Added perf32 as target 2022-04-18 17:09:02 +02:00
Magnus Persson
5b267210d2 Added save for additional formula points 2022-04-11 09:27:40 +02:00
Magnus
e9742888f8 Updated html 2022-04-11 08:16:57 +02:00
Magnus Persson
5e1d2a73fa Started release notes for 1.0 2022-04-10 19:19:57 +02:00
Magnus Persson
7eddf35b97 Added 3 more points for formula calculation 2022-04-10 19:13:50 +02:00
Magnus Persson
a648d54a14 Removed brewfather option 2022-04-10 15:47:44 +02:00
Magnus Persson
e9229efe56 Removed dev code 2022-04-10 15:43:15 +02:00
Magnus Persson
635d788ba6 Rough update to bootstrap 5 done 2022-04-10 15:33:00 +02:00
GitHub Action
2e3820ca73 GitHub Action Build 2022-04-09 12:09:24 +00:00
Magnus Persson
d52615f8e3 Update screendumps for 0.9 2022-04-09 14:05:18 +02:00
Magnus Persson
786b8e9b19 Update docs 2022-04-09 10:49:29 +02:00
GitHub Action
10fa71d7ca GitHub Action Build 2022-04-09 07:09:20 +00:00
Magnus Persson
aeda821396 Fixed bad link to firmware upload 2022-04-09 09:05:28 +02:00
GitHub Action
3db3968e07 GitHub Action Build 2022-04-09 07:00:17 +00:00
Magnus Persson
430f01943a Disable firmware upgrade if no file is selected 2022-04-09 08:56:59 +02:00
GitHub Action
ca97a586c1 GitHub Action Build 2022-04-08 18:19:45 +00:00
Magnus Persson
5c80b780a0 Added check for valid SSID 2022-04-08 20:15:25 +02:00
Magnus Persson
2e42b86444 Updated hw docs for esp32 2022-04-08 20:14:57 +02:00
Magnus Persson
acb53bf611 Updated voltage factor for esp32 2022-04-08 20:07:23 +02:00
GitHub Action
5b99304d7f GitHub Action Build 2022-04-07 18:40:31 +00:00
Magnus Persson
d94028a7b9 Updated docs 2022-04-07 20:37:35 +02:00
Magnus Persson
f4ca86e8ff Added link to firmware updata 2022-04-07 20:36:39 +02:00
GitHub Action
2c9f5c72f2 GitHub Action Build 2022-04-07 14:56:20 +00:00
Magnus Persson
f5aae4f2ea Fixing temp adj in F correcly 2022-04-07 16:52:12 +02:00
GitHub Action
51daa23327 GitHub Action Build 2022-04-06 18:21:08 +00:00
Magnus
a5fb4f40b9 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-04-06 20:17:46 +02:00
Magnus Persson
17e9b0d51b added upgrade instructions 2022-04-06 20:17:26 +02:00
GitHub Action
2877f91ef8 GitHub Action Build 2022-04-06 17:56:29 +00:00
Magnus Persson
8dd509214b Added git revision on UI 2022-04-06 19:52:56 +02:00
GitHub Action
223ab7f81e GitHub Action Build 2022-04-06 13:50:10 +00:00
Magnus
b6ba01f6e0 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-04-06 15:46:32 +02:00
Magnus Persson
75e9d178a3 Added bug to release notes 2022-04-06 15:44:26 +02:00
Magnus Persson
e8740364cf Adding leading zero to id 2022-04-06 15:43:01 +02:00
GitHub Action
53f1373432 GitHub Action Build 2022-04-05 15:51:46 +00:00
Magnus
40e7a37aa5 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-04-05 17:47:32 +02:00
Magnus Persson
24b2446521 Added perf target for esp32 2022-04-05 17:47:06 +02:00
GitHub Action
82f48604a2 GitHub Action Build 2022-04-05 07:10:29 +00:00
Magnus Persson
f5b627616e Updated docs with ispindel web interface 2022-04-05 09:06:38 +02:00
GitHub Action
dc4eb4f4a1 GitHub Action Build 2022-04-05 06:38:20 +00:00
Magnus Persson
52785871b9 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-04-05 08:35:06 +02:00
Magnus Persson
9c92bb9214 Update firmware htm 2022-04-05 08:35:00 +02:00
GitHub Action
96295f161a GitHub Action Build 2022-04-05 06:32:51 +00:00
Magnus Persson
50116e8b45 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-04-05 08:29:27 +02:00
Magnus Persson
29b243f115 Esp32 support for firmware upload 2022-04-04 23:32:41 +02:00
Magnus Persson
c779a45ea9 pre-commit updates 2022-04-04 23:16:24 +02:00
Magnus Persson
2d67a44ad0 Added firmware upload page 2022-04-04 23:03:36 +02:00
Magnus Persson
8d44a5dcea Moved temp sensor code to correct api 2022-04-04 09:47:20 +02:00
Magnus Persson
3d3293138f Showing mDNS on wifi config page 2022-04-04 08:32:13 +02:00
Magnus Persson
cf3e683137 Fix rounding error for temp sensor adj 2022-04-04 08:08:14 +02:00
Magnus Persson
f2b926cce6 Temp sensor adj when in F 2022-04-03 20:28:59 +02:00
Magnus Persson
1494b2b3b2 iSpindle temp format error 2022-04-03 20:01:12 +02:00
GitHub Action
474f987e73 GitHub Action Build 2022-04-01 06:45:57 +00:00
Magnus Persson
294b7a7fdd Added remove led on esp32 2022-04-01 08:41:31 +02:00
Magnus Persson
fda5c6ff27 Enable blue LED when running on ESP32 2022-03-31 16:51:27 +02:00
GitHub Action
58144a5187 GitHub Action Build 2022-03-29 05:59:46 +00:00
Magnus
877afbc26a Merge branch 'master' into dev 2022-03-29 07:56:23 +02:00
GitHub Action
98a4c3650f GitHub Action Build 2022-03-27 20:37:11 +00:00
Magnus Persson
dcbedf5899 Fixed bug in upload api 2022-03-27 22:33:29 +02:00
Magnus
4fff5ad185
Merge pull request #43 from jinjorge/doc_fix
Minor edits to docs
2022-03-27 11:51:41 +02:00
Jeremiah Njoroge
d1f1e926e7 Minor edits to docs 2022-03-26 23:08:32 -07:00
GitHub Action
a20baa6b27 GitHub Action Build 2022-03-24 07:55:57 +00:00
Magnus Persson
42ca66555a Fixed test setup in htm 2022-03-24 08:52:37 +01:00
GitHub Action
13d5280d76 GitHub Action Build 2022-03-21 21:08:33 +00:00
Magnus Persson
ae4d5eb8a2 Showing only last 6 chars of git rev 2022-03-21 22:05:13 +01:00
GitHub Action
d6227b6dad GitHub Action Build 2022-03-21 20:46:07 +00:00
Magnus Persson
07fefe41fb Added git rev to build info 2022-03-21 21:42:44 +01:00
GitHub Action
dab4d0ed22 GitHub Action Build 2022-03-20 14:39:41 +00:00
Magnus Persson
faba3d7619 Build skip html files in version.json 2022-03-20 15:36:13 +01:00
Magnus Persson
8637b0f72d Fix crashbug in ota 2022-03-20 15:35:37 +01:00
Magnus Persson
169798e0eb Remove unused file 2022-03-19 21:26:23 +01:00
GitHub Action
04b2721c5d GitHub Action Build 2022-03-19 20:24:00 +00:00
Magnus Persson
14909b241b Bump to v0.9 2022-03-19 21:20:24 +01:00
GitHub Action
f8e72a50e9 GitHub Action Build 2022-03-18 18:29:43 +00:00
Magnus Persson
5d03a33253 Bump version 2022-03-18 19:25:38 +01:00
Magnus Persson
65983e638a Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-03-18 19:25:04 +01:00
Magnus Persson
aae29786bb Added estimated runtime on config 2022-03-18 19:24:28 +01:00
GitHub Action
b28797a79b GitHub Action Build 2022-03-16 18:08:46 +00:00
Magnus Persson
d4452e1d59 Added btn on test page 2022-03-16 19:05:11 +01:00
GitHub Action
3717561466 GitHub Action Build 2022-03-14 17:59:26 +00:00
Magnus Persson
4cad6f888f Added http get option to push 2022-03-14 18:55:12 +01:00
GitHub Action
6e9d562977 GitHub Action Build 2022-03-13 18:11:32 +00:00
Magnus Persson
3e6d698a40 Increased json buffer 2022-03-13 19:07:23 +01:00
Magnus Persson
dc70b250d8 Updated screenshots for 0.9 2022-03-13 19:07:12 +01:00
GitHub Action
276b311194 GitHub Action Build 2022-03-13 16:41:38 +00:00
Magnus Persson
2609bf840c Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-03-13 17:38:26 +01:00
Magnus Persson
fe3ca247b9 Split push config into 2 sections 2022-03-13 17:37:53 +01:00
GitHub Action
37a42aa2a1 GitHub Action Build 2022-03-13 10:00:37 +00:00
Magnus Persson
68dfacb07c Fixed bug in build script 2022-03-13 10:57:15 +01:00
Magnus Persson
a60abdbaa1 Merge branch 'master' into dev 2022-03-13 10:39:52 +01:00
Magnus Persson
ef3cc5b523 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-03-13 10:39:47 +01:00
Magnus Persson
99a57978fa Bump version 2022-03-13 10:38:08 +01:00
Magnus Persson
a2aaeb3f84 Format test using real data 2022-03-13 10:37:21 +01:00
Magnus Persson
2f8a324bfc Fixed issue with converstion when SG was 0 2022-03-13 10:37:01 +01:00
Magnus Persson
16e91ec4f5 Merged device and index pages in UI 2022-03-13 10:07:03 +01:00
GitHub Action
3407567568 GitHub Action Build 2022-03-12 18:52:27 +00:00
Magnus Persson
83aa1b2202 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-03-12 19:49:26 +01:00
Magnus Persson
6193f422e8 Added target with esp32 fwk 2.0 2022-03-12 19:49:04 +01:00
GitHub Action
a657f698b8 GitHub Action Build 2022-03-12 17:31:44 +00:00
Magnus Persson
3d497a1acc Dont start wifi on esp32 is not needed 2022-03-12 18:28:02 +01:00
Magnus Persson
4e190af499 Adjusted voltage factor on esp32 2022-03-12 17:28:41 +01:00
GitHub Action
f07f845dfb GitHub Action Build 2022-03-12 12:57:17 +00:00
Magnus Persson
f1936b6b0d Fix compile error on linux 2022-03-12 13:53:40 +01:00
Magnus Persson
aa4e3b5e8d Updated docs to 0.9 2022-03-12 10:37:02 +01:00
Magnus Persson
7cafedd9bf Added CR on errors in serial console. 2022-03-12 08:25:24 +01:00
Magnus Persson
1e44d9bd01 Fixed back link in format editor 2022-03-12 08:20:17 +01:00
Magnus Persson
1bc3abc9f0 Integrated test function 2022-03-12 08:20:02 +01:00
Magnus Persson
8d51c5ad12 Updated q&a 2022-03-11 22:53:58 +01:00
Magnus Persson
d9c467d54f Added test option for push targets 2022-03-11 22:50:12 +01:00
Magnus Persson
07e7cbee1c Update partition info 2022-03-11 15:11:22 +01:00
Magnus Persson
f5fcf42fbe Fix littlefs header name (case sensitive) 2022-03-11 10:36:26 +01:00
Magnus Persson
036e10cd5d Updated esp32 target, updated github actions 2022-03-11 08:14:46 +01:00
Magnus Persson
2d1317af0d Migrated to littlefs on esp32 + fix A0 pin 2022-03-10 11:11:39 +01:00
Magnus Persson
21fba0481c Added BLE on ESP32 target. 2022-03-10 11:10:54 +01:00
Magnus Persson
6dfe5a80fd Update wifimgr 2022-03-05 19:55:16 +01:00
Magnus Persson
c681619be8 Update wifimgr 2022-03-05 17:30:49 +01:00
Magnus Persson
02cb91e918 Update dbl resetdetector 2022-03-05 17:30:32 +01:00
GitHub Action
8a597162ae GitHub Action Build 2022-03-05 14:27:39 +00:00
Magnus Persson
f49f386569 Updated html for calibration 2022-03-05 15:25:48 +01:00
GitHub Action
008ad490a7 GitHub Action Build 2022-03-05 14:17:23 +00:00
Magnus Persson
3533ee8dac Show tdevice movement in ui 2022-03-05 15:14:46 +01:00
Magnus Persson
2b9abda873 Bump html version 2022-03-05 14:56:46 +01:00
GitHub Action
4bbb558c8b GitHub Action Build 2022-03-05 13:38:39 +00:00
Magnus Persson
8ebbc6559f Bump version 2022-03-05 14:36:15 +01:00
Magnus Persson
0af872e743 Updated docs for 0.8 2022-03-05 14:36:04 +01:00
Magnus Persson
c20f9a534a Remove sled 2022-02-14 21:49:07 +01:00
Magnus Persson
10ce1fc245 Adding moving info on html 2022-02-14 21:48:34 +01:00
Magnus Persson
b43874d151 Sorting graph data 2022-02-14 21:47:59 +01:00
GitHub Action
1428bec3da GitHub Action Build 2022-02-08 18:07:39 +00:00
Magnus Persson
f9791dd349 Bump vuild version 2022-02-08 19:05:29 +01:00
Magnus Persson
1a7f28413c Revert gyro change 2022-02-08 19:05:13 +01:00
GitHub Action
d22309bb2e GitHub Action Build 2022-02-06 20:32:15 +00:00
Magnus Persson
b901a12699 Removed trailing LF from error log 2022-02-06 21:29:58 +01:00
Magnus Persson
914b4125d8 Fixed tilt calulation error #29 2022-02-06 21:22:46 +01:00
Magnus Persson
95216ecc54 Fixed ESP32 build 2022-02-06 21:21:55 +01:00
GitHub Action
4d83bf8fce GitHub Action Build 2022-02-06 10:21:33 +00:00
Magnus Persson
e125ca4a10 Added data to upload api 2022-02-06 10:04:53 +01:00
GitHub Action
5880d3a6ba GitHub Action Build 2022-02-04 15:35:08 +00:00
Magnus Persson
cda3a87dd9 Docs updated 2022-02-04 16:22:27 +01:00
Magnus Persson
4bcacea9d7 Fixed hostname for min ssl buffer 2022-02-04 16:22:14 +01:00
Magnus Persson
838d062eea validation on header form 2022-02-03 21:48:11 +01:00
Magnus Persson
77cdbf7649 Updated bootstrap to 4.6.1 2022-02-03 20:33:20 +01:00
GitHub Action
f33a58cffe GitHub Action Build 2022-02-03 15:05:54 +00:00
Magnus Persson
7ca536b216 Changed from s to ms in post timeout 2022-02-03 16:03:39 +01:00
GitHub Action
f0ec352538 GitHub Action Build 2022-02-03 08:35:02 +00:00
Magnus Persson
e336633c38 Mem debug, Variable http timeout and min heapfrag 2022-02-03 09:32:09 +01:00
GitHub Action
a130ebd67d GitHub Action Build 2022-02-02 12:32:48 +00:00
Magnus Persson
4c789a8b37 Minor update 2022-02-02 13:30:48 +01:00
GitHub Action
7ab5f451f5 GitHub Action Build 2022-02-02 12:26:05 +00:00
Magnus Persson
545f274a47 Refactored to free up heap for SSL 2022-02-02 13:23:16 +01:00
GitHub Action
9bea54b703 GitHub Action Build 2022-02-01 15:46:41 +00:00
Magnus Persson
35333469c7 Added thingsspeak setup docs 2022-02-01 16:44:07 +01:00
Magnus Persson
a9e0d9290a Update release notes 2022-02-01 11:28:28 +01:00
Magnus Persson
e459ceb2fa Updated docs 2022-02-01 11:27:46 +01:00
Magnus Persson
2615debe35 Added prel calculation for estimated total runtime 2022-02-01 10:37:17 +01:00
Magnus Persson
6113a436b0 Added battery percentage to index page 2022-01-31 22:42:41 +01:00
Magnus Persson
2d5158465f Removed batt from device page 2022-01-31 22:32:52 +01:00
GitHub Action
3af52b5464 GitHub Action Build 2022-01-31 17:57:57 +00:00
Magnus Persson
c116d672d1 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-01-31 18:51:06 +01:00
Magnus Persson
e1cc54d188 Added token as option in UI #32 2022-01-31 18:50:47 +01:00
Magnus Persson
761d570d39 Added average runtime + voltage to device page 2022-01-31 18:29:27 +01:00
Magnus Persson
22ade61af8 Added runtime time logger 2022-01-30 22:54:48 +01:00
GitHub Action
928054458a GitHub Action Build 2022-01-30 18:23:44 +00:00
Magnus Persson
2e67bd1d57 Checking missing params in API #16 2022-01-30 19:21:32 +01:00
Magnus Persson
83d7aee944 Merge branch 'master' into dev 2022-01-30 17:29:36 +01:00
Magnus Persson
8bcd27a076 Fix bad link in docs 2022-01-30 17:29:12 +01:00
Magnus Persson
0912d672fe Merge branch 'master' into dev 2022-01-30 17:27:36 +01:00
Magnus Persson
01d7c8adb5 Merge branch 'dev' of https://github.com/mp-se/gravitymon into dev 2022-01-30 17:27:29 +01:00
Magnus Persson
69076d5878 Fixed bad doc_ref 2022-01-30 17:26:13 +01:00
Magnus Persson
3d939ad733 Merge branch 'master' into dev 2022-01-30 17:23:59 +01:00
Magnus Persson
f2112cb344 Added intro section to docs 2022-01-30 17:22:38 +01:00
GitHub Action
f88fb8241b GitHub Action Build 2022-01-30 13:57:05 +00:00
Magnus Persson
6eed5f143b Merged patch 0.7.1 into dev 2022-01-30 14:55:00 +01:00
GitHub Action
64e582d0e5 GitHub Action Build 2022-01-30 10:52:11 +00:00
Magnus Persson
5d0f02eb18 Update docs 2022-01-30 11:50:09 +01:00
Magnus Persson
fbc1eb4e31 Updated docs 2022-01-28 17:15:12 +01:00
GitHub Action
700f00f48d GitHub Action Build 2022-01-27 20:33:34 +00:00
Magnus Persson
9a2f86fed7 Updated docs for 0.8 2022-01-27 21:31:35 +01:00
Magnus Persson
63fd80e750 Added errlog, custom http headers 2022-01-27 14:00:12 +01:00
Magnus Persson
b106ebfa20 Extended length of http url 2022-01-25 21:57:22 +01:00
176 changed files with 9319 additions and 4430 deletions

31
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,31 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. ...
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Software:**
- Platform (esp8266, esp32)
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

17
.github/stale.yaml vendored Normal file
View File

@ -0,0 +1,17 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 14
# Issues with these labels will never be considered stale
exemptLabels:
- on-hold
- security
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View File

@ -4,20 +4,26 @@ on:
push: push:
branches: branches:
- master - master
#- dev
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: ammaraskar/sphinx-action@master - uses: ammaraskar/sphinx-action@master
with: with:
docs-folder: "src_docs/" docs-folder: "src_docs/"
pre-build-command: | pre-build-command: |
pip install sphinx_rtd_theme pip install --upgrade pip
pip install furo pip install sphinx==4.3.2
pip install docutils==0.16
pip install pygments==2.11.1
pip install furo==2022.1.2
pip install sphinx-copybutton
pip list
build-command: "sphinx-build -b html ./source ../docs" build-command: "sphinx-build -b html ./source ../docs"
- name: Commit documentation changes - name: Commit documentation changes
@ -34,9 +40,19 @@ jobs:
git config --local user.name "GitHub Action" git config --local user.name "GitHub Action"
git add . git add .
git commit -m "Update documentation" -a || true git commit -m "Update documentation" -a || true
# git push https://${{secrets.token}}@github.com/mp-se/gravitymon.git
# The above command will fail if no changes were present, so we ignore - uses: wangyucode/sftp-upload-action@v1.4.8
# the return code. with:
host: ${{ secrets.SFTP_HOST }}
port: 22
username: ${{ secrets.SFTP_USER }}
password: ${{ secrets.SFTP_PASSWD }}
forceUpload: false
localDir: 'docs/'
remoteDir: '/run/www/docs/'
exclude: ''
dryRun: false
- name: Push changes - name: Push changes
uses: ad-m/github-push-action@master uses: ad-m/github-push-action@master
with: with:

View File

@ -10,10 +10,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Cache pip - name: Cache pip
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: ~/.cache/pip path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
@ -21,13 +21,13 @@ jobs:
${{ runner.os }}-pip- ${{ runner.os }}-pip-
- name: Cache PlatformIO - name: Cache PlatformIO
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: ~/.platformio path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v3
- name: Install PlatformIO - name: Install PlatformIO
run: | run: |
@ -36,11 +36,9 @@ jobs:
git config --global advice.detachedHead false git config --global advice.detachedHead false
- name: Run PlatformIO - name: Run PlatformIO
#run: pio run -e gravity-release -e gravity-perf -e gravity-debug run: pio run -e gravity-release
run: pio run -e gravity-release -e gravity-perf
#run: pio run -e gravity-release
- uses: EndBug/add-and-commit@v7 # You can change this to use a specific version. https://github.com/marketplace/actions/add-commit - uses: EndBug/add-and-commit@v9 # You can change this to use a specific version. https://github.com/marketplace/actions/add-commit
with: with:
add: 'bin' add: 'bin'
author_name: GitHub Action author_name: GitHub Action

View File

@ -10,10 +10,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Cache pip - name: Cache pip
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: ~/.cache/pip path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
@ -21,7 +21,7 @@ jobs:
${{ runner.os }}-pip- ${{ runner.os }}-pip-
- name: Cache PlatformIO - name: Cache PlatformIO
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: ~/.platformio path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
@ -36,11 +36,9 @@ jobs:
git config --global advice.detachedHead false git config --global advice.detachedHead false
- name: Run PlatformIO - name: Run PlatformIO
#run: pio run -e gravity-release -e gravity-perf -e gravity-debug run: pio run -e gravity-release -e gravity32-release -e gravity32c3-release -e gravity32s2-release -e gravity32c3v1-release -e gravity32lite-release
run: pio run -e gravity-release -e gravity-perf
#run: pio run -e gravity-release
- uses: EndBug/add-and-commit@v7 # You can change this to use a specific version. https://github.com/marketplace/actions/add-commit - uses: EndBug/add-and-commit@v9 # You can change this to use a specific version. https://github.com/marketplace/actions/add-commit
with: with:
add: 'bin' add: 'bin'
author_name: GitHub Action author_name: GitHub Action

19
.github/workflows/pre-commit.yaml vendored Normal file
View File

@ -0,0 +1,19 @@
name: pre-commit
on:
pull_request:
push:
branches:
- master
- dev
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- name: clang format support
run: |
sudo apt install clang-format cppcheck
- uses: pre-commit/action@v3.0.0

7
.gitignore vendored
View File

@ -1,9 +1,6 @@
.pio/* .pio/*
.vscode/* .vscode/*
*.map *.map
test/*.md
test/env/*
test/configure_*.py
TODO.md
src_docs/_build/* src_docs/_build/*
data/*.min.htm .env/*
*.pyc

View File

@ -4,7 +4,10 @@ repos:
hooks: hooks:
- id: clang-format - id: clang-format
files: ^src/ files: ^src/
exclude: 'lib/'
- id: cpplint - id: cpplint
files: ^src/ files: ^src/
exclude: 'lib/'
- id: cppcheck - id: cppcheck
files: ^src/ files: ^src/
exclude: 'lib/'

35
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,35 @@
## How to contribute to GravityMon
#### **Did you find a bug?**
* **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/mp-se/gravitymon/issues). Dont forget to look under closed issues. There might be a fix but not yet included in the released version.
* If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/mp-se/gravitymon/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, Use the function on the device to extract configuration and device information (does not contain any sensitive data). This can help to pinpoint the issue.
#### **Did you write a patch that fixes a bug?**
* Open a new GitHub pull request with the patch.
* Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
* Before submitting, please use `pre-commit` to validate that your code contribution complies with the formatting standards for C++ and C.
* Check the contribution section under the documentation for additional information.
#### **Do you intend to add a new feature or change an existing one?**
* Suggest your change in the [Discussion forums](https://github.com/mp-se/gravitymon/discussions) and start writing code.
* Do not open an issue on GitHub until you have collected positive feedback about the change. GitHub issues are primarily intended for bug reports and fixes.
#### **Do you have questions about the source software?**
* Start a discussion in the [Discussion forums](https://github.com/mp-se/gravitymon/discussions) and start writing code.
#### **Do you want to contribute to the documentation?**
* Open a new GitHub pull request with the updated documentation changes.
* Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
Thanks!

View File

@ -3,30 +3,30 @@
![release](https://img.shields.io/github/v/release/mp-se/gravitymon?label=latest%20release) ![release](https://img.shields.io/github/v/release/mp-se/gravitymon?label=latest%20release)
![issues](https://img.shields.io/github/issues/mp-se/gravitymon) ![issues](https://img.shields.io/github/issues/mp-se/gravitymon)
![pr](https://img.shields.io/github/issues-pr/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) ![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/workflow/status/mp-se/gravitymon/Sphinx%20Build/dev?label=doc%20build) ![doc_build](https://img.shields.io/github/actions/workflow/status/mp-se/gravitymon/doc-build.yaml?branch=master)
# Gravity Monitor for Beer Brewing # Gravity Monitor for Beer Brewing
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 Logo](src_docs/source/images/gravitymon_logo_s.png)
Installation can be made using https://www.brewflasher.com 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.
The main differences: Now also works with ESP32 d1 mini, ESP32 c3 mini, ESP32 S2 mini which both are pin compatible with ESP8266.
---------------------
* Operates in two modes gravity monitoring and configuration mode (simplify calibration) Installation can be made using https://www.brewflasher.com or the web version at https://web.brewflasher.com
* Modern web based UI for configuration (in config mode)
* REST API
* Send data to multiple endpoints when pushing data (2xhttp, brewfather, influxdb v2, mqtt supported)
* Automatic temperature adjustment of gravity reading
* OTA support from local webserver
* Built in function to create gravity formulas, no need for additional software, just enter tilt/gravity.
* Visual graph showing how formula will be interpreted
* Using the temperature sensor in gyro instead of DS18B20 (faster)
* Built in performance measurements (used to optimise code)
* SSL support in standard HTTP and MQTT connections.
No code has been reused from the iSpindle project. The documentation can be found here: https://www.gravitymon.com/docs.html
The documenation can be found here: https://mp-se.github.io/gravitymon/index.html Visit the gravitymon homepage here for more information about the project: https://www.gravitymon.com
If you want to support my work you can do that through these options
[<img src="https://gravitymon.com/images/buymecoffee.png" height=40>](https://www.buymeacoffee.com/mpse/) [<img src="https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86" height=40>](https://github.com/sponsors/mp-se)
# Supporters
Thanks to the following persons for supporting me and this project:
* 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.

View File

@ -1 +0,0 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item active"><a class="nav-link" href="/about.htm">About</a></li></ul></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="row mb-3"><h3>Beer Gravity Monitor</h3>This is a piece of software for the iSpindle hardware and will work in a similar way. No part of this software is copied from the iSpindle project.</div><div class="row mb-3"><h3>MIT License</h3>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</div><hr class="my-4"></div><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item active"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Current version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3" id="h-app-ver-new" hidden><div class="col-md-8 themed-grid-col bg-light">New version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver-new">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Host name:</div><div class="col-md-4 themed-grid-col bg-light" id="mdns">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Device ID:</div><div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div></div><hr class="my-4"></div><script type="text/javascript">function getConfig(){var n="/api/device";$("#spinner").show(),$.getJSON(n,function(n){console.log(n),$("#app-ver").text(n["app-ver"]+" (html 0.7.1)"),$("#mdns").text(n.mdns),$("#id").text(n.id)}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}window.onload=getConfig</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>

Binary file not shown.

Binary file not shown.

BIN
bin/firmware32.bin Normal file

Binary file not shown.

BIN
bin/firmware32c3.bin Normal file

Binary file not shown.

BIN
bin/firmware32c3v1.bin Normal file

Binary file not shown.

BIN
bin/firmware32lite.bin Normal file

Binary file not shown.

BIN
bin/firmware32s2.bin Normal file

Binary file not shown.

View File

@ -1,2 +0,0 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/config.htm">Back to configuration</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-2"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button></div><script type="text/javascript">function showError(s){$("#alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$("#alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$("#alert").addClass("d-none").removeClass("show")})</script><div class="accordion" id="accordion"><div class="card"><div class="card-header" id="headingOne"><h2 class="mb-0"><button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">Push Format Templates</button></h2></div><div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion"><div class="card-body"><input type="text" name="id" id="id" hidden> <input type="text" name="http-1" id="http-1" hidden> <input type="text" name="http-2" id="http-2" hidden><!--<input type="text" name="brewfather" id="brewfather" hidden>--> <input type="text" name="influxdb" id="influxdb" hidden> <input type="text" name="mqtt" id="mqtt" hidden><div class="form-group row"><label for="push-target" class="col-sm-2 col-form-label">Push target:</label> <select class="custom-select col-sm-4" required name="push-target" id="push-target"><option value="http-1">HTTP option 1</option><option value="http-2">HTTP option 2</option><!--<option value="brewfather">Brewfather</option>--><option value="influxdb">Influx DB</option><option value="mqtt">MQTT</option></select></div><div class="form-group row"><div class="col-sm-12"><textarea rows="5" class="form-control" name="format" id="format">
</textarea></div></div><div class="form-group row"><div class="col-sm-8 offset-sm-2"><button class="btn btn-primary" id="format-btn">Save</button> <button class="btn btn-secondary" id="test-btn">Test</button></div></div><pre class="card-preview" id="preview" name="preview"></pre></div></div></div><hr class="my-4"></div><script type="text/javascript">function setButtonDisabled(l){$("#format-btn").prop("disabled",l),$("#test-btn").prop("disabled",l)}function selectFormat(){var l="#"+$("#push-target").val();console.log(l),l=decodeURIComponent($(l).val()),console.log(l),l=l.replaceAll("|","|\n"),console.log(l),$("#format").val(l),$("#preview").text("")}function getConfig(){setButtonDisabled(!0);var l="/api/config/format";$("#spinner").show(),$.getJSON(l,function(l){console.log(l),$("#id").val(l.id),$("#http-1").val(l["http-1"]),$("#http-2").val(l["http-2"]),$("#influxdb").val(l.influxdb),$("#mqtt").val(l.mqtt),selectFormat()}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide(),setButtonDisabled(!1)})}window.onload=getConfig,setButtonDisabled(!0),$(document).ready(function(){null!=location.hash&&""!=location.hash&&($(".collapse").removeClass("in"),$(location.hash+".collapse").collapse("show"))}),$("#push-target").change(function(l){console.log(l),selectFormat()}),$("#format-btn").click(function(l){var e=$("#format").val();e=e.replaceAll("\n","");var t="id="+$("#id").val()+"&"+$("#push-target").val()+"="+encodeURIComponent(e);console.log(t),$.ajax({type:"POST",url:"/api/config/format",data:t,success:function(l){showSuccess("Format stored successfully."),getConfig()},error:function(l){showError("Unable to store format.")}})}),$("#test-btn").click(function(l){var e=$("#format").val();e=e.replaceAll("${mdns}","gravmon2"),e=e.replaceAll("${id}","e4a344"),e=e.replaceAll("${sleep-interval}","300"),e=e.replaceAll("${temp}","21.1"),e=e.replaceAll("${temp-c}","21.1"),e=e.replaceAll("${temp-f}","51.3"),e=e.replaceAll("${temp-unit}","C"),e=e.replaceAll("${battery}","3.86"),e=e.replaceAll("${rssi}","-76"),e=e.replaceAll("${run-time}","4.32"),e=e.replaceAll("${gravity}","1.044"),e=e.replaceAll("${gravity-sg}","1.044"),e=e.replaceAll("${gravity-plato}","9.5"),e=e.replaceAll("${gravity-unit}","G"),e=e.replaceAll("${corr-gravity}","1.044"),e=e.replaceAll("${corr-gravity-sg}","1.044"),e=e.replaceAll("${corr-gravity-plato}","9.5"),e=e.replaceAll("${angle}","54.5"),e=e.replaceAll("${tilt}","54.5");try{var t=JSON.parse(e);e=JSON.stringify(t,null,2)}catch(l){console.log("Not a javascript object!")}$("#preview").text(e)})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></div></body></html>

View File

@ -1 +0,0 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item active"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="" id="id" hidden></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Gravity:</div><div class="col-md-4 themed-grid-col bg-light" id="gravity">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Temperature:</div><div class="col-md-4 themed-grid-col bg-light" id="temp">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Angle/Tilt:</div><div class="col-md-4 themed-grid-col bg-light" id="angle">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Battery:</div><div class="col-md-4 themed-grid-col bg-light" id="battery">Loading...</div></div><div class="row mb-3"><div class="col-md-12 px-md-5 themed-grid-col bg-light custom-control custom-checkbox"><input type="checkbox" class="custom-control-input" name="sleep-mode" id="sleep-mode" disabled> <label class="custom-control-label" for="sleep-mode">Do not enter sleep mode when floating (check this if you are collecting angles/tilt for calibration).</label></div></div><hr class="my-4"></div><script type="text/javascript">function getStatus(){var e="/api/status";$("#spinner").show(),$.getJSON(e,function(e){console.log(e),$("#id").text(e.id),$("#angle").text(e.angle),"G"==e["gravity-format"]?$("#gravity").text(e.gravity+" SG"):$("#gravity").text(e.gravity+" °P"),$("#battery").text(e.battery+" V"),"C"==e["temp-format"]?$("#temp").text(e["temp-c"]+" C"):$("#temp").text(e["temp-f"]+" F"),e["sleep-mode"]?$("#sleep-mode").attr("checked",!0):$("#sleep-mode").attr("checked",!1),$("#sleep-mode").removeAttr("disabled")}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}function start(){setInterval(getStatus,3e3)}window.onload=start,$("#sleep-mode").click(function(e){console.log("Blocking sleep mode = "+$("#sleep-mode").is(":checked")),$.ajax({type:"POST",url:"/api/status/sleepmode",data:{id:$("#id").text(),"sleep-mode":$("#sleep-mode").is(":checked")},success:function(e){},error:function(e){showError("Could not update sleep mode for device.")}})})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>

BIN
bin/partitions32.bin Normal file

Binary file not shown.

BIN
bin/partitions32c3.bin Normal file

Binary file not shown.

BIN
bin/partitions32lite.bin Normal file

Binary file not shown.

BIN
bin/partitions32s2.bin Normal file

Binary file not shown.

View File

@ -1 +1 @@
{ "project":"gravmon", "version":"0.7.1", "html": [ "index.min.htm", "device.min.htm", "config.min.htm", "calibration.min.htm", "format.min.htm", "about.min.htm" ] } { "project":"gravmon", "version":"1.2.1", "html": [ ] }

View File

@ -5,53 +5,75 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content=""> <meta name="description" content="">
<title>Beer Gravity Monitor</title> <title>Beer Gravity Monitor</title>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script> <style>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script> .row-margin-10 { margin-top: 1.0em; }
</style>
</head> </head>
<body class="py-4"> <body class="py-4">
<!-- START MENU --> <!-- START MENU -->
<nav class="navbar navbar-expand-sm navbar-dark bg-primary"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarNav">
<div class="collapse navbar-collapse" id="navbar"> <ul class="navbar-nav">
<ul class="navbar-nav mr-auto">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a> <a class="nav-link" href="/index.htm">Home</a>
</li> </li>
<li class="nav-item"> <li class="nav-item dropdown">
<a class="nav-link" href="/device.htm">Device</a> <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
</li> Configuration
<li class="nav-item"> </a>
<a class="nav-link" href="/config.htm">Configuration</a> <ul class="dropdown-menu">
<li><a class="dropdown-item" href="/config.htm">Configuration</a></li>
<li><a class="dropdown-item" href="/format.htm">Format editor</a></li>
<li><a class="dropdown-item" href="/test.htm">Test push</a></li>
<li><a class="dropdown-item" href="/firmware.htm">Upload firmware</a></li>
</ul>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/calibration.htm">Calibration</a> <a class="nav-link" href="/calibration.htm">Calibration</a>
</li> </li>
<li class="nav-item active"> <li class="nav-item">
<a class="nav-link" href="/about.htm">About</a> <a class="nav-link active" href="#">About</a>
</li> </li>
</ul> </ul>
</div> </div>
</nav> <div class="spinner-border text-light" id="spinner" role="status"></div>
</div>
</nav>
<!-- START MAIN INDEX --> <!-- START BODY -->
<div class="container"> <div class="container row-margin-10">
<hr class="my-4">
<div class="row mb-3"> <div class="accordion row-margin-10" id="accordion">
<h3>Beer Gravity Monitor</h3> <div class="accordion-item">
<h2 class="accordion-header" id="headingAbout">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseAbout" aria-expanded="true" aria-controls="collapseAbout">
<b>About</b>
</button>
</h2>
<div id="collapseAbout" class="accordion-collapse collapse show" aria-labelledby="headingAbout" data-bs-parent="#accordion">
<div class="accordion-body">
<div class="row h3 col-sm-8">
Beer Gravity Monitor
</div>
<div class="row col-sm-8 mb-3">
This is a piece of software for the iSpindle hardware and will work in a similar way. No part of this software is copied from the iSpindle project. This is a piece of software for the iSpindle hardware and will work in a similar way. No part of this software is copied from the iSpindle project.
</div> </div>
<div class="row mb-3"> <div class="row h3 col-sm-8 mb-3">
<h3>MIT License</h3> MIT License
</div>
<div class="row col-sm-8 mb-3">
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
@ -71,11 +93,38 @@
SOFTWARE. SOFTWARE.
</div> </div>
<hr class="my-4"> <div class="row h3 col-sm-8 mb-3">
</div> Credits to
</div>
<div class="row col-sm-8 mb-3">
This software uses
the following libraries and without these this software would have been much more difficult to acheive:<br><br>
<ul>
<li>https://github.com/jrowberg/i2cdevlib</li>
<li>https://github.com/codeplea/tinyexpr</li>
<li>https://github.com/graphitemaster/incbin</li>
<li>https://github.com/khoih-prog/ESP_DoubleResetDetector</li>
<li>https://github.com/khoih-prog/ESP_WiFiManager</li>
<li>https://github.com/thijse/Arduino-Log</li>
<li>https://github.com/bblanchon/ArduinoJson</li>
<li>https://github.com/PaulStoffregen/OneWire</li>
<li>https://github.com/milesburton/Arduino-Temperature-Control-Library</li>
<li>https://github.com/Rotario</li>
<li>https://github.com/256dpi/arduino-mqtt</li>
<li>https://graphjs.com</li>
<li>https://getbootstrap.com</li>
<li>https://github.com/lorol/LITTLEFS</li>
<li>https://github.com/h2zero/NimBLE-Arduino</li>
<li>https://github.com/spouliot/tilt-sim</li>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- START FOOTER --> <!-- START FOOTER -->
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div> <div class="container themed-container bg-primary text-light row-margin-10">(C) Copyright 2021-22 Magnus Persson</div>
</body> </body>
</html> </html>

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item active"><a class="nav-link" href="/about.htm">About</a></li></ul></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="row mb-3"><h3>Beer Gravity Monitor</h3>This is a piece of software for the iSpindle hardware and will work in a similar way. No part of this software is copied from the iSpindle project.</div><div class="row mb-3"><h3>MIT License</h3>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</div><hr class="my-4"></div><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html> <!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"><style>.row-margin-10{margin-top:1em}</style></head><body class="py-4"><!-- START MENU --><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script><nav class="navbar navbar-expand-lg navbar-dark bg-primary"><div class="container"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbarNav"><ul class="navbar-nav"><li class="nav-item"><a class="nav-link" href="/index.htm">Home</a></li><li class="nav-item dropdown"><a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">Configuration</a><ul class="dropdown-menu"><li><a class="dropdown-item" href="/config.htm">Configuration</a></li><li><a class="dropdown-item" href="/format.htm">Format editor</a></li><li><a class="dropdown-item" href="/test.htm">Test push</a></li><li><a class="dropdown-item" href="/firmware.htm">Upload firmware</a></li></ul></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item"><a class="nav-link active" href="#">About</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START BODY --><div class="container row-margin-10"><div class="accordion row-margin-10" id="accordion"><div class="accordion-item"><h2 class="accordion-header" id="headingAbout"><button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseAbout" aria-expanded="true" aria-controls="collapseAbout"><b>About</b></button></h2><div id="collapseAbout" class="accordion-collapse collapse show" aria-labelledby="headingAbout" data-bs-parent="#accordion"><div class="accordion-body"><div class="row h3 col-sm-8">Beer Gravity Monitor</div><div class="row col-sm-8 mb-3">This is a piece of software for the iSpindle hardware and will work in a similar way. No part of this software is copied from the iSpindle project.</div><div class="row h3 col-sm-8 mb-3">MIT License</div><div class="row col-sm-8 mb-3">Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</div><div class="row h3 col-sm-8 mb-3">Credits to</div><div class="row col-sm-8 mb-3">This software uses the following libraries and without these this software would have been much more difficult to acheive:<br><br><ul><li>https://github.com/jrowberg/i2cdevlib</li><li>https://github.com/codeplea/tinyexpr</li><li>https://github.com/graphitemaster/incbin</li><li>https://github.com/khoih-prog/ESP_DoubleResetDetector</li><li>https://github.com/khoih-prog/ESP_WiFiManager</li><li>https://github.com/thijse/Arduino-Log</li><li>https://github.com/bblanchon/ArduinoJson</li><li>https://github.com/PaulStoffregen/OneWire</li><li>https://github.com/milesburton/Arduino-Temperature-Control-Library</li><li>https://github.com/Rotario</li><li>https://github.com/256dpi/arduino-mqtt</li><li>https://graphjs.com</li><li>https://getbootstrap.com</li><li>https://github.com/lorol/LITTLEFS</li><li>https://github.com/h2zero/NimBLE-Arduino</li><li>https://github.com/spouliot/tilt-sim</li></ul></div></div></div></div></div></div><!-- START FOOTER --><div class="container themed-container bg-primary text-light row-margin-10">(C) Copyright 2021-22 Magnus Persson</div></body></html>

View File

@ -5,35 +5,41 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content=""> <meta name="description" content="">
<title>Beer Gravity Monitor</title> <title>Beer Gravity Monitor</title>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script> <style>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script> .row-margin-10 { margin-top: 1.0em; }
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> </style>
</head> </head>
<body class="py-4"> <body class="py-4">
<!-- START MENU --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<nav class="navbar navbar-expand-sm navbar-dark bg-primary"> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarNav">
<div class="collapse navbar-collapse" id="navbar"> <ul class="navbar-nav">
<ul class="navbar-nav mr-auto">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a> <a class="nav-link" href="/index.htm">Home</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Configuration
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/config.htm">Configuration</a></li>
<li><a class="dropdown-item" href="/format.htm">Format editor</a></li>
<li><a class="dropdown-item" href="/test.htm">Test push</a></li>
<li><a class="dropdown-item" href="/firmware.htm">Upload firmware</a></li>
</ul>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/device.htm">Device</a> <a class="nav-link active" href="#">Calibration</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config.htm">Configuration</a>
</li>
<li class="nav-item active">
<a class="nav-link" href="/calibration.htm">Calibration</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/about.htm">About</a> <a class="nav-link" href="/about.htm">About</a>
@ -41,50 +47,42 @@
</ul> </ul>
</div> </div>
<div class="spinner-border text-light" id="spinner" role="status"></div> <div class="spinner-border text-light" id="spinner" role="status"></div>
</nav> </div>
</nav>
<!-- START MAIN INDEX --> <!-- START BODY -->
<div class="container"> <div class="container row-margin-10">
<div class="alert alert-success alert-dismissible hide fade d-none" role="alert">
<hr class="my-2"> <div id="alert"></div>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
<div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert">
<div id="alert-msg">...</div>
<button type="button" id="alert-btn" class="close" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
function showError( msg ) { function showError( msg ) {
$('.alert').removeClass('alert-success').addClass('alert-danger').removeClass('d-none').addClass('show') $('.alert').removeClass('alert-success').addClass('alert-danger').removeClass('hide').addClass('show').removeClass('d-none');
$('#alert-msg').text( msg ); $('#alert').text( msg );
} }
function showSuccess( msg ) { function showSuccess( msg ) {
$('.alert').addClass('alert-success').removeClass('alert-danger').removeClass('d-none').addClass('show') $('.alert').addClass('alert-success').removeClass('alert-danger').removeClass('hide').addClass('show').removeClass('d-none');
$('#alert-msg').text( msg ); $('#alert').text( msg );
} }
$("#alert-btn").click(function(e){ $("#alert-btn").click(function(e){
$('.alert').addClass('d-none').removeClass('show') $('.alert').addClass('hide').removeClass('show').addClass('d-none');
}); });
</script> </script>
<div class="accordion" id="accordion"> <div class="accordion" id="accordion">
<div class="accordion-item">
<div class="card"> <h2 class="accordion-header" id="headingFormula">
<div class="card-header" id="headingOne"> <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFormula" aria-expanded="true" aria-controls="collapseFormula">
<h2 class="mb-0"> <b>Formula calculation</b>
<button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
Formula calculation
</button> </button>
</h2> </h2>
</div> <div id="collapseFormula" class="accordion-collapse collapse show" aria-labelledby="headingFormula" data-bs-parent="#accordion">
<div class="accordion-body">
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
<div class="card-body">
<form action="/api/formula" method="post"> <form action="/api/formula" method="post">
<input type="text" name="gravity-format" id="gravity-format" hidden> <input type="text" name="gravity-format" id="gravity-format" hidden>
<input type="text" name="id" id="id" hidden> <input type="text" name="id" id="id" hidden>
@ -96,73 +94,84 @@
rejected. On the bottom of the page you can see a graph over the entered values + values calcualated by the formula. rejected. On the bottom of the page you can see a graph over the entered values + values calcualated by the formula.
</div> </div>
<div class="form-group row"> <div class="row">
<label class="col-sm-2 col-form-label">#:</label> <label class="col-sm-1 col-form-label">#</label>
<label class="col-sm-4 col-form-label">Angle/Tilt:</label> <label class="col-sm-4 col-form-label">Angle/Tilt</label>
<label class="col-sm-4 col-form-label" id="gravity-header">Gravity (SG):</label> <label class="col-sm-4 col-form-label" id="gravity-header">Gravity</label>
</div> </div>
<div class="form-group row"> <div class="row mb-3">
<label for="angle1" class="col-sm-2 col-form-label">1.</label> <label for="angle1" class="col-sm-1 col-form-label">1.</label>
<div class="col-sm-4"> <div class="col-sm-4"><input type="number" min="0" max="90" step="0.001" class="form-control" name="a1" id="a1" data-bs-toggle="tooltip" title="Enter the angle for the gravity. Zero value will be ignored"></div>
<input type="number" min="0" max="90" step="0.001" class="form-control" name="a1" id="a1"> <input hidden type="number" name="g1" id="g1">
</div> <div class="col-sm-4"><input disabled type="number" class="form-control" name="g1a" id="g1a" data-bs-toggle="tooltip" title="The first value is reserved for water gravity."></div>
<div class="col-sm-4">
<input type="number" min="0" max="26" step="0.0001" class="form-control" name="g1" id="g1">
</div>
</div> </div>
<div class="form-group row"> <div class="row mb-3">
<label for="angle2" class="col-sm-2 col-form-label">2.</label> <label for="angle2" class="col-sm-1 col-form-label">2.</label>
<div class="col-sm-4"> <div class="col-sm-4"><input type="number" min="0" max="90" step="0.001" class="form-control" name="a2" id="a2" data-bs-toggle="tooltip" title="Enter the angle for the gravity. Zero value will be ignored"></div>
<input type="number" min="0" max="90" step="0.001" class="form-control" name="a2" id="a2"> <div class="col-sm-4"><input type="number" min="0" max="26" step="0.0001" class="form-control" name="g2" id="g2" data-bs-toggle="tooltip" title="Enter the gravity for the angle."></div>
</div>
<div class="col-sm-4">
<input type="number" min="0" max="26" step="0.0001" class="form-control" name="g2" id="g2">
</div>
</div> </div>
<div class="form-group row"> <div class="row mb-3">
<label for="angle3" class="col-sm-2 col-form-label">3.</label> <label for="angle3" class="col-sm-1 col-form-label">3.</label>
<div class="col-sm-4"> <div class="col-sm-4"><input type="number" min="0" max="90" step="0.001" class="form-control" name="a3" id="a3" data-bs-toggle="tooltip" title="Enter the angle for the gravity. Zero value will be ignored"></div>
<input type="number" min="0" max="90" step="0.001" class="form-control" name="a3" id="a3"> <div class="col-sm-4"><input type="number" min="0" max="26" step="0.0001" class="form-control" name="g3" id="g3" data-bs-toggle="tooltip" title="Enter the gravity for the angle."></div>
</div>
<div class="col-sm-4">
<input type="number" min="0" max="26" step="0.0001" class="form-control" name="g3" id="g3">
</div>
</div> </div>
<div class="form-group row"> <div class="row mb-3">
<label for="angle4" class="col-sm-2 col-form-label">4.</label> <label for="angle4" class="col-sm-1 col-form-label">4.</label>
<div class="col-sm-4"> <div class="col-sm-4"><input type="number" min="0" max="90" step="0.001" class="form-control" name="a4" id="a4" data-bs-toggle="tooltip" title="Enter the angle for the gravity. Zero value will be ignored"></div>
<input type="number" min="0" max="90" step="0.001" class="form-control" name="a4" id="a4"> <div class="col-sm-4"><input type="number" min="0" max="26" step="0.0001" class="form-control" name="g4" id="g4" data-bs-toggle="tooltip" title="Enter the gravity for the angle."></div>
</div>
<div class="col-sm-4">
<input type="number" min="0" max="26" step="0.0001" class="form-control" name="g4" id="g4">
</div>
</div> </div>
<div class="form-group row"> <div class="row mb-3">
<label for="angle5" class="col-sm-2 col-form-label">5.</label> <label for="angle5" class="col-sm-1 col-form-label">5.</label>
<div class="col-sm-4"> <div class="col-sm-4"><input type="number" min="0" max="90" step="0.001" class="form-control" name="a5" id="a5" data-bs-toggle="tooltip" title="Enter the angle for the gravity. Zero value will be ignored"></div>
<input type="number" min="0" max="90" step="0.001" class="form-control" name="a5" id="a5"> <div class="col-sm-4"><input type="number" min="0" max="26" step="0.0001" class="form-control" name="g5" id="g5" data-bs-toggle="tooltip" title="Enter the gravity for the angle."></div>
</div>
<div class="col-sm-4">
<input type="number" min="0" max="26" step="0.0001" class="form-control" name="g5" id="g5">
</div>
</div> </div>
<div class="form-group row"> <div class="row mb-3">
<div class="col-sm-8 offset-sm-0"> <label for="angle6" class="col-sm-1 col-form-label">6.</label>
<button type="submit" class="btn btn-primary" id="calculate-btn">Save & Calculate</button> <div class="col-sm-4"><input type="number" min="0" max="90" step="0.001" class="form-control" name="a6" id="a6" data-bs-toggle="tooltip" title="Enter the angle for the gravity. Zero value will be ignored"></div>
</div> <div class="col-sm-4"><input type="number" min="0" max="26" step="0.0001" class="form-control" name="g6" id="g6" data-bs-toggle="tooltip" title="Enter the gravity for the angle."></div>
</div> </div>
<div class="form-group row"> <div class="row mb-3">
<label for="angle7" class="col-sm-1 col-form-label">7.</label>
<div class="col-sm-4"><input type="number" min="0" max="90" step="0.001" class="form-control" name="a7" id="a7" data-bs-toggle="tooltip" title="Enter the angle for the gravity. Zero value will be ignored"></div>
<div class="col-sm-4"><input type="number" min="0" max="26" step="0.0001" class="form-control" name="g7" id="g7" data-bs-toggle="tooltip" title="Enter the gravity for the angle."></div>
</div>
<div class="row mb-3">
<label for="angle8" class="col-sm-1 col-form-label">8.</label>
<div class="col-sm-4"><input type="number" min="0" max="90" step="0.001" class="form-control" name="a8" id="a8" data-bs-toggle="tooltip" title="Enter the angle for the gravity. Zero value will be ignored"></div>
<div class="col-sm-4"><input type="number" min="0" max="26" step="0.0001" class="form-control" name="g8" id="g8" data-bs-toggle="tooltip" title="Enter the gravity for the angle."></div>
</div>
<div class="row mb-3">
<label for="angle9" class="col-sm-1 col-form-label">9.</label>
<div class="col-sm-4"><input type="number" min="0" max="90" step="0.001" class="form-control" name="a9" id="a9" data-bs-toggle="tooltip" title="Enter the angle for the gravity. Zero value will be ignored"></div>
<div class="col-sm-4"><input type="number" min="0" max="26" step="0.0001" class="form-control" name="g9" id="g9" data-bs-toggle="tooltip" title="Enter the gravity for the angle."></div>
</div>
<div class="row mb-3">
<label for="angle10" class="col-sm-1 col-form-label">10.</label>
<div class="col-sm-4"><input type="number" min="0" max="90" step="0.001" class="form-control" name="a10" id="a10" data-bs-toggle="tooltip" title="Enter the angle for the gravity. Zero value will be ignored"></div>
<div class="col-sm-4"><input type="number" min="0" max="26" step="0.0001" class="form-control" name="g10" id="g10" data-bs-toggle="tooltip" title="Enter the gravity for the angle."></div>
</div>
<div class="row mb-3">
<div class="col-sm-8 offset-sm-1"><button type="submit" class="btn btn-primary" id="calculate-btn" data-bs-toggle="tooltip" title="Save the values and try to create a formula">Save & Create</button></div>
</div>
<hr class="my-2">
<div class="row">
<label for="calculate-btn" class="col-sm-2 col-form-label">Current angle: </label> <label for="calculate-btn" class="col-sm-2 col-form-label">Current angle: </label>
<label for="calculate-btn" class="col-sm-2 col-form-label" id="angle"></label> <label for="calculate-btn" class="col-sm-2 col-form-label" id="angle"></label>
</div> </div>
<div class="form-group row"> <div class="row">
<label for="calculate-btn" class="col-sm-2 col-form-label">Formula: </label> <label for="calculate-btn" class="col-sm-2 col-form-label">Formula: </label>
<label for="calculate-btn" class="col-sm-8 col-form-label" id="formula">Loading...</label> <label for="calculate-btn" class="col-sm-8 col-form-label" id="formula">Loading...</label>
</div> </div>
@ -170,15 +179,23 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<hr class="my-4"> <div class="accordion-item">
<div> <h2 class="accordion-header" id="headingGraph">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseGraph" aria-expanded="false" aria-controls="collapseGraph">
<b>Formula graph</b>
</button>
</h2>
<div id="collapseGraph" class="accordion-collapse collapse" aria-labelledby="headingGraph" data-bs-parent="#accordion">
<div class="accordion-body">
<canvas id="gravityChart"></canvas> <canvas id="gravityChart"></canvas>
</div> </div>
<hr class="my-4"> </div>
</div> </div>
</div>
</div>
<script type="text/javascript"> <script type="text/javascript">
var chartDataForm = []; var chartDataForm = [];
var chartDataCalc = []; var chartDataCalc = [];
@ -231,20 +248,30 @@
}; };
var myChart = 0; var myChart = 0;
</script> </script>
<script type="text/javascript"> <script type="text/javascript">
g1.onchange = setGravityDecimal g1.onchange = setGravityDecimal
g2.onchange = setGravityDecimal g2.onchange = setGravityDecimal
g3.onchange = setGravityDecimal g3.onchange = setGravityDecimal
g4.onchange = setGravityDecimal g4.onchange = setGravityDecimal
g5.onchange = setGravityDecimal g5.onchange = setGravityDecimal
g6.onchange = setGravityDecimal
g7.onchange = setGravityDecimal
g8.onchange = setGravityDecimal
g9.onchange = setGravityDecimal
g10.onchange = setGravityDecimal
a1.onchange = setAngleDecimal a1.onchange = setAngleDecimal
a2.onchange = setAngleDecimal a2.onchange = setAngleDecimal
a3.onchange = setAngleDecimal a3.onchange = setAngleDecimal
a4.onchange = setAngleDecimal a4.onchange = setAngleDecimal
a5.onchange = setAngleDecimal a5.onchange = setAngleDecimal
a6.onchange = setAngleDecimal
a7.onchange = setAngleDecimal
a8.onchange = setAngleDecimal
a9.onchange = setAngleDecimal
a10.onchange = setAngleDecimal
window.onload = getConfig; window.onload = getConfig;
setButtonDisabled( true ); setButtonDisabled( true );
@ -274,6 +301,10 @@
function populateChartForm(a, g) { function populateChartForm(a, g) {
if( a != 0) if( a != 0)
chartDataForm.push( { x: parseFloat(a), y: parseFloat(g) }); chartDataForm.push( { x: parseFloat(a), y: parseFloat(g) });
chartDataForm.sort(function (a, b) {
return a.x - b.x;
});
} }
function populateChartCalc(a, g) { function populateChartCalc(a, g) {
@ -308,6 +339,11 @@
populateChartForm( $("#a3").val(), $("#g3").val() ); populateChartForm( $("#a3").val(), $("#g3").val() );
populateChartForm( $("#a4").val(), $("#g4").val() ); populateChartForm( $("#a4").val(), $("#g4").val() );
populateChartForm( $("#a5").val(), $("#g5").val() ); populateChartForm( $("#a5").val(), $("#g5").val() );
populateChartForm( $("#a6").val(), $("#g6").val() );
populateChartForm( $("#a7").val(), $("#g7").val() );
populateChartForm( $("#a8").val(), $("#g8").val() );
populateChartForm( $("#a9").val(), $("#g9").val() );
populateChartForm( $("#a10").val(), $("#g10").val() );
if( myChart ) if( myChart )
myChart.destroy(); myChart.destroy();
@ -340,17 +376,29 @@
if(isPlato()) { if(isPlato()) {
$("#gravity-header").text("Gravity (Plato):"); $("#gravity-header").text("Gravity (Plato):");
$("#g1").val( parseFloat(cfg["g1"]).toFixed(1) ); $("#g1").val( parseFloat(cfg["g1"]).toFixed(1) );
$("#g1a").val( "0.0" );
$("#g2").val( parseFloat(cfg["g2"]).toFixed(1) ); $("#g2").val( parseFloat(cfg["g2"]).toFixed(1) );
$("#g3").val( parseFloat(cfg["g3"]).toFixed(1) ); $("#g3").val( parseFloat(cfg["g3"]).toFixed(1) );
$("#g4").val( parseFloat(cfg["g4"]).toFixed(1) ); $("#g4").val( parseFloat(cfg["g4"]).toFixed(1) );
$("#g5").val( parseFloat(cfg["g5"]).toFixed(1) ); $("#g5").val( parseFloat(cfg["g5"]).toFixed(1) );
$("#g6").val( parseFloat(cfg["g6"]).toFixed(1) );
$("#g7").val( parseFloat(cfg["g7"]).toFixed(1) );
$("#g8").val( parseFloat(cfg["g8"]).toFixed(1) );
$("#g9").val( parseFloat(cfg["g9"]).toFixed(1) );
$("#g10").val( parseFloat(cfg["g10"]).toFixed(1) );
} else { } else {
$("#gravity-header").text("Gravity (SG):"); $("#gravity-header").text("Gravity (SG):");
$("#g1").val( parseFloat(cfg["g1"]).toFixed(4) ); $("#g1").val( parseFloat(cfg["g1"]).toFixed(4) );
$("#g1a").val( "1.0000" );
$("#g2").val( parseFloat(cfg["g2"]).toFixed(4) ); $("#g2").val( parseFloat(cfg["g2"]).toFixed(4) );
$("#g3").val( parseFloat(cfg["g3"]).toFixed(4) ); $("#g3").val( parseFloat(cfg["g3"]).toFixed(4) );
$("#g4").val( parseFloat(cfg["g4"]).toFixed(4) ); $("#g4").val( parseFloat(cfg["g4"]).toFixed(4) );
$("#g5").val( parseFloat(cfg["g5"]).toFixed(4) ); $("#g5").val( parseFloat(cfg["g5"]).toFixed(4) );
$("#g6").val( parseFloat(cfg["g6"]).toFixed(4) );
$("#g7").val( parseFloat(cfg["g7"]).toFixed(4) );
$("#g8").val( parseFloat(cfg["g8"]).toFixed(4) );
$("#g9").val( parseFloat(cfg["g9"]).toFixed(4) );
$("#g10").val( parseFloat(cfg["g10"]).toFixed(4) );
} }
$("#a1").val( parseFloat(cfg["a1"]).toFixed(2) ); $("#a1").val( parseFloat(cfg["a1"]).toFixed(2) );
@ -358,6 +406,11 @@
$("#a3").val( parseFloat(cfg["a3"]).toFixed(2) ); $("#a3").val( parseFloat(cfg["a3"]).toFixed(2) );
$("#a4").val( parseFloat(cfg["a4"]).toFixed(2) ); $("#a4").val( parseFloat(cfg["a4"]).toFixed(2) );
$("#a5").val( parseFloat(cfg["a5"]).toFixed(2) ); $("#a5").val( parseFloat(cfg["a5"]).toFixed(2) );
$("#a6").val( parseFloat(cfg["a6"]).toFixed(2) );
$("#a7").val( parseFloat(cfg["a7"]).toFixed(2) );
$("#a8").val( parseFloat(cfg["a8"]).toFixed(2) );
$("#a9").val( parseFloat(cfg["a9"]).toFixed(2) );
$("#a10").val( parseFloat(cfg["a10"]).toFixed(2) );
if( cfg["error"]!="" ) { if( cfg["error"]!="" ) {
showError(cfg["error"]); showError(cfg["error"]);
@ -373,10 +426,10 @@
setButtonDisabled( false ); setButtonDisabled( false );
}); });
} }
</script> </script>
<!-- START FOOTER --> <!-- START FOOTER -->
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div> <div class="container themed-container bg-primary text-light row-margin-10">(C) Copyright 2021-22 Magnus Persson</div>
</body> </body>
</html> </html>

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,120 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<title>Beer Gravity Monitor</title>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script>
</head>
<body class="py-4">
<!-- START MENU -->
<nav class="navbar navbar-expand-sm navbar-dark bg-primary">
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbar">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item active">
<a class="nav-link" href="/device.htm">Device</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config.htm">Configuration</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/calibration.htm">Calibration</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/about.htm">About</a>
</li>
</ul>
</div>
<div class="spinner-border text-light" id="spinner" role="status"></div>
</nav>
<!-- START MAIN INDEX -->
<div class="container">
<hr class="my-4">
<div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert">
<div id="alert-msg">...</div>
<button type="button" id="alert-btn" class="close" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<script type="text/javascript">
function showError( msg ) {
$('.alert').removeClass('alert-success').addClass('alert-danger').removeClass('d-none').addClass('show')
$('#alert-msg').text( msg );
}
function showSuccess( msg ) {
$('.alert').addClass('alert-success').removeClass('alert-danger').removeClass('d-none').addClass('show')
$('#alert-msg').text( msg );
}
$("#alert-btn").click(function(e){
$('.alert').addClass('d-none').removeClass('show')
});
</script>
<div class="row mb-3">
<div class="col-md-8 themed-grid-col bg-light">Current version:</div>
<div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div>
</div>
<div class="row mb-3" id="h-app-ver-new" hidden>
<div class="col-md-8 themed-grid-col bg-light">New version:</div>
<div class="col-md-4 themed-grid-col bg-light" id="app-ver-new">Loading...</div>
</div>
<div class="row mb-3">
<div class="col-md-8 themed-grid-col bg-light">Host name:</div>
<div class="col-md-4 themed-grid-col bg-light" id="mdns">Loading...</div>
</div>
<div class="row mb-3">
<div class="col-md-8 themed-grid-col bg-light">Device ID:</div>
<div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div>
</div>
<hr class="my-4">
</div>
<script type="text/javascript">
window.onload = getConfig;
function getConfig() {
var url = "/api/device";
//var url = "/test/device.json";
$('#spinner').show();
$.getJSON(url, function (cfg) {
console.log( cfg );
$("#app-ver").text(cfg["app-ver"] + " (html 0.7.1)");
$("#mdns").text(cfg["mdns"]);
$("#id").text(cfg["id"]);
})
.fail(function () {
showError('Unable to get data from the device.');
})
.always(function () {
$('#spinner').hide();
});
}
</script>
<!-- START FOOTER -->
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div>
</body>
</html>

View File

@ -1 +0,0 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item active"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Current version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3" id="h-app-ver-new" hidden><div class="col-md-8 themed-grid-col bg-light">New version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver-new">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Host name:</div><div class="col-md-4 themed-grid-col bg-light" id="mdns">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Device ID:</div><div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div></div><hr class="my-4"></div><script type="text/javascript">function getConfig(){var n="/api/device";$("#spinner").show(),$.getJSON(n,function(n){console.log(n),$("#app-ver").text(n["app-ver"]+" (html 0.7.1)"),$("#mdns").text(n.mdns),$("#id").text(n.id)}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}window.onload=getConfig</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>

211
html/firmware.htm Normal file
View File

@ -0,0 +1,211 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<title>Beer Gravity Monitor</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<style>
.row-margin-10 { margin-top: 1.0em; }
</style>
</head>
<body class="py-4">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/index.htm">Home</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle active" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Configuration
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/config.htm">Configuration</a></li>
<li><a class="dropdown-item" href="/format.htm">Format editor</a></li>
<li><a class="dropdown-item" href="/test.htm">Test push</a></li>
<li><a class="dropdown-item" href="#">Upload firmware</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link" href="/calibration.htm">Calibration</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/about.htm">About</a>
</li>
</ul>
</div>
<div class="spinner-border text-light" id="spinner" role="status"></div>
</div>
</nav>
<!-- START MAIN INDEX -->
<div class="container row-margin-10">
<div class="alert alert-success alert-dismissible hide fade d-none" role="alert">
<div id="alert"></div>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<script type="text/javascript">
function showError( msg ) {
$('.alert').removeClass('alert-success').addClass('alert-danger').removeClass('hide').addClass('show').removeClass('d-none');
$('#alert').text( msg );
}
function showSuccess( msg ) {
$('.alert').addClass('alert-success').removeClass('alert-danger').removeClass('hide').addClass('show').removeClass('d-none');
$('#alert').text( msg );
}
$("#alert-btn").click(function(e){
$('.alert').addClass('hide').removeClass('show').addClass('d-none');
});
</script>
<div class="accordion" id="accordion">
<div class="accordion-item">
<h2 class="accordion-header" id="headingFirmware">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFirmware" aria-expanded="true" aria-controls="collapseFirmware">
<b>Upload firmware</b>
</button>
</h2>
<div id="collapseFirmware" class="accordion-collapse collapse show" aria-labelledby="headingFirmware" data-bs-parent="#accordion">
<div class="accordion-body">
<div class="row mb-3">
<div class="col-md-12 themed-grid-col bg-light">Here you can upload a new firmware version, it will not check the version number so you can also downgrade the firmware here.
</div>
</div>
<div class="row mb-3">
<div class="col-md-2 themed-grid-col bg-light">Current version:</div>
<div class="col-md-10 themed-grid-col bg-light" id="app-ver">Loading...</div>
</div>
<div class="row mb-3">
<div class="col-md-2 themed-grid-col bg-light">Platform:</div>
<div class="col-md-10 themed-grid-col bg-light" id="platform">Loading...</div>
</div>
<form id="uploadForm" enctype="multipart/form-data">
<div class="row mb-3">
<div class="col-md-8 custom-file">
<input type="file" accept=".bin" class="custom-file-input" name="name" id="name" onchange="checkName()" data-bs-toggle="tooltip" title="Select a firmware file to upload">
</div>
</div>
<div class="row mb-3">
<div class="col-md-4">
<button type="submit" class="btn btn-primary" id="upload-btn" value="upload" data-bs-toggle="tooltip" title="Update the device with the selected firmware">Flash firmware</button>
</div>
</div>
</form>
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
window.onload = getStatus;
$(document).ready(function() {
$("#uploadForm").on('submit', function(e) {
e.preventDefault();
$.ajax( {
xhr: function() {
var xhr = new window.XMLHttpRequest();
xhr.upload.addEventListener("progress", function(evt) {
if (evt.lengthComputable) {
progressHandler(evt);
}
}, false);
return xhr;
},
type: 'POST',
url: '/api/upload',
data: new FormData(this),
contentType: false,
cache: false,
processData:false,
beforeSend: function() {
setProgress(0);
},
error:function() {
showError("Upload failed");
},
success: function(resp) {
showSuccess("Upload completed, device is restarting. Waiting 10 seconds to refresh browser. If you don't get any gyro readings after update, please press the reset button!");
setTimeout(() => {
window.location = "/";
}, 10000);
}
});
});
});
function checkName() {
setButtonDisabled( $("#name").val()!="" ? false : true );
}
function progressHandler(event) {
var percent = (event.loaded / event.total) * 100;
setProgress(Math.round(percent));
}
function setProgress(val) {
$('.progress-bar').css('width', val+'%').attr('aria-valuenow', val).text(val + "%");
}
function setButtonDisabled( b ) {
$("#upload-btn").prop("disabled", b);
}
function getStatus() {
setButtonDisabled( true );
var url = "/api/status";
//var url = "/test/status.json";
$('#spinner').show();
$.getJSON(url, function (cfg) {
console.log( cfg );
$("#app-ver").text(cfg["app-ver"]);
$("#platform").text(cfg["platform"]);
})
.fail(function () {
showError('Unable to get data from the device.');
})
.always(function() {
$('#spinner').hide();
});
}
function start() {
setInterval(getStatus, 3000);
}
</script>
<!-- START FOOTER -->
<div class="container themed-container bg-primary text-light row-margin-10">(C) Copyright 2021-22 Magnus Persson</div>
</body>
</html>

79
html/firmware.min.htm Normal file
View File

@ -0,0 +1,79 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"><style>.row-margin-10{margin-top:1em}</style></head><body class="py-4"><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><nav class="navbar navbar-expand-lg navbar-dark bg-primary"><div class="container"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbarNav"><ul class="navbar-nav"><li class="nav-item"><a class="nav-link" href="/index.htm">Home</a></li><li class="nav-item dropdown"><a class="nav-link dropdown-toggle active" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">Configuration</a><ul class="dropdown-menu"><li><a class="dropdown-item" href="/config.htm">Configuration</a></li><li><a class="dropdown-item" href="/format.htm">Format editor</a></li><li><a class="dropdown-item" href="/test.htm">Test push</a></li><li><a class="dropdown-item" href="#">Upload firmware</a></li></ul></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START MAIN INDEX --><div class="container row-margin-10"><div class="alert alert-success alert-dismissible hide fade d-none" role="alert"><div id="alert"></div><button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("hide").addClass("show").removeClass("d-none"),$("#alert").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("hide").addClass("show").removeClass("d-none"),$("#alert").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("hide").removeClass("show").addClass("d-none")})</script><div class="accordion" id="accordion"><div class="accordion-item"><h2 class="accordion-header" id="headingFirmware"><button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFirmware" aria-expanded="true" aria-controls="collapseFirmware"><b>Upload firmware</b></button></h2><div id="collapseFirmware" class="accordion-collapse collapse show" aria-labelledby="headingFirmware" data-bs-parent="#accordion"><div class="accordion-body"><div class="row mb-3"><div class="col-md-12 themed-grid-col bg-light">Here you can upload a new firmware version, it will not check the version number so you can also downgrade the firmware here.</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">Current version:</div><div class="col-md-10 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">Platform:</div><div class="col-md-10 themed-grid-col bg-light" id="platform">Loading...</div></div><form id="uploadForm" enctype="multipart/form-data"><div class="row mb-3"><div class="col-md-8 custom-file"><input type="file" accept=".bin" class="custom-file-input" name="name" id="name" onchange="checkName()" data-bs-toggle="tooltip" title="Select a firmware file to upload"></div></div><div class="row mb-3"><div class="col-md-4"><button type="submit" class="btn btn-primary" id="upload-btn" value="upload" data-bs-toggle="tooltip" title="Update the device with the selected firmware">Flash firmware</button></div></div></form><div class="progress"><div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div></div></div></div></div></div><script type="text/javascript">window.onload = getStatus;
$(document).ready(function() {
$("#uploadForm").on('submit', function(e) {
e.preventDefault();
$.ajax( {
xhr: function() {
var xhr = new window.XMLHttpRequest();
xhr.upload.addEventListener("progress", function(evt) {
if (evt.lengthComputable) {
progressHandler(evt);
}
}, false);
return xhr;
},
type: 'POST',
url: '/api/upload',
data: new FormData(this),
contentType: false,
cache: false,
processData:false,
beforeSend: function() {
setProgress(0);
},
error:function() {
showError("Upload failed");
},
success: function(resp) {
showSuccess("Upload completed, device is restarting. Waiting 10 seconds to refresh browser. If you don't get any gyro readings after update, please press the reset button!");
setTimeout(() => {
window.location = "/";
}, 10000);
}
});
});
});
function checkName() {
setButtonDisabled( $("#name").val()!="" ? false : true );
}
function progressHandler(event) {
var percent = (event.loaded / event.total) * 100;
setProgress(Math.round(percent));
}
function setProgress(val) {
$('.progress-bar').css('width', val+'%').attr('aria-valuenow', val).text(val + "%");
}
function setButtonDisabled( b ) {
$("#upload-btn").prop("disabled", b);
}
function getStatus() {
setButtonDisabled( true );
var url = "/api/status";
//var url = "/test/status.json";
$('#spinner').show();
$.getJSON(url, function (cfg) {
console.log( cfg );
$("#app-ver").text(cfg["app-ver"]);
$("#platform").text(cfg["platform"]);
})
.fail(function () {
showError('Unable to get data from the device.');
})
.always(function() {
$('#spinner').hide();
});
}
function start() {
setInterval(getStatus, 3000);
}</script><!-- START FOOTER --><div class="container themed-container bg-primary text-light row-margin-10">(C) Copyright 2021-22 Magnus Persson</div></div></body></html>

View File

@ -5,115 +5,189 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content=""> <meta name="description" content="">
<title>Beer Gravity Monitor</title> <title>Beer Gravity Monitor</title>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script> <style>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script> .row-margin-10 { margin-top: 1.0em; }
</style>
</head> </head>
<body class="py-4"> <body class="py-4">
<!-- START MENU --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<nav class="navbar navbar-expand-sm navbar-dark bg-primary">
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarNav">
<div class="collapse navbar-collapse" id="navbar"> <ul class="navbar-nav">
<ul class="navbar-nav mr-auto">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/config.htm">Back to configuration</a> <a class="nav-link" href="/index.htm">Home</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle active" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Configuration
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/config.htm">Configuration</a></li>
<li><a class="dropdown-item" href="#">Format editor</a></li>
<li><a class="dropdown-item" href="/test.htm">Test push</a></li>
<li><a class="dropdown-item" href="/firmware.htm">Upload firmware</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link" href="/calibration.htm">Calibration</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/about.htm">About</a>
</li> </li>
</ul> </ul>
</div> </div>
<div class="spinner-border text-light" id="spinner" role="status"></div> <div class="spinner-border text-light" id="spinner" role="status"></div>
</nav> </div>
</nav>
<!-- START MAIN INDEX -->
<div class="container"> <!-- START MAIN INDEX -->
<hr class="my-2"> <div class="container row-margin-10">
<div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"> <div class="alert alert-success alert-dismissible hide fade d-none" role="alert" id="alert">
<div id="alert-msg">...</div> <div id="alert-msg"></div>
<button type="button" id="alert-btn" class="close" aria-label="Close"> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
<span aria-hidden="true">&times;</span> </div>
</button>
<div class="alert alert-warning alert-dismissible hide fade d-none" role="alert" id="warning-ha">
<div>Home Assistant device configuration detected in MQTT format. These messages will be posted when format is saved and not during gravity measurement.</div>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<div class="alert alert-warning alert-dismissible hide fade d-none" role="alert" id="warning-length">
<div>The format template is quite large, its possible the device will not have enough memory to handle it.</div>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
function showError( msg ) { function showError( msg ) {
$('#alert').removeClass('alert-success').addClass('alert-danger').removeClass('d-none').addClass('show'); $('#alert').removeClass('alert-success').addClass('alert-danger').removeClass('hide').addClass('show').removeClass('d-none');
$('#alert-msg').text( msg ); $('#alert-msg').text( msg );
} }
function showSuccess( msg ) { function showSuccess( msg ) {
$('#alert').addClass('alert-success').removeClass('alert-danger').removeClass('d-none').addClass('show'); $('#alert').addClass('alert-success').removeClass('alert-danger').removeClass('hide').addClass('show').removeClass('d-none');
$('#alert-msg').text( msg ); $('#alert-msg').text( msg );
} }
$("#alert-btn").click(function(e){ $("#alert-btn").click(function(e){
$('#alert').addClass('d-none').removeClass('show'); $('#alert').addClass('hide').removeClass('show').addClass('d-none');
}); });
function showWarningHomeAssistant() {
$('#warning-ha').removeClass('d-none').addClass('show').removeClass('hide');
}
function hideWarningHomeAssistant() {
$('#warning-ha').addClass('d-none').removeClass('show').addClass('hide');
}
function showWarningLength() {
$('#warning-length').removeClass('d-none').addClass('show').removeClass('hide');
}
function hideWarningLength() {
$('#warning-length').addClass('d-none').removeClass('show').addClass('hide');
}
</script> </script>
<div class="accordion" id="accordion"> <div class="accordion" id="accordion">
<div class="accordion-item">
<div class="card"> <h2 class="accordion-header" id="headingFormat">
<div class="card-header" id="headingOne"> <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFormat" aria-expanded="true" aria-controls="collapseFormat">
<h2 class="mb-0"> <b>Push Format Templates</b>
<button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
Push Format Templates
</button> </button>
</h2> </h2>
</div> <div id="collapseFormat" class="accordion-collapse collapse show" aria-labelledby="headingFormat" data-bs-parent="#accordion">
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion"> <div class="accordion-body">
<div class="card-body">
<input type="text" name="id" id="id" hidden> <input type="text" name="id" id="id" hidden>
<input type="text" name="http-1" id="http-1" hidden> <input type="text" name="http-1" id="http-1" hidden>
<input type="text" name="http-2" id="http-2" hidden> <input type="text" name="http-2" id="http-2" hidden>
<!--<input type="text" name="brewfather" id="brewfather" hidden>--> <input type="text" name="http-3" id="http-3" hidden>
<input type="text" name="influxdb" id="influxdb" hidden> <input type="text" name="influxdb" id="influxdb" hidden>
<input type="text" name="mqtt" id="mqtt" hidden> <input type="text" name="mqtt" id="mqtt" hidden>
<div class="form-group row"> <div class="row mb-3">
<label for="push-target" class="col-sm-2 col-form-label">Push target:</label> <label for="push-target" class="col-sm-2 col-form-label">Push target:</label>
<select class="custom-select col-sm-4" required name="push-target" id="push-target"> <select class="custom-select col-sm-4" required name="push-target" id="push-target" data-bs-toggle="tooltip" title="Select the push target to edit format template for">
<option value="http-1">HTTP option 1</option> <option value="http-1">HTTP option 1 (post)</option>
<option value="http-2">HTTP option 2</option> <option value="http-2">HTTP option 2 (post)</option>
<!--<option value="brewfather">Brewfather</option>--> <option value="http-3">HTTP option 3 (get)</option>
<option value="influxdb">Influx DB</option> <option value="influxdb">Influx DB</option>
<option value="mqtt">MQTT</option> <option value="mqtt">MQTT</option>
</select> </select>
</div> </div>
<div class="form-group row"> <div class="row mb-3">
<div class="col-sm-12"> <div class="col-sm-12">
<textarea rows="5" class="form-control" name="format" id="format"> <textarea rows="10" class="form-control" name="format" id="format" onchange="updateStatusField()" onkeydown="updateStatusField()">
</textarea> </textarea>
</div> </div>
</div> </div>
<div class="form-group row"> <script>
<div class="col-sm-8 offset-sm-2"> let formatTemplates = [
<button class="btn btn-primary" id="format-btn">Save</button> { "id": "GravityMon-Post", "format": "%7B%20%22name%22%20%3A%20%22%24%7Bmdns%7D%22%2C%20%22ID%22%3A%20%22%24%7Bid%7D%22%2C%20%22token%22%20%3A%20%22%24%7Btoken%7D%22%2C%20%22interval%22%3A%20%24%7Bsleep-interval%7D%2C%20%22temperature%22%3A%20%24%7Btemp%7D%2C%20%22temp_units%22%3A%20%22%24%7Btemp-unit%7D%22%2C%20%22gravity%22%3A%20%24%7Bgravity%7D%2C%20%22angle%22%3A%20%24%7Bangle%7D%2C%20%22battery%22%3A%20%24%7Bbattery%7D%2C%20%22RSSI%22%3A%20%24%7Brssi%7D%2C%20%22corr-gravity%22%3A%20%24%7Bcorr-gravity%7D%2C%20%22gravity-unit%22%3A%20%22%24%7Bgravity-unit%7D%22%2C%20%22run-time%22%3A%20%24%7Brun-time%7D%7D" },
<button class="btn btn-secondary" id="test-btn">Test</button> { "id": "GravityMon-Get", "format": "%3Fname%3D%24%7Bmdns%7D%26id%3D%24%7Bid%7D%26token%3D%24%7Btoken2%7D%26interval%3D%24%7Bsleep-interval%7D%26temperature%3D%24%7Btemp%7D%26%0Atemp-units%3D%24%7Btemp-unit%7D%26gravity%3D%24%7Bgravity%7D%26angle%3D%24%7Bangle%7D%26battery%3D%24%7Bbattery%7D%26rssi%3D%24%7Brssi%7D%26%0Acorr-gravity%3D%24%7Bcorr-gravity%7D%26gravity-unit%3D%24%7Bgravity-unit%7D%26run-time%3D%24%7Brun-time%7D" },
{ "id": "iSpindle-Post", "format": "%7B%20%22name%22%20%3A%20%22%24%7Bmdns%7D%22%2C%20%22ID%22%3A%20%22%24%7Bid%7D%22%2C%20%22token%22%20%3A%20%22%24%7Btoken%7D%22%2C%20%22interval%22%3A%20%24%7Bsleep-interval%7D%2C%20%22temperature%22%3A%20%24%7Btemp%7D%2C%20%22temp_units%22%3A%20%22%24%7Btemp-unit%7D%22%2C%20%22gravity%22%3A%20%24%7Bgravity%7D%2C%20%22angle%22%3A%20%24%7Bangle%7D%2C%20%22battery%22%3A%20%24%7Bbattery%7D%2C%20%22RSSI%22%3A%20%24%7Brssi%7D%7D" },
{ "id": "BrewFatherCustom-Post", "format": "%7B%20%20%20%22name%22%3A%20%22%24%7Bmdns%7D%22%2C%20%20%20%22temp%22%3A%20%24%7Btemp%7D%2C%20%20%20%22aux_temp%22%3A%200%2C%20%20%20%22ext_temp%22%3A%200%2C%20%20%20%22temp_unit%22%3A%20%22%24%7Btemp-unit%7D%22%2C%20%20%20%22gravity%22%3A%20%24%7Bgravity%7D%2C%20%20%20%22gravity_unit%22%3A%20%22%24%7Bgravity-unit%7D%22%2C%20%20%20%22pressure%22%3A%200%2C%20%20%20%22pressure_unit%22%3A%20%22PSI%22%2C%20%20%20%22ph%22%3A%200%2C%20%20%20%22bpm%22%3A%200%2C%20%20%20%22comment%22%3A%20%22%22%2C%20%20%20%22beer%22%3A%20%22%22%2C%20%20%20%22battery%22%3A%20%24%7Bbattery%7D%7D" },
{ "id": "iSpindle-Mqtt", "format": "ispindel%2F%24%7Bmdns%7D%2Ftilt%3A%24%7Bangle%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2Ftemperature%3A%24%7Btemp%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2Ftemp_units%3A%24%7Btemp-unit%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2Fbattery%3A%24%7Bbattery%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2Fgravity%3A%24%7Bgravity%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2Finterval%3A%24%7Bsleep-interval%7D%7C%0Aispindel%2F%24%7Bmdns%7D%2FRSSI%3A%24%7Brssi%7D%7C" },
{ "id": "HomeAssistant-Mqtt", "format": "gravmon%2F%24%7Bmdns%7D%2Ftemperature%3A%24%7Btemp%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Fgravity%3A%24%7Bgravity%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Frssi%3A%24%7Brssi%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Ftilt%3A%24%7Btilt%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Fbattery%3A%24%7Bbattery%7D%7C%0A" },
{ "id": "HomeAssistant-Mqtt2", "format": "gravmon%2F%24%7Bmdns%7D%2Ftemperature%3A%24%7Btemp%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Fgravity%3A%24%7Bgravity%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Frssi%3A%24%7Brssi%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Ftilt%3A%24%7Btilt%7D%7C%0Agravmon%2F%24%7Bmdns%7D%2Fbattery%3A%24%7Bbattery%7D%7C%0Ahomeassistant%2Fsensor%2Fgravmon_%24%7Bid%7D%2Ftemperature%2Fconfig%3A%7B%22dev%22%3A%7B%22name%22%3A%22%24%7Bmdns%7D%22%2C%22mdl%22%3A%22gravmon%22%2C%22sw%22%3A%22%24%7Bapp-ver%7D%22%2C%22ids%22%3A%22%24%7Bid%7D%22%7D%2C%22uniq_id%22%3A%22%24%7Bid%7D_temp%22%2C%22name%22%3A%22temperature%22%2C%22dev_cla%22%3A%22temperature%22%2C%22unit_of_meas%22%3A%22%24%7Btemp-unit%7D%22%2C%22stat_t%22%3A%22gravmon%2F%24%7Bmdns%7D%2Ftemperature%22%7D%7C%0Ahomeassistant%2Fsensor%2Fgravmon_%24%7Bid%7D%2Fgravity%2Fconfig%3A%7B%22dev%22%3A%7B%22name%22%3A%22%24%7Bmdns%7D%22%2C%22mdl%22%3A%22gravmon%22%2C%22sw%22%3A%22%24%7Bapp-ver%7D%22%2C%22ids%22%3A%22%24%7Bid%7D%22%7D%2C%22uniq_id%22%3A%22%24%7Bid%7D_grav%22%2C%22name%22%3A%22gravity%22%2C%22dev_cla%22%3A%22temperature%22%2C%22unit_of_meas%22%3A%22%20%24%7Bgravity-unit%7D%22%2C%22stat_t%22%3A%22gravmon%2F%24%7Bmdns%7D%2Fgravity%22%7D%7C%0Ahomeassistant%2Fsensor%2Fgravmon_%24%7Bid%7D%2Frssi%2Fconfig%3A%7B%22dev%22%3A%7B%22name%22%3A%22%24%7Bmdns%7D%22%2C%22mdl%22%3A%22gravmon%22%2C%22sw%22%3A%22%24%7Bapp-ver%7D%22%2C%22ids%22%3A%22%24%7Bid%7D%22%7D%2C%22uniq_id%22%3A%22%24%7Bid%7D_rssi%22%2C%22name%22%3A%22rssi%22%2C%22dev_cla%22%3A%22signal_strength%22%2C%22unit_of_meas%22%3A%22dBm%22%2C%22stat_t%22%3A%22gravmon%2F%24%7Bmdns%7D%2Frssi%22%7D%7C%0Ahomeassistant%2Fsensor%2Fgravmon_%24%7Bid%7D%2Ftilt%2Fconfig%3A%7B%22dev%22%3A%7B%22name%22%3A%22%24%7Bmdns%7D%22%2C%22mdl%22%3A%22gravmon%22%2C%22sw%22%3A%22%24%7Bapp-ver%7D%22%2C%22ids%22%3A%22%24%7Bid%7D%22%7D%2C%22uniq_id%22%3A%22%24%7Bid%7D_tilt%22%2C%22name%22%3A%22tilt%22%2C%22dev_cla%22%3A%22temperature%22%2C%22stat_t%22%3A%22gravmon%2F%24%7Bmdns%7D%2Ftilt%22%7D%7C%0Ahomeassistant%2Fsensor%2Fgravmon_%24%7Bid%7D%2Fbattery%2Fconfig%3A%7B%22dev%22%3A%7B%22name%22%3A%22%24%7Bmdns%7D%22%2C%22mdl%22%3A%22gravmon%22%2C%22sw%22%3A%22%24%7Bapp-ver%7D%22%2C%22ids%22%3A%22%24%7Bid%7D%22%7D%2C%22uniq_id%22%3A%22%24%7Bid%7D_batt%22%2C%22name%22%3A%22battery%22%2C%22dev_cla%22%3A%22voltage%22%2C%22unit_of_meas%22%3A%22V%22%2C%22stat_t%22%3A%22gravmon%2F%24%7Bmdns%7D%2Fbattery%22%7D%7C%0A" },
{ "id": "Brewblox-Mqtt", "format": "brewcast%2Fhistory%3A%7B%22key%22%3A%22%24%7Bmdns%7D%22%2C%22data%22%3A%7B%22Temperature%5BdegC%5D%22%3A%20%24%7Btemp-c%7D%2C%22Temperature%5BdegF%5D%22%3A%20%24%7Btemp-f%7D%2C%22Battery%5BV%5D%22%3A%24%7Bbattery%7D%2C%22Tilt%5Bdeg%5D%22%3A%24%7Bangle%7D%2C%22Rssi%5BdBm%5D%22%3A%24%7Brssi%7D%2C%22SG%22%3A%24%7Bgravity-sg%7D%2C%22Plato%22%3A%24%7Bgravity-plato%7D%7D%7D%7C" },
{ "id": "UBIDots-Post", "format": "%7B%0A%20%20%20%22temperature%22%3A%20%24%7Btemp%7D%2C%0A%20%20%20%22gravity%22%3A%20%24%7Bgravity%7D%2C%0A%20%20%20%22angle%22%3A%20%24%7Bangle%7D%2C%0A%20%20%20%22battery%22%3A%20%24%7Bbattery%7D%2C%0A%20%20%20%22rssi%22%3A%20%24%7Brssi%7D%0A%7D" } ];
</script>
<div class="row mb-3">
<div class="col-sm-2">
<button class="btn btn-primary" id="format-btn" data-bs-toggle="tooltip" title="Save the format template, saving an empty form will reset the template to default">Save</button>
<button class="btn btn-secondary" id="test-btn" data-bs-toggle="tooltip" title="Apply device data to template to see how it works">Test</button>
</div>
<select class="custom-select col-sm-4" required name="predefined" id="predefined" data-bs-toggle="tooltip" title="Select a pre-defined format template">
<option value="iSpindle-Post">iSpindle (POST)</option>
<option value="GravityMon-Post">GravityMon (POST)</option>
<option value="iSpindle-Mqtt">iSpindle (MQTT)</option>
<option value="HomeAssistant-Mqtt">Home Assistant (MQTT)</option>
<option value="HomeAssistant-Mqtt2">Home Assistant - Auto register sensor (MQTT)</option>
<option value="UBIDots-Post">UBIdots (POST)</option>
<option value="BrewFatherCustom-Post">Brewfather - Custom Endpoint (POST)</option>
<option value="iSpindle-Post">Brewfather - iSpindle Endpoint (POST)</option>
<option value="GravityMon-Get">GravityMon (GET)</option>
<option value="Brewblox-Mqtt">BrewBlox (MQTT)</option>
</select>
<div class="col-sm-4">
<button class="btn btn-secondary" id="copy-btn" data-bs-toggle="tooltip" title="Copy the selected format template to the selected push target">Copy format</button>
<button class="btn btn-secondary" id="clear-btn" data-bs-toggle="tooltip" title="Clear the current format template">Clear format</button>
</div> </div>
</div> </div>
<div class="col-sm-8" id="status">
</div>
<hr class="my-2">
<pre class="card-preview" id="preview" name="preview"></pre> <pre class="card-preview" id="preview" name="preview"></pre>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
<hr class="my-4"> <script type="text/javascript">
</div>
<script type="text/javascript">
window.onload = getConfig; window.onload = getConfig;
var maxCharsInFormatTemplate = 1500;
setButtonDisabled( true ); setButtonDisabled( true );
// Opens the targetet according (if URL has #collapseOne to #collapseFour) // Opens the targetet according (if URL has #collapseOne to #collapseFour)
@ -124,64 +198,191 @@
} }
}); });
function updateStatusField() {
var t = $("#format").val();
$("#status").text( t.length + " characters in template, recommended maximum is " + maxCharsInFormatTemplate );
if (t.length > maxCharsInFormatTemplate) {
showWarningLength();
} else {
hideWarningLength();
}
};
$("#push-target").change(function(e){ $("#push-target").change(function(e){
console.log(e) console.log(e)
selectFormat(); selectFormat();
}); });
// Copy the selected template
$("#copy-btn").click(function(e) {
var id = $("#predefined").val();
//console.log( encodeURIComponent( $("#format").val() ) );
formatTemplates.forEach(function (item, index) {
if( item.id == id ) {
$("#format").val( decodeURIComponent(item.format) );
}
});
updateStatusField();
});
// Clear the selected template
$("#clear-btn").click(function(e) {
$("#format").val( "" );
updateStatusField();
});
// Store the format // Store the format
$("#format-btn").click(function(e) { $("#format-btn").click(function(e) {
var s = $("#format").val(); var s = $("#format").val();
/*if (s.length > maxCharsInFormatTemplate) {
showError("Format template is too large for the device to handle, unable to save.")
return;
}*/
$('#spinner').show();
setButtonDisabled( true );
var ha = false;
hideWarningHomeAssistant();
if ($("#push-target").val() == "mqtt") {
if (s.search("homeassistant/sensor/") != -1) {
console.log("Current format is mqtt, it contains topics for Home Assistant device registration.")
showWarningHomeAssistant();
ha = true;
}
}
s = s.replaceAll("\n", ""); s = s.replaceAll("\n", "");
var obj = 'id=' + $("#id").val() + '&' + $("#push-target").val() + '=' + encodeURIComponent(s); var obj = 'id=' + $("#id").val() + "&" + $("#push-target").val() + '=' + encodeURIComponent(s);
console.log(obj); console.log(obj);
$.ajax( { $.ajax( {
type: "POST", type: "POST",
url: "/api/config/format", url: "/api/config/format",
data: obj, data: obj,
success: function(result) { showSuccess('Format stored successfully.'); getConfig(); }, success: function(result) { showSuccess('Format stored successfully.'); postHomeAssistant(ha); },
error: function(result) { showError('Unable to store format.'); } error: function(result) { showError('Unable to store format.'); $('#spinner').hide(); },
always: function() { $('#spinner').hide(); setButtonDisabled( false ); }
} ); } );
}); });
function postHomeAssistant(active) {
if (!active) {
getConfig();
return;
}
console.log("Current format is mqtt, running post tests to register device.")
$('#spinner').show();
setButtonDisabled( true );
var url = "/api/test/push";
url += "?id=" + $("#id").val() + "&format=mqtt";
//var url = "/test/push.json";
$.getJSON(url, function (cfg) {
console.log(cfg);
var code = cfg["code"];
var success = cfg["success"];
var enabled = cfg["enabled"];
if(success) {
showSuccess( "Format stored successfully. Home Assistant Device Registration Successful." );
} else {
showError( "Format stored successfully. Home Assistant Device Registration Failed!" );
}
})
.fail(function() {
showError( "Format stored successfully. Home Assistant Device Registration Failed!" );
})
.always(function() {
$('#spinner').hide();
setButtonDisabled( false );
getConfig();
});
}
// Test the calibration // Test the calibration
$("#test-btn").click(function(e) { $("#test-btn").click(function(e) {
var url = "/api/status";
//var url = "/test/status.json";
$('#spinner').show();
$.getJSON(url, function (cfg) {
console.log( cfg );
var doc = $("#format").val(); var doc = $("#format").val();
doc = doc.replaceAll("${mdns}", "gravmon2");
doc = doc.replaceAll("${id}", "e4a344"); if (cfg["temp-format"]=="C")
doc = doc.replaceAll("${sleep-interval}", "300"); doc = doc.replaceAll("${temp}", cfg["temp-c"]);
doc = doc.replaceAll("${temp}", "21.1"); else
doc = doc.replaceAll("${temp-c}", "21.1"); doc = doc.replaceAll("${temp}", cfg["temp-f"]);
doc = doc.replaceAll("${temp-f}", "51.3");
doc = doc.replaceAll("${temp-unit}", "C"); if (cfg["gravity-format"]=="G") {
doc = doc.replaceAll("${battery}", "3.86"); var sg = cfg["gravity"];
doc = doc.replaceAll("${rssi}", "-76"); doc = doc.replaceAll("${gravity-sg}", sg);
doc = doc.replaceAll("${run-time}", "4.32"); doc = doc.replaceAll("${corr-gravity-sg}", sg);
doc = doc.replaceAll("${gravity}", "1.044"); var plato = 259 - (259 - sg);
doc = doc.replaceAll("${gravity-sg}", "1.044"); doc = doc.replaceAll("${gravity-plato}", plato);
doc = doc.replaceAll("${gravity-plato}", "9.5"); doc = doc.replaceAll("${corr-gravity-plato}", plato);
doc = doc.replaceAll("${gravity-unit}", "G"); }
doc = doc.replaceAll("${corr-gravity}", "1.044"); else {
doc = doc.replaceAll("${corr-gravity-sg}", "1.044"); var plato = cfg["gravity"];
doc = doc.replaceAll("${corr-gravity-plato}", "9.5"); doc = doc.replaceAll("${gravity-plato}", plato);
doc = doc.replaceAll("${angle}", "54.5"); doc = doc.replaceAll("${corr-gravity-plato}", plato);
doc = doc.replaceAll("${tilt}", "54.5"); var sg = 259 / (259 - plato);
doc = doc.replaceAll("${gravity-sg}", sg);
doc = doc.replaceAll("${corr-gravity-sg}", sg);
}
doc = doc.replaceAll("${mdns}", cfg["mdns"]);
doc = doc.replaceAll("${id}", cfg["id"]);
doc = doc.replaceAll("${sleep-interval}", cfg["sleep-interval"]);
doc = doc.replaceAll("${token}", cfg["token"]);
doc = doc.replaceAll("${token2}", cfg["token2"]);
doc = doc.replaceAll("${temp-c}", cfg["temp-c"]);
doc = doc.replaceAll("${temp-f}", cfg["temp-f"]);
doc = doc.replaceAll("${temp-unit}", cfg["temp-format"]);
doc = doc.replaceAll("${battery}", cfg["battery"]);
doc = doc.replaceAll("${rssi}", cfg["rssi"]);
doc = doc.replaceAll("${run-time}", cfg["runtime-average"]);
doc = doc.replaceAll("${gravity}", cfg["gravity"]);
doc = doc.replaceAll("${gravity-unit}", cfg["gravity-format"]);
doc = doc.replaceAll("${corr-gravity}", cfg["gravity"]);
doc = doc.replaceAll("${angle}", cfg["angle"]);
doc = doc.replaceAll("${tilt}", cfg["angle"]);
doc = doc.replaceAll("${app-ver}", cfg["app-ver"]);
doc = doc.replaceAll("${app-build}", cfg["app-build"]);
// Format in a readable json string. // Format in a readable json string.
try { try {
var json = JSON.parse(doc); var json = JSON.parse(doc);
doc = JSON.stringify(json, null, 2); doc = JSON.stringify(json, null, 2);
} catch(e) { } catch(e) {
console.log("Not a javascript object!") console.log("Not a JSON object!")
} }
$("#preview").text(doc); $("#preview").text(doc);
})
.fail(function () {
showError('Unable to get data from the device.');
})
.always(function() {
$('#spinner').hide();
});
}); });
function setButtonDisabled( b ) { function setButtonDisabled( b ) {
$("#format-btn").prop("disabled", b); $("#format-btn").prop("disabled", b);
$("#test-btn").prop("disabled", b); $("#test-btn").prop("disabled", b);
$("#copy-btn").prop("disabled", b);
$("#clear-btn").prop("disabled", b);
$("#push-target").prop("disabled", b);
} }
function selectFormat() { function selectFormat() {
@ -193,6 +394,7 @@
console.log(s); console.log(s);
$("#format").val(s); $("#format").val(s);
$("#preview").text(""); $("#preview").text("");
updateStatusField();
} }
// Get the configuration values from the API // Get the configuration values from the API
@ -200,7 +402,7 @@
setButtonDisabled( true ); setButtonDisabled( true );
var url = "/api/config/format"; var url = "/api/config/format";
//var url = "/test/format.json"; // var url = "/test/format.json";
$('#spinner').show(); $('#spinner').show();
$.getJSON(url, function (cfg) { $.getJSON(url, function (cfg) {
console.log( cfg ); console.log( cfg );
@ -208,7 +410,7 @@
$("#http-1").val(cfg["http-1"]); $("#http-1").val(cfg["http-1"]);
$("#http-2").val(cfg["http-2"]); $("#http-2").val(cfg["http-2"]);
//$("#brewfather").val(cfg["brewfather"]); $("#http-3").val(cfg["http-3"]);
$("#influxdb").val(cfg["influxdb"]); $("#influxdb").val(cfg["influxdb"]);
$("#mqtt").val(cfg["mqtt"]); $("#mqtt").val(cfg["mqtt"]);
selectFormat(); selectFormat();
@ -220,11 +422,13 @@
$('#spinner').hide(); $('#spinner').hide();
setButtonDisabled( false ); setButtonDisabled( false );
}); });
updateStatusField();
} }
</script> </script>
<!-- START FOOTER --> <!-- START FOOTER -->
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div> <div class="container themed-container bg-primary text-light row-margin-10">(C) Copyright 2021-22 Magnus Persson</div>
</body> </body>
</html> </html>

File diff suppressed because one or more lines are too long

View File

@ -5,31 +5,38 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content=""> <meta name="description" content="">
<title>Beer Gravity Monitor</title> <title>Beer Gravity Monitor</title>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script> <style>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script> .row-margin-10 { margin-top: 1.0em; }
</style>
</head> </head>
<body class="py-4"> <body class="py-4">
<!-- START MENU --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<nav class="navbar navbar-expand-sm navbar-dark bg-primary"> <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarNav">
<div class="collapse navbar-collapse" id="navbar"> <ul class="navbar-nav">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/device.htm">Device</a> <a class="nav-link active" href="#">Home</a>
</li> </li>
<li class="nav-item"> <li class="nav-item dropdown">
<a class="nav-link" href="/config.htm">Configuration</a> <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Configuration
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/config.htm">Configuration</a></li>
<li><a class="dropdown-item" href="/format.htm">Format editor</a></li>
<li><a class="dropdown-item" href="/test.htm">Test push</a></li>
<li><a class="dropdown-item" href="/firmware.htm">Upload firmware</a></li>
</ul>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/calibration.htm">Calibration</a> <a class="nav-link" href="/calibration.htm">Calibration</a>
@ -38,72 +45,255 @@
<a class="nav-link" href="/about.htm">About</a> <a class="nav-link" href="/about.htm">About</a>
</li> </li>
</ul> </ul>
</div>
<div class="spinner-border text-light" id="spinner" role="status"></div> <div class="spinner-border text-light" id="spinner" role="status"></div>
</div> </div>
</nav> </nav>
<!-- START MAIN INDEX -->
<div class="container"> <!-- START MAIN INDEX -->
<hr class="my-4"> <div class="container row-margin-10">
<div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"> <div class="alert alert-success alert-dismissible hide fade d-none" role="alert">
<div id="alert-msg">...</div> <div id="alert"></div>
<button type="button" id="alert-btn" class="close" aria-label="Close"> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
<span aria-hidden="true">&times;</span>
</button>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
function showError( msg ) { function showError( msg ) {
$('.alert').removeClass('alert-success').addClass('alert-danger').removeClass('d-none').addClass('show') console.log("Error:" + msg);
$('#alert-msg').text( msg ); $('.alert').removeClass('alert-success').addClass('alert-danger').removeClass('hide').addClass('show').removeClass('d-none');
$('#alert').text( msg );
} }
function showSuccess( msg ) { function showSuccess( msg ) {
$('.alert').addClass('alert-success').removeClass('alert-danger').removeClass('d-none').addClass('show') console.log("Success:" + msg);
$('#alert-msg').text( msg ); $('.alert').addClass('alert-success').removeClass('alert-danger').removeClass('hide').addClass('show').removeClass('d-none');
$('#alert').text( msg );
} }
$("#alert-btn").click(function(e){ $("#alert-btn").click(function(e){
$('.alert').addClass('d-none').removeClass('show') console.log("Disable");
$('.alert').addClass('hide').removeClass('show').addClass('d-none');
}); });
</script> </script>
<div class="" id="id" hidden></div> <div class="accordion" id="accordion">
<h2 class="accordion-header" id="headingData">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseData" aria-expanded="true" aria-controls="collapseData">
<b>Measurement</b>
</button>
</h2>
<div id="collapseData" class="accordion-collapse collapse show" aria-labelledby="headingData" data-bs-parent="#accordion">
<div class="accordion-body">
<div class="row mb-3"> <div class="row mb-3">
<div class="col-md-8 themed-grid-col bg-light">Gravity:</div> <div class="col-md-4 bg-light">Gravity:</div>
<div class="col-md-4 themed-grid-col bg-light" id="gravity">Loading...</div> <div class="col-md-4 bg-light" id="gravity">Loading...</div>
</div> </div>
<div class="row mb-3"> <div class="row mb-3">
<div class="col-md-8 themed-grid-col bg-light">Temperature:</div> <div class="col-md-4 bg-light">Temperature:</div>
<div class="col-md-4 themed-grid-col bg-light" id="temp">Loading...</div> <div class="col-md-4 bg-light" id="temp">Loading...</div>
</div> </div>
<div class="row mb-3"> <div class="row mb-3">
<div class="col-md-8 themed-grid-col bg-light">Angle/Tilt:</div> <div class="col-md-4 bg-light">Angle/Tilt:</div>
<div class="col-md-4 themed-grid-col bg-light" id="angle">Loading...</div> <div class="col-md-4 bg-light" id="angle">Loading...</div>
</div> </div>
<div class="row mb-3"> <div class="row mb-3">
<div class="col-md-8 themed-grid-col bg-light">Battery:</div> <div class="col-md-4 bg-light">Battery:</div>
<div class="col-md-4 themed-grid-col bg-light" id="battery">Loading...</div> <div class="col-md-4 bg-light" id="battery">Loading...</div>
</div> </div>
<div class="row mb-3"> <div class="row mb-3">
<div class="col-md-12 px-md-5 themed-grid-col bg-light custom-control custom-checkbox"> <div class="col-md-4 bg-light">Average runtime:</div>
<div class="col-md-4 bg-light" id="runtime">Loading...</div>
</div>
<div class="row mb-3">
<div class="col-md-8 bg-light custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" name="sleep-mode" id="sleep-mode" disabled> <input type="checkbox" class="custom-control-input" name="sleep-mode" id="sleep-mode" disabled>
<label class="custom-control-label" for="sleep-mode">Do not enter sleep mode when floating (check this if you are collecting angles/tilt for calibration).</label> <label class="custom-control-label" for="sleep-mode">Do not enter sleep mode when floating (check this if you are collecting angles/tilt for calibration).</label>
</div> </div>
</div> </div>
<hr class="my-4"> </div>
</div> </div>
<div class="accordion-item">
<h2 class="accordion-header" id="headingSoftware">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSoftware" aria-expanded="true" aria-controls="collapseSoftware">
<b>Device</b>
</button>
</h2>
<div id="collapseSoftware" class="accordion-collapse collapse show" aria-labelledby="headingSoftware" data-bs-parent="#accordion">
<div class="accordion-body">
<div class="row mb-3">
<div class="col-md-4 bg-light">Current version:</div>
<div class="col-md-4 bg-light" id="app-ver">Loading...</div>
</div>
<div class="row mb-3" id="h-app-ver-new" hidden>
<div class="col-md-4 bg-light">New version:</div>
<div class="col-md-4 bg-light" id="app-ver-new">Loading...</div>
</div>
<div class="row mb-3">
<div class="col-md-4 bg-light">Host name:</div>
<div class="col-md-4 bg-light" id="mdns">Loading...</div>
</div>
<div class="row mb-3">
<div class="col-md-4 bg-light">Device ID:</div>
<div class="col-md-4 bg-light" id="id">Loading...</div>
</div>
<div class="row mb-3">
<div class="col-md-4 bg-light">Platform:</div>
<div class="col-md-4 bg-light" id="platform">Loading...</div>
</div>
<div class="row mb-3">
<div class="col-md-4 bg-light">SSID:</div>
<div class="col-md-4 bg-light" id="wifi-ssid">Loading...</div>
</div>
<script>
$("#log-btn").click(function(e){
loadLog();
});
setInterval(function() {
loadLog();
}, 30000);
function loadLog() {
var url2 = "/log2";
var url1 = "/log";
//var url2 = "/test/log2";
//var url1 = "/test/log1";
var log = "";
$.get(url2, function(data) {
console.log(data);
var list = data.split("\n");
list.forEach(function (item, index) {
log = item + "\n" + log;
});
}).always( function() {
$.get(url1, function(data) {
console.log(data);
var list = data.split("\n");
list.forEach(function (item, index) {
log = item + "\n" + log;
});
}).always( function() {
console.log(log);
$("#logContent").text(log);
});
});
};
</script>
<button class="btn btn-primary btn-sm" type="button" data-bs-toggle="collapse" data-bs-target="#collapseLog" aria-expanded="false" aria-controls="collapseLog" data-bs-toggle="tooltip" title="Load and show the last 10 errors that has occured on the device">
View error log
</button>
<button class="btn btn-secondary btn-sm" type="button" data-bs-toggle="tooltip" name="github-btn" id="github-btn" title="Go to github and place a issue">
Submit a issue on github
</button>
<button class="btn btn-secondary btn-sm" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSupport" aria-expanded="false" aria-controls="collapseSupport" data-bs-toggle="tooltip" name="support-btn" id="support-btn" title="Collect data relevant for a support case">
Gather support information
</button>
<div class="collapse row-margin-10" id="collapseLog">
<div class="card card-body">
<pre><code class="card-text" id="logContent" data-bs-toggle="tooltip" title="Shows the last errors on the device, newest on top.">Loading log data, please wait...</code></pre>
</div>
</div>
<div class="collapse row-margin-10" id="collapseSupport">
<div class="card card-body">
<pre><code class="card-text" id="supportContent">Collecting support data, please wait...</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
var debug = {};
<script type="text/javascript">
window.onload = start; window.onload = start;
$("#github-btn").click(function(e){
window.location.href = "https://github.com/mp-se/gravitymon/issues";
});
$("#support-btn").click(function(e){
$('#spinner').show();
var url = "/api/config";
//var url = "/test/config.json";
$.getJSON(url, function (cfg) {
debug = {};
debug["mdns"] = cfg["mdns"];
debug["id"] = cfg["id"];
debug["sleep-interval"] = cfg["sleep-interval"];
debug["temp-format"] = cfg["temp-format"];
debug["gravity-format"] = cfg["gravity-format"];
debug["gravity-temp-adjustment"] = cfg["gravity-temp-adjustment"];
debug["voltage-factor"] = cfg["voltage-factor"];
debug["platform"] = cfg["platform"];
debug["ble"] = cfg["ble"];
debug["gyro-temp"] = cfg["gyro-temp"];
debug["gyro-calibration-data"] = cfg["gyro-calibration-data"];
debug["temp-adjustment-value"] = cfg["temp-adjustment-value"];
var url = "/api/status";
//var url = "/test/status.json";
$.getJSON(url, function (cfg) {
debug["runtime-average"] = cfg["runtime-average"];
debug["rssi"] = cfg["rssi"];
debug["app-ver"] = cfg["app-ver"];
debug["app-build"] = cfg["app-build"];
});
var url = "/api/config/format";
//var url = "/test/format.json";
$.getJSON(url, function (cfg) {
debug["formats"] = cfg;
var url = "/api/config/advanced";
//var url = "/test/adv.json";
$.getJSON(url, function (cfg) {
debug["advanced"] = cfg;
var url = "/api/upload";
//var url = "/test/upload.json";
$.getJSON(url, function (cfg) {
debug["files"] = cfg;
var url = "/log";
//var url = "/test/log";
$.ajax({url: url, method: 'get', success: function (data) {
debug["log"] = data.split("\n");
var s = JSON.stringify(debug, null, 2);
$("#supportContent").text( s );
$('#spinner').hide();
//navigator.clipboard.writeText(s);
//alert("Support information copied to clipboard");
}
});
});
});
});
});
});
$("#sleep-mode").click(function(e){ $("#sleep-mode").click(function(e){
console.log( "Blocking sleep mode = " + $("#sleep-mode").is(":checked")); console.log( "Blocking sleep mode = " + $("#sleep-mode").is(":checked"));
$.ajax( { $.ajax( {
@ -119,22 +309,60 @@
var url = "/api/status"; var url = "/api/status";
//var url = "/test/status.json"; //var url = "/test/status.json";
$('#spinner').show(); $('#spinner').show();
$.getJSON(url, function (cfg) { $.getJSON(url, function (cfg) {
console.log( cfg ); console.log( cfg );
$("#app-ver").text(cfg["app-ver"] + " (" + cfg["app-build"] + ")");
$("#mdns").text(cfg["mdns"]);
$("#id").text(cfg["id"]); $("#id").text(cfg["id"]);
$("#platform").text(cfg["platform"]);
$("#wifi-ssid").text(cfg["wifi-ssid"]);
$("#runtime").text(cfg["runtime-average"] + " seconds");
var angle = cfg["angle"];
if(angle==-1) {
showError("Unable to connect to gyro, try a reset or power off/on")
$("#angle").text("No gyro");
$("#gravity").text("No gyro");
} else if(angle==0) {
$("#angle").text("Gyro moving");
$("#gravity").text("Gyro moving");
} else {
$("#angle").text(cfg["angle"]); $("#angle").text(cfg["angle"]);
if( cfg["gravity-format"] == "G") if( cfg["gravity-format"] == "G")
$("#gravity").text(cfg["gravity"] + " SG"); $("#gravity").text(cfg["gravity"] + " SG");
else else
$("#gravity").text(cfg["gravity"] + " °P"); $("#gravity").text(cfg["gravity"] + " °P");
}
$("#battery").text(cfg["battery"] + " V"); var batt = cfg["battery"];
var charge = 0;
if(batt>4.15) charge = 100;
else if(batt>4.05) charge = 90;
else if(batt>3.97) charge = 80;
else if(batt>3.91) charge = 70;
else if(batt>3.86) charge = 60;
else if(batt>3.81) charge = 50;
else if(batt>3.78) charge = 40;
else if(batt>3.76) charge = 30;
else if(batt>3.73) charge = 20;
else if(batt>3.67) charge = 10;
else if(batt>3.44) charge = 5;
$("#battery").text(batt + " V (" + charge + "%)" );
if( cfg["temp-c"] == -273) {
$("#temp").text("No temp sensor");
} else {
if( cfg["temp-format"] == "C") if( cfg["temp-format"] == "C")
$("#temp").text(cfg["temp-c"] + " C"); $("#temp").text(cfg["temp-c"] + " C");
else else
$("#temp").text(cfg["temp-f"] + " F"); $("#temp").text(cfg["temp-f"] + " F");
}
if( cfg["sleep-mode"] ) if( cfg["sleep-mode"] )
$("#sleep-mode").attr("checked", true ); $("#sleep-mode").attr("checked", true );
@ -151,12 +379,12 @@
} }
function start() { function start() {
setInterval(getStatus, 3000); setInterval(getStatus, 5000);
} }
</script> </script>
<!-- START FOOTER --> <!-- START FOOTER -->
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div> <div class="container themed-container bg-primary text-light row-margin-10">(C) Copyright 2021-22 Magnus Persson</div>
</body> </body>
</html> </html>

File diff suppressed because one or more lines are too long

244
html/test.htm Normal file
View File

@ -0,0 +1,244 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<title>Beer Gravity Monitor</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<style>
.row-margin-10 { margin-top: 1.0em; }
</style>
</head>
<body class="py-4">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/index.htm">Home</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle active" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Configuration
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/config.htm">Configuration</a></li>
<li><a class="dropdown-item" href="/format.htm">Format editor</a></li>
<li><a class="dropdown-item" href="#">Test push</a></li>
<li><a class="dropdown-item" href="/firmware.htm">Upload firmware</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link" href="/calibration.htm">Calibration</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/about.htm">About</a>
</li>
</ul>
</div>
<div class="spinner-border text-light" id="spinner" role="status"></div>
</div>
</nav>
<!-- START MAIN INDEX -->
<div class="container row-margin-10">
<div class="alert alert-success alert-dismissible hide fade d-none" role="alert">
<div id="alert"></div>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<script type="text/javascript">
function showError( msg ) {
$('.alert').removeClass('alert-success').addClass('alert-danger').removeClass('hide').addClass('show').removeClass('d-none');
$('#alert').text( msg );
}
function showSuccess( msg ) {
$('.alert').addClass('alert-success').removeClass('alert-danger').removeClass('hide').addClass('show').removeClass('d-none');
$('#alert').text( msg );
}
$("#alert-btn").click(function(e){
$('.alert').addClass('hide').removeClass('show').addClass('d-none');
});
</script>
<div class="accordion" id="accordion">
<div class="accordion-item">
<h2 class="accordion-header" id="headingTest">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTest" aria-expanded="true" aria-controls="collapseTest">
<b>Testing push targets</b>
</button>
</h2>
<div id="collapseTest" class="accordion-collapse collapse show" aria-labelledby="headingTest" data-bs-parent="#accordion">
<div class="accordion-body">
<div class="row mb-3">
<pre class="card-preview" id="preview" name="preview">Press test button to start testing all defined push targets.</pre>
</div>
<div class="row mb-3">
<div class="col-sm-8">
<button type="button" class="btn btn-primary" id="test-btn" data-bs-toggle="tooltip" title="Test all push targets">Test</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$('#spinner').hide();
function clearLog() {
$("#preview").text("");
}
function appendLog(log) {
doc = $("#preview").text();
doc += log + "\n";
$("#preview").text(doc);
}
// Get the configuration values from the API
$("#test-btn").click(function(e) {
clearLog();
appendLog( "Starting test of push targets" );
var url = "/api/status";
//var url = "/test/status.json";
$("#test-btn").prop("disabled", true);
$('#spinner').show();
$.getJSON(url, function (cfg) {
var id = cfg["id"];
console.log( id );
testHttp( id, "http-1" );
testHttp( id, "http-2" );
testHttp( id, "http-3" );
testInfluxdb( id );
testMqtt( id );
$('#spinner').hide();
$("#test-btn").prop("disabled", false);
})
.fail(function () {
showError('Unable to get data from the device.');
$('#spinner').hide();
$("#test-btn").prop("disabled", false);
})
.always(function() {
});
});
function testMqtt(id) {
var url = "/api/test/push";
url += "?id=" + id + "&format=mqtt";
//var url = "/test/push.json";
$.getJSON(url, function (cfg) {
var code = cfg["code"];
var success = cfg["success"];
var enabled = cfg["enabled"];
if(!enabled) {
appendLog( "Push target 'mqtt' is not configured/used" );
} else if(success) {
appendLog( "Push target 'mqtt' successful" );
} else{
if(code==-3)
appendLog( "Push target 'mqtt' failed to connect" );
else if(code==-4)
appendLog( "Push target 'mqtt' failed with error timeout" );
else if(code==-10)
appendLog( "Push target 'mqtt' failed with error denied" );
else if(code==-100)
appendLog( "Push target 'mqtt' skipped since it's using SSL" );
else
appendLog( "Push target 'mqtt' failed with error code " + code );
}
})
.fail(function () {
appendLog( "Failed to test push target 'mqtt'");
})
}
function testInfluxdb(id) {
var url = "/api/test/push";
url += "?id=" + id + "&format=influxdb";
//var url = "/test/push.json";
$.getJSON(url, function (cfg) {
var code = cfg["code"];
var success = cfg["success"];
var enabled = cfg["enabled"];
if(!enabled) {
appendLog( "Push target 'influxdb' is not configured/used" );
} else if(success) {
appendLog( "Push target 'influxdb' successful" );
} else{
if(code==400)
appendLog( "Push target 'influxdb' failed with error code 400, bad request" );
else if(code==401)
appendLog( "Push target 'influxdb' failed with error code 401, unauthorized" );
else if(code==404)
appendLog( "Push target 'influxdb' failed with error code 404, url not found" );
else if(code==-100)
appendLog( "Push target 'influxdb' skipped since it's using SSL" );
else
appendLog( "Push target 'influxdb' failed with error code " + code );
}
})
.fail(function () {
appendLog( "Failed to test push target 'influxdb'");
})
}
function testHttp(id, target) {
var url = "/api/test/push";
url += "?id=" + id + "&format=" + target;
//var url = "/test/push.json";
$.getJSON(url, function (cfg) {
var code = cfg["code"];
var success = cfg["success"];
var enabled = cfg["enabled"];
if(!enabled) {
appendLog( "Push target '" + target + "' is not configured/used" );
} else if(success) {
appendLog( "Push target '" + target + "' successful" );
} else{
if(code==400)
appendLog( "Push target '" + target + "' failed with error code 400, bad request" );
else if(code==401)
appendLog( "Push target '" + target + "' failed with error code 401, unauthorized" );
else if(code==404)
appendLog( "Push target '" + target + "' failed with error code 404, url not found" );
else if(code==-100)
appendLog( "Push target '" + target + "' skipped since it's using SSL" );
else
appendLog( "Push target '" + target + "' failed with error code " + code );
}
})
.fail(function () {
appendLog( "Failed to test push target '" + target + "'");
})
}
</script>
<!-- START FOOTER -->
<div class="container themed-container bg-primary text-light row-margin-10">(C) Copyright 2021-22 Magnus Persson</div>
</body>
</html>

1
html/test.min.htm Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,164 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<title>Beer Gravity Monitor</title>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script>
</head>
<body class="py-4">
<!-- START MENU -->
<nav class="navbar navbar-expand-sm navbar-dark bg-primary">
<a class="navbar-brand" href="/upload.htm">Beer Gravity Monitor - Missing html files</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbar">
<div class="spinner-border text-light" id="spinner" role="status"></div>
</div>
</nav>
<!-- START MAIN INDEX -->
<div class="container">
<hr class="my-4">
<div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert">
<div id="alert-msg">...</div>
<button type="button" id="alert-btn" class="close" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<script type="text/javascript">
function showError( msg ) {
$('.alert').removeClass('alert-success').addClass('alert-danger').removeClass('d-none').addClass('show')
$('#alert-msg').text( msg );
}
function showSuccess( msg ) {
$('.alert').addClass('alert-success').removeClass('alert-danger').removeClass('d-none').addClass('show')
$('#alert-msg').text( msg );
}
$("#alert-btn").click(function(e){
$('.alert').addClass('d-none').removeClass('show')
});
</script>
<div class="row mb-3">
<div class="col-md-8 themed-grid-col bg-light">The listed files below needs to be uploaded to the FileSystem in order for the GUI to work.
You can also flash the LittleFS filesystem but in that case you will loose your device settings. An OTA upgrade will automatically download
the files if they are found in the same location as the firmware.bin. This page is a fallback option.
</div>
<div class="col-md-8 themed-grid-col bg-light"><br><strong>Once all the files are confirmed, please reboot the device for normal operation.</strong></div>
</div>
<div class="row mb-3">
<div class="col-md-2 themed-grid-col bg-light">index.min.htm</div>
<div class="col-md-6 themed-grid-col bg-light" id="index">Checking...</div>
</div>
<div class="row mb-3">
<div class="col-md-2 themed-grid-col bg-light">device.min.htm</div>
<div class="col-md-6 themed-grid-col bg-light" id="device">Checking...</div>
</div>
<div class="row mb-3">
<div class="col-md-2 themed-grid-col bg-light">config.min.htm</div>
<div class="col-md-6 themed-grid-col bg-light" id="config">Checking...</div>
</div>
<div class="row mb-3">
<div class="col-md-2 themed-grid-col bg-light">calibration.min.htm</div>
<div class="col-md-6 themed-grid-col bg-light" id="calibration">Checking...</div>
</div>
<div class="row mb-3">
<div class="col-md-2 themed-grid-col bg-light">format.min.htm</div>
<div class="col-md-6 themed-grid-col bg-light" id="format">Checking...</div>
</div>
<div class="row mb-3">
<div class="col-md-2 themed-grid-col bg-light">about.min.htm</div>
<div class="col-md-6 themed-grid-col bg-light" id="about">Checking...</div>
</div>
<div class="row mb-3">
<form action="/api/upload" method="post" enctype="multipart/form-data">
<div class="col-md-8 custom-file">
<input type="file" class="custom-file-input" name="name" id="name">
<label class="custom-file-label" for="name">Choose file</label>
</div>
<button type="submit" class="btn btn-primary" id="upload-btn" value="upload">Upload</button>
</form>
</div>
<hr class="my-4">
</div>
<script type="text/javascript">
window.onload = getUpload;
// Add the following code if you want the name of the file appear on select
$(".custom-file-input").on("change", function() {
var fileName = $(this).val().split("\\").pop();
$(this).siblings(".custom-file-label").addClass("selected").html(fileName);
});
function getUpload() {
var url = "/api/upload";
//var url = "/test/upload.json";
$('#spinner').show();
$.getJSON(url, function (cfg) {
console.log( cfg );
if( cfg["index"] )
$("#index").text("Completed.");
else
$("#index").text("File is missing.");
if( cfg["device"] )
$("#device").text("Completed.");
else
$("#device").text("File is missing.");
if( cfg["config"] )
$("#config").text("Completed.");
else
$("#config").text("File is missing.");
if( cfg["calibration"] )
$("#calibration").text("Completed.");
else
$("#calibration").text("File is missing.");
if( cfg["format"] )
$("#format").text("Completed.");
else
$("#format").text("File is missing.");
if( cfg["about"] )
$("#about").text("Completed.");
else
$("#about").text("File is missing.");
})
.fail(function () {
showError('Unable to get data from the device.');
})
.always(function() {
$('#spinner').hide();
});
}
</script>
<!-- START FOOTER -->
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div>
</body>
</html>

View File

@ -1 +0,0 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/upload.htm">Beer Gravity Monitor - Missing html files</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">The listed files below needs to be uploaded to the FileSystem in order for the GUI to work. You can also flash the LittleFS filesystem but in that case you will loose your device settings. An OTA upgrade will automatically download the files if they are found in the same location as the firmware.bin. This page is a fallback option.</div><div class="col-md-8 themed-grid-col bg-light"><br><strong>Once all the files are confirmed, please reboot the device for normal operation.</strong></div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">index.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="index">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">device.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="device">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">config.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="config">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">calibration.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="calibration">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">format.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="format">Checking...</div></div><div class="row mb-3"><div class="col-md-2 themed-grid-col bg-light">about.min.htm</div><div class="col-md-6 themed-grid-col bg-light" id="about">Checking...</div></div><div class="row mb-3"><form action="/api/upload" method="post" enctype="multipart/form-data"><div class="col-md-8 custom-file"><input type="file" class="custom-file-input" name="name" id="name"> <label class="custom-file-label" for="name">Choose file</label></div><button type="submit" class="btn btn-primary" id="upload-btn" value="upload">Upload</button></form></div><hr class="my-4"></div><script type="text/javascript">function getUpload(){var i="/api/upload";$("#spinner").show(),$.getJSON(i,function(i){console.log(i),i.index?$("#index").text("Completed."):$("#index").text("File is missing."),i.device?$("#device").text("Completed."):$("#device").text("File is missing."),i.config?$("#config").text("Completed."):$("#config").text("File is missing."),i.calibration?$("#calibration").text("Completed."):$("#calibration").text("File is missing."),i.format?$("#format").text("Completed."):$("#format").text("File is missing."),i.about?$("#about").text("Completed."):$("#about").text("File is missing.")}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}window.onload=getUpload,$(".custom-file-input").on("change",function(){var i=$(this).val().split("\\").pop();$(this).siblings(".custom-file-label").addClass("selected").html(i)})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>

View File

@ -9,7 +9,7 @@
Built by Khoi Hoang https://github.com/khoih-prog/ESP_DoubleResetDetector Built by Khoi Hoang https://github.com/khoih-prog/ESP_DoubleResetDetector
Licensed under MIT license Licensed under MIT license
Version: 1.2.1 Version: 1.3.2
Version Modified By Date Comments Version Modified By Date Comments
------- ----------- ---------- ----------- ------- ----------- ---------- -----------
@ -22,6 +22,9 @@
1.1.2 K Hoang 10/10/2021 Update `platform.ini` and `library.json` 1.1.2 K Hoang 10/10/2021 Update `platform.ini` and `library.json`
1.2.0 K Hoang 26/11/2021 Auto detect ESP32 core and use either built-in LittleFS or LITTLEFS library 1.2.0 K Hoang 26/11/2021 Auto detect ESP32 core and use either built-in LittleFS or LITTLEFS library
1.2.1 K Hoang 26/11/2021 Fix compile error for ESP32 core v1.0.5- 1.2.1 K Hoang 26/11/2021 Fix compile error for ESP32 core v1.0.5-
1.3.0 K Hoang 10/02/2022 Add support to new ESP32-S3
1.3.1 K Hoang 04/03/2022 Add waitingForDRD() function to signal in DRD wating period
1.3.2 K Hoang 09/09/2022 Fix ESP32 chipID for example ConfigOnDoubleReset
*****************************************************************************************************************************/ *****************************************************************************************************************************/
#pragma once #pragma once
@ -29,13 +32,26 @@
#ifndef ESP_DoubleResetDetector_H #ifndef ESP_DoubleResetDetector_H
#define ESP_DoubleResetDetector_H #define ESP_DoubleResetDetector_H
#ifndef DOUBLERESETDETECTOR_DEBUG
#define DOUBLERESETDETECTOR_DEBUG false
#endif
#if defined(ARDUINO) && (ARDUINO >= 100) #if defined(ARDUINO) && (ARDUINO >= 100)
#include <Arduino.h> #include <Arduino.h>
#else #else
#include <WProgram.h> #include <WProgram.h>
#endif #endif
#define ESP_DOUBLE_RESET_DETECTOR_VERSION "ESP_DoubleResetDetector v1.2.1" #ifndef ESP_DOUBLE_RESET_DETECTOR_VERSION
#define ESP_DOUBLE_RESET_DETECTOR_VERSION "ESP_DoubleResetDetector v1.3.2"
#define ESP_DOUBLE_RESET_DETECTOR_VERSION_MAJOR 1
#define ESP_DOUBLE_RESET_DETECTOR_VERSION_MINOR 3
#define ESP_DOUBLE_RESET_DETECTOR_VERSION_PATCH 2
#define ESP_DOUBLE_RESET_DETECTOR_VERSION_INT 1003002
#endif
#define ESP_DOUBLERESETDETECTOR_VERSION ESP_DOUBLE_RESET_DETECTOR_VERSION #define ESP_DOUBLERESETDETECTOR_VERSION ESP_DOUBLE_RESET_DETECTOR_VERSION
//#define ESP_DRD_USE_EEPROM false //#define ESP_DRD_USE_EEPROM false
@ -45,7 +61,11 @@
#ifdef ESP32 #ifdef ESP32
#if (!ESP_DRD_USE_EEPROM && !ESP_DRD_USE_SPIFFS && !ESP_DRD_USE_LITTLEFS) #if (!ESP_DRD_USE_EEPROM && !ESP_DRD_USE_SPIFFS && !ESP_DRD_USE_LITTLEFS)
#if (DOUBLERESETDETECTOR_DEBUG)
#warning Neither EEPROM, SPIFFS nor LittleFS selected. Default to EEPROM #warning Neither EEPROM, SPIFFS nor LittleFS selected. Default to EEPROM
#endif
#ifdef ESP_DRD_USE_EEPROM #ifdef ESP_DRD_USE_EEPROM
#undef ESP_DRD_USE_EEPROM #undef ESP_DRD_USE_EEPROM
#define ESP_DRD_USE_EEPROM true #define ESP_DRD_USE_EEPROM true
@ -55,7 +75,10 @@
#ifdef ESP8266 #ifdef ESP8266
#if (!ESP8266_DRD_USE_RTC && !ESP_DRD_USE_EEPROM && !ESP_DRD_USE_SPIFFS && !ESP_DRD_USE_LITTLEFS) #if (!ESP8266_DRD_USE_RTC && !ESP_DRD_USE_EEPROM && !ESP_DRD_USE_SPIFFS && !ESP_DRD_USE_LITTLEFS)
#if (DOUBLERESETDETECTOR_DEBUG)
#warning Neither RTC, EEPROM, LITTLEFS nor SPIFFS selected. Default to EEPROM #warning Neither RTC, EEPROM, LITTLEFS nor SPIFFS selected. Default to EEPROM
#endif
#ifdef ESP_DRD_USE_EEPROM #ifdef ESP_DRD_USE_EEPROM
#undef ESP_DRD_USE_EEPROM #undef ESP_DRD_USE_EEPROM
#define ESP_DRD_USE_EEPROM true #define ESP_DRD_USE_EEPROM true
@ -87,14 +110,20 @@
// Check cores/esp32/esp_arduino_version.h and cores/esp32/core_version.h // Check cores/esp32/esp_arduino_version.h and cores/esp32/core_version.h
//#if ( ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0) ) //(ESP_ARDUINO_VERSION_MAJOR >= 2) //#if ( ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0) ) //(ESP_ARDUINO_VERSION_MAJOR >= 2)
#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) ) #if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) )
#if (DOUBLERESETDETECTOR_DEBUG)
#warning Using ESP32 Core 1.0.6 or 2.0.0+ #warning Using ESP32 Core 1.0.6 or 2.0.0+
#endif
// The library has been merged into esp32 core from release 1.0.6 // The library has been merged into esp32 core from release 1.0.6
#include <LittleFS.h> #include <LittleFS.h>
#define FileFS LittleFS #define FileFS LittleFS
#define FS_Name "LittleFS" #define FS_Name "LittleFS"
#else #else
#if (DOUBLERESETDETECTOR_DEBUG)
#warning Using ESP32 Core 1.0.5-. You must install LITTLEFS library #warning Using ESP32 Core 1.0.5-. You must install LITTLEFS library
#endif
// The library has been merged into esp32 core from release 1.0.6 // The library has been merged into esp32 core from release 1.0.6
#include <LITTLEFS.h> // https://github.com/lorol/LITTLEFS #include <LITTLEFS.h> // https://github.com/lorol/LITTLEFS
@ -125,9 +154,7 @@
#endif //#if ESP_DRD_USE_EEPROM #endif //#if ESP_DRD_USE_EEPROM
#ifndef DOUBLERESETDETECTOR_DEBUG
#define DOUBLERESETDETECTOR_DEBUG false
#endif
#define DOUBLERESETDETECTOR_FLAG_SET 0xD0D01234 #define DOUBLERESETDETECTOR_FLAG_SET 0xD0D01234
#define DOUBLERESETDETECTOR_FLAG_CLEAR 0xD0D04321 #define DOUBLERESETDETECTOR_FLAG_CLEAR 0xD0D04321
@ -195,6 +222,11 @@ class DoubleResetDetector
}; };
bool waitingForDRD()
{
return waitingForDoubleReset;
}
void loop() void loop()
{ {
if (waitingForDoubleReset && millis() > timeout) if (waitingForDoubleReset && millis() > timeout)

View File

@ -16,47 +16,18 @@
Built by Khoi Hoang https://github.com/khoih-prog/ESP_WiFiManager Built by Khoi Hoang https://github.com/khoih-prog/ESP_WiFiManager
Licensed under MIT license Licensed under MIT license
Version: 1.8.0 Version: 1.11.0
Version Modified By Date Comments Version Modified By Date Comments
------- ----------- ---------- ----------- ------- ----------- ---------- -----------
1.0.0 K Hoang 07/10/2019 Initial coding 1.0.0 K Hoang 07/10/2019 Initial coding
1.0.1 K Hoang 13/12/2019 Fix bug. Add features. Add support for ESP32 ...
1.0.2 K Hoang 19/12/2019 Fix bug thatkeeps ConfigPortal in endless loop if Portal/Router SSID or Password is NULL.
1.0.3 K Hoang 05/01/2020 Option not displaying AvailablePages in Info page. Enhance README.md. Modify examples
1.0.4 K Hoang 07/01/2020 Add RFC952 setHostname feature.
1.0.5 K Hoang 15/01/2020 Add configurable DNS feature. Thanks to @Amorphous of https://community.blynk.cc
1.0.6 K Hoang 03/02/2020 Add support for ArduinoJson version 6.0.0+ ( tested with v6.14.1 )
1.0.7 K Hoang 13/04/2020 Reduce start time, fix SPIFFS bug in examples, update README.md
1.0.8 K Hoang 10/06/2020 Fix STAstaticIP issue. Restructure code. Add LittleFS support for ESP8266 core 2.7.1+
1.0.9 K Hoang 29/07/2020 Fix ESP32 STAstaticIP bug. Permit changing from DHCP <-> static IP using Config Portal.
Add, enhance examples (fix MDNS for ESP32)
1.0.10 K Hoang 08/08/2020 Add more features to Config Portal. Use random WiFi AP channel to avoid conflict.
1.0.11 K Hoang 17/08/2020 Add CORS feature. Fix bug in softAP, autoConnect, resetSettings.
1.1.0 K Hoang 28/08/2020 Add MultiWiFi feature to autoconnect to best WiFi at runtime
1.1.1 K Hoang 30/08/2020 Add setCORSHeader function to allow flexible CORS. Fix typo and minor improvement.
1.1.2 K Hoang 17/08/2020 Fix bug. Add example.
1.2.0 K Hoang 09/10/2020 Restore cpp code besides Impl.h code to use if linker error. Fix bug.
1.3.0 K Hoang 04/12/2020 Add LittleFS support to ESP32 using LITTLEFS Library
1.4.1 K Hoang 22/12/2020 Fix staticIP not saved. Add functions. Add complex examples. Sync with ESPAsync_WiFiManager
1.4.2 K Hoang 14/01/2021 Fix examples' bug not using saved WiFi Credentials after losing all WiFi connections.
1.4.3 K Hoang 23/01/2021 Fix examples' bug not saving Static IP in certain cases.
1.5.0 K Hoang 12/02/2021 Add support to new ESP32-S2
1.5.1 K Hoang 26/03/2021 Fix compiler error if setting Compiler Warnings to All. Retest with esp32 core v1.0.6
1.5.2 K Hoang 08/04/2021 Fix example misleading messages.
1.5.3 K Hoang 13/04/2021 Add dnsServer error message.
1.6.0 K Hoang 20/04/2021 Add support to new ESP32-C3 using SPIFFS or EEPROM
1.6.1 K Hoang 25/04/2021 Fix MultiWiFi bug. Fix captive-portal bug if CP AP address is not default 192.168.4.1
1.7.0 K Hoang 06/05/2021 Set _timezoneName. Add support to new ESP32-S2 (METRO_ESP32S2, FUNHOUSE_ESP32S2, etc.)
1.7.1 K Hoang 08/05/2021 Fix Json bug. Fix timezoneName not displayed in Info page.
1.7.2 K Hoang 08/05/2021 Fix warnings with ESP8266 core v3.0.0
1.7.3 K Hoang 29/07/2021 Fix MultiWiFi connection issue with ESP32 core v2.0.0-rc1+
1.7.4 K Hoang 13/08/2021 Add WiFi scanning of hidden SSIDs
1.7.5 K Hoang 10/10/2021 Update `platform.ini` and `library.json`
1.7.6 K Hoang 26/11/2021 Auto detect ESP32 core and use either built-in LittleFS or LITTLEFS library
1.7.7 K Hoang 26/11/2021 Fix compile error for ESP32 core v1.0.5-
1.7.8 K Hoang 30/11/2021 Fix bug to permit using HTTP port different from 80. Fix bug
1.8.0 K Hoang 29/12/2021 Fix `multiple-definitions` linker error and weird bug related to src_cpp 1.8.0 K Hoang 29/12/2021 Fix `multiple-definitions` linker error and weird bug related to src_cpp
1.9.0 K Hoang 17/01/2022 Enable compatibility with old code to include only ESP_WiFiManager.h
1.10.0 K Hoang 10/02/2022 Add support to new ESP32-S3
1.10.1 K Hoang 11/02/2022 Add LittleFS support to ESP32-C3. Use core LittleFS instead of Lorol's LITTLEFS for v2.0.0+
1.10.2 K Hoang 13/03/2022 Send CORS header in handleWifiSave() function
1.11.0 K Hoang 09/09/2022 Fix ESP32 chipID and add ESP_getChipOUI()
*****************************************************************************************************************************/ *****************************************************************************************************************************/
#pragma once #pragma once
@ -80,7 +51,7 @@ ESP_WMParameter::ESP_WMParameter(const char *custom)
////////////////////////////////////////// //////////////////////////////////////////
ESP_WMParameter::ESP_WMParameter(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom, int labelPlacement) ESP_WMParameter::ESP_WMParameter(const char *id, const char *placeholder, const char *defaultValue, const int& length, const char *custom, const int& labelPlacement)
{ {
init(id, placeholder, defaultValue, length, custom, labelPlacement); init(id, placeholder, defaultValue, length, custom, labelPlacement);
} }
@ -94,7 +65,7 @@ ESP_WMParameter::ESP_WMParameter(const WMParam_Data& WMParam_data)
////// //////
////////////////////////////////////////// //////////////////////////////////////////
void ESP_WMParameter::init(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom, int labelPlacement) void ESP_WMParameter::init(const char *id, const char *placeholder, const char *defaultValue, const int& length, const char *custom, const int& labelPlacement)
{ {
_WMParam_data._id = id; _WMParam_data._id = id;
_WMParam_data._placeholder = placeholder; _WMParam_data._placeholder = placeholder;
@ -252,7 +223,8 @@ ESP_WiFiManager::ESP_WiFiManager(const char *iHostname)
#ifdef ESP8266 #ifdef ESP8266
String _hostname = "ESP8266-" + String(ESP.getChipId(), HEX); String _hostname = "ESP8266-" + String(ESP.getChipId(), HEX);
#else //ESP32 #else //ESP32
String _hostname = "ESP32-" + String((uint32_t)ESP.getEfuseMac(), HEX); String _hostname = "ESP32-" + String(ESP_getChipId(), HEX);
#endif #endif
_hostname.toUpperCase(); _hostname.toUpperCase();
@ -461,7 +433,8 @@ bool ESP_WiFiManager::autoConnect()
#ifdef ESP8266 #ifdef ESP8266
String ssid = "ESP_" + String(ESP.getChipId()); String ssid = "ESP_" + String(ESP.getChipId());
#else //ESP32 #else //ESP32
String ssid = "ESP_" + String((uint32_t)ESP.getEfuseMac()); String ssid = "ESP_" + String(ESP_getChipId());
#endif #endif
return autoConnect(ssid.c_str(), NULL); return autoConnect(ssid.c_str(), NULL);
@ -522,7 +495,7 @@ bool ESP_WiFiManager::startConfigPortal()
#ifdef ESP8266 #ifdef ESP8266
String ssid = "ESP_" + String(ESP.getChipId()); String ssid = "ESP_" + String(ESP.getChipId());
#else //ESP32 #else //ESP32
String ssid = "ESP_" + String((uint32_t)ESP.getEfuseMac()); String ssid = "ESP_" + String(ESP_getChipId());
#endif #endif
ssid.toUpperCase(); ssid.toUpperCase();
@ -864,7 +837,7 @@ void ESP_WiFiManager::startWPS()
//Convenient for debugging but wasteful of program space. //Convenient for debugging but wasteful of program space.
//Remove if short of space //Remove if short of space
const char* ESP_WiFiManager::getStatus(int status) const char* ESP_WiFiManager::getStatus(const int& status)
{ {
switch (status) switch (status)
{ {
@ -920,21 +893,21 @@ void ESP_WiFiManager::resetSettings()
////////////////////////////////////////// //////////////////////////////////////////
void ESP_WiFiManager::setTimeout(unsigned long seconds) void ESP_WiFiManager::setTimeout(const unsigned long& seconds)
{ {
setConfigPortalTimeout(seconds); setConfigPortalTimeout(seconds);
} }
////////////////////////////////////////// //////////////////////////////////////////
void ESP_WiFiManager::setConfigPortalTimeout(unsigned long seconds) void ESP_WiFiManager::setConfigPortalTimeout(const unsigned long& seconds)
{ {
_configPortalTimeout = seconds * 1000; _configPortalTimeout = seconds * 1000;
} }
////////////////////////////////////////// //////////////////////////////////////////
void ESP_WiFiManager::setConnectTimeout(unsigned long seconds) void ESP_WiFiManager::setConnectTimeout(const unsigned long& seconds)
{ {
_connectTimeout = seconds * 1000; _connectTimeout = seconds * 1000;
} }
@ -949,7 +922,7 @@ void ESP_WiFiManager::setDebugOutput(bool debug)
////////////////////////////////////////// //////////////////////////////////////////
// KH, new from v1.0.10 to enable dynamic/random channel // KH, new from v1.0.10 to enable dynamic/random channel
int ESP_WiFiManager::setConfigPortalChannel(int channel) int ESP_WiFiManager::setConfigPortalChannel(const int& channel)
{ {
// If channel < MIN_WIFI_CHANNEL - 1 or channel > MAX_WIFI_CHANNEL => channel = 1 // If channel < MIN_WIFI_CHANNEL - 1 or channel > MAX_WIFI_CHANNEL => channel = 1
// If channel == 0 => will use random channel from MIN_WIFI_CHANNEL to MAX_WIFI_CHANNEL // If channel == 0 => will use random channel from MIN_WIFI_CHANNEL to MAX_WIFI_CHANNEL
@ -1037,7 +1010,7 @@ void ESP_WiFiManager::setSTAStaticIPConfig(const IPAddress& ip, const IPAddress&
////////////////////////////////////////// //////////////////////////////////////////
void ESP_WiFiManager::setMinimumSignalQuality(int quality) void ESP_WiFiManager::setMinimumSignalQuality(const int& quality)
{ {
_minimumQuality = quality; _minimumQuality = quality;
} }
@ -1393,6 +1366,11 @@ void ESP_WiFiManager::handleWifiSave()
/////////////////////// ///////////////////////
#if USING_CORS_FEATURE
// New from v1.10.2, for configure CORS Header, default to WM_HTTP_CORS_ALLOW_ALL = "*"
server->sendHeader(FPSTR(WM_HTTP_CORS), _CORS_Header);
#endif
#if USE_ESP_WIFIMANAGER_NTP #if USE_ESP_WIFIMANAGER_NTP
if (server->arg("timezone") != "") if (server->arg("timezone") != "")
@ -1584,10 +1562,23 @@ void ESP_WiFiManager::handleInfo()
page += F("<table class=\"table\">"); page += F("<table class=\"table\">");
page += F("<thead><tr><th>Name</th><th>Value</th></tr></thead><tbody><tr><td>Chip ID</td><td>"); page += F("<thead><tr><th>Name</th><th>Value</th></tr></thead><tbody><tr><td>Chip ID</td><td>");
page += F("0x");
#ifdef ESP8266 #ifdef ESP8266
page += String(ESP.getChipId(), HEX); //ESP.getChipId(); page += String(ESP.getChipId(), HEX); //ESP.getChipId();
#else //ESP32 #else //ESP32
page += String((uint32_t)ESP.getEfuseMac(), HEX); //ESP.getChipId();
page += String(ESP_getChipId(), HEX); //ESP.getChipId();
page += F("</td></tr>");
page += F("<tr><td>Chip OUI</td><td>");
page += F("0x");
page += String(getChipOUI(), HEX); //ESP.getChipId();
page += F("</td></tr>");
page += F("<tr><td>Chip Model</td><td>");
page += ESP.getChipModel();
page += F(" Rev");
page += ESP.getChipRevision();
#endif #endif
page += F("</td></tr>"); page += F("</td></tr>");
@ -2019,7 +2010,7 @@ int ESP_WiFiManager::scanWifiNetworks(int **indicesptr)
////////////////////////////////////////// //////////////////////////////////////////
int ESP_WiFiManager::getRSSIasQuality(int RSSI) int ESP_WiFiManager::getRSSIasQuality(const int& RSSI)
{ {
int quality = 0; int quality = 0;
@ -2116,6 +2107,31 @@ String ESP_WiFiManager::getStoredWiFiPass()
return String(reinterpret_cast<char*>(conf.sta.password)); return String(reinterpret_cast<char*>(conf.sta.password));
} }
uint32_t getChipID()
{
uint64_t chipId64 = 0;
for (int i = 0; i < 6; i++)
{
chipId64 |= ( ( (uint64_t) ESP.getEfuseMac() >> (40 - (i * 8)) ) & 0xff ) << (i * 8);
}
return (uint32_t) (chipId64 & 0xFFFFFF);
}
uint32_t getChipOUI()
{
uint64_t chipId64 = 0;
for (int i = 0; i < 6; i++)
{
chipId64 |= ( ( (uint64_t) ESP.getEfuseMac() >> (40 - (i * 8)) ) & 0xff ) << (i * 8);
}
return (uint32_t) (chipId64 >> 24);
}
#endif #endif

View File

@ -0,0 +1,42 @@
/****************************************************************************************************************************
ESP_WiFiManager.h
For ESP8266 / ESP32 boards
ESP_WiFiManager is a library for the ESP8266/Arduino platform
(https://github.com/esp8266/Arduino) to enable easy
configuration and reconfiguration of WiFi credentials using a Captive Portal
inspired by:
http://www.esp8266.com/viewtopic.php?f=29&t=2520
https://github.com/chriscook8/esp-arduino-apboot
https://github.com/esp8266/Arduino/blob/master/libraries/DNSServer/examples/CaptivePortalAdvanced/
Modified from Tzapu https://github.com/tzapu/WiFiManager
and from Ken Taylor https://github.com/kentaylor
Built by Khoi Hoang https://github.com/khoih-prog/ESP_WiFiManager
Licensed under MIT license
Version: 1.11.0
Version Modified By Date Comments
------- ----------- ---------- -----------
1.0.0 K Hoang 07/10/2019 Initial coding
...
1.8.0 K Hoang 29/12/2021 Fix `multiple-definitions` linker error and weird bug related to src_cpp
1.9.0 K Hoang 17/01/2022 Enable compatibility with old code to include only ESP_WiFiManager.h
1.10.0 K Hoang 10/02/2022 Add support to new ESP32-S3
1.10.1 K Hoang 11/02/2022 Add LittleFS support to ESP32-C3. Use core LittleFS instead of Lorol's LITTLEFS for v2.0.0+
1.10.2 K Hoang 13/03/2022 Send CORS header in handleWifiSave() function
1.11.0 K Hoang 09/09/2022 Fix ESP32 chipID and add ESP_getChipOUI()
*****************************************************************************************************************************/
#pragma once
#ifndef ESP_WiFiManager_h
#define ESP_WiFiManager_h
#include <ESP_WiFiManager.hpp> //https://github.com/khoih-prog/ESP_WiFiManager
#include <ESP_WiFiManager-Impl.h> //https://github.com/khoih-prog/ESP_WiFiManager
#endif // ESP_WiFiManager_h

View File

@ -1,5 +1,5 @@
/**************************************************************************************************************************** /****************************************************************************************************************************
ESP_WiFiManager.h ESP_WiFiManager.hpp
For ESP8266 / ESP32 boards For ESP8266 / ESP32 boards
ESP_WiFiManager is a library for the ESP8266/Arduino platform ESP_WiFiManager is a library for the ESP8266/Arduino platform
@ -16,86 +16,72 @@
Built by Khoi Hoang https://github.com/khoih-prog/ESP_WiFiManager Built by Khoi Hoang https://github.com/khoih-prog/ESP_WiFiManager
Licensed under MIT license Licensed under MIT license
Version: 1.8.0 Version: 1.11.0
Version Modified By Date Comments Version Modified By Date Comments
------- ----------- ---------- ----------- ------- ----------- ---------- -----------
1.0.0 K Hoang 07/10/2019 Initial coding 1.0.0 K Hoang 07/10/2019 Initial coding
1.0.1 K Hoang 13/12/2019 Fix bug. Add features. Add support for ESP32 ...
1.0.2 K Hoang 19/12/2019 Fix bug thatkeeps ConfigPortal in endless loop if Portal/Router SSID or Password is NULL.
1.0.3 K Hoang 05/01/2020 Option not displaying AvailablePages in Info page. Enhance README.md. Modify examples
1.0.4 K Hoang 07/01/2020 Add RFC952 setHostname feature.
1.0.5 K Hoang 15/01/2020 Add configurable DNS feature. Thanks to @Amorphous of https://community.blynk.cc
1.0.6 K Hoang 03/02/2020 Add support for ArduinoJson version 6.0.0+ ( tested with v6.14.1 )
1.0.7 K Hoang 13/04/2020 Reduce start time, fix SPIFFS bug in examples, update README.md
1.0.8 K Hoang 10/06/2020 Fix STAstaticIP issue. Restructure code. Add LittleFS support for ESP8266 core 2.7.1+
1.0.9 K Hoang 29/07/2020 Fix ESP32 STAstaticIP bug. Permit changing from DHCP <-> static IP using Config Portal.
Add, enhance examples (fix MDNS for ESP32)
1.0.10 K Hoang 08/08/2020 Add more features to Config Portal. Use random WiFi AP channel to avoid conflict.
1.0.11 K Hoang 17/08/2020 Add CORS feature. Fix bug in softAP, autoConnect, resetSettings.
1.1.0 K Hoang 28/08/2020 Add MultiWiFi feature to autoconnect to best WiFi at runtime
1.1.1 K Hoang 30/08/2020 Add setCORSHeader function to allow flexible CORS. Fix typo and minor improvement.
1.1.2 K Hoang 17/08/2020 Fix bug. Add example.
1.2.0 K Hoang 09/10/2020 Restore cpp code besides Impl.h code to use if linker error. Fix bug.
1.3.0 K Hoang 04/12/2020 Add LittleFS support to ESP32 using LITTLEFS Library
1.4.1 K Hoang 22/12/2020 Fix staticIP not saved. Add functions. Add complex examples. Sync with ESPAsync_WiFiManager
1.4.2 K Hoang 14/01/2021 Fix examples' bug not using saved WiFi Credentials after losing all WiFi connections.
1.4.3 K Hoang 23/01/2021 Fix examples' bug not saving Static IP in certain cases.
1.5.0 K Hoang 12/02/2021 Add support to new ESP32-S2
1.5.1 K Hoang 26/03/2021 Fix compiler error if setting Compiler Warnings to All. Retest with esp32 core v1.0.6
1.5.2 K Hoang 08/04/2021 Fix example misleading messages.
1.5.3 K Hoang 13/04/2021 Add dnsServer error message.
1.6.0 K Hoang 20/04/2021 Add support to new ESP32-C3 using SPIFFS or EEPROM
1.6.1 K Hoang 25/04/2021 Fix MultiWiFi bug. Fix captive-portal bug if CP AP address is not default 192.168.4.1
1.7.0 K Hoang 06/05/2021 Set _timezoneName. Add support to new ESP32-S2 (METRO_ESP32S2, FUNHOUSE_ESP32S2, etc.)
1.7.1 K Hoang 08/05/2021 Fix Json bug. Fix timezoneName not displayed in Info page.
1.7.2 K Hoang 08/05/2021 Fix warnings with ESP8266 core v3.0.0
1.7.3 K Hoang 29/07/2021 Fix MultiWiFi connection issue with ESP32 core v2.0.0-rc1+
1.7.4 K Hoang 13/08/2021 Add WiFi scanning of hidden SSIDs
1.7.5 K Hoang 10/10/2021 Update `platform.ini` and `library.json`
1.7.6 K Hoang 26/11/2021 Auto detect ESP32 core and use either built-in LittleFS or LITTLEFS library
1.7.7 K Hoang 26/11/2021 Fix compile error for ESP32 core v1.0.5-
1.7.8 K Hoang 30/11/2021 Fix bug to permit using HTTP port different from 80. Fix bug
1.8.0 K Hoang 29/12/2021 Fix `multiple-definitions` linker error and weird bug related to src_cpp 1.8.0 K Hoang 29/12/2021 Fix `multiple-definitions` linker error and weird bug related to src_cpp
1.9.0 K Hoang 17/01/2022 Enable compatibility with old code to include only ESP_WiFiManager.h
1.10.0 K Hoang 10/02/2022 Add support to new ESP32-S3
1.10.1 K Hoang 11/02/2022 Add LittleFS support to ESP32-C3. Use core LittleFS instead of Lorol's LITTLEFS for v2.0.0+
1.10.2 K Hoang 13/03/2022 Send CORS header in handleWifiSave() function
1.11.0 K Hoang 09/09/2022 Fix ESP32 chipID and add ESP_getChipOUI()
*****************************************************************************************************************************/ *****************************************************************************************************************************/
#pragma once #pragma once
#ifndef ESP_WiFiManager_h #ifndef ESP_WiFiManager_hpp
#define ESP_WiFiManager_h #define ESP_WiFiManager_hpp
#if !( defined(ESP8266) || defined(ESP32) ) #if !( defined(ESP8266) || defined(ESP32) )
#error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting. #error This code is intended to run on the ESP8266 or ESP32 platform! Please check your Tools->Board setting.
#elif ( ARDUINO_ESP32S2_DEV || ARDUINO_FEATHERS2 || ARDUINO_ESP32S2_THING_PLUS || ARDUINO_MICROS2 || \ #elif ( ARDUINO_ESP32S2_DEV || ARDUINO_FEATHERS2 || ARDUINO_ESP32S2_THING_PLUS || ARDUINO_MICROS2 || \
ARDUINO_METRO_ESP32S2 || ARDUINO_MAGTAG29_ESP32S2 || ARDUINO_FUNHOUSE_ESP32S2 || \ ARDUINO_METRO_ESP32S2 || ARDUINO_MAGTAG29_ESP32S2 || ARDUINO_FUNHOUSE_ESP32S2 || \
ARDUINO_ADAFRUIT_FEATHER_ESP32S2_NOPSRAM ) ARDUINO_ADAFRUIT_FEATHER_ESP32S2_NOPSRAM )
#if (_WIFIMGR_LOGLEVEL_ > 3)
#warning Using ESP32_S2. To follow library instructions to install esp32-s2 core and WebServer Patch #warning Using ESP32_S2. To follow library instructions to install esp32-s2 core and WebServer Patch
#warning You have to select HUGE APP or 1.9-2.0 MB APP to be able to run Config Portal. Must use PSRAM #warning You have to select HUGE APP or 1.9-2.0 MB APP to be able to run Config Portal. Must use PSRAM
#endif
#define USING_ESP32_S2 true #define USING_ESP32_S2 true
#elif ( ARDUINO_ESP32C3_DEV ) #elif ( ARDUINO_ESP32C3_DEV )
#warning Using ESP32_C3. To follow library instructions to install esp32-c3 core. Only SPIFFS and EEPROM OK. #if (_WIFIMGR_LOGLEVEL_ > 3)
#warning You have to select Flash size 2MB and Minimal APP (1.3MB + 700KB) for some boards #if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) )
#warning Using ESP32_C3 using core v2.0.0+. Either LittleFS, SPIFFS or EEPROM OK.
#else
#warning Using ESP32_C3 using core v1.0.6-. To follow library instructions to install esp32-c3 core. Only SPIFFS and EEPROM OK.
#endif
#endif
// #warning You have to select Flash size 2MB and Minimal APP (1.3MB + 700KB) for some boards
#define USING_ESP32_C3 true #define USING_ESP32_C3 true
#elif ( defined(ARDUINO_ESP32S3_DEV) || defined(ARDUINO_ESP32_S3_BOX) || defined(ARDUINO_TINYS3) || \
defined(ARDUINO_PROS3) || defined(ARDUINO_FEATHERS3) )
#if (_WIFIMGR_LOGLEVEL_ > 3)
#warning Using ESP32_S3. To install esp32-s3-support branch if using core v2.0.2-
#endif
#define USING_ESP32_S3 true
#endif #endif
#define ESP_WIFIMANAGER_VERSION "ESP_WiFiManager v1.8.0" #define ESP_WIFIMANAGER_VERSION "ESP_WiFiManager v1.11.0"
#define ESP_WIFIMANAGER_VERSION_MAJOR 1 #define ESP_WIFIMANAGER_VERSION_MAJOR 1
#define ESP_WIFIMANAGER_VERSION_MINOR 8 #define ESP_WIFIMANAGER_VERSION_MINOR 11
#define ESP_WIFIMANAGER_VERSION_PATCH 0 #define ESP_WIFIMANAGER_VERSION_PATCH 0
#define ESP_WIFIMANAGER_VERSION_INT 1008000 #define ESP_WIFIMANAGER_VERSION_INT 1011000
#include "ESP_WiFiManager_Debug.h" #include "ESP_WiFiManager_Debug.h"
#if ( defined(HTTP_PORT) && (HTTP_PORT < 65536) && (HTTP_PORT > 0) ) #if ( defined(HTTP_PORT) && (HTTP_PORT < 65536) && (HTTP_PORT > 0) )
#if (_WIFIMGR_LOGLEVEL_ > 2) #if (_WIFIMGR_LOGLEVEL_ > 3)
#warning Using custom HTTP_PORT #warning Using custom HTTP_PORT
#endif #endif
#define HTTP_PORT_TO_USE HTTP_PORT #define HTTP_PORT_TO_USE HTTP_PORT
#else #else
#if (_WIFIMGR_LOGLEVEL_ > 2) #if (_WIFIMGR_LOGLEVEL_ > 3)
#warning Using default HTTP_PORT = 80 #warning Using default HTTP_PORT = 80
#endif #endif
@ -125,9 +111,20 @@
} }
#define ESP_getChipId() (ESP.getChipId()) #define ESP_getChipId() (ESP.getChipId())
#else //ESP32 #else //ESP32
#include <esp_wifi.h> #include <esp_wifi.h>
#define ESP_getChipId() ((uint32_t)ESP.getEfuseMac())
uint32_t getChipID();
uint32_t getChipOUI();
#if defined(ESP_getChipId)
#undef ESP_getChipId
#endif
#define ESP_getChipId() getChipID() // ((uint32_t)ESP.getEfuseMac())
#define ESP_getChipOUI() getChipOUI() // ((uint32_t)ESP.getEfuseMac())
#endif #endif
// New in v1.4.0 // New in v1.4.0
@ -198,8 +195,11 @@ const char WM_HTTP_SCRIPT_NTP_HIDDEN[] PROGMEM = "<p><input type='hidden' id='ti
#if !(USE_CLOUDFLARE_NTP) #if !(USE_CLOUDFLARE_NTP)
#undef USE_CLOUDFLARE_NTP #undef USE_CLOUDFLARE_NTP
#define USE_CLOUDFLARE_NTP true #define USE_CLOUDFLARE_NTP true
#if (_WIFIMGR_LOGLEVEL_ > 3)
#warning Forcing USE_CLOUDFLARE_NTP for ESP8266 as low memory can cause blank page #warning Forcing USE_CLOUDFLARE_NTP for ESP8266 as low memory can cause blank page
#endif #endif
#endif
#endif #endif
// To permit disable or configure NTP from sketch // To permit disable or configure NTP from sketch
@ -231,8 +231,7 @@ const char WM_HTTP_ITEM[] PROGMEM = "<div><a href='#p' onclick='c(this)'>{v}</a>
const char JSON_ITEM[] PROGMEM = "{\"SSID\":\"{v}\", \"Encryption\":{i}, \"Quality\":\"{r}\"}"; const char JSON_ITEM[] PROGMEM = "{\"SSID\":\"{v}\", \"Encryption\":{i}, \"Quality\":\"{r}\"}";
// KH, update from v1.1.0 // KH, update from v1.1.0
//const char WM_HTTP_FORM_START[] PROGMEM = "<form method='get' action='wifisave'><fieldset><div><label>SSID</label><input id='s' name='s' length=32 placeholder='SSID'><div></div></div><div><label>Password</label><input id='p' name='p' length=64 placeholder='password'><div></div></div><div><label>SSID1</label><input id='s1' name='s1' length=32 placeholder='SSID1'><div></div></div><div><label>Password</label><input id='p1' name='p1' length=64 placeholder='password1'><div></div></div></fieldset>"; const char WM_HTTP_FORM_START[] PROGMEM = "<form method='get' action='wifisave'><fieldset><div><label>SSID</label><input id='s' name='s' length=32 placeholder='SSID'><div></div></div><div><label>Password</label><input id='p' name='p' length=64 placeholder='password'><div></div></div><div><label>SSID1</label><input id='s1' name='s1' length=32 placeholder='SSID1'><div></div></div><div><label>Password</label><input id='p1' name='p1' length=64 placeholder='password1'><div></div></div></fieldset>";
//const char WM_HTTP_FORM_START[] PROGMEM = "<form method='get' action='wifisave'><fieldset><div><label>SSID</label><input id='s' name='s' length=32 placeholder='SSID'><div></div></div><div><label>Password</label><input id='p' name='p' length=64 placeholder='password'><div></div></div><div hidden><label>SSID1</label><input id='s1' name='s1' length=32 placeholder='SSID1'><div></div></div><div hidden><label>Password</label><input id='p1' name='p1' length=64 placeholder='password1'><div></div></div></fieldset>";
////// //////
// KH, add from v1.0.10 // KH, add from v1.0.10
@ -291,8 +290,8 @@ class ESP_WMParameter
{ {
public: public:
ESP_WMParameter(const char *custom); ESP_WMParameter(const char *custom);
ESP_WMParameter(const char *id, const char *placeholder, const char *defaultValue, int length, ESP_WMParameter(const char *id, const char *placeholder, const char *defaultValue, const int& length,
const char *custom = "", int labelPlacement = WFM_LABEL_BEFORE); const char *custom = "", const int& labelPlacement = WFM_LABEL_BEFORE);
// New in v1.4.0 // New in v1.4.0
ESP_WMParameter(const WMParam_Data& WMParam_data); ESP_WMParameter(const WMParam_Data& WMParam_data);
@ -302,7 +301,7 @@ class ESP_WMParameter
// New in v1.4.0 // New in v1.4.0
void setWMParam_Data(const WMParam_Data& WMParam_data); void setWMParam_Data(const WMParam_Data& WMParam_data);
void getWMParam_Data(WMParam_Data &WMParam_data); void getWMParam_Data(WMParam_Data& WMParam_data);
////// //////
const char *getID(); const char *getID();
@ -318,7 +317,8 @@ class ESP_WMParameter
const char *_customHTML; const char *_customHTML;
void init(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom, int labelPlacement); void init(const char *id, const char *placeholder, const char *defaultValue, const int& length,
const char *custom, const int& labelPlacement);
friend class ESP_WiFiManager; friend class ESP_WiFiManager;
}; };
@ -359,19 +359,19 @@ class ESP_WiFiManager
//sets timeout before webserver loop ends and exits even if there has been no setup. //sets timeout before webserver loop ends and exits even if there has been no setup.
//usefully for devices that failed to connect at some point and got stuck in a webserver loop //usefully for devices that failed to connect at some point and got stuck in a webserver loop
//in seconds setConfigPortalTimeout is a new name for setTimeout //in seconds setConfigPortalTimeout is a new name for setTimeout
void setConfigPortalTimeout(unsigned long seconds); void setConfigPortalTimeout(const unsigned long& seconds);
void setTimeout(unsigned long seconds); void setTimeout(const unsigned long& seconds);
//sets timeout for which to attempt connecting, usefull if you get a lot of failed connects //sets timeout for which to attempt connecting, usefull if you get a lot of failed connects
void setConnectTimeout(unsigned long seconds); void setConnectTimeout(const unsigned long& seconds);
void setDebugOutput(bool debug); void setDebugOutput(bool debug);
//defaults to not showing anything under 8% signal quality if called //defaults to not showing anything under 8% signal quality if called
void setMinimumSignalQuality(int quality = 8); void setMinimumSignalQuality(const int& quality = 8);
// KH, new from v1.0.10 to enable dynamic/random channel // KH, new from v1.0.10 to enable dynamic/random channel
int setConfigPortalChannel(int channel = 1); int setConfigPortalChannel(const int& channel = 1);
////// //////
//sets a custom ip /gateway /subnet configuration //sets a custom ip /gateway /subnet configuration
@ -379,7 +379,7 @@ class ESP_WiFiManager
// New in v1.4.0 // New in v1.4.0
void setAPStaticIPConfig(const WiFi_AP_IPConfig& WM_AP_IPconfig); void setAPStaticIPConfig(const WiFi_AP_IPConfig& WM_AP_IPconfig);
void getAPStaticIPConfig(WiFi_AP_IPConfig &WM_AP_IPconfig); void getAPStaticIPConfig(WiFi_AP_IPConfig& WM_AP_IPconfig);
////// //////
//sets config for a static IP //sets config for a static IP
@ -387,7 +387,7 @@ class ESP_WiFiManager
// New in v1.4.0 // New in v1.4.0
void setSTAStaticIPConfig(const WiFi_STA_IPConfig& WM_STA_IPconfig); void setSTAStaticIPConfig(const WiFi_STA_IPConfig& WM_STA_IPconfig);
void getSTAStaticIPConfig(WiFi_STA_IPConfig &WM_STA_IPconfig); void getSTAStaticIPConfig(WiFi_STA_IPConfig& WM_STA_IPconfig);
////// //////
#if USE_CONFIGURABLE_DNS #if USE_CONFIGURABLE_DNS
@ -447,7 +447,7 @@ class ESP_WiFiManager
#define MAX_WIFI_CREDENTIALS 2 #define MAX_WIFI_CREDENTIALS 2
String getSSID(uint8_t index) String getSSID(const uint8_t& index)
{ {
if (index == 0) if (index == 0)
return _ssid; return _ssid;
@ -457,7 +457,7 @@ class ESP_WiFiManager
return String(""); return String("");
} }
String getPW(uint8_t index) String getPW(const uint8_t& index)
{ {
if (index == 0) if (index == 0)
return _pass; return _pass;
@ -488,7 +488,7 @@ class ESP_WiFiManager
// returns the Parameters Count // returns the Parameters Count
int getParametersCount(); int getParametersCount();
const char* getStatus(int status); const char* getStatus(const int& status);
#ifdef ESP32 #ifdef ESP32
String getStoredWiFiSSID(); String getStoredWiFiSSID();
@ -605,7 +605,7 @@ class ESP_WiFiManager
void setupConfigPortal(); void setupConfigPortal();
void startWPS(); void startWPS();
//const char* getStatus(int status); //const char* getStatus(const int& status);
const char* _apName = "no-net"; const char* _apName = "no-net";
const char* _apPassword = NULL; const char* _apPassword = NULL;
@ -686,13 +686,13 @@ class ESP_WiFiManager
void handleNotFound(); void handleNotFound();
bool captivePortal(); bool captivePortal();
void reportStatus(String &page); void reportStatus(String& page);
// DNS server // DNS server
const byte DNS_PORT = 53; const byte DNS_PORT = 53;
//helpers //helpers
int getRSSIasQuality(int RSSI); int getRSSIasQuality(const int& RSSI);
bool isIp(const String& str); bool isIp(const String& str);
String toStringIp(const IPAddress& ip); String toStringIp(const IPAddress& ip);
@ -715,10 +715,13 @@ class ESP_WiFiManager
void DEBUG_WM(Generic text); void DEBUG_WM(Generic text);
template <class T> template <class T>
auto optionalIPFromString(T *obj, const char *s) -> decltype(obj->fromString(s)) { auto optionalIPFromString(T *obj, const char *s) -> decltype(obj->fromString(s))
{
return obj->fromString(s); return obj->fromString(s);
} }
auto optionalIPFromString(...) -> bool {
auto optionalIPFromString(...) -> bool
{
LOGINFO("NO fromString METHOD ON IPAddress, you need ESP8266 core 2.1.0 or newer for Custom IP configuration to work."); LOGINFO("NO fromString METHOD ON IPAddress, you need ESP8266 core 2.1.0 or newer for Custom IP configuration to work.");
return false; return false;
} }

View File

@ -16,47 +16,18 @@
Built by Khoi Hoang https://github.com/khoih-prog/ESP_WiFiManager Built by Khoi Hoang https://github.com/khoih-prog/ESP_WiFiManager
Licensed under MIT license Licensed under MIT license
Version: 1.8.0 Version: 1.11.0
Version Modified By Date Comments Version Modified By Date Comments
------- ----------- ---------- ----------- ------- ----------- ---------- -----------
1.0.0 K Hoang 07/10/2019 Initial coding 1.0.0 K Hoang 07/10/2019 Initial coding
1.0.1 K Hoang 13/12/2019 Fix bug. Add features. Add support for ESP32 ...
1.0.2 K Hoang 19/12/2019 Fix bug thatkeeps ConfigPortal in endless loop if Portal/Router SSID or Password is NULL.
1.0.3 K Hoang 05/01/2020 Option not displaying AvailablePages in Info page. Enhance README.md. Modify examples
1.0.4 K Hoang 07/01/2020 Add RFC952 setHostname feature.
1.0.5 K Hoang 15/01/2020 Add configurable DNS feature. Thanks to @Amorphous of https://community.blynk.cc
1.0.6 K Hoang 03/02/2020 Add support for ArduinoJson version 6.0.0+ ( tested with v6.14.1 )
1.0.7 K Hoang 13/04/2020 Reduce start time, fix SPIFFS bug in examples, update README.md
1.0.8 K Hoang 10/06/2020 Fix STAstaticIP issue. Restructure code. Add LittleFS support for ESP8266 core 2.7.1+
1.0.9 K Hoang 29/07/2020 Fix ESP32 STAstaticIP bug. Permit changing from DHCP <-> static IP using Config Portal.
Add, enhance examples (fix MDNS for ESP32)
1.0.10 K Hoang 08/08/2020 Add more features to Config Portal. Use random WiFi AP channel to avoid conflict.
1.0.11 K Hoang 17/08/2020 Add CORS feature. Fix bug in softAP, autoConnect, resetSettings.
1.1.0 K Hoang 28/08/2020 Add MultiWiFi feature to autoconnect to best WiFi at runtime
1.1.1 K Hoang 30/08/2020 Add setCORSHeader function to allow flexible CORS. Fix typo and minor improvement.
1.1.2 K Hoang 17/08/2020 Fix bug. Add example.
1.2.0 K Hoang 09/10/2020 Restore cpp code besides Impl.h code to use if linker error. Fix bug.
1.3.0 K Hoang 04/12/2020 Add LittleFS support to ESP32 using LITTLEFS Library
1.4.1 K Hoang 22/12/2020 Fix staticIP not saved. Add functions. Add complex examples. Sync with ESPAsync_WiFiManager
1.4.2 K Hoang 14/01/2021 Fix examples' bug not using saved WiFi Credentials after losing all WiFi connections.
1.4.3 K Hoang 23/01/2021 Fix examples' bug not saving Static IP in certain cases.
1.5.0 K Hoang 12/02/2021 Add support to new ESP32-S2
1.5.1 K Hoang 26/03/2021 Fix compiler error if setting Compiler Warnings to All. Retest with esp32 core v1.0.6
1.5.2 K Hoang 08/04/2021 Fix example misleading messages.
1.5.3 K Hoang 13/04/2021 Add dnsServer error message.
1.6.0 K Hoang 20/04/2021 Add support to new ESP32-C3 using SPIFFS or EEPROM
1.6.1 K Hoang 25/04/2021 Fix MultiWiFi bug. Fix captive-portal bug if CP AP address is not default 192.168.4.1
1.7.0 K Hoang 06/05/2021 Set _timezoneName. Add support to new ESP32-S2 (METRO_ESP32S2, FUNHOUSE_ESP32S2, etc.)
1.7.1 K Hoang 08/05/2021 Fix Json bug. Fix timezoneName not displayed in Info page.
1.7.2 K Hoang 08/05/2021 Fix warnings with ESP8266 core v3.0.0
1.7.3 K Hoang 29/07/2021 Fix MultiWiFi connection issue with ESP32 core v2.0.0-rc1+
1.7.4 K Hoang 13/08/2021 Add WiFi scanning of hidden SSIDs
1.7.5 K Hoang 10/10/2021 Update `platform.ini` and `library.json`
1.7.6 K Hoang 26/11/2021 Auto detect ESP32 core and use either built-in LittleFS or LITTLEFS library
1.7.7 K Hoang 26/11/2021 Fix compile error for ESP32 core v1.0.5-
1.7.8 K Hoang 30/11/2021 Fix bug to permit using HTTP port different from 80. Fix bug
1.8.0 K Hoang 29/12/2021 Fix `multiple-definitions` linker error and weird bug related to src_cpp 1.8.0 K Hoang 29/12/2021 Fix `multiple-definitions` linker error and weird bug related to src_cpp
1.9.0 K Hoang 17/01/2022 Enable compatibility with old code to include only ESP_WiFiManager.h
1.10.0 K Hoang 10/02/2022 Add support to new ESP32-S3
1.10.1 K Hoang 11/02/2022 Add LittleFS support to ESP32-C3. Use core LittleFS instead of Lorol's LITTLEFS for v2.0.0+
1.10.2 K Hoang 13/03/2022 Send CORS header in handleWifiSave() function
1.11.0 K Hoang 09/09/2022 Fix ESP32 chipID and add ESP_getChipOUI()
*****************************************************************************************************************************/ *****************************************************************************************************************************/
#pragma once #pragma once

View File

@ -71,8 +71,6 @@
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
#if 1
#define TZ_Africa_Abidjan ("GMT0") #define TZ_Africa_Abidjan ("GMT0")
#define TZ_Africa_Accra ("GMT0") #define TZ_Africa_Accra ("GMT0")
#define TZ_Africa_Addis_Ababa ("EAT-3") #define TZ_Africa_Addis_Ababa ("EAT-3")
@ -534,479 +532,11 @@
#define TZ_Etc_Universal ("UTC0") #define TZ_Etc_Universal ("UTC0")
#define TZ_Etc_Zulu ("UTC0") #define TZ_Etc_Zulu ("UTC0")
#else
#define TZ_Africa_Abidjan PSTR("GMT0")
#define TZ_Africa_Accra PSTR("GMT0")
#define TZ_Africa_Addis_Ababa PSTR("EAT-3")
#define TZ_Africa_Algiers PSTR("CET-1")
#define TZ_Africa_Asmara PSTR("EAT-3")
#define TZ_Africa_Bamako PSTR("GMT0")
#define TZ_Africa_Bangui PSTR("WAT-1")
#define TZ_Africa_Banjul PSTR("GMT0")
#define TZ_Africa_Bissau PSTR("GMT0")
#define TZ_Africa_Blantyre PSTR("CAT-2")
#define TZ_Africa_Brazzaville PSTR("WAT-1")
#define TZ_Africa_Bujumbura PSTR("CAT-2")
#define TZ_Africa_Cairo PSTR("EET-2")
#define TZ_Africa_Casablanca PSTR("<+01>-1")
#define TZ_Africa_Ceuta PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Africa_Conakry PSTR("GMT0")
#define TZ_Africa_Dakar PSTR("GMT0")
#define TZ_Africa_Dar_es_Salaam PSTR("EAT-3")
#define TZ_Africa_Djibouti PSTR("EAT-3")
#define TZ_Africa_Douala PSTR("WAT-1")
#define TZ_Africa_El_Aaiun PSTR("<+01>-1")
#define TZ_Africa_Freetown PSTR("GMT0")
#define TZ_Africa_Gaborone PSTR("CAT-2")
#define TZ_Africa_Harare PSTR("CAT-2")
#define TZ_Africa_Johannesburg PSTR("SAST-2")
#define TZ_Africa_Juba PSTR("EAT-3")
#define TZ_Africa_Kampala PSTR("EAT-3")
#define TZ_Africa_Khartoum PSTR("CAT-2")
#define TZ_Africa_Kigali PSTR("CAT-2")
#define TZ_Africa_Kinshasa PSTR("WAT-1")
#define TZ_Africa_Lagos PSTR("WAT-1")
#define TZ_Africa_Libreville PSTR("WAT-1")
#define TZ_Africa_Lome PSTR("GMT0")
#define TZ_Africa_Luanda PSTR("WAT-1")
#define TZ_Africa_Lubumbashi PSTR("CAT-2")
#define TZ_Africa_Lusaka PSTR("CAT-2")
#define TZ_Africa_Malabo PSTR("WAT-1")
#define TZ_Africa_Maputo PSTR("CAT-2")
#define TZ_Africa_Maseru PSTR("SAST-2")
#define TZ_Africa_Mbabane PSTR("SAST-2")
#define TZ_Africa_Mogadishu PSTR("EAT-3")
#define TZ_Africa_Monrovia PSTR("GMT0")
#define TZ_Africa_Nairobi PSTR("EAT-3")
#define TZ_Africa_Ndjamena PSTR("WAT-1")
#define TZ_Africa_Niamey PSTR("WAT-1")
#define TZ_Africa_Nouakchott PSTR("GMT0")
#define TZ_Africa_Ouagadougou PSTR("GMT0")
#define TZ_Africa_PortomNovo PSTR("WAT-1")
#define TZ_Africa_Sao_Tome PSTR("GMT0")
#define TZ_Africa_Tripoli PSTR("EET-2")
#define TZ_Africa_Tunis PSTR("CET-1")
#define TZ_Africa_Windhoek PSTR("CAT-2")
#define TZ_America_Adak PSTR("HST10HDT,M3.2.0,M11.1.0")
#define TZ_America_Anchorage PSTR("AKST9AKDT,M3.2.0,M11.1.0")
#define TZ_America_Anguilla PSTR("AST4")
#define TZ_America_Antigua PSTR("AST4")
#define TZ_America_Araguaina PSTR("<-03>3")
#define TZ_America_Argentina_Buenos_Aires PSTR("<-03>3")
#define TZ_America_Argentina_Catamarca PSTR("<-03>3")
#define TZ_America_Argentina_Cordoba PSTR("<-03>3")
#define TZ_America_Argentina_Jujuy PSTR("<-03>3")
#define TZ_America_Argentina_La_Rioja PSTR("<-03>3")
#define TZ_America_Argentina_Mendoza PSTR("<-03>3")
#define TZ_America_Argentina_Rio_Gallegos PSTR("<-03>3")
#define TZ_America_Argentina_Salta PSTR("<-03>3")
#define TZ_America_Argentina_San_Juan PSTR("<-03>3")
#define TZ_America_Argentina_San_Luis PSTR("<-03>3")
#define TZ_America_Argentina_Tucuman PSTR("<-03>3")
#define TZ_America_Argentina_Ushuaia PSTR("<-03>3")
#define TZ_America_Aruba PSTR("AST4")
#define TZ_America_Asuncion PSTR("<-04>4<-03>,M10.1.0/0,M3.4.0/0")
#define TZ_America_Atikokan PSTR("EST5")
#define TZ_America_Bahia PSTR("<-03>3")
#define TZ_America_Bahia_Banderas PSTR("CST6CDT,M4.1.0,M10.5.0")
#define TZ_America_Barbados PSTR("AST4")
#define TZ_America_Belem PSTR("<-03>3")
#define TZ_America_Belize PSTR("CST6")
#define TZ_America_BlancmSablon PSTR("AST4")
#define TZ_America_Boa_Vista PSTR("<-04>4")
#define TZ_America_Bogota PSTR("<-05>5")
#define TZ_America_Boise PSTR("MST7MDT,M3.2.0,M11.1.0")
#define TZ_America_Cambridge_Bay PSTR("MST7MDT,M3.2.0,M11.1.0")
#define TZ_America_Campo_Grande PSTR("<-04>4")
#define TZ_America_Cancun PSTR("EST5")
#define TZ_America_Caracas PSTR("<-04>4")
#define TZ_America_Cayenne PSTR("<-03>3")
#define TZ_America_Cayman PSTR("EST5")
#define TZ_America_Chicago PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Chihuahua PSTR("MST7MDT,M4.1.0,M10.5.0")
#define TZ_America_Costa_Rica PSTR("CST6")
#define TZ_America_Creston PSTR("MST7")
#define TZ_America_Cuiaba PSTR("<-04>4")
#define TZ_America_Curacao PSTR("AST4")
#define TZ_America_Danmarkshavn PSTR("GMT0")
#define TZ_America_Dawson PSTR("MST7")
#define TZ_America_Dawson_Creek PSTR("MST7")
#define TZ_America_Denver PSTR("MST7MDT,M3.2.0,M11.1.0")
#define TZ_America_Detroit PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Dominica PSTR("AST4")
#define TZ_America_Edmonton PSTR("MST7MDT,M3.2.0,M11.1.0")
#define TZ_America_Eirunepe PSTR("<-05>5")
#define TZ_America_El_Salvador PSTR("CST6")
#define TZ_America_Fortaleza PSTR("<-03>3")
#define TZ_America_Fort_Nelson PSTR("MST7")
#define TZ_America_Glace_Bay PSTR("AST4ADT,M3.2.0,M11.1.0")
#define TZ_America_Godthab PSTR("<-03>3<-02>,M3.5.0/-2,M10.5.0/-1")
#define TZ_America_Goose_Bay PSTR("AST4ADT,M3.2.0,M11.1.0")
#define TZ_America_Grand_Turk PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Grenada PSTR("AST4")
#define TZ_America_Guadeloupe PSTR("AST4")
#define TZ_America_Guatemala PSTR("CST6")
#define TZ_America_Guayaquil PSTR("<-05>5")
#define TZ_America_Guyana PSTR("<-04>4")
#define TZ_America_Halifax PSTR("AST4ADT,M3.2.0,M11.1.0")
#define TZ_America_Havana PSTR("CST5CDT,M3.2.0/0,M11.1.0/1")
#define TZ_America_Hermosillo PSTR("MST7")
#define TZ_America_Indiana_Indianapolis PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Indiana_Knox PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Indiana_Marengo PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Indiana_Petersburg PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Indiana_Tell_City PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Indiana_Vevay PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Indiana_Vincennes PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Indiana_Winamac PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Inuvik PSTR("MST7MDT,M3.2.0,M11.1.0")
#define TZ_America_Iqaluit PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Jamaica PSTR("EST5")
#define TZ_America_Juneau PSTR("AKST9AKDT,M3.2.0,M11.1.0")
#define TZ_America_Kentucky_Louisville PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Kentucky_Monticello PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Kralendijk PSTR("AST4")
#define TZ_America_La_Paz PSTR("<-04>4")
#define TZ_America_Lima PSTR("<-05>5")
#define TZ_America_Los_Angeles PSTR("PST8PDT,M3.2.0,M11.1.0")
#define TZ_America_Lower_Princes PSTR("AST4")
#define TZ_America_Maceio PSTR("<-03>3")
#define TZ_America_Managua PSTR("CST6")
#define TZ_America_Manaus PSTR("<-04>4")
#define TZ_America_Marigot PSTR("AST4")
#define TZ_America_Martinique PSTR("AST4")
#define TZ_America_Matamoros PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Mazatlan PSTR("MST7MDT,M4.1.0,M10.5.0")
#define TZ_America_Menominee PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Merida PSTR("CST6CDT,M4.1.0,M10.5.0")
#define TZ_America_Metlakatla PSTR("AKST9AKDT,M3.2.0,M11.1.0")
#define TZ_America_Mexico_City PSTR("CST6CDT,M4.1.0,M10.5.0")
#define TZ_America_Miquelon PSTR("<-03>3<-02>,M3.2.0,M11.1.0")
#define TZ_America_Moncton PSTR("AST4ADT,M3.2.0,M11.1.0")
#define TZ_America_Monterrey PSTR("CST6CDT,M4.1.0,M10.5.0")
#define TZ_America_Montevideo PSTR("<-03>3")
#define TZ_America_Montreal PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Montserrat PSTR("AST4")
#define TZ_America_Nassau PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_New_York PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Nipigon PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Nome PSTR("AKST9AKDT,M3.2.0,M11.1.0")
#define TZ_America_Noronha PSTR("<-02>2")
#define TZ_America_North_Dakota_Beulah PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_North_Dakota_Center PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_North_Dakota_New_Salem PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Ojinaga PSTR("MST7MDT,M3.2.0,M11.1.0")
#define TZ_America_Panama PSTR("EST5")
#define TZ_America_Pangnirtung PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Paramaribo PSTR("<-03>3")
#define TZ_America_Phoenix PSTR("MST7")
#define TZ_America_PortmaumPrince PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Port_of_Spain PSTR("AST4")
#define TZ_America_Porto_Velho PSTR("<-04>4")
#define TZ_America_Puerto_Rico PSTR("AST4")
#define TZ_America_Punta_Arenas PSTR("<-03>3")
#define TZ_America_Rainy_River PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Rankin_Inlet PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Recife PSTR("<-03>3")
#define TZ_America_Regina PSTR("CST6")
#define TZ_America_Resolute PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Rio_Branco PSTR("<-05>5")
#define TZ_America_Santarem PSTR("<-03>3")
#define TZ_America_Santiago PSTR("<-04>4<-03>,M9.1.6/24,M4.1.6/24")
#define TZ_America_Santo_Domingo PSTR("AST4")
#define TZ_America_Sao_Paulo PSTR("<-03>3")
#define TZ_America_Scoresbysund PSTR("<-01>1<+00>,M3.5.0/0,M10.5.0/1")
#define TZ_America_Sitka PSTR("AKST9AKDT,M3.2.0,M11.1.0")
#define TZ_America_St_Barthelemy PSTR("AST4")
#define TZ_America_St_Johns PSTR("NST3:30NDT,M3.2.0,M11.1.0")
#define TZ_America_St_Kitts PSTR("AST4")
#define TZ_America_St_Lucia PSTR("AST4")
#define TZ_America_St_Thomas PSTR("AST4")
#define TZ_America_St_Vincent PSTR("AST4")
#define TZ_America_Swift_Current PSTR("CST6")
#define TZ_America_Tegucigalpa PSTR("CST6")
#define TZ_America_Thule PSTR("AST4ADT,M3.2.0,M11.1.0")
#define TZ_America_Thunder_Bay PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Tijuana PSTR("PST8PDT,M3.2.0,M11.1.0")
#define TZ_America_Toronto PSTR("EST5EDT,M3.2.0,M11.1.0")
#define TZ_America_Tortola PSTR("AST4")
#define TZ_America_Vancouver PSTR("PST8PDT,M3.2.0,M11.1.0")
#define TZ_America_Whitehorse PSTR("MST7")
#define TZ_America_Winnipeg PSTR("CST6CDT,M3.2.0,M11.1.0")
#define TZ_America_Yakutat PSTR("AKST9AKDT,M3.2.0,M11.1.0")
#define TZ_America_Yellowknife PSTR("MST7MDT,M3.2.0,M11.1.0")
#define TZ_Antarctica_Casey PSTR("<+11>-11")
#define TZ_Antarctica_Davis PSTR("<+07>-7")
#define TZ_Antarctica_DumontDUrville PSTR("<+10>-10")
#define TZ_Antarctica_Macquarie PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
#define TZ_Antarctica_Mawson PSTR("<+05>-5")
#define TZ_Antarctica_McMurdo PSTR("NZST-12NZDT,M9.5.0,M4.1.0/3")
#define TZ_Antarctica_Palmer PSTR("<-03>3")
#define TZ_Antarctica_Rothera PSTR("<-03>3")
#define TZ_Antarctica_Syowa PSTR("<+03>-3")
#define TZ_Antarctica_Troll PSTR("<+00>0<+02>-2,M3.5.0/1,M10.5.0/3")
#define TZ_Antarctica_Vostok PSTR("<+06>-6")
#define TZ_Arctic_Longyearbyen PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Asia_Aden PSTR("<+03>-3")
#define TZ_Asia_Almaty PSTR("<+06>-6")
#define TZ_Asia_Amman PSTR("EET-2EEST,M3.5.4/24,M10.5.5/1")
#define TZ_Asia_Anadyr PSTR("<+12>-12")
#define TZ_Asia_Aqtau PSTR("<+05>-5")
#define TZ_Asia_Aqtobe PSTR("<+05>-5")
#define TZ_Asia_Ashgabat PSTR("<+05>-5")
#define TZ_Asia_Atyrau PSTR("<+05>-5")
#define TZ_Asia_Baghdad PSTR("<+03>-3")
#define TZ_Asia_Bahrain PSTR("<+03>-3")
#define TZ_Asia_Baku PSTR("<+04>-4")
#define TZ_Asia_Bangkok PSTR("<+07>-7")
#define TZ_Asia_Barnaul PSTR("<+07>-7")
#define TZ_Asia_Beirut PSTR("EET-2EEST,M3.5.0/0,M10.5.0/0")
#define TZ_Asia_Bishkek PSTR("<+06>-6")
#define TZ_Asia_Brunei PSTR("<+08>-8")
#define TZ_Asia_Chita PSTR("<+09>-9")
#define TZ_Asia_Choibalsan PSTR("<+08>-8")
#define TZ_Asia_Colombo PSTR("<+0530>-5:30")
#define TZ_Asia_Damascus PSTR("EET-2EEST,M3.5.5/0,M10.5.5/0")
#define TZ_Asia_Dhaka PSTR("<+06>-6")
#define TZ_Asia_Dili PSTR("<+09>-9")
#define TZ_Asia_Dubai PSTR("<+04>-4")
#define TZ_Asia_Dushanbe PSTR("<+05>-5")
#define TZ_Asia_Famagusta PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Asia_Gaza PSTR("EET-2EEST,M3.4.4/48,M10.4.4/49")
#define TZ_Asia_Hebron PSTR("EET-2EEST,M3.4.4/48,M10.4.4/49")
#define TZ_Asia_Ho_Chi_Minh PSTR("<+07>-7")
#define TZ_Asia_Hong_Kong PSTR("HKT-8")
#define TZ_Asia_Hovd PSTR("<+07>-7")
#define TZ_Asia_Irkutsk PSTR("<+08>-8")
#define TZ_Asia_Jakarta PSTR("WIB-7")
#define TZ_Asia_Jayapura PSTR("WIT-9")
#define TZ_Asia_Jerusalem PSTR("IST-2IDT,M3.4.4/26,M10.5.0")
#define TZ_Asia_Kabul PSTR("<+0430>-4:30")
#define TZ_Asia_Kamchatka PSTR("<+12>-12")
#define TZ_Asia_Karachi PSTR("PKT-5")
#define TZ_Asia_Kathmandu PSTR("<+0545>-5:45")
#define TZ_Asia_Khandyga PSTR("<+09>-9")
#define TZ_Asia_Kolkata PSTR("IST-5:30")
#define TZ_Asia_Krasnoyarsk PSTR("<+07>-7")
#define TZ_Asia_Kuala_Lumpur PSTR("<+08>-8")
#define TZ_Asia_Kuching PSTR("<+08>-8")
#define TZ_Asia_Kuwait PSTR("<+03>-3")
#define TZ_Asia_Macau PSTR("CST-8")
#define TZ_Asia_Magadan PSTR("<+11>-11")
#define TZ_Asia_Makassar PSTR("WITA-8")
#define TZ_Asia_Manila PSTR("PST-8")
#define TZ_Asia_Muscat PSTR("<+04>-4")
#define TZ_Asia_Nicosia PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Asia_Novokuznetsk PSTR("<+07>-7")
#define TZ_Asia_Novosibirsk PSTR("<+07>-7")
#define TZ_Asia_Omsk PSTR("<+06>-6")
#define TZ_Asia_Oral PSTR("<+05>-5")
#define TZ_Asia_Phnom_Penh PSTR("<+07>-7")
#define TZ_Asia_Pontianak PSTR("WIB-7")
#define TZ_Asia_Pyongyang PSTR("KST-9")
#define TZ_Asia_Qatar PSTR("<+03>-3")
#define TZ_Asia_Qyzylorda PSTR("<+05>-5")
#define TZ_Asia_Riyadh PSTR("<+03>-3")
#define TZ_Asia_Sakhalin PSTR("<+11>-11")
#define TZ_Asia_Samarkand PSTR("<+05>-5")
#define TZ_Asia_Seoul PSTR("KST-9")
#define TZ_Asia_Shanghai PSTR("CST-8")
#define TZ_Asia_Singapore PSTR("<+08>-8")
#define TZ_Asia_Srednekolymsk PSTR("<+11>-11")
#define TZ_Asia_Taipei PSTR("CST-8")
#define TZ_Asia_Tashkent PSTR("<+05>-5")
#define TZ_Asia_Tbilisi PSTR("<+04>-4")
#define TZ_Asia_Tehran PSTR("<+0330>-3:30<+0430>,J79/24,J263/24")
#define TZ_Asia_Thimphu PSTR("<+06>-6")
#define TZ_Asia_Tokyo PSTR("JST-9")
#define TZ_Asia_Tomsk PSTR("<+07>-7")
#define TZ_Asia_Ulaanbaatar PSTR("<+08>-8")
#define TZ_Asia_Urumqi PSTR("<+06>-6")
#define TZ_Asia_UstmNera PSTR("<+10>-10")
#define TZ_Asia_Vientiane PSTR("<+07>-7")
#define TZ_Asia_Vladivostok PSTR("<+10>-10")
#define TZ_Asia_Yakutsk PSTR("<+09>-9")
#define TZ_Asia_Yangon PSTR("<+0630>-6:30")
#define TZ_Asia_Yekaterinburg PSTR("<+05>-5")
#define TZ_Asia_Yerevan PSTR("<+04>-4")
#define TZ_Atlantic_Azores PSTR("<-01>1<+00>,M3.5.0/0,M10.5.0/1")
#define TZ_Atlantic_Bermuda PSTR("AST4ADT,M3.2.0,M11.1.0")
#define TZ_Atlantic_Canary PSTR("WET0WEST,M3.5.0/1,M10.5.0")
#define TZ_Atlantic_Cape_Verde PSTR("<-01>1")
#define TZ_Atlantic_Faroe PSTR("WET0WEST,M3.5.0/1,M10.5.0")
#define TZ_Atlantic_Madeira PSTR("WET0WEST,M3.5.0/1,M10.5.0")
#define TZ_Atlantic_Reykjavik PSTR("GMT0")
#define TZ_Atlantic_South_Georgia PSTR("<-02>2")
#define TZ_Atlantic_Stanley PSTR("<-03>3")
#define TZ_Atlantic_St_Helena PSTR("GMT0")
#define TZ_Australia_Adelaide PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3")
#define TZ_Australia_Brisbane PSTR("AEST-10")
#define TZ_Australia_Broken_Hill PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3")
#define TZ_Australia_Currie PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
#define TZ_Australia_Darwin PSTR("ACST-9:30")
#define TZ_Australia_Eucla PSTR("<+0845>-8:45")
#define TZ_Australia_Hobart PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
#define TZ_Australia_Lindeman PSTR("AEST-10")
#define TZ_Australia_Lord_Howe PSTR("<+1030>-10:30<+11>-11,M10.1.0,M4.1.0")
#define TZ_Australia_Melbourne PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
#define TZ_Australia_Perth PSTR("AWST-8")
#define TZ_Australia_Sydney PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
#define TZ_Europe_Amsterdam PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Andorra PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Astrakhan PSTR("<+04>-4")
#define TZ_Europe_Athens PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Belgrade PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Berlin PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Bratislava PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Brussels PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Bucharest PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Budapest PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Busingen PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Chisinau PSTR("EET-2EEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Copenhagen PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Dublin PSTR("IST-1GMT0,M10.5.0,M3.5.0/1")
#define TZ_Europe_Gibraltar PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Guernsey PSTR("GMT0BST,M3.5.0/1,M10.5.0")
#define TZ_Europe_Helsinki PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Isle_of_Man PSTR("GMT0BST,M3.5.0/1,M10.5.0")
#define TZ_Europe_Istanbul PSTR("<+03>-3")
#define TZ_Europe_Jersey PSTR("GMT0BST,M3.5.0/1,M10.5.0")
#define TZ_Europe_Kaliningrad PSTR("EET-2")
#define TZ_Europe_Kiev PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Kirov PSTR("<+03>-3")
#define TZ_Europe_Lisbon PSTR("WET0WEST,M3.5.0/1,M10.5.0")
#define TZ_Europe_Ljubljana PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_London PSTR("GMT0BST,M3.5.0/1,M10.5.0")
#define TZ_Europe_Luxembourg PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Madrid PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Malta PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Mariehamn PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Minsk PSTR("<+03>-3")
#define TZ_Europe_Monaco PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Moscow PSTR("MSK-3")
#define TZ_Europe_Oslo PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Paris PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Podgorica PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Prague PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Riga PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Rome PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Samara PSTR("<+04>-4")
#define TZ_Europe_San_Marino PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Sarajevo PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Saratov PSTR("<+04>-4")
#define TZ_Europe_Simferopol PSTR("MSK-3")
#define TZ_Europe_Skopje PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Sofia PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Stockholm PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Tallinn PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Tirane PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Ulyanovsk PSTR("<+04>-4")
#define TZ_Europe_Uzhgorod PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Vaduz PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Vatican PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Vienna PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Vilnius PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Volgograd PSTR("<+04>-4")
#define TZ_Europe_Warsaw PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Zagreb PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Europe_Zaporozhye PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
#define TZ_Europe_Zurich PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
#define TZ_Indian_Antananarivo PSTR("EAT-3")
#define TZ_Indian_Chagos PSTR("<+06>-6")
#define TZ_Indian_Christmas PSTR("<+07>-7")
#define TZ_Indian_Cocos PSTR("<+0630>-6:30")
#define TZ_Indian_Comoro PSTR("EAT-3")
#define TZ_Indian_Kerguelen PSTR("<+05>-5")
#define TZ_Indian_Mahe PSTR("<+04>-4")
#define TZ_Indian_Maldives PSTR("<+05>-5")
#define TZ_Indian_Mauritius PSTR("<+04>-4")
#define TZ_Indian_Mayotte PSTR("EAT-3")
#define TZ_Indian_Reunion PSTR("<+04>-4")
#define TZ_Pacific_Apia PSTR("<+13>-13<+14>,M9.5.0/3,M4.1.0/4")
#define TZ_Pacific_Auckland PSTR("NZST-12NZDT,M9.5.0,M4.1.0/3")
#define TZ_Pacific_Bougainville PSTR("<+11>-11")
#define TZ_Pacific_Chatham PSTR("<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45")
#define TZ_Pacific_Chuuk PSTR("<+10>-10")
#define TZ_Pacific_Easter PSTR("<-06>6<-05>,M9.1.6/22,M4.1.6/22")
#define TZ_Pacific_Efate PSTR("<+11>-11")
#define TZ_Pacific_Enderbury PSTR("<+13>-13")
#define TZ_Pacific_Fakaofo PSTR("<+13>-13")
#define TZ_Pacific_Fiji PSTR("<+12>-12<+13>,M11.2.0,M1.2.3/99")
#define TZ_Pacific_Funafuti PSTR("<+12>-12")
#define TZ_Pacific_Galapagos PSTR("<-06>6")
#define TZ_Pacific_Gambier PSTR("<-09>9")
#define TZ_Pacific_Guadalcanal PSTR("<+11>-11")
#define TZ_Pacific_Guam PSTR("ChST-10")
#define TZ_Pacific_Honolulu PSTR("HST10")
#define TZ_Pacific_Kiritimati PSTR("<+14>-14")
#define TZ_Pacific_Kosrae PSTR("<+11>-11")
#define TZ_Pacific_Kwajalein PSTR("<+12>-12")
#define TZ_Pacific_Majuro PSTR("<+12>-12")
#define TZ_Pacific_Marquesas PSTR("<-0930>9:30")
#define TZ_Pacific_Midway PSTR("SST11")
#define TZ_Pacific_Nauru PSTR("<+12>-12")
#define TZ_Pacific_Niue PSTR("<-11>11")
#define TZ_Pacific_Norfolk PSTR("<+11>-11<+12>,M10.1.0,M4.1.0/3")
#define TZ_Pacific_Noumea PSTR("<+11>-11")
#define TZ_Pacific_Pago_Pago PSTR("SST11")
#define TZ_Pacific_Palau PSTR("<+09>-9")
#define TZ_Pacific_Pitcairn PSTR("<-08>8")
#define TZ_Pacific_Pohnpei PSTR("<+11>-11")
#define TZ_Pacific_Port_Moresby PSTR("<+10>-10")
#define TZ_Pacific_Rarotonga PSTR("<-10>10")
#define TZ_Pacific_Saipan PSTR("ChST-10")
#define TZ_Pacific_Tahiti PSTR("<-10>10")
#define TZ_Pacific_Tarawa PSTR("<+12>-12")
#define TZ_Pacific_Tongatapu PSTR("<+13>-13")
#define TZ_Pacific_Wake PSTR("<+12>-12")
#define TZ_Pacific_Wallis PSTR("<+12>-12")
#define TZ_Etc_GMT PSTR("GMT0")
#define TZ_Etc_GMTm0 PSTR("GMT0")
#define TZ_Etc_GMTm1 PSTR("<+01>-1")
#define TZ_Etc_GMTm2 PSTR("<+02>-2")
#define TZ_Etc_GMTm3 PSTR("<+03>-3")
#define TZ_Etc_GMTm4 PSTR("<+04>-4")
#define TZ_Etc_GMTm5 PSTR("<+05>-5")
#define TZ_Etc_GMTm6 PSTR("<+06>-6")
#define TZ_Etc_GMTm7 PSTR("<+07>-7")
#define TZ_Etc_GMTm8 PSTR("<+08>-8")
#define TZ_Etc_GMTm9 PSTR("<+09>-9")
#define TZ_Etc_GMTm10 PSTR("<+10>-10")
#define TZ_Etc_GMTm11 PSTR("<+11>-11")
#define TZ_Etc_GMTm12 PSTR("<+12>-12")
#define TZ_Etc_GMTm13 PSTR("<+13>-13")
#define TZ_Etc_GMTm14 PSTR("<+14>-14")
#define TZ_Etc_GMT0 PSTR("GMT0")
#define TZ_Etc_GMTp0 PSTR("GMT0")
#define TZ_Etc_GMTp1 PSTR("<-01>1")
#define TZ_Etc_GMTp2 PSTR("<-02>2")
#define TZ_Etc_GMTp3 PSTR("<-03>3")
#define TZ_Etc_GMTp4 PSTR("<-04>4")
#define TZ_Etc_GMTp5 PSTR("<-05>5")
#define TZ_Etc_GMTp6 PSTR("<-06>6")
#define TZ_Etc_GMTp7 PSTR("<-07>7")
#define TZ_Etc_GMTp8 PSTR("<-08>8")
#define TZ_Etc_GMTp9 PSTR("<-09>9")
#define TZ_Etc_GMTp10 PSTR("<-10>10")
#define TZ_Etc_GMTp11 PSTR("<-11>11")
#define TZ_Etc_GMTp12 PSTR("<-12>12")
#define TZ_Etc_UCT PSTR("UTC0")
#define TZ_Etc_UTC PSTR("UTC0")
#define TZ_Etc_Greenwich PSTR("GMT0")
#define TZ_Etc_Universal PSTR("UTC0")
#define TZ_Etc_Zulu PSTR("UTC0")
#endif
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
#define TIMEZONE_MAX_LEN 50 #define TIMEZONE_MAX_LEN 50
#if 1 static const char TZ_NAME[][TIMEZONE_MAX_LEN] /*PROGMEM*/ =
const char TZ_NAME[][TIMEZONE_MAX_LEN] /*PROGMEM*/ =
{ {
#if USING_AFRICA #if USING_AFRICA
"Africa/Abidjan", //PSTR("GMT0") "Africa/Abidjan", //PSTR("GMT0")
@ -1503,7 +1033,7 @@ const char TZ_NAME[][TIMEZONE_MAX_LEN] /*PROGMEM*/ =
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
const char ESP_TZ_NAME[][TIMEZONE_MAX_LEN] /*PROGMEM*/ = static const char ESP_TZ_NAME[][TIMEZONE_MAX_LEN] /*PROGMEM*/ =
{ {
#if USING_AFRICA #if USING_AFRICA
TZ_Africa_Abidjan, //PSTR("GMT0") TZ_Africa_Abidjan, //PSTR("GMT0")
@ -1999,152 +1529,4 @@ const char ESP_TZ_NAME[][TIMEZONE_MAX_LEN] /*PROGMEM*/ =
#endif #endif
}; };
#else
const char TZ_NAME[][TIMEZONE_MAX_LEN] =
{
"Pacific/Pago_Pago",
"America/Adak",
"Pacific/Honolulu",
"Pacific/Marquesas",
"Pacific/Gambier",
"America/Anchorage",
"America/Los_Angeles",
"Pacific/Pitcairn",
"America/Phoenix",
"America/Denver",
"America/Guatemala",
"America/Chicago",
"Pacific/Easter",
"America/Bogota",
"America/New_York",
"America/Caracas",
"America/Halifax",
"America/Santo_Domingo",
"America/Santiago",
"America/St_Johns",
"America/Godthab",
"America/Argentina/Buenos_Aires",
"America/Montevideo",
"Etc/GMT+2",
"Atlantic/Azores",
"Atlantic/Cape_Verde",
"Etc/UTC",
"Europe/London",
"Europe/Berlin",
"Africa/Lagos",
"Africa/Windhoek",
"Asia/Beirut",
"Africa/Johannesburg",
"Asia/Baghdad",
"Europe/Moscow",
"Asia/Tehran",
"Asia/Dubai",
"Asia/Baku",
"Asia/Kabul",
"Asia/Yekaterinburg",
"Asia/Karachi",
"Asia/Kolkata",
"Asia/Kathmandu",
"Asia/Dhaka",
"Asia/Omsk",
"Asia/Krasnoyarsk",
"Asia/Jakarta",
"Asia/Shanghai",
"Asia/Irkutsk",
"Australia/Eucla",
"Asia/Yakutsk",
"Asia/Tokyo",
"Australia/Darwin",
"Australia/Adelaide",
"Australia/Brisbane",
"Asia/Vladivostok",
"Australia/Sydney",
"Australia/Lord_Howe",
"Asia/Kamchatka",
"Pacific/Noumea",
"Pacific/Norfolk",
"Pacific/Auckland",
"Pacific/Tarawa",
"Pacific/Chatham",
"Pacific/Tongatapu",
"Pacific/Apia",
"Pacific/Kiritimati",
};
////////////////////////////////////////////////////////////
const char ESP_TZ_NAME[][TIMEZONE_MAX_LEN] =
{
TZ_Pacific_Pago_Pago, //PSTR("SST11")
TZ_America_Adak, //PSTR("HST10HDT,M3.2.0,M11.1.0")
TZ_Pacific_Honolulu, //PSTR("HST10")
TZ_Pacific_Marquesas, //PSTR("<-0930>9:30")
TZ_Pacific_Gambier, //PSTR("<-09>9")
TZ_America_Anchorage, //PSTR("AKST9AKDT,M3.2.0,M11.1.0")
TZ_America_Los_Angeles, //"PST8PDT,M3.2.0,M11.1.0", //"America/Los_Angeles",
TZ_Pacific_Pitcairn, //PSTR("<-08>8")
TZ_America_Phoenix, //PSTR("MST7")
TZ_America_Denver, //"MST7MDT,M3.2.0,M11.1.0", //"America/Denver",
TZ_America_Guatemala, //PSTR("CST6")
TZ_America_Chicago, //"CST6CDT,M3.2.0,M11.1.0", //"America/Chicago",
TZ_Pacific_Easter, //PSTR("<-06>6<-05>,M9.1.6/22,M4.1.6/22")
TZ_America_Bogota, //PSTR("<-05>5")
TZ_America_New_York, //"EST5EDT,M3.2.0,M11.1.0", //"America/New_York",
TZ_America_Caracas, //PSTR("<-04>4")
TZ_America_Halifax, //"AST4ADT,M3.2.0,M11.1.0", //"America/Halifax",
TZ_America_Santo_Domingo, //PSTR("AST4")
TZ_America_Santiago, //"<-04>4<-03>,M9.1.6/24,M4.1.6/24", //"America/Santiago",
TZ_America_St_Johns, //PSTR("NST3:30NDT,M3.2.0,M11.1.0")
TZ_America_Godthab, //"<-03>3<-02>,M3.5.0/-2,M10.5.0/-1", //"America/Godthab",
TZ_America_Argentina_Buenos_Aires, //PSTR("<-03>3")
TZ_America_Montevideo, //"<-03>3", //"America/Montevideo",
TZ_Etc_GMTp2, //PSTR("<-02>2")
TZ_Atlantic_Azores, //PSTR("<-01>1<+00>,M3.5.0/0,M10.5.0/1")
TZ_Atlantic_Cape_Verde, //PSTR("<-01>1")
TZ_Etc_UTC, //PSTR("UTC0")
TZ_Europe_London, //PSTR("GMT0BST,M3.5.0/1,M10.5.0")
TZ_Europe_Berlin, //PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
TZ_Africa_Lagos, //PSTR("WAT-1")
TZ_Africa_Windhoek, //PSTR("CAT-2")
TZ_Asia_Beirut, //"EET-2EEST,M3.5.0/0,M10.5.0/0" //"Asia/Beirut",
TZ_Africa_Johannesburg, //"SAST-2", //"Africa/Johannesburg",
TZ_Asia_Baghdad, //"<+03>-3", //"Asia/Baghdad",
TZ_Europe_Moscow, //PSTR("MSK-3")
TZ_Asia_Tehran, //PSTR("<+0330>-3:30<+0430>,J79/24,J263/24")
TZ_Asia_Dubai, //"<+04>-4", //"Asia/Dubai",
TZ_Asia_Baku, //PSTR("<+04>-4")
TZ_Asia_Kabul, //PSTR("<+0430>-4:30")
TZ_Asia_Yekaterinburg, //PSTR("<+05>-5")
TZ_Asia_Karachi, //PSTR("PKT-5")
TZ_Asia_Kolkata, //PSTR("IST-5:30")
TZ_Asia_Kathmandu, //PSTR("<+0545>-5:45")
TZ_Asia_Dhaka, //"<+06>-6", //"Asia/Dhaka",
TZ_Asia_Omsk, //PSTR("<+06>-6")
TZ_Asia_Krasnoyarsk, //PSTR("<+07>-7")
TZ_Asia_Jakarta, //"WIB-7", //"Asia/Jakarta",
TZ_Asia_Shanghai, //"CST-8", //"Asia/Shanghai",
TZ_Asia_Irkutsk, //PSTR("<+08>-8")
TZ_Australia_Eucla, //PSTR("<+0845>-8:45")
TZ_Asia_Yakutsk, //PSTR("<+09>-9")
TZ_Asia_Tokyo, //"JST-9", //"Asia/Tokyo",
TZ_Australia_Darwin, //PSTR("ACST-9:30")
TZ_Australia_Adelaide, //PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3")
TZ_Australia_Brisbane, //"AEST-10", //"Australia/Brisbane",
TZ_Asia_Vladivostok, //PSTR("<+10>-10")
TZ_Australia_Sydney, //PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
TZ_Australia_Lord_Howe, //PSTR("<+1030>-10:30<+11>-11,M10.1.0,M4.1.0")
TZ_Asia_Kamchatka, //PSTR("<+12>-12")
TZ_Pacific_Noumea, //"<+11>-11", //"Pacific/Noumea",
TZ_Pacific_Norfolk, //PSTR("<+11>-11<+12>,M10.1.0,M4.1.0/3")
TZ_Pacific_Auckland, //"NZST-12NZDT,M9.5.0,M4.1.0/3", //"Pacific/Auckland",
TZ_Pacific_Tarawa, //"<+12>-12", //"Pacific/Tarawa",
TZ_Pacific_Chatham, //PSTR("<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45")
TZ_Pacific_Tongatapu, //PSTR("<+13>-13")
TZ_Pacific_Apia, //PSTR("<+13>-13<+14>,M9.5.0/3,M4.1.0/4")
TZ_Pacific_Kiritimati, //PSTR("<+14>-14")
};
#endif
#endif // TZDB_H #endif // TZDB_H

View File

@ -195,11 +195,7 @@ uint8_t OneWire::reset(void)
// Write a bit. Port and bit is used to cut lookup time and provide // Write a bit. Port and bit is used to cut lookup time and provide
// more certain timing. // more certain timing.
// //
#if defined(ARDUINO_ARCH_ESP32)
void IRAM_ATTR OneWire::write_bit(uint8_t v)
#else
void OneWire::write_bit(uint8_t v) void OneWire::write_bit(uint8_t v)
#endif
{ {
IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask; IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask;
volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg; volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg;
@ -227,11 +223,7 @@ void OneWire::write_bit(uint8_t v)
// Read a bit. Port and bit is used to cut lookup time and provide // Read a bit. Port and bit is used to cut lookup time and provide
// more certain timing. // more certain timing.
// //
#if defined(ARDUINO_ARCH_ESP32)
uint8_t IRAM_ATTR OneWire::read_bit(void)
#else
uint8_t OneWire::read_bit(void) uint8_t OneWire::read_bit(void)
#endif
{ {
IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask; IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask;
volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg; volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg;

View File

@ -99,18 +99,10 @@ class OneWire
// Write a bit. The bus is always left powered at the end, see // Write a bit. The bus is always left powered at the end, see
// note in write() about that. // note in write() about that.
#if defined (ARDUINO_ARCH_ESP32)
void IRAM_ATTR write_bit(uint8_t v);
#else
void write_bit(uint8_t v); void write_bit(uint8_t v);
#endif
// Read a bit. // Read a bit.
#if defined (ARDUINO_ARCH_ESP32)
uint8_t IRAM_ATTR read_bit(void);
#else
uint8_t read_bit(void); uint8_t read_bit(void);
#endif
// Stop forcing power onto the bus. You only need to do this if // Stop forcing power onto the bus. You only need to do this if
// you used the 'power' flag to write() or used a write_bit() call // you used the 'power' flag to write() or used a write_bit() call

View File

@ -127,10 +127,14 @@
static inline __attribute__((always_inline)) static inline __attribute__((always_inline))
IO_REG_TYPE directRead(IO_REG_TYPE pin) IO_REG_TYPE directRead(IO_REG_TYPE pin)
{ {
#if CONFIG_IDF_TARGET_ESP32C3
return (GPIO.in.val >> pin) & 0x1;
#else // plain ESP32
if ( pin < 32 ) if ( pin < 32 )
return (GPIO.in >> pin) & 0x1; return (GPIO.in >> pin) & 0x1;
else if ( pin < 40 ) else if ( pin < 46 )
return (GPIO.in1.val >> (pin - 32)) & 0x1; return (GPIO.in1.val >> (pin - 32)) & 0x1;
#endif
return 0; return 0;
} }
@ -138,38 +142,38 @@ IO_REG_TYPE directRead(IO_REG_TYPE pin)
static inline __attribute__((always_inline)) static inline __attribute__((always_inline))
void directWriteLow(IO_REG_TYPE pin) void directWriteLow(IO_REG_TYPE pin)
{ {
#if CONFIG_IDF_TARGET_ESP32C3
GPIO.out_w1tc.val = ((uint32_t)1 << pin);
#else // plain ESP32
if ( pin < 32 ) if ( pin < 32 )
GPIO.out_w1tc = ((uint32_t)1 << pin); GPIO.out_w1tc = ((uint32_t)1 << pin);
else if ( pin < 34 ) else if ( pin < 46 )
GPIO.out1_w1tc.val = ((uint32_t)1 << (pin - 32)); GPIO.out1_w1tc.val = ((uint32_t)1 << (pin - 32));
#endif
} }
static inline __attribute__((always_inline)) static inline __attribute__((always_inline))
void directWriteHigh(IO_REG_TYPE pin) void directWriteHigh(IO_REG_TYPE pin)
{ {
#if CONFIG_IDF_TARGET_ESP32C3
GPIO.out_w1ts.val = ((uint32_t)1 << pin);
#else // plain ESP32
if ( pin < 32 ) if ( pin < 32 )
GPIO.out_w1ts = ((uint32_t)1 << pin); GPIO.out_w1ts = ((uint32_t)1 << pin);
else if ( pin < 34 ) else if ( pin < 46 )
GPIO.out1_w1ts.val = ((uint32_t)1 << (pin - 32)); GPIO.out1_w1ts.val = ((uint32_t)1 << (pin - 32));
#endif
} }
static inline __attribute__((always_inline)) static inline __attribute__((always_inline))
void directModeInput(IO_REG_TYPE pin) void directModeInput(IO_REG_TYPE pin)
{ {
#if CONFIG_IDF_TARGET_ESP32C3
GPIO.enable_w1tc.val = ((uint32_t)1 << (pin));
#else
if ( digitalPinIsValid(pin) ) if ( digitalPinIsValid(pin) )
{ {
#if defined(ESP_ARDUINO_VERSION) #if ESP_IDF_VERSION_MAJOR < 4 // IDF 3.x ESP32/PICO-D4
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0)
int pin_io = rtc_io_number_get((gpio_num_t)pin);
uint32_t rtc_reg(rtc_io_desc[pin_io].reg);
if ( rtc_reg ) // RTC pins PULL settings
{
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_io_desc[pin_io].mux);
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_io_desc[pin_io].pullup | rtc_io_desc[pin_io].pulldown);
}
#endif
#else
uint32_t rtc_reg(rtc_gpio_desc[pin].reg); uint32_t rtc_reg(rtc_gpio_desc[pin].reg);
if ( rtc_reg ) // RTC pins PULL settings if ( rtc_reg ) // RTC pins PULL settings
@ -177,40 +181,25 @@ void directModeInput(IO_REG_TYPE pin)
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].mux); ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].mux);
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].pullup | rtc_gpio_desc[pin].pulldown); ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].pullup | rtc_gpio_desc[pin].pulldown);
} }
#endif #endif
// Input
if ( pin < 32 ) if ( pin < 32 )
GPIO.enable_w1tc = ((uint32_t)1 << pin); GPIO.enable_w1tc = ((uint32_t)1 << pin);
else else
GPIO.enable1_w1tc.val = ((uint32_t)1 << (pin - 32)); GPIO.enable1_w1tc.val = ((uint32_t)1 << (pin - 32));
uint32_t pinFunction((uint32_t)2 << FUN_DRV_S); // what are the drivers?
pinFunction |= FUN_IE; // input enable but required for output as well?
pinFunction |= ((uint32_t)2 << MCU_SEL_S);
ESP_REG(DR_REG_IO_MUX_BASE + esp32_gpioMux[pin].reg) = pinFunction;
GPIO.pin[pin].val = 0;
} }
#endif
} }
static inline __attribute__((always_inline)) static inline __attribute__((always_inline))
void directModeOutput(IO_REG_TYPE pin) void directModeOutput(IO_REG_TYPE pin)
{ {
#if CONFIG_IDF_TARGET_ESP32C3
GPIO.enable_w1ts.val = ((uint32_t)1 << (pin));
#else
if ( digitalPinIsValid(pin) && pin <= 33 ) // pins above 33 can be only inputs if ( digitalPinIsValid(pin) && pin <= 33 ) // pins above 33 can be only inputs
{ {
#if defined(ESP_ARDUINO_VERSION) #if ESP_IDF_VERSION_MAJOR < 4 // IDF 3.x ESP32/PICO-D4
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0)
int pin_io = rtc_io_number_get((gpio_num_t)pin);
uint32_t rtc_reg(rtc_io_desc[pin_io].reg);
if ( rtc_reg ) // RTC pins PULL settings
{
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_io_desc[pin_io].mux);
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_io_desc[pin_io].pullup | rtc_io_desc[pin_io].pulldown);
}
#endif
#else
uint32_t rtc_reg(rtc_gpio_desc[pin].reg); uint32_t rtc_reg(rtc_gpio_desc[pin].reg);
if ( rtc_reg ) // RTC pins PULL settings if ( rtc_reg ) // RTC pins PULL settings
@ -218,21 +207,14 @@ void directModeOutput(IO_REG_TYPE pin)
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].mux); ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].mux);
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].pullup | rtc_gpio_desc[pin].pulldown); ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].pullup | rtc_gpio_desc[pin].pulldown);
} }
#endif #endif
// Output
if ( pin < 32 ) if ( pin < 32 )
GPIO.enable_w1ts = ((uint32_t)1 << pin); GPIO.enable_w1ts = ((uint32_t)1 << pin);
else // already validated to pins <= 33 else // already validated to pins <= 33
GPIO.enable1_w1ts.val = ((uint32_t)1 << (pin - 32)); GPIO.enable1_w1ts.val = ((uint32_t)1 << (pin - 32));
uint32_t pinFunction((uint32_t)2 << FUN_DRV_S); // what are the drivers?
pinFunction |= FUN_IE; // input enable but required for output as well?
pinFunction |= ((uint32_t)2 << MCU_SEL_S);
ESP_REG(DR_REG_IO_MUX_BASE + esp32_gpioMux[pin].reg) = pinFunction;
GPIO.pin[pin].val = 0;
} }
#endif
} }
#define DIRECT_READ(base, pin) directRead(pin) #define DIRECT_READ(base, pin) directRead(pin)
@ -445,6 +427,20 @@ void directWriteHigh(IO_REG_TYPE mask)
#define DIRECT_MODE_INPUT(base, mask) directModeInput(mask) #define DIRECT_MODE_INPUT(base, mask) directModeInput(mask)
#define DIRECT_MODE_OUTPUT(base, mask) directModeOutput(mask) #define DIRECT_MODE_OUTPUT(base, mask) directModeOutput(mask)
#elif defined(ARDUINO_ARCH_MBED_RP2040)|| defined(ARDUINO_ARCH_RP2040)
#define delayMicroseconds(time) busy_wait_us(time)
#define PIN_TO_BASEREG(pin) (0)
#define PIN_TO_BITMASK(pin) (pin)
#define IO_REG_TYPE unsigned int
#define IO_REG_BASE_ATTR
#define IO_REG_MASK_ATTR
#define DIRECT_READ(base, pin) digitalRead(pin)
#define DIRECT_WRITE_LOW(base, pin) digitalWrite(pin, LOW)
#define DIRECT_WRITE_HIGH(base, pin) digitalWrite(pin, HIGH)
#define DIRECT_MODE_INPUT(base, pin) pinMode(pin,INPUT)
#define DIRECT_MODE_OUTPUT(base, pin) pinMode(pin,OUTPUT)
#warning "OneWire. RP2040 in Fallback mode. Using API calls for pinMode,digitalRead and digitalWrite."
#else #else
#define PIN_TO_BASEREG(pin) (0) #define PIN_TO_BASEREG(pin) (0)
#define PIN_TO_BITMASK(pin) (pin) #define PIN_TO_BITMASK(pin) (pin)

View File

@ -234,9 +234,7 @@ int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8
useWire->beginTransmission(devAddr); useWire->beginTransmission(devAddr);
useWire->send(regAddr); useWire->send(regAddr);
useWire->endTransmission(); useWire->endTransmission();
useWire->beginTransmission(devAddr);
useWire->requestFrom((uint8_t)devAddr, (uint8_t)min((int)length - k, I2CDEVLIB_WIRE_BUFFER_LENGTH)); useWire->requestFrom((uint8_t)devAddr, (uint8_t)min((int)length - k, I2CDEVLIB_WIRE_BUFFER_LENGTH));
for (; useWire->available() && (timeout == 0 || millis() - t1 < timeout); count++) { for (; useWire->available() && (timeout == 0 || millis() - t1 < timeout); count++) {
data[count] = useWire->receive(); data[count] = useWire->receive();
#ifdef I2CDEV_SERIAL_DEBUG #ifdef I2CDEV_SERIAL_DEBUG
@ -244,8 +242,6 @@ int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8
if (count + 1 < length) Serial.print(" "); if (count + 1 < length) Serial.print(" ");
#endif #endif
} }
useWire->endTransmission();
} }
#elif (ARDUINO == 100) #elif (ARDUINO == 100)
// Arduino v1.0.0, Wire library // Arduino v1.0.0, Wire library
@ -258,9 +254,7 @@ int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8
useWire->beginTransmission(devAddr); useWire->beginTransmission(devAddr);
useWire->write(regAddr); useWire->write(regAddr);
useWire->endTransmission(); useWire->endTransmission();
useWire->beginTransmission(devAddr);
useWire->requestFrom((uint8_t)devAddr, (uint8_t)min((int)length - k, I2CDEVLIB_WIRE_BUFFER_LENGTH)); useWire->requestFrom((uint8_t)devAddr, (uint8_t)min((int)length - k, I2CDEVLIB_WIRE_BUFFER_LENGTH));
for (; useWire->available() && (timeout == 0 || millis() - t1 < timeout); count++) { for (; useWire->available() && (timeout == 0 || millis() - t1 < timeout); count++) {
data[count] = useWire->read(); data[count] = useWire->read();
#ifdef I2CDEV_SERIAL_DEBUG #ifdef I2CDEV_SERIAL_DEBUG
@ -268,8 +262,6 @@ int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8
if (count + 1 < length) Serial.print(" "); if (count + 1 < length) Serial.print(" ");
#endif #endif
} }
useWire->endTransmission();
} }
#elif (ARDUINO > 100) #elif (ARDUINO > 100)
// Arduino v1.0.1+, Wire library // Arduino v1.0.1+, Wire library
@ -282,9 +274,7 @@ int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8
useWire->beginTransmission(devAddr); useWire->beginTransmission(devAddr);
useWire->write(regAddr); useWire->write(regAddr);
useWire->endTransmission(); useWire->endTransmission();
useWire->beginTransmission(devAddr);
useWire->requestFrom((uint8_t)devAddr, (uint8_t)min((int)length - k, I2CDEVLIB_WIRE_BUFFER_LENGTH)); useWire->requestFrom((uint8_t)devAddr, (uint8_t)min((int)length - k, I2CDEVLIB_WIRE_BUFFER_LENGTH));
for (; useWire->available() && (timeout == 0 || millis() - t1 < timeout); count++) { for (; useWire->available() && (timeout == 0 || millis() - t1 < timeout); count++) {
data[count] = useWire->read(); data[count] = useWire->read();
#ifdef I2CDEV_SERIAL_DEBUG #ifdef I2CDEV_SERIAL_DEBUG
@ -356,7 +346,6 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1
useWire->beginTransmission(devAddr); useWire->beginTransmission(devAddr);
useWire->send(regAddr); useWire->send(regAddr);
useWire->endTransmission(); useWire->endTransmission();
useWire->beginTransmission(devAddr);
useWire->requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes useWire->requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes
bool msb = true; // starts with MSB, then LSB bool msb = true; // starts with MSB, then LSB
@ -375,8 +364,6 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1
} }
msb = !msb; msb = !msb;
} }
useWire->endTransmission();
} }
#elif (ARDUINO == 100) #elif (ARDUINO == 100)
// Arduino v1.0.0, Wire library // Arduino v1.0.0, Wire library
@ -389,7 +376,6 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1
useWire->beginTransmission(devAddr); useWire->beginTransmission(devAddr);
useWire->write(regAddr); useWire->write(regAddr);
useWire->endTransmission(); useWire->endTransmission();
useWire->beginTransmission(devAddr);
useWire->requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes useWire->requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes
bool msb = true; // starts with MSB, then LSB bool msb = true; // starts with MSB, then LSB
@ -408,8 +394,6 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1
} }
msb = !msb; msb = !msb;
} }
useWire->endTransmission();
} }
#elif (ARDUINO > 100) #elif (ARDUINO > 100)
// Arduino v1.0.1+, Wire library // Arduino v1.0.1+, Wire library
@ -422,7 +406,6 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1
useWire->beginTransmission(devAddr); useWire->beginTransmission(devAddr);
useWire->write(regAddr); useWire->write(regAddr);
useWire->endTransmission(); useWire->endTransmission();
useWire->beginTransmission(devAddr);
useWire->requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes useWire->requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes
bool msb = true; // starts with MSB, then LSB bool msb = true; // starts with MSB, then LSB
@ -441,8 +424,6 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1
} }
msb = !msb; msb = !msb;
} }
useWire->endTransmission();
} }
#endif #endif

View File

@ -2556,8 +2556,8 @@ void MPU6050_Base::setClockSource(uint8_t source) {
* -------------+------------------ * -------------+------------------
* 0 | 1.25 Hz * 0 | 1.25 Hz
* 1 | 2.5 Hz * 1 | 2.5 Hz
* 2 | 5 Hz * 2 | 20 Hz
* 3 | 10 Hz * 3 | 40 Hz
* </pre> * </pre>
* *
* For further information regarding the MPU-60X0's power modes, please refer to * For further information regarding the MPU-60X0's power modes, please refer to
@ -3371,25 +3371,27 @@ void MPU6050_Base::PID(uint8_t ReadAddress, float kP,float kI, uint8_t Loops){
resetDMP(); resetDMP();
} }
void MPU6050_Base::PrintActiveOffsets() { int16_t * MPU6050_Base::GetActiveOffsets() {
uint8_t AOffsetRegister = (getDeviceID() < 0x38 )? MPU6050_RA_XA_OFFS_H:0x77; uint8_t AOffsetRegister = (getDeviceID() < 0x38 )? MPU6050_RA_XA_OFFS_H:0x77;
int16_t Data[3]; if(AOffsetRegister == 0x06) I2Cdev::readWords(devAddr, AOffsetRegister, 3, (uint16_t *)offsets, I2Cdev::readTimeout, wireObj);
//Serial.print(F("Offset Register 0x"));
//Serial.print(AOffsetRegister>>4,HEX);Serial.print(AOffsetRegister&0x0F,HEX);
Serial.print(F("\n// X Accel Y Accel Z Accel X Gyro Y Gyro Z Gyro\n// OFFSETS "));
if(AOffsetRegister == 0x06) I2Cdev::readWords(devAddr, AOffsetRegister, 3, (uint16_t *)Data, I2Cdev::readTimeout, wireObj);
else { else {
I2Cdev::readWords(devAddr, AOffsetRegister, 1, (uint16_t *)Data, I2Cdev::readTimeout, wireObj); I2Cdev::readWords(devAddr, AOffsetRegister, 1, (uint16_t *)offsets, I2Cdev::readTimeout, wireObj);
I2Cdev::readWords(devAddr, AOffsetRegister+3, 1, (uint16_t *)Data+1, I2Cdev::readTimeout, wireObj); I2Cdev::readWords(devAddr, AOffsetRegister+3, 1, (uint16_t *)(offsets+1), I2Cdev::readTimeout, wireObj);
I2Cdev::readWords(devAddr, AOffsetRegister+6, 1, (uint16_t *)Data+2, I2Cdev::readTimeout, wireObj); I2Cdev::readWords(devAddr, AOffsetRegister+6, 1, (uint16_t *)(offsets+2), I2Cdev::readTimeout, wireObj);
} }
// A_OFFSET_H_READ_A_OFFS(Data); I2Cdev::readWords(devAddr, 0x13, 3, (uint16_t *)(offsets+3), I2Cdev::readTimeout, wireObj);
Serial.print((float)Data[0], 5); Serial.print(", "); return offsets;
Serial.print((float)Data[1], 5); Serial.print(", "); }
Serial.print((float)Data[2], 5); Serial.print(", ");
I2Cdev::readWords(devAddr, 0x13, 3, (uint16_t *)Data, I2Cdev::readTimeout, wireObj); void MPU6050_Base::PrintActiveOffsets() {
// XG_OFFSET_H_READ_OFFS_USR(Data); GetActiveOffsets();
Serial.print((float)Data[0], 5); Serial.print(", "); // A_OFFSET_H_READ_A_OFFS(Data);
Serial.print((float)Data[1], 5); Serial.print(", "); Serial.print((float)offsets[0], 5); Serial.print(",\t");
Serial.print((float)Data[2], 5); Serial.print("\n"); Serial.print((float)offsets[1], 5); Serial.print(",\t");
Serial.print((float)offsets[2], 5); Serial.print(",\t");
// XG_OFFSET_H_READ_OFFS_USR(Data);
Serial.print((float)offsets[3], 5); Serial.print(",\t");
Serial.print((float)offsets[4], 5); Serial.print(",\t");
Serial.print((float)offsets[5], 5); Serial.print("\n\n");
} }

View File

@ -832,12 +832,16 @@ class MPU6050_Base {
void CalibrateAccel(uint8_t Loops = 15);// Fine tune after setting offsets with less Loops. void CalibrateAccel(uint8_t Loops = 15);// Fine tune after setting offsets with less Loops.
void PID(uint8_t ReadAddress, float kP,float kI, uint8_t Loops); // Does the math void PID(uint8_t ReadAddress, float kP,float kI, uint8_t Loops); // Does the math
void PrintActiveOffsets(); // See the results of the Calibration void PrintActiveOffsets(); // See the results of the Calibration
int16_t * GetActiveOffsets();
protected: protected:
uint8_t devAddr; uint8_t devAddr;
void *wireObj; void *wireObj;
uint8_t buffer[14]; uint8_t buffer[14];
uint32_t fifoTimeout = MPU6050_FIFO_DEFAULT_TIMEOUT; uint32_t fifoTimeout = MPU6050_FIFO_DEFAULT_TIMEOUT;
private:
int16_t offsets[6];
}; };
#ifndef I2CDEVLIB_MPU6050_TYPEDEF #ifndef I2CDEVLIB_MPU6050_TYPEDEF

13
part32.csv Normal file
View File

@ -0,0 +1,13 @@
# Name,Type,SubType,Offset,Size,Flags
nvs,data,nvs,0x9000,0x5000
otadata,data,ota,0xe000,0x2000
app0,app,ota_0,0x10000,0x1c0000
app1,app,ota_1,0x1d0000,0x1c0000
spiffs,data,spiffs,0x390000,0x70000
# Name, Type, SubType, Offset, Size, Flags
#nvs, data, nvs, 0x9000, 0x5000,
#otadata, data, ota, 0xe000, 0x2000,
#app0, app, ota_0, 0x10000, 0x140000, # 1310720
#app1, app, ota_1, 0x150000,0x140000, # 1310720
#spiffs, data, spiffs, 0x290000,0x170000, # 1507328
1 # Name,Type,SubType,Offset,Size,Flags
2 nvs,data,nvs,0x9000,0x5000
3 otadata,data,ota,0xe000,0x2000
4 app0,app,ota_0,0x10000,0x1c0000
5 app1,app,ota_1,0x1d0000,0x1c0000
6 spiffs,data,spiffs,0x390000,0x70000
7 # Name, Type, SubType, Offset, Size, Flags
8 #nvs, data, nvs, 0x9000, 0x5000,
9 #otadata, data, ota, 0xe000, 0x2000,
10 #app0, app, ota_0, 0x10000, 0x140000, # 1310720
11 #app1, app, ota_1, 0x150000,0x140000, # 1310720
12 #spiffs, data, spiffs, 0x290000,0x170000, # 1507328

View File

@ -15,137 +15,328 @@ include_dir = lib
[common_env_data] [common_env_data]
upload_speed = 921600 upload_speed = 921600
monitor_speed = 115200 monitor_speed = 115200
platform = espressif8266 @ 3.2.0 platform = espressif8266 @ 4.0.1
platform32 = espressif32 @ 5.2.0
framework = arduino framework = arduino
board = d1_mini board = d1_mini
build_unflags = build_unflags =
build_flags = build_flags =
-Wl,-Map,output.map -Wl,-Map,output.map
-D BAUD=${common_env_data.monitor_speed} -DBAUD=${common_env_data.monitor_speed}
-D ACTIVATE_OTA
#-D DEBUG_ESP_HTTP_CLIENT
#-D DEBUG_ESP_HTTP_SERVER
#-D DEBUG_ESP_PORT=Serial
#-D DEBUG_ESP_WIFI
#-D DEBUG_ESP_SSL
#-D DEBUG_ESP_CORE
#-D SKIP_SLEEPMODE #-D SKIP_SLEEPMODE
-D USE_LITTLEFS=true #-D FORCE_GRAVITY_MODE
-D EMBED_HTML # If this is not used the html files needs to be on the file system (can be uploaded) #-D DOUBLERESETDETECTOR_DEBUG=true
-D USER_SSID=\""\"" # =\""myssid\"" -DACTIVATE_OTA
-D USER_SSID_PWD=\""\"" # =\""mypwd\"" -DCFG_DISABLE_LOGGING # Turn off verbose/notice logging to reduce size and dont overload uart (applies to LOG_LEVEL6)
-D CFG_APPVER="\"0.7.1\"" -DGYRO_DISABLE_LOGGING
lib_deps = # Switched to forks for better version control. -DCALC_DISABLE_LOGGING
-DHELPER_DISABLE_LOGGING
-DPUSH_DISABLE_LOGGING
-DTSEN_DISABLE_LOGGING
-DWIFI_DISABLE_LOGGING
-DWEB_DISABLE_LOGGING
-DMAIN_DISABLE_LOGGING
-DUSE_LITTLEFS=true
-DUSER_SSID=\""\"" # =\""myssid\""
-DUSER_SSID_PWD=\""\"" # =\""mypwd\""
-DCFG_APPVER="\"1.2.1\""
#-DCFG_GITREV=\""beta-3\""
!python script/git_rev.py
lib_deps =
# Using local copy of these libraries # Using local copy of these libraries
#https://github.com/jrowberg/i2cdevlib.git#<document> # https://github.com/mp-se/i2cdevlib.git#<document>
#https://github.com/khoih-prog/ESP_WiFiManager#<document> # https://github.com/mp-se/OneWire
#https://github.com/khoih-prog/ESP_DoubleResetDetector#<document> # https://github.com/mp-se/Arduino-Temperature-Control-Library
#https://github.com/PaulStoffregen/OneWire # https://github.com/khoih-prog/ESP_WiFiManager
#https://github.com/milesburton/Arduino-Temperature-Control-Library # https://github.com/khoih-prog/ESP_DoubleResetDetector
https://github.com/mp-se/tinyexpr # https://github.com/codeplea/tinyexpr https://github.com/mp-se/tinyexpr # https://github.com/codeplea/tinyexpr
https://github.com/mp-se/incbin # https://github.com/graphitemaster/incbin
https://github.com/mp-se/Arduino-Log#1.1.1 # https://github.com/thijse/Arduino-Log https://github.com/mp-se/Arduino-Log#1.1.1 # https://github.com/thijse/Arduino-Log
https://github.com/mp-se/ArduinoJson#v6.18.5 # https://github.com/bblanchon/ArduinoJson 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/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/arduino-mqtt#v2.5.0 # https://github.com/256dpi/arduino-mqtt
lib_deps32 =
[env:gravity-debug] https://github.com/mp-se/NimBLE-Arduino#1.3.8 # https://github.com/h2zero/NimBLE-Arduino
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 = extra_scripts =
script/copy_html.py
script/copy_firmware.py script/copy_firmware.py
script/create_versionjson.py script/create_versionjson.py
build_unflags =
${common_env_data.build_unflags} ; [env:gravity-debug]
build_flags = ; upload_speed = ${common_env_data.upload_speed}
${common_env_data.build_flags} ; monitor_speed = ${common_env_data.monitor_speed}
#-D PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS ; framework = ${common_env_data.framework}
#-D SKIP_SLEEPMODE ; platform = ${common_env_data.platform}
#-D DOUBLERESETDETECTOR_DEBUG=true ; extra_scripts = ${common_env_data.extra_scripts}
-D COLLECT_PERFDATA # This option will collect runtime data for a few defined methods to measure time, dumped to serial and/or influxdb ; build_unflags = ${common_env_data.build_unflags}
-D LOG_LEVEL=6 # Maximum log level for the debug build. ; build_flags =
-D CFG_DISABLE_LOGGING # Turn off verbose/notice logging to reduce size and dont overload uart. ; ${common_env_data.build_flags}
-D GYRO_DISABLE_LOGGING ; #-D DEBUG_ESP_HTTP_CLIENT
-D CALC_DISABLE_LOGGING ; #-D DEBUG_ESP_HTTP_SERVER
-D HELPER_DISABLE_LOGGING ; #-D DEBUG_ESP_PORT=Serial
-D PUSH_DISABLE_LOGGING ; #-D DEBUG_ESP_WIFI
-D TSEN_DISABLE_LOGGING ; #-D DEBUG_ESP_SSL
-D WIFI_DISABLE_LOGGING ; #-D DEBUG_ESP_CORE
-D WEB_DISABLE_LOGGING ; -DPIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS
-D MAIN_DISABLE_LOGGING ; -DCOLLECT_PERFDATA # Collect runtime data for a few defined methods to measure time, dumped to serial and/or influxdb
lib_deps = ; -DLOG_LEVEL=6
${common_env_data.lib_deps} ; lib_deps =
board = ${common_env_data.board} ; https://github.com/mp-se/incbin # https://github.com/graphitemaster/incbin
#build_type = debug ; ${common_env_data.lib_deps}
build_type = release ; board = ${common_env_data.board}
board_build.filesystem = littlefs ; build_type = release
monitor_filters = esp8266_exception_decoder ; board_build.filesystem = littlefs
; monitor_filters = esp8266_exception_decoder
[env:gravity-release] [env:gravity-release]
upload_speed = ${common_env_data.upload_speed} upload_speed = ${common_env_data.upload_speed}
monitor_speed = ${common_env_data.monitor_speed} monitor_speed = ${common_env_data.monitor_speed}
framework = ${common_env_data.framework} framework = ${common_env_data.framework}
platform = ${common_env_data.platform} platform = ${common_env_data.platform}
extra_scripts = extra_scripts = ${common_env_data.extra_scripts}
script/copy_html.py build_unflags = ${common_env_data.build_unflags}
script/copy_firmware.py build_flags =
script/create_versionjson.py ${common_env_data.build_flags}
-D LOG_LEVEL=4
#-DUSE_SERIAL_PINS # Use the TX/RX pins for the serial port
lib_deps =
https://github.com/mp-se/incbin # https://github.com/graphitemaster/incbin
${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_unflags = ${common_env_data.build_unflags}
build_flags = build_flags =
${common_env_data.build_flags} ${common_env_data.build_flags}
-D LOG_LEVEL=4 -D LOG_LEVEL=4
lib_deps = 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} ${common_env_data.lib_deps}
board = ${common_env_data.board} board = ${common_env_data.board}
build_type = release build_type = debug
board_build.filesystem = littlefs board_build.filesystem = littlefs
build_src_filter = +<*> -<main.cpp> +<../test/tests*.cpp>
monitor_filters = esp8266_exception_decoder
[env:gravity-perf] [env:gravity32-release]
upload_speed = ${common_env_data.upload_speed}
monitor_speed = ${common_env_data.monitor_speed}
framework = ${common_env_data.framework} framework = ${common_env_data.framework}
platform = ${common_env_data.platform} platform = ${common_env_data.platform32}
extra_scripts =
script/copy_html.py
script/copy_firmware.py
script/create_versionjson.py
build_unflags = ${common_env_data.build_unflags}
build_flags =
${common_env_data.build_flags}
-D COLLECT_PERFDATA
-D LOG_LEVEL=5
lib_deps =
${common_env_data.lib_deps}
board = ${common_env_data.board}
build_type = release
board_build.filesystem = littlefs
[env:gravity32-perf]
framework = arduino
# platformio only supports v1.0.6 of the esp32 libs.
platform = espressif32
# tasmota port of v2.0.2
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.1/platform-tasmota-espressif32-2.0.2.1.zip
upload_speed = ${common_env_data.upload_speed} upload_speed = ${common_env_data.upload_speed}
monitor_speed = ${common_env_data.monitor_speed} monitor_speed = ${common_env_data.monitor_speed}
extra_scripts = extra_scripts = ${common_env_data.extra_scripts}
script/copy_html.py
script/copy_firmware.py
script/create_versionjson.py
build_unflags = build_unflags =
${common_env_data.build_unflags} ${common_env_data.build_unflags}
build_flags = build_flags =
-Wl,-Map,output.map
#-DCORE_DEBUG_LEVEL=0
${common_env_data.build_flags} ${common_env_data.build_flags}
-D COLLECT_PERFDATA
-D LOG_LEVEL=5 -D LOG_LEVEL=5
-DESP32D1
-DCORE_DEBUG_LEVEL=0
#-DUSE_SERIAL_PINS # Use the TX/RX pins for the serial port
lib_deps = lib_deps =
${common_env_data.lib_deps} ${common_env_data.lib_deps}
board = nodemcu-32s ${common_env_data.lib_deps32}
lib_ignore =
board = wemos_d1_mini32
build_type = release build_type = release
#build_type = debug board_build.partitions = part32.csv
#board_build.filesystem = littlefs board_build.filesystem = littlefs
board_build.filesystem = spiffs board_build.embed_txtfiles =
monitor_filters = esp32_exception_decoder 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:gravity32c3-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 =
-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 =
-Wl,-Map,output.map
${common_env_data.build_flags}
-DLOG_LEVEL=5
-DCORE_DEBUG_LEVEL=0
-DESP32C3
-DARDUINO_ESP32C3_DEV
-DREDUCE_WIFI_POWER # Enable this if v1.0 chip is used
#-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:gravity32s2-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 =
-Wl,-Map,output.map
${common_env_data.build_flags}
-DLOG_LEVEL=5
-DCORE_DEBUG_LEVEL=0
-DESP32S2
-DARDUINO_ESP32S2_DEV
#-DUSE_SERIAL_PINS # Use the TX/RX pins for the serial port
lib_deps =
${common_env_data.lib_deps}
lib_ignore =
board = lolin_s2_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:gravity32c3-debug]
framework = ${common_env_data.framework}
platform = ${common_env_data.platform32}
upload_speed = ${common_env_data.upload_speed}
monitor_speed = ${common_env_data.monitor_speed}
monitor_filters = time, colorize, log2file, esp32_exception_decoder
extra_scripts = ${common_env_data.extra_scripts}
upload_port = COM8
debug_tool = esp-prog
debug_init_break = break setup
build_unflags =
-DCFG_DISABLE_LOGGING
-DGYRO_DISABLE_LOGGING
-DCALC_DISABLE_LOGGING
-DHELPER_DISABLE_LOGGING
-DPUSH_DISABLE_LOGGING
-DTSEN_DISABLE_LOGGING
-DWIFI_DISABLE_LOGGING
-DWEB_DISABLE_LOGGING
-DMAIN_DISABLE_LOGGING
${common_env_data.build_unflags}
build_flags =
-Wl,-Map,output.map
${common_env_data.build_flags}
-DLOG_LEVEL=6
-DCORE_DEBUG_LEVEL=5
-DJTAG_DEBUG
-DESP32C3
#-DUSE_SERIAL_PINS # Use the TX/RX pins for the serial port
-DARDUINO_ESP32C3_DEV
-DCOLLECT_PERFDATA # Collect runtime data for a few defined methods to measure time, dumped to serial and/or influxdb
lib_deps =
${common_env_data.lib_deps}
${common_env_data.lib_deps32}
lib_ignore =
board = esp32-c3-devkitm-1
build_type = debug
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
# This is a version for the floaty hardware. No DSB18 sensor and no battery measurement.
[env:gravity32lite-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 =
-Wl,-Map,output.map
${common_env_data.build_flags}
-DLOG_LEVEL=5
-DCORE_DEBUG_LEVEL=0
-DESP32LITE
-DFLOATY
#-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 = lolin32_lite
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

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
requests

View File

@ -11,18 +11,67 @@ def after_build(source, target, env):
print( "Executing custom step " ) print( "Executing custom step " )
dir = env.GetLaunchDir() dir = env.GetLaunchDir()
name = env.get( "PIOENV" ) name = env.get( "PIOENV" )
source = dir + "/.pio/build/" + name + "/firmware.bin"
if name == "gravity-debug" : if name == "gravity-debug" :
target = dir + "/bin/firmware-debug.bin" target = dir + "/bin/firmware-debug.bin"
if name == "gravity-release" : source = dir + "/.pio/build/" + name + "/firmware.bin"
target = dir + "/bin/firmware.bin"
if name == "gravity-perf" :
target = dir + "/bin/firmware-perf.bin"
if name == "gravity32-perf" :
target = dir + "/bin/firmware32-perf.bin"
print( "Copy file : " + source + " -> " + target ) print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target ) shutil.copyfile( source, target )
if name == "gravity-release" :
target = dir + "/bin/firmware.bin"
source = dir + "/.pio/build/" + name + "/firmware.bin"
print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target )
if name == "gravity32-release" :
target = dir + "/bin/firmware32.bin"
source = dir + "/.pio/build/" + name + "/firmware.bin"
print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target )
target = dir + "/bin/partitions32.bin"
source = dir + "/.pio/build/" + name + "/partitions.bin"
print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target )
if name == "gravity32c3-release" :
target = dir + "/bin/firmware32c3.bin"
source = dir + "/.pio/build/" + name + "/firmware.bin"
print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target )
target = dir + "/bin/partitions32c3.bin"
source = dir + "/.pio/build/" + name + "/partitions.bin"
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"
print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target )
target = dir + "/bin/partitions32s2.bin"
source = dir + "/.pio/build/" + name + "/partitions.bin"
print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target )
if name == "gravity32lite-release" :
target = dir + "/bin/firmware32lite.bin"
source = dir + "/.pio/build/" + name + "/firmware.bin"
print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target )
target = dir + "/bin/partitions32lite.bin"
source = dir + "/.pio/build/" + name + "/partitions.bin"
print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target )
print( "Adding custom build step (copy firmware): ") print( "Adding custom build step (copy firmware): ")
env.AddPostAction("buildprog", after_build) env.AddPostAction("buildprog", after_build)

View File

@ -1,31 +0,0 @@
Import("env")
import shutil, os
print( "Executing custom step " )
dir = env.GetLaunchDir()
source = dir + "/html/"
target = dir + "/data/"
print( "Copy html-files from " + source + " -> " + target )
os.makedirs(os.path.dirname( target ), exist_ok=True)
file = "about.min.htm"
#print( "Copy file: " + source + file + "->" + target + file)
shutil.copyfile( source + file, target + file )
file = "calibration.min.htm"
#print( "Copy file: " + source + file + "->" + target + file)
shutil.copyfile( source + file, target + file )
file = "config.min.htm"
#print( "Copy file: " + source + file + "->" + target + file)
shutil.copyfile( source + file, target + file )
file = "device.min.htm"
#print( "Copy file: " + source + file + "->" + target + file)
shutil.copyfile( source + file, target + file )
file = "index.min.htm"
#print( "Copy file: " + source + file + "->" + target + file)
shutil.copyfile( source + file, target + file )
file = "upload.min.htm"
#print( "Copy file: " + source + file + "->" + target + file)
shutil.copyfile( source + file, target + file )
file = "format.min.htm"
#print( "Copy file: " + source + file + "->" + target + file)
shutil.copyfile( source + file, target + file )

View File

@ -12,49 +12,14 @@ def after_build(source, target, env):
dir = env.GetLaunchDir() dir = env.GetLaunchDir()
#name = env.get( "PIOENV" ) #name = env.get( "PIOENV" )
# Copy file 1
source = dir + "/data/index.min.htm"
target = dir + "/bin/index.min.htm"
print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target )
# Copy file 2
source = dir + "/data/device.min.htm"
target = dir + "/bin/device.min.htm"
print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target )
# Copy file 3
source = dir + "/data/config.min.htm"
target = dir + "/bin/config.min.htm"
print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target )
# Copy file 4
source = dir + "/data/about.min.htm"
target = dir + "/bin/about.min.htm"
print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target )
# Copy file 5
source = dir + "/data/calibration.min.htm"
target = dir + "/bin/calibration.min.htm"
print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target )
# Copy file 6
source = dir + "/data/format.min.htm"
target = dir + "/bin/format.min.htm"
print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target )
target = dir + "/bin/version.json" target = dir + "/bin/version.json"
ver = get_build_flag_value("CFG_APPVER") ver = get_build_flag_value("CFG_APPVER")
print( "Creating version.json" ) print( "Creating version.json" )
f = open( target, "w" ) f = open( target, "w" )
f.write( "{ \"project\":\"gravmon\", \"version\":" + ver + ", " ) f.write( "{ \"project\":\"gravmon\", \"version\":" + ver + ", " )
f.write( " \"html\": [ \"index.min.htm\", \"device.min.htm\", \"config.min.htm\", \"calibration.min.htm\", \"format.min.htm\", \"about.min.htm\" ] }" ) #f.write( " \"html\": [ \"index.min.htm\", \"config.min.htm\", \"calibration.min.htm\", \"test.min.htm\", \"format.min.htm\", \"about.min.htm\" ] }" )
f.write( " \"html\": [ ] }" )
f.close() f.close()

9
script/git_rev.py Normal file
View File

@ -0,0 +1,9 @@
import subprocess
revision = (
subprocess.check_output(["git", "rev-parse", "HEAD"])
.strip()
.decode("utf-8")
)
revision = revision[-6:]
print("-D CFG_GITREV='\"..%s\"'" % revision)

108
src/ble.cpp Normal file
View File

@ -0,0 +1,108 @@
/*
MIT License
Copyright (c) 2021-22 Magnus
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#if defined(ESP32) && !defined(ESP32S2)
#include <ble.hpp>
#include <string>
// Tilt UUID variants and data format, based on tilt-sim
//
// https://github.com/spouliot/tilt-sim
//
// Tilt data format is described here. Only SG and Temp is transmitted over BLE.
// https://kvurd.com/blog/tilt-hydrometer-ibeacon-data-format/
//
// Create ble sender
//
BleSender::BleSender(const char* color) {
BLEDevice::init("");
// boost power to maximum, these might be changed once battery life using BLE
// has been tested.
#if defined(ESP32C3) && defined(REDUCE_WIFI_POWER)
esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, ESP_PWR_LVL_P6);
esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P6);
esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_SCAN, ESP_PWR_LVL_P6);
#else
esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, ESP_PWR_LVL_P9);
esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P9);
esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_SCAN, ESP_PWR_LVL_P9);
#endif
_advertising = BLEDevice::getAdvertising();
_color = color;
if (!_color.compareTo("red"))
_uuid = BLEUUID::fromString("A495BB10-C5B1-4B44-B512-1370F02D74DE");
else if (!_color.compareTo("green"))
_uuid = BLEUUID::fromString("A495BB20-C5B1-4B44-B512-1370F02D74DE");
else if (!_color.compareTo("black"))
_uuid = BLEUUID::fromString("A495BB30-C5B1-4B44-B512-1370F02D74DE");
else if (!_color.compareTo("purple"))
_uuid = BLEUUID::fromString("A495BB40-C5B1-4B44-B512-1370F02D74DE");
else if (!_color.compareTo("orange"))
_uuid = BLEUUID::fromString("A495BB50-C5B1-4B44-B512-1370F02D74DE");
else if (!_color.compareTo("blue"))
_uuid = BLEUUID::fromString("A495BB60-C5B1-4B44-B512-1370F02D74DE");
else if (!_color.compareTo("yellow"))
_uuid = BLEUUID::fromString("A495BB70-C5B1-4B44-B512-1370F02D74DE");
else // if (_color.compareTo("pink"))
_uuid = BLEUUID::fromString("A495BB80-C5B1-4B44-B512-1370F02D74DE");
}
//
// Send temp and gravity via BLE
//
void BleSender::sendData(float tempF, float gravSG) {
uint16_t gravity = gravSG * 1000; // SG * 1000 or SG * 10000 for Tilt Pro/HD
uint16_t temperature = tempF; // Deg F _or_ Deg F * 10 for Tilt Pro/HD
BLEBeacon oBeacon = BLEBeacon();
oBeacon.setManufacturerId(
0x4C00); // fake Apple 0x004C LSB (ENDIAN_CHANGE_U16!)
oBeacon.setProximityUUID(_uuid);
oBeacon.setMajor(temperature);
oBeacon.setMinor(gravity);
std::string strServiceData = "";
strServiceData += static_cast<char>(26); // Len
strServiceData += static_cast<char>(0xFF); // Type
strServiceData += oBeacon.getData();
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
oAdvertisementData.setFlags(0x04); // BR_EDR_NOT_SUPPORTED 0x04
oAdvertisementData.addData(strServiceData);
BLEAdvertisementData oScanResponseData = BLEAdvertisementData();
_advertising->setAdvertisementData(oAdvertisementData);
_advertising->setScanResponseData(oScanResponseData);
_advertising->setAdvertisementType(BLE_GAP_CONN_MODE_NON);
_advertising->start();
delay(100);
_advertising->stop();
delay(100);
}
#endif // ESP32 && !ESP32S2

45
src/ble.hpp Normal file
View File

@ -0,0 +1,45 @@
/*
MIT License
Copyright (c) 2021-22 Magnus
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef SRC_BLE_HPP_
#define SRC_BLE_HPP_
#if defined(ESP32) && !defined(ESP32S2)
#include <Arduino.h>
#include <NimBLEBeacon.h>
#include <NimBLEDevice.h>
class BleSender {
private:
BLEAdvertising* _advertising;
String _color;
BLEUUID _uuid;
public:
explicit BleSender(const char* color);
void sendData(float tempF, float gravSG);
};
#endif // ESP32 && !ESP32S2
#endif // SRC_BLE_HPP_

View File

@ -27,20 +27,20 @@ SOFTWARE.
#include <calc.hpp> #include <calc.hpp>
#include <main.hpp> #include <main.hpp>
//
// Use values to derive a formula
//
int createFormula(RawFormulaData &fd, char *formulaBuffer, int createFormula(RawFormulaData &fd, char *formulaBuffer,
int formulaBufferSize, int order) { int formulaBufferSize, int order) {
int noAngles = 0; int noAngles = 0;
RawFormulaData fd2;
// Check how many valid values we have got // Check how many valid values we have got and make sure we have a full
if (fd.a[0] > 0 && fd.a[1] > 0 && fd.a[2] > 0 && fd.a[3] > 0 && fd.a[4] > 0) // series.
noAngles = 5; for (int i = 0; i < FORMULA_DATA_SIZE; i++) {
else if (fd.a[0] > 0 && fd.a[1] > 0 && fd.a[2] > 0 && fd.a[3] > 0) if (fd.a[i]) {
noAngles = 4; fd2.a[noAngles] = fd.a[i];
else if (fd.a[0] > 0 && fd.a[1] > 0 && fd.a[2] > 0) fd2.g[noAngles] = fd.g[i];
noAngles = 3; noAngles++;
}
}
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)
Log.verbose( Log.verbose(
@ -48,18 +48,19 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer,
order, noAngles); order, noAngles);
#endif #endif
if (!noAngles) { if (noAngles < 3) {
Log.error(F("CALC: Not enough values for deriving formula" CR)); writeErrorLog("CALC: Not enough values for deriving formula");
return ERR_FORMULA_NOTENOUGHVALUES; return ERR_FORMULA_NOTENOUGHVALUES;
} else { } else {
double coeffs[order + 1]; double coeffs[order + 1];
int ret = fitCurve(order, noAngles, fd.a, fd.g, int ret = fitCurve(order, noAngles, fd2.a, fd2.g,
sizeof(coeffs) / sizeof(double), coeffs); sizeof(coeffs) / sizeof(double), coeffs);
// Returned value is 0 if no error // Returned value is 0 if no error
if (ret == 0) { if (ret == 0) {
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)
Log.verbose(F("CALC: Finshied processing data points." CR)); Log.verbose(F("CALC: Finshied processing data points, order = %d." CR),
order);
#endif #endif
// Print the formula based on 'order' // Print the formula based on 'order'
@ -92,18 +93,16 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer,
double dev = (g - fd.g[i]) < 0 ? (fd.g[i] - g) : (g - fd.g[i]); double dev = (g - fd.g[i]) < 0 ? (fd.g[i] - g) : (g - fd.g[i]);
// If the deviation is more than 2 degress we mark it as failed. // If the deviation is more than 2 degress we mark it as failed.
if (dev * 1000 > myHardwareConfig.getMaxFormulaCreationDeviation()) { if (dev * 1000 > myAdvancedConfig.getMaxFormulaCreationDeviation()) {
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) writeErrorLog(
char s[20]; "CALC: Validation failed on angle %.2f, deviation too large %.4f "
snprintf(&s[0], sizeof(s), "%.8f", dev); "SG, formula order %d",
Log.verbose(F("CALC: Deviation is: %s" CR), &s[0]); fd.a[i], dev * 1000, order);
#endif
valid = false; valid = false;
} }
} }
if (!valid) { if (!valid) {
Log.error(F("CALC: Deviation to large, formula rejected." CR));
return ERR_FORMULA_UNABLETOFFIND; return ERR_FORMULA_UNABLETOFFIND;
} }
@ -112,14 +111,10 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer,
} }
} }
Log.error(F("CALC: Internal error finding formula." CR)); writeErrorLog("CALC: Internal error finding formula.");
return ERR_FORMULA_INTERNAL; return ERR_FORMULA_INTERNAL;
} }
//
// Calculates gravity according to supplied formula, compatible with
// iSpindle/Fermentrack formula
//
double calculateGravity(double angle, double temp, const char *tempFormula) { double calculateGravity(double angle, double temp, const char *tempFormula) {
const char *formula = myConfig.getGravityFormula(); const char *formula = myConfig.getGravityFormula();
@ -157,26 +152,23 @@ double calculateGravity(double angle, double temp, const char *tempFormula) {
return g; return g;
} }
Log.error(F("CALC: Failed to parse expression %d." CR), err); writeErrorLog("CALC: Failed to parse gravity expression %d", err);
return 0; return 0;
} }
//
// Do a standard gravity temperature correction. This is a simple way to adjust // Do a standard gravity temperature correction. This is a simple way to adjust
// for differnt worth temperatures. This function uses C as temperature. // for differnt worth temperatures. This function uses C as temperature.
// //
// Source: https://homebrewacademy.com/hydrometer-temperature-correction/ // Source: https://homebrewacademy.com/hydrometer-temperature-correction/
// double gravityTemperatureCorrectionC(double gravitySG, double tempC,
double gravityTemperatureCorrectionC(double gravity, double tempC,
double calTempC) { double calTempC) {
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)
Log.verbose(F("CALC: Adjusting gravity based on temperature, gravity %F, " Log.verbose(F("CALC: Adjusting gravity based on temperature, gravity %F, "
"temp %F, calTemp %F." CR), "temp %F, calTemp %F." CR),
gravity, tempC, calTempC); gravitySG, tempC, calTempC);
#endif #endif
// float tempF = convertCtoF(tempC); double tempF = convertCtoF(tempC);
// float calTempF = convertCtoF(calTempC); double calTempF = convertCtoF(calTempC);
const char *formula = const char *formula =
"gravity*((1.00130346-0.000134722124*temp+0.00000204052596*temp^2-0." "gravity*((1.00130346-0.000134722124*temp+0.00000204052596*temp^2-0."
"00000000232820948*temp^3)/" "00000000232820948*temp^3)/"
@ -185,7 +177,7 @@ double gravityTemperatureCorrectionC(double gravity, double tempC,
// Store variable names and pointers. // Store variable names and pointers.
te_variable vars[] = { te_variable vars[] = {
{"gravity", &gravity}, {"temp", &tempC}, {"cal", &calTempC}}; {"gravity", &gravitySG}, {"temp", &tempF}, {"cal", &calTempF}};
int err; int err;
// Compile the expression with variables. // Compile the expression with variables.
@ -196,17 +188,18 @@ double gravityTemperatureCorrectionC(double gravity, double tempC,
te_free(expr); te_free(expr);
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)
char s[10]; char s[80];
snprintf(&s[0], sizeof(s), "%.8f", g); snprintf(&s[0], sizeof(s), "Corrected gravity=%.8f, input gravity=%.8f", g,
Log.verbose(F("CALC: Corrected gravity is %s." CR), &s[0]); gravitySG);
Log.verbose(F("CALC: %s." CR), &s[0]);
#endif #endif
return g; return g;
} }
Log.error( writeErrorLog(
F("CALC: Failed to parse expression %d, no correction has been made." CR), "CALC: Failed to parse expression for gravity temperature correction %d",
err); err);
return gravity; return gravitySG;
} }
// EOF // EOF

View File

@ -32,9 +32,8 @@ SOFTWARE.
double calculateGravity(double angle, double tempC, double calculateGravity(double angle, double tempC,
const char *tempFormula = 0); const char *tempFormula = 0);
double gravityTemperatureCorrectionC( double gravityTemperatureCorrectionC(double gravity, double tempC,
double gravity, double tempC, double calTempC);
double calTempC = myHardwareConfig.getDefaultCalibrationTemp());
int createFormula(RawFormulaData &fd, char *formulaBuffer, int createFormula(RawFormulaData &fd, char *formulaBuffer,
int formulaBufferSize, int order); int formulaBufferSize, int order);

View File

@ -26,7 +26,7 @@ SOFTWARE.
#include <wifi.hpp> #include <wifi.hpp>
Config myConfig; Config myConfig;
HardwareConfig myHardwareConfig; AdvancedConfig myAdvancedConfig;
// //
// Create the config class with default settings. // Create the config class with default settings.
@ -34,14 +34,14 @@ HardwareConfig myHardwareConfig;
Config::Config() { Config::Config() {
// Assiging default values // Assiging default values
char buf[30]; char buf[30];
#if defined (ESP8266) #if defined(ESP8266)
snprintf(&buf[0], sizeof(buf), "%6x", (unsigned int)ESP.getChipId()); snprintf(&buf[0], sizeof(buf), "%06x", (unsigned int)ESP.getChipId());
#else // defined (ESP32) #else // defined (ESP32)
uint32_t chipId = 0; uint32_t chipId = 0;
for (int i = 0; i < 17; i = i+8) { for (int i = 0; i < 17; i = i + 8) {
chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i; chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
} }
snprintf(&buf[0], sizeof(buf), "%6x", chipId); snprintf(&buf[0], sizeof(buf), "%06x", chipId);
#endif #endif
_id = String(&buf[0]); _id = String(&buf[0]);
snprintf(&buf[0], sizeof(buf), "" WIFI_MDNS "%s", getID()); snprintf(&buf[0], sizeof(buf), "" WIFI_MDNS "%s", getID());
@ -51,22 +51,6 @@ Config::Config() {
Log.verbose(F("CFG : Created config for %s (%s)." CR), _id.c_str(), Log.verbose(F("CFG : Created config for %s (%s)." CR), _id.c_str(),
_mDNS.c_str()); _mDNS.c_str());
#endif #endif
setTempFormat('C');
setGravityFormat('G');
setSleepInterval(900); // 15 minutes
#if defined (ESP8266)
setVoltageFactor(1.59); // Conversion factor for battery on ESP8266
#else // defined (ESP32)
setVoltageFactor(1.43); // Conversion factor for battery on ESP32
#endif
setTempSensorAdjC(0.0);
setGravityTempAdj(false);
_gyroCalibration = {0, 0, 0, 0, 0, 0};
_formulaData = {{0, 0, 0, 0, 0}, {1, 1, 1, 1, 1}};
_gyroTemp = false;
_saveNeeded = false;
_mqttPort = 1883;
} }
// //
@ -75,14 +59,24 @@ Config::Config() {
// //
void Config::createJson(DynamicJsonDocument& doc) { void Config::createJson(DynamicJsonDocument& doc) {
doc[PARAM_MDNS] = getMDNS(); doc[PARAM_MDNS] = getMDNS();
// doc[PARAM_CONFIG_VER] = getConfigVersion();
doc[PARAM_ID] = getID(); doc[PARAM_ID] = getID();
doc[PARAM_OTA] = getOtaURL(); doc[PARAM_OTA] = getOtaURL();
doc[PARAM_SSID] = getWifiSSID(); doc[PARAM_SSID] = getWifiSSID(0);
doc[PARAM_PASS] = getWifiPass(); doc[PARAM_PASS] = getWifiPass(0);
doc[PARAM_SSID2] = getWifiSSID(1);
doc[PARAM_PASS2] = getWifiPass(1);
doc[PARAM_BLE] = getColorBLE();
doc[PARAM_TEMPFORMAT] = String(getTempFormat()); doc[PARAM_TEMPFORMAT] = String(getTempFormat());
doc[PARAM_PUSH_BREWFATHER] = getBrewfatherPushUrl(); doc[PARAM_TOKEN] = getToken();
doc[PARAM_PUSH_HTTP] = getHttpPushUrl(); doc[PARAM_TOKEN2] = getToken2();
doc[PARAM_PUSH_HTTP2] = getHttpPushUrl2(); doc[PARAM_PUSH_HTTP] = getHttpUrl();
doc[PARAM_PUSH_HTTP_H1] = getHttpHeader(0);
doc[PARAM_PUSH_HTTP_H2] = getHttpHeader(1);
doc[PARAM_PUSH_HTTP2] = getHttp2Url();
doc[PARAM_PUSH_HTTP2_H1] = getHttp2Header(0);
doc[PARAM_PUSH_HTTP2_H2] = getHttp2Header(1);
doc[PARAM_PUSH_HTTP3] = getHttp3Url();
doc[PARAM_PUSH_INFLUXDB2] = getInfluxDb2PushUrl(); doc[PARAM_PUSH_INFLUXDB2] = getInfluxDb2PushUrl();
doc[PARAM_PUSH_INFLUXDB2_ORG] = getInfluxDb2PushOrg(); doc[PARAM_PUSH_INFLUXDB2_ORG] = getInfluxDb2PushOrg();
doc[PARAM_PUSH_INFLUXDB2_BUCKET] = getInfluxDb2PushBucket(); doc[PARAM_PUSH_INFLUXDB2_BUCKET] = getInfluxDb2PushBucket();
@ -92,12 +86,14 @@ void Config::createJson(DynamicJsonDocument& doc) {
doc[PARAM_PUSH_MQTT_USER] = getMqttUser(); doc[PARAM_PUSH_MQTT_USER] = getMqttUser();
doc[PARAM_PUSH_MQTT_PASS] = getMqttPass(); doc[PARAM_PUSH_MQTT_PASS] = getMqttPass();
doc[PARAM_SLEEP_INTERVAL] = getSleepInterval(); doc[PARAM_SLEEP_INTERVAL] = getSleepInterval();
doc[PARAM_VOLTAGEFACTOR] = getVoltageFactor(); doc[PARAM_VOLTAGE_FACTOR] = getVoltageFactor();
doc[PARAM_VOLTAGE_CONFIG] = getVoltageConfig();
doc[PARAM_GRAVITY_FORMULA] = getGravityFormula(); doc[PARAM_GRAVITY_FORMULA] = getGravityFormula();
doc[PARAM_GRAVITY_FORMAT] = String(getGravityFormat()); doc[PARAM_GRAVITY_FORMAT] = String(getGravityFormat());
doc[PARAM_TEMP_ADJ] = getTempSensorAdjC(); doc[PARAM_TEMP_ADJ] = getTempSensorAdjC();
doc[PARAM_GRAVITY_TEMP_ADJ] = isGravityTempAdj(); doc[PARAM_GRAVITY_TEMP_ADJ] = isGravityTempAdj();
doc[PARAM_GYRO_TEMP] = isGyroTemp(); doc[PARAM_GYRO_TEMP] = isGyroTemp();
doc[PARAM_STORAGE_SLEEP] = isStorageSleep();
JsonObject cal = doc.createNestedObject(PARAM_GYRO_CALIBRATION); JsonObject cal = doc.createNestedObject(PARAM_GYRO_CALIBRATION);
cal["ax"] = _gyroCalibration.ax; cal["ax"] = _gyroCalibration.ax;
@ -113,12 +109,22 @@ void Config::createJson(DynamicJsonDocument& doc) {
cal2["a3"] = reduceFloatPrecision(_formulaData.a[2], 2); cal2["a3"] = reduceFloatPrecision(_formulaData.a[2], 2);
cal2["a4"] = reduceFloatPrecision(_formulaData.a[3], 2); cal2["a4"] = reduceFloatPrecision(_formulaData.a[3], 2);
cal2["a5"] = reduceFloatPrecision(_formulaData.a[4], 2); cal2["a5"] = reduceFloatPrecision(_formulaData.a[4], 2);
cal2["a6"] = reduceFloatPrecision(_formulaData.a[5], 2);
cal2["a7"] = reduceFloatPrecision(_formulaData.a[6], 2);
cal2["a8"] = reduceFloatPrecision(_formulaData.a[7], 2);
cal2["a9"] = reduceFloatPrecision(_formulaData.a[8], 2);
cal2["a10"] = reduceFloatPrecision(_formulaData.a[9], 2);
cal2["g1"] = reduceFloatPrecision(_formulaData.g[0], 4); cal2["g1"] = reduceFloatPrecision(_formulaData.g[0], 4);
cal2["g2"] = reduceFloatPrecision(_formulaData.g[1], 4); cal2["g2"] = reduceFloatPrecision(_formulaData.g[1], 4);
cal2["g3"] = reduceFloatPrecision(_formulaData.g[2], 4); cal2["g3"] = reduceFloatPrecision(_formulaData.g[2], 4);
cal2["g4"] = reduceFloatPrecision(_formulaData.g[3], 4); cal2["g4"] = reduceFloatPrecision(_formulaData.g[3], 4);
cal2["g5"] = reduceFloatPrecision(_formulaData.g[4], 4); cal2["g5"] = reduceFloatPrecision(_formulaData.g[4], 4);
cal2["g6"] = reduceFloatPrecision(_formulaData.g[5], 4);
cal2["g7"] = reduceFloatPrecision(_formulaData.g[6], 4);
cal2["g8"] = reduceFloatPrecision(_formulaData.g[7], 4);
cal2["g9"] = reduceFloatPrecision(_formulaData.g[8], 4);
cal2["g10"] = reduceFloatPrecision(_formulaData.g[9], 4);
} }
// //
@ -139,16 +145,16 @@ bool Config::saveFile() {
File configFile = LittleFS.open(CFG_FILENAME, "w"); File configFile = LittleFS.open(CFG_FILENAME, "w");
if (!configFile) { if (!configFile) {
Log.error(F("CFG : Failed to open file " CFG_FILENAME " for save." CR)); writeErrorLog("CFG : Failed to save configuration.");
return false; return false;
} }
DynamicJsonDocument doc(CFG_JSON_BUFSIZE); DynamicJsonDocument doc(3000);
createJson(doc); createJson(doc);
#if LOG_LEVEL == 6 && !defined(DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(DISABLE_LOGGING)
serializeJson(doc, Serial); serializeJson(doc, EspSerial);
Serial.print(CR); EspSerial.print(CR);
#endif #endif
serializeJson(doc, configFile); serializeJson(doc, configFile);
@ -156,7 +162,6 @@ bool Config::saveFile() {
configFile.close(); configFile.close();
_saveNeeded = false; _saveNeeded = false;
myConfig.debug();
Log.notice(F("CFG : Configuration saved to " CFG_FILENAME "." CR)); Log.notice(F("CFG : Configuration saved to " CFG_FILENAME "." CR));
return true; return true;
} }
@ -170,32 +175,30 @@ bool Config::loadFile() {
#endif #endif
if (!LittleFS.exists(CFG_FILENAME)) { if (!LittleFS.exists(CFG_FILENAME)) {
Log.error( writeErrorLog("CFG : Configuration file does not exist.");
F("CFG : Configuration file does not exist " CFG_FILENAME "." CR));
return false; return false;
} }
File configFile = LittleFS.open(CFG_FILENAME, "r"); File configFile = LittleFS.open(CFG_FILENAME, "r");
if (!configFile) { if (!configFile) {
Log.error(F("CFG : Failed to open " CFG_FILENAME "." CR)); writeErrorLog("CFG : Failed to load configuration.");
return false; return false;
} }
Log.notice(F("CFG : Size of configuration file=%d bytes." CR), Log.notice(F("CFG : Size of configuration file=%d bytes." CR),
configFile.size()); configFile.size());
DynamicJsonDocument doc(CFG_JSON_BUFSIZE); DynamicJsonDocument doc(3000);
DeserializationError err = deserializeJson(doc, configFile); DeserializationError err = deserializeJson(doc, configFile);
#if LOG_LEVEL == 6 #if LOG_LEVEL == 6
serializeJson(doc, Serial); serializeJson(doc, EspSerial);
Serial.print(CR); EspSerial.print(CR);
#endif #endif
configFile.close(); configFile.close();
if (err) { if (err) {
Log.error(F("CFG : Failed to parse " CFG_FILENAME " file, Err: %s, %d." CR), writeErrorLog("CFG : Failed to parse configuration (json)");
err.c_str(), doc.capacity());
return false; return false;
} }
@ -204,19 +207,30 @@ bool Config::loadFile() {
#endif #endif
if (!doc[PARAM_OTA].isNull()) setOtaURL(doc[PARAM_OTA]); if (!doc[PARAM_OTA].isNull()) setOtaURL(doc[PARAM_OTA]);
if (!doc[PARAM_MDNS].isNull()) setMDNS(doc[PARAM_MDNS]); if (!doc[PARAM_MDNS].isNull()) setMDNS(doc[PARAM_MDNS]);
if (!doc[PARAM_SSID].isNull()) setWifiSSID(doc[PARAM_SSID]); if (!doc[PARAM_SSID].isNull()) setWifiSSID(doc[PARAM_SSID], 0);
if (!doc[PARAM_PASS].isNull()) setWifiPass(doc[PARAM_PASS]); if (!doc[PARAM_PASS].isNull()) setWifiPass(doc[PARAM_PASS], 0);
if (!doc[PARAM_SSID2].isNull()) setWifiSSID(doc[PARAM_SSID2], 1);
if (!doc[PARAM_PASS2].isNull()) setWifiPass(doc[PARAM_PASS2], 1);
if (!doc[PARAM_BLE].isNull()) setColorBLE(doc[PARAM_BLE]);
if (!doc[PARAM_TEMPFORMAT].isNull()) { if (!doc[PARAM_TEMPFORMAT].isNull()) {
String s = doc[PARAM_TEMPFORMAT]; String s = doc[PARAM_TEMPFORMAT];
setTempFormat(s.charAt(0)); setTempFormat(s.charAt(0));
} }
if (!doc[PARAM_PUSH_BREWFATHER].isNull()) if (!doc[PARAM_TOKEN].isNull()) setToken(doc[PARAM_TOKEN]);
setBrewfatherPushUrl(doc[PARAM_PUSH_BREWFATHER]); if (!doc[PARAM_TOKEN2].isNull()) setToken2(doc[PARAM_TOKEN2]);
if (!doc[PARAM_PUSH_HTTP].isNull()) setHttpUrl(doc[PARAM_PUSH_HTTP]);
if (!doc[PARAM_PUSH_HTTP].isNull()) setHttpPushUrl(doc[PARAM_PUSH_HTTP]); if (!doc[PARAM_PUSH_HTTP_H1].isNull())
if (!doc[PARAM_PUSH_HTTP2].isNull()) setHttpPushUrl2(doc[PARAM_PUSH_HTTP2]); setHttpHeader(doc[PARAM_PUSH_HTTP_H1], 0);
if (!doc[PARAM_PUSH_HTTP_H2].isNull())
setHttpHeader(doc[PARAM_PUSH_HTTP_H2], 1);
if (!doc[PARAM_PUSH_HTTP2].isNull()) setHttp2Url(doc[PARAM_PUSH_HTTP2]);
if (!doc[PARAM_PUSH_HTTP2_H1].isNull())
setHttp2Header(doc[PARAM_PUSH_HTTP2_H1], 0);
if (!doc[PARAM_PUSH_HTTP2_H2].isNull())
setHttp2Header(doc[PARAM_PUSH_HTTP2_H2], 1);
if (!doc[PARAM_PUSH_HTTP3].isNull()) setHttp3Url(doc[PARAM_PUSH_HTTP3]);
if (!doc[PARAM_PUSH_INFLUXDB2].isNull()) if (!doc[PARAM_PUSH_INFLUXDB2].isNull())
setInfluxDb2PushUrl(doc[PARAM_PUSH_INFLUXDB2]); setInfluxDb2PushUrl(doc[PARAM_PUSH_INFLUXDB2]);
@ -237,14 +251,18 @@ bool Config::loadFile() {
if (!doc[PARAM_SLEEP_INTERVAL].isNull()) if (!doc[PARAM_SLEEP_INTERVAL].isNull())
setSleepInterval(doc[PARAM_SLEEP_INTERVAL].as<int>()); setSleepInterval(doc[PARAM_SLEEP_INTERVAL].as<int>());
if (!doc[PARAM_VOLTAGEFACTOR].isNull()) if (!doc[PARAM_VOLTAGE_FACTOR].isNull())
setVoltageFactor(doc[PARAM_VOLTAGEFACTOR].as<float>()); setVoltageFactor(doc[PARAM_VOLTAGE_FACTOR].as<float>());
if (!doc[PARAM_VOLTAGE_CONFIG].isNull())
setVoltageConfig(doc[PARAM_VOLTAGE_CONFIG].as<float>());
if (!doc[PARAM_GRAVITY_FORMULA].isNull()) if (!doc[PARAM_GRAVITY_FORMULA].isNull())
setGravityFormula(doc[PARAM_GRAVITY_FORMULA]); setGravityFormula(doc[PARAM_GRAVITY_FORMULA]);
if (!doc[PARAM_GRAVITY_TEMP_ADJ].isNull()) if (!doc[PARAM_GRAVITY_TEMP_ADJ].isNull())
setGravityTempAdj(doc[PARAM_GRAVITY_TEMP_ADJ].as<bool>()); setGravityTempAdj(doc[PARAM_GRAVITY_TEMP_ADJ].as<bool>());
if (!doc[PARAM_GYRO_TEMP].isNull()) if (!doc[PARAM_GYRO_TEMP].isNull())
setGyroTemp(doc[PARAM_GYRO_TEMP].as<bool>()); setGyroTemp(doc[PARAM_GYRO_TEMP].as<bool>());
if (!doc[PARAM_STORAGE_SLEEP].isNull())
setStorageSleep(doc[PARAM_STORAGE_SLEEP].as<bool>());
if (!doc[PARAM_GRAVITY_FORMAT].isNull()) { if (!doc[PARAM_GRAVITY_FORMAT].isNull()) {
String s = doc[PARAM_GRAVITY_FORMAT]; String s = doc[PARAM_GRAVITY_FORMAT];
setGravityFormat(s.charAt(0)); setGravityFormat(s.charAt(0));
@ -275,6 +293,16 @@ bool Config::loadFile() {
_formulaData.a[3] = doc[PARAM_FORMULA_DATA]["a4"].as<double>(); _formulaData.a[3] = doc[PARAM_FORMULA_DATA]["a4"].as<double>();
if (!doc[PARAM_FORMULA_DATA]["a5"].isNull()) if (!doc[PARAM_FORMULA_DATA]["a5"].isNull())
_formulaData.a[4] = doc[PARAM_FORMULA_DATA]["a5"].as<double>(); _formulaData.a[4] = doc[PARAM_FORMULA_DATA]["a5"].as<double>();
if (!doc[PARAM_FORMULA_DATA]["a6"].isNull())
_formulaData.a[5] = doc[PARAM_FORMULA_DATA]["a6"].as<double>();
if (!doc[PARAM_FORMULA_DATA]["a7"].isNull())
_formulaData.a[6] = doc[PARAM_FORMULA_DATA]["a7"].as<double>();
if (!doc[PARAM_FORMULA_DATA]["a8"].isNull())
_formulaData.a[7] = doc[PARAM_FORMULA_DATA]["a8"].as<double>();
if (!doc[PARAM_FORMULA_DATA]["a9"].isNull())
_formulaData.a[8] = doc[PARAM_FORMULA_DATA]["a9"].as<double>();
if (!doc[PARAM_FORMULA_DATA]["a10"].isNull())
_formulaData.a[9] = doc[PARAM_FORMULA_DATA]["a10"].as<double>();
if (!doc[PARAM_FORMULA_DATA]["g1"].isNull()) if (!doc[PARAM_FORMULA_DATA]["g1"].isNull())
_formulaData.g[0] = doc[PARAM_FORMULA_DATA]["g1"].as<double>(); _formulaData.g[0] = doc[PARAM_FORMULA_DATA]["g1"].as<double>();
@ -286,8 +314,25 @@ bool Config::loadFile() {
_formulaData.g[3] = doc[PARAM_FORMULA_DATA]["g4"].as<double>(); _formulaData.g[3] = doc[PARAM_FORMULA_DATA]["g4"].as<double>();
if (!doc[PARAM_FORMULA_DATA]["g5"].isNull()) if (!doc[PARAM_FORMULA_DATA]["g5"].isNull())
_formulaData.g[4] = doc[PARAM_FORMULA_DATA]["g5"].as<double>(); _formulaData.g[4] = doc[PARAM_FORMULA_DATA]["g5"].as<double>();
if (!doc[PARAM_FORMULA_DATA]["g6"].isNull())
_formulaData.g[5] = doc[PARAM_FORMULA_DATA]["g6"].as<double>();
if (!doc[PARAM_FORMULA_DATA]["g7"].isNull())
_formulaData.g[6] = doc[PARAM_FORMULA_DATA]["g7"].as<double>();
if (!doc[PARAM_FORMULA_DATA]["g8"].isNull())
_formulaData.g[7] = doc[PARAM_FORMULA_DATA]["g8"].as<double>();
if (!doc[PARAM_FORMULA_DATA]["g9"].isNull())
_formulaData.g[8] = doc[PARAM_FORMULA_DATA]["g9"].as<double>();
if (!doc[PARAM_FORMULA_DATA]["g10"].isNull())
_formulaData.g[9] = doc[PARAM_FORMULA_DATA]["g10"].as<double>();
/*if( doc[PARAM_CONFIG_VER].isNull() ) {
// If this parameter is missing we need to reset the gyrocalibaration due to
bug #29 _gyroCalibration.ax = _gyroCalibration.ay = _gyroCalibration.az = 0;
_gyroCalibration.gx = _gyroCalibration.gy = _gyroCalibration.gz = 0;
Log.warning(F("CFG : Old configuration format, clearing gyro calibration."
CR));
}*/
myConfig.debug();
_saveNeeded = false; // Reset save flag _saveNeeded = false; // Reset save flag
Log.notice(F("CFG : Configuration file " CFG_FILENAME " loaded." CR)); Log.notice(F("CFG : Configuration file " CFG_FILENAME " loaded." CR));
return true; return true;
@ -317,38 +362,10 @@ void Config::checkFileSystem() {
} }
} }
//
// Dump the configuration to the serial port
//
void Config::debug() {
#if LOG_LEVEL == 6 && !defined(DISABLE_LOGGING)
Log.verbose(F("CFG : Dumping configration " CFG_FILENAME "." CR));
Log.verbose(F("CFG : ID; '%s'." CR), getID());
Log.verbose(F("CFG : WIFI; '%s', '%s'." CR), getWifiSSID(), getWifiPass());
Log.verbose(F("CFG : mDNS; '%s'." CR), getMDNS());
Log.verbose(F("CFG : Sleep interval; %d." CR), getSleepInterval());
Log.verbose(F("CFG : OTA; '%s'." CR), getOtaURL());
Log.verbose(F("CFG : Temp Format; %c." CR), getTempFormat());
Log.verbose(F("CFG : Temp Adj; %F." CR), getTempSensorAdjC());
Log.verbose(F("CFG : VoltageFactor; %F." CR), getVoltageFactor());
Log.verbose(F("CFG : Gravity formula; '%s'." CR), getGravityFormula());
Log.verbose(F("CFG : Gravity format; '%c'." CR), getGravityFormat());
Log.verbose(F("CFG : Gravity temp adj; %s." CR),
isGravityTempAdj() ? "true" : "false");
Log.verbose(F("CFG : Gyro temp; %s." CR), isGyroTemp() ? "true" : "false");
Log.verbose(F("CFG : Push brewfather; '%s'." CR), getBrewfatherPushUrl());
Log.verbose(F("CFG : Push http; '%s'." CR), getHttpPushUrl());
Log.verbose(F("CFG : Push http2; '%s'." CR), getHttpPushUrl2());
Log.verbose(F("CFG : InfluxDb2; '%s', '%s', '%s', '%s'." CR),
getInfluxDb2PushUrl(), getInfluxDb2PushOrg(),
getInfluxDb2PushBucket(), getInfluxDb2PushToken());
#endif
}
// //
// Save json document to file // Save json document to file
// //
bool HardwareConfig::saveFile() { bool AdvancedConfig::saveFile() {
#if LOG_LEVEL == 6 && !defined(DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(DISABLE_LOGGING)
Log.verbose(F("CFG : Saving hardware configuration to file." CR)); Log.verbose(F("CFG : Saving hardware configuration to file." CR));
#endif #endif
@ -356,22 +373,30 @@ bool HardwareConfig::saveFile() {
File configFile = LittleFS.open(CFG_HW_FILENAME, "w"); File configFile = LittleFS.open(CFG_HW_FILENAME, "w");
if (!configFile) { if (!configFile) {
Log.error(F("CFG : Failed to open file " CFG_HW_FILENAME " for save." CR)); writeErrorLog("CFG : Failed to write hardware configuration ");
return false; return false;
} }
DynamicJsonDocument doc(512); DynamicJsonDocument doc(512);
doc[PARAM_HW_GYRO_READ_COUNT] = this->getGyroReadCount(); doc[PARAM_HW_GYRO_READ_COUNT] = this->getGyroReadCount();
doc[PARAM_HW_GYRO_READ_DELAY] = this->getGyroReadDelay(); // doc[PARAM_HW_GYRO_READ_DELAY] = this->getGyroReadDelay();
doc[PARAM_HW_GYRO_MOVING_THREASHOLD] = this->getGyroSensorMovingThreashold(); doc[PARAM_HW_GYRO_MOVING_THREASHOLD] = this->getGyroSensorMovingThreashold();
doc[PARAM_HW_FORMULA_DEVIATION] = this->getMaxFormulaCreationDeviation(); doc[PARAM_HW_FORMULA_DEVIATION] = this->getMaxFormulaCreationDeviation();
doc[PARAM_HW_WIFI_PORTALTIMEOUT] = this->getWifiPortalTimeout(); doc[PARAM_HW_WIFI_PORTAL_TIMEOUT] = this->getWifiPortalTimeout();
doc[PARAM_HW_WIFI_CONNECT_TIMEOUT] = this->getWifiConnectTimeout();
doc[PARAM_HW_FORMULA_CALIBRATION_TEMP] = this->getDefaultCalibrationTemp(); doc[PARAM_HW_FORMULA_CALIBRATION_TEMP] = this->getDefaultCalibrationTemp();
doc[PARAM_HW_PUSH_INTERVAL_HTTP1] = this->getPushIntervalHttp1();
doc[PARAM_HW_PUSH_INTERVAL_HTTP2] = this->getPushIntervalHttp2();
doc[PARAM_HW_PUSH_INTERVAL_HTTP3] = this->getPushIntervalHttp3();
doc[PARAM_HW_PUSH_INTERVAL_INFLUX] = this->getPushIntervalInflux();
doc[PARAM_HW_PUSH_INTERVAL_MQTT] = this->getPushIntervalMqtt();
doc[PARAM_HW_TEMPSENSOR_RESOLUTION] = this->getTempSensorResolution();
doc[PARAM_HW_IGNORE_LOW_ANGLES] = this->isIgnoreLowAnges();
#if LOG_LEVEL == 6 && !defined(DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(DISABLE_LOGGING)
serializeJson(doc, Serial); serializeJson(doc, EspSerial);
Serial.print(CR); EspSerial.print(CR);
#endif #endif
serializeJson(doc, configFile); serializeJson(doc, configFile);
@ -385,7 +410,7 @@ bool HardwareConfig::saveFile() {
// //
// Load config file from disk // Load config file from disk
// //
bool HardwareConfig::loadFile() { bool AdvancedConfig::loadFile() {
#if LOG_LEVEL == 6 && !defined(DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(DISABLE_LOGGING)
Log.verbose(F("CFG : Loading hardware configuration from file." CR)); Log.verbose(F("CFG : Loading hardware configuration from file." CR));
#endif #endif
@ -399,7 +424,7 @@ bool HardwareConfig::loadFile() {
File configFile = LittleFS.open(CFG_HW_FILENAME, "r"); File configFile = LittleFS.open(CFG_HW_FILENAME, "r");
if (!configFile) { if (!configFile) {
Log.error(F("CFG : Failed to open " CFG_HW_FILENAME "." CR)); writeErrorLog("CFG : Failed to read hardware configuration");
return false; return false;
} }
@ -409,15 +434,13 @@ bool HardwareConfig::loadFile() {
DynamicJsonDocument doc(512); DynamicJsonDocument doc(512);
DeserializationError err = deserializeJson(doc, configFile); DeserializationError err = deserializeJson(doc, configFile);
#if LOG_LEVEL == 6 #if LOG_LEVEL == 6
serializeJson(doc, Serial); serializeJson(doc, EspSerial);
Serial.print(CR); EspSerial.print(CR);
#endif #endif
configFile.close(); configFile.close();
if (err) { if (err) {
Log.error( writeErrorLog("CFG : Failed to parse hardware configuration (json)");
F("CFG : Failed to parse " CFG_HW_FILENAME " file, Err: %s, %d." CR),
err.c_str(), doc.capacity());
return false; return false;
} }
@ -427,8 +450,8 @@ bool HardwareConfig::loadFile() {
if (!doc[PARAM_HW_GYRO_READ_COUNT].isNull()) if (!doc[PARAM_HW_GYRO_READ_COUNT].isNull())
this->setGyroReadCount(doc[PARAM_HW_GYRO_READ_COUNT].as<int>()); this->setGyroReadCount(doc[PARAM_HW_GYRO_READ_COUNT].as<int>());
if (!doc[PARAM_HW_GYRO_READ_DELAY].isNull()) // if (!doc[PARAM_HW_GYRO_READ_DELAY].isNull())
this->setGyroReadDelay(doc[PARAM_HW_GYRO_READ_DELAY].as<int>()); // this->setGyroReadDelay(doc[PARAM_HW_GYRO_READ_DELAY].as<int>());
if (!doc[PARAM_HW_GYRO_MOVING_THREASHOLD].isNull()) if (!doc[PARAM_HW_GYRO_MOVING_THREASHOLD].isNull())
this->setGyroSensorMovingThreashold( this->setGyroSensorMovingThreashold(
doc[PARAM_HW_GYRO_MOVING_THREASHOLD].as<int>()); doc[PARAM_HW_GYRO_MOVING_THREASHOLD].as<int>());
@ -438,8 +461,27 @@ bool HardwareConfig::loadFile() {
if (!doc[PARAM_HW_FORMULA_CALIBRATION_TEMP].isNull()) if (!doc[PARAM_HW_FORMULA_CALIBRATION_TEMP].isNull())
this->SetDefaultCalibrationTemp( this->SetDefaultCalibrationTemp(
doc[PARAM_HW_FORMULA_CALIBRATION_TEMP].as<float>()); doc[PARAM_HW_FORMULA_CALIBRATION_TEMP].as<float>());
if (!doc[PARAM_HW_WIFI_PORTALTIMEOUT].isNull()) if (!doc[PARAM_HW_WIFI_PORTAL_TIMEOUT].isNull())
this->setWifiPortalTimeout(doc[PARAM_HW_WIFI_PORTALTIMEOUT].as<int>()); this->setWifiPortalTimeout(doc[PARAM_HW_WIFI_PORTAL_TIMEOUT].as<int>());
if (!doc[PARAM_HW_WIFI_CONNECT_TIMEOUT].isNull())
this->setWifiConnectTimeout(doc[PARAM_HW_WIFI_CONNECT_TIMEOUT].as<int>());
if (!doc[PARAM_HW_PUSH_TIMEOUT].isNull())
this->setPushTimeout(doc[PARAM_HW_PUSH_TIMEOUT].as<int>());
if (!doc[PARAM_HW_PUSH_INTERVAL_HTTP1].isNull())
this->setPushIntervalHttp1(doc[PARAM_HW_PUSH_INTERVAL_HTTP1].as<int>());
if (!doc[PARAM_HW_PUSH_INTERVAL_HTTP2].isNull())
this->setPushIntervalHttp2(doc[PARAM_HW_PUSH_INTERVAL_HTTP2].as<int>());
if (!doc[PARAM_HW_PUSH_INTERVAL_HTTP3].isNull())
this->setPushIntervalHttp3(doc[PARAM_HW_PUSH_INTERVAL_HTTP3].as<int>());
if (!doc[PARAM_HW_PUSH_INTERVAL_INFLUX].isNull())
this->setPushIntervalInflux(doc[PARAM_HW_PUSH_INTERVAL_INFLUX].as<int>());
if (!doc[PARAM_HW_PUSH_INTERVAL_MQTT].isNull())
this->setPushIntervalMqtt(doc[PARAM_HW_PUSH_INTERVAL_MQTT].as<int>());
if (!doc[PARAM_HW_TEMPSENSOR_RESOLUTION].isNull())
this->setTempSensorResolution(
doc[PARAM_HW_TEMPSENSOR_RESOLUTION].as<int>());
if (!doc[PARAM_HW_IGNORE_LOW_ANGLES].isNull())
setIgnoreLowAnges(doc[PARAM_HW_IGNORE_LOW_ANGLES].as<bool>());
Log.notice(F("CFG : Configuration file " CFG_HW_FILENAME " loaded." CR)); Log.notice(F("CFG : Configuration file " CFG_HW_FILENAME " loaded." CR));
return true; return true;

View File

@ -27,9 +27,7 @@ SOFTWARE.
#include <helper.hpp> #include <helper.hpp>
#include <resources.hpp> #include <resources.hpp>
#define CFG_JSON_BUFSIZE 3192 #define CFG_APPNAME "GravityMon" // Name of firmware
#define CFG_APPNAME "GravityMon " // Name of firmware
#define CFG_FILENAME "/gravitymon.json" // Name of config file #define CFG_FILENAME "/gravitymon.json" // Name of config file
#define CFG_HW_FILENAME "/hardware.json" // Name of config file for hw #define CFG_HW_FILENAME "/hardware.json" // Name of config file for hw
@ -47,86 +45,162 @@ struct RawGyroData {
}; };
// Used for holding formulaData (used for calculating formula on device) // Used for holding formulaData (used for calculating formula on device)
#define FORMULA_DATA_SIZE 10
struct RawFormulaData { struct RawFormulaData {
double a[5]; double a[FORMULA_DATA_SIZE];
double g[5]; double g[FORMULA_DATA_SIZE];
}; };
class HardwareConfig { class AdvancedConfig {
private: private:
int _wifiPortalTimeout = 120; int _wifiPortalTimeout = 120; // seconds
float _maxFormulaCreationDeviation = 1.6; int _wifiConnectTimeout = 20; // seconds
float _defaultCalibrationTemp = 20.0; float _maxFormulaCreationDeviation = 3; // SG
float _defaultCalibrationTemp = 20.0; // C
int _gyroSensorMovingThreashold = 500; int _gyroSensorMovingThreashold = 500;
int _tempSensorResolution = 9; // bits
int _gyroReadCount = 50; int _gyroReadCount = 50;
int _gyroReadDelay = 3150; // us, empirical, to hold sampling to 200 Hz int _gyroReadDelay = 3150; // us, empirical, to hold sampling to 200 Hz
int _pushTimeout = 10; // seconds
int _pushIntervalHttp1 = 0;
int _pushIntervalHttp2 = 0;
int _pushIntervalHttp3 = 0;
int _pushIntervalInflux = 0;
int _pushIntervalMqtt = 0;
bool _IgnoreLowAnges = false;
public: public:
int getWifiPortalTimeout() { return _wifiPortalTimeout; } int getWifiPortalTimeout() { return _wifiPortalTimeout; }
void setWifiPortalTimeout(int t) { _wifiPortalTimeout = t; } void setWifiPortalTimeout(int t) { _wifiPortalTimeout = t; }
int getWifiConnectTimeout() { return _wifiConnectTimeout; }
void setWifiConnectTimeout(int t) { _wifiConnectTimeout = t; }
float getMaxFormulaCreationDeviation() { float getMaxFormulaCreationDeviation() {
return _maxFormulaCreationDeviation; return _maxFormulaCreationDeviation;
} }
void setMaxFormulaCreationDeviation(float f) { void setMaxFormulaCreationDeviation(float f) {
_maxFormulaCreationDeviation = f; _maxFormulaCreationDeviation = f;
} }
int getTempSensorResolution() { return _tempSensorResolution; }
void setTempSensorResolution(int t) {
if (t >= 9 && t <= 12) _tempSensorResolution = t;
}
float getDefaultCalibrationTemp() { return _defaultCalibrationTemp; } float getDefaultCalibrationTemp() { return _defaultCalibrationTemp; }
void SetDefaultCalibrationTemp(float t) { _defaultCalibrationTemp = t; } void SetDefaultCalibrationTemp(float t) { _defaultCalibrationTemp = t; }
int getGyroSensorMovingThreashold() { return _gyroSensorMovingThreashold; } int getGyroSensorMovingThreashold() { return _gyroSensorMovingThreashold; }
void setGyroSensorMovingThreashold(int t) { _gyroSensorMovingThreashold = t; } void setGyroSensorMovingThreashold(int t) { _gyroSensorMovingThreashold = t; }
int getGyroReadCount() { return _gyroReadCount; } int getGyroReadCount() { return _gyroReadCount; }
void setGyroReadCount(int c) { _gyroReadCount = c; } void setGyroReadCount(int c) { _gyroReadCount = c; }
int getGyroReadDelay() { return _gyroReadDelay; } int getGyroReadDelay() { return _gyroReadDelay; }
void setGyroReadDelay(int d) { _gyroReadDelay = d; } void setGyroReadDelay(int d) { _gyroReadDelay = d; }
int getPushTimeout() { return _pushTimeout; }
void setPushTimeout(int t) { _pushTimeout = t; }
int getPushIntervalHttp1() { return _pushIntervalHttp1; }
void setPushIntervalHttp1(int t) { _pushIntervalHttp1 = t; }
int getPushIntervalHttp2() { return _pushIntervalHttp2; }
void setPushIntervalHttp2(int t) { _pushIntervalHttp2 = t; }
int getPushIntervalHttp3() { return _pushIntervalHttp3; }
void setPushIntervalHttp3(int t) { _pushIntervalHttp3 = t; }
int getPushIntervalInflux() { return _pushIntervalInflux; }
void setPushIntervalInflux(int t) { _pushIntervalInflux = t; }
int getPushIntervalMqtt() { return _pushIntervalMqtt; }
void setPushIntervalMqtt(int t) { _pushIntervalMqtt = t; }
bool isPushIntervalActive() {
return (_pushIntervalHttp1 + _pushIntervalHttp2 + _pushIntervalHttp3 +
_pushIntervalInflux + _pushIntervalMqtt) == 0
? false
: true;
}
const bool isIgnoreLowAnges() { return _IgnoreLowAnges; }
void setIgnoreLowAnges(bool b) { _IgnoreLowAnges = b; }
bool saveFile(); bool saveFile();
bool loadFile(); bool loadFile();
}; };
class Config { class Config {
private: private:
bool _saveNeeded; bool _saveNeeded = false;
int _configVersion = 2;
// Device configuration // Device configuration
String _id; String _id = "";
String _mDNS; String _mDNS = "";
String _otaURL; String _otaURL = "";
char _tempFormat; char _tempFormat = 'C';
float _voltageFactor; #if defined(ESP8266)
float _tempSensorAdjC; float _voltageFactor = 1.59;
int _sleepInterval; #elif defined(ESP32C3)
bool _gyroTemp; float _voltageFactor = 1.3;
#elif defined(ESP32S2)
float _voltageFactor = 0.59;
#elif defined(ESP32LITE)
float _voltageFactor = 1.59;
#else // ESP32
float _voltageFactor = 1.3;
#endif
float _voltageConfig = 4.15;
float _tempSensorAdjC = 0;
int _sleepInterval = 900;
#if defined(FLOATY)
bool _gyroTemp = true;
#else
bool _gyroTemp = false;
#endif
bool _storageSleep = false;
// Wifi Config // Wifi Config
String _wifiSSID; String _wifiSSID[2] = {"", ""};
String _wifiPASS; String _wifiPASS[2] = {"", ""};
// Push target settings // Push target settings
String _brewfatherPushUrl; String _token = "";
String _token2 = "";
String _httpPushUrl; String _httpUrl = "";
String _httpPushUrl2; String _httpHeader[2] = {"Content-Type: application/json", ""};
String _http2Url = "";
String _http2Header[2] = {"Content-Type: application/json", ""};
String _http3Url = "";
String _influxDb2Url; String _influxDb2Url = "";
String _influxDb2Org; String _influxDb2Org = "";
String _influxDb2Bucket; String _influxDb2Bucket = "";
String _influxDb2Token; String _influxDb2Token = "";
String _mqttUrl; String _mqttUrl = "";
int _mqttPort; int _mqttPort = 1883;
String _mqttUser; String _mqttUser = "";
String _mqttPass; String _mqttPass = "";
// Gravity and temperature calculations // Gravity and temperature calculations
String _gravityFormula; String _gravityFormula = "";
bool _gravityTempAdj; bool _gravityTempAdj = false;
char _gravityFormat; char _gravityFormat = 'G';
// BLE (ESP32 only)
String _colorBLE;
// Gyro calibration and formula calculation data // Gyro calibration and formula calculation data
RawGyroData _gyroCalibration; RawGyroData _gyroCalibration = {0, 0, 0, 0, 0, 0};
RawFormulaData _formulaData; RawFormulaData _formulaData = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};
void debug();
void formatFileSystem(); void formatFileSystem();
public: public:
@ -139,10 +213,22 @@ class Config {
_saveNeeded = true; _saveNeeded = true;
} }
int getConfigVersion() { return _configVersion; }
const bool isGyroTemp() { return _gyroTemp; } const bool isGyroTemp() { return _gyroTemp; }
void setGyroTemp(bool b) { void setGyroTemp(bool b) {
#if defined(FLOATY)
// Floaty hardware dont have a temp sensor, uses gyro temperature
#else
_gyroTemp = b; _gyroTemp = b;
_saveNeeded = true; _saveNeeded = true;
#endif
}
const bool isStorageSleep() { return _storageSleep; }
void setStorageSleep(bool b) {
_storageSleep = b;
_saveNeeded = true;
} }
const char* getOtaURL() { return _otaURL.c_str(); } const char* getOtaURL() { return _otaURL.c_str(); }
@ -151,41 +237,80 @@ class Config {
_saveNeeded = true; _saveNeeded = true;
} }
bool isOtaActive() { return _otaURL.length() ? true : false; } bool isOtaActive() { return _otaURL.length() ? true : false; }
bool isOtaSSL() { return _otaURL.startsWith("https://"); }
const char* getWifiSSID() { return _wifiSSID.c_str(); } const char* getWifiSSID(int idx) { return _wifiSSID[idx].c_str(); }
void setWifiSSID(String s) { void setWifiSSID(String s, int idx) {
_wifiSSID = s; _wifiSSID[idx] = s;
_saveNeeded = true; _saveNeeded = true;
} }
const char* getWifiPass() { return _wifiPASS.c_str(); } const char* getWifiPass(int idx) { return _wifiPASS[idx].c_str(); }
void setWifiPass(String s) { void setWifiPass(String s, int idx) {
_wifiPASS = s; _wifiPASS[idx] = s;
_saveNeeded = true;
}
bool dualWifiConfigured() {
return _wifiSSID[0].length() > 0 && _wifiSSID[1].length() > 0 ? true
: false;
}
void swapPrimaryWifi() {
String s = _wifiSSID[0];
_wifiSSID[0] = _wifiSSID[1];
_wifiSSID[1] = s;
String p = _wifiPASS[0];
_wifiPASS[0] = _wifiPASS[1];
_wifiPASS[1] = p;
_saveNeeded = true; _saveNeeded = true;
} }
// Brewfather // Token parameter
const char* getBrewfatherPushUrl() { return _brewfatherPushUrl.c_str(); } const char* getToken() { return _token.c_str(); }
void setBrewfatherPushUrl(String s) { void setToken(String s) {
_brewfatherPushUrl = s; _token = s;
_saveNeeded = true; _saveNeeded = true;
} }
bool isBrewfatherActive() { const char* getToken2() { return _token2.c_str(); }
return _brewfatherPushUrl.length() ? true : false; void setToken2(String s) {
_token2 = s;
_saveNeeded = true;
} }
// Standard HTTP // Standard HTTP
const char* getHttpPushUrl() { return _httpPushUrl.c_str(); } const char* getHttpUrl() { return _httpUrl.c_str(); }
void setHttpPushUrl(String s) { void setHttpUrl(String s) {
_httpPushUrl = s; _httpUrl = s;
_saveNeeded = true; _saveNeeded = true;
} }
bool isHttpActive() { return _httpPushUrl.length() ? true : false; } const char* getHttpHeader(int idx) { return _httpHeader[idx].c_str(); }
const char* getHttpPushUrl2() { return _httpPushUrl2.c_str(); } void setHttpHeader(String s, int idx) {
void setHttpPushUrl2(String s) { _httpHeader[idx] = s;
_httpPushUrl2 = s;
_saveNeeded = true; _saveNeeded = true;
} }
bool isHttpActive2() { return _httpPushUrl2.length() ? true : false; } bool isHttpActive() { return _httpUrl.length() ? true : false; }
bool isHttpSSL() { return _httpUrl.startsWith("https://"); }
const char* getHttp2Url() { return _http2Url.c_str(); }
void setHttp2Url(String s) {
_http2Url = s;
_saveNeeded = true;
}
const char* getHttp2Header(int idx) { return _http2Header[idx].c_str(); }
void setHttp2Header(String s, int idx) {
_http2Header[idx] = s;
_saveNeeded = true;
}
bool isHttp2Active() { return _http2Url.length() ? true : false; }
bool isHttp2SSL() { return _http2Url.startsWith("https://"); }
const char* getHttp3Url() { return _http3Url.c_str(); }
void setHttp3Url(String s) {
_http3Url = s;
_saveNeeded = true;
}
bool isHttp3Active() { return _http3Url.length() ? true : false; }
bool isHttp3SSL() { return _http3Url.startsWith("https://"); }
// InfluxDB2 // InfluxDB2
const char* getInfluxDb2PushUrl() { return _influxDb2Url.c_str(); } const char* getInfluxDb2PushUrl() { return _influxDb2Url.c_str(); }
@ -194,6 +319,7 @@ class Config {
_saveNeeded = true; _saveNeeded = true;
} }
bool isInfluxDb2Active() { return _influxDb2Url.length() ? true : false; } bool isInfluxDb2Active() { return _influxDb2Url.length() ? true : false; }
bool isInfluxSSL() { return _influxDb2Url.startsWith("https://"); }
const char* getInfluxDb2PushOrg() { return _influxDb2Org.c_str(); } const char* getInfluxDb2PushOrg() { return _influxDb2Org.c_str(); }
void setInfluxDb2PushOrg(String s) { void setInfluxDb2PushOrg(String s) {
_influxDb2Org = s; _influxDb2Org = s;
@ -211,12 +337,14 @@ class Config {
} }
// MQTT // MQTT
bool isMqttActive() { return _mqttUrl.length() ? true : false; }
const char* getMqttUrl() { return _mqttUrl.c_str(); } const char* getMqttUrl() { return _mqttUrl.c_str(); }
void setMqttUrl(String s) { void setMqttUrl(String s) {
_mqttUrl = s; _mqttUrl = s;
_saveNeeded = true; _saveNeeded = true;
} }
bool isMqttActive() { return _mqttUrl.length() ? true : false; }
bool isMqttSSL() { return _mqttPort > 8000 ? true : false; }
int getMqttPort() { return _mqttPort; } int getMqttPort() { return _mqttPort; }
void setMqttPort(String s) { void setMqttPort(String s) {
_mqttPort = s.toInt(); _mqttPort = s.toInt();
@ -267,17 +395,27 @@ class Config {
_saveNeeded = true; _saveNeeded = true;
} }
float getVoltageConfig() { return _voltageConfig; }
void setVoltageConfig(float f) {
_voltageConfig = f;
_saveNeeded = true;
}
void setVoltageConfig(String s) {
_voltageConfig = s.toFloat();
_saveNeeded = true;
}
float getTempSensorAdjC() { return _tempSensorAdjC; } float getTempSensorAdjC() { return _tempSensorAdjC; }
void setTempSensorAdjC(float f) { void setTempSensorAdjC(float f) {
_tempSensorAdjC = f; _tempSensorAdjC = f;
_saveNeeded = true; _saveNeeded = true;
} }
void setTempSensorAdjC(String s) { void setTempSensorAdjC(String s, float adjustC = 0) {
_tempSensorAdjC = s.toFloat(); _tempSensorAdjC = s.toFloat() + adjustC;
_saveNeeded = true; _saveNeeded = true;
} }
void setTempSensorAdjF(String s) { void setTempSensorAdjF(String s, float adjustF = 0) {
_tempSensorAdjC = convertFtoC(s.toFloat()); _tempSensorAdjC = convertFtoC(s.toFloat() + adjustF);
_saveNeeded = true; _saveNeeded = true;
} }
@ -303,6 +441,19 @@ class Config {
bool isGravitySG() { return _gravityFormat == 'G'; } bool isGravitySG() { return _gravityFormat == 'G'; }
bool isGravityPlato() { return _gravityFormat == 'P'; } bool isGravityPlato() { return _gravityFormat == 'P'; }
const char* getColorBLE() { return _colorBLE.c_str(); }
void setColorBLE(String c) {
_colorBLE = c;
_saveNeeded = true;
}
bool isBLEActive() { return _colorBLE.length() ? true : false; }
bool isWifiPushActive() {
return (isHttpActive() || isHttp2Active() || isHttp3Active() ||
isInfluxDb2Active() || isMqttActive())
? true
: false;
}
const RawGyroData& getGyroCalibration() { return _gyroCalibration; } const RawGyroData& getGyroCalibration() { return _gyroCalibration; }
void setGyroCalibration(const RawGyroData& r) { void setGyroCalibration(const RawGyroData& r) {
_gyroCalibration = r; _gyroCalibration = r;
@ -325,7 +476,7 @@ class Config {
}; };
extern Config myConfig; extern Config myConfig;
extern HardwareConfig myHardwareConfig; extern AdvancedConfig myAdvancedConfig;
#endif // SRC_CONFIG_HPP_ #endif // SRC_CONFIG_HPP_

View File

@ -30,21 +30,42 @@ MPU6050 accelgyro;
#define GYRO_USE_INTERRUPT // Use interrupt to detect when new sample is ready #define GYRO_USE_INTERRUPT // Use interrupt to detect when new sample is ready
#define GYRO_SHOW_MINMAX // Will calculate the min/max values when doing #define GYRO_SHOW_MINMAX // Will calculate the min/max values when doing
// calibration // calibration
// #define GYRO_CALIBRATE_STARTUP // Will calibrate sensor at startup
//
// Initialize the sensor chip.
//
bool GyroSensor::setup() { bool GyroSensor::setup() {
int clock = 400000;
#if defined(FLOATY)
pinMode(PIN_VCC, OUTPUT);
pinMode(PIN_GND, OUTPUT_OPEN_DRAIN);
digitalWrite(PIN_VCC, HIGH);
digitalWrite(PIN_GND, LOW);
delay(10); // Wait for the pins to settle or we will fail to connect
#else
#endif
/* For testing pin config of new boards with led.
pinMode(PIN_SDA, OUTPUT);
pinMode(PIN_SCL, OUTPUT);
for(int i = 0, j = LOW, k = LOW; i < 100; i++) {
digitalWrite(PIN_SDA, k);
digitalWrite(PIN_SCL, j);
k = !k;
delay(300);
digitalWrite(PIN_SDA, k);
k = !k;
j = !j;
delay(300);
}*/
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
Log.verbose(F("GYRO: Setting up hardware." CR)); Log.verbose(F("GYRO: Setting up hardware." CR));
#endif #endif
Wire.begin(PIN_SDA, PIN_SCL); Wire.begin(PIN_SDA, PIN_SCL);
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having Wire.setClock(clock); // 400kHz I2C clock.
// compilation difficulties
if (!accelgyro.testConnection()) { uint8_t id = accelgyro.getDeviceID();
Log.error(F("GYRO: Failed to connect to MPU6050 (gyro)." CR));
if (id != 0x34 && id != 0x38) { // Allow both MPU6050 and MPU6500
writeErrorLog("GYRO: Failed to connect to gyro, is it connected?");
_sensorConnected = false; _sensorConnected = false;
} else { } else {
#if !defined(GYRO_DISABLE_LOGGING) #if !defined(GYRO_DISABLE_LOGGING)
@ -66,11 +87,6 @@ bool GyroSensor::setup() {
accelgyro.setIntDataReadyEnabled(true); accelgyro.setIntDataReadyEnabled(true);
#endif #endif
#if defined(GYRO_CALIBRATE_STARTUP)
// Run the calibration at start, useful for testing.
calibrateSensor();
#endif
// Once we have calibration values stored we just apply them from the // Once we have calibration values stored we just apply them from the
// config. // config.
_calibrationOffset = myConfig.getGyroCalibration(); _calibrationOffset = myConfig.getGyroCalibration();
@ -79,19 +95,17 @@ bool GyroSensor::setup() {
return _sensorConnected; return _sensorConnected;
} }
//
// Set sensor in sleep mode to conserve battery
//
void GyroSensor::enterSleep() { void GyroSensor::enterSleep() {
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
Log.verbose(F("GYRO: Setting up hardware." CR)); Log.verbose(F("GYRO: Setting up hardware." CR));
#endif #endif
#if defined(FLOATY)
digitalWrite(PIN_VCC, LOW);
#else
accelgyro.setSleepEnabled(true); accelgyro.setSleepEnabled(true);
#endif
} }
//
// Do a number of reads to get a more stable value.
//
void GyroSensor::readSensor(RawGyroData &raw, const int noIterations, void GyroSensor::readSensor(RawGyroData &raw, const int noIterations,
const int delayTime) { const int delayTime) {
RawGyroDataL average = {0, 0, 0, 0, 0, 0}; RawGyroDataL average = {0, 0, 0, 0, 0, 0};
@ -103,7 +117,8 @@ void GyroSensor::readSensor(RawGyroData &raw, const int noIterations,
// Set some initial values // Set some initial values
#if defined(GYRO_SHOW_MINMAX) #if defined(GYRO_SHOW_MINMAX)
RawGyroData min, max; RawGyroData min = {0, 0, 0};
RawGyroData max = {0, 0, 0};
accelgyro.getAcceleration(&min.ax, &min.ay, &min.az); accelgyro.getAcceleration(&min.ax, &min.ay, &min.az);
min.temp = accelgyro.getTemperature(); min.temp = accelgyro.getTemperature();
max = min; max = min;
@ -177,9 +192,6 @@ void GyroSensor::readSensor(RawGyroData &raw, const int noIterations,
#endif #endif
} }
//
// Calcuate the angles (tilt)
//
float GyroSensor::calculateAngle(RawGyroData &raw) { float GyroSensor::calculateAngle(RawGyroData &raw) {
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
Log.verbose(F("GYRO: Calculating the angle." CR)); Log.verbose(F("GYRO: Calculating the angle." CR));
@ -195,23 +207,25 @@ float GyroSensor::calculateAngle(RawGyroData &raw) {
az = (static_cast<float>(raw.az)) / 16384; az = (static_cast<float>(raw.az)) / 16384;
// Source: https://www.nxp.com/docs/en/application-note/AN3461.pdf // Source: https://www.nxp.com/docs/en/application-note/AN3461.pdf
float v = (acos(ay / sqrt(ax * ax + ay * ay + az * az)) * 180.0 / PI); float vY = (acos(abs(ay) / sqrt(ax * ax + ay * ay + az * az)) * 180.0 / PI);
// float vZ = (acos(abs(az) / sqrt(ax * ax + ay * ay + az * az)) * 180.0 /
// PI); float vX = (acos(abs(ax) / sqrt(ax * ax + ay * ay + az * az)) * 180.0
// / PI);
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
Log.verbose(F("GYRO: angle = %F." CR), v); // Log.notice(F("GYRO: angleX= %F." CR), vX);
Log.notice(F("GYRO: angleY= %F." CR), vY);
// Log.notice(F("GYRO: angleZ= %F." CR), vZ);
#endif #endif
return v; return vY;
} }
//
// Check if the values are high that indicate that the sensor is moving.
//
bool GyroSensor::isSensorMoving(RawGyroData &raw) { bool GyroSensor::isSensorMoving(RawGyroData &raw) {
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
Log.verbose(F("GYRO: Checking for sensor movement." CR)); Log.verbose(F("GYRO: Checking for sensor movement." CR));
#endif #endif
int x = abs(raw.gx), y = abs(raw.gy), z = abs(raw.gz); int x = abs(raw.gx), y = abs(raw.gy), z = abs(raw.gz);
int threashold = myHardwareConfig.getGyroSensorMovingThreashold(); int threashold = myAdvancedConfig.getGyroSensorMovingThreashold();
if (x > threashold || y > threashold || z > threashold) { if (x > threashold || y > threashold || z > threashold) {
Log.notice(F("GYRO: Movement detected (%d)\t%d\t%d\t%d." CR), threashold, x, Log.notice(F("GYRO: Movement detected (%d)\t%d\t%d\t%d." CR), threashold, x,
@ -222,9 +236,6 @@ bool GyroSensor::isSensorMoving(RawGyroData &raw) {
return false; return false;
} }
//
// Read the tilt angle from the gyro.
//
bool GyroSensor::read() { bool GyroSensor::read() {
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
Log.verbose(F("GYRO: Getting new gyro position." CR)); Log.verbose(F("GYRO: Getting new gyro position." CR));
@ -233,22 +244,22 @@ bool GyroSensor::read() {
if (!_sensorConnected) return false; if (!_sensorConnected) return false;
readSensor( readSensor(
_lastGyroData, myHardwareConfig.getGyroReadCount(), _lastGyroData, myAdvancedConfig.getGyroReadCount(),
myHardwareConfig.getGyroReadDelay()); // Last param is unused if myAdvancedConfig.getGyroReadDelay()); // Last param is unused if
// GYRO_USE_INTERRUPT is defined. // GYRO_USE_INTERRUPT is defined.
// If the sensor is unstable we return false to signal we dont have valid // If the sensor is unstable we return false to signal we dont have valid
// value // value
if (isSensorMoving(_lastGyroData)) { if (isSensorMoving(_lastGyroData)) {
#if !defined(GYRO_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
Log.notice(F("GYRO: Sensor is moving." CR)); Log.notice(F("GYRO: Sensor is moving." CR));
#endif #endif
_validValue = false; _validValue = false;
} else { } else {
_validValue = true; _validValue = true;
_angle = calculateAngle(_lastGyroData); _angle = calculateAngle(_lastGyroData);
#if !defined(GYRO_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
Log.notice(F("GYRO: Sensor values %d,%d,%d\t%F" CR), _lastGyroData.ax, Log.verbose(F("GYRO: Sensor values %d,%d,%d\t%F" CR), _lastGyroData.ax,
_lastGyroData.ay, _lastGyroData.az, _angle); _lastGyroData.ay, _lastGyroData.az, _angle);
#endif #endif
} }
@ -263,9 +274,6 @@ bool GyroSensor::read() {
return _validValue; return _validValue;
} }
//
// Dump the stored calibration values.
//
void GyroSensor::dumpCalibration() { void GyroSensor::dumpCalibration() {
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
Log.verbose(F("GYRO: Accel offset\t%d\t%d\t%d" CR), _calibrationOffset.ax, Log.verbose(F("GYRO: Accel offset\t%d\t%d\t%d" CR), _calibrationOffset.ax,
@ -275,9 +283,6 @@ void GyroSensor::dumpCalibration() {
#endif #endif
} }
//
// Update the sensor with out calculated offsets.
//
void GyroSensor::applyCalibration() { void GyroSensor::applyCalibration() {
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
Log.verbose(F("GYRO: Applying calibration offsets to sensor." CR)); Log.verbose(F("GYRO: Applying calibration offsets to sensor." CR));
@ -286,7 +291,8 @@ void GyroSensor::applyCalibration() {
if ((_calibrationOffset.ax + _calibrationOffset.ay + _calibrationOffset.az + if ((_calibrationOffset.ax + _calibrationOffset.ay + _calibrationOffset.az +
_calibrationOffset.gx + _calibrationOffset.gy + _calibrationOffset.gz) == _calibrationOffset.gx + _calibrationOffset.gy + _calibrationOffset.gz) ==
0) { 0) {
Log.error(F("GYRO: No valid calibraion values exist, aborting." CR)); writeErrorLog(
"GYRO: No valid calibration values, please calibrate the device.");
return; return;
} }
@ -298,22 +304,19 @@ void GyroSensor::applyCalibration() {
accelgyro.setZGyroOffset(_calibrationOffset.gz); accelgyro.setZGyroOffset(_calibrationOffset.gz);
} }
//
// Calculate the offsets for calibration.
//
void GyroSensor::calibrateSensor() { void GyroSensor::calibrateSensor() {
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
Log.verbose(F("GYRO: Calibrating sensor" CR)); Log.verbose(F("GYRO: Calibrating sensor" CR));
#endif #endif
// accelgyro.PrintActiveOffsets(); // accelgyro.PrintActiveOffsets();
// Serial.print( CR ); // EspSerial.print( CR );
accelgyro.setDLPFMode(MPU6050_DLPF_BW_5); accelgyro.setDLPFMode(MPU6050_DLPF_BW_5);
accelgyro.CalibrateAccel(6); // 6 = 600 readings accelgyro.CalibrateAccel(6); // 6 = 600 readings
accelgyro.CalibrateGyro(6); accelgyro.CalibrateGyro(6);
accelgyro.PrintActiveOffsets(); accelgyro.PrintActiveOffsets();
Serial.print(CR); EspSerial.print(CR);
_calibrationOffset.ax = accelgyro.getXAccelOffset(); _calibrationOffset.ax = accelgyro.getXAccelOffset();
_calibrationOffset.ay = accelgyro.getYAccelOffset(); _calibrationOffset.ay = accelgyro.getYAccelOffset();
@ -322,14 +325,10 @@ void GyroSensor::calibrateSensor() {
_calibrationOffset.gy = accelgyro.getYGyroOffset(); _calibrationOffset.gy = accelgyro.getYGyroOffset();
_calibrationOffset.gz = accelgyro.getZGyroOffset(); _calibrationOffset.gz = accelgyro.getZGyroOffset();
// Save the calibrated values
myConfig.setGyroCalibration(_calibrationOffset); myConfig.setGyroCalibration(_calibrationOffset);
myConfig.saveFile(); myConfig.saveFile();
} }
//
// Calibrate the device.
//
void GyroSensor::debug() { void GyroSensor::debug() {
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
Log.verbose(F("GYRO: Debug - Clock src %d." CR), Log.verbose(F("GYRO: Debug - Clock src %d." CR),

View File

@ -21,12 +21,12 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#if defined (ESP8266) #if defined(ESP8266)
#include <ESP8266HTTPClient.h> #include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#else // defined (ESP32) #else // defined (ESP32)
#include <WiFi.h>
#include <HTTPClient.h> #include <HTTPClient.h>
#include <WiFi.h>
#endif #endif
#include <config.hpp> #include <config.hpp>
@ -36,48 +36,185 @@ SOFTWARE.
#include <tempsensor.hpp> #include <tempsensor.hpp>
#include <wifi.hpp> #include <wifi.hpp>
SerialDebug mySerial; // tcp cleanup, to avoid memory crash.
BatteryVoltage myBatteryVoltage; struct tcp_pcb;
extern struct tcp_pcb* tcp_tw_pcbs;
extern "C" void tcp_abort(struct tcp_pcb* pcb);
void tcp_cleanup() {
while (tcp_tw_pcbs) tcp_abort(tcp_tw_pcbs);
}
// void checkResetReason() {
// Convert sg to plato #if defined(ESP8266)
// rst_info* _rinfo;
double convertToPlato(double sg) { return 259 - (259 / sg); } _rinfo = ESP.getResetInfoPtr();
Log.notice(F("HELP: Last reset cause %d" CR), _rinfo->exccause);
if (_rinfo->exccause > 0) {
char s[120];
snprintf(&s[0], sizeof(s),
"HELP: Exception (%d) reason=%d epc1=0x%08x epc2=0x%08x "
"epc3=0x%08x execvaddr=0x%08x depc=0x%08x",
_rinfo->exccause, _rinfo->reason, _rinfo->epc1, _rinfo->epc2,
_rinfo->epc3, _rinfo->excvaddr, _rinfo->depc);
writeErrorLog(&s[0]);
}
#else // defined (ESP32)
RESET_REASON r = rtc_get_reset_reason(
0); // We only check cpu0 since we dont use cpu1 on the esp32
String rStr;
switch (r) {
case 0:
rStr = F("None");
break;
case 1:
rStr = F("vbat power on reset");
break;
case 3:
rStr = F("software reset digital core");
break;
case 4:
rStr = F("legacy watch dog reset digital core");
break;
case 5:
rStr = F("deep Sleep reset digital core");
break;
case 6:
rStr = F("reset by SLC module, reset digital core");
break;
case 7:
rStr = F("timer Group0 Watch dog reset digital core");
break;
case 8:
rStr = F("timer Group1 Watch dog reset digital core");
break;
case 9:
rStr = F("RTC Watch dog Reset digital core");
break;
case 10:
rStr = F("instrusion tested to reset CPU");
break;
case 11:
rStr = F("time Group reset CPU");
break;
case 12:
rStr = F("software reset CPU");
break;
case 13:
rStr = F("RTC Watch dog Reset CPU");
break;
case 14:
rStr = F("for APP CPU, reseted by PRO CPU");
break;
case 15:
rStr = F("reset when the vdd voltage is not stable");
break;
case 16:
rStr = F("RTC Watch dog reset digital core and rtc module");
break;
default:
rStr = F("unknown reset reason");
break;
}
Log.notice(F("HELP: Last reset cause '%s' (%d)" CR), rStr.c_str(), r);
// Havent found a good way to get exception cause from an ESP32
#endif
}
void writeErrorLog(const char* format, ...) {
File f = LittleFS.open(ERR_FILENAME, "a");
if (f && f.size() > ERR_FILEMAXSIZE) {
f.close();
LittleFS.remove(ERR_FILENAME2);
LittleFS.rename(ERR_FILENAME, ERR_FILENAME2);
f = LittleFS.open(ERR_FILENAME, "a");
}
va_list arg;
va_start(arg, format);
char buf[120];
vsnprintf(&buf[0], sizeof(buf), format, arg);
va_end(arg);
Log.errorln(&buf[0]);
if (f) {
#if defined(ESP8266)
f.write(&buf[0], strlen(&buf[0]));
#else // ESP32
f.write((unsigned char*)&buf[0], strlen(&buf[0]));
#endif
f.println();
f.close();
} else {
Log.warning(F("HELP: Failed to open error log." CR));
}
}
double convertToPlato(double sg) {
if (sg) return 259 - (259 / sg);
return 0;
}
//
// Convert plato to sg
//
double convertToSG(double plato) { return 259 / (259 - plato); } double convertToSG(double plato) { return 259 / (259 - plato); }
//
// Conversion to F
//
float convertCtoF(float c) { return (c * 1.8) + 32.0; } float convertCtoF(float c) { return (c * 1.8) + 32.0; }
//
// Conversion to C
//
float convertFtoC(float f) { return (f - 32.0) / 1.8; } float convertFtoC(float f) { return (f - 32.0) / 1.8; }
// FloatHistoryLog::FloatHistoryLog(String fName) {
// Print the heap information. _fName = fName;
//
void printHeap() { File runFile = LittleFS.open(_fName, "r");
#if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING) if (runFile) {
#if defined (ESP8266) for (int i = 0; i < 10; i++) {
Log.verbose(F("HELP: Heap %d kb, HeapFrag %d %%, FreeSketch %d kb." CR), _runTime[i] = runFile.readStringUntil('\n').toFloat();
ESP.getFreeHeap() / 1024, ESP.getHeapFragmentation(), if (_runTime[i]) {
ESP.getFreeSketchSpace() / 1024); _average += _runTime[i];
_count++;
}
}
runFile.close();
_average = _average / _count;
}
}
void FloatHistoryLog::addEntry(float time) {
for (int i = (10 - 1); i > 0; i--) {
_runTime[i] = _runTime[i - 1];
}
_runTime[0] = time;
save();
}
void FloatHistoryLog::save() {
File runFile = LittleFS.open(_fName, "w");
if (runFile) {
for (int i = 0; i < 10; i++) {
runFile.println(_runTime[i], 2);
}
runFile.close();
}
}
void printHeap(String prefix) {
#if defined(ESP8266)
Log.notice(
F("%s: Free-heap %d kb, Heap-rag %d %%, Max-block %d kb Stack=%d b." CR),
prefix.c_str(), ESP.getFreeHeap() / 1024, ESP.getHeapFragmentation(),
ESP.getMaxFreeBlockSize() / 1024, ESP.getFreeContStack());
// Log.notice(F("%s: Heap %d kb, HeapFrag %d %%, FreeSketch %d kb." CR),
// prefix.c_str(), ESP.getFreeHeap() / 1024,
// ESP.getHeapFragmentation(), ESP.getFreeSketchSpace() / 1024);
#else // defined (ESP32) #else // defined (ESP32)
Log.verbose(F("HELP: Heap %d kb, FreeSketch %d kb." CR), Log.verbose(F("HELP: Heap %d kb, FreeSketch %d kb." CR),
ESP.getFreeHeap() / 1024, ESP.getFreeSketchSpace() / 1024); ESP.getFreeHeap() / 1024, ESP.getFreeSketchSpace() / 1024);
#endif #endif
#endif
} }
//
// Enter deep sleep for the defined duration (Argument is seconds)
//
void deepSleep(int t) { void deepSleep(int t) {
#if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING)
Log.verbose(F("HELP: Entering sleep mode for %ds." CR), t); Log.verbose(F("HELP: Entering sleep mode for %ds." CR), t);
@ -86,62 +223,87 @@ void deepSleep(int t) {
ESP.deepSleep(wake); ESP.deepSleep(wake);
} }
//
// Print the build options used
//
void printBuildOptions() { void printBuildOptions() {
Log.notice(F("Build options: %s LOGLEVEL %d " Log.notice(F("Build options: %s (%s) LOGLEVEL %d "
#ifdef SKIP_SLEEPMODE #ifdef SKIP_SLEEPMODE
"SKIP_SLEEP " "SKIP_SLEEP "
#endif #endif
#ifdef EMBED_HTML
"EMBED_HTML "
#endif
#ifdef COLLECT_PERFDATA #ifdef COLLECT_PERFDATA
"PERFDATA " "PERFDATA "
#endif
#ifdef ACTIVATE_OTA
"OTA "
#endif #endif
CR), CR),
CFG_APPVER, LOG_LEVEL); CFG_APPVER, CFG_GITREV, LOG_LEVEL);
} }
//
// Configure serial debug output
//
SerialDebug::SerialDebug(const uint32_t serialSpeed) { SerialDebug::SerialDebug(const uint32_t serialSpeed) {
// Start serial with auto-detected rate (default to defined BAUD) // Start serial with auto-detected rate (default to defined BAUD)
Serial.flush(); #if defined(USE_SERIAL_PINS) && defined(ESP8266)
Serial.begin(serialSpeed); uint8_t txPin = 3;
EspSerial.begin(serialSpeed, SERIAL_8N1, SERIAL_TX_ONLY, txPin);
#elif defined(ESP8266)
EspSerial.begin(serialSpeed);
#elif defined(USE_SERIAL_PINS) && defined(ESP32C3)
EspSerial.begin(115200L, SERIAL_8N1, 20, 21);
#elif defined(ESP32C3)
EspSerial.begin(115200L);
#elif defined(USE_SERIAL_PINS) && defined(ESP32S2)
EspSerial.begin(115200L, SERIAL_8N1, 37, 39);
#elif defined(ESP32S2)
EspSerial.begin(115200L);
#elif defined(USE_SERIAL_PINS) && defined(ESP32LITE)
EspSerial.begin(serialSpeed, SERIAL_8N1, 16, 17);
#elif defined(USE_SERIAL_PINS) && defined(ESP32)
EspSerial.begin(serialSpeed, SERIAL_8N1, 1, 3);
#elif defined(ESP32)
EspSerial.begin(115200L);
#endif
getLog()->begin(LOG_LEVEL, &Serial, true); EspSerial.println("Serial connection established");
EspSerial.setDebugOutput(true);
getLog()->begin(LOG_LEVEL, &EspSerial, true);
getLog()->setPrefix(printTimestamp); getLog()->setPrefix(printTimestamp);
getLog()->notice(F("SDBG: Serial logging started at %u." CR), serialSpeed); getLog()->notice(F("SDBG: Serial logging started at %u." CR), serialSpeed);
} }
//
// Print the timestamp (ms since start of device)
//
void printTimestamp(Print* _logOutput, int _logLevel) { void printTimestamp(Print* _logOutput, int _logLevel) {
char c[12]; char c[12];
snprintf(c, sizeof(c), "%10lu ", millis()); snprintf(c, sizeof(c), "%10lu ", millis());
_logOutput->print(c); _logOutput->print(c);
} }
// bool checkPinConnected() {
// Read and calculate the battery voltage #if defined(ESP8266)
// pinMode(PIN_CFG1, INPUT);
#else
pinMode(PIN_CFG1, INPUT_PULLDOWN);
#endif
pinMode(PIN_CFG2, OUTPUT);
delay(5);
digitalWrite(PIN_CFG2, 1);
delay(5);
int i = digitalRead(PIN_CFG1);
digitalWrite(PIN_CFG2, 0);
return i == LOW ? false : true;
}
BatteryVoltage::BatteryVoltage() {
#if defined(ESP8266)
pinMode(PIN_VOLT, INPUT);
#else
pinMode(PIN_VOLT, INPUT_PULLDOWN);
#endif
}
void BatteryVoltage::read() { void BatteryVoltage::read() {
// The analog pin can only handle 3.3V maximum voltage so we need to reduce // The analog pin can only handle 3.3V maximum voltage so we need to reduce
// the voltage (from max 5V) // the voltage (from max 5V)
float factor = myConfig.getVoltageFactor(); // Default value is 1.63 float factor = myConfig.getVoltageFactor(); // Default value is 1.63
int v = analogRead(A0); int v = analogRead(PIN_VOLT);
// An ESP8266 has a ADC range of 0-1023 and a maximum voltage of 3.3V // An ESP8266 has a ADC range of 0-1023 and a maximum voltage of 3.3V
// An ESP32 has an ADC range of 0-4095 and a maximum voltage of 3.3V // An ESP32 has an ADC range of 0-4095 and a maximum voltage of 3.3V
#if defined (ESP8266) #if defined(ESP8266)
_batteryLevel = ((3.3 / 1023) * v) * factor; _batteryLevel = ((3.3 / 1023) * v) * factor;
#else // defined (ESP32) #else // defined (ESP32)
_batteryLevel = ((3.3 / 4095) * v) * factor; _batteryLevel = ((3.3 / 4095) * v) * factor;
@ -157,9 +319,6 @@ void BatteryVoltage::read() {
PerfLogging myPerfLogging; PerfLogging myPerfLogging;
//
// Clear the current cache
//
void PerfLogging::clear() { void PerfLogging::clear() {
// Clear the measurements // Clear the measurements
if (first == 0) return; if (first == 0) return;
@ -176,17 +335,11 @@ void PerfLogging::clear() {
} while (pe != 0); } while (pe != 0);
} }
//
// Start measuring this performance point
//
void PerfLogging::start(const char* key) { void PerfLogging::start(const char* key) {
PerfEntry* pe = add(key); PerfEntry* pe = add(key);
pe->start = millis(); pe->start = millis();
} }
//
// Finalize measuring of this performance point
//
void PerfLogging::stop(const char* key) { void PerfLogging::stop(const char* key) {
PerfEntry* pe = find(key); PerfEntry* pe = find(key);
@ -199,9 +352,6 @@ void PerfLogging::stop(const char* key) {
} }
} }
//
// Print the collected performance data
//
void PerfLogging::print() { void PerfLogging::print() {
PerfEntry* pe = first; PerfEntry* pe = first;
@ -211,28 +361,34 @@ void PerfLogging::print() {
} }
} }
//
// Push collected performance data to influx (use influx configuration)
//
void PerfLogging::pushInflux() { void PerfLogging::pushInflux() {
if (!myConfig.isInfluxDb2Active()) return; if (!myConfig.isInfluxDb2Active()) return;
if (myConfig.isInfluxSSL()) {
Log.warning(
F("PERF: InfluxDB2 with SSL is not supported when pushing performance "
"data, skipping" CR));
return;
}
WiFiClient wifi;
HTTPClient http; HTTPClient http;
String serverPath = String serverPath =
String(myConfig.getInfluxDb2PushUrl()) + String(myConfig.getInfluxDb2PushUrl()) +
"/api/v2/write?org=" + String(myConfig.getInfluxDb2PushOrg()) + "/api/v2/write?org=" + String(myConfig.getInfluxDb2PushOrg()) +
"&bucket=" + String(myConfig.getInfluxDb2PushBucket()); "&bucket=" + String(myConfig.getInfluxDb2PushBucket());
http.begin(myWifi.getWifiClient(), serverPath); http.begin(wifi, serverPath);
// Create body for influxdb2, format used // Create body for influxdb2, format used
// key,host=mdns value=0.0 // key,host=mdns value=0.0
String body; String body;
body.reserve(500);
// Create the payload with performance data. // Create the payload with performance data.
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
PerfEntry* pe = first; PerfEntry* pe = first;
char buf[100]; char buf[150];
snprintf(&buf[0], sizeof(buf), "perf,host=%s,device=%s ", myConfig.getMDNS(), snprintf(&buf[0], sizeof(buf), "perf,host=%s,device=%s ", myConfig.getMDNS(),
myConfig.getID()); myConfig.getID());
body += &buf[0]; body += &buf[0];
@ -254,15 +410,28 @@ void PerfLogging::pushInflux() {
snprintf(&buf[0], sizeof(buf), "\ndebug,host=%s,device=%s ", snprintf(&buf[0], sizeof(buf), "\ndebug,host=%s,device=%s ",
myConfig.getMDNS(), myConfig.getID()); myConfig.getMDNS(), myConfig.getID());
body += &buf[0]; body += &buf[0];
snprintf( #if defined(ESP8266)
&buf[0], sizeof(buf), snprintf(&buf[0], sizeof(buf),
"angle=%.4f,gyro-ax=%d,gyro-ay=%d,gyro-az=%d,gyro-temp=%.2f,ds-temp=%.2f", "angle=%.4f,gyro-ax=%d,gyro-ay=%d,gyro-az=%d,gyro-temp=%.2f,ds-temp="
"%.2f,heap=%d,heap-frag=%d,heap-max=%d,stack=%d",
myGyro.getAngle(), myGyro.getLastGyroData().ax, myGyro.getAngle(), myGyro.getLastGyroData().ax,
myGyro.getLastGyroData().ay, myGyro.getLastGyroData().az, myGyro.getLastGyroData().ay, myGyro.getLastGyroData().az,
myGyro.getSensorTempC(), myTempSensor.getTempC(myConfig.isGyroTemp())); myGyro.getSensorTempC(),
body += &buf[0]; myTempSensor.getTempC(myConfig.isGyroTemp()), ESP.getFreeHeap(),
ESP.getHeapFragmentation(), ESP.getMaxFreeBlockSize(),
ESP.getFreeContStack());
#else // defined (ESP32)
snprintf(&buf[0], sizeof(buf),
"angle=%.4f,gyro-ax=%d,gyro-ay=%d,gyro-az=%d,gyro-temp=%.2f,ds-temp="
"%.2f,heap=%d,heap-frag=%d,heap-max=%d",
myGyro.getAngle(), myGyro.getLastGyroData().ax,
myGyro.getLastGyroData().ay, myGyro.getLastGyroData().az,
myGyro.getSensorTempC(),
myTempSensor.getTempC(myConfig.isGyroTemp()), ESP.getFreeHeap(), 0,
ESP.getMaxAllocHeap());
#endif
// Log.notice(F("PERF: data %s." CR), body.c_str() ); body += &buf[0];
#if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(HELPER_DISABLE_LOGGING)
Log.verbose(F("PERF: url %s." CR), serverPath.c_str()); Log.verbose(F("PERF: url %s." CR), serverPath.c_str());
@ -272,6 +441,7 @@ void PerfLogging::pushInflux() {
// Send HTTP POST request // Send HTTP POST request
String auth = "Token " + String(myConfig.getInfluxDb2PushToken()); String auth = "Token " + String(myConfig.getInfluxDb2PushToken());
http.addHeader(F("Authorization"), auth.c_str()); http.addHeader(F("Authorization"), auth.c_str());
http.setTimeout(myAdvancedConfig.getPushTimeout() * 1000);
int httpResponseCode = http.POST(body); int httpResponseCode = http.POST(body);
if (httpResponseCode == 204) { if (httpResponseCode == 204) {
@ -286,49 +456,42 @@ void PerfLogging::pushInflux() {
} }
http.end(); http.end();
myWifi.closeWifiClient(); wifi.stop();
tcp_cleanup();
} }
#endif // COLLECT_PERFDATA #endif // COLLECT_PERFDATA
//
// Convert float to formatted string with n decimals. Buffer should be at least
// 10 chars.
//
char* convertFloatToString(float f, char* buffer, int dec) { char* convertFloatToString(float f, char* buffer, int dec) {
dtostrf(f, 6, dec, buffer); dtostrf(f, 6, dec, buffer);
return buffer; return buffer;
} }
//
// Reduce precision to n decimals
//
float reduceFloatPrecision(float f, int dec) { float reduceFloatPrecision(float f, int dec) {
char buffer[5]; char buffer[5];
dtostrf(f, 6, dec, &buffer[0]); dtostrf(f, 6, dec, &buffer[0]);
return atof(&buffer[0]); return atof(&buffer[0]);
} }
//
// urlencode // urlencode
//
// https://circuits4you.com/2019/03/21/esp8266-url-encode-decode-example/ // https://circuits4you.com/2019/03/21/esp8266-url-encode-decode-example/
//
String urlencode(String str) { String urlencode(String str) {
String encodedString = ""; String encodedString;
encodedString.reserve(str.length() * 2);
encodedString = "";
char c; char c;
char code0; char code0;
char code1; char code1;
for (int i =0; i < static_cast<int>(str.length()); i++) { for (int i = 0; i < static_cast<int>(str.length()); i++) {
c = str.charAt(i); c = str.charAt(i);
if (isalnum(c)){ if (isalnum(c)) {
encodedString += c; encodedString += c;
} else { } else {
code1 = (c & 0xf) + '0'; code1 = (c & 0xf) + '0';
if ((c & 0xf) >9) { if ((c & 0xf) > 9) {
code1 = (c & 0xf) - 10 + 'A'; code1 = (c & 0xf) - 10 + 'A';
} }
c = (c>>4) & 0xf; c = (c >> 4) & 0xf;
code0 = c + '0'; code0 = c + '0';
if (c > 9) { if (c > 9) {
code0 = c - 10 + 'A'; code0 = c - 10 + 'A';
@ -338,29 +501,31 @@ String urlencode(String str) {
encodedString += code1; encodedString += code1;
} }
} }
//Log.verbose(F("HELP: encode=%s" CR), encodedString.c_str()); // Log.verbose(F("HELP: encode=%s" CR), encodedString.c_str());
return encodedString; return encodedString;
} }
unsigned char h2int(char c) { unsigned char h2int(char c) {
if (c >= '0' && c <='9') { if (c >= '0' && c <= '9') {
return((unsigned char)c - '0'); return ((unsigned char)c - '0');
} }
if (c >= 'a' && c <='f') { if (c >= 'a' && c <= 'f') {
return((unsigned char)c - 'a' + 10); return ((unsigned char)c - 'a' + 10);
} }
if (c >= 'A' && c <='F') { if (c >= 'A' && c <= 'F') {
return((unsigned char)c - 'A' + 10); return ((unsigned char)c - 'A' + 10);
} }
return(0); return (0);
} }
String urldecode(String str) { String urldecode(String str) {
String encodedString = ""; String encodedString;
encodedString.reserve(str.length());
encodedString = "";
char c; char c;
char code0; char code0;
char code1; char code1;
for (int i = 0; i < static_cast<int>(str.length()); i++){ for (int i = 0; i < static_cast<int>(str.length()); i++) {
c = str.charAt(i); c = str.charAt(i);
if (c == '%') { if (c == '%') {
i++; i++;
@ -374,7 +539,7 @@ String urldecode(String str) {
} }
} }
//Log.verbose(F("HELP: decode=%s" CR), encodedString.c_str()); // Log.verbose(F("HELP: decode=%s" CR), encodedString.c_str());
return encodedString; return encodedString;
} }

View File

@ -27,9 +27,25 @@ SOFTWARE.
// Includes // Includes
#include <main.hpp> #include <main.hpp>
#define ERR_FILENAME "/error.log"
#define ERR_FILENAME2 "/error2.log"
#define ERR_FILEMAXSIZE 4000
#define RUNTIME_FILENAME "/runtime.log"
// tcp cleanup
void tcp_cleanup();
// Error logging
void writeErrorLog(const char* format, ...);
void checkResetReason();
// Sleep mode // Sleep mode
void deepSleep(int t); void deepSleep(int t);
// Force config mode
bool checkPinConnected();
// Show build options // Show build options
void printBuildOptions(); void printBuildOptions();
@ -50,7 +66,7 @@ float reduceFloatPrecision(float f, int dec = 2);
// Logging via serial // Logging via serial
void printTimestamp(Print* _logOutput, int _logLevel); void printTimestamp(Print* _logOutput, int _logLevel);
void printNewline(Print* _logOutput); void printNewline(Print* _logOutput);
void printHeap(); void printHeap(String prefix = "HELP");
// Classes // Classes
class SerialDebug { class SerialDebug {
@ -59,11 +75,26 @@ class SerialDebug {
static Logging* getLog() { return &Log; } static Logging* getLog() { return &Log; }
}; };
class BatteryVoltage { class FloatHistoryLog {
private: private:
float _batteryLevel; String _fName;
float _average = 0;
float _runTime[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int _count = 0;
void save();
public: public:
explicit FloatHistoryLog(String fName);
void addEntry(float time);
float getAverage() { return _average; }
};
class BatteryVoltage {
private:
float _batteryLevel = 0;
public:
BatteryVoltage();
void read(); void read();
float getVoltage() { return _batteryLevel; } float getVoltage() { return _batteryLevel; }
}; };

View File

@ -21,6 +21,9 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include <ble.hpp>
#undef LOG_LEVEL_ERROR
#undef LOG_LEVEL_INFO
#include <calc.hpp> #include <calc.hpp>
#include <config.hpp> #include <config.hpp>
#include <gyro.hpp> #include <gyro.hpp>
@ -31,6 +34,10 @@ SOFTWARE.
#include <webserver.hpp> #include <webserver.hpp>
#include <wifi.hpp> #include <wifi.hpp>
// #define FORCE_GRAVITY_MODE
SerialDebug mySerial;
BatteryVoltage myBatteryVoltage;
// Define constats for this program // Define constats for this program
#ifdef DEACTIVATE_SLEEPMODE #ifdef DEACTIVATE_SLEEPMODE
const int interval = 1000; // ms, time to wait between changes to output const int interval = 1000; // ms, time to wait between changes to output
@ -40,17 +47,13 @@ int interval = 200; // ms, time to wait between changes to output
bool sleepModeAlwaysSkip = bool sleepModeAlwaysSkip =
false; // Flag set in web interface to override normal behaviour false; // Flag set in web interface to override normal behaviour
uint32_t loopMillis = 0; // Used for main loop to run the code every _interval_ uint32_t loopMillis = 0; // Used for main loop to run the code every _interval_
uint32_t pushMillis = 0; // Used to control how often we will send push data
uint32_t runtimeMillis; // Used to calculate the total time since start/wakeup uint32_t runtimeMillis; // Used to calculate the total time since start/wakeup
uint32_t stableGyroMillis; // Used to calculate the total time since last uint32_t stableGyroMillis; // Used to calculate the total time since last
// stable gyro reading // stable gyro reading
enum RunMode { gravityMode = 0, configurationMode = 1, wifiSetupMode = 2 };
RunMode runMode = RunMode::gravityMode; RunMode runMode = RunMode::gravityMode;
//
// Check if we should be in sleep mode
//
void checkSleepMode(float angle, float volt) { void checkSleepMode(float angle, float volt) {
#if defined(SKIP_SLEEPMODE) #if defined(SKIP_SLEEPMODE)
runMode = RunMode::configurationMode; runMode = RunMode::configurationMode;
@ -58,6 +61,12 @@ void checkSleepMode(float angle, float volt) {
return; return;
#endif #endif
#if defined(FORCE_GRAVITY_MODE)
Log.notice(F("MAIN: Forcing device into gravity mode for debugging" CR));
runMode = RunMode::gravityMode;
return;
#endif
const RawGyroData &g = myConfig.getGyroCalibration(); const RawGyroData &g = myConfig.getGyroCalibration();
if (!g.ax && !g.ay && !g.az && !g.gx && !g.gy && !g.gz) { if (!g.ax && !g.ay && !g.az && !g.gx && !g.gy && !g.gz) {
@ -75,8 +84,12 @@ void checkSleepMode(float angle, float volt) {
Log.notice(F("MAIN: Sleep mode disabled from web interface." CR)); Log.notice(F("MAIN: Sleep mode disabled from web interface." CR));
#endif #endif
runMode = RunMode::configurationMode; runMode = RunMode::configurationMode;
} else if ((volt < 4.15 && (angle > 85 && angle < 95)) || (volt > 4.15)) { } else if ((volt < myConfig.getVoltageConfig() &&
(angle > 85 && angle < 95)) ||
(volt > myConfig.getVoltageConfig())) {
runMode = RunMode::configurationMode; runMode = RunMode::configurationMode;
} else if (angle < 5 && myConfig.isStorageSleep()) {
runMode = RunMode::storageMode;
} else { } else {
runMode = RunMode::gravityMode; runMode = RunMode::gravityMode;
} }
@ -96,49 +109,62 @@ void checkSleepMode(float angle, float volt) {
volt); volt);
#endif #endif
break; break;
case RunMode::storageMode:
#if !defined(MAIN_DISABLE_LOGGING)
Log.notice(F("MAIN: run mode STORAGE (angle=%F)." CR), angle);
#endif
break;
}
// If we are in storage mode, just go back to sleep
if (runMode == RunMode::storageMode) {
Log.notice(
F("Main: Storage mode entered, going to sleep for maximum time." CR));
#if defined(ESP8266)
ESP.deepSleep(0); // indefinite sleep
#else
ESP.deepSleep(0); // indefinite sleep
#endif
} }
} }
//
// Setup
//
void setup() { void setup() {
LOG_PERF_START("run-time"); LOG_PERF_START("run-time");
LOG_PERF_START("main-setup"); LOG_PERF_START("main-setup");
runtimeMillis = millis(); runtimeMillis = millis();
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
// Add a delay so that serial is started.
// delay(3000);
#if defined (ESP8266)
Log.verbose(F("Main: Reset reason %s." CR), ESP.getResetInfo().c_str());
#else // defined (ESP32)
#endif
#endif
// Main startup // Main startup
#if defined (ESP8266) #if defined(ESP8266)
Log.notice(F("Main: Started setup for %s." CR), Log.notice(F("Main: Started setup for %s." CR),
String(ESP.getChipId(), HEX).c_str()); String(ESP.getChipId(), HEX).c_str());
#else // defined (ESP32) #else // defined (ESP32)
char buf[20]; char buf[20];
uint32_t chipId = 0; uint32_t chipId = 0;
for (int i = 0; i < 17; i = i+8) { for (int i = 0; i < 17; i = i + 8) {
chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i; chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
} }
snprintf(&buf[0], sizeof(buf), "%6x", chipId); snprintf(&buf[0], sizeof(buf), "%6x", chipId);
Log.notice(F("Main: Started setup for %s." CR), &buf[0]); Log.notice(F("Main: Started setup for %s." CR), &buf[0]);
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, HIGH);
#endif #endif
printBuildOptions(); printBuildOptions();
LOG_PERF_START("main-config-load"); LOG_PERF_START("main-config-load");
myConfig.checkFileSystem(); myConfig.checkFileSystem();
checkResetReason();
myConfig.loadFile(); myConfig.loadFile();
myWifi.init(); myWifi.init();
myHardwareConfig.loadFile(); myAdvancedConfig.loadFile();
LOG_PERF_STOP("main-config-load"); LOG_PERF_STOP("main-config-load");
sleepModeAlwaysSkip = checkPinConnected();
if (sleepModeAlwaysSkip) {
Log.notice(F("Main: Forcing config mode since D7/D8 are connected." CR));
}
// Setup watchdog // Setup watchdog
#if defined (ESP8266) #if defined(ESP8266)
ESP.wdtDisable(); ESP.wdtDisable();
ESP.wdtEnable(5000); // 5 seconds ESP.wdtEnable(5000); // 5 seconds
#else // defined (ESP32) #else // defined (ESP32)
@ -157,6 +183,9 @@ void setup() {
runMode = RunMode::wifiSetupMode; runMode = RunMode::wifiSetupMode;
} }
bool needWifi = true; // Under ESP32 we dont need wifi if only BLE is active
// in gravityMode
// Do this setup for all modes exect wifi setup // Do this setup for all modes exect wifi setup
switch (runMode) { switch (runMode) {
case RunMode::wifiSetupMode: case RunMode::wifiSetupMode:
@ -164,24 +193,39 @@ void setup() {
break; break;
default: default:
LOG_PERF_START("main-wifi-connect"); if (myGyro.setup()) {
myWifi.connect();
LOG_PERF_STOP("main-wifi-connect");
LOG_PERF_START("main-temp-setup");
myTempSensor.setup();
LOG_PERF_STOP("main-temp-setup");
if (!myGyro.setup()) {
Log.error(F("Main: Failed to initialize the gyro." CR));
} else {
LOG_PERF_START("main-gyro-read"); LOG_PERF_START("main-gyro-read");
myGyro.read(); myGyro.read();
LOG_PERF_STOP("main-gyro-read"); LOG_PERF_STOP("main-gyro-read");
} else {
Log.notice(
F("Main: Failed to connect to the gyro, software will not be able "
"to detect angles." CR));
} }
myBatteryVoltage.read(); myBatteryVoltage.read();
checkSleepMode(myGyro.getAngle(), myBatteryVoltage.getVoltage()); checkSleepMode(myGyro.getAngle(), myBatteryVoltage.getVoltage());
Log.notice(F("Main: Battery %F V, Gyro=%F, Run-mode=%d." CR),
myBatteryVoltage.getVoltage(), myGyro.getAngle(), runMode);
#if defined(ESP32)
if (!myConfig.isWifiPushActive() && runMode == RunMode::gravityMode) {
Log.notice(
F("Main: Wifi is not needed in gravity mode, skipping "
"connection." CR));
needWifi = false;
}
#endif
if (needWifi) {
LOG_PERF_START("main-wifi-connect");
myWifi.connect();
LOG_PERF_STOP("main-wifi-connect");
}
LOG_PERF_START("main-temp-setup");
myTempSensor.setup();
LOG_PERF_STOP("main-temp-setup");
break; break;
} }
@ -189,6 +233,7 @@ void setup() {
switch (runMode) { switch (runMode) {
case RunMode::configurationMode: case RunMode::configurationMode:
if (myWifi.isConnected()) { if (myWifi.isConnected()) {
Log.notice(F("Main: Activating web server." CR));
#if defined(ACTIVATE_OTA) #if defined(ACTIVATE_OTA)
LOG_PERF_START("main-wifi-ota"); LOG_PERF_START("main-wifi-ota");
if (myWifi.checkFirmwareVersion()) myWifi.updateFirmware(); if (myWifi.checkFirmwareVersion()) myWifi.updateFirmware();
@ -207,14 +252,12 @@ void setup() {
LOG_PERF_STOP("main-setup"); LOG_PERF_STOP("main-setup");
Log.notice(F("Main: Setup completed." CR)); Log.notice(F("Main: Setup completed." CR));
stableGyroMillis = millis(); // Dont include time for wifi connection pushMillis = stableGyroMillis =
millis(); // Dont include time for wifi connection
} }
//
// Main loop that does gravity readings and push data to targets // Main loop that does gravity readings and push data to targets
//
// Return true if gravity reading was successful // Return true if gravity reading was successful
//
bool loopReadGravity() { bool loopReadGravity() {
float angle = 0; float angle = 0;
@ -236,36 +279,71 @@ bool loopReadGravity() {
LOG_PERF_STOP("loop-temp-read"); LOG_PERF_STOP("loop-temp-read");
float gravitySG = calculateGravity(angle, tempC); float gravitySG = calculateGravity(angle, tempC);
float corrGravitySG = gravityTemperatureCorrectionC(gravitySG, tempC); float corrGravitySG = gravityTemperatureCorrectionC(
gravitySG, tempC, myAdvancedConfig.getDefaultCalibrationTemp());
if (myConfig.isGravityTempAdj()) {
gravitySG = corrGravitySG;
}
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
Log.verbose(F("Main: Sensor values gyro angle=%F, temp=%FC, gravity=%F, " Log.verbose(F("Main: Sensor values gyro angle=%F, temp=%FC, gravity=%F, "
"corr_gravity=%F." CR), "corr_gravity=%F." CR),
angle, tempC, gravity, corrGravity); angle, tempC, gravitySG, corrGravitySG);
#endif #endif
bool pushExpired = (abs((int32_t)(millis() - pushMillis)) >
(myConfig.getSleepInterval() * 1000));
if (myAdvancedConfig.isIgnoreLowAnges() &&
(angle < myConfig.getFormulaData().a[0])) {
Log.warning(
F("Main: Angle is lower than water, so we regard this as faulty and "
"dont send any data." CR));
pushExpired = false;
}
if (pushExpired || runMode == RunMode::gravityMode) {
pushMillis = millis();
LOG_PERF_START("loop-push"); LOG_PERF_START("loop-push");
// Force the transmission if we are going to sleep
myPushTarget.send(angle, gravitySG, corrGravitySG, tempC, #if defined(ESP32) && !defined(ESP32S2)
(millis() - runtimeMillis) / 1000, if (myConfig.isBLEActive()) {
runMode == RunMode::gravityMode ? true : false); BleSender ble(myConfig.getColorBLE());
ble.sendData(convertCtoF(tempC), gravitySG);
Log.notice(F("MAIN: Broadcast data over bluetooth." CR));
}
#endif // ESP32 && !ESP32S2
if (myWifi.isConnected()) { // no need to try if there is no wifi
// connection.
PushTarget push;
push.sendAll(angle, gravitySG, corrGravitySG, tempC,
(millis() - runtimeMillis) / 1000);
}
LOG_PERF_STOP("loop-push"); LOG_PERF_STOP("loop-push");
// Send stats to influx after each push run.
if (runMode == RunMode::configurationMode) {
LOG_PERF_PUSH();
}
}
return true; return true;
} else { } else {
Log.error(F("Main: No gyro value." CR)); // Log.error(F("MAIN: No gyro value found, the device might be moving."
// CR));
} }
return false; return false;
} }
//
// Wrapper for loopGravity that only calls every 200ms so that we dont overload // Wrapper for loopGravity that only calls every 200ms so that we dont overload
// this. // this.
//
void loopGravityOnInterval() { void loopGravityOnInterval() {
if (abs((int32_t)(millis() - loopMillis)) > interval) { if (abs((int32_t)(millis() - loopMillis)) > interval) {
loopReadGravity(); loopReadGravity();
loopMillis = millis(); loopMillis = millis();
printHeap(); // printHeap("MAIN");
LOG_PERF_START("loop-gyro-read"); LOG_PERF_START("loop-gyro-read");
myGyro.read(); myGyro.read();
LOG_PERF_STOP("loop-gyro-read"); LOG_PERF_STOP("loop-gyro-read");
@ -274,13 +352,17 @@ void loopGravityOnInterval() {
} }
} }
// bool skipRunTimeLog = false;
// Main loop that determines if device should go to sleep
//
void goToSleep(int sleepInterval) { void goToSleep(int sleepInterval) {
float volt = myBatteryVoltage.getVoltage(); float volt = myBatteryVoltage.getVoltage();
float runtime = (millis() - runtimeMillis); float runtime = (millis() - runtimeMillis);
if (!skipRunTimeLog) {
FloatHistoryLog runLog(RUNTIME_FILENAME);
runLog.addEntry(runtime);
}
Log.notice(F("MAIN: Entering deep sleep for %ds, run time %Fs, " Log.notice(F("MAIN: Entering deep sleep for %ds, run time %Fs, "
"battery=%FV." CR), "battery=%FV." CR),
sleepInterval, reduceFloatPrecision(runtime / 1000, 2), volt); sleepInterval, reduceFloatPrecision(runtime / 1000, 2), volt);
@ -292,21 +374,28 @@ void goToSleep(int sleepInterval) {
deepSleep(sleepInterval); deepSleep(sleepInterval);
} }
//
// Main loops
//
void loop() { void loop() {
switch (runMode) { switch (runMode) {
case RunMode::storageMode:
// This point is never reached, just here to remove warning.
break;
case RunMode::configurationMode: case RunMode::configurationMode:
myWebServerHandler.loop(); myWebServerHandler.loop();
myWifi.loop(); myWifi.loop();
loopGravityOnInterval(); loopGravityOnInterval();
delay(1);
// If we switched mode, dont include this in the log.
if (runMode != RunMode::configurationMode) skipRunTimeLog = true;
break; break;
case RunMode::gravityMode: case RunMode::gravityMode:
// If we didnt get a wifi connection, we enter sleep for a short time to // If we didnt get a wifi connection, we enter sleep for a short time to
// conserve battery. // conserve battery.
if (!myWifi.isConnected()) { // no connection to wifi if (!myWifi.isConnected() &&
myConfig.isWifiPushActive()) { // no connection to wifi and we have
// defined push targets.
Log.notice( Log.notice(
F("MAIN: No connection to wifi established, sleeping for 60s." CR)); F("MAIN: No connection to wifi established, sleeping for 60s." CR));
myWifi.stopDoubleReset(); myWifi.stopDoubleReset();

View File

@ -29,23 +29,113 @@ SOFTWARE.
#include <ArduinoLog.h> #include <ArduinoLog.h>
#include <stdlib.h> #include <stdlib.h>
#if defined (ESP8266) enum RunMode {
gravityMode = 0,
configurationMode = 1,
wifiSetupMode = 2,
storageMode = 3
};
extern RunMode runMode;
#if defined(ESP8266)
// Hardware config for ESP8266-d1, iSpindel hardware
// ------------------------------------------------------
#include <LittleFS.h> #include <LittleFS.h>
#define ESP_RESET ESP.reset #define ESP_RESET ESP.reset
#define PIN_SDA D3 #define PIN_SDA D3
#define PIN_SCL D4 #define PIN_SCL D4
#define PIN_CFG1 D8
#define PIN_CFG2 D7
#define PIN_DS D6 #define PIN_DS D6
#else // defined (ESP32) #define PIN_LED 2
#define LittleFS SPIFFS #define PIN_VOLT PIN_A0
#elif defined(ESP32C3)
// Hardware config for ESP32-c3-mini, iSpindel hardware
// ------------------------------------------------------
#include <FS.h>
#include <LittleFS.h>
#include "esp32c3/rom/rtc.h"
#define ESPhttpUpdate httpUpdate #define ESPhttpUpdate httpUpdate
#define ESP_RESET ESP.restart #define ESP_RESET ESP.restart
#define ESP8266WebServer WebServer #define ESP8266WebServer WebServer
#include <spiffs.h> #if defined(JTAG_DEBUG)
#define PIN_SDA 17 #define PIN_SDA 8
#define PIN_SCL 16 #define PIN_SCL 9
#define PIN_DS 19 #warning "ESP32C3 JTAG debugging enabled, using GYRO on GPIO 8/9"
#else
#define PIN_SDA 7
#define PIN_SCL 6
#endif // JTAG_DEBUG
#define PIN_CFG1 A5
#define PIN_CFG2 A4
#define PIN_DS A3
#define PIN_VOLT A0
// This should be the LED_BUILTIN, but that is also connected SDA (Gyro) so we
// cannot use both. So we point LED to pin 8 which is not used.
#define PIN_LED 8
#elif defined(ESP32S2)
// Hardware config for ESP32-s2-mini, iSpindel hardware
// ------------------------------------------------------
#include <FS.h>
#include <LittleFS.h>
#include "esp32s2/rom/rtc.h"
#define ESPhttpUpdate httpUpdate
#define ESP_RESET ESP.restart
#define ESP8266WebServer WebServer
#define PIN_SDA A17
#define PIN_SCL A15
#define PIN_CFG1 A11
#define PIN_CFG2 A10
#define PIN_DS A8
#define PIN_VOLT A2
#define PIN_LED LED_BUILTIN
#elif defined(ESP32LITE)
// Hardware config for ESP32-lite, Floaty hardware
// ------------------------------------------------------
#include <FS.h>
#include <LittleFS.h>
#include "esp32/rom/rtc.h"
#define ESPhttpUpdate httpUpdate
#define ESP_RESET ESP.restart
#define ESP8266WebServer WebServer
#define PIN_SDA A17
#define PIN_SCL A19
#define PIN_DS A3
#define PIN_VOLT A7
#define PIN_CFG1 A14
#define PIN_CFG2 A13
#define PIN_VCC A5
#define PIN_GND A18
#define PIN_LED LED_BUILTIN
#else // defined (ESP32)
// Hardware config for ESP32-d1-min, iSpindel hardware
// ------------------------------------------------------
#include <FS.h>
#include <LittleFS.h>
#include "esp32/rom/rtc.h"
#define ESPhttpUpdate httpUpdate
#define ESP_RESET ESP.restart
#define ESP8266WebServer WebServer
#define PIN_SDA D3
#define PIN_SCL D4
#define PIN_DS D6
#define PIN_CFG1 D8
#define PIN_CFG2 D7
#define PIN_LED LED_BUILTIN
#define PIN_VOLT PIN_A0
#endif #endif
#define PIN_LED 2 #if defined(USE_SERIAL_PINS) && (defined(ESP32C3) || defined(ESP32S2))
// #define EspSerial Serial0 // We cant use Serial on newer boards since this is
// using USBC port
#define EspSerial \
Serial0 // We cant use Serial on newer boards since this is using USBC port
#else
#define EspSerial Serial
#endif
#endif // SRC_MAIN_HPP_ #endif // SRC_MAIN_HPP_

View File

@ -22,10 +22,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#if defined(ESP8266) #if defined(ESP8266)
#include <ESP8266HTTPClient.h>
#include <ESP8266mDNS.h> #include <ESP8266mDNS.h>
#else // defined (ESP32) #else // defined (ESP32)
#include <HTTPClient.h>
#endif #endif
#include <MQTT.h> #include <MQTT.h>
@ -34,79 +32,158 @@ SOFTWARE.
#include <pushtarget.hpp> #include <pushtarget.hpp>
#include <wifi.hpp> #include <wifi.hpp>
PushTarget myPushTarget; #define PUSHINT_FILENAME "/push.dat"
void PushIntervalTracker::update(const int index, const int defaultValue) {
if (_counters[index] <= 0)
_counters[index] = defaultValue;
else
_counters[index]--;
}
void PushIntervalTracker::load() {
File intFile = LittleFS.open(PUSHINT_FILENAME, "r");
if (intFile) {
String line = intFile.readStringUntil('\n');
Log.notice(F("PUSH: Read interval tracker %s." CR), line.c_str());
char temp[80];
char *s, *p = &temp[0];
int i = 0;
snprintf(&temp[0], sizeof(temp), "%s", line.c_str());
while ((s = strtok_r(p, ":", &p)) != NULL) {
_counters[i++] = atoi(s);
}
intFile.close();
}
#if !defined(PUSH_DISABLE_LOGGING)
Log.verbose(F("PUSH: Parsed trackers: %d:%d:%d:%d:%d." CR), _counters[0],
_counters[1], _counters[2], _counters[3], _counters[4]);
#endif
}
//
// Update and save counters
//
void PushIntervalTracker::save() {
update(0, myAdvancedConfig.getPushIntervalHttp1());
update(1, myAdvancedConfig.getPushIntervalHttp2());
update(2, myAdvancedConfig.getPushIntervalHttp3());
update(3, myAdvancedConfig.getPushIntervalInflux());
update(4, myAdvancedConfig.getPushIntervalMqtt());
// If this feature is disabled we skip saving the file
if (!myAdvancedConfig.isPushIntervalActive()) {
#if !defined(PUSH_DISABLE_LOGGING)
Log.notice(F("PUSH: Variabled push interval disabled." CR));
#endif
LittleFS.remove(PUSHINT_FILENAME);
} else {
Log.notice(
F("PUSH: Variabled push interval enabled, updating counters." CR));
File intFile = LittleFS.open(PUSHINT_FILENAME, "w");
if (intFile) {
// Format=http1:http2:http3:influx:mqtt
intFile.printf("%d:%d:%d:%d:%d\n", _counters[0], _counters[1],
_counters[2], _counters[3], _counters[4]);
intFile.close();
}
}
}
// //
// Send the data to targets // Send the data to targets
// //
void PushTarget::send(float angle, float gravitySG, float corrGravitySG, void PushTarget::sendAll(float angle, float gravitySG, float corrGravitySG,
float tempC, float runTime, bool force) { float tempC, float runTime) {
uint32_t timePassed = abs((int32_t)(millis() - _ms)); printHeap("PUSH");
uint32_t interval = myConfig.getSleepInterval() * 1000; _http.setReuse(true);
_httpSecure.setReuse(true);
if ((timePassed < interval) && !force) {
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
Log.verbose(F("PUSH: Timer has not expired %l vs %l." CR), timePassed,
interval);
#endif
return;
}
_ms = millis();
#if defined(ESP8266)
if (ESP.getFreeContStack() < 1500) {
Log.error(F("PUSH: Low on memory, skipping push since it will crasch. "
"(stack=%d, heap=%d)." CR),
ESP.getFreeContStack(), ESP.getFreeHeap());
myWifi.closeWifiClient();
return;
}
#endif
TemplatingEngine engine; TemplatingEngine engine;
engine.initialize(angle, gravitySG, corrGravitySG, tempC, runTime); engine.initialize(angle, gravitySG, corrGravitySG, tempC, runTime);
if (myConfig.isBrewfatherActive()) { PushIntervalTracker intDelay;
LOG_PERF_START("push-brewfather"); intDelay.load();
sendBrewfather(engine);
LOG_PERF_STOP("push-brewfather");
}
if (myConfig.isHttpActive()) { if (myConfig.isHttpActive() && intDelay.useHttp1()) {
LOG_PERF_START("push-http"); LOG_PERF_START("push-http");
sendHttp(engine, 0); sendHttpPost(engine, myConfig.isHttpSSL(), 0);
LOG_PERF_STOP("push-http"); LOG_PERF_STOP("push-http");
} }
if (myConfig.isHttpActive2()) { if (myConfig.isHttp2Active() && intDelay.useHttp2()) {
LOG_PERF_START("push-http2"); LOG_PERF_START("push-http2");
sendHttp(engine, 1); sendHttpPost(engine, myConfig.isHttp2SSL(), 1);
LOG_PERF_STOP("push-http2"); LOG_PERF_STOP("push-http2");
} }
if (myConfig.isInfluxDb2Active()) { if (myConfig.isHttp3Active() && intDelay.useHttp3()) {
LOG_PERF_START("push-http3");
sendHttpGet(engine, myConfig.isHttp3SSL());
LOG_PERF_STOP("push-http3");
}
if (myConfig.isInfluxDb2Active() && intDelay.useInflux()) {
LOG_PERF_START("push-influxdb2"); LOG_PERF_START("push-influxdb2");
sendInfluxDb2(engine); sendInfluxDb2(engine, myConfig.isInfluxSSL());
LOG_PERF_STOP("push-influxdb2"); LOG_PERF_STOP("push-influxdb2");
} }
if (myConfig.isMqttActive()) { if (myConfig.isMqttActive() && intDelay.useMqtt()) {
LOG_PERF_START("push-mqtt"); LOG_PERF_START("push-mqtt");
sendMqtt(engine); sendMqtt(engine, myConfig.isMqttSSL(), true);
LOG_PERF_STOP("push-mqtt"); LOG_PERF_STOP("push-mqtt");
} }
LOG_PERF_PUSH(); engine.freeMemory();
intDelay.save();
}
//
// Check if the server can reduce the buffer size to save memory (ESP8266 only)
//
void PushTarget::probeMaxFragement(String& serverPath) {
#if defined(ESP8266) // Looks like this is feature is not supported by influxdb
// Format: http:://servername:port/path
int port = 443;
String host = serverPath.substring(8); // remove the prefix or the probe will
// fail, it needs a pure host name.
// Remove the path if it exist
int idx = host.indexOf("/");
if (idx != -1) host = host.substring(0, idx);
// If a server port is defined, lets extract that part
idx = host.indexOf(":");
if (idx != -1) {
String p = host.substring(idx + 1);
port = p.toInt();
host = host.substring(0, idx);
}
Log.notice(F("PUSH: Probing server to max fragment %s:%d" CR), host.c_str(),
port);
if (_wifiSecure.probeMaxFragmentLength(host, port, 512)) {
Log.notice(F("PUSH: Server supports smaller SSL buffer." CR));
_wifiSecure.setBufferSizes(512, 512);
}
#endif
} }
// //
// Send to influx db v2 // Send to influx db v2
// //
void PushTarget::sendInfluxDb2(TemplatingEngine& engine) { void PushTarget::sendInfluxDb2(TemplatingEngine& engine, bool isSecure) {
#if !defined(PUSH_DISABLE_LOGGING) #if !defined(PUSH_DISABLE_LOGGING)
Log.notice(F("PUSH: Sending values to influxdb2." CR)); Log.notice(F("PUSH: Sending values to influxdb2." CR));
#endif #endif
_lastCode = 0;
_lastSuccess = false;
String serverPath = String serverPath =
String(myConfig.getInfluxDb2PushUrl()) + String(myConfig.getInfluxDb2PushUrl()) +
@ -114,136 +191,263 @@ void PushTarget::sendInfluxDb2(TemplatingEngine& engine) {
"&bucket=" + String(myConfig.getInfluxDb2PushBucket()); "&bucket=" + String(myConfig.getInfluxDb2PushBucket());
String doc = engine.create(TemplatingEngine::TEMPLATE_INFLUX); String doc = engine.create(TemplatingEngine::TEMPLATE_INFLUX);
HTTPClient http;
http.begin(myWifi.getWifiClient(), serverPath);
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str()); Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
Log.verbose(F("PUSH: data %s." CR), doc.c_str()); Log.verbose(F("PUSH: data %s." CR), doc.c_str());
#endif #endif
// Send HTTP POST request
String auth = "Token " + String(myConfig.getInfluxDb2PushToken()); String auth = "Token " + String(myConfig.getInfluxDb2PushToken());
http.addHeader(F("Authorization"), auth.c_str());
int httpResponseCode = http.POST(doc);
if (httpResponseCode == 204) { if (isSecure) {
Log.notice(F("PUSH: InfluxDB2 push successful, response=%d" CR), #if defined(ESP8266)
httpResponseCode); if (runMode == RunMode::configurationMode) {
Log.notice(
F("PUSH: Skipping InfluxDB since SSL is enabled and we are in config "
"mode." CR));
_lastCode = -100;
return;
}
#endif
Log.notice(F("PUSH: InfluxDB, SSL enabled without validation." CR));
_wifiSecure.setInsecure();
probeMaxFragement(serverPath);
_httpSecure.setTimeout(myAdvancedConfig.getPushTimeout() * 1000);
_httpSecure.begin(_wifiSecure, serverPath);
_httpSecure.addHeader(F("Authorization"), auth.c_str());
_lastCode = _httpSecure.POST(doc);
} else { } else {
Log.error(F("PUSH: InfluxDB2 push failed, response=%d" CR), _http.setTimeout(myAdvancedConfig.getPushTimeout() * 1000);
httpResponseCode); _http.begin(_wifi, serverPath);
_http.addHeader(F("Authorization"), auth.c_str());
_lastCode = _http.POST(doc);
} }
http.end(); if (_lastCode == 204) {
myWifi.closeWifiClient(); _lastSuccess = true;
Log.notice(F("PUSH: InfluxDB2 push successful, response=%d" CR), _lastCode);
} else {
writeErrorLog("PUSH: Influxdb push failed response=%d", _lastCode);
}
if (isSecure) {
_httpSecure.end();
_wifiSecure.stop();
} else {
_http.end();
_wifi.stop();
}
tcp_cleanup();
} }
// //
// Send data to brewfather // Add HTTP header to request
// //
void PushTarget::sendBrewfather(TemplatingEngine& engine) { void PushTarget::addHttpHeader(HTTPClient& http, String header) {
#if !defined(PUSH_DISABLE_LOGGING) if (!header.length()) return;
Log.notice(F("PUSH: Sending values to brewfather" CR));
#endif
String serverPath = myConfig.getBrewfatherPushUrl(); int i = header.indexOf(":");
String doc = engine.create(TemplatingEngine::TEMPLATE_BREWFATHER); if (i) {
String name = header.substring(0, i);
// Your Domain name with URL path or IP address with path String value = header.substring(i + 1);
HTTPClient http; value.trim();
http.begin(myWifi.getWifiClient(), serverPath); Log.notice(F("PUSH: Adding header '%s': '%s'" CR), name.c_str(),
value.c_str());
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING) http.addHeader(name, value);
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
Log.verbose(F("PUSH: json %s." CR), doc.c_str());
#endif
// Send HTTP POST request
http.addHeader(F("Content-Type"), F("application/json"));
int httpResponseCode = http.POST(doc);
if (httpResponseCode == 200) {
Log.notice(F("PUSH: Brewfather push successful, response=%d" CR),
httpResponseCode);
} else { } else {
Log.error(F("PUSH: Brewfather push failed, response=%d" CR), writeErrorLog("PUSH: Unable to set header, invalid value %s",
httpResponseCode); header.c_str());
} }
http.end();
myWifi.closeWifiClient();
} }
// //
// Send data to http target // Send data to http target using POST
// //
void PushTarget::sendHttp(TemplatingEngine& engine, int index) { void PushTarget::sendHttpPost(TemplatingEngine& engine, bool isSecure,
int index) {
#if !defined(PUSH_DISABLE_LOGGING) #if !defined(PUSH_DISABLE_LOGGING)
Log.notice(F("PUSH: Sending values to http (%s)" CR), Log.notice(F("PUSH: Sending values to http (%s)" CR),
index ? "http2" : "http1"); index ? "http2" : "http");
#endif #endif
_lastCode = 0;
_lastSuccess = false;
String serverPath, doc; String serverPath, doc;
if (index == 0) { if (index == 0) {
serverPath = myConfig.getHttpPushUrl(); serverPath = myConfig.getHttpUrl();
doc = engine.create(TemplatingEngine::TEMPLATE_HTTP1); doc = engine.create(TemplatingEngine::TEMPLATE_HTTP1);
} else { } else {
serverPath = myConfig.getHttpPushUrl2(); serverPath = myConfig.getHttp2Url();
doc = engine.create(TemplatingEngine::TEMPLATE_HTTP2); doc = engine.create(TemplatingEngine::TEMPLATE_HTTP2);
} }
HTTPClient http;
if (serverPath.startsWith("https://")) {
myWifi.getWifiClientSecure().setInsecure();
Log.notice(F("PUSH: HTTP, SSL enabled without validation." CR));
http.begin(myWifi.getWifiClientSecure(), serverPath);
} else {
http.begin(myWifi.getWifiClient(), serverPath);
}
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str()); Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
Log.verbose(F("PUSH: json %s." CR), doc.c_str()); Log.verbose(F("PUSH: json %s." CR), doc.c_str());
#endif #endif
// Send HTTP POST request if (isSecure) {
http.addHeader(F("Content-Type"), F("application/json")); #if defined(ESP8266)
int httpResponseCode = http.POST(doc); if (runMode == RunMode::configurationMode) {
Log.notice(
F("PUSH: Skipping HTTP since SSL is enabled and we are in config "
"mode." CR));
_lastCode = -100;
return;
}
#endif
if (httpResponseCode == 200) { Log.notice(F("PUSH: HTTP, SSL enabled without validation." CR));
Log.notice(F("PUSH: HTTP push successful, response=%d" CR), _wifiSecure.setInsecure();
httpResponseCode); probeMaxFragement(serverPath);
_httpSecure.setTimeout(myAdvancedConfig.getPushTimeout() * 1000);
_httpSecure.begin(_wifiSecure, serverPath);
if (index == 0) {
addHttpHeader(_httpSecure, myConfig.getHttpHeader(0));
addHttpHeader(_httpSecure, myConfig.getHttpHeader(1));
} else { } else {
Log.error(F("PUSH: HTTP push failed, response=%d" CR), httpResponseCode); addHttpHeader(_httpSecure, myConfig.getHttp2Header(0));
addHttpHeader(_httpSecure, myConfig.getHttp2Header(1));
} }
http.end(); _lastCode = _httpSecure.POST(doc);
myWifi.closeWifiClient(); } else {
_http.setTimeout(myAdvancedConfig.getPushTimeout() * 1000);
_http.begin(_wifi, serverPath);
if (index == 0) {
addHttpHeader(_http, myConfig.getHttpHeader(0));
addHttpHeader(_http, myConfig.getHttpHeader(1));
} else {
addHttpHeader(_http, myConfig.getHttp2Header(0));
addHttpHeader(_http, myConfig.getHttp2Header(1));
}
_lastCode = _http.POST(doc);
}
if (_lastCode == 200) {
_lastSuccess = true;
Log.notice(F("PUSH: HTTP post successful, response=%d" CR), _lastCode);
} else {
writeErrorLog("PUSH: HTTP post failed response=%d http%d", _lastCode,
index + 1);
}
if (isSecure) {
_httpSecure.end();
_wifiSecure.stop();
} else {
_http.end();
_wifi.stop();
}
tcp_cleanup();
} }
// //
// Send data to http target // Send data to http target using GET
// //
void PushTarget::sendMqtt(TemplatingEngine& engine) { void PushTarget::sendHttpGet(TemplatingEngine& engine, bool isSecure) {
#if !defined(PUSH_DISABLE_LOGGING) #if !defined(PUSH_DISABLE_LOGGING)
Log.notice(F("PUSH: Sending values to mqtt." CR)); Log.notice(F("PUSH: Sending values to http3" CR));
#endif
_lastCode = 0;
_lastSuccess = false;
String serverPath;
serverPath = myConfig.getHttp3Url();
serverPath += engine.create(TemplatingEngine::TEMPLATE_HTTP3);
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
#endif #endif
if (isSecure) {
#if defined(ESP8266)
if (runMode == RunMode::configurationMode) {
Log.notice(
F("PUSH: Skipping HTTP since SSL is enabled and we are in config "
"mode." CR));
_lastCode = -100;
return;
}
#endif
Log.notice(F("PUSH: HTTP, SSL enabled without validation." CR));
_wifiSecure.setInsecure();
probeMaxFragement(serverPath);
_httpSecure.setTimeout(myAdvancedConfig.getPushTimeout() * 1000);
_httpSecure.begin(_wifiSecure, serverPath);
_lastCode = _httpSecure.GET();
} else {
_http.setTimeout(myAdvancedConfig.getPushTimeout() * 1000);
_http.begin(_wifi, serverPath);
_lastCode = _http.GET();
}
if (_lastCode == 200) {
_lastSuccess = true;
Log.notice(F("PUSH: HTTP get successful, response=%d" CR), _lastCode);
} else {
writeErrorLog("PUSH: HTTP get failed response=%d", _lastCode);
}
if (isSecure) {
_httpSecure.end();
_wifiSecure.stop();
} else {
_http.end();
_wifi.stop();
}
tcp_cleanup();
}
//
// Send data to mqtt target
//
void PushTarget::sendMqtt(TemplatingEngine& engine, bool isSecure,
bool skipHomeAssistantRegistration) {
#if !defined(PUSH_DISABLE_LOGGING)
Log.notice(F("PUSH: Sending values to mqtt. Skip HA registration=%s" CR),
skipHomeAssistantRegistration ? "yes" : "no");
#endif
_lastCode = 0;
_lastSuccess = false;
MQTTClient mqtt(512); MQTTClient mqtt(512);
String url = myConfig.getMqttUrl(); String host = myConfig.getMqttUrl();
String doc = engine.create(TemplatingEngine::TEMPLATE_MQTT); String doc = engine.create(TemplatingEngine::TEMPLATE_MQTT);
int port = myConfig.getMqttPort(); int port = myConfig.getMqttPort();
// if (url.endsWith(":8883")) { if (myConfig.isMqttSSL()) {
if (port > 8000) { #if defined(ESP8266)
// Allow secure channel, but without certificate validation if (runMode == RunMode::configurationMode) {
myWifi.getWifiClientSecure().setInsecure(); Log.notice(
F("PUSH: Skipping MQTT since SSL is enabled and we are in config "
"mode." CR));
_lastCode = -100;
return;
}
#endif
Log.notice(F("PUSH: MQTT, SSL enabled without validation." CR)); Log.notice(F("PUSH: MQTT, SSL enabled without validation." CR));
mqtt.begin(url.c_str(), port, myWifi.getWifiClientSecure()); _wifiSecure.setInsecure();
#if defined(ESP8266)
if (_wifiSecure.probeMaxFragmentLength(host, port, 512)) {
Log.notice(F("PUSH: MQTT server supports smaller SSL buffer." CR));
_wifiSecure.setBufferSizes(512, 512);
}
#endif
mqtt.setTimeout(myAdvancedConfig.getPushTimeout() * 1000);
mqtt.begin(host.c_str(), port, _wifiSecure);
} else { } else {
mqtt.begin(myConfig.getMqttUrl(), port, myWifi.getWifiClient()); mqtt.setTimeout(myAdvancedConfig.getPushTimeout() * 1000);
mqtt.begin(host.c_str(), port, _wifi);
} }
mqtt.connect(myConfig.getMDNS(), myConfig.getMqttUser(), mqtt.connect(myConfig.getMDNS(), myConfig.getMqttUser(),
@ -254,9 +458,6 @@ void PushTarget::sendMqtt(TemplatingEngine& engine) {
Log.verbose(F("PUSH: data %s." CR), doc.c_str()); Log.verbose(F("PUSH: data %s." CR), doc.c_str());
#endif #endif
// Send MQQT message(s)
mqtt.setTimeout(10); // 10 seconds timeout
int lines = 1; int lines = 1;
// Find out how many lines are in the document. Each line is one // Find out how many lines are in the document. Each line is one
// topic/message. | is used as new line. // topic/message. | is used as new line.
@ -276,11 +477,22 @@ void PushTarget::sendMqtt(TemplatingEngine& engine) {
Log.verbose(F("PUSH: topic '%s', value '%s'." CR), topic.c_str(), Log.verbose(F("PUSH: topic '%s', value '%s'." CR), topic.c_str(),
value.c_str()); value.c_str());
#endif #endif
if (mqtt.publish(topic, value)) {
Log.notice(F("PUSH: MQTT publish successful on %s" CR), topic.c_str()); if (skipHomeAssistantRegistration &&
topic.startsWith("homeassistant/sensor/")) {
Log.notice(F("PUSH: Ignoring Home Assistant registration topic %s" CR),
topic.c_str());
} else { } else {
Log.error(F("PUSH: MQTT publish failed err=%d, ret=%d" CR), if (mqtt.publish(topic, value)) {
mqtt.lastError(), mqtt.returnCode()); _lastSuccess = true;
Log.notice(F("PUSH: MQTT publish successful on %s" CR), topic.c_str());
_lastCode = 0;
} else {
_lastSuccess = false;
_lastCode = mqtt.lastError();
writeErrorLog("PUSH: MQTT push on %s failed error=%d", topic.c_str(),
_lastCode);
}
} }
index = next + 1; index = next + 1;
@ -288,7 +500,12 @@ void PushTarget::sendMqtt(TemplatingEngine& engine) {
} }
mqtt.disconnect(); mqtt.disconnect();
myWifi.closeWifiClient(); if (isSecure) {
_wifiSecure.stop();
} else {
_wifi.stop();
}
tcp_cleanup();
} }
// EOF // EOF

View File

@ -26,22 +26,61 @@ SOFTWARE.
#include <templating.hpp> #include <templating.hpp>
#if defined(ESP8266)
#include <ESP8266HTTPClient.h>
#include <WiFiClientSecure.h>
#else // defined (ESP32)
#include <HTTPClient.h>
#endif
class PushTarget { class PushTarget {
private: private:
uint32_t _ms; // Used to check that we do not post to often WiFiClient _wifi;
WiFiClientSecure _wifiSecure;
HTTPClient _http;
HTTPClient _httpSecure;
int _lastCode;
bool _lastSuccess;
void sendBrewfather(TemplatingEngine& engine); void sendHttpPost(TemplatingEngine& engine, bool isSecure, int index);
void sendHttp(TemplatingEngine& engine, int index); void sendHttpGet(TemplatingEngine& engine, bool isSecure);
void sendInfluxDb2(TemplatingEngine& engine); void addHttpHeader(HTTPClient& http, String header);
void sendMqtt(TemplatingEngine& engine); void probeMaxFragement(String& serverPath);
public: public:
PushTarget() { _ms = millis(); } void sendAll(float angle, float gravitySG, float corrGravitySG, float tempC,
void send(float angle, float gravitySG, float corrGravitySG, float tempC, float runTime);
float runTime, bool force = false);
void sendHttp1(TemplatingEngine& engine, bool isSecure) {
sendHttpPost(engine, isSecure, 0);
}
void sendHttp2(TemplatingEngine& engine, bool isSecure) {
sendHttpPost(engine, isSecure, 1);
}
void sendHttp3(TemplatingEngine& engine, bool isSecure) {
sendHttpGet(engine, isSecure);
}
void sendInfluxDb2(TemplatingEngine& engine, bool isSecure);
void sendMqtt(TemplatingEngine& engine, bool isSecure,
bool skipHomeAssistantRegistration = true);
int getLastCode() { return _lastCode; }
bool getLastSuccess() { return _lastSuccess; }
}; };
extern PushTarget myPushTarget; class PushIntervalTracker {
private:
int _counters[5] = {0, 0, 0, 0, 0};
void update(const int index, const int defaultValue);
public:
bool useHttp1() { return _counters[0] == 0 ? true : false; }
bool useHttp2() { return _counters[1] == 0 ? true : false; }
bool useHttp3() { return _counters[2] == 0 ? true : false; }
bool useInflux() { return _counters[3] == 0 ? true : false; }
bool useMqtt() { return _counters[4] == 0 ? true : false; }
void load();
void save();
};
#endif // SRC_PUSHTARGET_HPP_ #endif // SRC_PUSHTARGET_HPP_

View File

@ -21,24 +21,18 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#if defined (ESP8266) #if defined(ESP8266)
#define INCBIN_OUTPUT_SECTION ".irom.text" #define INCBIN_OUTPUT_SECTION ".irom.text"
#endif
#include <incbin.h> #include <incbin.h>
#include <resources.hpp> #include <resources.hpp>
INCBIN(IndexHtm, "html/index.min.htm");
#if defined(EMBED_HTML) INCBIN(ConfigHtm, "html/config.min.htm");
// Using minify to reduce memory usage. Reducing RAM memory usage with about 7% INCBIN(CalibrationHtm, "html/calibration.min.htm");
INCBIN(IndexHtm, "data/index.min.htm"); INCBIN(FormatHtm, "html/format.min.htm");
INCBIN(DeviceHtm, "data/device.min.htm"); INCBIN(TestHtm, "html/test.min.htm");
INCBIN(ConfigHtm, "data/config.min.htm"); INCBIN(AboutHtm, "html/about.min.htm");
INCBIN(CalibrationHtm, "data/calibration.min.htm"); INCBIN(FirmwareHtm, "html/firmware.min.htm");
INCBIN(FormatHtm, "data/format.min.htm");
INCBIN(AboutHtm, "data/about.min.htm");
#else
// Minium web interface for uploading htm files
INCBIN(UploadHtm, "data/upload.min.htm");
#endif #endif
// EOF // EOF

View File

@ -27,12 +27,22 @@ SOFTWARE.
// Common strings used in json formats. // Common strings used in json formats.
#define PARAM_ID "id" #define PARAM_ID "id"
#define PARAM_MDNS "mdns" #define PARAM_MDNS "mdns"
#define PARAM_CONFIG_VER "config-version"
#define PARAM_OTA "ota-url" #define PARAM_OTA "ota-url"
#define PARAM_SSID "wifi-ssid" #define PARAM_SSID "wifi-ssid"
#define PARAM_PASS "wifi-pass" #define PARAM_PASS "wifi-pass"
#define PARAM_PUSH_BREWFATHER "brewfather-push" #define PARAM_SSID2 "wifi-ssid2"
#define PARAM_PASS2 "wifi-pass2"
#define PARAM_RUNTIME_AVERAGE "runtime-average"
#define PARAM_TOKEN "token"
#define PARAM_TOKEN2 "token2"
#define PARAM_PUSH_HTTP "http-push" #define PARAM_PUSH_HTTP "http-push"
#define PARAM_PUSH_HTTP_H1 "http-push-h1"
#define PARAM_PUSH_HTTP_H2 "http-push-h2"
#define PARAM_PUSH_HTTP2 "http-push2" #define PARAM_PUSH_HTTP2 "http-push2"
#define PARAM_PUSH_HTTP2_H1 "http-push2-h1"
#define PARAM_PUSH_HTTP2_H2 "http-push2-h2"
#define PARAM_PUSH_HTTP3 "http-push3"
#define PARAM_PUSH_INFLUXDB2 "influxdb2-push" #define PARAM_PUSH_INFLUXDB2 "influxdb2-push"
#define PARAM_PUSH_INFLUXDB2_ORG "influxdb2-org" #define PARAM_PUSH_INFLUXDB2_ORG "influxdb2-org"
#define PARAM_PUSH_INFLUXDB2_BUCKET "influxdb2-bucket" #define PARAM_PUSH_INFLUXDB2_BUCKET "influxdb2-bucket"
@ -43,16 +53,21 @@ SOFTWARE.
#define PARAM_PUSH_MQTT_PORT "mqtt-port" #define PARAM_PUSH_MQTT_PORT "mqtt-port"
#define PARAM_SLEEP_INTERVAL "sleep-interval" #define PARAM_SLEEP_INTERVAL "sleep-interval"
#define PARAM_TEMPFORMAT "temp-format" #define PARAM_TEMPFORMAT "temp-format"
#define PARAM_VOLTAGEFACTOR "voltage-factor" #define PARAM_VOLTAGE_FACTOR "voltage-factor"
#define PARAM_VOLTAGE_CONFIG "voltage-config"
#define PARAM_GRAVITY_FORMULA "gravity-formula" #define PARAM_GRAVITY_FORMULA "gravity-formula"
#define PARAM_GRAVITY_FORMAT "gravity-format" #define PARAM_GRAVITY_FORMAT "gravity-format"
#define PARAM_GRAVITY_TEMP_ADJ "gravity-temp-adjustment" #define PARAM_GRAVITY_TEMP_ADJ "gravity-temp-adjustment"
#define PARAM_TEMP_ADJ "temp-adjustment-value" #define PARAM_TEMP_ADJ "temp-adjustment-value"
#define PARAM_GYRO_CALIBRATION "gyro-calibration-data" #define PARAM_GYRO_CALIBRATION "gyro-calibration-data"
#define PARAM_GYRO_TEMP "gyro-temp" #define PARAM_GYRO_TEMP "gyro-temp"
#define PARAM_STORAGE_SLEEP "storage-sleep"
#define PARAM_FORMULA_DATA "formula-calculation-data" #define PARAM_FORMULA_DATA "formula-calculation-data"
#define PARAM_APP_NAME "app-name" #define PARAM_FILES "files"
#define PARAM_FILE_NAME "file-name"
#define PARAM_FILE_SIZE "file-size"
#define PARAM_APP_VER "app-ver" #define PARAM_APP_VER "app-ver"
#define PARAM_APP_BUILD "app-build"
#define PARAM_ANGLE "angle" #define PARAM_ANGLE "angle"
#define PARAM_GRAVITY "gravity" #define PARAM_GRAVITY "gravity"
#define PARAM_TEMP_C "temp-c" #define PARAM_TEMP_C "temp-c"
@ -61,16 +76,31 @@ SOFTWARE.
#define PARAM_SLEEP_MODE "sleep-mode" #define PARAM_SLEEP_MODE "sleep-mode"
#define PARAM_RSSI "rssi" #define PARAM_RSSI "rssi"
#define PARAM_ERROR "error" #define PARAM_ERROR "error"
#define PARAM_PLATFORM "platform"
#define PARAM_BLE "ble"
#define PARAM_HW_GYRO_READ_COUNT "gyro-read-count" #define PARAM_HW_GYRO_READ_COUNT "gyro-read-count"
#define PARAM_HW_GYRO_READ_DELAY "gyro-read-delay" // #define PARAM_HW_GYRO_READ_DELAY "gyro-read-delay"
#define PARAM_HW_GYRO_MOVING_THREASHOLD "gyro-moving-threashold" #define PARAM_HW_GYRO_MOVING_THREASHOLD "gyro-moving-threashold"
#define PARAM_HW_FORMULA_DEVIATION "formula-max-deviation" #define PARAM_HW_FORMULA_DEVIATION "formula-max-deviation"
#define PARAM_HW_FORMULA_CALIBRATION_TEMP "formula-calibration-temp" #define PARAM_HW_FORMULA_CALIBRATION_TEMP "formula-calibration-temp"
#define PARAM_HW_WIFI_PORTALTIMEOUT "wifi-portaltimeout" #define PARAM_HW_WIFI_PORTAL_TIMEOUT "wifi-portal-timeout"
#define PARAM_HW_WIFI_CONNECT_TIMEOUT "wifi-connect-timeout"
#define PARAM_HW_TEMPSENSOR_RESOLUTION "tempsensor-resolution"
#define PARAM_HW_PUSH_TIMEOUT "push-timeout"
#define PARAM_HW_PUSH_INTERVAL_HTTP1 "int-http1"
#define PARAM_HW_PUSH_INTERVAL_HTTP2 "int-http2"
#define PARAM_HW_PUSH_INTERVAL_HTTP3 "int-http3"
#define PARAM_HW_PUSH_INTERVAL_INFLUX "int-influx"
#define PARAM_HW_PUSH_INTERVAL_MQTT "int-mqtt"
#define PARAM_HW_IGNORE_LOW_ANGLES "ignore-low-angles"
#define PARAM_FORMAT_HTTP1 "http-1" #define PARAM_FORMAT_HTTP1 "http-1"
#define PARAM_FORMAT_HTTP2 "http-2" #define PARAM_FORMAT_HTTP2 "http-2"
#define PARAM_FORMAT_BREWFATHER "brewfather" #define PARAM_FORMAT_HTTP3 "http-3"
#define PARAM_FORMAT_INFLUXDB "influxdb" #define PARAM_FORMAT_INFLUXDB "influxdb"
#define PARAM_FORMAT_MQTT "mqtt" #define PARAM_FORMAT_MQTT "mqtt"
#define PARAM_PUSH_FORMAT "format"
#define PARAM_PUSH_SUCCESS "success"
#define PARAM_PUSH_CODE "code"
#define PARAM_PUSH_ENABLED "enabled"
#endif // SRC_RESOURCES_HPP_ #endif // SRC_RESOURCES_HPP_

View File

@ -21,54 +21,54 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include <templating.hpp>
#include <config.hpp> #include <config.hpp>
#include <templating.hpp>
#if defined (ESP8266) #if defined(ESP8266)
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#else // defined (ESP32) #else // defined (ESP32)
#include <WiFi.h> #include <WiFi.h>
#endif #endif
// Use iSpindle format for compatibility // Use iSpindle format for compatibility, HTTP POST
const char iSpindleFormat[] PROGMEM = const char iSpindleFormat[] PROGMEM =
"{" "{"
"\"name\" : \"${mdns}\", " "\"name\" : \"${mdns}\", "
"\"ID\": \"${id}\", " "\"ID\": \"${id}\", "
"\"token\" : \"gravmon\", " "\"token\" : \"${token}\", "
"\"interval\": ${sleep-interval}, " "\"interval\": ${sleep-interval}, "
"\"temperature\": ${temp}, " "\"temperature\": ${temp}, "
"\"temp-units\": \"${temp-unit}\", " "\"temp_units\": \"${temp-unit}\", "
"\"gravity\": ${gravity}, " "\"gravity\": ${gravity}, "
"\"angle\": ${angle}, " "\"angle\": ${angle}, "
"\"battery\": ${battery}, " "\"battery\": ${battery}, "
"\"rssi\": ${rssi}, " "\"RSSI\": ${rssi}, "
"\"corr-gravity\": ${corr-gravity}, " "\"corr-gravity\": ${corr-gravity}, "
"\"gravity-unit\": \"${gravity-unit}\", " "\"gravity-unit\": \"${gravity-unit}\", "
"\"run-time\": ${run-time} " "\"run-time\": ${run-time} "
"}"; "}";
const char brewfatherFormat[] PROGMEM = // Format for an HTTP GET
"{" const char iHttpGetFormat[] PROGMEM =
"\"name\": \"${mdns}\"," "?name=${mdns}"
"\"temp\": ${temp}, " "&id=${id}"
"\"aux_temp\": 0, " "&token=${token2}"
"\"ext_temp\": 0, " "&interval=${sleep-interval}"
"\"temp_unit\": \"${temp-unit}\", " "&temperature=${temp}"
"\"gravity\": ${gravity}, " "&temp-units=${temp-unit}"
"\"gravity_unit\": \"${gravity-unit}\", " "&gravity=${gravity}"
"\"pressure\": 0, " "&angle=${angle}"
"\"pressure_unit\": \"PSI\", " "&battery=${battery}"
"\"ph\": 0, " "&rssi=${rssi}"
"\"bpm\": 0, " "&corr-gravity=${corr-gravity}"
"\"comment\": \"\", " "&gravity-unit=${gravity-unit}"
"\"beer\": \"\", " "&run-time=${run-time}";
"\"battery\": ${battery}"
"}";
const char influxDbFormat[] PROGMEM = const char influxDbFormat[] PROGMEM =
"measurement,host=${mdns},device=${id},temp-format=${temp-unit},gravity-format=${gravity-unit} " "measurement,host=${mdns},device=${id},temp-format=${temp-unit},gravity-"
"gravity=${gravity},corr-gravity=${corr-gravity},angle=${angle},temp=${temp},battery=${battery}," "format=${gravity-unit} "
"gravity=${gravity},corr-gravity=${corr-gravity},angle=${angle},temp=${"
"temp},battery=${battery},"
"rssi=${rssi}\n"; "rssi=${rssi}\n";
const char mqttFormat[] PROGMEM = const char mqttFormat[] PROGMEM =
@ -80,14 +80,14 @@ const char mqttFormat[] PROGMEM =
"ispindel/${mdns}/interval:${sleep-interval}|" "ispindel/${mdns}/interval:${sleep-interval}|"
"ispindel/${mdns}/RSSI:${rssi}|"; "ispindel/${mdns}/RSSI:${rssi}|";
// void TemplatingEngine::initialize(float angle, float gravitySG,
// Initialize the variables float corrGravitySG, float tempC,
// float runTime) {
void TemplatingEngine::initialize(float angle, float gravitySG, float corrGravitySG, float tempC, float runTime) {
// Names // Names
setVal(TPL_MDNS, myConfig.getMDNS()); setVal(TPL_MDNS, myConfig.getMDNS());
setVal(TPL_ID, myConfig.getID()); setVal(TPL_ID, myConfig.getID());
setVal(TPL_TOKEN, myConfig.getToken());
setVal(TPL_TOKEN2, myConfig.getToken2());
// Temperature // Temperature
if (myConfig.isTempC()) { if (myConfig.isTempC()) {
@ -116,8 +116,7 @@ void TemplatingEngine::initialize(float angle, float gravitySG, float corrGravit
if (myConfig.isGravitySG()) { if (myConfig.isGravitySG()) {
setVal(TPL_GRAVITY, gravitySG, 4); setVal(TPL_GRAVITY, gravitySG, 4);
setVal(TPL_GRAVITY_CORR, corrGravitySG, 4); setVal(TPL_GRAVITY_CORR, corrGravitySG, 4);
} } else {
else {
setVal(TPL_GRAVITY, convertToPlato(gravitySG), 1); setVal(TPL_GRAVITY, convertToPlato(gravitySG), 1);
setVal(TPL_GRAVITY_CORR, convertToPlato(corrGravitySG), 1); setVal(TPL_GRAVITY_CORR, convertToPlato(corrGravitySG), 1);
} }
@ -128,64 +127,84 @@ void TemplatingEngine::initialize(float angle, float gravitySG, float corrGravit
setVal(TPL_GRAVITY_CORR_P, convertToPlato(corrGravitySG), 1); setVal(TPL_GRAVITY_CORR_P, convertToPlato(corrGravitySG), 1);
setVal(TPL_GRAVITY_UNIT, myConfig.getGravityFormat()); setVal(TPL_GRAVITY_UNIT, myConfig.getGravityFormat());
setVal(TPL_APP_VER, CFG_APPVER);
setVal(TPL_APP_BUILD, CFG_GITREV);
#if LOG_LEVEL == 6 #if LOG_LEVEL == 6
// dumpAll(); // dumpAll();
#endif #endif
} }
// // the useDefaultTemplate param is there to support unit tests.
// Create the data using defined template. const char* TemplatingEngine::create(TemplatingEngine::Templates idx,
// bool useDefaultTemplate) {
const String& TemplatingEngine::create(TemplatingEngine::Templates idx) {
String fname; String fname;
_baseTemplate.reserve(600);
// Load templates from memory // Load templates from memory
switch (idx) { switch (idx) {
case TEMPLATE_HTTP1: case TEMPLATE_HTTP1:
baseTemplate = String(iSpindleFormat); _baseTemplate = String(iSpindleFormat);
fname = TPL_FNAME_HTTP1; fname = TPL_FNAME_HTTP1;
break; break;
case TEMPLATE_HTTP2: case TEMPLATE_HTTP2:
baseTemplate = String(iSpindleFormat); _baseTemplate = String(iSpindleFormat);
fname = TPL_FNAME_HTTP2; fname = TPL_FNAME_HTTP2;
break; break;
case TEMPLATE_BREWFATHER: case TEMPLATE_HTTP3:
baseTemplate = String(brewfatherFormat); _baseTemplate = String(iHttpGetFormat);
//fname = TPL_FNAME_BREWFATHER; fname = TPL_FNAME_HTTP3;
break; break;
case TEMPLATE_INFLUX: case TEMPLATE_INFLUX:
baseTemplate = String(influxDbFormat); _baseTemplate = String(influxDbFormat);
fname = TPL_FNAME_INFLUXDB; fname = TPL_FNAME_INFLUXDB;
break; break;
case TEMPLATE_MQTT: case TEMPLATE_MQTT:
baseTemplate = String(mqttFormat); _baseTemplate = String(mqttFormat);
fname = TPL_FNAME_MQTT; fname = TPL_FNAME_MQTT;
break; break;
} }
// TODO: Add code to load templates from disk if they exist. if (!useDefaultTemplate) {
File file = LittleFS.open(fname, "r"); File file = LittleFS.open(fname, "r");
if (file) { if (file) {
char buf[file.size()+1]; char buf[file.size() + 1];
memset(&buf[0], 0, file.size()+1); memset(&buf[0], 0, file.size() + 1);
file.readBytes(&buf[0], file.size()); file.readBytes(&buf[0], file.size());
baseTemplate = String(&buf[0]); _baseTemplate = String(&buf[0]);
file.close(); file.close();
Log.notice(F("TPL : Template loaded from disk %s." CR), fname.c_str()); Log.notice(F("TPL : Template loaded from disk %s." CR), fname.c_str());
} }
}
#if LOG_LEVEL == 6 #if LOG_LEVEL == 6
//Log.verbose(F("TPL : Base '%s'." CR), baseTemplate.c_str()); // Log.verbose(F("TPL : Base '%s'." CR), baseTemplate.c_str());
#endif #endif
// Insert data into template. // Insert data into template.
transform(baseTemplate); transform();
_baseTemplate.clear();
#if LOG_LEVEL == 6 #if LOG_LEVEL == 6
//Log.verbose(F("TPL : Transformed '%s'." CR), baseTemplate.c_str()); // Log.verbose(F("TPL : Transformed '%s'." CR), baseTemplate.c_str());
#endif #endif
return baseTemplate; if (_output) return _output;
return "";
}
// 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 // EOF

View File

@ -26,13 +26,16 @@ SOFTWARE.
// Includes // Includes
#include <Arduino.h> #include <Arduino.h>
#include <main.hpp>
#include <helper.hpp>
#include <algorithm> #include <algorithm>
#include <helper.hpp>
#include <main.hpp>
// Templating variables // Templating variables
#define TPL_MDNS "${mdns}" #define TPL_MDNS "${mdns}"
#define TPL_ID "${id}" #define TPL_ID "${id}"
#define TPL_TOKEN "${token}"
#define TPL_TOKEN2 "${token2}"
#define TPL_SLEEP_INTERVAL "${sleep-interval}" #define TPL_SLEEP_INTERVAL "${sleep-interval}"
#define TPL_TEMP "${temp}" #define TPL_TEMP "${temp}"
#define TPL_TEMP_C "${temp-c}" #define TPL_TEMP_C "${temp-c}"
@ -50,15 +53,17 @@ SOFTWARE.
#define TPL_GRAVITY_CORR_G "${corr-gravity-sg}" #define TPL_GRAVITY_CORR_G "${corr-gravity-sg}"
#define TPL_GRAVITY_CORR_P "${corr-gravity-plato}" #define TPL_GRAVITY_CORR_P "${corr-gravity-plato}"
#define TPL_GRAVITY_UNIT "${gravity-unit}" // G or P #define TPL_GRAVITY_UNIT "${gravity-unit}" // G or P
#define TPL_APP_VER "${app-ver}"
#define TPL_APP_BUILD "${app-build}"
#define TPL_FNAME_HTTP1 "/http-1.tpl" #define TPL_FNAME_HTTP1 "/http-1.tpl"
#define TPL_FNAME_HTTP2 "/http-2.tpl" #define TPL_FNAME_HTTP2 "/http-2.tpl"
// #define TPL_FNAME_BREWFATHER "/brewfather.tpl" #define TPL_FNAME_HTTP3 "/http-3.tpl"
#define TPL_FNAME_INFLUXDB "/influxdb.tpl" #define TPL_FNAME_INFLUXDB "/influxdb.tpl"
#define TPL_FNAME_MQTT "/mqtt.tpl" #define TPL_FNAME_MQTT "/mqtt.tpl"
extern const char iSpindleFormat[] PROGMEM; extern const char iSpindleFormat[] PROGMEM;
extern const char brewfatherFormat[] PROGMEM; extern const char iHttpGetFormat[] PROGMEM;
extern const char influxDbFormat[] PROGMEM; extern const char influxDbFormat[] PROGMEM;
extern const char mqttFormat[] PROGMEM; extern const char mqttFormat[] PROGMEM;
@ -70,62 +75,111 @@ class TemplatingEngine {
String val; String val;
}; };
KeyVal items[19] = { KeyVal _items[23] = {{TPL_MDNS, ""}, {TPL_ID, ""},
{ TPL_MDNS, "" }, {TPL_SLEEP_INTERVAL, ""}, {TPL_TEMP, ""},
{ TPL_ID, "" }, {TPL_TEMP_C, ""}, {TPL_TEMP_F, ""},
{ TPL_SLEEP_INTERVAL, "" }, {TPL_TEMP_UNITS, ""}, {TPL_BATTERY, ""},
{ TPL_TEMP, "" }, {TPL_RSSI, ""}, {TPL_RUN_TIME, ""},
{ TPL_TEMP_C, "" }, {TPL_ANGLE, ""}, {TPL_TILT, ""},
{ TPL_TEMP_F, "" }, {TPL_GRAVITY, ""}, {TPL_GRAVITY_G, ""},
{ TPL_TEMP_UNITS, "" }, {TPL_GRAVITY_P, ""}, {TPL_GRAVITY_CORR, ""},
{ TPL_BATTERY, "" }, {TPL_GRAVITY_CORR_G, ""}, {TPL_GRAVITY_CORR_P, ""},
{ TPL_RSSI, "" }, {TPL_GRAVITY_UNIT, ""}, {TPL_TOKEN, ""},
{ TPL_RUN_TIME, "" }, {TPL_TOKEN2, ""}, {TPL_APP_VER, ""},
{ TPL_ANGLE, "" }, {TPL_APP_BUILD, ""}};
{ TPL_TILT, "" },
{ TPL_GRAVITY, "" },
{ TPL_GRAVITY_G, "" },
{ TPL_GRAVITY_P, "" },
{ TPL_GRAVITY_CORR, "" },
{ TPL_GRAVITY_CORR_G, "" },
{ TPL_GRAVITY_CORR_P, "" },
{ TPL_GRAVITY_UNIT, "" }
};
char buffer[20]; char _buffer[20] = "";
String baseTemplate; String _baseTemplate;
char *_output = 0;
void setVal(String key, float val, int dec = 2) { String s = convertFloatToString(val, &buffer[0], dec); s.trim(); setVal(key, s); } void setVal(String key, float val, int dec = 2) {
String s = convertFloatToString(val, &_buffer[0], dec);
s.trim();
setVal(key, s);
}
void setVal(String key, int val) { setVal(key, String(val)); } void setVal(String key, int val) { setVal(key, String(val)); }
void setVal(String key, char val) { setVal(key, String(val)); } void setVal(String key, char val) { setVal(key, String(val)); }
void setVal(String key, String val) { void setVal(String key, String val) {
int max = sizeof(items)/sizeof(KeyVal); int max = sizeof(_items) / sizeof(KeyVal);
for (int i = 0; i < max; i++) { for (int i = 0; i < max; i++) {
if (items[i].key.equals(key)) { if (_items[i].key.equals(key)) {
items[i].val = val; _items[i].val = val;
return; return;
} }
} }
Log.error(F("TPL : Key not found %s." CR), key.c_str()); Log.warning(F("TPL : Key not found %s." CR), key.c_str());
} }
void transform(String& s) { void transform() {
int max = sizeof(items)/sizeof(KeyVal); const char *format = _baseTemplate.c_str();
int len = _baseTemplate.length();
int size = len;
// Lets check how much memory will be needed to transform the template
for (int j = 0; j < len - 2; j++) {
if (*(format + j) == '$' && *(format + j + 1) == '{') {
// Start of format tag found
int max = sizeof(_items) / sizeof(KeyVal);
for (int i = 0; i < max; i++) { for (int i = 0; i < max; i++) {
while (s.indexOf(items[i].key) != -1) if (strncmp(format + j, _items[i].key.c_str(),
s.replace(items[i].key, items[i].val); _items[i].key.length()) == 0) {
// Found key
size = size - _items[i].key.length() + _items[i].val.length();
} }
} }
}
}
freeMemory(); // In case this is reused
_output = static_cast<char *>(malloc(size + 20));
if (!_output) {
Log.error(F("TPL : Unable to allocate memory for transforming template, "
"needed %d." CR),
size);
return;
}
memset(_output, 0, size + 20);
// Lets do the transformation
int k = 0;
for (int j = 0; j < len - 2; j++) {
if (*(format + j) == '$' && *(format + j + 1) == '{') {
// Start of format tag found
int max = sizeof(_items) / sizeof(KeyVal);
for (int i = 0; i < max; i++) {
if (strncmp(format + j, _items[i].key.c_str(),
_items[i].key.length()) == 0) {
// Found key
strncat(_output, format + k, j - k);
strncat(_output, _items[i].val.c_str(), _items[i].val.length());
k = j + _items[i].key.length();
}
}
}
}
// strncat(_output, format + k, size - k);
strncat(_output, format + k, strlen(format + k));
Log.notice(F("TPL : Transformed template %d chars to %d chars" CR),
strlen(format), strlen(_output));
#if LOG_LEVEL == 6
printHeap("TPL ");
Log.verboseln(format);
Log.verboseln(_output);
#endif
}
void dumpAll() { void dumpAll() {
int max = sizeof(items)/sizeof(KeyVal); int max = sizeof(_items) / sizeof(KeyVal);
for (int i = 0; i < max; i++) { for (int i = 0; i < max; i++) {
Serial.print( "Key=\'" ); EspSerial.print("Key=\'");
Serial.print( items[i].key.c_str() ); EspSerial.print(_items[i].key.c_str());
Serial.print( "\', Val=\'" ); EspSerial.print("\', Val=\'");
Serial.print( items[i].val.c_str() ); EspSerial.print(_items[i].val.c_str());
Serial.println( "\'" ); EspSerial.println("\'");
} }
} }
@ -133,13 +187,23 @@ class TemplatingEngine {
enum Templates { enum Templates {
TEMPLATE_HTTP1 = 0, TEMPLATE_HTTP1 = 0,
TEMPLATE_HTTP2 = 1, TEMPLATE_HTTP2 = 1,
TEMPLATE_BREWFATHER = 2, TEMPLATE_HTTP3 = 2,
TEMPLATE_INFLUX = 3, TEMPLATE_INFLUX = 3,
TEMPLATE_MQTT = 4 TEMPLATE_MQTT = 4
}; };
void initialize(float angle, float gravitySG, float corrGravitySG, float tempC, float runTime); TemplatingEngine() {}
const String& create(TemplatingEngine::Templates idx); ~TemplatingEngine() { freeMemory(); }
void freeMemory() {
if (_output) free(_output);
_output = 0;
}
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);
}; };
#endif // SRC_TEMPLATING_HPP_ #endif // SRC_TEMPLATING_HPP_

View File

@ -32,19 +32,10 @@ SOFTWARE.
OneWire myOneWire(PIN_DS); OneWire myOneWire(PIN_DS);
DallasTemperature mySensors(&myOneWire); DallasTemperature mySensors(&myOneWire);
#define TEMPERATURE_PRECISION 9
TempSensor myTempSensor; TempSensor myTempSensor;
//
// Setup DS18B20 temp sensor. Doing setup is not that time consuming.
//
void TempSensor::setup() { void TempSensor::setup() {
#if defined(SIMULATE_TEMP)
hasSensors = true;
return;
#endif
#if LOG_LEVEL == 6 && !defined(TSEN_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(TSEN_DISABLE_LOGGING)
Log.verbose(F("TSEN: Looking for temp sensors." CR)); Log.verbose(F("TSEN: Looking for temp sensors." CR));
#endif #endif
@ -52,10 +43,10 @@ void TempSensor::setup() {
if (mySensors.getDS18Count()) { if (mySensors.getDS18Count()) {
#if !defined(TSEN_DISABLE_LOGGING) #if !defined(TSEN_DISABLE_LOGGING)
Log.notice(F("TSEN: Found %d temperature sensor(s)." CR), Log.notice(
mySensors.getDS18Count()); F("TSEN: Found %d temperature sensor(s). Using %d resolution" CR),
mySensors.getDS18Count(), myAdvancedConfig.getTempSensorResolution());
#endif #endif
mySensors.setResolution(TEMPERATURE_PRECISION);
} }
// Set the temp sensor adjustment values // Set the temp sensor adjustment values
@ -67,14 +58,7 @@ void TempSensor::setup() {
#endif #endif
} }
//
// Retrieving value from sensor, value is in Celcius
//
float TempSensor::getValue(bool useGyro) { float TempSensor::getValue(bool useGyro) {
#if defined(SIMULATE_TEMP)
return 21;
#endif
if (useGyro) { if (useGyro) {
// When using the gyro temperature only the first read value will be // When using the gyro temperature only the first read value will be
// accurate so we will use this for processing. // accurate so we will use this for processing.
@ -95,6 +79,7 @@ float TempSensor::getValue(bool useGyro) {
} }
// Read the sensors // Read the sensors
mySensors.setResolution(myAdvancedConfig.getTempSensorResolution());
mySensors.requestTemperatures(); mySensors.requestTemperatures();
float c = 0; float c = 0;

File diff suppressed because it is too large Load Diff

View File

@ -24,26 +24,46 @@ SOFTWARE.
#ifndef SRC_WEBSERVER_HPP_ #ifndef SRC_WEBSERVER_HPP_
#define SRC_WEBSERVER_HPP_ #define SRC_WEBSERVER_HPP_
#if defined (ESP8266) #if defined(ESP8266)
#include <ESP8266WebServer.h> #include <ESP8266WebServer.h>
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESP8266mDNS.h> #include <ESP8266mDNS.h>
#else // defined (ESP32) #define MAX_SKETCH_SPACE 1044464
#else
#include <ESPmDNS.h>
#include <Update.h>
#include <WebServer.h> #include <WebServer.h>
#include <WiFi.h> #include <WiFi.h>
#include <ESPmDNS.h> #define MAX_SKETCH_SPACE 1835008
#endif #endif
#include <incbin.h>
#if defined(EMBED_HTML) #if defined(ESP8266)
#include <incbin.h>
INCBIN_EXTERN(IndexHtm); INCBIN_EXTERN(IndexHtm);
INCBIN_EXTERN(DeviceHtm);
INCBIN_EXTERN(ConfigHtm); INCBIN_EXTERN(ConfigHtm);
INCBIN_EXTERN(CalibrationHtm); INCBIN_EXTERN(CalibrationHtm);
INCBIN_EXTERN(FormatHtm); INCBIN_EXTERN(FormatHtm);
INCBIN_EXTERN(TestHtm);
INCBIN_EXTERN(AboutHtm); INCBIN_EXTERN(AboutHtm);
#else INCBIN_EXTERN(FirmwareHtm);
INCBIN_EXTERN(UploadHtm); #else // ESP32
extern const uint8_t indexHtmStart[] asm("_binary_html_index_min_htm_start");
extern const uint8_t indexHtmEnd[] asm("_binary_html_index_min_htm_end");
extern const uint8_t configHtmStart[] asm("_binary_html_config_min_htm_start");
extern const uint8_t configHtmEnd[] asm("_binary_html_config_min_htm_end");
extern const uint8_t calibrationHtmStart[] asm(
"_binary_html_calibration_min_htm_start");
extern const uint8_t calibrationHtmEnd[] asm(
"_binary_html_calibration_min_htm_end");
extern const uint8_t formatHtmStart[] asm("_binary_html_format_min_htm_start");
extern const uint8_t formatHtmEnd[] asm("_binary_html_format_min_htm_end");
extern const uint8_t testHtmStart[] asm("_binary_html_test_min_htm_start");
extern const uint8_t testHtmEnd[] asm("_binary_html_test_min_htm_end");
extern const uint8_t aboutHtmStart[] asm("_binary_html_about_min_htm_start");
extern const uint8_t aboutHtmEnd[] asm("_binary_html_about_min_htm_end");
extern const uint8_t firmwareHtmStart[] asm(
"_binary_html_firmware_min_htm_start");
extern const uint8_t firmwareHtmEnd[] asm("_binary_html_firmware_min_htm_end");
#endif #endif
class WebServerHandler { class WebServerHandler {
@ -51,25 +71,27 @@ class WebServerHandler {
ESP8266WebServer* _server = 0; ESP8266WebServer* _server = 0;
File _uploadFile; File _uploadFile;
int _lastFormulaCreateError = 0; int _lastFormulaCreateError = 0;
int _uploadReturn = 200;
void webHandleConfig(); void webHandleConfig();
void webHandleFormulaWrite(); void webHandleFormulaWrite();
void webHandleFormulaRead(); void webHandleFormulaRead();
void webHandleConfigAdvancedRead();
void webHandleConfigAdvancedWrite();
void webHandleConfigHardware(); void webHandleConfigHardware();
void webHandleConfigGravity(); void webHandleConfigGravity();
void webHandleConfigPush(); void webHandleConfigPush();
void webHandleConfigDevice(); void webHandleConfigDevice();
void webHandleConfigFormatRead(); void webHandleConfigFormatRead();
void webHandleConfigFormatWrite(); void webHandleConfigFormatWrite();
void webHandleTestPush();
void webHandleStatusSleepmode(); void webHandleStatusSleepmode();
void webHandleClearWIFI(); void webHandleClearWIFI();
void webHandleStatus(); void webHandleStatus();
void webHandleFactoryReset(); void webHandleFactoryDefaults();
void webHandleCalibrate(); void webHandleCalibrate();
void webHandleUploadFile(); void webHandleUploadFile();
void webHandleUpload(); void webHandleLogClear();
void webHandleDevice();
void webHandleDeviceParam();
void webHandlePageNotFound(); void webHandlePageNotFound();
String readFile(String fname); String readFile(String fname);
@ -78,16 +100,12 @@ class WebServerHandler {
String getRequestArguments(); String getRequestArguments();
// Inline functions. // Inline functions.
void webReturnOK() { _server->send(200); } void webReturnOK() { _server->send(_uploadReturn); }
#if defined(EMBED_HTML) #if defined(ESP8266)
void webReturnIndexHtm() { void webReturnIndexHtm() {
_server->send_P(200, "text/html", (const char*)gIndexHtmData, _server->send_P(200, "text/html", (const char*)gIndexHtmData,
gIndexHtmSize); gIndexHtmSize);
} }
void webReturnDeviceHtm() {
_server->send_P(200, "text/html", (const char*)gDeviceHtmData,
gDeviceHtmSize);
}
void webReturnConfigHtm() { void webReturnConfigHtm() {
_server->send_P(200, "text/html", (const char*)gConfigHtmData, _server->send_P(200, "text/html", (const char*)gConfigHtmData,
gConfigHtmSize); gConfigHtmSize);
@ -104,27 +122,49 @@ class WebServerHandler {
_server->send_P(200, "text/html", (const char*)gAboutHtmData, _server->send_P(200, "text/html", (const char*)gAboutHtmData,
gAboutHtmSize); gAboutHtmSize);
} }
#else void webReturnTestHtm() {
void webReturnUploadHtm() { _server->send_P(200, "text/html", (const char*)gTestHtmData, gTestHtmSize);
_server->send_P(200, "text/html", (const char*)gUploadHtmData, }
gUploadHtmSize); void webReturnFirmwareHtm() {
_server->send_P(200, "text/html", (const char*)gFirmwareHtmData,
gFirmwareHtmSize);
}
#else // ESP32
void webReturnIndexHtm() {
_server->send_P(200, "text/html", (const char*)indexHtmStart,
strlen(reinterpret_cast<const char*>(&indexHtmStart[0])));
}
void webReturnConfigHtm() {
_server->send_P(200, "text/html", (const char*)configHtmStart,
strlen(reinterpret_cast<const char*>(&configHtmStart[0])));
}
void webReturnCalibrationHtm() {
_server->send_P(
200, "text/html", (const char*)calibrationHtmStart,
strlen(reinterpret_cast<const char*>(&calibrationHtmStart[0])));
}
void webReturnFormatHtm() {
_server->send_P(200, "text/html", (const char*)formatHtmStart,
strlen(reinterpret_cast<const char*>(&formatHtmStart[0])));
}
void webReturnAboutHtm() {
_server->send_P(200, "text/html", (const char*)aboutHtmStart,
strlen(reinterpret_cast<const char*>(&aboutHtmStart[0])));
}
void webReturnTestHtm() {
_server->send_P(200, "text/html", (const char*)testHtmStart,
strlen(reinterpret_cast<const char*>(&testHtmStart[0])));
}
void webReturnFirmwareHtm() {
_server->send_P(
200, "text/html", (const char*)firmwareHtmStart,
strlen(reinterpret_cast<const char*>(&firmwareHtmStart[0])));
} }
#endif #endif
public: public:
enum HtmlFile {
HTML_INDEX = 0,
HTML_DEVICE = 1,
HTML_CONFIG = 2,
HTML_ABOUT = 3,
HTML_CALIBRATION = 4,
HTML_FORMAT = 5
};
bool setupWebServer(); bool setupWebServer();
void loop(); void loop();
bool checkHtmlFile(HtmlFile item);
const char* getHtmlFileName(HtmlFile item);
}; };
// Global instance created // Global instance created

View File

@ -21,27 +21,23 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#if defined (ESP8266) #if defined(ESP8266)
#include <ESP8266HTTPClient.h> #include <ESP8266WiFi.h>
#include <ESP8266httpUpdate.h> #include <ESP8266httpUpdate.h>
#else // defined (ESP32) #else // defined (ESP32)
#include <WiFi.h>
#include <HTTPClient.h>
#include <HTTPUpdate.h> #include <HTTPUpdate.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiClientSecure.h>
#endif #endif
#include <incbin.h>
#include <config.hpp> #include <config.hpp>
#include <main.hpp> #include <main.hpp>
#include <wifi.hpp> #include <wifi.hpp>
// Settings for DRD // Settings for DRD
#if defined (ESP8266)
#define ESP_DRD_USE_LITTLEFS true #define ESP_DRD_USE_LITTLEFS true
#define ESP_DRD_USE_SPIFFS false #define ESP_DRD_USE_SPIFFS false
#else // defined (ESP32)
#define ESP_DRD_USE_LITTLEFS false
#define ESP_DRD_USE_SPIFFS true
#endif
#define ESP_DRD_USE_EEPROM false #define ESP_DRD_USE_EEPROM false
#include <ESP_DoubleResetDetector.h> #include <ESP_DoubleResetDetector.h>
#define DRD_TIMEOUT 3 #define DRD_TIMEOUT 3
@ -53,80 +49,57 @@ SOFTWARE.
#define USING_CORS_FEATURE false #define USING_CORS_FEATURE false
#define NUM_WIFI_CREDENTIALS 1 #define NUM_WIFI_CREDENTIALS 1
#define USE_STATIC_IP_CONFIG_IN_CP false #define USE_STATIC_IP_CONFIG_IN_CP false
#define _WIFIMGR_LOGLEVEL_ 3
#include <ESP_WiFiManager.h> #include <ESP_WiFiManager.h>
// Override the look and feel of the standard ui (hide secondary forms)
const char WM_HTTP_FORM_START[] PROGMEM =
"<form method='get' "
"action='wifisave'><fieldset><div><label>SSID</label><input id='s' "
"name='s' length=32 "
"placeholder='SSID'><div></div></div><div><label>Password</label><input "
"id='p' name='p' length=64 placeholder='password'><div></div></div><div "
"hidden><label>SSID1</label><input id='s1' name='s1' length=32 "
"placeholder='SSID1'><div></div></div><div "
"hidden><label>Password</label><input id='p1' name='p1' length=64 "
"placeholder='password1'><div></div></div></fieldset>";
#include <ESP_WiFiManager-Impl.h>
ESP_WiFiManager *myWifiManager; ESP_WiFiManager *myWifiManager;
DoubleResetDetector *myDRD; DoubleResetDetector *myDRD;
WifiConnection myWifi; WifiConnection myWifi;
const char *userSSID = USER_SSID; const char *userSSID = USER_SSID;
const char *userPWD = USER_SSID_PWD; const char *userPWD = USER_SSID_PWD;
//
// Initialize
//
void WifiConnection::init() { void WifiConnection::init() {
myDRD = new DoubleResetDetector(DRD_TIMEOUT, DRD_ADDRESS); myDRD = new DoubleResetDetector(DRD_TIMEOUT, DRD_ADDRESS);
} }
//
// Check if we have a valid wifi configuration
//
bool WifiConnection::hasConfig() { bool WifiConnection::hasConfig() {
if (strlen(myConfig.getWifiSSID())) return true; if (strlen(myConfig.getWifiSSID(0))) return true;
if (strlen(userSSID)) return true; if (strlen(userSSID)) return true;
// Check if there are stored WIFI Settings we can use. // Check if there are stored WIFI Settings we can use.
#if defined(ESP8266)
String ssid = WiFi.SSID(); String ssid = WiFi.SSID();
String pwd = WiFi.psk();
#else
ESP_WiFiManager wifiMgr;
String ssid = wifiMgr.WiFi_SSID();
String pwd = wifiMgr.WiFi_Pass();
#endif
if (ssid.length()) { if (ssid.length()) {
Log.notice(F("WIFI: Found credentials in EEPORM." CR)); Log.notice(F("WIFI: Found stored credentials." CR));
myConfig.setWifiSSID(WiFi.SSID()); myConfig.setWifiSSID(ssid, 0);
myConfig.setWifiPass(WiFi.psk());
if (pwd.length()) myConfig.setWifiPass(pwd, 0);
myConfig.saveFile(); myConfig.saveFile();
return true; return true;
} }
return false; return false;
} }
//
// Check if the wifi is connected
//
bool WifiConnection::isConnected() { return WiFi.status() == WL_CONNECTED; } bool WifiConnection::isConnected() { return WiFi.status() == WL_CONNECTED; }
//
// Get the IP adress
//
String WifiConnection::getIPAddress() { return WiFi.localIP().toString(); } String WifiConnection::getIPAddress() { return WiFi.localIP().toString(); }
//
// Additional method to detect double reset.
//
bool WifiConnection::isDoubleResetDetected() { bool WifiConnection::isDoubleResetDetected() {
if (strlen(userSSID)) if (strlen(userSSID))
return false; // Ignore this if we have hardcoded settings. return false; // Ignore this if we have hardcoded settings.
return myDRD->detectDoubleReset(); return myDRD->detectDoubleReset();
} }
//
// Stop double reset detection
//
void WifiConnection::stopDoubleReset() { myDRD->stop(); } void WifiConnection::stopDoubleReset() { myDRD->stop(); }
//
// Start the wifi manager
//
void WifiConnection::startPortal() { void WifiConnection::startPortal() {
Log.notice(F("WIFI: Starting Wifi config portal." CR)); Log.notice(F("WIFI: Starting Wifi config portal." CR));
@ -137,85 +110,173 @@ void WifiConnection::startPortal() {
myWifiManager->setMinimumSignalQuality(-1); myWifiManager->setMinimumSignalQuality(-1);
myWifiManager->setConfigPortalChannel(0); myWifiManager->setConfigPortalChannel(0);
myWifiManager->setConfigPortalTimeout( myWifiManager->setConfigPortalTimeout(
myHardwareConfig.getWifiPortalTimeout()); myAdvancedConfig.getWifiPortalTimeout());
if (myWifiManager->startConfigPortal(WIFI_DEFAULT_SSID, WIFI_DEFAULT_PWD)) { String mdns("<p>Default mDNS name is: http://");
Log.notice(F("WIFI: Exited portal, connected to wifi. Rebooting..." CR)); mdns += myConfig.getMDNS();
myConfig.setWifiSSID(myWifiManager->getSSID()); mdns += ".local<p>";
myConfig.setWifiPass(myWifiManager->getPW()); ESP_WMParameter deviceName(mdns.c_str());
myWifiManager->addParameter(&deviceName);
#if defined(ESP32C3) && defined(REDUCE_WIFI_POWER)
Log.notice(F("WIFI: Reducing wifi power for c3 chip." CR));
WiFi.setTxPower(WIFI_POWER_8_5dBm); // Required for ESP32C3 Mini
#endif
myWifiManager->startConfigPortal(WIFI_DEFAULT_SSID, WIFI_DEFAULT_PWD);
if (myWifiManager->getSSID(0).length()) {
myConfig.setWifiSSID(myWifiManager->getSSID(0), 0);
myConfig.setWifiPass(myWifiManager->getPW(0), 0);
myConfig.setWifiSSID(myWifiManager->getSSID(1), 1);
myConfig.setWifiPass(myWifiManager->getPW(1), 1);
// If the same SSID has been used, lets delete the second
if (!strcmp(myConfig.getWifiSSID(0), myConfig.getWifiSSID(1))) {
myConfig.setWifiSSID("", 1);
myConfig.setWifiPass("", 1);
}
Log.notice(F("WIFI: Stored SSID1:'%s' SSID2:'%s'" CR),
myConfig.getWifiSSID(0), myConfig.getWifiSSID(1));
myConfig.saveFile(); myConfig.saveFile();
} else { } else {
Log.notice( Log.notice(
F("WIFI: Exited portal, no connection to wifi. Rebooting..." CR)); F("WIFI: Could not find first SSID so assuming we got a timeout." CR));
} }
Log.notice(F("WIFI: Exited wifi config portal. Rebooting..." CR));
stopDoubleReset(); stopDoubleReset();
delay(500); delay(500);
ESP_RESET(); ESP_RESET();
} }
//
// Call the wifi manager in loop
//
void WifiConnection::loop() { myDRD->loop(); } void WifiConnection::loop() { myDRD->loop(); }
// void WifiConnection::connectAsync(int wifiIndex) {
// Connect to last known access point, non blocking mode.
//
void WifiConnection::connectAsync() {
WiFi.persistent(true); WiFi.persistent(true);
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
#if defined(ESP32C3) && defined(REDUCE_WIFI_POWER)
Log.notice(F("WIFI: Reducing wifi power for c3 chip." CR));
WiFi.setTxPower(WIFI_POWER_8_5dBm); // Required for ESP32C3 Mini
#endif
if (strlen(userSSID)) { if (strlen(userSSID)) {
Log.notice(F("WIFI: Connecting to wifi using hardcoded settings %s." CR), Log.notice(F("WIFI: Connecting to wifi using hardcoded settings %s." CR),
userSSID); userSSID);
WiFi.begin(userSSID, userPWD); WiFi.begin(userSSID, userPWD);
} else { } else {
Log.notice(F("WIFI: Connecting to wifi using stored settings %s." CR), Log.notice(F("WIFI: Connecting to wifi (%d) using stored settings %s." CR),
myConfig.getWifiSSID()); wifiIndex, myConfig.getWifiSSID(wifiIndex));
WiFi.begin(myConfig.getWifiSSID(), myConfig.getWifiPass()); WiFi.begin(myConfig.getWifiSSID(wifiIndex),
myConfig.getWifiPass(wifiIndex));
} }
} }
//
// Blocks until wifi connection has been found
//
bool WifiConnection::waitForConnection(int maxTime) { bool WifiConnection::waitForConnection(int maxTime) {
#if DEBUG_LEVEL == 6 #if DEBUG_LEVEL == 6
WiFi.printDiag(Serial); WiFi.printDiag(EspSerial);
#endif #endif
int i = 0; int i = 0;
while (WiFi.status() != WL_CONNECTED) { while (WiFi.status() != WL_CONNECTED) {
delay(100); delay(100);
if (i % 10) Serial.print("."); if (i % 10) EspSerial.print(".");
if (i++ > if (i++ >
(maxTime * 10)) { // Try for maxTime seconds. Since delay is 100ms. (maxTime * 10)) { // Try for maxTime seconds. Since delay is 100ms.
Log.error(F("WIFI: Failed to connect to wifi %d, aborting %s." CR), writeErrorLog("WIFI: Failed to connect to wifi %d", WiFi.status());
WiFi.status(), getIPAddress().c_str());
WiFi.disconnect(); WiFi.disconnect();
Serial.print(CR); EspSerial.print(CR);
return false; // Return to main that we have failed to connect. return false; // Return to main that we have failed to connect.
} }
} }
Serial.print(CR); EspSerial.print(CR);
Log.notice(F("WIFI: Connected to wifi ip=%s." CR), getIPAddress().c_str()); Log.notice(F("WIFI: Connected to wifi %s ip=%s." CR), WiFi.SSID().c_str(),
getIPAddress().c_str());
Log.notice(F("WIFI: Using mDNS name %s." CR), myConfig.getMDNS()); Log.notice(F("WIFI: Using mDNS name %s." CR), myConfig.getMDNS());
return true; return true;
} }
// int WifiConnection::findStrongestNetwork() {
// Connect to last known access point, blocking mode. if (!myConfig.dualWifiConfigured()) {
// Log.notice(F("WIFI: Only one wifi SSID is configured, skipping scan." CR));
bool WifiConnection::connect() { return -1;
connectAsync(); }
return waitForConnection(20); // 20 seconds.
Log.notice(F("WIFI: Scanning for wifi." CR));
int noNetwork = WiFi.scanNetworks(false, false);
int strenght[2] = {-100, -100};
for (int i = 0; i < noNetwork; i++) {
int rssi = WiFi.RSSI(i);
String ssid = WiFi.SSID(i);
if (ssid.compareTo(myConfig.getWifiSSID(0)))
strenght[0] = rssi;
else if (ssid.compareTo(myConfig.getWifiSSID(1)))
strenght[1] = rssi;
#if LOG_LEVEL == 6
Log.verbose(F("WIFI: Found %s %d." CR), ssid.c_str(), rssi);
#endif
}
if (strenght[0] == -100 && strenght[1] == -100)
return -1; // None of the stored networks can be seen
if (strenght[0] >= strenght[1])
return 0; // First network is strongest or they have equal strength
return 1; // Second network is the strongest
}
bool WifiConnection::connect() {
/*
// Alternative code for connecting to strongest wifi.
// Takes approximatly 2 seconds to scan for available network
int i = findStrongestNetwork();
if (i != -1) {
Log.notice(F("WIFI: Wifi %d:'%s' is strongest." CR), i,
myConfig.getWifiSSID(i)); } else { i = 0; // Use first SSID as default.
}
connectAsync(i);
return waitForConnection(myAdvancedConfig.getWifiConnectTimeout());
*/
// Alternative code for using seconday wifi settings as fallback.
// If success to seconday is successful this is used as standard
int timeout = myAdvancedConfig.getWifiConnectTimeout();
connectAsync(0);
if (!waitForConnection(timeout)) {
Log.warning(F("WIFI: Failed to connect to first SSID %s." CR),
myConfig.getWifiSSID(0));
if (strlen(myConfig.getWifiSSID(1))) {
connectAsync(1);
if (waitForConnection(timeout)) {
Log.notice(F("WIFI: Connected to second SSID %s, making secondary "
"default." CR),
myConfig.getWifiSSID(1));
myConfig.swapPrimaryWifi();
myConfig.saveFile();
return true;
}
}
Log.warning(F("WIFI: Failed to connect to any SSID." CR));
return false;
}
return true;
} }
//
// This will erase the stored credentials and forcing the WIFI manager to AP
// mode.
//
bool WifiConnection::disconnect() { bool WifiConnection::disconnect() {
Log.notice(F("WIFI: Erasing stored WIFI credentials." CR)); Log.notice(F("WIFI: Erasing stored WIFI credentials." CR));
// Erase WIFI credentials // Erase WIFI credentials
@ -223,10 +284,6 @@ bool WifiConnection::disconnect() {
} }
#if defined(ACTIVATE_OTA) #if defined(ACTIVATE_OTA)
//
//
//
bool WifiConnection::updateFirmware() { bool WifiConnection::updateFirmware() {
if (!_newFirmware) { if (!_newFirmware) {
Log.notice(F("WIFI: No newer version exist, skipping update." CR)); Log.notice(F("WIFI: No newer version exist, skipping update." CR));
@ -236,90 +293,84 @@ bool WifiConnection::updateFirmware() {
Log.verbose(F("WIFI: Updating firmware." CR)); Log.verbose(F("WIFI: Updating firmware." CR));
#endif #endif
String serverPath = myConfig.getOtaURL(); WiFiClient wifi;
serverPath += "firmware.bin"; WiFiClientSecure wifiSecure;
HTTPUpdateResult ret; HTTPUpdateResult ret;
String serverPath = myConfig.getOtaURL();
#if defined(ESP8266)
serverPath += "firmware.bin";
#elif defined(ESP32C3)
serverPath += "firmware32c3.bin";
#elif defined(ESP32S2)
serverPath += "firmware32s2.bin";
#elif defined(ESP32LITE)
serverPath += "firmware32lite.bin";
#else // defined (ESP32)
serverPath += "firmware32.bin";
#endif
if (serverPath.startsWith("https://")) { if (serverPath.startsWith("https://")) {
myWifi.getWifiClientSecure().setInsecure(); wifiSecure.setInsecure();
Log.notice(F("WIFI: OTA, SSL enabled without validation." CR)); Log.notice(F("WIFI: OTA, SSL enabled without validation." CR));
ret = ESPhttpUpdate.update(myWifi.getWifiClientSecure(), serverPath); ret = ESPhttpUpdate.update(wifiSecure, serverPath);
} else { } else {
ret = ESPhttpUpdate.update(myWifi.getWifiClient(), serverPath); ret = ESPhttpUpdate.update(wifi, serverPath);
} }
switch (ret) { switch (ret) {
case HTTP_UPDATE_FAILED: case HTTP_UPDATE_FAILED: {
Log.error(F("WIFI: OTA update failed %d, %s." CR), writeErrorLog("WIFI: OTA update failed %d", ESPhttpUpdate.getLastError());
ESPhttpUpdate.getLastError(), } break;
ESPhttpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES: case HTTP_UPDATE_NO_UPDATES:
break; break;
case HTTP_UPDATE_OK: case HTTP_UPDATE_OK: {
Log.notice("WIFI: OTA Update sucesfull, rebooting."); Log.notice("WIFI: OTA Update sucesfull, rebooting.");
delay(100); delay(100);
ESP_RESET(); ESP_RESET();
break; break;
} }
}
return false; return false;
} }
// void WifiConnection::downloadFile(HTTPClient &http, String &fname) {
// Download and save file
//
void WifiConnection::downloadFile(const char *fname) {
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
Log.verbose(F("WIFI: Download file %s." CR), fname); Log.verbose(F("WIFI: Download file %s." CR), fname);
#endif #endif
HTTPClient http;
String serverPath = myConfig.getOtaURL();
serverPath += fname;
if (serverPath.startsWith("https://")) {
myWifi.getWifiClientSecure().setInsecure();
Log.notice(F("WIFI: OTA, SSL enabled without validation." CR));
http.begin(myWifi.getWifiClientSecure(), serverPath);
} else {
http.begin(myWifi.getWifiClient(), serverPath);
}
int httpResponseCode = http.GET(); int httpResponseCode = http.GET();
if (httpResponseCode == 200) { if (httpResponseCode == 200) {
File f = LittleFS.open(fname, "w"); File f = LittleFS.open(fname, "w");
http.writeToStream(&f); http.writeToStream(&f);
f.close(); f.close();
Log.notice(F("WIFI: Downloaded file %s." CR), fname); Log.notice(F("WIFI: Downloaded file %s." CR), fname.c_str());
} else { } else {
Log.error(F("WIFI: Failed to download file, respone=%d" CR), writeErrorLog("WIFI: Failed to download html-file %d", httpResponseCode);
httpResponseCode);
} }
http.end();
myWifi.closeWifiClient();
} }
//
// Check what firmware version is available over OTA
//
bool WifiConnection::checkFirmwareVersion() { bool WifiConnection::checkFirmwareVersion() {
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
Log.verbose(F("WIFI: Checking if new version exist." CR)); Log.verbose(F("WIFI: Checking if new version exist." CR));
#endif #endif
WiFiClient wifi;
WiFiClientSecure wifiSecure;
HTTPClient http; HTTPClient http;
String serverPath = myConfig.getOtaURL(); String serverPath = myConfig.getOtaURL();
serverPath += "version.json"; serverPath += "version.json";
// Your Domain name with URL path or IP address with path // Your Domain name with URL path or IP address with path
if (serverPath.startsWith("https://")) { if (myConfig.isOtaSSL()) {
myWifi.getWifiClientSecure().setInsecure(); wifiSecure.setInsecure();
Log.notice(F("WIFI: OTA, SSL enabled without validation." CR)); Log.notice(F("WIFI: OTA, SSL enabled without validation." CR));
http.begin(myWifi.getWifiClientSecure(), serverPath); http.begin(wifiSecure, serverPath);
} else { } else {
http.begin(myWifi.getWifiClient(), serverPath); http.begin(wifi, serverPath);
} }
// Send HTTP GET request // Send HTTP GET request
DynamicJsonDocument ver(300);
int httpResponseCode = http.GET(); int httpResponseCode = http.GET();
if (httpResponseCode == 200) { if (httpResponseCode == 200) {
@ -329,10 +380,9 @@ bool WifiConnection::checkFirmwareVersion() {
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
Log.verbose(F("WIFI: Payload %s." CR), payload.c_str()); Log.verbose(F("WIFI: Payload %s." CR), payload.c_str());
#endif #endif
DynamicJsonDocument ver(300);
DeserializationError err = deserializeJson(ver, payload); DeserializationError err = deserializeJson(ver, payload);
if (err) { if (err) {
Log.error(F("WIFI: Failed to parse version.json, %s" CR), err); writeErrorLog("WIFI: Failed to parse version.json");
} else { } else {
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
Log.verbose(F("WIFI: Project %s version %s." CR), Log.verbose(F("WIFI: Project %s version %s." CR),
@ -351,10 +401,10 @@ bool WifiConnection::checkFirmwareVersion() {
// Compare major version // Compare major version
if (newVer[0] > curVer[0]) _newFirmware = true; if (newVer[0] > curVer[0]) _newFirmware = true;
// Compare minor version // Compare minor version
if (newVer[0] == curVer[0] && newVer[1] > curVer[1]) else if (newVer[0] == curVer[0] && newVer[1] > curVer[1])
_newFirmware = true; _newFirmware = true;
// Compare patch version // Compare patch version
if (newVer[0] == curVer[0] && newVer[1] == curVer[1] && else if (newVer[0] == curVer[0] && newVer[1] == curVer[1] &&
newVer[2] > curVer[2]) newVer[2] > curVer[2])
_newFirmware = true; _newFirmware = true;
} }
@ -362,14 +412,15 @@ bool WifiConnection::checkFirmwareVersion() {
// Download new html files to filesystem if they are present. // Download new html files to filesystem if they are present.
if (!ver["html"].isNull() && _newFirmware) { if (!ver["html"].isNull() && _newFirmware) {
Log.notice(F("WIFI: OTA downloading new html files." CR)); Log.notice(
F("WIFI: OTA checking if html files should be downloaded." CR));
JsonArray htmlFiles = ver["html"].as<JsonArray>(); JsonArray htmlFiles = ver["html"].as<JsonArray>();
for (JsonVariant v : htmlFiles) { for (JsonVariant v : htmlFiles) {
String s = v; String s = v;
#if LOG_LEVEL == 6 #if LOG_LEVEL == 6
Log.verbose(F("WIFI: OTA listed html file %s" CR), s.c_str()); Log.verbose(F("WIFI: OTA listed html file %s" CR), s.c_str());
#endif #endif
downloadFile(s.c_str()); downloadFile(http, s);
} }
} }
} }
@ -378,17 +429,17 @@ bool WifiConnection::checkFirmwareVersion() {
httpResponseCode); httpResponseCode);
} }
http.end(); http.end();
myWifi.closeWifiClient();
if (_newFirmware) Log.notice(F("WIFI: Found new version." CR));
#if LOG_LEVEL == 6 #if LOG_LEVEL == 6
Log.verbose(F("WIFI: OTA found new version %s." CR), Log.verbose(F("WIFI: OTA found new version %s." CR),
_newFirmware ? "true" : "false"); _newFirmware ? "true" : "false");
#endif #endif
return _newFirmware; return _newFirmware;
} }
//
// Parse a version string in the format M.m.p (eg. 1.2.10)
//
bool WifiConnection::parseFirmwareVersionString(int (&num)[3], bool WifiConnection::parseFirmwareVersionString(int (&num)[3],
const char *version) { const char *version) {
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)

View File

@ -24,11 +24,10 @@ SOFTWARE.
#ifndef SRC_WIFI_HPP_ #ifndef SRC_WIFI_HPP_
#define SRC_WIFI_HPP_ #define SRC_WIFI_HPP_
#if defined (ESP8266) #if defined(ESP8266)
#include <ESP8266WiFi.h> #include <ESP8266HTTPClient.h>
#else // defined (ESP32) #else // defined (ESP32)
#include <WiFiClient.h> #include <HTTPClient.h>
#include <WiFiClientSecure.h>
#endif #endif
#define WIFI_DEFAULT_SSID "GravityMon" // Name of created SSID #define WIFI_DEFAULT_SSID "GravityMon" // Name of created SSID
@ -36,22 +35,18 @@ SOFTWARE.
#define WIFI_MDNS "gravitymon" // Prefix for MDNS name #define WIFI_MDNS "gravitymon" // Prefix for MDNS name
// tcp cleanup, to avoid memory crash. // tcp cleanup, to avoid memory crash.
struct tcp_pcb;
extern struct tcp_pcb* tcp_tw_pcbs;
extern "C" void tcp_abort(struct tcp_pcb* pcb);
class WifiConnection { class WifiConnection {
private: private:
// WIFI
WiFiClient _client;
WiFiClientSecure _secureClient;
// OTA // OTA
bool _newFirmware = false; bool _newFirmware = false;
bool parseFirmwareVersionString(int (&num)[3], const char* version); bool parseFirmwareVersionString(int (&num)[3], const char* version);
void downloadFile(const char* fname); void downloadFile(HTTPClient& http, String& fname);
void connectAsync();
// WIFI
void connectAsync(int wifiIndex);
bool waitForConnection(int maxTime = 20); bool waitForConnection(int maxTime = 20);
int findStrongestNetwork();
public: public:
// WIFI // WIFI
@ -67,16 +62,6 @@ class WifiConnection {
void startPortal(); void startPortal();
void loop(); void loop();
WiFiClient& getWifiClient() { return _client; }
WiFiClientSecure& getWifiClientSecure() { return _secureClient; }
void closeWifiClient() {
_client.stop();
_secureClient.stop();
// Cleanup memory allocated by open tcp connetions.
while (tcp_tw_pcbs) tcp_abort(tcp_tw_pcbs);
}
// OTA // OTA
bool updateFirmware(); bool updateFirmware();
bool checkFirmwareVersion(); bool checkFirmwareVersion();

View File

@ -10,14 +10,18 @@ To reduce the need for adding custom endpoints for various services there is an
.. warning:: .. 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 .. image:: images/format.png
:width: 800 :width: 800
:alt: Format editor :alt: Format editor
You enter the format data in the text field and the test button will show an example on what the output would look like. If the data cannot be formatted in json it will just be displayed as a long string. You enter the format data in the text field and the test button will show an example on what the output would look like. If the data cannot be formatted in json it will just be displayed as a long string.
The save button will save the current formla and reload the data from the device. The save button will save the current formula and reload the data from the device.
You can also select a template from the list and copy that to the current endpoint.
Saving an empty formula will reset it to the default value.
.. tip:: .. tip::
@ -35,6 +39,12 @@ These are the format keys available for use in the format.
* - ${mdns} * - ${mdns}
- Name of the device - Name of the device
- gravmon2 - gravmon2
* - ${token}
- Token
- any value
* - ${token2}
- Token 2
- any value
* - ${id} * - ${id}
- Unique id of the device - Unique id of the device
- e422a3 - e422a3
@ -89,4 +99,10 @@ These are the format keys available for use in the format.
* - ${gravity-unit} * - ${gravity-unit}
- Gravity format, `G` or `P` - Gravity format, `G` or `P`
- G - G
* - ${app-ver}
- Software version
- 1.1.0
* - ${app-build}
- Software revision (git hash)
- ..e456743

View File

@ -8,10 +8,11 @@ All the API's use a key called ``ID`` which is the unique device id (chip id). T
GET: /api/config GET: /api/config
================ ================
Retrive the current configuation of the device via an HTTP GET command. Payload is in JSON format. Retrieve the current configuration of the device via an HTTP GET command. Payload is in JSON format.
* ``temp-format`` can be either ``C`` or ``F`` * ``temp-format`` can be either ``C`` or ``F``
* ``gravity-format`` is always ``G`` (plato is not yet supported) * ``gravity-format`` is always ``G`` (plato is not yet supported)
* ``ble`` is used to enable ble data transmission (only on esp32) simulating a tilt. Valid color names are; red, green, black, purple, orange, blue, yellow, pink
Other parameters are the same as in the configuration guide. Other parameters are the same as in the configuration guide.
@ -22,13 +23,16 @@ Other parameters are the same as in the configuration guide.
"id": "ee1bfc", "id": "ee1bfc",
"ota-url": "http://192.168.1.50:80/firmware/gravmon/", "ota-url": "http://192.168.1.50:80/firmware/gravmon/",
"temp-format": "C", "temp-format": "C",
"brewfather-push": "http://log.brewfather.net/stream?id=Qwerty", "ble": "color",
"token": "token",
"token2": "token2",
"http-push": "http://192.168.1.50:9090/api/v1/Qwerty/telemetry", "http-push": "http://192.168.1.50:9090/api/v1/Qwerty/telemetry",
"http-push-h1": "header: value", "http-push-h1": "header: value",
"http-push-h2": "header: value", "http-push-h2": "header: value",
"http-push2": "http://192.168.1.50/ispindel", "http-push2": "http://192.168.1.50/ispindel",
"http-push2-h1": "header: value", "http-push2-h1": "header: value",
"http-push2-h2": "header: value", "http-push2-h2": "header: value",
"http-push3": "http://192.168.1.50/ispindel",
"influxdb2-push": "http://192.168.1.50:8086", "influxdb2-push": "http://192.168.1.50:8086",
"influxdb2-org": "org", "influxdb2-org": "org",
"influxdb2-bucket": "bucket_id", "influxdb2-bucket": "bucket_id",
@ -52,33 +56,37 @@ Other parameters are the same as in the configuration guide.
"gy": -6, "gy": -6,
"gz": 4 "gz": 4
}, },
"formula-calculation-data": {
"a1":25,
"a10":0,
"g1":1,
"g10":1
},
"angle": 90.93, "angle": 90.93,
"gravity": 1.105, "gravity": 1.105,
"battery": 0.04 "battery": 0.04,
"app-ver": "0.1.0",
"app-build": "build",
"platform": "esp8266",
"runtime-average": 3.12
} }
GET: /api/device GET: /api/device
================ ================
Retrive the current device settings via an HTTP GET command. Payload is in JSON format. This API has been removed from 0.9 and merged with /api/status
.. code-block:: json
{
"app-name": "GravityMon",
"app-ver": "0.0.0",
"id": "ee1bfc",
"mdns": "gravmon"
}
GET: /api/status GET: /api/status
================ ================
Retrive the current device status via an HTTP GET command. Payload is in JSON format. Retrieve the current device status via an HTTP GET command. Payload is in JSON format.
* ``temp-format`` can be either ``C`` or ``F`` * ``temp-format`` can be either ``C`` or ``F``
* ``platform`` can be either ``esp8266``, ``esp32c3``, ``esp32s2``, ``esp32`` or ``esp32lite`` (floaty hardware)
* ``temp-c`` will be set to -273 C if there is no temp sensor
* ``angle`` will be set to 0 if no valid angle is found and -1 if there is no gyro
Other parameters are the same as in the configuration guide. Other parameters are the same as in the configuration guide.
@ -88,23 +96,31 @@ Other parameters are the same as in the configuration guide.
"id": "ee1bfc", "id": "ee1bfc",
"angle": 89.86, "angle": 89.86,
"gravity": 1.1052, "gravity": 1.1052,
"gravity-tempcorr": 1.1031,
"temp-c": 0, "temp-c": 0,
"temp-f": 32, "temp-f": 32,
"battery": 0, "battery": 0,
"wifi-ssid": "connected SSID",
"temp-format": "C", "temp-format": "C",
"sleep-mode": false, "sleep-mode": false,
"rssi": -56 "token": "token",
"token2": "token2",
"rssi": -56,
"app-ver": "0.0.0",
"app-build": "gitrev",
"mdns": "gravmon",
"sleep-interval": 30,
"platform": "esp8266",
"runtime-average": 3.12
} }
GET: /api/config/formula GET: /api/config/formula
======================== ========================
Retrive the data used for formula calculation data via an HTTP GET command. Payload is in JSON format. Retrieve the data used for formula calculation data via an HTTP GET command. Payload is in JSON format.
* ``a1``-``a4`` are the angles/tilt readings (up to 5 are currently supported) * ``a1``-``a10`` are the angles/tilt readings (up to 10 are currently supported)
* ``g1``-``g4`` are the corresponding gravity reaadings in SG or Plato depending on the device-format. * ``g1``-``g10`` are the corresponding gravity readings in SG or Plato depending on the device-format.
.. code-block:: json .. code-block:: json
@ -115,16 +131,107 @@ Retrive the data used for formula calculation data via an HTTP GET command. Payl
"a3": 58, "a3": 58,
"a4": 0, "a4": 0,
"a5": 0, "a5": 0,
"a6": 0,
"a7": 0,
"a8": 0,
"a9": 0,
"a10": 0,
"g1": 1.000, "g1": 1.000,
"g2": 1.053, "g2": 1.053,
"g3": 1.062, "g3": 1.062,
"g4": 1, "g4": 1,
"g5": 1, "g5": 1,
"g6": 1,
"g7": 1,
"g8": 1,
"g9": 1,
"g10": 1,
"error": "Potential error message",
"gravity-format": "G", "gravity-format": "G",
"gravity-formula": "0.0*tilt^3+0.0*tilt^2+0.0017978*tilt+0.9436" "gravity-formula": "0.0*tilt^3+0.0*tilt^2+0.0017978*tilt+0.9436"
} }
GET: /api/config/advanced
=========================
Used for adjusting some internal constants and other advanced settings. Should be used with caution.
.. code-block:: json
{
"gyro-read-count": 50,
"gyro-moving-threashold": 500,
"formula-max-deviation": 3.0,
"wifi-portal-timeout": 120,
"wifi-connect-timeout": 20,
"push-timeout": 10,
"formula-calibration-temp": 20,
"int-http1": 0,
"int-http2": 0,
"int-http3": 0,
"int-influx": 0,
"int-mqtt": 0,
"tempsensor-resolution": 9,
"ignore-low-angles": false
}
POST: /api/config/advanced
==========================
Same parameters as above.
Payload should be in standard format used for posting a form.
.. note::
``ignore-low-angles`` is defined as "on" or "off" when posting since this is the output values
from a checkbox, when reading data it's sent as boolean (true,false).
GET: /api/clearwifi
===================
Will reset the wifi settings both in the configuration file and eeprom, leaving the rest of the configuration.
For this to work you will need to supply the device id as a parameter in the request:
::
http://mygravity.local/api/clearwifi?id=<mydeviceid>
GET: /api/factory
=================
Will do a reset to factory defaults and delete all data except wifi settings.
For this to work you will need to supply the device id as a parameter in the request:
::
http://mygravity.local/api/factory?id=<mydeviceid>
GET: /api/test/push
===================
Trigger a push on one of the targets, used to validate the configuration from the UI.
Requires to parameters to function /api/test/push?id=<deviceid>&format=<format>
* ``format`` defines which endpoint to test, valid values are; http-1, http-2, http-3, influxdb, mqtt
The response is an json message with the following values.
* ``code`` is the return code from the push function, typically http responsecode or error code from mqtt library.
.. code-block:: json
{
"success": false,
"enabled": true,
"code": -3
}
POST: /api/config/device POST: /api/config/device
======================== ========================
@ -132,7 +239,7 @@ Used to update device settings via an HTTP POST command.
Payload should be in standard format used for posting a form. Such as as: `id=value&mdns=value` etc. Key value pairs are shown below. Payload should be in standard format used for posting a form. Such as as: `id=value&mdns=value` etc. Key value pairs are shown below.
* ``temp-format`` can be either ``C`` (Celcius) or ``F`` (Farenheight) * ``temp-format`` can be either ``C`` (Celsius) or ``F`` (Fahrenheit)
.. code-block:: .. code-block::
@ -152,13 +259,15 @@ Payload should be in standard format used for posting a form. Such as as: `id=va
.. code-block:: .. code-block::
id=ee1bfc id=ee1bfc
token=
token2=
http-push=http://192.168.1.50/ispindel http-push=http://192.168.1.50/ispindel
http-push2= http-push2=
http-push3=
http-push-h1= http-push-h1=
http-push-h2= http-push-h2=
http-push2-h1= http-push2-h1=
http-push2-h2= http-push2-h2=
brewfather-push=
influxdb2-push=http://192.168.1.50:8086 influxdb2-push=http://192.168.1.50:8086
influxdb2-org= influxdb2-org=
influxdb2-bucket= influxdb2-bucket=
@ -207,6 +316,7 @@ Payload should be in standard format used for posting a form. Such as as: `id=va
id=ee1bfc id=ee1bfc
voltage-factor=1.59 voltage-factor=1.59
temp-adjustment=0 temp-adjustment=0
ble=red
gyro-temp=off gyro-temp=off
ota-url=http://192.168.1.50/firmware/gravmon/ ota-url=http://192.168.1.50/firmware/gravmon/
@ -216,8 +326,8 @@ POST: /api/config/formula
Used to update formula calculation data via an HTTP POST command. Payload is in JSON format. Used to update formula calculation data via an HTTP POST command. Payload is in JSON format.
* ``a1``-``a4`` are the angles/tilt readings (up to 5 are currently supported) * ``a1``-``a10`` are the angles/tilt readings (up to 10 are currently supported)
* ``g1``-``g4`` are the corresponding gravity reaadings (in SG) * ``g1``-``g10`` are the corresponding gravity readings (in SG)
Payload should be in standard format used for posting a form. Such as as: `id=value&mdns=value` etc. Key value pairs are shown below. Payload should be in standard format used for posting a form. Such as as: `id=value&mdns=value` etc. Key value pairs are shown below.
@ -229,18 +339,28 @@ Payload should be in standard format used for posting a form. Such as as: `id=va
a3=58 a3=58
a4=0 a4=0
a5=0 a5=0
a6=0
a7=0
a8=0
a9=0
a19=0
g1=1.000 g1=1.000
g2=1.053 g2=1.053
g3=1.062 g3=1.062
g4=1 g4=1
g5=1 g5=1
g6=1
g7=1
g8=1
g9=1
g10=1
Calling the API's from Python Calling the API's from Python
============================= =============================
Here is some example code for how to access the API's from a python script. Keys should always be Here is some example code for how to access the API's from a python script. Keys should always be
present or the API call will fail. present or the API call will fail. You only need to include the parameters you want to change.
The requests package converts the json to standard form post format. The requests package converts the json to standard form post format.
@ -249,7 +369,7 @@ The requests package converts the json to standard form post format.
import requests import requests
import json import json
host = "192.168.1.1" # IP adress (or name) of the device to send these settings to host = "192.168.1.1" # IP address (or name) of the device to send these settings to
id = "ee1bfc" # Device ID (shown in serial console during startup or in UI) id = "ee1bfc" # Device ID (shown in serial console during startup or in UI)
def set_config( url, json ): def set_config( url, json ):
@ -271,13 +391,15 @@ The requests package converts the json to standard form post format.
url = "http://" + host + "/api/config/push" url = "http://" + host + "/api/config/push"
json = { "id": id, json = { "id": id,
"token": "",
"token2": "",
"http-push": "http://192.168.1.1/ispindel", "http-push": "http://192.168.1.1/ispindel",
"http-push2": "", "http-push2": "",
"http-push3": "",
"http-push-h1": "", "http-push-h1": "",
"http-push-h2": "", "http-push-h2": "",
"http-push2-h1": "", "http-push2-h1": ""
"http-push2-h2": "", "http-push2-h2": "",
"brewfather-push": "",
"influxdb2-push": "", "influxdb2-push": "",
"influxdb2-org": "", "influxdb2-org": "",
"influxdb2-bucket": "", "influxdb2-bucket": "",
@ -302,7 +424,8 @@ The requests package converts the json to standard form post format.
"voltage-factor": 1.59, # Default value for voltage calculation "voltage-factor": 1.59, # Default value for voltage calculation
"temp-adjustment": 0, # If temp sensor needs to be corrected "temp-adjustment": 0, # If temp sensor needs to be corrected
"gyro-temp": "on", # Use the temp sensor in the gyro instead (on/off) "gyro-temp": "on", # Use the temp sensor in the gyro instead (on/off)
"ota-url": "" # if the device should seach for a new update when active "ble": "red", # Enable ble on esp32
"ota-url": "" # if the device should search for a new update when active
} }
set_config( url, json ) set_config( url, json )
@ -313,10 +436,20 @@ The requests package converts the json to standard form post format.
"a3": 58, "a3": 58,
"a4": 0, "a4": 0,
"a5": 0, "a5": 0,
"a6": 0,
"a7": 0,
"a8": 0,
"a9": 0,
"a10": 0,
"g1": 1.000, "g1": 1.000,
"g2": 1.053, "g2": 1.053,
"g3": 1.062, "g3": 1.062,
"g4": 1, "g4": 1,
"g5": 1 "g5": 1,
"g6": 1,
"g7": 1,
"g8": 1,
"g9": 1,
"g10": 1
} }
set_config( url, json ) set_config( url, json )

View File

@ -29,18 +29,36 @@ In the platformio config there are 3 targets defined
* gravity-debug; Maximum logging for trouble shooting, deep sleep is disabled. * gravity-debug; Maximum logging for trouble shooting, deep sleep is disabled.
* gravity-release; Standard release * gravity-release; Standard release
* gravity-perf; Standard release but contains code for measuring performance * gravity32-release: Version for ESP32 mini.
* gravity32-perf: Experimental version for ESP32. * gravity32c3-release: Version for ESP32 C3 mini v2.1+.
* gravity32c3v1-release: Version for ESP32 C3 mini v1.0.
.. note:: * gravity32s2-release: Version for ESP32 S2 mini.
There is an experimental ESP32 target but since platformio only supports SDK 1.0.6 and the WIFI connection is really slow compared to ESP8266, * gravity32lite-release: Version for ESP32 lite (Floaty hardware).
so the recommendation is to wait for support on 2.0.x branch. With the tested version an wifi connection takes 3-8s on a ESP32 compared
to 0.5s on an ESP8266. There is also a bug in OneWire connected to ESP32 that has not been fixed in the main repository yet.
.. warning:: .. 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. The debug target can be unstable and crash the device under certain circumstanses. Excessive logging to the serial port can cause corruption and crashes.
So only enable enough debugging to troubleshoot your changes. So only enable enough debugging to troubleshoot your changes.
Serial debugging on battery
===========================
.. image:: images/serial.png
:width: 600
:alt: Serial output
On the ESP32 builds the serial output can be written to UART0 which is connected to the RX/TX pins on the chip. This way the serial output can be viewed
without a connection to the USB port, convinient when running the device on battery power. In order to get this to work you need to compile the sofware
with the option **DUSE_SERIAL_PINS** and attach as USB to TTL cable to the correct pins.
You connect the USB to TTL cable that you connect the TX, RX and GND pins. **Dont connect the power pin** if you are powering the device from USB or Battery.
.. image:: images/usb-ttl.jpg
:width: 300
:alt: USB to TTL cable
.. image:: images/serial_esp32c3.jpg
:width: 300
:alt: Serial output ESP32c3
Source structure Source structure
================ ================
@ -68,8 +86,6 @@ Source structure
- Source code for software - Source code for software
* - /src_docs * - /src_docs
- Source code for documentation - Source code for documentation
* - /stl
- 3d models
* - /test * - /test
- Test data for developing html files - Test data for developing html files
@ -92,12 +108,15 @@ This is a list of C++ defines that is used to enable/disable functions in the co
- Done include verbose logging in the corresponding class. Excessive logging may crash device. - Done include verbose logging in the corresponding class. Excessive logging may crash device.
* - USE_LITTLEFS * - USE_LITTLEFS
- Use the new filesystem in Ardurino - Use the new filesystem in Ardurino
* - EMBED_HTML
- Html files are included in code, if not defined they are served from the file system.
* - USER_SSID * - USER_SSID
- If defined the device will always use this SSID - If defined the device will always use this SSID
* - USER_SSID_PWD * - USER_SSID_PWD
- Password to the SSID - Password to the SSID
* - CFG_APPVER * - CFG_APPVER
- Defines the version of the compiled software - Defines the version of the compiled software
* - USE_SERIAL_PINS
- Will send the serial console to the TX/RX pins on an ESP32 target so that debugging can be done when on battery
* - REDUCE_WIFI_POWER
- Will reduce wifi power to support the ESP32C3 v1.0 which has a bad antenna
* - FLOATY
- Build for the ESP32lite FLOATY hardware option (no DS18B20 and no battery monitor)

View File

@ -18,11 +18,11 @@
# -- Project information ----------------------------------------------------- # -- Project information -----------------------------------------------------
project = 'GravityMon' project = 'GravityMon'
copyright = '2021-2022, Magnus Persson' copyright = '2021-2023, Magnus Persson'
author = 'Magnus Persson' author = 'Magnus Persson'
# The full version, including alpha/beta/rc tags # The full version, including alpha/beta/rc tags
release = '0.7.1' release = '1.2.1'
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------
@ -31,6 +31,7 @@ release = '0.7.1'
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones. # ones.
extensions = [ extensions = [
'sphinx_copybutton'
] ]
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
@ -47,6 +48,8 @@ exclude_patterns = []
# a list of builtin themes. # a list of builtin themes.
# #
html_theme = 'furo' html_theme = 'furo'
html_logo = "images/gravitymon_logo.png"
html_title = "GravityMon v1.2.0"
# Add any paths that contain custom static files (such as style sheets) here, # Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files, # relative to this directory. They are copied after the builtin static files,

View File

@ -10,7 +10,7 @@ One of the following conditions will place the device in ``configuration mode``:
- Gyro has not been calibrated - Gyro has not been calibrated
- Sleep mode has been disabled in the web interface - Sleep mode has been disabled in the web interface
- Placed in horizontal mode 85-90 degrees - Placed in horizontal mode 85-90 degrees
- Charger connected >4.15V - Charger connected >4.15V (or the value that is configured). This does not work on the Floaty variant due to lack of hardware support.
Status Status
@ -22,38 +22,24 @@ URL: (http://gravmon.local)
:width: 800 :width: 800
:alt: Index page :alt: Index page
Configuration is accessed by entering the URL for the device, this will be the mDNS name *device.local* or the IP adress. The following chapter assumes the device name is *gravmon*.
Configuration is accessed by entering the URL for the device, this will be the mDNS name *device.local* or the IP address. The following chapter assumes the device name is *gravmon*.
The main page shows the device readings; gravity, angle, temperature and battery charge. If the checkbox is active then the device will never go into sleep mode. This is useful if The main page shows the device readings; gravity, angle, temperature and battery charge. If the checkbox is active then the device will never go into sleep mode. This is useful if
you are collecting angle/tilt for calibration. If this is unchecked the device will change mode as explained before. you are collecting angle/tilt for calibration. If this is unchecked the device will change mode as explained before.
You can also view the average time a gravity measurement takes. Under optimal setting this should be around 1.5 - 2.0 seconds. If this is higher than 2 seconds this is most likely connected to slow wifi
connection. It will show 0 if data has not been collected yet.
.. tip:: .. tip::
If you are connected to the device via a serial console (speed: 115200) you can see the connection sequence and get the Unique ID and IP adress from there. If you are connected to the device via a serial console (speed: 115200) you can see the connection sequence and get the Unique ID and IP address from there.
.. tip::
Device The button `view error log` will show the last error messages on the device. This can be useful for checking errors without
====== the need to connect to the serial port or to check what errors has occurred while in `gravity mode`. From v1.1 it will also detect
any abnormal restarts or crashes and record these in the logfile (this applies to esp8266 only).
URL: (http://gravmon.local/device)
.. image:: images/device.png
:width: 800
:alt: Device Settings
* **Version:**
Installed version of the code and html files.
* **Device name:**
This is unique name of the device which is set in the configuration, also known as MDNS name.
* **Device ID:**
This is unique identifier for the device (ESP8266 id), this is required when using the API as an API Key to safeguard
against faulty requests. This is the ESP8266 chip ID, so it should be unique.
Configuration Configuration
@ -70,27 +56,29 @@ Device Setting
* **Device name:** * **Device name:**
This is unique name for the device. It will be used in pushing data as well as mDNS name on the network (<name>.local) This is unique name for the device. It will be used in pushing data as well as mDNS name on the network (<name>.local).
The limitation is 63 chars but using long names might break endpoints that data is sent to if they have other limitations.
* **Temperature format:** * **Temperature format:**
Choose between Celsius and Farenheight when displaying temperature. Choose between Celsius and Fahrenheit when displaying temperature.
* **Interval:** * **Interval:**
This defines how long the device should be sleeping between the readings when in `gravity monitoring` mode. You will also see This defines how long the device should be sleeping between the readings when in `gravity monitoring` mode. You will also see
the values in minutes/seconds to easier set the interval. 900s is a recommended interval. The sleep interval can the values in minutes/seconds to easier set the interval. 900s is a recommended interval. The sleep interval can
be set between 10 - 3600 seconds (60 minutes). be set between 10 - 3600 seconds (60 minutes).
.. note:: .. note::
A low value such as 30s will give a lifespan of 1-2 weeks and 300s (5 min) would last for 3+ weeks. This assumes that A low value such as 30s will give a lifespan of 1-2 weeks and 300s (5 min) would last for 3+ weeks. This assumes that
there is good wifi connection that takes less than 1s to reconnect. Poor wifi connection is the main reason for battery drain. there is good wifi connection that takes less than 1s to reconnect. Poor wifi connection is the main reason for battery drain.
The device will show the estimated lifespan based on the average connection time, if no data exist it will not be shown.
* **Calibration values:** * **Calibration values:**
These are calibration data for the gyro. Place the device flat on a table and press the button to save the default orientation values. Without this calibration we cannot calculate the correct angle/tilt. These are calibration data for the gyro. Place the device flat on a table and press the button to save the default orientation values. Without this calibration we cannot calculate the correct angle/tilt.
.. warning:: .. warning::
@ -106,60 +94,98 @@ Push Settings
.. note:: .. note::
When enabling SSL this will not validate the root CA of the remote service, this is a design decision based on two aspects. Enabling CA validation will take 3-4s extra on each connection which means way less When enabling SSL this will not validate the root CA of the remote service, this is a design decision based on two aspects. Enabling CA validation will take 3-4s extra on each connection which means way less
battery life, so the decision is to prioritize battery life over security. The data transmitted is not really that sensitive anyway so I belive this is a good balance. battery life, so the decision is to prioritize battery life over security. The data transmitted is not really that sensitive anyway so I believe this is a good balance.
* **HTTP 1 (POST):**
Endpoint to send data via http. Default format used Format used :ref:`data-formats-ispindle`. You can customize the format using :ref:`format-editor`.
If you add the prefix `https://` then the device will use SSL when sending data.
* **HTTP 2 (POST):**
Endpoint to send data via http. Default format used :ref:`data-formats-ispindle`. You can customize the format using :ref:`format-editor`.
If you add the prefix `https://` then the device will use SSL when sending data.
* **Token:**
The token is included in the iSpindle JSON format and will be used for both HTTP targets. If you
need to have 2 different tokens please use the :ref:`format-editor` to customize the data format.
* **HTTP 3 (GET):**
Endpoint to send data via http. This is using an HTTP GET request instead of a post. This means that the values are appended to the URL like; http://endpoint?param=value&param2=value2. You can customize the format using :ref:`format-editor`.
If you add the prefix `https://` then the device will use SSL when sending data.
* **Token 2:**
The token is included in the default format for the HTTP GET url but can be used for any of the formats. For HTTP GET use can use this for an authorization token with for instance ubidots or blynk http api.
* **HTTP Headers**
.. image:: images/config-popup1.png
:width: 300
:alt: HTTP Headers
You can define 2 http headers per push target. This is available via a pop-up window but don't forget
to press the save buttons on the post section to save the values. One common header is content type which is the
default setting for http targets.
The input must have the format **'<header>: <value>'** for it to work. The UI will accept any value so errors
will not show until the device tries to push data.
* **HTTP URL 1:** ::
Endpoint to send data via http. Default format used Format used :ref:`data-formats-ispindle`. You can customize the format using :ref:`format-editor`. Content-Type: application/json
X-Auth-Token: <api-token>
If you add the prefix `https://` then the device will use SSL when sending data.
* **HTTP URL 2:** Mozilla has a good guide on what headers are valid; `HTTP Headers <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers>`_
Endpoint to send data via http. Default format used :ref:`data-formats-ispindle`. You can customize the format using :ref:`format-editor`.
If you add the prefix `https://` then the device will use SSL when sending data. Push Settings (2)
+++++++++++++++++
* **Brewfather URL:** .. image:: images/config2b.png
:width: 800
Endpoint to send data via http to brewfather. Format used :ref:`data-formats-brewfather` :alt: Push Settings
SSL is not supported for this target.
* **Influx DB v2 URL:** * **Influx DB v2 URL:**
Endpoint to send data via http to InfluxDB. Format used :ref:`data-formats-influxdb2`. You can customize the format using :ref:`format-editor`. Endpoint to send data via http to InfluxDB. Format used :ref:`data-formats-influxdb2`. You can customize the format using :ref:`format-editor`.
SSL is not supported for this target. Raise a issue on github if this is wanted. SSL is not supported for this target. Raise a issue on github if this is wanted.
* **Influx DB v2 Organisation:** * **Influx DB v2 Organisation:**
Name of organisation in Influx. Name of organisation in Influx.
* **Influx DB v2 Bucket:** * **Influx DB v2 Bucket:**
Identifier for bucket. Token for bucket. Don't use the bucket name.
* **Influx DB v2 Token:** * **Influx DB v2 Token:**
Token with write access to bucket. Token with write access to bucket.
* **MQTT server:** * **MQTT server:**
IP or name of server to send data to. Default format used :ref:`data-formats-mqtt`. You can customize the format using :ref:`format-editor`. IP or name of server to send data to. Default format used :ref:`data-formats-mqtt`. You can customize the format using :ref:`format-editor`.
* **MQTT Port:** * **MQTT Port:**
Which port should be used for communication, default is 1883 (standard port). For SSL use 8883 (any port over 8000 is treated as SSL). Which port should be used for communication, default is 1883 (standard port). For SSL use 8883 (any port over 8000 is treated as SSL).
* **MQTT user:** * **MQTT user:**
Username or blank if anonymous is accepted Username or blank if anonymous is accepted
* **MQTT password:** * **MQTT password:**
Password or blank if anonymous is accepted Password or blank if anonymous is accepted
Gravity Settings Gravity Settings
@ -171,25 +197,26 @@ Gravity Settings
* **Gravity format:** * **Gravity format:**
Gravity format can be eihter `SG` or `Plato`. The device will use SG Internally and convert to Plato when displaying or sending data. Gravity format can be either `SG` or `Plato`. The device will use SG Internally and convert to Plato when displaying or sending data.
* **Gravity formula:** * **Gravity formula:**
Gravity formula is compatible with standard iSpindle formulas so any existing calculation option can be used. You can also use Gravity formula is compatible with standard iSpindle formulas so any existing calculation option can be used. You can also use
the feature to create the formula by supplying the raw data. See :ref:`create-formula` the feature to create the formula by supplying the raw data. See :ref:`create-formula`
The gravity formula accepts to paramaters, **tilt** for the angle or **temp** for temperature (temperature inserted into the formula The gravity formula accepts to parameters, **tilt** for the angle or **temp** for temperature (temperature inserted into the formula
will be in celsius). I would recommend to use the formula calculation feature instead since this is much easier. will be in celsius). I would recommend to use the formula calculation feature instead since this is much easier.
* **Temperature correct gravity:** * **Temperature correct gravity:**
Will apply a temperature calibration formula to the gravity as a second step after gravity has been calculated. It's also possible to Will apply a temperature calibration formula to the gravity as a second step after gravity has been calculated. It's also possible to
build this into the gravity formula. build this into the gravity formula.
.. warning:: .. warning::
This formula assumes that the calibration has been done at 20°C / 68°F. This formula assumes that the calibration has been done at 20°C / 68°F.
Formula used in temperature correction. Formula used in temperature correction. The calibration temperature can be changed under advanced settings.
:: ::
@ -206,33 +233,113 @@ Hardware Settings
* **Voltage factor:** * **Voltage factor:**
Factor used to calcualate the battery voltage. If you get a too low/high voltage you can adjust this value. Factor used to calculate the battery voltage. If you get a too low/high voltage you can adjust this value.
* **Config voltage:**
Defines the level of voltage when the device should enter config mode due to charging. This might vary between different battery manufacturers.
If you don't what the device to go into configuration mode when charging, set this to 6V. This was added since different batteries have different
voltages when fully charged.
* **Temperature correction:** * **Temperature correction:**
This value will be added to the temperature reading (negative value will reduce temperature reading). This is applied This value will be added to the temperature reading (negative value will reduce temperature reading). This is applied
when the device starts. So changing this will not take affect until the device is restarted. when the device starts. So changing this will not take affect until the device is restarted.
* **Gyro Temperature:** * **Gyro Temperature:**
Enable this feature will use the temp sensor i the gyro instead of the DS18B20, the benefit is shorter run time and Enable this feature will use the temp sensor i the gyro instead of the DS18B20, the benefit is shorter run time and
longer battery life (this is an experimental feature). The value used is the first temperature reading from when the longer battery life (this is an experimental feature). The value used is the first temperature reading from when the
device is activated, since the gyro should be cool this is reflecting the surronding temperature. After it has device is activated, since the gyro should be cool this is reflecting the surrounding temperature. After it has
been running the value would be totally off. been running the value would be totally off.
* **Enable storage mode when placed on cap**
When place on the cap (<5 degree tilt) the device will go into deep sleep forever (until reset). In order to wake it
up you need to do a reset. One option is to attach a magnetic reed switch (default open) to the reset pin and use a
magnet to force a reset without opening the tube. The reed switch is typically an electronic component of 14 mm
long encapsulated in a small glass tube. See hardware section for more information, :ref:`hardware`.
* **Bluetooth: (Only ESP32)**
If the build is using an ESP32 then you can send data over BLE, simulating a Tilt device. Choose the color that you want the device to simulate.
* **OTA URL:** * **OTA URL:**
Should point to a URL where the firmware.bin file + version.json file are located. Should point to a URL where the firmware.bin file + version.json file are located. For an ESP32 target the firmware should be named firmware32.bin.
For the OTA to work, place the following files (version.json + firmware.bin) at the location that you pointed out in OTA URL. If the version number in the json file is newer than in the For the OTA to work, place the following files (version.json + firmware.bin) at the location that you pointed out in OTA URL. If the version number in the json file is newer than in the
code the update will be done during startup. code the update will be done during startup.
If you have the previx `https://` then the device will use secure transfer without CA validation. If you have the previx `https://` then the device will use secure transfer without CA validation.
Example; OTA URL (don't forget trailing dash), the name of the file should be firmware.bin Example; OTA URL (don't forget trailing dash), the name of the file should be firmware.bin
.. code-block:: .. code-block::
http://192.168.1.1/firmware/gravmon/ http://192.168.1.1/firmware/gravmon/
https://192.168.1.1/firmware/gravmon/
* **Upload Firmware**
This option gives you the possibility to install an new version of the firmware (or any firmware that uses the standard flash layout).
.. image:: images/firmware.png
:width: 600
:alt: Update firmware
Advanced Settings
+++++++++++++++++
.. image:: images/config5.png
:width: 800
:alt: Advanced Settings
.. warning::
Change these parameters with caution. The wrong values might cause the device to become unresponsive.
* **Gyro reads:**
This defines how many gyro reads will be done before an angle is calculated. More reads will give better accuracy and also allow detection of
movement. Too many reads will take time and affect battery life. 50 takes about 800 ms to execute.
* **Gyro moving threshold:**
This is the max amount of deviation allowed for a stable reading.
* **Formula deviation:**
This is the maximum deviation on the formula allowed for it to be accepted. Once the formula has been derived it will be validated against the supplied
data and of the deviation on any point is bigger the formula will be rejected.
* **Ignore angles below water:**
If this option is checked any angles below that of SG 1 will be discarded as invalid and never sent to any server. Default = off.
* **Gravity calibration temp**
This option allows you to set the correction temperature used in the automatic temperature gravity adjustment formula. Standard is 20C.
* **DS18B20 Resolution:**
Define the resolution used on the temp sensor. 9 bits is default and will give an accuracy of 0.5C, 12 bits will give an accuracy of 0.0625C but will also
take longer time to measure..
* **Wifi connect timeout:**
This is the amount of time allowed for a wifi connect.
* **Wifi portal timeout:**
If the wifi portal is triggered (can be triggered by reset) then this is the amount of time allowed before it exists again.
* **Skip Interval (...):**
These options allow the user to have variable push intervals for the different endpoints. 0 means that every wakeup will send data to that endpoint. If you enter another number then that defines how many sleep cycles will be skipped.
If the sleep interval is 300s and MQTT is set to 0 and HTTP1 is set to 2 then MQTT will be sent every 300s while HTTP1 would be sent 900s. This is great if you want to send data to a local mqtt server often but brewfather will only
accept data every 15 min.

View File

@ -1,5 +1,33 @@
Contributing Contributing
############
Anyone is welcome to contribute to this project or create their own variant of it. I would appreciate a PR if your feature would be of benefit other users.
In order to keep the source code in good condition I use `pre-commit <https://pre-commit.com/>`_ to validate and format the code using their standards for C++/C.
.. note::
If you are using Windows as a base platform I would suggest that you install pre-commit under wsl (Windows Subsystem for Windows) and run it from there, I have found
that this approach works fine.
The following command will run pre-commit on all the source files. Assuming you are in the project directory.
.. code-block::
pre-commit run --files src/*
Design goals
------------ ------------
This section is under construction. My goals with this software has been the following:
* Create an open software for the excellent iSpindle hardware platform that is open and available for anyone.
* Add user requested features that have not made it into the iSpindle project.
* Focus on long battery life and stability.
* Explore new technologies and create the next generation gravity monitoring for home brewers.
I will do my best to assist users and respond to new features, pr and suggestions. But keep in mind I'm doing this on my spare time.
Regards, Magnus

View File

@ -5,8 +5,8 @@ Data Formats
.. _data-formats-ispindle: .. _data-formats-ispindle:
iSpindle format HTTP Post, iSpindle format
=============== ==========================
This is the format used for standard http posts. This is the format used for standard http posts.
@ -22,11 +22,11 @@ This is the format used for standard http posts.
"token" : "gravmon", "token" : "gravmon",
"interval": 900, "interval": 900,
"temperature": 20.5, "temperature": 20.5,
"temp-units": "C", "temp_units": "C",
"gravity": 1.0050, "gravity": 1.0050,
"angle": 45.34, "angle": 45.34,
"battery": 3.67, "battery": 3.67,
"rssi": -12, "RSSI": -12,
"corr-gravity": 1.0050, "corr-gravity": 1.0050,
"gravity-unit": "G", "gravity-unit": "G",
@ -40,41 +40,41 @@ This is the format template used to create the json above.
{ {
"name" : "${mdns}", "name" : "${mdns}",
"ID": "${id}", "ID": "${id}",
"token" : "gravmon", "token" : "${token}",
"interval": ${sleep-interval}, "interval": ${sleep-interval},
"temperature": ${temp}, "temperature": ${temp},
"temp-units": "${temp-unit}", "temp_units": "${temp-unit}",
"gravity": ${gravity}, "gravity": ${gravity},
"angle": ${angle}, "angle": ${angle},
"battery": ${battery}, "battery": ${battery},
"rssi": ${rssi}, "RSSI": ${rssi},
"corr-gravity": ${corr-gravity}, "corr-gravity": ${corr-gravity},
"gravity-unit": "${gravity-unit}", "gravity-unit": "${gravity-unit}",
"run-time": ${run-time} "run-time": ${run-time}
} }
.. _data-formats-brewfather:
Brewfather format
=================
This is the format for Brewfather. See: `Brewfather API docs <https://docs.brewfather.app/integrations/custom-stream>`_
.. code-block:: json
{
"name" : "gravmon",
"temp": 20.5,
"temp_unit": "C",
"battery": 3.67,
"gravity": 1.0050,
"gravity_unit": "G",
}
.. _data-formats-influxdb2: .. _data-formats-influxdb2:
HTTP Get
========
This is the format added to the URL when using HTTP get
.. code-block::
?name=<mdns>,id=<id>,token=<token>&interval=300&temperature=20.1&temp-units=<C|F>&
gravity=$1.004&angle=45.5&battery=3.96&rssi=-18&corr-gravity=1.004&gravity-unit=<G|P>&run-time=2.1
This is the format template used to create the data above.
.. code-block::
?name=${mdns}&id=${id}&token=${token2}&interval=${sleep-interval}&temperature=${temp}&
temp-units=${temp-unit}&gravity=${gravity}&angle=${angle}&battery=${battery}&rssi=${rssi}&
corr-gravity=${corr-gravity}&gravity-unit=${gravity-unit}&run-time=${run-time}
Influx DB v2 Influx DB v2
============ ============
@ -82,14 +82,17 @@ This is the format for InfluxDB v2
.. code-block:: .. code-block::
measurement,host=<mdns>,device=<id>,temp-format=<C|F>,gravity-format=SG,gravity=1.0004,corr-gravity=1.0004,angle=45.45,temp=20.1,battery=3.96,rssi=-18 measurement,host=<mdns>,device=<id>,temp-format=<C|F>,gravity-format=SG,
gravity=1.0004,corr-gravity=1.0004,angle=45.45,temp=20.1,battery=3.96,rssi=-18
This is the format template used to create the json above. This is the format template used to create the json above.
.. code-block:: .. code-block::
measurement,host=${mdns},device=${id},temp-format=${temp-unit},gravity-format=${gravity-unit} gravity=${gravity},corr-gravity=${corr-gravity},angle=${angle},temp=${temp},battery=${battery},rssi=${rssi} measurement,host=${mdns},device=${id},temp-format=${temp-unit},
gravity-format=${gravity-unit} gravity=${gravity},corr-gravity=${corr-gravity},
angle=${angle},temp=${temp},battery=${battery},rssi=${rssi}
.. _data-formats-mqtt: .. _data-formats-mqtt:
@ -126,31 +129,18 @@ This is the format template used to create the json above.
ispindel/${mdns}/interval:${sleep-interval}| ispindel/${mdns}/interval:${sleep-interval}|
ispindel/${mdns}/RSSI:${rssi}| ispindel/${mdns}/RSSI:${rssi}|
This is a format template that is compatible with v0.6. Just replace the `topic` with the topic you want to post data to.
.. code-block::
topic:{"name":"gravmon","ID":"${id}","token":"gravmon","interval": ${sleep-interval},"temperature": ${temp},"temp-units": "${temp-unit}","gravity":${gravity},"angle": ${angle},"battery":${battery},"rssi": ${rssi},"corr-gravity":${corr-gravity},"gravity-unit": "${gravity-unit}","run-time": ${run-time}}|
version.json version.json
============ ============
Contents version.json. The version is used by the device to check if the this version is newer. The html files will also be downloaded if the are present on the server. This way it's easy to Contents version.json. The version is used by the device to check if the this version is newer. The html files will also be downloaded if the are present on the server. This way it's easy to
upgrade to a version that serve the html files from the file system. If they dont exist nothing will happen, the OTA flashing will still work. If the html files are missing from the file system upgrade to a version that serve the html files from the file system. If they don't exist nothing will happen, the OTA flashing will still work. If the html files are missing from the file system
they can be uploaded manually afterwards. they can be uploaded manually afterwards.
.. code-block:: json .. code-block:: json
{ {
"project":"gravmon", "project":"gravmon",
"version":"0.7.0", "version":"1.0.0",
"html": [ "html": [ ]
"index.min.htm",
"device.min.htm",
"config.min.htm",
"format.min.htm",
"calibration.min.htm",
"about.min.htm"
]
} }

View File

@ -7,10 +7,33 @@ Create formula
:width: 800 :width: 800
:alt: Formula data :alt: Formula data
Here you can enter up to 5 values (angles + gravity) that is then used to create the formula. Angles equal to zero will be regarded as empty even if there is a gravity reading. Here you can enter up to 10 values (angles + gravity) that is then used to create the formula. Angles equal to zero will be regarded as empty even if there is a gravity reading.
When you submit the values the device will try create a formula with increasing level of complexity. It will start
with a order 2 formula and then try 3 and 4.
Once the formula has been created it will validate the formula against the supplied angles/gravity and if there is a too
high difference, it will fail. You can adjust the sensitivity under advanced settings if you have issues.
Under the Error Log you will also find hints to what problem the formula creator encountered. Here is an example:
`CALC: Validation failed on angle 33.430000, deviation too large 5.86, formula order 4`
`CALC: Validation failed on angle 33.430000, deviation too large 3.14, formula order 2`
This means that the angle 33.43 had a deviation of 5.8 SG and since the default threshold is 3, it will fail. You
can also see that it has failed on that point in both a order 2 and 4 formula.
.. image:: images/qa_1.png
:width: 400
:alt: Example of deviating value
So in this case you can either increase the threshold or remove the angle that has an issue. You can also
use the graph on the calibration page to identify angles that is probably not correct.
.. image:: images/formula2.png .. image:: images/formula2.png
:width: 800 :width: 800
:alt: Formula graph :alt: Formula graph
Once the formula is created a graph over the entered values and a simulation of the formula will give you a nice overview on how the formula will work. Once the formula is created a graph over the entered values and a simulation of the formula will give you a nice overview on how the formula will work.

View File

@ -0,0 +1,213 @@
.. _functionality:
Functionality
==============
The main features
-----------------
* **Operates in two modes gravity monitoring and configuration mode**
In ``gravity monitoring`` mode it behaves just like the iSpindle, it wakes up at regular intervals, measures
angle/tile, temperature, calculates gravity and pushes the data to defined endpoints.
In ``configuration mode`` the device is always active and the webserver is active. Here you can view the
angle/tilt values, change configuration, update the gravity formula. When in this mode you can also interact
with the device via an REST API so data can be pushed to the device via scripts (see API section for more information).
.. image:: images/index.png
:width: 700
:alt: UI example
You can force the device into ``configuration mode`` while measuring gravity. This is useful when calibrating
the device so you don't needs to wait for the device to wake up and push the data. The entire calibration
sequence can be handled via the web interface without need for additional software tools.
See the :ref:`setting-up-device` section for more information on how to trigger the configuration mode.
* **Can send data to multiple endpoints**
The original iSpindle can only have one destination, this software will push data to all defined endpoints so
in theory you can use them all. However this will consume more battery power so use only as many as needed. Its much
more efficient to have the endpoints on your local network than on the internet.
Currently the device supports the following endpoints.
* http (ssl optional)
* influxdb v2 (ssl optional)
* MQTT (ssl optional)
* Brewfather
* Home Assistant
* Brew Spy
* Brewers Friend
* Fermentrack
* Ubidots
* Thingsspeak
Under the :ref:`services` section you can find guides for how to connect GravityMon to these services. For a
description of what data is transmitted you can see :ref:`data-formats`.
The software support SSL endpoints but currently without CA validation, this means that the data is encrypted
but it does not validate if the remote endpoint is who it claims to be.
if you require CA validation please leave a comment on GitHub and I will make that a priority. Adding this function
will dramatically reduce the battery life of the device.
.. note::
Using SSL on a small device such as the esp8266 can be unstable since it requires a lot of RAM to work. And running out
of RAM will cause the device to crash. So enable SSL with caution and only when you really need it. GravityMon will try
to minimize the needed RAM but the remote service might not support that feature.
* **Create gravity formulas on the device**
Another big difference is that this software can create the gravity formula in the device, just enter the
angle/gravity data that you have collected. You will also see a graph simulating how the formula would work.
Currently the device can handle 10 data points which should be enough to get a accurate formula. At least 3 data points
is needed to get an accurate formula.
* **Customize the data format being sent to push targets**
In order to make it easier to support more targets there is a built in format editor that can be used to
customize the data that is to be sent. This way you can easily adapt the software to new targets without coding.
If you have a good template please share it on the github repository and I will add it to the documentation
for other users to enjoy. See the :ref:`format-editor` for more information. See :ref:`services` for a list of
services currently validated.
* **Automatic temperature adjustment of gravity reading**
If you want to correct gravity based on beer temperature you can do this in the formula but here is a nice
feature that can correct the gravity as a second step making this independent of the formula.
* **OTA support from webserver**
When starting up in configuration mode the device will check for a software update from a webserver. This is an easily
way to keep the software up to date. In the future I might add a hosted endpoint for providing updates. OTA can also be
done over a SSL connection.
* **DS18B20 temperature adjustments**
You can adjust the temperature reading of the temperature sensor. In normal cases this should not be needed since
the sensors should be calibrated.
* **Gyro Movement**
The software will detect if the gyro is moving and if this is the case it will go back to sleep for 60 seconds.
This way we should avoid faulty measurements and peaks in the graphs.
* **WIFI connection issues**
The software will not wait indefinitely for a wifi connection. If it takes longer than 20 seconds to connect then
the device will try the secondary wifi configuration, and that also fails it will go into deep sleep for 60 seconds and then
retry later. This to conserve batter as much as possible.
* **Use gyro temperature sensor**
This works fine when the device has time to cool down between measurements and it saves up to 400 ms.
My testing shows that this is quite accurate with a deviation of less than 0.3C. This
reduces the run time by 20% (with optimal wifi connection).
The graph below compares from the temp from two different devices in the same bucket of water. One with
gyro temp enabled and one with the DS18B20 sensor. The blue line is the gyro temperature and this clear
that the temperature will be higher after it has been running but cools down when in sleep mode. The interval
has been set to 300s. A low delay of 30s will not allow the gyro to cool down and the temperature will
be 0.5-1.0C higher.
.. image:: images/temp1.png
:width: 800
:alt: Gyro temp vs DS18B20
* **Celsius or Fahrenheit**
You can switch between different temperature formats. GravityMon will always use C for it's internal calculations and
convert to F when displayed.
* **SG or Plato**
You can switch between different gravity formats. GravityMon will always use SG for it's internal calculations and
convert to Plato when displayed.
* **Stable gyro data**
The device will read the gyro 50 times to get an accurate reading. If the standard deviation is to high it will not
use the data since this is inaccurate and the device is probably moving, probably do to active fermentation or movement of
fermentation vessel. This sequence takes 900 ms seconds to execute and besides wifi connection this is what consumes the most
battery. With more testing this might be changes to either speed up or provide more stable readings.
* **Crash detection and Error Logging**
There is a build in logging function so that errors that occurs can be detected and logged to a file. On the ESP8266 crashes will also
be logged so that these problems can be detected and fixed. Crash logging is not available on the ESP32 variants.
* **Performance measurements**
I've also create a small library to measure execution code in some areas of the code that i know is time consuming. This
way I can find a good balance between performance and quality. This is a lot of help trying to figure out where bottlenecks
are in the code and where to put optimization efforts. Examples of real measurements:
* Reading the gyro: 885 ms
* Reading DS18B20 temperature sensor: 546 ms
* Connect to WIFI: 408 ms
* Send data to local influxdb v2: 25 ms
* Send data to local mqtt server: 35 ms
* Send data to local http server: 40 ms
* Send data to http server on internet: 0.2 - 5 seconds
See the :ref:`compiling-the-software` for more information.
Battery life
------------
For the 1.2 version I have been running some long term battery tests on a few of the boards and also comparing wifi vs Bluetooth. I was using a standard 2200 mA battery
that was fully charged at the start of the tests. All devices started with factory settings with only a change in push destination and sleep interval.
For the wifi tests, I was pushing data every 30 seconds to a local influxdb2 server to reduce errors connected to slow response on the server side. The devices
was placed 2 meters from the wifi AP to ensure a good and stable wifi connection (so ideal conditions).
For the Bluetooth tests I was pusing data every 10 seconds to a linux server.
To make this comparable I measured how many times the device was able to wake up and push data before the battery was dead. I theory the power consumption when in
deep sleep is to low it can almost be ignored for the calculations. So the impact on battery is mainly caused by how long the device is awake. In the most optimal case
this can be as low as 1.5-2.0 seconds but in reality its probably around 3-4 seconds. Wifi consumes a lot of power so Bluetooth is a better option for long battery life.
.. list-table:: Battery power
:widths: 30 20 20 20
:header-rows: 1
* - Device
- Transmissions
- 30s
- 300s / 15min
* - ESP 8266 (wifi)
- 26,000
- 9 days
- 90 days
* - ESP32 c3 (wifi)
- 12,000
- 4 days
- 43 days
* - ESP32 d1 (ble)
- 56,000
- 20 days
- 196 days
As you can see from the table above there is quite some differences between the boards and connection methods.
Performance
-----------
Since I have the possibility to measure the performance of different function in the code this is what I have been able to gather.
The typical runtime in a measurement cycle is approx 2 seconds and in some cases it can take up to 6-8 seconds but this is mainly related to establishing the WIFI connection. So stable wifi is
essential for long battery life. Out of the 2 seconds of run-time the major time is spent on gyro readings (1.3s) and temperature measurements of (0.6s) so using the gyro sensor for measuring
temperature would reduce the total runtime with 25%. Sending data over http takes less than 100ms (on my local network) so this is not drawing much power.
The image below shows how the run-time varies over time. The pink line is the wifi connection time and this is why the time varies. The orange is the total runtime for the awake period.
.. image:: images/perf1.png
:width: 800
:alt: Performance view

View File

@ -1,145 +0,0 @@
.. _functionallity:
Functionallity
==============
The main features
-----------------
* **Operates in two modes gravity monitoring and configuration mode**
In ``gravity monitoring`` mode it behaves just like the iSpindle, it wakes up at regular intervals, measures
angle/tile, temperature, calculates gravity and pushes the data to defined endpoints.
In ``configuration mode`` the device is always active and the webserver is active. Here you can view the
angle/tilt values, change configuration options and more. When in this mode you can also interact with the device
via an REST API so data can be pushed to the device via scripts (see API section for more information).
You can force the device into ``configuration mode`` while measuring gravity. This is useful when calibrating
the device so you don't needs to wait for the device to wake up and push the data. The entire calibration
sequence can be handled via the web interface without need for additional software tools.
See the :ref:`setting-up-device` section for more information on how to trigger the configuration mode.
* **Can send data to multiple endpoints at once**
The original iSpindle can only have one destination, this software will push data to all defined endpoints so
in theory you can use them all. However this will consume more battery power so use only as many as needed.
Currently the device supports the following endpoints: http (2 different), influxdb2, Brewfather and MQTT.
If you want additional targets please raise a feature request in the github repo.
* **Create gravity formulas on the device**
Another big difference is that this software can create the gravity formula in the device, just enter the
angle/gravity data that you have collected. You will also see a graph simulating how the formula would work.
.. note::
This feature needs more testing to be validated.
* **Customize the data format beeing sent to push targets**
In order to make it easier to support more targets there is a built in format editor that can be used to
customize the data that is to be sent. This way you can easily adapt the software to new targets without coding.
If you have a good template please share it on the girhub repository and I will add it to the documentation
for other users to enjoy. See the :ref:`format-editor` for more information.
.. note::
This feature needs more testing to be validated.
* **Automatic temperature adjustment of gravity reading**
If you want to correct gravity based on beer temperature you can do this in the formula but here is a nice
feature that can correct the gravity as a second step making this independant of the formula.
.. note::
This feature needs more testing to be validated.
* **OTA support from local webserver**
When starting up in configuration mode the device will check for a software update from a local webserver.
* **DS18B20 temperature adjustments**
You can adjust the temperature reading of the temperature sensor.
* **Gyro Movement**
The software will detect if the gyro is moving and if this is the case it will go back to sleep for 60seconds.
This way we should avoid faulty measurements.
* **WIFI connection issues**
The software will not wait indefiently for a wifi connection. If it takes longer than 20 seconds to connect then
the device will go into deep sleep for 60 seoncds and then retry.
* **Use gyro temperature sensor**
This works fine when the device has time to cool down between measurements and it saves up to 400 ms.
My testing shows that this is quite accurate with a deviation of less than 0.3C. This
reduces the run time by 20% (with optimal wifi connection).
The graph below compares from the temp from two different devices in the same bucket of water. One with
gyro temp enabled and one with the DS18B20 sensor. The blue line is the gyro temperature and this clear
that the temperature will be higher after it has been running but cools down when in sleep mode. The interval
has been set to 300s. A low delay of 30s will not allow the gyro to cool down and the temperature will
be 0.5-1.0C higher.
.. image:: images/temp1.png
:width: 800
:alt: Gyro temp vs DS18B20
Other features
--------------
* Support for Celcius and Farenheigt as temperature formats.
* Support SG (Plato is not yet supported)
* Gyro data is read 50 times to ensure good accuracy
Experimental features
---------------------
* **Performance measurements**
I've also create a small library to measure execution code in some areas of the code that i know is time consuming. This way I can find a good balance between performace and quality.
See the :ref:`compiling-the-software` for more information.
* **Power measurements**
I've also create a project to measure the power consumption of the device, but more on this later.
Battery life
------------
I'm currently measuring battery life of v0.5 but previous versions have been able to measure gravity for
a 2-3 weeks without issues (Using 900 seconds as interval).
I had a device running with an sleep interval of only 30s with ok wifi connection. The device lasted
12 days which i think is excellent considering the short sleep interval. From what I have discovered
it's the WIFI connection that has the most impact on the battery life. In a perfect scenario it
can take around 400ms but can in some cases take up to 8 seconds.
*More on this topics once my tests are done*
Performance
-----------
Since I have the possibility to measure the performance of different function in the code this is what I have been able to gather.
The typical runtime in a measurement cycle is approx 2 seconds and in some cases it can take up to 6-8 seconds but this is mainly related to establishing the WIFI connection. So stable wifi is
essential for long batterylife. Out of the 2 seconds of run-time the major time is spent on gyro readings (1.3s) and temperature measurements of (0.6s) so using the gyro sensor for measureing
temperature would reduce the total runtime with 25%. Sending data over http takes less than 100ms (on my local network) so this is not drawing much power.
The image below shows how the run-time varies over time. The pink line is the wifi connection time and this is why the time varies. The orange is the total runtime for the awake period.
.. image:: images/perf1.png
:width: 800
:alt: Performance view

Some files were not shown because too many files have changed in this diff Show More