Compare commits

...
Author SHA1 Message Date
ESPEasy release bot 228f09f394 automatically updated release notes for mega-20181016 2018-10-16 04:00:06 +02:00
Gijs NoorlanderandGitHub 19ac19778b Merge pull request #1909 from giig1967g/bug/toggle
Fix for "togglegpio" without gpio initialization
2018-10-16 01:11:46 +02:00
Gijs NoorlanderandGitHub 5e2d517ba2 Merge pull request #1913 from TD-er/bugfix/revert_safe_strncpy
[Revert] Safe_strncpy cannot be used as generic strncpy replacement
2018-10-16 01:09:59 +02:00
TD-er 7a4b17983e [Revert] Safe_strncpy cannot be used as generic strncpy replacement
It does do more. So only use it to convert Strings to single char arrays.
2018-10-16 00:11:17 +02:00
Plebs 6eba74ccc1 Fix for togglegpio without gpio initialization 2018-10-15 17:34:35 +02:00
ESPEasy release bot 27a05b674c automatically updated release notes for mega-20181015 2018-10-15 04:00:10 +02:00
Gijs NoorlanderandGitHub 4a66bbf081 Merge pull request #1905 from TD-er/bugfix/safe_strncpy
[safe strncpy] Add warning when entered string is too large
2018-10-15 00:52:34 +02:00
TD-er 109bddb5c3 [safe strncpy] Add warning when entered string is too large
Started with this issue:
"Framed Oled Display mixing up multiple values in different lines" #1904

