mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
Compare commits
338
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad3514e765 | ||
|
|
f543995d86 | ||
|
|
97ccb8eca1 | ||
|
|
f291c9d25a | ||
|
|
bd9ead3851 | ||
|
|
43ea0039fd | ||
|
|
74eb638bcb | ||
|
|
c87b035908 | ||
|
|
0f450eaa3d | ||
|
|
02df033fc6 | ||
|
|
74d05e58f0 | ||
|
|
3d88537677 | ||
|
|
95b0a7bed9 | ||
|
|
bc3434532e | ||
|
|
1f7639cb42 | ||
|
|
923642bb27 | ||
|
|
a09cb906eb | ||
|
|
72b0a32839 | ||
|
|
e279e5504b | ||
|
|
6d5ddf06ce | ||
|
|
7f544b7a10 | ||
|
|
fc407e86e1 | ||
|
|
a35c3051df | ||
|
|
f8f2ddd33b | ||
|
|
6592b57615 | ||
|
|
6e8e8214b2 | ||
|
|
24b6611c54 | ||
|
|
8135defa6f | ||
|
|
d5e9cfb081 | ||
|
|
ffc8e1fb97 | ||
|
|
0255f41897 | ||
|
|
e88973861f | ||
|
|
d6ec7aa736 | ||
|
|
8aa5866dd9 | ||
|
|
0c384c332e | ||
|
|
7a4f3b15b3 | ||
|
|
c38e532e67 | ||
|
|
f983dd924d | ||
|
|
f0ff52b76b | ||
|
|
3c0e5d1965 | ||
|
|
14489a2e98 | ||
|
|
a920b95cfb | ||
|
|
a111175a8f | ||
|
|
db76aeb959 | ||
|
|
b313957311 | ||
|
|
9d0f741e84 | ||
|
|
7c0f1551b8 | ||
|
|
8a59811392 | ||
|
|
e2e715d7de | ||
|
|
ccb4cf6a7a | ||
|
|
1b4a217bc9 | ||
|
|
19ecf36358 | ||
|
|
bdea34a583 | ||
|
|
b7b5028c0c | ||
|
|
c519d887db | ||
|
|
29c8b850ba | ||
|
|
d01cb9b9a2 | ||
|
|
9d894d7c83 | ||
|
|
a418cb2bd0 | ||
|
|
907a672fa0 | ||
|
|
2f10274da2 | ||
|
|
399b344779 | ||
|
|
2e06ed14c0 | ||
|
|
b0032bebdb | ||
|
|
a525ec34b0 | ||
|
|
024ec7b727 | ||
|
|
1ff0141e38 | ||
|
|
125db8046b | ||
|
|
720317c829 | ||
|
|
bbe9005a32 | ||
|
|
afa16e80b6 | ||
|
|
37f5c92aa7 | ||
|
|
684b89ae4c | ||
|
|
8c4612c300 | ||
|
|
0afab53659 | ||
|
|
e6e98345fe | ||
|
|
2394e4a75f | ||
|
|
e1c4691325 | ||
|
|
3f84c35014 | ||
|
|
bc0bafa96b | ||
|
|
4e7e66822d | ||
|
|
6f226e6d65 | ||
|
|
a50a035238 | ||
|
|
d560c635f5 | ||
|
|
f8db0c0a05 | ||
|
|
79616022df | ||
|
|
559d294607 | ||
|
|
d2588c5e01 | ||
|
|
ba45a51576 | ||
|
|
8321caaeed | ||
|
|
b35ed7d809 | ||
|
|
a4583d150e | ||
|
|
cadc0a885d | ||
|
|
061eb20ae5 | ||
|
|
9a9799eee7 | ||
|
|
f4615bb76b | ||
|
|
ecc498c359 | ||
|
|
e0e10fa6c8 | ||
|
|
228f09f394 | ||
|
|
19ac19778b | ||
|
|
5e2d517ba2 | ||
|
|
7a4b17983e | ||
|
|
6eba74ccc1 | ||
|
|
27a05b674c | ||
|
|
4a66bbf081 | ||
|
|
109bddb5c3 | ||
|
|
8158a919d8 | ||
|
|
706b91168b | ||
|
|
d047999c5e | ||
|
|
42f361aacd | ||
|
|
3dd076cf25 | ||
|
|
c0d155dd99 | ||
|
|
c433174d71 | ||
|
|
b805b59baf | ||
|
|
9fae0477d8 | ||
|
|
f7ac04dc96 | ||
|
|
d50e661efb | ||
|
|
57b5338af6 | ||
|
|
bd035c8f81 | ||
|
|
ac63d93e1a | ||
|
|
4cff0d0e02 | ||
|
|
ca83983fd2 | ||
|
|
d808f7e79b | ||
|
|
e469350ad9 | ||
|
|
60536b1a93 | ||
|
|
b9e45bd515 | ||
|
|
a0992b8383 | ||
|
|
9315581858 | ||
|
|
9a763303ca | ||
|
|
7df9379f47 | ||
|
|
c32fe50521 | ||
|
|
e4569b26f7 | ||
|
|
48998886fe | ||
|
|
36f97d7909 | ||
|
|
0e3b9c9e47 | ||
|
|
acecff4108 | ||
|
|
3322b954a7 | ||
|
|
4c4b844a4d | ||
|
|
cf4d28eb4f | ||
|
|
555b5386ae | ||
|
|
1903fdc7f2 | ||
|
|
918353461e | ||
|
|
e648a20fd0 | ||
|
|
1d7ea5e129 | ||
|
|
6ce5240dd7 | ||
|
|
692fb248ce | ||
|
|
5a87397ee0 | ||
|
|
99ba09b024 | ||
|
|
3dea091d21 | ||
|
|
ca13317cbd | ||
|
|
f68516d5bb | ||
|
|
8102a0096d | ||
|
|
73cec59bd5 | ||
|
|
96ecf9bf29 | ||
|
|
ef1e371514 | ||
|
|
45d67292a3 | ||
|
|
b5662baad4 | ||
|
|
12c3e5f81f | ||
|
|
dececf24ee | ||
|
|
3fb6dec382 | ||
|
|
cef962c5f1 | ||
|
|
cca2335070 | ||
|
|
ffd0d26ed3 | ||
|
|
9b457ed77b | ||
|
|
f787a9a9a6 | ||
|
|
a0ae104794 | ||
|
|
2b78810457 | ||
|
|
43081c3ac8 | ||
|
|
33281c08c2 | ||
|
|
461aee26ad | ||
|
|
2b0b14b2e8 | ||
|
|
b7404666b1 | ||
|
|
da25aa87dd | ||
|
|
10a1e634e2 | ||
|
|
83f86449b9 | ||
|
|
eee337d3e3 | ||
|
|
c549ecd329 | ||
|
|
d9e682668d | ||
|
|
e38072ac24 | ||
|
|
6aebeba5dc | ||
|
|
da01c8f469 | ||
|
|
d3c86b9824 | ||
|
|
9621a91d90 | ||
|
|
b3b68856f0 | ||
|
|
bec5cb5334 | ||
|
|
bda6011390 | ||
|
|
f3fdb93a52 | ||
|
|
200b060b15 | ||
|
|
ff41cb2f94 | ||
|
|
c134eb6d13 | ||
|
|
f9e2a211e1 | ||
|
|
bd3d3f66f4 | ||
|
|
3bad683211 | ||
|
|
68f6e913a2 | ||
|
|
0b9c550fd7 | ||
|
|
96420faa2f | ||
|
|
8eea77e4ed | ||
|
|
3d1f723787 | ||
|
|
5d459003e4 | ||
|
|
ad208c357c | ||
|
|
b1c777e6fd | ||
|
|
f5ab15c255 | ||
|
|
001ec6dcd2 | ||
|
|
15e5edccd8 | ||
|
|
651353cc99 | ||
|
|
85f97551c5 | ||
|
|
6a158f68f5 | ||
|
|
1933714de5 | ||
|
|
545117ddf7 | ||
|
|
8eb479c411 | ||
|
|
b39f51ca3c | ||
|
|
4511e08d3c | ||
|
|
086e836956 | ||
|
|
3e20f617ea | ||
|
|
8d9aacb6a4 | ||
|
|
0670fa98a6 | ||
|
|
2b3d1517c9 | ||
|
|
0be79759d1 | ||
|
|
d47bede215 | ||
|
|
2988a244b2 | ||
|
|
8d87dc0175 | ||
|
|
21c86e666a | ||
|
|
e6fdd3380a | ||
|
|
e04f71ab47 | ||
|
|
90ae0720cf | ||
|
|
4ec2af24a3 | ||
|
|
c8ecd8d374 | ||
|
|
7a451dd672 | ||
|
|
b06f6aa360 | ||
|
|
6b67415c71 | ||
|
|
b095465d3a | ||
|
|
81a5d911ba | ||
|
|
48060d8d99 | ||
|
|
d303fa023f | ||
|
|
29b0cc1b32 | ||
|
|
584abd1e75 | ||
|
|
b4d8be0e59 | ||
|
|
30f5c5cb7d | ||
|
|
6e7286c6fb | ||
|
|
ae1db543ce | ||
|
|
947e1e0343 | ||
|
|
0578d720f4 | ||
|
|
3fee6794f2 | ||
|
|
019e33331b | ||
|
|
211d79bbd5 | ||
|
|
8a751a0922 | ||
|
|
450eca8dc5 | ||
|
|
ce0bfbf717 | ||
|
|
bb316bbd30 | ||
|
|
91c5d60af9 | ||
|
|
0d9bc8027d | ||
|
|
bd2bdab95c | ||
|
|
c4f98fbc8e | ||
|
|
b332a513b5 | ||
|
|
e36bb714f1 | ||
|
|
6e1cde343f | ||
|
|
cce9ddd2dc | ||
|
|
51be17a436 | ||
|
|
66d27a6697 | ||
|
|
a033e458fb | ||
|
|
494436bbff | ||
|
|
23bacd0762 | ||
|
|
9bdfab3fa2 | ||
|
|
9d446745cc | ||
|
|
d2ab946d17 | ||
|
|
fcc71a56b6 | ||
|
|
f131254853 | ||
|
|
657d15d962 | ||
|
|
60910da48d | ||
|
|
6cee2d0f32 | ||
|
|
533cb88a64 | ||
|
|
ffa32fe851 | ||
|
|
b900cc9389 | ||
|
|
8e11c19af6 | ||
|
|
f33ca6df78 | ||
|
|
194a230d20 | ||
|
|
7a5a432016 | ||
|
|
07511fe144 | ||
|
|
9a390136f9 | ||
|
|
27a03f166a | ||
|
|
0a63aeb8f0 | ||
|
|
22c10ac615 | ||
|
|
ac83cfa249 | ||
|
|
6ef0c08bc4 | ||
|
|
1ee965067d | ||
|
|
e46a873fce | ||
|
|
784e956c96 | ||
|
|
b6eebb330a | ||
|
|
a4fe8d42d2 | ||
|
|
fa42cadfa1 | ||
|
|
17dc6fa859 | ||
|
|
1432726dec | ||
|
|
7ba6ffa407 | ||
|
|
ab555e4d6b | ||
|
|
d4259f4e23 | ||
|
|
aef45c06f6 | ||
|
|
828f9e6a30 | ||
|
|
e79d778cf4 | ||
|
|
965eb2d994 | ||
|
|
657fccf49f | ||
|
|
228b41a1ac | ||
|
|
3bba294eb7 | ||
|
|
0254181b3e | ||
|
|
3b67282abb | ||
|
|
38af765935 | ||
|
|
ff180b085c | ||
|
|
e0785eb796 | ||
|
|
75a83787a4 | ||
|
|
65bea6343c | ||
|
|
6fe61411a0 | ||
|
|
4552e91022 | ||
|
|
cc94d70e6c | ||
|
|
2435078f76 | ||
|
|
dd2cd50d4b | ||
|
|
fe38eb629d | ||
|
|
e961507456 | ||
|
|
e5ee5644bf | ||
|
|
ac6d85e214 | ||
|
|
39c2d54aee | ||
|
|
68efd5ff9e | ||
|
|
627f516f85 | ||
|
|
a45fadf42e | ||
|
|
2ae52b849c | ||
|
|
639685ea0d | ||
|
|
867209de6d | ||
|
|
1d8b6d346b | ||
|
|
2a5ac327d3 | ||
|
|
886101d4d4 | ||
|
|
8fc6486855 | ||
|
|
eeb1cc71b3 | ||
|
|
13837830e8 | ||
|
|
f86ff46a82 | ||
|
|
e901bc957a | ||
|
|
e7e10c5305 | ||
|
|
5511ee9f38 | ||
|
|
427a22d23b | ||
|
|
d271898968 | ||
|
|
4051f0bd2e |
@@ -1,3 +1,25 @@
|
||||
### Checklist
|
||||
- [ ] The title of this issue is "to the point" and descriptive.
|
||||
- [ ] This issue describes what is happening.
|
||||
- [ ] This issue describes what components are affected (e.g. name of plugin/controller)
|
||||
- [ ] This issue describes how to reproduce it.
|
||||
- [ ] This issue describes when it was introduced (when known) and what version is now showing the problem.
|
||||
|
||||
### I have...
|
||||
- [ ] searched the [issue tracker](https://github.com/letscontrolit/ESPEasy/issues) or the [forum](https://www.letscontrolit.com/forum/viewforum.php?f=1) for a similar issue. (include links when applicable)
|
||||
- [ ] entered a system description using "Copy info to clipboard" on the sysinfo page. (when possible)
|
||||
- [ ] entered the full filename of the used version (e.g. `ESP_Easy_mega-20181001_test_ESP8266_4096_VCC.bin` )
|
||||
- [ ] given a list of active plugins or controllers when applicable.
|
||||
- [ ] filled out all applicable fields below.
|
||||
|
||||
### Steps already tried...
|
||||
- [ ] Tried a clean install (empty `.bin` files are included in the ZIP)
|
||||
- [ ] Tested previous/other build (mention which one already tested)
|
||||
- [ ] Tested on other node to make sure hardware isn't defective.
|
||||
- [ ] Verified if the problem is limited to a single plugin/controller
|
||||
|
||||
|
||||
|
||||
### If you self compile, please state this and PLEASE try to ONLY REPORT ISSUES WITH OFFICIAL BUILDS! ###
|
||||
<!--- If you self compile, please state this and PLEASE try to ONLY REPORT ISSUES WITH OFFICIAL BUILDS! --->
|
||||
<!--- NOTE: This is not a support forum! For questions and support go here: --->
|
||||
@@ -5,6 +27,7 @@
|
||||
<!--- Remove topics that are not applicable to your feature request of issue --->
|
||||
<!--- Remember to have a "to the point" TITLE --->
|
||||
|
||||
|
||||
### Summarize of the problem/feature request
|
||||
<!--- Describe the problem or feature request --->
|
||||
YOUR TEXT GOES HERE
|
||||
|
||||
@@ -18,3 +18,9 @@ lib/readme.txt
|
||||
src/Custom.h
|
||||
/ESPEasy
|
||||
test/output_export.cpp
|
||||
.vscode
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
|
||||
docs/build/
|
||||
|
||||
+2
-1
@@ -22,9 +22,10 @@ addons:
|
||||
|
||||
|
||||
install:
|
||||
- pip install -U platformio
|
||||
- pip install -U platformio sphinx recommonmark sphinx_bootstrap_theme
|
||||
|
||||
script:
|
||||
# - bash ./preflight.sh # make sure input files are OK before wasting time with prereqs
|
||||
# - cppcheck --enable=warning src/*.ino -q --force -I src --include=src/ESPEasy.ino --error-exitcode=1
|
||||
# - ./memanalyzer.py ~/.platformio/packages/toolchain-xtensa/bin/xtensa-lx106-elf-objdump
|
||||
- PLATFORMIO_BUILD_FLAGS="-D CONTINUOUS_INTEGRATION" platformio run
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|Latest Nightly | Build Status | Patreon | Ko-Fi |
|
||||
|-------|-------|-------|-------|
|
||||
| [](https://github.com/letscontrolit/ESPEasy/releases/latest) | [](https://travis-ci.org/letscontrolit/ESPEasy) | [](https://www.patreon.com/GrovkillenTDer) | [](https://ko-fi.com/grovkillentder) |
|
||||
|Latest Nightly | Build Status | Patreon | Ko-Fi | PayPal |
|
||||
|-------|-------|-------|-------|-------|
|
||||
| [](https://github.com/letscontrolit/ESPEasy/releases/latest) | [](https://travis-ci.org/letscontrolit/ESPEasy) | [](https://www.patreon.com/GrovkillenTDer) | [](https://ko-fi.com/grovkillentder) | [](https://www.paypal.me/espeasy) |
|
||||
|
||||
For ways to *support* us, see [this announcement on the forum](https://www.letscontrolit.com/forum/viewtopic.php?f=14&t=5787), or have a look at the [Patreon](https://www.patreon.com/GrovkillenTDer) or [Ko-Fi](https://ko-fi.com/grovkillentder) links above.
|
||||
For ways to *support* us, see [this announcement on the forum](https://www.letscontrolit.com/forum/viewtopic.php?f=14&t=5787), or have a look at the [Patreon](https://www.patreon.com/GrovkillenTDer), [Ko-Fi](https://ko-fi.com/grovkillentder) or [PayPal](https://www.paypal.me/espeasy) links above.
|
||||
|
||||
# ESPEasy (development branch)
|
||||
|
||||
@@ -41,4 +41,6 @@ ESPEasy_mega-20180102_dev_ESP8285_1024.bin | ESP8285 with 1Mb flash | Stabl
|
||||
|
||||
Details and discussion are on the Experimental forum: https://www.letscontrolit.com/forum/viewforum.php?f=18
|
||||
|
||||
Automated builds of the (new) documentation can be found at [ESPEasy.readthedocs.io](https://espeasy.readthedocs.io/en/latest/)
|
||||
|
||||
We're also on IRC: #ESPEasy @freenode
|
||||
|
||||
+9
-3
@@ -21,8 +21,10 @@ for ENV in \
|
||||
dev_ESP8266PUYA_1024\
|
||||
dev_ESP8266PUYA_1024_VCC\
|
||||
hard_SONOFF_POW\
|
||||
hard_SONOFF_POW_R2_4M\
|
||||
hard_Shelly_1\
|
||||
esp32dev;\
|
||||
esp32dev\
|
||||
esp32test_1M8_partition;\
|
||||
do
|
||||
echo
|
||||
echo "### Deploying environment $ENV for version $VERSION"
|
||||
@@ -34,10 +36,14 @@ done
|
||||
|
||||
|
||||
#create a source structure that is the same as the original ESPEasy project (and works with the howto on the wiki)
|
||||
rm -rf dist/Source 2>/dev/null
|
||||
mkdir dist/Source
|
||||
#rm -rf dist/Source 2>/dev/null
|
||||
rm -rf dist/Docs 2>/dev/null
|
||||
|
||||
mkdir -p dist/Source
|
||||
#mkdir -p dist/Docs
|
||||
cp -r lib dist/Source/
|
||||
cp -r src dist/Source/
|
||||
#cp -r docs/build/ dist/Docs/
|
||||
cp platformio.ini dist/Source/
|
||||
|
||||
cd dist
|
||||
|
||||
Vendored
+61
@@ -0,0 +1,61 @@
|
||||
_____ ____ ____
|
||||
| ____/ ___|| _ \ ___ __ _ ___ _ _
|
||||
| _| \___ \| |_) / _ \/ _` / __| | | |
|
||||
| |___ ___) | __/ __/ (_| \__ \ |_| |
|
||||
|_____|____/|_| \___|\__,_|___/\__, |
|
||||
|___/
|
||||
|
||||
|
||||
This is the distribution folder for ESPeasy.
|
||||
In here you will find a flash tool to program the ESP module.
|
||||
|
||||
Also a number of binary images is included.
|
||||
|
||||
The filename is quite descriptive:
|
||||
ESP_Easy_mega-<date>_<buildType>_<chip>_<memorySize>.bin
|
||||
|
||||
Build type can be: (differ in included plugins)
|
||||
- normal => Only Stable plugins and controllers
|
||||
- test => Stable + Testing
|
||||
- dev => Stable + Testing + Development
|
||||
|
||||
There is also a number of special builds:
|
||||
- normal_IR => "Normal" + IR receiver/transmitter plugins and library
|
||||
- hard_xxxxx => Special builds for some off-the-shelf hardware.
|
||||
|
||||
Chip can be:
|
||||
- ESP8266 => Most likely option
|
||||
- ESP8285 => Used in some Sonoff modules
|
||||
- ESP32 => Experimental support at this moment
|
||||
- ESP8266PUYA => For ESP modules with a flash chip labeled "PUYA" (often 1 MB)
|
||||
|
||||
The "PUYA" version deserves a bit more attention.
|
||||
If you cannot save settings, better try to see if the flash chip has "PUYA" written on it.
|
||||
If so, try the "PUYA" build.
|
||||
|
||||
MemorySize can be:
|
||||
- 1024 => 1 MB flash modules (e.g. almost all Sonoff modules)
|
||||
- 2048 => 2 MB flash modules (e.g. Shelly1/WROOM02)
|
||||
- 4096 => 4 MB flash modules (e.g. NodeMCU/ESP32)
|
||||
|
||||
ESP32 now has 2 builds:
|
||||
- esp32dev Using the default partition layout (1.4 MB for the sketch)
|
||||
- esp32test_1M8_partition Larger sketch partition (1.8MB) smaller SPIFFS (316 kB)
|
||||
|
||||
Please note that changing between those versions will destroy the settings!
|
||||
The SPIFFS partition will be lost and that contains all settings.
|
||||
|
||||
|
||||
To help recover from a bad flash, there are also blank images included.
|
||||
- blank_1MB.bin
|
||||
- blank_2MB.bin
|
||||
- blank_4MB.bin
|
||||
|
||||
When the wrong image is flashed, or the module behaves unstable, or is in a reboot loop,
|
||||
flash these images first and then the right image for the module.
|
||||
|
||||
Another great flash tool can be found here: (Windows only)
|
||||
https://github.com/Grovkillen/ESP_Easy_Flasher
|
||||
|
||||
For more information, see: https://github.com/letscontrolit/ESPEasy
|
||||
Or our forum: https://www.letscontrolit.com/forum/
|
||||
Vendored
+570
@@ -1,3 +1,573 @@
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181029 (since mega-20181028)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Mon Oct 29 04:00:08 CET 2018
|
||||
|
||||
Bartlomiej Zimon (2):
|
||||
Dallas: add ScratchPad to debug log - OK suffix means data integrity ok (no communication problems)
|
||||
Dallas: use reset timming from OneWire lib
|
||||
|
||||
TD-er (2):
|
||||
[NTP] Improve NTP accuracy
|
||||
[NTP] Change log level for NTP success to INFO
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181028 (since mega-20181027)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sun Oct 28 04:00:09 CET 2018
|
||||
|
||||
Saverio Cisternino (5):
|
||||
[CRON] Replace timeStruct with standard tm
|
||||
[CRON] Plugin
|
||||
[CRON] Add event PLUGIN_TIME_CHANGE
|
||||
[CRON] Intercept event PLUGIN_TIME_CHANGE
|
||||
[CRON] Fixed Daily flash write rate exceeded!
|
||||
|
||||
TD-er (2):
|
||||
[Cron] Rename plugin name to match naming convention
|
||||
[Eastron] Make configurable and allow for multiple values read (#1684)
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181027 (since mega-20181026)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sat Oct 27 04:00:12 CEST 2018
|
||||
|
||||
Bartlomiej Zimon (1):
|
||||
CSE7766 - fix stats plus add buffer fill level
|
||||
|
||||
Gilad (7):
|
||||
Add IR RAW2 Reading / Transmitting.
|
||||
Add IR RAW2 Reading / Transmitting.
|
||||
Add IR RAW2 Reading / Transmitting - fixed ambiguity.
|
||||
Add IR RAW2 Reading / Transmitting - added a safety check.
|
||||
Improved IR RAW2 format to support RLE.
|
||||
Fixed warnings...
|
||||
Fixed warnings...
|
||||
|
||||
Gilad Raz (1):
|
||||
Increased buffer size. Added constants.
|
||||
|
||||
Plebs (4):
|
||||
doubleclick and longpress for PCF and MCP
|
||||
fixed a display bug
|
||||
final fixes
|
||||
fixed as requested
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181026 (since mega-20181025)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Fri Oct 26 04:00:22 CEST 2018
|
||||
|
||||
Bartlomiej Zimon (7):
|
||||
CSE7766 uses 8E1 serial connection
|
||||
Ser2Net catch serial data only if plugin used
|
||||
CSE7766 add online checksumming, autosyncing without data loos
|
||||
move to PLUGIN_TEN_PER_SECOND update comments
|
||||
move to PLUGIN_SERIAL_IN from PLUGIN_TEN_PER_SECOND - serial commandline uses our data
|
||||
optimization - remove double checksum check
|
||||
CSE7766 - add some stats to debug log
|
||||
|
||||
Saverio Cisternino (1):
|
||||
Fixed plugin timer processing do not start, when call setPluginTaskTimer in PLUGIN_TIMER_IN
|
||||
|
||||
TD-er (1):
|
||||
[C009] Reduce memory usage for C009 controller.
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181025 (since mega-20181023)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Thu Oct 25 04:00:13 CEST 2018
|
||||
|
||||
Saverio Cisternino (1):
|
||||
Added auto redirect to home after run reboot command from web page.
|
||||
|
||||
TD-er (5):
|
||||
[WDT] Change yield() to delay(0)
|
||||
[Build] Add Sonoff POW R2 (4MB)
|
||||
[Sonoff] Add pre-defined switch and relay + rules on factory reset
|
||||
[Info] Remove MD5 check fail on self-built images
|
||||
[Travis] Use variables even when no predefined plugins are added.
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181023 (since mega-20181022)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Tue Oct 23 04:00:14 CEST 2018
|
||||
|
||||
Kurairaito (2):
|
||||
Update _P067_HX711_Load_Cell.ino
|
||||
Update _P067_HX711_Load_Cell.ino
|
||||
|
||||
TD-er (6):
|
||||
[#1486] Allow multiple instances of rotary encoder
|
||||
[JavaScript] Fix update values on Devices tab
|
||||
[JavaScript] Cleanup some code duplication
|
||||
[PubSubClient] Add delay when waiting for data and flush on disconnect
|
||||
[WiFi timeout] Set default to 1000 msec again
|
||||
[Docs] Add some WiFi related information to docs
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181022 (since mega-20181021)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Mon Oct 22 04:00:15 CEST 2018
|
||||
|
||||
TD-er (2):
|
||||
[Webserver] Generate copyText id names
|
||||
[Webserver] Uniform row labels to reduce flash string size
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181021 (since mega-20181020)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sun Oct 21 04:00:16 CEST 2018
|
||||
|
||||
Bartlomiej Zimon (2):
|
||||
fix for #1920 fix for out of sync, check both header bytes proper check for abnormal state
|
||||
Revert "fix for #1920"
|
||||
|
||||
TD-er (6):
|
||||
[stack usage] Allocate temp char array on heap for parsing arguments
|
||||
Proper look for connection.
|
||||
[Rules] Early exit on rules processing when rules not enabled
|
||||
[#1910] Notification settings extra strings on stack
|
||||
[#1891] Send all values for Generic UDP controller
|
||||
[#1870] Feature request build date as system info
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181020 (since mega-20181017)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sat Oct 20 04:00:16 CEST 2018
|
||||
|
||||
Plebs (22):
|
||||
Added [plugin#pcfgpio#pinstate#xx]
|
||||
added [plugin#mcpgpio#pinstate#xx]
|
||||
code optimization
|
||||
some code optimizations
|
||||
Update src/_P001_Switch.ino
|
||||
Update src/_P001_Switch.ino
|
||||
small fixes
|
||||
few changes
|
||||
Update src/_P001_Switch.ino
|
||||
Update src/_P001_Switch.ino
|
||||
some fixes
|
||||
added global variables for rules
|
||||
some fixes
|
||||
fixes
|
||||
fixed length check
|
||||
some optimizations
|
||||
modified command and variable name
|
||||
Update src/_P001_Switch.ino
|
||||
Update src/_P001_Switch.ino
|
||||
optimizations
|
||||
Update src/StringConverter.ino
|
||||
added 4 variables to verify the system status
|
||||
|
||||
kpalczewski (1):
|
||||
add support for 64bit IR codes.
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181017 (since mega-20181016)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Wed Oct 17 04:00:18 CEST 2018
|
||||
|
||||
Gijs Noorlander (1):
|
||||
Added checklist to the Issue_template
|
||||
|
||||
TD-er (3):
|
||||
[#1906] Allocate NotificationSettings on the heap
|
||||
[#1906] Allocate ControllerSettings on the heap
|
||||
[Stack] Add Free Stack to System Info plugin
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181016 (since mega-20181015)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Tue Oct 16 04:00:06 CEST 2018
|
||||
|
||||
Plebs (1):
|
||||
Fix for togglegpio without gpio initialization
|
||||
|
||||
TD-er (1):
|
||||
[Revert] Safe_strncpy cannot be used as generic strncpy replacement
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181015 (since mega-20181014)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Mon Oct 15 04:00:10 CEST 2018
|
||||
|
||||
Plebs (2):
|
||||
fix for bug #1864
|
||||
Update src/_P023_OLED.ino
|
||||
|
||||
TD-er (1):
|
||||
[safe strncpy] Add warning when entered string is too large
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181014 (since mega-20181011)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sun Oct 14 04:00:13 CEST 2018
|
||||
|
||||
Gijs Noorlander (1):
|
||||
[#1799] Add SSID to main page
|
||||
|
||||
TD-er (5):
|
||||
[#1891] Generic UPD controller - send only first value from sensor
|
||||
[Controller] Fix send only first value
|
||||
Time#Initialized event in rules before %syshour% is set
|
||||
[UX] Warn user for incomplete network IP settings
|
||||
[#1895] Incorrect number bytes read I2C_read32_reg
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181011 (since mega-20181010)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Thu Oct 11 04:00:24 CEST 2018
|
||||
|
||||
TD-er (5):
|
||||
[C009] Reduce memory usage of FHEM HTTP delay queue
|
||||
[Controller] Set default timeout to 300 msec (was 100 msec)
|
||||
Make sure to load ExtraTaskSettings when needed
|
||||
[Travis Doc] Remove building documentation by Travis
|
||||
[WebLog] Disable weblog when it is not being read
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181010 (since mega-20181009)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Wed Oct 10 04:00:09 CEST 2018
|
||||
|
||||
Plebs (1):
|
||||
Update src/_P019_PCF8574.ino
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181009 (since mega-20181008)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Tue Oct 9 04:00:18 CEST 2018
|
||||
|
||||
TD-er (7):
|
||||
[Docs] Add favicon to documentation
|
||||
[Docs] Add Travis check for documentation and stop build for docs only
|
||||
[Docs] Run Travic documentation check before compiling project
|
||||
[Docs] Disable travis_terminate for now
|
||||
[JSON] Replace \n and \r in text fields
|
||||
[LWIP1.4] Move back to LWIP1.4
|
||||
[StringConverter] Some optimisations to reduce memory allocation
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181008 (since mega-20181007)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Mon Oct 8 04:00:09 CEST 2018
|
||||
|
||||
Gijs Noorlander (1):
|
||||
Added link to ReadTheDocs
|
||||
|
||||
Grovkillen (1):
|
||||
[Sphinx] added extension "sphinx.ext.imgconverter"
|
||||
|
||||
TD-er (3):
|
||||
[Sphinx] Initial setup for Sphinx documentation
|
||||
[Docs] First ESPEasy documentation (Controllers)
|
||||
[Docs] Added some directory structure and template
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181007 (since mega-20181006)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sun Oct 7 04:00:09 CEST 2018
|
||||
|
||||
Max (2):
|
||||
Force slash to be converted in htmlEscape
|
||||
Fix XSS-Vulnerabilites on wifi configuration pages
|
||||
|
||||
Plebs (3):
|
||||
Add three new TOGGLE commands
|
||||
fixed float to int conversion
|
||||
added "mcpgpiotoggle" command
|
||||
|
||||
TD-er (3):
|
||||
[ESP32] Allow Wrover dev kit to be used for PIO debug
|
||||
[Stack] Move Arduino 'cont' stack into user RAM
|
||||
[SPIFFS] Make sure to close open files on SPIFFS
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181006 (since mega-20181004)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sat Oct 6 04:00:13 CEST 2018
|
||||
|
||||
TD-er (3):
|
||||
[Stack] Increase stack to 5k and reduce stack allocations in rules
|
||||
[WiFi] Add delay to connection attempts in ControllerSettings
|
||||
[Build flags] Remove NO_EXTRA_4K_HEAP
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181004 (since mega-20181003)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Thu Oct 4 04:00:08 CEST 2018
|
||||
|
||||
Bartlomiej Zimon (1):
|
||||
- partial fix for #1723
|
||||
|
||||
JojoS (1):
|
||||
added error checking and logging
|
||||
|
||||
JojoS62 (5):
|
||||
added float calculation for time to distance conversion
|
||||
added more settings: unit, filtertype and size
|
||||
max distance depends on measurement unit
|
||||
code clean up
|
||||
fixed 'unused variable' warning
|
||||
|
||||
TD-er (3):
|
||||
[sendHttp] #1830 Set timeout and early exit on timeout reached
|
||||
[WiFiClient] Set timeout and make it configurable for controllers
|
||||
[Core 2.4.1] Move back to core 2.4.1 from 2.4.2
|
||||
|
||||
Thomas (1):
|
||||
Update _P075_Nextion.ino (#1)
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181003 (since mega-20181002)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Wed Oct 3 04:00:18 CEST 2018
|
||||
|
||||
David Conran (2):
|
||||
Acknowledge credit & origin of code used in IRTX
|
||||
Acknowledge credit & origin of code used in _P016IR.ino
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181002 (since mega-20181001)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Tue Oct 2 04:00:29 CEST 2018
|
||||
|
||||
Plebs (1):
|
||||
split parseTemplate function
|
||||
|
||||
TD-er (4):
|
||||
[#1798] Bugfix Sonoff Si7021 stability
|
||||
[ESP32] Add default partition table for reference
|
||||
[ESP32] Update to PIO espressif32@1.4.0
|
||||
[Stack] Report stack usage on ESP32 and increase stack for ESP8266normal
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20181001 (since mega-20180930)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Mon Oct 1 13:13:13 CEST 2018
|
||||
|
||||
TD-er (4):
|
||||
[#1824] Show stack size on main page
|
||||
[Heap] Use NO_EXTRA_4K_HEAP to get old stack address space
|
||||
[Stack] Check if a block sent to save on SPIFFS is stack allocated
|
||||
[Stack] Revert move Settings from stack to heap
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180930 (since mega-20180927)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sun Sep 30 04:00:24 CEST 2018
|
||||
|
||||
JojoS (5):
|
||||
fixed distance reading
|
||||
on INIT, delete sensordef before adding new
|
||||
removed sensordefs helper structure
|
||||
changed implementation to use shared_ptr instead of helper structure
|
||||
minor change: unit was missing for threshold in 'state' mode
|
||||
|
||||
TD-er (2):
|
||||
[Default Settings] Properly clear settings when reset to default
|
||||
[Defaults] Other default initializations
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180927 (since mega-20180924)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Thu Sep 27 04:00:17 CEST 2018
|
||||
|
||||
TD-er (7):
|
||||
[ESP32] Change partition size (will clear all settings) and add plugins
|
||||
[Travis] Uninitialized variable in P073_7DGT
|
||||
[Stack] Move Settings from stack to heap
|
||||
[ESP32] RTOS move handle_schedule() to core1 and increase stack
|
||||
[Info] Add board name to the sysinfo page.
|
||||
[ESP32] Split new partition layout to separate build
|
||||
[Build] Add esp32test_1M8_partition build and warning on ESP32 partition
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180924 (since mega-20180923)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Mon Sep 24 04:00:20 CEST 2018
|
||||
|
||||
Benno Eigenmann (1):
|
||||
Fix platformio
|
||||
|
||||
TD-er (2):
|
||||
[MQTT] Work around for lost MQTT connection
|
||||
[ESP32] Fix I2C handling to allow I2C devices to work
|
||||
|
||||
sakinit (1):
|
||||
change to platform version core_esp32_1_3_0
|
||||
|
||||
stefan (1):
|
||||
Change type of sensor for MCP and PCF devices
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180923 (since mega-20180922)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sun Sep 23 04:00:12 CEST 2018
|
||||
|
||||
TD-er (2):
|
||||
[MQTT import] Fix crash at boot when plugin is enabled
|
||||
[BME280] Fix slow reading of BME280 causing watchdog resets
|
||||
|
||||
sakinit (5):
|
||||
ESP32 build issues fixed
|
||||
Fix ESP32 stack overflow in addPinSelect when called from P1WifiGateway
|
||||
ESP32: Split webserver spiffs filelist into pages
|
||||
Reverted Webserver change
|
||||
ESP32 platform updated to 1.2.0; Webserver replaced by platform one
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180922 (since mega-20180916)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sat Sep 22 04:00:22 CEST 2018
|
||||
|
||||
Gijs Noorlander (1):
|
||||
Revert "Revert "Added 2 new operators in Calculate() function""
|
||||
|
||||
TD-er (2):
|
||||
Revert "[Scheduler] Disable command scheduler"
|
||||
Revert "Revert "Fix elseif & Add if-elseif-else nesting levels""
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180916 (since mega-20180915)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sun Sep 16 04:00:11 CEST 2018
|
||||
|
||||
Gijs Noorlander (3):
|
||||
Revert "Added 2 new operators in Calculate() function"
|
||||
Revert "Fix elseif & Add if-elseif-else nesting levels"
|
||||
Added warning to release notes 20180915
|
||||
|
||||
TD-er (1):
|
||||
[HW Watchdog] Backgroundtasks instead of yield during rules handling
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180915 (since mega-20180914)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sat Sep 15 04:00:15 CEST 2018
|
||||
Known issue:
|
||||
Causes reboots. (see [#1748](https://github.com/letscontrolit/ESPEasy/issues/1748) )
|
||||
|
||||
|
||||
Plebs (1):
|
||||
Added 2 new operators in Calculate
|
||||
|
||||
TD-er (4):
|
||||
[Scheduler] Disable command scheduler
|
||||
[Controller settings] Validate settings on load/save
|
||||
[#1713] Make hostname-unitnumber optional & validate settings
|
||||
[#1698] Incorrect sunrise value southern hemisphere
|
||||
|
||||
svmac (4):
|
||||
Fix elseif & Add if-elseif-else nesting levels
|
||||
reenabled code about !
|
||||
some code optimization
|
||||
fix mistake
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180914 (since mega-20180910)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Fri Sep 14 04:00:17 CEST 2018
|
||||
|
||||
Gijs Noorlander (1):
|
||||
Added PayPal link
|
||||
|
||||
giddyhup (1):
|
||||
Fix IR plugin passing received code
|
||||
|
||||
Plebs (7):
|
||||
Use parameters inside rules when calling commands
|
||||
few changes
|
||||
fixed ESP32 compatibility issue
|
||||
fixed calls to F-macro
|
||||
adding more parameters to EVENT
|
||||
final version with '=' sign as a prefix to identify the formula in rules
|
||||
added %eventvalue4%
|
||||
|
||||
TD-er (2):
|
||||
[Flash tools] Add blank files and README.txt
|
||||
[CrashRecover] Disable failing step based on failed boot count
|
||||
|
||||
sakinit (1):
|
||||
Feature: Split webserver spiffs filelist into pages
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180910 (since mega-20180909)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Mon Sep 10 04:00:22 CEST 2018
|
||||
|
||||
TD-er (2):
|
||||
[HTTP] Command SendToHttp cannot connect
|
||||
[Domoticz IDX] Allow for IDX of > 9999
|
||||
|
||||
sakinit (1):
|
||||
Reduced buffer size to prevent stack overflow
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180909 (since mega-20180908)
|
||||
-------------------------------------------------
|
||||
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
@@ -0,0 +1,19 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
@@ -0,0 +1,35 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=source
|
||||
set BUILDDIR=build
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
|
||||
:end
|
||||
popd
|
||||
@@ -0,0 +1,3 @@
|
||||
recommonmark
|
||||
Sphinx
|
||||
sphinx-bootstrap-theme
|
||||
@@ -0,0 +1,66 @@
|
||||
Controller
|
||||
**********
|
||||
|
||||
A controller is a component to enable a plugin to send data elsewhere.
|
||||
|
||||
- Up-to 3 controllers can be active in ESPEasy.
|
||||
- Per plugin up-to 3 active controllers can be selected.
|
||||
- For some controllers an additional parameter can be given.
|
||||
|
||||
For example, Domoticz needs an 'IDX' value to identify the configured entry in
|
||||
Domoticz for which new data is sent.
|
||||
|
||||
Controller Parameters
|
||||
=====================
|
||||
|
||||
Generic fields
|
||||
--------------
|
||||
|
||||
- **Protocol** - The type of controller (e.g. ThingSpeak/OpenHAB MQTT/etc.)
|
||||
- **Locate Controller** - Selection between hostname/IP
|
||||
- **Controller Hostname/IP** - The address to reach the selected service
|
||||
- **Controller Port** - TCP/UDP Port number (0...65536)
|
||||
- **Enabled** - Whether or not the controller is active.
|
||||
|
||||
Send queue parameters
|
||||
---------------------
|
||||
|
||||
Controllers have a queue to keep unsent messages.
|
||||
This queue is used to handle message bursts and also store messages which are recorded
|
||||
before WiFi connection is made or during lost connection.
|
||||
|
||||
- **Minimum Send Interval** - Minimum time between two messages in msec.
|
||||
- **Max Queue Depth** - Maximum length of the buffer queue to keep unsent messages.
|
||||
- **Max Retries** - Maximum number of retries to send a message.
|
||||
- **Full Queue Action** - How to handle when queue is full, ignore new or delete oldest message.
|
||||
- **Client Timeout** - Timeout in msec for an network connection used by the controller.
|
||||
- **Check Reply** - When set to false, a sent message is considered always successful.
|
||||
|
||||
|
||||
Sample ThingSpeak configuration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Some controllers, like ThingSpeak, need a specific configuration.
|
||||
ThingSpeak only allows a message every 15 seconds for the free accounts.
|
||||
|
||||
- **Minimum Send Interval** - 15000 msec
|
||||
- **Max Queue Depth** - 1 (only report the last value)
|
||||
- **Max Retries** - 2
|
||||
- **Full Queue Action** - Delete Oldest
|
||||
- **Client Timeout** - 500 msec (server is online, so timeout must be a bit longer)
|
||||
|
||||
|
||||
Controller user credentials
|
||||
---------------------------
|
||||
|
||||
- **Controller User** - User name (optional)
|
||||
- **Controller Password** - Password (optional)
|
||||
|
||||
MQTT related settings
|
||||
---------------------
|
||||
|
||||
- **Controller Subscribe** - Subscribe to the given topic.
|
||||
- **Controller Publish** - Publish to the given topic.
|
||||
- **Controller lwl topic** - Topic to which LWT (Last Will Testament) messages should be sent.
|
||||
- **LWT Connect Message** - Connection established message.
|
||||
- **LWT Disconnect Message** - Connection lost message (sent to broker during connect and published by broker when connection is lost)
|
||||
@@ -0,0 +1,2 @@
|
||||
About Us
|
||||
********
|
||||
@@ -0,0 +1,2 @@
|
||||
ESPEasy Function Blocks
|
||||
***********************
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,35 @@
|
||||
Documentation
|
||||
*************
|
||||
|
||||
For documentation we use Sphinx and this will be read by ReadTheDocs.
|
||||
We use the `Sphinx Bootstrap Theme <https://github.com/ryan-roemer/sphinx-bootstrap-theme>`_
|
||||
|
||||
This documentation is included in the GitHub repository.
|
||||
It allows us to create documentation per version of ESPEasy.
|
||||
|
||||
See also the `ESPEasy wiki <https://www.letscontrolit.com/wiki/index.php/ESPEasy>`_
|
||||
for more documentation which has not been moved here.
|
||||
|
||||
Needed Python packages::
|
||||
|
||||
pip install sphinx recommonmark sphinx_bootstrap_theme
|
||||
|
||||
PlatformIO with Atom
|
||||
====================
|
||||
|
||||
With the ESPEasy project open in Atom, open the PIO terminal in Atom.
|
||||
|
||||
Install dependencies::
|
||||
|
||||
cd docs
|
||||
pip install ... (see needed Python packages above)
|
||||
|
||||
Build on Windows::
|
||||
|
||||
cd docs
|
||||
.\make.bat html
|
||||
|
||||
Build on Linux/Mac::
|
||||
|
||||
cd docs
|
||||
./make html
|
||||
@@ -0,0 +1,2 @@
|
||||
Plugins
|
||||
*******
|
||||
@@ -0,0 +1,2 @@
|
||||
Command Reference
|
||||
*****************
|
||||
@@ -0,0 +1,2 @@
|
||||
System Variables
|
||||
****************
|
||||
@@ -0,0 +1,2 @@
|
||||
Rules
|
||||
*****
|
||||
@@ -0,0 +1,25 @@
|
||||
WiFi
|
||||
****
|
||||
|
||||
WiFi disconnect reasons
|
||||
=======================
|
||||
|
||||
Beacon timeout (200)
|
||||
--------------------
|
||||
|
||||
For more information on the Beacon frame, see `Wikipedia <https://en.wikipedia.org/wiki/Beacon_frame>`_.
|
||||
|
||||
In short, the access point sends periodically a packet containing information about the network.
|
||||
This interval is typically 100 TU (102.4 msec).
|
||||
The ESP module is trying to listen to this beacon every time, but for a number of reasons it may
|
||||
miss a beacon frame.
|
||||
The timeout is longer than 100 TU, so it must miss a number of these beacon frames
|
||||
to report a beacon timeout.
|
||||
Reasons to miss such a beacon frame are:
|
||||
|
||||
- too busy processing other blocking tasks (very likely)
|
||||
- access point not sending a beacon due to high traffic demands of others (depending on brand/model/settings)
|
||||
- RF disturbances (not likely given how often this occurs)
|
||||
- clock drift (not really likely)
|
||||
|
||||
So the "beacon timeout" is happening every now and then on the ESP nodes and ESPeasy will try to reconnect.
|
||||
@@ -0,0 +1,286 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file does only contain a selection of the most common options. For a
|
||||
# full list see the documentation:
|
||||
# http://www.sphinx-doc.org/en/master/config
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
from recommonmark.parser import CommonMarkParser
|
||||
import sphinx_bootstrap_theme
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = u'ESPEasy'
|
||||
copyright = u'2018, ESPEasy'
|
||||
author = u'Many'
|
||||
|
||||
# The short X.Y version
|
||||
version = u''
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = u'2.1b1'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.imgmath',
|
||||
'sphinx.ext.imgconverter',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
source_parsers = {
|
||||
'.md': CommonMarkParser,
|
||||
}
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
source_suffix = ['.rst', '.md']
|
||||
#source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = []
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = None
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
html_logo = 'ESPEasy/espeasy_logo.png'
|
||||
html_favicon = 'ESPEasy/favicon.ico'
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
#html_theme = 'sphinx_rtd_theme'
|
||||
html_theme = 'bootstrap'
|
||||
html_theme_path = sphinx_bootstrap_theme.get_html_theme_path()
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
#html_theme_options = {
|
||||
# 'canonical_url': '',
|
||||
# 'analytics_id': '',
|
||||
# 'logo_only': False,
|
||||
# 'display_version': True,
|
||||
# 'prev_next_buttons_location': 'bottom',
|
||||
# 'style_external_links': False,
|
||||
# # Toc options
|
||||
# 'collapse_navigation': True,
|
||||
# 'sticky_navigation': True,
|
||||
# 'navigation_depth': 4,
|
||||
# 'includehidden': True,
|
||||
# 'titles_only': False
|
||||
#}
|
||||
|
||||
html_theme_options = {
|
||||
# Navigation bar title. (Default: ``project`` value)
|
||||
'navbar_title': "ESPEasy",
|
||||
|
||||
# Tab name for entire site. (Default: "Site")
|
||||
'navbar_site_name': "Site",
|
||||
|
||||
# A list of tuples containing pages or urls to link to.
|
||||
# Valid tuples should be in the following forms:
|
||||
# (name, page) # a link to a page
|
||||
# (name, "/aa/bb", 1) # a link to an arbitrary relative url
|
||||
# (name, "http://example.com", True) # arbitrary absolute url
|
||||
# Note the "1" or "True" value above as the third argument to indicate
|
||||
# an arbitrary url.
|
||||
'navbar_links': [
|
||||
("Rules", "rules"),
|
||||
("Forum", "https://www.letscontrolit.com/forum/viewforum.php?f=1", True),
|
||||
],
|
||||
|
||||
# Render the next and previous page links in navbar. (Default: true)
|
||||
'navbar_sidebarrel': True,
|
||||
|
||||
# Render the current pages TOC in the navbar. (Default: true)
|
||||
'navbar_pagenav': True,
|
||||
|
||||
# Tab name for the current pages TOC. (Default: "Page")
|
||||
'navbar_pagenav_name': "Page",
|
||||
|
||||
# Global TOC depth for "site" navbar tab. (Default: 1)
|
||||
# Switching to -1 shows all levels.
|
||||
'globaltoc_depth': 2,
|
||||
|
||||
# Include hidden TOCs in Site navbar?
|
||||
#
|
||||
# Note: If this is "false", you cannot have mixed ``:hidden:`` and
|
||||
# non-hidden ``toctree`` directives in the same page, or else the build
|
||||
# will break.
|
||||
#
|
||||
# Values: "true" (default) or "false"
|
||||
'globaltoc_includehidden': "true",
|
||||
|
||||
# HTML navbar class (Default: "navbar") to attach to <div> element.
|
||||
# For black navbar, do "navbar navbar-inverse"
|
||||
'navbar_class': "navbar navbar-inverse",
|
||||
|
||||
# Fix navigation bar to top of page?
|
||||
# Values: "true" (default) or "false"
|
||||
'navbar_fixed_top': "true",
|
||||
|
||||
# Location of link to source.
|
||||
# Options are "nav" (default), "footer" or anything else to exclude.
|
||||
'source_link_position': "none",
|
||||
|
||||
# Bootswatch (http://bootswatch.com/) theme.
|
||||
#
|
||||
# Options are nothing (default) or the name of a valid theme
|
||||
# such as "cosmo" or "sandstone".
|
||||
#
|
||||
# The set of valid themes depend on the version of Bootstrap
|
||||
# that's used (the next config option).
|
||||
#
|
||||
# Currently, the supported themes are:
|
||||
# - Bootstrap 2: https://bootswatch.com/2
|
||||
# - Bootstrap 3: https://bootswatch.com/3
|
||||
'bootswatch_theme': "cerulean",
|
||||
|
||||
# Choose Bootstrap version.
|
||||
# Values: "3" (default) or "2" (in quotes)
|
||||
'bootstrap_version': "3",
|
||||
}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Custom sidebar templates, must be a dictionary that maps document names
|
||||
# to template names.
|
||||
#
|
||||
# The default sidebars (for documents that don't match any pattern) are
|
||||
# defined by theme itself. Builtin themes are using these templates by
|
||||
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
|
||||
# 'searchbox.html']``.
|
||||
#
|
||||
# html_sidebars = {}
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ---------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'ESPEasydoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'ESPEasy.tex', u'ESPEasy Documentation',
|
||||
u'Many', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'espeasy', u'ESPEasy Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output ----------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'ESPEasy', u'ESPEasy Documentation',
|
||||
author, 'ESPEasy', 'ESP82xx/ESP32 firmware to connect people with any level of experience to system automation.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Epub output -------------------------------------------------
|
||||
|
||||
# Bibliographic Dublin Core info.
|
||||
epub_title = project
|
||||
|
||||
# The unique identifier of the text. This can be a ISBN number
|
||||
# or the project homepage.
|
||||
#
|
||||
# epub_identifier = ''
|
||||
|
||||
# A unique identification for the text.
|
||||
#
|
||||
# epub_uid = ''
|
||||
|
||||
# A list of files that should not be packed into the epub file.
|
||||
epub_exclude_files = ['search.html']
|
||||
|
||||
|
||||
# -- Extension configuration -------------------------------------------------
|
||||
|
||||
# -- Options for intersphinx extension ---------------------------------------
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {'https://docs.python.org/': None}
|
||||
|
||||
# -- Options for todo extension ----------------------------------------------
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = True
|
||||
@@ -0,0 +1,39 @@
|
||||
.. ESPEasy documentation master file, created by
|
||||
sphinx-quickstart on Sat Oct 06 22:34:34 2018.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to ESPEasy's documentation!
|
||||
===================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Table of Contents
|
||||
|
||||
ESPEasy/AboutUs.rst
|
||||
ESPEasy/FunctionBlocks.rst
|
||||
Controller/Controller.rst
|
||||
Plugin/Plugin.rst
|
||||
Rules/Rules.rst
|
||||
WiFi/WiFi.rst
|
||||
|
||||
.. raw:: latex
|
||||
|
||||
\appendix
|
||||
|
||||
|
||||
.. toctree::
|
||||
:caption: Appendix
|
||||
|
||||
Participate/Documentation.rst
|
||||
Reference/Command.rst
|
||||
Reference/SystemVariable.rst
|
||||
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
@@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x140000,
|
||||
app1, app, ota_1, 0x150000,0x140000,
|
||||
eeprom, data, 0x99, 0x290000,0x1000,
|
||||
spiffs, data, spiffs, 0x291000,0x16F000,
|
||||
|
@@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x1D0000,
|
||||
app1, app, ota_1, 0x1E0000,0x1D0000,
|
||||
eeprom, data, 0x99, 0x3B0000,0x1000,
|
||||
spiffs, data, spiffs, 0x3B1000,0x4F000,
|
||||
|
@@ -6,12 +6,13 @@
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#include "NewPingESP8266.h"
|
||||
|
||||
//#include "ESPEasy-Globals.h"
|
||||
// ---------------------------------------------------------------------------
|
||||
// NewPingESP8266 constructor
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
NewPingESP8266::NewPingESP8266(uint32_t trigger_pin, uint32_t echo_pin, unsigned int max_cm_distance) {
|
||||
_errorState = STATUS_SENSOR_READY;
|
||||
#if DO_BITWISE == true
|
||||
_triggerBit = digitalPinToBitMask(trigger_pin); // Get the port register bitmask for the trigger pin.
|
||||
_echoBit = digitalPinToBitMask(echo_pin); // Get the port register bitmask for the echo pin.
|
||||
@@ -58,7 +59,10 @@ unsigned int NewPingESP8266::ping(unsigned int max_cm_distance) {
|
||||
while (!digitalRead(_echoPin)) // Wait for the ping echo.
|
||||
#endif
|
||||
{
|
||||
if (micros() > _max_time) return NO_ECHO; // Stop the loop and return NO_ECHO (false) if we're beyond the set maximum distance.
|
||||
if ((_max_cm_distance > 0) && (micros() > _max_time)) {
|
||||
_errorState = STATUS_MAX_DISTANCE_EXCEEDED;
|
||||
return NO_ECHO; // Stop the loop and return NO_ECHO (false) if we're beyond the set maximum distance.
|
||||
}
|
||||
}
|
||||
#else
|
||||
#if DO_BITWISE == true
|
||||
@@ -67,10 +71,14 @@ unsigned int NewPingESP8266::ping(unsigned int max_cm_distance) {
|
||||
while (digitalRead(_echoPin)) // Wait for the ping echo.
|
||||
#endif
|
||||
{
|
||||
if (micros() > _max_time) return NO_ECHO; // Stop the loop and return NO_ECHO (false) if we're beyond the set maximum distance.
|
||||
if ((_max_cm_distance > 0) && (micros() > _max_time)) {
|
||||
_errorState = STATUS_MAX_DISTANCE_EXCEEDED;
|
||||
return NO_ECHO; // Stop the loop and return NO_ECHO (false) if we're beyond the set maximum distance.
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
_errorState = STATUS_MEASUREMENT_VALID;
|
||||
return (micros() - (_max_time - _maxEchoTime) - PING_OVERHEAD); // Calculate ping time, include overhead.
|
||||
}
|
||||
|
||||
@@ -144,15 +152,39 @@ boolean NewPingESP8266::ping_trigger() {
|
||||
#endif
|
||||
|
||||
#if URM37_ENABLED == true
|
||||
if (!(*_echoInput & _echoBit)) return false; // Previous ping hasn't finished, abort.
|
||||
if (!(*_echoInput & _echoBit)) { // Previous ping hasn't finished, abort.{
|
||||
_errorState = STATUS_ECHO_STATE_ERROR;
|
||||
return false;
|
||||
}
|
||||
_max_time = micros() + _maxEchoTime + MAX_SENSOR_DELAY; // Maximum time we'll wait for ping to start (most sensors are <450uS, the SRF06 can take up to 34,300uS!)
|
||||
while (*_echoInput & _echoBit) // Wait for ping to start.
|
||||
if (micros() > _max_time) return false; // Took too long to start, abort.
|
||||
{
|
||||
if (millis() > (start + 50)) {
|
||||
_errorState = STATUS_ECHO_START_TIMEOUT_50ms;
|
||||
return false;
|
||||
}
|
||||
if ((_max_cm_distance > 0) && (micros() > _max_time)) { // check max. distance if != 0 cm
|
||||
_errorState = STATUS_ECHO_START_TIMEOUT_DISTANCE;
|
||||
return false; // echo too late, max distance exceeded
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (*_echoInput & _echoBit) return false; // Previous ping hasn't finished, abort.
|
||||
if (*_echoInput & _echoBit) { // Previous ping hasn't finished, abort.
|
||||
_errorState = STATUS_ECHO_STATE_ERROR;
|
||||
return false;
|
||||
}
|
||||
_max_time = micros() + _maxEchoTime + MAX_SENSOR_DELAY; // Maximum time we'll wait for ping to start (most sensors are <450uS, the SRF06 can take up to 34,300uS!)
|
||||
while (!(*_echoInput & _echoBit)) // Wait for ping to start.
|
||||
if (micros() > _max_time) return false; // Took too long to start, abort.
|
||||
{
|
||||
if (millis() > (start + 50)) {
|
||||
_errorState = STATUS_ECHO_START_TIMEOUT_50ms;
|
||||
return false;
|
||||
}
|
||||
if ((_max_cm_distance > 0) && (micros() > _max_time)) { // check max. distance if != 0 cm
|
||||
_errorState = STATUS_ECHO_START_TIMEOUT_DISTANCE;
|
||||
return false; // echo too late, max distance exceeded
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
#if ONE_PIN_ENABLED == true
|
||||
@@ -170,30 +202,51 @@ boolean NewPingESP8266::ping_trigger() {
|
||||
#endif
|
||||
|
||||
#if URM37_ENABLED == true
|
||||
if (!digitalRead(_echoPin)) return false; // Previous ping hasn't finished, abort.
|
||||
if (!digitalRead(_echoPin)) {
|
||||
_errorState = STATUS_ECHO_STATE_ERROR;
|
||||
return false; // Previous ping hasn't finished, abort.
|
||||
}
|
||||
_max_time = micros() + _maxEchoTime + MAX_SENSOR_DELAY; // Maximum time we'll wait for ping to start (most sensors are <450uS, the SRF06 can take up to 34,300uS!)
|
||||
while (digitalRead(_echoPin)) // Wait for ping to start.
|
||||
{
|
||||
if (millis() > (start + 50)) return false;
|
||||
if (micros() > _max_time) return false; // Took too long to start, abort.
|
||||
if (millis() > (start + 50)) {
|
||||
_errorState = STATUS_ECHO_START_TIMEOUT_50ms;
|
||||
return false;
|
||||
}
|
||||
if ((_max_cm_distance > 0) && (micros() > _max_time)) { // check max. distance if != 0 cm
|
||||
_errorState = STATUS_ECHO_START_TIMEOUT_DISTANCE;
|
||||
return false; // echo too late, max distance exceeded
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (digitalRead(_echoPin)) return false; // Previous ping hasn't finished, abort.
|
||||
if (digitalRead(_echoPin)) {
|
||||
_errorState = STATUS_ECHO_STATE_ERROR;
|
||||
return false; // Previous ping hasn't finished, abort.
|
||||
}
|
||||
_max_time = micros() + _maxEchoTime + MAX_SENSOR_DELAY; // Maximum time we'll wait for ping to start (most sensors are <450uS, the SRF06 can take up to 34,300uS!)
|
||||
while (!digitalRead(_echoPin)) // Wait for ping to start.
|
||||
{
|
||||
if (millis() > (start + 50)) return false;
|
||||
if (micros() > _max_time) return false; // Took too long to start, abort.
|
||||
if (millis() > (start + 50)) {
|
||||
_errorState = STATUS_ECHO_START_TIMEOUT_50ms;
|
||||
return false;
|
||||
}
|
||||
if ((_max_cm_distance > 0) && (micros() > _max_time)) { // check max. distance if != 0 cm
|
||||
_errorState = STATUS_ECHO_START_TIMEOUT_DISTANCE;
|
||||
return false; // echo too late, max distance exceeded
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
_max_time = micros() + _maxEchoTime; // Ping started, set the time-out.
|
||||
_errorState = STATUS_ECHO_TRIGGERED;
|
||||
|
||||
return true; // Ping started successfully.
|
||||
}
|
||||
|
||||
|
||||
void NewPingESP8266::set_max_distance(unsigned int max_cm_distance) {
|
||||
_max_cm_distance = max_cm_distance;
|
||||
#if ROUNDING_ENABLED == false
|
||||
_maxEchoTime = min(max_cm_distance + 1, (unsigned int) MAX_SENSOR_DISTANCE + 1) * US_ROUNDTRIP_CM; // Calculate the maximum distance in uS (no rounding).
|
||||
#else
|
||||
@@ -222,3 +275,12 @@ unsigned int NewPingESP8266::convert_in(unsigned int echoTime) {
|
||||
return NewPingESP8266Convert(echoTime, US_ROUNDTRIP_IN); // Convert uS to inches.
|
||||
#endif
|
||||
}
|
||||
|
||||
float NewPingESP8266::convert_cm_F(unsigned int echoTime) {
|
||||
return ((float)echoTime / US_ROUNDTRIP_CM); // Convert uS to centimeters (no rounding).
|
||||
}
|
||||
|
||||
|
||||
float NewPingESP8266::convert_in_F(unsigned int echoTime) {
|
||||
return ((float)echoTime / US_ROUNDTRIP_IN); // Convert uS to inches (no rounding).
|
||||
}
|
||||
|
||||
@@ -194,15 +194,29 @@
|
||||
|
||||
class NewPingESP8266 {
|
||||
public:
|
||||
enum errorState {
|
||||
STATUS_SENSOR_READY, // no error, no measruement started
|
||||
STATUS_MEASUREMENT_VALID, // no errors
|
||||
STATUS_ECHO_TRIGGERED, // echo triggered, intermediate state
|
||||
STATUS_MAX_DISTANCE_EXCEEDED, // echo too late, maximum distance exceeded
|
||||
STATUS_ECHO_START_TIMEOUT_50ms, // no echo start signal after 50 ms
|
||||
STATUS_ECHO_START_TIMEOUT_DISTANCE, // no echo start signal after max. distance
|
||||
STATUS_ECHO_STATE_ERROR // echopin level not low after triggered
|
||||
};
|
||||
|
||||
NewPingESP8266(uint32_t trigger_pin, uint32_t echo_pin, unsigned int max_cm_distance = MAX_SENSOR_DISTANCE);
|
||||
~NewPingESP8266() { };
|
||||
unsigned int ping(unsigned int max_cm_distance = 0);
|
||||
unsigned long ping_cm(unsigned int max_cm_distance = 0);
|
||||
unsigned long ping_in(unsigned int max_cm_distance = 0);
|
||||
unsigned long ping_median(uint32_t it = 5, unsigned int max_cm_distance = 0);
|
||||
static unsigned int convert_cm(unsigned int echoTime);
|
||||
static unsigned int convert_in(unsigned int echoTime);
|
||||
static float convert_cm_F(unsigned int echoTime);
|
||||
static float convert_in_F(unsigned int echoTime);
|
||||
|
||||
unsigned int getMaxEchoTime() { return _maxEchoTime; }
|
||||
unsigned int getMaxEchoTime() { return _maxEchoTime; };
|
||||
errorState getErrorState() { return _errorState; };
|
||||
private:
|
||||
boolean ping_trigger();
|
||||
void set_max_distance(unsigned int max_cm_distance);
|
||||
@@ -218,6 +232,8 @@ class NewPingESP8266 {
|
||||
#endif
|
||||
unsigned int _maxEchoTime;
|
||||
unsigned long _max_time;
|
||||
errorState _errorState;
|
||||
unsigned int _max_cm_distance;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -45,6 +45,10 @@ User can set the parameters in two ways:
|
||||
- by editing the [SDM_Config_User.h](https://github.com/reaper7/SDM_Energy_Meter/blob/master/SDM_Config_User.h) file
|
||||
- by passing values during initialization (section below)
|
||||
|
||||
NOTE for Hardware Serial mode: <i>to force the Hardware Serial mode,</br>
|
||||
user must edit the corresponding entry in [SDM_Config_User.h](https://github.com/reaper7/SDM_Energy_Meter/blob/master/SDM_Config_User.h#L13) file.</br>
|
||||
adding #define USE_HARDWARESERIAL to the main ino file is not enough.</i>
|
||||
|
||||
---
|
||||
|
||||
### Initializing: ###
|
||||
@@ -152,6 +156,9 @@ The most common problems are:
|
||||
https://github.com/reaper7/SDM_Energy_Meter/issues/17#issue-313606825</br>
|
||||
https://github.com/reaper7/SDM_Energy_Meter/issues/13#issuecomment-353413146</br>
|
||||
https://github.com/reaper7/SDM_Energy_Meter/issues/13#issuecomment-353417658</br>
|
||||
- compilation error for hardware serial mode</br>
|
||||
https://github.com/reaper7/SDM_Energy_Meter/issues/23</br>
|
||||
https://github.com/reaper7/SDM_Energy_Meter/issues/24</br>
|
||||
|
||||
You can get last error code using function:
|
||||
```cpp
|
||||
|
||||
@@ -90,7 +90,7 @@ float SDM::readVal(uint16_t reg, uint8_t node) {
|
||||
|
||||
if (readErr == SDM_ERR_NO_ERROR) { //if no timeout...
|
||||
|
||||
if(sdmSer.available() == FRAMESIZE) {
|
||||
if(sdmSer.available() >= FRAMESIZE) {
|
||||
|
||||
for(int n=0; n<FRAMESIZE; n++) {
|
||||
sdmarr[n] = sdmSer.read();
|
||||
@@ -119,7 +119,11 @@ float SDM::readVal(uint16_t reg, uint8_t node) {
|
||||
|
||||
if (readErr != SDM_ERR_NO_ERROR) { //if error then copy temp error value to global val and increment global error counter
|
||||
readingerrcode = readErr;
|
||||
readingerrcount++;
|
||||
readingerrcount++;
|
||||
}
|
||||
|
||||
while (sdmSer.available() > 0) { //read redundant serial bytes, if any
|
||||
sdmSer.read();
|
||||
}
|
||||
|
||||
#ifndef USE_HARDWARESERIAL
|
||||
|
||||
@@ -136,6 +136,14 @@
|
||||
#define SDM630_EXPORT_REACTIVE_ENERGY 0x004E //VARh
|
||||
#define SDM630_TOTAL_SYSTEM_POWER_DEMAND 0x0054 //W
|
||||
#define SDM630_MAXIMUM_TOTAL_SYSTEM_POWER 0x0056 //W
|
||||
#define SDM630_PHASE_1_LN_VOLTS_THD 0x00EA //%
|
||||
#define SDM630_PHASE_2_LN_VOLTS_THD 0x00EC //%
|
||||
#define SDM630_PHASE_3_LN_VOLTS_THD 0x00EE //%
|
||||
#define SDM630_AVERAGE_VOLTS_THD 0x00F8 //%
|
||||
#define SDM630_PHASE_1_CURRENT_THD 0x00F0 //%
|
||||
#define SDM630_PHASE_2_CURRENT_THD 0x00F2 //%
|
||||
#define SDM630_PHASE_3_CURRENT_THD 0x00F4 //%
|
||||
#define SDM630_AVERAGE_CURRENT_THD 0x00FA //%
|
||||
|
||||
#define SDM_B_05 0x00 //BYTE 5
|
||||
#define SDM_B_06 0x02 //BYTE 6
|
||||
@@ -176,7 +184,7 @@ class SDM {
|
||||
long _baud = SDM_UART_BAUD;
|
||||
int _dere_pin = DERE_PIN;
|
||||
uint16_t readingerrcode = SDM_ERR_NO_ERROR; //4 = timeout; 3 = not enough bytes; 2 = number of bytes OK but bytes b0,b1 or b2 wrong, 1 = crc error
|
||||
uint16_t readingerrcount = 0; //total errors couter
|
||||
uint16_t readingerrcount = 0; //total errors couter
|
||||
uint16_t calculateCRC(uint8_t *array, uint8_t num);
|
||||
};
|
||||
#endif //SDM_h
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
/build/
|
||||
/nbproject
|
||||
/Makefile
|
||||
@@ -0,0 +1,34 @@
|
||||
# Copyright 2017, alex at staticlibs.net
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
language: cpp
|
||||
|
||||
sudo: false
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
script:
|
||||
- $CC ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c89 -DCRON_TEST_MALLOC -o a.out && ./a.out
|
||||
- $CXX ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c++11 -DCRON_TEST_MALLOC -o a.out && ./a.out
|
||||
- $CXX ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c++11 -DCRON_TEST_MALLOC -DCRON_COMPILE_AS_CXX -o a.out && ./a.out
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: always
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2015, staticlibs.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,91 @@
|
||||
Cron expression parsing in ANSI C
|
||||
=================================
|
||||
|
||||
[](https://travis-ci.org/staticlibs/ccronexpr)
|
||||
[](https://ci.appveyor.com/project/staticlibs/ccronexpr)
|
||||
|
||||
Given a cron expression and a date, you can get the next date which satisfies the cron expression.
|
||||
|
||||
Supports cron expressions with `seconds` field. Based on implementation of [CronSequenceGenerator](https://github.com/spring-projects/spring-framework/blob/babbf6e8710ab937cd05ece20270f51490299270/spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java) from Spring Framework.
|
||||
|
||||
Compiles and should work on Linux (GCC/Clang), Mac OS (Clang), Windows (MSVC), Android NDK, iOS and possibly on other platforms with `time.h` support.
|
||||
|
||||
Supports compilation in C (89) and in C++ modes.
|
||||
|
||||
Usage example
|
||||
-------------
|
||||
|
||||
#include "ccronexpr.h"
|
||||
|
||||
cron_expr expr;
|
||||
const char* err = NULL;
|
||||
memset(&expr, 0, sizeof(expr));
|
||||
cron_parse_expr("0 */2 1-4 * * *", &expr, &err);
|
||||
if (err) ... /* invalid expression */
|
||||
time_t cur = time(NULL);
|
||||
time_t next = cron_next(&expr, cur);
|
||||
|
||||
|
||||
Compilation and tests run examples
|
||||
----------------------------------
|
||||
|
||||
gcc ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c89 -DCRON_TEST_MALLOC -o a.out && ./a.out
|
||||
g++ ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c++11 -DCRON_TEST_MALLOC -o a.out && ./a.out
|
||||
g++ ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c++11 -DCRON_TEST_MALLOC -DCRON_COMPILE_AS_CXX -o a.out && ./a.out
|
||||
|
||||
clang ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c89 -DCRON_TEST_MALLOC -o a.out && ./a.out
|
||||
clang++ ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c++11 -DCRON_TEST_MALLOC -o a.out && ./a.out
|
||||
clang++ ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c++11 -DCRON_TEST_MALLOC -DCRON_COMPILE_AS_CXX -o a.out && ./a.out
|
||||
|
||||
cl ccronexpr.c ccronexpr_test.c /W4 /D_CRT_SECURE_NO_WARNINGS && ccronexpr.exe
|
||||
|
||||
Examples of supported expressions
|
||||
---------------------------------
|
||||
|
||||
Expression, input date, next date:
|
||||
|
||||
"*/15 * 1-4 * * *", "2012-07-01_09:53:50", "2012-07-02_01:00:00"
|
||||
"0 */2 1-4 * * *", "2012-07-01_09:00:00", "2012-07-02_01:00:00"
|
||||
"0 0 7 ? * MON-FRI", "2009-09-26_00:42:55", "2009-09-28_07:00:00"
|
||||
"0 30 23 30 1/3 ?", "2011-04-30_23:30:00", "2011-07-30_23:30:00"
|
||||
|
||||
See more examples in [tests](https://github.com/staticlibs/ccronexpr/blob/a1343bc5a546b13430bd4ac72f3b047ac08f8192/ccronexpr_test.c#L251).
|
||||
|
||||
Timezones
|
||||
---------
|
||||
|
||||
This implementation does not support explicit timezones handling. By default all dates are
|
||||
processed as UTC (GMT) dates without timezone infomation.
|
||||
|
||||
To use local dates (current system timezone) instead of GMT compile with `-DCRON_USE_LOCAL_TIME`.
|
||||
|
||||
License information
|
||||
-------------------
|
||||
|
||||
This project is released under the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
**2018-05-23**
|
||||
|
||||
* merged [#8](https://github.com/staticlibs/ccronexpr/pull/8)
|
||||
* merged [#9](https://github.com/staticlibs/ccronexpr/pull/9)
|
||||
* minor cleanups
|
||||
|
||||
**2018-01-27**
|
||||
|
||||
* merged [#6](https://github.com/staticlibs/ccronexpr/pull/6)
|
||||
* updated license file (to the one parse-able by github)
|
||||
|
||||
**2017-09-24**
|
||||
|
||||
* merged [#4](https://github.com/staticlibs/ccronexpr/pull/4)
|
||||
|
||||
**2016-06-17**
|
||||
|
||||
* use thread-safe versions of `gmtime` and `localtime`
|
||||
|
||||
**2015-02-28**
|
||||
|
||||
* initial public version
|
||||
@@ -0,0 +1,29 @@
|
||||
# Copyright 2017, alex at staticlibs.net
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
image: Visual Studio 2017
|
||||
|
||||
configuration: Release
|
||||
|
||||
build: off
|
||||
|
||||
build_script:
|
||||
- call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat"
|
||||
- cl ccronexpr.c ccronexpr_test.c /W4 /D_CRT_SECURE_NO_WARNINGS /Feccronexpr_32.exe
|
||||
- call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
|
||||
- cl ccronexpr.c ccronexpr_test.c /W4 /D_CRT_SECURE_NO_WARNINGS /Feccronexpr_64.exe
|
||||
|
||||
test_script:
|
||||
- ccronexpr_32.exe
|
||||
- ccronexpr_64.exe
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2015, alex at staticlibs.net
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* File: ccronexpr.h
|
||||
* Author: alex
|
||||
*
|
||||
* Created on February 24, 2015, 9:35 AM
|
||||
*/
|
||||
|
||||
#ifndef CCRONEXPR_H
|
||||
#define CCRONEXPR_H
|
||||
|
||||
#if defined(__cplusplus) && !defined(CRON_COMPILE_AS_CXX)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef ANDROID
|
||||
#include <time.h>
|
||||
#else /* ANDROID */
|
||||
#include <time64.h>
|
||||
#endif /* ANDROID */
|
||||
|
||||
#include <stdint.h> /*added for use if uint*_t data types*/
|
||||
|
||||
/**
|
||||
* Parsed cron expression
|
||||
*/
|
||||
typedef struct cron_expr_t{
|
||||
uint8_t seconds[8];
|
||||
uint8_t minutes[8];
|
||||
uint8_t hours[3];
|
||||
uint8_t days_of_week[1];
|
||||
uint8_t days_of_month[4];
|
||||
uint8_t months[2];
|
||||
} cron_expr;
|
||||
|
||||
#define CRON_INVALID_INSTANT ((time_t) -1)
|
||||
/**
|
||||
* Parses specified cron expression.
|
||||
*
|
||||
* @param expression cron expression as nul-terminated string,
|
||||
* should be no longer that 256 bytes
|
||||
* @param pointer to cron expression structure, it's client code responsibility
|
||||
* to free/destroy it afterwards
|
||||
* @param error output error message, will be set to string literal
|
||||
* error message in case of error. Will be set to NULL on success.
|
||||
* The error message should NOT be freed by client.
|
||||
*/
|
||||
void cron_parse_expr(const char* expression, cron_expr* target, const char** error);
|
||||
|
||||
/**
|
||||
* Uses the specified expression to calculate the next 'fire' date after
|
||||
* the specified date. All dates are processed as UTC (GMT) dates
|
||||
* without timezones information. To use local dates (current system timezone)
|
||||
* instead of GMT compile with '-DCRON_USE_LOCAL_TIME'
|
||||
*
|
||||
* @param expr parsed cron expression to use in next date calculation
|
||||
* @param date start date to start calculation from
|
||||
* @return next 'fire' date in case of success, '((time_t) -1)' in case of error.
|
||||
*/
|
||||
time_t cron_next(cron_expr* expr, time_t date);
|
||||
|
||||
/**
|
||||
* Uses the specified expression to calculate the previous 'fire' date after
|
||||
* the specified date. All dates are processed as UTC (GMT) dates
|
||||
* without timezones information. To use local dates (current system timezone)
|
||||
* instead of GMT compile with '-DCRON_USE_LOCAL_TIME'
|
||||
*
|
||||
* @param expr parsed cron expression to use in previous date calculation
|
||||
* @param date start date to start calculation from
|
||||
* @return previous 'fire' date in case of success, '((time_t) -1)' in case of error.
|
||||
*/
|
||||
time_t cron_prev(cron_expr* expr, time_t date);
|
||||
|
||||
|
||||
#if defined(__cplusplus) && !defined(CRON_COMPILE_AS_CXX)
|
||||
} /* extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /* CCRONEXPR_H */
|
||||
@@ -209,6 +209,7 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass
|
||||
boolean PubSubClient::readByte(uint8_t * result) {
|
||||
uint32_t previousMillis = millis();
|
||||
while(!_client->available()) {
|
||||
delay(1); // Prevent watchdog crashes
|
||||
uint32_t currentMillis = millis();
|
||||
if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){
|
||||
return false;
|
||||
@@ -522,9 +523,12 @@ boolean PubSubClient::unsubscribe(const char* topic) {
|
||||
void PubSubClient::disconnect() {
|
||||
buffer[0] = MQTTDISCONNECT;
|
||||
buffer[1] = 0;
|
||||
_client->write(buffer,2);
|
||||
if (_client != NULL) {
|
||||
_client->write(buffer,2);
|
||||
_client->flush();
|
||||
_client->stop();
|
||||
}
|
||||
_state = MQTT_DISCONNECTED;
|
||||
_client->stop();
|
||||
lastInActivity = lastOutActivity = millis();
|
||||
}
|
||||
|
||||
|
||||
+113
-71
@@ -65,6 +65,7 @@ platform = https://github.com/platformio/platform-espressif8266
|
||||
|
||||
[core_esp32_0_12_0]
|
||||
platform = espressif32@0.12.0
|
||||
lib_deps = "ESP32WebServer"
|
||||
|
||||
[core_esp32_1_2_0]
|
||||
platform = espressif32@1.2.0
|
||||
@@ -72,20 +73,23 @@ platform = espressif32@1.2.0
|
||||
[core_esp32_1_3_0]
|
||||
platform = espressif32@1.3.0
|
||||
|
||||
[core_esp32_1_4_0]
|
||||
platform = espressif32@1.4.0
|
||||
|
||||
[core_esp32_stage]
|
||||
platform = https://github.com/platformio/platform-espressif32.git#feature/stage
|
||||
|
||||
[core_esp32]
|
||||
platform = ${core_esp32_0_12_0.platform}
|
||||
platform = ${core_esp32_1_4_0.platform}
|
||||
build_unflags = -Wall
|
||||
build_flags = -D BUILD_GIT='"${sysenv.TRAVIS_TAG}"'
|
||||
-lstdc++ -lsupc++
|
||||
lib_ignore = AS_BH1750, ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266HTTPUpdateServer, ESP8266mDNS, IRremoteESP8266, ESPEasy_ESP8266Ping, SerialSensors, SDM
|
||||
lib_deps = ESP32WebServer
|
||||
lib_deps = ""
|
||||
monitor_speed = 115200
|
||||
|
||||
|
||||
|
||||
|
||||
; Build Flags:
|
||||
; -DUSE_CONFIG_OVERRIDE
|
||||
; lwIP 1.4 (Default)
|
||||
@@ -100,15 +104,20 @@ monitor_speed = 115200
|
||||
; -DVTABLES_IN_DRAM
|
||||
; VTABLES in IRAM
|
||||
; -DVTABLES_IN_IRAM
|
||||
|
||||
; NO_EXTRA_4K_HEAP - this forces the default NONOS-SDK user's heap location
|
||||
; Default currently overlaps cont stack (Arduino) with sys stack (System)
|
||||
; to save up-to 4 kB of heap. (starting core_2.4.2)
|
||||
; ESP8266_DISABLE_EXTRA4K - Calls disable_extra4k_at_link_time() from setup
|
||||
; to force the linker keep user's stack in user ram.
|
||||
; CONT_STACKSIZE to set the 'cont' (Arduino) stack size. Default = 4096
|
||||
|
||||
[common]
|
||||
board_build.f_cpu = 80000000L
|
||||
build_unflags = ""
|
||||
build_unflags =
|
||||
build_flags = -D BUILD_GIT='"${sysenv.TRAVIS_TAG}"'
|
||||
-D NDEBUG
|
||||
-lstdc++ -lsupc++
|
||||
-DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
|
||||
-DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
|
||||
-DVTABLES_IN_FLASH
|
||||
|
||||
|
||||
@@ -119,18 +128,20 @@ lib_archive = false
|
||||
upload_speed = 460800
|
||||
framework = arduino
|
||||
board = esp12e
|
||||
platform = ${core_2_4_2.platform}
|
||||
platform = ${core_2_4_1.platform}
|
||||
monitor_speed = 115200
|
||||
|
||||
|
||||
[normal]
|
||||
platform = ${common.platform}
|
||||
build_flags =
|
||||
|
||||
[testing]
|
||||
platform = ${core_2_4_2.platform}
|
||||
platform = ${core_2_4_1.platform}
|
||||
build_flags = -DPLUGIN_BUILD_TESTING
|
||||
|
||||
[dev]
|
||||
platform = ${core_2_4_2.platform}
|
||||
platform = ${core_2_4_1.platform}
|
||||
build_flags = -DPLUGIN_BUILD_DEV
|
||||
|
||||
[ir]
|
||||
lib_ignore = ESP32_ping, ESP32WebServer
|
||||
@@ -151,24 +162,6 @@ board_build.f_cpu = ${esp8266_1M.board_build.f_cpu}
|
||||
build_unflags = ${esp8266_1M.build_unflags}
|
||||
build_flags = ${esp8266_1M.build_flags} -DESP8285
|
||||
|
||||
[Sonoff]
|
||||
board = esp01_1m
|
||||
board_upload.maximum_size = ${esp8266_1M.board_upload.maximum_size}
|
||||
board_build.flash_mode = ${esp8266_1M.board_build.flash_mode}
|
||||
board_build.f_cpu = ${esp8266_1M.board_build.f_cpu}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.1m128.ld
|
||||
platform = ${core_2_4_2.platform}
|
||||
|
||||
[Sonoff_8285]
|
||||
board_upload.maximum_size = ${esp8285_1M.board_upload.maximum_size}
|
||||
board_build.flash_mode = ${esp8285_1M.board_build.flash_mode}
|
||||
board_build.f_cpu = ${esp8285_1M.board_build.f_cpu}
|
||||
board = ${esp8285_1M.board}
|
||||
build_unflags = ${esp8285_1M.build_unflags}
|
||||
build_flags = ${esp8285_1M.build_flags}
|
||||
platform = ${core_2_4_2.platform}
|
||||
|
||||
[espWroom2M]
|
||||
board_build.flash_mode = dout
|
||||
board_build.f_cpu = ${common.board_build.f_cpu}
|
||||
@@ -184,15 +177,64 @@ build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.2m.ld
|
||||
|
||||
[esp8266_4M]
|
||||
board_build.flash_mode = dio
|
||||
board_upload.maximum_size = 900000
|
||||
board_build.f_cpu = ${common.board_build.f_cpu}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.4m1m.ld
|
||||
|
||||
|
||||
[Sonoff]
|
||||
board = esp01_1m
|
||||
board_upload.maximum_size = ${esp8266_1M.board_upload.maximum_size}
|
||||
board_build.flash_mode = ${esp8266_1M.board_build.flash_mode}
|
||||
board_build.f_cpu = ${esp8266_1M.board_build.f_cpu}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.1m128.ld
|
||||
platform = ${core_2_4_1.platform}
|
||||
|
||||
[Sonoff_4M]
|
||||
board = esp12e
|
||||
board_upload.maximum_size = ${esp8266_4M.board_upload.maximum_size}
|
||||
board_build.flash_mode = ${esp8266_4M.board_build.flash_mode}
|
||||
board_build.f_cpu = ${esp8266_4M.board_build.f_cpu}
|
||||
build_unflags = ${esp8266_4M.build_unflags}
|
||||
build_flags = ${esp8266_4M.build_flags}
|
||||
platform = ${common.platform}
|
||||
|
||||
|
||||
[Sonoff_8285]
|
||||
board_upload.maximum_size = ${esp8285_1M.board_upload.maximum_size}
|
||||
board_build.flash_mode = ${esp8285_1M.board_build.flash_mode}
|
||||
board_build.f_cpu = ${esp8285_1M.board_build.f_cpu}
|
||||
board = ${esp8285_1M.board}
|
||||
build_unflags = ${esp8285_1M.build_unflags}
|
||||
build_flags = ${esp8285_1M.build_flags}
|
||||
platform = ${core_2_4_1.platform}
|
||||
|
||||
;;; ESP32 pre-alpha test build ; ****; ****; ****; ****; ****; ****; ****; ****; ****;;;;;
|
||||
; Very limited build for ESP32, to start testing regular building for ESP32. ;
|
||||
; Will probably not work, not tested and guaranteed to take a few hours time of some ;
|
||||
; still trying to build the version without reading this warning. ;
|
||||
; *********************************************************************
|
||||
[env:esp-wrover-kit_test_1M8_partition]
|
||||
platform = ${core_esp32.platform}
|
||||
board = esp-wrover-kit
|
||||
build_unflags = ${core_esp32.build_unflags}
|
||||
build_flags = ${core_esp32.build_flags} -DPLUGIN_SET_TEST_ESP32
|
||||
lib_deps = ${core_esp32.lib_deps}
|
||||
lib_ignore = ${core_esp32.lib_ignore}
|
||||
lib_ldf_mode = ${common.lib_ldf_mode}
|
||||
lib_archive = ${common.lib_archive}
|
||||
framework = ${common.framework}
|
||||
upload_speed = ${common.upload_speed}
|
||||
upload_protocol = ftdi
|
||||
debug_tool = ftdi
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
board_build.partitions = esp32_partition_app1810k_spiffs316k.csv
|
||||
board_upload.maximum_size = 1900544
|
||||
debug_extra_cmds = break Misc.ino:3011
|
||||
|
||||
|
||||
[env:esp32dev]
|
||||
platform = ${core_esp32.platform}
|
||||
board = esp32dev
|
||||
@@ -206,6 +248,21 @@ framework = ${common.framework}
|
||||
upload_speed = ${common.upload_speed}
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
|
||||
[env:esp32test_1M8_partition]
|
||||
platform = ${core_esp32.platform}
|
||||
board = esp32dev
|
||||
build_unflags = ${core_esp32.build_unflags}
|
||||
build_flags = ${core_esp32.build_flags} -DPLUGIN_SET_TEST_ESP32
|
||||
lib_deps = ${core_esp32.lib_deps}
|
||||
lib_ignore = ${core_esp32.lib_ignore}
|
||||
lib_ldf_mode = ${common.lib_ldf_mode}
|
||||
lib_archive = ${common.lib_archive}
|
||||
framework = ${common.framework}
|
||||
upload_speed = ${common.upload_speed}
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
board_build.partitions = esp32_partition_app1810k_spiffs316k.csv
|
||||
board_upload.maximum_size = 1900544
|
||||
|
||||
|
||||
;;; NORMAL (STABLE) ; ****; ****; ****; ****; ****; ****; ****; ****; ****; ****; ****;;;;
|
||||
; normal version with stable plugins ;
|
||||
@@ -226,7 +283,7 @@ board_upload.maximum_size = ${esp8266_1M.board_upload.maximum_size}
|
||||
board_build.f_cpu = ${esp8266_1M.board_build.f_cpu}
|
||||
board_build.flash_mode = ${esp8266_1M.board_build.flash_mode}
|
||||
build_unflags = ${esp8266_1M.build_unflags}
|
||||
build_flags = ${esp8266_1M.build_flags}
|
||||
build_flags = ${esp8266_1M.build_flags} ${normal.build_flags}
|
||||
|
||||
; NORMAL: 1024k for esp8285 ----------------------
|
||||
[env:normal_ESP8285_1024]
|
||||
@@ -243,7 +300,7 @@ board_upload.maximum_size = ${esp8285_1M.board_upload.maximum_size}
|
||||
board_build.f_cpu = ${esp8285_1M.board_build.f_cpu}
|
||||
board_build.flash_mode = ${esp8285_1M.board_build.flash_mode}
|
||||
build_unflags = ${esp8285_1M.build_unflags}
|
||||
build_flags = ${esp8285_1M.build_flags}
|
||||
build_flags = ${esp8285_1M.build_flags} ${normal.build_flags}
|
||||
|
||||
; NORMAL: 2048k version --------------------------
|
||||
[env:normal_WROOM02_2048]
|
||||
@@ -259,7 +316,7 @@ board = ${espWroom2M.board}
|
||||
board_build.f_cpu = ${espWroom2M.board_build.f_cpu}
|
||||
board_build.flash_mode = ${espWroom2M.board_build.flash_mode}
|
||||
build_unflags = ${espWroom2M.build_unflags}
|
||||
build_flags = ${espWroom2M.build_flags}
|
||||
build_flags = ${espWroom2M.build_flags} ${normal.build_flags}
|
||||
|
||||
; NORMAL: 4096k version --------------------------
|
||||
[env:normal_ESP8266_4096]
|
||||
@@ -275,7 +332,7 @@ board = ${common.board}
|
||||
board_build.f_cpu = ${esp8266_4M.board_build.f_cpu}
|
||||
board_build.flash_mode = ${esp8266_4M.board_build.flash_mode}
|
||||
build_unflags = ${esp8266_4M.build_unflags}
|
||||
build_flags = ${esp8266_4M.build_flags}
|
||||
build_flags = ${esp8266_4M.build_flags} ${normal.build_flags}
|
||||
|
||||
|
||||
; NORMAL IR: 4096k version --------------------------
|
||||
@@ -293,7 +350,7 @@ board = ${common.board}
|
||||
board_build.f_cpu = ${esp8266_4M.board_build.f_cpu}
|
||||
board_build.flash_mode = ${esp8266_4M.board_build.flash_mode}
|
||||
build_unflags = ${esp8266_4M.build_unflags}
|
||||
build_flags = ${esp8266_4M.build_flags} -DPLUGIN_BUILD_NORMAL_IR
|
||||
build_flags = ${esp8266_4M.build_flags} ${normal.build_flags} -DPLUGIN_BUILD_NORMAL_IR
|
||||
|
||||
|
||||
;;; TESTING ; ****; ****; ****; ****; ****; ****; ****; ****; ****; ****; ****; ****; ****
|
||||
@@ -315,7 +372,7 @@ board_upload.maximum_size = ${esp8266_1M.board_upload.maximum_size}
|
||||
board_build.flash_mode = ${esp8266_1M.board_build.flash_mode}
|
||||
board_build.f_cpu = ${esp8266_1M.board_build.f_cpu}
|
||||
build_unflags = ${esp8266_1M.build_unflags}
|
||||
build_flags = ${esp8266_1M.build_flags} -D PLUGIN_BUILD_TESTING
|
||||
build_flags = ${esp8266_1M.build_flags} ${testing.build_flags}
|
||||
|
||||
; TEST: 1024k for esp8285 ------------------------
|
||||
[env:test_ESP8285_1024]
|
||||
@@ -332,7 +389,7 @@ board_build.f_cpu = ${esp8285_1M.board_build.f_cpu}
|
||||
board_build.flash_mode = ${esp8285_1M.board_build.flash_mode}
|
||||
board = ${esp8285_1M.board}
|
||||
build_unflags = ${esp8285_1M.build_unflags}
|
||||
build_flags = ${esp8285_1M.build_flags} -D PLUGIN_BUILD_TESTING
|
||||
build_flags = ${esp8285_1M.build_flags} ${testing.build_flags}
|
||||
|
||||
; TEST: 2048k version ----------------------------
|
||||
[env:test_WROOM02_2048]
|
||||
@@ -348,7 +405,7 @@ board_build.f_cpu = ${espWroom2M.board_build.f_cpu}
|
||||
board_build.flash_mode = ${espWroom2M.board_build.flash_mode}
|
||||
board = ${espWroom2M.board}
|
||||
build_unflags = ${espWroom2M.build_unflags}
|
||||
build_flags = ${espWroom2M.build_flags} -D PLUGIN_BUILD_TESTING
|
||||
build_flags = ${espWroom2M.build_flags} ${testing.build_flags}
|
||||
|
||||
; TEST: 4096k version ----------------------------
|
||||
[env:test_ESP8266_4096]
|
||||
@@ -364,7 +421,7 @@ monitor_speed = ${common.monitor_speed}
|
||||
board_build.f_cpu = ${esp8266_4M.board_build.f_cpu}
|
||||
board_build.flash_mode = ${esp8266_4M.board_build.flash_mode}
|
||||
build_unflags = ${esp8266_4M.build_unflags}
|
||||
build_flags = ${esp8266_4M.build_flags} -D PLUGIN_BUILD_TESTING
|
||||
build_flags = ${esp8266_4M.build_flags} ${testing.build_flags}
|
||||
|
||||
; TEST: 4096k version + FEATURE_ADC_VCC ----------
|
||||
[env:test_ESP8266_4096_VCC]
|
||||
@@ -380,7 +437,7 @@ monitor_speed = ${common.monitor_speed}
|
||||
board_build.f_cpu = ${esp8266_4M.board_build.f_cpu}
|
||||
board_build.flash_mode = ${esp8266_4M.board_build.flash_mode}
|
||||
build_unflags = ${esp8266_4M.build_unflags}
|
||||
build_flags = ${esp8266_4M.build_flags} -D PLUGIN_BUILD_TESTING -D FEATURE_ADC_VCC=true
|
||||
build_flags = ${esp8266_4M.build_flags} ${testing.build_flags} -D FEATURE_ADC_VCC=true
|
||||
|
||||
|
||||
|
||||
@@ -403,7 +460,7 @@ board_build.f_cpu = ${esp8266_1M.board_build.f_cpu}
|
||||
board_build.flash_mode = ${esp8266_1M.board_build.flash_mode}
|
||||
board = ${common.board}
|
||||
build_unflags = ${esp8266_1M.build_unflags}
|
||||
build_flags = ${esp8266_1M.build_flags} -D PLUGIN_BUILD_DEV
|
||||
build_flags = ${esp8266_1M.build_flags} ${dev.build_flags}
|
||||
|
||||
; DEV: 1024k for esp8285 -------------------------
|
||||
[env:dev_ESP8285_1024]
|
||||
@@ -420,7 +477,7 @@ board_build.f_cpu = ${esp8285_1M.board_build.f_cpu}
|
||||
board_build.flash_mode = ${esp8285_1M.board_build.flash_mode}
|
||||
board = ${esp8285_1M.board}
|
||||
build_unflags = ${esp8285_1M.build_unflags}
|
||||
build_flags = ${esp8285_1M.build_flags} -D PLUGIN_BUILD_DEV
|
||||
build_flags = ${esp8285_1M.build_flags} ${dev.build_flags}
|
||||
|
||||
; DEV: 2048k version -----------------------------
|
||||
[env:dev_WROOM02_2048]
|
||||
@@ -436,7 +493,7 @@ board_build.f_cpu = ${espWroom2M.board_build.f_cpu}
|
||||
board_build.flash_mode = ${espWroom2M.board_build.flash_mode}
|
||||
board = ${espWroom2M.board}
|
||||
build_unflags = ${espWroom2M.build_unflags}
|
||||
build_flags = ${espWroom2M.build_flags} -D PLUGIN_BUILD_DEV
|
||||
build_flags = ${espWroom2M.build_flags} ${dev.build_flags}
|
||||
|
||||
; DEV : 4096k version ----------------------------
|
||||
[env:dev_ESP8266_4096]
|
||||
@@ -452,7 +509,7 @@ monitor_speed = ${common.monitor_speed}
|
||||
board_build.f_cpu = ${esp8266_4M.board_build.f_cpu}
|
||||
board_build.flash_mode = ${esp8266_4M.board_build.flash_mode}
|
||||
build_unflags = ${esp8266_4M.build_unflags}
|
||||
build_flags = ${esp8266_4M.build_flags} -D PLUGIN_BUILD_DEV
|
||||
build_flags = ${esp8266_4M.build_flags} ${dev.build_flags}
|
||||
|
||||
|
||||
|
||||
@@ -475,7 +532,7 @@ board_upload.maximum_size = ${esp8266_1M.board_upload.maximum_size}
|
||||
board_build.f_cpu = ${esp8266_1M.board_build.f_cpu}
|
||||
board_build.flash_mode = ${esp8266_1M.board_build.flash_mode}
|
||||
build_unflags = ${esp8266_1M.build_unflags}
|
||||
build_flags = ${esp8266_1M.build_flags} -D PLUGIN_BUILD_DEV -D FLASH_QUIRK_WRITE_0_TO_1
|
||||
build_flags = ${esp8266_1M.build_flags} ${dev.build_flags} -D FLASH_QUIRK_WRITE_0_TO_1
|
||||
|
||||
; DEV+PUYA : 1024k version + FEATURE_ADC_VCC -----
|
||||
[env:dev_ESP8266PUYA_1024_VCC]
|
||||
@@ -492,7 +549,7 @@ board_upload.maximum_size = ${esp8266_1M.board_upload.maximum_size}
|
||||
board_build.f_cpu = ${esp8266_1M.board_build.f_cpu}
|
||||
board_build.flash_mode = ${esp8266_1M.board_build.flash_mode}
|
||||
build_unflags = ${esp8266_1M.build_unflags}
|
||||
build_flags = ${esp8266_1M.build_flags} -D PLUGIN_BUILD_DEV -D FLASH_QUIRK_WRITE_0_TO_1 -D FEATURE_ADC_VCC=true
|
||||
build_flags = ${esp8266_1M.build_flags} ${dev.build_flags} -D FLASH_QUIRK_WRITE_0_TO_1 -D FEATURE_ADC_VCC=true
|
||||
|
||||
|
||||
|
||||
@@ -519,24 +576,9 @@ build_flags = ${esp8266_1M.build_flags} -D PLUGIN_BUILD_DEV -D FLA
|
||||
;build_unflags = ${Sonoff.build_unflags}
|
||||
;build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_BASIC
|
||||
|
||||
; ITEAD / SONOFF TH10 version -------------------
|
||||
;[env:hard_SONOFF_TH10]
|
||||
;upload_speed = ${common.upload_speed}
|
||||
;monitor_speed = ${common.monitor_speed}
|
||||
;framework = ${common.framework}
|
||||
;platform = ${Sonoff.platform}
|
||||
;lib_deps = ${common.lib_deps}
|
||||
;lib_ignore = ${common.lib_ignore}
|
||||
;lib_ldf_mode = ${common.lib_ldf_mode}
|
||||
;lib_archive = ${common.lib_archive}
|
||||
;board_build.f_cpu = ${Sonoff.board_build.f_cpu}
|
||||
;board_build.flash_mode = ${Sonoff.board_build.flash_mode}
|
||||
;board = ${Sonoff.board}
|
||||
;build_unflags = ${Sonoff.build_unflags}
|
||||
;build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_TH10
|
||||
|
||||
; ITEAD / SONOFF TH16 version -------------------
|
||||
;[env:hard_SONOFF_TH16]
|
||||
; ITEAD / SONOFF TH10/TH16 version -------------------
|
||||
;[env:hard_SONOFF_TH1x]
|
||||
;upload_speed = ${common.upload_speed}
|
||||
;monitor_speed = ${common.monitor_speed}
|
||||
;framework = ${common.framework}
|
||||
@@ -549,7 +591,7 @@ build_flags = ${esp8266_1M.build_flags} -D PLUGIN_BUILD_DEV -D FLA
|
||||
;board_build.flash_mode = ${Sonoff.board_build.flash_mode}
|
||||
;board = ${Sonoff.board}
|
||||
;build_unflags = ${Sonoff.build_unflags}
|
||||
;build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_TH16
|
||||
;build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_TH1x
|
||||
|
||||
; ITEAD / SONOFF POW version --------------------
|
||||
; Sonoff Pow (ESP8266 - HLW8012)
|
||||
@@ -574,26 +616,26 @@ board = ${Sonoff.board}
|
||||
build_unflags = ${Sonoff.build_unflags}
|
||||
build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_POW
|
||||
|
||||
; Sonoff Pow R2 (ESP8266 - CSE7766)
|
||||
; Sonoff Pow R2 (ESP8266 4M flash - CSE7766)
|
||||
; GPIO00 Button
|
||||
; GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor
|
||||
; GPIO03 Serial TXD
|
||||
; GPIO12 Red Led and Relay (0 = Off, 1 = On)
|
||||
; GPIO13 Blue Led (0 = On, 1 = Off)
|
||||
[env:hard_SONOFF_POW_R2]
|
||||
[env:hard_SONOFF_POW_R2_4M]
|
||||
upload_speed = ${common.upload_speed}
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
framework = ${common.framework}
|
||||
platform = ${Sonoff.platform}
|
||||
platform = ${Sonoff_4M.platform}
|
||||
lib_deps = ${common.lib_deps}
|
||||
lib_ignore = ${common.lib_ignore}
|
||||
lib_ldf_mode = ${common.lib_ldf_mode}
|
||||
lib_archive = ${common.lib_archive}
|
||||
board_build.f_cpu = ${Sonoff.board_build.f_cpu}
|
||||
board_build.flash_mode = ${Sonoff.board_build.flash_mode}
|
||||
board = ${Sonoff.board}
|
||||
build_unflags = ${Sonoff.build_unflags}
|
||||
build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_POW_R2
|
||||
board_build.f_cpu = ${Sonoff_4M.board_build.f_cpu}
|
||||
board_build.flash_mode = ${Sonoff_4M.board_build.flash_mode}
|
||||
board = ${Sonoff_4M.board}
|
||||
build_unflags = ${Sonoff_4M.build_unflags}
|
||||
build_flags = ${Sonoff_4M.build_flags} -D PLUGIN_SET_SONOFF_POW_R2
|
||||
|
||||
; ITEAD / SONOFF S20 version --------------------
|
||||
;[env:hard_SONOFF_S20]
|
||||
@@ -609,7 +651,7 @@ build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_POW_R2
|
||||
;board_build.flash_mode = ${Sonoff.board_build.flash_mode}
|
||||
;board = ${Sonoff.board}
|
||||
;build_unflags = ${Sonoff.build_unflags}
|
||||
;build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_S20
|
||||
;build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_S2x
|
||||
|
||||
; ITEAD / SONOFF 4CH version --------------------
|
||||
;[env:hard_SONOFF_4CH]
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Check for documentation only changes.
|
||||
# Source: https://github.com/dev-id/Magic-Spoiler/blob/8111a06ab6682e020169991d5e2aa4fa503d787f/preflight.sh
|
||||
|
||||
set -e
|
||||
MAIN_BRANCH="mega"
|
||||
MD=".md$"
|
||||
DOCS="^docs/"
|
||||
|
||||
CHANGED_FILES=`git diff --name-only ${MAIN_BRANCH}...${TRAVIS_COMMIT}`
|
||||
ONLY_READMES=True
|
||||
DOCUMENTATION_CHANGED=False
|
||||
|
||||
for CHANGED_FILE in $CHANGED_FILES; do
|
||||
if ! [[ $CHANGED_FILE =~ $MD || $CHANGED_FILE =~ $DOCS ]]; then
|
||||
ONLY_READMES=False
|
||||
# break
|
||||
else
|
||||
DOCUMENTATION_CHANGED=True
|
||||
fi
|
||||
done
|
||||
|
||||
#if [[ $ONLY_READMES == True ]]; then
|
||||
# echo "Only documentation files found, exiting."
|
||||
# travis_terminate 0
|
||||
# exit 1
|
||||
#else
|
||||
# echo "Non-documentation files found, continuing with build."
|
||||
#fi
|
||||
|
||||
if [[ $DOCUMENTATION_CHANGED == True ]]; then
|
||||
echo "Generate documentation."
|
||||
cd docs
|
||||
make html
|
||||
fi
|
||||
+10
-8
@@ -79,6 +79,7 @@ String doExecuteCommand(const char * cmd, struct EventStruct *event, const char*
|
||||
break;
|
||||
}
|
||||
case 'l': {
|
||||
COMMAND_CASE("let" , Command_Rules_Let); // Rules.h
|
||||
COMMAND_CASE("load" , Command_Settings_Load); // Settings.h
|
||||
COMMAND_CASE("logentry" , Command_logentry); // Diagnostic.h
|
||||
COMMAND_CASE("lowmem" , Command_Lowmem); // Diagnostic.h
|
||||
@@ -131,7 +132,8 @@ String doExecuteCommand(const char * cmd, struct EventStruct *event, const char*
|
||||
COMMAND_CASE("taskclear" , Command_Task_Clear); // Tasks.h
|
||||
COMMAND_CASE("taskclearall" , Command_Task_ClearAll); // Tasks.h
|
||||
COMMAND_CASE("taskrun" , Command_Task_Run); // Tasks.h
|
||||
COMMAND_CASE("taskvalueset" , Command_Task_ValueSet); // Tasks.h
|
||||
COMMAND_CASE("taskvalueset" , Command_Task_ValueSet); // Tasks.h
|
||||
COMMAND_CASE("taskvaluetoggle" , Command_Task_ValueToggle); // Tasks.h
|
||||
COMMAND_CASE("taskvaluesetandrun" , Command_Task_ValueSetAndRun); // Tasks.h
|
||||
COMMAND_CASE("timerpause" , Command_Timer_Pause); // Timers.h
|
||||
COMMAND_CASE("timerresume" , Command_Timer_Resume); // Timers.h
|
||||
@@ -216,18 +218,18 @@ void ExecuteCommand(byte source, const char *Line)
|
||||
// since commands can originate from anywhere.
|
||||
TempEvent.Source = source;
|
||||
GetArgv(Line, cmd, 1);
|
||||
if (GetArgv(Line, TmpStr1, 2)) TempEvent.Par1 = str2int(TmpStr1);
|
||||
if (GetArgv(Line, TmpStr1, 3)) TempEvent.Par2 = str2int(TmpStr1);
|
||||
if (GetArgv(Line, TmpStr1, 4)) TempEvent.Par3 = str2int(TmpStr1);
|
||||
if (GetArgv(Line, TmpStr1, 5)) TempEvent.Par4 = str2int(TmpStr1);
|
||||
if (GetArgv(Line, TmpStr1, 6)) TempEvent.Par5 = str2int(TmpStr1);
|
||||
if (GetArgv(Line, TmpStr1, 2)) TempEvent.Par1 = CalculateParam(TmpStr1);
|
||||
if (GetArgv(Line, TmpStr1, 3)) TempEvent.Par2 = CalculateParam(TmpStr1);
|
||||
if (GetArgv(Line, TmpStr1, 4)) TempEvent.Par3 = CalculateParam(TmpStr1);
|
||||
if (GetArgv(Line, TmpStr1, 5)) TempEvent.Par4 = CalculateParam(TmpStr1);
|
||||
if (GetArgv(Line, TmpStr1, 6)) TempEvent.Par5 = CalculateParam(TmpStr1);
|
||||
|
||||
if (source == VALUE_SOURCE_WEB_FRONTEND) {
|
||||
// Must run immediately, to see result in web frontend
|
||||
String status = doExecuteCommand((char*)&cmd[0], &TempEvent, Line);
|
||||
yield();
|
||||
delay(0);
|
||||
SendStatus(source, status);
|
||||
yield();
|
||||
delay(0);
|
||||
} else {
|
||||
// Schedule to run async
|
||||
schedule_command_timer((char*)&cmd[0], &TempEvent, Line);
|
||||
|
||||
+3
-2
@@ -24,7 +24,8 @@ String Command_HTTP_SendToHTTP(struct EventStruct *event, const char* Line)
|
||||
}
|
||||
WiFiClient client;
|
||||
const int port_int = port.toInt();
|
||||
if (client.connect(host.c_str(), port_int) != 1) {
|
||||
const bool connected = client.connect(host.c_str(), port_int) == 1;
|
||||
if (connected) {
|
||||
String hostportString = host;
|
||||
if (port_int != 0 && port_int != 80) {
|
||||
hostportString += ':';
|
||||
@@ -32,7 +33,7 @@ String Command_HTTP_SendToHTTP(struct EventStruct *event, const char* Line)
|
||||
}
|
||||
String request = do_create_http_request(hostportString, F("GET"), path);
|
||||
addLog(LOG_LEVEL_DEBUG, request);
|
||||
send_via_http(F("Command_HTTP_SendToHTTP"), client, request);
|
||||
send_via_http(F("Command_HTTP_SendToHTTP"), client, request, false);
|
||||
}
|
||||
}
|
||||
return return_command_success();
|
||||
|
||||
@@ -34,4 +34,15 @@ String Command_Rules_Events(struct EventStruct *event, const char* Line)
|
||||
return return_command_success();
|
||||
}
|
||||
|
||||
String Command_Rules_Let(struct EventStruct *event, const char* Line)
|
||||
{
|
||||
char TmpStr1[INPUT_COMMAND_SIZE];
|
||||
if (GetArgv(Line, TmpStr1, 3)) {
|
||||
float result = 0;
|
||||
Calculate(TmpStr1, &result);
|
||||
customFloatVar[event->Par1-1] = result;
|
||||
}
|
||||
return return_command_success();
|
||||
}
|
||||
|
||||
#endif // COMMAND_RULES_H
|
||||
|
||||
@@ -30,6 +30,15 @@ String Command_Task_ValueSet(struct EventStruct *event, const char* Line)
|
||||
return return_command_success();
|
||||
}
|
||||
|
||||
String Command_Task_ValueToggle(struct EventStruct *event, const char* Line)
|
||||
{
|
||||
const int result = round(UserVar[(VARS_PER_TASK * (event->Par1 - 1)) + event->Par2 - 1]);
|
||||
if (result == 0 || result == 1) {
|
||||
UserVar[(VARS_PER_TASK * (event->Par1 - 1)) + event->Par2 - 1] = !result;
|
||||
}
|
||||
return return_command_success();
|
||||
}
|
||||
|
||||
String Command_Task_ValueSetAndRun(struct EventStruct *event, const char* Line)
|
||||
{
|
||||
char TmpStr1[INPUT_COMMAND_SIZE];
|
||||
|
||||
+21
-8
@@ -132,8 +132,8 @@ void callback(char* c_topic, byte* b_payload, unsigned int length) {
|
||||
bool MQTTConnect(int controller_idx)
|
||||
{
|
||||
++mqtt_reconnect_count;
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(controller_idx, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(controller_idx, ControllerSettings);
|
||||
if (!ControllerSettings.checkHostReachable(true))
|
||||
return false;
|
||||
if (MQTTclient.connected()) {
|
||||
@@ -141,6 +141,8 @@ bool MQTTConnect(int controller_idx)
|
||||
updateMQTTclient_connected();
|
||||
}
|
||||
mqtt = WiFiClient(); // workaround see: https://github.com/esp8266/Arduino/issues/4497#issuecomment-373023864
|
||||
mqtt.setTimeout(ControllerSettings.ClientTimeout);
|
||||
MQTTclient.setClient(mqtt);
|
||||
if (ControllerSettings.UseDNS) {
|
||||
MQTTclient.setServer(ControllerSettings.getHost().c_str(), ControllerSettings.Port);
|
||||
} else {
|
||||
@@ -161,6 +163,15 @@ bool MQTTConnect(int controller_idx)
|
||||
clientid = F("ESPClient_");
|
||||
clientid += WiFi.macAddress();
|
||||
}
|
||||
// Work-around for 'lost connections' to the MQTT broker.
|
||||
// If the broker thinks the connection is still alive, a reconnect from the
|
||||
// client will be refused.
|
||||
// To overcome this issue, append the number of reconnects to the client ID to
|
||||
// make it different from the previous one.
|
||||
if (wifi_reconnects >= 1) {
|
||||
clientid += F("_");
|
||||
clientid += wifi_reconnects;
|
||||
}
|
||||
|
||||
String LWTTopic = ControllerSettings.MQTTLwtTopic;
|
||||
if(LWTTopic.length() == 0)
|
||||
@@ -193,10 +204,12 @@ bool MQTTConnect(int controller_idx)
|
||||
} else {
|
||||
MQTTresult = MQTTclient.connect(clientid.c_str(), LWTTopic.c_str(), willQos, willRetain, LWTMessageDisconnect.c_str());
|
||||
}
|
||||
yield();
|
||||
delay(0);
|
||||
|
||||
if (!MQTTresult) {
|
||||
addLog(LOG_LEVEL_ERROR, F("MQTT : Failed to connect to broker"));
|
||||
MQTTclient.disconnect();
|
||||
updateMQTTclient_connected();
|
||||
return false;
|
||||
}
|
||||
MQTTclient_should_reconnect = false;
|
||||
@@ -283,9 +296,9 @@ void scheduleNextMQTTdelayQueue() {
|
||||
|
||||
void processMQTTdelayQueue() {
|
||||
START_TIMER;
|
||||
MQTT_queue_element element;
|
||||
if (!MQTTDelayHandler.getNext(element)) return;
|
||||
if (MQTTclient.publish(element._topic.c_str(), element._payload.c_str(), element._retained)) {
|
||||
MQTT_queue_element* element(MQTTDelayHandler.getNext());
|
||||
if (element == NULL) return;
|
||||
if (MQTTclient.publish(element->_topic.c_str(), element->_payload.c_str(), element->_retained)) {
|
||||
setIntervalTimerOverride(TIMER_MQTT, 10); // Make sure the MQTT is being processed as soon as possible.
|
||||
MQTTDelayHandler.markProcessed(true);
|
||||
} else {
|
||||
@@ -306,10 +319,10 @@ void processMQTTdelayQueue() {
|
||||
\*********************************************************************************************/
|
||||
void MQTTStatus(String& status)
|
||||
{
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
int enabledMqttController = firstEnabledMQTTController();
|
||||
if (enabledMqttController >= 0) {
|
||||
LoadControllerSettings(enabledMqttController, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
LoadControllerSettings(enabledMqttController, ControllerSettings);
|
||||
String pubname = ControllerSettings.Subscribe;
|
||||
pubname.replace(F("/#"), F("/status"));
|
||||
parseSystemVariables(pubname, false);
|
||||
|
||||
+244
-106
@@ -12,7 +12,7 @@
|
||||
// You can always change these during runtime and save to eeprom
|
||||
// After loading firmware, issue a 'reset' command to load the defaults.
|
||||
// --- Basic Config Settings ------------------------------------------------------------------------
|
||||
#define DEFAULT_NAME "ESP_Easy" // Enter your device friendly name
|
||||
#define DEFAULT_NAME "ESP_Easy" // Enter your device friendly name
|
||||
#define UNIT 0 // Unit Number
|
||||
#define DEFAULT_DELAY 60 // Sleep Delay in seconds
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
#if defined(ESP32)
|
||||
#define USE_RTOS_MULTITASKING
|
||||
#endif
|
||||
#ifdef M5STACK_ESP
|
||||
// #include <M5Stack.h>
|
||||
#endif
|
||||
|
||||
#define DEFAULT_USE_RULES false // (true|false) Enable Rules?
|
||||
|
||||
@@ -76,7 +79,7 @@
|
||||
#define DEFAULT_MQTT_LWT_TOPIC "" // Default lwt topic
|
||||
#define DEFAULT_MQTT_LWT_CONNECT_MESSAGE "Connected" // Default lwt message
|
||||
#define DEFAULT_MQTT_LWT_DISCONNECT_MESSAGE "Connection Lost" // Default lwt message
|
||||
#define DEFAULT_MQTT_USE_UNITNANE_AS_CLIENTID 0
|
||||
#define DEFAULT_MQTT_USE_UNITNAME_AS_CLIENTID 0
|
||||
|
||||
#define DEFAULT_USE_NTP false // (true|false) Use NTP Server
|
||||
#define DEFAULT_NTP_HOST "" // NTP Server Hostname
|
||||
@@ -188,6 +191,8 @@
|
||||
|
||||
#define MAX_FLASHWRITES_PER_DAY 100 // per 24 hour window
|
||||
#define INPUT_COMMAND_SIZE 240 // Affects maximum command length in rules and other commands
|
||||
// FIXME TD-er: INPUT_COMMAND_SIZE is also used in commands where simply a check for valid parameter is needed
|
||||
// and some may need less memory. (which is stack allocated)
|
||||
|
||||
#define NODE_TYPE_ID_ESP_EASY_STD 1
|
||||
#define NODE_TYPE_ID_ESP_EASYM_STD 17
|
||||
@@ -223,6 +228,9 @@
|
||||
// N.B. Retries without a connection to wifi do not count as retry.
|
||||
#define CONTROLLER_DELAY_QUEUE_RETRY_MAX 10
|
||||
#define CONTROLLER_DELAY_QUEUE_RETRY_DFLT 10
|
||||
// Timeout of the client in msec.
|
||||
#define CONTROLLER_CLIENTTIMEOUT_MAX 1000
|
||||
#define CONTROLLER_CLIENTTIMEOUT_DFLT 1000
|
||||
|
||||
|
||||
#define PLUGIN_INIT_ALL 1
|
||||
@@ -251,6 +259,7 @@
|
||||
#define PLUGIN_GET_CONFIG 24
|
||||
#define PLUGIN_UNCONDITIONAL_POLL 25
|
||||
#define PLUGIN_REQUEST 26
|
||||
#define PLUGIN_TIME_CHANGE 27
|
||||
|
||||
#define CPLUGIN_PROTOCOL_ADD 1
|
||||
#define CPLUGIN_PROTOCOL_TEMPLATE 2
|
||||
@@ -284,6 +293,7 @@
|
||||
#define NPLUGIN_NOT_FOUND 255
|
||||
|
||||
|
||||
#define LOG_LEVEL_NONE 0
|
||||
#define LOG_LEVEL_ERROR 1
|
||||
#define LOG_LEVEL_INFO 2
|
||||
#define LOG_LEVEL_DEBUG 3
|
||||
@@ -295,9 +305,13 @@
|
||||
#define CMD_WIFI_DISCONNECT 135
|
||||
|
||||
#if defined(PLUGIN_BUILD_TESTING) || defined(PLUGIN_BUILD_DEV)
|
||||
#define DEVICES_MAX 75
|
||||
#define DEVICES_MAX 85
|
||||
#else
|
||||
#define DEVICES_MAX 50
|
||||
#ifdef ESP32
|
||||
#define DEVICES_MAX 75
|
||||
#else
|
||||
#define DEVICES_MAX 50
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266)
|
||||
@@ -325,6 +339,9 @@
|
||||
#define RULESETS_MAX 4
|
||||
#define RULES_BUFFER_SIZE 64
|
||||
#define NAME_FORMULA_LENGTH_MAX 40
|
||||
#define RULES_IF_MAX_NESTING_LEVEL 4
|
||||
#define CUSTOM_VARS_MAX 16
|
||||
|
||||
#define UDP_PACKETSIZE_MAX 2048
|
||||
|
||||
#define PIN_MODE_UNDEFINED 0
|
||||
@@ -357,6 +374,9 @@
|
||||
#define SENSOR_TYPE_LONG 20
|
||||
#define SENSOR_TYPE_WIND 21
|
||||
|
||||
#define UNIT_NUMBER_MAX 9999 // Stored in Settings.Unit
|
||||
#define DOMOTICZ_MAX_IDX 999999999 // Looks like it is an unsigned int, so could be up to 4 bln.
|
||||
|
||||
#define VALUE_SOURCE_SYSTEM 1
|
||||
#define VALUE_SOURCE_SERIAL 2
|
||||
#define VALUE_SOURCE_HTTP 3
|
||||
@@ -393,14 +413,21 @@
|
||||
#endif
|
||||
|
||||
// Forward declaration
|
||||
struct ControllerSettingsStruct;
|
||||
void scheduleNextDelayQueue(unsigned long id, unsigned long nextTime);
|
||||
String LoadControllerSettings(int ControllerIndex, byte* memAddress, int datasize);
|
||||
String LoadControllerSettings(int ControllerIndex, ControllerSettingsStruct& controller_settings);
|
||||
String get_formatted_Controller_number(int controller_index);
|
||||
bool loglevelActiveFor(byte logLevel);
|
||||
void addToLog(byte loglevel, const String& string);
|
||||
void addToLog(byte logLevel, const __FlashStringHelper* flashString);
|
||||
void statusLED(boolean traffic);
|
||||
void backgroundtasks();
|
||||
uint32_t getCurrentFreeStack();
|
||||
uint32_t getFreeStackWatermark();
|
||||
bool canYield();
|
||||
|
||||
bool getBitFromUL(uint32_t number, byte bitnr);
|
||||
void setBitToUL(uint32_t& number, byte bitnr, bool value);
|
||||
|
||||
enum SettingsType {
|
||||
BasicSettings_Type = 0,
|
||||
@@ -512,10 +539,10 @@ bool showSettingsFileLayout = false;
|
||||
#define FILE_RULES "/rules1.txt"
|
||||
#include <WiFi.h>
|
||||
#include "esp32_ping.h"
|
||||
#include <ESP32WebServer.h>
|
||||
#include <WebServer.h>
|
||||
#include "SPIFFS.h"
|
||||
#include <rom/rtc.h>
|
||||
ESP32WebServer WebServer(80);
|
||||
WebServer WebServer(80);
|
||||
#ifdef FEATURE_MDNS
|
||||
#include <ESPmDNS.h>
|
||||
#endif
|
||||
@@ -575,14 +602,17 @@ bool MQTTclient_should_reconnect = true;
|
||||
bool MQTTclient_connected = false;
|
||||
int mqtt_reconnect_count = 0;
|
||||
|
||||
//NTP status
|
||||
bool statusNTPInitialized = false;
|
||||
|
||||
// mqtt import status
|
||||
bool P037_MQTTImport_connected = false;
|
||||
|
||||
// udp protocol stuff (syslog, global sync, node info list, ntp time)
|
||||
WiFiUDP portUDP;
|
||||
|
||||
class TimingStats;
|
||||
|
||||
#define LOADFILE_STATS 0
|
||||
#define LOOP_STATS 1
|
||||
|
||||
/*********************************************************************************************\
|
||||
* CRCStruct
|
||||
\*********************************************************************************************/
|
||||
@@ -650,6 +680,16 @@ struct SecurityStruct
|
||||
uint8_t md5[16];
|
||||
} SecuritySettings;
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Custom Variables for usage in rules and http.
|
||||
* Syntax: %vX%
|
||||
* usage:
|
||||
* let,1,10
|
||||
* if %v1%=10 do ...
|
||||
\*********************************************************************************************/
|
||||
float customFloatVar[CUSTOM_VARS_MAX];
|
||||
|
||||
/*********************************************************************************************\
|
||||
* SettingsStruct
|
||||
\*********************************************************************************************/
|
||||
@@ -659,17 +699,46 @@ struct SettingsStruct
|
||||
clearAll();
|
||||
}
|
||||
|
||||
// VariousBits1 defaults to 0, keep in mind when adding bit lookups.
|
||||
bool appendUnitToHostname() { return !getBitFromUL(VariousBits1, 1); }
|
||||
void appendUnitToHostname(bool value) { setBitToUL(VariousBits1, 1, !value); }
|
||||
|
||||
void validate() {
|
||||
if (UDPPort > 65535) UDPPort = 0;
|
||||
|
||||
if (Latitude < -90.0 || Latitude > 90.0) Latitude = 0.0;
|
||||
if (Longitude < -180.0 || Longitude > 180.0) Longitude = 0.0;
|
||||
if (VariousBits1 > (1 << 30)) VariousBits1 = 0;
|
||||
}
|
||||
|
||||
bool networkSettingsEmpty() {
|
||||
return (IP[0] == 0 && Gateway[0] == 0 && Subnet[0] == 0 && DNS[0] == 0);
|
||||
}
|
||||
|
||||
void clearNetworkSettings() {
|
||||
for (byte i = 0; i < 4; ++i) {
|
||||
IP[i] = 0;
|
||||
Gateway[i] = 0;
|
||||
Subnet[i] = 0;
|
||||
DNS[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void clearAll() {
|
||||
PID = 0;
|
||||
Version = 0;
|
||||
Build = 0;
|
||||
IP_Octet = 0;
|
||||
Unit = 0;
|
||||
Name[0] = 0;
|
||||
NTPHost[0] = 0;
|
||||
Delay = 0;
|
||||
Pin_i2c_sda = -1;
|
||||
Pin_i2c_scl = -1;
|
||||
Pin_status_led = -1;
|
||||
Pin_sd_cs = -1;
|
||||
for (byte i = 0; i < 17; ++i) { PinBootStates[i] = 0; }
|
||||
for (byte i = 0; i < 4; ++i) { Syslog_IP[i] = 0; }
|
||||
UDPPort = 0;
|
||||
SyslogLevel = 0;
|
||||
SerialLogLevel = 0;
|
||||
@@ -694,13 +763,17 @@ struct SettingsStruct
|
||||
Pin_status_led_Inversed = false;
|
||||
deepSleepOnFail = false;
|
||||
UseValueLogger = false;
|
||||
ArduinoOTAEnable = false;
|
||||
DST_Start = 0;
|
||||
DST_End = 0;
|
||||
UseRTOSMultitasking = false;
|
||||
Pin_Reset = -1;
|
||||
SyslogFacility = DEFAULT_SYSLOG_FACILITY;
|
||||
StructSize = 0;
|
||||
StructSize = sizeof(SettingsStruct);
|
||||
MQTTUseUnitNameAsClientId = 0;
|
||||
Latitude = 0.0;
|
||||
Longitude = 0.0;
|
||||
VariousBits1 = 0;
|
||||
|
||||
for (byte i = 0; i < CONTROLLER_MAX; ++i) {
|
||||
Protocol[i] = 0;
|
||||
@@ -713,6 +786,7 @@ struct SettingsStruct
|
||||
for (byte task = 0; task < TASKS_MAX; ++task) {
|
||||
clearTask(task);
|
||||
}
|
||||
clearNetworkSettings();
|
||||
}
|
||||
|
||||
void clearTask(byte task) {
|
||||
@@ -827,6 +901,7 @@ struct SettingsStruct
|
||||
//TODO: document config.dat somewhere here
|
||||
float Latitude;
|
||||
float Longitude;
|
||||
uint32_t VariousBits1;
|
||||
|
||||
// FIXME @TD-er: As discussed in #1292, the CRC for the settings is now disabled.
|
||||
// make sure crc is the last value in the struct
|
||||
@@ -834,15 +909,35 @@ struct SettingsStruct
|
||||
// uint8_t ProgmemMd5[16]; // crc of the binary that last saved the struct to file.
|
||||
// uint8_t md5[16];
|
||||
} Settings;
|
||||
/*
|
||||
SettingsStruct* SettingsStruct_ptr = new SettingsStruct;
|
||||
SettingsStruct& Settings = *SettingsStruct_ptr;
|
||||
*/
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
* ControllerSettingsStruct
|
||||
* Analyze SettingsStruct and report inconsistencies
|
||||
* Not a member function to be able to use the F-macro
|
||||
\*********************************************************************************************/
|
||||
bool SettingsCheck(String& error) {
|
||||
error = "";
|
||||
if (!Settings.networkSettingsEmpty()) {
|
||||
if (Settings.IP[0] == 0 || Settings.Gateway[0] == 0 || Settings.Subnet[0] == 0 || Settings.DNS[0] == 0) {
|
||||
error += F("Error: Either fill all IP settings fields or leave all empty");
|
||||
}
|
||||
}
|
||||
|
||||
return error.length() == 0;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* ControllerSettingsStruct definition
|
||||
\*********************************************************************************************/
|
||||
struct ControllerSettingsStruct
|
||||
{
|
||||
ControllerSettingsStruct() : UseDNS(false), Port(0),
|
||||
MinimalTimeBetweenMessages(100), MaxQueueDepth(10), MaxRetry(10), DeleteOldest(false) {
|
||||
MinimalTimeBetweenMessages(100), MaxQueueDepth(10), MaxRetry(10),
|
||||
DeleteOldest(false), ClientTimeout(100), MustCheckReply(false) {
|
||||
for (byte i = 0; i < 4; ++i) {
|
||||
IP[i] = 0;
|
||||
}
|
||||
@@ -866,6 +961,19 @@ struct ControllerSettingsStruct
|
||||
unsigned int MaxQueueDepth;
|
||||
unsigned int MaxRetry;
|
||||
boolean DeleteOldest; // Action to perform when buffer full, delete oldest, or ignore newest.
|
||||
unsigned int ClientTimeout;
|
||||
boolean MustCheckReply; // When set to false, a sent message is considered always successful.
|
||||
|
||||
void validate() {
|
||||
if (Port > 65535) Port = 0;
|
||||
if (MinimalTimeBetweenMessages < 1 || MinimalTimeBetweenMessages > CONTROLLER_DELAY_QUEUE_DELAY_MAX)
|
||||
MinimalTimeBetweenMessages = CONTROLLER_DELAY_QUEUE_DELAY_DFLT;
|
||||
if (MaxQueueDepth > CONTROLLER_DELAY_QUEUE_DEPTH_MAX) MaxQueueDepth = CONTROLLER_DELAY_QUEUE_DEPTH_DFLT;
|
||||
if (MaxRetry > CONTROLLER_DELAY_QUEUE_RETRY_MAX) MaxRetry = CONTROLLER_DELAY_QUEUE_RETRY_MAX;
|
||||
if (ClientTimeout < 10 || ClientTimeout > CONTROLLER_CLIENTTIMEOUT_MAX) {
|
||||
ClientTimeout = CONTROLLER_CLIENTTIMEOUT_DFLT;
|
||||
}
|
||||
}
|
||||
|
||||
IPAddress getIP() const {
|
||||
IPAddress host(IP[0], IP[1], IP[2], IP[3]);
|
||||
@@ -888,6 +996,7 @@ struct ControllerSettingsStruct
|
||||
if (!WiFiConnected(10)) {
|
||||
return false; // Not connected, so no use in wasting time to connect to a host.
|
||||
}
|
||||
delay(1); // Make sure the Watchdog will not trigger a reset.
|
||||
if (quick && ipSet()) return true;
|
||||
if (UseDNS) {
|
||||
if (!updateIPcache()) {
|
||||
@@ -966,6 +1075,11 @@ private:
|
||||
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<ControllerSettingsStruct> ControllerSettingsStruct_ptr_type;
|
||||
#define MakeControllerSettings(T) ControllerSettingsStruct_ptr_type ControllerSettingsStruct_ptr(new ControllerSettingsStruct());\
|
||||
ControllerSettingsStruct& T = *ControllerSettingsStruct_ptr;
|
||||
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
* NotificationSettingsStruct
|
||||
@@ -997,6 +1111,11 @@ struct NotificationSettingsStruct
|
||||
//its safe to extend this struct, up to 4096 bytes, default values in config are 0
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<NotificationSettingsStruct> NotificationSettingsStruct_ptr_type;
|
||||
#define MakeNotificationSettings(T) NotificationSettingsStruct_ptr_type NotificationSettingsStruct_ptr(new NotificationSettingsStruct());\
|
||||
NotificationSettingsStruct& T = *NotificationSettingsStruct_ptr;
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
* ExtraTaskSettingsStruct
|
||||
\*********************************************************************************************/
|
||||
@@ -1080,22 +1199,36 @@ struct ExtraTaskSettingsStruct
|
||||
struct EventStruct
|
||||
{
|
||||
EventStruct() :
|
||||
Data(NULL), idx(0), Par1(0), Par2(0), Par3(0), Par4(0), Par5(0),
|
||||
Source(0), TaskIndex(TASKS_MAX), ControllerIndex(0), ProtocolIndex(0), NotificationIndex(0),
|
||||
BaseVarIndex(0), idx(0), sensorType(0), Par1(0), Par2(0), Par3(0), Par4(0), Par5(0),
|
||||
OriginTaskIndex(0), Data(NULL) {}
|
||||
BaseVarIndex(0), sensorType(0), OriginTaskIndex(0) {}
|
||||
EventStruct(const struct EventStruct& event):
|
||||
Source(event.Source), TaskIndex(event.TaskIndex), ControllerIndex(event.ControllerIndex)
|
||||
, ProtocolIndex(event.ProtocolIndex), NotificationIndex(event.NotificationIndex)
|
||||
, BaseVarIndex(event.BaseVarIndex), idx(event.idx), sensorType(event.sensorType)
|
||||
, Par1(event.Par1), Par2(event.Par2), Par3(event.Par3), Par4(event.Par4), Par5(event.Par5)
|
||||
, OriginTaskIndex(event.OriginTaskIndex)
|
||||
, String1(event.String1)
|
||||
String1(event.String1)
|
||||
, String2(event.String2)
|
||||
, String3(event.String3)
|
||||
, String4(event.String4)
|
||||
, String5(event.String5)
|
||||
, Data(event.Data) {}
|
||||
, Data(event.Data)
|
||||
, idx(event.idx)
|
||||
, Par1(event.Par1), Par2(event.Par2), Par3(event.Par3), Par4(event.Par4), Par5(event.Par5)
|
||||
, Source(event.Source), TaskIndex(event.TaskIndex), ControllerIndex(event.ControllerIndex)
|
||||
, ProtocolIndex(event.ProtocolIndex), NotificationIndex(event.NotificationIndex)
|
||||
, BaseVarIndex(event.BaseVarIndex), sensorType(event.sensorType)
|
||||
, OriginTaskIndex(event.OriginTaskIndex)
|
||||
{}
|
||||
|
||||
String String1;
|
||||
String String2;
|
||||
String String3;
|
||||
String String4;
|
||||
String String5;
|
||||
byte *Data;
|
||||
int idx;
|
||||
int Par1;
|
||||
int Par2;
|
||||
int Par3;
|
||||
int Par4;
|
||||
int Par5;
|
||||
byte Source;
|
||||
byte TaskIndex; // index position in TaskSettings array, 0-11
|
||||
byte ControllerIndex; // index position in Settings.Controller, 0-3
|
||||
@@ -1104,20 +1237,8 @@ struct EventStruct
|
||||
//Edwin: Not needed, and wasnt used. We can determine the protocol index with getNotificationProtocolIndex(NotificationIndex)
|
||||
// byte NotificationProtocolIndex; // index position in notification array, depending on which controller plugins are loaded.
|
||||
byte BaseVarIndex;
|
||||
int idx;
|
||||
byte sensorType;
|
||||
int Par1;
|
||||
int Par2;
|
||||
int Par3;
|
||||
int Par4;
|
||||
int Par5;
|
||||
byte OriginTaskIndex;
|
||||
String String1;
|
||||
String String2;
|
||||
String String3;
|
||||
String String4;
|
||||
String String5;
|
||||
byte *Data;
|
||||
};
|
||||
|
||||
|
||||
@@ -1127,18 +1248,19 @@ struct EventStruct
|
||||
#define LOG_STRUCT_MESSAGE_SIZE 128
|
||||
#ifdef ESP32
|
||||
#define LOG_STRUCT_MESSAGE_LINES 30
|
||||
#define LOG_BUFFER_EXPIRE 30000 // Time after which a buffered log item is considered expired.
|
||||
#else
|
||||
#if defined(PLUGIN_BUILD_TESTING) || defined(PLUGIN_BUILD_DEV)
|
||||
#define LOG_STRUCT_MESSAGE_LINES 10
|
||||
#else
|
||||
#define LOG_STRUCT_MESSAGE_LINES 15
|
||||
#endif
|
||||
#define LOG_BUFFER_EXPIRE 5000 // Time after which a buffered log item is considered expired.
|
||||
#endif
|
||||
|
||||
struct LogStruct {
|
||||
LogStruct() : write_idx(0), read_idx(0) {
|
||||
LogStruct() : write_idx(0), read_idx(0), lastReadTimeStamp(0) {
|
||||
for (int i = 0; i < LOG_STRUCT_MESSAGE_LINES; ++i) {
|
||||
Message[i].reserve(LOG_STRUCT_MESSAGE_SIZE);
|
||||
timeStamp[i] = 0;
|
||||
log_level[i] = 0;
|
||||
}
|
||||
@@ -1156,6 +1278,7 @@ struct LogStruct {
|
||||
if (linelength > LOG_STRUCT_MESSAGE_SIZE-1)
|
||||
linelength = LOG_STRUCT_MESSAGE_SIZE-1;
|
||||
Message[write_idx] = "";
|
||||
Message[write_idx].reserve(linelength);
|
||||
for (unsigned i = 0; i < linelength; ++i) {
|
||||
Message[write_idx] += *(line + i);
|
||||
}
|
||||
@@ -1164,6 +1287,7 @@ struct LogStruct {
|
||||
// Read the next item and append it to the given string.
|
||||
// Returns whether new lines are available.
|
||||
bool get(String& output, const String& lineEnd) {
|
||||
lastReadTimeStamp = millis();
|
||||
if (!isEmpty()) {
|
||||
read_idx = (read_idx + 1) % LOG_STRUCT_MESSAGE_LINES;
|
||||
output += formatLine(read_idx, lineEnd);
|
||||
@@ -1172,6 +1296,7 @@ struct LogStruct {
|
||||
}
|
||||
|
||||
String get_logjson_formatted(bool& logLinesAvailable, unsigned long& timestamp) {
|
||||
lastReadTimeStamp = millis();
|
||||
logLinesAvailable = false;
|
||||
if (isEmpty()) {
|
||||
return "";
|
||||
@@ -1185,31 +1310,15 @@ struct LogStruct {
|
||||
return output;
|
||||
}
|
||||
|
||||
bool get(String& output, const String& lineEnd, int line) {
|
||||
int tmpread((write_idx + 1+line) % LOG_STRUCT_MESSAGE_LINES);
|
||||
if (timeStamp[tmpread] != 0) {
|
||||
output += formatLine(tmpread, lineEnd);
|
||||
}
|
||||
return !isEmpty();
|
||||
}
|
||||
|
||||
bool getAll(String& output, const String& lineEnd) {
|
||||
int tmpread((write_idx + 1) % LOG_STRUCT_MESSAGE_LINES);
|
||||
bool someAdded = false;
|
||||
while (tmpread != write_idx) {
|
||||
if (timeStamp[tmpread] != 0) {
|
||||
output += formatLine(tmpread, lineEnd);
|
||||
someAdded = true;
|
||||
}
|
||||
tmpread = (tmpread + 1)% LOG_STRUCT_MESSAGE_LINES;
|
||||
}
|
||||
return someAdded;
|
||||
}
|
||||
|
||||
bool isEmpty() {
|
||||
return (write_idx == read_idx);
|
||||
}
|
||||
|
||||
bool logActiveRead() {
|
||||
clearExpiredEntries();
|
||||
return timePassedSince(lastReadTimeStamp) < LOG_BUFFER_EXPIRE;
|
||||
}
|
||||
|
||||
private:
|
||||
String formatLine(int index, const String& lineEnd) {
|
||||
String output;
|
||||
@@ -1233,20 +1342,38 @@ struct LogStruct {
|
||||
return output;
|
||||
}
|
||||
|
||||
void clearExpiredEntries() {
|
||||
if (isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (timePassedSince(lastReadTimeStamp) > LOG_BUFFER_EXPIRE) {
|
||||
// Clear the entire log.
|
||||
// If web log is the only log active, it will not be checked again until it is read.
|
||||
for (read_idx = 0; read_idx < LOG_STRUCT_MESSAGE_LINES; ++read_idx) {
|
||||
Message[read_idx] = String(); // Free also the reserved memory.
|
||||
timeStamp[read_idx] = 0;
|
||||
log_level[read_idx] = 0;
|
||||
}
|
||||
read_idx = 0;
|
||||
write_idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int write_idx;
|
||||
int read_idx;
|
||||
unsigned long timeStamp[LOG_STRUCT_MESSAGE_LINES];
|
||||
unsigned long lastReadTimeStamp;
|
||||
byte log_level[LOG_STRUCT_MESSAGE_LINES];
|
||||
String Message[LOG_STRUCT_MESSAGE_LINES];
|
||||
|
||||
} Logging;
|
||||
|
||||
std::deque<char> serialLogBuffer;
|
||||
unsigned long last_serial_log_emptied = 0;
|
||||
unsigned long last_serial_log_read = 0;
|
||||
|
||||
byte highest_active_log_level = 0;
|
||||
bool log_to_serial_disabled = false;
|
||||
bool log_to_serial_disabled_temporary = false;
|
||||
// Do this in a template to prevent casting to String when not needed.
|
||||
#define addLog(L,S) if (loglevelActiveFor(L)) { addToLog(L,S); }
|
||||
|
||||
@@ -1270,16 +1397,16 @@ struct DeviceStruct
|
||||
byte Type; // How the device is connected. e.g. DEVICE_TYPE_SINGLE => connected through 1 datapin
|
||||
byte VType; // Type of value the plugin will return, used only for Domoticz
|
||||
byte Ports; // Port to use when device has multiple I/O pins (N.B. not used much)
|
||||
byte ValueCount; // The number of output values of a plugin. The value should match the number of keys PLUGIN_VALUENAME1_xxx
|
||||
boolean PullUpOption; // Allow to set internal pull-up resistors.
|
||||
boolean InverseLogicOption; // Allow to invert the boolean state (e.g. a switch)
|
||||
boolean FormulaOption; // Allow to enter a formula to convert values during read. (not possible with Custom enabled)
|
||||
boolean Custom;
|
||||
boolean SendDataOption; // Allow to send data to a controller.
|
||||
boolean GlobalSyncOption; // No longer used. Was used for ESPeasy values sync between nodes
|
||||
boolean TimerOption; // Allow to set the "Interval" timer for the plugin.
|
||||
boolean TimerOptional; // When taskdevice timer is not set and not optional, use default "Interval" delay (Settings.Delay)
|
||||
boolean DecimalsOnly; // Allow to set the number of decimals (otherwise treated a 0 decimals)
|
||||
byte ValueCount; // The number of output values of a plugin. The value should match the number of keys PLUGIN_VALUENAME1_xxx
|
||||
bool PullUpOption : 1; // Allow to set internal pull-up resistors.
|
||||
bool InverseLogicOption : 1; // Allow to invert the boolean state (e.g. a switch)
|
||||
bool FormulaOption : 1; // Allow to enter a formula to convert values during read. (not possible with Custom enabled)
|
||||
bool Custom : 1;
|
||||
bool SendDataOption : 1; // Allow to send data to a controller.
|
||||
bool GlobalSyncOption : 1; // No longer used. Was used for ESPeasy values sync between nodes
|
||||
bool TimerOption : 1; // Allow to set the "Interval" timer for the plugin.
|
||||
bool TimerOptional : 1; // When taskdevice timer is not set and not optional, use default "Interval" delay (Settings.Delay)
|
||||
bool DecimalsOnly; // Allow to set the number of decimals (otherwise treated a 0 decimals)
|
||||
};
|
||||
typedef std::vector<DeviceStruct> DeviceVector;
|
||||
DeviceVector Device;
|
||||
@@ -1344,16 +1471,16 @@ NodesMap Nodes;
|
||||
struct systemTimerStruct
|
||||
{
|
||||
systemTimerStruct() :
|
||||
timer(0), plugin(0), TaskIndex(-1), Par1(0), Par2(0), Par3(0), Par4(0), Par5(0) {}
|
||||
timer(0), Par1(0), Par2(0), Par3(0), Par4(0), Par5(0), TaskIndex(-1), plugin(0) {}
|
||||
|
||||
unsigned long timer;
|
||||
byte plugin;
|
||||
int16_t TaskIndex;
|
||||
int Par1;
|
||||
int Par2;
|
||||
int Par3;
|
||||
int Par4;
|
||||
int Par5;
|
||||
int16_t TaskIndex;
|
||||
byte plugin;
|
||||
};
|
||||
std::map<unsigned long, systemTimerStruct> systemTimers;
|
||||
|
||||
@@ -1362,10 +1489,11 @@ std::map<unsigned long, systemTimerStruct> systemTimers;
|
||||
\*********************************************************************************************/
|
||||
struct pinStatesStruct
|
||||
{
|
||||
pinStatesStruct() : value(0), plugin(0), index(0), mode(0) {}
|
||||
uint16_t value;
|
||||
byte plugin;
|
||||
byte index;
|
||||
byte mode;
|
||||
uint16_t value;
|
||||
} pinStates[PINSTATE_TABLE_MAX];
|
||||
|
||||
|
||||
@@ -1379,18 +1507,20 @@ struct pinStatesStruct
|
||||
//max 40 bytes: ( 74 - 64 ) * 4
|
||||
struct RTCStruct
|
||||
{
|
||||
RTCStruct() : ID1(0), ID2(0), unused1(false), factoryResetCounter(0),
|
||||
deepSleepState(0), bootFailedCount(0), flashDayCounter(0),
|
||||
flashCounter(0), bootCounter(0) {}
|
||||
byte ID1;
|
||||
byte ID2;
|
||||
boolean unused1;
|
||||
byte factoryResetCounter;
|
||||
byte deepSleepState;
|
||||
byte unused2;
|
||||
byte bootFailedCount;
|
||||
byte flashDayCounter;
|
||||
unsigned long flashCounter;
|
||||
unsigned long bootCounter;
|
||||
} RTC;
|
||||
|
||||
|
||||
int deviceCount = -1;
|
||||
int protocolCount = -1;
|
||||
int notificationCount = -1;
|
||||
@@ -1406,6 +1536,8 @@ float UserVar[VARS_PER_TASK * TASKS_MAX];
|
||||
\*********************************************************************************************/
|
||||
struct rulesTimerStatus
|
||||
{
|
||||
rulesTimerStatus() : timestamp(0), interval(0), paused(false) {}
|
||||
|
||||
unsigned long timestamp;
|
||||
unsigned int interval; //interval in milliseconds
|
||||
boolean paused;
|
||||
@@ -1413,14 +1545,15 @@ struct rulesTimerStatus
|
||||
|
||||
msecTimerHandlerStruct msecTimerHandler;
|
||||
|
||||
unsigned long timermqtt_interval;
|
||||
unsigned long lastSend;
|
||||
unsigned long lastWeb;
|
||||
unsigned long timermqtt_interval = 250;
|
||||
unsigned long lastSend = 0;
|
||||
unsigned long lastWeb = 0;
|
||||
byte cmd_within_mainloop = 0;
|
||||
unsigned long connectionFailures;
|
||||
unsigned long connectionFailures = 0;
|
||||
unsigned long wdcounter = 0;
|
||||
unsigned long timerAPoff = 0;
|
||||
unsigned long timerAwakeFromDeepSleep = 0;
|
||||
unsigned long last_system_event_run = 0;
|
||||
|
||||
#if FEATURE_ADC_VCC
|
||||
float vcc = -1.0;
|
||||
@@ -1567,15 +1700,18 @@ String eventBuffer = "";
|
||||
|
||||
uint32_t lowestRAM = 0;
|
||||
String lowestRAMfunction = "";
|
||||
uint32_t lowestFreeStack = 0;
|
||||
String lowestFreeStackfunction = "";
|
||||
|
||||
|
||||
bool shouldReboot=false;
|
||||
bool firstLoop=true;
|
||||
|
||||
boolean activeRuleSets[RULESETS_MAX];
|
||||
|
||||
boolean UseRTOSMultitasking;
|
||||
boolean UseRTOSMultitasking = false;
|
||||
|
||||
void (*MainLoopCall_ptr)(void);
|
||||
// void (*MainLoopCall_ptr)(void); //FIXME TD-er: No idea what this does.
|
||||
|
||||
/*********************************************************************************************\
|
||||
* TimingStats
|
||||
@@ -1714,31 +1850,32 @@ unsigned long timediff_calls = 0;
|
||||
unsigned long timediff_cpu_cycles_total = 0;
|
||||
|
||||
#define LOADFILE_STATS 0
|
||||
#define LOOP_STATS 1
|
||||
#define PLUGIN_CALL_50PS 2
|
||||
#define PLUGIN_CALL_10PS 3
|
||||
#define PLUGIN_CALL_10PSU 4
|
||||
#define PLUGIN_CALL_1PS 5
|
||||
#define SENSOR_SEND_TASK 6
|
||||
#define SEND_DATA_STATS 7
|
||||
#define COMPUTE_FORMULA_STATS 8
|
||||
#define PROC_SYS_TIMER 9
|
||||
#define SET_NEW_TIMER 10
|
||||
#define TIME_DIFF_COMPUTE 11
|
||||
#define MQTT_DELAY_QUEUE 12
|
||||
#define C001_DELAY_QUEUE 13
|
||||
#define C002_DELAY_QUEUE 14
|
||||
#define C003_DELAY_QUEUE 15
|
||||
#define C004_DELAY_QUEUE 16
|
||||
#define C005_DELAY_QUEUE 17
|
||||
#define C006_DELAY_QUEUE 18
|
||||
#define C007_DELAY_QUEUE 19
|
||||
#define C008_DELAY_QUEUE 20
|
||||
#define C009_DELAY_QUEUE 21
|
||||
#define C010_DELAY_QUEUE 22
|
||||
#define C011_DELAY_QUEUE 23
|
||||
#define C012_DELAY_QUEUE 24
|
||||
#define C013_DELAY_QUEUE 25
|
||||
#define SAVEFILE_STATS 1
|
||||
#define LOOP_STATS 2
|
||||
#define PLUGIN_CALL_50PS 3
|
||||
#define PLUGIN_CALL_10PS 4
|
||||
#define PLUGIN_CALL_10PSU 5
|
||||
#define PLUGIN_CALL_1PS 6
|
||||
#define SENSOR_SEND_TASK 7
|
||||
#define SEND_DATA_STATS 8
|
||||
#define COMPUTE_FORMULA_STATS 9
|
||||
#define PROC_SYS_TIMER 10
|
||||
#define SET_NEW_TIMER 11
|
||||
#define TIME_DIFF_COMPUTE 12
|
||||
#define MQTT_DELAY_QUEUE 13
|
||||
#define C001_DELAY_QUEUE 14
|
||||
#define C002_DELAY_QUEUE 15
|
||||
#define C003_DELAY_QUEUE 16
|
||||
#define C004_DELAY_QUEUE 17
|
||||
#define C005_DELAY_QUEUE 18
|
||||
#define C006_DELAY_QUEUE 19
|
||||
#define C007_DELAY_QUEUE 20
|
||||
#define C008_DELAY_QUEUE 21
|
||||
#define C009_DELAY_QUEUE 22
|
||||
#define C010_DELAY_QUEUE 23
|
||||
#define C011_DELAY_QUEUE 24
|
||||
#define C012_DELAY_QUEUE 25
|
||||
#define C013_DELAY_QUEUE 26
|
||||
|
||||
|
||||
|
||||
@@ -1747,13 +1884,14 @@ unsigned long timediff_cpu_cycles_total = 0;
|
||||
|
||||
#define START_TIMER const unsigned statisticsTimerStart(micros());
|
||||
#define STOP_TIMER_TASK(T,F) if (mustLogFunction(F)) pluginStats[T*32 + F].add(usecPassedSince(statisticsTimerStart));
|
||||
#define STOP_TIMER_LOADFILE miscStats[LOADFILE_STATS].add(usecPassedSince(statisticsTimerStart));
|
||||
//#define STOP_TIMER_LOADFILE miscStats[LOADFILE_STATS].add(usecPassedSince(statisticsTimerStart));
|
||||
#define STOP_TIMER(L) miscStats[L].add(usecPassedSince(statisticsTimerStart));
|
||||
|
||||
|
||||
String getMiscStatsName(int stat) {
|
||||
switch (stat) {
|
||||
case LOADFILE_STATS: return F("Load File");
|
||||
case SAVEFILE_STATS: return F("Save File");
|
||||
case LOOP_STATS: return F("Loop");
|
||||
case PLUGIN_CALL_50PS: return F("Plugin call 50 p/s ");
|
||||
case PLUGIN_CALL_10PS: return F("Plugin call 10 p/s ");
|
||||
|
||||
+59
-14
@@ -106,9 +106,14 @@ int firstEnabledBlynkController() {
|
||||
\*********************************************************************************************/
|
||||
void setup()
|
||||
{
|
||||
#ifdef ESP8266_DISABLE_EXTRA4K
|
||||
disable_extra4k_at_link_time();
|
||||
#endif
|
||||
WiFi.persistent(false); // Do not use SDK storage of SSID/WPA parameters
|
||||
WiFi.setAutoReconnect(false);
|
||||
setWifiMode(WIFI_OFF);
|
||||
lowestFreeStack = getFreeStackWatermark();
|
||||
lowestRAM = FreeMem();
|
||||
|
||||
Plugin_id.resize(PLUGIN_MAX);
|
||||
Task_id_to_Plugin_id.resize(TASKS_MAX);
|
||||
@@ -119,8 +124,6 @@ void setup()
|
||||
ledChannelPin[x] = -1;
|
||||
#endif
|
||||
|
||||
lowestRAM = FreeMem();
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.print("\n\n\nBOOOTTT\n\n\n");
|
||||
|
||||
@@ -158,6 +161,7 @@ void setup()
|
||||
//warm boot
|
||||
if (readFromRTC())
|
||||
{
|
||||
RTC.bootFailedCount++;
|
||||
RTC.bootCounter++;
|
||||
readUserVarFromRTC();
|
||||
|
||||
@@ -193,6 +197,16 @@ void setup()
|
||||
fileSystemCheck();
|
||||
progMemMD5check();
|
||||
LoadSettings();
|
||||
if (RTC.bootFailedCount > 10 && RTC.bootCounter > 10) {
|
||||
byte toDisable = RTC.bootFailedCount - 10;
|
||||
toDisable = disablePlugin(toDisable);
|
||||
if (toDisable != 0) {
|
||||
toDisable = disableController(toDisable);
|
||||
}
|
||||
if (toDisable != 0) {
|
||||
toDisable = disableNotification(toDisable);
|
||||
}
|
||||
}
|
||||
|
||||
// setWifiMode(WIFI_STA);
|
||||
checkRuleSets();
|
||||
@@ -304,9 +318,17 @@ void setup()
|
||||
if(UseRTOSMultitasking){
|
||||
log = F("RTOS : Launching tasks");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
xTaskCreatePinnedToCore(RTOS_TaskServers, "RTOS_TaskServers", 8192, NULL, 1, NULL, 1);
|
||||
xTaskCreatePinnedToCore(RTOS_TaskServers, "RTOS_TaskServers", 16384, NULL, 1, NULL, 1);
|
||||
xTaskCreatePinnedToCore(RTOS_TaskSerial, "RTOS_TaskSerial", 8192, NULL, 1, NULL, 1);
|
||||
xTaskCreatePinnedToCore(RTOS_Task10ps, "RTOS_Task10ps", 8192, NULL, 1, NULL, 1);
|
||||
xTaskCreatePinnedToCore(
|
||||
RTOS_HandleSchedule, /* Function to implement the task */
|
||||
"RTOS_HandleSchedule", /* Name of the task */
|
||||
16384, /* Stack size in words */
|
||||
NULL, /* Task input parameter */
|
||||
1, /* Priority of the task */
|
||||
NULL, /* Task handle. */
|
||||
1); /* Core where the task should run */
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -353,6 +375,14 @@ void RTOS_Task10ps( void * parameter )
|
||||
run10TimesPerSecond();
|
||||
}
|
||||
}
|
||||
|
||||
void RTOS_HandleSchedule( void * parameter )
|
||||
{
|
||||
while (true){
|
||||
handle_schedule();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int firstEnabledMQTTController() {
|
||||
@@ -435,8 +465,11 @@ int getLoopCountPerSec() {
|
||||
\*********************************************************************************************/
|
||||
void loop()
|
||||
{
|
||||
/*
|
||||
//FIXME TD-er: No idea what this does.
|
||||
if(MainLoopCall_ptr)
|
||||
MainLoopCall_ptr();
|
||||
*/
|
||||
|
||||
updateLoopStats();
|
||||
|
||||
@@ -465,7 +498,10 @@ void loop()
|
||||
firstLoop = false;
|
||||
timerAwakeFromDeepSleep = millis(); // Allow to run for "awake" number of seconds, now we have wifi.
|
||||
// schedule_all_task_device_timers(); Disabled for now, since we are now using queues for controllers.
|
||||
}
|
||||
|
||||
RTC.bootFailedCount = 0;
|
||||
saveToRTC();
|
||||
}
|
||||
|
||||
// Deep sleep mode, just run all tasks one (more) time and go back to sleep as fast as possible
|
||||
if ((firstLoopConnectionsEstablished || readyForSleep()) && isDeepSleepEnabled())
|
||||
@@ -480,7 +516,10 @@ void loop()
|
||||
//normal mode, run each task when its time
|
||||
else
|
||||
{
|
||||
handle_schedule();
|
||||
if (!UseRTOSMultitasking) {
|
||||
// On ESP32 the schedule is executed on the 2nd core.
|
||||
handle_schedule();
|
||||
}
|
||||
}
|
||||
|
||||
backgroundtasks();
|
||||
@@ -493,6 +532,7 @@ void loop()
|
||||
}
|
||||
// Flush outstanding MQTT messages
|
||||
runPeriodicalMQTT();
|
||||
flushAndDisconnectAllClients();
|
||||
|
||||
deepSleep(Settings.Delay);
|
||||
//deepsleep will never return, its a special kind of reboot
|
||||
@@ -508,6 +548,14 @@ bool checkConnectionsEstablished() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void flushAndDisconnectAllClients() {
|
||||
if (MQTTclient.connected()) {
|
||||
MQTTclient.disconnect();
|
||||
updateMQTTclient_connected();
|
||||
}
|
||||
/// FIXME TD-er: add call to all controllers (delay queue) to flush all data.
|
||||
}
|
||||
|
||||
void runPeriodicalMQTT() {
|
||||
// MQTT_KEEPALIVE = 15 seconds.
|
||||
if (!WiFiConnected(10)) {
|
||||
@@ -800,13 +848,10 @@ void SensorSendTask(byte TaskIndex)
|
||||
{
|
||||
if (ExtraTaskSettings.TaskDeviceFormula[varNr][0] != 0)
|
||||
{
|
||||
String spreValue = String(preValue[varNr]);
|
||||
String formula = ExtraTaskSettings.TaskDeviceFormula[varNr];
|
||||
float value = UserVar[varIndex + varNr];
|
||||
formula.replace(F("%pvalue%"), String(preValue[varNr]));
|
||||
formula.replace(F("%value%"), String(UserVar[varIndex + varNr]));
|
||||
float result = 0;
|
||||
String svalue = String(value);
|
||||
formula.replace(F("%pvalue%"), spreValue);
|
||||
formula.replace(F("%value%"), svalue);
|
||||
byte error = Calculate(formula.c_str(), &result);
|
||||
if (error == 0)
|
||||
UserVar[varIndex + varNr] = result;
|
||||
@@ -827,11 +872,11 @@ void backgroundtasks()
|
||||
{
|
||||
//checkRAM(F("backgroundtasks"));
|
||||
//always start with a yield
|
||||
yield();
|
||||
delay(0);
|
||||
/*
|
||||
// Remove this watchdog feed for now.
|
||||
// See https://github.com/letscontrolit/ESPEasy/issues/1722#issuecomment-419659193
|
||||
|
||||
|
||||
#ifdef ESP32
|
||||
// Have to find a similar function to call ESP32's esp_task_wdt_feed();
|
||||
#else
|
||||
@@ -870,13 +915,13 @@ void backgroundtasks()
|
||||
//once OTA is triggered, only handle that and dont do other stuff. (otherwise it fails)
|
||||
while (ArduinoOTAtriggered)
|
||||
{
|
||||
yield();
|
||||
delay(0);
|
||||
ArduinoOTA.handle();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
yield();
|
||||
delay(0);
|
||||
|
||||
statusLED(false);
|
||||
|
||||
|
||||
+153
-34
@@ -41,6 +41,19 @@ String flashGuard()
|
||||
//use this in function that can return an error string. it automaticly returns with an error string if there where too many flash writes.
|
||||
#define FLASH_GUARD() { String flashErr=flashGuard(); if (flashErr.length()) return(flashErr); }
|
||||
|
||||
|
||||
String appendLineToFile(const String& fname, const String& line) {
|
||||
fs::File f = SPIFFS.open(fname, "a+");
|
||||
SPIFFS_CHECK(f, fname.c_str());
|
||||
const size_t lineLength = line.length();
|
||||
for (size_t i = 0; i < lineLength; ++i) {
|
||||
SPIFFS_CHECK(f.write(line[i]), fname.c_str());
|
||||
}
|
||||
f.close();
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************\
|
||||
Fix stuff to clear out differences between releases
|
||||
\*********************************************************************************************/
|
||||
@@ -77,7 +90,7 @@ String BuildFixes()
|
||||
Settings.UseRTOSMultitasking = false;
|
||||
Settings.Pin_Reset = -1;
|
||||
Settings.SyslogFacility = DEFAULT_SYSLOG_FACILITY;
|
||||
Settings.MQTTUseUnitNameAsClientId = DEFAULT_MQTT_USE_UNITNANE_AS_CLIENTID;
|
||||
Settings.MQTTUseUnitNameAsClientId = DEFAULT_MQTT_USE_UNITNAME_AS_CLIENTID;
|
||||
Settings.StructSize = sizeof(Settings);
|
||||
}
|
||||
|
||||
@@ -113,7 +126,7 @@ void fileSystemCheck()
|
||||
{
|
||||
ResetFactory();
|
||||
}
|
||||
f.close();
|
||||
if (f) f.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -147,9 +160,14 @@ String SaveSettings(void)
|
||||
// Settings have changed, save to file.
|
||||
memcpy(Settings.md5, tmp_md5, 16);
|
||||
*/
|
||||
Settings.validate();
|
||||
err=SaveToFile((char*)FILE_CONFIG, 0, (byte*)&Settings, sizeof(Settings));
|
||||
if (err.length())
|
||||
return(err);
|
||||
return(err);
|
||||
// Must check this after saving, or else it is not possible to fix multiple
|
||||
// issues which can only corrected on different pages.
|
||||
if (!SettingsCheck(err)) return err;
|
||||
|
||||
// }
|
||||
|
||||
memcpy( SecuritySettings.ProgmemMd5, CRCValues.runTimeMD5, 16);
|
||||
@@ -182,6 +200,7 @@ String LoadSettings()
|
||||
err=LoadFromFile((char*)FILE_CONFIG, 0, (byte*)&Settings, sizeof( SettingsStruct));
|
||||
if (err.length())
|
||||
return(err);
|
||||
Settings.validate();
|
||||
|
||||
// FIXME @TD-er: As discussed in #1292, the CRC for the settings is now disabled.
|
||||
/*
|
||||
@@ -219,6 +238,51 @@ String LoadSettings()
|
||||
return(err);
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
Disable Plugin, based on bootFailedCount
|
||||
\*********************************************************************************************/
|
||||
byte disablePlugin(byte bootFailedCount) {
|
||||
for (byte i = 0; i < TASKS_MAX && bootFailedCount > 0; ++i) {
|
||||
if (Settings.TaskDeviceEnabled[i]) {
|
||||
--bootFailedCount;
|
||||
if (bootFailedCount == 0) {
|
||||
Settings.TaskDeviceEnabled[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bootFailedCount;
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
Disable Controller, based on bootFailedCount
|
||||
\*********************************************************************************************/
|
||||
byte disableController(byte bootFailedCount) {
|
||||
for (byte i = 0; i < CONTROLLER_MAX && bootFailedCount > 0; ++i) {
|
||||
if (Settings.ControllerEnabled[i]) {
|
||||
--bootFailedCount;
|
||||
if (bootFailedCount == 0) {
|
||||
Settings.ControllerEnabled[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bootFailedCount;
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
Disable Notification, based on bootFailedCount
|
||||
\*********************************************************************************************/
|
||||
byte disableNotification(byte bootFailedCount) {
|
||||
for (byte i = 0; i < NOTIFICATION_MAX && bootFailedCount > 0; ++i) {
|
||||
if (Settings.NotificationEnabled[i]) {
|
||||
--bootFailedCount;
|
||||
if (bootFailedCount == 0) {
|
||||
Settings.NotificationEnabled[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bootFailedCount;
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
Offsets in settings files
|
||||
\*********************************************************************************************/
|
||||
@@ -366,6 +430,13 @@ String SaveCustomTaskSettings(int TaskIndex, byte* memAddress, int datasize)
|
||||
return(SaveToFile(CustomTaskSettings_Type, TaskIndex, (char*)FILE_CONFIG, memAddress, datasize));
|
||||
}
|
||||
|
||||
String getCustomTaskSettingsError(byte varNr) {
|
||||
String error = F("Error: Text too long for line ");
|
||||
error += varNr + 1;
|
||||
error += '\n';
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************\
|
||||
Clear custom task settings
|
||||
@@ -388,20 +459,25 @@ String LoadCustomTaskSettings(int TaskIndex, byte* memAddress, int datasize)
|
||||
/********************************************************************************************\
|
||||
Save Controller settings to SPIFFS
|
||||
\*********************************************************************************************/
|
||||
String SaveControllerSettings(int ControllerIndex, byte* memAddress, int datasize)
|
||||
String SaveControllerSettings(int ControllerIndex, ControllerSettingsStruct& controller_settings)
|
||||
{
|
||||
checkRAM(F("SaveControllerSettings"));
|
||||
return SaveToFile(ControllerSettings_Type, ControllerIndex, (char*)FILE_CONFIG, memAddress, datasize);
|
||||
controller_settings.validate(); // Make sure the saved controller settings have proper values.
|
||||
return SaveToFile(ControllerSettings_Type, ControllerIndex,
|
||||
(char*)FILE_CONFIG, (byte*)&controller_settings, sizeof(controller_settings));
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************\
|
||||
Load Controller settings to SPIFFS
|
||||
\*********************************************************************************************/
|
||||
String LoadControllerSettings(int ControllerIndex, byte* memAddress, int datasize)
|
||||
{
|
||||
String LoadControllerSettings(int ControllerIndex, ControllerSettingsStruct& controller_settings) {
|
||||
checkRAM(F("LoadControllerSettings"));
|
||||
return(LoadFromFile(ControllerSettings_Type, ControllerIndex, (char*)FILE_CONFIG, memAddress, datasize));
|
||||
String result =
|
||||
LoadFromFile(ControllerSettings_Type, ControllerIndex,
|
||||
(char*)FILE_CONFIG, (byte*)&controller_settings, sizeof(controller_settings));
|
||||
controller_settings.validate(); // Make sure the loaded controller settings have proper values.
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -466,13 +542,15 @@ String InitFile(const char* fname, int datasize)
|
||||
FLASH_GUARD();
|
||||
|
||||
fs::File f = SPIFFS.open(fname, "w");
|
||||
SPIFFS_CHECK(f, fname);
|
||||
if (f) {
|
||||
SPIFFS_CHECK(f, fname);
|
||||
|
||||
for (int x = 0; x < datasize ; x++)
|
||||
{
|
||||
SPIFFS_CHECK(f.write(0), fname);
|
||||
for (int x = 0; x < datasize ; x++)
|
||||
{
|
||||
SPIFFS_CHECK(f.write(0), fname);
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
f.close();
|
||||
|
||||
//OK
|
||||
return String();
|
||||
@@ -483,6 +561,15 @@ String InitFile(const char* fname, int datasize)
|
||||
\*********************************************************************************************/
|
||||
String SaveToFile(char* fname, int index, byte* memAddress, int datasize)
|
||||
{
|
||||
#ifndef ESP32
|
||||
if (allocatedOnStack(memAddress)) {
|
||||
String log = F("SaveToFile: ");
|
||||
log += fname;
|
||||
log += F(" ERROR, Data allocated on stack");
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
// return log; // FIXME TD-er: Should this be considered a breaking error?
|
||||
}
|
||||
#endif
|
||||
if (index < 0) {
|
||||
String log = F("SaveToFile: ");
|
||||
log += fname;
|
||||
@@ -490,24 +577,47 @@ String SaveToFile(char* fname, int index, byte* memAddress, int datasize)
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
return log;
|
||||
}
|
||||
|
||||
START_TIMER;
|
||||
checkRAM(F("SaveToFile"));
|
||||
FLASH_GUARD();
|
||||
|
||||
fs::File f = SPIFFS.open(fname, "r+");
|
||||
SPIFFS_CHECK(f, fname);
|
||||
|
||||
SPIFFS_CHECK(f.seek(index, fs::SeekSet), fname);
|
||||
byte *pointerToByteToSave = memAddress;
|
||||
for (int x = 0; x < datasize ; x++)
|
||||
{
|
||||
SPIFFS_CHECK(f.write(*pointerToByteToSave), fname);
|
||||
pointerToByteToSave++;
|
||||
String log = F("SaveToFile: free stack: ");
|
||||
log += getCurrentFreeStack();
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
delay(1);
|
||||
unsigned long timer = millis() + 50;
|
||||
fs::File f = SPIFFS.open(fname, "r+");
|
||||
if (f) {
|
||||
SPIFFS_CHECK(f, fname);
|
||||
SPIFFS_CHECK(f.seek(index, fs::SeekSet), fname);
|
||||
byte *pointerToByteToSave = memAddress;
|
||||
for (int x = 0; x < datasize ; x++)
|
||||
{
|
||||
SPIFFS_CHECK(f.write(*pointerToByteToSave), fname);
|
||||
pointerToByteToSave++;
|
||||
if (timeOutReached(timer)) {
|
||||
timer += 50;
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
String log = F("FILE : Saved ");
|
||||
log=log+fname;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
} else {
|
||||
String log = F("SaveToFile: ");
|
||||
log += fname;
|
||||
log += F(" ERROR, Cannot save to file");
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
return log;
|
||||
}
|
||||
STOP_TIMER(SAVEFILE_STATS);
|
||||
{
|
||||
String log = F("SaveToFile: free stack after: ");
|
||||
log += getCurrentFreeStack();
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
f.close();
|
||||
String log = F("FILE : Saved ");
|
||||
log=log+fname;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
|
||||
//OK
|
||||
return String();
|
||||
@@ -530,14 +640,22 @@ String ClearInFile(char* fname, int index, int datasize)
|
||||
FLASH_GUARD();
|
||||
|
||||
fs::File f = SPIFFS.open(fname, "r+");
|
||||
SPIFFS_CHECK(f, fname);
|
||||
if (f) {
|
||||
SPIFFS_CHECK(f, fname);
|
||||
|
||||
SPIFFS_CHECK(f.seek(index, fs::SeekSet), fname);
|
||||
for (int x = 0; x < datasize ; x++)
|
||||
{
|
||||
SPIFFS_CHECK(f.write(0), fname);
|
||||
SPIFFS_CHECK(f.seek(index, fs::SeekSet), fname);
|
||||
for (int x = 0; x < datasize ; x++)
|
||||
{
|
||||
SPIFFS_CHECK(f.write(0), fname);
|
||||
}
|
||||
f.close();
|
||||
} else {
|
||||
String log = F("ClearInFile: ");
|
||||
log += fname;
|
||||
log += F(" ERROR, Cannot save to file");
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
return log;
|
||||
}
|
||||
f.close();
|
||||
|
||||
//OK
|
||||
return String();
|
||||
@@ -556,10 +674,10 @@ String LoadFromFile(char* fname, int offset, byte* memAddress, int datasize)
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
return log;
|
||||
}
|
||||
delay(1);
|
||||
START_TIMER;
|
||||
|
||||
checkRAM(F("LoadFromFile"));
|
||||
|
||||
fs::File f = SPIFFS.open(fname, "r+");
|
||||
SPIFFS_CHECK(f, fname);
|
||||
SPIFFS_CHECK(f.seek(offset, fs::SeekSet), fname);
|
||||
@@ -567,6 +685,7 @@ String LoadFromFile(char* fname, int offset, byte* memAddress, int datasize)
|
||||
f.close();
|
||||
|
||||
STOP_TIMER(LOADFILE_STATS);
|
||||
delay(1);
|
||||
|
||||
return(String());
|
||||
}
|
||||
|
||||
+2
-12
@@ -3,17 +3,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <list>
|
||||
|
||||
struct timeStruct {
|
||||
timeStruct() : Second(0), Minute(0), Hour(0), Wday(0), Day(0), Month(0), Year(0) {}
|
||||
uint8_t Second;
|
||||
uint8_t Minute;
|
||||
uint8_t Hour;
|
||||
uint8_t Wday; // day of week, sunday is day 1
|
||||
uint8_t Day;
|
||||
uint8_t Month;
|
||||
uint8_t Year; // offset from 1970;
|
||||
};
|
||||
#include <time.h>
|
||||
|
||||
// convenient constants for TimeChangeRules
|
||||
enum week_t {Last=0, First, Second, Third, Fourth};
|
||||
@@ -184,7 +174,7 @@ private:
|
||||
if (is_idle) return;
|
||||
last_exec_time_usec = micros();
|
||||
is_idle = true;
|
||||
yield(); // Nothing to do, so call yield for backgroundtasks
|
||||
delay(0); // Nothing to do, so leave time for backgroundtasks
|
||||
}
|
||||
|
||||
void recordRunning() {
|
||||
|
||||
+5
-2
@@ -418,8 +418,10 @@ void setWifiMode(WiFiMode_t wifimode) {
|
||||
String WifiGetAPssid()
|
||||
{
|
||||
String ssid(Settings.Name);
|
||||
ssid+=F("_");
|
||||
ssid+=Settings.Unit;
|
||||
if (Settings.appendUnitToHostname()) {
|
||||
ssid+=F("_");
|
||||
ssid+=Settings.Unit;
|
||||
}
|
||||
return (ssid);
|
||||
}
|
||||
|
||||
@@ -676,6 +678,7 @@ void WifiScan()
|
||||
|
||||
String formatScanResult(int i, const String& separator) {
|
||||
String result = WiFi.SSID(i);
|
||||
htmlEscape(result);
|
||||
#ifndef ESP32
|
||||
if (WiFi.isHidden(i)) {
|
||||
result += F("#Hidden#");
|
||||
|
||||
+13
-5
@@ -11,6 +11,12 @@ bool I2C_read_words(uint8_t i2caddr, I2Cdata_words& data) {
|
||||
return size == i2cdev.readWords(i2caddr, data.getRegister(), size, data.get());
|
||||
}
|
||||
|
||||
// See https://github.com/platformio/platform-espressif32/issues/126
|
||||
#ifdef ESP32
|
||||
#define END_TRANSMISSION_FLAG true
|
||||
#else
|
||||
#define END_TRANSMISSION_FLAG false
|
||||
#endif
|
||||
|
||||
//**************************************************************************/
|
||||
// Wake up I2C device
|
||||
@@ -66,7 +72,7 @@ uint8_t I2C_read8_reg(uint8_t i2caddr, byte reg, bool * is_ok) {
|
||||
|
||||
Wire.beginTransmission(i2caddr);
|
||||
Wire.write((uint8_t)reg);
|
||||
Wire.endTransmission(false);
|
||||
Wire.endTransmission(END_TRANSMISSION_FLAG);
|
||||
byte count = Wire.requestFrom(i2caddr, (byte)1);
|
||||
if (is_ok != NULL) {
|
||||
*is_ok = (count == 1);
|
||||
@@ -84,7 +90,7 @@ uint16_t I2C_read16_reg(uint8_t i2caddr, byte reg) {
|
||||
|
||||
Wire.beginTransmission(i2caddr);
|
||||
Wire.write((uint8_t)reg);
|
||||
Wire.endTransmission(false);
|
||||
Wire.endTransmission(END_TRANSMISSION_FLAG);
|
||||
Wire.requestFrom(i2caddr, (byte)2);
|
||||
value = (Wire.read() << 8) | Wire.read();
|
||||
|
||||
@@ -99,7 +105,7 @@ int32_t I2C_read24_reg(uint8_t i2caddr, byte reg) {
|
||||
|
||||
Wire.beginTransmission(i2caddr);
|
||||
Wire.write((uint8_t)reg);
|
||||
Wire.endTransmission(false);
|
||||
Wire.endTransmission(END_TRANSMISSION_FLAG);
|
||||
Wire.requestFrom(i2caddr, (byte)3);
|
||||
value = (((int32_t)Wire.read()) << 16) | (Wire.read() << 8) | Wire.read();
|
||||
|
||||
@@ -114,8 +120,8 @@ int32_t I2C_read32_reg(uint8_t i2caddr, byte reg) {
|
||||
|
||||
Wire.beginTransmission(i2caddr);
|
||||
Wire.write((uint8_t)reg);
|
||||
Wire.endTransmission(false);
|
||||
Wire.requestFrom(i2caddr, (byte)3);
|
||||
Wire.endTransmission(END_TRANSMISSION_FLAG);
|
||||
Wire.requestFrom(i2caddr, (byte)4);
|
||||
value = (((int32_t)Wire.read()) <<24) | (((uint32_t)Wire.read()) << 16) | (Wire.read() << 8) | Wire.read();
|
||||
|
||||
return value;
|
||||
@@ -139,3 +145,5 @@ int16_t I2C_readS16_reg(uint8_t i2caddr, byte reg) {
|
||||
int16_t I2C_readS16_LE_reg(uint8_t i2caddr, byte reg) {
|
||||
return (int16_t)I2C_read16_LE_reg(i2caddr, reg);
|
||||
}
|
||||
|
||||
#undef END_TRANSMISSION_FLAG
|
||||
|
||||
+811
-347
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -70,7 +70,7 @@ bool Modbus::begin(uint8_t function, uint8_t ModbusID, uint16_t ModbusRegister,
|
||||
resultReceived = false;
|
||||
ModbusClient = new WiFiClient();
|
||||
ModbusClient->setNoDelay(true);
|
||||
ModbusClient->setTimeout(200);
|
||||
ModbusClient->setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT);
|
||||
timeout = millis();
|
||||
ModbusClient->flush();
|
||||
|
||||
|
||||
+1
-1
@@ -623,7 +623,7 @@ bool WiFiConnected(uint32_t timeout_ms) {
|
||||
uint32_t timer = millis() + (timeout_ms > 500 ? 500 : timeout_ms);
|
||||
uint32_t min_delay = timeout_ms / 20;
|
||||
if (min_delay < 10) {
|
||||
yield(); // Allow at least once time for backgroundtasks
|
||||
delay(0); // Allow at least once time for backgroundtasks
|
||||
min_delay = 10;
|
||||
}
|
||||
// Apparently something needs network, perform check to see if it is ready now.
|
||||
|
||||
+12
-5
@@ -41,19 +41,26 @@ unsigned long getMixedId(unsigned long timerType, unsigned long id) {
|
||||
\*********************************************************************************************/
|
||||
void handle_schedule() {
|
||||
unsigned long timer;
|
||||
const unsigned long mixed_id = msecTimerHandler.getNextId(timer);
|
||||
unsigned long mixed_id = 0;
|
||||
if (timePassedSince(last_system_event_run) < 500) {
|
||||
// Make sure system event queue will be looked at every now and then.
|
||||
mixed_id = msecTimerHandler.getNextId(timer);
|
||||
}
|
||||
if (mixed_id == 0) {
|
||||
// No id ready to run right now.
|
||||
// Events are not that important to run immediately.
|
||||
// Make sure normal scheduled jobs run at higher priority.
|
||||
backgroundtasks();
|
||||
process_system_event_queue();
|
||||
last_system_event_run = millis();
|
||||
return;
|
||||
}
|
||||
const unsigned long timerType = (mixed_id >> TIMER_ID_SHIFT);
|
||||
const unsigned long mask = (1 << TIMER_ID_SHIFT) -1;
|
||||
const unsigned long id = mixed_id & mask;
|
||||
|
||||
delay(0); // See: https://github.com/letscontrolit/ESPEasy/issues/1818#issuecomment-425351328
|
||||
|
||||
switch (timerType) {
|
||||
case CONST_INTERVAL_TIMER:
|
||||
process_interval_timer(id, timer);
|
||||
@@ -262,11 +269,11 @@ void process_plugin_task_timer(unsigned long id) {
|
||||
log += id;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
*/
|
||||
systemTimers.erase(id);
|
||||
if (y >= 0) {
|
||||
String dummy;
|
||||
Plugin_ptr[y](PLUGIN_TIMER_IN, &TempEvent, dummy);
|
||||
}
|
||||
systemTimers.erase(id);
|
||||
}
|
||||
STOP_TIMER(PROC_SYS_TIMER);
|
||||
}
|
||||
|
||||
@@ -427,9 +434,9 @@ void process_system_event_queue() {
|
||||
EventQueue.front().cmd.c_str(),
|
||||
&EventQueue.front().event,
|
||||
EventQueue.front().line.c_str());
|
||||
yield();
|
||||
delay(0);
|
||||
SendStatus(EventQueue.front().event.Source, status);
|
||||
yield();
|
||||
delay(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ void serial()
|
||||
{
|
||||
while (Serial.available())
|
||||
{
|
||||
yield();
|
||||
delay(0);
|
||||
SerialInByte = Serial.read();
|
||||
if (SerialInByte == 255) // binary data...
|
||||
{
|
||||
|
||||
+99
-28
@@ -131,7 +131,7 @@ String toString(float value, byte decimals)
|
||||
|
||||
String toString(WiFiMode_t mode)
|
||||
{
|
||||
String result = F("Undefinited");
|
||||
String result = F("Undefined");
|
||||
switch (mode)
|
||||
{
|
||||
case WIFI_OFF:
|
||||
@@ -165,11 +165,13 @@ String doFormatUserVar(byte TaskIndex, byte rel_index, bool mustCheck, bool& isv
|
||||
const byte DeviceIndex = getDeviceIndex(Settings.TaskDeviceNumber[TaskIndex]);
|
||||
if (Device[DeviceIndex].ValueCount <= rel_index) {
|
||||
isvalid = false;
|
||||
String log = F("No sensor value for TaskIndex: ");
|
||||
log += TaskIndex;
|
||||
log += F(" varnumber: ");
|
||||
log += rel_index;
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
|
||||
String log = F("No sensor value for TaskIndex: ");
|
||||
log += TaskIndex;
|
||||
log += F(" varnumber: ");
|
||||
log += rel_index;
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
if (Device[DeviceIndex].VType == SENSOR_TYPE_LONG) {
|
||||
@@ -178,11 +180,13 @@ String doFormatUserVar(byte TaskIndex, byte rel_index, bool mustCheck, bool& isv
|
||||
float f(UserVar[BaseVarIndex + rel_index]);
|
||||
if (mustCheck && !isValidFloat(f)) {
|
||||
isvalid = false;
|
||||
String log = F("Invalid float value for TaskIndex: ");
|
||||
log += TaskIndex;
|
||||
log += F(" varnumber: ");
|
||||
log += rel_index;
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
||||
String log = F("Invalid float value for TaskIndex: ");
|
||||
log += TaskIndex;
|
||||
log += F(" varnumber: ");
|
||||
log += rel_index;
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
}
|
||||
f = 0;
|
||||
}
|
||||
return toString(f, ExtraTaskSettings.TaskDeviceValueDecimals[rel_index]);
|
||||
@@ -210,13 +214,11 @@ String formatUserVar(struct EventStruct *event, byte rel_index, bool& isvalid)
|
||||
/*********************************************************************************************\
|
||||
Wrap a string with given pre- and postfix string.
|
||||
\*********************************************************************************************/
|
||||
String wrap_String(const String& string, const String& wrap) {
|
||||
String result;
|
||||
result.reserve(string.length() + 2* wrap.length());
|
||||
result = wrap;
|
||||
|
||||
void wrap_String(const String& string, const String& wrap, String& result) {
|
||||
result += wrap;
|
||||
result += string;
|
||||
result += wrap;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
@@ -225,19 +227,25 @@ String wrap_String(const String& string, const String& wrap) {
|
||||
String to_json_object_value(const String& object, const String& value) {
|
||||
String result;
|
||||
result.reserve(object.length() + value.length() + 6);
|
||||
result = wrap_String(object, F("\""));
|
||||
wrap_String(object, F("\""), result);
|
||||
result += F(":");
|
||||
if (value.length() == 0 || !isFloat(value)) {
|
||||
if (value.indexOf('\n') == -1 && value.indexOf('"') == -1 && value.indexOf(F("Pragma")) == -1) {
|
||||
result += wrap_String(value, F("\""));
|
||||
} else {
|
||||
if (value.length() == 0) {
|
||||
// Empty string
|
||||
result += F("\"\"");
|
||||
} else if (!isFloat(value)) {
|
||||
// Is not a numerical value, thus wrap with quotes
|
||||
if (value.indexOf('\n') != -1 || value.indexOf('\r') != -1 || value.indexOf('"') != -1) {
|
||||
// Must replace characters, so make a deepcopy
|
||||
String tmpValue(value);
|
||||
tmpValue.replace('\n', '^');
|
||||
tmpValue.replace('\r', '^');
|
||||
tmpValue.replace('"', '\'');
|
||||
tmpValue.replace(F("Pragma"), F("Bugje!"));
|
||||
result += wrap_String(tmpValue, F("\""));
|
||||
wrap_String(tmpValue, F("\""), result);
|
||||
} else {
|
||||
wrap_String(value, F("\""), result);
|
||||
}
|
||||
} else {
|
||||
// It is a numerical
|
||||
result += value;
|
||||
}
|
||||
return result;
|
||||
@@ -280,6 +288,26 @@ String stripQuotes(const String& text) {
|
||||
return text;
|
||||
}
|
||||
|
||||
bool safe_strncpy(char* dest, const String& source, size_t max_size) {
|
||||
return safe_strncpy(dest, source.c_str(), max_size);
|
||||
}
|
||||
|
||||
bool safe_strncpy(char* dest, const char* source, size_t max_size) {
|
||||
if (max_size < 1) return false;
|
||||
if (dest == NULL) return false;
|
||||
if (source == NULL) return false;
|
||||
bool result = true;
|
||||
memset(dest, 0, max_size);
|
||||
size_t str_length = strlen(source);
|
||||
if (str_length >= max_size) {
|
||||
str_length = max_size;
|
||||
result = false;
|
||||
}
|
||||
strncpy(dest, source, str_length);
|
||||
dest[max_size - 1] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
Parse a string and get the xth command or parameter in lower case
|
||||
\*********************************************************************************************/
|
||||
@@ -373,8 +401,32 @@ void htmlEscape(String & html)
|
||||
html.replace("'", F("'"));
|
||||
html.replace("<", F("<"));
|
||||
html.replace(">", F(">"));
|
||||
html.replace("/", F("/"));
|
||||
}
|
||||
|
||||
void htmlStrongEscape(String & html)
|
||||
{
|
||||
String escaped;
|
||||
escaped.reserve(html.length());
|
||||
for (unsigned i = 0; i < html.length(); ++i)
|
||||
{
|
||||
if ((html[i] >= 'a' && html[i] <= 'z') || (html[i] >= 'A' && html[i] <= 'Z') || (html[i] >= '0' && html[i] <= '9'))
|
||||
{
|
||||
escaped += html[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
char s [4];
|
||||
sprintf(s, "%03d", static_cast<int>(html[i]));
|
||||
escaped += "&#";
|
||||
escaped += s;
|
||||
escaped += ";";
|
||||
}
|
||||
}
|
||||
html = escaped;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************\
|
||||
replace other system variables like %sysname%, %systime%, %ip%
|
||||
\*********************************************************************************************/
|
||||
@@ -508,6 +560,8 @@ void parseSystemVariables(String& s, boolean useURLencode)
|
||||
SMART_REPL(F("%systm_hm_am%"), getTimeString_ampm(':', false))
|
||||
SMART_REPL(F("%systime%"), getTimeString(':'))
|
||||
SMART_REPL(F("%systime_am%"), getTimeString_ampm(':'))
|
||||
SMART_REPL(F("%sysbuild_date%"), String(CRCValues.compileDate))
|
||||
SMART_REPL(F("%sysbuild_time%"), String(CRCValues.compileTime))
|
||||
repl(F("%sysname%"), Settings.Name, s, useURLencode);
|
||||
|
||||
// valueString is being used by the macro.
|
||||
@@ -540,17 +594,34 @@ void parseSystemVariables(String& s, boolean useURLencode)
|
||||
SMART_REPL(F("%unixtime%"), String(getUnixTime()))
|
||||
SMART_REPL_T(F("%sunset"), replSunSetTimeString)
|
||||
SMART_REPL_T(F("%sunrise"), replSunRiseTimeString)
|
||||
|
||||
if (s.indexOf(F("%is")) != -1) {
|
||||
SMART_REPL(F("%ismqtt%"), String(MQTTclient_connected));
|
||||
SMART_REPL(F("%iswifi%"), String(wifiStatus)); //0=disconnected, 1=connected, 2=got ip, 3=services initialized
|
||||
SMART_REPL(F("%isntp%"), String(statusNTPInitialized));
|
||||
#ifdef USES_P037
|
||||
SMART_REPL(F("%ismqttimp%"), String(P037_MQTTImport_connected));
|
||||
#endif // USES_P037
|
||||
}
|
||||
const int v_index = s.indexOf("%v");
|
||||
if (v_index != -1 && isDigit(s[v_index+2])) {
|
||||
for (byte i = 0; i < CUSTOM_VARS_MAX; ++i) {
|
||||
SMART_REPL("%v"+toString(i+1,0)+'%', String(customFloatVar[i]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String getReplacementString(const String& format, String& s) {
|
||||
int startpos = s.indexOf(format);
|
||||
int endpos = s.indexOf('%', startpos + 1);
|
||||
String R = s.substring(startpos, endpos + 1);
|
||||
String log = F("ReplacementString SunTime: ");
|
||||
log += R;
|
||||
log += F(" offset: ");
|
||||
log += getSecOffset(R);
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
||||
String log = F("ReplacementString SunTime: ");
|
||||
log += R;
|
||||
log += F(" offset: ");
|
||||
log += getSecOffset(R);
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
|
||||
+352
-307
@@ -1,7 +1,7 @@
|
||||
|
||||
/********************************************************************************************\
|
||||
Time stuff
|
||||
\*********************************************************************************************/
|
||||
Time stuff
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define SECS_PER_MIN (60UL)
|
||||
#define SECS_PER_HOUR (3600UL)
|
||||
@@ -11,89 +11,96 @@
|
||||
#define SECS_PER_YEAR (SECS_PER_WEEK * 52UL)
|
||||
#define SECS_YR_2000 (946684800UL) // the time at the start of y2k
|
||||
#define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )
|
||||
#include <time.h>
|
||||
|
||||
timeStruct tm;
|
||||
struct tm tm;
|
||||
uint32_t syncInterval = 3600; // time sync will be attempted after this many seconds
|
||||
uint32_t sysTime = 0;
|
||||
double sysTime = 0.0; // Use high resolution double to get better sync between nodes when using NTP
|
||||
uint32_t prevMillis = 0;
|
||||
uint32_t nextSyncTime = 0;
|
||||
timeStruct tsRise, tsSet;
|
||||
timeStruct sunRise;
|
||||
timeStruct sunSet;
|
||||
struct tm tsRise, tsSet;
|
||||
struct tm sunRise;
|
||||
struct tm sunSet;
|
||||
|
||||
byte PrevMinutes = 0;
|
||||
|
||||
float sunDeclination(int doy) {
|
||||
// Declination of the sun in radians
|
||||
// Formula 2008 by Arnold(at)Barmettler.com, fit to 20 years of average declinations (2008-2027)
|
||||
return 0.409526325277017 * sin(0.0169060504029192 * (doy - 80.0856919827619));
|
||||
// Declination of the sun in radians
|
||||
// Formula 2008 by Arnold(at)Barmettler.com, fit to 20 years of average declinations (2008-2027)
|
||||
return 0.409526325277017 * sin(0.0169060504029192 * (doy - 80.0856919827619));
|
||||
}
|
||||
|
||||
float diurnalArc(float dec, float lat) {
|
||||
// Duration of the half sun path in hours (time from sunrise to the highest level in the south)
|
||||
float rad = 0.0174532925; // = pi/180.0
|
||||
float height = -50.0 / 60.0 * rad;
|
||||
float latRad = lat * rad;
|
||||
return 12.0 * acos((sin(height) - sin(latRad) * sin(dec)) / (cos(latRad) * cos(dec))) / 3.1415926536;
|
||||
// Duration of the half sun path in hours (time from sunrise to the highest level in the south)
|
||||
float rad = 0.0174532925; // = pi/180.0
|
||||
float height = -50.0 / 60.0 * rad;
|
||||
float latRad = lat * rad;
|
||||
return 12.0 * acos((sin(height) - sin(latRad) * sin(dec)) / (cos(latRad) * cos(dec))) / 3.1415926536;
|
||||
}
|
||||
|
||||
float equationOfTime(int doy) {
|
||||
// Difference between apparent and mean solar time
|
||||
// Formula 2008 by Arnold(at)Barmettler.com, fit to 20 years of average equation of time (2008-2027)
|
||||
return -0.170869921174742 * sin(0.0336997028793971 * doy + 0.465419984181394) - 0.129890681040717 * sin(0.0178674832556871 * doy - 0.167936777524864);
|
||||
// Difference between apparent and mean solar time
|
||||
// Formula 2008 by Arnold(at)Barmettler.com, fit to 20 years of average equation of time (2008-2027)
|
||||
return -0.170869921174742 * sin(0.0336997028793971 * doy + 0.465419984181394) - 0.129890681040717 * sin(0.0178674832556871 * doy - 0.167936777524864);
|
||||
}
|
||||
|
||||
int dayOfYear(int year, int month, int day) {
|
||||
// Algorithm borrowed from DateToOrdinal by Ritchie Lawrence, www.commandline.co.uk
|
||||
int z = 14 - month;
|
||||
z /= 12;
|
||||
int y = year + 4800 - z;
|
||||
int m = month + 12 * z - 3;
|
||||
int j = 153 * m + 2;
|
||||
j = j / 5 + day + y * 365 + y / 4 - y / 100 + y / 400 - 32045;
|
||||
y = year + 4799;
|
||||
int k = y * 365 + y / 4 - y / 100 + y / 400 - 31738;
|
||||
return j - k + 1;
|
||||
// Algorithm borrowed from DateToOrdinal by Ritchie Lawrence, www.commandline.co.uk
|
||||
int z = 14 - month;
|
||||
z /= 12;
|
||||
int y = year + 4800 - z;
|
||||
int m = month + 12 * z - 3;
|
||||
int j = 153 * m + 2;
|
||||
j = j / 5 + day + y * 365 + y / 4 - y / 100 + y / 400 - 32045;
|
||||
y = year + 4799;
|
||||
int k = y * 365 + y / 4 - y / 100 + y / 400 - 31738;
|
||||
return j - k + 1;
|
||||
}
|
||||
|
||||
void calcSunRiseAndSet() {
|
||||
int doy = dayOfYear(tm.Year, tm.Month, tm.Day);
|
||||
float eqt = equationOfTime(doy);
|
||||
float dec = sunDeclination(doy);
|
||||
float da = diurnalArc(dec, Settings.Latitude);
|
||||
float rise = 12 - da - eqt - Settings.Longitude / 15.0;
|
||||
float set = 12 + da - eqt - Settings.Longitude / 15.0;
|
||||
tsRise.Hour = (int)rise;
|
||||
tsRise.Minute = (rise - (int)rise) * 60.0;
|
||||
tsSet.Hour = (int)set;
|
||||
tsSet.Minute = (set - (int)set) * 60.0;
|
||||
tsRise.Day = tsSet.Day = tm.Day;
|
||||
tsRise.Month = tsSet.Month = tm.Month;
|
||||
tsRise.Year = tsSet.Year = tm.Year;
|
||||
int doy = dayOfYear(tm.tm_year, tm.tm_mon, tm.tm_mday);
|
||||
float eqt = equationOfTime(doy);
|
||||
float dec = sunDeclination(doy);
|
||||
float da = diurnalArc(dec, Settings.Latitude);
|
||||
float rise = 12 - da - eqt;
|
||||
float set = 12 + da - eqt;
|
||||
tsRise.tm_hour = (int)rise;
|
||||
tsRise.tm_min = (rise - (int)rise) * 60.0;
|
||||
tsSet.tm_hour = (int)set;
|
||||
tsSet.tm_min = (set - (int)set) * 60.0;
|
||||
tsRise.tm_mday = tsSet.tm_mday = tm.tm_mday;
|
||||
tsRise.tm_mon = tsSet.tm_mon = tm.tm_mon;
|
||||
tsRise.tm_year = tsSet.tm_year = tm.tm_year;
|
||||
// Now apply the longitude
|
||||
int secOffset_longitude = -1.0 * (Settings.Longitude / 15.0) * 3600;
|
||||
tsSet = addSeconds(tsSet, secOffset_longitude, false);
|
||||
tsRise = addSeconds(tsRise, secOffset_longitude, false);
|
||||
|
||||
breakTime(toLocal(makeTime(tsRise)), sunRise);
|
||||
breakTime(toLocal(makeTime(tsSet)), sunSet);
|
||||
}
|
||||
|
||||
timeStruct getSunRise(int secOffset) {
|
||||
return addSeconds(tsRise, secOffset, true);
|
||||
|
||||
struct tm getSunRise(int secOffset) {
|
||||
return addSeconds(tsRise, secOffset, true);
|
||||
}
|
||||
|
||||
timeStruct getSunSet(int secOffset) {
|
||||
return addSeconds(tsSet, secOffset, true);
|
||||
struct tm getSunSet(int secOffset) {
|
||||
return addSeconds(tsSet, secOffset, true);
|
||||
}
|
||||
|
||||
timeStruct addSeconds(const timeStruct& ts, int seconds, bool toLocalTime) {
|
||||
unsigned long time = makeTime(ts);
|
||||
time += seconds;
|
||||
if (toLocalTime) {
|
||||
time = toLocal(time);
|
||||
}
|
||||
timeStruct result;
|
||||
breakTime(time, result);
|
||||
return result;
|
||||
struct tm addSeconds(const struct tm& ts, int seconds, bool toLocalTime) {
|
||||
unsigned long time = makeTime(ts);
|
||||
time += seconds;
|
||||
if (toLocalTime) {
|
||||
time = toLocal(time);
|
||||
}
|
||||
struct tm result;
|
||||
breakTime(time, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void breakTime(unsigned long timeInput, struct timeStruct &tm) {
|
||||
void breakTime(unsigned long timeInput, struct tm &tm) {
|
||||
uint8_t year;
|
||||
uint8_t month, monthLength;
|
||||
uint32_t time;
|
||||
@@ -101,37 +108,37 @@ void breakTime(unsigned long timeInput, struct timeStruct &tm) {
|
||||
const uint8_t monthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
|
||||
time = (uint32_t)timeInput;
|
||||
tm.Second = time % 60;
|
||||
tm.tm_sec = time % 60;
|
||||
time /= 60; // now it is minutes
|
||||
tm.Minute = time % 60;
|
||||
tm.tm_min = time % 60;
|
||||
time /= 60; // now it is hours
|
||||
tm.Hour = time % 24;
|
||||
tm.tm_hour = time % 24;
|
||||
time /= 24; // now it is days
|
||||
tm.Wday = ((time + 4) % 7) + 1; // Sunday is day 1
|
||||
tm.tm_wday = ((time + 4) % 7) + 1; // Sunday is day 1
|
||||
|
||||
year = 0;
|
||||
days = 0;
|
||||
while ((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) {
|
||||
year++;
|
||||
}
|
||||
tm.Year = year; // year is offset from 1970
|
||||
tm.tm_year = year; // year is offset from 1970
|
||||
|
||||
days -= LEAP_YEAR(year) ? 366 : 365;
|
||||
time -= days; // now it is days in this year, starting at 0
|
||||
days -= LEAP_YEAR(year) ? 366 : 365;
|
||||
time -= days; // now it is days in this year, starting at 0
|
||||
|
||||
days = 0;
|
||||
month = 0;
|
||||
monthLength = 0;
|
||||
for (month = 0; month < 12; month++) {
|
||||
if (month == 1) { // february
|
||||
if (LEAP_YEAR(year)) {
|
||||
monthLength = 29;
|
||||
} else {
|
||||
monthLength = 28;
|
||||
}
|
||||
} else {
|
||||
monthLength = monthDays[month];
|
||||
}
|
||||
days = 0;
|
||||
month = 0;
|
||||
monthLength = 0;
|
||||
for (month = 0; month < 12; month++) {
|
||||
if (month == 1) { // february
|
||||
if (LEAP_YEAR(year)) {
|
||||
monthLength = 29;
|
||||
} else {
|
||||
monthLength = 28;
|
||||
}
|
||||
} else {
|
||||
monthLength = monthDays[month];
|
||||
}
|
||||
|
||||
if (time >= monthLength) {
|
||||
time -= monthLength;
|
||||
@@ -139,50 +146,37 @@ void breakTime(unsigned long timeInput, struct timeStruct &tm) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
tm.Month = month + 1; // jan is month 1
|
||||
tm.Day = time + 1; // day of month
|
||||
tm.tm_mon = month + 1; // jan is month 1
|
||||
tm.tm_mday = time + 1; // day of month
|
||||
}
|
||||
|
||||
void setTime(unsigned long t) {
|
||||
sysTime = (uint32_t)t;
|
||||
applyTimeZone(t);
|
||||
nextSyncTime = (uint32_t)t + syncInterval;
|
||||
prevMillis = millis(); // restart counting from now (thanks to Korman for this fix)
|
||||
if (Settings.UseRules)
|
||||
{
|
||||
static bool firstUpdate = true;
|
||||
String event = firstUpdate ? F("Time#Initialized") : F("Time#Set");
|
||||
firstUpdate = false;
|
||||
rulesProcessing(event);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t getUnixTime() {
|
||||
return sysTime;
|
||||
uint32_t getUnixTime()
|
||||
{
|
||||
return static_cast<uint32_t>(sysTime);
|
||||
}
|
||||
|
||||
int getSecOffset(const String& format) {
|
||||
int position_minus = format.indexOf('-');
|
||||
int position_plus = format.indexOf('+');
|
||||
if (position_minus == -1 && position_plus == -1)
|
||||
return 0;
|
||||
int sign_position = _max(position_minus, position_plus);
|
||||
int position_percent = format.indexOf('%', sign_position);
|
||||
if (position_percent == -1) {
|
||||
return 0;
|
||||
}
|
||||
String valueStr = getNumerical(format.substring(sign_position, position_percent), true);
|
||||
if (!isInt(valueStr)) return 0;
|
||||
int value = valueStr.toInt();
|
||||
switch (format.charAt(position_percent - 1)) {
|
||||
case 'm':
|
||||
case 'M':
|
||||
return value * 60;
|
||||
case 'h':
|
||||
case 'H':
|
||||
return value * 3600;
|
||||
}
|
||||
return value;
|
||||
int position_minus = format.indexOf('-');
|
||||
int position_plus = format.indexOf('+');
|
||||
if (position_minus == -1 && position_plus == -1)
|
||||
return 0;
|
||||
int sign_position = _max(position_minus, position_plus);
|
||||
int position_percent = format.indexOf('%', sign_position);
|
||||
if (position_percent == -1) {
|
||||
return 0;
|
||||
}
|
||||
String valueStr = getNumerical(format.substring(sign_position, position_percent), true);
|
||||
if (!isInt(valueStr)) return 0;
|
||||
int value = valueStr.toInt();
|
||||
switch (format.charAt(position_percent - 1)) {
|
||||
case 'm':
|
||||
case 'M':
|
||||
return value * 60;
|
||||
case 'h':
|
||||
case 'H':
|
||||
return value * 3600;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
String getSunriseTimeString(char delimiter) {
|
||||
@@ -194,14 +188,14 @@ String getSunsetTimeString(char delimiter) {
|
||||
}
|
||||
|
||||
String getSunriseTimeString(char delimiter, int secOffset) {
|
||||
if (secOffset == 0)
|
||||
return getSunriseTimeString(delimiter);
|
||||
if (secOffset == 0)
|
||||
return getSunriseTimeString(delimiter);
|
||||
return getTimeString(getSunRise(secOffset), delimiter, false, false);
|
||||
}
|
||||
|
||||
String getSunsetTimeString(char delimiter, int secOffset) {
|
||||
if (secOffset == 0)
|
||||
return getSunsetTimeString(delimiter);
|
||||
if (secOffset == 0)
|
||||
return getSunsetTimeString(delimiter);
|
||||
return getTimeString(getSunSet(secOffset), delimiter, false, false);
|
||||
}
|
||||
|
||||
@@ -210,95 +204,103 @@ unsigned long now() {
|
||||
// calculate number of seconds passed since last call to now()
|
||||
bool timeSynced = false;
|
||||
const long msec_passed = timePassedSince(prevMillis);
|
||||
const long seconds_passed = msec_passed / 1000;
|
||||
sysTime += seconds_passed;
|
||||
prevMillis += seconds_passed * 1000;
|
||||
if (nextSyncTime <= sysTime) {
|
||||
// nextSyncTime & sysTime are in seconds
|
||||
unsigned long t = getNtpTime();
|
||||
if (t != 0) {
|
||||
timeSynced = true;
|
||||
setTime(t);
|
||||
}
|
||||
}
|
||||
uint32_t localSystime = toLocal(sysTime);
|
||||
breakTime(localSystime, tm);
|
||||
if (timeSynced) {
|
||||
calcSunRiseAndSet();
|
||||
}
|
||||
sysTime += static_cast<double>(msec_passed) / 1000.0;
|
||||
prevMillis += msec_passed;
|
||||
if (nextSyncTime <= sysTime) {
|
||||
// nextSyncTime & sysTime are in seconds
|
||||
double unixTime_d;
|
||||
if (getNtpTime(unixTime_d)) {
|
||||
prevMillis = millis(); // restart counting from now (thanks to Korman for this fix)
|
||||
timeSynced = true;
|
||||
sysTime = unixTime_d;
|
||||
|
||||
applyTimeZone(unixTime_d);
|
||||
nextSyncTime = (uint32_t)unixTime_d + syncInterval;
|
||||
}
|
||||
}
|
||||
uint32_t localSystime = toLocal(sysTime);
|
||||
breakTime(localSystime, tm);
|
||||
if (timeSynced) {
|
||||
calcSunRiseAndSet();
|
||||
if (Settings.UseRules) {
|
||||
String event = statusNTPInitialized ? F("Time#Set") : F("Time#Initialized");
|
||||
rulesProcessing(event);
|
||||
}
|
||||
statusNTPInitialized = true; //@giig1967g: setting system variable %isntp%
|
||||
}
|
||||
return (unsigned long)localSystime;
|
||||
}
|
||||
|
||||
int year(unsigned long t) {
|
||||
timeStruct tmp;
|
||||
struct tm tmp;
|
||||
breakTime(t, tmp);
|
||||
return 1970 + tmp.Year;
|
||||
return 1970 + tmp.tm_year;
|
||||
}
|
||||
|
||||
int weekday(unsigned long t) {
|
||||
timeStruct tmp;
|
||||
struct tm tmp;
|
||||
breakTime(t, tmp);
|
||||
return tmp.Wday;
|
||||
return tmp.tm_wday;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int year()
|
||||
{
|
||||
return 1970 + tm.Year;
|
||||
return 1970 + tm.tm_year;
|
||||
}
|
||||
|
||||
byte month()
|
||||
{
|
||||
return tm.Month;
|
||||
return tm.tm_mon;
|
||||
}
|
||||
|
||||
byte day()
|
||||
{
|
||||
return tm.Day;
|
||||
return tm.tm_mday;
|
||||
}
|
||||
|
||||
byte hour()
|
||||
{
|
||||
return tm.Hour;
|
||||
return tm.tm_hour;
|
||||
}
|
||||
|
||||
byte minute()
|
||||
{
|
||||
return tm.Minute;
|
||||
return tm.tm_min;
|
||||
}
|
||||
|
||||
byte second()
|
||||
{
|
||||
return tm.Second;
|
||||
return tm.tm_sec;
|
||||
}
|
||||
|
||||
// day of week, sunday is day 1
|
||||
int weekday()
|
||||
{
|
||||
return tm.Wday;
|
||||
return tm.tm_wday;
|
||||
}
|
||||
|
||||
String weekday_str()
|
||||
{
|
||||
const int wday(weekday() - 1); // here: Count from Sunday = 0
|
||||
const String weekDays = F("SunMonTueWedThuFriSat");
|
||||
return weekDays.substring(wday * 3, wday * 3 + 3);
|
||||
const int wday(weekday() - 1); // here: Count from Sunday = 0
|
||||
const String weekDays = F("SunMonTueWedThuFriSat");
|
||||
return weekDays.substring(wday * 3, wday * 3 + 3);
|
||||
}
|
||||
|
||||
void initTime()
|
||||
{
|
||||
nextSyncTime = 0;
|
||||
now();
|
||||
nextSyncTime = 0;
|
||||
now();
|
||||
}
|
||||
|
||||
void checkTime()
|
||||
{
|
||||
now();
|
||||
if (tm.Minute != PrevMinutes)
|
||||
if (tm.tm_min != PrevMinutes)
|
||||
{
|
||||
PluginCall(PLUGIN_CLOCK_IN, 0, dummyString);
|
||||
PrevMinutes = tm.Minute;
|
||||
PrevMinutes = tm.tm_min;
|
||||
if (Settings.UseRules)
|
||||
{
|
||||
String event;
|
||||
@@ -319,50 +321,49 @@ void checkTime()
|
||||
}
|
||||
|
||||
|
||||
unsigned long getNtpTime()
|
||||
bool getNtpTime(double& unixTime_d)
|
||||
{
|
||||
if (!Settings.UseNTP || !WiFiConnected(10)) {
|
||||
return 0;
|
||||
}
|
||||
IPAddress timeServerIP;
|
||||
String log = F("NTP : NTP host ");
|
||||
if (Settings.NTPHost[0] != 0) {
|
||||
WiFi.hostByName(Settings.NTPHost, timeServerIP);
|
||||
log += Settings.NTPHost;
|
||||
// When single set host fails, retry again in a minute
|
||||
nextSyncTime = sysTime + 20;
|
||||
}
|
||||
else {
|
||||
// Have to do a lookup eacht time, since the NTP pool always returns another IP
|
||||
String ntpServerName = String(random(0, 3));
|
||||
ntpServerName += F(".pool.ntp.org");
|
||||
WiFi.hostByName(ntpServerName.c_str(), timeServerIP);
|
||||
log += ntpServerName;
|
||||
// When pool host fails, retry can be much sooner
|
||||
nextSyncTime = sysTime + 5;
|
||||
}
|
||||
if (!Settings.UseNTP || !WiFiConnected(10)) {
|
||||
return false;
|
||||
}
|
||||
IPAddress timeServerIP;
|
||||
String log = F("NTP : NTP host ");
|
||||
if (Settings.NTPHost[0] != 0) {
|
||||
WiFi.hostByName(Settings.NTPHost, timeServerIP);
|
||||
log += Settings.NTPHost;
|
||||
// When single set host fails, retry again in a minute
|
||||
nextSyncTime = sysTime + 20;
|
||||
}else {
|
||||
// Have to do a lookup eacht time, since the NTP pool always returns another IP
|
||||
String ntpServerName = String(random(0, 3));
|
||||
ntpServerName += F(".pool.ntp.org");
|
||||
WiFi.hostByName(ntpServerName.c_str(), timeServerIP);
|
||||
log += ntpServerName;
|
||||
// When pool host fails, retry can be much sooner
|
||||
nextSyncTime = sysTime + 5;
|
||||
}
|
||||
|
||||
log += F(" (");
|
||||
log += timeServerIP.toString();
|
||||
log += F(")");
|
||||
log += F(" (");
|
||||
log += timeServerIP.toString();
|
||||
log += F(")");
|
||||
|
||||
if (!hostReachable(timeServerIP)) {
|
||||
log += F(" unreachable");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
return 0;
|
||||
}
|
||||
if (!hostReachable(timeServerIP)) {
|
||||
log += F(" unreachable");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
return false;
|
||||
}
|
||||
|
||||
WiFiUDP udp;
|
||||
if (!beginWiFiUDP_randomPort(udp))
|
||||
return 0;
|
||||
if (!beginWiFiUDP_randomPort(udp))
|
||||
return 0;
|
||||
|
||||
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
|
||||
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
|
||||
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
|
||||
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
|
||||
|
||||
log += F(" queried");
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, log);
|
||||
log += F(" queried");
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, log);
|
||||
|
||||
while (udp.parsePacket() > 0) ; // discard any previously received packets
|
||||
while (udp.parsePacket() > 0) ; // discard any previously received packets
|
||||
|
||||
memset(packetBuffer, 0, NTP_PACKET_SIZE);
|
||||
packetBuffer[0] = 0b11100011; // LI, Version, Mode
|
||||
@@ -373,53 +374,89 @@ unsigned long getNtpTime()
|
||||
packetBuffer[13] = 0x4E;
|
||||
packetBuffer[14] = 49;
|
||||
packetBuffer[15] = 52;
|
||||
if (udp.beginPacket(timeServerIP, 123) == 0) { //NTP requests are to port 123
|
||||
udp.stop();
|
||||
return 0;
|
||||
}
|
||||
if (udp.beginPacket(timeServerIP, 123) == 0) { //NTP requests are to port 123
|
||||
udp.stop();
|
||||
return 0;
|
||||
}
|
||||
udp.write(packetBuffer, NTP_PACKET_SIZE);
|
||||
udp.endPacket();
|
||||
|
||||
uint32_t beginWait = millis();
|
||||
while (!timeOutReached(beginWait + 1000)) {
|
||||
int size = udp.parsePacket();
|
||||
|
||||
|
||||
uint32_t beginWait = millis();
|
||||
while (!timeOutReached(beginWait + 1000)) {
|
||||
int size = udp.parsePacket();
|
||||
int remotePort = udp.remotePort();
|
||||
if (size >= NTP_PACKET_SIZE && remotePort == 123) {
|
||||
udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
|
||||
unsigned long secsSince1900;
|
||||
// convert four bytes starting at location 40 to a long integer
|
||||
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[43];
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
|
||||
String log = F("NTP : NTP replied: ");
|
||||
log += timePassedSince(beginWait);
|
||||
log += F(" mSec");
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, log);
|
||||
if (size >= NTP_PACKET_SIZE && remotePort == 123) {
|
||||
udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
|
||||
|
||||
// For more detailed info on improving accuracy, see:
|
||||
// https://github.com/lettier/ntpclient/issues/4#issuecomment-360703503
|
||||
// For now, we simply use half the reply time as delay compensation.
|
||||
|
||||
unsigned long secsSince1900;
|
||||
// convert four bytes starting at location 40 to a long integer
|
||||
// TX time is used here.
|
||||
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[43];
|
||||
uint32_t txTm = secsSince1900 - 2208988800UL;
|
||||
|
||||
unsigned long txTm_f;
|
||||
txTm_f = (unsigned long)packetBuffer[44] << 24;
|
||||
txTm_f |= (unsigned long)packetBuffer[45] << 16;
|
||||
txTm_f |= (unsigned long)packetBuffer[46] << 8;
|
||||
txTm_f |= (unsigned long)packetBuffer[47];
|
||||
// Convert seconds to double
|
||||
unixTime_d = static_cast<double>(txTm);
|
||||
// Add fractional part.
|
||||
unixTime_d += (static_cast<double>(txTm_f) / 4294967295.0);
|
||||
|
||||
long total_delay = timePassedSince(beginWait);
|
||||
// compensate for the delay by adding half the total delay
|
||||
// N.B. unixTime_d is in seconds and delay in msec.
|
||||
double delay_compensation = static_cast<double>(total_delay) / 2000.0;
|
||||
unixTime_d += delay_compensation;
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log = F("NTP : NTP replied: delay ");
|
||||
log += total_delay;
|
||||
log += F(" mSec");
|
||||
log += F(" Accuracy increased by ");
|
||||
double fractpart, intpart;
|
||||
fractpart = modf(unixTime_d, &intpart);
|
||||
if (fractpart < delay_compensation) {
|
||||
// We gained more than 1 second in accuracy
|
||||
fractpart += 1.0;
|
||||
}
|
||||
log += String(fractpart, 3);
|
||||
log += F(" seconds");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
udp.stop();
|
||||
return secsSince1900 - 2208988800UL;
|
||||
}
|
||||
delay(10);
|
||||
}
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, F("NTP : No reply"));
|
||||
|
||||
return true;
|
||||
}
|
||||
delay(10);
|
||||
}
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, F("NTP : No reply"));
|
||||
udp.stop();
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************************************************\
|
||||
Unsigned long Timer timeOut check
|
||||
\*********************************************************************************************/
|
||||
Unsigned long Timer timeOut check
|
||||
\*********************************************************************************************/
|
||||
|
||||
// Return the time difference as a signed value, taking into account the timers may overflow.
|
||||
// Returned timediff is between -24.9 days and +24.9 days.
|
||||
// Returned value is positive when "next" is after "prev"
|
||||
long timeDiff(const unsigned long prev, const unsigned long next)
|
||||
{
|
||||
unsigned long start = ESP.getCycleCount();
|
||||
unsigned long start = ESP.getCycleCount();
|
||||
long signed_diff = 0;
|
||||
// To cast a value to a signed long, the difference may not exceed half the ULONG_MAX
|
||||
const unsigned long half_max_unsigned_long = 2147483647u; // = 2^31 -1
|
||||
@@ -446,11 +483,11 @@ long timeDiff(const unsigned long prev, const unsigned long next)
|
||||
signed_diff = static_cast<long>((ULONG_MAX - prev) + next + 1u);
|
||||
}
|
||||
}
|
||||
unsigned long end = ESP.getCycleCount();
|
||||
if (end > start) {
|
||||
++timediff_calls;
|
||||
timediff_cpu_cycles_total += (end - start);
|
||||
}
|
||||
unsigned long end = ESP.getCycleCount();
|
||||
if (end > start) {
|
||||
++timediff_calls;
|
||||
timediff_cpu_cycles_total += (end - start);
|
||||
}
|
||||
return signed_diff;
|
||||
}
|
||||
|
||||
@@ -479,115 +516,118 @@ void setNextTimeInterval(unsigned long& timer, const unsigned long step) {
|
||||
timer += step;
|
||||
const long passed = timePassedSince(timer);
|
||||
if (passed < 0) {
|
||||
// Event has not yet happened, which is fine.
|
||||
return;
|
||||
}
|
||||
if (static_cast<unsigned long>(passed) > step) {
|
||||
// No need to keep running behind, start again.
|
||||
timer = millis() + step;
|
||||
return;
|
||||
}
|
||||
// Try to get in sync again.
|
||||
timer = millis() + (step - passed);
|
||||
// Event has not yet happened, which is fine.
|
||||
return;
|
||||
}
|
||||
if (static_cast<unsigned long>(passed) > step) {
|
||||
// No need to keep running behind, start again.
|
||||
timer = millis() + step;
|
||||
return;
|
||||
}
|
||||
// Try to get in sync again.
|
||||
timer = millis() + (step - passed);
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************\
|
||||
Convert a 32 bit integer into a string like "Sun,12:30"
|
||||
\*********************************************************************************************/
|
||||
Convert a 32 bit integer into a string like "Sun,12:30"
|
||||
\*********************************************************************************************/
|
||||
String timeLong2String(unsigned long lngTime)
|
||||
{
|
||||
unsigned long x = 0;
|
||||
String time = "";
|
||||
unsigned long x = 0;
|
||||
String time = "";
|
||||
|
||||
x = (lngTime >> 16) & 0xf;
|
||||
if (x == 0x0f)
|
||||
x = 0;
|
||||
String weekDays = F("AllSunMonTueWedThuFriSatWrkWkd");
|
||||
time = weekDays.substring(x * 3, x * 3 + 3);
|
||||
time += ",";
|
||||
x = (lngTime >> 16) & 0xf;
|
||||
if (x == 0x0f)
|
||||
x = 0;
|
||||
String weekDays = F("AllSunMonTueWedThuFriSatWrkWkd");
|
||||
time = weekDays.substring(x * 3, x * 3 + 3);
|
||||
time += ",";
|
||||
|
||||
x = (lngTime >> 12) & 0xf;
|
||||
if (x == 0xf)
|
||||
time += "*";
|
||||
else if (x == 0xe)
|
||||
time += "-";
|
||||
else
|
||||
time += x;
|
||||
x = (lngTime >> 12) & 0xf;
|
||||
if (x == 0xf)
|
||||
time += "*";
|
||||
else if (x == 0xe)
|
||||
time += "-";
|
||||
else
|
||||
time += x;
|
||||
|
||||
x = (lngTime >> 8) & 0xf;
|
||||
if (x == 0xf)
|
||||
time += "*";
|
||||
else if (x == 0xe)
|
||||
time += "-";
|
||||
else
|
||||
time += x;
|
||||
x = (lngTime >> 8) & 0xf;
|
||||
if (x == 0xf)
|
||||
time += "*";
|
||||
else if (x == 0xe)
|
||||
time += "-";
|
||||
else
|
||||
time += x;
|
||||
|
||||
time += ":";
|
||||
time += ":";
|
||||
|
||||
x = (lngTime >> 4) & 0xf;
|
||||
if (x == 0xf)
|
||||
time += "*";
|
||||
else if (x == 0xe)
|
||||
time += "-";
|
||||
else
|
||||
time += x;
|
||||
x = (lngTime >> 4) & 0xf;
|
||||
if (x == 0xf)
|
||||
time += "*";
|
||||
else if (x == 0xe)
|
||||
time += "-";
|
||||
else
|
||||
time += x;
|
||||
|
||||
x = (lngTime) & 0xf;
|
||||
if (x == 0xf)
|
||||
time += "*";
|
||||
else if (x == 0xe)
|
||||
time += "-";
|
||||
else
|
||||
time += x;
|
||||
x = (lngTime) & 0xf;
|
||||
if (x == 0xf)
|
||||
time += "*";
|
||||
else if (x == 0xe)
|
||||
time += "-";
|
||||
else
|
||||
time += x;
|
||||
|
||||
return time;
|
||||
return time;
|
||||
}
|
||||
|
||||
// returns the current Date separated by the given delimiter
|
||||
// date format example with '-' delimiter: 2016-12-31 (YYYY-MM-DD)
|
||||
String getDateString(const timeStruct& ts, char delimiter) {
|
||||
String getDateString(const struct tm& ts, char delimiter) {
|
||||
char DateString[20]; //19 digits plus the null char
|
||||
const int year = 1970 + ts.Year;
|
||||
sprintf_P(DateString, PSTR("%4d%c%02d%c%02d"), year, delimiter, ts.Month, delimiter, ts.Day);
|
||||
const int year = 1970 + ts.tm_year;
|
||||
sprintf_P(DateString, PSTR("%4d%c%02d%c%02d"), year, delimiter, ts.tm_mon, delimiter, ts.tm_mday);
|
||||
return DateString;
|
||||
}
|
||||
|
||||
String getDateString(char delimiter)
|
||||
{
|
||||
return getDateString(tm, delimiter);
|
||||
return getDateString(tm, delimiter);
|
||||
}
|
||||
String getDateString(const struct tm& ts)
|
||||
{
|
||||
return getDateString(tm, ':');
|
||||
}
|
||||
|
||||
// returns the current Date without delimiter
|
||||
// date format example: 20161231 (YYYYMMDD)
|
||||
String getDateString()
|
||||
{
|
||||
return getDateString('\0');
|
||||
return getDateString('\0');
|
||||
}
|
||||
|
||||
// returns the current Time separated by the given delimiter
|
||||
// time format example with ':' delimiter: 23:59:59 (HH:MM:SS)
|
||||
String getTimeString(const timeStruct& ts, char delimiter, bool am_pm, bool show_seconds)
|
||||
String getTimeString(const struct tm& ts, char delimiter, bool am_pm, bool show_seconds)
|
||||
{
|
||||
char TimeString[20]; //19 digits plus the null char
|
||||
if (am_pm) {
|
||||
uint8_t hour(ts.Hour % 12);
|
||||
uint8_t hour(ts.tm_hour % 12);
|
||||
if (hour == 0) { hour = 12; }
|
||||
const char a_or_p = ts.Hour < 12 ? 'A' : 'P';
|
||||
const char a_or_p = ts.tm_hour < 12 ? 'A' : 'P';
|
||||
if (show_seconds) {
|
||||
sprintf_P(TimeString, PSTR("%d%c%02d%c%02d %cM"),
|
||||
hour, delimiter, ts.Minute, delimiter, ts.Second, a_or_p);
|
||||
hour, delimiter, ts.tm_min, delimiter, ts.tm_sec, a_or_p);
|
||||
} else {
|
||||
sprintf_P(TimeString, PSTR("%d%c%02d %cM"),
|
||||
hour, delimiter, ts.Minute, a_or_p);
|
||||
hour, delimiter, ts.tm_min, a_or_p);
|
||||
}
|
||||
} else {
|
||||
if (show_seconds) {
|
||||
sprintf_P(TimeString, PSTR("%02d%c%02d%c%02d"),
|
||||
ts.Hour, delimiter, ts.Minute, delimiter, ts.Second);
|
||||
ts.tm_hour, delimiter, ts.tm_min, delimiter, ts.tm_sec);
|
||||
} else {
|
||||
sprintf_P(TimeString, PSTR("%d%c%02d"),
|
||||
ts.Hour, delimiter, ts.Minute);
|
||||
ts.tm_hour, delimiter, ts.tm_min);
|
||||
}
|
||||
}
|
||||
return TimeString;
|
||||
@@ -595,36 +635,41 @@ String getTimeString(const timeStruct& ts, char delimiter, bool am_pm, bool show
|
||||
|
||||
String getTimeString(char delimiter, bool show_seconds /*=true*/)
|
||||
{
|
||||
return getTimeString(tm, delimiter, false, show_seconds);
|
||||
return getTimeString(tm, delimiter, false, show_seconds);
|
||||
}
|
||||
|
||||
String getTimeString_ampm(char delimiter, bool show_seconds /*=true*/)
|
||||
{
|
||||
return getTimeString(tm, delimiter, true, show_seconds);
|
||||
return getTimeString(tm, delimiter, true, show_seconds);
|
||||
}
|
||||
|
||||
// returns the current Time without delimiter
|
||||
// time format example: 235959 (HHMMSS)
|
||||
String getTimeString()
|
||||
{
|
||||
return getTimeString('\0');
|
||||
return getTimeString('\0');
|
||||
}
|
||||
|
||||
String getTimeString_ampm()
|
||||
{
|
||||
return getTimeString_ampm('\0');
|
||||
return getTimeString_ampm('\0');
|
||||
}
|
||||
|
||||
// returns the current Date and Time separated by the given delimiter
|
||||
// if called like this: getDateTimeString('\0', '\0', '\0');
|
||||
// it will give back this: 20161231235959 (YYYYMMDDHHMMSS)
|
||||
String getDateTimeString(const timeStruct& ts, char dateDelimiter, char timeDelimiter, char dateTimeDelimiter, bool am_pm)
|
||||
String getDateTimeString(const struct tm& ts, char dateDelimiter, char timeDelimiter, char dateTimeDelimiter, bool am_pm)
|
||||
{
|
||||
String ret = getDateString(ts, dateDelimiter);
|
||||
if (dateTimeDelimiter != '\0')
|
||||
ret += dateTimeDelimiter;
|
||||
ret += getTimeString(ts, timeDelimiter, am_pm, true);
|
||||
return ret;
|
||||
String ret = getDateString(ts, dateDelimiter);
|
||||
if (dateTimeDelimiter != '\0')
|
||||
ret += dateTimeDelimiter;
|
||||
ret += getTimeString(ts, timeDelimiter, am_pm, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
String getDateTimeString(const struct tm& ts)
|
||||
{
|
||||
return getDateTimeString(ts,'-', ':', ' ', false);
|
||||
}
|
||||
|
||||
String getDateTimeString(char dateDelimiter, char timeDelimiter, char dateTimeDelimiter) {
|
||||
@@ -636,14 +681,14 @@ String getDateTimeString_ampm(char dateDelimiter, char timeDelimiter, char date
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
Convert a string like "Sun,12:30" into a 32 bit integer
|
||||
\*********************************************************************************************/
|
||||
Convert a string like "Sun,12:30" into a 32 bit integer
|
||||
\*********************************************************************************************/
|
||||
unsigned long string2TimeLong(const String &str)
|
||||
{
|
||||
// format 0000WWWWAAAABBBBCCCCDDDD
|
||||
// WWWW=weekday, AAAA=hours tens digit, BBBB=hours, CCCC=minutes tens digit DDDD=minutes
|
||||
// format 0000WWWWAAAABBBBCCCCDDDD
|
||||
// WWWW=weekday, AAAA=hours tens digit, BBBB=hours, CCCC=minutes tens digit DDDD=minutes
|
||||
|
||||
#define TmpStr1Length 10
|
||||
#define TmpStr1Length 10
|
||||
char command[20];
|
||||
char TmpStr1[TmpStr1Length];
|
||||
int w, x, y;
|
||||
@@ -661,8 +706,8 @@ unsigned long string2TimeLong(const String &str)
|
||||
String day = TmpStr1;
|
||||
String weekDays = F("allsunmontuewedthufrisatwrkwkd");
|
||||
y = weekDays.indexOf(TmpStr1) / 3;
|
||||
if (y == 0)
|
||||
y = 0xf; // wildcard is 0xf
|
||||
if (y == 0)
|
||||
y = 0xf; // wildcard is 0xf
|
||||
lngTime |= (unsigned long)y << 16;
|
||||
}
|
||||
|
||||
@@ -690,13 +735,13 @@ unsigned long string2TimeLong(const String &str)
|
||||
}
|
||||
}
|
||||
}
|
||||
#undef TmpStr1Length
|
||||
#undef TmpStr1Length
|
||||
return lngTime;
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
Match clock event
|
||||
\*********************************************************************************************/
|
||||
Match clock event
|
||||
\*********************************************************************************************/
|
||||
boolean matchClockEvent(unsigned long clockEvent, unsigned long clockSet)
|
||||
{
|
||||
unsigned long Mask;
|
||||
@@ -727,6 +772,6 @@ boolean matchClockEvent(unsigned long clockEvent, unsigned long clockSet)
|
||||
}
|
||||
|
||||
if (clockEvent == clockSet)
|
||||
return true;
|
||||
return false;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
+19
-19
@@ -1,4 +1,4 @@
|
||||
|
||||
#include <time.h>
|
||||
/********************************************************************************************\
|
||||
Time zone
|
||||
\*********************************************************************************************/
|
||||
@@ -95,7 +95,7 @@ void logTimeZoneInfo() {
|
||||
// Summer time
|
||||
log += F(" DST time start: ");
|
||||
if (m_dstLoc != 0) {
|
||||
timeStruct tmp;
|
||||
struct tm tmp;
|
||||
breakTime(m_dstLoc, tmp);
|
||||
log += getDateTimeString(tmp, '-', ':', ' ', false);
|
||||
}
|
||||
@@ -106,7 +106,7 @@ void logTimeZoneInfo() {
|
||||
// Standard/Winter time.
|
||||
log += F("STD time start: ");
|
||||
if (m_stdLoc != 0) {
|
||||
timeStruct tmp;
|
||||
struct tm tmp;
|
||||
breakTime(m_stdLoc, tmp);
|
||||
log += getDateTimeString(tmp, '-', ':', ' ', false);
|
||||
}
|
||||
@@ -116,7 +116,7 @@ void logTimeZoneInfo() {
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
|
||||
uint32_t makeTime(const timeStruct &tm) {
|
||||
uint32_t makeTime(const struct tm &tm) {
|
||||
// assemble time elements into uint32_t
|
||||
// note year argument is offset from 1970 (see macros in time.h to convert to other formats)
|
||||
// previous version used full four digit year (or digits since 2000),i.e. 2009 was 2009 or 9
|
||||
@@ -125,25 +125,25 @@ uint32_t makeTime(const timeStruct &tm) {
|
||||
uint32_t seconds;
|
||||
|
||||
// seconds from 1970 till 1 jan 00:00:00 of the given year
|
||||
seconds= tm.Year*(SECS_PER_DAY * 365);
|
||||
for (i = 0; i < tm.Year; i++) {
|
||||
seconds= tm.tm_year*(SECS_PER_DAY * 365);
|
||||
for (i = 0; i < tm.tm_year; i++) {
|
||||
if (LEAP_YEAR(i)) {
|
||||
seconds += SECS_PER_DAY; // add extra days for leap years
|
||||
}
|
||||
}
|
||||
|
||||
// add days for this year, months start from 1
|
||||
for (i = 1; i < tm.Month; i++) {
|
||||
if ( (i == 2) && LEAP_YEAR(tm.Year)) {
|
||||
for (i = 1; i < tm.tm_mon; i++) {
|
||||
if ( (i == 2) && LEAP_YEAR(tm.tm_year)) {
|
||||
seconds += SECS_PER_DAY * 29;
|
||||
} else {
|
||||
seconds += SECS_PER_DAY * monthDays[i-1]; //monthDay array starts from 0
|
||||
}
|
||||
}
|
||||
seconds+= (tm.Day-1) * SECS_PER_DAY;
|
||||
seconds+= tm.Hour * SECS_PER_HOUR;
|
||||
seconds+= tm.Minute * SECS_PER_MIN;
|
||||
seconds+= tm.Second;
|
||||
seconds+= (tm.tm_mday-1) * SECS_PER_DAY;
|
||||
seconds+= tm.tm_hour * SECS_PER_HOUR;
|
||||
seconds+= tm.tm_min * SECS_PER_MIN;
|
||||
seconds+= tm.tm_sec;
|
||||
return (uint32_t)seconds;
|
||||
}
|
||||
|
||||
@@ -167,13 +167,13 @@ uint32_t calcTimeChangeForRule(const TimeChangeRule& r, int yr)
|
||||
}
|
||||
|
||||
// calculate first day of the month, or for "Last" rules, first day of the next month
|
||||
timeStruct tm;
|
||||
tm.Hour = r.hour;
|
||||
tm.Minute = 0;
|
||||
tm.Second = 0;
|
||||
tm.Day = 1;
|
||||
tm.Month = m;
|
||||
tm.Year = yr - 1970;
|
||||
struct tm tm;
|
||||
tm.tm_hour = r.hour;
|
||||
tm.tm_min = 0;
|
||||
tm.tm_sec = 0;
|
||||
tm.tm_mday = 1;
|
||||
tm.tm_mon = m;
|
||||
tm.tm_year = yr - 1970;
|
||||
uint32_t t = makeTime(tm);
|
||||
|
||||
// add offset from the first of the month to r.dow, and offset for the given week
|
||||
|
||||
+469
-298
File diff suppressed because it is too large
Load Diff
+7
-3
@@ -237,7 +237,7 @@ static const char pgDefaultCSS[] PROGMEM = {
|
||||
"table.multirow tr:nth-child(even){background-color: #DEE6FF; }"
|
||||
"table.multirow {color: #000; width: 100%; min-width: 420px; border-collapse: collapse; }"
|
||||
// Highlight row
|
||||
"tr.highlight td { background-color: #dbff0075; }"
|
||||
"tr.highlight td { background-color: #dbff0075; }"
|
||||
// inside a form
|
||||
".note {color: #444; font-style: italic; }"
|
||||
//header with title and menu
|
||||
@@ -415,8 +415,12 @@ static const char jsUpdateSensorValuesDevicePage[] PROGMEM = {
|
||||
"tempValue = data.Sensors[c].TaskValues[k].Value;"
|
||||
"decimalsValue = data.Sensors[c].TaskValues[k].NrDecimals;"
|
||||
"tempValue = parseFloat(tempValue).toFixed(decimalsValue);"
|
||||
"document.getElementById('value_' + (data.Sensors[c].TaskNumber - 1) + '_' + (data.Sensors[c].TaskValues[k].ValueNumber - 1)).innerHTML = tempValue;"
|
||||
"document.getElementById('valuename_' + (data.Sensors[c].TaskNumber - 1) + '_' + (data.Sensors[c].TaskValues[k].ValueNumber - 1)).innerHTML = data.Sensors[c].TaskValues[k].Name + ':';"
|
||||
"var valueID = 'value_' + (data.Sensors[c].TaskNumber - 1) + '_' + (data.Sensors[c].TaskValues[k].ValueNumber - 1);"
|
||||
"var valueNameID = 'valuename_' + (data.Sensors[c].TaskNumber - 1) + '_' + (data.Sensors[c].TaskValues[k].ValueNumber - 1);"
|
||||
"var valueElement = document.getElementById(valueID);"
|
||||
"var valueNameElement = document.getElementById(valueNameID);"
|
||||
"if (valueElement !== null) { valueElement.innerHTML = tempValue; }"
|
||||
"if (valueNameElement !== null) { valueNameElement.innerHTML = data.Sensors[c].TaskValues[k].Name + ':'; }"
|
||||
"}"
|
||||
"}"
|
||||
"}"
|
||||
|
||||
+3
-3
@@ -33,8 +33,8 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case CPLUGIN_INIT:
|
||||
{
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
|
||||
C001_DelayHandler.configureControllerSettings(ControllerSettings);
|
||||
break;
|
||||
}
|
||||
@@ -118,7 +118,7 @@ bool do_process_c001_delay_queue(int controller_number, const C001_queue_element
|
||||
String request = create_http_request_auth(controller_number, element.controller_idx, ControllerSettings, F("GET"), element.txt);
|
||||
|
||||
addLog(LOG_LEVEL_DEBUG, element.txt);
|
||||
return send_via_http(controller_number, client, request);
|
||||
return send_via_http(controller_number, client, request, ControllerSettings.MustCheckReply);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+4
-4
@@ -35,8 +35,8 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case CPLUGIN_INIT:
|
||||
{
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
|
||||
MQTTDelayHandler.configureControllerSettings(ControllerSettings);
|
||||
break;
|
||||
}
|
||||
@@ -145,8 +145,8 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
if (event->idx != 0)
|
||||
{
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
|
||||
/*
|
||||
if (!ControllerSettings.checkHostReachable(true)) {
|
||||
success = false;
|
||||
|
||||
+4
-4
@@ -32,8 +32,8 @@ boolean CPlugin_003(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case CPLUGIN_INIT:
|
||||
{
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
|
||||
C003_DelayHandler.configureControllerSettings(ControllerSettings);
|
||||
break;
|
||||
}
|
||||
@@ -78,7 +78,7 @@ bool do_process_c003_delay_queue(int controller_number, const C003_queue_element
|
||||
client.print(" \n");
|
||||
|
||||
unsigned long timer = millis() + 200;
|
||||
while (!client.available() && !timeOutReached(timer))
|
||||
while (!client_available(client) && !timeOutReached(timer))
|
||||
delay(1);
|
||||
|
||||
timer = millis() + 1000;
|
||||
@@ -102,7 +102,7 @@ bool do_process_c003_delay_queue(int controller_number, const C003_queue_element
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
client.println(SecuritySettings.ControllerPassword[element.controller_idx]);
|
||||
delay(100);
|
||||
while (client.available())
|
||||
while (client_available(client))
|
||||
client.read();
|
||||
|
||||
strcpy_P(log, PSTR("TELNT: Sending cmd"));
|
||||
|
||||
+3
-3
@@ -32,8 +32,8 @@ boolean CPlugin_004(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case CPLUGIN_INIT:
|
||||
{
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
|
||||
C004_DelayHandler.configureControllerSettings(ControllerSettings);
|
||||
break;
|
||||
}
|
||||
@@ -94,6 +94,6 @@ bool do_process_c004_delay_queue(int controller_number, const C004_queue_element
|
||||
postDataStr.length());
|
||||
postStr += postDataStr;
|
||||
|
||||
return send_via_http(controller_number, client, postStr);
|
||||
return send_via_http(controller_number, client, postStr, ControllerSettings.MustCheckReply);
|
||||
}
|
||||
#endif
|
||||
|
||||
+5
-5
@@ -33,8 +33,8 @@ boolean CPlugin_005(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case CPLUGIN_INIT:
|
||||
{
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
|
||||
MQTTDelayHandler.configureControllerSettings(ControllerSettings);
|
||||
break;
|
||||
}
|
||||
@@ -92,15 +92,15 @@ boolean CPlugin_005(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case CPLUGIN_PROTOCOL_SEND:
|
||||
{
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
|
||||
if (!ControllerSettings.checkHostReachable(true)) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
statusLED(true);
|
||||
|
||||
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
|
||||
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
|
||||
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
|
||||
|
||||
String pubname = ControllerSettings.Publish;
|
||||
|
||||
+5
-5
@@ -33,8 +33,8 @@ boolean CPlugin_006(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case CPLUGIN_INIT:
|
||||
{
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
|
||||
MQTTDelayHandler.configureControllerSettings(ControllerSettings);
|
||||
break;
|
||||
}
|
||||
@@ -90,12 +90,12 @@ boolean CPlugin_006(byte function, struct EventStruct *event, String& string)
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
|
||||
|
||||
statusLED(true);
|
||||
|
||||
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
|
||||
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
|
||||
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
|
||||
|
||||
String pubname = ControllerSettings.Publish;
|
||||
|
||||
+4
-3
@@ -32,8 +32,8 @@ boolean CPlugin_007(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case CPLUGIN_INIT:
|
||||
{
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
|
||||
C007_DelayHandler.configureControllerSettings(ControllerSettings);
|
||||
break;
|
||||
}
|
||||
@@ -78,6 +78,7 @@ bool do_process_c007_delay_queue(int controller_number, const C007_queue_element
|
||||
Serial.println(url);
|
||||
|
||||
return send_via_http(controller_number, client,
|
||||
create_http_get_request(controller_number, ControllerSettings, url));
|
||||
create_http_get_request(controller_number, ControllerSettings, url),
|
||||
ControllerSettings.MustCheckReply);
|
||||
}
|
||||
#endif
|
||||
|
||||
+6
-6
@@ -34,8 +34,8 @@ boolean CPlugin_008(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case CPLUGIN_INIT:
|
||||
{
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
|
||||
C008_DelayHandler.configureControllerSettings(ControllerSettings);
|
||||
break;
|
||||
}
|
||||
@@ -52,11 +52,11 @@ boolean CPlugin_008(byte function, struct EventStruct *event, String& string)
|
||||
// Collect the values at the same run, to make sure all are from the same sample
|
||||
byte valueCount = getValueCountFromSensorType(event->sensorType);
|
||||
C008_queue_element element(event, valueCount);
|
||||
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
|
||||
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
|
||||
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
|
||||
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
|
||||
|
||||
for (byte x = 0; x < valueCount; x++)
|
||||
{
|
||||
@@ -97,7 +97,7 @@ bool do_process_c008_delay_queue(int controller_number, const C008_queue_element
|
||||
return false;
|
||||
|
||||
String request = create_http_request_auth(controller_number, element.controller_idx, ControllerSettings, F("GET"), element.txt[element.valuesSent]);
|
||||
return element.checkDone(send_via_http(controller_number, client, request));
|
||||
return element.checkDone(send_via_http(controller_number, client, request, ControllerSettings.MustCheckReply));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+58
-54
@@ -24,7 +24,7 @@
|
||||
- removed obsolete http get url code
|
||||
- v1.04
|
||||
- added build options and node_type_id to JSON/device
|
||||
/******************************************************************************/
|
||||
******************************************************************************/
|
||||
|
||||
#define CPLUGIN_009
|
||||
#define CPLUGIN_ID_009 9
|
||||
@@ -57,60 +57,17 @@ boolean CPlugin_009(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case CPLUGIN_PROTOCOL_SEND:
|
||||
{
|
||||
if (!WiFiConnected(100)) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
|
||||
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
|
||||
|
||||
// We now create a URI for the request
|
||||
String url = F("/ESPEasy");
|
||||
|
||||
// Create json root object
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject& root = jsonBuffer.createObject();
|
||||
root[F("module")] = String(F("ESPEasy"));
|
||||
root[F("version")] = String(F("1.04"));
|
||||
|
||||
// Create nested objects
|
||||
JsonObject& data = root.createNestedObject(String(F("data")));
|
||||
JsonObject& ESP = data.createNestedObject(String(F("ESP")));
|
||||
ESP[F("name")] = Settings.Name;
|
||||
ESP[F("unit")] = Settings.Unit;
|
||||
ESP[F("version")] = Settings.Version;
|
||||
ESP[F("build")] = Settings.Build;
|
||||
ESP[F("build_notes")] = String(F(BUILD_NOTES));
|
||||
ESP[F("build_git")] = String(F(BUILD_GIT));
|
||||
ESP[F("node_type_id")] = NODE_TYPE_ID;
|
||||
ESP[F("sleep")] = Settings.deepSleep;
|
||||
|
||||
// embed IP, important if there is NAT/PAT
|
||||
// char ipStr[20];
|
||||
// IPAddress ip = WiFi.localIP();
|
||||
// sprintf_P(ipStr, PSTR("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]);
|
||||
ESP[F("ip")] = WiFi.localIP().toString();
|
||||
|
||||
// Create nested SENSOR json object
|
||||
JsonObject& SENSOR = data.createNestedObject(String(F("SENSOR")));
|
||||
byte valueCount = getValueCountFromSensorType(event->sensorType);
|
||||
// char itemNames[valueCount][2];
|
||||
C009_queue_element element(event);
|
||||
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
|
||||
|
||||
for (byte x = 0; x < valueCount; x++)
|
||||
{
|
||||
// Each sensor value get an own object (0..n)
|
||||
// sprintf(itemNames[x],"%d",x);
|
||||
JsonObject& val = SENSOR.createNestedObject(String(x));
|
||||
val[F("deviceName")] = getTaskDeviceName(event->TaskIndex);
|
||||
val[F("valueName")] = ExtraTaskSettings.TaskDeviceValueNames[x];
|
||||
val[F("type")] = event->sensorType;
|
||||
val[F("value")] = formatUserVarNoCheck(event, x);
|
||||
element.txt[x] = formatUserVarNoCheck(event, x);
|
||||
}
|
||||
|
||||
// Create json buffer
|
||||
String jsonString;
|
||||
root.printTo(jsonString);
|
||||
|
||||
success = C009_DelayHandler.addToQueue(C009_queue_element(event->ControllerIndex, url, jsonString));
|
||||
success = C009_DelayHandler.addToQueue(element);
|
||||
scheduleNextDelayQueue(TIMER_C009_DELAY_QUEUE, C009_DelayHandler.getNextScheduleTime());
|
||||
break;
|
||||
}
|
||||
@@ -126,11 +83,58 @@ bool do_process_c009_delay_queue(int controller_number, const C009_queue_element
|
||||
if (!try_connect_host(controller_number, client, ControllerSettings))
|
||||
return false;
|
||||
|
||||
LoadTaskSettings(element.TaskIndex);
|
||||
String jsonString;
|
||||
{
|
||||
// Create json root object
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject& root = jsonBuffer.createObject();
|
||||
root[F("module")] = String(F("ESPEasy"));
|
||||
root[F("version")] = String(F("1.04"));
|
||||
|
||||
// Create nested objects
|
||||
JsonObject& data = root.createNestedObject(String(F("data")));
|
||||
JsonObject& ESP = data.createNestedObject(String(F("ESP")));
|
||||
ESP[F("name")] = Settings.Name;
|
||||
ESP[F("unit")] = Settings.Unit;
|
||||
ESP[F("version")] = Settings.Version;
|
||||
ESP[F("build")] = Settings.Build;
|
||||
ESP[F("build_notes")] = String(F(BUILD_NOTES));
|
||||
ESP[F("build_git")] = String(F(BUILD_GIT));
|
||||
ESP[F("node_type_id")] = NODE_TYPE_ID;
|
||||
ESP[F("sleep")] = Settings.deepSleep;
|
||||
|
||||
// embed IP, important if there is NAT/PAT
|
||||
// char ipStr[20];
|
||||
// IPAddress ip = WiFi.localIP();
|
||||
// sprintf_P(ipStr, PSTR("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]);
|
||||
ESP[F("ip")] = WiFi.localIP().toString();
|
||||
|
||||
// Create nested SENSOR json object
|
||||
JsonObject& SENSOR = data.createNestedObject(String(F("SENSOR")));
|
||||
byte valueCount = getValueCountFromSensorType(element.sensorType);
|
||||
// char itemNames[valueCount][2];
|
||||
for (byte x = 0; x < valueCount; x++)
|
||||
{
|
||||
// Each sensor value get an own object (0..n)
|
||||
// sprintf(itemNames[x],"%d",x);
|
||||
JsonObject& val = SENSOR.createNestedObject(String(x));
|
||||
val[F("deviceName")] = getTaskDeviceName(element.TaskIndex);
|
||||
val[F("valueName")] = ExtraTaskSettings.TaskDeviceValueNames[x];
|
||||
val[F("type")] = element.sensorType;
|
||||
val[F("value")] = element.txt[x];
|
||||
}
|
||||
|
||||
// Create json buffer
|
||||
root.printTo(jsonString);
|
||||
}
|
||||
|
||||
// We now create a URI for the request
|
||||
String request = create_http_request_auth(
|
||||
controller_number, element.controller_idx, ControllerSettings,
|
||||
F("POST"), element.url, element.json.length());
|
||||
request += element.json;
|
||||
F("POST"), F("/ESPEasy"), jsonString.length());
|
||||
request += jsonString;
|
||||
|
||||
return send_via_http(controller_number, client, request);
|
||||
return send_via_http(controller_number, client, request, ControllerSettings.MustCheckReply);
|
||||
}
|
||||
#endif
|
||||
|
||||
+12
-7
@@ -42,11 +42,11 @@ boolean CPlugin_010(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
byte valueCount = getValueCountFromSensorType(event->sensorType);
|
||||
C010_queue_element element(event, valueCount);
|
||||
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
|
||||
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
|
||||
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
|
||||
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
|
||||
|
||||
for (byte x = 0; x < valueCount; x++)
|
||||
{
|
||||
@@ -85,13 +85,18 @@ bool do_process_c010_delay_queue(int controller_number, const C010_queue_element
|
||||
if (element.checkDone(true))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!try_connect_host(controller_number, portUDP, ControllerSettings))
|
||||
WiFiUDP C010_portUDP;
|
||||
if (!beginWiFiUDP_randomPort(C010_portUDP)) return false;
|
||||
if (!try_connect_host(controller_number, C010_portUDP, ControllerSettings))
|
||||
return false;
|
||||
|
||||
portUDP.write(
|
||||
C010_portUDP.write(
|
||||
(uint8_t*)element.txt[element.valuesSent].c_str(),
|
||||
element.txt[element.valuesSent].length());
|
||||
return element.checkDone(portUDP.endPacket() != 0);
|
||||
bool reply = C010_portUDP.endPacket();
|
||||
C010_portUDP.stop();
|
||||
if (ControllerSettings.MustCheckReply)
|
||||
return element.checkDone(reply);
|
||||
return element.checkDone(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
+4
-4
@@ -133,7 +133,7 @@ bool do_process_c011_delay_queue(int controller_number, const C011_queue_element
|
||||
if (!try_connect_host(controller_number, client, ControllerSettings))
|
||||
return false;
|
||||
|
||||
return send_via_http(controller_number, client, element.txt);
|
||||
return send_via_http(controller_number, client, element.txt, ControllerSettings.MustCheckReply);
|
||||
}
|
||||
|
||||
|
||||
@@ -147,8 +147,8 @@ boolean Create_schedule_HTTP_C011(struct EventStruct *event)
|
||||
if (!WiFiConnected(100)) {
|
||||
return false;
|
||||
}
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
|
||||
|
||||
C011_ConfigStruct customConfig;
|
||||
LoadCustomControllerSettings(event->ControllerIndex,(byte*)&customConfig, sizeof(customConfig));
|
||||
@@ -158,7 +158,7 @@ boolean Create_schedule_HTTP_C011(struct EventStruct *event)
|
||||
if (!try_connect_host(controller_number, client, ControllerSettings))
|
||||
return false;
|
||||
|
||||
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
|
||||
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
|
||||
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
|
||||
|
||||
String payload = create_http_request_auth(
|
||||
|
||||
+50
-51
@@ -37,11 +37,11 @@ boolean CPlugin_012(byte function, struct EventStruct *event, String& string)
|
||||
// Collect the values at the same run, to make sure all are from the same sample
|
||||
byte valueCount = getValueCountFromSensorType(event->sensorType);
|
||||
C012_queue_element element(event, valueCount);
|
||||
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
|
||||
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
|
||||
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
|
||||
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
|
||||
|
||||
for (byte x = 0; x < valueCount; x++)
|
||||
{
|
||||
@@ -73,17 +73,16 @@ bool do_process_c012_delay_queue(int controller_number, const C012_queue_element
|
||||
if (element.checkDone(true))
|
||||
return true;
|
||||
}
|
||||
if (wifiStatus != ESPEASY_WIFI_SERVICES_INITIALIZED) {
|
||||
return false;
|
||||
}
|
||||
return element.checkDone(Blynk_get(element.txt[element.valuesSent], element.controller_idx));
|
||||
}
|
||||
|
||||
boolean Blynk_get(const String& command, byte controllerIndex, float *data )
|
||||
{
|
||||
if (wifiStatus != ESPEASY_WIFI_SERVICES_INITIALIZED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(controllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(controllerIndex, ControllerSettings);
|
||||
|
||||
if ((SecuritySettings.ControllerPassword[controllerIndex][0] == 0)) {
|
||||
addLog(LOG_LEVEL_ERROR, F("Blynk : No password set"));
|
||||
@@ -104,58 +103,58 @@ boolean Blynk_get(const String& command, byte controllerIndex, float *data )
|
||||
ControllerSettings.getHost().c_str());
|
||||
addLog(LOG_LEVEL_DEBUG, request);
|
||||
client.print(request);
|
||||
boolean success = !ControllerSettings.MustCheckReply;
|
||||
if (ControllerSettings.MustCheckReply || data) {
|
||||
unsigned long timer = millis() + 200;
|
||||
while (!client_available(client) && !timeOutReached(timer))
|
||||
delay(1);
|
||||
|
||||
unsigned long timer = millis() + 200;
|
||||
while (!client.available() && !timeOutReached(timer))
|
||||
yield();
|
||||
char log[80] = {0};
|
||||
|
||||
boolean success = false;
|
||||
char log[80] = {0};
|
||||
|
||||
// Read all the lines of the reply from server and log them
|
||||
while (client_available(client)) {
|
||||
String line;
|
||||
safeReadStringUntil(client, line, '\n');
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, line);
|
||||
// success ?
|
||||
if (line.substring(0, 15) == F("HTTP/1.1 200 OK")) {
|
||||
strcpy_P(log, PSTR("HTTP : Success"));
|
||||
success = true;
|
||||
}
|
||||
else if (line.substring(0, 24) == F("HTTP/1.1 400 Bad Request")) {
|
||||
strcpy_P(log, PSTR("HTTP : Unauthorized"));
|
||||
}
|
||||
else if (line.substring(0, 25) == F("HTTP/1.1 401 Unauthorized")) {
|
||||
strcpy_P(log, PSTR("HTTP : Unauthorized"));
|
||||
}
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
|
||||
// data only
|
||||
if (data && line.startsWith("["))
|
||||
{
|
||||
String strValue = line;
|
||||
byte pos = strValue.indexOf('"',2);
|
||||
strValue = strValue.substring(2, pos);
|
||||
strValue.trim();
|
||||
float value = strValue.toFloat();
|
||||
*data = value;
|
||||
success = true;
|
||||
|
||||
char value_char[5] = {0};
|
||||
strValue.toCharArray(value_char, 5);
|
||||
sprintf_P(log, PSTR("Blynk get - %s => %s"),command.c_str(), value_char );
|
||||
// Read all the lines of the reply from server and log them
|
||||
while (client_available(client)) {
|
||||
String line;
|
||||
safeReadStringUntil(client, line, '\n');
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, line);
|
||||
// success ?
|
||||
if (line.substring(0, 15) == F("HTTP/1.1 200 OK")) {
|
||||
strcpy_P(log, PSTR("HTTP : Success"));
|
||||
success = true;
|
||||
}
|
||||
else if (line.substring(0, 24) == F("HTTP/1.1 400 Bad Request")) {
|
||||
strcpy_P(log, PSTR("HTTP : Unauthorized"));
|
||||
}
|
||||
else if (line.substring(0, 25) == F("HTTP/1.1 401 Unauthorized")) {
|
||||
strcpy_P(log, PSTR("HTTP : Unauthorized"));
|
||||
}
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
|
||||
// data only
|
||||
if (data && line.startsWith("["))
|
||||
{
|
||||
String strValue = line;
|
||||
byte pos = strValue.indexOf('"',2);
|
||||
strValue = strValue.substring(2, pos);
|
||||
strValue.trim();
|
||||
float value = strValue.toFloat();
|
||||
*data = value;
|
||||
success = true;
|
||||
|
||||
char value_char[5] = {0};
|
||||
strValue.toCharArray(value_char, 5);
|
||||
sprintf_P(log, PSTR("Blynk get - %s => %s"),command.c_str(), value_char );
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
}
|
||||
delay(0);
|
||||
}
|
||||
yield();
|
||||
}
|
||||
strcpy_P(log, PSTR("HTTP : closing connection (012)"));
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
addLog(LOG_LEVEL_DEBUG, F("HTTP : closing connection (012)"));
|
||||
|
||||
client.flush();
|
||||
client.stop();
|
||||
|
||||
// important - backgroundtasks - free mem
|
||||
timer = millis() + Settings.MessageDelay;
|
||||
unsigned long timer = millis() + Settings.MessageDelay;
|
||||
while (!timeOutReached(timer))
|
||||
backgroundtasks();
|
||||
|
||||
|
||||
+101
-78
@@ -126,10 +126,10 @@ public:
|
||||
return total;
|
||||
}
|
||||
|
||||
String txt[VARS_PER_TASK];
|
||||
int controller_idx;
|
||||
byte TaskIndex;
|
||||
int idx;
|
||||
String txt[VARS_PER_TASK];
|
||||
mutable byte valuesSent; // Value must be set by const function checkDone()
|
||||
byte valueCount;
|
||||
};
|
||||
@@ -146,19 +146,29 @@ public:
|
||||
\*********************************************************************************************/
|
||||
class C009_queue_element {
|
||||
public:
|
||||
C009_queue_element() : controller_idx(0) {}
|
||||
C009_queue_element(int ctrl_idx, const String& URI, const String& JSON) :
|
||||
controller_idx(ctrl_idx), url(URI), json(JSON) {}
|
||||
C009_queue_element() : controller_idx(0), TaskIndex(0), idx(0), sensorType(0) {}
|
||||
C009_queue_element(const struct EventStruct* event) :
|
||||
controller_idx(event->ControllerIndex),
|
||||
TaskIndex(event->TaskIndex),
|
||||
idx(event->idx),
|
||||
sensorType(event->sensorType) {}
|
||||
|
||||
size_t getSize() const {
|
||||
return sizeof(this) + url.length() + json.length();
|
||||
size_t total = sizeof(this);
|
||||
for (int i = 0; i < VARS_PER_TASK; ++i) {
|
||||
total += txt[i].length();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
String txt[VARS_PER_TASK];
|
||||
int controller_idx;
|
||||
String url;
|
||||
String json;
|
||||
byte TaskIndex;
|
||||
int idx;
|
||||
byte sensorType;
|
||||
};
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
* C010_queue_element for queueing requests for 010: Generic UDP
|
||||
* Using queue_element_single_value_base
|
||||
@@ -189,13 +199,15 @@ struct ControllerDelayHandlerStruct {
|
||||
max_queue_depth(CONTROLLER_DELAY_QUEUE_DEPTH_DFLT),
|
||||
attempt(0),
|
||||
max_retries(CONTROLLER_DELAY_QUEUE_RETRY_DFLT),
|
||||
delete_oldest(false) {}
|
||||
delete_oldest(false),
|
||||
must_check_reply(false) {}
|
||||
|
||||
void configureControllerSettings(const ControllerSettingsStruct& settings) {
|
||||
minTimeBetweenMessages = settings.MinimalTimeBetweenMessages;
|
||||
max_queue_depth = settings.MaxQueueDepth;
|
||||
max_retries = settings.MaxRetry;
|
||||
delete_oldest = settings.DeleteOldest;
|
||||
must_check_reply = settings.MustCheckReply;
|
||||
// Set some sound limits when not configured
|
||||
if (max_queue_depth == 0) max_queue_depth = CONTROLLER_DELAY_QUEUE_DEPTH_DFLT;
|
||||
if (max_retries == 0) max_retries = CONTROLLER_DELAY_QUEUE_RETRY_DFLT;
|
||||
@@ -251,15 +263,14 @@ struct ControllerDelayHandlerStruct {
|
||||
|
||||
// Get the next element.
|
||||
// Remove front element when max_retries is reached.
|
||||
bool getNext(T& element) {
|
||||
if (sendQueue.empty()) return false;
|
||||
T* getNext() {
|
||||
if (sendQueue.empty()) return NULL;
|
||||
if (attempt > max_retries) {
|
||||
sendQueue.pop_front();
|
||||
attempt = 0;
|
||||
if (sendQueue.empty()) return false;
|
||||
if (sendQueue.empty()) return NULL;
|
||||
}
|
||||
element = sendQueue.front();
|
||||
return true;
|
||||
return &sendQueue.front();
|
||||
}
|
||||
|
||||
// Mark as processed and return time to schedule for next process.
|
||||
@@ -302,6 +313,7 @@ struct ControllerDelayHandlerStruct {
|
||||
byte attempt;
|
||||
byte max_retries;
|
||||
bool delete_oldest;
|
||||
bool must_check_reply;
|
||||
};
|
||||
|
||||
ControllerDelayHandlerStruct<MQTT_queue_element> MQTTDelayHandler;
|
||||
@@ -321,17 +333,17 @@ ControllerDelayHandlerStruct<MQTT_queue_element> MQTTDelayHandler;
|
||||
ControllerDelayHandlerStruct<C##NNN##_queue_element> C##NNN##_DelayHandler; \
|
||||
bool do_process_c##NNN##_delay_queue(int controller_number, const C##NNN##_queue_element& element, ControllerSettingsStruct& ControllerSettings); \
|
||||
void process_c##NNN##_delay_queue() { \
|
||||
C##NNN##_queue_element element; \
|
||||
if (!C##NNN##_DelayHandler.getNext(element)) return; \
|
||||
ControllerSettingsStruct ControllerSettings; \
|
||||
LoadControllerSettings(element.controller_idx, (byte*)&ControllerSettings, sizeof(ControllerSettings)); \
|
||||
C##NNN##_queue_element* element(C##NNN##_DelayHandler.getNext()); \
|
||||
if (element == NULL) return; \
|
||||
MakeControllerSettings(ControllerSettings); \
|
||||
LoadControllerSettings(element->controller_idx, ControllerSettings); \
|
||||
C##NNN##_DelayHandler.configureControllerSettings(ControllerSettings); \
|
||||
if (!WiFiConnected(100)) { \
|
||||
scheduleNextDelayQueue(TIMER_C##NNN##_DELAY_QUEUE, C##NNN##_DelayHandler.getNextScheduleTime()); \
|
||||
return; \
|
||||
} \
|
||||
START_TIMER; \
|
||||
C##NNN##_DelayHandler.markProcessed(do_process_c##NNN##_delay_queue(M, element, ControllerSettings)); \
|
||||
C##NNN##_DelayHandler.markProcessed(do_process_c##NNN##_delay_queue(M, *element, ControllerSettings)); \
|
||||
STOP_TIMER(C##NNN##_DELAY_QUEUE); \
|
||||
scheduleNextDelayQueue(TIMER_C##NNN##_DELAY_QUEUE, C##NNN##_DelayHandler.getNextScheduleTime()); \
|
||||
}
|
||||
@@ -382,33 +394,37 @@ bool safeReadStringUntil(Stream &input, String &str, char terminator, unsigned i
|
||||
int c;
|
||||
const unsigned long start = millis();
|
||||
const unsigned long timer = start + timeout;
|
||||
unsigned long backgroundtasks_timer = start + 100;
|
||||
unsigned long backgroundtasks_timer = start + 10;
|
||||
str = "";
|
||||
|
||||
do {
|
||||
//read character
|
||||
c = input.read();
|
||||
if (c >= 0) {
|
||||
//found terminator, we're ok
|
||||
if (c == terminator) {
|
||||
return(true);
|
||||
}
|
||||
//found character, add to string
|
||||
else{
|
||||
str += char(c);
|
||||
//string at max size?
|
||||
if (str.length() >= maxSize) {
|
||||
addLog(LOG_LEVEL_ERROR, F("Not enough bufferspace to read all input data!"));
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// We must run the backgroundtasks every now and then.
|
||||
if (timeOutReached(backgroundtasks_timer)) {
|
||||
backgroundtasks_timer += 100;
|
||||
backgroundtasks();
|
||||
if (input.available()) {
|
||||
c = input.read();
|
||||
if (c >= 0) {
|
||||
//found terminator, we're ok
|
||||
if (c == terminator) {
|
||||
return(true);
|
||||
}
|
||||
//found character, add to string
|
||||
else{
|
||||
str += char(c);
|
||||
//string at max size?
|
||||
if (str.length() >= maxSize) {
|
||||
addLog(LOG_LEVEL_ERROR, F("Not enough bufferspace to read all input data!"));
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// We must run the backgroundtasks every now and then.
|
||||
if (timeOutReached(backgroundtasks_timer)) {
|
||||
backgroundtasks_timer += 10;
|
||||
backgroundtasks();
|
||||
} else {
|
||||
delay(0);
|
||||
}
|
||||
} else {
|
||||
yield();
|
||||
delay(0);
|
||||
}
|
||||
} while (!timeOutReached(timer));
|
||||
|
||||
@@ -584,6 +600,7 @@ bool count_connection_results(bool success, const String& prefix, int controller
|
||||
}
|
||||
|
||||
bool try_connect_host(int controller_number, WiFiUDP& client, ControllerSettingsStruct& ControllerSettings) {
|
||||
client.setTimeout(ControllerSettings.ClientTimeout);
|
||||
log_connecting_to(F("UDP : "), controller_number, ControllerSettings);
|
||||
bool success = ControllerSettings.beginPacket(client) != 0;
|
||||
return count_connection_results(
|
||||
@@ -593,6 +610,7 @@ bool try_connect_host(int controller_number, WiFiUDP& client, ControllerSettings
|
||||
|
||||
bool try_connect_host(int controller_number, WiFiClient& client, ControllerSettingsStruct& ControllerSettings) {
|
||||
// Use WiFiClient class to create TCP connections
|
||||
client.setTimeout(ControllerSettings.ClientTimeout);
|
||||
log_connecting_to(F("HTTP : "), controller_number, ControllerSettings);
|
||||
bool success = ControllerSettings.connectToHost(client);
|
||||
return count_connection_results(
|
||||
@@ -604,52 +622,57 @@ bool try_connect_host(int controller_number, WiFiClient& client, ControllerSetti
|
||||
// See: https://github.com/esp8266/Arduino/pull/5113
|
||||
// https://github.com/esp8266/Arduino/pull/1829
|
||||
bool client_available(WiFiClient& client) {
|
||||
delay(0);
|
||||
return client.available() || client.connected();
|
||||
}
|
||||
|
||||
bool send_via_http(const String& logIdentifier, WiFiClient& client, const String& postStr) {
|
||||
bool success = false;
|
||||
bool send_via_http(const String& logIdentifier, WiFiClient& client, const String& postStr, bool must_check_reply) {
|
||||
bool success = !must_check_reply;
|
||||
// This will send the request to the server
|
||||
client.print(postStr);
|
||||
|
||||
unsigned long timer = millis() + 200;
|
||||
while (!client.available() && !timeOutReached(timer))
|
||||
delay(1);
|
||||
|
||||
// Read all the lines of the reply from server and print them to Serial
|
||||
while (client_available(client) && !success) {
|
||||
// String line = client.readStringUntil('\n');
|
||||
String line;
|
||||
safeReadStringUntil(client, line, '\n');
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
|
||||
if (line.length() > 80) {
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, line.substring(0, 80));
|
||||
} else {
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, line);
|
||||
}
|
||||
if (must_check_reply) {
|
||||
unsigned long timer = millis() + 200;
|
||||
while (!client_available(client)) {
|
||||
if (timeOutReached(timer)) return false;
|
||||
delay(1);
|
||||
}
|
||||
if (line.startsWith(F("HTTP/1.1 2")))
|
||||
{
|
||||
success = true;
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
||||
String log = F("HTTP : ");
|
||||
log += logIdentifier;
|
||||
log += F(" Success! ");
|
||||
log += line;
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
|
||||
// Read all the lines of the reply from server and print them to Serial
|
||||
while (client_available(client) && !success) {
|
||||
// String line = client.readStringUntil('\n');
|
||||
String line;
|
||||
safeReadStringUntil(client, line, '\n');
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
|
||||
if (line.length() > 80) {
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, line.substring(0, 80));
|
||||
} else {
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, line);
|
||||
}
|
||||
}
|
||||
} else if (line.startsWith(F("HTTP/1.1 4"))) {
|
||||
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
|
||||
String log = F("HTTP : ");
|
||||
log += logIdentifier;
|
||||
log += F(" Error: ");
|
||||
log += line;
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
if (line.startsWith(F("HTTP/1.1 2")))
|
||||
{
|
||||
success = true;
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
||||
String log = F("HTTP : ");
|
||||
log += logIdentifier;
|
||||
log += F(" Success! ");
|
||||
log += line;
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
}
|
||||
} else if (line.startsWith(F("HTTP/1.1 4"))) {
|
||||
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
|
||||
String log = F("HTTP : ");
|
||||
log += logIdentifier;
|
||||
log += F(" Error: ");
|
||||
log += line;
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
}
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, postStr);
|
||||
}
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, postStr);
|
||||
delay(0);
|
||||
}
|
||||
yield();
|
||||
}
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
||||
String log = F("HTTP : ");
|
||||
@@ -663,8 +686,8 @@ bool send_via_http(const String& logIdentifier, WiFiClient& client, const String
|
||||
return success;
|
||||
}
|
||||
|
||||
bool send_via_http(int controller_number, WiFiClient& client, const String& postStr) {
|
||||
return send_via_http(get_formatted_Controller_number(controller_number), client, postStr);
|
||||
bool send_via_http(int controller_number, WiFiClient& client, const String& postStr, bool must_check_reply) {
|
||||
return send_via_http(get_formatted_Controller_number(controller_number), client, postStr, must_check_reply);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+6
-5
@@ -38,8 +38,8 @@ boolean NPlugin_001(byte function, struct EventStruct *event, String& string)
|
||||
//
|
||||
// if (command == F("email"))
|
||||
// {
|
||||
// NotificationSettingsStruct NotificationSettings;
|
||||
// LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettings));
|
||||
// MakeNotificationSettings(NotificationSettings);
|
||||
// LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettingsStruct));
|
||||
// NPlugin_001_send(NotificationSettings.Domain, NotificationSettings.Receiver, NotificationSettings.Sender, NotificationSettings.Subject, NotificationSettings.Body, NotificationSettings.Server, NotificationSettings.Port);
|
||||
// success = true;
|
||||
// }
|
||||
@@ -48,8 +48,8 @@ boolean NPlugin_001(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case NPLUGIN_NOTIFY:
|
||||
{
|
||||
NotificationSettingsStruct NotificationSettings;
|
||||
LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettings));
|
||||
MakeNotificationSettings(NotificationSettings);
|
||||
LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettingsStruct));
|
||||
String subject = NotificationSettings.Subject;
|
||||
String body = "";
|
||||
if (event->String1.length() > 0)
|
||||
@@ -73,6 +73,7 @@ boolean NPlugin_001_send(const NotificationSettingsStruct& notificationsettings,
|
||||
|
||||
// Use WiFiClient class to create TCP connections
|
||||
WiFiClient client;
|
||||
client.setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT);
|
||||
String aHost = notificationsettings.Server;
|
||||
addLog(LOG_LEVEL_DEBUG, String(F("EMAIL: Connecting to ")) + aHost + notificationsettings.Port);
|
||||
if (client.connect(aHost.c_str(), notificationsettings.Port) != 1) {
|
||||
@@ -193,7 +194,7 @@ boolean NPlugin_001_MTA(WiFiClient& client, String aStr, const String &aWaitForP
|
||||
return false;
|
||||
}
|
||||
|
||||
yield();
|
||||
delay(0);
|
||||
|
||||
// String line = client.readStringUntil('\n');
|
||||
String line;
|
||||
|
||||
@@ -35,8 +35,8 @@ boolean NPlugin_002(byte function, struct EventStruct *event, String& string)
|
||||
//
|
||||
// if (command == F("buzzer"))
|
||||
// {
|
||||
// NotificationSettingsStruct NotificationSettings;
|
||||
// LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettings));
|
||||
// MakeNotificationSettings(NotificationSettings);
|
||||
// LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettingsStruct));
|
||||
// success = true;
|
||||
// }
|
||||
// break;
|
||||
@@ -44,8 +44,8 @@ boolean NPlugin_002(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case NPLUGIN_NOTIFY:
|
||||
{
|
||||
NotificationSettingsStruct NotificationSettings;
|
||||
LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettings));
|
||||
MakeNotificationSettings(NotificationSettings);
|
||||
LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettingsStruct));
|
||||
//this reserves IRAM and uninitialized RAM
|
||||
#ifndef ESP32
|
||||
// Buzzer not compatible with ESP32 due to lack of tone command.
|
||||
|
||||
+322
-46
@@ -3,6 +3,31 @@
|
||||
//#################################### Plugin 001: Input Switch #########################################
|
||||
//#######################################################################################################
|
||||
|
||||
/**************************************************\
|
||||
CONFIG
|
||||
TaskDevicePluginConfig settings:
|
||||
0: button type (switch or dimmer)
|
||||
1: dim value
|
||||
2: button option (normal, push high, push low)
|
||||
3: send boot state (true,false)
|
||||
4: use doubleclick (0,1,2,3)
|
||||
5: use longpress (0,1,2,3)
|
||||
6: LP fired (true,false)
|
||||
7: doubleclick counter (=0,1,2,3)
|
||||
|
||||
TaskDevicePluginConfigFloat settings:
|
||||
0: debounce interval ms
|
||||
1: doubleclick interval ms
|
||||
2: longpress interval ms
|
||||
3:
|
||||
|
||||
TaskDevicePluginConfigLong settings:
|
||||
0: clickTime debounce ms
|
||||
1: clickTime doubleclick ms
|
||||
2: clickTime longpress ms
|
||||
3:
|
||||
\**************************************************/
|
||||
|
||||
#define PLUGIN_001
|
||||
#define PLUGIN_ID_001 1
|
||||
#define PLUGIN_NAME_001 "Switch input - Switch"
|
||||
@@ -18,6 +43,18 @@
|
||||
#define PLUGIN_001_BUTTON_TYPE_NORMAL_SWITCH 0
|
||||
#define PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW 1
|
||||
#define PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_HIGH 2
|
||||
#define PLUGIN_001_DOUBLECLICK_MIN_INTERVAL 1000
|
||||
#define PLUGIN_001_DOUBLECLICK_MAX_INTERVAL 3000
|
||||
#define PLUGIN_001_LONGPRESS_MIN_INTERVAL 1000
|
||||
#define PLUGIN_001_LONGPRESS_MAX_INTERVAL 5000
|
||||
#define PLUGIN_001_DC_DISABLED 0
|
||||
#define PLUGIN_001_DC_LOW 1
|
||||
#define PLUGIN_001_DC_HIGH 2
|
||||
#define PLUGIN_001_DC_BOTH 3
|
||||
#define PLUGIN_001_LONGPRESS_DISABLED 0
|
||||
#define PLUGIN_001_LONGPRESS_LOW 1
|
||||
#define PLUGIN_001_LONGPRESS_HIGH 2
|
||||
#define PLUGIN_001_LONGPRESS_BOTH 3
|
||||
|
||||
boolean Plugin_001_read_switch_state(struct EventStruct *event) {
|
||||
return digitalRead(Settings.TaskDevicePin1[event->TaskIndex]) == HIGH;
|
||||
@@ -26,8 +63,8 @@ boolean Plugin_001_read_switch_state(struct EventStruct *event) {
|
||||
boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
static boolean switchstate[TASKS_MAX];
|
||||
static boolean outputstate[TASKS_MAX];
|
||||
static byte switchstate[TASKS_MAX];
|
||||
static byte outputstate[TASKS_MAX];
|
||||
static int8_t PinMonitor[GPIO_MAX];
|
||||
static int8_t PinMonitorState[GPIO_MAX];
|
||||
|
||||
@@ -89,6 +126,47 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
addFormCheckBox(F("Send Boot state"),F("plugin_001_boot"),
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][3]);
|
||||
|
||||
addFormSubHeader(F("Advanced event management"));
|
||||
|
||||
addFormNumericBox(F("De-bounce (ms)"), F("plugin_001_debounce"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0]), 0, 250);
|
||||
|
||||
//set minimum value for doubleclick MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_001_DOUBLECLICK_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_001_DOUBLECLICK_MIN_INTERVAL;
|
||||
|
||||
byte choiceDC = Settings.TaskDevicePluginConfig[event->TaskIndex][4];
|
||||
String buttonDC[4];
|
||||
buttonDC[0] = F("Disabled");
|
||||
buttonDC[1] = F("Active only on LOW (EVENT=3)");
|
||||
buttonDC[2] = F("Active only on HIGH (EVENT=3)");
|
||||
buttonDC[3] = F("Active on LOW & HIGH (EVENT=3)");
|
||||
int buttonDCValues[4] = {PLUGIN_001_DC_DISABLED, PLUGIN_001_DC_LOW, PLUGIN_001_DC_HIGH,PLUGIN_001_DC_BOTH};
|
||||
|
||||
addFormSelector(F("Doubleclick event"), F("plugin_001_dc"), 4, buttonDC, buttonDCValues, choiceDC);
|
||||
|
||||
//addFormCheckBox(F("Doubleclick event (3)"), F("plugin_001_dc"), Settings.TaskDevicePluginConfig[event->TaskIndex][4]);
|
||||
addFormNumericBox(F("Doubleclick max. interval (ms)"), F("plugin_001_dcmaxinterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1]), PLUGIN_001_DOUBLECLICK_MIN_INTERVAL, PLUGIN_001_DOUBLECLICK_MAX_INTERVAL);
|
||||
|
||||
//set minimum value for longpress MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_001_LONGPRESS_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_001_LONGPRESS_MIN_INTERVAL;
|
||||
|
||||
byte choiceLP = Settings.TaskDevicePluginConfig[event->TaskIndex][5];
|
||||
String buttonLP[4];
|
||||
buttonLP[0] = F("Disabled");
|
||||
buttonLP[1] = F("Active only on LOW (EVENT= 10 [NORMAL] or 11 [INVERSED])");
|
||||
buttonLP[2] = F("Active only on HIGH (EVENT= 11 [NORMAL] or 10 [INVERSED])");
|
||||
buttonLP[3] = F("Active on LOW & HIGH (EVENT= 10 or 11)");
|
||||
int buttonLPValues[4] = {PLUGIN_001_LONGPRESS_DISABLED, PLUGIN_001_LONGPRESS_LOW, PLUGIN_001_LONGPRESS_HIGH,PLUGIN_001_LONGPRESS_BOTH};
|
||||
addFormSelector(F("Longpress event"), F("plugin_001_lp"), 4, buttonLP, buttonLPValues, choiceLP);
|
||||
|
||||
//addFormCheckBox(F("Longpress event (10 & 11)"), F("plugin_001_lp"), Settings.TaskDevicePluginConfig[event->TaskIndex][5]);
|
||||
addFormNumericBox(F("Longpress min. interval (ms)"), F("plugin_001_lpmininterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]), PLUGIN_001_LONGPRESS_MIN_INTERVAL, PLUGIN_001_LONGPRESS_MAX_INTERVAL);
|
||||
|
||||
//TO-DO: add Extra-Long Press event
|
||||
//addFormCheckBox(F("Extra-Longpress event (20 & 21)"), F("plugin_001_elp"), Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]);
|
||||
//addFormNumericBox(F("Extra-Longpress min. interval (ms)"), F("plugin_001_elpmininterval"), Settings.TaskDevicePluginConfigLong[event->TaskIndex][2], 500, 2000);
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
@@ -105,6 +183,18 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][3] = isFormItemChecked(F("plugin_001_boot"));
|
||||
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0] = getFormItemInt(F("plugin_001_debounce"));
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][4] = getFormItemInt(F("plugin_001_dc"));
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = getFormItemInt(F("plugin_001_dcmaxinterval"));
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][5] = getFormItemInt(F("plugin_001_lp"));
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = getFormItemInt(F("plugin_001_lpmininterval"));
|
||||
|
||||
//TO-DO: add Extra-Long Press event
|
||||
//Settings.TaskDevicePluginConfigLong[event->TaskIndex][1] = isFormItemChecked(F("plugin_001_elp"));
|
||||
//Settings.TaskDevicePluginConfigLong[event->TaskIndex][2] = getFormItemInt(F("plugin_001_elpmininterval"));
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
@@ -121,30 +211,62 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
else
|
||||
pinMode(Settings.TaskDevicePin1[event->TaskIndex], INPUT);
|
||||
|
||||
// @giig1967g-20181022: if it is in the device list we assume it's an input pin
|
||||
setPinState(PLUGIN_ID_001, Settings.TaskDevicePin1[event->TaskIndex], PIN_MODE_INPUT, 0);
|
||||
|
||||
// read and store current state to prevent switching at boot time
|
||||
switchstate[event->TaskIndex] = Plugin_001_read_switch_state(event);
|
||||
outputstate[event->TaskIndex] = switchstate[event->TaskIndex];
|
||||
|
||||
// if boot state must be send, inverse default state
|
||||
// this is done to force the trigger in PLUGIN_TEN_PER_SECOND
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][3])
|
||||
{
|
||||
switchstate[event->TaskIndex] = !switchstate[event->TaskIndex];
|
||||
outputstate[event->TaskIndex] = !outputstate[event->TaskIndex];
|
||||
}
|
||||
|
||||
// set initial UserVar of the switch
|
||||
if (Settings.TaskDevicePin1Inversed[event->TaskIndex]){
|
||||
UserVar[event->BaseVarIndex] = !switchstate[event->TaskIndex];
|
||||
} else {
|
||||
UserVar[event->BaseVarIndex] = switchstate[event->TaskIndex];
|
||||
}
|
||||
|
||||
// counter = 0
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0;
|
||||
|
||||
//used to track if LP has fired
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6]=false;
|
||||
|
||||
//store millis for debounce, doubleclick and long press
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]=millis(); //debounce timer
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis(); //doubleclick timer
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis(); //longpress timer
|
||||
|
||||
//set minimum value for doubleclick MIN interval speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_001_DOUBLECLICK_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_001_DOUBLECLICK_MIN_INTERVAL;
|
||||
|
||||
//set minimum value for longpress MIN interval speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_001_LONGPRESS_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_001_LONGPRESS_MIN_INTERVAL;
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_REQUEST:
|
||||
{
|
||||
String device = parseString(string, 1);
|
||||
String command = parseString(string, 2);
|
||||
String strPar1 = parseString(string, 3);
|
||||
if (device == F("gpio") && command == F("pinstate"))
|
||||
//String device = parseString(string, 1);
|
||||
//String command = parseString(string, 2);
|
||||
//String strPar1 = parseString(string, 3);
|
||||
|
||||
// returns pin value using syntax: [plugin#gpio#pinstate#xx]
|
||||
if (string.length()>=13 && string.substring(0,13).equalsIgnoreCase(F("gpio,pinstate")))
|
||||
{
|
||||
int par1;
|
||||
if (validIntFromString(strPar1, par1)) {
|
||||
if (validIntFromString(parseString(string, 3), par1)) {
|
||||
string = digitalRead(par1);
|
||||
}
|
||||
success = true;
|
||||
@@ -161,7 +283,7 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
if (PinMonitorState[x] != state){
|
||||
String eventString = F("GPIO#");
|
||||
eventString += x;
|
||||
eventString += F("=");
|
||||
eventString += '=';
|
||||
eventString += state;
|
||||
rulesProcessing(eventString);
|
||||
PinMonitorState[x] = state;
|
||||
@@ -173,51 +295,179 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
case PLUGIN_TEN_PER_SECOND:
|
||||
{
|
||||
const boolean state = Plugin_001_read_switch_state(event);
|
||||
|
||||
/**************************************************************************\
|
||||
20181009 - @giig1967g: new doubleclick logic is:
|
||||
if there is a 'state' change, check debounce period.
|
||||
Then if doubleclick interval exceeded, reset Settings.TaskDevicePluginConfig[event->TaskIndex][7] to 0
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7] contains the current status for doubleclick:
|
||||
0: start counting
|
||||
1: 1st click
|
||||
2: 2nd click
|
||||
3: 3rd click = doubleclick event if inside interval (calculated as: '3rd click time' minus '1st click time')
|
||||
|
||||
Returned EVENT value is = 3 always for doubleclick
|
||||
In rules this can be checked:
|
||||
on Button#Switch=3 do //will fire if doubleclick
|
||||
\**************************************************************************/
|
||||
|
||||
if (state != switchstate[event->TaskIndex])
|
||||
{
|
||||
switchstate[event->TaskIndex] = state;
|
||||
const boolean currentOutputState = outputstate[event->TaskIndex];
|
||||
boolean new_outputState = currentOutputState;
|
||||
switch(Settings.TaskDevicePluginConfig[event->TaskIndex][2]) {
|
||||
case PLUGIN_001_BUTTON_TYPE_NORMAL_SWITCH:
|
||||
new_outputState = state;
|
||||
break;
|
||||
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW:
|
||||
if (!state)
|
||||
new_outputState = !currentOutputState;
|
||||
break;
|
||||
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_HIGH:
|
||||
if (state)
|
||||
new_outputState = !currentOutputState;
|
||||
break;
|
||||
}
|
||||
//reset timer for long press
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis();
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = false;
|
||||
|
||||
// send if output needs to be changed
|
||||
if (currentOutputState != new_outputState)
|
||||
const unsigned long debounceTime = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]);
|
||||
if (debounceTime >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0])) //de-bounce check
|
||||
{
|
||||
outputstate[event->TaskIndex] = new_outputState;
|
||||
boolean sendState = new_outputState;
|
||||
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
|
||||
sendState = !sendState;
|
||||
|
||||
byte output_value = sendState ? 1 : 0;
|
||||
event->sensorType = SENSOR_TYPE_SWITCH;
|
||||
if (P001_getSwitchType(event) == PLUGIN_001_TYPE_DIMMER) {
|
||||
if (sendState) {
|
||||
output_value = Settings.TaskDevicePluginConfig[event->TaskIndex][1];
|
||||
// Only set type to being dimmer when setting a value else it is "switched off".
|
||||
event->sensorType = SENSOR_TYPE_DIMMER;
|
||||
}
|
||||
const unsigned long deltaDC = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]);
|
||||
if ((deltaDC >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1])) ||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3)
|
||||
{
|
||||
//reset timer for doubleclick
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0;
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis();
|
||||
}
|
||||
UserVar[event->BaseVarIndex] = output_value;
|
||||
String log = F("SW : Switch state ");
|
||||
log += state ? F("1") : F("0");
|
||||
log += F(" Output value ");
|
||||
log += output_value;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
sendData(event);
|
||||
|
||||
//just to simplify the reading of the code
|
||||
#define COUNTER Settings.TaskDevicePluginConfig[event->TaskIndex][7]
|
||||
#define DC Settings.TaskDevicePluginConfig[event->TaskIndex][4]
|
||||
|
||||
//check settings for doubleclick according to the settings
|
||||
if ( COUNTER!=0 || ( COUNTER==0 && (DC==3 || (DC==1 && state==0) || (DC==2 && state==1))) )
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]++;
|
||||
#undef DC
|
||||
#undef COUNTER
|
||||
|
||||
switchstate[event->TaskIndex] = state;
|
||||
const boolean currentOutputState = outputstate[event->TaskIndex];
|
||||
boolean new_outputState = currentOutputState;
|
||||
switch(Settings.TaskDevicePluginConfig[event->TaskIndex][2])
|
||||
{
|
||||
case PLUGIN_001_BUTTON_TYPE_NORMAL_SWITCH:
|
||||
new_outputState = state;
|
||||
break;
|
||||
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW:
|
||||
if (!state)
|
||||
new_outputState = !currentOutputState;
|
||||
break;
|
||||
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_HIGH:
|
||||
if (state)
|
||||
new_outputState = !currentOutputState;
|
||||
break;
|
||||
}
|
||||
|
||||
// send if output needs to be changed
|
||||
if (currentOutputState != new_outputState)
|
||||
{
|
||||
byte output_value;
|
||||
outputstate[event->TaskIndex] = new_outputState;
|
||||
boolean sendState = new_outputState;
|
||||
|
||||
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
|
||||
sendState = !sendState;
|
||||
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3 && Settings.TaskDevicePluginConfig[event->TaskIndex][4]>0)
|
||||
{
|
||||
output_value = 3; //double click
|
||||
} else {
|
||||
output_value = sendState ? 1 : 0; //single click
|
||||
}
|
||||
event->sensorType = SENSOR_TYPE_SWITCH;
|
||||
if (P001_getSwitchType(event) == PLUGIN_001_TYPE_DIMMER) {
|
||||
if (sendState) {
|
||||
output_value = Settings.TaskDevicePluginConfig[event->TaskIndex][1];
|
||||
// Only set type to being dimmer when setting a value else it is "switched off".
|
||||
event->sensorType = SENSOR_TYPE_DIMMER;
|
||||
}
|
||||
}
|
||||
UserVar[event->BaseVarIndex] = output_value;
|
||||
|
||||
String log = F("SW : GPIO=");
|
||||
log += Settings.TaskDevicePin1[event->TaskIndex];
|
||||
log += F(" State=");
|
||||
log += state ? '1' : '0';
|
||||
log += output_value==3 ? F(" Doubleclick=") : F(" Output value=");
|
||||
log += output_value;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
sendData(event);
|
||||
|
||||
//reset Userdata so it displays the correct state value in the web page
|
||||
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
|
||||
}
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0] = millis();
|
||||
}
|
||||
}
|
||||
|
||||
//just to simplify the reading of the code
|
||||
#define LP Settings.TaskDevicePluginConfig[event->TaskIndex][5]
|
||||
#define FIRED Settings.TaskDevicePluginConfig[event->TaskIndex][6]
|
||||
|
||||
//check if LP is enabled and if LP has not fired yet
|
||||
else if (!FIRED && (LP==3 ||(LP==1 && state==0)||(LP==2 && state==1) ) ) {
|
||||
|
||||
#undef LP
|
||||
#undef FIRED
|
||||
|
||||
/**************************************************************************\
|
||||
20181009 - @giig1967g: new longpress logic is:
|
||||
if there is no 'state' change, check if longpress interval reached
|
||||
When reached send longpress event.
|
||||
Returned Event value = state + 10
|
||||
So if state = 0 => EVENT longpress = 10
|
||||
if state = 1 => EVENT longpress = 11
|
||||
So we can trigger longpress for high or low contact
|
||||
|
||||
In rules this can be checked:
|
||||
on Button#Switch=10 do //will fire if longpress when state = 0
|
||||
on Button#Switch=11 do //will fire if longpress when state = 1
|
||||
\**************************************************************************/
|
||||
const unsigned long deltaLP = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]);
|
||||
if (deltaLP >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]))
|
||||
{
|
||||
byte output_value;
|
||||
byte needToSendEvent = false;
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = true;
|
||||
|
||||
switch(Settings.TaskDevicePluginConfig[event->TaskIndex][2])
|
||||
{
|
||||
case PLUGIN_001_BUTTON_TYPE_NORMAL_SWITCH:
|
||||
needToSendEvent = true;
|
||||
break;
|
||||
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW:
|
||||
if (!state)
|
||||
needToSendEvent = true;
|
||||
break;
|
||||
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_HIGH:
|
||||
if (state)
|
||||
needToSendEvent = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (needToSendEvent) {
|
||||
boolean sendState = state;
|
||||
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
|
||||
sendState = !sendState;
|
||||
output_value = sendState ? 1 : 0;
|
||||
output_value = output_value + 10;
|
||||
|
||||
UserVar[event->BaseVarIndex] = output_value;
|
||||
String log = F("SW : LongPress: GPIO= ");
|
||||
log += Settings.TaskDevicePin1[event->TaskIndex];
|
||||
log += F(" State=");
|
||||
log += state ? '1' : '0';
|
||||
log += F(" Output value=");
|
||||
log += output_value;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
sendData(event);
|
||||
|
||||
//reset Userdata so it displays the correct state value in the web page
|
||||
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
@@ -257,6 +507,32 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
}
|
||||
}
|
||||
|
||||
if (command == F("gpiotoggle"))
|
||||
{
|
||||
success = true;
|
||||
byte mode;
|
||||
uint16_t currentState;
|
||||
|
||||
if (event->Par1 >= 0 && event->Par1 <= PIN_D_MAX)
|
||||
{
|
||||
if (hasPinState(PLUGIN_ID_001,event->Par1)) {
|
||||
getPinState(PLUGIN_ID_001, event->Par1, &mode, ¤tState);
|
||||
} else {
|
||||
currentState = (digitalRead(event->Par1) == HIGH);
|
||||
mode = PIN_MODE_OUTPUT;
|
||||
}
|
||||
|
||||
if (mode != PIN_MODE_INPUT) { //toggle only output pins
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
digitalWrite(event->Par1, !currentState);
|
||||
setPinState(PLUGIN_ID_001, event->Par1, PIN_MODE_OUTPUT, !currentState);
|
||||
log = String(F("SW : Toggle GPIO ")) + String(event->Par1) + String(F(" Set to ")) + String(!currentState);
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_001, event->Par1, log, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (command == F("pwm"))
|
||||
{
|
||||
success = true;
|
||||
|
||||
+25
-8
@@ -90,7 +90,7 @@ boolean Plugin_004(byte function, struct EventStruct * event, String& string)
|
||||
resolutionChoice = Plugin_004_DS_getResolution(savedAddress);
|
||||
else
|
||||
resolutionChoice = 9;
|
||||
String resultsOptions[4] = { "9", "10", "11", "12" };
|
||||
String resultsOptions[4] = { F("9"), F("10"), F("11"), F("12") };
|
||||
int resultsOptionValues[4] = { 9, 10, 11, 12 };
|
||||
addFormSelector(F("Device Resolution"), F("plugin_004_res"), 4, resultsOptions, resultsOptionValues, resolutionChoice);
|
||||
addHtml(F(" Bit"));
|
||||
@@ -105,7 +105,6 @@ boolean Plugin_004(byte function, struct EventStruct * event, String& string)
|
||||
|
||||
// save the address for selected device and store into extra tasksettings
|
||||
Plugin_004_DallasPin = Settings.TaskDevicePin1[event->TaskIndex];
|
||||
// byte devCount =
|
||||
if (Plugin_004_DallasPin != -1){
|
||||
Plugin_004_DS_scan(getFormItemInt(F("plugin_004_dev")), addr);
|
||||
for (byte x = 0; x < 8; x++)
|
||||
@@ -124,11 +123,12 @@ boolean Plugin_004(byte function, struct EventStruct * event, String& string)
|
||||
{
|
||||
if (x != 0)
|
||||
string += "-";
|
||||
// string += String(ExtraTaskSettings.TaskDevicePluginConfigLong[x], HEX);
|
||||
string += String(ExtraTaskSettings.TaskDevicePluginConfigLong[x], HEX);
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
Plugin_004_DallasPin = Settings.TaskDevicePin1[event->TaskIndex];
|
||||
@@ -169,7 +169,7 @@ boolean Plugin_004(byte function, struct EventStruct * event, String& string)
|
||||
for (byte x = 0; x < 8; x++)
|
||||
{
|
||||
if (x != 0)
|
||||
log += "-";
|
||||
log += '-';
|
||||
log += String(ExtraTaskSettings.TaskDevicePluginConfigLong[x], HEX);
|
||||
}
|
||||
|
||||
@@ -244,8 +244,25 @@ boolean Plugin_004_DS_readTemp(uint8_t ROM[8], float * value)
|
||||
|
||||
for (byte i = 0; i < 9; i++) // read 9 bytes
|
||||
ScratchPad[i] = Plugin_004_DS_read();
|
||||
|
||||
bool crc_ok = Plugin_004_DS_crc8(ScratchPad);
|
||||
|
||||
if (!Plugin_004_DS_crc8(ScratchPad))
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
||||
String log = F("DS: SP: ");
|
||||
|
||||
for (byte x = 0; x < 9; x++)
|
||||
{
|
||||
if (x != 0)
|
||||
log += ',';
|
||||
log += String(ScratchPad[x], HEX);
|
||||
}
|
||||
|
||||
if (crc_ok)
|
||||
log += F(",OK");
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
}
|
||||
|
||||
if (!crc_ok)
|
||||
{
|
||||
*value = 0;
|
||||
return false;
|
||||
@@ -397,11 +414,11 @@ uint8_t Plugin_004_DS_reset()
|
||||
while (!digitalRead(Plugin_004_DallasPin));
|
||||
|
||||
pinMode(Plugin_004_DallasPin, OUTPUT); digitalWrite(Plugin_004_DallasPin, LOW);
|
||||
delayMicroseconds(492); // Dallas spec. = Min. 480uSec. Arduino 500uSec.
|
||||
delayMicroseconds(480); // Dallas spec. = Min. 480uSec. Arduino 500uSec.
|
||||
pinMode(Plugin_004_DallasPin, INPUT); // Float
|
||||
delayMicroseconds(40);
|
||||
delayMicroseconds(70);
|
||||
r = !digitalRead(Plugin_004_DallasPin);
|
||||
delayMicroseconds(420);
|
||||
delayMicroseconds(410);
|
||||
#if defined(ESP32)
|
||||
ESP32interrupts();
|
||||
#endif
|
||||
|
||||
+142
-116
@@ -9,6 +9,19 @@
|
||||
#define PLUGIN_VALUENAME1_005 "Temperature"
|
||||
#define PLUGIN_VALUENAME2_005 "Humidity"
|
||||
|
||||
#define P005_DHT11 11
|
||||
#define P005_DHT12 12
|
||||
#define P005_DHT22 22
|
||||
#define P005_AM2301 23
|
||||
#define P005_SI7021 70
|
||||
|
||||
#define P005_error_no_reading 1
|
||||
#define P005_error_protocol_timeout 2
|
||||
#define P005_error_checksum_error 3
|
||||
#define P005_error_invalid_NAN_reading 4
|
||||
#define P005_info_temperature 5
|
||||
#define P005_info_humidity 6
|
||||
|
||||
uint8_t Plugin_005_DHT_Pin;
|
||||
|
||||
boolean Plugin_005(byte function, struct EventStruct *event, String& string)
|
||||
@@ -49,7 +62,7 @@ boolean Plugin_005(byte function, struct EventStruct *event, String& string)
|
||||
case PLUGIN_WEBFORM_LOAD:
|
||||
{
|
||||
const String options[] = { F("DHT 11"), F("DHT 22"), F("DHT 12"), F("Sonoff am2301"), F("Sonoff si7021") };
|
||||
int indices[] = { 11, 22, 12, 23, 70 };
|
||||
int indices[] = { P005_DHT11, P005_DHT22, P005_DHT12, P005_AM2301, P005_SI7021 };
|
||||
|
||||
addFormSelector(F("DHT Type"), F("plugin_005_dhttype"), 5, options, indices, Settings.TaskDevicePluginConfig[event->TaskIndex][0] );
|
||||
|
||||
@@ -67,92 +80,7 @@ boolean Plugin_005(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
byte dht_dat[5];
|
||||
byte i;
|
||||
boolean error = false;
|
||||
|
||||
byte Par3 = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
Plugin_005_DHT_Pin = Settings.TaskDevicePin1[event->TaskIndex];
|
||||
|
||||
pinMode(Plugin_005_DHT_Pin, OUTPUT);
|
||||
digitalWrite(Plugin_005_DHT_Pin, LOW); // Pull low
|
||||
if(Par3 == 11 || Par3 == 22 || Par3 == 12) delay(18);
|
||||
else if (Par3 == 23 ) delayMicroseconds(900);
|
||||
else if (Par3 == 70 ) delayMicroseconds(500);
|
||||
pinMode(Plugin_005_DHT_Pin, INPUT); // change pin to input
|
||||
delayMicroseconds(50);
|
||||
|
||||
error = waitState(0);
|
||||
if(error)
|
||||
{ logError(event, F("DHT : no Reading !"));
|
||||
break;
|
||||
}
|
||||
error = waitState(1);
|
||||
if(error)
|
||||
{ logError(event, F("DHT : no Reading !"));
|
||||
break;
|
||||
}
|
||||
noInterrupts();
|
||||
error = waitState(0);
|
||||
if(error)
|
||||
{ logError(event, F("DHT : no Reading !"));
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
byte data = Plugin_005_read_dht_dat();
|
||||
if(data == -1)
|
||||
{ logError(event, F("DHT : protocol timeout!"));
|
||||
break;
|
||||
}
|
||||
dht_dat[i] = data;
|
||||
}
|
||||
interrupts();
|
||||
|
||||
// Checksum calculation is a Rollover Checksum by design!
|
||||
byte dht_check_sum = (dht_dat[0] + dht_dat[1] + dht_dat[2] + dht_dat[3]) & 0xFF; // check check_sum
|
||||
if (dht_dat[4] != dht_check_sum)
|
||||
{
|
||||
logError(event, F("DHT : checksum error!"));
|
||||
break;
|
||||
}
|
||||
|
||||
float temperature = NAN;
|
||||
float humidity = NAN;
|
||||
if (Par3 == 11)
|
||||
{
|
||||
temperature = float(dht_dat[2]); // Temperature
|
||||
humidity = float(dht_dat[0]); // Humidity
|
||||
}
|
||||
else if (Par3 == 12)
|
||||
{
|
||||
temperature = float(dht_dat[2]*10 + (dht_dat[3] & 0x7f)) / 10.0; // Temperature
|
||||
if (dht_dat[3] & 0x80) { temperature = -temperature; } // Negative temperature
|
||||
humidity = float(dht_dat[0]*10+dht_dat[1]) / 10.0; // Humidity
|
||||
}
|
||||
else if (Par3 == 22 || Par3 == 23 || Par3 == 70)
|
||||
{
|
||||
if (dht_dat[2] & 0x80) // negative temperature
|
||||
temperature = -0.1 * word(dht_dat[2] & 0x7F, dht_dat[3]);
|
||||
else
|
||||
temperature = 0.1 * word(dht_dat[2], dht_dat[3]);
|
||||
humidity = 0.1 * word(dht_dat[0], dht_dat[1]); // Humidity
|
||||
}
|
||||
|
||||
if (temperature == NAN || humidity == NAN)
|
||||
{ logError(event, F("DHT : invalid NAN reading !"));
|
||||
break;
|
||||
}
|
||||
|
||||
UserVar[event->BaseVarIndex] = temperature;
|
||||
UserVar[event->BaseVarIndex + 1] = humidity;
|
||||
String log = F("DHT : Temperature: ");
|
||||
log += UserVar[event->BaseVarIndex];
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
log = F("DHT : Humidity: ");
|
||||
log += UserVar[event->BaseVarIndex + 1];
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
success = true;
|
||||
success = P005_do_plugin_read(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -163,28 +91,144 @@ boolean Plugin_005(byte function, struct EventStruct *event, String& string)
|
||||
/*********************************************************************************************\
|
||||
* DHT sub to log an error
|
||||
\*********************************************************************************************/
|
||||
void logError(struct EventStruct *event, String text)
|
||||
void P005_log(struct EventStruct *event, int logNr)
|
||||
{
|
||||
bool isError = true;
|
||||
String text = F("DHT : ");
|
||||
switch (logNr) {
|
||||
case P005_error_no_reading: text += F("No Reading"); break;
|
||||
case P005_error_protocol_timeout: text += F("Protocol Timeout"); break;
|
||||
case P005_error_checksum_error: text += F("Checksum Error"); break;
|
||||
case P005_error_invalid_NAN_reading: text += F("Invalid NAN reading"); break;
|
||||
case P005_info_temperature:
|
||||
text += F("Temperature: ");
|
||||
text += UserVar[event->BaseVarIndex];
|
||||
isError = false;
|
||||
break;
|
||||
case P005_info_humidity:
|
||||
text += F("Humidity: ");
|
||||
text += UserVar[event->BaseVarIndex + 1];
|
||||
isError = false;
|
||||
break;
|
||||
}
|
||||
addLog(LOG_LEVEL_INFO, text);
|
||||
UserVar[event->BaseVarIndex] = NAN;
|
||||
UserVar[event->BaseVarIndex + 1] = NAN;
|
||||
if (isError) {
|
||||
UserVar[event->BaseVarIndex] = NAN;
|
||||
UserVar[event->BaseVarIndex + 1] = NAN;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* DHT sub to wait until a pin is in a certiin state
|
||||
* DHT sub to wait until a pin is in a certain state
|
||||
\*********************************************************************************************/
|
||||
boolean waitState(int state)
|
||||
boolean P005_waitState(int state)
|
||||
{
|
||||
byte counter = 0;
|
||||
while (( digitalRead(Plugin_005_DHT_Pin) != state) && (counter < 100))
|
||||
unsigned long timeout = micros() + 100;
|
||||
while (digitalRead(Plugin_005_DHT_Pin) != state)
|
||||
{
|
||||
if (usecTimeOutReached(timeout)) return false;
|
||||
delayMicroseconds(1);
|
||||
counter++;
|
||||
}
|
||||
if( counter < 100) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Perform the actual reading + interpreting of data.
|
||||
\*********************************************************************************************/
|
||||
bool P005_do_plugin_read(struct EventStruct *event) {
|
||||
byte i;
|
||||
|
||||
byte Par3 = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
Plugin_005_DHT_Pin = Settings.TaskDevicePin1[event->TaskIndex];
|
||||
|
||||
pinMode(Plugin_005_DHT_Pin, OUTPUT);
|
||||
digitalWrite(Plugin_005_DHT_Pin, LOW); // Pull low
|
||||
switch (Par3) {
|
||||
case P005_DHT11:
|
||||
case P005_DHT22:
|
||||
case P005_DHT12: delay(18); break; // FIXME TD-er: Must this be so long?
|
||||
case P005_AM2301: delayMicroseconds(900); break;
|
||||
case P005_SI7021: delayMicroseconds(500); break;
|
||||
}
|
||||
switch (Par3) {
|
||||
case P005_DHT11:
|
||||
case P005_DHT22:
|
||||
case P005_DHT12:
|
||||
case P005_AM2301:
|
||||
pinMode(Plugin_005_DHT_Pin, INPUT);
|
||||
delayMicroseconds(50);
|
||||
break;
|
||||
case P005_SI7021:
|
||||
// See: https://github.com/letscontrolit/ESPEasy/issues/1798
|
||||
digitalWrite(Plugin_005_DHT_Pin, HIGH);
|
||||
delayMicroseconds(20);
|
||||
pinMode(Plugin_005_DHT_Pin, INPUT);
|
||||
break;
|
||||
}
|
||||
if(!P005_waitState(0)) {P005_log(event, P005_error_no_reading); return false; }
|
||||
if(!P005_waitState(1)) {P005_log(event, P005_error_no_reading); return false; }
|
||||
noInterrupts();
|
||||
if(!P005_waitState(0)) {P005_log(event, P005_error_no_reading); return false; }
|
||||
bool readingAborted = false;
|
||||
byte dht_dat[5];
|
||||
for (i = 0; i < 5 && !readingAborted; i++)
|
||||
{
|
||||
byte data = Plugin_005_read_dht_dat();
|
||||
if(data == -1)
|
||||
{ P005_log(event, P005_error_protocol_timeout);
|
||||
readingAborted = true;
|
||||
}
|
||||
dht_dat[i] = data;
|
||||
}
|
||||
interrupts();
|
||||
if (readingAborted)
|
||||
return false;
|
||||
|
||||
// Checksum calculation is a Rollover Checksum by design!
|
||||
byte dht_check_sum = (dht_dat[0] + dht_dat[1] + dht_dat[2] + dht_dat[3]) & 0xFF; // check check_sum
|
||||
if (dht_dat[4] != dht_check_sum)
|
||||
{
|
||||
P005_log(event, P005_error_checksum_error);
|
||||
return false;
|
||||
}
|
||||
|
||||
float temperature = NAN;
|
||||
float humidity = NAN;
|
||||
switch (Par3) {
|
||||
case P005_DHT11:
|
||||
temperature = float(dht_dat[2]); // Temperature
|
||||
humidity = float(dht_dat[0]); // Humidity
|
||||
break;
|
||||
case P005_DHT12:
|
||||
temperature = float(dht_dat[2]*10 + (dht_dat[3] & 0x7f)) / 10.0; // Temperature
|
||||
if (dht_dat[3] & 0x80) { temperature = -temperature; } // Negative temperature
|
||||
humidity = float(dht_dat[0]*10+dht_dat[1]) / 10.0; // Humidity
|
||||
break;
|
||||
case P005_DHT22:
|
||||
case P005_AM2301:
|
||||
case P005_SI7021:
|
||||
if (dht_dat[2] & 0x80) // negative temperature
|
||||
temperature = -0.1 * word(dht_dat[2] & 0x7F, dht_dat[3]);
|
||||
else
|
||||
temperature = 0.1 * word(dht_dat[2], dht_dat[3]);
|
||||
humidity = 0.1 * word(dht_dat[0], dht_dat[1]); // Humidity
|
||||
break;
|
||||
}
|
||||
|
||||
if (temperature == NAN || humidity == NAN)
|
||||
{ P005_log(event, P005_error_invalid_NAN_reading);
|
||||
return false;
|
||||
}
|
||||
|
||||
UserVar[event->BaseVarIndex] = temperature;
|
||||
UserVar[event->BaseVarIndex + 1] = humidity;
|
||||
P005_log(event, P005_info_temperature);
|
||||
P005_log(event, P005_info_humidity);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
* DHT sub to get an 8 bit value from the receiving bitstream
|
||||
\*********************************************************************************************/
|
||||
@@ -192,31 +236,13 @@ int Plugin_005_read_dht_dat(void)
|
||||
{
|
||||
byte i = 0;
|
||||
byte result = 0;
|
||||
byte counter = 0;
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
while ((!digitalRead(Plugin_005_DHT_Pin)) && (counter < 100))
|
||||
{
|
||||
delayMicroseconds(1);
|
||||
counter++;
|
||||
}
|
||||
if (counter >= 100)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (!P005_waitState(1)) return -1;
|
||||
delayMicroseconds(35); // was 30
|
||||
if (digitalRead(Plugin_005_DHT_Pin))
|
||||
result |= (1 << (7 - i));
|
||||
counter = 0;
|
||||
while ((digitalRead(Plugin_005_DHT_Pin)) && (counter < 100))
|
||||
{
|
||||
delayMicroseconds(1);
|
||||
counter++;
|
||||
}
|
||||
if (counter >= 100)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (!P005_waitState(0)) return -1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+293
-16
@@ -3,15 +3,52 @@
|
||||
//#################################### Plugin 009: MCP23017 input #######################################
|
||||
//#######################################################################################################
|
||||
|
||||
/**************************************************\
|
||||
CONFIG
|
||||
TaskDevicePluginConfig settings:
|
||||
0: send boot state (true,false)
|
||||
1:
|
||||
2:
|
||||
3:
|
||||
4: use doubleclick (0,1,2,3)
|
||||
5: use longpress (0,1,2,3)
|
||||
6: LP fired (true,false)
|
||||
7: doubleclick counter (=0,1,2,3)
|
||||
|
||||
TaskDevicePluginConfigFloat settings:
|
||||
0: debounce interval ms
|
||||
1: doubleclick interval ms
|
||||
2: longpress interval ms
|
||||
3:
|
||||
|
||||
TaskDevicePluginConfigLong settings:
|
||||
0: clickTime debounce ms
|
||||
1: clickTime doubleclick ms
|
||||
2: clickTime longpress ms
|
||||
3:
|
||||
\**************************************************/
|
||||
|
||||
#define PLUGIN_009
|
||||
#define PLUGIN_ID_009 9
|
||||
#define PLUGIN_NAME_009 "Switch input - MCP23017"
|
||||
#define PLUGIN_VALUENAME1_009 "Switch"
|
||||
#define PLUGIN_009_DOUBLECLICK_MIN_INTERVAL 1000
|
||||
#define PLUGIN_009_DOUBLECLICK_MAX_INTERVAL 3000
|
||||
#define PLUGIN_009_LONGPRESS_MIN_INTERVAL 1000
|
||||
#define PLUGIN_009_LONGPRESS_MAX_INTERVAL 5000
|
||||
#define PLUGIN_009_DC_DISABLED 0
|
||||
#define PLUGIN_009_DC_LOW 1
|
||||
#define PLUGIN_009_DC_HIGH 2
|
||||
#define PLUGIN_009_DC_BOTH 3
|
||||
#define PLUGIN_009_LONGPRESS_DISABLED 0
|
||||
#define PLUGIN_009_LONGPRESS_LOW 1
|
||||
#define PLUGIN_009_LONGPRESS_HIGH 2
|
||||
#define PLUGIN_009_LONGPRESS_BOTH 3
|
||||
|
||||
boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
static byte switchstate[TASKS_MAX];
|
||||
static int8_t switchstate[TASKS_MAX];
|
||||
|
||||
switch (function)
|
||||
{
|
||||
@@ -20,10 +57,10 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_009;
|
||||
Device[deviceCount].Type = DEVICE_TYPE_I2C;
|
||||
Device[deviceCount].VType = SENSOR_TYPE_SINGLE;
|
||||
Device[deviceCount].VType = SENSOR_TYPE_SWITCH;
|
||||
Device[deviceCount].Ports = 16;
|
||||
Device[deviceCount].PullUpOption = false;
|
||||
Device[deviceCount].InverseLogicOption = false;
|
||||
Device[deviceCount].InverseLogicOption = true;
|
||||
Device[deviceCount].FormulaOption = false;
|
||||
Device[deviceCount].ValueCount = 1;
|
||||
Device[deviceCount].SendDataOption = true;
|
||||
@@ -49,6 +86,42 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
addFormCheckBox(F("Send Boot state") ,F("plugin_009_boot"), Settings.TaskDevicePluginConfig[event->TaskIndex][0]);
|
||||
|
||||
//@giig1967-20181022
|
||||
addFormSubHeader(F("Advanced event management"));
|
||||
|
||||
addFormNumericBox(F("De-bounce (ms)"), F("plugin_009_debounce"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0]), 0, 250);
|
||||
|
||||
//set minimum value for doubleclick MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_009_DOUBLECLICK_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_009_DOUBLECLICK_MIN_INTERVAL;
|
||||
|
||||
byte choiceDC = Settings.TaskDevicePluginConfig[event->TaskIndex][4];
|
||||
String buttonDC[4];
|
||||
buttonDC[0] = F("Disabled");
|
||||
buttonDC[1] = F("Active only on LOW (EVENT=3)");
|
||||
buttonDC[2] = F("Active only on HIGH (EVENT=3)");
|
||||
buttonDC[3] = F("Active on LOW & HIGH (EVENT=3)");
|
||||
int buttonDCValues[4] = {PLUGIN_009_DC_DISABLED, PLUGIN_009_DC_LOW, PLUGIN_009_DC_HIGH,PLUGIN_009_DC_BOTH};
|
||||
addFormSelector(F("Doubleclick event"), F("plugin_009_dc"), 4, buttonDC, buttonDCValues, choiceDC);
|
||||
|
||||
addFormNumericBox(F("Doubleclick max. interval (ms)"), F("plugin_009_dcmaxinterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1]), PLUGIN_009_DOUBLECLICK_MIN_INTERVAL, PLUGIN_009_DOUBLECLICK_MAX_INTERVAL);
|
||||
|
||||
//set minimum value for longpress MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_009_LONGPRESS_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_009_LONGPRESS_MIN_INTERVAL;
|
||||
|
||||
byte choiceLP = Settings.TaskDevicePluginConfig[event->TaskIndex][5];
|
||||
String buttonLP[4];
|
||||
buttonLP[0] = F("Disabled");
|
||||
buttonLP[1] = F("Active only on LOW (EVENT= 10 [NORMAL] or 11 [INVERSED])");
|
||||
buttonLP[2] = F("Active only on HIGH (EVENT= 11 [NORMAL] or 10 [INVERSED])");
|
||||
buttonLP[3] = F("Active on LOW & HIGH (EVENT= 10 or 11)");
|
||||
|
||||
int buttonLPValues[4] = {PLUGIN_009_LONGPRESS_DISABLED, PLUGIN_009_LONGPRESS_LOW, PLUGIN_009_LONGPRESS_HIGH,PLUGIN_009_LONGPRESS_BOTH};
|
||||
addFormSelector(F("Longpress event"), F("plugin_009_lp"), 4, buttonLP, buttonLPValues, choiceLP);
|
||||
|
||||
addFormNumericBox(F("Longpress min. interval (ms)"), F("plugin_009_lpmininterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]), PLUGIN_009_LONGPRESS_MIN_INTERVAL, PLUGIN_009_LONGPRESS_MAX_INTERVAL);
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
@@ -57,6 +130,15 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = isFormItemChecked(F("plugin_009_boot"));
|
||||
|
||||
//@giig1967-20181022
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0] = getFormItemInt(F("plugin_009_debounce"));
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][4] = getFormItemInt(F("plugin_009_dc"));
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = getFormItemInt(F("plugin_009_dcmaxinterval"));
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][5] = getFormItemInt(F("plugin_009_lp"));
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = getFormItemInt(F("plugin_009_lpmininterval"));
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
@@ -67,32 +149,185 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
Plugin_009_Config(Settings.TaskDevicePort[event->TaskIndex], 1);
|
||||
|
||||
// read and store current state to prevent switching at boot time
|
||||
// "state" could be -1, 0 or 1
|
||||
switchstate[event->TaskIndex] = Plugin_009_Read(Settings.TaskDevicePort[event->TaskIndex]);
|
||||
|
||||
// if boot state must be send, inverse default state
|
||||
// this is done to force the trigger in PLUGIN_TEN_PER_SECOND
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0])
|
||||
switchstate[event->TaskIndex] = !switchstate[event->TaskIndex];
|
||||
|
||||
// @giig1967g-20181022: set initial UserVar of the switch
|
||||
if (switchstate[event->TaskIndex] != -1 && Settings.TaskDevicePin1Inversed[event->TaskIndex]) {
|
||||
UserVar[event->BaseVarIndex] = !switchstate[event->TaskIndex];
|
||||
} else {
|
||||
UserVar[event->BaseVarIndex] = switchstate[event->TaskIndex];
|
||||
}
|
||||
|
||||
// @giig1967g-20181022: doubleclick counter = 0
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0;
|
||||
|
||||
// @giig1967g-20181022: used to track if LP has fired
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6]=false;
|
||||
|
||||
// @giig1967g-20181022: store millis for debounce, doubleclick and long press
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]=millis(); //debounce timer
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis(); //doubleclick timer
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis(); //longpress timer
|
||||
|
||||
// @giig1967g-20181022: set minimum value for doubleclick MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_009_DOUBLECLICK_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_009_DOUBLECLICK_MIN_INTERVAL;
|
||||
|
||||
// @giig1967g-20181022: set minimum value for longpress MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_009_LONGPRESS_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_009_LONGPRESS_MIN_INTERVAL;
|
||||
|
||||
setPinState(PLUGIN_ID_009, Settings.TaskDevicePort[event->TaskIndex], PIN_MODE_INPUT, 0);
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_TEN_PER_SECOND:
|
||||
{
|
||||
int state = Plugin_009_Read(Settings.TaskDevicePort[event->TaskIndex]);
|
||||
if (state != -1)
|
||||
{
|
||||
if (state != switchstate[event->TaskIndex])
|
||||
{
|
||||
String log = F("MCP : State ");
|
||||
log += state;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
switchstate[event->TaskIndex] = state;
|
||||
UserVar[event->BaseVarIndex] = state;
|
||||
event->sensorType = SENSOR_TYPE_SWITCH;
|
||||
sendData(event);
|
||||
const int8_t state = Plugin_009_Read(Settings.TaskDevicePort[event->TaskIndex]);
|
||||
/**************************************************************************\
|
||||
20181022 - @giig1967g: new doubleclick logic is:
|
||||
if there is a 'state' change, check debounce period.
|
||||
Then if doubleclick interval exceeded, reset Settings.TaskDevicePluginConfig[event->TaskIndex][7] to 0
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7] contains the current status for doubleclick:
|
||||
0: start counting
|
||||
1: 1st click
|
||||
2: 2nd click
|
||||
3: 3rd click = doubleclick event if inside interval (calculated as: '3rd click time' minus '1st click time')
|
||||
|
||||
Returned EVENT value is = 3 always for doubleclick
|
||||
In rules this can be checked:
|
||||
on Button#Switch=3 do //will fire if doubleclick
|
||||
\**************************************************************************/
|
||||
if (state != -1) {
|
||||
if (state != switchstate[event->TaskIndex]) {
|
||||
//@giig1967g20181022: reset timer for long press
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis();
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = false;
|
||||
|
||||
const unsigned long debounceTime = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]);
|
||||
if (debounceTime >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0])) //de-bounce check
|
||||
{
|
||||
const unsigned long deltaDC = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]);
|
||||
if ((deltaDC >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1])) ||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3)
|
||||
{
|
||||
//reset timer for doubleclick
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0;
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis();
|
||||
}
|
||||
|
||||
//just to simplify the reading of the code
|
||||
#define COUNTER Settings.TaskDevicePluginConfig[event->TaskIndex][7]
|
||||
#define DC Settings.TaskDevicePluginConfig[event->TaskIndex][4]
|
||||
|
||||
//check settings for doubleclick according to the settings
|
||||
if ( COUNTER!=0 || ( COUNTER==0 && (DC==3 || (DC==1 && state==0) || (DC==2 && state==1))) )
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]++;
|
||||
#undef DC
|
||||
#undef COUNTER
|
||||
|
||||
switchstate[event->TaskIndex] = state;
|
||||
|
||||
byte output_value;
|
||||
boolean sendState = switchstate[event->TaskIndex];
|
||||
|
||||
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
|
||||
sendState = !sendState;
|
||||
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3 && Settings.TaskDevicePluginConfig[event->TaskIndex][4]>0)
|
||||
{
|
||||
output_value = 3; //double click
|
||||
} else {
|
||||
output_value = sendState ? 1 : 0; //single click
|
||||
}
|
||||
|
||||
UserVar[event->BaseVarIndex] = output_value;
|
||||
|
||||
String log = F("MCP : Port=");
|
||||
log += Settings.TaskDevicePort[event->TaskIndex];
|
||||
log += F(" State=");
|
||||
log += state;
|
||||
log += output_value==3 ? F(" Doubleclick=") : F(" Output value=");
|
||||
log += output_value;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
|
||||
event->sensorType = SENSOR_TYPE_SWITCH;
|
||||
sendData(event);
|
||||
|
||||
//reset Userdata so it displays the correct state value in the web page
|
||||
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
|
||||
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0] = millis();
|
||||
}
|
||||
}
|
||||
|
||||
//just to simplify the reading of the code
|
||||
#define LP Settings.TaskDevicePluginConfig[event->TaskIndex][5]
|
||||
#define FIRED Settings.TaskDevicePluginConfig[event->TaskIndex][6]
|
||||
|
||||
//check if LP is enabled and if LP has not fired yet
|
||||
else if (!FIRED && (LP==3 ||(LP==1 && state==0)||(LP==2 && state==1) ) ) {
|
||||
|
||||
#undef LP
|
||||
#undef FIRED
|
||||
|
||||
/**************************************************************************\
|
||||
20181022 - @giig1967g: new longpress logic is:
|
||||
if there is no 'state' change, check if longpress interval reached
|
||||
When reached send longpress event.
|
||||
Returned Event value = state + 10
|
||||
So if state = 0 => EVENT longpress = 10
|
||||
if state = 1 => EVENT longpress = 11
|
||||
So we can trigger longpress for high or low contact
|
||||
|
||||
In rules this can be checked:
|
||||
on Button#Switch=10 do //will fire if longpress when state = 0
|
||||
on Button#Switch=11 do //will fire if longpress when state = 1
|
||||
\**************************************************************************/
|
||||
const unsigned long deltaLP = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]);
|
||||
if (deltaLP >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]))
|
||||
{
|
||||
byte output_value;
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = true; //fired = true
|
||||
|
||||
boolean sendState = state;
|
||||
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
|
||||
sendState = !sendState;
|
||||
|
||||
output_value = sendState ? 1 : 0;
|
||||
output_value = output_value + 10;
|
||||
|
||||
UserVar[event->BaseVarIndex] = output_value;
|
||||
String log = F("MCP : LongPress: Port=");
|
||||
log += Settings.TaskDevicePort[event->TaskIndex];
|
||||
log += F(" State=");
|
||||
log += state ? '1' : '0';
|
||||
log += F(" Output value=");
|
||||
log += output_value;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
sendData(event);
|
||||
|
||||
//reset Userdata so it displays the correct state value in the web page
|
||||
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
|
||||
}
|
||||
}
|
||||
} else if (state != switchstate[event->TaskIndex]) {
|
||||
//set UserVar and switchState = -1 and send EVENT to notify user
|
||||
UserVar[event->BaseVarIndex] = state;
|
||||
switchstate[event->TaskIndex] = state;
|
||||
String log = F("MCP : Port=");
|
||||
log += Settings.TaskDevicePort[event->TaskIndex];
|
||||
log += F(" is offline (EVENT= -1)");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
sendData(event);
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
@@ -102,13 +337,33 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
// We do not actually read the pin state as this is already done 10x/second
|
||||
// Instead we just send the last known state stored in Uservar
|
||||
String log = F("MCP : State ");
|
||||
String log = F("MCP : Port=");
|
||||
log += Settings.TaskDevicePort[event->TaskIndex];
|
||||
log += F(" State=");
|
||||
log += UserVar[event->BaseVarIndex];
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_REQUEST:
|
||||
{
|
||||
//parseString(string, 1) = device
|
||||
//parseString(string, 2) = command
|
||||
//parseString(string, 3) = gpio number
|
||||
|
||||
// returns pin value using syntax: [plugin#mcpgpio#pinstate#xx]
|
||||
if (string.length()>=16 && string.substring(0,16).equalsIgnoreCase(F("mcpgpio,pinstate")))
|
||||
{
|
||||
int par1;
|
||||
if (validIntFromString(parseString(string, 3), par1)) {
|
||||
string = Plugin_009_Read(par1);
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WRITE:
|
||||
{
|
||||
String log = "";
|
||||
@@ -124,6 +379,28 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
|
||||
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_009, event->Par1, log, 0));
|
||||
}
|
||||
|
||||
if (command == F("mcpgpiotoggle"))
|
||||
{
|
||||
success = true;
|
||||
byte mode;
|
||||
uint16_t currentState;
|
||||
|
||||
if (hasPinState(PLUGIN_ID_009,event->Par1)) {
|
||||
getPinState(PLUGIN_ID_009, event->Par1, &mode, ¤tState);
|
||||
} else {
|
||||
currentState = Plugin_009_Read(event->Par1);
|
||||
mode = PIN_MODE_OUTPUT;
|
||||
}
|
||||
|
||||
if (mode != PIN_MODE_INPUT) {
|
||||
setPinState(PLUGIN_ID_009, event->Par1, PIN_MODE_OUTPUT, !currentState);
|
||||
Plugin_009_Write(event->Par1, !currentState);
|
||||
log = String(F("MCP : Toggle GPIO ")) + String(event->Par1) + String(F(" Set to ")) + String(!currentState);
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_009, event->Par1, log, 0));
|
||||
}
|
||||
}
|
||||
|
||||
if (command == F("mcppulse"))
|
||||
{
|
||||
success = true;
|
||||
|
||||
+17
-12
@@ -21,6 +21,9 @@ int Plugin_012_mode = 1;
|
||||
#define PLUGIN_NAME_012 "Display - LCD2004"
|
||||
#define PLUGIN_VALUENAME1_012 "LCD"
|
||||
|
||||
#define P12_Nlines 4 // The number of different lines which can be displayed
|
||||
#define P12_Nchars 80
|
||||
|
||||
boolean Plugin_012(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
@@ -81,9 +84,9 @@ boolean Plugin_012(byte function, struct EventStruct *event, String& string)
|
||||
addFormSelector(F("Display Size"), F("plugin_012_size"), 2, options2, optionValues2, choice2);
|
||||
|
||||
|
||||
char deviceTemplate[4][80];
|
||||
char deviceTemplate[P12_Nlines][P12_Nchars];
|
||||
LoadCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
|
||||
for (byte varNr = 0; varNr < 4; varNr++)
|
||||
for (byte varNr = 0; varNr < P12_Nlines; varNr++)
|
||||
{
|
||||
addHtml(F("<TR><TD>Line "));
|
||||
addHtml(String(varNr + 1));
|
||||
@@ -122,17 +125,19 @@ boolean Plugin_012(byte function, struct EventStruct *event, String& string)
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][2] = getFormItemInt(F("plugin_12_timer"));
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][3] = getFormItemInt(F("plugin_012_mode"));
|
||||
|
||||
char deviceTemplate[4][80];
|
||||
for (byte varNr = 0; varNr < 4; varNr++)
|
||||
char deviceTemplate[P12_Nlines][P12_Nchars];
|
||||
String error;
|
||||
for (byte varNr = 0; varNr < P12_Nlines; varNr++)
|
||||
{
|
||||
char argc[25];
|
||||
String arg = F("Plugin_012_template");
|
||||
arg += varNr + 1;
|
||||
arg.toCharArray(argc, 25);
|
||||
String tmpString = WebServer.arg(argc);
|
||||
strncpy(deviceTemplate[varNr], tmpString.c_str(), sizeof(deviceTemplate[varNr]));
|
||||
String argName = F("Plugin_012_template");
|
||||
argName += varNr + 1;
|
||||
if (!safe_strncpy(deviceTemplate[varNr], WebServer.arg(argName), P12_Nchars)) {
|
||||
error += getCustomTaskSettingsError(varNr);
|
||||
}
|
||||
}
|
||||
if (error.length() > 0) {
|
||||
addHtmlError(error);
|
||||
}
|
||||
|
||||
SaveCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
|
||||
success = true;
|
||||
break;
|
||||
@@ -194,7 +199,7 @@ boolean Plugin_012(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
char deviceTemplate[4][80];
|
||||
char deviceTemplate[P12_Nlines][P12_Nchars];
|
||||
LoadCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
|
||||
|
||||
for (byte x = 0; x < Plugin_012_rows; x++)
|
||||
|
||||
+199
-58
@@ -12,27 +12,21 @@
|
||||
#include <map>
|
||||
#include <NewPingESP8266.h>
|
||||
|
||||
struct P_013_sensordef {
|
||||
P_013_sensordef() : sonar(NULL) {}
|
||||
|
||||
P_013_sensordef(byte TRIG_Pin, byte IRQ_Pin, int16_t max_cm_distance) : sonar(NULL) {
|
||||
sonar = new NewPingESP8266(TRIG_Pin, IRQ_Pin, max_cm_distance);
|
||||
}
|
||||
|
||||
~P_013_sensordef() {
|
||||
if (sonar != NULL) {
|
||||
delete sonar;
|
||||
sonar = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
NewPingESP8266 *sonar;
|
||||
};
|
||||
|
||||
std::map<unsigned int, P_013_sensordef> P_013_sensordefs;
|
||||
// PlugIn specific defines
|
||||
// operatingMode
|
||||
#define OPMODE_VALUE (0)
|
||||
#define OPMODE_STATE (1)
|
||||
|
||||
// measuringUnit
|
||||
#define UNIT_CM (0)
|
||||
#define UNIT_INCH (1)
|
||||
|
||||
// filterType
|
||||
#define FILTER_NONE (0)
|
||||
#define FILTER_MEDIAN (1)
|
||||
|
||||
// map of sensors
|
||||
std::map<unsigned int, std::shared_ptr<NewPingESP8266> > P_013_sensordefs;
|
||||
|
||||
boolean Plugin_013(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
@@ -54,6 +48,7 @@ boolean Plugin_013(byte function, struct EventStruct *event, String& string)
|
||||
Device[deviceCount].SendDataOption = true;
|
||||
Device[deviceCount].TimerOption = true;
|
||||
Device[deviceCount].GlobalSyncOption = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -72,19 +67,51 @@ boolean Plugin_013(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_WEBFORM_LOAD:
|
||||
{
|
||||
byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
String options[2];
|
||||
options[0] = F("Value");
|
||||
options[1] = F("State");
|
||||
int optionValues[2] = { 1, 2 };
|
||||
addFormSelector(F("Mode"), F("plugin_013_mode"), 2, options, optionValues, choice);
|
||||
int16_t operatingMode = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
int16_t threshold = Settings.TaskDevicePluginConfig[event->TaskIndex][1];
|
||||
int16_t max_distance = Settings.TaskDevicePluginConfig[event->TaskIndex][2];
|
||||
int16_t measuringUnit = Settings.TaskDevicePluginConfig[event->TaskIndex][3];
|
||||
int16_t filterType = Settings.TaskDevicePluginConfig[event->TaskIndex][4];
|
||||
int16_t filterSize = Settings.TaskDevicePluginConfig[event->TaskIndex][5];
|
||||
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0] == 2)
|
||||
{
|
||||
addFormNumericBox(F("Threshold"), F("plugin_013_threshold"), Settings.TaskDevicePluginConfig[event->TaskIndex][1]);
|
||||
// default filtersize = 5
|
||||
if (filterSize == 0) {
|
||||
filterSize = 5;
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][5] = filterSize;
|
||||
}
|
||||
addFormNumericBox(F("Max Distance"), F("plugin_013_max_distance"), Settings.TaskDevicePluginConfig[event->TaskIndex][2], 0, 500);
|
||||
addUnit(F("cm"));
|
||||
|
||||
|
||||
String strUnit = (measuringUnit == UNIT_CM) ? F("cm") : F("inch");
|
||||
|
||||
String optionsOpMode[2];
|
||||
int optionValuesOpMode[2] = { 0, 1 };
|
||||
optionsOpMode[0] = F("Value");
|
||||
optionsOpMode[1] = F("State");
|
||||
addFormSelector(F("Mode"), F("plugin_013_mode"), 2, optionsOpMode, optionValuesOpMode, operatingMode);
|
||||
|
||||
if (operatingMode == OPMODE_STATE)
|
||||
{
|
||||
addFormNumericBox(F("Threshold"), F("plugin_013_threshold"), threshold);
|
||||
addUnit(strUnit);
|
||||
}
|
||||
addFormNumericBox(F("Max Distance"), F("plugin_013_max_distance"), max_distance, 0, 500);
|
||||
addUnit(strUnit);
|
||||
|
||||
String optionsUnit[2];
|
||||
int optionValuesUnit[2] = { 0, 1 };
|
||||
optionsUnit[0] = F("Metric");
|
||||
optionsUnit[1] = F("Imperial");
|
||||
addFormSelector(F("Unit"), F("plugin_013_Unit"), 2, optionsUnit, optionValuesUnit, measuringUnit);
|
||||
|
||||
String optionsFilter[2];
|
||||
int optionValuesFilter[2] = { 0, 1 };
|
||||
optionsFilter[0] = F("None");
|
||||
optionsFilter[1] = F("Median");
|
||||
addFormSelector(F("Filter"), F("plugin_013_FilterType"), 2, optionsFilter, optionValuesFilter, filterType);
|
||||
|
||||
// enable filtersize option if filter is used,
|
||||
if (filterType != FILTER_NONE)
|
||||
addFormNumericBox(F("Filter size"), F("plugin_013_FilterSize"), filterSize, 2, 20);
|
||||
|
||||
success = true;
|
||||
break;
|
||||
@@ -92,33 +119,60 @@ boolean Plugin_013(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_WEBFORM_SAVE:
|
||||
{
|
||||
int16_t operatingMode = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
int16_t filterType = Settings.TaskDevicePluginConfig[event->TaskIndex][4];
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = getFormItemInt(F("plugin_013_mode"));
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0] == 2)
|
||||
{
|
||||
if (operatingMode == OPMODE_STATE)
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][1] = getFormItemInt(F("plugin_013_threshold"));
|
||||
}
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][2] = getFormItemInt(F("plugin_013_max_distance"));
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][3] = getFormItemInt(F("plugin_013_Unit"));
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][4] = getFormItemInt(F("plugin_013_FilterType"));
|
||||
if (filterType != FILTER_NONE)
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][5] = getFormItemInt(F("plugin_013_FilterSize"));
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
byte Plugin_013_TRIG_Pin = Settings.TaskDevicePin1[event->TaskIndex];
|
||||
byte Plugin_013_IRQ_Pin = Settings.TaskDevicePin2[event->TaskIndex];
|
||||
int16_t max_cm_distance = Settings.TaskDevicePluginConfig[event->TaskIndex][2];
|
||||
int16_t max_distance = Settings.TaskDevicePluginConfig[event->TaskIndex][2];
|
||||
int16_t measuringUnit = Settings.TaskDevicePluginConfig[event->TaskIndex][3];
|
||||
int16_t filterType = Settings.TaskDevicePluginConfig[event->TaskIndex][4];
|
||||
int16_t filterSize = Settings.TaskDevicePluginConfig[event->TaskIndex][5];
|
||||
|
||||
int8_t Plugin_013_TRIG_Pin = Settings.TaskDevicePin1[event->TaskIndex];
|
||||
int8_t Plugin_013_IRQ_Pin = Settings.TaskDevicePin2[event->TaskIndex];
|
||||
int16_t max_distance_cm = (measuringUnit == UNIT_CM) ? max_distance : (float)max_distance * 2.54f;
|
||||
|
||||
// create sensor instance and add to std::map
|
||||
P_013_sensordefs.erase(event->TaskIndex);
|
||||
P_013_sensordefs[event->TaskIndex] =
|
||||
P_013_sensordef(Plugin_013_TRIG_Pin, Plugin_013_IRQ_Pin, max_cm_distance);
|
||||
std::shared_ptr<NewPingESP8266> (new NewPingESP8266(Plugin_013_TRIG_Pin, Plugin_013_IRQ_Pin, max_distance_cm));
|
||||
|
||||
String log = F("ULTRASONIC : TaskNr: ");
|
||||
log += event->TaskIndex +1;
|
||||
log += F(" TrigPin: ");
|
||||
log += Plugin_013_TRIG_Pin;
|
||||
log += F(" IRQ_Pin: ");
|
||||
log += Plugin_013_IRQ_Pin;
|
||||
log += F(" max dist cm: ");
|
||||
log += max_cm_distance;
|
||||
log += F(" max dist ");
|
||||
log += (measuringUnit == UNIT_CM) ? F("[cm]: ") : F("[inch]: ");
|
||||
log += max_distance;
|
||||
log += F(" max echo: ");
|
||||
log += P_013_sensordefs[event->TaskIndex].sonar->getMaxEchoTime();
|
||||
log += P_013_sensordefs[event->TaskIndex]->getMaxEchoTime();
|
||||
log += F(" Filter: ");
|
||||
if (filterType == FILTER_NONE)
|
||||
log += F("none");
|
||||
else
|
||||
if (filterType == FILTER_MEDIAN) {
|
||||
log += F("Median size: ");
|
||||
log += filterSize;
|
||||
}
|
||||
else
|
||||
log += F("invalid!");
|
||||
log += F(" nr_tasks: ");
|
||||
log += P_013_sensordefs.size();
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
@@ -136,7 +190,6 @@ boolean Plugin_013(byte function, struct EventStruct *event, String& string)
|
||||
log += F(" usec, ");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
@@ -149,39 +202,48 @@ boolean Plugin_013(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_READ: // If we select value mode, read and send the value based on global timer
|
||||
{
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0] == 1)
|
||||
int16_t operatingMode = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
int16_t measuringUnit = Settings.TaskDevicePluginConfig[event->TaskIndex][3];
|
||||
|
||||
if (operatingMode == OPMODE_VALUE)
|
||||
{
|
||||
float value = Plugin_013_read(event->TaskIndex);
|
||||
String log = F("ULTRASONIC : TaskNr: ");
|
||||
log += event->TaskIndex +1;
|
||||
log += F(" Distance: ");
|
||||
if (value > 0)
|
||||
UserVar[event->BaseVarIndex] = value;
|
||||
log += UserVar[event->BaseVarIndex];
|
||||
log += (measuringUnit == UNIT_CM) ? F(" cm ") : F(" inch ");
|
||||
if (value == NO_ECHO)
|
||||
{
|
||||
UserVar[event->BaseVarIndex] = value;
|
||||
log += UserVar[event->BaseVarIndex];
|
||||
success = true;
|
||||
log += F(" Error: ");
|
||||
log += Plugin_013_getErrorStatusString(event->TaskIndex);
|
||||
}
|
||||
else
|
||||
log += F("No reading!");
|
||||
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_TEN_PER_SECOND: // If we select state mode, do more frequent checks and send only state changes
|
||||
{
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0] == 2)
|
||||
int16_t operatingMode = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
int16_t threshold = Settings.TaskDevicePluginConfig[event->TaskIndex][1];
|
||||
|
||||
if (operatingMode == OPMODE_STATE)
|
||||
{
|
||||
byte state = 0;
|
||||
float value = Plugin_013_read(event->TaskIndex);
|
||||
if (value > 0)
|
||||
if (value != NO_ECHO)
|
||||
{
|
||||
if (value < Settings.TaskDevicePluginConfig[event->TaskIndex][1])
|
||||
if (value < threshold)
|
||||
state = 1;
|
||||
if (state != switchstate[event->TaskIndex])
|
||||
{
|
||||
String log = F("ULTRASONIC : State ");
|
||||
String log = F("ULTRASONIC : TaskNr: ");
|
||||
log += event->TaskIndex +1;
|
||||
log += F(" state: ");
|
||||
log += state;
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
switchstate[event->TaskIndex] = state;
|
||||
@@ -190,6 +252,14 @@ boolean Plugin_013(byte function, struct EventStruct *event, String& string)
|
||||
sendData(event);
|
||||
}
|
||||
}
|
||||
else {
|
||||
String log = F("ULTRASONIC : TaskNr: ");
|
||||
log += event->TaskIndex +1;
|
||||
log += F(" Error: ");
|
||||
log += Plugin_013_getErrorStatusString(event->TaskIndex);
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
}
|
||||
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
@@ -202,11 +272,82 @@ boolean Plugin_013(byte function, struct EventStruct *event, String& string)
|
||||
float Plugin_013_read(unsigned int taskIndex)
|
||||
/*********************************************************************/
|
||||
{
|
||||
if (P_013_sensordefs.count(taskIndex) == 0) return 0;
|
||||
if (P_013_sensordefs[taskIndex].sonar == NULL) return 0;
|
||||
delay(1);
|
||||
float distance = P_013_sensordefs[taskIndex].sonar->ping_cm();
|
||||
delay(1);
|
||||
return distance;
|
||||
if (P_013_sensordefs.count(taskIndex) == 0)
|
||||
return 0;
|
||||
|
||||
int16_t max_distance = Settings.TaskDevicePluginConfig[taskIndex][2];
|
||||
int16_t measuringUnit = Settings.TaskDevicePluginConfig[taskIndex][3];
|
||||
int16_t filterType = Settings.TaskDevicePluginConfig[taskIndex][4];
|
||||
int16_t filterSize = Settings.TaskDevicePluginConfig[taskIndex][5];
|
||||
int16_t max_distance_cm = (measuringUnit == UNIT_CM) ? max_distance : (float)max_distance * 2.54f;
|
||||
|
||||
unsigned int echoTime = 0;
|
||||
|
||||
switch (filterType) {
|
||||
case FILTER_NONE:
|
||||
echoTime = (P_013_sensordefs[taskIndex])->ping();
|
||||
break;
|
||||
case FILTER_MEDIAN:
|
||||
echoTime = (P_013_sensordefs[taskIndex])->ping_median(filterSize, max_distance_cm);
|
||||
break;
|
||||
default:
|
||||
addLog(LOG_LEVEL_INFO, F("invalid Filter Type setting!"));
|
||||
}
|
||||
|
||||
if (measuringUnit == UNIT_CM)
|
||||
return NewPingESP8266::convert_cm_F(echoTime);
|
||||
else
|
||||
return NewPingESP8266::convert_in_F(echoTime);
|
||||
}
|
||||
|
||||
/*********************************************************************/
|
||||
String Plugin_013_getErrorStatusString(unsigned int taskIndex)
|
||||
/*********************************************************************/
|
||||
{
|
||||
if (P_013_sensordefs.count(taskIndex) == 0)
|
||||
return String(F("invalid taskindex"));
|
||||
|
||||
switch ((P_013_sensordefs[taskIndex])->getErrorState()) {
|
||||
case NewPingESP8266::STATUS_SENSOR_READY: {
|
||||
return String(F("Sensor ready"));
|
||||
break;
|
||||
}
|
||||
|
||||
case NewPingESP8266::STATUS_MEASUREMENT_VALID: {
|
||||
return String(F("no error, measurement valid"));
|
||||
break;
|
||||
}
|
||||
|
||||
case NewPingESP8266::STATUS_ECHO_TRIGGERED: {
|
||||
return String(F("Echo triggered, waiting for Echo end"));
|
||||
break;
|
||||
}
|
||||
|
||||
case NewPingESP8266::STATUS_ECHO_STATE_ERROR: {
|
||||
return String(F("Echo pulse error, Echopin not low on trigger"));
|
||||
break;
|
||||
}
|
||||
|
||||
case NewPingESP8266::STATUS_ECHO_START_TIMEOUT_50ms: {
|
||||
return String(F("Echo timeout error, no echo start whithin 50 ms"));
|
||||
break;
|
||||
}
|
||||
|
||||
case NewPingESP8266::STATUS_ECHO_START_TIMEOUT_DISTANCE: {
|
||||
return String(F("Echo timeout error, no echo start whithin time for max. distance"));
|
||||
break;
|
||||
}
|
||||
|
||||
case NewPingESP8266::STATUS_MAX_DISTANCE_EXCEEDED: {
|
||||
return String(F("Echo too late, maximum distance exceeded"));
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
return String(F("unknown error"));
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif // USES_P013
|
||||
|
||||
@@ -41,6 +41,9 @@ decode_results results;
|
||||
#define PLUGIN_NAME_016 "Communication - TSOP4838"
|
||||
#define PLUGIN_VALUENAME1_016 "IR"
|
||||
|
||||
// A lot of the following code has been taken directly (with permission) from the IRrecvDumpV2.ino example code
|
||||
// of the IRremoteESP8266 library. (https://github.com/markszabo/IRremoteESP8266)
|
||||
|
||||
// ==================== start of TUNEABLE PARAMETERS ====================
|
||||
// As this program is a special purpose capture/decoder, let us use a larger
|
||||
// than normal buffer so we can handle Air Conditioner remote codes.
|
||||
@@ -155,13 +158,18 @@ boolean Plugin_016(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
if (irReceiver->decode(&results))
|
||||
{
|
||||
unsigned long IRcode = results.value;
|
||||
irReceiver->resume();
|
||||
UserVar[event->BaseVarIndex] = (IRcode & 0xFFFF);
|
||||
UserVar[event->BaseVarIndex + 1] = ((IRcode >> 16) & 0xFFFF);
|
||||
String log = "IR: ";
|
||||
if (results.overflow)
|
||||
log += F("WARNING: IR code is too big for buffer. This result shouldn't be trusted until this is resolved. Edit & increase CAPTURE_BUFFER_SIZE.\n");
|
||||
// Display the basic output of what we found.
|
||||
log += resultToHumanReadableBasic(&results);
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
displayRawToReadableB32Hex();
|
||||
|
||||
// Display any extra A/C info if we have it.
|
||||
// Display the human readable state of an A/C message if we can.
|
||||
log = "";
|
||||
@@ -230,4 +238,130 @@ boolean Plugin_016(byte function, struct EventStruct *event, String& string)
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
#define PCT_TOLERANCE 7u
|
||||
#define pct_tolerance(v) ((v) / (100u / PCT_TOLERANCE))
|
||||
//#define MIN_TOLERANCE 10u
|
||||
//#define get_tolerance(v) (pct_tolerance(v) > MIN_TOLERANCE? pct_tolerance(v) : MIN_TOLERANCE)
|
||||
#define get_tolerance(v) (pct_tolerance(v))
|
||||
#define MIN_VIABLE_DIV 40u
|
||||
#define to_32hex(c) ((c) < 10 ? (c) + '0' : (c) + 'A' - 10)
|
||||
|
||||
// This function attempts to convert the raw IR timings buffer to a short string that can be sent over as
|
||||
// an IRSEND HTTP/MQTT command. It analyzes the timings, and searches for a common denominator which can be
|
||||
// used to compress the values. If found, it then produces a string consisting of B32 Hex digit for each
|
||||
// timing value, appended by the denominators for Pulse and Blank. This string can then be used in an
|
||||
// IRSEND command. An important advantage of this string over the current IRSEND RAW B32 format implemented
|
||||
// by GusPS is that it allows easy inspections and modifications after the code is constructed.
|
||||
//
|
||||
// Author: Gilad Raz (jazzgil) 23sep2018
|
||||
|
||||
void displayRawToReadableB32Hex() {
|
||||
String line;
|
||||
uint16_t div[2];
|
||||
|
||||
// print the values: either pulses or blanks
|
||||
for (uint16_t i = 1; i < results.rawlen; i++)
|
||||
line += uint64ToString(results.rawbuf[i] * RAWTICK, 10) + ",";
|
||||
addLog(LOG_LEVEL_DEBUG, line);
|
||||
|
||||
// Find a common denominator divisor for odd indexes (pulses) and then even indexes (blanks).
|
||||
for (uint16_t p = 0; p < 2; p++) {
|
||||
uint16_t cd = 0xFFFFU; // current divisor
|
||||
// find the lowest value to start the divisor with.
|
||||
for (uint16_t i = 1 + p; i < results.rawlen; i += 2) {
|
||||
uint16_t val = results.rawbuf[i] * RAWTICK;
|
||||
if (cd > val) cd = val;
|
||||
}
|
||||
|
||||
uint16_t bstDiv = -1, bstAvg = 0xFFFFU;
|
||||
float bstMul = 5000;
|
||||
cd += get_tolerance(cd) + 1;
|
||||
//Serial.println(String("p="+ uint64ToString(p, 10) + " start cd=" + uint64ToString(cd, 10)).c_str());
|
||||
// find the best divisor based on lowest avg err, within allowed tolerance.
|
||||
while (--cd >= MIN_VIABLE_DIV) {
|
||||
uint32_t avg = 0;
|
||||
uint16_t totTms = 0;
|
||||
// calculate average error for current divisor, and verify it's within tolerance for all timings.
|
||||
for (uint16_t i = 1 + p; i < results.rawlen; i += 2) {
|
||||
uint16_t val = results.rawbuf[i] * RAWTICK;
|
||||
uint16_t rmdr = val >= cd ? val % cd : cd - val;
|
||||
if (rmdr > get_tolerance(val)) { avg = 0xFFFFU; break; }
|
||||
avg += rmdr;
|
||||
totTms += val / cd + (cd > val? 1 : 0);
|
||||
}
|
||||
if (avg == 0xFFFFU) continue;
|
||||
avg /= results.rawlen / 2;
|
||||
float avgTms = (float)totTms / (results.rawlen / 2);
|
||||
if (avgTms <= bstMul && avg < bstAvg) {
|
||||
bstMul = avgTms;
|
||||
bstAvg = avg;
|
||||
bstDiv = cd;
|
||||
//Serial.println(String("p="+ uint64ToString(p, 10) + " cd=" + uint64ToString(cd, 10) +" avgErr=" + uint64ToString(avg, 10) + " totTms="+ uint64ToString(totTms, 10) + " avgTms="+ uint64ToString((uint16_t)(avgTms*10), 10) ).c_str());
|
||||
}
|
||||
}
|
||||
if (bstDiv == 0xFFFFU) {
|
||||
addLog(LOG_LEVEL_INFO, "No proper divisor found. Try again...");
|
||||
return;
|
||||
}
|
||||
div[p] = bstDiv;
|
||||
|
||||
line = String(p? "Blank: " : "Pulse: ") + " divisor=" + uint64ToString(bstDiv, 10)
|
||||
+" avgErr=" + uint64ToString(bstAvg, 10) + " avgMul="+ uint64ToString((uint16_t)bstMul, 10)
|
||||
+'.'+ ((char)((bstMul - (uint16_t)bstMul) * 10 )+ '0');
|
||||
addLog(LOG_LEVEL_DEBUG, line);
|
||||
}
|
||||
|
||||
// Generate the B32 Hex string, per the divisors found.
|
||||
uint16_t total = results.rawlen - 1, tmOut[total];
|
||||
//line = "Timing muls ("+ uint64ToString(total, 10) + "): ";
|
||||
|
||||
for (unsigned int i = 0; i < total; i++) {
|
||||
uint16_t val = results.rawbuf[i+1] * RAWTICK;
|
||||
unsigned int dv = div[(i) & 1];
|
||||
unsigned int tm = val / dv + (val % dv > dv / 2? 1 : 0);
|
||||
tmOut[i] = tm;
|
||||
//line += uint64ToString(tm, 10) + ",";
|
||||
}
|
||||
//Serial.println(line);
|
||||
|
||||
char out[total];
|
||||
unsigned int iOut = 0, s = 2, d = 0;
|
||||
for (; s+1 < total; d = s, s += 2) {
|
||||
unsigned int vals = 2;
|
||||
while (s+1 < total && tmOut[s] == tmOut[d] && tmOut[s+1] == tmOut[d+1]) {
|
||||
vals += 2;
|
||||
s += 2;
|
||||
}
|
||||
if (iOut + 5 > sizeof(out) || tmOut[d] >= 32*32 || tmOut[d+1] >= 32*32 || vals >= 64) {
|
||||
addLog(LOG_LEVEL_INFO, "Raw code too long. Try again...");
|
||||
return;
|
||||
}
|
||||
|
||||
if (vals > 4 || (vals == 4 && (tmOut[d] >= 32 || tmOut[d+1] >= 32))) {
|
||||
out[iOut++] = '*';
|
||||
out[iOut++] = to_32hex(vals / 2);
|
||||
vals = 2;
|
||||
}
|
||||
while (vals--)
|
||||
iOut = storeB32Hex(out, iOut, tmOut[d++]);
|
||||
}
|
||||
while (d < total)
|
||||
iOut = storeB32Hex(out, iOut, tmOut[d++]);
|
||||
|
||||
out[iOut] = 0;
|
||||
line = "IRSEND,RAW2," + String(out) + ",38," + uint64ToString(div[0], 10) +','+ uint64ToString(div[1], 10);
|
||||
addLog(LOG_LEVEL_INFO, line);
|
||||
}
|
||||
|
||||
unsigned int storeB32Hex(char out[], unsigned int iOut, unsigned int val) {
|
||||
if (val >= 32) {
|
||||
out[iOut++] = '^';
|
||||
out[iOut++] = to_32hex(val/32);
|
||||
val %= 32;
|
||||
}
|
||||
out[iOut++] = to_32hex(val);
|
||||
return iOut;
|
||||
}
|
||||
|
||||
#endif // USES_P016
|
||||
|
||||
+293
-16
@@ -3,15 +3,52 @@
|
||||
//#################################### Plugin 019: PCF8574 ##############################################
|
||||
//#######################################################################################################
|
||||
|
||||
/**************************************************\
|
||||
CONFIG
|
||||
TaskDevicePluginConfig settings:
|
||||
0: send boot state (true,false)
|
||||
1:
|
||||
2:
|
||||
3:
|
||||
4: use doubleclick (0,1,2,3)
|
||||
5: use longpress (0,1,2,3)
|
||||
6: LP fired (true,false)
|
||||
7: doubleclick counter (=0,1,2,3)
|
||||
|
||||
TaskDevicePluginConfigFloat settings:
|
||||
0: debounce interval ms
|
||||
1: doubleclick interval ms
|
||||
2: longpress interval ms
|
||||
3:
|
||||
|
||||
TaskDevicePluginConfigLong settings:
|
||||
0: clickTime debounce ms
|
||||
1: clickTime doubleclick ms
|
||||
2: clickTime longpress ms
|
||||
3:
|
||||
\**************************************************/
|
||||
|
||||
#define PLUGIN_019
|
||||
#define PLUGIN_ID_019 19
|
||||
#define PLUGIN_NAME_019 "Switch input - PCF8574"
|
||||
#define PLUGIN_VALUENAME1_019 "Switch"
|
||||
#define PLUGIN_019_DOUBLECLICK_MIN_INTERVAL 1000
|
||||
#define PLUGIN_019_DOUBLECLICK_MAX_INTERVAL 3000
|
||||
#define PLUGIN_019_LONGPRESS_MIN_INTERVAL 1000
|
||||
#define PLUGIN_019_LONGPRESS_MAX_INTERVAL 5000
|
||||
#define PLUGIN_019_DC_DISABLED 0
|
||||
#define PLUGIN_019_DC_LOW 1
|
||||
#define PLUGIN_019_DC_HIGH 2
|
||||
#define PLUGIN_019_DC_BOTH 3
|
||||
#define PLUGIN_019_LONGPRESS_DISABLED 0
|
||||
#define PLUGIN_019_LONGPRESS_LOW 1
|
||||
#define PLUGIN_019_LONGPRESS_HIGH 2
|
||||
#define PLUGIN_019_LONGPRESS_BOTH 3
|
||||
|
||||
boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
static byte switchstate[TASKS_MAX];
|
||||
static int8_t switchstate[TASKS_MAX];
|
||||
|
||||
switch (function)
|
||||
{
|
||||
@@ -20,10 +57,10 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_019;
|
||||
Device[deviceCount].Type = DEVICE_TYPE_I2C;
|
||||
Device[deviceCount].VType = SENSOR_TYPE_SINGLE;
|
||||
Device[deviceCount].VType = SENSOR_TYPE_SWITCH;
|
||||
Device[deviceCount].Ports = 8;
|
||||
Device[deviceCount].PullUpOption = false;
|
||||
Device[deviceCount].InverseLogicOption = false;
|
||||
Device[deviceCount].InverseLogicOption = true;
|
||||
Device[deviceCount].FormulaOption = false;
|
||||
Device[deviceCount].ValueCount = 1;
|
||||
Device[deviceCount].SendDataOption = true;
|
||||
@@ -49,6 +86,41 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
addFormCheckBox(F("Send Boot state"), F("plugin_019_boot"), Settings.TaskDevicePluginConfig[event->TaskIndex][0]);
|
||||
|
||||
//@giig1967-20181022
|
||||
addFormSubHeader(F("Advanced event management"));
|
||||
|
||||
addFormNumericBox(F("De-bounce (ms)"), F("plugin_019_debounce"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0]), 0, 250);
|
||||
|
||||
//set minimum value for doubleclick MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_019_DOUBLECLICK_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_019_DOUBLECLICK_MIN_INTERVAL;
|
||||
|
||||
byte choiceDC = Settings.TaskDevicePluginConfig[event->TaskIndex][4];
|
||||
String buttonDC[4];
|
||||
buttonDC[0] = F("Disabled");
|
||||
buttonDC[1] = F("Active only on LOW (EVENT=3)");
|
||||
buttonDC[2] = F("Active only on HIGH (EVENT=3)");
|
||||
buttonDC[3] = F("Active on LOW & HIGH (EVENT=3)");
|
||||
int buttonDCValues[4] = {PLUGIN_019_DC_DISABLED, PLUGIN_019_DC_LOW, PLUGIN_019_DC_HIGH,PLUGIN_019_DC_BOTH};
|
||||
addFormSelector(F("Doubleclick event"), F("plugin_019_dc"), 4, buttonDC, buttonDCValues, choiceDC);
|
||||
|
||||
addFormNumericBox(F("Doubleclick max. interval (ms)"), F("plugin_019_dcmaxinterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1]), PLUGIN_019_DOUBLECLICK_MIN_INTERVAL, PLUGIN_019_DOUBLECLICK_MAX_INTERVAL);
|
||||
|
||||
//set minimum value for longpress MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_019_LONGPRESS_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_019_LONGPRESS_MIN_INTERVAL;
|
||||
|
||||
byte choiceLP = Settings.TaskDevicePluginConfig[event->TaskIndex][5];
|
||||
String buttonLP[4];
|
||||
buttonLP[0] = F("Disabled");
|
||||
buttonLP[1] = F("Active only on LOW (EVENT= 10 [NORMAL] or 11 [INVERSED])");
|
||||
buttonLP[2] = F("Active only on HIGH (EVENT= 11 [NORMAL] or 10 [INVERSED])");
|
||||
buttonLP[3] = F("Active on LOW & HIGH (EVENT= 10 or 11)");
|
||||
int buttonLPValues[4] = {PLUGIN_019_LONGPRESS_DISABLED, PLUGIN_019_LONGPRESS_LOW, PLUGIN_019_LONGPRESS_HIGH,PLUGIN_019_LONGPRESS_BOTH};
|
||||
addFormSelector(F("Longpress event"), F("plugin_019_lp"), 4, buttonLP, buttonLPValues, choiceLP);
|
||||
|
||||
addFormNumericBox(F("Longpress min. interval (ms)"), F("plugin_019_lpmininterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]), PLUGIN_019_LONGPRESS_MIN_INTERVAL, PLUGIN_019_LONGPRESS_MAX_INTERVAL);
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
@@ -57,6 +129,15 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = isFormItemChecked(F("plugin_019_boot"));
|
||||
|
||||
//@giig1967-20181022
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0] = getFormItemInt(F("plugin_019_debounce"));
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][4] = getFormItemInt(F("plugin_019_dc"));
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = getFormItemInt(F("plugin_019_dcmaxinterval"));
|
||||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][5] = getFormItemInt(F("plugin_019_lp"));
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = getFormItemInt(F("plugin_019_lpmininterval"));
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
@@ -64,31 +145,186 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
// read and store current state to prevent switching at boot time
|
||||
// "state" could be -1, 0 or 1
|
||||
switchstate[event->TaskIndex] = Plugin_019_Read(Settings.TaskDevicePort[event->TaskIndex]);
|
||||
|
||||
// if boot state must be send, inverse default state
|
||||
// this is done to force the trigger in PLUGIN_TEN_PER_SECOND
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0])
|
||||
switchstate[event->TaskIndex] = !switchstate[event->TaskIndex];
|
||||
|
||||
// @giig1967g-20181022: set initial UserVar of the switch
|
||||
if (switchstate[event->TaskIndex] != -1 && Settings.TaskDevicePin1Inversed[event->TaskIndex]) {
|
||||
UserVar[event->BaseVarIndex] = !switchstate[event->TaskIndex];
|
||||
} else {
|
||||
UserVar[event->BaseVarIndex] = switchstate[event->TaskIndex];
|
||||
}
|
||||
|
||||
// @giig1967g-20181022: doubleclick counter = 0
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0;
|
||||
|
||||
// @giig1967g-20181022: used to track if LP has fired
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6]=false;
|
||||
|
||||
// @giig1967g-20181022: store millis for debounce, doubleclick and long press
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]=millis(); //debounce timer
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis(); //doubleclick timer
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis(); //longpress timer
|
||||
|
||||
// @giig1967g-20181022: set minimum value for doubleclick MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_019_DOUBLECLICK_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_019_DOUBLECLICK_MIN_INTERVAL;
|
||||
|
||||
// @giig1967g-20181022: set minimum value for longpress MIN max speed
|
||||
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_019_LONGPRESS_MIN_INTERVAL)
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_019_LONGPRESS_MIN_INTERVAL;
|
||||
|
||||
// @giig1967g-20181022: if it is in the device list we assume it's an input pin
|
||||
setPinState(PLUGIN_ID_019, Settings.TaskDevicePort[event->TaskIndex], PIN_MODE_INPUT, 0);
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_TEN_PER_SECOND:
|
||||
|
||||
{
|
||||
int state = Plugin_019_Read(Settings.TaskDevicePort[event->TaskIndex]);
|
||||
if (state != -1)
|
||||
{
|
||||
if (state != switchstate[event->TaskIndex])
|
||||
{
|
||||
String log = F("PCF : State ");
|
||||
log += state;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
switchstate[event->TaskIndex] = state;
|
||||
UserVar[event->BaseVarIndex] = state;
|
||||
event->sensorType = SENSOR_TYPE_SWITCH;
|
||||
sendData(event);
|
||||
const int8_t state = Plugin_019_Read(Settings.TaskDevicePort[event->TaskIndex]);
|
||||
/**************************************************************************\
|
||||
20181022 - @giig1967g: new doubleclick logic is:
|
||||
if there is a 'state' change, check debounce period.
|
||||
Then if doubleclick interval exceeded, reset Settings.TaskDevicePluginConfig[event->TaskIndex][7] to 0
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7] contains the current status for doubleclick:
|
||||
0: start counting
|
||||
1: 1st click
|
||||
2: 2nd click
|
||||
3: 3rd click = doubleclick event if inside interval (calculated as: '3rd click time' minus '1st click time')
|
||||
|
||||
Returned EVENT value is = 3 always for doubleclick
|
||||
In rules this can be checked:
|
||||
on Button#Switch=3 do //will fire if doubleclick
|
||||
\**************************************************************************/
|
||||
if (state != -1) {
|
||||
if (state != switchstate[event->TaskIndex]) {
|
||||
//@giig1967g20181022: reset timer for long press
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis();
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = false;
|
||||
|
||||
const unsigned long debounceTime = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]);
|
||||
if (debounceTime >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0])) //de-bounce check
|
||||
{
|
||||
const unsigned long deltaDC = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]);
|
||||
if ((deltaDC >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1])) ||
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3)
|
||||
{
|
||||
//reset timer for doubleclick
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0;
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis();
|
||||
}
|
||||
|
||||
//just to simplify the reading of the code
|
||||
#define COUNTER Settings.TaskDevicePluginConfig[event->TaskIndex][7]
|
||||
#define DC Settings.TaskDevicePluginConfig[event->TaskIndex][4]
|
||||
|
||||
//check settings for doubleclick according to the settings
|
||||
if ( COUNTER!=0 || ( COUNTER==0 && (DC==3 || (DC==1 && state==0) || (DC==2 && state==1))) )
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]++;
|
||||
#undef DC
|
||||
#undef COUNTER
|
||||
|
||||
switchstate[event->TaskIndex] = state;
|
||||
|
||||
byte output_value;
|
||||
boolean sendState = switchstate[event->TaskIndex];
|
||||
|
||||
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
|
||||
sendState = !sendState;
|
||||
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3 && Settings.TaskDevicePluginConfig[event->TaskIndex][4]>0)
|
||||
{
|
||||
output_value = 3; //double click
|
||||
} else {
|
||||
output_value = sendState ? 1 : 0; //single click
|
||||
}
|
||||
|
||||
UserVar[event->BaseVarIndex] = output_value;
|
||||
|
||||
String log = F("PCF : Port=");
|
||||
log += Settings.TaskDevicePort[event->TaskIndex];
|
||||
log += F(" State=");
|
||||
log += state;
|
||||
log += output_value==3 ? F(" Doubleclick=") : F(" Output value=");
|
||||
log += output_value;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
|
||||
event->sensorType = SENSOR_TYPE_SWITCH;
|
||||
sendData(event);
|
||||
|
||||
//reset Userdata so it displays the correct state value in the web page
|
||||
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
|
||||
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0] = millis();
|
||||
}
|
||||
}
|
||||
|
||||
//just to simplify the reading of the code
|
||||
#define LP Settings.TaskDevicePluginConfig[event->TaskIndex][5]
|
||||
#define FIRED Settings.TaskDevicePluginConfig[event->TaskIndex][6]
|
||||
|
||||
//check if LP is enabled and if LP has not fired yet
|
||||
else if (!FIRED && (LP==3 ||(LP==1 && state==0)||(LP==2 && state==1) ) ) {
|
||||
|
||||
#undef LP
|
||||
#undef FIRED
|
||||
/**************************************************************************\
|
||||
20181022 - @giig1967g: new longpress logic is:
|
||||
if there is no 'state' change, check if longpress interval reached
|
||||
When reached send longpress event.
|
||||
Returned Event value = state + 10
|
||||
So if state = 0 => EVENT longpress = 10
|
||||
if state = 1 => EVENT longpress = 11
|
||||
So we can trigger longpress for high or low contact
|
||||
|
||||
In rules this can be checked:
|
||||
on Button#Switch=10 do //will fire if longpress when state = 0
|
||||
on Button#Switch=11 do //will fire if longpress when state = 1
|
||||
\**************************************************************************/
|
||||
const unsigned long deltaLP = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]);
|
||||
if (deltaLP >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]))
|
||||
{
|
||||
byte output_value;
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = true; //fired = true
|
||||
|
||||
boolean sendState = state;
|
||||
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
|
||||
sendState = !sendState;
|
||||
|
||||
output_value = sendState ? 1 : 0;
|
||||
output_value = output_value + 10;
|
||||
|
||||
UserVar[event->BaseVarIndex] = output_value;
|
||||
String log = F("PCF : LongPress: Port= ");
|
||||
log += Settings.TaskDevicePort[event->TaskIndex];
|
||||
log += F(" State=");
|
||||
log += state ? '1' : '0';
|
||||
log += F(" Output value=");
|
||||
log += output_value;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
sendData(event);
|
||||
|
||||
//reset Userdata so it displays the correct state value in the web page
|
||||
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
|
||||
}
|
||||
}
|
||||
} else if (state != switchstate[event->TaskIndex]) {
|
||||
//set UserVar and switchState = -1 and send EVENT to notify user
|
||||
UserVar[event->BaseVarIndex] = state;
|
||||
switchstate[event->TaskIndex] = state;
|
||||
String log = F("PCF : Port=");
|
||||
log += Settings.TaskDevicePort[event->TaskIndex];
|
||||
log += F(" is offline (EVENT= -1)");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
sendData(event);
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
@@ -105,6 +341,24 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_REQUEST:
|
||||
{
|
||||
//parseString(string, 1) = device
|
||||
//parseString(string, 2) = command
|
||||
//parseString(string, 3) = gpio number
|
||||
|
||||
// returns pin value using syntax: [plugin#pcfgpio#pinstate#xx]
|
||||
if (string.length()>=16 && string.substring(0,16).equalsIgnoreCase(F("pcfgpio,pinstate")))
|
||||
{
|
||||
int par1;
|
||||
if (validIntFromString(parseString(string, 3), par1)) {
|
||||
string = Plugin_019_Read(par1);
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WRITE:
|
||||
{
|
||||
String log = "";
|
||||
@@ -128,6 +382,28 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_019, event->Par1, log, 0));
|
||||
}
|
||||
|
||||
if (command == F("pcfgpiotoggle"))
|
||||
{
|
||||
success = true;
|
||||
byte mode;
|
||||
uint16_t currentState;
|
||||
|
||||
if (hasPinState(PLUGIN_ID_019,event->Par1)) {
|
||||
getPinState(PLUGIN_ID_019, event->Par1, &mode, ¤tState);
|
||||
} else {
|
||||
currentState = Plugin_019_Read(event->Par1);
|
||||
mode = PIN_MODE_OUTPUT;
|
||||
}
|
||||
|
||||
if (mode != PIN_MODE_INPUT) {
|
||||
setPinState(PLUGIN_ID_019, event->Par1, PIN_MODE_OUTPUT, !currentState);
|
||||
Plugin_019_Write(event->Par1, !currentState);
|
||||
log = String(F("PCF : Toggle GPIO ")) + String(event->Par1) + String(F(" Set to ")) + String(!currentState);
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_019, event->Par1, log, 0));
|
||||
}
|
||||
}
|
||||
|
||||
if (command == F("pcfpulse"))
|
||||
{
|
||||
success = true;
|
||||
@@ -187,7 +463,8 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
//********************************************************************************
|
||||
// PCF8574 read
|
||||
//********************************************************************************
|
||||
int Plugin_019_Read(byte Par1)
|
||||
//@giig1967g-20181023: changed to int8_t
|
||||
int8_t Plugin_019_Read(byte Par1)
|
||||
{
|
||||
int8_t state = -1;
|
||||
byte unit = (Par1 - 1) / 8;
|
||||
|
||||
@@ -189,6 +189,8 @@ boolean Plugin_020(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_SERIAL_IN:
|
||||
{
|
||||
if (Plugin_020_init)
|
||||
{
|
||||
uint8_t serial_buf[P020_BUFFER_SIZE];
|
||||
int RXWait = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
if (RXWait == 0)
|
||||
@@ -279,6 +281,7 @@ boolean Plugin_020(byte function, struct EventStruct *event, String& string)
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
case PLUGIN_WRITE:
|
||||
{
|
||||
|
||||
+20
-11
@@ -15,6 +15,9 @@
|
||||
#define PLUGIN_VALUENAME1_023 "OLED"
|
||||
#define PLUGIN_023_MAX_DYSPALY 2
|
||||
|
||||
#define P23_Nlines 8 // The number of different lines which can be displayed
|
||||
#define P23_Nchars 64
|
||||
|
||||
struct Plugin_023_OLED_SettingStruct
|
||||
{
|
||||
Plugin_023_OLED_SettingStruct(): address(0)
|
||||
@@ -94,7 +97,7 @@ boolean Plugin_023(byte function, struct EventStruct *event, String& string)
|
||||
int optionValues4[2] = { 1, 2 };
|
||||
addFormSelector(F("Font Width"), F("plugin_023_font_width"), 2, options4, optionValues4, choice4);
|
||||
|
||||
char deviceTemplate[8][64];
|
||||
char deviceTemplate[P23_Nlines][P23_Nchars];
|
||||
LoadCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
|
||||
for (byte varNr = 0; varNr < 8; varNr++)
|
||||
{
|
||||
@@ -117,16 +120,19 @@ boolean Plugin_023(byte function, struct EventStruct *event, String& string)
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][3] = getFormItemInt(F("plugin_023_size"));
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][4] = getFormItemInt(F("plugin_023_font_width"));
|
||||
|
||||
char deviceTemplate[8][64];
|
||||
for (byte varNr = 0; varNr < 8; varNr++)
|
||||
char deviceTemplate[P23_Nlines][P23_Nchars];
|
||||
String error;
|
||||
for (byte varNr = 0; varNr < P23_Nlines; varNr++)
|
||||
{
|
||||
String arg = F("Plugin_023_template");
|
||||
arg += varNr + 1;
|
||||
String tmpString = WebServer.arg(arg);
|
||||
strncpy(deviceTemplate[varNr], tmpString.c_str(), sizeof(deviceTemplate[varNr])-1);
|
||||
deviceTemplate[varNr][63]=0;
|
||||
String argName = F("Plugin_023_template");
|
||||
argName += varNr + 1;
|
||||
if (!safe_strncpy(deviceTemplate[varNr], WebServer.arg(argName), P23_Nchars)) {
|
||||
error += getCustomTaskSettingsError(varNr);
|
||||
}
|
||||
}
|
||||
if (error.length() > 0) {
|
||||
addHtmlError(error);
|
||||
}
|
||||
|
||||
SaveCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
|
||||
success = true;
|
||||
break;
|
||||
@@ -203,7 +209,7 @@ boolean Plugin_023(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
char deviceTemplate[8][64];
|
||||
char deviceTemplate[P23_Nlines][P23_Nchars];
|
||||
LoadCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
|
||||
int index = Settings.TaskDevicePluginConfig[event->TaskIndex][0] == 0x3C
|
||||
? 0
|
||||
@@ -228,8 +234,10 @@ boolean Plugin_023(byte function, struct EventStruct *event, String& string)
|
||||
? 0
|
||||
: 1;
|
||||
String arguments = String(string);
|
||||
|
||||
//Fixed bug #1864
|
||||
int dotPos = arguments.indexOf('.');
|
||||
if(dotPos > -1)
|
||||
if(dotPos > -1 && arguments.substring(dotPos,dotPos+4).equalsIgnoreCase(F("oled")))
|
||||
{
|
||||
LoadTaskSettings(event->TaskIndex);
|
||||
String name = arguments.substring(0,dotPos);
|
||||
@@ -245,6 +253,7 @@ boolean Plugin_023(byte function, struct EventStruct *event, String& string)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int argIndex = arguments.indexOf(',');
|
||||
if (argIndex)
|
||||
arguments = arguments.substring(0, argIndex);
|
||||
|
||||
@@ -41,7 +41,7 @@ boolean Plugin_026(byte function, struct EventStruct *event, String& string)
|
||||
case PLUGIN_WEBFORM_LOAD:
|
||||
{
|
||||
byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
String options[10];
|
||||
String options[11];
|
||||
options[0] = F("Uptime");
|
||||
options[1] = F("Free RAM");
|
||||
options[2] = F("Wifi RSSI");
|
||||
@@ -52,7 +52,8 @@ boolean Plugin_026(byte function, struct EventStruct *event, String& string)
|
||||
options[7] = F("IP 3.Octet");
|
||||
options[8] = F("IP 4.Octet");
|
||||
options[9] = F("Web activity");
|
||||
addFormSelector(F("Indicator"), F("plugin_026"), 10, options, NULL, choice);
|
||||
options[10] = F("Free Stack");
|
||||
addFormSelector(F("Indicator"), F("plugin_026"), 11, options, NULL, choice);
|
||||
|
||||
success = true;
|
||||
break;
|
||||
@@ -124,6 +125,11 @@ boolean Plugin_026(byte function, struct EventStruct *event, String& string)
|
||||
value = (millis()-lastWeb)/1000; // respond in seconds
|
||||
break;
|
||||
}
|
||||
case 10:
|
||||
{
|
||||
value = getCurrentFreeStack();
|
||||
break;
|
||||
}
|
||||
}
|
||||
UserVar[event->BaseVarIndex] = value;
|
||||
String log = F("SYS : ");
|
||||
|
||||
+7
-12
@@ -382,8 +382,10 @@ bool Plugin_028_update_measurements(const uint8_t i2cAddress, float tempOffset,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!timeOutReached(sensor.last_measurement + 1000)) {
|
||||
// Must wait one second to make sure the filtered values stabilize.
|
||||
// It takes at least 1.587 sec for valit measurements to complete.
|
||||
// The datasheet names this the "T63" moment.
|
||||
// 1 second = 63% of the time needed to perform a measurement.
|
||||
if (!timeOutReached(sensor.last_measurement + 1587)) {
|
||||
return false;
|
||||
}
|
||||
if (!Plugin_028_readUncompensatedData(i2cAddress)) {
|
||||
@@ -510,7 +512,7 @@ bool Plugin_028_begin(uint8_t i2cAddress) {
|
||||
I2C_write8_reg(i2cAddress, BMx280_REGISTER_SOFTRESET, 0xB6);
|
||||
delay(2); // Startup time is 2 ms (datasheet)
|
||||
Plugin_028_readCoefficients(i2cAddress);
|
||||
delay(65); //May be needed here as well to fix first wrong measurement?
|
||||
// delay(65); //May be needed here as well to fix first wrong measurement?
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -548,8 +550,8 @@ void Plugin_028_readCoefficients(uint8_t i2cAddress)
|
||||
bool Plugin_028_readUncompensatedData(uint8_t i2cAddress) {
|
||||
// wait until measurement has been completed, otherwise we would read
|
||||
// the values from the last measurement
|
||||
while (I2C_read8_reg(i2cAddress, BMx280_REGISTER_STATUS) & 0x08)
|
||||
delay(1);
|
||||
if (I2C_read8_reg(i2cAddress, BMx280_REGISTER_STATUS) & 0x08)
|
||||
return false;
|
||||
|
||||
I2Cdata_bytes BME280_data(BME280_P_T_H_DATA_LEN, BME280_DATA_ADDR);
|
||||
bool allDataRead = I2C_read_bytes(i2cAddress, BME280_data);
|
||||
@@ -642,13 +644,6 @@ float Plugin_028_readHumidity(uint8_t i2cAddress)
|
||||
// No support for humidity
|
||||
return 0.0;
|
||||
}
|
||||
// It takes at least 1.587 sec for valit measurements to complete.
|
||||
// The datasheet names this the "T63" moment.
|
||||
// 1 second = 63% of the time needed to perform a measurement.
|
||||
unsigned long difTime = millis() - sensor.last_measurement;
|
||||
if (difTime < 1587) {
|
||||
delay(1587 - difTime);
|
||||
}
|
||||
int32_t adc_H = sensor.uncompensated.humidity;
|
||||
|
||||
int32_t v_x1_u32r;
|
||||
|
||||
@@ -53,7 +53,7 @@ boolean Plugin_029(byte function, struct EventStruct *event, String& string)
|
||||
addHtml(F("<TR><TD>IDX:<TD>"));
|
||||
String id = F("TDID"); //="taskdeviceid"
|
||||
id += controllerNr + 1;
|
||||
addNumericBox(id, Settings.TaskDeviceID[controllerNr][event->TaskIndex], 0, 9999);
|
||||
addNumericBox(id, Settings.TaskDeviceID[controllerNr][event->TaskIndex], 0, DOMOTICZ_MAX_IDX);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
+118
-76
@@ -16,6 +16,8 @@ IRsend *Plugin_035_irSender;
|
||||
#define PLUGIN_ID_035 35
|
||||
#define PLUGIN_NAME_035 "Communication - IR Transmit"
|
||||
|
||||
#define from_32hex(c) ((((c) | ('A' ^ 'a')) - '0') % 39)
|
||||
|
||||
boolean Plugin_035(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
@@ -86,7 +88,7 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
if (GetArgv(command, TmpStr1, 100, 2)) IrType = TmpStr1;
|
||||
|
||||
if (IrType.equalsIgnoreCase(F("RAW"))) {
|
||||
if (IrType.equalsIgnoreCase(F("RAW")) || IrType.equalsIgnoreCase(F("RAW2"))) {
|
||||
String IrRaw;
|
||||
uint16_t IrHz=0;
|
||||
unsigned int IrPLen=0;
|
||||
@@ -115,86 +117,120 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
uint16_t buf[200];
|
||||
uint16_t idx = 0;
|
||||
unsigned int c0 = 0; //count consecutives 0s
|
||||
unsigned int c1 = 0; //count consecutives 1s
|
||||
if (IrType.equalsIgnoreCase(F("RAW"))) {
|
||||
unsigned int c0 = 0; //count consecutives 0s
|
||||
unsigned int c1 = 0; //count consecutives 1s
|
||||
|
||||
printWebString += F("Interpreted RAW Code: ");
|
||||
//Loop throught every char in RAW string
|
||||
for(unsigned int i = 0; i < IrRaw.length(); i++)
|
||||
{
|
||||
//Get the decimal value from base32 table
|
||||
//See: https://en.wikipedia.org/wiki/Base32#base32hex
|
||||
char c = ((IrRaw[i] | ('A' ^ 'a')) - '0') % 39;
|
||||
printWebString += F("Interpreted RAW Code: ");
|
||||
//Loop throught every char in RAW string
|
||||
for(unsigned int i = 0; i < IrRaw.length(); i++)
|
||||
{
|
||||
//Get the decimal value from base32 table
|
||||
//See: https://en.wikipedia.org/wiki/Base32#base32hex
|
||||
char c = from_32hex(IrRaw[i]);
|
||||
|
||||
//Loop through 5 LSB (bits 16, 8, 4, 2, 1)
|
||||
for (unsigned int shft = 1; shft < 6; shft++)
|
||||
{
|
||||
//if bit is 1 (5th position - 00010000 = 16)
|
||||
if ((c & 16) != 0) {
|
||||
//add 1 to counter c1
|
||||
c1++;
|
||||
//if we already have any 0s in counting (the previous
|
||||
//bit was 0)
|
||||
if (c0 > 0) {
|
||||
//add the total ms into the buffer (number of 0s multiplied
|
||||
//by defined blank length ms)
|
||||
buf[idx++] = c0 * IrBLen;
|
||||
//print the number of 0s just for debuging/info purpouses
|
||||
for (uint t = 0; t < c0; t++)
|
||||
printWebString += F("0");
|
||||
}
|
||||
//So, as we receive a "1", and processed the counted 0s
|
||||
//sending them as a ms timing into the buffer, we clear
|
||||
//the 0s counter
|
||||
c0 = 0;
|
||||
} else {
|
||||
//So, bit is 0
|
||||
//Loop through 5 LSB (bits 16, 8, 4, 2, 1)
|
||||
for (unsigned int shft = 1; shft < 6; shft++)
|
||||
{
|
||||
//if bit is 1 (5th position - 00010000 = 16)
|
||||
if ((c & 16) != 0) {
|
||||
//add 1 to counter c1
|
||||
c1++;
|
||||
//if we already have any 0s in counting (the previous
|
||||
//bit was 0)
|
||||
if (c0 > 0) {
|
||||
//add the total ms into the buffer (number of 0s multiplied
|
||||
//by defined blank length ms)
|
||||
buf[idx++] = c0 * IrBLen;
|
||||
//print the number of 0s just for debuging/info purpouses
|
||||
for (uint t = 0; t < c0; t++)
|
||||
printWebString += F("0");
|
||||
}
|
||||
//So, as we receive a "1", and processed the counted 0s
|
||||
//sending them as a ms timing into the buffer, we clear
|
||||
//the 0s counter
|
||||
c0 = 0;
|
||||
} else {
|
||||
//So, bit is 0
|
||||
|
||||
//On first call, ignore 0s (suppress left-most 0s)
|
||||
if (c0+c1 != 0) {
|
||||
//add 1 to counter c0
|
||||
c0++;
|
||||
//if we already have any 1s in counting (the previous
|
||||
//bit was 1)
|
||||
if (c1 > 0) {
|
||||
//add the total ms into the buffer (number of 1s
|
||||
//multiplied by defined pulse length ms)
|
||||
buf[idx++] = c1 * IrPLen;
|
||||
//print the number of 1s just for debugging/info purposes
|
||||
for (uint t = 0; t < c1; t++)
|
||||
printWebString += F("1");
|
||||
//On first call, ignore 0s (suppress left-most 0s)
|
||||
if (c0+c1 != 0) {
|
||||
//add 1 to counter c0
|
||||
c0++;
|
||||
//if we already have any 1s in counting (the previous
|
||||
//bit was 1)
|
||||
if (c1 > 0) {
|
||||
//add the total ms into the buffer (number of 1s
|
||||
//multiplied by defined pulse length ms)
|
||||
buf[idx++] = c1 * IrPLen;
|
||||
//print the number of 1s just for debugging/info purposes
|
||||
for (uint t = 0; t < c1; t++)
|
||||
printWebString += F("1");
|
||||
}
|
||||
//So, as we receive a "0", and processed the counted 1s
|
||||
//sending them as a ms timing into the buffer, we clear
|
||||
//the 1s counter
|
||||
c1 = 0;
|
||||
}
|
||||
}
|
||||
//So, as we receive a "0", and processed the counted 1s
|
||||
//sending them as a ms timing into the buffer, we clear
|
||||
//the 1s counter
|
||||
c1 = 0;
|
||||
//shift to left the "c" variable to process the next bit that is
|
||||
//in 5th position (00010000 = 16)
|
||||
c <<= 1;
|
||||
}
|
||||
}
|
||||
//shift to left the "c" variable to process the next bit that is
|
||||
//in 5th position (00010000 = 16)
|
||||
c <<= 1;
|
||||
}
|
||||
|
||||
//Finally, we need to process the last counted bit that we were
|
||||
//processing
|
||||
|
||||
//If we have pendings 0s
|
||||
if (c0 > 0) {
|
||||
buf[idx++] = c0 * IrBLen;
|
||||
for (uint t = 0; t < c0; t++)
|
||||
printWebString += F("0");
|
||||
}
|
||||
//If we have pendings 1s
|
||||
if (c1 > 0) {
|
||||
buf[idx++] = c1 * IrPLen;
|
||||
for (uint t = 0; t < c1; t++)
|
||||
printWebString += F("1");
|
||||
}
|
||||
|
||||
printWebString += F("<BR>");
|
||||
|
||||
} else { // RAW2
|
||||
for (unsigned int i = 0, total = IrRaw.length(), gotRep = 0, rep = 0; i < total;) {
|
||||
char c = IrRaw[i++];
|
||||
if (c == '*') {
|
||||
if (i+2 >= total || idx + (rep = from_32hex(IrRaw[i++])) * 2 > sizeof(buf))
|
||||
return addErrorTrue("Invalid RAW2 B32 encoding!");
|
||||
gotRep = 2;
|
||||
} else {
|
||||
if ((c == '^' && i+1 >= total) || idx == sizeof(buf))
|
||||
return addErrorTrue("Invalid RAW2 B32 encoding!");
|
||||
|
||||
uint16_t irLen = (idx & 1)? IrBLen : IrPLen;
|
||||
if (c == '^') {
|
||||
buf[idx++] = (from_32hex(IrRaw[i]) * 32 + from_32hex(IrRaw[i+1])) * irLen;
|
||||
i += 2;
|
||||
} else
|
||||
buf[idx++] = from_32hex(c) * irLen;
|
||||
|
||||
if (--gotRep == 0) {
|
||||
while (--rep) {
|
||||
buf[idx] = buf[idx-2];
|
||||
buf[idx+1] = buf[idx-1];
|
||||
idx += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Finally, we need to process the last counted bit that we were
|
||||
//processing
|
||||
|
||||
//If we have pendings 0s
|
||||
if (c0 > 0) {
|
||||
buf[idx] = c0 * IrBLen;
|
||||
for (uint t = 0; t < c0; t++)
|
||||
printWebString += F("0");
|
||||
}
|
||||
//If we have pendings 1s
|
||||
if (c1 > 0) {
|
||||
buf[idx] = c1 * IrPLen;
|
||||
for (uint t = 0; t < c1; t++)
|
||||
printWebString += F("1");
|
||||
}
|
||||
|
||||
printWebString += F("<BR>");
|
||||
|
||||
Plugin_035_irSender->sendRaw(buf, idx+1, IrHz);
|
||||
Plugin_035_irSender->sendRaw(buf, idx, IrHz);
|
||||
//String line = "";
|
||||
//for (int i = 0; i < idx; i++)
|
||||
// line += uint64ToString(buf[i], 10) + ",";
|
||||
//Serial.println(line);
|
||||
|
||||
//sprintf_P(log, PSTR("IR Params1: Hz:%u - PLen: %u - BLen: %u"), IrHz, IrPLen, IrBLen);
|
||||
//addLog(LOG_LEVEL_INFO, log);
|
||||
@@ -205,7 +241,7 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string)
|
||||
// unsigned long IrSecondCode=0UL;
|
||||
char ircodestr[100];
|
||||
if (GetArgv(command, TmpStr1,100, 2)) IrType = TmpStr1;
|
||||
if (GetArgv(command, TmpStr1, 100, 3)){ IrCode = strtoul(TmpStr1, NULL, 16);
|
||||
if (GetArgv(command, TmpStr1, 100, 3)){ IrCode = strtoull(TmpStr1, NULL, 16);
|
||||
memcpy(ircodestr, TmpStr1, sizeof(TmpStr1[0])*100);
|
||||
}
|
||||
//if (GetArgv(command, TmpStr1, 100, 4)) IrBits = str2int(TmpStr1); //not needed any more... leave it for reverce compatibility or remove it and break existing instalations?
|
||||
@@ -256,7 +292,7 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string)
|
||||
if (IrType.equalsIgnoreCase(F("GICable"))) Plugin_035_irSender->sendGICable(IrCode);
|
||||
}
|
||||
|
||||
addLog(LOG_LEVEL_INFO, F("IRTX :IR Code Sent"));
|
||||
addLog(LOG_LEVEL_INFO, (String("IRTX :IR Code Sent: ") + IrType).c_str());
|
||||
if (printToWeb)
|
||||
{
|
||||
printWebString += F("IR Code Sent ");
|
||||
@@ -274,7 +310,13 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string)
|
||||
return success;
|
||||
}
|
||||
|
||||
boolean addErrorTrue(const char *str) {
|
||||
addLog(LOG_LEVEL_ERROR, str);
|
||||
return true;
|
||||
}
|
||||
|
||||
// A lot of the following code has been taken directly (with permission) from the IRMQTTServer.ino example code
|
||||
// of the IRremoteESP8266 library. (https://github.com/markszabo/IRremoteESP8266)
|
||||
// Parse an Air Conditioner A/C Hex String/code and send it.
|
||||
// Args:
|
||||
// irType: Nr. of the protocol we need to send.
|
||||
@@ -615,4 +657,4 @@ uint16_t countValuesInStr(const String str, char sep) {
|
||||
// }
|
||||
// return result;
|
||||
//}
|
||||
#endif // USES_P035
|
||||
#endif // USES_P035
|
||||
|
||||
@@ -180,15 +180,18 @@ boolean Plugin_036(byte function, struct EventStruct *event, String& string)
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][5] = getFormItemInt(F("plugin_036_controler"));
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = getFormItemInt(F("plugin_036_contrast"));
|
||||
|
||||
String argName;
|
||||
|
||||
String error;
|
||||
for (byte varNr = 0; varNr < P36_Nlines; varNr++)
|
||||
{
|
||||
argName = F("Plugin_036_template");
|
||||
String argName = F("Plugin_036_template");
|
||||
argName += varNr + 1;
|
||||
strncpy(P036_deviceTemplate[varNr], WebServer.arg(argName).c_str(), sizeof(P036_deviceTemplate[varNr]));
|
||||
if (!safe_strncpy(P036_deviceTemplate[varNr], WebServer.arg(argName), P36_Nchars)) {
|
||||
error += getCustomTaskSettingsError(varNr);
|
||||
}
|
||||
}
|
||||
if (error.length() > 0) {
|
||||
addHtmlError(error);
|
||||
}
|
||||
|
||||
SaveCustomTaskSettings(event->TaskIndex, (byte*)&P036_deviceTemplate, sizeof(P036_deviceTemplate));
|
||||
|
||||
success = true;
|
||||
|
||||
+69
-42
@@ -24,6 +24,37 @@
|
||||
WiFiClient espclient_037;
|
||||
PubSubClient *MQTTclient_037 = NULL;
|
||||
bool MQTTclient_037_connected = false;
|
||||
int reconnectCount = 0;
|
||||
|
||||
String getClientName() {
|
||||
//
|
||||
// Generate the MQTT import client name from the system name and a suffix
|
||||
//
|
||||
String tmpClientName = F("%sysname%-Import");
|
||||
String ClientName = parseTemplate(tmpClientName, 20);
|
||||
if (reconnectCount != 0) ClientName += reconnectCount;
|
||||
return ClientName;
|
||||
}
|
||||
|
||||
void Plugin_037_try_connect() {
|
||||
Plugin_037_update_connect_status();
|
||||
if (MQTTclient_037_connected) return;
|
||||
// workaround see: https://github.com/esp8266/Arduino/issues/4497#issuecomment-373023864
|
||||
espclient_037 = WiFiClient();
|
||||
espclient_037.setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT);
|
||||
|
||||
if (MQTTclient_037 == NULL) {
|
||||
MQTTclient_037 = new PubSubClient(espclient_037);
|
||||
}
|
||||
if (MQTTConnect_037())
|
||||
{
|
||||
// Subscribe to ALL the topics from ALL instance of this import module
|
||||
MQTTSubscribe_037();
|
||||
} else {
|
||||
MQTTclient_037->disconnect();
|
||||
}
|
||||
Plugin_037_update_connect_status();
|
||||
}
|
||||
|
||||
void Plugin_037_update_connect_status() {
|
||||
bool connected = false;
|
||||
@@ -32,6 +63,8 @@ void Plugin_037_update_connect_status() {
|
||||
}
|
||||
if (MQTTclient_037_connected != connected) {
|
||||
MQTTclient_037_connected = !MQTTclient_037_connected;
|
||||
P037_MQTTImport_connected = MQTTclient_037_connected;
|
||||
|
||||
if (Settings.UseRules) {
|
||||
String event = connected ? F("MQTTimport#Connected") : F("MQTTimport#Disconnected");
|
||||
rulesProcessing(event);
|
||||
@@ -39,6 +72,8 @@ void Plugin_037_update_connect_status() {
|
||||
if (!connected) {
|
||||
// workaround see: https://github.com/esp8266/Arduino/issues/4497#issuecomment-373023864
|
||||
espclient_037 = WiFiClient();
|
||||
espclient_037.setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT);
|
||||
++reconnectCount;
|
||||
addLog(LOG_LEVEL_ERROR, F("IMPT : MQTT 037 Connection lost"));
|
||||
}
|
||||
}
|
||||
@@ -49,11 +84,6 @@ boolean Plugin_037(byte function, struct EventStruct *event, String& string)
|
||||
boolean success = false;
|
||||
|
||||
char deviceTemplate[4][41]; // variable for saving the subscription topics
|
||||
//
|
||||
// Generate the MQTT import client name from the system name and a suffix
|
||||
//
|
||||
String tmpClientName = F("%sysname%-Import");
|
||||
String ClientName = parseTemplate(tmpClientName, 20);
|
||||
|
||||
switch (function)
|
||||
{
|
||||
@@ -121,30 +151,23 @@ boolean Plugin_037(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
if (!MQTTclient_037) {
|
||||
MQTTclient_037 = new PubSubClient(espclient_037);
|
||||
}
|
||||
|
||||
success = false;
|
||||
// When we edit the subscription data from the webserver, the plugin is called again with init.
|
||||
// In order to resubscribe we have to disconnect and reconnect in order to get rid of any obsolete subscriptions
|
||||
|
||||
MQTTclient_037->disconnect();
|
||||
|
||||
if (MQTTConnect_037(ClientName))
|
||||
{
|
||||
// Subscribe to ALL the topics from ALL instance of this import module
|
||||
MQTTSubscribe_037();
|
||||
success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
success = false;
|
||||
if (MQTTclient_037 != NULL) {
|
||||
MQTTclient_037->disconnect();
|
||||
if (MQTTConnect_037())
|
||||
{
|
||||
// Subscribe to ALL the topics from ALL instance of this import module
|
||||
MQTTSubscribe_037();
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case PLUGIN_TEN_PER_SECOND:
|
||||
{
|
||||
if (!MQTTclient_037->loop()) { // Listen out for callbacks
|
||||
if (MQTTclient_037 != NULL && !MQTTclient_037->loop()) { // Listen out for callbacks
|
||||
Plugin_037_update_connect_status();
|
||||
}
|
||||
success = true;
|
||||
@@ -154,25 +177,26 @@ boolean Plugin_037(byte function, struct EventStruct *event, String& string)
|
||||
case PLUGIN_ONCE_A_SECOND:
|
||||
{
|
||||
// Here we check that the MQTT client is alive.
|
||||
Plugin_037_try_connect();
|
||||
if (MQTTclient_037 != NULL) {
|
||||
if (!MQTTclient_037->connected() || MQTTclient_should_reconnect) {
|
||||
if (MQTTclient_should_reconnect) {
|
||||
addLog(LOG_LEVEL_ERROR, F("IMPT : MQTT 037 Intentional reconnect"));
|
||||
}
|
||||
|
||||
if (!MQTTclient_037->connected() || MQTTclient_should_reconnect) {
|
||||
if (MQTTclient_should_reconnect) {
|
||||
addLog(LOG_LEVEL_ERROR, F("IMPT : MQTT 037 Intentional reconnect"));
|
||||
MQTTclient_037->disconnect();
|
||||
Plugin_037_update_connect_status();
|
||||
delay(250);
|
||||
|
||||
if (! MQTTConnect_037()) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
MQTTSubscribe_037();
|
||||
}
|
||||
|
||||
MQTTclient_037->disconnect();
|
||||
Plugin_037_update_connect_status();
|
||||
delay(250);
|
||||
|
||||
if (! MQTTConnect_037(ClientName)) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
MQTTSubscribe_037();
|
||||
success = true;
|
||||
}
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -260,6 +284,7 @@ boolean Plugin_037(byte function, struct EventStruct *event, String& string)
|
||||
}
|
||||
boolean MQTTSubscribe_037()
|
||||
{
|
||||
if (!MQTTclient_037_connected) return false;
|
||||
|
||||
// Subscribe to the topics requested by ALL calls to this plugin.
|
||||
// We do this because if the connection to the broker is lost, we want to resubscribe for all instances.
|
||||
@@ -283,7 +308,7 @@ boolean MQTTSubscribe_037()
|
||||
if (subscribeTo.length() > 0)
|
||||
{
|
||||
parseSystemVariables(subscribeTo, false);
|
||||
if (MQTTclient_037->subscribe(subscribeTo.c_str()))
|
||||
if (MQTTclient_037 != NULL && MQTTclient_037->subscribe(subscribeTo.c_str()))
|
||||
{
|
||||
String log = F("IMPT : [");
|
||||
LoadTaskSettings(y);
|
||||
@@ -350,9 +375,11 @@ void mqttcallback_037(char* c_topic, byte* b_payload, unsigned int length)
|
||||
// For some reason this seems to failduring the call in INIT- however it succeeds later during recovery
|
||||
// It would be nice to understand this....
|
||||
|
||||
boolean MQTTConnect_037(String clientid)
|
||||
boolean MQTTConnect_037()
|
||||
{
|
||||
boolean result = false;
|
||||
if (MQTTclient_037 == NULL) return false;
|
||||
String clientid = getClientName();
|
||||
// @ToDo TD-er: Plugin allows for more than one MQTT controller, but we're now using only the first enabled one.
|
||||
int enabledMqttController = firstEnabledMQTTController();
|
||||
if (enabledMqttController < 0) {
|
||||
@@ -367,8 +394,8 @@ boolean MQTTConnect_037(String clientid)
|
||||
Plugin_037_update_connect_status();
|
||||
return false; // Not connected, so no use in wasting time to connect to a host.
|
||||
}
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(enabledMqttController, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
MakeControllerSettings(ControllerSettings);
|
||||
LoadControllerSettings(enabledMqttController, ControllerSettings);
|
||||
if (ControllerSettings.UseDNS) {
|
||||
MQTTclient_037->setServer(ControllerSettings.getHost().c_str(), ControllerSettings.Port);
|
||||
} else {
|
||||
|
||||
+16
-12
@@ -16,7 +16,7 @@
|
||||
|
||||
#define P044_STATUS_LED 12
|
||||
#define P044_BUFFER_SIZE 1024
|
||||
#define P044_NETBUF_SIZE 600
|
||||
#define P044_NETBUF_SIZE 128
|
||||
#define P044_DISABLED 0
|
||||
#define P044_WAITING 1
|
||||
#define P044_READING 2
|
||||
@@ -31,7 +31,7 @@ boolean CRCcheck = false;
|
||||
unsigned int currCRC = 0;
|
||||
int checkI = 0;
|
||||
|
||||
WiFiServer *P1GatewayServer;
|
||||
WiFiServer *P1GatewayServer = nullptr;
|
||||
WiFiClient P1GatewayClient;
|
||||
|
||||
boolean Plugin_044(byte function, struct EventStruct *event, String& string)
|
||||
@@ -125,7 +125,11 @@ boolean Plugin_044(byte function, struct EventStruct *event, String& string)
|
||||
#if defined(ESP32)
|
||||
Serial.begin(ExtraTaskSettings.TaskDevicePluginConfigLong[1], serialconfig);
|
||||
#endif
|
||||
if (P1GatewayServer) P1GatewayServer->close();
|
||||
if (P1GatewayServer)
|
||||
{
|
||||
P1GatewayServer->close();
|
||||
delete P1GatewayServer;
|
||||
}
|
||||
P1GatewayServer = new WiFiServer(ExtraTaskSettings.TaskDevicePluginConfigLong[0]);
|
||||
P1GatewayServer->begin();
|
||||
|
||||
@@ -164,7 +168,7 @@ boolean Plugin_044(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
if (P1GatewayServer) {
|
||||
P1GatewayServer->close();
|
||||
//FIXME: shouldnt P1P1GatewayServer be deleted?
|
||||
delete P1GatewayServer;
|
||||
P1GatewayServer = NULL;
|
||||
}
|
||||
success = true;
|
||||
@@ -175,7 +179,6 @@ boolean Plugin_044(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
if (Plugin_044_init)
|
||||
{
|
||||
size_t bytes_read;
|
||||
if (P1GatewayServer->hasClient())
|
||||
{
|
||||
if (P1GatewayClient) P1GatewayClient.stop();
|
||||
@@ -186,24 +189,25 @@ boolean Plugin_044(byte function, struct EventStruct *event, String& string)
|
||||
if (P1GatewayClient.connected())
|
||||
{
|
||||
connectionState = 1;
|
||||
uint8_t net_buf[P044_BUFFER_SIZE];
|
||||
uint8_t net_buf[P044_NETBUF_SIZE];
|
||||
int count = P1GatewayClient.available();
|
||||
if (count > 0)
|
||||
{
|
||||
if (count > P044_BUFFER_SIZE)
|
||||
count = P044_BUFFER_SIZE;
|
||||
bytes_read = P1GatewayClient.read(net_buf, count);
|
||||
Serial.write(net_buf, bytes_read);
|
||||
size_t net_bytes_read;
|
||||
if (count > P044_NETBUF_SIZE)
|
||||
count = P044_NETBUF_SIZE;
|
||||
net_bytes_read = P1GatewayClient.read(net_buf, count);
|
||||
Serial.write(net_buf, net_bytes_read);
|
||||
Serial.flush(); // Waits for the transmission of outgoing serial data to complete
|
||||
|
||||
if (count == P044_BUFFER_SIZE) // if we have a full buffer, drop the last position to stuff with string end marker
|
||||
if (count == P044_NETBUF_SIZE) // if we have a full buffer, drop the last position to stuff with string end marker
|
||||
{
|
||||
count--;
|
||||
// and log buffer full situation
|
||||
addLog(LOG_LEVEL_ERROR, F("P1 : Error: network buffer full!"));
|
||||
}
|
||||
net_buf[count] = 0; // before logging as a char array, zero terminate the last position to be safe.
|
||||
char log[P044_BUFFER_SIZE + 40];
|
||||
char log[P044_NETBUF_SIZE + 40];
|
||||
sprintf_P(log, PSTR("P1 : Error: N>: %s"), (char*)net_buf);
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
}
|
||||
|
||||
+20
-13
@@ -22,7 +22,7 @@
|
||||
|
||||
#include <QEIx4.h>
|
||||
|
||||
QEIx4* Plugin_059_QE = NULL;
|
||||
std::map<unsigned int, std::shared_ptr<QEIx4> > P_059_sensordefs;
|
||||
|
||||
#ifndef CONFIG
|
||||
#define CONFIG(n) (Settings.TaskDevicePluginConfig[event->TaskIndex][n])
|
||||
@@ -108,12 +108,13 @@ boolean Plugin_059(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
if (!Plugin_059_QE)
|
||||
Plugin_059_QE = new QEIx4;
|
||||
// create sensor instance and add to std::map
|
||||
P_059_sensordefs.erase(event->TaskIndex);
|
||||
P_059_sensordefs[event->TaskIndex] = std::shared_ptr<QEIx4>(new QEIx4);
|
||||
|
||||
Plugin_059_QE->begin(PIN(0),PIN(1),PIN(2),CONFIG(0));
|
||||
Plugin_059_QE->setLimit(CONFIG_L(0), CONFIG_L(1));
|
||||
Plugin_059_QE->setIndexTrigger(true);
|
||||
P_059_sensordefs[event->TaskIndex]->begin(PIN(0),PIN(1),PIN(2),CONFIG(0));
|
||||
P_059_sensordefs[event->TaskIndex]->setLimit(CONFIG_L(0), CONFIG_L(1));
|
||||
P_059_sensordefs[event->TaskIndex]->setIndexTrigger(true);
|
||||
|
||||
ExtraTaskSettings.TaskDeviceValueDecimals[event->BaseVarIndex] = 0;
|
||||
|
||||
@@ -135,13 +136,19 @@ boolean Plugin_059(byte function, struct EventStruct *event, String& string)
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_EXIT:
|
||||
{
|
||||
P_059_sensordefs.erase(event->TaskIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_TEN_PER_SECOND:
|
||||
{
|
||||
if (Plugin_059_QE)
|
||||
if (P_059_sensordefs.count(event->TaskIndex) != 0)
|
||||
{
|
||||
if (Plugin_059_QE->hasChanged())
|
||||
if (P_059_sensordefs[event->TaskIndex]->hasChanged())
|
||||
{
|
||||
long c = Plugin_059_QE->read();
|
||||
long c = P_059_sensordefs[event->TaskIndex]->read();
|
||||
UserVar[event->BaseVarIndex] = (float)c;
|
||||
event->sensorType = SENSOR_TYPE_SWITCH;
|
||||
|
||||
@@ -159,9 +166,9 @@ boolean Plugin_059(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
if (Plugin_059_QE)
|
||||
if (P_059_sensordefs.count(event->TaskIndex) != 0)
|
||||
{
|
||||
UserVar[event->BaseVarIndex] = (float)Plugin_059_QE->read();
|
||||
UserVar[event->BaseVarIndex] = (float)P_059_sensordefs[event->TaskIndex]->read();
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
@@ -169,7 +176,7 @@ boolean Plugin_059(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_WRITE:
|
||||
{
|
||||
if (Plugin_059_QE)
|
||||
if (P_059_sensordefs.count(event->TaskIndex) != 0)
|
||||
{
|
||||
String log = "";
|
||||
String command = parseString(string, 1);
|
||||
@@ -179,7 +186,7 @@ boolean Plugin_059(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
log = String(F("QEI : ")) + string;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
Plugin_059_QE->write(event->Par1);
|
||||
P_059_sensordefs[event->TaskIndex]->write(event->Par1);
|
||||
}
|
||||
success = true; // Command is handled.
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef USES_P067
|
||||
#ifdef USES_P067
|
||||
//#######################################################################################################
|
||||
//#################################### Plugin 063: _P067_HX711_Load_Cell ################################
|
||||
//#######################################################################################################
|
||||
@@ -28,8 +28,8 @@
|
||||
#define PIN(n) (Settings.TaskDevicePin[n][event->TaskIndex])
|
||||
#endif
|
||||
|
||||
int32_t Plugin_067_OversamplingValue = 0;
|
||||
int16_t Plugin_067_OversamplingCount = 0;
|
||||
std::map<byte, int32_t> Plugin_067_OversamplingValue;
|
||||
std::map<byte, int16_t> Plugin_067_OversamplingCount;
|
||||
|
||||
|
||||
void initHX711(int16_t pinSCL, int16_t pinDOUT)
|
||||
@@ -157,8 +157,8 @@ boolean Plugin_067(byte function, struct EventStruct *event, String& string)
|
||||
if (isFormItemChecked(F("tare")))
|
||||
{
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][3] = -UserVar[event->BaseVarIndex + 1];
|
||||
Plugin_067_OversamplingValue = 0;
|
||||
Plugin_067_OversamplingCount = 0;
|
||||
Plugin_067_OversamplingValue[event->TaskIndex] = 0;
|
||||
Plugin_067_OversamplingCount[event->TaskIndex] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -190,6 +190,8 @@ boolean Plugin_067(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
if (pinSCL >= 0 && pinDOUT >= 0)
|
||||
{
|
||||
Plugin_067_OversamplingValue[event->TaskIndex]=0;
|
||||
Plugin_067_OversamplingCount[event->TaskIndex]=0;
|
||||
initHX711(pinSCL, pinDOUT);
|
||||
}
|
||||
|
||||
@@ -202,7 +204,7 @@ boolean Plugin_067(byte function, struct EventStruct *event, String& string)
|
||||
int16_t pinSCL = PIN(0);
|
||||
int16_t pinDOUT = PIN(1);
|
||||
|
||||
if (Plugin_067_OversamplingCount < 250)
|
||||
if (Plugin_067_OversamplingCount[event->TaskIndex] < 250)
|
||||
if (pinSCL >= 0 && pinDOUT >= 0)
|
||||
if (isReadyHX711(pinSCL, pinDOUT))
|
||||
{
|
||||
@@ -210,13 +212,13 @@ boolean Plugin_067(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
if (CONFIG(0)) //Oversampling?
|
||||
{
|
||||
Plugin_067_OversamplingValue += value;
|
||||
Plugin_067_OversamplingCount ++;
|
||||
Plugin_067_OversamplingValue[event->TaskIndex] += value;
|
||||
Plugin_067_OversamplingCount[event->TaskIndex] ++;
|
||||
}
|
||||
else //use last value
|
||||
{
|
||||
Plugin_067_OversamplingValue = value;
|
||||
Plugin_067_OversamplingCount = 1;
|
||||
Plugin_067_OversamplingValue[event->TaskIndex] = value;
|
||||
Plugin_067_OversamplingCount[event->TaskIndex] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,11 +230,11 @@ boolean Plugin_067(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
String log = F("HX711: Value: ");
|
||||
|
||||
if (Plugin_067_OversamplingCount > 0)
|
||||
if (Plugin_067_OversamplingCount[event->TaskIndex] > 0)
|
||||
{
|
||||
UserVar[event->BaseVarIndex + 1] = (float)Plugin_067_OversamplingValue / Plugin_067_OversamplingCount;
|
||||
Plugin_067_OversamplingValue = 0;
|
||||
Plugin_067_OversamplingCount = 0;
|
||||
UserVar[event->BaseVarIndex + 1] = (float)Plugin_067_OversamplingValue[event->TaskIndex] / Plugin_067_OversamplingCount[event->TaskIndex];
|
||||
Plugin_067_OversamplingValue[event->TaskIndex] = 0;
|
||||
Plugin_067_OversamplingCount[event->TaskIndex] = 0;
|
||||
|
||||
UserVar[event->BaseVarIndex] = UserVar[event->BaseVarIndex + 1] + Settings.TaskDevicePluginConfigFloat[event->TaskIndex][3]; //Offset
|
||||
|
||||
@@ -272,8 +274,8 @@ boolean Plugin_067(byte function, struct EventStruct *event, String& string)
|
||||
String log = F("HX711: tare");
|
||||
|
||||
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][3] = -UserVar[event->BaseVarIndex + 1];
|
||||
Plugin_067_OversamplingValue = 0;
|
||||
Plugin_067_OversamplingCount = 0;
|
||||
Plugin_067_OversamplingValue[event->TaskIndex] = 0;
|
||||
Plugin_067_OversamplingCount[event->TaskIndex] = 0;
|
||||
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
success = true;
|
||||
@@ -281,8 +283,15 @@ boolean Plugin_067(byte function, struct EventStruct *event, String& string)
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_EXIT:
|
||||
{
|
||||
Plugin_067_OversamplingValue.erase(event->TaskIndex);
|
||||
Plugin_067_OversamplingCount.erase(event->TaskIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
#endif // USES_P067
|
||||
#endif // USES_P067
|
||||
|
||||
@@ -458,6 +458,7 @@ void p073_FillBufferWithTemp(long temperature)
|
||||
int p073_numlenght = strlen(p073_digit);
|
||||
byte p073_dispdigit;
|
||||
for (int i=0;i<p073_numlenght;i++) {
|
||||
p073_dispdigit = 10; // default is space
|
||||
if (p073_digit[i] > 47 && p073_digit[i] < 58)
|
||||
p073_dispdigit = p073_digit[i]-48;
|
||||
else if (p073_digit[i] == 32) // space
|
||||
|
||||
+135
-92
@@ -2,11 +2,16 @@
|
||||
//#######################################################################################################
|
||||
//################################### Plugin 075: Nextion <info@sensorio.cz> ###########################
|
||||
//################################### Created on the work of majklovec ###########################
|
||||
//################################### Revisions by BertB and ThomasB ###########################
|
||||
//################################### Last Revision: Aug-03-2018 (TB) ###########################
|
||||
//################################### Revisions by BertB and ThomasB ###########################
|
||||
//################################### Last Revision: Oct-03-2018 (TB) ###########################
|
||||
//#######################################################################################################
|
||||
//
|
||||
// Updated: Aug-03-2018, ThomasB.
|
||||
// Updated: Oct-03-2018, ThomasB.
|
||||
// Added DEBUG_LOG define to reduce info log messages and prevent serial log flooding.
|
||||
// Added SendStatus() to post log message on browser to acknowledge HTTP write.
|
||||
// Added reserve() to minimize string memory allocations.
|
||||
//
|
||||
// Aug-03-2018 change log summary:
|
||||
// This updated Nextion Plugin introduces hardware (UART) serial as an alternative to SoftSerial.
|
||||
// Hardware serial offers more reliable communication with the Nextion display.
|
||||
// Created on the experimental Serial.swap() work of BertB.
|
||||
@@ -19,6 +24,9 @@
|
||||
// Defines start here
|
||||
// *****************************************************************************************************
|
||||
|
||||
//#define DEBUG_LOG // Enable this to include additional info messages in log output.
|
||||
|
||||
|
||||
// Plug-In defines
|
||||
#define PLUGIN_075
|
||||
#define PLUGIN_ID_075 75
|
||||
@@ -28,18 +36,18 @@
|
||||
#define PLUGIN_VALUENAME2_075 "value"
|
||||
|
||||
// Configuration Settings. Custom Configuration Memory must be less than 1024 Bytes (per TD'er findings).
|
||||
//#define Nlines 12 // Custom Config, Number of user entered Command Statment Lines. DO NOT USE!
|
||||
//#define Lenlines 64 // Custom Config, Length of user entered Command Statment Lines. DO NOT USE!
|
||||
#define Nlines 10 // Custom Config, Number of user entered Command Statments.
|
||||
#define Lenlines 51 // Custom Config, Length of user entered Command Statments.
|
||||
//#define P75_Nlines 12 // Custom Config, Number of user entered Command Statment Lines. DO NOT USE!
|
||||
//#define P75_Nchars 64 // Custom Config, Length of user entered Command Statment Lines. DO NOT USE!
|
||||
#define P75_Nlines 10 // Custom Config, Number of user entered Command Statments.
|
||||
#define P75_Nchars 51 // Custom Config, Length of user entered Command Statments.
|
||||
|
||||
// Nextion defines
|
||||
#define RXBUFFSZ 64 // Serial RxD buffer (Local staging buffer and ESPeasySoftwareSerial).
|
||||
#define RXBUFFSZ 64 // Serial RxD buffer (Local staging buffer and ESPeasySoftwareSerial).
|
||||
#define RXBUFFWARN RXBUFFSZ-16 // Warning, Rx buffer close to being full.
|
||||
#define TOUCH_BASE 500 // Base offset for 0X65 Touch Event Send Component ID.
|
||||
|
||||
// Serial defines
|
||||
#define B9600 0
|
||||
#define B9600 0
|
||||
#define B38400 1
|
||||
#define B57600 2
|
||||
#define B115200 3
|
||||
@@ -50,7 +58,7 @@
|
||||
|
||||
// Global vars
|
||||
ESPeasySoftwareSerial *SoftSerial = NULL;
|
||||
char deviceTemplate[Nlines][Lenlines];
|
||||
char deviceTemplate[P75_Nlines][P75_Nchars];
|
||||
int rxPin = -1;
|
||||
int txPin = -1;
|
||||
|
||||
@@ -59,10 +67,10 @@ int txPin = -1;
|
||||
// PlugIn starts here
|
||||
// *****************************************************************************************************
|
||||
|
||||
boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
static uint8_t BaudCode = 0; // Web GUI baud rate drop down. 9600 if 0, See array for other rates.
|
||||
static uint8_t BaudCode = 0; // Web GUI baud rate drop down. 9600 if 0, See array for other rates.
|
||||
static boolean HwSerial = SOFTSERIAL; // Serial mode, hardware uart or softserial.
|
||||
static boolean AdvHwSerial = false; // Web GUI checkbox flag; false = softserial mode, true = hardware UART serial.
|
||||
static boolean IncludeValues = false; // Web GUI checkbox flag; false = don't send idx & value data at interval.
|
||||
@@ -126,7 +134,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
if (!((rxPin == 3 && txPin == 1) || (rxPin == 13 && txPin == 15))) { // Hardware Serial Compatible?
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = false; // Not HW serial compatible, Reset Check Box.
|
||||
}
|
||||
|
||||
|
||||
if (rxPin == 3 && txPin == 1) { // UART USB Port?
|
||||
if(Settings.TaskDevicePluginConfig[event->TaskIndex][0]==false && // Hardware serial currently disabled.
|
||||
Settings.TaskDeviceEnabled[event->TaskIndex] == true) { // Plugin is enabled.
|
||||
@@ -134,10 +142,10 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0] == false) { // SoftSerial mode.
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0] == false) { // SoftSerial mode.
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][1] = B9600; // Reset to 9600 baud.
|
||||
}
|
||||
|
||||
|
||||
if(rxPin <0 || txPin <0) { // Missing serial I/O pins!
|
||||
addFormNote(F("Please configure the RX and TX sensor pins before enabling this plugin."));
|
||||
addFormSubHeader(F("")); // Blank line, vertical space.
|
||||
@@ -153,13 +161,13 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
options[1] = F("38400");
|
||||
options[2] = F("57600");
|
||||
options[3] = F("115200");
|
||||
|
||||
addFormSelector(F("Baud Rate"), F("plugin_075_baud"), 4, options, NULL, choice);
|
||||
|
||||
addFormSelector(F("Baud Rate"), F("plugin_075_baud"), 4, options, NULL, choice);
|
||||
addFormNote(F("Un-check box for Soft Serial communication (low performance mode, 9600 Baud)."));
|
||||
addFormNote(F("Hardware Serial is available when the GPIO pins are RX=D7 and TX=D8."));
|
||||
addFormNote(F("D8 (GPIO-15) requires a Buffer Circuit (PNP transistor) or ESP boot may fail."));
|
||||
addFormNote(F("Do <b>NOT</b> enable the Serial Log file on Tools->Advanced->Serial Port."));
|
||||
|
||||
|
||||
// ** DEVELOPER DEBUG MESSAGE AREA **
|
||||
// int datax = (int)(Settings.TaskDeviceEnabled[event->TaskIndex]); // Debug value.
|
||||
// String Data = "Debug. Plugin Enable State: ";
|
||||
@@ -168,12 +176,12 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
addFormSubHeader(F("")); // Blank line, vertical space.
|
||||
addFormHeader(F("Nextion Command Statements (Optional)"));
|
||||
|
||||
char deviceTemplate[Nlines][Lenlines];
|
||||
|
||||
char deviceTemplate[P75_Nlines][P75_Nchars];
|
||||
LoadCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
|
||||
for (byte varNr = 0; varNr < Nlines; varNr++) {
|
||||
addFormTextBox(String(F("Line ")) + (varNr + 1), String(F("Plugin_075_template")) + (varNr + 1), deviceTemplate[varNr], Lenlines-1);
|
||||
|
||||
for (byte varNr = 0; varNr < P75_Nlines; varNr++) {
|
||||
addFormTextBox(String(F("Line ")) + (varNr + 1), String(F("Plugin_075_template")) + (varNr + 1), deviceTemplate[varNr], P75_Nchars-1);
|
||||
|
||||
}
|
||||
if( Settings.TaskDeviceTimer[event->TaskIndex]==0) { // Is interval timer disabled?
|
||||
if(IncludeValues) {
|
||||
@@ -194,17 +202,18 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
|
||||
case PLUGIN_WEBFORM_SAVE: {
|
||||
|
||||
String argName;
|
||||
|
||||
char deviceTemplate[Nlines][Lenlines];
|
||||
for (byte varNr = 0; varNr < Nlines; varNr++)
|
||||
char deviceTemplate[P75_Nlines][P75_Nchars];
|
||||
String error;
|
||||
for (byte varNr = 0; varNr < P75_Nlines; varNr++)
|
||||
{
|
||||
String arg = F("Plugin_075_template");
|
||||
arg += varNr + 1;
|
||||
String tmpString = WebServer.arg(arg);
|
||||
strncpy(deviceTemplate[varNr], tmpString.c_str(), sizeof(deviceTemplate[varNr])-1);
|
||||
deviceTemplate[varNr][Lenlines-1]=0;
|
||||
String argName = F("Plugin_075_template");
|
||||
argName += varNr + 1;
|
||||
if (!safe_strncpy(deviceTemplate[varNr], WebServer.arg(argName), P75_Nchars)) {
|
||||
error += getCustomTaskSettingsError(varNr);
|
||||
}
|
||||
}
|
||||
if (error.length() > 0) {
|
||||
addHtmlError(error);
|
||||
}
|
||||
if(getTaskDeviceName(event->TaskIndex) == "") { // Check to see if user entered device name.
|
||||
strcpy(ExtraTaskSettings.TaskDeviceName,PLUGIN_DEFAULT_NAME); // Name missing, populate default name.
|
||||
@@ -236,12 +245,14 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
txPin = Settings.TaskDevicePin2[event->TaskIndex];
|
||||
}
|
||||
|
||||
if (SoftSerial != NULL) {
|
||||
if (SoftSerial != NULL) {
|
||||
delete SoftSerial;
|
||||
SoftSerial = NULL;
|
||||
}
|
||||
|
||||
String log = F("NEXTION075 : serial pin config RX:");
|
||||
String log;
|
||||
log.reserve(80); // Prevent re-allocation
|
||||
log = F("NEXTION075 : serial pin config RX:");
|
||||
log += rxPin;
|
||||
log += F(", TX:");
|
||||
log += txPin;
|
||||
@@ -283,7 +294,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
HwSerial = SOFTSERIAL;
|
||||
if (SoftSerial == NULL) {
|
||||
SoftSerial = new ESPeasySoftwareSerial(rxPin, txPin, false, RXBUFFSZ);
|
||||
}
|
||||
}
|
||||
SoftSerial->begin(9600);
|
||||
SoftSerial->flush();
|
||||
}
|
||||
@@ -295,8 +306,8 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
}
|
||||
|
||||
|
||||
case PLUGIN_READ: {
|
||||
char deviceTemplate[Nlines][Lenlines];
|
||||
case PLUGIN_READ: {
|
||||
char deviceTemplate[P75_Nlines][P75_Nchars];
|
||||
int RssiIndex;
|
||||
String newString;
|
||||
String tmpString;
|
||||
@@ -305,7 +316,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
LoadCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
|
||||
|
||||
// Get optional LINE command statements. Special RSSIBAR bargraph keyword is supported.
|
||||
for (byte x = 0; x < Nlines; x++) {
|
||||
for (byte x = 0; x < P75_Nlines; x++) {
|
||||
tmpString = deviceTemplate[x];
|
||||
if (tmpString.length()) {
|
||||
UcTmpString = deviceTemplate[x];
|
||||
@@ -313,6 +324,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
RssiIndex = UcTmpString.indexOf(F("RSSIBAR")); // RSSI bargraph Keyword found, wifi value in dBm.
|
||||
if(RssiIndex >= 0) {
|
||||
int barVal;
|
||||
newString.reserve(P75_Nchars+10); // Prevent re-allocation
|
||||
newString = tmpString.substring(0, RssiIndex);
|
||||
int nbars = WiFi.RSSI();
|
||||
if (nbars < -100 || nbars >= 0)
|
||||
@@ -345,55 +357,64 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
}
|
||||
|
||||
sendCommand(newString.c_str(), HwSerial);
|
||||
|
||||
String log = F("NEXTION075 : Cmd Statement Line-");
|
||||
log += String(x+1);
|
||||
log += F(" Sent: ");
|
||||
log += newString;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
#ifdef DEBUG_LOG
|
||||
String log;
|
||||
log.reserve(P75_Nchars+50); // Prevent re-allocation
|
||||
log = F("NEXTION075 : Cmd Statement Line-");
|
||||
log += String(x+1);
|
||||
log += F(" Sent: ");
|
||||
log += newString;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// At Interval timer, send idx & value data only if user enabled "values" interval mode.
|
||||
if(IncludeValues) {
|
||||
/*
|
||||
String log = F("NEXTION075: Interval values data enabled, resending idx=");
|
||||
log += String(UserVar[event->BaseVarIndex]);
|
||||
log += F(", value=");
|
||||
log += String(UserVar[event->BaseVarIndex+1]);
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
*/
|
||||
#ifdef DEBUG_LOG
|
||||
String log;
|
||||
log.reserve(120); // Prevent re-allocation
|
||||
log = F("NEXTION075: Interval values data enabled, resending idx=");
|
||||
log += String(UserVar[event->BaseVarIndex]);
|
||||
log += F(", value=");
|
||||
log += String(UserVar[event->BaseVarIndex+1]);
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
#endif
|
||||
|
||||
success = true;
|
||||
}
|
||||
else {
|
||||
/*
|
||||
String log = F("NEXTION075: Interval values data disabled, idx & value not resent");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
*/
|
||||
#ifdef DEBUG_LOG
|
||||
String log = F("NEXTION075: Interval values data disabled, idx & value not resent");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
#endif
|
||||
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Nextion commands received from events (including http) get processed here. PLUGIN_WRITE
|
||||
// Nextion commands received from events (including http) get processed here. PLUGIN_WRITE
|
||||
// does NOT process publish commands that are sent.
|
||||
case PLUGIN_WRITE: {
|
||||
|
||||
|
||||
String tmpString = string;
|
||||
int argIndex = tmpString.indexOf(',');
|
||||
if (argIndex) tmpString = tmpString.substring(0, argIndex);
|
||||
|
||||
|
||||
// Enable addLog() code below to help debug plugin write problems.
|
||||
/*
|
||||
String log;
|
||||
log.reserve(140); // Prevent re-allocation
|
||||
String log = F("Nextion arg0: ");
|
||||
log += tmpString;
|
||||
log += tmpString;
|
||||
log += F(", TaskDeviceName: ");
|
||||
log += getTaskDeviceName(event->TaskIndex);
|
||||
log += F(", event->TaskIndex: ");
|
||||
log += String(event->TaskIndex);
|
||||
log += String(event->TaskIndex);
|
||||
log += F(", cmd str: ");
|
||||
log += string;
|
||||
log += string;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
*/
|
||||
if (tmpString.equalsIgnoreCase(getTaskDeviceName(event->TaskIndex)) == true) { // If device names match we have a command to write.
|
||||
@@ -401,11 +422,14 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
tmpString = string.substring(argIndex + 1);
|
||||
sendCommand(tmpString.c_str(), HwSerial);
|
||||
|
||||
String log = F("NEXTION075 : WRITE, ");
|
||||
log += F("Command is ");
|
||||
log += (tmpString.c_str());
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
|
||||
String log;
|
||||
log.reserve(110); // Prevent re-allocation
|
||||
log = F("NEXTION075 : WRITE = ");
|
||||
log += tmpString;
|
||||
#ifdef DEBUG_LOG
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
#endif
|
||||
SendStatus(event->Source, log); // Reply (echo) to sender. This will print message on browser.
|
||||
success = true; // Set true only if plugin found a command to execute.
|
||||
}
|
||||
break;
|
||||
@@ -434,20 +458,19 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
case PLUGIN_TEN_PER_SECOND: {
|
||||
uint16_t i;
|
||||
uint8_t c;
|
||||
uint8_t charCount;
|
||||
String log;
|
||||
String Vidx;
|
||||
String Nvalue;
|
||||
String Svalue;
|
||||
String Nswitch;
|
||||
char __buffer[RXBUFFSZ+1]; // Staging buffer.
|
||||
|
||||
|
||||
if(rxPin < 0) {
|
||||
String log = F("NEXTION075 : Missing RxD Pin Number, aborted serial receive");
|
||||
String log = F("NEXTION075 : Missing RxD Pin, aborted serial receive");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
break;
|
||||
}
|
||||
@@ -455,6 +478,8 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
if(HwSerial == UARTSERIAL) {
|
||||
charCount = Serial.available(); // Prime the Hardware Serial engine.
|
||||
if(charCount >= RXBUFFWARN) { // ESP8266 has 128 byte circular Rx buffer.
|
||||
String log;
|
||||
log.reserve(70); // Prevent re-allocation
|
||||
log = F("NEXTION075 : RxD UART Buffer capacity warning,");
|
||||
log += String(charCount);
|
||||
log += F(" bytes");
|
||||
@@ -465,6 +490,8 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
if(SoftSerial == NULL) break; // SoftSerial missing, exit.
|
||||
charCount = SoftSerial->available(); // Prime the Soft Serial engine.
|
||||
if(charCount >= RXBUFFWARN) {
|
||||
String log;
|
||||
log.reserve(70); // Prevent re-allocation
|
||||
log = F("NEXTION075 : RxD SoftSerial Buffer capacity warning, ");
|
||||
log += String(charCount);
|
||||
log += F(" bytes");
|
||||
@@ -495,16 +522,20 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
UserVar[event->BaseVarIndex + 1] = __buffer[3];
|
||||
sendData(event);
|
||||
|
||||
log = F("NEXTION075 : code: ");
|
||||
log += __buffer[1];
|
||||
log += ",";
|
||||
log += __buffer[2];
|
||||
log += ",";
|
||||
log += __buffer[3];
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
#ifdef DEBUG_LOG
|
||||
String log;
|
||||
log.reserve(70); // Prevent re-allocation
|
||||
log = F("NEXTION075 : code: ");
|
||||
log += __buffer[1];
|
||||
log += ",";
|
||||
log += __buffer[2];
|
||||
log += ",";
|
||||
log += __buffer[3];
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (c == '|') {
|
||||
__buffer[0] = c; // Store in staging buffer.
|
||||
@@ -515,7 +546,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
else charCount = SoftSerial->available();
|
||||
|
||||
if(HwSerial == UARTSERIAL) {
|
||||
i = 1;
|
||||
i = 1;
|
||||
while (Serial.available() > 0 && i<RXBUFFSZ) { // Copy global serial buffer to local buffer.
|
||||
__buffer[i] = Serial.read();
|
||||
if (__buffer[i]==0x0a || __buffer[i]==0x0d) break;
|
||||
@@ -523,7 +554,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
}
|
||||
}
|
||||
else {
|
||||
i = 1;
|
||||
i = 1;
|
||||
while (SoftSerial->available() > 0 && i<RXBUFFSZ) { // Copy global serial buffer to local buffer.
|
||||
__buffer[i] = SoftSerial->read();
|
||||
if (__buffer[i]==0x0a || __buffer[i]==0x0d) break;
|
||||
@@ -532,11 +563,16 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
}
|
||||
|
||||
__buffer[i] = 0x00;
|
||||
|
||||
|
||||
String tmpString = __buffer;
|
||||
log = F("NEXTION075 : code: ");
|
||||
log += tmpString;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
String log;
|
||||
log.reserve(50); // Prevent re-allocation
|
||||
log = F("NEXTION075 : Code = ");
|
||||
log += tmpString;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
#endif
|
||||
|
||||
int argIndex = tmpString.indexOf(F(",i"));
|
||||
int argEnd = tmpString.indexOf(',', argIndex + 1);
|
||||
@@ -568,14 +604,21 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
UserVar[event->BaseVarIndex+1] = Svalue.toFloat();
|
||||
sendData(event);
|
||||
|
||||
log = F("NEXTION075 : Pipe Command Sent: ");
|
||||
log += __buffer;
|
||||
log += UserVar[event->BaseVarIndex];
|
||||
#ifdef DEBUG_LOG
|
||||
String log;
|
||||
log.reserve(80); // Prevent re-allocation
|
||||
log = F("NEXTION075 : Pipe Command Sent: ");
|
||||
log += __buffer;
|
||||
log += UserVar[event->BaseVarIndex];
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
log = F("NEXTION075 : Unknown Pipe Command, skipped");
|
||||
#ifdef DEBUG_LOG
|
||||
String log = F("NEXTION075 : Unknown Pipe Command, skipped");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
#endif
|
||||
}
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
}
|
||||
if(HwSerial == UARTSERIAL) charCount = Serial.available();
|
||||
@@ -590,7 +633,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
|
||||
}
|
||||
|
||||
|
||||
void sendCommand(const char *cmd, boolean SerialMode)
|
||||
void sendCommand(const char *cmd, boolean SerialMode)
|
||||
{
|
||||
if(txPin < 0) {
|
||||
String log = F("NEXTION075 : Missing TxD Pin Number, aborted sendCommand");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user