mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-10-28 11:14:58 +00:00
Merge branch 'dev' into pool-signals
This commit is contained in:
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [3.8.6] 24 September 2026
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- own rgb_led library supporting WS2812B V5 and the newer V6 RGB LEDs
|
||||||
|
|
||||||
|
## [3.8.5] 22 September 2026
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Change webUI message when upgrading to a major version of the firmware, in preparation for the upcoming 3.9.0 release.
|
||||||
|
|
||||||
|
## [3.8.4] 4 August 2026
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Allow auto-download and install of firmware versions which are smaller to allow update to the latest 3.9.0-development version.
|
||||||
|
|
||||||
## [3.8.3] 1 August 2026
|
## [3.8.3] 1 August 2026
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|||||||
@@ -57,4 +57,5 @@ This release is based on the latest Espressif/Arduino core version 3. It brings
|
|||||||
- multiple optimization in network, power down WiFi when Ethernet and WiFi nosleep is instant when selected [#3213](https://github.com/emsesp/EMS-ESP32/pull/3213)
|
- multiple optimization in network, power down WiFi when Ethernet and WiFi nosleep is instant when selected [#3213](https://github.com/emsesp/EMS-ESP32/pull/3213)
|
||||||
- TLS buffers (~17KB per HTTPS request) are taken from PSRAM on boards that have it, instead of fragmenting the internal heap
|
- TLS buffers (~17KB per HTTPS request) are taken from PSRAM on boards that have it, instead of fragmenting the internal heap
|
||||||
- failed versions.json fetches back off from 5 up to 160 minutes instead of retrying every 5 minutes, as each attempt blocks the main loop for as long as the DNS, connect and read timeouts allow
|
- failed versions.json fetches back off from 5 up to 160 minutes instead of retrying every 5 minutes, as each attempt blocks the main loop for as long as the DNS, connect and read timeouts allow
|
||||||
|
- optimized the LED library to support WS2812B V5 and the newer V6 RGB LEDs
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"etag": "^1.8.1",
|
"etag": "^1.8.1",
|
||||||
"mime-types": "^3.0.2",
|
"mime-types": "^3.0.2",
|
||||||
"prettier": "^3.9.8",
|
"prettier": "^3.9.9",
|
||||||
"rollup-plugin-visualizer": "^7.1.1",
|
"rollup-plugin-visualizer": "^7.1.1",
|
||||||
"terser": "^5.51.2",
|
"terser": "^5.51.2",
|
||||||
"typescript": "^6.0.3",
|
"typescript": "^6.0.3",
|
||||||
|
|||||||
Generated
+87
-87
@@ -217,7 +217,7 @@ importers:
|
|||||||
version: 2.10.6(@babel/core@7.29.7(supports-color@10.2.2))(preact@10.29.8)(supports-color@10.2.2)(vite@8.3.0(@types/node@26.6.2)(terser@5.51.2))
|
version: 2.10.6(@babel/core@7.29.7(supports-color@10.2.2))(preact@10.29.8)(supports-color@10.2.2)(vite@8.3.0(@types/node@26.6.2)(terser@5.51.2))
|
||||||
'@trivago/prettier-plugin-sort-imports':
|
'@trivago/prettier-plugin-sort-imports':
|
||||||
specifier: ^6.0.2
|
specifier: ^6.0.2
|
||||||
version: 6.0.2(prettier@3.9.8)(supports-color@10.2.2)
|
version: 6.0.2(prettier@3.9.9)(supports-color@10.2.2)
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^26.6.2
|
specifier: ^26.6.2
|
||||||
version: 26.6.2
|
version: 26.6.2
|
||||||
@@ -243,11 +243,11 @@ importers:
|
|||||||
specifier: ^3.0.2
|
specifier: ^3.0.2
|
||||||
version: 3.0.2
|
version: 3.0.2
|
||||||
prettier:
|
prettier:
|
||||||
specifier: ^3.9.8
|
specifier: ^3.9.9
|
||||||
version: 3.9.8
|
version: 3.9.9
|
||||||
rollup-plugin-visualizer:
|
rollup-plugin-visualizer:
|
||||||
specifier: ^7.1.1
|
specifier: ^7.1.1
|
||||||
version: 7.1.1(rolldown@1.2.9)
|
version: 7.1.1(rolldown@1.2.10)
|
||||||
terser:
|
terser:
|
||||||
specifier: ^5.51.2
|
specifier: ^5.51.2
|
||||||
version: 5.51.2
|
version: 5.51.2
|
||||||
@@ -627,8 +627,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
'@oxc-project/types@0.150.0':
|
'@oxc-project/types@0.151.0':
|
||||||
resolution: {integrity: sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==}
|
resolution: {integrity: sha512-J1yXrIlNDZVzE3ada310xeAw7nH8yCAyLPuUIsjKatFPmfn5bS1oW+cM+QsGOtVWd5nhSpbwZWx/rue+r5Z+PA==}
|
||||||
|
|
||||||
'@popperjs/core@2.11.8':
|
'@popperjs/core@2.11.8':
|
||||||
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
||||||
@@ -661,98 +661,98 @@ packages:
|
|||||||
'@remix-run/route-pattern@0.22.1':
|
'@remix-run/route-pattern@0.22.1':
|
||||||
resolution: {integrity: sha512-czdGJWh09Lapvcqt7Vb09KlrU2PhRJ8xQaI+AItTuD9aDJ5MAgxnS38lnys6Ll+6RJEqPiaUqTwIQhVLwFvv+w==}
|
resolution: {integrity: sha512-czdGJWh09Lapvcqt7Vb09KlrU2PhRJ8xQaI+AItTuD9aDJ5MAgxnS38lnys6Ll+6RJEqPiaUqTwIQhVLwFvv+w==}
|
||||||
|
|
||||||
'@rolldown/binding-android-arm-eabi@1.2.9':
|
'@rolldown/binding-android-arm-eabi@1.2.10':
|
||||||
resolution: {integrity: sha512-tNISae1QEf/vkb3xkRcjV5SEdzPE97We5IVaa2Z8jSszQPZ8U60B/YCYpw4QI7VidYsBtKavczXf+DyDs9WGxw==}
|
resolution: {integrity: sha512-bp9svZb+QurZeh+8H4BhrZkifEB0YBNvTVzNSJnJQkj4NrRwmQoDUCGP0vSN7PbvLeM7l1tK6GXL8mrTiH2myg==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [android]
|
os: [android]
|
||||||
|
|
||||||
'@rolldown/binding-android-arm64@1.2.9':
|
'@rolldown/binding-android-arm64@1.2.10':
|
||||||
resolution: {integrity: sha512-YC8YsI30o606GTZi0VyzYlsDKFP8W61i/QzayHDkLbNEz/IShqAmTa+hsJRj13xTHA0H+6fk4b2UmGn+Q/cMlg==}
|
resolution: {integrity: sha512-wm6Dld3RXUAZ/gRWKyUy+4W1B5CB5UeFaOzsSWJWEdxZXHH8rCYiZ5dGe6oJmhsunAPWzL7FZV+VtvmN5Ye2eA==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [android]
|
os: [android]
|
||||||
|
|
||||||
'@rolldown/binding-darwin-arm64@1.2.9':
|
'@rolldown/binding-darwin-arm64@1.2.10':
|
||||||
resolution: {integrity: sha512-IwhlH3qK5urrY8hZiEgGkHKEFN901p/p2bjxCxJlr4GyNnF7wYpUvK+Y43uaRYuC4hpfjzbR3SJC3arX1jGvmw==}
|
resolution: {integrity: sha512-UbEfXq/AqGNgRTV3ik+X/iR6mUxu2QdYAadwRxJWquUGnW6gDqdP1FtLtFXRow7RJx0ssRwi80XAPr4r+4DtsA==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@rolldown/binding-darwin-x64@1.2.9':
|
'@rolldown/binding-darwin-x64@1.2.10':
|
||||||
resolution: {integrity: sha512-XxpJfVzFh+jilRxIXUqcfYAYcunIc/XEzIizsOL1fcJee5Sf7H3mH8WlLmfHfluz5amqR88QQo9izKtmMlavAw==}
|
resolution: {integrity: sha512-7f5h17q5KZVx/ji1vb8OTq31ch1O2I7K8NPIr44GkyWTApXMIsmhWqZfgpOH10xeauqghDAvGlZktasCkcF6Eg==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@rolldown/binding-freebsd-x64@1.2.9':
|
'@rolldown/binding-freebsd-x64@1.2.10':
|
||||||
resolution: {integrity: sha512-kSfvhmgeWyfkbT3p/1s5vSgboogoah2zkm9fX2zjg2hHxSV7T4KhMWRUUaRk4OXNqoD3QAUeRqLcs1aZOK4U1g==}
|
resolution: {integrity: sha512-ynOk/eEYhC6ZB2xCGvKrEOwE58oBy9LnrAqtkrDF9Fz1VTaNdGZTsV0VarJdhPwb+sOJTGjCLwcuyRJZ1dnMcQ==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [freebsd]
|
os: [freebsd]
|
||||||
|
|
||||||
'@rolldown/binding-linux-arm-gnueabihf@1.2.9':
|
'@rolldown/binding-linux-arm-gnueabihf@1.2.10':
|
||||||
resolution: {integrity: sha512-1RVzG17pxqbTfYLC352JlLt6kKLG+6Hr30n8DlIJqsnV5luUDd2Qdx9Ayw1Cabfyb1K9k0jXEZ7evxkRoT+uiw==}
|
resolution: {integrity: sha512-ERrAs185meZZhGan7a4l3RiiJK1ArSDlHdST++uvSxe+FDbR4TwUPahT/cbZJvaG6fIpDpF78surN+tX708Y4Q==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@rolldown/binding-linux-arm64-gnu@1.2.9':
|
'@rolldown/binding-linux-arm64-gnu@1.2.10':
|
||||||
resolution: {integrity: sha512-BXqPvZ2drqVD+/Z8UpKwcs4Mp7grM+eGFku4CAEKrEtcbAsUpzREphK1sogCRZGreVPiMkiiBtw0n3TPteuqvw==}
|
resolution: {integrity: sha512-KN7OHKD0J3jy1UzBwZWPxpwhODf9IARUIJcrH+yLYKOcmegZ8luEUM38lDP1bDVj40yP6PsSzCqOJF76vljFnQ==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
libc: [glibc]
|
||||||
|
|
||||||
'@rolldown/binding-linux-arm64-musl@1.2.9':
|
'@rolldown/binding-linux-arm64-musl@1.2.10':
|
||||||
resolution: {integrity: sha512-11vWvo8YDwLzukt27J3aYDWU+gg2P7J+ZOmiJ0hkF5BXZDW7pVya7r40MXDy6ya0i9KamoENSVKIugvJNgFXIA==}
|
resolution: {integrity: sha512-8l9wP8O+wa8zD6iw6egSfzVtu7oZVfH3hlUsMM4MwbLMhxleqeoXbZzjddyK3YyNlwLhqznq3tF7PkNJ8T/V2w==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
libc: [musl]
|
||||||
|
|
||||||
'@rolldown/binding-linux-ppc64-gnu@1.2.9':
|
'@rolldown/binding-linux-ppc64-gnu@1.2.10':
|
||||||
resolution: {integrity: sha512-a1tijMkdwsIARtc0F39ApURROkf3NwqinI6TOiSSWCTR7dT96dffNvMUtDHnq64wKNTIZOIlzKrFvvFUznJiyw==}
|
resolution: {integrity: sha512-SeXNKeQzA5kLhz/J0CH6ZP0/HJ3v1xm/0YbiYpE0kK7emfRC2OIGGIaE14xzkISEGv2aYuUSpiLiU5Gbq+OI0A==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [ppc64]
|
cpu: [ppc64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
libc: [glibc]
|
||||||
|
|
||||||
'@rolldown/binding-linux-s390x-gnu@1.2.9':
|
'@rolldown/binding-linux-s390x-gnu@1.2.10':
|
||||||
resolution: {integrity: sha512-x6SQNdAvv4c3hWqTMaWuawzMX9myaCs/yEmlGsxJzkdClnHW7FbrjQuSiRDhuSYzEYoEMhsaJy9qHG/XNemJPQ==}
|
resolution: {integrity: sha512-mtht0nR+y8/hart4175Ll15w7lY8dg7CtQ+j2FDNTsDRspOWTK/2V3l0aj9sIj7XmvqxT8Yli/wq22e7feTTWg==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [s390x]
|
cpu: [s390x]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
libc: [glibc]
|
||||||
|
|
||||||
'@rolldown/binding-linux-x64-gnu@1.2.9':
|
'@rolldown/binding-linux-x64-gnu@1.2.10':
|
||||||
resolution: {integrity: sha512-9s0AZ8BFK5/n7B/TBoa2yJE3gI3KURrbXcPBlsAsvjU4VeJKgE90y1YtNxyEUIcHPQkg6/yfF3qihUrcM/Kf0Q==}
|
resolution: {integrity: sha512-FSM94nGd55NYo48usCyM/nHfUKRnqc9+b0vJNuKV0oCCpIp/OGims7rO1Nv/DkFkt0S/s2rxsJ2kkS8J3HcpeA==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
libc: [glibc]
|
||||||
|
|
||||||
'@rolldown/binding-linux-x64-musl@1.2.9':
|
'@rolldown/binding-linux-x64-musl@1.2.10':
|
||||||
resolution: {integrity: sha512-P7VWAmV+WdJluH7ovnRGoiv2i8To7GAZ+kGzfGup635cyL7SyYl3lSUaA3Gp5THf0n/Co5EyEqb2zbqq+nMOHQ==}
|
resolution: {integrity: sha512-C3YxNB16myRLs7o+B+6PnQ6jBsdIS4+AE4Ah8glVGhDpEv9AOvxhZ/1duAb4B0UGczEK/lBbccksd8VI+p6zfw==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
libc: [musl]
|
||||||
|
|
||||||
'@rolldown/binding-openharmony-arm64@1.2.9':
|
'@rolldown/binding-openharmony-arm64@1.2.10':
|
||||||
resolution: {integrity: sha512-1qixtsE4BK8h+yS3BfmZ09UhA7O/N4IACva6YBr7EBvCJraByTuRcgOTaiA62Tm0vey3UcKXLOaoGHtYmNGEVg==}
|
resolution: {integrity: sha512-571TlE/F1eeTjjdjYAMMMPs1Mfv3MtX6s3+ZKVU6HiUjZ5Njc6c/qzNy/8K3zALTZnaw3JQVYrHxvNfjm43KAg==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [openharmony]
|
os: [openharmony]
|
||||||
|
|
||||||
'@rolldown/binding-win32-arm64-msvc@1.2.9':
|
'@rolldown/binding-win32-arm64-msvc@1.2.10':
|
||||||
resolution: {integrity: sha512-ok8IQjcEPs1AKZfuEUznVBrJw+gK4soq+bx8b1X2XoMqVClarc1q5JDmVtWXY1xfr6ZuHTAsPXHTgTrqKTZeww==}
|
resolution: {integrity: sha512-QXW+ZWaiqs2c7Fi++D/SsW07LTPcUrncxcskJGfGNBoaLik1IU6fJymz4HsqwEO0u5Iq11yTO0B/mc4cPk7jrQ==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@rolldown/binding-win32-x64-msvc@1.2.9':
|
'@rolldown/binding-win32-x64-msvc@1.2.10':
|
||||||
resolution: {integrity: sha512-Ip2mXoU0hM0boq3Rf+ekuT653OROSo6aSYcPT1VHE4q52KvyxgFkQgrgb/IEsxOuvQ2fZZbs8khJAyCEPM24/g==}
|
resolution: {integrity: sha512-5FQFGgah17YeMtG1Yd5a+rMxQpTksyNXxRtKz06FVTaQw3RKYUJQbUoKk0/5jrXBpDo+7makNP7UHA2LQyH64A==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
@@ -1338,8 +1338,8 @@ packages:
|
|||||||
duplexer3@0.1.5:
|
duplexer3@0.1.5:
|
||||||
resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==}
|
resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==}
|
||||||
|
|
||||||
electron-to-chromium@1.5.434:
|
electron-to-chromium@1.5.438:
|
||||||
resolution: {integrity: sha512-7EeFW9OLf1NN9NKQP9xdOE/XKqjxv7JDPP6AEAkHli11+Fae1OaLoNuAf13hQv1PPNtuES0pLQSPk2+fS3s8ww==}
|
resolution: {integrity: sha512-AN9xMU1hJiT65LkCUPL1DZm5TCbOPb2Qsm5pYwFLEXp6/qj9SdT4yMiqs7Qqstwvkn1zF7l36SRGt+s6XcJ0FA==}
|
||||||
|
|
||||||
emoji-regex@10.6.0:
|
emoji-regex@10.6.0:
|
||||||
resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
|
resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
|
||||||
@@ -2318,8 +2318,8 @@ packages:
|
|||||||
node-html-parser@6.1.13:
|
node-html-parser@6.1.13:
|
||||||
resolution: {integrity: sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==}
|
resolution: {integrity: sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==}
|
||||||
|
|
||||||
node-releases@2.0.56:
|
node-releases@2.0.57:
|
||||||
resolution: {integrity: sha512-x0InOIyzgdk+eyaWaRJFH5snEtiImgBgblZ2CyPrLmqqcuMQkEvcDPHbzqbD8eDsSeJbVOjn+crzyzHaM4D+/A==}
|
resolution: {integrity: sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
normalize-package-data@2.5.0:
|
normalize-package-data@2.5.0:
|
||||||
@@ -2555,8 +2555,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==}
|
resolution: {integrity: sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
prettier@3.9.8:
|
prettier@3.9.9:
|
||||||
resolution: {integrity: sha512-WRFq3Wn3WId7LLROfMLdH7xaFr2jR62wU8nLO6rQUOLOxNZUviyJQs1M0iIhLexSFy+L+w0ch66wtoO2jRjG0A==}
|
resolution: {integrity: sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@@ -2686,8 +2686,8 @@ packages:
|
|||||||
deprecated: Rimraf versions prior to v4 are no longer supported
|
deprecated: Rimraf versions prior to v4 are no longer supported
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
rolldown@1.2.9:
|
rolldown@1.2.10:
|
||||||
resolution: {integrity: sha512-hx/Pv0N1haXRb11qkfnK5MXB/iqr7i0yjWQqmO9uHqZpBgQSqzc8UsSnEpalsh+j1I8qQ2CkXAkJC8Br3dKSlg==}
|
resolution: {integrity: sha512-OxkA08pSryMK7B3XiFA09B4OJ1xJMPgIYCBMY2xchzpqgBGsV1o0DetPAE+Sl3N3L4oCPiEzmHVSOj7iR04Zog==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@@ -3591,7 +3591,7 @@ snapshots:
|
|||||||
'@nodelib/fs.scandir': 2.1.5
|
'@nodelib/fs.scandir': 2.1.5
|
||||||
fastq: 1.20.3
|
fastq: 1.20.3
|
||||||
|
|
||||||
'@oxc-project/types@0.150.0': {}
|
'@oxc-project/types@0.151.0': {}
|
||||||
|
|
||||||
'@popperjs/core@2.11.8': {}
|
'@popperjs/core@2.11.8': {}
|
||||||
|
|
||||||
@@ -3638,49 +3638,49 @@ snapshots:
|
|||||||
|
|
||||||
'@remix-run/route-pattern@0.22.1': {}
|
'@remix-run/route-pattern@0.22.1': {}
|
||||||
|
|
||||||
'@rolldown/binding-android-arm-eabi@1.2.9':
|
'@rolldown/binding-android-arm-eabi@1.2.10':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@rolldown/binding-android-arm64@1.2.9':
|
'@rolldown/binding-android-arm64@1.2.10':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@rolldown/binding-darwin-arm64@1.2.9':
|
'@rolldown/binding-darwin-arm64@1.2.10':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@rolldown/binding-darwin-x64@1.2.9':
|
'@rolldown/binding-darwin-x64@1.2.10':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@rolldown/binding-freebsd-x64@1.2.9':
|
'@rolldown/binding-freebsd-x64@1.2.10':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@rolldown/binding-linux-arm-gnueabihf@1.2.9':
|
'@rolldown/binding-linux-arm-gnueabihf@1.2.10':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@rolldown/binding-linux-arm64-gnu@1.2.9':
|
'@rolldown/binding-linux-arm64-gnu@1.2.10':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@rolldown/binding-linux-arm64-musl@1.2.9':
|
'@rolldown/binding-linux-arm64-musl@1.2.10':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@rolldown/binding-linux-ppc64-gnu@1.2.9':
|
'@rolldown/binding-linux-ppc64-gnu@1.2.10':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@rolldown/binding-linux-s390x-gnu@1.2.9':
|
'@rolldown/binding-linux-s390x-gnu@1.2.10':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@rolldown/binding-linux-x64-gnu@1.2.9':
|
'@rolldown/binding-linux-x64-gnu@1.2.10':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@rolldown/binding-linux-x64-musl@1.2.9':
|
'@rolldown/binding-linux-x64-musl@1.2.10':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@rolldown/binding-openharmony-arm64@1.2.9':
|
'@rolldown/binding-openharmony-arm64@1.2.10':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@rolldown/binding-win32-arm64-msvc@1.2.9':
|
'@rolldown/binding-win32-arm64-msvc@1.2.10':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@rolldown/binding-win32-x64-msvc@1.2.9':
|
'@rolldown/binding-win32-x64-msvc@1.2.10':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@rolldown/pluginutils@1.0.1': {}
|
'@rolldown/pluginutils@1.0.1': {}
|
||||||
@@ -3707,7 +3707,7 @@ snapshots:
|
|||||||
react-virtualized-auto-sizer: 1.0.26(react-dom@19.3.0(react@19.3.0))(react@19.3.0)
|
react-virtualized-auto-sizer: 1.0.26(react-dom@19.3.0(react@19.3.0))(react@19.3.0)
|
||||||
react-window: 1.8.11(react-dom@19.3.0(react@19.3.0))(react@19.3.0)
|
react-window: 1.8.11(react-dom@19.3.0(react@19.3.0))(react@19.3.0)
|
||||||
|
|
||||||
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.9.8)(supports-color@10.2.2)':
|
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.9.9)(supports-color@10.2.2)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/generator': 7.29.8
|
'@babel/generator': 7.29.8
|
||||||
'@babel/parser': 7.29.9
|
'@babel/parser': 7.29.9
|
||||||
@@ -3717,7 +3717,7 @@ snapshots:
|
|||||||
lodash-es: 4.18.1
|
lodash-es: 4.18.1
|
||||||
minimatch: 9.0.9
|
minimatch: 9.0.9
|
||||||
parse-imports-exports: 0.2.4
|
parse-imports-exports: 0.2.4
|
||||||
prettier: 3.9.8
|
prettier: 3.9.9
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -4009,8 +4009,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
baseline-browser-mapping: 2.11.25
|
baseline-browser-mapping: 2.11.25
|
||||||
caniuse-lite: 1.0.30001810
|
caniuse-lite: 1.0.30001810
|
||||||
electron-to-chromium: 1.5.434
|
electron-to-chromium: 1.5.438
|
||||||
node-releases: 2.0.56
|
node-releases: 2.0.57
|
||||||
update-browserslist-db: 1.3.3(browserslist@4.29.0)
|
update-browserslist-db: 1.3.3(browserslist@4.29.0)
|
||||||
|
|
||||||
buffer-alloc-unsafe@1.1.0: {}
|
buffer-alloc-unsafe@1.1.0: {}
|
||||||
@@ -4374,7 +4374,7 @@ snapshots:
|
|||||||
|
|
||||||
duplexer3@0.1.5: {}
|
duplexer3@0.1.5: {}
|
||||||
|
|
||||||
electron-to-chromium@1.5.434: {}
|
electron-to-chromium@1.5.438: {}
|
||||||
|
|
||||||
emoji-regex@10.6.0: {}
|
emoji-regex@10.6.0: {}
|
||||||
|
|
||||||
@@ -5311,7 +5311,7 @@ snapshots:
|
|||||||
css-select: 5.2.2
|
css-select: 5.2.2
|
||||||
he: 1.2.0
|
he: 1.2.0
|
||||||
|
|
||||||
node-releases@2.0.56: {}
|
node-releases@2.0.57: {}
|
||||||
|
|
||||||
normalize-package-data@2.5.0:
|
normalize-package-data@2.5.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -5515,7 +5515,7 @@ snapshots:
|
|||||||
|
|
||||||
prepend-http@2.0.0: {}
|
prepend-http@2.0.0: {}
|
||||||
|
|
||||||
prettier@3.9.8: {}
|
prettier@3.9.9: {}
|
||||||
|
|
||||||
process-nextick-args@2.0.1: {}
|
process-nextick-args@2.0.1: {}
|
||||||
|
|
||||||
@@ -5645,35 +5645,35 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
glob: 7.2.3
|
glob: 7.2.3
|
||||||
|
|
||||||
rolldown@1.2.9:
|
rolldown@1.2.10:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@oxc-project/types': 0.150.0
|
'@oxc-project/types': 0.151.0
|
||||||
'@rolldown/pluginutils': 1.0.1
|
'@rolldown/pluginutils': 1.0.1
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@rolldown/binding-android-arm-eabi': 1.2.9
|
'@rolldown/binding-android-arm-eabi': 1.2.10
|
||||||
'@rolldown/binding-android-arm64': 1.2.9
|
'@rolldown/binding-android-arm64': 1.2.10
|
||||||
'@rolldown/binding-darwin-arm64': 1.2.9
|
'@rolldown/binding-darwin-arm64': 1.2.10
|
||||||
'@rolldown/binding-darwin-x64': 1.2.9
|
'@rolldown/binding-darwin-x64': 1.2.10
|
||||||
'@rolldown/binding-freebsd-x64': 1.2.9
|
'@rolldown/binding-freebsd-x64': 1.2.10
|
||||||
'@rolldown/binding-linux-arm-gnueabihf': 1.2.9
|
'@rolldown/binding-linux-arm-gnueabihf': 1.2.10
|
||||||
'@rolldown/binding-linux-arm64-gnu': 1.2.9
|
'@rolldown/binding-linux-arm64-gnu': 1.2.10
|
||||||
'@rolldown/binding-linux-arm64-musl': 1.2.9
|
'@rolldown/binding-linux-arm64-musl': 1.2.10
|
||||||
'@rolldown/binding-linux-ppc64-gnu': 1.2.9
|
'@rolldown/binding-linux-ppc64-gnu': 1.2.10
|
||||||
'@rolldown/binding-linux-s390x-gnu': 1.2.9
|
'@rolldown/binding-linux-s390x-gnu': 1.2.10
|
||||||
'@rolldown/binding-linux-x64-gnu': 1.2.9
|
'@rolldown/binding-linux-x64-gnu': 1.2.10
|
||||||
'@rolldown/binding-linux-x64-musl': 1.2.9
|
'@rolldown/binding-linux-x64-musl': 1.2.10
|
||||||
'@rolldown/binding-openharmony-arm64': 1.2.9
|
'@rolldown/binding-openharmony-arm64': 1.2.10
|
||||||
'@rolldown/binding-win32-arm64-msvc': 1.2.9
|
'@rolldown/binding-win32-arm64-msvc': 1.2.10
|
||||||
'@rolldown/binding-win32-x64-msvc': 1.2.9
|
'@rolldown/binding-win32-x64-msvc': 1.2.10
|
||||||
|
|
||||||
rollup-plugin-visualizer@7.1.1(rolldown@1.2.9):
|
rollup-plugin-visualizer@7.1.1(rolldown@1.2.10):
|
||||||
dependencies:
|
dependencies:
|
||||||
open: 11.0.4
|
open: 11.0.4
|
||||||
picomatch: 4.0.7
|
picomatch: 4.0.7
|
||||||
source-map: 0.8.0
|
source-map: 0.8.0
|
||||||
yargs: 18.2.0
|
yargs: 18.2.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
rolldown: 1.2.9
|
rolldown: 1.2.10
|
||||||
|
|
||||||
run-applescript@7.1.0: {}
|
run-applescript@7.1.0: {}
|
||||||
|
|
||||||
@@ -6032,7 +6032,7 @@ snapshots:
|
|||||||
lightningcss: 1.33.0
|
lightningcss: 1.33.0
|
||||||
picomatch: 4.0.7
|
picomatch: 4.0.7
|
||||||
postcss: 8.5.28
|
postcss: 8.5.28
|
||||||
rolldown: 1.2.9
|
rolldown: 1.2.10
|
||||||
tinyglobby: 0.2.17
|
tinyglobby: 0.2.17
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/node': 26.6.2
|
'@types/node': 26.6.2
|
||||||
|
|||||||
@@ -148,7 +148,5 @@ double ledcSetup(uint8_t chan, double freq, uint8_t bit_num) {
|
|||||||
void ledcAttachPin(uint8_t pin, uint8_t chan) {};
|
void ledcAttachPin(uint8_t pin, uint8_t chan) {};
|
||||||
void ledcAttach(uint8_t pin, uint8_t chan, uint8_t bit_num) {};
|
void ledcAttach(uint8_t pin, uint8_t chan, uint8_t bit_num) {};
|
||||||
void ledcWrite(uint8_t chan, uint32_t duty) {};
|
void ledcWrite(uint8_t chan, uint32_t duty) {};
|
||||||
void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val) {};
|
|
||||||
void rgbLedWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val) {};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -83,8 +83,6 @@ double ledcSetup(uint8_t chan, double freq, uint8_t bit_num);
|
|||||||
void ledcAttachPin(uint8_t pin, uint8_t chan);
|
void ledcAttachPin(uint8_t pin, uint8_t chan);
|
||||||
void ledcAttach(uint8_t pin, uint8_t chan, uint8_t bit_num);
|
void ledcAttach(uint8_t pin, uint8_t chan, uint8_t bit_num);
|
||||||
void ledcWrite(uint8_t chan, uint32_t duty);
|
void ledcWrite(uint8_t chan, uint32_t duty);
|
||||||
void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val);
|
|
||||||
void rgbLedWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val);
|
|
||||||
|
|
||||||
inline int rtc_get_reset_reason(int cpu_no) {
|
inline int rtc_get_reset_reason(int cpu_no) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||||
"prettier": "^3.9.8"
|
"prettier": "^3.9.9"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@12.6.0"
|
"packageManager": "pnpm@12.6.0"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+8
-8
@@ -175,10 +175,10 @@ importers:
|
|||||||
devDependencies:
|
devDependencies:
|
||||||
'@trivago/prettier-plugin-sort-imports':
|
'@trivago/prettier-plugin-sort-imports':
|
||||||
specifier: ^6.0.2
|
specifier: ^6.0.2
|
||||||
version: 6.0.2(prettier@3.9.8)
|
version: 6.0.2(prettier@3.9.9)
|
||||||
prettier:
|
prettier:
|
||||||
specifier: ^3.9.8
|
specifier: ^3.9.9
|
||||||
version: 3.9.8
|
version: 3.9.9
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -303,8 +303,8 @@ packages:
|
|||||||
picocolors@1.1.1:
|
picocolors@1.1.1:
|
||||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||||
|
|
||||||
prettier@3.9.8:
|
prettier@3.9.9:
|
||||||
resolution: {integrity: sha512-WRFq3Wn3WId7LLROfMLdH7xaFr2jR62wU8nLO6rQUOLOxNZUviyJQs1M0iIhLexSFy+L+w0ch66wtoO2jRjG0A==}
|
resolution: {integrity: sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@@ -373,7 +373,7 @@ snapshots:
|
|||||||
|
|
||||||
'@msgpack/msgpack@3.1.3': {}
|
'@msgpack/msgpack@3.1.3': {}
|
||||||
|
|
||||||
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.9.8)':
|
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.9.9)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/generator': 7.29.8
|
'@babel/generator': 7.29.8
|
||||||
'@babel/parser': 7.29.9
|
'@babel/parser': 7.29.9
|
||||||
@@ -383,7 +383,7 @@ snapshots:
|
|||||||
lodash-es: 4.18.1
|
lodash-es: 4.18.1
|
||||||
minimatch: 9.0.9
|
minimatch: 9.0.9
|
||||||
parse-imports-exports: 0.2.4
|
parse-imports-exports: 0.2.4
|
||||||
prettier: 3.9.8
|
prettier: 3.9.9
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -421,4 +421,4 @@ snapshots:
|
|||||||
|
|
||||||
picocolors@1.1.1: {}
|
picocolors@1.1.1: {}
|
||||||
|
|
||||||
prettier@3.9.8: {}
|
prettier@3.9.9: {}
|
||||||
|
|||||||
+1
-1
@@ -786,7 +786,6 @@ negcond
|
|||||||
negheat
|
negheat
|
||||||
nein
|
nein
|
||||||
nennvolumentstrom
|
nennvolumentstrom
|
||||||
neopixel
|
|
||||||
netcmp
|
netcmp
|
||||||
netflowtemp
|
netflowtemp
|
||||||
netif
|
netif
|
||||||
@@ -1352,3 +1351,4 @@ zyxwvutsrqponmlkjihgfedcba
|
|||||||
öffnen
|
öffnen
|
||||||
česky
|
česky
|
||||||
živanović
|
živanović
|
||||||
|
neopixel
|
||||||
@@ -71,7 +71,7 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
|||||||
_is_filesystem = true;
|
_is_filesystem = true;
|
||||||
_md5[0] = '\0'; // clear any stale md5 so Update.end() doesn't compare against it
|
_md5[0] = '\0'; // clear any stale md5 so Update.end() doesn't compare against it
|
||||||
_md5_applied = false;
|
_md5_applied = false;
|
||||||
} else if ((extension == "bin") && (filesize > 1000000)) {
|
} else if ((extension == "bin") && (filesize >= emsesp::System::MIN_FIRMWARE_SIZE)) {
|
||||||
_is_firmware = true;
|
_is_firmware = true;
|
||||||
} else if (extension == "json") {
|
} else if (extension == "json") {
|
||||||
_md5[0] = '\0'; // clear md5
|
_md5[0] = '\0'; // clear md5
|
||||||
|
|||||||
@@ -307,7 +307,7 @@ void AnalogSensor::reload(bool get_nvs) {
|
|||||||
uint8_t r = v / 10000;
|
uint8_t r = v / 10000;
|
||||||
uint8_t g = (v - r * 10000) / 100;
|
uint8_t g = (v - r * 10000) / 100;
|
||||||
uint8_t b = v % 100;
|
uint8_t b = v % 100;
|
||||||
rgbLedWrite(sensor.gpio(), 2 * r, 2 * g, 2 * b);
|
rgb_led::write(sensor.gpio(), 2 * r, 2 * g, 2 * b);
|
||||||
LOG_DEBUG("RGB set to %d, %d, %d", r, g, b);
|
LOG_DEBUG("RGB set to %d, %d, %d", r, g, b);
|
||||||
} else if (sensor.type() == AnalogType::DIGITAL_OUT) {
|
} else if (sensor.type() == AnalogType::DIGITAL_OUT) {
|
||||||
LOG_DEBUG("Digital Write on GPIO %02d", sensor.gpio());
|
LOG_DEBUG("Digital Write on GPIO %02d", sensor.gpio());
|
||||||
@@ -405,8 +405,8 @@ void AnalogSensor::measure() {
|
|||||||
sensor.sum_ = (sensor.sum_ * 15 + a * 16) / 16;
|
sensor.sum_ = (sensor.sum_ * 15 + a * 16) / 16;
|
||||||
sensor.analog_ = sensor.sum_ / 16;
|
sensor.analog_ = sensor.sum_ / 16;
|
||||||
}
|
}
|
||||||
if (sensor.analog_ > 0 && sensor.analog_ < 3300 && (sensor.last_reading_ + 1 < sensor.analog_ || sensor.last_reading_ > sensor.analog_ + 1)) {
|
if (sensor.analog_ > 0 && sensor.analog_ < NTC_VREF_MV && (sensor.last_reading_ + 1 < sensor.analog_ || sensor.last_reading_ > sensor.analog_ + 1)) {
|
||||||
sensor.set_value(sensor.offset() + 1 / (1 / T25 + log((double)sensor.analog_ / (3300 - sensor.analog_) * (Rt / R0)) / Beta)
|
sensor.set_value(sensor.offset() + 1 / (1 / T25 + log((double)sensor.analog_ / (NTC_VREF_MV - sensor.analog_) * (Rt / R0)) / Beta)
|
||||||
- T0); // Temperature in Celsius
|
- T0); // Temperature in Celsius
|
||||||
sensor.last_reading_ = sensor.analog_;
|
sensor.last_reading_ = sensor.analog_;
|
||||||
sensorreads_++;
|
sensorreads_++;
|
||||||
@@ -424,7 +424,7 @@ void AnalogSensor::measure() {
|
|||||||
edgecnt[index] = 0;
|
edgecnt[index] = 0;
|
||||||
portEXIT_CRITICAL_ISR(&mux);
|
portEXIT_CRITICAL_ISR(&mux);
|
||||||
sensor.set_value(sensor.factor() * 1000000.0 / t);
|
sensor.set_value(sensor.factor() * 1000000.0 / t);
|
||||||
} else if (micros() - edge[index] > 10000000ul && sensor.value() > 0) {
|
} else if (micros() - edge[index] > FREQ_ZERO_TIMEOUT_US && sensor.value() > 0) {
|
||||||
sensor.set_value(0);
|
sensor.set_value(0);
|
||||||
}
|
}
|
||||||
if (sensor.value() != oldval) {
|
if (sensor.value() != oldval) {
|
||||||
@@ -999,7 +999,7 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) {
|
|||||||
uint8_t r = v / 10000;
|
uint8_t r = v / 10000;
|
||||||
uint8_t g = (v - r * 10000) / 100;
|
uint8_t g = (v - r * 10000) / 100;
|
||||||
uint8_t b = v % 100;
|
uint8_t b = v % 100;
|
||||||
rgbLedWrite(sensor.gpio(), 2 * r, 2 * g, 2 * b);
|
rgb_led::write(sensor.gpio(), 2 * r, 2 * g, 2 * b);
|
||||||
LOG_DEBUG("RGB set to %d, %d, %d", r, g, b);
|
LOG_DEBUG("RGB set to %d, %d, %d", r, g, b);
|
||||||
} else if (sensor.type() == AnalogType::PULSE) {
|
} else if (sensor.type() == AnalogType::PULSE) {
|
||||||
uint8_t v = val;
|
uint8_t v = val;
|
||||||
|
|||||||
@@ -189,8 +189,10 @@ class AnalogSensor {
|
|||||||
static constexpr double T25 = 298.15;
|
static constexpr double T25 = 298.15;
|
||||||
static constexpr double R0 = 100000;
|
static constexpr double R0 = 100000;
|
||||||
static constexpr double Rt = 60000;
|
static constexpr double Rt = 60000;
|
||||||
|
static constexpr uint32_t NTC_VREF_MV = 3300; // NTC voltage divider supply (in mV) - 3.3 V
|
||||||
static constexpr uint8_t MAX_SENSORS = 20;
|
static constexpr uint8_t MAX_SENSORS = 20;
|
||||||
static constexpr uint32_t MEASURE_ANALOG_INTERVAL = 500;
|
static constexpr uint32_t MEASURE_ANALOG_INTERVAL = 500;
|
||||||
|
static constexpr uint32_t FREQ_ZERO_TIMEOUT_US = 10000000; // report 0 Hz if no edge arrives for this long (in us) - 10 seconds
|
||||||
|
|
||||||
static uuid::log::Logger logger_;
|
static uuid::log::Logger logger_;
|
||||||
void remove_ha_topic(const int8_t type, const uint8_t id) const;
|
void remove_ha_topic(const int8_t type, const uint8_t id) const;
|
||||||
|
|||||||
@@ -2281,8 +2281,8 @@ bool EMSdevice::handle_telegram(const std::shared_ptr<const Telegram> & telegram
|
|||||||
EMSESP::logger().debug("This telegram (%s) is not recognized by the EMS bus", tf.telegram_type_name_);
|
EMSESP::logger().debug("This telegram (%s) is not recognized by the EMS bus", tf.telegram_type_name_);
|
||||||
#endif
|
#endif
|
||||||
// removing fetch after start causes issue: https://github.com/emsesp/EMS-ESP32/issues/1420
|
// removing fetch after start causes issue: https://github.com/emsesp/EMS-ESP32/issues/1420
|
||||||
// continue retry the first 5 minutes, then disable (added 15.3.2024)
|
// continue retrying for a while after boot, then disable (added 15.3.2024)
|
||||||
if (uuid::get_uptime_sec() > 600) {
|
if (uuid::get_uptime_sec() > FETCH_RETRY_DURATION_SEC) {
|
||||||
tf.fetch_ = false;
|
tf.fetch_ = false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -441,6 +441,9 @@ class EMSdevice {
|
|||||||
|
|
||||||
static constexpr uint8_t EMS_DEVICES_MAX_TELEGRAMS = 20;
|
static constexpr uint8_t EMS_DEVICES_MAX_TELEGRAMS = 20;
|
||||||
|
|
||||||
|
// keep fetching telegrams the bus master doesn't recognise for this long after boot, then give up (in seconds) - 10 minutes
|
||||||
|
static constexpr uint32_t FETCH_RETRY_DURATION_SEC = 600;
|
||||||
|
|
||||||
// static device IDs
|
// static device IDs
|
||||||
static constexpr uint8_t EMS_DEVICE_ID_BOILER = 0x08; // fixed device_id for Master Boiler/UBA
|
static constexpr uint8_t EMS_DEVICE_ID_BOILER = 0x08; // fixed device_id for Master Boiler/UBA
|
||||||
static constexpr uint8_t EMS_DEVICE_ID_HS1 = 0x70; // fixed device_id for 1st. Cascade Boiler/UBA
|
static constexpr uint8_t EMS_DEVICE_ID_HS1 = 0x70; // fixed device_id for 1st. Cascade Boiler/UBA
|
||||||
|
|||||||
+2
-2
@@ -1980,12 +1980,12 @@ void EMSESP::loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset status after 5 Minutes
|
// reset status after a timeout
|
||||||
if (EMSESP::system_.systemStatus() != SYSTEM_STATUS::SYSTEM_STATUS_NORMAL) {
|
if (EMSESP::system_.systemStatus() != SYSTEM_STATUS::SYSTEM_STATUS_NORMAL) {
|
||||||
static uint32_t starttime = 0;
|
static uint32_t starttime = 0;
|
||||||
if (starttime == 0) {
|
if (starttime == 0) {
|
||||||
starttime = uuid::get_uptime_ms();
|
starttime = uuid::get_uptime_ms();
|
||||||
} else if (uuid::get_uptime_ms() - starttime > 300000) {
|
} else if (uuid::get_uptime_ms() - starttime > SYSTEM_STATUS_RESET_TIMEOUT) {
|
||||||
starttime = 0;
|
starttime = 0;
|
||||||
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_NORMAL);
|
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_NORMAL);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -291,6 +291,8 @@ class EMSESP {
|
|||||||
static constexpr uint32_t EMS_FETCH_FREQUENCY = 60000; // check every minute
|
static constexpr uint32_t EMS_FETCH_FREQUENCY = 60000; // check every minute
|
||||||
static constexpr uint8_t EMS_WAIT_KM_TIMEOUT = 60; // wait one minute
|
static constexpr uint8_t EMS_WAIT_KM_TIMEOUT = 60; // wait one minute
|
||||||
|
|
||||||
|
static constexpr uint32_t SYSTEM_STATUS_RESET_TIMEOUT = 300000; // return a non-normal system status to normal after this (in ms) - 5 minutes
|
||||||
|
|
||||||
struct Device_record {
|
struct Device_record {
|
||||||
uint8_t product_id;
|
uint8_t product_id;
|
||||||
EMSdevice::DeviceType device_type;
|
EMSdevice::DeviceType device_type;
|
||||||
|
|||||||
@@ -66,13 +66,13 @@ int HttpClient::request(std::string url, const std::string & method, const std::
|
|||||||
ssl_client->setInsecure();
|
ssl_client->setInsecure();
|
||||||
// Notes: with root CA we should set here: ssl_client->setCACert(rootCACert);
|
// Notes: with root CA we should set here: ssl_client->setCACert(rootCACert);
|
||||||
// 1 KB RX buffer is fine for small JSON-style endpoints used by the scheduler/shunting-yard but it is NOT enough for servers that send full-size TLS records (>1 KB)
|
// 1 KB RX buffer is fine for small JSON-style endpoints used by the scheduler/shunting-yard but it is NOT enough for servers that send full-size TLS records (>1 KB)
|
||||||
ssl_client->setBufferSizes(16384, 1024);
|
ssl_client->setBufferSizes(TLS_RX_BUFFER_SIZE, TLS_TX_BUFFER_SIZE);
|
||||||
ssl_client->setSessionTimeout(120); // Set the timeout in seconds (>=120 seconds)
|
ssl_client->setSessionTimeout(TLS_SESSION_TIMEOUT_S);
|
||||||
}
|
}
|
||||||
// WiFiClient is NetworkClient, which declares no setTimeout() of its own - calling it binds to
|
// WiFiClient is NetworkClient, which declares no setTimeout() of its own - calling it binds to
|
||||||
// Stream::setTimeout() and only affects readBytes(), leaving the socket on the core's 3s default
|
// Stream::setTimeout() and only affects readBytes(), leaving the socket on the core's 3s default
|
||||||
basic_client->setConnectionTimeout(CONNECT_TIMEOUT_MS);
|
basic_client->setConnectionTimeout(CONNECT_TIMEOUT_MS);
|
||||||
ssl_client->setTimeout(5); // seconds, drives BearSSL only - unused on the plain HTTP path
|
ssl_client->setTimeout(TLS_READ_TIMEOUT_S); // drives BearSSL only - unused on the plain HTTP path
|
||||||
ssl_client->setClient(basic_client, is_https); // enableSSL = false for plain HTTP
|
ssl_client->setClient(basic_client, is_https); // enableSSL = false for plain HTTP
|
||||||
|
|
||||||
const uint16_t port = is_https ? 443 : 80;
|
const uint16_t port = is_https ? 443 : 80;
|
||||||
|
|||||||
@@ -29,6 +29,14 @@ class HttpClient {
|
|||||||
public:
|
public:
|
||||||
static int request(std::string url, const std::string & method, const std::string & value, JsonObjectConst headers, std::string & result);
|
static int request(std::string url, const std::string & method, const std::string & value, JsonObjectConst headers, std::string & result);
|
||||||
|
|
||||||
|
// ESP_SSLClient (BearSSL) settings, shared by every TLS client in the project.
|
||||||
|
// The RX buffer must hold one full TLS record: servers such as GitHub's CDN send up-to-16 KB
|
||||||
|
// records and don't negotiate max_fragment_length, so a smaller buffer can't decode the body
|
||||||
|
static constexpr size_t TLS_RX_BUFFER_SIZE = 16384;
|
||||||
|
static constexpr size_t TLS_TX_BUFFER_SIZE = 1024;
|
||||||
|
static constexpr uint32_t TLS_SESSION_TIMEOUT_S = 120; // in seconds, the library minimum
|
||||||
|
static constexpr uint32_t TLS_READ_TIMEOUT_S = 5; // in seconds, not ms like the socket timeouts
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uint32_t CONNECT_TIMEOUT_MS = 5000; // TCP connect, the core defaults to 3s
|
static constexpr uint32_t CONNECT_TIMEOUT_MS = 5000; // TCP connect, the core defaults to 3s
|
||||||
static constexpr uint32_t FIRST_BYTE_TIMEOUT_MS = 8000; // how long the server may take to start replying
|
static constexpr uint32_t FIRST_BYTE_TIMEOUT_MS = 8000; // how long the server may take to start replying
|
||||||
|
|||||||
+151
-69
@@ -19,19 +19,139 @@
|
|||||||
#include "led.h"
|
#include "led.h"
|
||||||
#include "emsesp.h"
|
#include "emsesp.h"
|
||||||
|
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
#include <esp32-hal-rmt.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
|
namespace rgb_led {
|
||||||
|
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// durations in 100 ns ticks
|
||||||
|
constexpr uint16_t T0H = 3; // 300 ns
|
||||||
|
constexpr uint16_t T0L = 9; // 900 ns
|
||||||
|
constexpr uint16_t T1H = 8; // 800 ns
|
||||||
|
constexpr uint16_t T1L = 6; // 600 ns
|
||||||
|
|
||||||
|
// rmt_data_t.val layout: duration0:15, level0:1, duration1:15, level1:1 (high then low)
|
||||||
|
constexpr uint32_t SYMBOL_0 = T0H | (1UL << 15) | (static_cast<uint32_t>(T0L) << 16);
|
||||||
|
constexpr uint32_t SYMBOL_1 = T1H | (1UL << 15) | (static_cast<uint32_t>(T1L) << 16);
|
||||||
|
|
||||||
|
constexpr uint32_t RMT_FREQ_HZ = 10000000; // 10 MHz
|
||||||
|
constexpr uint32_t RESET_US = 300;
|
||||||
|
constexpr uint8_t NO_PIN = 0xFF;
|
||||||
|
constexpr uint8_t MAX_PINS = 2; // status LED + an RGB analog sensor; the ESP32-C3 only has 2 RMT TX channels
|
||||||
|
|
||||||
|
struct Channel {
|
||||||
|
uint8_t pin = NO_PIN;
|
||||||
|
uint32_t last_tx_us = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
Channel channels[MAX_PINS];
|
||||||
|
|
||||||
|
Channel * init(uint8_t pin) {
|
||||||
|
Channel * free_slot = nullptr;
|
||||||
|
for (auto & ch : channels) {
|
||||||
|
if (ch.pin == pin) {
|
||||||
|
return &ch;
|
||||||
|
}
|
||||||
|
if (!free_slot && ch.pin == NO_PIN) {
|
||||||
|
free_slot = &ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!free_slot) {
|
||||||
|
// all slots in use: release the least recently written one
|
||||||
|
const uint32_t now = micros();
|
||||||
|
free_slot = &channels[0];
|
||||||
|
for (auto & ch : channels) {
|
||||||
|
if (now - ch.last_tx_us > now - free_slot->last_tx_us) {
|
||||||
|
free_slot = &ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end(free_slot->pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rmtInit(pin, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, RMT_FREQ_HZ)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
free_slot->pin = pin;
|
||||||
|
free_slot->last_tx_us = micros() - RESET_US; // no wait on first write
|
||||||
|
return free_slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool write(uint8_t pin, uint8_t red, uint8_t green, uint8_t blue, Order order) {
|
||||||
|
Channel * ch = init(pin);
|
||||||
|
if (!ch) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// enforce the latch/reset gap since the previous frame (V6 needs > 280 us)
|
||||||
|
const uint32_t elapsed = micros() - ch->last_tx_us;
|
||||||
|
if (elapsed < RESET_US) {
|
||||||
|
delayMicroseconds(RESET_US - elapsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t bytes[3] = {order == Order::GRB ? green : red, order == Order::GRB ? red : green, blue};
|
||||||
|
|
||||||
|
rmt_data_t frame[24];
|
||||||
|
for (uint8_t i = 0; i < 24; i++) {
|
||||||
|
frame[i].val = (bytes[i >> 3] & (0x80 >> (i & 7))) ? SYMBOL_1 : SYMBOL_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool ok = rmtWrite(pin, frame, 24, RMT_WAIT_FOR_EVER);
|
||||||
|
ch->last_tx_us = micros();
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void end(uint8_t pin) {
|
||||||
|
for (auto & ch : channels) {
|
||||||
|
if (ch.pin == pin && pin != NO_PIN) {
|
||||||
|
rmtDeinit(pin);
|
||||||
|
ch.pin = NO_PIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
bool write(uint8_t, uint8_t, uint8_t, uint8_t, Order) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void end(uint8_t) {
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace rgb_led
|
||||||
|
|
||||||
uuid::log::Logger LED::logger_{F_(led), uuid::log::Facility::KERN};
|
uuid::log::Logger LED::logger_{F_(led), uuid::log::Facility::KERN};
|
||||||
|
|
||||||
// initialise the LED, fetching the settings from the WebSettingsService
|
// initialise the LED, fetching the settings from the WebSettingsService
|
||||||
// set the LED to on or off when in normal operating mode
|
// set the LED to on or off when in normal operating mode
|
||||||
void LED::init() {
|
void LED::init() {
|
||||||
|
// settings may have changed: turn off and release the previous pin
|
||||||
|
if (led_gpio_) {
|
||||||
|
set_led(Color::OFF);
|
||||||
|
if (led_type_) {
|
||||||
|
rgb_led::end(led_gpio_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// copy the application settings
|
// copy the application settings
|
||||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||||
led_gpio_ = settings.led_gpio;
|
led_gpio_ = settings.led_gpio;
|
||||||
led_type_ = settings.led_type;
|
led_type_ = settings.led_type;
|
||||||
hide_led_ = settings.hide_led;
|
hide_led_ = settings.hide_led;
|
||||||
});
|
});
|
||||||
|
last_color_ = COLOR_UNSET;
|
||||||
|
|
||||||
if (!led_gpio_) { // 0 means disabled
|
if (!led_gpio_) { // 0 means disabled
|
||||||
LOG_INFO("LED disabled");
|
LOG_INFO("LED disabled");
|
||||||
@@ -74,10 +194,6 @@ bool LED::loop(uint8_t healthcheck, bool button_busy) {
|
|||||||
// check the system health.
|
// check the system health.
|
||||||
// Set the sequence accordingly, only if the healthcheck is not 0 and has changed
|
// Set the sequence accordingly, only if the healthcheck is not 0 and has changed
|
||||||
if (healthcheck != previous_healthcheck_) {
|
if (healthcheck != previous_healthcheck_) {
|
||||||
color_steps_[0] = Color::OFF;
|
|
||||||
color_steps_[1] = Color::OFF;
|
|
||||||
color_steps_[2] = Color::OFF;
|
|
||||||
|
|
||||||
// if the healthcheck is 0, i.e. system is healthy, reset the LED
|
// if the healthcheck is 0, i.e. system is healthy, reset the LED
|
||||||
if (healthcheck == 0) {
|
if (healthcheck == 0) {
|
||||||
reset_led();
|
reset_led();
|
||||||
@@ -85,25 +201,12 @@ bool LED::loop(uint8_t healthcheck, bool button_busy) {
|
|||||||
// 1 flash (blue) is the EMS bus is not connected
|
// 1 flash (blue) is the EMS bus is not connected
|
||||||
// 2 flashes (red, red) if the network (wifi or ethernet) is not connected
|
// 2 flashes (red, red) if the network (wifi or ethernet) is not connected
|
||||||
// 3 flashes (red, red, blue) is both the bus and the network are not connected
|
// 3 flashes (red, red, blue) is both the bus and the network are not connected
|
||||||
bool no_network = (healthcheck & System::HEALTHCHECK_NO_NETWORK) == System::HEALTHCHECK_NO_NETWORK;
|
const bool no_network = (healthcheck & System::HEALTHCHECK_NO_NETWORK) == System::HEALTHCHECK_NO_NETWORK;
|
||||||
bool no_bus = (healthcheck & System::HEALTHCHECK_NO_BUS) == System::HEALTHCHECK_NO_BUS;
|
const bool no_bus = (healthcheck & System::HEALTHCHECK_NO_BUS) == System::HEALTHCHECK_NO_BUS;
|
||||||
|
|
||||||
// set step 1
|
color_steps_[0] = no_network ? Color::RED : (no_bus ? Color::BLUE : Color::OFF);
|
||||||
if (no_network) {
|
color_steps_[1] = no_network ? Color::RED : Color::OFF;
|
||||||
color_steps_[0] = Color::RED; // red, no network
|
color_steps_[2] = (no_network && no_bus) ? Color::BLUE : Color::OFF;
|
||||||
} else if (no_bus) {
|
|
||||||
color_steps_[0] = Color::BLUE; // blue, no bus
|
|
||||||
}
|
|
||||||
|
|
||||||
// set step 2
|
|
||||||
if (no_network) {
|
|
||||||
color_steps_[1] = Color::RED; // red, no network
|
|
||||||
}
|
|
||||||
|
|
||||||
// set step 3
|
|
||||||
if (no_network && no_bus) {
|
|
||||||
color_steps_[2] = Color::BLUE; // blue, no network and no bus
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
previous_healthcheck_ = healthcheck; // must be set after reset_led(), which invalidates it
|
previous_healthcheck_ = healthcheck; // must be set after reset_led(), which invalidates it
|
||||||
@@ -126,9 +229,7 @@ bool LED::loop(uint8_t healthcheck, bool button_busy) {
|
|||||||
void LED::reset_led() {
|
void LED::reset_led() {
|
||||||
is_user_led_blink_ = false;
|
is_user_led_blink_ = false;
|
||||||
set_led(hide_led_ ? Color::OFF : Color::GREEN); // Green
|
set_led(hide_led_ ? Color::OFF : Color::GREEN); // Green
|
||||||
color_steps_[0] = Color::OFF;
|
color_steps_[0] = color_steps_[1] = color_steps_[2] = Color::OFF;
|
||||||
color_steps_[1] = Color::OFF;
|
|
||||||
color_steps_[2] = Color::OFF;
|
|
||||||
|
|
||||||
// invalidate the last health state so the sequence is rebuilt on the next check,
|
// invalidate the last health state so the sequence is rebuilt on the next check,
|
||||||
// needed because the color steps above have been cleared
|
// needed because the color steps above have been cleared
|
||||||
@@ -163,9 +264,10 @@ void LED::led_fast_flash() {
|
|||||||
// set LED on/off or RGB color
|
// set LED on/off or RGB color
|
||||||
// ignores whether the LED is hidden or not (if hide_led_ is set)
|
// ignores whether the LED is hidden or not (if hide_led_ is set)
|
||||||
void LED::set_led(Color color) {
|
void LED::set_led(Color color) {
|
||||||
if (!led_gpio_) {
|
if (!led_gpio_ || color == last_color_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
last_color_ = color;
|
||||||
|
|
||||||
// RGB lookup table indexed by Color enum (must match enum order in led.h)
|
// RGB lookup table indexed by Color enum (must match enum order in led.h)
|
||||||
static constexpr uint8_t B = RGB_LED_BRIGHTNESS;
|
static constexpr uint8_t B = RGB_LED_BRIGHTNESS;
|
||||||
@@ -181,28 +283,26 @@ void LED::set_led(Color color) {
|
|||||||
{0, B, B}, // CYAN
|
{0, B, B}, // CYAN
|
||||||
{H, 0, H} // PINK
|
{H, 0, H} // PINK
|
||||||
};
|
};
|
||||||
|
static_assert(sizeof(rgb_table) / sizeof(rgb_table[0]) == Color::PINK + 1, "rgb_table must match the Color enum");
|
||||||
|
|
||||||
const uint8_t color_idx = static_cast<uint8_t>(color);
|
const uint8_t * rgb = rgb_table[color];
|
||||||
const uint8_t idx = (color_idx < sizeof(rgb_table) / sizeof(rgb_table[0])) ? color_idx : static_cast<uint8_t>(Color::OFF);
|
|
||||||
const uint8_t red = rgb_table[idx][0];
|
|
||||||
const uint8_t green = rgb_table[idx][1];
|
|
||||||
const uint8_t blue = rgb_table[idx][2];
|
|
||||||
|
|
||||||
if (led_type_) {
|
if (led_type_) {
|
||||||
rgbLedWrite(led_gpio_, red, green, blue);
|
rgb_led::write(led_gpio_, rgb[0], rgb[1], rgb[2]);
|
||||||
} else {
|
} else {
|
||||||
digitalWrite(led_gpio_, (red == 0 && green == 0 && blue == 0) || color == Color::OFF ? !LED_ON : LED_ON);
|
digitalWrite(led_gpio_, color == Color::OFF ? !LED_ON : LED_ON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set LED custom routine
|
// set LED custom routine
|
||||||
// For example: /api/system/led?data=red:blink1
|
// For example: /api/system/led?data=red:blink1
|
||||||
// For older non-RGB models, the colour would default to just being on.
|
// For older non-RGB models, the colour would default to just being on.
|
||||||
bool LED::set_custom_led_routine(std::string color, std::string pattern) {
|
bool LED::set_custom_led_routine(const std::string & color, const std::string & pattern) {
|
||||||
static constexpr struct {
|
static constexpr struct {
|
||||||
const char * name;
|
const char * name;
|
||||||
Color value;
|
Color value;
|
||||||
} color_map[] = {
|
} color_map[] = {
|
||||||
|
{"", Color::OFF},
|
||||||
{"off", Color::OFF},
|
{"off", Color::OFF},
|
||||||
{"on", Color::ON},
|
{"on", Color::ON},
|
||||||
{"white", Color::ON},
|
{"white", Color::ON},
|
||||||
@@ -228,35 +328,33 @@ bool LED::set_custom_led_routine(std::string color, std::string pattern) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset the color steps
|
// build the steps locally so an unrecognized pattern leaves the current sequence untouched
|
||||||
color_steps_[0] = Color::OFF;
|
Color steps[3] = {Color::OFF, Color::OFF, Color::OFF};
|
||||||
color_steps_[1] = Color::OFF;
|
|
||||||
color_steps_[2] = Color::OFF;
|
|
||||||
|
|
||||||
// blink patterns
|
// blink patterns
|
||||||
if (pattern == "blink1") {
|
if (pattern == "blink1") {
|
||||||
color_steps_[0] = color_type;
|
steps[0] = color_type;
|
||||||
} else if (pattern == "blink2") {
|
} else if (pattern == "blink2") {
|
||||||
color_steps_[0] = color_type;
|
steps[0] = steps[1] = color_type;
|
||||||
color_steps_[1] = color_type;
|
|
||||||
} else if (pattern == "blink3") {
|
} else if (pattern == "blink3") {
|
||||||
color_steps_[0] = color_type;
|
steps[0] = steps[1] = steps[2] = color_type;
|
||||||
color_steps_[1] = color_type;
|
|
||||||
color_steps_[2] = color_type;
|
|
||||||
|
|
||||||
// special patterns, ignores the user color
|
// special patterns, ignores the user color
|
||||||
} else if (pattern == "rgb") {
|
} else if (pattern == "rgb") {
|
||||||
color_steps_[0] = Color::RED;
|
steps[0] = Color::RED;
|
||||||
color_steps_[1] = Color::GREEN;
|
steps[1] = Color::GREEN;
|
||||||
color_steps_[2] = Color::BLUE;
|
steps[2] = Color::BLUE;
|
||||||
} else if (pattern == "cpc") {
|
} else if (pattern == "cpc") {
|
||||||
color_steps_[0] = Color::CYAN;
|
steps[0] = Color::CYAN;
|
||||||
color_steps_[1] = Color::PINK;
|
steps[1] = Color::PINK;
|
||||||
color_steps_[2] = Color::CYAN;
|
steps[2] = Color::CYAN;
|
||||||
} else {
|
} else {
|
||||||
return false; // pattern not recognized
|
return false; // pattern not recognized
|
||||||
}
|
}
|
||||||
|
|
||||||
|
color_steps_[0] = steps[0];
|
||||||
|
color_steps_[1] = steps[1];
|
||||||
|
color_steps_[2] = steps[2];
|
||||||
is_user_led_blink_ = true; // user routine is active
|
is_user_led_blink_ = true; // user routine is active
|
||||||
|
|
||||||
// when this is called we want the sequence_led to restart immediately and skip the long pause
|
// when this is called we want the sequence_led to restart immediately and skip the long pause
|
||||||
@@ -286,7 +384,7 @@ void LED::sequence_led() {
|
|||||||
|
|
||||||
if (++led_flash_step_ == 8) {
|
if (++led_flash_step_ == 8) {
|
||||||
// finished first iteration, reset the whole sequence, turn off LED
|
// finished first iteration, reset the whole sequence, turn off LED
|
||||||
led_long_timer_ = uuid::get_uptime();
|
led_long_timer_ = current_time;
|
||||||
led_flash_step_ = 0;
|
led_flash_step_ = 0;
|
||||||
set_led(Color::OFF); // turn off the LED
|
set_led(Color::OFF); // turn off the LED
|
||||||
|
|
||||||
@@ -298,24 +396,8 @@ void LED::sequence_led() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (led_flash_step_ % 2) {
|
// odd steps 3, 5 and 7 show the three configured colors, even steps turn the LED off to make it flash
|
||||||
// handle the three step events (on odd numbers 3,5,7 etc). see if we need to set a LED color
|
set_led(led_flash_step_ % 2 ? color_steps_[(led_flash_step_ - 3) / 2] : Color::OFF);
|
||||||
switch (led_flash_step_) {
|
|
||||||
case 3: // first flash
|
|
||||||
set_led(color_steps_[0]);
|
|
||||||
break;
|
|
||||||
case 5: // second flash
|
|
||||||
set_led(color_steps_[1]);
|
|
||||||
break;
|
|
||||||
case 7: // third flash
|
|
||||||
set_led(color_steps_[2]);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
set_led(Color::OFF); // turn off on even number count, to make it flash
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+40
-14
@@ -24,6 +24,29 @@
|
|||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
|
// WS2812B driver replacing arduino-esp32's rgbLedWrite()/neopixelWrite(), which hard-code
|
||||||
|
// T0H = 400 ns, T1L = 400 ns and have no reset gap. The WS2812B-2020-V6 needs T0H 220-380 ns,
|
||||||
|
// T1L 580-1000 ns and a reset > 280 us, otherwise it misreads 0-bits as 1-bits (washed-out white).
|
||||||
|
// Timings used (10 MHz RMT clock, 1 tick = 100 ns) fit both the original WS2812B and the V6:
|
||||||
|
//
|
||||||
|
// sent old WS2812B WS2812B-2020-V6
|
||||||
|
// T0H 300 ns 250-550 ns 220-380 ns
|
||||||
|
// T0L 900 ns 700-1000 ns 580-1000 ns
|
||||||
|
// T1H 800 ns 650-950 ns 580-1000 ns
|
||||||
|
// T1L 600 ns 300-600 ns 580-1000 ns
|
||||||
|
// Reset >300 us >50 us >280 us
|
||||||
|
//
|
||||||
|
// Do not mix with rgbLedWrite()/neopixelWrite() on the same pin, as both claim the pin's RMT channel.
|
||||||
|
namespace rgb_led {
|
||||||
|
|
||||||
|
enum class Order : uint8_t { GRB, RGB }; // WS2812B is GRB
|
||||||
|
|
||||||
|
// Blocks ~30 us for the transmit, plus up to the reset gap if called again right after the previous update
|
||||||
|
bool write(uint8_t pin, uint8_t red, uint8_t green, uint8_t blue, Order order = Order::GRB);
|
||||||
|
void end(uint8_t pin); // release the pin's RMT channel, e.g. before reusing the pin for something else
|
||||||
|
|
||||||
|
} // namespace rgb_led
|
||||||
|
|
||||||
class LED {
|
class LED {
|
||||||
public:
|
public:
|
||||||
enum Color : uint8_t {
|
enum Color : uint8_t {
|
||||||
@@ -42,7 +65,7 @@ class LED {
|
|||||||
bool loop(uint8_t healthcheck, bool button_busy);
|
bool loop(uint8_t healthcheck, bool button_busy);
|
||||||
|
|
||||||
void start_led_fast_flash(uint8_t duration); // duration in seconds
|
void start_led_fast_flash(uint8_t duration); // duration in seconds
|
||||||
bool set_custom_led_routine(std::string color, std::string pattern);
|
bool set_custom_led_routine(const std::string & color, const std::string & pattern);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static uuid::log::Logger logger_;
|
static uuid::log::Logger logger_;
|
||||||
@@ -59,28 +82,31 @@ class LED {
|
|||||||
static constexpr uint32_t LED_FLASH_INTERVAL_MS = 100; // LED toggle period during factory-reset flash
|
static constexpr uint32_t LED_FLASH_INTERVAL_MS = 100; // LED toggle period during factory-reset flash
|
||||||
static constexpr uint8_t RGB_LED_BRIGHTNESS = 20; // 255 is max brightness
|
static constexpr uint8_t RGB_LED_BRIGHTNESS = 20; // 255 is max brightness
|
||||||
static constexpr uint8_t LED_ON = HIGH; // LED on
|
static constexpr uint8_t LED_ON = HIGH; // LED on
|
||||||
|
static constexpr Color COLOR_UNSET = static_cast<Color>(0xFF); // forces the next set_led() to write
|
||||||
|
|
||||||
|
// led_fast_flash() state
|
||||||
|
uint32_t led_flash_start_time_ = 0;
|
||||||
|
uint32_t led_flash_duration_ = 0;
|
||||||
|
uint32_t last_toggle_time_ = 0;
|
||||||
|
|
||||||
|
// sequence_led() state
|
||||||
|
uint32_t led_long_timer_ = 1; // 1 will kick it off immediately
|
||||||
|
uint32_t led_short_timer_ = 0;
|
||||||
|
|
||||||
// local copies of the application settings
|
// local copies of the application settings
|
||||||
uint8_t led_gpio_ = 0;
|
uint8_t led_gpio_ = 0;
|
||||||
uint8_t led_type_ = 0;
|
uint8_t led_type_ = 0;
|
||||||
bool hide_led_ = false;
|
bool hide_led_ = false;
|
||||||
|
|
||||||
bool led_fast_flash_timer_ = false;
|
bool led_fast_flash_timer_ = false;
|
||||||
uint32_t led_flash_start_time_ = 0;
|
bool led_flash_state_ = false;
|
||||||
uint32_t led_flash_duration_ = 0;
|
|
||||||
|
|
||||||
// led_flash() state
|
bool last_button_busy_ = false;
|
||||||
bool led_flash_state_ = false;
|
uint8_t led_flash_step_ = 0; // 0 means we're not in the short flash timer
|
||||||
uint32_t last_toggle_time_ = 0;
|
|
||||||
|
|
||||||
// sequence_led() state
|
// set_custom_led_routine() and healthcheck state
|
||||||
bool last_button_busy_ = false;
|
|
||||||
uint32_t led_long_timer_ = 1; // 1 will kick it off immediately
|
|
||||||
uint32_t led_short_timer_ = 0;
|
|
||||||
uint8_t led_flash_step_ = 0; // 0 means we're not in the short flash timer
|
|
||||||
|
|
||||||
// set_led_routine() state
|
|
||||||
Color color_steps_[3] = {Color::OFF, Color::OFF, Color::OFF};
|
Color color_steps_[3] = {Color::OFF, Color::OFF, Color::OFF};
|
||||||
|
Color last_color_ = COLOR_UNSET; // last color written, to skip redundant writes
|
||||||
|
|
||||||
// if true, the user has requested a custom LED blink, this always has preference over the button or showing the system health
|
// if true, the user has requested a custom LED blink, this always has preference over the button or showing the system health
|
||||||
bool is_user_led_blink_ = false;
|
bool is_user_led_blink_ = false;
|
||||||
|
|||||||
+1
-1
@@ -643,7 +643,7 @@ bool Mqtt::queue_message(const uint8_t operation, const std::string & topic, con
|
|||||||
// check free mem
|
// check free mem
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
// if (ESP.getFreeHeap() < 60 * 1024 || ESP.getMaxAllocHeap() < 40 * 1024) {
|
// if (ESP.getFreeHeap() < 60 * 1024 || ESP.getMaxAllocHeap() < 40 * 1024) {
|
||||||
if (heap_caps_get_free_size(MALLOC_CAP_8BIT) < 60 * 1024) { // checks free Heap+PSRAM
|
if (heap_caps_get_free_size(MALLOC_CAP_8BIT) < MQTT_MIN_FREE_HEAP) { // checks free Heap+PSRAM
|
||||||
if (operation == Operation::PUBLISH) {
|
if (operation == Operation::PUBLISH) {
|
||||||
mqtt_message_id_++;
|
mqtt_message_id_++;
|
||||||
mqtt_publish_fails_++;
|
mqtt_publish_fails_++;
|
||||||
|
|||||||
+3
-2
@@ -61,9 +61,10 @@ class Mqtt {
|
|||||||
enum Operation : uint8_t { PUBLISH, SUBSCRIBE, UNSUBSCRIBE };
|
enum Operation : uint8_t { PUBLISH, SUBSCRIBE, UNSUBSCRIBE };
|
||||||
enum NestedFormat : uint8_t { NESTED = 1, SINGLE };
|
enum NestedFormat : uint8_t { NESTED = 1, SINGLE };
|
||||||
|
|
||||||
static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 128; // fixed, not a user setting anymore
|
static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 128; // fixed, not a user setting anymore
|
||||||
static constexpr uint16_t MQTT_QUEUE_MAX_SIZE = 300;
|
static constexpr uint16_t MQTT_QUEUE_MAX_SIZE = 300;
|
||||||
static constexpr uint32_t MQTT_DISCONNECT_TIMEOUT = 1000; // max ms to flush a graceful disconnect on non-PSRAM boards
|
static constexpr uint32_t MQTT_DISCONNECT_TIMEOUT = 1000; // max ms to flush a graceful disconnect on non-PSRAM boards
|
||||||
|
static constexpr uint32_t MQTT_MIN_FREE_HEAP = 60 * 1024; // don't queue any MQTT message below this much free heap+PSRAM (in bytes) - 60 KB
|
||||||
|
|
||||||
static void on_connect();
|
static void on_connect();
|
||||||
static void on_disconnect(espMqttClientTypes::DisconnectReason reason);
|
static void on_disconnect(espMqttClientTypes::DisconnectReason reason);
|
||||||
|
|||||||
+34
-37
@@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
#include "firmwareVersion.h"
|
#include "firmwareVersion.h"
|
||||||
#include "shuntingYard.h" // for compute() used by the message and sendmail commands
|
#include "shuntingYard.h" // for compute() used by the message and sendmail commands
|
||||||
|
#include "httpClient.h" // for the shared TLS client settings
|
||||||
|
|
||||||
#if defined(EMSESP_TEST)
|
#if defined(EMSESP_TEST)
|
||||||
#include "../test/test.h"
|
#include "../test/test.h"
|
||||||
@@ -218,7 +219,7 @@ bool System::command_sendmail(const char * value, const int8_t) {
|
|||||||
ESP_SSLClient * ssl_client = nullptr;
|
ESP_SSLClient * ssl_client = nullptr;
|
||||||
SMTPClient * smtp = nullptr;
|
SMTPClient * smtp = nullptr;
|
||||||
|
|
||||||
basic_client->setTimeout(5000);
|
basic_client->setTimeout(SMTP_TIMEOUT_MS);
|
||||||
|
|
||||||
const bool implicit_ssl = (security == EMAIL_SECURITY::SSL);
|
const bool implicit_ssl = (security == EMAIL_SECURITY::SSL);
|
||||||
const bool start_tls = (security == EMAIL_SECURITY::STARTTLS);
|
const bool start_tls = (security == EMAIL_SECURITY::STARTTLS);
|
||||||
@@ -229,8 +230,8 @@ bool System::command_sendmail(const char * value, const int8_t) {
|
|||||||
} else {
|
} else {
|
||||||
ssl_client = new ESP_SSLClient;
|
ssl_client = new ESP_SSLClient;
|
||||||
ssl_client->setInsecure();
|
ssl_client->setInsecure();
|
||||||
ssl_client->setBufferSizes(16384, 1024);
|
ssl_client->setBufferSizes(HttpClient::TLS_RX_BUFFER_SIZE, HttpClient::TLS_TX_BUFFER_SIZE);
|
||||||
ssl_client->setTimeout(5);
|
ssl_client->setTimeout(HttpClient::TLS_READ_TIMEOUT_S);
|
||||||
// enableSSL is baked in at setClient() — true only for implicit SSL (port 465)
|
// enableSSL is baked in at setClient() — true only for implicit SSL (port 465)
|
||||||
ssl_client->setClient(basic_client, implicit_ssl);
|
ssl_client->setClient(basic_client, implicit_ssl);
|
||||||
if (start_tls) {
|
if (start_tls) {
|
||||||
@@ -2923,11 +2924,13 @@ bool System::load_board_profile(std::vector<int8_t> & data, const std::string &
|
|||||||
// https://github.com/emsesp/EMS-ESP32/issues/3063
|
// https://github.com/emsesp/EMS-ESP32/issues/3063
|
||||||
// /api//system/led command that takes an argument in the form [color]:[pattern]
|
// /api//system/led command that takes an argument in the form [color]:[pattern]
|
||||||
// color is red, green, blue, yellow, white
|
// color is red, green, blue, yellow, white
|
||||||
|
// color is optional
|
||||||
// pattern is
|
// pattern is
|
||||||
// blink1 for 1 time
|
// blink1 for 1 time
|
||||||
// blink2 for 2 times
|
// blink2 for 2 times
|
||||||
// blink3 for 3 times
|
// blink3 for 3 times
|
||||||
// rgb for RGB
|
// rgb for RGB
|
||||||
|
// cpc for CPC
|
||||||
// For example: /api/system/led?data=red:blink1
|
// For example: /api/system/led?data=red:blink1
|
||||||
// For older non-RGB models, the colour would default to just being on.
|
// For older non-RGB models, the colour would default to just being on.
|
||||||
bool System::command_led(const char * value, const int8_t) {
|
bool System::command_led(const char * value, const int8_t) {
|
||||||
@@ -2936,16 +2939,19 @@ bool System::command_led(const char * value, const int8_t) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string arg = value;
|
std::string arg = value;
|
||||||
if (arg.find(':') == std::string::npos) {
|
std::string color;
|
||||||
LOG_ERROR("LED command must be in the form [color]:[pattern]");
|
std::string pattern;
|
||||||
return false; // not in the form [color]:[pattern]
|
auto sep = arg.find(':');
|
||||||
|
if (sep == std::string::npos) {
|
||||||
|
pattern = arg; // no color given, only a pattern
|
||||||
|
} else {
|
||||||
|
color = arg.substr(0, sep);
|
||||||
|
pattern = arg.substr(sep + 1);
|
||||||
}
|
}
|
||||||
std::string color = arg.substr(0, arg.find(':'));
|
|
||||||
std::string pattern = arg.substr(arg.find(':') + 1);
|
|
||||||
|
|
||||||
// set and validate the color and pattern
|
// set and validate the color and pattern
|
||||||
if (!EMSESP::led_.set_custom_led_routine(color, pattern)) {
|
if (!EMSESP::led_.set_custom_led_routine(color, pattern)) {
|
||||||
LOG_ERROR("Invalid color or pattern.");
|
LOG_ERROR("Invalid color or pattern");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3093,8 +3099,7 @@ void System::ntp_connected(bool b) {
|
|||||||
|
|
||||||
// get NTP status
|
// get NTP status
|
||||||
bool System::ntp_connected() {
|
bool System::ntp_connected() {
|
||||||
// timeout 2 hours, ntp sync is normally every hour.
|
if ((uuid::get_uptime_sec() - ntp_last_check_ > NTP_TIMEOUT_SEC) && ntp_connected_) {
|
||||||
if ((uuid::get_uptime_sec() - ntp_last_check_ > 7201) && ntp_connected_) {
|
|
||||||
ntp_connected(false);
|
ntp_connected(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3200,20 +3205,14 @@ bool System::uploadFirmwareURL(const char * url) {
|
|||||||
|
|
||||||
if (is_https) {
|
if (is_https) {
|
||||||
ssl_client.setInsecure(); // no CA validation, matches the rest of the project
|
ssl_client.setInsecure(); // no CA validation, matches the rest of the project
|
||||||
// BearSSL needs a receive buffer large enough to hold one full TLS record.
|
// a small (e.g. 1 KB) RX buffer makes the body unreadable: the headers still fit one small
|
||||||
// GitHub's release-assets CDN sends standard up-to-16 KB records and does NOT
|
// record, hence Content-Length looks fine, but the first body record cannot be decoded
|
||||||
// negotiate max_fragment_length, so a small (e.g. 1 KB) RX buffer makes the
|
ssl_client.setBufferSizes(HttpClient::TLS_RX_BUFFER_SIZE, HttpClient::TLS_TX_BUFFER_SIZE);
|
||||||
// body unreadable (headers still fit one small record, hence Content-Length
|
ssl_client.setSessionTimeout(HttpClient::TLS_SESSION_TIMEOUT_S);
|
||||||
// looks fine, but the first body record cannot be decoded). 16384 + overhead
|
|
||||||
// is the safe value the library itself uses by default; we go a bit smaller
|
|
||||||
// to be friendlier to 4 MB / no-PSRAM boards while still big enough for any
|
|
||||||
// record the CDN actually sends in practice.
|
|
||||||
ssl_client.setBufferSizes(16384, 1024);
|
|
||||||
ssl_client.setSessionTimeout(120);
|
|
||||||
}
|
}
|
||||||
basic_client.setTimeout(15000); // socket-level read timeout
|
basic_client.setTimeout(FIRMWARE_UPLOAD_READ_TIMEOUT_S * 1000); // socket-level read timeout, in ms
|
||||||
ssl_client.setTimeout(15); // Stream::readBytes timeout used by Update
|
ssl_client.setTimeout(FIRMWARE_UPLOAD_READ_TIMEOUT_S); // Stream::readBytes timeout used by Update
|
||||||
ssl_client.setClient(&basic_client, is_https); // enableSSL = false for plain HTTP
|
ssl_client.setClient(&basic_client, is_https); // enableSSL = false for plain HTTP
|
||||||
|
|
||||||
const uint16_t port = is_https ? 443 : 80;
|
const uint16_t port = is_https ? 443 : 80;
|
||||||
String url_remain = saved_url.substring(scheme_len);
|
String url_remain = saved_url.substring(scheme_len);
|
||||||
@@ -3248,9 +3247,9 @@ bool System::uploadFirmwareURL(const char * url) {
|
|||||||
ssl_client.println("Connection: close");
|
ssl_client.println("Connection: close");
|
||||||
ssl_client.print("\r\n");
|
ssl_client.print("\r\n");
|
||||||
|
|
||||||
// wait for the first byte (up to 8s, matching the previous HTTP timeout)
|
// wait for the first byte (matching the previous HTTP timeout)
|
||||||
uint32_t ms = millis();
|
uint32_t ms = millis();
|
||||||
while (ssl_client.connected() && !ssl_client.available() && millis() - ms < 8000) {
|
while (ssl_client.connected() && !ssl_client.available() && millis() - ms < FIRMWARE_UPLOAD_RESPONSE_TIMEOUT) {
|
||||||
delay(1);
|
delay(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3326,7 +3325,7 @@ bool System::uploadFirmwareURL(const char * url) {
|
|||||||
// wait for the first byte of the body so the read loop sees real data
|
// wait for the first byte of the body so the read loop sees real data
|
||||||
// (headers and body may arrive in separate TLS records)
|
// (headers and body may arrive in separate TLS records)
|
||||||
uint32_t body_wait = millis();
|
uint32_t body_wait = millis();
|
||||||
while (ssl_client.connected() && !ssl_client.available() && millis() - body_wait < 8000) {
|
while (ssl_client.connected() && !ssl_client.available() && millis() - body_wait < FIRMWARE_UPLOAD_RESPONSE_TIMEOUT) {
|
||||||
delay(1);
|
delay(1);
|
||||||
}
|
}
|
||||||
if (!ssl_client.available()) {
|
if (!ssl_client.available()) {
|
||||||
@@ -3341,7 +3340,7 @@ bool System::uploadFirmwareURL(const char * url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check we have a valid size
|
// check we have a valid size
|
||||||
if (firmware_size < 1677721) { // 1.6MB or greater is required
|
if (firmware_size < (int)MIN_FIRMWARE_SIZE) {
|
||||||
LOG_ERROR("Firmware upload failed - invalid size");
|
LOG_ERROR("Firmware upload failed - invalid size");
|
||||||
return false; // error
|
return false; // error
|
||||||
}
|
}
|
||||||
@@ -3360,12 +3359,10 @@ bool System::uploadFirmwareURL(const char * url) {
|
|||||||
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING);
|
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING);
|
||||||
|
|
||||||
// explicit chunked read loop instead of Update.writeStream():
|
// explicit chunked read loop instead of Update.writeStream():
|
||||||
constexpr size_t CHUNK_SIZE = 1024;
|
uint8_t buf[FIRMWARE_UPLOAD_CHUNK_SIZE];
|
||||||
constexpr uint32_t READ_TIMEOUT_MS = 30000; // overall stall timeout per chunk
|
size_t total_read = 0;
|
||||||
uint8_t buf[CHUNK_SIZE];
|
bool magic_ok = false;
|
||||||
size_t total_read = 0;
|
int last_pct = -1;
|
||||||
bool magic_ok = false;
|
|
||||||
int last_pct = -1;
|
|
||||||
|
|
||||||
while (total_read < (size_t)firmware_size) {
|
while (total_read < (size_t)firmware_size) {
|
||||||
// a cancel is signalled by the WebUI dropping the status below UPLOADING (back to NORMAL)
|
// a cancel is signalled by the WebUI dropping the status below UPLOADING (back to NORMAL)
|
||||||
@@ -3386,7 +3383,7 @@ bool System::uploadFirmwareURL(const char * url) {
|
|||||||
if (!ssl_client.connected()) {
|
if (!ssl_client.connected()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (millis() - wait_start > READ_TIMEOUT_MS) {
|
if (millis() - wait_start > FIRMWARE_UPLOAD_STALL_TIMEOUT) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// also bail out promptly if a cancel arrives mid-stall
|
// also bail out promptly if a cancel arrives mid-stall
|
||||||
@@ -3407,8 +3404,8 @@ bool System::uploadFirmwareURL(const char * url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t want = (size_t)firmware_size - total_read;
|
size_t want = (size_t)firmware_size - total_read;
|
||||||
if (want > CHUNK_SIZE) {
|
if (want > FIRMWARE_UPLOAD_CHUNK_SIZE) {
|
||||||
want = CHUNK_SIZE;
|
want = FIRMWARE_UPLOAD_CHUNK_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t n = stream->readBytes(buf, want);
|
size_t n = stream->readBytes(buf, want);
|
||||||
|
|||||||
@@ -370,6 +370,9 @@ class System {
|
|||||||
static constexpr uint8_t HEALTHCHECK_NO_NETWORK = (1 << 1); // 2
|
static constexpr uint8_t HEALTHCHECK_NO_NETWORK = (1 << 1); // 2
|
||||||
static constexpr uint8_t HEALTHCHECK_RESET = (1 << 7); // 128
|
static constexpr uint8_t HEALTHCHECK_RESET = (1 << 7); // 128
|
||||||
|
|
||||||
|
// smallest .bin accepted as a firmware image, for both URL and web uploads - 1.6 MB
|
||||||
|
static constexpr size_t MIN_FIRMWARE_SIZE = 1677721;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static uuid::log::Logger logger_;
|
static uuid::log::Logger logger_;
|
||||||
|
|
||||||
@@ -402,6 +405,18 @@ class System {
|
|||||||
static constexpr uint32_t SYSTEM_CHECK_FREQUENCY = 5000; // do a system check every 5 seconds
|
static constexpr uint32_t SYSTEM_CHECK_FREQUENCY = 5000; // do a system check every 5 seconds
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// NTP syncs about every hour, so allow one missed sync before treating it as lost (in seconds) - 2 hours
|
||||||
|
static constexpr uint32_t NTP_TIMEOUT_SEC = 7201;
|
||||||
|
|
||||||
|
// email
|
||||||
|
static constexpr uint32_t SMTP_TIMEOUT_MS = 5000; // socket read timeout (in ms) - 5 seconds
|
||||||
|
|
||||||
|
// firmware upload from URL
|
||||||
|
static constexpr uint32_t FIRMWARE_UPLOAD_RESPONSE_TIMEOUT = 8000; // max wait for the first byte of the response headers/body (in ms) - 8 seconds
|
||||||
|
static constexpr uint32_t FIRMWARE_UPLOAD_READ_TIMEOUT_S = 15; // socket and Stream::readBytes timeout used by Update (in seconds)
|
||||||
|
static constexpr uint32_t FIRMWARE_UPLOAD_STALL_TIMEOUT = 30000; // abort if no data arrives for a chunk (in ms) - 30 seconds
|
||||||
|
static constexpr size_t FIRMWARE_UPLOAD_CHUNK_SIZE = 1024; // read buffer, on the stack
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
static uuid::syslog::SyslogService syslog_;
|
static uuid::syslog::SyslogService syslog_;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -286,7 +286,7 @@ void TxService::send_poll() const {
|
|||||||
uint8_t TxService::get_send_id() {
|
uint8_t TxService::get_send_id() {
|
||||||
static uint32_t count = 0;
|
static uint32_t count = 0;
|
||||||
if (!tx_telegrams_.empty() && tx_telegrams_.front().telegram_->src != ems_bus_id()) {
|
if (!tx_telegrams_.empty() && tx_telegrams_.front().telegram_->src != ems_bus_id()) {
|
||||||
if (++count > 500) { // after 500 polls (~3-10 sec) there will be no master poll for this id
|
if (++count > MAX_POLLS_WITHOUT_MASTER) {
|
||||||
tx_telegrams_.pop_front();
|
tx_telegrams_.pop_front();
|
||||||
count = 0;
|
count = 0;
|
||||||
return tx_telegrams_.empty() ? ems_bus_id() : tx_telegrams_.front().telegram_->src;
|
return tx_telegrams_.empty() ? ems_bus_id() : tx_telegrams_.front().telegram_->src;
|
||||||
|
|||||||
@@ -440,6 +440,9 @@ class TxService : public EMSbus {
|
|||||||
static constexpr uint8_t MAXIMUM_TX_RETRIES = 3;
|
static constexpr uint8_t MAXIMUM_TX_RETRIES = 3;
|
||||||
static constexpr uint32_t POST_SEND_DELAY = 2000;
|
static constexpr uint32_t POST_SEND_DELAY = 2000;
|
||||||
|
|
||||||
|
// drop a queued Tx telegram sent from another id if the master hasn't polled that id after this many polls (~3-10 seconds)
|
||||||
|
static constexpr uint32_t MAX_POLLS_WITHOUT_MASTER = 500;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::deque<QueuedTxTelegram, AllocatorPSRAM<QueuedTxTelegram>> tx_telegrams_; // the Tx queue
|
std::deque<QueuedTxTelegram, AllocatorPSRAM<QueuedTxTelegram>> tx_telegrams_; // the Tx queue
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define EMSESP_APP_VERSION "3.9.0-dev.13"
|
#define EMSESP_APP_VERSION "3.9.0-dev.14"
|
||||||
|
|||||||
@@ -76,9 +76,9 @@ void WebLogService::operator<<(std::shared_ptr<uuid::log::Message> message) {
|
|||||||
limit_log_messages_ = maximum_log_messages_;
|
limit_log_messages_ = maximum_log_messages_;
|
||||||
} else {
|
} else {
|
||||||
uint32_t maxAlloc = ESP.getMaxAllocHeap();
|
uint32_t maxAlloc = ESP.getMaxAllocHeap();
|
||||||
if (limit_log_messages_ > 5 && maxAlloc < (50 * 1024)) { // 50k
|
if (limit_log_messages_ > 5 && maxAlloc < LOG_HEAP_LOW) {
|
||||||
--limit_log_messages_;
|
--limit_log_messages_;
|
||||||
} else if (limit_log_messages_ < maximum_log_messages_ && maxAlloc > (60 * 1024)) { // 60k
|
} else if (limit_log_messages_ < maximum_log_messages_ && maxAlloc > LOG_HEAP_HIGH) {
|
||||||
++limit_log_messages_;
|
++limit_log_messages_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ class WebLogService : public uuid::log::Handler {
|
|||||||
virtual void operator<<(std::shared_ptr<uuid::log::Message> message);
|
virtual void operator<<(std::shared_ptr<uuid::log::Message> message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// on boards without PSRAM, shrink the log buffer below LOW and grow it again above HIGH (max alloc heap, in bytes).
|
||||||
|
// The gap between them stops the limit flapping around a single threshold
|
||||||
|
static constexpr uint32_t LOG_HEAP_LOW = 50 * 1024; // 50 KB
|
||||||
|
static constexpr uint32_t LOG_HEAP_HIGH = 60 * 1024; // 60 KB
|
||||||
|
|
||||||
AsyncEventSource events_;
|
AsyncEventSource events_;
|
||||||
|
|
||||||
class QueuedLogMessage {
|
class QueuedLogMessage {
|
||||||
|
|||||||
+65
-30
@@ -1,53 +1,88 @@
|
|||||||
#!/bin/sh
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
#
|
#
|
||||||
# Command line test for the API
|
# Command line test for the API
|
||||||
#
|
#
|
||||||
|
|
||||||
emsesp_url="http://ems-esp.local"
|
emsesp_url="http://ems-espT.local"
|
||||||
|
|
||||||
# get the token from the Security page. This is the token for the admin user, unless changed it'll always be the same
|
# get the token from the Security page. This is the token for the admin user, unless changed it'll always be the same
|
||||||
emsesp_token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWV9.2bHpWya2C7Q12WjNUBD6_7N3RCD7CMl-EGhyQVzFdDg"
|
emsesp_token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWV9.2bHpWya2C7Q12WjNUBD6_7N3RCD7CMl-EGhyQVzFdDg"
|
||||||
|
|
||||||
# This example will get the system info, via a GET request
|
# This example will get the system info, via a GET request
|
||||||
curl -X GET ${emsesp_url}/api/system/info
|
test0() {
|
||||||
echo "\n"
|
curl -X GET ${emsesp_url}/api/system/info
|
||||||
|
}
|
||||||
|
|
||||||
# This example will execute a read command on product ID 8 and type ID 1
|
# This example will execute a read command on product ID 8 and type ID 1
|
||||||
curl -X POST \
|
test1() {
|
||||||
-H "Authorization: Bearer ${emsesp_token}" \
|
curl -X POST \
|
||||||
-H "Content-Type: application/json" \
|
-H "Authorization: Bearer ${emsesp_token}" \
|
||||||
-d '{"data":"8 1"}' \
|
-H "Content-Type: application/json" \
|
||||||
${emsesp_url}/api/system/read
|
-d '{"data":"8 1"}' \
|
||||||
|
${emsesp_url}/api/system/read
|
||||||
echo "\n"
|
}
|
||||||
|
|
||||||
# This example will use the message command to get the locale (en)
|
# This example will use the message command to get the locale (en)
|
||||||
curl -X POST \
|
test2() {
|
||||||
-H "Authorization: Bearer ${emsesp_token}" \
|
curl -X POST \
|
||||||
-H "Content-Type: application/json" \
|
-H "Authorization: Bearer ${emsesp_token}" \
|
||||||
-d '{"value":"system/settings/locale"}' \
|
-H "Content-Type: application/json" \
|
||||||
${emsesp_url}/api/system/message
|
-d '{"value":"system/settings/locale"}' \
|
||||||
|
${emsesp_url}/api/system/message
|
||||||
echo "\n"
|
}
|
||||||
|
|
||||||
# This example will export all values to a json file, including custom entities, sensors and schedules
|
# This example will export all values to a json file, including custom entities, sensors and schedules
|
||||||
curl -X POST \
|
test3() {
|
||||||
-H "Authorization: Bearer ${emsesp_token}" \
|
curl -X POST \
|
||||||
-H "Content-Type: application/json" \
|
-H "Authorization: Bearer ${emsesp_token}" \
|
||||||
-d '{"action":"export", "param":"allvalues"}' \
|
-H "Content-Type: application/json" \
|
||||||
${emsesp_url}/rest/action
|
-d '{"action":"export", "param":"allvalues"}' \
|
||||||
|
${emsesp_url}/rest/action
|
||||||
echo "\n"
|
}
|
||||||
|
|
||||||
# Get all versions
|
# Get all versions
|
||||||
curl -X POST \
|
test4() {
|
||||||
|
curl -X POST \
|
||||||
-H "Authorization: Bearer ${emsesp_token}" \
|
-H "Authorization: Bearer ${emsesp_token}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"action":"getVersions"}' \
|
-d '{"action":"getVersions"}' \
|
||||||
${emsesp_url}/rest/action
|
${emsesp_url}/rest/action
|
||||||
|
}
|
||||||
|
|
||||||
|
# led tests
|
||||||
|
test5() {
|
||||||
|
curl -X POST \
|
||||||
|
-H "Authorization: Bearer ${emsesp_token}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"data":"red:blink1"}' \
|
||||||
|
${emsesp_url}/api/system/led
|
||||||
|
}
|
||||||
|
|
||||||
|
test6() {
|
||||||
|
curl -X POST \
|
||||||
|
-H "Authorization: Bearer ${emsesp_token}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"data":"rgb"}' \
|
||||||
|
${emsesp_url}/api/system/led
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create a list of test function names to run
|
||||||
|
tests_to_run=(
|
||||||
|
# test0
|
||||||
|
# test1
|
||||||
|
# test2
|
||||||
|
# test3
|
||||||
|
# test4
|
||||||
|
test5
|
||||||
|
test6
|
||||||
|
)
|
||||||
|
|
||||||
|
for test_func in "${tests_to_run[@]}"; do
|
||||||
|
echo "Running $test_func..."
|
||||||
|
$test_func
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
echo "\n"
|
|
||||||
|
|
||||||
# This example is how to call a service in Home Assistant via the API
|
# This example is how to call a service in Home Assistant via the API
|
||||||
# Which can be added to an EMS-EPS schedule
|
# Which can be added to an EMS-EPS schedule
|
||||||
|
|||||||
Reference in New Issue
Block a user