Compare commits

...
Author SHA1 Message Date
ESPEasy release bot 0585d48ffa automatically updated release notes for mega-20181101 2018-11-01 04:00:08 +01:00
GrovkillenandGitHub be9a5d622a Merge pull request #1991 from Grovkillen/Flashtool002003
[Flasher] updated flasher (some minor error)
2018-10-31 22:38:20 +01:00
GrovkillenandGitHub 821fcb9e54 [README] removed IRC link... we're not there. 2018-10-31 21:15:14 +01:00
Jimmy Westberg 6c5c12a6f2 [Flasher] updated flasher (some minor error) 2018-10-31 20:00:48 +01:00
Gijs NoorlanderandGitHub 1d5fbd926e Merge pull request #1986 from giig1967g/new/playground
Fixed some bugs and added the possibility to read the user variables with parameter
2018-10-31 12:16:38 +01:00
Gijs NoorlanderandGitHub 4d3ac95e5a Merge pull request #1985 from uzi18/patch-3
BlynkGet add timeout 1,5s.
2018-10-31 12:16:13 +01:00
Plebs 8a86ee9b89 Update src/Misc.ino 2018-10-31 09:54:23 +01:00
GrovkillenandGitHub ed592a3a0a [Deploy] "source" folder was named "Source" 2018-10-31 08:53:02 +01:00
GrovkillenandGitHub f2b330424c [README] fixed some \r\n 2018-10-31 08:29:14 +01:00
GrovkillenandGitHub 542a758fde [README] added more info about ESP.Easy.Flasher 2018-10-31 08:28:10 +01:00
ESPEasy release bot 8921ebbcaa automatically updated release notes for mega-20181031 2018-10-31 04:00:10 +01:00
Plebs d05d669284 Update src/Misc.ino 2018-10-30 23:54:27 +01:00
Bartłomiej ZimońandGitHub 71a6b344e7 BlynkGet add timeout 1,5s. 2018-10-30 23:15:33 +01:00
Gijs NoorlanderandGitHub c67becac0c Merge pull request #1981 from TD-er/feature/ota_warning
[OTA] Add OTA info and disable when not enough space
2018-10-30 22:56:11 +01:00
Gijs NoorlanderandGitHub 0ec06161c5 Merge pull request #1984 from uzi18/patch-2
BlynkGet fix loop #1951
2018-10-30 22:55:40 +01:00
Gijs NoorlanderandGitHub a2b2cd857a Merge pull request #1983 from Grovkillen/UpdateFlashTool
[Flasher] Updated to 0.02.002
2018-10-30 22:49:40 +01:00
Bartłomiej ZimońandGitHub 0e581f0288 BlynkGet fix loop #1951
this commit fix #1951
2018-10-30 22:47:32 +01:00
Jimmy Westberg fffcd0f2a4 [Path] folder rename 2 2018-10-30 22:45:10 +01:00
Jimmy Westberg 1d923a527f [Path] folder rename 1 2018-10-30 22:44:40 +01:00
Jimmy Westberg 356ebbc875 [Flasher] Updated to 0.02.002
Spelling error
2018-10-30 22:35:42 +01:00
Jimmy Westberg 25fadfc1ed [Deploy] small letters are the standard 2018-10-30 22:29:56 +01:00
Jimmy Westberg 54d892e162 [Flasher] Updated to 0.02.001 2018-10-30 22:27:53 +01:00
Jimmy Westberg 007ef78f2b [Deploy] Added sub folder for BIN files
I moved the compiled files to a sub folder (BIN) which makes it auto load if using the ESP.Easy.Flasher.exe
2018-10-30 21:05:09 +01:00
Jimmy Westberg d16daeca56 [Flasher] Added ESP.Easy.Flasher.exe
And moved the bin files to sub folder "BIN"
2018-10-30 20:52:46 +01:00
TD-er 90fdb8f809 [OTA] Add OTA info and disable when not enough space 2018-10-30 15:58:46 +01:00
ESPEasy release bot ac20349854 automatically updated release notes for mega-20181030 2018-10-30 04:00:07 +01:00
Gijs NoorlanderandGitHub 1a972d61a6 Merge pull request #1619 from jamestutton/mega
Added Special Case to allow Servo to be disabled
2018-10-30 01:01:17 +01:00
Gijs NoorlanderandGitHub d6cf2e141b Merge pull request #1944 from svmac/mega
P022_PCA9685 Enhancements
2018-10-30 01:00:35 +01:00
Gijs NoorlanderandGitHub 7a2e9ba203 Merge pull request #1978 from TD-er/feature/core_2_4_2
[Core 2.4.2] Set core 2.4.2 as default platform
2018-10-30 00:58:46 +01:00
TD-er e3591aceb2 [Core 2.4.2] Set core 2.4.2 as default platform
Also added separate build using core 2.4.1 for those with PWM issues.
2018-10-30 00:23:36 +01:00
ESPEasy release bot ad3514e765 automatically updated release notes for mega-20181029 2018-10-29 04:00:08 +01:00
Gijs NoorlanderandGitHub f543995d86 Merge pull request #1968 from TD-er/bugfix/increase_ntp_accuracy
[NTP] Improve NTP accuracy
2018-10-29 01:02:09 +01:00
Gijs NoorlanderandGitHub 97ccb8eca1 Merge pull request #1975 from uzi18/patch9
Dallas: use reset timming from OneWire lib
2018-10-29 01:01:51 +01:00
Bartlomiej Zimon f291c9d25a Dallas: use reset timming from OneWire lib 2018-10-28 14:59:50 +00:00
Gijs NoorlanderandGitHub bd9ead3851 Merge pull request #1974 from uzi18/patch9
Dallas: add ScratchPad to debug log
2018-10-28 14:48:29 +01:00
Bartlomiej Zimon 43ea0039fd Dallas: add ScratchPad to debug log - OK suffix means data integrity ok (no communication problems) 2018-10-28 12:50:34 +00:00
ESPEasy release bot 74eb638bcb automatically updated release notes for mega-20181028 2018-10-28 04:00:09 +01:00
TD-er c87b035908 [NTP] Change log level for NTP success to INFO 2018-10-28 01:10:11 +02:00
TD-er 0f450eaa3d [NTP] Improve NTP accuracy
Use fractional part of TX section in NTP packet.
Also compensate for total delay to retrieve answer.