Now almost all calls to `strncpy` are replaced with this safe variant which does some checking and zero-ing the buffer.
2018-10-15 00:24:57 +02:00
Gijs NoorlanderandGitHub 8158a919d8 Merge pull request #1879 from giig1967g/bug/1864
fix for bug #1864
2018-10-14 10:35:19 +02:00
ESPEasy release bot 706b91168b automatically updated release notes for mega-20181014 2018-10-14 04:00:13 +02:00
Gijs NoorlanderandGitHub d047999c5e Merge pull request #1901 from TD-er/bugfix/warn_incomplete_IPsettings
[UX] Warn user for incomplete network IP settings
2018-10-14 00:59:37 +02:00
Gijs NoorlanderandGitHub 42f361aacd Merge pull request #1902 from TD-er/bugfix/1895_I2C
[#1895] Incorrect number bytes read I2C_read32_reg
2018-10-14 00:43:15 +02:00
TD-er 3dd076cf25 [#1895] Incorrect number bytes read I2C_read32_reg
See #1895
2018-10-14 00:42:03 +02:00
TD-er c0d155dd99 [UX] Warn user for incomplete network IP settings
Was a left-over remark in #1291
2018-10-14 00:22:46 +02:00
Gijs NoorlanderandGitHub c433174d71 Merge pull request #1900 from TD-er/bugfix/time_initialized_event
Time#Initialized event in rules before %syshour% is set
2018-10-13 23:37:04 +02:00
Gijs NoorlanderandGitHub b805b59baf Merge pull request #1899 from TD-er/bugfix/controller_check_reply
Fix send only first value by controller (#1891 #1892 #1835)
2018-10-13 23:07:32 +02:00
Gijs NoorlanderandGitHub 9fae0477d8 Merge pull request #1894 from letscontrolit/feature/1799_SSID_mainpage
[#1799] Add SSID to main page
2018-10-13 23:06:55 +02:00
TD-er f7ac04dc96 Time#Initialized event in rules before %syshour% is set
See [forum](http://www.letscontrolit.com/forum/viewtopic.php?f=6&t=5819&p=31376&hilit=Initialized#p31376) and [other report of same issue](https://www.letscontrolit.com/forum/viewtopic.php?t=5925)
2018-10-13 23:05:35 +02:00
TD-er d50e661efb [Controller] Fix send only first value
See #1891 #1892 #1835
Added an extra option to explicitly check the reply  on sent messages by a controller.
2018-10-13 22:21:36 +02:00
TD-er ca83983fd2 [#1891] Generic UPD controller - send only first value from sensor
See #1891
2018-10-12 01:04:46 +02:00
Gijs NoorlanderandGitHub d808f7e79b [#1799] Add SSID to main page
See #1799
2018-10-12 00:57:08 +02:00
ESPEasy release bot 9a763303ca automatically updated release notes for mega-20181011 2018-10-11 04:00:24 +02:00
Gijs NoorlanderandGitHub 7df9379f47 Merge pull request #1886 from TD-er/bugfix/reduce_memory_usage
Bugfix/reduce memory usage & improve load ExtraTaskSettings check
2018-10-11 01:48:31 +02:00
Plebs c32fe50521 Update src/_P023_OLED.ino 2018-10-10 23:28:34 +02:00
TD-er 48998886fe [WebLog] Disable weblog when it is not being read
This will prevent log items being generated when web log level is the highest level and it will clear the web log buffer to free memory.
2018-10-10 20:23:10 +02:00
TD-er 0e3b9c9e47 [Travis Doc] Remove building documentation by Travis
It seems to break the nightly builds, so remove for now.
2018-10-10 15:54:34 +02:00
ESPEasy release bot acecff4108 automatically updated release notes for mega-20181010 2018-10-10 04:00:09 +02:00
TD-er 3322b954a7 Make sure to load ExtraTaskSettings when needed
There is only 1 `ExtraTaskSettings` object, so make sure it is always in sync.
2018-10-10 01:29:38 +02:00
TD-er 4c4b844a4d [Controller] Set default timeout to 300 msec (was 100 msec)
300 msec should be enough for most online services like `cloudmqtt.com`
2018-10-10 00:19:26 +02:00
Gijs NoorlanderandGitHub cf4d28eb4f Merge pull request #1877 from giig1967g/new/pcfinvertstate
Fix for #1767
2018-10-10 00:05:11 +02:00
TD-er 555b5386ae [C009] Reduce memory usage of FHEM HTTP delay queue
The entire JSON string was stored in the queue element, but lot of it can be computed later.
2018-10-09 23:52:46 +02:00
Plebs 1903fdc7f2 fix for bug #1864
Removed '.' check
2018-10-09 18:16:15 +02:00
Plebs e648a20fd0 Update src/_P019_PCF8574.ino 2018-10-09 16:24:13 +02:00
ESPEasy release bot 692fb248ce automatically updated release notes for mega-20181009 2018-10-09 04:00:18 +02:00
Gijs NoorlanderandGitHub 5a87397ee0 Merge pull request #1872 from TD-er/bugfix/JSON_newline
Bugfix/json newline and memory allocation optimizations.
2018-10-09 01:14:50 +02:00
TD-er 99ba09b024 [StringConverter] Some optimisations to reduce memory allocation 2018-10-09 01:11:45 +02:00
TD-er f68516d5bb [LWIP1.4] Move back to LWIP1.4
The combination LWIP1.4 and core 2.4.1 were used until begin of May and those were considered stable.
2018-10-08 23:56:55 +02:00
TD-er 8102a0096d [JSON] Replace \n and \r in text fields
Those are not allowed in strings in JSON.
This leads to (web) log viewer crashing.
2018-10-08 23:44:44 +02:00
Gijs NoorlanderandGitHub 96ecf9bf29 Merge pull request #1871 from TD-er/bugfix/travis_check_for_docs
[Docs] Disable travis_terminate for now
2018-10-08 21:05:55 +02:00
TD-er ef1e371514 [Docs] Disable travis_terminate for now
Apparently `travis_terminate` does not exist anymore as a command to stop the script without failing.
2018-10-08 19:29:56 +02:00
Gijs NoorlanderandGitHub 45d67292a3 Merge pull request #1869 from TD-er/feature/travis_build_docs
Feature/travis build docs
2018-10-08 16:38:16 +02:00
TD-er b5662baad4 [Docs] Run Travic documentation check before compiling project 2018-10-08 16:04:12 +02:00
TD-er 12c3e5f81f [Docs] Add Travis check for documentation and stop build for docs only
Let Travis build the documentation and prevent full build when only documentation changed.
2018-10-08 15:34:02 +02:00
TD-er dececf24ee [Docs] Add favicon to documentation 2018-10-08 15:31:48 +02:00
ESPEasy release bot 3fb6dec382 automatically updated release notes for mega-20181008 2018-10-08 04:00:09 +02:00
Gijs NoorlanderandGitHub cef962c5f1 Added link to ReadTheDocs 2018-10-08 00:05:58 +02:00
Gijs NoorlanderandGitHub cca2335070 Merge pull request #1868 from TD-er/feature/update_sphinx_docs
[Docs] Added some directory structure and template
2018-10-08 00:01:07 +02:00
TD-er ffd0d26ed3 [Docs] Added some directory structure and template 2018-10-08 00:00:27 +02:00
GrovkillenandGitHub f787a9a9a6 [Sphinx] added extension "sphinx.ext.imgconverter" 2018-10-07 19:11:49 +02:00
Gijs NoorlanderandGitHub a0ae104794 Merge pull request #1865 from TD-er/feature/sphinx_docs
Feature/sphinx docs
2018-10-07 18:26:06 +02:00
TD-er 2b78810457 [Docs] First ESPEasy documentation (Controllers) 2018-10-07 18:23:25 +02:00
TD-er 43081c3ac8 [Sphinx] Initial setup for Sphinx documentation 2018-10-07 17:09:52 +02:00
ESPEasy release bot 33281c08c2 automatically updated release notes for mega-20181007 2018-10-07 04:00:09 +02:00
Gijs NoorlanderandGitHub 461aee26ad Merge pull request #1861 from TD-er/feature/debug_esp32_wrover_kit
Move stack to user memory space and close file handles + Add WROVER dev kit debug support
2018-10-06 23:19:18 +02:00
Gijs NoorlanderandGitHub 2b0b14b2e8 Merge pull request #1853 from blenk92/XSS
Fix XSS-Vulnerabilites on wifi configuration pages
2018-10-06 22:56:47 +02:00
Gijs NoorlanderandGitHub b7404666b1 Merge pull request #1851 from blenk92/mega
Force slash to be converted in htmlEscape
2018-10-06 22:56:37 +02:00
Gijs NoorlanderandGitHub da25aa87dd Merge pull request #1733 from giig1967g/new/invertvalue
Add four new TOGGLE commands
2018-10-06 22:55:57 +02:00
TD-er 10a1e634e2 [SPIFFS] Make sure to close open files on SPIFFS 2018-10-06 22:36:41 +02:00
TD-er 83f86449b9 [Stack] Move Arduino 'cont' stack into user RAM
As suggested [here](https://github.com/letscontrolit/ESPEasy/pull/1858#issuecomment-427578956) the previous used flag may also enable WPS.
So I used [this suggestion](https://github.com/esp8266/Arduino/issues/5148#issuecomment-424334261) to only re-allocate the stack at link time.
2018-10-06 20:01:39 +02:00
TD-er eee337d3e3 [ESP32] Allow Wrover dev kit to be used for PIO debug
Initial setup to be able to debug ESP32 Wrover dev kit from within Visual Studio Code + PIO plus.
2018-10-06 18:31:17 +02:00
ESPEasy release bot c549ecd329 automatically updated release notes for mega-20181006 2018-10-06 04:00:13 +02:00
Gijs NoorlanderandGitHub d9e682668d Merge pull request #1858 from TD-er/bugfix/increase_stack_5k
[Stack] Increase stack to 5k and reduce stack allocations in rules
2018-10-06 00:57:31 +02:00
TD-er e38072ac24 [Build flags] Remove NO_EXTRA_4K_HEAP
Core is now set to 2.4.1 again, so this flag is not needed anymore.
Also not sure if it did fix something in 2.4.2 core.
2018-10-06 00:53:53 +02:00
TD-er 6aebeba5dc [WiFi] Add delay to connection attempts in ControllerSettings
As suggested by @s0170071 [here](https://github.com/letscontrolit/ESPEasy/issues/1774#issuecomment-425038026)
2018-10-06 00:50:43 +02:00
TD-er da01c8f469 [Stack] Increase stack to 5k and reduce stack allocations in rules
Removed some unneeded stack allocations of 240 bytes in the rules processing.

Also gave the `process_system_event_queue()` a bit more priority to make sure it will be looked at at least once every 500 msec.
2018-10-05 00:52:22 +02:00
ESPEasy release bot b3b68856f0 automatically updated release notes for mega-20181004 2018-10-04 04:00:08 +02:00
Max bec5cb5334 Fix XSS-Vulnerabilites on wifi configuration pages
Add escaping of wifi network names that can be discovered using the search
functionality of for instantce the /setup page. As these names are also used
inside an html tag, a stronger html encoding variant has been added.
2018-10-04 03:19:09 +02:00
Gijs NoorlanderandGitHub bda6011390 Merge pull request #1852 from TD-er/bugfix/wificlient_timeout
[#1830] WiFi client timeout & back to core 2.4.1
2018-10-04 00:56:09 +02:00
Gijs NoorlanderandGitHub f3fdb93a52 Merge pull request #1831 from JojoS62/mega
[P013_HCSR04] added error checking and logging
2018-10-04 00:54:02 +02:00
Gijs NoorlanderandGitHub 200b060b15 Merge pull request #1846 from thomastech/mega
Update _P075_Nextion.ino
2018-10-04 00:51:44 +02:00
Gijs NoorlanderandGitHub ff41cb2f94 Merge pull request #1849 from uzi18/patch9
- partial fix for #1723
2018-10-04 00:51:03 +02:00
TD-er c134eb6d13 [Core 2.4.1] Move back to core 2.4.1 from 2.4.2
PlatformIO `espressif8266@1.7.3` instead of `espressif8266@1.8.0`
2.4.2 has a lot of improvements like 4k of extra free memory.
But also the number of hardware watchdog reboots has increased a lot and the PWM related code no longer seems to work on 2.4.2
So for now, back to 2.4.1 library.
2018-10-04 00:24:28 +02:00
TD-er f9e2a211e1 [WiFiClient] Set timeout and make it configurable for controllers
The default timeout for wificlient is 1000 msec. This may cause all kind of strange effects when the node is being blocked for so long.
2018-10-04 00:21:00 +02:00
Max bd3d3f66f4 Force slash to be converted in htmlEscape
According to the owasp XSS prevention cheat sheet it is also necessary to encode / as it may helps to end html entities
(Source https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet#RULE_.231_-_HTML_Escape_Before_Inserting_Untrusted_Data_into_HTML_Element_Content)
2018-10-03 23:58:24 +02:00
Bartlomiej Zimon 3bad683211 - partial fix for #1723 2018-10-03 21:13:56 +00:00
TD-er 68f6e913a2 [sendHttp] #1830 Set timeout and early exit on timeout reached
See #1830
2018-10-03 23:10:47 +02:00
Plebs 96420faa2f added "mcpgpiotoggle" command 2018-10-03 22:07:42 +02:00
ThomasandGitHub 8eea77e4ed Update _P075_Nextion.ino (#1)
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.
2018-10-03 09:02:02 -07:00
JojoS62 3d1f723787 Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into mega 2018-10-03 15:27:40 +02:00
JojoS62 5d459003e4 fixed 'unused variable' warning 2018-10-03 15:19:19 +02:00
JojoS62 ad208c357c code clean up
added default for filter size = 5.
clode clieanup: using constants, read setting into local vars for better readability,
2018-10-03 14:33:17 +02:00
ESPEasy release bot b1c777e6fd automatically updated release notes for mega-20181003 2018-10-03 04:00:18 +02:00
JojoS62 f5ab15c255 max distance depends on measurement unit
setting is in user units, ping function needs cm. Conversion added.
added unit to log output.
2018-10-02 17:41:23 +02:00
JojoS62 001ec6dcd2 added more settings: unit, filtertype and size
Measuring unit can be changed bewtween metric/imperial
Filtertype can none or median. If median a filtersize can be set.
Meauring value time to distance conversion now as float division, resulting in a higher resolution output.
2018-10-02 17:00:22 +02:00
JojoS62 15e5edccd8 added float calculation for time to distance conversion
returns higher resolution, especially for measuring in inches the integer resolution was low.
2018-10-02 16:57:33 +02:00
Gijs NoorlanderandGitHub 651353cc99 Merge pull request #1842 from crankyoldgit/patch-2
Acknowledge credit & origin of code used in _P016IR.ino
2018-10-02 13:41:35 +02:00
Gijs NoorlanderandGitHub 85f97551c5 Merge pull request #1841 from crankyoldgit/patch-1
Acknowledge credit & origin of code used in IRTX
2018-10-02 13:41:23 +02:00
David ConranandGitHub 6a158f68f5 Acknowledge credit & origin of code used in _P016IR.ino
FYI @TD-er

Ref: https://github.com/letscontrolit/ESPEasy/pull/1645#issuecomment-422253497
2018-10-02 21:36:18 +10:00
David ConranandGitHub 1933714de5 Acknowledge credit & origin of code used in IRTX
FYI @TD-er

Ref: https://github.com/letscontrolit/ESPEasy/pull/1645#issuecomment-422253497
2018-10-02 21:29:57 +10:00
ESPEasy release bot 545117ddf7 automatically updated release notes for mega-20181002 2018-10-02 04:00:29 +02:00
Gijs NoorlanderandGitHub 8eb479c411 Merge pull request #1837 from TD-er/bugfix/schedule_commands_yield
[Stack] Report stack usage on ESP32 and increase stack for ESP8266normal
2018-10-01 23:27:07 +02:00
TD-er b39f51ca3c [Stack] Report stack usage on ESP32 and increase stack for ESP8266normal
Stack usage on ESP32 is querying current stack, not statistics per RTOS task.
2018-10-01 22:15:32 +02:00
Gijs NoorlanderandGitHub 4511e08d3c Merge pull request #1805 from giig1967g/new/parse
split parseTemplate function
2018-10-01 17:12:21 +02:00
Gijs NoorlanderandGitHub 086e836956 Merge pull request #1836 from TD-er/feature/esp32_pio_1_4_0
[ESP32] Update to platform espressif32@1.4.0
2018-10-01 17:10:45 +02:00
Gijs NoorlanderandGitHub 3e20f617ea Merge pull request #1827 from TD-er/bugfix/DTH_Si7021_reading_stability
[#1798] Bugfix Sonoff Si7021 stability
2018-10-01 17:10:23 +02:00
TD-er 8d9aacb6a4 [ESP32] Update to PIO espressif32@1.4.0
[ESP32 IDF 3.1](https://github.com/espressif/esp-idf/releases/tag/v3.1)
[ESP32 PIO 1.4.0](https://github.com/platformio/platform-espressif32/releases/tag/v1.4.0)
2018-10-01 16:34:21 +02:00
TD-er 0670fa98a6 [ESP32] Add default partition table for reference 2018-10-01 16:32:20 +02:00
ESPEasy release bot 2b3d1517c9 automatically updated release notes for mega-20181001 2018-10-01 13:13:13 +02:00
Gijs NoorlanderandGitHub 0be79759d1 Merge pull request #1834 from TD-er/bugfix/detect_large_stack_allocs
[Stack] Move Arduino stack back to top of System stack instead of overlap
2018-10-01 01:25:38 +02:00
TD-er d47bede215 [Stack] Revert move Settings from stack to heap
Apparently the settings are allocated on the system stack and not on the Arduino stack.
This means it takes a lot of ram when allocated on the heap. So move it back to the system stack.
2018-10-01 00:51:04 +02:00
TD-er 2988a244b2 [Stack] Check if a block sent to save on SPIFFS is stack allocated 2018-10-01 00:42:01 +02:00
TD-er 8d87dc0175 [Heap] Use NO_EXTRA_4K_HEAP to get old stack address space
Originally the Arduino stack was placed on top of the system stack.
But since the system stack is mostly empty, it was decided to shift the Arduino stack to overlap the system stack to save an extra 4k of memory.

However, since a lot of Watchdog timer reboots are reported lately, this optimisation will now be reverted to see if it will improve stability.
This will use about 4k more RAM.
2018-10-01 00:40:55 +02:00
Gijs NoorlanderandGitHub 21c86e666a Merge pull request #1829 from TD-er/feature/1824_stack_stats
[#1824] Show stack size on main page
2018-09-30 19:37:16 +02:00
JojoS e6fdd3380a added error checking and logging
there were several errors that all resulted in a distance value reading of 0. Added an errorState that is printed to the log and helps troubleshooting.
Max. Distance checking can be turned off by setting max distance to 0 cm. If max. distance is used, exceeding the range will return a distance of 0 cm and print this to the log.
2018-09-30 15:23:36 +02:00
TD-er e04f71ab47 [#1824] Show stack size on main page
See #1824
2018-09-30 09:32:01 +02:00
ESPEasy release bot 90ae0720cf automatically updated release notes for mega-20180930 2018-09-30 04:00:24 +02:00
TD-er c8ecd8d374 [#1798] Bugfix Sonoff Si7021 stability
See #1798
2018-09-29 22:50:24 +02:00
Gijs NoorlanderandGitHub 7a451dd672 Merge pull request #1825 from TD-er/bugfix/proper_default_settings
Bugfix/proper default settings
2018-09-29 15:39:21 +02:00
Gijs NoorlanderandGitHub b06f6aa360 Merge pull request #1823 from JojoS62/mega
minor change: unit was missing for threshold in 'state' mode
2018-09-29 15:06:36 +02:00
TD-er 6b67415c71 [Defaults] Other default initializations 2018-09-29 15:05:10 +02:00
JojoS b095465d3a minor change: unit was missing for threshold in 'state' mode 2018-09-29 14:09:03 +02:00
Gijs NoorlanderandGitHub 81a5d911ba Merge pull request #1809 from JojoS62/mega
fixed distance reading
2018-09-29 13:38:44 +02:00
TD-er 48060d8d99 [Default Settings] Properly clear settings when reset to default 2018-09-29 12:40:29 +02:00
JojoS d303fa023f changed implementation to use shared_ptr instead of helper structure
this makes it easier to use this as a template for other sensors. No additional helper class/structure is needed.
2018-09-29 11:58:57 +02:00
JojoS 29b0cc1b32 removed sensordefs helper structure
to simplify the code
2018-09-27 22:00:01 +02:00
JojoS 584abd1e75 on INIT, delete sensordef before adding new 2018-09-27 14:19:54 +02:00
JojoS b4d8be0e59 fixed distance reading
the P_013_sensordef object was created on the stack and the destructor was called after leaving the PLUGIN_INIT. That deleted the included NewPingESP8266 object and using this object failed.
2018-09-27 11:56:31 +02:00
ESPEasy release bot 30f5c5cb7d automatically updated release notes for mega-20180927 2018-09-27 04:00:17 +02:00
Plebs 6e7286c6fb split parseTemplate function
simplifying code
2018-09-26 23:40:01 +02:00
Gijs NoorlanderandGitHub ae1db543ce Merge pull request #1804 from TD-er/bugfix/ESP32_partition_build
[ESP32] Split new partition layout to separate build
2018-09-26 22:52:30 +02:00
TD-er 947e1e0343 [Build] Add esp32test_1M8_partition build and warning on ESP32 partition 2018-09-26 22:49:30 +02:00
TD-er 0578d720f4 [ESP32] Split new partition layout to separate build
See "ESP32 not start after last upgrade" #1802
2018-09-26 21:55:02 +02:00
Gijs NoorlanderandGitHub 211d79bbd5 Merge pull request #1792 from TD-er/feature/ESP32_partition_table
[ESP32] Change partition size (will clear all settings) and add plugins
2018-09-26 09:05:44 +02:00
TD-er 91c5d60af9 [Info] Add board name to the sysinfo page. 2018-09-25 00:19:17 +02:00
TD-er 0d9bc8027d [ESP32] RTOS move handle_schedule() to core1 and increase stack
Enable RTOS flag in advanced settings (and reboot) to create separate threads on the 2nd ESP32 core, which will handle the scheduler.
This lowers stack usage and CPU load.
Also the ESP will no longer crash when saving large settings like for the Framed OLED plugin.
2018-09-25 00:18:46 +02:00
TD-er bd2bdab95c [Stack] Move Settings from stack to heap 2018-09-24 22:08:02 +02:00
TD-er c4f98fbc8e [Travis] Uninitialized variable in P073_7DGT 2018-09-24 20:13:24 +02:00
ESPEasy release bot b332a513b5 automatically updated release notes for mega-20180924 2018-09-24 04:00:20 +02:00
TD-er e36bb714f1 [ESP32] Change partition size (will clear all settings) and add plugins
The last update to > 0.12 increased the binary size a lot. This is mainly due to the added BLE support.
This makes it almost impossible to use the default partition table layout.

This changes the app partitions to 1.8 MB and reduces the SPIFFS to 320 k.
Changing the partition table will clear all settings, so make a backup of the settings.

Also added all plugins that would compile (not tested) for ESP32.

Known issue:
ESP32 might crash due to stack overflow when saving settings of plugins. (e.g. OLED framed plugin)
2018-09-24 00:23:45 +02:00
Gijs NoorlanderandGitHub 6e1cde343f Merge pull request #1791 from TD-er/bugfix/ESP32_I2C
[ESP32] Fix I2C handling to allow I2C devices to work
2018-09-23 22:14:43 +02:00
TD-er cce9ddd2dc [ESP32] Fix I2C handling to allow I2C devices to work
See this issue: [I2C repeated start broken](https://github.com/platformio/platform-espressif32/issues/126)
2018-09-23 21:17:55 +02:00
Gijs NoorlanderandGitHub 51be17a436 Merge pull request #1780 from clumsy-stefan/sensor_type_change
Change type of sensor for MCP and PCF devices
2018-09-23 20:18:38 +02:00
Gijs NoorlanderandGitHub 66d27a6697 Merge pull request #1776 from beigenmann/mega
Fix platformio
2018-09-23 20:17:33 +02:00
Gijs NoorlanderandGitHub a033e458fb Merge pull request #1782 from TD-er/bugfix/MQTT_reconnect_name
[MQTT] Work around for lost MQTT connection
2018-09-23 20:17:07 +02:00
Gijs NoorlanderandGitHub 494436bbff Merge pull request #1789 from sakinit/ESP32-to-core_esp32_1_3_0
ESP32: change to platform version core_esp32_1_3_0
2018-09-23 20:16:52 +02:00
sakinit 23bacd0762 change to platform version core_esp32_1_3_0 2018-09-23 17:49:47 +02:00
ESPEasy release bot f131254853 automatically updated release notes for mega-20180923 2018-09-23 04:00:12 +02:00
TD-er 657d15d962 [MQTT] Work around for lost MQTT connection
Maybe a fix for #1525
Meant to be a fix for #1530

It looks like the MQTT broker is refusing to accept a new connection from a specific client ID, as long as the broker thinks a client with that ID is still connected.
This work around adds the number of wifi reconnects to the clientID, to make the ID unique when a reconnect has to be made.
2018-09-23 00:54:55 +02:00
Gijs NoorlanderandGitHub 60910da48d Merge pull request #1749 from sakinit/ESP32-P1WifiGateway-updates
Esp32 p1 wifi gateway updates
2018-09-22 20:59:32 +02:00
Gijs NoorlanderandGitHub 6cee2d0f32 Merge pull request #1779 from TD-er/bugfix/BME280_slow_reading
[BME280] Fix slow reading of BME280 causing watchdog resets
2018-09-22 20:45:20 +02:00
stefan 533cb88a64 Change type of sensor for MCP and PCF devices
MCP and PCF devices should be of type SWNSOR_TYPE_SWITCH instead of SENSOR_TYPE_SINGLE so that the controllers can identify them correctly as switches (for GPIO related functions)
2018-09-22 20:41:53 +02:00
TD-er ffa32fe851 [BME280] Fix slow reading of BME280 causing watchdog resets
The plugin still had some delay and an active polling to see if the reading was ready.
This  took more than 1.5 sec per reading, which could trigger a Watchdog reset.
2018-09-22 20:34:56 +02:00
Gijs NoorlanderandGitHub b900cc9389 Merge pull request #1777 from TD-er/bugfix/MQTTimport
[MQTT import] Fix crash at boot when plugin is enabled
2018-09-22 19:13:25 +02:00
TD-er 8e11c19af6 [MQTT import] Fix crash at boot when plugin is enabled
See #1722
Inspired by [this comment](https://github.com/letscontrolit/ESPEasy/issues/1722#issuecomment-422191721) of @kainhofer
2018-09-22 16:39:16 +02:00
Benno Eigenmann f33ca6df78 Fix platformio 2018-09-22 15:57:09 +02:00
ESPEasy release bot 194a230d20 automatically updated release notes for mega-20180922 2018-09-22 04:00:22 +02:00
Gijs NoorlanderandGitHub 7a5a432016 Merge pull request #1773 from TD-er/reapply_reverts
Reapply reverts
2018-09-22 02:04:30 +02:00
TD-er 07511fe144 Revert "Revert "Fix elseif & Add if-elseif-else nesting levels""
This reverts commit a4fe8d42d2.
2018-09-21 22:15:13 +02:00
Gijs Noorlander 9a390136f9 Revert "Revert "Added 2 new operators in Calculate() function""
This reverts commit fa42cadfa1.
2018-09-21 22:14:30 +02:00
Gijs NoorlanderandGitHub 27a03f166a Merge pull request #1770 from TD-er/bugfix/revert_disable_command_scheduler_0915
Revert "[Scheduler] Disable command scheduler"
2018-09-21 22:10:50 +02:00
TD-er 0a63aeb8f0 Revert "[Scheduler] Disable command scheduler"
This reverts commit 38af765935.
2018-09-20 23:20:19 +02:00
sakinit 22c10ac615 ESP32 platform updated to 1.2.0; Webserver replaced by platform one 2018-09-16 16:44:25 +02:00
ESPEasy release bot ac83cfa249 automatically updated release notes for mega-20180916 2018-09-16 04:00:11 +02:00
Gijs NoorlanderandGitHub 6ef0c08bc4 Merge pull request #1754 from TD-er/bugfix/backgroundtasks_rules
[HW Watchdog] Backgroundtasks instead of yield during rules handling
2018-09-16 01:19:47 +02:00
TD-er 1ee965067d [HW Watchdog] Backgroundtasks instead of yield during rules handling 2018-09-16 01:19:14 +02:00
Gijs NoorlanderandGitHub e46a873fce Merge pull request #1753 from letscontrolit/revert-1641-mega
Revert "Fix elseif & Add if-elseif-else nesting levels"
2018-09-16 01:15:04 +02:00
Gijs NoorlanderandGitHub 784e956c96 Merge pull request #1752 from letscontrolit/revert-1742-new/not
Revert "Added 2 new operators in Calculate() function"
2018-09-16 01:14:32 +02:00
Gijs NoorlanderandGitHub b6eebb330a Added warning to release notes 20180915 2018-09-16 01:13:49 +02:00
Gijs NoorlanderandGitHub a4fe8d42d2 Revert "Fix elseif & Add if-elseif-else nesting levels" 2018-09-16 01:08:42 +02:00
Gijs NoorlanderandGitHub fa42cadfa1 Revert "Added 2 new operators in Calculate() function" 2018-09-16 01:04:37 +02:00
sakinit 17dc6fa859 Reverted Webserver change 2018-09-15 16:22:58 +02:00
sakinit 1432726dec ESP32: Split webserver spiffs filelist into pages 2018-09-15 12:14:33 +02:00
sakinit 7ba6ffa407 Fix ESP32 stack overflow in addPinSelect when called from P1WifiGateway
In addFormPinSelect move local variables from the stack to the heap
because when it is called from the P1WifiGateway there is not enough
stack space resulting in a stack overflow.
2018-09-15 12:14:27 +02:00
sakinit ab555e4d6b ESP32 build issues fixed
- Compiler issue of comment within comment
 - ESP32Webserver renamed to Webserver in ESP32 Arduino library
2018-09-15 12:14:22 +02:00
ESPEasy release bot d4259f4e23 automatically updated release notes for mega-20180915 2018-09-15 04:00:15 +02:00
Gijs NoorlanderandGitHub aef45c06f6 Merge pull request #1747 from TD-er/bugfix/sunrise
[#1698] Incorrect sunrise value southern hemisphere
2018-09-15 00:19:42 +02:00
Gijs NoorlanderandGitHub 828f9e6a30 Merge pull request #1746 from TD-er/feature/hostname_settings
Validate settings + hostname format option
2018-09-15 00:14:30 +02:00
TD-er e79d778cf4 [#1698] Incorrect sunrise value southern hemisphere
See #1698
2018-09-15 00:10:45 +02:00
TD-er 965eb2d994 [#1713] Make hostname-unitnumber optional & validate settings
See #1713
Also added validation check for some values in the settings at load/save of settings.
This could be extended later on for task settings.
2018-09-14 23:44:58 +02:00
Gijs NoorlanderandGitHub 657fccf49f Merge pull request #1641 from svmac/mega
Fix elseif & Add if-elseif-else nesting levels
2018-09-14 23:27:00 +02:00
Gijs NoorlanderandGitHub 228b41a1ac Merge pull request #1742 from giig1967g/new/not
Added 2 new operators in Calculate() function
2018-09-14 23:26:25 +02:00
Gijs NoorlanderandGitHub 3bba294eb7 Merge pull request #1745 from TD-er/bugfix/commands_not_using_scheduler
[Scheduler] Disable command scheduler
2018-09-14 23:22:21 +02:00
TD-er 0254181b3e [Controller settings] Validate settings on load/save
Make sure new values are set to proper defaults and within valid range.
2018-09-14 22:54:23 +02:00
Gijs NoorlanderandGitHub 3b67282abb Merge branch 'mega' into mega 2018-09-14 21:26:21 +02:00
TD-er 38af765935 [Scheduler] Disable command scheduler
For now the commands will no longer use the scheduler.
Commands running asynchronous is causing too much issues.
2018-09-14 21:23:09 +02:00
Plebs ff180b085c Added 2 new operators in Calculate
hi,
added 2 new operators for calculations:

!: Logical NOT:
returns 1 if operand is = 0
returns 0 if operand is != 0

%: MOD:
converts operands in integers and calculates the remainder of the division
2018-09-14 16:44:11 +02:00
ESPEasy release bot e0785eb796 automatically updated release notes for mega-20180914 2018-09-14 04:00:17 +02:00
Gijs NoorlanderandGitHub 75a83787a4 Merge pull request #1740 from TD-er/bugfix/disable_failing_plugin
[CrashRecover] Disable failing step based on failed boot count
2018-09-14 00:18:48 +02:00
Gijs NoorlanderandGitHub 65bea6343c Merge pull request #1734 from giig1967g/new/eventvalue
adding more parameters to EVENT
2018-09-14 00:15:41 +02:00
Gijs NoorlanderandGitHub 6fe61411a0 Added PayPal link 2018-09-14 00:10:48 +02:00
Gijs NoorlanderandGitHub 4552e91022 Merge pull request #1731 from giig1967g/new/calculate
Use parameters inside rules when calling commands
2018-09-14 00:06:13 +02:00
Gijs NoorlanderandGitHub cc94d70e6c Merge pull request #1730 from sakinit/feature-webserver-spiffs-filelist-multiple-pages
Feature: Split webserver spiffs filelist into pages
2018-09-13 23:50:11 +02:00
Gijs NoorlanderandGitHub 2435078f76 Merge pull request #1732 from TD-er/feature/flash_tools
[Flash tools] Add blank files and README.txt to dist folder
2018-09-13 23:49:43 +02:00
Gijs NoorlanderandGitHub dd2cd50d4b Merge pull request #1738 from giddyhup/mega
Fix IR plugin passing received code
2018-09-13 23:48:46 +02:00
TD-er fe38eb629d [CrashRecover] Disable failing step based on failed boot count
When the node gets locked in a boot loop, the only recovery is to erase it and start all over.
This fix will count the number of failed boots and above a certain threshold it will start disabling plugin, then controller and then notification.

Only one will be disabled at once, so the user can see which one is giving issues.
The disabled state will not be saved, so make sure to save the failing part when it is accessible again.
2018-09-13 23:44:24 +02:00
Plebs e961507456 added %eventvalue4% 2018-09-13 19:14:13 +02:00
Plebs e5ee5644bf final version with '=' sign as a prefix to identify the formula in rules 2018-09-12 22:48:44 +02:00
Gijs NoorlanderandGitHub ac6d85e214 Merge branch 'mega' into mega 2018-09-12 21:54:50 +02:00
Markus Gloede 39c2d54aee Fix IR plugin passing received code 2018-09-12 21:09:05 +02:00
Plebs 68efd5ff9e adding more parameters to EVENT
Hi,
adding the possibility to have 3 parameters when calling an event:
event,eventname=p1,p2,p3

The variable name are:
param 1: %eventvalue% or %eventvalue1%
param 2: %eventvalue2%
param 3: %eventvalue3%
2018-09-11 17:38:47 +02:00
Plebs 627f516f85 fixed calls to F-macro 2018-09-11 16:03:35 +02:00
Plebs a45fadf42e fixed ESP32 compatibility issue 2018-09-11 14:38:14 +02:00
Plebs 2ae52b849c few changes
optimized the call to CALCULATE only when needed and description of error codes.
2018-09-11 13:10:00 +02:00
Plebs 639685ea0d fixed float to int conversion 2018-09-11 10:16:44 +02:00
Plebs 867209de6d Add three new TOGGLE commands
Added three new commands to toggle the pins from 0 to 1 or viceversa and to toggle the task value:

Command: taskvaluetoggle
P001: gpiotoggle
P019:pcfgpiotoggle
2018-09-11 00:38:55 +02:00
TD-er 1d8b6d346b [Flash tools] Add blank files and README.txt 2018-09-10 23:02:38 +02:00
Plebs 2a5ac327d3 Use parameters inside rules when calling commands
Hi,
here is a PR for using parameters when calling commands from inside the rules.

The scope is to be able to use formulas instead of an integer when calling a command inside the rules.

Example:
taskvalueset,1,%eventvalue%+65,[Relay1#r1]+1

Usage could be:

Assuming you have a PCF8574 with ports from 65 to 72.

You can write a rule:

on turnOnRelay do
  pcfgpio,%eventvalue%+64,0
endon

And to turn on port65, the rule can be called like this:

event,turnOn=1
2018-09-10 22:59:50 +02:00
sakinit 886101d4d4 Feature: Split webserver spiffs filelist into pages 2018-09-10 17:37:05 +02:00
ESPEasy release bot 8fc6486855 automatically updated release notes for mega-20180910 2018-09-10 04:00:23 +02:00
Gijs NoorlanderandGitHub eeb1cc71b3 Merge pull request #1728 from TD-er/bugfix/command_send_http
Bugfix/command SendToHTTP  & Domoticz IDX > 9999
2018-09-10 01:02:58 +02:00
TD-er 13837830e8 [Domoticz IDX] Allow for IDX of > 9999 2018-09-09 23:00:16 +02:00
Gijs NoorlanderandGitHub f86ff46a82 Merge pull request #1727 from sakinit/P1WifiGatewayModuleBufSize
Reduced buffer size to prevent stack overflow
2018-09-09 22:53:23 +02:00
TD-er e901bc957a [HTTP] Command SendToHttp cannot connect
Incorrect check for return value.
As suggested via the [forum](https://www.letscontrolit.com/forum/viewtopic.php?f=6&t=5794&p=31203#p31181)
2018-09-09 21:58:14 +02:00
sakinit e7e10c5305 Reduced buffer size to prevent stack overflow 2018-09-09 19:54:23 +02:00
ESPEasy release bot 466ddfbe88 automatically updated release notes for mega-20180909 2018-09-09 04:00:14 +02:00
Gijs NoorlanderandGitHub c8f02d2a4c Merge pull request #1721 from TD-er/bugfix/wifi_connection_slow_server
[#1720] WiFi read all lines from slow servers
2018-09-08 23:14:33 +02:00
TD-er af1d9b38c9 [Serial log] Use buffer to prevent Serial buffer to overflow
Write the log lines to a std::deque and write that buffer to the serial port without overflow of the Serial buffer.
This flushing is done during background task.
2018-09-08 20:48:29 +02:00
Gijs NoorlanderandGitHub 7294217489 Revert [WDT] Add watchdog feed to backgroundtasks()
See comment: https://github.com/letscontrolit/ESPEasy/issues/1722#issuecomment-419659193
2018-09-08 20:31:16 +02:00
TD-er 208fa8a3aa [ESP32] Fix Build, ESP32 doesn't have the availableForWrite 2018-09-08 18:24:48 +02:00
TD-er b3b8eff127 [Serial Log] Limit serial log to prevent buffer overflow
As suggested here: https://github.com/letscontrolit/ESPEasy/issues/1625#issuecomment-411558353 by @thomastech
2018-09-08 16:19:58 +02:00
TD-er c6a90ab2d0 Add forward declaration for void backgroundtasks() 2018-09-08 16:00:06 +02:00
TD-er e7d28e8576 [#1720] WiFi read all lines from slow servers
See #1720
2018-09-08 15:20:39 +02:00
ESPEasy release bot efa27dc01d automatically updated release notes for mega-20180908 2018-09-08 04:00:20 +02:00
Gijs NoorlanderandGitHub 7c7b81911d Merge pull request #1669 from TD-er/bugfix/buffer_send_controller
Use queue to manage controllers and add timing settings per controller
2018-09-07 23:31:55 +02:00
Gijs NoorlanderandGitHub c2962d7337 Update README.md 2018-09-07 23:30:33 +02:00
TD-er e2e2059ff0 [Queue] Add memory check to determine full queue
Depending on the type of controller, the queued messages can be quite big.
This means the queue limit set can cause the memory to fill up.
This commit adds another check. If the free memory is less than 5000 bytes, it will consider the queue being full.
It will then also log the actual memory usage of the queue.
2018-09-07 22:32:17 +02:00
TD-er 4e95bbef7f [Scheduler] Do not schedule task device timer for tasks without interval
See #1709
2018-09-06 22:21:39 +02:00
TD-er b0a90b5b14 [#1714] Parse notification sender to allow some sender formatting
`bla@bla.com` will be set as `nodename <bla@bla.com>` in the mail header as from part.
`John Doe <bla@bla.com>` will now be used like that in the mail header.  

N.B. `<` is actually the separator, so as long as that character is being used to separate the name and mail address, it will work. (see #1714 )
2018-09-05 23:20:49 +02:00
TD-er c8ac1bbd59 [#1707] Getting logs from the /log even when password is not entered
See #1707
2018-09-05 22:49:38 +02:00
TD-er e6105bfe71 [WDT] Add yield() and runbackgroundtasks() when parsing rules
Rules parsing may take some time, so perform some yields to prevent watchdog triggers.
2018-09-04 22:48:07 +02:00
TD-er f2b4e17e2a [MQTT] Force MQTT client reconnect when wifi reconnects
It looks like the check for `client->connected()` may return false positives.
This means the MQTT client may assume the connection is still active, while it isn't.
2018-09-02 23:01:36 +02:00
TD-er 6f62ba0f9b [Watchdog] Add watchdog feed to backgroundtasks() function
A lot of reports recently about resets by the Hardware Watchdog.
So maybe this timer is not reset as often as before.
2018-09-02 23:01:36 +02:00
TD-er 36c6fd4bf7 [SPIFFS] Add SPIFFS size + usage to sysinfo page 2018-09-02 23:01:36 +02:00
TD-er 4446d76c83 [MQTT] Give MQTT state in log when connect state changes. 2018-09-02 23:01:36 +02:00
TD-er f354b05b07 [Nodes] Fix String copy to properly read node names 2018-09-02 23:01:36 +02:00
TD-er 467df9dde2 [Cleanup] Memory optimisation in available controllers and plugins
These structs to keep track of available plugins and controllers were fixed in size and also the member variables were not ordered in the most optimum way.
Also the name of the nodes always used 26 Bytes and would impose a memory leak when deleted.
2018-09-02 23:01:36 +02:00
TD-er dac243cc82 [Nodes] Highlight current node in the list of nodes 2018-09-02 23:01:36 +02:00
TD-er 3d1be96f8a [Nodes] Make Nodes list a map with dynamic size
Nodes list is now a std::map, which may contain up-to 254 nodes (1...254), since 255 is reserved for broadcast and 0 for "all known".
2018-09-02 23:01:36 +02:00
TD-er 0dd53e38d7 [C013] Remove some obsolete function
`C013_Send` did actually only call another function and some other bogus object loading without using the data.
2018-09-02 23:01:36 +02:00
TD-er df6338dfee [C012] Controller C012 now also uses Delay Queue 2018-09-02 23:01:36 +02:00
TD-er 1bab99de40 [Controller] Moved the 'magic' default and max values to defines
The Max and Default values for the Controller Delay Queue are now set in the `ESPEasy-Globals.h` file.
2018-09-02 23:01:36 +02:00
TD-er ec44efb59f [C011] Controller C011 now also uses Delay Queue
And some code cleanup of shared code in C001 and C003.
Also fixed an issue in C011, where the result was not returned when sending a value.
2018-09-02 23:01:36 +02:00
TD-er 202a6fd1f7 [Cleanup] Merge code duplication between C008 and C010
Both use the same Queue Element class.
2018-09-02 23:01:36 +02:00
TD-er dc7828d343 [C010] Controller C010 now also uses Delay Queue 2018-09-02 23:01:36 +02:00
TD-er 5c92731b41 [HTTP] Add user agent in request header
Should make this optional later
2018-09-02 23:01:36 +02:00
TD-er 8c34067d9c [HTTP Auth] Now using proper index for retrieving credentials
I was using the controller number and not the controller index to get the user credentials.
Should now work for FHEM.
2018-09-02 23:01:36 +02:00
TD-er 92025249fc [C009] Remove 'no port' option in HTTP packet
As tested by @clumsy-stefan, it didn't make a difference.
2018-09-02 23:01:36 +02:00
TD-er dd5b5bb7c3 [Client Connect] Check for success on connecting with client.
In case of domain name resolution error result can be negative.
See [Dns.cpp](https://github.com/esp8266/Arduino/blob/18f643c7e2d6a0da9d26ff2b14c94e6536ab78c1/libraries/Ethernet/src/Dns.cpp#L44)

Thus must match the result with 1.
2018-09-02 23:01:36 +02:00
TD-er 03bc2f34f6 [C009] Controller C009 now also uses Delay Queue
Also fixed an issue where sending some value to a controller would fail when the buffer is full and the settings set to "Delete Oldest".
2018-09-02 23:01:36 +02:00
TD-er a19b59e184 [C009] Removed port number from HTTP host header
As suggested here: https://github.com/letscontrolit/ESPEasy/pull/1669#issuecomment-414434578
This plugin is not using the Queue yet, but only had some duplicate code moved to a helper class.
I now removed the port number from the HTTP header, since I am not really sure if the standard demands it or only for the connect part.
Also changed the HTTP command to use the code that has the port in the header, just to check if that will break now compared to older builds.
2018-09-02 23:01:36 +02:00
TD-er 861bc64ce3 [#1666] Moved code duplication for HTTP requests to special functions
The controller source files are now much cleaner and all use the same code.
So it is easier to fix issues and add features and checks.
2018-09-02 23:01:36 +02:00
TD-er 7428b03e18 [C008] Let C008 use queue and some bugfixes
Also added statistics
And some optimizations
2018-09-02 23:01:36 +02:00
TD-er 54a6ecac7d [Webserver] Add fields for new Controller settings
Make sure all new settings are configurable for all controllers.
2018-09-02 23:01:36 +02:00
TD-er ec4aa627bb [MQTT] Use set controller settings on MQTT controllers 2018-09-02 23:01:36 +02:00
TD-er 0dc47bc04c [C007] Let C007 controller use queue 2018-09-02 23:01:36 +02:00
TD-er 5f029c59d2 Added USES_Cxxx checks
functions may not be mentioned in the scheduler when the controller is not included in the build.
2018-09-02 23:01:36 +02:00
TD-er 8beeeaf7fa [C004] Let C004 controller use queue and simplify using macros
Added some simplifications to the code by creating one big macro in `_CPlugin_Helper.h` that does all the work and defines.
This will reduce the chance of typo errors a lot.
2018-09-02 23:01:36 +02:00
TD-er 293b63e654 [C003] Let C003 controller use queue 2018-09-02 23:01:36 +02:00
TD-er 734150e87b [C001] Let C001 controller use queue 2018-09-02 23:01:36 +02:00
TD-er 6bb1b4086c [Controller] Make ControllerDelayHandler templated and comment
Added recognisable comments throughout the `ESPEasy-Globals.h` file and made ControllerDelayHandler templated to be used for other controllers to use.
2018-09-02 23:01:36 +02:00
TD-er 9cd2d2d843 [MQTT] Add controller delay queue for MQTT
MQTT messages are queued and processed at set pace to not overload the broker.

N.B. other controllers still need a similar queue to process. Current interval for those is now probably too low.

Also initial queue parameters must still be corrected when set to 0.
2018-09-02 23:01:36 +02:00
TD-er 02c022d7d5 [controller delay] Let controllers handle their own delay and buffer 2018-09-02 23:01:36 +02:00
svmac 5511ee9f38 fix mistake 2018-08-15 05:22:27 +02:00
svmac 427a22d23b some code optimization 2018-08-15 04:54:29 +02:00
svmac d271898968 reenabled code about ! 2018-08-14 03:52:29 +02:00
svmac 4051f0bd2e Fix elseif & Add if-elseif-else nesting levels
- The implementation of elseif is not correct. See this example:

Rule:
On SYSVAL do
if %syssec% < 10
publish %sysname%/IP,%ip%
elseif %syssec% < 20
publish %sysname%/Uptime/min,%uptime%
elseif %syssec% < 30
publish %sysname%/Systime/time,%systime%
elseif %syssec% < 40
publish %sysname%/RSSI/dB,%rssi%
elseif %syssec% < 50
publish %sysname%/Sunrise/time,%sunrise%
else
publish %sysname%/Sunset/time,%sunset-30m%
endif
endon

Result:
109360661 : EVENT: SYSVAL
109360668 : [if 11 < 10]=false
109360675 : [elseif 11 < 20]=true //THIS IS CORRECT
109360677 : ACT : publish Wemos51/Uptime/min,1823
109360686 : Command: publish
109360692 : [elseif 11 < 30]=false
109360697 : [elseif 11 < 40]=false
109360703 : [elseif 11 < 50]=false
109360705 : ReplacementString SunTime: %sunrise% offset: 0
109360708 : else = true //THIS IS NOT CORRECT
109360710 : ReplacementString SunTime: %sunset-30m% offset: -1800
109360712 : ACT : publish Wemos51/Sunset/time,20:46
109360723 : Command: publish
109360729 : EVENT: SYSVAL Processing time:68 milliSeconds

- Added also nesting levels to if-elseif-else clausules define in RULES_IF_MAX_NESTING_LEVEL declaration. Example:

Rule:
On SYSVAL do
if %eventvalue% > 1
publish %sysname%/Sunrise/time,%sunrise+1h%
else
if %syssec% < 10
publish %sysname%/IP,%ip%
elseif %syssec% < 20
publish %sysname%/Uptime/min,%uptime%
elseif %syssec% < 30
publish %sysname%/Systime/time,%systime%
elseif %syssec% < 40
publish %sysname%/eventvalue,%eventvalue%
elseif %syssec% < 50
publish %sysname%/Sunrise/time,%sunrise%
else
publish %sysname%/Sunset/time,%sunset-30m%
endif
endif
endon

Result:
>event,SYSVAL=-5
4977875 : Command: event
4977876 : EVENT: SYSVAL=-5
4977883 : Lev.1: [if -5 > 1]=false
4977886 : ReplacementString SunTime: %sunrise+1h% offset: 3600
4977889 : Lev.1: [else]=true
4977893 : Lev.2: [if 30 < 10]=false
4977900 : Lev.2: [elseif 30 < 20]=false
4977906 : Lev.2: [elseif 30 < 30]=false
4977913 : Lev.2: [elseif 30 < 40]=true
4977916 : ACT : publish Wemos49/eventvalue,-5
4977932 : ReplacementString SunTime: %sunrise% offset: 0
4977935 : Lev.2: [else]=false
4977938 : ReplacementString SunTime: %sunset-30m% offset: -1800
4977944 : EVENT: SYSVAL=-5 Processing time:67 milliSeconds

- Finally, the substitution of %eventvalue% is done earlier so that it can be used in if sentence too.
2018-08-11 16:51:56 +02:00
82 changed files with 5100 additions and 1865 deletions
+6
View File
@@ -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
View File
@@ -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
+8 -4
View File
@@ -1,9 +1,11 @@
|Latest Nightly | Build Status | Patreon | Ko-Fi | PayPal |
|-------|-------|-------|-------|-------|
| [![GitHub version](https://img.shields.io/github/release/letscontrolit/ESPEasy/all.svg)](https://github.com/letscontrolit/ESPEasy/releases/latest) | [![Build Status](https://travis-ci.org/letscontrolit/ESPEasy.svg?branch=mega)](https://travis-ci.org/letscontrolit/ESPEasy) | [![donate](https://img.shields.io/badge/donate-Patreon-blue.svg)](https://www.patreon.com/GrovkillenTDer) | [![donate](https://img.shields.io/badge/donate-KoFi-blue.svg)](https://ko-fi.com/grovkillentder) | [![donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](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), [Ko-Fi](https://ko-fi.com/grovkillentder) or [PayPal](https://www.paypal.me/espeasy) links above.
# ESPEasy (development branch)
Badge | Info |
-------|-------|
[![Build Status](https://travis-ci.org/letscontrolit/ESPEasy.svg?branch=mega)](https://travis-ci.org/letscontrolit/ESPEasy) | build status |
[![GitHub version](https://img.shields.io/github/release/letscontrolit/ESPEasy/all.svg)](https://github.com/letscontrolit/ESPEasy/releases/latest) | latest nightly |
Introduction and wiki: https://www.letscontrolit.com/wiki/index.php/ESPEasy#Introduction
@@ -39,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
+8 -3
View File
@@ -22,7 +22,8 @@ for ENV in \
dev_ESP8266PUYA_1024_VCC\
hard_SONOFF_POW\
hard_Shelly_1\
esp32dev;\
esp32dev\
esp32test_1M8_partition;\
do
echo
echo "### Deploying environment $ENV for version $VERSION"
@@ -34,10 +35,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
+61
View File
@@ -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/
+438
View File
@@ -1,3 +1,441 @@
-------------------------------------------------
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)
-------------------------------------------------
Release date: Sun Sep 9 04:00:14 CEST 2018
Gijs Noorlander (1):
Revert [WDT] Add watchdog feed to backgroundtasks()
TD-er (5):
[#1720] WiFi read all lines from slow servers
Add forward declaration for void backgroundtasks()
[Serial Log] Limit serial log to prevent buffer overflow
[ESP32] Fix Build, ESP32 doesn't have the availableForWrite
[Serial log] Use buffer to prevent Serial buffer to overflow
-------------------------------------------------
Changes in release mega-20180908 (since mega-20180904)
-------------------------------------------------
Release date: Sat Sep 8 04:00:20 CEST 2018
Gijs Noorlander (1):
Update README.md
TD-er (37):
[controller delay] Let controllers handle their own delay and buffer
[MQTT] Add controller delay queue for MQTT
[Controller] Make ControllerDelayHandler templated and comment
[C001] Let C001 controller use queue
[C003] Let C003 controller use queue
[C004] Let C004 controller use queue and simplify using macros
Added `USES_Cxxx` checks
[C007] Let C007 controller use queue
[MQTT] Use set controller settings on MQTT controllers
[Webserver] Add fields for new Controller settings
[C008] Let C008 use queue and some bugfixes
[#1666] Moved code duplication for HTTP requests to special functions
[C009] Removed port number from HTTP host header
[C009] Controller C009 now also uses Delay Queue
[Client Connect] Check for success on connecting with client.
[C009] Remove 'no port' option in HTTP packet
[HTTP Auth] Now using proper index for retrieving credentials
[HTTP] Add user agent in request header
[C010] Controller C010 now also uses Delay Queue
[Cleanup] Merge code duplication between C008 and C010
[C011] Controller C011 now also uses Delay Queue
[Controller] Moved the 'magic' default and max values to defines
[C012] Controller C012 now also uses Delay Queue
[C013] Remove some obsolete function
[Nodes] Make Nodes list a map with dynamic size
[Nodes] Highlight current node in the list of nodes
[Cleanup] Memory optimisation in available controllers and plugins
[Nodes] Fix String copy to properly read node names
[MQTT] Give MQTT state in log when connect state changes.
[SPIFFS] Add SPIFFS size + usage to sysinfo page
[Watchdog] Add watchdog feed to backgroundtasks() function
[MQTT] Force MQTT client reconnect when wifi reconnects
[WDT] Add yield() and runbackgroundtasks() when parsing rules
[#1707] Getting logs from the /log even when password is not entered
[#1714] Parse notification sender to allow some sender formatting
[Scheduler] Do not schedule task device timer for tasks without interval
[Queue] Add memory check to determine full queue
-------------------------------------------------
Changes in release mega-20180904 (since mega-20180903)
-------------------------------------------------
+1
View File
File diff suppressed because one or more lines are too long
+1
View File
File diff suppressed because one or more lines are too long
+1
View File
File diff suppressed because one or more lines are too long
+19
View File
@@ -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)
+35
View File
@@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd
+3
View File
@@ -0,0 +1,3 @@
recommonmark
Sphinx
sphinx-bootstrap-theme
+66
View File
@@ -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)
+2
View File
@@ -0,0 +1,2 @@
About Us
********
+2
View File
@@ -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

+35
View File
@@ -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
+2
View File
@@ -0,0 +1,2 @@
Plugins
*******
+2
View File
@@ -0,0 +1,2 @@
Command Reference
*****************
+2
View File
@@ -0,0 +1,2 @@
System Variables
****************
+2
View File
@@ -0,0 +1,2 @@
Rules
*****
+286
View File
@@ -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
+38
View File
@@ -0,0 +1,38 @@
.. 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
.. raw:: latex
\appendix
.. toctree::
:caption: Appendix
Participate/Documentation.rst
Reference/Command.rst
Reference/SystemVariable.rst
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
+7
View File
@@ -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,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x140000
5 app1 app ota_1 0x150000 0x140000
6 eeprom data 0x99 0x290000 0x1000
7 spiffs data spiffs 0x291000 0x16F000
+7
View File
@@ -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,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x1D0000
5 app1 app ota_1 0x1E0000 0x1D0000
6 eeprom data 0x99 0x3B0000 0x1000
7 spiffs data spiffs 0x3B1000 0x4F000
+75 -13
View File
@@ -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).
}
+17 -1
View File
@@ -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;
};
+73 -28
View File
@@ -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
@@ -158,7 +169,7 @@ 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}
platform = ${core_2_4_1.platform}
[Sonoff_8285]
board_upload.maximum_size = ${esp8285_1M.board_upload.maximum_size}
@@ -167,7 +178,7 @@ 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}
platform = ${core_2_4_1.platform}
[espWroom2M]
board_build.flash_mode = dout
@@ -193,6 +204,25 @@ build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.4m1m.ld
; 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 +236,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 +271,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 +288,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 +304,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 +320,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 +338,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 +360,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 +377,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 +393,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 +409,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 +425,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 +448,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 +465,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 +481,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 +497,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 +520,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 +537,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
+36
View File
@@ -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
+7 -6
View File
@@ -131,7 +131,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,11 +217,11 @@ 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
-30
View File
@@ -18,36 +18,6 @@ bool IsNumeric(char * source)
return result;
}
bool safeReadStringUntil(Stream &input, String &str, char terminator, unsigned int maxSize = 1024, unsigned int timeout = 1000)
{
int c;
const unsigned long timer = millis() + timeout;
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);
}
}
}
yield();
} while (!timeOutReached(timer));
addLog(LOG_LEVEL_ERROR, F("Timeout while reading input data!"));
return(false);
}
String Command_GetORSetIP(struct EventStruct *event,
const __FlashStringHelper *targetDescription,
+10 -25
View File
@@ -23,32 +23,17 @@ String Command_HTTP_SendToHTTP(struct EventStruct *event, const char* Line)
addLog(LOG_LEVEL_DEBUG, log);
}
WiFiClient client;
if (client.connect(host.c_str(), port.toInt())) {
String reply = F("GET ");
reply += path;
reply += F(" HTTP/1.1\r\n");
reply += F("Host: ");
reply += host;
reply += F("\r\n");
reply += F("Connection: close\r\n\r\n");
addLog(LOG_LEVEL_DEBUG, reply);
client.print(reply);
unsigned long timer = millis() + 200;
while (!client.available() && !timeOutReached(timer))
delay(1);
while (client.available()) {
// String line = client.readStringUntil('\n');
String line;
safeReadStringUntil(client, line, '\n');
if (line.substring(0, 15) == F("HTTP/1.1 200 OK"))
addLog(LOG_LEVEL_DEBUG, line);
delay(1);
const int port_int = port.toInt();
const bool connected = client.connect(host.c_str(), port_int) == 1;
if (connected) {
String hostportString = host;
if (port_int != 0 && port_int != 80) {
hostportString += ':';
hostportString += port_int;
}
client.flush();
client.stop();
String request = do_create_http_request(hostportString, F("GET"), path);
addLog(LOG_LEVEL_DEBUG, request);
send_via_http(F("Command_HTTP_SendToHTTP"), client, request, false);
}
}
return return_command_success();
+9
View File
@@ -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];
+50 -12
View File
@@ -15,6 +15,8 @@ void sendData(struct EventStruct *event)
// if (!Settings.TaskDeviceSendData[event->TaskIndex])
// return false;
/*
// Disabed for now, using buffers at controller side.
if (Settings.MessageDelay != 0)
{
const long dif = timePassedSince(lastSend);
@@ -31,6 +33,7 @@ void sendData(struct EventStruct *event)
// backgroundtasks();
}
}
*/
LoadTaskSettings(event->TaskIndex); // could have changed during background tasks.
@@ -39,21 +42,25 @@ void sendData(struct EventStruct *event)
event->ControllerIndex = x;
event->idx = Settings.TaskDeviceID[x][event->TaskIndex];
if (Settings.TaskDeviceSendData[event->ControllerIndex][event->TaskIndex] &&
Settings.ControllerEnabled[event->ControllerIndex] && Settings.Protocol[event->ControllerIndex])
Settings.ControllerEnabled[event->ControllerIndex] &&
Settings.Protocol[event->ControllerIndex])
{
event->ProtocolIndex = getProtocolIndex(Settings.Protocol[event->ControllerIndex]);
if (validUserVar(event)) {
CPlugin_ptr[event->ProtocolIndex](CPLUGIN_PROTOCOL_SEND, event, dummyString);
} else {
String log = F("Invalid value detected for controller ");
String controllerName;
CPlugin_ptr[event->ProtocolIndex](CPLUGIN_GET_DEVICENAME, event, controllerName);
log += controllerName;
addLog(LOG_LEVEL_DEBUG, log);
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("Invalid value detected for controller ");
String controllerName;
CPlugin_ptr[event->ProtocolIndex](CPLUGIN_GET_DEVICENAME, event, controllerName);
log += controllerName;
addLog(LOG_LEVEL_DEBUG, log);
}
}
}
}
// FIXME TD-er: This PLUGIN_EVENT_OUT seems to be unused.
PluginCall(PLUGIN_EVENT_OUT, event, dummyString);
lastSend = millis();
STOP_TIMER(SEND_DATA_STATS);
@@ -126,7 +133,7 @@ bool MQTTConnect(int controller_idx)
{
++mqtt_reconnect_count;
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(controller_idx, (byte*)&ControllerSettings, sizeof(ControllerSettings));
LoadControllerSettings(controller_idx, ControllerSettings);
if (!ControllerSettings.checkHostReachable(true))
return false;
if (MQTTclient.connected()) {
@@ -134,6 +141,7 @@ bool MQTTConnect(int controller_idx)
updateMQTTclient_connected();
}
mqtt = WiFiClient(); // workaround see: https://github.com/esp8266/Arduino/issues/4497#issuecomment-373023864
mqtt.setTimeout(ControllerSettings.ClientTimeout);
if (ControllerSettings.UseDNS) {
MQTTclient.setServer(ControllerSettings.getHost().c_str(), ControllerSettings.Port);
} else {
@@ -154,6 +162,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)
@@ -265,12 +282,33 @@ void SendStatus(byte source, String status)
boolean MQTTpublish(int controller_idx, const char* topic, const char* payload, boolean retained)
{
if (MQTTclient.publish(topic, payload, retained)) {
const bool success = MQTTDelayHandler.addToQueue(MQTT_queue_element(controller_idx, topic, payload, retained));
scheduleNextMQTTdelayQueue();
return success;
}
void scheduleNextMQTTdelayQueue() {
scheduleNextDelayQueue(TIMER_MQTT_DELAY_QUEUE, MQTTDelayHandler.getNextScheduleTime());
}
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)) {
setIntervalTimerOverride(TIMER_MQTT, 10); // Make sure the MQTT is being processed as soon as possible.
return true;
MQTTDelayHandler.markProcessed(true);
} else {
MQTTDelayHandler.markProcessed(false);
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("MQTT : process MQTT queue not published, ");
log += MQTTDelayHandler.sendQueue.size();
log += F(" items left in queue");
addLog(LOG_LEVEL_DEBUG, log);
}
}
addLog(LOG_LEVEL_DEBUG, F("MQTT : publish failed"));
return false;
scheduleNextMQTTdelayQueue();
STOP_TIMER(MQTT_DELAY_QUEUE);
}
/*********************************************************************************************\
@@ -281,7 +319,7 @@ void MQTTStatus(String& status)
ControllerSettingsStruct 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);
+319 -70
View File
@@ -11,7 +11,6 @@
// Set default configuration settings if you want (not mandatory)
// 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 UNIT 0 // Unit Number
@@ -69,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?
@@ -77,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
@@ -189,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
@@ -202,6 +206,32 @@
#define TIMER_30SEC 4
#define TIMER_MQTT 5
#define TIMER_STATISTICS 6
#define TIMER_MQTT_DELAY_QUEUE 7
#define TIMER_C001_DELAY_QUEUE 8
#define TIMER_C003_DELAY_QUEUE 9
#define TIMER_C004_DELAY_QUEUE 10
#define TIMER_C007_DELAY_QUEUE 11
#define TIMER_C008_DELAY_QUEUE 12
#define TIMER_C009_DELAY_QUEUE 13
#define TIMER_C010_DELAY_QUEUE 14
#define TIMER_C011_DELAY_QUEUE 15
#define TIMER_C012_DELAY_QUEUE 16
#define TIMER_C013_DELAY_QUEUE 17
// Minimum delay between messages for a controller to send in msec.
#define CONTROLLER_DELAY_QUEUE_DELAY_MAX 3600000
#define CONTROLLER_DELAY_QUEUE_DELAY_DFLT 100
// Queue length for controller messages not yet sent.
#define CONTROLLER_DELAY_QUEUE_DEPTH_MAX 25
#define CONTROLLER_DELAY_QUEUE_DEPTH_DFLT 10
// Number of retries to send a message by a controller.
// 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 300
#define PLUGIN_INIT_ALL 1
#define PLUGIN_INIT 2
@@ -262,6 +292,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
@@ -275,7 +306,11 @@
#if defined(PLUGIN_BUILD_TESTING) || defined(PLUGIN_BUILD_DEV)
#define DEVICES_MAX 75
#else
#define DEVICES_MAX 50
#ifdef ESP32
#define DEVICES_MAX 75
#else
#define DEVICES_MAX 50
#endif
#endif
#if defined(ESP8266)
@@ -295,7 +330,7 @@
#define PLUGIN_EXTRACONFIGVAR_MAX 16
#define CPLUGIN_MAX 16
#define NPLUGIN_MAX 4
#define UNIT_MAX 32 // Only relevant for UDP unicast message 'sweeps' and the nodelist.
#define UNIT_MAX 254 // unit 255 = broadcast
#define RULES_TIMER_MAX 8
#define PINSTATE_TABLE_MAX 32
#define RULES_MAX_SIZE 2048
@@ -303,6 +338,8 @@
#define RULESETS_MAX 4
#define RULES_BUFFER_SIZE 64
#define NAME_FORMULA_LENGTH_MAX 40
#define RULES_IF_MAX_NESTING_LEVEL 4
#define UDP_PACKETSIZE_MAX 2048
#define PIN_MODE_UNDEFINED 0
@@ -335,6 +372,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
@@ -370,6 +410,23 @@
#define CONFIG_FILE_SIZE 131072
#endif
// Forward declaration
struct ControllerSettingsStruct;
void scheduleNextDelayQueue(unsigned long id, unsigned long nextTime);
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,
TaskSettings_Type,
@@ -412,6 +469,7 @@ bool showSettingsFileLayout = false;
#include "I2CTypes.h"
#include <I2Cdev.h>
#include <map>
#include <deque>
#define FS_NO_GLOBALS
#if defined(ESP8266)
@@ -479,10 +537,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
@@ -547,9 +605,9 @@ WiFiUDP portUDP;
class TimingStats;
#define LOADFILE_STATS 0
#define LOOP_STATS 1
/*********************************************************************************************\
* CRCStruct
\*********************************************************************************************/
struct CRCStruct{
char compileTimeMD5[16+32+1]= "MD5_MD5_MD5_MD5_BoundariesOfTheSegmentsGoHere...";
char binaryFilename[32+32+1]= "ThisIsTheDummyPlaceHolderForTheBinaryFilename64ByteLongFilenames";
@@ -580,7 +638,9 @@ int16_t I2C_readS16_reg(uint8_t i2caddr, byte reg);
int16_t I2C_readS16_LE_reg(uint8_t i2caddr, byte reg);
I2Cdev i2cdev;
/*********************************************************************************************\
* SecurityStruct
\*********************************************************************************************/
struct SecurityStruct
{
SecurityStruct() {
@@ -612,23 +672,56 @@ struct SecurityStruct
uint8_t md5[16];
} SecuritySettings;
/*********************************************************************************************\
* SettingsStruct
\*********************************************************************************************/
struct SettingsStruct
{
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;
@@ -653,13 +746,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;
@@ -672,6 +769,7 @@ struct SettingsStruct
for (byte task = 0; task < TASKS_MAX; ++task) {
clearTask(task);
}
clearNetworkSettings();
}
void clearTask(byte task) {
@@ -786,6 +884,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
@@ -793,10 +892,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;
*/
/*********************************************************************************************\
* 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) {
ControllerSettingsStruct() : UseDNS(false), Port(0),
MinimalTimeBetweenMessages(100), MaxQueueDepth(10), MaxRetry(10),
DeleteOldest(false), ClientTimeout(100), MustCheckReply(false) {
for (byte i = 0; i < 4; ++i) {
IP[i] = 0;
}
@@ -816,6 +940,23 @@ struct ControllerSettingsStruct
char MQTTLwtTopic[129];
char LWTMessageConnect[129];
char LWTMessageDisconnect[129];
unsigned int MinimalTimeBetweenMessages;
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]);
@@ -838,6 +979,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()) {
@@ -866,21 +1008,22 @@ struct ControllerSettingsStruct
return false;
}
// Returns 1 if successful, 0 if there was a problem resolving the hostname or port
int beginPacket(WiFiUDP &client) {
if (!checkHostReachable(true)) {
return 0; // Host not reachable
}
byte retry = 2;
int connected = 0;
while (retry > 0 && !connected) {
while (retry > 0 && connected == 0) {
--retry;
connected = client.beginPacket(getIP(), Port);
if (connected != 0) return connected;
if (!checkHostReachable(false))
return false;
return 0;
delay(10);
}
return false;
return 0;
}
String getHostPortString() const {
@@ -915,6 +1058,10 @@ private:
};
/*********************************************************************************************\
* NotificationSettingsStruct
\*********************************************************************************************/
struct NotificationSettingsStruct
{
NotificationSettingsStruct() : Port(0), Pin1(0), Pin2(0) {
@@ -942,6 +1089,10 @@ struct NotificationSettingsStruct
//its safe to extend this struct, up to 4096 bytes, default values in config are 0
};
/*********************************************************************************************\
* ExtraTaskSettingsStruct
\*********************************************************************************************/
// This is only used by some plugins to store extra settings like formula descriptions.
// These settings can only be active for one plugin, meaning they have to be loaded
// over and over again from flash when another active plugin uses these values.
@@ -1015,6 +1166,9 @@ struct ExtraTaskSettingsStruct
int16_t TaskDevicePluginConfig[PLUGIN_EXTRACONFIGVAR_MAX];
} ExtraTaskSettings;
/*********************************************************************************************\
* EventStruct
\*********************************************************************************************/
struct EventStruct
{
EventStruct() :
@@ -1058,21 +1212,26 @@ struct EventStruct
byte *Data;
};
/*********************************************************************************************\
* LogStruct
\*********************************************************************************************/
#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;
}
@@ -1090,6 +1249,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);
}
@@ -1098,6 +1258,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);
@@ -1106,6 +1267,7 @@ struct LogStruct {
}
String get_logjson_formatted(bool& logLinesAvailable, unsigned long& timestamp) {
lastReadTimeStamp = millis();
logLinesAvailable = false;
if (isEmpty()) {
return "";
@@ -1119,31 +1281,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;
@@ -1167,26 +1313,50 @@ 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_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); }
/*********************************************************************************************\
* DeviceStruct
\*********************************************************************************************/
struct DeviceStruct
{
DeviceStruct() :
Number(0), Type(0), VType(0), Ports(0),
Number(0), Type(0), VType(0), Ports(0), ValueCount(0),
PullUpOption(false), InverseLogicOption(false), FormulaOption(false),
ValueCount(0), Custom(false), SendDataOption(false), GlobalSyncOption(false),
Custom(false), SendDataOption(false), GlobalSyncOption(false),
TimerOption(false), TimerOptional(false), DecimalsOnly(false) {}
bool connectedToGPIOpins() {
@@ -1198,33 +1368,45 @@ 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)
byte ValueCount; // The number of output values of a plugin. The value should match the number of keys PLUGIN_VALUENAME1_xxx
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)
} Device[DEVICES_MAX + 1]; // 1 more because first device is empty device
};
typedef std::vector<DeviceStruct> DeviceVector;
DeviceVector Device;
/*********************************************************************************************\
* ProtocolStruct
\*********************************************************************************************/
struct ProtocolStruct
{
ProtocolStruct() :
Number(0), usesMQTT(false), usesAccount(false), usesPassword(false),
defaultPort(0), usesTemplate(false), usesID(false), Custom(false) {}
defaultPort(0), Number(0), usesMQTT(false), usesAccount(false), usesPassword(false),
usesTemplate(false), usesID(false), Custom(false) {}
uint16_t defaultPort;
byte Number;
boolean usesMQTT;
boolean usesAccount;
boolean usesPassword;
int defaultPort;
boolean usesTemplate;
boolean usesID;
boolean Custom;
} Protocol[CPLUGIN_MAX];
};
typedef std::vector<ProtocolStruct> ProtocolVector;
ProtocolVector Protocol;
/*********************************************************************************************\
* NotificationStruct
\*********************************************************************************************/
struct NotificationStruct
{
NotificationStruct() :
@@ -1234,20 +1416,29 @@ struct NotificationStruct
byte usesGPIO;
} Notification[NPLUGIN_MAX];
/*********************************************************************************************\
* NodeStruct
\*********************************************************************************************/
struct NodeStruct
{
NodeStruct() :
age(0), build(0), nodeName(NULL), nodeType(0)
build(0), age(0), nodeType(0)
{
for (byte i = 0; i < 4; ++i) ip[i] = 0;
}
String nodeName;
byte ip[4];
byte age;
uint16_t build;
char* nodeName;
byte age;
byte nodeType;
} Nodes[UNIT_MAX];
};
typedef std::map<byte, NodeStruct> NodesMap;
NodesMap Nodes;
/*********************************************************************************************\
* systemTimerStruct
\*********************************************************************************************/
struct systemTimerStruct
{
systemTimerStruct() :
@@ -1264,8 +1455,12 @@ struct systemTimerStruct
};
std::map<unsigned long, systemTimerStruct> systemTimers;
/*********************************************************************************************\
* pinStatesStruct
\*********************************************************************************************/
struct pinStatesStruct
{
pinStatesStruct() : plugin(0), index(0), mode(0), value(0) {}
byte plugin;
byte index;
byte mode;
@@ -1277,21 +1472,26 @@ struct pinStatesStruct
#define RTC_BASE_STRUCT 64
#define RTC_BASE_USERVAR 74
/*********************************************************************************************\
* RTCStruct
\*********************************************************************************************/
//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;
@@ -1301,8 +1501,14 @@ String printWebString = "";
boolean printToWebJSON = false;
float UserVar[VARS_PER_TASK * TASKS_MAX];
/*********************************************************************************************\
* rulesTimerStruct
\*********************************************************************************************/
struct rulesTimerStatus
{
rulesTimerStatus() : timestamp(0), interval(0), paused(false) {}
unsigned long timestamp;
unsigned int interval; //interval in milliseconds
boolean paused;
@@ -1310,14 +1516,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;
@@ -1464,16 +1671,22 @@ 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
\*********************************************************************************************/
class TimingStats {
public:
TimingStats() : _timeTotal(0.0), _count(0), _maxVal(0), _minVal(4294967295) {}
@@ -1608,17 +1821,33 @@ 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 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
@@ -1626,13 +1855,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 ");
@@ -1644,6 +1874,25 @@ String getMiscStatsName(int stat) {
case PROC_SYS_TIMER: return F("proc_system_timer() ");
case SET_NEW_TIMER: return F("setNewTimerAt() ");
case TIME_DIFF_COMPUTE: return F("timeDiff() ");
case MQTT_DELAY_QUEUE: return F("Delay queue MQTT");
case C001_DELAY_QUEUE:
case C002_DELAY_QUEUE:
case C003_DELAY_QUEUE:
case C004_DELAY_QUEUE:
case C005_DELAY_QUEUE:
case C006_DELAY_QUEUE:
case C007_DELAY_QUEUE:
case C008_DELAY_QUEUE:
case C009_DELAY_QUEUE:
case C010_DELAY_QUEUE:
case C011_DELAY_QUEUE:
case C012_DELAY_QUEUE:
case C013_DELAY_QUEUE:
{
String result = F("Delay queue ");
result += get_formatted_Controller_number(static_cast<int>(stat - C001_DELAY_QUEUE + 1));
return result;
}
}
return F("Unknown");
}
+85 -14
View File
@@ -83,6 +83,8 @@
// Define globals before plugin sets to allow a personal override of the selected plugins
#include "ESPEasy-Globals.h"
#include "define_plugin_sets.h"
// Plugin helper needs the defined controller sets, thus include after 'define_plugin_sets.h'
#include "_CPlugin_Helper.h"
// Blynk_get prototype
boolean Blynk_get(const String& command, byte controllerIndex,float *data = NULL );
@@ -104,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);
@@ -117,8 +124,6 @@ void setup()
ledChannelPin[x] = -1;
#endif
lowestRAM = FreeMem();
Serial.begin(115200);
// Serial.print("\n\n\nBOOOTTT\n\n\n");
@@ -156,6 +161,7 @@ void setup()
//warm boot
if (readFromRTC())
{
RTC.bootFailedCount++;
RTC.bootCounter++;
readUserVarFromRTC();
@@ -191,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();
@@ -302,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
@@ -351,6 +375,14 @@ void RTOS_Task10ps( void * parameter )
run10TimesPerSecond();
}
}
void RTOS_HandleSchedule( void * parameter )
{
while (true){
handle_schedule();
}
}
#endif
int firstEnabledMQTTController() {
@@ -433,8 +465,11 @@ int getLoopCountPerSec() {
\*********************************************************************************************/
void loop()
{
/*
//FIXME TD-er: No idea what this does.
if(MainLoopCall_ptr)
MainLoopCall_ptr();
*/
updateLoopStats();
@@ -462,8 +497,11 @@ void loop()
if (firstLoopConnectionsEstablished) {
firstLoop = false;
timerAwakeFromDeepSleep = millis(); // Allow to run for "awake" number of seconds, now we have wifi.
schedule_all_task_device_timers();
}
// 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())
@@ -478,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();
@@ -529,11 +570,34 @@ void runPeriodicalMQTT() {
}
}
String getMQTT_state() {
switch (MQTTclient.state()) {
case MQTT_CONNECTION_TIMEOUT : return F("Connection timeout");
case MQTT_CONNECTION_LOST : return F("Connection lost");
case MQTT_CONNECT_FAILED : return F("Connect failed");
case MQTT_DISCONNECTED : return F("Disconnected");
case MQTT_CONNECTED : return F("Connected");
case MQTT_CONNECT_BAD_PROTOCOL : return F("Connect bad protocol");
case MQTT_CONNECT_BAD_CLIENT_ID : return F("Connect bad client_id");
case MQTT_CONNECT_UNAVAILABLE : return F("Connect unavailable");
case MQTT_CONNECT_BAD_CREDENTIALS: return F("Connect bad credentials");
case MQTT_CONNECT_UNAUTHORIZED : return F("Connect unauthorized");
default: return "";
}
}
void updateMQTTclient_connected() {
if (MQTTclient_connected != MQTTclient.connected()) {
MQTTclient_connected = !MQTTclient_connected;
if (!MQTTclient_connected)
addLog(LOG_LEVEL_ERROR, F("MQTT : Connection lost"));
if (!MQTTclient_connected) {
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
String connectionError = F("MQTT : Connection lost, state: ");
connectionError += getMQTT_state();
addLog(LOG_LEVEL_ERROR, connectionError);
}
} else {
schedule_all_tasks_using_MQTT_controller();
}
if (Settings.UseRules) {
String event = MQTTclient_connected ? F("MQTT#Connected") : F("MQTT#Disconnected");
rulesProcessing(event);
@@ -775,13 +839,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;
@@ -803,6 +864,16 @@ void backgroundtasks()
//checkRAM(F("backgroundtasks"));
//always start with a yield
yield();
/*
// 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
ESP.wdtFeed();
#endif
*/
//prevent recursion!
if (runningBackgroundTasks)
@@ -814,7 +885,7 @@ void backgroundtasks()
#if defined(ESP8266)
tcpCleanup();
#endif
process_serialLogBuffer();
if(!UseRTOSMultitasking){
if (Settings.UseSerial)
if (Serial.available())
+140 -34
View File
@@ -77,7 +77,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 +113,7 @@ void fileSystemCheck()
{
ResetFactory();
}
f.close();
if (f) f.close();
}
else
{
@@ -147,9 +147,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 +187,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 +225,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 +417,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 +446,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 +529,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 +548,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 +564,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 +627,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 +661,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 +672,7 @@ String LoadFromFile(char* fname, int offset, byte* memAddress, int datasize)
f.close();
STOP_TIMER(LOADFILE_STATS);
delay(1);
return(String());
}
+6 -2
View File
@@ -126,6 +126,7 @@ void processGotIP() {
initTime();
}
mqtt_reconnect_count = 0;
MQTTclient_should_reconnect = true;
timermqtt_interval = 100;
setIntervalTimer(TIMER_MQTT);
if (Settings.UseRules)
@@ -417,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);
}
@@ -675,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
View File
@@ -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
+716 -336
View File
File diff suppressed because it is too large Load Diff
+50 -41
View File
@@ -107,20 +107,21 @@ void checkUDP()
for (byte x = 0; x < 4; x++)
ip[x] = packetBuffer[x + 8];
if (unit < UNIT_MAX)
{
Nodes[unit].age = 0; // Create a new element when not present
NodesMap::iterator it = Nodes.find(unit);
if (it != Nodes.end()) {
for (byte x = 0; x < 4; x++)
Nodes[unit].ip[x] = packetBuffer[x + 8];
Nodes[unit].age = 0; // reset 'age counter'
it->second.ip[x] = packetBuffer[x + 8];
it->second.age = 0; // reset 'age counter'
if (len >= 41) // extended packet size
{
// FIXME TD-er: Memory leak waiting to happen.
Nodes[unit].build = packetBuffer[13] + 256*packetBuffer[14];
if (Nodes[unit].nodeName==0)
Nodes[unit].nodeName = (char *)malloc(26);
memcpy(Nodes[unit].nodeName,reinterpret_cast<byte*>(&packetBuffer[15]), 25);
Nodes[unit].nodeName[25]=0;
Nodes[unit].nodeType = packetBuffer[40];
it->second.build = packetBuffer[13] + 256*packetBuffer[14];
char tmpNodeName[26] = {0};
memcpy(&tmpNodeName[0], reinterpret_cast<byte*>(&packetBuffer[15]), 25);
tmpNodeName[25] = 0;
it->second.nodeName = tmpNodeName;
it->second.nodeName.trim();
it->second.nodeType = packetBuffer[40];
}
}
@@ -166,17 +167,17 @@ void SendUDPCommand(byte destUnit, char* data, byte dataLength)
if (!WiFiConnected(100)) {
return;
}
byte firstUnit = 1;
byte lastUnit = UNIT_MAX - 1;
if (destUnit != 0)
{
firstUnit = destUnit;
lastUnit = destUnit;
}
for (int x = firstUnit; x <= lastUnit; x++)
{
sendUDP(x, (byte*)data, dataLength);
sendUDP(destUnit, (byte*)data, dataLength);
delay(10);
} else {
for (NodesMap::iterator it = Nodes.begin(); it != Nodes.end(); ++it) {
if (it->first != Settings.Unit) {
sendUDP(it->first, (byte*)data, dataLength);
delay(10);
}
}
}
delay(50);
}
@@ -190,9 +191,18 @@ void sendUDP(byte unit, byte* data, byte size)
if (!WiFiConnected(100)) {
return;
}
if (unit != 255)
if (Nodes[unit].ip[0] == 0)
IPAddress remoteNodeIP;
if (unit == 255)
remoteNodeIP = {255,255,255,255};
else {
NodesMap::iterator it = Nodes.find(unit);
if (it == Nodes.end())
return;
if (it->second.ip[0] == 0)
return;
remoteNodeIP = it->second.ip;
}
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
String log = F("UDP : Send UDP message to ");
@@ -201,12 +211,6 @@ void sendUDP(byte unit, byte* data, byte size)
}
statusLED(true);
IPAddress remoteNodeIP;
if (unit == 255)
remoteNodeIP = {255,255,255,255};
else
remoteNodeIP = Nodes[unit].ip;
portUDP.beginPacket(remoteNodeIP, Settings.UDPPort);
portUDP.write(data, size);
portUDP.endPacket();
@@ -218,14 +222,17 @@ void sendUDP(byte unit, byte* data, byte size)
\*********************************************************************************************/
void refreshNodeList()
{
for (byte counter = 0; counter < UNIT_MAX; counter++)
{
if (Nodes[counter].ip[0] != 0)
{
Nodes[counter].age++; // increment age counter
if (Nodes[counter].age > 10) // if entry to old, clear this node ip from the list.
for (byte x = 0; x < 4; x++)
Nodes[counter].ip[x] = 0;
for (NodesMap::iterator it = Nodes.begin(); it != Nodes.end(); ) {
bool mustRemove = true;
if (it->second.ip[0] != 0) {
if (it->second.age < 10) {
it->second.age++;
mustRemove = false;
++it;
}
}
if (mustRemove) {
it = Nodes.erase(it);
}
}
}
@@ -277,15 +284,17 @@ void sendSysInfoUDP(byte repeats)
delay(500);
}
// store my own info also in the list...
if (Settings.Unit < UNIT_MAX)
Nodes[Settings.Unit].age = 0; // Create new node when not already present.
// store my own info also in the list
NodesMap::iterator it = Nodes.find(Settings.Unit);
if (it != Nodes.end())
{
IPAddress ip = WiFi.localIP();
for (byte x = 0; x < 4; x++)
Nodes[Settings.Unit].ip[x] = ip[x];
Nodes[Settings.Unit].age = 0;
Nodes[Settings.Unit].build = Settings.Build;
Nodes[Settings.Unit].nodeType = NODE_TYPE_ID;
it->second.ip[x] = ip[x];
it->second.age = 0;
it->second.build = Settings.Build;
it->second.nodeType = NODE_TYPE_ID;
}
}
+122 -11
View File
@@ -41,18 +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;
yield(); // See: https://github.com/letscontrolit/ESPEasy/issues/1818#issuecomment-425351328
switch (timerType) {
case CONST_INTERVAL_TIMER:
process_interval_timer(id, timer);
@@ -75,12 +83,23 @@ void setIntervalTimer(unsigned long id) {
setIntervalTimer(id, millis());
}
void setIntervalTimerAt(unsigned long id, unsigned long newtimer) {
setNewTimerAt(getMixedId(CONST_INTERVAL_TIMER, id), newtimer);
}
void setIntervalTimerOverride(unsigned long id, unsigned long msecFromNow) {
unsigned long timer = millis();
setNextTimeInterval(timer, msecFromNow);
setNewTimerAt(getMixedId(CONST_INTERVAL_TIMER, id), timer);
}
void scheduleNextDelayQueue(unsigned long id, unsigned long nextTime) {
if (nextTime != 0) {
// Schedule for next process run.
setIntervalTimerAt(id, nextTime);
}
}
void setIntervalTimer(unsigned long id, unsigned long lasttimer) {
// Set the initial timers for the regular runs
unsigned long interval = 0;
@@ -91,6 +110,20 @@ void setIntervalTimer(unsigned long id, unsigned long lasttimer) {
case TIMER_30SEC: interval = 30000; break;
case TIMER_MQTT: interval = timermqtt_interval; break;
case TIMER_STATISTICS: interval = 30000; break;
// Fall-through for all DelayQueue, which are just the fall-back timers.
// The timers for all delay queues will be set according to their own settings as long as there is something to process.
case TIMER_MQTT_DELAY_QUEUE:
case TIMER_C001_DELAY_QUEUE:
case TIMER_C003_DELAY_QUEUE:
case TIMER_C004_DELAY_QUEUE:
case TIMER_C007_DELAY_QUEUE:
case TIMER_C008_DELAY_QUEUE:
case TIMER_C009_DELAY_QUEUE:
case TIMER_C010_DELAY_QUEUE:
case TIMER_C011_DELAY_QUEUE:
case TIMER_C012_DELAY_QUEUE:
case TIMER_C013_DELAY_QUEUE:
interval = 1000; break;
}
unsigned long timer = lasttimer;
setNextTimeInterval(timer, interval);
@@ -98,6 +131,8 @@ void setIntervalTimer(unsigned long id, unsigned long lasttimer) {
}
void process_interval_timer(unsigned long id, unsigned long lasttimer) {
// Set the interval timer now, it may be altered by the commands below.
// This is the default next-run-time.
setIntervalTimer(id, lasttimer);
switch (id) {
case TIMER_20MSEC:
@@ -119,6 +154,63 @@ void process_interval_timer(unsigned long id, unsigned long lasttimer) {
case TIMER_STATISTICS:
logTimerStatistics();
break;
case TIMER_MQTT_DELAY_QUEUE:
processMQTTdelayQueue();
break;
#ifdef USES_C001
case TIMER_C001_DELAY_QUEUE:
process_c001_delay_queue();
break;
#endif
#ifdef USES_C003
case TIMER_C003_DELAY_QUEUE:
process_c003_delay_queue();
break;
#endif
#ifdef USES_C004
case TIMER_C004_DELAY_QUEUE:
process_c004_delay_queue();
break;
#endif
#ifdef USES_C007
case TIMER_C007_DELAY_QUEUE:
process_c007_delay_queue();
break;
#endif
#ifdef USES_C008
case TIMER_C008_DELAY_QUEUE:
process_c008_delay_queue();
break;
#endif
#ifdef USES_C009
case TIMER_C009_DELAY_QUEUE:
process_c009_delay_queue();
break;
#endif
#ifdef USES_C010
case TIMER_C010_DELAY_QUEUE:
process_c010_delay_queue();
break;
#endif
#ifdef USES_C011
case TIMER_C011_DELAY_QUEUE:
process_c011_delay_queue();
break;
#endif
#ifdef USES_C012
case TIMER_C012_DELAY_QUEUE:
process_c012_delay_queue();
break;
#endif
/*
#ifdef USES_C013
case TIMER_C013_DELAY_QUEUE:
process_c013_delay_queue();
break;
#endif
*/
// When extending this, also extend in _CPlugin_Helper.h
// Look for DEFINE_Cxxx_DELAY_QUEUE_MACRO
}
}
@@ -213,18 +305,37 @@ void schedule_all_task_device_timers() {
}
}
void schedule_task_device_timer(unsigned long task_index, unsigned long runAt) {
/*
String log = F("schedule_task_device_timer: task: ");
log += task_index;
log += F(" @ ");
log += runAt;
if (Settings.TaskDeviceEnabled[task_index]) {
log += F(" (enabled)");
void schedule_all_tasks_using_MQTT_controller() {
int ControllerIndex = firstEnabledMQTTController();
if (ControllerIndex < 0) return;
for (byte task = 0; task < TASKS_MAX; task++) {
if (Settings.TaskDeviceSendData[ControllerIndex][task] &&
Settings.ControllerEnabled[ControllerIndex] &&
Settings.Protocol[ControllerIndex])
{
schedule_task_device_timer_at_init(task);
}
}
addLog(LOG_LEVEL_INFO, log);
*/
}
void schedule_task_device_timer(unsigned long task_index, unsigned long runAt) {
/*
String log = F("schedule_task_device_timer: task: ");
log += task_index;
log += F(" @ ");
log += runAt;
if (Settings.TaskDeviceEnabled[task_index]) {
log += F(" (enabled)");
}
addLog(LOG_LEVEL_INFO, log);
*/
if (task_index >= TASKS_MAX) return;
byte DeviceIndex = getDeviceIndex(Settings.TaskDeviceNumber[task_index]);
if (!Device[DeviceIndex].TimerOption) return;
if (Device[DeviceIndex].TimerOptional && Settings.TaskDeviceTimer[task_index] == 0) {
return;
}
if (Settings.TaskDeviceEnabled[task_index]) {
setNewTimerAt(getMixedId(TASK_DEVICE_TIMER, task_index), runAt);
}
+82 -28
View File
@@ -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("&#039;"));
html.replace("<", F("&lt;"));
html.replace(">", F("&gt;"));
html.replace("/", F("&#047;"));
}
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%
\*********************************************************************************************/
@@ -546,11 +598,13 @@ 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;
}
+17 -17
View File
@@ -61,8 +61,8 @@ void calcSunRiseAndSet() {
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;
float rise = 12 - da - eqt;
float set = 12 + da - eqt;
tsRise.Hour = (int)rise;
tsRise.Minute = (rise - (int)rise) * 60.0;
tsSet.Hour = (int)set;
@@ -70,6 +70,10 @@ void calcSunRiseAndSet() {
tsRise.Day = tsSet.Day = tm.Day;
tsRise.Month = tsSet.Month = tm.Month;
tsRise.Year = tsSet.Year = 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);
}
@@ -143,20 +147,6 @@ void breakTime(unsigned long timeInput, struct timeStruct &tm) {
tm.Day = 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;
}
@@ -218,13 +208,23 @@ unsigned long now() {
unsigned long t = getNtpTime();
if (t != 0) {
timeSynced = true;
setTime(t);
sysTime = (uint32_t)t;
applyTimeZone(t);
nextSyncTime = (uint32_t)t + syncInterval;
prevMillis = millis(); // restart counting from now (thanks to Korman for this fix)
}
}
uint32_t localSystime = toLocal(sysTime);
breakTime(localSystime, tm);
if (timeSynced) {
calcSunRiseAndSet();
if (Settings.UseRules)
{
static bool firstUpdate = true;
String event = firstUpdate ? F("Time#Initialized") : F("Time#Set");
firstUpdate = false;
rulesProcessing(event);
}
}
return (unsigned long)localSystime;
}
+273 -92
View File
@@ -875,6 +875,13 @@ void handle_root() {
TXBuffer += F(" - ");
TXBuffer += String(lowestRAMfunction);
TXBuffer += F(")");
html_TR_TD(); TXBuffer += F("Free Stack:<TD>");
TXBuffer += String(getCurrentFreeStack());
TXBuffer += F(" (");
TXBuffer += String(lowestFreeStack);
TXBuffer += F(" - ");
TXBuffer += String(lowestFreeStackfunction);
TXBuffer += F(")");
html_TR_TD(); TXBuffer += F("IP:<TD>");
TXBuffer += formatIP(ip);
@@ -883,7 +890,9 @@ void handle_root() {
if (wifiStatus == ESPEASY_WIFI_SERVICES_INITIALIZED)
{
TXBuffer += String(WiFi.RSSI());
TXBuffer += F(" dB");
TXBuffer += F(" dB (");
TXBuffer += WiFi.SSID();
TXBuffer += ')';
}
#ifdef FEATURE_MDNS
@@ -900,25 +909,31 @@ void handle_root() {
addButton(F("sysinfo"), F("More info"));
TXBuffer += F("</table><BR><BR><table class='multirow'><TR><TH>Node List:<TH>Name<TH>Build<TH>Type<TH>IP<TH>Age");
for (byte x = 0; x < UNIT_MAX; x++)
for (NodesMap::iterator it = Nodes.begin(); it != Nodes.end(); ++it)
{
if (Nodes[x].ip[0] != 0)
if (it->second.ip[0] != 0)
{
char url[80];
sprintf_P(url, PSTR("<a class='button link' href='http://%u.%u.%u.%u'>%u.%u.%u.%u</a>"), Nodes[x].ip[0], Nodes[x].ip[1], Nodes[x].ip[2], Nodes[x].ip[3], Nodes[x].ip[0], Nodes[x].ip[1], Nodes[x].ip[2], Nodes[x].ip[3]);
html_TR_TD(); TXBuffer += F("Unit ");
TXBuffer += String(x);
html_TD();
if (x != Settings.Unit)
TXBuffer += Nodes[x].nodeName;
sprintf_P(url, PSTR("<a class='button link' href='http://%u.%u.%u.%u'>%u.%u.%u.%u</a>"), it->second.ip[0], it->second.ip[1], it->second.ip[2], it->second.ip[3], it->second.ip[0], it->second.ip[1], it->second.ip[2], it->second.ip[3]);
bool isThisUnit = it->first == Settings.Unit;
if (isThisUnit)
html_TR_TD_highlight();
else
html_TR_TD();
TXBuffer += F("Unit ");
TXBuffer += String(it->first);
html_TD();
if (isThisUnit)
TXBuffer += Settings.Name;
else
TXBuffer += it->second.nodeName;
html_TD();
if (Nodes[x].build)
TXBuffer += String(Nodes[x].build);
if (it->second.build)
TXBuffer += String(it->second.build);
html_TD();
if (Nodes[x].nodeType)
switch (Nodes[x].nodeType)
if (it->second.nodeType)
switch (it->second.nodeType)
{
case NODE_TYPE_ID_ESP_EASY_STD:
TXBuffer += F("ESP Easy");
@@ -939,7 +954,7 @@ void handle_root() {
html_TD();
TXBuffer += url;
html_TD();
TXBuffer += String( Nodes[x].age);
TXBuffer += String( it->second.age);
}
}
@@ -1027,6 +1042,7 @@ void handle_config() {
MQTTclient_should_reconnect = true;
}
strncpy(Settings.Name, name.c_str(), sizeof(Settings.Name));
Settings.appendUnitToHostname(isFormItemChecked(F("appendunittohostname")));
//strncpy(SecuritySettings.Password, password.c_str(), sizeof(SecuritySettings.Password));
copyFormPassword(F("password"), SecuritySettings.Password, sizeof(SecuritySettings.Password));
strncpy(SecuritySettings.WifiSSID, ssid.c_str(), sizeof(SecuritySettings.WifiSSID));
@@ -1076,7 +1092,8 @@ void handle_config() {
Settings.Name[25] = 0;
SecuritySettings.Password[25] = 0;
addFormTextBox( F("Unit Name"), F("name"), Settings.Name, 25);
addFormNumericBox( F("Unit Number"), F("unit"), Settings.Unit, 0, 9999);
addFormNumericBox( F("Unit Number"), F("unit"), Settings.Unit, 0, UNIT_NUMBER_MAX);
addFormCheckBox(F("Append Unit Number to hostname"), F("appendunittohostname"), Settings.appendUnitToHostname());
addFormPasswordBox(F("Admin Password"), F("password"), SecuritySettings.Password, 25);
addFormSubHeader(F("Wifi Settings"));
@@ -1164,6 +1181,12 @@ void handle_controllers() {
String MQTTLwtTopic = WebServer.arg(F("mqttlwttopic"));
String lwtmessageconnect = WebServer.arg(F("lwtmessageconnect"));
String lwtmessagedisconnect = WebServer.arg(F("lwtmessagedisconnect"));
const int minimumsendinterval = getFormItemInt(F("minimumsendinterval"), 100);
const int maxqueuedepth = getFormItemInt(F("maxqueuedepth"), 10);
const int maxretry = getFormItemInt(F("maxretry"), 10);
String deleteoldest = WebServer.arg(F("deleteoldest"));
String mustcheckreply = WebServer.arg(F("mustcheckreply"));
const int clienttimeout = getFormItemInt(F("clienttimeout"), CONTROLLER_CLIENTTIMEOUT_DFLT);
//submitted data
@@ -1182,6 +1205,9 @@ void handle_controllers() {
//reset (some) default-settings
byte ProtocolIndex = getProtocolIndex(Settings.Protocol[controllerindex]);
ControllerSettings.Port = Protocol[ProtocolIndex].defaultPort;
ControllerSettings.MinimalTimeBetweenMessages = CONTROLLER_DELAY_QUEUE_DELAY_DFLT;
ControllerSettings.ClientTimeout = CONTROLLER_CLIENTTIMEOUT_DFLT;
// ControllerSettings.MaxQueueDepth = 0;
if (Protocol[ProtocolIndex].usesTemplate)
CPlugin_ptr[ProtocolIndex](CPLUGIN_PROTOCOL_TEMPLATE, &TempEvent, dummyString);
strncpy(ControllerSettings.Subscribe, TempEvent.String1.c_str(), sizeof(ControllerSettings.Subscribe));
@@ -1241,11 +1267,18 @@ void handle_controllers() {
strncpy(ControllerSettings.MQTTLwtTopic, MQTTLwtTopic.c_str(), sizeof(ControllerSettings.MQTTLwtTopic));
strncpy(ControllerSettings.LWTMessageConnect, lwtmessageconnect.c_str(), sizeof(ControllerSettings.LWTMessageConnect));
strncpy(ControllerSettings.LWTMessageDisconnect, lwtmessagedisconnect.c_str(), sizeof(ControllerSettings.LWTMessageDisconnect));
ControllerSettings.MinimalTimeBetweenMessages = minimumsendinterval;
ControllerSettings.MaxQueueDepth = maxqueuedepth;
ControllerSettings.MaxRetry = maxretry;
ControllerSettings.DeleteOldest = deleteoldest.toInt();
ControllerSettings.MustCheckReply = mustcheckreply.toInt();
ControllerSettings.ClientTimeout = clienttimeout;
CPlugin_ptr[ProtocolIndex](CPLUGIN_INIT, &TempEvent, dummyString);
}
}
addHtmlError(SaveControllerSettings(controllerindex, (byte*)&ControllerSettings, sizeof(ControllerSettings)));
addHtmlError(SaveControllerSettings(controllerindex, ControllerSettings));
addHtmlError(SaveSettings());
}
@@ -1259,7 +1292,7 @@ void handle_controllers() {
ControllerSettingsStruct ControllerSettings;
for (byte x = 0; x < CONTROLLER_MAX; x++)
{
LoadControllerSettings(x, (byte*)&ControllerSettings, sizeof(ControllerSettings));
LoadControllerSettings(x, ControllerSettings);
html_TR_TD();
TXBuffer += F("<a class='button link' href=\"controllers?index=");
TXBuffer += x + 1;
@@ -1315,18 +1348,28 @@ void handle_controllers() {
if (Settings.Protocol[controllerindex])
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(controllerindex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
LoadControllerSettings(controllerindex, ControllerSettings);
byte choice = ControllerSettings.UseDNS;
String options[2];
options[0] = F("Use IP address");
options[1] = F("Use Hostname");
byte choice_delete_oldest = ControllerSettings.DeleteOldest;
String options_delete_oldest[2];
options_delete_oldest[0] = F("Ignore New");
options_delete_oldest[1] = F("Delete Oldest");
byte choice_mustcheckreply = ControllerSettings.MustCheckReply;
String options_mustcheckreply[2];
options_mustcheckreply[0] = F("Ignore Acknowledgement");
options_mustcheckreply[1] = F("Check Acknowledgement");
byte ProtocolIndex = getProtocolIndex(Settings.Protocol[controllerindex]);
if (!Protocol[ProtocolIndex].Custom)
{
addFormSelector(F("Locate Controller"), F("usedns"), 2, options, NULL, NULL, choice, true);
if (ControllerSettings.UseDNS)
{
addFormTextBox( F("Controller Hostname"), F("controllerhostname"), ControllerSettings.HostName, sizeof(ControllerSettings.HostName)-1);
@@ -1337,15 +1380,24 @@ void handle_controllers() {
}
addFormNumericBox( F("Controller Port"), F("controllerport"), ControllerSettings.Port, 1, 65535);
addFormNumericBox( F("Minimum Send Interval"), F("minimumsendinterval"), ControllerSettings.MinimalTimeBetweenMessages, 1, CONTROLLER_DELAY_QUEUE_DELAY_MAX);
addUnit(F("ms"));
addFormNumericBox( F("Max Queue Depth"), F("maxqueuedepth"), ControllerSettings.MaxQueueDepth, 1, CONTROLLER_DELAY_QUEUE_DEPTH_MAX);
addFormNumericBox( F("Max Retries"), F("maxretry"), ControllerSettings.MaxRetry, 1, CONTROLLER_DELAY_QUEUE_RETRY_MAX);
addFormSelector(F("Full Queue Action"), F("deleteoldest"), 2, options_delete_oldest, NULL, NULL, choice_delete_oldest, true);
addFormSelector(F("Check Reply"), F("mustcheckreply"), 2, options_mustcheckreply, NULL, NULL, choice_mustcheckreply, true);
addFormNumericBox( F("Client Timeout"), F("clienttimeout"), ControllerSettings.ClientTimeout, 10, CONTROLLER_CLIENTTIMEOUT_MAX);
addUnit(F("ms"));
if (Protocol[ProtocolIndex].usesAccount)
{
String protoDisplayName;
String protoDisplayName;
if (!getControllerProtocolDisplayName(ProtocolIndex, CONTROLLER_USER, protoDisplayName)) {
protoDisplayName = F("Controller User");
}
addFormTextBox(protoDisplayName, F("controlleruser"), SecuritySettings.ControllerUser[controllerindex], sizeof(SecuritySettings.ControllerUser[0])-1);
}
}
if (Protocol[ProtocolIndex].usesPassword)
{
String protoDisplayName;
@@ -1428,6 +1480,11 @@ void handle_controllers() {
// Flash strings are not checked for duplication.
//********************************************************************************
void html_TR_TD_highlight() {
TXBuffer += F("<TR class=\"highlight\">");
html_TD();
}
void html_TR_TD() {
TXBuffer += F("<TR>");
html_TD();
@@ -1888,7 +1945,7 @@ void handle_devices() {
if (taskdevicenumber != 0) // set default values if a new device has been selected
{
//NOTE: do not enable task by default. allow user to enter sensible valus first and let him enable it when ready.
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0) // if field set empty, reload defaults
if (ExtraTaskSettings.TaskIndex != taskIndex) // if field set empty, reload defaults
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, &TempEvent, dummyString); //the plugin should populate ExtraTaskSettings with its default values.
} else {
SaveTaskSettings(taskIndex);
@@ -1953,7 +2010,7 @@ void handle_devices() {
// }
TempEvent.TaskIndex = taskIndex;
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0) // if field set empty, reload defaults
if (ExtraTaskSettings.TaskIndex != TempEvent.TaskIndex) // if field set empty, reload defaults
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, &TempEvent, dummyString);
//allow the plugin to save plugin-specific form settings.
@@ -2250,7 +2307,7 @@ void handle_devices() {
html_TR_TD(); TXBuffer += F("IDX:<TD>");
id = F("TDID"); //="taskdeviceid"
id += controllerNr + 1;
addNumericBox(id, Settings.TaskDeviceID[controllerNr][taskIndex], 0, 999999999); // Looks like it is an unsigned int, so could be up to 4 bln.
addNumericBox(id, Settings.TaskDeviceID[controllerNr][taskIndex], 0, DOMOTICZ_MAX_IDX);
}
}
}
@@ -2565,8 +2622,8 @@ void addPinSelect(boolean forI2C, String name, int choice)
//********************************************************************************
void addPinSelect(boolean forI2C, String name, int choice)
{
String options[PIN_D_MAX+1];
int optionValues[PIN_D_MAX+1];
String * options = new String[PIN_D_MAX+1];
int * optionValues = new int[PIN_D_MAX+1];
options[0] = F("- None -");
optionValues[0] = -1;
for(byte x=1; x < PIN_D_MAX+1; x++)
@@ -2576,6 +2633,8 @@ void addPinSelect(boolean forI2C, String name, int choice)
optionValues[x] = x;
}
renderHTMLForPinSelect(options, optionValues, forI2C, name, choice, PIN_D_MAX+1);
delete[] optionValues;
delete[] options;
}
#endif
@@ -3086,6 +3145,7 @@ void handle_log() {
// Web Interface JSON log page
//********************************************************************************
void handle_log_JSON() {
if (!isLoggedIn()) return;
TXBuffer.startJsonStream();
String webrequest = WebServer.arg(F("view"));
TXBuffer += F("{\"Log\": {");
@@ -3096,7 +3156,7 @@ void handle_log_JSON() {
TXBuffer += ',';
TXBuffer += '{';
int loglevel;
stream_next_json_object_value(F("label"), getLogLevelDisplayString(i, loglevel));
stream_next_json_object_value(F("label"), getLogLevelDisplayStringFromIndex(i, loglevel));
stream_last_json_object_value(F("loglevel"), String(loglevel));
}
TXBuffer += F("],\n");
@@ -3139,6 +3199,7 @@ void handle_log_JSON() {
stream_last_json_object_value(F("logTimeSpan"), String(logTimeSpan));
TXBuffer += F("}\n");
TXBuffer.endStream();
updateLogLevelCache();
}
//********************************************************************************
@@ -3615,6 +3676,7 @@ void handle_control() {
}
else if (command.equalsIgnoreCase(F("taskrun")) ||
command.equalsIgnoreCase(F("taskvalueset")) ||
command.equalsIgnoreCase(F("taskvaluetoggle")) ||
command.equalsIgnoreCase(F("rules"))) {
addLog(LOG_LEVEL_INFO,String(F("HTTP : ")) + webrequest);
ExecuteCommand(VALUE_SOURCE_HTTP,webrequest.c_str());
@@ -3756,14 +3818,14 @@ void handle_json()
}
if(showNodes) {
bool comma_between=false;
for (byte x = 0; x < UNIT_MAX; x++)
for (NodesMap::iterator it = Nodes.begin(); it != Nodes.end(); ++it)
{
if (Nodes[x].ip[0] != 0)
if (it->second.ip[0] != 0)
{
char ip[20];
sprintf_P(ip, PSTR("%u.%u.%u.%u"), Nodes[x].ip[0], Nodes[x].ip[1], Nodes[x].ip[2], Nodes[x].ip[3]);
sprintf_P(ip, PSTR("%u.%u.%u.%u"), it->second.ip[0], it->second.ip[1], it->second.ip[2], it->second.ip[3]);
if( comma_between ) {
TXBuffer += F(",");
@@ -3773,17 +3835,17 @@ void handle_json()
}
TXBuffer += F("{");
stream_next_json_object_value(F("nr"), String(x));
stream_next_json_object_value(F("nr"), String(it->first));
stream_next_json_object_value(F("name"),
(x != Settings.Unit) ? Nodes[x].nodeName : Settings.Name);
(it->first != Settings.Unit) ? it->second.nodeName : Settings.Name);
if (Nodes[x].build) {
stream_next_json_object_value(F("build"), String(Nodes[x].build));
if (it->second.build) {
stream_next_json_object_value(F("build"), String(it->second.build));
}
if (Nodes[x].nodeType) {
if (it->second.nodeType) {
String platform;
switch (Nodes[x].nodeType)
switch (it->second.nodeType)
{
case NODE_TYPE_ID_ESP_EASY_STD: platform = F("ESP Easy"); break;
case NODE_TYPE_ID_ESP_EASYM_STD: platform = F("ESP Easy Mega"); break;
@@ -3795,7 +3857,7 @@ void handle_json()
stream_next_json_object_value(F("platform"), platform);
}
stream_next_json_object_value(F("ip"), ip);
stream_last_json_object_value(F("age"), String( Nodes[x].age ));
stream_last_json_object_value(F("age"), String( it->second.age ));
} // if node info exists
} // for loop
if(comma_between) {
@@ -4081,10 +4143,10 @@ void addLogLevelSelect(String name, int choice)
{
String options[LOG_LEVEL_NRELEMENTS + 1];
int optionValues[LOG_LEVEL_NRELEMENTS + 1] = {0};
options[0] = F("None");
options[0] = getLogLevelDisplayString(0);
optionValues[0] = 0;
for (int i = 0; i < LOG_LEVEL_NRELEMENTS; ++i) {
options[i + 1] = getLogLevelDisplayString(i, optionValues[i + 1]);
options[i + 1] = getLogLevelDisplayStringFromIndex(i, optionValues[i + 1]);
}
addSelector(name, LOG_LEVEL_NRELEMENTS + 1, options, optionValues, NULL, choice, false);
}
@@ -4156,6 +4218,7 @@ void handle_download()
WebServer.sendHeader(F("Content-Disposition"), str);
WebServer.streamFile(dataFile, F("application/octet-stream"));
dataFile.close();
}
@@ -4381,16 +4444,19 @@ boolean handle_custom(String path) {
if(!unit) unit = btnunit; // unit element prevails, if not used then set to btnunit
if (unit && unit != Settings.Unit)
{
TXBuffer.startStream();
sendHeadandTail(F("TmplDsh"),_HEAD);
char url[40];
sprintf_P(url, PSTR("http://%u.%u.%u.%u/dashboard.esp"), Nodes[unit].ip[0], Nodes[unit].ip[1], Nodes[unit].ip[2], Nodes[unit].ip[3]);
TXBuffer += F("<meta http-equiv=\"refresh\" content=\"0; URL=");
TXBuffer += url;
TXBuffer += F("\">");
sendHeadandTail(F("TmplDsh"),_TAIL);
TXBuffer.endStream();
return true;
NodesMap::iterator it = Nodes.find(unit);
if (it != Nodes.end()) {
TXBuffer.startStream();
sendHeadandTail(F("TmplDsh"),_HEAD);
char url[40];
sprintf_P(url, PSTR("http://%u.%u.%u.%u/dashboard.esp"), it->second.ip[0], it->second.ip[1], it->second.ip[2], it->second.ip[3]);
TXBuffer += F("<meta http-equiv=\"refresh\" content=\"0; URL=");
TXBuffer += url;
TXBuffer += F("\">");
sendHeadandTail(F("TmplDsh"),_TAIL);
TXBuffer.endStream();
return true;
}
}
TXBuffer.startStream();
@@ -4404,16 +4470,16 @@ boolean handle_custom(String path) {
// create unit selector dropdown
addSelector_Head(F("unit"), true);
byte choice = Settings.Unit;
for (byte x = 0; x < UNIT_MAX; x++)
for (NodesMap::iterator it = Nodes.begin(); it != Nodes.end(); ++it)
{
if (Nodes[x].ip[0] != 0 || x == Settings.Unit)
if (it->second.ip[0] != 0 || it->first == Settings.Unit)
{
String name = String(x) + F(" - ");
if (x != Settings.Unit)
name += Nodes[x].nodeName;
String name = String(it->first) + F(" - ");
if (it->first != Settings.Unit)
name += it->second.nodeName;
else
name += Settings.Name;
addSelector_Item(name, x, choice == x, false, F(""));
addSelector_Item(name, it->first, choice == it->first, false, F(""));
}
}
addSelector_Foot();
@@ -4421,10 +4487,19 @@ boolean handle_custom(String path) {
// create <> navigation buttons
byte prev=Settings.Unit;
byte next=Settings.Unit;
for (byte x = Settings.Unit-1; x > 0; x--)
if (Nodes[x].ip[0] != 0) {prev = x; break;}
for (byte x = Settings.Unit+1; x < UNIT_MAX; x++)
if (Nodes[x].ip[0] != 0) {next = x; break;}
NodesMap::iterator it;
for (byte x = Settings.Unit-1; x > 0; x--) {
it = Nodes.find(x);
if (it != Nodes.end()) {
if (it->second.ip[0] != 0) {prev = x; break;}
}
}
for (byte x = Settings.Unit+1; x < UNIT_MAX; x++) {
it = Nodes.find(x);
if (it != Nodes.end()) {
if (it->second.ip[0] != 0) {next = x; break;}
}
}
TXBuffer += F("<a class='button link' href=");
TXBuffer += path;
@@ -4523,18 +4598,40 @@ void handle_filelist() {
checkRuleSets();
}
const int pageSize = 25;
int startIdx = 0;
String fstart = WebServer.arg(F("start"));
if (fstart.length() > 0)
{
startIdx = atoi(fstart.c_str());
}
int endIdx = startIdx + pageSize - 1;
TXBuffer += F("<table class='multirow' border=1px frame='box' rules='all'><TH style='width:50px;'><TH>Filename<TH style='width:80px;'>Size");
fs::Dir dir = SPIFFS.openDir("");
int count = -1;
while (dir.next())
{
++count;
if (count < startIdx)
{
continue;
}
html_TR_TD();
if (dir.fileName() != F(FILE_CONFIG) && dir.fileName() != F(FILE_SECURITY) && dir.fileName() != F(FILE_NOTIFICATION))
{
TXBuffer += F("<a class='button link' href=\"filelist?delete=");
TXBuffer += dir.fileName();
if (startIdx > 0)
{
TXBuffer += F("&start=");
TXBuffer += startIdx;
}
TXBuffer += F("\">Del</a>");
}
@@ -4545,12 +4642,32 @@ void handle_filelist() {
TXBuffer += F("</a>");
fs::File f = dir.openFile("r");
html_TD();
TXBuffer += f.size();
if (f) {
TXBuffer += f.size();
f.close();
}
if (count >= endIdx)
{
break;
}
}
TXBuffer += F("</table></form>");
TXBuffer += F("<BR><a class='button link' href=\"/upload\">Upload</a><BR><BR>");
sendHeadandTail(F("TmplStd"),true);
TXBuffer.endStream();
TXBuffer += F("<BR><a class='button link' href=\"/upload\">Upload</a>");
if (startIdx > 0)
{
TXBuffer += F("<a class='button link' href=\"/filelist?start=");
TXBuffer += max(0, startIdx - pageSize);
TXBuffer += F("\">Previous</a>");
}
if (count >= endIdx and dir.next())
{
TXBuffer += F("<a class='button link' href=\"/filelist?start=");
TXBuffer += endIdx + 1;
TXBuffer += F("\">Next</a>");
}
TXBuffer += F("<BR><BR>");
sendHeadandTail(F("TmplStd"),true);
TXBuffer.endStream();
#endif
#if defined(ESP32)
String fdelete = WebServer.arg(F("delete"));
@@ -4561,35 +4678,67 @@ void handle_filelist() {
// flashCount();
}
const int pageSize = 25;
int startIdx = 0;
String fstart = WebServer.arg(F("start"));
if (fstart.length() > 0)
{
startIdx = atoi(fstart.c_str());
}
int endIdx = startIdx + pageSize - 1;
TXBuffer += F("<table class='multirow' border=1px frame='box' rules='all'><TH><TH>Filename<TH>Size");
File root = SPIFFS.open("/");
File file = root.openNextFile();
while (file)
int count = -1;
while (file and count < endIdx)
{
if(!file.isDirectory()){
html_TR_TD();
if (strcmp(file.name(), FILE_CONFIG) != 0 && strcmp(file.name(), FILE_SECURITY) != 0 && strcmp(file.name(), FILE_NOTIFICATION) != 0)
{
TXBuffer += F("<a class='button link' href=\"filelist?delete=");
TXBuffer += file.name();
TXBuffer += F("\">Del</a>");
}
++count;
TXBuffer += F("<TD><a href=\"");
TXBuffer += file.name();
TXBuffer += F("\">");
TXBuffer += file.name();
TXBuffer += F("</a>");
html_TD();
TXBuffer += file.size();
file = root.openNextFile();
if (count >= startIdx)
{
html_TR_TD();
if (strcmp(file.name(), FILE_CONFIG) != 0 && strcmp(file.name(), FILE_SECURITY) != 0 && strcmp(file.name(), FILE_NOTIFICATION) != 0)
{
TXBuffer += F("<a class='button link' href=\"filelist?delete=");
TXBuffer += file.name();
if (startIdx > 0)
{
TXBuffer += F("&start=");
TXBuffer += startIdx;
}
TXBuffer += F("\">Del</a>");
}
TXBuffer += F("<TD><a href=\"");
TXBuffer += file.name();
TXBuffer += F("\">");
TXBuffer += file.name();
TXBuffer += F("</a>");
html_TD();
TXBuffer += file.size();
}
}
file = root.openNextFile();
}
TXBuffer += F("</table></form>");
TXBuffer += F("<BR><a class='button link' href=\"/upload\">Upload</a><BR><BR>");
TXBuffer += F("<BR><a class='button link' href=\"/upload\">Upload</a>");
if (startIdx > 0)
{
TXBuffer += F("<a class='button link' href=\"/filelist?start=");
TXBuffer += startIdx < pageSize ? 0 : startIdx - pageSize;
TXBuffer += F("\">Previous</a>");
}
if (count >= endIdx and file)
{
TXBuffer += F("<a class='button link' href=\"/filelist?start=");
TXBuffer += endIdx + 1;
TXBuffer += F("\">Next</a>");
}
TXBuffer += F("<BR><BR>");
sendHeadandTail(F("TmplStd"),true);
TXBuffer.endStream();
#endif
@@ -4854,7 +5003,11 @@ void handle_setup() {
{
html_TR_TD(); TXBuffer += F("<label class='container2'>");
TXBuffer += F("<input type='radio' name='ssid' value='");
TXBuffer += WiFi.SSID(i);
{
String escapeBuffer = WiFi.SSID(i);
htmlStrongEscape(escapeBuffer);
TXBuffer += escapeBuffer;
}
TXBuffer += F("'");
if (WiFi.SSID(i) == ssid)
TXBuffer += F(" checked ");
@@ -4993,7 +5146,7 @@ void handle_rules() {
log += F(" Create new file: ");
log += fileName;
fs::File f = SPIFFS.open(fileName, "w");
f.close();
if (f) f.close();
}
}
addLog(LOG_LEVEL_INFO, log);
@@ -5129,6 +5282,13 @@ void handle_sysinfo() {
TXBuffer += F(" - ");
TXBuffer += lowestRAMfunction;
TXBuffer += F(")");
html_TR_TD(); TXBuffer += F("Free Stack<TD>");
TXBuffer += getCurrentFreeStack();
TXBuffer += F(" (");
TXBuffer += lowestFreeStack;
TXBuffer += F(" - ");
TXBuffer += lowestFreeStackfunction;
TXBuffer += F(")");
html_TR_TD(); TXBuffer += F("Boot<TD>");
TXBuffer += getLastBootCauseString();
@@ -5190,16 +5350,6 @@ void handle_sysinfo() {
html_TR_TD(); TXBuffer += F("Allowed IP Range<TD>");
TXBuffer += describeAllowedIPrange();
html_TR_TD(); TXBuffer += F("Serial Port available:<TD>");
TXBuffer += String(SerialAvailableForWrite());
TXBuffer += F(" (");
#if defined(ESP8266)
TXBuffer += Serial.availableForWrite();
#endif
TXBuffer += F(" , ");
TXBuffer += Serial.available();
TXBuffer += F(")");
html_TR_TD(); TXBuffer += F("STA MAC<TD>");
uint8_t mac[] = {0, 0, 0, 0, 0, 0};
@@ -5264,6 +5414,23 @@ void handle_sysinfo() {
TXBuffer += F("<TR><TD id='copyText_11'>Binary filename<TD id='copyText_12'>");
TXBuffer += String(CRCValues.binaryFilename);
addTableSeparator(F("System Status"), 2, 3);
{
// Actual Loglevel
html_TR_TD(); TXBuffer += F("Syslog Log Level:<TD>");
TXBuffer += getLogLevelDisplayString(Settings.SyslogLevel);
html_TR_TD(); TXBuffer += F("Serial Log Level:<TD>");
TXBuffer += getLogLevelDisplayString(getSerialLogLevel());
html_TR_TD(); TXBuffer += F("Web Log Level:<TD>");
TXBuffer += getLogLevelDisplayString(getWebLogLevel());
#ifdef FEATURE_SD
html_TR_TD(); TXBuffer += F("SD Log Level:<TD>");
TXBuffer += getLogLevelDisplayString(Settings.SDLogLevel);
#endif
}
addTableSeparator(F("ESP board"), 2, 3);
html_TR_TD(); TXBuffer += F("ESP Chip ID<TD>");
@@ -5296,6 +5463,8 @@ void handle_sysinfo() {
TXBuffer += ESP.getCpuFreqMHz();
TXBuffer += F(" MHz");
#endif
html_TR_TD(); TXBuffer += F("ESP Board Name:<TD>");
TXBuffer += ARDUINO_BOARD;
addTableSeparator(F("Storage"), 2, 3);
@@ -5354,6 +5523,18 @@ void handle_sysinfo() {
TXBuffer += F(" kB free)");
#endif
html_TR_TD(); TXBuffer += F("SPIFFS Size<TD>");
{
#if defined(ESP8266)
fs::FSInfo fs_info;
SPIFFS.info(fs_info);
TXBuffer += fs_info.totalBytes / 1024;
TXBuffer += F(" kB (");
TXBuffer += (fs_info.totalBytes - fs_info.usedBytes) / 1024;
TXBuffer += F(" kB free)");
#endif
}
if (showSettingsFileLayout) {
addTableSeparator(F("Settings Files"), 2, 3);
html_TR_TD();
+2
View File
@@ -236,6 +236,8 @@ static const char pgDefaultCSS[] PROGMEM = {
"table.multirow tr {padding: 4px; }"
"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; }"
// inside a form
".note {color: #444; font-style: italic; }"
//header with title and menu
+24 -70
View File
@@ -31,49 +31,20 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
break;
}
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C001_DelayHandler.configureControllerSettings(ControllerSettings);
break;
}
case CPLUGIN_PROTOCOL_SEND:
{
if (event->idx != 0)
{
if (!WiFiConnected(100)) {
success = false;
break;
}
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof (ControllerSettings));
String authHeader = "";
if ((SecuritySettings.ControllerUser[event->ControllerIndex][0] != 0) && (SecuritySettings.ControllerPassword[event->ControllerIndex][0] != 0))
{
base64 encoder;
String auth = SecuritySettings.ControllerUser[event->ControllerIndex];
auth += ":";
auth += SecuritySettings.ControllerPassword[event->ControllerIndex];
authHeader = F("Authorization: Basic ");
authHeader += encoder.encode(auth);
authHeader += F(" \r\n");
}
// boolean success = false;
addLog(LOG_LEVEL_DEBUG, String(F("HTTP : connecting to "))+ControllerSettings.getHostPortString());
// Use WiFiClient class to create TCP connections
WiFiClient client;
if (!ControllerSettings.connectToHost(client))
{
connectionFailures++;
addLog(LOG_LEVEL_ERROR, F("HTTP : connection failed"));
return false;
}
statusLED(true);
if (connectionFailures)
connectionFailures--;
// We now create a URI for the request
String url = F("/json.htm?type=command&param=udevice&idx=");
url += event->idx;
String url;
switch (event->sensorType)
{
@@ -125,38 +96,8 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
url += mapVccToDomoticz();
#endif
// This will send the request to the server
String request = F("GET ");
request += url;
request += F(" HTTP/1.1\r\n");
request += F("Host: ");
request += ControllerSettings.getHost();
request += F("\r\n");
request += authHeader;
request += F("Connection: close\r\n\r\n");
client.print(request);
unsigned long timer = millis() + 200;
while (!client.available() && !timeOutReached(timer))
yield();
// Read all the lines of the reply from server and log them
while (client.available()) {
// String line = client.readStringUntil('\n');
String line;
safeReadStringUntil(client, line, '\n');
addLog(LOG_LEVEL_DEBUG_MORE, line);
if (line.startsWith(F("HTTP/1.1 200 OK")) )
{
addLog(LOG_LEVEL_DEBUG, F("HTTP : Success"));
success = true;
}
yield();
}
addLog(LOG_LEVEL_DEBUG, F("HTTP : closing connection"));
client.flush();
client.stop();
success = C001_DelayHandler.addToQueue(C001_queue_element(event->ControllerIndex, url));
scheduleNextDelayQueue(TIMER_C001_DELAY_QUEUE, C001_DelayHandler.getNextScheduleTime());
} // if ixd !=0
else
{
@@ -167,4 +108,17 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
}
return success;
}
bool do_process_c001_delay_queue(int controller_number, const C001_queue_element& element, ControllerSettingsStruct& ControllerSettings) {
WiFiClient client;
if (!try_connect_host(controller_number, client, ControllerSettings))
return false;
// This will send the request to the server
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, ControllerSettings.MustCheckReply);
}
#endif
+11 -1
View File
@@ -33,6 +33,14 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
break;
}
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
MQTTDelayHandler.configureControllerSettings(ControllerSettings);
break;
}
case CPLUGIN_PROTOCOL_TEMPLATE:
{
event->String1 = F("domoticz/out");
@@ -138,11 +146,13 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
if (event->idx != 0)
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
/*
if (!ControllerSettings.checkHostReachable(true)) {
success = false;
break;
}
*/
StaticJsonBuffer<200> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
+72 -62
View File
@@ -30,77 +30,24 @@ boolean CPlugin_003(byte function, struct EventStruct *event, String& string)
break;
}
case CPLUGIN_PROTOCOL_SEND:
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
boolean success = false;
char log[80];
addLog(LOG_LEVEL_DEBUG, String(F("TELNT : connecting to ")) + ControllerSettings.getHostPortString());
// Use WiFiClient class to create TCP connections
WiFiClient client;
if (!ControllerSettings.connectToHost(client))
{
connectionFailures++;
strcpy_P(log, PSTR("TELNT: connection failed"));
addLog(LOG_LEVEL_ERROR, log);
return false;
}
statusLED(true);
if (connectionFailures)
connectionFailures--;
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C003_DelayHandler.configureControllerSettings(ControllerSettings);
break;
}
case CPLUGIN_PROTOCOL_SEND:
{
// We now create a URI for the request
String url = F("variableset ");
url += event->idx;
url += ",";
url += formatUserVarNoCheck(event, 0);
url += "\n";
// strcpy_P(log, PSTR("TELNT: Sending enter"));
// addLog(LOG_LEVEL_ERROR, log);
client.print(" \n");
unsigned long timer = millis() + 200;
while (!client.available() && !timeOutReached(timer))
delay(1);
timer = millis() + 1000;
while (client.available() && !timeOutReached(timer) && !success)
{
// String line = client.readStringUntil('\n');
String line;
safeReadStringUntil(client, line, '\n');
if (line.startsWith(F("Enter your password:")))
{
success = true;
strcpy_P(log, PSTR("TELNT: Password request ok"));
addLog(LOG_LEVEL_DEBUG, log);
}
delay(1);
}
strcpy_P(log, PSTR("TELNT: Sending pw"));
addLog(LOG_LEVEL_DEBUG, log);
client.println(SecuritySettings.ControllerPassword[event->ControllerIndex]);
delay(100);
while (client.available())
client.read();
strcpy_P(log, PSTR("TELNT: Sending cmd"));
addLog(LOG_LEVEL_DEBUG, log);
client.print(url);
delay(10);
while (client.available())
client.read();
strcpy_P(log, PSTR("TELNT: closing connection"));
addLog(LOG_LEVEL_DEBUG, log);
client.stop();
success = C003_DelayHandler.addToQueue(C003_queue_element(event->ControllerIndex, url));
scheduleNextDelayQueue(TIMER_C003_DELAY_QUEUE, C003_DelayHandler.getNextScheduleTime());
break;
}
@@ -108,4 +55,67 @@ boolean CPlugin_003(byte function, struct EventStruct *event, String& string)
}
return success;
}
bool do_process_c003_delay_queue(int controller_number, const C003_queue_element& element, ControllerSettingsStruct& ControllerSettings) {
boolean success = false;
char log[80];
addLog(LOG_LEVEL_DEBUG, String(F("TELNT : connecting to ")) + ControllerSettings.getHostPortString());
// Use WiFiClient class to create TCP connections
WiFiClient client;
if (!ControllerSettings.connectToHost(client))
{
connectionFailures++;
strcpy_P(log, PSTR("TELNT: connection failed"));
addLog(LOG_LEVEL_ERROR, log);
return success;
}
statusLED(true);
if (connectionFailures)
connectionFailures--;
// strcpy_P(log, PSTR("TELNT: Sending enter"));
// addLog(LOG_LEVEL_ERROR, log);
client.print(" \n");
unsigned long timer = millis() + 200;
while (!client.available() && !timeOutReached(timer))
delay(1);
timer = millis() + 1000;
while (client_available(client) && !timeOutReached(timer) && !success)
{
// String line = client.readStringUntil('\n');
String line;
safeReadStringUntil(client, line, '\n');
if (line.startsWith(F("Enter your password:")))
{
success = true;
strcpy_P(log, PSTR("TELNT: Password request ok"));
addLog(LOG_LEVEL_DEBUG, log);
}
delay(1);
}
strcpy_P(log, PSTR("TELNT: Sending pw"));
addLog(LOG_LEVEL_DEBUG, log);
client.println(SecuritySettings.ControllerPassword[element.controller_idx]);
delay(100);
while (client.available())
client.read();
strcpy_P(log, PSTR("TELNT: Sending cmd"));
addLog(LOG_LEVEL_DEBUG, log);
client.print(element.txt);
delay(10);
while (client_available(client))
client.read();
strcpy_P(log, PSTR("TELNT: closing connection"));
addLog(LOG_LEVEL_DEBUG, log);
client.stop();
return success;
}
#endif
+41 -81
View File
@@ -30,6 +30,14 @@ boolean CPlugin_004(byte function, struct EventStruct *event, String& string)
break;
}
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C004_DelayHandler.configureControllerSettings(ControllerSettings);
break;
}
case CPLUGIN_GET_PROTOCOL_DISPLAY_NAME:
{
success = true;
@@ -48,92 +56,44 @@ boolean CPlugin_004(byte function, struct EventStruct *event, String& string)
case CPLUGIN_PROTOCOL_SEND:
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
success = C004_DelayHandler.addToQueue(C004_queue_element(event));
scheduleNextDelayQueue(TIMER_C004_DELAY_QUEUE, C004_DelayHandler.getNextScheduleTime());
// boolean success = false;
addLog(LOG_LEVEL_DEBUG, String(F("HTTP : connecting to "))+ControllerSettings.getHostPortString());
char log[80];
// Use WiFiClient class to create TCP connections
WiFiClient client;
if (!ControllerSettings.connectToHost(client))
{
connectionFailures++;
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
strcpy_P(log, PSTR("HTTP : connection failed"));
addLog(LOG_LEVEL_ERROR, log);
}
return false;
}
statusLED(true);
if (connectionFailures)
connectionFailures--;
String postDataStr = F("api_key=");
postDataStr += SecuritySettings.ControllerPassword[event->ControllerIndex]; // used for API key
byte valueCount = getValueCountFromSensorType(event->sensorType);
for (byte x = 0; x < valueCount; x++)
{
postDataStr += F("&field");
postDataStr += event->idx + x;
postDataStr += "=";
postDataStr += formatUserVarNoCheck(event, x);
}
String hostName = F("api.thingspeak.com"); // PM_CZ: HTTP requests must contain host headers.
if (ControllerSettings.UseDNS)
hostName = ControllerSettings.HostName;
String postStr = F("POST /update HTTP/1.1\r\n");
postStr += F("Host: ");
postStr += hostName;
postStr += F("\r\n");
postStr += F("Connection: close\r\n");
postStr += F("Content-Type: application/x-www-form-urlencoded\r\n");
postStr += F("Content-Length: ");
postStr += postDataStr.length();
postStr += F("\r\n\r\n");
postStr += postDataStr;
// 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()) {
// String line = client.readStringUntil('\n');
String line;
safeReadStringUntil(client, line, '\n');
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
line.toCharArray(log, 80);
addLog(LOG_LEVEL_DEBUG_MORE, log);
}
if (line.substring(0, 15) == F("HTTP/1.1 200 OK"))
{
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
strcpy_P(log, PSTR("HTTP : Success!"));
addLog(LOG_LEVEL_DEBUG, log);
}
success = true;
}
delay(1);
}
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
strcpy_P(log, PSTR("HTTP : closing connection"));
addLog(LOG_LEVEL_DEBUG, log);
}
client.flush();
client.stop();
break;
}
}
return success;
}
bool do_process_c004_delay_queue(int controller_number, const C004_queue_element& element, ControllerSettingsStruct& ControllerSettings) {
WiFiClient client;
if (!try_connect_host(controller_number, client, ControllerSettings))
return false;
String postDataStr = F("api_key=");
postDataStr += SecuritySettings.ControllerPassword[element.controller_idx]; // used for API key
byte valueCount = getValueCountFromSensorType(element.sensorType);
for (byte x = 0; x < valueCount; x++)
{
postDataStr += F("&field");
postDataStr += element.idx + x;
postDataStr += "=";
postDataStr += formatUserVarNoCheck(element.TaskIndex, x);
}
String hostName = F("api.thingspeak.com"); // PM_CZ: HTTP requests must contain host headers.
if (ControllerSettings.UseDNS)
hostName = ControllerSettings.HostName;
String postStr = do_create_http_request(
hostName, F("POST"),
F("/update"), // uri
"", // auth_header
F("Content-Type: application/x-www-form-urlencoded\r\n"),
postDataStr.length());
postStr += postDataStr;
return send_via_http(controller_number, client, postStr, ControllerSettings.MustCheckReply);
}
#endif
+10 -2
View File
@@ -31,6 +31,14 @@ boolean CPlugin_005(byte function, struct EventStruct *event, String& string)
break;
}
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
MQTTDelayHandler.configureControllerSettings(ControllerSettings);
break;
}
case CPLUGIN_PROTOCOL_TEMPLATE:
{
event->String1 = F("/%sysname%/#");
@@ -85,14 +93,14 @@ boolean CPlugin_005(byte function, struct EventStruct *event, String& string)
case CPLUGIN_PROTOCOL_SEND:
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(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;
+10 -2
View File
@@ -31,6 +31,14 @@ boolean CPlugin_006(byte function, struct EventStruct *event, String& string)
break;
}
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
MQTTDelayHandler.configureControllerSettings(ControllerSettings);
break;
}
case CPLUGIN_PROTOCOL_TEMPLATE:
{
event->String1 = F("/Home/#");
@@ -83,11 +91,11 @@ boolean CPlugin_006(byte function, struct EventStruct *event, String& string)
break;
}
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(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;
+38 -82
View File
@@ -30,99 +30,55 @@ boolean CPlugin_007(byte function, struct EventStruct *event, String& string)
break;
}
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C007_DelayHandler.configureControllerSettings(ControllerSettings);
break;
}
case CPLUGIN_PROTOCOL_SEND:
{
if (!WiFiConnected(100)) {
success = false;
break;
}
const byte valueCount = getValueCountFromSensorType(event->sensorType);
if (valueCount == 0 || valueCount > 3) {
addLog(LOG_LEVEL_ERROR, F("emoncms : Unknown sensortype or too many sensor values"));
break;
}
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
// boolean success = false;
addLog(LOG_LEVEL_DEBUG, String(F("HTTP : connecting to "))+ControllerSettings.getHostPortString());
char log[80];
// Use WiFiClient class to create TCP connections
WiFiClient client;
if (!ControllerSettings.connectToHost(client))
{
connectionFailures++;
strcpy_P(log, PSTR("HTTP : connection failed"));
addLog(LOG_LEVEL_ERROR, log);
return false;
}
statusLED(true);
if (connectionFailures)
connectionFailures--;
String postDataStr = F("GET /emoncms/input/post.json?node=");
postDataStr += Settings.Unit;
postDataStr += F("&json=");
for (byte i = 0; i < valueCount; ++i) {
postDataStr += (i == 0) ? F("{") : F(",");
postDataStr += F("field");
postDataStr += event->idx + i;
postDataStr += ":";
postDataStr += formatUserVarNoCheck(event, i);
}
postDataStr += "}";
postDataStr += F("&apikey=");
postDataStr += SecuritySettings.ControllerPassword[event->ControllerIndex]; // "0UDNN17RW6XAS2E5" // api key
String postStr = F(" HTTP/1.1\r\n");
postStr += F("Host: ");
postStr += ControllerSettings.getHost();
postStr += F("\r\n");
postStr += F("Connection: close\r\n");
postStr += F("\r\n");
postDataStr += postStr;
if (Settings.SerialLogLevel >= LOG_LEVEL_DEBUG_MORE)
Serial.println(postDataStr);
// This will send the request to the server
client.print(postDataStr);
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()) {
// String line = client.readStringUntil('\n');
String line;
safeReadStringUntil(client, line, '\n');
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
line.toCharArray(log, 80);
addLog(LOG_LEVEL_DEBUG_MORE, log);
}
if (line.substring(0, 15) == F("HTTP/1.1 200 OK"))
{
strcpy_P(log, PSTR("HTTP : Success!"));
addLog(LOG_LEVEL_DEBUG, log);
success = true;
}
delay(1);
}
strcpy_P(log, PSTR("HTTP : closing connection"));
addLog(LOG_LEVEL_DEBUG, log);
client.flush();
client.stop();
success = C007_DelayHandler.addToQueue(C007_queue_element(event));
scheduleNextDelayQueue(TIMER_C007_DELAY_QUEUE, C007_DelayHandler.getNextScheduleTime());
break;
}
}
return success;
}
bool do_process_c007_delay_queue(int controller_number, const C007_queue_element& element, ControllerSettingsStruct& ControllerSettings) {
WiFiClient client;
if (!try_connect_host(controller_number, client, ControllerSettings))
return false;
String url = F("/emoncms/input/post.json?node=");
url += Settings.Unit;
url += F("&json=");
const byte valueCount = getValueCountFromSensorType(element.sensorType);
for (byte i = 0; i < valueCount; ++i) {
url += (i == 0) ? F("{") : F(",");
url += F("field");
url += element.idx + i;
url += ":";
url += formatUserVarNoCheck(element.TaskIndex, i);
}
url += "}";
url += F("&apikey=");
url += SecuritySettings.ControllerPassword[element.controller_idx]; // "0UDNN17RW6XAS2E5" // api key
if (Settings.SerialLogLevel >= LOG_LEVEL_DEBUG_MORE)
Serial.println(url);
return send_via_http(controller_number, client,
create_http_get_request(controller_number, ControllerSettings, url),
ControllerSettings.MustCheckReply);
}
#endif
+36 -85
View File
@@ -32,6 +32,14 @@ boolean CPlugin_008(byte function, struct EventStruct *event, String& string)
break;
}
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C008_DelayHandler.configureControllerSettings(ControllerSettings);
break;
}
case CPLUGIN_PROTOCOL_TEMPLATE:
{
event->String1 = "";
@@ -41,21 +49,31 @@ boolean CPlugin_008(byte function, struct EventStruct *event, String& string)
case CPLUGIN_PROTOCOL_SEND:
{
// 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.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
for (byte x = 0; x < valueCount; x++)
{
bool isvalid;
String formattedValue = formatUserVar(event, x, isvalid);
if (isvalid)
HTTPSend(event, x, formattedValue);
if (valueCount > 1)
{
delayBackground(Settings.MessageDelay);
// unsigned long timer = millis() + Settings.MessageDelay;
// while (!timeOutReached(timer))
// backgroundtasks();
if (isvalid) {
element.txt[x] = "/";
element.txt[x] += ControllerSettings.Publish;
parseControllerVariables(element.txt[x], event, true);
element.txt[x].replace(F("%valname%"), URLEncode(ExtraTaskSettings.TaskDeviceValueNames[x]));
element.txt[x].replace(F("%value%"), formattedValue);
addLog(LOG_LEVEL_DEBUG_MORE, element.txt[x]);
}
}
success = C008_DelayHandler.addToQueue(element);
scheduleNextDelayQueue(TIMER_C008_DELAY_QUEUE, C008_DelayHandler.getNextScheduleTime());
break;
}
@@ -63,90 +81,23 @@ boolean CPlugin_008(byte function, struct EventStruct *event, String& string)
return success;
}
//********************************************************************************
// Generic HTTP get request
//********************************************************************************
boolean HTTPSend(struct EventStruct *event, byte varIndex, const String& formattedValue)
{
if (!WiFiConnected(100)) {
return false;
}
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
String authHeader = "";
if ((SecuritySettings.ControllerUser[event->ControllerIndex][0] != 0) && (SecuritySettings.ControllerPassword[event->ControllerIndex][0] != 0))
{
base64 encoder;
String auth = SecuritySettings.ControllerUser[event->ControllerIndex];
auth += ":";
auth += SecuritySettings.ControllerPassword[event->ControllerIndex];
authHeader = F("Authorization: Basic ");
authHeader += encoder.encode(auth) + " \r\n";
bool do_process_c008_delay_queue(int controller_number, const C008_queue_element& element, ControllerSettingsStruct& ControllerSettings) {
while (element.txt[element.valuesSent] == "") {
// A non valid value, which we are not going to send.
// Increase sent counter until a valid value is found.
if (element.checkDone(true))
return true;
}
// boolean success = false;
addLog(LOG_LEVEL_DEBUG, String(F("HTTP : connecting to "))+ControllerSettings.getHostPortString());
// Use WiFiClient class to create TCP connections
WiFiClient client;
if (!ControllerSettings.connectToHost(client))
{
connectionFailures++;
addLog(LOG_LEVEL_ERROR, F("HTTP : connection failed"));
if (!try_connect_host(controller_number, client, ControllerSettings))
return false;
}
statusLED(true);
if (connectionFailures)
connectionFailures--;
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
String url = "/";
url += ControllerSettings.Publish;
parseControllerVariables(url, event, true);
url.replace(F("%valname%"), URLEncode(ExtraTaskSettings.TaskDeviceValueNames[varIndex]));
url.replace(F("%value%"), formattedValue);
// url.toCharArray(log, 80);
addLog(LOG_LEVEL_DEBUG_MORE, url);
// This will send the request to the server
client.print(String(F("GET ")) + url + F(" HTTP/1.1\r\n") +
F("Host: ") + ControllerSettings.getHost() + F("\r\n") + authHeader +
F("Connection: close\r\n\r\n"));
unsigned long timer = millis() + 200;
while (!client.available() && !timeOutReached(timer))
yield();
// Read all the lines of the reply from server and print them to Serial
while (client.available()) {
// String line = client.readStringUntil('\n');
String line;
safeReadStringUntil(client, line, '\n');
// line.toCharArray(log, 80);
addLog(LOG_LEVEL_DEBUG_MORE, line);
if (line.startsWith(F("HTTP/1.1 200 OK")))
{
// strcpy_P(log, PSTR("HTTP : Success!"));
// addLog(LOG_LEVEL_DEBUG, log);
addLog(LOG_LEVEL_DEBUG, F("HTTP : Success!"));
// success = true;
}
delay(1);
}
// strcpy_P(log, PSTR("HTTP : closing connection"));
// addLog(LOG_LEVEL_DEBUG, log);
addLog(LOG_LEVEL_DEBUG, F("HTTP : closing connection"));
client.flush();
client.stop();
return(true);
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, ControllerSettings.MustCheckReply));
}
#endif
+64 -117
View File
@@ -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,139 +57,86 @@ 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)
byte valueCount = getValueCountFromSensorType(event->sensorType);
C009_queue_element element(event);
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
// We now create a URI for the request
String url = F("/ESPEasy");
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
// 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];
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);
element.valueNames[x] = ExtraTaskSettings.TaskDeviceValueNames[x];
}
// Create json buffer
// char buffer[root.measureLength() +1];
// root.printTo(buffer, sizeof(buffer));
String jsonString;
root.printTo(jsonString);
// Push data to server
FHEMHTTPsend(url, jsonString, event->ControllerIndex);
success = C009_DelayHandler.addToQueue(element);
scheduleNextDelayQueue(TIMER_C009_DELAY_QUEUE, C009_DelayHandler.getNextScheduleTime());
break;
}
}
return success;
}
//********************************************************************************
// FHEM HTTP request
//********************************************************************************
//TODO: create a generic HTTPSend function that we use in all the controllers. lots of code duplication here
void FHEMHTTPsend(String & url, String & buffer, byte index)
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(index, (byte*)&ControllerSettings, sizeof(ControllerSettings));
// boolean success = false;
String authHeader = "";
if ((SecuritySettings.ControllerUser[index][0] != 0) && (SecuritySettings.ControllerPassword[index][0] != 0)) {
base64 encoder;
String auth = SecuritySettings.ControllerUser[index];
auth += ":";
auth += SecuritySettings.ControllerPassword[index];
authHeader = String(F("Authorization: Basic ")) + encoder.encode(auth) + " \r\n";
}
addLog(LOG_LEVEL_DEBUG, String(F("HTTP : connecting to "))+ControllerSettings.getHostPortString());
// Use WiFiClient class to create TCP connections
/*********************************************************************************************\
* FHEM HTTP request
\*********************************************************************************************/
bool do_process_c009_delay_queue(int controller_number, const C009_queue_element& element, ControllerSettingsStruct& ControllerSettings) {
WiFiClient client;
if (!ControllerSettings.connectToHost(client)) {
connectionFailures++;
// strcpy_P(log, PSTR("HTTP : connection failed"));
addLog(LOG_LEVEL_ERROR, F("HTTP : connection failed"));
return;
if (!try_connect_host(controller_number, client, ControllerSettings))
return false;
// 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")] = element.valueNames[x];
val[F("type")] = element.sensorType;
val[F("value")] = element.txt[x];
}
statusLED(true);
if (connectionFailures)
connectionFailures--;
// Create json buffer
String jsonString;
root.printTo(jsonString);
// This will send the request to the server
int len = buffer.length();
client.print(String("POST ") + url + F(" HTTP/1.1\r\n") +
F("Content-Length: ")+ len + F("\r\n") +
F("Host: ") + ControllerSettings.getHost() + F("\r\n") + authHeader +
F("Connection: close\r\n\r\n")
+ buffer);
// We now create a URI for the request
String url = F("/ESPEasy");
unsigned long timer = millis() + 200;
while (!client.available() && !timeOutReached(timer))
yield();
String request = create_http_request_auth(
controller_number, element.controller_idx, ControllerSettings,
F("POST"), url, jsonString.length());
request += jsonString;
// Read all the lines of the reply from server and print them to Serial
while (client.available()) {
// String line = client.readStringUntil('\n');
String line;
safeReadStringUntil(client, line, '\n');
// String helper = line;
// line.toCharArray(log, 80);
addLog(LOG_LEVEL_DEBUG_MORE, line);
if (line.startsWith(F("HTTP/1.1 200 OK"))) {
// strcpy_P(log, PSTR("HTTP : Success"));
addLog(LOG_LEVEL_DEBUG_MORE, F("HTTP : Success"));
// success = true;
}
else if (line.startsWith(F("HTTP/1.1 4"))) {
addLog(LOG_LEVEL_ERROR, String(F("HTTP : Error: "))+line);
}
yield();
}
// strcpy_P(log, PSTR("HTTP : closing connection"));
addLog(LOG_LEVEL_DEBUG, F("HTTP : closing connection"));
client.flush();
client.stop();
return send_via_http(controller_number, client, request, ControllerSettings.MustCheckReply);
}
#endif
+38 -35
View File
@@ -41,20 +41,32 @@ boolean CPlugin_010(byte function, struct EventStruct *event, String& string)
case CPLUGIN_PROTOCOL_SEND:
{
byte valueCount = getValueCountFromSensorType(event->sensorType);
C010_queue_element element(event, valueCount);
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
for (byte x = 0; x < valueCount; x++)
{
bool isvalid;
String formattedValue = formatUserVar(event, x, isvalid);
if (isvalid)
C010_Send(event, x, formattedValue);
if (valueCount > 1)
{
delayBackground(Settings.MessageDelay);
// unsigned long timer = millis() + Settings.MessageDelay;
// while (!timeOutReached(timer))
// backgroundtasks();
if (isvalid) {
element.txt[x] = "";
element.txt[x] += ControllerSettings.Publish;
parseControllerVariables(element.txt[x], event, false);
element.txt[x].replace(F("%valname%"), ExtraTaskSettings.TaskDeviceValueNames[x]);
element.txt[x].replace(F("%value%"), formattedValue);
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
char log[80];
element.txt[x].toCharArray(log, 80);
addLog(LOG_LEVEL_DEBUG_MORE, log);
}
}
}
success = C010_DelayHandler.addToQueue(element);
scheduleNextDelayQueue(TIMER_C010_DELAY_QUEUE, C010_DelayHandler.getNextScheduleTime());
break;
}
@@ -66,34 +78,25 @@ boolean CPlugin_010(byte function, struct EventStruct *event, String& string)
//********************************************************************************
// Generic UDP message
//********************************************************************************
void C010_Send(struct EventStruct *event, byte varIndex, const String& formattedValue)
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
// boolean success = false;
addLog(LOG_LEVEL_DEBUG, String(F("UDP : sending to ")) + ControllerSettings.getHostPortString());
statusLED(true);
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
String msg = "";
msg += ControllerSettings.Publish;
parseControllerVariables(msg, event, false);
msg.replace(F("%valname%"), ExtraTaskSettings.TaskDeviceValueNames[varIndex]);
msg.replace(F("%value%"), formattedValue);
if (wifiStatus == ESPEASY_WIFI_SERVICES_INITIALIZED) {
ControllerSettings.beginPacket(portUDP);
portUDP.write((uint8_t*)msg.c_str(),msg.length());
portUDP.endPacket();
bool do_process_c010_delay_queue(int controller_number, const C010_queue_element& element, ControllerSettingsStruct& ControllerSettings) {
while (element.txt[element.valuesSent] == "") {
// A non valid value, which we are not going to send.
// Increase sent counter until a valid value is found.
if (element.checkDone(true))
return true;
}
WiFiUDP C010_portUDP;
if (!beginWiFiUDP_randomPort(C010_portUDP)) return false;
if (!try_connect_host(controller_number, C010_portUDP, ControllerSettings))
return false;
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
char log[80];
msg.toCharArray(log, 80);
addLog(LOG_LEVEL_DEBUG_MORE, log);
}
C010_portUDP.write(
(uint8_t*)element.txt[element.valuesSent].c_str(),
element.txt[element.valuesSent].length());
bool reply = C010_portUDP.endPacket();
C010_portUDP.stop();
if (ControllerSettings.MustCheckReply)
return element.checkDone(reply);
return element.checkDone(true);
}
#endif
+26 -68
View File
@@ -117,68 +117,53 @@ boolean CPlugin_011(byte function, struct EventStruct *event, String& string)
case CPLUGIN_PROTOCOL_SEND:
{
HTTPSend011(event);
success = Create_schedule_HTTP_C011(event);
break;
}
}
return success;
}
//********************************************************************************
// Generic HTTP request
//********************************************************************************
bool do_process_c011_delay_queue(int controller_number, const C011_queue_element& element, ControllerSettingsStruct& ControllerSettings) {
WiFiClient client;
if (!try_connect_host(controller_number, client, ControllerSettings))
return false;
return send_via_http(controller_number, client, element.txt, ControllerSettings.MustCheckReply);
}
//********************************************************************************
// Generic HTTP get request
// Create request
//********************************************************************************
boolean HTTPSend011(struct EventStruct *event)
boolean Create_schedule_HTTP_C011(struct EventStruct *event)
{
int controller_number = CPLUGIN_ID_011;
if (!WiFiConnected(100)) {
return false;
}
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
String authHeader = "";
if ((SecuritySettings.ControllerUser[event->ControllerIndex][0] != 0) && (SecuritySettings.ControllerPassword[event->ControllerIndex][0] != 0))
{
base64 encoder;
String auth = SecuritySettings.ControllerUser[event->ControllerIndex];
auth += ":";
auth += SecuritySettings.ControllerPassword[event->ControllerIndex];
authHeader = F("Authorization: Basic ");
authHeader += encoder.encode(auth);
authHeader += F(" \r\n");
}
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C011_ConfigStruct customConfig;
LoadCustomControllerSettings(event->ControllerIndex,(byte*)&customConfig, sizeof(customConfig));
customConfig.zero_last();
boolean success = false;
addLog(LOG_LEVEL_DEBUG, String(F("HTTP : connecting to "))+
ControllerSettings.getHostPortString());
// Use WiFiClient class to create TCP connections
WiFiClient client;
if (!ControllerSettings.connectToHost(client))
{
connectionFailures++;
addLog(LOG_LEVEL_ERROR, F("HTTP : connection failed"));
if (!try_connect_host(controller_number, client, ControllerSettings))
return false;
}
statusLED(true);
if (connectionFailures)
connectionFailures--;
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
String payload = String(customConfig.HttpMethod) + " /";
payload += customConfig.HttpUri;
payload += F(" HTTP/1.1\r\n");
payload += F("Host: ");
payload += ControllerSettings.getHostPortString();
payload += F("\r\n");
payload += authHeader;
payload += F("Connection: close\r\n");
String payload = create_http_request_auth(
controller_number, event->ControllerIndex, ControllerSettings,
String(customConfig.HttpMethod), customConfig.HttpUri);
if (strlen(customConfig.HttpHeader) > 0)
payload += customConfig.HttpHeader;
@@ -195,36 +180,9 @@ boolean HTTPSend011(struct EventStruct *event)
}
payload += F("\r\n");
// This will send the request to the server
client.print(payload);
addLog(LOG_LEVEL_DEBUG_MORE, payload);
unsigned long timer = millis() + 200;
while (!client.available() && !timeOutReached(timer))
yield();
// Read all the lines of the reply from server and print them to Serial
while (client.available()) {
// String line = client.readStringUntil('\n');
String line;
safeReadStringUntil(client, line, '\n');
// line.toCharArray(log, 80);
addLog(LOG_LEVEL_DEBUG_MORE, line);
if (line.startsWith(F("HTTP/1.1 2")))
{
addLog(LOG_LEVEL_DEBUG, F("HTTP : Success!"));
success = true;
}
yield();
}
addLog(LOG_LEVEL_DEBUG, F("HTTP : closing connection"));
client.flush();
client.stop();
return(success);
bool success = C011_DelayHandler.addToQueue(C011_queue_element(event->ControllerIndex, payload));
scheduleNextDelayQueue(TIMER_C011_DELAY_QUEUE, C011_DelayHandler.getNextScheduleTime());
return success;
}
// parses the string and returns only the the number of name/values we want
+87 -74
View File
@@ -34,52 +34,65 @@ boolean CPlugin_012(byte function, struct EventStruct *event, String& string)
case CPLUGIN_PROTOCOL_SEND:
{
if (wifiStatus != ESPEASY_WIFI_SERVICES_INITIALIZED) {
success = false;
break;
}
// 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.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
String postDataStr = F("");
const byte valueCount = getValueCountFromSensorType(event->sensorType);
success = CPlugin_012_send(event, valueCount);
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
for (byte x = 0; x < valueCount; x++)
{
bool isvalid;
String formattedValue = formatUserVar(event, x, isvalid);
if (isvalid) {
element.txt[x] = F("update/V");
element.txt[x] += event->idx + x;
element.txt[x] += F("?value=");
element.txt[x] += formattedValue;
addLog(LOG_LEVEL_DEBUG_MORE, element.txt[x]);
}
}
success = C012_DelayHandler.addToQueue(element);
scheduleNextDelayQueue(TIMER_C012_DELAY_QUEUE, C012_DelayHandler.getNextScheduleTime());
break;
}
}
return success;
}
boolean CPlugin_012_send(struct EventStruct *event, int nrValues) {
String postDataStr = F("");
boolean success = true;
for (int i = 0; i < nrValues && success; ++i) {
postDataStr = F("update/V") ;
postDataStr += event->idx + i;
postDataStr += F("?value=");
postDataStr += formatUserVarNoCheck(event, i);
success = Blynk_get(postDataStr, event->ControllerIndex);
//********************************************************************************
// Process Queued Blynk request, with data set to NULL
//********************************************************************************
bool do_process_c012_delay_queue(int controller_number, const C012_queue_element& element, ControllerSettingsStruct& ControllerSettings) {
while (element.txt[element.valuesSent] == "") {
// A non valid value, which we are not going to send.
// Increase sent counter until a valid value is found.
if (element.checkDone(true))
return true;
}
return success;
}
boolean Blynk_get(const String& command, byte controllerIndex, float *data )
{
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 )
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(controllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
// Use WiFiClient class to create TCP connections
WiFiClient client;
if ((SecuritySettings.ControllerPassword[controllerIndex][0] == 0) || !ControllerSettings.connectToHost(client))
{
connectionFailures++;
addLog(LOG_LEVEL_ERROR, F("Blynk : connection failed"));
LoadControllerSettings(controllerIndex, ControllerSettings);
if ((SecuritySettings.ControllerPassword[controllerIndex][0] == 0)) {
addLog(LOG_LEVEL_ERROR, F("Blynk : No password set"));
return false;
}
if (connectionFailures)
connectionFailures--;
WiFiClient client;
if (!try_connect_host(CPLUGIN_ID_012, client, ControllerSettings))
return false;
// We now create a URI for the request
char request[300] = {0};
@@ -90,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() && !timeOutReached(timer))
yield();
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()) {
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);
}
yield();
}
yield();
}
strcpy_P(log, PSTR("HTTP : closing connection"));
addLog(LOG_LEVEL_DEBUG, log);
addLog(LOG_LEVEL_DEBUG, F("HTTP : closing connection (012)"));
client.flush();
client.stop();
// important - backgroudtasks - free mem
timer = millis() + Settings.MessageDelay;
// important - backgroundtasks - free mem
unsigned long timer = millis() + Settings.MessageDelay;
while (!timeOutReached(timer))
backgroundtasks();
+29 -34
View File
@@ -80,7 +80,7 @@ boolean CPlugin_013(byte function, struct EventStruct *event, String& string)
case CPLUGIN_PROTOCOL_SEND:
{
C013_Send(event, 0, UserVar[event->BaseVarIndex], 0);
C013_SendUDPTaskData(0, event->TaskIndex, event->TaskIndex);
break;
}
@@ -98,14 +98,6 @@ boolean CPlugin_013(byte function, struct EventStruct *event, String& string)
//********************************************************************************
// Generic UDP message
//********************************************************************************
void C013_Send(struct EventStruct *event, byte varIndex, float value, unsigned long longValue)
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
statusLED(true);
C013_SendUDPTaskData(0, event->TaskIndex, event->TaskIndex);
}
void C013_SendUDPTaskInfo(byte destUnit, byte sourceTaskIndex, byte destTaskIndex)
{
if (!WiFiConnected(100)) {
@@ -121,19 +113,18 @@ void C013_SendUDPTaskInfo(byte destUnit, byte sourceTaskIndex, byte destTaskInde
for (byte x = 0; x < VARS_PER_TASK; x++)
strcpy(infoReply.ValueNames[x], ExtraTaskSettings.TaskDeviceValueNames[x]);
byte firstUnit = 1;
byte lastUnit = UNIT_MAX - 1;
if (destUnit != 0)
{
firstUnit = destUnit;
lastUnit = destUnit;
}
for (byte x = firstUnit; x <= lastUnit; x++)
{
if (x != Settings.Unit){
infoReply.destUnit = x;
C013_sendUDP(x, (byte*)&infoReply, sizeof(infoStruct));
delay(10);
infoReply.destUnit = destUnit;
C013_sendUDP(destUnit, (byte*)&infoReply, sizeof(infoStruct));
delay(10);
} else {
for (NodesMap::iterator it = Nodes.begin(); it != Nodes.end(); ++it) {
if (it->first != Settings.Unit) {
infoReply.destUnit = it->first;
C013_sendUDP(it->first, (byte*)&infoReply, sizeof(infoStruct));
delay(10);
}
}
}
delay(50);
@@ -151,19 +142,18 @@ void C013_SendUDPTaskData(byte destUnit, byte sourceTaskIndex, byte destTaskInde
for (byte x = 0; x < VARS_PER_TASK; x++)
dataReply.Values[x] = UserVar[dataReply.sourceTaskIndex * VARS_PER_TASK + x];
byte firstUnit = 1;
byte lastUnit = UNIT_MAX - 1;
if (destUnit != 0)
{
firstUnit = destUnit;
lastUnit = destUnit;
}
for (byte x = firstUnit; x <= lastUnit; x++)
{
if (x != Settings.Unit){
dataReply.destUnit = x;
C013_sendUDP(x, (byte*) &dataReply, sizeof(dataStruct));
delay(10);
dataReply.destUnit = destUnit;
C013_sendUDP(destUnit, (byte*) &dataReply, sizeof(dataStruct));
delay(10);
} else {
for (NodesMap::iterator it = Nodes.begin(); it != Nodes.end(); ++it) {
if (it->first != Settings.Unit) {
dataReply.destUnit = it->first;
C013_sendUDP(it->first, (byte*) &dataReply, sizeof(dataStruct));
delay(10);
}
}
}
delay(50);
@@ -177,9 +167,14 @@ void C013_sendUDP(byte unit, byte* data, byte size)
if (!WiFiConnected(100)) {
return;
}
if (unit != 255)
if (Nodes[unit].ip[0] == 0)
NodesMap::iterator it;
if (unit != 255) {
it = Nodes.find(unit);
if (it == Nodes.end())
return;
if (it->second.ip[0] == 0)
return;
}
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
String log = F("C013 : Send UDP message to ");
log += unit;
@@ -192,7 +187,7 @@ void C013_sendUDP(byte unit, byte* data, byte size)
if (unit == 255)
remoteNodeIP = {255, 255, 255, 255};
else
remoteNodeIP = Nodes[unit].ip;
remoteNodeIP = it->second.ip;
if (!beginWiFiUDP_randomPort(C013_portUDP)) return;
if (C013_portUDP.beginPacket(remoteNodeIP, Settings.UDPPort) == 0) return;
C013_portUDP.write(data, size);
+692
View File
@@ -0,0 +1,692 @@
#ifndef CPLUGIN_HELPER_H
#define CPLUGIN_HELPER_H CPLUGIN_HELPER_H
// These element classes should be defined as class, to be used as template.
/*********************************************************************************************\
* MQTT_queue_element for all MQTT base controllers
\*********************************************************************************************/
class MQTT_queue_element {
public:
MQTT_queue_element() : controller_idx(0), _retained(false) {}
MQTT_queue_element(int ctrl_idx,
const String& topic, const String& payload, boolean retained) :
controller_idx(ctrl_idx), _topic(topic), _payload(payload), _retained(retained)
{}
size_t getSize() const {
return sizeof(this) + _topic.length() + _payload.length();
}
int controller_idx;
String _topic;
String _payload;
boolean _retained;
};
/*********************************************************************************************\
* Simple queue element, only storing controller index and some String
\*********************************************************************************************/
class simple_queue_element_string_only {
public:
simple_queue_element_string_only() : controller_idx(0) {}
simple_queue_element_string_only(int ctrl_idx, const String& req) :
controller_idx(ctrl_idx), txt(req) {}
size_t getSize() const {
return sizeof(this) + txt.length();
}
int controller_idx;
String txt;
};
/*********************************************************************************************\
* C001_queue_element for queueing requests for C001.
\*********************************************************************************************/
#define C001_queue_element simple_queue_element_string_only
/*********************************************************************************************\
* C003_queue_element for queueing requests for C003 Nodo Telnet.
\*********************************************************************************************/
#define C003_queue_element simple_queue_element_string_only
/*********************************************************************************************\
* C004_queue_element for queueing requests for C004 ThingSpeak.
* Typical use case for Thingspeak is to only send values every N seconds/minutes.
* So we just store everything needed to recreate the event when the time is ready.
\*********************************************************************************************/
class C004_queue_element {
public:
C004_queue_element() : controller_idx(0), TaskIndex(0), idx(0), sensorType(0) {}
C004_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);
}
int controller_idx;
byte TaskIndex;
int idx;
byte sensorType;
};
/*********************************************************************************************\
* C007_queue_element for queueing requests for C007 Emoncms
\*********************************************************************************************/
class C007_queue_element {
public:
C007_queue_element() : controller_idx(0), TaskIndex(0), idx(0), sensorType(0) {}
C007_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);
}
int controller_idx;
byte TaskIndex;
int idx;
byte sensorType;
};
/*********************************************************************************************\
* Base class for controllers that only send a single value per request and thus needs to
* keep track of the number of values already sent.
\*********************************************************************************************/
class queue_element_single_value_base {
public:
queue_element_single_value_base() : controller_idx(0), TaskIndex(0), idx(0), valuesSent(0) {}
queue_element_single_value_base(const struct EventStruct* event, byte value_count) :
controller_idx(event->ControllerIndex),
TaskIndex(event->TaskIndex),
idx(event->idx),
valuesSent(0),
valueCount(value_count) {}
bool checkDone(bool succesfull) const {
if (succesfull) ++valuesSent;
return (valuesSent >= valueCount || valuesSent >= VARS_PER_TASK);
}
size_t getSize() const {
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;
byte TaskIndex;
int idx;
mutable byte valuesSent; // Value must be set by const function checkDone()
byte valueCount;
};
/*********************************************************************************************\
* C008_queue_element for queueing requests for 008: Generic HTTP
* Using queue_element_single_value_base
\*********************************************************************************************/
#define C008_queue_element queue_element_single_value_base
/*********************************************************************************************\
* C009_queue_element for queueing requests for C009: FHEM HTTP.
\*********************************************************************************************/
class C009_queue_element {
public:
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 {
size_t total = sizeof(this);
for (int i = 0; i < VARS_PER_TASK; ++i) {
total += txt[i].length();
total += valueNames[i].length();
}
return total;
}
String txt[VARS_PER_TASK];
String valueNames[VARS_PER_TASK];
int controller_idx;
byte TaskIndex;
int idx;
byte sensorType;
};
/*********************************************************************************************\
* C010_queue_element for queueing requests for 010: Generic UDP
* Using queue_element_single_value_base
\*********************************************************************************************/
#define C010_queue_element queue_element_single_value_base
/*********************************************************************************************\
* C011_queue_element for queueing requests for 011: Generic HTTP Advanced
\*********************************************************************************************/
#define C011_queue_element simple_queue_element_string_only
/*********************************************************************************************\
* C012_queue_element for queueing requests for 012: Blynk
* Using queue_element_single_value_base
\*********************************************************************************************/
#define C012_queue_element queue_element_single_value_base
/*********************************************************************************************\
* ControllerDelayHandlerStruct
\*********************************************************************************************/
template<class T>
struct ControllerDelayHandlerStruct {
ControllerDelayHandlerStruct() :
lastSend(0),
minTimeBetweenMessages(CONTROLLER_DELAY_QUEUE_DELAY_DFLT),
max_queue_depth(CONTROLLER_DELAY_QUEUE_DEPTH_DFLT),
attempt(0),
max_retries(CONTROLLER_DELAY_QUEUE_RETRY_DFLT),
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;
if (minTimeBetweenMessages == 0) minTimeBetweenMessages = CONTROLLER_DELAY_QUEUE_DELAY_DFLT;
// No less than 10 msec between messages.
if (minTimeBetweenMessages < 10) minTimeBetweenMessages = 10;
}
bool queueFull(const T& element) const {
if (sendQueue.size() >= max_queue_depth) return true;
// Number of elements is not exceeding the limit, check memory
int freeHeap = ESP.getFreeHeap();
if (freeHeap > 5000) return false; // Memory is not an issue.
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = "Controller-";
log += element.controller_idx +1;
log += " : Memory used: ";
log += getQueueMemorySize();
log += " bytes ";
log += sendQueue.size();
log += " items ";
log += freeHeap;
log += " free";
addLog(LOG_LEVEL_DEBUG, log);
}
return true;
}
// Try to add to the queue, if permitted by "delete_oldest"
// Return false when no item was added.
bool addToQueue(const T& element) {
if (delete_oldest) {
// Force add to the queue.
// If max buffer is reached, the oldest in the queue (first to be served) will be removed.
while (queueFull(element)) {
sendQueue.pop_front();
}
sendQueue.emplace_back(element);
return true;
}
if (!queueFull(element)) {
sendQueue.emplace_back(element);
return true;
}
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = get_formatted_Controller_number(element.controller_idx);
log += " : queue full";
addLog(LOG_LEVEL_DEBUG, log);
}
return false;
}
// Get the next element.
// Remove front element when max_retries is reached.
bool getNext(T& element) {
if (sendQueue.empty()) return false;
if (attempt > max_retries) {
sendQueue.pop_front();
attempt = 0;
if (sendQueue.empty()) return false;
}
element = sendQueue.front();
return true;
}
// Mark as processed and return time to schedule for next process.
// Return 0 when nothing to process.
// @param remove_from_queue indicates whether the elements should be removed from the queue.
unsigned long markProcessed(bool remove_from_queue) {
if (sendQueue.empty()) return 0;
if (remove_from_queue) {
sendQueue.pop_front();
attempt = 0;
} else {
++attempt;
}
lastSend = millis();
return getNextScheduleTime();
}
unsigned long getNextScheduleTime() const {
if (sendQueue.empty()) return 0;
unsigned long nextTime = lastSend + minTimeBetweenMessages;
if (timePassedSince(nextTime) > 0) {
nextTime = millis();
}
if (nextTime == 0) nextTime = 1; // Just to make sure it will be executed
return nextTime;
}
size_t getQueueMemorySize() const {
size_t totalSize = 0;
for (auto it = sendQueue.begin(); it != sendQueue.end(); ++it) {
totalSize += it->getSize();
}
return totalSize;
}
std::list<T> sendQueue;
unsigned long lastSend;
unsigned int minTimeBetweenMessages;
byte max_queue_depth;
byte attempt;
byte max_retries;
bool delete_oldest;
bool must_check_reply;
};
ControllerDelayHandlerStruct<MQTT_queue_element> MQTTDelayHandler;
// This macro defines the code needed to create the 'process_c##NNN##_delay_queue()'
// function and all needed objects and forward declarations.
// It is a macro to prevent common typo errors.
// This function will perform the (re)scheduling and mark if it is processed (and can be removed)
// The controller itself must implement the 'do_process_c004_delay_queue' function to actually
// send the data.
// Its return value must state whether it can be marked 'Processed'.
// N.B. some controllers only can send one value per iteration, so a returned "false" can mean it
// was still successful. The controller should keep track of the last value sent
// in the element stored in the queue.
#define DEFINE_Cxxx_DELAY_QUEUE_MACRO(NNN, M) \
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, 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)); \
STOP_TIMER(C##NNN##_DELAY_QUEUE); \
scheduleNextDelayQueue(TIMER_C##NNN##_DELAY_QUEUE, C##NNN##_DelayHandler.getNextScheduleTime()); \
}
// Define the function wrappers to handle the calling to Cxxx_DelayHandler etc.
// If someone knows how to add leading zeros in macros, please be my guest :)
#ifdef USES_C001
DEFINE_Cxxx_DELAY_QUEUE_MACRO(001, 1)
#endif
#ifdef USES_C003
DEFINE_Cxxx_DELAY_QUEUE_MACRO(003, 3)
#endif
#ifdef USES_C004
DEFINE_Cxxx_DELAY_QUEUE_MACRO(004, 4)
#endif
#ifdef USES_C007
DEFINE_Cxxx_DELAY_QUEUE_MACRO(007, 7)
#endif
#ifdef USES_C008
DEFINE_Cxxx_DELAY_QUEUE_MACRO(008, 8)
#endif
#ifdef USES_C009
DEFINE_Cxxx_DELAY_QUEUE_MACRO(009, 9)
#endif
#ifdef USES_C010
DEFINE_Cxxx_DELAY_QUEUE_MACRO(010, 10)
#endif
#ifdef USES_C011
DEFINE_Cxxx_DELAY_QUEUE_MACRO(011, 11)
#endif
#ifdef USES_C012
DEFINE_Cxxx_DELAY_QUEUE_MACRO(012, 12)
#endif
/*
#ifdef USES_C013
DEFINE_Cxxx_DELAY_QUEUE_MACRO(013, 13)
#endif
*/
// When extending this, also extend in Scheduler.ino:
// void process_interval_timer(unsigned long id, unsigned long lasttimer)
/*********************************************************************************************\
* Helper functions used in a number of controllers
\*********************************************************************************************/
bool safeReadStringUntil(Stream &input, String &str, char terminator, unsigned int maxSize = 1024, unsigned int timeout = 1000)
{
int c;
const unsigned long start = millis();
const unsigned long timer = start + timeout;
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 += 10;
backgroundtasks();
} else {
yield();
}
} while (!timeOutReached(timer));
addLog(LOG_LEVEL_ERROR, F("Timeout while reading input data!"));
return(false);
}
bool valid_controller_number(int controller_number) {
if (controller_number < 0) return false;
return true;
// return getProtocolIndex(controller_number) <= protocolCount;
}
String get_formatted_Controller_number(int controller_number) {
if (!valid_controller_number(controller_number)) {
return F("C---");
}
String result = F("C");
if (controller_number < 100) result += '0';
if (controller_number < 10) result += '0';
result += controller_number;
return result;
}
String get_auth_header(int controller_index) {
String authHeader = "";
if (controller_index < CONTROLLER_MAX) {
if ((SecuritySettings.ControllerUser[controller_index][0] != 0) &&
(SecuritySettings.ControllerPassword[controller_index][0] != 0))
{
base64 encoder;
String auth = SecuritySettings.ControllerUser[controller_index];
auth += ":";
auth += SecuritySettings.ControllerPassword[controller_index];
authHeader = F("Authorization: Basic ");
authHeader += encoder.encode(auth);
authHeader += F(" \r\n");
}
} else {
addLog(LOG_LEVEL_ERROR, F("Invalid controller index"));
}
return authHeader;
}
String get_user_agent_request_header_field() {
static unsigned int agent_size = 20;
String request;
request.reserve(agent_size);
request = F("User-Agent: ");
request += F("ESP Easy/");
request += BUILD;
request += '/';
request += String(CRCValues.compileDate);
request += ' ';
request += String(CRCValues.compileTime);
request += F("\r\n");
agent_size = request.length();
return request;
}
String do_create_http_request(
const String& hostportString,
const String& method, const String& uri,
const String& auth_header, const String& additional_options,
int content_length) {
int estimated_size = hostportString.length() + method.length()
+ uri.length() + auth_header.length()
+ additional_options.length()
+ 42;
if (content_length >= 0) estimated_size += 25;
String request;
request.reserve(estimated_size);
request += method;
request += ' ';
if (!uri.startsWith("/")) request += '/';
request += uri;
request += F(" HTTP/1.1\r\n");
if (content_length >= 0) {
request += F("Content-Length: ");
request += content_length;
request += F("\r\n");
}
request += F("Host: ");
request += hostportString;
request += F("\r\n");
request += auth_header;
request += additional_options;
request += get_user_agent_request_header_field();
request += F("Connection: close\r\n");
request += F("\r\n");
addLog(LOG_LEVEL_DEBUG, request);
return request;
}
String do_create_http_request(
const String& hostportString,
const String& method, const String& uri) {
return do_create_http_request(hostportString, method, uri,
"", // auth_header
"", // additional_options
-1 // content_length
);
}
String do_create_http_request(
int controller_number, ControllerSettingsStruct& ControllerSettings,
const String& method, const String& uri,
int content_length) {
const bool defaultport = ControllerSettings.Port == 0 || ControllerSettings.Port == 80;
return do_create_http_request(
defaultport ? ControllerSettings.getHost() : ControllerSettings.getHostPortString(),
method,
uri,
"", // auth_header
"", // additional_options
content_length);
}
String create_http_request_auth(
int controller_number, int controller_index, ControllerSettingsStruct& ControllerSettings,
const String& method, const String& uri,
int content_length) {
const bool defaultport = ControllerSettings.Port == 0 || ControllerSettings.Port == 80;
return do_create_http_request(
defaultport ? ControllerSettings.getHost() : ControllerSettings.getHostPortString(),
method,
uri,
get_auth_header(controller_index),
"", // additional_options
content_length);
}
String create_http_get_request(int controller_number, ControllerSettingsStruct& ControllerSettings,
const String& uri) {
return do_create_http_request(controller_number, ControllerSettings, F("GET"), uri, -1);
}
String create_http_request_auth(int controller_number, int controller_index, ControllerSettingsStruct& ControllerSettings,
const String& method, const String& uri) {
return create_http_request_auth(controller_number, controller_index, ControllerSettings, method, uri, -1);
}
void log_connecting_to(const String& prefix, int controller_number, ControllerSettingsStruct& ControllerSettings) {
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = prefix;
log += get_formatted_Controller_number(controller_number);
log += F(" connecting to ");
log += ControllerSettings.getHostPortString();
addLog(LOG_LEVEL_DEBUG, log);
}
}
void log_connecting_fail(const String& prefix, int controller_number, ControllerSettingsStruct& ControllerSettings) {
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
String log = prefix;
log += get_formatted_Controller_number(controller_number);
log += F(" connection failed");
addLog(LOG_LEVEL_ERROR, log);
}
}
bool count_connection_results(bool success, const String& prefix, int controller_number, ControllerSettingsStruct& ControllerSettings) {
if (!success)
{
connectionFailures++;
log_connecting_fail(prefix, controller_number, ControllerSettings);
return false;
}
statusLED(true);
if (connectionFailures)
connectionFailures--;
return true;
}
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(
success,
F("UDP : "), controller_number, 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(
success,
F("HTTP : "), controller_number, ControllerSettings);
}
// Use "client.available() || client.connected()" to read all lines from slow servers.
// See: https://github.com/esp8266/Arduino/pull/5113
// https://github.com/esp8266/Arduino/pull/1829
bool client_available(WiFiClient& client) {
return client.available() || client.connected();
}
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);
if (must_check_reply) {
unsigned long timer = millis() + 200;
while (!client.available()) {
if (timeOutReached(timer)) return false;
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 (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);
}
yield();
}
}
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("HTTP : ");
log += logIdentifier;
log += F(" closing connection");
addLog(LOG_LEVEL_DEBUG, log);
}
client.flush();
client.stop();
return success;
}
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);
}
#endif // CPLUGIN_HELPER_H
+19
View File
@@ -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) {
@@ -89,6 +90,24 @@ boolean NPlugin_001_send(const NotificationSettingsStruct& notificationsettings,
"X-Mailer: EspEasy v$espeasyversion\r\n\r\n"
);
String email_address = notificationsettings.Sender;
int pos_less = email_address.indexOf('<');
if (pos_less == -1) {
// No email address markup
mailheader.replace(String(F("$nodename")), Settings.Name);
mailheader.replace(String(F("$emailfrom")), notificationsettings.Sender);
} else {
String senderName = email_address.substring(0, pos_less);
senderName.replace("\"", ""); // Remove quotes
String address = email_address.substring(pos_less + 1);
address.replace("<", "");
address.replace(">", "");
address.trim();
senderName.trim();
mailheader.replace(String(F("$nodename")), senderName);
mailheader.replace(String(F("$emailfrom")), address);
}
mailheader.replace(String(F("$nodename")), Settings.Name);
mailheader.replace(String(F("$emailfrom")), notificationsettings.Sender);
mailheader.replace(String(F("$ato")), notificationsettings.Receiver);
+26
View File
@@ -257,6 +257,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, &currentState);
} 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;
+4 -4
View File
@@ -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);
}
+142 -116
View File
@@ -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;
}
+23 -1
View File
@@ -20,7 +20,7 @@ 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;
@@ -124,6 +124,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, &currentState);
} 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
View File
@@ -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
View File
@@ -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
+6
View File
@@ -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,7 +158,10 @@ 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");
+42 -7
View File
@@ -11,7 +11,7 @@
boolean Plugin_019(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
static byte switchstate[TASKS_MAX];
static boolean switchstate[TASKS_MAX];
switch (function)
{
@@ -20,10 +20,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;
@@ -66,10 +66,17 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
// read and store current state to prevent switching at boot time
switchstate[event->TaskIndex] = Plugin_019_Read(Settings.TaskDevicePort[event->TaskIndex]);
// if boot state must be send, inverse default state
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0])
switchstate[event->TaskIndex] = !switchstate[event->TaskIndex];
if (Settings.TaskDevicePin1Inversed[event->TaskIndex]){
UserVar[event->BaseVarIndex] = !switchstate[event->TaskIndex];
} else {
UserVar[event->BaseVarIndex] = 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][0]) {
switchstate[event->TaskIndex] = !switchstate[event->TaskIndex];
}
success = true;
break;
}
@@ -85,7 +92,13 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
log += state;
addLog(LOG_LEVEL_INFO, log);
switchstate[event->TaskIndex] = state;
UserVar[event->BaseVarIndex] = state;
if (Settings.TaskDevicePin1Inversed[event->TaskIndex]){
UserVar[event->BaseVarIndex] = !switchstate[event->TaskIndex];
} else {
UserVar[event->BaseVarIndex] = switchstate[event->TaskIndex];
}
event->sensorType = SENSOR_TYPE_SWITCH;
sendData(event);
}
@@ -128,6 +141,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, &currentState);
} 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;
+20 -11
View File
@@ -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);
+7 -12
View File
@@ -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;
+1 -1
View File
@@ -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;
}
+3 -1
View File
@@ -274,6 +274,8 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string)
return success;
}
// 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:
@@ -615,4 +617,4 @@ uint16_t countValuesInStr(const String str, char sep) {
// }
// return result;
//}
#endif // USES_P035
#endif // USES_P035
+8 -5
View File
@@ -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;
+66 -41
View File
@@ -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;
@@ -39,6 +70,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 +82,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 +149,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 +175,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 +282,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 +306,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 +373,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) {
@@ -368,7 +393,7 @@ boolean MQTTConnect_037(String clientid)
return false; // Not connected, so no use in wasting time to connect to a host.
}
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(enabledMqttController, (byte*)&ControllerSettings, sizeof(ControllerSettings));
LoadControllerSettings(enabledMqttController, ControllerSettings);
if (ControllerSettings.UseDNS) {
MQTTclient_037->setServer(ControllerSettings.getHost().c_str(), ControllerSettings.Port);
} else {
+16 -12
View File
@@ -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);
}
+1
View File
@@ -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
View File
@@ -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");
+10 -2
View File
@@ -148,11 +148,19 @@ byte CPluginCall(byte Function, struct EventStruct *event)
{
// Unconditional calls to all plugins
case CPLUGIN_PROTOCOL_ADD:
for (x = 0; x < CPLUGIN_MAX; x++)
if (CPlugin_id[x] != 0){
for (x = 0; x < CPLUGIN_MAX; x++) {
if (CPlugin_id[x] != 0) {
const unsigned int next_ProtocolIndex = protocolCount + 2;
if (next_ProtocolIndex > Protocol.size()) {
// Increase with 8 to get some compromise between number of resizes and wasted space
unsigned int newSize = Protocol.size();
newSize = newSize + 8 - (newSize % 8);
Protocol.resize(newSize);
}
checkRAM(F("CPluginCallADD"),x);
CPlugin_ptr[x](Function, event, dummyString);
}
}
return true;
break;
+10 -2
View File
@@ -1113,6 +1113,15 @@ byte PluginCall(byte Function, struct EventStruct *event, String& str)
case PLUGIN_UNCONDITIONAL_POLL:
for (byte x = 0; x < PLUGIN_MAX; x++) {
if (Plugin_id[x] != 0){
if (Function == PLUGIN_DEVICE_ADD) {
const unsigned int next_DeviceIndex = deviceCount + 2;
if (next_DeviceIndex > Device.size()) {
// Increase with 16 to get some compromise between number of resizes and wasted space
unsigned int newSize = Device.size();
newSize = newSize + 16 - (newSize % 16);
Device.resize(newSize);
}
}
START_TIMER;
Plugin_ptr[x](Function, event, str);
STOP_TIMER_TASK(x,Function);
@@ -1243,11 +1252,10 @@ byte PluginCall(byte Function, struct EventStruct *event, String& str)
// Schedule the plugin to be read.
schedule_task_device_timer_at_init(event->TaskIndex);
}
if (Function != PLUGIN_GET_DEVICEVALUENAMES) {
if (ExtraTaskSettings.TaskIndex != event->TaskIndex) {
// LoadTaskSettings may call PLUGIN_GET_DEVICEVALUENAMES.
LoadTaskSettings(event->TaskIndex);
}
event->BaseVarIndex = event->TaskIndex * VARS_PER_TASK;
checkRAM(F("PluginCall_init"),x);
START_TIMER;
+31 -2
View File
@@ -206,10 +206,25 @@ To create/register a plugin, you have to :
#define USES_P036 // FrameOLED
#define USES_P027 // INA219
#define USES_P028 // BME280
// TODO : Add list of compatible plugins for ESP32 board.
#endif
#ifdef PLUGIN_SET_TEST_ESP32
#ifndef ESP32
#define ESP32
#endif
#ifdef ESP8266
#undef ESP8266
#endif
// #define PLUGIN_SET_ONLY_SWITCH
#define PLUGIN_SET_TESTING
#define CONTROLLER_SET_STABLE
#define NOTIFIER_SET_STABLE
#define PLUGIN_SET_STABLE // add stable
// See also PLUGIN_SET_TEST_ESP32 section at end,
// where incompatible plugins will be disabled.
// TODO : Check compatibility of plugins for ESP32 board.
#endif
// Generic ------------------------------------
@@ -585,8 +600,22 @@ To create/register a plugin, you have to :
#ifdef NOTIFIER_SET_EXPERIMENTAL
#endif
/******************************************************************************\
* Remove incompatible plugins ************************************************
\******************************************************************************/
#ifdef PLUGIN_SET_TEST_ESP32
#undef USES_P010 // BH1750 (doesn't work yet on ESP32)
#undef USES_P049 // MHZ19 (doesn't work yet on ESP32)
#undef USES_P052 // SenseAir (doesn't work yet on ESP32)
#undef USES_P053 // PMSx003
#undef USES_P056 // SDS011-Dust (doesn't work yet on ESP32)
#undef USES_P065 // DRF0299
#undef USES_P071 // Kamstrup401
#undef USES_P075 // Nextion
#undef USES_P078 // Eastron Modbus Energy meters (doesn't work yet on ESP32)
#endif
/******************************************************************************\