More possibilities for improvement of NTP, see https://github.com/lettier/ntpclient/issues/4#issuecomment-360703503
2018-10-28 00:22:39 +02:00
Gijs NoorlanderandGitHub 02df033fc6 Merge pull request #1967 from TD-er/bugfix/1684_Eastron
[Eastron] Make configurable and allow for multiple values read (#1684)
2018-10-28 00:07:57 +02:00
Gijs NoorlanderandGitHub 74d05e58f0 Merge pull request #1768 from workgroupengineering/feature/cron
[Feature] [Plugin] Cron
2018-10-28 00:07:31 +02:00
ESPEasy release bot 3d88537677 automatically updated release notes for mega-20181027 2018-10-27 04:00:12 +02:00
Gijs NoorlanderandGitHub 95b0a7bed9 Merge pull request #1784 from jazzgil/add-raw-ir-reading
Add IR RAW2 Reading / Transmitting.
2018-10-27 02:45:26 +02:00
Gijs NoorlanderandGitHub bc3434532e Merge pull request #1942 from giig1967g/new/doubleclick-pcf
doubleclick and longpress for PCF and MCP
2018-10-27 02:41:12 +02:00
TD-er 1f7639cb42 [Eastron] Make configurable and allow for multiple values read (#1684)
See #1684
It should now be possible to actually select parameters and configure it from the frontend.

N.B. Not yet tested on real hardware.
2018-10-27 00:49:52 +02:00
Gijs NoorlanderandGitHub 923642bb27 Merge pull request #1966 from uzi18/patch9
CSE7766 - fix stats plus add buffer fill level, #1934
2018-10-26 23:25:21 +02:00
Bartlomiej Zimon a09cb906eb CSE7766 - fix stats plus add buffer fill level 2018-10-26 20:43:31 +00:00
Saverio Cisternino 72b0a32839 [CRON] Fixed Daily flash write rate exceeded! 2018-10-26 09:51:41 +02:00
TD-erandSaverio Cisternino e279e5504b [Cron] Rename plugin name to match naming convention 2018-10-26 09:51:41 +02:00
Saverio Cisternino 6d5ddf06ce [CRON] Intercept event PLUGIN_TIME_CHANGE 2018-10-26 09:51:41 +02:00
Saverio Cisternino 7f544b7a10 [CRON] Add event PLUGIN_TIME_CHANGE 2018-10-26 09:51:41 +02:00
Saverio Cisternino fc407e86e1 [CRON] Plugin 2018-10-26 09:51:41 +02:00
Saverio Cisternino a35c3051df [CRON] Replace timeStruct with standard tm 2018-10-26 09:51:41 +02:00
ESPEasy release bot f8f2ddd33b automatically updated release notes for mega-20181026 2018-10-26 04:00:22 +02:00
Gijs NoorlanderandGitHub 6592b57615 Merge pull request #1934 from uzi18/patch9
fix for #1920
2018-10-25 23:52:04 +02:00
Bartlomiej Zimon 6e8e8214b2 CSE7766 - add some stats to debug log 2018-10-25 20:47:36 +00:00
Gijs NoorlanderandGitHub 24b6611c54 Merge pull request #1956 from workgroupengineering/BugFix/Issue_1995
Fixed plugin timer processing do not start, when call setPluginTaskTimer in PLUGIN_TIMER_IN
2018-10-25 21:37:07 +02:00
Gijs NoorlanderandGitHub 8135defa6f Merge pull request #1957 from TD-er/bugfix/c009_reduce_memory
[C009] Reduce memory usage for C009 controller.
2018-10-25 21:27:18 +02:00
Bartlomiej Zimon d5e9cfb081 Merge branch 'mega' of github.com:letscontrolit/ESPEasy into patch9 2018-10-25 18:17:10 +00:00
TD-er ffc8e1fb97 [C009] Reduce memory usage for C009 controller. 2018-10-25 12:09:49 +02:00
Saverio Cisternino 0255f41897 Fixed plugin timer processing do not start, when call setPluginTaskTimer in PLUGIN_TIMER_IN 2018-10-25 10:24:09 +02:00
ESPEasy release bot e88973861f automatically updated release notes for mega-20181025 2018-10-25 04:00:13 +02:00
Gijs NoorlanderandGitHub d6ec7aa736 Merge pull request #1941 from workgroupengineering/feature/Issue_1794
Added auto redirect to home after run reboot command from web page.
2018-10-25 02:12:08 +02:00
Gijs NoorlanderandGitHub 8aa5866dd9 Merge pull request #1954 from TD-er/bugfix/replace_yield_delay_0
[WDT] Replace yield() with delay(0)  & add Sonoff POW R2 build
2018-10-25 02:11:15 +02:00
TD-er 0c384c332e [Travis] Use variables even when no predefined plugins are added. 2018-10-25 01:54:00 +02:00
TD-er 7a4f3b15b3 [Info] Remove MD5 check fail on self-built images
And add in bold a statement it is self built.
2018-10-25 01:43:26 +02:00
TD-er c38e532e67 [Sonoff] Add pre-defined switch and relay + rules on factory reset
Define `GPIO_KEY1` containing GPIO number to get a pre-configured button input on factory reset.

Define `GPIO_REL1` containing GPIO number to get a pre-configured relay on factory reset.

When couple of `GPIO_KEY1` and `GPIO_REL1` is defined, a rule matching both will be added.
This is defined for 1...4
2018-10-25 01:28:25 +02:00
TD-er f983dd924d [Build] Add Sonoff POW R2 (4MB)
Sonoff POW R2 has 4 MB flash. So this needs a separate build, compared to the regular POW.
2018-10-24 20:44:48 +02:00
TD-er f0ff52b76b [WDT] Change yield() to delay(0)
`delay(0)` is a special case of delay, which also calls the system scheduler, `yield()` does not call.
It may interrupt a bit more than `yield()` 

See #1950
2018-10-24 13:25:36 +02:00
Plebs 3c0e5d1965 fixed as requested
- Fixed bootstate logic and inversed logic
- added selected events for doubleclick and longpress
- fixed log messages
- cleaned code
2018-10-23 23:45:23 +02:00
Bartlomiej Zimon 14489a2e98 optimization - remove double checksum check 2018-10-23 19:53:36 +00:00
Bartlomiej Zimon a920b95cfb move to PLUGIN_SERIAL_IN from PLUGIN_TEN_PER_SECOND - serial commandline uses our data 2018-10-23 15:52:27 +00:00
Saverio Cisternino a111175a8f Added auto redirect to home after run reboot command from web page. 2018-10-23 09:14:31 +02:00
svmac 4fd2166b5e P022_PCA9685 Enhancements
Added freq setting
Added mode2 command and setting
Added range command and setting
2018-10-23 05:06:23 +02:00
ESPEasy release bot db76aeb959 automatically updated release notes for mega-20181023 2018-10-23 04:00:14 +02:00
Gijs NoorlanderandGitHub b313957311 Merge pull request #1943 from TD-er/bugfix/pubsubclient_fixes
Bugfix/pubsubclient fixes
2018-10-23 02:09:38 +02:00
TD-er 9d0f741e84 [Docs] Add some WiFi related information to docs
Last week I added this text in a discussion on an issue.
It is also useful in the documentation so keep a copy there.
2018-10-23 01:48:26 +02:00
TD-er 7c0f1551b8 [WiFi timeout] Set default to 1000 msec again
Lower values might give strange issues
2018-10-23 01:45:33 +02:00
TD-er 8a59811392 [PubSubClient] Add delay when waiting for data and flush on disconnect
Applied suggestions:
- https://github.com/knolleary/pubsubclient/pull/472
- https://github.com/knolleary/pubsubclient/pull/495
2018-10-23 01:44:22 +02:00
Plebs e2e715d7de final fixes 2018-10-22 23:31:49 +02:00
Bartlomiej Zimon ccb4cf6a7a move to PLUGIN_TEN_PER_SECOND
update comments
2018-10-22 21:19:00 +00:00
Plebs 1b4a217bc9 fixed a display bug 2018-10-22 23:07:00 +02:00
Plebs 19ecf36358 doubleclick and longpress for PCF and MCP
Added doubleclick and longpress events for PCF (P019) and MCP (P009)
2018-10-22 22:30:20 +02:00
Bartlomiej Zimon bdea34a583 CSE7766 add online checksumming, autosyncing without data loos 2018-10-22 19:54:58 +00:00
Gijs NoorlanderandGitHub b7b5028c0c Merge branch 'mega' into add-raw-ir-reading 2018-10-22 20:05:59 +02:00
Gijs NoorlanderandGitHub c519d887db Merge pull request #1932 from TD-er/bugfix/1486_rotary_encoder
[#1486] Allow multiple instances of rotary encoder
2018-10-22 19:58:07 +02:00
Gijs NoorlanderandGitHub 29c8b850ba Merge pull request #1930 from Kurairaito/patch-1
Allow multiple instances of _P067_HX711_Load_Cell
2018-10-22 13:01:09 +02:00
Gijs NoorlanderandGitHub d01cb9b9a2 Merge pull request #1933 from TD-er/bugfix/Javascript_update_device_values
[JavaScript] Fix update values on Devices tab
2018-10-22 10:59:29 +02:00
ESPEasy release bot 9d894d7c83 automatically updated release notes for mega-20181022 2018-10-22 04:00:15 +02:00
Bartlomiej Zimon a418cb2bd0 Ser2Net catch serial data only if plugin used 2018-10-21 18:51:43 +00:00
Bartlomiej Zimon 907a672fa0 CSE7766 uses 8E1 serial connection 2018-10-21 18:50:57 +00:00
TD-er 2f10274da2 [JavaScript] Cleanup some code duplication 2018-10-21 17:01:55 +02:00
TD-er 399b344779 [JavaScript] Fix update values on Devices tab
Not all plugins have all values.
Now the JavaScript code checks if a DOM element exists before updating its value.
2018-10-21 16:47:06 +02:00
TD-er 2e06ed14c0 [#1486] Allow multiple instances of rotary encoder
Used same procedure as for P013 ultrasonic
See #1486
2018-10-21 15:51:36 +02:00
KurairaitoandGitHub b0032bebdb Update _P067_HX711_Load_Cell.ino
Reduce Ram footprint by using map vector instead of array
2018-10-21 13:41:40 +02:00
KurairaitoandGitHub a525ec34b0 Update _P067_HX711_Load_Cell.ino
Make it compatible when instantiated multiple time
2018-10-21 12:46:26 +02:00
Gijs NoorlanderandGitHub 024ec7b727 Merge pull request #1928 from TD-er/bugfix/reduce_webserver_strings
Reduce webserver strings
2018-10-21 11:12:52 +02:00
ESPEasy release bot 1ff0141e38 automatically updated release notes for mega-20181021 2018-10-21 04:00:16 +02:00
Gijs NoorlanderandGitHub 125db8046b Merge pull request #1927 from TD-er/feature/1870_build_sysino
[#1870] Feature request build date as system info
2018-10-21 01:20:51 +02:00
TD-er 720317c829 [Webserver] Uniform row labels to reduce flash string size 2018-10-21 01:18:43 +02:00
TD-er bbe9005a32 [Webserver] Generate copyText id names
Reduce amount of flash strings to reduce build file size
2018-10-21 00:05:31 +02:00
Gijs NoorlanderandGitHub afa16e80b6 Merge pull request #1923 from TD-er/bugfix/wifi_stability
Various network stability fixes and reduce memory usage when saving settings
2018-10-20 23:09:50 +02:00
Gijs NoorlanderandGitHub 37f5c92aa7 Merge pull request #1926 from TD-er/bugfix/1891_multiple_values
[#1891] Send all values for Generic UDP controller
2018-10-20 23:08:05 +02:00
TD-er 684b89ae4c [#1870] Feature request build date as system info
See #1870
Added:
- %sysbuild_date%
- %sysbuild_time%
2018-10-20 23:05:59 +02:00
TD-er 8c4612c300 [#1891] Send all values for Generic UDP controller
See https://github.com/letscontrolit/ESPEasy/issues/1891#issuecomment-431612051

A copy of the element from the queue was used, not a reference to it.
Therefore increasing the internal counter to send the next value was only executed on the copy, not the element in the list.
2018-10-20 22:34:11 +02:00
Gijs NoorlanderandGitHub 0afab53659 Merge pull request #1925 from TD-er/revert/1924_pull_request
Revert "fix for #1920"
2018-10-20 20:05:22 +02:00
Bartlomiej ZimonandTD-er e6e98345fe Revert "fix for #1920"
This reverts commit 3f84c35014.
2018-10-20 20:03:56 +02:00
Gijs NoorlanderandGitHub 2394e4a75f Merge pull request #1924 from uzi18/patch9
fix for #1920
2018-10-20 18:51:05 +02:00
ESPEasy release bot e1c4691325 automatically updated release notes for mega-20181020 2018-10-20 04:00:16 +02:00
Bartlomiej Zimon 3f84c35014 fix for #1920
fix for out of sync, check both header bytes
proper check for abnormal state
2018-10-19 22:40:11 +00:00
TD-er bc0bafa96b [#1910] Notification settings extra strings on stack
See #1910
Added a lot more of these changes and changed the input fields of the Notification Settings to be the same as all input fields in other places.
2018-10-20 00:20:58 +02:00
TD-er 4e7e66822d [Rules] Early exit on rules processing when rules not enabled 2018-10-20 00:20:58 +02:00
TD-er 6f226e6d65 Proper look for connection.
See discussion here: https://github.com/esp8266/Arduino/issues/5257
2018-10-20 00:20:58 +02:00
TD-er a50a035238 [stack usage] Allocate temp char array on heap for parsing arguments
These char arrays were rather large, so allocate on heap instead of char[240] on the stack.
Also re-order elements in not stored structs to reduce memory usage
2018-10-20 00:20:58 +02:00
Gijs NoorlanderandGitHub d560c635f5 Merge pull request #1922 from kpalczewski/mega
add support for 64bit IR codes.
2018-10-20 00:05:31 +02:00
kpalczewski f8db0c0a05 add support for 64bit IR codes. 2018-10-19 22:34:04 +02:00
Gijs NoorlanderandGitHub 79616022df Merge pull request #1919 from giig1967g/new/statusvariables
added 4 variables to verify the system status
2018-10-19 22:15:26 +02:00
Gijs NoorlanderandGitHub 559d294607 Merge branch 'mega' into new/statusvariables 2018-10-19 21:36:45 +02:00
Gijs NoorlanderandGitHub d2588c5e01 Merge pull request #1848 from giig1967g/new/plugin_pcf
Added [plugin#pcfgpio#pinstate#xx] and [plugin#mcpgpio#pinstate#xx]
2018-10-19 21:34:18 +02:00
Gijs NoorlanderandGitHub ba45a51576 Merge pull request #1884 from giig1967g/new/uservars
added Global Variables for rules
2018-10-19 21:33:10 +02:00
Gijs NoorlanderandGitHub 8321caaeed Merge branch 'mega' into new/statusvariables 2018-10-19 21:31:18 +02:00
Gijs NoorlanderandGitHub b35ed7d809 Merge pull request #1880 from giig1967g/doubleclick
DEBOUNCE, DOUBLECLICK and LONGPRESS events for PLUGIN001
2018-10-19 21:23:22 +02:00
Plebs a4583d150e added 4 variables to verify the system status
4 new system variables:
%iswifi% (=1 if wifi is connected)
%isntp% (=1 if NTP has been initialized)
%ismqtt% (=1 if mqtt is connected)
%ismqttimp% (=1 if mqtt import is connected)

Example:
if %isntp%=1 and %systime%>%sunset% do
....
endif
2018-10-18 17:44:05 +02:00
ESPEasy release bot cadc0a885d automatically updated release notes for mega-20181017 2018-10-17 04:00:18 +02:00
Gijs NoorlanderandGitHub 061eb20ae5 Merge pull request #1914 from TD-er/bugfix/reduce_stack
[#1906] Move Notification and Controller settings to heap
2018-10-17 00:40:30 +02:00
TD-er 9a9799eee7 [Stack] Add Free Stack to System Info plugin 2018-10-17 00:04:22 +02:00
Gijs NoorlanderandGitHub f4615bb76b Added checklist to the Issue_template
Lately a lot of new issues were non-descriptive and the same basic questions were to be asked over and over again.
These are now included in this template.
2018-10-16 23:02:33 +02:00
TD-er ecc498c359 [#1906] Allocate ControllerSettings on the heap
See #1906
2018-10-16 12:24:06 +02:00
TD-er e0e10fa6c8 [#1906] Allocate NotificationSettings on the heap
See #1906
2018-10-16 11:47:46 +02:00
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
Plebs 57b5338af6 Update src/StringConverter.ino 2018-10-12 15:00:58 +02:00
Plebs bd035c8f81 optimizations 2018-10-12 12:52:22 +02:00
Plebs ac63d93e1a Update src/_P001_Switch.ino 2018-10-12 12:10:10 +02:00
Plebs 4cff0d0e02 Update src/_P001_Switch.ino 2018-10-12 10:16:54 +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
Plebs e469350ad9 modified command and variable name 2018-10-11 19:23:42 +02:00
Plebs 60536b1a93 some optimizations 2018-10-11 12:23:08 +02:00
Plebs b9e45bd515 fixed length check 2018-10-11 12:16:55 +02:00
Plebs a0992b8383 fixes 2018-10-11 11:50:37 +02:00
Plebs 9315581858 some fixes 2018-10-11 11:37:15 +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
Plebs e4569b26f7 added global variables for rules
Added a new command:
customvar,x,y
that stores in a system variable %customvarx% the value 'y'.

Values are float
2018-10-10 23:19:09 +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
Plebs 36f97d7909 some fixes 2018-10-10 19:24:45 +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 918353461e Update src/_P001_Switch.ino 2018-10-09 16:31:55 +02:00
Plebs e648a20fd0 Update src/_P019_PCF8574.ino 2018-10-09 16:24:13 +02:00
Plebs 1d7ea5e129 Update src/_P001_Switch.ino 2018-10-09 14:58:21 +02:00
Plebs 6ce5240dd7 few changes 2018-10-09 09:34:50 +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
Plebs 3dea091d21 small fixes 2018-10-09 00:20:27 +02:00
Plebs ca13317cbd Update src/_P001_Switch.ino 2018-10-09 00:12:50 +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
Plebs 73cec59bd5 Update src/_P001_Switch.ino 2018-10-08 22:30:02 +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
Plebs 9b457ed77b some code optimizations 2018-10-07 23:12:23 +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
Plebs d3c86b9824 code optimization 2018-10-04 12:07:32 +02:00
Plebs 9621a91d90 added [plugin#mcpgpio#pinstate#xx] 2018-10-04 11:31:49 +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 0b9c550fd7 Added [plugin#pcfgpio#pinstate#xx]
Added [plugin#pcfgpio#pinstate#xx] variable for rules
2018-10-03 22:49:11 +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
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
Gilad Raz 4ec2af24a3 Increased buffer size. Added constants. 2018-09-30 01:10:55 +03:00
TD-er c8ecd8d374 [#1798] Bugfix Sonoff Si7021 stability
See #1798
2018-09-29 22:50:24 +02:00
Plebs 6e7286c6fb split parseTemplate function
simplifying code
2018-09-26 23:40:01 +02:00
Gilad Raz 3fee6794f2 Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into add-raw-ir-reading 2018-09-26 19:03:47 +03:00
Gilad Raz 019e33331b Merge branch 'mega' into add-raw-ir-reading
# Conflicts:
#	src/_P016_IR.ino
#	src/_P035_IRTX.ino
2018-09-26 19:01:50 +03:00
Gilad 8a751a0922 Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into mega 2018-09-25 18:43:37 +03:00
Gilad 450eca8dc5 Fixed warnings... 2018-09-25 03:13:14 +03:00
Gilad ce0bfbf717 Fixed warnings... 2018-09-25 02:30:20 +03:00
Gilad bb316bbd30 Improved IR RAW2 format to support RLE. 2018-09-25 01:42:45 +03:00
Gilad 9bdfab3fa2 Add IR RAW2 Reading / Transmitting - added a safety check. 2018-09-23 15:49:26 +03:00
Gilad 9d446745cc Add IR RAW2 Reading / Transmitting - fixed ambiguity. 2018-09-23 14:38:36 +03:00
Gilad d2ab946d17 Add IR RAW2 Reading / Transmitting. 2018-09-23 12:44:56 +03:00
Gilad fcc71a56b6 Add IR RAW2 Reading / Transmitting. 2018-09-23 12:40:52 +03: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
James TuttonandGitHub 60925778c6 Fixed missing ;
Fixed missing ;
2018-07-30 14:19:56 +01:00
James TuttonandGitHub 15233bf100 Added Special Case to allow Servo to be disabled
Servo Motors by default forcefully hold the desired position.  This consumes power.  In some cases for example to turn a value or open a latch once the position is set  there may be no need to hold the new position.  With this update users can pass a special case value above >9000 which is out of normal range 0-180 or 365. when this value is passed the servo is detached and is free to move.
2018-07-30 14:10:22 +01:00
103 changed files with 7571 additions and 2078 deletions
+23
View File
@@ -1,3 +1,25 @@
### Checklist
- [ ] The title of this issue is "to the point" and descriptive.
- [ ] This issue describes what is happening.
- [ ] This issue describes what components are affected (e.g. name of plugin/controller)
- [ ] This issue describes how to reproduce it.
- [ ] This issue describes when it was introduced (when known) and what version is now showing the problem.
### I have...
- [ ] searched the [issue tracker](https://github.com/letscontrolit/ESPEasy/issues) or the [forum](https://www.letscontrolit.com/forum/viewforum.php?f=1) for a similar issue. (include links when applicable)
- [ ] entered a system description using "Copy info to clipboard" on the sysinfo page. (when possible)
- [ ] entered the full filename of the used version (e.g. `ESP_Easy_mega-20181001_test_ESP8266_4096_VCC.bin` )
- [ ] given a list of active plugins or controllers when applicable.
- [ ] filled out all applicable fields below.
### Steps already tried...
- [ ] Tried a clean install (empty `.bin` files are included in the ZIP)
- [ ] Tested previous/other build (mention which one already tested)
- [ ] Tested on other node to make sure hardware isn't defective.
- [ ] Verified if the problem is limited to a single plugin/controller
### If you self compile, please state this and PLEASE try to ONLY REPORT ISSUES WITH OFFICIAL BUILDS! ###
<!--- If you self compile, please state this and PLEASE try to ONLY REPORT ISSUES WITH OFFICIAL BUILDS! --->
<!--- NOTE: This is not a support forum! For questions and support go here: --->
@@ -5,6 +27,7 @@
<!--- Remove topics that are not applicable to your feature request of issue --->
<!--- Remember to have a "to the point" TITLE --->
### Summarize of the problem/feature request
<!--- Describe the problem or feature request --->
YOUR TEXT GOES HERE
+2
View File
@@ -22,3 +22,5 @@ test/output_export.cpp
.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
+1 -1
View File
@@ -41,4 +41,4 @@ 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
We're also on IRC: #ESPEasy @freenode
Automated builds of the (new) documentation can be found at [ESPEasy.readthedocs.io](https://espeasy.readthedocs.io/en/latest/)
+13 -6
View File
@@ -11,6 +11,8 @@ for ENV in \
normal_ESP8266_4096 \
normal_ESP8285_1024 \
normal_IR_ESP8266_4096 \
normal_core_241_ESP8266_1024 \
normal_core_241_ESP8266_4096 \
test_ESP8266_1024\
test_ESP8266_4096\
test_ESP8266_4096_VCC\
@@ -21,6 +23,7 @@ for ENV in \
dev_ESP8266PUYA_1024\
dev_ESP8266PUYA_1024_VCC\
hard_SONOFF_POW\
hard_SONOFF_POW_R2_4M\
hard_Shelly_1\
esp32dev\
esp32test_1M8_partition;\
@@ -30,16 +33,20 @@ do
BIN=.pioenvs/${ENV}/ESP_Easy_${VERSION}_${ENV}.bin
cp .pioenvs/${ENV}/firmware.bin $BIN
python2 crc2.py $BIN
mv $BIN "dist/ESP_Easy_${VERSION}_${ENV}.bin"
mv $BIN "dist/bin/ESP_Easy_${VERSION}_${ENV}.bin"
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
cp -r lib dist/Source/
cp -r src dist/Source/
cp platformio.ini 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
BIN
View File
Binary file not shown.
+11 -2
View File
@@ -22,6 +22,7 @@ Build type can be: (differ in included plugins)
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.
- normal_core_241 => "Normal" using core 2.4.1, since 2.4.2 has issues with PWM
Chip can be:
- ESP8266 => Most likely option
@@ -54,8 +55,16 @@ To help recover from a bad flash, there are also blank images included.
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
ESP.Easy.Flasher.exe...
... is the new flashing tool for ESP Easy. You need to run it in elevated mode (as admin)
for it to fetch the COM ports correctly. If you want you may save YOUR settings using the
"Save as default settings" button in the lower left corner. If the window is too big or
too small you can experiment with the "Pixels Per Inch" variable in the
..\Settings\Default.ini file. Setting it to =96 is working for most users but the
application will try to find the most optimal value by default. Sometimes it fails to
do that. More information about the tool is found here:
https://github.com/Grovkillen/ESP_Easy_Flasher
Further reading:
For more information, see: https://github.com/letscontrolit/ESPEasy
Or our forum: https://www.letscontrolit.com/forum/
+450
View File
@@ -1,3 +1,453 @@
-------------------------------------------------
Changes in release mega-20181101 (since mega-20181031)
-------------------------------------------------
Release date: Thu Nov 1 04:00:08 CET 2018
Bartłomiej Zimoń (1):
BlynkGet add timeout 1,5s.
Grovkillen (4):
[README] added more info about ESP.Easy.Flasher
[README] fixed some \r\n
[Deploy] "source" folder was named "Source"
[README] removed IRC link... we're not there.
Jimmy Westberg (1):
[Flasher] updated flasher (some minor error)
Plebs (2):
Update src/Misc.ino
Update src/Misc.ino
-------------------------------------------------
Changes in release mega-20181031 (since mega-20181030)
-------------------------------------------------
Release date: Wed Oct 31 04:00:10 CET 2018
Bartłomiej Zimoń (1):
BlynkGet fix loop #1951
Jimmy Westberg (7):
[Flasher] Added ESP.Easy.Flasher.exe
[Deploy] Added sub folder for BIN files
[Flasher] Updated to 0.02.001
[Deploy] small letters are the standard
[Flasher] Updated to 0.02.002
[Path] folder rename 1
[Path] folder rename 2
TD-er (1):
[OTA] Add OTA info and disable when not enough space
-------------------------------------------------
Changes in release mega-20181030 (since mega-20181029)
-------------------------------------------------
Release date: Tue Oct 30 04:00:07 CET 2018
James Tutton (2):
Added Special Case to allow Servo to be disabled
Fixed missing ;
TD-er (1):
[Core 2.4.2] Set core 2.4.2 as default platform
svmac (1):
P022_PCA9685 Enhancements
-------------------------------------------------
Changes in release mega-20181029 (since mega-20181028)
-------------------------------------------------
Release date: Mon Oct 29 04:00:08 CET 2018
Bartlomiej Zimon (2):
Dallas: add ScratchPad to debug log - OK suffix means data integrity ok (no communication problems)
Dallas: use reset timming from OneWire lib
TD-er (2):
[NTP] Improve NTP accuracy
[NTP] Change log level for NTP success to INFO
-------------------------------------------------
Changes in release mega-20181028 (since mega-20181027)
-------------------------------------------------
Release date: Sun Oct 28 04:00:09 CET 2018
Saverio Cisternino (5):
[CRON] Replace timeStruct with standard tm
[CRON] Plugin
[CRON] Add event PLUGIN_TIME_CHANGE
[CRON] Intercept event PLUGIN_TIME_CHANGE
[CRON] Fixed Daily flash write rate exceeded!
TD-er (2):
[Cron] Rename plugin name to match naming convention
[Eastron] Make configurable and allow for multiple values read (#1684)
-------------------------------------------------
Changes in release mega-20181027 (since mega-20181026)
-------------------------------------------------
Release date: Sat Oct 27 04:00:12 CEST 2018
Bartlomiej Zimon (1):
CSE7766 - fix stats plus add buffer fill level
Gilad (7):
Add IR RAW2 Reading / Transmitting.
Add IR RAW2 Reading / Transmitting.
Add IR RAW2 Reading / Transmitting - fixed ambiguity.
Add IR RAW2 Reading / Transmitting - added a safety check.
Improved IR RAW2 format to support RLE.
Fixed warnings...
Fixed warnings...
Gilad Raz (1):
Increased buffer size. Added constants.
Plebs (4):
doubleclick and longpress for PCF and MCP
fixed a display bug
final fixes
fixed as requested
-------------------------------------------------
Changes in release mega-20181026 (since mega-20181025)
-------------------------------------------------
Release date: Fri Oct 26 04:00:22 CEST 2018
Bartlomiej Zimon (7):
CSE7766 uses 8E1 serial connection
Ser2Net catch serial data only if plugin used
CSE7766 add online checksumming, autosyncing without data loos
move to PLUGIN_TEN_PER_SECOND update comments
move to PLUGIN_SERIAL_IN from PLUGIN_TEN_PER_SECOND - serial commandline uses our data
optimization - remove double checksum check
CSE7766 - add some stats to debug log
Saverio Cisternino (1):
Fixed plugin timer processing do not start, when call setPluginTaskTimer in PLUGIN_TIMER_IN
TD-er (1):
[C009] Reduce memory usage for C009 controller.
-------------------------------------------------
Changes in release mega-20181025 (since mega-20181023)
-------------------------------------------------
Release date: Thu Oct 25 04:00:13 CEST 2018
Saverio Cisternino (1):
Added auto redirect to home after run reboot command from web page.
TD-er (5):
[WDT] Change yield() to delay(0)
[Build] Add Sonoff POW R2 (4MB)
[Sonoff] Add pre-defined switch and relay + rules on factory reset
[Info] Remove MD5 check fail on self-built images
[Travis] Use variables even when no predefined plugins are added.
-------------------------------------------------
Changes in release mega-20181023 (since mega-20181022)
-------------------------------------------------
Release date: Tue Oct 23 04:00:14 CEST 2018
Kurairaito (2):
Update _P067_HX711_Load_Cell.ino
Update _P067_HX711_Load_Cell.ino
TD-er (6):
[#1486] Allow multiple instances of rotary encoder
[JavaScript] Fix update values on Devices tab
[JavaScript] Cleanup some code duplication
[PubSubClient] Add delay when waiting for data and flush on disconnect
[WiFi timeout] Set default to 1000 msec again
[Docs] Add some WiFi related information to docs
-------------------------------------------------
Changes in release mega-20181022 (since mega-20181021)
-------------------------------------------------
Release date: Mon Oct 22 04:00:15 CEST 2018
TD-er (2):
[Webserver] Generate copyText id names
[Webserver] Uniform row labels to reduce flash string size
-------------------------------------------------
Changes in release mega-20181021 (since mega-20181020)
-------------------------------------------------
Release date: Sun Oct 21 04:00:16 CEST 2018
Bartlomiej Zimon (2):
fix for #1920 fix for out of sync, check both header bytes proper check for abnormal state
Revert "fix for #1920"
TD-er (6):
[stack usage] Allocate temp char array on heap for parsing arguments
Proper look for connection.
[Rules] Early exit on rules processing when rules not enabled
[#1910] Notification settings extra strings on stack
[#1891] Send all values for Generic UDP controller
[#1870] Feature request build date as system info
-------------------------------------------------
Changes in release mega-20181020 (since mega-20181017)
-------------------------------------------------
Release date: Sat Oct 20 04:00:16 CEST 2018
Plebs (22):
Added [plugin#pcfgpio#pinstate#xx]
added [plugin#mcpgpio#pinstate#xx]
code optimization
some code optimizations
Update src/_P001_Switch.ino
Update src/_P001_Switch.ino
small fixes
few changes
Update src/_P001_Switch.ino
Update src/_P001_Switch.ino
some fixes
added global variables for rules
some fixes
fixes
fixed length check
some optimizations
modified command and variable name
Update src/_P001_Switch.ino
Update src/_P001_Switch.ino
optimizations
Update src/StringConverter.ino
added 4 variables to verify the system status
kpalczewski (1):
add support for 64bit IR codes.
-------------------------------------------------
Changes in release mega-20181017 (since mega-20181016)
-------------------------------------------------
Release date: Wed Oct 17 04:00:18 CEST 2018
Gijs Noorlander (1):
Added checklist to the Issue_template
TD-er (3):
[#1906] Allocate NotificationSettings on the heap
[#1906] Allocate ControllerSettings on the heap
[Stack] Add Free Stack to System Info plugin
-------------------------------------------------
Changes in release mega-20181016 (since mega-20181015)
-------------------------------------------------
Release date: Tue Oct 16 04:00:06 CEST 2018
Plebs (1):
Fix for togglegpio without gpio initialization
TD-er (1):
[Revert] Safe_strncpy cannot be used as generic strncpy replacement
-------------------------------------------------
Changes in release mega-20181015 (since mega-20181014)
-------------------------------------------------
Release date: Mon Oct 15 04:00:10 CEST 2018
Plebs (2):
fix for bug #1864
Update src/_P023_OLED.ino
TD-er (1):
[safe strncpy] Add warning when entered string is too large
-------------------------------------------------
Changes in release mega-20181014 (since mega-20181011)
-------------------------------------------------
Release date: Sun Oct 14 04:00:13 CEST 2018
Gijs Noorlander (1):
[#1799] Add SSID to main page
TD-er (5):
[#1891] Generic UPD controller - send only first value from sensor
[Controller] Fix send only first value
Time#Initialized event in rules before %syshour% is set
[UX] Warn user for incomplete network IP settings
[#1895] Incorrect number bytes read I2C_read32_reg
-------------------------------------------------
Changes in release mega-20181011 (since mega-20181010)
-------------------------------------------------
Release date: Thu Oct 11 04:00:24 CEST 2018
TD-er (5):
[C009] Reduce memory usage of FHEM HTTP delay queue
[Controller] Set default timeout to 300 msec (was 100 msec)
Make sure to load ExtraTaskSettings when needed
[Travis Doc] Remove building documentation by Travis
[WebLog] Disable weblog when it is not being read
-------------------------------------------------
Changes in release mega-20181010 (since mega-20181009)
-------------------------------------------------
Release date: Wed Oct 10 04:00:09 CEST 2018
Plebs (1):
Update src/_P019_PCF8574.ino
-------------------------------------------------
Changes in release mega-20181009 (since mega-20181008)
-------------------------------------------------
Release date: Tue Oct 9 04:00:18 CEST 2018
TD-er (7):
[Docs] Add favicon to documentation
[Docs] Add Travis check for documentation and stop build for docs only
[Docs] Run Travic documentation check before compiling project
[Docs] Disable travis_terminate for now
[JSON] Replace \n and \r in text fields
[LWIP1.4] Move back to LWIP1.4
[StringConverter] Some optimisations to reduce memory allocation
-------------------------------------------------
Changes in release mega-20181008 (since mega-20181007)
-------------------------------------------------
Release date: Mon Oct 8 04:00:09 CEST 2018
Gijs Noorlander (1):
Added link to ReadTheDocs
Grovkillen (1):
[Sphinx] added extension "sphinx.ext.imgconverter"
TD-er (3):
[Sphinx] Initial setup for Sphinx documentation
[Docs] First ESPEasy documentation (Controllers)
[Docs] Added some directory structure and template
-------------------------------------------------
Changes in release mega-20181007 (since mega-20181006)
-------------------------------------------------
Release date: Sun Oct 7 04:00:09 CEST 2018
Max (2):
Force slash to be converted in htmlEscape
Fix XSS-Vulnerabilites on wifi configuration pages
Plebs (3):
Add three new TOGGLE commands
fixed float to int conversion
added "mcpgpiotoggle" command
TD-er (3):
[ESP32] Allow Wrover dev kit to be used for PIO debug
[Stack] Move Arduino 'cont' stack into user RAM
[SPIFFS] Make sure to close open files on SPIFFS
-------------------------------------------------
Changes in release mega-20181006 (since mega-20181004)
-------------------------------------------------
Release date: Sat Oct 6 04:00:13 CEST 2018
TD-er (3):
[Stack] Increase stack to 5k and reduce stack allocations in rules
[WiFi] Add delay to connection attempts in ControllerSettings
[Build flags] Remove NO_EXTRA_4K_HEAP
-------------------------------------------------
Changes in release mega-20181004 (since mega-20181003)
-------------------------------------------------
Release date: Thu Oct 4 04:00:08 CEST 2018
Bartlomiej Zimon (1):
- partial fix for #1723
JojoS (1):
added error checking and logging
JojoS62 (5):
added float calculation for time to distance conversion
added more settings: unit, filtertype and size
max distance depends on measurement unit
code clean up
fixed 'unused variable' warning
TD-er (3):
[sendHttp] #1830 Set timeout and early exit on timeout reached
[WiFiClient] Set timeout and make it configurable for controllers
[Core 2.4.1] Move back to core 2.4.1 from 2.4.2
Thomas (1):
Update _P075_Nextion.ino (#1)
-------------------------------------------------
Changes in release mega-20181003 (since mega-20181002)
-------------------------------------------------
Release date: Wed Oct 3 04:00:18 CEST 2018
David Conran (2):
Acknowledge credit & origin of code used in IRTX
Acknowledge credit & origin of code used in _P016IR.ino
-------------------------------------------------
Changes in release mega-20181002 (since mega-20181001)
-------------------------------------------------
Release date: Tue Oct 2 04:00:29 CEST 2018
Plebs (1):
split parseTemplate function
TD-er (4):
[#1798] Bugfix Sonoff Si7021 stability
[ESP32] Add default partition table for reference
[ESP32] Update to PIO espressif32@1.4.0
[Stack] Report stack usage on ESP32 and increase stack for ESP8266normal
-------------------------------------------------
Changes in release mega-20181001 (since mega-20180930)
-------------------------------------------------
View File
View File
View File
+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
*****
+25
View File
@@ -0,0 +1,25 @@
WiFi
****
WiFi disconnect reasons
=======================
Beacon timeout (200)
--------------------
For more information on the Beacon frame, see `Wikipedia <https://en.wikipedia.org/wiki/Beacon_frame>`_.
In short, the access point sends periodically a packet containing information about the network.
This interval is typically 100 TU (102.4 msec).
The ESP module is trying to listen to this beacon every time, but for a number of reasons it may
miss a beacon frame.
The timeout is longer than 100 TU, so it must miss a number of these beacon frames
to report a beacon timeout.
Reasons to miss such a beacon frame are:
- too busy processing other blocking tasks (very likely)
- access point not sending a beacon due to high traffic demands of others (depending on brand/model/settings)
- RF disturbances (not likely given how often this occurs)
- clock drift (not really likely)
So the "beacon timeout" is happening every now and then on the ESP nodes and ESPeasy will try to reconnect.
+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
+39
View File
@@ -0,0 +1,39 @@
.. ESPEasy documentation master file, created by
sphinx-quickstart on Sat Oct 06 22:34:34 2018.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to ESPEasy's documentation!
===================================
.. toctree::
:maxdepth: 2
:caption: Table of Contents
ESPEasy/AboutUs.rst
ESPEasy/FunctionBlocks.rst
Controller/Controller.rst
Plugin/Plugin.rst
Rules/Rules.rst
WiFi/WiFi.rst
.. raw:: latex
\appendix
.. toctree::
:caption: Appendix
Participate/Documentation.rst
Reference/Command.rst
Reference/SystemVariable.rst
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
+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
+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;
};
+7
View File
@@ -45,6 +45,10 @@ User can set the parameters in two ways:
- by editing the [SDM_Config_User.h](https://github.com/reaper7/SDM_Energy_Meter/blob/master/SDM_Config_User.h) file
- by passing values during initialization (section below)
NOTE for Hardware Serial mode: <i>to force the Hardware Serial mode,</br>
user must edit the corresponding entry in [SDM_Config_User.h](https://github.com/reaper7/SDM_Energy_Meter/blob/master/SDM_Config_User.h#L13) file.</br>
adding #define USE_HARDWARESERIAL to the main ino file is not enough.</i>
---
### Initializing: ###
@@ -152,6 +156,9 @@ The most common problems are:
https://github.com/reaper7/SDM_Energy_Meter/issues/17#issue-313606825</br>
https://github.com/reaper7/SDM_Energy_Meter/issues/13#issuecomment-353413146</br>
https://github.com/reaper7/SDM_Energy_Meter/issues/13#issuecomment-353417658</br>
- compilation error for hardware serial mode</br>
https://github.com/reaper7/SDM_Energy_Meter/issues/23</br>
https://github.com/reaper7/SDM_Energy_Meter/issues/24</br>
You can get last error code using function:
```cpp
+6 -2
View File
@@ -90,7 +90,7 @@ float SDM::readVal(uint16_t reg, uint8_t node) {
if (readErr == SDM_ERR_NO_ERROR) { //if no timeout...
if(sdmSer.available() == FRAMESIZE) {
if(sdmSer.available() >= FRAMESIZE) {
for(int n=0; n<FRAMESIZE; n++) {
sdmarr[n] = sdmSer.read();
@@ -119,7 +119,11 @@ float SDM::readVal(uint16_t reg, uint8_t node) {
if (readErr != SDM_ERR_NO_ERROR) { //if error then copy temp error value to global val and increment global error counter
readingerrcode = readErr;
readingerrcount++;
readingerrcount++;
}
while (sdmSer.available() > 0) { //read redundant serial bytes, if any
sdmSer.read();
}
#ifndef USE_HARDWARESERIAL
+9 -1
View File
@@ -136,6 +136,14 @@
#define SDM630_EXPORT_REACTIVE_ENERGY 0x004E //VARh
#define SDM630_TOTAL_SYSTEM_POWER_DEMAND 0x0054 //W
#define SDM630_MAXIMUM_TOTAL_SYSTEM_POWER 0x0056 //W
#define SDM630_PHASE_1_LN_VOLTS_THD 0x00EA //%
#define SDM630_PHASE_2_LN_VOLTS_THD 0x00EC //%
#define SDM630_PHASE_3_LN_VOLTS_THD 0x00EE //%
#define SDM630_AVERAGE_VOLTS_THD 0x00F8 //%
#define SDM630_PHASE_1_CURRENT_THD 0x00F0 //%
#define SDM630_PHASE_2_CURRENT_THD 0x00F2 //%
#define SDM630_PHASE_3_CURRENT_THD 0x00F4 //%
#define SDM630_AVERAGE_CURRENT_THD 0x00FA //%
#define SDM_B_05 0x00 //BYTE 5
#define SDM_B_06 0x02 //BYTE 6
@@ -176,7 +184,7 @@ class SDM {
long _baud = SDM_UART_BAUD;
int _dere_pin = DERE_PIN;
uint16_t readingerrcode = SDM_ERR_NO_ERROR; //4 = timeout; 3 = not enough bytes; 2 = number of bytes OK but bytes b0,b1 or b2 wrong, 1 = crc error
uint16_t readingerrcount = 0; //total errors couter
uint16_t readingerrcount = 0; //total errors couter
uint16_t calculateCRC(uint8_t *array, uint8_t num);
};
#endif //SDM_h
+3
View File
@@ -0,0 +1,3 @@
/build/
/nbproject
/Makefile
+34
View File
@@ -0,0 +1,34 @@
# Copyright 2017, alex at staticlibs.net
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
language: cpp
sudo: false
os:
- linux
- osx
compiler:
- gcc
- clang
script:
- $CC ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c89 -DCRON_TEST_MALLOC -o a.out && ./a.out
- $CXX ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c++11 -DCRON_TEST_MALLOC -o a.out && ./a.out
- $CXX ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c++11 -DCRON_TEST_MALLOC -DCRON_COMPILE_AS_CXX -o a.out && ./a.out
notifications:
email:
on_success: always
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2015, staticlibs.net
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+91
View File
@@ -0,0 +1,91 @@
Cron expression parsing in ANSI C
=================================
[![travis](https://travis-ci.org/staticlibs/ccronexpr.svg?branch=master)](https://travis-ci.org/staticlibs/ccronexpr)
[![appveyor](https://ci.appveyor.com/api/projects/status/github/staticlibs/ccronexpr?svg=true)](https://ci.appveyor.com/project/staticlibs/ccronexpr)
Given a cron expression and a date, you can get the next date which satisfies the cron expression.
Supports cron expressions with `seconds` field. Based on implementation of [CronSequenceGenerator](https://github.com/spring-projects/spring-framework/blob/babbf6e8710ab937cd05ece20270f51490299270/spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java) from Spring Framework.
Compiles and should work on Linux (GCC/Clang), Mac OS (Clang), Windows (MSVC), Android NDK, iOS and possibly on other platforms with `time.h` support.
Supports compilation in C (89) and in C++ modes.
Usage example
-------------
#include "ccronexpr.h"
cron_expr expr;
const char* err = NULL;
memset(&expr, 0, sizeof(expr));
cron_parse_expr("0 */2 1-4 * * *", &expr, &err);
if (err) ... /* invalid expression */
time_t cur = time(NULL);
time_t next = cron_next(&expr, cur);
Compilation and tests run examples
----------------------------------
gcc ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c89 -DCRON_TEST_MALLOC -o a.out && ./a.out
g++ ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c++11 -DCRON_TEST_MALLOC -o a.out && ./a.out
g++ ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c++11 -DCRON_TEST_MALLOC -DCRON_COMPILE_AS_CXX -o a.out && ./a.out
clang ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c89 -DCRON_TEST_MALLOC -o a.out && ./a.out
clang++ ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c++11 -DCRON_TEST_MALLOC -o a.out && ./a.out
clang++ ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c++11 -DCRON_TEST_MALLOC -DCRON_COMPILE_AS_CXX -o a.out && ./a.out
cl ccronexpr.c ccronexpr_test.c /W4 /D_CRT_SECURE_NO_WARNINGS && ccronexpr.exe
Examples of supported expressions
---------------------------------
Expression, input date, next date:
"*/15 * 1-4 * * *", "2012-07-01_09:53:50", "2012-07-02_01:00:00"
"0 */2 1-4 * * *", "2012-07-01_09:00:00", "2012-07-02_01:00:00"
"0 0 7 ? * MON-FRI", "2009-09-26_00:42:55", "2009-09-28_07:00:00"
"0 30 23 30 1/3 ?", "2011-04-30_23:30:00", "2011-07-30_23:30:00"
See more examples in [tests](https://github.com/staticlibs/ccronexpr/blob/a1343bc5a546b13430bd4ac72f3b047ac08f8192/ccronexpr_test.c#L251).
Timezones
---------
This implementation does not support explicit timezones handling. By default all dates are
processed as UTC (GMT) dates without timezone infomation.
To use local dates (current system timezone) instead of GMT compile with `-DCRON_USE_LOCAL_TIME`.
License information
-------------------
This project is released under the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).
Changelog
---------
**2018-05-23**
* merged [#8](https://github.com/staticlibs/ccronexpr/pull/8)
* merged [#9](https://github.com/staticlibs/ccronexpr/pull/9)
* minor cleanups
**2018-01-27**
* merged [#6](https://github.com/staticlibs/ccronexpr/pull/6)
* updated license file (to the one parse-able by github)
**2017-09-24**
* merged [#4](https://github.com/staticlibs/ccronexpr/pull/4)
**2016-06-17**
* use thread-safe versions of `gmtime` and `localtime`
**2015-02-28**
* initial public version
+29
View File
@@ -0,0 +1,29 @@
# Copyright 2017, alex at staticlibs.net
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
image: Visual Studio 2017
configuration: Release
build: off
build_script:
- call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat"
- cl ccronexpr.c ccronexpr_test.c /W4 /D_CRT_SECURE_NO_WARNINGS /Feccronexpr_32.exe
- call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
- cl ccronexpr.c ccronexpr_test.c /W4 /D_CRT_SECURE_NO_WARNINGS /Feccronexpr_64.exe
test_script:
- ccronexpr_32.exe
- ccronexpr_64.exe
File diff suppressed because it is too large Load Diff
+94
View File
@@ -0,0 +1,94 @@
/*
* Copyright 2015, alex at staticlibs.net
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* File: ccronexpr.h
* Author: alex
*
* Created on February 24, 2015, 9:35 AM
*/
#ifndef CCRONEXPR_H
#define CCRONEXPR_H
#if defined(__cplusplus) && !defined(CRON_COMPILE_AS_CXX)
extern "C" {
#endif
#ifndef ANDROID
#include <time.h>
#else /* ANDROID */
#include <time64.h>
#endif /* ANDROID */
#include <stdint.h> /*added for use if uint*_t data types*/
/**
* Parsed cron expression
*/
typedef struct cron_expr_t{
uint8_t seconds[8];
uint8_t minutes[8];
uint8_t hours[3];
uint8_t days_of_week[1];
uint8_t days_of_month[4];
uint8_t months[2];
} cron_expr;
#define CRON_INVALID_INSTANT ((time_t) -1)
/**
* Parses specified cron expression.
*
* @param expression cron expression as nul-terminated string,
* should be no longer that 256 bytes
* @param pointer to cron expression structure, it's client code responsibility
* to free/destroy it afterwards
* @param error output error message, will be set to string literal
* error message in case of error. Will be set to NULL on success.
* The error message should NOT be freed by client.
*/
void cron_parse_expr(const char* expression, cron_expr* target, const char** error);
/**
* Uses the specified expression to calculate the next 'fire' date after
* the specified date. All dates are processed as UTC (GMT) dates
* without timezones information. To use local dates (current system timezone)
* instead of GMT compile with '-DCRON_USE_LOCAL_TIME'
*
* @param expr parsed cron expression to use in next date calculation
* @param date start date to start calculation from
* @return next 'fire' date in case of success, '((time_t) -1)' in case of error.
*/
time_t cron_next(cron_expr* expr, time_t date);
/**
* Uses the specified expression to calculate the previous 'fire' date after
* the specified date. All dates are processed as UTC (GMT) dates
* without timezones information. To use local dates (current system timezone)
* instead of GMT compile with '-DCRON_USE_LOCAL_TIME'
*
* @param expr parsed cron expression to use in previous date calculation
* @param date start date to start calculation from
* @return previous 'fire' date in case of success, '((time_t) -1)' in case of error.
*/
time_t cron_prev(cron_expr* expr, time_t date);
#if defined(__cplusplus) && !defined(CRON_COMPILE_AS_CXX)
} /* extern "C"*/
#endif
#endif /* CCRONEXPR_H */
+6 -2
View File
@@ -209,6 +209,7 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass
boolean PubSubClient::readByte(uint8_t * result) {
uint32_t previousMillis = millis();
while(!_client->available()) {
delay(1); // Prevent watchdog crashes
uint32_t currentMillis = millis();
if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){
return false;
@@ -522,9 +523,12 @@ boolean PubSubClient::unsubscribe(const char* topic) {
void PubSubClient::disconnect() {
buffer[0] = MQTTDISCONNECT;
buffer[1] = 0;
_client->write(buffer,2);
if (_client != NULL) {
_client->write(buffer,2);
_client->flush();
_client->stop();
}
_state = MQTT_DISCONNECTED;
_client->stop();
lastInActivity = lastOutActivity = millis();
}
+104 -52
View File
@@ -73,11 +73,14 @@ 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_1_3_0.platform}
platform = ${core_esp32_1_4_0.platform}
build_unflags = -Wall
build_flags = -D BUILD_GIT='"${sysenv.TRAVIS_TAG}"'
lib_ignore = AS_BH1750, ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266HTTPUpdateServer, ESP8266mDNS, IRremoteESP8266, ESPEasy_ESP8266Ping, SerialSensors, SDM
@@ -103,7 +106,10 @@ monitor_speed = 115200
; -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.
; 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
@@ -111,7 +117,7 @@ build_unflags =
build_flags = -D BUILD_GIT='"${sysenv.TRAVIS_TAG}"'
-D NDEBUG
-lstdc++ -lsupc++
-DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
-DVTABLES_IN_FLASH
@@ -125,18 +131,17 @@ board = esp12e
platform = ${core_2_4_2.platform}
monitor_speed = 115200
[normal]
platform = ${common.platform}
build_flags = -DNO_EXTRA_4K_HEAP
build_flags =
[testing]
platform = ${core_2_4_2.platform}
build_flags = -DNO_EXTRA_4K_HEAP -DPLUGIN_BUILD_TESTING
build_flags = -DPLUGIN_BUILD_TESTING
[dev]
platform = ${core_2_4_2.platform}
build_flags = -DNO_EXTRA_4K_HEAP -DPLUGIN_BUILD_DEV
build_flags = -DPLUGIN_BUILD_DEV
[ir]
lib_ignore = ESP32_ping, ESP32WebServer
@@ -157,24 +162,6 @@ board_build.f_cpu = ${esp8266_1M.board_build.f_cpu}
build_unflags = ${esp8266_1M.build_unflags}
build_flags = ${esp8266_1M.build_flags} -DESP8285
[Sonoff]
board = esp01_1m
board_upload.maximum_size = ${esp8266_1M.board_upload.maximum_size}
board_build.flash_mode = ${esp8266_1M.board_build.flash_mode}
board_build.f_cpu = ${esp8266_1M.board_build.f_cpu}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.1m128.ld
platform = ${core_2_4_2.platform}
[Sonoff_8285]
board_upload.maximum_size = ${esp8285_1M.board_upload.maximum_size}
board_build.flash_mode = ${esp8285_1M.board_build.flash_mode}
board_build.f_cpu = ${esp8285_1M.board_build.f_cpu}
board = ${esp8285_1M.board}
build_unflags = ${esp8285_1M.build_unflags}
build_flags = ${esp8285_1M.build_flags}
platform = ${core_2_4_2.platform}
[espWroom2M]
board_build.flash_mode = dout
board_build.f_cpu = ${common.board_build.f_cpu}
@@ -190,15 +177,64 @@ build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.2m.ld
[esp8266_4M]
board_build.flash_mode = dio
board_upload.maximum_size = 900000
board_build.f_cpu = ${common.board_build.f_cpu}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.4m1m.ld
[Sonoff]
board = esp01_1m
board_upload.maximum_size = ${esp8266_1M.board_upload.maximum_size}
board_build.flash_mode = ${esp8266_1M.board_build.flash_mode}
board_build.f_cpu = ${esp8266_1M.board_build.f_cpu}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.1m128.ld
platform = ${core_2_4_1.platform}
[Sonoff_4M]
board = esp12e
board_upload.maximum_size = ${esp8266_4M.board_upload.maximum_size}
board_build.flash_mode = ${esp8266_4M.board_build.flash_mode}
board_build.f_cpu = ${esp8266_4M.board_build.f_cpu}
build_unflags = ${esp8266_4M.build_unflags}
build_flags = ${esp8266_4M.build_flags}
platform = ${common.platform}
[Sonoff_8285]
board_upload.maximum_size = ${esp8285_1M.board_upload.maximum_size}
board_build.flash_mode = ${esp8285_1M.board_build.flash_mode}
board_build.f_cpu = ${esp8285_1M.board_build.f_cpu}
board = ${esp8285_1M.board}
build_unflags = ${esp8285_1M.build_unflags}
build_flags = ${esp8285_1M.build_flags}
platform = ${core_2_4_1.platform}
;;; ESP32 pre-alpha test build ; ****; ****; ****; ****; ****; ****; ****; ****; ****;;;;;
; Very limited build for ESP32, to start testing regular building for ESP32. ;
; Will probably not work, not tested and guaranteed to take a few hours time of some ;
; still trying to build the version without reading this warning. ;
; *********************************************************************
[env:esp-wrover-kit_test_1M8_partition]
platform = ${core_esp32.platform}
board = esp-wrover-kit
build_unflags = ${core_esp32.build_unflags}
build_flags = ${core_esp32.build_flags} -DPLUGIN_SET_TEST_ESP32
lib_deps = ${core_esp32.lib_deps}
lib_ignore = ${core_esp32.lib_ignore}
lib_ldf_mode = ${common.lib_ldf_mode}
lib_archive = ${common.lib_archive}
framework = ${common.framework}
upload_speed = ${common.upload_speed}
upload_protocol = ftdi
debug_tool = ftdi
monitor_speed = ${common.monitor_speed}
board_build.partitions = esp32_partition_app1810k_spiffs316k.csv
board_upload.maximum_size = 1900544
debug_extra_cmds = break Misc.ino:3011
[env:esp32dev]
platform = ${core_esp32.platform}
board = esp32dev
@@ -249,6 +285,22 @@ board_build.flash_mode = ${esp8266_1M.board_build.flash_mode}
build_unflags = ${esp8266_1M.build_unflags}
build_flags = ${esp8266_1M.build_flags} ${normal.build_flags}
[env:normal_core_241_ESP8266_1024]
platform = ${core_2_4_1.platform}
lib_deps = ${common.lib_deps}
lib_ignore = ${common.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 = ${common.board}
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} ${normal.build_flags}
; NORMAL: 1024k for esp8285 ----------------------
[env:normal_ESP8285_1024]
platform = ${common.platform}
@@ -298,6 +350,21 @@ board_build.flash_mode = ${esp8266_4M.board_build.flash_mode}
build_unflags = ${esp8266_4M.build_unflags}
build_flags = ${esp8266_4M.build_flags} ${normal.build_flags}
[env:normal_core_241_ESP8266_4096]
platform = ${core_2_4_1.platform}
lib_deps = ${common.lib_deps}
lib_ignore = ${common.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 = ${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} ${normal.build_flags}
; NORMAL IR: 4096k version --------------------------
; Build including IR libraries
@@ -540,24 +607,9 @@ build_flags = ${esp8266_1M.build_flags} ${dev.build_flags} -D FLAS
;build_unflags = ${Sonoff.build_unflags}
;build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_BASIC
; ITEAD / SONOFF TH10 version -------------------
;[env:hard_SONOFF_TH10]
;upload_speed = ${common.upload_speed}
;monitor_speed = ${common.monitor_speed}
;framework = ${common.framework}
;platform = ${Sonoff.platform}
;lib_deps = ${common.lib_deps}
;lib_ignore = ${common.lib_ignore}
;lib_ldf_mode = ${common.lib_ldf_mode}
;lib_archive = ${common.lib_archive}
;board_build.f_cpu = ${Sonoff.board_build.f_cpu}
;board_build.flash_mode = ${Sonoff.board_build.flash_mode}
;board = ${Sonoff.board}
;build_unflags = ${Sonoff.build_unflags}
;build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_TH10
; ITEAD / SONOFF TH16 version -------------------
;[env:hard_SONOFF_TH16]
; ITEAD / SONOFF TH10/TH16 version -------------------
;[env:hard_SONOFF_TH1x]
;upload_speed = ${common.upload_speed}
;monitor_speed = ${common.monitor_speed}
;framework = ${common.framework}
@@ -570,7 +622,7 @@ build_flags = ${esp8266_1M.build_flags} ${dev.build_flags} -D FLAS
;board_build.flash_mode = ${Sonoff.board_build.flash_mode}
;board = ${Sonoff.board}
;build_unflags = ${Sonoff.build_unflags}
;build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_TH16
;build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_TH1x
; ITEAD / SONOFF POW version --------------------
; Sonoff Pow (ESP8266 - HLW8012)
@@ -595,26 +647,26 @@ board = ${Sonoff.board}
build_unflags = ${Sonoff.build_unflags}
build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_POW
; Sonoff Pow R2 (ESP8266 - CSE7766)
; Sonoff Pow R2 (ESP8266 4M flash - CSE7766)
; GPIO00 Button
; GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor
; GPIO03 Serial TXD
; GPIO12 Red Led and Relay (0 = Off, 1 = On)
; GPIO13 Blue Led (0 = On, 1 = Off)
[env:hard_SONOFF_POW_R2]
[env:hard_SONOFF_POW_R2_4M]
upload_speed = ${common.upload_speed}
monitor_speed = ${common.monitor_speed}
framework = ${common.framework}
platform = ${Sonoff.platform}
platform = ${Sonoff_4M.platform}
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
lib_ldf_mode = ${common.lib_ldf_mode}
lib_archive = ${common.lib_archive}
board_build.f_cpu = ${Sonoff.board_build.f_cpu}
board_build.flash_mode = ${Sonoff.board_build.flash_mode}
board = ${Sonoff.board}
build_unflags = ${Sonoff.build_unflags}
build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_POW_R2
board_build.f_cpu = ${Sonoff_4M.board_build.f_cpu}
board_build.flash_mode = ${Sonoff_4M.board_build.flash_mode}
board = ${Sonoff_4M.board}
build_unflags = ${Sonoff_4M.build_unflags}
build_flags = ${Sonoff_4M.build_flags} -D PLUGIN_SET_SONOFF_POW_R2
; ITEAD / SONOFF S20 version --------------------
;[env:hard_SONOFF_S20]
@@ -630,7 +682,7 @@ build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_POW_R2
;board_build.flash_mode = ${Sonoff.board_build.flash_mode}
;board = ${Sonoff.board}
;build_unflags = ${Sonoff.build_unflags}
;build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_S20
;build_flags = ${Sonoff.build_flags} -D PLUGIN_SET_SONOFF_S2x
; ITEAD / SONOFF 4CH version --------------------
;[env:hard_SONOFF_4CH]
+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
+5 -3
View File
@@ -79,6 +79,7 @@ String doExecuteCommand(const char * cmd, struct EventStruct *event, const char*
break;
}
case 'l': {
COMMAND_CASE("let" , Command_Rules_Let); // Rules.h
COMMAND_CASE("load" , Command_Settings_Load); // Settings.h
COMMAND_CASE("logentry" , Command_logentry); // Diagnostic.h
COMMAND_CASE("lowmem" , Command_Lowmem); // Diagnostic.h
@@ -131,7 +132,8 @@ String doExecuteCommand(const char * cmd, struct EventStruct *event, const char*
COMMAND_CASE("taskclear" , Command_Task_Clear); // Tasks.h
COMMAND_CASE("taskclearall" , Command_Task_ClearAll); // Tasks.h
COMMAND_CASE("taskrun" , Command_Task_Run); // Tasks.h
COMMAND_CASE("taskvalueset" , Command_Task_ValueSet); // Tasks.h
COMMAND_CASE("taskvalueset" , Command_Task_ValueSet); // Tasks.h
COMMAND_CASE("taskvaluetoggle" , Command_Task_ValueToggle); // Tasks.h
COMMAND_CASE("taskvaluesetandrun" , Command_Task_ValueSetAndRun); // Tasks.h
COMMAND_CASE("timerpause" , Command_Timer_Pause); // Timers.h
COMMAND_CASE("timerresume" , Command_Timer_Resume); // Timers.h
@@ -225,9 +227,9 @@ void ExecuteCommand(byte source, const char *Line)
if (source == VALUE_SOURCE_WEB_FRONTEND) {
// Must run immediately, to see result in web frontend
String status = doExecuteCommand((char*)&cmd[0], &TempEvent, Line);
yield();
delay(0);
SendStatus(source, status);
yield();
delay(0);
} else {
// Schedule to run async
schedule_command_timer((char*)&cmd[0], &TempEvent, Line);
+1 -1
View File
@@ -33,7 +33,7 @@ String Command_HTTP_SendToHTTP(struct EventStruct *event, const char* Line)
}
String request = do_create_http_request(hostportString, F("GET"), path);
addLog(LOG_LEVEL_DEBUG, request);
send_via_http(F("Command_HTTP_SendToHTTP"), client, request);
send_via_http(F("Command_HTTP_SendToHTTP"), client, request, false);
}
}
return return_command_success();
+11
View File
@@ -34,4 +34,15 @@ String Command_Rules_Events(struct EventStruct *event, const char* Line)
return return_command_success();
}
String Command_Rules_Let(struct EventStruct *event, const char* Line)
{
char TmpStr1[INPUT_COMMAND_SIZE];
if (GetArgv(Line, TmpStr1, 3)) {
float result = 0;
Calculate(TmpStr1, &result);
customFloatVar[event->Par1-1] = result;
}
return return_command_success();
}
#endif // COMMAND_RULES_H
+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];
+10 -6
View File
@@ -132,7 +132,7 @@ void callback(char* c_topic, byte* b_payload, unsigned int length) {
bool MQTTConnect(int controller_idx)
{
++mqtt_reconnect_count;
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(controller_idx, ControllerSettings);
if (!ControllerSettings.checkHostReachable(true))
return false;
@@ -141,6 +141,8 @@ bool MQTTConnect(int controller_idx)
updateMQTTclient_connected();
}
mqtt = WiFiClient(); // workaround see: https://github.com/esp8266/Arduino/issues/4497#issuecomment-373023864
mqtt.setTimeout(ControllerSettings.ClientTimeout);
MQTTclient.setClient(mqtt);
if (ControllerSettings.UseDNS) {
MQTTclient.setServer(ControllerSettings.getHost().c_str(), ControllerSettings.Port);
} else {
@@ -202,10 +204,12 @@ bool MQTTConnect(int controller_idx)
} else {
MQTTresult = MQTTclient.connect(clientid.c_str(), LWTTopic.c_str(), willQos, willRetain, LWTMessageDisconnect.c_str());
}
yield();
delay(0);
if (!MQTTresult) {
addLog(LOG_LEVEL_ERROR, F("MQTT : Failed to connect to broker"));
MQTTclient.disconnect();
updateMQTTclient_connected();
return false;
}
MQTTclient_should_reconnect = false;
@@ -292,9 +296,9 @@ void scheduleNextMQTTdelayQueue() {
void processMQTTdelayQueue() {
START_TIMER;
MQTT_queue_element element;
if (!MQTTDelayHandler.getNext(element)) return;
if (MQTTclient.publish(element._topic.c_str(), element._payload.c_str(), element._retained)) {
MQTT_queue_element* element(MQTTDelayHandler.getNext());
if (element == NULL) return;
if (MQTTclient.publish(element->_topic.c_str(), element->_payload.c_str(), element->_retained)) {
setIntervalTimerOverride(TIMER_MQTT, 10); // Make sure the MQTT is being processed as soon as possible.
MQTTDelayHandler.markProcessed(true);
} else {
@@ -315,7 +319,7 @@ void processMQTTdelayQueue() {
\*********************************************************************************************/
void MQTTStatus(String& status)
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
int enabledMqttController = firstEnabledMQTTController();
if (enabledMqttController >= 0) {
LoadControllerSettings(enabledMqttController, ControllerSettings);
+172 -98
View File
@@ -12,7 +12,7 @@
// You can always change these during runtime and save to eeprom
// After loading firmware, issue a 'reset' command to load the defaults.
// --- Basic Config Settings ------------------------------------------------------------------------
#define DEFAULT_NAME "ESP_Easy" // Enter your device friendly name
#define DEFAULT_NAME "ESP_Easy" // Enter your device friendly name
#define UNIT 0 // Unit Number
#define DEFAULT_DELAY 60 // Sleep Delay in seconds
@@ -79,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
@@ -191,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
@@ -226,6 +228,9 @@
// N.B. Retries without a connection to wifi do not count as retry.
#define CONTROLLER_DELAY_QUEUE_RETRY_MAX 10
#define CONTROLLER_DELAY_QUEUE_RETRY_DFLT 10
// Timeout of the client in msec.
#define CONTROLLER_CLIENTTIMEOUT_MAX 1000
#define CONTROLLER_CLIENTTIMEOUT_DFLT 1000
#define PLUGIN_INIT_ALL 1
@@ -254,6 +259,7 @@
#define PLUGIN_GET_CONFIG 24
#define PLUGIN_UNCONDITIONAL_POLL 25
#define PLUGIN_REQUEST 26
#define PLUGIN_TIME_CHANGE 27
#define CPLUGIN_PROTOCOL_ADD 1
#define CPLUGIN_PROTOCOL_TEMPLATE 2
@@ -287,6 +293,7 @@
#define NPLUGIN_NOT_FOUND 255
#define LOG_LEVEL_NONE 0
#define LOG_LEVEL_ERROR 1
#define LOG_LEVEL_INFO 2
#define LOG_LEVEL_DEBUG 3
@@ -298,7 +305,7 @@
#define CMD_WIFI_DISCONNECT 135
#if defined(PLUGIN_BUILD_TESTING) || defined(PLUGIN_BUILD_DEV)
#define DEVICES_MAX 75
#define DEVICES_MAX 85
#else
#ifdef ESP32
#define DEVICES_MAX 75
@@ -333,6 +340,7 @@
#define RULES_BUFFER_SIZE 64
#define NAME_FORMULA_LENGTH_MAX 40
#define RULES_IF_MAX_NESTING_LEVEL 4
#define CUSTOM_VARS_MAX 16
#define UDP_PACKETSIZE_MAX 2048
@@ -414,6 +422,9 @@ 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);
@@ -508,6 +519,8 @@ bool showSettingsFileLayout = false;
#ifdef FEATURE_MDNS
#include <ESP8266mDNS.h>
#endif
#define SMALLEST_OTA_IMAGE 272*1024 // smallest known 2-step OTA image
#define MAX_SKETCH_SIZE 1044464
#ifdef FEATURE_ARDUINO_OTA
#include <ArduinoOTA.h>
#include <ESP8266mDNS.h>
@@ -591,14 +604,17 @@ bool MQTTclient_should_reconnect = true;
bool MQTTclient_connected = false;
int mqtt_reconnect_count = 0;
//NTP status
bool statusNTPInitialized = false;
// mqtt import status
bool P037_MQTTImport_connected = false;
// udp protocol stuff (syslog, global sync, node info list, ntp time)
WiFiUDP portUDP;
class TimingStats;
#define LOADFILE_STATS 0
#define LOOP_STATS 1
/*********************************************************************************************\
* CRCStruct
\*********************************************************************************************/
@@ -666,6 +682,16 @@ struct SecurityStruct
uint8_t md5[16];
} SecuritySettings;
/*********************************************************************************************\
* Custom Variables for usage in rules and http.
* Syntax: %vX%
* usage:
* let,1,10
* if %v1%=10 do ...
\*********************************************************************************************/
float customFloatVar[CUSTOM_VARS_MAX];
/*********************************************************************************************\
* SettingsStruct
\*********************************************************************************************/
@@ -687,6 +713,10 @@ struct SettingsStruct
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;
@@ -886,13 +916,30 @@ SettingsStruct* SettingsStruct_ptr = new SettingsStruct;
SettingsStruct& Settings = *SettingsStruct_ptr;
*/
/*********************************************************************************************\
* ControllerSettingsStruct
* Analyze SettingsStruct and report inconsistencies
* Not a member function to be able to use the F-macro
\*********************************************************************************************/
bool SettingsCheck(String& error) {
error = "";
if (!Settings.networkSettingsEmpty()) {
if (Settings.IP[0] == 0 || Settings.Gateway[0] == 0 || Settings.Subnet[0] == 0 || Settings.DNS[0] == 0) {
error += F("Error: Either fill all IP settings fields or leave all empty");
}
}
return error.length() == 0;
}
/*********************************************************************************************\
* ControllerSettingsStruct definition
\*********************************************************************************************/
struct ControllerSettingsStruct
{
ControllerSettingsStruct() : UseDNS(false), Port(0),
MinimalTimeBetweenMessages(100), MaxQueueDepth(10), MaxRetry(10), DeleteOldest(false) {
MinimalTimeBetweenMessages(100), MaxQueueDepth(10), MaxRetry(10),
DeleteOldest(false), ClientTimeout(100), MustCheckReply(false) {
for (byte i = 0; i < 4; ++i) {
IP[i] = 0;
}
@@ -916,13 +963,18 @@ struct ControllerSettingsStruct
unsigned int MaxQueueDepth;
unsigned int MaxRetry;
boolean DeleteOldest; // Action to perform when buffer full, delete oldest, or ignore newest.
unsigned int ClientTimeout;
boolean MustCheckReply; // When set to false, a sent message is considered always successful.
void validate() {
if (Port > 65535) Port = 0;
if (MinimalTimeBetweenMessages < 1) MinimalTimeBetweenMessages = 100;
if (MinimalTimeBetweenMessages > 3600000) MinimalTimeBetweenMessages = 100;
if (MaxQueueDepth > 25) MaxQueueDepth = 10;
if (MaxRetry > 10) MaxRetry = 10;
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 {
@@ -946,6 +998,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()) {
@@ -1024,6 +1077,11 @@ private:
};
typedef std::shared_ptr<ControllerSettingsStruct> ControllerSettingsStruct_ptr_type;
#define MakeControllerSettings(T) ControllerSettingsStruct_ptr_type ControllerSettingsStruct_ptr(new ControllerSettingsStruct());\
ControllerSettingsStruct& T = *ControllerSettingsStruct_ptr;
/*********************************************************************************************\
* NotificationSettingsStruct
@@ -1055,6 +1113,11 @@ struct NotificationSettingsStruct
//its safe to extend this struct, up to 4096 bytes, default values in config are 0
};
typedef std::shared_ptr<NotificationSettingsStruct> NotificationSettingsStruct_ptr_type;
#define MakeNotificationSettings(T) NotificationSettingsStruct_ptr_type NotificationSettingsStruct_ptr(new NotificationSettingsStruct());\
NotificationSettingsStruct& T = *NotificationSettingsStruct_ptr;
/*********************************************************************************************\
* ExtraTaskSettingsStruct
\*********************************************************************************************/
@@ -1138,22 +1201,36 @@ struct ExtraTaskSettingsStruct
struct EventStruct
{
EventStruct() :
Data(NULL), idx(0), Par1(0), Par2(0), Par3(0), Par4(0), Par5(0),
Source(0), TaskIndex(TASKS_MAX), ControllerIndex(0), ProtocolIndex(0), NotificationIndex(0),
BaseVarIndex(0), idx(0), sensorType(0), Par1(0), Par2(0), Par3(0), Par4(0), Par5(0),
OriginTaskIndex(0), Data(NULL) {}
BaseVarIndex(0), sensorType(0), OriginTaskIndex(0) {}
EventStruct(const struct EventStruct& event):
Source(event.Source), TaskIndex(event.TaskIndex), ControllerIndex(event.ControllerIndex)
, ProtocolIndex(event.ProtocolIndex), NotificationIndex(event.NotificationIndex)
, BaseVarIndex(event.BaseVarIndex), idx(event.idx), sensorType(event.sensorType)
, Par1(event.Par1), Par2(event.Par2), Par3(event.Par3), Par4(event.Par4), Par5(event.Par5)
, OriginTaskIndex(event.OriginTaskIndex)
, String1(event.String1)
String1(event.String1)
, String2(event.String2)
, String3(event.String3)
, String4(event.String4)
, String5(event.String5)
, Data(event.Data) {}
, Data(event.Data)
, idx(event.idx)
, Par1(event.Par1), Par2(event.Par2), Par3(event.Par3), Par4(event.Par4), Par5(event.Par5)
, Source(event.Source), TaskIndex(event.TaskIndex), ControllerIndex(event.ControllerIndex)
, ProtocolIndex(event.ProtocolIndex), NotificationIndex(event.NotificationIndex)
, BaseVarIndex(event.BaseVarIndex), sensorType(event.sensorType)
, OriginTaskIndex(event.OriginTaskIndex)
{}
String String1;
String String2;
String String3;
String String4;
String String5;
byte *Data;
int idx;
int Par1;
int Par2;
int Par3;
int Par4;
int Par5;
byte Source;
byte TaskIndex; // index position in TaskSettings array, 0-11
byte ControllerIndex; // index position in Settings.Controller, 0-3
@@ -1162,20 +1239,8 @@ struct EventStruct
//Edwin: Not needed, and wasnt used. We can determine the protocol index with getNotificationProtocolIndex(NotificationIndex)
// byte NotificationProtocolIndex; // index position in notification array, depending on which controller plugins are loaded.
byte BaseVarIndex;
int idx;
byte sensorType;
int Par1;
int Par2;
int Par3;
int Par4;
int Par5;
byte OriginTaskIndex;
String String1;
String String2;
String String3;
String String4;
String String5;
byte *Data;
};
@@ -1185,18 +1250,19 @@ struct EventStruct
#define LOG_STRUCT_MESSAGE_SIZE 128
#ifdef ESP32
#define LOG_STRUCT_MESSAGE_LINES 30
#define LOG_BUFFER_EXPIRE 30000 // Time after which a buffered log item is considered expired.
#else
#if defined(PLUGIN_BUILD_TESTING) || defined(PLUGIN_BUILD_DEV)
#define LOG_STRUCT_MESSAGE_LINES 10
#else
#define LOG_STRUCT_MESSAGE_LINES 15
#endif
#define LOG_BUFFER_EXPIRE 5000 // Time after which a buffered log item is considered expired.
#endif
struct LogStruct {
LogStruct() : write_idx(0), read_idx(0) {
LogStruct() : write_idx(0), read_idx(0), lastReadTimeStamp(0) {
for (int i = 0; i < LOG_STRUCT_MESSAGE_LINES; ++i) {
Message[i].reserve(LOG_STRUCT_MESSAGE_SIZE);
timeStamp[i] = 0;
log_level[i] = 0;
}
@@ -1214,6 +1280,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);
}
@@ -1222,6 +1289,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);
@@ -1230,6 +1298,7 @@ struct LogStruct {
}
String get_logjson_formatted(bool& logLinesAvailable, unsigned long& timestamp) {
lastReadTimeStamp = millis();
logLinesAvailable = false;
if (isEmpty()) {
return "";
@@ -1243,31 +1312,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;
@@ -1291,20 +1344,38 @@ struct LogStruct {
return output;
}
void clearExpiredEntries() {
if (isEmpty()) {
return;
}
if (timePassedSince(lastReadTimeStamp) > LOG_BUFFER_EXPIRE) {
// Clear the entire log.
// If web log is the only log active, it will not be checked again until it is read.
for (read_idx = 0; read_idx < LOG_STRUCT_MESSAGE_LINES; ++read_idx) {
Message[read_idx] = String(); // Free also the reserved memory.
timeStamp[read_idx] = 0;
log_level[read_idx] = 0;
}
read_idx = 0;
write_idx = 0;
}
}
int write_idx;
int read_idx;
unsigned long timeStamp[LOG_STRUCT_MESSAGE_LINES];
unsigned long lastReadTimeStamp;
byte log_level[LOG_STRUCT_MESSAGE_LINES];
String Message[LOG_STRUCT_MESSAGE_LINES];
} Logging;
std::deque<char> serialLogBuffer;
unsigned long last_serial_log_emptied = 0;
unsigned long last_serial_log_read = 0;
byte highest_active_log_level = 0;
bool log_to_serial_disabled = false;
bool log_to_serial_disabled_temporary = false;
// Do this in a template to prevent casting to String when not needed.
#define addLog(L,S) if (loglevelActiveFor(L)) { addToLog(L,S); }
@@ -1328,16 +1399,16 @@ struct DeviceStruct
byte Type; // How the device is connected. e.g. DEVICE_TYPE_SINGLE => connected through 1 datapin
byte VType; // Type of value the plugin will return, used only for Domoticz
byte Ports; // Port to use when device has multiple I/O pins (N.B. not used much)
byte ValueCount; // The number of output values of a plugin. The value should match the number of keys PLUGIN_VALUENAME1_xxx
boolean PullUpOption; // Allow to set internal pull-up resistors.
boolean InverseLogicOption; // Allow to invert the boolean state (e.g. a switch)
boolean FormulaOption; // Allow to enter a formula to convert values during read. (not possible with Custom enabled)
boolean Custom;
boolean SendDataOption; // Allow to send data to a controller.
boolean GlobalSyncOption; // No longer used. Was used for ESPeasy values sync between nodes
boolean TimerOption; // Allow to set the "Interval" timer for the plugin.
boolean TimerOptional; // When taskdevice timer is not set and not optional, use default "Interval" delay (Settings.Delay)
boolean DecimalsOnly; // Allow to set the number of decimals (otherwise treated a 0 decimals)
byte ValueCount; // The number of output values of a plugin. The value should match the number of keys PLUGIN_VALUENAME1_xxx
bool PullUpOption : 1; // Allow to set internal pull-up resistors.
bool InverseLogicOption : 1; // Allow to invert the boolean state (e.g. a switch)
bool FormulaOption : 1; // Allow to enter a formula to convert values during read. (not possible with Custom enabled)
bool Custom : 1;
bool SendDataOption : 1; // Allow to send data to a controller.
bool GlobalSyncOption : 1; // No longer used. Was used for ESPeasy values sync between nodes
bool TimerOption : 1; // Allow to set the "Interval" timer for the plugin.
bool TimerOptional : 1; // When taskdevice timer is not set and not optional, use default "Interval" delay (Settings.Delay)
bool DecimalsOnly; // Allow to set the number of decimals (otherwise treated a 0 decimals)
};
typedef std::vector<DeviceStruct> DeviceVector;
DeviceVector Device;
@@ -1402,16 +1473,16 @@ NodesMap Nodes;
struct systemTimerStruct
{
systemTimerStruct() :
timer(0), plugin(0), TaskIndex(-1), Par1(0), Par2(0), Par3(0), Par4(0), Par5(0) {}
timer(0), Par1(0), Par2(0), Par3(0), Par4(0), Par5(0), TaskIndex(-1), plugin(0) {}
unsigned long timer;
byte plugin;
int16_t TaskIndex;
int Par1;
int Par2;
int Par3;
int Par4;
int Par5;
int16_t TaskIndex;
byte plugin;
};
std::map<unsigned long, systemTimerStruct> systemTimers;
@@ -1420,11 +1491,11 @@ std::map<unsigned long, systemTimerStruct> systemTimers;
\*********************************************************************************************/
struct pinStatesStruct
{
pinStatesStruct() : plugin(0), index(0), mode(0), value(0) {}
pinStatesStruct() : value(0), plugin(0), index(0), mode(0) {}
uint16_t value;
byte plugin;
byte index;
byte mode;
uint16_t value;
} pinStates[PINSTATE_TABLE_MAX];
@@ -1484,6 +1555,7 @@ 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;
@@ -1780,31 +1852,32 @@ unsigned long timediff_calls = 0;
unsigned long timediff_cpu_cycles_total = 0;
#define LOADFILE_STATS 0
#define LOOP_STATS 1
#define PLUGIN_CALL_50PS 2
#define PLUGIN_CALL_10PS 3
#define PLUGIN_CALL_10PSU 4
#define PLUGIN_CALL_1PS 5
#define SENSOR_SEND_TASK 6
#define SEND_DATA_STATS 7
#define COMPUTE_FORMULA_STATS 8
#define PROC_SYS_TIMER 9
#define SET_NEW_TIMER 10
#define TIME_DIFF_COMPUTE 11
#define MQTT_DELAY_QUEUE 12
#define C001_DELAY_QUEUE 13
#define C002_DELAY_QUEUE 14
#define C003_DELAY_QUEUE 15
#define C004_DELAY_QUEUE 16
#define C005_DELAY_QUEUE 17
#define C006_DELAY_QUEUE 18
#define C007_DELAY_QUEUE 19
#define C008_DELAY_QUEUE 20
#define C009_DELAY_QUEUE 21
#define C010_DELAY_QUEUE 22
#define C011_DELAY_QUEUE 23
#define C012_DELAY_QUEUE 24
#define C013_DELAY_QUEUE 25
#define SAVEFILE_STATS 1
#define LOOP_STATS 2
#define PLUGIN_CALL_50PS 3
#define PLUGIN_CALL_10PS 4
#define PLUGIN_CALL_10PSU 5
#define PLUGIN_CALL_1PS 6
#define SENSOR_SEND_TASK 7
#define SEND_DATA_STATS 8
#define COMPUTE_FORMULA_STATS 9
#define PROC_SYS_TIMER 10
#define SET_NEW_TIMER 11
#define TIME_DIFF_COMPUTE 12
#define MQTT_DELAY_QUEUE 13
#define C001_DELAY_QUEUE 14
#define C002_DELAY_QUEUE 15
#define C003_DELAY_QUEUE 16
#define C004_DELAY_QUEUE 17
#define C005_DELAY_QUEUE 18
#define C006_DELAY_QUEUE 19
#define C007_DELAY_QUEUE 20
#define C008_DELAY_QUEUE 21
#define C009_DELAY_QUEUE 22
#define C010_DELAY_QUEUE 23
#define C011_DELAY_QUEUE 24
#define C012_DELAY_QUEUE 25
#define C013_DELAY_QUEUE 26
@@ -1813,13 +1886,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 ");
+18 -11
View File
@@ -106,12 +106,13 @@ 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);
#ifndef ESP32
lowestFreeStack = getFreeStackWatermark();
#endif
lowestFreeStack = getFreeStackWatermark();
lowestRAM = FreeMem();
Plugin_id.resize(PLUGIN_MAX);
@@ -531,6 +532,7 @@ void loop()
}
// Flush outstanding MQTT messages
runPeriodicalMQTT();
flushAndDisconnectAllClients();
deepSleep(Settings.Delay);
//deepsleep will never return, its a special kind of reboot
@@ -546,6 +548,14 @@ bool checkConnectionsEstablished() {
return true;
}
void flushAndDisconnectAllClients() {
if (MQTTclient.connected()) {
MQTTclient.disconnect();
updateMQTTclient_connected();
}
/// FIXME TD-er: add call to all controllers (delay queue) to flush all data.
}
void runPeriodicalMQTT() {
// MQTT_KEEPALIVE = 15 seconds.
if (!WiFiConnected(10)) {
@@ -838,13 +848,10 @@ void SensorSendTask(byte TaskIndex)
{
if (ExtraTaskSettings.TaskDeviceFormula[varNr][0] != 0)
{
String spreValue = String(preValue[varNr]);
String formula = ExtraTaskSettings.TaskDeviceFormula[varNr];
float value = UserVar[varIndex + varNr];
formula.replace(F("%pvalue%"), String(preValue[varNr]));
formula.replace(F("%value%"), String(UserVar[varIndex + varNr]));
float result = 0;
String svalue = String(value);
formula.replace(F("%pvalue%"), spreValue);
formula.replace(F("%value%"), svalue);
byte error = Calculate(formula.c_str(), &result);
if (error == 0)
UserVar[varIndex + varNr] = result;
@@ -865,7 +872,7 @@ void backgroundtasks()
{
//checkRAM(F("backgroundtasks"));
//always start with a yield
yield();
delay(0);
/*
// Remove this watchdog feed for now.
// See https://github.com/letscontrolit/ESPEasy/issues/1722#issuecomment-419659193
@@ -908,13 +915,13 @@ void backgroundtasks()
//once OTA is triggered, only handle that and dont do other stuff. (otherwise it fails)
while (ArduinoOTAtriggered)
{
yield();
delay(0);
ArduinoOTA.handle();
}
#endif
yield();
delay(0);
statusLED(false);
+87 -29
View File
@@ -41,6 +41,19 @@ String flashGuard()
//use this in function that can return an error string. it automaticly returns with an error string if there where too many flash writes.
#define FLASH_GUARD() { String flashErr=flashGuard(); if (flashErr.length()) return(flashErr); }
String appendLineToFile(const String& fname, const String& line) {
fs::File f = SPIFFS.open(fname, "a+");
SPIFFS_CHECK(f, fname.c_str());
const size_t lineLength = line.length();
for (size_t i = 0; i < lineLength; ++i) {
SPIFFS_CHECK(f.write(line[i]), fname.c_str());
}
f.close();
return "";
}
/********************************************************************************************\
Fix stuff to clear out differences between releases
\*********************************************************************************************/
@@ -77,7 +90,7 @@ String BuildFixes()
Settings.UseRTOSMultitasking = false;
Settings.Pin_Reset = -1;
Settings.SyslogFacility = DEFAULT_SYSLOG_FACILITY;
Settings.MQTTUseUnitNameAsClientId = DEFAULT_MQTT_USE_UNITNANE_AS_CLIENTID;
Settings.MQTTUseUnitNameAsClientId = DEFAULT_MQTT_USE_UNITNAME_AS_CLIENTID;
Settings.StructSize = sizeof(Settings);
}
@@ -113,7 +126,7 @@ void fileSystemCheck()
{
ResetFactory();
}
f.close();
if (f) f.close();
}
else
{
@@ -150,7 +163,11 @@ String SaveSettings(void)
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);
@@ -413,6 +430,13 @@ String SaveCustomTaskSettings(int TaskIndex, byte* memAddress, int datasize)
return(SaveToFile(CustomTaskSettings_Type, TaskIndex, (char*)FILE_CONFIG, memAddress, datasize));
}
String getCustomTaskSettingsError(byte varNr) {
String error = F("Error: Text too long for line ");
error += varNr + 1;
error += '\n';
return error;
}
/********************************************************************************************\
Clear custom task settings
@@ -518,13 +542,15 @@ String InitFile(const char* fname, int datasize)
FLASH_GUARD();
fs::File f = SPIFFS.open(fname, "w");
SPIFFS_CHECK(f, fname);
if (f) {
SPIFFS_CHECK(f, fname);
for (int x = 0; x < datasize ; x++)
{
SPIFFS_CHECK(f.write(0), fname);
for (int x = 0; x < datasize ; x++)
{
SPIFFS_CHECK(f.write(0), fname);
}
f.close();
}
f.close();
//OK
return String();
@@ -551,24 +577,47 @@ String SaveToFile(char* fname, int index, byte* memAddress, int datasize)
addLog(LOG_LEVEL_ERROR, log);
return log;
}
START_TIMER;
checkRAM(F("SaveToFile"));
FLASH_GUARD();
fs::File f = SPIFFS.open(fname, "r+");
SPIFFS_CHECK(f, fname);
SPIFFS_CHECK(f.seek(index, fs::SeekSet), fname);
byte *pointerToByteToSave = memAddress;
for (int x = 0; x < datasize ; x++)
{
SPIFFS_CHECK(f.write(*pointerToByteToSave), fname);
pointerToByteToSave++;
String log = F("SaveToFile: free stack: ");
log += getCurrentFreeStack();
addLog(LOG_LEVEL_INFO, log);
}
delay(1);
unsigned long timer = millis() + 50;
fs::File f = SPIFFS.open(fname, "r+");
if (f) {
SPIFFS_CHECK(f, fname);
SPIFFS_CHECK(f.seek(index, fs::SeekSet), fname);
byte *pointerToByteToSave = memAddress;
for (int x = 0; x < datasize ; x++)
{
SPIFFS_CHECK(f.write(*pointerToByteToSave), fname);
pointerToByteToSave++;
if (timeOutReached(timer)) {
timer += 50;
delay(1);
}
}
f.close();
String log = F("FILE : Saved ");
log=log+fname;
addLog(LOG_LEVEL_INFO, log);
} else {
String log = F("SaveToFile: ");
log += fname;
log += F(" ERROR, Cannot save to file");
addLog(LOG_LEVEL_ERROR, log);
return log;
}
STOP_TIMER(SAVEFILE_STATS);
{
String log = F("SaveToFile: free stack after: ");
log += getCurrentFreeStack();
addLog(LOG_LEVEL_INFO, log);
}
f.close();
String log = F("FILE : Saved ");
log=log+fname;
addLog(LOG_LEVEL_INFO, log);
//OK
return String();
@@ -591,14 +640,22 @@ String ClearInFile(char* fname, int index, int datasize)
FLASH_GUARD();
fs::File f = SPIFFS.open(fname, "r+");
SPIFFS_CHECK(f, fname);
if (f) {
SPIFFS_CHECK(f, fname);
SPIFFS_CHECK(f.seek(index, fs::SeekSet), fname);
for (int x = 0; x < datasize ; x++)
{
SPIFFS_CHECK(f.write(0), fname);
SPIFFS_CHECK(f.seek(index, fs::SeekSet), fname);
for (int x = 0; x < datasize ; x++)
{
SPIFFS_CHECK(f.write(0), fname);
}
f.close();
} else {
String log = F("ClearInFile: ");
log += fname;
log += F(" ERROR, Cannot save to file");
addLog(LOG_LEVEL_ERROR, log);
return log;
}
f.close();
//OK
return String();
@@ -617,10 +674,10 @@ String LoadFromFile(char* fname, int offset, byte* memAddress, int datasize)
addLog(LOG_LEVEL_ERROR, log);
return log;
}
delay(1);
START_TIMER;
checkRAM(F("LoadFromFile"));
fs::File f = SPIFFS.open(fname, "r+");
SPIFFS_CHECK(f, fname);
SPIFFS_CHECK(f.seek(offset, fs::SeekSet), fname);
@@ -628,6 +685,7 @@ String LoadFromFile(char* fname, int offset, byte* memAddress, int datasize)
f.close();
STOP_TIMER(LOADFILE_STATS);
delay(1);
return(String());
}
+2 -12
View File
@@ -3,17 +3,7 @@
#include <stdint.h>
#include <list>
struct timeStruct {
timeStruct() : Second(0), Minute(0), Hour(0), Wday(0), Day(0), Month(0), Year(0) {}
uint8_t Second;
uint8_t Minute;
uint8_t Hour;
uint8_t Wday; // day of week, sunday is day 1
uint8_t Day;
uint8_t Month;
uint8_t Year; // offset from 1970;
};
#include <time.h>
// convenient constants for TimeChangeRules
enum week_t {Last=0, First, Second, Third, Fourth};
@@ -184,7 +174,7 @@ private:
if (is_idle) return;
last_exec_time_usec = micros();
is_idle = true;
yield(); // Nothing to do, so call yield for backgroundtasks
delay(0); // Nothing to do, so leave time for backgroundtasks
}
void recordRunning() {
+1
View File
@@ -678,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#");
+1 -1
View File
@@ -121,7 +121,7 @@ int32_t I2C_read32_reg(uint8_t i2caddr, byte reg) {
Wire.beginTransmission(i2caddr);
Wire.write((uint8_t)reg);
Wire.endTransmission(END_TRANSMISSION_FLAG);
Wire.requestFrom(i2caddr, (byte)3);
Wire.requestFrom(i2caddr, (byte)4);
value = (((int32_t)Wire.read()) <<24) | (((uint32_t)Wire.read()) << 16) | (Wire.read() << 8) | Wire.read();
return value;
+593 -314
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -70,7 +70,7 @@ bool Modbus::begin(uint8_t function, uint8_t ModbusID, uint16_t ModbusRegister,
resultReceived = false;
ModbusClient = new WiFiClient();
ModbusClient->setNoDelay(true);
ModbusClient->setTimeout(200);
ModbusClient->setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT);
timeout = millis();
ModbusClient->flush();
+1 -1
View File
@@ -623,7 +623,7 @@ bool WiFiConnected(uint32_t timeout_ms) {
uint32_t timer = millis() + (timeout_ms > 500 ? 500 : timeout_ms);
uint32_t min_delay = timeout_ms / 20;
if (min_delay < 10) {
yield(); // Allow at least once time for backgroundtasks
delay(0); // Allow at least once time for backgroundtasks
min_delay = 10;
}
// Apparently something needs network, perform check to see if it is ready now.
+12 -5
View File
@@ -41,19 +41,26 @@ unsigned long getMixedId(unsigned long timerType, unsigned long id) {
\*********************************************************************************************/
void handle_schedule() {
unsigned long timer;
const unsigned long mixed_id = msecTimerHandler.getNextId(timer);
unsigned long mixed_id = 0;
if (timePassedSince(last_system_event_run) < 500) {
// Make sure system event queue will be looked at every now and then.
mixed_id = msecTimerHandler.getNextId(timer);
}
if (mixed_id == 0) {
// No id ready to run right now.
// Events are not that important to run immediately.
// Make sure normal scheduled jobs run at higher priority.
backgroundtasks();
process_system_event_queue();
last_system_event_run = millis();
return;
}
const unsigned long timerType = (mixed_id >> TIMER_ID_SHIFT);
const unsigned long mask = (1 << TIMER_ID_SHIFT) -1;
const unsigned long id = mixed_id & mask;
delay(0); // See: https://github.com/letscontrolit/ESPEasy/issues/1818#issuecomment-425351328
switch (timerType) {
case CONST_INTERVAL_TIMER:
process_interval_timer(id, timer);
@@ -262,11 +269,11 @@ void process_plugin_task_timer(unsigned long id) {
log += id;
addLog(LOG_LEVEL_INFO, log);
*/
systemTimers.erase(id);
if (y >= 0) {
String dummy;
Plugin_ptr[y](PLUGIN_TIMER_IN, &TempEvent, dummy);
}
systemTimers.erase(id);
}
STOP_TIMER(PROC_SYS_TIMER);
}
@@ -427,9 +434,9 @@ void process_system_event_queue() {
EventQueue.front().cmd.c_str(),
&EventQueue.front().event,
EventQueue.front().line.c_str());
yield();
delay(0);
SendStatus(EventQueue.front().event.Source, status);
yield();
delay(0);
break;
}
}
+1 -1
View File
@@ -11,7 +11,7 @@ void serial()
{
while (Serial.available())
{
yield();
delay(0);
SerialInByte = Serial.read();
if (SerialInByte == 255) // binary data...
{
+99 -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%
\*********************************************************************************************/
@@ -508,6 +560,8 @@ void parseSystemVariables(String& s, boolean useURLencode)
SMART_REPL(F("%systm_hm_am%"), getTimeString_ampm(':', false))
SMART_REPL(F("%systime%"), getTimeString(':'))
SMART_REPL(F("%systime_am%"), getTimeString_ampm(':'))
SMART_REPL(F("%sysbuild_date%"), String(CRCValues.compileDate))
SMART_REPL(F("%sysbuild_time%"), String(CRCValues.compileTime))
repl(F("%sysname%"), Settings.Name, s, useURLencode);
// valueString is being used by the macro.
@@ -540,17 +594,34 @@ void parseSystemVariables(String& s, boolean useURLencode)
SMART_REPL(F("%unixtime%"), String(getUnixTime()))
SMART_REPL_T(F("%sunset"), replSunSetTimeString)
SMART_REPL_T(F("%sunrise"), replSunRiseTimeString)
if (s.indexOf(F("%is")) != -1) {
SMART_REPL(F("%ismqtt%"), String(MQTTclient_connected));
SMART_REPL(F("%iswifi%"), String(wifiStatus)); //0=disconnected, 1=connected, 2=got ip, 3=services initialized
SMART_REPL(F("%isntp%"), String(statusNTPInitialized));
#ifdef USES_P037
SMART_REPL(F("%ismqttimp%"), String(P037_MQTTImport_connected));
#endif // USES_P037
}
const int v_index = s.indexOf("%v");
if (v_index != -1 && isDigit(s[v_index+2])) {
for (byte i = 0; i < CUSTOM_VARS_MAX; ++i) {
SMART_REPL("%v"+toString(i+1,0)+'%', String(customFloatVar[i]))
}
}
}
String getReplacementString(const String& format, String& s) {
int startpos = s.indexOf(format);
int endpos = s.indexOf('%', startpos + 1);
String R = s.substring(startpos, endpos + 1);
String log = F("ReplacementString SunTime: ");
log += R;
log += F(" offset: ");
log += getSecOffset(R);
addLog(LOG_LEVEL_DEBUG, log);
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("ReplacementString SunTime: ");
log += R;
log += F(" offset: ");
log += getSecOffset(R);
addLog(LOG_LEVEL_DEBUG, log);
}
return R;
}
+351 -310
View File
@@ -1,7 +1,7 @@
/********************************************************************************************\
Time stuff
\*********************************************************************************************/
Time stuff
\*********************************************************************************************/
#define SECS_PER_MIN (60UL)
#define SECS_PER_HOUR (3600UL)
@@ -11,93 +11,96 @@
#define SECS_PER_YEAR (SECS_PER_WEEK * 52UL)
#define SECS_YR_2000 (946684800UL) // the time at the start of y2k
#define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )
#include <time.h>
timeStruct tm;
struct tm tm;
uint32_t syncInterval = 3600; // time sync will be attempted after this many seconds
uint32_t sysTime = 0;
double sysTime = 0.0; // Use high resolution double to get better sync between nodes when using NTP
uint32_t prevMillis = 0;
uint32_t nextSyncTime = 0;
timeStruct tsRise, tsSet;
timeStruct sunRise;
timeStruct sunSet;
struct tm tsRise, tsSet;
struct tm sunRise;
struct tm sunSet;
byte PrevMinutes = 0;
float sunDeclination(int doy) {
// Declination of the sun in radians
// Formula 2008 by Arnold(at)Barmettler.com, fit to 20 years of average declinations (2008-2027)
return 0.409526325277017 * sin(0.0169060504029192 * (doy - 80.0856919827619));
// Declination of the sun in radians
// Formula 2008 by Arnold(at)Barmettler.com, fit to 20 years of average declinations (2008-2027)
return 0.409526325277017 * sin(0.0169060504029192 * (doy - 80.0856919827619));
}
float diurnalArc(float dec, float lat) {
// Duration of the half sun path in hours (time from sunrise to the highest level in the south)
float rad = 0.0174532925; // = pi/180.0
float height = -50.0 / 60.0 * rad;
float latRad = lat * rad;
return 12.0 * acos((sin(height) - sin(latRad) * sin(dec)) / (cos(latRad) * cos(dec))) / 3.1415926536;
// Duration of the half sun path in hours (time from sunrise to the highest level in the south)
float rad = 0.0174532925; // = pi/180.0
float height = -50.0 / 60.0 * rad;
float latRad = lat * rad;
return 12.0 * acos((sin(height) - sin(latRad) * sin(dec)) / (cos(latRad) * cos(dec))) / 3.1415926536;
}
float equationOfTime(int doy) {
// Difference between apparent and mean solar time
// Formula 2008 by Arnold(at)Barmettler.com, fit to 20 years of average equation of time (2008-2027)
return -0.170869921174742 * sin(0.0336997028793971 * doy + 0.465419984181394) - 0.129890681040717 * sin(0.0178674832556871 * doy - 0.167936777524864);
// Difference between apparent and mean solar time
// Formula 2008 by Arnold(at)Barmettler.com, fit to 20 years of average equation of time (2008-2027)
return -0.170869921174742 * sin(0.0336997028793971 * doy + 0.465419984181394) - 0.129890681040717 * sin(0.0178674832556871 * doy - 0.167936777524864);
}
int dayOfYear(int year, int month, int day) {
// Algorithm borrowed from DateToOrdinal by Ritchie Lawrence, www.commandline.co.uk
int z = 14 - month;
z /= 12;
int y = year + 4800 - z;
int m = month + 12 * z - 3;
int j = 153 * m + 2;
j = j / 5 + day + y * 365 + y / 4 - y / 100 + y / 400 - 32045;
y = year + 4799;
int k = y * 365 + y / 4 - y / 100 + y / 400 - 31738;
return j - k + 1;
// Algorithm borrowed from DateToOrdinal by Ritchie Lawrence, www.commandline.co.uk
int z = 14 - month;
z /= 12;
int y = year + 4800 - z;
int m = month + 12 * z - 3;
int j = 153 * m + 2;
j = j / 5 + day + y * 365 + y / 4 - y / 100 + y / 400 - 32045;
y = year + 4799;
int k = y * 365 + y / 4 - y / 100 + y / 400 - 31738;
return j - k + 1;
}
void calcSunRiseAndSet() {
int doy = dayOfYear(tm.Year, tm.Month, tm.Day);
float eqt = equationOfTime(doy);
float dec = sunDeclination(doy);
float da = diurnalArc(dec, Settings.Latitude);
float rise = 12 - da - eqt;
float set = 12 + da - eqt;
tsRise.Hour = (int)rise;
tsRise.Minute = (rise - (int)rise) * 60.0;
tsSet.Hour = (int)set;
tsSet.Minute = (set - (int)set) * 60.0;
tsRise.Day = tsSet.Day = tm.Day;
tsRise.Month = tsSet.Month = tm.Month;
tsRise.Year = tsSet.Year = tm.Year;
int doy = dayOfYear(tm.tm_year, tm.tm_mon, tm.tm_mday);
float eqt = equationOfTime(doy);
float dec = sunDeclination(doy);
float da = diurnalArc(dec, Settings.Latitude);
float rise = 12 - da - eqt;
float set = 12 + da - eqt;
tsRise.tm_hour = (int)rise;
tsRise.tm_min = (rise - (int)rise) * 60.0;
tsSet.tm_hour = (int)set;
tsSet.tm_min = (set - (int)set) * 60.0;
tsRise.tm_mday = tsSet.tm_mday = tm.tm_mday;
tsRise.tm_mon = tsSet.tm_mon = tm.tm_mon;
tsRise.tm_year = tsSet.tm_year = tm.tm_year;
// Now apply the longitude
int secOffset_longitude = -1.0 * (Settings.Longitude / 15.0) * 3600;
tsSet = addSeconds(tsSet, secOffset_longitude, false);
tsRise = addSeconds(tsRise, secOffset_longitude, false);
int secOffset_longitude = -1.0 * (Settings.Longitude / 15.0) * 3600;
tsSet = addSeconds(tsSet, secOffset_longitude, false);
tsRise = addSeconds(tsRise, secOffset_longitude, false);
breakTime(toLocal(makeTime(tsRise)), sunRise);
breakTime(toLocal(makeTime(tsSet)), sunSet);
}
timeStruct getSunRise(int secOffset) {
return addSeconds(tsRise, secOffset, true);
struct tm getSunRise(int secOffset) {
return addSeconds(tsRise, secOffset, true);
}
timeStruct getSunSet(int secOffset) {
return addSeconds(tsSet, secOffset, true);
struct tm getSunSet(int secOffset) {
return addSeconds(tsSet, secOffset, true);
}
timeStruct addSeconds(const timeStruct& ts, int seconds, bool toLocalTime) {
unsigned long time = makeTime(ts);
time += seconds;
if (toLocalTime) {
time = toLocal(time);
}
timeStruct result;
breakTime(time, result);
return result;
struct tm addSeconds(const struct tm& ts, int seconds, bool toLocalTime) {
unsigned long time = makeTime(ts);
time += seconds;
if (toLocalTime) {
time = toLocal(time);
}
struct tm result;
breakTime(time, result);
return result;
}
void breakTime(unsigned long timeInput, struct timeStruct &tm) {
void breakTime(unsigned long timeInput, struct tm &tm) {
uint8_t year;
uint8_t month, monthLength;
uint32_t time;
@@ -105,37 +108,37 @@ void breakTime(unsigned long timeInput, struct timeStruct &tm) {
const uint8_t monthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
time = (uint32_t)timeInput;
tm.Second = time % 60;
tm.tm_sec = time % 60;
time /= 60; // now it is minutes
tm.Minute = time % 60;
tm.tm_min = time % 60;
time /= 60; // now it is hours
tm.Hour = time % 24;
tm.tm_hour = time % 24;
time /= 24; // now it is days
tm.Wday = ((time + 4) % 7) + 1; // Sunday is day 1
tm.tm_wday = ((time + 4) % 7) + 1; // Sunday is day 1
year = 0;
days = 0;
while ((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) {
year++;
}
tm.Year = year; // year is offset from 1970
tm.tm_year = year; // year is offset from 1970
days -= LEAP_YEAR(year) ? 366 : 365;
time -= days; // now it is days in this year, starting at 0
days -= LEAP_YEAR(year) ? 366 : 365;
time -= days; // now it is days in this year, starting at 0
days = 0;
month = 0;
monthLength = 0;
for (month = 0; month < 12; month++) {
if (month == 1) { // february
if (LEAP_YEAR(year)) {
monthLength = 29;
} else {
monthLength = 28;
}
} else {
monthLength = monthDays[month];
}
days = 0;
month = 0;
monthLength = 0;
for (month = 0; month < 12; month++) {
if (month == 1) { // february
if (LEAP_YEAR(year)) {
monthLength = 29;
} else {
monthLength = 28;
}
} else {
monthLength = monthDays[month];
}
if (time >= monthLength) {
time -= monthLength;
@@ -143,50 +146,37 @@ void breakTime(unsigned long timeInput, struct timeStruct &tm) {
break;
}
}
tm.Month = month + 1; // jan is month 1
tm.Day = time + 1; // day of month
tm.tm_mon = month + 1; // jan is month 1
tm.tm_mday = time + 1; // day of month
}
void setTime(unsigned long t) {
sysTime = (uint32_t)t;
applyTimeZone(t);
nextSyncTime = (uint32_t)t + syncInterval;
prevMillis = millis(); // restart counting from now (thanks to Korman for this fix)
if (Settings.UseRules)
{
static bool firstUpdate = true;
String event = firstUpdate ? F("Time#Initialized") : F("Time#Set");
firstUpdate = false;
rulesProcessing(event);
}
}
uint32_t getUnixTime() {
return sysTime;
uint32_t getUnixTime()
{
return static_cast<uint32_t>(sysTime);
}
int getSecOffset(const String& format) {
int position_minus = format.indexOf('-');
int position_plus = format.indexOf('+');
if (position_minus == -1 && position_plus == -1)
return 0;
int sign_position = _max(position_minus, position_plus);
int position_percent = format.indexOf('%', sign_position);
if (position_percent == -1) {
return 0;
}
String valueStr = getNumerical(format.substring(sign_position, position_percent), true);
if (!isInt(valueStr)) return 0;
int value = valueStr.toInt();
switch (format.charAt(position_percent - 1)) {
case 'm':
case 'M':
return value * 60;
case 'h':
case 'H':
return value * 3600;
}
return value;
int position_minus = format.indexOf('-');
int position_plus = format.indexOf('+');
if (position_minus == -1 && position_plus == -1)
return 0;
int sign_position = _max(position_minus, position_plus);
int position_percent = format.indexOf('%', sign_position);
if (position_percent == -1) {
return 0;
}
String valueStr = getNumerical(format.substring(sign_position, position_percent), true);
if (!isInt(valueStr)) return 0;
int value = valueStr.toInt();
switch (format.charAt(position_percent - 1)) {
case 'm':
case 'M':
return value * 60;
case 'h':
case 'H':
return value * 3600;
}
return value;
}
String getSunriseTimeString(char delimiter) {
@@ -198,14 +188,14 @@ String getSunsetTimeString(char delimiter) {
}
String getSunriseTimeString(char delimiter, int secOffset) {
if (secOffset == 0)
return getSunriseTimeString(delimiter);
if (secOffset == 0)
return getSunriseTimeString(delimiter);
return getTimeString(getSunRise(secOffset), delimiter, false, false);
}
String getSunsetTimeString(char delimiter, int secOffset) {
if (secOffset == 0)
return getSunsetTimeString(delimiter);
if (secOffset == 0)
return getSunsetTimeString(delimiter);
return getTimeString(getSunSet(secOffset), delimiter, false, false);
}
@@ -214,95 +204,103 @@ unsigned long now() {
// calculate number of seconds passed since last call to now()
bool timeSynced = false;
const long msec_passed = timePassedSince(prevMillis);
const long seconds_passed = msec_passed / 1000;
sysTime += seconds_passed;
prevMillis += seconds_passed * 1000;
if (nextSyncTime <= sysTime) {
// nextSyncTime & sysTime are in seconds
unsigned long t = getNtpTime();
if (t != 0) {
timeSynced = true;
setTime(t);
}
}
uint32_t localSystime = toLocal(sysTime);
breakTime(localSystime, tm);
if (timeSynced) {
calcSunRiseAndSet();
}
sysTime += static_cast<double>(msec_passed) / 1000.0;
prevMillis += msec_passed;
if (nextSyncTime <= sysTime) {
// nextSyncTime & sysTime are in seconds
double unixTime_d;
if (getNtpTime(unixTime_d)) {
prevMillis = millis(); // restart counting from now (thanks to Korman for this fix)
timeSynced = true;
sysTime = unixTime_d;
applyTimeZone(unixTime_d);
nextSyncTime = (uint32_t)unixTime_d + syncInterval;
}
}
uint32_t localSystime = toLocal(sysTime);
breakTime(localSystime, tm);
if (timeSynced) {
calcSunRiseAndSet();
if (Settings.UseRules) {
String event = statusNTPInitialized ? F("Time#Set") : F("Time#Initialized");
rulesProcessing(event);
}
statusNTPInitialized = true; //@giig1967g: setting system variable %isntp%
}
return (unsigned long)localSystime;
}
int year(unsigned long t) {
timeStruct tmp;
struct tm tmp;
breakTime(t, tmp);
return 1970 + tmp.Year;
return 1970 + tmp.tm_year;
}
int weekday(unsigned long t) {
timeStruct tmp;
struct tm tmp;
breakTime(t, tmp);
return tmp.Wday;
return tmp.tm_wday;
}
int year()
{
return 1970 + tm.Year;
return 1970 + tm.tm_year;
}
byte month()
{
return tm.Month;
return tm.tm_mon;
}
byte day()
{
return tm.Day;
return tm.tm_mday;
}
byte hour()
{
return tm.Hour;
return tm.tm_hour;
}
byte minute()
{
return tm.Minute;
return tm.tm_min;
}
byte second()
{
return tm.Second;
return tm.tm_sec;
}
// day of week, sunday is day 1
int weekday()
{
return tm.Wday;
return tm.tm_wday;
}
String weekday_str()
{
const int wday(weekday() - 1); // here: Count from Sunday = 0
const String weekDays = F("SunMonTueWedThuFriSat");
return weekDays.substring(wday * 3, wday * 3 + 3);
const int wday(weekday() - 1); // here: Count from Sunday = 0
const String weekDays = F("SunMonTueWedThuFriSat");
return weekDays.substring(wday * 3, wday * 3 + 3);
}
void initTime()
{
nextSyncTime = 0;
now();
nextSyncTime = 0;
now();
}
void checkTime()
{
now();
if (tm.Minute != PrevMinutes)
if (tm.tm_min != PrevMinutes)
{
PluginCall(PLUGIN_CLOCK_IN, 0, dummyString);
PrevMinutes = tm.Minute;
PrevMinutes = tm.tm_min;
if (Settings.UseRules)
{
String event;
@@ -323,50 +321,49 @@ void checkTime()
}
unsigned long getNtpTime()
bool getNtpTime(double& unixTime_d)
{
if (!Settings.UseNTP || !WiFiConnected(10)) {
return 0;
}
IPAddress timeServerIP;
String log = F("NTP : NTP host ");
if (Settings.NTPHost[0] != 0) {
WiFi.hostByName(Settings.NTPHost, timeServerIP);
log += Settings.NTPHost;
// When single set host fails, retry again in a minute
nextSyncTime = sysTime + 20;
}
else {
// Have to do a lookup eacht time, since the NTP pool always returns another IP
String ntpServerName = String(random(0, 3));
ntpServerName += F(".pool.ntp.org");
WiFi.hostByName(ntpServerName.c_str(), timeServerIP);
log += ntpServerName;
// When pool host fails, retry can be much sooner
nextSyncTime = sysTime + 5;
}
if (!Settings.UseNTP || !WiFiConnected(10)) {
return false;
}
IPAddress timeServerIP;
String log = F("NTP : NTP host ");
if (Settings.NTPHost[0] != 0) {
WiFi.hostByName(Settings.NTPHost, timeServerIP);
log += Settings.NTPHost;
// When single set host fails, retry again in a minute
nextSyncTime = sysTime + 20;
}else {
// Have to do a lookup eacht time, since the NTP pool always returns another IP
String ntpServerName = String(random(0, 3));
ntpServerName += F(".pool.ntp.org");
WiFi.hostByName(ntpServerName.c_str(), timeServerIP);
log += ntpServerName;
// When pool host fails, retry can be much sooner
nextSyncTime = sysTime + 5;
}
log += F(" (");
log += timeServerIP.toString();
log += F(")");
log += F(" (");
log += timeServerIP.toString();
log += F(")");
if (!hostReachable(timeServerIP)) {
log += F(" unreachable");
addLog(LOG_LEVEL_INFO, log);
return 0;
}
if (!hostReachable(timeServerIP)) {
log += F(" unreachable");
addLog(LOG_LEVEL_INFO, log);
return false;
}
WiFiUDP udp;
if (!beginWiFiUDP_randomPort(udp))
return 0;
if (!beginWiFiUDP_randomPort(udp))
return 0;
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
log += F(" queried");
addLog(LOG_LEVEL_DEBUG_MORE, log);
log += F(" queried");
addLog(LOG_LEVEL_DEBUG_MORE, log);
while (udp.parsePacket() > 0) ; // discard any previously received packets
while (udp.parsePacket() > 0) ; // discard any previously received packets
memset(packetBuffer, 0, NTP_PACKET_SIZE);
packetBuffer[0] = 0b11100011; // LI, Version, Mode
@@ -377,53 +374,89 @@ unsigned long getNtpTime()
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
if (udp.beginPacket(timeServerIP, 123) == 0) { //NTP requests are to port 123
udp.stop();
return 0;
}
if (udp.beginPacket(timeServerIP, 123) == 0) { //NTP requests are to port 123
udp.stop();
return 0;
}
udp.write(packetBuffer, NTP_PACKET_SIZE);
udp.endPacket();
uint32_t beginWait = millis();
while (!timeOutReached(beginWait + 1000)) {
int size = udp.parsePacket();
uint32_t beginWait = millis();
while (!timeOutReached(beginWait + 1000)) {
int size = udp.parsePacket();
int remotePort = udp.remotePort();
if (size >= NTP_PACKET_SIZE && remotePort == 123) {
udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
String log = F("NTP : NTP replied: ");
log += timePassedSince(beginWait);
log += F(" mSec");
addLog(LOG_LEVEL_DEBUG_MORE, log);
if (size >= NTP_PACKET_SIZE && remotePort == 123) {
udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
// For more detailed info on improving accuracy, see:
// https://github.com/lettier/ntpclient/issues/4#issuecomment-360703503
// For now, we simply use half the reply time as delay compensation.
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
// TX time is used here.
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
uint32_t txTm = secsSince1900 - 2208988800UL;
unsigned long txTm_f;
txTm_f = (unsigned long)packetBuffer[44] << 24;
txTm_f |= (unsigned long)packetBuffer[45] << 16;
txTm_f |= (unsigned long)packetBuffer[46] << 8;
txTm_f |= (unsigned long)packetBuffer[47];
// Convert seconds to double
unixTime_d = static_cast<double>(txTm);
// Add fractional part.
unixTime_d += (static_cast<double>(txTm_f) / 4294967295.0);
long total_delay = timePassedSince(beginWait);
// compensate for the delay by adding half the total delay
// N.B. unixTime_d is in seconds and delay in msec.
double delay_compensation = static_cast<double>(total_delay) / 2000.0;
unixTime_d += delay_compensation;
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String log = F("NTP : NTP replied: delay ");
log += total_delay;
log += F(" mSec");
log += F(" Accuracy increased by ");
double fractpart, intpart;
fractpart = modf(unixTime_d, &intpart);
if (fractpart < delay_compensation) {
// We gained more than 1 second in accuracy
fractpart += 1.0;
}
log += String(fractpart, 3);
log += F(" seconds");
addLog(LOG_LEVEL_INFO, log);
}
udp.stop();
return secsSince1900 - 2208988800UL;
}
delay(10);
}
addLog(LOG_LEVEL_DEBUG_MORE, F("NTP : No reply"));
return true;
}
delay(10);
}
addLog(LOG_LEVEL_DEBUG_MORE, F("NTP : No reply"));
udp.stop();
return 0;
return false;
}
/********************************************************************************************\
Unsigned long Timer timeOut check
\*********************************************************************************************/
Unsigned long Timer timeOut check
\*********************************************************************************************/
// Return the time difference as a signed value, taking into account the timers may overflow.
// Returned timediff is between -24.9 days and +24.9 days.
// Returned value is positive when "next" is after "prev"
long timeDiff(const unsigned long prev, const unsigned long next)
{
unsigned long start = ESP.getCycleCount();
unsigned long start = ESP.getCycleCount();
long signed_diff = 0;
// To cast a value to a signed long, the difference may not exceed half the ULONG_MAX
const unsigned long half_max_unsigned_long = 2147483647u; // = 2^31 -1
@@ -450,11 +483,11 @@ long timeDiff(const unsigned long prev, const unsigned long next)
signed_diff = static_cast<long>((ULONG_MAX - prev) + next + 1u);
}
}
unsigned long end = ESP.getCycleCount();
if (end > start) {
++timediff_calls;
timediff_cpu_cycles_total += (end - start);
}
unsigned long end = ESP.getCycleCount();
if (end > start) {
++timediff_calls;
timediff_cpu_cycles_total += (end - start);
}
return signed_diff;
}
@@ -483,115 +516,118 @@ void setNextTimeInterval(unsigned long& timer, const unsigned long step) {
timer += step;
const long passed = timePassedSince(timer);
if (passed < 0) {
// Event has not yet happened, which is fine.
return;
}
if (static_cast<unsigned long>(passed) > step) {
// No need to keep running behind, start again.
timer = millis() + step;
return;
}
// Try to get in sync again.
timer = millis() + (step - passed);
// Event has not yet happened, which is fine.
return;
}
if (static_cast<unsigned long>(passed) > step) {
// No need to keep running behind, start again.
timer = millis() + step;
return;
}
// Try to get in sync again.
timer = millis() + (step - passed);
}
/********************************************************************************************\
Convert a 32 bit integer into a string like "Sun,12:30"
\*********************************************************************************************/
Convert a 32 bit integer into a string like "Sun,12:30"
\*********************************************************************************************/
String timeLong2String(unsigned long lngTime)
{
unsigned long x = 0;
String time = "";
unsigned long x = 0;
String time = "";
x = (lngTime >> 16) & 0xf;
if (x == 0x0f)
x = 0;
String weekDays = F("AllSunMonTueWedThuFriSatWrkWkd");
time = weekDays.substring(x * 3, x * 3 + 3);
time += ",";
x = (lngTime >> 16) & 0xf;
if (x == 0x0f)
x = 0;
String weekDays = F("AllSunMonTueWedThuFriSatWrkWkd");
time = weekDays.substring(x * 3, x * 3 + 3);
time += ",";
x = (lngTime >> 12) & 0xf;
if (x == 0xf)
time += "*";
else if (x == 0xe)
time += "-";
else
time += x;
x = (lngTime >> 12) & 0xf;
if (x == 0xf)
time += "*";
else if (x == 0xe)
time += "-";
else
time += x;
x = (lngTime >> 8) & 0xf;
if (x == 0xf)
time += "*";
else if (x == 0xe)
time += "-";
else
time += x;
x = (lngTime >> 8) & 0xf;
if (x == 0xf)
time += "*";
else if (x == 0xe)
time += "-";
else
time += x;
time += ":";
time += ":";
x = (lngTime >> 4) & 0xf;
if (x == 0xf)
time += "*";
else if (x == 0xe)
time += "-";
else
time += x;
x = (lngTime >> 4) & 0xf;
if (x == 0xf)
time += "*";
else if (x == 0xe)
time += "-";
else
time += x;
x = (lngTime) & 0xf;
if (x == 0xf)
time += "*";
else if (x == 0xe)
time += "-";
else
time += x;
x = (lngTime) & 0xf;
if (x == 0xf)
time += "*";
else if (x == 0xe)
time += "-";
else
time += x;
return time;
return time;
}
// returns the current Date separated by the given delimiter
// date format example with '-' delimiter: 2016-12-31 (YYYY-MM-DD)
String getDateString(const timeStruct& ts, char delimiter) {
String getDateString(const struct tm& ts, char delimiter) {
char DateString[20]; //19 digits plus the null char
const int year = 1970 + ts.Year;
sprintf_P(DateString, PSTR("%4d%c%02d%c%02d"), year, delimiter, ts.Month, delimiter, ts.Day);
const int year = 1970 + ts.tm_year;
sprintf_P(DateString, PSTR("%4d%c%02d%c%02d"), year, delimiter, ts.tm_mon, delimiter, ts.tm_mday);
return DateString;
}
String getDateString(char delimiter)
{
return getDateString(tm, delimiter);
return getDateString(tm, delimiter);
}
String getDateString(const struct tm& ts)
{
return getDateString(tm, ':');
}
// returns the current Date without delimiter
// date format example: 20161231 (YYYYMMDD)
String getDateString()
{
return getDateString('\0');
return getDateString('\0');
}
// returns the current Time separated by the given delimiter
// time format example with ':' delimiter: 23:59:59 (HH:MM:SS)
String getTimeString(const timeStruct& ts, char delimiter, bool am_pm, bool show_seconds)
String getTimeString(const struct tm& ts, char delimiter, bool am_pm, bool show_seconds)
{
char TimeString[20]; //19 digits plus the null char
if (am_pm) {
uint8_t hour(ts.Hour % 12);
uint8_t hour(ts.tm_hour % 12);
if (hour == 0) { hour = 12; }
const char a_or_p = ts.Hour < 12 ? 'A' : 'P';
const char a_or_p = ts.tm_hour < 12 ? 'A' : 'P';
if (show_seconds) {
sprintf_P(TimeString, PSTR("%d%c%02d%c%02d %cM"),
hour, delimiter, ts.Minute, delimiter, ts.Second, a_or_p);
hour, delimiter, ts.tm_min, delimiter, ts.tm_sec, a_or_p);
} else {
sprintf_P(TimeString, PSTR("%d%c%02d %cM"),
hour, delimiter, ts.Minute, a_or_p);
hour, delimiter, ts.tm_min, a_or_p);
}
} else {
if (show_seconds) {
sprintf_P(TimeString, PSTR("%02d%c%02d%c%02d"),
ts.Hour, delimiter, ts.Minute, delimiter, ts.Second);
ts.tm_hour, delimiter, ts.tm_min, delimiter, ts.tm_sec);
} else {
sprintf_P(TimeString, PSTR("%d%c%02d"),
ts.Hour, delimiter, ts.Minute);
ts.tm_hour, delimiter, ts.tm_min);
}
}
return TimeString;
@@ -599,36 +635,41 @@ String getTimeString(const timeStruct& ts, char delimiter, bool am_pm, bool show
String getTimeString(char delimiter, bool show_seconds /*=true*/)
{
return getTimeString(tm, delimiter, false, show_seconds);
return getTimeString(tm, delimiter, false, show_seconds);
}
String getTimeString_ampm(char delimiter, bool show_seconds /*=true*/)
{
return getTimeString(tm, delimiter, true, show_seconds);
return getTimeString(tm, delimiter, true, show_seconds);
}
// returns the current Time without delimiter
// time format example: 235959 (HHMMSS)
String getTimeString()
{
return getTimeString('\0');
return getTimeString('\0');
}
String getTimeString_ampm()
{
return getTimeString_ampm('\0');
return getTimeString_ampm('\0');
}
// returns the current Date and Time separated by the given delimiter
// if called like this: getDateTimeString('\0', '\0', '\0');
// it will give back this: 20161231235959 (YYYYMMDDHHMMSS)
String getDateTimeString(const timeStruct& ts, char dateDelimiter, char timeDelimiter, char dateTimeDelimiter, bool am_pm)
String getDateTimeString(const struct tm& ts, char dateDelimiter, char timeDelimiter, char dateTimeDelimiter, bool am_pm)
{
String ret = getDateString(ts, dateDelimiter);
if (dateTimeDelimiter != '\0')
ret += dateTimeDelimiter;
ret += getTimeString(ts, timeDelimiter, am_pm, true);
return ret;
String ret = getDateString(ts, dateDelimiter);
if (dateTimeDelimiter != '\0')
ret += dateTimeDelimiter;
ret += getTimeString(ts, timeDelimiter, am_pm, true);
return ret;
}
String getDateTimeString(const struct tm& ts)
{
return getDateTimeString(ts,'-', ':', ' ', false);
}
String getDateTimeString(char dateDelimiter, char timeDelimiter, char dateTimeDelimiter) {
@@ -640,14 +681,14 @@ String getDateTimeString_ampm(char dateDelimiter, char timeDelimiter, char date
}
/********************************************************************************************\
Convert a string like "Sun,12:30" into a 32 bit integer
\*********************************************************************************************/
Convert a string like "Sun,12:30" into a 32 bit integer
\*********************************************************************************************/
unsigned long string2TimeLong(const String &str)
{
// format 0000WWWWAAAABBBBCCCCDDDD
// WWWW=weekday, AAAA=hours tens digit, BBBB=hours, CCCC=minutes tens digit DDDD=minutes
// format 0000WWWWAAAABBBBCCCCDDDD
// WWWW=weekday, AAAA=hours tens digit, BBBB=hours, CCCC=minutes tens digit DDDD=minutes
#define TmpStr1Length 10
#define TmpStr1Length 10
char command[20];
char TmpStr1[TmpStr1Length];
int w, x, y;
@@ -665,8 +706,8 @@ unsigned long string2TimeLong(const String &str)
String day = TmpStr1;
String weekDays = F("allsunmontuewedthufrisatwrkwkd");
y = weekDays.indexOf(TmpStr1) / 3;
if (y == 0)
y = 0xf; // wildcard is 0xf
if (y == 0)
y = 0xf; // wildcard is 0xf
lngTime |= (unsigned long)y << 16;
}
@@ -694,13 +735,13 @@ unsigned long string2TimeLong(const String &str)
}
}
}
#undef TmpStr1Length
#undef TmpStr1Length
return lngTime;
}
/********************************************************************************************\
Match clock event
\*********************************************************************************************/
Match clock event
\*********************************************************************************************/
boolean matchClockEvent(unsigned long clockEvent, unsigned long clockSet)
{
unsigned long Mask;
@@ -731,6 +772,6 @@ boolean matchClockEvent(unsigned long clockEvent, unsigned long clockSet)
}
if (clockEvent == clockSet)
return true;
return false;
return true;
return false;
}
+19 -19
View File
@@ -1,4 +1,4 @@
#include <time.h>
/********************************************************************************************\
Time zone
\*********************************************************************************************/
@@ -95,7 +95,7 @@ void logTimeZoneInfo() {
// Summer time
log += F(" DST time start: ");
if (m_dstLoc != 0) {
timeStruct tmp;
struct tm tmp;
breakTime(m_dstLoc, tmp);
log += getDateTimeString(tmp, '-', ':', ' ', false);
}
@@ -106,7 +106,7 @@ void logTimeZoneInfo() {
// Standard/Winter time.
log += F("STD time start: ");
if (m_stdLoc != 0) {
timeStruct tmp;
struct tm tmp;
breakTime(m_stdLoc, tmp);
log += getDateTimeString(tmp, '-', ':', ' ', false);
}
@@ -116,7 +116,7 @@ void logTimeZoneInfo() {
addLog(LOG_LEVEL_INFO, log);
}
uint32_t makeTime(const timeStruct &tm) {
uint32_t makeTime(const struct tm &tm) {
// assemble time elements into uint32_t
// note year argument is offset from 1970 (see macros in time.h to convert to other formats)
// previous version used full four digit year (or digits since 2000),i.e. 2009 was 2009 or 9
@@ -125,25 +125,25 @@ uint32_t makeTime(const timeStruct &tm) {
uint32_t seconds;
// seconds from 1970 till 1 jan 00:00:00 of the given year
seconds= tm.Year*(SECS_PER_DAY * 365);
for (i = 0; i < tm.Year; i++) {
seconds= tm.tm_year*(SECS_PER_DAY * 365);
for (i = 0; i < tm.tm_year; i++) {
if (LEAP_YEAR(i)) {
seconds += SECS_PER_DAY; // add extra days for leap years
}
}
// add days for this year, months start from 1
for (i = 1; i < tm.Month; i++) {
if ( (i == 2) && LEAP_YEAR(tm.Year)) {
for (i = 1; i < tm.tm_mon; i++) {
if ( (i == 2) && LEAP_YEAR(tm.tm_year)) {
seconds += SECS_PER_DAY * 29;
} else {
seconds += SECS_PER_DAY * monthDays[i-1]; //monthDay array starts from 0
}
}
seconds+= (tm.Day-1) * SECS_PER_DAY;
seconds+= tm.Hour * SECS_PER_HOUR;
seconds+= tm.Minute * SECS_PER_MIN;
seconds+= tm.Second;
seconds+= (tm.tm_mday-1) * SECS_PER_DAY;
seconds+= tm.tm_hour * SECS_PER_HOUR;
seconds+= tm.tm_min * SECS_PER_MIN;
seconds+= tm.tm_sec;
return (uint32_t)seconds;
}
@@ -167,13 +167,13 @@ uint32_t calcTimeChangeForRule(const TimeChangeRule& r, int yr)
}
// calculate first day of the month, or for "Last" rules, first day of the next month
timeStruct tm;
tm.Hour = r.hour;
tm.Minute = 0;
tm.Second = 0;
tm.Day = 1;
tm.Month = m;
tm.Year = yr - 1970;
struct tm tm;
tm.tm_hour = r.hour;
tm.tm_min = 0;
tm.tm_sec = 0;
tm.tm_mday = 1;
tm.tm_mon = m;
tm.tm_year = yr - 1970;
uint32_t t = makeTime(tm);
// add offset from the first of the month to r.dow, and offset for the given week
+376 -289
View File
File diff suppressed because it is too large Load Diff
+7 -3
View File
@@ -237,7 +237,7 @@ static const char pgDefaultCSS[] PROGMEM = {
"table.multirow tr:nth-child(even){background-color: #DEE6FF; }"
"table.multirow {color: #000; width: 100%; min-width: 420px; border-collapse: collapse; }"
// Highlight row
"tr.highlight td { background-color: #dbff0075; }"
"tr.highlight td { background-color: #dbff0075; }"
// inside a form
".note {color: #444; font-style: italic; }"
//header with title and menu
@@ -415,8 +415,12 @@ static const char jsUpdateSensorValuesDevicePage[] PROGMEM = {
"tempValue = data.Sensors[c].TaskValues[k].Value;"
"decimalsValue = data.Sensors[c].TaskValues[k].NrDecimals;"
"tempValue = parseFloat(tempValue).toFixed(decimalsValue);"
"document.getElementById('value_' + (data.Sensors[c].TaskNumber - 1) + '_' + (data.Sensors[c].TaskValues[k].ValueNumber - 1)).innerHTML = tempValue;"
"document.getElementById('valuename_' + (data.Sensors[c].TaskNumber - 1) + '_' + (data.Sensors[c].TaskValues[k].ValueNumber - 1)).innerHTML = data.Sensors[c].TaskValues[k].Name + ':';"
"var valueID = 'value_' + (data.Sensors[c].TaskNumber - 1) + '_' + (data.Sensors[c].TaskValues[k].ValueNumber - 1);"
"var valueNameID = 'valuename_' + (data.Sensors[c].TaskNumber - 1) + '_' + (data.Sensors[c].TaskValues[k].ValueNumber - 1);"
"var valueElement = document.getElementById(valueID);"
"var valueNameElement = document.getElementById(valueNameID);"
"if (valueElement !== null) { valueElement.innerHTML = tempValue; }"
"if (valueNameElement !== null) { valueNameElement.innerHTML = data.Sensors[c].TaskValues[k].Name + ':'; }"
"}"
"}"
"}"
+2 -2
View File
@@ -33,7 +33,7 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C001_DelayHandler.configureControllerSettings(ControllerSettings);
break;
@@ -118,7 +118,7 @@ bool do_process_c001_delay_queue(int controller_number, const C001_queue_element
String request = create_http_request_auth(controller_number, element.controller_idx, ControllerSettings, F("GET"), element.txt);
addLog(LOG_LEVEL_DEBUG, element.txt);
return send_via_http(controller_number, client, request);
return send_via_http(controller_number, client, request, ControllerSettings.MustCheckReply);
}
#endif
+2 -2
View File
@@ -35,7 +35,7 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
MQTTDelayHandler.configureControllerSettings(ControllerSettings);
break;
@@ -145,7 +145,7 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
{
if (event->idx != 0)
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
/*
if (!ControllerSettings.checkHostReachable(true)) {
+3 -3
View File
@@ -32,7 +32,7 @@ boolean CPlugin_003(byte function, struct EventStruct *event, String& string)
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C003_DelayHandler.configureControllerSettings(ControllerSettings);
break;
@@ -78,7 +78,7 @@ bool do_process_c003_delay_queue(int controller_number, const C003_queue_element
client.print(" \n");
unsigned long timer = millis() + 200;
while (!client.available() && !timeOutReached(timer))
while (!client_available(client) && !timeOutReached(timer))
delay(1);
timer = millis() + 1000;
@@ -102,7 +102,7 @@ bool do_process_c003_delay_queue(int controller_number, const C003_queue_element
addLog(LOG_LEVEL_DEBUG, log);
client.println(SecuritySettings.ControllerPassword[element.controller_idx]);
delay(100);
while (client.available())
while (client_available(client))
client.read();
strcpy_P(log, PSTR("TELNT: Sending cmd"));
+2 -2
View File
@@ -32,7 +32,7 @@ boolean CPlugin_004(byte function, struct EventStruct *event, String& string)
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C004_DelayHandler.configureControllerSettings(ControllerSettings);
break;
@@ -94,6 +94,6 @@ bool do_process_c004_delay_queue(int controller_number, const C004_queue_element
postDataStr.length());
postStr += postDataStr;
return send_via_http(controller_number, client, postStr);
return send_via_http(controller_number, client, postStr, ControllerSettings.MustCheckReply);
}
#endif
+3 -3
View File
@@ -33,7 +33,7 @@ boolean CPlugin_005(byte function, struct EventStruct *event, String& string)
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
MQTTDelayHandler.configureControllerSettings(ControllerSettings);
break;
@@ -92,7 +92,7 @@ boolean CPlugin_005(byte function, struct EventStruct *event, String& string)
case CPLUGIN_PROTOCOL_SEND:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
if (!ControllerSettings.checkHostReachable(true)) {
success = false;
@@ -100,7 +100,7 @@ boolean CPlugin_005(byte function, struct EventStruct *event, String& string)
}
statusLED(true);
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
String pubname = ControllerSettings.Publish;
+3 -3
View File
@@ -33,7 +33,7 @@ boolean CPlugin_006(byte function, struct EventStruct *event, String& string)
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
MQTTDelayHandler.configureControllerSettings(ControllerSettings);
break;
@@ -90,12 +90,12 @@ boolean CPlugin_006(byte function, struct EventStruct *event, String& string)
success = false;
break;
}
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
statusLED(true);
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
String pubname = ControllerSettings.Publish;
+3 -2
View File
@@ -32,7 +32,7 @@ boolean CPlugin_007(byte function, struct EventStruct *event, String& string)
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C007_DelayHandler.configureControllerSettings(ControllerSettings);
break;
@@ -78,6 +78,7 @@ bool do_process_c007_delay_queue(int controller_number, const C007_queue_element
Serial.println(url);
return send_via_http(controller_number, client,
create_http_get_request(controller_number, ControllerSettings, url));
create_http_get_request(controller_number, ControllerSettings, url),
ControllerSettings.MustCheckReply);
}
#endif
+4 -4
View File
@@ -34,7 +34,7 @@ boolean CPlugin_008(byte function, struct EventStruct *event, String& string)
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C008_DelayHandler.configureControllerSettings(ControllerSettings);
break;
@@ -52,10 +52,10 @@ boolean CPlugin_008(byte function, struct EventStruct *event, String& string)
// Collect the values at the same run, to make sure all are from the same sample
byte valueCount = getValueCountFromSensorType(event->sensorType);
C008_queue_element element(event, valueCount);
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
for (byte x = 0; x < valueCount; x++)
@@ -97,7 +97,7 @@ bool do_process_c008_delay_queue(int controller_number, const C008_queue_element
return false;
String request = create_http_request_auth(controller_number, element.controller_idx, ControllerSettings, F("GET"), element.txt[element.valuesSent]);
return element.checkDone(send_via_http(controller_number, client, request));
return element.checkDone(send_via_http(controller_number, client, request, ControllerSettings.MustCheckReply));
}
#endif
+57 -53
View File
@@ -57,60 +57,17 @@ boolean CPlugin_009(byte function, struct EventStruct *event, String& string)
case CPLUGIN_PROTOCOL_SEND:
{
if (!WiFiConnected(100)) {
success = false;
break;
}
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
// We now create a URI for the request
String url = F("/ESPEasy");
// Create json root object
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root[F("module")] = String(F("ESPEasy"));
root[F("version")] = String(F("1.04"));
// Create nested objects
JsonObject& data = root.createNestedObject(String(F("data")));
JsonObject& ESP = data.createNestedObject(String(F("ESP")));
ESP[F("name")] = Settings.Name;
ESP[F("unit")] = Settings.Unit;
ESP[F("version")] = Settings.Version;
ESP[F("build")] = Settings.Build;
ESP[F("build_notes")] = String(F(BUILD_NOTES));
ESP[F("build_git")] = String(F(BUILD_GIT));
ESP[F("node_type_id")] = NODE_TYPE_ID;
ESP[F("sleep")] = Settings.deepSleep;
// embed IP, important if there is NAT/PAT
// char ipStr[20];
// IPAddress ip = WiFi.localIP();
// sprintf_P(ipStr, PSTR("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]);
ESP[F("ip")] = WiFi.localIP().toString();
// Create nested SENSOR json object
JsonObject& SENSOR = data.createNestedObject(String(F("SENSOR")));
byte valueCount = getValueCountFromSensorType(event->sensorType);
// char itemNames[valueCount][2];
C009_queue_element element(event);
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
for (byte x = 0; x < valueCount; x++)
{
// Each sensor value get an own object (0..n)
// sprintf(itemNames[x],"%d",x);
JsonObject& val = SENSOR.createNestedObject(String(x));
val[F("deviceName")] = getTaskDeviceName(event->TaskIndex);
val[F("valueName")] = ExtraTaskSettings.TaskDeviceValueNames[x];
val[F("type")] = event->sensorType;
val[F("value")] = formatUserVarNoCheck(event, x);
element.txt[x] = formatUserVarNoCheck(event, x);
}
// Create json buffer
String jsonString;
root.printTo(jsonString);
success = C009_DelayHandler.addToQueue(C009_queue_element(event->ControllerIndex, url, jsonString));
success = C009_DelayHandler.addToQueue(element);
scheduleNextDelayQueue(TIMER_C009_DELAY_QUEUE, C009_DelayHandler.getNextScheduleTime());
break;
}
@@ -126,11 +83,58 @@ bool do_process_c009_delay_queue(int controller_number, const C009_queue_element
if (!try_connect_host(controller_number, client, ControllerSettings))
return false;
LoadTaskSettings(element.TaskIndex);
String jsonString;
{
// Create json root object
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root[F("module")] = String(F("ESPEasy"));
root[F("version")] = String(F("1.04"));
// Create nested objects
JsonObject& data = root.createNestedObject(String(F("data")));
JsonObject& ESP = data.createNestedObject(String(F("ESP")));
ESP[F("name")] = Settings.Name;
ESP[F("unit")] = Settings.Unit;
ESP[F("version")] = Settings.Version;
ESP[F("build")] = Settings.Build;
ESP[F("build_notes")] = String(F(BUILD_NOTES));
ESP[F("build_git")] = String(F(BUILD_GIT));
ESP[F("node_type_id")] = NODE_TYPE_ID;
ESP[F("sleep")] = Settings.deepSleep;
// embed IP, important if there is NAT/PAT
// char ipStr[20];
// IPAddress ip = WiFi.localIP();
// sprintf_P(ipStr, PSTR("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]);
ESP[F("ip")] = WiFi.localIP().toString();
// Create nested SENSOR json object
JsonObject& SENSOR = data.createNestedObject(String(F("SENSOR")));
byte valueCount = getValueCountFromSensorType(element.sensorType);
// char itemNames[valueCount][2];
for (byte x = 0; x < valueCount; x++)
{
// Each sensor value get an own object (0..n)
// sprintf(itemNames[x],"%d",x);
JsonObject& val = SENSOR.createNestedObject(String(x));
val[F("deviceName")] = getTaskDeviceName(element.TaskIndex);
val[F("valueName")] = ExtraTaskSettings.TaskDeviceValueNames[x];
val[F("type")] = element.sensorType;
val[F("value")] = element.txt[x];
}
// Create json buffer
root.printTo(jsonString);
}
// We now create a URI for the request
String request = create_http_request_auth(
controller_number, element.controller_idx, ControllerSettings,
F("POST"), element.url, element.json.length());
request += element.json;
F("POST"), F("/ESPEasy"), jsonString.length());
request += jsonString;
return send_via_http(controller_number, client, request);
return send_via_http(controller_number, client, request, ControllerSettings.MustCheckReply);
}
#endif
+11 -6
View File
@@ -42,10 +42,10 @@ boolean CPlugin_010(byte function, struct EventStruct *event, String& string)
{
byte valueCount = getValueCountFromSensorType(event->sensorType);
C010_queue_element element(event, valueCount);
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
for (byte x = 0; x < valueCount; x++)
@@ -85,13 +85,18 @@ bool do_process_c010_delay_queue(int controller_number, const C010_queue_element
if (element.checkDone(true))
return true;
}
if (!try_connect_host(controller_number, portUDP, ControllerSettings))
WiFiUDP C010_portUDP;
if (!beginWiFiUDP_randomPort(C010_portUDP)) return false;
if (!try_connect_host(controller_number, C010_portUDP, ControllerSettings))
return false;
portUDP.write(
C010_portUDP.write(
(uint8_t*)element.txt[element.valuesSent].c_str(),
element.txt[element.valuesSent].length());
return element.checkDone(portUDP.endPacket() != 0);
bool reply = C010_portUDP.endPacket();
C010_portUDP.stop();
if (ControllerSettings.MustCheckReply)
return element.checkDone(reply);
return element.checkDone(true);
}
#endif
+3 -3
View File
@@ -133,7 +133,7 @@ bool do_process_c011_delay_queue(int controller_number, const C011_queue_element
if (!try_connect_host(controller_number, client, ControllerSettings))
return false;
return send_via_http(controller_number, client, element.txt);
return send_via_http(controller_number, client, element.txt, ControllerSettings.MustCheckReply);
}
@@ -147,7 +147,7 @@ boolean Create_schedule_HTTP_C011(struct EventStruct *event)
if (!WiFiConnected(100)) {
return false;
}
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C011_ConfigStruct customConfig;
@@ -158,7 +158,7 @@ boolean Create_schedule_HTTP_C011(struct EventStruct *event)
if (!try_connect_host(controller_number, client, ControllerSettings))
return false;
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
String payload = create_http_request_auth(
+49 -50
View File
@@ -37,10 +37,10 @@ boolean CPlugin_012(byte function, struct EventStruct *event, String& string)
// Collect the values at the same run, to make sure all are from the same sample
byte valueCount = getValueCountFromSensorType(event->sensorType);
C012_queue_element element(event, valueCount);
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
for (byte x = 0; x < valueCount; x++)
@@ -73,16 +73,15 @@ bool do_process_c012_delay_queue(int controller_number, const C012_queue_element
if (element.checkDone(true))
return true;
}
if (wifiStatus != ESPEASY_WIFI_SERVICES_INITIALIZED) {
return false;
}
return element.checkDone(Blynk_get(element.txt[element.valuesSent], element.controller_idx));
}
boolean Blynk_get(const String& command, byte controllerIndex, float *data )
{
if (wifiStatus != ESPEASY_WIFI_SERVICES_INITIALIZED) {
return false;
}
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(controllerIndex, ControllerSettings);
if ((SecuritySettings.ControllerPassword[controllerIndex][0] == 0)) {
@@ -104,58 +103,58 @@ boolean Blynk_get(const String& command, byte controllerIndex, float *data )
ControllerSettings.getHost().c_str());
addLog(LOG_LEVEL_DEBUG, request);
client.print(request);
boolean success = !ControllerSettings.MustCheckReply;
if (ControllerSettings.MustCheckReply || data) {
unsigned long timer = millis() + 200;
while (!client_available(client) && !timeOutReached(timer))
delay(1);
unsigned long timer = millis() + 200;
while (!client.available() && !timeOutReached(timer))
yield();
boolean success = false;
char log[80] = {0};
// Read all the lines of the reply from server and log them
while (client_available(client)) {
String line;
safeReadStringUntil(client, line, '\n');
addLog(LOG_LEVEL_DEBUG_MORE, line);
// success ?
if (line.substring(0, 15) == F("HTTP/1.1 200 OK")) {
strcpy_P(log, PSTR("HTTP : Success"));
success = true;
}
else if (line.substring(0, 24) == F("HTTP/1.1 400 Bad Request")) {
strcpy_P(log, PSTR("HTTP : Unauthorized"));
}
else if (line.substring(0, 25) == F("HTTP/1.1 401 Unauthorized")) {
strcpy_P(log, PSTR("HTTP : Unauthorized"));
}
addLog(LOG_LEVEL_DEBUG, log);
// data only
if (data && line.startsWith("["))
{
String strValue = line;
byte pos = strValue.indexOf('"',2);
strValue = strValue.substring(2, pos);
strValue.trim();
float value = strValue.toFloat();
*data = value;
success = true;
char value_char[5] = {0};
strValue.toCharArray(value_char, 5);
sprintf_P(log, PSTR("Blynk get - %s => %s"),command.c_str(), value_char );
char log[80] = {0};
timer = millis() + 1500;
// Read all the lines of the reply from server and log them
while (client_available(client) && !success && !timeOutReached(timer)) {
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"));
if (!data) success = true;
}
else if (line.substring(0, 24) == F("HTTP/1.1 400 Bad Request")) {
strcpy_P(log, PSTR("HTTP : Unauthorized"));
}
else if (line.substring(0, 25) == F("HTTP/1.1 401 Unauthorized")) {
strcpy_P(log, PSTR("HTTP : Unauthorized"));
}
addLog(LOG_LEVEL_DEBUG, log);
// data only
if (data && line.startsWith("["))
{
String strValue = line;
byte pos = strValue.indexOf('"',2);
strValue = strValue.substring(2, pos);
strValue.trim();
float value = strValue.toFloat();
*data = value;
success = true;
char value_char[5] = {0};
strValue.toCharArray(value_char, 5);
sprintf_P(log, PSTR("Blynk get - %s => %s"),command.c_str(), value_char );
addLog(LOG_LEVEL_DEBUG, log);
}
delay(0);
}
yield();
}
strcpy_P(log, PSTR("HTTP : closing connection (012)"));
addLog(LOG_LEVEL_DEBUG, log);
addLog(LOG_LEVEL_DEBUG, F("HTTP : closing connection (012)"));
client.flush();
client.stop();
// important - backgroundtasks - free mem
timer = millis() + Settings.MessageDelay;
unsigned long timer = millis() + Settings.MessageDelay;
while (!timeOutReached(timer))
backgroundtasks();
+101 -78
View File
@@ -126,10 +126,10 @@ public:
return total;
}
String txt[VARS_PER_TASK];
int controller_idx;
byte TaskIndex;
int idx;
String txt[VARS_PER_TASK];
mutable byte valuesSent; // Value must be set by const function checkDone()
byte valueCount;
};
@@ -146,19 +146,29 @@ public:
\*********************************************************************************************/
class C009_queue_element {
public:
C009_queue_element() : controller_idx(0) {}
C009_queue_element(int ctrl_idx, const String& URI, const String& JSON) :
controller_idx(ctrl_idx), url(URI), json(JSON) {}
C009_queue_element() : controller_idx(0), TaskIndex(0), idx(0), sensorType(0) {}
C009_queue_element(const struct EventStruct* event) :
controller_idx(event->ControllerIndex),
TaskIndex(event->TaskIndex),
idx(event->idx),
sensorType(event->sensorType) {}
size_t getSize() const {
return sizeof(this) + url.length() + json.length();
size_t total = sizeof(this);
for (int i = 0; i < VARS_PER_TASK; ++i) {
total += txt[i].length();
}
return total;
}
String txt[VARS_PER_TASK];
int controller_idx;
String url;
String json;
byte TaskIndex;
int idx;
byte sensorType;
};
/*********************************************************************************************\
* C010_queue_element for queueing requests for 010: Generic UDP
* Using queue_element_single_value_base
@@ -189,13 +199,15 @@ struct ControllerDelayHandlerStruct {
max_queue_depth(CONTROLLER_DELAY_QUEUE_DEPTH_DFLT),
attempt(0),
max_retries(CONTROLLER_DELAY_QUEUE_RETRY_DFLT),
delete_oldest(false) {}
delete_oldest(false),
must_check_reply(false) {}
void configureControllerSettings(const ControllerSettingsStruct& settings) {
minTimeBetweenMessages = settings.MinimalTimeBetweenMessages;
max_queue_depth = settings.MaxQueueDepth;
max_retries = settings.MaxRetry;
delete_oldest = settings.DeleteOldest;
must_check_reply = settings.MustCheckReply;
// Set some sound limits when not configured
if (max_queue_depth == 0) max_queue_depth = CONTROLLER_DELAY_QUEUE_DEPTH_DFLT;
if (max_retries == 0) max_retries = CONTROLLER_DELAY_QUEUE_RETRY_DFLT;
@@ -251,15 +263,14 @@ struct ControllerDelayHandlerStruct {
// Get the next element.
// Remove front element when max_retries is reached.
bool getNext(T& element) {
if (sendQueue.empty()) return false;
T* getNext() {
if (sendQueue.empty()) return NULL;
if (attempt > max_retries) {
sendQueue.pop_front();
attempt = 0;
if (sendQueue.empty()) return false;
if (sendQueue.empty()) return NULL;
}
element = sendQueue.front();
return true;
return &sendQueue.front();
}
// Mark as processed and return time to schedule for next process.
@@ -302,6 +313,7 @@ struct ControllerDelayHandlerStruct {
byte attempt;
byte max_retries;
bool delete_oldest;
bool must_check_reply;
};
ControllerDelayHandlerStruct<MQTT_queue_element> MQTTDelayHandler;
@@ -321,17 +333,17 @@ ControllerDelayHandlerStruct<MQTT_queue_element> MQTTDelayHandler;
ControllerDelayHandlerStruct<C##NNN##_queue_element> C##NNN##_DelayHandler; \
bool do_process_c##NNN##_delay_queue(int controller_number, const C##NNN##_queue_element& element, ControllerSettingsStruct& ControllerSettings); \
void process_c##NNN##_delay_queue() { \
C##NNN##_queue_element element; \
if (!C##NNN##_DelayHandler.getNext(element)) return; \
ControllerSettingsStruct ControllerSettings; \
LoadControllerSettings(element.controller_idx, ControllerSettings); \
C##NNN##_queue_element* element(C##NNN##_DelayHandler.getNext()); \
if (element == NULL) return; \
MakeControllerSettings(ControllerSettings); \
LoadControllerSettings(element->controller_idx, ControllerSettings); \
C##NNN##_DelayHandler.configureControllerSettings(ControllerSettings); \
if (!WiFiConnected(100)) { \
scheduleNextDelayQueue(TIMER_C##NNN##_DELAY_QUEUE, C##NNN##_DelayHandler.getNextScheduleTime()); \
return; \
} \
START_TIMER; \
C##NNN##_DelayHandler.markProcessed(do_process_c##NNN##_delay_queue(M, element, ControllerSettings)); \
C##NNN##_DelayHandler.markProcessed(do_process_c##NNN##_delay_queue(M, *element, ControllerSettings)); \
STOP_TIMER(C##NNN##_DELAY_QUEUE); \
scheduleNextDelayQueue(TIMER_C##NNN##_DELAY_QUEUE, C##NNN##_DelayHandler.getNextScheduleTime()); \
}
@@ -382,33 +394,37 @@ bool safeReadStringUntil(Stream &input, String &str, char terminator, unsigned i
int c;
const unsigned long start = millis();
const unsigned long timer = start + timeout;
unsigned long backgroundtasks_timer = start + 100;
unsigned long backgroundtasks_timer = start + 10;
str = "";
do {
//read character
c = input.read();
if (c >= 0) {
//found terminator, we're ok
if (c == terminator) {
return(true);
}
//found character, add to string
else{
str += char(c);
//string at max size?
if (str.length() >= maxSize) {
addLog(LOG_LEVEL_ERROR, F("Not enough bufferspace to read all input data!"));
return(false);
}
}
}
// We must run the backgroundtasks every now and then.
if (timeOutReached(backgroundtasks_timer)) {
backgroundtasks_timer += 100;
backgroundtasks();
if (input.available()) {
c = input.read();
if (c >= 0) {
//found terminator, we're ok
if (c == terminator) {
return(true);
}
//found character, add to string
else{
str += char(c);
//string at max size?
if (str.length() >= maxSize) {
addLog(LOG_LEVEL_ERROR, F("Not enough bufferspace to read all input data!"));
return(false);
}
}
}
// We must run the backgroundtasks every now and then.
if (timeOutReached(backgroundtasks_timer)) {
backgroundtasks_timer += 10;
backgroundtasks();
} else {
delay(0);
}
} else {
yield();
delay(0);
}
} while (!timeOutReached(timer));
@@ -584,6 +600,7 @@ bool count_connection_results(bool success, const String& prefix, int controller
}
bool try_connect_host(int controller_number, WiFiUDP& client, ControllerSettingsStruct& ControllerSettings) {
client.setTimeout(ControllerSettings.ClientTimeout);
log_connecting_to(F("UDP : "), controller_number, ControllerSettings);
bool success = ControllerSettings.beginPacket(client) != 0;
return count_connection_results(
@@ -593,6 +610,7 @@ bool try_connect_host(int controller_number, WiFiUDP& client, ControllerSettings
bool try_connect_host(int controller_number, WiFiClient& client, ControllerSettingsStruct& ControllerSettings) {
// Use WiFiClient class to create TCP connections
client.setTimeout(ControllerSettings.ClientTimeout);
log_connecting_to(F("HTTP : "), controller_number, ControllerSettings);
bool success = ControllerSettings.connectToHost(client);
return count_connection_results(
@@ -604,52 +622,57 @@ bool try_connect_host(int controller_number, WiFiClient& client, ControllerSetti
// See: https://github.com/esp8266/Arduino/pull/5113
// https://github.com/esp8266/Arduino/pull/1829
bool client_available(WiFiClient& client) {
delay(0);
return client.available() || client.connected();
}
bool send_via_http(const String& logIdentifier, WiFiClient& client, const String& postStr) {
bool success = false;
bool send_via_http(const String& logIdentifier, WiFiClient& client, const String& postStr, bool must_check_reply) {
bool success = !must_check_reply;
// This will send the request to the server
client.print(postStr);
unsigned long timer = millis() + 200;
while (!client.available() && !timeOutReached(timer))
delay(1);
// Read all the lines of the reply from server and print them to Serial
while (client_available(client) && !success) {
// String line = client.readStringUntil('\n');
String line;
safeReadStringUntil(client, line, '\n');
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
if (line.length() > 80) {
addLog(LOG_LEVEL_DEBUG_MORE, line.substring(0, 80));
} else {
addLog(LOG_LEVEL_DEBUG_MORE, line);
}
if (must_check_reply) {
unsigned long timer = millis() + 200;
while (!client_available(client)) {
if (timeOutReached(timer)) return false;
delay(1);
}
if (line.startsWith(F("HTTP/1.1 2")))
{
success = true;
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("HTTP : ");
log += logIdentifier;
log += F(" Success! ");
log += line;
addLog(LOG_LEVEL_DEBUG, log);
// Read all the lines of the reply from server and print them to Serial
while (client_available(client) && !success) {
// String line = client.readStringUntil('\n');
String line;
safeReadStringUntil(client, line, '\n');
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
if (line.length() > 80) {
addLog(LOG_LEVEL_DEBUG_MORE, line.substring(0, 80));
} else {
addLog(LOG_LEVEL_DEBUG_MORE, line);
}
}
} else if (line.startsWith(F("HTTP/1.1 4"))) {
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
String log = F("HTTP : ");
log += logIdentifier;
log += F(" Error: ");
log += line;
addLog(LOG_LEVEL_ERROR, log);
if (line.startsWith(F("HTTP/1.1 2")))
{
success = true;
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("HTTP : ");
log += logIdentifier;
log += F(" Success! ");
log += line;
addLog(LOG_LEVEL_DEBUG, log);
}
} else if (line.startsWith(F("HTTP/1.1 4"))) {
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
String log = F("HTTP : ");
log += logIdentifier;
log += F(" Error: ");
log += line;
addLog(LOG_LEVEL_ERROR, log);
}
addLog(LOG_LEVEL_DEBUG_MORE, postStr);
}
addLog(LOG_LEVEL_DEBUG_MORE, postStr);
delay(0);
}
yield();
}
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("HTTP : ");
@@ -663,8 +686,8 @@ bool send_via_http(const String& logIdentifier, WiFiClient& client, const String
return success;
}
bool send_via_http(int controller_number, WiFiClient& client, const String& postStr) {
return send_via_http(get_formatted_Controller_number(controller_number), client, postStr);
bool send_via_http(int controller_number, WiFiClient& client, const String& postStr, bool must_check_reply) {
return send_via_http(get_formatted_Controller_number(controller_number), client, postStr, must_check_reply);
}
+6 -5
View File
@@ -38,8 +38,8 @@ boolean NPlugin_001(byte function, struct EventStruct *event, String& string)
//
// if (command == F("email"))
// {
// NotificationSettingsStruct NotificationSettings;
// LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettings));
// MakeNotificationSettings(NotificationSettings);
// LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettingsStruct));
// NPlugin_001_send(NotificationSettings.Domain, NotificationSettings.Receiver, NotificationSettings.Sender, NotificationSettings.Subject, NotificationSettings.Body, NotificationSettings.Server, NotificationSettings.Port);
// success = true;
// }
@@ -48,8 +48,8 @@ boolean NPlugin_001(byte function, struct EventStruct *event, String& string)
case NPLUGIN_NOTIFY:
{
NotificationSettingsStruct NotificationSettings;
LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettings));
MakeNotificationSettings(NotificationSettings);
LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettingsStruct));
String subject = NotificationSettings.Subject;
String body = "";
if (event->String1.length() > 0)
@@ -73,6 +73,7 @@ boolean NPlugin_001_send(const NotificationSettingsStruct& notificationsettings,
// Use WiFiClient class to create TCP connections
WiFiClient client;
client.setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT);
String aHost = notificationsettings.Server;
addLog(LOG_LEVEL_DEBUG, String(F("EMAIL: Connecting to ")) + aHost + notificationsettings.Port);
if (client.connect(aHost.c_str(), notificationsettings.Port) != 1) {
@@ -193,7 +194,7 @@ boolean NPlugin_001_MTA(WiFiClient& client, String aStr, const String &aWaitForP
return false;
}
yield();
delay(0);
// String line = client.readStringUntil('\n');
String line;
+4 -4
View File
@@ -35,8 +35,8 @@ boolean NPlugin_002(byte function, struct EventStruct *event, String& string)
//
// if (command == F("buzzer"))
// {
// NotificationSettingsStruct NotificationSettings;
// LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettings));
// MakeNotificationSettings(NotificationSettings);
// LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettingsStruct));
// success = true;
// }
// break;
@@ -44,8 +44,8 @@ boolean NPlugin_002(byte function, struct EventStruct *event, String& string)
case NPLUGIN_NOTIFY:
{
NotificationSettingsStruct NotificationSettings;
LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettings));
MakeNotificationSettings(NotificationSettings);
LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettingsStruct));
//this reserves IRAM and uninitialized RAM
#ifndef ESP32
// Buzzer not compatible with ESP32 due to lack of tone command.
+334 -48
View File
@@ -3,6 +3,31 @@
//#################################### Plugin 001: Input Switch #########################################
//#######################################################################################################
/**************************************************\
CONFIG
TaskDevicePluginConfig settings:
0: button type (switch or dimmer)
1: dim value
2: button option (normal, push high, push low)
3: send boot state (true,false)
4: use doubleclick (0,1,2,3)
5: use longpress (0,1,2,3)
6: LP fired (true,false)
7: doubleclick counter (=0,1,2,3)
TaskDevicePluginConfigFloat settings:
0: debounce interval ms
1: doubleclick interval ms
2: longpress interval ms
3:
TaskDevicePluginConfigLong settings:
0: clickTime debounce ms
1: clickTime doubleclick ms
2: clickTime longpress ms
3:
\**************************************************/
#define PLUGIN_001
#define PLUGIN_ID_001 1
#define PLUGIN_NAME_001 "Switch input - Switch"
@@ -18,6 +43,18 @@
#define PLUGIN_001_BUTTON_TYPE_NORMAL_SWITCH 0
#define PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW 1
#define PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_HIGH 2
#define PLUGIN_001_DOUBLECLICK_MIN_INTERVAL 1000
#define PLUGIN_001_DOUBLECLICK_MAX_INTERVAL 3000
#define PLUGIN_001_LONGPRESS_MIN_INTERVAL 1000
#define PLUGIN_001_LONGPRESS_MAX_INTERVAL 5000
#define PLUGIN_001_DC_DISABLED 0
#define PLUGIN_001_DC_LOW 1
#define PLUGIN_001_DC_HIGH 2
#define PLUGIN_001_DC_BOTH 3
#define PLUGIN_001_LONGPRESS_DISABLED 0
#define PLUGIN_001_LONGPRESS_LOW 1
#define PLUGIN_001_LONGPRESS_HIGH 2
#define PLUGIN_001_LONGPRESS_BOTH 3
boolean Plugin_001_read_switch_state(struct EventStruct *event) {
return digitalRead(Settings.TaskDevicePin1[event->TaskIndex]) == HIGH;
@@ -26,8 +63,8 @@ boolean Plugin_001_read_switch_state(struct EventStruct *event) {
boolean Plugin_001(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
static boolean switchstate[TASKS_MAX];
static boolean outputstate[TASKS_MAX];
static byte switchstate[TASKS_MAX];
static byte outputstate[TASKS_MAX];
static int8_t PinMonitor[GPIO_MAX];
static int8_t PinMonitorState[GPIO_MAX];
@@ -89,6 +126,47 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
addFormCheckBox(F("Send Boot state"),F("plugin_001_boot"),
Settings.TaskDevicePluginConfig[event->TaskIndex][3]);
addFormSubHeader(F("Advanced event management"));
addFormNumericBox(F("De-bounce (ms)"), F("plugin_001_debounce"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0]), 0, 250);
//set minimum value for doubleclick MIN max speed
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_001_DOUBLECLICK_MIN_INTERVAL)
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_001_DOUBLECLICK_MIN_INTERVAL;
byte choiceDC = Settings.TaskDevicePluginConfig[event->TaskIndex][4];
String buttonDC[4];
buttonDC[0] = F("Disabled");
buttonDC[1] = F("Active only on LOW (EVENT=3)");
buttonDC[2] = F("Active only on HIGH (EVENT=3)");
buttonDC[3] = F("Active on LOW & HIGH (EVENT=3)");
int buttonDCValues[4] = {PLUGIN_001_DC_DISABLED, PLUGIN_001_DC_LOW, PLUGIN_001_DC_HIGH,PLUGIN_001_DC_BOTH};
addFormSelector(F("Doubleclick event"), F("plugin_001_dc"), 4, buttonDC, buttonDCValues, choiceDC);
//addFormCheckBox(F("Doubleclick event (3)"), F("plugin_001_dc"), Settings.TaskDevicePluginConfig[event->TaskIndex][4]);
addFormNumericBox(F("Doubleclick max. interval (ms)"), F("plugin_001_dcmaxinterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1]), PLUGIN_001_DOUBLECLICK_MIN_INTERVAL, PLUGIN_001_DOUBLECLICK_MAX_INTERVAL);
//set minimum value for longpress MIN max speed
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_001_LONGPRESS_MIN_INTERVAL)
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_001_LONGPRESS_MIN_INTERVAL;
byte choiceLP = Settings.TaskDevicePluginConfig[event->TaskIndex][5];
String buttonLP[4];
buttonLP[0] = F("Disabled");
buttonLP[1] = F("Active only on LOW (EVENT= 10 [NORMAL] or 11 [INVERSED])");
buttonLP[2] = F("Active only on HIGH (EVENT= 11 [NORMAL] or 10 [INVERSED])");
buttonLP[3] = F("Active on LOW & HIGH (EVENT= 10 or 11)");
int buttonLPValues[4] = {PLUGIN_001_LONGPRESS_DISABLED, PLUGIN_001_LONGPRESS_LOW, PLUGIN_001_LONGPRESS_HIGH,PLUGIN_001_LONGPRESS_BOTH};
addFormSelector(F("Longpress event"), F("plugin_001_lp"), 4, buttonLP, buttonLPValues, choiceLP);
//addFormCheckBox(F("Longpress event (10 & 11)"), F("plugin_001_lp"), Settings.TaskDevicePluginConfig[event->TaskIndex][5]);
addFormNumericBox(F("Longpress min. interval (ms)"), F("plugin_001_lpmininterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]), PLUGIN_001_LONGPRESS_MIN_INTERVAL, PLUGIN_001_LONGPRESS_MAX_INTERVAL);
//TO-DO: add Extra-Long Press event
//addFormCheckBox(F("Extra-Longpress event (20 & 21)"), F("plugin_001_elp"), Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]);
//addFormNumericBox(F("Extra-Longpress min. interval (ms)"), F("plugin_001_elpmininterval"), Settings.TaskDevicePluginConfigLong[event->TaskIndex][2], 500, 2000);
success = true;
break;
}
@@ -105,6 +183,18 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
Settings.TaskDevicePluginConfig[event->TaskIndex][3] = isFormItemChecked(F("plugin_001_boot"));
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0] = getFormItemInt(F("plugin_001_debounce"));
Settings.TaskDevicePluginConfig[event->TaskIndex][4] = getFormItemInt(F("plugin_001_dc"));
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = getFormItemInt(F("plugin_001_dcmaxinterval"));
Settings.TaskDevicePluginConfig[event->TaskIndex][5] = getFormItemInt(F("plugin_001_lp"));
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = getFormItemInt(F("plugin_001_lpmininterval"));
//TO-DO: add Extra-Long Press event
//Settings.TaskDevicePluginConfigLong[event->TaskIndex][1] = isFormItemChecked(F("plugin_001_elp"));
//Settings.TaskDevicePluginConfigLong[event->TaskIndex][2] = getFormItemInt(F("plugin_001_elpmininterval"));
success = true;
break;
}
@@ -121,30 +211,62 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
else
pinMode(Settings.TaskDevicePin1[event->TaskIndex], INPUT);
// @giig1967g-20181022: if it is in the device list we assume it's an input pin
setPinState(PLUGIN_ID_001, Settings.TaskDevicePin1[event->TaskIndex], PIN_MODE_INPUT, 0);
// read and store current state to prevent switching at boot time
switchstate[event->TaskIndex] = Plugin_001_read_switch_state(event);
outputstate[event->TaskIndex] = switchstate[event->TaskIndex];
// if boot state must be send, inverse default state
// this is done to force the trigger in PLUGIN_TEN_PER_SECOND
if (Settings.TaskDevicePluginConfig[event->TaskIndex][3])
{
switchstate[event->TaskIndex] = !switchstate[event->TaskIndex];
outputstate[event->TaskIndex] = !outputstate[event->TaskIndex];
}
// set initial UserVar of the switch
if (Settings.TaskDevicePin1Inversed[event->TaskIndex]){
UserVar[event->BaseVarIndex] = !switchstate[event->TaskIndex];
} else {
UserVar[event->BaseVarIndex] = switchstate[event->TaskIndex];
}
// counter = 0
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0;
//used to track if LP has fired
Settings.TaskDevicePluginConfig[event->TaskIndex][6]=false;
//store millis for debounce, doubleclick and long press
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]=millis(); //debounce timer
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis(); //doubleclick timer
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis(); //longpress timer
//set minimum value for doubleclick MIN interval speed
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_001_DOUBLECLICK_MIN_INTERVAL)
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_001_DOUBLECLICK_MIN_INTERVAL;
//set minimum value for longpress MIN interval speed
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_001_LONGPRESS_MIN_INTERVAL)
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_001_LONGPRESS_MIN_INTERVAL;
success = true;
break;
}
case PLUGIN_REQUEST:
{
String device = parseString(string, 1);
String command = parseString(string, 2);
String strPar1 = parseString(string, 3);
if (device == F("gpio") && command == F("pinstate"))
//String device = parseString(string, 1);
//String command = parseString(string, 2);
//String strPar1 = parseString(string, 3);
// returns pin value using syntax: [plugin#gpio#pinstate#xx]
if (string.length()>=13 && string.substring(0,13).equalsIgnoreCase(F("gpio,pinstate")))
{
int par1;
if (validIntFromString(strPar1, par1)) {
if (validIntFromString(parseString(string, 3), par1)) {
string = digitalRead(par1);
}
success = true;
@@ -161,7 +283,7 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
if (PinMonitorState[x] != state){
String eventString = F("GPIO#");
eventString += x;
eventString += F("=");
eventString += '=';
eventString += state;
rulesProcessing(eventString);
PinMonitorState[x] = state;
@@ -173,51 +295,179 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
case PLUGIN_TEN_PER_SECOND:
{
const boolean state = Plugin_001_read_switch_state(event);
/**************************************************************************\
20181009 - @giig1967g: new doubleclick logic is:
if there is a 'state' change, check debounce period.
Then if doubleclick interval exceeded, reset Settings.TaskDevicePluginConfig[event->TaskIndex][7] to 0
Settings.TaskDevicePluginConfig[event->TaskIndex][7] contains the current status for doubleclick:
0: start counting
1: 1st click
2: 2nd click
3: 3rd click = doubleclick event if inside interval (calculated as: '3rd click time' minus '1st click time')
Returned EVENT value is = 3 always for doubleclick
In rules this can be checked:
on Button#Switch=3 do //will fire if doubleclick
\**************************************************************************/
if (state != switchstate[event->TaskIndex])
{
switchstate[event->TaskIndex] = state;
const boolean currentOutputState = outputstate[event->TaskIndex];
boolean new_outputState = currentOutputState;
switch(Settings.TaskDevicePluginConfig[event->TaskIndex][2]) {
case PLUGIN_001_BUTTON_TYPE_NORMAL_SWITCH:
new_outputState = state;
break;
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW:
if (!state)
new_outputState = !currentOutputState;
break;
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_HIGH:
if (state)
new_outputState = !currentOutputState;
break;
}
//reset timer for long press
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis();
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = false;
// send if output needs to be changed
if (currentOutputState != new_outputState)
const unsigned long debounceTime = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]);
if (debounceTime >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0])) //de-bounce check
{
outputstate[event->TaskIndex] = new_outputState;
boolean sendState = new_outputState;
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
sendState = !sendState;
byte output_value = sendState ? 1 : 0;
event->sensorType = SENSOR_TYPE_SWITCH;
if (P001_getSwitchType(event) == PLUGIN_001_TYPE_DIMMER) {
if (sendState) {
output_value = Settings.TaskDevicePluginConfig[event->TaskIndex][1];
// Only set type to being dimmer when setting a value else it is "switched off".
event->sensorType = SENSOR_TYPE_DIMMER;
}
const unsigned long deltaDC = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]);
if ((deltaDC >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1])) ||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3)
{
//reset timer for doubleclick
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0;
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis();
}
UserVar[event->BaseVarIndex] = output_value;
String log = F("SW : Switch state ");
log += state ? F("1") : F("0");
log += F(" Output value ");
log += output_value;
addLog(LOG_LEVEL_INFO, log);
sendData(event);
//just to simplify the reading of the code
#define COUNTER Settings.TaskDevicePluginConfig[event->TaskIndex][7]
#define DC Settings.TaskDevicePluginConfig[event->TaskIndex][4]
//check settings for doubleclick according to the settings
if ( COUNTER!=0 || ( COUNTER==0 && (DC==3 || (DC==1 && state==0) || (DC==2 && state==1))) )
Settings.TaskDevicePluginConfig[event->TaskIndex][7]++;
#undef DC
#undef COUNTER
switchstate[event->TaskIndex] = state;
const boolean currentOutputState = outputstate[event->TaskIndex];
boolean new_outputState = currentOutputState;
switch(Settings.TaskDevicePluginConfig[event->TaskIndex][2])
{
case PLUGIN_001_BUTTON_TYPE_NORMAL_SWITCH:
new_outputState = state;
break;
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW:
if (!state)
new_outputState = !currentOutputState;
break;
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_HIGH:
if (state)
new_outputState = !currentOutputState;
break;
}
// send if output needs to be changed
if (currentOutputState != new_outputState)
{
byte output_value;
outputstate[event->TaskIndex] = new_outputState;
boolean sendState = new_outputState;
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
sendState = !sendState;
if (Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3 && Settings.TaskDevicePluginConfig[event->TaskIndex][4]>0)
{
output_value = 3; //double click
} else {
output_value = sendState ? 1 : 0; //single click
}
event->sensorType = SENSOR_TYPE_SWITCH;
if (P001_getSwitchType(event) == PLUGIN_001_TYPE_DIMMER) {
if (sendState) {
output_value = Settings.TaskDevicePluginConfig[event->TaskIndex][1];
// Only set type to being dimmer when setting a value else it is "switched off".
event->sensorType = SENSOR_TYPE_DIMMER;
}
}
UserVar[event->BaseVarIndex] = output_value;
String log = F("SW : GPIO=");
log += Settings.TaskDevicePin1[event->TaskIndex];
log += F(" State=");
log += state ? '1' : '0';
log += output_value==3 ? F(" Doubleclick=") : F(" Output value=");
log += output_value;
addLog(LOG_LEVEL_INFO, log);
sendData(event);
//reset Userdata so it displays the correct state value in the web page
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
}
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0] = millis();
}
}
//just to simplify the reading of the code
#define LP Settings.TaskDevicePluginConfig[event->TaskIndex][5]
#define FIRED Settings.TaskDevicePluginConfig[event->TaskIndex][6]
//check if LP is enabled and if LP has not fired yet
else if (!FIRED && (LP==3 ||(LP==1 && state==0)||(LP==2 && state==1) ) ) {
#undef LP
#undef FIRED
/**************************************************************************\
20181009 - @giig1967g: new longpress logic is:
if there is no 'state' change, check if longpress interval reached
When reached send longpress event.
Returned Event value = state + 10
So if state = 0 => EVENT longpress = 10
if state = 1 => EVENT longpress = 11
So we can trigger longpress for high or low contact
In rules this can be checked:
on Button#Switch=10 do //will fire if longpress when state = 0
on Button#Switch=11 do //will fire if longpress when state = 1
\**************************************************************************/
const unsigned long deltaLP = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]);
if (deltaLP >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]))
{
byte output_value;
byte needToSendEvent = false;
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = true;
switch(Settings.TaskDevicePluginConfig[event->TaskIndex][2])
{
case PLUGIN_001_BUTTON_TYPE_NORMAL_SWITCH:
needToSendEvent = true;
break;
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW:
if (!state)
needToSendEvent = true;
break;
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_HIGH:
if (state)
needToSendEvent = true;
break;
}
if (needToSendEvent) {
boolean sendState = state;
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
sendState = !sendState;
output_value = sendState ? 1 : 0;
output_value = output_value + 10;
UserVar[event->BaseVarIndex] = output_value;
String log = F("SW : LongPress: GPIO= ");
log += Settings.TaskDevicePin1[event->TaskIndex];
log += F(" State=");
log += state ? '1' : '0';
log += F(" Output value=");
log += output_value;
addLog(LOG_LEVEL_INFO, log);
sendData(event);
//reset Userdata so it displays the correct state value in the web page
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
}
}
}
success = true;
break;
}
@@ -257,6 +507,32 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
}
}
if (command == F("gpiotoggle"))
{
success = true;
byte mode;
uint16_t currentState;
if (event->Par1 >= 0 && event->Par1 <= PIN_D_MAX)
{
if (hasPinState(PLUGIN_ID_001,event->Par1)) {
getPinState(PLUGIN_ID_001, event->Par1, &mode, &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;
@@ -352,14 +628,24 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
//IRAM: doing servo stuff uses 740 bytes IRAM. (doesnt matter how many instances)
#if defined(ESP8266)
servo1.attach(event->Par2);
servo1.write(event->Par3);
//SPECIAL CASE TO ALLOW SERVO TO BE DETATTCHED AND SAVE POWER.
if (event->Par3 >= 9000) {
servo1.detach();
}else{
servo1.attach(event->Par2);
servo1.write(event->Par3);
}
#endif
break;
case 2:
#if defined(ESP8266)
if (event->Par3 >= 9000) {
servo2.detach();
}else{
servo2.attach(event->Par2);
servo2.write(event->Par3);
}
#endif
break;
}
+25 -8
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);
}
@@ -244,8 +244,25 @@ boolean Plugin_004_DS_readTemp(uint8_t ROM[8], float * value)
for (byte i = 0; i < 9; i++) // read 9 bytes
ScratchPad[i] = Plugin_004_DS_read();
bool crc_ok = Plugin_004_DS_crc8(ScratchPad);
if (!Plugin_004_DS_crc8(ScratchPad))
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("DS: SP: ");
for (byte x = 0; x < 9; x++)
{
if (x != 0)
log += ',';
log += String(ScratchPad[x], HEX);
}
if (crc_ok)
log += F(",OK");
addLog(LOG_LEVEL_DEBUG, log);
}
if (!crc_ok)
{
*value = 0;
return false;
@@ -397,11 +414,11 @@ uint8_t Plugin_004_DS_reset()
while (!digitalRead(Plugin_004_DallasPin));
pinMode(Plugin_004_DallasPin, OUTPUT); digitalWrite(Plugin_004_DallasPin, LOW);
delayMicroseconds(492); // Dallas spec. = Min. 480uSec. Arduino 500uSec.
delayMicroseconds(480); // Dallas spec. = Min. 480uSec. Arduino 500uSec.
pinMode(Plugin_004_DallasPin, INPUT); // Float
delayMicroseconds(40);
delayMicroseconds(70);
r = !digitalRead(Plugin_004_DallasPin);
delayMicroseconds(420);
delayMicroseconds(410);
#if defined(ESP32)
ESP32interrupts();
#endif
+142 -116
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;
}
+292 -15
View File
@@ -3,15 +3,52 @@
//#################################### Plugin 009: MCP23017 input #######################################
//#######################################################################################################
/**************************************************\
CONFIG
TaskDevicePluginConfig settings:
0: send boot state (true,false)
1:
2:
3:
4: use doubleclick (0,1,2,3)
5: use longpress (0,1,2,3)
6: LP fired (true,false)
7: doubleclick counter (=0,1,2,3)
TaskDevicePluginConfigFloat settings:
0: debounce interval ms
1: doubleclick interval ms
2: longpress interval ms
3:
TaskDevicePluginConfigLong settings:
0: clickTime debounce ms
1: clickTime doubleclick ms
2: clickTime longpress ms
3:
\**************************************************/
#define PLUGIN_009
#define PLUGIN_ID_009 9
#define PLUGIN_NAME_009 "Switch input - MCP23017"
#define PLUGIN_VALUENAME1_009 "Switch"
#define PLUGIN_009_DOUBLECLICK_MIN_INTERVAL 1000
#define PLUGIN_009_DOUBLECLICK_MAX_INTERVAL 3000
#define PLUGIN_009_LONGPRESS_MIN_INTERVAL 1000
#define PLUGIN_009_LONGPRESS_MAX_INTERVAL 5000
#define PLUGIN_009_DC_DISABLED 0
#define PLUGIN_009_DC_LOW 1
#define PLUGIN_009_DC_HIGH 2
#define PLUGIN_009_DC_BOTH 3
#define PLUGIN_009_LONGPRESS_DISABLED 0
#define PLUGIN_009_LONGPRESS_LOW 1
#define PLUGIN_009_LONGPRESS_HIGH 2
#define PLUGIN_009_LONGPRESS_BOTH 3
boolean Plugin_009(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
static byte switchstate[TASKS_MAX];
static int8_t switchstate[TASKS_MAX];
switch (function)
{
@@ -23,7 +60,7 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
Device[deviceCount].VType = SENSOR_TYPE_SWITCH;
Device[deviceCount].Ports = 16;
Device[deviceCount].PullUpOption = false;
Device[deviceCount].InverseLogicOption = false;
Device[deviceCount].InverseLogicOption = true;
Device[deviceCount].FormulaOption = false;
Device[deviceCount].ValueCount = 1;
Device[deviceCount].SendDataOption = true;
@@ -49,6 +86,42 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
{
addFormCheckBox(F("Send Boot state") ,F("plugin_009_boot"), Settings.TaskDevicePluginConfig[event->TaskIndex][0]);
//@giig1967-20181022
addFormSubHeader(F("Advanced event management"));
addFormNumericBox(F("De-bounce (ms)"), F("plugin_009_debounce"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0]), 0, 250);
//set minimum value for doubleclick MIN max speed
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_009_DOUBLECLICK_MIN_INTERVAL)
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_009_DOUBLECLICK_MIN_INTERVAL;
byte choiceDC = Settings.TaskDevicePluginConfig[event->TaskIndex][4];
String buttonDC[4];
buttonDC[0] = F("Disabled");
buttonDC[1] = F("Active only on LOW (EVENT=3)");
buttonDC[2] = F("Active only on HIGH (EVENT=3)");
buttonDC[3] = F("Active on LOW & HIGH (EVENT=3)");
int buttonDCValues[4] = {PLUGIN_009_DC_DISABLED, PLUGIN_009_DC_LOW, PLUGIN_009_DC_HIGH,PLUGIN_009_DC_BOTH};
addFormSelector(F("Doubleclick event"), F("plugin_009_dc"), 4, buttonDC, buttonDCValues, choiceDC);
addFormNumericBox(F("Doubleclick max. interval (ms)"), F("plugin_009_dcmaxinterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1]), PLUGIN_009_DOUBLECLICK_MIN_INTERVAL, PLUGIN_009_DOUBLECLICK_MAX_INTERVAL);
//set minimum value for longpress MIN max speed
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_009_LONGPRESS_MIN_INTERVAL)
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_009_LONGPRESS_MIN_INTERVAL;
byte choiceLP = Settings.TaskDevicePluginConfig[event->TaskIndex][5];
String buttonLP[4];
buttonLP[0] = F("Disabled");
buttonLP[1] = F("Active only on LOW (EVENT= 10 [NORMAL] or 11 [INVERSED])");
buttonLP[2] = F("Active only on HIGH (EVENT= 11 [NORMAL] or 10 [INVERSED])");
buttonLP[3] = F("Active on LOW & HIGH (EVENT= 10 or 11)");
int buttonLPValues[4] = {PLUGIN_009_LONGPRESS_DISABLED, PLUGIN_009_LONGPRESS_LOW, PLUGIN_009_LONGPRESS_HIGH,PLUGIN_009_LONGPRESS_BOTH};
addFormSelector(F("Longpress event"), F("plugin_009_lp"), 4, buttonLP, buttonLPValues, choiceLP);
addFormNumericBox(F("Longpress min. interval (ms)"), F("plugin_009_lpmininterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]), PLUGIN_009_LONGPRESS_MIN_INTERVAL, PLUGIN_009_LONGPRESS_MAX_INTERVAL);
success = true;
break;
}
@@ -57,6 +130,15 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
{
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = isFormItemChecked(F("plugin_009_boot"));
//@giig1967-20181022
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0] = getFormItemInt(F("plugin_009_debounce"));
Settings.TaskDevicePluginConfig[event->TaskIndex][4] = getFormItemInt(F("plugin_009_dc"));
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = getFormItemInt(F("plugin_009_dcmaxinterval"));
Settings.TaskDevicePluginConfig[event->TaskIndex][5] = getFormItemInt(F("plugin_009_lp"));
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = getFormItemInt(F("plugin_009_lpmininterval"));
success = true;
break;
}
@@ -67,32 +149,185 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
Plugin_009_Config(Settings.TaskDevicePort[event->TaskIndex], 1);
// read and store current state to prevent switching at boot time
// "state" could be -1, 0 or 1
switchstate[event->TaskIndex] = Plugin_009_Read(Settings.TaskDevicePort[event->TaskIndex]);
// if boot state must be send, inverse default state
// this is done to force the trigger in PLUGIN_TEN_PER_SECOND
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0])
switchstate[event->TaskIndex] = !switchstate[event->TaskIndex];
// @giig1967g-20181022: set initial UserVar of the switch
if (switchstate[event->TaskIndex] != -1 && Settings.TaskDevicePin1Inversed[event->TaskIndex]) {
UserVar[event->BaseVarIndex] = !switchstate[event->TaskIndex];
} else {
UserVar[event->BaseVarIndex] = switchstate[event->TaskIndex];
}
// @giig1967g-20181022: doubleclick counter = 0
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0;
// @giig1967g-20181022: used to track if LP has fired
Settings.TaskDevicePluginConfig[event->TaskIndex][6]=false;
// @giig1967g-20181022: store millis for debounce, doubleclick and long press
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]=millis(); //debounce timer
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis(); //doubleclick timer
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis(); //longpress timer
// @giig1967g-20181022: set minimum value for doubleclick MIN max speed
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_009_DOUBLECLICK_MIN_INTERVAL)
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_009_DOUBLECLICK_MIN_INTERVAL;
// @giig1967g-20181022: set minimum value for longpress MIN max speed
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_009_LONGPRESS_MIN_INTERVAL)
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_009_LONGPRESS_MIN_INTERVAL;
setPinState(PLUGIN_ID_009, Settings.TaskDevicePort[event->TaskIndex], PIN_MODE_INPUT, 0);
success = true;
break;
}
case PLUGIN_TEN_PER_SECOND:
{
int state = Plugin_009_Read(Settings.TaskDevicePort[event->TaskIndex]);
if (state != -1)
{
if (state != switchstate[event->TaskIndex])
{
String log = F("MCP : State ");
log += state;
addLog(LOG_LEVEL_INFO, log);
switchstate[event->TaskIndex] = state;
UserVar[event->BaseVarIndex] = state;
event->sensorType = SENSOR_TYPE_SWITCH;
sendData(event);
const int8_t state = Plugin_009_Read(Settings.TaskDevicePort[event->TaskIndex]);
/**************************************************************************\
20181022 - @giig1967g: new doubleclick logic is:
if there is a 'state' change, check debounce period.
Then if doubleclick interval exceeded, reset Settings.TaskDevicePluginConfig[event->TaskIndex][7] to 0
Settings.TaskDevicePluginConfig[event->TaskIndex][7] contains the current status for doubleclick:
0: start counting
1: 1st click
2: 2nd click
3: 3rd click = doubleclick event if inside interval (calculated as: '3rd click time' minus '1st click time')
Returned EVENT value is = 3 always for doubleclick
In rules this can be checked:
on Button#Switch=3 do //will fire if doubleclick
\**************************************************************************/
if (state != -1) {
if (state != switchstate[event->TaskIndex]) {
//@giig1967g20181022: reset timer for long press
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis();
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = false;
const unsigned long debounceTime = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]);
if (debounceTime >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0])) //de-bounce check
{
const unsigned long deltaDC = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]);
if ((deltaDC >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1])) ||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3)
{
//reset timer for doubleclick
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0;
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis();
}
//just to simplify the reading of the code
#define COUNTER Settings.TaskDevicePluginConfig[event->TaskIndex][7]
#define DC Settings.TaskDevicePluginConfig[event->TaskIndex][4]
//check settings for doubleclick according to the settings
if ( COUNTER!=0 || ( COUNTER==0 && (DC==3 || (DC==1 && state==0) || (DC==2 && state==1))) )
Settings.TaskDevicePluginConfig[event->TaskIndex][7]++;
#undef DC
#undef COUNTER
switchstate[event->TaskIndex] = state;
byte output_value;
boolean sendState = switchstate[event->TaskIndex];
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
sendState = !sendState;
if (Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3 && Settings.TaskDevicePluginConfig[event->TaskIndex][4]>0)
{
output_value = 3; //double click
} else {
output_value = sendState ? 1 : 0; //single click
}
UserVar[event->BaseVarIndex] = output_value;
String log = F("MCP : Port=");
log += Settings.TaskDevicePort[event->TaskIndex];
log += F(" State=");
log += state;
log += output_value==3 ? F(" Doubleclick=") : F(" Output value=");
log += output_value;
addLog(LOG_LEVEL_INFO, log);
event->sensorType = SENSOR_TYPE_SWITCH;
sendData(event);
//reset Userdata so it displays the correct state value in the web page
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0] = millis();
}
}
//just to simplify the reading of the code
#define LP Settings.TaskDevicePluginConfig[event->TaskIndex][5]
#define FIRED Settings.TaskDevicePluginConfig[event->TaskIndex][6]
//check if LP is enabled and if LP has not fired yet
else if (!FIRED && (LP==3 ||(LP==1 && state==0)||(LP==2 && state==1) ) ) {
#undef LP
#undef FIRED
/**************************************************************************\
20181022 - @giig1967g: new longpress logic is:
if there is no 'state' change, check if longpress interval reached
When reached send longpress event.
Returned Event value = state + 10
So if state = 0 => EVENT longpress = 10
if state = 1 => EVENT longpress = 11
So we can trigger longpress for high or low contact
In rules this can be checked:
on Button#Switch=10 do //will fire if longpress when state = 0
on Button#Switch=11 do //will fire if longpress when state = 1
\**************************************************************************/
const unsigned long deltaLP = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]);
if (deltaLP >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]))
{
byte output_value;
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = true; //fired = true
boolean sendState = state;
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
sendState = !sendState;
output_value = sendState ? 1 : 0;
output_value = output_value + 10;
UserVar[event->BaseVarIndex] = output_value;
String log = F("MCP : LongPress: Port=");
log += Settings.TaskDevicePort[event->TaskIndex];
log += F(" State=");
log += state ? '1' : '0';
log += F(" Output value=");
log += output_value;
addLog(LOG_LEVEL_INFO, log);
sendData(event);
//reset Userdata so it displays the correct state value in the web page
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
}
}
} else if (state != switchstate[event->TaskIndex]) {
//set UserVar and switchState = -1 and send EVENT to notify user
UserVar[event->BaseVarIndex] = state;
switchstate[event->TaskIndex] = state;
String log = F("MCP : Port=");
log += Settings.TaskDevicePort[event->TaskIndex];
log += F(" is offline (EVENT= -1)");
addLog(LOG_LEVEL_INFO, log);
sendData(event);
}
success = true;
break;
@@ -102,13 +337,33 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
{
// We do not actually read the pin state as this is already done 10x/second
// Instead we just send the last known state stored in Uservar
String log = F("MCP : State ");
String log = F("MCP : Port=");
log += Settings.TaskDevicePort[event->TaskIndex];
log += F(" State=");
log += UserVar[event->BaseVarIndex];
addLog(LOG_LEVEL_INFO, log);
success = true;
break;
}
case PLUGIN_REQUEST:
{
//parseString(string, 1) = device
//parseString(string, 2) = command
//parseString(string, 3) = gpio number
// returns pin value using syntax: [plugin#mcpgpio#pinstate#xx]
if (string.length()>=16 && string.substring(0,16).equalsIgnoreCase(F("mcpgpio,pinstate")))
{
int par1;
if (validIntFromString(parseString(string, 3), par1)) {
string = Plugin_009_Read(par1);
}
success = true;
}
break;
}
case PLUGIN_WRITE:
{
String log = "";
@@ -124,6 +379,28 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_009, event->Par1, log, 0));
}
if (command == F("mcpgpiotoggle"))
{
success = true;
byte mode;
uint16_t currentState;
if (hasPinState(PLUGIN_ID_009,event->Par1)) {
getPinState(PLUGIN_ID_009, event->Par1, &mode, &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++)
+198 -40
View File
@@ -10,9 +10,21 @@
#include <Arduino.h>
#include <map>
//#include <shared_ptr>
#include <NewPingESP8266.h>
// 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;
@@ -36,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;
}
@@ -54,20 +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]);
addUnit(F("cm"));
// 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;
@@ -75,34 +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] =
std::shared_ptr<NewPingESP8266> (new NewPingESP8266(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]->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);
@@ -120,7 +190,6 @@ boolean Plugin_013(byte function, struct EventStruct *event, String& string)
log += F(" usec, ");
addLog(LOG_LEVEL_INFO, log);
success = true;
break;
}
@@ -133,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;
@@ -174,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;
@@ -186,10 +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;
delay(1);
float distance = (P_013_sensordefs[taskIndex])->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
+131
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.
@@ -165,6 +168,8 @@ boolean Plugin_016(byte function, struct EventStruct *event, String& string)
// Display the basic output of what we found.
log += resultToHumanReadableBasic(&results);
addLog(LOG_LEVEL_INFO, log);
displayRawToReadableB32Hex();
// Display any extra A/C info if we have it.
// Display the human readable state of an A/C message if we can.
log = "";
@@ -233,4 +238,130 @@ boolean Plugin_016(byte function, struct EventStruct *event, String& string)
return success;
}
#define PCT_TOLERANCE 7u
#define pct_tolerance(v) ((v) / (100u / PCT_TOLERANCE))
//#define MIN_TOLERANCE 10u
//#define get_tolerance(v) (pct_tolerance(v) > MIN_TOLERANCE? pct_tolerance(v) : MIN_TOLERANCE)
#define get_tolerance(v) (pct_tolerance(v))
#define MIN_VIABLE_DIV 40u
#define to_32hex(c) ((c) < 10 ? (c) + '0' : (c) + 'A' - 10)
// This function attempts to convert the raw IR timings buffer to a short string that can be sent over as
// an IRSEND HTTP/MQTT command. It analyzes the timings, and searches for a common denominator which can be
// used to compress the values. If found, it then produces a string consisting of B32 Hex digit for each
// timing value, appended by the denominators for Pulse and Blank. This string can then be used in an
// IRSEND command. An important advantage of this string over the current IRSEND RAW B32 format implemented
// by GusPS is that it allows easy inspections and modifications after the code is constructed.
//
// Author: Gilad Raz (jazzgil) 23sep2018
void displayRawToReadableB32Hex() {
String line;
uint16_t div[2];
// print the values: either pulses or blanks
for (uint16_t i = 1; i < results.rawlen; i++)
line += uint64ToString(results.rawbuf[i] * RAWTICK, 10) + ",";
addLog(LOG_LEVEL_DEBUG, line);
// Find a common denominator divisor for odd indexes (pulses) and then even indexes (blanks).
for (uint16_t p = 0; p < 2; p++) {
uint16_t cd = 0xFFFFU; // current divisor
// find the lowest value to start the divisor with.
for (uint16_t i = 1 + p; i < results.rawlen; i += 2) {
uint16_t val = results.rawbuf[i] * RAWTICK;
if (cd > val) cd = val;
}
uint16_t bstDiv = -1, bstAvg = 0xFFFFU;
float bstMul = 5000;
cd += get_tolerance(cd) + 1;
//Serial.println(String("p="+ uint64ToString(p, 10) + " start cd=" + uint64ToString(cd, 10)).c_str());
// find the best divisor based on lowest avg err, within allowed tolerance.
while (--cd >= MIN_VIABLE_DIV) {
uint32_t avg = 0;
uint16_t totTms = 0;
// calculate average error for current divisor, and verify it's within tolerance for all timings.
for (uint16_t i = 1 + p; i < results.rawlen; i += 2) {
uint16_t val = results.rawbuf[i] * RAWTICK;
uint16_t rmdr = val >= cd ? val % cd : cd - val;
if (rmdr > get_tolerance(val)) { avg = 0xFFFFU; break; }
avg += rmdr;
totTms += val / cd + (cd > val? 1 : 0);
}
if (avg == 0xFFFFU) continue;
avg /= results.rawlen / 2;
float avgTms = (float)totTms / (results.rawlen / 2);
if (avgTms <= bstMul && avg < bstAvg) {
bstMul = avgTms;
bstAvg = avg;
bstDiv = cd;
//Serial.println(String("p="+ uint64ToString(p, 10) + " cd=" + uint64ToString(cd, 10) +" avgErr=" + uint64ToString(avg, 10) + " totTms="+ uint64ToString(totTms, 10) + " avgTms="+ uint64ToString((uint16_t)(avgTms*10), 10) ).c_str());
}
}
if (bstDiv == 0xFFFFU) {
addLog(LOG_LEVEL_INFO, "No proper divisor found. Try again...");
return;
}
div[p] = bstDiv;
line = String(p? "Blank: " : "Pulse: ") + " divisor=" + uint64ToString(bstDiv, 10)
+" avgErr=" + uint64ToString(bstAvg, 10) + " avgMul="+ uint64ToString((uint16_t)bstMul, 10)
+'.'+ ((char)((bstMul - (uint16_t)bstMul) * 10 )+ '0');
addLog(LOG_LEVEL_DEBUG, line);
}
// Generate the B32 Hex string, per the divisors found.
uint16_t total = results.rawlen - 1, tmOut[total];
//line = "Timing muls ("+ uint64ToString(total, 10) + "): ";
for (unsigned int i = 0; i < total; i++) {
uint16_t val = results.rawbuf[i+1] * RAWTICK;
unsigned int dv = div[(i) & 1];
unsigned int tm = val / dv + (val % dv > dv / 2? 1 : 0);
tmOut[i] = tm;
//line += uint64ToString(tm, 10) + ",";
}
//Serial.println(line);
char out[total];
unsigned int iOut = 0, s = 2, d = 0;
for (; s+1 < total; d = s, s += 2) {
unsigned int vals = 2;
while (s+1 < total && tmOut[s] == tmOut[d] && tmOut[s+1] == tmOut[d+1]) {
vals += 2;
s += 2;
}
if (iOut + 5 > sizeof(out) || tmOut[d] >= 32*32 || tmOut[d+1] >= 32*32 || vals >= 64) {
addLog(LOG_LEVEL_INFO, "Raw code too long. Try again...");
return;
}
if (vals > 4 || (vals == 4 && (tmOut[d] >= 32 || tmOut[d+1] >= 32))) {
out[iOut++] = '*';
out[iOut++] = to_32hex(vals / 2);
vals = 2;
}
while (vals--)
iOut = storeB32Hex(out, iOut, tmOut[d++]);
}
while (d < total)
iOut = storeB32Hex(out, iOut, tmOut[d++]);
out[iOut] = 0;
line = "IRSEND,RAW2," + String(out) + ",38," + uint64ToString(div[0], 10) +','+ uint64ToString(div[1], 10);
addLog(LOG_LEVEL_INFO, line);
}
unsigned int storeB32Hex(char out[], unsigned int iOut, unsigned int val) {
if (val >= 32) {
out[iOut++] = '^';
out[iOut++] = to_32hex(val/32);
val %= 32;
}
out[iOut++] = to_32hex(val);
return iOut;
}
#endif // USES_P016
+292 -15
View File
@@ -3,15 +3,52 @@
//#################################### Plugin 019: PCF8574 ##############################################
//#######################################################################################################
/**************************************************\
CONFIG
TaskDevicePluginConfig settings:
0: send boot state (true,false)
1:
2:
3:
4: use doubleclick (0,1,2,3)
5: use longpress (0,1,2,3)
6: LP fired (true,false)
7: doubleclick counter (=0,1,2,3)
TaskDevicePluginConfigFloat settings:
0: debounce interval ms
1: doubleclick interval ms
2: longpress interval ms
3:
TaskDevicePluginConfigLong settings:
0: clickTime debounce ms
1: clickTime doubleclick ms
2: clickTime longpress ms
3:
\**************************************************/
#define PLUGIN_019
#define PLUGIN_ID_019 19
#define PLUGIN_NAME_019 "Switch input - PCF8574"
#define PLUGIN_VALUENAME1_019 "Switch"
#define PLUGIN_019_DOUBLECLICK_MIN_INTERVAL 1000
#define PLUGIN_019_DOUBLECLICK_MAX_INTERVAL 3000
#define PLUGIN_019_LONGPRESS_MIN_INTERVAL 1000
#define PLUGIN_019_LONGPRESS_MAX_INTERVAL 5000
#define PLUGIN_019_DC_DISABLED 0
#define PLUGIN_019_DC_LOW 1
#define PLUGIN_019_DC_HIGH 2
#define PLUGIN_019_DC_BOTH 3
#define PLUGIN_019_LONGPRESS_DISABLED 0
#define PLUGIN_019_LONGPRESS_LOW 1
#define PLUGIN_019_LONGPRESS_HIGH 2
#define PLUGIN_019_LONGPRESS_BOTH 3
boolean Plugin_019(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
static byte switchstate[TASKS_MAX];
static int8_t switchstate[TASKS_MAX];
switch (function)
{
@@ -23,7 +60,7 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
Device[deviceCount].VType = SENSOR_TYPE_SWITCH;
Device[deviceCount].Ports = 8;
Device[deviceCount].PullUpOption = false;
Device[deviceCount].InverseLogicOption = false;
Device[deviceCount].InverseLogicOption = true;
Device[deviceCount].FormulaOption = false;
Device[deviceCount].ValueCount = 1;
Device[deviceCount].SendDataOption = true;
@@ -49,6 +86,41 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
{
addFormCheckBox(F("Send Boot state"), F("plugin_019_boot"), Settings.TaskDevicePluginConfig[event->TaskIndex][0]);
//@giig1967-20181022
addFormSubHeader(F("Advanced event management"));
addFormNumericBox(F("De-bounce (ms)"), F("plugin_019_debounce"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0]), 0, 250);
//set minimum value for doubleclick MIN max speed
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_019_DOUBLECLICK_MIN_INTERVAL)
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_019_DOUBLECLICK_MIN_INTERVAL;
byte choiceDC = Settings.TaskDevicePluginConfig[event->TaskIndex][4];
String buttonDC[4];
buttonDC[0] = F("Disabled");
buttonDC[1] = F("Active only on LOW (EVENT=3)");
buttonDC[2] = F("Active only on HIGH (EVENT=3)");
buttonDC[3] = F("Active on LOW & HIGH (EVENT=3)");
int buttonDCValues[4] = {PLUGIN_019_DC_DISABLED, PLUGIN_019_DC_LOW, PLUGIN_019_DC_HIGH,PLUGIN_019_DC_BOTH};
addFormSelector(F("Doubleclick event"), F("plugin_019_dc"), 4, buttonDC, buttonDCValues, choiceDC);
addFormNumericBox(F("Doubleclick max. interval (ms)"), F("plugin_019_dcmaxinterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1]), PLUGIN_019_DOUBLECLICK_MIN_INTERVAL, PLUGIN_019_DOUBLECLICK_MAX_INTERVAL);
//set minimum value for longpress MIN max speed
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_019_LONGPRESS_MIN_INTERVAL)
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_019_LONGPRESS_MIN_INTERVAL;
byte choiceLP = Settings.TaskDevicePluginConfig[event->TaskIndex][5];
String buttonLP[4];
buttonLP[0] = F("Disabled");
buttonLP[1] = F("Active only on LOW (EVENT= 10 [NORMAL] or 11 [INVERSED])");
buttonLP[2] = F("Active only on HIGH (EVENT= 11 [NORMAL] or 10 [INVERSED])");
buttonLP[3] = F("Active on LOW & HIGH (EVENT= 10 or 11)");
int buttonLPValues[4] = {PLUGIN_019_LONGPRESS_DISABLED, PLUGIN_019_LONGPRESS_LOW, PLUGIN_019_LONGPRESS_HIGH,PLUGIN_019_LONGPRESS_BOTH};
addFormSelector(F("Longpress event"), F("plugin_019_lp"), 4, buttonLP, buttonLPValues, choiceLP);
addFormNumericBox(F("Longpress min. interval (ms)"), F("plugin_019_lpmininterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]), PLUGIN_019_LONGPRESS_MIN_INTERVAL, PLUGIN_019_LONGPRESS_MAX_INTERVAL);
success = true;
break;
}
@@ -57,6 +129,15 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
{
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = isFormItemChecked(F("plugin_019_boot"));
//@giig1967-20181022
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0] = getFormItemInt(F("plugin_019_debounce"));
Settings.TaskDevicePluginConfig[event->TaskIndex][4] = getFormItemInt(F("plugin_019_dc"));
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = getFormItemInt(F("plugin_019_dcmaxinterval"));
Settings.TaskDevicePluginConfig[event->TaskIndex][5] = getFormItemInt(F("plugin_019_lp"));
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = getFormItemInt(F("plugin_019_lpmininterval"));
success = true;
break;
}
@@ -64,31 +145,186 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
case PLUGIN_INIT:
{
// read and store current state to prevent switching at boot time
// "state" could be -1, 0 or 1
switchstate[event->TaskIndex] = Plugin_019_Read(Settings.TaskDevicePort[event->TaskIndex]);
// if boot state must be send, inverse default state
// this is done to force the trigger in PLUGIN_TEN_PER_SECOND
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0])
switchstate[event->TaskIndex] = !switchstate[event->TaskIndex];
// @giig1967g-20181022: set initial UserVar of the switch
if (switchstate[event->TaskIndex] != -1 && Settings.TaskDevicePin1Inversed[event->TaskIndex]) {
UserVar[event->BaseVarIndex] = !switchstate[event->TaskIndex];
} else {
UserVar[event->BaseVarIndex] = switchstate[event->TaskIndex];
}
// @giig1967g-20181022: doubleclick counter = 0
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0;
// @giig1967g-20181022: used to track if LP has fired
Settings.TaskDevicePluginConfig[event->TaskIndex][6]=false;
// @giig1967g-20181022: store millis for debounce, doubleclick and long press
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]=millis(); //debounce timer
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis(); //doubleclick timer
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis(); //longpress timer
// @giig1967g-20181022: set minimum value for doubleclick MIN max speed
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_019_DOUBLECLICK_MIN_INTERVAL)
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_019_DOUBLECLICK_MIN_INTERVAL;
// @giig1967g-20181022: set minimum value for longpress MIN max speed
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_019_LONGPRESS_MIN_INTERVAL)
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_019_LONGPRESS_MIN_INTERVAL;
// @giig1967g-20181022: if it is in the device list we assume it's an input pin
setPinState(PLUGIN_ID_019, Settings.TaskDevicePort[event->TaskIndex], PIN_MODE_INPUT, 0);
success = true;
break;
}
case PLUGIN_TEN_PER_SECOND:
{
int state = Plugin_019_Read(Settings.TaskDevicePort[event->TaskIndex]);
if (state != -1)
{
if (state != switchstate[event->TaskIndex])
{
String log = F("PCF : State ");
log += state;
addLog(LOG_LEVEL_INFO, log);
switchstate[event->TaskIndex] = state;
UserVar[event->BaseVarIndex] = state;
event->sensorType = SENSOR_TYPE_SWITCH;
sendData(event);
const int8_t state = Plugin_019_Read(Settings.TaskDevicePort[event->TaskIndex]);
/**************************************************************************\
20181022 - @giig1967g: new doubleclick logic is:
if there is a 'state' change, check debounce period.
Then if doubleclick interval exceeded, reset Settings.TaskDevicePluginConfig[event->TaskIndex][7] to 0
Settings.TaskDevicePluginConfig[event->TaskIndex][7] contains the current status for doubleclick:
0: start counting
1: 1st click
2: 2nd click
3: 3rd click = doubleclick event if inside interval (calculated as: '3rd click time' minus '1st click time')
Returned EVENT value is = 3 always for doubleclick
In rules this can be checked:
on Button#Switch=3 do //will fire if doubleclick
\**************************************************************************/
if (state != -1) {
if (state != switchstate[event->TaskIndex]) {
//@giig1967g20181022: reset timer for long press
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis();
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = false;
const unsigned long debounceTime = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]);
if (debounceTime >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0])) //de-bounce check
{
const unsigned long deltaDC = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]);
if ((deltaDC >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1])) ||
Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3)
{
//reset timer for doubleclick
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0;
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis();
}
//just to simplify the reading of the code
#define COUNTER Settings.TaskDevicePluginConfig[event->TaskIndex][7]
#define DC Settings.TaskDevicePluginConfig[event->TaskIndex][4]
//check settings for doubleclick according to the settings
if ( COUNTER!=0 || ( COUNTER==0 && (DC==3 || (DC==1 && state==0) || (DC==2 && state==1))) )
Settings.TaskDevicePluginConfig[event->TaskIndex][7]++;
#undef DC
#undef COUNTER
switchstate[event->TaskIndex] = state;
byte output_value;
boolean sendState = switchstate[event->TaskIndex];
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
sendState = !sendState;
if (Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3 && Settings.TaskDevicePluginConfig[event->TaskIndex][4]>0)
{
output_value = 3; //double click
} else {
output_value = sendState ? 1 : 0; //single click
}
UserVar[event->BaseVarIndex] = output_value;
String log = F("PCF : Port=");
log += Settings.TaskDevicePort[event->TaskIndex];
log += F(" State=");
log += state;
log += output_value==3 ? F(" Doubleclick=") : F(" Output value=");
log += output_value;
addLog(LOG_LEVEL_INFO, log);
event->sensorType = SENSOR_TYPE_SWITCH;
sendData(event);
//reset Userdata so it displays the correct state value in the web page
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0] = millis();
}
}
//just to simplify the reading of the code
#define LP Settings.TaskDevicePluginConfig[event->TaskIndex][5]
#define FIRED Settings.TaskDevicePluginConfig[event->TaskIndex][6]
//check if LP is enabled and if LP has not fired yet
else if (!FIRED && (LP==3 ||(LP==1 && state==0)||(LP==2 && state==1) ) ) {
#undef LP
#undef FIRED
/**************************************************************************\
20181022 - @giig1967g: new longpress logic is:
if there is no 'state' change, check if longpress interval reached
When reached send longpress event.
Returned Event value = state + 10
So if state = 0 => EVENT longpress = 10
if state = 1 => EVENT longpress = 11
So we can trigger longpress for high or low contact
In rules this can be checked:
on Button#Switch=10 do //will fire if longpress when state = 0
on Button#Switch=11 do //will fire if longpress when state = 1
\**************************************************************************/
const unsigned long deltaLP = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]);
if (deltaLP >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]))
{
byte output_value;
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = true; //fired = true
boolean sendState = state;
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
sendState = !sendState;
output_value = sendState ? 1 : 0;
output_value = output_value + 10;
UserVar[event->BaseVarIndex] = output_value;
String log = F("PCF : LongPress: Port= ");
log += Settings.TaskDevicePort[event->TaskIndex];
log += F(" State=");
log += state ? '1' : '0';
log += F(" Output value=");
log += output_value;
addLog(LOG_LEVEL_INFO, log);
sendData(event);
//reset Userdata so it displays the correct state value in the web page
UserVar[event->BaseVarIndex] = sendState ? 1 : 0;
}
}
} else if (state != switchstate[event->TaskIndex]) {
//set UserVar and switchState = -1 and send EVENT to notify user
UserVar[event->BaseVarIndex] = state;
switchstate[event->TaskIndex] = state;
String log = F("PCF : Port=");
log += Settings.TaskDevicePort[event->TaskIndex];
log += F(" is offline (EVENT= -1)");
addLog(LOG_LEVEL_INFO, log);
sendData(event);
}
success = true;
break;
@@ -105,6 +341,24 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
break;
}
case PLUGIN_REQUEST:
{
//parseString(string, 1) = device
//parseString(string, 2) = command
//parseString(string, 3) = gpio number
// returns pin value using syntax: [plugin#pcfgpio#pinstate#xx]
if (string.length()>=16 && string.substring(0,16).equalsIgnoreCase(F("pcfgpio,pinstate")))
{
int par1;
if (validIntFromString(parseString(string, 3), par1)) {
string = Plugin_019_Read(par1);
}
success = true;
}
break;
}
case PLUGIN_WRITE:
{
String log = "";
@@ -128,6 +382,28 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_019, event->Par1, log, 0));
}
if (command == F("pcfgpiotoggle"))
{
success = true;
byte mode;
uint16_t currentState;
if (hasPinState(PLUGIN_ID_019,event->Par1)) {
getPinState(PLUGIN_ID_019, event->Par1, &mode, &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;
@@ -187,7 +463,8 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
//********************************************************************************
// PCF8574 read
//********************************************************************************
int Plugin_019_Read(byte Par1)
//@giig1967g-20181023: changed to int8_t
int8_t Plugin_019_Read(byte Par1)
{
int8_t state = -1;
byte unit = (Par1 - 1) / 8;
+3
View File
@@ -189,6 +189,8 @@ boolean Plugin_020(byte function, struct EventStruct *event, String& string)
case PLUGIN_SERIAL_IN:
{
if (Plugin_020_init)
{
uint8_t serial_buf[P020_BUFFER_SIZE];
int RXWait = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
if (RXWait == 0)
@@ -279,6 +281,7 @@ boolean Plugin_020(byte function, struct EventStruct *event, String& string)
success = true;
break;
}
}
case PLUGIN_WRITE:
{
+102 -13
View File
@@ -12,6 +12,7 @@
#define PLUGIN_022_PCA9685_MODE1 0x00 // location for Mode1 register address
#define PCA9685_MODE2 0x01 // location for Mode2 register address
#define PCA9685_MODE2_VALUES 0x20
#define PCA9685_LED0 0x06 // location for start of LED0 registers
#define PCA9685_ADDRESS 0x40 // I2C address
#define PCA9685_MAX_ADDRESS 0x7F
@@ -34,10 +35,20 @@ boolean Plugin_022(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
int address = 0;
int mode2 = 0x10;
uint16_t freq = PCA9685_MAX_FREQUENCY;
uint16_t range = PCA9685_MAX_PWM;
if(event != NULL && event->TaskIndex >- 1)
{
address = Settings.TaskDevicePort[event->TaskIndex];
mode2 = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
freq = Settings.TaskDevicePluginConfig[event->TaskIndex][1];
range = Settings.TaskDevicePluginConfig[event->TaskIndex][2];
}
if (freq == 0)
freq = PCA9685_MAX_FREQUENCY;
if (range == 0)
range = PCA9685_MAX_PWM;
switch (function)
{
@@ -76,6 +87,33 @@ boolean Plugin_022(byte function, struct EventStruct *event, String& string)
optionValues[i] = PCA9685_ADDRESS + i;
}
addFormSelectorI2C(F("i2c_addr"), PCA9685_NUMS_ADDRESS, optionValues, address);
String m2Options[PCA9685_MODE2_VALUES];
int m2Values[PCA9685_MODE2_VALUES];
for (int i=0;i < PCA9685_MODE2_VALUES; i++)
{
m2Values[i] = i;
m2Options[i] = formatToHex_decimal(i);
if (i == 0x10)
m2Options[i] += F(" - (default)");
}
addFormSelector(F("MODE2"), F("plugin_022_mode2"), PCA9685_MODE2_VALUES, m2Options, m2Values, mode2);
String freqString = F("Frequency (");
freqString += PCA9685_MIN_FREQUENCY;
freqString += F("-");
freqString += PCA9685_MAX_FREQUENCY;
freqString += F(")");
addFormNumericBox(freqString, F("plugin_022_freq"), freq, PCA9685_MIN_FREQUENCY, PCA9685_MAX_FREQUENCY);
String funitString = F("default ");
funitString += PCA9685_MAX_FREQUENCY;
addUnit(funitString);
addFormNumericBox(F("Range (1-10000)"), F("plugin_022_range"), range, 1, 10000);
String runitString = F("default ");
runitString += PCA9685_MAX_PWM;
addUnit(runitString);
success = true;
break;
}
@@ -83,6 +121,16 @@ boolean Plugin_022(byte function, struct EventStruct *event, String& string)
case PLUGIN_WEBFORM_SAVE:
{
Settings.TaskDevicePort[event->TaskIndex] = getFormItemInt(F("i2c_addr"));
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = getFormItemInt(F("plugin_022_mode2"));
Settings.TaskDevicePluginConfig[event->TaskIndex][1] = getFormItemInt(F("plugin_022_freq"));
Settings.TaskDevicePluginConfig[event->TaskIndex][2] = getFormItemInt(F("plugin_022_range"));
if (!IS_INIT(initializeState, (Settings.TaskDevicePort[event->TaskIndex] - PCA9685_ADDRESS)))
{
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0] != mode2)
Plugin_022_writeRegister(address, PCA9685_MODE2, Settings.TaskDevicePluginConfig[event->TaskIndex][0]);
if (Settings.TaskDevicePluginConfig[event->TaskIndex][1] != freq)
Plugin_022_Frequency(address, Settings.TaskDevicePluginConfig[event->TaskIndex][1]);
}
success = true;
break;
}
@@ -115,14 +163,18 @@ boolean Plugin_022(byte function, struct EventStruct *event, String& string)
if (command == F("pcapwm") || (istanceCommand && command == F("pwm")))
{
success = true;
log = String(F("PCA 0x")) + String(address, HEX) + String(F(": GPIO ")) + String(event->Par1);
log = String(F("PCA 0x")) + String(address, HEX) + String(F(": PWM ")) + String(event->Par1);
if(event->Par1 >= 0 && event->Par1 <= PCA9685_MAX_PINS)
{
if(event->Par2 >=0 && event->Par2 <= PCA9685_MAX_PWM)
if(event->Par2 >=0 && event->Par2 <= range)
{
if (!IS_INIT(initializeState, (address - PCA9685_ADDRESS))) Plugin_022_initialize(address);
Plugin_022_Write(address, event->Par1, event->Par2);
if (!IS_INIT(initializeState, (address - PCA9685_ADDRESS)))
{
Plugin_022_initialize(address);
Plugin_022_writeRegister(address, PCA9685_MODE2, mode2);
Plugin_022_Frequency(address, freq);
}
Plugin_022_Write(address, event->Par1, map(event->Par2, 0, range, 0, PCA9685_MAX_PWM));
setPinState(PLUGIN_ID_022, event->Par1, PIN_MODE_PWM, event->Par2);
addLog(LOG_LEVEL_INFO, log);
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_022, event->Par1, log, 0));
@@ -135,30 +187,57 @@ boolean Plugin_022(byte function, struct EventStruct *event, String& string)
addLog(LOG_LEVEL_ERROR, log + String(F(" is invalid value.")));
}
}
if (command == F("pcafrq") || (istanceCommand && command == F("frq")))
{
success = true;
if(event->Par1 >= PCA9685_MIN_FREQUENCY && event->Par1 <= PCA9685_MAX_FREQUENCY)
{
if (!IS_INIT(initializeState, (address - PCA9685_ADDRESS))) Plugin_022_initialize(address);
if (!IS_INIT(initializeState, (address - PCA9685_ADDRESS)))
{
Plugin_022_initialize(address);
Plugin_022_writeRegister(address, PCA9685_MODE2, mode2);
}
Plugin_022_Frequency(address, event->Par1);
setPinState(PLUGIN_ID_022, 99, PIN_MODE_UNDEFINED, event->Par1);
log = String(F("PCA 0x")) + String(address) + String(F(": FREQ ")) + String(event->Par1);
log = String(F("PCA 0x")) + String(address, HEX) + String(F(": FREQ ")) + String(event->Par1);
addLog(LOG_LEVEL_INFO, log);
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_022, 99, log, 0));
}
else{
addLog(LOG_LEVEL_ERROR,String(F("PCA ")) + String(address, HEX) + String(F(" The frequesncy ")) + String(event->Par1) + String(F(" is out of range.")));
addLog(LOG_LEVEL_ERROR,String(F("PCA 0x")) + String(address, HEX) + String(F(" The frequency ")) + String(event->Par1) + String(F(" is out of range.")));
}
}
if (istanceCommand && command == F("mode2"))
{
success = true;
if(event->Par1 >= 0 && event->Par1 < PCA9685_MODE2_VALUES)
{
if (!IS_INIT(initializeState, (address - PCA9685_ADDRESS)))
{
Plugin_022_initialize(address);
Plugin_022_Frequency(address, freq);
}
Plugin_022_writeRegister(address, PCA9685_MODE2, event->Par1);
log = String(F("PCA 0x")) + String(address, HEX) + String(F(": MODE2 0x")) + String(event->Par1, HEX);
addLog(LOG_LEVEL_INFO, log);
}
else{
addLog(LOG_LEVEL_ERROR,String(F("PCA 0x")) + String(address, HEX) + String(F(" MODE2 0x")) + String(event->Par1, HEX) + String(F(" is out of range.")));
}
}
if (command == F("status"))
{
if (parseString(line, 2) == F("pca"))
{
if (!IS_INIT(initializeState, (address - PCA9685_ADDRESS))) Plugin_022_initialize(address);
if (!IS_INIT(initializeState, (address - PCA9685_ADDRESS)))
{
Plugin_022_initialize(address);
Plugin_022_writeRegister(address, PCA9685_MODE2, mode2);
Plugin_022_Frequency(address, freq);
}
success = true;
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_022, event->Par2, dummyString, 0));
}
@@ -170,7 +249,12 @@ boolean Plugin_022(byte function, struct EventStruct *event, String& string)
log = String(F("PCA 0x")) + String(address, HEX) + String(F(": GPIO "));
if(event->Par1>=0 && event->Par1 <= PCA9685_MAX_PINS)
{
if (!IS_INIT(initializeState, (address - PCA9685_ADDRESS))) Plugin_022_initialize(address);
if (!IS_INIT(initializeState, (address - PCA9685_ADDRESS)))
{
Plugin_022_initialize(address);
Plugin_022_writeRegister(address, PCA9685_MODE2, mode2);
Plugin_022_Frequency(address, freq);
}
int pin = event->Par1;
if(parseString(line,2) == "all")
{
@@ -206,7 +290,12 @@ boolean Plugin_022(byte function, struct EventStruct *event, String& string)
log = String(F("PCA 0x")) + String(address, HEX) + String(F(": GPIO ")) + String(event->Par1);
if(event->Par1>=0 && event->Par1 <= PCA9685_MAX_PINS)
{
if (!IS_INIT(initializeState, ((address - PCA9685_ADDRESS)))) Plugin_022_initialize(address);
if (!IS_INIT(initializeState, (address - PCA9685_ADDRESS)))
{
Plugin_022_initialize(address);
Plugin_022_writeRegister(address, PCA9685_MODE2, mode2);
Plugin_022_Frequency(address, freq);
}
if(event->Par2 == 0)
{
@@ -370,7 +459,7 @@ void Plugin_022_initialize(int address)
Plugin_022_writeRegister(i2cAddress, PLUGIN_022_PCA9685_MODE1, (byte)0x01); // reset the device
delay(1);
Plugin_022_writeRegister(i2cAddress, PLUGIN_022_PCA9685_MODE1, (byte)B10100000); // set up for auto increment
Plugin_022_writeRegister(i2cAddress, PCA9685_MODE2, (byte)0x10); // set to output
//Plugin_022_writeRegister(i2cAddress, PCA9685_MODE2, (byte)0x10); // set to output
SET_INIT(initializeState, (address - PCA9685_ADDRESS));
}
#endif // USES_P022
+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);
+8 -2
View File
@@ -41,7 +41,7 @@ boolean Plugin_026(byte function, struct EventStruct *event, String& string)
case PLUGIN_WEBFORM_LOAD:
{
byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
String options[10];
String options[11];
options[0] = F("Uptime");
options[1] = F("Free RAM");
options[2] = F("Wifi RSSI");
@@ -52,7 +52,8 @@ boolean Plugin_026(byte function, struct EventStruct *event, String& string)
options[7] = F("IP 3.Octet");
options[8] = F("IP 4.Octet");
options[9] = F("Web activity");
addFormSelector(F("Indicator"), F("plugin_026"), 10, options, NULL, choice);
options[10] = F("Free Stack");
addFormSelector(F("Indicator"), F("plugin_026"), 11, options, NULL, choice);
success = true;
break;
@@ -124,6 +125,11 @@ boolean Plugin_026(byte function, struct EventStruct *event, String& string)
value = (millis()-lastWeb)/1000; // respond in seconds
break;
}
case 10:
{
value = getCurrentFreeStack();
break;
}
}
UserVar[event->BaseVarIndex] = value;
String log = F("SYS : ");
+118 -76
View File
@@ -16,6 +16,8 @@ IRsend *Plugin_035_irSender;
#define PLUGIN_ID_035 35
#define PLUGIN_NAME_035 "Communication - IR Transmit"
#define from_32hex(c) ((((c) | ('A' ^ 'a')) - '0') % 39)
boolean Plugin_035(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
@@ -86,7 +88,7 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string)
if (GetArgv(command, TmpStr1, 100, 2)) IrType = TmpStr1;
if (IrType.equalsIgnoreCase(F("RAW"))) {
if (IrType.equalsIgnoreCase(F("RAW")) || IrType.equalsIgnoreCase(F("RAW2"))) {
String IrRaw;
uint16_t IrHz=0;
unsigned int IrPLen=0;
@@ -115,86 +117,120 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string)
uint16_t buf[200];
uint16_t idx = 0;
unsigned int c0 = 0; //count consecutives 0s
unsigned int c1 = 0; //count consecutives 1s
if (IrType.equalsIgnoreCase(F("RAW"))) {
unsigned int c0 = 0; //count consecutives 0s
unsigned int c1 = 0; //count consecutives 1s
printWebString += F("Interpreted RAW Code: ");
//Loop throught every char in RAW string
for(unsigned int i = 0; i < IrRaw.length(); i++)
{
//Get the decimal value from base32 table
//See: https://en.wikipedia.org/wiki/Base32#base32hex
char c = ((IrRaw[i] | ('A' ^ 'a')) - '0') % 39;
printWebString += F("Interpreted RAW Code: ");
//Loop throught every char in RAW string
for(unsigned int i = 0; i < IrRaw.length(); i++)
{
//Get the decimal value from base32 table
//See: https://en.wikipedia.org/wiki/Base32#base32hex
char c = from_32hex(IrRaw[i]);
//Loop through 5 LSB (bits 16, 8, 4, 2, 1)
for (unsigned int shft = 1; shft < 6; shft++)
{
//if bit is 1 (5th position - 00010000 = 16)
if ((c & 16) != 0) {
//add 1 to counter c1
c1++;
//if we already have any 0s in counting (the previous
//bit was 0)
if (c0 > 0) {
//add the total ms into the buffer (number of 0s multiplied
//by defined blank length ms)
buf[idx++] = c0 * IrBLen;
//print the number of 0s just for debuging/info purpouses
for (uint t = 0; t < c0; t++)
printWebString += F("0");
}
//So, as we receive a "1", and processed the counted 0s
//sending them as a ms timing into the buffer, we clear
//the 0s counter
c0 = 0;
} else {
//So, bit is 0
//Loop through 5 LSB (bits 16, 8, 4, 2, 1)
for (unsigned int shft = 1; shft < 6; shft++)
{
//if bit is 1 (5th position - 00010000 = 16)
if ((c & 16) != 0) {
//add 1 to counter c1
c1++;
//if we already have any 0s in counting (the previous
//bit was 0)
if (c0 > 0) {
//add the total ms into the buffer (number of 0s multiplied
//by defined blank length ms)
buf[idx++] = c0 * IrBLen;
//print the number of 0s just for debuging/info purpouses
for (uint t = 0; t < c0; t++)
printWebString += F("0");
}
//So, as we receive a "1", and processed the counted 0s
//sending them as a ms timing into the buffer, we clear
//the 0s counter
c0 = 0;
} else {
//So, bit is 0
//On first call, ignore 0s (suppress left-most 0s)
if (c0+c1 != 0) {
//add 1 to counter c0
c0++;
//if we already have any 1s in counting (the previous
//bit was 1)
if (c1 > 0) {
//add the total ms into the buffer (number of 1s
//multiplied by defined pulse length ms)
buf[idx++] = c1 * IrPLen;
//print the number of 1s just for debugging/info purposes
for (uint t = 0; t < c1; t++)
printWebString += F("1");
//On first call, ignore 0s (suppress left-most 0s)
if (c0+c1 != 0) {
//add 1 to counter c0
c0++;
//if we already have any 1s in counting (the previous
//bit was 1)
if (c1 > 0) {
//add the total ms into the buffer (number of 1s
//multiplied by defined pulse length ms)
buf[idx++] = c1 * IrPLen;
//print the number of 1s just for debugging/info purposes
for (uint t = 0; t < c1; t++)
printWebString += F("1");
}
//So, as we receive a "0", and processed the counted 1s
//sending them as a ms timing into the buffer, we clear
//the 1s counter
c1 = 0;
}
}
//So, as we receive a "0", and processed the counted 1s
//sending them as a ms timing into the buffer, we clear
//the 1s counter
c1 = 0;
//shift to left the "c" variable to process the next bit that is
//in 5th position (00010000 = 16)
c <<= 1;
}
}
//shift to left the "c" variable to process the next bit that is
//in 5th position (00010000 = 16)
c <<= 1;
}
//Finally, we need to process the last counted bit that we were
//processing
//If we have pendings 0s
if (c0 > 0) {
buf[idx++] = c0 * IrBLen;
for (uint t = 0; t < c0; t++)
printWebString += F("0");
}
//If we have pendings 1s
if (c1 > 0) {
buf[idx++] = c1 * IrPLen;
for (uint t = 0; t < c1; t++)
printWebString += F("1");
}
printWebString += F("<BR>");
} else { // RAW2
for (unsigned int i = 0, total = IrRaw.length(), gotRep = 0, rep = 0; i < total;) {
char c = IrRaw[i++];
if (c == '*') {
if (i+2 >= total || idx + (rep = from_32hex(IrRaw[i++])) * 2 > sizeof(buf))
return addErrorTrue("Invalid RAW2 B32 encoding!");
gotRep = 2;
} else {
if ((c == '^' && i+1 >= total) || idx == sizeof(buf))
return addErrorTrue("Invalid RAW2 B32 encoding!");
uint16_t irLen = (idx & 1)? IrBLen : IrPLen;
if (c == '^') {
buf[idx++] = (from_32hex(IrRaw[i]) * 32 + from_32hex(IrRaw[i+1])) * irLen;
i += 2;
} else
buf[idx++] = from_32hex(c) * irLen;
if (--gotRep == 0) {
while (--rep) {
buf[idx] = buf[idx-2];
buf[idx+1] = buf[idx-1];
idx += 2;
}
}
}
}
}
//Finally, we need to process the last counted bit that we were
//processing
//If we have pendings 0s
if (c0 > 0) {
buf[idx] = c0 * IrBLen;
for (uint t = 0; t < c0; t++)
printWebString += F("0");
}
//If we have pendings 1s
if (c1 > 0) {
buf[idx] = c1 * IrPLen;
for (uint t = 0; t < c1; t++)
printWebString += F("1");
}
printWebString += F("<BR>");
Plugin_035_irSender->sendRaw(buf, idx+1, IrHz);
Plugin_035_irSender->sendRaw(buf, idx, IrHz);
//String line = "";
//for (int i = 0; i < idx; i++)
// line += uint64ToString(buf[i], 10) + ",";
//Serial.println(line);
//sprintf_P(log, PSTR("IR Params1: Hz:%u - PLen: %u - BLen: %u"), IrHz, IrPLen, IrBLen);
//addLog(LOG_LEVEL_INFO, log);
@@ -205,7 +241,7 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string)
// unsigned long IrSecondCode=0UL;
char ircodestr[100];
if (GetArgv(command, TmpStr1,100, 2)) IrType = TmpStr1;
if (GetArgv(command, TmpStr1, 100, 3)){ IrCode = strtoul(TmpStr1, NULL, 16);
if (GetArgv(command, TmpStr1, 100, 3)){ IrCode = strtoull(TmpStr1, NULL, 16);
memcpy(ircodestr, TmpStr1, sizeof(TmpStr1[0])*100);
}
//if (GetArgv(command, TmpStr1, 100, 4)) IrBits = str2int(TmpStr1); //not needed any more... leave it for reverce compatibility or remove it and break existing instalations?
@@ -256,7 +292,7 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string)
if (IrType.equalsIgnoreCase(F("GICable"))) Plugin_035_irSender->sendGICable(IrCode);
}
addLog(LOG_LEVEL_INFO, F("IRTX :IR Code Sent"));
addLog(LOG_LEVEL_INFO, (String("IRTX :IR Code Sent: ") + IrType).c_str());
if (printToWeb)
{
printWebString += F("IR Code Sent ");
@@ -274,7 +310,13 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string)
return success;
}
boolean addErrorTrue(const char *str) {
addLog(LOG_LEVEL_ERROR, str);
return true;
}
// A lot of the following code has been taken directly (with permission) from the IRMQTTServer.ino example code
// of the IRremoteESP8266 library. (https://github.com/markszabo/IRremoteESP8266)
// Parse an Air Conditioner A/C Hex String/code and send it.
// Args:
// irType: Nr. of the protocol we need to send.
@@ -615,4 +657,4 @@ uint16_t countValuesInStr(const String str, char sep) {
// }
// return result;
//}
#endif // USES_P035
#endif // USES_P035
+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;
+5 -1
View File
@@ -41,6 +41,7 @@ void Plugin_037_try_connect() {
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);
@@ -62,6 +63,8 @@ void Plugin_037_update_connect_status() {
}
if (MQTTclient_037_connected != connected) {
MQTTclient_037_connected = !MQTTclient_037_connected;
P037_MQTTImport_connected = MQTTclient_037_connected;
if (Settings.UseRules) {
String event = connected ? F("MQTTimport#Connected") : F("MQTTimport#Disconnected");
rulesProcessing(event);
@@ -69,6 +72,7 @@ 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"));
}
@@ -390,7 +394,7 @@ boolean MQTTConnect_037()
Plugin_037_update_connect_status();
return false; // Not connected, so no use in wasting time to connect to a host.
}
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(enabledMqttController, ControllerSettings);
if (ControllerSettings.UseDNS) {
MQTTclient_037->setServer(ControllerSettings.getHost().c_str(), ControllerSettings.Port);
+20 -13
View File
@@ -22,7 +22,7 @@
#include <QEIx4.h>
QEIx4* Plugin_059_QE = NULL;
std::map<unsigned int, std::shared_ptr<QEIx4> > P_059_sensordefs;
#ifndef CONFIG
#define CONFIG(n) (Settings.TaskDevicePluginConfig[event->TaskIndex][n])
@@ -108,12 +108,13 @@ boolean Plugin_059(byte function, struct EventStruct *event, String& string)
case PLUGIN_INIT:
{
if (!Plugin_059_QE)
Plugin_059_QE = new QEIx4;
// create sensor instance and add to std::map
P_059_sensordefs.erase(event->TaskIndex);
P_059_sensordefs[event->TaskIndex] = std::shared_ptr<QEIx4>(new QEIx4);
Plugin_059_QE->begin(PIN(0),PIN(1),PIN(2),CONFIG(0));
Plugin_059_QE->setLimit(CONFIG_L(0), CONFIG_L(1));
Plugin_059_QE->setIndexTrigger(true);
P_059_sensordefs[event->TaskIndex]->begin(PIN(0),PIN(1),PIN(2),CONFIG(0));
P_059_sensordefs[event->TaskIndex]->setLimit(CONFIG_L(0), CONFIG_L(1));
P_059_sensordefs[event->TaskIndex]->setIndexTrigger(true);
ExtraTaskSettings.TaskDeviceValueDecimals[event->BaseVarIndex] = 0;
@@ -135,13 +136,19 @@ boolean Plugin_059(byte function, struct EventStruct *event, String& string)
break;
}
case PLUGIN_EXIT:
{
P_059_sensordefs.erase(event->TaskIndex);
break;
}
case PLUGIN_TEN_PER_SECOND:
{
if (Plugin_059_QE)
if (P_059_sensordefs.count(event->TaskIndex) != 0)
{
if (Plugin_059_QE->hasChanged())
if (P_059_sensordefs[event->TaskIndex]->hasChanged())
{
long c = Plugin_059_QE->read();
long c = P_059_sensordefs[event->TaskIndex]->read();
UserVar[event->BaseVarIndex] = (float)c;
event->sensorType = SENSOR_TYPE_SWITCH;
@@ -159,9 +166,9 @@ boolean Plugin_059(byte function, struct EventStruct *event, String& string)
case PLUGIN_READ:
{
if (Plugin_059_QE)
if (P_059_sensordefs.count(event->TaskIndex) != 0)
{
UserVar[event->BaseVarIndex] = (float)Plugin_059_QE->read();
UserVar[event->BaseVarIndex] = (float)P_059_sensordefs[event->TaskIndex]->read();
}
success = true;
break;
@@ -169,7 +176,7 @@ boolean Plugin_059(byte function, struct EventStruct *event, String& string)
case PLUGIN_WRITE:
{
if (Plugin_059_QE)
if (P_059_sensordefs.count(event->TaskIndex) != 0)
{
String log = "";
String command = parseString(string, 1);
@@ -179,7 +186,7 @@ boolean Plugin_059(byte function, struct EventStruct *event, String& string)
{
log = String(F("QEI : ")) + string;
addLog(LOG_LEVEL_INFO, log);
Plugin_059_QE->write(event->Par1);
P_059_sensordefs[event->TaskIndex]->write(event->Par1);
}
success = true; // Command is handled.
}
+26 -17
View File
@@ -1,4 +1,4 @@
#ifdef USES_P067
#ifdef USES_P067
//#######################################################################################################
//#################################### Plugin 063: _P067_HX711_Load_Cell ################################
//#######################################################################################################
@@ -28,8 +28,8 @@
#define PIN(n) (Settings.TaskDevicePin[n][event->TaskIndex])
#endif
int32_t Plugin_067_OversamplingValue = 0;
int16_t Plugin_067_OversamplingCount = 0;
std::map<byte, int32_t> Plugin_067_OversamplingValue;
std::map<byte, int16_t> Plugin_067_OversamplingCount;
void initHX711(int16_t pinSCL, int16_t pinDOUT)
@@ -157,8 +157,8 @@ boolean Plugin_067(byte function, struct EventStruct *event, String& string)
if (isFormItemChecked(F("tare")))
{
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][3] = -UserVar[event->BaseVarIndex + 1];
Plugin_067_OversamplingValue = 0;
Plugin_067_OversamplingCount = 0;
Plugin_067_OversamplingValue[event->TaskIndex] = 0;
Plugin_067_OversamplingCount[event->TaskIndex] = 0;
}
else
{
@@ -190,6 +190,8 @@ boolean Plugin_067(byte function, struct EventStruct *event, String& string)
if (pinSCL >= 0 && pinDOUT >= 0)
{
Plugin_067_OversamplingValue[event->TaskIndex]=0;
Plugin_067_OversamplingCount[event->TaskIndex]=0;
initHX711(pinSCL, pinDOUT);
}
@@ -202,7 +204,7 @@ boolean Plugin_067(byte function, struct EventStruct *event, String& string)
int16_t pinSCL = PIN(0);
int16_t pinDOUT = PIN(1);
if (Plugin_067_OversamplingCount < 250)
if (Plugin_067_OversamplingCount[event->TaskIndex] < 250)
if (pinSCL >= 0 && pinDOUT >= 0)
if (isReadyHX711(pinSCL, pinDOUT))
{
@@ -210,13 +212,13 @@ boolean Plugin_067(byte function, struct EventStruct *event, String& string)
if (CONFIG(0)) //Oversampling?
{
Plugin_067_OversamplingValue += value;
Plugin_067_OversamplingCount ++;
Plugin_067_OversamplingValue[event->TaskIndex] += value;
Plugin_067_OversamplingCount[event->TaskIndex] ++;
}
else //use last value
{
Plugin_067_OversamplingValue = value;
Plugin_067_OversamplingCount = 1;
Plugin_067_OversamplingValue[event->TaskIndex] = value;
Plugin_067_OversamplingCount[event->TaskIndex] = 1;
}
}
@@ -228,11 +230,11 @@ boolean Plugin_067(byte function, struct EventStruct *event, String& string)
{
String log = F("HX711: Value: ");
if (Plugin_067_OversamplingCount > 0)
if (Plugin_067_OversamplingCount[event->TaskIndex] > 0)
{
UserVar[event->BaseVarIndex + 1] = (float)Plugin_067_OversamplingValue / Plugin_067_OversamplingCount;
Plugin_067_OversamplingValue = 0;
Plugin_067_OversamplingCount = 0;
UserVar[event->BaseVarIndex + 1] = (float)Plugin_067_OversamplingValue[event->TaskIndex] / Plugin_067_OversamplingCount[event->TaskIndex];
Plugin_067_OversamplingValue[event->TaskIndex] = 0;
Plugin_067_OversamplingCount[event->TaskIndex] = 0;
UserVar[event->BaseVarIndex] = UserVar[event->BaseVarIndex + 1] + Settings.TaskDevicePluginConfigFloat[event->TaskIndex][3]; //Offset
@@ -272,8 +274,8 @@ boolean Plugin_067(byte function, struct EventStruct *event, String& string)
String log = F("HX711: tare");
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][3] = -UserVar[event->BaseVarIndex + 1];
Plugin_067_OversamplingValue = 0;
Plugin_067_OversamplingCount = 0;
Plugin_067_OversamplingValue[event->TaskIndex] = 0;
Plugin_067_OversamplingCount[event->TaskIndex] = 0;
addLog(LOG_LEVEL_INFO, log);
success = true;
@@ -281,8 +283,15 @@ boolean Plugin_067(byte function, struct EventStruct *event, String& string)
break;
}
case PLUGIN_EXIT:
{
Plugin_067_OversamplingValue.erase(event->TaskIndex);
Plugin_067_OversamplingCount.erase(event->TaskIndex);
break;
}
}
return success;
}
#endif // USES_P067
#endif // USES_P067
+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");
+60 -42
View File
@@ -39,12 +39,14 @@ long cf_pulses = 0;
long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED;
long cf_frequency = 0;
uint8_t serial_in_buffer[32];
uint8_t serial_in_byte_counter = 0;
uint8_t serial_in_byte = 0;
uint8_t P077_checksum = 0;
float energy_voltage = 0; // 123.1 V
float energy_current = 0; // 123.123 A
float energy_power = 0; // 123.1 W
//stats
long P077_t_max = 0, P077_t_all = 0, P077_t_pkt=0, P077_t_pkt_tmp = 0;
uint16_t P077_count_bytes = 0, P077_count_max = 0, P077_count_pkt = 0;
boolean Plugin_077(byte function, struct EventStruct *event, String& string)
{
@@ -118,7 +120,7 @@ boolean Plugin_077(byte function, struct EventStruct *event, String& string)
disableSerialLog(); // disable logging on serial port (used for CSE7766 communication)
Settings.BaudRate = 4800; // set BaudRate for CSE7766
Serial.flush();
Serial.begin(Settings.BaudRate);
Serial.begin(Settings.BaudRate,SERIAL_8E1);
success = true;
break;
}
@@ -165,40 +167,64 @@ boolean Plugin_077(byte function, struct EventStruct *event, String& string)
case PLUGIN_SERIAL_IN:
{
if (Plugin_077_init) {
if (Serial.available() > 0) {
serial_in_byte = Serial.read();
success = true;
success = true;
/* ONLINE CHECKSUMMING by Bartłomiej Zimoń */
bool P077_found = false;
long t_start = millis();
while (Serial.available()>0 && !P077_found){ // if we have data try to find good checksum
uint8_t serial_in_byte = Serial.read();
P077_count_bytes++;
P077_checksum -= serial_in_buffer[2]; // substract from checksum data to be removed
memmove(serial_in_buffer,serial_in_buffer+1,sizeof(serial_in_buffer)-1); // scroll buffer
serial_in_buffer[25] = serial_in_byte; // add new data
P077_checksum += serial_in_buffer[22]; // add online checksum
if (P077_checksum == serial_in_buffer[23] && serial_in_buffer[1] == 0x5A){
P077_found = true;
P077_count_pkt++;
addLog(LOG_LEVEL_DEBUG, F("CSE: packet found"));
CseReceived(event);
if (cse_receive_flag) {
serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
if (24 == serial_in_byte_counter) {
addLog(LOG_LEVEL_DEBUG_DEV, F("CSE: packet received"));
CseReceived(event);
cse_receive_flag = 0;
serial_in_byte_counter = 0;
// new packet received, update values
UserVar[event->BaseVarIndex] = energy_voltage;
UserVar[event->BaseVarIndex + 1] = energy_power;
UserVar[event->BaseVarIndex + 2] = energy_current;
UserVar[event->BaseVarIndex + 3] = cf_pulses;
// break;
}
} else {
if (0x5A == serial_in_byte) { // 0x5A - Packet header 2
cse_receive_flag = 1;
addLog(LOG_LEVEL_DEBUG_DEV, F("CSE: Header received"));
} else {
serial_in_byte_counter = 0;
}
serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
// new packet received, update values
UserVar[event->BaseVarIndex] = energy_voltage;
UserVar[event->BaseVarIndex + 1] = energy_power;
UserVar[event->BaseVarIndex + 2] = energy_current;
UserVar[event->BaseVarIndex + 3] = cf_pulses;
}
serial_in_byte = 0; // Discard
}
long t_diff = millis()-t_start;
P077_t_all += t_diff;
// String log = F("Variable: Tag: ");
// log += key;
// addLog(LOG_LEVEL_INFO, log);
// success = true;
if (P077_count_pkt > 10) // bypass first 10 pkts
P077_t_max = max(P077_t_max, t_diff);
if (P077_found) {
P077_count_max = max(P077_count_max, P077_count_bytes);
P077_t_pkt = t_start - P077_t_pkt_tmp;
P077_t_pkt_tmp = t_start;
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("CSE: time ");
log += P077_t_max;
log += '/';
log += P077_t_pkt;
log += '/';
log += P077_t_all;
addLog(LOG_LEVEL_DEBUG, log);
log = F("CSE: bytes ");
log += P077_count_bytes;
log += '/';
log += P077_count_max;
log += '/';
log += Serial.available();
addLog(LOG_LEVEL_DEBUG, log);
log = F("CSE: nr ");
log += P077_count_pkt;
addLog(LOG_LEVEL_DEBUG, log);
}
P077_t_all = 0;
P077_count_bytes = 0;
}
}
break;
@@ -215,14 +241,6 @@ void CseReceived(struct EventStruct *event)
return;
}
// Calculate checksum
uint8_t checksum = 0;
for (byte i = 2; i < 23; i++) checksum += serial_in_buffer[i];
if (checksum != serial_in_buffer[23]) {
addLog(LOG_LEVEL_DEBUG, F("CSE: Checksum Failure"));
return;
}
// Get chip calibration data (coefficients) and use as initial defaults
if (HLW_UREF_PULSE == Settings.TaskDevicePluginConfig[event->TaskIndex][0]) {
@@ -318,7 +336,7 @@ void CseReceived(struct EventStruct *event)
log = F("CSE current: ");
log += energy_current;
addLog(LOG_LEVEL_DEBUG, log);
log = F("CSE piulses: ");
log = F("CSE pulses: ");
log += cf_pulses;
addLog(LOG_LEVEL_DEBUG, log);
}
+231 -107
View File
@@ -1,24 +1,45 @@
#ifdef USES_P078
//#######################################################################################################
//############################# Plugin 078: SDM120C Eastron Energy Meter ################################
//######################## Plugin 078: SDM120C/220T/230/630 Eastron Energy Meter ########################
//#######################################################################################################
/*
Plugin written by: Sergio Faustino sjfaustino__AT__gmail.com
This plugin reads available values of an Eastron SDM120C Energy Meter.
It will also work with all the other superior model such as SDM220 AND SDM630 series.
It will also work with all the other superior model such as SDM220T, SDM230 AND SDM630 series.
*/
#define PLUGIN_078
#define PLUGIN_ID_078 78
#define PLUGIN_NAME_078 "Energy (AC) - Eastron SDM120C [TESTING]"
#define PLUGIN_VALUENAME1_078 "Voltage"
#define PLUGIN_NAME_078 "Energy (AC) - Eastron SDM120C/220T/230/630 [TESTING]"
#define P078_DEV_ID Settings.TaskDevicePluginConfig[event->TaskIndex][0]
#define P078_MODEL Settings.TaskDevicePluginConfig[event->TaskIndex][1]
#define P078_BAUDRATE Settings.TaskDevicePluginConfig[event->TaskIndex][2]
#define P078_QUERY1 Settings.TaskDevicePluginConfig[event->TaskIndex][3]
#define P078_QUERY2 Settings.TaskDevicePluginConfig[event->TaskIndex][4]
#define P078_QUERY3 Settings.TaskDevicePluginConfig[event->TaskIndex][5]
#define P078_QUERY4 Settings.TaskDevicePluginConfig[event->TaskIndex][6]
#define P078_DEV_ID_DFLT 1
#define P078_MODEL_DFLT 0 // SDM120C
#define P078_BAUDRATE_DFLT 1 // 9600 baud
#define P078_QUERY1_DFLT 0 // Voltage (V)
#define P078_QUERY2_DFLT 1 // Current (A)
#define P078_QUERY3_DFLT 2 // Power (W)
#define P078_QUERY4_DFLT 5 // Power Factor (cos-phi)
boolean Plugin_078_init = false;
#include <SDM.h> // Requires SDM library from Reaper7 - https://github.com/reaper7/SDM_Energy_Meter/
ESPeasySoftwareSerial swSerSDM(6, 7); //config SoftwareSerial (rx->pin6 / tx->pin7)
SDM Plugin_078_SDM(swSerSDM, 9600, NOT_A_PIN); //config SDM
#include <ESPeasySoftwareSerial.h>
// These pointers may be used among multiple instances of the same plugin,
// as long as the same serial settings are used.
ESPeasySoftwareSerial* Plugin_078_SoftSerial = NULL;
SDM* Plugin_078_SDM = NULL;
boolean Plugin_078_init = false;
boolean Plugin_078(byte function, struct EventStruct *event, String& string)
{
@@ -31,12 +52,12 @@ boolean Plugin_078(byte function, struct EventStruct *event, String& string)
{
Device[++deviceCount].Number = PLUGIN_ID_078;
Device[deviceCount].Type = DEVICE_TYPE_DUAL; // connected through 2 datapins
Device[deviceCount].VType = SENSOR_TYPE_SINGLE;
Device[deviceCount].VType = SENSOR_TYPE_QUAD;
Device[deviceCount].Ports = 0;
Device[deviceCount].PullUpOption = false;
Device[deviceCount].InverseLogicOption = false;
Device[deviceCount].FormulaOption = true;
Device[deviceCount].ValueCount = 1;
Device[deviceCount].ValueCount = 4;
Device[deviceCount].SendDataOption = true;
Device[deviceCount].TimerOption = true;
Device[deviceCount].GlobalSyncOption = true;
@@ -51,39 +72,60 @@ boolean Plugin_078(byte function, struct EventStruct *event, String& string)
case PLUGIN_GET_DEVICEVALUENAMES:
{
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_078));
if (P078_QUERY1 == 0 && P078_QUERY2 == 0 && P078_QUERY3 == 0 && P078_QUERY4 == 0) {
P078_QUERY1 = P078_QUERY1_DFLT;
P078_QUERY2 = P078_QUERY2_DFLT;
P078_QUERY3 = P078_QUERY3_DFLT;
P078_QUERY4 = P078_QUERY4_DFLT;
}
safe_strncpy(ExtraTaskSettings.TaskDeviceValueNames[0],
p078_getQueryValueString(P078_QUERY1), sizeof(ExtraTaskSettings.TaskDeviceValueNames[0]));
safe_strncpy(ExtraTaskSettings.TaskDeviceValueNames[1],
p078_getQueryValueString(P078_QUERY2), sizeof(ExtraTaskSettings.TaskDeviceValueNames[1]));
safe_strncpy(ExtraTaskSettings.TaskDeviceValueNames[2],
p078_getQueryValueString(P078_QUERY3), sizeof(ExtraTaskSettings.TaskDeviceValueNames[2]));
safe_strncpy(ExtraTaskSettings.TaskDeviceValueNames[3],
p078_getQueryValueString(P078_QUERY4), sizeof(ExtraTaskSettings.TaskDeviceValueNames[3]));
break;
}
case PLUGIN_WEBFORM_LOAD:
{
byte meter_model = Settings.TaskDevicePluginConfig[event->TaskIndex][1];
byte meter_baudrate = Settings.TaskDevicePluginConfig[event->TaskIndex][2];
byte query = Settings.TaskDevicePluginConfig[event->TaskIndex][3];
if (P078_DEV_ID == 0 || P078_DEV_ID > 247 || P078_BAUDRATE >= 6) {
// Load some defaults
P078_DEV_ID = P078_DEV_ID_DFLT;
P078_MODEL = P078_MODEL_DFLT;
P078_BAUDRATE = P078_BAUDRATE_DFLT;
P078_QUERY1 = P078_QUERY1_DFLT;
P078_QUERY2 = P078_QUERY2_DFLT;
P078_QUERY3 = P078_QUERY3_DFLT;
P078_QUERY4 = P078_QUERY4_DFLT;
}
addFormNumericBox(F("Modbus Address"), F("plugin_078_dev_id"), P078_DEV_ID, 1, 247);
String options_model[3] = { F("SDM120C"), F("SDM220T"), F("SDM630") };
addFormSelector(F("Model Type"), F("plugin_078_meter_model"), 3, options_model, NULL, meter_model );
String options_model[4] = { F("SDM120C"), F("SDM220T"), F("SDM230"), F("SDM630") };
addFormSelector(F("Model Type"), F("plugin_078_model"), 3, options_model, NULL, P078_MODEL );
String options_baudrate[6] = { F("1200"), F("2400"), F("4800"), F("9600"), F("19200"), F("38400") };
addFormSelector(F("Baud Rate"), F("plugin_078_meter_baudrate"), 6, options_baudrate, NULL, meter_baudrate );
String options_baudrate[6];
for (int i = 0; i < 6; ++i) {
options_baudrate[i] = String(p078_storageValueToBaudrate(i));
}
addFormSelector(F("Baud Rate"), F("plugin_078_baudrate"), 6, options_baudrate, NULL, P078_BAUDRATE );
if (meter_model == 0 && meter_baudrate > 3)
if (P078_MODEL == 0 && P078_BAUDRATE > 3)
addFormNote(F("<span style=\"color:red\"> SDM120 only allows up to 9600 baud with default 2400!</span>"));
if (meter_model == 2 && meter_baudrate == 0)
if (P078_MODEL == 3 && P078_BAUDRATE == 0)
addFormNote(F("<span style=\"color:red\"> SDM630 only allows 2400 to 38400 baud with default 9600!</span>"));
String options_query[10] = { F("Voltage (V)"),
F("Current (A)"),
F("Power (W)"),
F("Active Apparent Power (VA)"),
F("Reactive Apparent Power (VAr)"),
F("Power Factor (cos-phi)"),
F("Frequency (Hz)"),
F("Import Active Energy (Wh)"),
F("Export Active Energy (Wh)"),
F("Total Active Energy (Wh)") };
addFormSelector(F("Query"), F("plugin_078_query"), 10, options_query, NULL, query );
String options_query[10];
for (int i = 0; i < 10; ++i) {
options_query[i] = p078_getQueryString(i);
}
addFormSelector(F("Variable 1"), F("plugin_078_query1"), 10, options_query, NULL, P078_QUERY1);
addFormSelector(F("Variable 2"), F("plugin_078_query2"), 10, options_query, NULL, P078_QUERY2);
addFormSelector(F("Variable 3"), F("plugin_078_query3"), 10, options_query, NULL, P078_QUERY3);
addFormSelector(F("Variable 4"), F("plugin_078_query4"), 10, options_query, NULL, P078_QUERY4);
success = true;
break;
@@ -92,9 +134,12 @@ boolean Plugin_078(byte function, struct EventStruct *event, String& string)
case PLUGIN_WEBFORM_SAVE:
{
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = getFormItemInt(F("plugin_078"));
Settings.TaskDevicePluginConfig[event->TaskIndex][1] = getFormItemInt(F("plugin_078_meter_model"));
Settings.TaskDevicePluginConfig[event->TaskIndex][2] = getFormItemInt(F("plugin_078_meter_baudrate"));
Settings.TaskDevicePluginConfig[event->TaskIndex][3] = getFormItemInt(F("plugin_078_query"));
P078_MODEL = getFormItemInt(F("plugin_078_model"));
P078_BAUDRATE = getFormItemInt(F("plugin_078_baudrate"));
P078_QUERY1 = getFormItemInt(F("plugin_078_query1"));
P078_QUERY2 = getFormItemInt(F("plugin_078_query2"));
P078_QUERY3 = getFormItemInt(F("plugin_078_query3"));
P078_QUERY4 = getFormItemInt(F("plugin_078_query4"));
Plugin_078_init = false; // Force device setup next time
success = true;
@@ -104,89 +149,46 @@ boolean Plugin_078(byte function, struct EventStruct *event, String& string)
case PLUGIN_INIT:
{
Plugin_078_init = true;
if (Plugin_078_SoftSerial != NULL) {
delete Plugin_078_SoftSerial;
Plugin_078_SoftSerial=NULL;
}
Plugin_078_SoftSerial = new ESPeasySoftwareSerial(Settings.TaskDevicePin1[event->TaskIndex], Settings.TaskDevicePin2[event->TaskIndex]);
unsigned int baudrate = p078_storageValueToBaudrate(P078_BAUDRATE);
Plugin_078_SoftSerial->begin(baudrate);
// SDM<2400, Settings.TaskDevicePin1[event->TaskIndex],
// Settings.TaskDevicePin2[event->TaskIndex]> Plugin_078_SDM;
Plugin_078_SDM.begin();
if (Plugin_078_SDM != NULL) {
delete Plugin_078_SDM;
Plugin_078_SDM=NULL;
}
Plugin_078_SDM = new SDM(*Plugin_078_SoftSerial, baudrate, NOT_A_PIN);
Plugin_078_SDM->begin();
success = true;
break;
}
case PLUGIN_EXIT:
{
Plugin_078_init = false;
if (Plugin_078_SoftSerial != NULL) {
delete Plugin_078_SoftSerial;
Plugin_078_SoftSerial=NULL;
}
if (Plugin_078_SDM != NULL) {
delete Plugin_078_SDM;
Plugin_078_SDM=NULL;
}
break;
}
case PLUGIN_READ:
{
if (Plugin_078_init)
{
float _tempvar = 0;
String log = F("EASTRON: ");
switch(Settings.TaskDevicePluginConfig[event->TaskIndex][3])
{
case 0:
{
_tempvar = Plugin_078_SDM.readVal(SDM120C_VOLTAGE);
log += F("Voltage ");
break;
}
case 1:
{
_tempvar = Plugin_078_SDM.readVal(SDM120C_CURRENT);
log += F("Current ");
break;
}
case 2:
{
_tempvar = Plugin_078_SDM.readVal(SDM120C_POWER);
log += F("Power ");
break;
}
case 3:
{
_tempvar = Plugin_078_SDM.readVal(SDM120C_ACTIVE_APPARENT_POWER);
log += F("Active Apparent Power ");
break;
}
case 4:
{
_tempvar = Plugin_078_SDM.readVal(SDM120C_REACTIVE_APPARENT_POWER);
log += F("Reactive Apparent Power ");
break;
}
case 5:
{
_tempvar = Plugin_078_SDM.readVal(SDM120C_POWER_FACTOR);
log += F("Power Factor ");
break;
}
case 6:
{
_tempvar = Plugin_078_SDM.readVal(SDM120C_FREQUENCY);
log += F("Frequency ");
break;
}
case 7:
{
_tempvar = Plugin_078_SDM.readVal(SDM120C_IMPORT_ACTIVE_ENERGY);
log += F("Import Active Energy ");
break;
}
case 8:
{
_tempvar = Plugin_078_SDM.readVal(SDM120C_EXPORT_ACTIVE_ENERGY);
log += F("Export Active Energy ");
break;
}
case 9:
{
_tempvar = Plugin_078_SDM.readVal(SDM120C_TOTAL_ACTIVE_ENERGY);
log += F("Total Active Energy ");
break;
}
}
UserVar[event->BaseVarIndex] = _tempvar;
log += _tempvar;
addLog(LOG_LEVEL_INFO, log);
UserVar[event->BaseVarIndex] = p078_readVal(P078_QUERY1, P078_DEV_ID);
UserVar[event->BaseVarIndex + 1] = p078_readVal(P078_QUERY2, P078_DEV_ID);
UserVar[event->BaseVarIndex + 2] = p078_readVal(P078_QUERY3, P078_DEV_ID);
UserVar[event->BaseVarIndex + 3] = p078_readVal(P078_QUERY4, P078_DEV_ID);
success = true;
break;
}
@@ -196,4 +198,126 @@ boolean Plugin_078(byte function, struct EventStruct *event, String& string)
return success;
}
float p078_readVal(byte query, byte node) {
if (Plugin_078_SDM == NULL) return 0.0;
const float _tempvar = Plugin_078_SDM->readVal(p078_getRegister(query, node), node);
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String log = F("EASTRON: (");
log += node;
log += ") ";
log += p078_getQueryString(query);
log += ": ";
log += _tempvar;
addLog(LOG_LEVEL_INFO, log);
}
return _tempvar;
}
unsigned int p078_getRegister(byte query, byte node) {
if (node == 0) { // SDM120C
switch (query) {
case 0: return SDM120C_VOLTAGE;
case 1: return SDM120C_CURRENT;
case 2: return SDM120C_POWER;
case 3: return SDM120C_ACTIVE_APPARENT_POWER;
case 4: return SDM120C_REACTIVE_APPARENT_POWER;
case 5: return SDM120C_POWER_FACTOR;
case 6: return SDM120C_FREQUENCY;
case 7: return SDM120C_IMPORT_ACTIVE_ENERGY;
case 8: return SDM120C_EXPORT_ACTIVE_ENERGY;
case 9: return SDM120C_TOTAL_ACTIVE_ENERGY;
}
} else if (node == 1) { // SDM220T
switch (query) {
case 0: return SDM220T_VOLTAGE;
case 1: return SDM220T_CURRENT;
case 2: return SDM220T_POWER;
case 3: return SDM220T_ACTIVE_APPARENT_POWER;
case 4: return SDM220T_REACTIVE_APPARENT_POWER;
case 5: return SDM220T_POWER_FACTOR;
case 6: return SDM220T_FREQUENCY;
case 7: return SDM220T_IMPORT_ACTIVE_ENERGY;
case 8: return SDM220T_EXPORT_ACTIVE_ENERGY;
case 9: return SDM220T_TOTAL_ACTIVE_ENERGY;
}
} else if (node == 2) { // SDM230
switch (query) {
case 0: return SDM230_VOLTAGE;
case 1: return SDM230_CURRENT;
case 2: return SDM230_POWER;
case 3: return SDM230_ACTIVE_APPARENT_POWER;
case 4: return SDM230_REACTIVE_APPARENT_POWER;
case 5: return SDM230_POWER_FACTOR;
case 6: return SDM230_FREQUENCY;
case 7: return SDM230_IMPORT_ACTIVE_ENERGY;
case 8: return SDM230_EXPORT_ACTIVE_ENERGY;
case 9: return SDM230_CURRENT_RESETTABLE_TOTAL_ACTIVE_ENERGY;
}
} else if (node == 3) { // SDM630
switch (query) {
case 0: return SDM630_VOLTAGE_AVERAGE;
case 1: return SDM630_CURRENTSUM;
case 2: return SDM630_POWERTOTAL;
case 3: return SDM630_VOLT_AMPS_TOTAL;
case 4: return SDM630_VOLT_AMPS_REACTIVE_TOTAL;
case 5: return SDM630_POWER_FACTOR_TOTAL;
case 6: return SDM630_FREQUENCY;
case 7: return SDM630_IMPORT_ACTIVE_ENERGY;
case 8: return SDM630_EXPORT_ACTIVE_ENERGY;
case 9: return SDM630_IMPORT_ACTIVE_ENERGY; // No equivalent for TOTAL_ACTIVE_ENERGY present in the SDM630
}
}
return 0;
}
String p078_getQueryString(byte query) {
switch(query)
{
case 0: return F("Voltage (V)");
case 1: return F("Current (A)");
case 2: return F("Power (W)");
case 3: return F("Active Apparent Power (VA)");
case 4: return F("Reactive Apparent Power (VAr)");
case 5: return F("Power Factor (cos-phi)");
case 6: return F("Frequency (Hz)");
case 7: return F("Import Active Energy (Wh)");
case 8: return F("Export Active Energy (Wh)");
case 9: return F("Total Active Energy (Wh)");
}
return "";
}
String p078_getQueryValueString(byte query) {
switch(query)
{
case 0: return F("V");
case 1: return F("A");
case 2: return F("W");
case 3: return F("VA");
case 4: return F("VAr");
case 5: return F("cos_phi");
case 6: return F("Hz");
case 7: return F("Wh_imp");
case 8: return F("Wh_exp");
case 9: return F("Wh_tot");
}
return "";
}
int p078_storageValueToBaudrate(byte baudrate_setting) {
unsigned int baudrate = 9600;
switch (baudrate_setting) {
case 0: baudrate = 1200; break;
case 1: baudrate = 2400; break;
case 2: baudrate = 4800; break;
case 3: baudrate = 9600; break;
case 4: baudrate = 19200; break;
case 5: baudrate = 38400; break;
}
return baudrate;
}
#endif // USES_P078
+2 -2
View File
@@ -219,7 +219,7 @@ void WemosMotor::setfreq(uint32_t freq)
Wire.write((byte)(freq >> 8));
Wire.write((byte)freq);
Wire.endTransmission(); // stop transmitting
yield();
delay(0);
}
/* setmotor() -- set motor
@@ -271,7 +271,7 @@ void WemosMotor::setmotor(uint8_t dir, float pwm_val)
Wire.write((byte)_pwm_val);
Wire.endTransmission(); // stop transmitting
yield();
delay(0);
}
void WemosMotor::setmotor(uint8_t dir)

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